Changeset 1042
- Timestamp:
- Mar 8, 2005, 5:35:19 PM (20 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
inundation/ga/storm_surge/pyvolution/shallow_water.py
r1037 r1042 21 21 The quantities are 22 22 23 symbol variable name explanation 23 symbol variable name explanation 24 24 x x horizontal distance from origin [m] 25 y y vertical distance from origin [m] 25 y y vertical distance from origin [m] 26 26 z elevation elevation of bed on which flow is modelled [m] 27 27 h height water height above z [m] … … 45 45 46 46 Ole Nielsen, Stephen Roberts, Duncan Gray, Christopher Zoppou 47 Geoscience Australia, 2004 47 Geoscience Australia, 2004 48 48 """ 49 49 … … 66 66 67 67 conserved_quantities = ['stage', 'xmomentum', 'ymomentum'] 68 other_quantities = ['elevation', 'friction'] 69 68 other_quantities = ['elevation', 'friction'] 69 70 70 Generic_domain.__init__(self, coordinates, vertices, boundary, 71 71 conserved_quantities, other_quantities, … … 84 84 #Stored output 85 85 self.store = False 86 self.format = 'sww' 86 self.format = 'sww' 87 87 self.smooth = True 88 88 89 #Reduction operation for get_vertex_values 89 #Reduction operation for get_vertex_values 90 90 #from util import mean 91 #self.reduction = mean 91 #self.reduction = mean 92 92 self.reduction = min #Looks better near steep slopes 93 93 94 94 self.quantities_to_be_stored = ['stage'] 95 95 96 96 97 97 #Establish shortcuts to relevant quantities (for efficiency) 98 98 #self.w = self.quantities['stage'] 99 #self.uh = self.quantities['xmomentum'] 100 #self.vh = self.quantities['ymomentum'] 101 #self.z = self.quantities['elevation'] 102 #self.eta = self.quantities['friction'] 99 #self.uh = self.quantities['xmomentum'] 100 #self.vh = self.quantities['ymomentum'] 101 #self.z = self.quantities['elevation'] 102 #self.eta = self.quantities['friction'] 103 103 104 104 def check_integrity(self): … … 113 113 msg = 'Third conserved quantity must be "ymomentum"' 114 114 assert self.conserved_quantities[2] == 'ymomentum', msg 115 115 116 116 117 117 def compute_fluxes(self): … … 122 122 def distribute_to_vertices_and_edges(self): 123 123 #Call correct module function 124 #(either from this module or C-extension) 124 #(either from this module or C-extension) 125 125 distribute_to_vertices_and_edges(self) 126 126 127 127 128 #FIXME: Under construction 128 #FIXME: Under construction 129 129 # def set_defaults(self): 130 130 # """Set default values for uninitialised quantities. … … 139 139 # if not self.quantities.has_key(name): 140 140 # if name == 'stage': 141 141 142 142 # if self.quantities.has_key('elevation'): 143 143 # z = self.quantities['elevation'].vertex_values 144 # self.set_quantity(name, z) 144 # self.set_quantity(name, z) 145 145 # else: 146 # self.set_quantity(name, 0.0) 147 # else: 146 # self.set_quantity(name, 0.0) 147 # else: 148 148 # self.set_quantity(name, 0.0) 149 149 150 150 151 151 … … 161 161 # # w[k, i] = z[k, i] 162 162 163 163 164 164 # #self.quantities['stage'].interpolate() 165 165 166 166 167 167 … … 170 170 """ 171 171 172 #Call check integrity here rather than from user scripts 172 #Call check integrity here rather than from user scripts 173 173 #self.check_integrity() 174 175 176 177 174 175 176 #Initial update of vertex and edge values before any storage 177 #and or visualisation 178 178 self.distribute_to_vertices_and_edges() 179 180 179 180 181 181 #Initialise real time viz if requested 182 182 if self.visualise is True and self.time == 0.0: … … 184 184 visualise.create_surface(self) 185 185 186 #Store model data, e.g. for visualisation 186 #Store model data, e.g. for visualisation 187 187 if self.store is True and self.time == 0.0: 188 188 self.initialise_storage() … … 201 201 visualise.update(self) 202 202 203 #Store model data, e.g. for subsequent visualisation 203 #Store model data, e.g. for subsequent visualisation 204 204 if self.store is True: 205 205 self.store_timestep(self.quantities_to_be_stored) 206 207 #FIXME: Could maybe be taken from specified list 208 #of 'store every step' quantities 209 210 #Pass control on to outer loop for more specific actions 206 207 #FIXME: Could maybe be taken from specified list 208 #of 'store every step' quantities 209 210 #Pass control on to outer loop for more specific actions 211 211 yield(t) 212 213 214 def initialise_storage(self): 212 213 214 def initialise_storage(self): 215 215 """Create and initialise self.writer object for storing data. 216 216 Also, save x,y and bed elevation 217 217 """ 218 218 219 219 import data_manager 220 220 221 221 #Initialise writer 222 222 self.writer = data_manager.get_dataobject(self, mode = 'w') … … 233 233 """ 234 234 self.writer.store_timestep(name) 235 235 236 236 237 237 #Rotation of momentum vector … … 241 241 242 242 If direction is negative the rotation is inverted. 243 243 244 244 Input vector is preserved 245 245 … … 248 248 249 249 from Numeric import zeros, Float 250 250 251 251 assert len(q) == 3,\ 252 252 'Vector of conserved quantities must have length 3'\ … … 259 259 260 260 assert l == 2, 'Normal vector must have 2 components' 261 262 261 262 263 263 n1 = normal[0] 264 n2 = normal[1] 265 264 n2 = normal[1] 265 266 266 r = zeros(len(q), Float) #Rotated quantities 267 267 r[0] = q[0] #First quantity, height, is not rotated … … 273 273 r[1] = n1*q[1] + n2*q[2] 274 274 r[2] = -n2*q[1] + n1*q[2] 275 275 276 276 return r 277 277 … … 279 279 280 280 #################################### 281 # Flux computation 282 def flux_function(normal, ql, qr, zl, zr): 281 # Flux computation 282 def flux_function(normal, ql, qr, zl, zr): 283 283 """Compute fluxes between volumes for the shallow water wave equation 284 284 cast in terms of w = h+z using the 'central scheme' as described in … … 313 313 u_left = 0.0 #Could have been negative 314 314 h_left = 0.0 315 else: 315 else: 316 316 u_left = uh_left/h_left 317 317 … … 325 325 u_right = 0.0 #Could have been negative 326 326 h_right = 0.0 327 else: 327 else: 328 328 u_right = uh_right/h_right 329 329 330 330 vh_left = q_left[2] 331 vh_right = q_right[2] 332 333 soundspeed_left = sqrt(g*h_left) 331 vh_right = q_right[2] 332 333 soundspeed_left = sqrt(g*h_left) 334 334 soundspeed_right = sqrt(g*h_right) 335 335 336 336 #Maximal wave speed 337 337 s_max = max(u_left+soundspeed_left, u_right+soundspeed_right, 0) 338 339 #Minimal wave speed 338 339 #Minimal wave speed 340 340 s_min = min(u_left-soundspeed_left, u_right-soundspeed_right, 0) 341 341 … … 346 346 flux_right = array([u_right*h_right, 347 347 u_right*uh_right + 0.5*g*h_right**2, 348 u_right*vh_right]) 348 u_right*vh_right]) 349 349 350 350 denom = s_max-s_min … … 352 352 edgeflux = array([0.0, 0.0, 0.0]) 353 353 max_speed = 0.0 354 else: 354 else: 355 355 edgeflux = (s_max*flux_left - s_min*flux_right)/denom 356 356 edgeflux += s_max*s_min*(q_right-q_left)/denom … … 359 359 max_speed = max(abs(s_max), abs(s_min)) 360 360 361 return edgeflux, max_speed 361 return edgeflux, max_speed 362 362 363 363 … … 370 370 Fluxes across each edge are scaled by edgelengths and summed up 371 371 Resulting flux is then scaled by area and stored in 372 explicit_update for each of the three conserved quantities 373 stage, xmomentum and ymomentum 372 explicit_update for each of the three conserved quantities 373 stage, xmomentum and ymomentum 374 374 375 375 The maximal allowable speed computed by the flux_function for each volume … … 379 379 Post conditions: 380 380 domain.explicit_update is reset to computed flux values 381 domain.timestep is set to the largest step satisfying all volumes. 381 domain.timestep is set to the largest step satisfying all volumes. 382 382 """ 383 383 … … 386 386 387 387 N = domain.number_of_elements 388 388 389 389 #Shortcuts 390 390 Stage = domain.quantities['stage'] 391 391 Xmom = domain.quantities['xmomentum'] 392 392 Ymom = domain.quantities['ymomentum'] 393 Bed = domain.quantities['elevation'] 393 Bed = domain.quantities['elevation'] 394 394 395 395 #Arrays … … 397 397 xmom = Xmom.edge_values 398 398 ymom = Ymom.edge_values 399 bed = Bed.edge_values 399 bed = Bed.edge_values 400 400 401 401 stage_bdry = Stage.boundary_values 402 402 xmom_bdry = Xmom.boundary_values 403 403 ymom_bdry = Ymom.boundary_values 404 404 405 405 flux = zeros(3, Float) #Work array for summing up fluxes 406 406 407 407 #Loop 408 timestep = float(sys.maxint) 408 timestep = float(sys.maxint) 409 409 for k in range(N): 410 410 … … 416 416 417 417 #Quantities at neighbour on nearest face 418 n = domain.neighbours[k,i] 418 n = domain.neighbours[k,i] 419 419 if n < 0: 420 420 m = -n-1 #Convert negative flag to index 421 421 qr = [stage_bdry[m], xmom_bdry[m], ymom_bdry[m]] 422 422 zr = zl #Extend bed elevation to boundary 423 else: 423 else: 424 424 m = domain.neighbour_edges[k,i] 425 425 qr = [stage[n, m], xmom[n, m], ymom[n, m]] 426 zr = bed[n, m] 427 428 429 #Outward pointing normal vector 426 zr = bed[n, m] 427 428 429 #Outward pointing normal vector 430 430 normal = domain.normals[k, 2*i:2*i+2] 431 431 … … 447 447 Ymom.explicit_update[k] = flux[2] 448 448 449 450 domain.timestep = timestep 449 450 domain.timestep = timestep 451 451 452 452 … … 459 459 460 460 N = domain.number_of_elements 461 461 462 462 #Shortcuts 463 463 Stage = domain.quantities['stage'] 464 464 Xmom = domain.quantities['xmomentum'] 465 465 Ymom = domain.quantities['ymomentum'] 466 Bed = domain.quantities['elevation'] 467 468 timestep = float(sys.maxint) 466 Bed = domain.quantities['elevation'] 467 468 timestep = float(sys.maxint) 469 469 from shallow_water_ext import compute_fluxes 470 470 domain.timestep = compute_fluxes(timestep, domain.epsilon, domain.g, … … 472 472 domain.neighbour_edges, 473 473 domain.normals, 474 domain.edgelengths, 474 domain.edgelengths, 475 475 domain.radii, 476 476 domain.areas, 477 Stage.edge_values, 478 Xmom.edge_values, 479 Ymom.edge_values, 480 Bed.edge_values, 477 Stage.edge_values, 478 Xmom.edge_values, 479 Ymom.edge_values, 480 Bed.edge_values, 481 481 Stage.boundary_values, 482 482 Xmom.boundary_values, … … 485 485 Xmom.explicit_update, 486 486 Ymom.explicit_update) 487 487 488 488 489 489 #################################### … … 496 496 497 497 It will ensure that h (w-z) is always non-negative even in the 498 presence of steep bed-slopes by taking a weighted average between shallow 498 presence of steep bed-slopes by taking a weighted average between shallow 499 499 and deep cases. 500 500 501 501 In addition, all conserved quantities get distributed as per either a 502 502 constant (order==1) or a piecewise linear function (order==2). 503 503 504 504 FIXME: more explanation about removal of artificial variability etc 505 505 … … 510 510 Postcondition 511 511 Conserved quantities defined at vertices 512 512 513 513 """ 514 514 … … 523 523 elif domain.order == 2: 524 524 Q.extrapolate_second_order() 525 Q.limit() 525 Q.limit() 526 526 else: 527 527 raise 'Unknown order' 528 528 529 529 #Take bed elevation into account when water heights are small 530 530 balance_deep_and_shallow(domain) … … 534 534 Q = domain.quantities[name] 535 535 Q.interpolate_from_vertices_to_edges() 536 536 537 537 538 538 … … 543 543 544 544 #FIXME: Experimental (from old version). Not in use at the moment 545 545 546 546 #Shortcuts 547 547 wv = domain.quantities['stage'].vertex_values 548 548 zv = domain.quantities['elevation'].vertex_values 549 549 xmomv = domain.quantities['xmomentum'].vertex_values 550 ymomv = domain.quantities['ymomentum'].vertex_values 550 ymomv = domain.quantities['ymomentum'].vertex_values 551 551 hv = wv - zv #Water depths at vertices 552 552 … … 554 554 for k in range(domain.number_of_elements): 555 555 hmax = max(hv[k, :]) 556 556 557 557 if hmax < domain.minimum_allowed_height: 558 558 #Control stage … … 561 561 #Control momentum 562 562 xmomv[k,:] = ymomv[k,:] = 0.0 563 563 564 564 565 565 def protect_against_infinitesimal_and_negative_heights(domain): 566 566 """Protect against infinitesimal heights and associated high velocities 567 567 """ 568 568 569 569 #FIXME: Look here for error 570 570 571 571 #Shortcuts 572 572 wc = domain.quantities['stage'].centroid_values 573 573 zc = domain.quantities['elevation'].centroid_values 574 574 xmomc = domain.quantities['xmomentum'].centroid_values 575 ymomc = domain.quantities['ymomentum'].centroid_values 576 hc = wc - zc #Water depths at centroids 575 ymomc = domain.quantities['ymomentum'].centroid_values 576 hc = wc - zc #Water depths at centroids 577 577 578 578 #print zc 579 #print '1', wc 579 #print '1', wc 580 580 #Update 581 581 for k in range(domain.number_of_elements): 582 582 583 if hc[k] < domain.minimum_allowed_height: 583 if hc[k] < domain.minimum_allowed_height: 584 584 #Control stage 585 585 wc[k] = zc[k] … … 587 587 #Control momentum 588 588 xmomc[k] = ymomc[k] = 0.0 589 590 #print '2', wc 589 590 #print '2', wc 591 591 592 592 … … 594 594 """Protect against infinitesimal heights and associated high velocities 595 595 """ 596 596 597 597 #Shortcuts 598 598 wc = domain.quantities['stage'].centroid_values 599 599 zc = domain.quantities['elevation'].centroid_values 600 600 xmomc = domain.quantities['xmomentum'].centroid_values 601 ymomc = domain.quantities['ymomentum'].centroid_values 601 ymomc = domain.quantities['ymomentum'].centroid_values 602 602 603 603 from shallow_water_ext import protect 604 604 605 605 protect(domain.minimum_allowed_height, wc, zc, xmomc, ymomc) 606 607 608 609 def h_limiter(domain): 606 607 608 609 def h_limiter(domain): 610 610 """Limit slopes for each volume to eliminate artificial variance 611 611 introduced by e.g. second order extrapolator 612 612 613 613 limit on h = w-z 614 614 … … 621 621 N = domain.number_of_elements 622 622 beta_h = domain.beta_h 623 623 624 624 #Shortcuts 625 625 wc = domain.quantities['stage'].centroid_values 626 626 zc = domain.quantities['elevation'].centroid_values 627 627 hc = wc - zc 628 628 629 629 wv = domain.quantities['stage'].vertex_values 630 630 zv = domain.quantities['elevation'].vertex_values 631 631 hv = wv-zv 632 632 633 hvbar = zeros(hv.shape, Float) #h-limited values 634 633 hvbar = zeros(hv.shape, Float) #h-limited values 634 635 635 #Find min and max of this and neighbour's centroid values 636 636 hmax = zeros(hc.shape, Float) 637 hmin = zeros(hc.shape, Float) 638 637 hmin = zeros(hc.shape, Float) 638 639 639 for k in range(N): 640 640 hmax[k] = hmin[k] = hc[k] … … 643 643 if n >= 0: 644 644 hn = hc[n] #Neighbour's centroid value 645 645 646 646 hmin[k] = min(hmin[k], hn) 647 647 hmax[k] = max(hmax[k], hn) 648 649 648 649 650 650 #Diffences between centroids and maxima/minima 651 dhmax = hmax - hc 651 dhmax = hmax - hc 652 652 dhmin = hmin - hc 653 653 654 654 #Deltas between vertex and centroid values 655 655 dh = zeros(hv.shape, Float) … … 657 657 dh[:,i] = hv[:,i] - hc 658 658 659 #Phi limiter 659 #Phi limiter 660 660 for k in range(N): 661 661 662 662 #Find the gradient limiter (phi) across vertices 663 663 phi = 1.0 … … 666 666 if (dh[k,i] > 0): r = dhmax[k]/dh[k,i] 667 667 if (dh[k,i] < 0): r = dhmin[k]/dh[k,i] 668 669 phi = min( min(r*beta_h, 1), phi ) 668 669 phi = min( min(r*beta_h, 1), phi ) 670 670 671 671 #Then update using phi limiter 672 for i in range(3): 672 for i in range(3): 673 673 hvbar[k,i] = hc[k] + phi*dh[k,i] 674 674 … … 677 677 678 678 679 def h_limiter_c(domain): 679 def h_limiter_c(domain): 680 680 """Limit slopes for each volume to eliminate artificial variance 681 681 introduced by e.g. second order extrapolator 682 682 683 683 limit on h = w-z 684 684 … … 693 693 N = domain.number_of_elements 694 694 beta_h = domain.beta_h 695 695 696 696 #Shortcuts 697 697 wc = domain.quantities['stage'].centroid_values 698 698 zc = domain.quantities['elevation'].centroid_values 699 699 hc = wc - zc 700 700 701 701 wv = domain.quantities['stage'].vertex_values 702 702 zv = domain.quantities['elevation'].vertex_values … … 709 709 return hvbar 710 710 711 711 712 712 def balance_deep_and_shallow(domain): 713 713 """Compute linear combination between stage as computed by 714 gradient-limiters limiting using w, and stage computed as 714 gradient-limiters limiting using w, and stage computed as 715 715 constant height above bed and limited using h. 716 716 The former takes precedence when heights are large compared to the … … 721 721 modes. 722 722 723 The h-limiter is always applied irrespective of the order. 723 The h-limiter is always applied irrespective of the order. 724 724 """ 725 725 726 726 #New idea. 727 727 # 728 # In the presence and near of bedslope it is necessary to 729 # limit slopes based on differences in h rather than w 728 # In the presence and near of bedslope it is necessary to 729 # limit slopes based on differences in h rather than w 730 730 # (which is what is needed away from the bed). 731 731 # … … 733 733 # it will need to be balanced with a h-limited gradient. 734 734 # 735 # For this we will use the quantity alpha as before 735 # For this we will use the quantity alpha as before 736 736 # 737 737 738 738 #Shortcuts 739 739 wc = domain.quantities['stage'].centroid_values 740 740 zc = domain.quantities['elevation'].centroid_values 741 741 hc = wc - zc 742 742 743 743 wv = domain.quantities['stage'].vertex_values 744 744 zv = domain.quantities['elevation'].vertex_values … … 760 760 abs(zv[k,2]-zc[k])) 761 761 762 762 763 763 hmin = min( hv[k,:] ) 764 764 765 #Create alpha in [0,1], where alpha==0 means using the h-limited 765 #Create alpha in [0,1], where alpha==0 means using the h-limited 766 766 #stage and alpha==1 means using the w-limited stage as 767 767 #computed by the gradient limiter (both 1st or 2nd order) 768 768 769 769 #If hmin > dz/2 then alpha = 1 and the bed will have no effect 770 770 #If hmin < 0 then alpha = 0 reverting to constant height above bed. … … 773 773 alpha = max( min( 2*hmin/dz, 1.0), 0.0 ) 774 774 else: 775 #Flat bed 775 #Flat bed 776 776 alpha = 1.0 777 777 778 #Let 778 #Let 779 779 # 780 # wvi be the w-limited stage (wvi = zvi + hvi) 780 # wvi be the w-limited stage (wvi = zvi + hvi) 781 781 # wvi- be the h-limited state (wvi- = zvi + hvi-) 782 # 783 # 782 # 783 # 784 784 #where i=0,1,2 denotes the vertex ids 785 785 # 786 #Weighted balance between w-limited and h-limited stage is 786 #Weighted balance between w-limited and h-limited stage is 787 787 # 788 # wvi := (1-alpha)*(zvi+hvi-) + alpha*(zvi+hvi) 788 # wvi := (1-alpha)*(zvi+hvi-) + alpha*(zvi+hvi) 789 789 # 790 790 #It follows that the updated wvi is … … 793 793 # Momentum is balanced between constant and limited 794 794 795 795 796 796 #for i in range(3): 797 797 # wv[k,i] = zv[k,i] + hvbar[k,i] 798 798 799 799 #return 800 801 if alpha < 1: 802 800 801 if alpha < 1: 802 803 803 for i in range(3): 804 804 wv[k,i] = zv[k,i] + (1-alpha)*hvbar[k,i] + alpha*hv[k,i] 805 805 806 806 #Momentums at centroids 807 807 xmomc = domain.quantities['xmomentum'].centroid_values 808 ymomc = domain.quantities['ymomentum'].centroid_values 808 ymomc = domain.quantities['ymomentum'].centroid_values 809 809 810 810 #Momentums at vertices 811 811 xmomv = domain.quantities['xmomentum'].vertex_values 812 ymomv = domain.quantities['ymomentum'].vertex_values 813 814 # Update momentum as a linear combination of 812 ymomv = domain.quantities['ymomentum'].vertex_values 813 814 # Update momentum as a linear combination of 815 815 # xmomc and ymomc (shallow) and momentum 816 816 # from extrapolator xmomv and ymomv (deep). 817 xmomv[k,:] = (1-alpha)*xmomc[k] + alpha*xmomv[k,:] 817 xmomv[k,:] = (1-alpha)*xmomc[k] + alpha*xmomv[k,:] 818 818 ymomv[k,:] = (1-alpha)*ymomc[k] + alpha*ymomv[k,:] 819 820 819 820 821 821 def balance_deep_and_shallow_c(domain): 822 822 """Wrapper for C implementation 823 823 """ 824 824 825 825 #Shortcuts 826 826 wc = domain.quantities['stage'].centroid_values 827 827 zc = domain.quantities['elevation'].centroid_values 828 828 hc = wc - zc 829 829 830 830 wv = domain.quantities['stage'].vertex_values 831 831 zv = domain.quantities['elevation'].vertex_values … … 834 834 #Momentums at centroids 835 835 xmomc = domain.quantities['xmomentum'].centroid_values 836 ymomc = domain.quantities['ymomentum'].centroid_values 836 ymomc = domain.quantities['ymomentum'].centroid_values 837 837 838 838 #Momentums at vertices 839 839 xmomv = domain.quantities['xmomentum'].vertex_values 840 ymomv = domain.quantities['ymomentum'].vertex_values 840 ymomv = domain.quantities['ymomentum'].vertex_values 841 841 842 842 #Limit h 843 hvbar = h_limiter(domain) 844 843 hvbar = h_limiter(domain) 844 845 845 #This is how one would make a first order h_limited value 846 846 #as in the old balancer (pre 17 Feb 2005): … … 852 852 from shallow_water_ext import balance_deep_and_shallow 853 853 balance_deep_and_shallow(wc, zc, hc, wv, zv, hv, hvbar, 854 xmomc, ymomc, xmomv, ymomv) 855 856 854 xmomc, ymomc, xmomv, ymomv) 855 856 857 857 858 858 … … 867 867 and third conserved quantities. 868 868 """ 869 870 def __init__(self, domain = None): 869 870 def __init__(self, domain = None): 871 871 Boundary.__init__(self) 872 872 … … 874 874 msg = 'Domain must be specified for reflective boundary' 875 875 raise msg 876 876 877 877 #Handy shorthands 878 878 self.stage = domain.quantities['stage'].edge_values 879 879 self.xmom = domain.quantities['xmomentum'].edge_values 880 self.ymom = domain.quantities['ymomentum'].edge_values 880 self.ymom = domain.quantities['ymomentum'].edge_values 881 881 self.normals = domain.normals 882 883 from Numeric import zeros, Float 884 self.conserved_quantities = zeros(3, Float) 885 882 883 from Numeric import zeros, Float 884 self.conserved_quantities = zeros(3, Float) 885 886 886 def __repr__(self): 887 887 return 'Reflective_boundary' … … 895 895 q = self.conserved_quantities 896 896 q[0] = self.stage[vol_id, edge_id] 897 q[1] = self.xmom[vol_id, edge_id] 898 q[2] = self.ymom[vol_id, edge_id] 899 900 normal = self.normals[vol_id, 2*edge_id:2*edge_id+2] 901 902 897 q[1] = self.xmom[vol_id, edge_id] 898 q[2] = self.ymom[vol_id, edge_id] 899 900 normal = self.normals[vol_id, 2*edge_id:2*edge_id+2] 901 902 903 903 r = rotate(q, normal, direction = 1) 904 904 r[1] = -r[1] … … 907 907 return q 908 908 909 909 910 910 #class Spatio_temporal_boundary(Boundary): 911 # """The spatio-temporal boundary, reads values for the conserved 912 # quantities from an sww NetCDF file, and returns interpolated values 911 # """The spatio-temporal boundary, reads values for the conserved 912 # quantities from an sww NetCDF file, and returns interpolated values 913 913 # at the midpoints of each associated boundaty segment. 914 914 # Time dependency is interpolated linearly as in util.File_function.# 915 915 # 916 916 # Example: 917 # Bf = Spatio_temporal_boundary('source_file.sww', domain) 918 # 917 # Bf = Spatio_temporal_boundary('source_file.sww', domain) 918 # 919 919 # """ 920 920 Spatio_temporal_boundary = File_boundary 921 922 923 921 922 923 924 924 925 925 ######################### … … 937 937 938 938 Stage = domain.quantities['stage'] 939 Elevation = domain.quantities['elevation'] 939 Elevation = domain.quantities['elevation'] 940 940 h = Stage.edge_values - Elevation.edge_values 941 941 v = Elevation.vertex_values 942 942 943 943 x = domain.get_vertex_coordinates() 944 944 g = domain.g 945 946 for k in range(domain.number_of_elements): 945 946 for k in range(domain.number_of_elements): 947 947 avg_h = sum( h[k,:] )/3 948 948 949 949 #Compute bed slope 950 x0, y0, x1, y1, x2, y2 = x[k,:] 950 x0, y0, x1, y1, x2, y2 = x[k,:] 951 951 z0, z1, z2 = v[k,:] 952 952 … … 955 955 #Update momentum 956 956 xmom[k] += -g*zx*avg_h 957 ymom[k] += -g*zy*avg_h 957 ymom[k] += -g*zy*avg_h 958 958 959 959 960 960 def gravity_c(domain): 961 961 """Wrapper calling C version 962 """ 962 """ 963 963 964 964 xmom = domain.quantities['xmomentum'].explicit_update 965 965 ymom = domain.quantities['ymomentum'].explicit_update 966 966 967 Stage = domain.quantities['stage'] 968 Elevation = domain.quantities['elevation'] 967 Stage = domain.quantities['stage'] 968 Elevation = domain.quantities['elevation'] 969 969 h = Stage.edge_values - Elevation.edge_values 970 970 v = Elevation.vertex_values 971 971 972 972 x = domain.get_vertex_coordinates() 973 973 g = domain.g 974 975 974 975 976 976 from shallow_water_ext import gravity 977 977 gravity(g, h, v, x, xmom, ymom) 978 979 978 979 980 980 def manning_friction(domain): 981 981 """Apply (Manning) friction to water momentum … … 987 987 z = domain.quantities['elevation'].centroid_values 988 988 h = w-z 989 989 990 990 uh = domain.quantities['xmomentum'].centroid_values 991 991 vh = domain.quantities['ymomentum'].centroid_values 992 eta = domain.quantities['friction'].centroid_values 993 992 eta = domain.quantities['friction'].centroid_values 993 994 994 xmom_update = domain.quantities['xmomentum'].semi_implicit_update 995 ymom_update = domain.quantities['ymomentum'].semi_implicit_update 996 995 ymom_update = domain.quantities['ymomentum'].semi_implicit_update 996 997 997 N = domain.number_of_elements 998 998 eps = domain.minimum_allowed_height 999 999 g = domain.g 1000 1000 1001 1001 for k in range(N): 1002 1002 if eta[k] >= eps: … … 1008 1008 xmom_update[k] += S*uh[k] 1009 1009 ymom_update[k] += S*vh[k] 1010 1011 1010 1011 1012 1012 def manning_friction_c(domain): 1013 1013 """Wrapper for c version … … 1016 1016 1017 1017 xmom = domain.quantities['xmomentum'] 1018 ymom = domain.quantities['ymomentum'] 1019 1018 ymom = domain.quantities['ymomentum'] 1019 1020 1020 w = domain.quantities['stage'].centroid_values 1021 1021 z = domain.quantities['elevation'].centroid_values 1022 1022 1023 1023 uh = xmom.centroid_values 1024 1024 vh = ymom.centroid_values 1025 eta = domain.quantities['friction'].centroid_values 1026 1025 eta = domain.quantities['friction'].centroid_values 1026 1027 1027 xmom_update = xmom.semi_implicit_update 1028 ymom_update = ymom.semi_implicit_update 1029 1028 ymom_update = ymom.semi_implicit_update 1029 1030 1030 N = domain.number_of_elements 1031 1031 eps = domain.minimum_allowed_height … … 1050 1050 uh = domain.quantities['xmomentum'].centroid_values 1051 1051 vh = domain.quantities['ymomentum'].centroid_values 1052 tau = domain.quantities['linear_friction'].centroid_values 1053 1052 tau = domain.quantities['linear_friction'].centroid_values 1053 1054 1054 xmom_update = domain.quantities['xmomentum'].semi_implicit_update 1055 ymom_update = domain.quantities['ymomentum'].semi_implicit_update 1056 1055 ymom_update = domain.quantities['ymomentum'].semi_implicit_update 1056 1057 1057 N = domain.number_of_elements 1058 1058 eps = domain.minimum_allowed_height 1059 1059 g = domain.g #Not necessary? Why was this added? 1060 1060 1061 1061 for k in range(N): 1062 1062 if tau[k] >= eps: … … 1067 1067 xmom_update[k] += S*uh[k] 1068 1068 ymom_update[k] += S*vh[k] 1069 1070 1071 1069 1070 1071 1072 1072 def check_forcefield(f): 1073 1073 """Check that f is either … … 1075 1075 and that it returns an array or a list of same length 1076 1076 as x and y 1077 2: a scalar 1077 2: a scalar 1078 1078 """ 1079 1079 1080 1080 from Numeric import ones, Float, array 1081 1081 1082 1082 1083 1083 if callable(f): 1084 1084 N = 3 1085 1085 x = ones(3, Float) 1086 y = ones(3, Float) 1086 y = ones(3, Float) 1087 1087 try: 1088 1088 q = f(1.0, x, y) … … 1091 1091 #FIXME: Reconsider this semantics 1092 1092 raise msg 1093 1093 1094 1094 try: 1095 1095 q = array(q).astype(Float) … … 1097 1097 msg = 'Return value from vector function %s could ' %f 1098 1098 msg += 'not be converted into a Numeric array of floats.\n' 1099 msg += 'Specified function should return either list or array.' 1099 msg += 'Specified function should return either list or array.' 1100 1100 raise msg 1101 1101 … … 1103 1103 msg += 'must have same lenght as input vectors' 1104 1104 assert len(q) == N, msg 1105 1106 else: 1105 1106 else: 1107 1107 try: 1108 1108 f = float(f) … … 1111 1111 msg += ' or a vector function' 1112 1112 raise msg 1113 return f 1113 return f 1114 1114 1115 1115 1116 1116 class Wind_stress: 1117 1117 """Apply wind stress to water momentum in terms of 1118 wind speed [m/s] and wind direction [degrees] 1118 wind speed [m/s] and wind direction [degrees] 1119 1119 """ 1120 1120 … … 1122 1122 """Initialise windfield from wind speed s [m/s] 1123 1123 and wind direction phi [degrees] 1124 1124 1125 1125 Inputs v and phi can be either scalars or Python functions, e.g. 1126 1126 … … 1128 1128 1129 1129 #FIXME - 'normal' degrees are assumed for now, i.e. the 1130 vector (1,0) has zero degrees. 1130 vector (1,0) has zero degrees. 1131 1131 We may need to convert from 'compass' degrees later on and also 1132 1132 map from True north to grid north. … … 1137 1137 ... 1138 1138 return s 1139 1139 1140 1140 def angle(t,x,y): 1141 1141 ... … … 1154 1154 can be applied, providing both quantities simultaneously. 1155 1155 As in 1156 W = Wind_stress(F), where returns (speed, angle) for each t. 1156 W = Wind_stress(F), where returns (speed, angle) for each t. 1157 1157 1158 1158 domain.forcing_terms.append(W) … … 1160 1160 1161 1161 from config import rho_a, rho_w, eta_w 1162 from Numeric import array, Float 1162 from Numeric import array, Float 1163 1163 1164 1164 if len(args) == 2: … … 1172 1172 else: 1173 1173 #Assume info is in 2 keyword arguments 1174 1174 1175 1175 if len(kwargs) == 2: 1176 1176 s = kwargs['s'] 1177 1177 phi = kwargs['phi'] 1178 1178 else: 1179 raise 'Assumes two keyword arguments: s=..., phi=....' 1180 1179 raise 'Assumes two keyword arguments: s=..., phi=....' 1180 1181 1181 self.speed = check_forcefield(s) 1182 1182 self.phi = check_forcefield(phi) 1183 1183 1184 1184 self.const = eta_w*rho_a/rho_w 1185 1185 1186 1186 1187 1187 def __call__(self, domain): … … 1190 1190 1191 1191 from math import pi, cos, sin, sqrt 1192 from Numeric import Float, ones, ArrayType 1193 1192 from Numeric import Float, ones, ArrayType 1193 1194 1194 xmom_update = domain.quantities['xmomentum'].explicit_update 1195 ymom_update = domain.quantities['ymomentum'].explicit_update 1196 1195 ymom_update = domain.quantities['ymomentum'].explicit_update 1196 1197 1197 N = domain.number_of_elements 1198 t = domain.time 1199 1198 t = domain.time 1199 1200 1200 if callable(self.speed): 1201 xc = domain.get_centroid_coordinates() 1201 xc = domain.get_centroid_coordinates() 1202 1202 s_vec = self.speed(t, xc[:,0], xc[:,1]) 1203 1203 else: … … 1212 1212 1213 1213 if callable(self.phi): 1214 xc = domain.get_centroid_coordinates() 1214 xc = domain.get_centroid_coordinates() 1215 1215 phi_vec = self.phi(t, xc[:,0], xc[:,1]) 1216 1216 else: … … 1218 1218 1219 1219 try: 1220 phi_vec = self.phi * ones(N, Float) 1220 phi_vec = self.phi * ones(N, Float) 1221 1221 except: 1222 1222 msg = 'Angle must be either callable or a scalar: %s' %self.phi … … 1233 1233 """ 1234 1234 from math import pi, cos, sin, sqrt 1235 1235 1236 1236 N = len(s_vec) 1237 1237 for k in range(N): … … 1245 1245 u = s*cos(phi) 1246 1246 v = s*sin(phi) 1247 1247 1248 1248 #Compute wind stress 1249 1249 S = const * sqrt(u**2 + v**2) 1250 1250 xmom_update[k] += S*u 1251 ymom_update[k] += S*v 1252 1251 ymom_update[k] += S*v 1252 1253 1253 1254 1254 1255 1255 ############################## 1256 1256 #OBSOLETE STUFF 1257 1257 1258 1258 def balance_deep_and_shallow_old(domain): 1259 1259 """Compute linear combination between stage as computed by … … 1268 1268 1269 1269 #OBSOLETE 1270 1270 1271 1271 #Shortcuts 1272 1272 wc = domain.quantities['stage'].centroid_values 1273 1273 zc = domain.quantities['elevation'].centroid_values 1274 1274 hc = wc - zc 1275 1275 1276 1276 wv = domain.quantities['stage'].vertex_values 1277 1277 zv = domain.quantities['elevation'].vertex_values … … 1280 1280 1281 1281 #Computed linear combination between constant stages and and 1282 #stages parallel to the bed elevation. 1282 #stages parallel to the bed elevation. 1283 1283 for k in range(domain.number_of_elements): 1284 1284 #Compute maximal variation in bed elevation … … 1293 1293 abs(zv[k,2]-zc[k])) 1294 1294 1295 1295 1296 1296 hmin = min( hv[k,:] ) 1297 1297 1298 #Create alpha in [0,1], where alpha==0 means using shallow 1298 #Create alpha in [0,1], where alpha==0 means using shallow 1299 1299 #first order scheme and alpha==1 means using the stage w as 1300 1300 #computed by the gradient limiter (1st or 2nd order) … … 1302 1302 #If hmin > dz/2 then alpha = 1 and the bed will have no effect 1303 1303 #If hmin < 0 then alpha = 0 reverting to constant height above bed. 1304 1304 1305 1305 if dz > 0.0: 1306 1306 alpha = max( min( 2*hmin/dz, 1.0), 0.0 ) 1307 1307 else: 1308 #Flat bed 1308 #Flat bed 1309 1309 alpha = 1.0 1310 1310 1311 #Weighted balance between stage parallel to bed elevation 1312 #(wvi = zvi + hc) and stage as computed by 1st or 2nd 1311 #Weighted balance between stage parallel to bed elevation 1312 #(wvi = zvi + hc) and stage as computed by 1st or 2nd 1313 1313 #order gradient limiter 1314 1314 #(wvi = zvi + hvi) where i=0,1,2 denotes the vertex ids 1315 1315 # 1316 1316 #It follows that the updated wvi is 1317 # wvi := (1-alpha)*(zvi+hc) + alpha*(zvi+hvi) = 1317 # wvi := (1-alpha)*(zvi+hc) + alpha*(zvi+hvi) = 1318 1318 # zvi + hc + alpha*(hvi - hc) 1319 1319 # 1320 1320 #Note that hvi = zc+hc-zvi in the first order case (constant). 1321 1321 1322 if alpha < 1: 1322 if alpha < 1: 1323 1323 for i in range(3): 1324 1324 wv[k,i] = zv[k,i] + hc[k] + alpha*(hv[k,i]-hc[k]) 1325 1325 1326 1326 1327 1327 #Momentums at centroids 1328 1328 xmomc = domain.quantities['xmomentum'].centroid_values 1329 ymomc = domain.quantities['ymomentum'].centroid_values 1329 ymomc = domain.quantities['ymomentum'].centroid_values 1330 1330 1331 1331 #Momentums at vertices 1332 1332 xmomv = domain.quantities['xmomentum'].vertex_values 1333 ymomv = domain.quantities['ymomentum'].vertex_values 1334 1335 # Update momentum as a linear combination of 1333 ymomv = domain.quantities['ymomentum'].vertex_values 1334 1335 # Update momentum as a linear combination of 1336 1336 # xmomc and ymomc (shallow) and momentum 1337 1337 # from extrapolator xmomv and ymomv (deep). 1338 xmomv[k,:] = (1-alpha)*xmomc[k] + alpha*xmomv[k,:] 1338 xmomv[k,:] = (1-alpha)*xmomc[k] + alpha*xmomv[k,:] 1339 1339 ymomv[k,:] = (1-alpha)*ymomc[k] + alpha*ymomv[k,:] 1340 1340 1341 1341 1342 1342 … … 1353 1353 x,y are assumed to be in the unit square 1354 1354 """ 1355 1355 1356 1356 def __init__(self, stage): 1357 1357 self.inflow_stage = stage 1358 1358 1359 def __call__(self, x, y): 1359 def __call__(self, x, y): 1360 1360 from Numeric import zeros, Float 1361 1361 from math import sqrt 1362 1362 1363 1363 N = len(x) 1364 assert N == len(y) 1364 assert N == len(y) 1365 1365 1366 1366 z = zeros(N, Float) … … 1370 1370 #Flattish bit to the left 1371 1371 if x[i] < 0.3: 1372 z[i] = -x[i]/10 1373 1372 z[i] = -x[i]/10 1373 1374 1374 #Weir 1375 1375 if x[i] >= 0.3 and x[i] < 0.4: … … 1381 1381 depth = -1.0 1382 1382 #plateaux = -0.9 1383 plateaux = -0.6 1383 plateaux = -0.6 1384 1384 if y[i] < 0.7: 1385 1385 if x[i] > x0 and x[i] < 0.9: … … 1389 1389 if x[i] >= 0.9: 1390 1390 z[i] = plateaux 1391 1392 1391 1392 1393 1393 elif y[i] >= 0.7 and y[i] < 1.5: 1394 1394 #Restrict and deepen … … 1397 1397 #z[i] = depth-y[i]/5 1398 1398 #z[i] = depth 1399 elif x[i] >= 0.8: 1399 elif x[i] >= 0.8: 1400 1400 #RHS plateaux 1401 1401 z[i] = plateaux 1402 1402 1403 1403 elif y[i] >= 1.5: 1404 1404 if x[i] >= x0 and x[i] < 0.8 + (y[i]-1.5)/1.2: 1405 1405 #Widen up and stay at constant depth 1406 1406 z[i] = depth-1.5/5 1407 elif x[i] >= 0.8 + (y[i]-1.5)/1.2: 1407 elif x[i] >= 0.8 + (y[i]-1.5)/1.2: 1408 1408 #RHS plateaux 1409 z[i] = plateaux 1410 1409 z[i] = plateaux 1410 1411 1411 1412 1412 #Hole in weir (slightly higher than inflow condition) … … 1418 1418 if x[i] >= 0.4 and x[i] < x0 and y[i] > 0.2 and y[i] < 0.4: 1419 1419 z[i] = -x[i]+self.inflow_stage + 0.02 1420 1420 1421 1421 if x[i] >= x0 and x[i] < 0.6 and y[i] > 0.2 and y[i] < 0.4: 1422 1422 #Flatten it out towards the end … … 1432 1432 if x[i] >= 1.14 and x[i] < 1.2 and y[i] >= 0.4 and y[i] < 0.6: 1433 1433 z[i] = -0.9 #North south 1434 1434 1435 1435 if x[i] >= 0.9 and x[i] < 1.18 and y[i] >= 0.58 and y[i] < 0.65: 1436 1436 z[i] = -1.0 + (x[i]-0.9)/3 #East west 1437 1438 1437 1438 1439 1439 1440 1440 #Stuff not in use 1441 1441 1442 1442 #Upward slope at inlet to the north west 1443 1443 #if x[i] < 0.0: # and y[i] > 0.5: … … 1460 1460 x,y are assumed to be in the unit square 1461 1461 """ 1462 1462 1463 1463 def __init__(self, stage): 1464 1464 self.inflow_stage = stage 1465 1465 1466 def __call__(self, x, y): 1467 from Numeric import zeros, Float 1468 1466 def __call__(self, x, y): 1467 from Numeric import zeros, Float 1468 1469 1469 N = len(x) 1470 assert N == len(y) 1470 assert N == len(y) 1471 1471 1472 1472 z = zeros(N, Float) … … 1477 1477 if x[i] < 0.3: 1478 1478 z[i] = -x[i]/10 #General slope 1479 1479 1480 1480 #Weir 1481 1481 if x[i] > 0.3 and x[i] < 0.4: … … 1488 1488 #Hole in weir (slightly higher than inflow condition) 1489 1489 if x[i] > 0.3 and x[i] < 0.4 and y[i] > 0.2 and y[i] < 0.4: 1490 z[i] = -x[i]+self.inflow_stage + 0.05 1491 1490 z[i] = -x[i]+self.inflow_stage + 0.05 1491 1492 1492 1493 1493 return z/2 1494 1495 1496 1494 1495 1496 1497 1497 class Constant_height: 1498 1498 """Set an initial condition with constant water height, e.g … … 1521 1521 Fluxes across each edge are scaled by edgelengths and summed up 1522 1522 Resulting flux is then scaled by area and stored in 1523 explicit_update for each of the three conserved quantities 1524 stage, xmomentum and ymomentum 1523 explicit_update for each of the three conserved quantities 1524 stage, xmomentum and ymomentum 1525 1525 1526 1526 The maximal allowable speed computed by the flux_function for each volume … … 1530 1530 Post conditions: 1531 1531 domain.explicit_update is reset to computed flux values 1532 domain.timestep is set to the largest step satisfying all volumes. 1532 domain.timestep is set to the largest step satisfying all volumes. 1533 1533 """ 1534 1534 … … 1537 1537 1538 1538 N = domain.number_of_elements 1539 1539 1540 1540 #Shortcuts 1541 1541 Stage = domain.quantities['stage'] 1542 1542 Xmom = domain.quantities['xmomentum'] 1543 1543 Ymom = domain.quantities['ymomentum'] 1544 Bed = domain.quantities['elevation'] 1544 Bed = domain.quantities['elevation'] 1545 1545 1546 1546 #Arrays … … 1548 1548 xmom = Xmom.edge_values 1549 1549 ymom = Ymom.edge_values 1550 bed = Bed.edge_values 1550 bed = Bed.edge_values 1551 1551 1552 1552 stage_bdry = Stage.boundary_values 1553 1553 xmom_bdry = Xmom.boundary_values 1554 1554 ymom_bdry = Ymom.boundary_values 1555 1555 1556 1556 flux = zeros((N,3), Float) #Work array for summing up fluxes 1557 1557 1558 1558 #Loop 1559 timestep = float(sys.maxint) 1559 timestep = float(sys.maxint) 1560 1560 for k in range(N): 1561 1561 … … 1566 1566 1567 1567 #Quantities at neighbour on nearest face 1568 n = domain.neighbours[k,i] 1568 n = domain.neighbours[k,i] 1569 1569 if n < 0: 1570 1570 m = -n-1 #Convert negative flag to index 1571 1571 qr = [stage_bdry[m], xmom_bdry[m], ymom_bdry[m]] 1572 1572 zr = zl #Extend bed elevation to boundary 1573 else: 1573 else: 1574 1574 m = domain.neighbour_edges[k,i] 1575 1575 qr = [stage[n, m], xmom[n, m], ymom[n, m]] 1576 zr = bed[n, m] 1577 1578 1579 #Outward pointing normal vector 1576 zr = bed[n, m] 1577 1578 1579 #Outward pointing normal vector 1580 1580 normal = domain.normals[k, 2*i:2*i+2] 1581 1581 1582 1582 #Flux computation using provided function 1583 1583 edgeflux, max_speed = flux_function(normal, ql, qr, zl, zr) 1584 1584 1585 1585 flux[k,:] = edgeflux 1586 1586 1587 1587 return flux 1588 1589 1588 1589 1590 1590 1591 1591 … … 1600 1600 if compile.can_use_C_extension('shallow_water_ext.c'): 1601 1601 #Replace python version with c implementations 1602 1602 1603 1603 from shallow_water_ext import rotate, assign_windfield_values 1604 1604 compute_fluxes = compute_fluxes_c … … 1609 1609 protect_against_infinitesimal_and_negative_heights = protect_against_infinitesimal_and_negative_heights_c 1610 1610 1611 1611 1612 1612 #distribute_to_vertices_and_edges = distribute_to_vertices_and_edges_c 1613 1613 … … 1631 1631 psyco.bind(Domain.distribute_to_vertices_and_edges) 1632 1632 psyco.bind(Domain.compute_fluxes) 1633 1633 1634 1634 if __name__ == "__main__": 1635 1635 pass
Note: See TracChangeset
for help on using the changeset viewer.