/42TestingPoints/TOOLS/make42tree.py

http://github.com/xyan075/examples · Python · 280 lines · 138 code · 41 blank · 101 comment · 32 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. import sys
  58. import os
  59. import subprocess
  60. import stat
  61. # This function traverses a directory tree, calling 'nodeFunc' in every folder
  62. # that has subfolders and calling 'leafFunc' in the remaining folders. By
  63. # changing the functions passed in 'traverseDirTree' can be used for each step
  64. # of setting up a '42 Testing Points' example and the structure specification
  65. # format allows flexibility in the exact structure of the tree. (Eg. 'cubic'
  66. # was changed to 'CubicVelocityLinearPressure' in the Navier Stokes tree)
  67. #
  68. # The tree structure specification is defined below
  69. #
  70. # tree ::= [level, level, ...]
  71. # level ::= [option, option, ...]
  72. # | (tree, tree, ...)
  73. #
  74. # Here 'option' must be a string - this denotes an actual folder name.
  75. #
  76. # INTERPRETATION:
  77. # This structure in essence just defines a tree where each folder has a sub-
  78. # folders that are defined by the next 'level' spec. in the list.
  79. #
  80. # Eg. [['left', 'right'], ['up', 'down'], ['forward', 'reverse']] would
  81. # define a tree where 'left' and 'right' had as subfolders 'up' and 'down'
  82. # etc.
  83. #
  84. # We can define more complex trees by throwing in tuples of trees where lists
  85. # of folder names once were. A tuple of trees effectively results in each tree
  86. # being substituted into the whole spec and executed as above.
  87. #
  88. # Eg. [['L', 'R'], ([['U'], ['F', 'R']], [['D'],['N', 'S']]), ['foo', 'bar']]
  89. # results in the following two trees:
  90. # [['L', 'R'], ['U'], ['F', 'R'], ['foo', 'bar']]
  91. # and
  92. # [['L', 'R'], ['D'], ['N', 'S'], ['foo', 'bar']]
  93. #
  94. # Notice how difficult this is to read and write. One should probably proceed
  95. # by carefully modifying the specification defined later in the document.
  96. def traverseDirTree(spec, funcs, sofar = []):
  97. if len(spec) == 0: # Leaf node
  98. funcs.leafFunc(sofar)
  99. else:
  100. cur = spec[0]
  101. if type(cur) == tuple: # Tuple trees
  102. for t in cur:
  103. traverseDirTree(t + spec[1:], funcs, sofar)
  104. else: # An actual tree
  105. for n in cur:
  106. funcs.nodeFunc(sofar + [n])
  107. try:
  108. os.chdir(n)
  109. traverseDirTree(spec[1:], funcs, sofar + [n])
  110. os.chdir('..')
  111. except OSError:
  112. print('%s does not exist, continuing...' % n)
  113. class makeDirFuncs(object):
  114. def nodeFunc(self, sofar):
  115. d = sofar[-1]
  116. print('Making directory %s' % d)
  117. try:
  118. os.mkdir(d)
  119. except OSError:
  120. print('Cannot create %s (or it exists), continuing...' % d)
  121. def leafFunc(self, sofar): pass
  122. class makeScriptFuncs(object):
  123. def __init__(self, defaultargs = '', execdir = '/'):
  124. self.defaultargs = defaultargs
  125. self.execdir = execdir
  126. @staticmethod
  127. def makeArgs(sofar):
  128. # The easiest way to do this
  129. args = ''
  130. args += '-DIM=' + sofar[0] + ' '
  131. args += '-ELEM=' + sofar[1] + ' '
  132. if (sofar[1] == 'HERMITE'):
  133. args += '-BASIS=hermite '
  134. else:
  135. args += '-BASIS=' + sofar[2] + ' '
  136. args += '-LEVEL=' + sofar[-1][-1]
  137. return args
  138. @staticmethod
  139. def makeLocalDirs(sofar):
  140. try:
  141. os.mkdir('input')
  142. os.mkdir('output')
  143. os.mkdir('expected_results')
  144. except OSError:
  145. print('Error making leaf folders, continuing...')
  146. def nodeFunc(self, sofar): pass
  147. def leafFunc(self, sofar):
  148. print('Making leaf %s' % '/'.join(sofar))
  149. makeScriptFuncs.makeLocalDirs(sofar)
  150. options = makeScriptFuncs.makeArgs(sofar)
  151. # Make the run script
  152. try:
  153. run42 = open('run42.sh', 'w')
  154. except IOError:
  155. print('Cannot make run42.sh in leaf, continuing...!?')
  156. else:
  157. run42.write('#!/bin/bash\n')
  158. # Should look at making following line more robust, people will most likely make mistakes
  159. if sofar[-1] == 'LEVEL_3':
  160. run42.write('mpiexec -n 2 $OPENCMISS_ROOT/' + self.execdir + ' ' + self.defaultargs + ' ' + options + '\n')
  161. else:
  162. run42.write('$OPENCMISS_ROOT/' + self.execdir + ' ' + self.defaultargs + ' ' + options + '\n')
  163. run42.write('mv *.exnode *.exelem output/\n')
  164. run42.close()
  165. # This method did not work, need to read more help
  166. # os.chmod('run42.sh', stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH)
  167. os.chmod('run42.sh', 0755)
  168. # Neither does this
  169. # subprocess.call('chmod +x run42.sh')
  170. class firstTimeRunFuncs(object):
  171. def nodeFunc(self, sofar): pass
  172. def leafFunc(self, sofar):
  173. print('Running %s' % '/'.join(sofar) + '/run42.sh')
  174. try:
  175. ret = subprocess.call('/bin/bash run42.sh', shell=True)
  176. except Exception:
  177. print('Could not run subprocess')
  178. else:
  179. if not ret == 0:
  180. print('Bad return code %d, continuing...' % ret)
  181. else:
  182. subprocess.call('mv *.exnode *.exelem expected_results')
  183. # The specification for the default 42 Testing Points tree
  184. spec42 = [(
  185. [['2D'], (
  186. [['TRI', 'QUAD'], ['linear', 'quadratic', 'cubic']],
  187. [['HERMITE']]
  188. )
  189. ],
  190. [['3D'], (
  191. [['TET', 'HEX'], ['linear', 'quadratic', 'cubic']],
  192. [['HERMITE']]
  193. )
  194. ]
  195. ),
  196. ['LEVEL_1', 'LEVEL_2', 'LEVEL_3']
  197. ]
  198. # Nice wrappers for the 3 main stages of setup
  199. def make42Structure(spec = spec42):
  200. traverseDirTree(spec, makeDirFuncs())
  201. def make42Scripts(defaultargs, execdir, spec = spec42):
  202. traverseDirTree(spec, makeScriptFuncs(defaultargs, execdir))
  203. def run42FirstTime(spec = spec42):
  204. traverseDirTree(spec, firstTimeRunFuncs())
  205. # When used as a script
  206. if __name__ == '__main__':
  207. def printUsageDie():
  208. print('make42tree.py')
  209. print('')
  210. print('Usage:')
  211. print(' ./make42tree.py EXECUTABLE ARGS ROOT_DIR')
  212. print('')
  213. print(' EXECUTABLE : Path to the compiled example binary')
  214. print(' This must be with respect to OPENCMISS_ROOT,')
  215. print(' which will be prepended to this string.')
  216. print(' ARGS : Default arguments that the example requires '
  217. '[= \'\']')
  218. print(' ROOT_DIR : Directory to build the folder structure '
  219. '[= ./]')
  220. sys.exit('Incorrect usage!')
  221. if len(sys.argv) < 2:
  222. printUsageDie()
  223. execdir = sys.argv[1]
  224. if len(sys.argv) < 3:
  225. rootdir = './'
  226. else:
  227. rootdir = sys.argv[2]
  228. if len(sys.argv) < 4:
  229. defaultargs = ''
  230. else:
  231. defaultargs = sys.argv[3]
  232. try:
  233. os.chdir(rootdir)
  234. except OSError:
  235. print('Cannot go to ROOT_DIR=%s' % rootdir)
  236. printUsageDie()
  237. make42Structure()
  238. make42Scripts(defaultargs, execdir)
  239. run42FirstTime()