ATLAS Offline Software
Loading...
Searching...
No Matches
cmake_newanalysisalg.py
Go to the documentation of this file.
1# Copyright (C) 2002-2026 CERN for the benefit of the ATLAS collaboration
2
3# @file PyUtils.scripts.cmt_newanalysisalg
4# @purpose streamline and ease the creation of new athena algs
5# @author Will Buttinger
6# @date February 2017
7
8#Note - this code could use a serious rewrite, I just hacked it together to get something working
9
10__author__ = "Will Buttinger"
11__doc__ = "streamline and ease the creation of new AthAnalysisAlgorithm"
12
13
14import os
15import textwrap
16import subprocess
17import PyUtils.acmdlib as acmdlib
18
20
21 script_template = """\
22#!/usr/bin/env python
23
24# Run this application/script like this:
25# run%(klass)s.py --filesInput file.root --evtMax 100
26# See --help for more arguments and flag options
27
28from AthenaConfiguration.AllConfigFlags import initConfigFlags
29flags = initConfigFlags()
30flags._parser = flags.getArgumentParser(description=\"\"\"My Demo Application\"\"\") # an argparse.ArgumentParser
31flags.parser().add_argument('--accessMode',default="POOLAccess", # can add arguments to the parser as usual
32 choices={"POOLAccess","ClassAccess"},help="Input file reading mode (ClassAccess can be faster but is less supported)")
33# changes to default flag values (done before fillFromArgs so appears in the --help flag system
34flags.Exec.PrintAlgsSequence = True # displays algsequence at start of job
35
36
37args = flags.fillFromArgs() # parse command line arguments
38flags.lock() # lock the flags
39
40
41# configure main services and input file reading
42from AthenaConfiguration.MainServicesConfig import MainServicesCfg
43from AthenaConfiguration.Enums import Format
44cfg = MainServicesCfg(flags)
45if flags.Input.Format is Format.BS:
46 # read RAW (bytestream)
47 from ByteStreamCnvSvc.ByteStreamConfig import ByteStreamReadCfg
48 cfg.merge(ByteStreamReadCfg(flags))
49else:
50 # reading POOL, use argument to decide which read mode
51 if flags.args().accessMode == "POOLAccess":
52 from AthenaPoolCnvSvc.PoolReadConfig import PoolReadCfg
53 cfg.merge(PoolReadCfg(flags))
54 else:
55 from AthenaRootComps.xAODEventSelectorConfig import xAODReadCfg,xAODAccessMode
56 cfg.merge(xAODReadCfg(flags, AccessMode = xAODAccessMode.CLASS_ACCESS))
57
58# configure output ROOT files from Output.HISTOutputs flag (should be of form: "STREAMNAME:file.root")
59from AthenaConfiguration.ComponentFactory import CompFactory
60if flags.Output.HISTFileName != "":
61 outputs = []
62 for file in (flags.Output.HISTFileName if type(flags.Output.HISTFileName)==list else flags.Output.HISTFileName.split(",")):
63 streamName = file.split(":")[0] if ":" in file else "ANALYSIS"
64 fileName = file.split(":")[1] if ":" in file else file
65 outputs += ["{} DATAFILE='{}' OPT='RECREATE'".format(streamName,fileName)]
66 cfg.addService(CompFactory.THistSvc(Output = outputs))
67
68# add our algorithm
69cfg.addEventAlgo(CompFactory.%(klass)s(),sequenceName="AthAlgSeq")
70
71
72# final cfg tweaks before launching:
73if cfg.getAppProps()["EventLoop"]=="AthenaEventLoopMgr":
74 cfg.getService("AthenaEventLoopMgr").IntervalInSeconds = 5 # enable processing rate reporting every 5s
75# suppress logging from some core services that we usually don't care about hearing from
76cfg.getService("MessageSvc").setWarning += ["ClassIDSvc","PoolSvc","AthDictLoaderSvc","AthenaPoolAddressProviderSvc",
77 "ProxyProviderSvc","DBReplicaSvc","MetaDataSvc","MetaDataStore","AthenaPoolCnvSvc",
78 "TagMetaDataStore","EventSelector","CoreDumpSvc","AthMasterSeq","EventPersistencySvc",
79 "ActiveStoreSvc","AthOutSeq","AthRegSeq","FPEAuditor"]
80
81# run the job
82if cfg.run().isFailure():
83 exit(1)
84"""
85
86 alg_hdr_template = """\
87#ifndef %(guard)s
88#define %(guard)s 1
89
90#include "AthAnalysisBaseComps/AthAnalysisAlgorithm.h"
91
92//Example ROOT Includes
93//#include "TTree.h"
94//#include "TH1D.h"
95
96%(namespace_begin)s
97
98class %(klass)s: public ::AthAnalysisAlgorithm {
99 public:
100 %(klass)s( const std::string& name, ISvcLocator* pSvcLocator );
101 virtual ~%(klass)s();
102
103 ///uncomment and implement methods as required
104
105 //IS EXECUTED:
106 virtual StatusCode initialize(); //once, before any input is loaded
107 virtual StatusCode beginInputFile(); //start of each input file, only metadata loaded
108 //virtual StatusCode firstExecute(); //once, after first eventdata is loaded (not per file)
109 virtual StatusCode execute(); //per event
110 //virtual StatusCode endInputFile(); //end of each input file
111 //virtual StatusCode metaDataStop(); //when outputMetaStore is populated by MetaDataTools
112 virtual StatusCode finalize(); //once, after all events processed
113
114
115 ///Other useful methods provided by base class are:
116 ///evtStore() : ServiceHandle to main event data storegate
117 ///inputMetaStore() : ServiceHandle to input metadata storegate
118 ///outputMetaStore() : ServiceHandle to output metadata storegate
119 ///histSvc() : ServiceHandle to output ROOT service (writing TObjects)
120 ///currentFile() : TFile* to the currently open input file
121 ///retrieveMetadata(...): See twiki.cern.ch/twiki/bin/view/AtlasProtected/AthAnalysisBase#ReadingMetaDataInCpp
122
123
124 private:
125
126 //Example algorithm property, see constructor for declaration:
127 //int m_nProperty = 0;
128
129 //Example histogram, see initialize method for registration to output histSvc
130 //TH1D* m_myHist = 0;
131 //TTree* m_myTree = 0;
132
133};
134%(namespace_end)s
135#endif //> !%(guard)s
136"""
137
138 alg_cxx_template = """\
139// %(pkg)s includes
140#include "%(namespace_klass)s.h"
141
142//#include "xAODEventInfo/EventInfo.h"
143
144
145%(namespace_begin)s
146
147%(klass)s::%(klass)s( const std::string& name, ISvcLocator* pSvcLocator ) : AthAnalysisAlgorithm( name, pSvcLocator ){
148
149 //declareProperty( "Property", m_nProperty = 0, "My Example Integer Property" ); //example property declaration
150
151}
152
153
154%(klass)s::~%(klass)s() {}
155
156
157StatusCode %(klass)s::initialize() {
158 ATH_MSG_INFO ("Initializing " << name() << "...");
159 //
160 //This is called once, before the start of the event loop
161 //Retrieves of tools you have configured in the joboptions go here
162 //
163
164 //HERE IS AN EXAMPLE
165 //We will create a histogram and a ttree and register them to the histsvc
166 //Remember to configure the histsvc stream in the joboptions
167 //
168 //m_myHist = new TH1D("myHist","myHist",100,0,100);
169 //CHECK( histSvc()->regHist("/MYSTREAM/myHist", m_myHist) ); //registers histogram to output stream
170 //m_myTree = new TTree("myTree","myTree");
171 //CHECK( histSvc()->regTree("/MYSTREAM/SubDirectory/myTree", m_myTree) ); //registers tree to output stream inside a sub-directory
172
173
174 return StatusCode::SUCCESS;
175}
176
177StatusCode %(klass)s::finalize() {
178 ATH_MSG_INFO ("Finalizing " << name() << "...");
179 //
180 //Things that happen once at the end of the event loop go here
181 //
182
183
184 return StatusCode::SUCCESS;
185}
186
187StatusCode %(klass)s::execute() {
188 ATH_MSG_DEBUG ("Executing " << name() << "...");
189 setFilterPassed(false); //optional: start with algorithm not passed
190
191
192
193 //
194 //Your main analysis code goes here
195 //If you will use this algorithm to perform event skimming, you
196 //should ensure the setFilterPassed method is called
197 //If never called, the algorithm is assumed to have 'passed' by default
198 //
199
200
201 //HERE IS AN EXAMPLE
202 //const xAOD::EventInfo* ei = 0;
203 //CHECK( evtStore()->retrieve( ei , "EventInfo" ) );
204 //ATH_MSG_INFO("eventNumber=" << ei->eventNumber() );
205 //m_myHist->Fill( ei->averageInteractionsPerCrossing() ); //fill mu into histogram
206
207
208 setFilterPassed(true); //if got here, assume that means algorithm passed
209 return StatusCode::SUCCESS;
210}
211
212StatusCode %(klass)s::beginInputFile() {
213 //
214 //This method is called at the start of each input file, even if
215 //the input file contains no events. Accumulate metadata information here
216 //
217
218 //example of retrieval of CutBookkeepers: (remember you will need to include the necessary header files and use statements in requirements file)
219 // const xAOD::CutBookkeeperContainer* bks = 0;
220 // CHECK( inputMetaStore()->retrieve(bks, "CutBookkeepers") );
221
222 //example of IOVMetaData retrieval (see https://twiki.cern.ch/twiki/bin/viewauth/AtlasProtected/AthAnalysisBase#How_to_access_file_metadata_in_C)
223 //float beamEnergy(0); CHECK( retrieveMetadata("/TagInfo","beam_energy",beamEnergy) );
224 //std::vector<float> bunchPattern; CHECK( retrieveMetadata("/Digitiation/Parameters","BeamIntensityPattern",bunchPattern) );
225
226
227
228 return StatusCode::SUCCESS;
229}
230
231%(namespace_end)s
232"""
233
234
235
236@acmdlib.command(
237 name='cmake.new-analysisalg'
238 )
239@acmdlib.argument(
240 'algname',
241 help="name of the new alg"
242 )
243
244def main(args):
245 """create a new AthAnalysisAlgorithm inside the current package. Call from within the package directory
246
247 ex:
248 $ acmd cmake new-analysisalg MyAlg
249 """
250 sc = 0
251
252 full_alg_name = args.algname
253
254 #determine the package from the cwd
255 cwd = os.getcwd()
256 #check that src dir exists and CMakeLists.txt exists (i.e. this is a package)
257 if not os.path.isdir(cwd+"/src") or not os.path.isfile(cwd+"/CMakeLists.txt"):
258 print("ERROR you must call new-analysisalg from within the package you want to add the algorithm to")
259 return -1
260
261
262 full_pkg_name = os.path.basename(cwd)
263 print(textwrap.dedent("""\
264 ::: create alg [%(full_alg_name)s] in pkg [%(full_pkg_name)s]""" %locals()))
265
266
267 #first we must check that CMakeLists.txt file has the AthAnalysisBaseComps dependency in it
268 foundBaseComps=False
269 hasxAODEventInfo=False
270 hasAtlasROOT=False
271 hasAsgTools=False
272 lastUse=0
273 lineCount=0
274 hasLibraryLine=False
275 hasComponentLine=False
276 for line in open('CMakeLists.txt'):
277 lineCount +=1
278 if "atlas_add_library" in line: hasLibraryLine=True
279 if "atlas_add_component" in line: hasComponentLine=True
280
281#GOT THIS FAR WITH EDITING
282
283
284
285
286
287 #following code borrowed from gen_klass
288 hdr = Templates.alg_hdr_template
289 cxx = Templates.alg_cxx_template
290
291 namespace_klass = full_alg_name.replace('::','__')
292 namespace_begin,namespace_end = "",""
293 namespace = ""
294 if full_alg_name.count("::")>0:
295 namespace = full_alg_name.split("::")[0]
296 full_alg_name = full_alg_name.split("::")[1]
297 namespace_begin = "namespace %s {" % namespace
298 namespace_end = "} //> end namespace %s" % namespace
299 pass
300
301 guard = "%s_%s_H" % (full_pkg_name.upper(), namespace_klass.upper())
302
303 d = dict( pkg=full_pkg_name,
304 klass=full_alg_name,
305 guard=guard,
306 namespace_begin=namespace_begin,
307 namespace_end=namespace_end,namespace_klass=namespace_klass,namespace=namespace
308 )
309 fname = os.path.splitext("src/%s"%namespace_klass)[0]
310 #first check doesn't exist
311 if os.path.isfile(fname+'.h'):
312 print("::: ERROR %s.h already exists" % fname)
313 return -1
314 print("::: INFO Creating %s.h" % fname)
315 o_hdr = open(fname+'.h', 'w')
316 o_hdr.writelines(hdr%d)
317 o_hdr.flush()
318 o_hdr.close()
319
320 if os.path.isfile(fname+'.cxx'):
321 print("::: ERROR %s.cxx already exists" % fname)
322 return -1
323 print("::: INFO Creating %s.cxx" % fname)
324 o_cxx = open(fname+'.cxx', 'w')
325 o_cxx.writelines(cxx%d)
326 o_cxx.flush()
327 o_cxx.close()
328
329
330 #now add the algorithm to the _entries.cxx file in the components folder
331 #first check they exist
332 if not os.path.exists("src/components"): os.mkdir("src/components")
333 if not os.path.isfile("src/components/%s_entries.cxx"%full_pkg_name):
334 print("::: INFO Creating src/components/%s_entries.cxx"%full_pkg_name)
335 loadFile = open("src/components/%s_entries.cxx"%full_pkg_name,'w')
336 if len(namespace_begin)>0:
337 d["namespace"] = args.algname.split("::")[0]
338 loadFile.writelines("""
339#include "../%(namespace_klass)s.h"
340DECLARE_COMPONENT(%(namespace)s::%(klass)s )
341"""%d)
342 else:
343 loadFile.writelines("""
344#include "../%(namespace_klass)s.h"
345DECLARE_COMPONENT( %(klass)s )
346"""%d)
347 loadFile.flush()
348 loadFile.close()
349 else:
350 #first check algorithm not already in _entries file
351 inFile=False
352 for line in open("src/components/%s_entries.cxx"%full_pkg_name):
353 if len(namespace_begin)==0 and "DECLARE_COMPONENT" in line and d["klass"] in line: inFile=True
354 if len(namespace_begin)>0 and "DECLARE_COMPONENT" in line and d["klass"] in line and d["namespace"]: inFile=True
355
356 if not inFile:
357 print("::: INFO Adding %s to src/components/%s_entries.cxx"% (args.algname,full_pkg_name))
358 nextAdd=True
359 with open("src/components/%s_entries.cxx"%full_pkg_name, "a") as f:
360 if len(namespace_begin)>0:
361 f.write(""" DECLARE_COMPONENT(%(namespace)s::%(klass)s );"""%d)
362 else:
363 f.write(""" DECLARE_COMPONENT( %(klass)s );"""%d)
364
365
366 full_script_name = "run" + namespace_klass
367 full_alg_name = namespace_klass
368
369 print(textwrap.dedent("""\
370 ::: create script [%(full_script_name)s] for alg [%(full_alg_name)s]""" %locals()))
371
372 e = dict( klass=full_alg_name,
373 inFile=os.environ['ASG_TEST_FILE_MC'],
374 )
375 fname = 'scripts/%s.py' % full_script_name
376 #first check doesn't exist
377 if os.path.isfile(fname):
378 print("::: WARNING %s already exists .. will not overwrite" % fname)
379 else:
380 o_hdr = open(fname, 'w')
381 o_hdr.writelines(Templates.script_template % e)
382 o_hdr.flush()
383 o_hdr.close()
384 os.chmod(fname, 0o755)
385
386 #need to reconfigure cmake so it knows about the new files
387 #rely on the WorkDir_DIR env var for this
388 workDir = os.environ.get("WorkDir_DIR")
389 if workDir is None:
390 print("::: ERROR No WorkDir_DIR env var, did you forget to source the setup.sh script?")
391 print("::: ERROR Please do this and reconfigure cmake manually!")
392 else:
393 print("::: INFO Reconfiguring cmake %s/../." % workDir)
394 res = subprocess.getstatusoutput('cmake %s/../.' % workDir)
395 if res[0]!=0:
396 print("::: WARNING reconfigure unsuccessful. Please reconfigure manually!")
397
398
399 print("::: INFO Please ensure your CMakeLists.txt file has ")
400 print("::: atlas_add_component( %s src/component/*.cxx ... )" % full_pkg_name)
401 print("::: INFO and necessary dependencies declared ")
402 print("::: INFO Minimum dependency is: Control/AthAnalysisBaseComps")
403
void print(char *figname, TCanvas *c1)