17#include <TGeoManager.h>
18#include <TGeoPhysicalNode.h>
19#include <nlohmann/json.hpp>
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';
49void AlignableVolume::init()
52 mPNE = gGeoManager->GetAlignableEntry(mSymName.c_str());
53 if (
mPNE ==
nullptr) {
54 LOGP(fatal,
"Symbolic volume '{}' has no corresponding alignable entry!", mSymName);
58 LOGP(
debug,
"Adding physical node to {}", mSymName);
59 mPN = gGeoManager->MakePhysicalNode(
mPNE->GetTitle());
61 LOGP(fatal,
"Failed to make physical node for {}", mSymName);
69 LOGP(fatal,
"Finalise should be called only from the root node!");
74 for (
const auto&
c : mChildren) {
79 int nActiveChildren = 0;
80 for (
const auto&
c : mChildren) {
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);
105 TGeoHMatrix mat = *
mPN->GetMatrix();
109 auto inv =
mParent->
mPN->GetMatrix()->Inverse();
110 mat.MultiplyLeft(inv);
114 Eigen::Map<const Eigen::Matrix<double, 3, 3, Eigen::RowMajor>> rotL2P(
mL2P.GetRotationMatrix());
115 Eigen::Matrix3d rotInv = rotL2P.transpose();
116 const double* t =
mL2P.GetTranslation();
117 Eigen::Matrix3d skewT;
118 skewT << 0, -t[2], t[1], t[2], 0, -t[0], -t[1], t[0], 0;
120 mJL2P.topLeftCorner<3, 3>() = rotInv;
121 mJL2P.topRightCorner<3, 3>() = -rotInv * skewT;
122 mJL2P.bottomRightCorner<3, 3>() = rotInv;
129 if (
isLeaf() || !mRigidBody) {
131 for (
const auto&
c : mChildren) {
132 c->writeRigidBodyConstraints(os);
137 for (
int iDOF = 0; iDOF < mRigidBody->nDOFs(); ++iDOF) {
138 if (!mRigidBody->isFree(iDOF)) {
141 double nActiveChildren = 0.;
142 for (
const auto&
c : mChildren) {
147 if (nActiveChildren == 0.) {
148 LOGP(fatal,
"{} has dof {} active but no active children!", mSymName, mRigidBody->dofName(iDOF));
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) {
156 for (
int jDOF = 0; jDOF <
c->mRigidBody->nDOFs(); ++jDOF) {
157 if (!
c->mRigidBody->isFree(jDOF)) {
160 double coeff = invN *
c->getJP2L()(iDOF, jDOF);
161 if (std::abs(coeff) > 1e-16f) {
162 con.
add(
c->getLabel().raw(jDOF), coeff);
171 for (
const auto&
c : mChildren) {
172 c->writeRigidBodyConstraints(os);
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))
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))
198 for (
const auto&
c : mChildren) {
199 c->writeParameters(os);
205 os << std::string(static_cast<size_t>(indent * 2),
' ') << mSymName << (mLabel.
sens() ?
" (sens)" :
" (pasv)");
210 if (mRigidBody && mRigidBody->nFreeDOFs()) {
211 nFreeDofs += mRigidBody->nFreeDOFs();
213 for (
int i = 0;
i < mRigidBody->nDOFs(); ++
i) {
214 if (mRigidBody->isFree(
i)) {
215 os <<
" " << mRigidBody->dofName(
i) <<
"(" << mLabel.
raw(
i) <<
")";
220 if (mCalib && mCalib->nFreeDOFs()) {
221 nFreeDofs += mCalib->nFreeDOFs();
223 auto calibLbl = mLabel.
asCalib();
224 for (
int i = 0;
i < mCalib->nDOFs(); ++
i) {
225 if (mCalib->isFree(
i)) {
226 os <<
" " << mCalib->dofName(
i) <<
"(" << calibLbl.raw(
i) <<
")";
236 for (
const auto&
c : mChildren) {
237 c->writeTree(os, indent + 2);
243 using json = nlohmann::json;
244 std::ifstream
f(jsonPath);
246 LOGP(fatal,
"Cannot open DOF config file: {}", jsonPath);
248 auto data = json::parse(
f);
251 static const std::map<std::string, int> rbNameToIdx = {
252 {
"TX", 0}, {
"TY", 1}, {
"TZ", 2}, {
"RX", 3}, {
"RY", 4}, {
"RZ", 5}};
254 auto matchPattern = [](
const std::string&
pattern,
const std::string& sym) ->
bool {
255 if (fnmatch(
pattern.c_str(), sym.c_str(), 0) == 0) {
258 std::string prefixed =
"*" +
pattern;
259 return fnmatch(prefixed.c_str(), sym.c_str(), 0) == 0;
262 if (
data.is_object() &&
data.contains(
"defaults")) {
264 defRule[
"match"] =
"*";
265 rules.insert(rules.begin(), defRule);
270 for (
const auto& rule : rules) {
271 const auto pattern = rule[
"match"].get<std::string>();
272 if (!matchPattern(
pattern, sym)) {
276 if (rule.contains(
"rigidBody")) {
277 const auto& rb = rule[
"rigidBody"];
278 if (rb.is_string()) {
279 auto s = rb.get<std::string>();
280 if (s ==
"all" || s ==
"free") {
282 }
else if (s ==
"fixed") {
283 auto dofSet = std::make_unique<RigidBodyDOFSet>();
284 dofSet->setAllFree(
false);
287 }
else if (rb.is_array()) {
288 auto dofSet = std::make_unique<RigidBodyDOFSet>();
289 dofSet->setAllFree(
false);
290 for (
const auto&
name : rb) {
291 auto it = rbNameToIdx.find(
name.get<std::string>());
292 if (it != rbNameToIdx.end()) {
293 dofSet->setFree(it->second,
true);
297 }
else if (rb.is_object()) {
298 auto dofs = rb.value(
"dofs", std::string(
"all"));
299 bool fixed = rb.value(
"fixed",
false);
301 auto dofSet = std::make_unique<RigidBodyDOFSet>();
303 dofSet->setAllFree(
false);
306 }
else if (rb[
"dofs"].is_array()) {
307 auto dofSet = std::make_unique<RigidBodyDOFSet>();
308 dofSet->setAllFree(
false);
309 for (
const auto&
name : rb[
"dofs"]) {
310 auto it = rbNameToIdx.find(
name.get<std::string>());
311 if (it != rbNameToIdx.end()) {
312 dofSet->setFree(it->second, !fixed);
320 if (rule.contains(
"calib")) {
321 const auto& cal = rule[
"calib"];
322 auto calType = cal.value(
"type", std::string(
""));
323 if (calType ==
"legendre") {
324 int order = cal.value(
"order", 3);
325 auto dofSet = std::make_unique<LegendreDOFSet>(order);
326 bool fixed = cal.value(
"fixed",
false);
328 dofSet->setAllFree(
false);
331 if (cal.contains(
"free")) {
332 dofSet->setAllFree(
false);
333 for (
const auto& item : cal[
"free"]) {
334 if (item.is_number_integer()) {
335 dofSet->setFree(item.get<
int>(),
true);
336 }
else if (item.is_string()) {
338 for (
int k = 0; k < dofSet->nDOFs(); ++k) {
339 if (dofSet->dofName(k) == item.get<std::string>()) {
340 dofSet->setFree(k,
true);
346 if (cal.contains(
"fix")) {
347 for (
const auto& item : cal[
"fix"]) {
348 if (item.is_number_integer()) {
349 dofSet->setFree(item.get<
int>(),
false);
350 }
else if (item.is_string()) {
351 for (
int k = 0; k < dofSet->nDOFs(); ++k) {
352 if (dofSet->dofName(k) == item.get<std::string>()) {
353 dofSet->setFree(k,
false);
368 using json = nlohmann::json;
371 std::ifstream fin(milleResPath);
372 if (!fin.is_open()) {
373 LOGP(fatal,
"Cannot open millepede result file: {}", milleResPath);
375 std::map<uint32_t, double> labelToValue;
377 while (std::getline(fin, line)) {
378 if (line.empty() || line[0] ==
'!' || line[0] ==
'*') {
381 if (line.find(
"Parameter") != std::string::npos) {
384 std::istringstream iss(line);
386 double value = NAN, presigma = NAN;
390 if (presigma >= 0.0) {
395 LOGP(info,
"Parsed {} not fixed parameters from {}", labelToValue.size(), milleResPath);
399 std::map<int, std::vector<double>> injRB;
400 std::map<int, std::vector<std::vector<double>>> injMatrix;
401 if (!injectedJsonPath.empty()) {
402 std::ifstream injFile(injectedJsonPath);
403 if (injFile.is_open()) {
404 json injData = json::parse(injFile);
405 for (
const auto& item : injData) {
406 int id = item[
"id"].get<
int>();
407 if (item.contains(
"rigidBody")) {
408 injRB[
id] = item[
"rigidBody"].get<std::vector<double>>();
410 if (item.contains(
"matrix")) {
411 injMatrix[
id] = item[
"matrix"].get<std::vector<std::vector<double>>>();
414 LOGP(info,
"Loaded injected misalignment for {} sensors", injData.size());
416 LOGP(warn,
"Cannot open injected misalignment file: {}, writing absolute values", injectedJsonPath);
425 if ((!rb && !cal) || vol->
isPseudo()) {
435 if (rb && rb->nFreeDOFs()) {
437 json rbArr = json::array();
438 const auto& inj = injRB.contains(
id) ? injRB[
id] : std::vector<double>{};
439 for (
int i = 0;
i < rb->nDOFs(); ++
i) {
441 auto it = labelToValue.find(raw);
442 double fitted = it != labelToValue.end() ? it->second : 0.0;
443 double ref = i < static_cast<int>(inj.size()) ? inj[
i] : 0.0;
444 rbArr.push_back(fitted -
ref);
446 entry[
"rigidBody"] = rbArr;
453 int order = leg->
order();
455 const auto& inj = injMatrix.contains(
id) ? injMatrix[
id] : std::vector<std::vector<double>>{};
456 json matrix = json::array();
458 for (
int i = 0;
i <= order; ++
i) {
460 for (
int j = 0;
j <=
i; ++
j) {
461 uint32_t raw = calibLbl.raw(idx);
462 auto it = labelToValue.find(raw);
463 double fitted = it != labelToValue.end() ? it->second : 0.0;
464 double ref = (i < static_cast<int>(inj.size()) && j < static_cast<int>(inj[
i].
size())) ? inj[
i][
j] : 0.0;
465 row.push_back(fitted -
ref);
468 matrix.push_back(
row);
470 entry[
"matrix"] = matrix;
477 std::ofstream fout(outJsonPath);
478 if (!fout.is_open()) {
479 LOGP(fatal,
"Cannot open output file: {}", outJsonPath);
481 fout <<
output.dump(2) <<
'\n';
483 LOGP(info,
"Wrote millepede results to {}", outJsonPath);
General auxilliary methods.
Definition of the GeometryTGeo class.
bool isLeaf() const noexcept
void traverse(const std::function< void(AlignableVolume *)> &visitor)
DOFSet * getRigidBody() const
DOFSet * getCalib() const
void writeParameters(std::ostream &os) const
int getSensorId() const noexcept
virtual void defineMatrixT2L()
GlobalLabel getLabel() const noexcept
void finalise(uint8_t level=0)
bool isPseudo() const noexcept
bool isRoot() const noexcept
AlignableVolume * mParent
matrices
void setRigidBody(std::unique_ptr< DOFSet > rb)
virtual void defineMatrixL2G()
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)
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 bool sens() const noexcept
void add(uint32_t lab, double coeff)
auto getSize() const noexcept
void write(std::ostream &os) const
GLuint const GLchar * name
GLsizei const GLfloat * value
GLuint GLsizei const GLchar * label
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::array< uint16_t, 5 > pattern