/* Copyright (C) 2006-2020 GSI Helmholtzzentrum fuer Schwerionenforschung, Darmstadt
   SPDX-License-Identifier: GPL-3.0-only
   Authors: Ivan Kisel,  Sergey Gorbunov [committer], Igor Kulakov, Valentina Akishina, Grigory Kozlov */

/*
 *====================================================================
 *
 *  CBM Level 1 Reconstruction
 *
 *  Authors: I.Kisel,  S.Gorbunov
 *
 *  e-mail : ikisel@kip.uni-heidelberg.de
 *
 *====================================================================
 *
 *  L1 Fit performance 
 *
 *====================================================================
 */

#include "CbmL1.h"
#include "CbmL1Constants.h"
#include "CbmL1Counters.h"
#include "CbmMatch.h"
#include "CbmMuchPixelHit.h"
#include "CbmMuchPoint.h"
#include "CbmQaTable.h"
#include "CbmStsDigi.h"
#include "CbmStsHit.h"
#include "CbmStsPoint.h"
#include "CbmStsSetup.h"
#include "CbmStsStation.h"
#include "CbmTofHit.h"
#include "CbmTofPoint.h"
#include "CbmTrdHit.h"
#include "CbmTrdPoint.h"

#include "FairField.h"
#include "FairRunAna.h"
#include "FairTrackParam.h"  // for vertex pulls

#include "TFile.h"
#include "TH1.h"
#include "TH2.h"
#include "TMath.h"
#include "TNtuple.h"
#include "TProfile.h"

#include <boost/filesystem.hpp>

#include <iostream>
#include <list>
#include <map>
#include <vector>

#include "CaToolsDebugger.h"
#include "L1Algo/L1Algo.h"
#include "L1Algo/L1Def.h"
#include "L1Algo/L1Fit.h"

using std::cout;
using std::endl;
using std::ios;
using std::map;
using std::pair;
using std::setw;
using std::vector;

void CbmL1::TrackMatch()
{
  map<int, CbmL1MCTrack*> pMCTrackMap;
  pMCTrackMap.clear();

  // ***** DEBUG: Start
  for (int iTrk = 0; iTrk < int(fvMCTracks.size()); ++iTrk) {
    assert(iTrk == fvMCTracks[iTrk].ID);
  }
  // ***** DEBUG: End

  // fill pMCTrackMap
  for (vector<CbmL1MCTrack>::iterator i = fvMCTracks.begin(); i != fvMCTracks.end(); ++i) {
    CbmL1MCTrack& MC = *i;
    if (pMCTrackMap.find(MC.ID) == pMCTrackMap.end()) { pMCTrackMap.insert(pair<int, CbmL1MCTrack*>(MC.ID, &MC)); }
    else {
      cout << "*** L1: Track ID " << MC.ID << " encountered twice! ***" << endl;
    }
  }

  // -- prepare information about reconstructed tracks
  const int nRTracks = fvRecoTracks.size();
  for (int iR = 0; iR < nRTracks; iR++) {
    CbmL1Track* prtra = &(fvRecoTracks[iR]);

    //  cout<<iR<<" iR"<<endl;

    int hitsum = prtra->Hits.size();  // number of hits in track

    // count how many hits from each mcTrack belong to current recoTrack
    map<int, int>& hitmap = prtra->hitMap;  // how many hits from each mcTrack belong to current recoTrack
    map<int, int> hitmapChain;
    for (vector<int>::iterator ih = (prtra->Hits).begin(); ih != (prtra->Hits).end(); ++ih) {
      auto& vP = fvHitDebugInfo[*ih].GetMcPointIds();
      for (int iP : vP) {
        int ID      = -1;
        int chainID = -1;
        if (iP >= 0) {
          ID      = fvMCPoints[iP].ID;
          chainID = fvMCTracks[ID].chainID;
        }
        hitmap[ID]++;
        hitmapChain[chainID]++;
      }
    }  // for iHit

    // RTrack <-> MCTrack identification
    double max_percent = 0.0;  // [%]. maximum persent of hits, which belong to one mcTrack.
    for (map<int, int>::iterator posIt = hitmap.begin(); posIt != hitmap.end();
         posIt++) {  // loop over all touched MCTracks

      int iMcTrack     = posIt->first;
      int nMcTrackHits = posIt->second;

      if (iMcTrack < 0) continue;  // not a MC track - based on fake hits

      if (0) {  // debug: consider tracks from decay chains as one MC track
        nMcTrackHits = hitmapChain[fvMCTracks[iMcTrack].chainID];
      }

      double purity = double(nMcTrackHits) / double(hitsum);

      // count max-purity
      if (max_percent < purity) { max_percent = purity; }

      // set relation to the mcTrack
      assert(pMCTrackMap.find(iMcTrack) != pMCTrackMap.end());

      CbmL1MCTrack* pmtra = pMCTrackMap[iMcTrack];

      if (purity >= CbmL1Constants::MinPurity) {  // found correspondent MCTrack
        pmtra->AddRecoTrack(prtra);
        pmtra->AddRecoTrackIndex(iR);
        prtra->AddMCTrack(pmtra);
      }
      else {
        pmtra->AddTouchTrack(prtra);
        pmtra->AddTouchTrackIndex(iR);
      }
    }  // for hitmap
    prtra->SetMaxPurity(max_percent);

  }  // for reco tracks
}  //


struct TL1PerfEfficiencies : public TL1Efficiencies {
  TL1PerfEfficiencies()
    : TL1Efficiencies()
    , ratio_killed()
    , ratio_clone()
    , ratio_length()
    , ratio_fakes()
    , killed()
    , clone()
    , reco_length()
    , reco_fakes()
    , mc_length()
    , mc_length_hits()
  {
    // add total efficiency
    AddCounter("long_fast_prim", "LongRPrim efficiency");
    AddCounter("fast_prim", "FastPrim  efficiency");
    AddCounter("fast_sec", "FastSec   efficiency");
    AddCounter("fast", "Fastset   efficiency");
    AddCounter("total", "Allset    efficiency");
    AddCounter("slow_prim", "SlowPrim  efficiency");
    AddCounter("slow_sec", "SlowSec   efficiency");
    AddCounter("slow", "Slow      efficiency");
    AddCounter("d0", "D0        efficiency");
    AddCounter("short", "Short123s efficiency");
    AddCounter("shortPion", "Short123s pion   eff");
    AddCounter("shortProton", "Short123s proton eff");
    AddCounter("shortKaon", "Short123s kaon   eff");
    AddCounter("shortE", "Short123s e      eff");
    AddCounter("shortRest", "Short123s rest   eff");

    AddCounter("fast_sec_e", "FastSec E efficiency");
    AddCounter("fast_e", "Fastset E efficiency");
    AddCounter("total_e", "Allset  E efficiency");
    AddCounter("slow_sec_e", "SlowSec E efficiency");
    AddCounter("slow_e", "Slow    E efficiency");
  }

  virtual ~TL1PerfEfficiencies() {};

  virtual void AddCounter(const TString& shortname, const TString& name)
  {
    TL1Efficiencies::AddCounter(shortname, name);
    ratio_killed.AddCounter();
    ratio_clone.AddCounter();
    ratio_length.AddCounter();
    ratio_fakes.AddCounter();
    killed.AddCounter();
    clone.AddCounter();
    reco_length.AddCounter();
    reco_fakes.AddCounter();
    mc_length.AddCounter();
    mc_length_hits.AddCounter();
  };

  TL1PerfEfficiencies& operator+=(TL1PerfEfficiencies& a)
  {
    TL1Efficiencies::operator+=(a);
    killed += a.killed;
    clone += a.clone;
    reco_length += a.reco_length;
    reco_fakes += a.reco_fakes;
    mc_length += a.mc_length;
    mc_length_hits += a.mc_length_hits;
    return *this;
  };

  void CalcEff()
  {
    TL1Efficiencies::CalcEff();
    ratio_killed                      = killed / mc;
    ratio_clone                       = clone / mc;
    TL1TracksCatCounters<int> allReco = reco + clone;
    ratio_length                      = reco_length / allReco;
    ratio_fakes                       = reco_fakes / allReco;
  };

  void Inc(bool isReco, bool isKilled, double _ratio_length, double _ratio_fakes, int _nclones, int _mc_length,
           int _mc_length_hits, const TString& name)
  {
    TL1Efficiencies::Inc(isReco, name);

    const int index = indices[name];

    if (isKilled) killed.counters[index]++;
    reco_length.counters[index] += _ratio_length;
    reco_fakes.counters[index] += _ratio_fakes;
    clone.counters[index] += _nclones;
    mc_length.counters[index] += _mc_length;
    mc_length_hits.counters[index] += _mc_length_hits;
  };

  void PrintEff(bool ifPrintTableToLog = false, bool ifDeleteTable = false,
                const std::string& nameOfTable = "efficiency_table")
  {
    L1_assert(nEvents != 0);
    int NCounters = mc.GetNcounters();
    std::vector<std::string> rowNames(NCounters + 2);
    for (int iC = 0; iC < NCounters; ++iC) {
      rowNames[iC] = std::string(names[iC].Data());
    }
    rowNames[NCounters]               = "Ghost prob.";
    rowNames[NCounters + 1]           = "N ghosts";
    std::vector<std::string> colNames = {"Eff.",     "Killed", "Length",    "Fakes",    "Clones",
                                         "All Reco", "All MC", "MCl(hits)", "MCl(MCps)"};

    CbmQaTable* aTable = new CbmQaTable(nameOfTable.c_str(), "Track Efficiency", NCounters + 2, 9);
    aTable->SetColWidth(20);
    aTable->SetNamesOfRows(rowNames);
    aTable->SetNamesOfCols(colNames);
    for (int iC = 0; iC < NCounters; iC++) {
      aTable->SetCell(iC, 0, ratio_reco.counters[iC]);
      aTable->SetCell(iC, 1, ratio_killed.counters[iC]);
      aTable->SetCell(iC, 2, ratio_length.counters[iC]);
      aTable->SetCell(iC, 3, ratio_fakes.counters[iC]);
      aTable->SetCell(iC, 4, ratio_clone.counters[iC]);
      aTable->SetCell(iC, 5, reco.counters[iC] / double(nEvents));
      aTable->SetCell(iC, 6, mc.counters[iC] / double(nEvents));
      aTable->SetCell(iC, 7, mc_length_hits.counters[iC] / double(mc.counters[iC]));
      aTable->SetCell(iC, 8, mc_length.counters[iC] / double(mc.counters[iC]));
    }
    aTable->SetCell(NCounters, 0, ratio_ghosts);
    aTable->SetCell(NCounters + 1, 0, ghosts);

    if (ifPrintTableToLog) {
      cout << *aTable;  // print a table to log
    }
    if (!ifDeleteTable) { aTable->SetDirectory(fOutDir); }
    else {
      delete aTable;
    }
  };

  TL1TracksCatCounters<double> ratio_killed;
  TL1TracksCatCounters<double> ratio_clone;
  TL1TracksCatCounters<double> ratio_length;
  TL1TracksCatCounters<double> ratio_fakes;

  TL1TracksCatCounters<int> killed;
  TL1TracksCatCounters<int> clone;
  TL1TracksCatCounters<double> reco_length;
  TL1TracksCatCounters<double> reco_fakes;
  TL1TracksCatCounters<int> mc_length;
  TL1TracksCatCounters<int> mc_length_hits;

  TDirectory* fOutDir {nullptr};  // Specified for saving tables
};


void CbmL1::EfficienciesPerformance()
{
  static TL1PerfEfficiencies L1_NTRA;  // average efficiencies

  static int L1_NEVENTS   = 0;
  static double L1_CATIME = 0.0;


  TL1PerfEfficiencies ntra;     // efficiencies for current event
  ntra.fOutDir    = fTableDir;  // Setup a pointer for output directory
  L1_NTRA.fOutDir = fTableDir;  // Save average efficiencies

  ca::tools::Debugger::Instance().AddNtuple("ghost", "it:ih:p:x:y:z:t:dx:dy");
  static int statNghost = 0;

  for (vector<CbmL1Track>::iterator rtraIt = fvRecoTracks.begin(); rtraIt != fvRecoTracks.end(); ++rtraIt) {
    ntra.ghosts += rtraIt->IsGhost();
    if (0 && rtraIt->IsGhost()) {  // Debug.
      L1TrackPar tr;
      tr.copyFromArrays(rtraIt->T, rtraIt->C);

      cout << " L1: ghost track: nhits " << rtraIt->GetNOfHits() << " p " << 1. / rtraIt->T[4] << " purity "
           << rtraIt->GetMaxPurity() << " | hits ";
      for (map<int, int>::iterator posIt = rtraIt->hitMap.begin(); posIt != rtraIt->hitMap.end(); posIt++) {
        cout << " (" << posIt->second << " " << posIt->first << ") ";
      }
      cout << endl;
      for (map<int, int>::iterator posIt = rtraIt->hitMap.begin(); posIt != rtraIt->hitMap.end(); posIt++) {
        CbmL1MCTrack& t = fvMCTracks[posIt->first];
        cout << "mc " << posIt->first << " pdg " << t.pdg << " mother: " << t.mother_ID << " chain " << t.chainID;
        cout << " n mc stations: " << t.NMCStations() << endl;
      }
      for (unsigned int i = 0; i < rtraIt->Hits.size(); i++) {
        const L1Hit& h             = fpAlgo->fInputData.GetHit(rtraIt->Hits[i]);
        const CbmL1HitDebugInfo& s = fvHitDebugInfo[rtraIt->Hits[i]];
        cout << " x y z t " << s.x << " " << s.y << " " << h.z << " dx " << s.dx << " dy " << s.dy << std::endl;
        ca::tools::Debugger::Instance().FillNtuple("ghost", statNghost, i, fabs(1. / tr.qp[0]), s.x, s.y, h.z, h.t,
                                                   s.dx, s.dy);
      }
      tr.Print(0);
      statNghost++;
    }
  }

  int sta_nhits[fpAlgo->GetParameters()->GetNstationsActive()];
  int sta_nfakes[fpAlgo->GetParameters()->GetNstationsActive()];

  for (int i = 0; i < fpAlgo->GetParameters()->GetNstationsActive(); i++) {
    sta_nhits[i]  = 0;
    sta_nfakes[i] = 0;
  }

  for (vector<CbmL1MCTrack>::iterator mtraIt = fvMCTracks.begin(); mtraIt != fvMCTracks.end(); mtraIt++) {
    CbmL1MCTrack& mtra = *(mtraIt);

    //    if( !( mtra.pdg == -11 && mtra.mother_ID == -1 ) ) continue; // electrons only
    if (!mtra.IsReconstructable() && !mtra.IsAdditional()) continue;

    // -- find used constans --
    // is track reconstructed
    const bool reco = (mtra.IsReconstructed());
    // is track killed. At least one hit of it belong to some recoTrack
    const bool killed = !reco && mtra.IsDisturbed();
    // ration length for current mcTrack
    L1Vector<CbmL1Track*>& rTracks = mtra.GetRecoTracks();  // for length calculations
    double ratio_length            = 0;
    double ratio_fakes             = 0;
    double mc_length_hits          = mtra.NStations();


    int mc_length = mtra.NMCStations();
    if (reco) {
      for (unsigned int irt = 0; irt < rTracks.size(); irt++) {
        ratio_length += static_cast<double>(rTracks[irt]->GetNofHits()) * rTracks[irt]->GetMaxPurity() / mc_length_hits;
        ratio_fakes += 1 - rTracks[irt]->GetMaxPurity();
      }
    }


    // number of clones
    int nclones = 0;
    if (reco) nclones = mtra.GetNClones();
    //     if (nclones){ // Debug. Look at clones
    //       for (int irt = 0; irt < rTracks.size(); irt++){
    //         const int ista = fvHitDebugInfo[rTracks[irt]->Hits[0]].iStation;
    //         cout << rTracks[irt]->GetNOfHits() << "(" << ista << ") ";
    //       }
    //       cout << mtra.NStations() << endl;
    //     }

    if (mtra.IsAdditional()) {  // short
      ntra.Inc(reco, killed, ratio_length, ratio_fakes, nclones, mc_length, mc_length_hits, "short");
      switch (mtra.pdg) {
        case 11:
        case -11:
          ntra.Inc(reco, killed, ratio_length, ratio_fakes, nclones, mc_length, mc_length_hits, "shortE");
          break;
        case 211:
        case -211:
          ntra.Inc(reco, killed, ratio_length, ratio_fakes, nclones, mc_length, mc_length_hits, "shortPion");
          break;
        case 321:
        case -321:
          ntra.Inc(reco, killed, ratio_length, ratio_fakes, nclones, mc_length, mc_length_hits, "shortKaon");
          break;
        case 2212:
        case -2212:
          ntra.Inc(reco, killed, ratio_length, ratio_fakes, nclones, mc_length, mc_length_hits, "shortProton");
          break;
        default: ntra.Inc(reco, killed, ratio_length, ratio_fakes, nclones, mc_length, mc_length_hits, "shortRest");
      }
    }
    else {  // separate all efficiecies from short eff


      ntra.Inc(reco, killed, ratio_length, ratio_fakes, nclones, mc_length, mc_length_hits, "total");

      if (mtra.isSignal) {  // D0
        ntra.Inc(reco, killed, ratio_length, ratio_fakes, nclones, mc_length, mc_length_hits, "d0");
      }

      if (mtra.p > CbmL1Constants::MinFastMom) {  // fast tracks
        ntra.Inc(reco, killed, ratio_length, ratio_fakes, nclones, mc_length, mc_length_hits, "fast");

        if (mtra.IsPrimary()) {                  // fast primary
          if (mtra.NStations() == fNStations) {  // long fast primary
            ntra.Inc(reco, killed, ratio_length, ratio_fakes, nclones, mc_length, mc_length_hits, "long_fast_prim");
          }
          ntra.Inc(reco, killed, ratio_length, ratio_fakes, nclones, mc_length, mc_length_hits, "fast_prim");
        }
        else {  // fast secondary
          ntra.Inc(reco, killed, ratio_length, ratio_fakes, nclones, mc_length, mc_length_hits, "fast_sec");
        }
      }
      else {  // slow set of tracks
        ntra.Inc(reco, killed, ratio_length, ratio_fakes, nclones, mc_length, mc_length_hits, "slow");

        if (mtra.IsPrimary()) {  // slow primary
          ntra.Inc(reco, killed, ratio_length, ratio_fakes, nclones, mc_length, mc_length_hits, "slow_prim");
        }
        else {
          ntra.Inc(reco, killed, ratio_length, ratio_fakes, nclones, mc_length, mc_length_hits, "slow_sec");
        }
      }  // if slow

      if (mtra.pdg == 11 || mtra.pdg == -11) {
        ntra.Inc(reco, killed, ratio_length, ratio_fakes, nclones, mc_length, mc_length_hits, "total_e");

        if (mtra.p > CbmL1Constants::MinFastMom) {  // fast tracks
          ntra.Inc(reco, killed, ratio_length, ratio_fakes, nclones, mc_length, mc_length_hits, "fast_e");

          if (mtra.IsPrimary()) {  // fast primary
          }
          else {  // fast secondary
            ntra.Inc(reco, killed, ratio_length, ratio_fakes, nclones, mc_length, mc_length_hits, "fast_sec_e");
          }
        }
        else {  // slow set of tracks
          ntra.Inc(reco, killed, ratio_length, ratio_fakes, nclones, mc_length, mc_length_hits, "slow_e");

          if (mtra.IsPrimary()) {  // slow primary
          }
          else {
            ntra.Inc(reco, killed, ratio_length, ratio_fakes, nclones, mc_length, mc_length_hits, "slow_sec_e");
          }
        }  // if slow
      }
    }

  }  // for mcTracks

  L1_CATIME += fTrackingTime;
  L1_NEVENTS++;
  ntra.IncNEvents();
  L1_NTRA += ntra;

  ntra.CalcEff();
  L1_NTRA.CalcEff();

  //   cout.precision(3);
  if (fVerbose) {
    if (fVerbose > 1) {
      ntra.PrintEff(true, true);
      cout << "Number of true and fake hits in stations: " << endl;
      for (int i = 0; i < fpAlgo->GetParameters()->GetNstationsActive(); i++) {
        cout << sta_nhits[i] - sta_nfakes[i] << "+" << sta_nfakes[i] << "   ";
      }
      cout << endl;
    }  // fVerbose > 1
    cout << endl;
    cout << "L1 ACCUMULATED STAT    : " << L1_NEVENTS << " EVENTS " << endl << endl;
    L1_NTRA.PrintEff(/*ifPrintTableToLog = */ true, false);
    cout << "Reconstructible MC tracks/event: "
         << (double(L1_NTRA.mc.counters[L1_NTRA.indices["total"]]) / double(L1_NEVENTS)) << endl;
    cout << "Reconstructed MC tracks/event: "
         << (double(L1_NTRA.reco.counters[L1_NTRA.indices["total"]]) / double(L1_NEVENTS)) << endl;
    cout << endl;
    cout << "CA Track Finder: " << L1_CATIME / L1_NEVENTS << " s/time slice" << endl << endl;
  }
}  // void CbmL1::Performance()

void CbmL1::HistoPerformance()  // TODO: check if works correctly. Change vHitFast on match data in CbmL1**Track classes
{

  static TProfile *p_eff_all_vs_mom, *p_eff_prim_vs_mom, *p_eff_sec_vs_mom, *p_eff_d0_vs_mom, *p_eff_prim_vs_theta,
    *p_eff_all_vs_pt, *p_eff_prim_vs_pt, *p_eff_all_vs_nhits, *p_eff_prim_vs_nhits, *p_eff_sec_vs_nhits;

  static TH1F *h_reg_MCmom, *h_acc_MCmom, *h_reco_MCmom, *h_ghost_Rmom;
  static TH1F *h_reg_prim_MCmom, *h_acc_prim_MCmom, *h_reco_prim_MCmom;
  static TH1F *h_reg_sec_MCmom, *h_acc_sec_MCmom, *h_reco_sec_MCmom;

  static TH1F* h_acc_mom_short123s;

  static TH1F *h_reg_mom_prim, *h_reg_mom_sec, *h_reg_nhits_prim, *h_reg_nhits_sec;
  static TH1F *h_acc_mom_prim, *h_acc_mom_sec, *h_acc_nhits_prim, *h_acc_nhits_sec;
  static TH1F *h_reco_mom_prim, *h_reco_mom_sec, *h_reco_nhits_prim, *h_reco_nhits_sec;
  static TH1F *h_rest_mom_prim, *h_rest_mom_sec, *h_rest_nhits_prim, *h_rest_nhits_sec;

  //static TH1F *h_hit_density[10];

  static TH1F *h_ghost_purity, *h_ghost_mom, *h_ghost_nhits, *h_ghost_fstation, *h_ghost_chi2, *h_ghost_prob,
    *h_ghost_tx, *h_ghost_ty;
  static TH1F *h_reco_mom, *h_reco_d0_mom, *h_reco_nhits, *h_reco_station, *h_reco_chi2, *h_reco_prob, *h_rest_prob,
    *h_reco_purity, *h_reco_time;
  static TProfile *h_reco_timeNtr, *h_reco_timeNhit;
  static TProfile *h_reco_fakeNtr, *h_reco_fakeNhit;
  static TH1F *h_tx, *h_ty, *h_sec_r, *h_ghost_r;

  static TH1F *h_notfound_mom, *h_notfound_nhits, *h_notfound_station, *h_notfound_r, *h_notfound_tx, *h_notfound_ty;

  static TH1F *h_mcp, *h_nmchits;
  //  static TH1F *h_chi2, *h_prob, *MC_vx, *MC_vy, *MC_vz, *VtxChiPrim, *VtxChiSec;

  //  static TH2F *P_vs_P ;

  static TH2F *h2_vertex, *h2_prim_vertex, *h2_sec_vertex;
  //static TH3F *h3_vertex, *h3_prim_vertex, *h3_sec_vertex;

  static TH2F *h2_reg_nhits_vs_mom_prim, *h2_reg_nhits_vs_mom_sec, *h2_reg_fstation_vs_mom_prim,
    *h2_reg_fstation_vs_mom_sec, *h2_reg_lstation_vs_fstation_prim, *h2_reg_lstation_vs_fstation_sec;
  static TH2F *h2_acc_nhits_vs_mom_prim, *h2_acc_nhits_vs_mom_sec, *h2_acc_fstation_vs_mom_prim,
    *h2_acc_fstation_vs_mom_sec, *h2_acc_lstation_vs_fstation_prim, *h2_acc_lstation_vs_fstation_sec;
  static TH2F *h2_reco_nhits_vs_mom_prim, *h2_reco_nhits_vs_mom_sec, *h2_reco_fstation_vs_mom_prim,
    *h2_reco_fstation_vs_mom_sec, *h2_reco_lstation_vs_fstation_prim, *h2_reco_lstation_vs_fstation_sec;
  static TH2F *h2_ghost_nhits_vs_mom, *h2_ghost_fstation_vs_mom, *h2_ghost_lstation_vs_fstation;
  static TH2F *h2_rest_nhits_vs_mom_prim, *h2_rest_nhits_vs_mom_sec, *h2_rest_fstation_vs_mom_prim,
    *h2_rest_fstation_vs_mom_sec, *h2_rest_lstation_vs_fstation_prim, *h2_rest_lstation_vs_fstation_sec;

  static TH1F* h_reg_phi_prim;
  static TH1F* h_reg_phi_sec;
  static TH1F* h_acc_phi_prim;
  static TH1F* h_acc_phi_sec;
  static TH1F* h_reco_phi_prim;
  static TH1F* h_reco_phi_sec;
  static TH1F* h_rest_phi_prim;
  static TH1F* h_rest_phi_sec;
  static TH1F* h_ghost_phi;
  static TH1F* h_reco_phi;
  static TH1F* h_notfound_phi;

  static TH2F* h2_fst_hit_yz;  // occupancy of track first hit in y-z plane
  static TH2F* h2_lst_hit_yz;  // occupancy of track last hit in y-z plane
  static TH2F* h2_all_hit_yz;  // occupancy of track all hits in y-z plane

  static bool first_call = 1;

  if (first_call) {
    first_call = 0;

    TDirectory* curdir = gDirectory;
    gDirectory         = fHistoDir;

    h2_fst_hit_yz =
      new TH2F("h2_fst_hit_yz", "Track first hit occupancy in y-z plane;z [cm];y [cm]", 80, 0, 0, 80, 0, 0);
    h2_lst_hit_yz =
      new TH2F("h2_lst_hit_yz", "Track last hit occupancy in y-z plane;z[ cm];y [cm]", 80, 0, 0, 80, 0, 0);
    h2_all_hit_yz = new TH2F("h2_all_hit_yz", "Track hit occupancy in y-z plane;z[ cm];y [cm]", 80, 0, 0, 80, 0, 0);

    p_eff_all_vs_mom = new TProfile("p_eff_all_vs_mom", "AllSet Efficiency vs Momentum", 100, 0.0, 5.0, 0.0, 100.0);
    p_eff_prim_vs_mom =
      new TProfile("p_eff_prim_vs_mom", "Primary Set Efficiency vs Momentum", 100, 0.0, 5.0, 0.0, 100.0);
    p_eff_sec_vs_mom =
      new TProfile("p_eff_sec_vs_mom", "Secondary Set Efficiency vs Momentum", 100, 0.0, 5.0, 0.0, 100.0);
    p_eff_d0_vs_mom =
      new TProfile("p_eff_d0_vs_mom", "D0 Secondary Tracks Efficiency vs Momentum", 150, 0.0, 15.0, 0.0, 100.0);
    p_eff_prim_vs_theta =
      new TProfile("p_eff_prim_vs_theta", "All Primary Set Efficiency vs Theta", 100, 0.0, 30.0, 0.0, 100.0);
    p_eff_all_vs_pt  = new TProfile("p_eff_all_vs_pt", "AllSet Efficiency vs Pt", 100, 0.0, 5.0, 0.0, 100.0);
    p_eff_prim_vs_pt = new TProfile("p_eff_prim_vs_pt", "Primary Set Efficiency vs Pt", 100, 0.0, 5.0, 0.0, 100.0);

    p_eff_all_vs_nhits = new TProfile("p_eff_all_vs_nhits", "AllSet Efficiency vs NMCHits", 8, 3.0, 11.0, 0.0, 100.0);
    p_eff_prim_vs_nhits =
      new TProfile("p_eff_prim_vs_nhits", "PrimSet Efficiency vs NMCHits", 8, 3.0, 11.0, 0.0, 100.0);
    p_eff_sec_vs_nhits = new TProfile("p_eff_sec_vs_nhits", "SecSet Efficiency vs NMCHits", 8, 3.0, 11.0, 0.0, 100.0);

    h_reg_MCmom  = new TH1F("h_reg_MCmom", "Momentum of registered tracks", 100, 0.0, 5.0);
    h_acc_MCmom  = new TH1F("h_acc_MCmom", "Reconstructable tracks", 100, 0.0, 5.0);
    h_reco_MCmom = new TH1F("h_reco_MCmom", "Reconstructed tracks", 100, 0.0, 5.0);

    h_ghost_Rmom      = new TH1F("h_ghost_Rmom", "Ghost tracks", 100, 0.0, 5.0);
    h_reg_prim_MCmom  = new TH1F("h_reg_prim_MCmom", "Momentum of registered tracks", 100, 0.0, 5.0);
    h_acc_prim_MCmom  = new TH1F("h_acc_prim_MCmom", "Reconstructable tracks", 100, 0.0, 5.0);
    h_reco_prim_MCmom = new TH1F("h_reco_prim_MCmom", "Reconstructed tracks", 100, 0.0, 5.0);
    h_reg_sec_MCmom   = new TH1F("h_reg_sec_MCmom", "Momentum of registered tracks", 100, 0.0, 5.0);
    h_acc_sec_MCmom   = new TH1F("h_acc_sec_MCmom", "Reconstructable tracks", 100, 0.0, 5.0);
    h_reco_sec_MCmom  = new TH1F("h_reco_sec_MCmom", "Reconstructed tracks", 100, 0.0, 5.0);

    h_acc_mom_short123s =
      new TH1F("h_acc_mom_short123s", "Momentum of accepted tracks with 3 hits on first stations", 500, 0.0, 5.0);

    h_reg_mom_prim  = new TH1F("h_reg_mom_prim", "Momentum of registered primary tracks", 500, 0.0, 5.0);
    h_reg_mom_sec   = new TH1F("h_reg_mom_sec", "Momentum of registered secondary tracks", 500, 0.0, 5.0);
    h_acc_mom_prim  = new TH1F("h_acc_mom_prim", "Momentum of accepted primary tracks", 500, 0.0, 5.0);
    h_acc_mom_sec   = new TH1F("h_acc_mom_sec", "Momentum of accepted secondary tracks", 500, 0.0, 5.0);
    h_reco_mom_prim = new TH1F("h_reco_mom_prim", "Momentum of reconstructed primary tracks", 500, 0.0, 5.0);
    h_reco_mom_sec  = new TH1F("h_reco_mom_sec", "Momentum of reconstructed secondary tracks", 500, 0.0, 5.0);
    h_rest_mom_prim = new TH1F("h_rest_mom_prim", "Momentum of not found primary tracks", 500, 0.0, 5.0);
    h_rest_mom_sec  = new TH1F("h_rest_mom_sec", "Momentum of not found secondary tracks", 500, 0.0, 5.0);

    h_reg_phi_prim  = new TH1F("h_reg_phi_prim", "Azimuthal angle of registered primary tracks", 1000, -3.15, 3.15);
    h_reg_phi_sec   = new TH1F("h_reg_phi_sec", "Azimuthal angle of registered secondary tracks", 1000, -3.15, 3.15);
    h_acc_phi_prim  = new TH1F("h_acc_phi_prim", "Azimuthal angle of accepted primary tracks", 1000, -3.15, 3.15);
    h_acc_phi_sec   = new TH1F("h_acc_phi_sec", "Azimuthal angle of accepted secondary tracks", 1000, -3.15, 3.15);
    h_reco_phi_prim = new TH1F("h_reco_phi_prim", "Azimuthal angle of reconstructed primary tracks", 1000, -3.15, 3.15);
    h_reco_phi_sec = new TH1F("h_reco_phi_sec", "Azimuthal angle of reconstructed secondary tracks", 1000, -3.15, 3.15);
    h_rest_phi_prim = new TH1F("h_rest_phi_prim", "Azimuthal angle of not found primary tracks", 1000, -3.15, 3.15);
    h_rest_phi_sec  = new TH1F("h_rest_phi_sec", "Azimuthal angle of not found secondary tracks", 1000, -3.15, 3.15);

    h_reg_nhits_prim  = new TH1F("h_reg_nhits_prim", "Number of hits in registered primary track", 51, -0.1, 10.1);
    h_reg_nhits_sec   = new TH1F("h_reg_nhits_sec", "Number of hits in registered secondary track", 51, -0.1, 10.1);
    h_acc_nhits_prim  = new TH1F("h_acc_nhits_prim", "Number of hits in accepted primary track", 51, -0.1, 10.1);
    h_acc_nhits_sec   = new TH1F("h_acc_nhits_sec", "Number of hits in accepted secondary track", 51, -0.1, 10.1);
    h_reco_nhits_prim = new TH1F("h_reco_nhits_prim", "Number of hits in reconstructed primary track", 51, -0.1, 10.1);
    h_reco_nhits_sec  = new TH1F("h_reco_nhits_sec", "Number of hits in reconstructed secondary track", 51, -0.1, 10.1);
    h_rest_nhits_prim = new TH1F("h_rest_nhits_prim", "Number of hits in not found primary track", 51, -0.1, 10.1);
    h_rest_nhits_sec  = new TH1F("h_rest_nhits_sec", "Number of hits in not found secondary track", 51, -0.1, 10.1);

    h_ghost_mom      = new TH1F("h_ghost_mom", "Momentum of ghost tracks", 500, 0.0, 5.0);
    h_ghost_phi      = new TH1F("h_ghost_phi", "Azimuthal angle of ghost tracks", 1000, -3.15, 3.15);
    h_ghost_nhits    = new TH1F("h_ghost_nhits", "Number of hits in ghost track", 51, -0.1, 10.1);
    h_ghost_fstation = new TH1F("h_ghost_fstation", "First station of ghost track", 50, -0.5, 10.0);
    h_ghost_chi2     = new TH1F("h_ghost_chi2", "Chi2/NDF of ghost track", 50, -0.5, 10.0);
    h_ghost_prob     = new TH1F("h_ghost_prob", "Prob of ghost track", 505, 0., 1.01);
    h_ghost_r        = new TH1F("h_ghost_r", "R of ghost track at the first hit", 50, 0.0, 150.0);
    h_ghost_tx       = new TH1F("h_ghost_tx", "tx of ghost track at the first hit", 50, -5.0, 5.0);
    h_ghost_ty       = new TH1F("h_ghost_ty", "ty of ghost track at the first hit", 50, -1.0, 1.0);
    h_ghost_purity   = new TH1F("h_ghost_purity", "Ghost: percentage of correct hits", 100, -0.5, 100.5);

    h_reco_mom      = new TH1F("h_reco_mom", "Momentum of reco track", 50, 0.0, 5.0);
    h_reco_phi      = new TH1F("h_reco_phi", "Azimuthal angle of reco track", 1000, -3.15, 3.15);
    h_reco_nhits    = new TH1F("h_reco_nhits", "Number of hits in reco track", 50, 0.0, 10.0);
    h_reco_station  = new TH1F("h_reco_station", "First station of reco track", 50, -0.5, 10.0);
    h_reco_chi2     = new TH1F("h_reco_chi2", "Chi2/NDF of reco track", 50, -0.5, 10.0);
    h_reco_prob     = new TH1F("h_reco_prob", "Prob of reco track", 505, 0., 1.01);
    h_rest_prob     = new TH1F("h_rest_prob", "Prob of reco rest track", 505, 0., 1.01);
    h_reco_purity   = new TH1F("h_reco_purity", "Percentage of correct hits", 100, -0.5, 100.5);
    h_reco_time     = new TH1F("h_reco_time", "CA Track Finder Time (s/ev)", 20, 0.0, 20.0);
    h_reco_timeNtr  = new TProfile("h_reco_timeNtr", "CA Track Finder Time (s/ev) vs N Tracks", 200, 0.0, 1000.0);
    h_reco_timeNhit = new TProfile("h_reco_timeNhit", "CA Track Finder Time (s/ev) vs N Hits", 200, 0.0, 30000.0);
    h_reco_fakeNtr  = new TProfile("h_reco_fakeNtr", "N Fake hits vs N Tracks", 200, 0.0, 1000.0);
    h_reco_fakeNhit = new TProfile("h_reco_fakeNhit", "N Fake hits vs N Hits", 200, 0.0, 30000.0);

    h_reco_d0_mom = new TH1F("h_reco_d0_mom", "Momentum of reco D0 track", 150, 0.0, 15.0);

    //     h_hit_density[0] = new TH1F("h_hit_density0", "Hit density station 1", 50, 0.0,  5.00);
    //     h_hit_density[1] = new TH1F("h_hit_density1", "Hit density station 2", 100, 0.0, 10.00);
    //     h_hit_density[2] = new TH1F("h_hit_density2", "Hit density station 3", 140, 0.0, 13.99);
    //     h_hit_density[3] = new TH1F("h_hit_density3", "Hit density station 4", 180, 0.0, 18.65);
    //     h_hit_density[4] = new TH1F("h_hit_density4", "Hit density station 5", 230, 0.0, 23.32);
    //     h_hit_density[5] = new TH1F("h_hit_density5", "Hit density station 6", 270, 0.0, 27.98);
    //     h_hit_density[6] = new TH1F("h_hit_density6", "Hit density station 7", 340, 0.0, 34.97);
    //     h_hit_density[7] = new TH1F("h_hit_density7", "Hit density station 8", 460, 0.0, 46.63);
    //     h_hit_density[8] = new TH1F("h_hit_density8", "Hit density station 9", 500, 0.0, 50.00);
    //     h_hit_density[9] = new TH1F("h_hit_density8", "Hit density station 9", 500, 0.0, 50.00);
    //     h_hit_density[10] = new TH1F("h_hit_density8", "Hit density station 9", 500, 0.0, 50.00);

    h_tx = new TH1F("h_tx", "tx of MC track at the vertex", 50, -0.5, 0.5);
    h_ty = new TH1F("h_ty", "ty of MC track at the vertex", 50, -0.5, 0.5);

    h_sec_r = new TH1F("h_sec_r", "R of sec MC track at the first hit", 50, 0.0, 15.0);

    h_notfound_mom     = new TH1F("h_notfound_mom", "Momentum of not found track", 50, 0.0, 5.0);
    h_notfound_phi     = new TH1F("h_notfound_phi", "Momentum of not found track", 50, 0.0, 5.0);
    h_notfound_nhits   = new TH1F("h_notfound_nhits", "Num hits of not found track", 50, 0.0, 10.0);
    h_notfound_station = new TH1F("h_notfound_station", "First station of not found track", 50, 0.0, 10.0);
    h_notfound_r       = new TH1F("h_notfound_r", "R at first hit of not found track", 50, 0.0, 15.0);
    h_notfound_tx      = new TH1F("h_notfound_tx", "tx of not found track at the first hit", 50, -5.0, 5.0);
    h_notfound_ty      = new TH1F("h_notfound_ty", "ty of not found track at the first hit", 50, -1.0, 1.0);

    /*
    h_chi2 = new TH1F("chi2", "Chi^2", 100, 0.0, 10.);
    h_prob = new TH1F("prob", "Prob", 100, 0.0, 1.01);
    VtxChiPrim = new TH1F("vtxChiPrim", "Chi to primary vtx for primary tracks", 100, 0.0, 10.);
    VtxChiSec = new TH1F("vtxChiSec", "Chi to primary vtx for secondary tracks", 100, 0.0, 10.);
*/
    h_mcp = new TH1F("h_mcp", "MC P ", 500, 0.0, 5.0);
    /*
    MC_vx = new TH1F("MC_vx", "MC Vertex X", 100, -.05, .05);
    MC_vy = new TH1F("MC_vy", "MC Vertex Y", 100, -.05, .05);
    MC_vz = new TH1F("MC_vz", "MC Vertex Z", 100, -.05, .05);
*/
    h_nmchits = new TH1F("h_nmchits", "N STS hits on MC track", 50, 0.0, 10.0);

    //    P_vs_P = new TH2F("P_vs_P", "Resolution P/Q vs P", 20, 0., 20.,100, -.05, .05);

    h2_vertex      = new TH2F("h2_vertex", "2D vertex distribution", 105, -5., 100., 100, -50., 50.);
    h2_prim_vertex = new TH2F("h2_primvertex", "2D primary vertex distribution", 105, -5., 100., 100, -50., 50.);
    h2_sec_vertex  = new TH2F("h2_sec_vertex", "2D secondary vertex distribution", 105, -5., 100., 100, -50., 50.);

    //h3_vertex = new TH3F("h3_vertex", "3D vertex distribution", 20, -5., 100., 100, -50., 50., 100, -50., 50.);
    //h3_prim_vertex = new TH3F("h3_primvertex", "3D vertex distribution", 20, -5., 100., 100, -50., 50., 100, -50., 50.);
    //h3_sec_vertex = new TH3F("h3_sec_vertex", "3D vertex distribution", 20, -5., 100., 100, -50., 50., 100, -50., 50.);

    h2_reg_nhits_vs_mom_prim =
      new TH2F("h2_reg_nhits_vs_mom_prim", "NHits vs. Momentum (Reg. Primary Tracks)", 51, -0.05, 5.05, 11, -0.5, 10.5);
    h2_reg_nhits_vs_mom_sec = new TH2F("h2_reg_nhits_vs_mom_sec", "NHits vs. Momentum (Reg. Secondary Tracks)", 51,
                                       -0.05, 5.05, 11, -0.5, 10.5);
    h2_acc_nhits_vs_mom_prim =
      new TH2F("h2_acc_nhits_vs_mom_prim", "NHits vs. Momentum (Acc. Primary Tracks)", 51, -0.05, 5.05, 11, -0.5, 10.5);
    h2_acc_nhits_vs_mom_sec   = new TH2F("h2_acc_nhits_vs_mom_sec", "NHits vs. Momentum (Acc. Secondary Tracks)", 51,
                                       -0.05, 5.05, 11, -0.5, 10.5);
    h2_reco_nhits_vs_mom_prim = new TH2F("h2_reco_nhits_vs_mom_prim", "NHits vs. Momentum (Reco Primary Tracks)", 51,
                                         -0.05, 5.05, 11, -0.5, 10.5);
    h2_reco_nhits_vs_mom_sec  = new TH2F("h2_reco_nhits_vs_mom_sec", "NHits vs. Momentum (Reco Secondary Tracks)", 51,
                                        -0.05, 5.05, 11, -0.5, 10.5);
    h2_ghost_nhits_vs_mom =
      new TH2F("h2_ghost_nhits_vs_mom", "NHits vs. Momentum (Ghost Tracks)", 51, -0.05, 5.05, 11, -0.5, 10.5);
    h2_rest_nhits_vs_mom_prim = new TH2F("h2_rest_nhits_vs_mom_prim", "NHits vs. Momentum (!Found Primary Tracks)", 51,
                                         -0.05, 5.05, 11, -0.5, 10.5);
    h2_rest_nhits_vs_mom_sec  = new TH2F("h2_rest_nhits_vs_mom_sec", "NHits vs. Momentum (!Found Secondary Tracks)", 51,
                                        -0.05, 5.05, 11, -0.5, 10.5);

    h2_reg_fstation_vs_mom_prim =
      new TH2F("h2_reg_fstation_vs_mom_prim", "First Station vs. Momentum (Reg. Primary Tracks)", 51, -0.05, 5.05, 11,
               -0.5, 10.5);
    h2_reg_fstation_vs_mom_sec =
      new TH2F("h2_reg_fstation_vs_mom_sec", "First Station vs. Momentum (Reg. Secondary Tracks)", 51, -0.05, 5.05, 11,
               -0.5, 10.5);
    h2_acc_fstation_vs_mom_prim =
      new TH2F("h2_acc_fstation_vs_mom_prim", "First Station vs. Momentum (Acc. Primary Tracks)", 51, -0.05, 5.05, 11,
               -0.5, 10.5);
    h2_acc_fstation_vs_mom_sec =
      new TH2F("h2_acc_fstation_vs_mom_sec", "First Station vs. Momentum (Acc. Secondary Tracks)", 51, -0.05, 5.05, 11,
               -0.5, 10.5);
    h2_reco_fstation_vs_mom_prim =
      new TH2F("h2_reco_fstation_vs_mom_prim", "First Station vs. Momentum (Reco Primary Tracks)", 51, -0.05, 5.05, 11,
               -0.5, 10.5);
    h2_reco_fstation_vs_mom_sec =
      new TH2F("h2_reco_fstation_vs_mom_sec", "First Station vs. Momentum (Reco Secondary Tracks)", 51, -0.05, 5.05, 11,
               -0.5, 10.5);
    h2_ghost_fstation_vs_mom = new TH2F("h2_ghost_fstation_vs_mom", "First Station vs. Momentum (Ghost Tracks)", 51,
                                        -0.05, 5.05, 11, -0.5, 10.5);
    h2_rest_fstation_vs_mom_prim =
      new TH2F("h2_rest_fstation_vs_mom_prim", "First Station vs. Momentum (!Found Primary Tracks)", 51, -0.05, 5.05,
               11, -0.5, 10.5);
    h2_rest_fstation_vs_mom_sec =
      new TH2F("h2_rest_fstation_vs_mom_sec", "First Station vs. Momentum (!Found Secondary Tracks)", 51, -0.05, 5.05,
               11, -0.5, 10.5);

    h2_reg_lstation_vs_fstation_prim =
      new TH2F("h2_reg_lstation_vs_fstation_prim", "Last vs. First Station (Reg. Primary Tracks)", 11, -0.5, 10.5, 11,
               -0.5, 10.5);
    h2_reg_lstation_vs_fstation_sec =
      new TH2F("h2_reg_lstation_vs_fstation_sec", "Last vs. First Station (Reg. Secondary Tracks)", 11, -0.5, 10.5, 11,
               -0.5, 10.5);
    h2_acc_lstation_vs_fstation_prim =
      new TH2F("h2_acc_lstation_vs_fstation_prim", "Last vs. First Station (Acc. Primary Tracks)", 11, -0.5, 10.5, 11,
               -0.5, 10.5);
    h2_acc_lstation_vs_fstation_sec =
      new TH2F("h2_acc_lstation_vs_fstation_sec", "Last vs. First Station (Acc. Secondary Tracks)", 11, -0.5, 10.5, 11,
               -0.5, 10.5);
    h2_reco_lstation_vs_fstation_prim =
      new TH2F("h2_reco_lstation_vs_fstation_prim", "Last vs. First Station (Reco Primary Tracks)", 11, -0.5, 10.5, 11,
               -0.5, 10.5);
    h2_reco_lstation_vs_fstation_sec =
      new TH2F("h2_reco_lstation_vs_fstation_sec", "Last vs. First Station (Reco Secondary Tracks)", 11, -0.5, 10.5, 11,
               -0.5, 10.5);
    h2_ghost_lstation_vs_fstation = new TH2F("h2_ghost_lstation_vs_fstation", "Last vs. First Station (Ghost Tracks)",
                                             11, -0.5, 10.5, 11, -0.5, 10.5);
    h2_rest_lstation_vs_fstation_prim =
      new TH2F("h2_rest_lstation_vs_fstation_prim", "Last vs. First Station (!Found Primary Tracks)", 11, -0.5, 10.5,
               11, -0.5, 10.5);
    h2_rest_lstation_vs_fstation_sec =
      new TH2F("h2_rest_lstation_vs_fstation_sec", "Last vs. First Station (!Found Secondary Tracks)", 11, -0.5, 10.5,
               11, -0.5, 10.5);

    //maindir->cd();

    // ----- Create list of all histogram pointers

    gDirectory = curdir;

  }  // first_call


  static int NEvents = 0;
  if (NEvents > 0) {
    h_reg_MCmom->Scale(NEvents);
    h_acc_MCmom->Scale(NEvents);
    h_reco_MCmom->Scale(NEvents);
    h_ghost_Rmom->Scale(NEvents);
    h_reg_prim_MCmom->Scale(NEvents);
    h_acc_prim_MCmom->Scale(NEvents);
    h_reco_prim_MCmom->Scale(NEvents);
    h_reg_sec_MCmom->Scale(NEvents);
    h_acc_sec_MCmom->Scale(NEvents);
    h_reco_sec_MCmom->Scale(NEvents);
  }
  NEvents++;

  //   //hit density calculation: h_hit_density[10]
  //
  //   for (vector<CbmL1HitDebugInfo>::iterator hIt = fvHitDebugInfo.begin(); hIt != fvHitDebugInfo.end(); ++hIt){
  //     float x = hIt->x;
  //     float y = hIt->y;
  //     float r = sqrt(x*x+y*y);
  //     h_hit_density[hIt->iStation]->Fill(r, 1.0/(2.0*3.1415*r));
  //   }

  //
  for (vector<CbmL1Track>::iterator rtraIt = fvRecoTracks.begin(); rtraIt != fvRecoTracks.end(); ++rtraIt) {
    CbmL1Track* prtra = &(*rtraIt);
    if ((prtra->Hits).size() < 1) continue;
    {  // fill histos
      if (fabs(prtra->T[4]) > 1.e-10)
        h_reco_mom->Fill(
          fabs(1.0 / prtra->T[4]));  // TODO: Is it a right precision? In FairTrackParam it is 1.e-4 (S.Zharko)
      // NOTE: p = (TMath::Abs(fQp) > 1.e-4) ? 1. / TMath::Abs(fQp) : 1.e4; // FairTrackParam::Momentum(TVector3)
      // h_reco_mom->Fill(TMath::Abs(prtra->T[4] > 1.e-4) ? 1. / TMath::Abs(prtra->T[4]) : 1.e+4); // this should be correct

      h_reco_phi->Fill(TMath::ATan2(-prtra->T[3], -prtra->T[2]));  // TODO: What is precision?
      h_reco_nhits->Fill((prtra->Hits).size());
      CbmL1HitDebugInfo& mh = fvHitDebugInfo[prtra->Hits[0]];
      h_reco_station->Fill(mh.iStation);

      int iFstHit  = prtra->GetFirstHitIndex();
      auto& fstHit = fvHitDebugInfo[iFstHit];
      h2_fst_hit_yz->Fill(fpAlgo->GetParameters()->GetStation(fstHit.iStation).fZ[0], fstHit.GetY());

      int iLstHit  = prtra->GetLastHitIndex();
      auto& lstHit = fvHitDebugInfo[iLstHit];
      h2_lst_hit_yz->Fill(fpAlgo->GetParameters()->GetStation(lstHit.iStation).fZ[0], lstHit.GetY());

      for (int iH : prtra->Hits) {
        const auto& hit = fvHitDebugInfo[iH];
        int y           = hit.GetY();
        int z           = fpAlgo->GetParameters()->GetStation(hit.iStation).fZ[0];
        h2_all_hit_yz->Fill(z, y);
      }
    }


    h_reco_purity->Fill(100 * prtra->GetMaxPurity());

    if (prtra->NDF > 0) {
      if (prtra->IsGhost()) {
        h_ghost_chi2->Fill(prtra->chi2 / prtra->NDF);
        h_ghost_prob->Fill(TMath::Prob(prtra->chi2, prtra->NDF));
      }
      else if (prtra->GetNMCTracks() > 0) {
        if (prtra->GetMCTrack()[0].IsReconstructable()) {
          h_reco_chi2->Fill(prtra->chi2 / prtra->NDF);
          h_reco_prob->Fill(TMath::Prob(prtra->chi2, prtra->NDF));
        }
        else {
          //          h_rest_chi2->Fill(prtra->chi2/prtra->NDF);
          h_rest_prob->Fill(TMath::Prob(prtra->chi2, prtra->NDF));
        }
      }
    }


    // fill ghost histos
    if (prtra->IsGhost()) {
      fMonitor.Increment(EMonitorKey::kGhostTrack);
      h_ghost_purity->Fill(100 * prtra->GetMaxPurity());
      if (fabs(prtra->T[4]) > 1.e-10) {
        h_ghost_mom->Fill(fabs(1.0 / prtra->T[4]));
        h_ghost_phi->Fill(atan(prtra->T[3] / prtra->T[2]));  // phi = atan(py / px) = atan(ty / tx)
        h_ghost_Rmom->Fill(fabs(1.0 / prtra->T[4]));
      }
      h_ghost_nhits->Fill((prtra->Hits).size());
      CbmL1HitDebugInfo& h1 = fvHitDebugInfo[prtra->Hits[0]];
      CbmL1HitDebugInfo& h2 = fvHitDebugInfo[prtra->Hits[1]];
      h_ghost_fstation->Fill(h1.iStation);
      h_ghost_r->Fill(sqrt(fabs(h1.x * h1.x + h1.y * h1.y)));
      double z1 = fpAlgo->GetParameters()->GetStation(h1.iStation).fZ[0];
      double z2 = fpAlgo->GetParameters()->GetStation(h2.iStation).fZ[0];
      if (fabs(z2 - z1) > 1.e-4) {
        h_ghost_tx->Fill((h2.x - h1.x) / (z2 - z1));
        h_ghost_ty->Fill((h2.y - h1.y) / (z2 - z1));
      }

      if (fabs(prtra->T[4]) > 1.e-10) h2_ghost_nhits_vs_mom->Fill(fabs(1.0 / prtra->T[4]), (prtra->Hits).size());
      CbmL1HitDebugInfo& hf = fvHitDebugInfo[prtra->Hits[0]];
      CbmL1HitDebugInfo& hl = fvHitDebugInfo[prtra->Hits[(prtra->Hits).size() - 1]];
      if (fabs(prtra->T[4]) > 1.e-10) h2_ghost_fstation_vs_mom->Fill(fabs(1.0 / prtra->T[4]), hf.iStation + 1);
      if (hl.iStation >= hf.iStation) h2_ghost_lstation_vs_fstation->Fill(hf.iStation + 1, hl.iStation + 1);
    }

  }  // for reco tracks

  int mc_total = 0;  // total amount of reconstructable mcTracks
  for (vector<CbmL1MCTrack>::iterator mtraIt = fvMCTracks.begin(); mtraIt != fvMCTracks.end(); mtraIt++) {
    CbmL1MCTrack& mtra = *(mtraIt);
    //    if( !( mtra.pdg == -11 && mtra.mother_ID == -1 ) ) continue; // electrons only

    // No Sts hits?
    int nmchits = mtra.Hits.size();
    if (nmchits == 0) continue;

    double momentum = mtra.p;
    double pt       = sqrt(mtra.px * mtra.px + mtra.py * mtra.py);
    double theta    = acos(mtra.pz / mtra.p) * 180 / 3.1415;
    double phi      = TMath::ATan2(-mtra.py, -mtra.px);

    h_mcp->Fill(momentum);
    h_nmchits->Fill(nmchits);

    int nSta = mtra.NStations();

    CbmL1HitDebugInfo& fh = fvHitDebugInfo[*(mtra.Hits.begin())];
    CbmL1HitDebugInfo& lh = fvHitDebugInfo[*(mtra.Hits.rbegin())];

    h_reg_MCmom->Fill(momentum);
    if (mtra.IsPrimary()) {
      h_reg_mom_prim->Fill(momentum);
      h_reg_phi_prim->Fill(phi);
      h_reg_prim_MCmom->Fill(momentum);
      h_reg_nhits_prim->Fill(nSta);
      h2_reg_nhits_vs_mom_prim->Fill(momentum, nSta);
      h2_reg_fstation_vs_mom_prim->Fill(momentum, fh.iStation + 1);
      if (lh.iStation >= fh.iStation) h2_reg_lstation_vs_fstation_prim->Fill(fh.iStation + 1, lh.iStation + 1);
    }
    else {
      h_reg_mom_sec->Fill(momentum);
      h_reg_phi_sec->Fill(phi);
      h_reg_sec_MCmom->Fill(momentum);
      h_reg_nhits_sec->Fill(nSta);
      if (momentum > 0.01) {
        h2_reg_nhits_vs_mom_sec->Fill(momentum, nSta);
        h2_reg_fstation_vs_mom_sec->Fill(momentum, fh.iStation + 1);
        if (lh.iStation >= fh.iStation) h2_reg_lstation_vs_fstation_sec->Fill(fh.iStation + 1, lh.iStation + 1);
      }
    }

    if (mtra.IsAdditional()) { h_acc_mom_short123s->Fill(momentum); }

    if (!mtra.IsReconstructable()) continue;
    mc_total++;

    h_acc_MCmom->Fill(momentum);
    if (mtra.IsPrimary()) {
      h_acc_mom_prim->Fill(momentum);
      h_acc_phi_prim->Fill(phi);
      h_acc_prim_MCmom->Fill(momentum);
      h_acc_nhits_prim->Fill(nSta);
      h2_acc_nhits_vs_mom_prim->Fill(momentum, nSta);
      h2_acc_fstation_vs_mom_prim->Fill(momentum, fh.iStation + 1);
      if (lh.iStation >= fh.iStation) h2_acc_lstation_vs_fstation_prim->Fill(fh.iStation + 1, lh.iStation + 1);
    }
    else {
      h_acc_mom_sec->Fill(momentum);
      h_acc_phi_sec->Fill(phi);
      h_acc_sec_MCmom->Fill(momentum);
      h_acc_nhits_sec->Fill(nSta);
      if (momentum > 0.01) {
        h2_acc_nhits_vs_mom_sec->Fill(momentum, nSta);
        h2_acc_fstation_vs_mom_sec->Fill(momentum, fh.iStation + 1);
        if (lh.iStation >= fh.iStation) h2_acc_lstation_vs_fstation_sec->Fill(fh.iStation + 1, lh.iStation + 1);
      }
    }


    // vertex distribution of primary and secondary tracks
    h2_vertex->Fill(mtra.z, mtra.y);
    //h3_vertex->Fill(mtra.z, mtra.x, mtra.y);
    if (mtra.mother_ID < 0) {  // primary
      h2_prim_vertex->Fill(mtra.z, mtra.y);
      //h3_prim_vertex->Fill(mtra.z, mtra.x, mtra.y);
    }
    else {
      h2_sec_vertex->Fill(mtra.z, mtra.y);
      //h3_sec_vertex->Fill(mtra.z, mtra.x, mtra.y);
    }


    int iph               = mtra.Hits[0];
    CbmL1HitDebugInfo& ph = fvHitDebugInfo[iph];

    h_sec_r->Fill(sqrt(fabs(ph.x * ph.x + ph.y * ph.y)));
    if (fabs(mtra.pz) > 1.e-8) {
      h_tx->Fill(mtra.px / mtra.pz);
      h_ty->Fill(mtra.py / mtra.pz);
    }

    // reconstructed tracks
    bool reco   = (mtra.IsReconstructed());
    int nMCHits = mtra.NStations();

    if (reco) {
      p_eff_all_vs_mom->Fill(momentum, 100.0);
      p_eff_all_vs_nhits->Fill(nMCHits, 100.0);
      p_eff_all_vs_pt->Fill(pt, 100.0);
      h_reco_MCmom->Fill(momentum);
      if (mtra.mother_ID < 0) {  // primary
        p_eff_prim_vs_mom->Fill(momentum, 100.0);
        p_eff_prim_vs_nhits->Fill(nMCHits, 100.0);
        p_eff_prim_vs_pt->Fill(pt, 100.0);
        p_eff_prim_vs_theta->Fill(theta, 100.0);
      }
      else {
        p_eff_sec_vs_mom->Fill(momentum, 100.0);
        p_eff_sec_vs_nhits->Fill(nMCHits, 100.0);
      }
      if (mtra.mother_ID < 0) {  // primary
        h_reco_mom_prim->Fill(momentum);
        h_reco_phi_prim->Fill(phi);
        h_reco_prim_MCmom->Fill(momentum);
        h_reco_nhits_prim->Fill(nSta);
        h2_reco_nhits_vs_mom_prim->Fill(momentum, nSta);
        h2_reco_fstation_vs_mom_prim->Fill(momentum, fh.iStation + 1);
        if (lh.iStation >= fh.iStation) h2_reco_lstation_vs_fstation_prim->Fill(fh.iStation + 1, lh.iStation + 1);
      }
      else {
        h_reco_mom_sec->Fill(momentum);
        h_reco_phi_sec->Fill(phi);
        h_reco_sec_MCmom->Fill(momentum);
        h_reco_nhits_sec->Fill(nSta);
        if (momentum > 0.01) {
          h2_reco_nhits_vs_mom_sec->Fill(momentum, nSta);
          h2_reco_fstation_vs_mom_sec->Fill(momentum, fh.iStation + 1);
          if (lh.iStation >= fh.iStation) h2_reco_lstation_vs_fstation_sec->Fill(fh.iStation + 1, lh.iStation + 1);
        }
      }
    }
    else {
      h_notfound_mom->Fill(momentum);
      h_notfound_phi->Fill(phi);
      p_eff_all_vs_mom->Fill(momentum, 0.0);
      p_eff_all_vs_nhits->Fill(nMCHits, 0.0);
      p_eff_all_vs_pt->Fill(pt, 0.0);
      if (mtra.mother_ID < 0) {  // primary
        p_eff_prim_vs_mom->Fill(momentum, 0.0);
        p_eff_prim_vs_nhits->Fill(nMCHits, 0.0);
        p_eff_prim_vs_pt->Fill(pt, 0.0);
        p_eff_prim_vs_theta->Fill(theta, 0.0);
      }
      else {
        p_eff_sec_vs_mom->Fill(momentum, 0.0);
        p_eff_sec_vs_nhits->Fill(nMCHits, 0.0);
      }
      if (mtra.mother_ID < 0) {  // primary
        h_rest_mom_prim->Fill(momentum);
        h_rest_phi_prim->Fill(phi);
        h_rest_nhits_prim->Fill(nSta);
        h2_rest_nhits_vs_mom_prim->Fill(momentum, nSta);
        h2_rest_fstation_vs_mom_prim->Fill(momentum, fh.iStation + 1);
        if (lh.iStation >= fh.iStation) h2_rest_lstation_vs_fstation_prim->Fill(fh.iStation + 1, lh.iStation + 1);
      }
      else {
        h_rest_mom_sec->Fill(momentum);
        h_rest_phi_sec->Fill(phi);
        h_rest_nhits_sec->Fill(nSta);
        if (momentum > 0.01) {
          h2_rest_nhits_vs_mom_sec->Fill(momentum, nSta);
          h2_rest_fstation_vs_mom_sec->Fill(momentum, fh.iStation + 1);
          if (lh.iStation >= fh.iStation) h2_rest_lstation_vs_fstation_sec->Fill(fh.iStation + 1, lh.iStation + 1);
        }
      }
    }

    // killed tracks. At least one hit of they belong to some recoTrack
    //     bool killed = 0;
    if (!reco) {
      h_notfound_nhits->Fill(nmchits);
      h_notfound_station->Fill(ph.iStation);
      h_notfound_r->Fill(sqrt(fabs(ph.x * ph.x + ph.y * ph.y)));

      if (mtra.Points.size() > 0) {
        CbmL1MCPoint& pMC = fvMCPoints[mtra.Points[0]];
        h_notfound_tx->Fill(pMC.px / pMC.pz);
        h_notfound_ty->Fill(pMC.py / pMC.pz);
      }

      //      CbmL1HitDebugInfo &ph21 = fvHitDebugInfo[mtra.Hits[0]];
      //      CbmL1HitDebugInfo &ph22 = fvHitDebugInfo[mtra.Hits[1]];

      //      double z21 = fpAlgo->GetParameters()->GetStation(ph21.iStation).fZ[0];
      //      double z22 = fpAlgo->GetParameters()->GetStation(ph22.iStation).fZ[0];
      //      if( fabs(z22-z21)>1.e-4 ){
      //        h_notfound_tx->Fill((ph22.x-ph21.x)/(z22-z21));
      //        h_notfound_ty->Fill((ph22.y-ph21.y)/(z22-z21));
      //      }

      //       if( mtra.IsDisturbed() ) killed = 1;
    }


    if (mtra.isSignal) {  // D0
      h_reco_d0_mom->Fill(momentum);
      if (reco) p_eff_d0_vs_mom->Fill(momentum, 100.0);
      else
        p_eff_d0_vs_mom->Fill(momentum, 0.0);
    }

  }  // for mcTracks

  int NFakes = 0;
  for (unsigned int ih = 0; ih < fpAlgo->GetInputData().GetNhits(); ih++) {
    int iMC = fvHitBestPointIndices[ih];  // TODO2: adapt to linking
    if (iMC < 0) NFakes++;
  }

  h_reco_time->Fill(fTrackingTime);
  h_reco_timeNtr->Fill(mc_total, fTrackingTime);
  h_reco_timeNhit->Fill(fpAlgo->GetInputData().GetNhits(), fTrackingTime);

  h_reco_fakeNtr->Fill(mc_total, NFakes);
  h_reco_fakeNhit->Fill(fpAlgo->GetInputData().GetNhits() - NFakes, NFakes);


  h_reg_MCmom->Scale(1.f / NEvents);
  h_acc_MCmom->Scale(1.f / NEvents);
  h_reco_MCmom->Scale(1.f / NEvents);
  h_ghost_Rmom->Scale(1.f / NEvents);
  h_reg_prim_MCmom->Scale(1.f / NEvents);
  h_acc_prim_MCmom->Scale(1.f / NEvents);
  h_reco_prim_MCmom->Scale(1.f / NEvents);
  h_reg_sec_MCmom->Scale(1.f / NEvents);
  h_acc_sec_MCmom->Scale(1.f / NEvents);
  h_reco_sec_MCmom->Scale(1.f / NEvents);

}  // void CbmL1::HistoPerformance()


void CbmL1::TrackFitPerformance()
{
  const int Nh_fit = 17;
  static TH1F *h_fit[Nh_fit], *h_fitL[Nh_fit], *h_fitSV[Nh_fit], *h_fitPV[Nh_fit], *h_fit_chi2;

  static TH2F *PRes2D, *PRes2DPrim, *PRes2DSec;

  static TH2F* pion_res_pt_fstDca;
  static TH2F* kaon_res_pt_fstDca;
  static TH2F* pton_res_pt_fstDca;

  static TH2F* pion_res_pt_vtxDca;
  static TH2F* kaon_res_pt_vtxDca;
  static TH2F* pton_res_pt_vtxDca;

  static TH2F* pion_res_p_fstDca;
  static TH2F* kaon_res_p_fstDca;
  static TH2F* pton_res_p_fstDca;

  static TH2F* pion_res_p_vtxDca;
  static TH2F* kaon_res_p_vtxDca;
  static TH2F* pton_res_p_vtxDca;

  static bool first_call = 1;

  L1Fit fit;
  fit.SetParticleMass(fpAlgo->GetDefaultParticleMass());
  fit.SetMask(fmask::One());
  //fit.SetMaxExtrapolationStep(10.);
  fit.SetDoFitVelocity(true);

  if (first_call) {
    first_call = 0;

    TDirectory* currentDir = gDirectory;
    gDirectory             = fHistoDir;
    gDirectory->cd("Fit");
    {
      PRes2D     = new TH2F("PRes2D", "Resolution P [%] vs P", 100, 0., 20., 100, -15., 15.);
      PRes2DPrim = new TH2F("PRes2DPrim", "Resolution P [%] vs P", 100, 0., 20., 100, -15., 15.);
      PRes2DSec  = new TH2F("PRes2DSec", "Resolution P [%] vs P", 100, 0., 20., 100, -15., 15.);

      pion_res_pt_fstDca = new TH2F("pion_res_pt_fstDca", "", 100, 0, 10, 1000, -500, 500);
      kaon_res_pt_fstDca = new TH2F("kaon_res_pt_fstDca", "", 100, 0, 10, 1000, -500, 500);
      pton_res_pt_fstDca = new TH2F("pton_res_pt_fstDca", "", 100, 0, 10, 1000, -500, 500);

      pion_res_pt_vtxDca = new TH2F("pion_res_pt_vtxDca", "", 100, 0, 10, 1000, -5000, 5000);
      kaon_res_pt_vtxDca = new TH2F("kaon_res_pt_vtxDca", "", 100, 0, 10, 1000, -5000, 5000);
      pton_res_pt_vtxDca = new TH2F("pton_res_pt_vtxDca", "", 100, 0, 10, 1000, -5000, 5000);

      pion_res_p_fstDca = new TH2F("pion_res_p_fstDca", "", 100, 0, 10, 1000, -500, 500);
      kaon_res_p_fstDca = new TH2F("kaon_res_p_fstDca", "", 100, 0, 10, 1000, -500, 500);
      pton_res_p_fstDca = new TH2F("pton_res_p_fstDca", "", 100, 0, 10, 1000, -500, 500);

      pion_res_p_vtxDca = new TH2F("pion_res_p_vtxDca", "", 100, 0, 10, 1000, -5000, 5000);
      kaon_res_p_vtxDca = new TH2F("kaon_res_p_vtxDca", "", 100, 0, 10, 1000, -5000, 5000);
      pton_res_p_vtxDca = new TH2F("pton_res_p_vtxDca", "", 100, 0, 10, 1000, -5000, 5000);

      struct {
        const char* name;
        const char* title;
        Int_t n;
        Double_t l, r;
      } Table[Nh_fit] = {{"x", "Residual X [#mum]", 140, -40., 40.},
                         {"y", "Residual Y [#mum]", 100, -450., 450.},
                         {"tx", "Residual Tx [mrad]", 100, -4., 4.},
                         {"ty", "Residual Ty [mrad]", 100, -3.5, 3.5},
                         {"P", "Resolution P/Q [%]", 100, -15., 15.},
                         {"px", "Pull X [residual/estimated_error]", 100, -6., 6.},
                         {"py", "Pull Y [residual/estimated_error]", 100, -6., 6.},
                         {"ptx", "Pull Tx [residual/estimated_error]", 100, -6., 6.},
                         {"pty", "Pull Ty [residual/estimated_error]", 100, -6., 6.},
                         {"pQP", "Pull Q/P [residual/estimated_error]", 100, -6., 6.},
                         {"QPreco", "Reco Q/P ", 100, -10., 10.},
                         {"QPmc", "MC Q/P ", 100, -10., 10.},
                         {"time", "Residual Time [ns]", 100, -10., 10.},
                         {"ptime", "Pull Time [residual/estimated_error]", 100, -10., 10.},
                         {"vi", "Residual Vi [1/c]", 100, -4., 4.},
                         {"pvi", "Pull Vi [residual/estimated_error]", 100, -10., 10.},
                         {"distrVi", "Vi distribution [1/c]", 100, 0., 4.}};

      if (L1Algo::kGlobal == fpAlgo->fTrackingMode) {
        Table[4].l = -1.;
        Table[4].r = 1.;
      }

      struct Tab {
        const char* name;
        const char* title;
        Int_t n;
        Double_t l, r;
      };
      Tab TableVertex[Nh_fit] = {//{"x",  "Residual X [cm]",                   200, -0.01, 0.01},
                                 {"x", "Residual X [cm]", 2000, -1., 1.},
                                 //{"y",  "Residual Y [cm]",                   200, -0.01, 0.01},
                                 {"y", "Residual Y [cm]", 2000, -1., 1.},
                                 //{"tx", "Residual Tx [mrad]",                  100,   -3.,   3.},
                                 {"tx", "Residual Tx [mrad]", 100, -2., 2.},
                                 //{"ty", "Residual Ty [mrad]",                  100,   -3.,   3.},
                                 {"ty", "Residual Ty [mrad]", 100, -2., 2.},
                                 {"P", "Resolution P/Q [%]", 100, -15., 15.},
                                 {"px", "Pull X [residual/estimated_error]", 100, -10., 10.},
                                 {"py", "Pull Y [residual/estimated_error]", 100, -10., 10.},
                                 {"ptx", "Pull Tx [residual/estimated_error]", 100, -10., 10.},
                                 {"pty", "Pull Ty [residual/estimated_error]", 100, -10., 10.},
                                 {"pQP", "Pull Q/P [residual/estimated_error]", 100, -10., 10.},
                                 {"QPreco", "Reco Q/P ", 100, -10., 10.},
                                 {"QPmc", "MC Q/P ", 100, -10., 10.},
                                 {"time", "Residual Time [ns]", 100, -10., 10.},
                                 {"ptime", "Pull Time [residual/estimated_error]", 100, -10., 10.},
                                 {"vi", "Residual Vi [1/c]", 100, -10., 10.},
                                 {"pvi", "Pull Vi [residual/estimated_error]", 100, -10., 10.},
                                 {"distrVi", "Vi distribution [1/c]", 100, -10., 10.}};

      if (L1Algo::kGlobal == fpAlgo->fTrackingMode) {
        TableVertex[4].l = -1.;
        TableVertex[4].r = 1.;
      }

      for (int i = 0; i < Nh_fit; i++) {
        char n[225], t[255];
        sprintf(n, "fst_%s", Table[i].name);
        sprintf(t, "First point %s", Table[i].title);
        h_fit[i] = new TH1F(n, t, Table[i].n, Table[i].l, Table[i].r);
        sprintf(n, "lst_%s", Table[i].name);
        sprintf(t, "Last point %s", Table[i].title);
        h_fitL[i] = new TH1F(n, t, Table[i].n, Table[i].l, Table[i].r);
        sprintf(n, "svrt_%s", TableVertex[i].name);
        sprintf(t, "Secondary vertex point %s", TableVertex[i].title);
        h_fitSV[i] = new TH1F(n, t, TableVertex[i].n, TableVertex[i].l, TableVertex[i].r);
        sprintf(n, "pvrt_%s", TableVertex[i].name);
        sprintf(t, "Primary vertex point %s", TableVertex[i].title);
        h_fitPV[i] = new TH1F(n, t, TableVertex[i].n, TableVertex[i].l, TableVertex[i].r);

        for (int j = 0; j < 12; j++) {
          sprintf(n, "smth_%s_sta_%i", Table[i].name, j);
          sprintf(t, "Station %i %s", j, Table[i].title);
          //	  h_smoothed[j][i] = new TH1F(n,t, Table[i].n, Table[i].l, Table[i].r);
        }
      }
      h_fit_chi2 = new TH1F("h_fit_chi2", "Chi2/NDF", 50, -0.5, 10.0);
    }
    gDirectory = currentDir;
  }  // if first call


  for (vector<CbmL1Track>::iterator it = fvRecoTracks.begin(); it != fvRecoTracks.end(); ++it) {

    if (it->IsGhost()) continue;

    bool isTimeFitted = 0;

    {
      int nTimeMeasurenments = 0;
      for (unsigned int ih = 0; ih < it->Hits.size(); ih++) {
        int ista = fvHitDebugInfo[it->Hits[ih]].iStation;
        if (fpAlgo->GetParameters()->GetStation(ista).timeInfo) { nTimeMeasurenments++; }
      }
      isTimeFitted = (nTimeMeasurenments > 1);
    }

    do {  // first hit

      if (it->GetNMCTracks() < 1) { break; }

      const CbmL1MCTrack& mcTrack = *(it->GetMCTracks()[0]);

      int imcPoint = fvHitBestPointIndices[it->Hits.front()];

      // extrapolate to the first mc point of the mc track !

      imcPoint = mcTrack.Points[0];

      if (imcPoint < 0) break;

      const CbmL1MCPoint& mcP = fvMCPoints[imcPoint];

      L1TrackPar tr;
      tr.copyFromArrays(it->T, it->C);
      FillFitHistos(tr, mcP, isTimeFitted, h_fit);

      double dx  = tr.x[0] - mcP.xIn;
      double dy  = tr.y[0] - mcP.yIn;
      double dca = sqrt(dx * dx + dy * dy);
      // make dca distance negative for the half of the tracks to ease the gaussian fit and the rms calculation
      if (mcTrack.ID % 2) dca = -dca;
      double pt = sqrt(mcP.px * mcP.px + mcP.py * mcP.py);
      double dP = (fabs(1. / tr.qp[0]) - mcP.p) / mcP.p;

      PRes2D->Fill(mcP.p, 100. * dP);

      if (mcTrack.IsPrimary()) {

        h_fit[4]->Fill(100. * dP);

        PRes2DPrim->Fill(mcP.p, 100. * dP);

        if (abs(mcTrack.pdg) == 211) {
          pion_res_p_fstDca->Fill(mcP.p, dca * 1.e4);
          pion_res_pt_fstDca->Fill(pt, dca * 1.e4);
        }
        if (abs(mcTrack.pdg) == 321) {
          kaon_res_p_fstDca->Fill(mcP.p, dca * 1.e4);
          kaon_res_pt_fstDca->Fill(pt, dca * 1.e4);
        }
        if (abs(mcTrack.pdg) == 2212) {
          pton_res_p_fstDca->Fill(mcP.p, dca * 1.e4);
          pton_res_pt_fstDca->Fill(pt, dca * 1.e4);
        }
      }
      else {
        PRes2DSec->Fill(mcP.p, 100. * dP);
      }
    } while (0);


    {  // last hit

      int iMC = fvHitBestPointIndices[it->Hits.back()];  // TODO2: adapt to linking
      if (iMC < 0) continue;
      CbmL1MCPoint& mcP = fvMCPoints[iMC];
      L1TrackPar tr;
      tr.copyFromArrays(it->TLast, it->CLast);
      FillFitHistos(tr, mcP, isTimeFitted, h_fitL);
    }

    do {  // vertex

      if (it->GetNMCTracks() < 1) { break; }

      CbmL1MCTrack mc = *(it->GetMCTracks()[0]);
      fit.SetTrack(it->T, it->C);

      const L1TrackPar& tr = fit.Tr();

      //      if (mc.mother_ID != -1) {  // secondary
      if (!mc.IsPrimary()) {  // secondary

        {  // extrapolate to vertex
          L1FieldRegion fld _fvecalignment;
          fld.SetUseOriginalField();
          fit.Extrapolate(mc.z, fld);
          // add material
          const int fSta = fvHitDebugInfo[it->Hits[0]].iStation;
          const int dir  = int((mc.z - fpAlgo->GetParameters()->GetStation(fSta).fZ[0])
                              / fabs(mc.z - fpAlgo->GetParameters()->GetStation(fSta).fZ[0]));
          //         if (abs(mc.z - fpAlgo->GetParameters()->GetStation(fSta).fZ[0]) > 10.) continue; // can't extrapolate on large distance
          for (int iSta = fSta /*+dir*/; (iSta >= 0) && (iSta < fNStations)
                                         && (dir * (mc.z - fpAlgo->GetParameters()->GetStation(iSta).fZ[0]) > 0);
               iSta += dir) {
            //           cout << iSta << " " << dir << endl;
            auto radThick = fpAlgo->GetParameters()->GetMaterialThickness(iSta, fit.Tr().x, fit.Tr().y);
            fit.AddMsInMaterial(radThick);
            fit.EnergyLossCorrection(radThick, fvec::One());
          }
        }
        if (mc.z != tr.z[0]) continue;

        //       static int good = 0;
        //       static int bad = 0;
        //       if (mc.z != tr.z[0]){
        //         bad++;
        //         continue;
        //       }
        //       else good++;
        //       cout << "bad\\good" << bad << " " << good << endl;

        // calculate pulls
        //h_fitSV[0]->Fill( (mc.x-tr.x[0]) *1.e4);
        //h_fitSV[1]->Fill( (mc.y-tr.y[0]) *1.e4);
        h_fitSV[0]->Fill((tr.x[0] - mc.x));
        h_fitSV[1]->Fill((tr.y[0] - mc.y));
        h_fitSV[2]->Fill((tr.tx[0] - mc.px / mc.pz) * 1.e3);
        h_fitSV[3]->Fill((tr.ty[0] - mc.py / mc.pz) * 1.e3);
        h_fitSV[4]->Fill(100. * (fabs(1. / tr.qp[0]) / mc.p - 1.));
        if (std::isfinite(tr.C00[0]) && tr.C00[0] > 0) { h_fitSV[5]->Fill((tr.x[0] - mc.x) / sqrt(tr.C00[0])); }
        if (std::isfinite(tr.C11[0]) && tr.C11[0] > 0) h_fitSV[6]->Fill((tr.y[0] - mc.y) / sqrt(tr.C11[0]));
        if (std::isfinite(tr.C22[0]) && tr.C22[0] > 0) h_fitSV[7]->Fill((tr.tx[0] - mc.px / mc.pz) / sqrt(tr.C22[0]));
        if (std::isfinite(tr.C33[0]) && tr.C33[0] > 0) h_fitSV[8]->Fill((tr.ty[0] - mc.py / mc.pz) / sqrt(tr.C33[0]));
        if (std::isfinite(tr.C44[0]) && tr.C44[0] > 0) h_fitSV[9]->Fill((tr.qp[0] - mc.q / mc.p) / sqrt(tr.C44[0]));
        h_fitSV[10]->Fill(tr.qp[0]);
        h_fitSV[11]->Fill(mc.q / mc.p);
        if (isTimeFitted) {
          h_fitSV[12]->Fill(tr.t[0] - mc.time);
          if (std::isfinite(tr.C55[0]) && tr.C55[0] > 0.) { h_fitSV[13]->Fill((tr.t[0] - mc.time) / sqrt(tr.C55[0])); }
          double dvi = tr.vi[0] - sqrt(1. + mc.mass * mc.mass / mc.p / mc.p) * L1TrackPar::kClightNsInv;
          h_fitSV[14]->Fill(dvi * L1TrackPar::kClightNs);
          if (std::isfinite(tr.C66[0]) && tr.C66[0] > 0.) { h_fitSV[15]->Fill(dvi / sqrt(tr.C66[0])); }
          h_fitSV[16]->Fill(tr.vi[0] * L1TrackPar::kClightNs);
        }
      }
      else {  // primary

        {  // extrapolate to vertex
          L1FieldRegion fld _fvecalignment;
          fld.SetUseOriginalField();

          // add material
          const int fSta = fvHitDebugInfo[it->Hits[0]].iStation;

          const int dir = (mc.z - fpAlgo->GetParameters()->GetStation(fSta).fZ[0])
                          / abs(mc.z - fpAlgo->GetParameters()->GetStation(fSta).fZ[0]);
          //         if (abs(mc.z - fpAlgo->GetParameters()->GetStation(fSta].fZ[0]) > 10.) continue; // can't extrapolate on large distance

          for (int iSta = fSta + dir; (iSta >= 0) && (iSta < fNStations)
                                      && (dir * (mc.z - fpAlgo->GetParameters()->GetStation(iSta).fZ[0]) > 0);
               iSta += dir) {

            fit.Extrapolate(fpAlgo->GetParameters()->GetStation(iSta).fZ, fld);
            auto radThick = fpAlgo->GetParameters()->GetMaterialThickness(iSta, fit.Tr().x, fit.Tr().y);
            fit.AddMsInMaterial(radThick);
            fit.EnergyLossCorrection(radThick, fvec::One());
          }
          fit.Extrapolate(mc.z, fld);
        }
        if (mc.z != tr.z[0]) continue;

        double dx = tr.x[0] - mc.x;
        double dy = tr.y[0] - mc.y;
        double dt = sqrt(dx * dx + dy * dy);
        // make dt distance negative for the half of the tracks to ease the gaussian fit and the rms calculation
        if (mc.ID % 2) dt = -dt;

        double pt = sqrt(mc.px * mc.px + mc.py * mc.py);

        if (abs(mc.pdg) == 211) {
          pion_res_p_vtxDca->Fill(mc.p, dt * 1.e4);
          pion_res_pt_vtxDca->Fill(pt, dt * 1.e4);
        }
        if (abs(mc.pdg) == 321) {
          kaon_res_p_vtxDca->Fill(mc.p, dt * 1.e4);
          kaon_res_pt_vtxDca->Fill(pt, dt * 1.e4);
        }
        if (abs(mc.pdg) == 2212) {
          pton_res_p_vtxDca->Fill(mc.p, dt * 1.e4);
          pton_res_pt_vtxDca->Fill(pt, dt * 1.e4);
        }

        // calculate pulls
        h_fitPV[0]->Fill((mc.x - tr.x[0]));
        h_fitPV[1]->Fill((mc.y - tr.y[0]));
        h_fitPV[2]->Fill((mc.px / mc.pz - tr.tx[0]) * 1.e3);
        h_fitPV[3]->Fill((mc.py / mc.pz - tr.ty[0]) * 1.e3);
        h_fitPV[4]->Fill(100. * (fabs(1 / tr.qp[0]) / mc.p - 1.));
        if (std::isfinite(tr.C00[0]) && tr.C00[0] > 0) h_fitPV[5]->Fill((mc.x - tr.x[0]) / sqrt(tr.C00[0]));
        if (std::isfinite(tr.C11[0]) && tr.C11[0] > 0) h_fitPV[6]->Fill((mc.y - tr.y[0]) / sqrt(tr.C11[0]));
        if (std::isfinite(tr.C22[0]) && tr.C22[0] > 0) h_fitPV[7]->Fill((mc.px / mc.pz - tr.tx[0]) / sqrt(tr.C22[0]));
        if (std::isfinite(tr.C33[0]) && tr.C33[0] > 0) h_fitPV[8]->Fill((mc.py / mc.pz - tr.ty[0]) / sqrt(tr.C33[0]));
        if (std::isfinite(tr.C44[0]) && tr.C44[0] > 0) h_fitPV[9]->Fill((mc.q / mc.p - tr.qp[0]) / sqrt(tr.C44[0]));
        h_fitPV[10]->Fill(tr.qp[0]);
        h_fitPV[11]->Fill(mc.q / mc.p);
        if (isTimeFitted) {
          h_fitPV[12]->Fill(tr.t[0] - mc.time);
          if (std::isfinite(tr.C55[0]) && tr.C55[0] > 0.) { h_fitPV[13]->Fill((tr.t[0] - mc.time) / sqrt(tr.C55[0])); }
          double dvi = tr.vi[0] - sqrt(1. + mc.mass * mc.mass / mc.p / mc.p) * L1TrackPar::kClightNsInv;
          h_fitPV[14]->Fill(dvi * L1TrackPar::kClightNs);
          if (std::isfinite(tr.C66[0]) && tr.C66[0] > 0.) { h_fitPV[15]->Fill(dvi / sqrt(tr.C66[0])); }
          h_fitPV[16]->Fill(tr.vi[0] * L1TrackPar::kClightNs);
        }
      }
    } while (0);

    h_fit_chi2->Fill(it->chi2 / it->NDF);

    // last TRD point
    /*
    do {
      break;  // only produce these plots in debug mode     
      if (it->GetNMCTracks() < 1) { break; }

      if (!fpTrdPoints) break;
      const CbmL1MCTrack& mcTrack = *(it->GetMCTracks()[0]);
      int nTrdPoints              = fpTrdPoints->Size(mcTrack.iFile, mcTrack.iEvent);
      int lastP                   = -1;
      double lastZ                = -1.e8;
      for (int iPoint = 0; iPoint < nTrdPoints; iPoint++) {
        const CbmTrdPoint* pt = dynamic_cast<CbmTrdPoint*>(fpTrdPoints->Get(mcTrack.iFile, mcTrack.iEvent, iPoint));
        if (!pt) { continue; }
        if (pt->GetTrackID() != mcTrack.ID) { continue; }
        if (lastP < 0 || lastZ < pt->GetZ()) {
          lastP = iPoint;
          lastZ = pt->GetZ();
        }
      }
      CbmL1MCPoint mcP;
      if (CbmL1::ReadMCPoint(&mcP, lastP, mcTrack.iFile, mcTrack.iEvent, 3)) { break; }
      L1TrackPar tr;
      tr.copyFromArrays(it->TLast, it->CLast);
      FillFitHistos(tr, mcP, isTimeFitted, h_fitTrd);
    } while (0);  // Trd point


    // last TOF point
    do {
      break;  // only produce these plots in debug mode

      if (it->GetNMCTracks() < 1) { break; }

      if (!fpTofPoints) break;
      const CbmL1MCTrack& mcTrack = *(it->GetMCTracks()[0]);
      int nTofPoints              = fpTofPoints->Size(mcTrack.iFile, mcTrack.iEvent);
      int lastP                   = -1;
      double lastZ                = -1.e8;
      for (int iPoint = 0; iPoint < nTofPoints; iPoint++) {
        const CbmTofPoint* pt = dynamic_cast<CbmTofPoint*>(fpTofPoints->Get(mcTrack.iFile, mcTrack.iEvent, iPoint));
        if (!pt) { continue; }
        if (pt->GetTrackID() != mcTrack.ID) { continue; }
        if (lastP < 0 || lastZ < pt->GetZ()) {
          lastP = iPoint;
          lastZ = pt->GetZ();
        }
      }
      CbmL1MCPoint mcP;
      if (CbmL1::ReadMCPoint(&mcP, lastP, mcTrack.iFile, mcTrack.iEvent, 4)) { break; }
      L1TrackPar tr;
      tr.copyFromArrays(it->TLast, it->CLast);
      FillFitHistos(tr, mcP, isTimeFitted, h_fitTof);
    } while (0);  // tof point
*/
  }  // reco track

}  // void CbmL1::TrackFitPerformance()


void CbmL1::FillFitHistos(L1TrackPar& track, const CbmL1MCPoint& mcP, bool isTimeFitted, TH1F* h[])
{
  L1Fit fit;
  fit.SetParticleMass(fpAlgo->GetDefaultParticleMass());
  fit.SetMask(fmask::One());
  //fit.SetMaxExtrapolationStep(10.);
  fit.SetDoFitVelocity(true);
  fit.SetTrack(track);
  L1FieldRegion fld _fvecalignment;
  fld.SetUseOriginalField();
  fit.Extrapolate(mcP.zOut, fld);
  track = fit.Tr();

  const L1TrackPar& tr = track;

  h[0]->Fill((tr.x[0] - mcP.xOut) * 1.e4);
  h[1]->Fill((tr.y[0] - mcP.yOut) * 1.e4);
  h[2]->Fill((tr.tx[0] - mcP.pxOut / mcP.pzOut) * 1.e3);
  h[3]->Fill((tr.ty[0] - mcP.pyOut / mcP.pzOut) * 1.e3);
  h[4]->Fill(100. * (fabs(1. / tr.qp[0]) / mcP.p - 1.));

  if (std::isfinite(tr.C00[0]) && tr.C00[0] > 0) h[5]->Fill((tr.x[0] - mcP.xOut) / sqrt(tr.C00[0]));
  if (std::isfinite(tr.C11[0]) && tr.C11[0] > 0) h[6]->Fill((tr.y[0] - mcP.yOut) / sqrt(tr.C11[0]));
  if (std::isfinite(tr.C22[0]) && tr.C22[0] > 0) h[7]->Fill((tr.tx[0] - mcP.pxOut / mcP.pzOut) / sqrt(tr.C22[0]));
  if (std::isfinite(tr.C33[0]) && tr.C33[0] > 0) h[8]->Fill((tr.ty[0] - mcP.pyOut / mcP.pzOut) / sqrt(tr.C33[0]));
  if (std::isfinite(tr.C44[0]) && tr.C44[0] > 0) h[9]->Fill((tr.qp[0] - mcP.q / mcP.p) / sqrt(tr.C44[0]));
  h[10]->Fill(tr.qp[0]);
  h[11]->Fill(mcP.q / mcP.p);
  if (isTimeFitted) {
    h[12]->Fill(tr.t[0] - mcP.time);
    if (std::isfinite(tr.C55[0]) && tr.C55[0] > 0.) { h[13]->Fill((tr.t[0] - mcP.time) / sqrt(tr.C55[0])); }
    double dvi = tr.vi[0] - sqrt(1. + mcP.mass * mcP.mass / mcP.p / mcP.p) * L1TrackPar::kClightNsInv;
    h[14]->Fill(dvi * L1TrackPar::kClightNs);
    if (std::isfinite(tr.C66[0]) && tr.C66[0] > 0.) { h[15]->Fill(dvi / sqrt(tr.C66[0])); }
    h[16]->Fill(tr.vi[0] * L1TrackPar::kClightNs);
  }
}


void CbmL1::FieldApproxCheck()
{
  TDirectory* curr   = gDirectory;
  TFile* currentFile = gFile;
  TFile* fout        = new TFile("FieldApprox.root", "RECREATE");
  fout->cd();

  assert(FairRunAna::Instance());
  FairField* MF = FairRunAna::Instance()->GetField();
  assert(MF);

  for (int ist = 0; ist < fpAlgo->GetParameters()->GetNstationsActive(); ist++) {

    const L1Station& st = fpAlgo->GetParameters()->GetStation(ist);

    double z    = st.fZ[0];
    double Xmax = st.Xmax[0];
    double Ymax = st.Ymax[0];

    //    float step = 1.;

    int NbinsX = 100;  //static_cast<int>(2*Xmax/step);
    int NbinsY = 100;  //static_cast<int>(2*Ymax/step);
    float ddx  = 2 * Xmax / NbinsX;
    float ddy  = 2 * Ymax / NbinsY;

    TH2F* stB  = new TH2F(Form("station %i, dB", ist + 1), Form("station %i, dB, z = %0.f cm", ist + 1, z),
                         static_cast<int>(NbinsX + 1), -(Xmax + ddx / 2.), (Xmax + ddx / 2.),
                         static_cast<int>(NbinsY + 1), -(Ymax + ddy / 2.), (Ymax + ddy / 2.));
    TH2F* stBx = new TH2F(Form("station %i, dBx", ist + 1), Form("station %i, dBx, z = %0.f cm", ist + 1, z),
                          static_cast<int>(NbinsX + 1), -(Xmax + ddx / 2.), (Xmax + ddx / 2.),
                          static_cast<int>(NbinsY + 1), -(Ymax + ddy / 2.), (Ymax + ddy / 2.));
    TH2F* stBy = new TH2F(Form("station %i, dBy", ist + 1), Form("station %i, dBy, z = %0.f cm", ist + 1, z),
                          static_cast<int>(NbinsX + 1), -(Xmax + ddx / 2.), (Xmax + ddx / 2.),
                          static_cast<int>(NbinsY + 1), -(Ymax + ddy / 2.), (Ymax + ddy / 2.));
    TH2F* stBz = new TH2F(Form("station %i, dBz", ist + 1), Form("station %i, dBz, z = %0.f cm", ist + 1, z),
                          static_cast<int>(NbinsX + 1), -(Xmax + ddx / 2.), (Xmax + ddx / 2.),
                          static_cast<int>(NbinsY + 1), -(Ymax + ddy / 2.), (Ymax + ddy / 2.));

    Double_t r[3], B[3];
    L1FieldSlice FSl;
    L1FieldValue B_L1;
    Double_t bbb, bbb_L1;

    const int M = 5;  // polinom order
    const int N = (M + 1) * (M + 2) / 2;

    for (int i = 0; i < N; i++) {
      FSl.cx[i] = st.fieldSlice.cx[i][0];
      FSl.cy[i] = st.fieldSlice.cy[i][0];
      FSl.cz[i] = st.fieldSlice.cz[i][0];
    }

    Int_t i = 1, j = 1;

    double x, y;
    for (int ii = 1; ii <= NbinsX + 1; ii++) {
      j = 1;
      x = -Xmax + (ii - 1) * ddx;
      for (int jj = 1; jj <= NbinsY + 1; jj++) {
        y          = -Ymax + (jj - 1) * ddy;
        double rrr = sqrt(fabs(x * x / Xmax / Xmax + y / Ymax * y / Ymax));
        if (rrr > 1.) {
          j++;
          continue;
        }
        r[2] = z;
        r[0] = x;
        r[1] = y;
        MF->GetFieldValue(r, B);
        bbb = sqrt(B[0] * B[0] + B[1] * B[1] + B[2] * B[2]);

        FSl.GetFieldValue(x, y, B_L1);
        bbb_L1 = sqrt(B_L1.x[0] * B_L1.x[0] + B_L1.y[0] * B_L1.y[0] + B_L1.z[0] * B_L1.z[0]);

        stB->SetBinContent(ii, jj, (bbb - bbb_L1));
        stBx->SetBinContent(ii, jj, (B[0] - B_L1.x[0]));
        stBy->SetBinContent(ii, jj, (B[1] - B_L1.y[0]));
        stBz->SetBinContent(ii, jj, (B[2] - B_L1.z[0]));
        j++;
      }
      i++;
    }

    stB->GetXaxis()->SetTitle("X, cm");
    stB->GetYaxis()->SetTitle("Y, cm");
    stB->GetXaxis()->SetTitleOffset(1);
    stB->GetYaxis()->SetTitleOffset(1);
    stB->GetZaxis()->SetTitle("B_map - B_L1, kGauss");
    stB->GetZaxis()->SetTitleOffset(1.3);

    stBx->GetXaxis()->SetTitle("X, cm");
    stBx->GetYaxis()->SetTitle("Y, cm");
    stBx->GetXaxis()->SetTitleOffset(1);
    stBx->GetYaxis()->SetTitleOffset(1);
    stBx->GetZaxis()->SetTitle("Bx_map - Bx_L1, kGauss");
    stBx->GetZaxis()->SetTitleOffset(1.3);

    stBy->GetXaxis()->SetTitle("X, cm");
    stBy->GetYaxis()->SetTitle("Y, cm");
    stBy->GetXaxis()->SetTitleOffset(1);
    stBy->GetYaxis()->SetTitleOffset(1);
    stBy->GetZaxis()->SetTitle("By_map - By_L1, kGauss");
    stBy->GetZaxis()->SetTitleOffset(1.3);

    stBz->GetXaxis()->SetTitle("X, cm");
    stBz->GetYaxis()->SetTitle("Y, cm");
    stBz->GetXaxis()->SetTitleOffset(1);
    stBz->GetYaxis()->SetTitleOffset(1);
    stBz->GetZaxis()->SetTitle("Bz_map - Bz_L1, kGauss");
    stBz->GetZaxis()->SetTitleOffset(1.3);

    stB->Write();
    stBx->Write();
    stBy->Write();
    stBz->Write();

  }  // for ista

  fout->Close();
  fout->Delete();
  gFile      = currentFile;
  gDirectory = curr;
}  // void CbmL1::FieldApproxCheck()

#include "TMath.h"
void CbmL1::FieldIntegralCheck()
{
  TDirectory* curr   = gDirectory;
  TFile* currentFile = gFile;
  TFile* fout        = new TFile("FieldApprox.root", "RECREATE");
  fout->cd();

  assert(FairRunAna::Instance());
  FairField* MF = FairRunAna::Instance()->GetField();
  assert(MF);

  int nPointsZ     = 1000;
  int nPointsPhi   = 100;
  int nPointsTheta = 100;
  double startZ = 0, endZ = 100.;
  double startPhi = 0, endPhi = 2 * TMath::Pi();
  double startTheta = -30. / 180. * TMath::Pi(), endTheta = 30. / 180. * TMath::Pi();

  double DZ = endZ - startZ;
  double DP = endPhi - startPhi;
  double DT = endTheta - startTheta;

  float ddp = endPhi / nPointsPhi;
  float ddt = 2 * endTheta / nPointsTheta;

  TH2F* hSb =
    new TH2F("Field Integral", "Field Integral", static_cast<int>(nPointsPhi), -(startPhi + ddp / 2.),
             (endPhi + ddp / 2.), static_cast<int>(nPointsTheta), (startTheta - ddt / 2.), (endTheta + ddt / 2.));

  for (int iP = 0; iP < nPointsPhi; iP++) {
    double phi = startPhi + iP * DP / nPointsPhi;
    for (int iT = 0; iT < nPointsTheta; iT++) {
      double theta = startTheta + iT * DT / nPointsTheta;

      double Sb = 0;
      for (int iZ = 1; iZ < nPointsZ; iZ++) {
        double z    = startZ + DZ * iZ / nPointsZ;
        double x    = z * TMath::Tan(theta) * TMath::Cos(phi);
        double y    = z * TMath::Tan(theta) * TMath::Sin(phi);
        double r[3] = {x, y, z};
        double b[3];
        MF->GetFieldValue(r, b);
        double B = sqrt(b[0] * b[0] + b[1] * b[1] + b[2] * b[2]);
        Sb += B * DZ / nPointsZ / 100. / 10.;
      }
      hSb->SetBinContent(iP + 1, iT + 1, Sb);
    }
  }

  hSb->GetXaxis()->SetTitle("#phi [rad]");
  hSb->GetYaxis()->SetTitle("#theta [rad]");
  hSb->GetXaxis()->SetTitleOffset(1);
  hSb->GetYaxis()->SetTitleOffset(1);
  hSb->GetZaxis()->SetTitle("Field Integral [T#dotm]");
  hSb->GetZaxis()->SetTitleOffset(1.3);

  hSb->Write();


  fout->Close();
  fout->Delete();
  gFile      = currentFile;
  gDirectory = curr;
}  // void CbmL1::FieldApproxCheck()

void CbmL1::InputPerformance()
{
  // The input performance is currently evaluated by other CBM tasks
  // TODO: obsolete code, remove it

  //  static TH1I *nStripFHits, *nStripBHits, *nStripFMC, *nStripBMC;

  static TH1F *resXsts, *resYsts, *resTsts, *resXmvd, *resYmvd /*, *pullZ*/;
  static TH1F *pullXsts, *pullYsts, *pullTsts, *pullXmvd, *pullYmvd /*, *pullZ*/;
  static TH1F *pullXmuch, *pullYmuch, *pullTmuch, *resXmuch, *resYmuch, *resTmuch /*, *pullZ*/;
  static TH1F *pullXtrd, *pullYtrd, *pullTtrd, *resXtrd, *resYtrd, *resTtrd /*, *pullZ*/;
  static TH1F *pullXtof, *pullYtof, *pullTtof, *resXtof, *resYtof, *resTtof /*, *pullZ*/;
  static TH1F* trdMCPointsZ = nullptr;

  static bool first_call = 1;
  if (first_call) {
    first_call = 0;

    TDirectory* currentDir = gDirectory;
    gDirectory             = fHistoDir;
    gDirectory->cd("Input");
    gDirectory->mkdir("STS");
    gDirectory->cd("STS");

    //    nStripFHits = new TH1I("nHits_f", "NHits On Front Strip", 10, 0, 10);
    //    nStripBHits = new TH1I("nHits_b", "NHits On Back Strip", 10, 0, 10);
    //    nStripFMC = new TH1I("nMC_f", "N MC Points On Front Strip", 10, 0, 10);
    //    nStripBMC = new TH1I("nMC_b", "N MC Points On Back Strip", 10, 0, 10);

    pullXsts = new TH1F("Px_sts", "STS Pull x", 100, -5, 5);
    pullYsts = new TH1F("Py_sts", "STS Pull y", 100, -5, 5);
    pullTsts = new TH1F("Pt_sts", "STS Pull t", 100, -5, 5);

    resXsts = new TH1F("x_sts", "STS Residual x", 100, -50, 50);
    resYsts = new TH1F("y_sts", "STS Residual y", 100, -500, 500);
    resTsts = new TH1F("t_sts", "STS Residual t", 100, -20, 20);

    gDirectory->cd("..");
    gDirectory->mkdir("MVD");
    gDirectory->cd("MVD");

    pullXmvd = new TH1F("Px_mvd", "MVD Pull x", 100, -5, 5);
    pullYmvd = new TH1F("Py_mvd", "MVD Pull y", 100, -5, 5);

    resXmvd = new TH1F("x_mvd", "MVD Residual x", 100, -50, 50);
    resYmvd = new TH1F("y_mvd", "MVD Residual y", 100, -50, 50);

    TH1* histo;
    histo = resXsts;
    histo->GetXaxis()->SetTitle("Residual x, um");
    histo = resYsts;
    histo->GetXaxis()->SetTitle("Residual y, um");
    histo = resTsts;
    histo->GetXaxis()->SetTitle("Residual t, ns");
    histo = resXmvd;
    histo->GetXaxis()->SetTitle("Residual x, um");
    histo = resYmvd;
    histo->GetXaxis()->SetTitle("Residual y, um");
    histo = pullXsts;
    histo->GetXaxis()->SetTitle("Pull x");
    histo = pullYsts;
    histo->GetXaxis()->SetTitle("Pull y");
    histo = pullTsts;
    histo->GetXaxis()->SetTitle("Pull t");
    histo = pullXmvd;
    histo->GetXaxis()->SetTitle("Pull x");
    histo = pullYmvd;
    histo->GetXaxis()->SetTitle("Pull y");


    gDirectory->cd("..");
    gDirectory->mkdir("MUCH");
    gDirectory->cd("MUCH");

    pullXmuch = new TH1F("Px_much", "MUCH Pull x", 100, -10, 10);
    pullYmuch = new TH1F("Py_much", "MUCH Pull y", 100, -10, 10);
    pullTmuch = new TH1F("Pt_much", "MUCH Pull t", 100, -10, 10);

    resXmuch = new TH1F("x_much", "MUCH Residual x", 100, -100000, 100000);
    resYmuch = new TH1F("y_much", "MUCH Residual y", 100, -100000, 100000);
    resTmuch = new TH1F("t_much", "MUCH Residual t", 100, -40, 40);


    histo = resXmuch;
    histo->GetXaxis()->SetTitle("Residual x, um");
    histo = resYmuch;
    histo->GetXaxis()->SetTitle("Residual y, um");
    histo = resTmuch;
    histo->GetXaxis()->SetTitle("Residual t, ns");
    histo = pullXmuch;
    histo->GetXaxis()->SetTitle("Pull x");
    histo = pullYmuch;
    histo->GetXaxis()->SetTitle("Pull y");
    histo = pullTmuch;
    histo->GetXaxis()->SetTitle("Pull t");

    gDirectory->cd("..");
    gDirectory->mkdir("TRD");
    gDirectory->cd("TRD");

    trdMCPointsZ = new TH1F("trdMCPointsZ", "trdMCPointsZ", 1000, 400., 700.);

    pullXtrd = new TH1F("Px_trd", "TRD Pull x", 100, -5, 5);
    pullYtrd = new TH1F("Py_trd", "TRD Pull y", 100, -5, 5);
    pullTtrd = new TH1F("Pt_trd", "TRD Pull t", 100, -5, 5);

    resXtrd = new TH1F("x_trd", "TRD Residual x", 100, -10000, 10000);
    resYtrd = new TH1F("y_trd", "TRD Residual y", 100, -10000, 10000);
    resTtrd = new TH1F("t_trd", "TRD Residual t", 100, -40, 40);


    histo = resXtrd;
    histo->GetXaxis()->SetTitle("Residual x, um");
    histo = resYtrd;
    histo->GetXaxis()->SetTitle("Residual y, um");
    histo = resTtrd;
    histo->GetXaxis()->SetTitle("Residual t, ns");
    histo = pullXtrd;
    histo->GetXaxis()->SetTitle("Pull x");
    histo = pullYtrd;
    histo->GetXaxis()->SetTitle("Pull y");
    histo = pullTtrd;
    histo->GetXaxis()->SetTitle("Pull t");

    gDirectory->cd("..");
    gDirectory->mkdir("TOF");
    gDirectory->cd("TOF");

    pullXtof = new TH1F("Px_tof", "TOF Pull x", 100, -5, 5);
    pullYtof = new TH1F("Py_tof", "TOF Pull y", 100, -5, 5);
    pullTtof = new TH1F("Pt_tof", "TOF Pull t", 100, -5, 5);

    resXtof = new TH1F("x_tof", "TOF Residual x", 100, -50000, 50000);
    resYtof = new TH1F("y_tof", "TOF Residual y", 100, -50000, 50000);
    resTtof = new TH1F("t_tof", "TOF Residual t", 100, -0.4, 0.4);


    histo = resXtof;
    histo->GetXaxis()->SetTitle("Residual x, um");
    histo = resYtof;
    histo->GetXaxis()->SetTitle("Residual y, um");
    histo = resTtof;
    histo->GetXaxis()->SetTitle("Residual t, ns");
    histo = pullXtof;
    histo->GetXaxis()->SetTitle("Pull x");
    histo = pullYtof;
    histo->GetXaxis()->SetTitle("Pull y");
    histo = pullTtof;
    histo->GetXaxis()->SetTitle("Pull t");

    gDirectory = currentDir;
  }  // first_call

  //  std::map<unsigned int, unsigned int> stripFToNHitMap,stripBToNHitMap;
  //  std::map<unsigned int, unsigned int> stripFToNMCMap,stripBToNMCMap;

  map<unsigned int, unsigned int>::iterator it;

  if (fpStsHits && fpStsHitMatches && fpStsPoints) {
    for (int iH = 0; iH < fpStsHits->GetEntriesFast(); iH++) {

      const CbmStsHit* sh = L1_DYNAMIC_CAST<CbmStsHit*>(fpStsHits->At(iH));

      //    int iMCPoint = -1;
      CbmLink link;
      CbmStsPoint* pt = 0;

      if (fpStsClusterMatches) {
        const CbmMatch* frontClusterMatch =
          static_cast<const CbmMatch*>(fpStsClusterMatches->At(sh->GetFrontClusterId()));
        const CbmMatch* backClusterMatch =
          static_cast<const CbmMatch*>(fpStsClusterMatches->At(sh->GetBackClusterId()));
        CbmMatch stsHitMatch;

        for (Int_t iFrontLink = 0; iFrontLink < frontClusterMatch->GetNofLinks(); iFrontLink++) {
          const CbmLink& frontLink = frontClusterMatch->GetLink(iFrontLink);

          for (Int_t iBackLink = 0; iBackLink < backClusterMatch->GetNofLinks(); iBackLink++) {
            const CbmLink& backLink = backClusterMatch->GetLink(iBackLink);
            if (frontLink == backLink) {
              stsHitMatch.AddLink(frontLink);
              stsHitMatch.AddLink(backLink);
            }
          }
        }

        if (stsHitMatch.GetNofLinks() > 0) {
          Float_t bestWeight = 0.f;
          for (Int_t iLink = 0; iLink < stsHitMatch.GetNofLinks(); iLink++) {
            if (stsHitMatch.GetLink(iLink).GetWeight() > bestWeight) {
              link = stsHitMatch.GetLink(iLink);
              if (link.GetIndex() < 0) break;
              bestWeight   = link.GetWeight();
              Int_t iFile  = link.GetFile();
              Int_t iEvent = link.GetEntry();
              pt           = (CbmStsPoint*) fpStsPoints->Get(iFile, iEvent, link.GetIndex());
            }
          }
        }

        if (pt == 0) continue;

        double mcTime = pt->GetTime();

        mcTime += fpMcEventList->GetEventTime(link.GetEntry(), link.GetFile());

        // hit pulls and residuals

        TVector3 hitPos, mcPos, hitErr;
        sh->Position(hitPos);
        sh->PositionError(hitErr);

        double t = (hitPos.Z() - pt->GetZIn()) / (pt->GetZOut() - pt->GetZIn());
        mcPos.SetX(pt->GetXIn() + t * (pt->GetXOut() - pt->GetXIn()));
        mcPos.SetY(pt->GetYIn() + t * (pt->GetYOut() - pt->GetYIn()));
        mcPos.SetZ(hitPos.Z());

        {  // qa errors
          if (sh->GetDx() > 1.e-8) pullXsts->Fill((hitPos.X() - mcPos.X()) / sh->GetDx());
          if (sh->GetDy() > 1.e-8) pullYsts->Fill((hitPos.Y() - mcPos.Y()) / sh->GetDy());
          pullTsts->Fill((sh->GetTime() - mcTime) / sh->GetTimeError());
        }

        resXsts->Fill((hitPos.X() - mcPos.X()) * 10 * 1000);
        resYsts->Fill((hitPos.Y() - mcPos.Y()) * 10 * 1000);
        resTsts->Fill((sh->GetTime() - mcTime));
      }
    }
  }  // sts


  if (fpMvdHits && fpMvdHitMatches && fpMvdPoints) {
    Int_t nEnt = fpMvdHits->GetEntriesFast();
    for (int j = 0; j < nEnt; j++) {

      CbmMvdHit* sh = L1_DYNAMIC_CAST<CbmMvdHit*>(fpMvdHits->At(j));
      CbmMatch* hm  = L1_DYNAMIC_CAST<CbmMatch*>(fpMvdHitMatches->At(j));

      CbmMvdPoint* pt = nullptr;
      {
        float mcWeight = -1.f;
        for (int iLink = 0; iLink < hm->GetNofLinks(); iLink++) {
          const CbmLink& link = hm->GetLink(iLink);
          if (link.GetIndex() < 0) break;
          if (link.GetWeight() < mcWeight) continue;
          mcWeight = link.GetWeight();
          pt       = dynamic_cast<CbmMvdPoint*>(fpMvdPoints->Get(&link));
        }
      }
      if (!pt) continue;

      // hit pulls and residuals

      TVector3 hitPos, mcPos, hitErr;
      sh->Position(hitPos);
      sh->PositionError(hitErr);

      mcPos.SetX((pt->GetX() + pt->GetXOut()) / 2.);
      mcPos.SetY((pt->GetY() + pt->GetYOut()) / 2.);
      mcPos.SetZ(hitPos.Z());

      if (hitErr.X() != 0) pullXmvd->Fill((hitPos.X() - mcPos.X()) / hitErr.X());
      if (hitErr.Y() != 0) pullYmvd->Fill((hitPos.Y() - mcPos.Y()) / hitErr.Y());

      resXmvd->Fill((hitPos.X() - mcPos.X()) * 10 * 1000);
      resYmvd->Fill((hitPos.Y() - mcPos.Y()) * 10 * 1000);
    }
  }  // mvd


  if (fpMuchPixelHits && fpMuchHitMatches && fpMuchPoints) {
    for (int iH = 0; iH < fpMuchPixelHits->GetEntriesFast(); iH++) {

      const CbmMuchPixelHit* sh = L1_DYNAMIC_CAST<CbmMuchPixelHit*>(fpMuchPixelHits->At(iH));
      const CbmMatch* hm        = L1_DYNAMIC_CAST<CbmMatch*>(fpMuchHitMatches->At(iH));

      if (hm->GetNofLinks() == 0) continue;
      Float_t bestWeight  = 0.f;
      Float_t totalWeight = 0.f;
      int iMCPoint        = -1;
      CbmLink link;

      for (int iLink = 0; iLink < hm->GetNofLinks(); iLink++) {
        totalWeight += hm->GetLink(iLink).GetWeight();
        if (hm->GetLink(iLink).GetWeight() > bestWeight) {
          if (hm->GetLink(iLink).GetIndex() < 0) break;
          bestWeight = hm->GetLink(iLink).GetWeight();
          iMCPoint   = hm->GetLink(iLink).GetIndex();
          link       = hm->GetLink(iLink);
        }
      }
      if (bestWeight / totalWeight < 0.7 || iMCPoint < 0) continue;

      CbmMuchPoint* pt = (CbmMuchPoint*) fpMuchPoints->Get(link.GetFile(), link.GetEntry(), link.GetIndex());
      double mcTime    = pt->GetTime();
      mcTime += fpMcEventList->GetEventTime(link.GetEntry(), link.GetFile());
      // mcTime+=20;

      // hit pulls and residuals


      TVector3 hitPos, mcPos, hitErr;
      sh->Position(hitPos);
      sh->PositionError(hitErr);

      //       pt->Position(mcPos); // this is wrong!
      //       mcPos.SetX( pt->GetX( hitPos.Z() ) );
      //       mcPos.SetY( pt->GetY( hitPos.Z() ) );
      //       mcPos.SetZ( hitPos.Z() );

      mcPos.SetX(0.5 * (pt->GetXIn() + pt->GetXOut()));
      mcPos.SetY(0.5 * (pt->GetYIn() + pt->GetYOut()));
      mcPos.SetZ(hitPos.Z());

      {  // qa errors
        if (sh->GetDx() > 1.e-8) pullXmuch->Fill((sh->GetX() - mcPos.X()) / sh->GetDx());
        if (sh->GetDy() > 1.e-8) pullYmuch->Fill((sh->GetY() - mcPos.Y()) / sh->GetDy());
        pullTmuch->Fill((sh->GetTime() - mcTime) / sh->GetTimeError());
      }

      resXmuch->Fill((sh->GetX() - mcPos.X()) * 10 * 1000);
      resYmuch->Fill((sh->GetY() - mcPos.Y()) * 10 * 1000);
      resTmuch->Fill((sh->GetTime() - mcTime));
    }
  }  // much


  if (fpTrdHits && fpTrdHitMatches && fpTrdPoints) {
    for (int iH = 0; iH < fpTrdHits->GetEntriesFast(); iH++) {

      const CbmTrdHit* sh = L1_DYNAMIC_CAST<CbmTrdHit*>(fpTrdHits->At(iH));
      const CbmMatch* hm  = L1_DYNAMIC_CAST<CbmMatch*>(fpTrdHitMatches->At(iH));

      if (hm->GetNofLinks() == 0) continue;
      if (hm->GetNofLinks() != 1) continue;  // only check single-linked hits

      Float_t bestWeight  = 0.f;
      Float_t totalWeight = 0.f;
      int iMCPoint        = -1;
      CbmLink link;

      for (int iLink = 0; iLink < hm->GetNofLinks(); iLink++) {
        totalWeight += hm->GetLink(iLink).GetWeight();
        if (hm->GetLink(iLink).GetWeight() > bestWeight) {
          if (hm->GetLink(iLink).GetIndex() < 0) {
            iMCPoint = -1;
            break;
          }
          bestWeight = hm->GetLink(iLink).GetWeight();
          iMCPoint   = hm->GetLink(iLink).GetIndex();
          link       = hm->GetLink(iLink);
        }
      }
      if (bestWeight / totalWeight < 0.7 || iMCPoint < 0) continue;

      CbmTrdPoint* pt = (CbmTrdPoint*) fpTrdPoints->Get(link.GetFile(), link.GetEntry(), link.GetIndex());
      double mcTime   = pt->GetTime();

      mcTime += fpMcEventList->GetEventTime(link.GetEntry(), link.GetFile());

      // hit pulls and residuals
      //      if ((sh->GetPlaneId()) == 0) continue;
      //      if ((sh->GetPlaneId()) == 2) continue;
      //      if ((sh->GetPlaneId()) == 4) continue;

      TVector3 hitPos, mcPos, hitErr;
      sh->Position(hitPos);
      sh->PositionError(hitErr);

      //       pt->Position(mcPos); // this is wrong!
      mcPos.SetX((pt->GetXIn() + pt->GetXOut()) / 2.);
      mcPos.SetY((pt->GetYIn() + pt->GetYOut()) / 2.);
      mcPos.SetZ(hitPos.Z());

      {  // qa errors
        if (sh->GetDx() > 1.e-8) pullXtrd->Fill((sh->GetX() - mcPos.X()) / sh->GetDx());
        if (sh->GetDy() > 1.e-8) pullYtrd->Fill((sh->GetY() - mcPos.Y()) / sh->GetDy());
        pullTtrd->Fill((sh->GetTime() - mcTime) / sh->GetTimeError());
      }

      resXtrd->Fill((sh->GetX() - mcPos.X()) * 10 * 1000);
      resYtrd->Fill((sh->GetY() - mcPos.Y()) * 10 * 1000);
      resTtrd->Fill((sh->GetTime() - mcTime));
      trdMCPointsZ->Fill(mcPos.Z());
    }
  }  // much


  if (fpTofHits && fpTofHitMatches && fpTofPoints) {
    for (int iH = 0; iH < fpTofHits->GetEntriesFast(); iH++) {

      const CbmTofHit* sh = L1_DYNAMIC_CAST<CbmTofHit*>(fpTofHits->At(iH));
      const CbmMatch* hm  = L1_DYNAMIC_CAST<CbmMatch*>(fpTofHitMatches->At(iH));

      if (hm->GetNofLinks() == 0) continue;
      Float_t bestWeight = 0.f;
      //Float_t totalWeight = 0.f;
      int iMCPoint = -1;
      CbmLink link;

      for (int iLink = 0; iLink < hm->GetNofLinks(); iLink++) {
        //totalWeight += hm->GetLink(iLink).GetWeight();
        if (hm->GetLink(iLink).GetWeight() > bestWeight) {
          if (hm->GetLink(iLink).GetIndex() < 0) {
            iMCPoint = -1;
            break;
          };
          bestWeight = hm->GetLink(iLink).GetWeight();
          iMCPoint   = hm->GetLink(iLink).GetIndex();
          link       = hm->GetLink(iLink);
        }
      }

      // TODO: Unify the cut and the whole code block for different detectors (S.Zharko)
      // if (bestWeight / totalWeight < 0.7 || iMCPoint < 0) continue;
      if (iMCPoint < 0) continue;

      CbmTofPoint* pt = (CbmTofPoint*) fpTofPoints->Get(link.GetFile(), link.GetEntry(), link.GetIndex());
      double mcTime   = pt->GetTime();

      mcTime += fpMcEventList->GetEventTime(link.GetEntry(), link.GetFile());

      // hit pulls and residuals


      TVector3 hitPos, mcPos, hitErr;
      sh->Position(hitPos);
      sh->PositionError(hitErr);

      //       pt->Position(mcPos); // this is wrong!
      mcPos.SetX((pt->GetX()));
      mcPos.SetY((pt->GetY()));
      mcPos.SetZ(hitPos.Z());

      {  // qa errors
        if (sh->GetDx() > 1.e-8) pullXtof->Fill((sh->GetX() - mcPos.X()) / sh->GetDx());
        if (sh->GetDy() > 1.e-8) pullYtof->Fill((sh->GetY() - mcPos.Y()) / sh->GetDy());
        pullTtof->Fill((sh->GetTime() - mcTime) / sh->GetTimeError());
      }

      resXtof->Fill((sh->GetX() - mcPos.X()) * 10 * 1000);
      resYtof->Fill((sh->GetY() - mcPos.Y()) * 10 * 1000);
      resTtof->Fill((sh->GetTime() - mcTime));
    }
  }  // much

  //  for (it = stripFToNHitMap.begin(); it != stripFToNHitMap.end(); it++){
  //    nStripFHits->Fill(it->second);
  //  }
  //  for (it = stripBToNHitMap.begin(); it != stripBToNHitMap.end(); it++){
  //    nStripBHits->Fill(it->second);
  //  }
  //  for (it = stripFToNMCMap.begin(); it != stripFToNMCMap.end(); it++){
  //    nStripFMC->Fill(it->second);
  //  }
  //  for (it = stripBToNMCMap.begin(); it != stripBToNMCMap.end(); it++){
  //    nStripBMC->Fill(it->second);
  //  }

  // strips   Not ended
  //   if( listCbmStsDigi ){
  //     Int_t nEnt = listCbmStsDigi->GetEntriesFast();
  //     for (int j=0; j < nEnt; j++ ){
  //       CbmStsDigi *digi = (CbmStsDigi*) listCbmStsDigi->At(j);
  // //  = sh->GetNLinks(0);
  //         // find position of mc point
  //       FairLink link = digi->GetLink(0);
  //       int iMCPoint = link.GetIndex();
  //       CbmStsPoint *mcPoint = (CbmStsPoint*) listStsPts->At(iMCPoint);
  //       TVector3 mcPos;
  //       if (digi->GetSide() == 0)
  //         mcPoint->PositionIn(mcPos);
  //       else
  //         mcPoint->PositionOut(mcPos);
  //
  //       CbmStsStation_old *sta = StsDigi.GetStation(digi->GetStationNr());
  //       CbmStsSector* sector = sta->GetSector(digi->GetSectorNr());
  //       digi->GetChannelNr();
  //
  //     }
  //   } // listCbmStsDigi


}  // void CbmL1::InputPerformance()

// ---------------------------------------------------------------------------------------------------------------------
//
void CbmL1::DumpMCTripletsToTree()
{
  if (!fpMcTripletsTree) {
    TDirectory* currentDir = gDirectory;
    TFile* currentFile     = gFile;


    // Get prefix and directory
    boost::filesystem::path p = (FairRunAna::Instance()->GetUserOutputFileName()).Data();
    std::string dir           = p.parent_path().string();
    if (dir.empty()) dir = ".";
    std::string filename = dir + "/" + fsMcTripletsOutputFilename + "." + p.filename().string();
    std::cout << "\033[1;31mSAVING A TREE: " << filename << "\033[0m\n";

    fpMcTripletsOutFile = new TFile(filename.c_str(), "RECREATE");
    fpMcTripletsOutFile->cd();
    fpMcTripletsTree = new TTree("t", "MC Triplets");
    // motherId   - id of mother particle (< 0, if primary)
    // pdg        - PDG code of particle
    // processId  - id of the process (ROOT::TMCProcessID)
    // pq         - absolute value of track momentum [GeV/c], divided by charge [e]
    // zv         - z-component of track vertex [cm]
    // s          - global index of station
    // x0, y0, z0 - position of the left MC point in a triplet [cm]
    // x1, y1, z1 - position of the middle MC point in a triplet [cm]
    // x2, y2, z2 - position of the right MC point in a triplet [cm]

    gFile      = currentFile;
    gDirectory = currentDir;
  }

  // Variables for tree branches
  int brMotherId        = -1;                 ///< mother ID of track
  int brPdg             = -1;                 ///< PDG code of track
  unsigned int brProcId = (unsigned int) -1;  ///< process ID (see ROOT::TMCProcessID)
  float brP             = 0.f;                ///< abs. momentum of track [GeV/c]
  int brQ               = 0;                  ///< charge of track [e]
  float brVertexZ       = 0.f;                ///< z-component of the MC track vertex [cm]
  int brStation         = -1;                 ///< global index of the left MC point station

  float brX0 = 0.f;  ///< x-component of the left MC point in a triplet [cm]
  float brY0 = 0.f;  ///< y-component of the left MC point in a triplet [cm]
  float brZ0 = 0.f;  ///< z-component of the left MC point in a triplet [cm]
  float brX1 = 0.f;  ///< x-component of the middle MC point in a triplet [cm]
  float brY1 = 0.f;  ///< y-component of the middle MC point in a triplet [cm]
  float brZ1 = 0.f;  ///< z-component of the middle MC point in a triplet [cm]
  float brX2 = 0.f;  ///< x-component of the right MC point in a triplet [cm]
  float brY2 = 0.f;  ///< y-component of the right MC point in a triplet [cm]
  float brZ2 = 0.f;  ///< z-component of the right MC point in a triplet [cm]

  // Register branches
  fpMcTripletsTree->Branch("brMotherId", &brMotherId, "motherId/I");
  fpMcTripletsTree->Branch("brPdg", &brPdg, "pdg/I");
  fpMcTripletsTree->Branch("brProcId", &brProcId, "processId/i");
  fpMcTripletsTree->Branch("brP", &brP, "p/F");
  fpMcTripletsTree->Branch("brQ", &brQ, "q/I");
  fpMcTripletsTree->Branch("brVertexZ", &brVertexZ, "zv/F");
  fpMcTripletsTree->Branch("brStation", &brStation, "s/I");
  fpMcTripletsTree->Branch("brX0", &brX0, "x0/F");
  fpMcTripletsTree->Branch("brY0", &brY0, "y0/F");
  fpMcTripletsTree->Branch("brZ0", &brZ0, "z0/F");
  fpMcTripletsTree->Branch("brX1", &brX1, "x1/F");
  fpMcTripletsTree->Branch("brY1", &brY1, "y1/F");
  fpMcTripletsTree->Branch("brZ1", &brZ1, "z1/F");
  fpMcTripletsTree->Branch("brX2", &brX2, "x2/F");
  fpMcTripletsTree->Branch("brY2", &brY2, "y2/F");
  fpMcTripletsTree->Branch("brZ2", &brZ2, "z2/F");

  struct ReducedMcPoint {
    int s;    ///< global active index of tracking station
    float x;  ///< x-component of point position [cm]
    float y;  ///< y-component of point position [cm]
    float z;  ///< z-component of point position [cm]
  };

  for (const auto& tr : fvMCTracks) {
    // Use only reconstructable tracks
    if (!tr.IsReconstructable()) { continue; }

    std::vector<ReducedMcPoint> vPoints;
    vPoints.reserve(tr.Points.size());
    for (unsigned int iP = 0; iP < tr.Points.size(); ++iP) {
      const auto& point = fvMCPoints[tr.Points[iP]];
      vPoints.emplace_back(ReducedMcPoint {point.iStation, float(point.x), float(point.y), float(point.z)});
    }

    std::sort(vPoints.begin(), vPoints.end(),
              [](const ReducedMcPoint& lhs, const ReducedMcPoint& rhs) { return lhs.s < rhs.s; });

    for (unsigned int i = 0; i + 2 < vPoints.size(); ++i) {
      // Condition to collect only triplets without gaps in stations
      // TODO: SZh 20.10.2022 Add cases for jump iterations
      if (vPoints[i + 1].s == vPoints[i].s + 1 && vPoints[i + 2].s == vPoints[i].s + 2) {
        // Fill MC-triplets tree
        brMotherId = tr.mother_ID;
        brPdg      = tr.pdg;
        brProcId   = tr.process_ID;
        brP        = tr.p;
        brQ        = tr.q;
        brVertexZ  = tr.z;
        brStation  = vPoints[i].s;
        brX0       = vPoints[i].x;
        brY0       = vPoints[i].y;
        brZ0       = vPoints[i].z;
        brX1       = vPoints[i + 1].x;
        brY1       = vPoints[i + 1].y;
        brZ1       = vPoints[i + 1].z;
        brX2       = vPoints[i + 2].x;
        brY2       = vPoints[i + 2].y;
        brZ2       = vPoints[i + 2].z;

        fpMcTripletsTree->Fill();
      }
    }
  }
}