File indexing completed on 2026-07-12 07:36:54
0001
0002
0003
0004
0005
0006
0007
0008
0009 #include "ActsExamples/Io/Arrow/ColliderMLRelease1InputConverter.hpp"
0010
0011 #include "Acts/Definitions/TrackParametrization.hpp"
0012 #include "Acts/Geometry/TrackingGeometry.hpp"
0013 #include "Acts/Surfaces/Surface.hpp"
0014 #include "ActsExamples/Digitization/MeasurementCreation.hpp"
0015 #include "ActsExamples/EventData/Index.hpp"
0016 #include "ActsExamples/EventData/TruthMatching.hpp"
0017 #include "ActsExamples/Framework/AlgorithmContext.hpp"
0018 #include "ActsExamples/Io/Csv/CsvInputOutput.hpp"
0019 #include "ActsPlugins/Arrow/ArrowUtil.hpp"
0020
0021 #include <cmath>
0022 #include <cstdint>
0023 #include <limits>
0024 #include <memory>
0025 #include <numeric>
0026 #include <stdexcept>
0027 #include <string>
0028 #include <unordered_set>
0029 #include <utility>
0030 #include <vector>
0031
0032 #include <arrow/api.h>
0033
0034 namespace ActsExamples {
0035
0036 namespace {
0037
0038
0039 std::pair<std::int64_t, std::int64_t> rowBounds(const arrow::Table& table,
0040 const std::string& name) {
0041 auto col = table.GetColumnByName(name);
0042 if (!col) {
0043 throw std::runtime_error(
0044 "ColliderMLRelease1InputConverter: missing column '" + name + "'");
0045 }
0046 auto listArr = std::dynamic_pointer_cast<arrow::ListArray>(col->chunk(0));
0047 if (!listArr) {
0048 throw std::runtime_error("ColliderMLRelease1InputConverter: column '" +
0049 name +
0050 "' is not a list array (expected nested layout)");
0051 }
0052 return {listArr->value_offset(0), listArr->value_length(0)};
0053 }
0054
0055
0056 template <typename ArrowArrayType>
0057 std::shared_ptr<ArrowArrayType> colValues(const arrow::Table& table,
0058 const std::string& name) {
0059 auto col = table.GetColumnByName(name);
0060 if (!col) {
0061 throw std::runtime_error(
0062 "ColliderMLRelease1InputConverter: missing column '" + name + "'");
0063 }
0064 auto listArr = std::dynamic_pointer_cast<arrow::ListArray>(col->chunk(0));
0065 if (!listArr) {
0066 throw std::runtime_error("ColliderMLRelease1InputConverter: column '" +
0067 name +
0068 "' is not a list array (expected nested layout)");
0069 }
0070 auto values = std::dynamic_pointer_cast<ArrowArrayType>(listArr->values());
0071 if (!values) {
0072 throw std::runtime_error("ColliderMLRelease1InputConverter: column '" +
0073 name + "' has unexpected value type");
0074 }
0075 return values;
0076 }
0077
0078 std::unordered_map<Acts::GeometryIdentifier, Acts::GeometryIdentifier>
0079 loadGeoIdMapFromCsv(const std::filesystem::path& path,
0080 const std::string& srcPrefix,
0081 const std::string& tgtPrefix) {
0082 CsvReader reader(path.string());
0083 std::vector<std::string> columns;
0084
0085 if (!reader.read(columns)) {
0086 throw std::runtime_error("loadGeoIdMapFromCsv: empty file '" +
0087 path.string() + "'");
0088 }
0089
0090 auto findCol = [&](const std::string& name) -> std::size_t {
0091 for (std::size_t i = 0; i < columns.size(); ++i) {
0092 if (columns[i] == name) {
0093 return i;
0094 }
0095 }
0096 throw std::runtime_error("loadGeoIdMapFromCsv: missing column '" + name +
0097 "' in '" + path.string() + "'");
0098 };
0099
0100 const std::size_t iExtra = findCol(srcPrefix + "_extra");
0101 const std::size_t iVolume = findCol(srcPrefix + "_volume");
0102 const std::size_t iLayer = findCol(srcPrefix + "_layer");
0103 const std::size_t iSensitive = findCol(srcPrefix + "_sensitive");
0104 const std::size_t iTarget = findCol(tgtPrefix + "_packed");
0105
0106 std::unordered_map<Acts::GeometryIdentifier, Acts::GeometryIdentifier> map;
0107 while (reader.read(columns)) {
0108 auto key =
0109 Acts::GeometryIdentifier()
0110 .withExtra(static_cast<std::uint32_t>(std::stoul(columns[iExtra])))
0111 .withVolume(
0112 static_cast<std::uint32_t>(std::stoul(columns[iVolume])))
0113 .withLayer(static_cast<std::uint32_t>(std::stoul(columns[iLayer])))
0114 .withSensitive(
0115 static_cast<std::uint32_t>(std::stoul(columns[iSensitive])));
0116 map.emplace(key, Acts::GeometryIdentifier(std::stoull(columns[iTarget])));
0117 }
0118 return map;
0119 }
0120
0121 }
0122
0123
0124
0125
0126
0127 ActsPlugins::ArrowUtil::ArrowSchemaHandle
0128 ColliderMLRelease1InputConverter::particleSchema() {
0129 return ActsPlugins::ArrowUtil::ArrowSchemaHandle{arrow::schema({
0130 arrow::field("particle_id", arrow::list(arrow::uint64()), false),
0131 arrow::field("pdg_id", arrow::list(arrow::int64()), false),
0132 arrow::field("mass", arrow::list(arrow::float32()), false),
0133 arrow::field("charge", arrow::list(arrow::float32()), false),
0134 arrow::field("vx", arrow::list(arrow::float32()), false),
0135 arrow::field("vy", arrow::list(arrow::float32()), false),
0136 arrow::field("vz", arrow::list(arrow::float32()), false),
0137 arrow::field("time", arrow::list(arrow::float32()), false),
0138 arrow::field("px", arrow::list(arrow::float32()), false),
0139 arrow::field("py", arrow::list(arrow::float32()), false),
0140 arrow::field("pz", arrow::list(arrow::float32()), false),
0141 arrow::field("vertex_primary", arrow::list(arrow::uint16()), false),
0142 arrow::field("primary", arrow::list(arrow::boolean()), false),
0143 })};
0144 }
0145
0146 ActsPlugins::ArrowUtil::ArrowSchemaHandle
0147 ColliderMLRelease1InputConverter::hitSchema() {
0148 return ActsPlugins::ArrowUtil::ArrowSchemaHandle{arrow::schema({
0149 arrow::field("x", arrow::list(arrow::float32()), false),
0150 arrow::field("y", arrow::list(arrow::float32()), false),
0151 arrow::field("z", arrow::list(arrow::float32()), false),
0152 arrow::field("true_x", arrow::list(arrow::float32()), false),
0153 arrow::field("true_y", arrow::list(arrow::float32()), false),
0154 arrow::field("true_z", arrow::list(arrow::float32()), false),
0155 arrow::field("time", arrow::list(arrow::float32()), false),
0156 arrow::field("particle_id", arrow::list(arrow::uint64()), false),
0157 arrow::field("detector", arrow::list(arrow::uint8()), false),
0158 arrow::field("volume_id", arrow::list(arrow::uint8()), false),
0159 arrow::field("layer_id", arrow::list(arrow::uint16()), false),
0160 arrow::field("surface_id", arrow::list(arrow::uint32()), false),
0161 })};
0162 }
0163
0164
0165
0166
0167
0168 ColliderMLRelease1InputConverter::ColliderMLRelease1InputConverter(
0169 const Config& cfg, std::unique_ptr<const Acts::Logger> _logger)
0170 : IAlgorithm("ColliderMLRelease1InputConverter", std::move(_logger)),
0171 m_cfg(cfg) {
0172 if (m_cfg.inputParticlesTable.empty()) {
0173 throw std::invalid_argument("inputParticlesTable must be set");
0174 }
0175 if (m_cfg.inputHitsTable.empty()) {
0176 throw std::invalid_argument("inputHitsTable must be set");
0177 }
0178 if (m_cfg.outputParticles.empty() && m_cfg.outputSimHits.empty() &&
0179 m_cfg.outputMeasurements.empty()) {
0180 throw std::invalid_argument(
0181 "at least one output (outputParticles, outputSimHits, "
0182 "outputMeasurements) must be set");
0183 }
0184
0185 if (!m_cfg.outputMeasurements.empty()) {
0186 if (m_cfg.trackingGeometry == nullptr) {
0187 throw std::invalid_argument(
0188 "trackingGeometry is required for outputMeasurements");
0189 }
0190 }
0191
0192
0193
0194 auto sigmaFromPitch = [](double pitch) { return pitch / std::sqrt(12.0); };
0195 struct PitchEntry {
0196 std::uint8_t volumeId;
0197 std::vector<std::pair<Acts::BoundIndices, double>> pitches;
0198 };
0199
0200 static const std::vector<PitchEntry> kPitchData = {
0201
0202 {16, {{Acts::eBoundLoc0, 0.050}, {Acts::eBoundLoc1, 0.050}}},
0203 {17, {{Acts::eBoundLoc0, 0.050}, {Acts::eBoundLoc1, 0.050}}},
0204 {18, {{Acts::eBoundLoc0, 0.050}, {Acts::eBoundLoc1, 0.050}}},
0205
0206 {23, {{Acts::eBoundLoc0, 0.080}, {Acts::eBoundLoc1, 0.500}}},
0207 {24, {{Acts::eBoundLoc0, 0.080}, {Acts::eBoundLoc1, 0.500}}},
0208 {25, {{Acts::eBoundLoc0, 0.080}, {Acts::eBoundLoc1, 0.500}}},
0209
0210 {28, {{Acts::eBoundLoc0, 0.125}}},
0211 {29, {{Acts::eBoundLoc0, 0.100}}},
0212 {30, {{Acts::eBoundLoc0, 0.125}}},
0213 };
0214
0215 for (const auto& entry : kPitchData) {
0216 std::vector<std::pair<Acts::BoundIndices, double>> sigmas;
0217 sigmas.reserve(entry.pitches.size());
0218 for (const auto& [idx, pitch] : entry.pitches) {
0219 sigmas.emplace_back(idx, sigmaFromPitch(pitch));
0220 }
0221 m_subsystemSigmas.emplace(entry.volumeId, std::move(sigmas));
0222 }
0223
0224 m_inputParticles.initialize(m_cfg.inputParticlesTable);
0225 m_inputHits.initialize(m_cfg.inputHitsTable);
0226
0227 m_outputParticles.maybeInitialize(m_cfg.outputParticles);
0228 m_outputSimHits.maybeInitialize(m_cfg.outputSimHits);
0229 m_outputMeasurements.maybeInitialize(m_cfg.outputMeasurements);
0230 m_outputClusters.maybeInitialize(m_cfg.outputClusters);
0231 m_outputMeasurementSubset.maybeInitialize(m_cfg.outputMeasurementSubset);
0232 m_outputMeasSimHitsMap.maybeInitialize(m_cfg.outputMeasSimHitsMap);
0233 m_outputMeasParticlesMap.maybeInitialize(m_cfg.outputMeasParticlesMap);
0234 m_outputParticleMeasurementsMap.maybeInitialize(
0235 m_cfg.outputParticleMeasurementsMap);
0236
0237 if (!m_cfg.geoIdMapPath.empty()) {
0238 m_geoIdMap =
0239 loadGeoIdMapFromCsv(m_cfg.geoIdMapPath, m_cfg.geoIdMapSourcePrefix,
0240 m_cfg.geoIdMapTargetPrefix);
0241 ACTS_INFO("Loaded geo-ID map with " << m_geoIdMap.size() << " entries from "
0242 << m_cfg.geoIdMapPath);
0243 } else if (m_cfg.trackingGeometry != nullptr) {
0244 for (const auto& [gid, surface] :
0245 m_cfg.trackingGeometry->geoIdSurfaceMap()) {
0246 if (gid.sensitive() == 0) {
0247 continue;
0248 }
0249 Acts::GeometryIdentifier key = Acts::GeometryIdentifier()
0250 .withVolume(gid.volume())
0251 .withLayer(gid.layer())
0252 .withSensitive(gid.sensitive());
0253 m_geoIdMap[key] = gid;
0254 }
0255 ACTS_INFO(
0256 "No geoIdMap CSV provided — geometry IDs resolved by matching "
0257 "(volume, layer, sensitive) from the tracking geometry.");
0258 ACTS_DEBUG("Built (vol, lay, sen) fallback map with " << m_geoIdMap.size()
0259 << " entries.");
0260 }
0261 }
0262
0263 ColliderMLRelease1InputConverter::ColliderMLRelease1InputConverter(
0264 const Config& cfg, Acts::Logging::Level level)
0265 : ColliderMLRelease1InputConverter(
0266 cfg,
0267 Acts::getDefaultLogger("ColliderMLRelease1InputConverter", level)) {}
0268
0269 ColliderMLRelease1InputConverter::~ColliderMLRelease1InputConverter() = default;
0270
0271 ProcessCode ColliderMLRelease1InputConverter::execute(
0272 const AlgorithmContext& ctx) const {
0273 const arrow::Table& particleTable = *m_inputParticles(ctx).table();
0274 const arrow::Table& hitsTable = *m_inputHits(ctx).table();
0275
0276
0277
0278
0279
0280
0281
0282 std::unordered_set<std::uint64_t> particleIdsWithHits;
0283 if (!m_cfg.keepParticlesWithoutHits) {
0284 auto [hpOff, nHitsForFilter] = rowBounds(hitsTable, "particle_id");
0285 auto hpidFilterArr =
0286 colValues<arrow::UInt64Array>(hitsTable, "particle_id");
0287 for (std::int64_t i = 0; i < nHitsForFilter; ++i) {
0288 particleIdsWithHits.insert(hpidFilterArr->Value(hpOff + i));
0289 }
0290 }
0291
0292
0293
0294
0295 auto [pOff, nParticles] = rowBounds(particleTable, "particle_id");
0296 auto pidArr = colValues<arrow::UInt64Array>(particleTable, "particle_id");
0297 auto pdgArr = colValues<arrow::Int64Array>(particleTable, "pdg_id");
0298 auto massArr = colValues<arrow::FloatArray>(particleTable, "mass");
0299 auto chargeArr = colValues<arrow::FloatArray>(particleTable, "charge");
0300 auto vxArr = colValues<arrow::FloatArray>(particleTable, "vx");
0301 auto vyArr = colValues<arrow::FloatArray>(particleTable, "vy");
0302 auto vzArr = colValues<arrow::FloatArray>(particleTable, "vz");
0303 auto vtArr = colValues<arrow::FloatArray>(particleTable, "time");
0304 auto pxArr = colValues<arrow::FloatArray>(particleTable, "px");
0305 auto pyArr = colValues<arrow::FloatArray>(particleTable, "py");
0306 auto pzArr = colValues<arrow::FloatArray>(particleTable, "pz");
0307 auto vprimArr =
0308 colValues<arrow::UInt16Array>(particleTable, "vertex_primary");
0309 auto primaryArr = colValues<arrow::BooleanArray>(particleTable, "primary");
0310
0311
0312 std::vector<SimBarcode> barcodes(static_cast<std::size_t>(nParticles));
0313
0314 SimParticleContainer::sequence_type particleSeq;
0315 particleSeq.reserve(static_cast<std::size_t>(nParticles));
0316
0317 for (std::int64_t i = 0; i < nParticles; ++i) {
0318 const std::uint16_t vp = vprimArr->Value(pOff + i);
0319 const bool isPrimary = primaryArr->Value(pOff + i);
0320
0321 SimBarcode bc = SimBarcode()
0322 .withVertexPrimary(vp)
0323 .withParticle(static_cast<std::uint64_t>(i))
0324 .withGeneration(isPrimary ? 0u : 1u);
0325 barcodes[static_cast<std::size_t>(i)] = bc;
0326
0327 if (!m_cfg.keepParticlesWithoutHits &&
0328 particleIdsWithHits.count(static_cast<std::uint64_t>(i)) == 0) {
0329 continue;
0330 }
0331
0332 const double mass = static_cast<double>(massArr->Value(pOff + i));
0333 const double charge = static_cast<double>(chargeArr->Value(pOff + i));
0334 const auto pdg =
0335 Acts::PdgParticle{static_cast<int>(pdgArr->Value(pOff + i))};
0336
0337 SimParticleState state(bc, pdg, charge, mass);
0338 state.setPosition4(vxArr->Value(pOff + i), vyArr->Value(pOff + i),
0339 vzArr->Value(pOff + i), vtArr->Value(pOff + i));
0340 const double px = pxArr->Value(pOff + i);
0341 const double py = pyArr->Value(pOff + i);
0342 const double pz = pzArr->Value(pOff + i);
0343 state.setDirection(px, py, pz);
0344 state.setAbsoluteMomentum(std::hypot(px, py, pz));
0345
0346 particleSeq.emplace_back(state, state);
0347 }
0348
0349 SimParticleContainer particles;
0350 particles.insert(particleSeq.begin(), particleSeq.end());
0351
0352 if (m_outputParticles.isInitialized()) {
0353 m_outputParticles(ctx, SimParticleContainer(particles));
0354 }
0355
0356
0357
0358
0359 const bool needSimHits = m_outputSimHits.isInitialized();
0360 const bool needMeasurements = m_outputMeasurements.isInitialized();
0361
0362 if (!needSimHits && !needMeasurements) {
0363 return ProcessCode::SUCCESS;
0364 }
0365
0366 auto [hOff, nHits] = rowBounds(hitsTable, "x");
0367 auto hxArr = colValues<arrow::FloatArray>(hitsTable, "x");
0368 auto hyArr = colValues<arrow::FloatArray>(hitsTable, "y");
0369 auto hzArr = colValues<arrow::FloatArray>(hitsTable, "z");
0370 auto txArr = colValues<arrow::FloatArray>(hitsTable, "true_x");
0371 auto tyArr = colValues<arrow::FloatArray>(hitsTable, "true_y");
0372 auto tzArr = colValues<arrow::FloatArray>(hitsTable, "true_z");
0373 auto htArr = colValues<arrow::FloatArray>(hitsTable, "time");
0374 auto hpidArr = colValues<arrow::UInt64Array>(hitsTable, "particle_id");
0375 auto detArr = colValues<arrow::UInt8Array>(hitsTable, "detector");
0376 auto volArr = colValues<arrow::UInt8Array>(hitsTable, "volume_id");
0377 auto layerArr = colValues<arrow::UInt16Array>(hitsTable, "layer_id");
0378 auto surfArr = colValues<arrow::UInt32Array>(hitsTable, "surface_id");
0379
0380 SimHitContainer simHits;
0381 MeasurementContainer measurements;
0382 ClusterContainer clusters;
0383
0384
0385 std::unordered_map<std::int32_t, Index> hitIndexToMeas;
0386 MeasurementParticlesMap measParticlesMap;
0387
0388 if (needMeasurements) {
0389 measurements.reserve(static_cast<std::size_t>(nHits));
0390 clusters.reserve(static_cast<std::size_t>(nHits));
0391 }
0392
0393 for (std::int64_t i = 0; i < nHits; ++i) {
0394 const std::uint8_t cmlDet = detArr->Value(hOff + i);
0395 const std::uint8_t cmlVol = volArr->Value(hOff + i);
0396 const std::uint16_t cmlLay = layerArr->Value(hOff + i);
0397 const std::uint32_t cmlSurf = surfArr->Value(hOff + i);
0398 const auto cmlKey = Acts::GeometryIdentifier()
0399 .withExtra(cmlDet)
0400 .withVolume(cmlVol)
0401 .withLayer(cmlLay)
0402 .withSensitive(cmlSurf);
0403
0404 const auto lookupKey = m_cfg.geoIdMapPath.empty()
0405 ? Acts::GeometryIdentifier()
0406 .withVolume(cmlVol)
0407 .withLayer(cmlLay)
0408 .withSensitive(cmlSurf)
0409 : cmlKey;
0410 auto geoIt = m_geoIdMap.find(lookupKey);
0411 if (geoIt == m_geoIdMap.end()) {
0412 ACTS_ERROR("Hit " << i << " (det=" << +cmlDet << " vol=" << +cmlVol
0413 << " lay=" << cmlLay << " surf=" << cmlSurf
0414 << ") not found in geo-ID map");
0415 return ProcessCode::ABORT;
0416 }
0417 const Acts::GeometryIdentifier geoId = geoIt->second;
0418
0419 const std::uint64_t cmlPid = hpidArr->Value(hOff + i);
0420 SimBarcode barcode{};
0421 if (cmlPid < static_cast<std::uint64_t>(barcodes.size())) {
0422 barcode = barcodes[static_cast<std::size_t>(cmlPid)];
0423 }
0424
0425 const double tx = txArr->Value(hOff + i);
0426 const double ty = tyArr->Value(hOff + i);
0427 const double tz = tzArr->Value(hOff + i);
0428 const double tt = htArr->Value(hOff + i);
0429
0430 if (needSimHits) {
0431 Acts::Vector4 pos4{tx, ty, tz, tt};
0432 Acts::Vector4 zero4 = Acts::Vector4::Zero();
0433 simHits.emplace_hint(simHits.end(), geoId, barcode, pos4, zero4, zero4,
0434 static_cast<std::int32_t>(i));
0435 }
0436
0437 if (needMeasurements) {
0438 auto sigmaIt = m_subsystemSigmas.find(cmlVol);
0439 if (sigmaIt == m_subsystemSigmas.end()) {
0440 ACTS_ERROR("Hit " << i << " ColliderML volume_id " << +cmlVol
0441 << " is not a known tracker subsystem");
0442 return ProcessCode::ABORT;
0443 }
0444
0445 const Acts::Surface* surface = m_cfg.trackingGeometry->findSurface(geoId);
0446 if (surface == nullptr) {
0447 ACTS_ERROR("Hit " << i << " geoId " << geoId
0448 << " not found in tracking geometry");
0449 return ProcessCode::ABORT;
0450 }
0451
0452 const auto* regSurface =
0453 dynamic_cast<const Acts::RegularSurface*>(surface);
0454 if (regSurface == nullptr) {
0455 ACTS_ERROR("Hit " << i << " geoId " << geoId
0456 << " surface is not a RegularSurface — unsupported");
0457 return ProcessCode::ABORT;
0458 }
0459
0460 Acts::Vector3 globalPos{static_cast<double>(hxArr->Value(hOff + i)),
0461 static_cast<double>(hyArr->Value(hOff + i)),
0462 static_cast<double>(hzArr->Value(hOff + i))};
0463
0464 auto localResult = regSurface->globalToLocal(
0465 ctx.geoContext, globalPos, std::numeric_limits<double>::max());
0466 if (!localResult.ok() ||
0467 !surface->bounds().inside(localResult.value(),
0468 Acts::BoundaryTolerance::AbsoluteEuclidean(
0469 m_cfg.hitBoundsTolerance))) {
0470 ACTS_ERROR(
0471 "Hit " << i << " geoId " << geoId
0472 << " projected local position outside sensor bounds (tol="
0473 << m_cfg.hitBoundsTolerance << " mm)");
0474 return ProcessCode::ABORT;
0475 }
0476 const Acts::Vector2& lp = localResult.value();
0477
0478 DigitizedParameters dParams;
0479 dParams.cluster.globalPosition = globalPos;
0480 for (const auto& [idx, sigma] : sigmaIt->second) {
0481 dParams.indices.push_back(idx);
0482 dParams.values.push_back(lp[static_cast<int>(idx)]);
0483 dParams.variances.push_back(sigma * sigma);
0484 }
0485
0486 clusters.push_back(std::move(dParams.cluster));
0487 auto meas = createMeasurement(measurements, geoId, dParams);
0488 const Index measIdx = meas.index();
0489
0490 hitIndexToMeas.emplace(static_cast<std::int32_t>(i), measIdx);
0491 if (barcode != SimBarcode{}) {
0492 measParticlesMap.emplace(measIdx, barcode);
0493 }
0494 }
0495 }
0496
0497
0498
0499 MeasurementSimHitsMap measSimHitsMap;
0500 if (needMeasurements && needSimHits && !hitIndexToMeas.empty()) {
0501 SimHitIndex sortedPos = 0;
0502 for (auto& hit : simHits) {
0503 auto it = hitIndexToMeas.find(hit.index());
0504 if (it != hitIndexToMeas.end()) {
0505 measSimHitsMap.emplace(it->second, sortedPos);
0506 }
0507 hit = SimHit(hit.geometryId(), hit.particleId(), hit.fourPosition(),
0508 hit.momentum4Before(), hit.momentum4After(), -1);
0509 ++sortedPos;
0510 }
0511 }
0512
0513 if (needSimHits) {
0514 m_outputSimHits(ctx, std::move(simHits));
0515 }
0516
0517 if (needMeasurements) {
0518 const auto& storedMeasurements =
0519 m_outputMeasurements(ctx, std::move(measurements));
0520 if (m_outputClusters.isInitialized()) {
0521 m_outputClusters(ctx, std::move(clusters));
0522 }
0523
0524 if (m_outputMeasurementSubset.isInitialized()) {
0525 std::vector<MeasurementContainer::Index> allIndices(
0526 storedMeasurements.size());
0527 std::iota(allIndices.begin(), allIndices.end(), Index{0});
0528 m_outputMeasurementSubset(
0529 ctx, MeasurementSubset(storedMeasurements, std::move(allIndices)));
0530 }
0531 if (m_outputMeasSimHitsMap.isInitialized()) {
0532 m_outputMeasSimHitsMap(ctx, std::move(measSimHitsMap));
0533 }
0534 if (m_outputParticleMeasurementsMap.isInitialized()) {
0535 m_outputParticleMeasurementsMap(ctx,
0536 invertIndexMultimap(measParticlesMap));
0537 }
0538 if (m_outputMeasParticlesMap.isInitialized()) {
0539 m_outputMeasParticlesMap(ctx, std::move(measParticlesMap));
0540 }
0541 }
0542
0543 return ProcessCode::SUCCESS;
0544 }
0545
0546 }