Skip to content

Add multithreading support to ddsim#1240

Open
wdconinc wants to merge 52 commits into
AIDASoft:masterfrom
wdconinc:ddsim-mt
Open

Add multithreading support to ddsim#1240
wdconinc wants to merge 52 commits into
AIDASoft:masterfrom
wdconinc:ddsim-mt

Conversation

@wdconinc

@wdconinc wdconinc commented Mar 11, 2024

Copy link
Copy Markdown
Contributor

This PR adds multithreading support to ddsim, through the -j flag.

Backwards-incompatible effects (as the corner cases that they are):

  • Because some of the actions have to be place into __setupWorker, this means they may not be available in the same context as global actions. This means that during user physics list initialization one cannot query particle handlers which have not been created yet.

BEGINRELEASENOTES

  • Multithreading support in ddsim

ENDRELEASENOTES

@github-actions

github-actions Bot commented Mar 11, 2024

Copy link
Copy Markdown

Test Results

   18 files     18 suites   10h 7m 13s ⏱️
  380 tests   380 ✅ 0 💤 0 ❌
3 310 runs  3 310 ✅ 0 💤 0 ❌

Results for commit dcfd47d.

♻️ This comment has been updated with latest results.

Comment thread DDG4/python/DDSim/DD4hepSimulation.py
Comment thread DDG4/python/DDSim/DD4hepSimulation.py Outdated
@wdconinc

Copy link
Copy Markdown
Contributor Author

This is still a work in progress, but let me give some more background here as to the plan (the "more later" from the top).

  • The intention is, of course, full backwards compatibility so this runs in the single-threaded G4RunManager with command line options as given now (and therefore must pass all tests unchanged).
  • This PR already works in multi-threaded mode for generators like the gun, but
    • Not for external (hepmc) input files: I would like to consider this beyond the scope of this PR since this will require some changes to the input file reader to make sure it does not read the same sequence of events in each file.
    • Adapting to MT has caused single-threaded running to break: As written above, this needs to get fixed, of course.
  • There may be changes to the output files that are due to event reordering. These are likely unavoidable.

It is likely that I will be doing some rewrites of the last commit (5bf711c) which is a bit too big for my taste and not as clear as one would like for non-squash merging.

@wdconinc

Copy link
Copy Markdown
Contributor Author

The status of this PR after #1417 is that we can now run MT gun simulations, like so:

ddsim --compactFile DDDetectors/compact/SiD.xml -j8 -G -N160 --gun.distribution uniform --outputFile SiD.edm4hep.root

Main missing feature is ensuring inputFiles is serialized. Right now this just reads the same event sequence for all threads.

Comment thread DDG4/python/DDSim/DD4hepSimulation.py
Comment thread DDG4/python/DDSim/DD4hepSimulation.py Outdated
@wdconinc

Copy link
Copy Markdown
Contributor Author

The primary remaining difficulty I'm having here is with the event input files (I'm using HepMC3), which we create in:

gen = DDG4.GeneratorAction(kernel, "Geant4InputAction/hepmc%d" % index)
gen.Parameters = self.hepmc3.getParameters()
gen.Input = "HEPMC3FileReader|" + inputFile

Simply making the GeneratorAction shared with DDG4.GeneratorAction(kernel, "Geant4InputAction/hepmc%d" % index, shared=True) fails because the properties (Parameters, Input) that are subsequently set are properties of the Geant4InputAction and not exposed to the GeneratorAction handle gen.

To get around this, I went through setting up the necessary DDG4.InputAction support (duplicating what I felt was too much stuff along the way), and it still ended up not working.

So, I'm open to input as to how best to have a single Geant4InputAction where we can hand-out events sequentially while locking access with G4AutoLock in Geant4InputAction::create_reader and Geant4InputAction::operator().

@MarkusFrankATcernch @andresailer Do you have any suggestions?

@MarkusFrankATcernch

Copy link
Copy Markdown
Contributor

Hi Wouter,

This construct is not simple. Let's see what is necessary before the hacking starts...

For Geant4 the GeneratorAction belongs to the worker thread's execution flow, not the main thread.
This idea is also reflected in DDG4. If you look at DD4hep/blob/master/DDG4/examples/SiDSim_MT.py you can clearly see this.

Now what one probably wants is somehow a structure that this worker thread bound GeneratorAction is sharable between all workers (share same input file, share common parametrization etc) and use this action from all threads. Secondly the underlying reader implementation should be accessible by the configuration (python with ddg4, ddsim, etc) to allow more fine grained customization than just setting some input file and defining a factory.

From what I see somehow the input stage needs then to be re-designed. In particular exchanging data between the actions building the queue of GeneratorActions which define the input stage, cannot use the Geant4Event and its extension mechanism to pass these data. The Geant4Event is bound to the worker thread via the Geant4Context.

Having said all this, it looks necessary that the queue of GeneratorActions needs to be cut in two:

  • the first part being locked and single threaded doing the file reading (multiple inputs, spillover, multiple collisions per crossing, etc) and the data preparation with user exposure of the queue elements before execution starts.
  • the second part is not locked and can then finalize the event generation (smearing, primary vertex movements, etc.) and passes all the initial elements to the Geant4Event, which is then passed to Geant4 for the actual simulation work.

How to do something like this in a backwards compatible way is not so obvious to me and should be designed properly.
As I use to say: software is never difficult or complicated unless there is a lack of thinking upfront.
Maybe we should first collect all the goodies we want to see in such an implementation. The parameter setup you mention here is already a first step.

@MarkusFrankATcernch

Copy link
Copy Markdown
Contributor

So after coming back from the drawing board....
What you effectively want is a transition from the following object model as it is implemented now:

Geant4-Input

to an input stage which looks much rather like this (details are left out here):

Geant4-MT

Here you have

  • a single threaded simple input stage filling the Geant4Event structure. The Geant4Events must be requested by the initializing action (Geant4GenerationInit) from the Geant4EventQueue object to limit the number of queued events (memory, etc). This compromises the first actions from the above model.
  • these Geant4Event entities are then pushed into a queue from which these Geant4Events are then requested by the multi-threaded simulation.
  • the multi-thread action sequence then finalizes the primaruy generation and the rest continues as shown here: DD4hep/blob/master/DDG4/examples/SiDSim_MT.py

In principle this is not too difficult, but still quite more difficult that you have imagined upfront.

In addition:

  • the Geant4InputAction objects in the queue need their properties be exposed to the user
  • the entire machinery must be embedded in ddsim. How this can be done elegantly has to be seen.

@wdconinc

Copy link
Copy Markdown
Contributor Author

While I agree that the diagram you outline (with an event queue) is optimal for throughput performance, I wonder if there is another less optimal but still functional approach (which may have some threads waiting unnecessarily for some of the time). What I had originally anticipated was a pattern where there are multiple Geant4InputActions sequences, one per thread as now, but the action that reads from disk would have a shared reader that is locked. This would mimic the approach of the Geant4OutputAction where each thread has an output event action, but they share and lock the output file.

@MarkusFrankATcernch

Copy link
Copy Markdown
Contributor

@wdconinc
We should under no circumstances deviate too far from the processing model Geant4 uses.
Geant4 expects the G4GeneratorAction to be part of the worker thread and each worker thread is only provided with a unique seed for the random generator for this event. That's it.

Where one actually puts the Geant4EventQueue is sort of user-gusto here, but it must be located before the
G4Primary particles and vertices are created. If you want to work with input files a la HepMC, you have to go the complicated way I am afraid. Otherwise you would need one input per thread.

If we invest here, we should do it properly....even if it costs some development.

@wdconinc

wdconinc commented Apr 2, 2025

Copy link
Copy Markdown
Contributor Author

In addition:

  • the Geant4InputAction objects in the queue need their properties be exposed to the user

See #1440, which exposes the properties correctly now.

@murnanedaniel

Copy link
Copy Markdown

@wdconinc @MarkusFrankATcernch Hi Wouter and Markus, what is the status of including multithreading into ddsim with HEPMC3 file inputs? I am working on a large simulation, and having MT could potentially give a nice boost in throughput. If there are concrete places that I can contribute, please let me know. If the exposure of properties is handled, is this "simply" a matter of having ddsim handle file reads and writes correctly? Or are there more fundamental components to update?

Again, keen to help where it makes sense!

@wdconinc

Copy link
Copy Markdown
Contributor Author

@murnanedaniel Depending on your use case, this already works fine (multi-threaded).

  • It works with DD4hep particle gun, input files (hepmc3, haven't tried evtgen and edm4hep inputs but should also work).
  • It doesn't work with GPS and Geant4 particle gun since the setup macro is only passed to one thread.

I have been using this for a month or two (hepmc3 input files) typically 8 threads and it scales without loss of efficiency at that (small) thread count.

@MarkusFrankATcernch

Copy link
Copy Markdown
Contributor

@wdconinc @murnanedaniel
So in other words, only the raw DDG4 python interface can currently be used to run multi-threaded.
Correct Wouter ?

@wdconinc

Copy link
Copy Markdown
Contributor Author

@wdconinc @murnanedaniel
So in other words, only the raw DDG4 python interface can currently be used to run multi-threaded.
Correct Wouter ?

The scope of this PR has always been to make multi-threaded running work with ddsim. I don't know what other ways of running DDG4 people are using, but I'm not familiar with them.

@murnanedaniel

Copy link
Copy Markdown

Hi @wdconinc. Thanks for the quick response. I have been trying to reproduce your working setup with input hepmc3 files, but get this error, when setting to 8 threads:

=======================================================================
Geant4Kernel     INFO  +++ Created worker instance id=1046861376l
UserInitialization INFO  +++ Executing Geant4UserActionInitialization::Build. Context:0x7f9b303af6a0 Kernel:0x7f9b303afb30 [0]
PyG4Init         INFO  +++ Worker:0 Build PYTHON Worker [empty]....
Geant4Kernel     INFO  ++ Registered global action RunInit of type dd4hep::sim::Test::Geant4TestRunAction
Geant4UI         INFO  +++ RunAction> Install Geant4 control directory:/ddg4.0/RunAction/
Traceback (most recent call last):
  File "/global/cfs/cdirs/m4958/usr/danieltm/ColliderML/software/OtherLibraries/dd4hep-mt/lib/python3.11/site-packages/DDSim/DD4hepSimulation.py", line 480, in __setupWorker
    self.__setupGeneratorActions(kernel, geant4)
  File "/global/cfs/cdirs/m4958/usr/danieltm/ColliderML/software/OtherLibraries/dd4hep-mt/lib/python3.11/site-packages/DDSim/DD4hepSimulation.py", line 339, in __setupGeneratorActions
    shared = (kernel.RunManagerType == "G4MTRunManager")
              ^^^^^^^^^^^^^^^^^^^^^
  File "/global/cfs/cdirs/m4958/usr/danieltm/ColliderML/software/OtherLibraries/dd4hep-mt/lib/python3.11/site-packages/DDG4.py", line 161, in _getKernelProperty
    elif hasattr(self, name):
         ^^^^^^^^^^^^^^^^^^^
  File "/global/cfs/cdirs/m4958/usr/danieltm/ColliderML/software/OtherLibraries/dd4hep-mt/lib/python3.11/site-packages/DDG4.py", line 161, in _getKernelProperty
    elif hasattr(self, name):
         ^^^^^^^^^^^^^^^^^^^
  File "/global/cfs/cdirs/m4958/usr/danieltm/ColliderML/software/OtherLibraries/dd4hep-mt/lib/python3.11/site-packages/DDG4.py", line 161, in _getKernelProperty
    elif hasattr(self, name):
         ^^^^^^^^^^^^^^^^^^^
  [Previous line repeated 995 more times]
RecursionError: maximum recursion depth exceeded
TypeError: 'NoneType' object cannot be interpreted as an integer
PyG4Init         FATAL +++ Worker setup command returned -1, not SUCCESS (1). Terminating setup

**************************************************** 
*  A runtime error has occured :                     
*    +++ Worker setup command returned -1, not SUCCESS (1). Terminating setup
*  the program will have to be terminated - sorry.   
**************************************************** 

I removed the elif python handler test that was causing the recursion, leading to (I believe) the real error, which is this:

=======================================================================
Geant4Kernel     INFO  +++ Created worker instance id=16168512l
UserInitialization INFO  +++ Executing Geant4UserActionInitialization::Build. Context:0x7f01f43af6a0 Kernel:0x7f01f43afb30 [0]
PyG4Init         INFO  +++ Worker:0 Build PYTHON Worker [empty]....
Geant4Kernel     INFO  ++ Registered global action RunInit of type dd4hep::sim::Test::Geant4TestRunAction
Geant4UI         INFO  +++ RunAction> Install Geant4 control directory:/ddg4.0/RunAction/
Traceback (most recent call last):
  File "/global/cfs/cdirs/m4958/usr/danieltm/ColliderML/software/OtherLibraries/dd4hep-mt/lib/python3.11/site-packages/DDSim/DD4hepSimulation.py", line 480, in __setupWorker
    self.__setupGeneratorActions(kernel, geant4)
  File "/global/cfs/cdirs/m4958/usr/danieltm/ColliderML/software/OtherLibraries/dd4hep-mt/lib/python3.11/site-packages/DDSim/DD4hepSimulation.py", line 339, in __setupGeneratorActions
    shared = (kernel.RunManagerType == "G4MTRunManager")
              ^^^^^^^^^^^^^^^^^^^^^
  File "/global/cfs/cdirs/m4958/usr/danieltm/ColliderML/software/OtherLibraries/dd4hep-mt/lib/python3.11/site-packages/DDG4.py", line 162, in _getKernelProperty
    raise KeyError(msg)
KeyError: 'Geant4Kernel::GetProperty [Unhandled]: Cannot access Kernel.RunManagerType'
TypeError: 'NoneType' object cannot be interpreted as an integer
PyG4Init         FATAL +++ Worker setup command returned -1, not SUCCESS (1). Terminating setup

**************************************************** 
*  A runtime error has occured :                     
*    +++ Worker setup command returned -1, not SUCCESS (1). Terminating setup
*  the program will have to be terminated - sorry.   
**************************************************** 

For context, here is the full output log: full_output.log
My environment should be perfectly vanilla - LCG 107, running on the OpenDataDetector, with Pythia-generated hepmc3 files. I will continue to debug, but hoped you might have a quick idea.

@murnanedaniel

Copy link
Copy Markdown

Okay, I made a couple of quick tweaks and it does seem to work, which is really promising. Firstly, the tweaks I made are here: murnanedaniel@cbbe86e. I'm not sure if you agree with them, or why they are not necessary in your setup?

Secondly, there is one final error appearing after the simulation that doesn't appear in the main DD4hep. You can see it at the end of the successful log file: full_output_mt.log
Any idea where this is coming from?

@murnanedaniel

murnanedaniel commented May 14, 2025

Copy link
Copy Markdown

@wdconinc Just following up here, since we will soon be running a large set of simulations with your MT changes. I just want to ensure that the small error that appears, and the tweaks that I needed to make, are still consistent with the working version of ddsim as you see it. On that same point - have you validated that MT sim of hepmc3 inputs produces more or less identical outputs to DD4hep main?

@BrieucF

BrieucF commented Nov 28, 2025

Copy link
Copy Markdown
Contributor

Is there any plan to have this integrated?

@MarkusFrankATcernch

Copy link
Copy Markdown
Contributor

@BrieucF Whenever Wouter gives hist OK.

@wdconinc

wdconinc commented Mar 4, 2026

Copy link
Copy Markdown
Contributor Author

I added what I have locally, which consists of mainly a number of new added tests that aim to compare single- and multi-threaded output. This requires a comparison script to allow for out-of-order event output (but still identical when comparing the event using an index map). There are some persistent differences between 2 and 4 threaded running. Those are still under investigation.

jmcarcell and others added 12 commits June 7, 2026 19:01
In MT mode, creating the EventSeeder with shared=True caused the
Geant4EventSeed to be instantiated on the master kernel, registering
beginEvent on the master's event action sequence rather than the
worker's.  Since no master-side G4UserEventAction is ever created,
the callback never fired and every MT event used the same random seed.

Fix by using shared=False so each worker gets its own Geant4EventSeed
with callbacks registered on the worker's event and stacking action
sequences.

Add a prepareEvent (stacking prepare) callback that re-seeds after
GeneratePrimaries.  File-based generators call event->SetEventID()
inside GeneratePrimaries to assign the sequential file position, which
differs from the Geant4-internal pre-assigned event ID seen by
beginEvent in multi-threaded mode.  Re-seeding after GeneratePrimaries
guarantees reproducible physics across different thread counts.
Rename tests to t_ddsim_{generator}_mt{N}[_mt{M}_compare] scheme.
Rename st to mt1 throughout.

Remove duplicate and redundant tests:
- t_ddsim_mt_basic_2/4threads and t_ddsim_mt_single_thread are merged
  into t_ddsim_gun_mt1/mt2/mt4 which now carry the PASS_REGULAR_EXPRESSION
  thread-count checks and serve as inputs to both comparison pairs.
- t_ddsim_mt2_vs_mt4_field_* removed: SiD already has a solenoid field
  so the regular gun tests exercise the same field path.
- t_ddsim_mt2_vs_mt4_gps_generate_* and _compare removed: they used
  the regular DD4hep gun (-G), not GPS, making them redundant with the
  gun comparison tests.

Add t_ddsim_gun_mt1_mt2_compare (single- vs multi-threaded gun).
The shared t_ddsim_gun_mt2 output is reused by both gun comparison tests.
Add t_ddsim_gps_mt2, t_ddsim_gps_mt4, t_ddsim_gps_mt2_mt4_compare,
parallel to the existing G4Gun comparison tests.  GPS batch tests use
--macroFile for consistency; particle configuration uses Geant4 defaults
since run_test.sh cannot pass macro-file contents in batch mode.

Raise /run/beamOn in g4gun_test.mac and g4gps_test.mac from 3 to 10
to match the minimum event count used in all other tests.
The EDM4hepFileReader plugin lives in libDDG4EDM4HEPReader.so, a
separate library from the output plugin libDDG4EDM4HEP.so.  In MT
mode the reader is created lazily inside Geant4InputAction::createReader()
which executes on a worker thread.  Loading a new shared library via
the GaudiPluginService from a worker thread can fail because ROOT's
internal state is not fully thread-safe at that point.

Explicitly load libDDG4EDM4HEPReader on the master thread before
PyDDG4.configure()/initialize() spawn the worker threads, so the
factory is already registered when workers request it.
…ribute

Two issues when comparing DD4hep-native ROOT output (used as fallback
when EDM4hep is not available):

1. Without loading libDDG4/libDDG4Plugins, ROOT has no dictionary for
   dd4hep::sim::Geant4Tracker::Hit etc. and cppyy cannot cast the
   vector<...Hit*> branch objects, crashing on every branch access.
   Fix: load both libraries before opening files.

2. DD4hep hit objects use 'energyDeposit' while the comparison chain
   only checked getEDep/EDep/getEnergy/energy, silently skipping energy
   comparison for all DD4hep-format hits.
   Fix: add 'energyDeposit' to the fallback chain.
When the run() method was refactored to inline the detector construction
setup (instead of calling self.geometry.constructGeometry()), two things
were lost:

1. The geometry debug/print flags from self.geometry (DebugMaterials,
   DebugElements, DebugVolumes, etc.) were not applied to the
   Geant4DetectorGeometryConstruction action.

2. The Geant4RegexSensitivesConstruction actions configured via
   SIM.geometry.regexSensitiveDetector were never registered, breaking
   the BoxOfStraws example and any detector using regex-matched sensitive
   volumes.

Both are restored by propagating geometry properties to the ConstructGeo
action and iterating over regexSensitiveDetector after ConstructSD.
@wdconinc

wdconinc commented Jun 8, 2026

Copy link
Copy Markdown
Contributor Author

The only remaining issue here is now the G4EventID that isn't a 'proper' branch but a direct leaf, and it fails in DDDigi. @jmcarcell Do you want to take a look?

wdconinc added 4 commits June 8, 2026 11:30
Scalar leaf branches (Int_t, Float_t etc.) have no ROOT class name and
are not expected by downstream readers such as DDDigi's DigiROOTInput,
which iterates the EVENT tree's object branches to convert hit collections.
Writing G4EventID as a scalar branch in EVENT caused 'Unknown data type'
FATAL errors in DDDigi.

Fix by buffering G4EventIDs in commit() and writing them as a separate
TTree 'G4EventIDs' (one entry per event) at file-close time.  DDDigi
opens only the 'EVENT' tree by name and is unaffected.  The comparison
script reads G4EventID from the companion tree for event-ID-based
matching in MT mode; EDM4hep files continue to use EventHeader as before.

Also clear m_sections fully at file close to prevent dangling TTree*
pointers to trees owned by the (now closed) TFile.
The G4EventIDs TTree filled in commit() was never flushed to disk
because m_sections was cleared before any Write() calls on it.
TFile::Close() does not auto-flush TTrees; only m_tree->Write() was
called explicitly, leaving G4EventIDs in memory only.

Iterate m_sections (after erasing the main EVENT entry) and call
Write() on each companion tree before clearing the map.
@wdconinc

wdconinc commented Jun 8, 2026

Copy link
Copy Markdown
Contributor Author

   18 files     18 suites   12h 14m 8s ⏱️
  380 tests   380 ✅ 0 💤 0 ❌
3 310 runs  3 310 ✅ 0 💤 0 ❌

Passes all checks. Zarro boogs!

@wdconinc

wdconinc commented Jun 8, 2026

Copy link
Copy Markdown
Contributor Author

Now, let's see what copilot thinks about all this...

@wdconinc wdconinc marked this pull request as ready for review June 8, 2026 20:26
Copilot AI review requested due to automatic review settings June 8, 2026 20:26

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Note

Copilot was unable to run its full agentic suite in this review.

Adds multi-threaded (MT) simulation validation by running ddsim with multiple thread counts and comparing outputs for physics-equivalence, alongside core MT determinism and ROOT I/O safety improvements.

Changes:

  • Add CTest coverage for MT runs (gun, HepMC3, EDM4hep, G4Gun, GPS) plus file-to-file output comparisons.
  • Introduce a ROOT-output comparison utility script and Geant4 macro inputs for G4Gun/GPS tests.
  • Improve MT reproducibility and stability via per-thread random engines, event reseeding updates, ROOT I/O locking, and exposing PyDDG4 step-wise lifecycle calls.

Reviewed changes

Copilot reviewed 17 out of 17 changed files in this pull request and generated 8 comments.

Show a summary per file
File Description
DDTest/python/compare_simulation_output.py New Python comparator to validate ST vs MT output equivalence (branches, hits, particle properties).
DDTest/inputFiles/g4gun_test.mac Adds a simple Geant4 Gun macro for MT/ST macroFile testing.
DDTest/inputFiles/g4gps_test.mac Adds a simple Geant4 GPS macro for MT/ST macroFile testing.
DDTest/CMakeLists.txt Adds many new MT CTest invocations and comparator-based validations.
DDG4/src/python/PyDDG4.cpp Splits lifecycle into configure/initialize/run/terminate and renames legacy “all phases” runner to runAll.
DDG4/src/Geant4Random.cpp Makes random instance thread-local and routes calls through a per-instance ROOT RNG member.
DDG4/src/Geant4Output2ROOT.cpp Adds ROOT I/O mutexing, endRun-based closing, and a companion G4EventIDs tree for MT ordering.
DDG4/src/Geant4Kernel.cpp Declares RunManagerType as a property for worker kernels.
DDG4/python/DDSim/Helper/Random.py Adds per-worker random initialization and defers/duplicates EventSeeder per worker.
DDG4/python/DDSim/Helper/Geometry.py Refactors geometry construction to only build geometry/regex SD steps (sensitives attached elsewhere).
DDG4/python/DDSim/DD4hepSimulation.py Adds -j/--numberOfThreads, MT setup hooks, adjusted generator sharing, and step-wise PyDDG4 lifecycle usage.
DDG4/python/DDG4.py Avoids shared output actions in ST and switches Geant4.run() to PyDDG4.runAll().
DDG4/plugins/Geant4EventSeed.h Adds prepare-stacking hook to reseed after GeneratePrimaries with final event ID.
DDG4/plugins/Geant4EventSeed.cpp Implements reseeding in prepareEvent via G4EventManager current event.
DDG4/include/DDG4/Python/PyDDG4.h Adds runAll/configure/initialize/terminate entry points.
DDG4/include/DDG4/Geant4Output2ROOT.h Adds mutex + atomic endRun counter and overrides endRun.
DDG4/include/DDG4/Geant4Kernel.h Exposes numThreads() accessor for kernel thread count.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines 68 to 70
int PyDDG4::execute() {
Kernel& k = Kernel::instance(dd4hep::Detector::getInstance());
return run(k);

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

This is intentional since we need to be able to call the phases in isolation, not only all together (with the new runAll which is the previous behavior.

Comment thread DDG4/python/DDSim/DD4hepSimulation.py Outdated
Comment thread DDG4/python/DDSim/DD4hepSimulation.py
Comment thread DDTest/CMakeLists.txt
Comment thread DDG4/src/Geant4Output2ROOT.cpp Outdated
Comment thread DDG4/src/Geant4Output2ROOT.cpp
Comment thread DDTest/python/compare_simulation_output.py
Comment thread DDTest/python/compare_simulation_output.py Outdated
wdconinc added 5 commits June 8, 2026 16:23
'is not []' is an identity comparison that is always True regardless of
whether the list is empty, since it compares against a freshly allocated
list object.  Use bare truthiness checks instead.
The inlined close body in beginRun() was identical to closeOutput() but
couldn't call it due to the mutex already being held.  Extract the core
logic into a private closeOutputLocked() (called with s_rootMutex held)
and have both closeOutput() and beginRun() use it.
Resetting the counter inside endRun() after closeOutput() leaves a
window between close and reset where a new run could theoretically start
and see stale counts.  Reset in beginRun() under the mutex, before any
events are processed for the new run, which is the natural boundary.
compare_vectors() silently returned True when neither (x,y,z) nor
(X,Y,Z) coordinate attributes were found, potentially masking real
mismatches for future hit types.  Print a WARNING instead.

Also check the return value of gSystem.Load(): a return value < 0
indicates failure.  Emit a warning so the user knows DD4hep-native
branches may not be readable, rather than hitting confusing errors later.
@wdconinc

wdconinc commented Jun 9, 2026

Copy link
Copy Markdown
Contributor Author

Now that the tests run multi-threaded, the issues like https://github.com/AIDASoft/DD4hep/actions/runs/27167948215/job/80199881284?pr=1240#step:4:6627 will pop up for older LCG releases due to Geant4's ongoing efforts at removing thread unsafe code (e.g. multiple threads writing simultaneously to a single static cache as in e.g. G4EmSaturation, or the ones listed in wdconinc/geant4#43 which still apply to 11.4.1).

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.

7 participants