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);
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);
373 dofSet->setAllFree(
false);
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);
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);
411 using json = nlohmann::json;
414 std::ifstream fin(milleResPath);
415 if (!fin.is_open()) {
416 LOGP(fatal,
"Cannot open millepede result file: {}", milleResPath);
418 std::map<uint32_t, double> labelToValue;
420 while (std::getline(fin, line)) {
421 if (line.empty() || line[0] ==
'!' || line[0] ==
'*') {
424 if (line.find(
"Parameter") != std::string::npos) {
427 std::istringstream iss(line);
429 double value = NAN, presigma = NAN;
433 if (presigma >= 0.0) {
438 LOGP(info,
"Parsed {} not fixed parameters from {}", labelToValue.size(), milleResPath);
442 std::map<int, std::vector<double>> injRB;
443 std::map<int, std::vector<std::vector<double>>> injMatrix;
445 std::map<int, double>
f;
446 std::map<int, double>
g;
447 std::map<std::pair<int, int>,
double>
h;
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>>();
459 if (item.contains(
"matrix")) {
460 injMatrix[
id] = item[
"matrix"].get<std::vector<std::vector<double>>>();
462 if (item.contains(
"inextensional")) {
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>();
470 if (inex.contains(
"g")) {
471 for (
auto& [
key,
val] : inex[
"g"].items()) {
472 ii.g[std::stoi(
key)] =
val.get<
double>();
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) {
481 ii.h[{std::stoi(
key.substr(0, sep)), std::stoi(
key.substr(sep + 1))}] =
val.get<
double>();
487 LOGP(info,
"Loaded injected misalignment for {} sensors", injData.size());
489 LOGP(warn,
"Cannot open injected misalignment file: {}, writing absolute values", injectedJsonPath);
498 if ((!rb && !cal) || vol->
isPseudo()) {
508 if (rb && rb->nFreeDOFs()) {
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) {
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);
519 entry[
"rigidBody"] = rbArr;
526 int order = leg->
order();
528 const auto& inj = injMatrix.contains(
id) ? injMatrix[
id] : std::vector<std::vector<double>>{};
529 json matrix = json::array();
531 for (
int i = 0;
i <= order; ++
i) {
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);
541 matrix.push_back(
row);
543 entry[
"matrix"] = matrix;
548 const auto& inj = injInex.contains(
id) ? injInex[
id] : InjInex{};
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;
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;
565 inexEntry[
"f"] = fObj;
566 inexEntry[
"g"] = gObj;
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;
577 inexEntry[
"h"] = hObj;
580 entry[
"inextensional"] = inexEntry;
587 std::ofstream fout(outJsonPath);
588 if (!fout.is_open()) {
589 LOGP(fatal,
"Cannot open output file: {}", outJsonPath);
591 fout <<
output.dump(2) <<
'\n';
593 LOGP(info,
"Wrote millepede results to {}", outJsonPath);