2 // Class to take survey data and
3 // transform that to alignment objects.
7 #include "AliFMDSurveyToAlignObjs.h"
9 #include "AliSurveyPoint.h"
10 #include <TGraph2DErrors.h>
16 #include <TRotation.h>
17 #include <TGeoMatrix.h>
18 #include <TGeoManager.h>
19 #include <TGeoPhysicalNode.h>
20 #include "AliFMDGeometry.h"
22 //____________________________________________________________________
24 AliFMDSurveyToAlignObjs::GetUnitFactor() const
26 // Returns the conversion factor from the measured values to
28 if (!fSurveyObj) return 0;
29 TString units(fSurveyObj->GetUnits());
30 if (units.CompareTo("mm", TString::kIgnoreCase) == 0) return .1;
31 else if (units.CompareTo("cm", TString::kIgnoreCase) == 0) return 1.;
32 else if (units.CompareTo("m", TString::kIgnoreCase) == 0) return 100.;
36 //____________________________________________________________________
38 AliFMDSurveyToAlignObjs::GetPoint(const char* name,
40 TVector3& error) const
42 // Get named point. On return, point will contain the point
43 // coordinates in centimeters, and error will contain the
44 // meassurement errors in centimeters too. If no point is found,
45 // returns false, otherwise true.
46 if (!fSurveyPoints) return kFALSE;
48 Double_t unit = GetUnitFactor();
49 if (unit == 0) return kFALSE;
51 TObject* obj = fSurveyPoints->FindObject(name);
52 if (!obj) return kFALSE;
54 AliSurveyPoint* p = static_cast<AliSurveyPoint*>(obj);
55 point.SetXYZ(unit * p->GetX(),
58 error.SetXYZ(unit * p->GetPrecisionX(),
59 unit * p->GetPrecisionY(),
60 unit * p->GetPrecisionZ());
64 //____________________________________________________________________
66 AliFMDSurveyToAlignObjs::CalculatePlane(const TVector3& a,
74 // Calculate the plane translation and rotation from 3 survey points
80 // trans Translation vector
81 // rot Rotation matrix (direction cosines)
87 // Vector a->b, b->c, and normal to plane defined by these two
89 TVector3 ab(b-a), bc(c-a);
91 // Normal vector to the plane of the fiducial marks obtained
92 // as cross product of the two vectors on the plane d0^d1
93 TVector3 nn(ab.Cross(bc));
94 if (nn.Mag() < 1e-8) {
95 Info("CalculatePlane", "Normal vector is null vector");
99 // We express the plane in Hessian normal form.
103 // where n is the normalised normal vector given by
105 // n_x = a / l, n_y = b / l, n_z = c / l, p = d / l
107 // with l = sqrt(a^2+b^2+c^2) and a, b, c, and d are from the
108 // normal plane equation
110 // ax + by + cz + d = 0
113 TVector3 n(nn.Unit());
114 // Double_t p = - (n * a);
116 // The center of the square with the fiducial marks as the
117 // corners. The mid-point of one diagonal - md. Used to get the
118 // center of the surveyd box.
119 // TVector3 md(a + c);
121 //Info("CalculatePlane", "corner=(%8f,%8f,%8f)", c.X(),c.Y(),c.Z());
122 //Info("CalculatePlane", "corner=(%8f,%8f,%8f)", b.X(),b.Y(),b.Z());
125 //Info("CalculatePlane", "mid=(%8f,%8f,%8f)", md.X(),md.Y(),md.Z());
126 //Info("CalculatePlane", "normal=(%8f,%8f,%8f)", n.X(),n.Y(),n.Z());
128 // The center of the box.
129 TVector3 orig(md - depth * n);
130 // Info("CalculatePlane", "orig=(%8f,%8f,%8f)", orig.X(),orig.Y(),orig.Z());
134 //Info("CalculatePlane", "trans=(%8f,%8f,%8f)", trans[0],trans[1],trans[2]);
136 // Normalize the spanning vectors
137 TVector3 uab(ab.Unit());
138 TVector3 ubc(bc.Unit());
140 for (size_t i = 0; i < 3; i++) {
141 rot[i * 3 + 0] = ubc[i];
142 rot[i * 3 + 1] = uab[i];
143 // rot[i * 3 + 0] = uab[i];
144 // rot[i * 3 + 1] = ubc[i];
145 rot[i * 3 + 2] = n[i];
150 //____________________________________________________________________
152 AliFMDSurveyToAlignObjs::FitPlane(const TObjArray& points,
153 const TObjArray& errors,
154 Double_t /* depth */,
159 // Calculate the plane rotation and translation by doing a fit of
160 // the plane equation to the surveyed points. At least 4 points
161 // must be passed in the @a points array with corresponding errors
162 // in the array @a errors. The arrays are assumed to contain
166 // points Array surveyed positions
167 // errors Array of errors corresponding to @a points
168 // depth Survey targets depth (perpendicular to the plane)
169 // trans On return, translation of the plane
170 // rot On return, rotation (direction cosines) of the plane
173 // @c true on success, @c false otherwise
176 Int_t nPoints = points.GetEntries();
178 AliError(Form("Cannot fit a plane equation to less than 4 survey points, "
179 "got only %d", nPoints));
184 // Loop and fill graph
185 for (int i = 0; i < nPoints; i++) {
186 TVector3* p = static_cast<TVector3*>(points.At(i));
187 TVector3* e = static_cast<TVector3*>(errors.At(i));
189 if (!p || !e) continue;
191 g.SetPoint(i, p->X(), p->Y(), p->Z());
192 g.SetPointError(i, e->X(), e->Y(), e->Z());
196 // Check that we have enough points
198 AliError(Form("Only got %d survey points - no good for plane fit",
203 // Next, declare fitting function and fit to graph.
204 // Fit to the plane equation:
206 // ax + by + cz + d = 0
210 // z = - ax/c - by/c - d/c
212 TF2 f("plane", "-[0]*x-[1]*y-[2]",
213 g.GetXmin(), g.GetXmax(), g.GetYmin(), g.GetYmax());
216 // Now, extract the normal and offset
217 TVector3 nv(f.GetParameter(0), f.GetParameter(1), 1);
218 TVector3 n(nv.Unit());
219 Double_t p = -f.GetParameter(2);
221 // Create two vectors spanning the plane
222 TVector3 a(1, 0, f.Eval(1, 0)-p);
223 TVector3 b(0, -1, f.Eval(0, -1)-p);
224 TVector3 ua(a.Unit());
225 TVector3 ub(b.Unit());
226 // Double_t angAb = ua.Angle(ub);
227 // PrintVector("ua: ", ua);
228 // PrintVector("ub: ", ub);
229 // std::cout << "Angle: " << angAb * 180 / TMath::Pi() << std::endl;
231 for (size_t i = 0; i < 3; i++) {
232 rot[i * 3 + 0] = ua[i];
233 rot[i * 3 + 1] = ub[i];
234 rot[i * 3 + 2] = n[i];
237 // The intersection of the plane is given by (0, 0, -d/c)
246 //____________________________________________________________________
248 AliFMDSurveyToAlignObjs::MakeDelta(const char* path,
250 const Double_t* trans,
251 TGeoHMatrix& delta) const
254 // Create a delta transform from a global rotation matrix and
258 // path Path of element to transform.
259 // rot Rotation matrix (direction cosines)
261 // delta On return, the delta transform
266 if (!gGeoManager) return kFALSE;
267 if (!gGeoManager->cd(path)) return kFALSE;
270 TGeoMatrix* global = gGeoManager->GetCurrentMatrix();
272 PrintRotation(Form("%s rot:", global->GetName()),global->GetRotationMatrix());
273 PrintVector(Form("%s trans:", global->GetName()),global->GetTranslation());
276 return MakeDelta(global, rot, trans, delta);
279 //____________________________________________________________________
281 AliFMDSurveyToAlignObjs::MakeDelta(const TGeoMatrix* global,
283 const Double_t* trans,
284 TGeoHMatrix& delta) const
287 // Create a delta transform from a global rotation matrix and
291 // global Global matrix of element to transform.
292 // rot Rotation matrix (direction cosines)
294 // delta On return, the delta transform
299 TGeoHMatrix* geoM = new TGeoHMatrix;
300 geoM->SetTranslation(trans);
301 geoM->SetRotation(rot);
302 // Info("MakeDelta", "The HMatrix from survey");
304 // Info("MakeDelta", "The global matrix");
307 delta = global->Inverse();
308 // Info("MakeDelta", "The inverse global matrix");
310 delta.MultiplyLeft(geoM);
311 // Info("MakeDelta", "The delta matrix");
317 Double_t getFMD1Offset()
319 static Double_t off = 0;
323 if (off != 0) return off;
325 const char* lidN = "FMD1_lid_mat0";
326 TGeoMatrix* lidM = static_cast<TGeoMatrix*>(gGeoManager->GetListOfMatrices()
329 Error("getFMD1Offset", "Couldn't find FMD1 lid transformation %s", lidN);
333 const Double_t* lidT = lidM->GetTranslation();
334 Double_t lidZ = lidT[2];
342 //____________________________________________________________________
344 AliFMDSurveyToAlignObjs::GetFMD1Plane(Double_t* rot, Double_t* trans) const
347 // Get the FMD1 plane from the survey points
350 // rot Rotation matrix (direction cosines)
354 // @c true on success, @c false otherwise.
357 // The possile survey points
358 TVector3 icb, ict, ocb, oct, eicb, eict, eocb, eoct;
360 if (!GetPoint("V0L_ICB", icb, eicb)) missing++;
361 if (!GetPoint("V0L_ICT", ict, eict)) missing++;
362 if (!GetPoint("V0L_OCB", ocb, eocb)) missing++;
363 if (!GetPoint("V0L_OCT", oct, eoct)) missing++;
365 // Check that we have enough points
367 AliWarning(Form("Only got %d survey points - no good for FMD1 plane",
374 points.Add(&icb); errors.Add(&eicb);
375 points.Add(&ict); errors.Add(&eict);
376 points.Add(&oct); errors.Add(&eoct);
377 points.Add(&ocb); errors.Add(&eocb);
379 Bool_t ret = FitPlane(points, errors, 0, trans, rot);
381 Warning("GetFMD1Plane", "fit to plane failed");
383 for (Int_t i = 0; i < 4; i++) {
384 TVector3* v = static_cast<TVector3*>(points.At(i));
385 TVector3* e = static_cast<TVector3*>(errors.At(i));
386 Info("GetFMD1Plane", "p%d=(%8f,%8f,%8f)+/-(%8f,%8f,%8f)",
387 i, v->X(), v->Y(), v->Z(), e->X(), e->Y(), e->Z());
390 Double_t off = getFMD1Offset();
391 Info("GetFMD1Plane", "Lid offset is %f", off);
393 // if (!CalculatePlane(ocb, icb, ict, off, trans, rot)) return kFALSE;
394 // Bool_t ret = CalculatePlane(ocb, icb, oct, off, trans, rot);
395 Bool_t ret = CalculatePlane(oct, ocb, ict, off, trans, rot);
397 PrintRotation("FMD1 rotation:", rot);
398 PrintVector("FMD1 translation:", trans);
403 //____________________________________________________________________
405 AliFMDSurveyToAlignObjs::DoFMD1()
408 // Do the FMD1 analysis. We have 4 survey targets on V0-A on the
411 // - V0A_ICT In-side, C-side, top.
412 // - V0A_ICB In-side, C-side, bottom.
413 // - V0A_OCT Out-side, C-side, top.
414 // - V0A_OCB Out-side, C-side, bottom.
416 // These 4 survey targets sit 3.3mm over the V0-A C-side surface, or
417 // 3.3mm over the back surface of FMD1.
419 // Since these are really sitting on a plane, we can use the method
420 // proposed by the CORE offline.
423 // @c true on success, @c false otherwise.
427 Double_t rot[9], trans[3];
428 if (!GetFMD1Plane(rot, trans)) return kFALSE;
429 // const char* path = "/ALIC_1/F1MT_1/FMD1_lid_0";
432 // TGeoHMatrix delta;
433 Double_t gRot[9], gTrans[3];
434 TVector3 ocb(-127, -220, 324.67);
435 TVector3 oct(-127, +220, 324.67);
436 TVector3 icb(+127, -220, 324.67);
437 TVector3 ict(+127, +220, 324.67);
438 if (!CalculatePlane(ocb, icb, oct, 0, gTrans, gRot)) {
439 Warning("DoFMD1", "Failed to make reference plane");
442 PrintRotation("FMD1 ref rotation:", gRot);
443 PrintVector("FMD1 ref translation:", gTrans);
444 TGeoRotation ggRot; ggRot.SetMatrix(gRot);
445 TGeoCombiTrans global(gTrans[0], gTrans[1], gTrans[2], &ggRot);
447 Double_t off = getFMD1Offset();
448 Info("DoFMD1", "Lid offset is %f", off);
450 TGeoTranslation global(0,0,324.670-off);
451 if (!MakeDelta(&global, rot, trans, fFMD1Delta))
454 // PrintRotation("FMD1 delta rotation:", fFMD1Delta.GetRotationMatrix());
455 // PrintVector("FMD1 delta translation:", fFMD1Delta.GetTranslation());
460 //____________________________________________________________________
462 AliFMDSurveyToAlignObjs::GetFMD2Plane(Double_t* rot, Double_t* trans) const
465 // Get the surveyed plane corresponding to the backside of FMD2.
466 // The plane is done as a best fit of the plane equation to at least
467 // 4 of the available survey points.
470 // rot Rotation matrix (direction cosines)
471 // trans Translation vector.
474 // @c true on success, @c false otherwise
477 // The possible survey points
478 const char* names[] = { "FMD2_ITOP", "FMD2_OTOP",
479 "FMD2_IBOTM", "FMD2_OBOTM",
480 "FMD2_IBOT", "FMD2_OBOT",
482 const char** name = names;
487 // Loop and fill graph
491 if (!GetPoint(*name, p, e)) {
498 Warning("GetFMD2plane", "Setting error on %d, %s to 0.4", i, *name);
499 e.SetXYZ(0.4, 0.4, 0.4); // OBOT
501 points.Add(new TVector3(p));
502 errors.Add(new TVector3(e));
506 if (points.GetEntries() < 4) {
507 AliWarning(Form("Only got %d survey points - no good for FMD2 plane",
508 points.GetEntries()));
512 return FitPlane(points, errors, 0, trans, rot);
515 #define M(I,J) rot[(J-1) * 3 + (I-1)]
516 //____________________________________________________________________
518 AliFMDSurveyToAlignObjs::DoFMD2()
521 // Do the FMD2 calculations. We have 6 survey points of which only
522 // 5 are normally surveyed. These are all sittings
524 // - FMD2_ITOP - In-side, top
525 // - FMD2_IBOTM - In-side, middle bottom
526 // - FMD2_IBOT - In-side, bottom
527 // - FMD2_OTOP - Out-side, top
528 // - FMD2_OBOTM - Out-side, middle bottom
529 // - FMD2_OBOT - Out-side, bottom
531 // The nominal coordinates of these retro-fitted survey stickers
532 // isn't known. Also, these stickers are put on a thin (0.3mm
533 // thick) carbon cover which flexes quite easily. This means, that
534 // to rotations and xy-translation obtained from the survey data
535 // cannot be used, and left is only the z-translation.
537 // Further more, since FMD2 to is attached to the ITS SPD thermal
538 // screen, it is questionable if the FMD2 survey will ever be used.
541 // @c true on success, @c false otherwise.
545 Double_t rot[9], trans[3];
546 if (!GetFMD2Plane(rot, trans)) return kFALSE;
547 PrintRotation("FMD2 rotation:", rot);
548 PrintVector("FMD2 translation:", trans);
551 for (int i = 0; i < 3; i++) {
552 for (int j = 0; j < 3; j++) {
553 rot[i*3+j] = (i == j ? 1 : 0);
557 trans[0] = trans[1] = 0;
559 // PrintRotation("FMD2 rotation:", rot);
560 // PrintVector("FMD2 translation:", trans);
562 // TGeoHMatrix delta;
563 if (!MakeDelta("/ALIC_1/F2MT_2/FMD2_support_0/FMD2_back_cover_2",
564 rot, trans, fFMD2Delta)) return kFALSE;
566 // PrintRotation("FMD2 delta rotation:", fFMD2Delta.GetRotationMatrix());
567 // PrintVector("FMD2 delta translation:", fFMD2Delta.GetTranslation());
572 //____________________________________________________________________
574 AliFMDSurveyToAlignObjs::Run()
581 AliFMDGeometry* geom = AliFMDGeometry::Instance();
583 geom->InitTransformations();
589 //____________________________________________________________________
591 AliFMDSurveyToAlignObjs::Run(const char** files)
598 AliFMDGeometry* geom = AliFMDGeometry::Instance();
600 geom->InitTransformations();
602 const char** file = files;
604 if ((*file)[0] == '\0') {
605 Warning("Run", "no file specified");
609 if (!LoadSurveyFromLocalFile(*file)) {
610 Warning("Run", "Failed to load %s", *file);
614 TString sDet(fSurveyObj->GetDetector());
615 Int_t d = Int_t(sDet[sDet.Length()-1] - '0');
616 Info("Run", "Making alignment for %s (%d)", sDet.Data(), d);
619 case 1: ret = DoFMD1(); break;
620 case 2: ret = DoFMD2(); break;
622 Warning("Run", "Do not know how to deal with %s", sDet.Data());
626 Warning("Run", "Calculation for %s failed", sDet.Data());
631 GetAlignObjArray()->Print();
632 FillDefaultAlignObjs();
635 //____________________________________________________________________
637 AliFMDSurveyToAlignObjs::CreateDefaultAlignObj(const TString& path,
640 Int_t nAlign = fAlignObjArray->GetEntries();
641 AliAlignObjParams* obj =
642 new ((*fAlignObjArray)[nAlign]) AliAlignObjParams(path.Data(),
643 id,0,0,0,0,0,0,kTRUE);
645 AliError(Form("Failed to create alignment object for %s", path.Data()));
648 if (!obj->SetLocalPars(0, 0, 0, 0, 0, 0)) {
649 AliError(Form("Failed to set local transforms on %s", path.Data()));
655 //____________________________________________________________________
657 AliFMDSurveyToAlignObjs::FindAlignObj(const TString& path) const
659 AliAlignObjParams* p = 0;
660 for (int i = 0; i < fAlignObjArray->GetEntries(); i++) {
661 p = static_cast<AliAlignObjParams*>(fAlignObjArray->At(i));
662 if (path.EqualTo(p->GetSymName())) return p;
667 //____________________________________________________________________
669 AliFMDSurveyToAlignObjs::FillDefaultAlignObjs()
671 for (int d = 1; d <= 3; d++) {
672 const char sides[] = { 'T', 'B', 0 };
673 const char* side = sides;
675 TString path = TString::Format("FMD/FMD%d_%c", d, *side);
676 if (!FindAlignObj(path)) CreateDefaultAlignObj(path, 0);
678 const char halves[] = { 'I', d == 1 ? '\0' : 'O', 0 };
679 const char* half = halves;
681 int nsec = *half == 'I' ? 10 : 20;
682 int start = *side == 'T' ? 0 : nsec/2;
683 int end = *side == 'T' ? nsec/2 : nsec;
684 for (int s=start; s < end; s++) {
685 path = TString::Format("FMD/FMD%d_%c/FMD%c_%02d",
687 CreateDefaultAlignObj(path, 0);
698 //____________________________________________________________________
700 AliFMDSurveyToAlignObjs::CreateAlignObjs()
704 // Method to create the alignment objects
707 // @c true on success, @c false otherwise
709 TClonesArray& array = *fAlignObjArray;
710 Int_t n = array.GetEntriesFast();
712 if (!fFMD1Delta.IsIdentity()) {
713 new (array[n++]) AliAlignObjParams("FMD/FMD1_T", 0, fFMD1Delta, kTRUE);
714 new (array[n++]) AliAlignObjParams("FMD/FMD1_B", 0, fFMD1Delta, kTRUE);
716 if (!fFMD2Delta.IsIdentity()) {
717 new (array[n++]) AliAlignObjParams("FMD/FMD2_T", 0, fFMD2Delta, kTRUE);
718 new (array[n++]) AliAlignObjParams("FMD/FMD2_B", 0, fFMD2Delta, kTRUE);
725 //____________________________________________________________________
727 AliFMDSurveyToAlignObjs::PrintVector(const char* text, const TVector3& v)
730 // Service member function to print a vector
736 Double_t va[] = { v.X(), v.Y(), v.Z() };
737 PrintVector(text, va);
739 //____________________________________________________________________
741 AliFMDSurveyToAlignObjs::PrintVector(const char* text, const Double_t* v)
744 // Service member function to print a vector
748 // v Vector (array of 3 doubles)
751 << std::setw(15) << v[0]
752 << std::setw(15) << v[1]
753 << std::setw(15) << v[2]
758 //____________________________________________________________________
760 AliFMDSurveyToAlignObjs::PrintRotation(const char* text, const Double_t* rot)
763 // Service member function to print a rotation matrix
767 // v Matrix (array of 9 doubles)
770 std::cout << text << std::endl;
771 for (size_t i = 0; i < 3; i++) {
772 for (size_t j = 0; j < 3; j++)
773 std::cout << std::setw(15) << rot[i * 3 + j];
774 std::cout << std::endl;
778 //____________________________________________________________________