Skip to content

Commit 3b68ca4

Browse files
committed
Add TOF misaligner
1 parent 3347f8b commit 3b68ca4

2 files changed

Lines changed: 263 additions & 0 deletions

File tree

Detectors/Upgrades/ALICE3/IOTOF/macros/CMakeLists.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,3 +28,6 @@ o2_add_test_root_macro(CheckDigitsIOTOF.C
2828

2929
o2_add_test_root_macro(CheckClustersIOTOF.C
3030
LABELS iotof COMPILE_ONLY)
31+
32+
o2_add_test_root_macro(IOTOFMisaligner.C
33+
LABELS alice3)
Lines changed: 260 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,260 @@
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(&params, "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

Comments
 (0)