1 |
#!/usr/bin/env python |
2 |
|
3 |
# $Id$ |
4 |
|
5 |
import os, sys |
6 |
import vtk |
7 |
from esys.escript import * |
8 |
from esys.escript.linearPDEs import LinearPDE |
9 |
from esys.escript.modelframe import Model |
10 |
import numarray |
11 |
import math |
12 |
#import urllib |
13 |
import urllib2 |
14 |
|
15 |
EPS=1.e-8 |
16 |
|
17 |
#==================================================================================================================================================== |
18 |
class GridData: |
19 |
""" |
20 |
this object is used to store data on grid. |
21 |
it will be replaced by Bruce at a later stage. |
22 |
|
23 |
data[i,j] are data are x=j*s[0]+o[0] and y=i*s[1]+o[1] |
24 |
|
25 |
for 0<=j<n[0] and 0<=i<n[1] |
26 |
""" |
27 |
def __init__(self,s,o,n,data): |
28 |
self.s=tuple(s) |
29 |
self.o=tuple(o) |
30 |
self.n=tuple(n) |
31 |
self.data=data |
32 |
|
33 |
def getOrigin(self): |
34 |
return self.o |
35 |
def getSpacing(self): |
36 |
return self.s |
37 |
def getData(self): |
38 |
return self.data |
39 |
|
40 |
def interpolate(self,x): |
41 |
if hasattr(x,"convertToNumArray"): |
42 |
x_array=x.convertToNumArray() |
43 |
return_data_object=True |
44 |
else: |
45 |
x_array=numarray.array(x) |
46 |
return_data_object=False |
47 |
data=numarray.zeros(x_array.shape[0],numarray.Float) |
48 |
ox,oy=self.getOrigin() |
49 |
dx,dy=self.getSpacing() |
50 |
data_array=self.getData() |
51 |
i_dx=1 |
52 |
i_dy=1 |
53 |
for i in range(x_array.shape[0]): |
54 |
x_long=x_array[i,0]-ox |
55 |
x_lat=x_array[i,1]-oy |
56 |
j0=min(max(int(x_long/dx),0),data_array.shape[1]-1-i_dy) |
57 |
j1=min(max(int(x_lat/dy),0),data_array.shape[0]-1-i_dx) |
58 |
f01=(x_long-j0*dx)/dx |
59 |
f00=1.-f01 |
60 |
f11=(x_lat-j1*dy)/dy |
61 |
f10=1.-f11 |
62 |
H00=data_array[j1,j0] |
63 |
H01=data_array[j1,j0+i_dx] |
64 |
H11=data_array[j1+i_dy,j0+i_dx] |
65 |
H10=data_array[j1+i_dy,j0] |
66 |
data[i]=(H00*f00+H01*f01)*f10+(H10*f00+H11*f01)*f11 |
67 |
if return_data_object: |
68 |
out=Scalar(0,x.getFunctionSpace()) |
69 |
out.fillFromNumArray(data) |
70 |
return out |
71 |
else: |
72 |
return data |
73 |
|
74 |
def getVTK(self): |
75 |
pass |
76 |
|
77 |
|
78 |
class PointOnEarthSurface: |
79 |
""" |
80 |
coordinates of a point on the surface of the Earth |
81 |
""" |
82 |
def __init__(self,long=0,lat=0): |
83 |
self.long=long |
84 |
self.lat=lat |
85 |
def __str__(self): |
86 |
return "(%s,%s)"%(self.long,self.lat) |
87 |
|
88 |
def __sub__(self,other): |
89 |
return self.dist(other) |
90 |
|
91 |
def split(self,p,t): |
92 |
return PointOnEarthSurface(long=self.long+t*(p.long-self.long),lat=self.lat+t*(p.lat-self.lat)) |
93 |
|
94 |
def midPoint(self,other): |
95 |
return PointOnEarthSurface(long=(self.long+other.long)/2,lat=(self.lat+other.lat)/2) |
96 |
|
97 |
def dist(self,other): |
98 |
return math.sqrt((self.long-other.long)**2+(self.lat-other.lat)**2) |
99 |
|
100 |
class RegionOnEarthSurface: |
101 |
""" |
102 |
defines an region by a south,east and north,west corner |
103 |
""" |
104 |
RADIUS=6378.8e3 |
105 |
GRAVITY=9.81 |
106 |
WEST=0 |
107 |
NORTH=1 |
108 |
EAST=2 |
109 |
SOUTH=3 |
110 |
def __init__(self,west_south=PointOnEarthSurface(),east_north=PointOnEarthSurface(),resolution=1.): |
111 |
if resolution<=0: |
112 |
raise ValueError,"resolution must be positive" |
113 |
if west_south.long>=east_north.long: |
114 |
raise ValueError,"south west corner must be further east than west north corner" |
115 |
if west_south.lat>=east_north.lat: |
116 |
raise ValueError,"east south corner must be further down than west north corner" |
117 |
if east_north.lat-west_south.lat < resolution/2: |
118 |
raise ValueError,"latitude length of region must be 2*bigger then resolution" |
119 |
if east_north.long-west_south.long < resolution/2: |
120 |
raise ValueError,"longitude length of region must be 2*bigger then resolution" |
121 |
|
122 |
self.west_south=west_south |
123 |
self.east_north=east_north |
124 |
self.resolution=resolution |
125 |
|
126 |
def __str__(self): |
127 |
return "RegionOnEarthSurface between %s and %s"%(str(self.west_south),str(self.east_north)) |
128 |
|
129 |
def isOnFace(self,p): |
130 |
return self.isOnThisFace(p,self.SOUTH) or self.isOnThisFace(p,self.NORTH) or self.isOnThisFace(p,self.WEST) or self.isOnThisFace(p,self.EAST) |
131 |
def isOnThisFace(self,p,face): |
132 |
if face==self.WEST: |
133 |
return self.west_south.long==p.long |
134 |
if face==self.EAST: |
135 |
return p.long==self.east_north.long |
136 |
if face==self.SOUTH: |
137 |
return self.west_south.lat==p.lat |
138 |
if face==self.NORTH: |
139 |
return p.lat==self.east_north.lat |
140 |
|
141 |
def isBoxVertex(self,p): |
142 |
return ( self.isOnThisFace(p,self.WEST) and self.isOnThisFace(p,self.SOUTH) ) or \ |
143 |
( self.isOnThisFace(p,self.WEST) and self.isOnThisFace(p,self.NORTH) ) or \ |
144 |
( self.isOnThisFace(p,self.EAST) and self.isOnThisFace(p,self.NORTH) ) or \ |
145 |
( self.isOnThisFace(p,self.EAST) and self.isOnThisFace(p,self.SOUTH) ) |
146 |
|
147 |
|
148 |
def getFace(self,p): |
149 |
# order is critical |
150 |
if self.west_south.long==p.long: return self.WEST |
151 |
if p.lat==self.east_north.lat: return self.NORTH |
152 |
if p.long==self.east_north.long: return self.EAST |
153 |
if self.west_south.lat==p.lat: return self.SOUTH |
154 |
|
155 |
def comparePointsOnFace(self,p0,p1): |
156 |
f0=self.getFace(p0) |
157 |
f1=self.getFace(p1) |
158 |
|
159 |
if f0<f1: |
160 |
return -1 |
161 |
elif f0>f1: |
162 |
return 1 |
163 |
else: |
164 |
if f0 == self.WEST: |
165 |
if p0.lat<p1.lat: |
166 |
return -1 |
167 |
elif p0.lat>p1.lat: |
168 |
return 1 |
169 |
else: |
170 |
return 0 |
171 |
elif f0 == self.EAST: |
172 |
if p0.lat<p1.lat: |
173 |
return 1 |
174 |
elif p0.lat>p1.lat: |
175 |
return -1 |
176 |
else: |
177 |
return 0 |
178 |
elif f0 == self.NORTH: |
179 |
if p0.long<p1.long: |
180 |
return -1 |
181 |
elif p0.long>p1.long: |
182 |
return 1 |
183 |
else: |
184 |
return 0 |
185 |
else: |
186 |
if p0.long<p1.long: |
187 |
return 1 |
188 |
elif p0.long>p1.long: |
189 |
return -1 |
190 |
else: |
191 |
return 0 |
192 |
|
193 |
|
194 |
def isInRegion(self,p): |
195 |
return self.west_south.long<=p.long \ |
196 |
and p.long<=self.east_north.long \ |
197 |
and self.west_south.lat<=p.lat \ |
198 |
and p.lat<=self.east_north.lat |
199 |
|
200 |
def cutSegment(self,p0,p1): |
201 |
t=None |
202 |
p=None |
203 |
tmp=self.interseptOfSegment(p0,p1,d=0,v=self.west_south.long) |
204 |
if not tmp==None: |
205 |
p_tmp=p0.split(p1,tmp) |
206 |
if self.isInRegion(p_tmp) and t<tmp: |
207 |
t=tmp |
208 |
p=p_tmp |
209 |
|
210 |
tmp=self.interseptOfSegment(p0,p1,d=0,v=self.east_north.long) |
211 |
if not tmp==None: |
212 |
p_tmp=p0.split(p1,tmp) |
213 |
if self.isInRegion(p_tmp) and t<tmp: |
214 |
t=tmp |
215 |
p=p_tmp |
216 |
|
217 |
tmp=self.interseptOfSegment(p0,p1,d=1,v=self.west_south.lat) |
218 |
if not tmp==None: |
219 |
p_tmp=p0.split(p1,tmp) |
220 |
if self.isInRegion(p_tmp) and t<tmp: |
221 |
t=tmp |
222 |
p=p_tmp |
223 |
|
224 |
tmp=self.interseptOfSegment(p0,p1,d=1,v=self.east_north.lat) |
225 |
if not tmp==None: |
226 |
p_tmp=p0.split(p1,tmp) |
227 |
if self.isInRegion(p_tmp) and t<tmp: |
228 |
t=tmp |
229 |
p=p_tmp |
230 |
return p |
231 |
|
232 |
def interseptOfSegment(self,p0,p1,d=0,v=0.): |
233 |
""" |
234 |
find t isn [0,1] such that p0+t*(p0-p1) cuts x[d]=v. if it does nit exist None is returned. |
235 |
""" |
236 |
if d==0: |
237 |
a=p0.long |
238 |
b=p1.long |
239 |
else: |
240 |
a=p0.lat |
241 |
b=p1.lat |
242 |
if b==a: |
243 |
if a==v: |
244 |
t=0 |
245 |
else: |
246 |
t=None |
247 |
else: |
248 |
t=(v-a)/(b-a) |
249 |
if not (0<=t and t<=1): t=None |
250 |
return t |
251 |
|
252 |
class Polyline: |
253 |
""" |
254 |
defines set of segments through a list of coordinates |
255 |
""" |
256 |
def __init__(self,list_of_coordinates=[],name="none"): |
257 |
c=[] |
258 |
if len(list_of_coordinates)>0: |
259 |
for i in range(len(list_of_coordinates)-1): |
260 |
if list_of_coordinates[i]-list_of_coordinates[i+1]>0: c.append(list_of_coordinates[i]) |
261 |
c.append(list_of_coordinates[-1]) |
262 |
self.list_of_coordinates=c |
263 |
self.name=name |
264 |
def getDiameter(self): |
265 |
out=0. |
266 |
for p in self.list_of_coordinates: |
267 |
for q in self.list_of_coordinates: |
268 |
out=max(out,p-q) |
269 |
return out |
270 |
def isLoop(self): |
271 |
if len(self)>0: |
272 |
return not self[0]-self[-1]>EPS |
273 |
else: |
274 |
return True |
275 |
|
276 |
def insert(self,index,coordinate): |
277 |
""" |
278 |
insert point before index |
279 |
""" |
280 |
if self.list_of_coordinates[index]-coordinate<EPS: |
281 |
return index |
282 |
elif self.list_of_coordinates[index-1]-coordinate<EPS: |
283 |
return index-1 |
284 |
else: |
285 |
self.list_of_coordinates.insert(index,coordinate) |
286 |
return index |
287 |
|
288 |
def split(self,index): |
289 |
""" |
290 |
splits the polyline at point index |
291 |
""" |
292 |
return Polyline(self.list_of_coordinates[:index],self.name),Polyline(self.list_of_coordinates[index:],self.name) |
293 |
|
294 |
def __getitem__(self,s): |
295 |
return self.list_of_coordinates.__getitem__(s) |
296 |
def __iter__(self): |
297 |
return iter(self.list_of_coordinates) |
298 |
|
299 |
def __str__(self): |
300 |
if self.isLoop(): |
301 |
out="loop[" |
302 |
else: |
303 |
out="[" |
304 |
for i in self: |
305 |
if out[-1]=="[": |
306 |
out+="%s"%str(i) |
307 |
else: |
308 |
out+=",%s"%str(i) |
309 |
return out+"]" |
310 |
|
311 |
def __len__(self): |
312 |
return len(self.list_of_coordinates) |
313 |
|
314 |
def orientation(self): |
315 |
""" |
316 |
returns the orientation of the polyline |
317 |
""" |
318 |
if not self.isLoop(): |
319 |
raise TypeError,"polyline is not a loop" |
320 |
|
321 |
integ=0. |
322 |
for i in range(len(self.list_of_coordinates)-1): |
323 |
p0=self.list_of_coordinates[i] |
324 |
p1=self.list_of_coordinates[i+1] |
325 |
integ+=(p1.lat-p0.lat)*(p1.long+p0.long)-(p1.long-p0.long)*(p1.lat+p0.lat) |
326 |
if integ>=0.: |
327 |
return 1. |
328 |
else: |
329 |
return -1. |
330 |
|
331 |
def givePositiveOrientation(self): |
332 |
if self.orientation()<0: self.list_of_coordinates.reverse() |
333 |
|
334 |
class Coastline: |
335 |
""" |
336 |
defines a coast line by a Polyline within a RegionOnEarthSurface |
337 |
""" |
338 |
def __init__(self,region,name="none"): |
339 |
self.region=region |
340 |
self.name=name |
341 |
self.polylines=[] |
342 |
def __str__(self): |
343 |
out=self.name+" in "+str(self.region) |
344 |
for pl in self.polylines: |
345 |
out+="\n"+str(pl) |
346 |
return out |
347 |
def makeTriangulation(self,west_south_is_water=True,east_south_is_water=True,west_north_is_water=True,east_north_is_water=True): |
348 |
self.clean() |
349 |
vertices=[] |
350 |
segments=[] |
351 |
holes=[] |
352 |
vertices_on_face=[] |
353 |
for pl in self.polylines: |
354 |
if pl.getDiameter()>self.region.resolution: |
355 |
short_pl=[pl[0]] |
356 |
for i in range(1,len(pl)): |
357 |
if pl[i]-short_pl[-1]>0*EPS+self.region.resolution/10: |
358 |
short_pl.append(pl[i]) |
359 |
elif i==len(pl)-1: |
360 |
short_pl[-1]=pl[i] |
361 |
if pl.isLoop(): |
362 |
if len(short_pl)>3: |
363 |
offset=len(vertices) |
364 |
v_tmp=[short_pl[0]] |
365 |
s_tmp=[] |
366 |
for i in range(1,len(short_pl)): |
367 |
if i==len(short_pl)-1: |
368 |
s_tmp.append((len(v_tmp)-1+offset,offset)) |
369 |
else: |
370 |
s_tmp.append((len(v_tmp)-1+offset,len(v_tmp)+offset)) |
371 |
v_tmp.append(short_pl[i]) |
372 |
vertices+=v_tmp |
373 |
segments+=s_tmp |
374 |
# find a point in the loop: |
375 |
d_long=v_tmp[1].long-v_tmp[0].long |
376 |
d_lat=v_tmp[1].lat-v_tmp[0].lat |
377 |
l=math.sqrt(d_long**2+d_lat**2) |
378 |
mid=v_tmp[0].midPoint(v_tmp[1]) |
379 |
n_long=-d_lat/l |
380 |
n_lat=d_long/l |
381 |
s=self.region.resolution |
382 |
for seg in s_tmp: |
383 |
p0=vertices[seg[0]] |
384 |
p1=vertices[seg[1]] |
385 |
a_long=p1.long-p0.long |
386 |
a_lat=p1.lat-p0.lat |
387 |
d=a_lat*n_long-a_long*n_lat |
388 |
if abs(d)>0.: |
389 |
t=((mid.lat-p0.lat)*n_long-(mid.long-p0.long)*n_lat)/d |
390 |
if 0<=t and t<=1: |
391 |
s_tmp=((mid.lat-p0.lat)*a_long-(mid.long-p0.long)*a_lat)/d |
392 |
if s_tmp>EPS: s=min(s,s_tmp) |
393 |
h=PointOnEarthSurface(long=mid.long+s/2*n_long,lat=mid.lat+s/2*n_lat) |
394 |
holes.append(h) |
395 |
else: |
396 |
if len(short_pl)>1: |
397 |
if self.region.isOnFace(short_pl[0]): vertices_on_face.append(short_pl[0]) |
398 |
if self.region.isOnFace(short_pl[-1]): vertices_on_face.append(short_pl[-1]) |
399 |
vertices.append(short_pl[0]) |
400 |
for i in range(1,len(short_pl)): |
401 |
segments.append((len(vertices)-1,len(vertices))) |
402 |
vertices.append(short_pl[i]) |
403 |
# put into the bounding box: |
404 |
new_vertices=[] |
405 |
if west_south_is_water: |
406 |
new_vertices.append(PointOnEarthSurface(long=self.region.west_south.long,lat=self.region.west_south.lat)) |
407 |
if east_south_is_water: |
408 |
new_vertices.append(PointOnEarthSurface(long=self.region.east_north.long,lat=self.region.west_south.lat)) |
409 |
if west_north_is_water: |
410 |
new_vertices.append(PointOnEarthSurface(long=self.region.west_south.long,lat=self.region.east_north.lat)) |
411 |
if east_north_is_water: |
412 |
new_vertices.append(PointOnEarthSurface(long=self.region.east_north.long,lat=self.region.east_north.lat)) |
413 |
|
414 |
# add new vertices if they don't exist yet |
415 |
for q in new_vertices: |
416 |
for p2 in vertices_on_face: |
417 |
if p2-q<EPS: |
418 |
q=None |
419 |
raise ValueError,"coast line crosses boundary box vertex. This case is currenrly not supported." |
420 |
if not q==None: |
421 |
vertices.append(q) |
422 |
vertices_on_face.append(q) |
423 |
vertices_on_face.sort(self.region.comparePointsOnFace) |
424 |
index=0 |
425 |
walking_on_water=True |
426 |
l=len(vertices_on_face) |
427 |
while index<l: |
428 |
p1=vertices_on_face[(index+1)%l] |
429 |
p0=vertices_on_face[index] |
430 |
if walking_on_water: |
431 |
segments.append((vertices.index(p0),vertices.index(p1))) |
432 |
walking_on_water=False |
433 |
else: |
434 |
if self.region.isBoxVertex(p0): |
435 |
segments.append((vertices.index(p0),vertices.index(p1))) |
436 |
else: |
437 |
walking_on_water=True |
438 |
index+=1 |
439 |
return EarthTriangulation(vertices,segments,holes,self.region.resolution) |
440 |
|
441 |
def clean(self): |
442 |
""" |
443 |
cleans up the coast line by joining polylines to loops or connecting faces of the region |
444 |
""" |
445 |
# find a poylines that are linked |
446 |
while True: |
447 |
k0=None |
448 |
for pl in self.polylines: |
449 |
if not pl.isLoop(): |
450 |
for k in [0,-1]: |
451 |
for ql in self.polylines: |
452 |
if not (ql==pl or ql.isLoop()): |
453 |
for k2 in [0,-1]: |
454 |
if ql[k2]-pl[k]<EPS: |
455 |
pl0=pl |
456 |
pl1=ql |
457 |
k0=k |
458 |
k1=k2 |
459 |
break |
460 |
if not k0==None: break # ql |
461 |
if not k0==None: break # k |
462 |
if not k0==None: break # pl |
463 |
|
464 |
if k0==None: |
465 |
break |
466 |
else: |
467 |
self.polylines.remove(pl0) |
468 |
self.polylines.remove(pl1) |
469 |
pl0c=pl0.list_of_coordinates |
470 |
pl1c=pl1.list_of_coordinates |
471 |
if k0==0: pl0c.reverse() |
472 |
if k1==-1: pl1c.reverse() |
473 |
pl=Polyline(pl0c+pl1c[1:],pl0.name+" + "+pl1.name) |
474 |
self.append(pl) |
475 |
|
476 |
# find a polyline that is not a loop and has an end or start point not on the face of the region: |
477 |
while True: |
478 |
pl=None |
479 |
k=None |
480 |
for pl2 in self.polylines: |
481 |
if not pl2.isLoop(): |
482 |
pl=pl2 |
483 |
if not self.region.isOnFace(pl[0]): k=0 |
484 |
if not self.region.isOnFace(pl[-1]): k=-1 |
485 |
if not k==None: break |
486 |
if k==None: break |
487 |
self.polylines.remove(pl) |
488 |
d_min=50000. |
489 |
k_min=None |
490 |
for pl2 in self.polylines: |
491 |
if not pl2.isLoop(): |
492 |
for k2 in [0,-1]: |
493 |
if not self.region.isOnFace(pl2[k2]): |
494 |
d2=pl2[k2]-pl[k] |
495 |
if d2<d_min: |
496 |
d_min=d2 |
497 |
pl_min=pl2 |
498 |
k_min=k2 |
499 |
if k_min==None: |
500 |
raise ValueError,"cannot link coastline %s to any other coastline."%pl.name |
501 |
plc=pl.list_of_coordinates |
502 |
plc_min=pl_min.list_of_coordinates |
503 |
if k==0: plc.reverse() |
504 |
if k_min==-1: plc_min.reverse() |
505 |
if d_min<EPS: |
506 |
new_pl=Polyline(plc+plc_min[1:],pl.name+" + "+pl_min.name) |
507 |
else: |
508 |
new_pl=Polyline(plc+plc_min,pl.name+" + "+pl_min.name) |
509 |
self.polylines.remove(pl_min) |
510 |
self.append(new_pl) |
511 |
# give positive orientation to loops: |
512 |
for pl in self.polylines: |
513 |
if pl.isLoop(): pl.givePositiveOrientation() |
514 |
|
515 |
def append(self,polyline=Polyline()): |
516 |
"""append a polyline """ |
517 |
if len(polyline)>1: |
518 |
pl=[] |
519 |
outside_region=None |
520 |
for i in range(len(polyline)): |
521 |
if not self.region.isInRegion(polyline[i]): |
522 |
outside_region=i |
523 |
break |
524 |
# pl.append(self.region.nudgeToFace(polyline[i])) |
525 |
pl.append(polyline[i]) |
526 |
if not outside_region==None: |
527 |
if outside_region==0: |
528 |
for i in range(outside_region+1,len(polyline)): |
529 |
if self.region.isInRegion(polyline[i]): |
530 |
polyline.insert(i,self.region.cutSegment(polyline[i-1],\ |
531 |
polyline[i])) |
532 |
pl1=polyline.split(i)[1] |
533 |
self.append(pl1) |
534 |
break |
535 |
else: |
536 |
# split polyline in two part first one is fully within the region the other starts with |
537 |
# point outside the region |
538 |
c=self.region.cutSegment(polyline[outside_region-1],polyline[outside_region]) |
539 |
i=polyline.insert(outside_region,c) |
540 |
pl0,pl1=polyline.split(i+1) |
541 |
self.append(pl0) |
542 |
self.append(pl1) |
543 |
else: |
544 |
if len(pl)>1: |
545 |
pply= Polyline(pl,polyline.name) |
546 |
self.polylines.append(pply) |
547 |
|
548 |
class EarthTriangulation: |
549 |
GENERATOR="triangle -pqa%g %s" |
550 |
|
551 |
def __init__(self,vertices=[],segments=[],holes=[],resolution=1.): |
552 |
self.fn=os.tempnam() |
553 |
# write triangle input file |
554 |
poly_file=self.fn+".poly" |
555 |
f=open(poly_file,"w") |
556 |
f.writelines("%d %d %d %d\n"%(len(vertices),2,0,0)) |
557 |
for i in range(len(vertices)): f.writelines("%d %e %e\n"%(i,vertices[i].long,vertices[i].lat)) |
558 |
f.writelines("%d %d\n"%(len(segments),0)) |
559 |
for i in range(len(segments)): f.writelines("%d %d %d\n"%(i,segments[i][0],segments[i][1])) |
560 |
f.writelines("%d\n"%(len(holes))) |
561 |
for i in range(len(holes)): f.writelines("%d %e %e\n"%(i,holes[i].long,holes[i].lat)) |
562 |
f.close() |
563 |
# start mesh generator: |
564 |
os.system(self.GENERATOR%(resolution**2,poly_file)) |
565 |
# read mesh file: |
566 |
self.node_coordinates=[] |
567 |
self.node_tags=[] |
568 |
self.node_ids=[] |
569 |
self.triangles_nodes=[] |
570 |
self.triangles_id=[] |
571 |
node_file=open("%s.1.node"%self.fn,"r") |
572 |
nodes=node_file.readline().strip().split() |
573 |
nn=int(nodes[0]) |
574 |
for i in range(nn): |
575 |
nodes=node_file.readline().strip().split() |
576 |
self.node_coordinates.append((float(nodes[1]),float(nodes[2]))) |
577 |
self.node_tags.append(int(nodes[3])) |
578 |
self.node_ids.append(int(nodes[0])) |
579 |
node_file.close() |
580 |
ele_file=open("%s.1.ele"%self.fn,"r") |
581 |
elem=ele_file.readline().strip().split() |
582 |
ne=int(elem[0]) |
583 |
for i in range(ne): |
584 |
elem=ele_file.readline().strip().split() |
585 |
self.triangles_id.append(int(elem[0])) |
586 |
self.triangles_nodes.append((int(elem[1]),int(elem[2]),int(elem[3]))) |
587 |
|
588 |
ele_file.close() |
589 |
# os.remove("%s.1.node"%self.fn) |
590 |
# os.remove("%s.1.ele"%self.fn) |
591 |
# os.remove("%s.ploy"%self.fn) |
592 |
|
593 |
def getFinleyDomain(self): |
594 |
from esys.finley import ReadMesh |
595 |
finley_file=open("%s.msh"%self.fn,"w") |
596 |
finley_file.writelines("%s\n2D-Nodes %d\n"%(self.fn,len(self.node_coordinates))) |
597 |
for i in range(len(self.node_coordinates)): |
598 |
finley_file.writelines("%s %s %s %e %e\n"%(self.node_ids[i],self.node_ids[i],self.node_tags[i],\ |
599 |
self.node_coordinates[i][0],self.node_coordinates[i][1])) |
600 |
|
601 |
finley_file.writelines("Tri3 %d\n"%len(self.triangles_nodes)) |
602 |
for i in range(len(self.triangles_nodes)): |
603 |
finley_file.writelines("%s 0 %s %s %s\n"%(self.triangles_id[i], \ |
604 |
self.triangles_nodes[i][0], \ |
605 |
self.triangles_nodes[i][1], \ |
606 |
self.triangles_nodes[i][2])) |
607 |
finley_file.writelines("Line2 %d\n"%0) |
608 |
finley_file.writelines("Line2_Contact %d\n"%0) |
609 |
finley_file.writelines("Point1 %d\n"%0) |
610 |
finley_file.close() |
611 |
# get the mesh |
612 |
out=ReadMesh("%s.msh"%self.fn) |
613 |
os.remove("%s.msh"%self.fn) |
614 |
return out |
615 |
|
616 |
|
617 |
#================================= |
618 |
# Model interfaces: |
619 |
#================================ |
620 |
class OceanRegionCollector(Model): |
621 |
""" |
622 |
|
623 |
|
624 |
""" |
625 |
def __init__(self,debug=False): |
626 |
Model.__init__(self,debug=debug) |
627 |
self.declareParameter(coastline_source="http://jamboree.esscc.uq.edu.au/cgi-bin/doreen/datafile.txt?west=%%west%%&east=%%east%%&south=%%south%%&north=%%north%%&resolution=%%resolution%%&range360=%%range360%%&river=false&city=false&citytyp=wcity.dat&volcano=false&hotspot=false&shoreline=true&state=false&WSMdata=false&plate=false", |
628 |
bathymetry_source="http://jamboree.esscc.uq.edu.au/cgi-bin/doreen/grdfile.xyz?west=%%west%%&east=%%east%%&south=%%south%%&north=%%north%%&resolution=%%resolution%%", |
629 |
resolution=1., |
630 |
south=0., |
631 |
north=10., |
632 |
east=0., |
633 |
west=20., |
634 |
range360=True, |
635 |
coastline_stream=None, |
636 |
bathymetry_stream=None) |
637 |
|
638 |
|
639 |
def doInitialization(self): |
640 |
""" |
641 |
Initializes the ocean region |
642 |
""" |
643 |
c=self.__mergeParameters(self.coastline_source) |
644 |
b=self.__mergeParameters(self.bathymetry_source) |
645 |
self.coastline_stream=urllib2.urlopen(c) |
646 |
self.bathymetry_stream=urllib2.urlopen(b) |
647 |
|
648 |
def __mergeParameters(self,txt): |
649 |
return txt.replace("%%west%%",str(self.west))\ |
650 |
.replace("%%east%%",str(self.east))\ |
651 |
.replace("%%south%%",str(self.south))\ |
652 |
.replace("%%north%%",str(self.north))\ |
653 |
.replace("%%resolution%%",str(self.resolution)) \ |
654 |
.replace("%%range360%%",str(self.range360).lower()) |
655 |
|
656 |
class Bathymetry(Model): |
657 |
""" |
658 |
generates the bathymetry data within a region on the earth |
659 |
""" |
660 |
def __init__(self,debug=False): |
661 |
Model.__init__(self,debug=debug) |
662 |
self.declareParameter(source="none", |
663 |
bathymetry=1.) |
664 |
|
665 |
def doInitialization(self): |
666 |
""" |
667 |
Initializes the |
668 |
""" |
669 |
if hasattr(self.source,"readline"): |
670 |
f=self.source |
671 |
else: |
672 |
f=open(filename,"r") |
673 |
x_grd_list=[] |
674 |
y_grd_list=[] |
675 |
data_grd_list=[] |
676 |
line=f.readline().strip() |
677 |
while line!="": |
678 |
v=line.split() |
679 |
x_grd_list.append(float(v[0])) |
680 |
y_grd_list.append(float(v[1])) |
681 |
data_grd_list.append(float(v[2])) |
682 |
line=f.readline().strip() |
683 |
self.trace("%s data have been read from %s."%(len(data_grd_list),self.source)) |
684 |
data_grd=numarray.array(data_grd_list) |
685 |
x_grd=numarray.array(x_grd_list) |
686 |
y_grd=numarray.array(y_grd_list) |
687 |
if len(x_grd)<2: |
688 |
raise ValueError,"%s: data base is two small"%str(self) |
689 |
ox=x_grd[0] |
690 |
oy=y_grd[0] |
691 |
diam=max(abs(x_grd[len(x_grd)-1]-ox),abs(y_grd[len(y_grd)-1]-oy)) |
692 |
dx=x_grd[1]-ox |
693 |
nx=1 |
694 |
nx=1 |
695 |
while abs(x_grd[nx]-ox)>EPS*diam: |
696 |
nx+=1 |
697 |
dy=y_grd[nx]-oy |
698 |
ny=len(x_grd)/nx |
699 |
data_grd.resize((ny,nx)) |
700 |
self.bathymetry=GridData(s=[dx,dy],o=[ox,oy],n=[nx,ny],data=data_grd) |
701 |
self.trace("%s x %s grid with %s x %s spacing."%(nx,ny,dx,dy)) |
702 |
|
703 |
|
704 |
class OceanRegion(Model): |
705 |
""" |
706 |
generates the ocean region with a coast line and a bathymetry |
707 |
|
708 |
""" |
709 |
def __init__(self,debug=False): |
710 |
Model.__init__(self,debug=debug) |
711 |
self.declareParameter(domain=None, \ |
712 |
resolution=1., |
713 |
south=0., |
714 |
north=10., |
715 |
east=0., |
716 |
west=20., |
717 |
bathymetry=None, |
718 |
bathymetry_data=None, |
719 |
coastline=None, |
720 |
source="none") |
721 |
|
722 |
def doInitialization(self): |
723 |
""" |
724 |
Initializes the ocean region |
725 |
""" |
726 |
if hasattr(self.source,"readline"): |
727 |
f=self.source |
728 |
data_name=f.geturl() |
729 |
else: |
730 |
f=open(self.source,"r") |
731 |
data_name=self.source |
732 |
|
733 |
segs=[] |
734 |
name="" |
735 |
line=f.readline() |
736 |
while not line=="": |
737 |
line=line.strip() |
738 |
if line[:7]=="#region": |
739 |
data=line[line.index("[")+1:line.index("]")].split(",") |
740 |
reg=RegionOnEarthSurface(PointOnEarthSurface(lat=self.south,long=self.west),PointOnEarthSurface(lat=self.north,long=self.east),self.resolution) |
741 |
self.trace(str(reg)) |
742 |
self.coastline=Coastline(region=reg,name=data_name) |
743 |
elif line.find("Shore Bin")>-1: |
744 |
self.coastline.append(Polyline(segs,name)) |
745 |
name=line[2:] |
746 |
segs=[] |
747 |
if not (line=="" or line[0]=="#" or line[0]==">") : |
748 |
x=line.split() |
749 |
segs.append(PointOnEarthSurface(long=float(x[0]),lat=float(x[1]))) |
750 |
line=f.readline() |
751 |
self.coastline.append(Polyline(segs,name)) |
752 |
d=self.bathymetry_data.interpolate([[self.east,self.south],[self.west,self.south],[self.east,self.north],[self.west,self.north]]) |
753 |
self.domain=self.coastline.makeTriangulation(east_south_is_water=d[0]<=0,west_south_is_water=d[1]<=0,east_north_is_water=d[2]<=0,west_north_is_water=d[3]<=0).getFinleyDomain() |
754 |
self.bathymetry=maximum(-self.bathymetry_data.interpolate(Function(self.domain).getX()),0.) |
755 |
|
756 |
|
757 |
class TsunamiSource(Model): |
758 |
""" |
759 |
defines a wave in Gaussean form between start and end. |
760 |
""" |
761 |
GAMMA=0.05 |
762 |
def __init__(self,debug=False): |
763 |
Model.__init__(self,debug=debug) |
764 |
self.declareParameter(domain=None, |
765 |
start_lat=-10., |
766 |
start_long=110., |
767 |
end_lat=-12., |
768 |
end_long=120., |
769 |
width=5., |
770 |
decay_zone=0.1, |
771 |
amplitude=1., |
772 |
wave_height=1.) |
773 |
|
774 |
def doInitialization(self): |
775 |
""" |
776 |
set initial wave form |
777 |
""" |
778 |
beta=math.sqrt(-math.log(self.GAMMA))/self.decay_zone |
779 |
x=self.domain.getX() |
780 |
x_long=x[0] |
781 |
x_lat=x[1] |
782 |
mid_long=(self.start_long+self.end_long)/2 |
783 |
mid_lat=(self.start_lat+self.end_lat)/2 |
784 |
dist=math.sqrt((mid_long-self.end_long)**2+(mid_lat-self.end_lat)**2) |
785 |
a=(self.start_long-mid_long)/dist |
786 |
b=(self.start_lat-mid_lat)/dist |
787 |
self.trace("source length = %s"%(dist*2.)) |
788 |
x_long_hat= a*(x_long-mid_long)+b*(x_lat-mid_lat) |
789 |
x_lat_hat=-b*(x_long-mid_long)+a*(x_lat-mid_lat) |
790 |
# x_lat-direction |
791 |
s=abs(x_lat_hat)-self.width |
792 |
m=whereNegative(s) |
793 |
f1=(1.-m)*exp(-(s*beta)**2)+m |
794 |
# x_long-direction |
795 |
s=abs(x_long_hat)-dist |
796 |
m=whereNegative(s) |
797 |
f0=(1.-m)*exp(-(s*beta)**2)+m |
798 |
self.wave_height=f1*f0*self.amplitude |
799 |
|
800 |
#==================================================================================================================================================== |
801 |
class TsunamiInDeepWater(Model): |
802 |
""" |
803 |
Runs the deep water tsunami model based on a simplfied form of the shallow water equation. |
804 |
|
805 |
|
806 |
M{d^2 h/dt^2 =div(c grad(h)) } |
807 |
|
808 |
where h is the wave height above sea level, and with H the depth of the water level and g is the gravity constant |
809 |
c=sqrt(g*H). |
810 |
|
811 |
The simulation uses the Verlet scheme. |
812 |
|
813 |
""" |
814 |
def __init__(self,debug=False): |
815 |
Model.__init__(self,debug=debug) |
816 |
self.declareParameter(domain=None, \ |
817 |
wave_height=1., |
818 |
wave_velocity=0., |
819 |
initial_time_step=None, |
820 |
bathymetry=1., |
821 |
safety_factor=1.) |
822 |
|
823 |
def doInitialization(self): |
824 |
""" |
825 |
Initializes the time integartion scheme |
826 |
""" |
827 |
self.__pde=LinearPDE(self.domain) |
828 |
self.__pde.setSolverMethod(self.__pde.LUMPING) |
829 |
self.__pde.setValue(D=1.) |
830 |
self.__c2=RegionOnEarthSurface.GRAVITY*self.bathymetry/(RegionOnEarthSurface.RADIUS*2*numarray.pi/360.)**2 |
831 |
c_max=math.sqrt(Lsup(self.__c2)) |
832 |
self.__dt=self.safety_factor*inf(self.domain.getSize()/(sqrt(self.__c2)+EPS*c_max)) |
833 |
print c_max,inf(self.domain.getSize()) |
834 |
if self.initial_time_step==None: self.initial_time_step=self.__dt |
835 |
self.trace("maximum wave velocity %s m/sec"%c_max) |
836 |
self.trace("Time step size is %s sec"%self.__dt) |
837 |
|
838 |
|
839 |
def getSafeTimeStepSize(self,dt): |
840 |
""" |
841 |
returns new step size |
842 |
|
843 |
@param dt: last time step size used |
844 |
@type dt: C{float} |
845 |
@return: time step size that can savely be used |
846 |
@rtype: C{float} |
847 |
""" |
848 |
return self.__dt |
849 |
|
850 |
def doStepPostprocessing(self,dt): |
851 |
""" |
852 |
perform the time step using the Valet scheme |
853 |
|
854 |
@param dt: time step size to be used |
855 |
@type dt: C{float} |
856 |
""" |
857 |
self.__pde.setValue(X=-self.__c2*grad(self.wave_height)) |
858 |
|
859 |
new_height=self.wave_height+dt*self.wave_velocity+dt*(self.initial_time_step+dt)/2*self.__pde.getSolution() |
860 |
|
861 |
self.wave_velocity=(new_height-self.wave_height)/dt |
862 |
self.wave_height=new_height |
863 |
self.initial_time_step=dt |
864 |
self.trace("Wave height range is %e %e"%(inf(self.wave_height),sup(self.wave_height))) |
865 |
|
866 |
class SurfMovie(Model): |
867 |
""" |
868 |
movie from a wave propagation on the sea |
869 |
|
870 |
@ivar time: current time |
871 |
@ivar bathymetry: scalar data set |
872 |
@ivar wave_height: vector data set |
873 |
@ivar filename: name of the movie file |
874 |
""" |
875 |
def __init__(self,debug=False): |
876 |
Model.__init__(self,debug=debug) |
877 |
self.declareParameter(bathymetry=1., |
878 |
wave_height=1., |
879 |
coastline=None, |
880 |
t=0., |
881 |
dt=1., |
882 |
south=2., |
883 |
north=5., |
884 |
east=3., |
885 |
west=15., |
886 |
max_height=1.0, |
887 |
filename="movie.mpg") |
888 |
|
889 |
def doInitialization(self): |
890 |
""" |
891 |
Initializes the time integration scheme |
892 |
""" |
893 |
self.__fn=os.tempnam()+".xml" |
894 |
self.__frame_name=os.tempnam() |
895 |
self.__next_t=self.dt |
896 |
# self.coastline.getVTK() |
897 |
# self.bathymetry.getVTK() |
898 |
# wndow(south,west,north,east) |
899 |
|
900 |
# set up the movie parameters |
901 |
self.paramsFileString = "REFERENCE_FRAME DECODED\n" |
902 |
self.paramsFileString += "FRAME_RATE 24\n" |
903 |
self.paramsFileString += "OUTPUT %s\n" % self.filename |
904 |
self.paramsFileString += "PATTERN IBBPBBPBB\n" |
905 |
self.paramsFileString += "FORCE_ENCODE_LAST_FRAME\n" |
906 |
self.paramsFileString += "GOP_SIZE 20\n" |
907 |
self.paramsFileString += "BSEARCH_ALG CROSS2\n" |
908 |
self.paramsFileString += "PSEARCH_ALG TWOLEVEL\n" |
909 |
self.paramsFileString += "IQSCALE 10\n" |
910 |
self.paramsFileString += "PQSCALE 11\n" |
911 |
self.paramsFileString += "BQSCALE 16\n" |
912 |
self.paramsFileString += "RANGE 8\n" |
913 |
self.paramsFileString += "SLICES_PER_FRAME 1\n" |
914 |
self.paramsFileString += "BASE_FILE_FORMAT PNM\n" |
915 |
self.paramsFileString += "INPUT_DIR \n" |
916 |
self.paramsFileString += "INPUT_CONVERT *\n" |
917 |
self.paramsFileString += "INPUT\n" |
918 |
|
919 |
self.firstFrame = True |
920 |
|
921 |
self.imageFiles = [] |
922 |
|
923 |
# the bathymmetry colourmap |
924 |
data = [] |
925 |
data.append([-8000, 0, 0, 0]) |
926 |
data.append([-7000, 0, 5, 25]) |
927 |
data.append([-6000, 0, 10, 50]) |
928 |
data.append([-5000, 0, 80, 125]) |
929 |
data.append([-4000, 0, 150, 200]) |
930 |
data.append([-3000, 86, 197, 184]) |
931 |
data.append([-2000, 172, 245, 168]) |
932 |
data.append([-1000, 211, 250, 211]) |
933 |
data.append([0, 16, 48, 16]) |
934 |
data.append([300, 70, 150, 50]) |
935 |
data.append([500, 146, 126, 60]) |
936 |
data.append([1000, 198, 178, 80]) |
937 |
data.append([1250, 250, 230, 100]) |
938 |
data.append([1500, 250, 234, 126]) |
939 |
data.append([1750, 252, 238, 152]) |
940 |
data.append([2000, 252, 243, 177]) |
941 |
data.append([2250, 253, 249, 216]) |
942 |
data.append([2500, 255, 255, 255]) |
943 |
|
944 |
# the amount to scale the data by |
945 |
scale = 255.0 |
946 |
numColours = len(data) |
947 |
|
948 |
# convert the colourmap into something vtk is more happy with |
949 |
height = numarray.zeros(numColours, numarray.Float) |
950 |
red = numarray.zeros(numColours, numarray.Float) |
951 |
green = numarray.zeros(numColours, numarray.Float) |
952 |
blue = numarray.zeros(numColours, numarray.Float) |
953 |
for i in range(numColours): |
954 |
(h, r, g, b) = data[i] |
955 |
height[i] = float(h) |
956 |
red[i] = float(r)/scale |
957 |
green[i] = float(g)/scale |
958 |
blue[i] = float(b)/scale |
959 |
|
960 |
print "Loading bathymmetry data..." |
961 |
# grab the z data |
962 |
bathZData = self.bathymetry.getData() |
963 |
|
964 |
# get the origin |
965 |
bathOrigin = self.bathymetry.getOrigin() |
966 |
# get the delta_x and delta_y |
967 |
bathSpacing = self.bathymetry.getSpacing() |
968 |
|
969 |
# now construct the x and y data from the spacing and the origin |
970 |
numXPoints = bathZData.shape[1] |
971 |
numYPoints = bathZData.shape[0] |
972 |
numPoints = numXPoints*numYPoints |
973 |
|
974 |
bathXData = numarray.zeros(numXPoints, numarray.Float) |
975 |
bathYData = numarray.zeros(numYPoints, numarray.Float) |
976 |
for i in range(numXPoints): |
977 |
bathXData[i] = bathOrigin[0] + i*bathSpacing[0] |
978 |
|
979 |
for i in range(numYPoints): |
980 |
bathYData[i] = bathOrigin[1] + i*bathSpacing[1] |
981 |
|
982 |
# calculate the appropriate window size |
983 |
dataWidth = max(bathXData) - min(bathXData) |
984 |
dataHeight = max(bathYData) - min(bathYData) |
985 |
winHeight = 600 |
986 |
winWidth = int(dataWidth*float(winHeight)/dataHeight) |
987 |
|
988 |
print "Done loading bathymmetry data" |
989 |
|
990 |
### now do the vtk stuff |
991 |
|
992 |
# make sure rendering is offscreen |
993 |
factGraphics = vtk.vtkGraphicsFactory() |
994 |
factGraphics.SetUseMesaClasses(1) |
995 |
|
996 |
factImage = vtk.vtkImagingFactory() |
997 |
factImage.SetUseMesaClasses(1) |
998 |
|
999 |
# make the bathymmetry colourmap |
1000 |
transFunc = vtk.vtkColorTransferFunction() |
1001 |
transFunc.SetColorSpaceToRGB() |
1002 |
for i in range(numColours): |
1003 |
transFunc.AddRGBPoint(height[i], red[i], green[i], blue[i]) |
1004 |
h = height[i] |
1005 |
while i < numColours-1 and h < height[i+1]: |
1006 |
h += 1 |
1007 |
transFunc.AddRGBPoint(h, red[i], green[i], blue[i]) |
1008 |
|
1009 |
# set up the lookup table for the wave data |
1010 |
refLut = vtk.vtkLookupTable() |
1011 |
self.lutTrans = vtk.vtkLookupTable() |
1012 |
refLut.Build() |
1013 |
alpha = 0.7 # alpha channel value |
1014 |
for i in range(256): |
1015 |
(r,g,b,a) = refLut.GetTableValue(255-i) |
1016 |
if g == 1.0 and b < 0.5 and r < 0.5: |
1017 |
self.lutTrans.SetTableValue(i, r, g, b, 0.0) |
1018 |
else: |
1019 |
self.lutTrans.SetTableValue(i, r, g-0.2, b, alpha) |
1020 |
|
1021 |
print "Generating the bathymmetry vtk object..." |
1022 |
|
1023 |
# set up the renderer and the render window |
1024 |
self.ren = vtk.vtkRenderer() |
1025 |
self.renWin = vtk.vtkRenderWindow() |
1026 |
self.renWin.AddRenderer(self.ren) |
1027 |
self.renWin.SetSize(winWidth, winHeight) |
1028 |
self.renWin.OffScreenRenderingOn() |
1029 |
|
1030 |
# make an unstructured grid |
1031 |
bathGrid = vtk.vtkUnstructuredGrid() |
1032 |
|
1033 |
# make the points for the vtk data |
1034 |
bathPoints = vtk.vtkPoints() |
1035 |
bathPoints.SetNumberOfPoints(numPoints) |
1036 |
|
1037 |
# make the vtk bathymmetry data set |
1038 |
bathData = vtk.vtkFloatArray() |
1039 |
bathData.SetNumberOfComponents(1) |
1040 |
bathData.SetNumberOfTuples(numPoints) |
1041 |
|
1042 |
# add the points and data |
1043 |
count = 0 |
1044 |
for i in range(numXPoints): |
1045 |
for j in range(numYPoints): |
1046 |
bathPoints.InsertPoint(count, bathXData[i], bathYData[j], 0.0) |
1047 |
bathData.InsertTuple1(count, bathZData[j,i]) |
1048 |
count += 1 |
1049 |
|
1050 |
# set the data to the grid |
1051 |
bathGrid.SetPoints(bathPoints) |
1052 |
bathGrid.GetPointData().SetScalars(bathData) |
1053 |
|
1054 |
# do a delaunay on the grid |
1055 |
bathDelaunay = vtk.vtkDelaunay2D() |
1056 |
bathDelaunay.SetInput(bathGrid) |
1057 |
bathDelaunay.SetTolerance(0.001) |
1058 |
bathDelaunay.SetAlpha(2.5) |
1059 |
|
1060 |
# set up the mapper |
1061 |
bathMapper = vtk.vtkDataSetMapper() |
1062 |
bathMapper.SetInput(bathDelaunay.GetOutput()) |
1063 |
bathMapper.SetLookupTable(transFunc) |
1064 |
|
1065 |
# set up the actor |
1066 |
bathActor = vtk.vtkActor() |
1067 |
bathActor.SetMapper(bathMapper) |
1068 |
|
1069 |
self.ren.AddActor(bathActor) |
1070 |
|
1071 |
### now add the coastline |
1072 |
print "Loading the coastline data..." |
1073 |
|
1074 |
# make the coastline points |
1075 |
coastPoints = vtk.vtkPoints() |
1076 |
|
1077 |
# make the coastline grid |
1078 |
coastGrid = vtk.vtkUnstructuredGrid() |
1079 |
|
1080 |
# now point the points and lines into the grid |
1081 |
totalCoastPoints = 0 |
1082 |
for polyline in self.coastline.polylines: |
1083 |
numPoints = len(polyline) |
1084 |
coastLine = vtk.vtkPolyLine() |
1085 |
coastLine.GetPointIds().SetNumberOfIds(numPoints) |
1086 |
j = 0 |
1087 |
for point in polyline: |
1088 |
coastLine.GetPointIds().SetId(j, j+totalCoastPoints) |
1089 |
coastPoints.InsertNextPoint(point.long, point.lat, 0.0) |
1090 |
j += 1 |
1091 |
coastGrid.InsertNextCell(coastLine.GetCellType(), |
1092 |
coastLine.GetPointIds()) |
1093 |
totalCoastPoints += numPoints |
1094 |
|
1095 |
coastGrid.SetPoints(coastPoints) |
1096 |
|
1097 |
# make the coast's mapper |
1098 |
coastMapper = vtk.vtkDataSetMapper() |
1099 |
coastMapper.SetInput(coastGrid) |
1100 |
|
1101 |
# make its actor |
1102 |
coastActor = vtk.vtkActor() |
1103 |
coastActor.SetMapper(coastMapper) |
1104 |
coastActor.GetProperty().SetColor(0,0,0) |
1105 |
|
1106 |
# add the actor to the renderer |
1107 |
self.ren.AddActor(coastActor) |
1108 |
|
1109 |
# set up the actor for the wave |
1110 |
self.waveActor = vtk.vtkActor() |
1111 |
|
1112 |
# add the actor to the renderer |
1113 |
self.ren.AddActor(self.waveActor) |
1114 |
|
1115 |
print "Done loading the coastline data" |
1116 |
|
1117 |
def doStepPostprocessing(self, dt): |
1118 |
""" |
1119 |
Does any necessary postprocessing after each step |
1120 |
|
1121 |
@param dt: |
1122 |
""" |
1123 |
if self.t>=self.__next_t: |
1124 |
print self.t,"write ",Lsup(self.wave_height)," to ",self.__fn |
1125 |
saveVTK(self.__fn,h=self.wave_height) |
1126 |
# vtkobj=... |
1127 |
# save(self.__frame_name) |
1128 |
|
1129 |
# make a reader for the data |
1130 |
waveReader = vtk.vtkXMLUnstructuredGridReader() |
1131 |
waveReader.SetFileName(self.__fn) |
1132 |
waveReader.Update() |
1133 |
|
1134 |
# make the grid |
1135 |
waveGrid = waveReader.GetOutput() |
1136 |
waveGrid.Update() |
1137 |
|
1138 |
(zMin, zMax) = waveGrid.GetPointData().GetScalars().GetRange() |
1139 |
print "Wave height range %f - %f" % (zMin, zMax) |
1140 |
|
1141 |
# make a mapper for the grid |
1142 |
waveMapper = vtk.vtkDataSetMapper() |
1143 |
waveMapper.SetInput(waveGrid) |
1144 |
waveMapper.SetLookupTable(self.lutTrans) |
1145 |
waveMapper.SetScalarRange(-self.max_height*0.3, self.max_height*0.3) |
1146 |
|
1147 |
self.waveActor.SetMapper(waveMapper) |
1148 |
|
1149 |
# do stuff here that can only be done on the first frame |
1150 |
if self.firstFrame: |
1151 |
# zoom in a bit |
1152 |
self.ren.GetActiveCamera().Zoom(2.0) |
1153 |
self.firstFrame = False |
1154 |
|
1155 |
# render the window |
1156 |
self.renWin.Render() |
1157 |
|
1158 |
# convert the render window to an image |
1159 |
win2imgFilter = vtk.vtkWindowToImageFilter() |
1160 |
win2imgFilter.SetInput(self.renWin) |
1161 |
|
1162 |
# the image name |
1163 |
imgFname = "%s%f.pnm" % (self.__frame_name, self.__next_t) |
1164 |
|
1165 |
# save the image to file |
1166 |
outWriter = vtk.vtkPNMWriter() |
1167 |
outWriter.SetInput(win2imgFilter.GetOutput()) |
1168 |
outWriter.SetFileName(imgFname) |
1169 |
outWriter.Write() |
1170 |
print "Wrote %s" % imgFname |
1171 |
|
1172 |
# helpful for debugging: |
1173 |
#os.system("display %s" % imgFname) |
1174 |
|
1175 |
self.paramsFileString += "%s\n" % imgFname |
1176 |
self.imageFiles.append(imgFname) |
1177 |
|
1178 |
self.__next_t+=self.dt |
1179 |
|
1180 |
def getSafeTimeStepSize(self,dt): |
1181 |
""" |
1182 |
returns new step size |
1183 |
|
1184 |
@param dt: last time step size used |
1185 |
@type dt: C{float} |
1186 |
@return: time step size that can savely be used |
1187 |
@rtype: C{float} |
1188 |
""" |
1189 |
return self.__next_t-self.t |
1190 |
|
1191 |
def doFinalization(self): |
1192 |
""" |
1193 |
Finalises the visualisation. For instance, makes a movie of the |
1194 |
image files. |
1195 |
""" |
1196 |
# make the movie into self.filename |
1197 |
self.paramsFileString += "END_INPUT\n" |
1198 |
self.paramsFileString += "PIXEL HALF\n" |
1199 |
self.paramsFileString += "ASPECT_RATIO 1\n" |
1200 |
|
1201 |
# write the params string to file |
1202 |
fp = open("%s.params" % self.filename, "w") |
1203 |
fp.write(self.paramsFileString + '\n') |
1204 |
fp.close() |
1205 |
|
1206 |
print "Performing conversion to mpeg" |
1207 |
convertString = "ppmtompeg %s.params" % self.filename |
1208 |
result = os.system(convertString) |
1209 |
if result != 0: |
1210 |
print "An error occurred in mpeg conversion" |
1211 |
|
1212 |
# now clean up the image files |
1213 |
if result == 0: |
1214 |
print "Removing temporary image files" |
1215 |
os.unlink("%s.params" % self.filename) |
1216 |
for fname in self.imageFiles: |
1217 |
os.unlink(fname) |
1218 |
|
1219 |
def main(): |
1220 |
from esys.escript.modelframe import Link,Simulation |
1221 |
from esys.modellib.input import Sequencer |
1222 |
|
1223 |
oc=OceanRegionCollector() |
1224 |
oc.north= 26.7 |
1225 |
oc.west= 102.9 |
1226 |
oc.range360= True |
1227 |
oc.east= 232.6 |
1228 |
oc.resolution= 1. |
1229 |
oc.south= -71.3 |
1230 |
|
1231 |
|
1232 |
b=Bathymetry() |
1233 |
b.source=Link(oc,"bathymetry_stream") |
1234 |
|
1235 |
oreg=OceanRegion() |
1236 |
oreg.source=Link(oc,"coastline_stream") |
1237 |
oreg.resolution=Link(oc,"resolution") |
1238 |
oreg.south=Link(oc,"south") |
1239 |
oreg.north=Link(oc,"north") |
1240 |
oreg.east=Link(oc,"east") |
1241 |
oreg.west=Link(oc,"west") |
1242 |
oreg.bathymetry_data=Link(b,"bathymetry") |
1243 |
|
1244 |
src=TsunamiSource() |
1245 |
src.domain=Link(oreg,"domain") |
1246 |
src.decay_zone= 0.01 |
1247 |
src.end_long= 185. |
1248 |
src.end_lat= -37. |
1249 |
src.width=0.5 |
1250 |
src.start_long= 174. |
1251 |
src.start_lat= -15. |
1252 |
src.amplitude= 3 |
1253 |
|
1254 |
ts=TsunamiInDeepWater() |
1255 |
ts.domain=Link(oreg,"domain") |
1256 |
ts.wave_height=Link(src,"wave_height") |
1257 |
ts.wave_velocity=0. |
1258 |
ts.bathymetry=Link(oreg,"bathymetry") |
1259 |
|
1260 |
sq=Sequencer() |
1261 |
sq.t_end=15000. |
1262 |
|
1263 |
sm=SurfMovie() |
1264 |
sm.bathymetry=Link(b,"bathymetry") |
1265 |
sm.wave_height=Link(ts,"wave_height") |
1266 |
sm.coastline=Link(oreg,"coastline") |
1267 |
sm.t=Link(sq,"t") |
1268 |
sm.filename="mymovie.mpg" |
1269 |
sm.north= 8.7 |
1270 |
sm.west= 138.9 |
1271 |
sm.dt= 50. |
1272 |
sm.east= 196.6 |
1273 |
sm.south= -53.3 |
1274 |
sm.max_height=Link(src,"amplitude") |
1275 |
|
1276 |
s=Simulation([sq,oc,b,oreg,src,ts,sm]) |
1277 |
s.writeXML() |
1278 |
s.run() |
1279 |
|
1280 |
if __name__=="__main__": |
1281 |
from esys.modellib import tsunami |
1282 |
tsunami.main() |
1283 |
|
1284 |
# vim: expandtab shiftwidth=4: |