Analysis Software
Documentation for sPHENIX simulation software
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
test_writer.py
Go to the documentation of this file. Or view the newest version in sPHENIX GitHub for file test_writer.py
1 import os
2 import inspect
3 from pathlib import Path
4 import shutil
5 import math
6 import sys
7 import tempfile
8 
9 import pytest
10 
11 from helpers import (
12  dd4hepEnabled,
13  hepmc3Enabled,
14  geant4Enabled,
15  edm4hepEnabled,
16  podioEnabled,
17  AssertCollectionExistsAlg,
18 )
19 
20 import acts
21 
22 from common import getOpenDataDetectorDirectory
23 
24 from acts import PlanarModuleStepper, UnitConstants as u
25 
26 
27 from acts.examples import (
28  ObjPropagationStepsWriter,
29  TrackFinderPerformanceWriter,
30  SeedingPerformanceWriter,
31  RootPropagationStepsWriter,
32  RootParticleWriter,
33  RootTrackParameterWriter,
34  RootMaterialTrackWriter,
35  RootMaterialWriter,
36  RootPlanarClusterWriter,
37  RootSimHitWriter,
38  RootTrajectoryStatesWriter,
39  RootTrajectorySummaryWriter,
40  VertexPerformanceWriter,
41  RootMeasurementWriter,
42  CsvParticleWriter,
43  CsvPlanarClusterWriter,
44  CsvSimHitWriter,
45  CsvMultiTrajectoryWriter,
46  CsvTrackingGeometryWriter,
47  CsvMeasurementWriter,
48  PlanarSteppingAlgorithm,
49  JsonMaterialWriter,
50  JsonFormat,
51  Sequencer,
52  GenericDetector,
53 )
54 
55 
56 @pytest.mark.obj
57 def test_obj_propagation_step_writer(tmp_path, trk_geo, conf_const, basic_prop_seq):
58  with pytest.raises(TypeError):
59  ObjPropagationStepsWriter()
60 
61  obj = tmp_path / "obj"
62  obj.mkdir()
63 
64  s, alg = basic_prop_seq(trk_geo)
65  w = conf_const(
66  ObjPropagationStepsWriter,
67  acts.logging.INFO,
68  collection=alg.config.propagationStepCollection,
69  outputDir=str(obj),
70  )
71 
72  s.addWriter(w)
73 
74  s.run()
75 
76  assert len([f for f in obj.iterdir() if f.is_file()]) == s.config.events
77  for f in obj.iterdir():
78  assert f.stat().st_size > 1024
79 
80 
81 @pytest.mark.csv
82 def test_csv_particle_writer(tmp_path, conf_const, ptcl_gun):
83  s = Sequencer(numThreads=1, events=10)
84  evGen = ptcl_gun(s)
85 
86  out = tmp_path / "csv"
87 
88  out.mkdir()
89 
90  s.addWriter(
91  conf_const(
92  CsvParticleWriter,
93  acts.logging.INFO,
94  inputParticles=evGen.config.outputParticles,
95  outputStem="particle",
96  outputDir=str(out),
97  )
98  )
99 
100  s.run()
101 
102  assert len([f for f in out.iterdir() if f.is_file()]) == s.config.events
103  assert all(f.stat().st_size > 200 for f in out.iterdir())
104 
105 
106 @pytest.mark.root
108  tmp_path, trk_geo, conf_const, basic_prop_seq, assert_root_hash
109 ):
110  with pytest.raises(TypeError):
111  RootPropagationStepsWriter()
112 
113  file = tmp_path / "prop_steps.root"
114  assert not file.exists()
115 
116  s, alg = basic_prop_seq(trk_geo)
117  w = conf_const(
118  RootPropagationStepsWriter,
119  acts.logging.INFO,
120  collection=alg.config.propagationStepCollection,
121  filePath=str(file),
122  )
123 
124  s.addWriter(w)
125 
126  s.run()
127 
128  assert file.exists()
129  assert file.stat().st_size > 2**10 * 50
130  assert_root_hash(file.name, file)
131 
132 
133 @pytest.mark.root
134 def test_root_particle_writer(tmp_path, conf_const, ptcl_gun, assert_root_hash):
135  s = Sequencer(numThreads=1, events=10)
136  evGen = ptcl_gun(s)
137 
138  file = tmp_path / "particles.root"
139 
140  assert not file.exists()
141 
142  s.addWriter(
143  conf_const(
144  RootParticleWriter,
145  acts.logging.INFO,
146  inputParticles=evGen.config.outputParticles,
147  filePath=str(file),
148  )
149  )
150 
151  s.run()
152 
153  assert file.exists()
154  assert file.stat().st_size > 1024 * 10
155  assert_root_hash(file.name, file)
156 
157 
158 @pytest.mark.root
159 def test_root_meas_writer(tmp_path, fatras, trk_geo, assert_root_hash):
160  s = Sequencer(numThreads=1, events=10)
161  evGen, simAlg, digiAlg = fatras(s)
162 
163  out = tmp_path / "meas.root"
164 
165  assert not out.exists()
166 
167  config = RootMeasurementWriter.Config(
168  inputMeasurements=digiAlg.config.outputMeasurements,
169  inputClusters=digiAlg.config.outputClusters,
170  inputSimHits=simAlg.config.outputSimHits,
171  inputMeasurementSimHitsMap=digiAlg.config.outputMeasurementSimHitsMap,
172  filePath=str(out),
173  trackingGeometry=trk_geo,
174  )
175  config.addBoundIndicesFromDigiConfig(digiAlg.config)
176  s.addWriter(RootMeasurementWriter(level=acts.logging.INFO, config=config))
177  s.run()
178 
179  assert out.exists()
180  assert out.stat().st_size > 40000
181  assert_root_hash(out.name, out)
182 
183 
184 @pytest.mark.root
185 def test_root_simhits_writer(tmp_path, fatras, conf_const, assert_root_hash):
186  s = Sequencer(numThreads=1, events=10)
187  evGen, simAlg, digiAlg = fatras(s)
188 
189  out = tmp_path / "meas.root"
190 
191  assert not out.exists()
192 
193  s.addWriter(
194  conf_const(
195  RootSimHitWriter,
196  level=acts.logging.INFO,
197  inputSimHits=simAlg.config.outputSimHits,
198  filePath=str(out),
199  )
200  )
201 
202  s.run()
203  assert out.exists()
204  assert out.stat().st_size > 2e4
205  assert_root_hash(out.name, out)
206 
207 
208 @pytest.mark.root
210  tmp_path, fatras, conf_const, trk_geo, rng, assert_root_hash
211 ):
212  s = Sequencer(numThreads=1, events=10) # we're not going to use this one
213  evGen, simAlg, _ = fatras(s)
214  s = Sequencer(numThreads=1, events=10)
215  s.addReader(evGen)
216  s.addAlgorithm(simAlg)
217  digiAlg = PlanarSteppingAlgorithm(
218  level=acts.logging.INFO,
219  inputSimHits=simAlg.config.outputSimHits,
220  outputClusters="clusters",
221  outputSourceLinks="sourcelinks",
222  outputDigiSourceLinks="digi_sourcelinks",
223  outputMeasurements="measurements",
224  outputMeasurementParticlesMap="meas_ptcl_map",
225  outputMeasurementSimHitsMap="meas_sh_map",
226  trackingGeometry=trk_geo,
227  randomNumbers=rng,
228  planarModuleStepper=PlanarModuleStepper(),
229  )
230  s.addAlgorithm(digiAlg)
231 
232  out = tmp_path / "clusters.root"
233 
234  assert not out.exists()
235 
236  s.addWriter(
237  conf_const(
238  RootPlanarClusterWriter,
239  level=acts.logging.INFO,
240  filePath=str(out),
241  inputSimHits=simAlg.config.outputSimHits,
242  inputClusters=digiAlg.config.outputClusters,
243  trackingGeometry=trk_geo,
244  )
245  )
246 
247  s.run()
248  assert out.exists()
249  assert out.stat().st_size > 2**10 * 50
250  assert_root_hash(out.name, out)
251 
252 
253 @pytest.mark.csv
254 def test_csv_meas_writer(tmp_path, fatras, trk_geo, conf_const):
255  s = Sequencer(numThreads=1, events=10)
256  evGen, simAlg, digiAlg = fatras(s)
257 
258  out = tmp_path / "csv"
259  out.mkdir()
260 
261  s.addWriter(
262  conf_const(
263  CsvMeasurementWriter,
264  level=acts.logging.INFO,
265  inputMeasurements=digiAlg.config.outputMeasurements,
266  inputClusters=digiAlg.config.outputClusters,
267  inputMeasurementSimHitsMap=digiAlg.config.outputMeasurementSimHitsMap,
268  outputDir=str(out),
269  )
270  )
271  s.run()
272 
273  assert len([f for f in out.iterdir() if f.is_file()]) == s.config.events * 3
274  assert all(f.stat().st_size > 10 for f in out.iterdir())
275 
276 
277 @pytest.mark.csv
278 def test_csv_simhits_writer(tmp_path, fatras, conf_const):
279  s = Sequencer(numThreads=1, events=10)
280  evGen, simAlg, digiAlg = fatras(s)
281 
282  out = tmp_path / "csv"
283  out.mkdir()
284 
285  s.addWriter(
286  conf_const(
287  CsvSimHitWriter,
288  level=acts.logging.INFO,
289  inputSimHits=simAlg.config.outputSimHits,
290  outputDir=str(out),
291  outputStem="hits",
292  )
293  )
294 
295  s.run()
296  assert len([f for f in out.iterdir() if f.is_file()]) == s.config.events
297  assert all(f.stat().st_size > 200 for f in out.iterdir())
298 
299 
300 @pytest.mark.csv
301 def test_csv_clusters_writer(tmp_path, fatras, conf_const, trk_geo, rng):
302  s = Sequencer(numThreads=1, events=10) # we're not going to use this one
303  evGen, simAlg, _ = fatras(s)
304  s = Sequencer(numThreads=1, events=10)
305  s.addReader(evGen)
306  s.addAlgorithm(simAlg)
307  digiAlg = PlanarSteppingAlgorithm(
308  level=acts.logging.WARNING,
309  inputSimHits=simAlg.config.outputSimHits,
310  outputClusters="clusters",
311  outputSourceLinks="sourcelinks",
312  outputDigiSourceLinks="digi_sourcelinks",
313  outputMeasurements="measurements",
314  outputMeasurementParticlesMap="meas_ptcl_map",
315  outputMeasurementSimHitsMap="meas_sh_map",
316  trackingGeometry=trk_geo,
317  randomNumbers=rng,
318  planarModuleStepper=PlanarModuleStepper(),
319  )
320  s.addAlgorithm(digiAlg)
321 
322  out = tmp_path / "csv"
323  out.mkdir()
324 
325  s.addWriter(
326  conf_const(
327  CsvPlanarClusterWriter,
328  level=acts.logging.WARNING,
329  outputDir=str(out),
330  inputSimHits=simAlg.config.outputSimHits,
331  inputClusters=digiAlg.config.outputClusters,
332  trackingGeometry=trk_geo,
333  )
334  )
335 
336  s.run()
337  assert len([f for f in out.iterdir() if f.is_file()]) == s.config.events * 3
338  assert all(f.stat().st_size > 1024 for f in out.iterdir())
339 
340 
341 @pytest.mark.parametrize(
342  "writer",
343  [
344  RootPropagationStepsWriter,
345  RootParticleWriter,
346  TrackFinderPerformanceWriter,
347  SeedingPerformanceWriter,
348  RootTrackParameterWriter,
349  RootMaterialTrackWriter,
350  RootMeasurementWriter,
351  RootMaterialWriter,
352  RootPlanarClusterWriter,
353  RootSimHitWriter,
354  RootTrajectoryStatesWriter,
355  RootTrajectorySummaryWriter,
356  VertexPerformanceWriter,
357  SeedingPerformanceWriter,
358  ],
359 )
360 @pytest.mark.root
361 def test_root_writer_interface(writer, conf_const, tmp_path, trk_geo):
362  assert hasattr(writer, "Config")
363 
364  config = writer.Config
365 
366  assert hasattr(config, "filePath")
367  assert hasattr(config, "fileMode")
368 
369  f = tmp_path / "target.root"
370  assert not f.exists()
371 
372  kw = {"level": acts.logging.INFO, "filePath": str(f)}
373 
374  for k, _ in inspect.getmembers(config):
375  if k.startswith("input"):
376  kw[k] = "collection"
377  if k == "trackingGeometry":
378  kw[k] = trk_geo
379 
380  assert conf_const(writer, **kw)
381 
382  assert f.exists()
383 
384 
385 @pytest.mark.parametrize(
386  "writer",
387  [
388  CsvParticleWriter,
389  CsvMeasurementWriter,
390  CsvPlanarClusterWriter,
391  CsvSimHitWriter,
392  CsvMultiTrajectoryWriter,
393  CsvTrackingGeometryWriter,
394  ],
395 )
396 @pytest.mark.csv
397 def test_csv_writer_interface(writer, conf_const, tmp_path, trk_geo):
398  assert hasattr(writer, "Config")
399 
400  config = writer.Config
401 
402  assert hasattr(config, "outputDir")
403 
404  kw = {"level": acts.logging.INFO, "outputDir": str(tmp_path)}
405 
406  for k, _ in inspect.getmembers(config):
407  if k.startswith("input"):
408  kw[k] = "collection"
409  if k == "trackingGeometry":
410  kw[k] = trk_geo
411  if k == "outputStem":
412  kw[k] = "stem"
413 
414  assert conf_const(writer, **kw)
415 
416 
417 @pytest.mark.root
418 @pytest.mark.odd
419 @pytest.mark.skipif(not dd4hepEnabled, reason="DD4hep not set up")
420 def test_root_material_writer(tmp_path, assert_root_hash):
421  from acts.examples.dd4hep import DD4hepDetector
422 
423  detector, trackingGeometry, _ = DD4hepDetector.create(
424  xmlFileNames=[str(getOpenDataDetectorDirectory() / "xml/OpenDataDetector.xml")]
425  )
426 
427  out = tmp_path / "material.root"
428 
429  assert not out.exists()
430 
431  rmw = RootMaterialWriter(level=acts.logging.WARNING, filePath=str(out))
432  assert out.exists()
433  assert out.stat().st_size > 0 and out.stat().st_size < 500
434  rmw.write(trackingGeometry)
435 
436  assert out.stat().st_size > 1000
437  assert_root_hash(out.name, out)
438 
439 
440 @pytest.mark.json
441 @pytest.mark.odd
442 @pytest.mark.parametrize("fmt", [JsonFormat.Json, JsonFormat.Cbor])
443 @pytest.mark.skipif(not dd4hepEnabled, reason="DD4hep not set up")
444 def test_json_material_writer(tmp_path, fmt):
445  from acts.examples.dd4hep import DD4hepDetector
446 
447  detector, trackingGeometry, _ = DD4hepDetector.create(
448  xmlFileNames=[str(getOpenDataDetectorDirectory() / "xml/OpenDataDetector.xml")]
449  )
450 
451  out = (tmp_path / "material").with_suffix("." + fmt.name.lower())
452 
453  assert not out.exists()
454 
455  jmw = JsonMaterialWriter(
456  level=acts.logging.WARNING, fileName=str(out.with_suffix("")), writeFormat=fmt
457  )
458  assert not out.exists()
459  jmw.write(trackingGeometry)
460 
461  assert out.stat().st_size > 1000
462 
463 
464 @pytest.mark.csv
466  detector, trackingGeometry, decorators = GenericDetector.create()
467  field = acts.ConstantBField(acts.Vector3(0, 0, 2 * u.T))
468 
469  from truth_tracking_kalman import runTruthTrackingKalman
470 
471  s = Sequencer(numThreads=1, events=10)
473  trackingGeometry,
474  field,
475  digiConfigFile=Path(
476  str(
477  Path(__file__).parent.parent.parent.parent
478  / "Examples/Algorithms/Digitization/share/default-smearing-config-generic.json"
479  )
480  ),
481  outputDir=tmp_path,
482  s=s,
483  )
484 
485  csv_dir = tmp_path / "csv"
486  csv_dir.mkdir()
487  s.addWriter(
488  CsvMultiTrajectoryWriter(
489  level=acts.logging.INFO,
490  inputTrajectories="trajectories",
491  inputMeasurementParticlesMap="measurement_particles_map",
492  outputDir=str(csv_dir),
493  )
494  )
495  s.run()
496  del s
497  assert len([f for f in csv_dir.iterdir() if f.is_file()]) == 10
498  assert all(f.stat().st_size > 20 for f in csv_dir.iterdir())
499 
500 
501 @pytest.fixture(scope="session")
502 def hepmc_data_impl(tmp_path_factory):
503  import subprocess
504 
505  script = (
506  Path(__file__).parent.parent.parent.parent
507  / "Examples"
508  / "Scripts"
509  / "Python"
510  / "event_recording.py"
511  )
512  assert script.exists()
513 
514  with tempfile.TemporaryDirectory() as tmp_path:
515  env = os.environ.copy()
516  env["NEVENTS"] = "1"
517  subprocess.check_call([sys.executable, str(script)], cwd=tmp_path, env=env)
518 
519  outfile = Path(tmp_path) / "hepmc3/event000000000-events.hepmc3"
520  # fake = Path("/scratch/pagessin/acts/hepmc3/event000000000-events.hepmc3")
521 
522  # outfile.parent.mkdir()
523  # shutil.copy(fake, outfile)
524 
525  assert outfile.exists()
526 
527  yield outfile
528 
529 
530 @pytest.fixture
531 def hepmc_data(hepmc_data_impl: Path, tmp_path):
532  dest = tmp_path / hepmc_data_impl.name
533  shutil.copy(hepmc_data_impl, dest)
534 
535  return dest
536 
537 
538 @pytest.mark.skipif(not hepmc3Enabled, reason="HepMC3 plugin not available")
539 @pytest.mark.skipif(not dd4hepEnabled, reason="DD4hep not set up")
540 @pytest.mark.skipif(not geant4Enabled, reason="Geant4 not set up")
541 @pytest.mark.odd
542 @pytest.mark.slow
543 def test_hepmc3_histogram(hepmc_data, tmp_path):
544  from acts.examples.hepmc3 import (
545  HepMC3AsciiReader,
546  HepMCProcessExtractor,
547  )
548 
549  s = Sequencer(numThreads=1)
550 
551  s.addReader(
552  HepMC3AsciiReader(
553  level=acts.logging.INFO,
554  inputDir=str(hepmc_data.parent),
555  inputStem="events",
556  outputEvents="hepmc-events",
557  )
558  )
559 
560  s.addAlgorithm(
561  HepMCProcessExtractor(
562  level=acts.logging.INFO,
563  inputEvents="hepmc-events",
564  extractionProcess="Inelastic",
565  )
566  )
567 
568  # This segfaults, see https://github.com/acts-project/acts/issues/914
569  # s.addWriter(
570  # RootNuclearInteractionParametersWriter(
571  # level=acts.logging.INFO, inputSimulationProcesses="event-fraction"
572  # )
573  # )
574 
575  alg = AssertCollectionExistsAlg(
576  "hepmc-events", name="check_alg", level=acts.logging.INFO
577  )
578  s.addAlgorithm(alg)
579 
580  s.run()
581 
582 
583 @pytest.mark.edm4hep
584 @pytest.mark.skipif(not edm4hepEnabled, reason="EDM4hep is not set up")
585 def test_edm4hep_measurement_writer(tmp_path, fatras):
586  from acts.examples.edm4hep import EDM4hepMeasurementWriter
587 
588  s = Sequencer(numThreads=1, events=10)
589  _, simAlg, digiAlg = fatras(s)
590 
591  out = tmp_path / "measurements_edm4hep.root"
592 
593  s.addWriter(
594  EDM4hepMeasurementWriter(
595  level=acts.logging.VERBOSE,
596  inputMeasurements=digiAlg.config.outputMeasurements,
597  inputClusters=digiAlg.config.outputClusters,
598  outputPath=str(out),
599  )
600  )
601 
602  s.run()
603 
604  assert os.path.isfile(out)
605  assert os.stat(out).st_size > 10
606 
607 
608 @pytest.mark.edm4hep
609 @pytest.mark.skipif(not edm4hepEnabled, reason="EDM4hep is not set up")
610 def test_edm4hep_simhit_writer(tmp_path, fatras, conf_const):
611  from acts.examples.edm4hep import EDM4hepSimHitWriter
612 
613  s = Sequencer(numThreads=1, events=10)
614  _, simAlg, _ = fatras(s)
615 
616  out = tmp_path / "simhits_edm4hep.root"
617 
618  s.addWriter(
619  conf_const(
620  EDM4hepSimHitWriter,
621  level=acts.logging.INFO,
622  inputSimHits=simAlg.config.outputSimHits,
623  outputPath=str(out),
624  )
625  )
626 
627  s.run()
628 
629  assert os.path.isfile(out)
630  assert os.stat(out).st_size > 200
631 
632 
633 @pytest.mark.edm4hep
634 @pytest.mark.skipif(not edm4hepEnabled, reason="EDM4hep is not set up")
635 def test_edm4hep_particle_writer(tmp_path, conf_const, ptcl_gun):
636  from acts.examples.edm4hep import EDM4hepParticleWriter
637 
638  s = Sequencer(numThreads=1, events=10)
639  evGen = ptcl_gun(s)
640 
641  out = tmp_path / "particles_edm4hep.root"
642 
643  out.mkdir()
644 
645  s.addWriter(
646  conf_const(
647  EDM4hepParticleWriter,
648  acts.logging.INFO,
649  inputParticles=evGen.config.outputParticles,
650  outputPath=str(out),
651  )
652  )
653 
654  s.run()
655 
656  assert os.path.isfile(out)
657  assert os.stat(out).st_size > 200
658 
659 
660 @pytest.mark.edm4hep
661 @pytest.mark.skipif(not edm4hepEnabled, reason="EDM4hep is not set up")
663  from acts.examples.edm4hep import EDM4hepMultiTrajectoryWriter
664 
665  detector, trackingGeometry, decorators = GenericDetector.create()
666  field = acts.ConstantBField(acts.Vector3(0, 0, 2 * u.T))
667 
668  from truth_tracking_kalman import runTruthTrackingKalman
669 
670  s = Sequencer(numThreads=1, events=10)
672  trackingGeometry,
673  field,
674  digiConfigFile=Path(
675  str(
676  Path(__file__).parent.parent.parent.parent
677  / "Examples/Algorithms/Digitization/share/default-smearing-config-generic.json"
678  )
679  ),
680  outputDir=tmp_path,
681  s=s,
682  )
683 
684  out = tmp_path / "trajectories_edm4hep.root"
685 
686  s.addWriter(
687  EDM4hepMultiTrajectoryWriter(
688  level=acts.logging.VERBOSE,
689  inputTrajectories="trajectories",
690  inputMeasurementParticlesMap="measurement_particles_map",
691  outputPath=str(out),
692  )
693  )
694 
695  s.run()
696 
697  assert os.path.isfile(out)
698  assert os.stat(out).st_size > 200
699 
700 
701 @pytest.mark.edm4hep
702 @pytest.mark.skipif(not edm4hepEnabled, reason="EDM4hep is not set up")
704  from acts.examples.edm4hep import EDM4hepTrackWriter
705 
706  detector, trackingGeometry, decorators = GenericDetector.create()
707  field = acts.ConstantBField(acts.Vector3(0, 0, 2 * u.T))
708 
709  from truth_tracking_kalman import runTruthTrackingKalman
710 
711  s = Sequencer(numThreads=1, events=10)
713  trackingGeometry,
714  field,
715  digiConfigFile=Path(
716  str(
717  Path(__file__).parent.parent.parent.parent
718  / "Examples/Algorithms/Digitization/share/default-smearing-config-generic.json"
719  )
720  ),
721  outputDir=tmp_path,
722  s=s,
723  )
724 
725  out = tmp_path / "tracks_edm4hep.root"
726 
727  s.addWriter(
728  EDM4hepTrackWriter(
729  level=acts.logging.VERBOSE,
730  inputTracks="kfTracks",
731  outputPath=str(out),
732  Bz=2 * u.T,
733  )
734  )
735 
736  s.run()
737 
738  assert os.path.isfile(out)
739  assert os.stat(out).st_size > 200
740 
741  if not podioEnabled:
742  import warnings
743 
744  warnings.warn(
745  "edm4hep output checks were skipped, because podio was not on the python path"
746  )
747  return
748 
749  from podio.root_io import Reader
750  import cppyy
751 
752  reader = Reader(str(out))
753 
754  actual = []
755 
756  for frame in reader.get("events"):
757  tracks = frame.get("ActsTracks")
758  for track in tracks:
759  actual.append(
760  (track.getChi2(), track.getNdf(), len(track.getTrackStates()))
761  )
762 
763  locs = []
764 
765  perigee = None
766  for ts in track.getTrackStates():
767  if ts.location == cppyy.gbl.edm4hep.TrackState.AtIP:
768  perigee = ts
769  continue
770  locs.append(ts.location)
771 
772  rp = ts.referencePoint
773  r = math.sqrt(rp.x**2 + rp.y**2)
774  assert r > 25
775 
776  assert locs[0] == cppyy.gbl.edm4hep.TrackState.AtLastHit
777  assert locs[-1] == cppyy.gbl.edm4hep.TrackState.AtFirstHit
778 
779  assert perigee is not None
780  rp = perigee.referencePoint
781  assert rp.x == 0.0
782  assert rp.y == 0.0
783  assert rp.z == 0.0
784  assert abs(perigee.D0) < 1e0
785  assert abs(perigee.Z0) < 1e1