source: inundation/ga/storm_surge/pyvolution/util.py @ 631

Last change on this file since 631 was 630, checked in by ole, 20 years ago
File size: 7.2 KB
Line 
1
2
3def angle(v):
4    """Compute angle between e1 (the unit vector in the x-direction)
5    and the specified vector
6    """
7
8    from math import acos, pi, sqrt
9    from Numeric import sum, array
10
11    l = sqrt( sum (array(v)**2))
12    v1 = v[0]/l
13    v2 = v[1]/l
14       
15    theta = acos(v1)
16
17    if v2 < 0:
18        #Quadrant 3 or 4
19        theta = 2*pi-theta
20
21    return theta
22
23
24def anglediff(v0, v1):
25    """Compute difference between angle of vector x0, y0 and x1, y1.
26    This is used for determining the ordering of vertices,
27    e.g. for checking if they are counter clockwise.
28   
29    Always return a positive value
30    """
31       
32    from math import pi
33   
34    a0 = angle(v0)
35    a1 = angle(v1)
36
37    #Ensure that difference will be positive
38    if a0 < a1:
39        a0 += 2*pi
40           
41    return a0-a1
42
43
44def mean(x):
45    from Numeric import sum
46    return sum(x)/len(x)
47
48
49
50class File_function:
51    """Read time series from file and return a callable object:
52    f(t,x,y)
53    which will return interpolated values based on the input file.
54
55    The file format is assumed to be either two fields separated by a comma:
56
57        time [DD/MM/YY hh:mm:ss], value0 value1 value2 ...
58
59    or four comma separated fields
60
61        time [DD/MM/YY hh:mm:ss], x, y, value0 value1 value2 ...
62
63    In either case, the callable obejct will return a tuple of
64    interpolated values, one each value stated in the file.
65
66   
67    E.g
68
69      31/08/04 00:00:00, 1.328223 0 0
70      31/08/04 00:15:00, 1.292912 0 0
71
72    will provide a time dependent function f(t,x=None,y=None),
73    ignoring x and y, which returns three values per call.
74
75   
76    NOTE: At this stage the function is assumed to depend on
77    time only, i.e  no spatial dependency!!!!!
78    When that is need we can use the least_squares interpolation.
79    """
80
81   
82    def __init__(self, filename, domain=None):
83        """Initialise callable object from a file.
84        See docstring for class File_function
85
86        If domain is specified, model time (domain,starttime)
87        will be checked and possibly modified
88
89        ALl times are assumed to be in UTC
90        """
91
92        import time, calendar
93        from Numeric import array
94        from config import time_format
95
96        assert type(filename) == type(''),\
97               'First argument to File_function must be a string'
98
99
100        try:
101            fid = open(filename)
102        except Exception, e:
103            msg = 'File "%s" could not be opened: Error="%s"'\
104                  %(filename, e)
105            raise msg
106
107
108        line = fid.readline()
109        fid.close()
110        fields = line.split(',')
111        msg = 'File %s must have the format date, value0 value1 value2 ...'
112        msg += ' or date, x, y, value0 value1 value2 ...'
113        mode = len(fields)
114        assert mode in [2,4], msg
115
116        try:
117            starttime = calendar.timegm(time.strptime(fields[0], time_format))
118        except ValueError:   
119            msg = 'First field in file %s must be' %filename
120            msg += ' date-time with format %s.\n' %time_format
121            msg += 'I got %s instead.' %fields[0] 
122            raise msg
123
124
125        #Split values
126        values = []
127        for value in fields[mode-1].split():
128            values.append(float(value))
129
130        q = array(values)
131       
132        msg = 'ERROR: File must contain at least one independent value'
133        assert len(q.shape) == 1, msg
134           
135        self.number_of_values = len(q)
136
137        self.filename = filename
138        self.starttime = starttime
139        self.domain = domain
140
141        if domain is not None:
142            if domain.starttime is None:
143                domain.starttime = self.starttime
144            else:
145                msg = 'WARNING: Start time as specified in domain (%s)'\
146                      %domain.starttime
147                msg += ' is earlier than the starttime of file %s: %s.'\
148                         %(self.filename, self.starttime)
149                msg += 'Modifying starttime accordingly.'
150                if self.starttime > domain.starttime:
151                    #FIXME: Print depending on some verbosity setting
152                    #print msg
153                    domain.starttime = self.starttime #Modifying model time
154
155        if mode == 2:       
156            self.read_times() #Now read all times in.
157        else:
158            raise 'x,y dependecy not yet implemented'
159
160       
161    def read_times(self):
162        """Read time and values
163        """
164        from Numeric import zeros, Float, alltrue
165        from config import time_format
166        import time, calendar
167       
168        fid = open(self.filename)       
169        lines = fid.readlines()
170        fid.close()
171       
172        N = len(lines)
173        d = self.number_of_values
174
175        T = zeros(N, Float)       #Time
176        Q = zeros((N, d), Float)  #Values
177       
178        for i, line in enumerate(lines):
179            fields = line.split(',')
180            realtime = calendar.timegm(time.strptime(fields[0], time_format))
181
182            T[i] = realtime - self.starttime
183
184            for j, value in enumerate(fields[1].split()):
185                Q[i, j] = float(value)
186
187        msg = 'File %s must list time as a monotonuosly ' %self.filename
188        msg += 'increasing sequence'
189        assert alltrue( T[1:] - T[:-1] > 0 ), msg
190
191        self.T = T     #Time
192        self.Q = Q     #Values
193        self.index = 0 #Initial index
194
195       
196    def __repr__(self):
197        return 'File function'
198
199       
200
201    def __call__(self, t, x=None, y=None):
202        """Evaluate f(t,x,y)
203
204        FIXME: x, y dependency not yet implemented
205        """
206
207        from math import pi, cos, sin, sqrt
208       
209
210        #Find time tau such that
211        #
212        # domain.starttime + t == self.starttime + tau
213       
214        if self.domain is not None:
215            tau = self.domain.starttime-self.starttime+t
216        else:
217            tau = t
218       
219               
220        msg = 'Time interval derived from file %s (%s:%s) does not match model time: %s'\
221              %(self.filename, self.T[0], self.T[1], tau)
222        if tau < self.T[0]: raise msg
223        if tau > self.T[-1]: raise msg       
224
225        oldindex = self.index
226
227        #Find slot
228        while tau > self.T[self.index]: self.index += 1
229        while tau < self.T[self.index]: self.index -= 1
230
231        #t is now between index and index+1
232        ratio = (tau - self.T[self.index])/\
233                (self.T[self.index+1] - self.T[self.index])
234
235        #Compute interpolated values for values
236        q = self.Q[self.index,:] +\
237            ratio*(self.Q[self.index+1,:] - self.Q[self.index,:]) 
238
239        #Return vector of interpolated values
240        return q
241   
242
243
244
245####################################################################
246#Python versions of function that are also implemented in util_gateway.c
247#
248
249def gradient_python(x0, y0, x1, y1, x2, y2, q0, q1, q2):
250    """
251    """
252   
253    det = (y2-y0)*(x1-x0) - (y1-y0)*(x2-x0)           
254    a = (y2-y0)*(q1-q0) - (y1-y0)*(q2-q0)
255    a /= det
256
257    b = (x1-x0)*(q2-q0) - (x2-x0)*(q1-q0)
258    b /= det           
259
260    return a, b
261
262
263
264
265
266##############################################
267#Initialise module
268
269import compile
270if compile.can_use_C_extension('util_ext.c'):
271    from util_ext import gradient
272else:
273    gradient = gradient_python
274
275
276if __name__ == "__main__":
277    pass
278
Note: See TracBrowser for help on using the repository browser.