Skip to content
Snippets Groups Projects
PlanarDigiAlg.cpp 15.65 KiB
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
#include "PlanarDigiAlg.h"
#include "DataHelper/TrackerHitHelper.h"
#include "GearSvc/IGearSvc.h"
#include "EventSeeder/IEventSeeder.h"
#include "TrackSystemSvc/ITrackSystemSvc.h"
#include "edm4hep/MCParticle.h"
#include "edm4hep/Vector3d.h"
/*
#include <EVENT/LCCollection.h>
#include <EVENT/SimTrackerHit.h>
#include <EVENT/MCParticle.h>
#include <IMPL/LCCollectionVec.h>
#include <IMPL/LCRelationImpl.h>
#include <IMPL/TrackerHitPlaneImpl.h>

#include "UTIL/CellIDEncoder.h"
#include <UTIL/Operators.h>
*/
#include "DetIdentifier/CEPCConf.h"
#include "UTIL/ILDConf.h"

// STUFF needed for GEAR
#include <gear/GEAR.h>
#include "gear/gearsurf/MeasurementSurfaceStore.h"
#include "gear/gearsurf/MeasurementSurface.h"
#include "gear/gearsurf/ICoordinateSystem.h"
#include "gear/gearsurf/CartesianCoordinateSystem.h"

#include <gsl/gsl_rng.h>
#include <gsl/gsl_randist.h>

#include "CLHEP/Vector/TwoVector.h"

#include <cmath>
#include <algorithm>

DECLARE_COMPONENT( PlanarDigiAlg )

PlanarDigiAlg::PlanarDigiAlg(const std::string& name, ISvcLocator* svcLoc)
  : GaudiAlgorithm(name, svcLoc),
    _nEvt(0)
{
  //_description = "PlanarDigiAlg creates TrackerHits from SimTrackerHits, smearing them according to the input parameters." ;
  
  // Input collections
  declareProperty("HeaderCol", _headerCol);
  declareProperty("SimTrackHitCollection", _inColHdl, "Handle of the Input SimTrackerHit collection");
  
  // Output collections
  declareProperty("TrackerHitCollection", _outColHdl, "Handle of the TrackerHit output collection");
  declareProperty("TrackerHitAssociationCollection", _outRelColHdl, "Handle of TrackerHit SimTrackHit relation collection");
}

StatusCode PlanarDigiAlg::initialize()
{
  if (_parameterize) {
    if (_parU.size()!=10 || _parV.size()!=10) {
      fatal() << "parameters number must be 10! now " << _parU.size() << " for U and " << _parV.size() << " for V" << endmsg;
      return StatusCode::FAILURE;
    }
  }
  else {
    if (_resU.size() !=  _resV.size()) {
      fatal() << "Inconsistent number of resolutions given for U and V coordinate: " 
              << "ResolutionU  :" <<   _resU.size() << " != ResolutionV : " <<  _resV.size()
              << endmsg;

      return StatusCode::FAILURE;
    }
  }

  // get the GEAR manager
  auto _gear = service<IGearSvc>("GearSvc");
  if ( !_gear ) {
    error() << "Failed to find GearSvc ..." << endmsg;
    return StatusCode::FAILURE;
  }
  _GEAR = _gear->getGearMgr();

  // initialize gsl random generator
  _rng = gsl_rng_alloc(gsl_rng_ranlxs2);
  _SEEDER = service<IEventSeeder>("EventSeeder");
  _SEEDER->registerAlg(this);

  // zoujh: TODO - is this necessary? ...
  //FIXME:SJA: if we want the surface store to be filled we need to create an instance of MarlinTrk implemented with KalTest/KalDet
  //TODO:MarlinTrk::IMarlinTrkSystem* trksystem =  MarlinTrk::Factory::createMarlinTrkSystem( "KalTest" , marlin::Global::GEAR , "" ) ;
  //TODO:if( trksystem == 0 ) {
  //TODO:  throw EVENT::Exception( std::string("  Cannot initialize MarlinTrkSystem of Type: ") + std::string("KalTest" )  ) ;
  //TODO:}
  //TODO:trksystem->init() ;
  //FIXME:SJA gear surface store has now been filled so we can dispose of the MarlinTrkSystem
  //TODO:delete trksystem;
  // fucd: fix TODO - surface store is needed to fill once always if does not handle tracking algorithm in job
  auto _trackSystemSvc = service<ITrackSystemSvc>("TrackSystemSvc");
  if ( !_trackSystemSvc ) {
    error() << "Failed to find TrackSystemSvc ..." << endmsg;
    return StatusCode::FAILURE;
  }

  MarlinTrk::IMarlinTrkSystem* _trksystem =  _trackSystemSvc->getTrackSystem(this);
  _trksystem->init();

  _trackSystemSvc->removeTrackSystem(this);
  
  return GaudiAlgorithm::initialize();
}

StatusCode PlanarDigiAlg::execute()
{
  //auto header = _headerCol.get()->at(0);
  //int evtNo = header.getEventNumber();
  //int runNo = header.getRunNumber();
  //debug() << "Processing Run[" << runNo << "]::Event[" << evtNo << "]" << endmsg;

  //unsigned int thisSeed = _SEEDER->getSeed(this, evtNo, runNo);
  unsigned int thisSeed = _SEEDER->getSeed(this, _nEvt, 0);
  gsl_rng_set( _rng, thisSeed ) ;   
  debug() << "seed set to " << thisSeed << endmsg;

  auto trkhitVec = _outColHdl.createAndPut();
  auto relCol = _outRelColHdl.createAndPut();
  //auto STHcol = _inColHdl.get();
  //if ( STHcol == nullptr ) {
  //  debug() << "Collection " << _inColHdl.fullKey() << " is unavailable in event " << _nEvt << endmsg;
  //  return StatusCode::SUCCESS;  // or FAILURE ?
  //}

  const edm4hep::SimTrackerHitCollection* STHcol = nullptr;
  try {
    STHcol = _inColHdl.get();
  }
  catch ( GaudiException &e ) {
    debug() << "Collection " << _inColHdl.fullKey() << " is unavailable in event " << _nEvt << endmsg;
    return StatusCode::SUCCESS;
  }

  // **
  // ** STHcol != 0
  // **

  unsigned nCreatedHits=0;
  unsigned nDismissedHits=0;

  //CellIDEncoder<TrackerHitPlaneImpl> cellid_encoder( lcio::ILDCellID0::encoder_string , trkhitVec ) ;

  int nSimHits = STHcol->size()  ;

  int det_id = 0 ;
  UTIL::BitField64 encoder( lcio::ILDCellID0::encoder_string ) ;

  if ( nSimHits>0 ) {
    auto SimTHit = STHcol->at( 0 ) ;
    encoder.setValue(SimTHit.getCellID()) ;
    det_id  = encoder[lcio::ILDCellID0::subdet] ;
  
    if     ( det_id == CEPCConf::DetID::VXD ){}
    else if( det_id == CEPCConf::DetID::SIT ){}
    else if( det_id == CEPCConf::DetID::SET ){}
    else if( det_id == CEPCConf::DetID::FTD ){}
    else if( det_id == CEPCConf::DetID::ETD ){}
    else {
      fatal() << "unsupported detector ID = " << det_id << " CellID = " << SimTHit.getCellID()
              << ": file " << __FILE__ << " line " << __LINE__
              << endmsg;
      return StatusCode::FAILURE;
    }
  }

  //smearing
  debug() << " processing collection " << _inColHdl.fullKey()
          << " with " <<  nSimHits  << " hits ... " << endmsg ;

  int i = 0;
  for( auto SimTHit : *STHcol ) {
    if (SimTHit.getEDep()<=_eThreshold) continue;
    if (gsl_ran_flat(_rng, 0, 1)>_efficiency) continue;
    debug() << "MCParticle id " << SimTHit.getMCParticle().id() << endmsg;

    const int celId = SimTHit.getCellID() ;

    encoder.setValue(celId) ;
    int side   = encoder[lcio::ILDCellID0::side];
    int layer  = encoder[lcio::ILDCellID0::layer];
    int module = encoder[lcio::ILDCellID0::module];
    int sensor = encoder[lcio::ILDCellID0::sensor];

    debug() << "Hit = " << i << " has celId " << celId << endmsg;
    debug() << "side = " << side << endmsg;
    debug() << "layerNumber = " <<  layer << endmsg;
    debug() << "moduleNumber = " << module << endmsg;
    debug() << "sensorNumber = " << sensor << endmsg;

    //      //************************************************************
    //      // Quick check if the MCParticle is in the list of MCParticles
    //      //************************************************************
    //
    //      if ( ! SimTHit->getMCParticle().isAvailable() ) {
    //        error() << " SimHit " << SimTHit << " Created by zero MCParticle which is not in the list of MCParticles: "
    //                << endmsg;
    //        continue;
    //      }
    //
    //      //************************************************************
    //      // Quick check if the MCParticle is of zero charge
    //      //************************************************************
    //
    //      if( abs( SimTHit->getMCParticle().getCharge()) < 0.01  ){
    //        error() << " SimHit Created by zero charge particle: "
    //                << " Charge =  " << SimTHit->getMCParticle().getCharge()
    //                << " EDep =  " << SimTHit->getEDep()
    //                << " x =  " << SimTHit->getPosition()[0]
    //                << " PDG =  " << SimTHit->getMCParticle().getPDG()
    //                << " ID =  " << SimTHit->getMCParticle().id()
    //                << endmsg;
    //        continue;
    //      }
    gear::MeasurementSurface const* ms = _GEAR->getMeasurementSurfaceStore().GetMeasurementSurface( encoder.lowWord() );
    gear::CartesianCoordinateSystem* cartesian = dynamic_cast< gear::CartesianCoordinateSystem* >( ms->getCoordinateSystem() );
    CLHEP::Hep3Vector uVec = cartesian->getLocalXAxis();
    CLHEP::Hep3Vector vVec = cartesian->getLocalYAxis();

    float u_direction[2] ;
    u_direction[0] = uVec.theta();
    u_direction[1] = uVec.phi();
    
    float v_direction[2] ;
    v_direction[0] = vVec.theta();
    v_direction[1] = vVec.phi();
    
    debug() << " U[0] = "<< u_direction[0] << " U[1] = "<< u_direction[1]
            << " V[0] = "<< v_direction[0] << " V[1] = "<< v_direction[1]
            << endmsg ;
    
    float resU(0), resV(0), resT(0);

    if (!_parameterize) {
      if( (_resU.size() > 1 && layer > _resU.size()-1) || (_resV.size() > 1 && layer > _resV.size()-1) ) {
        fatal() << "layer exceeds resolution vector, please check input parameters ResolutionU and ResolutionV" << endmsg;
        return StatusCode::FAILURE;
      }

      resU = ( _resU.size() > 1 ?   _resU.value().at(  layer )     : _resU.value().at(0)   )  ;
      resV = ( _resV.size() > 1 ?   _resV.value().at(  layer )     : _resV.value().at(0)   )  ; 
    }
    else { // Riccardo's parameterized model
      auto mom = SimTHit.getMomentum();
      CLHEP::Hep3Vector momVec(mom[0], mom[1], mom[2]);
      const double alpha = uVec.azimAngle(momVec, vVec);
      const double cotanAlpha = 1./tan(alpha);
      // TODO: title angle (PI/2), magnetic field (3) 
      const double tanLorentzAngle = (side==0) ? 0. : 0.053 * 3 * cos(M_PI/2.);
      const double x = fabs(-cotanAlpha - tanLorentzAngle);
      resU = _parU[0] + _parU[1] * x + _parU[2] * exp(-_parU[9] * x) * cos(_parU[3] * x + _parU[4])
        + _parU[5] * exp(-0.5 * pow(((x - _parU[6]) / _parU[7]), 2)) + _parU[8] * pow(x, 0.5);
      
      const double beta = vVec.azimAngle(momVec, uVec);
      const double cotanBeta = 1./tan(beta);
      const double y = fabs(-cotanBeta);
      resV = _parV[0] + _parV[1] * y + _parV[2] * exp(-_parV[9] * y) * cos(_parV[3] * y + _parV[4])
        + _parV[5] * exp(-0.5 * pow(((y - _parV[6]) / _parV[7]), 2)) + _parV[8] * pow(y, 0.5);
    }
    // parameterize only for position now, todo
    resT = ( _resT.size() > 1 ? _resT.value().at(layer) : _resT.value().at(0) );
    // from ps (input unit) to ns (record unit, Geant4)
    resT *= CLHEP::ps/CLHEP::ns;

    debug() << " --- will smear hit with resU = " << resU << " and resV = " << resV << endmsg;

    auto& pos =  SimTHit.getPosition() ;  

    CLHEP::Hep3Vector globalPoint(pos[0],pos[1],pos[2]);
    CLHEP::Hep3Vector localPoint = ms->getCoordinateSystem()->getLocalPoint(globalPoint);
    CLHEP::Hep3Vector localPointSmeared = localPoint;

    debug() << std::setprecision(8) << "Position of hit before smearing global: ( "<<pos[0]<<" "<<pos[1]<<" "<<pos[2]<< " ) "
            << "local: ( " << localPoint.x() << " " << localPoint.y() << " " << localPoint.z() << " )" << endmsg;

    // A small check, if the hit is in the boundaries:
    if ( !ms->isLocalInBoundary( localPoint ) ){

      error() << "Hit local: ( " << localPoint.x() << " " << localPoint.y() << " " << localPoint.z() << " )"
              << " is not within boundaries. Hit is skipped." << endmsg;

      nDismissedHits++;
      continue;
    }

    unsigned  tries = 0;              

    bool accept_hit = false;

    // Now try to smear the hit and make sure it is still on the surface
    while( tries < 100 ) {

      if (tries > 0) {
        debug() << "retry smearing for side" << side << " layer"<< layer<< " module" << module
                << " sensor" << sensor << " : retries " << tries << endmsg;
      }

      double dx = gsl_ran_gaussian(_rng, resU);
      double dy = gsl_ran_gaussian(_rng, resV);
      localPointSmeared.setX( localPoint.x() + dx );
      localPointSmeared.setY( localPoint.y() + dy );

      //check if hit is in boundaries
      if ( ms->isLocalInBoundary( localPointSmeared ) && fabs(dx)<=_maxPull*resU && fabs(dy)<=_maxPull*resV ) {
        accept_hit = true;
        break;
      }

      tries++;
    }

    if( accept_hit == false ) {
      debug() << "hit could not be smeared within ladder after 100 tries: hit dropped"  << endmsg;
      continue; 
    }

    // for 1D strip measurements: set v to 0! Only the measurement in u counts!
    if( _isStrip || (resU!=0&&resV==0) ) localPointSmeared[1] = 0. ;

    // convert back to global position for TrackerHitPlaneImpl
    CLHEP::Hep3Vector globalPointSmeared = ms->getCoordinateSystem()->getGlobalPoint(localPointSmeared);

    debug() <<"Position of hit after smearing global: ( "  
            << globalPointSmeared.x() <<" "<< globalPointSmeared.y() <<" "<< globalPointSmeared.z() << " ) "
            << "local: ( "
            << localPointSmeared.x() << " " << localPointSmeared.y() << " " << localPointSmeared.z() << " )"
            << endmsg;
    
    //make the TrackerHitPlaneImpl
    auto trkHit = trkhitVec->create();

    trkHit.setCellID( encoder.lowWord() );

    edm4hep::Vector3d smearedPos(globalPointSmeared.x(), globalPointSmeared.y(), globalPointSmeared.z());
    trkHit.setPosition( smearedPos ) ;
    /*
    gear::CartesianCoordinateSystem* cartesian = dynamic_cast< gear::CartesianCoordinateSystem* >( ms->getCoordinateSystem() ); 
    CLHEP::Hep3Vector uVec = cartesian->getLocalXAxis();
    CLHEP::Hep3Vector vVec = cartesian->getLocalYAxis();

    float u_direction[2] ;
    u_direction[0] = uVec.theta();
    u_direction[1] = uVec.phi();

    float v_direction[2] ;
    v_direction[0] = vVec.theta();
    v_direction[1] = vVec.phi();

    debug() << " U[0] = "<< u_direction[0] << " U[1] = "<< u_direction[1]
            << " V[0] = "<< v_direction[0] << " V[1] = "<< v_direction[1]
            << endmsg ;
    */
    // fucd: next TODO: cov[0] = resU*reU, cov[2] = resV*resV, cov[5] = 0
    if(_usePlanarTag){
      std::array<float, 6> cov;
      cov[0] = u_direction[0];
      cov[1] = u_direction[1];
      cov[2] = resU;
      cov[3] = v_direction[0];
      cov[4] = v_direction[1];
      cov[5] = resV;
      trkHit.setCovMatrix(cov);
    /* zoujh: TODO - generate TrackerHitPlane with podio
    trkHit->setU( u_direction ) ;
    trkHit->setV( v_direction ) ;

    trkHit->setdU( resU ) ;

    if( _isStrip ) trkHit->setdV( 0 ); // no error in v direction for strip hits as there is no meesurement information in v direction
    else trkHit->setdV( resV ) ;
    */
      std::bitset<32> type;
      type.set(CEPCConf::TrkHitTypeBit::PLANAR);
      trkHit.setType((int)type.to_ulong());
    }
    else{
      trkHit.setCovMatrix(CEPC::ConvertToCovXYZ(resU, u_direction[0], u_direction[1], resV, v_direction[0], v_direction[1]));
    }

    if( _isStrip || (resU!=0&&resV==0) ){
        trkHit.setType( UTIL::set_bit( trkHit.getType() , CEPCConf::TrkHitTypeBit::ONE_DIMENSIONAL ) ) ;
    }
    trkHit.setEDep( SimTHit.getEDep() );
    trkHit.setTime( SimTHit.getTime() + gsl_ran_gaussian(_rng, resT) );

    // make the relation
    auto rel = relCol->create();

    float weight = 1.0;

    debug() <<" Set relation between "
            << " sim hit " << SimTHit.id() 
            << " to tracker hit " << trkHit.id()
            << " with a weight of " << weight 
            << endmsg;
    trkHit.addToRawHits(SimTHit.getObjectID());
    rel.setSim(SimTHit);
    rel.setRec(trkHit);
    rel.setWeight(weight);

    nCreatedHits++;

    debug() << "-------------------------------------------------------" << endmsg;

    ++i;
  }

  debug() << "Created " << nCreatedHits << " hits, "
          << nDismissedHits << " hits got dismissed for being out of boundary"
          << endmsg;
    
  _nEvt ++ ;

  return StatusCode::SUCCESS;
}
StatusCode PlanarDigiAlg::finalize()
{
  info() << "Processed " << _nEvt << " events " << endmsg;

  if(_rng) gsl_rng_free(_rng);

  return GaudiAlgorithm::finalize();
}