runOPAL.py 8.68 KB
Newer Older
1
#!/usr/bin/env python
gsell's avatar
gsell committed
2 3

"""
4
Script that launches OPAL simulations
gsell's avatar
gsell committed
5 6

@author: Andreas Adelmann <andreas.adelmann@psi.ch>
7
@author: Yves Ineichen
gsell's avatar
gsell committed
8 9 10 11
@version: 0.1

"""

Renato Bellotti's avatar
Renato Bellotti committed
12 13 14 15
import sys
import os
import shutil
import glob
gsell's avatar
gsell committed
16 17 18
from simulation import Simulation
from opaldict import OpalDict

19

Renato Bellotti's avatar
Renato Bellotti committed
20 21 22 23 24 25 26 27 28 29 30 31
def getTemplatePaths(parentDir, pattern):
    '''
    Return the paths to template files.

    The template files are files in the parent directory
    that follow the given pattern.

    Returns
    =======
    The paths to the template files.
    '''
    result = glob.glob(os.path.join(parentDir, pattern))
32
    if not result:
Renato Bellotti's avatar
Renato Bellotti committed
33
        print(f'No template file ({pattern}) found')
gsell's avatar
gsell committed
34
        sys.exit()
35 36
    return result

gsell's avatar
gsell committed
37

38
def getBaseName(inputfilePath):
Renato Bellotti's avatar
Renato Bellotti committed
39 40 41 42 43
    templates = getTemplatePaths(inputfilePath, '*.tmpl')

    # NOTE: choose first (alphanumeric order) *.tmpl file by default
    name = templates[0].split('/')[-1][:-5]
    if os.path.isfile(os.path.join('.', name + '.data')):
44
        return name
Renato Bellotti's avatar
Renato Bellotti committed
45 46

    print(f'Template and data filename do not match, {name}.data expected')
47 48 49
    sys.exit()


50
def printUsage():
51
    print("./runOPAL.py [--help] [--filename=str] [--test] [--quiet] [--info=num] [--test] [--keep] [--queue=qname] [--hypert=num] [--nobatch] [ATTR=SCANVALUE] {[ATTR=VALUE]}")
ext-neveu_n's avatar
ext-neveu_n committed
52
    print("")
53
    print("--help                prints this message")
54
    print("--filename | -f=<str> sets base file name for both *.data and *.tmpl")
55 56
    print("--test | -t           does everything but submitting the job")
    print("--keep | -k           if same simulation has been run before, keep old data and abort")
57
    print("--nobatch             run OPAL locally not using the batch system and waits until the job is done")
58 59 60
    print("--noopt               ignore optimization template (if any) and perform regular simulation")
    print("--quiet               suppress debug printout")
    print("--info | -i=<num>     steers the std-output of OPAL. The range is 0 < num < 6 (default), from minimal to maximum output")
ext-neveu_n's avatar
ext-neveu_n committed
61 62
    print("--queue=<qname> defines in which queue the job goes. Overwrites QUEUE (deprecated SGE_QUEUE)")
    print("--hypert=<num>  defines the number of Hyper-Threads used. Default 0")
63 64 65
    print("")
    print("SCANVALUE=start:end:step, scans a parameter space, e.g. example TFWHM=0.85:0.90:0.01 ")
    print("ATTR refers to a name in the data file")
66
    print("")
67
    print("Recognized environment variables: DISTRIBUTIONS, FIELDMAPS, OPTIMIZER, OPAL_EXE_PATH, TEMPLATES, QUEUE, RAM, TIME (deprecated SGE_)")
68 69
    # temporary see issue #8
    print("")
70
    print("Important: runOPAL is currently not compatible with the command SAMPLE")
gsell's avatar
gsell committed
71

72

Renato Bellotti's avatar
Renato Bellotti committed
73 74 75
# NOTE: SAMPLE command not compatible with runOPAL (issue #8)
def checkCompat(tmplFile, incompatible):
    templateFile = open(tmplFile, 'r')
76 77 78
    for line in templateFile:
        if line.startswith('//'):
            continue
79
        if any(command in line for command in incompatible):
80 81 82 83 84
            print(', '.join(incompatible)+' command(s) currently not compatible with runOPAL')
            sys.exit()
    templateFile.close()


85
def traverseRanges(list, opaldict, args, doNobatch):
86
    """
Renato Bellotti's avatar
Renato Bellotti committed
87 88 89
    Traverse all possible combinations of range variable values.
    Start simulation once all range variables are fixed to a value.
    A list entry has the following structure:
90 91
    ['name of var', start_value, end_value, step_value]
    """
gsell's avatar
gsell committed
92 93 94
    head = list[0]
    tail = list[1:]
    curval = head[1][0]
95
    endval = head[1][1]
Renato Bellotti's avatar
Renato Bellotti committed
96
    step = head[1][2]
gsell's avatar
gsell committed
97
    qid = -1
98
    if curval > endval:
Renato Bellotti's avatar
Renato Bellotti committed
99 100
        print('range is empty, start value', curval,
              'needs to be higher than end value', endval)
101
    while curval <= endval:
Renato Bellotti's avatar
Renato Bellotti committed
102 103 104 105 106 107 108
        opaldict[head[0]] = curval
        if len(tail) == 0:
            # run simulation
            sim = Simulation(opaldict)
            qid = sim.run(*args)
            if doNobatch:
                print("... finished!\n")
gsell's avatar
gsell committed
109
            else:
Renato Bellotti's avatar
Renato Bellotti committed
110 111 112 113 114
                print("SGE-ID= {}\n".format(qid))
        else:
            traverseRanges(tail, opaldict, args, doNobatch)
        curval = curval + step

115

gsell's avatar
gsell committed
116
def main(argv):
117 118 119
    """
    main method
    """
Renato Bellotti's avatar
Renato Bellotti committed
120 121
    N = -1              # a running number; if given use it to label directory!
    quiet = False
122 123 124
    doTest = False
    doKeep = False
    doNobatch = False
Renato Bellotti's avatar
Renato Bellotti committed
125
    doOptimize = True   # NOTE: this flag is opposite of --noopt
126
    queue = ""
127
    info = 6
Renato Bellotti's avatar
Renato Bellotti committed
128
    hypert = 0
129
    qid = -1
Renato Bellotti's avatar
Renato Bellotti committed
130

131 132
    inputfilePath = None
    baseFileName = None
Renato Bellotti's avatar
Renato Bellotti committed
133

134
    for arg in argv:
135 136 137
        if arg.startswith("--help"):
            printUsage()
            exit()
snuverink_j's avatar
snuverink_j committed
138
        elif arg.startswith("--filename") or arg.startswith("-f"):
139 140
            baseFileName = arg.split("=")[1]
        elif arg.startswith("--test") or arg.startswith("-t"):
141
            doTest = True
142
        elif arg.startswith("--keep") or arg.startswith("-k"):
143
            doKeep = True
ext-neveu_n's avatar
ext-neveu_n committed
144 145
        elif arg.startswith("--nobatch"):
            doNobatch = True
146 147 148 149 150
        elif arg.startswith("--noopt"):
            doOptimize = False
        elif arg.startswith("--quiet"):
            quiet = True
        elif arg.startswith("--info") or arg.startswith("-i"):
151
            info = arg.split("=")[1]
ext-neveu_n's avatar
ext-neveu_n committed
152 153
        elif arg.startswith("--queue"):
            queue = arg.split("=")[1]
154 155
        elif arg.startswith("--hypert"):
            hypert = int(arg.split("=")[1])
156
        elif arg.startswith("-"):
Renato Bellotti's avatar
Renato Bellotti committed
157
            print(arg, 'is not a valid option, see --help for the available options')
158
            exit()
159

160
    # safety check
161 162
    if os.getcwd() == os.environ.get('TEMPLATES') or os.getcwd() == os.environ.get('OPTIMIZER'):
        print('Working directory is the same as the TEMPLATES or OPTIMIZER directory! This is not allowed... bye!')
gsell's avatar
gsell committed
163
        sys.exit()
Renato Bellotti's avatar
Renato Bellotti committed
164

165
    # determine what kind of job should be ran, simulation by default
166
    if doOptimize and os.environ.get('OPTIMIZER'):
Renato Bellotti's avatar
Renato Bellotti committed
167
        if not quiet:
snuverink_j's avatar
snuverink_j committed
168
            print('job type: OPTIMIZATION')
169
        inputfilePath = os.environ.get('OPTIMIZER')
Renato Bellotti's avatar
Renato Bellotti committed
170 171
    if not (inputfilePath and glob.glob(os.path.join(inputfilePath, '*.tmpl'))):
        if not quiet:
snuverink_j's avatar
snuverink_j committed
172
            print('job type: SIMULATION')
173 174 175
        doOptimize = False
        if os.environ.get('TEMPLATES'):
            inputfilePath = os.environ.get('TEMPLATES')
Renato Bellotti's avatar
Renato Bellotti committed
176
        elif (glob.glob(os.path.join('.', '*.tmpl'))):
gsell's avatar
gsell committed
177 178
            inputfilePath = '../'
        else:
ext-neveu_n's avatar
ext-neveu_n committed
179
            print('Template file unknown -> exiting ...')
gsell's avatar
gsell committed
180
            sys.exit()
Renato Bellotti's avatar
Renato Bellotti committed
181 182

    # check that tmpl and data files can be found or guessed
183
    if not baseFileName:
snuverink_j's avatar
snuverink_j committed
184
        baseFileName = getBaseName(inputfilePath)
Renato Bellotti's avatar
Renato Bellotti committed
185 186
    elif not os.path.isfile(os.path.join(inputfilePath, baseFileName+'.tmpl')):
        print(f'{baseFileName}.tmpl cannot be found! Check if it exists in {inputfilePath}')
187
        sys.exit()
Renato Bellotti's avatar
Renato Bellotti committed
188
    if not quiet:
snuverink_j's avatar
snuverink_j committed
189
        print('baseFileName = '+baseFileName)
Renato Bellotti's avatar
Renato Bellotti committed
190

gsell's avatar
gsell committed
191
    dataFile = baseFileName + '.data'
Renato Bellotti's avatar
Renato Bellotti committed
192
    tmplFile = os.path.join(inputfilePath, baseFileName + '.tmpl')
gsell's avatar
gsell committed
193
    oinpFile = baseFileName + '.in' # the resulting OPAL input file
Renato Bellotti's avatar
Renato Bellotti committed
194

195
    checkCompat(tmplFile, ['SAMPLE']) # check compatibility
Renato Bellotti's avatar
Renato Bellotti committed
196 197

    # create the dictionary
gsell's avatar
gsell committed
198 199 200 201 202 203
    opaldict = OpalDict(dataFile)
    # check if template values must be changed
    # if so add update the dictionary with the default values
    opaldict.addUserValues(argv)
    opaldict.scale()

204
    if not opaldict.hasRanges():
gsell's avatar
gsell committed
205
        sim = Simulation(opaldict)
Renato Bellotti's avatar
Renato Bellotti committed
206 207 208
        qid = sim.run(N, baseFileName, inputfilePath, tmplFile, oinpFile,
                      doTest, doKeep, doNobatch, doOptimize,
                      info, queue, hypert, quiet)
209
        if doNobatch:
Renato Bellotti's avatar
Renato Bellotti committed
210 211
            if not quiet:
                print("... finished!\n")
ext-neveu_n's avatar
ext-neveu_n committed
212 213
        #else:
        #    print( "SGE-ID= {}\n".format(qid))
gsell's avatar
gsell committed
214 215 216
    else:
        ranges = opaldict.Range()

Renato Bellotti's avatar
Renato Bellotti committed
217
        # create range toplevel dir
gsell's avatar
gsell committed
218 219 220 221 222
        dirname = baseFileName
        for p in opaldict.uservars:
            dirname += "_" + str(p[0]) + "=" + str(p[1])
        for (k, v) in ranges.items():
            dirname += "_" + k + "=" + str(v[0]) + ":" + str(v[1]) + ":" + str(v[2])
223
        # If there's already a directory remove it...
gsell's avatar
gsell committed
224
        if os.path.isdir(dirname):
225
            if doKeep:
Renato Bellotti's avatar
Renato Bellotti committed
226
                print('KEEP existing directory ', dirname)
227
            else:
Renato Bellotti's avatar
Renato Bellotti committed
228
                print('REMOVE existing directory', dirname)
229 230 231
                shutil.rmtree(dirname)
                # create directory and change to the directory
                os.mkdir(dirname)
232 233
        else:
            os.mkdir(dirname)
Renato Bellotti's avatar
Renato Bellotti committed
234

235
        os.chdir(dirname)
gsell's avatar
gsell committed
236

ext-neveu_n's avatar
ext-neveu_n committed
237
        print(ranges)
Renato Bellotti's avatar
Renato Bellotti committed
238 239 240 241
        # run simulations of all possible combinations
        args = [N, baseFileName, inputfilePath, tmplFile, oinpFile,
                doTest, doKeep, doNobatch, doOptimize,
                info, queue, hypert, quiet]
242
        traverseRanges(list(ranges.items()), opaldict, args, doNobatch)
Renato Bellotti's avatar
Renato Bellotti committed
243

adelmann's avatar
adelmann committed
244 245
        # clean up
        os.system("rm -f *.bak ")
gsell's avatar
gsell committed
246
        os.chdir("..")
adelmann's avatar
adelmann committed
247

Renato Bellotti's avatar
Renato Bellotti committed
248 249

# call main
gsell's avatar
gsell committed
250 251
if __name__ == "__main__":
    main(sys.argv[1:])