From be6900e03d10702eea316b98bc0b4b202ba3e098 Mon Sep 17 00:00:00 2001 From: Ben Messerly Date: Wed, 5 Jan 2022 16:10:01 -0600 Subject: [PATCH 01/17] Initial commit of Mehreen's michel code. Also: add Ever's new michel tuple location to ccpion_common. And: reformat my Michel.h. --- ccpion_common.h | 5 + .../MehreenMichels/BestMichelDistance2D_cut.h | 165 +++ includes/MehreenMichels/Cluster.h | 37 + includes/MehreenMichels/HasMichel_cut.h | 89 ++ includes/MehreenMichels/Michel.h | 1180 +++++++++++++++++ includes/MehreenMichels/MichelEvent.h | 34 + xsec/mehreenEventLoop.cpp | 1143 ++++++++++++++++ 7 files changed, 2653 insertions(+) create mode 100644 includes/MehreenMichels/BestMichelDistance2D_cut.h create mode 100644 includes/MehreenMichels/Cluster.h create mode 100644 includes/MehreenMichels/HasMichel_cut.h create mode 100644 includes/MehreenMichels/Michel.h create mode 100644 includes/MehreenMichels/MichelEvent.h create mode 100644 xsec/mehreenEventLoop.cpp diff --git a/ccpion_common.h b/ccpion_common.h index dc1f50a..14a9417 100644 --- a/ccpion_common.h +++ b/ccpion_common.h @@ -8,6 +8,11 @@ std::string GetPlaylistFile(std::string plist, bool is_mc, bool use_xrootd = true) { + + // small sample with michel branches + // /pnfs/minerva/persistent/users/granados/MADtuplas/merged/mc/Michel/ME1A/MasterAnaDev_mc_AnaTuple_run00110000_Playlist.root + // /pnfs/minerva/persistent/users/granados/MADtuplas/merged/data/Michel/ + // const std::string processing_date = "20200713"; // new short tracking branches const std::string processing_date = "20211115"; // new recoil energy branches // const std::string processing_date = "test"; // For test with small MAD tuplas diff --git a/includes/MehreenMichels/BestMichelDistance2D_cut.h b/includes/MehreenMichels/BestMichelDistance2D_cut.h new file mode 100644 index 0000000..5a7d56e --- /dev/null +++ b/includes/MehreenMichels/BestMichelDistance2D_cut.h @@ -0,0 +1,165 @@ +// File: BestMichelDistance2D.h +// Brief: Require the the distance to vertex for the best Michel electron is below some value. +// Author: Andrew Olivier aolivier@ur.rochester.edu, Mehreen Sultana msultana@ur.rochester.edu + +// PlotUtils includes +#include "PlotUtils/Cut.h" +#include "event/Michel.h" + +template +class BestMichelDistance2D : public PlotUtils::Cut { + public: + BestMichelDistance2D(const double maxDistance) + : PlotUtils::Cut( + "Per Michel 2D Distance in at least two views is < " + + std::to_string(maxDistance) + "mm"), + m_maxDistance(maxDistance) {} + + private: + double m_maxDistance; // Maximum distance from the vertex that the best + // Michel can have in mm + + bool checkCut(const UNIVERSE& univ, EVENT& evt) const { + // std::cout << "Implementing Michel Cut with 2D distnace of " << + // m_maxDistance << " mm" << std::endl; + std::vector nmichelspass; + for (unsigned int i = 0; i < evt.m_nmichels.size(); i++) { + // For Vertex Match Check to see if 2D distance cut will + double upvtxXZ = evt.m_nmichels[i].up_to_vertex_XZ; + double downvtxXZ = evt.m_nmichels[i].down_to_vertex_XZ; + double upvtxUZ = evt.m_nmichels[i].up_to_vertex_UZ; + double downvtxUZ = evt.m_nmichels[i].down_to_vertex_UZ; + double upvtxVZ = evt.m_nmichels[i].up_to_vertex_VZ; + double downvtxVZ = evt.m_nmichels[i].down_to_vertex_VZ; + + if (upvtxXZ < m_maxDistance && + (upvtxUZ < m_maxDistance || upvtxVZ < m_maxDistance)) + evt.m_nmichels[i].passable_matchtype.at(0) = 1; + else if (upvtxUZ < m_maxDistance && + (upvtxXZ < m_maxDistance || upvtxVZ < m_maxDistance)) + evt.m_nmichels[i].passable_matchtype.at(0) = 1; + else if (upvtxVZ < m_maxDistance && + (upvtxXZ < m_maxDistance || upvtxUZ < m_maxDistance)) + evt.m_nmichels[i].passable_matchtype.at(0) = 1; + else + evt.m_nmichels[i].passable_matchtype.at(0) = -1; + + if (downvtxXZ < m_maxDistance && + (downvtxUZ < m_maxDistance || downvtxVZ < m_maxDistance)) + evt.m_nmichels[i].passable_matchtype.at(1) = 2; + else if (downvtxUZ < m_maxDistance && + (downvtxXZ < m_maxDistance || downvtxVZ < m_maxDistance)) + evt.m_nmichels[i].passable_matchtype.at(1) = 2; + else if (downvtxVZ < m_maxDistance && + (downvtxXZ < m_maxDistance || downvtxUZ < m_maxDistance)) + evt.m_nmichels[i].passable_matchtype.at(1) = 2; + else + evt.m_nmichels[i].passable_matchtype.at(1) = -1; + + double upclusXZ = evt.m_nmichels[i].up_to_clus_XZ; + double upclusUZ = evt.m_nmichels[i].up_to_clus_UZ; + double upclusVZ = evt.m_nmichels[i].up_to_clus_VZ; + double downclusXZ = evt.m_nmichels[i].down_to_clus_XZ; + double downclusUZ = evt.m_nmichels[i].down_to_clus_UZ; + double downclusVZ = evt.m_nmichels[i].down_to_clus_VZ; + + if (upclusXZ < m_maxDistance && + (upclusUZ < m_maxDistance || upclusVZ < m_maxDistance)) + evt.m_nmichels[i].passable_matchtype.at(2) = 3; + else if (upclusUZ < m_maxDistance && + (upclusXZ < m_maxDistance || upclusVZ < m_maxDistance)) + evt.m_nmichels[i].passable_matchtype.at(2) = 3; + else if (upclusVZ < m_maxDistance && + (upclusXZ < m_maxDistance || upclusUZ < m_maxDistance)) + evt.m_nmichels[i].passable_matchtype.at(2) = 3; + else + evt.m_nmichels[i].passable_matchtype.at(2) = -1; + + if (downclusXZ < m_maxDistance && + (downclusUZ < m_maxDistance || downclusVZ < m_maxDistance)) + evt.m_nmichels[i].passable_matchtype.at(3) = 4; + else if (downclusUZ < m_maxDistance && + (downclusXZ < m_maxDistance || downclusVZ < m_maxDistance)) + evt.m_nmichels[i].passable_matchtype.at(3) = 4; + else if (downclusVZ < m_maxDistance && + (downclusXZ < m_maxDistance || downclusUZ < m_maxDistance)) + evt.m_nmichels[i].passable_matchtype.at(3) = 4; + else + evt.m_nmichels[i].passable_matchtype.at(3) = -1; + + std::vector distances3D; + + if (evt.m_nmichels[i].passable_matchtype[0] == 1) + distances3D.push_back(evt.m_nmichels[i].up_to_vertex_dist3D); + if (evt.m_nmichels[i].passable_matchtype[1] == 2) + distances3D.push_back(evt.m_nmichels[i].down_to_vertex_dist3D); + if (evt.m_nmichels[i].passable_matchtype[2] == 3) + distances3D.push_back(evt.m_nmichels[i].up_clus_michel_dist3D); + if (evt.m_nmichels[i].passable_matchtype[3] == 4) + distances3D.push_back(evt.m_nmichels[i].down_clus_michel_dist3D); + if (distances3D.empty()) distances3D = {9999., 9999., 9999., 9999.}; + if (distances3D[0] == 9999.) + continue; // Comment this line out if you are making efficiency plots + // std::cout << "Passable Match Types for this Michel are " << + // evt.m_nmichels[i].passable_matchtype.at(0) << + // evt.m_nmichels[i].passable_matchtype.at(1) << + // evt.m_nmichels[i].passable_matchtype.at(2) << + // evt.m_nmichels[i].passable_matchtype.at(3) << std::endl; + + std::sort(distances3D.begin(), distances3D.end()); + + if (distances3D[0] == evt.m_nmichels[i].up_to_vertex_dist3D) { + evt.m_nmichels[i].best_XZ = evt.m_nmichels[i].up_to_vertex_XZ; + evt.m_nmichels[i].best_UZ = evt.m_nmichels[i].up_to_vertex_UZ; + evt.m_nmichels[i].best_VZ = evt.m_nmichels[i].up_to_vertex_VZ; + evt.m_nmichels[i].BestMatch = 1; + evt.m_nmichels[i].Best3Ddist = evt.m_nmichels[i].up_to_vertex_dist3D; + + // std::cout << "This Michel is UPVTX and has true endpoint " << + // evt.m_nmichels[i].trueEndpoint << std::endl; + } else if (distances3D[0] == evt.m_nmichels[i].down_to_vertex_dist3D) { + evt.m_nmichels[i].BestMatch = 2; + evt.m_nmichels[i].best_XZ = evt.m_nmichels[i].down_to_vertex_XZ; + evt.m_nmichels[i].best_UZ = evt.m_nmichels[i].down_to_vertex_UZ; + evt.m_nmichels[i].best_VZ = evt.m_nmichels[i].down_to_vertex_VZ; + evt.m_nmichels[i].Best3Ddist = evt.m_nmichels[i].down_to_vertex_dist3D; + // std::cout << "This Michel is DOWNVTX and has true endpoint " << + // evt.m_nmichels[i].trueEndpoint << std::endl; + + } else if (distances3D[0] == evt.m_nmichels[i].up_clus_michel_dist3D) { + evt.m_nmichels[i].BestMatch = 3; + evt.m_nmichels[i].best_XZ = evt.m_nmichels[i].up_to_clus_XZ; + evt.m_nmichels[i].best_UZ = evt.m_nmichels[i].up_to_clus_VZ; + evt.m_nmichels[i].best_VZ = evt.m_nmichels[i].up_to_clus_UZ; + evt.m_nmichels[i].Best3Ddist = evt.m_nmichels[i].up_clus_michvtx_dist3D; + // std::cout << "This Michel is UPCLUS and has true endpoint " << + // evt.m_nmichels[i].trueEndpoint << std::endl; + + } else if (distances3D[0] == evt.m_nmichels[i].down_clus_michel_dist3D) { + evt.m_nmichels[i].BestMatch = 4; + evt.m_nmichels[i].Best3Ddist = + evt.m_nmichels[i].down_clus_michvtx_dist3D; + evt.m_nmichels[i].best_XZ = evt.m_nmichels[i].down_to_clus_XZ; + evt.m_nmichels[i].best_UZ = evt.m_nmichels[i].down_to_clus_UZ; + evt.m_nmichels[i].best_VZ = evt.m_nmichels[i].down_to_clus_VZ; + // std::cout << "This Michel is DOWNCLUS and has true endpoint " << + // evt.m_nmichels[i].trueEndpoint << std::endl; + + } else { + evt.m_nmichels[i].BestMatch = 0; + evt.m_nmichels[i].Best3Ddist = 9999.; + evt.m_nmichels[i].best_XZ = 9999.; + evt.m_nmichels[i].best_UZ = 9999.; + evt.m_nmichels[i].best_VZ = 9999.; + continue; + } + + nmichelspass.push_back(evt.m_nmichels[i]); + } + + evt.m_nmichels.clear(); // empty existing vector of Michels + evt.m_nmichels = nmichelspass; // replace vector of michels with the vector + // of michels that passed the above cut + return !evt.m_nmichels.empty(); + } +}; diff --git a/includes/MehreenMichels/Cluster.h b/includes/MehreenMichels/Cluster.h new file mode 100644 index 0000000..4fa97fd --- /dev/null +++ b/includes/MehreenMichels/Cluster.h @@ -0,0 +1,37 @@ +#ifndef CLUSTER_H +#define CLUSTER_H + +// A cluster object will simply be the ith cluster in the event. +class Cluster; + +// using ClusterMap = std::map; + +class Cluster { + public: + // constructors + Cluster(const CVUniverse &univ, int &ci); + + int cluster_idx; + // Does this michel satisfy our quality + bool is_quality; + + double energy; + + double time; + + int view; // 1 = x, 2 = u, 3 = v + + double zpos; // z position + + double pos; +}; + +Cluster::Cluster(const CVUniverse &univ, int &ci) { + energy = univ.GetVecElem("FittedMichel_cluster_energy", ci); // MeV + time = univ.GetVecElem("FittedMichel_cluster_time", ci) / pow(10, 3); // microseconds + pos = univ.GetVecElem("FittedMichel_cluster_pos", ci); // in mm + zpos = univ.GetVecElem("FittedMichel_cluster_z", ci); // in mm + view = univ.GetVecElem("FittedMichel_cluster_view", ci); // 1 = X view, 2 = U view, 3 = V view +} + +#endif // CLUSTER_H diff --git a/includes/MehreenMichels/HasMichel_cut.h b/includes/MehreenMichels/HasMichel_cut.h new file mode 100644 index 0000000..c3eb23b --- /dev/null +++ b/includes/MehreenMichels/HasMichel_cut.h @@ -0,0 +1,89 @@ +//#include "CVUniverse.h" +//#include "Michel.h" +// Cutter written to select Michels +// PlotUtils includes +#include "PlotUtils/Cut.h" + +template +class hasMichel : public PlotUtils::Cut { + public: + hasMichel() : PlotUtils::Cut("Event Has Michel ") {} + + private: + using Michel = typename std::remove_reference().m_nmichels.front())>::type; + + bool checkCut(const UNIVERSE& univ, EVENT& evt) const { + int nmichels = univ.GetNMichels(); + evt.m_bestdist = 9999.; // setting some default value for best distance + for (int i = 0; i < nmichels; ++i) { + Michel current_michel = Michel(univ, i); + // if (current_michel.is_fitted != 1) continue; + // if (current_michel.vtx_michel_timediff < .400) continue; + // if (current_michel.pionKE < 80) continue; + // if (current_michel.true_parentpdg != 211) continue; + // if (current_michel.true_parentid != 0) continue; + if (current_michel.true_parentpdg == 211) + evt.m_ntruepiparents.push_back(current_michel); + // if (current_michel.overlay_fraction >= 0.5) continue; + // if (current_michel.true_parentpdg != 211) continue; + double dist = + current_michel.Best3Ddist; // getting the minimum pion range (vertex + // to Michel/Clus distance) + // std::cout << "Pion Energy of Current Michel is " << + // current_michel.pionKE << std::endl; + if (dist <= evt.m_bestdist) { + evt.m_bestdist = dist; + evt.m_idx = i; + + evt.m_best_XZ = current_michel.best_XZ; + evt.m_best_UZ = current_michel.best_UZ; + evt.m_best_VZ = current_michel.best_VZ; + evt.m_matchtype = current_michel.BestMatch; + int bmatch = current_michel.BestMatch; + if (bmatch == 1 || bmatch == 3) { + evt.best_x = current_michel.m_x1; + evt.best_y = current_michel.m_y1; + evt.best_z = current_michel.m_z1; + } else if (bmatch == 2 || bmatch == 4) { + evt.best_x = current_michel.m_x2; + evt.best_y = current_michel.m_y2; + evt.best_z = current_michel.m_z2; + } + evt.b_truex = current_michel.true_initialx; + evt.b_truey = current_michel.true_initialy; + evt.b_truez = current_michel.true_initialz; + } + evt.m_nmichels.push_back(current_michel); + } + // std::cout << "Now GEtting True Tpi Info from Event" << std::endl; + + double lowtpiinevent = univ.GetTrueTpi(); + evt.lowTpi = lowtpiinevent; + /* + std::vector pdgcodes = univ.GetTrueFSPDGCodes(); + int npiplus = 0; + int npiminus = 0; + int npi0 = 0; + int nkaons = 0; + int nQE = 0; + + for (int j = 0; j < pdgcodes.size(); j++) + { + int pdg = pdgcodes[j]; + if (pdg == 211) npiplus++; + else if (pdg == 111) npi0++; + else if (pdg == 321 || 311) nkaons++; + + } + + if (npiplus == 1 && npi0 == 0) evt.eventtype = 1; + else if (npiplus > 0 && npi0 > 0) evt.eventtype = 2; + else if (npiplus == 0 && npi0 > 0) evt.eventtype = 3; + else if (nkaons > 0) evt.eventtype = 4; + else {evt.eventtype = 5;} + //return true; + */ + return !evt.m_nmichels.empty(); + } +}; diff --git a/includes/MehreenMichels/Michel.h b/includes/MehreenMichels/Michel.h new file mode 100644 index 0000000..9898fe8 --- /dev/null +++ b/includes/MehreenMichels/Michel.h @@ -0,0 +1,1180 @@ +#ifndef MICHEL_H +#define MICHEL_H + +#include "CVUniverse.h" +#include "Cluster.h" + +class Michel; + +class Michel { + public: + // constructors + Michel(const CVUniverse& univ, int ci); + // Functions + Michel(){}; // fill in most basic stuff in "generic info" . this is default + // constructor you need it if you have public data members + void DoMoreInitializationAndProcessing(); // fill in more complicated stuff + // in "generic info" + void DoMatching(); // fill in "best matching stuff" + void DoesMichelMatchVtx(const CVUniverse& univ); // GEts info for Vtx Match + void DoesMichelMatchClus( + const CVUniverse& univ); // Gets info for ClusterMatch + void GetBestMatch(); // get type for best match out of all four saved matches + void GetPionAngle( + const CVUniverse& + univ); // Function to calculate pion angle with respect to beam. + // Simply gets angle between best michel endpoint and vertex. + + // std::vector CreateMichels(CVUniverse& univ); + // generic info + std::vector up_location; // upstream location 0 X 1 U 2 V 3 Z + std::vector down_location; // downstream location + double m_x1 = 9999.; // Michel Endpoint 1 x + double m_x2 = 9999.; // Michel Endpoint 2 x + double m_y1 = 9999.; // Michel Endpoint 1 y + double m_y2 = 9999.; // Michel Endpoint 2 y + double m_u1 = 9999.; // Michel Endpoint 1 u + double m_u2 = 9999.; // Michel Endpoint 2 u + double m_v1 = 9999.; // Michel Endpoint 1 v + double m_v2 = 9999.; // Michel Endpoint 2 v + double m_z1 = 9999.; // Mihel Endpoint z 1 + double m_z2 = 9999.; // Michel Endpoiint z2 + double energy = -999.; // Michel energy + double time = -999.; // Michel Time + int is_fitted = -1; // Is the Michel fitted? 0 no. 1 yes. + // Following are 2D distances (were stored in vectors but now as explicit data + // members) + double up_to_vertex_XZ = 9999.; + double up_to_vertex_UZ = 9999.; + double up_to_vertex_VZ = 9999.; + double down_to_vertex_XZ = 9999.; + double down_to_vertex_UZ = 9999.; + double down_to_vertex_VZ = 9999.; + double down_to_clus_XZ = 9999.; + double down_to_clus_UZ = 9999.; + double down_to_clus_VZ = 9999.; + double up_to_clus_XZ = 9999.; + double up_to_clus_UZ = 9999.; + double up_to_clus_VZ = 9999.; + // Michel End point to Vertex distance (up = endpoint 1 and down = endpoint + // 2) TODO: actually find out which end point is upstream or downstream + double up_to_vertex_dist3D = 9999.; + double down_to_vertex_dist3D = 9999.; + // Maybe keep a vector of clusters that matched to each endpoint? + std::vector cluster_to_up_match; + std::vector cluster_to_down_match; + // 3D distances between cluster and Michel + double up_to_cluster_dist3D = 9999.; // Distance between vertex and cluster + // that was matched to endpoint 1 + double down_to_cluster_dist3D = + 9999.; // Distance between vertex and cluster matched to endpoint 2 + double up_clus_michel_dist3D = + 9999.; // Distance between Michel endpoint 1 and clusters + double down_clus_michel_dist3D = + 9999.; // Distance between Michel endpoint 2 and clusters + double up_clus_michvtx_dist3D = + 9999.; // Distance between the Michel end point 1 that matched to + // clusters and the vertex - this will be used as pion range + double down_clus_michvtx_dist3D = + 9999.; // Distance between the Michel endpoint 2 that matched to clusters + // and the vertex - this will be used as pion range + double vtx_michel_timediff = 9999.; + + double overlay_fraction = + -1.0; // Overlay fraction of the Michel, Default if Data. 0 if MC 1 if + // Data... (maybe some events in between?) + int nclusters = 0; // number of (non-muon) clusters in the primary event + int vtx_endpoint = 0; // 1 or 2 for which Michel end point is closest + int clus_endpoint = 0; // 1 or 2 for which Michel endpoint is closest + // best matching stuff + // enum *best_cluster_match; // just tells you which of the four matches are + // the best match enum BestClusterMatch {kUpVtxMatch, kDownVtxMatch, + // kUpClusMatch, kDownClusMatch, kNClusterMatches}; the following is in place + // until i figure out how to use the enum. + int BestMatch = 0; // 0 = null match, 1= kUpVtxMatch, 2 = kDownVtxMatch, 3 = + // kUpClusMatch, 4= kDownClusMatch, + int SecondBestMatch = 0; + int tuple_idx; // index of the Michel out of all the michels saved in the + // tuple + double Best3Ddist = + 9999.; // Best 3D distance out of eitehr a vertex or a cluster match + // best 2D distance for the best type of match + double best_XZ = 9999.; + double best_UZ = 9999.; + double best_VZ = 9999.; + // Want to save the index of the clusters that the Michel best matched to. + int xclus_idx; + int uclus_idx; + int vclus_idx; + + // True initial position of the michel TODO: initial the other Michel truth + // member data here (energy, time, momentum etc) + double true_angle = 9999.; + double true_initialx = 9999.; + double true_initialy = 9999.; + double true_initialz = 9999.; + double true_e = 9999.; + double true_p = 9999.; + double true_pdg = -1.0; + int true_parentid = -1; + int true_parentpdg = -1; + double true_parent_energy = -9999.; + double true_parent_p = -9999.; + double true_parent_px = -9999.; + double true_parent_py = -9999.; + double true_parent_pz = -9999.; + double true_parent_xi = -9999.; + double true_parebt_yi = -9999.; + double true_parent_zi = -9999.; + double true_parent_xf = -9999.; + double true_parent_yf = -9999.; + double true_parent_zf = -9999.; + // the following member data were created to investigate my weird convoluted + // way of geting x and y values. Probably dont need them now. // TODO: check + // and remove the following member data + double best_angle = -9999.; + double up_clus_x = 9999.; + double up_clus_y = 9999.; + double up_clus_z = 9999.; + double down_clus_x = 9999.; + double down_clus_y = 9999.; + double down_clus_z = 9999.; + double up_vtx_x = 9999.; + double up_vtx_y = 9999.; + double up_vtx_z = 9999.; + double down_vtx_x = 9999.; + double down_vtx_y = 9999.; + double down_vtx_z = 9999.; + double is_overlay = -1; + double pionKE = -9999.; + // Adding the following member data to determine the true endpoint position of + // the Michel + int trueEndpoint = + -1; // 0 = Overlay Michel, 1 = Endpoint 1 is correct intial position of + // Michel, 2 = Endpoint 2 is correct Initial Position of Michel + // ~DeleteMichel(){delete this;}; // This is going to be the main destructor. + int recoEndpoint = -1; // -1 is default/NULL like above. 1 = Endpoint 1 is + // better match, 2 = Endpoint 2 is better match + + std::vector passable_matchtype{ + -1, -1, -1, + -1}; // This vector will contain a value for each match type -1 or the + // matchtype 1, 2, 3, 4 for UpVtx, DownVTx, Upclus, DownClus + // depending of that match type passes our 2D distance cut. (if + // distance is large, then it'll pass all of them). +}; + +// Setting the Michel data members directly from CV universe. No calculations +// made at this stage +Michel::Michel(const CVUniverse& univ, int ci) { + energy = univ.GetVecElem("FittedMichel_michel_energy", ci); + time = univ.GetVecElem("FittedMichel_michel_time", ci) / pow(10, 3); + is_fitted = univ.GetVecElem("FittedMichel_michel_fitPass", ci); + up_location.push_back(univ.GetVecElem("FittedMichel_michel_x1", ci)); + up_location.push_back(univ.GetVecElem("FittedMichel_michel_u1", ci)); + up_location.push_back(univ.GetVecElem("FittedMichel_michel_v1", ci)); + up_location.push_back(univ.GetVecElem("FittedMichel_michel_z1", ci)); + down_location.push_back(univ.GetVecElem("FittedMichel_michel_x2", ci)); + down_location.push_back(univ.GetVecElem("FittedMichel_michel_u2", ci)); + down_location.push_back(univ.GetVecElem("FittedMichel_michel_v2", ci)); + down_location.push_back(univ.GetVecElem("FittedMichel_michel_z2", ci)); + m_x1 = univ.GetVecElem("FittedMichel_michel_x1", ci); + m_y1 = univ.GetVecElem("FittedMichel_michel_y1", ci); + m_u1 = univ.GetVecElem("FittedMichel_michel_u1", ci); + m_v1 = univ.GetVecElem("FittedMichel_michel_v1", ci); + m_z1 = univ.GetVecElem("FittedMichel_michel_z1", ci); + m_x2 = univ.GetVecElem("FittedMichel_michel_x2", ci); + m_y2 = univ.GetVecElem("FittedMichel_michel_y2", ci); + m_u2 = univ.GetVecElem("FittedMichel_michel_u2", ci); + m_z2 = univ.GetVecElem("FittedMichel_michel_z2", ci); + m_v2 = univ.GetVecElem("FittedMichel_michel_v2", ci); + nclusters = univ.GetInt("FittedMichel_cluster_view_sz"); + overlay_fraction = univ.GetVecElem("FittedMichel_michel_datafraction", ci); + + true_initialx = + univ.GetVecElem("FittedMichel_reco_micheltrajectory_initialx", ci); + true_initialy = + univ.GetVecElem("FittedMichel_reco_micheltrajectory_initialy", ci); + true_initialz = + univ.GetVecElem("FittedMichel_reco_micheltrajectory_initialz", ci); + is_overlay = univ.GetVecElem("FittedMichel_michel_isoverlay", ci); + true_e = univ.GetVecElem("FittedMichel_reco_micheltrajectory_energy", ci); + true_pdg = univ.GetVecElem("FittedMichel_reco_micheltrajectory_pdg", ci); + true_parentpdg = univ.GetVecElem("FittedMichel_true_primaryparent_pdg", ci); + true_parentid = + univ.GetVecElem("FittedMichel_true_primaryparent_trackID", ci); + true_p = univ.GetVecElem("FittedMichel_reco_micheltrajectory_momentum", ci); + + double true_parentp = + univ.GetVecElem("FittedMichel_true_primaryparent_momentum", ci); + double true_parente = + univ.GetVecElem("FittedMichel_true_primaryparent_energy", ci); + double mass = mass = sqrt(pow(true_parente, 2) - pow(true_parentp, 2)); + pionKE = true_parente - mass; + + double true_parentpx = + univ.GetVecElem("FittedMichel_true_primaryparent_momentumx", ci); + double true_parentpy = + univ.GetVecElem("FittedMichel_true_primaryparent_momentumy", ci); + double true_parentpz = + univ.GetVecElem("FittedMichel_true_primaryparent_momentumz", ci); + + TVector3 truep(true_parentpx, true_parentpy, true_parentpz); + double true_theta = truep.Theta(); + true_angle = true_theta; //*TMath::RadToDeg(); + + // if (overlay_fraction < 0.5) std::cout << "True Parent of Michel is PDG: " + // << true_parentpdg << " And Parent trackID: " << true_parentid << + // std::endl; + double end1diff = + abs(true_initialz - + m_z1); // This gives a value for determining how close the + // reconstructed endpoint of the michel is to the true intial + // endpoint (the start point of where the michel decayed from) + double end2diff = + abs(true_initialz - + m_z2); // this is for endpoint 2. If you compare this to the endpoint + // that gets matched to a verted or cluster, you can determine + // which type of match ends up getting correcct matches or + // wrong matches. + + if (overlay_fraction > 0.5) + trueEndpoint = 0; + else if (true_parentpdg == 211 && end1diff < end2diff) + trueEndpoint = 1; + else if (true_parentpdg == 211 && end2diff < end1diff) + trueEndpoint = 2; + if (is_fitted == 1) { // Do theMatching for Fitted Michels + DoesMichelMatchVtx(univ); // GEts info for Vtx Match + DoesMichelMatchClus(univ); // Gets info for ClusterMatch + GetBestMatch(); + GetPionAngle(univ); + } +} + +// This function will get the angle between Michel endpoint that was matched and +// the vertex + +void Michel::GetPionAngle(const CVUniverse& univ) { + double vtx_x = univ.GetVertex().X(); // mm + double vtx_y = univ.GetVertex().Y(); // mm + double vtx_z = univ.GetVertex().Z(); // mm + + TVector3 vtx(vtx_x, vtx_y, vtx_z); + TVector3 endpoint; + + if (this->BestMatch == 1 || this->BestMatch == 3) + endpoint.SetXYZ(this->m_x1, this->m_y1, this->m_z1); + else if (this->BestMatch == 2 || this->BestMatch == 4) + endpoint.SetXYZ(this->m_x2, this->m_y2, this->m_z2); + else + endpoint.SetXYZ(9999., 9999., 9999.); + + TVector3 range = endpoint - vtx; + double angle = univ.thetaWRTBeam(range.x(), range.y(), range.z()); + this->best_angle = angle; //*TMath::RadToDeg(); // in Degrees +} + +// This function will get an integer for the best match type of the Michel. +// It compares distance between MIchel and whatever it's best match is to find +// the Best type of Michel for a single Michel. + +void Michel::GetBestMatch() { + int upvtxmatch = 0; + int downvtxmatch = 0; + int upclusmatch = 0; + int downclusmatch = 0; + + // if (this->up_to_vertex_dist3D != NULL && ) + + // std::cout << "GET BEST MATCH FOR THIS MICHEL " << std::endl; + // This is setting the values for which endpoint is a better match for each + // type. TODO: Revise this function. There has to be a better way to compare + // distances than what I wrote. + + std::vector distances{ + this->up_to_vertex_dist3D, this->down_to_vertex_dist3D, + this->up_clus_michel_dist3D, this->down_clus_michel_dist3D}; + // std::cout << "The distances for this michel are upvtx, downvtx, upclus, + // downclus " << distances[0] << " - " << distances[1] << " - " << distances[2] + // << " - " << distances[3] << " - " << std::endl; + std::sort(distances.begin(), distances.end()); + + // std::cout << "The distances after sorting " << distances[0] << " - " << + // distances[1] << " - " << distances[2] << " - " << distances[3] << " - " << + // std::endl; + // This bit of code will try to force the true Endpoint of hte Michel + /* + if (trueEndpoint == 1) + { + if (this->up_clus_michel_dist3D > this->up_to_vertex_dist3D){ + upvtxmatch = 1; + this->best_XZ = this->up_to_vertex_XZ; + this->best_UZ = this->up_to_vertex_UZ; + this->best_VZ = this->up_to_vertex_VZ; + this->BestMatch = 1; + this->Best3Ddist = this->up_to_vertex_dist3D; + + } + else if (this->up_clus_michel_dist3D < this->up_to_vertex_dist3D) + { + this->BestMatch = 3; + this->best_XZ = this->up_to_clus_XZ; + this->best_UZ = this->up_to_clus_VZ; + this->best_VZ = this->up_to_clus_UZ; + this->Best3Ddist = this->up_clus_michvtx_dist3D; + upclusmatch = 1; + } + } + else if (trueEndpoint == 2) + { + if (this->down_to_vertex_dist3D < this->down_clus_michel_dist3D) + { + this->BestMatch = 2; + this->best_XZ = this->down_to_vertex_XZ; + this->best_UZ = this->down_to_vertex_UZ; + this->best_VZ = this->down_to_vertex_VZ; + this->Best3Ddist = this->down_to_vertex_dist3D; + downvtxmatch = 1; + } + else if (this->down_to_vertex_dist3D > this->down_clus_michel_dist3D) + { + this->BestMatch = 4; + this->Best3Ddist = this->down_clus_michvtx_dist3D; + this->best_XZ = this->down_to_clus_XZ; + this->best_UZ = this->down_to_clus_UZ; + this->best_VZ = this->down_to_clus_VZ; + downclusmatch = 1; + } + } + else{ + this->BestMatch = 0; + this->Best3Ddist = 9999.; + this->best_XZ = 9999.; + this->best_UZ = 9999.; + this->best_VZ = 9999.; + } +*/ + // This bit of code will try to find the best 3D distance end point + if (distances[0] == this->up_to_vertex_dist3D) { + upvtxmatch = 1; + this->best_XZ = this->up_to_vertex_XZ; + this->best_UZ = this->up_to_vertex_UZ; + this->best_VZ = this->up_to_vertex_VZ; + this->BestMatch = 1; + this->Best3Ddist = this->up_to_vertex_dist3D; + // std::cout << "This Michel is UPVTX and has true endpoint " << + // this->trueEndpoint << std::endl; + } else if (distances[0] == this->down_to_vertex_dist3D) { + this->BestMatch = 2; + this->best_XZ = this->down_to_vertex_XZ; + this->best_UZ = this->down_to_vertex_UZ; + this->best_VZ = this->down_to_vertex_VZ; + this->Best3Ddist = this->down_to_vertex_dist3D; + downvtxmatch = 1; + // std::cout << "This Michel is DOWNVTX and has true endpoint " << + // this->trueEndpoint << std::endl; + + } else if (distances[0] == this->up_clus_michel_dist3D) { + this->BestMatch = 3; + this->best_XZ = this->up_to_clus_XZ; + this->best_UZ = this->up_to_clus_VZ; + this->best_VZ = this->up_to_clus_UZ; + this->Best3Ddist = this->up_clus_michvtx_dist3D; + upclusmatch = 1; + // std::cout << "This Michel is UPCLUS and has true endpoint " << + // this->trueEndpoint << std::endl; + + } else if (distances[0] == this->down_clus_michel_dist3D) { + this->BestMatch = 4; + this->Best3Ddist = this->down_clus_michvtx_dist3D; + this->best_XZ = this->down_to_clus_XZ; + this->best_UZ = this->down_to_clus_UZ; + this->best_VZ = this->down_to_clus_VZ; + downclusmatch = 1; + // std::cout << "This Michel is DOWNCLUS and has true endpoint " << + // this->trueEndpoint << std::endl; + + } else { + this->BestMatch = 0; + this->Best3Ddist = 9999.; + this->best_XZ = 9999.; + this->best_UZ = 9999.; + this->best_VZ = 9999.; + } + + if (distances[1] == this->up_to_vertex_dist3D) + this->SecondBestMatch == 1; + else if (distances[1] == this->down_to_vertex_dist3D) + this->SecondBestMatch == 2; + else if (distances[1] == this->up_clus_michel_dist3D) + this->SecondBestMatch == 3; + else if (distances[1] == this->down_clus_michel_dist3D) + this->SecondBestMatch == 4; + else { + this->SecondBestMatch == 0; + } + /* + if (upvtxmatch == 1 && (upclusmatch == 1 || downclusmatch == 1 )){ + if (this->up_to_vertex_dist3D < this->up_clus_michel_dist3D) { + //std::cout << "UPVTX IS BEST " << std::endl; + this->best_XZ = this->up_to_vertex_XZ; + this->best_UZ = this->up_to_vertex_UZ; + this->best_VZ = this->up_to_vertex_VZ; + this->BestMatch = 1; + this->Best3Ddist = this->up_to_vertex_dist3D; + } + else if (this->up_to_vertex_dist3D >= this->up_clus_michel_dist3D) + { + //std::cout << "UPCLUS IS BEST " << std::endl; + this->BestMatch = 3; + this->best_XZ = this->up_to_clus_XZ; + this->best_UZ = this->up_to_clus_VZ; + this->best_VZ = this->up_to_clus_UZ; + this->Best3Ddist = this->up_clus_michvtx_dist3D; + } + else if (this->up_to_vertex_dist3D < this->down_clus_michel_dist3D) { + //std::cout << "UPVTX IS BEST " << std::endl; + this->BestMatch = 1; + this->Best3Ddist = this->up_to_vertex_dist3D; + this->best_XZ = this->up_to_vertex_XZ; + this->best_UZ = this->up_to_vertex_UZ; + this->best_VZ = this->up_to_vertex_VZ; + } + else if (this->up_to_vertex_dist3D >= this->down_clus_michel_dist3D) + { + //std::cout << "DOWNCLUSISBEST" << std::endl; + this->BestMatch = 4; + this->Best3Ddist = this->down_clus_michvtx_dist3D; + this->best_XZ = this->down_to_clus_XZ; + this->best_UZ = this->down_to_clus_UZ; + this->best_VZ = this->down_to_clus_VZ; + } + } + else if (downvtxmatch == 1 && (upclusmatch == 1 || downclusmatch == 1 )){ + if (this->down_to_vertex_dist3D < this->up_clus_michel_dist3D) { + //std::cout << "DOWNVTX IS BEST " << std::endl; + + this->BestMatch = 2; + this->best_XZ = this->down_to_vertex_XZ; + this->best_UZ = this->down_to_vertex_UZ; + this->best_VZ = this->down_to_vertex_VZ; + this->Best3Ddist = this->down_to_vertex_dist3D; + + } + else if (this->down_to_vertex_dist3D >= this->up_clus_michel_dist3D) + { + //std::cout << "UPCLUS IS BEST " << std::endl; + this->BestMatch = 3; + this->Best3Ddist = this->up_clus_michvtx_dist3D; + this->best_XZ = this->up_to_clus_XZ; + this->best_UZ = this->up_to_clus_UZ; + this->best_VZ = this->up_to_clus_VZ; + } + else if (this->down_to_vertex_dist3D < this->down_clus_michel_dist3D) + { + //std::cout << "DOWNVTX IS BEST " << std::endl; + + this->BestMatch = 2; + this->best_XZ = this->down_to_vertex_XZ; + this->best_UZ = this->down_to_vertex_UZ; + this->best_VZ = this->down_to_vertex_VZ; + //std::cout << "Setting Best Dist" << std::endl; + this->Best3Ddist = this->down_to_vertex_dist3D; + + } + else if (this->down_to_vertex_dist3D >= this->down_clus_michel_dist3D) + { + + //std::cout << "DOWNCLUSISBEST" << std::endl; + this->BestMatch = 4; + this->Best3Ddist = this->up_clus_michvtx_dist3D; + this->best_XZ = this->down_to_clus_XZ; + this->best_UZ = this->down_to_clus_UZ; + this->best_VZ = this->down_to_clus_VZ; + } + + }*/ + // else{ + // this->BestMatch = 0; + // this->Best3Ddist = 9999.; + // this->best_XZ = 9999.; + // this->best_UZ = 9999.; + // this->best_VZ = 9999.; + // } + // std::cout << "The Best Match for this Michel is " << this->BestMatch << + // std::endl; + int matchtype = this->BestMatch; + // Identifying the best reco endpoint based on the Best MAtch type. + if (matchtype == 1 || matchtype == 3) + this->recoEndpoint = 1; + else if (matchtype == 2 || matchtype == 4) + this->recoEndpoint = 2; +} + +void Michel::DoesMichelMatchVtx(const CVUniverse& univ) { + // std::cout << "GETTING VTX MATCH FOR MICHEL " << std::endl; + + // Getting Vertex Information + double vtx_x = univ.GetVertex().X(); // mm + double vtx_y = univ.GetVertex().Y(); // mm + double vtx_z = univ.GetVertex().Z(); // mm + double vtx_t = univ.GetVertex().T() / pow(10, 3); // mus + double vtx_u = (0.5 * (vtx_x - sqrt(3.) * vtx_y)); + double vtx_v = (0.5 * (vtx_x + sqrt(3.) * vtx_y)); + + // std::cout << "VTX POSITION is (x, u , v, y, z) (" << vtx_x << " , " << + // vtx_u << " , " << vtx_v << " , " << vtx_y << " , " << vtx_z << std::endl; + // Initializing all the distance comparisons I will need to make + double zdiff1 = vtx_z - this->m_z1; + double zdiff2 = vtx_z - this->m_z2; + double xdiff = 9999.; + double udiff = 9999.; + double vdiff = 9999.; + double XZdist = 9999.; + double UZdist = 9999.; + double VZdist = 9999.; + + double michely1 = this->m_y1; + double michely2 = this->m_y2; + double michelx1 = this->m_x1; + double michelx2 = this->m_z2; + double michelz1 = this->m_z1; + double michelz2 = this->m_z2; + double timediff = (this->time) - vtx_t; + // std::cout << "Michel time " << this->time << " Vertex time " << vtx_t << + // "\n" << std::endl; + this->vtx_michel_timediff = timediff; + + // 2D distance calculations for Endpoint 1 + xdiff = abs(vtx_x - this->m_x1); + udiff = abs(vtx_u - this->m_u1); + vdiff = abs(vtx_v - this->m_v1); + + XZdist = sqrt(xdiff * xdiff + zdiff1 * zdiff1); + UZdist = sqrt(udiff * udiff + zdiff1 * zdiff1); + VZdist = sqrt(vdiff * vdiff + zdiff1 * zdiff1); + + this->up_to_vertex_XZ = XZdist; + this->up_to_vertex_UZ = UZdist; + this->up_to_vertex_VZ = VZdist; + + // 2D Distance calculations for endpoint2 + xdiff = abs(vtx_x - this->m_x2); + udiff = abs(vtx_u - this->m_u2); + vdiff = abs(vtx_v - this->m_v2); + XZdist = sqrt(xdiff * xdiff + zdiff2 * zdiff2); + UZdist = sqrt(udiff * udiff + zdiff2 * zdiff2); + VZdist = sqrt(vdiff * vdiff + zdiff2 * zdiff2); + + this->down_to_vertex_XZ = XZdist; + this->down_to_vertex_UZ = UZdist; + this->down_to_vertex_VZ = VZdist; + + // 3D distance calculations + double xdiff1 = abs(vtx_x - this->m_x1); + double xdiff2 = abs(vtx_x - this->m_x2); + double ydiff1 = abs(vtx_y - this->m_y1); + double ydiff2 = abs(vtx_y - this->m_y2); + + double dist1 = sqrt(zdiff1 * zdiff1 + xdiff1 * xdiff1 + ydiff1 * ydiff1); + double dist2 = sqrt(zdiff2 * zdiff2 + xdiff2 * xdiff2 + ydiff2 * ydiff2); + + this->up_to_vertex_dist3D = dist1; + this->down_to_vertex_dist3D = dist2; + + if (dist1 < dist2) + this->vtx_endpoint = 1; + else if (dist2 < dist1) + this->vtx_endpoint = 2; + + ////std::cout << "END OF LOOP TO FIND VTX MATCH" << std::endl; +} + +void Michel::DoesMichelMatchClus(const CVUniverse& univ) { + // This is where the function for Cluster Matching goes + + ////std::cout << "STARTING SEARCH FOR CLUSTER MATCH " << std::endl; + // Inititalizing vertex variables needed for cluster matching + int nclusters = this->nclusters; + // std::cout << "There are " << nclusters << " available clusters for this + // matching " << std::endl; + double vtx_x = univ.GetVertex().X(); // mm + double vtx_y = univ.GetVertex().Y(); // mm + double vtx_z = univ.GetVertex().Z(); // mm + double vtx_t = univ.GetVertex().T() / pow(10, 3); // mus + double vtx_u = (0.5 * (vtx_x - sqrt(3.) * vtx_y)); + double vtx_v = (0.5 * (vtx_x + sqrt(3.) * vtx_y)); + + double closestdistance1x = 9999.; + double closestdistance1u = 9999.; + double closestdistance1v = 9999.; + double closestdistance1z = 9999.; + + double closestdistance2x = 9999.; + double closestdistance2u = 9999.; + double closestdistance2v = 9999.; + double closestdistance2z = 9999.; + + double michelx1 = this->m_x1; + double michelx2 = this->m_x2; + double michelu1 = this->m_u1; + double michelu2 = this->m_u2; + double michelv1 = this->m_v1; + double michelv2 = this->m_v2; + double michelz1 = this->m_z1; + double michelz2 = this->m_z2; + double michely1 = this->m_y1; + double michely2 = this->m_y2; + + double micheltime = this->time; + + // std::cout << "Michel position 1 is (x, u, v, y, z) " << michelx1 << " , " + // << michelu1 << " , " << michelv1 << " , "<< michely1 << " , " << michelz1 + // << std::endl; + + // std::cout << "Michel position 2 is (x, y, v, y, z) " << michelx2 << " , " + // << michelu2 << " , " << michelv2 << " , " << michely2 << " , " << michelz2 + // << std::endl; + + std::vector endpoint1_clus; + std::vector endpoint2_clus; + + // Get the closest distance for each view + + ////std::cout << "STARTING LOOP OVER CLUSTERS " << std::endl; + int x1_idx = -1; // want to save the index for each closest cluster + int u1_idx = -1; + int v1_idx = -1; + int x2_idx = -1; + int u2_idx = -1; + int v2_idx = -1; + + for (int i = 0; i < nclusters; i++) { + Cluster current_cluster = Cluster(univ, i); + + double energy = current_cluster.energy; + double time = current_cluster.time; + double pos = current_cluster.pos; + double zpos = current_cluster.zpos; + int view = current_cluster.view; + double timediff = micheltime - time; + + if (energy < 2.) continue; // only get clusters greater than 2 MeV + + // std::cout << "printing cluster info " << "energy " << energy << " time " + // << time << " pos " << pos << " zpos " << zpos << std::endl; + + double zdiff1 = abs(zpos - michelz1); + double zdiff2 = abs(zpos - michelz2); + // Calculating 2D distance in X view + if (view == 1) { + // Endpoint 1 calculations + double xdiff1 = abs(pos - michelx1); + + double x2Ddistance1 = sqrt(xdiff1 * xdiff1 + zdiff1 * zdiff1); + + // Endpoint 2 Calculations + double xdiff2 = abs(pos - michelx2); + + double x2Ddistance2 = sqrt(xdiff2 * xdiff2 + zdiff2 * zdiff2); + + if (x2Ddistance1 <= closestdistance1x) { + closestdistance1x = + x2Ddistance1; // this is redundant if I just use index instead + x1_idx = i; + } + if (x2Ddistance2 <= closestdistance2x) { + closestdistance2x = x2Ddistance2; + x2_idx = i; + } + } else if (view == 2) // Calculating 2D distance in U view + { + // Endpoint 2 Calculations + double udiff1 = abs(pos - michelu1); + + double u2Ddistance1 = sqrt(udiff1 * udiff1 + zdiff1 * zdiff1); + + // Endpoint 1 Calculations + double udiff2 = abs(pos - michelu2); + + double u2Ddistance2 = sqrt(udiff2 * udiff2 + zdiff2 * zdiff2); + + if (u2Ddistance1 < closestdistance1u) { + closestdistance1u = u2Ddistance1; + u1_idx = i; + } + if (u2Ddistance2 < closestdistance2u) { + closestdistance2u = u2Ddistance2; + u2_idx = i; + } + + } else if (view == 3) // Calculating 2D dsitance in V view + { + // Endpoint 1 Calculations + double vdiff1 = abs(pos - michelv1); + + double v2Ddistance1 = sqrt(vdiff1 * vdiff1 + zdiff1 * zdiff1); + // Endpoint 2 Calculations + double vdiff2 = abs(pos - michelv2); + + double v2Ddistance2 = sqrt(vdiff2 * vdiff2 + zdiff2 * zdiff2); + + if (v2Ddistance1 < closestdistance1v) { + closestdistance1v = v2Ddistance1; + v1_idx = i; + } + if (v2Ddistance2 <= closestdistance2v) { + closestdistance2v = v2Ddistance2; + v2_idx = i; + } + } + } + + // std::cout << "Printing closest clusters index to each end point: x1: " << + // x1_idx << " u1: " << u1_idx << " v1: " << v1_idx << " x2: " << x2_idx << " + // u2: " << u2_idx << " v2: " << v2_idx << std::endl; + + // Now store the closest X, u, v clusters for each Michel Endpoint based on + // the above closest distance + + // Closest cluster's index will be used to only + + std::vector clusx1; + std::vector clusx2; + + std::vector clusu1; + std::vector clusu2; + + std::vector clusv1; + std::vector clusv2; + for (int i = 0; i < nclusters; i++) { + Cluster current_cluster = Cluster(univ, i); + + double energy = current_cluster.energy; + double time = current_cluster.time; + double pos = current_cluster.pos; + double zpos = current_cluster.zpos; + int view = current_cluster.view; + double timediff = micheltime - time; + if (energy < 2.) continue; + // std::cout << "Printing details about cluster "<< i << " : " << energy + // << " : " << time << " : " << pos << " : " << zpos << " : " << view << " + // : " << timediff << std::endl; + + double zdiff1 = abs(zpos - michelz1); + double zdiff2 = abs(zpos - michelz2); + + // Saving clusters with distances equal to the closest clusters. Again, this + // is probably not the best way. I need to rewrite this section to just use + // clusters from the index I saved in the previous loop. That way I reduce + // the number of clusters that I have to loop over. + + if (view == 1) { + // Endpoint 1 + + if (i == x1_idx) { // if current index is same as the closest endpoint 1 + // cluster in X View + endpoint1_clus.push_back(current_cluster); + this->cluster_to_up_match.push_back( + current_cluster); // this one is redundant + // std::cout << "Printing details about Endpoint + // 1 X cluster index "<< i << " energy : " << + // energy << " time : " << time << " pos : " << + // pos << " zpos : " << zpos << " view : " << view + // << " timedifference with Michel : " << timediff + // << std::endl; + + clusx1.push_back(pos); + clusx1.push_back(zpos); + } + if (i == x2_idx) { // if current index is same as the closest endpoint 2 + // cluster in X View + endpoint2_clus.push_back(current_cluster); + this->cluster_to_down_match.push_back(current_cluster); // TODO remove + // std::cout << "Printing details about Endpoint 2 X cluster "<< i << " + // energy : " << energy << " time : " << time << " pos : " << pos << " + // zpos: " << zpos << " view : " << view << " timediff : " << timediff << + // std::endl; + + clusx2.push_back(pos); + clusx2.push_back(zpos); + } + + } else if (view == 2) { + if (i == u1_idx) { // if current index is same as the closest endpoint 1 + // cluster in U View + endpoint1_clus.push_back(current_cluster); + this->cluster_to_up_match.push_back(current_cluster); // Remove? + // std::cout << "Printing details about Endpoint 1 U cluster "<< i << " + // : " << energy << " : " << time << " : " << pos << " : " << zpos << " + // : " << view << " : " << timediff << std::endl; + + clusu1.push_back(pos); + clusu1.push_back(zpos); + } + if (i == u2_idx) // if current index is same as the closest endpoint 2 + // cluster in U View + { + endpoint2_clus.push_back(current_cluster); + this->cluster_to_down_match.push_back(current_cluster); // remove? + // std::cout << "Printing details about Endpoint 2 U cluster "<< i << " + // : " << energy << " : " << time << " : " << pos << " : " << zpos << " + // : " << view << " : " << timediff << std::endl; + + clusu2.push_back(pos); + clusu2.push_back(zpos); + } + } else if (view == 3) { + if (i == v1_idx) { // if current index is same as the closest endpoint 1 + // cluster in V View + endpoint1_clus.push_back(current_cluster); + this->cluster_to_up_match.push_back(current_cluster); // remove? + // std::cout << "Printing details about Endpoint 1 V cluster "<< i << " + // : " << energy << " : " << time << " : " << pos << " : " << zpos << " + // : " << view << " : " << timediff << std::endl; + + clusv1.push_back(pos); + clusv1.push_back(zpos); + } + if (i == v2_idx) { // if current index is same as the closest endpoint 2 + // cluster in V View + endpoint1_clus.push_back(current_cluster); + this->cluster_to_down_match.push_back(current_cluster); // remove? + // std::cout << "Printing details about Endpoint 2 V cluster "<< i << " + // : " << energy << " : " << time << " : " << pos << " : " << zpos << " + // : " << view << " : " << timediff << std::endl; + + clusv2.push_back(pos); + clusv2.push_back(zpos); + } + } + + } // End of loop over clusters + + // This is vector of positions for each endpoint cluster match + std::vector matchclus1; // index [0] = x, [1] = y, [2] = z + std::vector matchclus2; // index [0] = x, [1] = y, [2] = z + + // std::cout << "LOOPING OVER ENDPOINT1 CLUSTERS" << std::endl; + + double XZdist1 = 9999.; + double UZdist1 = 9999.; + double VZdist1 = 9999.; + + // Check if our cluster vectors are empty and calculate 2D distances for + // endpoint 1 + if (!clusx1.empty()) { + double xdif = abs(this->m_x1 - clusx1[0]); + double zdif = abs(this->m_z1 - clusx1[1]); + XZdist1 = sqrt(xdif * xdif + zdif * zdif); + } + if (!clusu1.empty()) { + double udif = abs(this->m_u1 - clusu1[0]); + double zdif = abs(this->m_z1 - clusu1[1]); + UZdist1 = sqrt(udif * udif + zdif * zdif); + } + if (!clusv1.empty()) { + double vdif = abs(this->m_v1 - clusv1[0]); + double zdif = abs(this->m_z1 - clusv1[1]); + VZdist1 = sqrt(vdif * vdif + zdif * zdif); + } + + // std::cout << " XZ, UZ, VZ 1: " << XZdist1 << " , " << UZdist1 << " , " << + // VZdist1 << std::endl; Saving the 2D distances for endpoint 1 + this->up_to_clus_XZ = XZdist1; + this->up_to_clus_UZ = UZdist1; + this->up_to_clus_VZ = VZdist1; + + double XZdist2 = 9999.; + double UZdist2 = 9999.; + double VZdist2 = 9999.; + + if (!clusx2.empty()) { + double xdif = abs(this->m_x2 - clusx2[0]); + double zdif = abs(this->m_z2 - clusx2[1]); + XZdist2 = sqrt(xdif * xdif + zdif * zdif); + } + if (!clusu2.empty()) { + double udif = abs(this->m_u2 - clusu2[0]); + double zdif = abs(this->m_z2 - clusu2[1]); + UZdist2 = sqrt(udif * udif + zdif * zdif); + } + if (!clusv2.empty()) { + double vdif = abs(this->m_v2 - clusv2[0]); + double zdif = abs(this->m_z2 - clusv2[1]); + VZdist2 = sqrt(vdif * vdif + zdif * zdif); + } + + this->down_to_clus_XZ = XZdist2; + this->down_to_clus_UZ = UZdist2; + this->down_to_clus_VZ = VZdist2; + this->down_clus_y = michely2; + this->down_clus_x = michelx2; + this->down_clus_z = michelz2; + + // std::cout << "GET 3D Information for Clusters - ENDPOINT1" << std::endl; + // This is the convoluted system that calculates the Endpoint + if (XZdist1 < UZdist1 && XZdist1 < VZdist1 && + UZdist1 < VZdist1) { // XU views closest + if (!clusu1.empty() && !clusx1.empty()) { + double yclus = (1. / sqrt(3.)) * (clusx1[0] - 2 * clusu1[0]); + matchclus1.push_back(clusx1[0]); + matchclus1.push_back(yclus); // y point of match 3D point + matchclus1.push_back(clusx1[1]); // setting the cluster 3D point z to be + // of the closest view + // std::cout << "The 2 closest clusters to EndPoint 1 are X and U with (x, + // y, z) point ( " << clusx1[0] << " , " << yclus << " , " << clusx1[1] << + // " ) " << std::endl; + } + } else if (XZdist1 < UZdist1 && XZdist1 < VZdist1 && + UZdist1 > VZdist1) { // XV closest + if (!clusv1.empty() && !clusx1.empty()) { + double yclus = (1. / sqrt(3.)) * (2 * clusv1[0] - clusx1[0]); + // std::cout << "The 2 closest clusters to Endpoint 1 are X and V with (x, + // y, z) point ( " << clusx1[0] << " , " << yclus << " , " << clusx1[1] << + // " ) " << std::endl; + + matchclus1.push_back(clusx1[0]); + matchclus1.push_back(yclus); + matchclus1.push_back(clusx1[1]); // seting the cluster 3D point z to be + // of the closest view + } + } else if (UZdist1 < XZdist1 && UZdist1 < VZdist1 && + VZdist1 < XZdist1) { // UV closest + if (!clusv1.empty() && !clusu1.empty()) { + double yclus = (1. / sqrt(3.)) * (clusv1[0] - clusu1[0]); + double xclus = clusu1[0] + clusv1[0]; + // std::cout << "The 2 closest clusters to EndPoint 1 are U and V with (x, + // y, z) point ( " << xclus << " , " << yclus << " , " << clusu1[1] << " ) + // " << std::endl; + + matchclus1.push_back(xclus); + matchclus1.push_back(yclus); + matchclus1.push_back(clusu1[1]); // seting the cluster 3D point z to be + // of the closest view + } + } else if (UZdist1 < XZdist1 && UZdist1 < VZdist1 && + VZdist1 > XZdist1) { // UX closest + if (!clusu1.empty() && !clusx1.empty()) { + double yclus = (1. / sqrt(3.)) * (clusx1[0] - 2 * clusu1[0]); + // std::cout << "The 2 closest clusters to EndPoint 1 are U and X with (x, + // y, z) point ( " << clusx1[0] << " , " << yclus << " , " << clusu1[1] << + // " ) " << std::endl; + + matchclus1.push_back(clusx1[0]); + matchclus1.push_back(yclus); + matchclus1.push_back(clusu1[1]); // seting the cluster 3D point z to be + // of the closest view + this->up_clus_y = michely1; + } + } else if (VZdist1 < XZdist1 && VZdist1 < UZdist1 && + XZdist1 < UZdist1) { // VX closest + if (!clusv1.empty() && !clusx1.empty()) { + double yclus = ((1. / sqrt(3.)) * (2 * clusv1[0] - clusx1[0])); + // std::cout << "The 2 closest clusters to EndPoint 1 are V and X with (x, + // y, z) point ( " << clusx1[0] << " , " << yclus << " , " << clusv1[1] << + // " ) " << std::endl; + + matchclus1.push_back(clusx1[0]); + matchclus1.push_back(yclus); + matchclus1.push_back(clusv1[1]); // seting the cluster 3D point z to be + // of the closest view + this->up_clus_y = michely1; + } + } else if (VZdist1 < XZdist1 && VZdist1 < UZdist1 && + XZdist1 > UZdist1) { // VU closest + if (!clusu1.empty() && !clusv1.empty()) { + double xclus = (1. / sqrt(3.)) * (clusv1[0] - clusu1[0]); + double yclus = clusu1[0] + clusv1[0]; + // std::cout << "The 2 closest clusters to EndPoint 1 are V and U with (x, + // y, z) point ( " << xclus << " , " << yclus << " , " << clusv1[1] << " ) + // " << std::endl; + + matchclus1.push_back(xclus); + matchclus1.push_back(yclus); + matchclus1.push_back(clusv1[1]); // seting the cluster 3D point z to be + // of the closest view + this->up_clus_y = michely1; + this->up_clus_x = michelx1; + } + } + if (XZdist2 < UZdist2 && XZdist2 < VZdist2 && UZdist2 < VZdist2) { + // std::cout << "XU closest to endpoint 2" << std::endl; + if (!clusu2.empty() && !clusx2.empty()) { + double yclus = (1. / sqrt(3.)) * (clusx2[0] - 2 * clusu2[0]); + // std::cout << "The 2 closest clusters to EndPoint 2 are X and U with (x, + // y, z) point ( " << clusx2[0] << " , " << yclus << " , " << clusx2[1] << + // " ) " << std::endl; + + matchclus2.push_back(clusx2[0]); + matchclus2.push_back(yclus); + matchclus2.push_back(clusx2[1]); // seting the cluster 3D point z to be + // of the closest view + + this->down_clus_y = michely2; + } + } else if (XZdist2 < UZdist2 && XZdist2 < VZdist2 && UZdist2 > VZdist2) { + // std::cout << "XV closest to endpoint 2" << std::endl; + if (!clusv2.empty() && !clusx2.empty()) { + double yclus = (1. / sqrt(3.)) * (2 * clusv2[0] - clusx2[0]); + // std::cout << "The 2 closest clusters to EndPoint 2 are X and V with (x, + // y, z) point ( " << clusx2[0] << " , " << yclus << " , " << clusx2[1] << + // " ) " << std::endl; + + matchclus2.push_back(clusx2[0]); + matchclus2.push_back(yclus); + matchclus2.push_back(clusx2[1]); // seting the cluster 3D point z to be + // of the closest view + this->down_clus_y = michely2; + } + } else if (UZdist2 < XZdist2 && UZdist2 < VZdist2 && VZdist2 < XZdist2) { + // std::cout << "UV closest to endpoint 2" << std::endl; + if (!clusu2.empty() && !clusv2.empty()) { + double xclus = clusu2[0] + clusv2[0]; + double yclus = (1. / sqrt(3.)) * (clusv2[0] - clusu2[0]); + // std::cout << "The 2 closest clusters to EndPoint 2 are U and V with (x, + // y, z) point ( " << xclus << " , " << yclus << " , " << clusu2[1] << " ) + // " << std::endl; + + matchclus2.push_back(xclus); + matchclus2.push_back(yclus); + matchclus2.push_back(clusu2[1]); // seting the cluster 3D point z to be + // of the closest view + this->down_clus_y = michely2; + this->down_clus_y = michelx2; + } + } else if (UZdist2 < XZdist2 && UZdist2 < VZdist2 && VZdist2 > XZdist2) { + // std::cout << "UX closest to endpoint 2" << std::endl; + if (!clusu2.empty() && !clusx2.empty()) { + double yclus = (1. / sqrt(3.)) * (clusx2[0] - 2 * clusu2[0]); + // std::cout << "The 2 closest clusters to EndPoint 2 are U and X with (x, + // y, z) point ( " << clusx2[0] << " , " << yclus << " , " << clusu2[1] << + // " ) " << std::endl; + + matchclus2.push_back(clusx2[0]); + matchclus2.push_back(yclus); + matchclus2.push_back(clusu2[1]); // seting the cluster 3D point z to be + // of the closest view + this->down_clus_y = michely2; + } + } else if (VZdist2 < XZdist2 && VZdist2 < UZdist2 && XZdist2 < UZdist2) { + // std::cout << "VX closest to endpoint 2" << std::endl; + if (!clusv2.empty() && !clusx2.empty()) { + double yclus = ((1. / sqrt(3.)) * (2 * clusv2[0] - clusx2[0])); + // std::cout << "The 2 closest clusters to EndPoint 2 are V and X with (x, + // y, z) point ( " << clusx2[0] << " , " << yclus << " , " << clusv2[1] << + // " ) " << std::endl; + + matchclus2.push_back(clusx2[0]); + matchclus2.push_back(yclus); + matchclus2.push_back(clusv2[1]); // seting the cluster 3D point z to be + // of the closest view + this->down_clus_y = michely2; + } + } else if (VZdist2 < XZdist2 && VZdist2 < UZdist2 && XZdist2 > UZdist2) { + // std::cout << "VU closest to endpoint 2" << std::endl; + if (!clusu2.empty() && !clusv2.empty()) { + double xclus = (1. / sqrt(3.)) * (clusv2[0] - clusu2[0]); + double yclus = clusu2[0] + clusv2[0]; + // std::cout << "The 2 closest clusters to EndPoint 2 are V and U with (x, + // y, z) point ( " << xclus << " , " << yclus << " , " << clusv2[1] << " ) + // " << std::endl; + + matchclus2.push_back(xclus); + matchclus2.push_back(yclus); + matchclus2.push_back(clusv2[1]); // seting the cluster 3D point z to be + // of the closest view + this->down_clus_y = michely2; + this->down_clus_x = michelx2; + } + } + + // std::cout << "GETTING 3D DISTANCES FOR THE CUSTERS " << std::endl; + double clusx1diff = 9999.; + double clusy1diff = 9999.; + double clusz1diff = 9999.; + + double mclusx1diff = 9999.; + double mclusy1diff = 9999.; + double mclusz1diff = 9999.; + + double michvtx_x1diff = 9999.; + double michvtx_x2diff = 9999.; + double michvtx_y1diff = 9999.; + double michvtx_y2diff = 9999.; + double michvtx_z1diff = 9999.; + double michvtx_z2diff = 9999.; + + if (!matchclus1.empty()) { + clusx1diff = vtx_x - matchclus1[0]; + clusy1diff = vtx_y - matchclus1[1]; + clusz1diff = vtx_z - matchclus1[2]; + mclusx1diff = michelx1 - matchclus1[0]; + mclusy1diff = michely1 - matchclus1[1]; + mclusz1diff = michelz1 - matchclus1[2]; + michvtx_x1diff = michelx1 - vtx_x; + michvtx_y1diff = michely1 - vtx_y; + michvtx_z1diff = michelz1 - vtx_z; + // std::cout << "3D point for Endpoint 1 Clusters is (x, y, z) " << + // matchclus1[0] << " , " << matchclus1[1] << " , " << matchclus1[2] << + // std::endl; + } + double clusx2diff = 9999.; + double clusy2diff = 9999.; + double clusz2diff = 9999.; + + double mclusx2diff = 9999.; + double mclusy2diff = 9999.; + double mclusz2diff = 9999.; + + if (!matchclus2.empty()) { + clusx2diff = vtx_x - matchclus2[0]; + clusy2diff = vtx_y - matchclus2[1]; + clusz2diff = vtx_z - matchclus2[2]; + mclusx2diff = michelx2 - matchclus2[0]; + mclusy2diff = michely2 - matchclus2[1]; + mclusz2diff = michelz2 - matchclus2[2]; + michvtx_x2diff = michelx2 - vtx_x; + michvtx_y2diff = michely2 - vtx_y; + michvtx_z2diff = michelz2 - vtx_z; + // std::cout << "3D point for Endpoint 2 Clusters is (x, y, z) " << + // matchclus2[0] << " , " << matchclus2[1] << " , " << matchclus2[2] << + // std::endl; + } + /// 2 types of 3D distance calculations for Cluster matching: + // 1. Cluster to Vertex + double dist1 = + sqrt(pow(clusx1diff, 2) + pow(clusy1diff, 2) + pow(clusz1diff, 2)); + double dist2 = + sqrt(pow(clusx2diff, 2) + pow(clusy2diff, 2) + pow(clusz2diff, 2)); + // 2. Cluster to Michel + double mdist1 = + sqrt(pow(mclusx1diff, 2) + pow(mclusy1diff, 2) + pow(mclusz1diff, 2)); + double mdist2 = + sqrt(pow(mclusx2diff, 2) + pow(mclusy2diff, 2) + pow(mclusz2diff, 2)); + // std::cout << " The michel endpoint 1 - cluster 3D point distance is " << + // mdist1 << std::endl; std::cout << " The michel endpoint 2 - cluster 3D point + // distance is " << mdist2 << std::endl; Saving all the distances to the Michel + // member data + this->down_clus_michel_dist3D = mdist2; + this->up_clus_michel_dist3D = mdist1; + this->up_to_cluster_dist3D = dist1; + this->down_to_cluster_dist3D = dist2; + // std::cout << "Printing 3D distances to vertex for cluster matches " << + // dist1 << " and " << dist2 << std::endl; std::cout << "Printing 3D distances + // to michel for cluster matches " << mdist1 << " and " << mdist2 << std::endl; + double michdist1 = sqrt(pow(michvtx_x1diff, 2) + pow(michvtx_y1diff, 2) + + pow(michvtx_z1diff, 2)); + double michdist2 = sqrt(pow(michvtx_x2diff, 2) + pow(michvtx_y2diff, 2) + + pow(michvtx_z2diff, 2)); + this->up_clus_michvtx_dist3D = michdist1; + this->down_clus_michvtx_dist3D = michdist2; + // Marks which endpoint is the closest match for this match type + if (dist1 < dist2) + this->clus_endpoint = 1; + else if (dist1 > dist2) + this->clus_endpoint = 2; +} + +#endif // MICHEL_H diff --git a/includes/MehreenMichels/MichelEvent.h b/includes/MehreenMichels/MichelEvent.h new file mode 100644 index 0000000..aa0f35d --- /dev/null +++ b/includes/MehreenMichels/MichelEvent.h @@ -0,0 +1,34 @@ +#ifndef MichelEvent_h +#define MichelEvent_h + +#include "CVUniverse.h" +#include "Cluster.h" +#include "Michel.h" + +struct MichelEvent { + int m_idx = -1; // Index for Best Michel in nmichels + double m_bestdist = 9999.; // in mm + std::vector m_best2D; // 0: XZ, 1: UZ, 2:VZ + double m_best_XZ = 9999.; + double m_best_UZ = 9999.; + double m_best_VZ = 9999.; + int m_matchtype; // 0 NULL 1 UPVTX 2 DOWNVTX 3 UPCLUS 4 DOWNCLUS + std::vector m_nmichels; // nmatched michels + std::vector m_ntruepiparents; // michels with true pion parent + std::vector + m_nmichelspass; // if some distance cut is applied, we can store the + // michels that passed for this event in here + double best_x = 9999.; + double best_y = 9999.; + double best_z = 9999.; + double b_truex = 9999.; + double b_truey = 9999.; + double b_truez = 9999.; + int bestparentpdg = -1; + int bestparenttrackid = -1; + int eventtype = + 0; // 0 = null, 1 = only 1 pi+ and no other pion, 2= npi+ and other pion, + // 3 = npi0 and no other pion, 4 = kaons in event, 5 = other + double lowTpi = 9999.; +}; +#endif diff --git a/xsec/mehreenEventLoop.cpp b/xsec/mehreenEventLoop.cpp new file mode 100644 index 0000000..713f493 --- /dev/null +++ b/xsec/mehreenEventLoop.cpp @@ -0,0 +1,1143 @@ +#define MC_OUT_FILE_NAME "runEventLoopMC_Dec312021_q3bin2_atleast1pi_distcuts.root" +#define DATA_OUT_FILE_NAME "runEventLoopData_Dec312021_q3bin2_atleast1pi_distcuts.root" + +#define USAGE \ +"\n*** USAGE ***\n"\ +"runEventLoop \n\n"\ +"*** Explanation ***\n"\ +"Reduce MasterAnaDev AnaTuples to event selection histograms to extract a\n"\ +"single-differential inclusive cross section for the 2021 MINERvA 101 tutorial.\n\n"\ +"*** The Input Files ***\n"\ +"Playlist files are plaintext files with 1 file name per line. Filenames may be\n"\ +"xrootd URLs or refer to the local filesystem. The first playlist file's\n"\ +"entries will be treated like data, and the second playlist's entries must\n"\ +"have the \"Truth\" tree to use for calculating the efficiency denominator.\n\n"\ +"*** Output ***\n"\ +"Produces a two files, " MC_OUT_FILE_NAME " and " DATA_OUT_FILE_NAME ", with\n"\ +"all histograms needed for the ExtractCrossSection program also built by this\n"\ +"package. You'll need a .rootlogon.C that loads ROOT object definitions from\n"\ +"PlotUtils to access systematics information from these files.\n\n"\ +"*** Environment Variables ***\n"\ +"Setting up this package appends to PATH and LD_LIBRARY_PATH. PLOTUTILSROOT,\n"\ +"MPARAMFILESROOT, and MPARAMFILES must be set according to the setup scripts in\n"\ +"those packages for systematics and flux reweighters to function.\n"\ +"If MNV101_SKIP_SYST is defined at all, output histograms will have no error bands.\n"\ +"This is useful for debugging the CV and running warping studies.\n\n"\ +"*** Return Codes ***\n"\ +"0 indicates success. All histograms are valid only in this case. Any other\n"\ +"return code indicates that histograms should not be used. Error messages\n"\ +"about what went wrong will be printed to stderr. So, they'll end up in your\n"\ +"terminal, but you can separate them from everything else with something like:\n"\ +"\"runEventLoop data.txt mc.txt 2> errors.txt\"\n" + +enum ErrorCodes +{ + success = 0, + badCmdLine = 1, + badInputFile = 2, + badFileRead = 3, + badOutputFile = 4 +}; + +//PlotUtils includes +//No junk from PlotUtils please! I already +//know that MnvH1D does horrible horrible things. +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Woverloaded-virtual" + +//Includes from this package +#include "event/CVUniverse.h" +#include "event/MichelEvent.h" +#include "systematics/Systematics.h" +#include "cuts/MaxPzMu.h" +#include "util/Variable.h" +#include "util/Variable2D.h" +#include "util/GetFluxIntegral.h" +#include "util/GetPlaylist.h" +#include "cuts/SignalDefinition.h" +#include "cuts/q3RecoCut.h" +#include "studies/Study.h" +#include "studies/PerMichelVarByGENIELabel.h" +#include "studies/PerMichelEventVarByGENIELabel.h" +#include "studies/PerMichel2DVar.h" +#include "cuts/hasMichel.h" +#include "event/Michel.h" +#include "cuts/BestMichelDistance2D.h" +#include "cuts/VtxMatchFirst.h" +#include "cuts/hasTruePion.h" +//#include "Binning.h" //TODO: Fix me + +//PlotUtils includes +#include "PlotUtils/makeChainWrapper.h" +#include "PlotUtils/HistWrapper.h" +#include "PlotUtils/Hist2DWrapper.h" +#include "PlotUtils/MacroUtil.h" +#include "PlotUtils/MnvPlotter.h" +#include "PlotUtils/CCInclusiveCuts.h" +#include "PlotUtils/CCInclusiveSignal.h" +#include "PlotUtils/CrashOnROOTMessage.h" //Sets up ROOT's debug callbacks by itself +#include "PlotUtils/Cutter.h" +#include "PlotUtils/Model.h" +#include "PlotUtils/FluxAndCVReweighter.h" +#include "PlotUtils/GENIEReweighter.h" +#include "PlotUtils/LowRecoil2p2hReweighter.h" +#include "PlotUtils/RPAReweighter.h" +#include "PlotUtils/MINOSEfficiencyReweighter.h" +#include "PlotUtils/TargetUtils.h" +#pragma GCC diagnostic pop + +//ROOT includes +#include "TParameter.h" + +//c++ includes +#include +#include //getenv() + +//============================================================================== +// Loop and Fill +//============================================================================== +void LoopAndFillEventSelection( + PlotUtils::ChainWrapper* chain, + std::map > error_bands, + std::vector vars, + std::vector vars2D, + std::vector studies, + PlotUtils::Cutter& michelcuts, + PlotUtils::Model& model) +{ + assert(!error_bands["cv"].empty() && "\"cv\" error band is empty! Can't set Model weight."); + auto& cvUniv = error_bands["cv"].front(); + + std::cout << "Starting MC reco loop...\n"; + const int nEntries = chain->GetEntries(); + for (int i=0; iSetEntry(i); + model.SetEntry(*cvUniv, cvEvent); + const double cvWeight = model.GetWeight(*cvUniv, cvEvent); + + //========================================= + // Systematics loop(s) + //========================================= + for (auto band : error_bands) + { + std::vector error_band_universes = band.second; + for (auto universe : error_band_universes) + { + MichelEvent myevent; // make sure your event is inside the error band loop. + + // Tell the Event which entry in the TChain it's looking at + universe->SetEntry(i); + + // This is where you would Access/create a Michel + + //weight is ignored in isMCSelected() for all but the CV Universe. + if (!michelcuts.isMCSelected(*universe, myevent, cvWeight).all()) continue; //all is another function that will later help me with sidebands + const double weight = model.GetWeight(*universe, myevent); //Only calculate the per-universe weight for events that will actually use it. + std::vector q3bin1weights = {0.82435, 0.830887, 0.862543, 0.917496, 0.991634, 1.08006, 1.17502, 1.2697, 1.35885, 1.43734, 1.49575, 1.51875, 1.47963, 1.34423, 1.13559, 0.918846, 0.788976, 0.735919, 0.71303, 0.706644, 0.70802, 0.710867, 0.711998}; + + std::vector q3bin2weights = {0.169059, 0.182445, 0.242976, 0.339379, 0.459126, 0.586182, 0.708931, 0.82085, 0.924898, 1.03088, 1.14148, 1.24816, 1.32363, 1.32895, 1.24746, 1.06005, 0.868318, 0.767249, 0.771477, 0.835023, 0.913111, 0.971778, 0.987021}; + + std::vector q3bin3weights = {0.406167, 0.400722, 0.381415, 0.359252, 0.345346, 0.355835, 0.406526, 0.510034, 0.666337, 0.857511, 1.04733 , 1.20275 , 1.311 , 1.36494 , 1.34045 , 1.23871 , 1.09418 , 0.959903, 0.908177, 0.930722, 0.995452, 1.05769 , 1.07703 }; + + std::vector q3bin4weights = {0.465274 ,0.475479 ,0.521774 ,0.59605 ,0.685829 ,0.781784 ,0.876495 ,0.967061 ,1.05796 ,1.15474 ,1.25674 ,1.35469 ,1.43084 ,1.47119 ,1.47522 ,1.41 ,1.25184 ,1.07685 ,0.968015 ,0.941743 ,0.966135 ,1.00764 ,1.02239}; + + std::vector q3bin5weights = {0.549138, 0.562134, 0.624496, 0.72724, 0.859891, 1.00808, 1.15921, 1.30858, 1.45383, 1.5935, 1.7235, 1.83011, 1.88988, 1.88183, 1.80408, 1.63456, 1.38423, 1.11548, 0.902733, 0.778054, 0.732044, 0.731376, 0.738204}; + + std::vector tpibin = {.002, .006, .010, .014, .018, .022, .026, .030, .034, .038, .043, .049, .061, .075, .090, .125, .175, .225, .275, .325, .375, .450, .550}; + double weight2 = weight; + std::vector currentbins; + double q3_mecAna = universe->Getq3(); + if (q3_mecAna < 0.40 ) currentbins = q3bin1weights; + else if (q3_mecAna >= 0.40 || q3_mecAna < 0.60) currentbins = q3bin2weights; + else if (q3_mecAna >= 0.60 || q3_mecAna < 0.80) currentbins = q3bin3weights; + else if (q3_mecAna >= 0.80 || q3_mecAna < 1.00) currentbins = q3bin4weights; + else if (q3_mecAna >= 1.00 || q3_mecAna < 1.20) currentbins = q3bin5weights; + + int nbins = currentbins.size(); + + double tpi = universe->GetTrueTpi(); + /* + for (int i = 0; i tpibin[0] && tpi <= 0.500){ + double binedge1 = (tpibin[i]+tpibin[i]); + double binedge2 = (tpibin[i]+tpibin[i+1]); + if (tpi > binedge1 && tpi < binedge2 && tpi < 0.5) { + weight2 = weight*currentbins[i]; + } + } + else{ + if (tpi > .500 ) weight2 = weight*currentbins[22]; + } + } +i*/ + + for(auto& var: vars) var->selectedMCReco->FillUniverse(universe, var->GetRecoValue(*universe), weight2); //"Fake data" for closure + + const bool isSignal = michelcuts.isSignal(*universe, weight2); + + if(isSignal) + { + for(auto& study: studies) study->SelectedSignal(*universe, myevent, weight2); + + for(auto& var: vars) + { + //Cross section components + var->efficiencyNumerator->FillUniverse(universe, var->GetTrueValue(*universe), weight2); + var->migration->FillUniverse(universe, var->GetRecoValue(*universe), var->GetTrueValue(*universe), weight2); + var->selectedSignalReco->FillUniverse(universe, var->GetRecoValue(*universe), weight2); //Efficiency numerator in reco variables. Useful for warping studies. + } + + for(auto& var: vars2D) + { + var->efficiencyNumerator->FillUniverse(universe, var->GetTrueValueX(*universe), var->GetTrueValueY(*universe), weight2); + } + } + else + { + int bkgd_ID = -1; + if (universe->GetCurrent()==2)bkgd_ID=0; + else bkgd_ID=1; + + for(auto& var: vars) (*var->m_backgroundHists)[bkgd_ID].FillUniverse(universe, var->GetRecoValue(*universe), weight2); + for(auto& var: vars2D) (*var->m_backgroundHists)[bkgd_ID].FillUniverse(universe, var->GetRecoValueX(*universe), var->GetRecoValueY(*universe), weight2); + } + } // End band's universe loop + } // End Band loop + } //End entries loop + std::cout << "Finished MC reco loop.\n"; +} + +void LoopAndFillData( PlotUtils::ChainWrapper* data, + std::vector data_band, + std::vector vars, + std::vector vars2D, + std::vector studies, + PlotUtils::Cutter& michelcuts) + +{ + std::cout << "Starting data loop...\n"; + const int nEntries = data->GetEntries(); + for (int i=0; iGetEntries(); ++i) { + for (auto universe : data_band) { + universe->SetEntry(i); + if(i%1000==0) std::cout << i << " / " << nEntries << "\r" << std::flush; + //std::cout << "Creating Michel Event" << std::endl; + MichelEvent myevent; + //std::cout << "Applying Cuts to Data Event " << std::endl; + if (!michelcuts.isDataSelected(*universe, myevent).all()) continue; + //std::cout << "Filling Data STudies" << std::endl; + for(auto& study: studies) study->Selected(*universe, myevent, 1); + + for(auto& var: vars) + { + var->dataHist->FillUniverse(universe, var->GetRecoValue(*universe, myevent.m_idx), 1); + } + + for(auto& var: vars2D) + { + var->dataHist->FillUniverse(universe, var->GetRecoValueX(*universe), var->GetRecoValueY(*universe), 1); + } + } + } + std::cout << "Finished data loop.\n"; +} + +void LoopAndFillEffDenom( PlotUtils::ChainWrapper* truth, + std::map > truth_bands, + std::vector vars, + std::vector vars2D, + std::vector studies, + PlotUtils::Cutter& michelcuts, + PlotUtils::Model& model) +{ + assert(!truth_bands["cv"].empty() && "\"cv\" error band is empty! Could not set Model entry."); + auto& cvUniv = truth_bands["cv"].front(); + + std::cout << "Starting efficiency denominator loop...\n"; + const int nEntries = truth->GetEntries(); + for (int i=0; iSetEntry(i); + model.SetEntry(*cvUniv, cvEvent); + const double cvWeight = model.GetWeight(*cvUniv, cvEvent); + + //========================================= + // Systematics loop(s) + //========================================= + for (auto band : truth_bands) + { + std::vector truth_band_universes = band.second; + for (auto universe : truth_band_universes) + { + MichelEvent myevent; //Only used to keep the Model happy + + // Tell the Event which entry in the TChain it's looking at + universe->SetEntry(i); + + if (!michelcuts.isEfficiencyDenom(*universe, cvWeight)) continue; //Weight is ignored for isEfficiencyDenom() in all but the CV universe + const double weight = model.GetWeight(*universe, myevent); //Only calculate the weight for events that will use it + + //Fill efficiency denominator now: + for(auto var: vars) + { + var->efficiencyDenominator->FillUniverse(universe, var->GetTrueValue(*universe), weight); + } + // Fill Studies denominator: + //for(auto& study: studies) study->SelectedSignal(*universe, cvEvent, weight); + + for(auto var: vars2D) + { + var->efficiencyDenominator->FillUniverse(universe, var->GetTrueValueX(*universe), var->GetTrueValueY(*universe), weight); + } + } + } + } + std::cout << "Finished efficiency denominator loop.\n"; +} + +//Returns false if recoTreeName could not be inferred +bool inferRecoTreeNameAndCheckTreeNames(const std::string& mcPlaylistName, const std::string& dataPlaylistName, std::string& recoTreeName) +{ + const std::vector knownTreeNames = {"Truth", "Meta", "Header"}; + bool areFilesOK = false; + + std::ifstream playlist(mcPlaylistName); + std::string firstFile = ""; + playlist >> firstFile; + auto testFile = TFile::Open(firstFile.c_str()); + if(!testFile) + { + std::cerr << "Failed to open the first MC file at " << firstFile << "\n"; + return false; + } + + //Does the MC playlist have the Truth tree? This is needed for the efficiency denominator. + const auto truthTree = testFile->Get("Truth"); + if(truthTree == nullptr || !truthTree->IsA()->InheritsFrom(TClass::GetClass("TTree"))) + { + std::cerr << "Could not find the \"Truth\" tree in MC file named " << firstFile << "\n"; + return false; + } + + //Figure out what the reco tree name is + for(auto key: *testFile->GetListOfKeys()) + { + if(static_cast(key)->ReadObj()->IsA()->InheritsFrom(TClass::GetClass("TTree")) + && std::find(knownTreeNames.begin(), knownTreeNames.end(), key->GetName()) == knownTreeNames.end()) + { + recoTreeName = key->GetName(); + areFilesOK = true; + } + } + delete testFile; + testFile = nullptr; + + //Make sure the data playlist's first file has the same reco tree + playlist.open(dataPlaylistName); + playlist >> firstFile; + testFile = TFile::Open(firstFile.c_str()); + if(!testFile) + { + std::cerr << "Failed to open the first data file at " << firstFile << "\n"; + return false; + } + + const auto recoTree = testFile->Get(recoTreeName.c_str()); + if(recoTree == nullptr || !recoTree->IsA()->InheritsFrom(TClass::GetClass("TTree"))) + { + std::cerr << "Could not find the \"" << recoTreeName << "\" tree in data file named " << firstFile << "\n"; + return false; + } + + return areFilesOK; +} + +//============================================================================== +// Main +//============================================================================== +int main(const int argc, const char** argv) +{ + TH1::AddDirectory(false); + + //Validate input. + //I expect a data playlist file name and an MC playlist file name which is exactly 2 arguments. + const int nArgsExpected = 2; + if(argc != nArgsExpected + 1) //argc is the size of argv. I check for number of arguments + 1 because + //argv[0] is always the path to the executable. + { + std::cerr << "Expected " << nArgsExpected << " arguments, but got " << argc - 1 << "\n" << USAGE << "\n"; + return badCmdLine; + } + + //One playlist must contain only MC files, and the other must contain only data files. + //Only checking the first file in each playlist because opening each file an extra time + //remotely (e.g. through xrootd) can get expensive. + //TODO: Look in INSTALL_DIR if files not found? + const std::string mc_file_list = argv[2], + data_file_list = argv[1]; + //Check that necessary TTrees exist in the first file of mc_file_list and data_file_list + std::string reco_tree_name; + if(!inferRecoTreeNameAndCheckTreeNames(mc_file_list, data_file_list, reco_tree_name)) + { + std::cerr << "Failed to find required trees in MC playlist " << mc_file_list << " and/or data playlist " << data_file_list << ".\n" << USAGE << "\n"; + return badInputFile; + } + + const bool doCCQENuValidation = (reco_tree_name == "CCQENu"); //Enables extra histograms and might influence which systematics I use. + + const bool is_grid = false; + PlotUtils::MacroUtil options(reco_tree_name, mc_file_list, data_file_list, "minervame1A", true, is_grid); + std::cout << options.m_mc->GetChain()->GetName() << std::endl; + options.m_plist_string = util::GetPlaylist(*options.m_mc, true); //TODO: Put GetPlaylist into PlotUtils::MacroUtil + + // You're required to make some decisions + PlotUtils::MinervaUniverse::SetNuEConstraint(true); + PlotUtils::MinervaUniverse::SetPlaylist(options.m_plist_string); //TODO: Infer this from the files somehow? + PlotUtils::MinervaUniverse::SetAnalysisNuPDG(14); + PlotUtils::MinervaUniverse::SetNFluxUniverses(100); + PlotUtils::MinervaUniverse::SetZExpansionFaReweight(false); + + //Now that we've defined what a cross section is, decide which sample and model + //we're extracting a cross section for. + PlotUtils::Cutter::reco_t sidebands, preCuts; + PlotUtils::Cutter::truth_t signalDefinition, phaseSpace; + + const double minZ = 5980, maxZ = 8422, apothem = 850; //All in mm + preCuts.emplace_back(new reco::ZRange("Tracker", minZ, maxZ)); + preCuts.emplace_back(new reco::Apothem(apothem)); + preCuts.emplace_back(new reco::MaxMuonAngle(20.)); + preCuts.emplace_back(new reco::HasMINOSMatch()); + preCuts.emplace_back(new reco::NoDeadtime(1, "Deadtime")); + preCuts.emplace_back(new reco::IsNeutrino()); + preCuts.emplace_back(new Q3RangeReco(0.4,0.6)); + //preCuts.emplace_back(new hasMichel()); + //preCuts.emplace_back(new BestMichelDistance2D(100.)); + //preCuts.emplace_back(new VtxMatchFirst(200., 102.)); + preCuts.emplace_back(new NPiCut(1)); + preCuts.emplace_back(new hasMichel()); + preCuts.emplace_back(new BestMichelDistance2D(150.)); + + TFile* mc_MichelStudies = TFile::Open("Dec312021_noreweight_distcuts_q3bin2_atleast1pi_MC.root", "RECREATE"); + TFile* data_MichelStudies = TFile::Open("Dec312021_noreweight_distcuts_q3bin2_atleast1pi_data.root", "RECREATE"); + + signalDefinition.emplace_back(new truth::IsNeutrino()); + signalDefinition.emplace_back(new truth::IsCC()); + signalDefinition.emplace_back(new Q3Limit(0.4, 0.6)); + //signalDefinition.emplace_back(new hasTruePion()); + phaseSpace.emplace_back(new truth::ZRange("Tracker", minZ, maxZ)); + phaseSpace.emplace_back(new truth::Apothem(apothem)); + phaseSpace.emplace_back(new truth::MuonAngle(20.)); + phaseSpace.emplace_back(new truth::PZMuMin(1500.)); + + PlotUtils::Cutter mycuts(std::move(preCuts), std::move(sidebands) , std::move(signalDefinition),std::move(phaseSpace)); + + std::vector>> MnvTunev1; + MnvTunev1.emplace_back(new PlotUtils::FluxAndCVReweighter()); + MnvTunev1.emplace_back(new PlotUtils::GENIEReweighter(true, false)); + MnvTunev1.emplace_back(new PlotUtils::LowRecoil2p2hReweighter()); + MnvTunev1.emplace_back(new PlotUtils::MINOSEfficiencyReweighter()); + MnvTunev1.emplace_back(new PlotUtils::RPAReweighter()); + //TODO: Add my pion reweighter here. - Mehreen S. Nov 22, 2021 + PlotUtils::Model model(std::move(MnvTunev1)); + + // Make a map of systematic universes + // Leave out systematics when making validation histograms + const bool doSystematics = (getenv("MNV101_SKIP_SYST") == nullptr); + if(!doSystematics){ + std::cout << "Skipping systematics (except 1 flux universe) because environment variable MNV101_SKIP_SYST is set.\n"; + PlotUtils::MinervaUniverse::SetNFluxUniverses(2); //Necessary to get Flux integral later... Doesn't work with just 1 flux universe though because _that_ triggers "spread errors". + } + + std::map< std::string, std::vector > error_bands; + if(doSystematics) error_bands = GetStandardSystematics(options.m_mc); + else{ + std::map > band_flux = PlotUtils::GetFluxSystematicsMap(options.m_mc, CVUniverse::GetNFluxUniverses()); + error_bands.insert(band_flux.begin(), band_flux.end()); //Necessary to get flux integral later... + } + error_bands["cv"] = {new CVUniverse(options.m_mc)}; + std::map< std::string, std::vector > truth_bands; + if(doSystematics) truth_bands = GetStandardSystematics(options.m_truth); + truth_bands["cv"] = {new CVUniverse(options.m_truth)}; + + std::vector dansPTBins = {0, 0.075, 0.15, 0.25, 0.325, 0.4, 0.475, 0.55, 0.7, 0.85, 1, 1.25, 1.5}, + dansPzBins = {1.5, 2, 2.5, 3, 3.5, 4, 4.5, 5, 6, 7, 8, 9, 10, 15, 20, 40, 60}, + robsEmuBins = {0,1,2,3,4,5,7,9,12,15,18,22,36,50,75,80}, + robsRecoilBins; + + std::vector tpibins = {0, 4., 8., 12., 16., 20., 24., 28., 32., 36., 40., 46., 52., 70., 80., 100., 150., 200., 250., 300., 350., 400., 500., 1000., 2000.}; + + const double robsRecoilBinWidth = 50; //MeV + for(int whichBin = 0; whichBin < 100 + 1; ++whichBin) robsRecoilBins.push_back(robsRecoilBinWidth * whichBin); + + std::vector vars = { + new Variable("pTmu", "p_{T, #mu} [GeV/c]", dansPTBins, &CVUniverse::GetMuonPT, &CVUniverse::GetMuonPTTrue), + new Variable("q3", "q3 [GeV]", dansPTBins, &CVUniverse::Getq3, &CVUniverse::GetTrueQ3) + }; + + std::vector vars2D; + if(doCCQENuValidation) + { + std::cerr << "Detected that tree name is CCQENu. Making validation histograms.\n"; + vars.push_back(new Variable("pzmu", "p_{||, #mu} [GeV/c]", dansPzBins, &CVUniverse::GetMuonPz, &CVUniverse::GetMuonPzTrue)); + vars.push_back(new Variable("Emu", "E_{#mu} [GeV]", robsEmuBins, &CVUniverse::GetEmuGeV, &CVUniverse::GetElepTrueGeV)); + vars.push_back(new Variable("Erecoil", "E_{recoil}", robsRecoilBins, &CVUniverse::GetRecoilE, &CVUniverse::GetTrueEAvail)); //TODO: q0 is not the same as recoil energy without a spline correction + + vars2D.push_back(new Variable2D(*vars[1], *vars[0])); + } + + +//This is where your list of Studies go for PerMichel variables -> Accessed through MichelEvent + + std::vector studies; + + std::function delta_t = [](const CVUniverse& univ, const MichelEvent& evt, const int whichMichel) + { + int evttype = evt.eventtype; + double micheltime = evt.m_nmichels[whichMichel].time; + double vtxtime = univ.GetVertex().t(); + double deltat = (micheltime - vtxtime/1000.); //hopefully this is in microseconds (mus) + //if (evttype == 1) return deltat; + //else return -9999.; + return deltat; + }; + + std::function permichel_range = [](const CVUniverse& univ, const MichelEvent& evt, const int whichMichel) + { + double micheldist = evt.m_nmichels[whichMichel].Best3Ddist; + //if (evt.eventtype == 1) return micheldist; + //else return -9999.; + return micheldist; + }; + std::function pertruepimichel_range = [](const CVUniverse& univ, const MichelEvent& evt, const int whichMichel) + { + double micheldist = evt.m_nmichels[whichMichel].Best3Ddist; + if (evt.m_nmichels[whichMichel].true_parentpdg == 211) return micheldist; + else return -9999.; + }; + std::function permichel_tpi = [](const CVUniverse& univ, const MichelEvent& evt, const int whichMichel) + { + return evt.m_nmichels[whichMichel].pionKE; + + + }; + + std::function overlay_vtx_tpi = [](const CVUniverse& univ, const MichelEvent& evt, const int whichMichel) + { + double micheldist = evt.m_nmichels[whichMichel].Best3Ddist; + double KE = evt.m_nmichels[whichMichel].pionKE; + int matchtype = evt.m_nmichels[whichMichel].BestMatch; + int overlayfrac = evt.m_nmichels[whichMichel].overlay_fraction; + if (overlayfrac > .5 && (matchtype == 1 || matchtype == 2 )) return KE; + else return -9999.; + }; + +std::function overlay_clus_tpi = [](const CVUniverse& univ, const MichelEvent& evt, const int whichMichel) + { + double micheldist = evt.m_nmichels[whichMichel].Best3Ddist; + double KE = evt.m_nmichels[whichMichel].pionKE; + int matchtype = evt.m_nmichels[whichMichel].BestMatch; + int overlayfrac = evt.m_nmichels[whichMichel].overlay_fraction; + if (overlayfrac > .5 && (matchtype == 3 || matchtype == 4 )) return KE; + else return -9999.; + }; + + std::function truemichel_goodvtx_tpi = [](const CVUniverse& univ, const MichelEvent& evt, const int whichMichel) + { + double micheldist = evt.m_nmichels[whichMichel].Best3Ddist; + int overlayfrac = evt.m_nmichels[whichMichel].overlay_fraction; + double KE = evt.m_nmichels[whichMichel].pionKE; + int matchtype = evt.m_nmichels[whichMichel].BestMatch; + int trueEnd = evt.m_nmichels[whichMichel].trueEndpoint; + int recoEnd = evt.m_nmichels[whichMichel].recoEndpoint; + if (overlayfrac < .5 && trueEnd == recoEnd && (matchtype == 1 || matchtype == 2)) return KE; + else return -9999.; + }; + std::function truemichel_goodclus_tpi = [](const CVUniverse& univ, const MichelEvent& evt, const int whichMichel) + { + double micheldist = evt.m_nmichels[whichMichel].Best3Ddist; + int overlayfrac = evt.m_nmichels[whichMichel].overlay_fraction; + double KE = evt.m_nmichels[whichMichel].pionKE; + int matchtype = evt.m_nmichels[whichMichel].BestMatch; + int trueEnd = evt.m_nmichels[whichMichel].trueEndpoint; + int recoEnd = evt.m_nmichels[whichMichel].recoEndpoint; + if (overlayfrac < .5 && trueEnd == recoEnd && (matchtype == 3 || matchtype == 4)) return KE; + else return -9999.; + }; + std::function truemichel_badvtx_tpi = [](const CVUniverse& univ, const MichelEvent& evt, const int whichMichel) + { + double micheldist = evt.m_nmichels[whichMichel].Best3Ddist; + double KE = evt.m_nmichels[whichMichel].pionKE; + int overlayfrac = evt.m_nmichels[whichMichel].overlay_fraction; + int matchtype = evt.m_nmichels[whichMichel].BestMatch; + int trueEnd = evt.m_nmichels[whichMichel].trueEndpoint; + int recoEnd = evt.m_nmichels[whichMichel].recoEndpoint; + if (overlayfrac < .5 && trueEnd != recoEnd && (matchtype == 1 || matchtype == 2)) + { + //univ.PrintArachneLink(); + //std::cout << "Printing Michel Time for bad VTX match type " << evt.m_nmichels[whichMichel].time << std::endl; + return KE; + } + else return -9999.; + }; + + std::function truemichel_badclus_tpi = [](const CVUniverse& univ, const MichelEvent& evt, const int whichMichel) + { + double micheldist = evt.m_nmichels[whichMichel].Best3Ddist; + double KE = evt.m_nmichels[whichMichel].pionKE; + int overlayfrac = evt.m_nmichels[whichMichel].overlay_fraction; + int matchtype = evt.m_nmichels[whichMichel].BestMatch; + int trueEnd = evt.m_nmichels[whichMichel].trueEndpoint; + int recoEnd = evt.m_nmichels[whichMichel].recoEndpoint; + if (overlayfrac < .5 && trueEnd != recoEnd && (matchtype == 3 || matchtype == 4)) + { + //univ.PrintArachneLink(); + //std::cout << "Printing Michel Time for bad CLUS match type " << evt.m_nmichels[whichMichel].time << std::endl; + return KE; + } + else return -9999.; + }; + + + + std::function overlay_vtx_range = [](const CVUniverse& univ, const MichelEvent& evt, const int whichMichel) + { + double micheldist = evt.m_nmichels[whichMichel].Best3Ddist; + + int matchtype = evt.m_nmichels[whichMichel].BestMatch; + int overlayfrac = evt.m_nmichels[whichMichel].overlay_fraction; + if (overlayfrac > .5 && (matchtype == 1 || matchtype == 2 )) return micheldist; + else return -9999.; + }; + + std::function overlay_clus_range = [](const CVUniverse& univ, const MichelEvent& evt, const int whichMichel) + { + double micheldist = evt.m_nmichels[whichMichel].Best3Ddist; + + int matchtype = evt.m_nmichels[whichMichel].BestMatch; + int overlayfrac = evt.m_nmichels[whichMichel].overlay_fraction; + if (overlayfrac > .5 && (matchtype == 3 || matchtype == 4 )) return micheldist; + else return -9999.; + }; + + std::function truemichel_goodvtx_range = [](const CVUniverse& univ, const MichelEvent& evt, const int whichMichel) + { + double micheldist = evt.m_nmichels[whichMichel].Best3Ddist; + int overlayfrac = evt.m_nmichels[whichMichel].overlay_fraction; + + int matchtype = evt.m_nmichels[whichMichel].BestMatch; + int trueEnd = evt.m_nmichels[whichMichel].trueEndpoint; + int recoEnd = evt.m_nmichels[whichMichel].recoEndpoint; + if (overlayfrac < .5 && trueEnd == recoEnd && (matchtype == 1 || matchtype == 2)) return micheldist; + else return -9999.; + }; + + std::function truemichel_goodclus_range = [](const CVUniverse& univ, const MichelEvent& evt, const int whichMichel) + { + double micheldist = evt.m_nmichels[whichMichel].Best3Ddist; + int overlayfrac = evt.m_nmichels[whichMichel].overlay_fraction; + int matchtype = evt.m_nmichels[whichMichel].BestMatch; + int trueEnd = evt.m_nmichels[whichMichel].trueEndpoint; + int recoEnd = evt.m_nmichels[whichMichel].recoEndpoint; + if (overlayfrac < .5 && trueEnd == recoEnd && (matchtype == 3 || matchtype == 4)) return micheldist; + else return -9999.; + }; + + std::function truemichel_badvtx_range = [](const CVUniverse& univ, const MichelEvent& evt, const int whichMichel) + { + double micheldist = evt.m_nmichels[whichMichel].Best3Ddist; + int overlayfrac = evt.m_nmichels[whichMichel].overlay_fraction; + int matchtype = evt.m_nmichels[whichMichel].BestMatch; + int trueEnd = evt.m_nmichels[whichMichel].trueEndpoint; + int recoEnd = evt.m_nmichels[whichMichel].recoEndpoint; + if (overlayfrac < .5 && trueEnd != recoEnd && (matchtype == 1 || matchtype == 2)) + { + // univ.PrintArachneLink(); + // std::cout << "Printing Michel Time for bad VTX match type " << evt.m_nmichels[whichMichel].time << std::endl; + return micheldist; + } + else return -9999.; + }; + std::function truemichel_badclus_range = [](const CVUniverse& univ, const MichelEvent& evt, const int whichMichel) + { + double micheldist = evt.m_nmichels[whichMichel].Best3Ddist; + int overlayfrac = evt.m_nmichels[whichMichel].overlay_fraction; + int matchtype = evt.m_nmichels[whichMichel].BestMatch; + int trueEnd = evt.m_nmichels[whichMichel].trueEndpoint; + int recoEnd = evt.m_nmichels[whichMichel].recoEndpoint; + if (overlayfrac < .5 && trueEnd != recoEnd && (matchtype == 3 || matchtype == 4)) + { + // univ.PrintArachneLink(); + // std::cout << "Printing Michel Time for bad CLUS match type " << evt.m_nmichels[whichMichel].time << std::endl; + return micheldist; + } + else return -9999.; + }; + + +std::function michel_XZ = [](const CVUniverse& univ, const MichelEvent& evt, const int whichMichel) + { + double twoDdist = evt.m_nmichels[whichMichel].best_XZ; + return twoDdist; + }; + +std::function michel_UZ = [](const CVUniverse& univ, const MichelEvent& evt, const int whichMichel) + { + double twoDdist = evt.m_nmichels[whichMichel].best_UZ; + return twoDdist; + }; + + +std::function michel_VZ = [](const CVUniverse& univ, const MichelEvent& evt, const int whichMichel) + { + double twoDdist = evt.m_nmichels[whichMichel].best_VZ; + return twoDdist; + }; + +std::function michel_XZ_upvtx = [](const CVUniverse& univ, const MichelEvent& evt, const int whichMichel) + { + double twoDdist = evt.m_nmichels[whichMichel].up_to_vertex_XZ; + int trueend = evt.m_nmichels[whichMichel].trueEndpoint; + if (trueend == 1) return twoDdist; + else return 9999.; + }; + +std::function michel_UZ_upvtx = [](const CVUniverse& univ, const MichelEvent& evt, const int whichMichel) + { + double twoDdist = evt.m_nmichels[whichMichel].up_to_vertex_UZ; + int trueend = evt.m_nmichels[whichMichel].trueEndpoint; + if (trueend == 1) return twoDdist; + else return 9999.; + }; + + +std::function michel_VZ_upvtx = [](const CVUniverse& univ, const MichelEvent& evt, const int whichMichel) + { + double twoDdist = evt.m_nmichels[whichMichel].up_to_vertex_VZ; + int trueend = evt.m_nmichels[whichMichel].trueEndpoint; + if (trueend == 1) return twoDdist; + else return 9999.; + }; + + +std::function michel_XZ_upclus = [](const CVUniverse& univ, const MichelEvent& evt, const int whichMichel) + { + double twoDdist = evt.m_nmichels[whichMichel].up_to_clus_XZ; + int trueend = evt.m_nmichels[whichMichel].trueEndpoint; + if (trueend == 1) return twoDdist; + else return 9999.; + }; + +std::function michel_UZ_upclus = [](const CVUniverse& univ, const MichelEvent& evt, const int whichMichel) + { + double twoDdist = evt.m_nmichels[whichMichel].up_to_clus_UZ; + int trueend = evt.m_nmichels[whichMichel].trueEndpoint; + if (trueend == 1) return twoDdist; + else return 9999.; + }; + +std::function michel_VZ_upclus = [](const CVUniverse& univ, const MichelEvent& evt, const int whichMichel) + { + double twoDdist = evt.m_nmichels[whichMichel].up_to_clus_VZ; + int trueend = evt.m_nmichels[whichMichel].trueEndpoint; + if (trueend == 1) return twoDdist; + else return 9999.; + }; + +std::function michel_XZ_downclus = [](const CVUniverse& univ, const MichelEvent& evt, const int whichMichel) + { + double twoDdist = evt.m_nmichels[whichMichel].down_to_clus_XZ; + int trueend = evt.m_nmichels[whichMichel].trueEndpoint; + if (trueend == 2) return twoDdist; + else return 9999.; + }; +std::function michel_UZ_downclus = [](const CVUniverse& univ, const MichelEvent& evt, const int whichMichel) + { + double twoDdist = evt.m_nmichels[whichMichel].down_to_clus_UZ; + int trueend = evt.m_nmichels[whichMichel].trueEndpoint; + if (trueend == 2) return twoDdist; + else return 9999.; + }; +std::function michel_VZ_downclus = [](const CVUniverse& univ, const MichelEvent& evt, const int whichMichel) + { + double twoDdist = evt.m_nmichels[whichMichel].down_to_clus_VZ; + int trueend = evt.m_nmichels[whichMichel].trueEndpoint; + if (trueend == 2) return twoDdist; + else return 9999.; + }; +std::function michel_XZ_downvtx = [](const CVUniverse& univ, const MichelEvent& evt, const int whichMichel) + { + double twoDdist = evt.m_nmichels[whichMichel].down_to_vertex_XZ; + int trueend = evt.m_nmichels[whichMichel].trueEndpoint; + if (trueend == 2) return twoDdist; + else return 9999.; + }; + +std::function michel_UZ_downvtx = [](const CVUniverse& univ, const MichelEvent& evt, const int whichMichel) + { + double twoDdist = evt.m_nmichels[whichMichel].down_to_vertex_UZ; + int trueend = evt.m_nmichels[whichMichel].trueEndpoint; + if (trueend == 2) return twoDdist; + else return 9999.; + }; + + + +std::function michel_VZ_downvtx = [](const CVUniverse& univ, const MichelEvent& evt, const int whichMichel) + { + double twoDdist = evt.m_nmichels[whichMichel].down_to_vertex_VZ; + int trueend = evt.m_nmichels[whichMichel].trueEndpoint; + if (trueend == 2) return twoDdist; + else return 9999.; + }; + + +std::function pion_angle = [](const CVUniverse& univ, const MichelEvent& evt, const int whichMichel) + { + double angle = evt.m_nmichels[whichMichel].best_angle; + return cos(angle); + }; + +std::function pion_angle_range1 = [](const CVUniverse& univ, const MichelEvent& evt, const int whichMichel) + { + double angle = evt.m_nmichels[whichMichel].best_angle; + double micheldist = evt.m_nmichels[whichMichel].Best3Ddist; + if (micheldist <= 150.) return cos(angle); + else return 9999.; + }; + +std::function pion_angle_range2 = [](const CVUniverse& univ, const MichelEvent& evt, const int whichMichel) + { + double angle = evt.m_nmichels[whichMichel].best_angle; + double micheldist = evt.m_nmichels[whichMichel].Best3Ddist; + if (micheldist >150. && micheldist <= 250.) return cos(angle); + else return 9999.; + }; + +std::function pion_angle_range3 = [](const CVUniverse& univ, const MichelEvent& evt, const int whichMichel) + { + double angle = evt.m_nmichels[whichMichel].best_angle; + double micheldist = evt.m_nmichels[whichMichel].Best3Ddist; + if (micheldist > 250. && micheldist <=500.) return cos(angle); + else return 9999.; + }; +std::function pion_angle_range4 = [](const CVUniverse& univ, const MichelEvent& evt, const int whichMichel) + { + double angle = evt.m_nmichels[whichMichel].best_angle; + double micheldist = evt.m_nmichels[whichMichel].Best3Ddist; + if (micheldist > 500.) return cos(angle); + else return 9999.; + }; + +std::function true_angle = [](const CVUniverse& univ, const MichelEvent& evt, const int whichMichel) + { + double angle = evt.m_nmichels[whichMichel].true_angle; + return cos(angle); + }; + +std::function true_angle_range1 = [](const CVUniverse& univ, const MichelEvent& evt, const int whichMichel) + { + double angle = evt.m_nmichels[whichMichel].true_angle; + double micheldist = evt.m_nmichels[whichMichel].Best3Ddist; + if (micheldist <= 150.) return cos(angle); + else return 9999.; + }; + +std::function true_angle_range2 = [](const CVUniverse& univ, const MichelEvent& evt, const int whichMichel) + { + double angle = evt.m_nmichels[whichMichel].true_angle; + double micheldist = evt.m_nmichels[whichMichel].Best3Ddist; + if (micheldist > 150 && micheldist <= 250.) return cos(angle); + else return 9999.; + }; +std::function true_angle_range3 = [](const CVUniverse& univ, const MichelEvent& evt, const int whichMichel) + { + double angle = evt.m_nmichels[whichMichel].true_angle; + double micheldist = evt.m_nmichels[whichMichel].Best3Ddist; + if (micheldist > 250 && micheldist <= 500.) return cos(angle); + else return 9999.; + }; + +std::function true_angle_range4 = [](const CVUniverse& univ, const MichelEvent& evt, const int whichMichel) + { + double angle = evt.m_nmichels[whichMichel].true_angle; + double micheldist = evt.m_nmichels[whichMichel].Best3Ddist; + if (micheldist > 500) return cos(angle); + else return 9999.; + }; + studies.push_back(new PerMichelVarByGENIELabel(true_angle, "true_angle", "cos(#theta)", 21, -1.0, 1.0, error_bands)); + studies.push_back(new PerMichelVarByGENIELabel(true_angle_range1, "true_angle_range1", "cos(#theta)", 21, -1.0, 1., error_bands)); + studies.push_back(new PerMichelVarByGENIELabel(true_angle_range2, "true_angle_range2", "cos(#theta)", 21, -1.0, 1., error_bands)); + studies.push_back(new PerMichelVarByGENIELabel(true_angle_range3, "true_angle_range3", "cos(#theta)", 21, -1.0, 1., error_bands)); + studies.push_back(new PerMichelVarByGENIELabel(true_angle_range4, "true_angle_range4", "cos(#theta)", 21, -1.0, 1., error_bands)); + studies.push_back(new PerMichelVarByGENIELabel(pion_angle, "pion_angle", "cos(#theta)", 21, -1.0, 1., error_bands)); + studies.push_back(new PerMichelVarByGENIELabel(pion_angle_range1, "pion_angle_range1", "cos(#theta)", 21, -1.0, 1., error_bands)); + studies.push_back(new PerMichelVarByGENIELabel(pion_angle_range2, "pion_angle_range2", "cos(#theta)", 21, -1.0, 1., error_bands)); + studies.push_back(new PerMichelVarByGENIELabel(pion_angle_range3, "pion_angle_range3", "cos(#theta)", 21, -1.0, 1., error_bands)); + studies.push_back(new PerMichelVarByGENIELabel(pion_angle_range4, "pion_angle_range4", "cos(#theta)", 21, -1.0, 1., error_bands)); + + studies.push_back(new PerMichelVarByGENIELabel(michel_XZ, "best_XZ", "mm", 100, 0.0, 2000., error_bands)); + studies.push_back(new PerMichelVarByGENIELabel(michel_UZ, "best_UZ", "mm", 100, 0.0, 2000., error_bands)); + studies.push_back(new PerMichelVarByGENIELabel(michel_VZ, "best_VZ", "mm", 100, 0.0, 2000., error_bands)); + studies.push_back(new PerMichelVarByGENIELabel(michel_XZ_upvtx, "upvtx_XZ", "mm", 100, 0.0, 2000., error_bands)); + studies.push_back(new PerMichelVarByGENIELabel(michel_XZ_upclus, "upclus_XZ", "mm", 100, 0.0, 2000., error_bands)); + studies.push_back(new PerMichelVarByGENIELabel(michel_XZ_downclus, "downclus_XZ", "mm", 100, 0.0, 2000., error_bands)); + studies.push_back(new PerMichelVarByGENIELabel(michel_XZ_downvtx, "downvtx_XZ", "mm", 100, 0.0, 2000., error_bands)); + studies.push_back(new PerMichelVarByGENIELabel(michel_UZ_upvtx, "upvtx_UZ", "mm", 100, 0.0, 2000., error_bands)); + studies.push_back(new PerMichelVarByGENIELabel(michel_UZ_upclus, "upclus_UZ", "mm", 100, 0.0, 2000., error_bands)); + studies.push_back(new PerMichelVarByGENIELabel(michel_UZ_downclus, "downclus_UZ", "mm", 100, 0.0, 2000., error_bands)); + studies.push_back(new PerMichelVarByGENIELabel(michel_UZ_downvtx, "downvtx_UZ", "mm", 100, 0.0, 2000., error_bands)); + studies.push_back(new PerMichelVarByGENIELabel(michel_VZ_upvtx, "upvtx_VZ", "mm", 100, 0.0, 2000., error_bands)); + studies.push_back(new PerMichelVarByGENIELabel(michel_VZ_upclus, "upclus_VZ", "mm", 100, 0.0, 2000., error_bands)); + studies.push_back(new PerMichelVarByGENIELabel(michel_VZ_downclus, "downclus_VZ", "mm", 100, 0.0, 2000., error_bands)); + studies.push_back(new PerMichelVarByGENIELabel(michel_VZ_downvtx, "downvtx_VZ", "mm", 100, 0.0, 2000., error_bands)); + + studies.push_back(new PerMichelVarByGENIELabel(delta_t, "michelmuon_deltat", "#mus", 30, 0.0, 9.0, error_bands)); + studies.push_back(new PerMichelVarByGENIELabel(permichel_range, "permichel_pirange", "mm", 100, 0.0, 2000.0, error_bands)); + studies.push_back(new PerMichelVarByGENIELabel(overlay_vtx_range, "overlay_vtx_range", "mm", 100, 0.0, 2000.0, error_bands)); + studies.push_back(new PerMichelVarByGENIELabel(overlay_clus_range, "overlay_clus_range", "mm", 100, 0.0, 2000.0, error_bands)); + studies.push_back(new PerMichelVarByGENIELabel(truemichel_goodvtx_range, "truemichel_goodvtx_range", "mm", 100, 0.0, 2000.0, error_bands)); + studies.push_back(new PerMichelVarByGENIELabel(truemichel_goodclus_range, "truemichel_goodclus_range", "mm", 100, 0.0, 2000.0, error_bands)); + studies.push_back(new PerMichelVarByGENIELabel(truemichel_badvtx_range, "truemichel_badvtx_range", "mm", 100, 0.0, 2000.0, error_bands)); + studies.push_back(new PerMichelVarByGENIELabel(truemichel_badclus_range, "truemichel_badclus_range", "mm", 100, 0.0, 2000.0, error_bands)); + studies.push_back(new PerMichelVarByGENIELabel(permichel_tpi, "Per_Michel_PrimaryParentKE", "meV", 100, 0, 1000., error_bands)); + studies.push_back(new PerMichelVarByGENIELabel(pertruepimichel_range, "permichel_pirange_truepi", "mm", 100, 0.0, 2000.0, error_bands)); + + VarConfig deltat_config{"deltat", "#mus", 30, 0., 9.}; + VarConfig pirange_config{"pirange", "mm", 100, 0.0, 2000.0}; + VarConfig tpi_config{"KE", "meV", 100, 0., 1000.}; + + //studies.push_back(new PerMichel2DVar(delta_t, permichel_range, deltat_config, pirange_config, error_bands)); + studies.push_back(new PerMichel2DVar(permichel_tpi, permichel_range, tpi_config, pirange_config, error_bands)); + // studies.push_back(new PerMichel2DVar(overlay_vtx_tpi, overlay_vtx_range, tpi_config, pirange_config, error_bands)); + // studies.push_back(new PerMichel2DVar(overlay_clus_tpi, overlay_clus_range, tpi_config, pirange_config, error_bands)); + // studies.push_back(new PerMichel2DVar(truemichel_goodvtx_range, truemichel_goodvtx_tpi, tpi_config, pirange_config, error_bands)); + // studies.push_back(new PerMichel2DVar(truemichel_goodclus_range, truemichel_goodclus_tpi, tpi_config, pirange_config, error_bands)); + // studies.push_back(new PerMichel2DVar(truemichel_badvtx_range, truemichel_badvtx_tpi, tpi_config, pirange_config, error_bands)); + // studies.push_back(new PerMichel2DVar(truemichel_badclus_range, truemichel_badclus_tpi, tpi_config, pirange_config, error_bands)); +// Studies for Per Event Variables (Best Michel In Event) + + std::function best_pionrange = [](const CVUniverse& univ, const MichelEvent& evt) + { + int bestidx = evt.m_idx; + if (bestidx < 0) return -9999.; + else{ + double dist = evt.m_nmichels[bestidx].Best3Ddist; + return dist; + } + }; + + + std::function best_pionrange_overlay_vtx = [](const CVUniverse& univ, const MichelEvent& evt) + { + int bestidx = evt.m_idx; + if (bestidx < 0) return -9999.; + else{ + double micheldist = evt.m_nmichels[bestidx].Best3Ddist; + int overlayfrac = evt.m_nmichels[bestidx].overlay_fraction; + int matchtype = evt.m_nmichels[bestidx].BestMatch; + int trueEnd = evt.m_nmichels[bestidx].trueEndpoint; + int recoEnd = evt.m_nmichels[bestidx].recoEndpoint; + if (overlayfrac > .5 && (matchtype == 1 || matchtype == 2)) return micheldist; + else return -9999.; + } + }; + + std::function best_pionrange_overlay_clus = [](const CVUniverse& univ, const MichelEvent& evt) + { + int bestidx = evt.m_idx; + if (bestidx < 0) return -9999.; + else{ + double micheldist = evt.m_nmichels[bestidx].Best3Ddist; + int overlayfrac = evt.m_nmichels[bestidx].overlay_fraction; + int matchtype = evt.m_nmichels[bestidx].BestMatch; + int trueEnd = evt.m_nmichels[bestidx].trueEndpoint; + int recoEnd = evt.m_nmichels[bestidx].recoEndpoint; + if (overlayfrac > .5 && (matchtype == 3 || matchtype == 4)) return micheldist; + else return -9999.; + } + }; + +std::function best_pionrange_truegood_vtx = [](const CVUniverse& univ, const MichelEvent& evt) + { + int bestidx = evt.m_idx; + if (bestidx < 0) return -9999.; + else{ + double micheldist = evt.m_nmichels[bestidx].Best3Ddist; + int overlayfrac = evt.m_nmichels[bestidx].overlay_fraction; + int matchtype = evt.m_nmichels[bestidx].BestMatch; + int trueEnd = evt.m_nmichels[bestidx].trueEndpoint; + int recoEnd = evt.m_nmichels[bestidx].recoEndpoint; + if (overlayfrac < .5 && trueEnd == recoEnd && (matchtype == 1 || matchtype == 2)) return micheldist; + else return -9999.; + } + }; + +std::function best_pionrange_truebad_vtx = [](const CVUniverse& univ, const MichelEvent& evt) + { + int bestidx = evt.m_idx; + if (bestidx < 0) return -9999.; + else{ + double micheldist = evt.m_nmichels[bestidx].Best3Ddist; + int overlayfrac = evt.m_nmichels[bestidx].overlay_fraction; + int matchtype = evt.m_nmichels[bestidx].BestMatch; + int trueEnd = evt.m_nmichels[bestidx].trueEndpoint; + int recoEnd = evt.m_nmichels[bestidx].recoEndpoint; + if (overlayfrac < .5 && trueEnd != recoEnd && (matchtype == 1 || matchtype == 2)) return micheldist; + else return -9999.; + } + }; + +std::function best_pionrange_truegood_clus = [](const CVUniverse& univ, const MichelEvent& evt) + { + int bestidx = evt.m_idx; + if (bestidx < 0) return -9999.; + else{ + double micheldist = evt.m_nmichels[bestidx].Best3Ddist; + int overlayfrac = evt.m_nmichels[bestidx].overlay_fraction; + int matchtype = evt.m_nmichels[bestidx].BestMatch; + int trueEnd = evt.m_nmichels[bestidx].trueEndpoint; + int recoEnd = evt.m_nmichels[bestidx].recoEndpoint; + if (overlayfrac < .5 && trueEnd == recoEnd && (matchtype == 3 || matchtype == 4)) return micheldist; + else return -9999.; + } + }; + +std::function best_pionrange_truebad_clus = [](const CVUniverse& univ, const MichelEvent& evt) + { + int bestidx = evt.m_idx; + if (bestidx < 0) return -9999.; + else{ + double micheldist = evt.m_nmichels[bestidx].Best3Ddist; + int overlayfrac = evt.m_nmichels[bestidx].overlay_fraction; + int matchtype = evt.m_nmichels[bestidx].BestMatch; + int trueEnd = evt.m_nmichels[bestidx].trueEndpoint; + int recoEnd = evt.m_nmichels[bestidx].recoEndpoint; + if (overlayfrac < .5 && trueEnd != recoEnd && (matchtype == 3 || matchtype == 4)) return micheldist; + else return -9999.; + } + }; + + +std::function lowesttpi = [](const CVUniverse& univ, const MichelEvent& evt) +{ + double lowtpi = evt.lowTpi; + return lowtpi; + +}; + studies.push_back(new PerMichelEventVarByGENIELabel(lowesttpi, "LowestKE_pion", "MeV", 100, 0, 1000., error_bands)); + + studies.push_back(new PerMichelEventVarByGENIELabel(best_pionrange, "best_pionrange", "mm", 100, 0.0, 2000.0, error_bands)); + studies.push_back(new PerMichelEventVarByGENIELabel(best_pionrange_overlay_clus, "best_pionrange_overlay_clus", "mm", 100, 0.0, 2000.0, error_bands)); + studies.push_back(new PerMichelEventVarByGENIELabel(best_pionrange_overlay_vtx , "best_pionrange_overlay_vtx ", "mm", 100, 0.0, 2000.0, error_bands)); + studies.push_back(new PerMichelEventVarByGENIELabel(best_pionrange_truegood_vtx, "best_pionrange_truegood_vtx", "mm", 100, 0.0, 2000.0, error_bands)); + studies.push_back(new PerMichelEventVarByGENIELabel(best_pionrange_truebad_vtx, "best_pionrange_truebad_vtx", "mm", 100, 0.0, 2000.0, error_bands)); + studies.push_back(new PerMichelEventVarByGENIELabel(best_pionrange_truegood_clus, "best_pionrange_truegood_clus", "mm", 100, 0.0, 2000.0, error_bands)); + studies.push_back(new PerMichelEventVarByGENIELabel(best_pionrange_truebad_clus, "best_pionrange_truebad_clus", "mm", 100, 0.0, 2000.0, error_bands)); + + +// Set Up Data Universe + + CVUniverse* data_universe = new CVUniverse(options.m_data); + std::vector data_band = {data_universe}; + std::map > data_error_bands; + data_error_bands["cv"] = data_band; + + std::vector data_studies; + + data_studies.push_back(new PerMichelEventVarByGENIELabel(best_pionrange, "best_pionrange", "mm", 100, 0.0, 2000.0, data_error_bands)); + data_studies.push_back(new PerMichelVarByGENIELabel(delta_t, "michelmuon_deltat", "#mus", 30, 0.0, 9.0, data_error_bands)); + data_studies.push_back(new PerMichelVarByGENIELabel(permichel_range, "permichel_pirange", "mm", 100, 0.0, 2000.0, data_error_bands)); + data_studies.push_back(new PerMichelVarByGENIELabel(pion_angle, "pion_angle", "cos(#theta)", 21, -1.0, 1., data_error_bands)); + data_studies.push_back(new PerMichelVarByGENIELabel(pion_angle_range1, "pion_angle_range1", "cos(#theta)", 21, -1.0, 1., data_error_bands)); + data_studies.push_back(new PerMichelVarByGENIELabel(pion_angle_range2, "pion_angle_range2", "cos(#theta)", 21, -1.0, 1., data_error_bands)); + data_studies.push_back(new PerMichelVarByGENIELabel(pion_angle_range3, "pion_angle_range3", "cos(#theta)", 21, -1.0, 1., data_error_bands)); + data_studies.push_back(new PerMichelVarByGENIELabel(pion_angle_range4, "pion_angle_range4", "cos(#theta)", 21, -1.0, 1., data_error_bands)); + + + for(auto& var: vars) var->InitializeMCHists(error_bands, truth_bands); + for(auto& var: vars) var->InitializeDATAHists(data_band); + + for(auto& var: vars2D) var->InitializeMCHists(error_bands, truth_bands); + for(auto& var: vars2D) var->InitializeDATAHists(data_band); + + // Loop entries and fill + try + { + CVUniverse::SetTruth(false); + LoopAndFillEventSelection(options.m_mc, error_bands, vars, vars2D, studies, mycuts, model); + CVUniverse::SetTruth(true); + LoopAndFillEffDenom(options.m_truth, truth_bands, vars, vars2D,studies, mycuts, model); + options.PrintMacroConfiguration(argv[0]); + std::cout << "MC cut summary:\n" << mycuts << "\n"; + mycuts.resetStats(); + + //CVUniverse::SetTruth(false); + //LoopAndFillData(options.m_data, data_band, vars, vars2D, data_studies, mycuts); + //std::cout << "Data cut summary:\n" << mycuts << "\n"; + + //Write MC results + TFile* mcOutDir = TFile::Open(MC_OUT_FILE_NAME, "RECREATE"); + if(!mcOutDir) + { + std::cerr << "Failed to open a file named " << MC_OUT_FILE_NAME << " in the current directory for writing histograms.\n"; + return badOutputFile; + } + + //TFile* mc_MichelStudies = TFile::Open("AllMichel_hasFittedMichel_500mm.root", "RECREATE"); + //"ALL2DDistprinted_OnlyPionMichels_tpimorethan80meV_forceendpointmatch_2Ddistcut_mc.root", "RECREATE"); + for(auto& study: studies) study->SaveOrDraw(*mc_MichelStudies); + for(auto& var: vars) var->WriteMC(*mcOutDir); + for(auto& var: vars2D) var->Write(*mcOutDir); + for(auto& study: data_studies) study->SaveOrDraw(*data_MichelStudies); + //Protons On Target + auto mcPOT = new TParameter("POTUsed", options.m_mc_pot); + mcPOT->Write(); + mc_MichelStudies->cd(); + mcPOT->Write(); + + PlotUtils::TargetUtils targetInfo; + assert(error_bands["cv"].size() == 1 && "List of error bands must contain a universe named \"cv\" for the flux integral."); + + for(const auto& var: vars) + { + //Flux integral only if systematics are being done (temporary solution) + util::GetFluxIntegral(*error_bands["cv"].front(), var->efficiencyNumerator->hist)->Write((var->GetName() + "_reweightedflux_integrated").c_str()); + //Always use MC number of nucleons for cross section + auto nNucleons = new TParameter((var->GetName() + "_fiducial_nucleons").c_str(), targetInfo.GetTrackerNNucleons(minZ, maxZ, true, apothem)); + nNucleons->Write(); + } + + //Write data results + TFile* dataOutDir = TFile::Open(DATA_OUT_FILE_NAME, "RECREATE"); + if(!dataOutDir) + { + std::cerr << "Failed to open a file named " << DATA_OUT_FILE_NAME << " in the current directory for writing histograms.\n"; + return badOutputFile; + } + + for(auto& var: vars) var->WriteData(*dataOutDir); + + //Protons On Target + auto dataPOT = new TParameter("POTUsed", options.m_data_pot); + dataPOT->Write(); + data_MichelStudies->cd(); + dataPOT->Write(); + std::cout << "Success" << std::endl; + } + catch(const ROOT::exception& e) + { + std::cerr << "Ending on a ROOT error message. No histograms will be produced.\n" + << "If the message talks about \"TNetXNGFile\", this could be a problem with dCache. The message is:\n" + << e.what() << "\n" << USAGE << "\n"; + return badFileRead; + } + + return success; +} From 7b3247c71a845e1147d02e0abacf0a4566cdee7a Mon Sep 17 00:00:00 2001 From: Ben Messerly Date: Wed, 5 Jan 2022 17:36:51 -0600 Subject: [PATCH 02/17] Prepare GetTpi for Mehreen michels. --- includes/CVUniverse.cxx | 27 ++++++++++++++------------- includes/CVUniverse.h | 1 + 2 files changed, 15 insertions(+), 13 deletions(-) diff --git a/includes/CVUniverse.cxx b/includes/CVUniverse.cxx index 26636f3..8334a08 100644 --- a/includes/CVUniverse.cxx +++ b/includes/CVUniverse.cxx @@ -268,21 +268,22 @@ double CVUniverse::GetThetapiDeg(RecoPionIdx hadron) const { } double CVUniverse::GetTpi(RecoPionIdx hadron) const { - if (hadron == -1) { - std::cerr << "CVU::GetTpi: pion_idx = -1.\n" - "In the future this will be the code for a vertex pion.\n"; - throw hadron; + // We have a pion associated with a track -- prefer this to a Mehreen vertex pion + if (hadron != -1) { + // return (GetVecElem("MasterAnaDev_hadron_pion_E", hadron) + // - CCNuPionIncConsts::CHARGED_PION_MASS)/0.96; + double Epi = GetVecElem("MasterAnaDev_pion_E", hadron); + if (Epi == 0) { + return GetTpiMBR(hadron); // TODO maybe do from momentum instead + // Not sure what this fail mode means. + // std::cout << "CVUniverse::GetTpi: warning Epi < 0\n"; + // return 0; + } + return Epi - CCNuPionIncConsts::CHARGED_PION_MASS; } - // return (GetVecElem("MasterAnaDev_hadron_pion_E", hadron) - // - CCNuPionIncConsts::CHARGED_PION_MASS)/0.96; - double Epi = GetVecElem("MasterAnaDev_pion_E", hadron); - if (Epi == 0) { - return GetTpiMBR(hadron); // TODO maybe do from momentum instead - // Not sure what this fail mode means. - // std::cout << "CVUniverse::GetTpi: warning Epi < 0\n"; - // return 0; + else { + return GetTpiMehreen(); } - return Epi - CCNuPionIncConsts::CHARGED_PION_MASS; } double CVUniverse::GetTpiMBR(RecoPionIdx hadron) const { diff --git a/includes/CVUniverse.h b/includes/CVUniverse.h index 41c7840..6519df3 100644 --- a/includes/CVUniverse.h +++ b/includes/CVUniverse.h @@ -75,6 +75,7 @@ class CVUniverse : public PlotUtils::MinervaUniverse { virtual double GetTpiMBR(RecoPionIdx) const; virtual double GetpimuAngle(RecoPionIdx) const; virtual double Gett(RecoPionIdx) const; + virtual double GetTpiMehreen() { return 0; } //============================================================================== // Truth From efc86d1447edbe458ed0cbc10fb5fd7e61978d5f Mon Sep 17 00:00:00 2001 From: Ben Messerly Date: Wed, 5 Jan 2022 17:40:39 -0600 Subject: [PATCH 03/17] Reformat and prepare the michel cut for mehreen michels. --- includes/Cuts.cxx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/includes/Cuts.cxx b/includes/Cuts.cxx index cc32193..0ab428e 100644 --- a/includes/Cuts.cxx +++ b/includes/Cuts.cxx @@ -166,7 +166,6 @@ std::tuple> PassesCuts( //============================================================================== // Fuction to count the number of events that pass the cuts //============================================================================== - EventCount PassedCuts(const CVUniverse& univ, std::vector& pion_candidate_idxs, bool is_mc, SignalDefinition signal_definition, @@ -272,7 +271,8 @@ bool PassesCut(const CVUniverse& univ, const ECuts cut, const bool is_mc, else endpoint_michels.insert(m); } - return endpoint_michels.size() > 0; + // mehreen_michels = GetPassingMehreenMichels(); + return endpoint_michels.size() > 0 /*|| mehreen_michels.size() = 0*/; } case kAtLeastOnePionCandidateTrack: From e42a38292f12f17d42f415d65d3446421656fbb5 Mon Sep 17 00:00:00 2001 From: Ben Messerly Date: Wed, 5 Jan 2022 17:42:06 -0600 Subject: [PATCH 04/17] Clean up. --- includes/MehreenMichels/HasMichel_cut.h | 5 +- includes/MehreenMichels/Michel.h | 113 +- xsec/mehreenEventLoop.cpp | 2318 +++++++++++++---------- 3 files changed, 1399 insertions(+), 1037 deletions(-) diff --git a/includes/MehreenMichels/HasMichel_cut.h b/includes/MehreenMichels/HasMichel_cut.h index c3eb23b..c044a7b 100644 --- a/includes/MehreenMichels/HasMichel_cut.h +++ b/includes/MehreenMichels/HasMichel_cut.h @@ -27,9 +27,7 @@ class hasMichel : public PlotUtils::Cut { evt.m_ntruepiparents.push_back(current_michel); // if (current_michel.overlay_fraction >= 0.5) continue; // if (current_michel.true_parentpdg != 211) continue; - double dist = - current_michel.Best3Ddist; // getting the minimum pion range (vertex - // to Michel/Clus distance) + double dist = current_michel.Best3Ddist; // getting the minimum pion range (vertex to Michel/Clus distance) // std::cout << "Pion Energy of Current Michel is " << // current_michel.pionKE << std::endl; if (dist <= evt.m_bestdist) { @@ -74,7 +72,6 @@ class hasMichel : public PlotUtils::Cut { if (pdg == 211) npiplus++; else if (pdg == 111) npi0++; else if (pdg == 321 || 311) nkaons++; - } if (npiplus == 1 && npi0 == 0) evt.eventtype = 1; diff --git a/includes/MehreenMichels/Michel.h b/includes/MehreenMichels/Michel.h index 9898fe8..8dbcb12 100644 --- a/includes/MehreenMichels/Michel.h +++ b/includes/MehreenMichels/Michel.h @@ -296,7 +296,8 @@ void Michel::GetBestMatch() { this->up_to_vertex_dist3D, this->down_to_vertex_dist3D, this->up_clus_michel_dist3D, this->down_clus_michel_dist3D}; // std::cout << "The distances for this michel are upvtx, downvtx, upclus, - // downclus " << distances[0] << " - " << distances[1] << " - " << distances[2] + // downclus " << distances[0] << " - " << distances[1] << " - " << + // distances[2] // << " - " << distances[3] << " - " << std::endl; std::sort(distances.begin(), distances.end()); @@ -305,56 +306,57 @@ void Michel::GetBestMatch() { // std::endl; // This bit of code will try to force the true Endpoint of hte Michel /* - if (trueEndpoint == 1) - { - if (this->up_clus_michel_dist3D > this->up_to_vertex_dist3D){ - upvtxmatch = 1; - this->best_XZ = this->up_to_vertex_XZ; - this->best_UZ = this->up_to_vertex_UZ; - this->best_VZ = this->up_to_vertex_VZ; - this->BestMatch = 1; - this->Best3Ddist = this->up_to_vertex_dist3D; - - } - else if (this->up_clus_michel_dist3D < this->up_to_vertex_dist3D) + if (trueEndpoint == 1) { - this->BestMatch = 3; - this->best_XZ = this->up_to_clus_XZ; - this->best_UZ = this->up_to_clus_VZ; - this->best_VZ = this->up_to_clus_UZ; - this->Best3Ddist = this->up_clus_michvtx_dist3D; - upclusmatch = 1; - } - } - else if (trueEndpoint == 2) - { - if (this->down_to_vertex_dist3D < this->down_clus_michel_dist3D) - { - this->BestMatch = 2; - this->best_XZ = this->down_to_vertex_XZ; - this->best_UZ = this->down_to_vertex_UZ; - this->best_VZ = this->down_to_vertex_VZ; - this->Best3Ddist = this->down_to_vertex_dist3D; - downvtxmatch = 1; + if (this->up_clus_michel_dist3D > this->up_to_vertex_dist3D){ + upvtxmatch = 1; + this->best_XZ = this->up_to_vertex_XZ; + this->best_UZ = this->up_to_vertex_UZ; + this->best_VZ = this->up_to_vertex_VZ; + this->BestMatch = 1; + this->Best3Ddist = this->up_to_vertex_dist3D; + + } + else if (this->up_clus_michel_dist3D < this->up_to_vertex_dist3D) + { + this->BestMatch = 3; + this->best_XZ = this->up_to_clus_XZ; + this->best_UZ = this->up_to_clus_VZ; + this->best_VZ = this->up_to_clus_UZ; + this->Best3Ddist = this->up_clus_michvtx_dist3D; + upclusmatch = 1; + } } - else if (this->down_to_vertex_dist3D > this->down_clus_michel_dist3D) + else if (trueEndpoint == 2) { - this->BestMatch = 4; - this->Best3Ddist = this->down_clus_michvtx_dist3D; - this->best_XZ = this->down_to_clus_XZ; - this->best_UZ = this->down_to_clus_UZ; - this->best_VZ = this->down_to_clus_VZ; - downclusmatch = 1; + if (this->down_to_vertex_dist3D < this->down_clus_michel_dist3D) + { + this->BestMatch = 2; + this->best_XZ = this->down_to_vertex_XZ; + this->best_UZ = this->down_to_vertex_UZ; + this->best_VZ = this->down_to_vertex_VZ; + this->Best3Ddist = this->down_to_vertex_dist3D; + downvtxmatch = 1; + } + else if (this->down_to_vertex_dist3D > this->down_clus_michel_dist3D) + { + this->BestMatch = 4; + this->Best3Ddist = this->down_clus_michvtx_dist3D; + this->best_XZ = this->down_to_clus_XZ; + this->best_UZ = this->down_to_clus_UZ; + this->best_VZ = this->down_to_clus_VZ; + downclusmatch = 1; + } } - } - else{ - this->BestMatch = 0; - this->Best3Ddist = 9999.; - this->best_XZ = 9999.; - this->best_UZ = 9999.; - this->best_VZ = 9999.; + else{ + this->BestMatch = 0; + this->Best3Ddist = 9999.; + this->best_XZ = 9999.; + this->best_UZ = 9999.; + this->best_VZ = 9999.; } -*/ + */ + // This bit of code will try to find the best 3D distance end point if (distances[0] == this->up_to_vertex_dist3D) { upvtxmatch = 1; @@ -781,8 +783,10 @@ void Michel::DoesMichelMatchClus(const CVUniverse& univ) { // std::cout << "Printing details about Endpoint // 1 X cluster index "<< i << " energy : " << // energy << " time : " << time << " pos : " << - // pos << " zpos : " << zpos << " view : " << view - // << " timedifference with Michel : " << timediff + // pos << " zpos : " << zpos << " view : " << + // view + // << " timedifference with Michel : " << + // timediff // << std::endl; clusx1.push_back(pos); @@ -794,8 +798,8 @@ void Michel::DoesMichelMatchClus(const CVUniverse& univ) { this->cluster_to_down_match.push_back(current_cluster); // TODO remove // std::cout << "Printing details about Endpoint 2 X cluster "<< i << " // energy : " << energy << " time : " << time << " pos : " << pos << " - // zpos: " << zpos << " view : " << view << " timediff : " << timediff << - // std::endl; + // zpos: " << zpos << " view : " << view << " timediff : " << timediff + // << std::endl; clusx2.push_back(pos); clusx2.push_back(zpos); @@ -1154,16 +1158,17 @@ void Michel::DoesMichelMatchClus(const CVUniverse& univ) { double mdist2 = sqrt(pow(mclusx2diff, 2) + pow(mclusy2diff, 2) + pow(mclusz2diff, 2)); // std::cout << " The michel endpoint 1 - cluster 3D point distance is " << - // mdist1 << std::endl; std::cout << " The michel endpoint 2 - cluster 3D point - // distance is " << mdist2 << std::endl; Saving all the distances to the Michel - // member data + // mdist1 << std::endl; std::cout << " The michel endpoint 2 - cluster 3D + // point distance is " << mdist2 << std::endl; Saving all the distances to the + // Michel member data this->down_clus_michel_dist3D = mdist2; this->up_clus_michel_dist3D = mdist1; this->up_to_cluster_dist3D = dist1; this->down_to_cluster_dist3D = dist2; // std::cout << "Printing 3D distances to vertex for cluster matches " << // dist1 << " and " << dist2 << std::endl; std::cout << "Printing 3D distances - // to michel for cluster matches " << mdist1 << " and " << mdist2 << std::endl; + // to michel for cluster matches " << mdist1 << " and " << mdist2 << + // std::endl; double michdist1 = sqrt(pow(michvtx_x1diff, 2) + pow(michvtx_y1diff, 2) + pow(michvtx_z1diff, 2)); double michdist2 = sqrt(pow(michvtx_x2diff, 2) + pow(michvtx_y2diff, 2) + diff --git a/xsec/mehreenEventLoop.cpp b/xsec/mehreenEventLoop.cpp index 713f493..442648a 100644 --- a/xsec/mehreenEventLoop.cpp +++ b/xsec/mehreenEventLoop.cpp @@ -1,37 +1,52 @@ -#define MC_OUT_FILE_NAME "runEventLoopMC_Dec312021_q3bin2_atleast1pi_distcuts.root" -#define DATA_OUT_FILE_NAME "runEventLoopData_Dec312021_q3bin2_atleast1pi_distcuts.root" - -#define USAGE \ -"\n*** USAGE ***\n"\ -"runEventLoop \n\n"\ -"*** Explanation ***\n"\ -"Reduce MasterAnaDev AnaTuples to event selection histograms to extract a\n"\ -"single-differential inclusive cross section for the 2021 MINERvA 101 tutorial.\n\n"\ -"*** The Input Files ***\n"\ -"Playlist files are plaintext files with 1 file name per line. Filenames may be\n"\ -"xrootd URLs or refer to the local filesystem. The first playlist file's\n"\ -"entries will be treated like data, and the second playlist's entries must\n"\ -"have the \"Truth\" tree to use for calculating the efficiency denominator.\n\n"\ -"*** Output ***\n"\ -"Produces a two files, " MC_OUT_FILE_NAME " and " DATA_OUT_FILE_NAME ", with\n"\ -"all histograms needed for the ExtractCrossSection program also built by this\n"\ -"package. You'll need a .rootlogon.C that loads ROOT object definitions from\n"\ -"PlotUtils to access systematics information from these files.\n\n"\ -"*** Environment Variables ***\n"\ -"Setting up this package appends to PATH and LD_LIBRARY_PATH. PLOTUTILSROOT,\n"\ -"MPARAMFILESROOT, and MPARAMFILES must be set according to the setup scripts in\n"\ -"those packages for systematics and flux reweighters to function.\n"\ -"If MNV101_SKIP_SYST is defined at all, output histograms will have no error bands.\n"\ -"This is useful for debugging the CV and running warping studies.\n\n"\ -"*** Return Codes ***\n"\ -"0 indicates success. All histograms are valid only in this case. Any other\n"\ -"return code indicates that histograms should not be used. Error messages\n"\ -"about what went wrong will be printed to stderr. So, they'll end up in your\n"\ -"terminal, but you can separate them from everything else with something like:\n"\ -"\"runEventLoop data.txt mc.txt 2> errors.txt\"\n" - -enum ErrorCodes -{ +#define MC_OUT_FILE_NAME \ + "runEventLoopMC_Dec312021_q3bin2_atleast1pi_distcuts.root" +#define DATA_OUT_FILE_NAME \ + "runEventLoopData_Dec312021_q3bin2_atleast1pi_distcuts.root" + +#define USAGE \ + "\n*** USAGE ***\n" \ + "runEventLoop \n\n" \ + "*** Explanation ***\n" \ + "Reduce MasterAnaDev AnaTuples to event selection histograms to extract a\n" \ + "single-differential inclusive cross section for the 2021 MINERvA 101 " \ + "tutorial.\n\n" \ + "*** The Input Files ***\n" \ + "Playlist files are plaintext files with 1 file name per line. Filenames " \ + "may be\n" \ + "xrootd URLs or refer to the local filesystem. The first playlist file's\n" \ + "entries will be treated like data, and the second playlist's entries " \ + "must\n" \ + "have the \"Truth\" tree to use for calculating the efficiency " \ + "denominator.\n\n" \ + "*** Output ***\n" \ + "Produces a two files, " MC_OUT_FILE_NAME " and " DATA_OUT_FILE_NAME \ + ", with\n" \ + "all histograms needed for the ExtractCrossSection program also built by " \ + "this\n" \ + "package. You'll need a .rootlogon.C that loads ROOT object definitions " \ + "from\n" \ + "PlotUtils to access systematics information from these files.\n\n" \ + "*** Environment Variables ***\n" \ + "Setting up this package appends to PATH and LD_LIBRARY_PATH. " \ + "PLOTUTILSROOT,\n" \ + "MPARAMFILESROOT, and MPARAMFILES must be set according to the setup " \ + "scripts in\n" \ + "those packages for systematics and flux reweighters to function.\n" \ + "If MNV101_SKIP_SYST is defined at all, output histograms will have no " \ + "error bands.\n" \ + "This is useful for debugging the CV and running warping studies.\n\n" \ + "*** Return Codes ***\n" \ + "0 indicates success. All histograms are valid only in this case. Any " \ + "other\n" \ + "return code indicates that histograms should not be used. Error " \ + "messages\n" \ + "about what went wrong will be printed to stderr. So, they'll end up in " \ + "your\n" \ + "terminal, but you can separate them from everything else with something " \ + "like:\n" \ + "\"runEventLoop data.txt mc.txt 2> errors.txt\"\n" + +enum ErrorCodes { success = 0, badCmdLine = 1, badInputFile = 2, @@ -39,80 +54,79 @@ enum ErrorCodes badOutputFile = 4 }; -//PlotUtils includes -//No junk from PlotUtils please! I already -//know that MnvH1D does horrible horrible things. +// PlotUtils includes +// No junk from PlotUtils please! I already +// know that MnvH1D does horrible horrible things. #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Woverloaded-virtual" -//Includes from this package +// Includes from this package +#include "cuts/BestMichelDistance2D.h" +#include "cuts/MaxPzMu.h" +#include "cuts/SignalDefinition.h" +#include "cuts/VtxMatchFirst.h" +#include "cuts/hasMichel.h" +#include "cuts/hasTruePion.h" +#include "cuts/q3RecoCut.h" #include "event/CVUniverse.h" +#include "event/Michel.h" #include "event/MichelEvent.h" +#include "studies/PerMichel2DVar.h" +#include "studies/PerMichelEventVarByGENIELabel.h" +#include "studies/PerMichelVarByGENIELabel.h" +#include "studies/Study.h" #include "systematics/Systematics.h" -#include "cuts/MaxPzMu.h" -#include "util/Variable.h" -#include "util/Variable2D.h" #include "util/GetFluxIntegral.h" #include "util/GetPlaylist.h" -#include "cuts/SignalDefinition.h" -#include "cuts/q3RecoCut.h" -#include "studies/Study.h" -#include "studies/PerMichelVarByGENIELabel.h" -#include "studies/PerMichelEventVarByGENIELabel.h" -#include "studies/PerMichel2DVar.h" -#include "cuts/hasMichel.h" -#include "event/Michel.h" -#include "cuts/BestMichelDistance2D.h" -#include "cuts/VtxMatchFirst.h" -#include "cuts/hasTruePion.h" +#include "util/Variable.h" +#include "util/Variable2D.h" //#include "Binning.h" //TODO: Fix me -//PlotUtils includes -#include "PlotUtils/makeChainWrapper.h" -#include "PlotUtils/HistWrapper.h" -#include "PlotUtils/Hist2DWrapper.h" -#include "PlotUtils/MacroUtil.h" -#include "PlotUtils/MnvPlotter.h" +// PlotUtils includes #include "PlotUtils/CCInclusiveCuts.h" #include "PlotUtils/CCInclusiveSignal.h" -#include "PlotUtils/CrashOnROOTMessage.h" //Sets up ROOT's debug callbacks by itself +#include "PlotUtils/CrashOnROOTMessage.h" //Sets up ROOT's debug callbacks by itself #include "PlotUtils/Cutter.h" -#include "PlotUtils/Model.h" #include "PlotUtils/FluxAndCVReweighter.h" #include "PlotUtils/GENIEReweighter.h" +#include "PlotUtils/Hist2DWrapper.h" +#include "PlotUtils/HistWrapper.h" #include "PlotUtils/LowRecoil2p2hReweighter.h" -#include "PlotUtils/RPAReweighter.h" #include "PlotUtils/MINOSEfficiencyReweighter.h" +#include "PlotUtils/MacroUtil.h" +#include "PlotUtils/MnvPlotter.h" +#include "PlotUtils/Model.h" +#include "PlotUtils/RPAReweighter.h" #include "PlotUtils/TargetUtils.h" +#include "PlotUtils/makeChainWrapper.h" #pragma GCC diagnostic pop -//ROOT includes +// ROOT includes #include "TParameter.h" -//c++ includes +// c++ includes +#include //getenv() #include -#include //getenv() //============================================================================== // Loop and Fill //============================================================================== void LoopAndFillEventSelection( PlotUtils::ChainWrapper* chain, - std::map > error_bands, - std::vector vars, - std::vector vars2D, + std::map> error_bands, + std::vector vars, std::vector vars2D, std::vector studies, PlotUtils::Cutter& michelcuts, - PlotUtils::Model& model) -{ - assert(!error_bands["cv"].empty() && "\"cv\" error band is empty! Can't set Model weight."); + PlotUtils::Model& model) { + assert(!error_bands["cv"].empty() && + "\"cv\" error band is empty! Can't set Model weight."); auto& cvUniv = error_bands["cv"].front(); std::cout << "Starting MC reco loop...\n"; const int nEntries = chain->GetEntries(); - for (int i=0; iSetEntry(i); @@ -122,147 +136,192 @@ void LoopAndFillEventSelection( //========================================= // Systematics loop(s) //========================================= - for (auto band : error_bands) - { + for (auto band : error_bands) { std::vector error_band_universes = band.second; - for (auto universe : error_band_universes) - { - MichelEvent myevent; // make sure your event is inside the error band loop. - + for (auto universe : error_band_universes) { + MichelEvent + myevent; // make sure your event is inside the error band loop. + // Tell the Event which entry in the TChain it's looking at universe->SetEntry(i); - - // This is where you would Access/create a Michel - - //weight is ignored in isMCSelected() for all but the CV Universe. - if (!michelcuts.isMCSelected(*universe, myevent, cvWeight).all()) continue; //all is another function that will later help me with sidebands - const double weight = model.GetWeight(*universe, myevent); //Only calculate the per-universe weight for events that will actually use it. - std::vector q3bin1weights = {0.82435, 0.830887, 0.862543, 0.917496, 0.991634, 1.08006, 1.17502, 1.2697, 1.35885, 1.43734, 1.49575, 1.51875, 1.47963, 1.34423, 1.13559, 0.918846, 0.788976, 0.735919, 0.71303, 0.706644, 0.70802, 0.710867, 0.711998}; - - std::vector q3bin2weights = {0.169059, 0.182445, 0.242976, 0.339379, 0.459126, 0.586182, 0.708931, 0.82085, 0.924898, 1.03088, 1.14148, 1.24816, 1.32363, 1.32895, 1.24746, 1.06005, 0.868318, 0.767249, 0.771477, 0.835023, 0.913111, 0.971778, 0.987021}; - - std::vector q3bin3weights = {0.406167, 0.400722, 0.381415, 0.359252, 0.345346, 0.355835, 0.406526, 0.510034, 0.666337, 0.857511, 1.04733 , 1.20275 , 1.311 , 1.36494 , 1.34045 , 1.23871 , 1.09418 , 0.959903, 0.908177, 0.930722, 0.995452, 1.05769 , 1.07703 }; - std::vector q3bin4weights = {0.465274 ,0.475479 ,0.521774 ,0.59605 ,0.685829 ,0.781784 ,0.876495 ,0.967061 ,1.05796 ,1.15474 ,1.25674 ,1.35469 ,1.43084 ,1.47119 ,1.47522 ,1.41 ,1.25184 ,1.07685 ,0.968015 ,0.941743 ,0.966135 ,1.00764 ,1.02239}; - - std::vector q3bin5weights = {0.549138, 0.562134, 0.624496, 0.72724, 0.859891, 1.00808, 1.15921, 1.30858, 1.45383, 1.5935, 1.7235, 1.83011, 1.88988, 1.88183, 1.80408, 1.63456, 1.38423, 1.11548, 0.902733, 0.778054, 0.732044, 0.731376, 0.738204}; + // This is where you would Access/create a Michel - std::vector tpibin = {.002, .006, .010, .014, .018, .022, .026, .030, .034, .038, .043, .049, .061, .075, .090, .125, .175, .225, .275, .325, .375, .450, .550}; + // weight is ignored in isMCSelected() for all but the CV Universe. + if (!michelcuts.isMCSelected(*universe, myevent, cvWeight).all()) + continue; // all is another function that will later help me with + // sidebands + const double weight = model.GetWeight( + *universe, myevent); // Only calculate the per-universe weight for + // events that will actually use it. + std::vector q3bin1weights = { + 0.82435, 0.830887, 0.862543, 0.917496, 0.991634, 1.08006, + 1.17502, 1.2697, 1.35885, 1.43734, 1.49575, 1.51875, + 1.47963, 1.34423, 1.13559, 0.918846, 0.788976, 0.735919, + 0.71303, 0.706644, 0.70802, 0.710867, 0.711998}; + + std::vector q3bin2weights = { + 0.169059, 0.182445, 0.242976, 0.339379, 0.459126, 0.586182, + 0.708931, 0.82085, 0.924898, 1.03088, 1.14148, 1.24816, + 1.32363, 1.32895, 1.24746, 1.06005, 0.868318, 0.767249, + 0.771477, 0.835023, 0.913111, 0.971778, 0.987021}; + + std::vector q3bin3weights = { + 0.406167, 0.400722, 0.381415, 0.359252, 0.345346, 0.355835, + 0.406526, 0.510034, 0.666337, 0.857511, 1.04733, 1.20275, + 1.311, 1.36494, 1.34045, 1.23871, 1.09418, 0.959903, + 0.908177, 0.930722, 0.995452, 1.05769, 1.07703}; + + std::vector q3bin4weights = { + 0.465274, 0.475479, 0.521774, 0.59605, 0.685829, 0.781784, + 0.876495, 0.967061, 1.05796, 1.15474, 1.25674, 1.35469, + 1.43084, 1.47119, 1.47522, 1.41, 1.25184, 1.07685, + 0.968015, 0.941743, 0.966135, 1.00764, 1.02239}; + + std::vector q3bin5weights = { + 0.549138, 0.562134, 0.624496, 0.72724, 0.859891, 1.00808, + 1.15921, 1.30858, 1.45383, 1.5935, 1.7235, 1.83011, + 1.88988, 1.88183, 1.80408, 1.63456, 1.38423, 1.11548, + 0.902733, 0.778054, 0.732044, 0.731376, 0.738204}; + + std::vector tpibin = {.002, .006, .010, .014, .018, .022, + .026, .030, .034, .038, .043, .049, + .061, .075, .090, .125, .175, .225, + .275, .325, .375, .450, .550}; double weight2 = weight; std::vector currentbins; - double q3_mecAna = universe->Getq3(); - if (q3_mecAna < 0.40 ) currentbins = q3bin1weights; - else if (q3_mecAna >= 0.40 || q3_mecAna < 0.60) currentbins = q3bin2weights; - else if (q3_mecAna >= 0.60 || q3_mecAna < 0.80) currentbins = q3bin3weights; - else if (q3_mecAna >= 0.80 || q3_mecAna < 1.00) currentbins = q3bin4weights; - else if (q3_mecAna >= 1.00 || q3_mecAna < 1.20) currentbins = q3bin5weights; - - int nbins = currentbins.size(); + double q3_mecAna = universe->Getq3(); + if (q3_mecAna < 0.40) + currentbins = q3bin1weights; + else if (q3_mecAna >= 0.40 || q3_mecAna < 0.60) + currentbins = q3bin2weights; + else if (q3_mecAna >= 0.60 || q3_mecAna < 0.80) + currentbins = q3bin3weights; + else if (q3_mecAna >= 0.80 || q3_mecAna < 1.00) + currentbins = q3bin4weights; + else if (q3_mecAna >= 1.00 || q3_mecAna < 1.20) + currentbins = q3bin5weights; + + int nbins = currentbins.size(); double tpi = universe->GetTrueTpi(); - /* - for (int i = 0; i tpibin[0] && tpi <= 0.500){ - double binedge1 = (tpibin[i]+tpibin[i]); - double binedge2 = (tpibin[i]+tpibin[i+1]); - if (tpi > binedge1 && tpi < binedge2 && tpi < 0.5) { - weight2 = weight*currentbins[i]; - } - } - else{ - if (tpi > .500 ) weight2 = weight*currentbins[22]; - } - } -i*/ - - for(auto& var: vars) var->selectedMCReco->FillUniverse(universe, var->GetRecoValue(*universe), weight2); //"Fake data" for closure + /* + for (int i = 0; i tpibin[0] && tpi <= 0.500){ + double binedge1 = (tpibin[i]+tpibin[i]); + double binedge2 = (tpibin[i]+tpibin[i+1]); + if (tpi > binedge1 && tpi < binedge2 && tpi < + 0.5) { weight2 = weight*currentbins[i]; + } + } + else{ + if (tpi > .500 ) weight2 = + weight*currentbins[22]; + } + } + */ + for (auto& var : vars) + var->selectedMCReco->FillUniverse(universe, + var->GetRecoValue(*universe), + weight2); //"Fake data" for closure const bool isSignal = michelcuts.isSignal(*universe, weight2); - if(isSignal) - { - for(auto& study: studies) study->SelectedSignal(*universe, myevent, weight2); - - for(auto& var: vars) - { - //Cross section components - var->efficiencyNumerator->FillUniverse(universe, var->GetTrueValue(*universe), weight2); - var->migration->FillUniverse(universe, var->GetRecoValue(*universe), var->GetTrueValue(*universe), weight2); - var->selectedSignalReco->FillUniverse(universe, var->GetRecoValue(*universe), weight2); //Efficiency numerator in reco variables. Useful for warping studies. + if (isSignal) { + for (auto& study : studies) + study->SelectedSignal(*universe, myevent, weight2); + + for (auto& var : vars) { + // Cross section components + var->efficiencyNumerator->FillUniverse( + universe, var->GetTrueValue(*universe), weight2); + var->migration->FillUniverse(universe, var->GetRecoValue(*universe), + var->GetTrueValue(*universe), weight2); + var->selectedSignalReco->FillUniverse( + universe, var->GetRecoValue(*universe), + weight2); // Efficiency numerator in reco variables. Useful + // for warping studies. } - for(auto& var: vars2D) - { - var->efficiencyNumerator->FillUniverse(universe, var->GetTrueValueX(*universe), var->GetTrueValueY(*universe), weight2); + for (auto& var : vars2D) { + var->efficiencyNumerator->FillUniverse( + universe, var->GetTrueValueX(*universe), + var->GetTrueValueY(*universe), weight2); } - } - else - { + } else { int bkgd_ID = -1; - if (universe->GetCurrent()==2)bkgd_ID=0; - else bkgd_ID=1; - - for(auto& var: vars) (*var->m_backgroundHists)[bkgd_ID].FillUniverse(universe, var->GetRecoValue(*universe), weight2); - for(auto& var: vars2D) (*var->m_backgroundHists)[bkgd_ID].FillUniverse(universe, var->GetRecoValueX(*universe), var->GetRecoValueY(*universe), weight2); + if (universe->GetCurrent() == 2) + bkgd_ID = 0; + else + bkgd_ID = 1; + + for (auto& var : vars) + (*var->m_backgroundHists)[bkgd_ID].FillUniverse( + universe, var->GetRecoValue(*universe), weight2); + for (auto& var : vars2D) + (*var->m_backgroundHists)[bkgd_ID].FillUniverse( + universe, var->GetRecoValueX(*universe), + var->GetRecoValueY(*universe), weight2); } - } // End band's universe loop - } // End Band loop - } //End entries loop + } // End band's universe loop + } // End Band loop + } // End entries loop std::cout << "Finished MC reco loop.\n"; } -void LoopAndFillData( PlotUtils::ChainWrapper* data, - std::vector data_band, - std::vector vars, - std::vector vars2D, - std::vector studies, - PlotUtils::Cutter& michelcuts) +void LoopAndFillData(PlotUtils::ChainWrapper* data, + std::vector data_band, + std::vector vars, + std::vector vars2D, + std::vector studies, + PlotUtils::Cutter& michelcuts) { std::cout << "Starting data loop...\n"; const int nEntries = data->GetEntries(); - for (int i=0; iGetEntries(); ++i) { + for (int i = 0; i < data->GetEntries(); ++i) { for (auto universe : data_band) { universe->SetEntry(i); - if(i%1000==0) std::cout << i << " / " << nEntries << "\r" << std::flush; - //std::cout << "Creating Michel Event" << std::endl; - MichelEvent myevent; - //std::cout << "Applying Cuts to Data Event " << std::endl; + if (i % 1000 == 0) + std::cout << i << " / " << nEntries << "\r" << std::flush; + // std::cout << "Creating Michel Event" << std::endl; + MichelEvent myevent; + // std::cout << "Applying Cuts to Data Event " << std::endl; if (!michelcuts.isDataSelected(*universe, myevent).all()) continue; - //std::cout << "Filling Data STudies" << std::endl; - for(auto& study: studies) study->Selected(*universe, myevent, 1); + // std::cout << "Filling Data STudies" << std::endl; + for (auto& study : studies) study->Selected(*universe, myevent, 1); - for(auto& var: vars) - { - var->dataHist->FillUniverse(universe, var->GetRecoValue(*universe, myevent.m_idx), 1); + for (auto& var : vars) { + var->dataHist->FillUniverse( + universe, var->GetRecoValue(*universe, myevent.m_idx), 1); } - for(auto& var: vars2D) - { - var->dataHist->FillUniverse(universe, var->GetRecoValueX(*universe), var->GetRecoValueY(*universe), 1); + for (auto& var : vars2D) { + var->dataHist->FillUniverse(universe, var->GetRecoValueX(*universe), + var->GetRecoValueY(*universe), 1); } } } std::cout << "Finished data loop.\n"; } -void LoopAndFillEffDenom( PlotUtils::ChainWrapper* truth, - std::map > truth_bands, - std::vector vars, - std::vector vars2D, - std::vector studies, - PlotUtils::Cutter& michelcuts, - PlotUtils::Model& model) -{ - assert(!truth_bands["cv"].empty() && "\"cv\" error band is empty! Could not set Model entry."); +void LoopAndFillEffDenom( + PlotUtils::ChainWrapper* truth, + std::map> truth_bands, + std::vector vars, std::vector vars2D, + std::vector studies, + PlotUtils::Cutter& michelcuts, + PlotUtils::Model& model) { + assert(!truth_bands["cv"].empty() && + "\"cv\" error band is empty! Could not set Model entry."); auto& cvUniv = truth_bands["cv"].front(); std::cout << "Starting efficiency denominator loop...\n"; const int nEntries = truth->GetEntries(); - for (int i=0; iSetEntry(i); model.SetEntry(*cvUniv, cvEvent); @@ -271,30 +330,34 @@ void LoopAndFillEffDenom( PlotUtils::ChainWrapper* truth, //========================================= // Systematics loop(s) //========================================= - for (auto band : truth_bands) - { + for (auto band : truth_bands) { std::vector truth_band_universes = band.second; - for (auto universe : truth_band_universes) - { - MichelEvent myevent; //Only used to keep the Model happy + for (auto universe : truth_band_universes) { + MichelEvent myevent; // Only used to keep the Model happy // Tell the Event which entry in the TChain it's looking at universe->SetEntry(i); - if (!michelcuts.isEfficiencyDenom(*universe, cvWeight)) continue; //Weight is ignored for isEfficiencyDenom() in all but the CV universe - const double weight = model.GetWeight(*universe, myevent); //Only calculate the weight for events that will use it - - //Fill efficiency denominator now: - for(auto var: vars) - { - var->efficiencyDenominator->FillUniverse(universe, var->GetTrueValue(*universe), weight); + if (!michelcuts.isEfficiencyDenom(*universe, cvWeight)) + continue; // Weight is ignored for isEfficiencyDenom() in all but the + // CV universe + const double weight = model.GetWeight( + *universe, + myevent); // Only calculate the weight for events that will use it + + // Fill efficiency denominator now: + for (auto var : vars) { + var->efficiencyDenominator->FillUniverse( + universe, var->GetTrueValue(*universe), weight); } // Fill Studies denominator: - //for(auto& study: studies) study->SelectedSignal(*universe, cvEvent, weight); + // for(auto& study: studies) study->SelectedSignal(*universe, cvEvent, + // weight); - for(auto var: vars2D) - { - var->efficiencyDenominator->FillUniverse(universe, var->GetTrueValueX(*universe), var->GetTrueValueY(*universe), weight); + for (auto var : vars2D) { + var->efficiencyDenominator->FillUniverse( + universe, var->GetTrueValueX(*universe), + var->GetTrueValueY(*universe), weight); } } } @@ -302,9 +365,10 @@ void LoopAndFillEffDenom( PlotUtils::ChainWrapper* truth, std::cout << "Finished efficiency denominator loop.\n"; } -//Returns false if recoTreeName could not be inferred -bool inferRecoTreeNameAndCheckTreeNames(const std::string& mcPlaylistName, const std::string& dataPlaylistName, std::string& recoTreeName) -{ +// Returns false if recoTreeName could not be inferred +bool inferRecoTreeNameAndCheckTreeNames(const std::string& mcPlaylistName, + const std::string& dataPlaylistName, + std::string& recoTreeName) { const std::vector knownTreeNames = {"Truth", "Meta", "Header"}; bool areFilesOK = false; @@ -312,26 +376,27 @@ bool inferRecoTreeNameAndCheckTreeNames(const std::string& mcPlaylistName, const std::string firstFile = ""; playlist >> firstFile; auto testFile = TFile::Open(firstFile.c_str()); - if(!testFile) - { + if (!testFile) { std::cerr << "Failed to open the first MC file at " << firstFile << "\n"; return false; } - //Does the MC playlist have the Truth tree? This is needed for the efficiency denominator. + // Does the MC playlist have the Truth tree? This is needed for the + // efficiency denominator. const auto truthTree = testFile->Get("Truth"); - if(truthTree == nullptr || !truthTree->IsA()->InheritsFrom(TClass::GetClass("TTree"))) - { - std::cerr << "Could not find the \"Truth\" tree in MC file named " << firstFile << "\n"; + if (truthTree == nullptr || + !truthTree->IsA()->InheritsFrom(TClass::GetClass("TTree"))) { + std::cerr << "Could not find the \"Truth\" tree in MC file named " + << firstFile << "\n"; return false; } - //Figure out what the reco tree name is - for(auto key: *testFile->GetListOfKeys()) - { - if(static_cast(key)->ReadObj()->IsA()->InheritsFrom(TClass::GetClass("TTree")) - && std::find(knownTreeNames.begin(), knownTreeNames.end(), key->GetName()) == knownTreeNames.end()) - { + // Figure out what the reco tree name is + for (auto key : *testFile->GetListOfKeys()) { + if (static_cast(key)->ReadObj()->IsA()->InheritsFrom( + TClass::GetClass("TTree")) && + std::find(knownTreeNames.begin(), knownTreeNames.end(), + key->GetName()) == knownTreeNames.end()) { recoTreeName = key->GetName(); areFilesOK = true; } @@ -339,20 +404,20 @@ bool inferRecoTreeNameAndCheckTreeNames(const std::string& mcPlaylistName, const delete testFile; testFile = nullptr; - //Make sure the data playlist's first file has the same reco tree + // Make sure the data playlist's first file has the same reco tree playlist.open(dataPlaylistName); playlist >> firstFile; testFile = TFile::Open(firstFile.c_str()); - if(!testFile) - { + if (!testFile) { std::cerr << "Failed to open the first data file at " << firstFile << "\n"; return false; } const auto recoTree = testFile->Get(recoTreeName.c_str()); - if(recoTree == nullptr || !recoTree->IsA()->InheritsFrom(TClass::GetClass("TTree"))) - { - std::cerr << "Could not find the \"" << recoTreeName << "\" tree in data file named " << firstFile << "\n"; + if (recoTree == nullptr || + !recoTree->IsA()->InheritsFrom(TClass::GetClass("TTree"))) { + std::cerr << "Could not find the \"" << recoTreeName + << "\" tree in data file named " << firstFile << "\n"; return false; } @@ -362,782 +427,1077 @@ bool inferRecoTreeNameAndCheckTreeNames(const std::string& mcPlaylistName, const //============================================================================== // Main //============================================================================== -int main(const int argc, const char** argv) -{ - TH1::AddDirectory(false); - - //Validate input. - //I expect a data playlist file name and an MC playlist file name which is exactly 2 arguments. - const int nArgsExpected = 2; - if(argc != nArgsExpected + 1) //argc is the size of argv. I check for number of arguments + 1 because - //argv[0] is always the path to the executable. - { - std::cerr << "Expected " << nArgsExpected << " arguments, but got " << argc - 1 << "\n" << USAGE << "\n"; - return badCmdLine; - } - - //One playlist must contain only MC files, and the other must contain only data files. - //Only checking the first file in each playlist because opening each file an extra time - //remotely (e.g. through xrootd) can get expensive. - //TODO: Look in INSTALL_DIR if files not found? - const std::string mc_file_list = argv[2], - data_file_list = argv[1]; - //Check that necessary TTrees exist in the first file of mc_file_list and data_file_list - std::string reco_tree_name; - if(!inferRecoTreeNameAndCheckTreeNames(mc_file_list, data_file_list, reco_tree_name)) - { - std::cerr << "Failed to find required trees in MC playlist " << mc_file_list << " and/or data playlist " << data_file_list << ".\n" << USAGE << "\n"; - return badInputFile; - } - - const bool doCCQENuValidation = (reco_tree_name == "CCQENu"); //Enables extra histograms and might influence which systematics I use. - - const bool is_grid = false; - PlotUtils::MacroUtil options(reco_tree_name, mc_file_list, data_file_list, "minervame1A", true, is_grid); - std::cout << options.m_mc->GetChain()->GetName() << std::endl; - options.m_plist_string = util::GetPlaylist(*options.m_mc, true); //TODO: Put GetPlaylist into PlotUtils::MacroUtil - - // You're required to make some decisions - PlotUtils::MinervaUniverse::SetNuEConstraint(true); - PlotUtils::MinervaUniverse::SetPlaylist(options.m_plist_string); //TODO: Infer this from the files somehow? - PlotUtils::MinervaUniverse::SetAnalysisNuPDG(14); - PlotUtils::MinervaUniverse::SetNFluxUniverses(100); - PlotUtils::MinervaUniverse::SetZExpansionFaReweight(false); - - //Now that we've defined what a cross section is, decide which sample and model - //we're extracting a cross section for. - PlotUtils::Cutter::reco_t sidebands, preCuts; - PlotUtils::Cutter::truth_t signalDefinition, phaseSpace; - - const double minZ = 5980, maxZ = 8422, apothem = 850; //All in mm - preCuts.emplace_back(new reco::ZRange("Tracker", minZ, maxZ)); - preCuts.emplace_back(new reco::Apothem(apothem)); - preCuts.emplace_back(new reco::MaxMuonAngle(20.)); - preCuts.emplace_back(new reco::HasMINOSMatch()); - preCuts.emplace_back(new reco::NoDeadtime(1, "Deadtime")); - preCuts.emplace_back(new reco::IsNeutrino()); - preCuts.emplace_back(new Q3RangeReco(0.4,0.6)); - //preCuts.emplace_back(new hasMichel()); - //preCuts.emplace_back(new BestMichelDistance2D(100.)); - //preCuts.emplace_back(new VtxMatchFirst(200., 102.)); - preCuts.emplace_back(new NPiCut(1)); - preCuts.emplace_back(new hasMichel()); - preCuts.emplace_back(new BestMichelDistance2D(150.)); - - TFile* mc_MichelStudies = TFile::Open("Dec312021_noreweight_distcuts_q3bin2_atleast1pi_MC.root", "RECREATE"); - TFile* data_MichelStudies = TFile::Open("Dec312021_noreweight_distcuts_q3bin2_atleast1pi_data.root", "RECREATE"); - - signalDefinition.emplace_back(new truth::IsNeutrino()); - signalDefinition.emplace_back(new truth::IsCC()); - signalDefinition.emplace_back(new Q3Limit(0.4, 0.6)); - //signalDefinition.emplace_back(new hasTruePion()); - phaseSpace.emplace_back(new truth::ZRange("Tracker", minZ, maxZ)); - phaseSpace.emplace_back(new truth::Apothem(apothem)); - phaseSpace.emplace_back(new truth::MuonAngle(20.)); - phaseSpace.emplace_back(new truth::PZMuMin(1500.)); - - PlotUtils::Cutter mycuts(std::move(preCuts), std::move(sidebands) , std::move(signalDefinition),std::move(phaseSpace)); - - std::vector>> MnvTunev1; - MnvTunev1.emplace_back(new PlotUtils::FluxAndCVReweighter()); - MnvTunev1.emplace_back(new PlotUtils::GENIEReweighter(true, false)); - MnvTunev1.emplace_back(new PlotUtils::LowRecoil2p2hReweighter()); - MnvTunev1.emplace_back(new PlotUtils::MINOSEfficiencyReweighter()); - MnvTunev1.emplace_back(new PlotUtils::RPAReweighter()); - //TODO: Add my pion reweighter here. - Mehreen S. Nov 22, 2021 - PlotUtils::Model model(std::move(MnvTunev1)); - - // Make a map of systematic universes - // Leave out systematics when making validation histograms - const bool doSystematics = (getenv("MNV101_SKIP_SYST") == nullptr); - if(!doSystematics){ - std::cout << "Skipping systematics (except 1 flux universe) because environment variable MNV101_SKIP_SYST is set.\n"; - PlotUtils::MinervaUniverse::SetNFluxUniverses(2); //Necessary to get Flux integral later... Doesn't work with just 1 flux universe though because _that_ triggers "spread errors". - } - - std::map< std::string, std::vector > error_bands; - if(doSystematics) error_bands = GetStandardSystematics(options.m_mc); - else{ - std::map > band_flux = PlotUtils::GetFluxSystematicsMap(options.m_mc, CVUniverse::GetNFluxUniverses()); - error_bands.insert(band_flux.begin(), band_flux.end()); //Necessary to get flux integral later... - } - error_bands["cv"] = {new CVUniverse(options.m_mc)}; - std::map< std::string, std::vector > truth_bands; - if(doSystematics) truth_bands = GetStandardSystematics(options.m_truth); - truth_bands["cv"] = {new CVUniverse(options.m_truth)}; - - std::vector dansPTBins = {0, 0.075, 0.15, 0.25, 0.325, 0.4, 0.475, 0.55, 0.7, 0.85, 1, 1.25, 1.5}, - dansPzBins = {1.5, 2, 2.5, 3, 3.5, 4, 4.5, 5, 6, 7, 8, 9, 10, 15, 20, 40, 60}, - robsEmuBins = {0,1,2,3,4,5,7,9,12,15,18,22,36,50,75,80}, - robsRecoilBins; - - std::vector tpibins = {0, 4., 8., 12., 16., 20., 24., 28., 32., 36., 40., 46., 52., 70., 80., 100., 150., 200., 250., 300., 350., 400., 500., 1000., 2000.}; - - const double robsRecoilBinWidth = 50; //MeV - for(int whichBin = 0; whichBin < 100 + 1; ++whichBin) robsRecoilBins.push_back(robsRecoilBinWidth * whichBin); - - std::vector vars = { - new Variable("pTmu", "p_{T, #mu} [GeV/c]", dansPTBins, &CVUniverse::GetMuonPT, &CVUniverse::GetMuonPTTrue), - new Variable("q3", "q3 [GeV]", dansPTBins, &CVUniverse::Getq3, &CVUniverse::GetTrueQ3) - }; - - std::vector vars2D; - if(doCCQENuValidation) - { - std::cerr << "Detected that tree name is CCQENu. Making validation histograms.\n"; - vars.push_back(new Variable("pzmu", "p_{||, #mu} [GeV/c]", dansPzBins, &CVUniverse::GetMuonPz, &CVUniverse::GetMuonPzTrue)); - vars.push_back(new Variable("Emu", "E_{#mu} [GeV]", robsEmuBins, &CVUniverse::GetEmuGeV, &CVUniverse::GetElepTrueGeV)); - vars.push_back(new Variable("Erecoil", "E_{recoil}", robsRecoilBins, &CVUniverse::GetRecoilE, &CVUniverse::GetTrueEAvail)); //TODO: q0 is not the same as recoil energy without a spline correction - - vars2D.push_back(new Variable2D(*vars[1], *vars[0])); - } - - -//This is where your list of Studies go for PerMichel variables -> Accessed through MichelEvent - - std::vector studies; - - std::function delta_t = [](const CVUniverse& univ, const MichelEvent& evt, const int whichMichel) - { - int evttype = evt.eventtype; - double micheltime = evt.m_nmichels[whichMichel].time; - double vtxtime = univ.GetVertex().t(); - double deltat = (micheltime - vtxtime/1000.); //hopefully this is in microseconds (mus) - //if (evttype == 1) return deltat; - //else return -9999.; - return deltat; - }; - - std::function permichel_range = [](const CVUniverse& univ, const MichelEvent& evt, const int whichMichel) - { - double micheldist = evt.m_nmichels[whichMichel].Best3Ddist; - //if (evt.eventtype == 1) return micheldist; - //else return -9999.; - return micheldist; - }; - std::function pertruepimichel_range = [](const CVUniverse& univ, const MichelEvent& evt, const int whichMichel) - { - double micheldist = evt.m_nmichels[whichMichel].Best3Ddist; - if (evt.m_nmichels[whichMichel].true_parentpdg == 211) return micheldist; - else return -9999.; - }; - std::function permichel_tpi = [](const CVUniverse& univ, const MichelEvent& evt, const int whichMichel) - { - return evt.m_nmichels[whichMichel].pionKE; - - - }; - - std::function overlay_vtx_tpi = [](const CVUniverse& univ, const MichelEvent& evt, const int whichMichel) - { - double micheldist = evt.m_nmichels[whichMichel].Best3Ddist; - double KE = evt.m_nmichels[whichMichel].pionKE; - int matchtype = evt.m_nmichels[whichMichel].BestMatch; - int overlayfrac = evt.m_nmichels[whichMichel].overlay_fraction; - if (overlayfrac > .5 && (matchtype == 1 || matchtype == 2 )) return KE; - else return -9999.; - }; - -std::function overlay_clus_tpi = [](const CVUniverse& univ, const MichelEvent& evt, const int whichMichel) - { - double micheldist = evt.m_nmichels[whichMichel].Best3Ddist; - double KE = evt.m_nmichels[whichMichel].pionKE; - int matchtype = evt.m_nmichels[whichMichel].BestMatch; - int overlayfrac = evt.m_nmichels[whichMichel].overlay_fraction; - if (overlayfrac > .5 && (matchtype == 3 || matchtype == 4 )) return KE; - else return -9999.; - }; - - std::function truemichel_goodvtx_tpi = [](const CVUniverse& univ, const MichelEvent& evt, const int whichMichel) - { - double micheldist = evt.m_nmichels[whichMichel].Best3Ddist; - int overlayfrac = evt.m_nmichels[whichMichel].overlay_fraction; - double KE = evt.m_nmichels[whichMichel].pionKE; - int matchtype = evt.m_nmichels[whichMichel].BestMatch; - int trueEnd = evt.m_nmichels[whichMichel].trueEndpoint; - int recoEnd = evt.m_nmichels[whichMichel].recoEndpoint; - if (overlayfrac < .5 && trueEnd == recoEnd && (matchtype == 1 || matchtype == 2)) return KE; - else return -9999.; - }; - std::function truemichel_goodclus_tpi = [](const CVUniverse& univ, const MichelEvent& evt, const int whichMichel) - { - double micheldist = evt.m_nmichels[whichMichel].Best3Ddist; - int overlayfrac = evt.m_nmichels[whichMichel].overlay_fraction; - double KE = evt.m_nmichels[whichMichel].pionKE; - int matchtype = evt.m_nmichels[whichMichel].BestMatch; - int trueEnd = evt.m_nmichels[whichMichel].trueEndpoint; - int recoEnd = evt.m_nmichels[whichMichel].recoEndpoint; - if (overlayfrac < .5 && trueEnd == recoEnd && (matchtype == 3 || matchtype == 4)) return KE; - else return -9999.; - }; - std::function truemichel_badvtx_tpi = [](const CVUniverse& univ, const MichelEvent& evt, const int whichMichel) - { - double micheldist = evt.m_nmichels[whichMichel].Best3Ddist; - double KE = evt.m_nmichels[whichMichel].pionKE; - int overlayfrac = evt.m_nmichels[whichMichel].overlay_fraction; - int matchtype = evt.m_nmichels[whichMichel].BestMatch; - int trueEnd = evt.m_nmichels[whichMichel].trueEndpoint; - int recoEnd = evt.m_nmichels[whichMichel].recoEndpoint; - if (overlayfrac < .5 && trueEnd != recoEnd && (matchtype == 1 || matchtype == 2)) - { - //univ.PrintArachneLink(); - //std::cout << "Printing Michel Time for bad VTX match type " << evt.m_nmichels[whichMichel].time << std::endl; - return KE; - } - else return -9999.; - }; - - std::function truemichel_badclus_tpi = [](const CVUniverse& univ, const MichelEvent& evt, const int whichMichel) - { - double micheldist = evt.m_nmichels[whichMichel].Best3Ddist; - double KE = evt.m_nmichels[whichMichel].pionKE; - int overlayfrac = evt.m_nmichels[whichMichel].overlay_fraction; - int matchtype = evt.m_nmichels[whichMichel].BestMatch; - int trueEnd = evt.m_nmichels[whichMichel].trueEndpoint; - int recoEnd = evt.m_nmichels[whichMichel].recoEndpoint; - if (overlayfrac < .5 && trueEnd != recoEnd && (matchtype == 3 || matchtype == 4)) - { - //univ.PrintArachneLink(); - //std::cout << "Printing Michel Time for bad CLUS match type " << evt.m_nmichels[whichMichel].time << std::endl; - return KE; - } - else return -9999.; - }; - - - - std::function overlay_vtx_range = [](const CVUniverse& univ, const MichelEvent& evt, const int whichMichel) - { - double micheldist = evt.m_nmichels[whichMichel].Best3Ddist; - - int matchtype = evt.m_nmichels[whichMichel].BestMatch; - int overlayfrac = evt.m_nmichels[whichMichel].overlay_fraction; - if (overlayfrac > .5 && (matchtype == 1 || matchtype == 2 )) return micheldist; - else return -9999.; - }; - - std::function overlay_clus_range = [](const CVUniverse& univ, const MichelEvent& evt, const int whichMichel) - { - double micheldist = evt.m_nmichels[whichMichel].Best3Ddist; - - int matchtype = evt.m_nmichels[whichMichel].BestMatch; - int overlayfrac = evt.m_nmichels[whichMichel].overlay_fraction; - if (overlayfrac > .5 && (matchtype == 3 || matchtype == 4 )) return micheldist; - else return -9999.; - }; - - std::function truemichel_goodvtx_range = [](const CVUniverse& univ, const MichelEvent& evt, const int whichMichel) - { - double micheldist = evt.m_nmichels[whichMichel].Best3Ddist; - int overlayfrac = evt.m_nmichels[whichMichel].overlay_fraction; - - int matchtype = evt.m_nmichels[whichMichel].BestMatch; - int trueEnd = evt.m_nmichels[whichMichel].trueEndpoint; - int recoEnd = evt.m_nmichels[whichMichel].recoEndpoint; - if (overlayfrac < .5 && trueEnd == recoEnd && (matchtype == 1 || matchtype == 2)) return micheldist; - else return -9999.; - }; - - std::function truemichel_goodclus_range = [](const CVUniverse& univ, const MichelEvent& evt, const int whichMichel) - { - double micheldist = evt.m_nmichels[whichMichel].Best3Ddist; - int overlayfrac = evt.m_nmichels[whichMichel].overlay_fraction; - int matchtype = evt.m_nmichels[whichMichel].BestMatch; - int trueEnd = evt.m_nmichels[whichMichel].trueEndpoint; - int recoEnd = evt.m_nmichels[whichMichel].recoEndpoint; - if (overlayfrac < .5 && trueEnd == recoEnd && (matchtype == 3 || matchtype == 4)) return micheldist; - else return -9999.; - }; - - std::function truemichel_badvtx_range = [](const CVUniverse& univ, const MichelEvent& evt, const int whichMichel) - { - double micheldist = evt.m_nmichels[whichMichel].Best3Ddist; - int overlayfrac = evt.m_nmichels[whichMichel].overlay_fraction; - int matchtype = evt.m_nmichels[whichMichel].BestMatch; - int trueEnd = evt.m_nmichels[whichMichel].trueEndpoint; - int recoEnd = evt.m_nmichels[whichMichel].recoEndpoint; - if (overlayfrac < .5 && trueEnd != recoEnd && (matchtype == 1 || matchtype == 2)) - { - // univ.PrintArachneLink(); - // std::cout << "Printing Michel Time for bad VTX match type " << evt.m_nmichels[whichMichel].time << std::endl; - return micheldist; - } - else return -9999.; - }; - std::function truemichel_badclus_range = [](const CVUniverse& univ, const MichelEvent& evt, const int whichMichel) - { - double micheldist = evt.m_nmichels[whichMichel].Best3Ddist; - int overlayfrac = evt.m_nmichels[whichMichel].overlay_fraction; - int matchtype = evt.m_nmichels[whichMichel].BestMatch; - int trueEnd = evt.m_nmichels[whichMichel].trueEndpoint; - int recoEnd = evt.m_nmichels[whichMichel].recoEndpoint; - if (overlayfrac < .5 && trueEnd != recoEnd && (matchtype == 3 || matchtype == 4)) - { - // univ.PrintArachneLink(); - // std::cout << "Printing Michel Time for bad CLUS match type " << evt.m_nmichels[whichMichel].time << std::endl; - return micheldist; - } - else return -9999.; - }; - - -std::function michel_XZ = [](const CVUniverse& univ, const MichelEvent& evt, const int whichMichel) - { - double twoDdist = evt.m_nmichels[whichMichel].best_XZ; - return twoDdist; - }; - -std::function michel_UZ = [](const CVUniverse& univ, const MichelEvent& evt, const int whichMichel) - { - double twoDdist = evt.m_nmichels[whichMichel].best_UZ; - return twoDdist; - }; - - -std::function michel_VZ = [](const CVUniverse& univ, const MichelEvent& evt, const int whichMichel) - { - double twoDdist = evt.m_nmichels[whichMichel].best_VZ; - return twoDdist; - }; - -std::function michel_XZ_upvtx = [](const CVUniverse& univ, const MichelEvent& evt, const int whichMichel) - { - double twoDdist = evt.m_nmichels[whichMichel].up_to_vertex_XZ; - int trueend = evt.m_nmichels[whichMichel].trueEndpoint; - if (trueend == 1) return twoDdist; - else return 9999.; - }; - -std::function michel_UZ_upvtx = [](const CVUniverse& univ, const MichelEvent& evt, const int whichMichel) - { - double twoDdist = evt.m_nmichels[whichMichel].up_to_vertex_UZ; - int trueend = evt.m_nmichels[whichMichel].trueEndpoint; - if (trueend == 1) return twoDdist; - else return 9999.; - }; - - -std::function michel_VZ_upvtx = [](const CVUniverse& univ, const MichelEvent& evt, const int whichMichel) - { - double twoDdist = evt.m_nmichels[whichMichel].up_to_vertex_VZ; - int trueend = evt.m_nmichels[whichMichel].trueEndpoint; - if (trueend == 1) return twoDdist; - else return 9999.; - }; - - -std::function michel_XZ_upclus = [](const CVUniverse& univ, const MichelEvent& evt, const int whichMichel) - { - double twoDdist = evt.m_nmichels[whichMichel].up_to_clus_XZ; - int trueend = evt.m_nmichels[whichMichel].trueEndpoint; - if (trueend == 1) return twoDdist; - else return 9999.; - }; - -std::function michel_UZ_upclus = [](const CVUniverse& univ, const MichelEvent& evt, const int whichMichel) - { - double twoDdist = evt.m_nmichels[whichMichel].up_to_clus_UZ; - int trueend = evt.m_nmichels[whichMichel].trueEndpoint; - if (trueend == 1) return twoDdist; - else return 9999.; - }; - -std::function michel_VZ_upclus = [](const CVUniverse& univ, const MichelEvent& evt, const int whichMichel) - { - double twoDdist = evt.m_nmichels[whichMichel].up_to_clus_VZ; - int trueend = evt.m_nmichels[whichMichel].trueEndpoint; - if (trueend == 1) return twoDdist; - else return 9999.; - }; - -std::function michel_XZ_downclus = [](const CVUniverse& univ, const MichelEvent& evt, const int whichMichel) - { - double twoDdist = evt.m_nmichels[whichMichel].down_to_clus_XZ; - int trueend = evt.m_nmichels[whichMichel].trueEndpoint; - if (trueend == 2) return twoDdist; - else return 9999.; - }; -std::function michel_UZ_downclus = [](const CVUniverse& univ, const MichelEvent& evt, const int whichMichel) - { - double twoDdist = evt.m_nmichels[whichMichel].down_to_clus_UZ; - int trueend = evt.m_nmichels[whichMichel].trueEndpoint; - if (trueend == 2) return twoDdist; - else return 9999.; - }; -std::function michel_VZ_downclus = [](const CVUniverse& univ, const MichelEvent& evt, const int whichMichel) - { - double twoDdist = evt.m_nmichels[whichMichel].down_to_clus_VZ; - int trueend = evt.m_nmichels[whichMichel].trueEndpoint; - if (trueend == 2) return twoDdist; - else return 9999.; - }; -std::function michel_XZ_downvtx = [](const CVUniverse& univ, const MichelEvent& evt, const int whichMichel) - { - double twoDdist = evt.m_nmichels[whichMichel].down_to_vertex_XZ; - int trueend = evt.m_nmichels[whichMichel].trueEndpoint; - if (trueend == 2) return twoDdist; - else return 9999.; - }; - -std::function michel_UZ_downvtx = [](const CVUniverse& univ, const MichelEvent& evt, const int whichMichel) - { - double twoDdist = evt.m_nmichels[whichMichel].down_to_vertex_UZ; - int trueend = evt.m_nmichels[whichMichel].trueEndpoint; - if (trueend == 2) return twoDdist; - else return 9999.; - }; - - - -std::function michel_VZ_downvtx = [](const CVUniverse& univ, const MichelEvent& evt, const int whichMichel) - { - double twoDdist = evt.m_nmichels[whichMichel].down_to_vertex_VZ; - int trueend = evt.m_nmichels[whichMichel].trueEndpoint; - if (trueend == 2) return twoDdist; - else return 9999.; - }; - - -std::function pion_angle = [](const CVUniverse& univ, const MichelEvent& evt, const int whichMichel) - { - double angle = evt.m_nmichels[whichMichel].best_angle; - return cos(angle); - }; - -std::function pion_angle_range1 = [](const CVUniverse& univ, const MichelEvent& evt, const int whichMichel) - { - double angle = evt.m_nmichels[whichMichel].best_angle; - double micheldist = evt.m_nmichels[whichMichel].Best3Ddist; - if (micheldist <= 150.) return cos(angle); - else return 9999.; - }; - -std::function pion_angle_range2 = [](const CVUniverse& univ, const MichelEvent& evt, const int whichMichel) - { - double angle = evt.m_nmichels[whichMichel].best_angle; - double micheldist = evt.m_nmichels[whichMichel].Best3Ddist; - if (micheldist >150. && micheldist <= 250.) return cos(angle); - else return 9999.; - }; - -std::function pion_angle_range3 = [](const CVUniverse& univ, const MichelEvent& evt, const int whichMichel) - { - double angle = evt.m_nmichels[whichMichel].best_angle; - double micheldist = evt.m_nmichels[whichMichel].Best3Ddist; - if (micheldist > 250. && micheldist <=500.) return cos(angle); - else return 9999.; - }; -std::function pion_angle_range4 = [](const CVUniverse& univ, const MichelEvent& evt, const int whichMichel) - { - double angle = evt.m_nmichels[whichMichel].best_angle; - double micheldist = evt.m_nmichels[whichMichel].Best3Ddist; - if (micheldist > 500.) return cos(angle); - else return 9999.; - }; - -std::function true_angle = [](const CVUniverse& univ, const MichelEvent& evt, const int whichMichel) - { - double angle = evt.m_nmichels[whichMichel].true_angle; - return cos(angle); - }; - -std::function true_angle_range1 = [](const CVUniverse& univ, const MichelEvent& evt, const int whichMichel) - { - double angle = evt.m_nmichels[whichMichel].true_angle; - double micheldist = evt.m_nmichels[whichMichel].Best3Ddist; - if (micheldist <= 150.) return cos(angle); - else return 9999.; - }; - -std::function true_angle_range2 = [](const CVUniverse& univ, const MichelEvent& evt, const int whichMichel) - { - double angle = evt.m_nmichels[whichMichel].true_angle; - double micheldist = evt.m_nmichels[whichMichel].Best3Ddist; - if (micheldist > 150 && micheldist <= 250.) return cos(angle); - else return 9999.; - }; -std::function true_angle_range3 = [](const CVUniverse& univ, const MichelEvent& evt, const int whichMichel) - { - double angle = evt.m_nmichels[whichMichel].true_angle; - double micheldist = evt.m_nmichels[whichMichel].Best3Ddist; - if (micheldist > 250 && micheldist <= 500.) return cos(angle); - else return 9999.; - }; - -std::function true_angle_range4 = [](const CVUniverse& univ, const MichelEvent& evt, const int whichMichel) - { - double angle = evt.m_nmichels[whichMichel].true_angle; - double micheldist = evt.m_nmichels[whichMichel].Best3Ddist; - if (micheldist > 500) return cos(angle); - else return 9999.; - }; - studies.push_back(new PerMichelVarByGENIELabel(true_angle, "true_angle", "cos(#theta)", 21, -1.0, 1.0, error_bands)); - studies.push_back(new PerMichelVarByGENIELabel(true_angle_range1, "true_angle_range1", "cos(#theta)", 21, -1.0, 1., error_bands)); - studies.push_back(new PerMichelVarByGENIELabel(true_angle_range2, "true_angle_range2", "cos(#theta)", 21, -1.0, 1., error_bands)); - studies.push_back(new PerMichelVarByGENIELabel(true_angle_range3, "true_angle_range3", "cos(#theta)", 21, -1.0, 1., error_bands)); - studies.push_back(new PerMichelVarByGENIELabel(true_angle_range4, "true_angle_range4", "cos(#theta)", 21, -1.0, 1., error_bands)); - studies.push_back(new PerMichelVarByGENIELabel(pion_angle, "pion_angle", "cos(#theta)", 21, -1.0, 1., error_bands)); - studies.push_back(new PerMichelVarByGENIELabel(pion_angle_range1, "pion_angle_range1", "cos(#theta)", 21, -1.0, 1., error_bands)); - studies.push_back(new PerMichelVarByGENIELabel(pion_angle_range2, "pion_angle_range2", "cos(#theta)", 21, -1.0, 1., error_bands)); - studies.push_back(new PerMichelVarByGENIELabel(pion_angle_range3, "pion_angle_range3", "cos(#theta)", 21, -1.0, 1., error_bands)); - studies.push_back(new PerMichelVarByGENIELabel(pion_angle_range4, "pion_angle_range4", "cos(#theta)", 21, -1.0, 1., error_bands)); - - studies.push_back(new PerMichelVarByGENIELabel(michel_XZ, "best_XZ", "mm", 100, 0.0, 2000., error_bands)); - studies.push_back(new PerMichelVarByGENIELabel(michel_UZ, "best_UZ", "mm", 100, 0.0, 2000., error_bands)); - studies.push_back(new PerMichelVarByGENIELabel(michel_VZ, "best_VZ", "mm", 100, 0.0, 2000., error_bands)); - studies.push_back(new PerMichelVarByGENIELabel(michel_XZ_upvtx, "upvtx_XZ", "mm", 100, 0.0, 2000., error_bands)); - studies.push_back(new PerMichelVarByGENIELabel(michel_XZ_upclus, "upclus_XZ", "mm", 100, 0.0, 2000., error_bands)); - studies.push_back(new PerMichelVarByGENIELabel(michel_XZ_downclus, "downclus_XZ", "mm", 100, 0.0, 2000., error_bands)); - studies.push_back(new PerMichelVarByGENIELabel(michel_XZ_downvtx, "downvtx_XZ", "mm", 100, 0.0, 2000., error_bands)); - studies.push_back(new PerMichelVarByGENIELabel(michel_UZ_upvtx, "upvtx_UZ", "mm", 100, 0.0, 2000., error_bands)); - studies.push_back(new PerMichelVarByGENIELabel(michel_UZ_upclus, "upclus_UZ", "mm", 100, 0.0, 2000., error_bands)); - studies.push_back(new PerMichelVarByGENIELabel(michel_UZ_downclus, "downclus_UZ", "mm", 100, 0.0, 2000., error_bands)); - studies.push_back(new PerMichelVarByGENIELabel(michel_UZ_downvtx, "downvtx_UZ", "mm", 100, 0.0, 2000., error_bands)); - studies.push_back(new PerMichelVarByGENIELabel(michel_VZ_upvtx, "upvtx_VZ", "mm", 100, 0.0, 2000., error_bands)); - studies.push_back(new PerMichelVarByGENIELabel(michel_VZ_upclus, "upclus_VZ", "mm", 100, 0.0, 2000., error_bands)); - studies.push_back(new PerMichelVarByGENIELabel(michel_VZ_downclus, "downclus_VZ", "mm", 100, 0.0, 2000., error_bands)); - studies.push_back(new PerMichelVarByGENIELabel(michel_VZ_downvtx, "downvtx_VZ", "mm", 100, 0.0, 2000., error_bands)); - - studies.push_back(new PerMichelVarByGENIELabel(delta_t, "michelmuon_deltat", "#mus", 30, 0.0, 9.0, error_bands)); - studies.push_back(new PerMichelVarByGENIELabel(permichel_range, "permichel_pirange", "mm", 100, 0.0, 2000.0, error_bands)); - studies.push_back(new PerMichelVarByGENIELabel(overlay_vtx_range, "overlay_vtx_range", "mm", 100, 0.0, 2000.0, error_bands)); - studies.push_back(new PerMichelVarByGENIELabel(overlay_clus_range, "overlay_clus_range", "mm", 100, 0.0, 2000.0, error_bands)); - studies.push_back(new PerMichelVarByGENIELabel(truemichel_goodvtx_range, "truemichel_goodvtx_range", "mm", 100, 0.0, 2000.0, error_bands)); - studies.push_back(new PerMichelVarByGENIELabel(truemichel_goodclus_range, "truemichel_goodclus_range", "mm", 100, 0.0, 2000.0, error_bands)); - studies.push_back(new PerMichelVarByGENIELabel(truemichel_badvtx_range, "truemichel_badvtx_range", "mm", 100, 0.0, 2000.0, error_bands)); - studies.push_back(new PerMichelVarByGENIELabel(truemichel_badclus_range, "truemichel_badclus_range", "mm", 100, 0.0, 2000.0, error_bands)); - studies.push_back(new PerMichelVarByGENIELabel(permichel_tpi, "Per_Michel_PrimaryParentKE", "meV", 100, 0, 1000., error_bands)); - studies.push_back(new PerMichelVarByGENIELabel(pertruepimichel_range, "permichel_pirange_truepi", "mm", 100, 0.0, 2000.0, error_bands)); - - VarConfig deltat_config{"deltat", "#mus", 30, 0., 9.}; - VarConfig pirange_config{"pirange", "mm", 100, 0.0, 2000.0}; - VarConfig tpi_config{"KE", "meV", 100, 0., 1000.}; - - //studies.push_back(new PerMichel2DVar(delta_t, permichel_range, deltat_config, pirange_config, error_bands)); - studies.push_back(new PerMichel2DVar(permichel_tpi, permichel_range, tpi_config, pirange_config, error_bands)); - // studies.push_back(new PerMichel2DVar(overlay_vtx_tpi, overlay_vtx_range, tpi_config, pirange_config, error_bands)); - // studies.push_back(new PerMichel2DVar(overlay_clus_tpi, overlay_clus_range, tpi_config, pirange_config, error_bands)); - // studies.push_back(new PerMichel2DVar(truemichel_goodvtx_range, truemichel_goodvtx_tpi, tpi_config, pirange_config, error_bands)); - // studies.push_back(new PerMichel2DVar(truemichel_goodclus_range, truemichel_goodclus_tpi, tpi_config, pirange_config, error_bands)); - // studies.push_back(new PerMichel2DVar(truemichel_badvtx_range, truemichel_badvtx_tpi, tpi_config, pirange_config, error_bands)); - // studies.push_back(new PerMichel2DVar(truemichel_badclus_range, truemichel_badclus_tpi, tpi_config, pirange_config, error_bands)); -// Studies for Per Event Variables (Best Michel In Event) - - std::function best_pionrange = [](const CVUniverse& univ, const MichelEvent& evt) - { - int bestidx = evt.m_idx; - if (bestidx < 0) return -9999.; - else{ - double dist = evt.m_nmichels[bestidx].Best3Ddist; - return dist; - } - }; - - - std::function best_pionrange_overlay_vtx = [](const CVUniverse& univ, const MichelEvent& evt) - { - int bestidx = evt.m_idx; - if (bestidx < 0) return -9999.; - else{ - double micheldist = evt.m_nmichels[bestidx].Best3Ddist; - int overlayfrac = evt.m_nmichels[bestidx].overlay_fraction; - int matchtype = evt.m_nmichels[bestidx].BestMatch; - int trueEnd = evt.m_nmichels[bestidx].trueEndpoint; - int recoEnd = evt.m_nmichels[bestidx].recoEndpoint; - if (overlayfrac > .5 && (matchtype == 1 || matchtype == 2)) return micheldist; - else return -9999.; - } - }; - - std::function best_pionrange_overlay_clus = [](const CVUniverse& univ, const MichelEvent& evt) - { - int bestidx = evt.m_idx; - if (bestidx < 0) return -9999.; - else{ - double micheldist = evt.m_nmichels[bestidx].Best3Ddist; - int overlayfrac = evt.m_nmichels[bestidx].overlay_fraction; - int matchtype = evt.m_nmichels[bestidx].BestMatch; - int trueEnd = evt.m_nmichels[bestidx].trueEndpoint; - int recoEnd = evt.m_nmichels[bestidx].recoEndpoint; - if (overlayfrac > .5 && (matchtype == 3 || matchtype == 4)) return micheldist; - else return -9999.; - } - }; - -std::function best_pionrange_truegood_vtx = [](const CVUniverse& univ, const MichelEvent& evt) - { - int bestidx = evt.m_idx; - if (bestidx < 0) return -9999.; - else{ - double micheldist = evt.m_nmichels[bestidx].Best3Ddist; - int overlayfrac = evt.m_nmichels[bestidx].overlay_fraction; - int matchtype = evt.m_nmichels[bestidx].BestMatch; - int trueEnd = evt.m_nmichels[bestidx].trueEndpoint; - int recoEnd = evt.m_nmichels[bestidx].recoEndpoint; - if (overlayfrac < .5 && trueEnd == recoEnd && (matchtype == 1 || matchtype == 2)) return micheldist; - else return -9999.; - } - }; - -std::function best_pionrange_truebad_vtx = [](const CVUniverse& univ, const MichelEvent& evt) - { - int bestidx = evt.m_idx; - if (bestidx < 0) return -9999.; - else{ - double micheldist = evt.m_nmichels[bestidx].Best3Ddist; - int overlayfrac = evt.m_nmichels[bestidx].overlay_fraction; - int matchtype = evt.m_nmichels[bestidx].BestMatch; - int trueEnd = evt.m_nmichels[bestidx].trueEndpoint; - int recoEnd = evt.m_nmichels[bestidx].recoEndpoint; - if (overlayfrac < .5 && trueEnd != recoEnd && (matchtype == 1 || matchtype == 2)) return micheldist; - else return -9999.; - } - }; - -std::function best_pionrange_truegood_clus = [](const CVUniverse& univ, const MichelEvent& evt) - { - int bestidx = evt.m_idx; - if (bestidx < 0) return -9999.; - else{ - double micheldist = evt.m_nmichels[bestidx].Best3Ddist; - int overlayfrac = evt.m_nmichels[bestidx].overlay_fraction; - int matchtype = evt.m_nmichels[bestidx].BestMatch; - int trueEnd = evt.m_nmichels[bestidx].trueEndpoint; - int recoEnd = evt.m_nmichels[bestidx].recoEndpoint; - if (overlayfrac < .5 && trueEnd == recoEnd && (matchtype == 3 || matchtype == 4)) return micheldist; - else return -9999.; - } - }; - -std::function best_pionrange_truebad_clus = [](const CVUniverse& univ, const MichelEvent& evt) - { - int bestidx = evt.m_idx; - if (bestidx < 0) return -9999.; - else{ - double micheldist = evt.m_nmichels[bestidx].Best3Ddist; - int overlayfrac = evt.m_nmichels[bestidx].overlay_fraction; - int matchtype = evt.m_nmichels[bestidx].BestMatch; - int trueEnd = evt.m_nmichels[bestidx].trueEndpoint; - int recoEnd = evt.m_nmichels[bestidx].recoEndpoint; - if (overlayfrac < .5 && trueEnd != recoEnd && (matchtype == 3 || matchtype == 4)) return micheldist; - else return -9999.; - } - }; - - -std::function lowesttpi = [](const CVUniverse& univ, const MichelEvent& evt) -{ - double lowtpi = evt.lowTpi; - return lowtpi; - -}; - studies.push_back(new PerMichelEventVarByGENIELabel(lowesttpi, "LowestKE_pion", "MeV", 100, 0, 1000., error_bands)); - - studies.push_back(new PerMichelEventVarByGENIELabel(best_pionrange, "best_pionrange", "mm", 100, 0.0, 2000.0, error_bands)); - studies.push_back(new PerMichelEventVarByGENIELabel(best_pionrange_overlay_clus, "best_pionrange_overlay_clus", "mm", 100, 0.0, 2000.0, error_bands)); - studies.push_back(new PerMichelEventVarByGENIELabel(best_pionrange_overlay_vtx , "best_pionrange_overlay_vtx ", "mm", 100, 0.0, 2000.0, error_bands)); - studies.push_back(new PerMichelEventVarByGENIELabel(best_pionrange_truegood_vtx, "best_pionrange_truegood_vtx", "mm", 100, 0.0, 2000.0, error_bands)); - studies.push_back(new PerMichelEventVarByGENIELabel(best_pionrange_truebad_vtx, "best_pionrange_truebad_vtx", "mm", 100, 0.0, 2000.0, error_bands)); - studies.push_back(new PerMichelEventVarByGENIELabel(best_pionrange_truegood_clus, "best_pionrange_truegood_clus", "mm", 100, 0.0, 2000.0, error_bands)); - studies.push_back(new PerMichelEventVarByGENIELabel(best_pionrange_truebad_clus, "best_pionrange_truebad_clus", "mm", 100, 0.0, 2000.0, error_bands)); - - -// Set Up Data Universe - - CVUniverse* data_universe = new CVUniverse(options.m_data); - std::vector data_band = {data_universe}; - std::map > data_error_bands; - data_error_bands["cv"] = data_band; - - std::vector data_studies; - - data_studies.push_back(new PerMichelEventVarByGENIELabel(best_pionrange, "best_pionrange", "mm", 100, 0.0, 2000.0, data_error_bands)); - data_studies.push_back(new PerMichelVarByGENIELabel(delta_t, "michelmuon_deltat", "#mus", 30, 0.0, 9.0, data_error_bands)); - data_studies.push_back(new PerMichelVarByGENIELabel(permichel_range, "permichel_pirange", "mm", 100, 0.0, 2000.0, data_error_bands)); - data_studies.push_back(new PerMichelVarByGENIELabel(pion_angle, "pion_angle", "cos(#theta)", 21, -1.0, 1., data_error_bands)); - data_studies.push_back(new PerMichelVarByGENIELabel(pion_angle_range1, "pion_angle_range1", "cos(#theta)", 21, -1.0, 1., data_error_bands)); - data_studies.push_back(new PerMichelVarByGENIELabel(pion_angle_range2, "pion_angle_range2", "cos(#theta)", 21, -1.0, 1., data_error_bands)); - data_studies.push_back(new PerMichelVarByGENIELabel(pion_angle_range3, "pion_angle_range3", "cos(#theta)", 21, -1.0, 1., data_error_bands)); - data_studies.push_back(new PerMichelVarByGENIELabel(pion_angle_range4, "pion_angle_range4", "cos(#theta)", 21, -1.0, 1., data_error_bands)); - +int main(const int argc, const char** argv) { + //============================================================================ + // Setup + //============================================================================ + TH1::AddDirectory(false); + + // Validate input. + // I expect a data playlist file name and an MC playlist file name which is + // exactly 2 arguments. + const int nArgsExpected = 2; + // argc is the size of argv. I check for number of arguments + 1 because + // argv[0] is always the path to the executable. + if (argc != nArgsExpected + 1) { + std::cerr << "Expected " << nArgsExpected << " arguments, but got " + << argc - 1 << "\n" + << USAGE << "\n"; + return badCmdLine; + } - for(auto& var: vars) var->InitializeMCHists(error_bands, truth_bands); - for(auto& var: vars) var->InitializeDATAHists(data_band); + // One playlist must contain only MC files, and the other must contain only + // data files. Only checking the first file in each playlist because opening + // each file an extra time remotely (e.g. through xrootd) can get expensive. + // TODO: Look in INSTALL_DIR if files not found? + const std::string mc_file_list = argv[2], data_file_list = argv[1]; + // Check that necessary TTrees exist in the first file of mc_file_list and + // data_file_list + std::string reco_tree_name; + if (!inferRecoTreeNameAndCheckTreeNames(mc_file_list, data_file_list, + reco_tree_name)) { + std::cerr << "Failed to find required trees in MC playlist " << mc_file_list + << " and/or data playlist " << data_file_list << ".\n" + << USAGE << "\n"; + return badInputFile; + } - for(auto& var: vars2D) var->InitializeMCHists(error_bands, truth_bands); - for(auto& var: vars2D) var->InitializeDATAHists(data_band); + const bool doCCQENuValidation = + (reco_tree_name == "CCQENu"); // Enables extra histograms and might + // influence which systematics I use. + + const bool is_grid = false; + PlotUtils::MacroUtil options(reco_tree_name, mc_file_list, data_file_list, "minervame1A", true, is_grid); + std::cout << options.m_mc->GetChain()->GetName() << std::endl; + options.m_plist_string = util::GetPlaylist(*options.m_mc, true); // TODO: Put GetPlaylist into PlotUtils::MacroUtil + + // You're required to make some decisions + PlotUtils::MinervaUniverse::SetNuEConstraint(true); + PlotUtils::MinervaUniverse::SetPlaylist(options.m_plist_string); // TODO: Infer this from the files somehow? + PlotUtils::MinervaUniverse::SetAnalysisNuPDG(14); + PlotUtils::MinervaUniverse::SetNFluxUniverses(100); + PlotUtils::MinervaUniverse::SetZExpansionFaReweight(false); + + // outfiles + TFile* mc_MichelStudies = TFile::Open( + "Dec312021_noreweight_distcuts_q3bin2_atleast1pi_MC.root", "RECREATE"); + TFile* data_MichelStudies = TFile::Open( + "Dec312021_noreweight_distcuts_q3bin2_atleast1pi_data.root", "RECREATE"); + + //============================================================================ + // Cuts + //============================================================================ + // Now that we've defined what a cross section is, decide which sample and + // model we're extracting a cross section for. + PlotUtils::Cutter::reco_t sidebands, preCuts; + PlotUtils::Cutter::truth_t signalDefinition, phaseSpace; + + const double minZ = 5980, maxZ = 8422, apothem = 850; // All in mm + preCuts.emplace_back( + new reco::ZRange("Tracker", minZ, maxZ)); + preCuts.emplace_back(new reco::Apothem(apothem)); + preCuts.emplace_back(new reco::MaxMuonAngle(20.)); + preCuts.emplace_back(new reco::HasMINOSMatch()); + preCuts.emplace_back( + new reco::NoDeadtime(1, "Deadtime")); + preCuts.emplace_back(new reco::IsNeutrino()); + preCuts.emplace_back(new Q3RangeReco(0.4, 0.6)); + // preCuts.emplace_back(new hasMichel()); + // preCuts.emplace_back(new BestMichelDistance2D(100.)); preCuts.emplace_back(new VtxMatchFirst(200., 102.)); + preCuts.emplace_back(new NPiCut(1)); + preCuts.emplace_back(new hasMichel()); + preCuts.emplace_back(new BestMichelDistance2D(150.)); + + //============================================================================ + // Signal Definition + //============================================================================ + signalDefinition.emplace_back(new truth::IsNeutrino()); + signalDefinition.emplace_back(new truth::IsCC()); + signalDefinition.emplace_back(new Q3Limit(0.4, 0.6)); + // signalDefinition.emplace_back(new hasTruePion()); + phaseSpace.emplace_back(new truth::ZRange("Tracker", minZ, maxZ)); + phaseSpace.emplace_back(new truth::Apothem(apothem)); + phaseSpace.emplace_back(new truth::MuonAngle(20.)); + phaseSpace.emplace_back(new truth::PZMuMin(1500.)); + + PlotUtils::Cutter mycuts(std::move(preCuts), std::move(sidebands), std::move(signalDefinition), std::move(phaseSpace)); + + //============================================================================ + // Weights + //============================================================================ + std::vector>> MnvTunev1; + MnvTunev1.emplace_back( + new PlotUtils::FluxAndCVReweighter()); + MnvTunev1.emplace_back( + new PlotUtils::GENIEReweighter(true, false)); + MnvTunev1.emplace_back( + new PlotUtils::LowRecoil2p2hReweighter()); + MnvTunev1.emplace_back( + new PlotUtils::MINOSEfficiencyReweighter()); + MnvTunev1.emplace_back( + new PlotUtils::RPAReweighter()); + + // TODO: Add my pion reweighter here. - Mehreen S. Nov 22, 2021 + PlotUtils::Model model(std::move(MnvTunev1)); + + //============================================================================ + // set up systematics + //============================================================================ + const bool doSystematics = (getenv("MNV101_SKIP_SYST") == nullptr); + if (!doSystematics) { + std::cout << "Skipping systematics (except 1 flux universe) because " + "environment variable MNV101_SKIP_SYST is set.\n"; + // Necessary to get Flux integral later... Doesn't work with just + // 1 flux universe though because _that_ triggers "spread errors". + PlotUtils::MinervaUniverse::SetNFluxUniverses(2); + } - // Loop entries and fill - try - { - CVUniverse::SetTruth(false); - LoopAndFillEventSelection(options.m_mc, error_bands, vars, vars2D, studies, mycuts, model); - CVUniverse::SetTruth(true); - LoopAndFillEffDenom(options.m_truth, truth_bands, vars, vars2D,studies, mycuts, model); - options.PrintMacroConfiguration(argv[0]); - std::cout << "MC cut summary:\n" << mycuts << "\n"; - mycuts.resetStats(); - - //CVUniverse::SetTruth(false); - //LoopAndFillData(options.m_data, data_band, vars, vars2D, data_studies, mycuts); - //std::cout << "Data cut summary:\n" << mycuts << "\n"; - - //Write MC results - TFile* mcOutDir = TFile::Open(MC_OUT_FILE_NAME, "RECREATE"); - if(!mcOutDir) - { - std::cerr << "Failed to open a file named " << MC_OUT_FILE_NAME << " in the current directory for writing histograms.\n"; - return badOutputFile; + std::map> error_bands; + if (doSystematics) { + error_bands = GetStandardSystematics(options.m_mc); } - - //TFile* mc_MichelStudies = TFile::Open("AllMichel_hasFittedMichel_500mm.root", "RECREATE"); - //"ALL2DDistprinted_OnlyPionMichels_tpimorethan80meV_forceendpointmatch_2Ddistcut_mc.root", "RECREATE"); - for(auto& study: studies) study->SaveOrDraw(*mc_MichelStudies); - for(auto& var: vars) var->WriteMC(*mcOutDir); - for(auto& var: vars2D) var->Write(*mcOutDir); - for(auto& study: data_studies) study->SaveOrDraw(*data_MichelStudies); - //Protons On Target - auto mcPOT = new TParameter("POTUsed", options.m_mc_pot); - mcPOT->Write(); - mc_MichelStudies->cd(); - mcPOT->Write(); - - PlotUtils::TargetUtils targetInfo; - assert(error_bands["cv"].size() == 1 && "List of error bands must contain a universe named \"cv\" for the flux integral."); - - for(const auto& var: vars) - { - //Flux integral only if systematics are being done (temporary solution) - util::GetFluxIntegral(*error_bands["cv"].front(), var->efficiencyNumerator->hist)->Write((var->GetName() + "_reweightedflux_integrated").c_str()); - //Always use MC number of nucleons for cross section - auto nNucleons = new TParameter((var->GetName() + "_fiducial_nucleons").c_str(), targetInfo.GetTrackerNNucleons(minZ, maxZ, true, apothem)); - nNucleons->Write(); + else { + std::map> band_flux = + PlotUtils::GetFluxSystematicsMap( + options.m_mc, CVUniverse::GetNFluxUniverses()); + error_bands.insert( + band_flux.begin(), + band_flux.end()); // Necessary to get flux integral later... } - - //Write data results - TFile* dataOutDir = TFile::Open(DATA_OUT_FILE_NAME, "RECREATE"); - if(!dataOutDir) - { - std::cerr << "Failed to open a file named " << DATA_OUT_FILE_NAME << " in the current directory for writing histograms.\n"; - return badOutputFile; + error_bands["cv"] = {new CVUniverse(options.m_mc)}; + std::map> truth_bands; + if (doSystematics) truth_bands = GetStandardSystematics(options.m_truth); + truth_bands["cv"] = {new CVUniverse(options.m_truth)}; + + //============================================================================ + // Set Up a Data Universe + //============================================================================ + CVUniverse* data_universe = new CVUniverse(options.m_data); + std::vector data_band = {data_universe}; + std::map> data_error_bands; + data_error_bands["cv"] = data_band; + + //============================================================================ + // Variables and binning + //============================================================================ + std::vector dansPTBins = {0, 0.075, 0.15, 0.25, 0.325, 0.4, 0.475, + 0.55, 0.7, 0.85, 1, 1.25, 1.5}, + dansPzBins = {1.5, 2, 2.5, 3, 3.5, 4, 4.5, 5, 6, + 7, 8, 9, 10, 15, 20, 40, 60}, + robsEmuBins = {0, 1, 2, 3, 4, 5, 7, 9, + 12, 15, 18, 22, 36, 50, 75, 80}, + robsRecoilBins; + + std::vector tpibins = {0, 4., 8., 12., 16., 20., 24., + 28., 32., 36., 40., 46., 52., 70., + 80., 100., 150., 200., 250., 300., 350., + 400., 500., 1000., 2000.}; + + const double robsRecoilBinWidth = 50; // MeV + for (int whichBin = 0; whichBin < 100 + 1; ++whichBin) + robsRecoilBins.push_back(robsRecoilBinWidth * whichBin); + + std::vector vars = { + new Variable("pTmu", "p_{T, #mu} [GeV/c]", dansPTBins, + &CVUniverse::GetMuonPT, &CVUniverse::GetMuonPTTrue), + new Variable("q3", "q3 [GeV]", dansPTBins, &CVUniverse::Getq3, + &CVUniverse::GetTrueQ3)}; + + std::vector vars2D; + if (doCCQENuValidation) { + std::cerr << "Detected that tree name is CCQENu. Making validation " + "histograms.\n"; + vars.push_back(new Variable("pzmu", "p_{||, #mu} [GeV/c]", dansPzBins, + &CVUniverse::GetMuonPz, + &CVUniverse::GetMuonPzTrue)); + vars.push_back(new Variable("Emu", "E_{#mu} [GeV]", robsEmuBins, + &CVUniverse::GetEmuGeV, + &CVUniverse::GetElepTrueGeV)); + vars.push_back(new Variable( + "Erecoil", "E_{recoil}", robsRecoilBins, &CVUniverse::GetRecoilE, + &CVUniverse::GetTrueEAvail)); // TODO: q0 is not the same as recoil + // energy without a spline correction + + vars2D.push_back(new Variable2D(*vars[1], *vars[0])); } - for(auto& var: vars) var->WriteData(*dataOutDir); + //============================================================================ + // This is where your list of Studies go for PerMichel variables -> Accessed + // through MichelEvent + //============================================================================ + std::vector studies; + + VarConfig deltat_config{"deltat", "#mus", 30, 0., 9.}; + VarConfig pirange_config{"pirange", "mm", 100, 0.0, 2000.0}; + VarConfig tpi_config{"KE", "meV", 100, 0., 1000.}; + + // Variables that we want to plot + std::function + delta_t = [](const CVUniverse& univ, const MichelEvent& evt, + const int whichMichel) { + int evttype = evt.eventtype; + double micheltime = evt.m_nmichels[whichMichel].time; + double vtxtime = univ.GetVertex().t(); + double deltat = + (micheltime - + vtxtime / 1000.); // hopefully this is in microseconds (mus) + // if (evttype == 1) return deltat; + // else return -9999.; + return deltat; + }; + + std::function + permichel_range = [](const CVUniverse& univ, const MichelEvent& evt, + const int whichMichel) { + double micheldist = evt.m_nmichels[whichMichel].Best3Ddist; + // if (evt.eventtype == 1) return micheldist; + // else return -9999.; + return micheldist; + }; + std::function + pertruepimichel_range = [](const CVUniverse& univ, const MichelEvent& evt, + const int whichMichel) { + double micheldist = evt.m_nmichels[whichMichel].Best3Ddist; + if (evt.m_nmichels[whichMichel].true_parentpdg == 211) + return micheldist; + else + return -9999.; + }; + std::function + permichel_tpi = [](const CVUniverse& univ, const MichelEvent& evt, + const int whichMichel) { + return evt.m_nmichels[whichMichel].pionKE; + }; + + std::function + overlay_vtx_tpi = [](const CVUniverse& univ, const MichelEvent& evt, + const int whichMichel) { + double micheldist = evt.m_nmichels[whichMichel].Best3Ddist; + double KE = evt.m_nmichels[whichMichel].pionKE; + int matchtype = evt.m_nmichels[whichMichel].BestMatch; + int overlayfrac = evt.m_nmichels[whichMichel].overlay_fraction; + if (overlayfrac > .5 && (matchtype == 1 || matchtype == 2)) + return KE; + else + return -9999.; + }; + + std::function + overlay_clus_tpi = [](const CVUniverse& univ, const MichelEvent& evt, + const int whichMichel) { + double micheldist = evt.m_nmichels[whichMichel].Best3Ddist; + double KE = evt.m_nmichels[whichMichel].pionKE; + int matchtype = evt.m_nmichels[whichMichel].BestMatch; + int overlayfrac = evt.m_nmichels[whichMichel].overlay_fraction; + if (overlayfrac > .5 && (matchtype == 3 || matchtype == 4)) + return KE; + else + return -9999.; + }; + + std::function + truemichel_goodvtx_tpi = [](const CVUniverse& univ, + const MichelEvent& evt, + const int whichMichel) { + double micheldist = evt.m_nmichels[whichMichel].Best3Ddist; + int overlayfrac = evt.m_nmichels[whichMichel].overlay_fraction; + double KE = evt.m_nmichels[whichMichel].pionKE; + int matchtype = evt.m_nmichels[whichMichel].BestMatch; + int trueEnd = evt.m_nmichels[whichMichel].trueEndpoint; + int recoEnd = evt.m_nmichels[whichMichel].recoEndpoint; + if (overlayfrac < .5 && trueEnd == recoEnd && + (matchtype == 1 || matchtype == 2)) + return KE; + else + return -9999.; + }; + std::function + truemichel_goodclus_tpi = [](const CVUniverse& univ, + const MichelEvent& evt, + const int whichMichel) { + double micheldist = evt.m_nmichels[whichMichel].Best3Ddist; + int overlayfrac = evt.m_nmichels[whichMichel].overlay_fraction; + double KE = evt.m_nmichels[whichMichel].pionKE; + int matchtype = evt.m_nmichels[whichMichel].BestMatch; + int trueEnd = evt.m_nmichels[whichMichel].trueEndpoint; + int recoEnd = evt.m_nmichels[whichMichel].recoEndpoint; + if (overlayfrac < .5 && trueEnd == recoEnd && + (matchtype == 3 || matchtype == 4)) + return KE; + else + return -9999.; + }; + std::function + truemichel_badvtx_tpi = [](const CVUniverse& univ, const MichelEvent& evt, + const int whichMichel) { + double micheldist = evt.m_nmichels[whichMichel].Best3Ddist; + double KE = evt.m_nmichels[whichMichel].pionKE; + int overlayfrac = evt.m_nmichels[whichMichel].overlay_fraction; + int matchtype = evt.m_nmichels[whichMichel].BestMatch; + int trueEnd = evt.m_nmichels[whichMichel].trueEndpoint; + int recoEnd = evt.m_nmichels[whichMichel].recoEndpoint; + if (overlayfrac < .5 && trueEnd != recoEnd && + (matchtype == 1 || matchtype == 2)) { + // univ.PrintArachneLink(); + // std::cout << "Printing Michel Time for bad VTX match type " << + // evt.m_nmichels[whichMichel].time << std::endl; + return KE; + } else + return -9999.; + }; + + std::function + truemichel_badclus_tpi = [](const CVUniverse& univ, + const MichelEvent& evt, + const int whichMichel) { + double micheldist = evt.m_nmichels[whichMichel].Best3Ddist; + double KE = evt.m_nmichels[whichMichel].pionKE; + int overlayfrac = evt.m_nmichels[whichMichel].overlay_fraction; + int matchtype = evt.m_nmichels[whichMichel].BestMatch; + int trueEnd = evt.m_nmichels[whichMichel].trueEndpoint; + int recoEnd = evt.m_nmichels[whichMichel].recoEndpoint; + if (overlayfrac < .5 && trueEnd != recoEnd && + (matchtype == 3 || matchtype == 4)) { + // univ.PrintArachneLink(); + // std::cout << "Printing Michel Time for bad CLUS match type " << + // evt.m_nmichels[whichMichel].time << std::endl; + return KE; + } else + return -9999.; + }; + + std::function + overlay_vtx_range = [](const CVUniverse& univ, const MichelEvent& evt, + const int whichMichel) { + double micheldist = evt.m_nmichels[whichMichel].Best3Ddist; + + int matchtype = evt.m_nmichels[whichMichel].BestMatch; + int overlayfrac = evt.m_nmichels[whichMichel].overlay_fraction; + if (overlayfrac > .5 && (matchtype == 1 || matchtype == 2)) + return micheldist; + else + return -9999.; + }; + + std::function + overlay_clus_range = [](const CVUniverse& univ, const MichelEvent& evt, + const int whichMichel) { + double micheldist = evt.m_nmichels[whichMichel].Best3Ddist; + + int matchtype = evt.m_nmichels[whichMichel].BestMatch; + int overlayfrac = evt.m_nmichels[whichMichel].overlay_fraction; + if (overlayfrac > .5 && (matchtype == 3 || matchtype == 4)) + return micheldist; + else + return -9999.; + }; + + std::function + truemichel_goodvtx_range = [](const CVUniverse& univ, + const MichelEvent& evt, + const int whichMichel) { + double micheldist = evt.m_nmichels[whichMichel].Best3Ddist; + int overlayfrac = evt.m_nmichels[whichMichel].overlay_fraction; + + int matchtype = evt.m_nmichels[whichMichel].BestMatch; + int trueEnd = evt.m_nmichels[whichMichel].trueEndpoint; + int recoEnd = evt.m_nmichels[whichMichel].recoEndpoint; + if (overlayfrac < .5 && trueEnd == recoEnd && + (matchtype == 1 || matchtype == 2)) + return micheldist; + else + return -9999.; + }; + + std::function truemichel_goodclus_range + = [](const CVUniverse& univ, + const MichelEvent& evt, + const int whichMichel) { + double micheldist = evt.m_nmichels[whichMichel].Best3Ddist; + int overlayfrac = evt.m_nmichels[whichMichel].overlay_fraction; + int matchtype = evt.m_nmichels[whichMichel].BestMatch; + int trueEnd = evt.m_nmichels[whichMichel].trueEndpoint; + int recoEnd = evt.m_nmichels[whichMichel].recoEndpoint; + if (overlayfrac < .5 && trueEnd == recoEnd && + (matchtype == 3 || matchtype == 4)) + return micheldist; + else + return -9999.; + }; + + std::function + truemichel_badvtx_range = [](const CVUniverse& univ, + const MichelEvent& evt, + const int whichMichel) { + double micheldist = evt.m_nmichels[whichMichel].Best3Ddist; + int overlayfrac = evt.m_nmichels[whichMichel].overlay_fraction; + int matchtype = evt.m_nmichels[whichMichel].BestMatch; + int trueEnd = evt.m_nmichels[whichMichel].trueEndpoint; + int recoEnd = evt.m_nmichels[whichMichel].recoEndpoint; + if (overlayfrac < .5 && trueEnd != recoEnd && + (matchtype == 1 || matchtype == 2)) { + // univ.PrintArachneLink(); + // std::cout << "Printing Michel Time for bad VTX match type " << + //evt.m_nmichels[whichMichel].time << std::endl; + return micheldist; + } else + return -9999.; + }; + std::function + truemichel_badclus_range = [](const CVUniverse& univ, + const MichelEvent& evt, + const int whichMichel) { + double micheldist = evt.m_nmichels[whichMichel].Best3Ddist; + int overlayfrac = evt.m_nmichels[whichMichel].overlay_fraction; + int matchtype = evt.m_nmichels[whichMichel].BestMatch; + int trueEnd = evt.m_nmichels[whichMichel].trueEndpoint; + int recoEnd = evt.m_nmichels[whichMichel].recoEndpoint; + if (overlayfrac < .5 && trueEnd != recoEnd && + (matchtype == 3 || matchtype == 4)) { + // univ.PrintArachneLink(); + // std::cout << "Printing Michel Time for bad CLUS match type " + // << evt.m_nmichels[whichMichel].time << std::endl; + return micheldist; + } else + return -9999.; + }; + + std::function + michel_XZ = [](const CVUniverse& univ, const MichelEvent& evt, + const int whichMichel) { + double twoDdist = evt.m_nmichels[whichMichel].best_XZ; + return twoDdist; + }; + + std::function + michel_UZ = [](const CVUniverse& univ, const MichelEvent& evt, + const int whichMichel) { + double twoDdist = evt.m_nmichels[whichMichel].best_UZ; + return twoDdist; + }; + + std::function + michel_VZ = [](const CVUniverse& univ, const MichelEvent& evt, + const int whichMichel) { + double twoDdist = evt.m_nmichels[whichMichel].best_VZ; + return twoDdist; + }; + + std::function + michel_XZ_upvtx = [](const CVUniverse& univ, const MichelEvent& evt, + const int whichMichel) { + double twoDdist = evt.m_nmichels[whichMichel].up_to_vertex_XZ; + int trueend = evt.m_nmichels[whichMichel].trueEndpoint; + if (trueend == 1) + return twoDdist; + else + return 9999.; + }; + + std::function + michel_UZ_upvtx = [](const CVUniverse& univ, const MichelEvent& evt, + const int whichMichel) { + double twoDdist = evt.m_nmichels[whichMichel].up_to_vertex_UZ; + int trueend = evt.m_nmichels[whichMichel].trueEndpoint; + if (trueend == 1) + return twoDdist; + else + return 9999.; + }; + + std::function + michel_VZ_upvtx = [](const CVUniverse& univ, const MichelEvent& evt, + const int whichMichel) { + double twoDdist = evt.m_nmichels[whichMichel].up_to_vertex_VZ; + int trueend = evt.m_nmichels[whichMichel].trueEndpoint; + if (trueend == 1) + return twoDdist; + else + return 9999.; + }; + + std::function + michel_XZ_upclus = [](const CVUniverse& univ, const MichelEvent& evt, + const int whichMichel) { + double twoDdist = evt.m_nmichels[whichMichel].up_to_clus_XZ; + int trueend = evt.m_nmichels[whichMichel].trueEndpoint; + if (trueend == 1) + return twoDdist; + else + return 9999.; + }; + + std::function + michel_UZ_upclus = [](const CVUniverse& univ, const MichelEvent& evt, + const int whichMichel) { + double twoDdist = evt.m_nmichels[whichMichel].up_to_clus_UZ; + int trueend = evt.m_nmichels[whichMichel].trueEndpoint; + if (trueend == 1) + return twoDdist; + else + return 9999.; + }; + + std::function + michel_VZ_upclus = [](const CVUniverse& univ, const MichelEvent& evt, + const int whichMichel) { + double twoDdist = evt.m_nmichels[whichMichel].up_to_clus_VZ; + int trueend = evt.m_nmichels[whichMichel].trueEndpoint; + if (trueend == 1) + return twoDdist; + else + return 9999.; + }; + + std::function + michel_XZ_downclus = [](const CVUniverse& univ, const MichelEvent& evt, + const int whichMichel) { + double twoDdist = evt.m_nmichels[whichMichel].down_to_clus_XZ; + int trueend = evt.m_nmichels[whichMichel].trueEndpoint; + if (trueend == 2) + return twoDdist; + else + return 9999.; + }; + std::function + michel_UZ_downclus = [](const CVUniverse& univ, const MichelEvent& evt, + const int whichMichel) { + double twoDdist = evt.m_nmichels[whichMichel].down_to_clus_UZ; + int trueend = evt.m_nmichels[whichMichel].trueEndpoint; + if (trueend == 2) + return twoDdist; + else + return 9999.; + }; + std::function + michel_VZ_downclus = [](const CVUniverse& univ, const MichelEvent& evt, + const int whichMichel) { + double twoDdist = evt.m_nmichels[whichMichel].down_to_clus_VZ; + int trueend = evt.m_nmichels[whichMichel].trueEndpoint; + if (trueend == 2) + return twoDdist; + else + return 9999.; + }; + std::function + michel_XZ_downvtx = [](const CVUniverse& univ, const MichelEvent& evt, + const int whichMichel) { + double twoDdist = evt.m_nmichels[whichMichel].down_to_vertex_XZ; + int trueend = evt.m_nmichels[whichMichel].trueEndpoint; + if (trueend == 2) + return twoDdist; + else + return 9999.; + }; + + std::function + michel_UZ_downvtx = [](const CVUniverse& univ, const MichelEvent& evt, + const int whichMichel) { + double twoDdist = evt.m_nmichels[whichMichel].down_to_vertex_UZ; + int trueend = evt.m_nmichels[whichMichel].trueEndpoint; + if (trueend == 2) + return twoDdist; + else + return 9999.; + }; + + std::function + michel_VZ_downvtx = [](const CVUniverse& univ, const MichelEvent& evt, + const int whichMichel) { + double twoDdist = evt.m_nmichels[whichMichel].down_to_vertex_VZ; + int trueend = evt.m_nmichels[whichMichel].trueEndpoint; + if (trueend == 2) + return twoDdist; + else + return 9999.; + }; + + std::function + pion_angle = [](const CVUniverse& univ, const MichelEvent& evt, + const int whichMichel) { + double angle = evt.m_nmichels[whichMichel].best_angle; + return cos(angle); + }; + + std::function + pion_angle_range1 = [](const CVUniverse& univ, const MichelEvent& evt, + const int whichMichel) { + double angle = evt.m_nmichels[whichMichel].best_angle; + double micheldist = evt.m_nmichels[whichMichel].Best3Ddist; + if (micheldist <= 150.) + return cos(angle); + else + return 9999.; + }; + + std::function + pion_angle_range2 = [](const CVUniverse& univ, const MichelEvent& evt, + const int whichMichel) { + double angle = evt.m_nmichels[whichMichel].best_angle; + double micheldist = evt.m_nmichels[whichMichel].Best3Ddist; + if (micheldist > 150. && micheldist <= 250.) + return cos(angle); + else + return 9999.; + }; + + std::function + pion_angle_range3 = [](const CVUniverse& univ, const MichelEvent& evt, + const int whichMichel) { + double angle = evt.m_nmichels[whichMichel].best_angle; + double micheldist = evt.m_nmichels[whichMichel].Best3Ddist; + if (micheldist > 250. && micheldist <= 500.) + return cos(angle); + else + return 9999.; + }; + std::function + pion_angle_range4 = [](const CVUniverse& univ, const MichelEvent& evt, + const int whichMichel) { + double angle = evt.m_nmichels[whichMichel].best_angle; + double micheldist = evt.m_nmichels[whichMichel].Best3Ddist; + if (micheldist > 500.) + return cos(angle); + else + return 9999.; + }; + + std::function + true_angle = [](const CVUniverse& univ, const MichelEvent& evt, + const int whichMichel) { + double angle = evt.m_nmichels[whichMichel].true_angle; + return cos(angle); + }; + + std::function + true_angle_range1 = [](const CVUniverse& univ, const MichelEvent& evt, + const int whichMichel) { + double angle = evt.m_nmichels[whichMichel].true_angle; + double micheldist = evt.m_nmichels[whichMichel].Best3Ddist; + if (micheldist <= 150.) + return cos(angle); + else + return 9999.; + }; + + std::function + true_angle_range2 = [](const CVUniverse& univ, const MichelEvent& evt, + const int whichMichel) { + double angle = evt.m_nmichels[whichMichel].true_angle; + double micheldist = evt.m_nmichels[whichMichel].Best3Ddist; + if (micheldist > 150 && micheldist <= 250.) + return cos(angle); + else + return 9999.; + }; + std::function + true_angle_range3 = [](const CVUniverse& univ, const MichelEvent& evt, + const int whichMichel) { + double angle = evt.m_nmichels[whichMichel].true_angle; + double micheldist = evt.m_nmichels[whichMichel].Best3Ddist; + if (micheldist > 250 && micheldist <= 500.) + return cos(angle); + else + return 9999.; + }; + + std::function + true_angle_range4 = [](const CVUniverse& univ, const MichelEvent& evt, + const int whichMichel) { + double angle = evt.m_nmichels[whichMichel].true_angle; + double micheldist = evt.m_nmichels[whichMichel].Best3Ddist; + if (micheldist > 500) + return cos(angle); + else + return 9999.; + }; + std::function best_pionrange = + [](const CVUniverse& univ, const MichelEvent& evt) { + int bestidx = evt.m_idx; + if (bestidx < 0) + return -9999.; + else { + double dist = evt.m_nmichels[bestidx].Best3Ddist; + return dist; + } + }; + + std::function + best_pionrange_overlay_vtx = + [](const CVUniverse& univ, const MichelEvent& evt) { + int bestidx = evt.m_idx; + if (bestidx < 0) + return -9999.; + else { + double micheldist = evt.m_nmichels[bestidx].Best3Ddist; + int overlayfrac = evt.m_nmichels[bestidx].overlay_fraction; + int matchtype = evt.m_nmichels[bestidx].BestMatch; + int trueEnd = evt.m_nmichels[bestidx].trueEndpoint; + int recoEnd = evt.m_nmichels[bestidx].recoEndpoint; + if (overlayfrac > .5 && (matchtype == 1 || matchtype == 2)) + return micheldist; + else + return -9999.; + } + }; + + std::function + best_pionrange_overlay_clus = + [](const CVUniverse& univ, const MichelEvent& evt) { + int bestidx = evt.m_idx; + if (bestidx < 0) + return -9999.; + else { + double micheldist = evt.m_nmichels[bestidx].Best3Ddist; + int overlayfrac = evt.m_nmichels[bestidx].overlay_fraction; + int matchtype = evt.m_nmichels[bestidx].BestMatch; + int trueEnd = evt.m_nmichels[bestidx].trueEndpoint; + int recoEnd = evt.m_nmichels[bestidx].recoEndpoint; + if (overlayfrac > .5 && (matchtype == 3 || matchtype == 4)) + return micheldist; + else + return -9999.; + } + }; + + std::function + best_pionrange_truegood_vtx = + [](const CVUniverse& univ, const MichelEvent& evt) { + int bestidx = evt.m_idx; + if (bestidx < 0) + return -9999.; + else { + double micheldist = evt.m_nmichels[bestidx].Best3Ddist; + int overlayfrac = evt.m_nmichels[bestidx].overlay_fraction; + int matchtype = evt.m_nmichels[bestidx].BestMatch; + int trueEnd = evt.m_nmichels[bestidx].trueEndpoint; + int recoEnd = evt.m_nmichels[bestidx].recoEndpoint; + if (overlayfrac < .5 && trueEnd == recoEnd && + (matchtype == 1 || matchtype == 2)) + return micheldist; + else + return -9999.; + } + }; + + std::function + best_pionrange_truebad_vtx = + [](const CVUniverse& univ, const MichelEvent& evt) { + int bestidx = evt.m_idx; + if (bestidx < 0) + return -9999.; + else { + double micheldist = evt.m_nmichels[bestidx].Best3Ddist; + int overlayfrac = evt.m_nmichels[bestidx].overlay_fraction; + int matchtype = evt.m_nmichels[bestidx].BestMatch; + int trueEnd = evt.m_nmichels[bestidx].trueEndpoint; + int recoEnd = evt.m_nmichels[bestidx].recoEndpoint; + if (overlayfrac < .5 && trueEnd != recoEnd && + (matchtype == 1 || matchtype == 2)) + return micheldist; + else + return -9999.; + } + }; + + std::function + best_pionrange_truegood_clus = + [](const CVUniverse& univ, const MichelEvent& evt) { + int bestidx = evt.m_idx; + if (bestidx < 0) + return -9999.; + else { + double micheldist = evt.m_nmichels[bestidx].Best3Ddist; + int overlayfrac = evt.m_nmichels[bestidx].overlay_fraction; + int matchtype = evt.m_nmichels[bestidx].BestMatch; + int trueEnd = evt.m_nmichels[bestidx].trueEndpoint; + int recoEnd = evt.m_nmichels[bestidx].recoEndpoint; + if (overlayfrac < .5 && trueEnd == recoEnd && + (matchtype == 3 || matchtype == 4)) + return micheldist; + else + return -9999.; + } + }; + + std::function + best_pionrange_truebad_clus = + [](const CVUniverse& univ, const MichelEvent& evt) { + int bestidx = evt.m_idx; + if (bestidx < 0) + return -9999.; + else { + double micheldist = evt.m_nmichels[bestidx].Best3Ddist; + int overlayfrac = evt.m_nmichels[bestidx].overlay_fraction; + int matchtype = evt.m_nmichels[bestidx].BestMatch; + int trueEnd = evt.m_nmichels[bestidx].trueEndpoint; + int recoEnd = evt.m_nmichels[bestidx].recoEndpoint; + if (overlayfrac < .5 && trueEnd != recoEnd && + (matchtype == 3 || matchtype == 4)) + return micheldist; + else + return -9999.; + } + }; + + std::function lowesttpi = + [](const CVUniverse& univ, const MichelEvent& evt) { + double lowtpi = evt.lowTpi; + return lowtpi; + }; + + // Fill studies, use the above-defined functions + studies.push_back(new PerMichelVarByGENIELabel( + true_angle, "true_angle", "cos(#theta)", 21, -1.0, 1.0, error_bands)); + studies.push_back( + new PerMichelVarByGENIELabel(true_angle_range1, "true_angle_range1", + "cos(#theta)", 21, -1.0, 1., error_bands)); + studies.push_back( + new PerMichelVarByGENIELabel(true_angle_range2, "true_angle_range2", + "cos(#theta)", 21, -1.0, 1., error_bands)); + studies.push_back( + new PerMichelVarByGENIELabel(true_angle_range3, "true_angle_range3", + "cos(#theta)", 21, -1.0, 1., error_bands)); + studies.push_back( + new PerMichelVarByGENIELabel(true_angle_range4, "true_angle_range4", + "cos(#theta)", 21, -1.0, 1., error_bands)); + studies.push_back(new PerMichelVarByGENIELabel( + pion_angle, "pion_angle", "cos(#theta)", 21, -1.0, 1., error_bands)); + studies.push_back( + new PerMichelVarByGENIELabel(pion_angle_range1, "pion_angle_range1", + "cos(#theta)", 21, -1.0, 1., error_bands)); + studies.push_back( + new PerMichelVarByGENIELabel(pion_angle_range2, "pion_angle_range2", + "cos(#theta)", 21, -1.0, 1., error_bands)); + studies.push_back( + new PerMichelVarByGENIELabel(pion_angle_range3, "pion_angle_range3", + "cos(#theta)", 21, -1.0, 1., error_bands)); + studies.push_back( + new PerMichelVarByGENIELabel(pion_angle_range4, "pion_angle_range4", + "cos(#theta)", 21, -1.0, 1., error_bands)); + + studies.push_back(new PerMichelVarByGENIELabel(michel_XZ, "best_XZ", "mm", + 100, 0.0, 2000., error_bands)); + studies.push_back(new PerMichelVarByGENIELabel(michel_UZ, "best_UZ", "mm", + 100, 0.0, 2000., error_bands)); + studies.push_back(new PerMichelVarByGENIELabel(michel_VZ, "best_VZ", "mm", + 100, 0.0, 2000., error_bands)); + studies.push_back(new PerMichelVarByGENIELabel( + michel_XZ_upvtx, "upvtx_XZ", "mm", 100, 0.0, 2000., error_bands)); + studies.push_back(new PerMichelVarByGENIELabel( + michel_XZ_upclus, "upclus_XZ", "mm", 100, 0.0, 2000., error_bands)); + studies.push_back(new PerMichelVarByGENIELabel( + michel_XZ_downclus, "downclus_XZ", "mm", 100, 0.0, 2000., error_bands)); + studies.push_back(new PerMichelVarByGENIELabel( + michel_XZ_downvtx, "downvtx_XZ", "mm", 100, 0.0, 2000., error_bands)); + studies.push_back(new PerMichelVarByGENIELabel( + michel_UZ_upvtx, "upvtx_UZ", "mm", 100, 0.0, 2000., error_bands)); + studies.push_back(new PerMichelVarByGENIELabel( + michel_UZ_upclus, "upclus_UZ", "mm", 100, 0.0, 2000., error_bands)); + studies.push_back(new PerMichelVarByGENIELabel( + michel_UZ_downclus, "downclus_UZ", "mm", 100, 0.0, 2000., error_bands)); + studies.push_back(new PerMichelVarByGENIELabel( + michel_UZ_downvtx, "downvtx_UZ", "mm", 100, 0.0, 2000., error_bands)); + studies.push_back(new PerMichelVarByGENIELabel( + michel_VZ_upvtx, "upvtx_VZ", "mm", 100, 0.0, 2000., error_bands)); + studies.push_back(new PerMichelVarByGENIELabel( + michel_VZ_upclus, "upclus_VZ", "mm", 100, 0.0, 2000., error_bands)); + studies.push_back(new PerMichelVarByGENIELabel( + michel_VZ_downclus, "downclus_VZ", "mm", 100, 0.0, 2000., error_bands)); + studies.push_back(new PerMichelVarByGENIELabel( + michel_VZ_downvtx, "downvtx_VZ", "mm", 100, 0.0, 2000., error_bands)); + + studies.push_back(new PerMichelVarByGENIELabel( + delta_t, "michelmuon_deltat", "#mus", 30, 0.0, 9.0, error_bands)); + studies.push_back(new PerMichelVarByGENIELabel(permichel_range, + "permichel_pirange", "mm", 100, + 0.0, 2000.0, error_bands)); + studies.push_back(new PerMichelVarByGENIELabel(overlay_vtx_range, + "overlay_vtx_range", "mm", 100, + 0.0, 2000.0, error_bands)); + studies.push_back( + new PerMichelVarByGENIELabel(overlay_clus_range, "overlay_clus_range", + "mm", 100, 0.0, 2000.0, error_bands)); + studies.push_back(new PerMichelVarByGENIELabel( + truemichel_goodvtx_range, "truemichel_goodvtx_range", "mm", 100, 0.0, + 2000.0, error_bands)); + studies.push_back(new PerMichelVarByGENIELabel( + truemichel_goodclus_range, "truemichel_goodclus_range", "mm", 100, 0.0, + 2000.0, error_bands)); + studies.push_back(new PerMichelVarByGENIELabel( + truemichel_badvtx_range, "truemichel_badvtx_range", "mm", 100, 0.0, + 2000.0, error_bands)); + studies.push_back(new PerMichelVarByGENIELabel( + truemichel_badclus_range, "truemichel_badclus_range", "mm", 100, 0.0, + 2000.0, error_bands)); + studies.push_back( + new PerMichelVarByGENIELabel(permichel_tpi, "Per_Michel_PrimaryParentKE", + "meV", 100, 0, 1000., error_bands)); + studies.push_back(new PerMichelVarByGENIELabel( + pertruepimichel_range, "permichel_pirange_truepi", "mm", 100, 0.0, 2000.0, + error_bands)); + + // studies.push_back(new PerMichel2DVar(delta_t, permichel_range, + // deltat_config, pirange_config, error_bands)); + studies.push_back(new PerMichel2DVar( + permichel_tpi, permichel_range, tpi_config, pirange_config, error_bands)); + // studies.push_back(new PerMichel2DVar(overlay_vtx_tpi, overlay_vtx_range, + // tpi_config, pirange_config, error_bands)); studies.push_back(new + // PerMichel2DVar(overlay_clus_tpi, overlay_clus_range, tpi_config, + // pirange_config, error_bands)); studies.push_back(new + // PerMichel2DVar(truemichel_goodvtx_range, truemichel_goodvtx_tpi, + // tpi_config, pirange_config, error_bands)); studies.push_back(new + // PerMichel2DVar(truemichel_goodclus_range, truemichel_goodclus_tpi, + // tpi_config, pirange_config, error_bands)); studies.push_back(new + // PerMichel2DVar(truemichel_badvtx_range, truemichel_badvtx_tpi, tpi_config, + // pirange_config, error_bands)); studies.push_back(new + // PerMichel2DVar(truemichel_badclus_range, truemichel_badclus_tpi, + // tpi_config, pirange_config, error_bands)); + // Studies for Per Event Variables (Best Michel In Event) + + studies.push_back(new PerMichelEventVarByGENIELabel( + lowesttpi, "LowestKE_pion", "MeV", 100, 0, 1000., error_bands)); + + studies.push_back(new PerMichelEventVarByGENIELabel( + best_pionrange, "best_pionrange", "mm", 100, 0.0, 2000.0, error_bands)); + studies.push_back(new PerMichelEventVarByGENIELabel( + best_pionrange_overlay_clus, "best_pionrange_overlay_clus", "mm", 100, + 0.0, 2000.0, error_bands)); + studies.push_back(new PerMichelEventVarByGENIELabel( + best_pionrange_overlay_vtx, "best_pionrange_overlay_vtx ", "mm", 100, 0.0, + 2000.0, error_bands)); + studies.push_back(new PerMichelEventVarByGENIELabel( + best_pionrange_truegood_vtx, "best_pionrange_truegood_vtx", "mm", 100, + 0.0, 2000.0, error_bands)); + studies.push_back(new PerMichelEventVarByGENIELabel( + best_pionrange_truebad_vtx, "best_pionrange_truebad_vtx", "mm", 100, 0.0, + 2000.0, error_bands)); + studies.push_back(new PerMichelEventVarByGENIELabel( + best_pionrange_truegood_clus, "best_pionrange_truegood_clus", "mm", 100, + 0.0, 2000.0, error_bands)); + studies.push_back(new PerMichelEventVarByGENIELabel( + best_pionrange_truebad_clus, "best_pionrange_truebad_clus", "mm", 100, + 0.0, 2000.0, error_bands)); + + // Data studies + std::vector data_studies; + + data_studies.push_back( + new PerMichelEventVarByGENIELabel(best_pionrange, "best_pionrange", "mm", + 100, 0.0, 2000.0, data_error_bands)); + data_studies.push_back(new PerMichelVarByGENIELabel( + delta_t, "michelmuon_deltat", "#mus", 30, 0.0, 9.0, data_error_bands)); + data_studies.push_back( + new PerMichelVarByGENIELabel(permichel_range, "permichel_pirange", "mm", + 100, 0.0, 2000.0, data_error_bands)); + data_studies.push_back(new PerMichelVarByGENIELabel( + pion_angle, "pion_angle", "cos(#theta)", 21, -1.0, 1., data_error_bands)); + data_studies.push_back(new PerMichelVarByGENIELabel( + pion_angle_range1, "pion_angle_range1", "cos(#theta)", 21, -1.0, 1., + data_error_bands)); + data_studies.push_back(new PerMichelVarByGENIELabel( + pion_angle_range2, "pion_angle_range2", "cos(#theta)", 21, -1.0, 1., + data_error_bands)); + data_studies.push_back(new PerMichelVarByGENIELabel( + pion_angle_range3, "pion_angle_range3", "cos(#theta)", 21, -1.0, 1., + data_error_bands)); + data_studies.push_back(new PerMichelVarByGENIELabel( + pion_angle_range4, "pion_angle_range4", "cos(#theta)", 21, -1.0, 1., + data_error_bands)); + + //============================================================================ + // Initialize Hists + //============================================================================ + for (auto& var : vars) var->InitializeMCHists(error_bands, truth_bands); + for (auto& var : vars) var->InitializeDATAHists(data_band); + + for (auto& var : vars2D) var->InitializeMCHists(error_bands, truth_bands); + for (auto& var : vars2D) var->InitializeDATAHists(data_band); + + //============================================================================ + // Loop entries and fill + //============================================================================ + try { + CVUniverse::SetTruth(false); + LoopAndFillEventSelection(options.m_mc, error_bands, vars, vars2D, studies, + mycuts, model); + CVUniverse::SetTruth(true); + LoopAndFillEffDenom(options.m_truth, truth_bands, vars, vars2D, studies, + mycuts, model); + options.PrintMacroConfiguration(argv[0]); + std::cout << "MC cut summary:\n" << mycuts << "\n"; + mycuts.resetStats(); + + // CVUniverse::SetTruth(false); + // LoopAndFillData(options.m_data, data_band, vars, vars2D, data_studies, + // mycuts); std::cout << "Data cut summary:\n" << mycuts << "\n"; + + // Write MC results + TFile* mcOutDir = TFile::Open(MC_OUT_FILE_NAME, "RECREATE"); + if (!mcOutDir) { + std::cerr << "Failed to open a file named " << MC_OUT_FILE_NAME + << " in the current directory for writing histograms.\n"; + return badOutputFile; + } - //Protons On Target - auto dataPOT = new TParameter("POTUsed", options.m_data_pot); - dataPOT->Write(); - data_MichelStudies->cd(); - dataPOT->Write(); - std::cout << "Success" << std::endl; - } - catch(const ROOT::exception& e) - { - std::cerr << "Ending on a ROOT error message. No histograms will be produced.\n" - << "If the message talks about \"TNetXNGFile\", this could be a problem with dCache. The message is:\n" - << e.what() << "\n" << USAGE << "\n"; - return badFileRead; - } + // TFile* mc_MichelStudies = + // TFile::Open("AllMichel_hasFittedMichel_500mm.root", "RECREATE"); + //"ALL2DDistprinted_OnlyPionMichels_tpimorethan80meV_forceendpointmatch_2Ddistcut_mc.root", + //"RECREATE"); + for (auto& study : studies) study->SaveOrDraw(*mc_MichelStudies); + for (auto& var : vars) var->WriteMC(*mcOutDir); + for (auto& var : vars2D) var->Write(*mcOutDir); + for (auto& study : data_studies) study->SaveOrDraw(*data_MichelStudies); + // Protons On Target + auto mcPOT = new TParameter("POTUsed", options.m_mc_pot); + mcPOT->Write(); + mc_MichelStudies->cd(); + mcPOT->Write(); + + PlotUtils::TargetUtils targetInfo; + assert(error_bands["cv"].size() == 1 && + "List of error bands must contain a universe named \"cv\" for the " + "flux integral."); + + for (const auto& var : vars) { + // Flux integral only if systematics are being done (temporary solution) + util::GetFluxIntegral(*error_bands["cv"].front(), + var->efficiencyNumerator->hist) + ->Write((var->GetName() + "_reweightedflux_integrated").c_str()); + // Always use MC number of nucleons for cross section + auto nNucleons = new TParameter( + (var->GetName() + "_fiducial_nucleons").c_str(), + targetInfo.GetTrackerNNucleons(minZ, maxZ, true, apothem)); + nNucleons->Write(); + } + + // Write data results + TFile* dataOutDir = TFile::Open(DATA_OUT_FILE_NAME, "RECREATE"); + if (!dataOutDir) { + std::cerr << "Failed to open a file named " << DATA_OUT_FILE_NAME + << " in the current directory for writing histograms.\n"; + return badOutputFile; + } + + for (auto& var : vars) var->WriteData(*dataOutDir); + + // Protons On Target + auto dataPOT = new TParameter("POTUsed", options.m_data_pot); + dataPOT->Write(); + data_MichelStudies->cd(); + dataPOT->Write(); + std::cout << "Success" << std::endl; + } catch (const ROOT::exception& e) { + std::cerr + << "Ending on a ROOT error message. No histograms will be produced.\n" + << "If the message talks about \"TNetXNGFile\", this could be a " + "problem with dCache. The message is:\n" + << e.what() << "\n" + << USAGE << "\n"; + return badFileRead; + } return success; } From 1c178cd06c6c56227dad01280f6e0b3b71675682 Mon Sep 17 00:00:00 2001 From: Ben Messerly Date: Wed, 5 Jan 2022 17:42:48 -0600 Subject: [PATCH 05/17] Initial commit of a scratch file containing all the variables that Mehreen plots. --- .../MehreenMichels/MehreenMichelFunctions.h | 487 ++++++++++++++++++ 1 file changed, 487 insertions(+) create mode 100644 includes/MehreenMichels/MehreenMichelFunctions.h diff --git a/includes/MehreenMichels/MehreenMichelFunctions.h b/includes/MehreenMichels/MehreenMichelFunctions.h new file mode 100644 index 0000000..59bea57 --- /dev/null +++ b/includes/MehreenMichels/MehreenMichelFunctions.h @@ -0,0 +1,487 @@ + +std::function + delta_t = [](const CVUniverse& univ, const MichelEvent& evt, + const int whichMichel) { + int evttype = evt.eventtype; + double micheltime = evt.m_nmichels[whichMichel].time; + double vtxtime = univ.GetVertex().t(); + double deltat = + (micheltime - + vtxtime / 1000.); // hopefully this is in microseconds (mus) + // if (evttype == 1) return deltat; + // else return -9999.; + return deltat; + }; + +std::function + permichel_range = [](const CVUniverse& univ, const MichelEvent& evt, + const int whichMichel) { + double micheldist = evt.m_nmichels[whichMichel].Best3Ddist; + // if (evt.eventtype == 1) return micheldist; + // else return -9999.; + return micheldist; + }; + +std::function + pertruepimichel_range = [](const CVUniverse& univ, const MichelEvent& evt, + const int whichMichel) { + double micheldist = evt.m_nmichels[whichMichel].Best3Ddist; + if (evt.m_nmichels[whichMichel].true_parentpdg == 211) + return micheldist; + else + return -9999.; + }; + +std::function + permichel_tpi = [](const CVUniverse& univ, const MichelEvent& evt, + const int whichMichel) { + return evt.m_nmichels[whichMichel].pionKE; + }; + +std::function + overlay_vtx_tpi = [](const CVUniverse& univ, const MichelEvent& evt, + const int whichMichel) { + double micheldist = evt.m_nmichels[whichMichel].Best3Ddist; + double KE = evt.m_nmichels[whichMichel].pionKE; + int matchtype = evt.m_nmichels[whichMichel].BestMatch; + int overlayfrac = evt.m_nmichels[whichMichel].overlay_fraction; + if (overlayfrac > .5 && (matchtype == 1 || matchtype == 2)) + return KE; + else + return -9999.; + }; + +std::function + overlay_clus_tpi = [](const CVUniverse& univ, const MichelEvent& evt, + const int whichMichel) { + double micheldist = evt.m_nmichels[whichMichel].Best3Ddist; + double KE = evt.m_nmichels[whichMichel].pionKE; + int matchtype = evt.m_nmichels[whichMichel].BestMatch; + int overlayfrac = evt.m_nmichels[whichMichel].overlay_fraction; + if (overlayfrac > .5 && (matchtype == 3 || matchtype == 4)) + return KE; + else + return -9999.; + }; + +std::function + truemichel_goodvtx_tpi = [](const CVUniverse& univ, + const MichelEvent& evt, + const int whichMichel) { + double micheldist = evt.m_nmichels[whichMichel].Best3Ddist; + int overlayfrac = evt.m_nmichels[whichMichel].overlay_fraction; + double KE = evt.m_nmichels[whichMichel].pionKE; + int matchtype = evt.m_nmichels[whichMichel].BestMatch; + int trueEnd = evt.m_nmichels[whichMichel].trueEndpoint; + int recoEnd = evt.m_nmichels[whichMichel].recoEndpoint; + if (overlayfrac < .5 && trueEnd == recoEnd && + (matchtype == 1 || matchtype == 2)) + return KE; + else + return -9999.; + }; + +std::function + truemichel_goodclus_tpi = [](const CVUniverse& univ, + const MichelEvent& evt, + const int whichMichel) { + double micheldist = evt.m_nmichels[whichMichel].Best3Ddist; + int overlayfrac = evt.m_nmichels[whichMichel].overlay_fraction; + double KE = evt.m_nmichels[whichMichel].pionKE; + int matchtype = evt.m_nmichels[whichMichel].BestMatch; + int trueEnd = evt.m_nmichels[whichMichel].trueEndpoint; + int recoEnd = evt.m_nmichels[whichMichel].recoEndpoint; + if (overlayfrac < .5 && trueEnd == recoEnd && + (matchtype == 3 || matchtype == 4)) + return KE; + else + return -9999.; + }; + +std::function + truemichel_badvtx_tpi = [](const CVUniverse& univ, const MichelEvent& evt, + const int whichMichel) { + double micheldist = evt.m_nmichels[whichMichel].Best3Ddist; + double KE = evt.m_nmichels[whichMichel].pionKE; + int overlayfrac = evt.m_nmichels[whichMichel].overlay_fraction; + int matchtype = evt.m_nmichels[whichMichel].BestMatch; + int trueEnd = evt.m_nmichels[whichMichel].trueEndpoint; + int recoEnd = evt.m_nmichels[whichMichel].recoEndpoint; + if (overlayfrac < .5 && trueEnd != recoEnd && + (matchtype == 1 || matchtype == 2)) { + // univ.PrintArachneLink(); + // std::cout << "Printing Michel Time for bad VTX match type " << + // evt.m_nmichels[whichMichel].time << std::endl; + return KE; + } else + return -9999.; + }; + +std::function + truemichel_badclus_tpi = [](const CVUniverse& univ, + const MichelEvent& evt, + const int whichMichel) { + double micheldist = evt.m_nmichels[whichMichel].Best3Ddist; + double KE = evt.m_nmichels[whichMichel].pionKE; + int overlayfrac = evt.m_nmichels[whichMichel].overlay_fraction; + int matchtype = evt.m_nmichels[whichMichel].BestMatch; + int trueEnd = evt.m_nmichels[whichMichel].trueEndpoint; + int recoEnd = evt.m_nmichels[whichMichel].recoEndpoint; + if (overlayfrac < .5 && trueEnd != recoEnd && + (matchtype == 3 || matchtype == 4)) { + // univ.PrintArachneLink(); + // std::cout << "Printing Michel Time for bad CLUS match type " << + // evt.m_nmichels[whichMichel].time << std::endl; + return KE; + } else + return -9999.; + }; + +std::function + overlay_vtx_range = [](const CVUniverse& univ, const MichelEvent& evt, + const int whichMichel) { + double micheldist = evt.m_nmichels[whichMichel].Best3Ddist; + + int matchtype = evt.m_nmichels[whichMichel].BestMatch; + int overlayfrac = evt.m_nmichels[whichMichel].overlay_fraction; + if (overlayfrac > .5 && (matchtype == 1 || matchtype == 2)) + return micheldist; + else + return -9999.; + }; + +std::function + overlay_clus_range = [](const CVUniverse& univ, const MichelEvent& evt, + const int whichMichel) { + double micheldist = evt.m_nmichels[whichMichel].Best3Ddist; + + int matchtype = evt.m_nmichels[whichMichel].BestMatch; + int overlayfrac = evt.m_nmichels[whichMichel].overlay_fraction; + if (overlayfrac > .5 && (matchtype == 3 || matchtype == 4)) + return micheldist; + else + return -9999.; + }; + +std::function + truemichel_goodvtx_range = [](const CVUniverse& univ, + const MichelEvent& evt, + const int whichMichel) { + double micheldist = evt.m_nmichels[whichMichel].Best3Ddist; + int overlayfrac = evt.m_nmichels[whichMichel].overlay_fraction; + + int matchtype = evt.m_nmichels[whichMichel].BestMatch; + int trueEnd = evt.m_nmichels[whichMichel].trueEndpoint; + int recoEnd = evt.m_nmichels[whichMichel].recoEndpoint; + if (overlayfrac < .5 && trueEnd == recoEnd && + (matchtype == 1 || matchtype == 2)) + return micheldist; + else + return -9999.; + }; + +std::function truemichel_goodclus_range + = [](const CVUniverse& univ, + const MichelEvent& evt, + const int whichMichel) { + double micheldist = evt.m_nmichels[whichMichel].Best3Ddist; + int overlayfrac = evt.m_nmichels[whichMichel].overlay_fraction; + int matchtype = evt.m_nmichels[whichMichel].BestMatch; + int trueEnd = evt.m_nmichels[whichMichel].trueEndpoint; + int recoEnd = evt.m_nmichels[whichMichel].recoEndpoint; + if (overlayfrac < .5 && trueEnd == recoEnd && + (matchtype == 3 || matchtype == 4)) + return micheldist; + else + return -9999.; + }; + +std::function + truemichel_badvtx_range = [](const CVUniverse& univ, + const MichelEvent& evt, + const int whichMichel) { + double micheldist = evt.m_nmichels[whichMichel].Best3Ddist; + int overlayfrac = evt.m_nmichels[whichMichel].overlay_fraction; + int matchtype = evt.m_nmichels[whichMichel].BestMatch; + int trueEnd = evt.m_nmichels[whichMichel].trueEndpoint; + int recoEnd = evt.m_nmichels[whichMichel].recoEndpoint; + if (overlayfrac < .5 && trueEnd != recoEnd && + (matchtype == 1 || matchtype == 2)) { + // univ.PrintArachneLink(); + // std::cout << "Printing Michel Time for bad VTX match type " << + //evt.m_nmichels[whichMichel].time << std::endl; + return micheldist; + } else + return -9999.; + }; +std::function + truemichel_badclus_range = [](const CVUniverse& univ, + const MichelEvent& evt, + const int whichMichel) { + double micheldist = evt.m_nmichels[whichMichel].Best3Ddist; + int overlayfrac = evt.m_nmichels[whichMichel].overlay_fraction; + int matchtype = evt.m_nmichels[whichMichel].BestMatch; + int trueEnd = evt.m_nmichels[whichMichel].trueEndpoint; + int recoEnd = evt.m_nmichels[whichMichel].recoEndpoint; + if (overlayfrac < .5 && trueEnd != recoEnd && + (matchtype == 3 || matchtype == 4)) { + // univ.PrintArachneLink(); + // std::cout << "Printing Michel Time for bad CLUS match type " + // << evt.m_nmichels[whichMichel].time << std::endl; + return micheldist; + } else + return -9999.; + }; + +std::function + michel_XZ = [](const CVUniverse& univ, const MichelEvent& evt, + const int whichMichel) { + double twoDdist = evt.m_nmichels[whichMichel].best_XZ; + return twoDdist; + }; + +std::function + michel_UZ = [](const CVUniverse& univ, const MichelEvent& evt, + const int whichMichel) { + double twoDdist = evt.m_nmichels[whichMichel].best_UZ; + return twoDdist; + }; + +std::function + michel_VZ = [](const CVUniverse& univ, const MichelEvent& evt, + const int whichMichel) { + double twoDdist = evt.m_nmichels[whichMichel].best_VZ; + return twoDdist; + }; + +std::function + michel_XZ_upvtx = [](const CVUniverse& univ, const MichelEvent& evt, + const int whichMichel) { + double twoDdist = evt.m_nmichels[whichMichel].up_to_vertex_XZ; + int trueend = evt.m_nmichels[whichMichel].trueEndpoint; + if (trueend == 1) + return twoDdist; + else + return 9999.; + }; + +std::function + michel_UZ_upvtx = [](const CVUniverse& univ, const MichelEvent& evt, + const int whichMichel) { + double twoDdist = evt.m_nmichels[whichMichel].up_to_vertex_UZ; + int trueend = evt.m_nmichels[whichMichel].trueEndpoint; + if (trueend == 1) + return twoDdist; + else + return 9999.; + }; + +std::function + michel_VZ_upvtx = [](const CVUniverse& univ, const MichelEvent& evt, + const int whichMichel) { + double twoDdist = evt.m_nmichels[whichMichel].up_to_vertex_VZ; + int trueend = evt.m_nmichels[whichMichel].trueEndpoint; + if (trueend == 1) + return twoDdist; + else + return 9999.; + }; + +std::function + michel_XZ_upclus = [](const CVUniverse& univ, const MichelEvent& evt, + const int whichMichel) { + double twoDdist = evt.m_nmichels[whichMichel].up_to_clus_XZ; + int trueend = evt.m_nmichels[whichMichel].trueEndpoint; + if (trueend == 1) + return twoDdist; + else + return 9999.; + }; + +std::function + michel_UZ_upclus = [](const CVUniverse& univ, const MichelEvent& evt, + const int whichMichel) { + double twoDdist = evt.m_nmichels[whichMichel].up_to_clus_UZ; + int trueend = evt.m_nmichels[whichMichel].trueEndpoint; + if (trueend == 1) + return twoDdist; + else + return 9999.; + }; + +std::function + michel_VZ_upclus = [](const CVUniverse& univ, const MichelEvent& evt, + const int whichMichel) { + double twoDdist = evt.m_nmichels[whichMichel].up_to_clus_VZ; + int trueend = evt.m_nmichels[whichMichel].trueEndpoint; + if (trueend == 1) + return twoDdist; + else + return 9999.; + }; + +std::function + michel_XZ_downclus = [](const CVUniverse& univ, const MichelEvent& evt, + const int whichMichel) { + double twoDdist = evt.m_nmichels[whichMichel].down_to_clus_XZ; + int trueend = evt.m_nmichels[whichMichel].trueEndpoint; + if (trueend == 2) + return twoDdist; + else + return 9999.; + }; + +std::function + michel_UZ_downclus = [](const CVUniverse& univ, const MichelEvent& evt, + const int whichMichel) { + double twoDdist = evt.m_nmichels[whichMichel].down_to_clus_UZ; + int trueend = evt.m_nmichels[whichMichel].trueEndpoint; + if (trueend == 2) + return twoDdist; + else + return 9999.; + }; + +std::function + michel_VZ_downclus = [](const CVUniverse& univ, const MichelEvent& evt, + const int whichMichel) { + double twoDdist = evt.m_nmichels[whichMichel].down_to_clus_VZ; + int trueend = evt.m_nmichels[whichMichel].trueEndpoint; + if (trueend == 2) + return twoDdist; + else + return 9999.; + }; + +std::function + michel_XZ_downvtx = [](const CVUniverse& univ, const MichelEvent& evt, + const int whichMichel) { + double twoDdist = evt.m_nmichels[whichMichel].down_to_vertex_XZ; + int trueend = evt.m_nmichels[whichMichel].trueEndpoint; + if (trueend == 2) + return twoDdist; + else + return 9999.; + }; + +std::function + michel_UZ_downvtx = [](const CVUniverse& univ, const MichelEvent& evt, + const int whichMichel) { + double twoDdist = evt.m_nmichels[whichMichel].down_to_vertex_UZ; + int trueend = evt.m_nmichels[whichMichel].trueEndpoint; + if (trueend == 2) + return twoDdist; + else + return 9999.; + }; + +std::function + michel_VZ_downvtx = [](const CVUniverse& univ, const MichelEvent& evt, + const int whichMichel) { + double twoDdist = evt.m_nmichels[whichMichel].down_to_vertex_VZ; + int trueend = evt.m_nmichels[whichMichel].trueEndpoint; + if (trueend == 2) + return twoDdist; + else + return 9999.; + }; + +std::function + pion_angle = [](const CVUniverse& univ, const MichelEvent& evt, + const int whichMichel) { + double angle = evt.m_nmichels[whichMichel].best_angle; + return cos(angle); + }; + +std::function + pion_angle_range1 = [](const CVUniverse& univ, const MichelEvent& evt, + const int whichMichel) { + double angle = evt.m_nmichels[whichMichel].best_angle; + double micheldist = evt.m_nmichels[whichMichel].Best3Ddist; + if (micheldist <= 150.) + return cos(angle); + else + return 9999.; + }; + +std::function + pion_angle_range2 = [](const CVUniverse& univ, const MichelEvent& evt, + const int whichMichel) { + double angle = evt.m_nmichels[whichMichel].best_angle; + double micheldist = evt.m_nmichels[whichMichel].Best3Ddist; + if (micheldist > 150. && micheldist <= 250.) + return cos(angle); + else + return 9999.; + }; + +std::function + pion_angle_range3 = [](const CVUniverse& univ, const MichelEvent& evt, + const int whichMichel) { + double angle = evt.m_nmichels[whichMichel].best_angle; + double micheldist = evt.m_nmichels[whichMichel].Best3Ddist; + if (micheldist > 250. && micheldist <= 500.) + return cos(angle); + else + return 9999.; + }; +std::function + pion_angle_range4 = [](const CVUniverse& univ, const MichelEvent& evt, + const int whichMichel) { + double angle = evt.m_nmichels[whichMichel].best_angle; + double micheldist = evt.m_nmichels[whichMichel].Best3Ddist; + if (micheldist > 500.) + return cos(angle); + else + return 9999.; + }; + +std::function + true_angle = [](const CVUniverse& univ, const MichelEvent& evt, + const int whichMichel) { + double angle = evt.m_nmichels[whichMichel].true_angle; + return cos(angle); + }; + +std::function + true_angle_range1 = [](const CVUniverse& univ, const MichelEvent& evt, + const int whichMichel) { + double angle = evt.m_nmichels[whichMichel].true_angle; + double micheldist = evt.m_nmichels[whichMichel].Best3Ddist; + if (micheldist <= 150.) + return cos(angle); + else + return 9999.; + }; + +std::function + true_angle_range2 = [](const CVUniverse& univ, const MichelEvent& evt, + const int whichMichel) { + double angle = evt.m_nmichels[whichMichel].true_angle; + double micheldist = evt.m_nmichels[whichMichel].Best3Ddist; + if (micheldist > 150 && micheldist <= 250.) + return cos(angle); + else + return 9999.; + }; +std::function + true_angle_range3 = [](const CVUniverse& univ, const MichelEvent& evt, + const int whichMichel) { + double angle = evt.m_nmichels[whichMichel].true_angle; + double micheldist = evt.m_nmichels[whichMichel].Best3Ddist; + if (micheldist > 250 && micheldist <= 500.) + return cos(angle); + else + return 9999.; + }; + +std::function + true_angle_range4 = [](const CVUniverse& univ, const MichelEvent& evt, + const int whichMichel) { + double angle = evt.m_nmichels[whichMichel].true_angle; + double micheldist = evt.m_nmichels[whichMichel].Best3Ddist; + if (micheldist > 500) + return cos(angle); + else + return 9999.; + }; From 25313383f3c07b9ff1e73c74fb0a4038a1fc73b7 Mon Sep 17 00:00:00 2001 From: Ben Messerly Date: Wed, 12 Jan 2022 21:25:08 -0600 Subject: [PATCH 06/17] Consolidate all the michel stuff into the michel header. Remove some of it from Cuts. Mehreen's vertex michels need Cluster. --- includes/Cluster.h | 35 + includes/Cuts.cxx | 138 +--- includes/Cuts.h | 6 +- includes/Michel.h | 1528 +++++++++++++++++++++++++++++++++++++++++--- 4 files changed, 1492 insertions(+), 215 deletions(-) create mode 100644 includes/Cluster.h diff --git a/includes/Cluster.h b/includes/Cluster.h new file mode 100644 index 0000000..99245dc --- /dev/null +++ b/includes/Cluster.h @@ -0,0 +1,35 @@ +#ifndef CLUSTER_H +#define CLUSTER_H + +// A cluster object will simply be the ith cluster in the event. + +// using ClusterMap = std::map; + +struct Cluster { + // constructors + Cluster(const CVUniverse &univ, int &ci); + + int cluster_idx; + // Does this michel satisfy our quality + bool is_quality; + + double energy; + + double time; + + int view; // 1 = x, 2 = u, 3 = v + + double zpos; // z position + + double pos; +}; + +Cluster::Cluster(const CVUniverse &univ, int &ci) { + energy = univ.GetVecElem("FittedMichel_cluster_energy", ci); // MeV + time = univ.GetVecElem("FittedMichel_cluster_time", ci) / pow(10, 3); // microseconds + pos = univ.GetVecElem("FittedMichel_cluster_pos", ci); // in mm + zpos = univ.GetVecElem("FittedMichel_cluster_z", ci); // in mm + view = univ.GetVecElem("FittedMichel_cluster_view", ci); // 1 = X view, 2 = U view, 3 = V view +} + +#endif // CLUSTER_H diff --git a/includes/Cuts.cxx b/includes/Cuts.cxx index 0ab428e..7abb88c 100644 --- a/includes/Cuts.cxx +++ b/includes/Cuts.cxx @@ -9,42 +9,8 @@ //============================================================================== // Helpers //============================================================================== -// -- Given a single michel cluster matched to two vertices -// return vertex with the better-matched michel. -Michel CompareMichels(Michel r, Michel c) { - if (r.match_category > c.match_category) - return r; - else if (r.match_category < c.match_category) - return c; - else { - if (r.fit_distance < c.fit_distance) - return r; - else if (r.fit_distance > c.fit_distance) - return c; - else { - // This must mean we're comparing the same michels with the same fits. - // std::cout << "WEIRD COMPAREMICHELS PROBLEM" << std::endl; - return r; - } - } -} - -// Add michel to MichelMap. Check if this cluster has already been matched. -// Then use only the best match. -bool AddOrReplaceMichel(MichelMap& mm, Michel m) { - std::pair SC; - if (mm.count(m.idx) == 0) - SC = mm.insert(pair(m.idx, m)); - else { - Michel reigning_michel = mm.at(m.idx); - Michel best_michel = CompareMichels(reigning_michel, m); - mm[m.idx] = best_michel; - } - return true; -} - // Get the pion candidate indexes from a map of michels -std::vector GetHadIdxsFromMichels(MichelMap michels) { +std::vector GetHadIdxsFromMichels(endpoint::MichelMap michels) { std::vector ret; for (auto m : michels) ret.push_back(m.second.had_idx); return ret; @@ -60,8 +26,8 @@ bool PassesCuts(CVUniverse& univ, std::vector& pion_candidate_idxs, bool is_mc, SignalDefinition signal_definition, std::vector cuts) { pion_candidate_idxs.clear(); - static MichelMap endpoint_michels; - static MichelMap + static endpoint::MichelMap endpoint_michels; + static endpoint::MichelMap vertex_michels; // Keep track of these, but not used currently endpoint_michels.clear(); vertex_michels.clear(); @@ -121,8 +87,8 @@ std::tuple> PassesCuts( //============================================================================ // passes all cuts but w cut //============================================================================ - MichelMap endpoint_michels; - MichelMap vertex_michels; + endpoint::MichelMap endpoint_michels; + endpoint::MichelMap vertex_michels; bool passes_all_but_w_cut = true; for (auto c : GetWSidebandCuts()) { // Set the pion candidates to the universe. The values set in early cuts @@ -171,8 +137,8 @@ EventCount PassedCuts(const CVUniverse& univ, SignalDefinition signal_definition, std::vector cuts) { pion_candidate_idxs.clear(); - static MichelMap endpoint_michels; - static MichelMap vertex_michels; + static endpoint::MichelMap endpoint_michels; + static endpoint::MichelMap vertex_michels; endpoint_michels.clear(); vertex_michels.clear(); EventCount Pass; @@ -196,7 +162,7 @@ EventCount PassedCuts(const CVUniverse& univ, // Updates the michel containers bool PassesCut(const CVUniverse& univ, const ECuts cut, const bool is_mc, const SignalDefinition signal_definition, - MichelMap& endpoint_michels, MichelMap& vertex_michels) { + endpoint::MichelMap& endpoint_michels, endpoint::MichelMap& vertex_michels) { const bool useOVMichels = false; if (IsPrecut(cut) && !is_mc) return true; @@ -264,14 +230,14 @@ bool PassesCut(const CVUniverse& univ, const ECuts cut, const bool is_mc, // This cut fills our michel containers, which we use to ID pion tracks // and subsequently make track cuts (LLR, node). case kAtLeastOneMichel: { - MichelMap all_michels = GetQualityMichels(univ); + endpoint::MichelMap all_michels = endpoint::GetQualityMichels(univ); for (auto m : all_michels) { if (m.second.had_idx == -1) vertex_michels.insert(m); else endpoint_michels.insert(m); } - // mehreen_michels = GetPassingMehreenMichels(); + vertex::MichelEvent mehreen_michels = vertex::GetQualityMichels(univ); return endpoint_michels.size() > 0 /*|| mehreen_michels.size() = 0*/; } @@ -293,7 +259,7 @@ bool PassesCut(const CVUniverse& univ, const ECuts cut, const bool is_mc, // If a michel's pion fails the LLR cut, remove it from the michels case kLLR: { ContainerEraser::erase_if(endpoint_michels, - [&univ](std::pair mm) { + [&univ](std::pair mm) { return !LLRCut(univ, mm.second.had_idx); }); return endpoint_michels.size() > 0; @@ -302,7 +268,7 @@ bool PassesCut(const CVUniverse& univ, const ECuts cut, const bool is_mc, // If a michel's pion fails the node cut, remove it from the michels case kNode: { ContainerEraser::erase_if(endpoint_michels, - [&univ](std::pair mm) { + [&univ](std::pair mm) { return !NodeCut(univ, mm.second.had_idx); }); return endpoint_michels.size() > 0; @@ -372,80 +338,6 @@ bool IsoProngCut(const CVUniverse& univ) { return univ.GetNIsoProngs() < CCNuPionIncConsts::kIsoProngCutVal; } -//============================================================================ -// Collect the good michels in this event -//============================================================================ -// Get quality michels map<(int michel cluster ID, Michel)> -// * A Michel is uniquely ID-ed by its cluster(of hits) integer index. -// * The michel tool has already vetted the clusters themselves. -// Whereas here we evaluate the quality of the fit. -// * At most one interaction vertex michel (which MUST be "fitted") -// * An OV michel will only be kept if no better michels are in the event. -// * In the case of 2+ OV michels, only keep the best one. -// * required: one-to-one michel<->vertex matching: -// * when one michel cluster is matched to multiple vertices, the vtx -// with the better match is chosen. -// * We don't have the problem in the other direction -- michel tool: a -// vertex cannot be matched to more than one cluster. -// * At the moment, we can return a single michel that is a quality -// interaction vertex michel. We'd like to call these signal, but we don't -// know the pion energy...yet. In the meantime, cut them. -MichelMap GetQualityMichels(const CVUniverse& univ) { - std::map ret_michels; - std::vector matched_michel_idxs = univ.GetVec("matched_michel_idx"); - - // Loop vertices in the event, i.e. the indices of the michel index vector - for (uint vtx = 0; vtx < matched_michel_idxs.size(); ++vtx) { - int mm_idx = matched_michel_idxs[vtx]; - - // NO MATCH -- GO TO NEXT VTX. No michel cluster matched to this vertex. - if (mm_idx < 0) continue; - - // MICHEL CONSTRUCTOR - // Set match category (e.g. fitted, one-view, etc), match distance. - Michel current_michel = Michel(univ, mm_idx, vtx); - - // MICHEL MATCH QUALITY CUT -- NEXT VTX - // A michel cluster was matched to this vertex, but the match doesn't - // pass match quality cuts. - if (current_michel.match_category == Michel::kNoMatch) continue; - - // VERTEX MICHEL -- must meet the gold standard for its fit - bool isIntVtx = (vtx == 0); - if (isIntVtx && current_michel.match_category <= Michel::kNoFit) { - continue; - } - // ENDPOINT MICHELS - else { - // ZERO MICHELS FOUND SO FAR - if (ret_michels.size() == 0) - ; - - // ONE MICHEL FOUND SO FAR - // -- If either the michel we have already or this michel is OV, pick - // the better of the two. Only one will remain. - else if (ret_michels.size() == 1) { - Michel reigning_michel = (ret_michels.begin())->second; - if (reigning_michel.match_category == Michel::kOV || - current_michel.match_category == Michel::kOV) - ret_michels.clear(); - current_michel = CompareMichels(reigning_michel, current_michel); - } - - // 2+ MICHELS FOUND SO FAR - else { - if (current_michel.match_category == Michel::kOV) continue; - } - } - - // ADD THIS MICHEL - // When a cluster is matched to two vertices, pick the vtx with the - // better match. - bool SC = AddOrReplaceMichel(ret_michels, current_michel); - } // end vtx loop - return ret_michels; -} - bool NodeCut(const CVUniverse& univ, const RecoPionIdx pion_candidate_idx) { return 6. < univ.GetEnode01(pion_candidate_idx) && univ.GetEnode01(pion_candidate_idx) < 32. && @@ -618,9 +510,9 @@ bool ExactlyOneEndpointMichelCut(const CVUniverse& univ, if (signal_definition == kNPi || signal_definition == kNPiNoW) { return true; } else if (signal_definition == kOnePi || signal_definition == kOnePiNoW) { - MichelMap mm = GetQualityMichels(univ); + endpoint::MichelMap mm = endpoint::GetQualityMichels(univ); if (mm.size() == 1) { // require only one michel - Michel m = (mm.begin())->second; + endpoint::Michel m = (mm.begin())->second; if (m.vtx == 0) return false; // so this has a no-vertex michel cut baked in // pion_candidate_idx = m.vtx - 1; // SELECT OUR PION @@ -767,4 +659,6 @@ std::string GetCutName(ECuts cut) { }; } + + #endif // Cuts_cxx diff --git a/includes/Cuts.h b/includes/Cuts.h index 355e9bc..8f323e3 100644 --- a/includes/Cuts.h +++ b/includes/Cuts.h @@ -88,8 +88,8 @@ EventCount PassedCuts(const CVUniverse&, std::vector& pion_candidate_idxs, std::vector cuts = kCutsVector); bool PassesCut(const CVUniverse&, const ECuts cut, const bool is_mc, - const SignalDefinition, MichelMap& endpoint_michels, - MichelMap& vertex_michels); + const SignalDefinition, endpoint::MichelMap& endpoint_michels, + endpoint::MichelMap& vertex_michels); // Get a vector of integers which are unique hadron prong identifiers. // The length of this vector is the number of pion candidate tracks found. @@ -97,7 +97,6 @@ bool PassesCut(const CVUniverse&, const ECuts cut, const bool is_mc, // Helper std::string GetCutName(ECuts cut); -bool AddOrReplaceMichel(MichelMap& mm, Michel m); // Cuts functions -- eventwide -- TRUTH ONLY bool GoodObjectsCut(const CVUniverse&); @@ -118,7 +117,6 @@ bool XYVertexCut(const CVUniverse& univ, const double a); bool PmuCut(const CVUniverse& univ); // Cuts functions -- on pion candidate tracks -MichelMap GetQualityMichels(const CVUniverse&); bool LLRCut(const CVUniverse&, const RecoPionIdx pion_candidate_idx); bool NodeCut(const CVUniverse&, const RecoPionIdx pion_candidate_idx); diff --git a/includes/Michel.h b/includes/Michel.h index e24aae7..16e360f 100644 --- a/includes/Michel.h +++ b/includes/Michel.h @@ -2,101 +2,1451 @@ #define Michel_H #include "CVUniverse.h" +#include "Cluster.h" -class Michel; - -typedef std::map MichelMap; - -bool IsQualityMatchedMichel_Fit(double fit_dist, double fit_cut); -bool IsQualityMatchedMichel_NoFit(double nofit_dist, double nofit_cut); -bool IsQualityMatchedMichel_OneView(double ov_dist, double ov_cut); - -class Michel { - public: - enum EMatchCategory { kNoMatch, kFit, kNoFit, kOV, kNMatchCategories }; - - // constructors - Michel(const CVUniverse& univ, int i, int v); - Michel() - : idx(-105), - vtx(-106), - had_idx(-107), - match_category(kNoMatch), - fit_distance(-1.) {} - - // integer that uniquely identifies cluster of hits - int idx; - - // integer corresponding to vertex to which cluster was matched. - // vtx == 0 --> interaction vertex - // vtx == 1 --> "first" track endpoint, corresponds to hadron index 0. - // i.e. hadron index = michel vtx - 1. - int vtx; - - // hadron index to which this michel is matched (had_idx = vtx - 1) - int had_idx; - - EMatchCategory match_category; - double fit_distance; -}; - -Michel::Michel(const CVUniverse& univ, int i, int v) - : idx(i), vtx(v), had_idx(v - 1) { - bool isIntVtx = (vtx == 0); - // distances for fitted, 2/3-view nofit, and 1-view michels, respectively - double mm_fit_dist = univ.GetVecElem("matched_michel_end_dist", idx) / 10.; - double mm_nofit_dist = univ.GetVecElem("matched_michel_avg_dist", idx) / 10.; - double mm_ov_dist = univ.GetVecElem("matched_michel_ov_dist", idx) / 10.; - // NEW - const double FIT_CUT = isIntVtx ? 9.0 : 7.5; // cm - const double NOFIT_CUT = isIntVtx ? 10.0 : 50.; // cm - // OLD - // const double FIT_CUT = isIntVtx ? 9.0 : 5.; // cm - // const double NOFIT_CUT = isIntVtx ? 10.0 : 10.; // cm - // TODO distinguish between 2/3 view and OV - // const double FIT_CUT = isIntVtx ? 9. : 15.0; // cm - // const double NOFIT_CUT = isIntVtx ? 10. : 15.0; // cm - if (IsQualityMatchedMichel_Fit(mm_fit_dist, FIT_CUT)) { - match_category = kFit; - fit_distance = mm_fit_dist; - return; - } else if (IsQualityMatchedMichel_NoFit(mm_nofit_dist, NOFIT_CUT)) { - match_category = kNoFit; - fit_distance = mm_nofit_dist; - return; - } else if (IsQualityMatchedMichel_OneView(mm_ov_dist, NOFIT_CUT)) { - match_category = kOV; - fit_distance = mm_ov_dist; - return; - } else { - match_category = kNoMatch; - fit_distance = -2.; - return; +//============================================================================== +// Endpoint Michel Class +//============================================================================== +namespace endpoint { + class Michel; + typedef std::map MichelMap; + + //============================================================================== + // Michel quality cuts + //============================================================================== + bool IsQualityMatchedMichel_Fit(double fit_dist, double fit_cut) { + if (fit_dist >= 0 && fit_dist < fit_cut) + return true; + else + return false; + } + + bool IsQualityMatchedMichel_NoFit(double nofit_dist, double nofit_cut) { + if (nofit_dist >= 0 && nofit_dist < nofit_cut) + return true; + else + return false; + } + + bool IsQualityMatchedMichel_OneView(double ov_dist, double ov_cut) { + if (ov_dist >= 0 && ov_dist * (2.0 / 3) < ov_cut) + return true; + else + return false; + } + + class Michel { + public: + enum EMatchCategory { kNoMatch, kFit, kNoFit, kOV, kNMatchCategories }; + + // constructors + Michel(const CVUniverse& univ, int i, int v); + Michel() + : idx(-105), + vtx(-106), + had_idx(-107), + match_category(kNoMatch), + fit_distance(-1.) {} + + // integer that uniquely identifies cluster of hits + int idx; + + // integer corresponding to vertex to which cluster was matched. + // vtx == 0 --> interaction vertex + // vtx == 1 --> "first" track endpoint, corresponds to hadron index 0. + // i.e. hadron index = michel vtx - 1. + int vtx; + + // hadron index to which this michel is matched (had_idx = vtx - 1) + int had_idx; + + EMatchCategory match_category; + double fit_distance; + }; + + Michel::Michel(const CVUniverse& univ, int i, int v) + : idx(i), vtx(v), had_idx(v - 1) { + bool isIntVtx = (vtx == 0); + // distances for fitted, 2/3-view nofit, and 1-view michels, respectively + double mm_fit_dist = univ.GetVecElem("matched_michel_end_dist", idx) / 10.; + double mm_nofit_dist = univ.GetVecElem("matched_michel_avg_dist", idx) / 10.; + double mm_ov_dist = univ.GetVecElem("matched_michel_ov_dist", idx) / 10.; + // NEW + const double FIT_CUT = isIntVtx ? 9.0 : 7.5; // cm + const double NOFIT_CUT = isIntVtx ? 10.0 : 50.; // cm + // OLD + // const double FIT_CUT = isIntVtx ? 9.0 : 5.; // cm + // const double NOFIT_CUT = isIntVtx ? 10.0 : 10.; // cm + // TODO distinguish between 2/3 view and OV + // const double FIT_CUT = isIntVtx ? 9. : 15.0; // cm + // const double NOFIT_CUT = isIntVtx ? 10. : 15.0; // cm + if (IsQualityMatchedMichel_Fit(mm_fit_dist, FIT_CUT)) { + match_category = kFit; + fit_distance = mm_fit_dist; + return; + } else if (IsQualityMatchedMichel_NoFit(mm_nofit_dist, NOFIT_CUT)) { + match_category = kNoFit; + fit_distance = mm_nofit_dist; + return; + } else if (IsQualityMatchedMichel_OneView(mm_ov_dist, NOFIT_CUT)) { + match_category = kOV; + fit_distance = mm_ov_dist; + return; + } else { + match_category = kNoMatch; + fit_distance = -2.; + return; + } + } + + // -- Given a single michel cluster matched to two vertices + // return vertex with the better-matched michel. + Michel CompareMichels(Michel r, Michel c) { + if (r.match_category > c.match_category) + return r; + else if (r.match_category < c.match_category) + return c; + else { + if (r.fit_distance < c.fit_distance) + return r; + else if (r.fit_distance > c.fit_distance) + return c; + else { + // This must mean we're comparing the same michels with the same fits. + // std::cout << "WEIRD COMPAREMICHELS PROBLEM" << std::endl; + return r; + } + } } -} + + // Add michel to MichelMap. Check if this cluster has already been matched. + // Then use only the best match. + bool AddOrReplaceMichel(MichelMap& mm, Michel m) { + std::pair SC; + if (mm.count(m.idx) == 0) + SC = mm.insert(pair(m.idx, m)); + else { + Michel reigning_michel = mm.at(m.idx); + Michel best_michel = CompareMichels(reigning_michel, m); + mm[m.idx] = best_michel; + } + return true; + } + + //============================================================================ + // Collect the good michels in this event + //============================================================================ + // Get quality michels map<(int michel cluster ID, Michel)> + // * A Michel is uniquely ID-ed by its cluster(of hits) integer index. + // * The michel tool has already vetted the clusters themselves. + // Whereas here we evaluate the quality of the fit. + // * At most one interaction vertex michel (which MUST be "fitted") + // * An OV michel will only be kept if no better michels are in the event. + // * In the case of 2+ OV michels, only keep the best one. + // * required: one-to-one michel<->vertex matching: + // * when one michel cluster is matched to multiple vertices, the vtx + // with the better match is chosen. + // * We don't have the problem in the other direction -- michel tool: a + // vertex cannot be matched to more than one cluster. + // * At the moment, we can return a single michel that is a quality + // interaction vertex michel. We'd like to call these signal, but we don't + // know the pion energy...yet. In the meantime, cut them. + + MichelMap GetQualityMichels(const CVUniverse& univ) { + std::map ret_michels; + std::vector matched_michel_idxs = univ.GetVec("matched_michel_idx"); + + // Loop vertices in the event, i.e. the indices of the michel index vector + for (uint vtx = 0; vtx < matched_michel_idxs.size(); ++vtx) { + int mm_idx = matched_michel_idxs[vtx]; + + // NO MATCH -- GO TO NEXT VTX. No michel cluster matched to this vertex. + if (mm_idx < 0) continue; + + // MICHEL CONSTRUCTOR + // Set match category (e.g. fitted, one-view, etc), match distance. + Michel current_michel = Michel(univ, mm_idx, vtx); + + // MICHEL MATCH QUALITY CUT -- NEXT VTX + // A michel cluster was matched to this vertex, but the match doesn't + // pass match quality cuts. + if (current_michel.match_category == Michel::kNoMatch) continue; + + // VERTEX MICHEL -- must meet the gold standard for its fit + bool isIntVtx = (vtx == 0); + if (isIntVtx && current_michel.match_category <= Michel::kNoFit) { + continue; + } + // ENDPOINT MICHELS + else { + // ZERO MICHELS FOUND SO FAR + if (ret_michels.size() == 0) + ; + + // ONE MICHEL FOUND SO FAR + // -- If either the michel we have already or this michel is OV, pick + // the better of the two. Only one will remain. + else if (ret_michels.size() == 1) { + Michel reigning_michel = (ret_michels.begin())->second; + if (reigning_michel.match_category == Michel::kOV || + current_michel.match_category == Michel::kOV) + ret_michels.clear(); + current_michel = CompareMichels(reigning_michel, current_michel); + } + + // 2+ MICHELS FOUND SO FAR + else { + if (current_michel.match_category == Michel::kOV) continue; + } + } + + // ADD THIS MICHEL + // When a cluster is matched to two vertices, pick the vtx with the + // better match. + bool SC = AddOrReplaceMichel(ret_michels, current_michel); + } // end vtx loop + return ret_michels; + } + +} // namespace endpoint //============================================================================== -// MICHEL QUALITY CUTS +// Vertex Michel Class //============================================================================== -bool IsQualityMatchedMichel_Fit(double fit_dist, double fit_cut) { - if (fit_dist >= 0 && fit_dist < fit_cut) - return true; - else - return false; -} +namespace vertex { + class Michel { + public: + // constructors + Michel(const CVUniverse& univ, int ci); + // Functions + Michel(){}; // fill in most basic stuff in "generic info" . this is default + // constructor you need it if you have public data members + void DoMoreInitializationAndProcessing(); // fill in more complicated stuff + // in "generic info" + void DoMatching(); // fill in "best matching stuff" + void DoesMichelMatchVtx(const CVUniverse& univ); // GEts info for Vtx Match + void DoesMichelMatchClus( + const CVUniverse& univ); // Gets info for ClusterMatch + void GetBestMatch(); // get type for best match out of all four saved matches + void GetPionAngle( + const CVUniverse& + univ); // Function to calculate pion angle with respect to beam. + // Simply gets angle between best michel endpoint and vertex. -bool IsQualityMatchedMichel_NoFit(double nofit_dist, double nofit_cut) { - if (nofit_dist >= 0 && nofit_dist < nofit_cut) - return true; - else - return false; -} + // std::vector CreateMichels(CVUniverse& univ); + // generic info + std::vector up_location; // upstream location 0 X 1 U 2 V 3 Z + std::vector down_location; // downstream location + double m_x1 = 9999.; // Michel Endpoint 1 x + double m_x2 = 9999.; // Michel Endpoint 2 x + double m_y1 = 9999.; // Michel Endpoint 1 y + double m_y2 = 9999.; // Michel Endpoint 2 y + double m_u1 = 9999.; // Michel Endpoint 1 u + double m_u2 = 9999.; // Michel Endpoint 2 u + double m_v1 = 9999.; // Michel Endpoint 1 v + double m_v2 = 9999.; // Michel Endpoint 2 v + double m_z1 = 9999.; // Mihel Endpoint z 1 + double m_z2 = 9999.; // Michel Endpoiint z2 + double energy = -999.; // Michel energy + double time = -999.; // Michel Time + int is_fitted = -1; // Is the Michel fitted? 0 no. 1 yes. + // Following are 2D distances (were stored in vectors but now as explicit data + // members) + double up_to_vertex_XZ = 9999.; + double up_to_vertex_UZ = 9999.; + double up_to_vertex_VZ = 9999.; + double down_to_vertex_XZ = 9999.; + double down_to_vertex_UZ = 9999.; + double down_to_vertex_VZ = 9999.; + double down_to_clus_XZ = 9999.; + double down_to_clus_UZ = 9999.; + double down_to_clus_VZ = 9999.; + double up_to_clus_XZ = 9999.; + double up_to_clus_UZ = 9999.; + double up_to_clus_VZ = 9999.; + // Michel End point to Vertex distance (up = endpoint 1 and down = endpoint + // 2) TODO: actually find out which end point is upstream or downstream + double up_to_vertex_dist3D = 9999.; + double down_to_vertex_dist3D = 9999.; + // Maybe keep a vector of clusters that matched to each endpoint? + std::vector cluster_to_up_match; + std::vector cluster_to_down_match; + // 3D distances between cluster and Michel + double up_to_cluster_dist3D = 9999.; // Distance between vertex and cluster + // that was matched to endpoint 1 + double down_to_cluster_dist3D = + 9999.; // Distance between vertex and cluster matched to endpoint 2 + double up_clus_michel_dist3D = + 9999.; // Distance between Michel endpoint 1 and clusters + double down_clus_michel_dist3D = + 9999.; // Distance between Michel endpoint 2 and clusters + double up_clus_michvtx_dist3D = + 9999.; // Distance between the Michel end point 1 that matched to + // clusters and the vertex - this will be used as pion range + double down_clus_michvtx_dist3D = + 9999.; // Distance between the Michel endpoint 2 that matched to clusters + // and the vertex - this will be used as pion range + double vtx_michel_timediff = 9999.; + + double overlay_fraction = + -1.0; // Overlay fraction of the Michel, Default if Data. 0 if MC 1 if + // Data... (maybe some events in between?) + int nclusters = 0; // number of (non-muon) clusters in the primary event + int vtx_endpoint = 0; // 1 or 2 for which Michel end point is closest + int clus_endpoint = 0; // 1 or 2 for which Michel endpoint is closest + // best matching stuff + // enum *best_cluster_match; // just tells you which of the four matches are + // the best match enum BestClusterMatch {kUpVtxMatch, kDownVtxMatch, + // kUpClusMatch, kDownClusMatch, kNClusterMatches}; the following is in place + // until i figure out how to use the enum. + int BestMatch = 0; // 0 = null match, 1= kUpVtxMatch, 2 = kDownVtxMatch, 3 = + // kUpClusMatch, 4= kDownClusMatch, + int SecondBestMatch = 0; + int tuple_idx; // index of the Michel out of all the michels saved in the + // tuple + double Best3Ddist = + 9999.; // Best 3D distance out of eitehr a vertex or a cluster match + // best 2D distance for the best type of match + double best_XZ = 9999.; + double best_UZ = 9999.; + double best_VZ = 9999.; + // Want to save the index of the clusters that the Michel best matched to. + int xclus_idx; + int uclus_idx; + int vclus_idx; + + // True initial position of the michel TODO: initial the other Michel truth + // member data here (energy, time, momentum etc) + double true_angle = 9999.; + double true_initialx = 9999.; + double true_initialy = 9999.; + double true_initialz = 9999.; + double true_e = 9999.; + double true_p = 9999.; + double true_pdg = -1.0; + int true_parentid = -1; + int true_parentpdg = -1; + double true_parent_energy = -9999.; + double true_parent_p = -9999.; + double true_parent_px = -9999.; + double true_parent_py = -9999.; + double true_parent_pz = -9999.; + double true_parent_xi = -9999.; + double true_parebt_yi = -9999.; + double true_parent_zi = -9999.; + double true_parent_xf = -9999.; + double true_parent_yf = -9999.; + double true_parent_zf = -9999.; + // the following member data were created to investigate my weird convoluted + // way of geting x and y values. Probably dont need them now. // TODO: check + // and remove the following member data + double best_angle = -9999.; + double up_clus_x = 9999.; + double up_clus_y = 9999.; + double up_clus_z = 9999.; + double down_clus_x = 9999.; + double down_clus_y = 9999.; + double down_clus_z = 9999.; + double up_vtx_x = 9999.; + double up_vtx_y = 9999.; + double up_vtx_z = 9999.; + double down_vtx_x = 9999.; + double down_vtx_y = 9999.; + double down_vtx_z = 9999.; + double is_overlay = -1; + double pionKE = -9999.; + // Adding the following member data to determine the true endpoint position of + // the Michel + int trueEndpoint = + -1; // 0 = Overlay Michel, 1 = Endpoint 1 is correct intial position of + // Michel, 2 = Endpoint 2 is correct Initial Position of Michel + // ~DeleteMichel(){delete this;}; // This is going to be the main destructor. + int recoEndpoint = -1; // -1 is default/NULL like above. 1 = Endpoint 1 is + // better match, 2 = Endpoint 2 is better match + + std::vector passable_matchtype{ + -1, -1, -1, + -1}; // This vector will contain a value for each match type -1 or the + // matchtype 1, 2, 3, 4 for UpVtx, DownVTx, Upclus, DownClus + // depending of that match type passes our 2D distance cut. (if + // distance is large, then it'll pass all of them). + }; + + // Setting the Michel data members directly from CV universe. No calculations + // made at this stage + Michel::Michel(const CVUniverse& univ, int ci) { + energy = univ.GetVecElem("FittedMichel_michel_energy", ci); + time = univ.GetVecElem("FittedMichel_michel_time", ci) / pow(10, 3); + is_fitted = univ.GetVecElem("FittedMichel_michel_fitPass", ci); + up_location.push_back(univ.GetVecElem("FittedMichel_michel_x1", ci)); + up_location.push_back(univ.GetVecElem("FittedMichel_michel_u1", ci)); + up_location.push_back(univ.GetVecElem("FittedMichel_michel_v1", ci)); + up_location.push_back(univ.GetVecElem("FittedMichel_michel_z1", ci)); + down_location.push_back(univ.GetVecElem("FittedMichel_michel_x2", ci)); + down_location.push_back(univ.GetVecElem("FittedMichel_michel_u2", ci)); + down_location.push_back(univ.GetVecElem("FittedMichel_michel_v2", ci)); + down_location.push_back(univ.GetVecElem("FittedMichel_michel_z2", ci)); + m_x1 = univ.GetVecElem("FittedMichel_michel_x1", ci); + m_y1 = univ.GetVecElem("FittedMichel_michel_y1", ci); + m_u1 = univ.GetVecElem("FittedMichel_michel_u1", ci); + m_v1 = univ.GetVecElem("FittedMichel_michel_v1", ci); + m_z1 = univ.GetVecElem("FittedMichel_michel_z1", ci); + m_x2 = univ.GetVecElem("FittedMichel_michel_x2", ci); + m_y2 = univ.GetVecElem("FittedMichel_michel_y2", ci); + m_u2 = univ.GetVecElem("FittedMichel_michel_u2", ci); + m_z2 = univ.GetVecElem("FittedMichel_michel_z2", ci); + m_v2 = univ.GetVecElem("FittedMichel_michel_v2", ci); + nclusters = univ.GetInt("FittedMichel_cluster_view_sz"); + overlay_fraction = univ.GetVecElem("FittedMichel_michel_datafraction", ci); + + true_initialx = + univ.GetVecElem("FittedMichel_reco_micheltrajectory_initialx", ci); + true_initialy = + univ.GetVecElem("FittedMichel_reco_micheltrajectory_initialy", ci); + true_initialz = + univ.GetVecElem("FittedMichel_reco_micheltrajectory_initialz", ci); + is_overlay = univ.GetVecElem("FittedMichel_michel_isoverlay", ci); + true_e = univ.GetVecElem("FittedMichel_reco_micheltrajectory_energy", ci); + true_pdg = univ.GetVecElem("FittedMichel_reco_micheltrajectory_pdg", ci); + true_parentpdg = univ.GetVecElem("FittedMichel_true_primaryparent_pdg", ci); + true_parentid = + univ.GetVecElem("FittedMichel_true_primaryparent_trackID", ci); + true_p = univ.GetVecElem("FittedMichel_reco_micheltrajectory_momentum", ci); + + double true_parentp = + univ.GetVecElem("FittedMichel_true_primaryparent_momentum", ci); + double true_parente = + univ.GetVecElem("FittedMichel_true_primaryparent_energy", ci); + double mass = mass = sqrt(pow(true_parente, 2) - pow(true_parentp, 2)); + pionKE = true_parente - mass; + + double true_parentpx = + univ.GetVecElem("FittedMichel_true_primaryparent_momentumx", ci); + double true_parentpy = + univ.GetVecElem("FittedMichel_true_primaryparent_momentumy", ci); + double true_parentpz = + univ.GetVecElem("FittedMichel_true_primaryparent_momentumz", ci); + + TVector3 truep(true_parentpx, true_parentpy, true_parentpz); + double true_theta = truep.Theta(); + true_angle = true_theta; //*TMath::RadToDeg(); + + // if (overlay_fraction < 0.5) std::cout << "True Parent of Michel is PDG: " + // << true_parentpdg << " And Parent trackID: " << true_parentid << + // std::endl; + double end1diff = + abs(true_initialz - + m_z1); // This gives a value for determining how close the + // reconstructed endpoint of the michel is to the true intial + // endpoint (the start point of where the michel decayed from) + double end2diff = + abs(true_initialz - + m_z2); // this is for endpoint 2. If you compare this to the endpoint + // that gets matched to a verted or cluster, you can determine + // which type of match ends up getting correcct matches or + // wrong matches. + + if (overlay_fraction > 0.5) + trueEndpoint = 0; + else if (true_parentpdg == 211 && end1diff < end2diff) + trueEndpoint = 1; + else if (true_parentpdg == 211 && end2diff < end1diff) + trueEndpoint = 2; + if (is_fitted == 1) { // Do theMatching for Fitted Michels + DoesMichelMatchVtx(univ); // GEts info for Vtx Match + DoesMichelMatchClus(univ); // Gets info for ClusterMatch + GetBestMatch(); + GetPionAngle(univ); + } + } + + // Get the angle between Michel endpoint that was matched and the vertex + void Michel::GetPionAngle(const CVUniverse& univ) { + double vtx_x = univ.GetVertex().X(); // mm + double vtx_y = univ.GetVertex().Y(); // mm + double vtx_z = univ.GetVertex().Z(); // mm + + TVector3 vtx(vtx_x, vtx_y, vtx_z); + TVector3 endpoint; + + if (this->BestMatch == 1 || this->BestMatch == 3) + endpoint.SetXYZ(this->m_x1, this->m_y1, this->m_z1); + else if (this->BestMatch == 2 || this->BestMatch == 4) + endpoint.SetXYZ(this->m_x2, this->m_y2, this->m_z2); + else + endpoint.SetXYZ(9999., 9999., 9999.); + + TVector3 range = endpoint - vtx; + double angle = univ.thetaWRTBeam(range.x(), range.y(), range.z()); + this->best_angle = angle; //*TMath::RadToDeg(); // in Degrees + } + + // This function will get an integer for the best match type of the Michel. + // It compares distance between MIchel and whatever it's best match is to find + // the Best type of Michel for a single Michel. + void Michel::GetBestMatch() { + int upvtxmatch = 0; + int downvtxmatch = 0; + int upclusmatch = 0; + int downclusmatch = 0; + + // This is setting the values for which endpoint is a better match for each + // type. + // + // TODO: Revise this function. There has to be a better way to compare + // distances than what I wrote. + std::vector distances{ + this->up_to_vertex_dist3D, this->down_to_vertex_dist3D, + this->up_clus_michel_dist3D, this->down_clus_michel_dist3D}; + std::sort(distances.begin(), distances.end()); + + // This bit of code will try to find the best 3D distance end point + if (distances[0] == this->up_to_vertex_dist3D) { + upvtxmatch = 1; + this->best_XZ = this->up_to_vertex_XZ; + this->best_UZ = this->up_to_vertex_UZ; + this->best_VZ = this->up_to_vertex_VZ; + this->BestMatch = 1; + this->Best3Ddist = this->up_to_vertex_dist3D; + // std::cout << "This Michel is UPVTX and has true endpoint " << + // this->trueEndpoint << std::endl; + } else if (distances[0] == this->down_to_vertex_dist3D) { + this->BestMatch = 2; + this->best_XZ = this->down_to_vertex_XZ; + this->best_UZ = this->down_to_vertex_UZ; + this->best_VZ = this->down_to_vertex_VZ; + this->Best3Ddist = this->down_to_vertex_dist3D; + downvtxmatch = 1; + // std::cout << "This Michel is DOWNVTX and has true endpoint " << + // this->trueEndpoint << std::endl; + } else if (distances[0] == this->up_clus_michel_dist3D) { + this->BestMatch = 3; + this->best_XZ = this->up_to_clus_XZ; + this->best_UZ = this->up_to_clus_VZ; + this->best_VZ = this->up_to_clus_UZ; + this->Best3Ddist = this->up_clus_michvtx_dist3D; + upclusmatch = 1; + // std::cout << "This Michel is UPCLUS and has true endpoint " << + // this->trueEndpoint << std::endl; + } else if (distances[0] == this->down_clus_michel_dist3D) { + this->BestMatch = 4; + this->Best3Ddist = this->down_clus_michvtx_dist3D; + this->best_XZ = this->down_to_clus_XZ; + this->best_UZ = this->down_to_clus_UZ; + this->best_VZ = this->down_to_clus_VZ; + downclusmatch = 1; + // std::cout << "This Michel is DOWNCLUS and has true endpoint " << + // this->trueEndpoint << std::endl; + } else { + this->BestMatch = 0; + this->Best3Ddist = 9999.; + this->best_XZ = 9999.; + this->best_UZ = 9999.; + this->best_VZ = 9999.; + } + + // Second best + if (distances[1] == this->up_to_vertex_dist3D) + this->SecondBestMatch = 1; + else if (distances[1] == this->down_to_vertex_dist3D) + this->SecondBestMatch = 2; + else if (distances[1] == this->up_clus_michel_dist3D) + this->SecondBestMatch = 3; + else if (distances[1] == this->down_clus_michel_dist3D) + this->SecondBestMatch = 4; + else { + this->SecondBestMatch = 0; + } + int matchtype = this->BestMatch; + // Identifying the best reco endpoint based on the Best MAtch type. + if (matchtype == 1 || matchtype == 3) + this->recoEndpoint = 1; + else if (matchtype == 2 || matchtype == 4) + this->recoEndpoint = 2; + } + + void Michel::DoesMichelMatchVtx(const CVUniverse& univ) { + // std::cout << "GETTING VTX MATCH FOR MICHEL " << std::endl; + + // Getting Vertex Information + double vtx_x = univ.GetVertex().X(); // mm + double vtx_y = univ.GetVertex().Y(); // mm + double vtx_z = univ.GetVertex().Z(); // mm + double vtx_t = univ.GetVertex().T() / pow(10, 3); // mus + double vtx_u = (0.5 * (vtx_x - sqrt(3.) * vtx_y)); + double vtx_v = (0.5 * (vtx_x + sqrt(3.) * vtx_y)); + + // std::cout << "VTX POSITION is (x, u , v, y, z) (" << vtx_x << " , " << + // vtx_u << " , " << vtx_v << " , " << vtx_y << " , " << vtx_z << std::endl; + // Initializing all the distance comparisons I will need to make + double zdiff1 = vtx_z - this->m_z1; + double zdiff2 = vtx_z - this->m_z2; + double xdiff = 9999.; + double udiff = 9999.; + double vdiff = 9999.; + double XZdist = 9999.; + double UZdist = 9999.; + double VZdist = 9999.; + + double michely1 = this->m_y1; + double michely2 = this->m_y2; + double michelx1 = this->m_x1; + double michelx2 = this->m_z2; + double michelz1 = this->m_z1; + double michelz2 = this->m_z2; + double timediff = (this->time) - vtx_t; + // std::cout << "Michel time " << this->time << " Vertex time " << vtx_t << + // "\n" << std::endl; + this->vtx_michel_timediff = timediff; + + // 2D distance calculations for Endpoint 1 + xdiff = abs(vtx_x - this->m_x1); + udiff = abs(vtx_u - this->m_u1); + vdiff = abs(vtx_v - this->m_v1); + + XZdist = sqrt(xdiff * xdiff + zdiff1 * zdiff1); + UZdist = sqrt(udiff * udiff + zdiff1 * zdiff1); + VZdist = sqrt(vdiff * vdiff + zdiff1 * zdiff1); + + this->up_to_vertex_XZ = XZdist; + this->up_to_vertex_UZ = UZdist; + this->up_to_vertex_VZ = VZdist; + + // 2D Distance calculations for endpoint2 + xdiff = abs(vtx_x - this->m_x2); + udiff = abs(vtx_u - this->m_u2); + vdiff = abs(vtx_v - this->m_v2); + XZdist = sqrt(xdiff * xdiff + zdiff2 * zdiff2); + UZdist = sqrt(udiff * udiff + zdiff2 * zdiff2); + VZdist = sqrt(vdiff * vdiff + zdiff2 * zdiff2); + + this->down_to_vertex_XZ = XZdist; + this->down_to_vertex_UZ = UZdist; + this->down_to_vertex_VZ = VZdist; + + // 3D distance calculations + double xdiff1 = abs(vtx_x - this->m_x1); + double xdiff2 = abs(vtx_x - this->m_x2); + double ydiff1 = abs(vtx_y - this->m_y1); + double ydiff2 = abs(vtx_y - this->m_y2); + + double dist1 = sqrt(zdiff1 * zdiff1 + xdiff1 * xdiff1 + ydiff1 * ydiff1); + double dist2 = sqrt(zdiff2 * zdiff2 + xdiff2 * xdiff2 + ydiff2 * ydiff2); + + this->up_to_vertex_dist3D = dist1; + this->down_to_vertex_dist3D = dist2; + + if (dist1 < dist2) + this->vtx_endpoint = 1; + else if (dist2 < dist1) + this->vtx_endpoint = 2; + + ////std::cout << "END OF LOOP TO FIND VTX MATCH" << std::endl; + } + + void Michel::DoesMichelMatchClus(const CVUniverse& univ) { + // This is where the function for Cluster Matching goes + + ////std::cout << "STARTING SEARCH FOR CLUSTER MATCH " << std::endl; + // Inititalizing vertex variables needed for cluster matching + int nclusters = this->nclusters; + // std::cout << "There are " << nclusters << " available clusters for this + // matching " << std::endl; + double vtx_x = univ.GetVertex().X(); // mm + double vtx_y = univ.GetVertex().Y(); // mm + double vtx_z = univ.GetVertex().Z(); // mm + double vtx_t = univ.GetVertex().T() / pow(10, 3); // mus + double vtx_u = (0.5 * (vtx_x - sqrt(3.) * vtx_y)); + double vtx_v = (0.5 * (vtx_x + sqrt(3.) * vtx_y)); + + double closestdistance1x = 9999.; + double closestdistance1u = 9999.; + double closestdistance1v = 9999.; + double closestdistance1z = 9999.; + + double closestdistance2x = 9999.; + double closestdistance2u = 9999.; + double closestdistance2v = 9999.; + double closestdistance2z = 9999.; + + double michelx1 = this->m_x1; + double michelx2 = this->m_x2; + double michelu1 = this->m_u1; + double michelu2 = this->m_u2; + double michelv1 = this->m_v1; + double michelv2 = this->m_v2; + double michelz1 = this->m_z1; + double michelz2 = this->m_z2; + double michely1 = this->m_y1; + double michely2 = this->m_y2; + + double micheltime = this->time; + + // std::cout << "Michel position 1 is (x, u, v, y, z) " << michelx1 << " , " + // << michelu1 << " , " << michelv1 << " , "<< michely1 << " , " << michelz1 + // << std::endl; + + // std::cout << "Michel position 2 is (x, y, v, y, z) " << michelx2 << " , " + // << michelu2 << " , " << michelv2 << " , " << michely2 << " , " << michelz2 + // << std::endl; + + std::vector endpoint1_clus; + std::vector endpoint2_clus; + + // Get the closest distance for each view + + ////std::cout << "STARTING LOOP OVER CLUSTERS " << std::endl; + int x1_idx = -1; // want to save the index for each closest cluster + int u1_idx = -1; + int v1_idx = -1; + int x2_idx = -1; + int u2_idx = -1; + int v2_idx = -1; + + for (int i = 0; i < nclusters; i++) { + Cluster current_cluster = Cluster(univ, i); + + double energy = current_cluster.energy; + double time = current_cluster.time; + double pos = current_cluster.pos; + double zpos = current_cluster.zpos; + int view = current_cluster.view; + double timediff = micheltime - time; + + if (energy < 2.) continue; // only get clusters greater than 2 MeV + + // std::cout << "printing cluster info " << "energy " << energy << " time " + // << time << " pos " << pos << " zpos " << zpos << std::endl; + + double zdiff1 = abs(zpos - michelz1); + double zdiff2 = abs(zpos - michelz2); + // Calculating 2D distance in X view + if (view == 1) { + // Endpoint 1 calculations + double xdiff1 = abs(pos - michelx1); + + double x2Ddistance1 = sqrt(xdiff1 * xdiff1 + zdiff1 * zdiff1); + + // Endpoint 2 Calculations + double xdiff2 = abs(pos - michelx2); + + double x2Ddistance2 = sqrt(xdiff2 * xdiff2 + zdiff2 * zdiff2); + + if (x2Ddistance1 <= closestdistance1x) { + closestdistance1x = + x2Ddistance1; // this is redundant if I just use index instead + x1_idx = i; + } + if (x2Ddistance2 <= closestdistance2x) { + closestdistance2x = x2Ddistance2; + x2_idx = i; + } + } else if (view == 2) // Calculating 2D distance in U view + { + // Endpoint 2 Calculations + double udiff1 = abs(pos - michelu1); + + double u2Ddistance1 = sqrt(udiff1 * udiff1 + zdiff1 * zdiff1); + + // Endpoint 1 Calculations + double udiff2 = abs(pos - michelu2); + + double u2Ddistance2 = sqrt(udiff2 * udiff2 + zdiff2 * zdiff2); + + if (u2Ddistance1 < closestdistance1u) { + closestdistance1u = u2Ddistance1; + u1_idx = i; + } + if (u2Ddistance2 < closestdistance2u) { + closestdistance2u = u2Ddistance2; + u2_idx = i; + } + + } else if (view == 3) // Calculating 2D dsitance in V view + { + // Endpoint 1 Calculations + double vdiff1 = abs(pos - michelv1); + + double v2Ddistance1 = sqrt(vdiff1 * vdiff1 + zdiff1 * zdiff1); + // Endpoint 2 Calculations + double vdiff2 = abs(pos - michelv2); + + double v2Ddistance2 = sqrt(vdiff2 * vdiff2 + zdiff2 * zdiff2); + + if (v2Ddistance1 < closestdistance1v) { + closestdistance1v = v2Ddistance1; + v1_idx = i; + } + if (v2Ddistance2 <= closestdistance2v) { + closestdistance2v = v2Ddistance2; + v2_idx = i; + } + } + } + + // std::cout << "Printing closest clusters index to each end point: x1: " << + // x1_idx << " u1: " << u1_idx << " v1: " << v1_idx << " x2: " << x2_idx << " + // u2: " << u2_idx << " v2: " << v2_idx << std::endl; + + // Now store the closest X, u, v clusters for each Michel Endpoint based on + // the above closest distance + + // Closest cluster's index will be used to only + + std::vector clusx1; + std::vector clusx2; + + std::vector clusu1; + std::vector clusu2; + + std::vector clusv1; + std::vector clusv2; + for (int i = 0; i < nclusters; i++) { + Cluster current_cluster = Cluster(univ, i); + + double energy = current_cluster.energy; + double time = current_cluster.time; + double pos = current_cluster.pos; + double zpos = current_cluster.zpos; + int view = current_cluster.view; + double timediff = micheltime - time; + if (energy < 2.) continue; + // std::cout << "Printing details about cluster "<< i << " : " << energy + // << " : " << time << " : " << pos << " : " << zpos << " : " << view << " + // : " << timediff << std::endl; + + double zdiff1 = abs(zpos - michelz1); + double zdiff2 = abs(zpos - michelz2); + + // Saving clusters with distances equal to the closest clusters. Again, this + // is probably not the best way. I need to rewrite this section to just use + // clusters from the index I saved in the previous loop. That way I reduce + // the number of clusters that I have to loop over. + + if (view == 1) { + // Endpoint 1 + + if (i == x1_idx) { // if current index is same as the closest endpoint 1 + // cluster in X View + endpoint1_clus.push_back(current_cluster); + this->cluster_to_up_match.push_back( + current_cluster); // this one is redundant + // std::cout << "Printing details about Endpoint + // 1 X cluster index "<< i << " energy : " << + // energy << " time : " << time << " pos : " << + // pos << " zpos : " << zpos << " view : " << + // view + // << " timedifference with Michel : " << + // timediff + // << std::endl; + + clusx1.push_back(pos); + clusx1.push_back(zpos); + } + if (i == x2_idx) { // if current index is same as the closest endpoint 2 + // cluster in X View + endpoint2_clus.push_back(current_cluster); + this->cluster_to_down_match.push_back(current_cluster); // TODO remove + // std::cout << "Printing details about Endpoint 2 X cluster "<< i << " + // energy : " << energy << " time : " << time << " pos : " << pos << " + // zpos: " << zpos << " view : " << view << " timediff : " << timediff + // << std::endl; + + clusx2.push_back(pos); + clusx2.push_back(zpos); + } + + } else if (view == 2) { + if (i == u1_idx) { // if current index is same as the closest endpoint 1 + // cluster in U View + endpoint1_clus.push_back(current_cluster); + this->cluster_to_up_match.push_back(current_cluster); // Remove? + // std::cout << "Printing details about Endpoint 1 U cluster "<< i << " + // : " << energy << " : " << time << " : " << pos << " : " << zpos << " + // : " << view << " : " << timediff << std::endl; + + clusu1.push_back(pos); + clusu1.push_back(zpos); + } + if (i == u2_idx) // if current index is same as the closest endpoint 2 + // cluster in U View + { + endpoint2_clus.push_back(current_cluster); + this->cluster_to_down_match.push_back(current_cluster); // remove? + // std::cout << "Printing details about Endpoint 2 U cluster "<< i << " + // : " << energy << " : " << time << " : " << pos << " : " << zpos << " + // : " << view << " : " << timediff << std::endl; + + clusu2.push_back(pos); + clusu2.push_back(zpos); + } + } else if (view == 3) { + if (i == v1_idx) { // if current index is same as the closest endpoint 1 + // cluster in V View + endpoint1_clus.push_back(current_cluster); + this->cluster_to_up_match.push_back(current_cluster); // remove? + // std::cout << "Printing details about Endpoint 1 V cluster "<< i << " + // : " << energy << " : " << time << " : " << pos << " : " << zpos << " + // : " << view << " : " << timediff << std::endl; + + clusv1.push_back(pos); + clusv1.push_back(zpos); + } + if (i == v2_idx) { // if current index is same as the closest endpoint 2 + // cluster in V View + endpoint1_clus.push_back(current_cluster); + this->cluster_to_down_match.push_back(current_cluster); // remove? + // std::cout << "Printing details about Endpoint 2 V cluster "<< i << " + // : " << energy << " : " << time << " : " << pos << " : " << zpos << " + // : " << view << " : " << timediff << std::endl; + + clusv2.push_back(pos); + clusv2.push_back(zpos); + } + } + + } // End of loop over clusters + + // This is vector of positions for each endpoint cluster match + std::vector matchclus1; // index [0] = x, [1] = y, [2] = z + std::vector matchclus2; // index [0] = x, [1] = y, [2] = z + + // std::cout << "LOOPING OVER ENDPOINT1 CLUSTERS" << std::endl; + + double XZdist1 = 9999.; + double UZdist1 = 9999.; + double VZdist1 = 9999.; + + // Check if our cluster vectors are empty and calculate 2D distances for + // endpoint 1 + if (!clusx1.empty()) { + double xdif = abs(this->m_x1 - clusx1[0]); + double zdif = abs(this->m_z1 - clusx1[1]); + XZdist1 = sqrt(xdif * xdif + zdif * zdif); + } + if (!clusu1.empty()) { + double udif = abs(this->m_u1 - clusu1[0]); + double zdif = abs(this->m_z1 - clusu1[1]); + UZdist1 = sqrt(udif * udif + zdif * zdif); + } + if (!clusv1.empty()) { + double vdif = abs(this->m_v1 - clusv1[0]); + double zdif = abs(this->m_z1 - clusv1[1]); + VZdist1 = sqrt(vdif * vdif + zdif * zdif); + } + + // std::cout << " XZ, UZ, VZ 1: " << XZdist1 << " , " << UZdist1 << " , " << + // VZdist1 << std::endl; Saving the 2D distances for endpoint 1 + this->up_to_clus_XZ = XZdist1; + this->up_to_clus_UZ = UZdist1; + this->up_to_clus_VZ = VZdist1; + + double XZdist2 = 9999.; + double UZdist2 = 9999.; + double VZdist2 = 9999.; + + if (!clusx2.empty()) { + double xdif = abs(this->m_x2 - clusx2[0]); + double zdif = abs(this->m_z2 - clusx2[1]); + XZdist2 = sqrt(xdif * xdif + zdif * zdif); + } + if (!clusu2.empty()) { + double udif = abs(this->m_u2 - clusu2[0]); + double zdif = abs(this->m_z2 - clusu2[1]); + UZdist2 = sqrt(udif * udif + zdif * zdif); + } + if (!clusv2.empty()) { + double vdif = abs(this->m_v2 - clusv2[0]); + double zdif = abs(this->m_z2 - clusv2[1]); + VZdist2 = sqrt(vdif * vdif + zdif * zdif); + } + + this->down_to_clus_XZ = XZdist2; + this->down_to_clus_UZ = UZdist2; + this->down_to_clus_VZ = VZdist2; + this->down_clus_y = michely2; + this->down_clus_x = michelx2; + this->down_clus_z = michelz2; + + // std::cout << "GET 3D Information for Clusters - ENDPOINT1" << std::endl; + // This is the convoluted system that calculates the Endpoint + if (XZdist1 < UZdist1 && XZdist1 < VZdist1 && + UZdist1 < VZdist1) { // XU views closest + if (!clusu1.empty() && !clusx1.empty()) { + double yclus = (1. / sqrt(3.)) * (clusx1[0] - 2 * clusu1[0]); + matchclus1.push_back(clusx1[0]); + matchclus1.push_back(yclus); // y point of match 3D point + matchclus1.push_back(clusx1[1]); // setting the cluster 3D point z to be + // of the closest view + // std::cout << "The 2 closest clusters to EndPoint 1 are X and U with (x, + // y, z) point ( " << clusx1[0] << " , " << yclus << " , " << clusx1[1] << + // " ) " << std::endl; + } + } else if (XZdist1 < UZdist1 && XZdist1 < VZdist1 && + UZdist1 > VZdist1) { // XV closest + if (!clusv1.empty() && !clusx1.empty()) { + double yclus = (1. / sqrt(3.)) * (2 * clusv1[0] - clusx1[0]); + // std::cout << "The 2 closest clusters to Endpoint 1 are X and V with (x, + // y, z) point ( " << clusx1[0] << " , " << yclus << " , " << clusx1[1] << + // " ) " << std::endl; + + matchclus1.push_back(clusx1[0]); + matchclus1.push_back(yclus); + matchclus1.push_back(clusx1[1]); // seting the cluster 3D point z to be + // of the closest view + } + } else if (UZdist1 < XZdist1 && UZdist1 < VZdist1 && + VZdist1 < XZdist1) { // UV closest + if (!clusv1.empty() && !clusu1.empty()) { + double yclus = (1. / sqrt(3.)) * (clusv1[0] - clusu1[0]); + double xclus = clusu1[0] + clusv1[0]; + // std::cout << "The 2 closest clusters to EndPoint 1 are U and V with (x, + // y, z) point ( " << xclus << " , " << yclus << " , " << clusu1[1] << " ) + // " << std::endl; + + matchclus1.push_back(xclus); + matchclus1.push_back(yclus); + matchclus1.push_back(clusu1[1]); // seting the cluster 3D point z to be + // of the closest view + } + } else if (UZdist1 < XZdist1 && UZdist1 < VZdist1 && + VZdist1 > XZdist1) { // UX closest + if (!clusu1.empty() && !clusx1.empty()) { + double yclus = (1. / sqrt(3.)) * (clusx1[0] - 2 * clusu1[0]); + // std::cout << "The 2 closest clusters to EndPoint 1 are U and X with (x, + // y, z) point ( " << clusx1[0] << " , " << yclus << " , " << clusu1[1] << + // " ) " << std::endl; + + matchclus1.push_back(clusx1[0]); + matchclus1.push_back(yclus); + matchclus1.push_back(clusu1[1]); // seting the cluster 3D point z to be + // of the closest view + this->up_clus_y = michely1; + } + } else if (VZdist1 < XZdist1 && VZdist1 < UZdist1 && + XZdist1 < UZdist1) { // VX closest + if (!clusv1.empty() && !clusx1.empty()) { + double yclus = ((1. / sqrt(3.)) * (2 * clusv1[0] - clusx1[0])); + // std::cout << "The 2 closest clusters to EndPoint 1 are V and X with (x, + // y, z) point ( " << clusx1[0] << " , " << yclus << " , " << clusv1[1] << + // " ) " << std::endl; + + matchclus1.push_back(clusx1[0]); + matchclus1.push_back(yclus); + matchclus1.push_back(clusv1[1]); // seting the cluster 3D point z to be + // of the closest view + this->up_clus_y = michely1; + } + } else if (VZdist1 < XZdist1 && VZdist1 < UZdist1 && + XZdist1 > UZdist1) { // VU closest + if (!clusu1.empty() && !clusv1.empty()) { + double xclus = (1. / sqrt(3.)) * (clusv1[0] - clusu1[0]); + double yclus = clusu1[0] + clusv1[0]; + // std::cout << "The 2 closest clusters to EndPoint 1 are V and U with (x, + // y, z) point ( " << xclus << " , " << yclus << " , " << clusv1[1] << " ) + // " << std::endl; + + matchclus1.push_back(xclus); + matchclus1.push_back(yclus); + matchclus1.push_back(clusv1[1]); // seting the cluster 3D point z to be + // of the closest view + this->up_clus_y = michely1; + this->up_clus_x = michelx1; + } + } + if (XZdist2 < UZdist2 && XZdist2 < VZdist2 && UZdist2 < VZdist2) { + // std::cout << "XU closest to endpoint 2" << std::endl; + if (!clusu2.empty() && !clusx2.empty()) { + double yclus = (1. / sqrt(3.)) * (clusx2[0] - 2 * clusu2[0]); + // std::cout << "The 2 closest clusters to EndPoint 2 are X and U with (x, + // y, z) point ( " << clusx2[0] << " , " << yclus << " , " << clusx2[1] << + // " ) " << std::endl; + + matchclus2.push_back(clusx2[0]); + matchclus2.push_back(yclus); + matchclus2.push_back(clusx2[1]); // seting the cluster 3D point z to be + // of the closest view + + this->down_clus_y = michely2; + } + } else if (XZdist2 < UZdist2 && XZdist2 < VZdist2 && UZdist2 > VZdist2) { + // std::cout << "XV closest to endpoint 2" << std::endl; + if (!clusv2.empty() && !clusx2.empty()) { + double yclus = (1. / sqrt(3.)) * (2 * clusv2[0] - clusx2[0]); + // std::cout << "The 2 closest clusters to EndPoint 2 are X and V with (x, + // y, z) point ( " << clusx2[0] << " , " << yclus << " , " << clusx2[1] << + // " ) " << std::endl; + + matchclus2.push_back(clusx2[0]); + matchclus2.push_back(yclus); + matchclus2.push_back(clusx2[1]); // seting the cluster 3D point z to be + // of the closest view + this->down_clus_y = michely2; + } + } else if (UZdist2 < XZdist2 && UZdist2 < VZdist2 && VZdist2 < XZdist2) { + // std::cout << "UV closest to endpoint 2" << std::endl; + if (!clusu2.empty() && !clusv2.empty()) { + double xclus = clusu2[0] + clusv2[0]; + double yclus = (1. / sqrt(3.)) * (clusv2[0] - clusu2[0]); + // std::cout << "The 2 closest clusters to EndPoint 2 are U and V with (x, + // y, z) point ( " << xclus << " , " << yclus << " , " << clusu2[1] << " ) + // " << std::endl; + + matchclus2.push_back(xclus); + matchclus2.push_back(yclus); + matchclus2.push_back(clusu2[1]); // seting the cluster 3D point z to be + // of the closest view + this->down_clus_y = michely2; + this->down_clus_y = michelx2; + } + } else if (UZdist2 < XZdist2 && UZdist2 < VZdist2 && VZdist2 > XZdist2) { + // std::cout << "UX closest to endpoint 2" << std::endl; + if (!clusu2.empty() && !clusx2.empty()) { + double yclus = (1. / sqrt(3.)) * (clusx2[0] - 2 * clusu2[0]); + // std::cout << "The 2 closest clusters to EndPoint 2 are U and X with (x, + // y, z) point ( " << clusx2[0] << " , " << yclus << " , " << clusu2[1] << + // " ) " << std::endl; + + matchclus2.push_back(clusx2[0]); + matchclus2.push_back(yclus); + matchclus2.push_back(clusu2[1]); // seting the cluster 3D point z to be + // of the closest view + this->down_clus_y = michely2; + } + } else if (VZdist2 < XZdist2 && VZdist2 < UZdist2 && XZdist2 < UZdist2) { + // std::cout << "VX closest to endpoint 2" << std::endl; + if (!clusv2.empty() && !clusx2.empty()) { + double yclus = ((1. / sqrt(3.)) * (2 * clusv2[0] - clusx2[0])); + // std::cout << "The 2 closest clusters to EndPoint 2 are V and X with (x, + // y, z) point ( " << clusx2[0] << " , " << yclus << " , " << clusv2[1] << + // " ) " << std::endl; + + matchclus2.push_back(clusx2[0]); + matchclus2.push_back(yclus); + matchclus2.push_back(clusv2[1]); // seting the cluster 3D point z to be + // of the closest view + this->down_clus_y = michely2; + } + } else if (VZdist2 < XZdist2 && VZdist2 < UZdist2 && XZdist2 > UZdist2) { + // std::cout << "VU closest to endpoint 2" << std::endl; + if (!clusu2.empty() && !clusv2.empty()) { + double xclus = (1. / sqrt(3.)) * (clusv2[0] - clusu2[0]); + double yclus = clusu2[0] + clusv2[0]; + // std::cout << "The 2 closest clusters to EndPoint 2 are V and U with (x, + // y, z) point ( " << xclus << " , " << yclus << " , " << clusv2[1] << " ) + // " << std::endl; + + matchclus2.push_back(xclus); + matchclus2.push_back(yclus); + matchclus2.push_back(clusv2[1]); // seting the cluster 3D point z to be + // of the closest view + this->down_clus_y = michely2; + this->down_clus_x = michelx2; + } + } + + // std::cout << "GETTING 3D DISTANCES FOR THE CUSTERS " << std::endl; + double clusx1diff = 9999.; + double clusy1diff = 9999.; + double clusz1diff = 9999.; + + double mclusx1diff = 9999.; + double mclusy1diff = 9999.; + double mclusz1diff = 9999.; + + double michvtx_x1diff = 9999.; + double michvtx_x2diff = 9999.; + double michvtx_y1diff = 9999.; + double michvtx_y2diff = 9999.; + double michvtx_z1diff = 9999.; + double michvtx_z2diff = 9999.; + + if (!matchclus1.empty()) { + clusx1diff = vtx_x - matchclus1[0]; + clusy1diff = vtx_y - matchclus1[1]; + clusz1diff = vtx_z - matchclus1[2]; + mclusx1diff = michelx1 - matchclus1[0]; + mclusy1diff = michely1 - matchclus1[1]; + mclusz1diff = michelz1 - matchclus1[2]; + michvtx_x1diff = michelx1 - vtx_x; + michvtx_y1diff = michely1 - vtx_y; + michvtx_z1diff = michelz1 - vtx_z; + // std::cout << "3D point for Endpoint 1 Clusters is (x, y, z) " << + // matchclus1[0] << " , " << matchclus1[1] << " , " << matchclus1[2] << + // std::endl; + } + double clusx2diff = 9999.; + double clusy2diff = 9999.; + double clusz2diff = 9999.; + + double mclusx2diff = 9999.; + double mclusy2diff = 9999.; + double mclusz2diff = 9999.; + + if (!matchclus2.empty()) { + clusx2diff = vtx_x - matchclus2[0]; + clusy2diff = vtx_y - matchclus2[1]; + clusz2diff = vtx_z - matchclus2[2]; + mclusx2diff = michelx2 - matchclus2[0]; + mclusy2diff = michely2 - matchclus2[1]; + mclusz2diff = michelz2 - matchclus2[2]; + michvtx_x2diff = michelx2 - vtx_x; + michvtx_y2diff = michely2 - vtx_y; + michvtx_z2diff = michelz2 - vtx_z; + // std::cout << "3D point for Endpoint 2 Clusters is (x, y, z) " << + // matchclus2[0] << " , " << matchclus2[1] << " , " << matchclus2[2] << + // std::endl; + } + /// 2 types of 3D distance calculations for Cluster matching: + // 1. Cluster to Vertex + double dist1 = + sqrt(pow(clusx1diff, 2) + pow(clusy1diff, 2) + pow(clusz1diff, 2)); + double dist2 = + sqrt(pow(clusx2diff, 2) + pow(clusy2diff, 2) + pow(clusz2diff, 2)); + // 2. Cluster to Michel + double mdist1 = + sqrt(pow(mclusx1diff, 2) + pow(mclusy1diff, 2) + pow(mclusz1diff, 2)); + double mdist2 = + sqrt(pow(mclusx2diff, 2) + pow(mclusy2diff, 2) + pow(mclusz2diff, 2)); + // std::cout << " The michel endpoint 1 - cluster 3D point distance is " << + // mdist1 << std::endl; std::cout << " The michel endpoint 2 - cluster 3D + // point distance is " << mdist2 << std::endl; Saving all the distances to the + // Michel member data + this->down_clus_michel_dist3D = mdist2; + this->up_clus_michel_dist3D = mdist1; + this->up_to_cluster_dist3D = dist1; + this->down_to_cluster_dist3D = dist2; + // std::cout << "Printing 3D distances to vertex for cluster matches " << + // dist1 << " and " << dist2 << std::endl; std::cout << "Printing 3D distances + // to michel for cluster matches " << mdist1 << " and " << mdist2 << + // std::endl; + double michdist1 = sqrt(pow(michvtx_x1diff, 2) + pow(michvtx_y1diff, 2) + + pow(michvtx_z1diff, 2)); + double michdist2 = sqrt(pow(michvtx_x2diff, 2) + pow(michvtx_y2diff, 2) + + pow(michvtx_z2diff, 2)); + this->up_clus_michvtx_dist3D = michdist1; + this->down_clus_michvtx_dist3D = michdist2; + // Marks which endpoint is the closest match for this match type + if (dist1 < dist2) + this->clus_endpoint = 1; + else if (dist1 > dist2) + this->clus_endpoint = 2; + } + + struct MichelEvent { + int m_idx = -1; // Index for Best Michel in nmichels + double m_bestdist = 9999.; // in mm + std::vector m_best2D; // 0: XZ, 1: UZ, 2:VZ + double m_best_XZ = 9999.; + double m_best_UZ = 9999.; + double m_best_VZ = 9999.; + int m_matchtype; // 0 NULL 1 UPVTX 2 DOWNVTX 3 UPCLUS 4 DOWNCLUS + std::vector m_nmichels; // nmatched michels + std::vector m_ntruepiparents; // michels with true pion parent + std::vector + m_nmichelspass; // if some distance cut is applied, we can store the + // michels that passed for this event in here + double best_x = 9999.; + double best_y = 9999.; + double best_z = 9999.; + double b_truex = 9999.; + double b_truey = 9999.; + double b_truez = 9999.; + int bestparentpdg = -1; + int bestparenttrackid = -1; + int eventtype = + 0; // 0 = null, 1 = only 1 pi+ and no other pion, 2= npi+ and other pion, + // 3 = npi0 and no other pion, 4 = kaons in event, 5 = other + double lowTpi = 9999.; + }; + + // Create Michel objects for each Michel candidate. Add the passing ones to + // the MichelEvent container. + MichelEvent GetQualityMichels(const CVUniverse& univ) { + MichelEvent evt{}; + //========================================================================== + // First: Create a Michel object from each candidate and add them to the + // MichelEvent container. + //========================================================================== + int nmichels = univ.GetNMichels(); + for (int i = 0; i < nmichels; ++i) { + Michel current_michel = Michel(univ, i); + if (current_michel.true_parentpdg == 211) + evt.m_ntruepiparents.push_back(current_michel); + double dist = current_michel.Best3Ddist; // getting the minimum pion range (vertex to Michel/Clus distance) + if (dist <= evt.m_bestdist) { + evt.m_bestdist = dist; + evt.m_idx = i; + + evt.m_best_XZ = current_michel.best_XZ; + evt.m_best_UZ = current_michel.best_UZ; + evt.m_best_VZ = current_michel.best_VZ; + evt.m_matchtype = current_michel.BestMatch; + int bmatch = current_michel.BestMatch; + if (bmatch == 1 || bmatch == 3) { + evt.best_x = current_michel.m_x1; + evt.best_y = current_michel.m_y1; + evt.best_z = current_michel.m_z1; + } else if (bmatch == 2 || bmatch == 4) { + evt.best_x = current_michel.m_x2; + evt.best_y = current_michel.m_y2; + evt.best_z = current_michel.m_z2; + } + evt.b_truex = current_michel.true_initialx; + evt.b_truey = current_michel.true_initialy; + evt.b_truez = current_michel.true_initialz; + } + evt.m_nmichels.push_back(current_michel); + } + + double lowtpiinevent = univ.GetTrueTpi(); + evt.lowTpi = lowtpiinevent; + //========================================================================== + + //========================================================================== + // Second: remove Michels from the MichelEvent container that fail (and + // fill-in more info about the passing Michels). + //========================================================================== + std::vector nmichelspass; + const double m_maxDistance = 150; // Maximum distance from the vertex that + // the best Michel can have in mm + for (unsigned int i = 0; i < evt.m_nmichels.size(); i++) { + // For Vertex Match Check to see if 2D distance cut will + double upvtxXZ = evt.m_nmichels[i].up_to_vertex_XZ; + double downvtxXZ = evt.m_nmichels[i].down_to_vertex_XZ; + double upvtxUZ = evt.m_nmichels[i].up_to_vertex_UZ; + double downvtxUZ = evt.m_nmichels[i].down_to_vertex_UZ; + double upvtxVZ = evt.m_nmichels[i].up_to_vertex_VZ; + double downvtxVZ = evt.m_nmichels[i].down_to_vertex_VZ; + + if (upvtxXZ < m_maxDistance && + (upvtxUZ < m_maxDistance || upvtxVZ < m_maxDistance)) + evt.m_nmichels[i].passable_matchtype.at(0) = 1; + else if (upvtxUZ < m_maxDistance && + (upvtxXZ < m_maxDistance || upvtxVZ < m_maxDistance)) + evt.m_nmichels[i].passable_matchtype.at(0) = 1; + else if (upvtxVZ < m_maxDistance && + (upvtxXZ < m_maxDistance || upvtxUZ < m_maxDistance)) + evt.m_nmichels[i].passable_matchtype.at(0) = 1; + else + evt.m_nmichels[i].passable_matchtype.at(0) = -1; + + if (downvtxXZ < m_maxDistance && + (downvtxUZ < m_maxDistance || downvtxVZ < m_maxDistance)) + evt.m_nmichels[i].passable_matchtype.at(1) = 2; + else if (downvtxUZ < m_maxDistance && + (downvtxXZ < m_maxDistance || downvtxVZ < m_maxDistance)) + evt.m_nmichels[i].passable_matchtype.at(1) = 2; + else if (downvtxVZ < m_maxDistance && + (downvtxXZ < m_maxDistance || downvtxUZ < m_maxDistance)) + evt.m_nmichels[i].passable_matchtype.at(1) = 2; + else + evt.m_nmichels[i].passable_matchtype.at(1) = -1; + + double upclusXZ = evt.m_nmichels[i].up_to_clus_XZ; + double upclusUZ = evt.m_nmichels[i].up_to_clus_UZ; + double upclusVZ = evt.m_nmichels[i].up_to_clus_VZ; + double downclusXZ = evt.m_nmichels[i].down_to_clus_XZ; + double downclusUZ = evt.m_nmichels[i].down_to_clus_UZ; + double downclusVZ = evt.m_nmichels[i].down_to_clus_VZ; + + if (upclusXZ < m_maxDistance && + (upclusUZ < m_maxDistance || upclusVZ < m_maxDistance)) + evt.m_nmichels[i].passable_matchtype.at(2) = 3; + else if (upclusUZ < m_maxDistance && + (upclusXZ < m_maxDistance || upclusVZ < m_maxDistance)) + evt.m_nmichels[i].passable_matchtype.at(2) = 3; + else if (upclusVZ < m_maxDistance && + (upclusXZ < m_maxDistance || upclusUZ < m_maxDistance)) + evt.m_nmichels[i].passable_matchtype.at(2) = 3; + else + evt.m_nmichels[i].passable_matchtype.at(2) = -1; + + if (downclusXZ < m_maxDistance && + (downclusUZ < m_maxDistance || downclusVZ < m_maxDistance)) + evt.m_nmichels[i].passable_matchtype.at(3) = 4; + else if (downclusUZ < m_maxDistance && + (downclusXZ < m_maxDistance || downclusVZ < m_maxDistance)) + evt.m_nmichels[i].passable_matchtype.at(3) = 4; + else if (downclusVZ < m_maxDistance && + (downclusXZ < m_maxDistance || downclusUZ < m_maxDistance)) + evt.m_nmichels[i].passable_matchtype.at(3) = 4; + else + evt.m_nmichels[i].passable_matchtype.at(3) = -1; + + std::vector distances3D; + + if (evt.m_nmichels[i].passable_matchtype[0] == 1) + distances3D.push_back(evt.m_nmichels[i].up_to_vertex_dist3D); + if (evt.m_nmichels[i].passable_matchtype[1] == 2) + distances3D.push_back(evt.m_nmichels[i].down_to_vertex_dist3D); + if (evt.m_nmichels[i].passable_matchtype[2] == 3) + distances3D.push_back(evt.m_nmichels[i].up_clus_michel_dist3D); + if (evt.m_nmichels[i].passable_matchtype[3] == 4) + distances3D.push_back(evt.m_nmichels[i].down_clus_michel_dist3D); + if (distances3D.empty()) distances3D = {9999., 9999., 9999., 9999.}; + if (distances3D[0] == 9999.) + continue; // Comment this line out if you are making efficiency plots + + std::sort(distances3D.begin(), distances3D.end()); + + if (distances3D[0] == evt.m_nmichels[i].up_to_vertex_dist3D) { + evt.m_nmichels[i].best_XZ = evt.m_nmichels[i].up_to_vertex_XZ; + evt.m_nmichels[i].best_UZ = evt.m_nmichels[i].up_to_vertex_UZ; + evt.m_nmichels[i].best_VZ = evt.m_nmichels[i].up_to_vertex_VZ; + evt.m_nmichels[i].BestMatch = 1; + evt.m_nmichels[i].Best3Ddist = evt.m_nmichels[i].up_to_vertex_dist3D; + + } else if (distances3D[0] == evt.m_nmichels[i].down_to_vertex_dist3D) { + evt.m_nmichels[i].BestMatch = 2; + evt.m_nmichels[i].best_XZ = evt.m_nmichels[i].down_to_vertex_XZ; + evt.m_nmichels[i].best_UZ = evt.m_nmichels[i].down_to_vertex_UZ; + evt.m_nmichels[i].best_VZ = evt.m_nmichels[i].down_to_vertex_VZ; + evt.m_nmichels[i].Best3Ddist = evt.m_nmichels[i].down_to_vertex_dist3D; + // std::cout << "This Michel is DOWNVTX and has true endpoint " << + // evt.m_nmichels[i].trueEndpoint << std::endl; + + } else if (distances3D[0] == evt.m_nmichels[i].up_clus_michel_dist3D) { + evt.m_nmichels[i].BestMatch = 3; + evt.m_nmichels[i].best_XZ = evt.m_nmichels[i].up_to_clus_XZ; + evt.m_nmichels[i].best_UZ = evt.m_nmichels[i].up_to_clus_VZ; + evt.m_nmichels[i].best_VZ = evt.m_nmichels[i].up_to_clus_UZ; + evt.m_nmichels[i].Best3Ddist = evt.m_nmichels[i].up_clus_michvtx_dist3D; + // std::cout << "This Michel is UPCLUS and has true endpoint " << + // evt.m_nmichels[i].trueEndpoint << std::endl; + + } else if (distances3D[0] == evt.m_nmichels[i].down_clus_michel_dist3D) { + evt.m_nmichels[i].BestMatch = 4; + evt.m_nmichels[i].Best3Ddist = + evt.m_nmichels[i].down_clus_michvtx_dist3D; + evt.m_nmichels[i].best_XZ = evt.m_nmichels[i].down_to_clus_XZ; + evt.m_nmichels[i].best_UZ = evt.m_nmichels[i].down_to_clus_UZ; + evt.m_nmichels[i].best_VZ = evt.m_nmichels[i].down_to_clus_VZ; + // std::cout << "This Michel is DOWNCLUS and has true endpoint " << + // evt.m_nmichels[i].trueEndpoint << std::endl; + + } else { + evt.m_nmichels[i].BestMatch = 0; + evt.m_nmichels[i].Best3Ddist = 9999.; + evt.m_nmichels[i].best_XZ = 9999.; + evt.m_nmichels[i].best_UZ = 9999.; + evt.m_nmichels[i].best_VZ = 9999.; + continue; + } + + nmichelspass.push_back(evt.m_nmichels[i]); + } + + evt.m_nmichels.clear(); // empty existing vector of Michels + evt.m_nmichels = nmichelspass; // replace vector of michels with the vector + // of michels that passed the above cut + //========================================================================== + + + return evt; + } + +} // namespace vertex -bool IsQualityMatchedMichel_OneView(double ov_dist, double ov_cut) { - if (ov_dist >= 0 && ov_dist * (2.0 / 3) < ov_cut) - return true; - else - return false; -} #endif From 81e05837697b454c6d747957efe242b48a51bb8d Mon Sep 17 00:00:00 2001 From: Ben Messerly Date: Wed, 12 Jan 2022 21:25:53 -0600 Subject: [PATCH 07/17] Mehreen's michels need some CVU functions. They probably overlap with some of my functions or some standard functions. Should consolidate them. --- includes/CVUniverse.h | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/includes/CVUniverse.h b/includes/CVUniverse.h index 6519df3..23fed66 100644 --- a/includes/CVUniverse.h +++ b/includes/CVUniverse.h @@ -206,6 +206,44 @@ class CVUniverse : public PlotUtils::MinervaUniverse { double Calct(const double epi, const double emu, const double pzpi, const double pzmu, const double pxpi, const double pxmu, const double pypi, const double pymu) const; + + // Mehreen + virtual double GetTpiMehreen() const { return 0; } + ROOT::Math::XYZTVector GetVertex() const + { + ROOT::Math::XYZTVector result; + result.SetCoordinates(GetVec("vtx").data()); + return result; + } + virtual double thetaWRTBeam(double x, double y, double z) const { + double pyp = -1.0 *sin( MinervaUnits::numi_beam_angle_rad)*z + cos( MinervaUnits::numi_beam_angle_rad )*y; + double pzp = cos( MinervaUnits::numi_beam_angle_rad )*z + sin( MinervaUnits::numi_beam_angle_rad )*y; + double denom2 = pow(x,2) + pow(pyp,2) + pow(pzp,2); + if( 0. == denom2 ) return -9999.; + else return acos(pzp / sqrt(denom2) ); + } + virtual int GetNMichels() const{ + return GetInt("FittedMichel_michel_fitPass_sz"); + } + virtual int GetNTruePions() const{ + return GetInt("FittedMichel_all_piontrajectory_trackID_sz"); + } + virtual double GetTrueTpi() const { + int nFSpi = GetNTruePions(); + double pionKE = 9999.; + for (int i = 0; i < nFSpi; i++){ + int pdg = GetVecElem("FittedMichel_all_piontrajectory_pdg", i); + int pitrackid = GetVecElem("FittedMichel_all_piontrajectory_ParentID", i); + + double energy = GetVecElem("FittedMichel_all_piontrajectory_energy",i); + double p = GetVecElem("FittedMichel_all_piontrajectory_momentum", i); + double mass = sqrt(pow(energy,2) - pow(p, 2)); + double tpi = energy - mass; + if (tpi <= pionKE) pionKE = tpi; + } + + return pionKE; + } }; #endif // CVUniverse_H From 106202bf977511c9e99719c5797840379c6bb0bb Mon Sep 17 00:00:00 2001 From: Ben Messerly Date: Wed, 12 Jan 2022 21:26:53 -0600 Subject: [PATCH 08/17] Going to save michel processing to CCPiEvent. I don't think we'll need to save it to CVU, but maybe. --- includes/CCPiEvent.cxx | 10 ++++++---- includes/CCPiEvent.h | 5 +++++ 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/includes/CCPiEvent.cxx b/includes/CCPiEvent.cxx index 2922509..27a81af 100644 --- a/includes/CCPiEvent.cxx +++ b/includes/CCPiEvent.cxx @@ -26,6 +26,8 @@ CCPiEvent::CCPiEvent(const bool is_mc, const bool is_truth, m_w_type = is_mc ? GetWSidebandType(*universe, signal_definition, sidebands::kNWFitCategories) : kNWSidebandTypes; + m_endpoint_michels = endpoint::GetQualityMichels(*universe); + m_vertex_michels = vertex::GetQualityMichels(*universe); } //============================================================================== @@ -326,7 +328,7 @@ void ccpi_event::FillCounters( const std::pair& counters) { EventCount* signal = counters.first; EventCount* bg = event.m_is_mc ? counters.second : nullptr; - MichelMap dummy1, dummy2; + endpoint::MichelMap dummy1, dummy2; bool pass = true; // Purity and efficiency for (auto i_cut : kCutsVector) { @@ -357,10 +359,10 @@ void ccpi_event::FillCutVars(CCPiEvent& event, if (universe->ShortName() != "cv") return; - MichelMap endpoint_michels; + endpoint::MichelMap endpoint_michels; endpoint_michels.clear(); - MichelMap vertex_mich; + endpoint::MichelMap vertex_mich; vertex_mich.clear(); // loop cuts @@ -409,7 +411,7 @@ void ccpi_event::FillCutVars(CCPiEvent& event, } // N michels if (next_cut == kAtLeastOneMichel && HasVar(variables, "michel_count")) { - double fill_val = GetQualityMichels(*universe).size(); + double fill_val = endpoint::GetQualityMichels(*universe).size(); FillStackedHists(event, GetVar(variables, "michel_count"), fill_val); // if (fill_val == 0 && event.m_is_signal) // universe->PrintArachneLink(); diff --git a/includes/CCPiEvent.h b/includes/CCPiEvent.h index bc1cae6..17cfb50 100644 --- a/includes/CCPiEvent.h +++ b/includes/CCPiEvent.h @@ -3,6 +3,7 @@ #include "CVUniverse.h" #include "Constants.h" // typedef RecoPionIdx, EventCount +#include "Michel.h" #include "SignalDefinition.h" #include "TruthCategories/Sidebands.h" // WSidebandType #include "TruthCategories/SignalBackground.h" // SignalBackgroundType @@ -42,10 +43,14 @@ struct CCPiEvent { double m_weight; WSidebandType m_w_type; + endpoint::MichelMap m_endpoint_michels; + vertex::MichelEvent m_vertex_michels; + // Fixed (directly) outside of constructor -- with time-intensive functions bool m_passes_cuts; // PassesCuts bool m_is_w_sideband; // IsWSideband RecoPionIdx m_highest_energy_pion_idx; // GetHighestEnergyPionCandidateIndex + }; // Helper Functions From 59c692ae49999114bbd7b106bb18f755667805de Mon Sep 17 00:00:00 2001 From: Ben Messerly Date: Wed, 12 Jan 2022 21:27:32 -0600 Subject: [PATCH 09/17] Add Mehreen's CVU and update format her hascut file. Add Mehreen's CVU and update format her hascut file. --- includes/MehreenMichels/CVUniverse.h | 344 ++++++++++++++++++++++++ includes/MehreenMichels/HasMichel_cut.h | 38 +-- 2 files changed, 363 insertions(+), 19 deletions(-) create mode 100644 includes/MehreenMichels/CVUniverse.h diff --git a/includes/MehreenMichels/CVUniverse.h b/includes/MehreenMichels/CVUniverse.h new file mode 100644 index 0000000..2367be2 --- /dev/null +++ b/includes/MehreenMichels/CVUniverse.h @@ -0,0 +1,344 @@ +// ============================================================================= +// Base class for an un-systematically shifted (i.e. CV) universe. Implement +// "Get" functions for all the quantities that you need for your analysis. +// +// This class inherits from PU::MinervaUniverse, which in turn inherits from +// PU::BaseUniverse. PU::BU defines the interface with anatuples. +// +// Within the class, "WeightFunctions" and "MuonFunctions" are included to gain +// access to standardized weight and muon variable getters. See: +// https://cdcvs.fnal.gov/redmine/projects/minerva-sw/wiki/MinervaUniverse_Structure_ +// for a full list of standardized functions you can use. In general, if a +// standard version of a function is available, you should be using it. +// ============================================================================= +#ifndef CVUNIVERSE_H +#define CVUNIVERSE_H + +#include + +#include "PlotUtils/MinervaUniverse.h" + +class CVUniverse : public PlotUtils::MinervaUniverse { + + public: + #include "PlotUtils/MuonFunctions.h" // GetMinosEfficiencyWeight + #include "PlotUtils/TruthFunctions.h" //Getq3True + // ======================================================================== + // Constructor/Destructor + // ======================================================================== + CVUniverse(PlotUtils::ChainWrapper* chw, double nsigma = 0) + : PlotUtils::MinervaUniverse(chw, nsigma) {} + + virtual ~CVUniverse() {} + + // ======================================================================== + // Quantities defined here as constants for the sake of below. Definition + // matched to Dan's CCQENuInclusiveME variables from: + // `/minerva/app/users/drut1186/cmtuser/Minerva_v22r1p1_OrigCCQENuInc/Ana/CCQENu/ana_common/include/CCQENuUtils.h` + // ======================================================================== + static constexpr double M_n = 939.56536; + static constexpr double M_p = 938.272013; + static constexpr double M_nucleon = (1.5*M_n+M_p)/2.5; + + static constexpr int PDG_n = 2112; + static constexpr int PDG_p = 2212; + + // ======================================================================== + // Write a "Get" function for all quantities access by your analysis. + // For composite quantities (e.g. Enu) use a calculator function. + // + // In order to properly calculate muon variables and systematics use the + // various functions defined in MinervaUniverse. + // E.g. GetPmu, GetEmu, etc. + // ======================================================================== + + // Quantities only needed for cuts + // Although unlikely, in principle these quanties could be shifted by a + // systematic. And when they are, they'll only be shifted correctly if we + // write these accessor functions. + double GetTrueQ3() const + { + return Getq3True()/pow(10,3); + } + //Muon kinematics + double GetMuonPT() const //GeV/c + { + return GetPmu()/1000. * sin(GetThetamu()); + } + + double GetMuonPz() const //GeV/c + { + return GetPmu()/1000. * cos(GetThetamu()); + } + + double GetMuonPTTrue() const //GeV/c + { + return GetPlepTrue()/1000. * sin(GetThetalepTrue()); + } + + double GetMuonPzTrue() const //GeV/c + { + return GetPlepTrue()/1000. * cos(GetThetalepTrue()); + } + + double GetEmuGeV() const //GeV + { + return GetEmu()/1000.; + } + + double GetElepTrueGeV() const //GeV + { + return GetElepTrue()/1000.; + } + + int GetInteractionType() const { + return GetInt("mc_intType"); + } + + int GetTargetNucleon() const { + return GetInt("mc_targetNucleon"); + } + + double GetBjorkenXTrue() const { + return GetDouble("mc_Bjorkenx"); + } + + double GetBjorkenYTrue() const { + return GetDouble("mc_Bjorkeny"); + } + + virtual bool IsMinosMatchMuon() const { + return GetInt("has_interaction_vertex") == 1; + } + + ROOT::Math::XYZTVector GetVertex() const + { + ROOT::Math::XYZTVector result; + result.SetCoordinates(GetVec("vtx").data()); + return result; + } + + ROOT::Math::XYZTVector GetTrueVertex() const + { + ROOT::Math::XYZTVector result; + result.SetCoordinates(GetVec("mc_vtx").data()); + return result; + } + + virtual int GetTDead() const { + return GetInt("phys_n_dead_discr_pair_upstream_prim_track_proj"); + } + + //TODO: If there was a spline correcting Eavail, it might not really be Eavail. + // Our energy correction spline, one of at least 2 I know of, corrects q0 + // so that we get the right neutrino energy in an inclusive sample. So, + // this function could be correcting for neutron energy which Eavail should + // not do. + virtual double GetEavail() const { + return GetDouble("recoilE_SplineCorrected"); + } + + virtual double GetQ2Reco() const{ + return GetDouble("qsquared_recoil"); + } + + //GetRecoilE is designed to match the NSF validation suite + virtual double GetRecoilE() const { + return GetVecElem("recoil_summed_energy", 0); + } + + virtual double Getq3() const{ + double eavail = GetEavail()/pow(10,3); + double q2 = GetQ2Reco() / pow(10,6); + double q3mec = sqrt(eavail*eavail + q2); + return q3mec; + } + + virtual int GetCurrent() const { return GetInt("mc_current"); } + + virtual int GetTruthNuPDG() const { return GetInt("mc_incoming"); } + + virtual double GetMuonQP() const { + return GetDouble((GetAnaToolName() + "_minos_trk_qp").c_str()); + } + + //Some functions to match CCQENuInclusive treatment of DIS weighting. Name matches same Dan area as before. + virtual double GetTrueExperimentersQ2() const { + double Enu = GetEnuTrue(); //MeV + double Emu = GetElepTrue(); //MeV + double thetaMu = GetThetalepTrue(); + return 4.0*Enu*Emu*pow(sin(thetaMu/2.0),2.0);//MeV^2 + } + + virtual double CalcTrueExperimentersQ2(double Enu, double Emu, double thetaMu) const{ + return 4.0*Enu*Emu*pow(sin(thetaMu/2.0),2.0);//MeV^2 + } + + virtual double GetTrueExperimentersW() const { + double nuclMass = M_nucleon; + int struckNucl = GetTargetNucleon(); + if (struckNucl == PDG_n){ + nuclMass=M_n; + } + else if (struckNucl == PDG_p){ + nuclMass=M_p; + } + double Enu = GetEnuTrue(); + double Emu = GetElepTrue(); + double thetaMu = GetThetalepTrue(); + double Q2 = CalcTrueExperimentersQ2(Enu, Emu, thetaMu); + return TMath::Sqrt(pow(nuclMass,2) + 2.0*(Enu-Emu)*nuclMass - Q2); + } + + // Adding Branches for Mehreen's Michels + virtual int GetNMichels() const{ + return GetInt("FittedMichel_michel_fitPass_sz"); + } + + virtual std::vector GetTrueFSPDGCodes() const { + std::vector pdgcodes; + int pdgsize = GetInt("mc_nFSPart"); + for (int i = 0; i< pdgsize; i++) + { + int pdg = GetVecElem("mc_FSPartPDG", i); + pdgcodes.push_back(pdg); + } + + return pdgcodes; + } + + virtual int GetTrueNMichels() const { + int ntruemichels = 0; + int nmichels = GetNMichels(); + for (int i =0; i< nmichels; i++) + { + + double datafrac = GetVecElem("FittedMichel_michel_datafraction", i); + int fitted = GetVecElem("FittedMichel_michel_fitPass", i); + if (fitted == 0) continue; + if (datafrac > 0.5) continue; + ntruemichels++; + + } + + return ntruemichels; + } + + virtual int GetTrueNPionsinEvent() const { + int npion = 0; + int pdgsize = GetInt("mc_nFSPart"); + for (int i = 0; i< pdgsize; i++) + { + int pdg = GetVecElem("mc_FSPartPDG", i); + if (pdg == 211) npion++; + } + return npion; + + } + + virtual int GetNTruePions() const{ + return GetInt("FittedMichel_all_piontrajectory_trackID_sz"); + } + + virtual int GetPionParentID(int i) const { + return GetVecElem("FittedMichel_all_piontrajectory_ParentID", i); + } + + virtual int GetPionPDG(int i) const{ + return GetVecElem("FittedMichel_all_piontrajectory_pdg", i); + } + + virtual double GetPionE(int i) const{ + return GetVecElem("FittedMichel_all_piontrajectory_energy",i)/pow(10,3); + } + + virtual double GetPionP(int i) const{ + return GetVecElem("FittedMichel_all_piontrajectory_momentum", i)/pow(10,3); + } + + virtual double GetPionMass(int i) const{ + double pionmass = sqrt(pow(GetPionE(i), 2) - pow(GetPionP(i), 2)); + return pionmass; + } + + virtual double GetPionKE(int i) const{ + double pionKE = GetPionE(i) - GetPionMass(i); + } + + virtual double thetaXWRTBeam(double x, double y, double z) const{ + + double pzp = cos( MinervaUnits::numi_beam_angle_rad)*z + sin( MinervaUnits::numi_beam_angle_rad)*y; + int sign = (x<0.) ? -1 : 1; + double denom2 = pow(x,2) + pow(pzp,2); + if( 0. == denom2 ) return -999.; + else + return sign * acos(pzp / sqrt(denom2)); + } + + virtual double thetaWRTBeam(double x, double y, double z) const{ + double pyp = -1.0 *sin( MinervaUnits::numi_beam_angle_rad)*z + cos( MinervaUnits::numi_beam_angle_rad )*y; + double pzp = cos( MinervaUnits::numi_beam_angle_rad )*z + sin( MinervaUnits::numi_beam_angle_rad )*y; + double denom2 = pow(x,2) + pow(pyp,2) + pow(pzp,2); + if( 0. == denom2 ) return -9999.; + else return acos(pzp / sqrt(denom2) ); + } + + virtual double GetTrueTpi() const { + int nFSpi = GetNTruePions(); + double pionKE = 9999.; + for (int i = 0; i < nFSpi; i++){ + int pdg = GetVecElem("FittedMichel_all_piontrajectory_pdg", i); + int pitrackid = GetVecElem("FittedMichel_all_piontrajectory_ParentID", i); + + double energy = GetVecElem("FittedMichel_all_piontrajectory_energy",i); + double p = GetVecElem("FittedMichel_all_piontrajectory_momentum", i); + double mass = sqrt(pow(energy,2) - pow(p, 2)); + double tpi = energy - mass; + if (tpi <= pionKE) pionKE = tpi; + } + + return pionKE; + } + virtual void PrintArachneLink() const { + int link_size = 200; + char link[link_size]; + int run = GetInt("mc_run"); + int subrun = GetInt("mc_subrun"); + int gate = GetInt("mc_nthEvtInFile") + 1; + int slice = GetVecElem("slice_numbers", 0); + sprintf(link, + "http://minerva05.fnal.gov/Arachne/" + "arachne.html\?det=SIM_minerva&recoVer=v21r1p1&run=%d&subrun=%d&gate=" + "%d&slice=%d", + run, subrun, gate, slice); + // strncpy(); // FAIL + // memcpy(); // FAIL + std::cout << link << std::endl; + } + virtual double GetTrueEAvail() const + { + double Eavail = 0.0; + int pdgsize = GetInt("mc_nFSPart"); + for (int i = 0; i< pdgsize; i++) + { + int pdg = GetVecElem("mc_FSPartPDG", i); + double energy = GetVecElem("mc_FSPartE", i); // hopefully this is in MeV + if (pdg == 211) Eavail+= energy - 139.57; // subtracting pion mass to get Kinetic energy + if (pdg == 2212) Eavail += energy - 938.28; // proton + if (pdg == 111) Eavail += energy; // pi0 + if (pdg == 22) Eavail += energy; // photons + if (pdg == 311) Eavail += energy - 497.611/2.0; // K0 ???? - Kaon rest mass / 2 + if (pdg == 321) Eavail += energy - 497.611/2.0; // Kaon+ Kinetic Energy divide by Kmass/2 + if (pdg == 551) Eavail += energy; //Adding etas + } + + return Eavail; // in MeV + } + + + //Still needed for some systematics to compile, but shouldn't be used for reweighting anymore. + protected: + #include "PlotUtils/WeightFunctions.h" // Get*Weight +}; + +#endif diff --git a/includes/MehreenMichels/HasMichel_cut.h b/includes/MehreenMichels/HasMichel_cut.h index c044a7b..76a159b 100644 --- a/includes/MehreenMichels/HasMichel_cut.h +++ b/includes/MehreenMichels/HasMichel_cut.h @@ -59,27 +59,27 @@ class hasMichel : public PlotUtils::Cut { double lowtpiinevent = univ.GetTrueTpi(); evt.lowTpi = lowtpiinevent; /* - std::vector pdgcodes = univ.GetTrueFSPDGCodes(); - int npiplus = 0; - int npiminus = 0; - int npi0 = 0; - int nkaons = 0; - int nQE = 0; + std::vector pdgcodes = univ.GetTrueFSPDGCodes(); + int npiplus = 0; + int npiminus = 0; + int npi0 = 0; + int nkaons = 0; + int nQE = 0; - for (int j = 0; j < pdgcodes.size(); j++) - { - int pdg = pdgcodes[j]; - if (pdg == 211) npiplus++; - else if (pdg == 111) npi0++; - else if (pdg == 321 || 311) nkaons++; - } + for (int j = 0; j < pdgcodes.size(); j++) + { + int pdg = pdgcodes[j]; + if (pdg == 211) npiplus++; + else if (pdg == 111) npi0++; + else if (pdg == 321 || 311) nkaons++; + } - if (npiplus == 1 && npi0 == 0) evt.eventtype = 1; - else if (npiplus > 0 && npi0 > 0) evt.eventtype = 2; - else if (npiplus == 0 && npi0 > 0) evt.eventtype = 3; - else if (nkaons > 0) evt.eventtype = 4; - else {evt.eventtype = 5;} - //return true; + if (npiplus == 1 && npi0 == 0) evt.eventtype = 1; + else if (npiplus > 0 && npi0 > 0) evt.eventtype = 2; + else if (npiplus == 0 && npi0 > 0) evt.eventtype = 3; + else if (nkaons > 0) evt.eventtype = 4; + else {evt.eventtype = 5;} + //return true; */ return !evt.m_nmichels.empty(); } From b8c0ddbe3f15120e22367337ab92c85c11f214d5 Mon Sep 17 00:00:00 2001 From: Ben Messerly Date: Fri, 4 Mar 2022 13:00:28 -0600 Subject: [PATCH 10/17] Commit of current status. --- ccpion_common.h | 12 ++-- studies/runNewMichels.C | 128 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 134 insertions(+), 6 deletions(-) create mode 100644 studies/runNewMichels.C diff --git a/ccpion_common.h b/ccpion_common.h index 14a9417..e695e17 100644 --- a/ccpion_common.h +++ b/ccpion_common.h @@ -8,19 +8,19 @@ std::string GetPlaylistFile(std::string plist, bool is_mc, bool use_xrootd = true) { - // small sample with michel branches // /pnfs/minerva/persistent/users/granados/MADtuplas/merged/mc/Michel/ME1A/MasterAnaDev_mc_AnaTuple_run00110000_Playlist.root // /pnfs/minerva/persistent/users/granados/MADtuplas/merged/data/Michel/ + // /pnfs/minerva/persistent/users/granados/MADtuplas/merged/20220101/mc/ME1A/ + // /pnfs/minerva/persistent/users/granados/MADtuplas/merged/data/Michel/ME1A/ + // /minerva/data/users/bmesserl/MECCCHpip_ana_plists/20220101/ // const std::string processing_date = "20200713"; // new short tracking branches - const std::string processing_date = "20211115"; // new recoil energy branches - // const std::string processing_date = "test"; // For test with small MAD tuplas + // const std::string processing_date = "20211012"; // new recoil energy branches + const std::string processing_date = "20220101"; // includes mehreen's michel branches const std::string is_mc_str = is_mc ? "mc" : "data"; std::transform(plist.begin(), plist.end(), plist.begin(), ::toupper); - std::string topdir = - is_mc ? "/minerva/data/users/granados/MAD_ana_plists/" - : "/minerva/data/users/granados/MAD_ana_plists/"; // correct merging method + std::string topdir = "/minerva/data/users/bmesserl/MECCCHpip_ana_plists/"; topdir += processing_date; std::string playlist_file = use_xrootd ? Form("%s/%s_%s_xrootd_plist.txt", topdir.c_str(), diff --git a/studies/runNewMichels.C b/studies/runNewMichels.C new file mode 100644 index 0000000..4350bb1 --- /dev/null +++ b/studies/runNewMichels.C @@ -0,0 +1,128 @@ +//============================================================================== +// Template for a generic loop data/mc and plot script. +// Assume that NO systematics are being analyzed. +// Good for stacked histograms, branch validation, residuals, etc. +//============================================================================== +#include +#include + +#include "ccpion_common.h" // GetPlaylistFile +#include "includes/Binning.h" +#include "includes/CCPiEvent.h" +#include "includes/CVUniverse.h" +#include "includes/MacroUtil.h" +#include "includes/HadronVariable.h" +#include "includes/Variable.h" +#include "plotting_functions.h" + +#include "includes/common_functions.h" + +// Forward declare my variables because we're hiding the header. +class Variable; +class HadronVariable; + +namespace run_new_michels{ +//============================================================================== +// Do some event processing (e.g. make cuts, get best pion) and fill hists +//============================================================================== +void FillVars(CCPiEvent& event, const std::vector& variables) { + const CVUniverse* universe = event.m_universe; + const double wgt = event.m_weight; + const bool is_mc = event.m_is_mc; + const SignalDefinition sd = event.m_signal_definition; + + if (universe->ShortName() != "cv") return; + + event.m_passes_cuts = PassesCuts(event, event.m_is_w_sideband); + event.m_highest_energy_pion_idx = GetHighestEnergyPionCandidateIndex(event); + if(event.m_passes_cuts) + ccpi_event::FillStackedHists(event, variables); +} + +//============================================================================== +// Get Variables +//============================================================================== +std::vector GetVariables() { + typedef Variable Var; + typedef HadronVariable HVar; + HVar* thetapi_deg = new HVar("thetapi_deg", "#theta_{#pi}", "deg", CCPi::GetBinning("thetapi_deg"), &CVUniverse::GetThetapiDeg); + Var* pmu = new Var("pmu", "p_{#mu}", "MeV", CCPi::GetBinning("pmu"), &CVUniverse::GetPmu); + std::vector variables = {thetapi_deg, pmu}; + return variables; +} +} // namespace run_new_michels + +//============================================================================== +// Loop and Fill +//============================================================================== +void LoopAndFill(const CCPi::MacroUtil& util, CVUniverse* universe, + const EDataMCTruth& type, + std::vector& variables) { + + std::cout << "Loop and Fill CutVars\n"; + bool is_mc, is_truth; + Long64_t n_entries; + SetupLoop(type, util, is_mc, is_truth, n_entries); + + for(Long64_t i_event=0; i_event < n_entries; ++i_event){ + if (i_event%500000==0) std::cout << (i_event/1000) << "k " << std::endl; + universe->SetEntry(i_event); + + // For mc, get weight, check signal, and sideband + CCPiEvent event(is_mc, is_truth, util.m_signal_definition, universe); + + // WRITE THE FILL FUNCTION + run_new_michels::FillVars(event, variables); + } // events + std::cout << "*** Done ***\n\n"; +} + +//============================================================================== +// Main +//============================================================================== +void runNewMichels(std::string plist = "ME1A") { + //========================================= + // Input tuples + //========================================= + bool is_mc = true; + std::string mc_file_list, data_file_list; + mc_file_list = GetPlaylistFile(plist, is_mc); + is_mc = false; + data_file_list = GetPlaylistFile(plist, is_mc); + + //========================================= + // Init macro utility + //========================================= + const int signal_definition_int = 0; const std::string macro("runNewMichels"); + const bool is_grid = false; + const bool do_truth = false; + const bool do_systematics = false; + + CCPi::MacroUtil util(signal_definition_int, mc_file_list, data_file_list, plist, do_truth, is_grid, do_systematics); + util.PrintMacroConfiguration(macro); + +/* + //========================================= + // Get variables and initialize their hists + //========================================= + std::vector variables = run_study_template::GetVariables(); + for (auto v : variables) + v->InitializeAllHists(util.m_error_bands, util.m_error_bands_truth); + + //========================================= + // Loop and Fill + //========================================= + LoopAndFill(util, util.m_data_universe, kData, variables); + LoopAndFill(util, util.m_error_bands.at("cv").at(0), kMC, variables); + + for (auto v : variables) { + std::string tag = v->Name(); + double ymax = -1; + bool do_bwn = true; + std::cout << "Plotting" << std::endl; + PlotCutVar(v, v->m_hists.m_selection_data, v->GetStackArray(kS), util.m_data_pot, util.m_mc_pot, util.m_signal_definition, v->Name(),"SSB", ymax, do_bwn); + } + + std::cout << "Success" << std::endl; +*/ +} From b8dcbfa41c3fe07837885cb2ef3831b0a110a924 Mon Sep 17 00:00:00 2001 From: Ben Messerly Date: Wed, 9 Mar 2022 16:31:27 -0600 Subject: [PATCH 11/17] Commit of current status. Probably broken now. Cleared out Cuts.* files. Moved unused cuts to RetiredCuts, which probably doesn't compile, but can be used for reference or to easily revive old cuts. Moved most helper functions to CutUtils, which is compiled and doesn't have many dependencies. Lastly, attempting to use the new PassesCuts function which returns all returns by value, not by reference. --- includes/CCPiEvent.cxx | 4 + includes/CCPiEvent.h | 3 +- includes/CutUtils.h | 159 +++++++++++ includes/Cuts.cxx | 614 +++++++++++----------------------------- includes/Cuts.h | 122 +++----- includes/RetiredCuts.h | 149 ++++++++++ loadLibs.C | 1 + studies/runNewMichels.C | 22 +- 8 files changed, 528 insertions(+), 546 deletions(-) create mode 100644 includes/CutUtils.h create mode 100644 includes/RetiredCuts.h diff --git a/includes/CCPiEvent.cxx b/includes/CCPiEvent.cxx index 27a81af..fcce52d 100644 --- a/includes/CCPiEvent.cxx +++ b/includes/CCPiEvent.cxx @@ -33,6 +33,10 @@ CCPiEvent::CCPiEvent(const bool is_mc, const bool is_truth, //============================================================================== // Helper Functions //============================================================================== +std::tuple> PassesCuts(CCPiEvent& e) { + return PassesCuts(*e.m_universe, e.m_is_mc, e.m_signal_definition); +} + // Used in analysis pipeline bool PassesCuts(CCPiEvent& e, bool& is_w_sideband) { return PassesCuts(*e.m_universe, e.m_reco_pion_candidate_idxs, e.m_is_mc, diff --git a/includes/CCPiEvent.h b/includes/CCPiEvent.h index 17cfb50..190cf41 100644 --- a/includes/CCPiEvent.h +++ b/includes/CCPiEvent.h @@ -56,7 +56,8 @@ struct CCPiEvent { // Helper Functions // bool IsWSideband(CCPiEvent&); bool PassesCuts(CCPiEvent&, bool& is_w_sideband); -bool PassesCuts(CCPiEvent& e, std::vector cuts = kCutsVector); +bool PassesCuts(CCPiEvent&, std::vector); +std::tuple> PassesCuts(CCPiEvent&); RecoPionIdx GetHighestEnergyPionCandidateIndex(const CCPiEvent&); SignalBackgroundType GetSignalBackgroundType(const CCPiEvent&); diff --git a/includes/CutUtils.h b/includes/CutUtils.h new file mode 100644 index 0000000..08bb10c --- /dev/null +++ b/includes/CutUtils.h @@ -0,0 +1,159 @@ +#ifndef CutUtils_h +#define CutUtils_h + +#include "Constants.h" // enum ECuts, CCNuPionIncConsts +#include "Michel.h" // endpoint::MichelMap + +// Analysis Cuts - default vector +const std::vector kCutsVector = { + kNoCuts, + kPrecuts, + kVtx, + kMinosMuon, + kAtLeastOnePionCandidateTrack, + kAtLeastOneMichel, + kLLR, + kNode, + kWexp, // Calling this after pion candidate determined! + kIsoProngs, + kPionMult, + kPmu}; + +// Remove W cut from cuts vector +const std::vector GetWSidebandCuts() { + std::vector w_sideband_cuts = kCutsVector; + w_sideband_cuts.erase( + std::remove(w_sideband_cuts.begin(), w_sideband_cuts.end(), kWexp), + w_sideband_cuts.end()); + return w_sideband_cuts; +} + +// Gaudi tool cuts - only work when checking truth tuple +bool IsPrecut(ECuts c) { + if (c == kNoCuts || c == kGoodObjects || c == kGoodVertex || + c == kFiducialVolume || c == kMinosActivity || c == kPrecuts) + return true; + else + return false; +} + +// Cut Names +std::string GetCutName(ECuts cut) { + switch (cut) { + case kNoCuts: + return "No Cuts"; + + case kGoodObjects: + return "Good Objects"; + + case kGoodVertex: + return "Good Vertex"; + + case kFiducialVolume: + return "Fiducial Volume"; + + case kMinosActivity: + return "MINOS Activity"; + + case kPrecuts: + return "Anatool Precuts"; + + case kVtx: + return "vertex position Cut"; + + case kMinosMatch: + return "MINOS Muon"; + + case kMinosCharge: + return "MINOS Charge"; + + case kMinosCoil: + return "MINOS Coil"; + + case kMinosMuon: + return "MINOS Muon"; + + case kThetaMu: + return "Muon Angle"; + + case kDeadTime: + return "Dead Time"; + + case kWexp: + return "$W_{experimental}$"; + + case kIsoBlobs: + return "$<$1 Isolated Blobs"; + + case kIsoProngs: + return "$<$2 Isolated Prongs"; + + case kIsoProngSep: + return "Iso Prong Sep $<$ 300"; + + case kNProngs: + return "Max 2 Hadr Prongs"; + + case kNPionCandidates: + return "$\\pi$ candidate"; + + case kPionCandidateQuality: + return "Quality $\\pi$ candidate"; + + case kAtLeastOneMichel: + return "$>$= 1 Michel"; + + case kAtLeastOneBrandonMichel: + return "$>$= 1 Brandon Michel"; + + case kAtLeastOneAnchoredProng: + return "$>$= 1 Anchored Prong"; + + case kAtLeastOnePionCandidateTrack: + return "$>$= 1 Hadron Track"; + + case kAtLeastOneLLRCandidate: + return "$>$= 1 Track Pass LLR Cut"; + + case kAtLeastOneNodeCandidate: + return "$>$= 1 Track Pass Node Cut"; + + case kExactlyOneEndpointMichel: + return "== 1 Michel"; + + case kNode: + return "Node"; + + case kPionMult: + return "Pion Multiplicity"; + + case kOldMichel: + return "Old Michel Cut"; + + case kdEdx: + return "dEdx PID"; + + case kLLR: + return "LLR PID"; + + case kAllCuts: + return "Total"; + + case kPmu: + return "1.5 GeV $<$ Pmu $<$ 20 GeV"; + + default: + std::cout << "ERROR: GetCutName unknown cut!" << std::endl; + return ""; + }; +} + +// Get pion candidate indexes from michel map +// (our cuts strategy enforces a 1-1 michel-pion candidate match) +std::vector GetHadIdxsFromMichels(endpoint::MichelMap michels) { + std::vector ret; + for (auto m : michels) ret.push_back(m.second.had_idx); + return ret; +} + +#endif diff --git a/includes/Cuts.cxx b/includes/Cuts.cxx index 7abb88c..74a18e8 100644 --- a/includes/Cuts.cxx +++ b/includes/Cuts.cxx @@ -2,85 +2,17 @@ #define Cuts_cxx #include "Cuts.h" - #include "TruthCategories/Sidebands.h" // sidebands::kSidebandCutVal #include "utilities.h" // ContainerEraser //============================================================================== -// Helpers -//============================================================================== -// Get the pion candidate indexes from a map of michels -std::vector GetHadIdxsFromMichels(endpoint::MichelMap michels) { - std::vector ret; - for (auto m : michels) ret.push_back(m.second.had_idx); - return ret; -} - -//============================================================================== -// Passes ALL Cuts +// Generic Pass Cut(s) Functions +// * passes, is_sideband, pion_indices = PassesCuts() +// * bool PassesCut(cut) +// * PassedCuts <-- just an event counter //============================================================================== -// cut-by-cut, we fill endpoint_michels and vertex_michels. -// if a track fails a cut, we remove the track's michel from the lists. -// then at the end, return the track indices. -bool PassesCuts(CVUniverse& univ, std::vector& pion_candidate_idxs, - bool is_mc, SignalDefinition signal_definition, - std::vector cuts) { - pion_candidate_idxs.clear(); - static endpoint::MichelMap endpoint_michels; - static endpoint::MichelMap - vertex_michels; // Keep track of these, but not used currently - endpoint_michels.clear(); - vertex_michels.clear(); - bool pass = true; - for (auto c : cuts) { - univ.SetPionCandidates(GetHadIdxsFromMichels( - endpoint_michels)); // Set the pion candidates to the universe - pass = pass && PassesCut(univ, c, is_mc, signal_definition, - endpoint_michels, vertex_michels); - } - - // Each endpoint michel has an associated hadron track. - // Our official pion candidates are those tracks. - pion_candidate_idxs = GetHadIdxsFromMichels(endpoint_michels); - - return pass; -} - -// Check all cuts AND whether we are W sideband. Save a lot of time. -// Strategy is: -// 1. check all cuts except for W -// 2. check if W > 1.5 (sideband) -// 3. check if W < 1.4 (signal) -bool PassesCuts(CVUniverse& universe, std::vector& pion_candidate_idxs, - const bool is_mc, SignalDefinition signal_definition, - bool& is_w_sideband, std::vector cuts) { - // is the W cut even in the cuts vector provided? - bool do_w_cut = std::find(cuts.begin(), cuts.end(), kWexp) != cuts.end(); - - // either way, attempt to remove it - std::vector w_sideband_cuts = kCutsVector; - w_sideband_cuts.erase( - std::remove(w_sideband_cuts.begin(), w_sideband_cuts.end(), kWexp), - w_sideband_cuts.end()); - - // check passes all but w cut - bool passes_all_but_w_cut = PassesCuts(universe, pion_candidate_idxs, is_mc, - signal_definition, w_sideband_cuts); - - // is w sideband = all cuts but W && W > 1.5 - is_w_sideband = passes_all_but_w_cut && - (universe.GetWexp() >= sidebands::kSidebandCutVal); - - // Finally check all cuts == all cuts but W && W - bool passes_all_cuts = passes_all_but_w_cut; - if (do_w_cut) - passes_all_cuts = - passes_all_but_w_cut && WexpCut(universe, signal_definition); - - return passes_all_cuts; -} - -// NEW! Return passes_all_cuts, is_w_sideband, and pion_candidate_indices +// Passes All Cuts v3 (latest and greatest) +// return tuple {passes_all_cuts, is_w_sideband, pion_candidate_idxs} std::tuple> PassesCuts( CVUniverse& universe, const bool is_mc, const SignalDefinition signal_definition, std::vector cuts) { @@ -129,9 +61,64 @@ std::tuple> PassesCuts( return {passes_all_cuts, is_w_sideband, pion_candidate_idxs}; } -//============================================================================== -// Fuction to count the number of events that pass the cuts -//============================================================================== +// Passes All Cuts v2 (being deprecated) +// fills stuff by reference, instead of returning +bool PassesCuts(CVUniverse& universe, std::vector& pion_candidate_idxs, + const bool is_mc, SignalDefinition signal_definition, + bool& is_w_sideband, std::vector cuts) { + // is the W cut even in the cuts vector provided? + bool do_w_cut = std::find(cuts.begin(), cuts.end(), kWexp) != cuts.end(); + + // either way, attempt to remove it + std::vector w_sideband_cuts = kCutsVector; + w_sideband_cuts.erase( + std::remove(w_sideband_cuts.begin(), w_sideband_cuts.end(), kWexp), + w_sideband_cuts.end()); + + // check passes all but w cut + bool passes_all_but_w_cut = PassesCuts(universe, pion_candidate_idxs, is_mc, + signal_definition, w_sideband_cuts); + + // is w sideband = all cuts but W && W > 1.5 + is_w_sideband = passes_all_but_w_cut && + (universe.GetWexp() >= sidebands::kSidebandCutVal); + + // Finally check all cuts == all cuts but W && W + bool passes_all_cuts = passes_all_but_w_cut; + if (do_w_cut) + passes_all_cuts = + passes_all_but_w_cut && WexpCut(universe, signal_definition); + + return passes_all_cuts; +} + +// Passes All Cuts v1 (being deprecated) +// fills by reference and doesn't check W sideband +bool PassesCuts(CVUniverse& univ, std::vector& pion_candidate_idxs, + bool is_mc, SignalDefinition signal_definition, + std::vector cuts) { + pion_candidate_idxs.clear(); + static endpoint::MichelMap endpoint_michels; + static endpoint::MichelMap + vertex_michels; // Keep track of these, but not used currently + endpoint_michels.clear(); + vertex_michels.clear(); + bool pass = true; + for (auto c : cuts) { + univ.SetPionCandidates(GetHadIdxsFromMichels( + endpoint_michels)); // Set the pion candidates to the universe + pass = pass && PassesCut(univ, c, is_mc, signal_definition, + endpoint_michels, vertex_michels); + } + + // Each endpoint michel has an associated hadron track. + // Our official pion candidates are those tracks. + pion_candidate_idxs = GetHadIdxsFromMichels(endpoint_michels); + + return pass; +} + +// Count events EventCount PassedCuts(const CVUniverse& univ, std::vector& pion_candidate_idxs, bool is_mc, SignalDefinition signal_definition, @@ -156,10 +143,7 @@ EventCount PassedCuts(const CVUniverse& univ, return Pass; } -//============================================================================== -// Passes INDIVIDUAL Cut -//============================================================================== -// Updates the michel containers +// Pass Single, Given Cut bool PassesCut(const CVUniverse& univ, const ECuts cut, const bool is_mc, const SignalDefinition signal_definition, endpoint::MichelMap& endpoint_michels, endpoint::MichelMap& vertex_michels) { @@ -197,31 +181,12 @@ bool PassesCut(const CVUniverse& univ, const ECuts cut, const bool is_mc, case kMinosCharge: return MinosChargeCut(univ); - case kMinosCoil: - return MinosCoilCut(univ); - case kMinosMuon: return MinosMatchCut(univ) && MinosChargeCut(univ); - //&& MinosCoilCut(univ); - - case kThetaMu: - return ThetamuCut(univ); - - case kDeadTime: - return DeadTimeCut(univ); case kWexp: return WexpCut(univ, signal_definition); - case kIsoBlobs: - return IsoBlobCut(univ); - - case kIsoProngs: - return IsoProngCut(univ); - - case kIsoProngSep: - return IsoProngSepCut(univ); - case kPmu: return PmuCut(univ); @@ -244,18 +209,6 @@ bool PassesCut(const CVUniverse& univ, const ECuts cut, const bool is_mc, case kAtLeastOnePionCandidateTrack: return GetQualityPionCandidateIndices(univ).size() > 0; - case kAtLeastOneLLRCandidate: - return AtLeastOneLLRCandidateCut(univ); - - case kAtLeastOneNodeCandidate: - return AtLeastOneNodeCandidateCut(univ); - - case kAtLeastOneAnchoredProng: - return AtLeastOneAnchoredProngCut(univ); - - case kAtLeastOneBrandonMichel: - return AtLeastOneBrandonMichelCut(univ); - // If a michel's pion fails the LLR cut, remove it from the michels case kLLR: { ContainerEraser::erase_if(endpoint_michels, @@ -291,374 +244,135 @@ bool PassesCut(const CVUniverse& univ, const ECuts cut, const bool is_mc, } //============================================================================== -// Cut functions +// Cut Definitions //============================================================================== // Truth precuts -bool GoodObjectsCut(const CVUniverse& univ) { - return univ.GetBool("truth_reco_hasGoodObjects"); -} -bool GoodVertexCut(const CVUniverse& univ) { - return univ.GetBool("truth_reco_isGoodVertex"); -} -bool FiducialVolumeCut(const CVUniverse& univ) { - return univ.GetBool("truth_reco_isFidVol_smeared"); -} -bool MinosActivityCut(const CVUniverse& univ) { - return univ.GetInt("truth_reco_muon_is_minos_match"); -} + bool GoodObjectsCut(const CVUniverse& univ) { + return univ.GetBool("truth_reco_hasGoodObjects"); + } + bool GoodVertexCut(const CVUniverse& univ) { + return univ.GetBool("truth_reco_isGoodVertex"); + } + bool FiducialVolumeCut(const CVUniverse& univ) { + return univ.GetBool("truth_reco_isFidVol_smeared"); + } + bool MinosActivityCut(const CVUniverse& univ) { + return univ.GetInt("truth_reco_muon_is_minos_match"); + } // Eventwide reco cuts -bool MinosMatchCut(const CVUniverse& univ) { - return univ.GetBool("isMinosMatchTrack"); -} -// Equivalent to Brandon's, but using standard minos branches -bool MinosChargeCut(const CVUniverse& univ) { - return univ.GetDouble("MasterAnaDev_minos_trk_qp") < 0.0; -} - -bool ThetamuCut(const CVUniverse& univ) { return univ.GetThetamu() < 0.3491; } -bool DeadTimeCut(const CVUniverse& univ) { return univ.GetInt("tdead") <= 1; } -bool WexpCut(const CVUniverse& univ, SignalDefinition signal_definition) { - switch (signal_definition) { - case kOnePi: - case kNPi: - return univ.GetWexp() < GetWCutValue(signal_definition); - case kOnePiNoW: - case kNPiNoW: - return true; - default: - std::cout << "WexpCut SIGNAL DEF ERROR"; - return false; + bool MinosMatchCut(const CVUniverse& univ) { + return univ.GetBool("isMinosMatchTrack"); + } + // Equivalent to Brandon's, but using standard minos branches + bool MinosChargeCut(const CVUniverse& univ) { + return univ.GetDouble("MasterAnaDev_minos_trk_qp") < 0.0; } -} - -// cut on max number of iso prongs -// PrimaryBlobProngTool::makeShowerBlobProngs -bool IsoProngCut(const CVUniverse& univ) { - return univ.GetNIsoProngs() < CCNuPionIncConsts::kIsoProngCutVal; -} - -bool NodeCut(const CVUniverse& univ, const RecoPionIdx pion_candidate_idx) { - return 6. < univ.GetEnode01(pion_candidate_idx) && - univ.GetEnode01(pion_candidate_idx) < 32. && - 2. < univ.GetEnode2(pion_candidate_idx) && - univ.GetEnode2(pion_candidate_idx) < 22. && - 0. < univ.GetEnode3(pion_candidate_idx) && - univ.GetEnode3(pion_candidate_idx) < 19. && - 0. < univ.GetEnode4(pion_candidate_idx) && - univ.GetEnode4(pion_candidate_idx) < 31. && - 0. < univ.GetEnode5(pion_candidate_idx) && - univ.GetEnode5(pion_candidate_idx) < 60.; -} - -bool LLRCut(const CVUniverse& univ, const RecoPionIdx pion_candidate_idx) { - // if (pion_candidate_idx < 0) return false; - // else return univ.GetLLRScore(pion_candidate_idx) > 0.; - return univ.GetLLRScore(pion_candidate_idx) > 0.; -} -// Helper -- get vector of good-enough had tracks, id'ed by unique indices. -std::vector GetQualityPionCandidateIndices(const CVUniverse& univ) { - std::vector pion_candidate_indices; - int n_hadrons = univ.GetInt("MasterAnaDev_hadron_number"); - for (int i_hadron = 0; i_hadron != n_hadrons; ++i_hadron) - if (HadronQualityCuts(univ, i_hadron)) - pion_candidate_indices.push_back(i_hadron); - return pion_candidate_indices; -} + bool WexpCut(const CVUniverse& univ, SignalDefinition signal_definition) { + switch (signal_definition) { + case kOnePi: + case kNPi: + return univ.GetWexp() < GetWCutValue(signal_definition); + case kOnePiNoW: + case kNPiNoW: + return true; + default: + std::cout << "WexpCut SIGNAL DEF ERROR"; + return false; + } + } -bool HadronQualityCuts(const CVUniverse& univ, - const RecoPionIdx pion_candidate_idx) { - return univ.GetVecElem("MasterAnaDev_hadron_isForked", pion_candidate_idx) == - 0 && - univ.GetVecElem("MasterAnaDev_hadron_isExiting", pion_candidate_idx) == - 0 && - univ.GetVecElem("MasterAnaDev_hadron_isSideECAL", - pion_candidate_idx) == 0 && - univ.GetVecElem("MasterAnaDev_hadron_isODMatch", pion_candidate_idx) == - 0 && - univ.GetVecElem("MasterAnaDev_hadron_isTracker", pion_candidate_idx) == - 1; -}; -// Vtx cut for detection volume -bool vtxCut(const CVUniverse& univ) { - bool pass = true; - pass = pass && zVertexCut(univ, 8340.0, 5990.0); - pass = pass && XYVertexCut(univ, 850.0); - return pass; -} + // cut on max number of iso prongs + // PrimaryBlobProngTool::makeShowerBlobProngs + bool IsoProngCut(const CVUniverse& univ) { + return univ.GetNIsoProngs() < CCNuPionIncConsts::kIsoProngCutVal; + } -bool zVertexCut(const CVUniverse& univ, const double upZ, const double downZ) { - double vtxZ = univ.GetVecElem("vtx", 2); - if (vtxZ > downZ && vtxZ < upZ) - return true; - else - return false; -} + // Vtx cut for detection volume + bool vtxCut(const CVUniverse& univ) { + bool pass = true; + pass = pass && zVertexCut(univ, 8340.0, 5990.0); + pass = pass && XYVertexCut(univ, 850.0); + return pass; + } -bool XYVertexCut(const CVUniverse& univ, const double a) { - const double x = univ.GetVecElem("vtx", 0), y = univ.GetVecElem("vtx", 1); - if (x < 0) { - if (x > -a && univ.leftlinesCut(a, x, y)) - return true; - else - return false; - } else { - if (x < a && univ.rightlinesCut(a, x, y)) + bool zVertexCut(const CVUniverse& univ, const double upZ, const double downZ) { + double vtxZ = univ.GetVecElem("vtx", 2); + if (vtxZ > downZ && vtxZ < upZ) return true; else return false; } -} - -bool PmuCut(const CVUniverse& univ) { - if (univ.GetPmu() / 1000 < 1.5 || 20 < univ.GetPmu() / 1000) - return false; - else - return true; -} - -//============================================================================== -// Retired -//============================================================================== -// Brandon's Minos Cut -bool BrandonMinosChargeCut(CVUniverse& univ) { - return univ.GetDouble("MasterAnaDev_muon_qpqpe") < 0.0; -} - -// CCInclusive-style minos charge cut -bool CCIncMinosChargeCut(CVUniverse& univ) { - if (univ.GetBool("MasterAnaDev_minos_used_curvature")) - return 1. / univ.GetDouble("MasterAnaDev_minos_trk_eqp_qp") < -5.0; - else if (univ.GetBool("MasterAnaDev_minos_used_range")) - return univ.GetBool("MasterAnaDev_minos_trk_qp") < 0.0; - else - return false; -} - -bool MinosCoilCut(const CVUniverse& univ) { - const double MINOS_COIL_RADIUS = 210; // mm - const double MAX_MINOS_RADIUS = 2500; // mm - const double coilXPos = 1219.0; - const double coilYPos = 393.0; - const double minos_x = - univ.GetDouble("MasterAnaDev_minos_trk_end_x") + coilXPos; - const double minos_y = - univ.GetDouble("MasterAnaDev_minos_trk_end_y") + coilYPos; - double minosR = sqrt(pow(minos_x, 2) + pow(minos_y, 2)); - // if (!((pow(minos_x,2) + pow(minos_y,2) )>= pow(MINOS_COIL_RADIUS, 2)) ) - // cout << minos_x << " " << minos_y << " " << MINOS_COIL_RADIUS << endl; - return (minosR > MINOS_COIL_RADIUS && minosR < MAX_MINOS_RADIUS); -} - -// no isolated blobs allowed -bool IsoBlobCut(const CVUniverse& univ) { - return univ.GetInt("n_iso_blob_prongs") < - 1; // RecoilUtils::createIsoBlobProngs -} - -// IsoProngSeparation -bool IsoProngSepCut(const CVUniverse& univ) { - return univ.GetLargestIsoProngSep() < 300; -} -bool AtLeastOneBrandonMichelCut(const CVUniverse& univ) { - // Get quality hadron candidates - std::vector pion_candidate_indices = - GetQualityPionCandidateIndices(univ); - - // Loop quality hadron candidates to see if they have a good brandon michel - for (auto pion_candidate_idx : pion_candidate_indices) { - int michel_views = univ.GetVecElem("MasterAnaDev_hadron_endMichel_category", - pion_candidate_idx); - int michel_ndigits = univ.GetVecElem( - "MasterAnaDev_hadron_endMichel_ndigits", pion_candidate_idx); - double michel_energy = - univ.GetVecElem("MasterAnaDev_hadron_endMichel_energy", - pion_candidate_idx); /// TODO sys universe function - double michel_slice_energy = univ.GetVecElem( - "MasterAnaDev_hadron_endMichel_slice_energy", pion_candidate_idx); - - if (michel_views < 1) - continue; // no michel - else if (michel_views == 1) { // 1 view - if (michel_energy < 55.0 && michel_ndigits < 35 && - michel_slice_energy < 100.0) + bool XYVertexCut(const CVUniverse& univ, const double a) { + const double x = univ.GetVecElem("vtx", 0), y = univ.GetVecElem("vtx", 1); + if (x < 0) { + if (x > -a && univ.leftlinesCut(a, x, y)) return true; else - continue; - } else if (michel_views > 1) { // 2+3 views - if (michel_energy < 55.0 && michel_ndigits < 35 && - michel_ndigits >= michel_views) + return false; + } else { + if (x < a && univ.rightlinesCut(a, x, y)) return true; else - continue; + return false; } } - return false; -} -// ntrk are tracks not including the muon prong -bool AtLeastOneAnchoredProngCut(const CVUniverse& univ) { - int ntrk = univ.GetInt("n_anchored_long_trk_prongs") + - univ.GetInt("n_anchored_short_trk_prongs"); - return ntrk > 0; -} - -bool ExactlyOneEndpointMichelCut(const CVUniverse& univ, - SignalDefinition signal_definition) { - if (signal_definition == kNPi || signal_definition == kNPiNoW) { - return true; - } else if (signal_definition == kOnePi || signal_definition == kOnePiNoW) { - endpoint::MichelMap mm = endpoint::GetQualityMichels(univ); - if (mm.size() == 1) { // require only one michel - endpoint::Michel m = (mm.begin())->second; - if (m.vtx == 0) - return false; // so this has a no-vertex michel cut baked in - // pion_candidate_idx = m.vtx - 1; // SELECT OUR PION - return true; - } else + bool PmuCut(const CVUniverse& univ) { + if (univ.GetPmu() / 1000 < 1.5 || 20 < univ.GetPmu() / 1000) return false; - } else { - std::cout << "ExactlyOneEndpointMichelcut SIGNAL DEFINITION ERROR" - << std::endl; - return false; + else + return true; } -} -bool AtLeastOneLLRCandidateCut(const CVUniverse& univ) { - // Get quality hadron candidates - std::vector pion_candidate_indices = - GetQualityPionCandidateIndices(univ); - for (auto pion_candidate_idx : pion_candidate_indices) { - if (LLRCut(univ, pion_candidate_idx)) return true; +// Exclusive - cuts on pion tracks + bool NodeCut(const CVUniverse& univ, const RecoPionIdx pion_candidate_idx) { + return 6. < univ.GetEnode01(pion_candidate_idx) && + univ.GetEnode01(pion_candidate_idx) < 32. && + 2. < univ.GetEnode2(pion_candidate_idx) && + univ.GetEnode2(pion_candidate_idx) < 22. && + 0. < univ.GetEnode3(pion_candidate_idx) && + univ.GetEnode3(pion_candidate_idx) < 19. && + 0. < univ.GetEnode4(pion_candidate_idx) && + univ.GetEnode4(pion_candidate_idx) < 31. && + 0. < univ.GetEnode5(pion_candidate_idx) && + univ.GetEnode5(pion_candidate_idx) < 60.; } - return false; -} -bool AtLeastOneNodeCandidateCut(const CVUniverse& univ) { - // Get quality hadron candidates - std::vector pion_candidate_indices = - GetQualityPionCandidateIndices(univ); - for (auto pion_candidate_idx : pion_candidate_indices) { - if (NodeCut(univ, pion_candidate_idx)) return true; + bool LLRCut(const CVUniverse& univ, const RecoPionIdx pion_candidate_idx) { + // if (pion_candidate_idx < 0) return false; + // else return univ.GetLLRScore(pion_candidate_idx) > 0.; + return univ.GetLLRScore(pion_candidate_idx) > 0.; } - return false; -} -//============================================================================== -// Cut Names -//============================================================================== -std::string GetCutName(ECuts cut) { - switch (cut) { - case kNoCuts: - return "No Cuts"; - - case kGoodObjects: - return "Good Objects"; - - case kGoodVertex: - return "Good Vertex"; - - case kFiducialVolume: - return "Fiducial Volume"; - - case kMinosActivity: - return "MINOS Activity"; - - case kPrecuts: - return "Anatool Precuts"; - - case kVtx: - return "vertex position Cut"; - - case kMinosMatch: - return "MINOS Muon"; - - case kMinosCharge: - return "MINOS Charge"; - - case kMinosCoil: - return "MINOS Coil"; - - case kMinosMuon: - return "MINOS Muon"; - - case kThetaMu: - return "Muon Angle"; - - case kDeadTime: - return "Dead Time"; - - case kWexp: - return "$W_{experimental}$"; - - case kIsoBlobs: - return "$<$1 Isolated Blobs"; - - case kIsoProngs: - return "$<$2 Isolated Prongs"; - - case kIsoProngSep: - return "Iso Prong Sep $<$ 300"; - - case kNProngs: - return "Max 2 Hadr Prongs"; - - case kNPionCandidates: - return "$\\pi$ candidate"; - - case kPionCandidateQuality: - return "Quality $\\pi$ candidate"; - - case kAtLeastOneMichel: - return "$>$= 1 Michel"; - - case kAtLeastOneBrandonMichel: - return "$>$= 1 Brandon Michel"; - - case kAtLeastOneAnchoredProng: - return "$>$= 1 Anchored Prong"; - - case kAtLeastOnePionCandidateTrack: - return "$>$= 1 Hadron Track"; - - case kAtLeastOneLLRCandidate: - return "$>$= 1 Track Pass LLR Cut"; - - case kAtLeastOneNodeCandidate: - return "$>$= 1 Track Pass Node Cut"; - - case kExactlyOneEndpointMichel: - return "== 1 Michel"; - - case kNode: - return "Node"; - - case kPionMult: - return "Pion Multiplicity"; - - case kOldMichel: - return "Old Michel Cut"; - - case kdEdx: - return "dEdx PID"; - - case kLLR: - return "LLR PID"; - - case kAllCuts: - return "Total"; - - case kPmu: - return "1.5 GeV $<$ Pmu $<$ 20 GeV"; - - default: - std::cout << "ERROR: GetCutName unknown cut!" << std::endl; - return ""; + bool HadronQualityCuts(const CVUniverse& univ, + const RecoPionIdx pion_candidate_idx) { + return univ.GetVecElem("MasterAnaDev_hadron_isForked", pion_candidate_idx) == + 0 && + univ.GetVecElem("MasterAnaDev_hadron_isExiting", pion_candidate_idx) == + 0 && + univ.GetVecElem("MasterAnaDev_hadron_isSideECAL", + pion_candidate_idx) == 0 && + univ.GetVecElem("MasterAnaDev_hadron_isODMatch", pion_candidate_idx) == + 0 && + univ.GetVecElem("MasterAnaDev_hadron_isTracker", pion_candidate_idx) == + 1; }; -} +//============================================================================== +// Helper +//============================================================================== +// Get candidate pions that pass the minimal HadronQualityCuts +std::vector GetQualityPionCandidateIndices(const CVUniverse& univ) { + std::vector pion_candidate_indices; + int n_hadrons = univ.GetInt("MasterAnaDev_hadron_number"); + for (int i_hadron = 0; i_hadron != n_hadrons; ++i_hadron) + if (HadronQualityCuts(univ, i_hadron)) + pion_candidate_indices.push_back(i_hadron); + return pion_candidate_indices; +} #endif // Cuts_cxx diff --git a/includes/Cuts.h b/includes/Cuts.h index 8f323e3..a0ffcaa 100644 --- a/includes/Cuts.h +++ b/includes/Cuts.h @@ -1,137 +1,87 @@ //============================================================================== -// I'd probably like to turn this into a class and/or break all of this up into -// smaller parts. But I'm hesitant to mess with it. +// This file contains all event selection cuts definitions. // -// How it all works: -// * PassesCuts calls PassesCut on given vector of cuts. -// * A static container of Michel objects is shlepped around, passed to each -// PassesCut. -// * The michels correspond to the pion candidates. -// * One of the cuts fills the container, and subsequent cuts remove -// michels/pions from the container. -// * A container of pion candidate indices corresponding to the michel -// container is returned by reference from PassesCuts. +// As well as generic PassesCut and PassesCuts functions. +// +// PassesCuts does more than just check whether events pass all cuts. Checking +// cuts is expensive. So additionally, it checks whether the event-universe is +// w sideband, and it returns the tracks deemed to be pion candidates. //============================================================================== - #ifndef Cuts_H #define Cuts_H #include #include +#include "CutUtils.h" #include "CVUniverse.h" #include "Constants.h" // enum ECuts, CCNuPionIncConsts #include "Michel.h" #include "SignalDefinition.h" //============================================================================== -// USE THESE CUTS -// -// Note 0: The michel cut IS the selector of the hadron candidate. Without it, -// you cannot make further cuts on pion variables nor plot pion variables. -// Note 1: The order of precuts matters for some things: -// 1. objects 2. vertex 3. FV 4. Minos activity -//============================================================================== -const std::vector kCutsVector = { - kNoCuts, - kPrecuts, - kVtx, - kMinosMuon, - kAtLeastOnePionCandidateTrack, - kAtLeastOneMichel, - kLLR, - kNode, - kWexp, // Calling this after pion candidate determined! - kIsoProngs, - kPionMult, - kPmu}; - -const std::vector GetWSidebandCuts() { - std::vector w_sideband_cuts = kCutsVector; - w_sideband_cuts.erase( - std::remove(w_sideband_cuts.begin(), w_sideband_cuts.end(), kWexp), - w_sideband_cuts.end()); - return w_sideband_cuts; -} - -//============================================================================== -// These cuts were made in the ana tool -// So for reco data and MC these are always true. +// Generic Pass Cut(s) Functions +// * passes, is_sideband, pion_indices = PassesCuts() +// * bool PassesCut(cut) +// * PassedCuts <-- just an event counter //============================================================================== -bool IsPrecut(ECuts c) { - if (c == kNoCuts || c == kGoodObjects || c == kGoodVertex || - c == kFiducialVolume || c == kMinosActivity || c == kPrecuts) - return true; - else - return false; -} +// PassesCuts v3 (latest and greatest) +std::tuple> PassesCuts( + CVUniverse&, const bool is_mc, const SignalDefinition, + const std::vector cuts = kCutsVector); -//============================================================================== -// Function Declarations -//============================================================================== -// Call the cut functions -- fill-in/return the ref to the good pion candidate +// PassesCuts v2 (being deprecated) bool PassesCuts(CVUniverse&, std::vector& pion_candidate_idxs, bool is_mc, SignalDefinition, std::vector cuts = kCutsVector); -// also tell whether we are w sideband +// PassesCuts v1 (being deprecated) bool PassesCuts(CVUniverse&, std::vector& pion_candidate_idxs, const bool is_mc, const SignalDefinition, bool& is_w_sideband, std::vector cuts = kCutsVector); -// NEW return passes_all_cuts, is_w_sideband, and pion_candidate_indices -std::tuple> PassesCuts( - CVUniverse&, const bool is_mc, const SignalDefinition, - const std::vector cuts = kCutsVector); - +// Event Counter EventCount PassedCuts(const CVUniverse&, std::vector& pion_candidate_idxs, bool is_mc, SignalDefinition, std::vector cuts = kCutsVector); +// Passes Single, Given Cut bool PassesCut(const CVUniverse&, const ECuts cut, const bool is_mc, const SignalDefinition, endpoint::MichelMap& endpoint_michels, endpoint::MichelMap& vertex_michels); -// Get a vector of integers which are unique hadron prong identifiers. -// The length of this vector is the number of pion candidate tracks found. -// std::vector GetPionCandidates(CVUniverse&); - -// Helper -std::string GetCutName(ECuts cut); - -// Cuts functions -- eventwide -- TRUTH ONLY +//============================================================================== +// Cuts Definitions +//============================================================================== +// Gaudi tool cuts -- read from truth tuple. +// (won't work if we pass a reco mc universe) bool GoodObjectsCut(const CVUniverse&); bool GoodVertexCut(const CVUniverse&); bool FiducialVolumeCut(const CVUniverse&); bool MinosActivityCut(const CVUniverse&); -// Cut functions -- eventwide +// Cut Definitions -- eventwide bool MinosMatchCut(const CVUniverse&); bool MinosChargeCut(const CVUniverse&); bool WexpCut(const CVUniverse&, SignalDefinition); bool IsoProngCut(const CVUniverse&); -std::vector GetQualityPionCandidateIndices(const CVUniverse&); -bool HadronQualityCuts(const CVUniverse&, const RecoPionIdx pion_candidate_idx); bool vtxCut(const CVUniverse& univ); bool zVertexCut(const CVUniverse& univ, const double upZ, const double downZ); bool XYVertexCut(const CVUniverse& univ, const double a); bool PmuCut(const CVUniverse& univ); -// Cuts functions -- on pion candidate tracks +// Cuts Definitions -- exclusive, i.e. on pion candidate tracks +bool HadronQualityCuts(const CVUniverse&, const RecoPionIdx pion_candidate_idx); bool LLRCut(const CVUniverse&, const RecoPionIdx pion_candidate_idx); bool NodeCut(const CVUniverse&, const RecoPionIdx pion_candidate_idx); -// Retired -bool DeadTimeCut(const CVUniverse&); -bool MinosCoilCut(const CVUniverse&); -bool IsoBlobCut(const CVUniverse&); -bool IsoProngSepCut(const CVUniverse&); -bool ThetamuCut(const CVUniverse&); -bool BrandonMinosChargeCut(const CVUniverse&); -bool CCIncMinosChargeCut(const CVUniverse&); -bool ExactlyOneEndpointMichelCut(const CVUniverse&, SignalDefinition); -bool AtLeastOneBrandonMichelCut(const CVUniverse&); -bool AtLeastOneAnchoredProngCut(const CVUniverse&); -bool AtLeastOneNodeCandidateCut(const CVUniverse&); -bool AtLeastOneLLRCandidateCut(const CVUniverse&); +//============================================================================== +// Helper +//============================================================================== +// Get candidate pions that pass the minimal HadronQualityCuts +std::vector GetQualityPionCandidateIndices(const CVUniverse&); + +//bool AtLeastOnePionCut(const CVUniverse& univ) { +// std::tuple<> GetAllMichels(); +//} #endif diff --git a/includes/RetiredCuts.h b/includes/RetiredCuts.h new file mode 100644 index 0000000..7262fc5 --- /dev/null +++ b/includes/RetiredCuts.h @@ -0,0 +1,149 @@ +//============================================================================== +// Cleaned out Cuts.* files. +// Don't plan on using these cuts anymore and don't try to compile them. +//============================================================================== +#ifndef RetiredCuts_H +#define RetiredCuts_H + +#include "Constants.h" // ECuts +#include "CVUniverse.h" +#include "SignalDefinition.h" + +bool DeadTimeCut(const CVUniverse&); +bool DeadTimeCut(const CVUniverse& univ) { return univ.GetInt("tdead") <= 1; } + +bool MinosCoilCut(const CVUniverse&); +bool MinosCoilCut(const CVUniverse& univ) { + const double MINOS_COIL_RADIUS = 210; // mm + const double MAX_MINOS_RADIUS = 2500; // mm + const double coilXPos = 1219.0; + const double coilYPos = 393.0; + const double minos_x = + univ.GetDouble("MasterAnaDev_minos_trk_end_x") + coilXPos; + const double minos_y = + univ.GetDouble("MasterAnaDev_minos_trk_end_y") + coilYPos; + double minosR = sqrt(pow(minos_x, 2) + pow(minos_y, 2)); + // if (!((pow(minos_x,2) + pow(minos_y,2) )>= pow(MINOS_COIL_RADIUS, 2)) ) + // cout << minos_x << " " << minos_y << " " << MINOS_COIL_RADIUS << endl; + return (minosR > MINOS_COIL_RADIUS && minosR < MAX_MINOS_RADIUS); +} + +bool IsoBlobCut(const CVUniverse&); +bool IsoBlobCut(const CVUniverse& univ) { + return univ.GetInt("n_iso_blob_prongs") < + 1; // RecoilUtils::createIsoBlobProngs +} + +bool IsoProngSepCut(const CVUniverse&); +bool IsoProngSepCut(const CVUniverse& univ) { + return univ.GetLargestIsoProngSep() < 300; +} + +bool ThetamuCut(const CVUniverse&); +bool ThetamuCut(const CVUniverse& univ) { return univ.GetThetamu() < 0.3491; } + +bool BrandonMinosChargeCut(const CVUniverse&); +bool BrandonMinosChargeCut(CVUniverse& univ) { + return univ.GetDouble("MasterAnaDev_muon_qpqpe") < 0.0; +} + +bool CCIncMinosChargeCut(const CVUniverse&); +bool CCIncMinosChargeCut(CVUniverse& univ) { + if (univ.GetBool("MasterAnaDev_minos_used_curvature")) + return 1. / univ.GetDouble("MasterAnaDev_minos_trk_eqp_qp") < -5.0; + else if (univ.GetBool("MasterAnaDev_minos_used_range")) + return univ.GetBool("MasterAnaDev_minos_trk_qp") < 0.0; + else + return false; +} + +bool ExactlyOneEndpointMichelCut(const CVUniverse&, SignalDefinition); +bool ExactlyOneEndpointMichelCut(const CVUniverse& univ, + SignalDefinition signal_definition) { + if (signal_definition == kNPi || signal_definition == kNPiNoW) { + return true; + } else if (signal_definition == kOnePi || signal_definition == kOnePiNoW) { + endpoint::MichelMap mm = endpoint::GetQualityMichels(univ); + if (mm.size() == 1) { // require only one michel + endpoint::Michel m = (mm.begin())->second; + if (m.vtx == 0) + return false; // so this has a no-vertex michel cut baked in + // pion_candidate_idx = m.vtx - 1; // SELECT OUR PION + return true; + } else + return false; + } else { + std::cout << "ExactlyOneEndpointMichelcut SIGNAL DEFINITION ERROR" + << std::endl; + return false; + } +} + +bool AtLeastOneBrandonMichelCut(const CVUniverse&); +bool AtLeastOneBrandonMichelCut(const CVUniverse& univ) { + // Get quality hadron candidates + std::vector pion_candidate_indices = + GetQualityPionCandidateIndices(univ); + + // Loop quality hadron candidates to see if they have a good brandon michel + for (auto pion_candidate_idx : pion_candidate_indices) { + int michel_views = univ.GetVecElem("MasterAnaDev_hadron_endMichel_category", + pion_candidate_idx); + int michel_ndigits = univ.GetVecElem( + "MasterAnaDev_hadron_endMichel_ndigits", pion_candidate_idx); + double michel_energy = + univ.GetVecElem("MasterAnaDev_hadron_endMichel_energy", + pion_candidate_idx); /// TODO sys universe function + double michel_slice_energy = univ.GetVecElem( + "MasterAnaDev_hadron_endMichel_slice_energy", pion_candidate_idx); + + if (michel_views < 1) + continue; // no michel + else if (michel_views == 1) { // 1 view + if (michel_energy < 55.0 && michel_ndigits < 35 && + michel_slice_energy < 100.0) + return true; + else + continue; + } else if (michel_views > 1) { // 2+3 views + if (michel_energy < 55.0 && michel_ndigits < 35 && + michel_ndigits >= michel_views) + return true; + else + continue; + } + } + return false; +} + +// ntrk are tracks not including the muon prong +bool AtLeastOneAnchoredProngCut(const CVUniverse&); +bool AtLeastOneAnchoredProngCut(const CVUniverse& univ) { + int ntrk = univ.GetInt("n_anchored_long_trk_prongs") + + univ.GetInt("n_anchored_short_trk_prongs"); + return ntrk > 0; +} + +bool AtLeastOneNodeCandidateCut(const CVUniverse&); +bool AtLeastOneNodeCandidateCut(const CVUniverse& univ) { + // Get quality hadron candidates + std::vector pion_candidate_indices = + GetQualityPionCandidateIndices(univ); + for (auto pion_candidate_idx : pion_candidate_indices) { + if (NodeCut(univ, pion_candidate_idx)) return true; + } + return false; +} + +bool AtLeastOneLLRCandidateCut(const CVUniverse&); +bool AtLeastOneLLRCandidateCut(const CVUniverse& univ) { + // Get quality hadron candidates + std::vector pion_candidate_indices = + GetQualityPionCandidateIndices(univ); + for (auto pion_candidate_idx : pion_candidate_indices) { + if (LLRCut(univ, pion_candidate_idx)) return true; + } + return false; +} + +#endif diff --git a/loadLibs.C b/loadLibs.C index 7227341..99173f1 100644 --- a/loadLibs.C +++ b/loadLibs.C @@ -15,6 +15,7 @@ void loadIncludes(bool verbose_cvu) { oldpath += path; gSystem->SetIncludePath(oldpath); gSystem->CompileMacro("CVUniverse.cxx", cvu_flags); + gSystem->CompileMacro("CutUtils.h", "k"); gSystem->CompileMacro("Cuts.cxx", "k"); gSystem->CompileMacro("StackedHistogram.cxx", "k"); gSystem->CompileMacro("Histograms.cxx", "k"); diff --git a/studies/runNewMichels.C b/studies/runNewMichels.C index 4350bb1..bd6aa2f 100644 --- a/studies/runNewMichels.C +++ b/studies/runNewMichels.C @@ -64,15 +64,17 @@ void LoopAndFill(const CCPi::MacroUtil& util, CVUniverse* universe, Long64_t n_entries; SetupLoop(type, util, is_mc, is_truth, n_entries); - for(Long64_t i_event=0; i_event < n_entries; ++i_event){ + //for(Long64_t i_event=0; i_event < n_entries; ++i_event){ + for(Long64_t i_event=0; i_event < 5000; ++i_event){ if (i_event%500000==0) std::cout << (i_event/1000) << "k " << std::endl; universe->SetEntry(i_event); // For mc, get weight, check signal, and sideband CCPiEvent event(is_mc, is_truth, util.m_signal_definition, universe); + std::tie(event.m_passes_cuts, event.m_is_w_sideband, event.m_reco_pion_candidate_idxs) = PassesCuts(event); // WRITE THE FILL FUNCTION - run_new_michels::FillVars(event, variables); + //run_new_michels::FillVars(event, variables); } // events std::cout << "*** Done ***\n\n"; } @@ -86,14 +88,16 @@ void runNewMichels(std::string plist = "ME1A") { //========================================= bool is_mc = true; std::string mc_file_list, data_file_list; - mc_file_list = GetPlaylistFile(plist, is_mc); + bool use_xrootd = true; + mc_file_list = GetPlaylistFile(plist, is_mc, use_xrootd); is_mc = false; - data_file_list = GetPlaylistFile(plist, is_mc); + data_file_list = GetPlaylistFile(plist, is_mc, use_xrootd); //========================================= // Init macro utility //========================================= - const int signal_definition_int = 0; const std::string macro("runNewMichels"); + const int signal_definition_int = 0; + const std::string macro("runNewMichels"); const bool is_grid = false; const bool do_truth = false; const bool do_systematics = false; @@ -101,19 +105,19 @@ void runNewMichels(std::string plist = "ME1A") { CCPi::MacroUtil util(signal_definition_int, mc_file_list, data_file_list, plist, do_truth, is_grid, do_systematics); util.PrintMacroConfiguration(macro); -/* //========================================= // Get variables and initialize their hists //========================================= - std::vector variables = run_study_template::GetVariables(); + std::vector variables = run_new_michels::GetVariables(); for (auto v : variables) v->InitializeAllHists(util.m_error_bands, util.m_error_bands_truth); //========================================= // Loop and Fill //========================================= - LoopAndFill(util, util.m_data_universe, kData, variables); LoopAndFill(util, util.m_error_bands.at("cv").at(0), kMC, variables); +/* + LoopAndFill(util, util.m_data_universe, kData, variables); for (auto v : variables) { std::string tag = v->Name(); @@ -122,7 +126,7 @@ void runNewMichels(std::string plist = "ME1A") { std::cout << "Plotting" << std::endl; PlotCutVar(v, v->m_hists.m_selection_data, v->GetStackArray(kS), util.m_data_pot, util.m_mc_pot, util.m_signal_definition, v->Name(),"SSB", ymax, do_bwn); } +*/ std::cout << "Success" << std::endl; -*/ } From 1cb8f5bdd9b604be90b7d541c30d52d6cd20eada Mon Sep 17 00:00:00 2001 From: Ben Messerly Date: Fri, 18 Mar 2022 14:05:32 -0500 Subject: [PATCH 12/17] Small fixes. 1. update some branch names for newer version of mehreen branches. 2. had accidentally removed iso prongs cut. 3. replace raw branch read in signal def with official mnrva function. --- includes/Cluster.h | 10 +++++----- includes/Cuts.cxx | 5 ++++- includes/Michel.h | 2 +- includes/SignalDefinition.h | 2 +- 4 files changed, 11 insertions(+), 8 deletions(-) diff --git a/includes/Cluster.h b/includes/Cluster.h index 99245dc..d202f84 100644 --- a/includes/Cluster.h +++ b/includes/Cluster.h @@ -25,11 +25,11 @@ struct Cluster { }; Cluster::Cluster(const CVUniverse &univ, int &ci) { - energy = univ.GetVecElem("FittedMichel_cluster_energy", ci); // MeV - time = univ.GetVecElem("FittedMichel_cluster_time", ci) / pow(10, 3); // microseconds - pos = univ.GetVecElem("FittedMichel_cluster_pos", ci); // in mm - zpos = univ.GetVecElem("FittedMichel_cluster_z", ci); // in mm - view = univ.GetVecElem("FittedMichel_cluster_view", ci); // 1 = X view, 2 = U view, 3 = V view + energy = univ.GetVecElem("cluster_energy", ci); // MeV + time = univ.GetVecElem("cluster_time", ci) / pow(10, 3); // microseconds + pos = univ.GetVecElem("cluster_pos", ci); // in mm + zpos = univ.GetVecElem("cluster_z", ci); // in mm + view = univ.GetVecElem("cluster_view", ci); // 1 = X view, 2 = U view, 3 = V view } #endif // CLUSTER_H diff --git a/includes/Cuts.cxx b/includes/Cuts.cxx index 74a18e8..f2388ec 100644 --- a/includes/Cuts.cxx +++ b/includes/Cuts.cxx @@ -187,6 +187,9 @@ bool PassesCut(const CVUniverse& univ, const ECuts cut, const bool is_mc, case kWexp: return WexpCut(univ, signal_definition); + case kIsoProngs: + return IsoProngCut(univ); + case kPmu: return PmuCut(univ); @@ -238,7 +241,7 @@ bool PassesCut(const CVUniverse& univ, const ECuts cut, const bool is_mc, return true; default: - std::cout << "PassesCut Error Unknown Cut!" << cut << std::endl; + std::cout << "PassesCut Error Unknown Cut!" << cut << " " << GetCutName(cut) << "\n"; return false; }; } diff --git a/includes/Michel.h b/includes/Michel.h index 16e360f..829ea57 100644 --- a/includes/Michel.h +++ b/includes/Michel.h @@ -396,7 +396,7 @@ namespace vertex { m_u2 = univ.GetVecElem("FittedMichel_michel_u2", ci); m_z2 = univ.GetVecElem("FittedMichel_michel_z2", ci); m_v2 = univ.GetVecElem("FittedMichel_michel_v2", ci); - nclusters = univ.GetInt("FittedMichel_cluster_view_sz"); + nclusters = univ.GetInt("cluster_view_sz"); overlay_fraction = univ.GetVecElem("FittedMichel_michel_datafraction", ci); true_initialx = diff --git a/includes/SignalDefinition.h b/includes/SignalDefinition.h index 40bba99..40b0518 100644 --- a/includes/SignalDefinition.h +++ b/includes/SignalDefinition.h @@ -85,7 +85,7 @@ bool IsSignal(const CVUniverse& universe, SignalDefinition signal_definition = k && universe.GetBool("truth_is_fiducial") && VtxSignal(universe) && universe.GetInt("mc_incoming") == 14 - && universe.GetDouble("truth_muon_theta") < 0.3491 // 20 deg + && universe.GetThetalepTrue() < 0.3491 // 20 deg && universe.GetWexpTrue() > 0 && universe.GetWexpTrue() < GetWCutValue(signal_definition) && n_signal_pions > 0 From 5cc6aac53f6861e8acea49aa39a5d551ae7b0691 Mon Sep 17 00:00:00 2001 From: Ben Messerly Date: Wed, 23 Mar 2022 08:52:52 -0500 Subject: [PATCH 13/17] Autoformat. --- studies/runNewMichels.C | 98 ++++++++++++++++++++++------------------- 1 file changed, 52 insertions(+), 46 deletions(-) diff --git a/studies/runNewMichels.C b/studies/runNewMichels.C index bd6aa2f..3038407 100644 --- a/studies/runNewMichels.C +++ b/studies/runNewMichels.C @@ -6,22 +6,21 @@ #include #include -#include "ccpion_common.h" // GetPlaylistFile +#include "ccpion_common.h" // GetPlaylistFile #include "includes/Binning.h" #include "includes/CCPiEvent.h" #include "includes/CVUniverse.h" -#include "includes/MacroUtil.h" #include "includes/HadronVariable.h" +#include "includes/MacroUtil.h" #include "includes/Variable.h" -#include "plotting_functions.h" - #include "includes/common_functions.h" +#include "plotting_functions.h" // Forward declare my variables because we're hiding the header. class Variable; class HadronVariable; -namespace run_new_michels{ +namespace run_new_michels { //============================================================================== // Do some event processing (e.g. make cuts, get best pion) and fill hists //============================================================================== @@ -33,10 +32,9 @@ void FillVars(CCPiEvent& event, const std::vector& variables) { if (universe->ShortName() != "cv") return; - event.m_passes_cuts = PassesCuts(event, event.m_is_w_sideband); + event.m_passes_cuts = PassesCuts(event, event.m_is_w_sideband); event.m_highest_energy_pion_idx = GetHighestEnergyPionCandidateIndex(event); - if(event.m_passes_cuts) - ccpi_event::FillStackedHists(event, variables); + if (event.m_passes_cuts) ccpi_event::FillStackedHists(event, variables); } //============================================================================== @@ -45,37 +43,42 @@ void FillVars(CCPiEvent& event, const std::vector& variables) { std::vector GetVariables() { typedef Variable Var; typedef HadronVariable HVar; - HVar* thetapi_deg = new HVar("thetapi_deg", "#theta_{#pi}", "deg", CCPi::GetBinning("thetapi_deg"), &CVUniverse::GetThetapiDeg); - Var* pmu = new Var("pmu", "p_{#mu}", "MeV", CCPi::GetBinning("pmu"), &CVUniverse::GetPmu); + HVar* thetapi_deg = + new HVar("thetapi_deg", "#theta_{#pi}", "deg", + CCPi::GetBinning("thetapi_deg"), &CVUniverse::GetThetapiDeg); + Var* pmu = new Var("pmu", "p_{#mu}", "MeV", CCPi::GetBinning("pmu"), + &CVUniverse::GetPmu); std::vector variables = {thetapi_deg, pmu}; return variables; } -} // namespace run_new_michels +} // namespace run_new_michels //============================================================================== // Loop and Fill //============================================================================== void LoopAndFill(const CCPi::MacroUtil& util, CVUniverse* universe, - const EDataMCTruth& type, - std::vector& variables) { - + const EDataMCTruth& type, std::vector& variables) { std::cout << "Loop and Fill CutVars\n"; bool is_mc, is_truth; Long64_t n_entries; SetupLoop(type, util, is_mc, is_truth, n_entries); - //for(Long64_t i_event=0; i_event < n_entries; ++i_event){ - for(Long64_t i_event=0; i_event < 5000; ++i_event){ - if (i_event%500000==0) std::cout << (i_event/1000) << "k " << std::endl; + // for(Long64_t i_event=0; i_event < n_entries; ++i_event){ + for (Long64_t i_event = 0; i_event < 5000; ++i_event) { + if (i_event % 500000 == 0) + std::cout << (i_event / 1000) << "k " << std::endl; universe->SetEntry(i_event); // For mc, get weight, check signal, and sideband CCPiEvent event(is_mc, is_truth, util.m_signal_definition, universe); - std::tie(event.m_passes_cuts, event.m_is_w_sideband, event.m_reco_pion_candidate_idxs) = PassesCuts(event); + + // PassesCuts sets these properties + std::tie(event.m_passes_cuts, event.m_is_w_sideband, + event.m_reco_pion_candidate_idxs) = PassesCuts(event); // WRITE THE FILL FUNCTION - //run_new_michels::FillVars(event, variables); - } // events + // run_new_michels::FillVars(event, variables); + } // events std::cout << "*** Done ***\n\n"; } @@ -86,24 +89,25 @@ void runNewMichels(std::string plist = "ME1A") { //========================================= // Input tuples //========================================= - bool is_mc = true; - std::string mc_file_list, data_file_list; - bool use_xrootd = true; - mc_file_list = GetPlaylistFile(plist, is_mc, use_xrootd); - is_mc = false; - data_file_list = GetPlaylistFile(plist, is_mc, use_xrootd); - + bool is_mc = true; + std::string mc_file_list, data_file_list; + bool use_xrootd = true; + mc_file_list = GetPlaylistFile(plist, is_mc, use_xrootd); + is_mc = false; + data_file_list = GetPlaylistFile(plist, is_mc, use_xrootd); + //========================================= // Init macro utility //========================================= - const int signal_definition_int = 0; - const std::string macro("runNewMichels"); - const bool is_grid = false; - const bool do_truth = false; - const bool do_systematics = false; + const int signal_definition_int = 0; + const std::string macro("runNewMichels"); + const bool is_grid = false; + const bool do_truth = false; + const bool do_systematics = false; - CCPi::MacroUtil util(signal_definition_int, mc_file_list, data_file_list, plist, do_truth, is_grid, do_systematics); - util.PrintMacroConfiguration(macro); + CCPi::MacroUtil util(signal_definition_int, mc_file_list, data_file_list, + plist, do_truth, is_grid, do_systematics); + util.PrintMacroConfiguration(macro); //========================================= // Get variables and initialize their hists @@ -115,18 +119,20 @@ void runNewMichels(std::string plist = "ME1A") { //========================================= // Loop and Fill //========================================= - LoopAndFill(util, util.m_error_bands.at("cv").at(0), kMC, variables); -/* - LoopAndFill(util, util.m_data_universe, kData, variables); - - for (auto v : variables) { - std::string tag = v->Name(); - double ymax = -1; - bool do_bwn = true; - std::cout << "Plotting" << std::endl; - PlotCutVar(v, v->m_hists.m_selection_data, v->GetStackArray(kS), util.m_data_pot, util.m_mc_pot, util.m_signal_definition, v->Name(),"SSB", ymax, do_bwn); - } -*/ + LoopAndFill(util, util.m_error_bands.at("cv").at(0), kMC, variables); + /* + LoopAndFill(util, util.m_data_universe, kData, variables); + + for (auto v : variables) { + std::string tag = v->Name(); + double ymax = -1; + bool do_bwn = true; + std::cout << "Plotting" << std::endl; + PlotCutVar(v, v->m_hists.m_selection_data, v->GetStackArray(kS), + util.m_data_pot, util.m_mc_pot, util.m_signal_definition, v->Name(),"SSB", + ymax, do_bwn); + } + */ std::cout << "Success" << std::endl; } From 737c78be45d374ac3d3e2b0d71fe58321092e3de Mon Sep 17 00:00:00 2001 From: Ben Messerly Date: Wed, 23 Mar 2022 11:30:21 -0500 Subject: [PATCH 14/17] Work in progress. Not functional. --- includes/Cuts.cxx | 179 +++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 177 insertions(+), 2 deletions(-) diff --git a/includes/Cuts.cxx b/includes/Cuts.cxx index f2388ec..d86a0e6 100644 --- a/includes/Cuts.cxx +++ b/includes/Cuts.cxx @@ -11,6 +11,57 @@ // * bool PassesCut(cut) // * PassedCuts <-- just an event counter //============================================================================== +// Passes All Cuts v4 (experimental) +// return tuple {passes_all_cuts, is_w_sideband, pion_candidate_idxs} +std::tuple> PassesCuts( + CVUniverse& universe, const bool is_mc, + const SignalDefinition signal_definition, std::vector cuts) { + //============================================================================ + // passes all cuts but w cut + //============================================================================ + endpoint::MichelMap endpoint_michels; + endpoint::MichelMap vertex_michels; + bool passes_all_but_w_cut = true; + for (auto c : GetWSidebandCuts()) { + // Set the pion candidates to the universe. The values set in early cuts + // are used for later cuts, which is why we assign them to the CVU. + universe.SetPionCandidates(GetHadIdxsFromMichels(endpoint_michels)); + + passes_all_but_w_cut = + passes_all_but_w_cut && PassesCut(universe, c, is_mc, signal_definition, + endpoint_michels, vertex_michels); + } + + //============================================================================ + // The cuts function returns a container of endpoint michels which are + // matched to hadron tracks that have passed the pion candidate cuts. From + // here on out, use the pion candidates to calculate pion quantities for this + // event-universe. + //============================================================================ + std::vector pion_candidate_idxs = + GetHadIdxsFromMichels(endpoint_michels); + + //============================================================================ + // is in the w sideband + //============================================================================ + bool is_w_sideband = passes_all_but_w_cut && + (universe.GetWexp() >= sidebands::kSidebandCutVal); + + //============================================================================ + // finally: check the w cut + //============================================================================ + // is the W cut in the cuts vector provided? + bool do_w_cut = std::find(cuts.begin(), cuts.end(), kWexp) != cuts.end(); + + bool passes_all_cuts = passes_all_but_w_cut; + if (do_w_cut) + passes_all_cuts = + passes_all_but_w_cut && WexpCut(universe, signal_definition); + + return {passes_all_cuts, is_w_sideband, pion_candidate_idxs}; +} + + // Passes All Cuts v3 (latest and greatest) // return tuple {passes_all_cuts, is_w_sideband, pion_candidate_idxs} std::tuple> PassesCuts( @@ -143,7 +194,130 @@ EventCount PassedCuts(const CVUniverse& univ, return Pass; } -// Pass Single, Given Cut +// v2 Pass Single, Given Cut +std::tuple PassesCut( + const CVUniverse& univ, const ECuts cut, const bool is_mc, + const SignalDefinition signal_definition) { + endpoint::MichelMap endpoint_michels; + vertex::MichelEvent vtx_michels; + const bool useOVMichels = false; + if (IsPrecut(cut) && !is_mc) return true; + + switch (cut) { + case kNoCuts: + return true; + + case kGoodObjects: + return univ.IsTruth() ? GoodObjectsCut(univ) : true; + + case kGoodVertex: + return univ.IsTruth() ? GoodVertexCut(univ) : true; + + case kFiducialVolume: + return univ.IsTruth() ? FiducialVolumeCut(univ) : true; + + case kMinosActivity: + return univ.IsTruth() ? MinosActivityCut(univ) : true; + + case kPrecuts: + return univ.IsTruth() ? GoodObjectsCut(univ) && GoodVertexCut(univ) && + FiducialVolumeCut(univ) + : true; + // MinosActivityCut(univ) : true; + + case kVtx: + return vtxCut(univ); + + case kMinosMatch: + return MinosMatchCut(univ); + + case kMinosCharge: + return MinosChargeCut(univ); + + case kMinosMuon: + return MinosMatchCut(univ) && MinosChargeCut(univ); + + case kWexp: + return WexpCut(univ, signal_definition); + + case kIsoProngs: + return IsoProngCut(univ); + + case kPmu: + return PmuCut(univ); + + // ==== At Least One Michel ==== + // For now, we need at least one ENDPOINT michel (any # of vtx michels). + // This cut fills our michel containers, which we use to ID pion tracks + // and subsequently make track cuts (LLR, node). + case kAtLeastOneMichel: { + endpoint::MichelMap all_michels = endpoint::GetQualityMichels(univ); + for (auto m : all_michels) { + if (m.second.had_idx == -1) + vertex_michels.insert(m); + else + endpoint_michels.insert(m); + } + vertex::MichelEvent mehreen_michels = vertex::GetQualityMichels(univ); + return endpoint_michels.size() > 0 /*|| mehreen_michels.size() = 0*/; + } + + // ==== At Least One Michel (NEW) ==== + // Going to try allowing at least one michel of either endpoint or vtx + // type. + // + // This cut fills our michel containers. + // + // Endpoint michels will be (already have been?) subject to subsequent cuts + // (LLR and Node) on their matched tracks. + // + // Vtx michels already have been subjected to Mehreen's quality cuts + case kAtLeastOneMichelv2: { + endpoint::MichelMap endpoint_michels; + vertex::MichelEvent vtx_michels; + std::tie(endpoint_michels, vtx_michels) = GetQualityMichelsv2(univ); + return + } + + case kAtLeastOnePionCandidateTrack: + return GetQualityPionCandidateIndices(univ).size() > 0; + + // If a michel's pion fails the LLR cut, remove it from the michels + case kLLR: { + ContainerEraser::erase_if(endpoint_michels, + [&univ](std::pair mm) { + return !LLRCut(univ, mm.second.had_idx); + }); + return endpoint_michels.size() > 0; + } + + // If a michel's pion fails the node cut, remove it from the michels + case kNode: { + ContainerEraser::erase_if(endpoint_michels, + [&univ](std::pair mm) { + return !NodeCut(univ, mm.second.had_idx); + }); + return endpoint_michels.size() > 0; + } + + case kPionMult: { + if (signal_definition == kOnePi || signal_definition == kOnePiNoW) + return endpoint_michels.size() == 1 && vertex_michels.size() == 0; + else + return endpoint_michels.size() >= 1; + } + + case kAllCuts: + return true; + + default: + std::cout << "PassesCut Error Unknown Cut!" << cut << " " << GetCutName(cut) << "\n"; + return false; + }; +} + +/* +// v1 Pass Single, Given Cut bool PassesCut(const CVUniverse& univ, const ECuts cut, const bool is_mc, const SignalDefinition signal_definition, endpoint::MichelMap& endpoint_michels, endpoint::MichelMap& vertex_michels) { @@ -206,7 +380,7 @@ bool PassesCut(const CVUniverse& univ, const ECuts cut, const bool is_mc, endpoint_michels.insert(m); } vertex::MichelEvent mehreen_michels = vertex::GetQualityMichels(univ); - return endpoint_michels.size() > 0 /*|| mehreen_michels.size() = 0*/; + return endpoint_michels.size() > 0; // || mehreen_michels.size() = 0; } case kAtLeastOnePionCandidateTrack: @@ -245,6 +419,7 @@ bool PassesCut(const CVUniverse& univ, const ECuts cut, const bool is_mc, return false; }; } +*/ //============================================================================== // Cut Definitions From 750d8654c6d1a833ddc7e4fa327522daaf18dd3b Mon Sep 17 00:00:00 2001 From: Ben Messerly Date: Sun, 10 Apr 2022 22:56:30 -0500 Subject: [PATCH 15/17] Big update. Cluster, Michel, and Cuts all compile now. Only CCPiEvent needs to be reviewed before all compiles. In the end I only tweaked the cutting strategy for vertex michels: still do all the exclusive cuts kLLR, etc as usual, but now first fill a separate vertex michels container, which the exclusive cuts don't affect. Then only, liek the multipi cut changed. Big restructure of Michel and cuts code. Don't modify michel containers by ref any more - only return them directly. Break out MichelEvent to be a mere data container class. Switch vectors of Michels in there to be vectors of pointers to Michels, to dodge circular dependency. Save the trackless michels to the CVU in order to calculate pion quantities from them. Otherwise a good vertex michel is coded as a pion candidate track of index -1. Big todos: 1) match michels (using clusters) between vertex and endpoint so we don't double count. 2) argument or option to turn off vertex michel analysis. Otherwise, next is fix CCPiEvent and implement test loop in studies/runNewMichel.C --- includes/CVUniverse.h | 13 +- includes/Cluster.h | 8 +- includes/Constants.h | 7 + includes/CutUtils.h | 29 +- includes/Cuts.cxx | 670 ++++++++-------- includes/Cuts.h | 38 +- includes/Michel.cxx | 1243 +++++++++++++++++++++++++++++ includes/Michel.h | 1676 ++++++--------------------------------- includes/MichelEvent.h | 46 ++ loadLibs.C | 20 +- studies/runNewMichels.C | 2 + 11 files changed, 1933 insertions(+), 1819 deletions(-) create mode 100644 includes/Michel.cxx create mode 100644 includes/MichelEvent.h diff --git a/includes/CVUniverse.h b/includes/CVUniverse.h index 23fed66..b313184 100644 --- a/includes/CVUniverse.h +++ b/includes/CVUniverse.h @@ -5,6 +5,7 @@ #include "Binning.h" // CCPi::GetBinning for ehad_nopi #include "Constants.h" // CCNuPionIncConsts, CCNuPionIncShifts, Reco/TruePionIdx +#include "MichelEvent.h" // a data/container struct #include "PlotUtils/ChainWrapper.h" #include "PlotUtils/MinervaUniverse.h" @@ -12,6 +13,7 @@ class CVUniverse : public PlotUtils::MinervaUniverse { private: // Pion Candidates - clear these when SetEntry is called std::vector m_pion_candidates; + trackless::MichelEvent m_vtx_michels; public: #include "PlotUtils/MichelFunctions.h" @@ -33,7 +35,7 @@ class CVUniverse : public PlotUtils::MinervaUniverse { virtual double GetDummyHadVar(const int x) const; // No stale cache! - virtual void OnNewEntry() override { m_pion_candidates.clear(); } + virtual void OnNewEntry() override { m_pion_candidates.clear(); m_vtx_michels = trackless::MichelEvent();} // Get and set pion candidates TruePionIdx GetHighestEnergyTruePionIndex() const; @@ -41,6 +43,13 @@ class CVUniverse : public PlotUtils::MinervaUniverse { std::vector GetPionCandidates() const; void SetPionCandidates(std::vector c); + void SetVtxMichels(const trackless::MichelEvent& m) { + m_vtx_michels = m; + } + trackless::MichelEvent GetVtxMichels() const { + return m_vtx_michels; + } + //============================================================================== // Analysis Variables //============================================================================== @@ -52,7 +61,7 @@ class CVUniverse : public PlotUtils::MinervaUniverse { virtual double GetThetamuDeg() const; // event-wide - virtual double GetEhad() const; + virtual double GetEhad() const; // relies on member m_pion_candidates virtual double GetEnu() const; virtual double GetQ2() const; virtual double GetWexp() const; diff --git a/includes/Cluster.h b/includes/Cluster.h index d202f84..f86ecdb 100644 --- a/includes/Cluster.h +++ b/includes/Cluster.h @@ -1,15 +1,19 @@ #ifndef CLUSTER_H #define CLUSTER_H +#include "CVUniverse.h" + // A cluster object will simply be the ith cluster in the event. // using ClusterMap = std::map; struct Cluster { // constructors - Cluster(const CVUniverse &univ, int &ci); + Cluster(const CVUniverse &univ, const int &ci); + Cluster(){}; int cluster_idx; + // Does this michel satisfy our quality bool is_quality; @@ -24,7 +28,7 @@ struct Cluster { double pos; }; -Cluster::Cluster(const CVUniverse &univ, int &ci) { +Cluster::Cluster(const CVUniverse &univ, const int &ci) { energy = univ.GetVecElem("cluster_energy", ci); // MeV time = univ.GetVecElem("cluster_time", ci) / pow(10, 3); // microseconds pos = univ.GetVecElem("cluster_pos", ci); // in mm diff --git a/includes/Constants.h b/includes/Constants.h index 6c659c8..f332336 100644 --- a/includes/Constants.h +++ b/includes/Constants.h @@ -46,6 +46,8 @@ enum ECuts { kIsoProngSep, kIsoProngs, kPmu, + kAtLeastOnePionCandidate, + kTrackQuality, kAllCuts, }; @@ -82,6 +84,11 @@ const int kIsVertexPion = -1; const int kEmptyPionCandidateVector = -2; const int kIsoProngCutVal = 2; +const double kPmuMinCutVal = 1.5; // GeV/c +const double kPmuMaxCutVal = 20.; // GeV/c +const double kZVtxMinCutVal = 5990.; +const double kZVtxMaxCutVal = 8340.; +const double kApothemCutVal = 850.; const bool kUseNueConstraint = true; const int kAnaNuPDG = 14; diff --git a/includes/CutUtils.h b/includes/CutUtils.h index 08bb10c..7bd6787 100644 --- a/includes/CutUtils.h +++ b/includes/CutUtils.h @@ -10,14 +10,16 @@ const std::vector kCutsVector = { kPrecuts, kVtx, kMinosMuon, - kAtLeastOnePionCandidateTrack, + kPmu, kAtLeastOneMichel, + kAtLeastOnePionCandidate, kLLR, kNode, - kWexp, // Calling this after pion candidate determined! + kTrackQuality, + kWexp, kIsoProngs, - kPionMult, - kPmu}; + kPionMult +}; // Remove W cut from cuts vector const std::vector GetWSidebandCuts() { @@ -139,9 +141,15 @@ std::string GetCutName(ECuts cut) { case kAllCuts: return "Total"; + case kTrackQuality: + return "General Track Quality"; + case kPmu: return "1.5 GeV $<$ Pmu $<$ 20 GeV"; + case kAtLeastOnePionCandidate: + return "At Least One Pion"; + default: std::cout << "ERROR: GetCutName unknown cut!" << std::endl; return ""; @@ -150,9 +158,18 @@ std::string GetCutName(ECuts cut) { // Get pion candidate indexes from michel map // (our cuts strategy enforces a 1-1 michel-pion candidate match) -std::vector GetHadIdxsFromMichels(endpoint::MichelMap michels) { +std::vector GetHadIdxsFromMichels(const endpoint::MichelMap endpoint_michels, const trackless::MichelEvent vtx_michels) { std::vector ret; - for (auto m : michels) ret.push_back(m.second.had_idx); + + // endpoint michels + for (auto m : endpoint_michels) ret.push_back(m.second.had_idx); + + // vertex michels + // When m_idx is set (i.e. != -1), then we have a good vertex michel. + // In that case, a -1 in this hadron index return vector is the code that for + // this analysis that we have a good vertex michel. + if (vtx_michels.m_idx != -1) ret.push_back(-1); + return ret; } diff --git a/includes/Cuts.cxx b/includes/Cuts.cxx index d86a0e6..dd45342 100644 --- a/includes/Cuts.cxx +++ b/includes/Cuts.cxx @@ -1,66 +1,46 @@ #ifndef Cuts_cxx #define Cuts_cxx -#include "Cuts.h" -#include "TruthCategories/Sidebands.h" // sidebands::kSidebandCutVal -#include "utilities.h" // ContainerEraser +//============================================================================ +/* +This file contains the definitions of individual reco (aka event selection) +cuts. -//============================================================================== -// Generic Pass Cut(s) Functions -// * passes, is_sideband, pion_indices = PassesCuts() -// * bool PassesCut(cut) -// * PassedCuts <-- just an event counter -//============================================================================== -// Passes All Cuts v4 (experimental) -// return tuple {passes_all_cuts, is_w_sideband, pion_candidate_idxs} -std::tuple> PassesCuts( - CVUniverse& universe, const bool is_mc, - const SignalDefinition signal_definition, std::vector cuts) { - //============================================================================ - // passes all cuts but w cut - //============================================================================ - endpoint::MichelMap endpoint_michels; - endpoint::MichelMap vertex_michels; - bool passes_all_but_w_cut = true; - for (auto c : GetWSidebandCuts()) { - // Set the pion candidates to the universe. The values set in early cuts - // are used for later cuts, which is why we assign them to the CVU. - universe.SetPionCandidates(GetHadIdxsFromMichels(endpoint_michels)); +It also contains the PassesCut(s) functions to apply all of them at once to +perform the event selection. - passes_all_but_w_cut = - passes_all_but_w_cut && PassesCut(universe, c, is_mc, signal_definition, - endpoint_michels, vertex_michels); - } +The default cuts used for the analysis are in the kCutsVector, located in +includes/CutUtils.h - //============================================================================ - // The cuts function returns a container of endpoint michels which are - // matched to hadron tracks that have passed the pion candidate cuts. From - // here on out, use the pion candidates to calculate pion quantities for this - // event-universe. - //============================================================================ - std::vector pion_candidate_idxs = - GetHadIdxsFromMichels(endpoint_michels); +A not brief note on how the "exclusive" pion cuts work: - //============================================================================ - // is in the w sideband - //============================================================================ - bool is_w_sideband = passes_all_but_w_cut && - (universe.GetWexp() >= sidebands::kSidebandCutVal); + The cuts system makes "exclusive" cuts on michels and on their associated + pion tracks. In the end, PassesCuts spits out vectors of michels that passed + the exclusive cuts. - //============================================================================ - // finally: check the w cut - //============================================================================ - // is the W cut in the cuts vector provided? - bool do_w_cut = std::find(cuts.begin(), cuts.end(), kWexp) != cuts.end(); + The way it has worked for a long time is that for the rest of the analysis + (in particular, when calculating the exclusive/pion properties of events), + we would switch from operating in michels to operating in pion track + indices. - bool passes_all_cuts = passes_all_but_w_cut; - if (do_w_cut) - passes_all_cuts = - passes_all_but_w_cut && WexpCut(universe, signal_definition); + But now we've got trackless michels. At the moment I'm restricting to one + trackless michel -- the best one. When a good trackless michel is found, + convert/code it to a pion track index of -1. When calculating pion + quantities with a track index of -1, we'll do something special to calculate + those quantities. - return {passes_all_cuts, is_w_sideband, pion_candidate_idxs}; -} + Going forward, we could want to use more than one trackless michels. In + that case, we may need to move away from calculating pion quantities + directly from track indices. And instead extract pion quantities from the + michels themselves. +*/ +//============================================================================ +#include "Cuts.h" + +#include "CutUtils.h" // GetHadIdxsFromMichels +#include "TruthCategories/Sidebands.h" // sidebands::kSidebandCutVal +#include "utilities.h" // ContainerEraser // Passes All Cuts v3 (latest and greatest) // return tuple {passes_all_cuts, is_w_sideband, pion_candidate_idxs} @@ -71,26 +51,28 @@ std::tuple> PassesCuts( // passes all cuts but w cut //============================================================================ endpoint::MichelMap endpoint_michels; - endpoint::MichelMap vertex_michels; + trackless::MichelEvent vtx_michels; bool passes_all_but_w_cut = true; for (auto c : GetWSidebandCuts()) { // Set the pion candidates to the universe. The values set in early cuts // are used for later cuts, which is why we assign them to the CVU. - universe.SetPionCandidates(GetHadIdxsFromMichels(endpoint_michels)); + universe.SetPionCandidates( + GetHadIdxsFromMichels(endpoint_michels, vtx_michels)); - passes_all_but_w_cut = - passes_all_but_w_cut && PassesCut(universe, c, is_mc, signal_definition, - endpoint_michels, vertex_michels); + bool passes_this_cut = false; + std::tie(passes_this_cut, endpoint_michels, vtx_michels) = + PassesCut(universe, c, is_mc, signal_definition); + passes_all_but_w_cut = passes_all_but_w_cut && passes_this_cut; } + universe.SetPionCandidates( + GetHadIdxsFromMichels(endpoint_michels, vtx_michels)); //============================================================================ - // The cuts function returns a container of endpoint michels which are - // matched to hadron tracks that have passed the pion candidate cuts. From - // here on out, use the pion candidates to calculate pion quantities for this - // event-universe. + // Convert michels --> tracks + // (we're done manipulating the michels, so we can do this now.) //============================================================================ std::vector pion_candidate_idxs = - GetHadIdxsFromMichels(endpoint_michels); + GetHadIdxsFromMichels(endpoint_michels, vtx_michels); //============================================================================ // is in the w sideband @@ -112,215 +94,344 @@ std::tuple> PassesCuts( return {passes_all_cuts, is_w_sideband, pion_candidate_idxs}; } -// Passes All Cuts v2 (being deprecated) -// fills stuff by reference, instead of returning -bool PassesCuts(CVUniverse& universe, std::vector& pion_candidate_idxs, - const bool is_mc, SignalDefinition signal_definition, - bool& is_w_sideband, std::vector cuts) { - // is the W cut even in the cuts vector provided? - bool do_w_cut = std::find(cuts.begin(), cuts.end(), kWexp) != cuts.end(); - - // either way, attempt to remove it - std::vector w_sideband_cuts = kCutsVector; - w_sideband_cuts.erase( - std::remove(w_sideband_cuts.begin(), w_sideband_cuts.end(), kWexp), - w_sideband_cuts.end()); - - // check passes all but w cut - bool passes_all_but_w_cut = PassesCuts(universe, pion_candidate_idxs, is_mc, - signal_definition, w_sideband_cuts); - - // is w sideband = all cuts but W && W > 1.5 - is_w_sideband = passes_all_but_w_cut && - (universe.GetWexp() >= sidebands::kSidebandCutVal); - - // Finally check all cuts == all cuts but W && W - bool passes_all_cuts = passes_all_but_w_cut; - if (do_w_cut) - passes_all_cuts = - passes_all_but_w_cut && WexpCut(universe, signal_definition); - - return passes_all_cuts; -} - -// Passes All Cuts v1 (being deprecated) -// fills by reference and doesn't check W sideband -bool PassesCuts(CVUniverse& univ, std::vector& pion_candidate_idxs, - bool is_mc, SignalDefinition signal_definition, - std::vector cuts) { - pion_candidate_idxs.clear(); - static endpoint::MichelMap endpoint_michels; - static endpoint::MichelMap - vertex_michels; // Keep track of these, but not used currently - endpoint_michels.clear(); - vertex_michels.clear(); - bool pass = true; - for (auto c : cuts) { - univ.SetPionCandidates(GetHadIdxsFromMichels( - endpoint_michels)); // Set the pion candidates to the universe - pass = pass && PassesCut(univ, c, is_mc, signal_definition, - endpoint_michels, vertex_michels); - } - - // Each endpoint michel has an associated hadron track. - // Our official pion candidates are those tracks. - pion_candidate_idxs = GetHadIdxsFromMichels(endpoint_michels); - - return pass; -} - -// Count events -EventCount PassedCuts(const CVUniverse& univ, - std::vector& pion_candidate_idxs, bool is_mc, - SignalDefinition signal_definition, - std::vector cuts) { - pion_candidate_idxs.clear(); - static endpoint::MichelMap endpoint_michels; - static endpoint::MichelMap vertex_michels; - endpoint_michels.clear(); - vertex_michels.clear(); - EventCount Pass; - bool pass = true; - for (auto cu : cuts) Pass[cu] = 0; - - for (auto c : cuts) { - pass = pass && PassesCut(univ, c, is_mc, signal_definition, - endpoint_michels, vertex_michels); - if (pass) { - Pass[c] = 1.; - } - } - - return Pass; -} - -// v2 Pass Single, Given Cut -std::tuple PassesCut( - const CVUniverse& univ, const ECuts cut, const bool is_mc, - const SignalDefinition signal_definition) { +// Pass Single, Given Cut v2 +std::tuple PassesCut( + const CVUniverse& univ, const ECuts cut, const bool is_mc, + const SignalDefinition signal_definition) { + bool pass = false; endpoint::MichelMap endpoint_michels; - vertex::MichelEvent vtx_michels; + trackless::MichelEvent vtx_michels; const bool useOVMichels = false; - if (IsPrecut(cut) && !is_mc) return true; + + if (IsPrecut(cut) && !is_mc) return {true, endpoint_michels, vtx_michels}; switch (cut) { case kNoCuts: - return true; + pass = true; + // gaudi cut AKA precut (maybe still used in MAD?) case kGoodObjects: - return univ.IsTruth() ? GoodObjectsCut(univ) : true; + pass = univ.IsTruth() ? GoodObjectsCut(univ) : true; + // gaudi cut AKA precut (maybe still used in MAD?) case kGoodVertex: - return univ.IsTruth() ? GoodVertexCut(univ) : true; + pass = univ.IsTruth() ? GoodVertexCut(univ) : true; + // gaudi cut AKA precut (probably not used in MAD) case kFiducialVolume: - return univ.IsTruth() ? FiducialVolumeCut(univ) : true; + pass = univ.IsTruth() ? FiducialVolumeCut(univ) : true; + // gaudi cut AKA precut (probably not used in MAD) case kMinosActivity: - return univ.IsTruth() ? MinosActivityCut(univ) : true; + pass = univ.IsTruth() ? MinosActivityCut(univ) : true; case kPrecuts: - return univ.IsTruth() ? GoodObjectsCut(univ) && GoodVertexCut(univ) && + pass = univ.IsTruth() ? GoodObjectsCut(univ) && GoodVertexCut(univ) && FiducialVolumeCut(univ) : true; // MinosActivityCut(univ) : true; case kVtx: - return vtxCut(univ); + pass = vtxCut(univ); case kMinosMatch: - return MinosMatchCut(univ); + pass = MinosMatchCut(univ); case kMinosCharge: - return MinosChargeCut(univ); + pass = MinosChargeCut(univ); case kMinosMuon: - return MinosMatchCut(univ) && MinosChargeCut(univ); + pass = MinosMatchCut(univ) && MinosChargeCut(univ); case kWexp: - return WexpCut(univ, signal_definition); + pass = WexpCut(univ, signal_definition); case kIsoProngs: - return IsoProngCut(univ); + pass = IsoProngCut(univ); case kPmu: - return PmuCut(univ); + pass = PmuCut(univ); - // ==== At Least One Michel ==== - // For now, we need at least one ENDPOINT michel (any # of vtx michels). - // This cut fills our michel containers, which we use to ID pion tracks - // and subsequently make track cuts (LLR, node). + // modify michels case kAtLeastOneMichel: { - endpoint::MichelMap all_michels = endpoint::GetQualityMichels(univ); - for (auto m : all_michels) { - if (m.second.had_idx == -1) - vertex_michels.insert(m); - else - endpoint_michels.insert(m); - } - vertex::MichelEvent mehreen_michels = vertex::GetQualityMichels(univ); - return endpoint_michels.size() > 0 /*|| mehreen_michels.size() = 0*/; - } - - // ==== At Least One Michel (NEW) ==== - // Going to try allowing at least one michel of either endpoint or vtx - // type. - // - // This cut fills our michel containers. - // - // Endpoint michels will be (already have been?) subject to subsequent cuts - // (LLR and Node) on their matched tracks. - // - // Vtx michels already have been subjected to Mehreen's quality cuts - case kAtLeastOneMichelv2: { - endpoint::MichelMap endpoint_michels; - vertex::MichelEvent vtx_michels; - std::tie(endpoint_michels, vtx_michels) = GetQualityMichelsv2(univ); - return + endpoint_michels = endpoint::GetQualityMichels(univ); + vtx_michels = trackless::GetQualityMichels(univ); + pass = endpoint_michels.size() > 0 || vtx_michels.m_idx != -1; } - case kAtLeastOnePionCandidateTrack: - return GetQualityPionCandidateIndices(univ).size() > 0; - + // modify michels // If a michel's pion fails the LLR cut, remove it from the michels case kLLR: { ContainerEraser::erase_if(endpoint_michels, [&univ](std::pair mm) { return !LLRCut(univ, mm.second.had_idx); }); - return endpoint_michels.size() > 0; + pass = endpoint_michels.size() > 0; } + // modify michels // If a michel's pion fails the node cut, remove it from the michels case kNode: { ContainerEraser::erase_if(endpoint_michels, [&univ](std::pair mm) { return !NodeCut(univ, mm.second.had_idx); }); - return endpoint_michels.size() > 0; + pass = endpoint_michels.size() > 0; } + // modify michels + // If a michel's pion fails track quality, remove it from the michels + case kTrackQuality: { + ContainerEraser::erase_if( + endpoint_michels, [&univ](std::pair mm) { + return !HadronQualityCuts(univ, mm.second.had_idx); + }); + pass = endpoint_michels.size() > 0; + } + + // modify michels + // the quality track, LLR, and node cuts may have removed the michels of + // failed tracks + case kAtLeastOnePionCandidate: + pass = endpoint_michels.size() > 0 || vtx_michels.m_idx != -1; + case kPionMult: { - if (signal_definition == kOnePi || signal_definition == kOnePiNoW) - return endpoint_michels.size() == 1 && vertex_michels.size() == 0; - else - return endpoint_michels.size() >= 1; + if (signal_definition == kOnePi || signal_definition == kOnePiNoW) { + pass = (endpoint_michels.size() == 1 && vtx_michels.m_idx == -1) || + (endpoint_michels.size() == 0 && vtx_michels.m_idx != -1); + } else { + pass = endpoint_michels.size() > 0 || vtx_michels.m_idx != -1; + } } + // Deprecated + case kAtLeastOnePionCandidateTrack: + pass = GetQualityPionCandidateIndices(univ).size() > 0; + case kAllCuts: - return true; + pass = true; default: - std::cout << "PassesCut Error Unknown Cut!" << cut << " " << GetCutName(cut) << "\n"; - return false; + std::cout << "PassesCut Error Unknown Cut!" << cut << " " + << GetCutName(cut) << "\n"; + pass = false; }; + + return {pass, endpoint_michels, vtx_michels}; +} + +//============================================================================== +// Cut Definitions +//============================================================================== +// Truth precuts +bool GoodObjectsCut(const CVUniverse& univ) { + return univ.GetBool("truth_reco_hasGoodObjects"); +} +bool GoodVertexCut(const CVUniverse& univ) { + return univ.GetBool("truth_reco_isGoodVertex"); +} +bool FiducialVolumeCut(const CVUniverse& univ) { + return univ.GetBool("truth_reco_isFidVol_smeared"); +} +bool MinosActivityCut(const CVUniverse& univ) { + return univ.GetInt("truth_reco_muon_is_minos_match"); +} + +// Eventwide reco cuts +bool MinosMatchCut(const CVUniverse& univ) { + return univ.GetBool("isMinosMatchTrack"); +} +// Equivalent to Brandon's, but using standard minos branches +bool MinosChargeCut(const CVUniverse& univ) { + return univ.GetDouble("MasterAnaDev_minos_trk_qp") < 0.0; +} + +bool WexpCut(const CVUniverse& univ, SignalDefinition signal_definition) { + switch (signal_definition) { + case kOnePi: + case kNPi: + return univ.GetWexp() < GetWCutValue(signal_definition); + case kOnePiNoW: + case kNPiNoW: + return true; + default: + std::cout << "WexpCut SIGNAL DEF ERROR"; + return false; + } +} + +// cut on max number of iso prongs +// PrimaryBlobProngTool::makeShowerBlobProngs +bool IsoProngCut(const CVUniverse& univ) { + return univ.GetNIsoProngs() < CCNuPionIncConsts::kIsoProngCutVal; +} + +// Vtx cut for detection volume +bool vtxCut(const CVUniverse& univ) { + bool pass = true; + pass = pass && zVertexCut(univ, CCNuPionIncConsts::kZVtxMaxCutVal, + CCNuPionIncConsts::kZVtxMaxCutVal); + pass = pass && XYVertexCut(univ, CCNuPionIncConsts::kApothemCutVal); + return pass; +} + +bool zVertexCut(const CVUniverse& univ, const double upZ, const double downZ) { + double vtxZ = univ.GetVecElem("vtx", 2); + if (vtxZ > downZ && vtxZ < upZ) + return true; + else + return false; +} + +bool XYVertexCut(const CVUniverse& univ, const double a) { + const double x = univ.GetVecElem("vtx", 0), y = univ.GetVecElem("vtx", 1); + if (x < 0) { + if (x > -a && univ.leftlinesCut(a, x, y)) + return true; + else + return false; + } else { + if (x < a && univ.rightlinesCut(a, x, y)) + return true; + else + return false; + } +} + +bool PmuCut(const CVUniverse& univ) { + double pmu = univ.GetPmu() / 1000.; + return CCNuPionIncConsts::kPmuMinCutVal < pmu && + pmu < CCNuPionIncConsts::kPmuMaxCutVal; +} + +// Exclusive - cuts on pion tracks +bool NodeCut(const CVUniverse& univ, const RecoPionIdx pidx) { + return 6. < univ.GetEnode01(pidx) && univ.GetEnode01(pidx) < 32. && + 2. < univ.GetEnode2(pidx) && univ.GetEnode2(pidx) < 22. && + 0. < univ.GetEnode3(pidx) && univ.GetEnode3(pidx) < 19. && + 0. < univ.GetEnode4(pidx) && univ.GetEnode4(pidx) < 31. && + 0. < univ.GetEnode5(pidx) && univ.GetEnode5(pidx) < 60.; +} + +bool LLRCut(const CVUniverse& univ, const RecoPionIdx pidx) { + // if (pidx < 0) return false; + // else return univ.GetLLRScore(pidx) > 0.; + return univ.GetLLRScore(pidx) > 0.; +} + +bool HadronQualityCuts(const CVUniverse& univ, const RecoPionIdx pidx) { + return univ.GetVecElem("MasterAnaDev_hadron_isForked", pidx) == 0 && + univ.GetVecElem("MasterAnaDev_hadron_isExiting", pidx) == 0 && + univ.GetVecElem("MasterAnaDev_hadron_isSideECAL", pidx) == 0 && + univ.GetVecElem("MasterAnaDev_hadron_isODMatch", pidx) == 0 && + univ.GetVecElem("MasterAnaDev_hadron_isTracker", pidx) == 1; +}; + +//============================================================================== +// Helper +//============================================================================== +// Get candidate pions that pass the minimal HadronQualityCuts +std::vector GetQualityPionCandidateIndices(const CVUniverse& univ) { + std::vector pion_candidate_indices; + int n_hadrons = univ.GetInt("MasterAnaDev_hadron_number"); + for (int i_hadron = 0; i_hadron != n_hadrons; ++i_hadron) + if (HadronQualityCuts(univ, i_hadron)) + pion_candidate_indices.push_back(i_hadron); + return pion_candidate_indices; +} + +// Count events +EventCount PassedCuts(const CVUniverse& univ, + std::vector& pion_candidate_idxs, bool is_mc, + SignalDefinition signal_definition, + std::vector cuts) { + pion_candidate_idxs.clear(); + static endpoint::MichelMap endpoint_michels; + static endpoint::MichelMap vtx_michels; + endpoint_michels.clear(); + vtx_michels.clear(); + EventCount Pass; + bool pass = true; + for (auto cu : cuts) Pass[cu] = 0; + + for (auto c : cuts) { + pass = pass && PassesCut(univ, c, is_mc, signal_definition, + endpoint_michels, vtx_michels); + if (pass) { + Pass[c] = 1.; + } + } + + return Pass; +} + +//============================================================================== +// Retiring +//============================================================================== +// Passes All Cuts v2 (being deprecated) +// fills stuff by reference, instead of returning +bool PassesCuts(CVUniverse& universe, std::vector& pion_candidate_idxs, + const bool is_mc, SignalDefinition signal_definition, + bool& is_w_sideband, std::vector cuts) { + // is the W cut even in the cuts vector provided? + bool do_w_cut = std::find(cuts.begin(), cuts.end(), kWexp) != cuts.end(); + + // either way, attempt to remove it + std::vector w_sideband_cuts = kCutsVector; + w_sideband_cuts.erase( + std::remove(w_sideband_cuts.begin(), w_sideband_cuts.end(), kWexp), + w_sideband_cuts.end()); + + // check passes all but w cut + bool passes_all_but_w_cut = PassesCuts(universe, pion_candidate_idxs, is_mc, + signal_definition, w_sideband_cuts); + + // is w sideband = all cuts but W && W > 1.5 + is_w_sideband = passes_all_but_w_cut && + (universe.GetWexp() >= sidebands::kSidebandCutVal); + + // Finally check all cuts == all cuts but W && W + bool passes_all_cuts = passes_all_but_w_cut; + if (do_w_cut) + passes_all_cuts = + passes_all_but_w_cut && WexpCut(universe, signal_definition); + + return passes_all_cuts; +} + +// Passes All Cuts v1 (being deprecated) +// fills by reference and doesn't check W sideband +bool PassesCuts(CVUniverse& univ, std::vector& pion_candidate_idxs, + bool is_mc, SignalDefinition signal_definition, + std::vector cuts) { + pion_candidate_idxs.clear(); + static endpoint::MichelMap endpoint_michels; + static endpoint::MichelMap + vtx_michels; // Keep track of these, but not used currently + endpoint_michels.clear(); + vtx_michels.clear(); + bool pass = true; + for (auto c : cuts) { + // Set the pion candidates to the universe + // univ.SetPionCandidates(GetHadIdxsFromMichels(endpoint_michels, + // vtx_michels)); + pass = pass && PassesCut(univ, c, is_mc, signal_definition, + endpoint_michels, vtx_michels); + } + + // Each endpoint michel has an associated hadron track. + // Our official pion candidates are those tracks. + // pion_candidate_idxs = GetHadIdxsFromMichels(endpoint_michels, vtx_michels); + + return pass; } -/* // v1 Pass Single, Given Cut +// PassesCut(univ, c, is_mc, signal_definition, endpoint_michels, vtx_michels); bool PassesCut(const CVUniverse& univ, const ECuts cut, const bool is_mc, const SignalDefinition signal_definition, - endpoint::MichelMap& endpoint_michels, endpoint::MichelMap& vertex_michels) { + endpoint::MichelMap& endpoint_michels, + endpoint::MichelMap& vtx_michels) { const bool useOVMichels = false; if (IsPrecut(cut) && !is_mc) return true; @@ -375,12 +486,13 @@ bool PassesCut(const CVUniverse& univ, const ECuts cut, const bool is_mc, endpoint::MichelMap all_michels = endpoint::GetQualityMichels(univ); for (auto m : all_michels) { if (m.second.had_idx == -1) - vertex_michels.insert(m); + vtx_michels.insert(m); else endpoint_michels.insert(m); } - vertex::MichelEvent mehreen_michels = vertex::GetQualityMichels(univ); - return endpoint_michels.size() > 0; // || mehreen_michels.size() = 0; + trackless::MichelEvent mehreen_michels = + trackless::GetQualityMichels(univ); + return endpoint_michels.size() > 0; // || mehreen_michels.size() = 0; } case kAtLeastOnePionCandidateTrack: @@ -406,7 +518,7 @@ bool PassesCut(const CVUniverse& univ, const ECuts cut, const bool is_mc, case kPionMult: { if (signal_definition == kOnePi || signal_definition == kOnePiNoW) - return endpoint_michels.size() == 1 && vertex_michels.size() == 0; + return endpoint_michels.size() == 1 && vtx_michels.size() == 0; else return endpoint_michels.size() >= 1; } @@ -415,142 +527,10 @@ bool PassesCut(const CVUniverse& univ, const ECuts cut, const bool is_mc, return true; default: - std::cout << "PassesCut Error Unknown Cut!" << cut << " " << GetCutName(cut) << "\n"; + std::cout << "PassesCut Error Unknown Cut!" << cut << " " + << GetCutName(cut) << "\n"; return false; }; } -*/ - -//============================================================================== -// Cut Definitions -//============================================================================== -// Truth precuts - bool GoodObjectsCut(const CVUniverse& univ) { - return univ.GetBool("truth_reco_hasGoodObjects"); - } - bool GoodVertexCut(const CVUniverse& univ) { - return univ.GetBool("truth_reco_isGoodVertex"); - } - bool FiducialVolumeCut(const CVUniverse& univ) { - return univ.GetBool("truth_reco_isFidVol_smeared"); - } - bool MinosActivityCut(const CVUniverse& univ) { - return univ.GetInt("truth_reco_muon_is_minos_match"); - } - -// Eventwide reco cuts - bool MinosMatchCut(const CVUniverse& univ) { - return univ.GetBool("isMinosMatchTrack"); - } - // Equivalent to Brandon's, but using standard minos branches - bool MinosChargeCut(const CVUniverse& univ) { - return univ.GetDouble("MasterAnaDev_minos_trk_qp") < 0.0; - } - - bool WexpCut(const CVUniverse& univ, SignalDefinition signal_definition) { - switch (signal_definition) { - case kOnePi: - case kNPi: - return univ.GetWexp() < GetWCutValue(signal_definition); - case kOnePiNoW: - case kNPiNoW: - return true; - default: - std::cout << "WexpCut SIGNAL DEF ERROR"; - return false; - } - } - - // cut on max number of iso prongs - // PrimaryBlobProngTool::makeShowerBlobProngs - bool IsoProngCut(const CVUniverse& univ) { - return univ.GetNIsoProngs() < CCNuPionIncConsts::kIsoProngCutVal; - } - - // Vtx cut for detection volume - bool vtxCut(const CVUniverse& univ) { - bool pass = true; - pass = pass && zVertexCut(univ, 8340.0, 5990.0); - pass = pass && XYVertexCut(univ, 850.0); - return pass; - } - - bool zVertexCut(const CVUniverse& univ, const double upZ, const double downZ) { - double vtxZ = univ.GetVecElem("vtx", 2); - if (vtxZ > downZ && vtxZ < upZ) - return true; - else - return false; - } - - bool XYVertexCut(const CVUniverse& univ, const double a) { - const double x = univ.GetVecElem("vtx", 0), y = univ.GetVecElem("vtx", 1); - if (x < 0) { - if (x > -a && univ.leftlinesCut(a, x, y)) - return true; - else - return false; - } else { - if (x < a && univ.rightlinesCut(a, x, y)) - return true; - else - return false; - } - } - - bool PmuCut(const CVUniverse& univ) { - if (univ.GetPmu() / 1000 < 1.5 || 20 < univ.GetPmu() / 1000) - return false; - else - return true; - } - -// Exclusive - cuts on pion tracks - bool NodeCut(const CVUniverse& univ, const RecoPionIdx pion_candidate_idx) { - return 6. < univ.GetEnode01(pion_candidate_idx) && - univ.GetEnode01(pion_candidate_idx) < 32. && - 2. < univ.GetEnode2(pion_candidate_idx) && - univ.GetEnode2(pion_candidate_idx) < 22. && - 0. < univ.GetEnode3(pion_candidate_idx) && - univ.GetEnode3(pion_candidate_idx) < 19. && - 0. < univ.GetEnode4(pion_candidate_idx) && - univ.GetEnode4(pion_candidate_idx) < 31. && - 0. < univ.GetEnode5(pion_candidate_idx) && - univ.GetEnode5(pion_candidate_idx) < 60.; - } - - bool LLRCut(const CVUniverse& univ, const RecoPionIdx pion_candidate_idx) { - // if (pion_candidate_idx < 0) return false; - // else return univ.GetLLRScore(pion_candidate_idx) > 0.; - return univ.GetLLRScore(pion_candidate_idx) > 0.; - } - - bool HadronQualityCuts(const CVUniverse& univ, - const RecoPionIdx pion_candidate_idx) { - return univ.GetVecElem("MasterAnaDev_hadron_isForked", pion_candidate_idx) == - 0 && - univ.GetVecElem("MasterAnaDev_hadron_isExiting", pion_candidate_idx) == - 0 && - univ.GetVecElem("MasterAnaDev_hadron_isSideECAL", - pion_candidate_idx) == 0 && - univ.GetVecElem("MasterAnaDev_hadron_isODMatch", pion_candidate_idx) == - 0 && - univ.GetVecElem("MasterAnaDev_hadron_isTracker", pion_candidate_idx) == - 1; - }; - - -//============================================================================== -// Helper -//============================================================================== -// Get candidate pions that pass the minimal HadronQualityCuts -std::vector GetQualityPionCandidateIndices(const CVUniverse& univ) { - std::vector pion_candidate_indices; - int n_hadrons = univ.GetInt("MasterAnaDev_hadron_number"); - for (int i_hadron = 0; i_hadron != n_hadrons; ++i_hadron) - if (HadronQualityCuts(univ, i_hadron)) - pion_candidate_indices.push_back(i_hadron); - return pion_candidate_indices; -} #endif // Cuts_cxx diff --git a/includes/Cuts.h b/includes/Cuts.h index a0ffcaa..cb5a4dd 100644 --- a/includes/Cuts.h +++ b/includes/Cuts.h @@ -13,9 +13,9 @@ #include #include -#include "CutUtils.h" #include "CVUniverse.h" #include "Constants.h" // enum ECuts, CCNuPionIncConsts +#include "CutUtils.h" #include "Michel.h" #include "SignalDefinition.h" @@ -30,24 +30,15 @@ std::tuple> PassesCuts( CVUniverse&, const bool is_mc, const SignalDefinition, const std::vector cuts = kCutsVector); -// PassesCuts v2 (being deprecated) -bool PassesCuts(CVUniverse&, std::vector& pion_candidate_idxs, bool is_mc, - SignalDefinition, std::vector cuts = kCutsVector); - -// PassesCuts v1 (being deprecated) -bool PassesCuts(CVUniverse&, std::vector& pion_candidate_idxs, - const bool is_mc, const SignalDefinition, bool& is_w_sideband, - std::vector cuts = kCutsVector); - // Event Counter EventCount PassedCuts(const CVUniverse&, std::vector& pion_candidate_idxs, bool is_mc, SignalDefinition, std::vector cuts = kCutsVector); // Passes Single, Given Cut -bool PassesCut(const CVUniverse&, const ECuts cut, const bool is_mc, - const SignalDefinition, endpoint::MichelMap& endpoint_michels, - endpoint::MichelMap& vertex_michels); +std::tuple PassesCut( + const CVUniverse& univ, const ECuts cut, const bool is_mc, + const SignalDefinition signal_definition); //============================================================================== // Cuts Definitions @@ -80,8 +71,27 @@ bool NodeCut(const CVUniverse&, const RecoPionIdx pion_candidate_idx); // Get candidate pions that pass the minimal HadronQualityCuts std::vector GetQualityPionCandidateIndices(const CVUniverse&); -//bool AtLeastOnePionCut(const CVUniverse& univ) { +// bool AtLeastOnePionCut(const CVUniverse& univ) { // std::tuple<> GetAllMichels(); //} +//============================================================================== +// Retiring +//============================================================================== + +// PassesCuts v2 (being deprecated) +bool PassesCuts(CVUniverse&, std::vector& pion_candidate_idxs, bool is_mc, + SignalDefinition, std::vector cuts = kCutsVector); + +// PassesCuts v1 (being deprecated) +bool PassesCuts(CVUniverse&, std::vector& pion_candidate_idxs, + const bool is_mc, const SignalDefinition, bool& is_w_sideband, + std::vector cuts = kCutsVector); + +// PassesCut v1 (being deprecated) +bool PassesCut(const CVUniverse& univ, const ECuts cut, const bool is_mc, + const SignalDefinition signal_definition, + endpoint::MichelMap& endpoint_michels, + endpoint::MichelMap& vtx_michels); + #endif diff --git a/includes/Michel.cxx b/includes/Michel.cxx new file mode 100644 index 0000000..6354d1b --- /dev/null +++ b/includes/Michel.cxx @@ -0,0 +1,1243 @@ +#ifndef Michel_cxx +#define Michel_cxx + +//============================================================================== +// Two classes and helper functions for different michel electron objects +// endpoint: Aaron-style michels matched to track endpoints +// trackless: Mehreen-style michels NOT matched to track endpoints +//============================================================================== + +#include "Michel.h" + +#include + +#include "Cluster.h" +#include "MichelEvent.h" + +//============================================================================== +// Track Endpoint Michel +//============================================================================== +namespace endpoint { +// CTOR +Michel::Michel(const CVUniverse& univ, const int i, const int v) + : idx(i), vtx(v), had_idx(v - 1) { + bool isIntVtx = (vtx == 0); + // distances for fitted, 2/3-view nofit, and 1-view michels, respectively + double mm_fit_dist = univ.GetVecElem("matched_michel_end_dist", idx) / 10.; + double mm_nofit_dist = univ.GetVecElem("matched_michel_avg_dist", idx) / 10.; + double mm_ov_dist = univ.GetVecElem("matched_michel_ov_dist", idx) / 10.; + // NEW + const double FIT_CUT = isIntVtx ? 9.0 : 7.5; // cm + const double NOFIT_CUT = isIntVtx ? 10.0 : 50.; // cm + // OLD + // const double FIT_CUT = isIntVtx ? 9.0 : 5.; // cm + // const double NOFIT_CUT = isIntVtx ? 10.0 : 10.; // cm + // TODO distinguish between 2/3 view and OV + // const double FIT_CUT = isIntVtx ? 9. : 15.0; // cm + // const double NOFIT_CUT = isIntVtx ? 10. : 15.0; // cm + if (IsQualityMatchedMichel_Fit(mm_fit_dist, FIT_CUT)) { + match_category = kFit; + fit_distance = mm_fit_dist; + return; + } else if (IsQualityMatchedMichel_NoFit(mm_nofit_dist, NOFIT_CUT)) { + match_category = kNoFit; + fit_distance = mm_nofit_dist; + return; + } else if (IsQualityMatchedMichel_OneView(mm_ov_dist, NOFIT_CUT)) { + match_category = kOV; + fit_distance = mm_ov_dist; + return; + } else { + match_category = kNoMatch; + fit_distance = -2.; + return; + } +} + +// Michel quality cuts +bool IsQualityMatchedMichel_Fit(double fit_dist, double fit_cut) { + if (fit_dist >= 0 && fit_dist < fit_cut) + return true; + else + return false; +} + +bool IsQualityMatchedMichel_NoFit(double nofit_dist, double nofit_cut) { + if (nofit_dist >= 0 && nofit_dist < nofit_cut) + return true; + else + return false; +} + +bool IsQualityMatchedMichel_OneView(double ov_dist, double ov_cut) { + if (ov_dist >= 0 && ov_dist * (2.0 / 3) < ov_cut) + return true; + else + return false; +} + +// -- Given a single michel cluster matched to two vertices +// return vertex with the better-matched michel. +Michel CompareMichels(const Michel& r, const Michel& c) { + if (r.match_category > c.match_category) + return r; + else if (r.match_category < c.match_category) + return c; + else { + if (r.fit_distance < c.fit_distance) + return r; + else if (r.fit_distance > c.fit_distance) + return c; + else { + // This must mean we're comparing the same michels with the same fits. + // std::cout << "WEIRD COMPAREMICHELS PROBLEM" << std::endl; + return r; + } + } +} + +// Add michel to MichelMap. Check if this cluster has already been matched. +// Then use only the best match. +bool AddOrReplaceMichel(MichelMap& mm, const Michel& m) { + std::pair SC; + if (mm.count(m.idx) == 0) + SC = mm.insert(pair(m.idx, m)); + else { + Michel reigning_michel = mm.at(m.idx); + Michel best_michel = CompareMichels(reigning_michel, m); + mm[m.idx] = best_michel; + } + return true; +} + +//============================================================================ +// Collect the good michels in this event +/* + Get quality michels map<(int michel cluster ID, Michel)> + * A Michel is uniquely ID-ed by its cluster(of hits) integer index. + * The michel tool has already vetted the clusters themselves. + Whereas here we evaluate the quality of the fit. + * At most one interaction vertex michel (which MUST be "fitted") + * An OV michel will only be kept if no better michels are in the event. + * In the case of 2+ OV michels, only keep the best one. + * required: one-to-one michel<->vertex matching: + * when one michel cluster is matched to multiple vertices, the vtx + with the better match is chosen. + * We don't have the problem in the other direction -- michel tool: a + vertex cannot be matched to more than one cluster. + * At the moment, we can return a single michel that is a quality + interaction vertex michel. We'd like to call these signal, but we don't + know the pion energy...yet. In the meantime, cut them. +*/ +//============================================================================ +MichelMap GetQualityMichels(const CVUniverse& univ) { + std::map ret_michels; + std::vector matched_michel_idxs = univ.GetVec("matched_michel_idx"); + + // Loop vertices in the event, i.e. the indices of the michel index vector + for (uint vtx = 0; vtx < matched_michel_idxs.size(); ++vtx) { + int mm_idx = matched_michel_idxs[vtx]; + + // NO MATCH -- GO TO NEXT VTX. No michel cluster matched to this vertex. + if (mm_idx < 0) continue; + + // MICHEL CONSTRUCTOR + // Set match category (e.g. fitted, one-view, etc), match distance. + Michel current_michel = Michel(univ, mm_idx, vtx); + + // MICHEL MATCH QUALITY CUT -- NEXT VTX + // A michel cluster was matched to this vertex, but the match doesn't + // pass match quality cuts. + if (current_michel.match_category == Michel::kNoMatch) continue; + + // VERTEX MICHEL -- must meet the gold standard for its fit + bool isIntVtx = (vtx == 0); + if (isIntVtx && current_michel.match_category <= Michel::kNoFit) { + continue; + } + // ENDPOINT MICHELS + else { + // ZERO MICHELS FOUND SO FAR + if (ret_michels.size() == 0) + ; + + // ONE MICHEL FOUND SO FAR + // -- If either the michel we have already or this michel is OV, pick + // the better of the two. Only one will remain. + else if (ret_michels.size() == 1) { + Michel reigning_michel = (ret_michels.begin())->second; + if (reigning_michel.match_category == Michel::kOV || + current_michel.match_category == Michel::kOV) + ret_michels.clear(); + current_michel = CompareMichels(reigning_michel, current_michel); + } + + // 2+ MICHELS FOUND SO FAR + else { + if (current_michel.match_category == Michel::kOV) continue; + } + } + + // ADD THIS MICHEL + // When a cluster is matched to two vertices, pick the vtx with the + // better match. + bool SC = AddOrReplaceMichel(ret_michels, current_michel); + } // end vtx loop + return ret_michels; +} +} // namespace endpoint + +//============================================================================== +// Trackless Michel +//============================================================================== +namespace trackless { +// CTOR +Michel::Michel(const CVUniverse& univ, const int ci) { + energy = univ.GetVecElem("FittedMichel_michel_energy", ci); + time = univ.GetVecElem("FittedMichel_michel_time", ci) / pow(10, 3); + is_fitted = univ.GetVecElem("FittedMichel_michel_fitPass", ci); + up_location.push_back(univ.GetVecElem("FittedMichel_michel_x1", ci)); + up_location.push_back(univ.GetVecElem("FittedMichel_michel_u1", ci)); + up_location.push_back(univ.GetVecElem("FittedMichel_michel_v1", ci)); + up_location.push_back(univ.GetVecElem("FittedMichel_michel_z1", ci)); + down_location.push_back(univ.GetVecElem("FittedMichel_michel_x2", ci)); + down_location.push_back(univ.GetVecElem("FittedMichel_michel_u2", ci)); + down_location.push_back(univ.GetVecElem("FittedMichel_michel_v2", ci)); + down_location.push_back(univ.GetVecElem("FittedMichel_michel_z2", ci)); + m_x1 = univ.GetVecElem("FittedMichel_michel_x1", ci); + m_y1 = univ.GetVecElem("FittedMichel_michel_y1", ci); + m_u1 = univ.GetVecElem("FittedMichel_michel_u1", ci); + m_v1 = univ.GetVecElem("FittedMichel_michel_v1", ci); + m_z1 = univ.GetVecElem("FittedMichel_michel_z1", ci); + m_x2 = univ.GetVecElem("FittedMichel_michel_x2", ci); + m_y2 = univ.GetVecElem("FittedMichel_michel_y2", ci); + m_u2 = univ.GetVecElem("FittedMichel_michel_u2", ci); + m_z2 = univ.GetVecElem("FittedMichel_michel_z2", ci); + m_v2 = univ.GetVecElem("FittedMichel_michel_v2", ci); + nclusters = univ.GetInt("cluster_view_sz"); + overlay_fraction = univ.GetVecElem("FittedMichel_michel_datafraction", ci); + + true_initialx = + univ.GetVecElem("FittedMichel_reco_micheltrajectory_initialx", ci); + true_initialy = + univ.GetVecElem("FittedMichel_reco_micheltrajectory_initialy", ci); + true_initialz = + univ.GetVecElem("FittedMichel_reco_micheltrajectory_initialz", ci); + is_overlay = univ.GetVecElem("FittedMichel_michel_isoverlay", ci); + true_e = univ.GetVecElem("FittedMichel_reco_micheltrajectory_energy", ci); + true_pdg = univ.GetVecElem("FittedMichel_reco_micheltrajectory_pdg", ci); + true_parentpdg = univ.GetVecElem("FittedMichel_true_primaryparent_pdg", ci); + true_parentid = + univ.GetVecElem("FittedMichel_true_primaryparent_trackID", ci); + true_p = univ.GetVecElem("FittedMichel_reco_micheltrajectory_momentum", ci); + + double true_parentp = + univ.GetVecElem("FittedMichel_true_primaryparent_momentum", ci); + double true_parente = + univ.GetVecElem("FittedMichel_true_primaryparent_energy", ci); + double mass = mass = sqrt(pow(true_parente, 2) - pow(true_parentp, 2)); + pionKE = true_parente - mass; + + double true_parentpx = + univ.GetVecElem("FittedMichel_true_primaryparent_momentumx", ci); + double true_parentpy = + univ.GetVecElem("FittedMichel_true_primaryparent_momentumy", ci); + double true_parentpz = + univ.GetVecElem("FittedMichel_true_primaryparent_momentumz", ci); + + TVector3 truep(true_parentpx, true_parentpy, true_parentpz); + double true_theta = truep.Theta(); + true_angle = true_theta; //*TMath::RadToDeg(); + + // if (overlay_fraction < 0.5) std::cout << "True Parent of Michel is PDG: " + // << true_parentpdg << " And Parent trackID: " << true_parentid << + // std::endl; + double end1diff = + abs(true_initialz - + m_z1); // This gives a value for determining how close the + // reconstructed endpoint of the michel is to the true intial + // endpoint (the start point of where the michel decayed from) + double end2diff = + abs(true_initialz - + m_z2); // this is for endpoint 2. If you compare this to the endpoint + // that gets matched to a verted or cluster, you can determine + // which type of match ends up getting correcct matches or + // wrong matches. + + if (overlay_fraction > 0.5) + trueEndpoint = 0; + else if (true_parentpdg == 211 && end1diff < end2diff) + trueEndpoint = 1; + else if (true_parentpdg == 211 && end2diff < end1diff) + trueEndpoint = 2; + if (is_fitted == 1) { // Do theMatching for Fitted Michels + DoesMichelMatchVtx(univ); // GEts info for Vtx Match + DoesMichelMatchClus(univ); // Gets info for ClusterMatch + GetBestMatch(); + GetPionAngle(univ); + } +} + +// Get the angle between Michel endpoint that was matched and the vertex +void Michel::GetPionAngle(const CVUniverse& univ) { + double vtx_x = univ.GetVertex().X(); // mm + double vtx_y = univ.GetVertex().Y(); // mm + double vtx_z = univ.GetVertex().Z(); // mm + + TVector3 vtx(vtx_x, vtx_y, vtx_z); + TVector3 endpoint; + + if (this->BestMatch == 1 || this->BestMatch == 3) + endpoint.SetXYZ(this->m_x1, this->m_y1, this->m_z1); + else if (this->BestMatch == 2 || this->BestMatch == 4) + endpoint.SetXYZ(this->m_x2, this->m_y2, this->m_z2); + else + endpoint.SetXYZ(9999., 9999., 9999.); + + TVector3 range = endpoint - vtx; + double angle = univ.thetaWRTBeam(range.x(), range.y(), range.z()); + this->best_angle = angle; //*TMath::RadToDeg(); // in Degrees +} + +// This function will get an integer for the best match type of the Michel. +// It compares distance between MIchel and whatever it's best match is to find +// the Best type of Michel for a single Michel. +void Michel::GetBestMatch() { + int upvtxmatch = 0; + int downvtxmatch = 0; + int upclusmatch = 0; + int downclusmatch = 0; + + // This is setting the values for which endpoint is a better match for each + // type. + // + // TODO: Revise this function. There has to be a better way to compare + // distances than what I wrote. + std::vector distances{ + this->up_to_vertex_dist3D, this->down_to_vertex_dist3D, + this->up_clus_michel_dist3D, this->down_clus_michel_dist3D}; + std::sort(distances.begin(), distances.end()); + + // This bit of code will try to find the best 3D distance end point + if (distances[0] == this->up_to_vertex_dist3D) { + upvtxmatch = 1; + this->best_XZ = this->up_to_vertex_XZ; + this->best_UZ = this->up_to_vertex_UZ; + this->best_VZ = this->up_to_vertex_VZ; + this->BestMatch = 1; + this->Best3Ddist = this->up_to_vertex_dist3D; + // std::cout << "This Michel is UPVTX and has true endpoint " << + // this->trueEndpoint << std::endl; + } else if (distances[0] == this->down_to_vertex_dist3D) { + this->BestMatch = 2; + this->best_XZ = this->down_to_vertex_XZ; + this->best_UZ = this->down_to_vertex_UZ; + this->best_VZ = this->down_to_vertex_VZ; + this->Best3Ddist = this->down_to_vertex_dist3D; + downvtxmatch = 1; + // std::cout << "This Michel is DOWNVTX and has true endpoint " << + // this->trueEndpoint << std::endl; + } else if (distances[0] == this->up_clus_michel_dist3D) { + this->BestMatch = 3; + this->best_XZ = this->up_to_clus_XZ; + this->best_UZ = this->up_to_clus_VZ; + this->best_VZ = this->up_to_clus_UZ; + this->Best3Ddist = this->up_clus_michvtx_dist3D; + upclusmatch = 1; + // std::cout << "This Michel is UPCLUS and has true endpoint " << + // this->trueEndpoint << std::endl; + } else if (distances[0] == this->down_clus_michel_dist3D) { + this->BestMatch = 4; + this->Best3Ddist = this->down_clus_michvtx_dist3D; + this->best_XZ = this->down_to_clus_XZ; + this->best_UZ = this->down_to_clus_UZ; + this->best_VZ = this->down_to_clus_VZ; + downclusmatch = 1; + // std::cout << "This Michel is DOWNCLUS and has true endpoint " << + // this->trueEndpoint << std::endl; + } else { + this->BestMatch = 0; + this->Best3Ddist = 9999.; + this->best_XZ = 9999.; + this->best_UZ = 9999.; + this->best_VZ = 9999.; + } + + // Second best + if (distances[1] == this->up_to_vertex_dist3D) + this->SecondBestMatch = 1; + else if (distances[1] == this->down_to_vertex_dist3D) + this->SecondBestMatch = 2; + else if (distances[1] == this->up_clus_michel_dist3D) + this->SecondBestMatch = 3; + else if (distances[1] == this->down_clus_michel_dist3D) + this->SecondBestMatch = 4; + else { + this->SecondBestMatch = 0; + } + int matchtype = this->BestMatch; + // Identifying the best reco endpoint based on the Best MAtch type. + if (matchtype == 1 || matchtype == 3) + this->recoEndpoint = 1; + else if (matchtype == 2 || matchtype == 4) + this->recoEndpoint = 2; +} + +// sets and reads properties of this +void Michel::DoesMichelMatchVtx(const CVUniverse& univ) { + // std::cout << "GETTING VTX MATCH FOR MICHEL " << std::endl; + + // Getting Vertex Information + double vtx_x = univ.GetVertex().X(); // mm + double vtx_y = univ.GetVertex().Y(); // mm + double vtx_z = univ.GetVertex().Z(); // mm + double vtx_t = univ.GetVertex().T() / pow(10, 3); // mus + double vtx_u = (0.5 * (vtx_x - sqrt(3.) * vtx_y)); + double vtx_v = (0.5 * (vtx_x + sqrt(3.) * vtx_y)); + + // std::cout << "VTX POSITION is (x, u , v, y, z) (" << vtx_x << " , " << + // vtx_u << " , " << vtx_v << " , " << vtx_y << " , " << vtx_z << std::endl; + // Initializing all the distance comparisons I will need to make + double zdiff1 = vtx_z - this->m_z1; + double zdiff2 = vtx_z - this->m_z2; + double xdiff = 9999.; + double udiff = 9999.; + double vdiff = 9999.; + double XZdist = 9999.; + double UZdist = 9999.; + double VZdist = 9999.; + + double michely1 = this->m_y1; + double michely2 = this->m_y2; + double michelx1 = this->m_x1; + double michelx2 = this->m_z2; + double michelz1 = this->m_z1; + double michelz2 = this->m_z2; + double timediff = (this->time) - vtx_t; + // std::cout << "Michel time " << this->time << " Vertex time " << vtx_t << + // "\n" << std::endl; + this->vtx_michel_timediff = timediff; + + // 2D distance calculations for Endpoint 1 + xdiff = abs(vtx_x - this->m_x1); + udiff = abs(vtx_u - this->m_u1); + vdiff = abs(vtx_v - this->m_v1); + + XZdist = sqrt(xdiff * xdiff + zdiff1 * zdiff1); + UZdist = sqrt(udiff * udiff + zdiff1 * zdiff1); + VZdist = sqrt(vdiff * vdiff + zdiff1 * zdiff1); + + this->up_to_vertex_XZ = XZdist; + this->up_to_vertex_UZ = UZdist; + this->up_to_vertex_VZ = VZdist; + + // 2D Distance calculations for endpoint2 + xdiff = abs(vtx_x - this->m_x2); + udiff = abs(vtx_u - this->m_u2); + vdiff = abs(vtx_v - this->m_v2); + XZdist = sqrt(xdiff * xdiff + zdiff2 * zdiff2); + UZdist = sqrt(udiff * udiff + zdiff2 * zdiff2); + VZdist = sqrt(vdiff * vdiff + zdiff2 * zdiff2); + + this->down_to_vertex_XZ = XZdist; + this->down_to_vertex_UZ = UZdist; + this->down_to_vertex_VZ = VZdist; + + // 3D distance calculations + double xdiff1 = abs(vtx_x - this->m_x1); + double xdiff2 = abs(vtx_x - this->m_x2); + double ydiff1 = abs(vtx_y - this->m_y1); + double ydiff2 = abs(vtx_y - this->m_y2); + + double dist1 = sqrt(zdiff1 * zdiff1 + xdiff1 * xdiff1 + ydiff1 * ydiff1); + double dist2 = sqrt(zdiff2 * zdiff2 + xdiff2 * xdiff2 + ydiff2 * ydiff2); + + this->up_to_vertex_dist3D = dist1; + this->down_to_vertex_dist3D = dist2; + + if (dist1 < dist2) + this->vtx_endpoint = 1; + else if (dist2 < dist1) + this->vtx_endpoint = 2; + + ////std::cout << "END OF LOOP TO FIND VTX MATCH" << std::endl; +} + +// sets and reads properties of this +void Michel::DoesMichelMatchClus(const CVUniverse& univ) { + // This is where the function for Cluster Matching goes + + ////std::cout << "STARTING SEARCH FOR CLUSTER MATCH " << std::endl; + // Inititalizing vertex variables needed for cluster matching + int nclusters = this->nclusters; + // std::cout << "There are " << nclusters << " available clusters for this + // matching " << std::endl; + double vtx_x = univ.GetVertex().X(); // mm + double vtx_y = univ.GetVertex().Y(); // mm + double vtx_z = univ.GetVertex().Z(); // mm + double vtx_t = univ.GetVertex().T() / pow(10, 3); // mus + double vtx_u = (0.5 * (vtx_x - sqrt(3.) * vtx_y)); + double vtx_v = (0.5 * (vtx_x + sqrt(3.) * vtx_y)); + + double closestdistance1x = 9999.; + double closestdistance1u = 9999.; + double closestdistance1v = 9999.; + double closestdistance1z = 9999.; + + double closestdistance2x = 9999.; + double closestdistance2u = 9999.; + double closestdistance2v = 9999.; + double closestdistance2z = 9999.; + + double michelx1 = this->m_x1; + double michelx2 = this->m_x2; + double michelu1 = this->m_u1; + double michelu2 = this->m_u2; + double michelv1 = this->m_v1; + double michelv2 = this->m_v2; + double michelz1 = this->m_z1; + double michelz2 = this->m_z2; + double michely1 = this->m_y1; + double michely2 = this->m_y2; + + double micheltime = this->time; + + // std::cout << "Michel position 1 is (x, u, v, y, z) " << michelx1 << " , " + // << michelu1 << " , " << michelv1 << " , "<< michely1 << " , " << michelz1 + // << std::endl; + + // std::cout << "Michel position 2 is (x, y, v, y, z) " << michelx2 << " , " + // << michelu2 << " , " << michelv2 << " , " << michely2 << " , " << michelz2 + // << std::endl; + + std::vector endpoint1_clus; + std::vector endpoint2_clus; + + // Get the closest distance for each view + + ////std::cout << "STARTING LOOP OVER CLUSTERS " << std::endl; + int x1_idx = -1; // want to save the index for each closest cluster + int u1_idx = -1; + int v1_idx = -1; + int x2_idx = -1; + int u2_idx = -1; + int v2_idx = -1; + + for (int i = 0; i < nclusters; i++) { + Cluster current_cluster = Cluster(univ, i); + + double energy = current_cluster.energy; + double time = current_cluster.time; + double pos = current_cluster.pos; + double zpos = current_cluster.zpos; + int view = current_cluster.view; + double timediff = micheltime - time; + + if (energy < 2.) continue; // only get clusters greater than 2 MeV + + // std::cout << "printing cluster info " << "energy " << energy << " time " + // << time << " pos " << pos << " zpos " << zpos << std::endl; + + double zdiff1 = abs(zpos - michelz1); + double zdiff2 = abs(zpos - michelz2); + // Calculating 2D distance in X view + if (view == 1) { + // Endpoint 1 calculations + double xdiff1 = abs(pos - michelx1); + + double x2Ddistance1 = sqrt(xdiff1 * xdiff1 + zdiff1 * zdiff1); + + // Endpoint 2 Calculations + double xdiff2 = abs(pos - michelx2); + + double x2Ddistance2 = sqrt(xdiff2 * xdiff2 + zdiff2 * zdiff2); + + if (x2Ddistance1 <= closestdistance1x) { + closestdistance1x = + x2Ddistance1; // this is redundant if I just use index instead + x1_idx = i; + } + if (x2Ddistance2 <= closestdistance2x) { + closestdistance2x = x2Ddistance2; + x2_idx = i; + } + } else if (view == 2) // Calculating 2D distance in U view + { + // Endpoint 2 Calculations + double udiff1 = abs(pos - michelu1); + + double u2Ddistance1 = sqrt(udiff1 * udiff1 + zdiff1 * zdiff1); + + // Endpoint 1 Calculations + double udiff2 = abs(pos - michelu2); + + double u2Ddistance2 = sqrt(udiff2 * udiff2 + zdiff2 * zdiff2); + + if (u2Ddistance1 < closestdistance1u) { + closestdistance1u = u2Ddistance1; + u1_idx = i; + } + if (u2Ddistance2 < closestdistance2u) { + closestdistance2u = u2Ddistance2; + u2_idx = i; + } + + } else if (view == 3) // Calculating 2D dsitance in V view + { + // Endpoint 1 Calculations + double vdiff1 = abs(pos - michelv1); + + double v2Ddistance1 = sqrt(vdiff1 * vdiff1 + zdiff1 * zdiff1); + // Endpoint 2 Calculations + double vdiff2 = abs(pos - michelv2); + + double v2Ddistance2 = sqrt(vdiff2 * vdiff2 + zdiff2 * zdiff2); + + if (v2Ddistance1 < closestdistance1v) { + closestdistance1v = v2Ddistance1; + v1_idx = i; + } + if (v2Ddistance2 <= closestdistance2v) { + closestdistance2v = v2Ddistance2; + v2_idx = i; + } + } + } + + // std::cout << "Printing closest clusters index to each end point: x1: " << + // x1_idx << " u1: " << u1_idx << " v1: " << v1_idx << " x2: " << x2_idx << " + // u2: " << u2_idx << " v2: " << v2_idx << std::endl; + + // Now store the closest X, u, v clusters for each Michel Endpoint based on + // the above closest distance + + // Closest cluster's index will be used to only + + std::vector clusx1; + std::vector clusx2; + + std::vector clusu1; + std::vector clusu2; + + std::vector clusv1; + std::vector clusv2; + for (int i = 0; i < nclusters; i++) { + Cluster current_cluster = Cluster(univ, i); + + double energy = current_cluster.energy; + double time = current_cluster.time; + double pos = current_cluster.pos; + double zpos = current_cluster.zpos; + int view = current_cluster.view; + double timediff = micheltime - time; + if (energy < 2.) continue; + // std::cout << "Printing details about cluster "<< i << " : " << energy + // << " : " << time << " : " << pos << " : " << zpos << " : " << view << " + // : " << timediff << std::endl; + + double zdiff1 = abs(zpos - michelz1); + double zdiff2 = abs(zpos - michelz2); + + // Saving clusters with distances equal to the closest clusters. Again, this + // is probably not the best way. I need to rewrite this section to just use + // clusters from the index I saved in the previous loop. That way I reduce + // the number of clusters that I have to loop over. + + if (view == 1) { + // Endpoint 1 + + if (i == x1_idx) { // if current index is same as the closest endpoint 1 + // cluster in X View + endpoint1_clus.push_back(¤t_cluster); + this->cluster_to_up_match.push_back(¤t_cluster); + // this one is redundant + // std::cout << "Printing details about Endpoint + // 1 X cluster index "<< i << " energy : " << + // energy << " time : " << time << " pos : " << + // pos << " zpos : " << zpos << " view : " << + // view + // << " timedifference with Michel : " << + // timediff + // << std::endl; + + clusx1.push_back(pos); + clusx1.push_back(zpos); + } + if (i == x2_idx) { // if current index is same as the closest endpoint 2 + // cluster in X View + endpoint2_clus.push_back(¤t_cluster); + this->cluster_to_down_match.push_back(¤t_cluster); // TODO remove + // std::cout << "Printing details about Endpoint 2 X cluster "<< i << " + // energy : " << energy << " time : " << time << " pos : " << pos << " + // zpos: " << zpos << " view : " << view << " timediff : " << timediff + // << std::endl; + + clusx2.push_back(pos); + clusx2.push_back(zpos); + } + + } else if (view == 2) { + if (i == u1_idx) { // if current index is same as the closest endpoint 1 + // cluster in U View + endpoint1_clus.push_back(¤t_cluster); + this->cluster_to_up_match.push_back(¤t_cluster); // Remove? + // std::cout << "Printing details about Endpoint 1 U cluster "<< i << " + // : " << energy << " : " << time << " : " << pos << " : " << zpos << " + // : " << view << " : " << timediff << std::endl; + + clusu1.push_back(pos); + clusu1.push_back(zpos); + } + // if current index is same as the closest endpoint 2 cluster in U View + if (i == u2_idx) { + endpoint2_clus.push_back(¤t_cluster); + this->cluster_to_down_match.push_back(¤t_cluster); // remove? + // std::cout << "Printing details about Endpoint 2 U cluster "<< i << " + // : " << energy << " : " << time << " : " << pos << " : " << zpos << " + // : " << view << " : " << timediff << std::endl; + + clusu2.push_back(pos); + clusu2.push_back(zpos); + } + } else if (view == 3) { + if (i == v1_idx) { // if current index is same as the closest endpoint 1 + // cluster in V View + endpoint1_clus.push_back(¤t_cluster); + this->cluster_to_up_match.push_back(¤t_cluster); // remove? + // std::cout << "Printing details about Endpoint 1 V cluster "<< i << " + // : " << energy << " : " << time << " : " << pos << " : " << zpos << " + // : " << view << " : " << timediff << std::endl; + + clusv1.push_back(pos); + clusv1.push_back(zpos); + } + if (i == v2_idx) { // if current index is same as the closest endpoint 2 + // cluster in V View + endpoint1_clus.push_back(¤t_cluster); + this->cluster_to_down_match.push_back(¤t_cluster); // remove? + // std::cout << "Printing details about Endpoint 2 V cluster "<< i << " + // : " << energy << " : " << time << " : " << pos << " : " << zpos << " + // : " << view << " : " << timediff << std::endl; + + clusv2.push_back(pos); + clusv2.push_back(zpos); + } + } + + } // End of loop over clusters + + // This is vector of positions for each endpoint cluster match + std::vector matchclus1; // index [0] = x, [1] = y, [2] = z + std::vector matchclus2; // index [0] = x, [1] = y, [2] = z + + // std::cout << "LOOPING OVER ENDPOINT1 CLUSTERS" << std::endl; + + double XZdist1 = 9999.; + double UZdist1 = 9999.; + double VZdist1 = 9999.; + + // Check if our cluster vectors are empty and calculate 2D distances for + // endpoint 1 + if (!clusx1.empty()) { + double xdif = abs(this->m_x1 - clusx1[0]); + double zdif = abs(this->m_z1 - clusx1[1]); + XZdist1 = sqrt(xdif * xdif + zdif * zdif); + } + if (!clusu1.empty()) { + double udif = abs(this->m_u1 - clusu1[0]); + double zdif = abs(this->m_z1 - clusu1[1]); + UZdist1 = sqrt(udif * udif + zdif * zdif); + } + if (!clusv1.empty()) { + double vdif = abs(this->m_v1 - clusv1[0]); + double zdif = abs(this->m_z1 - clusv1[1]); + VZdist1 = sqrt(vdif * vdif + zdif * zdif); + } + + // std::cout << " XZ, UZ, VZ 1: " << XZdist1 << " , " << UZdist1 << " , " << + // VZdist1 << std::endl; Saving the 2D distances for endpoint 1 + this->up_to_clus_XZ = XZdist1; + this->up_to_clus_UZ = UZdist1; + this->up_to_clus_VZ = VZdist1; + + double XZdist2 = 9999.; + double UZdist2 = 9999.; + double VZdist2 = 9999.; + + if (!clusx2.empty()) { + double xdif = abs(this->m_x2 - clusx2[0]); + double zdif = abs(this->m_z2 - clusx2[1]); + XZdist2 = sqrt(xdif * xdif + zdif * zdif); + } + if (!clusu2.empty()) { + double udif = abs(this->m_u2 - clusu2[0]); + double zdif = abs(this->m_z2 - clusu2[1]); + UZdist2 = sqrt(udif * udif + zdif * zdif); + } + if (!clusv2.empty()) { + double vdif = abs(this->m_v2 - clusv2[0]); + double zdif = abs(this->m_z2 - clusv2[1]); + VZdist2 = sqrt(vdif * vdif + zdif * zdif); + } + + this->down_to_clus_XZ = XZdist2; + this->down_to_clus_UZ = UZdist2; + this->down_to_clus_VZ = VZdist2; + this->down_clus_y = michely2; + this->down_clus_x = michelx2; + this->down_clus_z = michelz2; + + // std::cout << "GET 3D Information for Clusters - ENDPOINT1" << std::endl; + // This is the convoluted system that calculates the Endpoint + if (XZdist1 < UZdist1 && XZdist1 < VZdist1 && + UZdist1 < VZdist1) { // XU views closest + if (!clusu1.empty() && !clusx1.empty()) { + double yclus = (1. / sqrt(3.)) * (clusx1[0] - 2 * clusu1[0]); + matchclus1.push_back(clusx1[0]); + matchclus1.push_back(yclus); // y point of match 3D point + matchclus1.push_back(clusx1[1]); // setting the cluster 3D point z to be + // of the closest view + // std::cout << "The 2 closest clusters to EndPoint 1 are X and U with (x, + // y, z) point ( " << clusx1[0] << " , " << yclus << " , " << clusx1[1] << + // " ) " << std::endl; + } + } else if (XZdist1 < UZdist1 && XZdist1 < VZdist1 && + UZdist1 > VZdist1) { // XV closest + if (!clusv1.empty() && !clusx1.empty()) { + double yclus = (1. / sqrt(3.)) * (2 * clusv1[0] - clusx1[0]); + // std::cout << "The 2 closest clusters to Endpoint 1 are X and V with (x, + // y, z) point ( " << clusx1[0] << " , " << yclus << " , " << clusx1[1] << + // " ) " << std::endl; + + matchclus1.push_back(clusx1[0]); + matchclus1.push_back(yclus); + matchclus1.push_back(clusx1[1]); // seting the cluster 3D point z to be + // of the closest view + } + } else if (UZdist1 < XZdist1 && UZdist1 < VZdist1 && + VZdist1 < XZdist1) { // UV closest + if (!clusv1.empty() && !clusu1.empty()) { + double yclus = (1. / sqrt(3.)) * (clusv1[0] - clusu1[0]); + double xclus = clusu1[0] + clusv1[0]; + // std::cout << "The 2 closest clusters to EndPoint 1 are U and V with (x, + // y, z) point ( " << xclus << " , " << yclus << " , " << clusu1[1] << " ) + // " << std::endl; + + matchclus1.push_back(xclus); + matchclus1.push_back(yclus); + matchclus1.push_back(clusu1[1]); // seting the cluster 3D point z to be + // of the closest view + } + } else if (UZdist1 < XZdist1 && UZdist1 < VZdist1 && + VZdist1 > XZdist1) { // UX closest + if (!clusu1.empty() && !clusx1.empty()) { + double yclus = (1. / sqrt(3.)) * (clusx1[0] - 2 * clusu1[0]); + // std::cout << "The 2 closest clusters to EndPoint 1 are U and X with (x, + // y, z) point ( " << clusx1[0] << " , " << yclus << " , " << clusu1[1] << + // " ) " << std::endl; + + matchclus1.push_back(clusx1[0]); + matchclus1.push_back(yclus); + matchclus1.push_back(clusu1[1]); // seting the cluster 3D point z to be + // of the closest view + this->up_clus_y = michely1; + } + } else if (VZdist1 < XZdist1 && VZdist1 < UZdist1 && + XZdist1 < UZdist1) { // VX closest + if (!clusv1.empty() && !clusx1.empty()) { + double yclus = ((1. / sqrt(3.)) * (2 * clusv1[0] - clusx1[0])); + // std::cout << "The 2 closest clusters to EndPoint 1 are V and X with (x, + // y, z) point ( " << clusx1[0] << " , " << yclus << " , " << clusv1[1] << + // " ) " << std::endl; + + matchclus1.push_back(clusx1[0]); + matchclus1.push_back(yclus); + matchclus1.push_back(clusv1[1]); // seting the cluster 3D point z to be + // of the closest view + this->up_clus_y = michely1; + } + } else if (VZdist1 < XZdist1 && VZdist1 < UZdist1 && + XZdist1 > UZdist1) { // VU closest + if (!clusu1.empty() && !clusv1.empty()) { + double xclus = (1. / sqrt(3.)) * (clusv1[0] - clusu1[0]); + double yclus = clusu1[0] + clusv1[0]; + // std::cout << "The 2 closest clusters to EndPoint 1 are V and U with (x, + // y, z) point ( " << xclus << " , " << yclus << " , " << clusv1[1] << " ) + // " << std::endl; + + matchclus1.push_back(xclus); + matchclus1.push_back(yclus); + matchclus1.push_back(clusv1[1]); // seting the cluster 3D point z to be + // of the closest view + this->up_clus_y = michely1; + this->up_clus_x = michelx1; + } + } + if (XZdist2 < UZdist2 && XZdist2 < VZdist2 && UZdist2 < VZdist2) { + // std::cout << "XU closest to endpoint 2" << std::endl; + if (!clusu2.empty() && !clusx2.empty()) { + double yclus = (1. / sqrt(3.)) * (clusx2[0] - 2 * clusu2[0]); + // std::cout << "The 2 closest clusters to EndPoint 2 are X and U with (x, + // y, z) point ( " << clusx2[0] << " , " << yclus << " , " << clusx2[1] << + // " ) " << std::endl; + + matchclus2.push_back(clusx2[0]); + matchclus2.push_back(yclus); + matchclus2.push_back(clusx2[1]); // seting the cluster 3D point z to be + // of the closest view + + this->down_clus_y = michely2; + } + } else if (XZdist2 < UZdist2 && XZdist2 < VZdist2 && UZdist2 > VZdist2) { + // std::cout << "XV closest to endpoint 2" << std::endl; + if (!clusv2.empty() && !clusx2.empty()) { + double yclus = (1. / sqrt(3.)) * (2 * clusv2[0] - clusx2[0]); + // std::cout << "The 2 closest clusters to EndPoint 2 are X and V with (x, + // y, z) point ( " << clusx2[0] << " , " << yclus << " , " << clusx2[1] << + // " ) " << std::endl; + + matchclus2.push_back(clusx2[0]); + matchclus2.push_back(yclus); + matchclus2.push_back(clusx2[1]); // seting the cluster 3D point z to be + // of the closest view + this->down_clus_y = michely2; + } + } else if (UZdist2 < XZdist2 && UZdist2 < VZdist2 && VZdist2 < XZdist2) { + // std::cout << "UV closest to endpoint 2" << std::endl; + if (!clusu2.empty() && !clusv2.empty()) { + double xclus = clusu2[0] + clusv2[0]; + double yclus = (1. / sqrt(3.)) * (clusv2[0] - clusu2[0]); + // std::cout << "The 2 closest clusters to EndPoint 2 are U and V with (x, + // y, z) point ( " << xclus << " , " << yclus << " , " << clusu2[1] << " ) + // " << std::endl; + + matchclus2.push_back(xclus); + matchclus2.push_back(yclus); + matchclus2.push_back(clusu2[1]); // seting the cluster 3D point z to be + // of the closest view + this->down_clus_y = michely2; + this->down_clus_y = michelx2; + } + } else if (UZdist2 < XZdist2 && UZdist2 < VZdist2 && VZdist2 > XZdist2) { + // std::cout << "UX closest to endpoint 2" << std::endl; + if (!clusu2.empty() && !clusx2.empty()) { + double yclus = (1. / sqrt(3.)) * (clusx2[0] - 2 * clusu2[0]); + // std::cout << "The 2 closest clusters to EndPoint 2 are U and X with (x, + // y, z) point ( " << clusx2[0] << " , " << yclus << " , " << clusu2[1] << + // " ) " << std::endl; + + matchclus2.push_back(clusx2[0]); + matchclus2.push_back(yclus); + matchclus2.push_back(clusu2[1]); // seting the cluster 3D point z to be + // of the closest view + this->down_clus_y = michely2; + } + } else if (VZdist2 < XZdist2 && VZdist2 < UZdist2 && XZdist2 < UZdist2) { + // std::cout << "VX closest to endpoint 2" << std::endl; + if (!clusv2.empty() && !clusx2.empty()) { + double yclus = ((1. / sqrt(3.)) * (2 * clusv2[0] - clusx2[0])); + // std::cout << "The 2 closest clusters to EndPoint 2 are V and X with (x, + // y, z) point ( " << clusx2[0] << " , " << yclus << " , " << clusv2[1] << + // " ) " << std::endl; + + matchclus2.push_back(clusx2[0]); + matchclus2.push_back(yclus); + matchclus2.push_back(clusv2[1]); // seting the cluster 3D point z to be + // of the closest view + this->down_clus_y = michely2; + } + } else if (VZdist2 < XZdist2 && VZdist2 < UZdist2 && XZdist2 > UZdist2) { + // std::cout << "VU closest to endpoint 2" << std::endl; + if (!clusu2.empty() && !clusv2.empty()) { + double xclus = (1. / sqrt(3.)) * (clusv2[0] - clusu2[0]); + double yclus = clusu2[0] + clusv2[0]; + // std::cout << "The 2 closest clusters to EndPoint 2 are V and U with (x, + // y, z) point ( " << xclus << " , " << yclus << " , " << clusv2[1] << " ) + // " << std::endl; + + matchclus2.push_back(xclus); + matchclus2.push_back(yclus); + matchclus2.push_back(clusv2[1]); // seting the cluster 3D point z to be + // of the closest view + this->down_clus_y = michely2; + this->down_clus_x = michelx2; + } + } + + // std::cout << "GETTING 3D DISTANCES FOR THE CUSTERS " << std::endl; + double clusx1diff = 9999.; + double clusy1diff = 9999.; + double clusz1diff = 9999.; + + double mclusx1diff = 9999.; + double mclusy1diff = 9999.; + double mclusz1diff = 9999.; + + double michvtx_x1diff = 9999.; + double michvtx_x2diff = 9999.; + double michvtx_y1diff = 9999.; + double michvtx_y2diff = 9999.; + double michvtx_z1diff = 9999.; + double michvtx_z2diff = 9999.; + + if (!matchclus1.empty()) { + clusx1diff = vtx_x - matchclus1[0]; + clusy1diff = vtx_y - matchclus1[1]; + clusz1diff = vtx_z - matchclus1[2]; + mclusx1diff = michelx1 - matchclus1[0]; + mclusy1diff = michely1 - matchclus1[1]; + mclusz1diff = michelz1 - matchclus1[2]; + michvtx_x1diff = michelx1 - vtx_x; + michvtx_y1diff = michely1 - vtx_y; + michvtx_z1diff = michelz1 - vtx_z; + // std::cout << "3D point for Endpoint 1 Clusters is (x, y, z) " << + // matchclus1[0] << " , " << matchclus1[1] << " , " << matchclus1[2] << + // std::endl; + } + double clusx2diff = 9999.; + double clusy2diff = 9999.; + double clusz2diff = 9999.; + + double mclusx2diff = 9999.; + double mclusy2diff = 9999.; + double mclusz2diff = 9999.; + + if (!matchclus2.empty()) { + clusx2diff = vtx_x - matchclus2[0]; + clusy2diff = vtx_y - matchclus2[1]; + clusz2diff = vtx_z - matchclus2[2]; + mclusx2diff = michelx2 - matchclus2[0]; + mclusy2diff = michely2 - matchclus2[1]; + mclusz2diff = michelz2 - matchclus2[2]; + michvtx_x2diff = michelx2 - vtx_x; + michvtx_y2diff = michely2 - vtx_y; + michvtx_z2diff = michelz2 - vtx_z; + // std::cout << "3D point for Endpoint 2 Clusters is (x, y, z) " << + // matchclus2[0] << " , " << matchclus2[1] << " , " << matchclus2[2] << + // std::endl; + } + /// 2 types of 3D distance calculations for Cluster matching: + // 1. Cluster to Vertex + double dist1 = + sqrt(pow(clusx1diff, 2) + pow(clusy1diff, 2) + pow(clusz1diff, 2)); + double dist2 = + sqrt(pow(clusx2diff, 2) + pow(clusy2diff, 2) + pow(clusz2diff, 2)); + // 2. Cluster to Michel + double mdist1 = + sqrt(pow(mclusx1diff, 2) + pow(mclusy1diff, 2) + pow(mclusz1diff, 2)); + double mdist2 = + sqrt(pow(mclusx2diff, 2) + pow(mclusy2diff, 2) + pow(mclusz2diff, 2)); + // std::cout << " The michel endpoint 1 - cluster 3D point distance is " << + // mdist1 << std::endl; std::cout << " The michel endpoint 2 - cluster 3D + // point distance is " << mdist2 << std::endl; Saving all the distances to the + // Michel member data + this->down_clus_michel_dist3D = mdist2; + this->up_clus_michel_dist3D = mdist1; + this->up_to_cluster_dist3D = dist1; + this->down_to_cluster_dist3D = dist2; + // std::cout << "Printing 3D distances to vertex for cluster matches " << + // dist1 << " and " << dist2 << std::endl; std::cout << "Printing 3D distances + // to michel for cluster matches " << mdist1 << " and " << mdist2 << + // std::endl; + double michdist1 = sqrt(pow(michvtx_x1diff, 2) + pow(michvtx_y1diff, 2) + + pow(michvtx_z1diff, 2)); + double michdist2 = sqrt(pow(michvtx_x2diff, 2) + pow(michvtx_y2diff, 2) + + pow(michvtx_z2diff, 2)); + this->up_clus_michvtx_dist3D = michdist1; + this->down_clus_michvtx_dist3D = michdist2; + // Marks which endpoint is the closest match for this match type + if (dist1 < dist2) + this->clus_endpoint = 1; + else if (dist1 > dist2) + this->clus_endpoint = 2; +} + +// Create Michel objects for each Michel candidate. Add the passing ones to +// the MichelEvent container. +MichelEvent GetQualityMichels(const CVUniverse& univ) { + MichelEvent evt{}; + //========================================================================== + // First: Create a Michel object from each candidate and add them to the + // MichelEvent container. + //========================================================================== + int nmichels = univ.GetNMichels(); + for (int i = 0; i < nmichels; ++i) { + Michel current_michel = Michel(univ, i); + if (current_michel.true_parentpdg == 211) + evt.m_ntruepiparents.push_back(¤t_michel); + double dist = + current_michel.Best3Ddist; // getting the minimum pion range (vertex to + // Michel/Clus distance) + if (dist <= evt.m_bestdist) { + evt.m_bestdist = dist; + evt.m_idx = i; + + evt.m_best_XZ = current_michel.best_XZ; + evt.m_best_UZ = current_michel.best_UZ; + evt.m_best_VZ = current_michel.best_VZ; + evt.m_matchtype = current_michel.BestMatch; + int bmatch = current_michel.BestMatch; + if (bmatch == 1 || bmatch == 3) { + evt.best_x = current_michel.m_x1; + evt.best_y = current_michel.m_y1; + evt.best_z = current_michel.m_z1; + } else if (bmatch == 2 || bmatch == 4) { + evt.best_x = current_michel.m_x2; + evt.best_y = current_michel.m_y2; + evt.best_z = current_michel.m_z2; + } + evt.b_truex = current_michel.true_initialx; + evt.b_truey = current_michel.true_initialy; + evt.b_truez = current_michel.true_initialz; + } + evt.m_nmichels.push_back(¤t_michel); + } + + double lowtpiinevent = univ.GetTrueTpi(); + evt.lowTpi = lowtpiinevent; + //========================================================================== + + //========================================================================== + // Second: remove Michels from the MichelEvent container that fail (and + // fill-in more info about the passing Michels). + //========================================================================== + std::vector nmichelspass; + const double m_maxDistance = 150; // Maximum distance from the vertex that + // the best Michel can have in mm + for (unsigned int i = 0; i < evt.m_nmichels.size(); i++) { + // For Vertex Match Check to see if 2D distance cut will + double upvtxXZ = evt.m_nmichels[i]->up_to_vertex_XZ; + double downvtxXZ = evt.m_nmichels[i]->down_to_vertex_XZ; + double upvtxUZ = evt.m_nmichels[i]->up_to_vertex_UZ; + double downvtxUZ = evt.m_nmichels[i]->down_to_vertex_UZ; + double upvtxVZ = evt.m_nmichels[i]->up_to_vertex_VZ; + double downvtxVZ = evt.m_nmichels[i]->down_to_vertex_VZ; + + if (upvtxXZ < m_maxDistance && + (upvtxUZ < m_maxDistance || upvtxVZ < m_maxDistance)) + evt.m_nmichels[i]->passable_matchtype.at(0) = 1; + else if (upvtxUZ < m_maxDistance && + (upvtxXZ < m_maxDistance || upvtxVZ < m_maxDistance)) + evt.m_nmichels[i]->passable_matchtype.at(0) = 1; + else if (upvtxVZ < m_maxDistance && + (upvtxXZ < m_maxDistance || upvtxUZ < m_maxDistance)) + evt.m_nmichels[i]->passable_matchtype.at(0) = 1; + else + evt.m_nmichels[i]->passable_matchtype.at(0) = -1; + + if (downvtxXZ < m_maxDistance && + (downvtxUZ < m_maxDistance || downvtxVZ < m_maxDistance)) + evt.m_nmichels[i]->passable_matchtype.at(1) = 2; + else if (downvtxUZ < m_maxDistance && + (downvtxXZ < m_maxDistance || downvtxVZ < m_maxDistance)) + evt.m_nmichels[i]->passable_matchtype.at(1) = 2; + else if (downvtxVZ < m_maxDistance && + (downvtxXZ < m_maxDistance || downvtxUZ < m_maxDistance)) + evt.m_nmichels[i]->passable_matchtype.at(1) = 2; + else + evt.m_nmichels[i]->passable_matchtype.at(1) = -1; + + double upclusXZ = evt.m_nmichels[i]->up_to_clus_XZ; + double upclusUZ = evt.m_nmichels[i]->up_to_clus_UZ; + double upclusVZ = evt.m_nmichels[i]->up_to_clus_VZ; + double downclusXZ = evt.m_nmichels[i]->down_to_clus_XZ; + double downclusUZ = evt.m_nmichels[i]->down_to_clus_UZ; + double downclusVZ = evt.m_nmichels[i]->down_to_clus_VZ; + + if (upclusXZ < m_maxDistance && + (upclusUZ < m_maxDistance || upclusVZ < m_maxDistance)) + evt.m_nmichels[i]->passable_matchtype.at(2) = 3; + else if (upclusUZ < m_maxDistance && + (upclusXZ < m_maxDistance || upclusVZ < m_maxDistance)) + evt.m_nmichels[i]->passable_matchtype.at(2) = 3; + else if (upclusVZ < m_maxDistance && + (upclusXZ < m_maxDistance || upclusUZ < m_maxDistance)) + evt.m_nmichels[i]->passable_matchtype.at(2) = 3; + else + evt.m_nmichels[i]->passable_matchtype.at(2) = -1; + + if (downclusXZ < m_maxDistance && + (downclusUZ < m_maxDistance || downclusVZ < m_maxDistance)) + evt.m_nmichels[i]->passable_matchtype.at(3) = 4; + else if (downclusUZ < m_maxDistance && + (downclusXZ < m_maxDistance || downclusVZ < m_maxDistance)) + evt.m_nmichels[i]->passable_matchtype.at(3) = 4; + else if (downclusVZ < m_maxDistance && + (downclusXZ < m_maxDistance || downclusUZ < m_maxDistance)) + evt.m_nmichels[i]->passable_matchtype.at(3) = 4; + else + evt.m_nmichels[i]->passable_matchtype.at(3) = -1; + + std::vector distances3D; + + if (evt.m_nmichels[i]->passable_matchtype[0] == 1) + distances3D.push_back(evt.m_nmichels[i]->up_to_vertex_dist3D); + if (evt.m_nmichels[i]->passable_matchtype[1] == 2) + distances3D.push_back(evt.m_nmichels[i]->down_to_vertex_dist3D); + if (evt.m_nmichels[i]->passable_matchtype[2] == 3) + distances3D.push_back(evt.m_nmichels[i]->up_clus_michel_dist3D); + if (evt.m_nmichels[i]->passable_matchtype[3] == 4) + distances3D.push_back(evt.m_nmichels[i]->down_clus_michel_dist3D); + if (distances3D.empty()) distances3D = {9999., 9999., 9999., 9999.}; + if (distances3D[0] == 9999.) + continue; // Comment this line out if you are making efficiency plots + + std::sort(distances3D.begin(), distances3D.end()); + + if (distances3D[0] == evt.m_nmichels[i]->up_to_vertex_dist3D) { + evt.m_nmichels[i]->best_XZ = evt.m_nmichels[i]->up_to_vertex_XZ; + evt.m_nmichels[i]->best_UZ = evt.m_nmichels[i]->up_to_vertex_UZ; + evt.m_nmichels[i]->best_VZ = evt.m_nmichels[i]->up_to_vertex_VZ; + evt.m_nmichels[i]->BestMatch = 1; + evt.m_nmichels[i]->Best3Ddist = evt.m_nmichels[i]->up_to_vertex_dist3D; + + } else if (distances3D[0] == evt.m_nmichels[i]->down_to_vertex_dist3D) { + evt.m_nmichels[i]->BestMatch = 2; + evt.m_nmichels[i]->best_XZ = evt.m_nmichels[i]->down_to_vertex_XZ; + evt.m_nmichels[i]->best_UZ = evt.m_nmichels[i]->down_to_vertex_UZ; + evt.m_nmichels[i]->best_VZ = evt.m_nmichels[i]->down_to_vertex_VZ; + evt.m_nmichels[i]->Best3Ddist = evt.m_nmichels[i]->down_to_vertex_dist3D; + // std::cout << "This Michel is DOWNVTX and has true endpoint " << + // evt.m_nmichels[i]->trueEndpoint << std::endl; + + } else if (distances3D[0] == evt.m_nmichels[i]->up_clus_michel_dist3D) { + evt.m_nmichels[i]->BestMatch = 3; + evt.m_nmichels[i]->best_XZ = evt.m_nmichels[i]->up_to_clus_XZ; + evt.m_nmichels[i]->best_UZ = evt.m_nmichels[i]->up_to_clus_VZ; + evt.m_nmichels[i]->best_VZ = evt.m_nmichels[i]->up_to_clus_UZ; + evt.m_nmichels[i]->Best3Ddist = evt.m_nmichels[i]->up_clus_michvtx_dist3D; + // std::cout << "This Michel is UPCLUS and has true endpoint " << + // evt.m_nmichels[i]->trueEndpoint << std::endl; + + } else if (distances3D[0] == evt.m_nmichels[i]->down_clus_michel_dist3D) { + evt.m_nmichels[i]->BestMatch = 4; + evt.m_nmichels[i]->Best3Ddist = + evt.m_nmichels[i]->down_clus_michvtx_dist3D; + evt.m_nmichels[i]->best_XZ = evt.m_nmichels[i]->down_to_clus_XZ; + evt.m_nmichels[i]->best_UZ = evt.m_nmichels[i]->down_to_clus_UZ; + evt.m_nmichels[i]->best_VZ = evt.m_nmichels[i]->down_to_clus_VZ; + // std::cout << "This Michel is DOWNCLUS and has true endpoint " << + // evt.m_nmichels[i]->trueEndpoint << std::endl; + + } else { + evt.m_nmichels[i]->BestMatch = 0; + evt.m_nmichels[i]->Best3Ddist = 9999.; + evt.m_nmichels[i]->best_XZ = 9999.; + evt.m_nmichels[i]->best_UZ = 9999.; + evt.m_nmichels[i]->best_VZ = 9999.; + continue; + } + + nmichelspass.push_back(evt.m_nmichels[i]); + } + + evt.m_nmichels.clear(); // empty existing vector of Michels + evt.m_nmichels = nmichelspass; // replace vector of michels with the vector + // of michels that passed the above cut + //========================================================================== + + return evt; +} +} // namespace trackless + +#endif // Michel_cxx diff --git a/includes/Michel.h b/includes/Michel.h index 829ea57..88949f2 100644 --- a/includes/Michel.h +++ b/includes/Michel.h @@ -1,1452 +1,246 @@ -#ifndef Michel_H -#define Michel_H - -#include "CVUniverse.h" -#include "Cluster.h" +#ifndef Michel_h +#define Michel_h //============================================================================== -// Endpoint Michel Class +// Two classes and helper functions for different michel electron objects +// endpoint: Aaron-style michels matched to track endpoints (written by me) +// trackless: Mehreen-style michels NOT matched to track endpoints (written by +// mehreen) //============================================================================== -namespace endpoint { - class Michel; - typedef std::map MichelMap; - - //============================================================================== - // Michel quality cuts - //============================================================================== - bool IsQualityMatchedMichel_Fit(double fit_dist, double fit_cut) { - if (fit_dist >= 0 && fit_dist < fit_cut) - return true; - else - return false; - } - - bool IsQualityMatchedMichel_NoFit(double nofit_dist, double nofit_cut) { - if (nofit_dist >= 0 && nofit_dist < nofit_cut) - return true; - else - return false; - } - - bool IsQualityMatchedMichel_OneView(double ov_dist, double ov_cut) { - if (ov_dist >= 0 && ov_dist * (2.0 / 3) < ov_cut) - return true; - else - return false; - } - - class Michel { - public: - enum EMatchCategory { kNoMatch, kFit, kNoFit, kOV, kNMatchCategories }; - - // constructors - Michel(const CVUniverse& univ, int i, int v); - Michel() - : idx(-105), - vtx(-106), - had_idx(-107), - match_category(kNoMatch), - fit_distance(-1.) {} - - // integer that uniquely identifies cluster of hits - int idx; - - // integer corresponding to vertex to which cluster was matched. - // vtx == 0 --> interaction vertex - // vtx == 1 --> "first" track endpoint, corresponds to hadron index 0. - // i.e. hadron index = michel vtx - 1. - int vtx; - - // hadron index to which this michel is matched (had_idx = vtx - 1) - int had_idx; - - EMatchCategory match_category; - double fit_distance; - }; - - Michel::Michel(const CVUniverse& univ, int i, int v) - : idx(i), vtx(v), had_idx(v - 1) { - bool isIntVtx = (vtx == 0); - // distances for fitted, 2/3-view nofit, and 1-view michels, respectively - double mm_fit_dist = univ.GetVecElem("matched_michel_end_dist", idx) / 10.; - double mm_nofit_dist = univ.GetVecElem("matched_michel_avg_dist", idx) / 10.; - double mm_ov_dist = univ.GetVecElem("matched_michel_ov_dist", idx) / 10.; - // NEW - const double FIT_CUT = isIntVtx ? 9.0 : 7.5; // cm - const double NOFIT_CUT = isIntVtx ? 10.0 : 50.; // cm - // OLD - // const double FIT_CUT = isIntVtx ? 9.0 : 5.; // cm - // const double NOFIT_CUT = isIntVtx ? 10.0 : 10.; // cm - // TODO distinguish between 2/3 view and OV - // const double FIT_CUT = isIntVtx ? 9. : 15.0; // cm - // const double NOFIT_CUT = isIntVtx ? 10. : 15.0; // cm - if (IsQualityMatchedMichel_Fit(mm_fit_dist, FIT_CUT)) { - match_category = kFit; - fit_distance = mm_fit_dist; - return; - } else if (IsQualityMatchedMichel_NoFit(mm_nofit_dist, NOFIT_CUT)) { - match_category = kNoFit; - fit_distance = mm_nofit_dist; - return; - } else if (IsQualityMatchedMichel_OneView(mm_ov_dist, NOFIT_CUT)) { - match_category = kOV; - fit_distance = mm_ov_dist; - return; - } else { - match_category = kNoMatch; - fit_distance = -2.; - return; - } - } - - // -- Given a single michel cluster matched to two vertices - // return vertex with the better-matched michel. - Michel CompareMichels(Michel r, Michel c) { - if (r.match_category > c.match_category) - return r; - else if (r.match_category < c.match_category) - return c; - else { - if (r.fit_distance < c.fit_distance) - return r; - else if (r.fit_distance > c.fit_distance) - return c; - else { - // This must mean we're comparing the same michels with the same fits. - // std::cout << "WEIRD COMPAREMICHELS PROBLEM" << std::endl; - return r; - } - } - } - - // Add michel to MichelMap. Check if this cluster has already been matched. - // Then use only the best match. - bool AddOrReplaceMichel(MichelMap& mm, Michel m) { - std::pair SC; - if (mm.count(m.idx) == 0) - SC = mm.insert(pair(m.idx, m)); - else { - Michel reigning_michel = mm.at(m.idx); - Michel best_michel = CompareMichels(reigning_michel, m); - mm[m.idx] = best_michel; - } - return true; - } - - //============================================================================ - // Collect the good michels in this event - //============================================================================ - // Get quality michels map<(int michel cluster ID, Michel)> - // * A Michel is uniquely ID-ed by its cluster(of hits) integer index. - // * The michel tool has already vetted the clusters themselves. - // Whereas here we evaluate the quality of the fit. - // * At most one interaction vertex michel (which MUST be "fitted") - // * An OV michel will only be kept if no better michels are in the event. - // * In the case of 2+ OV michels, only keep the best one. - // * required: one-to-one michel<->vertex matching: - // * when one michel cluster is matched to multiple vertices, the vtx - // with the better match is chosen. - // * We don't have the problem in the other direction -- michel tool: a - // vertex cannot be matched to more than one cluster. - // * At the moment, we can return a single michel that is a quality - // interaction vertex michel. We'd like to call these signal, but we don't - // know the pion energy...yet. In the meantime, cut them. - - MichelMap GetQualityMichels(const CVUniverse& univ) { - std::map ret_michels; - std::vector matched_michel_idxs = univ.GetVec("matched_michel_idx"); - - // Loop vertices in the event, i.e. the indices of the michel index vector - for (uint vtx = 0; vtx < matched_michel_idxs.size(); ++vtx) { - int mm_idx = matched_michel_idxs[vtx]; - - // NO MATCH -- GO TO NEXT VTX. No michel cluster matched to this vertex. - if (mm_idx < 0) continue; - - // MICHEL CONSTRUCTOR - // Set match category (e.g. fitted, one-view, etc), match distance. - Michel current_michel = Michel(univ, mm_idx, vtx); - // MICHEL MATCH QUALITY CUT -- NEXT VTX - // A michel cluster was matched to this vertex, but the match doesn't - // pass match quality cuts. - if (current_michel.match_category == Michel::kNoMatch) continue; - - // VERTEX MICHEL -- must meet the gold standard for its fit - bool isIntVtx = (vtx == 0); - if (isIntVtx && current_michel.match_category <= Michel::kNoFit) { - continue; - } - // ENDPOINT MICHELS - else { - // ZERO MICHELS FOUND SO FAR - if (ret_michels.size() == 0) - ; - - // ONE MICHEL FOUND SO FAR - // -- If either the michel we have already or this michel is OV, pick - // the better of the two. Only one will remain. - else if (ret_michels.size() == 1) { - Michel reigning_michel = (ret_michels.begin())->second; - if (reigning_michel.match_category == Michel::kOV || - current_michel.match_category == Michel::kOV) - ret_michels.clear(); - current_michel = CompareMichels(reigning_michel, current_michel); - } - - // 2+ MICHELS FOUND SO FAR - else { - if (current_michel.match_category == Michel::kOV) continue; - } - } - - // ADD THIS MICHEL - // When a cluster is matched to two vertices, pick the vtx with the - // better match. - bool SC = AddOrReplaceMichel(ret_michels, current_michel); - } // end vtx loop - return ret_michels; - } +#include "CVUniverse.h" -} // namespace endpoint +class Cluster; +class MichelEvent; //============================================================================== -// Vertex Michel Class +// Track Endpoint Michel //============================================================================== -namespace vertex { - class Michel { - public: - // constructors - Michel(const CVUniverse& univ, int ci); - // Functions - Michel(){}; // fill in most basic stuff in "generic info" . this is default - // constructor you need it if you have public data members - void DoMoreInitializationAndProcessing(); // fill in more complicated stuff - // in "generic info" - void DoMatching(); // fill in "best matching stuff" - void DoesMichelMatchVtx(const CVUniverse& univ); // GEts info for Vtx Match - void DoesMichelMatchClus( - const CVUniverse& univ); // Gets info for ClusterMatch - void GetBestMatch(); // get type for best match out of all four saved matches - void GetPionAngle( - const CVUniverse& - univ); // Function to calculate pion angle with respect to beam. - // Simply gets angle between best michel endpoint and vertex. - - // std::vector CreateMichels(CVUniverse& univ); - // generic info - std::vector up_location; // upstream location 0 X 1 U 2 V 3 Z - std::vector down_location; // downstream location - double m_x1 = 9999.; // Michel Endpoint 1 x - double m_x2 = 9999.; // Michel Endpoint 2 x - double m_y1 = 9999.; // Michel Endpoint 1 y - double m_y2 = 9999.; // Michel Endpoint 2 y - double m_u1 = 9999.; // Michel Endpoint 1 u - double m_u2 = 9999.; // Michel Endpoint 2 u - double m_v1 = 9999.; // Michel Endpoint 1 v - double m_v2 = 9999.; // Michel Endpoint 2 v - double m_z1 = 9999.; // Mihel Endpoint z 1 - double m_z2 = 9999.; // Michel Endpoiint z2 - double energy = -999.; // Michel energy - double time = -999.; // Michel Time - int is_fitted = -1; // Is the Michel fitted? 0 no. 1 yes. - // Following are 2D distances (were stored in vectors but now as explicit data - // members) - double up_to_vertex_XZ = 9999.; - double up_to_vertex_UZ = 9999.; - double up_to_vertex_VZ = 9999.; - double down_to_vertex_XZ = 9999.; - double down_to_vertex_UZ = 9999.; - double down_to_vertex_VZ = 9999.; - double down_to_clus_XZ = 9999.; - double down_to_clus_UZ = 9999.; - double down_to_clus_VZ = 9999.; - double up_to_clus_XZ = 9999.; - double up_to_clus_UZ = 9999.; - double up_to_clus_VZ = 9999.; - // Michel End point to Vertex distance (up = endpoint 1 and down = endpoint - // 2) TODO: actually find out which end point is upstream or downstream - double up_to_vertex_dist3D = 9999.; - double down_to_vertex_dist3D = 9999.; - // Maybe keep a vector of clusters that matched to each endpoint? - std::vector cluster_to_up_match; - std::vector cluster_to_down_match; - // 3D distances between cluster and Michel - double up_to_cluster_dist3D = 9999.; // Distance between vertex and cluster - // that was matched to endpoint 1 - double down_to_cluster_dist3D = - 9999.; // Distance between vertex and cluster matched to endpoint 2 - double up_clus_michel_dist3D = - 9999.; // Distance between Michel endpoint 1 and clusters - double down_clus_michel_dist3D = - 9999.; // Distance between Michel endpoint 2 and clusters - double up_clus_michvtx_dist3D = - 9999.; // Distance between the Michel end point 1 that matched to - // clusters and the vertex - this will be used as pion range - double down_clus_michvtx_dist3D = - 9999.; // Distance between the Michel endpoint 2 that matched to clusters - // and the vertex - this will be used as pion range - double vtx_michel_timediff = 9999.; - - double overlay_fraction = - -1.0; // Overlay fraction of the Michel, Default if Data. 0 if MC 1 if - // Data... (maybe some events in between?) - int nclusters = 0; // number of (non-muon) clusters in the primary event - int vtx_endpoint = 0; // 1 or 2 for which Michel end point is closest - int clus_endpoint = 0; // 1 or 2 for which Michel endpoint is closest - // best matching stuff - // enum *best_cluster_match; // just tells you which of the four matches are - // the best match enum BestClusterMatch {kUpVtxMatch, kDownVtxMatch, - // kUpClusMatch, kDownClusMatch, kNClusterMatches}; the following is in place - // until i figure out how to use the enum. - int BestMatch = 0; // 0 = null match, 1= kUpVtxMatch, 2 = kDownVtxMatch, 3 = - // kUpClusMatch, 4= kDownClusMatch, - int SecondBestMatch = 0; - int tuple_idx; // index of the Michel out of all the michels saved in the - // tuple - double Best3Ddist = - 9999.; // Best 3D distance out of eitehr a vertex or a cluster match - // best 2D distance for the best type of match - double best_XZ = 9999.; - double best_UZ = 9999.; - double best_VZ = 9999.; - // Want to save the index of the clusters that the Michel best matched to. - int xclus_idx; - int uclus_idx; - int vclus_idx; - - // True initial position of the michel TODO: initial the other Michel truth - // member data here (energy, time, momentum etc) - double true_angle = 9999.; - double true_initialx = 9999.; - double true_initialy = 9999.; - double true_initialz = 9999.; - double true_e = 9999.; - double true_p = 9999.; - double true_pdg = -1.0; - int true_parentid = -1; - int true_parentpdg = -1; - double true_parent_energy = -9999.; - double true_parent_p = -9999.; - double true_parent_px = -9999.; - double true_parent_py = -9999.; - double true_parent_pz = -9999.; - double true_parent_xi = -9999.; - double true_parebt_yi = -9999.; - double true_parent_zi = -9999.; - double true_parent_xf = -9999.; - double true_parent_yf = -9999.; - double true_parent_zf = -9999.; - // the following member data were created to investigate my weird convoluted - // way of geting x and y values. Probably dont need them now. // TODO: check - // and remove the following member data - double best_angle = -9999.; - double up_clus_x = 9999.; - double up_clus_y = 9999.; - double up_clus_z = 9999.; - double down_clus_x = 9999.; - double down_clus_y = 9999.; - double down_clus_z = 9999.; - double up_vtx_x = 9999.; - double up_vtx_y = 9999.; - double up_vtx_z = 9999.; - double down_vtx_x = 9999.; - double down_vtx_y = 9999.; - double down_vtx_z = 9999.; - double is_overlay = -1; - double pionKE = -9999.; - // Adding the following member data to determine the true endpoint position of - // the Michel - int trueEndpoint = - -1; // 0 = Overlay Michel, 1 = Endpoint 1 is correct intial position of - // Michel, 2 = Endpoint 2 is correct Initial Position of Michel - // ~DeleteMichel(){delete this;}; // This is going to be the main destructor. - int recoEndpoint = -1; // -1 is default/NULL like above. 1 = Endpoint 1 is - // better match, 2 = Endpoint 2 is better match - - std::vector passable_matchtype{ - -1, -1, -1, - -1}; // This vector will contain a value for each match type -1 or the - // matchtype 1, 2, 3, 4 for UpVtx, DownVTx, Upclus, DownClus - // depending of that match type passes our 2D distance cut. (if - // distance is large, then it'll pass all of them). - }; - - // Setting the Michel data members directly from CV universe. No calculations - // made at this stage - Michel::Michel(const CVUniverse& univ, int ci) { - energy = univ.GetVecElem("FittedMichel_michel_energy", ci); - time = univ.GetVecElem("FittedMichel_michel_time", ci) / pow(10, 3); - is_fitted = univ.GetVecElem("FittedMichel_michel_fitPass", ci); - up_location.push_back(univ.GetVecElem("FittedMichel_michel_x1", ci)); - up_location.push_back(univ.GetVecElem("FittedMichel_michel_u1", ci)); - up_location.push_back(univ.GetVecElem("FittedMichel_michel_v1", ci)); - up_location.push_back(univ.GetVecElem("FittedMichel_michel_z1", ci)); - down_location.push_back(univ.GetVecElem("FittedMichel_michel_x2", ci)); - down_location.push_back(univ.GetVecElem("FittedMichel_michel_u2", ci)); - down_location.push_back(univ.GetVecElem("FittedMichel_michel_v2", ci)); - down_location.push_back(univ.GetVecElem("FittedMichel_michel_z2", ci)); - m_x1 = univ.GetVecElem("FittedMichel_michel_x1", ci); - m_y1 = univ.GetVecElem("FittedMichel_michel_y1", ci); - m_u1 = univ.GetVecElem("FittedMichel_michel_u1", ci); - m_v1 = univ.GetVecElem("FittedMichel_michel_v1", ci); - m_z1 = univ.GetVecElem("FittedMichel_michel_z1", ci); - m_x2 = univ.GetVecElem("FittedMichel_michel_x2", ci); - m_y2 = univ.GetVecElem("FittedMichel_michel_y2", ci); - m_u2 = univ.GetVecElem("FittedMichel_michel_u2", ci); - m_z2 = univ.GetVecElem("FittedMichel_michel_z2", ci); - m_v2 = univ.GetVecElem("FittedMichel_michel_v2", ci); - nclusters = univ.GetInt("cluster_view_sz"); - overlay_fraction = univ.GetVecElem("FittedMichel_michel_datafraction", ci); - - true_initialx = - univ.GetVecElem("FittedMichel_reco_micheltrajectory_initialx", ci); - true_initialy = - univ.GetVecElem("FittedMichel_reco_micheltrajectory_initialy", ci); - true_initialz = - univ.GetVecElem("FittedMichel_reco_micheltrajectory_initialz", ci); - is_overlay = univ.GetVecElem("FittedMichel_michel_isoverlay", ci); - true_e = univ.GetVecElem("FittedMichel_reco_micheltrajectory_energy", ci); - true_pdg = univ.GetVecElem("FittedMichel_reco_micheltrajectory_pdg", ci); - true_parentpdg = univ.GetVecElem("FittedMichel_true_primaryparent_pdg", ci); - true_parentid = - univ.GetVecElem("FittedMichel_true_primaryparent_trackID", ci); - true_p = univ.GetVecElem("FittedMichel_reco_micheltrajectory_momentum", ci); - - double true_parentp = - univ.GetVecElem("FittedMichel_true_primaryparent_momentum", ci); - double true_parente = - univ.GetVecElem("FittedMichel_true_primaryparent_energy", ci); - double mass = mass = sqrt(pow(true_parente, 2) - pow(true_parentp, 2)); - pionKE = true_parente - mass; - - double true_parentpx = - univ.GetVecElem("FittedMichel_true_primaryparent_momentumx", ci); - double true_parentpy = - univ.GetVecElem("FittedMichel_true_primaryparent_momentumy", ci); - double true_parentpz = - univ.GetVecElem("FittedMichel_true_primaryparent_momentumz", ci); - - TVector3 truep(true_parentpx, true_parentpy, true_parentpz); - double true_theta = truep.Theta(); - true_angle = true_theta; //*TMath::RadToDeg(); - - // if (overlay_fraction < 0.5) std::cout << "True Parent of Michel is PDG: " - // << true_parentpdg << " And Parent trackID: " << true_parentid << - // std::endl; - double end1diff = - abs(true_initialz - - m_z1); // This gives a value for determining how close the - // reconstructed endpoint of the michel is to the true intial - // endpoint (the start point of where the michel decayed from) - double end2diff = - abs(true_initialz - - m_z2); // this is for endpoint 2. If you compare this to the endpoint - // that gets matched to a verted or cluster, you can determine - // which type of match ends up getting correcct matches or - // wrong matches. - - if (overlay_fraction > 0.5) - trueEndpoint = 0; - else if (true_parentpdg == 211 && end1diff < end2diff) - trueEndpoint = 1; - else if (true_parentpdg == 211 && end2diff < end1diff) - trueEndpoint = 2; - if (is_fitted == 1) { // Do theMatching for Fitted Michels - DoesMichelMatchVtx(univ); // GEts info for Vtx Match - DoesMichelMatchClus(univ); // Gets info for ClusterMatch - GetBestMatch(); - GetPionAngle(univ); - } - } - - // Get the angle between Michel endpoint that was matched and the vertex - void Michel::GetPionAngle(const CVUniverse& univ) { - double vtx_x = univ.GetVertex().X(); // mm - double vtx_y = univ.GetVertex().Y(); // mm - double vtx_z = univ.GetVertex().Z(); // mm - - TVector3 vtx(vtx_x, vtx_y, vtx_z); - TVector3 endpoint; - - if (this->BestMatch == 1 || this->BestMatch == 3) - endpoint.SetXYZ(this->m_x1, this->m_y1, this->m_z1); - else if (this->BestMatch == 2 || this->BestMatch == 4) - endpoint.SetXYZ(this->m_x2, this->m_y2, this->m_z2); - else - endpoint.SetXYZ(9999., 9999., 9999.); - - TVector3 range = endpoint - vtx; - double angle = univ.thetaWRTBeam(range.x(), range.y(), range.z()); - this->best_angle = angle; //*TMath::RadToDeg(); // in Degrees - } - - // This function will get an integer for the best match type of the Michel. - // It compares distance between MIchel and whatever it's best match is to find - // the Best type of Michel for a single Michel. - void Michel::GetBestMatch() { - int upvtxmatch = 0; - int downvtxmatch = 0; - int upclusmatch = 0; - int downclusmatch = 0; - - // This is setting the values for which endpoint is a better match for each - // type. - // - // TODO: Revise this function. There has to be a better way to compare - // distances than what I wrote. - std::vector distances{ - this->up_to_vertex_dist3D, this->down_to_vertex_dist3D, - this->up_clus_michel_dist3D, this->down_clus_michel_dist3D}; - std::sort(distances.begin(), distances.end()); - - // This bit of code will try to find the best 3D distance end point - if (distances[0] == this->up_to_vertex_dist3D) { - upvtxmatch = 1; - this->best_XZ = this->up_to_vertex_XZ; - this->best_UZ = this->up_to_vertex_UZ; - this->best_VZ = this->up_to_vertex_VZ; - this->BestMatch = 1; - this->Best3Ddist = this->up_to_vertex_dist3D; - // std::cout << "This Michel is UPVTX and has true endpoint " << - // this->trueEndpoint << std::endl; - } else if (distances[0] == this->down_to_vertex_dist3D) { - this->BestMatch = 2; - this->best_XZ = this->down_to_vertex_XZ; - this->best_UZ = this->down_to_vertex_UZ; - this->best_VZ = this->down_to_vertex_VZ; - this->Best3Ddist = this->down_to_vertex_dist3D; - downvtxmatch = 1; - // std::cout << "This Michel is DOWNVTX and has true endpoint " << - // this->trueEndpoint << std::endl; - } else if (distances[0] == this->up_clus_michel_dist3D) { - this->BestMatch = 3; - this->best_XZ = this->up_to_clus_XZ; - this->best_UZ = this->up_to_clus_VZ; - this->best_VZ = this->up_to_clus_UZ; - this->Best3Ddist = this->up_clus_michvtx_dist3D; - upclusmatch = 1; - // std::cout << "This Michel is UPCLUS and has true endpoint " << - // this->trueEndpoint << std::endl; - } else if (distances[0] == this->down_clus_michel_dist3D) { - this->BestMatch = 4; - this->Best3Ddist = this->down_clus_michvtx_dist3D; - this->best_XZ = this->down_to_clus_XZ; - this->best_UZ = this->down_to_clus_UZ; - this->best_VZ = this->down_to_clus_VZ; - downclusmatch = 1; - // std::cout << "This Michel is DOWNCLUS and has true endpoint " << - // this->trueEndpoint << std::endl; - } else { - this->BestMatch = 0; - this->Best3Ddist = 9999.; - this->best_XZ = 9999.; - this->best_UZ = 9999.; - this->best_VZ = 9999.; - } - - // Second best - if (distances[1] == this->up_to_vertex_dist3D) - this->SecondBestMatch = 1; - else if (distances[1] == this->down_to_vertex_dist3D) - this->SecondBestMatch = 2; - else if (distances[1] == this->up_clus_michel_dist3D) - this->SecondBestMatch = 3; - else if (distances[1] == this->down_clus_michel_dist3D) - this->SecondBestMatch = 4; - else { - this->SecondBestMatch = 0; - } - int matchtype = this->BestMatch; - // Identifying the best reco endpoint based on the Best MAtch type. - if (matchtype == 1 || matchtype == 3) - this->recoEndpoint = 1; - else if (matchtype == 2 || matchtype == 4) - this->recoEndpoint = 2; - } - - void Michel::DoesMichelMatchVtx(const CVUniverse& univ) { - // std::cout << "GETTING VTX MATCH FOR MICHEL " << std::endl; - - // Getting Vertex Information - double vtx_x = univ.GetVertex().X(); // mm - double vtx_y = univ.GetVertex().Y(); // mm - double vtx_z = univ.GetVertex().Z(); // mm - double vtx_t = univ.GetVertex().T() / pow(10, 3); // mus - double vtx_u = (0.5 * (vtx_x - sqrt(3.) * vtx_y)); - double vtx_v = (0.5 * (vtx_x + sqrt(3.) * vtx_y)); - - // std::cout << "VTX POSITION is (x, u , v, y, z) (" << vtx_x << " , " << - // vtx_u << " , " << vtx_v << " , " << vtx_y << " , " << vtx_z << std::endl; - // Initializing all the distance comparisons I will need to make - double zdiff1 = vtx_z - this->m_z1; - double zdiff2 = vtx_z - this->m_z2; - double xdiff = 9999.; - double udiff = 9999.; - double vdiff = 9999.; - double XZdist = 9999.; - double UZdist = 9999.; - double VZdist = 9999.; - - double michely1 = this->m_y1; - double michely2 = this->m_y2; - double michelx1 = this->m_x1; - double michelx2 = this->m_z2; - double michelz1 = this->m_z1; - double michelz2 = this->m_z2; - double timediff = (this->time) - vtx_t; - // std::cout << "Michel time " << this->time << " Vertex time " << vtx_t << - // "\n" << std::endl; - this->vtx_michel_timediff = timediff; - - // 2D distance calculations for Endpoint 1 - xdiff = abs(vtx_x - this->m_x1); - udiff = abs(vtx_u - this->m_u1); - vdiff = abs(vtx_v - this->m_v1); - - XZdist = sqrt(xdiff * xdiff + zdiff1 * zdiff1); - UZdist = sqrt(udiff * udiff + zdiff1 * zdiff1); - VZdist = sqrt(vdiff * vdiff + zdiff1 * zdiff1); - - this->up_to_vertex_XZ = XZdist; - this->up_to_vertex_UZ = UZdist; - this->up_to_vertex_VZ = VZdist; - - // 2D Distance calculations for endpoint2 - xdiff = abs(vtx_x - this->m_x2); - udiff = abs(vtx_u - this->m_u2); - vdiff = abs(vtx_v - this->m_v2); - XZdist = sqrt(xdiff * xdiff + zdiff2 * zdiff2); - UZdist = sqrt(udiff * udiff + zdiff2 * zdiff2); - VZdist = sqrt(vdiff * vdiff + zdiff2 * zdiff2); - - this->down_to_vertex_XZ = XZdist; - this->down_to_vertex_UZ = UZdist; - this->down_to_vertex_VZ = VZdist; - - // 3D distance calculations - double xdiff1 = abs(vtx_x - this->m_x1); - double xdiff2 = abs(vtx_x - this->m_x2); - double ydiff1 = abs(vtx_y - this->m_y1); - double ydiff2 = abs(vtx_y - this->m_y2); - - double dist1 = sqrt(zdiff1 * zdiff1 + xdiff1 * xdiff1 + ydiff1 * ydiff1); - double dist2 = sqrt(zdiff2 * zdiff2 + xdiff2 * xdiff2 + ydiff2 * ydiff2); - - this->up_to_vertex_dist3D = dist1; - this->down_to_vertex_dist3D = dist2; - - if (dist1 < dist2) - this->vtx_endpoint = 1; - else if (dist2 < dist1) - this->vtx_endpoint = 2; - - ////std::cout << "END OF LOOP TO FIND VTX MATCH" << std::endl; - } - - void Michel::DoesMichelMatchClus(const CVUniverse& univ) { - // This is where the function for Cluster Matching goes - - ////std::cout << "STARTING SEARCH FOR CLUSTER MATCH " << std::endl; - // Inititalizing vertex variables needed for cluster matching - int nclusters = this->nclusters; - // std::cout << "There are " << nclusters << " available clusters for this - // matching " << std::endl; - double vtx_x = univ.GetVertex().X(); // mm - double vtx_y = univ.GetVertex().Y(); // mm - double vtx_z = univ.GetVertex().Z(); // mm - double vtx_t = univ.GetVertex().T() / pow(10, 3); // mus - double vtx_u = (0.5 * (vtx_x - sqrt(3.) * vtx_y)); - double vtx_v = (0.5 * (vtx_x + sqrt(3.) * vtx_y)); - - double closestdistance1x = 9999.; - double closestdistance1u = 9999.; - double closestdistance1v = 9999.; - double closestdistance1z = 9999.; - - double closestdistance2x = 9999.; - double closestdistance2u = 9999.; - double closestdistance2v = 9999.; - double closestdistance2z = 9999.; - - double michelx1 = this->m_x1; - double michelx2 = this->m_x2; - double michelu1 = this->m_u1; - double michelu2 = this->m_u2; - double michelv1 = this->m_v1; - double michelv2 = this->m_v2; - double michelz1 = this->m_z1; - double michelz2 = this->m_z2; - double michely1 = this->m_y1; - double michely2 = this->m_y2; - - double micheltime = this->time; - - // std::cout << "Michel position 1 is (x, u, v, y, z) " << michelx1 << " , " - // << michelu1 << " , " << michelv1 << " , "<< michely1 << " , " << michelz1 - // << std::endl; - - // std::cout << "Michel position 2 is (x, y, v, y, z) " << michelx2 << " , " - // << michelu2 << " , " << michelv2 << " , " << michely2 << " , " << michelz2 - // << std::endl; - - std::vector endpoint1_clus; - std::vector endpoint2_clus; - - // Get the closest distance for each view - - ////std::cout << "STARTING LOOP OVER CLUSTERS " << std::endl; - int x1_idx = -1; // want to save the index for each closest cluster - int u1_idx = -1; - int v1_idx = -1; - int x2_idx = -1; - int u2_idx = -1; - int v2_idx = -1; - - for (int i = 0; i < nclusters; i++) { - Cluster current_cluster = Cluster(univ, i); - - double energy = current_cluster.energy; - double time = current_cluster.time; - double pos = current_cluster.pos; - double zpos = current_cluster.zpos; - int view = current_cluster.view; - double timediff = micheltime - time; - - if (energy < 2.) continue; // only get clusters greater than 2 MeV - - // std::cout << "printing cluster info " << "energy " << energy << " time " - // << time << " pos " << pos << " zpos " << zpos << std::endl; - - double zdiff1 = abs(zpos - michelz1); - double zdiff2 = abs(zpos - michelz2); - // Calculating 2D distance in X view - if (view == 1) { - // Endpoint 1 calculations - double xdiff1 = abs(pos - michelx1); - - double x2Ddistance1 = sqrt(xdiff1 * xdiff1 + zdiff1 * zdiff1); - - // Endpoint 2 Calculations - double xdiff2 = abs(pos - michelx2); - - double x2Ddistance2 = sqrt(xdiff2 * xdiff2 + zdiff2 * zdiff2); - - if (x2Ddistance1 <= closestdistance1x) { - closestdistance1x = - x2Ddistance1; // this is redundant if I just use index instead - x1_idx = i; - } - if (x2Ddistance2 <= closestdistance2x) { - closestdistance2x = x2Ddistance2; - x2_idx = i; - } - } else if (view == 2) // Calculating 2D distance in U view - { - // Endpoint 2 Calculations - double udiff1 = abs(pos - michelu1); - - double u2Ddistance1 = sqrt(udiff1 * udiff1 + zdiff1 * zdiff1); - - // Endpoint 1 Calculations - double udiff2 = abs(pos - michelu2); - - double u2Ddistance2 = sqrt(udiff2 * udiff2 + zdiff2 * zdiff2); - - if (u2Ddistance1 < closestdistance1u) { - closestdistance1u = u2Ddistance1; - u1_idx = i; - } - if (u2Ddistance2 < closestdistance2u) { - closestdistance2u = u2Ddistance2; - u2_idx = i; - } - - } else if (view == 3) // Calculating 2D dsitance in V view - { - // Endpoint 1 Calculations - double vdiff1 = abs(pos - michelv1); - - double v2Ddistance1 = sqrt(vdiff1 * vdiff1 + zdiff1 * zdiff1); - // Endpoint 2 Calculations - double vdiff2 = abs(pos - michelv2); - - double v2Ddistance2 = sqrt(vdiff2 * vdiff2 + zdiff2 * zdiff2); - - if (v2Ddistance1 < closestdistance1v) { - closestdistance1v = v2Ddistance1; - v1_idx = i; - } - if (v2Ddistance2 <= closestdistance2v) { - closestdistance2v = v2Ddistance2; - v2_idx = i; - } - } - } - - // std::cout << "Printing closest clusters index to each end point: x1: " << - // x1_idx << " u1: " << u1_idx << " v1: " << v1_idx << " x2: " << x2_idx << " - // u2: " << u2_idx << " v2: " << v2_idx << std::endl; - - // Now store the closest X, u, v clusters for each Michel Endpoint based on - // the above closest distance - - // Closest cluster's index will be used to only - - std::vector clusx1; - std::vector clusx2; - - std::vector clusu1; - std::vector clusu2; - - std::vector clusv1; - std::vector clusv2; - for (int i = 0; i < nclusters; i++) { - Cluster current_cluster = Cluster(univ, i); - - double energy = current_cluster.energy; - double time = current_cluster.time; - double pos = current_cluster.pos; - double zpos = current_cluster.zpos; - int view = current_cluster.view; - double timediff = micheltime - time; - if (energy < 2.) continue; - // std::cout << "Printing details about cluster "<< i << " : " << energy - // << " : " << time << " : " << pos << " : " << zpos << " : " << view << " - // : " << timediff << std::endl; - - double zdiff1 = abs(zpos - michelz1); - double zdiff2 = abs(zpos - michelz2); - - // Saving clusters with distances equal to the closest clusters. Again, this - // is probably not the best way. I need to rewrite this section to just use - // clusters from the index I saved in the previous loop. That way I reduce - // the number of clusters that I have to loop over. - - if (view == 1) { - // Endpoint 1 - - if (i == x1_idx) { // if current index is same as the closest endpoint 1 - // cluster in X View - endpoint1_clus.push_back(current_cluster); - this->cluster_to_up_match.push_back( - current_cluster); // this one is redundant - // std::cout << "Printing details about Endpoint - // 1 X cluster index "<< i << " energy : " << - // energy << " time : " << time << " pos : " << - // pos << " zpos : " << zpos << " view : " << - // view - // << " timedifference with Michel : " << - // timediff - // << std::endl; - - clusx1.push_back(pos); - clusx1.push_back(zpos); - } - if (i == x2_idx) { // if current index is same as the closest endpoint 2 - // cluster in X View - endpoint2_clus.push_back(current_cluster); - this->cluster_to_down_match.push_back(current_cluster); // TODO remove - // std::cout << "Printing details about Endpoint 2 X cluster "<< i << " - // energy : " << energy << " time : " << time << " pos : " << pos << " - // zpos: " << zpos << " view : " << view << " timediff : " << timediff - // << std::endl; - - clusx2.push_back(pos); - clusx2.push_back(zpos); - } - - } else if (view == 2) { - if (i == u1_idx) { // if current index is same as the closest endpoint 1 - // cluster in U View - endpoint1_clus.push_back(current_cluster); - this->cluster_to_up_match.push_back(current_cluster); // Remove? - // std::cout << "Printing details about Endpoint 1 U cluster "<< i << " - // : " << energy << " : " << time << " : " << pos << " : " << zpos << " - // : " << view << " : " << timediff << std::endl; - - clusu1.push_back(pos); - clusu1.push_back(zpos); - } - if (i == u2_idx) // if current index is same as the closest endpoint 2 - // cluster in U View - { - endpoint2_clus.push_back(current_cluster); - this->cluster_to_down_match.push_back(current_cluster); // remove? - // std::cout << "Printing details about Endpoint 2 U cluster "<< i << " - // : " << energy << " : " << time << " : " << pos << " : " << zpos << " - // : " << view << " : " << timediff << std::endl; - - clusu2.push_back(pos); - clusu2.push_back(zpos); - } - } else if (view == 3) { - if (i == v1_idx) { // if current index is same as the closest endpoint 1 - // cluster in V View - endpoint1_clus.push_back(current_cluster); - this->cluster_to_up_match.push_back(current_cluster); // remove? - // std::cout << "Printing details about Endpoint 1 V cluster "<< i << " - // : " << energy << " : " << time << " : " << pos << " : " << zpos << " - // : " << view << " : " << timediff << std::endl; - - clusv1.push_back(pos); - clusv1.push_back(zpos); - } - if (i == v2_idx) { // if current index is same as the closest endpoint 2 - // cluster in V View - endpoint1_clus.push_back(current_cluster); - this->cluster_to_down_match.push_back(current_cluster); // remove? - // std::cout << "Printing details about Endpoint 2 V cluster "<< i << " - // : " << energy << " : " << time << " : " << pos << " : " << zpos << " - // : " << view << " : " << timediff << std::endl; - - clusv2.push_back(pos); - clusv2.push_back(zpos); - } - } - - } // End of loop over clusters - - // This is vector of positions for each endpoint cluster match - std::vector matchclus1; // index [0] = x, [1] = y, [2] = z - std::vector matchclus2; // index [0] = x, [1] = y, [2] = z - - // std::cout << "LOOPING OVER ENDPOINT1 CLUSTERS" << std::endl; - - double XZdist1 = 9999.; - double UZdist1 = 9999.; - double VZdist1 = 9999.; - - // Check if our cluster vectors are empty and calculate 2D distances for - // endpoint 1 - if (!clusx1.empty()) { - double xdif = abs(this->m_x1 - clusx1[0]); - double zdif = abs(this->m_z1 - clusx1[1]); - XZdist1 = sqrt(xdif * xdif + zdif * zdif); - } - if (!clusu1.empty()) { - double udif = abs(this->m_u1 - clusu1[0]); - double zdif = abs(this->m_z1 - clusu1[1]); - UZdist1 = sqrt(udif * udif + zdif * zdif); - } - if (!clusv1.empty()) { - double vdif = abs(this->m_v1 - clusv1[0]); - double zdif = abs(this->m_z1 - clusv1[1]); - VZdist1 = sqrt(vdif * vdif + zdif * zdif); - } - - // std::cout << " XZ, UZ, VZ 1: " << XZdist1 << " , " << UZdist1 << " , " << - // VZdist1 << std::endl; Saving the 2D distances for endpoint 1 - this->up_to_clus_XZ = XZdist1; - this->up_to_clus_UZ = UZdist1; - this->up_to_clus_VZ = VZdist1; - - double XZdist2 = 9999.; - double UZdist2 = 9999.; - double VZdist2 = 9999.; - - if (!clusx2.empty()) { - double xdif = abs(this->m_x2 - clusx2[0]); - double zdif = abs(this->m_z2 - clusx2[1]); - XZdist2 = sqrt(xdif * xdif + zdif * zdif); - } - if (!clusu2.empty()) { - double udif = abs(this->m_u2 - clusu2[0]); - double zdif = abs(this->m_z2 - clusu2[1]); - UZdist2 = sqrt(udif * udif + zdif * zdif); - } - if (!clusv2.empty()) { - double vdif = abs(this->m_v2 - clusv2[0]); - double zdif = abs(this->m_z2 - clusv2[1]); - VZdist2 = sqrt(vdif * vdif + zdif * zdif); - } - - this->down_to_clus_XZ = XZdist2; - this->down_to_clus_UZ = UZdist2; - this->down_to_clus_VZ = VZdist2; - this->down_clus_y = michely2; - this->down_clus_x = michelx2; - this->down_clus_z = michelz2; - - // std::cout << "GET 3D Information for Clusters - ENDPOINT1" << std::endl; - // This is the convoluted system that calculates the Endpoint - if (XZdist1 < UZdist1 && XZdist1 < VZdist1 && - UZdist1 < VZdist1) { // XU views closest - if (!clusu1.empty() && !clusx1.empty()) { - double yclus = (1. / sqrt(3.)) * (clusx1[0] - 2 * clusu1[0]); - matchclus1.push_back(clusx1[0]); - matchclus1.push_back(yclus); // y point of match 3D point - matchclus1.push_back(clusx1[1]); // setting the cluster 3D point z to be - // of the closest view - // std::cout << "The 2 closest clusters to EndPoint 1 are X and U with (x, - // y, z) point ( " << clusx1[0] << " , " << yclus << " , " << clusx1[1] << - // " ) " << std::endl; - } - } else if (XZdist1 < UZdist1 && XZdist1 < VZdist1 && - UZdist1 > VZdist1) { // XV closest - if (!clusv1.empty() && !clusx1.empty()) { - double yclus = (1. / sqrt(3.)) * (2 * clusv1[0] - clusx1[0]); - // std::cout << "The 2 closest clusters to Endpoint 1 are X and V with (x, - // y, z) point ( " << clusx1[0] << " , " << yclus << " , " << clusx1[1] << - // " ) " << std::endl; - - matchclus1.push_back(clusx1[0]); - matchclus1.push_back(yclus); - matchclus1.push_back(clusx1[1]); // seting the cluster 3D point z to be - // of the closest view - } - } else if (UZdist1 < XZdist1 && UZdist1 < VZdist1 && - VZdist1 < XZdist1) { // UV closest - if (!clusv1.empty() && !clusu1.empty()) { - double yclus = (1. / sqrt(3.)) * (clusv1[0] - clusu1[0]); - double xclus = clusu1[0] + clusv1[0]; - // std::cout << "The 2 closest clusters to EndPoint 1 are U and V with (x, - // y, z) point ( " << xclus << " , " << yclus << " , " << clusu1[1] << " ) - // " << std::endl; - - matchclus1.push_back(xclus); - matchclus1.push_back(yclus); - matchclus1.push_back(clusu1[1]); // seting the cluster 3D point z to be - // of the closest view - } - } else if (UZdist1 < XZdist1 && UZdist1 < VZdist1 && - VZdist1 > XZdist1) { // UX closest - if (!clusu1.empty() && !clusx1.empty()) { - double yclus = (1. / sqrt(3.)) * (clusx1[0] - 2 * clusu1[0]); - // std::cout << "The 2 closest clusters to EndPoint 1 are U and X with (x, - // y, z) point ( " << clusx1[0] << " , " << yclus << " , " << clusu1[1] << - // " ) " << std::endl; - - matchclus1.push_back(clusx1[0]); - matchclus1.push_back(yclus); - matchclus1.push_back(clusu1[1]); // seting the cluster 3D point z to be - // of the closest view - this->up_clus_y = michely1; - } - } else if (VZdist1 < XZdist1 && VZdist1 < UZdist1 && - XZdist1 < UZdist1) { // VX closest - if (!clusv1.empty() && !clusx1.empty()) { - double yclus = ((1. / sqrt(3.)) * (2 * clusv1[0] - clusx1[0])); - // std::cout << "The 2 closest clusters to EndPoint 1 are V and X with (x, - // y, z) point ( " << clusx1[0] << " , " << yclus << " , " << clusv1[1] << - // " ) " << std::endl; - - matchclus1.push_back(clusx1[0]); - matchclus1.push_back(yclus); - matchclus1.push_back(clusv1[1]); // seting the cluster 3D point z to be - // of the closest view - this->up_clus_y = michely1; - } - } else if (VZdist1 < XZdist1 && VZdist1 < UZdist1 && - XZdist1 > UZdist1) { // VU closest - if (!clusu1.empty() && !clusv1.empty()) { - double xclus = (1. / sqrt(3.)) * (clusv1[0] - clusu1[0]); - double yclus = clusu1[0] + clusv1[0]; - // std::cout << "The 2 closest clusters to EndPoint 1 are V and U with (x, - // y, z) point ( " << xclus << " , " << yclus << " , " << clusv1[1] << " ) - // " << std::endl; - - matchclus1.push_back(xclus); - matchclus1.push_back(yclus); - matchclus1.push_back(clusv1[1]); // seting the cluster 3D point z to be - // of the closest view - this->up_clus_y = michely1; - this->up_clus_x = michelx1; - } - } - if (XZdist2 < UZdist2 && XZdist2 < VZdist2 && UZdist2 < VZdist2) { - // std::cout << "XU closest to endpoint 2" << std::endl; - if (!clusu2.empty() && !clusx2.empty()) { - double yclus = (1. / sqrt(3.)) * (clusx2[0] - 2 * clusu2[0]); - // std::cout << "The 2 closest clusters to EndPoint 2 are X and U with (x, - // y, z) point ( " << clusx2[0] << " , " << yclus << " , " << clusx2[1] << - // " ) " << std::endl; - - matchclus2.push_back(clusx2[0]); - matchclus2.push_back(yclus); - matchclus2.push_back(clusx2[1]); // seting the cluster 3D point z to be - // of the closest view - - this->down_clus_y = michely2; - } - } else if (XZdist2 < UZdist2 && XZdist2 < VZdist2 && UZdist2 > VZdist2) { - // std::cout << "XV closest to endpoint 2" << std::endl; - if (!clusv2.empty() && !clusx2.empty()) { - double yclus = (1. / sqrt(3.)) * (2 * clusv2[0] - clusx2[0]); - // std::cout << "The 2 closest clusters to EndPoint 2 are X and V with (x, - // y, z) point ( " << clusx2[0] << " , " << yclus << " , " << clusx2[1] << - // " ) " << std::endl; - - matchclus2.push_back(clusx2[0]); - matchclus2.push_back(yclus); - matchclus2.push_back(clusx2[1]); // seting the cluster 3D point z to be - // of the closest view - this->down_clus_y = michely2; - } - } else if (UZdist2 < XZdist2 && UZdist2 < VZdist2 && VZdist2 < XZdist2) { - // std::cout << "UV closest to endpoint 2" << std::endl; - if (!clusu2.empty() && !clusv2.empty()) { - double xclus = clusu2[0] + clusv2[0]; - double yclus = (1. / sqrt(3.)) * (clusv2[0] - clusu2[0]); - // std::cout << "The 2 closest clusters to EndPoint 2 are U and V with (x, - // y, z) point ( " << xclus << " , " << yclus << " , " << clusu2[1] << " ) - // " << std::endl; - - matchclus2.push_back(xclus); - matchclus2.push_back(yclus); - matchclus2.push_back(clusu2[1]); // seting the cluster 3D point z to be - // of the closest view - this->down_clus_y = michely2; - this->down_clus_y = michelx2; - } - } else if (UZdist2 < XZdist2 && UZdist2 < VZdist2 && VZdist2 > XZdist2) { - // std::cout << "UX closest to endpoint 2" << std::endl; - if (!clusu2.empty() && !clusx2.empty()) { - double yclus = (1. / sqrt(3.)) * (clusx2[0] - 2 * clusu2[0]); - // std::cout << "The 2 closest clusters to EndPoint 2 are U and X with (x, - // y, z) point ( " << clusx2[0] << " , " << yclus << " , " << clusu2[1] << - // " ) " << std::endl; - - matchclus2.push_back(clusx2[0]); - matchclus2.push_back(yclus); - matchclus2.push_back(clusu2[1]); // seting the cluster 3D point z to be - // of the closest view - this->down_clus_y = michely2; - } - } else if (VZdist2 < XZdist2 && VZdist2 < UZdist2 && XZdist2 < UZdist2) { - // std::cout << "VX closest to endpoint 2" << std::endl; - if (!clusv2.empty() && !clusx2.empty()) { - double yclus = ((1. / sqrt(3.)) * (2 * clusv2[0] - clusx2[0])); - // std::cout << "The 2 closest clusters to EndPoint 2 are V and X with (x, - // y, z) point ( " << clusx2[0] << " , " << yclus << " , " << clusv2[1] << - // " ) " << std::endl; - - matchclus2.push_back(clusx2[0]); - matchclus2.push_back(yclus); - matchclus2.push_back(clusv2[1]); // seting the cluster 3D point z to be - // of the closest view - this->down_clus_y = michely2; - } - } else if (VZdist2 < XZdist2 && VZdist2 < UZdist2 && XZdist2 > UZdist2) { - // std::cout << "VU closest to endpoint 2" << std::endl; - if (!clusu2.empty() && !clusv2.empty()) { - double xclus = (1. / sqrt(3.)) * (clusv2[0] - clusu2[0]); - double yclus = clusu2[0] + clusv2[0]; - // std::cout << "The 2 closest clusters to EndPoint 2 are V and U with (x, - // y, z) point ( " << xclus << " , " << yclus << " , " << clusv2[1] << " ) - // " << std::endl; - - matchclus2.push_back(xclus); - matchclus2.push_back(yclus); - matchclus2.push_back(clusv2[1]); // seting the cluster 3D point z to be - // of the closest view - this->down_clus_y = michely2; - this->down_clus_x = michelx2; - } - } - - // std::cout << "GETTING 3D DISTANCES FOR THE CUSTERS " << std::endl; - double clusx1diff = 9999.; - double clusy1diff = 9999.; - double clusz1diff = 9999.; - - double mclusx1diff = 9999.; - double mclusy1diff = 9999.; - double mclusz1diff = 9999.; - - double michvtx_x1diff = 9999.; - double michvtx_x2diff = 9999.; - double michvtx_y1diff = 9999.; - double michvtx_y2diff = 9999.; - double michvtx_z1diff = 9999.; - double michvtx_z2diff = 9999.; - - if (!matchclus1.empty()) { - clusx1diff = vtx_x - matchclus1[0]; - clusy1diff = vtx_y - matchclus1[1]; - clusz1diff = vtx_z - matchclus1[2]; - mclusx1diff = michelx1 - matchclus1[0]; - mclusy1diff = michely1 - matchclus1[1]; - mclusz1diff = michelz1 - matchclus1[2]; - michvtx_x1diff = michelx1 - vtx_x; - michvtx_y1diff = michely1 - vtx_y; - michvtx_z1diff = michelz1 - vtx_z; - // std::cout << "3D point for Endpoint 1 Clusters is (x, y, z) " << - // matchclus1[0] << " , " << matchclus1[1] << " , " << matchclus1[2] << - // std::endl; - } - double clusx2diff = 9999.; - double clusy2diff = 9999.; - double clusz2diff = 9999.; - - double mclusx2diff = 9999.; - double mclusy2diff = 9999.; - double mclusz2diff = 9999.; - - if (!matchclus2.empty()) { - clusx2diff = vtx_x - matchclus2[0]; - clusy2diff = vtx_y - matchclus2[1]; - clusz2diff = vtx_z - matchclus2[2]; - mclusx2diff = michelx2 - matchclus2[0]; - mclusy2diff = michely2 - matchclus2[1]; - mclusz2diff = michelz2 - matchclus2[2]; - michvtx_x2diff = michelx2 - vtx_x; - michvtx_y2diff = michely2 - vtx_y; - michvtx_z2diff = michelz2 - vtx_z; - // std::cout << "3D point for Endpoint 2 Clusters is (x, y, z) " << - // matchclus2[0] << " , " << matchclus2[1] << " , " << matchclus2[2] << - // std::endl; - } - /// 2 types of 3D distance calculations for Cluster matching: - // 1. Cluster to Vertex - double dist1 = - sqrt(pow(clusx1diff, 2) + pow(clusy1diff, 2) + pow(clusz1diff, 2)); - double dist2 = - sqrt(pow(clusx2diff, 2) + pow(clusy2diff, 2) + pow(clusz2diff, 2)); - // 2. Cluster to Michel - double mdist1 = - sqrt(pow(mclusx1diff, 2) + pow(mclusy1diff, 2) + pow(mclusz1diff, 2)); - double mdist2 = - sqrt(pow(mclusx2diff, 2) + pow(mclusy2diff, 2) + pow(mclusz2diff, 2)); - // std::cout << " The michel endpoint 1 - cluster 3D point distance is " << - // mdist1 << std::endl; std::cout << " The michel endpoint 2 - cluster 3D - // point distance is " << mdist2 << std::endl; Saving all the distances to the - // Michel member data - this->down_clus_michel_dist3D = mdist2; - this->up_clus_michel_dist3D = mdist1; - this->up_to_cluster_dist3D = dist1; - this->down_to_cluster_dist3D = dist2; - // std::cout << "Printing 3D distances to vertex for cluster matches " << - // dist1 << " and " << dist2 << std::endl; std::cout << "Printing 3D distances - // to michel for cluster matches " << mdist1 << " and " << mdist2 << - // std::endl; - double michdist1 = sqrt(pow(michvtx_x1diff, 2) + pow(michvtx_y1diff, 2) + - pow(michvtx_z1diff, 2)); - double michdist2 = sqrt(pow(michvtx_x2diff, 2) + pow(michvtx_y2diff, 2) + - pow(michvtx_z2diff, 2)); - this->up_clus_michvtx_dist3D = michdist1; - this->down_clus_michvtx_dist3D = michdist2; - // Marks which endpoint is the closest match for this match type - if (dist1 < dist2) - this->clus_endpoint = 1; - else if (dist1 > dist2) - this->clus_endpoint = 2; - } - - struct MichelEvent { - int m_idx = -1; // Index for Best Michel in nmichels - double m_bestdist = 9999.; // in mm - std::vector m_best2D; // 0: XZ, 1: UZ, 2:VZ - double m_best_XZ = 9999.; - double m_best_UZ = 9999.; - double m_best_VZ = 9999.; - int m_matchtype; // 0 NULL 1 UPVTX 2 DOWNVTX 3 UPCLUS 4 DOWNCLUS - std::vector m_nmichels; // nmatched michels - std::vector m_ntruepiparents; // michels with true pion parent - std::vector - m_nmichelspass; // if some distance cut is applied, we can store the - // michels that passed for this event in here - double best_x = 9999.; - double best_y = 9999.; - double best_z = 9999.; - double b_truex = 9999.; - double b_truey = 9999.; - double b_truez = 9999.; - int bestparentpdg = -1; - int bestparenttrackid = -1; - int eventtype = - 0; // 0 = null, 1 = only 1 pi+ and no other pion, 2= npi+ and other pion, - // 3 = npi0 and no other pion, 4 = kaons in event, 5 = other - double lowTpi = 9999.; - }; - - // Create Michel objects for each Michel candidate. Add the passing ones to - // the MichelEvent container. - MichelEvent GetQualityMichels(const CVUniverse& univ) { - MichelEvent evt{}; - //========================================================================== - // First: Create a Michel object from each candidate and add them to the - // MichelEvent container. - //========================================================================== - int nmichels = univ.GetNMichels(); - for (int i = 0; i < nmichels; ++i) { - Michel current_michel = Michel(univ, i); - if (current_michel.true_parentpdg == 211) - evt.m_ntruepiparents.push_back(current_michel); - double dist = current_michel.Best3Ddist; // getting the minimum pion range (vertex to Michel/Clus distance) - if (dist <= evt.m_bestdist) { - evt.m_bestdist = dist; - evt.m_idx = i; - - evt.m_best_XZ = current_michel.best_XZ; - evt.m_best_UZ = current_michel.best_UZ; - evt.m_best_VZ = current_michel.best_VZ; - evt.m_matchtype = current_michel.BestMatch; - int bmatch = current_michel.BestMatch; - if (bmatch == 1 || bmatch == 3) { - evt.best_x = current_michel.m_x1; - evt.best_y = current_michel.m_y1; - evt.best_z = current_michel.m_z1; - } else if (bmatch == 2 || bmatch == 4) { - evt.best_x = current_michel.m_x2; - evt.best_y = current_michel.m_y2; - evt.best_z = current_michel.m_z2; - } - evt.b_truex = current_michel.true_initialx; - evt.b_truey = current_michel.true_initialy; - evt.b_truez = current_michel.true_initialz; - } - evt.m_nmichels.push_back(current_michel); - } - - double lowtpiinevent = univ.GetTrueTpi(); - evt.lowTpi = lowtpiinevent; - //========================================================================== - - //========================================================================== - // Second: remove Michels from the MichelEvent container that fail (and - // fill-in more info about the passing Michels). - //========================================================================== - std::vector nmichelspass; - const double m_maxDistance = 150; // Maximum distance from the vertex that - // the best Michel can have in mm - for (unsigned int i = 0; i < evt.m_nmichels.size(); i++) { - // For Vertex Match Check to see if 2D distance cut will - double upvtxXZ = evt.m_nmichels[i].up_to_vertex_XZ; - double downvtxXZ = evt.m_nmichels[i].down_to_vertex_XZ; - double upvtxUZ = evt.m_nmichels[i].up_to_vertex_UZ; - double downvtxUZ = evt.m_nmichels[i].down_to_vertex_UZ; - double upvtxVZ = evt.m_nmichels[i].up_to_vertex_VZ; - double downvtxVZ = evt.m_nmichels[i].down_to_vertex_VZ; - - if (upvtxXZ < m_maxDistance && - (upvtxUZ < m_maxDistance || upvtxVZ < m_maxDistance)) - evt.m_nmichels[i].passable_matchtype.at(0) = 1; - else if (upvtxUZ < m_maxDistance && - (upvtxXZ < m_maxDistance || upvtxVZ < m_maxDistance)) - evt.m_nmichels[i].passable_matchtype.at(0) = 1; - else if (upvtxVZ < m_maxDistance && - (upvtxXZ < m_maxDistance || upvtxUZ < m_maxDistance)) - evt.m_nmichels[i].passable_matchtype.at(0) = 1; - else - evt.m_nmichels[i].passable_matchtype.at(0) = -1; - - if (downvtxXZ < m_maxDistance && - (downvtxUZ < m_maxDistance || downvtxVZ < m_maxDistance)) - evt.m_nmichels[i].passable_matchtype.at(1) = 2; - else if (downvtxUZ < m_maxDistance && - (downvtxXZ < m_maxDistance || downvtxVZ < m_maxDistance)) - evt.m_nmichels[i].passable_matchtype.at(1) = 2; - else if (downvtxVZ < m_maxDistance && - (downvtxXZ < m_maxDistance || downvtxUZ < m_maxDistance)) - evt.m_nmichels[i].passable_matchtype.at(1) = 2; - else - evt.m_nmichels[i].passable_matchtype.at(1) = -1; - - double upclusXZ = evt.m_nmichels[i].up_to_clus_XZ; - double upclusUZ = evt.m_nmichels[i].up_to_clus_UZ; - double upclusVZ = evt.m_nmichels[i].up_to_clus_VZ; - double downclusXZ = evt.m_nmichels[i].down_to_clus_XZ; - double downclusUZ = evt.m_nmichels[i].down_to_clus_UZ; - double downclusVZ = evt.m_nmichels[i].down_to_clus_VZ; - - if (upclusXZ < m_maxDistance && - (upclusUZ < m_maxDistance || upclusVZ < m_maxDistance)) - evt.m_nmichels[i].passable_matchtype.at(2) = 3; - else if (upclusUZ < m_maxDistance && - (upclusXZ < m_maxDistance || upclusVZ < m_maxDistance)) - evt.m_nmichels[i].passable_matchtype.at(2) = 3; - else if (upclusVZ < m_maxDistance && - (upclusXZ < m_maxDistance || upclusUZ < m_maxDistance)) - evt.m_nmichels[i].passable_matchtype.at(2) = 3; - else - evt.m_nmichels[i].passable_matchtype.at(2) = -1; - - if (downclusXZ < m_maxDistance && - (downclusUZ < m_maxDistance || downclusVZ < m_maxDistance)) - evt.m_nmichels[i].passable_matchtype.at(3) = 4; - else if (downclusUZ < m_maxDistance && - (downclusXZ < m_maxDistance || downclusVZ < m_maxDistance)) - evt.m_nmichels[i].passable_matchtype.at(3) = 4; - else if (downclusVZ < m_maxDistance && - (downclusXZ < m_maxDistance || downclusUZ < m_maxDistance)) - evt.m_nmichels[i].passable_matchtype.at(3) = 4; - else - evt.m_nmichels[i].passable_matchtype.at(3) = -1; - - std::vector distances3D; - - if (evt.m_nmichels[i].passable_matchtype[0] == 1) - distances3D.push_back(evt.m_nmichels[i].up_to_vertex_dist3D); - if (evt.m_nmichels[i].passable_matchtype[1] == 2) - distances3D.push_back(evt.m_nmichels[i].down_to_vertex_dist3D); - if (evt.m_nmichels[i].passable_matchtype[2] == 3) - distances3D.push_back(evt.m_nmichels[i].up_clus_michel_dist3D); - if (evt.m_nmichels[i].passable_matchtype[3] == 4) - distances3D.push_back(evt.m_nmichels[i].down_clus_michel_dist3D); - if (distances3D.empty()) distances3D = {9999., 9999., 9999., 9999.}; - if (distances3D[0] == 9999.) - continue; // Comment this line out if you are making efficiency plots - - std::sort(distances3D.begin(), distances3D.end()); - - if (distances3D[0] == evt.m_nmichels[i].up_to_vertex_dist3D) { - evt.m_nmichels[i].best_XZ = evt.m_nmichels[i].up_to_vertex_XZ; - evt.m_nmichels[i].best_UZ = evt.m_nmichels[i].up_to_vertex_UZ; - evt.m_nmichels[i].best_VZ = evt.m_nmichels[i].up_to_vertex_VZ; - evt.m_nmichels[i].BestMatch = 1; - evt.m_nmichels[i].Best3Ddist = evt.m_nmichels[i].up_to_vertex_dist3D; - - } else if (distances3D[0] == evt.m_nmichels[i].down_to_vertex_dist3D) { - evt.m_nmichels[i].BestMatch = 2; - evt.m_nmichels[i].best_XZ = evt.m_nmichels[i].down_to_vertex_XZ; - evt.m_nmichels[i].best_UZ = evt.m_nmichels[i].down_to_vertex_UZ; - evt.m_nmichels[i].best_VZ = evt.m_nmichels[i].down_to_vertex_VZ; - evt.m_nmichels[i].Best3Ddist = evt.m_nmichels[i].down_to_vertex_dist3D; - // std::cout << "This Michel is DOWNVTX and has true endpoint " << - // evt.m_nmichels[i].trueEndpoint << std::endl; - - } else if (distances3D[0] == evt.m_nmichels[i].up_clus_michel_dist3D) { - evt.m_nmichels[i].BestMatch = 3; - evt.m_nmichels[i].best_XZ = evt.m_nmichels[i].up_to_clus_XZ; - evt.m_nmichels[i].best_UZ = evt.m_nmichels[i].up_to_clus_VZ; - evt.m_nmichels[i].best_VZ = evt.m_nmichels[i].up_to_clus_UZ; - evt.m_nmichels[i].Best3Ddist = evt.m_nmichels[i].up_clus_michvtx_dist3D; - // std::cout << "This Michel is UPCLUS and has true endpoint " << - // evt.m_nmichels[i].trueEndpoint << std::endl; - - } else if (distances3D[0] == evt.m_nmichels[i].down_clus_michel_dist3D) { - evt.m_nmichels[i].BestMatch = 4; - evt.m_nmichels[i].Best3Ddist = - evt.m_nmichels[i].down_clus_michvtx_dist3D; - evt.m_nmichels[i].best_XZ = evt.m_nmichels[i].down_to_clus_XZ; - evt.m_nmichels[i].best_UZ = evt.m_nmichels[i].down_to_clus_UZ; - evt.m_nmichels[i].best_VZ = evt.m_nmichels[i].down_to_clus_VZ; - // std::cout << "This Michel is DOWNCLUS and has true endpoint " << - // evt.m_nmichels[i].trueEndpoint << std::endl; - - } else { - evt.m_nmichels[i].BestMatch = 0; - evt.m_nmichels[i].Best3Ddist = 9999.; - evt.m_nmichels[i].best_XZ = 9999.; - evt.m_nmichels[i].best_UZ = 9999.; - evt.m_nmichels[i].best_VZ = 9999.; - continue; - } - - nmichelspass.push_back(evt.m_nmichels[i]); - } - - evt.m_nmichels.clear(); // empty existing vector of Michels - evt.m_nmichels = nmichelspass; // replace vector of michels with the vector - // of michels that passed the above cut - //========================================================================== - - - return evt; - } - -} // namespace vertex +namespace endpoint { +class Michel; // forward declare +typedef std::map MichelMap; + +class Michel { + public: + enum EMatchCategory { kNoMatch, kFit, kNoFit, kOV, kNMatchCategories }; + + // constructors + Michel(const CVUniverse& univ, const int i, const int v); + Michel() + : idx(-105), + vtx(-106), + had_idx(-107), + match_category(kNoMatch), + fit_distance(-1.) {} + + // integer that uniquely identifies cluster of hits + int idx; + + // integer corresponding to vertex to which cluster was matched. + // vtx == 0 --> interaction vertex + // vtx == 1 --> "first" track endpoint, corresponds to hadron index 0. + // i.e. hadron index = michel vtx - 1. + int vtx; + + // hadron index to which this michel is matched (had_idx = vtx - 1) + int had_idx; + + EMatchCategory match_category; + double fit_distance; +}; + +// Michel quality cuts +bool IsQualityMatchedMichel_Fit(const double fit_dist, const double fit_cut); +bool IsQualityMatchedMichel_NoFit(const double nofit_dist, + const double nofit_cut); +bool IsQualityMatchedMichel_OneView(const double ov_dist, const double ov_cut); + +// -- Given a single michel cluster matched to two vertices +// return vertex with the better-matched michel. +Michel CompareMichels(const Michel& r, const Michel& c); + +// Add michel to MichelMap. Check if this cluster has already been matched. +// Then use only the best match. +bool AddOrReplaceMichel(MichelMap& mm, const Michel& m); + +// Collect the good michels in this event +MichelMap GetQualityMichels(const CVUniverse& univ); +} // namespace endpoint +//============================================================================== +// Trackless Michel +//============================================================================== +namespace trackless { +class Michel { + public: + // constructors + Michel(const CVUniverse& univ, const int ci); + Michel(){}; + + // fill in more complicated stuff in "generic info" + void DoMoreInitializationAndProcessing(); + + // fill in "best matching stuff" + void DoMatching(); + + // Gets info for Vtx Match + void DoesMichelMatchVtx(const CVUniverse& univ); + + // Gets info for ClusterMatch + void DoesMichelMatchClus(const CVUniverse& univ); + + // get type for best match out of all four saved matches + void GetBestMatch(); + + // Function to calculate pion angle with respect to beam. Simply gets + // angle between best michel endpoint and vertex. + void GetPionAngle(const CVUniverse& univ); + + // std::vector CreateMichels(CVUniverse& univ); + + // Data + std::vector up_location; // upstream location 0 X 1 U 2 V 3 Z + std::vector down_location; // downstream location + double m_x1 = 9999.; // Michel Endpoint 1 x + double m_x2 = 9999.; // Michel Endpoint 2 x + double m_y1 = 9999.; // Michel Endpoint 1 y + double m_y2 = 9999.; // Michel Endpoint 2 y + double m_u1 = 9999.; // Michel Endpoint 1 u + double m_u2 = 9999.; // Michel Endpoint 2 u + double m_v1 = 9999.; // Michel Endpoint 1 v + double m_v2 = 9999.; // Michel Endpoint 2 v + double m_z1 = 9999.; // Mihel Endpoint z 1 + double m_z2 = 9999.; // Michel Endpoiint z2 + double energy = -999.; // Michel energy + double time = -999.; // Michel Time + int is_fitted = -1; // Is the Michel fitted? 0 no. 1 yes. + // Following are 2D distances (were stored in vectors but now as explicit data + // members) + double up_to_vertex_XZ = 9999.; + double up_to_vertex_UZ = 9999.; + double up_to_vertex_VZ = 9999.; + double down_to_vertex_XZ = 9999.; + double down_to_vertex_UZ = 9999.; + double down_to_vertex_VZ = 9999.; + double down_to_clus_XZ = 9999.; + double down_to_clus_UZ = 9999.; + double down_to_clus_VZ = 9999.; + double up_to_clus_XZ = 9999.; + double up_to_clus_UZ = 9999.; + double up_to_clus_VZ = 9999.; + // Michel End point to Vertex distance (up = endpoint 1 and down = endpoint + // 2) TODO: actually find out which end point is upstream or downstream + double up_to_vertex_dist3D = 9999.; + double down_to_vertex_dist3D = 9999.; + // Maybe keep a vector of clusters that matched to each endpoint? + std::vector cluster_to_up_match; + std::vector cluster_to_down_match; + // 3D distances between cluster and Michel + double up_to_cluster_dist3D = 9999.; // Distance between vertex and cluster + // that was matched to endpoint 1 + double down_to_cluster_dist3D = + 9999.; // Distance between vertex and cluster matched to endpoint 2 + double up_clus_michel_dist3D = + 9999.; // Distance between Michel endpoint 1 and clusters + double down_clus_michel_dist3D = + 9999.; // Distance between Michel endpoint 2 and clusters + double up_clus_michvtx_dist3D = + 9999.; // Distance between the Michel end point 1 that matched to + // clusters and the vertex - this will be used as pion range + double down_clus_michvtx_dist3D = + 9999.; // Distance between the Michel endpoint 2 that matched to clusters + // and the vertex - this will be used as pion range + double vtx_michel_timediff = 9999.; + + double overlay_fraction = + -1.0; // Overlay fraction of the Michel, Default if Data. 0 if MC 1 if + // Data... (maybe some events in between?) + int nclusters = 0; // number of (non-muon) clusters in the primary event + int vtx_endpoint = 0; // 1 or 2 for which Michel end point is closest + int clus_endpoint = 0; // 1 or 2 for which Michel endpoint is closest + // best matching stuff + // enum *best_cluster_match; // just tells you which of the four matches are + // the best match enum BestClusterMatch {kUpVtxMatch, kDownVtxMatch, + // kUpClusMatch, kDownClusMatch, kNClusterMatches}; the following is in place + // until i figure out how to use the enum. + int BestMatch = 0; // 0 = null match, 1= kUpVtxMatch, 2 = kDownVtxMatch, 3 = + // kUpClusMatch, 4= kDownClusMatch, + int SecondBestMatch = 0; + int tuple_idx; // index of the Michel out of all the michels saved in the + // tuple + double Best3Ddist = + 9999.; // Best 3D distance out of eitehr a vertex or a cluster match + // best 2D distance for the best type of match + double best_XZ = 9999.; + double best_UZ = 9999.; + double best_VZ = 9999.; + // Want to save the index of the clusters that the Michel best matched to. + int xclus_idx; + int uclus_idx; + int vclus_idx; + + // True initial position of the michel TODO: initial the other Michel truth + // member data here (energy, time, momentum etc) + double true_angle = 9999.; + double true_initialx = 9999.; + double true_initialy = 9999.; + double true_initialz = 9999.; + double true_e = 9999.; + double true_p = 9999.; + double true_pdg = -1.0; + int true_parentid = -1; + int true_parentpdg = -1; + double true_parent_energy = -9999.; + double true_parent_p = -9999.; + double true_parent_px = -9999.; + double true_parent_py = -9999.; + double true_parent_pz = -9999.; + double true_parent_xi = -9999.; + double true_parebt_yi = -9999.; + double true_parent_zi = -9999.; + double true_parent_xf = -9999.; + double true_parent_yf = -9999.; + double true_parent_zf = -9999.; + // the following member data were created to investigate my weird convoluted + // way of geting x and y values. Probably dont need them now. + // TODO: check and remove the following member data + double best_angle = -9999.; + double up_clus_x = 9999.; + double up_clus_y = 9999.; + double up_clus_z = 9999.; + double down_clus_x = 9999.; + double down_clus_y = 9999.; + double down_clus_z = 9999.; + double up_vtx_x = 9999.; + double up_vtx_y = 9999.; + double up_vtx_z = 9999.; + double down_vtx_x = 9999.; + double down_vtx_y = 9999.; + double down_vtx_z = 9999.; + double is_overlay = -1; + double pionKE = -9999.; + // Adding the following member data to determine the true endpoint position of + // the Michel + // 0 = Overlay Michel, 1 = Endpoint 1 is correct intial position of + // Michel, 2 = Endpoint 2 is correct Initial Position of Michel + int trueEndpoint = -1; + + // ~DeleteMichel(){delete this;}; // This is going to be the main destructor. + + // -1 is default/NULL like above. 1 = Endpoint 1 is better match, 2 = + // Endpoint 2 is better match + int recoEndpoint = -1; + + // This vector will contain a value for each match type -1 or the + // matchtype 1, 2, 3, 4 for UpVtx, DownVTx, Upclus, DownClus depending of + // that match type passes our 2D distance cut. (if distance is large, then + // it'll pass all of them). + std::vector passable_matchtype{-1, -1, -1, -1}; +}; + +// Create Michel objects for each Michel candidate. Add the good ones to the +// MichelEvent container. +MichelEvent GetQualityMichels(const CVUniverse& univ); +} // namespace trackless #endif diff --git a/includes/MichelEvent.h b/includes/MichelEvent.h new file mode 100644 index 0000000..431ad92 --- /dev/null +++ b/includes/MichelEvent.h @@ -0,0 +1,46 @@ +#ifndef MichelEvent_h +#define MichelEvent_h + +//============================================================================== +// Data container class containing all the michel info needed to do a trackless +// michel analysis. +// The trackless::Michel class initializes these. +//============================================================================== + +#include + +namespace trackless { +class Michel; // forward declare +struct MichelEvent { + int m_idx = -1; // Index for Best Michel in nmichels + double m_bestdist = 9999.; // in mm + std::vector m_best2D; // 0: XZ, 1: UZ, 2:VZ + double m_best_XZ = 9999.; + double m_best_UZ = 9999.; + double m_best_VZ = 9999.; + int m_matchtype; // 0 NULL 1 UPVTX 2 DOWNVTX 3 UPCLUS 4 DOWNCLUS + std::vector m_nmichels; // nmatched michels + std::vector m_ntruepiparents; // michels with true pion parent + + // if some distance cut is applied, we can store the michels that passed for + // this event in here. + std::vector m_nmichelspass; + + double best_x = 9999.; + double best_y = 9999.; + double best_z = 9999.; + double b_truex = 9999.; + double b_truey = 9999.; + double b_truez = 9999.; + int bestparentpdg = -1; + int bestparenttrackid = -1; + + // 0 = null, 1 = only 1 pi+ and no other pion, 2= npi+ and other pion, + // 3 = npi0 and no other pion, 4 = kaons in event, 5 = other + int eventtype = 0; + + double lowTpi = 9999.; +}; +} // namespace trackless + +#endif diff --git a/loadLibs.C b/loadLibs.C index 99173f1..dfd918e 100644 --- a/loadLibs.C +++ b/loadLibs.C @@ -15,16 +15,18 @@ void loadIncludes(bool verbose_cvu) { oldpath += path; gSystem->SetIncludePath(oldpath); gSystem->CompileMacro("CVUniverse.cxx", cvu_flags); - gSystem->CompileMacro("CutUtils.h", "k"); + gSystem->CompileMacro("Cluster.h", "k"); + gSystem->CompileMacro("Michel.cxx", "k"); + //gSystem->CompileMacro("CutUtils.h", "k"); gSystem->CompileMacro("Cuts.cxx", "k"); - gSystem->CompileMacro("StackedHistogram.cxx", "k"); - gSystem->CompileMacro("Histograms.cxx", "k"); - gSystem->CompileMacro("Variable.cxx", "k"); - gSystem->CompileMacro("HadronVariable.cxx", "k"); - gSystem->CompileMacro("MacroUtil.cxx", "k"); - gSystem->CompileMacro("CCPiEvent.cxx", "k"); - gSystem->CompileMacro("WSidebandFitter.cxx", "k"); - gSystem->CompileMacro("CohDiffractiveSystematics.cxx", "k"); + //gSystem->CompileMacro("StackedHistogram.cxx", "k"); + //gSystem->CompileMacro("Histograms.cxx", "k"); + //gSystem->CompileMacro("Variable.cxx", "k"); + //gSystem->CompileMacro("HadronVariable.cxx", "k"); + //gSystem->CompileMacro("MacroUtil.cxx", "k"); + //gSystem->CompileMacro("CCPiEvent.cxx", "k"); + //gSystem->CompileMacro("WSidebandFitter.cxx", "k"); + //gSystem->CompileMacro("CohDiffractiveSystematics.cxx", "k"); } void loadLibs(bool verbose_cvu = true) { diff --git a/studies/runNewMichels.C b/studies/runNewMichels.C index 3038407..659499c 100644 --- a/studies/runNewMichels.C +++ b/studies/runNewMichels.C @@ -72,6 +72,8 @@ void LoopAndFill(const CCPi::MacroUtil& util, CVUniverse* universe, // For mc, get weight, check signal, and sideband CCPiEvent event(is_mc, is_truth, util.m_signal_definition, universe); + //universe->SetVtxMichels(trackless::GetQualityMichels(*universe)); + // PassesCuts sets these properties std::tie(event.m_passes_cuts, event.m_is_w_sideband, event.m_reco_pion_candidate_idxs) = PassesCuts(event); From 7b8e89713fff5666b508e732145482e53d066e8e Mon Sep 17 00:00:00 2001 From: Ben Messerly Date: Mon, 14 Nov 2022 23:20:55 -0600 Subject: [PATCH 16/17] Reverting somethings to head to clean up the PR. --- includes/CCPiEvent.cxx | 104 ++++++++++++++++++++++++----------------- includes/CCPiEvent.h | 18 +++---- includes/Constants.h | 16 +------ includes/CutUtils.h | 83 +++++++++----------------------- 4 files changed, 95 insertions(+), 126 deletions(-) diff --git a/includes/CCPiEvent.cxx b/includes/CCPiEvent.cxx index fcce52d..56aff29 100644 --- a/includes/CCPiEvent.cxx +++ b/includes/CCPiEvent.cxx @@ -3,8 +3,8 @@ #include "CCPiEvent.h" -#include "Cuts.h" // kCutsVector -#include "Michel.h" // class Michel, typdef MichelMap +#include "Cuts.h" // kCutsVector +#include "Michel.h" // class endpoint::Michel, typdef endpoint::MichelMap, endpoint::GetQualityMichels #include "common_functions.h" // GetVar, HasVar //============================================================================== @@ -18,37 +18,22 @@ CCPiEvent::CCPiEvent(const bool is_mc, const bool is_truth, m_signal_definition(signal_definition), m_universe(universe), m_reco_pion_candidate_idxs(), - m_highest_energy_pion_idx(-300) -// m_reco_pion_candidate_idxs_sideband() -{ + m_highest_energy_pion_idx(-300) { m_is_signal = is_mc ? IsSignal(*universe, signal_definition) : false; m_weight = is_mc ? universe->GetWeight() : 1.; m_w_type = is_mc ? GetWSidebandType(*universe, signal_definition, sidebands::kNWFitCategories) : kNWSidebandTypes; - m_endpoint_michels = endpoint::GetQualityMichels(*universe); - m_vertex_michels = vertex::GetQualityMichels(*universe); } //============================================================================== // Helper Functions //============================================================================== -std::tuple> PassesCuts(CCPiEvent& e) { +// return tuple {passes_all_cuts, is_w_sideband, pion_candidate_idxs} +std::tuple> PassesCuts(const CCPiEvent& e) { return PassesCuts(*e.m_universe, e.m_is_mc, e.m_signal_definition); } -// Used in analysis pipeline -bool PassesCuts(CCPiEvent& e, bool& is_w_sideband) { - return PassesCuts(*e.m_universe, e.m_reco_pion_candidate_idxs, e.m_is_mc, - e.m_signal_definition, is_w_sideband); -} - -// Only used for studies -- not used in analysis pipeline -bool PassesCuts(CCPiEvent& e, std::vector cuts) { - return PassesCuts(*e.m_universe, e.m_reco_pion_candidate_idxs, e.m_is_mc, - e.m_signal_definition, cuts); -} - SignalBackgroundType GetSignalBackgroundType(const CCPiEvent& e) { return GetSignalBackgroundType(*e.m_universe, e.m_signal_definition); } @@ -108,28 +93,6 @@ void ccpi_event::FillRecoEvent(const CCPiEvent& event, FillMigration(event, variables, std::string("pimuAngle")); if (HasVar(variables, "PT") && HasVar(variables, "PT_true")) FillMigration(event, variables, std::string("PT")); - if (HasVar(variables, "thetaZ") && HasVar(variables, "thetaZ_true")) - FillMigration(event, variables, std::string("thetaZ")); - if (HasVar(variables, "Pxpi") && HasVar(variables, "Pxpi_true")) - FillMigration(event, variables, std::string("Pxpi")); - if (HasVar(variables, "Pypi") && HasVar(variables, "Pypi_true")) - FillMigration(event, variables, std::string("Pypi")); - if (HasVar(variables, "Pzpi") && HasVar(variables, "Pzpi_true")) - FillMigration(event, variables, std::string("Pzpi")); - if (HasVar(variables, "PxMu") && HasVar(variables, "PxMu_true")) - FillMigration(event, variables, std::string("PxMu")); - if (HasVar(variables, "PyMu") && HasVar(variables, "PyMu_true")) - FillMigration(event, variables, std::string("PyMu")); - if (HasVar(variables, "PzMu") && HasVar(variables, "PzMu_true")) - FillMigration(event, variables, std::string("PzMu")); - if (HasVar(variables, "PxNu") && HasVar(variables, "PxNu_true")) - FillMigration(event, variables, std::string("PxNu")); - if (HasVar(variables, "PyNu") && HasVar(variables, "PyNu_true")) - FillMigration(event, variables, std::string("PyNu")); - if (HasVar(variables, "PzNu") && HasVar(variables, "PzNu_true")) - FillMigration(event, variables, std::string("PzNu")); - if (HasVar(variables, "Ppi") && HasVar(variables, "Ppi_true")) - FillMigration(event, variables, std::string("Ppi")); } } @@ -327,6 +290,8 @@ void ccpi_event::FillWSideband_Study(CCPiEvent& event, } } +// Like FillCutVars, this function loops through cuts and calls PassesCut. +// Michel containers updated as we go, but thrown away at the end. void ccpi_event::FillCounters( const CCPiEvent& event, const std::pair& counters) { @@ -354,6 +319,38 @@ void ccpi_event::FillCounters( } // cuts } +std::pair ccpi_event::FillCounters( + const CCPiEvent& event, const EventCount& s, const EventCount& b) { + EventCount signal = s; + EventCount bg = b; + + endpoint::MichelMap endpoint_michels; + trackless::MichelEvent vtx_michels; + bool pass = true; + for (auto i_cut : kCutsVector) { + if (event.m_is_truth != IsPrecut(i_cut)) continue; + + bool passes_this_cut = true; + std::tie(passes_this_cut, endpoint_michels, vtx_michels) = + PassesCut(*event.m_universe, i_cut, event.m_is_mc, + event.m_signal_definition, endpoint_michels, vtx_michels); + + pass = pass && passes_this_cut; + + if (!pass) continue; + + if (!event.m_is_mc) { + signal[i_cut] += event.m_weight; // selected data + } else { + if (event.m_is_signal) + signal[i_cut] += event.m_weight; // selected mc signal + else + bg[i_cut] += event.m_weight; // selected mc bg + } + } // cuts loop + return {signal, bg}; +} + void ccpi_event::FillCutVars(CCPiEvent& event, const std::vector& variables) { const CVUniverse* universe = event.m_universe; @@ -489,7 +486,7 @@ void ccpi_event::FillCutVars(CCPiEvent& event, if (HasVar(variables, "adphi")) FillStackedHists(event, GetVar(variables, "adphi")); if (HasVar(variables, "pimuAngle")) - FillStackedHists(event, GetVar(variables, "pimuAngle")); + FillStackedHists(event, GetVar(variables, "pimuAngle")); if (HasVar(variables, "PT")) FillStackedHists(event, GetVar(variables, "PT")); } @@ -556,4 +553,25 @@ void ccpi_event::FillStackedHists(const CCPiEvent& event, Variable* v, ->Fill(fill_val, event.m_weight); } +//============================================================================== +// BEING DEPRECATED +//============================================================================== + +// Used in analysis pipeline +// Uses PassesCuts v2. Does check w sideband, but fills by reference instead of +// returning its results. v3 is the future. +bool PassesCuts(CCPiEvent& e, bool& is_w_sideband) { + return PassesCuts(*e.m_universe, e.m_reco_pion_candidate_idxs, e.m_is_mc, + e.m_signal_definition, is_w_sideband); +} + +// Uses PassesCuts v1. +// No longer used anywhere. Doesn't check w sideband while looping all cuts. +// Nothing wrong with it per se. Checking the w sideband is just practically +// free. v3 of PassesCuts is the future, anyways. +bool PassesCuts(CCPiEvent& e, std::vector cuts) { + return PassesCuts(*e.m_universe, e.m_reco_pion_candidate_idxs, e.m_is_mc, + e.m_signal_definition, cuts); +} + #endif // CCPiEvent_cxx diff --git a/includes/CCPiEvent.h b/includes/CCPiEvent.h index 190cf41..178239c 100644 --- a/includes/CCPiEvent.h +++ b/includes/CCPiEvent.h @@ -3,7 +3,6 @@ #include "CVUniverse.h" #include "Constants.h" // typedef RecoPionIdx, EventCount -#include "Michel.h" #include "SignalDefinition.h" #include "TruthCategories/Sidebands.h" // WSidebandType #include "TruthCategories/SignalBackground.h" // SignalBackgroundType @@ -43,21 +42,15 @@ struct CCPiEvent { double m_weight; WSidebandType m_w_type; - endpoint::MichelMap m_endpoint_michels; - vertex::MichelEvent m_vertex_michels; - // Fixed (directly) outside of constructor -- with time-intensive functions bool m_passes_cuts; // PassesCuts bool m_is_w_sideband; // IsWSideband RecoPionIdx m_highest_energy_pion_idx; // GetHighestEnergyPionCandidateIndex - }; // Helper Functions // bool IsWSideband(CCPiEvent&); -bool PassesCuts(CCPiEvent&, bool& is_w_sideband); -bool PassesCuts(CCPiEvent&, std::vector); -std::tuple> PassesCuts(CCPiEvent&); +std::tuple> PassesCuts(const CCPiEvent&); RecoPionIdx GetHighestEnergyPionCandidateIndex(const CCPiEvent&); SignalBackgroundType GetSignalBackgroundType(const CCPiEvent&); @@ -76,6 +69,9 @@ void FillMigration(const CCPiEvent&, const std::vector&, void FillWSideband_Study(CCPiEvent&, std::vector); void FillCounters(const CCPiEvent&, const std::pair& counters); +std::pair FillCounters(const CCPiEvent&, + const EventCount& signal, + const EventCount& bg); void FillCutVars(CCPiEvent&, const std::vector&); void FillStackedHists(const CCPiEvent&, const std::vector&); // all variables @@ -83,4 +79,10 @@ void FillStackedHists(const CCPiEvent&, Variable*, const double fill_value = -999.); // Single variable } // namespace ccpi_event +//============================================================================== +// BEING DEPRECATED +//============================================================================== +bool PassesCuts(CCPiEvent&, bool& is_w_sideband); +bool PassesCuts(CCPiEvent&, std::vector cuts); + #endif // CCPiEvent diff --git a/includes/Constants.h b/includes/Constants.h index f332336..2c28a1a 100644 --- a/includes/Constants.h +++ b/includes/Constants.h @@ -21,10 +21,7 @@ enum ECuts { kVtx, kMinosMatch, kMinosCharge, - kMinosCoil, kMinosMuon, - kThetaMu, - kDeadTime, kNProngs, kWexp, kNPionCandidates, @@ -32,18 +29,8 @@ enum ECuts { kAtLeastOnePionCandidateTrack, kNode, kPionMult, - kAtLeastOneAnchoredProng, - kNoInteractionVertexMichels, kAtLeastOneMichel, - kAtLeastOneBrandonMichel, - kExactlyOneEndpointMichel, - kOldMichel, - kdEdx, - kAtLeastOneLLRCandidate, - kAtLeastOneNodeCandidate, kLLR, - kIsoBlobs, - kIsoProngSep, kIsoProngs, kPmu, kAtLeastOnePionCandidate, @@ -100,7 +87,8 @@ const int kNFluxUniverses = 100; //============================================================================== namespace CCNuPionIncShifts { -const std::string kLowQ2PiChannel("NU1PI"); +const std::string kLowQ2PiChannel("MENU1PI"); // MENU1PI is the weight + // for GENIE v4.3 const double muon_angle_res = 0.002; // radians const double pion_angle_res = 5.0 * muon_angle_res; // radians diff --git a/includes/CutUtils.h b/includes/CutUtils.h index 7bd6787..c936ef4 100644 --- a/includes/CutUtils.h +++ b/includes/CutUtils.h @@ -1,25 +1,26 @@ #ifndef CutUtils_h #define CutUtils_h -#include "Constants.h" // enum ECuts, CCNuPionIncConsts -#include "Michel.h" // endpoint::MichelMap +#include "Constants.h" // enum ECuts, CCNuPionIncConsts +#include "Michel.h" // endpoint::MichelMap +#include "MichelEvent.h" // trackless::MichelEvent +// At the moment, endpoint::MichelMap and trackless::MichelEvent accomplish the +// same thing for their respective namespaces, viz hold a bunch of info about +// the michel. May merge them in the future. // Analysis Cuts - default vector -const std::vector kCutsVector = { - kNoCuts, - kPrecuts, - kVtx, - kMinosMuon, - kPmu, - kAtLeastOneMichel, - kAtLeastOnePionCandidate, - kLLR, - kNode, - kTrackQuality, - kWexp, - kIsoProngs, - kPionMult -}; +const std::vector kCutsVector = {kNoCuts, + kPrecuts, + kVtx, + kMinosMuon, + kAtLeastOnePionCandidateTrack, + kAtLeastOneMichel, + kLLR, + kNode, + kWexp, + kIsoProngs, + kPionMult, + kPmu}; // Remove W cut from cuts vector const std::vector GetWSidebandCuts() { @@ -69,72 +70,30 @@ std::string GetCutName(ECuts cut) { case kMinosCharge: return "MINOS Charge"; - case kMinosCoil: - return "MINOS Coil"; - case kMinosMuon: return "MINOS Muon"; - case kThetaMu: - return "Muon Angle"; - - case kDeadTime: - return "Dead Time"; - case kWexp: return "$W_{experimental}$"; - case kIsoBlobs: - return "$<$1 Isolated Blobs"; - case kIsoProngs: return "$<$2 Isolated Prongs"; - case kIsoProngSep: - return "Iso Prong Sep $<$ 300"; - - case kNProngs: - return "Max 2 Hadr Prongs"; - case kNPionCandidates: return "$\\pi$ candidate"; - case kPionCandidateQuality: - return "Quality $\\pi$ candidate"; - case kAtLeastOneMichel: return "$>$= 1 Michel"; - case kAtLeastOneBrandonMichel: - return "$>$= 1 Brandon Michel"; - - case kAtLeastOneAnchoredProng: - return "$>$= 1 Anchored Prong"; - case kAtLeastOnePionCandidateTrack: return "$>$= 1 Hadron Track"; - case kAtLeastOneLLRCandidate: - return "$>$= 1 Track Pass LLR Cut"; - - case kAtLeastOneNodeCandidate: - return "$>$= 1 Track Pass Node Cut"; - - case kExactlyOneEndpointMichel: - return "== 1 Michel"; - case kNode: return "Node"; case kPionMult: return "Pion Multiplicity"; - case kOldMichel: - return "Old Michel Cut"; - - case kdEdx: - return "dEdx PID"; - case kLLR: return "LLR PID"; @@ -158,7 +117,9 @@ std::string GetCutName(ECuts cut) { // Get pion candidate indexes from michel map // (our cuts strategy enforces a 1-1 michel-pion candidate match) -std::vector GetHadIdxsFromMichels(const endpoint::MichelMap endpoint_michels, const trackless::MichelEvent vtx_michels) { +std::vector GetHadIdxsFromMichels( + const endpoint::MichelMap endpoint_michels, + const trackless::MichelEvent vtx_michels = trackless::MichelEvent()) { std::vector ret; // endpoint michels @@ -168,7 +129,7 @@ std::vector GetHadIdxsFromMichels(const endpoint::MichelMap endpoint_michel // When m_idx is set (i.e. != -1), then we have a good vertex michel. // In that case, a -1 in this hadron index return vector is the code that for // this analysis that we have a good vertex michel. - if (vtx_michels.m_idx != -1) ret.push_back(-1); + if (vtx_michels.m_idx != -1) ret.push_back(-1); return ret; } From c27b8c042fb5fb0d56aaed67a10137f7146f1822 Mon Sep 17 00:00:00 2001 From: Ben Messerly Date: Mon, 14 Nov 2022 23:26:38 -0600 Subject: [PATCH 17/17] Reverting somethings to head to clean up the PR. --- includes/SignalDefinition.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/includes/SignalDefinition.h b/includes/SignalDefinition.h index 40b0518..40bba99 100644 --- a/includes/SignalDefinition.h +++ b/includes/SignalDefinition.h @@ -85,7 +85,7 @@ bool IsSignal(const CVUniverse& universe, SignalDefinition signal_definition = k && universe.GetBool("truth_is_fiducial") && VtxSignal(universe) && universe.GetInt("mc_incoming") == 14 - && universe.GetThetalepTrue() < 0.3491 // 20 deg + && universe.GetDouble("truth_muon_theta") < 0.3491 // 20 deg && universe.GetWexpTrue() > 0 && universe.GetWexpTrue() < GetWCutValue(signal_definition) && n_signal_pions > 0