source: anuga_core/source/anuga/utilities/numerical_tools.py @ 3850

Last change on this file since 3850 was 3850, checked in by ole, 18 years ago

Introduced a covariance measure to verify Okushiri timeseries plus
some incidental work

File size: 8.6 KB
Line 
1#!/usr/bin/env python
2"""Auxiliary numerical tools
3
4"""
5
6from math import acos, pi, sqrt
7from warnings import warn
8
9#Establish which Numeric package to use
10#(this should move to somewhere central)
11#try:
12#    from scipy import ArrayType, array, sum, innerproduct, ravel, sqrt,
13# searchsorted, sort, concatenate, Float, arange   
14#except:
15#    #print 'Could not find scipy - using Numeric'
16#    from Numeric import ArrayType, array, sum, innerproduct, ravel, sqrt,
17#searchsorted, sort, concatenate, Float, arange
18
19from Numeric import ArrayType, array, sum, innerproduct, ravel, sqrt,\
20     searchsorted, sort, concatenate, Float, arange   
21
22# Getting an infinite number to use when using Numeric
23#INF = (array([1])/0.)[0]
24
25NAN = (array([1])/0.)[0]
26# Note, INF is used instead of NAN (Not a number), since Numeric has no NAN
27# if we use a package that has NAN, this should be updated to use NAN.
28
29
30def safe_acos(x):
31    """Safely compute acos
32
33    Protect against cases where input argument x is outside the allowed
34    interval [-1.0, 1.0] by no more than machine precision
35    """
36
37    error_msg = 'Input to acos is outside allowed domain [-1.0, 1.0].'+\
38                'I got %.12f' %x
39    warning_msg = 'Changing argument to acos from %.18f to %.1f' %(x, sign(x))
40
41    eps = get_machine_precision() # Machine precision
42    if x < -1.0:
43        if x < -1.0 - eps:
44            raise ValueError, errmsg
45        else:
46            warn(warning_msg)
47            x = -1.0
48
49    if x > 1.0:
50        if x > 1.0 + eps:
51            raise ValueError, errmsg
52        else:
53            print 'NOTE: changing argument to acos from %.18f to 1.0' %x
54            x = 1.0
55
56    return acos(x)
57
58
59def sign(x):
60    if x > 0: return 1
61    if x < 0: return -1
62    if x == 0: return 0   
63   
64
65def angle(v1, v2=None):
66    """Compute angle between 2D vectors v1 and v2.
67   
68    If v2 is not specified it will default
69    to e1 (the unit vector in the x-direction)
70
71    The angle is measured as a number in [0, 2pi] from v2 to v1.
72    """
73 
74    # Prepare two Numeric vectors
75    if v2 is None:
76        v2 = [1.0, 0.0] # Unit vector along the x-axis
77       
78    v1 = ensure_numeric(v1, Float)
79    v2 = ensure_numeric(v2, Float)   
80   
81    # Normalise
82    v1 = v1/sqrt(sum(v1**2))
83    v2 = v2/sqrt(sum(v2**2))
84
85    # Compute angle
86    p = innerproduct(v1, v2)
87    c = innerproduct(v1, normal_vector(v2)) # Projection onto normal
88                                            # (negative cross product)
89       
90    theta = safe_acos(p)
91           
92   
93    # Correct if v1 is in quadrant 3 or 4 with respect to v2 (as the x-axis)
94    # If v2 was the unit vector [1,0] this would correspond to the test
95    # if v1[1] < 0: theta = 2*pi-theta   
96    # In general we use the sign of the projection onto the normal.
97    if c < 0: 
98       #Quadrant 3 or 4
99       theta = 2*pi-theta       
100       
101    return theta
102
103   
104def anglediff(v0, v1):
105    """Compute difference between angle of vector v0 (x0, y0) and v1 (x1, y1).
106    This is used for determining the ordering of vertices,
107    e.g. for checking if they are counter clockwise.
108
109    Always return a positive value
110    """
111
112    from math import pi
113
114    a0 = angle(v0)
115    a1 = angle(v1)
116
117    #Ensure that difference will be positive
118    if a0 < a1:
119        a0 += 2*pi
120
121    return a0-a1
122
123def normal_vector(v):
124    """Normal vector to v.
125
126    Returns vector 90 degrees counter clockwise to and of same length as v
127    """
128   
129    return array([-v[1], v[0]], Float)
130
131   
132#def crossproduct_length(v1, v2):
133#    return v1[0]*v2[1]-v2[0]*v1[1]
134   
135       
136def mean(x):
137    """Mean value of a vector
138    """
139    return(float(sum(x))/len(x))
140
141
142def cov(x, y=None):
143    """Covariance of vectors x and y.
144
145    If y is None: return cov(x, x)
146    """
147   
148    if y is None:
149        y = x
150
151    x = ensure_numeric(x)
152    y = ensure_numeric(y)       
153    assert(len(x)==len(y))
154
155    N = len(x) 
156    cx = x - mean(x) 
157    cy = y - mean(y) 
158
159    p = innerproduct(cx,cy)/N
160    return(p)
161
162
163def err(x, y=0, n=2, relative=True):
164    """Relative error of ||x-y|| to ||y||
165       n = 2:    Two norm
166       n = None: Max norm
167
168       If denominator evaluates to zero or
169       if y is omitted or
170       if keyword relative is False,
171       absolute error is returned
172    """
173
174    x = ensure_numeric(x)
175    if y:
176        y = ensure_numeric(y)       
177
178    if n == 2:
179        err = norm(x-y)
180        if relative is True:
181            try:
182                err = err/norm(y)
183            except:
184                pass
185
186    else:
187        err = max(abs(x-y))
188        if relative is True:
189            try:
190                err = err/max(abs(y))   
191            except:
192                pass
193         
194    return err
195 
196
197def norm(x):
198    """2-norm of x
199    """
200 
201    y = ravel(x)
202    p = sqrt(innerproduct(y,y))
203    return p
204   
205 
206def corr(x, y=None):
207    """Correlation of x and y
208    If y is None return autocorrelation of x
209    """
210
211    from math import sqrt
212    if y is None:
213        y = x
214
215    varx = cov(x)
216    vary = cov(y)
217
218    if varx == 0 or vary == 0:
219        C = 0
220    else: 
221        C = cov(x,y)/sqrt(varx * vary)   
222
223    return(C)
224
225
226       
227def ensure_numeric(A, typecode = None):
228    """Ensure that sequence is a Numeric array.
229    Inputs:
230        A: Sequence. If A is already a Numeric array it will be returned
231                     unaltered
232                     If not, an attempt is made to convert it to a Numeric
233                     array
234        typecode: Numeric type. If specified, use this in the conversion.
235                                If not, let Numeric decide
236
237    This function is necessary as array(A) can cause memory overflow.
238    """
239
240    if typecode is None:
241        if type(A) == ArrayType:
242            return A
243        else:
244            return array(A)
245    else:
246        if type(A) == ArrayType:
247            if A.typecode == typecode:
248                return array(A)  #FIXME: Shouldn't this just return A?
249            else:
250                return array(A,typecode)
251        else:
252            return array(A,typecode)
253
254
255
256
257def histogram(a, bins, relative=False):
258    """Standard histogram straight from the Numeric manual
259
260    If relative is True, values will be normalised againts the total and
261    thus represent frequencies rather than counts.
262    """
263
264    n = searchsorted(sort(a), bins)
265    n = concatenate( [n, [len(a)]] )
266
267    hist = n[1:]-n[:-1]
268
269    if relative is True:
270        hist = hist/float(sum(hist))
271       
272    return hist
273
274def create_bins(data, number_of_bins = None):
275    """Safely create bins for use with histogram
276    If data contains only one point or is constant, one bin will be created.
277    If number_of_bins in omitted 10 bins will be created
278    """
279
280    mx = max(data)
281    mn = min(data)
282
283    if mx == mn:
284        bins = array([mn])
285    else:
286        if number_of_bins is None:
287            number_of_bins = 10
288           
289        bins = arange(mn, mx, (mx-mn)/number_of_bins)
290
291    return bins
292
293def get_machine_precision():
294    """Calculate the machine precision for Floats
295    """
296
297    epsilon = 1.
298    while epsilon/2 + 1. > 1.:
299        epsilon /= 2
300
301    return epsilon   
302
303####################################################################
304#Python versions of function that are also implemented in numerical_tools_ext.c
305#
306
307def gradient_python(x0, y0, x1, y1, x2, y2, q0, q1, q2):
308    """
309    """
310
311    det = (y2-y0)*(x1-x0) - (y1-y0)*(x2-x0)
312    a = (y2-y0)*(q1-q0) - (y1-y0)*(q2-q0)
313    a /= det
314
315    b = (x1-x0)*(q2-q0) - (x2-x0)*(q1-q0)
316    b /= det
317
318    return a, b
319
320
321def gradient2_python(x0, y0, x1, y1, q0, q1):
322    """Compute radient based on two points and enforce zero gradient
323    in the direction orthogonal to (x1-x0), (y1-y0)
324    """
325
326    #Old code
327    #det = x0*y1 - x1*y0
328    #if det != 0.0:
329    #    a = (y1*q0 - y0*q1)/det
330    #    b = (x0*q1 - x1*q0)/det
331
332    #Correct code (ON)
333    det = (x1-x0)**2 + (y1-y0)**2
334    if det != 0.0:
335        a = (x1-x0)*(q1-q0)/det
336        b = (y1-y0)*(q1-q0)/det
337       
338    return a, b       
339
340
341##############################################
342#Initialise module
343
344from anuga.utilities import compile
345if compile.can_use_C_extension('util_ext.c'):
346    from util_ext import gradient, gradient2
347else:
348    gradient = gradient_python
349    gradient2 = gradient2_python   
350
351
352if __name__ == "__main__":
353    pass
354
355
356   
357def angle_obsolete(v):
358    """Compute angle between e1 (the unit vector in the x-direction)
359    and the specified vector v.
360   
361    Return a number in [0, 2pi]
362    """
363    from math import acos, pi, sqrt
364 
365    # Normalise v
366    v = ensure_numeric(v, Float)
367    v = v/sqrt(sum(v**2))
368   
369    # Compute angle
370    theta = acos(v[0])
371     
372    if v[1] < 0: 
373       #Quadrant 3 or 4
374        theta = 2*pi-theta
375   
376    return theta
377   
Note: See TracBrowser for help on using the repository browser.