Skip to content
Snippets Groups Projects
HitFinder2D.cxx 9.22 KiB
/* Copyright (C) 2024 Facility for Antiproton and Ion Research in Europe, Darmstadt
   SPDX-License-Identifier: GPL-3.0-only
   Authors: Dominik Smith [committer], Alexandru Bercuci */

#include "HitFinder2D.h"

#include "AlgoFairloggerCompat.h"

#include <numeric>

using std::endl;
using std::vector;

namespace cbm::algo::trd
{

  //////////// TO DO: fdEdep is not used!!! Will be needed at some point.
  double HitFinder2D::fgDT[] = {4.181e-6, 1586, 24};

  //_______________________________________________________________________________
  HitFinder2D::HitFinder2D(HitFinder2DModPar params) : fParams(params) {}

  //_______________________________________________________________________________
  std::vector<trd::HitFinder2D::resultType> HitFinder2D::operator()(std::vector<Cluster2D>* clusters)
  {
    const int nclusters = clusters->size();
    std::vector<resultType> hitData;

    for (int icluster = 0; icluster < nclusters; icluster++) {
      auto& cluster = clusters->at(icluster);
      auto hit      = MakeHit(icluster, &cluster);
      if (hit.Address() == -1) continue;
      hitData.emplace_back(hit, cluster.GetRecDigis());
    }
    return hitData;
  }

  //_______________________________________________________________________________
  Hit HitFinder2D::MakeHit(int ic, const Cluster2D* cluster)
  {
    Hit hit;
    if (cluster->GetRecDigis().size() == 0) return hit;

    auto [hitFlag, hitF] = ProjectDigis(cluster);
    if (!hitFlag) return hit;

    hit.SetAddress(fParams.address);
    hit.SetRefId(ic);
    BuildHit(&hit, hitF);
    return hit;
  }


  //_______________________________________________________________________________
  bool HitFinder2D::BuildHit(Hit* h, HitFactory2D& hitF)
  {
    const int n0  = hitF.fSignal.size() - 2;
    auto [dx, dy] = hitF.GetDxDy(n0);

    // get position correction
    const double xcorr = hitF.GetXcorr(dx, hitF.GetHitClass()) / fParams.padSizeX;
    std::tie(dx, dy)   = hitF.CorrectPosition(dx, dy, xcorr, fParams.padSizeX, fParams.padSizeY);

    // get anode wire offset
    for (int ia = 0; ia <= NANODE; ia++) {
      const float ya = -1.35 + ia * 0.3;  //anode position in local pad coordinates
      if (dy > ya + 0.15) continue;
      break;
    }
    // Error parametrization X : parabolic model on cl size
    const double parX[] = {0.713724, -0.318667, 0.0366036};
    const double parY[] = {0.0886413, 0., 0.0435141};
    const int nex       = std::min(n0, 7);
    const double edx    = (n0 < 3) ? 1. : parX[0] + parX[1] * nex + parX[2] * nex * nex;
    const double edy    = (n0 < 3) ? 1. : parY[0] + parY[2] * dy * dy;
    const double edt    = (n0 < 3) ? 60. : 26.33;  // should be parametrized as function of da TODO

    // COMPUTE TIME
    double t_avg = 0.;
    for (int idx = 1; idx <= n0; idx++) {
      HitFactory2D::signal& sig = hitF.fSignal[idx];
      const double dtFEE        = fgDT[0] * (sig.s - fgDT[1]) * (sig.s - fgDT[1]) / fClk;
      t_avg += (sig.t - dtFEE);
      if (sig.xe > 0) sig.x += dy / fParams.padSizeY;
    }

    const double time   = (n0 > 1) ? t_avg / n0 : -21.;
    const double tdrift = 100.;  // should depend on Ua

    // COMPUTE ENERGY (conversion to GeV)
    const double e_avg = 1.e-9
                         * std::accumulate(hitF.fSignal.begin(), hitF.fSignal.end(), 0,
                                           [](int sum, const auto& sig) { return sum + sig.s; });

    CalibrateHit(h, dx, dy, edx, edy, edt, time, tdrift, e_avg, hitF);
    return kTRUE;
  }

  //_______________________________________________________________________________
  void HitFinder2D::CalibrateHit(Hit* h, const double dx, const double dy, const double edx, const double edy,
                                 const double edt, const double time, const double tdrift, const double eloss,
                                 const HitFactory2D& hitF)
  {
    const ROOT::Math::XYZVector& local_pad = fParams.rowPar[hitF.vrM].padPar[hitF.vcM].pos;
    const ROOT::Math::XYZVector local(local_pad.X() + dx, local_pad.Y() + dy, local_pad.Z());
    const ROOT::Math::XYZVector global = fParams.translation + fParams.rotation(local);
    h->SetX(global.X());
    h->SetY(global.Y());
    h->SetZ(global.Z());
    h->SetDx(edx);
    h->SetDy(edy);
    h->SetDz(0.);
    h->SetDxy(0.);
    h->SetTime(fClk * (hitF.vt0 + time) - tdrift + 30.29 + fHitTimeOff, edt);
    h->SetELoss(eloss);
    h->SetClassType();
    h->SetMaxType(hitF.IsMaxTilt());
    h->SetOverFlow(hitF.HasOvf());
  }

  //_______________________________________________________________________________
  std::pair<int, HitFactory2D> HitFinder2D::ProjectDigis(const Cluster2D* cluster)
  {
    /** Load digis information in working vectors Digis are represented in the normal coordinate system of
   * (pad width [pw], DAQ time [clk], signal [ADC chs]) with rectangular signals at integer
   * positions.
   */

    if (cluster->GetRecDigis().empty()) {
      L_(debug) << "TrdModuleRec2D::ProjectDigis : Requested cluster not found.";
      return std::make_pair(0, HitFactory2D(0));
    }
    HitFactory2D hitF(fParams.rowPar[0].padPar.size());

    std::vector<HitFactory2D::signal>& hitSig = hitF.fSignal;
    hitF.reset();

    bool on(0);              // flag signal transmition
    int n0(0), nsr(0),       // no of signals in the cluster (all/rect)
      NR(0),                 // no of rect signals/channel in case of RC
      NT(0),                 // no of tilt signals/channel in case of RC
      ovf(1);                // over-flow flag for at least one of the digis
    Char_t dt0(0);           // cluster time offset wrt arbitrary t0
    double err,              // final error parametrization for signal
      xc(-0.5),              // running signal-pad position
      max(0.);               // max signal
    const double re = 100.;  // rect signal
    const double te = 100.;  // tilt signal

    int j(0), col(-1), col0(0);

    for (auto i = cluster->GetRecDigis().begin(); i != cluster->GetRecDigis().end(); i++, j++) {
      const DigiRec* dg = &(*i);

      //  initialize
      if (col < 0) {
        hitF.vrM = GetPadRowCol(dg->GetAddressChannel(), col);
        hitF.vt0 = dg->GetTimeDAQ();  // set arbitrary t0 to avoid double digis loop
      }
      GetPadRowCol(dg->GetAddressChannel(), col0);
      int nt = 0;  // no of tilt signals/channel in case of RC
      int nr = 0;  // no of rect signals/channel in case of RC

      // read calibrated signals
      double t = dg->GetTiltCharge(on);
      if (on) nt = 1;
      double r = dg->GetRectCharge(on);
      if (on) nr = 1;

      //TO DO: two blocks below might be mergable
      // process tilt signal/time
      char ddt = dg->GetTiltTime() - hitF.vt0;  // signal time offset wrt prompt
      if (ddt < dt0) dt0 = ddt;
      if (abs(t) > 0) {
        if (nt > 1) t *= 0.5;
        err = te * (nt > 1 ? 0.707 : 1);
        if (dg->HasTiltOvf()) {
          ovf = -1;
          err = 150.;
        }
        if (t > max) {
          max      = t;
          hitF.vcM = j;
          hitF.SetMaxTilt(1);
          hitF.viM = hitSig.size();
        }
      }
      else
        err = 300.;

      hitSig.emplace_back(t, err, ddt, xc, 0.035);
      xc += 0.5;

      // process rect signal/time
      ddt = dg->GetRectTime() - hitF.vt0;  // signal time offset wrt prompt
      if (ddt < dt0) dt0 = ddt;
      if (abs(r) > 0) {
        nsr++;
        if (nr > 1) r *= 0.5;
        err = re * (nr > 1 ? 0.707 : 1);
        if (dg->HasRectOvf()) {
          ovf = -1;
          err = 150.;
        }
        if (r > max) {
          max      = r;
          hitF.vcM = j;
          hitF.SetMaxTilt(0);
          hitF.viM = hitSig.size();
        }
      }
      else
        err = 300.;

      hitSig.emplace_back(r, err, ddt, xc, 0.);
      xc += 0.5;
      NR += nr;
      NT += nt;
    }

    //TO DO: two blocks below might be mergable
    // add front and back anchor points if needed
    if (std::abs(hitSig[0].s) > 1.e-3) {
      xc       = hitSig[0].x;
      char ddt = hitSig[0].t;
      hitSig.emplace(hitSig.begin(), 0., 300., ddt, xc - 0.5, 0.);
      hitF.viM++;
    }
    int n(hitSig.size() - 1);
    if (std::abs(hitSig[n].s) > 1.e-3) {
      xc       = hitSig[n].x + 0.5;
      char ddt = hitSig[n].t;
      hitSig.emplace_back(0., 300., ddt, xc, 0.035);
    }

    n0 = hitSig.size() - 2;
    // compute cluster asymmetry
    int nR = n0 + 1 - hitF.viM;
    if (nR == hitF.viM) {
      hitF.SetSymmHit(1);
      if (hitSig.size() % 2) {
        double LS(0.), RS(0.);
        for (UChar_t idx(0); idx < hitF.viM; idx++)
          LS += hitSig[idx].s;
        for (uint idx(hitF.viM + 1); idx < hitSig.size(); idx++)
          RS += hitSig[idx].s;
        hitF.SetLeftSgn(LS < RS ? 0 : 1);
      }
    }
    else {
      hitF.SetSymmHit(0);
      if (hitF.viM < nR)
        hitF.SetLeftHit(0);
      else if (hitF.viM > nR)
        hitF.SetLeftHit(1);
    }
    // recenter time and space profile
    hitF.vt0 += dt0;
    for (auto& sig : hitSig) {
      sig.t -= dt0;
      sig.x -= hitF.vcM;
    }
    hitF.vcM += col;

    hitF.SetBiasX(0);
    hitF.SetBiasY(0);

    if (ovf < 0) hitF.SetOvf();
    return std::make_pair(ovf * (hitSig.size() - 2), hitF);
  }


  int HitFinder2D::GetPadRowCol(int address, int& c)
  {
    c = address % fParams.rowPar[0].padPar.size();
    return address / fParams.rowPar[0].padPar.size();
  }

}  // namespace cbm::algo::trd