PageRenderTime 46ms CodeModel.GetById 14ms RepoModel.GetById 0ms app.codeStats 0ms

/src/pyclaw/util.py

https://github.com/donnaaboise/pyclaw
Python | 484 lines | 418 code | 12 blank | 54 comment | 24 complexity | ef075dcdfa17f53d0dee4d6f8e57cdc5 MD5 | raw file
Possible License(s): BSD-3-Clause
  1. #!/usr/bin/env python
  2. # encoding: utf-8
  3. r"""
  4. Pyclaw utility methods
  5. :Authors:
  6. Kyle T. Mandli (2008-08-07) Initial version
  7. Randall J. LeVeque (2009-01-01) Added svn revision
  8. Kyle T. Mandli (2009-03-01) Added topo file utilities
  9. """
  10. # ============================================================================
  11. # Copyright (C) 2008 Kyle T. Mandli <mandli@amath.washington.edu>
  12. # Copyright (C) 2009 Randall J. LeVeque <rjl@amath.washington.edu>
  13. #
  14. # Distributed under the terms of the Berkeley Software Distribution (BSD)
  15. # license
  16. # http://www.opensource.org/licenses/
  17. # ============================================================================
  18. import time
  19. import os,sys
  20. import subprocess
  21. import logging
  22. import tempfile
  23. import numpy as np
  24. def run_app_from_main(application):
  25. r"""
  26. Runs an application from apps/, automatically parsing command line keyword
  27. arguments (key=value) as parameters to the application, with positional
  28. arguments being passed to PETSc (if it is enabled).
  29. Perhaps we should take the PETSc approach of having a database of PyClaw
  30. options that can be queried for options on specific objects within the
  31. PyClaw runtime instead of front-loading everything through the application
  32. main...
  33. """
  34. # Arguments to the PyClaw should be keyword based, positional arguments
  35. # will be passed to PETSc
  36. petsc_args, app_kwargs = _info_from_argv(sys.argv)
  37. if 'use_petsc' in app_kwargs and app_kwargs['use_petsc']:
  38. import petsc4py
  39. petsc_args = [arg.replace('--','-') for arg in sys.argv[1:] if '=' not in arg]
  40. petsc4py.init(petsc_args)
  41. output=application(**app_kwargs)
  42. return output
  43. # ============================================================================
  44. # F2PY Utility Functions
  45. # ============================================================================
  46. def compile_library(source_list,module_name,interface_functions=[],
  47. local_path='./',library_path='./',f2py_flags='',
  48. FC=None,FFLAGS=None,recompile=False,clean=False):
  49. r"""
  50. Compiles and wraps fortran source into a callable module in python.
  51. This function uses f2py to create an interface from python to the fortran
  52. sources in source_list. The source_list can either be a list of names
  53. of source files in which case compile_library will search for the file in
  54. local_path and then in library_path. If a path is given, the file will be
  55. checked to see if it exists, if not it will look for the file in the above
  56. resolution order. If any source file is not found, an IOException is
  57. raised.
  58. The list interface_functions allows the user to specify which fortran
  59. functions are actually available to python. The interface functions are
  60. assumed to be in the file with their name, i.e. claw1 is located in
  61. 'claw1.f95' or 'claw1.f'.
  62. The interface from fortran may be different than the original function
  63. call in fortran so the user should make sure to check the automatically
  64. created doc string for the fortran module for proper use.
  65. Source files will not be recompiled if they have not been changed.
  66. One set of options of note is for enabling OpenMP, it requires the usual
  67. fortran flags but the OpenMP library also must be compiled in, this is
  68. done with the flag -lgomp. The call to compile_library would then be:
  69. compile_library(src,module_name,f2py_flags='-lgomp',FFLAGS='-fopenmp')
  70. For complete optimization use:
  71. FFLAGS='-O3 -fopenmp -funroll-loops -finline-functions -fdefault-real-8'
  72. :Input:
  73. - *source_list* - (list of strings) List of source files, if these are
  74. just names of the source files, i.e. 'bc1.f' then they will be searched
  75. for in the default source resolution order, if an explicit path is
  76. given, i.e. './bc1.f', then the function will use that source if it can
  77. find it.
  78. - *module_name* - (string) Name of the resulting module
  79. - *interface_functions* - (list of strings) List of function names to
  80. provide access to, if empty, all functions are accessible to python.
  81. Defaults to [].
  82. - *local_path* - (string) The base path for source resolution, defaults
  83. to './'.
  84. - *library_path* - (string) The library path for source resolution,
  85. defaults to './'.
  86. - *f2py_flags* - (string) f2py flags to be passed
  87. - *FC* - (string) Override the environment variable FC and use it to
  88. compile, note that this does not replace the compiler that f2py uses,
  89. only the object file compilation (functions that do not have
  90. interfaces)
  91. - *FFLAGS* - (string) Override the environment variable FFLAGS and pass
  92. them to the fortran compiler
  93. - *recompile* - (bool) Force recompilation of the library, defaults to
  94. False
  95. - *clean* - (bool) Force a clean build of all source files
  96. """
  97. # Setup logger
  98. logger = logging.getLogger('f2py')
  99. temp_file = tempfile.TemporaryFile()
  100. logger.info('Compiling %s' % module_name)
  101. # Force recompile if the clean flag is set
  102. if clean:
  103. recompile = True
  104. # Expand local_path and library_path
  105. local_path = os.path.expandvars(local_path)
  106. local_path = os.path.expanduser(local_path)
  107. library_path = os.path.expandvars(library_path)
  108. library_path = os.path.expanduser(library_path)
  109. # Fetch environment variables we need for compilation
  110. if FC is None:
  111. if os.environ.has_key('FC'):
  112. FC = os.environ['FC']
  113. else:
  114. FC = 'gfortran'
  115. if FFLAGS is None:
  116. if os.environ.has_key('FFLAGS'):
  117. FFLAGS = os.environ['FFLAGS']
  118. else:
  119. FFLAGS = ''
  120. # Create the list of paths to sources
  121. path_list = []
  122. for source in source_list:
  123. # Check to see if the source looks like a path, i.e. it contains the
  124. # os.path.sep character
  125. if source.find(os.path.sep) >= 0:
  126. source = os.path.expandvars(source)
  127. source = os.path.expanduser(source)
  128. # This is a path, check to see if it's valid
  129. if os.path.exists(source):
  130. path_list.append(source)
  131. continue
  132. # Otherwise, take the last part of the path and try searching for
  133. # it in the resolution order
  134. source = os.path.split(source)
  135. # Search for the source file in local_path and then library_path
  136. if os.path.exists(os.path.join(local_path,source)):
  137. path_list.append(os.path.join(local_path,source))
  138. continue
  139. elif os.path.exists(os.path.join(library_path,source)):
  140. path_list.append(os.path.join(library_path,source))
  141. continue
  142. else:
  143. raise IOError('Could not find source file %s' % source)
  144. # Compile each of the source files if the object files are not present or
  145. # if the modification date of the source file is newer than the object
  146. # file's creation date
  147. object_list = []
  148. src_list = []
  149. for path in path_list:
  150. object_path = os.path.join(os.path.split(path)[0],
  151. '.'.join((os.path.split(path)[1].split('.')[:-1][0],'o')))
  152. # Check to see if this path contains one of the interface functions
  153. if os.path.split(path)[1].split('.')[:-1][0] in interface_functions:
  154. src_list.append(path)
  155. continue
  156. # If there are no interface functions specified, then all source files
  157. # must be included in the f2py call
  158. elif len(interface_functions) == 0:
  159. src_list.append(path)
  160. continue
  161. if os.path.exists(object_path) and not clean:
  162. # Check to see if the modification date of the source file is
  163. # greater than the object file
  164. if os.path.getmtime(object_path) > os.path.getmtime(path):
  165. object_list.append(object_path)
  166. continue
  167. # Compile the source file into the object file
  168. command = '%s %s -c %s -o %s' % (FC,FFLAGS,path,object_path)
  169. logger.debug(command)
  170. subprocess.call(command,shell=True,stdout=temp_file)
  171. object_list.append(object_path)
  172. # Check to see if recompile is needed
  173. if not recompile:
  174. module_path = os.path.join('.','.'.join((module_name,'so')))
  175. if os.path.exists(module_path):
  176. for src in src_list:
  177. if os.path.getmtime(module_path) < os.path.getmtime(src):
  178. recompile = True
  179. break
  180. for obj in object_list:
  181. if os.path.getmtime(module_path) < os.path.getmtime(obj):
  182. recompile = True
  183. break
  184. else:
  185. recompile = True
  186. if recompile:
  187. # Wrap the object files into a python module
  188. f2py_command = "f2py -c"
  189. # Add standard compiler flags
  190. f2py_command = ' '.join((f2py_command,f2py_flags))
  191. f2py_command = ' '.join((f2py_command,"--f90flags='%s'" % FFLAGS))
  192. # Add module names
  193. f2py_command = ' '.join((f2py_command,'-m %s' % module_name))
  194. # Add source files
  195. f2py_command = ' '.join((f2py_command,' '.join(src_list)))
  196. # Add object files
  197. f2py_command = ' '.join((f2py_command,' '.join(object_list)))
  198. # Add interface functions
  199. if len(interface_functions) > 0:
  200. f2py_command = ' '.join( (f2py_command,'only:') )
  201. for interface in interface_functions:
  202. f2py_command = ' '.join( (f2py_command,interface) )
  203. f2py_command = ''.join( (f2py_command,' :') )
  204. logger.debug(f2py_command)
  205. status = subprocess.call(f2py_command,shell=True,stdout=temp_file)
  206. if status == 0:
  207. logger.info("Module %s compiled" % module_name)
  208. else:
  209. logger.info("Module %s failed to compile with code %s" % (module_name,status))
  210. sys.exit(13)
  211. else:
  212. logger.info("Module %s is up to date." % module_name)
  213. temp_file.seek(0)
  214. logger.debug(temp_file.read())
  215. temp_file.close()
  216. def construct_function_handle(path,function_name=None):
  217. r"""
  218. Constructs a function handle from the file at path.
  219. This function will attempt to construct a function handle from the python
  220. file at path.
  221. :Input:
  222. - *path* - (string) Path to the file containing the function
  223. - *function_name* - (string) Name of the function defined in the file
  224. that the handle will point to. Defaults to the same name as the file
  225. without the extension.
  226. :Output:
  227. - (func) Function handle to the constructed function, None if this has
  228. failed.
  229. """
  230. # Determine the resulting function_name
  231. if function_name is None:
  232. function_name = path.split('/')[-1].split('.')[0]
  233. full_path = os.path.abspath(path)
  234. if os.path.exists(full_path):
  235. suffix = path.split('.')[-1]
  236. # This is a python file and we just need to read it and map it
  237. if suffix in ['py']:
  238. execfile(full_path,globals())
  239. return eval('%s' % function_name)
  240. else:
  241. raise Exception("Invalid file type for function handle.")
  242. else:
  243. raise Exception("Invalid file path %s" % path)
  244. #---------------------------------------------------------
  245. def read_data_line(inputfile,num_entries=1,type='float'):
  246. #---------------------------------------------------------
  247. r"""
  248. Read data a single line from an input file
  249. Reads one line from an input file and returns an array of values
  250. inputfile: a file pointer to an open file object
  251. num_entries: number of entries that should be read, defaults to only 1
  252. type: Type of the values to be read in, they all most be the same type
  253. This function will return either a single value or an array of values
  254. depending on if num_entries > 1
  255. """
  256. l = []
  257. while l==[]: # skip over blank lines
  258. line = inputfile.readline()
  259. l = line.split()
  260. val = np.empty(num_entries,type)
  261. if num_entries > len(l):
  262. print 'Error in read_data_line: num_entries = ', num_entries
  263. print ' is larger than length of l = ',l
  264. try:
  265. for i in range(num_entries):
  266. exec("val[i] = %s(l[i])" % type)
  267. if num_entries == 1: # This is a convenience for calling functions
  268. return val[0]
  269. return val
  270. except(ValueError):
  271. print "Invalid type for the %s value in %s" % (i,l)
  272. print " type = ",type
  273. return None
  274. except:
  275. raise
  276. #----------------------------------------
  277. def convert_fort_double_to_float(number):
  278. #----------------------------------------
  279. r"""
  280. Converts a fortran format double to a float
  281. Converts a fortran format double to a python float.
  282. number: is a string representation of the double. Number should
  283. be of the form "1.0d0"
  284. """
  285. a = number.split('d')
  286. return float(a[0])*10**float(a[1])
  287. #-----------------------------
  288. def current_time(addtz=False):
  289. #-----------------------------
  290. # determine current time and reformat:
  291. time1 = time.asctime()
  292. year = time1[-5:]
  293. day = time1[:-14]
  294. hour = time1[-13:-5]
  295. current_time = day + year + ' at ' + hour
  296. if addtz:
  297. current_time = current_time + ' ' + time.tzname[time.daylight]
  298. return current_time
  299. def _method_info_from_argv(argv=None):
  300. """Command-line -> method call arg processing.
  301. - positional args:
  302. a b -> method('a', 'b')
  303. - intifying args:
  304. a 123 -> method('a', 123)
  305. - json loading args:
  306. a '["pi", 3.14, null]' -> method('a', ['pi', 3.14, None])
  307. - keyword args:
  308. a foo=bar -> method('a', foo='bar')
  309. - using more of the above
  310. 1234 'extras=["r2"]' -> method(1234, extras=["r2"])
  311. @param argv {list} Command line arg list. Defaults to `sys.argv`.
  312. @returns (<method-name>, <args>, <kwargs>)
  313. """
  314. import json
  315. if argv is None:
  316. argv = sys.argv
  317. method_name, arg_strs = argv[1], argv[2:]
  318. args = []
  319. kwargs = {}
  320. for s in arg_strs:
  321. if s.count('=') == 1:
  322. key, value = s.split('=', 1)
  323. else:
  324. key, value = None, s
  325. try:
  326. value = json.loads(value)
  327. except ValueError:
  328. pass
  329. if value=='True': value=True
  330. if value.lower()=='false': value=False
  331. if key:
  332. kwargs[key] = value
  333. else:
  334. args.append(value)
  335. return method_name, args, kwargs
  336. def _info_from_argv(argv=None):
  337. """Command-line -> method call arg processing.
  338. - positional args:
  339. a b -> method('a', 'b')
  340. - intifying args:
  341. a 123 -> method('a', 123)
  342. - json loading args:
  343. a '["pi", 3.14, null]' -> method('a', ['pi', 3.14, None])
  344. - keyword args:
  345. a foo=bar -> method('a', foo='bar')
  346. - using more of the above
  347. 1234 'extras=["r2"]' -> method(1234, extras=["r2"])
  348. @param argv {list} Command line arg list. Defaults to `sys.argv`.
  349. @returns (<method-name>, <args>, <kwargs>)
  350. """
  351. import json
  352. if argv is None:
  353. argv = sys.argv
  354. arg_strs = argv[1:]
  355. args = []
  356. kwargs = {}
  357. for s in arg_strs:
  358. if s.count('=') == 1:
  359. key, value = s.split('=', 1)
  360. else:
  361. key, value = None, s
  362. try:
  363. value = json.loads(value)
  364. except ValueError:
  365. pass
  366. if value=='True': value=True
  367. if value=='False': value=False
  368. if key:
  369. kwargs[key] = value
  370. else:
  371. args.append(value)
  372. return args, kwargs
  373. def _arguments_str_from_dictionary(options):
  374. """
  375. Convert method options passed as a dictionary to a str object
  376. having the form of the method arguments
  377. """
  378. option_string = ""
  379. for k in options:
  380. if isinstance(options[k], str):
  381. option_string += k+"='"+str(options[k])+"',"
  382. else:
  383. option_string += k+"="+str(options[k])+","
  384. option_string = option_string.strip(',')
  385. return option_string
  386. #-----------------------------
  387. class FrameCounter:
  388. #-----------------------------
  389. r"""
  390. Simple frame counter
  391. Simple frame counter to keep track of current frame number. This can
  392. also be used to keep multiple runs frames seperated by having multiple
  393. counters at once.
  394. Initializes to 0
  395. """
  396. def __init__(self):
  397. self.__frame = 0
  398. def __repr__(self):
  399. return str(self.__frame)
  400. def increment(self):
  401. r"""
  402. Increment the counter by one
  403. """
  404. self.__frame += 1
  405. def set_counter(self,new_frame_num):
  406. r"""
  407. Set the counter to new_frame_num
  408. """
  409. self.__frame = new_frame_num
  410. def get_counter(self):
  411. r"""
  412. Get the current frame number
  413. """
  414. return self.__frame
  415. def reset_counter(self):
  416. r"""
  417. Reset the counter to 0
  418. """
  419. self.__frame = 0