1 | |
---|
2 | |
---|
3 | def 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 | |
---|
24 | def 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 | |
---|
44 | def 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 |
---|
52 | class 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 | |
---|
241 | def 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 | |
---|
261 | import compile |
---|
262 | if compile.can_use_C_extension('util_ext.c'): |
---|
263 | from util_ext import gradient |
---|
264 | else: |
---|
265 | gradient = gradient_python |
---|
266 | |
---|
267 | |
---|
268 | if __name__ == "__main__": |
---|
269 | pass |
---|
270 | |
---|