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

Last change on this file since 628 was 624, checked in by ole, 20 years ago
File size: 7.0 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
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        assert type(filename) == type(''),\
99               'First argument to File_function must be a string'
100
101
102        try:
103            fid = open(filename)
104        except Exception, e:
105            msg = 'File "%s" could not be opened: Error="%s"'\
106                  %(filename, e)
107            raise msg
108
109
110        line = fid.readline()
111        fid.close()
112        fields = line.split(',')
113        msg = 'File %s must have the format date, value0 value1 value2 ...'
114        msg += ' or date, x, y, value0 value1 value2 ...'
115        mode = len(fields)
116        assert mode in [2,4], msg
117
118        try:
119            starttime = calendar.timegm(time.strptime(fields[0], time_format))
120        except ValueError:   
121            msg = 'First field in file %s must be' %filename
122            msg += ' date-time with format %s.\n' %time_format
123            msg += 'I got %s instead.' %fields[0] 
124            raise msg
125
126
127        #Split values
128        values = []
129        for value in fields[mode-1].split():
130            values.append(float(value))
131
132        q = array(values)
133       
134        msg = 'ERROR: File must contain at least one independent value'
135        assert len(q.shape) == 1, msg
136           
137        self.number_of_values = len(q)
138
139        self.filename = filename
140        self.starttime = starttime
141
142
143        if domain is not None:
144            if domain.starttime is None:
145                domain.starttime = starttime
146            else:
147                msg = 'WARNING: Start time as specified in domain (%s)'\
148                      %domain.starttime
149                msg += ' is earlier than the starttime of file %s: %s.'\
150                         %(self.filename, self.starttime)
151                msg += 'Modifying starttime accordingly.'
152                if self.starttime > domain.starttime:
153                    #FIXME: Print depending on some verbosity setting
154                    #print msg
155                    domain.starttime = self.starttime #Modifying model time
156
157        if mode == 2:       
158            self.read_times() #Now read all times in.
159        else:
160            raise 'x,y dependecy not yet implemented'
161
162       
163    def read_times(self):
164        """Read time and values
165        """
166        from Numeric import zeros, Float, alltrue
167        from config import time_format
168        import time, calendar
169       
170        fid = open(self.filename)       
171        lines = fid.readlines()
172        fid.close()
173       
174        N = len(lines)
175        d = self.number_of_values
176
177        T = zeros(N, Float)       #Time
178        Q = zeros((N, d), Float)  #Values
179       
180        for i, line in enumerate(lines):
181            fields = line.split(',')
182            realtime = calendar.timegm(time.strptime(fields[0], time_format))
183
184            T[i] = realtime - self.starttime
185
186            for j, value in enumerate(fields[1].split()):
187                Q[i, j] = float(value)
188
189        msg = 'File %s must list time as a monotonuosly ' %self.filename
190        msg += 'increasing sequence'
191        assert alltrue( T[1:] - T[:-1] > 0 ), msg
192
193        self.T = T     #Time
194        self.Q = Q     #Values
195        self.index = 0 #Initial index
196
197       
198    def __repr__(self):
199        return 'File function'
200
201       
202
203    def __call__(self, t, x=None, y=None):
204        """Evaluate f(t,x,y)
205
206        FIXME: x, y dependency not yet implemented
207        """
208
209        from math import pi, cos, sin, sqrt
210       
211       
212        msg = 'Time interval derived from file %s (%s:%s) does not match model time: %s'\
213              %(self.filename, self.T[0], self.T[1], t)
214        if t < self.T[0]: raise msg
215        if t > self.T[-1]: raise msg       
216
217        oldindex = self.index
218
219        #Find slot
220        while t > self.T[self.index]: self.index += 1
221        while t < self.T[self.index]: self.index -= 1
222
223        #t is now between index and index+1
224        ratio = (t - self.T[self.index])/\
225                (self.T[self.index+1] - self.T[self.index])
226
227        #Compute interpolated values for values
228        q = self.Q[self.index,:] +\
229            ratio*(self.Q[self.index+1,:] - self.Q[self.index,:]) 
230
231        #Return vector of interpolated values
232        return q
233   
234
235
236
237####################################################################
238#Python versions of function that are also implemented in util_gateway.c
239#
240
241def gradient_python(x0, y0, x1, y1, x2, y2, q0, q1, q2):
242    """
243    """
244   
245    det = (y2-y0)*(x1-x0) - (y1-y0)*(x2-x0)           
246    a = (y2-y0)*(q1-q0) - (y1-y0)*(q2-q0)
247    a /= det
248
249    b = (x1-x0)*(q2-q0) - (x2-x0)*(q1-q0)
250    b /= det           
251
252    return a, b
253
254
255
256
257
258##############################################
259#Initialise module
260
261import compile
262if compile.can_use_C_extension('util_ext.c'):
263    from util_ext import gradient
264else:
265    gradient = gradient_python
266
267
268if __name__ == "__main__":
269    pass
270
Note: See TracBrowser for help on using the repository browser.