diff --git a/Reconstruction/CMakeLists.txt b/Reconstruction/CMakeLists.txt index db8d7c2c2d235b8ce81772520b7465a5f4d96989..4ce55556ae4e3dbe4ec8b2be28d59939d56d9554 100644 --- a/Reconstruction/CMakeLists.txt +++ b/Reconstruction/CMakeLists.txt @@ -7,3 +7,4 @@ add_subdirectory(RecTrkGlobal) add_subdirectory(RecDCHGenfit) add_subdirectory(RecAssociationMaker) add_subdirectory(ParticleID) +add_subdirectory(RecActsTracking) \ No newline at end of file diff --git a/Reconstruction/RecActsTracking/CMakeLists.txt b/Reconstruction/RecActsTracking/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..d0c8c9d9afb80c5cb5b86a8b3df2b3df14ecbc0e --- /dev/null +++ b/Reconstruction/RecActsTracking/CMakeLists.txt @@ -0,0 +1,37 @@ +find_package(Acts COMPONENTS + Core PluginFpeMonitoring PluginGeant4 PluginJson + PluginTGeo PluginDD4hep PluginEDM4hep Fatras) + +if(NOT Acts_FOUND) + message("Acts package not found. RecActsTracking module requires Acts.") + return() +endif() + +gaudi_add_module(RecActsTracking + SOURCES + src/RecActsTracking.cpp + LINK DetInterface + k4FWCore::k4FWCore + Gaudi::GaudiAlgLib Gaudi::GaudiKernel + ${LCIO_LIBRARIES} + ${DD4hep_COMPONENT_LIBRARIES} + EDM4HEP::edm4hep EDM4HEP::edm4hepDict + ${podio_LIBRARIES} podio::podioRootIO + GearSvc ${GEAR_LIBRARIES} + ActsCore ActsPluginFpeMonitoring ActsPluginGeant4 + ActsPluginJson ActsPluginTGeo ActsPluginDD4hep + ActsPluginEDM4hep ActsFatras +) + +target_include_directories(RecActsTracking PUBLIC $ENV{ACTS}/include) + +target_include_directories(RecActsTracking PUBLIC + ${LCIO_INCLUDE_DIRS} + $<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}>/include + $<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>) + +install(TARGETS RecActsTracking + EXPORT CEPCSWTargets + RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}" COMPONENT bin + LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}" COMPONENT shlib + COMPONENT dev) diff --git a/Reconstruction/RecActsTracking/options/RecActsTracking.py b/Reconstruction/RecActsTracking/options/RecActsTracking.py new file mode 100644 index 0000000000000000000000000000000000000000..c570bad28e859a3e9333567108be4e90225b798f --- /dev/null +++ b/Reconstruction/RecActsTracking/options/RecActsTracking.py @@ -0,0 +1,79 @@ +#!/usr/bin/env python +import os + +from Gaudi.Configuration import * + +NTupleSvc().Output = ["MyTuples DATAFILE='test.root' OPT='NEW' TYP='ROOT'"] + +from Configurables import k4DataSvc +dsvc = k4DataSvc("EventDataSvc", input="rec00.root") + +from Configurables import PodioInput +podioinput = PodioInput("PodioReader", collections=[ + "MCParticle", + "VXDCollection", + "SITCollection", + "VXDTrackerHits", + "SITTrackerHits", +]) + +############################################################################## +# Geometry Svc +############################################################################## + +# geometry_option = "TDR_o1_v01/TDR_o1_v01-onlyTracker.xml" +geometry_option = "TDR_o1_v01/TDR_o1_v01.xml" + +if not os.getenv("DETCRDROOT"): + print("Can't find the geometry. Please setup envvar DETCRDROOT." ) + sys.exit(-1) + +geometry_path = os.path.join(os.getenv("DETCRDROOT"), "compact", geometry_option) +if not os.path.exists(geometry_path): + print("Can't find the compact geometry file: %s"%geometry_path) + sys.exit(-1) + +from Configurables import DetGeomSvc +geosvc = DetGeomSvc("GeomSvc") +geosvc.compact = geometry_path + +from Configurables import MarlinEvtSeeder +evtseeder = MarlinEvtSeeder("EventSeeder") + +from Configurables import GearSvc +gearsvc = GearSvc("GearSvc") + +from Configurables import TrackSystemSvc +tracksystemsvc = TrackSystemSvc("TrackSystemSvc") + +############################################################################## +# CEPCSWData +############################################################################## +cepcswdatatop ="/cvmfs/cepcsw.ihep.ac.cn/prototype/releases/data/latest" + +############################################################################## +# RecActsTracking +############################################################################## + +from Configurables import RecActsTracking +actstracking = RecActsTracking("RecActsTracking") +actstracking.TGeoFile = os.path.join(cepcswdatatop, "CEPCSWData/offline-data/Reconstruction/RecActsTracking/data/tdr24.12.0/SiTrack-tgeo.root") +actstracking.TGeoConfigFile = os.path.join(cepcswdatatop, "CEPCSWData/offline-data/Reconstruction/RecActsTracking/data/tdr24.12.0/SiTrack-tgeo-config.json") +actstracking.MaterialMapFile = os.path.join(cepcswdatatop, "CEPCSWData/offline-data/Reconstruction/RecActsTracking/data/tdr24.12.0/SiTrack-material-maps.json") + +############################################################################## +# output +############################################################################## +from Configurables import PodioOutput +out = PodioOutput("out") +out.filename = "ACTS_rec00.root" +out.outputCommands = ["keep *"] + +# ApplicationMgr +from Configurables import ApplicationMgr +ApplicationMgr( TopAlg = [podioinput, actstracking, out], + EvtSel = 'NONE', + EvtMax = 5, + ExtSvc = [dsvc, geosvc, evtseeder, gearsvc], + # OutputLevel=DEBUG +) diff --git a/Reconstruction/RecActsTracking/src/RecActsTracking.cpp b/Reconstruction/RecActsTracking/src/RecActsTracking.cpp new file mode 100644 index 0000000000000000000000000000000000000000..cd228ee7b5be4b3f5fc31b2ea115424a4293746e --- /dev/null +++ b/Reconstruction/RecActsTracking/src/RecActsTracking.cpp @@ -0,0 +1,928 @@ +#include <iostream> +#include <fstream> +#include <cstdlib> +#include <sstream> + +// dependence +#include "RecActsTracking.h" + +#include "GearSvc/IGearSvc.h" + +// csv parser +// #include "csv2/writer.hpp" + +// MC +#include "CLHEP/Units/SystemOfUnits.h" + + +using namespace Acts::UnitLiterals; + +DECLARE_COMPONENT(RecActsTracking) + +RecActsTracking::RecActsTracking(const std::string& name, ISvcLocator* svcLoc) + : GaudiAlgorithm(name, svcLoc) +{ + // input collections + // declareProperty("VXDTrackerHits", _inVTXTrackHdl, "Handle of the Input VTX TrackerHits collection"); + // declareProperty("SITTrackerHits", _inSITTrackHdl, "Handle of the Input SIT TrackerHits collection"); + + // declareProperty("VXDCollection", _inVTXColHdl, "Handle of the Input VTX Sim Hit collection"); + // declareProperty("SITCollection", _inSITColHdl, "Handle of the Input SIT Sim Hit collection"); + + // declareProperty("MCParticleCollection", _inMCColHdl, "Handle of the Input MCParticle collection"); + + // Output Collections + // declareProperty("OutputTracks", _outColHdl, "Handle of the ACTS SiTrack output collection"); +} + +StatusCode RecActsTracking::initialize() +{ + chronoStatSvc = service<IChronoStatSvc>("ChronoStatSvc"); + _nEvt = 0; + + if (!std::filesystem::exists(TGeo_path.value())) { + error() << "CEPC TGeo file: " << TGeo_path.value() << " does not exist!" << endmsg; + return StatusCode::FAILURE; + } + + if (!std::filesystem::exists(TGeo_config_path.value())) { + error() << "CEPC TGeo config file: " << TGeo_config_path.value() << " does not exist!" << endmsg; + return StatusCode::FAILURE; + } + + if (!std::filesystem::exists(MaterialMap_path.value())) { + error() << "CEPC Material map file: " << MaterialMap_path.value() << " does not exist!" << endmsg; + return StatusCode::FAILURE; + } + + TGeo_ROOTFilePath = TGeo_path.value(); + TGeoConfig_jFilePath = TGeo_config_path.value(); + MaterialMap_jFilePath = MaterialMap_path.value(); + + chronoStatSvc->chronoStart("read geometry"); + m_geosvc = service<IGeomSvc>("GeomSvc"); + if (!m_geosvc) { + error() << "Failed to find GeomSvc." << endmsg; + return StatusCode::FAILURE; + } + + if(m_geosvc){ + const dd4hep::Direction& field = m_geosvc->lcdd()->field().magneticField(dd4hep::Position(0,0,0)); + m_field = field.z()/dd4hep::tesla; + } + + m_vtx_surfaces = m_geosvc->getSurfaceMap("VXD"); + debug() << "Surface map size: " << m_vtx_surfaces->size() << endmsg; + + m_sit_surfaces = m_geosvc->getSurfaceMap("SIT"); + debug() << "Surface map size: " << m_sit_surfaces->size() << endmsg; + + vxd_decoder = m_geosvc->getDecoder("VXDCollection"); + if(!vxd_decoder){ + return StatusCode::FAILURE; + } + + sit_decoder = m_geosvc->getDecoder("SITCollection"); + if(!sit_decoder){ + return StatusCode::FAILURE; + } + + info() << "ACTS Tracking Geometry initialized successfully!" << endmsg; + // initialize tgeo detector + auto logger = Acts::getDefaultLogger("TGeoDetector", Acts::Logging::INFO); + trackingGeometry = buildTGeoDetector(geoContext, detElementStore, TGeo_ROOTFilePath, TGeoConfig_jFilePath, MaterialMap_jFilePath, *logger); + + info() << "Seeding tools initialized successfully!" << endmsg; + + // configure the acts tools + seed_cfg.seedFinderOptions.bFieldInZ = 3_T; + seed_cfg.seedFinderConfig.deltaRMin = 4_mm; + seed_cfg.seedFinderConfig.deltaRMax = 25_mm; + seed_cfg.seedFinderConfig.rMax = 40_mm; + seed_cfg.seedFinderConfig.rMin = 8_mm; + seed_cfg.seedFinderConfig.impactMax = 4_mm; + seed_cfg.seedFinderConfig.useVariableMiddleSPRange = false; + seed_cfg.seedFinderConfig.rMinMiddle = 15_mm; // range for middle spacepoint (TDR) + seed_cfg.seedFinderConfig.rMaxMiddle = 30_mm; // range for middle spacepoint (TDR) + + // initialize the acts tools + seed_cfg.seedFilterConfig = seed_cfg.seedFilterConfig.toInternalUnits(); + seed_cfg.seedFinderConfig.seedFilter = + std::make_unique<Acts::SeedFilter<SimSpacePoint>>(seed_cfg.seedFilterConfig); + seed_cfg.seedFinderConfig = + seed_cfg.seedFinderConfig.toInternalUnits().calculateDerivedQuantities(); + seed_cfg.seedFinderOptions = + seed_cfg.seedFinderOptions.toInternalUnits().calculateDerivedQuantities(seed_cfg.seedFinderConfig); + seed_cfg.gridConfig = seed_cfg.gridConfig.toInternalUnits(); + seed_cfg.gridOptions = seed_cfg.gridOptions.toInternalUnits(); + + if (std::isnan(seed_cfg.seedFinderConfig.deltaRMaxTopSP)) { + seed_cfg.seedFinderConfig.deltaRMaxTopSP = seed_cfg.seedFinderConfig.deltaRMax;} + if (std::isnan(seed_cfg.seedFinderConfig.deltaRMinTopSP)) { + seed_cfg.seedFinderConfig.deltaRMinTopSP = seed_cfg.seedFinderConfig.deltaRMin;} + if (std::isnan(seed_cfg.seedFinderConfig.deltaRMaxBottomSP)) { + seed_cfg.seedFinderConfig.deltaRMaxBottomSP = seed_cfg.seedFinderConfig.deltaRMax;} + if (std::isnan(seed_cfg.seedFinderConfig.deltaRMinBottomSP)) { + seed_cfg.seedFinderConfig.deltaRMinBottomSP = seed_cfg.seedFinderConfig.deltaRMin;} + + m_bottomBinFinder = std::make_unique<const Acts::GridBinFinder<2ul>>( + seed_cfg.numPhiNeighbors, seed_cfg.zBinNeighborsBottom); + m_topBinFinder = std::make_unique<const Acts::GridBinFinder<2ul>>( + seed_cfg.numPhiNeighbors, seed_cfg.zBinNeighborsTop); + + seed_cfg.seedFinderConfig.seedFilter = + std::make_unique<Acts::SeedFilter<SimSpacePoint>>(seed_cfg.seedFilterConfig); + m_seedFinder = + Acts::SeedFinder<SimSpacePoint, Acts::CylindricalSpacePointGrid<SimSpacePoint>>(seed_cfg.seedFinderConfig); + + // initialize the ckf + findTracks = makeTrackFinderFunction(trackingGeometry, magneticField); + + info() << "CKF Track Finder initialized successfully!" << endmsg; + chronoStatSvc->chronoStop("read geometry"); + + return GaudiAlgorithm::initialize(); +} + +StatusCode RecActsTracking::execute() +{ + auto trkCol = _outColHdl.createAndPut(); + + bool MCsuccess = true; + const edm4hep::MCParticleCollection* mcCols = nullptr; + mcCols = _inMCColHdl.get(); + if(mcCols) + { + for(auto particle : *mcCols) + { + if (particle.getPDG() != 13) + { + MCsuccess = false; + continue; + } + break; + } + } + + if (!MCsuccess) + { + _nEvt++; + return StatusCode::SUCCESS; + } + + SpacePointPtrs.clear(); + sourceLinks.clear(); + measurements.clear(); + initialParameters.clear(); + Selected_Seeds.clear(); + + chronoStatSvc->chronoStart("read input hits"); + int successVTX = InitialiseVTX(); + + if (successVTX == 0) + { + _nEvt++; + return StatusCode::SUCCESS; + } + + int successSIT = InitialiseSIT(); + if (successSIT == 0) + { + _nEvt++; + return StatusCode::SUCCESS; + } + + chronoStatSvc->chronoStop("read input hits"); + // info() << "Generated " << SpacePointPtrs.size() << " spacepoints for event " << _nEvt << "!" << endmsg; + // info() << "Generated " << measurements.size() << " measurements for event " << _nEvt << "!" << endmsg; + + // -------------------------------------------- + // Seeding + // -------------------------------------------- + chronoStatSvc->chronoStart("seeding"); + + // construct the seeding tools + // covariance tool, extracts covariances per spacepoint as required + auto extractGlobalQuantities = [=](const SimSpacePoint& sp, float, float, float) + { + Acts::Vector3 position{sp.x(), sp.y(), sp.z()}; + Acts::Vector2 covariance{sp.varianceR(), sp.varianceZ()}; + return std::make_tuple(position, covariance, sp.t()); + }; + + // extent used to store r range for middle spacepoint + Acts::Extent rRangeSPExtent; + + // construct the seeding tool + Acts::CylindricalSpacePointGrid<SimSpacePoint> grid = + Acts::CylindricalSpacePointGridCreator::createGrid<SimSpacePoint>(seed_cfg.gridConfig, seed_cfg.gridOptions); + Acts::CylindricalSpacePointGridCreator::fillGrid( + seed_cfg.seedFinderConfig, seed_cfg.seedFinderOptions, grid, + SpacePointPtrs.begin(), SpacePointPtrs.end(), extractGlobalQuantities, rRangeSPExtent); + + std::array<std::vector<std::size_t>, 2ul> navigation; + navigation[1ul] = seed_cfg.seedFinderConfig.zBinsCustomLooping; + + auto spacePointsGrouping = Acts::CylindricalBinnedGroup<SimSpacePoint>( + std::move(grid), *m_bottomBinFinder, *m_topBinFinder, std::move(navigation)); + + // safely clamp double to float + float up = Acts::clampValue<float>( + std::floor(rRangeSPExtent.max(Acts::binR) / 2) * 2); + + /// variable middle SP radial region of interest + const Acts::Range1D<float> rMiddleSPRange( + std::floor(rRangeSPExtent.min(Acts::binR) / 2) * 2 + + seed_cfg.seedFinderConfig.deltaRMiddleMinSPRange, + up - seed_cfg.seedFinderConfig.deltaRMiddleMaxSPRange); + + // run the seeding + static thread_local SimSeedContainer seeds; + seeds.clear(); + static thread_local decltype(m_seedFinder)::SeedingState state; + state.spacePointData.resize( + SpacePointPtrs.size(), + seed_cfg.seedFinderConfig.useDetailedDoubleMeasurementInfo); + + // use double stripe measurement + if (seed_cfg.seedFinderConfig.useDetailedDoubleMeasurementInfo) + { + for (std::size_t grid_glob_bin(0); grid_glob_bin < spacePointsGrouping.grid().size(); ++grid_glob_bin) + { + const auto& collection = spacePointsGrouping.grid().at(grid_glob_bin); + for (const auto& sp : collection) + { + std::size_t index = sp->index(); + const float topHalfStripLength = + seed_cfg.seedFinderConfig.getTopHalfStripLength(sp->sp()); + const float bottomHalfStripLength = + seed_cfg.seedFinderConfig.getBottomHalfStripLength(sp->sp()); + const Acts::Vector3 topStripDirection = + seed_cfg.seedFinderConfig.getTopStripDirection(sp->sp()); + const Acts::Vector3 bottomStripDirection = + seed_cfg.seedFinderConfig.getBottomStripDirection(sp->sp()); + + state.spacePointData.setTopStripVector( + index, topHalfStripLength * topStripDirection); + state.spacePointData.setBottomStripVector( + index, bottomHalfStripLength * bottomStripDirection); + state.spacePointData.setStripCenterDistance( + index, seed_cfg.seedFinderConfig.getStripCenterDistance(sp->sp())); + state.spacePointData.setTopStripCenterPosition( + index, seed_cfg.seedFinderConfig.getTopStripCenterPosition(sp->sp())); + } + } + } + + for (const auto [bottom, middle, top] : spacePointsGrouping) + { + m_seedFinder.createSeedsForGroup( + seed_cfg.seedFinderOptions, state, spacePointsGrouping.grid(), + std::back_inserter(seeds), bottom, middle, top, rMiddleSPRange); + } + + // int seed_counter = 0; + // for (const auto& seed : seeds) + // { + // for (const auto& sp : seed.sp()) + // { + // info() << "found seed #" << seed_counter << ": x:" << sp->x() << " y: " << sp->y() << " z: " << sp->z() << endmsg; + // } + // seed_counter++; + // } + + chronoStatSvc->chronoStop("seeding"); + debug() << "Found " << seeds.size() << " seeds for event " << _nEvt << "!" << endmsg; + + // -------------------------------------------- + // track estimation + // -------------------------------------------- + chronoStatSvc->chronoStart("track_param"); + + IndexSourceLink::SurfaceAccessor surfaceAccessor{*trackingGeometry}; + + for (std::size_t iseed = 0; iseed < seeds.size(); ++iseed) + { + const auto& seed = seeds[iseed]; + // Get the bottom space point and its reference surface + const auto bottomSP = seed.sp().front(); + const auto& sourceLink = bottomSP->sourceLinks()[0]; + const Acts::Surface* surface = surfaceAccessor(sourceLink); + if (surface == nullptr) { + debug() << "Surface from source link is not found in the tracking geometry: iseed " << iseed << endmsg; + continue; + } + + auto optParams = Acts::estimateTrackParamsFromSeed( + geoContext, seed.sp().begin(), seed.sp().end(), *surface, acts_field_value, bFieldMin); + + if (!optParams.has_value()) { + debug() << "Estimation of track parameters for seed " << iseed << " failed." << endmsg; + continue; + } + + const auto& params = optParams.value(); + Acts::BoundSquareMatrix cov = Acts::BoundSquareMatrix::Zero(); + for (std::size_t i = Acts::eBoundLoc0; i < Acts::eBoundSize; ++i) { + double sigma = initialSigmas[i]; + sigma += initialSimgaQoverPCoefficients[i] * params[Acts::eBoundQOverP]; + double var = sigma * sigma; + if (i == Acts::eBoundTime && !bottomSP->t().has_value()) { var *= noTimeVarInflation; } + var *= initialVarInflation[i]; + + cov(i, i) = var; + } + initialParameters.emplace_back(surface->getSharedPtr(), params, cov, particleHypothesis); + Selected_Seeds.push_back(seed); + } + + chronoStatSvc->chronoStop("track_param"); + debug() << "Found " << initialParameters.size() << " tracks for event " << _nEvt << "!" << endmsg; + + // -------------------------------------------- + // CKF track finding + // -------------------------------------------- + chronoStatSvc->chronoStart("ckf_findTracks"); + + // Construct a perigee surface as the target surface + auto pSurface = Acts::Surface::makeShared<Acts::PerigeeSurface>(Acts::Vector3{0., 0., 0.}); + PassThroughCalibrator pcalibrator; + MeasurementCalibratorAdapter calibrator(pcalibrator, measurements); + Acts::GainMatrixUpdater kfUpdater; + Acts::MeasurementSelector::Config measurementSelectorCfg = + { + {Acts::GeometryIdentifier(), {{}, {30}, {1u}}}, + }; + + MeasurementSelector measSel{ Acts::MeasurementSelector(measurementSelectorCfg) }; + using Extensions = Acts::CombinatorialKalmanFilterExtensions<Acts::VectorMultiTrajectory>; + BranchStopper branchStopper(trackSelectorCfg); + + // Construct the CKF + Extensions extensions; + extensions.calibrator.connect<&MeasurementCalibratorAdapter::calibrate>(&calibrator); + extensions.updater.connect<&Acts::GainMatrixUpdater::operator()<Acts::VectorMultiTrajectory>>(&kfUpdater); + extensions.measurementSelector.connect<&MeasurementSelector::select>(&measSel); + extensions.branchStopper.connect<&BranchStopper::operator()>(&branchStopper); + + IndexSourceLinkAccessor slAccessor; + slAccessor.container = &sourceLinks; + Acts::SourceLinkAccessorDelegate<IndexSourceLinkAccessor::Iterator> slAccessorDelegate; + slAccessorDelegate.connect<&IndexSourceLinkAccessor::range>(&slAccessor); + + Acts::PropagatorPlainOptions firstPropOptions; + firstPropOptions.maxSteps = maxSteps; + firstPropOptions.direction = Acts::Direction::Forward; + + Acts::PropagatorPlainOptions secondPropOptions; + secondPropOptions.maxSteps = maxSteps; + secondPropOptions.direction = firstPropOptions.direction.invert(); + + // Set the CombinatorialKalmanFilter options + TrackFinderOptions firstOptions( + geoContext, magFieldContext, calibContext, + slAccessorDelegate, extensions, firstPropOptions); + + TrackFinderOptions secondOptions( + geoContext, magFieldContext, calibContext, + slAccessorDelegate, extensions, secondPropOptions); + secondOptions.targetSurface = pSurface.get(); + + Acts::Propagator<Acts::EigenStepper<>, Acts::Navigator> extrapolator( + Acts::EigenStepper<>(magneticField), Acts::Navigator({trackingGeometry})); + + Acts::PropagatorOptions<Acts::ActionList<Acts::MaterialInteractor>, Acts::AbortList<Acts::EndOfWorldReached>> + extrapolationOptions(geoContext, magFieldContext); + + auto trackContainer = std::make_shared<Acts::VectorTrackContainer>(); + auto trackStateContainer = std::make_shared<Acts::VectorMultiTrajectory>(); + auto trackContainerTemp = std::make_shared<Acts::VectorTrackContainer>(); + auto trackStateContainerTemp = std::make_shared<Acts::VectorMultiTrajectory>(); + TrackContainer tracks(trackContainer, trackStateContainer); + TrackContainer tracksTemp(trackContainerTemp, trackStateContainerTemp); + + tracks.addColumn<unsigned int>("trackGroup"); + tracksTemp.addColumn<unsigned int>("trackGroup"); + Acts::ProxyAccessor<unsigned int> seedNumber("trackGroup"); + + unsigned int nSeed = 0; + // A map indicating whether a seed has been discovered already + std::unordered_map<SeedIdentifier, bool> discoveredSeeds; + auto addTrack = [&](const TrackProxy& track) + { + ++m_nFoundTracks; + // flag seeds which are covered by the track + visitSeedIdentifiers(track, [&](const SeedIdentifier& seedIdentifier) + { + if (auto it = discoveredSeeds.find(seedIdentifier); it != discoveredSeeds.end()) + { + it->second = true; + } + }); + + if (m_trackSelector.has_value() && !m_trackSelector->isValidTrack(track)) { return; } + + ++m_nSelectedTracks; + auto destProxy = tracks.makeTrack(); + // make sure we copy track states! + destProxy.copyFrom(track, true); + }; + + for (const auto& seed : Selected_Seeds) { + SeedIdentifier seedIdentifier = makeSeedIdentifier(seed); + discoveredSeeds.emplace(seedIdentifier, false); + } + + for (std::size_t iSeed = 0; iSeed < initialParameters.size(); ++iSeed) + { + m_nTotalSeeds++; + const auto& seed = Selected_Seeds[iSeed]; + + SeedIdentifier seedIdentifier = makeSeedIdentifier(seed); + // check if the seed has been discovered already + if (auto it = discoveredSeeds.find(seedIdentifier); it != discoveredSeeds.end() && it->second) + { + m_nDeduplicatedSeeds++; + continue; + } + + /// Whether to stick on the seed measurements during track finding. + // measSel.setSeed(seed); + + // Clear trackContainerTemp and trackStateContainerTemp + tracksTemp.clear(); + + const Acts::BoundTrackParameters& firstInitialParameters = initialParameters.at(iSeed); + auto firstResult = (*findTracks)(firstInitialParameters, firstOptions, tracksTemp); + + nSeed++; + if (!firstResult.ok()) + { + m_nFailedSeeds++; + continue; + } + + auto& firstTracksForSeed = firstResult.value(); + for (auto& firstTrack : firstTracksForSeed) + { + auto trackCandidate = tracksTemp.makeTrack(); + trackCandidate.copyFrom(firstTrack, true); + + auto firstSmoothingResult = Acts::smoothTrack(geoContext, trackCandidate); + + if (!firstSmoothingResult.ok()) + { + m_nFailedSmoothing++; + continue; + } + + seedNumber(trackCandidate) = nSeed - 1; + + auto firstExtrapolationResult = Acts::extrapolateTrackToReferenceSurface( + trackCandidate, *pSurface, extrapolator, extrapolationOptions, extrapolationStrategy); + + if (!firstExtrapolationResult.ok()) + { + m_nFailedExtrapolation++; + continue; + } + + addTrack(trackCandidate); + } + } + + chronoStatSvc->chronoStop("ckf_findTracks"); + debug() << "CKF found " << tracks.size() << " tracks for event " << _nEvt << "!" << endmsg; + + m_memoryStatistics.local().hist += tracks.trackStateContainer().statistics().hist; + auto constTrackStateContainer = std::make_shared<Acts::ConstVectorMultiTrajectory>(std::move(*trackStateContainer)); + auto constTrackContainer = std::make_shared<Acts::ConstVectorTrackContainer>(std::move(*trackContainer)); + ConstTrackContainer constTracks{constTrackContainer, constTrackStateContainer}; + + chronoStatSvc->chronoStart("writeout tracks"); + + if (constTracks.size() == 0) { + chronoStatSvc->chronoStop("writeout tracks"); + _nEvt++; + return StatusCode::SUCCESS; + } + + for (const auto& cur_track : constTracks) + { + auto writeout_track = trkCol->create(); + int nVTXHit = 0, nFTDHit = 0, nSITHit = 0; + int getFirstHit = 0; + + writeout_track.setChi2(cur_track.chi2()); + writeout_track.setNdf(cur_track.nDoF()); + writeout_track.setDEdx(cur_track.absoluteMomentum() / Acts::UnitConstants::GeV); + writeout_track.setDEdxError(cur_track.qOverP()); + for (auto trackState : cur_track.trackStates()){ + if (trackState.hasUncalibratedSourceLink()){ + auto cur_measurement_sl = trackState.getUncalibratedSourceLink(); + const auto& MeasSourceLink = cur_measurement_sl.get<IndexSourceLink>(); + auto cur_measurement = measurements[MeasSourceLink.index()]; + auto cur_measurement_gid = MeasSourceLink.geometryId(); + if (std::find(VXD_inner_volume_ids.begin(), VXD_inner_volume_ids.end(), cur_measurement_gid.volume()) != VXD_inner_volume_ids.end()){ + nVTXHit++; + } else if (std::find(SIT_acts_volume_ids.begin(), SIT_acts_volume_ids.end(), cur_measurement_gid.volume()) != SIT_acts_volume_ids.end()){ + nSITHit++; + } + + writeout_track.addToTrackerHits(MeasSourceLink.getTrackerHit()); + + if (!getFirstHit){ + const auto& par = std::get<1>(cur_measurement).parameters(); + const Acts::Surface* surface = surfaceAccessor(cur_measurement_sl); + auto acts_global_postion = surface->localToGlobal(geoContext, par, globalFakeMom); + writeout_track.setRadiusOfInnermostHit( + std::sqrt(acts_global_postion[0] * acts_global_postion[0] + + acts_global_postion[1] * acts_global_postion[1] + + acts_global_postion[2] * acts_global_postion[2]) + ); + getFirstHit = 1; + } + } + } + + // SubdetectorHitNumbers: VXD = 0, FTD = 1, SIT = 2 + writeout_track.addToSubdetectorHitNumbers(nVTXHit); + writeout_track.addToSubdetectorHitNumbers(nFTDHit); + writeout_track.addToSubdetectorHitNumbers(nSITHit); + + std::array<float, 21> writeout_covMatrix; + auto cur_track_covariance = cur_track.covariance(); + for (int i = 0; i < 6; i++) { + for (int j = 0; j < 6-i; j++) { + writeout_covMatrix[i * 6 + j] = cur_track_covariance(writeout_indices[i], writeout_indices[j]); + } + } + // location: At{Other|IP|FirstHit|LastHit|Calorimeter|Vertex}|LastLocation + // TrackState: location, d0, phi, omega, z0, tanLambda, time, referencePoint, covMatrix + edm4hep::TrackState writeout_trackState{ + 0, // location: AtOther + cur_track.loc0() / Acts::UnitConstants::mm, // d0 + cur_track.phi(), // phi + cur_track.qOverP() * sin(cur_track.theta()) * _FCT * m_field, // omega = qop * sin(theta) * _FCT * bf + cur_track.loc1() / Acts::UnitConstants::mm, // z0 + 1 / tan(cur_track.theta()), // tanLambda = 1 / tan(theta) + cur_track.time(), // time + ::edm4hep::Vector3f(0, 0, 0), // referencePoint + writeout_covMatrix + }; + + debug() << "Found track with momentum " << cur_track.absoluteMomentum() / Acts::UnitConstants::GeV << " !" << endmsg; + writeout_track.addToTrackStates(writeout_trackState); + } + + chronoStatSvc->chronoStop("writeout tracks"); + + _nEvt++; + return StatusCode::SUCCESS; +} + +StatusCode RecActsTracking::finalize() +{ + debug() << "finalize RecActsTracking" << endmsg; + info() << "Total number of events processed: " << _nEvt << endmsg; + info() << "Total number of **TotalSeeds** processed: " << m_nTotalSeeds << endmsg; + info() << "Total number of **FoundTracks** processed: " << m_nFoundTracks << endmsg; + info() << "Total number of **SelectedTracks** processed: " << m_nSelectedTracks << endmsg; + + return GaudiAlgorithm::finalize(); +} + +int RecActsTracking::InitialiseVTX() +{ + int success = 1; + + const edm4hep::TrackerHitCollection* hitVTXCol = nullptr; + const edm4hep::SimTrackerHitCollection* SimhitVTXCol = nullptr; + + try { + hitVTXCol = _inVTXTrackHdl.get(); + } catch (GaudiException& e) { + debug() << "Collection " << _inVTXTrackHdl.fullKey() << " is unavailable in event " << _nEvt << endmsg; + success = 0; + } + + try { + SimhitVTXCol = _inVTXColHdl.get(); + } catch (GaudiException& e) { + debug() << "Sim Collection " << _inVTXColHdl.fullKey() << " is unavailable in event " << _nEvt << endmsg; + success = 0; + } + + if(hitVTXCol && SimhitVTXCol) + { + int nelem = hitVTXCol->size(); + debug() << "Number of VTX hits = " << nelem << endmsg; + if ((nelem < 3) or (nelem > 10)) { success = 0; } + + // std::string truth_file = "obj/vtx/truth/event" + std::to_string(_nEvt) + ".csv"; + // std::ofstream truth_stream(truth_file); + // csv2::Writer<csv2::delimiter<','>> truth_writer(truth_stream); + // std::vector<std::string> truth_header = {"layer", "x", "y", "z"}; + // truth_writer.write_row(truth_header); + + // std::string converted_file = "obj/vtx/converted/event" + std::to_string(_nEvt) + ".csv"; + // std::ofstream converted_stream(converted_file); + // csv2::Writer<csv2::delimiter<','>> converted_writer(converted_stream); + // std::vector<std::string> converted_header = {"layer", "x", "y", "z"}; + // converted_writer.write_row(converted_header); + + for (int ielem = 0; ielem < nelem; ++ielem) + { + auto hit = hitVTXCol->at(ielem); + auto simhit = SimhitVTXCol->at(ielem); + + auto simcellid = simhit.getCellID(); + // system:5,side:-2,layer:9,module:8,sensor:32:8 + uint64_t m_layer = vxd_decoder->get(simcellid, "layer"); + uint64_t m_module = vxd_decoder->get(simcellid, "module"); + uint64_t m_sensor = vxd_decoder->get(simcellid, "sensor"); + double acts_x = simhit.getPosition()[0]; + double acts_y = simhit.getPosition()[1]; + double acts_z = simhit.getPosition()[2]; + + double momentum_x = simhit.getMomentum()[0]; + double momentum_y = simhit.getMomentum()[1]; + double momentum_z = simhit.getMomentum()[2]; + + dd4hep::rec::ISurface* surface = nullptr; + auto it = m_vtx_surfaces->find(simcellid); + if (it != m_vtx_surfaces->end()) { + surface = it->second; + if (!surface) { + fatal() << "found surface for VTX cell id " << simcellid << ", but NULL" << endmsg; + return 0; + } + } + else { + fatal() << "not found surface for VTX cell id " << simcellid << endmsg; + return 0; + } + + // dd4hep::rec::Vector3D oldPos(simhit.getPosition()[0]*dd4hep::mm/CLHEP::mm, simhit.getPosition()[1]*dd4hep::mm/CLHEP::mm, simhit.getPosition()[2]*dd4hep::mm/CLHEP::mm); + // dd4hep::rec::Vector2D localPoint = surface->globalToLocal(oldPos); + + // if (m_layer < current_layer){ + // info() << "ring hits happend in layer " << m_layer << " before layer " << current_layer << ", at event " << _nEvt << endmsg; + // success = 0; + // break; + // } + // current_layer = m_layer; + + if (m_layer <= 3){ + // set acts geometry identifier + // VXD_inner_volume_ids{16, 17, 18, 19}; + uint64_t acts_volume = VXD_inner_volume_ids[m_layer]; + uint64_t acts_boundary = 0; + uint64_t acts_layer = 2; + uint64_t acts_approach = 0; + uint64_t acts_sensitive = m_module + 1; + + Acts::GeometryIdentifier moduleGeoId; + moduleGeoId.setVolume(acts_volume); + moduleGeoId.setBoundary(acts_boundary); + moduleGeoId.setLayer(acts_layer); + moduleGeoId.setApproach(acts_approach); + moduleGeoId.setSensitive(acts_sensitive); + + // create and store the source link + uint32_t measurementIdx = measurements.size(); + IndexSourceLink sourceLink{moduleGeoId, measurementIdx, hit}; + sourceLinks.insert(sourceLinks.end(), sourceLink); + Acts::SourceLink sl{sourceLink}; + boost::container::static_vector<Acts::SourceLink, 2> slinks; + slinks.emplace_back(sl); + + // get the surface of the hit + IndexSourceLink::SurfaceAccessor surfaceAccessor{*trackingGeometry}; + const Acts::Surface* acts_surface = surfaceAccessor(sl); + + // get the local position of the hit + const Acts::Vector3 globalPos{acts_x, acts_y, acts_z}; + const Acts::Vector3 globalmom{momentum_x, momentum_y, momentum_z}; + auto acts_local_postion = acts_surface->globalToLocal(geoContext, globalPos, globalmom, onSurfaceTolerance); + if (!acts_local_postion.ok()){ + info() << "Error: failed to get local position for VTX hit " << simcellid << endmsg; + acts_local_postion = acts_surface->globalToLocal(geoContext, globalPos, globalmom, 100*onSurfaceTolerance); + } + const std::array<Acts::BoundIndices, 2> indices{Acts::BoundIndices::eBoundLoc0, Acts::BoundIndices::eBoundLoc1}; + const Acts::Vector2 par{acts_local_postion.value()[0], acts_local_postion.value()[1]}; + + // *** debug *** + debug() << "VXD measurements global position(x,y,z): " << simhit.getPosition()[0] << ", " << simhit.getPosition()[1] << ", " << simhit.getPosition()[2] + << "; local position(loc0, loc1): "<< acts_local_postion.value()[0] << ", " << acts_local_postion.value()[1] << endmsg; + auto acts_global_postion = acts_surface->localToGlobal(geoContext, par, globalFakeMom); + debug() << "debug surface at: x:" << acts_global_postion[0] << ", y:" << acts_global_postion[1] << ", z:" << acts_global_postion[2] << endmsg; + + // SimSpacePoint *hitExt = new SimSpacePoint(hit, simhit, slinks); + SimSpacePoint *hitExt = new SimSpacePoint(hit, acts_global_postion[0], acts_global_postion[1], acts_global_postion[2], 0.002, slinks); + // debug() << "debug hitExt at: x:" << hitExt->x() << ", y:" << hitExt->y() << ", z:" << hitExt->z() << endmsg; + SpacePointPtrs.push_back(hitExt); + + // create and store the measurement + Acts::ActsSquareMatrix<2> cov = Acts::ActsSquareMatrix<2>::Identity(); + cov(0, 0) = std::max<double>(std::abs(acts_global_postion[0] - simhit.getPosition()[0]), eps); + cov(1, 1) = std::max<double>(std::abs(acts_global_postion[1] - simhit.getPosition()[1]), eps); + measurements.emplace_back(Acts::Measurement<Acts::BoundIndices, 2>(sl, indices, par, cov)); + + // std::vector<std::string> truth_col = {std::to_string(m_layer*2 + m_module), std::to_string(simhit.getPosition()[0]), std::to_string(simhit.getPosition()[1]), std::to_string(simhit.getPosition()[2])}; + // truth_writer.write_row(truth_col); + // std::vector<std::string> converted_col = {std::to_string(m_layer*2 + m_module), std::to_string(acts_global_postion[0]), std::to_string(acts_global_postion[1]), std::to_string(acts_global_postion[2])}; + // converted_writer.write_row(converted_col); + + } else { + // set acts geometry identifier + // VXD_outer_volume_id = 20; + uint64_t acts_volume = VXD_outer_volume_id; + uint64_t acts_boundary = 0; + uint64_t acts_layer = 2; + uint64_t acts_approach = 0; + uint64_t acts_sensitive = (m_layer == 5) ? m_module*2 + 1 : m_module*2 + 2; + + Acts::GeometryIdentifier moduleGeoId; + moduleGeoId.setVolume(acts_volume); + moduleGeoId.setBoundary(acts_boundary); + moduleGeoId.setLayer(acts_layer); + moduleGeoId.setApproach(acts_approach); + moduleGeoId.setSensitive(acts_sensitive); + + // create and store the source link + uint32_t measurementIdx = measurements.size(); + IndexSourceLink sourceLink{moduleGeoId, measurementIdx, hit}; + sourceLinks.insert(sourceLinks.end(), sourceLink); + Acts::SourceLink sl{sourceLink}; + + // get the local position of the hit + IndexSourceLink::SurfaceAccessor surfaceAccessor{*trackingGeometry}; + const Acts::Surface* acts_surface = surfaceAccessor(sl); + const Acts::Vector3 globalPos{acts_x, acts_y, acts_z}; + const Acts::Vector3 globalmom{momentum_x, momentum_y, momentum_z}; + auto acts_local_postion = acts_surface->globalToLocal(geoContext, globalPos, globalmom, onSurfaceTolerance); + if (!acts_local_postion.ok()){ + info() << "Error: failed to get local position for VTX hit " << simcellid << endmsg; + acts_local_postion = acts_surface->globalToLocal(geoContext, globalPos, globalmom, 100*onSurfaceTolerance); + } + const std::array<Acts::BoundIndices, 2> indices{Acts::BoundIndices::eBoundLoc0, Acts::BoundIndices::eBoundLoc1}; + const Acts::Vector2 par{acts_local_postion.value()[0], acts_local_postion.value()[1]}; + + // *** debug *** + debug() << "VXD measurements global position(x,y,z): " << simhit.getPosition()[0] << ", " << simhit.getPosition()[1] << ", " << simhit.getPosition()[2] + << "; local position(loc0, loc1): "<< acts_local_postion.value()[0] << ", " << acts_local_postion.value()[1] << endmsg; + auto acts_global_postion = acts_surface->localToGlobal(geoContext, par, globalFakeMom); + debug() << "debug surface at: x:" << acts_global_postion[0] << ", y:" << acts_global_postion[1] << ", z:" << acts_global_postion[2] << endmsg; + + // create and store the measurement + Acts::ActsSquareMatrix<2> cov = Acts::ActsSquareMatrix<2>::Identity(); + cov(0, 0) = std::max<double>(std::abs(acts_global_postion[0] - simhit.getPosition()[0]), eps); + cov(1, 1) = std::max<double>(std::abs(acts_global_postion[1] - simhit.getPosition()[1]), eps); + measurements.emplace_back(Acts::Measurement<Acts::BoundIndices, 2>(sl, indices, par, cov)); + + // std::vector<std::string> truth_col = {std::to_string(m_layer+4), std::to_string(simhit.getPosition()[0]), std::to_string(simhit.getPosition()[1]), std::to_string(simhit.getPosition()[2])}; + // truth_writer.write_row(truth_col); + // std::vector<std::string> converted_col = {std::to_string(m_layer+4), std::to_string(acts_global_postion[0]), std::to_string(acts_global_postion[1]), std::to_string(acts_global_postion[2])}; + // converted_writer.write_row(converted_col); + } + } + } else { success = 0; } + + return success; +} + +int RecActsTracking::InitialiseSIT() +{ + int success = 1; + + const edm4hep::TrackerHitCollection* hitSITCol = nullptr; + const edm4hep::SimTrackerHitCollection* SimhitSITCol = nullptr; + + double min_z = 0; + try { + hitSITCol = _inSITTrackHdl.get(); + } catch (GaudiException& e) { + debug() << "Collection " << _inSITTrackHdl.fullKey() << " is unavailable in event " << _nEvt << endmsg; + success = 0; + } + + try { + SimhitSITCol = _inSITColHdl.get(); + } catch (GaudiException& e) { + debug() << "Sim Collection " << _inSITColHdl.fullKey() << " is unavailable in event " << _nEvt << endmsg; + success = 0; + } + + if(hitSITCol && SimhitSITCol) + { + int nelem = hitSITCol->size(); + debug() << "Number of SIT hits = " << nelem << endmsg; + // SpacePointPtrs.resize(nelem); + + // std::string truth_file = "obj/sit/truth/event" + std::to_string(_nEvt) + ".csv"; + // std::ofstream truth_stream(truth_file); + // csv2::Writer<csv2::delimiter<','>> truth_writer(truth_stream); + // std::vector<std::string> truth_header = {"layer", "x", "y", "z"}; + // truth_writer.write_row(truth_header); + + // std::string converted_file = "obj/sit/converted/event" + std::to_string(_nEvt) + ".csv"; + // std::ofstream converted_stream(converted_file); + // csv2::Writer<csv2::delimiter<','>> converted_writer(converted_stream); + // std::vector<std::string> converted_header = {"layer", "x", "y", "z"}; + // converted_writer.write_row(converted_header); + + for (int ielem = 0; ielem < nelem; ++ielem) + { + auto hit = hitSITCol->at(ielem); + auto simhit = SimhitSITCol->at(ielem); + + auto simcellid = simhit.getCellID(); + // <id>system:5,side:-2,layer:9,stave:8,module:8,sensor:5,y:-11,z:-11</id> + uint64_t m_layer = sit_decoder->get(simcellid, "layer"); + uint64_t m_stave = sit_decoder->get(simcellid, "stave"); + uint64_t m_module = sit_decoder->get(simcellid, "module"); + uint64_t m_sensor = sit_decoder->get(simcellid, "sensor"); + double acts_x = simhit.getPosition()[0]; + double acts_y = simhit.getPosition()[1]; + double acts_z = simhit.getPosition()[2]; + double momentum_x = simhit.getMomentum()[0]; + double momentum_y = simhit.getMomentum()[1]; + double momentum_z = simhit.getMomentum()[2]; + + dd4hep::rec::ISurface* surface = nullptr; + auto it = m_sit_surfaces->find(simcellid); + if (it != m_sit_surfaces->end()) { + surface = it->second; + if (!surface) { + fatal() << "found surface for SIT cell id " << simcellid << ", but NULL" << endmsg; + return 0; + } + } + else { + fatal() << "not found surface for SIT cell id " << simcellid << endmsg; + return 0; + } + + if (ielem == 0) { + min_z = hitSITCol->at(ielem).getPosition()[2]; + } else { + if (std::abs(acts_z - min_z) > 100) { + continue; + } + } + + // set acts geometry identifier + uint64_t acts_volume = SIT_acts_volume_ids[m_layer]; + uint64_t acts_boundary = 0; + uint64_t acts_layer = 2; + uint64_t acts_approach = 0; + uint64_t acts_sensitive = m_stave*SIT_module_nums[m_layer]*SIT_sensor_nums + m_module*SIT_sensor_nums + m_sensor + 1; + + Acts::GeometryIdentifier moduleGeoId; + moduleGeoId.setVolume(acts_volume); + moduleGeoId.setBoundary(acts_boundary); + moduleGeoId.setLayer(acts_layer); + moduleGeoId.setApproach(acts_approach); + moduleGeoId.setSensitive(acts_sensitive); + + // create and store the source link + uint32_t measurementIdx = measurements.size(); + IndexSourceLink sourceLink{moduleGeoId, measurementIdx, hit}; + sourceLinks.insert(sourceLinks.end(), sourceLink); + Acts::SourceLink sl{sourceLink}; + + // get the local position of the hit + IndexSourceLink::SurfaceAccessor surfaceAccessor{*trackingGeometry}; + const Acts::Surface* acts_surface = surfaceAccessor(sl); + const Acts::Vector3 globalPos{acts_x, acts_y, acts_z}; + const Acts::Vector3 globalmom{momentum_x, momentum_y, momentum_z}; + auto acts_local_postion = acts_surface->globalToLocal(geoContext, globalPos, globalmom, onSurfaceTolerance); + if (!acts_local_postion.ok()){ + info() << "Error: failed to get local position for SIT hit " << simcellid << endmsg; + acts_local_postion = acts_surface->globalToLocal(geoContext, globalPos, globalmom, 100*onSurfaceTolerance); + } + const std::array<Acts::BoundIndices, 2> indices{Acts::BoundIndices::eBoundLoc0, Acts::BoundIndices::eBoundLoc1}; + const Acts::Vector2 par{acts_local_postion.value()[0], acts_local_postion.value()[1]}; + + // *** debug *** + debug() << "SIT measurements global position(x,y,z): " << simhit.getPosition()[0] << ", " << simhit.getPosition()[1] << ", " << simhit.getPosition()[2] + << "; local position(loc0, loc1): "<< acts_local_postion.value()[0] << ", " << acts_local_postion.value()[1] << endmsg; + auto acts_global_postion = acts_surface->localToGlobal(geoContext, par, globalFakeMom); + debug() << "debug surface at: x:" << acts_global_postion[0] << ", y:" << acts_global_postion[1] << ", z:" << acts_global_postion[2] << endmsg; + + // create and store the measurement + Acts::ActsSquareMatrix<2> cov = Acts::ActsSquareMatrix<2>::Identity(); + cov(0, 0) = std::max<double>(std::abs(acts_global_postion[0] - simhit.getPosition()[0]), eps); + cov(1, 1) = std::max<double>(std::abs(acts_global_postion[1] - simhit.getPosition()[1]), eps); + measurements.emplace_back(Acts::Measurement<Acts::BoundIndices, 2>(sl, indices, par, cov)); + + // std::vector<std::string> truth_col = {std::to_string(m_layer), std::to_string(simhit.getPosition()[0]), std::to_string(simhit.getPosition()[1]), std::to_string(simhit.getPosition()[2])}; + // truth_writer.write_row(truth_col); + // std::vector<std::string> converted_col = {std::to_string(m_layer), std::to_string(acts_global_postion[0]), std::to_string(acts_global_postion[1]), std::to_string(acts_global_postion[2])}; + // converted_writer.write_row(converted_col); + } + } else { success = 0; } + + return success; +} \ No newline at end of file diff --git a/Reconstruction/RecActsTracking/src/RecActsTracking.h b/Reconstruction/RecActsTracking/src/RecActsTracking.h new file mode 100644 index 0000000000000000000000000000000000000000..be7c889ac054830b290f1c255740f05b93074e3c --- /dev/null +++ b/Reconstruction/RecActsTracking/src/RecActsTracking.h @@ -0,0 +1,288 @@ +#ifndef RecActsTracking_H +#define RecActsTracking_H + +#include <iostream> +#include <fstream> +#include <cstdlib> +#include <sstream> +#include <filesystem> + +#include "k4FWCore/DataHandle.h" +#include "GaudiAlg/GaudiAlgorithm.h" +#include "DD4hep/Detector.h" +#include "DDRec/DetectorData.h" +#include "DDRec/ISurface.h" +#include "DDRec/SurfaceManager.h" +#include "DDRec/Vector3D.h" + +#include "UTIL/ILDConf.h" +#include "GaudiKernel/NTuple.h" +#include "DetInterface/IGeomSvc.h" + +// gear +#include "GearSvc/IGearSvc.h" +#include <gear/GEAR.h> +#include <gear/GearMgr.h> +#include <gear/GearParameters.h> +#include <gear/VXDLayerLayout.h> +#include <gear/VXDParameters.h> +#include "gear/FTDLayerLayout.h" +#include "gear/FTDParameters.h" +#include <gear/BField.h> + +// edm4hep +#include "edm4hep/MCParticle.h" +#include "edm4hep/Track.h" +#include "edm4hep/MutableTrack.h" +// #include "edm4hep/TrackerHit.h" +// #include "edm4hep/SimTrackerHit.h" +#include "edm4hep/TrackState.h" +#include "edm4hep/EventHeaderCollection.h" +#include "edm4hep/MCParticleCollection.h" +#include "edm4hep/SimTrackerHitCollection.h" +#include "edm4hep/TrackerHitCollection.h" +#include "edm4hep/TrackCollection.h" +#include "edm4hep/MCRecoTrackerAssociationCollection.h" + +// acts +#include "Acts/Definitions/Algebra.hpp" +#include "Acts/Definitions/Direction.hpp" +#include "Acts/Definitions/TrackParametrization.hpp" + +#include "Acts/EventData/MultiTrajectory.hpp" +#include "Acts/EventData/ProxyAccessor.hpp" +#include "Acts/EventData/SpacePointData.hpp" +#include <Acts/EventData/Measurement.hpp> +#include "Acts/EventData/TrackParameters.hpp" +#include "Acts/EventData/TrackContainer.hpp" +#include "Acts/EventData/VectorMultiTrajectory.hpp" +#include "Acts/EventData/VectorTrackContainer.hpp" +#include "Acts/EventData/ParticleHypothesis.hpp" + +#include "Acts/Propagator/AbortList.hpp" +#include "Acts/Propagator/EigenStepper.hpp" +#include "Acts/Propagator/MaterialInteractor.hpp" +#include "Acts/Propagator/Navigator.hpp" +#include "Acts/Propagator/Propagator.hpp" +#include "Acts/Propagator/StandardAborters.hpp" + +#include "Acts/Geometry/Extent.hpp" +#include "Acts/Geometry/GeometryIdentifier.hpp" +#include <Acts/Geometry/GeometryContext.hpp> + +#include "Acts/Seeding/BinnedGroup.hpp" +#include "Acts/Seeding/EstimateTrackParamsFromSeed.hpp" +#include "Acts/Seeding/InternalSpacePoint.hpp" +#include "Acts/Seeding/SeedFilter.hpp" +#include "Acts/Seeding/SeedFilterConfig.hpp" +#include "Acts/Seeding/SeedFinder.hpp" +#include "Acts/Seeding/SeedFinderConfig.hpp" +#include "Acts/Seeding/SpacePointGrid.hpp" + +#include "Acts/TrackFinding/CombinatorialKalmanFilter.hpp" +#include "Acts/TrackFitting/GainMatrixSmoother.hpp" +#include "Acts/TrackFitting/GainMatrixUpdater.hpp" +#include "Acts/TrackFitting/KalmanFitter.hpp" + +#include "Acts/Surfaces/PerigeeSurface.hpp" +#include "Acts/Surfaces/Surface.hpp" + +#include "Acts/Utilities/BinningType.hpp" +#include "Acts/Utilities/Delegate.hpp" +#include "Acts/Utilities/Grid.hpp" +#include "Acts/Utilities/GridBinFinder.hpp" +#include "Acts/Utilities/Helpers.hpp" +#include "Acts/Utilities/Logger.hpp" +#include "Acts/Utilities/Enumerate.hpp" +#include "Acts/Utilities/TrackHelpers.hpp" + +// local include +#include "utils/TGeoDetector.hpp" +#include "utils/CKFhelper.hpp" +#include "utils/MagneticField.hpp" + +namespace gear{ + class GearMgr; +} + +namespace dd4hep { + namespace DDSegmentation { + class BitFieldCoder; + } + namespace rec{ + class ISurface; + } +} + +// Seeding: Construct track seeds from space points. +// config for seed finding +struct SeedingConfig +{ + /// Input space point collections. + /// + /// We allow multiple space point collections to allow different parts of + /// the detector to use different algorithms for space point construction, + /// e.g. single-hit space points for pixel-like detectors or double-hit + /// space points for strip-like detectors. + std::vector<std::string> inputSpacePoints; + + /// Output track seed collection. + std::string outputSeeds; + + + Acts::SeedFilterConfig seedFilterConfig; + Acts::SeedFinderConfig<SimSpacePoint> seedFinderConfig; + Acts::CylindricalSpacePointGridConfig gridConfig; + Acts::CylindricalSpacePointGridOptions gridOptions; + Acts::SeedFinderOptions seedFinderOptions; + + // allow for different values of rMax in gridConfig and seedFinderConfig + bool allowSeparateRMax = false; + + // vector containing the map of z bins in the top and bottom layers + std::vector<std::pair<int, int>> zBinNeighborsTop; + std::vector<std::pair<int, int>> zBinNeighborsBottom; + + // number of phiBin neighbors at each side of the current bin that will be + // used to search for SPs + int numPhiNeighbors = 1; +}; + +class RecActsTracking : public GaudiAlgorithm +{ + + public : + + RecActsTracking(const std::string& name, ISvcLocator* svcLoc); + + virtual StatusCode initialize(); + + virtual StatusCode execute(); + + virtual StatusCode finalize(); + + private : + + // Input collections + DataHandle<edm4hep::TrackerHitCollection> _inVTXTrackHdl{"VXDTrackerHits", Gaudi::DataHandle::Reader, this}; + DataHandle<edm4hep::TrackerHitCollection> _inSITTrackHdl{"SITTrackerHits", Gaudi::DataHandle::Reader, this}; + + DataHandle<edm4hep::SimTrackerHitCollection> _inVTXColHdl{"VXDCollection", Gaudi::DataHandle::Reader, this}; + DataHandle<edm4hep::SimTrackerHitCollection> _inSITColHdl{"SITCollection", Gaudi::DataHandle::Reader, this}; + + DataHandle<edm4hep::MCParticleCollection> _inMCColHdl{"MCParticle", Gaudi::DataHandle::Reader, this}; + + // Output collections + DataHandle<edm4hep::TrackCollection> _outColHdl{"ACTSSiTracks", Gaudi::DataHandle::Writer, this}; + + // properties + Gaudi::Property<std::string> TGeo_path{this, "TGeoFile"}; + Gaudi::Property<std::string> TGeo_config_path{this, "TGeoConfigFile"}; + Gaudi::Property<std::string> MaterialMap_path{this, "MaterialMapFile"}; + Gaudi::Property<double> m_field{this, "Field", 3.0}; // tesla + Gaudi::Property<double> onSurfaceTolerance{this, "onSurfaceTolerance", 1e-2}; // mm + + SmartIF<IGeomSvc> m_geosvc; + SmartIF<IChronoStatSvc> chronoStatSvc; + dd4hep::DDSegmentation::BitFieldCoder *vxd_decoder; + dd4hep::DDSegmentation::BitFieldCoder *sit_decoder; + const dd4hep::rec::SurfaceMap* m_vtx_surfaces; + const dd4hep::rec::SurfaceMap* m_sit_surfaces; + + // configs to build acts geometry + Acts::GeometryContext geoContext; + Acts::MagneticFieldContext magFieldContext; + Acts::CalibrationContext calibContext; + std::string TGeo_ROOTFilePath; + std::string TGeoConfig_jFilePath; + std::string MaterialMap_jFilePath; + std::shared_ptr<const Acts::TrackingGeometry> trackingGeometry; + std::vector<std::shared_ptr<const Acts::TGeoDetectorElement>> detElementStore; + + // Store the space points & measurements & sourcelinks **per event** + // * only store the VXD tracker hits to the SpacePointPtrs + std::vector<const SimSpacePoint*> SpacePointPtrs; + IndexSourceLinkContainer sourceLinks; + std::vector<::Acts::BoundVariantMeasurement> measurements; + std::vector<::Acts::BoundTrackParameters> initialParameters; + SimSeedContainer Selected_Seeds; + + // seed finder + SeedingConfig seed_cfg; + std::unique_ptr<const Acts::GridBinFinder<2ul>> m_bottomBinFinder; + std::unique_ptr<const Acts::GridBinFinder<2ul>> m_topBinFinder; + Acts::SeedFinder<SimSpacePoint, Acts::CylindricalSpacePointGrid<SimSpacePoint>> m_seedFinder; + + int InitialiseVTX(); + int InitialiseSIT(); + const dd4hep::rec::ISurface* getISurface(edm4hep::TrackerHit* hit); + const Acts::GeometryIdentifier getVTXGid(uint64_t cellid); + const Acts::GeometryIdentifier getSITGid(uint64_t cellid); + + // utils + int _nEvt; + double eps = 1.0e-05; + const double _FCT = 2.99792458E-4; + Acts::Vector3 acts_field_value = Acts::Vector3(0., 0., 3*_FCT); // tesla + // Acts::Vector3 acts_field_value = Acts::Vector3(0., 0., 3); // tesla + std::shared_ptr<const Acts::MagneticFieldProvider> magneticField + = std::make_shared<Acts::ConstantBField>(acts_field_value); + double bFieldMin = 0.3 * _FCT * Acts::UnitConstants::T; // tesla + // double bFieldMin = 0.1 * Acts::UnitConstants::T; // tesla + const Acts::Vector3 globalFakeMom{1.e6, 1.e6, 1.e6}; + const std::array<Acts::BoundIndices, 6> writeout_indices{ + Acts::BoundIndices::eBoundLoc0, Acts::BoundIndices::eBoundPhi, + Acts::BoundIndices::eBoundQOverP, Acts::BoundIndices::eBoundLoc1, + Acts::BoundIndices::eBoundTheta, Acts::BoundIndices::eBoundTime}; + + // param estimate configuration + double noTimeVarInflation = 100.; + std::array<double, 6> initialSigmas = { + 25 * Acts::UnitConstants::um, + 100 * Acts::UnitConstants::um, + 0.02 * Acts::UnitConstants::degree, + 0.02 * Acts::UnitConstants::degree, + 0.1 * Acts::UnitConstants::e / Acts::UnitConstants::GeV, + 1400 * Acts::UnitConstants::s}; + std::array<double, 6> initialSimgaQoverPCoefficients = { + 0 * Acts::UnitConstants::mm / (Acts::UnitConstants::e * Acts::UnitConstants::GeV), + 0 * Acts::UnitConstants::mm / (Acts::UnitConstants::e * Acts::UnitConstants::GeV), + 0 * Acts::UnitConstants::degree / (Acts::UnitConstants::e * Acts::UnitConstants::GeV), + 0 * Acts::UnitConstants::degree / (Acts::UnitConstants::e * Acts::UnitConstants::GeV), + 0.1, + 0 * Acts::UnitConstants::ns / (Acts::UnitConstants::e * Acts::UnitConstants::GeV)}; + std::array<double, 6> initialVarInflation = {10., 10., 10., 10., 10., 10.}; + Acts::ParticleHypothesis particleHypothesis = Acts::ParticleHypothesis::muon(); + // Acts::ParticleHypothesis particleHypothesis = Acts::ParticleHypothesis::chargedGeantino(); + + // gid convert configuration + std::vector<uint64_t> VXD_inner_volume_ids{16, 17, 18, 19}; + uint64_t VXD_outer_volume_id = 20; + std::vector<uint64_t> SIT_acts_volume_ids{22, 24, 26}; + std::vector<uint64_t> SIT_module_nums{7, 10, 14}; + uint64_t SIT_sensor_nums = 14; + + // CKF configuration + // Acts::MeasurementSelector::Config measurementSelectorCfg; + std::shared_ptr<TrackFinderFunction> findTracks; + std::optional<Acts::TrackSelector> m_trackSelector; + std::optional<std::variant<Acts::TrackSelector::Config, Acts::TrackSelector::EtaBinnedConfig>> trackSelectorCfg = std::nullopt; + mutable std::atomic<std::size_t> m_nTotalSeeds{0}; + mutable std::atomic<std::size_t> m_nDeduplicatedSeeds{0}; + mutable std::atomic<std::size_t> m_nFailedSeeds{0}; + mutable std::atomic<std::size_t> m_nFailedSmoothing{0}; + mutable std::atomic<std::size_t> m_nFailedExtrapolation{0}; + mutable std::atomic<std::size_t> m_nFoundTracks{0}; + mutable std::atomic<std::size_t> m_nSelectedTracks{0}; + mutable std::atomic<std::size_t> m_nStoppedBranches{0}; + + mutable tbb::combinable<Acts::VectorMultiTrajectory::Statistics> m_memoryStatistics{[]() { + auto mtj = std::make_shared<Acts::VectorMultiTrajectory>(); + return mtj->statistics(); + }}; + bool twoWay = false; /// Run finding in two directions + Acts::TrackExtrapolationStrategy extrapolationStrategy = Acts::TrackExtrapolationStrategy::firstOrLast; /// Extrapolation strategy + unsigned int maxSteps = 100000; +}; + +#endif // RecActsTracking_H diff --git a/Reconstruction/RecActsTracking/src/utils/CKFhelper.hpp b/Reconstruction/RecActsTracking/src/utils/CKFhelper.hpp new file mode 100644 index 0000000000000000000000000000000000000000..71dae4381bdae4dec68133ad069add0853267dcb --- /dev/null +++ b/Reconstruction/RecActsTracking/src/utils/CKFhelper.hpp @@ -0,0 +1,388 @@ +#include "Acts/Definitions/Algebra.hpp" +#include "Acts/Definitions/Direction.hpp" +#include "Acts/Definitions/TrackParametrization.hpp" + +#include "Acts/EventData/ProxyAccessor.hpp" +#include "Acts/EventData/TrackParameters.hpp" +#include "Acts/EventData/MultiTrajectory.hpp" +#include "Acts/EventData/SourceLink.hpp" +#include "Acts/EventData/TrackContainer.hpp" +#include "Acts/EventData/TrackProxy.hpp" +#include <Acts/EventData/Measurement.hpp> +#include "Acts/EventData/VectorMultiTrajectory.hpp" +#include "Acts/EventData/VectorTrackContainer.hpp" + +#include "ActsFatras/Digitization/Segmentizer.hpp" + +#include "Acts/Geometry/GeometryIdentifier.hpp" +#include "Acts/Geometry/TrackingGeometry.hpp" +#include "Acts/Geometry/GeometryContext.hpp" + +#include "Acts/Surfaces/PerigeeSurface.hpp" +#include "Acts/Surfaces/Surface.hpp" + +#include "Acts/Propagator/AbortList.hpp" +#include "Acts/Propagator/EigenStepper.hpp" +#include "Acts/Propagator/MaterialInteractor.hpp" +#include "Acts/Propagator/Navigator.hpp" +#include "Acts/Propagator/Propagator.hpp" +#include "Acts/Propagator/StandardAborters.hpp" + + +#include "Acts/TrackFinding/CombinatorialKalmanFilter.hpp" +#include "Acts/TrackFinding/MeasurementSelector.hpp" +#include "Acts/TrackFinding/TrackSelector.hpp" +#include "Acts/TrackFitting/GainMatrixSmoother.hpp" +#include "Acts/TrackFitting/GainMatrixUpdater.hpp" +#include "Acts/TrackFitting/KalmanFitter.hpp" + +#include "Acts/Utilities/Logger.hpp" +#include "Acts/Utilities/Result.hpp" +#include "Acts/Utilities/TrackHelpers.hpp" +#include "Acts/Utilities/Delegate.hpp" +#include "Acts/Utilities/Enumerate.hpp" +#include "Acts/Utilities/TrackHelpers.hpp" +#include "Acts/Utilities/CalibrationContext.hpp" + +#include <atomic> +#include <cstddef> +#include <functional> +#include <limits> +#include <memory> +#include <optional> +#include <string> +#include <variant> +#include <vector> +#include <cmath> +#include <ostream> +#include <stdexcept> +#include <system_error> +#include <unordered_map> +#include <utility> + +#include <tbb/combinable.h> +#include <boost/functional/hash.hpp> + +#include "SimSpacePoint.hpp" + +namespace Acts +{ + class MagneticFieldProvider; + class TrackingGeometry; +} + +using Updater = Acts::GainMatrixUpdater; +using Smoother = Acts::GainMatrixSmoother; +using Stepper = Acts::EigenStepper<>; +using Navigator = Acts::Navigator; +using Propagator = Acts::Propagator<Stepper, Navigator>; +using CKF = Acts::CombinatorialKalmanFilter<Propagator, Acts::VectorMultiTrajectory>; + +// track container types +using TrackContainer = Acts::TrackContainer<Acts::VectorTrackContainer, Acts::VectorMultiTrajectory, std::shared_ptr>; +using ConstTrackContainer = Acts::TrackContainer<Acts::ConstVectorTrackContainer, Acts::ConstVectorMultiTrajectory, std::shared_ptr>; +using TrackParameters = ::Acts::BoundTrackParameters; +using TrackParametersContainer = std::vector<TrackParameters>; +using TrackIndexType = TrackContainer::IndexType; +using TrackProxy = TrackContainer::TrackProxy; +using ConstTrackProxy = ConstTrackContainer::ConstTrackProxy; + +// track finder types +using TrackFinderOptions = Acts::CombinatorialKalmanFilterOptions<IndexSourceLinkAccessor::Iterator, Acts::VectorMultiTrajectory>; +using TrackFinderResult = Acts::Result<std::vector<TrackContainer::TrackProxy>>; + +// measurement types +using Measurement = ::Acts::BoundVariantMeasurement; +using MeasurementContainer = std::vector<Measurement>; + +class TrackFinderFunction +{ +public: + virtual ~TrackFinderFunction() = default; + virtual TrackFinderResult operator()(const TrackParameters&, const TrackFinderOptions&, TrackContainer&) const = 0; +}; + +static std::shared_ptr<TrackFinderFunction> makeTrackFinderFunction( + std::shared_ptr<const Acts::TrackingGeometry> trackingGeometry, + std::shared_ptr<const Acts::MagneticFieldProvider> magneticField, + const Acts::Logger& logger); + +struct TrackFinderFunctionImpl : public TrackFinderFunction { + CKF trackFinder; + TrackFinderFunctionImpl(CKF&& f) : trackFinder(std::move(f)) {} + TrackFinderResult operator()( const TrackParameters& initialParameters, + const TrackFinderOptions& options, + TrackContainer& tracks) const override + { + return trackFinder.findTracks(initialParameters, options, tracks); + }; +}; + +std::shared_ptr<TrackFinderFunction> makeTrackFinderFunction( + std::shared_ptr<const Acts::TrackingGeometry> trackingGeometry, + std::shared_ptr<const Acts::MagneticFieldProvider> magneticField) +{ + Stepper stepper(magneticField); + Navigator::Config cfg{trackingGeometry}; + cfg.resolvePassive = false; + cfg.resolveMaterial = true; + cfg.resolveSensitive = true; + Navigator navigator(cfg); + Propagator propagator(std::move(stepper), std::move(navigator)); + CKF trackFinder(std::move(propagator)); + + return std::make_shared<TrackFinderFunctionImpl>(std::move(trackFinder)); +} + +struct Cluster +{ + using Cell = ActsFatras::Segmentizer::ChannelSegment; + std::size_t sizeLoc0 = 0; + std::size_t sizeLoc1 = 0; + std::vector<Cell> channels; +}; + +using ClusterContainer = std::vector<Cluster>; + +/// Abstract base class for measurement-based calibration +class MeasurementCalibrator +{ + public: + virtual void calibrate( + const MeasurementContainer& measurements, const ClusterContainer* clusters, + const Acts::GeometryContext& gctx, const Acts::CalibrationContext& cctx, + const Acts::SourceLink& sourceLink, + Acts::VectorMultiTrajectory::TrackStateProxy& trackState) const = 0; + virtual ~MeasurementCalibrator() = default; + virtual bool needsClusters() const { return false; } +}; + +// Calibrator to convert an index source link to a measurement as-is +class PassThroughCalibrator : public MeasurementCalibrator +{ + public: + /// Find the measurement corresponding to the source link. + /// + /// @tparam parameters_t Track parameters type + /// @param gctx The geometry context (unused) + /// @param trackState The track state to calibrate + void calibrate( + const MeasurementContainer& measurements, + const ClusterContainer* clusters, const Acts::GeometryContext& gctx, + const Acts::CalibrationContext& cctx, const Acts::SourceLink& sourceLink, + Acts::VectorMultiTrajectory::TrackStateProxy& trackState) const override; +}; + +// Adapter class that wraps a MeasurementCalibrator to conform to the +// core ACTS calibration interface +class MeasurementCalibratorAdapter +{ + public: + MeasurementCalibratorAdapter(const MeasurementCalibrator& calibrator, + const MeasurementContainer& measurements, + const ClusterContainer* clusters = nullptr); + MeasurementCalibratorAdapter() = delete; + + void calibrate(const Acts::GeometryContext& gctx, + const Acts::CalibrationContext& cctx, + const Acts::SourceLink& sourceLink, + Acts::VectorMultiTrajectory::TrackStateProxy trackState) const; + + private: + const MeasurementCalibrator& m_calibrator; + const MeasurementContainer& m_measurements; + const ClusterContainer* m_clusters; +}; + +void PassThroughCalibrator::calibrate( + const MeasurementContainer& measurements, const ClusterContainer* /*clusters*/, + const Acts::GeometryContext& /*gctx*/, const Acts::CalibrationContext& /*cctx*/, + const Acts::SourceLink& sourceLink, Acts::VectorMultiTrajectory::TrackStateProxy& trackState) const +{ + trackState.setUncalibratedSourceLink(sourceLink); + const IndexSourceLink& idxSourceLink = sourceLink.get<IndexSourceLink>(); + + assert((idxSourceLink.index() < measurements.size()) && + "Source link index is outside the container bounds"); + + std::visit( + [&trackState](const auto& meas) { trackState.setCalibrated(meas); }, + measurements[idxSourceLink.index()]); +} + +MeasurementCalibratorAdapter::MeasurementCalibratorAdapter( + const MeasurementCalibrator& calibrator, const MeasurementContainer& measurements, + const ClusterContainer* clusters) : m_calibrator{calibrator}, m_measurements{measurements}, m_clusters{clusters} {} + +void MeasurementCalibratorAdapter::calibrate( + const Acts::GeometryContext& gctx, const Acts::CalibrationContext& cctx, const Acts::SourceLink& sourceLink, + Acts::VectorMultiTrajectory::TrackStateProxy trackState) const +{ + return m_calibrator.calibrate(m_measurements, m_clusters, gctx, cctx, sourceLink, trackState); +} + +// Specialize std::hash for SeedIdentifier +// This is required to use SeedIdentifier as a key in an `std::unordered_map`. +template <class T, std::size_t N> +struct std::hash<std::array<T, N>> +{ + std::size_t operator()(const std::array<T, N>& array) const + { + std::hash<T> hasher; + std::size_t result = 0; + for (auto&& element : array) { boost::hash_combine(result, hasher(element)); } + return result; + } +}; + +// Measurement selector for seed +class MeasurementSelector +{ + public: + using Traj = Acts::VectorMultiTrajectory; + explicit MeasurementSelector(Acts::MeasurementSelector selector) + : m_selector(std::move(selector)) {} + + void setSeed(const std::optional<SimSeed>& seed) { m_seed = seed; } + + Acts::Result<std::pair<std::vector<Traj::TrackStateProxy>::iterator, + std::vector<Traj::TrackStateProxy>::iterator>> + select(std::vector<Traj::TrackStateProxy>& candidates, + bool& isOutlier, const Acts::Logger& logger) const + { + if (m_seed.has_value()) + { + std::vector<Traj::TrackStateProxy> newCandidates; + for (const auto& candidate : candidates) + { + if (isSeedCandidate(candidate)) { newCandidates.push_back(candidate); } + } + + if (!newCandidates.empty()) { candidates = std::move(newCandidates); } + } + + return m_selector.select<Acts::VectorMultiTrajectory>(candidates, isOutlier, logger); + } + + private: + Acts::MeasurementSelector m_selector; + std::optional<SimSeed> m_seed; + + bool isSeedCandidate(const Traj::TrackStateProxy& candidate) const + { + assert(candidate.hasUncalibratedSourceLink()); + const Acts::SourceLink& sourceLink = candidate.getUncalibratedSourceLink(); + for (const auto& sp : m_seed->sp()) + { + for (const auto& sl : sp->sourceLinks()) + { + if (sourceLink.get<IndexSourceLink>() == sl.get<IndexSourceLink>()) { return true; } + } + } + return false; + } + +}; // class MeasurementSelector + +/// Source link indices of the bottom, middle, top measurements. +/// * In case of strip seeds only the first source link of the pair is used. +using SeedIdentifier = std::array<Index, 3>; + +/// Build a seed identifier from a seed. +/// +/// @param seed The seed to build the identifier from. +/// @return The seed identifier. +SeedIdentifier makeSeedIdentifier(const SimSeed& seed) +{ + SeedIdentifier result; + for (const auto& [i, sp] : Acts::enumerate(seed.sp())) + { + const Acts::SourceLink& firstSourceLink = sp->sourceLinks().front(); + result.at(i) = firstSourceLink.get<IndexSourceLink>().index(); + } + return result; +} + +/// Visit all possible seed identifiers of a track. +/// +/// @param track The track to visit the seed identifiers of. +/// @param visitor The visitor to call for each seed identifier. +template <typename Visitor> +void visitSeedIdentifiers(const TrackProxy& track, Visitor visitor) +{ + // first we collect the source link indices of the track states + std::vector<Index> sourceLinkIndices; + sourceLinkIndices.reserve(track.nMeasurements()); + for (const auto& trackState : track.trackStatesReversed()) + { + if (!trackState.hasUncalibratedSourceLink()) { continue; } + const Acts::SourceLink& sourceLink = trackState.getUncalibratedSourceLink(); + sourceLinkIndices.push_back(sourceLink.get<IndexSourceLink>().index()); + } + + // then we iterate over all possible triplets and form seed identifiers + for (std::size_t i = 0; i < sourceLinkIndices.size(); ++i) + { + for (std::size_t j = i + 1; j < sourceLinkIndices.size(); ++j) + { + for (std::size_t k = j + 1; k < sourceLinkIndices.size(); ++k) + { + // Putting them into reverse order (k, j, i) to compensate for the `trackStatesReversed` above. + visitor({sourceLinkIndices.at(k), sourceLinkIndices.at(j), sourceLinkIndices.at(i)}); + } + } + } +} + +class BranchStopper +{ + public: + using Config = std::optional<std::variant<Acts::TrackSelector::Config, Acts::TrackSelector::EtaBinnedConfig>>; + using BranchStopperResult = Acts::CombinatorialKalmanFilterBranchStopperResult; + + mutable std::atomic<std::size_t> m_nStoppedBranches{0}; + explicit BranchStopper(const Config& config) : m_config(config) {} + + BranchStopperResult operator()( const Acts::CombinatorialKalmanFilterTipState& tipState, + Acts::VectorMultiTrajectory::TrackStateProxy& trackState ) const + { + if (!m_config.has_value()) { return BranchStopperResult::Continue; } + + const Acts::TrackSelector::Config* singleConfig = std::visit + ( + [&](const auto& config) -> const Acts::TrackSelector::Config* + { + using T = std::decay_t<decltype(config)>; + if constexpr (std::is_same_v<T, Acts::TrackSelector::Config>) { return &config; } + else if constexpr (std::is_same_v<T, Acts::TrackSelector::EtaBinnedConfig>) + { + double theta = trackState.parameters()[Acts::eBoundTheta]; + double eta = -std::log(std::tan(0.5 * theta)); + return config.hasCuts(eta) ? &config.getCuts(eta) : nullptr; + } + }, *m_config + ); + + if (singleConfig == nullptr) + { + ++m_nStoppedBranches; + return BranchStopperResult::StopAndDrop; + } + + bool enoughMeasurements = + tipState.nMeasurements >= singleConfig->minMeasurements; + bool tooManyHoles = tipState.nHoles > singleConfig->maxHoles; + bool tooManyOutliers = tipState.nOutliers > singleConfig->maxOutliers; + + if (tooManyHoles || tooManyOutliers) { + ++m_nStoppedBranches; + return enoughMeasurements ? BranchStopperResult::StopAndKeep + : BranchStopperResult::StopAndDrop; + } + + return BranchStopperResult::Continue; + } + + private: + Config m_config; +}; \ No newline at end of file diff --git a/Reconstruction/RecActsTracking/src/utils/GeometryContainers.hpp b/Reconstruction/RecActsTracking/src/utils/GeometryContainers.hpp new file mode 100644 index 0000000000000000000000000000000000000000..6d4b6dcc5f9b2b39f6e6ab2b6e5a74703cce2653 --- /dev/null +++ b/Reconstruction/RecActsTracking/src/utils/GeometryContainers.hpp @@ -0,0 +1,355 @@ +// STL +#include <algorithm> +#include <cassert> +#include <cstddef> +#include <iostream> +#include <utility> +#include <iterator> + +// boost +#include <boost/container/flat_map.hpp> +#include <boost/container/flat_set.hpp> + +// acts +#include "Acts/EventData/SourceLink.hpp" +#include "Acts/Geometry/GeometryIdentifier.hpp" +#include "Acts/Surfaces/Surface.hpp" + +template <typename Iterator> +class Range +{ + public: + Range(Iterator b, Iterator e) : m_begin(b), m_end(e) {} + Range(Range&&) = default; + Range(const Range&) = default; + ~Range() = default; + Range& operator=(Range&&) = default; + Range& operator=(const Range&) = default; + + Iterator begin() const { return m_begin; } + Iterator end() const { return m_end; } + bool empty() const { return m_begin == m_end; } + std::size_t size() const { return std::distance(m_begin, m_end); } + + private: + Iterator m_begin; + Iterator m_end; +}; + +template <typename Iterator> +Range<Iterator> makeRange(Iterator begin, Iterator end) +{ return Range<Iterator>(begin, end); } + +template <typename Iterator> +Range<Iterator> makeRange(std::pair<Iterator, Iterator> range) +{ return Range<Iterator>(range.first, range.second); } + + +/// Proxy for iterating over groups of elements within a container. +/// +/// @note Each group will contain at least one element. +/// +/// Consecutive elements with the same key (as defined by the KeyGetter) are +/// placed in one group. The proxy should always be used as part of a +/// range-based for loop. In combination with structured bindings to reduce the +/// boilerplate, the group iteration can be written as +/// +/// for (auto&& [key, elements] : GroupBy<...>(...)) { +/// // do something with just the key +/// ... +/// +/// // iterate over the group elements +/// for (const auto& element : elements) { +/// ... +/// } +/// } +/// +template <typename Iterator, typename KeyGetter> +class GroupBy { + public: + /// The key type that identifies elements within a group. + using Key = std::decay_t<decltype(KeyGetter()(*Iterator()))>; + /// A Group is an iterator range with the associated key. + using Group = std::pair<Key, Range<Iterator>>; + /// Iterator type representing the end of the groups. + /// + /// The end iterator will not be dereferenced in C++17 range-based loops. It + /// can thus be a simpler type without the overhead of the full group iterator + /// below. + using GroupEndIterator = Iterator; + /// Iterator type representing a group of elements. + class GroupIterator { + public: + using iterator_category = std::input_iterator_tag; + using value_type = Group; + using difference_type = std::ptrdiff_t; + using pointer = Group*; + using reference = Group&; + + constexpr GroupIterator(const GroupBy& groupBy, Iterator groupBegin) + : m_groupBy(groupBy), + m_groupBegin(groupBegin), + m_groupEnd(groupBy.findEndOfGroup(groupBegin)) {} + /// Pre-increment operator to advance to the next group. + constexpr GroupIterator& operator++() { + // make the current end the new group beginning + std::swap(m_groupBegin, m_groupEnd); + // find the end of the next group starting from the new beginning + m_groupEnd = m_groupBy.findEndOfGroup(m_groupBegin); + return *this; + } + /// Post-increment operator to advance to the next group. + constexpr GroupIterator operator++(int) { + GroupIterator retval = *this; + ++(*this); + return retval; + } + /// Dereference operator that returns the pointed-to group of elements. + constexpr Group operator*() const { + const Key key = (m_groupBegin != m_groupEnd) + ? m_groupBy.m_keyGetter(*m_groupBegin) + : Key(); + return {key, makeRange(m_groupBegin, m_groupEnd)}; + } + + private: + const GroupBy& m_groupBy; + Iterator m_groupBegin; + Iterator m_groupEnd; + + friend constexpr bool operator==(const GroupIterator& lhs, + const GroupEndIterator& rhs) { + return lhs.m_groupBegin == rhs; + } + friend constexpr bool operator!=(const GroupIterator& lhs, + const GroupEndIterator& rhs) { + return !(lhs == rhs); + } + }; + + /// Construct the group-by proxy for an iterator range. + constexpr GroupBy(Iterator begin, Iterator end, + KeyGetter keyGetter = KeyGetter()) + : m_begin(begin), m_end(end), m_keyGetter(std::move(keyGetter)) {} + constexpr GroupIterator begin() const { + return GroupIterator(*this, m_begin); + } + constexpr GroupEndIterator end() const { return m_end; } + constexpr bool empty() const { return m_begin == m_end; } + + private: + Iterator m_begin; + Iterator m_end; + KeyGetter m_keyGetter; + + /// Find the end of the group that starts at the given position. + /// + /// This uses a linear search from the start position and thus has linear + /// complexity in the group size. It does not assume any ordering of the + /// underlying container and is a cache-friendly access pattern. + constexpr Iterator findEndOfGroup(Iterator start) const { + // check for end so we can safely dereference the start iterator. + if (start == m_end) { + return start; + } + // search the first element that does not share a key with the start. + return std::find_if_not(std::next(start), m_end, + [this, start](const auto& x) { + return m_keyGetter(x) == m_keyGetter(*start); + }); + } +}; + +/// Construct the group-by proxy for a container. +template <typename Container, typename KeyGetter> +auto makeGroupBy(const Container& container, KeyGetter keyGetter) + -> GroupBy<decltype(std::begin(container)), KeyGetter> { + return {std::begin(container), std::end(container), std::move(keyGetter)}; +} + +// extract the geometry identifier from a variety of types +struct GeometryIdGetter +{ + // explicit geometry identifier are just forwarded + constexpr Acts::GeometryIdentifier operator()( + Acts::GeometryIdentifier geometryId) const { return geometryId; } + + // encoded geometry ids are converted back to geometry identifiers. + constexpr Acts::GeometryIdentifier operator()( + Acts::GeometryIdentifier::Value encoded) const { return Acts::GeometryIdentifier(encoded); } + + // support elements in map-like structures. + template <typename T> + constexpr Acts::GeometryIdentifier operator()( + const std::pair<Acts::GeometryIdentifier, T>& mapItem) const { return mapItem.first; } + + // support elements that implement `.geometryId()`. + template <typename T> + inline auto operator()(const T& thing) const -> + decltype(thing.geometryId(), Acts::GeometryIdentifier()) { return thing.geometryId(); } + + // support reference_wrappers around such types as well + template <typename T> + inline auto operator()(std::reference_wrapper<T> thing) const -> + decltype(thing.get().geometryId(), Acts::GeometryIdentifier()) { return thing.get().geometryId(); } +}; + +struct CompareGeometryId +{ + // indicate that comparisons between keys and full objects are allowed. + using is_transparent = void; + // compare two elements using the automatic key extraction. + template <typename Left, typename Right> + constexpr bool operator()(Left&& lhs, Right&& rhs) const + { return GeometryIdGetter()(lhs) < GeometryIdGetter()(rhs); } +}; + +/// Store elements that know their detector geometry id, e.g. simulation hits. +/// +/// @tparam T type to be stored, must be compatible with `CompareGeometryId` +/// +/// The container stores an arbitrary number of elements for any geometry +/// id. Elements can be retrieved via the geometry id; elements can be selected +/// for a specific geometry id or for a larger range, e.g. a volume or a layer +/// within the geometry hierarchy using the helper functions below. Elements can +/// also be accessed by index that uniquely identifies each element regardless +/// of geometry id. +template <typename T> +using GeometryIdMultiset = boost::container::flat_multiset<T, CompareGeometryId>; + +/// Store elements indexed by an geometry id. +/// +/// @tparam T type to be stored +/// +/// The behaviour is the same as for the `GeometryIdMultiset` except that the +/// stored elements do not know their geometry id themself. When iterating +/// the iterator elements behave as for the `std::map`, i.e. +/// +/// for (const auto& entry: elements) { +/// auto id = entry.first; // geometry id +/// const auto& el = entry.second; // stored element +/// } +/// +template <typename T> +using GeometryIdMultimap = GeometryIdMultiset<std::pair<Acts::GeometryIdentifier, T>>; + +/// Select all elements within the given volume. +template <typename T> +inline Range<typename GeometryIdMultiset<T>::const_iterator> selectVolume( + const GeometryIdMultiset<T>& container, Acts::GeometryIdentifier::Value volume) +{ + auto cmp = Acts::GeometryIdentifier().setVolume(volume); + auto beg = std::lower_bound(container.begin(), container.end(), cmp, CompareGeometryId{}); + // WARNING overflows to volume==0 if the input volume is the last one + cmp = Acts::GeometryIdentifier().setVolume(volume + 1u); + // optimize search by using the lower bound as start point. also handles + // volume overflows since the geo id would be located before the start of + // the upper edge search window. + auto end = std::lower_bound(beg, container.end(), cmp, CompareGeometryId{}); + return makeRange(beg, end); +} + +/// Select all elements within the given volume. +template <typename T> +inline auto selectVolume(const GeometryIdMultiset<T>& container, Acts::GeometryIdentifier id) +{ + return selectVolume(container, id.volume()); +} + +/// Select all elements within the given layer. +template <typename T> +inline Range<typename GeometryIdMultiset<T>::const_iterator> selectLayer( + const GeometryIdMultiset<T>& container, + Acts::GeometryIdentifier::Value volume, + Acts::GeometryIdentifier::Value layer) +{ + auto cmp = Acts::GeometryIdentifier().setVolume(volume).setLayer(layer); + auto beg = std::lower_bound(container.begin(), container.end(), cmp, CompareGeometryId{}); + // WARNING resets to layer==0 if the input layer is the last one + cmp = Acts::GeometryIdentifier().setVolume(volume).setLayer(layer + 1u); + // optimize search by using the lower bound as start point. also handles + // volume overflows since the geo id would be located before the start of + // the upper edge search window. + auto end = std::lower_bound(beg, container.end(), cmp, CompareGeometryId{}); + return makeRange(beg, end); +} + +// Select all elements within the given layer. +template <typename T> +inline auto selectLayer(const GeometryIdMultiset<T>& container, Acts::GeometryIdentifier id) +{ + return selectLayer(container, id.volume(), id.layer()); +} + +/// Select all elements for the given module / sensitive surface. +template <typename T> +inline Range<typename GeometryIdMultiset<T>::const_iterator> selectModule( +const GeometryIdMultiset<T>& container, Acts::GeometryIdentifier geoId) +{ + // module is the lowest level and defines a single geometry id value + return makeRange(container.equal_range(geoId)); +} + +/// Select all elements for the given module / sensitive surface. +template <typename T> +inline auto selectModule( + const GeometryIdMultiset<T>& container, + Acts::GeometryIdentifier::Value volume, + Acts::GeometryIdentifier::Value layer, + Acts::GeometryIdentifier::Value module) +{ + return selectModule(container, + Acts::GeometryIdentifier().setVolume(volume).setLayer(layer).setSensitive(module)); +} + +/// Select all elements for the lowest non-zero identifier component. +/// +/// Zero values of lower components are interpreted as wildcard search patterns +/// that select all element at the given geometry hierarchy and below. This only +/// applies to the lower components and not to intermediate zeros. +/// +/// Examples: +/// - volume=2,layer=0,module=3 -> select all elements in the module +/// - volume=1,layer=2,module=0 -> select all elements in the layer +/// - volume=3,layer=0,module=0 -> select all elements in the volume +/// +/// @note An identifier with all components set to zero selects the whole input +/// container. +/// @note Boundary and approach surfaces do not really fit into the geometry +/// hierarchy and must be set to zero for the selection. If they are set on an +/// input identifier, the behaviour of this search method is undefined. +template <typename T> +inline Range<typename GeometryIdMultiset<T>::const_iterator> +selectLowestNonZeroGeometryObject(const GeometryIdMultiset<T>& container, Acts::GeometryIdentifier geoId) +{ + assert((geoId.boundary() == 0u) && "Boundary component must be zero"); + assert((geoId.approach() == 0u) && "Approach component must be zero"); + + if (geoId.sensitive() != 0u) { return selectModule(container, geoId); } + else if (geoId.layer() != 0u) { return selectLayer(container, geoId); } + else if (geoId.volume() != 0u) { return selectVolume(container, geoId); } + else { return makeRange(container.begin(), container.end()); } +} + +/// Iterate over groups of elements belonging to each module/ sensitive surface. +template <typename T> +inline GroupBy<typename GeometryIdMultiset<T>::const_iterator, GeometryIdGetter> +groupByModule(const GeometryIdMultiset<T>& container) +{ + return makeGroupBy(container, GeometryIdGetter()); +} + +/// The accessor for the GeometryIdMultiset container +/// +/// It wraps up a few lookup methods to be used in the Combinatorial Kalman +/// Filter +template <typename T> +struct GeometryIdMultisetAccessor { +using Container = GeometryIdMultiset<T>; +using Key = Acts::GeometryIdentifier; +using Value = typename GeometryIdMultiset<T>::value_type; +using Iterator = typename GeometryIdMultiset<T>::const_iterator; + +// pointer to the container +const Container* container = nullptr; +}; \ No newline at end of file diff --git a/Reconstruction/RecActsTracking/src/utils/MagneticField.hpp b/Reconstruction/RecActsTracking/src/utils/MagneticField.hpp new file mode 100644 index 0000000000000000000000000000000000000000..94ef4152ada600ff5879e9764f65024e10e5b4f4 --- /dev/null +++ b/Reconstruction/RecActsTracking/src/utils/MagneticField.hpp @@ -0,0 +1,121 @@ +// STL +#include <memory> +#include <variant> +#include <vector> + +// acts +#include "Acts/Definitions/Algebra.hpp" +#include "Acts/MagneticField/ConstantBField.hpp" +#include "Acts/MagneticField/InterpolatedBFieldMap.hpp" +#include "Acts/MagneticField/MagneticFieldProvider.hpp" +#include "Acts/MagneticField/MagneticFieldContext.hpp" +#include "Acts/MagneticField/NullBField.hpp" +#include "Acts/Utilities/Grid.hpp" +#include "Acts/Utilities/Result.hpp" +#include "Acts/Utilities/detail/Axis.hpp" +#include "Acts/Utilities/detail/AxisFwd.hpp" +#include "Acts/Utilities/detail/grid_helper.hpp" + +/// The ScalableBField-specific magnetic field context. +struct ScalableBFieldContext +{ Acts::ActsScalar scalor = 1.; }; + +/// A constant magnetic field that is scaled depending on the event context. +class ScalableBField final : public Acts::MagneticFieldProvider +{ + public: + struct Cache + { + Acts::ActsScalar scalor = 1.; + /// @brief constructor with context + Cache(const Acts::MagneticFieldContext& mctx) + { scalor = mctx.get<const ScalableBFieldContext>().scalor; } + }; + + /// @brief construct constant magnetic field from field vector + /// + /// @param [in] B magnetic field vector in global coordinate system + explicit ScalableBField(Acts::Vector3 B) : m_BField(std::move(B)) {} + + /// @brief construct constant magnetic field from components + /// + /// @param [in] Bx magnetic field component in global x-direction + /// @param [in] By magnetic field component in global y-direction + /// @param [in] Bz magnetic field component in global z-direction + ScalableBField(Acts::ActsScalar Bx = 0, Acts::ActsScalar By = 0, Acts::ActsScalar Bz = 0) + : m_BField(Bx, By, Bz) {} + + /// @brief retrieve magnetic field value + /// + /// @param [in] position global position + /// @param [in] cache Cache object (is ignored) + /// @return magnetic field vector + /// + /// @note The @p position is ignored and only kept as argument to provide + /// a consistent interface with other magnetic field services. + Acts::Result<Acts::Vector3> getField( + const Acts::Vector3& /*position*/, + MagneticFieldProvider::Cache& gCache) const override + { + Cache& cache = gCache.as<Cache>(); + return Acts::Result<Acts::Vector3>::success(m_BField * cache.scalor); + } + + /// @brief retrieve magnetic field value & its gradient + /// + /// @param [in] position global position + /// @param [out] derivative gradient of magnetic field vector as (3x3) + /// matrix + /// @param [in] cache Cache object (is ignored) + /// @return magnetic field vector + /// + /// @note The @p position is ignored and only kept as argument to provide + /// a consistent interface with other magnetic field services. + /// @note currently the derivative is not calculated + /// @todo return derivative + Acts::Result<Acts::Vector3> getFieldGradient( + const Acts::Vector3& /*position*/, Acts::ActsMatrix<3, 3>& /*derivative*/, + MagneticFieldProvider::Cache& gCache) const override + { + Cache& cache = gCache.as<Cache>(); + return Acts::Result<Acts::Vector3>::success(m_BField * cache.scalor); + } + + Acts::MagneticFieldProvider::Cache makeCache( + const Acts::MagneticFieldContext& mctx) const override + { + return Acts::MagneticFieldProvider::Cache(std::in_place_type<Cache>, mctx); + } + + /// @brief check whether given 3D position is inside look-up domain + /// + /// @param [in] position global 3D position + /// @return @c true if position is inside the defined look-up grid, + /// otherwise @c false + /// @note The method will always return true for the constant B-Field + bool isInside(const Acts::Vector3& /*position*/) const { return true; } + + /// @brief update magnetic field vector from components + /// + /// @param [in] Bx magnetic field component in global x-direction + /// @param [in] By magnetic field component in global y-direction + /// @param [in] Bz magnetic field component in global z-direction + void setField(double Bx, double By, double Bz) { m_BField << Bx, By, Bz; } + + /// @brief update magnetic field vector + /// + /// @param [in] B magnetic field vector in global coordinate system + void setField(const Acts::Vector3& B) { m_BField = B; } + + private: + /// magnetic field vector + Acts::Vector3 m_BField; +}; // ScalableBField + +using InterpolatedMagneticField2 = Acts::InterpolatedBFieldMap< + Acts::Grid<Acts::Vector2, Acts::detail::EquidistantAxis, + Acts::detail::EquidistantAxis>>; + +using InterpolatedMagneticField3 = Acts::InterpolatedBFieldMap< + Acts::Grid<Acts::Vector3, Acts::detail::EquidistantAxis, + Acts::detail::EquidistantAxis, Acts::detail::EquidistantAxis>>; diff --git a/Reconstruction/RecActsTracking/src/utils/Options.cpp b/Reconstruction/RecActsTracking/src/utils/Options.cpp new file mode 100644 index 0000000000000000000000000000000000000000..0ce24a871fe75c6a368db3b3fd0db3b7d08cdbe7 --- /dev/null +++ b/Reconstruction/RecActsTracking/src/utils/Options.cpp @@ -0,0 +1,166 @@ +// This file is part of the Acts project. +// +// Copyright (C) 2020 CERN for the benefit of the Acts project +// +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#include "Options.hpp" + +#include <algorithm> +#include <iostream> +#include <sstream> +#include <stdexcept> +#include <string> +#include <utility> + +namespace { +constexpr char s_separator = ':'; +} + +// interval + +std::istream& Options::operator>>( + std::istream& is, Options::Interval& interval) { + std::string buf; + is >> buf; + + // default to an unbounded interval + interval.lower.reset(); + interval.upper.reset(); + + // find the limit separator + auto pos = buf.find_first_of(s_separator); + // no separator -> invalid input -> unbounded interval + if (pos == std::string::npos) { + return is; + } + + // if it exists, parse limit before separator + if (0 < pos) { + auto lowerStr = buf.substr(0, pos); + interval.lower = std::stod(lowerStr); + } + // if it exists, parse limit after separator + if ((pos + 1) < buf.size()) { + auto upperStr = buf.substr(pos + 1); + interval.upper = std::stod(upperStr); + } + + return is; +} + +std::ostream& Options::operator<<( + std::ostream& os, const Options::Interval& interval) { + if (!interval.lower.has_value() && !interval.upper.has_value()) { + os << "unbounded"; + } else { + if (interval.lower.has_value()) { + os << interval.lower.value(); + } + os << s_separator; + if (interval.upper.has_value()) { + os << interval.upper.value(); + } + } + return os; +} + +// helper functions to parse and print multiple values + +namespace { + +template <typename value_t, typename converter_t> +void parseVariable(std::istream& is, std::vector<value_t>& values, + converter_t&& convert) { + values.clear(); + + std::string buf; + is >> buf; + std::string bufValue; + std::string::size_type pos = 0; + std::string::size_type end = std::string::npos; + do { + end = buf.find_first_of(s_separator, pos); + if (end == std::string::npos) { + // last element; take the rest of the buffer + bufValue = buf.substr(pos); + } else { + bufValue = buf.substr(pos, end - pos); + pos = end + 1u; + } + values.push_back(convert(bufValue)); + } while (end != std::string::npos); +} + +template <typename value_t, typename converter_t> +void parseFixed(std::istream& is, std::size_t size, value_t* values, + converter_t&& convert) { + // reserve space for the expected number of values + std::vector<value_t> tmp(size, 0); + parseVariable(is, tmp, std::forward<converter_t>(convert)); + if (tmp.size() < size) { + throw std::invalid_argument( + "Not enough values for fixed-size user option, expected " + + std::to_string(size) + " received " + std::to_string(tmp.size())); + } + if (size < tmp.size()) { + throw std::invalid_argument( + "Too many values for fixed-size user option, expected " + + std::to_string(size) + " received " + std::to_string(tmp.size())); + } + std::copy(tmp.begin(), tmp.end(), values); +} + +template <typename value_t> +void print(std::ostream& os, std::size_t size, const value_t* values) { + for (std::size_t i = 0; i < size; ++i) { + if (0u < i) { + os << s_separator; + } + os << values[i]; + } +} + +} // namespace + +// fixed and variable number of generic values + +void Options::detail::parseDoublesFixed(std::istream& is, + std::size_t size, + double* values) { + parseFixed(is, size, values, + [](const std::string& s) { return std::stod(s); }); +} + +void Options::detail::parseDoublesVariable( + std::istream& is, std::vector<double>& values) { + parseVariable(is, values, [](const std::string& s) { return std::stod(s); }); +} + +void Options::detail::printDoubles(std::ostream& os, + std::size_t size, + const double* values) { + print(os, size, values); +} + +// fixed and variable number of integers + +void Options::detail::parseIntegersFixed(std::istream& is, + std::size_t size, + int* values) { + parseFixed(is, size, values, + [](const std::string& s) { return std::stoi(s); }); +} + +void Options::detail::parseIntegersVariable( + std::istream& is, std::vector<int>& values) { + parseVariable(is, values, [](const std::string& s) { return std::stoi(s); }); +} + +void Options::detail::printIntegers(std::ostream& os, + std::size_t size, + const int* values) { + print(os, size, values); +} diff --git a/Reconstruction/RecActsTracking/src/utils/Options.hpp b/Reconstruction/RecActsTracking/src/utils/Options.hpp new file mode 100644 index 0000000000000000000000000000000000000000..897233cbc70b08c3c46419ac39daae967d41bcd0 --- /dev/null +++ b/Reconstruction/RecActsTracking/src/utils/Options.hpp @@ -0,0 +1,159 @@ +#include <array> +#include <cstddef> +#include <iosfwd> +#include <optional> +#include <vector> + +namespace Options { + +/// @defgroup option-types Additional types for program options +/// +/// All types are intended as utility type for the user options and not as a +/// variable type for the configuration structs. They should only be used where +/// a single option can not be represented by an existing primitive types. +/// +/// They also must be distinct types and can not just be typedefs; otherwise we +/// can not define the required operator{<<,>>} overloads in this namespace. +/// +/// @{ + +/// Half open [lower,upper) interval type for a single user option. +/// +/// A missing limit represents an unbounded upper or lower limit. With just +/// one defined limit the interval is just a lower/upper bound; with both +/// limits undefined, the interval is unbounded everywhere and thus contains +/// all possible values. +struct Interval { + std::optional<double> lower; + std::optional<double> upper; +}; + +/// A fixed number of real values as one user option. +/// +/// @note Implemented as a subclass so it is distinct from `std::array` +/// and we can provide overloads in the same namespace. +template <std::size_t kSize> +class Reals : public std::array<double, kSize> {}; + +/// An arbitrary number of revaluesal as one user option. +/// +/// @note Making this a `std::vector<double>` typedef or subclass confuses +/// program options, since `std::vector<double>` is interpreted as a `double` +/// option that can be provided multiple times. +struct VariableReals { + std::vector<double> values; +}; + +/// A fixed number of integers as one user option. +/// +/// @note Implemented as a subclass so it is distinct from `std::array` +/// and we can provide overloads in the same namespace. +template <std::size_t kSize> +class Integers : public std::array<int, kSize> {}; + +/// An arbitrary number of integers as one user option. +/// +/// @note Making this a `std::vector<int>` typedef or subclass confuses +/// program options, since `std::vector<int>` is interpreted as an `int` +/// option that can be provided multiple times. +struct VariableIntegers { + std::vector<int> values; +}; + +/// @} + +/// Extract an interval from an input of the form 'lower:upper'. +/// +/// An input of the form `lower:` or `:upper` sets just one of the limits. Any +/// other input leads to an unbounded interval. +/// +/// @note The more common range notation uses `lower-upper` but the `-` +/// separator complicates the parsing of negative values. +std::istream& operator>>(std::istream& is, Interval& interval); + +/// Print an interval as `lower:upper`. +std::ostream& operator<<(std::ostream& os, const Interval& interval); + +namespace detail { +void parseDoublesFixed(std::istream& is, std::size_t size, double* values); +void parseDoublesVariable(std::istream& is, std::vector<double>& values); +void printDoubles(std::ostream& os, std::size_t size, const double* values); +} // namespace detail + +/// Extract a fixed number of doubles from an input of the form 'x:y:z'. +/// +/// @note If the values would be separated by whitespace, negative values +/// and additional command line both start with `-` and would be +/// undistinguishable. +template <std::size_t kSize> +inline std::istream& operator>>(std::istream& is, Reals<kSize>& values) { + detail::parseDoublesFixed(is, kSize, values.data()); + return is; +} + +/// Extract a variable number of doubles from an input of the form 'x:y:...'. +/// +/// @note If the values would be separated by whitespace, negative values +/// and additional command line both start with `-` and would be +/// undistinguishable. +inline std::istream& operator>>(std::istream& is, VariableReals& values) { + detail::parseDoublesVariable(is, values.values); + return is; +} + +/// Print a fixed number of doubles as `x:y:z`. +template <std::size_t kSize> +inline std::ostream& operator<<(std::ostream& os, const Reals<kSize>& values) { + detail::printDoubles(os, kSize, values.data()); + return os; +} + +/// Print a variable number of doubles as `x:y:z:...`. +inline std::ostream& operator<<(std::ostream& os, const VariableReals& values) { + detail::printDoubles(os, values.values.size(), values.values.data()); + return os; +} + +namespace detail { +void parseIntegersFixed(std::istream& is, std::size_t size, int* values); +void parseIntegersVariable(std::istream& is, std::vector<int>& values); +void printIntegers(std::ostream& os, std::size_t size, const int* values); +} // namespace detail + +/// Extract a fixed number of integers from an input of the form 'x:y:z'. +/// +/// @note If the values would be separated by whitespace, negative values +/// and additional command line both start with `-` and would be +/// undistinguishable. +template <std::size_t kSize> +inline std::istream& operator>>(std::istream& is, Integers<kSize>& values) { + detail::parseIntegersFixed(is, kSize, values.data()); + return is; +} + +/// Extract a variable number of integers from an input of the form 'x:y:...'. +/// +/// @note If the values would be separated by whitespace, negative values +/// and additional command line both start with `-` and would be +/// undistinguishable. +inline std::istream& operator>>(std::istream& is, VariableIntegers& values) { + detail::parseIntegersVariable(is, values.values); + return is; +} + +/// Print a fixed number of integers as `x:y:z`. +template <std::size_t kSize> +inline std::ostream& operator<<(std::ostream& os, + const Integers<kSize>& values) { + detail::printIntegers(os, kSize, values.data()); + return os; +} + +/// Print a variable number of integers as `x:y:z:...`. +inline std::ostream& operator<<(std::ostream& os, + const VariableIntegers& values) { + detail::printIntegers(os, values.values.size(), values.values.data()); + return os; +} + +} // namespace Options diff --git a/Reconstruction/RecActsTracking/src/utils/SimSpacePoint.hpp b/Reconstruction/RecActsTracking/src/utils/SimSpacePoint.hpp new file mode 100644 index 0000000000000000000000000000000000000000..4c577ccdeee387218557f50c30d39ff62b07dc83 --- /dev/null +++ b/Reconstruction/RecActsTracking/src/utils/SimSpacePoint.hpp @@ -0,0 +1,243 @@ +// STL +#include <cmath> +#include <vector> +#include <cstdint> + +// boost +#include <boost/container/static_vector.hpp> + +// acts tools +#include "Acts/Definitions/Algebra.hpp" +#include "Acts/Definitions/Common.hpp" +#include "Acts/EventData/SourceLink.hpp" +#include "Acts/Seeding/Seed.hpp" +#include "Acts/Geometry/GeometryIdentifier.hpp" +#include "Acts/Geometry/TrackingGeometry.hpp" + +// edm4hep +#include "edm4hep/TrackerHit.h" +#include "edm4hep/SimTrackerHit.h" + +// local +#include "GeometryContainers.hpp" + +using Index = std::uint32_t; +template <typename value_t> +using IndexMultimap = boost::container::flat_multimap<Index, value_t>; + +template <typename value_t> +inline boost::container::flat_multimap<value_t, Index> invertIndexMultimap( + const IndexMultimap<value_t>& multimap) { + using InverseMultimap = boost::container::flat_multimap<value_t, Index>; + + // switch key-value without enforcing the new ordering (linear copy) + typename InverseMultimap::sequence_type unordered; + unordered.reserve(multimap.size()); + for (auto&& [index, value] : multimap) { + // value is now the key and the index is now the value + unordered.emplace_back(value, index); + } + + // adopting the unordered sequence will reestablish the correct order + InverseMultimap inverse; +#if BOOST_VERSION < 107800 + for (const auto& i : unordered) { + inverse.insert(i); + } +#else + inverse.insert(unordered.begin(), unordered.end()); +#endif + return inverse; +} + +/// Space point representation of a measurement suitable for track seeding. +class SimSpacePoint { + using Scalar = Acts::ActsScalar; + +public: + /// Construct the space point from edm4hep::TrackerHit + /// + /// @param trackerhit tracker hit to construct the space point from + SimSpacePoint(const edm4hep::TrackerHit trackerhit, + float x, float y, float z, float t, + // const edm4hep::SimTrackerHit simtrackerhit, + // Acts::GeometryIdentifier geometryId, + boost::container::static_vector<Acts::SourceLink, 2> sourceLinks) + : m_x(x), m_y(y), m_z(z), m_t(t), + m_sourceLinks(std::move(sourceLinks)) + // , m_geometryId(geometryId) + { + // m_trackerHit = trackerhit; + // m_simtrackerHit = simtrackerhit; + // m_x = simtrackerhit.getPosition()[0]; + // m_y = simtrackerhit.getPosition()[1]; + // m_z = simtrackerhit.getPosition()[2]; + // m_t = simtrackerhit.getTime(); + // m_cellid = simtrackerhit.getCellID(); + m_rho = std::sqrt(m_x * m_x + m_y * m_y + m_z * m_z); + m_varianceRho = trackerhit.getCovMatrix()[0]; + m_varianceZ = trackerhit.getCovMatrix()[5]; + } + + // edm4hep::TrackerHit getTrackerHit() { return m_trackerHit; } + // edm4hep::SimTrackerHit getSimTrackerHit() { return m_simtrackerHit; } + + constexpr Scalar x() const { return m_x; } + constexpr Scalar y() const { return m_y; } + constexpr Scalar z() const { return m_z; } + constexpr std::optional<Scalar> t() const { return m_t; } + constexpr Scalar r() const { return m_rho; } + constexpr Scalar varianceR() const { return m_varianceRho; } + constexpr Scalar varianceZ() const { return m_varianceZ; } + constexpr std::optional<Scalar> varianceT() const { return m_varianceT; } + + // constexpr std::uint64_t cellid() const { return m_cellid; } + // constexpr Acts::GeometryIdentifier geometryId() const { return m_geometryId; } + + const boost::container::static_vector<Acts::SourceLink, 2>& + sourceLinks() const { return m_sourceLinks; } + + constexpr float topHalfStripLength() const { return m_topHalfStripLength; } + constexpr float bottomHalfStripLength() const { return m_bottomHalfStripLength; } + + Acts::Vector3 topStripDirection() const { return m_topStripDirection; } + Acts::Vector3 bottomStripDirection() const { return m_bottomStripDirection; } + Acts::Vector3 stripCenterDistance() const { return m_stripCenterDistance; } + Acts::Vector3 topStripCenterPosition() const { return m_topStripCenterPosition; } + + constexpr bool validDoubleMeasurementDetails() const { + return m_validDoubleMeasurementDetails; + } + +private: + + // edm4hep::TrackerHit m_trackerHit; + // edm4hep::SimTrackerHit m_simtrackerHit; + // std::uint64_t m_cellid; + // Acts::GeometryIdentifier m_geometryId; + + // Global position + Scalar m_x; + Scalar m_y; + Scalar m_z; + std::optional<Scalar> m_t; + Scalar m_rho; + // Variance in rho/z of the global coordinates + Scalar m_varianceRho; + Scalar m_varianceZ; + std::optional<Scalar> m_varianceT; + // SourceLinks of the corresponding measurements. A Pixel (strip) SP has one + // (two) sourceLink(s). + boost::container::static_vector<Acts::SourceLink, 2> m_sourceLinks; + + // half of the length of the top strip + float m_topHalfStripLength = 0; + // half of the length of the bottom strip + float m_bottomHalfStripLength = 0; + // direction of the top strip + Acts::Vector3 m_topStripDirection = {0, 0, 0}; + // direction of the bottom strip + Acts::Vector3 m_bottomStripDirection = {0, 0, 0}; + // distance between the center of the two strips + Acts::Vector3 m_stripCenterDistance = {0, 0, 0}; + // position of the center of the bottom strip + Acts::Vector3 m_topStripCenterPosition = {0, 0, 0}; + bool m_validDoubleMeasurementDetails = false; + +}; // SimSpacePoint + +inline bool operator==(const SimSpacePoint& lhs, const SimSpacePoint& rhs) { + // (TODO) use sourceLinks for comparison + return ((lhs.x() == rhs.x()) && (lhs.y() == rhs.y()) && + (lhs.z() == rhs.z()) && (lhs.t() == rhs.t())); +} + +/// Container of space points. +using SimSpacePointContainer = std::vector<SimSpacePoint>; +using SimSeed = Acts::Seed<SimSpacePoint>; +using SimSeedContainer = std::vector<Acts::Seed<SimSpacePoint>>; + + +// -------------------------- +// IndexSourceLink +// -------------------------- + +/// A source link that stores just an index. +/// +/// This is intentionally kept as barebones as possible. The source link +/// is just a reference and will be copied, moved around, etc. often. +/// Keeping it small and separate from the actual, potentially large, +/// measurement data should result in better overall performance. +/// Using an index instead of e.g. a pointer, means source link and +/// measurement are decoupled and the measurement representation can be +/// easily changed without having to also change the source link. +class IndexSourceLink final +{ + public: + /// Construct from geometry identifier and index. + IndexSourceLink(Acts::GeometryIdentifier gid, Index idx, edm4hep::TrackerHit trackhit) + : m_geometryId(gid), m_index(idx), m_trackhit(trackhit) {} + + // Construct an invalid source link. + // Must be default constructible to satisfy SourceLinkConcept. + IndexSourceLink() = default; + IndexSourceLink(const IndexSourceLink&) = default; + IndexSourceLink(IndexSourceLink&&) = default; + IndexSourceLink& operator=(const IndexSourceLink&) = default; + IndexSourceLink& operator=(IndexSourceLink&&) = default; + + /// Access the index. + Index index() const { return m_index; } + Acts::GeometryIdentifier geometryId() const { return m_geometryId; } + edm4hep::TrackerHit getTrackerHit() const { return m_trackhit; } + + struct SurfaceAccessor + { + const Acts::TrackingGeometry& trackingGeometry; + const Acts::Surface* operator()(const Acts::SourceLink& sourceLink) const + { + const auto& indexSourceLink = sourceLink.get<IndexSourceLink>(); + return trackingGeometry.findSurface(indexSourceLink.geometryId()); + } + }; + + private: + Acts::GeometryIdentifier m_geometryId; + Index m_index = 0; + edm4hep::TrackerHit m_trackhit; + + friend bool operator==(const IndexSourceLink& lhs, const IndexSourceLink& rhs) + { + return (lhs.geometryId() == rhs.geometryId()) && (lhs.m_index == rhs.m_index); + } + + friend bool operator!=(const IndexSourceLink& lhs, const IndexSourceLink& rhs) + { + return !(lhs == rhs); + } +}; // IndexSourceLink + +/// Container of index source links. +/// +/// Since the source links provide a `.geometryId()` accessor, +/// they can be stored in an ordered geometry container. +using IndexSourceLinkContainer = GeometryIdMultiset<IndexSourceLink>; + +/// Accessor for the above source link container +/// +/// It wraps up a few lookup methods to be used in the Combinatorial Kalman +/// Filter +struct IndexSourceLinkAccessor : GeometryIdMultisetAccessor<IndexSourceLink> +{ + using BaseIterator = GeometryIdMultisetAccessor<IndexSourceLink>::Iterator; + + using Iterator = Acts::SourceLinkAdapterIterator<BaseIterator>; + + // get the range of elements with requested geoId + std::pair<Iterator, Iterator> range(const Acts::Surface& surface) const + { + assert(container != nullptr); + auto [begin, end] = container->equal_range(surface.geometryId()); + return {Iterator{begin}, Iterator{end}}; + } +}; \ No newline at end of file diff --git a/Reconstruction/RecActsTracking/src/utils/TGeoDetector.hpp b/Reconstruction/RecActsTracking/src/utils/TGeoDetector.hpp new file mode 100644 index 0000000000000000000000000000000000000000..4163d23b371700d8707d10793bf7a5637c93a849 --- /dev/null +++ b/Reconstruction/RecActsTracking/src/utils/TGeoDetector.hpp @@ -0,0 +1,596 @@ +#include "Acts/Geometry/CylinderVolumeBuilder.hpp" +#include "Acts/Geometry/CylinderVolumeHelper.hpp" +#include "Acts/Geometry/GeometryContext.hpp" +#include "Acts/Geometry/GeometryIdentifier.hpp" +#include "Acts/Geometry/ITrackingVolumeBuilder.hpp" +#include "Acts/Geometry/LayerArrayCreator.hpp" +#include "Acts/Geometry/LayerCreator.hpp" +#include "Acts/Geometry/PassiveLayerBuilder.hpp" +#include "Acts/Geometry/ProtoLayerHelper.hpp" +#include "Acts/Geometry/SurfaceArrayCreator.hpp" +#include "Acts/Geometry/SurfaceBinningMatcher.hpp" +#include "Acts/Geometry/TrackingGeometry.hpp" +#include "Acts/Geometry/TrackingGeometryBuilder.hpp" +#include "Acts/Geometry/TrackingVolumeArrayCreator.hpp" + +#include "Acts/Plugins/TGeo/TGeoCylinderDiscSplitter.hpp" +#include "Acts/Plugins/TGeo/TGeoLayerBuilder.hpp" +#include "Acts/Plugins/Json/JsonMaterialDecorator.hpp" +#include "Acts/Plugins/Json/ActsJson.hpp" + +#include "Acts/Utilities/BinningType.hpp" +#include "Acts/Utilities/Logger.hpp" + +#include <boost/program_options.hpp> +#include <nlohmann/json.hpp> + +#include <cstddef> +#include <map> +#include <memory> +#include <stdexcept> +#include <string> +#include <utility> +#include <vector> + +namespace Acts +{ + class TGeoDetectorElement; + class TrackingGeometry; + class IMaterialDecorator; +} + +/// ---------------------------- +/// pre definitions +/// ---------------------------- + +/// Half open [lower,upper) interval type for a single user option. +/// +/// A missing limit represents an unbounded upper or lower limit. With just +/// one defined limit the interval is just a lower/upper bound; with both +/// limits undefined, the interval is unbounded everywhere and thus contains +/// all possible values. +struct Interval +{ + std::optional<double> lower; + std::optional<double> upper; +}; + +/// Extract an interval from an input of the form 'lower:upper'. +/// +/// An input of the form `lower:` or `:upper` sets just one of the limits. Any +/// other input leads to an unbounded interval. +/// +/// @note The more common range notation uses `lower-upper` but the `-` +/// separator complicates the parsing of negative values. +std::istream& operator>>(std::istream& is, Interval& interval); + +/// Print an interval as `lower:upper`. +std::ostream& operator<<(std::ostream& os, const Interval& interval); + +struct TGeoConfig { + Acts::Logging::Level surfaceLogLevel = Acts::Logging::WARNING; + Acts::Logging::Level layerLogLevel = Acts::Logging::WARNING; + Acts::Logging::Level volumeLogLevel = Acts::Logging::WARNING; + + std::string fileName; + bool buildBeamPipe = false; + double beamPipeRadius{0}; + double beamPipeHalflengthZ{0}; + double beamPipeLayerThickness{0}; + double beamPipeEnvelopeR{1.0}; + double layerEnvelopeR{1.0}; + + double unitScalor = 1.0; + + Acts::TGeoLayerBuilder::ElementFactory elementFactory = + Acts::TGeoLayerBuilder::defaultElementFactory; + + /// Optional geometry identifier hook to be used during closure + std::shared_ptr<const Acts::GeometryIdentifierHook> geometryIdentifierHook = + std::make_shared<Acts::GeometryIdentifierHook>(); + + enum SubVolume : std::size_t { Negative = 0, Central, Positive }; + + template <typename T> + struct LayerTriplet + { + LayerTriplet() = default; + + LayerTriplet(T value) + : negative{value}, central{value}, positive{value} {} + + LayerTriplet(T _negative, T _central, T _positive) + : negative{_negative}, central{_central}, positive{_positive} {} + + T negative; + T central; + T positive; + + T& at(SubVolume i) + { + switch (i) + { + case Negative: return negative; + case Central: return central; + case Positive: return positive; + default: throw std::invalid_argument{"Unknown index"}; + } + } + + const T& at(SubVolume i) const + { + switch (i) + { + case Negative: return negative; + case Central: return central; + case Positive: return positive; + default: throw std::invalid_argument{"Unknown index"}; + } + } + }; + + struct Volume { + std::string name; + LayerTriplet<bool> layers{false}; + LayerTriplet<std::string> subVolumeName; + LayerTriplet<std::vector<std::string>> sensitiveNames; + LayerTriplet<std::string> sensitiveAxes; + LayerTriplet<Interval> rRange; + LayerTriplet<Interval> zRange; + LayerTriplet<double> splitTolR{0}; + LayerTriplet<double> splitTolZ{0}; + LayerTriplet<std::vector<std::pair<int, Acts::BinningType>>> binning0; + LayerTriplet<std::vector<std::pair<int, Acts::BinningType>>> binning1; + + Interval binToleranceR; + Interval binTolerancePhi; + Interval binToleranceZ; + + bool cylinderDiscSplit = false; + unsigned int cylinderNZSegments = 0; + unsigned int cylinderNPhiSegments = 0; + unsigned int discNRSegments = 0; + unsigned int discNPhiSegments = 0; + + bool itkModuleSplit = false; + std::map<std::string, unsigned int> barrelMap; + std::map<std::string, std::vector<std::pair<double, double>>> discMap; + /// pairs of regular expressions to match sensor names and category keys + /// for either the barrelMap or the discMap + /// @TODO in principle vector<pair< > > would be good enough + std::map<std::string, std::string> splitPatterns; + }; + + std::vector<Volume> volumes; +}; + +/// ------------------------------------------------------------------------------------------------ +/// @note nlohmann::json must define functions *from_json* && *to_json* for json conversion +/// ------------------------------------------------------------------------------------------------ + +namespace Acts { +/// Read & Write config for cylinder/disc module splitter +void from_json(const nlohmann::json& j, + Acts::TGeoCylinderDiscSplitter::Config& cdc) +{ + /// Number of segments in phi for a disc + cdc.cylinderPhiSegments = j.at("geo-tgeo-cyl-nphi-segs"); + /// Number of segments in r for a disk + cdc.cylinderLongitudinalSegments = j.at("geo-tgeo-cyl-nz-segs"); + /// Number of segments in phi for a disc + cdc.discPhiSegments = j.at("geo-tgeo-disc-nphi-segs"); + /// Number of segments in r for a disk + cdc.discRadialSegments = j.at("geo-tgeo-disc-nr-segs"); +} + +void to_json(nlohmann::json& j, + const Acts::TGeoCylinderDiscSplitter::Config& cdc) +{ + j = nlohmann::json{{"geo-tgeo-cyl-nphi-segs", cdc.cylinderPhiSegments}, + {"geo-tgeo-cyl-nz-segs", cdc.cylinderLongitudinalSegments}, + {"geo-tgeo-disc-nphi-segs", cdc.discPhiSegments}, + {"geo-tgeo-disc-nr-segs", cdc.discRadialSegments}}; +} + +// enum specialization by nlohman library +NLOHMANN_JSON_SERIALIZE_ENUM(Acts::BinningType, + { + {Acts::BinningType::equidistant, "equidistant"}, + {Acts::BinningType::arbitrary, "arbitrary"}, + }) +} + +/// Read & Write config for options interval +void from_json(const nlohmann::json& j, + Interval& interval) +{ + interval.lower = j.at("lower"); + interval.upper = j.at("upper"); +} + +void to_json(nlohmann::json& j, + const Interval& interval) +{ + j = nlohmann::json{{"lower", interval.lower.value_or(0)}, + {"upper", interval.upper.value_or(0)}}; +} + +/// Read & Write layer configuration triplets +template <typename T> +void from_json(const nlohmann::json& j, + TGeoConfig::LayerTriplet<T>& ltr) +{ + ltr.negative = j.at("negative").get<T>(); + ltr.central = j.at("central").get<T>(); + ltr.positive = j.at("positive").get<T>(); +} + +template <typename T> +void to_json(nlohmann::json& j, + const TGeoConfig::LayerTriplet<T>& ltr) +{ + j = nlohmann::json{{"negative", ltr.negative}, + {"central", ltr.central}, + {"positive", ltr.positive}}; +} + +/// Read & Write volume struct +void from_json(const nlohmann::json& j, TGeoConfig::Volume& vol) { + // subdetector selection + vol.name = j.at("geo-tgeo-volume-name"); + + // configure surface autobinning + vol.binToleranceR = j.at("geo-tgeo-sfbin-r-tolerance"); + vol.binToleranceZ = j.at("geo-tgeo-sfbin-z-tolerance"); + vol.binTolerancePhi = j.at("geo-tgeo-sfbin-phi-tolerance"); + + // Fill layer triplets + vol.layers = j.at("geo-tgeo-volume-layers"); + vol.subVolumeName = j.at("geo-tgeo-subvolume-names"); + vol.sensitiveNames = j.at("geo-tgeo-sensitive-names"); + vol.sensitiveAxes = j.at("geo-tgeo-sensitive-axes"); + vol.rRange = j.at("geo-tgeo-layer-r-ranges"); + vol.zRange = j.at("geo-tgeo-layer-z-ranges"); + vol.splitTolR = j.at("geo-tgeo-layer-r-split"); + vol.splitTolZ = j.at("geo-tgeo-layer-z-split"); + // Set binning manually + vol.binning0 = j.at("geo-tgeo-binning0"); + vol.binning1 = j.at("geo-tgeo-binning1"); + + vol.cylinderDiscSplit = j.at("geo-tgeo-cyl-disc-split"); + if (vol.cylinderDiscSplit) + { + Acts::TGeoCylinderDiscSplitter::Config cdConfig = j.at("Splitters").at("CylinderDisk"); + vol.cylinderNZSegments = cdConfig.cylinderLongitudinalSegments; + vol.cylinderNPhiSegments = cdConfig.cylinderPhiSegments; + vol.discNRSegments = cdConfig.discRadialSegments; + vol.discNPhiSegments = cdConfig.discPhiSegments; + } + vol.itkModuleSplit = false; +} + +void to_json(nlohmann::json& j, const TGeoConfig::Volume& vol) { + j["geo-tgeo-volume-name"] = vol.name; + + j["geo-tgeo-sfbin-r-tolerance"] = vol.binToleranceR; + j["geo-tgeo-sfbin-z-tolerance"] = vol.binToleranceZ; + j["geo-tgeo-sfbin-phi-tolerance"] = vol.binTolerancePhi; + + j["geo-tgeo-volume-layers"] = vol.layers; + j["geo-tgeo-subvolume-names"] = vol.subVolumeName; + j["geo-tgeo-sensitive-names"] = vol.sensitiveNames; + j["geo-tgeo-sensitive-axes"] = vol.sensitiveAxes; + j["geo-tgeo-layer-r-ranges"] = vol.rRange; + j["geo-tgeo-layer-z-ranges"] = vol.zRange; + j["geo-tgeo-layer-r-split"] = vol.splitTolR; + j["geo-tgeo-layer-z-split"] = vol.splitTolZ; + j["geo-tgeo-binning0"] = vol.binning0; + j["geo-tgeo-binning1"] = vol.binning1; + + j["geo-tgeo-cyl-disc-split"] = vol.cylinderDiscSplit; + j["geo-tgeo-itk-module-split"] = vol.itkModuleSplit; + + Acts::TGeoCylinderDiscSplitter::Config cdConfig; + cdConfig.cylinderLongitudinalSegments = vol.cylinderNZSegments; + cdConfig.cylinderPhiSegments = vol.cylinderNPhiSegments; + cdConfig.discRadialSegments = vol.discNRSegments; + cdConfig.discPhiSegments = vol.discNPhiSegments; + j["Splitters"]["CylinderDisk"] = cdConfig; +} + +/// @brief Function that constructs a set of layer builder configs from a central @c TGeoDetector config. +/// +/// @param config The input config +/// @return Vector of layer builder configs +std::vector<Acts::TGeoLayerBuilder::Config> makeLayerBuilderConfigs( + const TGeoConfig& config, const Acts::Logger& logger) +{ + std::vector<Acts::TGeoLayerBuilder::Config> detLayerConfigs; + + // iterate over all configured detector volumes + for (const auto& volume : config.volumes) + { + Acts::TGeoLayerBuilder::Config layerBuilderConfig; + layerBuilderConfig.configurationName = volume.name; + layerBuilderConfig.unit = config.unitScalor; + layerBuilderConfig.elementFactory = config.elementFactory; + + // configure surface autobinning + std::vector<std::pair<double, double>> binTolerances( + static_cast<std::size_t>(Acts::binValues), {0., 0.}); + binTolerances[Acts::binR] = {volume.binToleranceR.lower.value_or(0.), + volume.binToleranceR.upper.value_or(0.)}; + binTolerances[Acts::binZ] = {volume.binToleranceZ.lower.value_or(0.), + volume.binToleranceZ.upper.value_or(0.)}; + binTolerances[Acts::binPhi] = {volume.binTolerancePhi.lower.value_or(0.), + volume.binTolerancePhi.upper.value_or(0.)}; + + layerBuilderConfig.autoSurfaceBinning = true; + layerBuilderConfig.surfaceBinMatcher = Acts::SurfaceBinningMatcher(binTolerances); + + // loop over the negative/central/positive layer configurations + for (auto ncp : { TGeoConfig::Negative, + TGeoConfig::Central, + TGeoConfig::Positive,} ) + { + if (!volume.layers.at(ncp)) { continue; } + + Acts::TGeoLayerBuilder::LayerConfig lConfig; + lConfig.volumeName = volume.subVolumeName.at(ncp); + lConfig.sensorNames = volume.sensitiveNames.at(ncp); + lConfig.localAxes = volume.sensitiveAxes.at(ncp); + lConfig.envelope = {config.layerEnvelopeR, config.layerEnvelopeR}; + + auto rR = volume.rRange.at(ncp); + auto rMin = rR.lower.value_or(0.); + auto rMax = rR.upper.value_or(std::numeric_limits<double>::max()); + auto zR = volume.zRange.at(ncp); + auto zMin = zR.lower.value_or(-std::numeric_limits<double>::max()); + auto zMax = zR.upper.value_or(std::numeric_limits<double>::max()); + lConfig.parseRanges = + { + {Acts::binR, {rMin, rMax}}, + {Acts::binZ, {zMin, zMax}}, + }; + + // Fill the layer splitting parameters in r/z + auto str = volume.splitTolR.at(ncp); + auto stz = volume.splitTolZ.at(ncp); + if (0 < str) { lConfig.splitConfigs.emplace_back(Acts::binR, str); } + if (0 < stz) { lConfig.splitConfigs.emplace_back(Acts::binZ, stz); } + lConfig.binning0 = volume.binning0.at(ncp); + lConfig.binning1 = volume.binning1.at(ncp); + + layerBuilderConfig.layerConfigurations[ncp].push_back(lConfig); + } + + // Perform splitting of cylinders and discs + if (volume.cylinderDiscSplit) + { + Acts::TGeoCylinderDiscSplitter::Config cdsConfig; + cdsConfig.cylinderPhiSegments = volume.cylinderNPhiSegments; + cdsConfig.cylinderLongitudinalSegments = volume.cylinderNZSegments; + cdsConfig.discPhiSegments = volume.discNPhiSegments; + cdsConfig.discRadialSegments = volume.discNRSegments; + layerBuilderConfig.detectorElementSplitter = + std::make_shared<const Acts::TGeoCylinderDiscSplitter>(cdsConfig, + logger.clone("TGeoCylinderDiscSplitter", config.layerLogLevel)); + } + + detLayerConfigs.push_back(layerBuilderConfig); + } + + return detLayerConfigs; +} + +/// @brief Function to build the generic tracking geometry from a TGeo object. +/// +/// @param TGeo_ROOTFilePath is the TGeo ROOT file path +/// @param TGeoConfig_jFilePath is the TGeo configuration file path +/// @param MaterialMap_jFilePath is the material map file path +/// @param logger is the logger object +/// @return a shared pointer to the tracking geometry +std::shared_ptr<const Acts::TrackingGeometry> buildTGeoDetector( + const Acts::GeometryContext& context, + std::vector<std::shared_ptr<const Acts::TGeoDetectorElement>>& detElementStore, + const std::string& TGeo_ROOTFilePath, + const std::string& TGeoConfig_jFilePath, + const std::string& MaterialMap_jFilePath, + const Acts::Logger& logger) +{ + if (TGeo_ROOTFilePath.empty() | TGeoConfig_jFilePath.empty() | + MaterialMap_jFilePath.empty()) { return nullptr; } + + TGeoConfig config; + nlohmann::json djson; + const Acts::MaterialMapJsonConverter::Config m_converter; + + // read input files + config.fileName = TGeo_ROOTFilePath; + std::shared_ptr<const Acts::IMaterialDecorator> mdecorator = std::make_shared<const Acts::JsonMaterialDecorator>(m_converter, MaterialMap_jFilePath, Acts::Logging::Level::INFO); + std::ifstream infile(TGeoConfig_jFilePath, std::ifstream::in | std::ifstream::binary); + + // ------------------------------------------ + // read TGeo Layer Builder Configs File + // ------------------------------------------ + infile >> djson; + config.unitScalor = djson["geo-tgeo-unit-scalor"]; + config.buildBeamPipe = djson["geo-tgeo-build-beampipe"]; + if (config.buildBeamPipe) + { + const auto beamPipeParameters = djson["geo-tgeo-beampipe-parameters"].get<std::array<double, 3>>(); + config.beamPipeRadius = beamPipeParameters[0]; + config.beamPipeHalflengthZ = beamPipeParameters[1]; + config.beamPipeLayerThickness = beamPipeParameters[2]; + } + + // Fill nested volume configs + for (const auto& volume : djson["Volumes"]) + { + auto& vol = config.volumes.emplace_back(); + vol = volume; + } + + // ------------------------------------------ + // logger Configaration + // ------------------------------------------ + Acts::SurfaceArrayCreator::Config sacConfig; + auto surfaceArrayCreator = std::make_shared<const Acts::SurfaceArrayCreator>( + sacConfig, logger.clone("SurfaceArrayCreator", config.surfaceLogLevel)); + // configure the proto layer helper + Acts::ProtoLayerHelper::Config plhConfig; + auto protoLayerHelper = std::make_shared<const Acts::ProtoLayerHelper>( + plhConfig, logger.clone("ProtoLayerHelper", config.layerLogLevel)); + // configure the layer creator that uses the surface array creator + Acts::LayerCreator::Config lcConfig; + lcConfig.surfaceArrayCreator = surfaceArrayCreator; + auto layerCreator = std::make_shared<const Acts::LayerCreator>( + lcConfig, logger.clone("LayerCreator", config.layerLogLevel)); + // configure the layer array creator + Acts::LayerArrayCreator::Config lacConfig; + auto layerArrayCreator = std::make_shared<const Acts::LayerArrayCreator>( + lacConfig, logger.clone("LayerArrayCreator", config.layerLogLevel)); + // tracking volume array creator + Acts::TrackingVolumeArrayCreator::Config tvacConfig; + auto tVolumeArrayCreator = + std::make_shared<const Acts::TrackingVolumeArrayCreator>( tvacConfig, + logger.clone("TrackingVolumeArrayCreator", config.volumeLogLevel)); + + // configure the cylinder volume helper + Acts::CylinderVolumeHelper::Config cvhConfig; + cvhConfig.layerArrayCreator = layerArrayCreator; + cvhConfig.trackingVolumeArrayCreator = tVolumeArrayCreator; + auto cylinderVolumeHelper = + std::make_shared<const Acts::CylinderVolumeHelper>(cvhConfig, + logger.clone("CylinderVolumeHelper", config.volumeLogLevel)); + + // ------------------------------------------ + // logger Configaration + // ------------------------------------------ + // list the volume builders + std::list<std::shared_ptr<const Acts::ITrackingVolumeBuilder>> volumeBuilders; + + // Create a beam pipe if configured to do so + if (config.buildBeamPipe) + { + /// configure the beam pipe layer builder + Acts::PassiveLayerBuilder::Config bplConfig; + bplConfig.layerIdentification = "BeamPipe"; + bplConfig.centralLayerRadii = {config.beamPipeRadius}; + bplConfig.centralLayerHalflengthZ = {config.beamPipeHalflengthZ}; + bplConfig.centralLayerThickness = {config.beamPipeLayerThickness}; + auto beamPipeBuilder = std::make_shared<const Acts::PassiveLayerBuilder>( + bplConfig, logger.clone("BeamPipeLayerBuilder", config.layerLogLevel)); + // create the volume for the beam pipe + Acts::CylinderVolumeBuilder::Config bpvConfig; + bpvConfig.trackingVolumeHelper = cylinderVolumeHelper; + bpvConfig.volumeName = "BeamPipe"; + bpvConfig.layerBuilder = beamPipeBuilder; + bpvConfig.layerEnvelopeR = {config.beamPipeEnvelopeR, config.beamPipeEnvelopeR}; + bpvConfig.buildToRadiusZero = true; + auto beamPipeVolumeBuilder = + std::make_shared<const Acts::CylinderVolumeBuilder>(bpvConfig, + logger.clone("BeamPipeVolumeBuilder", config.volumeLogLevel)); + // add to the list of builders + volumeBuilders.push_back(beamPipeVolumeBuilder); + } + + // Import the file from + TGeoManager::Import(config.fileName.c_str()); + auto layerBuilderConfigs = makeLayerBuilderConfigs(config, logger); + // Remember the layer builders to collect the detector elements + std::vector<std::shared_ptr<const Acts::TGeoLayerBuilder>> tgLayerBuilders; + for (auto& lbc : layerBuilderConfigs) + { + std::shared_ptr<const Acts::LayerCreator> layerCreatorLB = nullptr; + if (lbc.autoSurfaceBinning) + { + // Configure surface array creator (optionally) per layer builder + // (in order to configure them to work appropriately) + Acts::SurfaceArrayCreator::Config sacConfigLB; + sacConfigLB.surfaceMatcher = lbc.surfaceBinMatcher; + auto surfaceArrayCreatorLB = + std::make_shared<const Acts::SurfaceArrayCreator>(sacConfigLB, + logger.clone(lbc.configurationName + "SurfaceArrayCreator", config.surfaceLogLevel)); + // configure the layer creator that uses the surface array creator + Acts::LayerCreator::Config lcConfigLB; + lcConfigLB.surfaceArrayCreator = surfaceArrayCreatorLB; + layerCreatorLB = std::make_shared<const Acts::LayerCreator>(lcConfigLB, + logger.clone(lbc.configurationName + "LayerCreator", config.layerLogLevel)); + } + + // Configure the proto layer helper + Acts::ProtoLayerHelper::Config plhConfigLB; + auto protoLayerHelperLB = std::make_shared<const Acts::ProtoLayerHelper>( plhConfigLB, + logger.clone(lbc.configurationName + "ProtoLayerHelper", config.layerLogLevel)); + + lbc.layerCreator = + (layerCreatorLB != nullptr) ? layerCreatorLB : layerCreator; + lbc.protoLayerHelper = + (protoLayerHelperLB != nullptr) ? protoLayerHelperLB : protoLayerHelper; + + auto layerBuilder = std::make_shared<const Acts::TGeoLayerBuilder>( lbc, + logger.clone(lbc.configurationName + "LayerBuilder", config.layerLogLevel)); + + // remember the layer builder + tgLayerBuilders.push_back(layerBuilder); + + // build the pixel volume + Acts::CylinderVolumeBuilder::Config volumeConfig; + volumeConfig.trackingVolumeHelper = cylinderVolumeHelper; + volumeConfig.volumeName = lbc.configurationName; + volumeConfig.buildToRadiusZero = volumeBuilders.empty(); + volumeConfig.layerEnvelopeR = {config.layerEnvelopeR, config.layerEnvelopeR}; + auto ringLayoutConfiguration = + [&](const std::vector<Acts::TGeoLayerBuilder::LayerConfig>& lConfigs) -> void + { + for (const auto& lcfg : lConfigs) + { + for (const auto& scfg : lcfg.splitConfigs) + { + if (scfg.first == Acts::binR && scfg.second > 0.) + { + volumeConfig.ringTolerance = std::max(volumeConfig.ringTolerance, scfg.second); + volumeConfig.checkRingLayout = true; + } + } + } + }; + ringLayoutConfiguration(lbc.layerConfigurations[0]); + ringLayoutConfiguration(lbc.layerConfigurations[2]); + volumeConfig.layerBuilder = layerBuilder; + auto volumeBuilder = std::make_shared<const Acts::CylinderVolumeBuilder>( volumeConfig, + logger.clone(lbc.configurationName + "VolumeBuilder", config.volumeLogLevel)); + // add to the list of builders + volumeBuilders.push_back(volumeBuilder); + } + + //------------------------------------------------------------------------------------- + // create the tracking geometry + Acts::TrackingGeometryBuilder::Config tgConfig; + // Add the builders + tgConfig.materialDecorator = std::move(mdecorator); + tgConfig.geometryIdentifierHook = config.geometryIdentifierHook; + + for (auto& vb : volumeBuilders) + { + tgConfig.trackingVolumeBuilders.push_back( + [=](const auto& gcontext, const auto& inner, const auto&) + { return vb->trackingVolume(gcontext, inner); }); + } + // Add the helper + tgConfig.trackingVolumeHelper = cylinderVolumeHelper; + auto cylinderGeometryBuilder = std::make_shared<const Acts::TrackingGeometryBuilder>( + tgConfig, logger.clone("TrackerGeometryBuilder", config.volumeLogLevel)); + // get the geometry + auto trackingGeometry = cylinderGeometryBuilder->trackingGeometry(context); + // collect the detector element store + for (auto& lBuilder : tgLayerBuilders) + { + auto detElements = lBuilder->detectorElements(); + detElementStore.insert(detElementStore.begin(), detElements.begin(), detElements.end()); + } + + /// return the tracking geometry + return trackingGeometry; +} \ No newline at end of file