Build the DV ONNX input tensors: x, edge_index, edge_attr, n_muon_nodes.
260 {
261 graphData.
graph.reset();
267 graphData.
graph = std::make_unique<InferenceGraph>();
269
270 std::vector<DVNodeAux> nodes;
271
274
275 nodes.reserve(segments ? segments->
size() : 0u);
276
278 using SegmentsPerBucket_t =
279 std::unordered_map<const MuonR4::SpacePointBucket*, SegmentList>;
280
281 using SegmentsPerBucketSignature_t =
282 std::unordered_map<std::string, SegmentList>;
283
284 SegmentsPerBucket_t segmentsPerBucket{};
285 SegmentsPerBucketSignature_t segmentsPerBucketSignature{};
288 const MuonR4::SpacePointBucket* parentBucket = detailed->parent()->parentBucket();
289 appendUniqueSegment(segmentsPerBucket[parentBucket], seg);
290 const std::string parentSig = bucketSignatureKey(*parentBucket);
291 if (!parentSig.empty()) appendUniqueSegment(segmentsPerBucketSignature[parentSig], seg);
292 }
293 std::size_t nSignatureMatchedBuckets = 0
u;
294 for (
const SG::ReadHandleKey<MuonR4::SpacePointContainer>& spKey :
m_spacePointKeys) {
297
298 for (const MuonR4::SpacePointBucket* bucket : *spContainer) {
299 const auto it = segmentsPerBucket.find(bucket);
300 const SegmentList* matchedSegments{nullptr};
301 if (it != segmentsPerBucket.end() && !
it->second.empty()) {
302 matchedSegments = &
it->second;
303 } else {
304 const std::string
sig = bucketSignatureKey(*bucket);
305 const auto sigIt =
sig.empty() ? segmentsPerBucketSignature.end()
306 : segmentsPerBucketSignature.find(sig);
307 if (sigIt != segmentsPerBucketSignature.end() && !sigIt->second.empty()) {
308 matchedSegments = &sigIt->second;
309 ++nSignatureMatchedBuckets;
310 }
311 }
312 if (!matchedSegments) continue;
313
314 const int bucketSector = bucket->msSector() ? static_cast<int>(bucket->msSector()->sector()) : -1;
315 const uint16_t bucketLayers = countLayersInBucket(*bucket);
317 << " sector=" << bucketSector
318 << " layers=" << bucketLayers
319 << " segments=" << matchedSegments->size());
320
322 appendMuonSegmentNode(*seg, bucketSector, nodes);
323 }
324 }
325 }
326
328 << " muon nodes from BucketDumper-style SpacePointBucket-associated segments"
329 << " (signature-matched filtered buckets=" << nSignatureMatchedBuckets << ")");
330 }
331
332 if (segments && nodes.empty() &&
335 ATH_MSG_WARNING(
"No bucket-associated segments were found for DV graph building; "
336 "falling back to all segments from " <<
m_segmentKey.key()
337 << ". This does not match the training converter exactly.");
338 }
340 appendMuonSegmentNode(*seg, static_cast<int>(seg->sector()), nodes);
341 }
342 }
343
346 ATH_MSG_WARNING(
"No bucket-associated segments were found for DV graph building. "
347 "Not falling back to all segments because that does not match the training converter.");
348 }
349
350 const std::size_t nMuonNodes = nodes.size();
351
355
356 nodes.reserve(nodes.size() +
towers->size());
357 for (const CaloTower* tower : *towers) {
358 const float energyMeV = static_cast<float>(tower->energy());
360
361 const float eta =
static_cast<float>(tower->eta());
362 const float phi =
static_cast<float>(tower->phi());
363 float minDR = std::numeric_limits<float>::max();
364 for (std::size_t i = 0;
i < nMuonNodes; ++
i) {
365 minDR = std::min(
366 minDR,
368 }
369
372 static_cast<double>(
phi),
static_cast<double>(
eta));
373 const std::optional<Amg::Vector3D> posMm =
375 if (!posMm) continue;
376
378
379 DVNodeAux node{};
380 node.kind = NodeKind::Calo;
381 node.features[0] = static_cast<float>(posM.mag());
382 node.features[1] = static_cast<float>(posM.theta());
383 node.features[2] = static_cast<float>(posM.phi());
384 node.features[3] =
static_cast<float>(
direction.theta());
385 node.features[4] =
static_cast<float>(
direction.phi());
386 node.features[5] = energyMeV;
387 node.features[6] = static_cast<float>(tower->size());
390 node.energyLike = energyMeV;
392 node.sector = static_cast<int>(
394 nodes.push_back(node);
395 }
396 }
397
398 const std::size_t nCaloNodes = nodes.size() - nMuonNodes;
399 const std::size_t nNodes = nodes.size();
402
403 if (nNodes == 0u) {
404 ATH_MSG_WARNING(
"No muon segment or calo tower nodes found. Skipping DV inference.");
405 return StatusCode::SUCCESS;
406 }
407
409 for (const DVNodeAux& node : nodes) {
411 node.features.begin(), node.features.end());
412 }
413
415 std::vector<float> edgeAttr;
416 edgeAttr.reserve(2u * nMuonNodes * std::max<std::size_t>(nCaloNodes, 1u) *
kEdgeFeatureCount);
417
418 auto addEdge = [&graphData, &edgeAttr, maxEdges](std::size_t
src,
419 std::size_t dst,
421 const DVNodeAux&
b) ->
bool {
422 if (maxEdges >= 0 &&
static_cast<int>(graphData.
srcEdges.size()) >= maxEdges)
return false;
424 const float dEta =
b.eta -
a.eta;
425 const float cosAng = std::clamp(
static_cast<float>(
a.direction.dot(
b.direction)), -1.f, 1.f);
426 std::array<float, kEdgeFeatureCount> attr{
427 b.energyLike -
a.energyLike,
430 cosAng,
431 (
a.sector ==
b.sector) ? 1.f : 0.f};
432
433 graphData.
srcEdges.push_back(
static_cast<int64_t
>(src));
434 graphData.
desEdges.push_back(
static_cast<int64_t
>(dst));
435 edgeAttr.insert(edgeAttr.end(), attr.begin(), attr.end());
436 return true;
437 };
438
439 bool edgeCapReached = false;
440 for (std::size_t im = 0;
im < nMuonNodes && !edgeCapReached; ++
im) {
441 for (std::size_t ic = nMuonNodes;
ic < nNodes; ++
ic) {
444 if (!addEdge(im, ic, nodes[im], nodes[ic])) {
445 edgeCapReached = true;
446 break;
447 }
448 if (!addEdge(ic, im, nodes[ic], nodes[im])) {
449 edgeCapReached = true;
450 break;
451 }
452 }
453 }
454
455 const std::size_t nEdges = graphData.
srcEdges.size();
457 ATH_MSG_DEBUG(
"DV graph has no segment-tower edges and RequireEdges=True; skip inference.");
458 graphData.
graph.reset();
459 return StatusCode::SUCCESS;
460 }
461
463 ATH_MSG_ERROR(
"DV edge attribute size mismatch: E=" << nEdges
464 << " edge_attr.size=" << edgeAttr.size());
465 return StatusCode::FAILURE;
466 }
467
468 Ort::MemoryInfo memInfo = Ort::MemoryInfo::CreateCpu(OrtDeviceAllocator, OrtMemTypeCPU);
469 std::vector<int64_t> nodeShape{
static_cast<int64_t
>(nNodes),
static_cast<int64_t
>(
kNodeFeatureCount)};
470 graphData.
graph->dataTensor.emplace_back(
471 Ort::Value::CreateTensor<float>(memInfo,
474 nodeShape.data(),
475 nodeShape.size()));
476
481
482 std::vector<int64_t> edgeIndexShape{2, static_cast<int64_t>(nEdges)};
483 graphData.
graph->dataTensor.emplace_back(
484 Ort::Value::CreateTensor<int64_t>(memInfo,
487 edgeIndexShape.data(),
488 edgeIndexShape.size()));
489
490 Ort::AllocatorWithDefaultOptions allocator;
491 std::vector<int64_t> edgeAttrShape{
static_cast<int64_t
>(nEdges),
static_cast<int64_t
>(
kEdgeFeatureCount)};
492 Ort::Value edgeAttrTensor = Ort::Value::CreateTensor(allocator,
493 edgeAttrShape.data(),
494 edgeAttrShape.size(),
495 ONNX_TENSOR_ELEMENT_DATA_TYPE_FLOAT);
496 if (!edgeAttr.empty()) {
497 float* edgeAttrData = edgeAttrTensor.GetTensorMutableData<float>();
498 std::copy(edgeAttr.begin(), edgeAttr.end(), edgeAttrData);
499 }
500 graphData.
graph->dataTensor.emplace_back(std::move(edgeAttrTensor));
501
502 std::vector<int64_t> nMuonShape{1};
503 graphData.
graph->dataTensor.emplace_back(
504 Ort::Value::CreateTensor<int64_t>(memInfo,
506 1,
507 nMuonShape.data(),
508 nMuonShape.size()));
509
510 if (msgLvl(MSG::DEBUG)) {
511 ATH_MSG_DEBUG(
"Built DV graph: N=" << nNodes <<
" (muon=" << nMuonNodes
512 << ", calo=" << nCaloNodes << "), E=" << nEdges
515 for (std::size_t i = 0;
i < dumpNodes; ++
i) {
516 std::ostringstream
row;
517 row <<
"DVNode[" <<
i <<
"] kind=" << (nodes[
i].kind == NodeKind::Muon ?
"muon" :
"calo") <<
":";
520 }
522 }
524 for (std::size_t e = 0;
e < dumpEdges; ++
e) {
527 << " edge_attr=["
533 }
534 }
535
538 return StatusCode::SUCCESS;
539}
Scalar eta() const
pseudorapidity method
Scalar phi() const
phi method
#define ATH_CHECK
Evaluate an expression and check for errors.
#define ATH_MSG_VERBOSE(x)
#define ATH_MSG_WARNING(x)
Athena::TPCnvVers::Old Athena::TPCnvVers::Old Athena::TPCnvVers::Current Athena::TPCnvVers::Current CaloTowerContainer
size_type size() const noexcept
Returns the number of elements in the collection.
Eigen::Matrix< double, 3, 1 > Vector3D
T wrapToPi(T phi)
Wrap angle in radians to [-pi, pi].
T deltaPhi(T phiA, T phiB)
Return difference phiA - phiB in range [-pi, pi].
DataVector< SpacePointBucket > SpacePointContainer
Abrivation of the space point container type.
const Segment * detailedSegment(const xAOD::MuonSegment &seg)
Helper function to navigate from the xAOD::MuonSegment to the MuonR4::Segment.
row
Appending html table to final .html summary file.
const T * get(const ReadCondHandleKey< T > &key, const EventContext &ctx)
Convenience function to retrieve an object given a ReadCondHandleKey.
bool dPhi(const xAOD::TauJet &tau, const xAOD::CaloVertexedTopoCluster &cluster, float &out)
bool dEta(const xAOD::TauJet &tau, const xAOD::CaloVertexedTopoCluster &cluster, float &out)
const Amg::Vector3D & direction() const
Method to retrieve the direction at the Intersection.
@ u
Enums for curvilinear frames.
double deltaR(double rapidity1, double phi1, double rapidity2, double phi2)
from bare bare rapidity,phi
MuonSegmentContainer_v1 MuonSegmentContainer
Definition of the current "MuonSegment container version".
MuonSegment_v1 MuonSegment
Reference the current persistent version:
FeatureVec_t featureLeaves
Vector containing all features.
EdgeCounterVec_t edgeIndexPacked
Packed edge index buffer (kept alive for ONNX tensors that reference it) This stores [srcEdges,...
std::unique_ptr< InferenceGraph > graph
Pointer to the graph to be parsed to ONNX.
EdgeCounterVec_t srcEdges
Vector encoding the source index of the.
EdgeCounterVec_t desEdges
Vect.
NodeConnectVec_t spacePointsInBucket
Vector keeping track of how many space points are in each parsed bucket.