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