source: inundation/utilities/numerical_tools.py @ 3390

Last change on this file since 3390 was 3103, checked in by ole, 19 years ago

Comments to userguide from Ole (meeting with Howard)

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