ATLAS Offline Software
Loading...
Searching...
No Matches
DumpGeoConfig.py
Go to the documentation of this file.
1# Copyright (C) 2002-2026 CERN for the benefit of the ATLAS collaboration
2
3#----------------------------------------------------------------
4# Author: Riccardo Maria BIANCHI <riccardo.maria.bianchi@cern.ch>
5# Initial version: Feb 2024
6#
7# Main updates:
8# - 2025, Feb -- Riccardo Maria BIANCHI <riccardo.maria.bianchi@cern.ch>
9# Added dedicated DumpGeo flags as GeoModel.DumpGeo;
10# also, support the use of DumpGeo transforms and other
11# Athena jobs -- that is, not standalone. This is useful
12# when we want to dump the geometry that comes out as the
13# output of an Athena job.
14#----------------------------------------------------------------
15import os, sys
16
17# Set the CA environment
18from AthenaConfiguration.ComponentAccumulator import (
19 ComponentAccumulator,
20 ConfigurationError,
21)
22from AthenaConfiguration.ComponentFactory import CompFactory
23
24# Set the Athena Logger
25from AthenaCommon.Logging import logging
26_logger = logging.getLogger('DumpGeo')
27
28def configureGeometry(flags, cfg):
29
30 # Beam pipe
31 if flags.Detector.GeometryBpipe:
32 from BeamPipeGeoModel.BeamPipeGMConfig import BeamPipeGeometryCfg
33 cfg.merge(BeamPipeGeometryCfg(flags))
34
35 # Inner Detectors
36 if flags.Detector.GeometryPixel:
37 from PixelGeoModel.PixelGeoModelConfig import PixelReadoutGeometryCfg
38 cfg.merge(PixelReadoutGeometryCfg(flags))
39 # TODO: do we need to set this separately?
40 # if flags.Detector.GeometryBCM:
41
42 if flags.Detector.GeometrySCT:
43 from SCT_GeoModel.SCT_GeoModelConfig import SCT_ReadoutGeometryCfg
44 cfg.merge(SCT_ReadoutGeometryCfg(flags))
45
46 if flags.Detector.GeometryTRT:
47 from TRT_GeoModel.TRT_GeoModelConfig import TRT_ReadoutGeometryCfg
48 cfg.merge(TRT_ReadoutGeometryCfg(flags))
49
50 # InDetServMat
51 # Trigger the build of the InDetServMat geometry
52 # if any ID subsystems have been enabled
53 if flags.Detector.GeometryID:
54 from InDetServMatGeoModel.InDetServMatGeoModelConfig import (
55 InDetServiceMaterialCfg)
56 cfg.merge(InDetServiceMaterialCfg(flags))
57
58 # Calorimeters
59 if flags.Detector.GeometryLAr:
60 from LArGeoAlgsNV.LArGMConfig import LArGMCfg
61 cfg.merge(LArGMCfg(flags))
62
63 if flags.Detector.GeometryTile:
64 from TileGeoModel.TileGMConfig import TileGMCfg
65 #flags.Tile.forceFullGeometry = True
66 cfg.merge(TileGMCfg(flags))
67 # We must set the "FULL" geometry explicitly, otherwise the "RECO" version will be used by default,
68 # which is almost 'empty' (just the first level of child volumes is created for the "RECO" geo).
69 cfg.getService("GeoModelSvc").DetectorTools["TileDetectorTool"].GeometryConfig="FULL"
70 # TODO: do we need to set this separately?
71 # if flags.Detector.GeometryMBTS:
72
73 # Muon spectrometer
74 if flags.Detector.GeometryMuon:
75 from MuonConfig.MuonGeometryConfig import MuonGeoModelCfg
76 cfg.merge(MuonGeoModelCfg(flags))
77
78 # HGTD (defined only for Run4 geometry tags)
79 if flags.Detector.GeometryHGTD:
80 #set up geometry
81 if flags.HGTD.Geometry.useGeoModelXml:
82 from HGTD_GeoModelXml.HGTD_GeoModelConfig import HGTD_SimulationGeometryCfg
83 else:
84 from HGTD_GeoModel.HGTD_GeoModelConfig import HGTD_SimulationGeometryCfg
85 cfg.merge(HGTD_SimulationGeometryCfg(flags))
86
87 # ITk (defined only for Run4 geometry tags)
88 if flags.Detector.GeometryITkPixel:
89 from PixelGeoModelXml.ITkPixelGeoModelConfig import ITkPixelReadoutGeometryCfg
90 cfg.merge(ITkPixelReadoutGeometryCfg(flags))
91 if flags.Detector.GeometryITkStrip:
92 from StripGeoModelXml.ITkStripGeoModelConfig import ITkStripReadoutGeometryCfg
93 cfg.merge(ITkStripReadoutGeometryCfg(flags))
94 # TODO: do we need to set those separately?
95 # if flags.Detector.GeometryBCMPrime:
96 # if flags.Detector.GeometryPLR:
97
98 # Cavern (disabled by default)
99 if flags.Detector.GeometryCavern:
100 from AtlasGeoModel.CavernGMConfig import CavernGeometryCfg
101 cfg.merge(CavernGeometryCfg(flags))
102
103 # Forward detectors (disabled by default)
104 if flags.Detector.GeometryLucid or flags.Detector.GeometryALFA or flags.Detector.GeometryAFP or flags.Detector.GeometryFwdRegion :
105 from AtlasGeoModel.ForDetGeoModelConfig import ForDetGeometryCfg
106 cfg.merge(ForDetGeometryCfg(flags))
107 if flags.Detector.GeometryZDC:
108 from ZDC_GeoM.ZdcGeoModelConfig import ZDC_DetToolCfg
109 cfg.merge(ZDC_DetToolCfg(flags))
110
111 # Temporary 'hack':
112 # Replace EllipticTube with Box,
113 # to bypass a crash due to lack of support
114 # for EllipticTube in GeoModelIO
115 # See: https://its.cern.ch/jira/browse/ATLASSIM-7263
116 if "ForwardRegionGeoModelTool" in cfg.getService("GeoModelSvc").DetectorTools:
117 cfg.getService("GeoModelSvc").DetectorTools["ForwardRegionGeoModelTool"].vp1Compatibility=True
118
119
121 if "AtlasVersion" in os.environ:
122 return os.environ["AtlasVersion"]
123 if "AtlasBaseVersion" in os.environ:
124 return os.environ["AtlasBaseVersion"]
125 return "Unknown"
126
127
129 """Return the configured DumpGeo SQLite output file name."""
130 if flags.GeoModel.DumpGeo.OutputFileName:
131 return flags.GeoModel.DumpGeo.OutputFileName
132
133 output_file = f"geometry-{flags.GeoModel.AtlasVersion}"
134 filter_det_managers = flags.GeoModel.DumpGeo.FilterDetManagers
135 if filter_det_managers:
136 output_file += "-" + "-".join(filter_det_managers)
137
138 return output_file + ".db"
139
140
141def resolveDumpGeoGeometryTag(det_descr, configured_tag, fallback_tag):
142 """Resolve the geometry tag using the DumpGeo command-line precedence."""
143 if det_descr:
144 return det_descr
145 if configured_tag:
146 return configured_tag
147 return fallback_tag
148
149
150def validateDumpGeoOutputFile(output_file, force_overwrite):
151 """Reject an existing output file unless overwrite is enabled."""
152 if os.path.exists(output_file) and not force_overwrite:
153 raise ConfigurationError(
154 f"DumpGeo output file '{output_file}' already exists. "
155 "Move or remove it, or enable "
156 "'GeoModel.DumpGeo.ForceOverwrite' "
157 "(or use the '-f' option from the command line)."
158 )
159
160
161def zdcGeometryWarning(flags, default_geometry_tags):
162 """Return logger arguments for an incompatible ZDC geometry tag."""
163 if not flags.Detector.GeometryZDC:
164 return None
165
166 known_zdc_tags = {
167 default_geometry_tags.RUN2_ZDC,
168 default_geometry_tags.RUN3_ZDC23,
169 default_geometry_tags.RUN3_ZDC24,
170 }
171 if flags.GeoModel.AtlasVersion in known_zdc_tags:
172 return None
173
174 return (
175 "ZDC geometry was enabled with geometry tag '%s', which may "
176 "not contain a ZDC GeoDB payload. Consider using "
177 "--detDescr=%s or --detDescr=%s for Run 3.",
178 flags.GeoModel.AtlasVersion,
179 default_geometry_tags.RUN3_ZDC23,
180 default_geometry_tags.RUN3_ZDC24,
181 )
182
183
184def logZDCGeometryWarning(warning, repeated=False):
185 """Log a ZDC geometry warning, optionally as an end-of-run reminder."""
186 if not warning:
187 return
188 if repeated:
189 _logger.warning("Repeating the earlier ZDC geometry warning:")
190 _logger.warning(*warning)
191
192
193def DumpGeoCfg(flags, name="DumpGeoAlg", **kwargs):
194 if _logger.isEnabledFor(logging.DEBUG):
195 _logger.debug(
196 "Dumping the 'GeoModel.DumpGeo' configuration flags:"
197 )
198 flags.dump("GeoModel.DumpGeo")
199
200 # Debug messages
201 _logger.debug("kwargs: %s", kwargs)
202
203 # set additional DumpGeo Alg's properties
204 _logger.verbose("Using ATLAS/Athena version: %s", getATLASVersion())
205 _logger.verbose("Using GeoModel ATLAS version: %s", flags.GeoModel.AtlasVersion)
206 kwargs.setdefault("AtlasRelease", getATLASVersion())
207 kwargs.setdefault("AtlasVersion", flags.GeoModel.AtlasVersion)
208
209 # Set the user's choice to see the content of the Treetops
210 if flags.GeoModel.DumpGeo.ShowTreetopContent:
211 kwargs.setdefault("ShowTreetopContent", True)
212
213 # Configure DetectorManager filtering independently of the output file
214 # name. In particular, a user-defined file name must not disable the
215 # filter passed to the C++ algorithm.
216 filterDetManagers = flags.GeoModel.DumpGeo.FilterDetManagers
217 if filterDetManagers:
218 _logger.info(
219 "+++ Filtering on these GeoModel 'Detector Managers': '%s'",
220 filterDetManagers,
221 )
222 kwargs.setdefault("UserFilterDetManager", filterDetManagers)
223
224 # Set the name of the output '.db' file.
225 configuredOutFileName = dumpGeoOutputFileName(flags)
226 if not flags.GeoModel.DumpGeo.OutputFileName:
227 _logger.info(
228 "+++ Dumping this Detector Description geometry TAG: '%s'",
229 flags.GeoModel.AtlasVersion,
230 )
231
232 # Set the output file name variable in the C++ code
233 kwargs.setdefault("OutSQLiteFileName", configuredOutFileName)
234
235 # The final algorithm property is authoritative. A caller can override the
236 # flag-derived filename through kwargs, so validation and deletion must use
237 # the same path that the C++ algorithm will write.
238 outFileName = kwargs["OutSQLiteFileName"]
239 kwargs.setdefault(
240 "ForceOverwrite",
241 flags.GeoModel.DumpGeo.ForceOverwrite,
242 )
243 forceOverwrite = kwargs["ForceOverwrite"]
244
245 # Reject an existing file unless overwrite is enabled. Destructive removal
246 # is deliberately deferred to DumpGeo::initialize(), immediately before
247 # the C++ algorithm opens the output database.
249 outFileName,
250 forceOverwrite,
251 )
252
253 # Schedule the DumpGeo Athena Algorithm
254 result = ComponentAccumulator()
255 the_alg = CompFactory.DumpGeo(name=name, **kwargs)
256 result.addEventAlgo(the_alg, primary=True)
257 return result
258
259
260if __name__=="__main__":
261 # Run with e.g. python -m DumpGeo.DumpGeoConfig --detDescr=<ATLAS-geometry-tag> --filterDetManagers=[<list of tree tops>]
262
263 from AthenaConfiguration.TestDefaults import defaultGeometryTags
264
265 # ++++ Firstly we setup flags ++++
266 # +++ Set the Athena Flags
267 from AthenaConfiguration.AllConfigFlags import initConfigFlags
268 flags = initConfigFlags()
269
270 flags.Exec.MaxEvents = 0
271 # ^ We do not need any events to get the GeoModel tree from the GeoModelSvc.
272 # So, we don't need to run on any events,
273 # and we don't need to trigger any execute() Athena methods either.
274 # So, we set 'EvtMax' to 0 and Athena will skip the 'execute' phase;
275 # only the 'finalize' step will be run after the 'init'.
276 # -- Note:
277 # Also, if we run on events (even on 1 event) and we dump the Pixel
278 # as part of the DetectorManager filter, then we get a crash because
279 # the PixelDetectorManager complains during the 'execute' phase,
280 # because we 'stole' a handle on its TreeTop,
281 # which contains a FullPhysVol and cannot be shared.
282
283 flags.Concurrency.NumThreads = 0
284 # ^ DumpGeo will not work with the scheduler, since its condition/data dependencies are not known in advance
285 # More in details: the scheduler needs to know BEFORE the event, what the dependencies of each Alg are.
286 # So for DumpGeo, no dependencies are declared, which means the conditions data is not there.
287 # So when I load tracks, the geometry is missing and it crashes.
288 # Turning off the scheduler (with NumThreads=0) fixes this.
289
290 # +++ Set custom CLI parameters for DumpGeo when ran as a standalone program
291 # (that is, from the command line,
292 # and not as part of an Athena Transform or job)
293 parser = flags.getArgumentParser(description="Dump the detector geometry to a GeoModel-based SQLite '.db' file.")
294 parser.prog = 'dump-geo'
295 # here we extend the parser with CLI options specific to DumpGeo
296 parser.add_argument(
297 "--detDescr",
298 default=None,
299 help=(
300 "Override the ATLAS geometry tag. This is a convenience alias "
301 "for 'GeoModel.AtlasVersion=TAG'. If omitted, the generic flag "
302 "or input-file metadata is used."
303 ),
304 metavar="TAG",
305 )
306 parser.add_argument("--outFilename", default="",
307 help="Here you can set a custom name for the output '.db' file. It will replace the name that is built with the geometry tag and the list of filtered Detector Managers, if any.", metavar="FILENAME")
308 # parser.add_argument("--filterTreeTops", help="Only output the GeoModel Tree Tops specified in the FILTER list; input is a comma-separated list")
309 parser.add_argument("--filterDetManagers", help="Only output the GeoModel Detector Managers specified in the FILTER list; input is a comma-separated list")
310 parser.add_argument("-f", "--forceOverwrite",
311 help="Force to overwrite an existing SQLite output file with the same name, if any", action = 'store_true')
312 parser.add_argument("--showTreetopContent",
313 help="Show the content of the Treetops --- (by default, only the list of Treetops is shown)", action = 'store_true')
314 parser.add_argument("--debugCA", help="Debug the CA configuration: print flags, tools, ... --- mainly, for DumpGeo developers. '1' prints a subset of the CA flags, '2' prints all of them.")
315
316 from AthenaConfiguration.TestDefaults import defaultTestFiles
317 parser.set_defaults(filesInput=f"{defaultTestFiles.EVNT[0]}")
318 args = flags.fillFromArgs(parser=parser)
319
320 # +++ Get CLI parameters and set the corresponding configuration flags
321 # Get the user's custom file name, if set;
322 # this will replace the filename computed
323 # from the geometry tag and the filtered volumes
324 if args.outFilename:
325 flags.GeoModel.DumpGeo.OutputFileName = args.outFilename
326 if args.filterDetManagers:
327 flags.GeoModel.DumpGeo.FilterDetManagers = [
328 manager.strip()
329 for manager in args.filterDetManagers.split(",")
330 if manager.strip()
331 ]
332 if args.showTreetopContent:
333 flags.GeoModel.DumpGeo.ShowTreetopContent = True
334 if args.forceOverwrite:
335 flags.GeoModel.DumpGeo.ForceOverwrite = True
336
337 # A custom filename is already final and can be checked without resolving
338 # the geometry tag, which may otherwise require a metadata lookup.
339 if flags.GeoModel.DumpGeo.OutputFileName:
341 flags.GeoModel.DumpGeo.OutputFileName,
342 flags.GeoModel.DumpGeo.ForceOverwrite,
343 )
344
345 # Resolve the geometry tag with this precedence: an explicit --detDescr,
346 # the generic GeoModel.AtlasVersion flag (including its metadata-derived
347 # value), then the standalone Run-3 fallback. Avoid evaluating the generic
348 # flag when --detDescr was supplied, since doing so may trigger an
349 # unnecessary metadata lookup.
350 configured_geometry_tag = None
351 if not args.detDescr:
352 configured_geometry_tag = flags.GeoModel.AtlasVersion
354 args.detDescr,
355 configured_geometry_tag,
356 defaultGeometryTags.RUN3,
357 )
358 if args.detDescr:
359 _logger.verbose(
360 "+ About to set this detector description tag: '%s'",
361 args.detDescr,
362 )
363 flags.GeoModel.AtlasVersion = geometry_tag
364 _logger.verbose("+ Using detector description tag: '%s'", geometry_tag)
365
366 # Fail as soon as the final geometry tag is known and before detector or
367 # geometry configuration. Resolving a metadata-derived tag necessarily
368 # performs the metadata lookup first. This check deliberately does not
369 # delete an existing file when force overwrite is enabled; deletion remains
370 # in DumpGeoCfg after configuration succeeds.
373 flags.GeoModel.DumpGeo.ForceOverwrite,
374 )
375
376 # +++ Set the empty input
377 _logger.verbose("+ About to set flags related to the input")
378
379 # Empty input is not normal for Athena, so we will need to check
380 # this repeatedly below (the same as with VP1)
381 dumpgeo_empty_input = False
382
383 # This covers the use case where we launch DumpGeo
384 # without input files; e.g., to check the detector description
385 from AthenaConfiguration.AutoConfigFlags import GetFileMD
386
387 input_metadata = GetFileMD(flags.Input.Files)
388 input_geometry_tag = input_metadata.get("GeoAtlas", None)
389
390 # Treat both None and an empty string as a missing geometry tag.
391 dumpgeo_empty_input = (
392 len(flags.Input.Files) == 0 or not input_geometry_tag
393 )
394
395 if dumpgeo_empty_input:
396 from Campaigns.Utils import Campaign
397 from AthenaConfiguration.TestDefaults import (
398 defaultConditionsTags,
399 defaultGeometryTags
400 )
401
402 # NB Must set e.g. ConfigFlags.Input.Runparse_args() Number and
403 # ConfigFlags.Input.TimeStamp before calling the
404 # MainServicesCfg to avoid it attempting auto-configuration
405 # from an input file, which is empty in this use case.
406 # If you don't have it, it (and/or other Cfg routines) complains and crashes.
407 # See also:
408 # https://acode-browser1.usatlas.bnl.gov/lxr/source/athena/InnerDetector/InDetConditions/SCT_ConditionsAlgorithms/python/SCT_DCSConditionsTestAlgConfig.py#0023
409 flags.Input.ProjectName = "mc23_13p6TeV"
410 flags.Input.RunNumbers = [330000]
411 flags.Input.TimeStamps = [1]
412 flags.Input.TypedCollections = []
413
414 # set default CondDB and Geometry version
415 flags.IOVDb.GlobalTag = defaultConditionsTags.RUN3_MC
416 flags.Input.isMC = True
417 flags.Input.MCCampaign = Campaign.Unknown
418
419 _logger.verbose("+ ... Done")
420 _logger.verbose("+ empty input: '%s'", dumpgeo_empty_input)
421
422
423 # +++ Set the detector geometry
424 _logger.verbose("+ About to set the detector flags")
425 # So we can now set up the geometry flags from the input
426 from AthenaConfiguration.DetectorConfigFlags import setupDetectorFlags
427 setupDetectorFlags(
428 flags,
429 None,
430 use_metadata=not dumpgeo_empty_input,
431 toggle_geometry=True,
432 keep_beampipe=True
433 )
434 _logger.verbose("+ ... Done")
435
436 # ZDC geometry is stored only in dedicated geometry tags. Warn rather than
437 # silently replacing the user's selected tag, since the choice between the
438 # available ZDC layouts is significant.
439 zdc_warning = zdcGeometryWarning(flags, defaultGeometryTags)
440 logZDCGeometryWarning(zdc_warning)
441
442 # finalize setting flags: lock them.
443 flags.lock()
444
445 # ++++ Now we setup the actual configuration ++++
446 _logger.verbose("+ Setup main services")
447 from AthenaConfiguration.MainServicesConfig import MainServicesCfg
448 cfg = MainServicesCfg(flags)
449 _logger.verbose("+ ...Done")
450
451 _logger.verbose("+ About to setup geometry")
452 configureGeometry(flags,cfg)
453 _logger.verbose("+ ...Done")
454
455 # debug messages
456 if args.debugCA:
457 debugCAlevel = int(args.debugCA)
458 if debugCAlevel >= 1:
459 _logger.verbose("Debug --- printing flags...")
460 print("\nflags:", flags)
461 for fl in flags:
462 print("fl:", fl)
463 print("\nflags.Tile:", flags.Tile)
464 for fl in flags.Tile:
465 print("fl.Tile:", fl)
466 print(dir(cfg))
467 print("cfg._privateTools: ", cfg._privateTools)
468 print("cfg._publicTools: ", cfg._publicTools)
469 if debugCAlevel >= 2:
470 flags.dump()
471 flags._loadDynaFlags('GeoModel')
472 flags._loadDynaFlags('Detector')
473 flags.dump('Detector.(Geometry|Enable)', True)
474 if debugCAlevel >= 1:
475 _logger.verbose("We're in a debugCA session, flags have been printed out, now exiting...")
476 sys.exit()
477
478 # +++ Configure DumpGeo and run
479 cfg.merge(DumpGeoCfg(flags))
480 status = cfg.run()
481
482 logZDCGeometryWarning(zdc_warning, repeated=True)
483
484 sys.exit(not status.isSuccess())
void print(char *figname, TCanvas *c1)
zdcGeometryWarning(flags, default_geometry_tags)
DumpGeoCfg(flags, name="DumpGeoAlg", **kwargs)
dumpGeoOutputFileName(flags)
resolveDumpGeoGeometryTag(det_descr, configured_tag, fallback_tag)
configureGeometry(flags, cfg)
validateDumpGeoOutputFile(output_file, force_overwrite)
logZDCGeometryWarning(warning, repeated=False)