From a36f4d76bb3ad7954f2fd355769baa3770fa4dac Mon Sep 17 00:00:00 2001 From: rowanc1 Date: Wed, 23 Apr 2014 12:11:36 -0700 Subject: [PATCH] Rearranging util codes. Added meshUtils.closestPoints --- SimPEG/Solver.py | 6 +- SimPEG/Utils/CounterUtils.py | 114 +++++++++++++ SimPEG/Utils/__init__.py | 306 +---------------------------------- SimPEG/Utils/codeutils.py | 199 +++++++++++++++++++++++ SimPEG/Utils/matutils.py | 9 +- SimPEG/Utils/meshutils.py | 17 +- 6 files changed, 329 insertions(+), 322 deletions(-) create mode 100644 SimPEG/Utils/CounterUtils.py create mode 100644 SimPEG/Utils/codeutils.py diff --git a/SimPEG/Solver.py b/SimPEG/Solver.py index 85a87bdc..e91affb9 100644 --- a/SimPEG/Solver.py +++ b/SimPEG/Solver.py @@ -7,18 +7,20 @@ import warnings DEFAULTS = {'direct':'scipy', 'iter':'scipy', 'triangular':'fortran', 'diagonal':'python'} OPTIONS = {'direct':['scipy'], 'iter':['scipy'], 'triangular':['python'], 'diagonal':['python']} +HELP = [] + try: import Utils.TriSolve as TriSolve OPTIONS['triangular'].append('fortran') except Exception, e: - print 'Warning: Python backend is being used for solver. Run setup.py from the command line.' + HELP += ['Warning: Python backend is being used for solver. Run setup.py from the command line.'] DEFAULTS['triangular'] = 'python' try: import mumps OPTIONS['direct'].append('mumps') except Exception, e: - print 'Warning: mumps solver not available.' + HELP += ['Warning: mumps solver not available.'] class Solver(object): """ diff --git a/SimPEG/Utils/CounterUtils.py b/SimPEG/Utils/CounterUtils.py new file mode 100644 index 00000000..fd9b7727 --- /dev/null +++ b/SimPEG/Utils/CounterUtils.py @@ -0,0 +1,114 @@ +import types +import time +import numpy as np +from functools import wraps + +class Counter(object): + """ + Counter allows anything that calls it to record iterations and + timings in a simple way. + + Also has plotting functions that allow quick recalls of data. + + If you want to use this, import *count* or *timeIt* and use them as decorators on class methods. + + :: + + class MyClass(object): + def __init__(self, url): + self.counter = Counter() + + @count + def MyMethod(self): + pass + + @timeIt + def MySecondMethod(self): + pass + + c = MyClass('blah') + for i in range(100): c.MyMethod() + for i in range(300): c.MySecondMethod() + c.counter.summary() + + """ + def __init__(self): + self._countList = {} + self._timeList = {} + + def count(self, prop): + """ + Increases the count of the property. + """ + assert type(prop) is str, 'The property must be a string.' + if prop not in self._countList: + self._countList[prop] = 0 + self._countList[prop] += 1 + + def countTic(self, prop): + """ + Times a property call, this is the init call. + """ + assert type(prop) is str, 'The property must be a string.' + if prop not in self._timeList: + self._timeList[prop] = [] + self._timeList[prop].append(-time.time()) + + def countToc(self, prop): + """ + Times a property call, this is the end call. + """ + assert type(prop) is str, 'The property must be a string.' + assert prop in self._timeList, 'The property must already be in the dictionary.' + self._timeList[prop][-1] += time.time() + + def summary(self): + """ + Provides a text summary of the current counters and timers. + """ + print 'Counters:' + for prop in sorted(self._countList): + print " {0:<40}: {1:8d}".format(prop,self._countList[prop]) + print '\nTimes:'+' '*40+'mean sum' + for prop in sorted(self._timeList): + l = len(self._timeList[prop]) + a = np.array(self._timeList[prop]) + print " {0:<40}: {1:4.2e}, {2:4.2e}, {3:4d}x".format(prop,a.mean(),a.sum(),l) + +def count(f): + @wraps(f) + def wrapper(self,*args,**kwargs): + counter = getattr(self,'counter',None) + if type(counter) is Counter: counter.count(self.__class__.__name__+'.'+f.__name__) + out = f(self,*args,**kwargs) + return out + return wrapper + +def timeIt(f): + @wraps(f) + def wrapper(self,*args,**kwargs): + counter = getattr(self,'counter',None) + if type(counter) is Counter: counter.countTic(self.__class__.__name__+'.'+f.__name__) + out = f(self,*args,**kwargs) + if type(counter) is Counter: counter.countToc(self.__class__.__name__+'.'+f.__name__) + return out + return wrapper + + +if __name__ == '__main__': + class MyClass(object): + def __init__(self, url): + self.counter = Counter() + + @count + def MyMethod(self): + pass + + @timeIt + def MySecondMethod(self): + pass + + c = MyClass('blah') + for i in range(100): c.MyMethod() + for i in range(300): c.MySecondMethod() + c.counter.summary() diff --git a/SimPEG/Utils/__init__.py b/SimPEG/Utils/__init__.py index 918740e9..eb8975b9 100644 --- a/SimPEG/Utils/__init__.py +++ b/SimPEG/Utils/__init__.py @@ -1,310 +1,10 @@ from matutils import * -from meshutils import exampleLrmGrid, meshTensors, points2nodes, writeUBCTensorMesh, writeUBCTensorModel, readUBCTensorMesh, readUBCTensorModel +from codeutils import * +from meshutils import exampleLrmGrid, meshTensors, closestPoints, writeUBCTensorMesh, writeUBCTensorModel from lrmutils import volTetra, faceInfo, indexCube from interputils import interpmat from ipythonutils import easyAnimate as animate +from CounterUtils import * import ModelBuilder import SolverUtils -import types -import time -import numpy as np -from functools import wraps - - -class SimPEGMetaClass(type): - def __new__(cls, name, bases, attrs): - return super(SimPEGMetaClass, cls).__new__(cls, name, bases, attrs) - - -def hook(obj, method, name=None, overwrite=False, silent=False): - """ - This dynamically binds a method to the instance of the class. - - If name is None, the name of the method is used. - """ - if name is None: - name = method.__name__ - if name == '': - raise Exception('Must provide name to hook lambda functions.') - if not hasattr(obj,name) or overwrite: - setattr(obj, name, types.MethodType( method, obj )) - if getattr(obj,'debug',False): - print 'Method '+name+' was added to class.' - elif not silent or getattr(obj,'debug',False): - print 'Method '+name+' was not overwritten.' - - -def setKwargs(obj, **kwargs): - """Sets key word arguments (kwargs) that are present in the object, throw an error if they don't exist.""" - for attr in kwargs: - if hasattr(obj, attr): - setattr(obj, attr, kwargs[attr]) - else: - raise Exception('%s attr is not recognized' % attr) - - hook(obj,hook, silent=True) - hook(obj,setKwargs, silent=True) - -def printTitles(obj, printers, name='Print Titles', pad=''): - titles = '' - widths = 0 - for printer in printers: - titles += ('{:^%i}'%printer['width']).format(printer['title']) + '' - widths += printer['width'] - print pad + "{0} {1} {0}".format('='*((widths-1-len(name))/2), name) - print pad + titles - print pad + "%s" % '-'*widths - -def printLine(obj, printers, pad=''): - values = '' - for printer in printers: - values += ('{:^%i}'%printer['width']).format(printer['format'] % printer['value'](obj)) - print pad + values - -def checkStoppers(obj, stoppers): - # check stopping rules - optimal = [] - critical = [] - for stopper in stoppers: - l = stopper['left'](obj) - r = stopper['right'](obj) - if stopper['stopType'] == 'optimal': - optimal.append(l <= r) - if stopper['stopType'] == 'critical': - critical.append(l <= r) - - if obj.debug: print 'checkStoppers.optimal: ', optimal - if obj.debug: print 'checkStoppers.critical: ', critical - - return (len(optimal)>0 and all(optimal)) | (len(critical)>0 and any(critical)) - -def printStoppers(obj, stoppers, pad='', stop='STOP!', done='DONE!'): - print pad + "%s%s%s" % ('-'*25,stop,'-'*25) - for stopper in stoppers: - l = stopper['left'](obj) - r = stopper['right'](obj) - print pad + stopper['str'] % (l<=r,l,r) - print pad + "%s%s%s" % ('-'*25,done,'-'*25) - -def callHooks(match, mainFirst=False): - """ - Use this to wrap a funciton:: - - @callHooks('doEndIteration') - def doEndIteration(self): - pass - - This will call everything named _doEndIteration* at the beginning of the function call. - By default the master method (doEndIteration) is run after all of the sub methods (_doEndIteration*). - This can be reversed by adding the mainFirst=True kwarg. - """ - def callHooksWrap(f): - @wraps(f) - def wrapper(self,*args,**kwargs): - - if not mainFirst: - for method in [posible for posible in dir(self) if ('_'+match) in posible]: - if getattr(self,'debug',False): print (match+' is calling self.'+method) - getattr(self,method)(*args, **kwargs) - - return f(self,*args,**kwargs) - else: - out = f(self,*args,**kwargs) - - for method in [posible for posible in dir(self) if ('_'+match) in posible]: - if getattr(self,'debug',False): print (match+' is calling self.'+method) - getattr(self,method)(*args, **kwargs) - - return out - - - extra = """ - If you have things that also need to run in the method %s, you can create a method:: - - def _%s*(self, ... ): - pass - - Where the * can be any string. If present, _%s* will be called at the start of the default %s call. - You may also completely overwrite this function. - """ % (match, match, match, match) - doc = wrapper.__doc__ - wrapper.__doc__ = ('' if doc is None else doc) + extra - return wrapper - return callHooksWrap - -def dependentProperty(name, value, children, doc): - def fget(self): return getattr(self,name,value) - def fset(self, val): - if isScalar(val) and getattr(self,name,value) == val: - return # it is the same! - for child in children: - if hasattr(self, child): - delattr(self, child) - setattr(self, name, val) - return property(fget=fget, fset=fset, doc=doc) - - -def asArray_N_x_Dim(pts, dim): - if type(pts) == list: - pts = np.array(pts) - assert type(pts) == np.ndarray, "pts must be a numpy array" - - if dim > 1: - pts = np.atleast_2d(pts) - elif len(pts.shape) == 1: - pts = pts[:,np.newaxis] - - assert pts.shape[1] == dim, "pts must be a column vector of shape (nPts, %d) not (%d, %d)" % ((dim,)+pts.shape) - - return pts - -def requires(var): - """ - Use this to wrap a funciton:: - - @requires('prob') - def dpred(self): - pass - - This wrapper will ensure that a problem has been bound to the data. - If a problem is not bound an Exception will be raised, and an nice error message printed. - """ - def requiresVar(f): - if var is 'prob': - extra = """ - - .. note:: - - To use survey.%s(), SimPEG requires that a problem be bound to the survey. - If a problem has not been bound, an Exception will be raised. - To bind a problem to the Data object:: - - survey.pair(myProblem) - - """ % f.__name__ - else: - extra = """ - To use *%s* method, SimPEG requires that the %s be specified. - """ % (f.__name__, var) - @wraps(f) - def requiresVarWrapper(self,*args,**kwargs): - if getattr(self, var, None) is None: - raise Exception(extra) - return f(self,*args,**kwargs) - - doc = requiresVarWrapper.__doc__ - requiresVarWrapper.__doc__ = ('' if doc is None else doc) + extra - - return requiresVarWrapper - return requiresVar - -class Counter(object): - """ - Counter allows anything that calls it to record iterations and - timings in a simple way. - - Also has plotting functions that allow quick recalls of data. - - If you want to use this, import *count* or *timeIt* and use them as decorators on class methods. - - :: - - class MyClass(object): - def __init__(self, url): - self.counter = Counter() - - @count - def MyMethod(self): - pass - - @timeIt - def MySecondMethod(self): - pass - - c = MyClass('blah') - for i in range(100): c.MyMethod() - for i in range(300): c.MySecondMethod() - c.counter.summary() - - """ - def __init__(self): - self._countList = {} - self._timeList = {} - - def count(self, prop): - """ - Increases the count of the property. - """ - assert type(prop) is str, 'The property must be a string.' - if prop not in self._countList: - self._countList[prop] = 0 - self._countList[prop] += 1 - - def countTic(self, prop): - """ - Times a property call, this is the init call. - """ - assert type(prop) is str, 'The property must be a string.' - if prop not in self._timeList: - self._timeList[prop] = [] - self._timeList[prop].append(-time.time()) - - def countToc(self, prop): - """ - Times a property call, this is the end call. - """ - assert type(prop) is str, 'The property must be a string.' - assert prop in self._timeList, 'The property must already be in the dictionary.' - self._timeList[prop][-1] += time.time() - - def summary(self): - """ - Provides a text summary of the current counters and timers. - """ - print 'Counters:' - for prop in sorted(self._countList): - print " {0:<40}: {1:8d}".format(prop,self._countList[prop]) - print '\nTimes:'+' '*40+'mean sum' - for prop in sorted(self._timeList): - l = len(self._timeList[prop]) - a = np.array(self._timeList[prop]) - print " {0:<40}: {1:4.2e}, {2:4.2e}, {3:4d}x".format(prop,a.mean(),a.sum(),l) - -def count(f): - @wraps(f) - def wrapper(self,*args,**kwargs): - counter = getattr(self,'counter',None) - if type(counter) is Counter: counter.count(self.__class__.__name__+'.'+f.__name__) - out = f(self,*args,**kwargs) - return out - return wrapper - -def timeIt(f): - @wraps(f) - def wrapper(self,*args,**kwargs): - counter = getattr(self,'counter',None) - if type(counter) is Counter: counter.countTic(self.__class__.__name__+'.'+f.__name__) - out = f(self,*args,**kwargs) - if type(counter) is Counter: counter.countToc(self.__class__.__name__+'.'+f.__name__) - return out - return wrapper - - -if __name__ == '__main__': - class MyClass(object): - def __init__(self, url): - self.counter = Counter() - - @count - def MyMethod(self): - pass - - @timeIt - def MySecondMethod(self): - pass - - c = MyClass('blah') - for i in range(100): c.MyMethod() - for i in range(300): c.MySecondMethod() - c.counter.summary() diff --git a/SimPEG/Utils/codeutils.py b/SimPEG/Utils/codeutils.py new file mode 100644 index 00000000..5762117d --- /dev/null +++ b/SimPEG/Utils/codeutils.py @@ -0,0 +1,199 @@ +import types +import time +import numpy as np +from functools import wraps + + +class SimPEGMetaClass(type): + def __new__(cls, name, bases, attrs): + return super(SimPEGMetaClass, cls).__new__(cls, name, bases, attrs) + + +def hook(obj, method, name=None, overwrite=False, silent=False): + """ + This dynamically binds a method to the instance of the class. + + If name is None, the name of the method is used. + """ + if name is None: + name = method.__name__ + if name == '': + raise Exception('Must provide name to hook lambda functions.') + if not hasattr(obj,name) or overwrite: + setattr(obj, name, types.MethodType( method, obj )) + if getattr(obj,'debug',False): + print 'Method '+name+' was added to class.' + elif not silent or getattr(obj,'debug',False): + print 'Method '+name+' was not overwritten.' + + +def setKwargs(obj, **kwargs): + """Sets key word arguments (kwargs) that are present in the object, throw an error if they don't exist.""" + for attr in kwargs: + if hasattr(obj, attr): + setattr(obj, attr, kwargs[attr]) + else: + raise Exception('%s attr is not recognized' % attr) + + hook(obj,hook, silent=True) + hook(obj,setKwargs, silent=True) + +def printTitles(obj, printers, name='Print Titles', pad=''): + titles = '' + widths = 0 + for printer in printers: + titles += ('{:^%i}'%printer['width']).format(printer['title']) + '' + widths += printer['width'] + print pad + "{0} {1} {0}".format('='*((widths-1-len(name))/2), name) + print pad + titles + print pad + "%s" % '-'*widths + +def printLine(obj, printers, pad=''): + values = '' + for printer in printers: + values += ('{:^%i}'%printer['width']).format(printer['format'] % printer['value'](obj)) + print pad + values + +def checkStoppers(obj, stoppers): + # check stopping rules + optimal = [] + critical = [] + for stopper in stoppers: + l = stopper['left'](obj) + r = stopper['right'](obj) + if stopper['stopType'] == 'optimal': + optimal.append(l <= r) + if stopper['stopType'] == 'critical': + critical.append(l <= r) + + if obj.debug: print 'checkStoppers.optimal: ', optimal + if obj.debug: print 'checkStoppers.critical: ', critical + + return (len(optimal)>0 and all(optimal)) | (len(critical)>0 and any(critical)) + +def printStoppers(obj, stoppers, pad='', stop='STOP!', done='DONE!'): + print pad + "%s%s%s" % ('-'*25,stop,'-'*25) + for stopper in stoppers: + l = stopper['left'](obj) + r = stopper['right'](obj) + print pad + stopper['str'] % (l<=r,l,r) + print pad + "%s%s%s" % ('-'*25,done,'-'*25) + +def callHooks(match, mainFirst=False): + """ + Use this to wrap a funciton:: + + @callHooks('doEndIteration') + def doEndIteration(self): + pass + + This will call everything named _doEndIteration* at the beginning of the function call. + By default the master method (doEndIteration) is run after all of the sub methods (_doEndIteration*). + This can be reversed by adding the mainFirst=True kwarg. + """ + def callHooksWrap(f): + @wraps(f) + def wrapper(self,*args,**kwargs): + + if not mainFirst: + for method in [posible for posible in dir(self) if ('_'+match) in posible]: + if getattr(self,'debug',False): print (match+' is calling self.'+method) + getattr(self,method)(*args, **kwargs) + + return f(self,*args,**kwargs) + else: + out = f(self,*args,**kwargs) + + for method in [posible for posible in dir(self) if ('_'+match) in posible]: + if getattr(self,'debug',False): print (match+' is calling self.'+method) + getattr(self,method)(*args, **kwargs) + + return out + + + extra = """ + If you have things that also need to run in the method %s, you can create a method:: + + def _%s*(self, ... ): + pass + + Where the * can be any string. If present, _%s* will be called at the start of the default %s call. + You may also completely overwrite this function. + """ % (match, match, match, match) + doc = wrapper.__doc__ + wrapper.__doc__ = ('' if doc is None else doc) + extra + return wrapper + return callHooksWrap + +def dependentProperty(name, value, children, doc): + def fget(self): return getattr(self,name,value) + def fset(self, val): + if isScalar(val) and getattr(self,name,value) == val: + return # it is the same! + for child in children: + if hasattr(self, child): + delattr(self, child) + setattr(self, name, val) + return property(fget=fget, fset=fset, doc=doc) + +def isScalar(f): + scalarTypes = [float, int, long, np.float_, np.int_] + if type(f) in scalarTypes: + return True + elif type(f) == np.ndarray and f.size == 1 and type(f[0]) in scalarTypes: + return True + return False + +def asArray_N_x_Dim(pts, dim): + if type(pts) == list: + pts = np.array(pts) + assert type(pts) == np.ndarray, "pts must be a numpy array" + + if dim > 1: + pts = np.atleast_2d(pts) + elif len(pts.shape) == 1: + pts = pts[:,np.newaxis] + + assert pts.shape[1] == dim, "pts must be a column vector of shape (nPts, %d) not (%d, %d)" % ((dim,)+pts.shape) + + return pts + +def requires(var): + """ + Use this to wrap a funciton:: + + @requires('prob') + def dpred(self): + pass + + This wrapper will ensure that a problem has been bound to the data. + If a problem is not bound an Exception will be raised, and an nice error message printed. + """ + def requiresVar(f): + if var is 'prob': + extra = """ + + .. note:: + + To use survey.%s(), SimPEG requires that a problem be bound to the survey. + If a problem has not been bound, an Exception will be raised. + To bind a problem to the Data object:: + + survey.pair(myProblem) + + """ % f.__name__ + else: + extra = """ + To use *%s* method, SimPEG requires that the %s be specified. + """ % (f.__name__, var) + @wraps(f) + def requiresVarWrapper(self,*args,**kwargs): + if getattr(self, var, None) is None: + raise Exception(extra) + return f(self,*args,**kwargs) + + doc = requiresVarWrapper.__doc__ + requiresVarWrapper.__doc__ = ('' if doc is None else doc) + extra + + return requiresVarWrapper + return requiresVar diff --git a/SimPEG/Utils/matutils.py b/SimPEG/Utils/matutils.py index 80ef32e3..992ed3bc 100644 --- a/SimPEG/Utils/matutils.py +++ b/SimPEG/Utils/matutils.py @@ -1,15 +1,8 @@ import numpy as np import scipy.sparse as sp +from codeutils import isScalar -def isScalar(f): - scalarTypes = [float, int, long, np.float_, np.int_] - if type(f) in scalarTypes: - return True - elif type(f) == np.ndarray and f.size == 1 and type(f[0]) in scalarTypes: - return True - return False - def mkvc(x, numDims=1): """Creates a vector with the number of dimension specified diff --git a/SimPEG/Utils/meshutils.py b/SimPEG/Utils/meshutils.py index 231a20e6..b64cecfc 100644 --- a/SimPEG/Utils/meshutils.py +++ b/SimPEG/Utils/meshutils.py @@ -1,6 +1,7 @@ import numpy as np from scipy import sparse as sp from matutils import mkvc, ndgrid, sub2ind, sdiag +from codeutils import asArray_N_x_Dim def exampleLrmGrid(nC, exType): assert type(nC) == list, "nC must be a list containing the number of nodes" @@ -52,25 +53,23 @@ def meshTensors(*args): return list(tensors) if len(tensors) > 1 else tensors[0] -def points2nodes(mesh, pts): +def closestPoints(mesh, pts, gridLoc='CC'): """ - Move a list of the nearest nodes to a set of points + Move a list of points to the closest points on a grid. - :param simpeg.Mesh.TensorMesh mesh: The mesh + :param simpeg.Mesh.BaseMesh mesh: The mesh :param numpy.ndarray pts: Points to move + :param string gridLoc: ['CC', 'N', 'Fx', 'Fy', 'Fz', 'Ex', 'Ex', 'Ey', 'Ez'] :rtype: numpy.ndarray :return: nodeInds """ - pts = np.atleast_2d(pts) - - assert mesh._meshType in ['TENSOR', 'CYL'] - assert pts.shape[1] == mesh.dim - + pts = asArray_N_x_Dim(pts, mesh.dim) + grid = getattr(mesh, 'grid' + gridLoc) nodeInds = np.empty(pts.shape[0], dtype=int) for i, pt in enumerate(pts): - nodeInds[i] = ((np.tile(pt, (mesh.gridN.shape[0],1)) - mesh.gridN)**2).sum(axis=1).argmin() + nodeInds[i] = ((np.tile(pt, (grid.shape[0],1)) - grid)**2).sum(axis=1).argmin() return nodeInds