source: inundation/ga/storm_surge/pyvolution/test_util.py @ 1507

Last change on this file since 1507 was 1486, checked in by ole, 19 years ago

Applyed new formula for computing gradients for triangles that have only one neighbour (thanks to Matt for pointing out the bug) and added unit tests as appropriate.
See util_ext.c for documentation of the derivation

File size: 32.5 KB
Line 
1#!/usr/bin/env python
2
3
4import unittest
5from Numeric import zeros, array, allclose
6from math import sqrt, pi
7
8from util import *
9from config import epsilon
10
11
12def test_function(x, y):
13    return x+y
14
15class Test_Util(unittest.TestCase):
16    def setUp(self):
17        pass
18
19    def tearDown(self):
20        pass
21
22
23    def test_gradient(self):
24        x0 = 0.0; y0 = 0.0; z0 = 0.0
25        x1 = 1.0; y1 = 0.0; z1 = -1.0
26        x2 = 0.0; y2 = 1.0; z2 = 0.0
27
28        zx, zy = gradient(x0, y0, x1, y1, x2, y2, z0, z1, z2)
29
30        assert zx == -1.0
31        assert zy == 0.0
32
33    def test_gradient_more(self):
34        x0 = 2.0/3; y0 = 2.0/3
35        x1=  8.0/3; y1 = 2.0/3
36        x2 = 2.0/3; y2 = 8.0/3
37
38        q0 = 2.0+2.0/3
39        q1 = 8.0+2.0/3
40        q2 = 2.0+8.0/3
41
42        #Gradient of fitted pwl surface
43        a, b = gradient(x0, y0, x1, y1, x2, y2, q0, q1, q2)
44
45        assert abs(a - 3.0) < epsilon
46        assert abs(b - 1.0) < epsilon
47
48
49    def test_gradient2(self):
50        """Test two-point gradient
51        """
52       
53        x0 = 5.0; y0 = 5.0; z0 = 10.0
54        x1 = 8.0; y1 = 2.0; z1 = 1.0
55        x2 = 8.0; y2 = 8.0; z2 = 10.0
56
57        #Reference
58        zx, zy = gradient(x0, y0, x1, y1, x2, y2, z0, z1, z2)
59        a, b = gradient2(x0, y0, x1, y1, z0, z1)
60
61        assert zx == a
62        assert zy == b
63
64        z2_computed = z0 + a*(x2-x0) + b*(y2-y0)
65        assert z2_computed == z2
66       
67    def test_gradient2_more(self):
68        """Test two-point gradient more
69        """
70        x0 = 2.0; y0 = 2.0
71        x1 = 8.0; y1 = 3.0
72        x2 = 1.0; y2 = 8.0
73
74        q0 = 2.0
75        q1 = 8.0
76        q2 = q0
77
78        #Gradient of fitted pwl surface
79        a_ref, b_ref = gradient(x0, y0, x1, y1, x2, y2, q0, q1, q2)
80        a, b = gradient2(x0, y0, x1, y1, q0, q1)       
81
82        assert a == a_ref
83        assert b == b_ref
84
85
86    def test_that_C_extension_compiles(self):
87        FN = 'util_ext.c'
88        try:
89            import util_ext
90        except:
91            from compile import compile
92
93            try:
94                compile(FN)
95            except:
96                raise 'Could not compile %s' %FN
97            else:
98                import util_ext
99
100
101    def test_gradient_C_extension(self):
102        from util_ext import gradient as gradient_c
103
104        x0 = 2.0/3; y0 = 2.0/3
105        x1=  8.0/3; y1 = 2.0/3
106        x2 = 2.0/3; y2 = 8.0/3
107
108        q0 = 2.0+2.0/3
109        q1 = 8.0+2.0/3
110        q2 = 2.0+8.0/3
111
112        #Gradient of fitted pwl surface
113        a, b = gradient_c(x0, y0, x1, y1, x2, y2, q0, q1, q2)
114
115        assert abs(a - 3.0) < epsilon
116        assert abs(b - 1.0) < epsilon
117
118
119    def test_gradient_C_extension3(self):
120        from util_ext import gradient as gradient_c
121
122        from RandomArray import uniform, seed
123        seed(17, 53)
124
125        x0, x1, x2, y0, y1, y2 = uniform(0.0,3.0,6)
126
127        q0 = uniform(0.0, 10.0, 4)
128        q1 = uniform(1.0, 3.0, 4)
129        q2 = uniform(7.0, 20.0, 4)
130
131
132        for i in range(4):
133            #Gradient of fitted pwl surface
134            from util import gradient_python
135            a_ref, b_ref = gradient(x0, y0, x1, y1, x2, y2,
136                                    q0[i], q1[i], q2[i])
137
138            #print a_ref, b_ref
139            a, b = gradient_c(x0, y0, x1, y1, x2, y2,
140                              q0[i], q1[i], q2[i])
141
142            #print a, a_ref, b, b_ref
143            assert abs(a - a_ref) < epsilon
144            assert abs(b - b_ref) < epsilon
145
146
147
148    #Geometric
149    #def test_distance(self):
150    #    from util import distance#
151    #
152    #    self.failUnless( distance([4,2],[7,6]) == 5.0,
153    #                     'Distance is wrong!')
154    #    self.failUnless( allclose(distance([7,6],[9,8]), 2.82842712475),
155    #                    'distance is wrong!')
156    #    self.failUnless( allclose(distance([9,8],[4,2]), 7.81024967591),
157    #                    'distance is wrong!')
158    #
159    #    self.failUnless( distance([9,8],[4,2]) == distance([4,2],[9,8]),
160    #                    'distance is wrong!')
161
162    def test_angle(self):
163        from util import angle
164
165        assert allclose(angle([1.0, 1.0])/pi*180, 45.0)
166
167
168    def test_anglediff(self):
169        from util import anglediff
170
171        assert allclose(anglediff([0.0, 1.], [1.0, 1.0])/pi*180, 45.0)
172
173
174
175
176    def test_file_function_time(self):
177        """Test that File function interpolates correctly
178        between given times. No x,y dependency here.
179        """
180
181        #Write file
182        import os, time
183        from config import time_format
184        from math import sin, pi
185
186        finaltime = 1200
187        filename = 'test_file_function.txt'
188        fid = open(filename, 'w')
189        start = time.mktime(time.strptime('2000', '%Y'))
190        dt = 60  #One minute intervals
191        t = 0.0
192        while t <= finaltime:
193            t_string = time.strftime(time_format, time.gmtime(t+start))
194            fid.write('%s, %f %f %f\n' %(t_string, 2*t, t**2, sin(t*pi/600)))
195            t += dt
196
197        fid.close()
198
199        F = file_function(filename)
200
201        #Now try interpolation
202        for i in range(20):
203            t = i*10
204            q = F(t)
205
206            #Exact linear intpolation
207            assert allclose(q[0], 2*t)
208            if i%6 == 0:
209                assert allclose(q[1], t**2)
210                assert allclose(q[2], sin(t*pi/600))
211
212        #Check non-exact
213
214        t = 90 #Halfway between 60 and 120
215        q = F(t)
216        assert allclose( (120**2 + 60**2)/2, q[1] )
217        assert allclose( (sin(120*pi/600) + sin(60*pi/600))/2, q[2] )
218
219
220        t = 100 #Two thirds of the way between between 60 and 120
221        q = F(t)
222        assert allclose( 2*120**2/3 + 60**2/3, q[1] )
223        assert allclose( 2*sin(120*pi/600)/3 + sin(60*pi/600)/3, q[2] )
224
225        os.remove(filename)
226
227
228    def test_ensure_numeric(self):
229        from util import ensure_numeric
230        from Numeric import ArrayType, Float, array
231
232        A = [1,2,3,4]
233        B = ensure_numeric(A)
234        assert type(B) == ArrayType
235        assert B.typecode() == 'l'
236        assert B[0] == 1 and B[1] == 2 and B[2] == 3 and B[3] == 4
237
238
239        A = [1,2,3.14,4]
240        B = ensure_numeric(A)
241        assert type(B) == ArrayType
242        assert B.typecode() == 'd'
243        assert B[0] == 1 and B[1] == 2 and B[2] == 3.14 and B[3] == 4
244
245
246        A = [1,2,3,4]
247        B = ensure_numeric(A, Float)
248        assert type(B) == ArrayType
249        assert B.typecode() == 'd'
250        assert B[0] == 1.0 and B[1] == 2.0 and B[2] == 3.0 and B[3] == 4.0
251
252
253        A = [1,2,3,4]
254        B = ensure_numeric(A, Float)
255        assert type(B) == ArrayType
256        assert B.typecode() == 'd'
257        assert B[0] == 1.0 and B[1] == 2.0 and B[2] == 3.0 and B[3] == 4.0
258
259
260        A = array([1,2,3,4])
261        B = ensure_numeric(A)
262        assert type(B) == ArrayType
263        assert B.typecode() == 'l'       
264        assert A == B   
265        assert A is B   #Same object
266
267
268        A = array([1,2,3,4])
269        B = ensure_numeric(A, Float)
270        assert type(B) == ArrayType
271        assert B.typecode() == 'd'       
272        assert A == B   
273        assert A is not B   #Not the same object       
274
275
276       
277       
278
279    def test_spatio_temporal_file_function_time(self):
280        """Test that File function interpolates correctly
281        between given times.
282        NetCDF version (x,y,t dependency)
283        """
284
285        #Create NetCDF (sww) file to be read
286        # x: 0, 5, 10, 15
287        # y: -20, -10, 0, 10
288        # t: 0, 60, 120, ...., 1200
289        #
290        # test quantities (arbitrary but non-trivial expressions):
291        #
292        #   stage     = 3*x - y**2 + 2*t
293        #   xmomentum = exp( -((x-7)**2 + (y+5)**2)/20 ) * t**2
294        #   ymomentum = x**2 + y**2 * sin(t*pi/600)
295
296        #Nice test that may render some of the others redundant.
297
298        import os, time
299        from config import time_format
300        from Numeric import sin, pi, exp
301        from mesh_factory import rectangular
302        from shallow_water import Domain
303        import data_manager
304
305        finaltime = 1200
306        filename = 'test_file_function'
307
308        #Create a domain to hold test grid
309
310        points, vertices, boundary =\
311                rectangular(4, 4, 15, 30, origin = (0, -20))
312
313
314        #print 'Number of elements', len(vertices)
315        domain = Domain(points, vertices, boundary)
316        domain.smooth = False
317        domain.default_order = 2
318        domain.set_datadir('.')
319        domain.set_name(filename)
320        domain.store = True
321        domain.format = 'sww'   #Native netcdf visualisation format
322
323        #print 'E', domain.get_extent()
324        #print points
325        start = time.mktime(time.strptime('2000', '%Y'))
326        domain.starttime = start
327
328
329        #Store structure
330        domain.initialise_storage()
331
332        #Compute artificial time steps and store
333        dt = 60  #One minute intervals
334        t = 0.0
335        while t <= finaltime:
336            #Compute quantities
337            f1 = lambda x,y: 3*x - y**2 + 2*t + 4
338            domain.set_quantity('stage', f1)
339
340            f2 = lambda x,y: x+y+t**2
341            domain.set_quantity('xmomentum', f2)
342
343            f3 = lambda x,y: x**2 + y**2 * sin(t*pi/600)
344            domain.set_quantity('ymomentum', f3)
345
346            #Store and advance time
347            domain.time = t
348            domain.store_timestep(domain.conserved_quantities)
349            t += dt
350
351
352        interpolation_points = [[0,-20], [1,0], [0,1], [1.1, 3.14], [10,-12.5]]
353
354
355
356        #Set domain.starttime to too early
357        domain.starttime = start - 1
358
359        #Create file function
360        F = file_function(filename + '.sww', domain,
361                          quantities = domain.conserved_quantities,
362                          interpolation_points = interpolation_points)
363
364        #Check that FF updates fixes domain starttime
365        assert allclose(domain.starttime, start)
366
367        #Check that domain.starttime isn't updated if later
368        domain.starttime = start + 1
369        F = file_function(filename + '.sww', domain,
370                          quantities = domain.conserved_quantities,
371                          interpolation_points = interpolation_points)
372        assert allclose(domain.starttime, start+1)
373
374        domain.starttime = start
375
376
377
378        #Check linear interpolation in time
379        #for id in range(len(interpolation_points)):
380        for id in [1]:
381            x = interpolation_points[id][0]
382            y = interpolation_points[id][1]
383
384            for i in range(20):
385                t = i*10
386                k = i%6
387
388                if k == 0:
389                    q0 = F(t, point_id=id)
390                    q1 = F(t+60, point_id=id)
391
392
393                q = F(t, point_id=id)
394                assert allclose(q, (k*q1 + (6-k)*q0)/6)
395
396
397        #Another check of linear interpolation in time
398        for id in range(len(interpolation_points)):
399            q60 = F(60, point_id=id)
400            q120 = F(120, point_id=id)
401
402            t = 90 #Halfway between 60 and 120
403            q = F(t,point_id=id)
404            assert allclose( (q120+q60)/2, q )
405
406            t = 100 #Two thirds of the way between between 60 and 120
407            q = F(t, point_id=id)
408            assert allclose(q60/3 + 2*q120/3, q)
409
410
411
412        #Check that domain.starttime isn't updated if later than file starttime but earlier
413        #than file end time
414        delta = 23
415        domain.starttime = start + delta
416        F = file_function(filename + '.sww', domain,
417                          quantities = domain.conserved_quantities,
418                          interpolation_points = interpolation_points)
419        assert allclose(domain.starttime, start+delta)
420
421
422
423
424        #Now try interpolation with delta offset
425        for id in [1]:
426            x = interpolation_points[id][0]
427            y = interpolation_points[id][1]
428
429            for i in range(20):
430                t = i*10
431                k = i%6
432
433                if k == 0:
434                    q0 = F(t-delta, point_id=id)
435                    q1 = F(t+60-delta, point_id=id)
436
437                q = F(t-delta, point_id=id)
438                assert allclose(q, (k*q1 + (6-k)*q0)/6)
439
440
441        os.remove(filename + '.sww')
442
443
444
445    def test_file_function_time_with_domain(self):
446        """Test that File function interpolates correctly
447        between given times. No x,y dependency here.
448        Use domain with starttime
449        """
450
451        #Write file
452        import os, time, calendar
453        from config import time_format
454        from math import sin, pi
455        from domain import Domain
456
457        finaltime = 1200
458        filename = 'test_file_function.txt'
459        fid = open(filename, 'w')
460        start = time.mktime(time.strptime('2000', '%Y'))
461        dt = 60  #One minute intervals
462        t = 0.0
463        while t <= finaltime:
464            t_string = time.strftime(time_format, time.gmtime(t+start))
465            fid.write('%s, %f %f %f\n' %(t_string, 2*t, t**2, sin(t*pi/600)))
466            t += dt
467
468        fid.close()
469
470        a = [0.0, 0.0]
471        b = [4.0, 0.0]
472        c = [0.0, 3.0]
473
474        points = [a, b, c]
475        vertices = [[0,1,2]]
476        domain = Domain(points, vertices)
477
478        #Check that domain.starttime is updated if non-existing
479        F = file_function(filename, domain)
480        assert allclose(domain.starttime, start)
481
482        #Check that domain.starttime is updated if too early
483        domain.starttime = start - 1
484        F = file_function(filename, domain)
485        assert allclose(domain.starttime, start)
486
487        #Check that domain.starttime isn't updated if later
488        domain.starttime = start + 1
489        F = file_function(filename, domain)
490        assert allclose(domain.starttime, start+1)
491
492        domain.starttime = start
493
494
495        #Now try interpolation
496        for i in range(20):
497            t = i*10
498            q = F(t)
499
500            #Exact linear intpolation
501            assert allclose(q[0], 2*t)
502            if i%6 == 0:
503                assert allclose(q[1], t**2)
504                assert allclose(q[2], sin(t*pi/600))
505
506        #Check non-exact
507
508        t = 90 #Halfway between 60 and 120
509        q = F(t)
510        assert allclose( (120**2 + 60**2)/2, q[1] )
511        assert allclose( (sin(120*pi/600) + sin(60*pi/600))/2, q[2] )
512
513
514        t = 100 #Two thirds of the way between between 60 and 120
515        q = F(t)
516        assert allclose( 2*120**2/3 + 60**2/3, q[1] )
517        assert allclose( 2*sin(120*pi/600)/3 + sin(60*pi/600)/3, q[2] )
518
519        os.remove(filename)
520
521
522    def test_file_function_time_with_domain_different_start(self):
523        """Test that File function interpolates correctly
524        between given times. No x,y dependency here.
525        Use domain with a starttime later than that of file
526
527        ASCII version
528        """
529
530        #Write file
531        import os, time, calendar
532        from config import time_format
533        from math import sin, pi
534        from domain import Domain
535
536        finaltime = 1200
537        filename = 'test_file_function.txt'
538        fid = open(filename, 'w')
539        start = time.mktime(time.strptime('2000', '%Y'))
540        dt = 60  #One minute intervals
541        t = 0.0
542        while t <= finaltime:
543            t_string = time.strftime(time_format, time.gmtime(t+start))
544            fid.write('%s, %f %f %f\n' %(t_string, 2*t, t**2, sin(t*pi/600)))
545            t += dt
546
547        fid.close()
548
549        a = [0.0, 0.0]
550        b = [4.0, 0.0]
551        c = [0.0, 3.0]
552
553        points = [a, b, c]
554        vertices = [[0,1,2]]
555        domain = Domain(points, vertices)
556
557        #Check that domain.starttime isn't updated if later than file starttime but earlier
558        #than file end time
559        delta = 23
560        domain.starttime = start + delta
561        F = file_function(filename, domain)
562        assert allclose(domain.starttime, start+delta)
563
564
565
566
567        #Now try interpolation with delta offset
568        for i in range(20):
569            t = i*10
570            q = F(t-delta)
571
572            #Exact linear intpolation
573            assert allclose(q[0], 2*t)
574            if i%6 == 0:
575                assert allclose(q[1], t**2)
576                assert allclose(q[2], sin(t*pi/600))
577
578        #Check non-exact
579
580        t = 90 #Halfway between 60 and 120
581        q = F(t-delta)
582        assert allclose( (120**2 + 60**2)/2, q[1] )
583        assert allclose( (sin(120*pi/600) + sin(60*pi/600))/2, q[2] )
584
585
586        t = 100 #Two thirds of the way between between 60 and 120
587        q = F(t-delta)
588        assert allclose( 2*120**2/3 + 60**2/3, q[1] )
589        assert allclose( 2*sin(120*pi/600)/3 + sin(60*pi/600)/3, q[2] )
590
591        os.remove(filename)
592
593
594    def test_spatio_temporal_file_function(self):
595        """Test that spatio temporal file function performs the correct
596        interpolations in both time and space
597        """
598        import time
599
600        #Create sww file of simple propagation from left to right
601        #through rectangular domain
602        from shallow_water import Domain, Dirichlet_boundary
603        from mesh_factory import rectangular
604        from Numeric import take, concatenate, reshape
605
606        #Create basic mesh and shallow water domain
607        points, vertices, boundary = rectangular(3, 3)
608        domain1 = Domain(points, vertices, boundary)
609
610        from util import mean
611        domain1.reduction = mean
612        domain1.smooth = True #NOTE: Mimic sww output where each vertex has
613                              # only one value.
614
615        domain1.default_order = 2
616        domain1.store = True
617        domain1.set_datadir('.')
618        domain1.set_name('spatio_temporal_boundary_source_%d' %(id(self)))
619        domain1.quantities_to_be_stored = ['stage', 'xmomentum', 'ymomentum']
620
621        #Bed-slope, friction and IC at vertices (and interpolated elsewhere)
622        domain1.set_quantity('elevation', 0)
623        domain1.set_quantity('friction', 0)
624        domain1.set_quantity('stage', 0)
625
626        # Boundary conditions
627        B0 = Dirichlet_boundary([0,0,0])
628        B6 = Dirichlet_boundary([0.6,0,0])
629        domain1.set_boundary({'left': B6, 'top': B6, 'right': B0, 'bottom': B0})
630        domain1.check_integrity()
631
632        finaltime = 8
633        #Evolution
634        for t in domain1.evolve(yieldstep = 0.1, finaltime = finaltime):
635            pass
636            #domain1.write_time()
637
638
639        #Now read data from sww and check
640        from Scientific.IO.NetCDF import NetCDFFile
641        filename = domain1.get_name() + '.' + domain1.format
642        fid = NetCDFFile(filename)
643
644        x = fid.variables['x'][:]
645        y = fid.variables['y'][:]
646        stage = fid.variables['stage'][:]
647        xmomentum = fid.variables['xmomentum'][:]
648        ymomentum = fid.variables['ymomentum'][:]
649        time = fid.variables['time'][:]
650
651        #Take stage vertex values at last timestep on diagonal
652        #Diagonal is identified by vertices: 0, 5, 10, 15
653
654        timestep = len(time)-1 #Last timestep
655        d_stage = reshape(take(stage[timestep, :], [0,5,10,15]), (4,1))
656        d_uh = reshape(take(xmomentum[timestep, :], [0,5,10,15]), (4,1))
657        d_vh = reshape(take(ymomentum[timestep, :], [0,5,10,15]), (4,1))
658        D = concatenate( (d_stage, d_uh, d_vh), axis=1)
659
660        #Reference interpolated values at midpoints on diagonal at
661        #this timestep are
662        r0 = (D[0] + D[1])/2
663        r1 = (D[1] + D[2])/2
664        r2 = (D[2] + D[3])/2
665
666        #And the midpoints are found now
667        Dx = take(reshape(x, (16,1)), [0,5,10,15])
668        Dy = take(reshape(y, (16,1)), [0,5,10,15])
669
670        diag = concatenate( (Dx, Dy), axis=1)
671        d_midpoints = (diag[1:] + diag[:-1])/2
672
673        #Let us see if the file function can find the correct
674        #values at the midpoints at the last timestep:
675        f = file_function(filename, domain1,
676                          interpolation_points = d_midpoints)
677
678        q = f(timestep/10., point_id=0); assert allclose(r0, q)
679        q = f(timestep/10., point_id=1); assert allclose(r1, q)
680        q = f(timestep/10., point_id=2); assert allclose(r2, q)
681
682
683        ##################
684        #Now do the same for the first timestep
685
686        timestep = 0 #First timestep
687        d_stage = reshape(take(stage[timestep, :], [0,5,10,15]), (4,1))
688        d_uh = reshape(take(xmomentum[timestep, :], [0,5,10,15]), (4,1))
689        d_vh = reshape(take(ymomentum[timestep, :], [0,5,10,15]), (4,1))
690        D = concatenate( (d_stage, d_uh, d_vh), axis=1)
691
692        #Reference interpolated values at midpoints on diagonal at
693        #this timestep are
694        r0 = (D[0] + D[1])/2
695        r1 = (D[1] + D[2])/2
696        r2 = (D[2] + D[3])/2
697
698        #Let us see if the file function can find the correct
699        #values
700        q = f(0, point_id=0); assert allclose(r0, q)
701        q = f(0, point_id=1); assert allclose(r1, q)
702        q = f(0, point_id=2); assert allclose(r2, q)
703
704
705        ##################
706        #Now do it again for a timestep in the middle
707
708        timestep = 33
709        d_stage = reshape(take(stage[timestep, :], [0,5,10,15]), (4,1))
710        d_uh = reshape(take(xmomentum[timestep, :], [0,5,10,15]), (4,1))
711        d_vh = reshape(take(ymomentum[timestep, :], [0,5,10,15]), (4,1))
712        D = concatenate( (d_stage, d_uh, d_vh), axis=1)
713
714        #Reference interpolated values at midpoints on diagonal at
715        #this timestep are
716        r0 = (D[0] + D[1])/2
717        r1 = (D[1] + D[2])/2
718        r2 = (D[2] + D[3])/2
719
720        q = f(timestep/10., point_id=0); assert allclose(r0, q)
721        q = f(timestep/10., point_id=1); assert allclose(r1, q)
722        q = f(timestep/10., point_id=2); assert allclose(r2, q)
723
724
725        ##################
726        #Now check temporal interpolation
727        #Halfway between timestep 15 and 16
728
729        timestep = 15
730        d_stage = reshape(take(stage[timestep, :], [0,5,10,15]), (4,1))
731        d_uh = reshape(take(xmomentum[timestep, :], [0,5,10,15]), (4,1))
732        d_vh = reshape(take(ymomentum[timestep, :], [0,5,10,15]), (4,1))
733        D = concatenate( (d_stage, d_uh, d_vh), axis=1)
734
735        #Reference interpolated values at midpoints on diagonal at
736        #this timestep are
737        r0_0 = (D[0] + D[1])/2
738        r1_0 = (D[1] + D[2])/2
739        r2_0 = (D[2] + D[3])/2
740
741        #
742        timestep = 16
743        d_stage = reshape(take(stage[timestep, :], [0,5,10,15]), (4,1))
744        d_uh = reshape(take(xmomentum[timestep, :], [0,5,10,15]), (4,1))
745        d_vh = reshape(take(ymomentum[timestep, :], [0,5,10,15]), (4,1))
746        D = concatenate( (d_stage, d_uh, d_vh), axis=1)
747
748        #Reference interpolated values at midpoints on diagonal at
749        #this timestep are
750        r0_1 = (D[0] + D[1])/2
751        r1_1 = (D[1] + D[2])/2
752        r2_1 = (D[2] + D[3])/2
753
754        # The reference values are
755        r0 = (r0_0 + r0_1)/2
756        r1 = (r1_0 + r1_1)/2
757        r2 = (r2_0 + r2_1)/2
758
759        q = f((timestep - 0.5)/10., point_id=0); assert allclose(r0, q)
760        q = f((timestep - 0.5)/10., point_id=1); assert allclose(r1, q)
761        q = f((timestep - 0.5)/10., point_id=2); assert allclose(r2, q)
762
763        ##################
764        #Finally check interpolation 2 thirds of the way
765        #between timestep 15 and 16
766
767        # The reference values are
768        r0 = (r0_0 + 2*r0_1)/3
769        r1 = (r1_0 + 2*r1_1)/3
770        r2 = (r2_0 + 2*r2_1)/3
771
772        #And the file function gives
773        q = f((timestep - 1.0/3)/10., point_id=0); assert allclose(r0, q)
774        q = f((timestep - 1.0/3)/10., point_id=1); assert allclose(r1, q)
775        q = f((timestep - 1.0/3)/10., point_id=2); assert allclose(r2, q)
776
777        fid.close()
778        import os
779        os.remove(filename)
780
781
782    def test_xya_ascii(self):
783        import time, os
784        FN = 'xyatest' + str(time.time()) + '.xya'
785        fid = open(FN, 'w')
786        fid.write('      %s %s %s\n' %('a1', 'a2', 'a3') )
787        fid.write('%f %f %f %f %f\n' %(0,1,10,20,30) )
788        fid.write('%f %f %f %f %f\n' %(1,0,30,20,10) )
789        fid.write('%f %f %f %f %f\n' %(1,1,40.2,40.3,40.4) )
790        fid.close()
791
792        points, attributes = read_xya(FN, format = 'asc')
793
794        assert allclose(points, [ [0,1], [1,0], [1,1] ])
795        assert allclose(attributes['a1'], [10,30,40.2])
796        assert allclose(attributes['a2'], [20,20,40.3])
797        assert allclose(attributes['a3'], [30,10,40.4])
798
799        os.remove(FN)
800
801    def test_xya_ascii_w_names(self):
802        import time, os
803        FN = 'xyatest' + str(time.time()) + '.xya'
804        fid = open(FN, 'w')
805        fid.write('      %s %s %s\n' %('a1', 'a2', 'a3') )
806        fid.write('%f %f %f %f %f\n' %(0,1,10,20,30) )
807        fid.write('%f %f %f %f %f\n' %(1,0,30,20,10) )
808        fid.write('%f %f %f %f %f\n' %(1,1,40.2,40.3,40.4) )
809        fid.close()
810
811        points, attributes = read_xya(FN, format = 'asc')
812
813        assert allclose(points, [ [0,1], [1,0], [1,1] ])
814
815        assert allclose(attributes['a1'], [10,30,40.2])
816        assert allclose(attributes['a2'], [20,20,40.3])
817        assert allclose(attributes['a3'], [30,10,40.4])
818
819
820        os.remove(FN)
821
822
823
824
825    #Polygon stuff
826    def test_polygon_function_constants(self):
827        p1 = [[0,0], [10,0], [10,10], [0,10]]
828        p2 = [[0,0], [10,10], [15,5], [20, 10], [25,0], [30,10], [40,-10]]
829
830        f = Polygon_function( [(p1, 1.0)] )
831        z = f([5, 5, 27, 35], [5, 9, 8, -5]) #Two first inside p1
832        assert allclose(z, [1,1,0,0])
833
834
835        f = Polygon_function( [(p2, 2.0)] )
836        z = f([5, 5, 27, 35], [5, 9, 8, -5]) #First and last inside p2
837        assert allclose(z, [2,0,0,2])
838
839
840        #Combined
841        f = Polygon_function( [(p1, 1.0), (p2, 2.0)] )
842        z = f([5, 5, 27, 35], [5, 9, 8, -5])
843        assert allclose(z, [2,1,0,2])
844
845
846    def test_polygon_function_callable(self):
847        """Check that values passed into Polygon_function can be callable
848        themselves.
849        """
850        p1 = [[0,0], [10,0], [10,10], [0,10]]
851        p2 = [[0,0], [10,10], [15,5], [20, 10], [25,0], [30,10], [40,-10]]
852
853        f = Polygon_function( [(p1, test_function)] )
854        z = f([5, 5, 27, 35], [5, 9, 8, -5]) #Two first inside p1
855        assert allclose(z, [10,14,0,0])
856
857        #Combined
858        f = Polygon_function( [(p1, test_function), (p2, 2.0)] )
859        z = f([5, 5, 27, 35], [5, 9, 8, -5])
860        assert allclose(z, [2,14,0,2])
861
862
863        #Combined w default
864        f = Polygon_function( [(p1, test_function), (p2, 2.0)], default = 3.14)
865        z = f([5, 5, 27, 35], [5, 9, 8, -5])
866        assert allclose(z, [2,14,3.14,2])
867
868
869        #Combined w default func
870        f = Polygon_function( [(p1, test_function), (p2, 2.0)],
871                              default = test_function)
872        z = f([5, 5, 27, 35], [5, 9, 8, -5])
873        assert allclose(z, [2,14,35,2])
874
875
876    def test_point_on_line(self):
877
878        #Endpoints first
879        assert point_on_line( 0, 0, 0,0, 1,0 )
880        assert point_on_line( 1, 0, 0,0, 1,0 )
881
882        #Then points on line
883        assert point_on_line( 0.5, 0, 0,0, 1,0 )
884        assert point_on_line( 0, 0.5, 0,1, 0,0 )
885        assert point_on_line( 1, 0.5, 1,1, 1,0 )
886        assert point_on_line( 0.5, 0.5, 0,0, 1,1 )
887
888        #Then points not on line
889        assert not point_on_line( 0.5, 0, 0,1, 1,1 )
890        assert not point_on_line( 0, 0.5, 0,0, 1,1 )
891
892        #From real example that failed
893        assert not point_on_line( 40,50, 40,20, 40,40 )
894
895
896        #From real example that failed
897        assert not point_on_line( 40,19, 40,20, 40,40 )
898
899
900
901
902    def test_inside_polygon_main(self):
903
904
905        #Simplest case: Polygon is the unit square
906        polygon = [[0,0], [1,0], [1,1], [0,1]]
907
908        assert inside_polygon( (0.5, 0.5), polygon )
909        assert not inside_polygon( (0.5, 1.5), polygon )
910        assert not inside_polygon( (0.5, -0.5), polygon )
911        assert not inside_polygon( (-0.5, 0.5), polygon )
912        assert not inside_polygon( (1.5, 0.5), polygon )
913
914        #Try point on borders
915        assert inside_polygon( (1., 0.5), polygon, closed=True)
916        assert inside_polygon( (0.5, 1), polygon, closed=True)
917        assert inside_polygon( (0., 0.5), polygon, closed=True)
918        assert inside_polygon( (0.5, 0.), polygon, closed=True)
919
920        assert not inside_polygon( (0.5, 1), polygon, closed=False)
921        assert not inside_polygon( (0., 0.5), polygon, closed=False)
922        assert not inside_polygon( (0.5, 0.), polygon, closed=False)
923        assert not inside_polygon( (1., 0.5), polygon, closed=False)
924
925
926
927        #From real example (that failed)
928        polygon = [[20,20], [40,20], [40,40], [20,40]]
929        points = [ [40, 50] ]
930        res = inside_polygon(points, polygon)
931        assert len(res) == 0
932
933        polygon = [[20,20], [40,20], [40,40], [20,40]]
934        points = [ [25, 25], [30, 20], [40, 50], [90, 20], [40, 90] ]
935        res = inside_polygon(points, polygon)
936        assert len(res) == 2
937        assert allclose(res, [0,1])
938
939
940
941        #More convoluted and non convex polygon
942        polygon = [[0,0], [1,0], [0.5,-1], [2, -1], [2,1], [0,1]]
943        assert inside_polygon( (0.5, 0.5), polygon )
944        assert inside_polygon( (1, -0.5), polygon )
945        assert inside_polygon( (1.5, 0), polygon )
946
947        assert not inside_polygon( (0.5, 1.5), polygon )
948        assert not inside_polygon( (0.5, -0.5), polygon )
949
950
951        #Very convoluted polygon
952        polygon = [[0,0], [10,10], [15,5], [20, 10], [25,0], [30,10], [40,-10]]
953        assert inside_polygon( (5, 5), polygon )
954        assert inside_polygon( (17, 7), polygon )
955        assert inside_polygon( (27, 2), polygon )
956        assert inside_polygon( (35, -5), polygon )
957        assert not inside_polygon( (15, 7), polygon )
958        assert not inside_polygon( (24, 3), polygon )
959        assert not inside_polygon( (25, -10), polygon )
960
961
962
963        #Another combination (that failed)
964        polygon = [[0,0], [10,0], [10,10], [0,10]]
965        assert inside_polygon( (5, 5), polygon )
966        assert inside_polygon( (7, 7), polygon )
967        assert not inside_polygon( (-17, 7), polygon )
968        assert not inside_polygon( (7, 17), polygon )
969        assert not inside_polygon( (17, 7), polygon )
970        assert not inside_polygon( (27, 8), polygon )
971        assert not inside_polygon( (35, -5), polygon )
972
973
974
975
976        #Multiple polygons
977
978        polygon = [[0,0], [1,0], [1,1], [0,1], [0,0],
979                   [10,10], [11,10], [11,11], [10,11], [10,10]]
980        assert inside_polygon( (0.5, 0.5), polygon )
981        assert inside_polygon( (10.5, 10.5), polygon )
982
983        #FIXME: Fails if point is 5.5, 5.5
984        assert not inside_polygon( (0, 5.5), polygon )
985
986        #Polygon with a hole
987        polygon = [[-1,-1], [2,-1], [2,2], [-1,2], [-1,-1],
988                   [0,0], [1,0], [1,1], [0,1], [0,0]]
989
990        assert inside_polygon( (0, -0.5), polygon )
991        assert not inside_polygon( (0.5, 0.5), polygon )
992
993    def test_inside_polygon_vector_version(self):
994        #Now try the vector formulation returning indices
995        polygon = [[0,0], [1,0], [0.5,-1], [2, -1], [2,1], [0,1]]
996        points = [ [0.5, 0.5], [1, -0.5], [1.5, 0], [0.5, 1.5], [0.5, -0.5]]
997        res = inside_polygon( points, polygon, verbose=False )
998
999        assert allclose( res, [0,1,2] )
1000
1001    def test_outside_polygon(self):
1002        U = [[0,0], [1,0], [1,1], [0,1]] #Unit square   
1003
1004        assert not outside_polygon( [0.5, 0.5], U )
1005        #evaluate to False as the point 0.5, 0.5 is inside the unit square
1006       
1007        assert outside_polygon( [1.5, 0.5], U )
1008        #evaluate to True as the point 1.5, 0.5 is outside the unit square
1009       
1010        indices = outside_polygon( [[0.5, 0.5], [1, -0.5], [0.3, 0.2]], U )
1011        assert allclose( indices, [1] )
1012       
1013        #One more test of vector formulation returning indices
1014        polygon = [[0,0], [1,0], [0.5,-1], [2, -1], [2,1], [0,1]]
1015        points = [ [0.5, 0.5], [1, -0.5], [1.5, 0], [0.5, 1.5], [0.5, -0.5]]
1016        res = outside_polygon( points, polygon )
1017
1018        assert allclose( res, [3, 4] )
1019
1020
1021
1022        polygon = [[0,0], [1,0], [0.5,-1], [2, -1], [2,1], [0,1]]
1023        points = [ [0.5, 1.4], [0.5, 0.5], [1, -0.5], [1.5, 0], [0.5, 1.5], [0.5, -0.5]]
1024        res = outside_polygon( points, polygon )
1025
1026        assert allclose( res, [0, 4, 5] )       
1027     
1028    def test_outside_polygon2(self):
1029        U = [[0,0], [1,0], [1,1], [0,1]] #Unit square   
1030   
1031        assert not outside_polygon( [0.5, 1.0], U, closed = True )
1032        #evaluate to False as the point 0.5, 1.0 is inside the unit square
1033       
1034        assert outside_polygon( [0.5, 1.0], U, closed = False )
1035        #evaluate to True as the point 0.5, 1.0 is outside the unit square
1036
1037    def test_separate_points_by_polygon(self):
1038        U = [[0,0], [1,0], [1,1], [0,1]] #Unit square   
1039
1040        indices, count = separate_points_by_polygon( [[0.5, 0.5], [1, -0.5], [0.3, 0.2]], U )
1041        assert allclose( indices, [0,2,1] )
1042        assert count == 2
1043       
1044        #One more test of vector formulation returning indices
1045        polygon = [[0,0], [1,0], [0.5,-1], [2, -1], [2,1], [0,1]]
1046        points = [ [0.5, 0.5], [1, -0.5], [1.5, 0], [0.5, 1.5], [0.5, -0.5]]
1047        res, count = separate_points_by_polygon( points, polygon )
1048
1049        assert allclose( res, [0,1,2,4,3] )
1050        assert count == 3
1051
1052
1053        polygon = [[0,0], [1,0], [0.5,-1], [2, -1], [2,1], [0,1]]
1054        points = [ [0.5, 1.4], [0.5, 0.5], [1, -0.5], [1.5, 0], [0.5, 1.5], [0.5, -0.5]]
1055        res, count = separate_points_by_polygon( points, polygon )
1056
1057        assert allclose( res, [1,2,3,5,4,0] )       
1058        assert count == 3
1059       
1060
1061    def test_populate_polygon(self):
1062
1063        polygon = [[0,0], [1,0], [1,1], [0,1]]
1064        points = populate_polygon(polygon, 5)
1065
1066        assert len(points) == 5
1067        for point in points:
1068            assert inside_polygon(point, polygon)
1069
1070
1071        #Very convoluted polygon
1072        polygon = [[0,0], [10,10], [15,5], [20, 10], [25,0], [30,10], [40,-10]]
1073
1074        points = populate_polygon(polygon, 5)
1075
1076        assert len(points) == 5
1077        for point in points:
1078            assert inside_polygon(point, polygon)
1079
1080
1081
1082#-------------------------------------------------------------
1083if __name__ == "__main__":
1084    #suite = unittest.makeSuite(TestCase,'test_inside_polygon_main')
1085    suite = unittest.makeSuite(Test_Util,'test')
1086    runner = unittest.TextTestRunner()
1087    runner.run(suite)
1088
1089
1090
1091
Note: See TracBrowser for help on using the repository browser.