Skip to content

Simple sim addition#268

Draft
anthoak13 wants to merge 18 commits intoATTPC:developfrom
anthoak13:SimpleSimAddition
Draft

Simple sim addition#268
anthoak13 wants to merge 18 commits intoATTPC:developfrom
anthoak13:SimpleSimAddition

Conversation

@anthoak13
Copy link
Copy Markdown
Member

No description provided.

anthoak13 and others added 17 commits April 12, 2026 16:16
Port AtPropagator (Lorentz force + RK4/adaptive stepping) and
AtKinematics helpers from OpenKF-Impl into AtTools.

Extend AtSimpleSimulation with SetElectricField/SetMagneticField;
when fields are non-zero the propagator replaces the straight-line
loop. Zero-field fast path is preserved.

Add AtSimParticleCollector, a minimal FairGenericStack stub that
captures FairPrimaryGenerator output without Geant4. Wire it into
AtTestSimulation via SetPrimaryGenerator() so any existing generator
can drive the standalone simulation unchanged.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Wire AtPropagator into AtSimpleSimulation: when fEField or fBField is
  non-zero, use RK4 adaptive stepping (Lorentz force) instead of the
  straight-line fast path.  Both fields default to zero, so existing
  callers are unaffected.
- Add SetMagneticField / SetElectricField / SetMaxPropagationStep API.
- Fix AtRK4AdaptiveStepper: clamp hNew to [fMinStep, fMaxStep] on the
  ACCEPT branch (previously only clamped on reject, allowing unbounded
  step growth with zero energy loss).
- Add two GTest physics tests (AtSimTest.cxx / AtDigitizationTests):
    ZeroFieldStraightLine  — proton stops at expected range, X/Y = 0
    MagneticFieldLarmorRadius — curved path matches r = p⊥/(qB) ±5 %
- Add simpleSim_Bfield.C macro: end-to-end FairRunAna example with 2 T
  solenoid field and AtTPCIonGenerator proton beam.
- Add compareSimVsGeant.C macro: overlays SimpleSim vs Geant4 MCPoints
  on Bragg curve, track-length, total eLoss, XY-map, and Z distributions.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Remove all framework #include directives from simpleSim_Bfield.C and
  compareSimVsGeant.C; Cling autoloads these via rootmap and explicit
  includes cause FairMCPoint incomplete-type errors.
- Replace AtTPCIonGenerator with FairBoxGenerator: AtTPCIonGenerator
  requires FairRunSim in its constructor and cannot be used with
  FairRunAna.
- Fix beam start position to (0, 6.079, 1) cm — inside the drift_volume
  cylinder (r=25 cm, z=0–100 cm, y-offset 6.079 cm in ATTPC_He1bar).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
simpleSim_Bfield.C:
- Reduce proton energy from 50 MeV to 1 MeV (p=43.33 MeV/c).
  HinH.txt covers 0–11 MeV; a 50 MeV proton is outside the table
  range and produces eLoss=0 throughout.  A 1 MeV proton has range
  ~200 mm and stops cleanly inside the 1000 mm drift volume.

compareSimVsGeant.C:
- Replace cumulative-GetLength()-difference step size with 3-D
  position difference between consecutive hits.  GetLength() is the
  cumulative track length from the particle origin, which for Geant4
  tracks includes path outside the active volume; the old approach
  gave a spuriously large first-step dEdx.
- Fix histogram ranges (Z: 0–1100 mm, eLoss: 0–15 MeV) and add
  guard against zero-length / zero-loss steps in the Bragg profile.
- Skip missing files gracefully instead of crashing.
- Drop spurious normalisation argument (n) that was unused.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Refactor AtTpc around a transport-neutral step payload and route both VMC and the SimpleSim adapter through the same detector-owned hit and reaction logic. Keep the VMC entrypoint intact while moving reaction triggering, vertex handoff, punch-through reset, and non-beam metadata lookup under the shared path.

Extend AtSimpleSimulation with callback-based transport reporting and update AtTestSimulation to use a detector-coupled adapter. Reuse the canonical MCTrack branch, preserve generator-assigned track IDs, and carry explicit beam-phase information into detector stepping so reaction-event track 0 keeps the Geant truth contract without tripping beam-only handoff logic.

Add focused regression coverage for AtTpc reaction triggering, reaction-event track-0 handling, AtVertexPropagator state writes/resets, and the SimpleSim callback path. Align the local validation macros and comparison tooling to the 16C+p campaign, restore visualizer-readable MC truth, and document the verified detector-contract state plus the remaining Z-parity investigation.
Add an explicit detector-entry submission path in AtTestSimulation so the SimpleSim adapter emits a zero-length, zero-loss AtTpc point at the actual sensitive-volume start state before the first propagated step. This restores the detector-side contract for tracks that start inside the active volume and removes the previous one-step Z offset in the first recorded point.

Keep the transport and detector logic otherwise unchanged: the reaction handoff still fires at the same detector-side threshold, while beam and reaction products now begin with Geant-style entry points at z~=1 mm instead of one full step downstream. The remaining difference versus Geant is reduced to the sub-micron boundary bookkeeping level rather than a full transport-step mismatch.

Add regression coverage for the new entry-point behavior in AtSimTest and verify the updated kinematic validation path still renders in visualizeKinematic.C. On the current 2-event spot check the visualizer reports 'Visualized track 2 (recoil proton)' and 'Drew 1 trajectories and 1 event points'.
Document the validated migration path from Geant-style macros to AtTestSimulation-backed SimpleSim transport in the AtSimValidation area.

Make the detector stop transport when tracks exit the active reaction volume so Geant and SimpleSim follow the same wall-bounded physics assumption for this geometry.

Add detector coverage for the active-volume exit stop and keep the existing shared detector-step contract exercised through tests.

Update the fixed and kinematic comparison macros to truth-match by reaction index, keep zero-point events in the bookkeeping, and report usable, generator-only, and incomplete event counts separately.

This commit captures the current working state of the SimpleSim migration effort, including the previously validated adapter, propagator, and generator-side changes already present in the tree.
Refocus the AtSimValidation migration note on what a user needs to edit in a normal simulation macro when swapping Geant transport for SimpleSim.

Remove the implementation-history framing and the validation-macro-specific assumptions, and present the migration as a transport-hookup change for existing user macros with inline generator setup.
- Fix beam/reaction flag inversion in generator task by capturing
  IsBeamEvent() before GenerateEvent() toggles it
- Guard ProcessStep exit-stop with fStopOnReactionVolumeExit flag so
  Geant4 path behavior is preserved (defaults off)
- Replace 15+ raw unit conversion factors with named constants
- Unify sensitive volume logic via AtTpc::IsSensitiveVolume()
- Remove vestigial AtTestSimulation class and #define private hack
- Defer geometry check in AtSimpleSimulation default constructor so
  it can use FairRunSim's gGeoManager
- Escalate missing energy-loss model from LOG(debug) to LOG(fatal)
- Add SimpleSim migration guide and update simulation pipeline docs

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
New macros demonstrate drop-in SimpleSim replacement of Geant4 transport
using AtELossModelFactory. Each starts from the corresponding Geant4 macro
with minimal changes: dummy generator, factory setup, and task creation.

Updated energy-loss, simplesim-migration, simulation-pipeline, integration
review, and modules docs to cover the factory pattern.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Introduce AtELossModelFactory (abstract), AtELossFactoryBetheBloch, and
AtELossFactoryCATIMA to auto-create energy loss models from ROOT geometry
materials at transport time. This eliminates the need to manually register
a model for every particle species before simulation.

Consolidate the duplicate SimulateParticle/TransportParticle code paths
into a single PropagateParticle method, removing ~100 lines of duplicated
transport logic. The standalone SimulateParticle API now delegates to
PropagateParticle with a volume-boundary callback.

Require detector coupling in AtSimpleSimulationTask (standalone hit
writing removed from the task; direct callers still use SimulateParticle).
Add automatic B-field extraction from FairRun for drop-in Geant4
replacement. Update integration review docs and CLAUDE.md.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…eview issues

Split AtSimpleSimulation into a pure transport engine (callback-based API) and
AtStandaloneSimulation (hit-recording wrapper for MCFitter). Integrate transport
with AtTpc::ProcessStep via AtSimpleSimulationTask, which translates SimpleSim
TransportSteps into the detector's StepState contract.

Review fixes:
- Cache TGeoVolume* from loop conditions to eliminate redundant FindNode calls
- Unify stop tolerance (fStopTol) and max-step limit (fMaxTransportSteps) across
  both curved and straight-line transport paths
- Add max-step safety valve to straight-line loop
- Document ProcessStep enter/exit/accumulate contract in AtTpc.h
- Remove redundant GetVolumeNameAt calls and duplicated comment in task
- Clarify navigator abandonment on gGeoManager change
- Make AtSimParticleCollector stubs LOG(fatal) instead of silently returning nullptr

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown
Member Author

@anthoak13 anthoak13 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add initial comments on the core AtSimulation classes

Comment thread AtDetectors/AtTpc/AtTpc.cxx Outdated

// Position of the first hit of the beam in the TPC volume ( For tracking purposes in the TPC)
if (fTrackID == 0 && (fVolName == "drift_volume" || fVolName == "cell"))
if (fIsBeamTrack && IsReactionVolume(fVolName))
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why has this changed from the existing convention? It might break other code to not enforce beam track means track ID is 0.

Comment thread AtDetectors/AtTpc/AtTpc.cxx Outdated
Comment on lines +65 to +72
auto AZ = DecodePdG(gMC->TrackPid());
auto AZ = DecodePdG(step.pdg);
fELoss = 0.;
fELossAcc = 0.;
fTime = gMC->TrackTime() * 1.0e09;
fLength = gMC->TrackLength();
gMC->TrackPosition(fPosIn);
gMC->TrackMomentum(fMomIn);
fTrackID = gMC->GetStack()->GetCurrentTrackNumber();
fTime = step.timeNs;
fLength = step.trackLength;
fPosIn = step.pos;
fMomIn = step.mom;
fTrackID = step.trackID;
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Repeats contents of getTrackPrameters function?

Comment on lines -122 to 123
// Correct fPosOut
if (gMC->IsTrackExiting()) {
correctPosOut();
if ((fVolName.Contains("drift_volume") || fVolName.Contains("cell")) && fTrackID == 0)
resetVertex();
}
fTrackID = step.trackID;
fPosOut = step.posOut;
fMomOut = step.momOut;

if (step.exiting && IsReactionVolume(fVolName) && fIsBeamTrack)
resetVertex();
}
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is the correctPosOut code no longer being run?

Comment thread AtDetectors/AtTpc/AtTpc.cxx Outdated
bool isPrimaryBeam = fTrackID == 0;
bool isInRightVolume = fVolName.Contains("drift_volume") || fVolName.Contains("cell");
bool isPrimaryBeam = fIsBeamTrack;
bool isInRightVolume = IsReactionVolume(fVolName);
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IsReactionVolume is a bad name. The cell is not a reaction volume

getTrackParametersFromMC();

if (gMC->IsTrackExiting() || gMC->IsTrackStop() || gMC->IsTrackDisappeared())
getTrackParametersWhileExiting();
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this function called anywhere anymore?

Comment thread AtDigitization/AtSimpleSimulation.cxx Outdated
Comment on lines +204 to +211
// For non-uniform fields, re-query the field at the current position before each RK4 step.
// Within a single step, the field is treated as uniform (piecewise-constant approximation).
if (fFieldFunc) {
auto [eField, bField] = fFieldFunc(prop.GetPosition());
prop.SetEField(eField);
prop.SetBField(bField);
}

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems repetative?

Comment thread AtDigitization/AtSimpleSimulation.cxx Outdated
Comment on lines +216 to +221
if (std::isnan(posBefore.X()) || std::isnan(prop.GetMomentum().X())) {
LOG(error) << "Failed to propagate a point with nan!";
return {{0, 0, 0}, {0, 0, 0, 0}};
}

prop.PropagateOneStep(stepper);
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I feel like we are re-implementing the propagator here. Which seems like a poor design choice

Comment thread AtDigitization/AtSimpleSimulation.cxx Outdated
Comment on lines +227 to +248
auto posAfter = prop.GetPosition();
auto momAfter = AtTools::Kinematics::Get4Vector(prop.GetMomentum(), info.mass);
double KE_after = AtTools::Kinematics::KE(prop.GetMomentum(), info.mass);
double eLoss = KE - KE_after;
if (eLoss < 0)
eLoss = 0; // magnetic field does no work

double stepDist = (posAfter - state.fLastPos).R(); // mm
if (stepDist <= minAcceptedStepMm || state.hUsed <= stepper.fMinStep * kMinStepGuardScale) {
if (++minStepSteps > kMaxMinStepCurvedSteps) {
LOG(warning) << "Aborting curved SimpleSim track after " << minStepSteps
<< " minimum-size steps at position " << posAfter << " with KE " << KE_after
<< " MeV for PDG " << pdg;
break;
}
} else {
minStepSteps = 0;
}
length += stepDist;

if (callback) {
TransportStep step;
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is all very messy. And I imagine there is a much better way to handle this mess of a function

Comment thread AtDigitization/AtSimpleSimulation.cxx Outdated
Comment on lines +276 to +284
int numSteps = 0;

TGeoVolume *curVol = nullptr;
while ((curVol = GetVolume(pos)) != nullptr) {
if (++numSteps > fMaxTransportSteps) {
LOG(warning) << "Aborting straight-line SimpleSim track after " << numSteps
<< " steps without leaving the geometry";
break;
}
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not convinced you haven't changed the behavior here from the original code

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here being this entire function. Revert most of it to the initial form and only make small targeted changed required. Do not make stylistic changes which obfuscate logic changes

Comment thread AtDigitization/AtSimpleSimulation.cxx Outdated
Comment on lines 309 to 324
if (callback) {
TransportStep step;
step.pdg = pdg;
step.preVolumeName = preVolumeName;
step.postVolumeName = GetVolumeName(pos);
step.energyLoss = eLoss;
step.length = length;
step.trackMass = info.mass;
step.prePosition = posBefore;
step.postPosition = pos;
step.preMomentum = momBefore;
step.postMomentum = mom;
if (!callback(step))
break;
}
}
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Repetative

Resolves the ~39 inline review comments on PR ATTPC#268.

AtTpc: drop fIsBeamTrack and fStopOnReactionVolumeExit; restore
fTrackID == 0 as the beam predicate; rename IsReactionVolume to
IsActiveGasVolume; delete unused AddHit overload and the redundant
IsSensitiveVolume static; move correctPosOut back into
getTrackParametersWhileExiting; demote CheckIfSensitive log to debug.

AtTPC2Body: revert the beam-axis normalization so the file matches
develop.

AtSimParticleCollector: rewrite on top of FairGenericStack using
TParticle directly so parent ID, time, polarization and weight are
preserved instead of thrown away.

Energy-loss rework: replace AtELossModelFactory hierarchy with
AtELossManager that both accepts pre-built models and, in subclasses
(AtELossManagerBetheBloch, AtELossManagerCATIMA), auto-generates them.
Two AddModel overloads support material-agnostic registration (for
MCFission/AtMCFitter) and material-specific registration. Transport
queries the manager on each volume change so different materials get
different models. Delete the unused legacy AtELossManager lookup class.

Transport-engine / standalone-class rename: the callback-based engine
is AtSimTransport; the standalone hit-recording class is AtSimpleSimulation
(formerly AtStandaloneSimulation). FairRoot task classes rename to
AtSimTransportTask / GeneratorTask / ReplayTask. AtSimpleSimulation
carries thin forwarders so pre-refactor macros compile unchanged.
PropagateParticle is split into PropagateCurved + PropagateStraightLine
with a shared BuildStep helper; the default stop tolerance is restored
to 1e-3 MeV and stuck-step abort constants become settable members.

Docs updated to match. Tests green (90/90).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant