|
| 1 | +#if !defined(__CLING__) || defined(__ROOTCLING__) |
| 2 | +// #define ENABLE_UPGRADES |
| 3 | +#include "DetectorsBase/GeometryManager.h" |
| 4 | +#include <IOTOFBase/IOTOFBaseParam.h> |
| 5 | +#include "DetectorsCommonDataFormats/AlignParam.h" |
| 6 | +#include "DetectorsCommonDataFormats/DetectorNameConf.h" |
| 7 | +#include "DetectorsCommonDataFormats/DetID.h" |
| 8 | +#include "IOTOFBase/GeometryTGeo.h" |
| 9 | +#include <TCanvas.h> |
| 10 | +#include <TFile.h> |
| 11 | +#include <TGeoBBox.h> |
| 12 | +#include <TGeoVolume.h> |
| 13 | +#include <TH2F.h> |
| 14 | +#include <TObjArray.h> |
| 15 | +#include <TROOT.h> |
| 16 | +#include <TRandom.h> |
| 17 | +#include <algorithm> |
| 18 | +#include <array> |
| 19 | +#include <cmath> |
| 20 | +#include <cstring> |
| 21 | +#include <fmt/format.h> |
| 22 | +#include <string> |
| 23 | +#include <vector> |
| 24 | +#endif |
| 25 | + |
| 26 | +// Inspired from Detectors/ITSMFT/ITS/macros/test/ITSMisaligner.C |
| 27 | + |
| 28 | +using AlgPar = std::array<double, 6>; |
| 29 | +using PreviewPoint = std::array<double, 5>; // lay, sta, mod, chip, deltaY |
| 30 | + |
| 31 | +AlgPar generateMisalignment(double x, double y, double z, double psi, double theta, double phi); |
| 32 | +double getVolumeHalfLengthZ(const char* volumeName); |
| 33 | +int countNodesMatching(const char* volumeName, const char* pattern); |
| 34 | +int getNumberOfStaves(int lay); |
| 35 | +double getChipLocalZ(int lay, int mod, int chip); |
| 36 | +double getChipPhi(int lay, int sta); |
| 37 | +double computeSagitta(int lay, int sta, int mod, int chip, double sagA); |
| 38 | +void drawMisalignmentPreview(const std::vector<PreviewPoint>& previewPoints, |
| 39 | + double sagA); |
| 40 | + |
| 41 | +void IOTOFMisaligner(long tmin = 0, long tmax = -1, |
| 42 | + double xEnv = 0., double yEnv = 0., double zEnv = 0., double psiEnv = 0., double thetaEnv = 0., double phiEnv = 0., |
| 43 | + double xHBa = 0., double yHBa = 0., double zHBa = 0., double psiHBa = 0., double thetaHBa = 0., double phiHBa = 0., |
| 44 | + double xSta = 0., double ySta = 0., double zSta = 0., double psiSta = 0., double thetaSta = 0., double phiSta = 0., |
| 45 | + double xHSt = 0., double yHSt = 0., double zHSt = 0., double psiHSt = 0., double thetaHSt = 0., double phiHSt = 0., |
| 46 | + double xMod = 0., double yMod = 0., double zMod = 0., double psiMod = 0., double thetaMod = 0., double phiMod = 0., |
| 47 | + double xChp = 0., double yChp = 0., double zChp = 0., double psiChp = 0., double thetaChp = 0., double phiChp = 0., |
| 48 | + double sagA = 1000., |
| 49 | + const std::string& objectPath = "", |
| 50 | + const std::string& fileName = "IOTOFAlignment.root") |
| 51 | +{ |
| 52 | + std::vector<o2::detectors::AlignParam> params; |
| 53 | + std::vector<PreviewPoint> previewPoints; |
| 54 | + o2::base::GeometryManager::loadGeometry("", false); |
| 55 | + o2::conf::ConfigurableParam::setValue<bool>("IOTOFBase", "segmentedInnerTOF", true); |
| 56 | + o2::conf::ConfigurableParam::setValue<bool>("IOTOFBase", "segmentedOuterTOF", true); |
| 57 | + auto geom = o2::iotof::GeometryTGeo::Instance(); |
| 58 | + if (!geom || geom->getSize() <= 0) { |
| 59 | + LOG(fatal) << "Failed to load IOTOF geometry: size is " << (geom ? geom->getSize() : -1) << ". Is the geometry file correct and IOTOF segmented?"; |
| 60 | + return; |
| 61 | + } |
| 62 | + std::string symname; |
| 63 | + AlgPar pars; |
| 64 | + bool glo = true; |
| 65 | + o2::detectors::DetID detIOTOF("TF3"); |
| 66 | + |
| 67 | + (void)tmin; |
| 68 | + (void)tmax; |
| 69 | + (void)objectPath; |
| 70 | + |
| 71 | + for (int ich = 0; ich < geom->getSize(); ich++) { |
| 72 | + LOGP(info, "Processing IOTOF chip index %d/%d", ich, geom->getSize()); |
| 73 | + int lay = -1; |
| 74 | + int sta = -1; |
| 75 | + int mod = -1; |
| 76 | + int chip = -1; |
| 77 | + geom->getIOTOFChipId(ich, lay, sta, mod, chip); |
| 78 | + const auto* chipSymbolicName = o2::base::GeometryManager::getSymbolicName(detIOTOF, ich); |
| 79 | + if (chipSymbolicName == nullptr) { |
| 80 | + LOG(error) << "Failed to find IOTOF symbolic name for chip index " << ich; |
| 81 | + continue; |
| 82 | + } |
| 83 | + |
| 84 | + symname = chipSymbolicName; |
| 85 | + pars = generateMisalignment(xChp, yChp, zChp, psiChp, thetaChp, phiChp); |
| 86 | + pars[1] += computeSagitta(lay, sta, mod, chip, sagA); |
| 87 | + int chID = o2::base::GeometryManager::getSensID(detIOTOF, ich); |
| 88 | + params.emplace_back(symname.c_str(), chID, pars[0], pars[1], pars[2], pars[3], pars[4], pars[5], glo); |
| 89 | + previewPoints.push_back({static_cast<double>(lay), static_cast<double>(sta), static_cast<double>(mod), static_cast<double>(chip), pars[1]}); |
| 90 | + } |
| 91 | + |
| 92 | + drawMisalignmentPreview(previewPoints, sagA); |
| 93 | + |
| 94 | + if (!fileName.empty()) { |
| 95 | + LOGP(info, "Storing IOTOF alignment in local file {}", fileName); |
| 96 | + TFile algFile(fileName.c_str(), "recreate"); |
| 97 | + algFile.WriteObjectAny(¶ms, "std::vector<o2::detectors::AlignParam>", "alignment"); |
| 98 | + algFile.Close(); |
| 99 | + } |
| 100 | +} |
| 101 | + |
| 102 | +AlgPar generateMisalignment(double x, double y, double z, double psi, double theta, double phi) |
| 103 | +{ |
| 104 | + AlgPar pars; |
| 105 | + pars[0] = gRandom->Gaus(0, x); |
| 106 | + pars[1] = gRandom->Gaus(0, y); |
| 107 | + pars[2] = gRandom->Gaus(0, z); |
| 108 | + pars[3] = gRandom->Gaus(0, psi); |
| 109 | + pars[4] = gRandom->Gaus(0, theta); |
| 110 | + pars[5] = gRandom->Gaus(0, phi); |
| 111 | + return std::move(pars); |
| 112 | +} |
| 113 | + |
| 114 | +double getVolumeHalfLengthZ(const char* volumeName) |
| 115 | +{ |
| 116 | + auto* volume = gGeoManager->GetVolume(volumeName); |
| 117 | + if (volume == nullptr) { |
| 118 | + LOG(fatal) << "Can't find volume " << volumeName; |
| 119 | + return 0.; |
| 120 | + } |
| 121 | + auto* box = dynamic_cast<TGeoBBox*>(volume->GetShape()); |
| 122 | + if (box == nullptr) { |
| 123 | + LOG(fatal) << "Volume " << volumeName << " is not a box"; |
| 124 | + return 0.; |
| 125 | + } |
| 126 | + return box->GetDZ(); |
| 127 | +} |
| 128 | + |
| 129 | +int countNodesMatching(const char* volumeName, const char* pattern) |
| 130 | +{ |
| 131 | + auto* volume = gGeoManager->GetVolume(volumeName); |
| 132 | + if (volume == nullptr) { |
| 133 | + LOG(fatal) << "Can't find volume " << volumeName; |
| 134 | + return 0; |
| 135 | + } |
| 136 | + |
| 137 | + TObjArray* nodes = volume->GetNodes(); |
| 138 | + int count = 0; |
| 139 | + for (int i = 0; i < nodes->GetEntriesFast(); ++i) { |
| 140 | + if (strstr(nodes->At(i)->GetName(), pattern) != nullptr) { |
| 141 | + ++count; |
| 142 | + } |
| 143 | + } |
| 144 | + return count; |
| 145 | +} |
| 146 | + |
| 147 | +int getNumberOfStaves(int lay) |
| 148 | +{ |
| 149 | + const char* layerName = lay == 0 ? o2::iotof::GeometryTGeo::getITOFLayerPattern() : o2::iotof::GeometryTGeo::getOTOFLayerPattern(); |
| 150 | + const char* staveName = lay == 0 ? o2::iotof::GeometryTGeo::getITOFStavePattern() : o2::iotof::GeometryTGeo::getOTOFStavePattern(); |
| 151 | + return countNodesMatching(layerName, staveName); |
| 152 | +} |
| 153 | + |
| 154 | +double getChipLocalZ(int lay, int mod, int chip) |
| 155 | +{ |
| 156 | + const char* staveName = lay == 0 ? o2::iotof::GeometryTGeo::getITOFStavePattern() : o2::iotof::GeometryTGeo::getOTOFStavePattern(); |
| 157 | + const char* moduleName = lay == 0 ? o2::iotof::GeometryTGeo::getITOFModulePattern() : o2::iotof::GeometryTGeo::getOTOFModulePattern(); |
| 158 | + const char* chipName = lay == 0 ? o2::iotof::GeometryTGeo::getITOFChipPattern() : o2::iotof::GeometryTGeo::getOTOFChipPattern(); |
| 159 | + |
| 160 | + constexpr int kChipsPerModuleZ = 2; |
| 161 | + |
| 162 | + const double staveSizeZ = 2. * getVolumeHalfLengthZ(staveName); |
| 163 | + const double moduleSizeZ = 2. * getVolumeHalfLengthZ(moduleName); |
| 164 | + const double chipSizeZ = 2. * getVolumeHalfLengthZ(chipName); |
| 165 | + const int chipZIndex = chip % kChipsPerModuleZ; |
| 166 | + return ((mod + 0.5) * moduleSizeZ - 0.5 * staveSizeZ) + ((chipZIndex + 0.5) * chipSizeZ - 0.5 * moduleSizeZ); |
| 167 | +} |
| 168 | + |
| 169 | +double getChipPhi(int lay, int sta) |
| 170 | +{ |
| 171 | + constexpr double kTwoPi = 2.0 * 3.141592653589793238462643383279502884; |
| 172 | + const int nStaves = getNumberOfStaves(lay); |
| 173 | + if (nStaves <= 0) { |
| 174 | + return 0.; |
| 175 | + } |
| 176 | + return kTwoPi * static_cast<double>(sta) / static_cast<double>(nStaves); |
| 177 | +} |
| 178 | + |
| 179 | +double computeSagitta(int lay, int sta, int mod, int chip, double sagA) |
| 180 | +{ |
| 181 | + if (sagA <= 0.) { |
| 182 | + return 0.; |
| 183 | + } |
| 184 | + |
| 185 | + const int nStaves = getNumberOfStaves(lay); |
| 186 | + if (nStaves <= 0) { |
| 187 | + return 0.; |
| 188 | + } |
| 189 | + |
| 190 | + const double z = getChipLocalZ(lay, mod, chip); |
| 191 | + const double phi = getChipPhi(lay, sta); |
| 192 | + |
| 193 | + // The local y direction is normal to the stave plane; sign follows the chosen phi convention. |
| 194 | + return -sagA * (std::cosh(z / sagA) - 1.0) * std::sin(phi); |
| 195 | +} |
| 196 | + |
| 197 | +void drawMisalignmentPreview(const std::vector<PreviewPoint>& previewPoints, |
| 198 | + double sagA) |
| 199 | +{ |
| 200 | + if (previewPoints.empty()) { |
| 201 | + Printf("No preview points to display for IOTOF misalignment preview"); |
| 202 | + return; |
| 203 | + } |
| 204 | + |
| 205 | + double zMin[2] = {1.e9, 1.e9}; |
| 206 | + double zMax[2] = {-1.e9, -1.e9}; |
| 207 | + |
| 208 | + for (const auto& p : previewPoints) { |
| 209 | + const int lay = static_cast<int>(p[0]); |
| 210 | + const int mod = static_cast<int>(p[2]); |
| 211 | + const int chip = static_cast<int>(p[3]); |
| 212 | + if (lay < 0 || lay > 1) { |
| 213 | + continue; |
| 214 | + } |
| 215 | + const double z = getChipLocalZ(lay, mod, chip); |
| 216 | + zMin[lay] = std::min(zMin[lay], z); |
| 217 | + zMax[lay] = std::max(zMax[lay], z); |
| 218 | + } |
| 219 | + |
| 220 | + static int sCanvasCounter = 0; |
| 221 | + const auto canvasName = fmt::format("cIOTOFMisalignPreview_{}", sCanvasCounter++); |
| 222 | + auto* canvas = new TCanvas(canvasName.c_str(), "IOTOF misalignment preview", 1300, 600); |
| 223 | + canvas->Divide(2, 1); |
| 224 | + |
| 225 | + const char* layerLabel[2] = {"ITOF", "OTOF"}; |
| 226 | + TH2F* hMap[2] = {nullptr, nullptr}; |
| 227 | + |
| 228 | + for (int lay = 0; lay < 2; ++lay) { |
| 229 | + const std::string histName = fmt::format("hIOTOFdYMap_l{}_{}", lay, sCanvasCounter); |
| 230 | + const std::string histTitle = Form("%s local #Deltay map (sagA=%f);z_{local};#varphi [rad]", layerLabel[lay], sagA); |
| 231 | + const double zLow = zMin[lay] < zMax[lay] ? zMin[lay] : -1.; |
| 232 | + const double zHigh = zMin[lay] < zMax[lay] ? zMax[lay] : 1.; |
| 233 | + hMap[lay] = new TH2F(histName.c_str(), histTitle.c_str(), 120, zLow, zHigh, 120, -3.1416, 3.1416); |
| 234 | + } |
| 235 | + |
| 236 | + for (const auto& p : previewPoints) { |
| 237 | + const int lay = static_cast<int>(p[0]); |
| 238 | + const int sta = static_cast<int>(p[1]); |
| 239 | + const int mod = static_cast<int>(p[2]); |
| 240 | + const int chip = static_cast<int>(p[3]); |
| 241 | + const double deltaY = p[4]; |
| 242 | + if (lay < 0 || lay > 1 || hMap[lay] == nullptr) { |
| 243 | + continue; |
| 244 | + } |
| 245 | + const double z = getChipLocalZ(lay, mod, chip); |
| 246 | + const double phi = getChipPhi(lay, sta); |
| 247 | + hMap[lay]->Fill(z, phi, deltaY); |
| 248 | + } |
| 249 | + |
| 250 | + for (int lay = 0; lay < 2; ++lay) { |
| 251 | + canvas->cd(lay + 1); |
| 252 | + if (hMap[lay] != nullptr) { |
| 253 | + hMap[lay]->SetStats(false); |
| 254 | + hMap[lay]->Draw("COLZ"); |
| 255 | + } |
| 256 | + } |
| 257 | + |
| 258 | + canvas->Update(); |
| 259 | + LOG(info) << "Displayed IOTOF misalignment preview canvas before writing alignment file"; |
| 260 | +} |
0 commit comments