Project
Loading...
Searching...
No Matches
AlignmentHierarchy.cxx
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#include <format>
13#include <fstream>
14#include <sstream>
15#include <fnmatch.h>
16#include <cmath>
17#include <TGeoManager.h>
18#include <TGeoPhysicalNode.h>
19#include <nlohmann/json.hpp>
20
23#include "Framework/Logger.h"
24#include "MathUtils/Utils.h"
25
26namespace o2::its3::align
27{
28
29void HierarchyConstraint::write(std::ostream& os) const
30{
31 os << "!!! " << mName << '\n';
32 os << "Constraint " << mValue << '\n';
33 for (size_t i{0}; i < mLabels.size(); ++i) {
34 os << mLabels[i] << " " << mCoeff[i] << '\n';
35 }
36 os << '\n';
37}
38
39AlignableVolume::AlignableVolume(const char* symName, uint32_t label, uint32_t det, bool sens) : mSymName(symName), mLabel(det, label, sens)
40{
41 init();
42}
43
44AlignableVolume::AlignableVolume(const char* symName, GlobalLabel label) : mSymName(symName), mLabel(label)
45{
46 init();
47}
48
49void AlignableVolume::init()
50{
51 // check if this sym volume actually exists
52 mPNE = gGeoManager->GetAlignableEntry(mSymName.c_str());
53 if (mPNE == nullptr) {
54 LOGP(fatal, "Symbolic volume '{}' has no corresponding alignable entry!", mSymName);
55 }
56 mPN = mPNE->GetPhysicalNode();
57 if (mPN == nullptr) {
58 LOGP(debug, "Adding physical node to {}", mSymName);
59 mPN = gGeoManager->MakePhysicalNode(mPNE->GetTitle());
60 if (mPN == nullptr) {
61 LOGP(fatal, "Failed to make physical node for {}", mSymName);
62 }
63 }
64}
65
67{
68 if (level == 0 && !isRoot()) {
69 LOGP(fatal, "Finalise should be called only from the root node!");
70 }
71 mLevel = level;
72 if (!isLeaf()) {
73 // depth first
74 for (const auto& c : mChildren) {
75 c->finalise(level + 1);
76 }
77 // auto-disable parent RB DOFs if no children are active
78 if (mRigidBody) {
79 int nActiveChildren = 0;
80 for (const auto& c : mChildren) {
81 if (c->isActive()) {
82 ++nActiveChildren;
83 }
84 }
85 if (!nActiveChildren) {
86 for (int iDOF = 0; iDOF < mRigidBody->nDOFs(); ++iDOF) {
87 if (mRigidBody->isFree(iDOF)) {
88 LOGP(warn, "Auto-disabling DOF {} for {} since no active children",
89 mRigidBody->dofName(iDOF), mSymName);
90 mRigidBody->setFree(iDOF, false);
91 }
92 }
93 }
94 }
95 } else {
96 // for sensors we need also to define the transformation from the measurment (TRK) to the local frame (LOC)
97 // need to it with including possible pre-alignment to allow for iterative convergence
98 // (TRK) is defined wrt global z-axis
101 }
102 if (!isRoot()) {
103 // prepare the transformation matrices, e.g. from child frame to parent frame
104 // this is not necessarily just one level transformation
105 TGeoHMatrix mat = *mPN->GetMatrix(); // global matrix (including possible pre-alignment) from this volume to the global frame
106 if (isLeaf()) {
107 mat = mL2G; // for sensor volumes they might have redefined the L2G definition
108 }
109 auto inv = mParent->mPN->GetMatrix()->Inverse(); // global (including possible pre-alignment) from this volume to the global frame
110 mat.MultiplyLeft(inv); // left mult. effectively subtracts the parent transformation which is included in the the childs
111 mL2P = mat; // now this is directly the child to the parent transformation (LOC) (including possible pre-alignment)
112
113 // prepare jacobian from child to parent frame
114 Eigen::Map<const Eigen::Matrix<double, 3, 3, Eigen::RowMajor>> rotL2P(mL2P.GetRotationMatrix());
115 Eigen::Matrix3d rotInv = rotL2P.transpose(); // parent-to-child rotation
116 const double* t = mL2P.GetTranslation(); // child origin in parent frame
117 Eigen::Matrix3d skewT;
118 skewT << 0, -t[2], t[1], t[2], 0, -t[0], -t[1], t[0], 0;
119 mJL2P.setZero();
120 mJL2P.topLeftCorner<3, 3>() = rotInv;
121 mJL2P.topRightCorner<3, 3>() = -rotInv * skewT;
122 mJL2P.bottomRightCorner<3, 3>() = rotInv;
123 mJP2L = mJL2P.inverse();
124 }
125}
126
128{
129 if (isLeaf() || !mRigidBody) {
130 // recurse even if this node has no RB DOFs
131 for (const auto& c : mChildren) {
132 c->writeRigidBodyConstraints(os);
133 }
134 return;
135 }
136
137 for (int iDOF = 0; iDOF < mRigidBody->nDOFs(); ++iDOF) {
138 if (!mRigidBody->isFree(iDOF)) {
139 continue;
140 }
141 double nActiveChildren = 0.;
142 for (const auto& c : mChildren) {
143 if (c->isActive()) {
144 ++nActiveChildren;
145 }
146 }
147 if (nActiveChildren == 0.) {
148 LOGP(fatal, "{} has dof {} active but no active children!", mSymName, mRigidBody->dofName(iDOF));
149 }
150 const double invN = 1.0 / nActiveChildren;
151 HierarchyConstraint con(std::format("DOF {} for {}", mRigidBody->dofName(iDOF), mSymName), 0.0);
152 for (const auto& c : mChildren) {
153 if (!c->mRigidBody) {
154 continue;
155 }
156 for (int jDOF = 0; jDOF < c->mRigidBody->nDOFs(); ++jDOF) {
157 if (!c->mRigidBody->isFree(jDOF)) {
158 continue;
159 }
160 double coeff = invN * c->getJP2L()(iDOF, jDOF);
161 if (std::abs(coeff) > 1e-16f) {
162 con.add(c->getLabel().raw(jDOF), coeff);
163 }
164 }
165 }
166
167 if (con.getSize() > 1) {
168 con.write(os);
169 }
170 }
171 for (const auto& c : mChildren) {
172 c->writeRigidBodyConstraints(os);
173 }
174}
175
176void AlignableVolume::writeParameters(std::ostream& os) const
177{
178 if (isRoot()) {
179 os << "Parameter\n";
180 }
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 }
189 }
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 }
198 }
199 }
200 for (const auto& c : mChildren) {
201 c->writeParameters(os);
202 }
203}
204
205void AlignableVolume::writeTree(std::ostream& os, int indent) const
206{
207 os << std::string(static_cast<size_t>(indent * 2), ' ') << mSymName << (mLabel.sens() ? " (sens)" : " (pasv)");
208 if (mIsPseudo) {
209 os << " pseudo";
210 } else {
211 int nFreeDofs{0};
212 if (mRigidBody && mRigidBody->nFreeDOFs()) {
213 nFreeDofs += mRigidBody->nFreeDOFs();
214 os << " RB[";
215 for (int i = 0; i < mRigidBody->nDOFs(); ++i) {
216 if (mRigidBody->isFree(i)) {
217 os << " " << mRigidBody->dofName(i) << "(" << mLabel.raw(i) << ")";
218 }
219 }
220 os << " ]";
221 }
222 if (mCalib && mCalib->nFreeDOFs()) {
223 nFreeDofs += mCalib->nFreeDOFs();
224 os << " CAL[";
225 auto calibLbl = mLabel.asCalib();
226 for (int i = 0; i < mCalib->nDOFs(); ++i) {
227 if (mCalib->isFree(i)) {
228 os << " " << mCalib->dofName(i) << "(" << calibLbl.raw(i) << ")";
229 }
230 }
231 os << " ]";
232 }
233 if (!nFreeDofs) {
234 os << " no DOFs";
235 }
236 }
237 os << '\n';
238 for (const auto& c : mChildren) {
239 c->writeTree(os, indent + 2);
240 }
241}
242
243void applyDOFConfig(AlignableVolume* root, const std::string& jsonPath)
244{
245 using json = nlohmann::json;
246 std::ifstream f(jsonPath);
247 if (!f.is_open()) {
248 LOGP(fatal, "Cannot open DOF config file: {}", jsonPath);
249 }
250 auto data = json::parse(f);
251 json rules = data.is_array() ? data : data.value("rules", json::array());
252
253 static const std::map<std::string, int> rbNameToIdx = {
254 {"TX", 0}, {"TY", 1}, {"TZ", 2}, {"RX", 3}, {"RY", 4}, {"RZ", 5}};
255
256 auto matchPattern = [](const std::string& pattern, const std::string& sym) -> bool {
257 if (fnmatch(pattern.c_str(), sym.c_str(), 0) == 0) {
258 return true;
259 }
260 std::string prefixed = "*" + pattern;
261 return fnmatch(prefixed.c_str(), sym.c_str(), 0) == 0;
262 };
263
264 if (data.is_object() && data.contains("defaults")) {
265 json defRule = data["defaults"];
266 defRule["match"] = "*";
267 rules.insert(rules.begin(), defRule);
268 }
269
270 root->traverse([&](AlignableVolume* vol) {
271 if (vol->isPseudo()) {
272 return;
273 }
274 const std::string& sym = vol->getSymName();
275 for (const auto& rule : rules) {
276 const auto pattern = rule["match"].get<std::string>();
277 if (!matchPattern(pattern, sym)) {
278 continue;
279 }
280 // rigid body DOFs
281 if (rule.contains("rigidBody")) {
282 const auto& rb = rule["rigidBody"];
283 if (rb.is_string()) {
284 auto s = rb.get<std::string>();
285 if (s == "all" || s == "free") {
286 vol->setRigidBody(std::make_unique<RigidBodyDOFSet>());
287 } else if (s == "fixed") {
288 auto dofSet = std::make_unique<RigidBodyDOFSet>();
289 dofSet->setAllFree(false);
290 vol->setRigidBody(std::move(dofSet));
291 }
292 } else if (rb.is_array()) {
293 auto dofSet = std::make_unique<RigidBodyDOFSet>();
294 dofSet->setAllFree(false);
295 for (const auto& name : rb) {
296 auto it = rbNameToIdx.find(name.get<std::string>());
297 if (it != rbNameToIdx.end()) {
298 dofSet->setFree(it->second, true);
299 }
300 }
301 vol->setRigidBody(std::move(dofSet));
302 } else if (rb.is_object()) {
303 auto dofs = rb.value("dofs", std::string("all"));
304 bool fixed = rb.value("fixed", false);
305 if (dofs == "all") {
306 auto dofSet = std::make_unique<RigidBodyDOFSet>();
307 if (fixed) {
308 dofSet->setAllFree(false);
309 }
310 vol->setRigidBody(std::move(dofSet));
311 } else if (rb["dofs"].is_array()) {
312 auto dofSet = std::make_unique<RigidBodyDOFSet>();
313 dofSet->setAllFree(false);
314 for (const auto& name : rb["dofs"]) {
315 auto it = rbNameToIdx.find(name.get<std::string>());
316 if (it != rbNameToIdx.end()) {
317 dofSet->setFree(it->second, !fixed);
318 }
319 }
320 vol->setRigidBody(std::move(dofSet));
321 }
322 }
323 }
324 // calibration DOFs
325 if (rule.contains("calib")) {
326 const auto& cal = rule["calib"];
327 auto calType = cal.value("type", std::string(""));
328 if (calType == "legendre") {
329 int order = cal.value("order", 3);
330 auto dofSet = std::make_unique<LegendreDOFSet>(order);
331 bool fixed = cal.value("fixed", false);
332 if (fixed) {
333 dofSet->setAllFree(false);
334 }
335 // fix/free individual coefficients by name or index
336 if (cal.contains("free")) {
337 dofSet->setAllFree(false);
338 for (const auto& item : cal["free"]) {
339 if (item.is_number_integer()) {
340 dofSet->setFree(item.get<int>(), true);
341 } else if (item.is_string()) {
342 // match by name e.g. "L(1,0)"
343 for (int k = 0; k < dofSet->nDOFs(); ++k) {
344 if (dofSet->dofName(k) == item.get<std::string>()) {
345 dofSet->setFree(k, true);
346 }
347 }
348 }
349 }
350 }
351 if (cal.contains("fix")) {
352 for (const auto& item : cal["fix"]) {
353 if (item.is_number_integer()) {
354 dofSet->setFree(item.get<int>(), false);
355 } else if (item.is_string()) {
356 for (int k = 0; k < dofSet->nDOFs(); ++k) {
357 if (dofSet->dofName(k) == item.get<std::string>()) {
358 dofSet->setFree(k, false);
359 }
360 }
361 }
362 }
363 }
364 vol->setCalib(std::move(dofSet));
365 } else if (calType == "inextensional") {
366 int maxOrder = cal.value("order", 2);
367 // optional strictly radial (extensional) modes h_{k,l}, l >= 1
368 int extOrderPhi = cal.value("extOrderPhi", -1);
369 int extOrderZ = cal.value("extOrderZ", 0);
370 auto dofSet = std::make_unique<InextensionalDOFSet>(maxOrder, extOrderPhi, extOrderZ);
371 bool fixed = cal.value("fixed", false);
372 if (fixed) {
373 dofSet->setAllFree(false);
374 }
375 if (cal.contains("free")) {
376 dofSet->setAllFree(false);
377 for (const auto& item : cal["free"]) {
378 if (item.is_number_integer()) {
379 dofSet->setFree(item.get<int>(), true);
380 } else if (item.is_string()) {
381 for (int k = 0; k < dofSet->nDOFs(); ++k) {
382 if (dofSet->dofName(k) == item.get<std::string>()) {
383 dofSet->setFree(k, true);
384 }
385 }
386 }
387 }
388 }
389 if (cal.contains("fix")) {
390 for (const auto& item : cal["fix"]) {
391 if (item.is_number_integer()) {
392 dofSet->setFree(item.get<int>(), false);
393 } else if (item.is_string()) {
394 for (int k = 0; k < dofSet->nDOFs(); ++k) {
395 if (dofSet->dofName(k) == item.get<std::string>()) {
396 dofSet->setFree(k, false);
397 }
398 }
399 }
400 }
401 }
402 vol->setCalib(std::move(dofSet));
403 }
404 }
405 }
406 });
407}
408
409void writeMillepedeResults(AlignableVolume* root, const std::string& milleResPath, const std::string& outJsonPath, const std::string& injectedJsonPath)
410{
411 using json = nlohmann::json;
412
413 // parse millepede.res: label fittedValue presigma [...]
414 std::ifstream fin(milleResPath);
415 if (!fin.is_open()) {
416 LOGP(fatal, "Cannot open millepede result file: {}", milleResPath);
417 }
418 std::map<uint32_t, double> labelToValue;
419 std::string line;
420 while (std::getline(fin, line)) {
421 if (line.empty() || line[0] == '!' || line[0] == '*') {
422 continue;
423 }
424 if (line.find("Parameter") != std::string::npos) {
425 continue;
426 }
427 std::istringstream iss(line);
428 uint32_t label = 0;
429 double value = NAN, presigma = NAN;
430 if (!(iss >> label >> value >> presigma)) {
431 continue;
432 }
433 if (presigma >= 0.0) { // skip fixed parameters
434 labelToValue[label] = value;
435 }
436 }
437 fin.close();
438 LOGP(info, "Parsed {} not fixed parameters from {}", labelToValue.size(), milleResPath);
439
440 // load injected misalignment if provided (same format as closure test input)
441 // indexed by sensorID
442 std::map<int, std::vector<double>> injRB;
443 std::map<int, std::vector<std::vector<double>>> injMatrix;
444 struct InjInex {
445 std::map<int, double> f;
446 std::map<int, double> g;
447 std::map<std::pair<int, int>, double> h;
448 };
449 std::map<int, InjInex> injInex;
450 if (!injectedJsonPath.empty()) {
451 std::ifstream injFile(injectedJsonPath);
452 if (injFile.is_open()) {
453 json injData = json::parse(injFile);
454 for (const auto& item : injData) {
455 int id = item["id"].get<int>();
456 if (item.contains("rigidBody")) {
457 injRB[id] = item["rigidBody"].get<std::vector<double>>();
458 }
459 if (item.contains("matrix")) {
460 injMatrix[id] = item["matrix"].get<std::vector<std::vector<double>>>();
461 }
462 if (item.contains("inextensional")) {
463 InjInex ii;
464 const auto& inex = item["inextensional"];
465 if (inex.contains("f")) {
466 for (auto& [key, val] : inex["f"].items()) {
467 ii.f[std::stoi(key)] = val.get<double>();
468 }
469 }
470 if (inex.contains("g")) {
471 for (auto& [key, val] : inex["g"].items()) {
472 ii.g[std::stoi(key)] = val.get<double>();
473 }
474 }
475 if (inex.contains("h")) {
476 for (auto& [key, val] : inex["h"].items()) {
477 const auto sep = key.find('_');
478 if (sep == std::string::npos) {
479 continue;
480 }
481 ii.h[{std::stoi(key.substr(0, sep)), std::stoi(key.substr(sep + 1))}] = val.get<double>();
482 }
483 }
484 injInex[id] = ii;
485 }
486 }
487 LOGP(info, "Loaded injected misalignment for {} sensors", injData.size());
488 } else {
489 LOGP(warn, "Cannot open injected misalignment file: {}, writing absolute values", injectedJsonPath);
490 }
491 }
492
493 // collect results per volume that has RB or calib DOFs
494 json output = json::array();
495 root->traverse([&](AlignableVolume* vol) {
496 auto* rb = vol->getRigidBody();
497 auto* cal = vol->getCalib();
498 if ((!rb && !cal) || vol->isPseudo()) {
499 return;
500 }
501 int id = vol->getSensorId();
502 json entry;
503 entry["symName"] = vol->getSymName();
504 entry["id"] = id;
505 bool write = false;
506
507 // rigid body parameters
508 if (rb && rb->nFreeDOFs()) {
509 write = true;
510 json rbArr = json::array();
511 const auto& inj = injRB.contains(id) ? injRB[id] : std::vector<double>{};
512 for (int i = 0; i < rb->nDOFs(); ++i) {
513 uint32_t raw = vol->getLabel().raw(i);
514 auto it = labelToValue.find(raw);
515 double fitted = it != labelToValue.end() ? it->second : 0.0;
516 double ref = i < static_cast<int>(inj.size()) ? inj[i] : 0.0;
517 rbArr.push_back(fitted - ref);
518 }
519 entry["rigidBody"] = rbArr;
520 }
521
522 // calibration (Legendre) parameters
523 if (cal && cal->nFreeDOFs() && cal->type() == DOFSet::Type::Legendre) {
524 write = true;
525 auto* leg = dynamic_cast<const LegendreDOFSet*>(cal);
526 int order = leg->order();
527 auto calibLbl = vol->getLabel().asCalib();
528 const auto& inj = injMatrix.contains(id) ? injMatrix[id] : std::vector<std::vector<double>>{};
529 json matrix = json::array();
530 int idx = 0;
531 for (int i = 0; i <= order; ++i) {
532 json row = json::array();
533 for (int j = 0; j <= i; ++j) {
534 uint32_t raw = calibLbl.raw(idx);
535 auto it = labelToValue.find(raw);
536 double fitted = it != labelToValue.end() ? it->second : 0.0;
537 double ref = (i < static_cast<int>(inj.size()) && j < static_cast<int>(inj[i].size())) ? inj[i][j] : 0.0;
538 row.push_back(fitted - ref);
539 ++idx;
540 }
541 matrix.push_back(row);
542 }
543 entry["matrix"] = matrix;
544 } else if (cal && cal->nFreeDOFs() && cal->type() == DOFSet::Type::Inextensional) {
545 write = true;
546 auto* inexSet = static_cast<const InextensionalDOFSet*>(cal);
547 auto calibLbl = vol->getLabel().asCalib();
548 const auto& inj = injInex.contains(id) ? injInex[id] : InjInex{};
549
550 auto fittedAt = [&](int idx) {
551 uint32_t raw = calibLbl.raw(idx);
552 auto it = labelToValue.find(raw);
553 return it != labelToValue.end() ? it->second : 0.0;
554 };
555
556 json inexEntry;
557 json fObj = json::object();
558 json gObj = json::object();
559 for (int k = 0; k <= inexSet->maxOrder(); ++k) {
560 const double injF = inj.f.contains(k) ? inj.f.at(k) : 0.0;
561 const double injG = inj.g.contains(k) ? inj.g.at(k) : 0.0;
562 fObj[std::to_string(k)] = fittedAt(InextensionalDOFSet::fIdx(k)) - injF;
563 gObj[std::to_string(k)] = fittedAt(InextensionalDOFSet::gIdx(k)) - injG;
564 }
565 inexEntry["f"] = fObj;
566 inexEntry["g"] = gObj;
567
568 if (inexSet->hasExtensional()) {
569 json hObj = json::object();
570 for (int k = 0; k <= inexSet->extOrderPhi(); ++k) {
571 for (int l = 1; l <= inexSet->extOrderZ(); ++l) {
572 const auto key = std::pair<int, int>{k, l};
573 const double injH = inj.h.contains(key) ? inj.h.at(key) : 0.0;
574 hObj[std::format("{}_{}", k, l)] = fittedAt(inexSet->hIdx(k, l)) - injH;
575 }
576 }
577 inexEntry["h"] = hObj;
578 }
579
580 entry["inextensional"] = inexEntry;
581 }
582 if (write) {
583 output.push_back(entry);
584 }
585 });
586
587 std::ofstream fout(outJsonPath);
588 if (!fout.is_open()) {
589 LOGP(fatal, "Cannot open output file: {}", outJsonPath);
590 }
591 fout << output.dump(2) << '\n';
592 fout.close();
593 LOGP(info, "Wrote millepede results to {}", outJsonPath);
594}
595
596} // namespace o2::its3::align
General auxilliary methods.
std::ostringstream debug
int32_t i
o2::raw::RawFileWriter * raw
Definition of the GeometryTGeo class.
void output(const std::map< std::string, ChannelStat > &channels)
Definition rawdump.cxx:197
uint32_t j
Definition RawData.h:0
uint32_t c
Definition RawData.h:2
StringRef key
constexpr T raw(T dof) const noexcept
produce the raw Millepede label for a given DOF index (rigid body: calib=0 in label)
GlobalLabel asCalib() const noexcept
return a copy of this label with the CALIB bit set (for calibration DOFs on same volume)
constexpr bool sens() const noexcept
static int fIdx(int k)
static int gIdx(int k)
int order() const
Class for time synchronization of RawReader instances.
void traverse(const std::function< void(AlignableVolume *)> &visitor)
void writeParameters(std::ostream &os) const
GlobalLabel getLabel() const noexcept
AlignableVolume * mParent
matrices
void setRigidBody(std::unique_ptr< DOFSet > rb)
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)
void add(uint32_t lab, double coeff)
GLuint entry
Definition glcorearb.h:5735
GLsizeiptr size
Definition glcorearb.h:659
GLuint const GLchar * name
Definition glcorearb.h:781
GLdouble f
Definition glcorearb.h:310
GLsizei const GLfloat * value
Definition glcorearb.h:819
GLboolean * data
Definition glcorearb.h:298
GLuint GLsizei const GLchar * label
Definition glcorearb.h:2519
GLboolean GLboolean g
Definition glcorearb.h:1233
GLuint GLfloat * val
Definition glcorearb.h:1582
GLint level
Definition glcorearb.h:275
GLint ref
Definition glcorearb.h:291
GLuint id
Definition glcorearb.h:650
void writeMillepedeResults(AlignableVolume *root, const std::string &milleResPath, const std::string &outJsonPath, const std::string &injectedJsonPath="")
void applyDOFConfig(AlignableVolume *root, const std::string &jsonPath)
std::string to_string(gsl::span< T, Size > span)
Definition common.h:52
nlohmann::json json
std::vector< int > row
std::array< uint16_t, 5 > pattern