|
| 1 | +/* |
| 2 | +.L $O2DPG/UTILS/Parsers/treeFastCache.C |
| 3 | +*/ |
| 4 | + |
| 5 | +/* |
| 6 | + treeFastCache.C |
| 7 | + Simple caching system for fast lookup of 1D values from a TTree, using nearest-neighbor interpolation. |
| 8 | + This utility allows registration of (X, Y) pairs from a TTree into a std::map, |
| 9 | + indexed by a user-defined mapID or map name. The lookup function `getNearest1D(x, mapID)` |
| 10 | + retrieves the Y value for the X closest to the query. |
| 11 | + Features: |
| 12 | + - Register maps via string name or numeric ID |
| 13 | + - Query nearest-neighbor value for any X |
| 14 | + - Graceful error handling and range checking |
| 15 | + - Base for future ND extension |
| 16 | +*/ |
| 17 | + |
| 18 | +#include <TTree.h> |
| 19 | +#include <TTreeFormula.h> |
| 20 | +#include <map> |
| 21 | +#include <string> |
| 22 | +#include <cmath> |
| 23 | +#include <iostream> |
| 24 | +#include <functional> |
| 25 | + |
| 26 | +using namespace std; |
| 27 | + |
| 28 | +// Map: mapID -> map<X, Y> |
| 29 | +std::map<int, std::map<double, float>> registeredMaps; |
| 30 | +std::map<std::string, int> nameToMapID; |
| 31 | + |
| 32 | +/// Hash a string to create a deterministic mapID |
| 33 | +int hashMapName(const std::string& name) { |
| 34 | + std::hash<std::string> hasher; |
| 35 | + return static_cast<int>(hasher(name)); |
| 36 | +} |
| 37 | + |
| 38 | +/// Register a 1D lookup map from TTree (X -> Y) |
| 39 | +/// @param valX Name of the X-axis variable (lookup key) |
| 40 | +/// @param valY Name of the Y-axis variable (value to retrieve) |
| 41 | +/// @param tree Pointer to TTree to extract data from |
| 42 | +/// @param selection Selection string (TTree::Draw-compatible) |
| 43 | +/// @param mapID Integer ID to associate with this map |
| 44 | +void registerMap1D(const std::string& valX, const std::string& valY, TTree* tree, const std::string& selection, int mapID) { |
| 45 | + if (!tree) { |
| 46 | + std::cerr << "[registerMap1D] Null TTree pointer." << std::endl; |
| 47 | + return; |
| 48 | + } |
| 49 | + |
| 50 | + int entries = tree->Draw((valY + ":" + valX).c_str(), selection.c_str(), "goff"); |
| 51 | + if (entries <= 0) { |
| 52 | + std::cerr << "[registerMap1D] No entries matched for mapID=" << mapID << std::endl; |
| 53 | + return; |
| 54 | + } |
| 55 | + |
| 56 | + if (!tree->GetV1() || !tree->GetV2()) { |
| 57 | + std::cerr << "[registerMap1D] Internal Draw buffer pointers are null." << std::endl; |
| 58 | + return; |
| 59 | + } |
| 60 | + |
| 61 | + std::map<double, float> newMap; |
| 62 | + for (int i = 0; i < entries; ++i) { |
| 63 | + if (i >= tree->GetSelectedRows()) { |
| 64 | + std::cerr << "[registerMap1D] Index out of range at i=" << i << std::endl; |
| 65 | + break; |
| 66 | + } |
| 67 | + double x = tree->GetV2()[i]; // valX |
| 68 | + float y = tree->GetV1()[i]; // valY |
| 69 | + newMap[x] = y; |
| 70 | + } |
| 71 | + |
| 72 | + registeredMaps[mapID] = std::move(newMap); |
| 73 | + std::cout << "[registerMap1D] Registered map " << mapID << " with " << entries << " entries." << std::endl; |
| 74 | +} |
| 75 | + |
| 76 | +/// Register by name; returns mapID computed from name |
| 77 | +int registerMap1DByName(const std::string& mapName, const std::string& valX, const std::string& valY, TTree* tree, const std::string& selection) { |
| 78 | + int mapID = hashMapName(mapName); |
| 79 | + nameToMapID[mapName] = mapID; |
| 80 | + registerMap1D(valX, valY, tree, selection, mapID); |
| 81 | + return mapID; |
| 82 | +} |
| 83 | + |
| 84 | +/// Get the nearest Y for a given X from the map registered with mapID |
| 85 | +/// @param x Query value along X axis |
| 86 | +/// @param mapID Map identifier used in registration |
| 87 | +/// @return Y value corresponding to nearest X in the map |
| 88 | +float getNearest1D(float x, int mapID) { |
| 89 | + const auto itMap = registeredMaps.find(mapID); |
| 90 | + if (itMap == registeredMaps.end()) { |
| 91 | + std::cerr << "[getNearest1D] Map ID " << mapID << " not found." << std::endl; |
| 92 | + return NAN; |
| 93 | + } |
| 94 | + |
| 95 | + const auto& map = itMap->second; |
| 96 | + if (map.empty()) { |
| 97 | + std::cerr << "[getNearest1D] Map ID " << mapID << " is empty." << std::endl; |
| 98 | + return NAN; |
| 99 | + } |
| 100 | + |
| 101 | + auto it = map.lower_bound(x); |
| 102 | + if (it == map.begin()) return it->second; |
| 103 | + if (it == map.end()) return std::prev(it)->second; |
| 104 | + |
| 105 | + auto prev = std::prev(it); |
| 106 | + return (std::abs(prev->first - x) < std::abs(it->first - x)) ? prev->second : it->second; |
| 107 | +} |
| 108 | + |
| 109 | +/// Convenience version: lookup by name |
| 110 | +float getNearest1DByName(float x, const std::string& mapName) { |
| 111 | + auto it = nameToMapID.find(mapName); |
| 112 | + if (it == nameToMapID.end()) { |
| 113 | + std::cerr << "[getNearest1DByName] Map name \"" << mapName << "\" not found." << std::endl; |
| 114 | + return NAN; |
| 115 | + } |
| 116 | + return getNearest1D(x, it->second); |
| 117 | +} |
| 118 | + |
| 119 | +/// Example usage |
| 120 | +void example1D() { |
| 121 | + TFile *f = TFile::Open("timeSeries10000_apass5.root"); |
| 122 | + TTree * tree0=(TTree*)f->Get("timeSeries"); |
| 123 | + // Fill tree here or load from file |
| 124 | + int mapID = registerMap1DByName("dcar_vs_time", "time", "mTSITSTPC.mDCAr_A_NTracks_median", tree0, "subentry==127"); |
| 125 | + tree0->SetAlias("mDCAr_A_NTracks_median_All" ,("getNearest1D(time, " + std::to_string(mapID) + ")").data()); |
| 126 | + tree0->Draw("mTSITSTPC.mDCAr_A_NTracks_median:mDCAr_A_NTracks_median_All","indexType==1","",10000); |
| 127 | +} |
0 commit comments