Skip to content

feat: Synthetic space point event generator in Fatras - #5806

Draft
andiwand wants to merge 1 commit into
acts-project:mainfrom
andiwand:feat-fatras-synthetic-generator
Draft

feat: Synthetic space point event generator in Fatras#5806
andiwand wants to merge 1 commit into
acts-project:mainfrom
andiwand:feat-fatras-synthetic-generator

Conversation

@andiwand

@andiwand andiwand commented Jul 30, 2026

Copy link
Copy Markdown
Contributor

ActsFatras::Synthetic is a second, much smaller fast simulation that shares
none of the Fatras machinery: no tracking geometry, no propagator, no input
data. It fills an Acts::SpacePointContainer directly and makes an ATLAS-like
pile-up of 200 in tens of milliseconds, which is what a seeding benchmark needs
and what the propagator-based simulation cannot provide. Space points carry the
layer and the particle they came from, so a caller needs no truth matching.

The geometry is cylinders at a fixed radius and discs at a fixed z as plain
structs, so a helix crosses every surface in closed form. The restriction is on
the shape alone: every cylinder has its own half-length and every disc its own
rings, so a real endcap is described as the staggered discs and rings it is.
Surfaces carry the material their detector's map reports, banded along the
coordinate they extend in, and that material is the whole of the physics model:
x/X0 scatters and makes electrons, x/L0 makes nuclear products, and the slab
takes the energy. Nothing is fitted on top of it.

Both material effects displace the measured position of a hit and leave the
trajectory a nominal helix -- a scatter by the angle times the lever arm, energy
loss by half the curvature change times that lever arm squared -- which is what
gives a seed the spread a seeder cuts on at a fraction of the cost of stepping.

It is deliberately coarse: a tool for throughput and combinatorics studies
rather than for physics performance. The Generic pixel description ships with
it; the descriptions of real detectors and the configurations fitted to them
follow.

--- END COMMIT MESSAGE ---

What ships here, and what follows

This PR is the generator and the Generic pixel description, with default
settings that belong to no detector in particular. The two real detector
descriptions and the configurations fitted to them were split out into their own
branches, which is what took this from 5.8k lines to 4.9k.

The fits are what says the model works, so, as evidence rather than as content
of this PR: each configuration is fitted against a full simulation of its
detector -- the ITk pixels against a GNN4ITk Athena dump, the ODD pixels against
ColliderML -- on one half of the sample and scored on the other. On the held-out
half, per event and normalised to the reference:

ITk ODD
space points 0.99 0.99
  primary, inside the generated acceptance 0.97 1.02
  primary, outside it 1.03 1.16
  non-primary 1.00 0.95
primaries / event 0.98 1.00
mean primary hits 0.99 1.02
mean secondary hits 0.97 1.10

Surface positions and material are read out of the detector description rather
than fitted; the beam spot, the primary spectrum and the two secondary rates are
what the fit sets.

Where the lines go

area lines files
Fatras, simulation 1850 EventGenerator, detail/{PrimaryGenerator,ParticlePropagator,Helix,Propagation,Sampling,GeneratorScratch}
unit tests 1409 DetectorLayout, Helix, Propagation, EventGenerator
Fatras, geometry 919 DetectorLayout, its builder and the Generic description
configuration 297 EventConfig
Fatras, truth 151 SeedingTruth
documentation 96 docs/groups/fatras.md, Fatras/README.md, namespaces.dox
Fatras, I/O 72 EventCsvWriter
Fatras, event data 58 SyntheticEvent
build 19
total 4871

Six public headers -- DetectorLayout, EventConfig, EventGenerator,
SyntheticEvent, SeedingTruth, EventCsvWriter -- and everything else under
detail/, which the documentation build excludes. The helix, the intersections,
the samplers and the two halves of the generator are implementation.

The stack

  1. this one -- the generator, the Generic pixel description, the unit tests
    and the documentation
  2. feat-fatras-synthetic-itk-layout -- the ITk pixel description and the
    configuration fitted to it
  3. feat-fatras-synthetic-odd-layout -- the same for the Open Data Detector
  4. feat: Build a synthetic layout from a tracking geometry, and bind it to Python #5807 -- makeLayoutFromTrackingGeometry, which derives a layout from any
    Acts::TrackingGeometry, plus the python bindings
  5. test: Seeding benchmarks on the synthetic event #5808 -- four benchmarks: the generator itself and three seeders on its output

Each branch is one commit and includes the ones below it. 2 and 3 have no PR
yet; 4 and 5 sit on 3 and are independent of each other. ITk comes before the
ODD because the ODD's structure test is written against it.

Notes for review

Cost. About 120 ms per event for 236k space points on the ITk pixel layout
(branch 2 above), single threaded on a laptop RelWithDebInfo build. At a fixed
space point count, six times the surfaces costs about twelve percent more, the
per-surface work being an intersection with an early exit -- so resolving an
endcap into its real rings is affordable.

Precision. The intersection formulas are written in the cancellation-free
form throughout and makeHelixFromPoint recovers d0 in double. Both are
load-bearing: the direct form of the turning angle rounds to one above about
30 GeV and misses the intersection outright, and a float d0 loses three digits
there. HelixTests pins both down.

Scope. Deliberately coarse in its physics, and meant for throughput and
combinatorics studies rather than physics performance. It does not replace the
propagator-based Fatras and shares none of its machinery. The known gaps are the
ODD's secondary momentum and its forward production, and the spectrum below the
100 MeV the references stop recording at; they are documented next to the
configurations they belong to, on branch 3.

ACTS_BUILD_FATRAS is implied by ACTS_BUILD_UNITTESTS, since the unit tests of
the generator live in the Fatras test tree.

@github-actions

github-actions Bot commented Jul 30, 2026

Copy link
Copy Markdown
Contributor

📊: Physics performance monitoring for a4f0dca

Full contents

physmon summary

❗️: Downstream build failure

  • eic-shell EICrecon (cc @acts-project/epic-contacts)

@andiwand
andiwand force-pushed the feat-fatras-synthetic-generator branch 2 times, most recently from c40136a to 287ff47 Compare July 31, 2026 12:38
@github-actions

github-actions Bot commented Jul 31, 2026

Copy link
Copy Markdown
Contributor

Public API surface diff

+188 added, 0 breaking.

➕ Added public API

New types / aliases / enums / variables / concepts (27)
  • enums ActsFatras::Synthetic::EnergyLossModel
  • enums ActsFatras::Synthetic::SurfaceShape
  • enums ActsFatras::Synthetic::SurfaceSide
  • type ActsFatras::Synthetic::BandComposition
  • type ActsFatras::Synthetic::BarrelEndcapDescription
  • type ActsFatras::Synthetic::DetectorLayer
  • type ActsFatras::Synthetic::DetectorLayout
  • type ActsFatras::Synthetic::DetectorLayoutBuilder
  • type ActsFatras::Synthetic::DetectorSurface
  • type ActsFatras::Synthetic::DiscDescription
  • type ActsFatras::Synthetic::Event
  • type ActsFatras::Synthetic::EventConfig
  • type ActsFatras::Synthetic::EventGenerator
  • type ActsFatras::Synthetic::EventSummary
  • type ActsFatras::Synthetic::GeneratedParticle
  • type ActsFatras::Synthetic::GenerationConfig
  • type ActsFatras::Synthetic::MaterialConfig
  • type ActsFatras::Synthetic::MeasurementConfig
  • type ActsFatras::Synthetic::PassiveSurfaceDescription
  • type ActsFatras::Synthetic::PropagationConfig
  • type ActsFatras::Synthetic::RingBounds
  • type ActsFatras::Synthetic::SecondaryConfig
  • type ActsFatras::Synthetic::SecondarySamplingConfig
  • type ActsFatras::Synthetic::SeedingSummary
  • type ActsFatras::Synthetic::SimulationConfig
  • type ActsFatras::Synthetic::SurfaceMaterial
  • variables ActsFatras::Synthetic::kNoSurface
New call signatures (incl. defaulted-arg overloads) (36)
  • ActsFatras::Synthetic::DetectorLayoutBuilder::addCylinder(float, float, std::uint32_t)
  • ActsFatras::Synthetic::DetectorLayoutBuilder::addDisc(SurfaceSide, float, float, float, std::uint32_t)
  • ActsFatras::Synthetic::DetectorLayoutBuilder::addDisc(SurfaceSide, float, std::span< const RingBounds >)
  • ActsFatras::Synthetic::DetectorLayoutBuilder::addPassiveCylinder(float)
  • ActsFatras::Synthetic::DetectorLayoutBuilder::addPassiveCylinder(float, float)
  • ActsFatras::Synthetic::DetectorLayoutBuilder::addPassiveCylinder(float, float, float)
  • ActsFatras::Synthetic::DetectorLayoutBuilder::addPassiveDisc(SurfaceSide, float)
  • ActsFatras::Synthetic::DetectorLayoutBuilder::addPassiveDisc(SurfaceSide, float, float)
  • ActsFatras::Synthetic::DetectorLayoutBuilder::addPassiveDisc(SurfaceSide, float, float, float)
  • ActsFatras::Synthetic::DetectorLayoutBuilder::build()
  • ActsFatras::Synthetic::EventConfig::particleMass() const
  • ActsFatras::Synthetic::EventGenerator::EventGenerator(const DetectorLayout &, const EventConfig &)
  • ActsFatras::Synthetic::EventGenerator::generate()
  • ActsFatras::Synthetic::EventGenerator::generate(Event &)
  • ActsFatras::Synthetic::EventGenerator::reset(std::uint32_t)
  • ActsFatras::Synthetic::GeneratedParticle::primary() const
  • ActsFatras::Synthetic::GenerationConfig::numPrimaries() const
  • ActsFatras::Synthetic::SurfaceMaterial::SurfaceMaterial()
  • ActsFatras::Synthetic::SurfaceMaterial::SurfaceMaterial(const Acts::MaterialSlab &)
  • ActsFatras::Synthetic::SurfaceMaterial::SurfaceMaterial(const BandComposition &, std::vector< float >, const std::vector< float > &, const std::vector< float > &)
  • ActsFatras::Synthetic::SurfaceMaterial::SurfaceMaterial(std::vector< float >, std::vector< Acts::MaterialSlab >)
  • ActsFatras::Synthetic::SurfaceMaterial::at(const float) const
  • ActsFatras::Synthetic::SurfaceMaterial::average() const
  • ActsFatras::Synthetic::SurfaceMaterial::scaleThickness(const float)
  • ActsFatras::Synthetic::evaluateSeeds(const Event &, const Acts::SeedContainer &, float)
  • ActsFatras::Synthetic::evaluateSeeds(const Event &, const Acts::SeedContainer &, float, std::size_t)
  • ActsFatras::Synthetic::generateEvent(const DetectorLayout &, const EventConfig &)
  • ActsFatras::Synthetic::genericDetectorPixelDescription()
  • ActsFatras::Synthetic::makeGenericDetectorPixelLayout()
  • ActsFatras::Synthetic::makeLayout(const BarrelEndcapDescription &)
  • ActsFatras::Synthetic::materialSlab(float, float, float, float, float, float)
  • ActsFatras::Synthetic::subdivideRings(std::span< const RingBounds >, std::uint32_t)
  • ActsFatras::Synthetic::summarize(const Event &, float)
  • ActsFatras::Synthetic::uniformRings(float, float, std::uint32_t)
  • ActsFatras::Synthetic::updateSurfaceExtents(DetectorLayout &)
  • ActsFatras::Synthetic::writeEventCsv(const Event &, const DetectorLayout &, const std::string &)
New public data members (125)
  • ActsFatras::Synthetic::BandComposition::ar
  • ActsFatras::Synthetic::BandComposition::molarDensityX0
  • ActsFatras::Synthetic::BandComposition::thickness
  • ActsFatras::Synthetic::BandComposition::z
  • ActsFatras::Synthetic::BarrelEndcapDescription::barrelHalfLengthsZ
  • ActsFatras::Synthetic::BarrelEndcapDescription::barrelMaterials
  • ActsFatras::Synthetic::BarrelEndcapDescription::barrelModules
  • ActsFatras::Synthetic::BarrelEndcapDescription::barrelOverlapOffset
  • ActsFatras::Synthetic::BarrelEndcapDescription::barrelOverlapProbabilities
  • ActsFatras::Synthetic::BarrelEndcapDescription::barrelRadii
  • ActsFatras::Synthetic::BarrelEndcapDescription::beamPipeMaterial
  • ActsFatras::Synthetic::BarrelEndcapDescription::beamPipeRadius
  • ActsFatras::Synthetic::BarrelEndcapDescription::discOverlapOffset
  • ActsFatras::Synthetic::BarrelEndcapDescription::discOverlapProbability
  • ActsFatras::Synthetic::BarrelEndcapDescription::discs
  • ActsFatras::Synthetic::BarrelEndcapDescription::escapeHalfZ
  • ActsFatras::Synthetic::BarrelEndcapDescription::escapeRadius
  • ActsFatras::Synthetic::BarrelEndcapDescription::passiveSurfaces
  • ActsFatras::Synthetic::DetectorLayer::layer
  • ActsFatras::Synthetic::DetectorLayer::maxBound
  • ActsFatras::Synthetic::DetectorLayer::minBound
  • ActsFatras::Synthetic::DetectorLayer::moduleIndex
  • ActsFatras::Synthetic::DetectorLayer::refCoord
  • ActsFatras::Synthetic::DetectorLayer::shape
  • ActsFatras::Synthetic::DetectorLayer::side
  • ActsFatras::Synthetic::DetectorLayout::escapeHalfZ
  • ActsFatras::Synthetic::DetectorLayout::escapeRadius
  • ActsFatras::Synthetic::DetectorLayout::layers
  • ActsFatras::Synthetic::DetectorLayout::surfaces
  • ActsFatras::Synthetic::DetectorSurface::layers
  • ActsFatras::Synthetic::DetectorSurface::material
  • ActsFatras::Synthetic::DetectorSurface::maxBound
  • ActsFatras::Synthetic::DetectorSurface::minBound
  • ActsFatras::Synthetic::DetectorSurface::overlapOffset
  • ActsFatras::Synthetic::DetectorSurface::overlapProbability
  • ActsFatras::Synthetic::DetectorSurface::refCoord
  • ActsFatras::Synthetic::DetectorSurface::shape
  • ActsFatras::Synthetic::DiscDescription::absZ
  • ActsFatras::Synthetic::DiscDescription::material
  • ActsFatras::Synthetic::DiscDescription::rings
  • ActsFatras::Synthetic::Event::particles
  • ActsFatras::Synthetic::Event::spacePoints
  • ActsFatras::Synthetic::EventConfig::bFieldZ
  • ActsFatras::Synthetic::EventConfig::generation
  • ActsFatras::Synthetic::EventConfig::particlePdg
  • ActsFatras::Synthetic::EventConfig::seed
  • ActsFatras::Synthetic::EventConfig::simulation
  • ActsFatras::Synthetic::EventSummary::primaries
  • ActsFatras::Synthetic::EventSummary::primaryHits
  • ActsFatras::Synthetic::EventSummary::secondaries
  • ActsFatras::Synthetic::EventSummary::secondaryHits
  • ActsFatras::Synthetic::EventSummary::seedablePrimaries
  • ActsFatras::Synthetic::EventSummary::spacePoints
  • ActsFatras::Synthetic::GeneratedParticle::charge
  • ActsFatras::Synthetic::GeneratedParticle::d0
  • ActsFatras::Synthetic::GeneratedParticle::eta
  • ActsFatras::Synthetic::GeneratedParticle::generation
  • ActsFatras::Synthetic::GeneratedParticle::numHits
  • ActsFatras::Synthetic::GeneratedParticle::phi
  • ActsFatras::Synthetic::GeneratedParticle::productionRadius
  • … and 65 more

@andiwand
andiwand force-pushed the feat-fatras-synthetic-generator branch 6 times, most recently from ef56322 to 820479f Compare August 6, 2026 13:16
`ActsFatras::Synthetic` is a second, much smaller fast simulation that shares
none of the Fatras machinery: no tracking geometry, no propagator, no input
data. It fills an `Acts::SpacePointContainer` directly and makes an ATLAS-like
pile-up of 200 in tens of milliseconds, which is what a seeding benchmark needs
and what the propagator-based simulation cannot provide. Space points carry the
layer and the particle they came from, so a caller needs no truth matching.

The geometry is cylinders at a fixed radius and discs at a fixed z as plain
structs, so a helix crosses every surface in closed form. The restriction is on
the shape alone: every cylinder has its own half-length and every disc its own
rings, so a real endcap is described as the staggered discs and rings it is.
Surfaces carry the material their detector's map reports, banded along the
coordinate they extend in, and that material is the whole of the physics model:
`x/X0` scatters and makes electrons, `x/L0` makes nuclear products, and the slab
takes the energy. Nothing is fitted on top of it.

Both material effects displace the *measured* position of a hit and leave the
trajectory a nominal helix -- a scatter by the angle times the lever arm, energy
loss by half the curvature change times that lever arm squared -- which is what
gives a seed the spread a seeder cuts on at a fraction of the cost of stepping.

It is deliberately coarse: a tool for throughput and combinatorics studies
rather than for physics performance. The Generic pixel description ships with
it; the descriptions of real detectors and the configurations fitted to them
follow.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CMdYgLmPKFybPzSr8wZxhd
@andiwand
andiwand force-pushed the feat-fatras-synthetic-generator branch from 820479f to a4f0dca Compare August 7, 2026 14:38
@sonarqubecloud

sonarqubecloud Bot commented Aug 7, 2026

Copy link
Copy Markdown

Quality Gate Failed Quality Gate failed

Failed conditions
C Security Rating on New Code (required ≥ A)
1 New Bugs (required ≤ 0)
B Reliability Rating on New Code (required ≥ A)

See analysis details on SonarQube Cloud

Catch issues before they fail your Quality Gate with our IDE extension SonarQube for IDE

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Component - Documentation Affects the documentation Component - Fatras Affects the Fatras module Public API

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant