// $Id: StFtpcConfMapper.cc,v 1.4 2000/06/07 10:04:18 oldi Exp $
// $Log: StFtpcConfMapper.cc,v $
// Revision 1.4 2000/06/07 10:04:18 oldi
// Changed 0 pointers to NULL pointers.
// Constructor changed. Setting of mNumRowSegment is not possible anymore to
// avoid that it is set to a value != 20. This was necessary to avoid the
// exit(-20) function call.
// New funtion CalcChiSquared(StFtpcTrack *track, StFtpcConfMapPoint *point,
// Double_t *chi2) introduced.
// StraightLineFit(StFtpcTrack *track, Double_t *a, Int_t n) now calculates the
// chi squared. Therefore the pointer to 'a' has to be a pointer to an 6-dim array
// (a[6]). Also the code of this function was modified to take care of the first
// point which is not allowd to be used in the circle fit (to avoid infinities).
// Introduction of new function CreateTrack() to cleanup ClusterLoop().
// Introduction of new function TrackLoop() and ExtendTrack() to get cleaner
// tracks.
// Cleanup.
//
// Revision 1.3 2000/05/12 12:59:12 oldi
// removed delete operator for mSegment in StFtpcConfMapper (mSegment was deleted twice),
// add two new constructors for StFtpcTracker to be able to refit already existing tracks,
// minor cosmetics
//
// Revision 1.2 2000/05/11 15:14:41 oldi
// Changed class names *Hit.* due to already existing class StFtpcHit.cxx in StEvent
//
// Revision 1.1 2000/05/10 13:39:09 oldi
// Initial version of StFtpcTrackMaker
//
//----------Author: Markus D. Oldenburg
//----------Last Modified: 07.06.2000
//----------Copyright: © MDO Production 1999
#include "StFtpcConfMapper.hh"
#include "StFtpcConfMapPoint.hh"
#include "StFtpcTrack.hh"
#include "StFormulary.hh"
#include "MIntArray.h"
#include "TMath.h"
#include "TBenchmark.h"
#include "TCanvas.h"
#include "TH1.h"
#include "TH2.h"
#include "TH3.h"
#include "TPolyMarker.h"
#include "TFile.h"
#include "TLine.h"
#include "TGraph.h"
#include "TMarker.h"
#include "TPolyLine3D.h"
#include "TPolyMarker3D.h"
#include "TTUBE.h"
#include "TBRIK.h"
#include <assert.h>
////////////////////////////////////////////////////////////////////////////////////
// //
// StFtpcConfMapper class - tracking class to do tracking with conformal mapping. //
// //
////////////////////////////////////////////////////////////////////////////////////
ClassImp(StFtpcConfMapper)
StFtpcConfMapper::StFtpcConfMapper()
{
// Default constructor.
mBench = NULL;
mHit = NULL;
mVolume = NULL;
mSegment = NULL;
mVertexConstraint = (Bool_t)true;
}
StFtpcConfMapper::StFtpcConfMapper(St_fcl_fppoint *const fcl_fppoint, Double_t vertexPos[3], Bool_t bench,
Int_t phi_segments, Int_t eta_segments)
: StFtpcTracker(fcl_fppoint, vertexPos)
{
// Constructor.
mNumRowSegment = 20; // The number of rows has to be fixed to 20 (because this is the number of rows in both Ftpc's)!
mNumPhiSegment = phi_segments;
mNumEtaSegment = eta_segments;
mBounds = mNumRowSegment * mNumPhiSegment * mNumEtaSegment;
mMaxFtpcRow = mNumRowSegment/2;
if (bench) {
mBench = new TBenchmark();
mBench->Start("init");
}
mMergedTracks = 0;
mMergedTracklets = 0;
mMergedSplits = 0;
mDiffHits = 0;
mDiffHitsStill = 0;
mLengthFitNaN = 0;
Int_t n_clusters = fcl_fppoint->GetNRows(); // number of clusters
mClustersUnused = n_clusters;
fcl_fppoint_st *point_st = fcl_fppoint->GetTable(); // pointer to first cluster structure
mHit = new TClonesArray("StFtpcConfMapPoint", n_clusters); // create TClonesArray
TClonesArray &hit = *mHit;
for (Int_t i = 0; i < n_clusters; i++) {
new(hit[i]) StFtpcConfMapPoint(point_st++, mVertex);
((StFtpcConfMapPoint *)mHit->At(i))->SetHitNumber(i);
}
mVolume = new TObjArray(mBounds, 0); // create ObjArray for volume cells (of size bounds)
for (Int_t i = 0; i < mBounds; i++) {
mSegment = new TObjArray(0, 0); // Fill ObjArray with empty ObjArrays
mVolume->AddLast(mSegment);
}
StFtpcConfMapPoint *h;
for (Int_t i = 0; i < mHit->GetEntriesFast(); i++) {
h = (StFtpcConfMapPoint *)mHit->At(i);
((TObjArray *)mVolume->At(GetSegm(GetRowSegm(h), GetPhiSegm(h), GetEtaSegm(h))))->AddLast(h);
}
if (mBench) {
mBench->Stop("init");
cout << "Setup finished (" << mBench->GetCpuTime("init") << " s)." << endl;
}
}
StFtpcConfMapper::~StFtpcConfMapper()
{
// Destructor.
if (mVolume) {
mVolume->Delete();
delete mVolume;
}
if (mHit) {
mHit->Delete();
delete mHit;
}
if (mBench) {
delete mBench;
}
}
void StFtpcConfMapper::MainVertexTracking()
{
// Tracking with vertex constraint.
if (mBench) {
mBench->Start("main_vertex");
}
SetVertexConstraint(true);
ClusterLoop();
TrackLoop();
if (mBench) {
mBench->Stop("main_vertex");
cout << "Main vertex tracking finished (" << mBench->GetCpuTime("main_vertex") << " s)." << endl;
}
return;
}
void StFtpcConfMapper::FreeTracking()
{
// Tracking without vertex constraint.
if (mBench) {
mBench->Start("non_vertex");
}
SetVertexConstraint(false);
ClusterLoop();
if (mBench) {
mBench->Stop("non_vertex");
cout << "Non vertex tracking finished (" << mBench->GetCpuTime("non_vertex") << " s)." << endl;
}
return;
}
void StFtpcConfMapper::TwoCycleTracking()
{
// Tracking in 2 cycles:
// 1st cycle: tracking with vertex constraint
// 2nd cycle: without vertex constraint (of remaining clusters)
MainVertexTracking();
FreeTracking();
return;
}
void StFtpcConfMapper::Settings(Int_t trackletlength1, Int_t trackletlength2, Int_t tracklength1, Int_t tracklength2, Int_t rowscopetracklet1, Int_t rowscopetracklet2, Int_t rowscopetrack1, Int_t rowscopetrack2, Int_t phiscope1, Int_t phiscope2, Int_t etascope1, Int_t etascope2)
{
// Sets all settings of the tracker.
//
// This is the order of settings given to this function:
//
// - number of points to perform 'tracklet' search for main vertex tracks
// - number of points to perform 'tracklet' search for non vertex tracks
// These two mean no fitting but just looking for the nearest point
// in the direction to the main vertex (also for non vertex tracks!).
//
// - minimum number of points on a main vertex track
// - minimum number of points on a non vertex track
// These remove tracks and release points again already during the tracking
// if the tracker hasn't found enough hits on the track.
//
// - number of row segments to look on both sides for main vertex tracklets
// - number of row segments to look on both sides for non vertex tracklets
// - number of row segments to look on both sides for main vertex tracks
// - number of row segments to look on both sides for non vertex tracks
// These should be set to 1 for tracklets and to a value not less than 1 for track.
// Otherwise (if the value is set to 0) the tracker looks for the next point
// only in the same row. It should be set to a value higher than 1 if you
// want to be able to miss a cluster (because it is not there) but still
// to extend the track. This is (may be) not a good idea for tracklets.
// The tracker will extend the search in the next to the following padrow only
// if it has not found a cluster in the following row.
//
// - number of phi segments to look on both sides for main vertex tracks
// - number of phi segments to look on both sides for non vertex tracks
// These values have the same meaning as the values above for the row segemnts
// but now for the azimuth angle phi.
// The diffences are that the search is performed in any case over all ofthese
// segemnts (not only if in the nearest segments nothing was found) and that the
// reason to be able to set these values differential for main and non vertex tracks
// is the fact that non vertex tracks may be bent more than the high momentum
// main vertex tracks.
//
// - number of eta segments to look on both sides for main vertex tracks
// - number of eta segments to look on both sides for non vertex tracks
// Same as for the phi segments (above but now in eta (pseudorapidity) space).
SetTrackletLength(trackletlength1, trackletlength2);
SetRowScopeTracklet(rowscopetracklet1, rowscopetracklet2);
SetRowScopeTrack(rowscopetrack1, rowscopetrack2);
SetPhiScope(phiscope1, phiscope2);
SetEtaScope(etascope1, etascope2);
SetMinPoints(tracklength1, tracklength2);
}
void StFtpcConfMapper::Settings(Int_t trackletlength, Int_t tracklength, Int_t rowscopetracklet, Int_t rowscopetrack, Int_t phiscope, Int_t etascope)
{
// Sets settings for the given vertex constraint.
// See above for details on the settings.
SetTrackletLength(trackletlength, mVertexConstraint);
SetRowScopeTracklet(rowscopetracklet, mVertexConstraint);
SetRowScopeTrack(rowscopetrack, mVertexConstraint);
SetPhiScope(phiscope, mVertexConstraint);
SetEtaScope(etascope, mVertexConstraint);
SetMinPoints(tracklength, mVertexConstraint);
}
void StFtpcConfMapper::MainVertexSettings(Int_t trackletlength, Int_t tracklength, Int_t rowscopetracklet, Int_t rowscopetrack, Int_t phiscope, Int_t etascope)
{
// Sets settings for vertex constraint on.
// See above for details on the settings.
SetTrackletLength(trackletlength, (Bool_t) true);
SetRowScopeTracklet(rowscopetracklet, (Bool_t) true);
SetRowScopeTrack(rowscopetrack, (Bool_t) true);
SetPhiScope(phiscope, (Bool_t) true);
SetEtaScope(etascope, (Bool_t) true);
SetMinPoints(tracklength, (Bool_t) true);
}
void StFtpcConfMapper::NonVertexSettings(Int_t trackletlength, Int_t tracklength, Int_t rowscopetracklet, Int_t rowscopetrack, Int_t phiscope, Int_t etascope)
{
// Sets settings for vertex constraint off.
//
// See above for details on the settings.
SetTrackletLength(trackletlength, (Bool_t) false);
SetRowScopeTracklet(rowscopetracklet, (Bool_t) false);
SetRowScopeTrack(rowscopetrack, (Bool_t) false);
SetPhiScope(phiscope, (Bool_t) false);
SetEtaScope(etascope, (Bool_t) false);
SetMinPoints(tracklength, (Bool_t) false);
}
void StFtpcConfMapper::SetCuts(Double_t maxangletracklet1, Double_t maxangletracklet2, Double_t maxangletrack1, Double_t maxangletrack2, Double_t maxcircletrack1, Double_t maxcircletrack2, Double_t maxlengthtrack1, Double_t maxlengthtrack2)
{
// Sets all cuts for the tracking.
//
// This is the order of settings given to this function:
//
// - maximum angle of main vertex tracklets
// - maximum angle of non vertex tracklets
// - maximum angle of main vertex tracks
// - maximum angle of non vertex tracks
// - maximal distance from circle fit for main vertex tracks
// - maximal distance from circle fit for non vertex tracks
// - maximal distance from length fit for main vertex tracks
// - maximal distance from length fit for non vertex tracks
SetMaxAngleTracklet(maxangletracklet1, maxangletracklet2);
SetMaxAngleTrack(maxangletrack1, maxangletrack2);
SetMaxCircleDistTrack(maxcircletrack1, maxcircletrack2);
SetMaxLengthDistTrack(maxlengthtrack1, maxlengthtrack2);
}
void StFtpcConfMapper::SetCuts(Double_t maxangletracklet, Double_t maxangletrack, Double_t maxcircletrack, Double_t maxlengthtrack)
{
// Sets cuts for vertex constraint on or off.
// See above for details on the cuts.
SetMaxAngleTracklet(maxangletracklet, mVertexConstraint);
SetMaxAngleTrack(maxangletrack, mVertexConstraint);
SetMaxCircleDistTrack(maxcircletrack, mVertexConstraint);
SetMaxLengthDistTrack(maxlengthtrack, mVertexConstraint);
}
void StFtpcConfMapper::SetTrackCuts(Double_t maxangle, Double_t maxcircletrack, Double_t maxlengthtrack, Bool_t vertex_constraint)
{
// Sets cuts of tracks for the given vertex constraint.
// See above for details on the cuts.
SetMaxAngleTrack(maxangle, vertex_constraint);
SetMaxCircleDistTrack(maxcircletrack, vertex_constraint);
SetMaxLengthDistTrack(maxlengthtrack, vertex_constraint);
}
void StFtpcConfMapper::SetTrackletCuts(Double_t maxangle, Bool_t vertex_constraint)
{
// Sets cuts of tracklets for the given vertex constraint.
// See above for details on the cuts.
SetMaxAngleTracklet(maxangle, vertex_constraint);
}
Int_t StFtpcConfMapper::GetRowSegm(StFtpcConfMapPoint *hit)
{
// Returns number of pad segment of a specific hit.
return hit->GetPadRow() - 1; // fPadRow (1-20) already segmented, only offset substraction
}
Int_t StFtpcConfMapper::GetPhiSegm(StFtpcConfMapPoint *hit)
{
// Returns number of phi segment of a specific hit.
return (Int_t)(hit->GetPhi() * mNumPhiSegment / (2.*TMath::Pi())); // fPhi has no offset but needs to be segmented (this is done by type conversion to Int_t)
}
Int_t StFtpcConfMapper::GetEtaSegm(StFtpcConfMapPoint *hit)
{
// Returns number of eta segment of a specific hit.
Double_t eta;
Int_t eta_segm;
// Short explanation of the following two lines of code:
// The FTPC are placed in a distance to the point of origin between 162.75 and 256.45 cm.
// Their inner radius is 8, the outer one 30 cm. This means they are seen from (0, 0, 0)
// under an angle between 1.79 and 10.44 degrees. If the main vertex is shifted about -/+50 cm
// they are seen between 1.50/2.22 and 8.03/14.90 degrees. This translates to limits in pseudorapidity
// between 4.339/3.944 and 2.657/2.034. The maximal possible values were chosen.
Double_t eta_min = 2.0; // minimal possible eta value
Double_t eta_max = 4.4; // maximal possible eta value
if ((eta = hit->GetEta()) > 0.) { // positive values
eta_segm = (Int_t)((eta-eta_min) * mNumEtaSegment/(eta_max-eta_min) /2.); // Only use n_eta_segm/2. bins because of negative eta values.
}
else { // negative eta values
eta_segm = (Int_t)((-eta-eta_min) * mNumEtaSegment/(eta_max-eta_min) /2. + mNumEtaSegment/2.);
}
return eta_segm;
}
Int_t StFtpcConfMapper::GetSegm(Int_t row_segm, Int_t phi_segm, Int_t eta_segm)
{
// Calculates the volume segment number from the segmented volumes (segm = segm(pad,phi,eta)).
return row_segm * (mNumPhiSegment * mNumEtaSegment) + phi_segm * (mNumEtaSegment) + eta_segm;
}
Int_t StFtpcConfMapper::GetRowSegm(Int_t segm)
{
// Returns number of pad segment of a specifiv segment.
return (segm - GetEtaSegm(segm) - GetPhiSegm(segm)) / (mNumPhiSegment * mNumEtaSegment);
}
Int_t StFtpcConfMapper::GetPhiSegm(Int_t segm)
{
// Returns number of phi segment of a specifiv segment.
return (segm - GetEtaSegm(segm)) % (mNumPhiSegment * mNumEtaSegment) / (mNumEtaSegment);
}
Int_t StFtpcConfMapper::GetEtaSegm(Int_t segm)
{
// Returns number of eta segment of a specifiv segment.
return (segm % (mNumPhiSegment * mNumEtaSegment)) % (mNumEtaSegment);
}
Double_t const StFtpcConfMapper::CalcDistance(const StFtpcConfMapPoint *hit1, const StFtpcConfMapPoint *hit2)
{
// Returns the distance of two given clusters. The distance in this respect (conformal mapping)
// is defined in the paper "A Fast track pattern recognition" by Pablo Yepes, NIM A 380 (1996) 585-585.
Double_t phi_diff = TMath::Abs( hit1->GetPhi() - hit2->GetPhi() );
if (phi_diff > TMath::Pi()) phi_diff = 2*TMath::Pi() - phi_diff;
return TMath::Abs(hit1->GetPadRow() - hit2->GetPadRow()) * (phi_diff + TMath::Abs( hit1->GetEta() - hit2->GetEta() ));
}
Double_t const StFtpcConfMapper::CalcDistance(const StFtpcConfMapPoint *hit, Double_t *coeff)
{
// Returns the distance of a point to a straight line.
// The point is given by the to conformal coordinates of a cluster and the
// straight line is given by its to coefficients: y = coeff[0]*x + coeff[1].
Double_t x = (coeff[0] / (1 + coeff[0]*coeff[0])) * (1/coeff[0] * hit->GetXprime() + hit->GetYprime() - coeff[1]);
return TMath::Sqrt(TMath::Power(x - hit->GetXprime(), 2) + TMath::Power(coeff[0]*x + coeff[1] - hit->GetYprime(), 2));
}
Bool_t const StFtpcConfMapper::VerifyCuts(const StFtpcConfMapPoint *lasttrackhit, const StFtpcConfMapPoint *newhit)
{
// Returns true if circle, length, and angle cut holds.
if (newhit->GetCircleDist() < mMaxCircleDist[mVertexConstraint] &&
newhit->GetLengthDist() < mMaxLengthDist[mVertexConstraint] &&
TrackAngle(lasttrackhit, newhit) < mMaxAngleTrack[mVertexConstraint]) {
return true;
}
else {
return false;
}
}
Double_t const StFtpcConfMapper::TrackAngle(const StFtpcPoint *lasthitoftrack, const StFtpcPoint *hit)
{
// Returns the 'angle' between the last two points on the track (of which the last point is
// given as input) and the second given point.
StFormulary f;
Double_t x1[3];
Double_t x2[3];
StFtpcTrack *track = lasthitoftrack->GetTrack(mTrack);
TObjArray *hits = track->GetHits();
Int_t n = track->GetNumberOfPoints();
if (n<2) {
cout << "StFtpcConfMapper::TrackAngle(StFtpcPoint *lasthitoftrack, StFtpcPoint *hit)" << endl
<< " - Call this function only if you are sure to have at least two points on the track already!" << endl;
return false;
}
x1[0] = ((StFtpcPoint *)hits->At(n-1))->GetX() - ((StFtpcPoint *)hits->At(n-2))->GetX();
x1[1] = ((StFtpcPoint *)hits->At(n-1))->GetY() - ((StFtpcPoint *)hits->At(n-2))->GetY();
x1[2] = ((StFtpcPoint *)hits->At(n-1))->GetZ() - ((StFtpcPoint *)hits->At(n-2))->GetZ();
x2[0] = hit->GetX() - ((StFtpcPoint *)hits->At(n-1))->GetX();
x2[1] = hit->GetY() - ((StFtpcPoint *)hits->At(n-1))->GetY();
x2[2] = hit->GetZ() - ((StFtpcPoint *)hits->At(n-1))->GetZ();
return f.Angle(x1, x2, 3);
}
Double_t const StFtpcConfMapper::TrackletAngle(StFtpcTrack *track, Int_t n)
{
// Returns the angle 'between' the last three points (started at point number n) on this track.
StFormulary f;
Double_t x1[3];
Double_t x2[3];
TObjArray *hits = track->GetHits();
if (n > track->GetNumberOfPoints()) {
n = track->GetNumberOfPoints();
}
if (n<3) {
cout << "StFtpcConfMapper::TrackletAngle(StFtpcTrack *track)" << endl
<< " - Call this function only if you are sure to have at least three points on this track already!" << endl;
return false;
}
x1[0] = ((StFtpcPoint *)hits->At(n-2))->GetX() - ((StFtpcPoint *)hits->At(n-3))->GetX();
x1[1] = ((StFtpcPoint *)hits->At(n-2))->GetY() - ((StFtpcPoint *)hits->At(n-3))->GetY();
x1[2] = ((StFtpcPoint *)hits->At(n-2))->GetZ() - ((StFtpcPoint *)hits->At(n-3))->GetZ();
x2[0] = ((StFtpcPoint *)hits->At(n-1))->GetX() - ((StFtpcPoint *)hits->At(n-2))->GetX();
x2[1] = ((StFtpcPoint *)hits->At(n-1))->GetY() - ((StFtpcPoint *)hits->At(n-2))->GetY();
x2[2] = ((StFtpcPoint *)hits->At(n-1))->GetZ() - ((StFtpcPoint *)hits->At(n-2))->GetZ();
return f.Angle(x1, x2, 3);
}
Double_t const StFtpcConfMapper::GetPhiDiff(const StFtpcConfMapPoint *hit1, const StFtpcConfMapPoint *hit2)
{
// Returns the difference in angle phi of the two given clusters.
// Normalizes the result to the arbitrary angle between two subsequent padrows.
Double_t angle = TMath::Abs(hit1->GetPhi() - hit2->GetPhi());
if (angle > TMath::Pi()) angle = 2*TMath::Pi() - angle;
Double_t row_diff = TMath::Abs(hit1->GetPadRow() - hit2->GetPadRow());
return angle/row_diff;
}
Double_t const StFtpcConfMapper::GetEtaDiff(const StFtpcConfMapPoint *hit1, const StFtpcConfMapPoint *hit2)
{
// Returns the difference in pseudrapidity eta of the two given clusters.
// Normalizes the result to the arbitrary pseudorapidity between two subsequent padrows.
Double_t eta = TMath::Abs(hit1->GetEta() - hit2->GetEta());
Double_t row_diff = TMath::Abs(hit1->GetPadRow() - hit2->GetPadRow());
return eta/row_diff;
}
Double_t const StFtpcConfMapper::GetClusterDistance(const StFtpcConfMapPoint *hit1, const StFtpcConfMapPoint *hit2)
{
// Returns the distance of two clusters measured in terms of angle phi and pseudorapidity eta weighted by the
// maximal allowed values for phi and eta.
return TMath::Sqrt(TMath::Power(GetPhiDiff(hit1, hit2)/mMaxCircleDist[mVertexConstraint], 2) + TMath::Power(GetEtaDiff(hit1, hit2)/mMaxLengthDist[mVertexConstraint], 2));
}
Double_t const StFtpcConfMapper::GetDistanceFromFit(const StFtpcConfMapPoint *hit)
{
// Returns the distance of the given cluster to the track to which it probably belongs.
// The distances to the circle and length fit are weighted by the cuts on these values.
// Make sure that the variables mCircleDist and mLengthDist for the hit are set already.
return TMath::Sqrt(TMath::Power((hit->GetCircleDist() / mMaxCircleDist[mVertexConstraint]), 2) + TMath::Power((hit->GetLengthDist() / mMaxLengthDist[mVertexConstraint]), 2));
}
void StFtpcConfMapper::CalcChiSquared(StFtpcTrack *track, StFtpcConfMapPoint *point, Double_t *chi2)
{
// Calculates the chi squared for the circle and the length fit for a given point
// as an extension for the also given track.
TObjArray *trackpoint = track->GetHits();
StFtpcConfMapPoint *last_point = (StFtpcConfMapPoint *)trackpoint->Last();
if (!mVertexConstraint) {
point->SetAllCoord(last_point);
}
trackpoint->AddLast(point);
Double_t a[6];
StraightLineFit(track, a);
chi2[0] = a[4];
chi2[1] = a[5];
trackpoint->Remove(point);
return;
}
void StFtpcConfMapper::StraightLineFit(StFtpcTrack *track, Double_t *a, Int_t n)
{
// Calculates two straight line fits with the given clusters
//
// The first calculation is performed in the conformal mapping space (Xprime, Yprime):
// Yprime(Xprime) = a[0]*Xprime + a[1].
// The second calculates the fit for length s vs. z:
// s(z) = a[2]*z +a[3].
// a[4] is the chi squared per degrees of freedom for the circle fit.
// a[5] is the chi squared per degrees of freedom for the length fit.
TObjArray *trackpoints = track->GetHits();
if (n>0) {
if (n > trackpoints->GetEntriesFast()) {
n = trackpoints->GetEntriesFast();
}
}
else {
n = trackpoints->GetEntriesFast();
}
Double_t L11 = 0.;
Double_t L12 = 0.;
Double_t L22 = 0.;
Double_t g1 = 0.;
Double_t g2 = 0.;
Int_t start_counter = 0;
if (!mVertexConstraint) {
start_counter = 1;
}
// Circle Fit
StFtpcConfMapPoint *trackpoint;
for (Int_t i = start_counter; i < n; i++) {
trackpoint = (StFtpcConfMapPoint *)trackpoints->At(i);
L11 += 1./* / (trackpoint->GetYprimeerr() * trackpoint->GetYprimeerr())*/;
L12 += trackpoint->GetXprime()/* / (trackpoint->GetYprimeerr() * trackpoint->GetYprimeerr())*/;
L22 += (trackpoint->GetXprime() * trackpoint->GetXprime())/* / (trackpoint->GetYprimeerr() * trackpoint->GetYprimeerr())*/;
g1 += trackpoint->GetYprime()/* / (trackpoint->GetYprimeerr() * trackpoint->GetYprimeerr())*/;
g2 += (trackpoint->GetXprime() * trackpoint->GetYprime())/* / (trackpoint->GetYprimeerr() * trackpoint->GetYprimeerr())*/;
}
Double_t D = L11*L22 - L12*L12;
a[0] = (g2*L11 - g1*L12)/D;
a[1] = (g1*L22 - g2*L12)/D;
// Set circle parameters
track->SetCenterX(- a[0] / (2. * a[1]) + trackpoint->GetXt());
track->SetCenterY(- 1. / (2. * a[1]) + trackpoint->GetYt());
track->SetRadius(TMath::Sqrt(a[0]*a[0] + 1.) / (2. * TMath::Abs(a[1])));
track->SetAlpha0(TMath::ASin((trackpoint->GetYt() - track->GetCenterY()) / track->GetRadius()));
// Tracklength Fit
Double_t s;
Double_t asin_arg;
// Set variables again
// first track point is main vertex or first track point (both at (0, 0, 0) [shifted coordinates])
L11 = 1.;
L12 = L22 = g1 = g2 = 0.;
for (Int_t i = start_counter; i < n; i++ ) {
trackpoint= (StFtpcConfMapPoint *)trackpoints->At(i);
asin_arg = (trackpoint->GetYv() - track->GetCenterY()) / track->GetRadius();
// The following lines were inserted because ~1% of all tracks produce arguments of arcsin
// which are above the |1| limit. But they were differing only in the 5th digit after the point.
if (TMath::Abs(asin_arg) > 1.) {
asin_arg = (asin_arg >= 0) ? +1. : -1.;
mLengthFitNaN++;
}
s = TMath::Sqrt(TMath::Power(track->GetRadius() * (TMath::ASin(asin_arg) - track->GetAlpha0()), 2) +
TMath::Power(trackpoint->GetZv() - trackpoint->GetZt(), 2));
L11 += 1;
L12 += trackpoint->GetZv();
L22 += (trackpoint->GetZv() * trackpoint->GetZv());
g1 += s;
g2 += (s * trackpoint->GetZv());
}
D = L11*L22 - L12*L12;
a[2] = (g2*L11 - g1*L12)/D;
a[3] = (g1*L22 - g2*L12)/D;
// Set chi squared
Double_t chi2circle = 0.;
Double_t chi2length = 0.;
for (Int_t i = 0; i < n; i++) {
trackpoint = (StFtpcConfMapPoint *)trackpoints->At(i);
asin_arg = (trackpoint->GetYv() - track->GetCenterY()) / track->GetRadius();
if (TMath::Abs(asin_arg) > 1.) {
asin_arg = (asin_arg >= 0) ? +1. : -1.;
}
s = TMath::Sqrt(TMath::Power(track->GetRadius() * (TMath::ASin(asin_arg) - track->GetAlpha0()), 2) +
TMath::Power(trackpoint->GetZv() - trackpoint->GetZt(), 2));
chi2circle += TMath::Power(trackpoint->GetYprime() - a[0]*trackpoint->GetXprime()+a[1], 2.) / (a[0]*trackpoint->GetXprime()+a[1]);
chi2length += TMath::Power(s - a[2]*trackpoint->GetZv()+a[3], 2.) / (a[2]*trackpoint->GetZv()+a[3]);
}
if (mVertexConstraint) {
n++;
}
a[4] = chi2circle/(n-3.);
a[5] = chi2length/(n-2.);
//track->SetChi2Circle(chi2circle/(n-2.));
//track->SetChi2Length(chi2length/(n-2.));
return;
}
void StFtpcConfMapper::HandleSplitTracks(Double_t max_dist, Double_t ratio_min, Double_t ratio_max)
{
// Looks for split tracks and passes them to the track merger.
Double_t ratio;
Double_t dist;
Double_t r1;
Double_t r2;
Double_t R1;
Double_t R2;
Double_t phi1;
Double_t phi2;
Double_t Phi1;
Double_t Phi2;
StFtpcTrack *t1;
StFtpcTrack *t2;
Int_t entries = mTrack->GetEntriesFast();
for (Int_t i = 0; i < entries; i++) {
t1 = (StFtpcTrack *)mTrack->At(i);
if (!t1) {
// track was removed before (already merged)
continue;
}
for (Int_t j = i+1; j < entries; j++) {
t2 = (StFtpcTrack *)mTrack->At(j);
if (!t2) {
// track was removed before (already merged)
continue;
}
r1 = t1->GetRLast();
r2 = t2->GetRLast();
phi1 = t1->GetAlphaLast();
phi2 = t2->GetAlphaLast();
R1 = t1->GetRFirst();
R2 = t2->GetRFirst();
Phi1 = t1->GetAlphaFirst();
Phi2 = t2->GetAlphaFirst();
dist = (TMath::Sqrt(r2*r2+r1*r1-2*r1*r2*(TMath::Cos(phi1)*TMath::Cos(phi2)+TMath::Sin(phi1)*TMath::Sin(phi2))) +
TMath::Sqrt(R2*R2+R1*R1-2*R1*R2*(TMath::Cos(Phi1)*TMath::Cos(Phi2)+TMath::Sin(Phi1)*TMath::Sin(Phi2)))) / 2.;
ratio = (Double_t)(t1->GetNumberOfPoints() + t2->GetNumberOfPoints()) / (Double_t)(t1->GetNMax() + t2->GetNMax());
if (!(t1->GetRowsWithPoints() & t2->GetRowsWithPoints()) &&
(t1->GetHemisphere() == t2->GetHemisphere()) &&
dist <= max_dist && ratio <= ratio_max && ratio >= ratio_min) {
MergeSplitTracks(t1, t2);
}
}
}
mTrack->Compress();
mTrack->Expand(mTrack->GetLast()+1);
return;
}
void StFtpcConfMapper::MergeSplitTracks(StFtpcTrack *t1, StFtpcTrack *t2)
{
// Merges two tracks which are split.
Int_t new_track_number = mTrack->GetEntriesFast();
Int_t num_t1_points = t1->GetNumberOfPoints();
Int_t num_t2_points = t2->GetNumberOfPoints();
TObjArray *t1_points = t1->GetHits();
TObjArray *t2_points = t2->GetHits();
TClonesArray &track_init = *mTrack;
new(track_init[new_track_number]) StFtpcTrack();
StFtpcTrack *track = (StFtpcTrack *)mTrack->At(new_track_number);
TObjArray *trackpoint = track->GetHits();
trackpoint->Expand(mMaxFtpcRow);
MIntArray *trackhitnumber = track->GetHitNumbers();
for (Int_t i = 0; i < mMaxFtpcRow; i++) {
if (i < num_t1_points) {
trackpoint->AddAt(t1_points->At(i), mMaxFtpcRow - 1 -(((StFtpcPoint *)t1_points->At(i))->GetPadRow()-1)%mMaxFtpcRow);
}
if (i < num_t2_points) {
trackpoint->AddAt(t2_points->At(i), mMaxFtpcRow - 1 -(((StFtpcPoint *)t2_points->At(i))->GetPadRow()-1)%mMaxFtpcRow);
}
}
trackpoint->Compress();
trackpoint->Expand(trackpoint->GetLast()+1);
for (Int_t i = 0; i < trackpoint->GetEntriesFast(); i++) {
trackhitnumber->AddLast(((StFtpcPoint *)trackpoint->At(i))->GetHitNumber());
}
track->SetProperties(true, new_track_number);
track->ComesFromMainVertex(mVertexConstraint);
track->CalculateNMax();
if (mVertexConstraint) mMainVertexTracks++;
if (t1->ComesFromMainVertex()) mMainVertexTracks--;
if (t2->ComesFromMainVertex()) mMainVertexTracks--;
mTrack->Remove(t1);
mTrack->Remove(t2);
mMergedSplits++;
return;
}
void StFtpcConfMapper::ClusterLoop()
{
// This function loops over all clusters to do the tracking.
// These clusters act as starting points for new tracks.
Int_t row_segm;
Int_t phi_segm;
Int_t eta_segm;
Int_t entries;
Int_t hit_num;
TObjArray *segment;
StFtpcConfMapPoint *hit;
// loop over two Ftpcs
for (mFtpc = 1; mFtpc <= 2; mFtpc++) {
// loop over the respective 10 RowSegments ("layers") per Ftpc
// loop only so far to where you can still put a track in the remaining padrows (due to length)
for (row_segm = mFtpc * mMaxFtpcRow - 1; row_segm >= (mFtpc-1) * mMaxFtpcRow + mMinPoints[mVertexConstraint] - 1; row_segm--) {
// loop over phi segments
for (Int_t phi_segm_counter = 0; phi_segm_counter < mNumPhiSegment; phi_segm_counter++) {
// go over phi in two directions, one segment in each direction alternately
if(phi_segm_counter%2) {
phi_segm = mNumPhiSegment - phi_segm_counter - mNumPhiSegment%2;
}
else {
phi_segm = phi_segm_counter;
}
// loop over eta segments
for(eta_segm = 0; eta_segm < mNumEtaSegment; eta_segm++) {
// loop over entries in one segment
if ((entries = (segment = (TObjArray *)mVolume->At(GetSegm(row_segm, phi_segm, eta_segm)))->GetEntriesFast())) {
for (hit_num = 0; hit_num < entries; hit_num++) {
hit = (StFtpcConfMapPoint *)segment->At(hit_num);
if (hit->GetUsage() == true) { // start hit was used before
continue;
}
else { // start hit was not used before
CreateTrack(hit);
}
}
}
else continue; // no entries in this segment
}
}
}
}
return; // end of track finding
}
void StFtpcConfMapper::CreateTrack(StFtpcConfMapPoint *hit)
{
// This function takes as input a point in the Ftpc which acts as starting point for a new track.
// It forms tracklets then extends them to tracks.
Double_t *coeff = NULL;
Double_t chi2[2];
StFtpcConfMapPoint *closest_hit = NULL;
StFtpcTrack *track = NULL;
Int_t point;
Int_t tracks = GetNumberOfTracks();
TClonesArray &track_init = *mTrack;
new(track_init[tracks]) StFtpcTrack();
track = (StFtpcTrack *)mTrack->At(tracks);
TObjArray *trackpoint = track->GetHits();
MIntArray *trackhitnumber = track->GetHitNumbers();
trackpoint->AddLast(hit); // add address of first cluster to cluster list
trackhitnumber->AddLast(hit->GetHitNumber()); // add number of first cluster to number list
// set conformal mapping coordinates if looking for non vertex tracks
if (!mVertexConstraint) {
hit->SetAllCoord(hit);
}
// create tracklets
for (point = 1; point < mTrackletLength[mVertexConstraint]; point++) {
if ((closest_hit = GetNextNeighbor(hit, coeff))) {
// closest_hit for hit exists
trackhitnumber->AddLast(closest_hit->GetHitNumber());
trackpoint->AddLast(closest_hit);
hit = closest_hit;
}
else {
// closest hit does not exist
mTrack->Remove(track); // remove track
point = mTrackletLength[mVertexConstraint]; // continue with next hit in segment
}
}
// tracklet is long enough to be extended to a track
if (trackpoint->GetEntriesFast() == mTrackletLength[mVertexConstraint]) {
track->SetProperties(true, tracks); // set properties for tracklet
// (otherwise TrackletAngle() does not work properly)
if (TrackletAngle(track) > mMaxAngleTracklet[mVertexConstraint]) { // proof if the first points seem to be a beginning of a track
track->SetProperties(false, tracks); // set usage of the clusters to "unused"
mTrack->Remove(track); // remove track
}
else { // good tracklet -> proceed
tracks++;
// create tracks
for (point = mTrackletLength[mVertexConstraint]; point < mMaxFtpcRow; point++) {
if (!coeff) coeff = new Double_t[6];
//Double_t chi_circ = track->GetChi2Circle();
//Double_t chi_len = track->GetChi2Length();
StraightLineFit(track, coeff);
closest_hit = GetNextNeighbor((StFtpcConfMapPoint *)trackpoint->Last(), coeff);
if (closest_hit) {
/*
CalcChiSquared(track, closest_hit, chi2);
if (coeff[4]-chi2[0]>1.) {
//cout << coeff[4] << " - " << chi2[0] << " = " << coeff[4]-chi2[0] << endl;
//cout << coeff[5] << " - " << chi2[1] << " = " << coeff[5]-chi2[1] << endl << endl;
point = mMaxFtpcRow;
}
else {
*/
// add closest hit to track
trackhitnumber->AddLast(closest_hit->GetHitNumber());
trackpoint->AddLast(closest_hit);
closest_hit->SetUsage(true);
closest_hit->SetTrackNumber(tracks-1);
// }
}
else {
// closest hit does not exist
/*
probably switch off vertexconstraint!
if (point.PadRow() > limit) {
}
else
*/
point = mMaxFtpcRow; // continue with next hit in segment
}
}
// remove tracks with not enough points already now
if (track->GetNumberOfPoints() < mMinPoints[mVertexConstraint]) {
track->SetProperties(false, tracks-1); // set usage of the clusters to "unused"
mTrack->Remove(track); // remove track
tracks--;
}
else {
mClustersUnused -= track->GetNumberOfPoints();
track->ComesFromMainVertex(mVertexConstraint); // mark track as main vertex track or not
track->CalculateNMax();
if (mVertexConstraint) mMainVertexTracks++;
}
// cleanup
delete[] coeff;
coeff = NULL;
}
}
return;
}
StFtpcConfMapPoint *StFtpcConfMapper::GetNextNeighbor(StFtpcConfMapPoint *start_hit, Double_t *coeff = NULL)
{
// Returns the nearest cluster to a given start_hit.
Double_t dist, closest_dist = 1.e7;
Double_t closest_circle_dist = 1.e7;
Double_t closest_length_dist = 1.e7;
StFtpcConfMapPoint *hit = NULL;
StFtpcConfMapPoint *closest_hit = NULL;
StFtpcConfMapPoint *closest_circle_hit = NULL;
StFtpcConfMapPoint *closest_length_hit = NULL;
TObjArray *sub_segment;
Int_t sub_entries;
Int_t sub_row_segm;
Int_t sub_phi_segm;
Int_t sub_eta_segm;
Int_t sub_hit_num;
Int_t max_row = GetRowSegm(start_hit) - 1;
Int_t min_row;
if (coeff) {
min_row = GetRowSegm(start_hit) - mRowScopeTrack[mVertexConstraint];
}
else {
min_row = GetRowSegm(start_hit) - mRowScopeTracklet[mVertexConstraint];
}
// loop over sub volume
while (min_row < (mFtpc-1) * mMaxFtpcRow) {
min_row++;
}
if (max_row < min_row) return 0;
else {
// loop over sub rows
for (sub_row_segm = max_row; sub_row_segm >= min_row; sub_row_segm--) {
// loop over sub phi segments
for (Int_t i = -(mPhiScope[mVertexConstraint]); i <= mPhiScope[mVertexConstraint]; i++) {
sub_phi_segm = GetPhiSegm(start_hit) + i; // neighboring phi segment
if (sub_phi_segm < 0) { // find neighboring segment if #segment < 0
sub_phi_segm += mNumPhiSegment;
}
else if (sub_phi_segm >= mNumPhiSegment) { // find neighboring segment if #segment > fNum_phi_segm
sub_phi_segm -= mNumPhiSegment;
}
// loop over sub eta segments
for (Int_t j = -(mEtaScope[mVertexConstraint]); j <= mEtaScope[mVertexConstraint]; j++) {
sub_eta_segm = GetEtaSegm(start_hit) + j; // neighboring eta segment
if (sub_eta_segm < 0 || sub_eta_segm >= mNumEtaSegment) {
continue; // #segment exceeds bounds -> skip
}
// loop over entries in one sub segment
if ((sub_entries = ((sub_segment = (TObjArray *)mVolume->At(GetSegm(sub_row_segm, sub_phi_segm, sub_eta_segm)))->GetEntriesFast()))) {
for (sub_hit_num = 0; sub_hit_num < sub_entries; sub_hit_num++) {
hit = (StFtpcConfMapPoint *)sub_segment->At(sub_hit_num);
if (!(hit = (StFtpcConfMapPoint *)sub_segment->At(sub_hit_num))->GetUsage()) {
// hit was not used before
// set conformal mapping coordinates if looking for non vertex tracks
if (!mVertexConstraint) {
hit->SetAllCoord(start_hit);
}
if (coeff) { // track search - look for nearest neighbor to extrapolated track
// test distance
hit->SetDist(CalcDistance(hit, coeff+0), CalcDistance(hit, coeff+2));
if (hit->GetCircleDist() < closest_circle_dist) {
closest_circle_dist = hit->GetCircleDist();
closest_circle_hit = hit;
}
if (hit->GetLengthDist() < closest_length_dist) {
closest_length_dist = hit->GetLengthDist();
closest_length_hit = hit;
}
}
else {
// tracklet search - just look for the nearest neighbor (distance defined by Pablo Jepes)
// test distance
if ((dist = CalcDistance(start_hit, hit)) < closest_dist) {
closest_dist = dist;
closest_hit = hit;
}
else { // sub hit was farther away than a hit before
continue;
}
}
}
else continue; // sub hit was used before
}
}
else continue; // no sub hits
}
}
if ((coeff && (closest_circle_hit || closest_length_hit)) || ((!coeff) && closest_hit)) {
if ((max_row - sub_row_segm) >= 1) {
if (coeff) {
mMergedTracks++;
}
else {
mMergedTracklets++;
}
}
// found a hit in a sub layer - don't look in other sub layers
break;
}
else {
// didn't find a hit in this sub layer - try next sub layer
continue;
}
}
}
if (coeff) {
if (closest_circle_hit && closest_length_hit) { // hits are not zero
if (closest_circle_hit == closest_length_hit) { // both found hits are identical
if (VerifyCuts(start_hit, closest_circle_hit)) {
// closest hit within limits found
return closest_circle_hit;
}
else { // limits exceeded
return 0;
}
}
else { // found hits are different
mDiffHits++;
Bool_t cut_circle = VerifyCuts(start_hit, closest_circle_hit);
Bool_t cut_length = VerifyCuts(start_hit, closest_length_hit);
if (cut_circle && cut_length) { // both hits are within the limit
if (GetDistanceFromFit(closest_circle_hit) < GetDistanceFromFit(closest_length_hit)) { // circle_hit is closer
return closest_circle_hit;
}
else { // length_hit is closer
return closest_length_hit;
}
}
else if (!(cut_circle || cut_length)) { // both hits exceed limits
return 0;
}
else if (cut_circle) { // closest_circle_hit is the only one within limits
return closest_circle_hit;
}
else { // closest_length_hit is the only one within limits
return closest_length_hit;
}
}
}
else { // no hits found
return 0;
}
}
else { // closest hit for tracklet found
if (closest_hit) { // closest hit exists
return closest_hit;
}
else { // hit does not exist
return 0;
}
}
}
void StFtpcConfMapper::TrackLoop()
{
// Loops over all found tracks and passes them to the part of the program where each track is tried to be extended.
Int_t num_extended = 0;
for (Int_t t = 0; t < mTrack->GetEntriesFast(); t++) {
if (ExtendTrack((StFtpcTrack *)mTrack->At(t))) {
num_extended++;
}
}
cout << num_extended << " tracks extended" << endl;
return;
}
Bool_t StFtpcConfMapper::ExtendTrack(StFtpcTrack *track)
{
// Trys to extend a given track.
Int_t point;
Int_t number_of_points = track->GetNumberOfPoints();
Int_t tracks = mTrack->IndexOf(track);
Double_t *coeff = NULL;
StFtpcConfMapPoint *closest_hit;
TObjArray *trackpoint = track->GetHits();
MIntArray *trackhitnumber = track->GetHitNumbers();
for (point = number_of_points; point < mMaxFtpcRow; point++) {
if (!coeff) coeff = new Double_t[6];
StraightLineFit(track, coeff);
closest_hit = GetNextNeighbor((StFtpcConfMapPoint *)trackpoint->Last(), coeff);
if (closest_hit) {
// add closest hit to track
trackhitnumber->AddLast(closest_hit->GetHitNumber());
trackpoint->AddLast(closest_hit);
closest_hit->SetUsage(true);
closest_hit->SetTrackNumber(tracks);
}
else {
// closest hit does not exist
/*
probably switch off vertexconstraint!
if (point.PadRow() > limit) {
}
else
*/
point = mMaxFtpcRow; // continue with next hit in segment
}
}
// cleanup
delete[] coeff;
coeff = NULL;
if (track->GetNumberOfPoints() - number_of_points) {
mClustersUnused -= (track->GetNumberOfPoints() - number_of_points);
track->CalculateNMax();
return (Bool_t)true;
}
else {
return (Bool_t)false;
}
}
void StFtpcConfMapper::Cout(Int_t width, Int_t figure)
{
// Prints the integer figure in a field with width.
cout.width(width);
cout << figure;
return;
}
void StFtpcConfMapper::Cout(Int_t width, Double_t figure)
{
// Prints the double precission figure in a field with width.
cout.width(width);
cout << figure;
return;
}
void StFtpcConfMapper::TrackingInfo()
{
// Information about the tracking process.
cout << endl;
cout << "Tracking information" << endl;
cout << "--------------------" << endl;
Cout(5, GetNumberOfTracks()); cout << " (";
Cout(5, GetNumMainVertexTracks()); cout << "/";
Cout(5, GetNumberOfTracks() - GetNumMainVertexTracks()); cout << ") tracks (main vertex/non vertex) found." << endl;
Cout(5, GetNumberOfClusters()); cout << " (";
Cout(5, GetNumberOfClusters() - GetNumClustersUnused()); cout << "/";
Cout(5, GetNumClustersUnused()); cout << ") clusters (used/unused)." << endl;
cout << " ";
Cout(5, GetNumMergedTracks()); cout << "/";
Cout(5, GetNumMergedTracklets()); cout << " tracks/tracklets merged." << endl;
Cout(18, GetNumDiffHits()); cout << " times different hits for circle and length fit found." << endl;
Cout(18, GetNumLengthFitNaN()); cout << " times argument of arcsin set to +/-1." << endl;
Cout(18, GetNumMergedSplits()); cout << " split tracks merged." << endl;
return;
}
void StFtpcConfMapper::CutInfo()
{
// Information about cuts.
cout << endl;
cout << "Cuts for main vertex constraint on / off" << endl;
cout << "----------------------------------------" << endl;
cout << "Max. angle between last three points of tracklets: ";
Cout(6, mMaxAngleTracklet[1]); cout << " / ";
Cout(6, mMaxAngleTracklet[0]); cout << endl;
cout << "Max. angle between last three points of tracks: ";
Cout(6, mMaxAngleTrack[1]); cout << " / ";
Cout(6, mMaxAngleTrack[0]); cout << endl;
cout << "Max. distance between circle fit and trackpoint: ";
Cout(6, mMaxCircleDist[1]); cout << " / ";
Cout(6, mMaxCircleDist[0]); cout << endl;
cout << "Max. distance between length fit and trackpoint: ";
Cout(6, mMaxLengthDist[1]); cout << " / ";
Cout(6, mMaxLengthDist[0]); cout << endl;
return;
}
void StFtpcConfMapper::SettingInfo()
{
// Information about settings.
cout << endl;
cout << "Settings for main vertex constraint on / off" << endl;
cout << "--------------------------------------------" << endl;
cout << "Points required to create a tracklet: ";
cout << mTrackletLength[1] << " / ";
cout << mTrackletLength[0] << endl;
cout << "Points required for a track: ";
cout << mMinPoints[1] << " / ";
cout << mMinPoints[0] << endl;
cout << "Subsequent padrows to look for next tracklet point: ";
cout << mRowScopeTracklet[1] << " / ";
cout << mRowScopeTracklet[0] << endl;
cout << "Subsequent padrows to look for next track point: ";
cout << mRowScopeTrack[1] << " / ";
cout << mRowScopeTrack[0] << endl;
cout << "Adjacent phi segments to look for next point: ";
cout << mPhiScope[1] << " / ";
cout << mPhiScope[0] << endl;
cout << "Adjacent eta segments to look for next point: ";
cout << mEtaScope[1] << " / ";
cout << mEtaScope[0] << endl;
return;
}
ROOT page - Home page - Class index - Top of the page
This page has been automatically generated. If you have any comments or suggestions about the page layout send a mail to ROOT support, or contact the developers with any questions or problems regarding ROOT.