Changeset 623


Ignore:
Timestamp:
Nov 24, 2004, 4:43:40 PM (20 years ago)
Author:
ole
Message:

Created and tested general File_function (reading and interpolating time series from file) and used it to refactor and simplify File_boundary

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

Legend:

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

    r566 r623  
    115115        return self.f(self.domain.time)
    116116
     117
    117118class File_boundary(Boundary):
    118119    """Boundary values obtained from file and interpolated.
     
    125126
    126127
    127     def __init__(self, domain = None, filename = None):
     128    def __init__(self, filename, domain):
    128129        import time
    129130        from Numeric import array
    130131        from config import time_format
    131        
    132         Boundary.__init__(self)
    133 
    134 
    135         try:
    136             fid = open(filename)
    137         except Exception, e:
    138             msg = 'Boundary file %s could not be opened: %s\n' %(filename, e)
    139             raise msg
    140 
    141 
    142         line = fid.readline()
    143         fid.close()
    144         fields = line.split(',')
    145         msg = 'File %s must have the format date, values'
    146         assert len(fields) == 2, msg
    147 
    148         try:
    149             starttime = time.mktime(time.strptime(fields[0], time_format))
    150         except ValueError:   
    151             msg = 'First field in file %s must be' %filename
    152             msg += ' date-time with format %s.\n' %time_format
    153             msg += 'I got %s instead.' %fields[0]
    154             raise msg
    155 
    156         #Split values
    157         values = []
    158         for value in fields[1].split():
    159             values.append(float(value))
    160 
    161         q = array(values)
    162        
    163         msg = 'ERROR: File boundary function must return a 1d list or array '
    164         assert len(q.shape) == 1, msg
    165            
     132        from util import File_function
     133       
     134        Boundary.__init__(self)
     135
     136        self.F = File_function(filename, domain)
     137        self.domain = domain
     138
     139        #Test
     140        q = self.F(0)
     141
    166142        d = len(domain.conserved_quantities)
    167143        msg = 'Values specified in file must be a list or an array of length %d' %d
    168144        assert len(q) == d, msg
    169145
    170         self.filename = filename
    171         self.domain = domain
    172         self.starttime = starttime
    173 
    174         if domain.starttime is None:
    175             domain.starttime = starttime
    176         else:
    177             msg = 'Start time as specified in domain (%s) is earlier '
    178             'than the starttime of file %s: %s'\
    179                   %(domain.starttime, self.filename, self.starttime)
    180             if self.starttime > domain.starttime:
    181                 raise msg
    182        
    183         self.read_time_boundary() #Now read all times in.
    184        
    185        
    186     def read_time_boundary(self):
    187         from Numeric import zeros, Float, alltrue
    188         from config import time_format
    189         import time
    190        
    191         fid = open(self.filename)       
    192         lines = fid.readlines()
    193         fid.close()
    194        
    195         N = len(lines)
    196         d = len(self.domain.conserved_quantities)
    197 
    198         T = zeros(N, Float)
    199         Q = zeros((N, d), Float)
    200 
    201        
    202         for i, line in enumerate(lines):
    203             fields = line.split(',')
    204             real_time = time.mktime(time.strptime(fields[0], time_format))
    205 
    206             T[i] = real_time - self.starttime
    207 
    208            
    209             for j, value in enumerate(fields[1].split()):
    210                 Q[i, j] = float(value)
    211 
    212         msg = 'Time boundary must list time as a monotonuosly '
    213         msg += 'increasing sequence'
    214        
    215         assert alltrue( T[1:] - T[:-1] > 0 ), msg
    216 
    217         self.T = T     #Time
    218         self.Q = Q     #Boundary values
    219         self.index = 0 #Initial index
    220 
    221146       
    222147    def __repr__(self):
     
    225150    def evaluate(self, vol_id=None, edge_id=None):
    226151        """Return linearly interpolated values based on domain.time
     152
     153        vol_id and edge_id are ignored
    227154        """
    228155
    229156        t = self.domain.time
    230        
    231         msg = 'Time given in File boundary does not match model time'
    232         if t < self.T[0]: raise msg
    233         if t > self.T[-1]: raise msg       
    234 
    235         oldindex = self.index
    236 
    237         #Find slot
    238         while t > self.T[self.index]: self.index += 1
    239         while t < self.T[self.index]: self.index -= 1
    240 
    241         #if oldindex != self.index:
    242         #    print 'Changing from %d to %d' %(oldindex, self.index)
    243        
    244        
    245         #t is now between index and index+1
    246         ratio = (t - self.T[self.index])/\
    247                 (self.T[self.index+1] - self.T[self.index])
    248 
    249         return self.Q[self.index,:] +\
    250                ratio*(self.Q[self.index+1,:] - self.Q[self.index,:])
     157        return self.F(t)
    251158
    252159
     
    344251                      self.domain.time)
    345252
    346    
    347    
    348        
     253
     254
     255
     256
     257class File_boundary_old(Boundary):
     258    """Boundary values obtained from file and interpolated.
     259    conserved quantities as a function of time.
     260
     261    Assumes that file contains a time series.
     262
     263    No spatial info assumed.
     264    """
     265
     266
     267    def __init__(self, domain = None, filename = None):
     268        import time
     269        from Numeric import array
     270        from config import time_format
     271       
     272        Boundary.__init__(self)
     273
     274
     275        try:
     276            fid = open(filename)
     277        except Exception, e:
     278            msg = 'Boundary file %s could not be opened: %s\n' %(filename, e)
     279            raise msg
     280
     281
     282        line = fid.readline()
     283        fid.close()
     284        fields = line.split(',')
     285        msg = 'File %s must have the format date, values'
     286        assert len(fields) == 2, msg
     287
     288        try:
     289            starttime = time.mktime(time.strptime(fields[0], time_format))
     290        except ValueError:   
     291            msg = 'First field in file %s must be' %filename
     292            msg += ' date-time with format %s.\n' %time_format
     293            msg += 'I got %s instead.' %fields[0]
     294            raise msg
     295
     296        #Split values
     297        values = []
     298        for value in fields[1].split():
     299            values.append(float(value))
     300
     301        q = array(values)
     302       
     303        msg = 'ERROR: File boundary function must return a 1d list or array '
     304        assert len(q.shape) == 1, msg
     305           
     306        d = len(domain.conserved_quantities)
     307        msg = 'Values specified in file must be a list or an array of length %d' %d
     308        assert len(q) == d, msg
     309
     310        self.filename = filename
     311        self.domain = domain
     312        self.starttime = starttime
     313
     314        if domain.starttime is None:
     315            domain.starttime = starttime
     316        else:
     317            msg = 'Start time as specified in domain (%s) is earlier '
     318            'than the starttime of file %s: %s'\
     319                  %(domain.starttime, self.filename, self.starttime)
     320            if self.starttime > domain.starttime:
     321                raise msg
     322       
     323        self.read_time_boundary() #Now read all times in.
     324       
     325       
     326    def read_time_boundary(self):
     327        from Numeric import zeros, Float, alltrue
     328        from config import time_format
     329        import time
     330       
     331        fid = open(self.filename)       
     332        lines = fid.readlines()
     333        fid.close()
     334       
     335        N = len(lines)
     336        d = len(self.domain.conserved_quantities)
     337
     338        T = zeros(N, Float)
     339        Q = zeros((N, d), Float)
     340
     341       
     342        for i, line in enumerate(lines):
     343            fields = line.split(',')
     344            real_time = time.mktime(time.strptime(fields[0], time_format))
     345
     346            T[i] = real_time - self.starttime
     347
     348           
     349            for j, value in enumerate(fields[1].split()):
     350                Q[i, j] = float(value)
     351
     352        msg = 'Time boundary must list time as a monotonuosly '
     353        msg += 'increasing sequence'
     354       
     355        assert alltrue( T[1:] - T[:-1] > 0 ), msg
     356
     357        self.T = T     #Time
     358        self.Q = Q     #Boundary values
     359        self.index = 0 #Initial index
     360
     361       
     362    def __repr__(self):
     363        return 'File boundary'
     364
     365    def evaluate(self, vol_id=None, edge_id=None):
     366        """Return linearly interpolated values based on domain.time
     367        """
     368
     369        t = self.domain.time
     370       
     371        msg = 'Time given in File boundary does not match model time'
     372        if t < self.T[0]: raise msg
     373        if t > self.T[-1]: raise msg       
     374
     375        oldindex = self.index
     376
     377        #Find slot
     378        while t > self.T[self.index]: self.index += 1
     379        while t < self.T[self.index]: self.index -= 1
     380
     381        #if oldindex != self.index:
     382        #    print 'Changing from %d to %d' %(oldindex, self.index)
     383       
     384       
     385        #t is now between index and index+1
     386        ratio = (t - self.T[self.index])/\
     387                (self.T[self.index+1] - self.T[self.index])
     388
     389        return self.Q[self.index,:] +\
     390               ratio*(self.Q[self.index+1,:] - self.Q[self.index,:])
     391
     392
     393   
     394   
     395       
  • inundation/ga/storm_surge/pyvolution/shallow_water.py

    r620 r623  
    518518    """
    519519
    520     #FIXME: Experimental
     520    #FIXME: Experimental (from old version). Not in use at the moment
    521521   
    522522    #Shortcuts
     
    560560            xmomc[k] = ymomc[k] = 0.0
    561561       
     562        #FIXME: Delete   
    562563        #From 'newstyle
    563564        #if hc[k] < domain.minimum_allowed_height:       
     
    901902def check_forcefield(f):
    902903    """Check that f is either
    903     1: a callable object of x,y,t, where x and y are vectors
     904    1: a callable object f(t,x,y), where x and y are vectors
    904905       and that it returns an array or a list of same length
    905906       as x and y
     
    915916        y = ones(3, Float)           
    916917        try:
    917             q = f(x, y, 1.0)
     918            q = f(1.0, x, y)
    918919        except Exception, e:
    919920            msg = 'Function %s could not be executed:\n%s' %(f, e)
     
    943944
    944945class Wind_stress:
    945     """Apply wind stress to water momentum
     946    """Apply wind stress to water momentum in terms of
     947    wind speed [m/s] and wind direction [degrees]   
    946948    """
    947949
     
    959961        map from True north to grid north.
    960962
    961         Arguments can also be Python functions of x,y,t as in
    962 
    963         def speed(x,y,t):
     963        Arguments can also be Python functions of t,x,y as in
     964
     965        def speed(t,x,y):
    964966            ...
    965967            return s
    966968       
    967         def angle(x,y,t):
     969        def angle(t,x,y):
    968970            ...
    969971            return phi
     
    978980        forcing_terms as in
    979981
     982        Alternatively, one vector valued function for (speed, angle)
     983        can be applied, providing both quantities simultaneously.
     984        FIXME: Not yet implemented
     985
    980986        domain.forcing_terms.append(W)
    981        
    982987        """
    983988
     
    10061011        if callable(self.speed):
    10071012            xc = domain.get_centroid_coordinates()           
    1008             s_vec = self.speed(xc[:,0], xc[:,1], t)
     1013            s_vec = self.speed(t, xc[:,0], xc[:,1])
    10091014        else:
    10101015            #Assume s is a scalar
     
    10191024        if callable(self.phi):
    10201025            xc = domain.get_centroid_coordinates()           
    1021             phi_vec = self.phi(xc[:,0], xc[:,1], t)
     1026            phi_vec = self.phi(t, xc[:,0], xc[:,1])
    10221027        else:
    10231028            #Assume phi is a scalar
     
    10571062        ymom_update[k] += S*v       
    10581063           
    1059 
    1060 class Wind_stress_from_file:
    1061     """Apply wind stress read from a file to water momentum
    1062 
    1063     At this stage the wind field is assumed to depend on time only, i.e
    1064     no spatial dependency.
    1065    
    1066     FIXME: This class may be incorporated in the generic Wind_stress class
    1067     Superseded
    1068     """
    1069 
    1070    
    1071     def __init__(self, filename):
    1072         """Initialise windfield from a file with the following format
    1073 
    1074         time [DD/MM/YY hh:mm:ss], speed [m/s] direction [degrees]
    1075        
    1076         e.g.
    1077        
    1078         03/09/04 19:15:00, 9.53 39
    1079        
    1080         domain.forcing_terms.append(W)
    1081        
    1082         """
    1083 
    1084         import time
    1085         from Numeric import array
    1086         from config import time_format
    1087 
    1088 
    1089         from config import rho_a, rho_w, eta_w
    1090         self.const = eta_w*rho_a/rho_w
    1091        
    1092 
    1093         try:
    1094             fid = open(filename)
    1095         except Exception, e:
    1096             msg = 'Wind stress file %s could not be opened: %s\n'\
    1097                   %(filename, e)
    1098             raise msg
    1099 
    1100 
    1101         line = fid.readline()
    1102         fid.close()
    1103         fields = line.split(',')
    1104         msg = 'File %s must have the format date, values'
    1105         assert len(fields) == 2, msg
    1106 
    1107         try:
    1108             starttime = time.mktime(time.strptime(fields[0], time_format))
    1109         except ValueError:   
    1110             msg = 'First field in file %s must be' %filename
    1111             msg += ' date-time with format %s.\n' %time_format
    1112             msg += 'I got %s instead.' %fields[0]
    1113             raise msg
    1114 
    1115         #Split values
    1116         values = []
    1117         for value in fields[1].split():
    1118             values.append(float(value))
    1119 
    1120         q = array(values)
    1121        
    1122         msg = 'ERROR: File function must return a 1d list or array '
    1123         assert len(q.shape) == 1, msg
    1124            
    1125 
    1126         msg = 'Values specified in file must convert to an array of length 2'
    1127         assert len(q) == 2, msg
    1128 
    1129         self.filename = filename
    1130         self.domain = domain
    1131         self.starttime = starttime
    1132 
    1133 
    1134         if domain.starttime is None:
    1135             domain.starttime = starttime
    1136         else:
    1137             msg = 'Start time as specified in domain (%s) is earlier '
    1138             'than the starttime of file %s: %s'\
    1139                   %(domain.starttime, self.filename, self.starttime)
    1140             if self.starttime > domain.starttime:
    1141                 raise msg
    1142 
    1143 
    1144         self.read_times() #Now read all times in.
    1145 
    1146        
    1147     def read_times(self):
    1148         from Numeric import zeros, Float, alltrue
    1149         from config import time_format
    1150         import time
    1151        
    1152         fid = open(self.filename)       
    1153         lines = fid.readlines()
    1154         fid.close()
    1155        
    1156         N = len(lines)
    1157 
    1158         T = zeros(N, Float)       #Time
    1159         Q = zeros((N, 2), Float)  #Wind field (s, phi)
    1160        
    1161         for i, line in enumerate(lines):
    1162             fields = line.split(',')
    1163             real_time = time.mktime(time.strptime(fields[0], time_format))
    1164 
    1165             T[i] = real_time - self.start_time
    1166 
    1167             for j, value in enumerate(fields[1].split()):
    1168                 Q[i, j] = float(value)
    1169 
    1170         msg = 'File %s must list time as a monotonuosly ' %self.filename
    1171         msg += 'increasing sequence'
    1172         assert alltrue( T[1:] - T[:-1] > 0 ), msg
    1173 
    1174         self.T = T     #Time
    1175         self.Q = Q     #Windfied
    1176         self.index = 0 #Initial index
    1177 
    1178        
    1179     def __repr__(self):
    1180         return 'Wind field from file'
    1181 
    1182        
    1183 
    1184     def __call__(self, domain):
    1185         """Evaluate windfield based on values found in domain
    1186         """
    1187 
    1188         from math import pi, cos, sin, sqrt
    1189        
    1190         xmom_update = domain.quantities['xmomentum'].explicit_update
    1191         ymom_update = domain.quantities['ymomentum'].explicit_update   
    1192        
    1193         N = domain.number_of_elements
    1194         t = self.domain.time
    1195        
    1196         msg = 'Time given in File %s does not match model time'\
    1197               %self.filename
    1198        
    1199         if t < self.T[0]: raise msg
    1200         if t > self.T[-1]: raise msg       
    1201 
    1202         oldindex = self.index
    1203 
    1204         #Find slot
    1205         while t > self.T[self.index]: self.index += 1
    1206         while t < self.T[self.index]: self.index -= 1
    1207 
    1208         #t is now between index and index+1
    1209         ratio = (t - self.T[self.index])/\
    1210                 (self.T[self.index+1] - self.T[self.index])
    1211 
    1212         #Compute interpolated values for s and phi
    1213         q = self.Q[self.index,:] +\
    1214             ratio*(self.Q[self.index+1,:] - self.Q[self.index,:])
    1215 
    1216         s = q[0]
    1217        
    1218         #Convert to radians
    1219         phi = q[1]*pi/180
    1220        
    1221         #Compute velocity vector (u, v)
    1222         u = s*cos(phi)
    1223         v = s*sin(phi)
    1224 
    1225         #Compute wind stress for this time step
    1226         S = self.const * sqrt(u**2 + v**2)
    1227         xmom_update += S*u
    1228         ymom_update += S*v       
    1229        
    1230 
    1231 
    12321064
    12331065
  • inundation/ga/storm_surge/pyvolution/test_generic_boundary_conditions.py

    r324 r623  
    144144            t_string = time.strftime(time_format, time.gmtime(t))
    145145
    146            
    147146            fid.write('%s,%f %f\n' %(t_string, 1.0*i, sin(i*2*pi/10)))
    148147        fid.close()
    149148                     
    150149       
    151         F = File_boundary(domain, filename)
     150        F = File_boundary(filename, domain)
    152151
    153152        from config import default_boundary_tag
     
    164163
    165164        os.remove(filename)
     165
     166
     167
     168    def test_fileboundary_exception(self):
     169        """Test that boundary object complians if number of
     170        conserved quantities are wrong
     171        """
     172
     173        from domain import Domain
     174        from quantity import Quantity
     175        import time, os
     176        from math import sin, pi
     177        from config import time_format
     178       
     179        a = [0.0, 0.0]
     180        b = [0.0, 2.0]
     181        c = [2.0,0.0]
     182        d = [0.0, 4.0]
     183        e = [2.0, 2.0]
     184        f = [4.0,0.0]
     185
     186        points = [a, b, c, d, e, f]
     187
     188        #bac, bce, ecf, dbe
     189        elements = [ [1,0,2], [1,2,4], [4,2,5], [3,1,4] ]
     190       
     191        domain = Domain(points, elements)
     192        domain.conserved_quantities = ['level', 'xmomentum', 'ymomentum']
     193        domain.quantities['level'] =\
     194                                   Quantity(domain, [[1,2,3], [5,5,5],
     195                                                     [0,0,9], [-6, 3, 3]])
     196
     197        domain.quantities['xmomentum'] =\
     198                                   Quantity(domain, [[2,3,4], [5,5,5],
     199                                                     [0,0,9], [-6, 3, 3]])
     200        domain.quantities['ymomentum'] =\
     201                                   Quantity(domain, [[2,3,4], [5,5,5],
     202                                                     [0,0,9], [-6, 3, 3]])
     203
     204        domain.check_integrity()
     205
     206        #Write file (with only two values)
     207        filename = 'boundarytest' + str(time.time())
     208        fid = open(filename, 'w')
     209        start = time.mktime(time.strptime('2000', '%Y'))
     210        dt = 5*60  #Five minute intervals
     211        for i in range(10):
     212            t = start + i*dt
     213            t_string = time.strftime(time_format, time.gmtime(t))
     214
     215            fid.write('%s,%f %f\n' %(t_string, 1.0*i, sin(i*2*pi/10)))
     216        fid.close()
     217                     
     218
     219        try:
     220            F = File_boundary(filename, domain)
     221        except AssertionError:
     222            pass
     223        else:
     224            raise 'Should have raised an exception'
     225
     226        os.remove(filename)
     227
     228
    166229       
    167230#-------------------------------------------------------------
  • inundation/ga/storm_surge/pyvolution/test_shallow_water.py

    r612 r623  
    1111
    1212#Variable windfield implemented using functions
    13 def speed(x,y,t):
     13def speed(t,x,y):
    1414    """Large speeds halfway between center and edges
    1515    Low speeds at center and edges
     
    3232
    3333
    34 def scalar_func(x,y,t):
     34def scalar_func(t,x,y):
    3535    """Function that returns a scalar.
    3636    Used to test error message when Numeric array is expected
     
    4040
    4141
    42 def angle(x,y,t):
     42def angle(t,x,y):
    4343    """Rotating field
    4444    """
     
    814814        x = xc[:,0]
    815815        y = xc[:,1]
    816         s_vec = speed(x,y,t)
    817         phi_vec = angle(x,y,t)
     816        s_vec = speed(t,x,y)
     817        phi_vec = angle(t,x,y)
    818818       
    819819
  • inundation/ga/storm_surge/pyvolution/test_util.py

    r258 r623  
    138138
    139139
     140
     141    def test_file_function_time(self):
     142        """Test that File function interpolates correctly
     143        between given times. No x,y dependency here.
     144        """
     145
     146        #Write file
     147        import os, time
     148        from config import time_format
     149        from math import sin, pi
     150
     151        finaltime = 1200
     152        filename = 'test_file_function.txt'
     153        fid = open(filename, 'w')
     154        start = time.mktime(time.strptime('2000', '%Y'))
     155        dt = 60  #One minute intervals
     156        t = 0.0
     157        while t <= finaltime:
     158            t_string = time.strftime(time_format, time.gmtime(t+start))
     159            fid.write('%s, %f %f %f\n' %(t_string, 2*t, t**2, sin(t*pi/600)))
     160            t += dt
     161   
     162        fid.close()
     163
     164        F = File_function(filename)
     165
     166        #Now try interpolation
     167        for i in range(20):
     168            t = i*10
     169            q = F(t)
     170
     171            #Exact linear intpolation
     172            assert allclose(q[0], 2*t)
     173            if i%6 == 0:
     174                assert allclose(q[1], t**2)
     175                assert allclose(q[2], sin(t*pi/600))               
     176
     177        #Check non-exact
     178
     179        t = 90 #Halfway between 60 and 120
     180        q = F(t)
     181        assert allclose( (120**2 + 60**2)/2, q[1] )
     182        assert allclose( (sin(120*pi/600) + sin(60*pi/600))/2, q[2] )
     183
     184
     185        t = 100 #Two thirds of the way between between 60 and 120
     186        q = F(t)
     187        assert allclose( 2*120**2/3 + 60**2/3, q[1] )
     188        assert allclose( 2*sin(120*pi/600)/3 + sin(60*pi/600)/3, q[2] )
     189       
     190
     191       
     192
     193    def test_file_function_time_with_domain(self):
     194        """Test that File function interpolates correctly
     195        between given times. No x,y dependency here.
     196        Use domain with starttime
     197        """
     198
     199        #Write file
     200        import os, time, calendar
     201        from config import time_format
     202        from math import sin, pi
     203        from domain import Domain
     204
     205        finaltime = 1200
     206        filename = 'test_file_function.txt'
     207        fid = open(filename, 'w')
     208        start = time.mktime(time.strptime('2000', '%Y'))
     209        dt = 60  #One minute intervals
     210        t = 0.0
     211        while t <= finaltime:
     212            t_string = time.strftime(time_format, time.gmtime(t+start))
     213            fid.write('%s, %f %f %f\n' %(t_string, 2*t, t**2, sin(t*pi/600)))
     214            t += dt
     215   
     216        fid.close()
     217
     218        a = [0.0, 0.0]
     219        b = [4.0, 0.0]
     220        c = [0.0, 3.0]
     221       
     222        points = [a, b, c]
     223        vertices = [[0,1,2]]
     224        domain = Domain(points, vertices)
     225
     226        #Check that domain.starttime is updated if non-existing
     227        F = File_function(filename, domain)
     228        assert allclose(domain.starttime, start)       
     229
     230        #Check that domain.starttime is updated if too early
     231        domain.starttime = start - 1               
     232        F = File_function(filename, domain)
     233        assert allclose(domain.starttime, start)
     234
     235        #Check that domain.starttime isn't updated if later
     236        domain.starttime = start + 1               
     237        F = File_function(filename, domain)
     238        assert allclose(domain.starttime, start+1)               
     239
     240       
     241       
     242        #Now try interpolation
     243        for i in range(20):
     244            t = i*10
     245            q = F(t)
     246
     247            #Exact linear intpolation
     248            assert allclose(q[0], 2*t)
     249            if i%6 == 0:
     250                assert allclose(q[1], t**2)
     251                assert allclose(q[2], sin(t*pi/600))               
     252
     253        #Check non-exact
     254
     255        t = 90 #Halfway between 60 and 120
     256        q = F(t)
     257        assert allclose( (120**2 + 60**2)/2, q[1] )
     258        assert allclose( (sin(120*pi/600) + sin(60*pi/600))/2, q[2] )
     259
     260
     261        t = 100 #Two thirds of the way between between 60 and 120
     262        q = F(t)
     263        assert allclose( 2*120**2/3 + 60**2/3, q[1] )
     264        assert allclose( 2*sin(120*pi/600)/3 + sin(60*pi/600)/3, q[2] )
     265       
    140266     
    141267#-------------------------------------------------------------
  • inundation/ga/storm_surge/pyvolution/util.py

    r274 r623  
    4646    return sum(x)/len(x)
    4747
     48
     49
     50
     51#FIXME: Maybe move to util as this is quite general
     52class File_function:
     53    """Read time series from file and return a callable object:
     54    f(t,x,y)
     55    which will return interpolated values based on the input file.
     56
     57    The file format is assumed to be either two fields separated by a comma:
     58
     59        time [DD/MM/YY hh:mm:ss], value0 value1 value2 ...
     60
     61    or four comma separated fields
     62
     63        time [DD/MM/YY hh:mm:ss], x, y, value0 value1 value2 ...
     64
     65    In either case, the callable obejct will return a tuple of
     66    interpolated values, one each value stated in the file.
     67
     68   
     69    E.g
     70
     71      31/08/04 00:00:00, 1.328223 0 0
     72      31/08/04 00:15:00, 1.292912 0 0
     73
     74    will provide a time dependent function f(t,x=None,y=None),
     75    ignoring x and y, which returns three values per call.
     76
     77   
     78    NOTE: At this stage the function is assumed to depend on
     79    time only, i.e  no spatial dependency!!!!!
     80    When that is need we can use the least_squares interpolation.
     81    """
     82
     83   
     84    def __init__(self, filename, domain=None):
     85        """Initialise callable object from a file.
     86        See docstring for class File_function
     87
     88        If domain is specified, model time (domain,starttime)
     89        will be checked and possibly modified
     90
     91        ALl times are assumed to be in UTC
     92        """
     93
     94        import time, calendar
     95        from Numeric import array
     96        from config import time_format
     97
     98        try:
     99            fid = open(filename)
     100        except Exception, e:
     101            msg = 'File %s could not be opened: %s\n'\
     102                  %(filename, e)
     103            raise msg
     104
     105
     106        line = fid.readline()
     107        fid.close()
     108        fields = line.split(',')
     109        msg = 'File %s must have the format date, value0 value1 value2 ...'
     110        msg += ' or date, x, y, value0 value1 value2 ...'
     111        mode = len(fields)
     112        assert mode in [2,4], msg
     113
     114        try:
     115            starttime = calendar.timegm(time.strptime(fields[0], time_format))
     116        except ValueError:   
     117            msg = 'First field in file %s must be' %filename
     118            msg += ' date-time with format %s.\n' %time_format
     119            msg += 'I got %s instead.' %fields[0]
     120            raise msg
     121
     122
     123        #Split values
     124        values = []
     125        for value in fields[mode-1].split():
     126            values.append(float(value))
     127
     128        q = array(values)
     129       
     130        msg = 'ERROR: File must contain at least one independent value'
     131        assert len(q.shape) == 1, msg
     132           
     133        self.number_of_values = len(q)
     134
     135        self.filename = filename
     136        self.starttime = starttime
     137
     138
     139        if domain is not None:
     140            if domain.starttime is None:
     141                domain.starttime = starttime
     142            else:
     143                msg = 'WARNING: Start time as specified in domain (%s)'\
     144                      %domain.starttime
     145                msg += ' is earlier than the starttime of file %s: %s.'\
     146                         %(self.filename, self.starttime)
     147                msg += 'Modifying starttime accordingly.'
     148                if self.starttime > domain.starttime:
     149                    #FIXME: Print depending on some verbosity setting
     150                    #print msg
     151                    domain.starttime = self.starttime #Modifying model time
     152
     153        if mode == 2:       
     154            self.read_times() #Now read all times in.
     155        else:
     156            raise 'x,y dependecy not yet implemented'
     157
     158       
     159    def read_times(self):
     160        """Read time and values
     161        """
     162        from Numeric import zeros, Float, alltrue
     163        from config import time_format
     164        import time, calendar
     165       
     166        fid = open(self.filename)       
     167        lines = fid.readlines()
     168        fid.close()
     169       
     170        N = len(lines)
     171        d = self.number_of_values
     172
     173        T = zeros(N, Float)       #Time
     174        Q = zeros((N, d), Float)  #Values
     175       
     176        for i, line in enumerate(lines):
     177            fields = line.split(',')
     178            realtime = calendar.timegm(time.strptime(fields[0], time_format))
     179
     180            T[i] = realtime - self.starttime
     181
     182            for j, value in enumerate(fields[1].split()):
     183                Q[i, j] = float(value)
     184
     185        msg = 'File %s must list time as a monotonuosly ' %self.filename
     186        msg += 'increasing sequence'
     187        assert alltrue( T[1:] - T[:-1] > 0 ), msg
     188
     189        self.T = T     #Time
     190        self.Q = Q     #Values
     191        self.index = 0 #Initial index
     192
     193       
     194    def __repr__(self):
     195        return 'File function'
     196
     197       
     198
     199    def __call__(self, t, x=None, y=None):
     200        """Evaluate f(t,x,y)
     201
     202        FIXME: x, y dependency not yet implemented
     203        """
     204
     205        from math import pi, cos, sin, sqrt
     206       
     207       
     208        msg = 'Time interval derived from file %s (%s:%s) does not match model time: %s'\
     209              %(self.filename, self.T[0], self.T[1], t)
     210        if t < self.T[0]: raise msg
     211        if t > self.T[-1]: raise msg       
     212
     213        oldindex = self.index
     214
     215        #Find slot
     216        while t > self.T[self.index]: self.index += 1
     217        while t < self.T[self.index]: self.index -= 1
     218
     219        #t is now between index and index+1
     220        ratio = (t - self.T[self.index])/\
     221                (self.T[self.index+1] - self.T[self.index])
     222
     223        #Compute interpolated values for values
     224        q = self.Q[self.index,:] +\
     225            ratio*(self.Q[self.index+1,:] - self.Q[self.index,:])
     226
     227        #Return vector of interpolated values
     228        return q
     229   
     230
     231
     232
    48233####################################################################
    49234#Python versions of function that are also implemented in util_gateway.c
Note: See TracChangeset for help on using the changeset viewer.