Skip to content

Commit 37b4052

Browse files
committed
ITS3: alignment for inextionsial surfaces
Signed-off-by: Felix Schlepper <felix.schlepper@cern.ch>
1 parent 02943e3 commit 37b4052

File tree

6 files changed

+349
-22
lines changed

6 files changed

+349
-22
lines changed

Detectors/Upgrades/ITS3/alignment/README.md

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,4 +27,38 @@ dofSet.json:
2727
"calib": { "type": "legendre", "order": 1, "fix": [0, 2] }
2828
}
2929
]
30-
}```
30+
}
31+
```
32+
33+
34+
## In-existensional modes
35+
```json
36+
{
37+
"defaults": { "rigidBody": "fixed" },
38+
"rules": [
39+
{
40+
"match": "ITS3Layer1/ITS3CarbonForm0",
41+
"calib": {
42+
"type": "inextensional",
43+
"order": 2,
44+
"free": ["a_2", "b_2", "c_2", "d_2", "alpha", "beta"]
45+
}
46+
}
47+
]
48+
}
49+
```
50+
51+
```json
52+
[
53+
{
54+
"id": 2,
55+
"inextensional": {
56+
"modes": {
57+
"2": [0.0008, -0.0005, 0.0006, -0.0007]
58+
},
59+
"alpha": 0.0004,
60+
"beta": -0.0003
61+
}
62+
}
63+
]
64+
```

Detectors/Upgrades/ITS3/alignment/include/ITS3Align/AlignmentDOF.h

Lines changed: 51 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,11 @@
2121
class DOFSet
2222
{
2323
public:
24-
enum class Type : uint8_t { RigidBody,
25-
Legendre };
24+
enum class Type : uint8_t {
25+
RigidBody,
26+
Legendre,
27+
Inextensional
28+
};
2629
virtual ~DOFSet() = default;
2730
virtual Type type() const = 0;
2831
int nDOFs() const { return static_cast<int>(mFree.size()); }
@@ -102,4 +105,50 @@ class LegendreDOFSet final : public DOFSet
102105
int mOrder;
103106
};
104107

108+
// In-extensional deformation DOFs for cylindrical half-shells
109+
// Fourier modes n=2..N: 4 params each (a_n, b_n, c_n, d_n)
110+
// Plus 2 non-periodic modes (alpha, beta) for the half-cylinder open edges
111+
// Total: 4*(N-1) + 2
112+
class InextensionalDOFSet final : public DOFSet
113+
{
114+
public:
115+
explicit InextensionalDOFSet(int maxOrder) : DOFSet((4 * (maxOrder - 1)) + 2), mMaxOrder(maxOrder)
116+
{
117+
if (maxOrder < 2) {
118+
// the rest is eq. to rigid body
119+
throw std::invalid_argument("InextensionalDOFSet requires maxOrder >= 2");
120+
}
121+
}
122+
Type type() const override { return Type::Inextensional; }
123+
int maxOrder() const { return mMaxOrder; }
124+
125+
// number of periodic DOFs (before alpha, beta)
126+
int nPeriodic() const { return 4 * (mMaxOrder - 1); }
127+
128+
// flat index layout: [a_2, b_2, c_2, d_2, a_3, b_3, c_3, d_3, ..., alpha, beta]
129+
// index of first DOF for mode n
130+
static int modeOffset(int n) { return 4 * (n - 2); }
131+
132+
// indices of the non-periodic modes
133+
int alphaIdx() const { return nPeriodic(); }
134+
int betaIdx() const { return nPeriodic() + 1; }
135+
136+
std::string dofName(int idx) const override
137+
{
138+
if (idx == alphaIdx()) {
139+
return "alpha";
140+
}
141+
if (idx == betaIdx()) {
142+
return "beta";
143+
}
144+
int n = (idx / 4) + 2;
145+
int sub = idx % 4;
146+
static constexpr const char* subNames[] = {"a", "b", "c", "d"};
147+
return std::format("{}_{}", subNames[sub], n);
148+
}
149+
150+
private:
151+
int mMaxOrder;
152+
};
153+
105154
#endif

Detectors/Upgrades/ITS3/alignment/include/ITS3Align/AlignmentParams.h

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,9 +38,10 @@ struct AlignmentParams : public o2::conf::ConfigurableParamHelper<AlignmentParam
3838
float extraClsErrZ[6] = {0};
3939

4040
// misalignment simulation
41-
bool doMisalignmentLeg = false; // simulate Legendre deformation on ITS3 layers
42-
bool doMisalignmentRB = false; // simulate rigid body misalignment on ITS3 layers
43-
std::string misAlgJson; // JSON file with deformation and/or rigid body params
41+
bool doMisalignmentLeg = false; // simulate Legendre deformation on ITS3 layers
42+
bool doMisalignmentRB = false; // simulate rigid body misalignment on ITS3 layers
43+
bool doMisalignmentInex = false; // simulate in-extensional deformation on ITS3 layers
44+
std::string misAlgJson; // JSON file with deformation and/or rigid body params
4445

4546
// DOF configuration (JSON file defining which volumes have which DOFs)
4647
std::string dofConfigJson; // if empty, no DOFs are configured

Detectors/Upgrades/ITS3/alignment/include/ITS3Align/AlignmentSpec.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ enum class OutputOpt : uint8_t {
2424
MilleData,
2525
MilleSteer,
2626
MilleRes,
27+
MisRes,
2728
Debug,
2829
};
2930
using OutputEnum = utils::EnumFlags<OutputOpt>;

Detectors/Upgrades/ITS3/alignment/src/AlignmentHierarchy.cxx

Lines changed: 113 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -178,21 +178,23 @@ void AlignableVolume::writeParameters(std::ostream& os) const
178178
if (isRoot()) {
179179
os << "Parameter\n";
180180
}
181-
if (mRigidBody) {
182-
for (int iDOF = 0; iDOF < mRigidBody->nDOFs(); ++iDOF) {
183-
os << std::format("{:<10} {:>+15g} {:>+15g} ! {} {} ",
184-
mLabel.raw(iDOF), 0.0, (mRigidBody->isFree(iDOF) ? 0.0 : -1.0),
185-
(mRigidBody->isFree(iDOF) ? 'V' : 'F'), mRigidBody->dofName(iDOF))
186-
<< mSymName << '\n';
181+
if (!mIsPseudo) {
182+
if (mRigidBody) {
183+
for (int iDOF = 0; iDOF < mRigidBody->nDOFs(); ++iDOF) {
184+
os << std::format("{:<10} {:>+15g} {:>+15g} ! {} {} ",
185+
mLabel.raw(iDOF), 0.0, (mRigidBody->isFree(iDOF) ? 0.0 : -1.0),
186+
(mRigidBody->isFree(iDOF) ? 'V' : 'F'), mRigidBody->dofName(iDOF))
187+
<< mSymName << '\n';
188+
}
187189
}
188-
}
189-
if (mCalib) {
190-
auto calibLbl = mLabel.asCalib();
191-
for (int iDOF = 0; iDOF < mCalib->nDOFs(); ++iDOF) {
192-
os << std::format("{:<10} {:>+15g} {:>+15g} ! {} {} ",
193-
calibLbl.raw(iDOF), 0.0, (mCalib->isFree(iDOF) ? 0.0 : -1.0),
194-
(mCalib->isFree(iDOF) ? 'V' : 'F'), mCalib->dofName(iDOF))
195-
<< mSymName << '\n';
190+
if (mCalib) {
191+
auto calibLbl = mLabel.asCalib();
192+
for (int iDOF = 0; iDOF < mCalib->nDOFs(); ++iDOF) {
193+
os << std::format("{:<10} {:>+15g} {:>+15g} ! {} {:<5} ",
194+
calibLbl.raw(iDOF), 0.0, (mCalib->isFree(iDOF) ? 0.0 : -1.0),
195+
(mCalib->isFree(iDOF) ? 'V' : 'F'), mCalib->dofName(iDOF))
196+
<< mSymName << '\n';
197+
}
196198
}
197199
}
198200
for (const auto& c : mChildren) {
@@ -266,6 +268,9 @@ void applyDOFConfig(AlignableVolume* root, const std::string& jsonPath)
266268
}
267269

268270
root->traverse([&](AlignableVolume* vol) {
271+
if (vol->isPseudo()) {
272+
return;
273+
}
269274
const std::string& sym = vol->getSymName();
270275
for (const auto& rule : rules) {
271276
const auto pattern = rule["match"].get<std::string>();
@@ -357,6 +362,41 @@ void applyDOFConfig(AlignableVolume* root, const std::string& jsonPath)
357362
}
358363
}
359364
vol->setCalib(std::move(dofSet));
365+
} else if (calType == "inextensional") {
366+
int maxOrder = cal.value("order", 2);
367+
auto dofSet = std::make_unique<InextensionalDOFSet>(maxOrder);
368+
bool fixed = cal.value("fixed", false);
369+
if (fixed) {
370+
dofSet->setAllFree(false);
371+
}
372+
if (cal.contains("free")) {
373+
dofSet->setAllFree(false);
374+
for (const auto& item : cal["free"]) {
375+
if (item.is_number_integer()) {
376+
dofSet->setFree(item.get<int>(), true);
377+
} else if (item.is_string()) {
378+
for (int k = 0; k < dofSet->nDOFs(); ++k) {
379+
if (dofSet->dofName(k) == item.get<std::string>()) {
380+
dofSet->setFree(k, true);
381+
}
382+
}
383+
}
384+
}
385+
}
386+
if (cal.contains("fix")) {
387+
for (const auto& item : cal["fix"]) {
388+
if (item.is_number_integer()) {
389+
dofSet->setFree(item.get<int>(), false);
390+
} else if (item.is_string()) {
391+
for (int k = 0; k < dofSet->nDOFs(); ++k) {
392+
if (dofSet->dofName(k) == item.get<std::string>()) {
393+
dofSet->setFree(k, false);
394+
}
395+
}
396+
}
397+
}
398+
}
399+
vol->setCalib(std::move(dofSet));
360400
}
361401
}
362402
}
@@ -398,6 +438,12 @@ void writeMillepedeResults(AlignableVolume* root, const std::string& milleResPat
398438
// indexed by sensorID
399439
std::map<int, std::vector<double>> injRB;
400440
std::map<int, std::vector<std::vector<double>>> injMatrix;
441+
struct InjInex {
442+
std::map<int, std::array<double, 4>> modes;
443+
double alpha{0.};
444+
double beta{0.};
445+
};
446+
std::map<int, InjInex> injInex;
401447
if (!injectedJsonPath.empty()) {
402448
std::ifstream injFile(injectedJsonPath);
403449
if (injFile.is_open()) {
@@ -410,6 +456,22 @@ void writeMillepedeResults(AlignableVolume* root, const std::string& milleResPat
410456
if (item.contains("matrix")) {
411457
injMatrix[id] = item["matrix"].get<std::vector<std::vector<double>>>();
412458
}
459+
if (item.contains("inextensional")) {
460+
InjInex ii;
461+
const auto& inex = item["inextensional"];
462+
if (inex.contains("modes")) {
463+
for (auto& [key, val] : inex["modes"].items()) {
464+
ii.modes[std::stoi(key)] = val.get<std::array<double, 4>>();
465+
}
466+
}
467+
if (inex.contains("alpha")) {
468+
ii.alpha = inex["alpha"].get<double>();
469+
}
470+
if (inex.contains("beta")) {
471+
ii.beta = inex["beta"].get<double>();
472+
}
473+
injInex[id] = ii;
474+
}
413475
}
414476
LOGP(info, "Loaded injected misalignment for {} sensors", injData.size());
415477
} else {
@@ -468,6 +530,43 @@ void writeMillepedeResults(AlignableVolume* root, const std::string& milleResPat
468530
matrix.push_back(row);
469531
}
470532
entry["matrix"] = matrix;
533+
} else if (cal && cal->nFreeDOFs() && cal->type() == DOFSet::Type::Inextensional) {
534+
write = true;
535+
auto* inexSet = static_cast<const InextensionalDOFSet*>(cal);
536+
int maxN = inexSet->maxOrder();
537+
auto calibLbl = vol->getLabel().asCalib();
538+
const auto& inj = injInex.contains(id) ? injInex[id] : InjInex{};
539+
540+
json inexEntry;
541+
json modesObj = json::object();
542+
for (int n = 2; n <= maxN; ++n) {
543+
int off = InextensionalDOFSet::modeOffset(n);
544+
std::array<double, 4> injCoeffs = {0., 0., 0., 0.};
545+
if (inj.modes.contains(n)) {
546+
injCoeffs = inj.modes.at(n);
547+
}
548+
json modeArr = json::array();
549+
for (int k = 0; k < 4; ++k) {
550+
uint32_t raw = calibLbl.raw(off + k);
551+
auto it = labelToValue.find(raw);
552+
double fitted = it != labelToValue.end() ? it->second : 0.0;
553+
modeArr.push_back(fitted - injCoeffs[k]);
554+
}
555+
modesObj[std::to_string(n)] = modeArr;
556+
}
557+
inexEntry["modes"] = modesObj;
558+
559+
// alpha
560+
uint32_t rawAlpha = calibLbl.raw(inexSet->alphaIdx());
561+
auto itA = labelToValue.find(rawAlpha);
562+
inexEntry["alpha"] = (itA != labelToValue.end() ? itA->second : 0.0) - inj.alpha;
563+
564+
// beta
565+
uint32_t rawBeta = calibLbl.raw(inexSet->betaIdx());
566+
auto itB = labelToValue.find(rawBeta);
567+
inexEntry["beta"] = (itB != labelToValue.end() ? itB->second : 0.0) - inj.beta;
568+
569+
entry["inextensional"] = inexEntry;
471570
}
472571
if (write) {
473572
output.push_back(entry);

0 commit comments

Comments
 (0)