2525
2626import pint
2727UNITS = pint .UnitRegistry ()
28+ Quantity = UNITS .Quantity
2829
2930from numpy import sqrt , array , linspace , pi
3031
3738from scipy .interpolate import interp1d
3839from scipy .integrate import odeint
3940from scipy .optimize import leastsq
41+ from scipy .optimize import minimize_scalar
4042
4143from time import sleep
4244
@@ -62,7 +64,7 @@ def linspace(start, stop, num=50, **kwargs):
6264 return array
6365
6466
65- def linrange (start , stop = None , step = 1 , ** kwargs ):
67+ def linrange (start = 0 , stop = None , step = 1 , ** kwargs ):
6668 """Returns evenly spaced samples over the interval [start, stop].
6769
6870 start: number or Quantity
@@ -158,6 +160,67 @@ def units_on():
158160 UNITS ._get_dimensionality = SAVED_PINT_METHOD
159161
160162
163+
164+ def min_bounded (min_func , bounds , * args , ** options ):
165+ """Finds the input value that minimizes `min_func`.
166+
167+ min_func: computes the function to be minimized
168+ bounds: sequence of two values, lower and upper bounds of the
169+ range to be searched
170+ args: any additional positional arguments are passed to min_func
171+ options: any keyword arguments are passed as options to minimize_scalar
172+
173+ returns: OptimizeResult object
174+ (see https://docs.scipy.org/doc/scipy/
175+ reference/generated/scipy.optimize.minimize_scalar.html)
176+ """
177+ try :
178+ midpoint = np .mean (bounds )
179+ min_func (midpoint , * args )
180+ except Exception as e :
181+ msg = """Before running scipy.integrate.odeint, I tried
182+ running the slope function you provided with the
183+ initial conditions in system and t=0, and I got
184+ the following error:"""
185+ logger .error (msg )
186+ raise (e )
187+
188+ underride (options , xatol = 1e-3 )
189+
190+ res = minimize_scalar (min_func ,
191+ bracket = bounds ,
192+ bounds = bounds ,
193+ args = args ,
194+ method = 'bounded' ,
195+ options = options )
196+
197+ if not res .success :
198+ msg = """scipy.optimize.minimize_scalar did not succeed.
199+ The message it returns is %s""" % res .message
200+ raise Exception (msg )
201+
202+ return res
203+
204+
205+ def max_bounded (max_func , bounds , * args , ** options ):
206+ """Finds the input value that maximizes `max_func`.
207+
208+ min_func: computes the function to be maximized
209+ bounds: sequence of two values, lower and upper bounds of the
210+ range to be searched
211+ args: any additional positional arguments are passed to max_func
212+ options: any keyword arguments are passed as options to minimize_scalar
213+
214+ returns: OptimizeResult object
215+ (see https://docs.scipy.org/doc/scipy/
216+ reference/generated/scipy.optimize.minimize_scalar.html)
217+ """
218+ def min_func (* args ):
219+ return - max_func (* args )
220+
221+ return min_bounded (min_func , bounds , * args , ** options )
222+
223+
161224def run_odeint (system , slope_func , ** kwargs ):
162225 """Runs a simulation of the system.
163226
@@ -229,8 +292,25 @@ def interpolate(series, **options):
229292 return interp1d (series .index , series .values , ** options )
230293
231294
232- def unpack (series , names = None ):
295+ def interp_inverse (series , ** options ):
296+ """Interpolate the inverse function of a Series.
297+
298+ series: Series object, represents a mapping from `a` to `b`
299+ kind: string, which kind of iterpolation
300+ options: keyword arguments passed to interpolate
301+
302+ returns: interpolation object, can be used as a function
303+ from `b` to `a`
233304 """
305+ inverse = Series (series .index , index = series .values )
306+ T = interpolate (inverse , ** options )
307+ return T
308+
309+
310+ def unpack (series ):
311+ """Make the names in `series` available as globals.
312+
313+ series: Series with variables names in the index
234314 """
235315 frame = inspect .currentframe ()
236316 caller = frame .f_back
@@ -710,7 +790,7 @@ class SweepFrame(MyDataFrame):
710790 pass
711791
712792
713- class _Vector (UNITS . Quantity ):
793+ class _Vector (Quantity ):
714794 """Represented as a Pint Quantity with a NumPy array
715795
716796 x, y, z, mag, mag2, and angle are accessible as attributes.
@@ -788,10 +868,13 @@ def dist(self, other):
788868 return diff .mag
789869
790870 def diff_angle (self , other ):
871+ """Angular difference between two vectors, in radians.
791872 """
792- """
793- #TODO: see http://www.euclideanspace.com/maths/algebra/vectors/angleBetween/
794- raise NotImplementedError ()
873+ if len (self ) == 2 :
874+ return self .angle - other .angle
875+ else :
876+ #TODO: see http://www.euclideanspace.com/maths/algebra/vectors/angleBetween/
877+ raise NotImplementedError ()
795878
796879
797880def Vector (* args , units = None ):
0 commit comments