#!/usr/local/bin/python2.6 """ Script to generate the configuration files for the replay of AGATA data using the multi-process distributed system Narval or the single-process emulator femul. The script is divided in a few sections, some of which are specific to the actual analysis (and therefore are likely to be changed by the user) while some others are normally not to be touched: 0) Type of analysis and replacement macros (in the style of the shell) used to parametrize the commands listed in 2). 1) The structure of the actual analysis is defined by the variables PROGTYPE and CONFTYPE and by the dictionaries Topology and Actors. The dictionary ExtraFiles contains a list of files, which are needed by the analysis but are not generated by this script (e.g. calibrations, mappings, ...); these files can be copied from a previous analysis if the script is started with the option -o or --old (python gen_conf.py -h to get the list of accepted options) 2) The command lines to be written in the conf files of the actors defined in Actors{}. Uncomment/comment/modify the command lines and their parameters 3) A small database defining the position and the PSA signal basis of the germanium crystals. This part should not need to be changed. To get a list of command line arguments, launch it as "gen_conf.py -h" """ #With the information reported in 1) and 2) it should be rather strightforward to produce the #topology file xxxxx.xml for narval (or xxxx.conf for femul) and the xxxx script for narval. ############################################################################################### ################### 0) Type of analysis and replacement symbols ############################# ############################################################################################### PROGTYPE='femul' # NARVAL or femul (to choose between os.getcwd() and '' for CWD) CONFTYPE='OFFLINE' # ONLINE or OFFLINE (used just to exclude the ReadDataDir line in the Producers) MACROS={ # various replacements for symbols defined in 2). '$CONFDIR' : 'Conf', # this will be prefixed by CWD/ '$READDIR' : 'Data', # this will be prefixed by CWD/; if ONLINE this will not be written '$SAVEDIR' : 'Out', # this will be prefixed by CWD/; if ONLINE this will be replaced by $READDIR '$ANCILLARY' : 'Ancillary', # this will be prefixed by CWD/ '$GLOBAL' : 'Global', # this will be prefixed by CWD/ #'$PSABASE' : 'D:/NarvalTests/bases/ADL', # on my Windows/Cygwin '$PSABASE' : '/data/PSAbases/ADL', # standard place at LNL/Linux '$CRYSTAL_ID' : "", # the actual value is defined in GeDataBase '$SIGNAL_BASIS' : "", # the actual value is defined in GeDataBase '$CRYSTAL' : "", # the actual value taken from Topology['CRYSTAL'] } ############################################################################################### ################### 1) Structure of analysis ################################################ ############################################################################################### Topology={ # The directories to be generated in Conf, Data and Out 'CRYSTAL' : "1R 1G 1B 2R 2G 2B 3R 3G 3B 4R 4G 4B 5R 5G 5B", 'ANCILLARY' : "Ancillary", 'GLOBAL' : "Global", } # The name of the used actors must correspond to one of the tuples defined in the following section. # This requirement creates a problem for BasicAFP and BasicAFC when they are used in chains of different type # (e.g. after PSA and after Tracking) and one wants to define chain-specific names for their input/output files. # The solution is to suffix the name of the chain-type (e.g. _CRYSTAL or _GLOBAL or any other), to the defining tuple. # This suffix will be silently removed from the actual name of the generated configuration files. Actors={ # These are the xxxx.conf files to be generated 'CRYSTAL' : "CrystalProducer PreprocessingFilter PSAFilter BasicAFC_CRYSTAL BasicAFP_CRYSTAL", # 'ANCILLARY' : "AncillaryProducerTCP AncillaryFilter BasicAFC_ANCILLARY", # 'GLOBAL' : "TrackingFilter BasicAFC_GLOBAL EventMerger EventBuilder", # } ExtraFiles={ # If not already present, these files can be copied from a directory specified in the command line 'CRYSTAL' : "CrystalProducerATCA.conf PreprocessingFilterPSA.conf xinv_1325-1340.cal xdir_1325-1340.cal", 'ANCILLARY' : "AncillaryFilterVME.conf", # PRISMA PRISMA/lutPRISMA.txt PRISMA/manager.conf 'GLOBAL' : "CrystalPositionLookUpTable", # lutPRISMA.txt } ############################################################################################### ################### 2) Tuples specifying the content of the configuration files ############# ############################################################################################### CrystalProducer=( "ActualClass CrystalProducerATCA", # name of the used daugther class "CrystalID $CRYSTAL_ID", # position of the crystal in the AGATA frame "ReadDataDir $READDIR/$CRYSTAL", # should not be present in the ONLINE producer "SaveDataDir $SAVEDIR/$CRYSTAL", # Out/1R... for the OFFLINE; Data/1R... for the online "TraceLength 100", # number of samples of the saved traces (100 since long time; default is still the initial 160) #"ValidationRate 300", # readout event-rate. Used to reduce the buffer size in the online ACQ so as to gent ~1 read/second "WriteDataMask 0", # 0=none, 1=input_mezzdata[], 2=event_mezzdata 4=event_mezzhead 8=event_energy 16=event_core #"ProjeM1 200 8", #to use Recal ... thresh and hgain... "WriteTraces 1000", # number of traces written to "Prod__1000-42-100-S__Traces.samp" at the beginning of the run #"WriteBaseLines 10", # distribution of preamplifier baselines; events decimated by 10 #"DecimateMezzdata 4", # write one out of 4 (if WriteDataMask enables) #"DecimateMezzener 2", # write one out of 2 (if WriteDataMask enables) #"TimeStep 10", # not needed since the online has now a dedicated watchdog #"TstampCorrection 0", # used in the offline to correct for rare cases of bad setup #"MaxTstampSeconds 3200", # #"WriteDataSplit 1000000", # Size of event_mezzdata.cdat.xxxx is ~4GB (.cdat as data is compressed) #"WriteCompressed", # WriteCompressed is the default #"WriteUnCompressed", # to get uncompressed raw data #"WriteDataRange 300 15000", # range of CC amplitude (in channels) to write the data, not given means write all #"WriteDataThreshold 5000", # still recognized but obsolete "InputDataFile SRM_AGATA_event_mezzdata.cdat", # modify definitions in the online version of CrystalProducerATCA.conf to read from a single raw-data file "AllInputFiles", # filename.ext --> filename.ext.0000 and increment the string after last . #"NumInputFiles 100", # anaysis file sequences: n>0 (n<0) increment the string before (after) last . #"StopErrorCode 100", # < 100 to avoid stopping everything at the first input error #"Verbose", # more verbose terminal-output ) PreprocessingFilter=( "ActualClass PreprocessingFilterPSA", # name of the used daugther class "SaveDataDir $SAVEDIR/$CRYSTAL", # normally Out/1R... "EnergyGain 2", # channels/keV of the calibrated energy spectra "XtalkFile xinv_1325-1340.cal", # cross talk correction coefficients for the energies "WriteTraces 1000", # number of traces written to "Prep__1000-42-100-S__Traces.samp" at the beginning of the run # "CFDparamsCC 5 8 6 0.25 20", # integr. diff delay fraction threshold #"TriggerWithCoreAlone", # local re-trigger done using CC alone (default is CC+sum_net_charge_segs) #"CoreEnergyGate 500 1500", # possibility to restrict the energy range "CoreEnergyGate 20 20000", # possibility to restrict the energy range #"SegmentFoldGate 1 1 20.", # selection of events based on the number of hit segments #"TraceLengthRaw 160", # needed only for data before 02-2010 #"SegmentEnergyThreshold 10", # 10 keV is the default #"HandShift 1.5", # extra shift when aligning traces #"Verbose", # more verbose terminal-output #### command lines to be produced only for the specified crystals. If more than one line needed, group items as () or [] { '2G' : "DeadSegment 4 0.958724 0.021746", '4B' : "UnstableSegment 10 0.95", #bad resolution!--it "moved" from 29 to this one ... '1B' : "UnstableSegment 13 0.95", #gain jumps up to a factor 2! #'3B' : "UnstableSegment 4 0.95", } ) PSAFilter=( "ActualClass PSAFilterGridSearch", # name of the used daugther class "BasisFile $SIGNAL_BASIS", # this is generated from the GeDataBase structure (see at line ~230 of this file) "SaveDataDir $SAVEDIR/$CRYSTAL", # normally Out "EnergyGain 2", # channels/keV of the calibrated energy spectra "WriteTraces 1000", # number of traces+fit written to "Prep__1000-42-100-S__Traces.samp" at the beginning of the run "XtalkFile xdir_1325-1340.cal", # cross talk correction coefficients for traces (applied to the signal basis) "Threads 4 100", # number of threads, number of events/thread (if PSA threads enabled at compile time) "GridSearchType SegCenter", # Full, Adaptive, AdaptiveTwoHits, CoarseOnly or SegCenter; default is Adaptive #"TauSlice 25 35 37 40 40 30 40", # preamp response of the 6 slices and the CC #"SegmentFoldGate 1 3", # selection of events based on the number of hit segments #"TshiftFile tshifts.cal", # correction of T0 for the PSA (not really used) #"WritePsaHits", # writes the hits in binary to calibrate neutron damage "LambdaE 500", # overall correction factor for the trapping of electrons #"LambdaH 60", # overall correction factor for the trapping of holes #"ForceSegmentsToCore", # sum of segments forced to energy of the core. Use it EITHER in the PSA OR in the Tracking #"Verbose", # more verbose terminal-output #### command lines to be produced only for the specified crystals. If more than one line needed, group items as () or [] { '1G' : "LambdaH 50", '1R' : "LambdaH 150", # '2R' : "LambdaH 40", '2G' : ("LambdaH 50","DeadSegment 4"), '2B' : "LambdaH 40", '3R' : "LambdaH 25", '3G' : "LambdaH 20", '3B' : "LambdaH 20", '4R' : "LambdaH 15", '4G' : "LambdaH 15", '4B' : "LambdaH 20", '5R' : "LambdaH 150", # '5G' : "LambdaH 120", # '5B' : "LambdaH 150", # } ) BasicAFC_CRYSTAL=( # _CRYSTAL will be removed when building the name of the file "$SAVEDIR/$CRYSTAL PSA_$CRYSTAL_ 0", # normally put after the PSA ) BasicAFP_CRYSTAL=( # _CRYSTAL will be removed when building the name of the file "$READDIR/$CRYSTAL PSA_$CRYSTAL_ 0", # normally put before the event builder ) AncillaryProducerTCP=( "ActualClass AncillaryProducerTCP", "ReadDataDir $READDIR/$ANCILLARY", "SaveDataDir $SAVEDIR/$ANCILLARY", "DetectorID 99", # 99 is default "PortTCP 9999", # 9999 is default "WriteDataMask 0", # input data not written if 0 or not given #"TcpLittleEndian", # default is big endian #"EndEventTag", # to remove the End Of Event 0xffffffff word (Nicola's format) "InputDataFile event_vmedata.bdat", #"KeyADF data:ranc0", # data:ranc0 (default) or data:ranc1 or data:ranc2 #"AdfKey data:ranc0", # a recognized variant of KeyADF #"TcpPort 9999", # a recognized variant of PortTCP #"Verbose", ) AncillaryFilter=( "ActualClass AncillaryFilterVME", "SaveDataDir $SAVEDIR/$ANCILLARY", #"TstampFile atstamps.txt", # file with evnumber and tstamp of events to select #"ReferenceTDC 3 17 550", # module index offset #"PrismaTDC 3 17 550", # module index offset #"PRISMALUTFile $CONFDIR/$ANCILLARY/PRISMA/lutPRISMA_WEEK49.txt", #"PRISMAManager $CONFDIR/$ANCILLARY/PRISMA/manager.conf", #"DanteChan 20 21 22", # T X Y #"DanteTDC 3 17 550", # module index offset #"DantePos 77 4 -62 -30 -56", # #"DanteBeta 0.06 ", # #"DanteCalX 100 .2", # #"DanteCalY -50 .5", # #"AllPairsMat", # generate a big matrix of all VME modules #"Verbose", ) BasicAFC_ANCILLARY=( # _CRYSTAL will be removed when building the name of the file "$SAVEDIR/$ANCILLARY AncillaryFilter_ 0", # normally put after the PSA ) EventBuilder=( "ActualClass EventBuilder", # name of the used daugther class "SaveDataDir $SAVEDIR/$GLOBAL", # Out/Global "BuilderType TimeStamp 200", # EventNumber also possible but not working well "KeyIn data:psa", # key of 1st queue (and all others if no mor given); default is data:psa "KeyIn data:psa", # key of 2nd queue (and all others if not given) "KeyOut event:data:psa", # key of the output frame; default is event:data:psa "MinFold 1", # selects the multiplicity of events passed to the output #"TimestampCorrect 0 -128", # indexed by the input queue number !! #"TimestampCorrect 2 -128", # given for each channel to correct "Verbose", # more verbose terminal-output ) EventMerger=( "ActualClass EventMerger", # name of the used daugther class "SaveDataDir $SAVEDIR/$GLOBAL", # Out/Global "BuilderType TimeStamp 200", # EventNumber also possible but not working well "keyIn data:ranc1", # key of 1st queue (and all others if not given) default is data:ranc1 "KeyIn event:data:psa", # key of 2nd queue (and all others if not given) default is event:data:psa "keyOut event:data", # key of the output frame default is event:data "MinFold 1", # 2 if you want to force the coincidence between AGATA and the Ancillary #"TimestampCorrect 0 -128", # indexed by the input queue number !! #"TimestampCorrect 2 -128", # given for each channel to correct "Verbose", # more verbose terminal-output ) TrackingFilter=( "ActualClass TrackingFilterOFT", # name of the used daugther class (TrackingFilterOFT or TrackingFilterMGT) "SaveDataDir $SAVEDIR/$GLOBAL", # Out/Global "EnergyGain 1", # channels/keV of the calibrated energy spectra #"ExcludeTracking", # skip the tracking part of the actor; remains only the data processing #"AcceptanceValue 1", # limiting value for the tracking algorithm (0=default) #"Ancillary", # to perform tracking only if ancillary data is present #"Recoiling", # Doppler shift correction to be done using event-by-event recoil direction from Ancillary "SourcePosition 0 0 -103", # position of source with respect to the center of AGATA Position of source #"RecoilDirection 0 0.682 0.535", # fixed recoil direction for Doppler correction (if not using info from Ancillary) #"RecoilBeta 0.11", # and beta of recoil #"KeepEmpty", # send to the adf output also events without gammas #"DiscardEmpty", # remove from the adf output events without gammas #"OutputModel kSafe", # kStrict (default) kSafe kGrowing #"RecalFile recalfile.cal", # recalibration of CC at the tracking level (chan offset gain) "ForceSegmentsToCore", # as a workaround of the neutron damage correction #"CoreEnergyGate 20 20000", # possibility to restrict the energy range in the individual detectors (better in PreprocessingFilter) #"DetectorFoldGate 2 2", # keep and process only events with this number of detectors #"WriteRootTree", # enable writing the root tree "WriteRootTree InputHits", # enable writing the root tree with the input hits #"WriteGsortData", # enable writing gsort-formatted data #"WriteMgtData", # enable writing Mgt_Hits.txt (same as: WriteInputHits WriteMgtHits WriteOftHits) #"WriteMgtData Ancillary Timing Extended", # enable writing Mgt_Hits.txt with ancillary,... data #"WriteTracked", # enable writing Mgt_Hits.txt with ancillary data #"TimeWindowGeAnc 673 683", # values should be taken from Oft__**__TA.spec #"TimeWindowGeAncillary 648 668", # variant of the previous #"RotoTranslations CrystalPositionLookUpTable", # this is the default #"Matrixgg1 4096 1", # size and gain of g-g matrix for CC before tracking (default size is 0 ==> no matrix) #"Matrixgg2 4096 1", # size and gain of g-g matrix for tracked and Doppler corrected gammas ( "" ) #"Verbose", # more verbose terminal-output #"Debug", # print the format of each event; extremely slow ) BasicAFC_GLOBAL=( # _GLOBAL will be removed when building the name of the file "$SAVEDIR/$GLOBAL Tracked_ 0", # name of the final adf file "Tracked_0000.adf" ) ############################################################################################### ################### 3) Database of AGATA germanium detectors ################################ ############################################################################################### # These values should be checked carefully against the detector configuration used to take the data GeDataBase2010={ '1R' : ['0', '$PSABASE/LibTrap_A001.dat'], '1G' : ['1', '$PSABASE/LibTrap_B002.dat'], '1B' : ['2', '$PSABASE/LibTrap_C002.dat'], '2R' : ['6', '$PSABASE/LibTrap_A003.dat'], '2G' : ['7', '$PSABASE/LibTrap_B003.dat'], '2B' : ['8', '$PSABASE/LibTrap_C005.dat'], '3R' : ['3', '$PSABASE/LibTrap_A002.dat'], '3G' : ['4', '$PSABASE/LibTrap_B005.dat'], '3B' : ['5', '$PSABASE/LibTrap_C006.dat'], '4R' : ['12', '$PSABASE/LibTrap_A005.dat'], '4G' : ['13', '$PSABASE/LibTrap_B001.dat'], '4B' : ['14', '$PSABASE/LibTrap_C001.dat'], } GeDataBase2011_1={ '1R' : ['12', '$PSABASE/LibTrap_A006.dat'], '1G' : ['13', '$PSABASE/LibTrap_B001.dat'], '1B' : ['14', '$PSABASE/LibTrap_C003.dat'], '2R' : ['6', '$PSABASE/LibTrap_A003.dat'], '2G' : ['7', '$PSABASE/LibTrap_B003.dat'], '2B' : ['8', '$PSABASE/LibTrap_C005.dat'], '3R' : ['3', '$PSABASE/LibTrap_A002.dat'], '3G' : ['4', '$PSABASE/LibTrap_B005.dat'], '3B' : ['5', '$PSABASE/LibTrap_C001.dat'], '4R' : ['9' , '$PSABASE/LibTrap_A001.dat'], '4G' : ['10', '$PSABASE/LibTrap_B002.dat'], '4B' : ['11', '$PSABASE/LibTrap_C002.dat'], '5R' : ['0' , '$PSABASE/LibTrap_A004.dat'], '5G' : ['1', '$PSABASE/LibTrap_B009.dat'], '5B' : ['2', '$PSABASE/LibTrap_C004.dat'], } GeDataBase2011_2={ '1R' : ['12', '$PSABASE/LibTrap_A006.dat'], '1G' : ['13', '$PSABASE/LibTrap_B001.dat'], '1B' : ['14', '$PSABASE/LibTrap_C003.dat'], '2R' : ['9', '$PSABASE/LibTrap_A003.dat'], '2G' : ['10', '$PSABASE/LibTrap_B003.dat'], '2B' : ['11', '$PSABASE/LibTrap_C005.dat'], '3R' : ['3', '$PSABASE/LibTrap_A002.dat'], '3G' : ['4', '$PSABASE/LibTrap_B005.dat'], '3B' : ['5', '$PSABASE/LibTrap_C001.dat'], '4R' : ['6' , '$PSABASE/LibTrap_A001.dat'], '4G' : ['7', '$PSABASE/LibTrap_B002.dat'], '4B' : ['8', '$PSABASE/LibTrap_C002.dat'], '5R' : ['0' , '$PSABASE/LibTrap_A004.dat'], '5G' : ['1', '$PSABASE/LibTrap_B009.dat'], '5B' : ['2', '$PSABASE/LibTrap_C004.dat'], } GeDataBase = GeDataBase2011_2 ############################################################################################### ###### Code to generate directories and files. You should not need to modify this part. ##### ############################################################################################### import os import os.path import shutil import sys import optparse verbose = False verbstr = "" def getGeData(cr): """ get data of crystal cr from GeDataBase """ if cr in GeDataBase: if GeDataBase[cr] != ['','']: return GeDataBase[cr] else: print 'GeDataBase for',cr,'is empty' sys.exit(-1) else: print cr, ' is not in GeDataBase' sys.exit(-1) def replaceMacros(ss, cr): """ replace macros (as defined in MACROS) in string ss. If cr is a Ge crystal (e.g. 1R), its data ($CRYSTAL_ID and $SIGNAL_BASIS are extracted from GeDataBase and replaced before checking MACRO """ if len(cr) == 2: # 2 characters for the ge crystals dd = getGeData(cr) ll = ss.replace("$CRYSTAL_ID", dd[0]) ll = ll.replace("$SIGNAL_BASIS", dd[1]) ll = ll.replace("$CRYSTAL", cr) else: ll = ss for mm in MACROS: ll = ll.replace(mm, MACROS[mm]) return ll def printItem(vv, cc, ff): """ item vv relative to the chain cc is printed to file ff, after macro replacement. If vv is a tuple or a list, its components are printed in separate lines """ if isinstance(vv, list) or isinstance(vv, tuple): # multi line: recursively call printItem for the elements for vi in vv: printItem(vi, cc, ff) else: # single line: replace macros and print it ll = replaceMacros(vv, cc) if CONFTYPE != 'ONLINE' or ll.find('ReadDataDir')!=0: ff.write(ll+os.linesep) #print ll+os.linesep def printActor(vv, cc, ff): """ print the command lines in vv to file ff of analysis chain cc """ global verbstr #import pdb; pdb.set_trace() if len(ff) < 1: return if os.path.exists(ff): if verbose: print " Replaced ", ff else: verbstr += "." else: print " Created ", ff with open(ff, "w") as fi: for vi in vv: if isinstance(vi, dict): if cc in vi: # dictionary refers to the present detector printItem(vi[cc], cc, fi) else: # common item printItem(vi, cc, fi) def checkMakeDirs(dd): """ created sub-directory dd in $CONFDIR, $READDIR and $SAVEDIR """ for cc in ['$CONFDIR', '$READDIR', '$SAVEDIR']: if cc in MACROS: ff = os.path.join(MACROS[cc], dd) if os.path.exists(ff) == False: print 'Creating ', ff os.makedirs(ff) def makeFileName(pre, cc, aa, topo): """ generate the actual name of the configuration file pre/cc/aa if the generated filename contains "_topo" this part is removed """ #import pdb; pdb.set_trace() if PROGTYPE == "NARVAL" : if aa == "EventBuilder" or aa == "EventMerger" : print (" "+aa+".conf").ljust(18), "not generated when using NARVAL" return "" ff = os.path.join(pre, cc, aa+'.conf') ff = ff.replace('_'+topo, '') return ff def checkExtraFiles(new, old, dd, files): """ check existence of the files defined in ExtraFiles. Try to copy it from oldDir if file not present and the script is launched with "-o oldDir" """ global verbstr newDir = os.path.join(new, dd) oldDir = os.path.join(old, dd) for f in files.split(): ff = os.path.join(newDir, f) if os.path.exists(ff): if verbose: print " Existing ", ff else: verbstr += "." else: if old != "": gg = os.path.join(oldDir, f) if os.path.exists(gg): if os.path.isdir(gg): shutil.copytree(gg, ff) else: shutil.copy(gg, ff) print " Copied ", gg if not os.path.exists(ff): print " Missing ", ff def checkArgsAndMacros(): """ decode command line arguments and perform some consistency checks on PROGTYPE and CONFTYPE """ parser = optparse.OptionParser() parser.add_option("-o", "--old", dest="confold", help="old Conf where to look for missing auxiliary files", metavar="DIR") parser.add_option("-d", "--dir", dest="workdir", help="the directory where to generate the analysis structure", metavar="DIR") parser.add_option("-v", "--verbose", action="store_true", dest="verbose", default=False, help="verbosity of printouts") (options, args) = parser.parse_args() #print 'confold = ', options.confold #print 'workdir = ', options.workdir #print 'verbose = ', options.verbose global verbose verbose = options.verbose if options.workdir is None: CWD = os.getcwd() else: CWD = options.workdir global PROGTYPE, CONFTYPE if PROGTYPE != 'NARVAL': CONFTYPE = 'OFFLINE' if CONFTYPE == 'ONLINE': CWD = os.getcwd() for cc in ['$CONFDIR', '$READDIR', '$SAVEDIR']: if cc in MACROS: MACROS[cc] = os.path.join(CWD, MACROS[cc]) if CONFTYPE == 'ONLINE': MACROS['$SAVEDIR'] = MACROS['$READDIR'] del MACROS['$READDIR'] print print 'CWD '.ljust(10), " --> ", CWD print 'PROGTYPE'.ljust(10), " --> ", PROGTYPE print 'CONFTYPE'.ljust(10), " --> ", CONFTYPE for a in MACROS: if MACROS[a] != '': print a.ljust(10), " --> ", MACROS[a] print newPrefix = MACROS['$CONFDIR'] if options.confold is None: oldPrefix = '' else: oldPrefix = options.confold return newPrefix, oldPrefix def main(): """ """ (newConf, oldConf) = checkArgsAndMacros() print "newConf = ", newConf print "oldConf = ", oldConf print global verbstr myGlobals = globals() for topo in Topology: block = Topology[topo] if isinstance(block, dict): dire = block.split() else: dire = block; for dd in dire.split(): checkMakeDirs(dd) if topo in Actors: print dd+'/' verbstr = "" actor = Actors[topo].split() for aa in actor: ff = makeFileName(newConf, dd, aa, topo) printActor(myGlobals[aa], dd, ff) if topo in ExtraFiles: checkExtraFiles(newConf, oldConf, dd, ExtraFiles[topo]) if verbstr: print " " + verbstr ############################################################################################### ############################################################################################### if __name__ == "__main__": main()