-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathnecofs_model.py
More file actions
344 lines (293 loc) · 10.9 KB
/
necofs_model.py
File metadata and controls
344 lines (293 loc) · 10.9 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
"""
Access data from the NECOFS (New England Coastal Ocean Forecast System) via OPeNDAP
Demonstration using the NetCDF4-Python library to access velocity data
from a triangular grid ocean model (FVCOM) via OPeNDAP, specifying the
desired URL, time, layer and lat/lon region of interest. The
resulting plot of forecast velocity vectors over color-shaded
bathymetry is useful for a variety of recreational and scientific
purposes.
NECOFS (Northeastern Coastal Ocean Forecast System) is run by groups
at the University of Massachusetts Dartmouth and the Woods Hole
Oceanographic Institution, led by Drs. C. Chen, R. C. Beardsley,
G. Cowles and B. Rothschild. Funding is provided to run the model by
the NOAA-led Integrated Ocean Observing System and the State of
Massachusetts.
NECOFS is a coupled numerical model that uses nested weather models, a
coastal ocean circulation model, and a wave model. The ocean model is
a volume-mesh model with horizontal resolution that is finer in
complicated regions. It is layered (not depth-averaged) and includes
the effects of tides, winds, and varying water densities caused by
temperature and salinity changes.
* Model description: http://fvcom.smast.umassd.edu/research_projects/NECOFS/model_system.html
* THREDDS server with other forecast and archive products: http://www.smast.umassd.edu:8080/thredds/catalog.html
"""
# standard library imports
import datetime as dt
import weakref
# Major library imports
import numpy as np
import matplotlib
# We want matplotlib to use a QT backend
matplotlib.use('Qt4Agg')
from matplotlib.figure import Figure
import matplotlib.animation as animation
import matplotlib.pyplot as plt
import matplotlib.tri as Tri
import netCDF4
# Enthought library imports
from traits.api import (HasTraits, List, Str, Int, Instance, Array, Property, Bool,
Range, Any, Float, cached_property, on_trait_change)
from enthought.traits.ui.api import View, Item
import enaml
from enaml.qt.qt_application import QtApplication
from enaml.stdlib.sessions import SimpleSession
from PySide.QtGui import QVBoxLayout, QWidget
#from pyface.qt import QtGui, QtCore
from matplotlib.backends.backend_qt4agg import (FigureCanvasQTAgg as FigureCanvas,
NavigationToolbar2QTAgg as NavigationToolbar)
from matplotlib.figure import Figure
from enthought.traits.ui.qt4.editor import Editor
from enthought.traits.ui.qt4.basic_editor_factory import BasicEditorFactory
VERBOSE = False
class _MPLFigureEditor(Editor):
scrollable = True
def init(self, parent):
self.control = self._create_canvas(parent)
self.set_tooltip()
def update_editor(self):
pass
def _create_canvas(self, parent):
""" Create the matplotlib canvas. """
main_frame = QWidget()
mpl_canvas = FigureCanvas(self.value)
mpl_toolbar = NavigationToolbar(mpl_canvas, main_frame)
vbox = QVBoxLayout()
vbox.setSpacing(0)
vbox.addWidget(mpl_toolbar)
vbox.addWidget(mpl_canvas)
main_frame.setLayout(vbox)
return main_frame
class MPLFigureEditor(BasicEditorFactory):
klass = _MPLFigureEditor
class OceanModel(HasTraits):
#url = Str('http://www.smast.umassd.edu:8080/thredds/dodsC/FVCOM/NECOFS/Forecasts/NECOFS_FVCOM_OCEAN_MASSBAY_FORECAST.nc')
url = Str('10step_nc4.nc')
running = Bool(False)
nc = Any()
keys = List
ilayer = Int(0)
start = Property(Instance(dt.datetime), depends_on='itime')
daystr = Property(Str, depends_on='itime')
itime = Int
time_var = Any()
time_steps = Property(Int)
lat = Array
lon = Array
latc = Array
lonc = Array
tri = Any()
nv = Array
h = Array
u = Property(Array)
v = Property(Array)
levels = Array
# west = Float(-70.97)
# east = Float(-70.82)
# south = Float(42.25)
# north = Float(42.35)
west = Float(-71.2)
east = Float(-69.4)
south = Float(40.95)
north = Float(42.1)
ax = Property(List) #, depends_on='west, east, south, north')
ind = Property(Array, depends_on='ax')
idv = Property(Array, depends_on='ind')
figure = Instance(Figure)
quiver = Any()
ani = Instance(animation.FuncAnimation)
verbose = Bool(VERBOSE)
# TraitsUI view
view = View(Item('figure', editor=MPLFigureEditor(),
show_label=False),
Item('itime'),
Item('running'),
Item('north'),
Item('south'),
Item('west'),
Item('east'),
width=800,
height=800,
resizable=True)
def _nc_default(self):
""" Open DAP
"""
return netCDF4.Dataset(self.url)
def _keys_default(self):
self.keys = self.nc.variables.keys()
def _set_start(self, start):
""" Desired time slice
"""
self.itime = netCDF4.date2index(self.start, self.time_var, select='nearest')
def _get_start(self):
return netCDF4.num2date(self.time_var[self.itime],
self.time_var.units)
def _time_var_default(self):
return self.nc.variables['time']
def _get_time_steps(self):
#self.itime.maximum = len(self.time_var)
#return self.itime.maximum
return len(self.time_var)
def _lat_default(self):
""" Latitude of grid nodes
"""
return self.nc.variables['lat'][:]
def _lon_default(self):
""" Longitude of grid nodes
"""
return self.nc.variables['lon'][:]
def _latc_default(self):
""" Latitude of cell centers (depth)
"""
return self.nc.variables['latc'][:]
def _lonc_default(self):
""" Longitude of cell centers (depth)
"""
return self.nc.variables['lonc'][:]
def _nv_default(self):
""" Get Connectivity array
"""
return self.nc.variables['nv'][:].T - 1
def _h_default(self):
""" Get depth
"""
return self.nc.variables['h'][:]
def _get_daystr(self):
""" String representation of current time stamp
"""
dtime = netCDF4.num2date(self.time_var[self.itime],
self.time_var.units)
return dtime.strftime('%Y-%b-%d %H:%M')
def _tri_default(self):
return Tri.Triangulation(self.lon, self.lat, triangles=self.nv)
def _get_u(self):
# get current at layer [0 = surface, -1 = bottom]
return self.nc.variables['u'][self.itime, self.ilayer, :]
def _get_v(self):
return self.nc.variables['v'][self.itime, self.ilayer, :]
@on_trait_change('itime, idv')
def update_quiver(self):
""" set the quiver plot data without redrawing
"""
try:
self.quiver.set_UVC(self.u[self.idv], self.v[self.idv])
self.figure.canvas.draw()
except Exception, e:
print e
def _levels_default(self):
""" depth contours to plot
"""
return np.arange(-32, 2, 1)
def _set_ax(self, ax):
""" region to plot
"""
self.west, self.east, self.south, self.north = ax
#import pudb; pudb.set_trace()
self.update_plot()
def _get_ax(self):
""" region to plot
"""
return [self.west, self.east, self.south, self.north]
#@cached_property
def _get_ind(self):
""" find velocity points in bounding box
"""
if self.verbose:
print 'get ind'
return np.argwhere((self.lonc >= self.west) &
(self.lonc <= self.east) &
(self.latc >= self.south) &
(self.latc <= self.north))
#@cached_property
def _get_idv(self):
if self.verbose:
print 'get idv'
subsample = max(1, len(self.ind) // 3000)
return self.ind[::subsample]
@on_trait_change('ax')
def update_plot(self, *args, **kwargs):
if self.verbose:
print 'updating quiver plot'
self.axis_and_quiver()
self.figure.canvas.draw()
def _animate(self, *args):
if self.running:
if self.itime >= self.time_steps - 1:
self.itime = 0
else:
self.itime += 1
return self.quiver,
def _running_changed(self):
if self.running:
if self.verbose:
print 'start'
self.ani = animation.FuncAnimation(self.figure,
self._animate,
interval=10,
blit=True,
repeat=False)
else:
if self.verbose:
print 'stop'
self.running = False
self.ani._stop()
del self.ani
def axis_and_quiver(self, axis=None):
""" Set (or change) axis limits and draw quiver plot in that region
"""
if axis is None and self.figure is not None:
axis = self.figure.axes[0]
axis.patch.set_facecolor('0.5')
#cbar = plt.colorbar()
#cbar.set_label('Water Depth (m)', rotation=-90)
if self.quiver is not None:
try:
self.quiver.remove()
del self.quiver
# XXX: is this necessary?
except AttributeError, TypeError:
pass
try:
self.quiver = axis.quiver(self.lonc[self.idv],
self.latc[self.idv],
self.u[self.idv],
self.v[self.idv],
scale=20, units='width', width=0.001)
axis.quiverkey(self.quiver, 0.92, 0.08, 0.50, '0.5 m/s', labelpos='W')
except Exception, e:
print e
#plt.title('NECOFS Velocity, Layer %d, %s' % (self.ilayer, self.daystr))
def _figure_default(self):
# tricontourf plot of water depth with vectors on top
if self.verbose:
print 'get figure'
fig1 = plt.figure(figsize=(12, 10))
ax1 = fig1.add_subplot(111, aspect=(1.0/np.cos(np.mean(self.lat)*np.pi/180.0)))
ax1.tricontourf(self.tri, -self.h,
levels=self.levels,
shading='faceted',
cmap=plt.cm.gist_earth)
ax1.axis(self.ax)
self.axis_and_quiver(axis=ax1)
ax1.callbacks.connect('xlim_changed', self.on_pan_or_zoom)
ax1.callbacks.connect('ylim_changed', self.on_pan_or_zoom)
return fig1
def on_pan_or_zoom(self, axis):
west, east = axis.xaxis.get_view_interval()
south, north = axis.yaxis.get_view_interval()
if not np.allclose(np.array(self.ax), np.array([west, east, south, north])):
if self.verbose:
print [west, east, south, north], self.ax
self.ax = [west, east, south, north]
if __name__ == '__main__':
model = OceanModel()
model.configure_traits()