ATLAS Offline Software
Loading...
Searching...
No Matches
PoolWriteConfig.py
Go to the documentation of this file.
1"""Configuration for POOL file writing
2
3Copyright (C) 2002-2024 CERN for the benefit of the ATLAS collaboration
4"""
5
6from AthenaConfiguration.AccumulatorCache import AccumulatorCache
7
8import ROOT
9
10def _overrideTreeAutoFlush(logger, flags, stream, value):
11 """Helper function to override TreeAutoFlush from flags."""
12 if not flags.Output.TreeAutoFlush or not isinstance(flags.Output.TreeAutoFlush, dict):
13 return value
14
15 if stream not in flags.Output.TreeAutoFlush:
16 return value
17
18 override = flags.Output.TreeAutoFlush[stream]
19 if override is not None:
20 logger.info('Overriding TreeAutoFlush value for stream "%s" from %d to %d', stream, value, override)
21 return override
22
23 return value
24
26 """
27 Helper to get all the streams from configuration flags
28 For each stream that's configured to be written out
29 we have two flags w/ the following convention:
30 + Output.{STREAM}FileName
31 + Output.doWrite{STREAM}
32 """
33 result = []
34 for key, value in flags._flagdict.items():
35 if key.startswith("Output.") and key.endswith("FileName") and value.get():
36 stream = key.removeprefix("Output.").removesuffix("FileName")
37 if stream not in ["HIST"]: # AthenaPool is not responsible for HIST storage settings
38 result.append(stream)
39 return result
40
41
42@AccumulatorCache
43def PoolWriteCfg(flags):
44 """Return ComponentAccumulator configured to Write POOL files"""
45 # based on WriteAthenaPool._configureWriteAthenaPool
46
47 from AthenaCommon.Logging import logging
48 logger = logging.getLogger( 'PoolWriteCfg' )
49
50 PoolAttributes = []
51 # Switch off splitting by setting default SplitLevel to 0
52 PoolAttributes += ["DEFAULT_SPLITLEVEL ='0'"]
53
54 # Set as default the member-wise streaming, ROOT default
55 PoolAttributes += ["STREAM_MEMBER_WISE = '1'"]
56
57 # Set the default container type
58 PoolAttributes += [f"DEFAULT_CONTAINER_TYPE = '{ROOT.pool.DbType.getType(flags.Output.DefaultContainerType).type()}'"]
59
60 # Increase default BasketSize to 32K, ROOT default (but overwritten by POOL)
61 PoolAttributes += ["DEFAULT_BUFFERSIZE = '32000'"]
62
63 # Set POOLContainerForm(DataHeaderForm) split level to 0
64 PoolAttributes += ["ContainerName = 'TTree=POOLContainerForm(DataHeaderForm)'; CONTAINER_SPLITLEVEL = '0'"]
65 PoolAttributes += ["TREE_BRANCH_OFFSETTAB_LEN ='100'"]
66
67 oneDHForm = flags.Output.OneDataHeaderForm
68
69 # Kept in sync with RecoUtils.py
70 from AthenaPoolCnvSvc import PoolAttributeHelper as pah
71
72 # Defaults for common formats
73 # Stream : [compression algorithm, compression level, auto flush, split level, dyn split level]
74 defaults = {
75 "EVNT" : [2, 1, 500, 0, 0],
76 "EVNT_TR" : [2, 1, 1, 0, 0],
77 "HITS" : [2, 1, 10, 0, 0],
78 "RDO" : [2, 1, 10, 0, 0],
79 "ESD" : [2, 1, 10, 0, 0],
80 "AOD" : [2, 1, 100, 0, 0],
81 "DAOD_PHYSVAL" : [5, 5, 100, 0, 1],
82 "DAOD_PHYS" : [5, 5, 500, 0, 1],
83 "DAOD_PHYSLITE" : [5, 5, 500, 1, 1],
84 "DAOD_TRUTH3" : [5, 5, 500, 1, 1],
85 "D2AOD_PHYSLITE" : [5, 5, 500, 1, 1],
86 }
87
88 # RNTuple output: different compression for some formats, TTree keeps the defaults above
89 # Stream : [compression algorithm, compression level]
90 rntupleCompression = {
91 "DAOD_PHYS" : [5, 9],
92 "DAOD_PHYSLITE" : [5, 9],
93 }
94
95 # Metadata containers needed for augmentations
96 OutputMetadataContainers = []
97
98 # Loop over all streams and set the appropriate attributes
99 fileFlushSetting = {}
100 maxAutoFlush = -1
101 for stream in _getStreamsFromFlags(flags):
102
103 # Get the file name - Guaranteed to exist at this point
104 fileName = getattr(flags.Output, f"{stream}FileName")
105
106 # Get the ROOT settings to be applied
107 compAlg, compLvl, autoFlush, splitLvl, dynSplitLvl = 2, 1, 10, 0, 0 # Defaults: LZMA, Level 1, AutoFlush 10, No Splitting
108 if stream in defaults:
109 compAlg, compLvl, autoFlush, splitLvl, dynSplitLvl = defaults[stream]
110 elif "DAOD" in stream:
111 compAlg, compLvl, autoFlush, splitLvl, dynSplitLvl = 5, 5, 100, 0, 1 # Change the defaults for DAODs
112 elif "D2AOD" in stream:
113 compAlg, compLvl, autoFlush, splitLvl, dynSplitLvl = 5, 5, 500, 1, 1 # Change the defaults for D2AODs
114 if "RNTUPLE" in flags.Output.DefaultContainerType and stream in rntupleCompression:
115 compAlg, compLvl = rntupleCompression[stream]
116
117 # For temporary streams/files we use either ZLIB or ZSTD for the compression algorithm to save CPU cycles
118 # Temporary in this context might mean one of three things:
119 # a) Outputs of intermediate steps of chained workflows (file name begins with tmp.),
120 # b) Outputs of workers in AthenaMP jobs that are to be merged (file name ends with _000), and
121 # c) Any output stream that is marked by the user as being temporary (via the CA flag Output.TemporaryStreams)
122 # The ultimate goal is to reconcile all three cases and propagate the information between the job transform
123 # and the job configuration (CA) so that we don't need to rely on the file names here...
124 isTemporaryStream = fileName.endswith('_000') or fileName.startswith('tmp.') or stream in flags.Output.TemporaryStreams
125 tempFileCompressionSetting = (5,1) # ZSTD at level 1
126 if isTemporaryStream:
127 # Outputs created in certain workflows are read with older ROOT versions.
128 # E.g., temporary RDO files that are used in Run-2 simulation.
129 # For those, we have to use ZLIB
130 from AthenaConfiguration.Enums import LHCPeriod
131 if "RDO" in stream and flags.hasCategory("GeoModel") and flags.GeoModel.Run < LHCPeriod.Run3:
132 tempFileCompressionSetting = (1,1) # ZLIB at level 1
133 logger.info(f"Stream {stream} is marked as temporary, overwriting the compression settings to {tempFileCompressionSetting}")
134 compAlg, compLvl = tempFileCompressionSetting if isTemporaryStream else (compAlg, compLvl)
135
136 # See if the user asked for the AutoFlush to be overwritten
137 autoFlush = _overrideTreeAutoFlush(logger, flags, stream, autoFlush)
138
139 # Print some debugging information
140 logger.debug(f"{fileName=} {stream=} {compAlg=} {compLvl=} {autoFlush=} {splitLvl=} {dynSplitLvl=}")
141
142 # Set the Collection/Container prefixes (make configurable?)
143 outputCollection = "POOLContainer"
144 poolContainerPrefix = "CollectionTree"
145
146 # Check to see if this stream is an augmentation
147 # Only set file-level attributes for the owning stream
148 isAugmentation = flags.hasFlag(f"Output.{stream}ParentStream")
149 if not isAugmentation:
150 # Set the Compression attributes
151 PoolAttributes += [ pah.setFileCompAlg( fileName, compAlg ) ]
152 PoolAttributes += [ pah.setFileCompLvl( fileName, compLvl ) ]
153
154 # By default use a maximum basket buffer size of 128k and minimum buffer entries of 10 for (D)AODs
155 if "AOD" in stream:
156 PoolAttributes += [ pah.setMaxBufferSize( fileName, "131072" ) ]
157 PoolAttributes += [ pah.setMinBufferEntries( fileName, "10" ) ]
158 else:
159 # Changes in this else block need to be coordinated w/ OutputStreamConfig!
160 # Set the master index
161 PoolAttributes += [ f"DatabaseName = '{fileName}'; INDEX_MASTER = 'POOLContainer(DataHeader)'" ]
162
163 # Set the Collection/Container prefixes
164 outputCollection += f"_{stream}"
165 poolContainerPrefix += f"_{stream}"
166 OutputMetadataContainers += [f"MetaData_{stream}"]
167
168 # Set the AutoFlush & Maximum Size attributes
169 PoolAttributes += [ pah.setTreeAutoFlush( fileName, poolContainerPrefix, autoFlush ) ]
170 PoolAttributes += [ pah.setTreeAutoFlush( fileName, outputCollection, autoFlush ) ]
171 PoolAttributes += [ pah.setTreeAutoFlush( fileName, "POOLContainerForm", autoFlush ) ]
172 PoolAttributes += [ pah.setTreeMaxSize( fileName, "*", "1099511627776L" ) ] # 1 TB
173 if flags.MP.UseSharedWriter and flags.MP.UseParallelCompression:
174 fileFlushSetting[fileName] = ( flags.MP.SharedWriter.FileFlushSetting.get(fileName, autoFlush) )
175 logger.info(f"Setting auto write for {fileName} to {fileFlushSetting[fileName]} events")
176
177 # Set the Spit Level attributes
178 PoolAttributes += [ pah.setContainerSplitLevel( fileName, poolContainerPrefix, splitLvl ) ]
179 PoolAttributes += [ pah.setContainerSplitLevel( fileName, "Aux.", splitLvl ) ]
180 PoolAttributes += [ pah.setContainerSplitLevel( fileName, "Dyn.", dynSplitLvl ) ]
181
182 # ROOT inadvertently broke forward compatibility in v6.30+ (see root/issues/15964)
183 # This workaround is needed so that older releases can read files created by the new ones
184 # For more information see ATEAM-1001
185 if "EVNT" in stream or "RDO" in stream:
186 PoolAttributes += [ f"DatabaseName = '{fileName}'; FILEFORWARD_COMPATIBILITY = '1'" ]
187 # also for compatibility with rel21 disable single DataHeaderForm
188 oneDHForm = False
189
190 # Find the maximum AutoFlush across all formats
191 maxAutoFlush = max(maxAutoFlush, autoFlush)
192
193 # If we don't have "enough" events, disable parallelCompression if we're using SharedWriter
194 # In this context, "enough" means each worker has a chance to make at least one flush to the disk
195 useParallelCompression = flags.MP.UseSharedWriter and flags.MP.UseParallelCompression
196 if useParallelCompression:
197 # Now compute the total number of events this job will process
198 requestedEvents = flags.Exec.MaxEvents
199 availableEvents = flags.Input.FileNentries - flags.Exec.SkipEvents
200 totalEntries = availableEvents if requestedEvents == -1 else min( availableEvents, requestedEvents )
201 if ( totalEntries > 0 ) and ( maxAutoFlush > 0 ) and ( maxAutoFlush * flags.Concurrency.NumProcs > totalEntries ):
202 logger.info( "Not enough events to process, disabling parallel compression for SharedWriter!" )
203 logger.info( f"Processing {totalEntries} events in {flags.Concurrency.NumProcs} workers "
204 f"and a maximum (across all outputs) AutoFlush of {maxAutoFlush}")
205 useParallelCompression = False
206
207 if flags.MP.UseSharedReader or flags.MP.UseSharedWriter:
208 from AthenaPoolCnvSvc.PoolCommonConfig import AthenaPoolSharedIOCnvSvcCfg
209 return AthenaPoolSharedIOCnvSvcCfg(flags,
210 PoolAttributes=PoolAttributes,
211 ParallelCompression=useParallelCompression,
212 OutputMetadataContainers=OutputMetadataContainers,
213 OneDataHeaderForm=oneDHForm,
214 FileFlushSetting=fileFlushSetting,
215 PoolContainerNamingScheme=("Canonical" if "RNTUPLE" in flags.Output.DefaultContainerType else "Historical"))
216 else:
217 from AthenaPoolCnvSvc.PoolCommonConfig import AthenaPoolCnvSvcCfg
218 return AthenaPoolCnvSvcCfg(flags,
219 PoolAttributes=PoolAttributes,
220 OneDataHeaderForm=oneDHForm,
221 PoolContainerNamingScheme=("Canonical" if "RNTUPLE" in flags.Output.DefaultContainerType else "Historical"))
#define min(a, b)
Definition cfImp.cxx:40
#define max(a, b)
Definition cfImp.cxx:41
_overrideTreeAutoFlush(logger, flags, stream, value)