Changeset 1668


Ignore:
Timestamp:
Aug 1, 2005, 5:30:53 PM (20 years ago)
Author:
ole
Message:

Separated Interpolation_function out from File_function and refactored the latter in
terms of the former.
Started unifying the general version with temporal interpolation only.

Location:
inundation/ga/storm_surge/pyvolution
Files:
2 edited

Legend:

Unmodified
Added
Removed
  • inundation/ga/storm_surge/pyvolution/test_util.py

    r1664 r1668  
    559559                          interpolation_points = interpolation_points)
    560560        assert allclose(domain.starttime, start+1)
    561 
    562561        domain.starttime = start
    563562
    564563
    565 
    566564        #Check linear interpolation in time
     565        F = file_function(filename + '.sww', domain,
     566                          quantities = domain.conserved_quantities,
     567                          interpolation_points = interpolation_points)               
    567568        #for id in range(len(interpolation_points)):
    568569        for id in [1]:
     
    580581
    581582                q = F(t, point_id=id)
     583                #print i, k, t, q
     584                #print ' ', q0
     585                #print ' ', q1
     586                #print 's', (k*q1 + (6-k)*q0)/6
     587                #print
    582588                assert allclose(q, (k*q1 + (6-k)*q0)/6)
    583589
     
    589595
    590596            t = 90 #Halfway between 60 and 120
    591             q = F(t,point_id=id)
     597            q = F(t, point_id=id)
    592598            assert allclose( (q120+q60)/2, q )
    593599
     
    10831089#-------------------------------------------------------------
    10841090if __name__ == "__main__":
    1085     #suite = unittest.makeSuite(TestCase,'test_inside_polygon_main')
     1091    #suite = unittest.makeSuite(Test_Util,'test_spatio_temporal_file_function_time')
    10861092    suite = unittest.makeSuite(Test_Util,'test')
    10871093    runner = unittest.TextTestRunner()
  • inundation/ga/storm_surge/pyvolution/util.py

    r1664 r1668  
    55"""
    66
     7#FIXME: Move polygon stuff out
     8#FIXME: Finish Interpolation function and get rid of File_function_ASCII
     9
     10
    711def angle(v):
    812    """Compute angle between e1 (the unit vector in the x-direction)
     
    1721    v2 = v[1]/l
    1822
     23
    1924    theta = acos(v1)
    20     try:
    21         theta = acos(v1)
    22     except ValueError, e:
    23         print 'WARNING (util.py): Angle acos(%s) failed: %s' %(str(v1), e)
    24 
    25         #FIXME, hack to avoid acos(1.0) Value error
    26         # why is it happening?
    27         # is it catching something we should avoid?
    28         #FIXME (Ole): When did this happen? We need a unit test to
    29         #reveal this condition or otherwise remove the hack
    30        
    31         s = 1e-6
    32         if (v1+s >  1.0) and (v1-s < 1.0) :
    33             theta = 0.0
    34         elif (v1+s >  -1.0) and (v1-s < -1.0):
    35             theta = 3.1415926535897931
    36         print 'WARNING (util.py): angle v1 is %f, setting acos to %f '\
    37               %(v1, theta)
     25   
     26    #try:
     27    #   theta = acos(v1)
     28    #except ValueError, e:
     29    #    print 'WARNING (util.py): Angle acos(%s) failed: %s' %(str(v1), e)
     30    #
     31    #    #FIXME, hack to avoid acos(1.0) Value error
     32    #    # why is it happening?
     33    #    # is it catching something we should avoid?
     34    #    #FIXME (Ole): When did this happen? We need a unit test to
     35    #    #reveal this condition or otherwise remove the hack
     36    #   
     37    #    s = 1e-6
     38    #    if (v1+s >  1.0) and (v1-s < 1.0) :
     39    #        theta = 0.0
     40    #    elif (v1+s >  -1.0) and (v1-s < -1.0):
     41    #        theta = 3.1415926535897931
     42    #    print 'WARNING (util.py): angle v1 is %f, setting acos to %f '\
     43    #          %(v1, theta)
    3844
    3945    if v2 < 0:
     
    156162        quantities = None
    157163
    158     #Choose format
    159     #FIXME: Maybe these can be merged later on
    160     #FIXME: We shoulp probably not waste energy on ASCII files -
    161     #the different formats are a pain. E.g. the time field.
    162     #
     164
    163165    if line[:3] == 'CDF':
    164         return File_function_NetCDF(filename, domain, quantities,
    165                                     interpolation_points, verbose = verbose)
     166        return get_netcdf_file_function(filename, domain, quantities,
     167                                        interpolation_points, verbose = verbose)
    166168    else:
     169        #FIXME Should be phased out
    167170        return File_function_ASCII(filename, domain, quantities,
    168171                                   interpolation_points)
    169 
    170 
     172   
     173   
     174
     175
     176
     177
     178
     179
     180
     181
     182def get_netcdf_file_function(filename, domain=None, quantity_names=None,
     183                             interpolation_points=None, verbose = False):
     184    """Read time history of spatial data from NetCDF sww file and
     185    return a callable object f(t,x,y)
     186    which will return interpolated values based on the input file.
     187
     188    If domain is specified, model time (domain.starttime)
     189    will be checked and possibly modified
     190   
     191    All times are assumed to be in UTC
     192
     193    See Interpolation function for further documetation
     194   
     195
     196    """
     197   
     198    #verbose = True
     199   
     200    #FIXME: Check that model origin is the same as file's origin
     201    #(both in UTM coordinates)
     202    #If not - modify those from file to match domain
     203    #Take this code from e.g. dem2pts in data_manager.py
     204    #FIXME: Use geo_reference to read and write xllcorner...
     205       
     206
     207    import time, calendar
     208    from config import time_format
     209    from Scientific.IO.NetCDF import NetCDFFile
     210    from Numeric import array, zeros, Float, alltrue, concatenate, reshape       
     211
     212    #Open NetCDF file
     213    if verbose: print 'Reading', filename
     214    fid = NetCDFFile(filename, 'r')
     215
     216    if quantity_names is None or len(quantity_names) < 1:
     217        msg = 'ERROR: At least one independent value must be specified'
     218        raise msg
     219
     220    missing = []
     221    for quantity in ['x', 'y', 'time'] + quantity_names:
     222        if not fid.variables.has_key(quantity):
     223            missing.append(quantity)
     224
     225    if len(missing) > 0:
     226        msg = 'Quantities %s could not be found in file %s'\
     227              %(str(missing), filename)
     228        raise msg
     229
     230
     231    #Get first timestep
     232    try:
     233        starttime = fid.starttime[0]
     234    except ValueError:
     235        msg = 'Could not read starttime from file %s' %filename
     236        raise msg
     237
     238    #Get origin
     239    xllcorner = fid.xllcorner[0]
     240    yllcorner = fid.yllcorner[0]
     241
     242
     243    #Get variables
     244    if verbose: print 'Get variables'
     245    x = fid.variables['x'][:]
     246    y = fid.variables['y'][:]
     247    triangles = fid.variables['volumes'][:]
     248    time = fid.variables['time'][:]
     249
     250    x = reshape(x, (len(x),1))
     251    y = reshape(y, (len(y),1))
     252    vertex_coordinates = concatenate((x,y), axis=1) #m x 2 array
     253
     254    if domain is not None:
     255        #Update domain.startime if it is *earlier* than starttime
     256        if starttime > domain.starttime:
     257            msg = 'WARNING: Start time as specified in domain (%f)'\
     258                  %domain.starttime
     259            msg += ' is earlier than the starttime of file %s (%f).'\
     260                     %(filename, starttime)
     261            msg += ' Modifying domain starttime accordingly.'
     262           
     263            if verbose: print msg
     264            domain.starttime = starttime #Modifying model time
     265            if verbose: print 'Domain starttime is now set to %f'\
     266               %domain.starttime
     267
     268
     269        #If domain.startime is *later* than starttime,
     270        #move time back - relative to domain's time
     271        if domain.starttime > starttime:
     272            time = time - domain.starttime + starttime
     273
     274
     275    if verbose:
     276        print 'File_function data obtained from: %s' %filename
     277        print '  References:'
     278        #print '    Datum ....' #FIXME
     279        print '    Lower left corner: [%f, %f]'\
     280              %(xllcorner, yllcorner)
     281        print '    Start time:   %f' %starttime               
     282       
     283   
     284    #Produce values for desired data points at
     285    #each timestep
     286
     287    quantities = {}
     288    for i, name in enumerate(quantity_names):
     289        quantities[name] = fid.variables[name][:]
     290    fid.close()
     291   
     292
     293    return Interpolation_function(vertex_coordinates,
     294                                  triangles,
     295                                  time,
     296                                  quantities,
     297                                  quantity_names, 
     298                                  interpolation_points,
     299                                  alpha = 0,
     300                                  verbose = verbose)
     301
     302
     303
     304
     305
     306class Interpolation_function:
     307    """Interpolation_function - creates callable object f(t, id) or f(t,x,y)
     308    which is interpolated from time series defined at vertices of
     309    triangular mesh
     310
     311    Input
     312        vertex_coordinates: mx2 array of coordinates (Float)
     313        triangles:          nx3 array of indices into vertex_coordinates (Int)
     314        time:               px1 array of times (Float)               
     315        quantities:         Dictionary of pxm values
     316        quantity_names:
     317        interpolation_points:
     318        alpha:
     319        verbose:
     320   
     321
     322   
     323    The quantities returned by the callable object are specified by
     324    the list quantities which must contain the names of the
     325    quantities to be returned and also reflect the order, e.g. for
     326    the shallow water wave equation, on would have
     327    quantities = ['stage', 'xmomentum', 'ymomentum']
     328
     329    The parameter interpolation_points decides at which points interpolated
     330    quantities are to be computed whenever object is called.
     331    If None, return average value
     332    """
     333
     334   
     335
     336   
     337    def __init__(self,
     338                 vertex_coordinates,
     339                 triangles,
     340                 time,
     341                 quantities,
     342                 quantity_names = None, 
     343                 interpolation_points = None,
     344                 alpha = 0,
     345                 verbose = False):
     346        """
     347        """
     348
     349        from Numeric import array, zeros, Float, alltrue, concatenate,\
     350             reshape, ArrayType
     351
     352        from config import time_format
     353        from least_squares import Interpolation
     354
     355        #Check
     356        msg = 'Time must be a monotonuosly '
     357        msg += 'increasing sequence %s' %time
     358        assert alltrue(time[1:] - time[:-1] > 0 ), msg
     359
     360
     361        #Check if quantities is a single array only
     362        if type(quantities) == ArrayType:
     363            quantity_names = 'Attribute'
     364            quantities = {quantity_names: quantities}           
     365
     366        #Use keys if no names specified
     367        if quantity_names is not None:
     368            self.quantity_names = quantity_names
     369        else:
     370            self.quantity_names = quantities.keys()
     371
     372
     373        self.interpolation_points = interpolation_points
     374        self.T = time[:]  #Time assumed to be relative to starttime
     375        self.index = 0    #Initial time index
     376        self.values = {}
     377           
     378
     379        if interpolation_points is not None:
     380            #Spatial interpolation
     381
     382            try:
     383                interpolation_points = ensure_numeric(interpolation_points)
     384            except:
     385                msg = 'Interpolation points must be an N x 2 Numeric array or a list of points\n'
     386                msg += 'I got: %s.' %( str(interpolation_points)[:60] + '...')
     387                raise msg
     388
     389
     390            for name in quantity_names:
     391                self.values[name] = zeros( (len(self.T),
     392                                            len(interpolation_points)),
     393                                            Float)
     394
     395            #Build interpolator
     396            interpol = Interpolation(vertex_coordinates,
     397                                     triangles,
     398                                     point_coordinates = interpolation_points,
     399                                     alpha = 0,
     400                                     verbose = verbose)
     401
     402
     403            if verbose: print 'Interpolate'
     404            for i, t in enumerate(self.T):
     405                #Interpolate quantities at this timestep
     406                if verbose: print ' time step %d of %d' %(i, len(self.T))
     407                for name in quantity_names:
     408                    self.values[name][i, :] =\
     409                    interpol.interpolate(quantities[name][i,:])
     410
     411            #Report
     412            if verbose:
     413                x = vertex_coordinates[:,0]
     414                y = vertex_coordinates[:,1]               
     415               
     416                print '------------------------------------------------'
     417                print 'Interpolation_function statistics:'
     418                print '  Extent:'
     419                print '    x in [%f, %f], len(x) == %d'\
     420                      %(min(x.flat), max(x.flat), len(x.flat))
     421                print '    y in [%f, %f], len(y) == %d'\
     422                      %(min(y.flat), max(y.flat), len(y.flat))
     423                print '    t in [%f, %f], len(t) == %d'\
     424                      %(min(self.T), max(self.T), len(self.T))
     425                print '  Quantities:'
     426                for name in quantity_names:
     427                    q = quantities[name][:].flat
     428                    print '    %s in [%f, %f]' %(name, min(q), max(q))
     429
     430
     431                print '  Interpolation points (xi, eta):'\
     432                      'number of points == %d ' %interpolation_points.shape[0]
     433                print '    xi in [%f, %f]' %(min(interpolation_points[:,0]),
     434                                             max(interpolation_points[:,0]))
     435                print '    eta in [%f, %f]' %(min(interpolation_points[:,1]),
     436                                              max(interpolation_points[:,1]))
     437                print '  Interpolated quantities (over all timesteps):'
     438                for name in quantity_names:
     439                    q = self.values[name][:].flat
     440                    print '    %s at interpolation points in [%f, %f]'\
     441                          %(name, min(q), max(q))
     442                print '------------------------------------------------'
     443        else:
     444            #Return an average, making this a time series
     445            #FIXME: Needs a test
     446            for name in quantity_names:
     447                self.values[name] = avg(quantities[name][i,:])
     448
     449
     450    def __repr__(self):
     451        return 'Interpolation function'
     452
     453    def __call__(self, t, point_id = None, x = None, y = None):
     454        """Evaluate f(t), f(t, point_id) or f(t, x, y)
     455
     456        Inputs:
     457          t: time - Model time. Must lie within existing timesteps
     458          point_id: index of one of the preprocessed points.
     459                    If point_id is None all preprocessed points are computed
     460                   
     461          If no x,y info is present, point_id and x,y arguments are ignored
     462          making f a function of time only.
     463         
     464          FIXME: point_id could also be a slice
     465          FIXME: One could allow arbitrary x, y coordinates
     466          (requires computation of a new interpolator)
     467          FIXME: What if x and y are vectors?
     468          FIXME: Does point_id or x,y take precedence?
     469          FIXME: What about f(x,y) without t
     470         
     471        """
     472
     473        from math import pi, cos, sin, sqrt
     474        from Numeric import zeros, Float
     475
     476
     477        msg = 'Time interval [%s:%s]' %(self.T[0], self.T[1])
     478        msg += ' does not match model time: %s\n' %t
     479        if t < self.T[0]: raise msg
     480        if t > self.T[-1]: raise msg
     481
     482        oldindex = self.index #Time index
     483
     484        #Find current time slot
     485        while t > self.T[self.index]: self.index += 1
     486        while t < self.T[self.index]: self.index -= 1
     487
     488        if t == self.T[self.index]:
     489            #Protect against case where t == T[-1] (last time)
     490            # - also works in general when t == T[i]
     491            ratio = 0
     492        else:
     493            #t is now between index and index+1
     494            ratio = (t - self.T[self.index])/\
     495                    (self.T[self.index+1] - self.T[self.index])
     496
     497        #Compute interpolated values
     498        q = zeros(len(self.quantity_names), Float)
     499
     500        for i, name in enumerate(self.quantity_names):
     501            Q = self.values[name]
     502
     503            if point_id is None:
     504                Q0 = Q[self.index]
     505
     506                try:
     507                    Q1 = Q[self.index+1]
     508                except:
     509                    pass               
     510            else:   
     511                Q0 = Q[self.index, point_id]
     512
     513                try:
     514                    Q1 = Q[self.index+1, point_id]
     515                except:
     516                    pass
     517               
     518
     519            #Linear temporal interpolation   
     520            if ratio > 0:
     521                q[i] = Q0 + ratio*(Q1 - Q0)
     522            else:
     523                q[i] = Q0
     524           
     525
     526            #if ratio > 0:
     527            #    q[i] = Q[self.index, point_id] +\
     528            #           ratio*(Q[self.index+1, point_id] -\
     529            #                  Q[self.index, point_id])
     530            #else:
     531            #    q[i] = Q[self.index, point_id]
     532
     533        #Return vector of interpolated values
     534        return q
     535
     536
     537
     538
     539
     540
     541
     542
     543
     544
     545#FIXME Obsolete
    171546class File_function_NetCDF:
    172547    """Read time history of spatial data from NetCDF sww file and
     
    203578        #If not - modify those from file to match domain
    204579        #Take this code from e.g. dem2pts in data_manager.py
     580        #FIXME: Use geo_reference to read and write xllcorner...
    205581       
    206582
     
    281657        x = fid.variables['x'][:]
    282658        y = fid.variables['y'][:]
    283         z = fid.variables['elevation'][:]
     659        #z = fid.variables['elevation'][:]
    284660        triangles = fid.variables['volumes'][:]
    285661        time = fid.variables['time'][:]
     
    410786            tau = self.domain.starttime-self.starttime+t
    411787        else:
    412             #print 'DOMAIN IS NONE!!!!!!!!!'
    413788            tau = t
    414789
    415         #print 'D start', self.domain.starttime
    416         #print 'S start', self.starttime
    417         #print 't', t
    418         #print 'tau', tau
    419790
    420791        msg = 'Time interval derived from file %s [%s:%s]'\
     
    464835        return q
    465836
    466 
     837#FIXME Obsolete
    467838class File_function_ASCII:
    468839    """Read time series from file and return a callable object:
Note: See TracChangeset for help on using the changeset viewer.