Changeset 5294


Ignore:
Timestamp:
May 8, 2008, 10:58:15 PM (16 years ago)
Author:
ole
Message:

Work on Inflow and Rain forcing terms.
Also looked at a few backwards compatibility issues with a view to
commit new limiter options

Location:
anuga_core/source/anuga/shallow_water
Files:
2 edited

Legend:

Unmodified
Added
Removed
  • anuga_core/source/anuga/shallow_water/shallow_water_domain.py

    r5290 r5294  
    9696     import Transmissive_boundary
    9797
    98 from anuga.utilities.numerical_tools import gradient, mean
     98from anuga.utilities.numerical_tools import gradient, mean, ensure_numeric
    9999from anuga.config import minimum_storable_height
    100100from anuga.config import minimum_allowed_height, maximum_allowed_speed
     
    106106from anuga.config import use_edge_limiter
    107107from anuga.config import use_centroid_velocities
     108
     109
     110from anuga.utilities.polygon import inside_polygon, polygon_area       
     111
    108112
    109113
     
    14791483
    14801484
    1481 class Rainfall:
     1485
     1486
     1487class General_forcing:
     1488    """Class General_forcing - general explicit forcing term for update of quantity
     1489   
     1490    This is used by Inflow and Rainfall for instance
     1491   
     1492
     1493    General_forcing(quantity_name, rate, center, radius, polygon)
     1494
     1495    domain:     ANUGA computational domain
     1496    quantity_name: Name of quantity to update. It must be a known conserved quantity.
     1497    rate [?/s]: Total rate of change over the specified area. 
     1498                This parameter can be either a constant or a
     1499                function of time. Positive values indicate increases,
     1500                negative values indicate decreases.
     1501                Rate can be None at initialisation but must be specified
     1502                before forcting term is applied (i.e. simulation has started).
     1503
     1504    center [m]: Coordinates at center of flow point
     1505    radius [m]: Size of circular area
     1506    polygon:    Arbitrary polygon.
     1507
     1508
     1509    Either center, radius or polygon can be specified but not both.
     1510    If neither are specified the entire domain gets updated.
     1511   
     1512    See Inflow or Rainfall for examples of use
     1513    """
     1514
     1515
     1516    # FIXME (AnyOne) : Add various methods to allow spatial variations
     1517
     1518    def __init__(self,
     1519                 domain,
     1520                 quantity_name,
     1521                 rate=None,
     1522                 center=None, radius=None,
     1523                 polygon=None,
     1524                 verbose=False):
     1525                     
     1526
     1527        from math import pi
     1528
     1529        self.domain = domain
     1530        self.quantity_name = quantity_name
     1531        self.rate = rate
     1532        self.center = ensure_numeric(center)
     1533        self.radius = radius
     1534        self.polygon = polygon       
     1535        self.verbose = verbose
     1536
     1537        # Update area if applicable
     1538        self.area = None       
     1539        if center is not None and radius is not None:
     1540            assert len(center) == 2
     1541            msg = 'Polygon cannot be specified when center and radius are'
     1542            assert polygon is None, msg
     1543
     1544            self.area = radius**2*pi
     1545       
     1546        if polygon is not None:
     1547            self.area = polygon_area(self.polygon)
     1548
     1549
     1550        # Pointer to update vector
     1551        self.update = domain.quantities[self.quantity_name].explicit_update           
     1552
     1553        # Determine indices in flow area
     1554        N = len(domain)   
     1555        points = domain.get_centroid_coordinates(absolute=True)
     1556
     1557        self.indices = None
     1558        if self.center is not None and self.radius is not None:
     1559            # Inlet is circular
     1560           
     1561            self.indices = []
     1562            for k in range(N):
     1563                x, y = points[k,:] # Centroid
     1564                if ((x-self.center[0])**2+(y-self.center[1])**2) < self.radius**2:
     1565                    self.indices.append(k)
     1566                   
     1567        if self.polygon is not None:                   
     1568            # Inlet is polygon
     1569            self.indices = inside_polygon(points, self.polygon)
     1570           
     1571
     1572           
     1573
     1574
     1575    def __call__(self, domain):
     1576        """Apply inflow function at time specified in domain and update stage
     1577        """
     1578
     1579        # Call virtual method allowing local modifications
     1580        rate = self.update_rate(domain.get_time())
     1581        if rate is None:
     1582            msg = 'Attribute rate must be specified in General_forcing'
     1583            msg += ' or its descendants before attempting to call it'
     1584            raise Exception, msg
     1585       
     1586
     1587        # Now rate is a number
     1588        if self.verbose is True:
     1589            print 'Rate of %s at time = %.2f = %f' %(self.quantity_name,
     1590                                                     domain.get_time(),
     1591                                                     rate)
     1592
     1593
     1594        if self.indices is None:
     1595            self.update[:] += rate
     1596        else:
     1597            # Brute force assignment of restricted rate
     1598            for k in self.indices:
     1599                self.update[k] += rate
     1600
     1601
     1602    def update_rate(self, t):
     1603        """Virtual method allowing local modifications by writing an
     1604        overriding version in descendant
     1605       
     1606        """
     1607        if callable(self.rate):
     1608            rate = self.rate(t)
     1609        else:
     1610            rate = self.rate
     1611
     1612        return rate
     1613
     1614
     1615    def get_quantity_values(self):
     1616        """Return values for specified quantity restricted to opening
     1617        """
     1618        return self.domain.quantities[self.quantity_name].get_values(indices=self.indices)
     1619   
     1620
     1621    def set_quantity_values(self, val):
     1622        """Set values for specified quantity restricted to opening
     1623        """
     1624        self.domain.quantities[self.quantity_name].set_values(val, indices=self.indices)   
     1625
     1626
     1627
     1628class Rainfall(General_forcing):
    14821629    """Class Rainfall - general 'rain over entire domain' forcing term.
    14831630   
     
    14901637   
    14911638    Rainfall(rain)
    1492        
     1639
     1640    domain   
    14931641    rain [mm/s]:  Total rain rate over the specified domain. 
    14941642                  NOTE: Raingauge Data needs to reflect the time step.
     
    15041652                  The specified flow will be divided by the area of
    15051653                  the inflow region and then applied to update the
    1506                   quantity in question.
     1654                  stage quantity.
    15071655
    15081656    polygon: Specifies a polygon to restrict the rainfall.
     
    15241672    """
    15251673
    1526     # FIXME (OLE): Add a polygon as an alternative.
    1527     # FIXME (AnyOne) : Add various methods to allow spatial variations
    1528     # FIXME (OLE): Generalise to all quantities
    1529 
    15301674   
    15311675    def __init__(self,
    1532                  rain=0.0,
    1533                  quantity_name='stage',
    1534                  polygon=None):
    1535 
    1536         self.rain = rain
    1537         self.quantity_name = quantity_name
    1538         self.polygon = polygon
    1539    
    1540     def __call__(self, domain):
    1541        
    1542         # FIXME(Ole): Move this to top of file eventually
    1543         from anuga.utilities.polygon import inside_polygon       
    1544 
    1545         # Update rainfall
    1546         if callable(self.rain):
    1547             rain = self.rain(domain.get_time())
    1548         else:
    1549             rain = self.rain
    1550 
    1551         # Now rain is a number
    1552         # Converting mm/s to m/s to apply in ANUGA
    1553         # 1mm of rainfall is equivalent to 1 litre /m2
    1554         # Flow is expressed as m3/s converted to a stage height in (m)
    1555        
    1556         # Note 1m3 = 1x10^9mm3 (mls)
    1557         # or is that m3 to Litres ??? Check this how is it applied !!!
    1558         rain_fall = rain/1000
    1559 
    1560         # Find indices for coordinates inside polygon if specified
    1561         indices = None
    1562         if self.polygon is not None:
    1563             points = domain.get_centroid_coordinates(absolute=True)
    1564             indices = inside_polygon(points, self.polygon)
    1565            
    1566 
    1567         # Assign rainfall value to explicit update vector to be used as
    1568         # a forcing term
    1569         quantity = domain.quantities[self.quantity_name].explicit_update
    1570         if indices is None:
    1571             quantity[:] += rain_fall
     1676                 domain,
     1677                 rate=0.0,
     1678                 center=None, radius=None,
     1679                 polygon=None,
     1680                 verbose=False):
     1681
     1682        # Converting mm/s to m/s to apply in ANUGA)
     1683        if callable(rate):
     1684            rain = lambda t: rate(t)/1000.0
    15721685        else:
    1573             # Brute force assignment of restricted rainfall
    1574             for i in indices:
    1575                 quantity[i] += rain_fall
    1576 
    1577        
    1578 
    1579 
    1580 
    1581 
    1582 class Inflow:
     1686            rain = rate/1000.0           
     1687           
     1688        General_forcing.__init__(self,
     1689                                 domain,
     1690                                 'stage',
     1691                                 rate=rain,
     1692                                 center=center, radius=radius,
     1693                                 polygon=polygon,
     1694                                 verbose=verbose)
     1695
     1696       
     1697
     1698
     1699
     1700
     1701class Inflow(General_forcing):
    15831702    """Class Inflow - general 'rain and drain' forcing term.
    15841703   
    15851704    Useful for implementing flows in and out of the domain.
    15861705   
    1587     Inflow(center, radius, flow)
    1588    
    1589     center [m]: Coordinates at center of flow point
    1590     radius [m]: Size of circular area
     1706    Inflow(flow, center, radius, polygon)
     1707
     1708    domain
    15911709    flow [m^3/s]: Total flow rate over the specified area. 
    15921710                  This parameter can be either a constant or a
     
    15951713                  The specified flow will be divided by the area of
    15961714                  the inflow region and then applied to update the
    1597                   quantity in question.
     1715                  quantity in question.     
     1716    center [m]: Coordinates at center of flow point
     1717    radius [m]: Size of circular area
     1718    polygon:    Arbitrary polygon.
     1719
     1720    Either center, radius or polygon must be specified
    15981721   
    15991722    Examples
     
    16121735    Inflow((0.5, 0.5), 0.03, lambda t: min(0.01*t, 0.0142))
    16131736
     1737
    16141738    #--------------------------------------------------------------------------
    16151739    # Setup specialised forcing terms
     
    16251749    """
    16261750
    1627     # FIXME (OLE): Add a polygon as an alternative.
    1628     # FIXME (OLE): Generalise to all quantities
    16291751
    16301752    def __init__(self,
     1753                 domain,
     1754                 rate=0.0,
    16311755                 center=None, radius=None,
    1632                  flow=0.0,
    1633                  quantity_name='stage',
    1634                  verbose=False):
    1635 
    1636         from math import pi
    1637 
    1638        
    1639                  
    1640         if center is not None and radius is not None:
    1641             assert len(center) == 2
     1756                 polygon=None,
     1757                 verbose=False):                 
     1758
     1759
     1760        #msg = 'Class Inflow must have either center & radius or a polygon specified.'
     1761        #assert center is not None and radius is not None or\
     1762        #       polygon is not None, msg
     1763
     1764
     1765        # Create object first to make area is available
     1766        General_forcing.__init__(self,
     1767                                 domain,
     1768                                 'stage',
     1769                                 rate=rate,
     1770                                 center=center, radius=radius,
     1771                                 polygon=polygon,
     1772                                 verbose=verbose)
     1773
     1774    def update_rate(self, t):
     1775        """Virtual method allowing local modifications by writing an
     1776        overriding version in descendant
     1777
     1778        This one converts m^3/s to m/s which can be added directly to 'stage' in ANUGA
     1779        """
     1780
     1781       
     1782       
     1783        if callable(self.rate):
     1784            _rate = self.rate(t)/self.area
    16421785        else:
    1643             msg = 'Both center and radius must be specified'
    1644             raise Exception, msg
    1645    
    1646         self.center = center
    1647         self.radius = radius
    1648         self.area = radius**2*pi
    1649         self.flow = flow
    1650         self.quantity_name = quantity_name
    1651         self.verbose = verbose
    1652    
    1653     def __call__(self, domain):
    1654 
    1655         # Determine indices in flow area
    1656         if not hasattr(self, 'indices'):
    1657             center = self.center
    1658             radius = self.radius
    1659            
    1660             N = len(domain)   
    1661             self.indices = []
    1662             coordinates = domain.get_centroid_coordinates()     
    1663             for k in range(N):
    1664                 x, y = coordinates[k,:] # Centroid
    1665                 if ((x-center[0])**2+(y-center[1])**2) < radius**2:
    1666                     self.indices.append(k)   
    1667 
    1668         # Update inflow
    1669         if callable(self.flow):
    1670             flow = self.flow(domain.get_time())
    1671         else:
    1672             flow = self.flow
    1673 
    1674         # Now flow is a number
    1675         if self.verbose is True:
    1676             print 'Inflow at time = %.2f = %f' %(domain.get_time(),
    1677                                                  flow)
    1678        
    1679         quantity = domain.quantities[self.quantity_name].explicit_update
    1680         for k in self.indices:
    1681             quantity[k] += flow/self.area                       
     1786            _rate = self.rate/self.area
     1787
     1788        return _rate
     1789
     1790
    16821791
    16831792
  • anuga_core/source/anuga/shallow_water/test_shallow_water_domain.py

    r5290 r5294  
    21942194        # Setup only one forcing term, constant rainfall
    21952195        domain.forcing_terms = []
    2196         domain.forcing_terms.append( Rainfall(rain=2.0) )
     2196        domain.forcing_terms.append( Rainfall(domain, rate=2.0) )
    21972197
    21982198        domain.compute_forcing_terms()
     
    22312231        # Setup only one forcing term, constant rainfall restricted to a polygon enclosing triangle #1 (bce)
    22322232        domain.forcing_terms = []
    2233         domain.forcing_terms.append( Rainfall(rain=2.0, polygon = [[1,1], [2,1], [2,2], [1,2]]))
     2233        domain.forcing_terms.append( Rainfall(domain, rate=2.0, polygon = [[1,1], [2,1], [2,2], [1,2]]))
    22342234
    22352235        domain.compute_forcing_terms()
     
    22422242        # FIXME: Do Time dependent rainfall with poly
    22432243
     2244
     2245
     2246
     2247    def test_inflow_using_circle(self):
     2248        from math import pi, cos, sin
     2249
     2250        a = [0.0, 0.0]
     2251        b = [0.0, 2.0]
     2252        c = [2.0, 0.0]
     2253        d = [0.0, 4.0]
     2254        e = [2.0, 2.0]
     2255        f = [4.0, 0.0]
     2256
     2257        points = [a, b, c, d, e, f]
     2258        #bac, bce, ecf, dbe
     2259        vertices = [ [1,0,2], [1,2,4], [4,2,5], [3,1,4]]
     2260
     2261
     2262        domain = Domain(points, vertices)
     2263
     2264        # Flat surface with 1m of water
     2265        domain.set_quantity('elevation', 0)
     2266        domain.set_quantity('stage', 1.0)
     2267        domain.set_quantity('friction', 0)
     2268
     2269        Br = Reflective_boundary(domain)
     2270        domain.set_boundary({'exterior': Br})
     2271
     2272        # Setup only one forcing term, constant inflow of 2 m^3/s on a circle affecting triangles #0 and #1 (bac and bce)
     2273        domain.forcing_terms = []
     2274        domain.forcing_terms.append( Inflow(domain, rate=2.0, center=(1,1), radius=1) )
     2275
     2276        domain.compute_forcing_terms()
     2277        #print domain.quantities['stage'].explicit_update
     2278       
     2279        assert allclose(domain.quantities['stage'].explicit_update[1], 2.0/pi)
     2280        assert allclose(domain.quantities['stage'].explicit_update[0], 2.0/pi)
     2281        assert allclose(domain.quantities['stage'].explicit_update[2:], 0)       
     2282
     2283
     2284    def test_inflow_using_circle_function(self):
     2285        from math import pi, cos, sin
     2286
     2287        a = [0.0, 0.0]
     2288        b = [0.0, 2.0]
     2289        c = [2.0, 0.0]
     2290        d = [0.0, 4.0]
     2291        e = [2.0, 2.0]
     2292        f = [4.0, 0.0]
     2293
     2294        points = [a, b, c, d, e, f]
     2295        #bac, bce, ecf, dbe
     2296        vertices = [ [1,0,2], [1,2,4], [4,2,5], [3,1,4]]
     2297
     2298
     2299        domain = Domain(points, vertices)
     2300
     2301        # Flat surface with 1m of water
     2302        domain.set_quantity('elevation', 0)
     2303        domain.set_quantity('stage', 1.0)
     2304        domain.set_quantity('friction', 0)
     2305
     2306        Br = Reflective_boundary(domain)
     2307        domain.set_boundary({'exterior': Br})
     2308
     2309        # Setup only one forcing term, time dependent inflow of 2 m^3/s on a circle affecting triangles #0 and #1 (bac and bce)
     2310        domain.forcing_terms = []
     2311        domain.forcing_terms.append( Inflow(domain, rate=lambda t: 2., center=(1,1), radius=1) )
     2312
     2313        domain.compute_forcing_terms()
     2314       
     2315        assert allclose(domain.quantities['stage'].explicit_update[1], 2.0/pi)
     2316        assert allclose(domain.quantities['stage'].explicit_update[0], 2.0/pi)
     2317        assert allclose(domain.quantities['stage'].explicit_update[2:], 0)       
     2318       
    22442319
    22452320
     
    32793354        from Numeric import array
    32803355
    3281         #Create basic mesh
     3356        # Create basic mesh
    32823357        points, vertices, boundary = rectangular(6, 6)
    32833358
    3284         #Create shallow water domain
     3359        # Create shallow water domain
    32853360        domain = Domain(points, vertices, boundary)
    32863361        domain.smooth = False
    3287         domain.default_order=2
    3288 
    3289         #IC
     3362        domain.default_order = 2
     3363
     3364        # IC
    32903365        def x_slope(x, y):
    32913366            return x/3
     
    32933368        domain.set_quantity('elevation', x_slope)
    32943369        domain.set_quantity('friction', 0)
    3295         domain.set_quantity('stage', 0.4) #Steady
     3370        domain.set_quantity('stage', 0.4) # Steady
    32963371
    32973372        # Boundary conditions (reflective everywhere)
     
    33083383
    33093384
    3310         #Evolution
     3385        # Evolution
    33113386        for t in domain.evolve(yieldstep = 0.05, finaltime = 15.0):
    33123387            stage =  domain.quantities['stage'].get_integral()
     
    33143389            ymom = domain.quantities['ymomentum'].get_integral()
    33153390
    3316             if allclose(t, 6):  #Steady state reached
     3391            if allclose(t, 6):  # Steady state reached
    33173392                steady_xmom = domain.quantities['xmomentum'].get_integral()
    33183393                steady_ymom = domain.quantities['ymomentum'].get_integral()
     
    33213396            if t > 6:
    33223397                #print '%.2f %14.8f %14.8f' %(t, ymom, steady_ymom)
    3323                 assert allclose(xmom, steady_xmom)
     3398                msg = 'xmom=%.2f, steady_xmom=%.2f' %(xmom, steady_xmom)
     3399                assert allclose(xmom, steady_xmom), msg
    33243400                assert allclose(ymom, steady_ymom)
    33253401                assert allclose(stage, steady_stage)
     
    34053481        domain = Domain(points, vertices, boundary)
    34063482        domain.smooth = False
    3407         domain.default_order=2
     3483        domain.default_order = 2
    34083484        domain.beta_w      = 0.9
    34093485        domain.beta_w_dry  = 0.9
     
    34213497        domain.check_integrity()
    34223498
    3423         #Evolution
     3499        # Evolution
    34243500        for t in domain.evolve(yieldstep = 0.05, finaltime = 0.05):
    34253501            pass# domain.write_time()
    34263502
    3427         #Data from earlier version of abstract_2d_finite_volumes
     3503        # Data from earlier version of abstract_2d_finite_volumes
    34283504        assert allclose(domain.min_timestep, 0.0396825396825)
    34293505        assert allclose(domain.max_timestep, 0.0396825396825)
     
    35663642        #domain.minimum_allowed_height = 0.0 #Makes it like the 'oldstyle' balance
    35673643        domain.H0 = 0 # Backwards compatibility (6/2/7)
     3644        domain.use_centroid_velocities = False # Backwards compatibility (8/5/8)
    35683645        domain.set_maximum_allowed_speed(1.0)       
    35693646
     
    35753652        domain.check_integrity()
    35763653
    3577         #Evolution
     3654        # Evolution
    35783655        for t in domain.evolve(yieldstep = 0.01, finaltime = 0.03):
    35793656            pass
    35803657
    3581 
    3582         assert allclose(domain.min_timestep, 0.0210448446782)
     3658        msg = 'min step was %f instead of %f' %(domain.min_timestep,
     3659                                                0.0210448446782)
     3660
     3661        assert allclose(domain.min_timestep, 0.0210448446782), msg
    35833662        assert allclose(domain.max_timestep, 0.0210448446782)
    35843663
     
    36303709        domain.beta_vh_dry = 0.9       
    36313710        domain.maximum_allowed_speed = 0.0 #Makes it like the 'oldstyle'
    3632         domain.H0 = 0 # Backwards compatibility (6/2/7)       
     3711        domain.H0 = 0 # Backwards compatibility (6/2/7)
     3712        domain.use_centroid_velocities = False # Backwards compatibility (8/5/8)       
    36333713
    36343714        # Boundary conditions
     
    36823762        domain.beta_vh_dry = 0.9
    36833763        domain.H0 = 0 # Backwards compatibility (6/2/7)
     3764        domain.use_centroid_velocities = False # Backwards compatibility (8/5/8)       
    36843765        domain.set_maximum_allowed_speed(1.0)       
    36853766
Note: See TracChangeset for help on using the changeset viewer.