Skip to content

TPC timeseries: Propagating TPC track to TOF cluster#12312

Merged
shahor02 merged 4 commits into
AliceO2Group:devfrom
matthias-kleiner:DCAd_monitoring
Nov 27, 2023
Merged

TPC timeseries: Propagating TPC track to TOF cluster#12312
shahor02 merged 4 commits into
AliceO2Group:devfrom
matthias-kleiner:DCAd_monitoring

Conversation

@matthias-kleiner
Copy link
Copy Markdown
Contributor

No description provided.

float tpcYatTOF = 0;
float tpcZatTOF = 0;
if (hasTOFCluster) {
if (trackTmp.rotate(o2::math_utils::sector2Angle(tofCl.getSector())) && propagator->propagateTo(trackTmp, tofCl.getX(), false, mMaxSnp, mFineStep, mMatType)) {
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hello @shahor02
we wanted to get for each TPC track that has a TOF cluster assigned the TPC track propagated to the TOF cluster position and then compare the propagated TPC track position with the TOF cluster position.
Can you take a look if I did it properly?
I am also not quite sure if the propagation is correctly done. I assumed here that the tfCl.getX() returns the local X position of the cluster.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One should not use the TPC innermost track param for propagation to TOF, instead use (since you don't use errors)

o2::track::TrackPar trackTmpOut(tracksTPC[iTrk].getParamOut());
...

// loop over ITS-TPC-TRD-TOF and ITS-TPC-TOF tracks an store for each ITS-TPC track the TOF track index
for (const auto& tofMatch : tofMatches) {
for (const auto& tpctofmatch : tofMatch) {
auto refTPC = recoData.getSingleDetectorRefs(tpctofmatch.getTrackRef())[TrkSrc::TPC];
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

since you need only TPC index, you can use faster method

auto refTPC = recoData.getTPCContributorGID(tpctofmatch.getTrackRef());

void fillDCA(const gsl::span<const TrackTPC> tracksTPC, const gsl::span<const o2::dataformats::TrackTPCITS> tracksITSTPC, const gsl::span<const o2::dataformats::PrimaryVertex> vertices, const int iTrk, const int iThread, const std::unordered_map<unsigned int, std::array<int, 2>>& indicesITSTPC, const gsl::span<const o2::its::TrackITS> tracksITS)
void fillDCA(const gsl::span<const TrackTPC> tracksTPC, const gsl::span<const o2::dataformats::TrackTPCITS> tracksITSTPC, const gsl::span<const o2::dataformats::PrimaryVertex> vertices, const int iTrk, const int iThread, const std::unordered_map<unsigned int, std::array<int, 2>>& indicesITSTPC, const gsl::span<const o2::its::TrackITS> tracksITS, const std::vector<int>& idxTPCTrackToTOFCluster, const gsl::span<const o2::tof::Cluster> tofClusters)
{
TrackTPC track = tracksTPC[iTrk];
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see that you are still cloning the full TPC track. Why? For the propagation it is enough to clone TrackParCov.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, I thought since we want the errors we would still need then the full track. But you are right, thanks.

float tpcYatTOF = 0;
float tpcZatTOF = 0;
if (hasTOFCluster) {
if (trackTmp.rotate(o2::math_utils::sector2Angle(tofCl.getSector())) && propagator->propagateTo(trackTmp, tofCl.getX(), false, mMaxSnp, mFineStep, mMatType)) {
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One should not use the TPC innermost track param for propagation to TOF, instead use (since you don't use errors)

o2::track::TrackPar trackTmpOut(tracksTPC[iTrk].getParamOut());
...

- storing delta parameter of TPC between inner and outer param
- small fixes
@miranov25
Copy link
Copy Markdown
Contributor

Hello @matthias-kleiner

Please include mDXatTOF and mDZatTOF for combined tracks as well.

If I come across anything later, we can make the necessary updates. I'm considering including TOF dY and dZ of combined tracks not only in the skimmed data but also to the time series.

In the pull request, let's explicitly provide the values for dY and dZ, and inquire whether they seem reasonable. The variation in dY suggests that the sectors may not be aligned correctly. I am checking what could be the reason (TPC or TOF?), but the code should be checked if alignment correction was applied.

image

@miranov25
Copy link
Copy Markdown
Contributor

TOF observations based on the RootInteractive dashboard:

  • observed TOF dY bias in respect to TPC tracks on A side and C side is similar - bias per sector bias of O(3 cm)
  • observed TOF dZ bias in respect to TPC tracks on A side and C side is different- up-down bias (up 0-9 down -9-18)

@shahor02
Copy link
Copy Markdown
Collaborator

@miranov25 is this with the corrected TPC outerParam track extrapolation or previous, inner param extrapolation to TOF?

@matthias-kleiner
Copy link
Copy Markdown
Contributor Author

@miranov25 is this with the corrected TPC outerParam track extrapolation or previous, inner param extrapolation to TOF?

This is with the latest version of the code like you suggested

@shahor02
Copy link
Copy Markdown
Collaborator

Very strange, could you produce a similar plot for the opposite polarity? What is the IR of this run?
The residuals I saw before and after the alignment (ITS/TRD/TOF refits, no TPC) are
https://alice.its.cern.ch/jira/secure/attachment/60038/algrepB0_Lev1ok3_vs_def.pdf
https://alice.its.cern.ch/jira/secure/attachment/60036/algrepBMinus_Lev1ok3_vs_def.pdf
https://alice.its.cern.ch/jira/secure/attachment/60034/algrepBPlus_Lev1ok3_vs_def.pdf
for B0, B- and B+ resp.

@matthias-kleiner
Copy link
Copy Markdown
Contributor Author

Very strange, could you produce a similar plot for the opposite polarity? What is the IR of this run? The residuals I saw before and after the alignment (ITS/TRD/TOF refits, no TPC) are https://alice.its.cern.ch/jira/secure/attachment/60038/algrepB0_Lev1ok3_vs_def.pdf https://alice.its.cern.ch/jira/secure/attachment/60036/algrepBMinus_Lev1ok3_vs_def.pdf https://alice.its.cern.ch/jira/secure/attachment/60034/algrepBPlus_Lev1ok3_vs_def.pdf for B0, B- and B+ resp.

We looked at different runs: 544126.50Hz, 544124.8kHz, 544122.18kHz, 544116.38kHz. It was the same for all of them.
I can try to run it for the different polarity

@miranov25
Copy link
Copy Markdown
Contributor

The residuals in tofmatch are ok. The problem is only when we are doing own matching. I assume it could be because we read clusters from root file, maybe some corrections were not applied.

@shahor02
Copy link
Copy Markdown
Collaborator

The residuals in tofmatch are ok. The problem is only when we are doing own matching. I assume it could be because we read clusters from root file, maybe some corrections were not applied.

That would be strange, but better @noferini comments.

@noferini
Copy link
Copy Markdown
Collaborator

We don't have any additional correction on TOF clusters. What is read is used.
However, when the residuals are calculated in TOF matching algorithm TPC tracks get the TOF time and then they can be shifted along Z.
If tracks are provided with a t0 they are shifted by (t0 - t_tof)*vdrift since TOF time is more precise (this is the only TOF constraint to the track).
I don't know if this can explain the difference (I assume this is not done in your own code).
Can you point me to your matching algorithm?

float tpcZDeltaAtTOF = -999;
if (hasTOFCluster) {
o2::track::TrackPar trackTmpOut(tracksTPC[iTrk].getParamOut());
if (trackTmpOut.rotate(o2::math_utils::sector2Angle(tofCl.getSector())) && propagator->propagateTo(trackTmpOut, tofCl.getX(), false, mMaxSnp, mFineStep, mMatType)) {
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hello @noferini
the propagation with the TPC track to TOF cluster is done here.
When we calculate the difference between TPC and TOF cluster in z (tpcZDeltaAtTOF) direction, we are also doing some correction, but I am not fully sure if this is correct:
tpcZDeltaAtTOF = signSide * (o2::tpc::ParameterElectronics::Instance().ZbinWidth * trackFull.getTime0() - vertex.getTimeStamp().getTimeStamp()) * mVDrift - trackTmpOut.getZ() + tofCl.getZ();

For the difference in y we are just doing tpcYDeltaAtTOF = trackTmpOut.getY() - tofCl.getY();

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@matthias-kleiner, I am not completely sure about the signs in tpcZDeltaAtTOF formula but for tpcYDeltaAtTOF there are no problems. I don't see any evident problem in the propagation call.
Do you have also tpcZDeltaAtTOF vs phi/sector?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @noferini ,
yes sure it looks like this.
tpcZDeltaAtTOF_544124_8kHz

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks much better than Y residual.
What is the fine-step you are using in propagation? 2 cm (default)?
Does this play a role?

@miranov25
Copy link
Copy Markdown
Contributor

Dear @matthias-kleiner, @shahor02 and @noferini

I've uploaded the RootInteractive dashboard with all observations to EOS. This allows you to test all residual data (DCA, delta parameters at the vertex, and TOF) as a function of the rate and kinematic variables.

The data is grouped into three categories, facilitating functional decomposition.

    varKine0=["qpt_10","tgl_10","side_type"]
    varKine1=["qpt_10","tgl_10","side_type","dSec_10"]
    varKine2=["qpt_10","tgl_10","side_type","sec_10"]

For TOF, we have data for the standard delta (as in official reconstruction) and for the TPC-only track deltas. I won't have time to follow that pull request during the weekend, but I'll post some observations here that might be helpful. From our perspective, it will be important to integrate TPC-TOF into production. Even if we don't obtain absolute values, relative values will still be useful.

More details will be provided in the following posts.

Marian

@miranov25
Copy link
Copy Markdown
Contributor

miranov25 commented Nov 25, 2023

Data aggregation analysis and interactive dashboard:

@miranov25
Copy link
Copy Markdown
Contributor

TOF residuals as function of rate:

  • "official delta" from TOFmatch as function of rate
image
  • TPC-TOF delta which we extracted in order to calibrate and characterize TPC distortion
image

@miranov25
Copy link
Copy Markdown
Contributor

DCA for the TPCITS track to be checked

  • looks like vertex position is not used
image

@miranov25
Copy link
Copy Markdown
Contributor

TPC--> DCA Bias with CPass1 Type of Distortion Calibration

The DCA bias should also be reflected in the TPC-TOF bias once the matching algorithm is fixed.

  • Modulation at sector boundaries as a function of rate: O 0.5 cm
image * Modulation removed by the factorized fits, except for some hotspot regions that should be treated separately. image

@miranov25
Copy link
Copy Markdown
Contributor

Hello all.

Do you have any suggestions for what could be the problem? I asked Matthias to create a full TOF cluster and TOF match dump so we can debug it. Indeed, this will be a good option to have in general (switched OFF in production)

@shahor02
Copy link
Copy Markdown
Collaborator

@noferini actually, in the TOF matching you are not using TOF cluster positions as they are stored, using

Geo::getPadDxDyDz(posFloat, detIdTemp, deltaPosTemp, sec);
instead. Also, in the alignment I am recalculating the coordinates from the channels ideal positions with particular "starting" alignment matrices:
const auto& clus = TOFClusters[gid.getIndex()];
int det[5] = {}, ch = clus.getMainContributingChannel();
o2::tof::Geo::getVolumeIndices(ch, det);
int sensID = o2::tof::Geo::getStripNumberPerSM(det[1], det[2]) + clus.getSector() * o2::tof::Geo::NSTRIPXSECTOR;
auto* sensor = (AlignableSensorTOF*)getSensor(sensID);
if (sensor->isDummy()) {
LOGP(error, "Dummy sensor {} is referred by a track", sensID);
return 0;
}
double loc[3] = {(det[4] + 0.5) * o2::tof::Geo::XPAD - o2::tof::Geo::XHALFSTRIP, 0., (det[3] - 0.5) * o2::tof::Geo::ZPAD}, locCorr[3] = {}, traCorr[3] = {};
const auto& matAlg = sensor->getMatrixClAlg();
matAlg.LocalToMaster(loc, locCorr);
// rotate to tracking
const auto& matT2L = sensor->getMatrixT2L();
matT2L.MasterToLocal(locCorr, traCorr);
.

Are you sure the coordinates as they are set in the clustering are equivalent to those set in the matching?

@miranov25
Copy link
Copy Markdown
Contributor

Hello @shahor02 and @noferini

Should we perform the "realignment" in our code, as indicated by @shahor02 in the code snippet, or do you prefer another solution?

@noferini actually, in the TOF matching you are not using TOF cluster positions as they are stored, using

Geo::getPadDxDyDz(posFloat, detIdTemp, deltaPosTemp, sec);

instead. Also, in the alignment I am recalculating the coordinates from the channels ideal positions with particular "starting" alignment matrices:

const auto& clus = TOFClusters[gid.getIndex()];
int det[5] = {}, ch = clus.getMainContributingChannel();
o2::tof::Geo::getVolumeIndices(ch, det);
int sensID = o2::tof::Geo::getStripNumberPerSM(det[1], det[2]) + clus.getSector() * o2::tof::Geo::NSTRIPXSECTOR;
auto* sensor = (AlignableSensorTOF*)getSensor(sensID);
if (sensor->isDummy()) {
LOGP(error, "Dummy sensor {} is referred by a track", sensID);
return 0;
}
double loc[3] = {(det[4] + 0.5) * o2::tof::Geo::XPAD - o2::tof::Geo::XHALFSTRIP, 0., (det[3] - 0.5) * o2::tof::Geo::ZPAD}, locCorr[3] = {}, traCorr[3] = {};
const auto& matAlg = sensor->getMatrixClAlg();
matAlg.LocalToMaster(loc, locCorr);
// rotate to tracking
const auto& matT2L = sensor->getMatrixT2L();
matT2L.MasterToLocal(locCorr, traCorr);

.
Are you sure the coordinates as they are set in the clustering are equivalent to those set in the matching?

@miranov25
Copy link
Copy Markdown
Contributor

Chiara just informed me that Francesco is on a trip, so we won't get an update soon. As we would like to test other aspects of the code in the test, I suggest merging it as it is and fixing the TOF alignment issue later. Do you agree? Can we proceed with the merge?
@shahor02, @matthias-kleiner ?

@matthias-kleiner matthias-kleiner marked this pull request as ready for review November 27, 2023 11:00
@matthias-kleiner
Copy link
Copy Markdown
Contributor Author

Chiara just informed me that Francesco is on a trip, so we won't get an update soon. As we would like to test other aspects of the code in the test, I suggest merging it as it is and fixing the TOF alignment issue later. Do you agree? Can we proceed with the merge? @shahor02, @matthias-kleiner ?

It is fine from my side. I removed the draft

@shahor02
Copy link
Copy Markdown
Collaborator

will merge after the CI but not before 4pm (in case @noferini responds)

@miranov25
Copy link
Copy Markdown
Contributor

Hello @shahor02
I think CI was done. There are only unresolved conversations, which we will resolve later.
Can we merge?
Thanks
Marian

@shahor02 shahor02 merged commit 9aad898 into AliceO2Group:dev Nov 27, 2023
@noferini
Copy link
Copy Markdown
Collaborator

@noferini actually, in the TOF matching you are not using TOF cluster positions as they are stored, using

Geo::getPadDxDyDz(posFloat, detIdTemp, deltaPosTemp, sec);

instead. Also, in the alignment I am recalculating the coordinates from the channels ideal positions with particular "starting" alignment matrices:

const auto& clus = TOFClusters[gid.getIndex()];
int det[5] = {}, ch = clus.getMainContributingChannel();
o2::tof::Geo::getVolumeIndices(ch, det);
int sensID = o2::tof::Geo::getStripNumberPerSM(det[1], det[2]) + clus.getSector() * o2::tof::Geo::NSTRIPXSECTOR;
auto* sensor = (AlignableSensorTOF*)getSensor(sensID);
if (sensor->isDummy()) {
LOGP(error, "Dummy sensor {} is referred by a track", sensID);
return 0;
}
double loc[3] = {(det[4] + 0.5) * o2::tof::Geo::XPAD - o2::tof::Geo::XHALFSTRIP, 0., (det[3] - 0.5) * o2::tof::Geo::ZPAD}, locCorr[3] = {}, traCorr[3] = {};
const auto& matAlg = sensor->getMatrixClAlg();
matAlg.LocalToMaster(loc, locCorr);
// rotate to tracking
const auto& matT2L = sensor->getMatrixT2L();
matT2L.MasterToLocal(locCorr, traCorr);

.

Are you sure the coordinates as they are set in the clustering are equivalent to those set in the matching?

Hi @shahor02 ,
sorry for the delay. Unfortunately during the day it is difficult for me to be online and check in the next 2 days.
I had a look at the code.
What is stored in tofclusters (XYZ) is defined in the TOFClusterer.cxx (line 275) and taken from Geo::GetPos(...) method.
In principle this takes the loaded geometry and I would not expect any difference wrt what done in MatchTOF but in the case the proper geometry is not loaded during clusterization.
I will try to compare the coordinates got in the two ways as soon as I can.

@noferini
Copy link
Copy Markdown
Collaborator

Hi, I did further checks inside the TOF matching algorithm comparing the position extracted with

  • getPadDxDyDz()
  • getPos
  • XYZ from tof clusters
    they are consistent

mwinn2 pushed a commit to mwinn2/AliceO2 that referenced this pull request Apr 25, 2024
)

* TPC timeseries: Propagating TPC track to TOF cluster

* Dont copy full TPC track

- storing delta parameter of TPC between inner and outer param
- small fixes

* Fixing deltaZ of TPC time when comparing to TOF cluster z

- storing getDXatTOF and getDZatTOF

* Fixing tpc only mode
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

4 participants