/42TestingPoints/TOOLS/make42tree.py
Python | 280 lines | 153 code | 28 blank | 99 comment | 11 complexity | 3cb1487687d3f1e76a32d59bd39de74a MD5 | raw file
1#! /usr/bin/env python 2# 3# Script to aid in setting up a 42 Testing points example. 4# 5# Bojan Blazevic 6# Nov 2010 7# 8# 9# USAGE: 10# ./make42tree.py EXECUTABLE ARGS ROOT_DIR 11# 12# EXECUTABLE : Path to the compiled example binary 13# This must be with respect to OPENCMISS_ROOT, 14# which will be prepended to this string. 15# ARGS : Default arguments that the example requires [= ''] 16# ROOT_DIR : Directory to build the folder structure [= ./] 17# 18# This file can also be loaded as a module, so that more parameters can be 19# changed, individual functions run etc. 20# 21# 22# BEHAVIOUR: 23# 1) Creates the directory structure as defined by the 'spec' argument of the 24# 'traverseDirTree' function. 25# 2) Creates the 'input' 'output' and 'expected_results' folders in each leaf 26# folder. Creates a 'run42.sh' that points to the executable passed in on 27# the command line. 28# 3) Runs every 'run42.sh' in the directory tree 29# 30# 31# USE CASES: 32# 'I have an example file that takes care of all 42 points in one go. It is 33# located in $OPENCMISS_ROOT/cm/examples/.../jimmysexample/': 34# ./make42tree.py /cm/examples/.../jimmysexample/bin/x86_64-linux/mpich2/gnu/jimmysexample 35# 36# 'I have made some changes and I need to regenerate my expected output': 37# python 38# import make42tree 39# run42FirstTime() 40# 41# 'But I don't want to re-run _everything_!': 42# Read the 'TODO' below 43# 44# 45# ARGUMENT FORMAT: 46# -DIM = 2D | 3D 47# -ELEM = TRI | TET | QUAD | HEX | HERMITE 48# -BASIS = linear | quadratic | cubic | hermite 49# -LEVEL = 1 | 2 | 3 50# 51# 52# TODO: 53# - Add docstrings 54# - Add 'exceptions' (folders to be skipped when traversing the tree). We kinda 55# need this for steps 2/3 above. 56# - Add parameter checking/automatic replacement of path with $OPENCMISS_ROOT 57 58 59import sys 60import os 61import subprocess 62import stat 63 64 65# This function traverses a directory tree, calling 'nodeFunc' in every folder 66# that has subfolders and calling 'leafFunc' in the remaining folders. By 67# changing the functions passed in 'traverseDirTree' can be used for each step 68# of setting up a '42 Testing Points' example and the structure specification 69# format allows flexibility in the exact structure of the tree. (Eg. 'cubic' 70# was changed to 'CubicVelocityLinearPressure' in the Navier Stokes tree) 71# 72# The tree structure specification is defined below 73# 74# tree ::= [level, level, ...] 75# level ::= [option, option, ...] 76# | (tree, tree, ...) 77# 78# Here 'option' must be a string - this denotes an actual folder name. 79# 80# INTERPRETATION: 81# This structure in essence just defines a tree where each folder has a sub- 82# folders that are defined by the next 'level' spec. in the list. 83# 84# Eg. [['left', 'right'], ['up', 'down'], ['forward', 'reverse']] would 85# define a tree where 'left' and 'right' had as subfolders 'up' and 'down' 86# etc. 87# 88# We can define more complex trees by throwing in tuples of trees where lists 89# of folder names once were. A tuple of trees effectively results in each tree 90# being substituted into the whole spec and executed as above. 91# 92# Eg. [['L', 'R'], ([['U'], ['F', 'R']], [['D'],['N', 'S']]), ['foo', 'bar']] 93# results in the following two trees: 94# [['L', 'R'], ['U'], ['F', 'R'], ['foo', 'bar']] 95# and 96# [['L', 'R'], ['D'], ['N', 'S'], ['foo', 'bar']] 97# 98# Notice how difficult this is to read and write. One should probably proceed 99# by carefully modifying the specification defined later in the document. 100 101def traverseDirTree(spec, funcs, sofar = []): 102 if len(spec) == 0: # Leaf node 103 funcs.leafFunc(sofar) 104 else: 105 cur = spec[0] 106 if type(cur) == tuple: # Tuple trees 107 for t in cur: 108 traverseDirTree(t + spec[1:], funcs, sofar) 109 else: # An actual tree 110 for n in cur: 111 funcs.nodeFunc(sofar + [n]) 112 try: 113 os.chdir(n) 114 traverseDirTree(spec[1:], funcs, sofar + [n]) 115 os.chdir('..') 116 except OSError: 117 print('%s does not exist, continuing...' % n) 118 119 120 121class makeDirFuncs(object): 122 def nodeFunc(self, sofar): 123 d = sofar[-1] 124 print('Making directory %s' % d) 125 try: 126 os.mkdir(d) 127 except OSError: 128 print('Cannot create %s (or it exists), continuing...' % d) 129 130 def leafFunc(self, sofar): pass 131 132 133 134class makeScriptFuncs(object): 135 def __init__(self, defaultargs = '', execdir = '/'): 136 self.defaultargs = defaultargs 137 self.execdir = execdir 138 139 @staticmethod 140 def makeArgs(sofar): 141 # The easiest way to do this 142 args = '' 143 args += '-DIM=' + sofar[0] + ' ' 144 args += '-ELEM=' + sofar[1] + ' ' 145 if (sofar[1] == 'HERMITE'): 146 args += '-BASIS=hermite ' 147 else: 148 args += '-BASIS=' + sofar[2] + ' ' 149 args += '-LEVEL=' + sofar[-1][-1] 150 return args 151 152 @staticmethod 153 def makeLocalDirs(sofar): 154 try: 155 os.mkdir('input') 156 os.mkdir('output') 157 os.mkdir('expected_results') 158 except OSError: 159 print('Error making leaf folders, continuing...') 160 161 def nodeFunc(self, sofar): pass 162 163 def leafFunc(self, sofar): 164 print('Making leaf %s' % '/'.join(sofar)) 165 makeScriptFuncs.makeLocalDirs(sofar) 166 options = makeScriptFuncs.makeArgs(sofar) 167 168 # Make the run script 169 try: 170 run42 = open('run42.sh', 'w') 171 except IOError: 172 print('Cannot make run42.sh in leaf, continuing...!?') 173 else: 174 run42.write('#!/bin/bash\n') 175 # Should look at making following line more robust, people will most likely make mistakes 176 if sofar[-1] == 'LEVEL_3': 177 run42.write('mpiexec -n 2 $OPENCMISS_ROOT/' + self.execdir + ' ' + self.defaultargs + ' ' + options + '\n') 178 else: 179 run42.write('$OPENCMISS_ROOT/' + self.execdir + ' ' + self.defaultargs + ' ' + options + '\n') 180 run42.write('mv *.exnode *.exelem output/\n') 181 run42.close() 182 183 # This method did not work, need to read more help 184 # os.chmod('run42.sh', stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH) 185 os.chmod('run42.sh', 0755) 186 # Neither does this 187 # subprocess.call('chmod +x run42.sh') 188 189 190 191class firstTimeRunFuncs(object): 192 def nodeFunc(self, sofar): pass 193 194 def leafFunc(self, sofar): 195 print('Running %s' % '/'.join(sofar) + '/run42.sh') 196 try: 197 ret = subprocess.call('/bin/bash run42.sh', shell=True) 198 except Exception: 199 print('Could not run subprocess') 200 else: 201 if not ret == 0: 202 print('Bad return code %d, continuing...' % ret) 203 else: 204 subprocess.call('mv *.exnode *.exelem expected_results') 205 206 207 208# The specification for the default 42 Testing Points tree 209spec42 = [( 210 [['2D'], ( 211 [['TRI', 'QUAD'], ['linear', 'quadratic', 'cubic']], 212 [['HERMITE']] 213 ) 214 ], 215 [['3D'], ( 216 [['TET', 'HEX'], ['linear', 'quadratic', 'cubic']], 217 [['HERMITE']] 218 ) 219 ] 220 ), 221 ['LEVEL_1', 'LEVEL_2', 'LEVEL_3'] 222 ] 223 224 225 226# Nice wrappers for the 3 main stages of setup 227def make42Structure(spec = spec42): 228 traverseDirTree(spec, makeDirFuncs()) 229 230def make42Scripts(defaultargs, execdir, spec = spec42): 231 traverseDirTree(spec, makeScriptFuncs(defaultargs, execdir)) 232 233def run42FirstTime(spec = spec42): 234 traverseDirTree(spec, firstTimeRunFuncs()) 235 236 237# When used as a script 238if __name__ == '__main__': 239 240 def printUsageDie(): 241 print('make42tree.py') 242 print('') 243 print('Usage:') 244 print(' ./make42tree.py EXECUTABLE ARGS ROOT_DIR') 245 print('') 246 print(' EXECUTABLE : Path to the compiled example binary') 247 print(' This must be with respect to OPENCMISS_ROOT,') 248 print(' which will be prepended to this string.') 249 print(' ARGS : Default arguments that the example requires ' 250 '[= \'\']') 251 print(' ROOT_DIR : Directory to build the folder structure ' 252 '[= ./]') 253 sys.exit('Incorrect usage!') 254 255 if len(sys.argv) < 2: 256 printUsageDie() 257 258 execdir = sys.argv[1] 259 260 if len(sys.argv) < 3: 261 rootdir = './' 262 else: 263 rootdir = sys.argv[2] 264 265 if len(sys.argv) < 4: 266 defaultargs = '' 267 else: 268 defaultargs = sys.argv[3] 269 270 try: 271 os.chdir(rootdir) 272 except OSError: 273 print('Cannot go to ROOT_DIR=%s' % rootdir) 274 printUsageDie() 275 276 make42Structure() 277 make42Scripts(defaultargs, execdir) 278 run42FirstTime() 279 280