SFrame 3.6
|
00001 # $Id: CycleCreators.py 173 2010-05-12 15:49:33Z krasznaa $ 00002 ########################################################################### 00003 # @Project: SFrame - ROOT-based analysis framework for ATLAS # 00004 # # 00005 # @author Stefan Ask <Stefan.Ask@cern.ch> - Manchester # 00006 # @author David Berge <David.Berge@cern.ch> - CERN # 00007 # @author Johannes Haller <Johannes.Haller@cern.ch> - Hamburg # 00008 # @author A. Krasznahorkay <Attila.Krasznahorkay@cern.ch> - CERN/Debrecen # 00009 # # 00010 ########################################################################### 00011 00012 ## @package CycleCreators 00013 # @short Functions for creating a new analysis cycle torso 00014 # 00015 # This package collects the functions used by sframe_create_cycle.py 00016 # to create the torso of a new analysis cycle. Apart from using 00017 # sframe_create_cycle.py, the functions can be used in an interactive 00018 # python session by executing: 00019 # 00020 # <code> 00021 # >>> import CycleCreators 00022 # </code> 00023 00024 ## @short Class creating analysis cycle templates 00025 # 00026 # This class can be used to create a template cycle inheriting from 00027 # SCycleBase. It is quite smart actually. If you call CycleCreator.CreateCycle 00028 # from inside an "SFrame package", it will find the right locations for the 00029 # created files and extend an already existing LinkDef.h file with the 00030 # line for the new cycle. 00031 class CycleCreator: 00032 00033 _headerFile = "" 00034 _sourceFile = "" 00035 00036 def __init__( self ): 00037 self._headerFile = "" 00038 self._sourceFile = "" 00039 00040 ## @short Template for cycle outside of a namespace 00041 # 00042 # This string is used by CreateHeader to create a header file that 00043 # holds a cycle which is not in a namespace. 00044 _header = """// Dear emacs, this is -*- c++ -*- 00045 // $Id: CycleCreators.py 173 2010-05-12 15:49:33Z krasznaa $ 00046 #ifndef %(class)-s_H 00047 #define %(class)-s_H 00048 00049 // SFrame include(s): 00050 #include \"core/include/SCycleBase.h\" 00051 00052 /** 00053 * @short Put short description of class here 00054 * 00055 * Put a longer description over here... 00056 * 00057 * @author Put your name here 00058 * @version $Revision: 173 $ 00059 */ 00060 class %(class)-s : public SCycleBase { 00061 00062 public: 00063 /// Default constructor 00064 %(class)-s(); 00065 /// Default destructor 00066 ~%(class)-s(); 00067 00068 /// Function called at the beginning of the cycle 00069 virtual void BeginCycle() throw( SError ); 00070 /// Function called at the end of the cycle 00071 virtual void EndCycle() throw( SError ); 00072 00073 /// Function called at the beginning of a new input data 00074 virtual void BeginInputData( const SInputData& ) throw( SError ); 00075 /// Function called after finishing to process an input data 00076 virtual void EndInputData ( const SInputData& ) throw( SError ); 00077 00078 /// Function called after opening each new input file 00079 virtual void BeginInputFile( const SInputData& ) throw( SError ); 00080 00081 /// Function called for every event 00082 virtual void ExecuteEvent( const SInputData&, Double_t ) throw( SError ); 00083 00084 private: 00085 // 00086 // Put all your private variables here 00087 // 00088 00089 // Macro adding the functions for dictionary generation 00090 ClassDef( %(class)-s, 0 ); 00091 00092 }; // class %(class)-s 00093 00094 #endif // %(class)-s_H 00095 00096 """ 00097 00098 ## @short Template for cycle in a namespace 00099 # 00100 # This string is used by CreateHeader to create a header file that 00101 # holds a cycle which is in a namespace. 00102 _headerNs = """// Dear emacs, this is -*- c++ -*- 00103 // $Id: CycleCreators.py 173 2010-05-12 15:49:33Z krasznaa $ 00104 #ifndef %(ns)-s_%(class)-s_H 00105 #define %(ns)-s_%(class)-s_H 00106 00107 // SFrame include(s): 00108 #include \"core/include/SCycleBase.h\" 00109 00110 namespace %(ns)-s { 00111 00112 /** 00113 * @short Put short description of class here 00114 * 00115 * Put a longer description over here... 00116 * 00117 * @author Put your name here 00118 * @version $Revision: 173 $ 00119 */ 00120 class %(class)-s : public SCycleBase { 00121 00122 public: 00123 /// Default constructor 00124 %(class)-s(); 00125 /// Default destructor 00126 ~%(class)-s(); 00127 00128 /// Function called at the beginning of the cycle 00129 virtual void BeginCycle() throw( SError ); 00130 /// Function called at the end of the cycle 00131 virtual void EndCycle() throw( SError ); 00132 00133 /// Function called at the beginning of a new input data 00134 virtual void BeginInputData( const SInputData& ) throw( SError ); 00135 /// Function called after finishing to process an input data 00136 virtual void EndInputData ( const SInputData& ) throw( SError ); 00137 00138 /// Function called after opening each new input file 00139 virtual void BeginInputFile( const SInputData& ) throw( SError ); 00140 00141 /// Function called for every event 00142 virtual void ExecuteEvent( const SInputData&, Double_t ) throw( SError ); 00143 00144 private: 00145 // 00146 // Put all your private variables here 00147 // 00148 00149 // Macro adding the functions for dictionary generation 00150 ClassDef( %(ns)-s::%(class)-s, 0 ); 00151 00152 }; // class %(class)-s 00153 00154 } // namespace %(ns)-s 00155 00156 #endif // %(ns)-s_%(class)-s_H 00157 00158 """ 00159 00160 ## @short Function creating an analysis cycle header 00161 # 00162 # This function can be used to create the header file for a new analysis 00163 # cycle. It can correctly create the header file if the cycle name is 00164 # defined like "Ana::AnalysisCycle". In this case it creates a cycle 00165 # called "AnalysisCycle" that is in the C++ namespace "Ana". Multiple 00166 # namespaces such as "Ana::MyAna::AnalysisCycle" are not supported! 00167 # 00168 # @param cycleName Name of the analysis cycle. Can contain the namespace name. 00169 # @param fileName Optional parameter with the output header file name 00170 def CreateHeader( self, cycleName, fileName = "" ): 00171 00172 # Extract the namespace name if it has been specified: 00173 namespace = "" 00174 import re 00175 if re.search( "::", cycleName ): 00176 print "CreateHeader:: We're creating a cycle in a namespace" 00177 m = re.match( "(.*)::(.*)", cycleName ) 00178 namespace = m.group( 1 ) 00179 cycleName = m.group( 2 ) 00180 print "CreateHeader:: Namespace name = " + namespace 00181 00182 # Construct the file name if it has not been specified: 00183 if fileName == "": 00184 fileName = cycleName + ".h" 00185 00186 # Some printouts: 00187 print "CreateHeader:: Cycle name = " + cycleName 00188 print "CreateHeader:: File name = " + fileName 00189 self._headerFile = fileName 00190 00191 # Create a backup of an already existing header file: 00192 import os.path 00193 if os.path.exists( fileName ): 00194 print "CreateHeader:: File \"" + fileName + "\" already exists" 00195 print "CreateHeader:: Moving \"" + fileName + "\" to \"" + \ 00196 fileName + ".backup\"" 00197 import shutil 00198 shutil.move( fileName, fileName + ".backup" ) 00199 00200 # Write the header file: 00201 output = open( fileName, "w" ) 00202 if namespace == "": 00203 output.write( self._header % { 'class' : cycleName } ) 00204 else: 00205 output.write( self._headerNs % { 'class' : cycleName, 00206 'ns' : namespace } ) 00207 00208 return 00209 00210 ## @short Template for cycle outside of a namespace 00211 # 00212 # This string is used by CreateSource to create a source file that 00213 # holds a cycle which is not in a namespace. 00214 _source = """// $Id: CycleCreators.py 173 2010-05-12 15:49:33Z krasznaa $ 00215 00216 // Local include(s): 00217 #include \"%(dir)-s/%(class)-s.h\" 00218 00219 ClassImp( %(class)-s ); 00220 00221 %(class)-s::%(class)-s() 00222 : SCycleBase() { 00223 00224 SetLogName( GetName() ); 00225 } 00226 00227 %(class)-s::~%(class)-s() { 00228 00229 } 00230 00231 void %(class)-s::BeginCycle() throw( SError ) { 00232 00233 return; 00234 00235 } 00236 00237 void %(class)-s::EndCycle() throw( SError ) { 00238 00239 return; 00240 00241 } 00242 00243 void %(class)-s::BeginInputData( const SInputData& ) throw( SError ) { 00244 00245 return; 00246 00247 } 00248 00249 void %(class)-s::EndInputData( const SInputData& ) throw( SError ) { 00250 00251 return; 00252 00253 } 00254 00255 void %(class)-s::BeginInputFile( const SInputData& ) throw( SError ) { 00256 00257 return; 00258 00259 } 00260 00261 void %(class)-s::ExecuteEvent( const SInputData&, Double_t ) throw( SError ) { 00262 00263 return; 00264 00265 } 00266 00267 """ 00268 00269 ## @short Template for cycle in a namespace 00270 # 00271 # This string is used by CreateSource to create a source file that 00272 # holds a cycle which is in a namespace. 00273 _sourceNs = """// $Id: CycleCreators.py 173 2010-05-12 15:49:33Z krasznaa $ 00274 00275 // Local include(s): 00276 #include \"%(dir)-s/%(class)-s.h\" 00277 00278 ClassImp( %(ns)-s::%(class)-s ); 00279 00280 namespace %(ns)-s { 00281 00282 %(class)-s::%(class)-s() 00283 : SCycleBase() { 00284 00285 SetLogName( GetName() ); 00286 } 00287 00288 %(class)-s::~%(class)-s() { 00289 00290 } 00291 00292 void %(class)-s::BeginCycle() throw( SError ) { 00293 00294 return; 00295 00296 } 00297 00298 void %(class)-s::EndCycle() throw( SError ) { 00299 00300 return; 00301 00302 } 00303 00304 void %(class)-s::BeginInputData( const SInputData& ) throw( SError ) { 00305 00306 return; 00307 00308 } 00309 00310 void %(class)-s::EndInputData( const SInputData& ) throw( SError ) { 00311 00312 return; 00313 00314 } 00315 00316 void %(class)-s::BeginInputFile( const SInputData& ) throw( SError ) { 00317 00318 return; 00319 00320 } 00321 00322 void %(class)-s::ExecuteEvent( const SInputData&, Double_t ) throw( SError ) { 00323 00324 return; 00325 00326 } 00327 00328 } // namespace %(ns)-s 00329 00330 """ 00331 00332 ## @short Function creating the analysis cycle source file 00333 # 00334 # This function creates the source file that works with the header created 00335 # by CreateHeader. It is important that CreateHeader is executed before 00336 # this function, as it depends on knowing where the header file is 00337 # physically. (To include it correctly in the source file.) It can 00338 # handle cycles in namespaces, just like CreateHeader. (The same 00339 # rules apply.) 00340 # 00341 # @param cycleName Name of the analysis cycle. Can contain the namespace name. 00342 # @param fileName Optional parameter with the output source file name 00343 def CreateSource( self, cycleName, fileName = "" ): 00344 00345 # Extract the namespace name if it has been specified: 00346 namespace = "" 00347 import re 00348 if re.search( "::", cycleName ): 00349 print "CreateSource:: We're creating a cycle in a namespace" 00350 m = re.match( "(.*)::(.*)", cycleName ) 00351 namespace = m.group( 1 ) 00352 cycleName = m.group( 2 ) 00353 print "CreateSource:: Namespace name = " + namespace 00354 00355 # Construct the file name if it has not been specified: 00356 if fileName == "": 00357 fileName = cycleName + ".cxx" 00358 00359 # Some printouts: 00360 print "CreateSource:: Cycle name = " + cycleName 00361 print "CreateSource:: File name = " + fileName 00362 self._sourceFile = fileName 00363 00364 # The following is a tricky part. Here I evaluate how the source file 00365 # will be able to include the previously created header file. 00366 # Probably a Python guru could've done it in a shorter way, but 00367 # at least it works. 00368 import os.path 00369 hdir = os.path.dirname( self._headerFile ) 00370 sdir = os.path.dirname( self._sourceFile ) 00371 prefix = os.path.commonprefix( [ self._headerFile, self._sourceFile ] ) 00372 00373 hdir = hdir.replace( prefix, "" ) 00374 sdir = sdir.replace( prefix, "" ) 00375 00376 nup = sdir.count( "/" ); 00377 nup = nup + 1 00378 dir = "" 00379 for i in range( 0, nup ): 00380 dir = dir.join( [ "../", hdir ] ) 00381 00382 # Create a backup of an already existing header file: 00383 if os.path.exists( fileName ): 00384 print "CreateHeader:: File \"" + fileName + "\" already exists" 00385 print "CreateHeader:: Moving \"" + fileName + "\" to \"" + \ 00386 fileName + ".backup\"" 00387 import shutil 00388 shutil.move( fileName, fileName + ".backup" ) 00389 00390 # Write the source file: 00391 output = open( fileName, "w" ) 00392 if namespace == "": 00393 output.write( self._source % { 'dir' : dir, 00394 'class' : cycleName } ) 00395 else: 00396 output.write( self._sourceNs % { 'dir' : dir, 00397 'class' : cycleName, 00398 'ns' : namespace } ) 00399 00400 return 00401 00402 ## @short Function adding link definitions for rootcint 00403 # 00404 # Each new analysis cycle has to declare itself in a so called "LinkDef 00405 # file". This makes sure that rootcint knows that a dictionary should 00406 # be generated for this C++ class. 00407 # 00408 # This function is also quite smart. If the file name specified does 00409 # not yet exist, it creates a fully functionaly LinkDef file. If the 00410 # file already exists, it just inserts one line declaring the new 00411 # cycle into this file. 00412 # 00413 # @param cycleName Name of the analysis cycle. Can contain the namespace name. 00414 # @param fileName Optional parameter with the LinkDef file name 00415 def AddLinkDef( self, cycleName, fileName = "LinkDef.h" ): 00416 00417 import os.path 00418 if os.path.exists( fileName ): 00419 print "AddLinkDef:: Extending already existing file \"" + fileName + "\"" 00420 # Read in the already existing file: 00421 output = open( fileName, "r" ) 00422 lines = output.readlines() 00423 output.close() 00424 00425 # Find the "#endif" line: 00426 endif_line = "" 00427 import re 00428 for line in lines: 00429 if re.search( "#endif", line ): 00430 endif_line = line 00431 if endif_line == "": 00432 print "AddLinkDef:: ERROR File \"" + file + "\" is not in the right format!" 00433 print "AddLinkDef:: ERROR Not adding link definitions!" 00434 return 00435 index = lines.index( endif_line ) 00436 00437 # Add the line defining the current analysis cycle: 00438 lines.insert( index, "#pragma link C++ class %s+;\n" % cycleName ) 00439 lines.insert( index + 1, "\n" ) 00440 00441 # Overwrite the file with the new contents: 00442 output = open( fileName, "w" ) 00443 for line in lines: 00444 output.write( line ) 00445 output.close() 00446 00447 else: 00448 # Create a new file and fill it with all the necessary lines: 00449 print "AddLinkDef:: Creating new file called \"" + fileName + "\"" 00450 output = open( fileName, "w" ) 00451 output.write( "// Dear emacs, this is -*- c++ -*-\n" ) 00452 output.write( "// $Id: CycleCreators.py 173 2010-05-12 15:49:33Z krasznaa $\n\n" ) 00453 output.write( "#ifdef __CINT__\n\n" ) 00454 output.write( "#pragma link off all globals;\n" ) 00455 output.write( "#pragma link off all classes;\n" ) 00456 output.write( "#pragma link off all functions;\n\n" ) 00457 output.write( "#pragma link C++ nestedclass;\n\n" ) 00458 output.write( "#pragma link C++ class %s+;\n\n" % cycleName ) 00459 output.write( "#endif // __CINT__\n" ) 00460 00461 return 00462 00463 ## @short Function creating a configuration file for the new cycle 00464 # 00465 # This function is supposed to create an example configuration file 00466 # for the new cycle. It uses PyXML to write the configuration, and 00467 # exactly this causes a bit of trouble. PyXML is about the worst 00468 # XML implementation I ever came accross... There are tons of things 00469 # that it can't do. Not to mention the lack of any proper documentation. 00470 # 00471 # All in all, the resulting XML file is not too usable at the moment, 00472 # it's probably easier just copying one of the example cycles from 00473 # SFrame/user/config and adjusting it to the user's needs... 00474 # 00475 # @param cycleName Name of the analysis cycle. Can contain the namespace name. 00476 # @param fileName Optional parameter with the configuration file name 00477 def CreateConfig( self, cycleName, fileName = "" ): 00478 00479 # Extract the namespace name if it has been specified: 00480 namespace = "" 00481 import re 00482 if re.search( "::", cycleName ): 00483 print "CreateConfig:: We're creating a cycle in a namespace" 00484 m = re.match( "(.*)::(.*)", cycleName ) 00485 namespace = m.group( 1 ) 00486 cycleName = m.group( 2 ) 00487 print "CreateConfig:: Namespace name = " + namespace 00488 00489 # Construct the file name if it has not been specified: 00490 if fileName == "": 00491 fileName = cycleName + "_config.xml" 00492 00493 # Some printouts: 00494 print "CreateConfig:: Cycle name = " + cycleName 00495 print "CreateConfig:: File name = " + fileName 00496 00497 # Use PyXML for the configuration creation: 00498 import xml.dom.minidom 00499 00500 doc = xml.dom.minidom.Document() 00501 00502 doctype = xml.dom.minidom.DocumentType( "JobConfiguration" ) 00503 doctype.publicId = "" 00504 doctype.systemId = "JobConfig.dtd" 00505 doc.doctype = doctype 00506 00507 jobconfig = doc.createElement( "JobConfiguration" ) 00508 doc.appendChild( jobconfig ) 00509 jobconfig.setAttribute( "JobName", cycleName + "Job" ) 00510 jobconfig.setAttribute( "OutputLevel", "INFO" ) 00511 00512 userlib = doc.createElement( "Library" ) 00513 jobconfig.appendChild( userlib ) 00514 userlib.setAttribute( "Name", "YourLibraryNameComesHere" ) 00515 00516 cycle = doc.createElement( "Cycle" ) 00517 jobconfig.appendChild( cycle ) 00518 cycle.setAttribute( "Name", cycleName ) 00519 cycle.setAttribute( "OutputDirectory", "./" ) 00520 cycle.setAttribute( "PostFix", "" ) 00521 cycle.setAttribute( "TargetLumi", "1.0" ) 00522 00523 inputdata = doc.createElement( "InputData" ) 00524 cycle.appendChild( inputdata ) 00525 inputdata.setAttribute( "Type", "Data1" ) 00526 inputdata.setAttribute( "Version", "Reco" ) 00527 inputdata.setAttribute( "Lumi", "0.0" ) 00528 inputdata.setAttribute( "NEventsMax", "-1" ) 00529 00530 infile = doc.createElement( "In" ) 00531 inputdata.appendChild( infile ) 00532 infile.setAttribute( "FileName", "YourInputFileComesHere" ) 00533 00534 userconf = doc.createElement( "UserConfig" ) 00535 cycle.appendChild( userconf ) 00536 00537 confitem = doc.createElement( "Item" ) 00538 userconf.appendChild( confitem ) 00539 confitem.setAttribute( "Name", "NameOfUserProperty" ) 00540 confitem.setAttribute( "Value", "ValueOfUserProperty" ) 00541 00542 output = open( fileName, "w" ) 00543 output.write( doc.toprettyxml( encoding="UTF-8" ) ) 00544 00545 return 00546 00547 ## @short Main analysis cycle creator function 00548 # 00549 # The users of this class should normally just use this function 00550 # to create a new analysis cycle. 00551 # 00552 # It only really needs to receive the name of the new cycle, it can guess 00553 # the name of the LinkDef file by itself if it has to. It calls all the 00554 # other functions of this class to create all the files for the new 00555 # cycle. 00556 # 00557 # @param cycleName Name of the analysis cycle. Can contain the namespace name. 00558 # @param linkdef Optional parameter with the name of the LinkDef file 00559 def CreateCycle( self, cycleName, linkdef = "" ): 00560 00561 # If the specified name contains a namespace, get just the class name: 00562 className = cycleName 00563 import re 00564 if re.search( "::", cycleName ): 00565 m = re.match( ".*::(.*)", cycleName ) 00566 className = m.group( 1 ) 00567 00568 # Check if a directory called "include" exists in the current directory. 00569 # If it does, put the new header in that directory. Otherwise leave it up 00570 # to the CreateHeader function to put it where it wants. 00571 import os.path 00572 if os.path.exists( "./include" ): 00573 self.CreateHeader( cycleName, "./include/" + className + ".h" ) 00574 00575 if linkdef == "": 00576 import glob 00577 filelist = glob.glob( "./include/*LinkDef.h" ) 00578 if len( filelist ) == 0: 00579 print "CreateCycle:: WARNING There is no LinkDef file under ./include" 00580 print "CreateCycle:: WARNING Creating one with the name ./include/LinkDef.h" 00581 linkdef = "./include/LinkDef.h" 00582 elif len( filelist ) == 1: 00583 linkdef = filelist[ 0 ] 00584 else: 00585 print "CreateCycle:: ERROR Multiple header files ending in LinkDef.h" 00586 print "CreateCycle:: ERROR I don't know which one to use..." 00587 return 00588 00589 self.AddLinkDef( cycleName, linkdef ) 00590 00591 else: 00592 self.CreateHeader( cycleName ) 00593 00594 if linkdef == "": 00595 import glob 00596 filelist = glob.glob( "*LinkDef.h" ) 00597 if len( filelist ) == 0: 00598 print "CreateCycle:: Creating new LinkDef file: LinkDef.h" 00599 linkdef = "LinkDef.h" 00600 elif len( filelist ) == 1: 00601 linkdef = filelist[ 0 ] 00602 else: 00603 print "CreateCycle:: ERROR Multiple header files ending in LinkDef.h" 00604 print "CreateCycle:: ERROR I don't know which one to use..." 00605 return 00606 00607 self.AddLinkDef( cycleName, linkdef ) 00608 00609 # Check if a directory called "src" exists in the current directory. 00610 # If it does, put the new source in that directory. Otherwise leave it up 00611 # to the CreateSource function to put it where it wants. 00612 if os.path.exists( "./src" ): 00613 self.CreateSource( cycleName, "./src/" + className + ".cxx" ) 00614 else: 00615 self.CreateSource( cycleName ) 00616 00617 # Check if a directory called "config" exists in the current directory. 00618 # If it does, put the new configuration in that directory. Otherwise leave it up 00619 # to the CreateConfig function to put it where it wants. 00620 if os.path.exists( "./config" ): 00621 self.CreateConfig( cycleName, "./config/" + className + "_config.xml" ) 00622 else: 00623 self.CreateConfig( cycleName ) 00624 00625 return