Project
Loading...
Searching...
No Matches
AlignmentHierarchy.h
Go to the documentation of this file.
1// Copyright 2019-2026 CERN and copyright holders of ALICE O2.
2// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders.
3// All rights not expressly granted are reserved.
4//
5// This software is distributed under the terms of the GNU General Public
6// License v3 (GPL Version 3), copied verbatim in the file "COPYING".
7//
8// In applying this license CERN does not waive the privileges and immunities
9// granted to it by virtue of its status as an Intergovernmental Organization
10// or submit itself to any jurisdiction.
11
12#ifndef O2_ITS3_ALIGNMENT_HIERARCHY_H
13#define O2_ITS3_ALIGNMENT_HIERARCHY_H
14
15#include <memory>
16#include <compare>
17#include <type_traits>
18#include <map>
19#include <utility>
20#include <vector>
21#include <ostream>
22#include <string>
23#include <format>
24#include <algorithm>
25
26#include <Eigen/Dense>
27
28#include <TGeoMatrix.h>
29#include <TGeoPhysicalNode.h>
30
32{
33using Matrix36 = Eigen::Matrix<double, 3, 6>;
34using Matrix66 = Eigen::Matrix<double, 6, 6>;
35
36// indices for rigid body parameters in LOC frame
37enum RigidBodyDOF : uint8_t {
38 TX = 0,
45};
46static constexpr const char* RigidBodyDOFNames[RigidBodyDOF::NDOF] = {"TX", "TY", "TZ", "RX", "RY", "RZ"};
47
48// return the rigid body derivatives
49// trk has be at in the measurment frame
50auto getRigidBodyDerivatives(const auto& trk)
51{
52 // calculate slopes
53 const double tgl = trk.getTgl(), snp = trk.getSnp();
54 const double csp = 1. / sqrt(1. + (tgl * tgl));
55 const double u = trk.getY(), v = trk.getZ();
56 const double uP = snp * csp, vP = tgl * csp;
57 Matrix36 der;
58 der.setZero();
59 // columns: Tt, Tu, Tv, Rt, Ru, Rv
60 // (X) (Y) (Z) (RX) (RY) (RZ)
61 der << uP, -1., 0., v, v * uP, -u * uP,
62 vP, 0., -1., -u, v * vP, -u * vP;
63 return der;
64}
65
66class DOFSet
67{
68 public:
69 enum class Type : uint8_t { RigidBody,
70 Legendre };
71 virtual ~DOFSet() = default;
72 virtual Type type() const = 0;
73 int nDOFs() const { return static_cast<int>(mFree.size()); }
74 virtual std::string dofName(int idx) const = 0;
75 bool isFree(int idx) const { return mFree[idx]; }
76 void setFree(int idx, bool f) { mFree[idx] = f; }
77 void setAllFree(bool f) { std::fill(mFree.begin(), mFree.end(), f); }
78 int nFreeDOFs() const
79 {
80 int n = 0;
81 for (bool f : mFree) {
82 n += f;
83 }
84 return n;
85 }
86
87 protected:
88 DOFSet(int n) : mFree(n, true) {}
89 std::vector<bool> mFree;
90};
91
92class RigidBodyDOFSet final : public DOFSet
93{
94 public:
95 static constexpr int NDOF = RigidBodyDOF::NDOF;
97 // mask: bitmask of free DOFs (bit i = DOF i is free)
98 explicit RigidBodyDOFSet(uint8_t mask) : DOFSet(NDOF)
99 {
100 for (int i = 0; i < NDOF; ++i) {
101 mFree[i] = (mask >> i) & 1;
102 }
103 }
104 Type type() const override { return Type::RigidBody; }
105 std::string dofName(int idx) const override { return RigidBodyDOFNames[idx]; }
106 uint8_t mask() const
107 {
108 uint8_t m = 0;
109 for (int i = 0; i < NDOF; ++i) {
110 m |= (uint8_t(mFree[i]) << i);
111 }
112 return m;
113 }
114};
115
116class LegendreDOFSet final : public DOFSet
117{
118 public:
119 explicit LegendreDOFSet(int order) : DOFSet((order + 1) * (order + 2) / 2), mOrder(order) {}
120 Type type() const override { return Type::Legendre; }
121 int order() const { return mOrder; }
122 std::string dofName(int idx) const override
123 {
124 int i = 0;
125 while ((i + 1) * (i + 2) / 2 <= idx) {
126 ++i;
127 }
128 int j = idx - (i * (i + 1) / 2);
129 return std::format("L({},{})", i, j);
130 }
131
132 private:
133 int mOrder;
134};
135
137{
138 // Millepede label is any positive integer [1....)
139 // Layout: DOF(5) | CALIB(1) | ID(22) | SENS(1) | DET(2) = 31 usable bits (MSB reserved, GBL uses signed int)
140 public:
141 using T = uint32_t;
142 static constexpr int DOF_BITS = 5; // bits 0-4
143 static constexpr int CALIB_BITS = 1; // bit 5: 0 = rigid body, 1 = calibration
144 static constexpr int ID_BITS = 22; // bits 6-27
145 static constexpr int SENS_BITS = 1; // bit 28
146 static constexpr int TOTAL_BITS = sizeof(T) * 8;
147 static constexpr int DET_BITS = TOTAL_BITS - (DOF_BITS + CALIB_BITS + ID_BITS + SENS_BITS) - 1; // one less bit since GBL uses int!
148 static constexpr T bitMask(int b) noexcept
149 {
150 return (T(1) << b) - T(1);
151 }
152 static constexpr int DOF_SHIFT = 0;
153 static constexpr T DOF_MAX = (T(1) << DOF_BITS) - T(1);
154 static constexpr T DOF_MASK = DOF_MAX << DOF_SHIFT;
155 static constexpr int CALIB_SHIFT = DOF_BITS;
156 static constexpr T CALIB_MAX = (T(1) << CALIB_BITS) - T(1);
157 static constexpr T CALIB_MASK = CALIB_MAX << CALIB_SHIFT;
158 static constexpr int ID_SHIFT = DOF_BITS + CALIB_BITS;
159 static constexpr T ID_MAX = (T(1) << ID_BITS) - T(1);
160 static constexpr T ID_MASK = ID_MAX << ID_SHIFT;
161 static constexpr int SENS_SHIFT = DOF_BITS + CALIB_BITS + ID_BITS;
162 static constexpr T SENS_MAX = (T(1) << SENS_BITS) - T(1);
163 static constexpr T SENS_MASK = SENS_MAX << SENS_SHIFT;
164 static constexpr int DET_SHIFT = DOF_BITS + CALIB_BITS + ID_BITS + SENS_BITS;
165 static constexpr T DET_MAX = (T(1) << DET_BITS) - T(1);
166 static constexpr T DET_MASK = DET_MAX << DET_SHIFT;
167
168 GlobalLabel(T det, T id, bool sens, bool calib = false)
169 : mID((((id + 1) & ID_MAX) << ID_SHIFT) |
170 ((det & DET_MAX) << DET_SHIFT) |
171 ((T(sens) & SENS_MAX) << SENS_SHIFT) |
172 ((T(calib) & CALIB_MAX) << CALIB_SHIFT))
173 {
174 }
175
177 constexpr T raw(T dof) const noexcept { return (mID & ~DOF_MASK) | ((dof & DOF_MAX) << DOF_SHIFT); }
178 constexpr int rawGBL(T dof) const noexcept { return static_cast<int>(raw(dof)); }
179
181 GlobalLabel asCalib() const noexcept
182 {
183 GlobalLabel c{*this};
184 c.mID |= (T(1) << CALIB_SHIFT);
185 return c;
186 }
187
188 constexpr T id() const noexcept { return ((mID >> ID_SHIFT) & ID_MAX) - 1; }
189 constexpr T det() const noexcept { return (mID & DET_MASK) >> DET_SHIFT; }
190 constexpr bool sens() const noexcept { return (mID & SENS_MASK) >> SENS_SHIFT; }
191 constexpr bool calib() const noexcept { return (mID & CALIB_MASK) >> CALIB_SHIFT; }
192
193 std::string asString() const
194 {
195 return std::format("Det:{} Id:{} Sens:{} Calib:{}", det(), id(), sens(), calib());
196 }
197
198 constexpr auto operator<=>(const GlobalLabel&) const noexcept = default;
199
200 private:
201 T mID{0};
202};
203
205{
206 public:
207 HierarchyConstraint(std::string name, double value) : mName(std::move(name)), mValue(value) {}
208 void add(uint32_t lab, double coeff)
209 {
210 mLabels.push_back(lab);
211 mCoeff.push_back(coeff);
212 }
213 void write(std::ostream& os) const;
214 auto getSize() const noexcept { return mLabels.size(); }
215
216 private:
217 std::string mName; // name of the constraint
218 double mValue{0.0}; // constraint value
219 std::vector<uint32_t> mLabels; // parameter labels
220 std::vector<double> mCoeff; // their coefficients
221};
222
223// --- AlignableVolume ---
224
226{
227 public:
228 using Ptr = std::unique_ptr<AlignableVolume>;
229 using SensorMapping = std::map<GlobalLabel, AlignableVolume*>;
230
235 AlignableVolume(const char* symName, uint32_t label, uint32_t det, bool sens);
236 AlignableVolume(const char* symName, GlobalLabel label);
237 virtual ~AlignableVolume() = default;
238
239 void finalise(uint8_t level = 0);
240
241 // steering file output
242 void writeRigidBodyConstraints(std::ostream& os) const;
243 void writeParameters(std::ostream& os) const;
244 void writeTree(std::ostream& os, int indent = 0) const;
245
246 // tree-like
247 auto getLevel() const noexcept { return mLevel; }
248 bool isRoot() const noexcept { return mParent == nullptr; }
249 bool isLeaf() const noexcept { return mChildren.empty(); }
250 template <class T = AlignableVolume>
251 requires std::derived_from<T, AlignableVolume>
252 AlignableVolume* addChild(const char* symName, uint32_t label, uint32_t det, bool sens)
253 {
254 auto c = std::make_unique<T>(symName, label, det, sens);
255 return setParent(std::move(c));
256 }
257 template <class T = AlignableVolume>
258 requires std::derived_from<T, AlignableVolume>
259 AlignableVolume* addChild(const char* symName, GlobalLabel lbl)
260 {
261 auto c = std::make_unique<T>(symName, lbl);
262 return setParent(std::move(c));
263 }
264
265 // bfs traversal
266 void traverse(const std::function<void(AlignableVolume*)>& visitor)
267 {
268 visitor(this);
269 for (auto& c : mChildren) {
270 c->traverse(visitor);
271 }
272 }
273
274 std::string getSymName() const noexcept { return mSymName; }
275 GlobalLabel getLabel() const noexcept { return mLabel; }
276 AlignableVolume* getParent() const { return mParent; }
277 size_t getNChildren() const noexcept { return mChildren.size(); }
278
279 // DOF management
280 void setRigidBody(std::unique_ptr<DOFSet> rb) { mRigidBody = std::move(rb); }
281 void setCalib(std::unique_ptr<DOFSet> cal) { mCalib = std::move(cal); }
282 DOFSet* getRigidBody() const { return mRigidBody.get(); }
283 DOFSet* getCalib() const { return mCalib.get(); }
284 void setPseudo(bool p) noexcept { mIsPseudo = p; }
285 bool isPseudo() const noexcept { return mIsPseudo; }
286 void setSensorId(int id) noexcept { mSensorId = id; }
287 int getSensorId() const noexcept { return mSensorId; }
288 // true if this volume participates in the hierarchy (has DOFs or is pseudo)
289 bool isActive() const noexcept { return mRigidBody != nullptr || mIsPseudo; }
290
291 // transformation matrices
292 virtual void defineMatrixL2G() {}
293 virtual void defineMatrixT2L() {}
294 virtual void computeJacobianL2T(const double* pos, Matrix66& jac) const {};
295 const TGeoHMatrix& getL2P() const { return mL2P; }
296 const TGeoHMatrix& getT2L() const { return mT2L; }
297 const Matrix66& getJL2P() const { return mJL2P; }
298 const Matrix66& getJP2L() const { return mJP2L; }
299
300 protected:
302 AlignableVolume* mParent{nullptr}; // parent
303 TGeoPNEntry* mPNE{nullptr}; // physical entry
304 TGeoPhysicalNode* mPN{nullptr}; // physical node
305 TGeoHMatrix mL2G; // (LOC) -> (GLO)
306 TGeoHMatrix mL2P; // (LOC) -> (PAR)
307 Matrix66 mJL2P; // jac (LOC) -> (PAR)
308 Matrix66 mJP2L; // jac (PAR) -> (LOC)
309 TGeoHMatrix mT2L; // (TRK) -> (LOC)
310
311 private:
312 std::string mSymName;
313 GlobalLabel mLabel;
314 uint8_t mLevel{0};
315 bool mIsPseudo{false};
316 int mSensorId{-1};
317 std::unique_ptr<DOFSet> mRigidBody;
318 std::unique_ptr<DOFSet> mCalib;
319
320 AlignableVolume* setParent(Ptr c)
321 {
322 c->mParent = this;
323 mChildren.push_back(std::move(c));
324 return mChildren.back().get();
325 }
326 std::vector<Ptr> mChildren; // children
327
328 void init();
329};
330
331// apply DOF configuration from a JSON file to the hierarchy
332void applyDOFConfig(AlignableVolume* root, const std::string& jsonPath);
333
334// parse millepede.res and write result.json with fitted parameters for ITS3 half barrels
335void writeMillepedeResults(AlignableVolume* root, const std::string& milleResPath, const std::string& outJsonPath, const std::string& injectedJsonPath = "");
336
337} // namespace o2::its3::align
338
339#endif
int32_t i
uint16_t pos
Definition RawData.h:3
uint32_t j
Definition RawData.h:0
uint32_t c
Definition RawData.h:2
AlignableVolume * addChild(const char *symName, GlobalLabel lbl)
virtual void computeJacobianL2T(const double *pos, Matrix66 &jac) const
size_t getNChildren() const noexcept
AlignableVolume * addChild(const char *symName, uint32_t label, uint32_t det, bool sens)
AlignableVolume * getParent() const
void traverse(const std::function< void(AlignableVolume *)> &visitor)
virtual ~AlignableVolume()=default
std::unique_ptr< AlignableVolume > Ptr
void writeParameters(std::ostream &os) const
AlignableVolume & operator=(AlignableVolume &&)=delete
GlobalLabel getLabel() const noexcept
const TGeoHMatrix & getT2L() const
AlignableVolume(AlignableVolume &&)=delete
AlignableVolume * mParent
matrices
const TGeoHMatrix & getL2P() const
void setRigidBody(std::unique_ptr< DOFSet > rb)
AlignableVolume & operator=(const AlignableVolume &)=delete
std::map< GlobalLabel, AlignableVolume * > SensorMapping
void writeTree(std::ostream &os, int indent=0) const
std::string getSymName() const noexcept
void writeRigidBodyConstraints(std::ostream &os) const
AlignableVolume(const AlignableVolume &)=delete
void setCalib(std::unique_ptr< DOFSet > cal)
virtual ~DOFSet()=default
std::vector< bool > mFree
virtual Type type() const =0
virtual std::string dofName(int idx) const =0
void setFree(int idx, bool f)
bool isFree(int idx) const
GlobalLabel asCalib() const noexcept
return a copy of this label with the CALIB bit set (for calibration DOFs on same volume)
constexpr T raw(T dof) const noexcept
produce the raw Millepede label for a given DOF index (rigid body: calib=0 in label)
constexpr T id() const noexcept
constexpr int rawGBL(T dof) const noexcept
constexpr auto operator<=>(const GlobalLabel &) const noexcept=default
constexpr bool calib() const noexcept
constexpr bool sens() const noexcept
constexpr T det() const noexcept
static constexpr T bitMask(int b) noexcept
GlobalLabel(T det, T id, bool sens, bool calib=false)
void add(uint32_t lab, double coeff)
HierarchyConstraint(std::string name, double value)
std::string dofName(int idx) const override
std::string dofName(int idx) const override
GLdouble n
Definition glcorearb.h:1982
const GLfloat * m
Definition glcorearb.h:4066
const GLdouble * v
Definition glcorearb.h:832
GLuint const GLchar * name
Definition glcorearb.h:781
GLdouble f
Definition glcorearb.h:310
GLboolean GLboolean GLboolean b
Definition glcorearb.h:1233
GLsizei const GLfloat * value
Definition glcorearb.h:819
GLuint GLsizei const GLchar * label
Definition glcorearb.h:2519
GLint GLuint mask
Definition glcorearb.h:291
GLuint id
Definition glcorearb.h:650
Eigen::Matrix< double, 3, 6 > Matrix36
Eigen::Matrix< double, 6, 6 > Matrix66
void writeMillepedeResults(AlignableVolume *root, const std::string &milleResPath, const std::string &outJsonPath, const std::string &injectedJsonPath="")
void applyDOFConfig(AlignableVolume *root, const std::string &jsonPath)
auto getRigidBodyDerivatives(const auto &trk)
Defining DataPointCompositeObject explicitly as copiable.