Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-05-14 07:57:12

0001 import os
0002 import multiprocessing
0003 from pathlib import Path
0004 import math
0005 import tempfile
0006 import shutil
0007 import pytest
0008 
0009 from helpers import (
0010     edm4hepEnabled,
0011     podioEnabled,
0012     AssertCollectionExistsAlg,
0013 )
0014 
0015 import acts
0016 from acts import UnitConstants as u
0017 
0018 from acts.examples import (
0019     Sequencer,
0020     GenericDetector,
0021 )
0022 
0023 from acts.examples.odd import getOpenDataDetector, getOpenDataDetectorDirectory
0024 
0025 
0026 def assert_podio(
0027     target_file: Path,
0028     category: str,
0029     collections: set[str] | None = None,
0030     nevents: int | None = None,
0031 ):
0032     assert target_file.exists(), f"File {target_file} does not exist"
0033     from podio.root_io import Reader
0034     import cppyy
0035 
0036     reader = Reader(str(target_file))
0037     assert (
0038         category in reader.categories
0039     ), f"Category {category} not found in {target_file} ({reader.categories})"
0040 
0041     if nevents is not None:
0042         assert (
0043             len(reader.get(category)) == nevents
0044         ), f"Expected {nevents} events in {target_file} ({category}) but got {len(reader.get(category))}"
0045 
0046     if collections is not None:
0047         for frame in reader.get(category):
0048             assert (
0049                 set(frame.getAvailableCollections()) == collections
0050             ), f"Expected collections {collections} in {target_file} ({category}) but got {frame.getAvailableCollections()}"
0051             break
0052 
0053 
0054 @pytest.mark.edm4hep
0055 @pytest.mark.skipif(not edm4hepEnabled, reason="EDM4hep is not set up")
0056 def test_edm4hep_measurement_writer(tmp_path, fatras):
0057     from acts.examples.edm4hep import EDM4hepMeasurementOutputConverter
0058     from acts.examples.podio import PodioWriter
0059 
0060     s = Sequencer(numThreads=1, events=10)
0061     _, simAlg, digiAlg = fatras(s)
0062 
0063     out = tmp_path / "measurements_edm4hep.root"
0064 
0065     converter = EDM4hepMeasurementOutputConverter(
0066         level=acts.logging.VERBOSE,
0067         inputMeasurements=digiAlg.config.outputMeasurements,
0068         inputClusters=digiAlg.config.outputClusters,
0069         outputTrackerHitsPlane="tracker_hits_plane",
0070         outputTrackerHitsRaw="tracker_hits_raw",
0071     )
0072     s.addAlgorithm(converter)
0073 
0074     s.addWriter(
0075         PodioWriter(
0076             level=acts.logging.VERBOSE,
0077             outputPath=str(out),
0078             category="events",
0079             collections=converter.collections,
0080         )
0081     )
0082 
0083     s.run()
0084 
0085     assert os.path.isfile(out)
0086     assert os.stat(out).st_size > 10
0087 
0088     assert_podio(
0089         out,
0090         "events",
0091         collections=set(["tracker_hits_plane", "tracker_hits_raw"]),
0092         nevents=10,
0093     )
0094 
0095 
0096 @pytest.mark.edm4hep
0097 @pytest.mark.skipif(not edm4hepEnabled, reason="EDM4hep is not set up")
0098 def test_edm4hep_simhit_writer(tmp_path, fatras, conf_const):
0099     from acts.examples.edm4hep import EDM4hepSimHitOutputConverter
0100     from acts.examples.podio import PodioWriter
0101 
0102     s = Sequencer(numThreads=1, events=10)
0103     _, simAlg, _ = fatras(s)
0104 
0105     out = tmp_path / "simhits_edm4hep.root"
0106 
0107     converter = EDM4hepSimHitOutputConverter(
0108         level=acts.logging.INFO,
0109         inputSimHits=simAlg.config.outputSimHits,
0110         outputSimTrackerHits="sim_tracker_hits",
0111     )
0112     s.addAlgorithm(converter)
0113 
0114     s.addWriter(
0115         PodioWriter(
0116             level=acts.logging.INFO,
0117             outputPath=str(out),
0118             category="events",
0119             collections=converter.collections,
0120         )
0121     )
0122     s.run()
0123 
0124     assert os.path.isfile(out)
0125     assert os.stat(out).st_size > 200
0126 
0127 
0128 @pytest.mark.edm4hep
0129 @pytest.mark.skipif(not edm4hepEnabled, reason="EDM4hep is not set up")
0130 def test_edm4hep_particle_writer(tmp_path, ptcl_gun):
0131     from acts.examples.edm4hep import EDM4hepParticleOutputConverter
0132     from acts.examples.podio import PodioWriter
0133 
0134     s = Sequencer(numThreads=1, events=10)
0135     _, h3conv = ptcl_gun(s)
0136 
0137     out = tmp_path / "particles_edm4hep.root"
0138 
0139     out.mkdir()
0140 
0141     converter = EDM4hepParticleOutputConverter(
0142         acts.logging.INFO,
0143         inputParticles=h3conv.config.outputParticles,
0144         outputParticles="MCParticles",
0145     )
0146     s.addAlgorithm(converter)
0147 
0148     s.addWriter(
0149         PodioWriter(
0150             level=acts.logging.INFO,
0151             outputPath=str(out),
0152             category="events",
0153             collections=converter.collections,
0154         )
0155     )
0156 
0157     s.run()
0158 
0159     assert os.path.isfile(out)
0160     assert os.stat(out).st_size > 200
0161 
0162     assert_podio(out, "events", collections=set(["MCParticles"]), nevents=10)
0163 
0164 
0165 @pytest.mark.edm4hep
0166 @pytest.mark.skipif(not edm4hepEnabled, reason="EDM4hep is not set up")
0167 def test_edm4hep_multitrajectory_writer(tmp_path):
0168     from acts.examples.edm4hep import EDM4hepMultiTrajectoryOutputConverter
0169     from acts.examples.podio import PodioWriter
0170 
0171     detector = GenericDetector()
0172     trackingGeometry = detector.trackingGeometry()
0173     field = acts.ConstantBField(acts.Vector3(0, 0, 2 * u.T))
0174 
0175     from truth_tracking_kalman import runTruthTrackingKalman
0176 
0177     s = Sequencer(numThreads=1, events=10)
0178     runTruthTrackingKalman(
0179         trackingGeometry,
0180         field,
0181         digiConfigFile=Path(
0182             str(
0183                 Path(__file__).parent.parent.parent.parent
0184                 / "Examples/Configs/generic-digi-smearing-config.json"
0185             )
0186         ),
0187         outputDir=tmp_path,
0188         s=s,
0189     )
0190 
0191     s.addAlgorithm(
0192         acts.examples.TracksToTrajectories(
0193             level=acts.logging.INFO,
0194             inputTracks="tracks",
0195             outputTrajectories="trajectories",
0196         )
0197     )
0198 
0199     out = tmp_path / "trajectories_edm4hep.root"
0200 
0201     converter = EDM4hepMultiTrajectoryOutputConverter(
0202         level=acts.logging.VERBOSE,
0203         inputTrajectories="trajectories",
0204         inputMeasurementParticlesMap="measurement_particles_map",
0205         outputTracks="ActsTrajectories",
0206     )
0207     s.addAlgorithm(converter)
0208 
0209     s.addWriter(
0210         PodioWriter(
0211             level=acts.logging.VERBOSE,
0212             outputPath=str(out),
0213             category="events",
0214             collections=converter.collections,
0215         )
0216     )
0217     s.run()
0218 
0219     assert os.path.isfile(out)
0220     assert os.stat(out).st_size > 200
0221 
0222 
0223 @pytest.mark.edm4hep
0224 @pytest.mark.skipif(not edm4hepEnabled, reason="EDM4hep is not set up")
0225 def test_edm4hep_tracks_writer(tmp_path):
0226     from acts.examples.edm4hep import EDM4hepTrackOutputConverter
0227     from acts.examples.podio import PodioWriter
0228 
0229     detector = GenericDetector()
0230     trackingGeometry = detector.trackingGeometry()
0231     field = acts.ConstantBField(acts.Vector3(0, 0, 2 * u.T))
0232 
0233     from truth_tracking_kalman import runTruthTrackingKalman
0234 
0235     s = Sequencer(numThreads=1, events=10)
0236     runTruthTrackingKalman(
0237         trackingGeometry,
0238         field,
0239         digiConfigFile=Path(
0240             str(
0241                 Path(__file__).parent.parent.parent.parent
0242                 / "Examples/Configs/generic-digi-smearing-config.json"
0243             )
0244         ),
0245         outputDir=tmp_path,
0246         s=s,
0247     )
0248 
0249     out = tmp_path / "tracks_edm4hep.root"
0250 
0251     converter = EDM4hepTrackOutputConverter(
0252         level=acts.logging.VERBOSE,
0253         inputTracks="kf_tracks",
0254         outputTracks="ActsTracks",
0255         Bz=2 * u.T,
0256     )
0257     s.addAlgorithm(converter)
0258 
0259     s.addWriter(
0260         PodioWriter(
0261             level=acts.logging.VERBOSE,
0262             outputPath=str(out),
0263             category="events",
0264             collections=converter.collections,
0265         )
0266     )
0267     s.run()
0268 
0269     assert os.path.isfile(out), f"File {out} does not exist"
0270     assert os.stat(out).st_size > 200, f"File {out} is too small"
0271 
0272     if not podioEnabled:
0273         import warnings
0274 
0275         warnings.warn(
0276             "edm4hep output checks were skipped, because podio was not on the python path"
0277         )
0278         return
0279 
0280     from podio.root_io import Reader
0281     import cppyy
0282 
0283     reader = Reader(str(out))
0284 
0285     actual = []
0286 
0287     for frame in reader.get("events"):
0288         tracks = frame.get("ActsTracks")
0289         for track in tracks:
0290             actual.append(
0291                 (track.getChi2(), track.getNdf(), len(track.getTrackStates()))
0292             )
0293 
0294             locs = []
0295 
0296             perigee = None
0297             for ts in track.getTrackStates():
0298                 if ts.location == cppyy.gbl.edm4hep.TrackState.AtIP:
0299                     perigee = ts
0300                     continue
0301                 locs.append(ts.location)
0302 
0303                 rp = ts.referencePoint
0304                 r = math.sqrt(rp.x**2 + rp.y**2)
0305                 assert r > 25
0306 
0307             assert locs[0] == cppyy.gbl.edm4hep.TrackState.AtLastHit
0308             assert locs[-1] == cppyy.gbl.edm4hep.TrackState.AtFirstHit
0309 
0310             assert perigee is not None
0311             rp = perigee.referencePoint
0312             assert rp.x == 0.0
0313             assert rp.y == 0.0
0314             assert rp.z == 0.0
0315             assert abs(perigee.D0) < 1e0
0316             assert abs(perigee.Z0) < 1e1
0317 
0318 
0319 def generate_input_test_edm4hep_simhit_reader(input, output):
0320     from DDSim.DD4hepSimulation import DD4hepSimulation
0321 
0322     ddsim = DD4hepSimulation()
0323     if isinstance(ddsim.compactFile, list):
0324         ddsim.compactFile = [input]
0325     else:
0326         ddsim.compactFile = input
0327     ddsim.enableGun = True
0328     ddsim.gun.direction = (1, 0, 0)
0329     ddsim.gun.particle = "pi-"
0330     ddsim.gun.distribution = "eta"
0331     ddsim.numberOfEvents = 10
0332     ddsim.outputFile = output
0333     ddsim.outputConfig.forceEDM4HEP = True
0334     ddsim.run()
0335 
0336 
0337 # Session scoped fixture that uses a temp folder
0338 @pytest.fixture(scope="session")
0339 def ddsim_input_session():
0340     with tempfile.TemporaryDirectory() as tmp_dir:
0341         odd_xml_file = str(
0342             getOpenDataDetectorDirectory() / "xml" / "OpenDataDetector.xml"
0343         )
0344 
0345         # output_file = str(Path(tmp_dir) / "output_edm4hep.root")
0346         output_file = str(Path.cwd() / "output_edm4hep.root")
0347 
0348         if not os.path.exists(output_file):
0349             # explicitly ask for "spawn" as CI failures were observed with "fork"
0350             spawn_context = multiprocessing.get_context("spawn")
0351             p = spawn_context.Process(
0352                 target=generate_input_test_edm4hep_simhit_reader,
0353                 args=(odd_xml_file, output_file),
0354             )
0355             p.start()
0356             p.join()
0357 
0358             assert os.path.exists(output_file)
0359 
0360         yield output_file
0361 
0362 
0363 # Function scoped fixture that uses a temp folder
0364 @pytest.fixture(scope="function")
0365 def ddsim_input(ddsim_input_session, tmp_path):
0366     tmp_file = str(tmp_path / "output_edm4hep.root")
0367     shutil.copy(ddsim_input_session, tmp_file)
0368     return tmp_file
0369 
0370 
0371 @pytest.mark.slow
0372 @pytest.mark.edm4hep
0373 @pytest.mark.skipif(not edm4hepEnabled, reason="EDM4hep is not set up")
0374 def test_edm4hep_simhit_particle_reader(tmp_path, ddsim_input):
0375     from acts.examples.edm4hep import EDM4hepSimInputConverter
0376     from acts.examples.podio import PodioReader
0377 
0378     s = Sequencer(numThreads=1)
0379 
0380     with getOpenDataDetector() as detector:
0381         trackingGeometry = detector.trackingGeometry()
0382 
0383         s.addReader(
0384             PodioReader(
0385                 level=acts.logging.INFO,
0386                 inputPath=ddsim_input,
0387                 outputFrame="events",
0388                 category="events",
0389             )
0390         )
0391 
0392         s.addAlgorithm(
0393             EDM4hepSimInputConverter(
0394                 level=acts.logging.INFO,
0395                 inputFrame="events",
0396                 inputSimHits=[
0397                     "PixelBarrelReadout",
0398                     "PixelEndcapReadout",
0399                     "ShortStripBarrelReadout",
0400                     "ShortStripEndcapReadout",
0401                     "LongStripBarrelReadout",
0402                     "LongStripEndcapReadout",
0403                 ],
0404                 outputParticlesGenerator="particles_generated",
0405                 outputParticlesSimulation="particles_simulated",
0406                 outputSimHits="simhits",
0407                 outputSimVertices="simvertices",
0408                 dd4hepDetector=detector,
0409                 trackingGeometry=trackingGeometry,
0410             )
0411         )
0412 
0413         alg = AssertCollectionExistsAlg(
0414             ["simhits", "simvertices", "particles_generated", "particles_simulated"],
0415             "check_alg",
0416             acts.logging.WARNING,
0417         )
0418         s.addAlgorithm(alg)
0419 
0420         alg = AssertCollectionExistsAlg(
0421             "particles_generated", "check_alg", acts.logging.WARNING
0422         )
0423         s.addAlgorithm(alg)
0424 
0425         s.run()
0426 
0427     assert alg.events_seen == 10
0428 
0429 
0430 @pytest.mark.edm4hep
0431 @pytest.mark.skipif(not edm4hepEnabled, reason="EDM4hep is not set up")
0432 def test_edm4hep_measurement_reader(tmp_path, fatras):
0433     from acts.examples.edm4hep import (
0434         EDM4hepMeasurementOutputConverter,
0435         EDM4hepMeasurementInputConverter,
0436     )
0437     from acts.examples.podio import PodioWriter, PodioReader
0438 
0439     s = Sequencer(numThreads=1, events=10)
0440     _, simAlg, digiAlg = fatras(s)
0441 
0442     out = tmp_path / "measurements_edm4hep.root"
0443 
0444     converter = EDM4hepMeasurementOutputConverter(
0445         level=acts.logging.INFO,
0446         inputMeasurements=digiAlg.config.outputMeasurements,
0447         inputClusters=digiAlg.config.outputClusters,
0448     )
0449     s.addAlgorithm(converter)
0450     s.addWriter(
0451         PodioWriter(
0452             level=acts.logging.INFO,
0453             outputPath=str(out),
0454             category="events",
0455             collections=converter.collections,
0456         )
0457     )
0458     s.run()
0459 
0460     # read back in
0461     s = Sequencer(numThreads=1)
0462 
0463     s.addReader(
0464         PodioReader(
0465             level=acts.logging.WARNING,
0466             inputPath=str(out),
0467             outputFrame="events",
0468             category="events",
0469         )
0470     )
0471     s.addAlgorithm(
0472         EDM4hepMeasurementInputConverter(
0473             level=acts.logging.WARNING,
0474             inputFrame="events",
0475             outputMeasurements="measurements",
0476             outputMeasurementSimHitsMap="simhitsmap",
0477         )
0478     )
0479 
0480     alg = AssertCollectionExistsAlg(
0481         ["measurements", "simhitsmap"], "check_alg", acts.logging.WARNING
0482     )
0483     s.addAlgorithm(alg)
0484 
0485     s.run()
0486 
0487     assert alg.events_seen == 10
0488 
0489 
0490 @pytest.mark.edm4hep
0491 @pytest.mark.skipif(not edm4hepEnabled, reason="EDM4hep is not set up")
0492 def test_edm4hep_tracks_reader(tmp_path):
0493     from acts.examples.edm4hep import (
0494         EDM4hepTrackOutputConverter,
0495         EDM4hepTrackInputConverter,
0496     )
0497     from acts.examples.podio import PodioWriter, PodioReader
0498 
0499     detector = acts.examples.GenericDetector()
0500     trackingGeometry = detector.trackingGeometry()
0501 
0502     field = acts.ConstantBField(acts.Vector3(0, 0, 2 * u.T))
0503 
0504     from truth_tracking_kalman import runTruthTrackingKalman
0505 
0506     s = Sequencer(numThreads=1, events=10)
0507     runTruthTrackingKalman(
0508         trackingGeometry,
0509         field,
0510         digiConfigFile=Path(
0511             str(
0512                 Path(__file__).parent.parent.parent.parent
0513                 / "Examples/Configs/generic-digi-smearing-config.json"
0514             )
0515         ),
0516         outputDir=tmp_path,
0517         s=s,
0518     )
0519 
0520     out = tmp_path / "tracks_edm4hep.root"
0521 
0522     converter = EDM4hepTrackOutputConverter(
0523         level=acts.logging.VERBOSE,
0524         inputTracks="kf_tracks",
0525         Bz=2 * u.T,
0526     )
0527     s.addAlgorithm(converter)
0528 
0529     s.addWriter(
0530         PodioWriter(
0531             level=acts.logging.VERBOSE,
0532             outputPath=str(out),
0533             category="events",
0534             collections=converter.collections,
0535         )
0536     )
0537     s.run()
0538 
0539     s = Sequencer(numThreads=1)
0540 
0541     s.addReader(
0542         PodioReader(
0543             level=acts.logging.VERBOSE,
0544             inputPath=str(out),
0545             outputFrame="events",
0546             category="events",
0547         )
0548     )
0549 
0550     s.addAlgorithm(
0551         EDM4hepTrackInputConverter(
0552             level=acts.logging.VERBOSE,
0553             inputFrame="events",
0554             inputTracks="kf_tracks",
0555             outputTracks="kf_tracks",
0556             Bz=2 * u.T,
0557         )
0558     )
0559 
0560     s.run()
0561 
0562 
0563 @pytest.mark.edm4hep
0564 @pytest.mark.skipif(not edm4hepEnabled, reason="EDM4hep is not set up")
0565 def test_edm4hep_reader(ddsim_input):
0566     from acts.examples.podio import PodioReader
0567 
0568     s = Sequencer(numThreads=1)
0569     s.addReader(
0570         PodioReader(
0571             level=acts.logging.VERBOSE,
0572             inputPath=ddsim_input,
0573             outputFrame="frame",
0574         )
0575     )
0576 
0577     alg = AssertCollectionExistsAlg("frame", "check_alg", acts.logging.WARNING)
0578     s.addAlgorithm(alg)
0579 
0580     s.run()
0581 
0582 
0583 @pytest.mark.edm4hep
0584 @pytest.mark.skipif(not edm4hepEnabled, reason="EDM4hep is not set up")
0585 def test_edm4hep_writer_copy(ddsim_input, tmp_path):
0586     from acts.examples.podio import PodioWriter, PodioReader
0587 
0588     target_file = tmp_path / "rewritten_edm4hep.root"
0589 
0590     s = Sequencer(numThreads=1)
0591     s.addReader(
0592         PodioReader(
0593             level=acts.logging.VERBOSE,
0594             inputPath=ddsim_input,
0595             outputFrame="myframe",
0596             category="events",
0597         )
0598     )
0599     s.addWriter(
0600         PodioWriter(
0601             level=acts.logging.VERBOSE,
0602             inputFrame="myframe",
0603             outputPath=str(target_file),
0604             category="myevents",
0605         )
0606     )
0607 
0608     s.run()
0609 
0610     assert_podio(
0611         target_file,
0612         "myevents",
0613         nevents=10,
0614         collections=set(
0615             [
0616                 "ShortStripEndcapReadout",
0617                 "MCParticles",
0618                 "LongStripBarrelReadout",
0619                 "PixelEndcapReadout",
0620                 "HCalEndcapCollectionContributions",
0621                 "HCalEndcapCollection",
0622                 "HCalBarrelCollectionContributions",
0623                 "ECalBarrelCollectionContributions",
0624                 "LongStripEndcapReadout",
0625                 "EventHeader",
0626                 "ECalEndcapCollectionContributions",
0627                 "ShortStripBarrelReadout",
0628                 "PixelBarrelReadout",
0629                 "ECalEndcapCollection",
0630                 "HCalBarrelCollection",
0631                 "ECalBarrelCollection",
0632             ]
0633         ),
0634     )