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);
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))
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))
200 for (
const auto&
c : mChildren) {
201 c->writeParameters(os);
207 os << std::string(static_cast<size_t>(indent * 2),
' ') << mSymName << (mLabel.
sens() ?
" (sens)" :
" (pasv)");
212 if (mRigidBody && mRigidBody->nFreeDOFs()) {
213 nFreeDofs += mRigidBody->nFreeDOFs();
215 for (
int i = 0;
i < mRigidBody->nDOFs(); ++
i) {
216 if (mRigidBody->isFree(
i)) {
217 os <<
" " << mRigidBody->dofName(
i) <<
"(" << mLabel.
raw(
i) <<
")";
222 if (mCalib && mCalib->nFreeDOFs()) {
223 nFreeDofs += mCalib->nFreeDOFs();
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) <<
")";
238 for (
const auto&
c : mChildren) {
239 c->writeTree(os, indent + 2);
245 using json = nlohmann::json;
246 std::ifstream
f(jsonPath);
248 LOGP(fatal,
"Cannot open DOF config file: {}", jsonPath);
250 auto data = json::parse(
f);
253 static const std::map<std::string, int> rbNameToIdx = {
254 {
"TX", 0}, {
"TY", 1}, {
"TZ", 2}, {
"RX", 3}, {
"RY", 4}, {
"RZ", 5}};
256 auto matchPattern = [](
const std::string&
pattern,
const std::string& sym) ->
bool {
257 if (fnmatch(
pattern.c_str(), sym.c_str(), 0) == 0) {
260 std::string prefixed =
"*" +
pattern;
261 return fnmatch(prefixed.c_str(), sym.c_str(), 0) == 0;
264 if (
data.is_object() &&
data.contains(
"defaults")) {
266 defRule[
"match"] =
"*";
267 rules.insert(rules.begin(), defRule);
275 for (
const auto& rule : rules) {
276 const auto pattern = rule[
"match"].get<std::string>();
277 if (!matchPattern(
pattern, sym)) {
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") {
287 }
else if (s ==
"fixed") {
288 auto dofSet = std::make_unique<RigidBodyDOFSet>();
289 dofSet->setAllFree(
false);
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);
302 }
else if (rb.is_object()) {
303 auto dofs = rb.value(
"dofs", std::string(
"all"));
304 bool fixed = rb.value(
"fixed",
false);
306 auto dofSet = std::make_unique<RigidBodyDOFSet>();
308 dofSet->setAllFree(
false);
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);
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);
333 dofSet->setAllFree(
false);
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()) {
343 for (
int k = 0; k < dofSet->nDOFs(); ++k) {
344 if (dofSet->dofName(k) == item.get<std::string>()) {
345 dofSet->setFree(k,
true);
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);
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);
370 dofSet->setAllFree(
false);
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);
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);
408 using json = nlohmann::json;
411 std::ifstream fin(milleResPath);
412 if (!fin.is_open()) {
413 LOGP(fatal,
"Cannot open millepede result file: {}", milleResPath);
415 std::map<uint32_t, double> labelToValue;
417 while (std::getline(fin, line)) {
418 if (line.empty() || line[0] ==
'!' || line[0] ==
'*') {
421 if (line.find(
"Parameter") != std::string::npos) {
424 std::istringstream iss(line);
426 double value = NAN, presigma = NAN;
430 if (presigma >= 0.0) {
435 LOGP(info,
"Parsed {} not fixed parameters from {}", labelToValue.size(), milleResPath);
439 std::map<int, std::vector<double>> injRB;
440 std::map<int, std::vector<std::vector<double>>> injMatrix;
442 std::map<int, std::array<double, 4>> modes;
446 std::map<int, InjInex> injInex;
447 if (!injectedJsonPath.empty()) {
448 std::ifstream injFile(injectedJsonPath);
449 if (injFile.is_open()) {
450 json injData = json::parse(injFile);
451 for (
const auto& item : injData) {
452 int id = item[
"id"].get<
int>();
453 if (item.contains(
"rigidBody")) {
454 injRB[
id] = item[
"rigidBody"].get<std::vector<double>>();
456 if (item.contains(
"matrix")) {
457 injMatrix[
id] = item[
"matrix"].get<std::vector<std::vector<double>>>();
459 if (item.contains(
"inextensional")) {
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>>();
467 if (inex.contains(
"alpha")) {
468 ii.alpha = inex[
"alpha"].get<
double>();
470 if (inex.contains(
"beta")) {
471 ii.beta = inex[
"beta"].get<
double>();
476 LOGP(info,
"Loaded injected misalignment for {} sensors", injData.size());
478 LOGP(warn,
"Cannot open injected misalignment file: {}, writing absolute values", injectedJsonPath);
487 if ((!rb && !cal) || vol->
isPseudo()) {
497 if (rb && rb->nFreeDOFs()) {
499 json rbArr = json::array();
500 const auto& inj = injRB.contains(
id) ? injRB[
id] : std::vector<double>{};
501 for (
int i = 0;
i < rb->nDOFs(); ++
i) {
503 auto it = labelToValue.find(raw);
504 double fitted = it != labelToValue.end() ? it->second : 0.0;
505 double ref = i < static_cast<int>(inj.size()) ? inj[
i] : 0.0;
506 rbArr.push_back(fitted -
ref);
508 entry[
"rigidBody"] = rbArr;
515 int order = leg->
order();
517 const auto& inj = injMatrix.contains(
id) ? injMatrix[
id] : std::vector<std::vector<double>>{};
518 json matrix = json::array();
520 for (
int i = 0;
i <= order; ++
i) {
522 for (
int j = 0;
j <=
i; ++
j) {
523 uint32_t raw = calibLbl.raw(idx);
524 auto it = labelToValue.find(raw);
525 double fitted = it != labelToValue.end() ? it->second : 0.0;
526 double ref = (i < static_cast<int>(inj.size()) && j < static_cast<int>(inj[
i].
size())) ? inj[
i][
j] : 0.0;
527 row.push_back(fitted -
ref);
530 matrix.push_back(
row);
532 entry[
"matrix"] = matrix;
538 const auto& inj = injInex.contains(
id) ? injInex[
id] : InjInex{};
541 json modesObj = json::object();
542 for (
int n = 2;
n <= maxN; ++
n) {
544 std::array<double, 4> injCoeffs = {0., 0., 0., 0.};
545 if (inj.modes.contains(
n)) {
546 injCoeffs = inj.modes.at(
n);
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]);
557 inexEntry[
"modes"] = modesObj;
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;
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;
569 entry[
"inextensional"] = inexEntry;
576 std::ofstream fout(outJsonPath);
577 if (!fout.is_open()) {
578 LOGP(fatal,
"Cannot open output file: {}", outJsonPath);
580 fout <<
output.dump(2) <<
'\n';
582 LOGP(info,
"Wrote millepede results to {}", outJsonPath);