mitgcm
Analysis of MITgcm output using python
core.py
Go to the documentation of this file.
1 """!
2 The main file with the class definitions
3 
4 Core
5 ==============
6 
7 This file contains all of the classes for the module. It has the base MITgcm_Simulation class, and all of the subclasses for different types of fields. Each class has methods for taking derivatives and doing useful manipulations. It also has some functions that might be of use.
8 """
9 
10 import numpy as np
11 import netCDF4
12 import copy
13 import os
14 import numba
15 import matplotlib.pyplot as plt
16 import glob
17 import collections
18 import warnings
19 
21  """!The simulation class is the main class of this package, and an instance of this class is a model object. All fields are associated with the model object - either directly (it is a dict), or indirectly through one of its subobjects (which are also dicts).
22 
23  """
24  def __init__(self,output_dir,grid_netcdf_filename,EOS_type='linear',g=9.81,
25  ntiles_x=1,ntiles_y=1,grid_type='cartesian'):
26  """!Instantiate an MITgcm model instance.
27 
28  ----
29  # Parameters #
30 
31  * output_dir - the location in which the grid netcdf file is, and the rest of the data can be found
32  * grid_netcdf_filename - name of the grid file
33  * EOS_type - the equation of state used. 'linear' is the only supported equation of state at the moment
34  * g - gravity
35  * ntiles_x - number of tiles in the x direction (nPx in MITgcm speak)
36  * ntiles_y - number of tiles in the y direction (nPy in MITgcm speak)
37  * grid_type - cartesian or polar. Fancy grids like cubed sphere aren't supported at the moment.
38  """
39 
40  os.chdir(output_dir)
41  self['output_dir'] = output_dir
42 
43  self.grid = Grid(grid_netcdf_filename)
44  self.grid['grid_type'] = grid_type
45 
46 
47  self['g'] = g
48  self['EOS_type'] = EOS_type
49  if EOS_type != 'linear':
50  raise NotImplementedError('Only linear equation of state is currently supported')
51 
52  self['ntiles_x'] = ntiles_x
53  self['ntiles_y'] = ntiles_y
54 
55  def load_field(self,model_instance, netcdf_filename,variable,time_level=-1,
56  field_name=None,grid_loc='T',single_file=False):
57  """!Load a model field from NetCDF output. This function associates the field with the object it is called on.
58 
59  time_level can be an integer or an array of integers. If it's an array, then multiple time levels will be returned as a higher dimensional array.
60 
61  netcdf_filename can be a string with shell wildcards - they'll be expanded and all of the files loaded. BUT, this is intended as a way to take a quick look at the model. I would *strongly* recommend using something like "gluemncbig" to join the multiple tiles into one file before doing proper analysis."""
62  if field_name == None:
63  field_name = variable
64 
65  file_list = glob.glob(netcdf_filename)
66 
67 
68  self[field_name] = self.load_from_file(model_instance,file_list,variable,time_level,grid_loc,single_file)
69  return
70 
71  def load_from_file(self,model_instance, file_list,variable,time_level,grid_loc,single_file):
72  """!Internal function to pull the data from the file(s). It is called by "load_field", and probably shouldn't be used independently."""
73 
74  # remove overlapping regions from tiles
75  if grid_loc == 'T':
76  clip_x = None
77  clip_y = None
78  elif grid_loc == 'W':
79  clip_x = None
80  clip_y = None
81  elif grid_loc == 'V':
82  clip_x = None
83  clip_y = -1
84  elif grid_loc == 'U':
85  clip_x = -1
86  clip_y = None
87  elif grid_loc == 'Zeta':
88  clip_x = -1
89  clip_y = -1
90  else:
91  print "grid_loc variable not set correctly"
92  return
93 
94  if not single_file:
95  if len(file_list) != model_instance['ntiles_x']*model_instance['ntiles_y']:
96  raise RuntimeError("The number of tiles found (" + str(len(file_list)) + ") isn't equal to ntiles_x*ntiles_y (" + str(model_instance['ntiles_x']*model_instance['ntiles_y']) + "). You should check this. \n Aborting import of " + variable)
97  return
98 
99  data = {}
100 
101  if time_level == 'All':
102  print 'Loading all available time levels in ' + str(variable) + '. This could take a while.'
103 
104  for files in file_list:
105  netcdf_file = netCDF4.Dataset(files)
106  index = files.find('.t')
107  tile = files[index+2:-3]
108  if variable in netcdf_file.variables.keys():
109  data[tile] = netcdf_file.variables[variable][:]
110  else:
111  netcdf_file.close()
112  raise KeyError(variable, 'not in', files, '- aborting import')
113  return
114 
115  netcdf_file.close()
116 
117  else:
118  for files in file_list:
119  netcdf_file = netCDF4.Dataset(files)
120  index = files.find('.t')
121  tile = files[index+2:-3]
122  if variable in netcdf_file.variables.keys():
123  data[tile] = netcdf_file.variables[variable][time_level,...]
124  else:
125  netcdf_file.close()
126  raise KeyError(variable, 'not in', files, '- aborting import')
127  return
128  netcdf_file.close()
129 
130 
131  if len(file_list) == 1:
132  loaded_field = data[tile]
133 
134  else:
135  data2 = collections.OrderedDict(sorted(data.items(), key=lambda t: t[0]))
136  del data #since the fields can be big, might as well get rid of the duplicate.
137 
138  # for tile in data2.keys():
139  # data2[tile] = 0*data2[tile] + float(tile)
140 
141  strip_x = {}
142  tiles = data2.keys()
143 
144  if model_instance['ntiles_x'] > 1:
145 
146  for i in xrange(0,model_instance['ntiles_y']):
147  strip_x[i] = np.concatenate([data2[tiles[n]][...,:clip_x]
148  for n in xrange(i*model_instance['ntiles_x'],
149  (i+1)*model_instance['ntiles_x'] - 1) ],axis=-1)
150 
151  strip_x[i] = np.concatenate((strip_x[i],data2[tiles[n+1]][...,:,:]),axis=-1)
152  else:
153  strip_x = data2
154 
155  strip_x_keys = strip_x.keys()
156 
157  if model_instance['ntiles_y'] > 1:
158 
159  loaded_field = np.concatenate([strip_x[strip_x_keys[n]][...,:clip_y,:]
160  for n in xrange(0,len(strip_x_keys)-1)],axis=-2)
161 
162  loaded_field = np.concatenate((loaded_field,strip_x[strip_x_keys[n+1]][...]),axis=-2)
163  else:
164  loaded_field = strip_x[strip_x_keys[0]]
165 
166 
167  return loaded_field
168 
169 
170  def __add__(self,other):
171  """!A method that allows model objects to be added together. It does element wise addition for each of the fields."""
172  me = copy.deepcopy(self)
173  for key, value in me.__dict__.iteritems():
174  for key1, value1 in other.__dict__.iteritems():
175  if key == key1:
176  setattr(me, key, value + value1)
177  return me
178 
179  def __div__(self,other):
180  """!A method that allows model objects to be divided by floating point numbers. Each field is divided by a floating point number."""
181  me = copy.deepcopy(self)
182  for key, value in me.__dict__.iteritems():
183  setattr(me, key, value/float(other))
184  return me
185 
186  def __mul__(self, other):
187  """! A method that allows model objects to be multiplied by floating point numbers. Each field is multiplied by a floating point number."""
188  me = copy.deepcopy(self)
189  for key, value in me.__dict__.iteritems():
190  setattr(me, key, value * float(other))
191  return me
192 
193  def __rmul__(self, other):
194  """! A method that allows model objects to be multiplied by floating point numbers. Each field is multiplied by a floating point number."""
195  me = copy.deepcopy(self)
196  for key, value in me.__dict__.iteritems():
197  setattr(me, key, value * float(other))
198  return me
199 
201  """! This is the class for all fields on zonal velocity points."""
202 
203  def __init__(self,model_instance,netcdf_filename,variable,time_level=-1,empty=False,field_name=None,single_file=False):
204  if empty:
205  pass
206  else:
207  self.load_field(model_instance,netcdf_filename,variable,time_level,field_name,grid_loc='U',single_file=False)
208 
209  return
210 
211  def load_field(self,model_instance, netcdf_filename,variable,time_level=-1,field_name=None,grid_loc='U',single_file=False):
212  """!Load a model field from NetCDF output. This function associates the field with the object it is called on.
213 
214  time_level can be an integer or an array of integers. If it's an array, then multiple time levels will be returned as a higher dimensional array.
215 
216  netcdf_filename can be a string with shell wildcards - they'll be expanded and all of the files loaded. BUT, this is intended as a way to take a quick look at the model. I would *strongly* recommend using something like "gluemncbig" to join the multiple tiles into one file before doing proper analysis."""
217  if field_name == None:
218  field_name = variable
219 
220  file_list = glob.glob(netcdf_filename)
221 
222  self[field_name] = self.load_from_file(model_instance,file_list,variable,time_level,grid_loc,single_file)
223  return
224 
225  ### Derivatives of model fields
226  def take_d_dx(self,model_instance,input_field = 'UVEL',output_field='dU_dx'):
227  """! Take the x derivative of the field given on u-points, using the spacings in grid object.
228 
229  Performs centred second-order differencing everywhere except next to boundaries. First order is
230  used there (meaning the gradients at the boundary are evaluated half a grid point away from where
231  they should be)."""
232 
233  if input_field in self:
234  np.seterr(divide='ignore')
235  UVEL = self[input_field]
236  dU_dx = np.zeros((UVEL.shape))
237 
238  for i in xrange(1,UVEL.shape[2]-2):
239  dU_dx[:,:,i] = np.nan_to_num(model_instance.grid['wet_mask_U'][:,:,i]*
240  (model_instance.grid['wet_mask_U'][:,:,i+1]*UVEL[:,:,i+1] +
241  (1 - model_instance.grid['wet_mask_U'][:,:,i+1])*UVEL[:,:,i] -
242  (1 - model_instance.grid['wet_mask_U'][:,:,i-1])*UVEL[:,:,i] -
243  model_instance.grid['wet_mask_U'][:,:,i-1]*UVEL[:,:,i-1])/(
244  model_instance.grid['wet_mask_U'][:,:,i-1]*model_instance.grid['dxF'][:,i-1] +
245  model_instance.grid['wet_mask_U'][:,:,i+1]*model_instance.grid['dxF'][:,i]))
246  i = 0
247  dU_dx[:,:,i] = (UVEL[:,:,i+1] - UVEL[:,:,i])/(model_instance.grid['dxF'][:,i])
248  i = UVEL.shape[2]-1
249  dU_dx[:,:,i] = (UVEL[:,:,i] - UVEL[:,:,i-1])/(model_instance.grid['dxF'][:,i-1])
250 
251  self[output_field] = dU_dx
252  else:
253  raise KeyError('Chosen input array ' + str(input_field) + ' is not defined')
254 
255  return
256 
257 
258 
259 
260  def take_d_dy(self,model_instance,input_field = 'UVEL',output_field='dU_dy'):
261  """! Take the y derivative of the field on u points, using the spacings provided.
262 
263 
264  Performs centred second-order differencing everywhere except next to boundaries. First order is
265  used there (meaning the gradients at the boundary are evaluated half a grid point away from where
266  they should be)."""
267 
268  if input_field in self:
269  np.seterr(divide='ignore')
270  UVEL = self[input_field]
271  dU_dy = np.zeros((UVEL.shape))
272 
273  for j in xrange(1,UVEL.shape[1]-1):
274  dU_dy[:,j,1:] = (model_instance.grid['wet_mask_U'][:,j,1:]*
275  (model_instance.grid['wet_mask_U'][:,j+1,1:]*UVEL[:,j+1,1:] +
276  (1 - model_instance.grid['wet_mask_U'][:,j+1,1:])*UVEL[:,j,1:] -
277  # if j+1 point is not fluid, use j point as the starting
278  # location for the derivative
279  (1 - model_instance.grid['wet_mask_U'][:,j-1,1:])*UVEL[:,j,1:] -
280  model_instance.grid['wet_mask_U'][:,j-1,1:]*UVEL[:,j-1,1:])/(
281  model_instance.grid['wet_mask_U'][:,j-1,1:]*model_instance.grid['dyC'][j,:] +
282  model_instance.grid['wet_mask_U'][:,j+1,1:]*model_instance.grid['dyC'][j+1,:]))
283 
284  j = 0
285  dU_dy[:,j,1:] = (model_instance.grid['wet_mask_U'][:,j,1:]*(UVEL[:,j+1,1:] -
286  UVEL[:,j,1:])/(model_instance.grid['dyC'][j+1,:]))
287  j = UVEL.shape[1]-1
288  dU_dy[:,j,1:] = (model_instance.grid['wet_mask_U'][:,j,1:]*(UVEL[:,j,1:] -
289  UVEL[:,j-1,1:])/(model_instance.grid['dyC'][j,:]))
290 
291  self[output_field] = np.nan_to_num(dU_dy)
292  else:
293  raise KeyError('Chosen input array ' + str(input_field) + ' is not defined')
294 
295  return
296 
297 
298 
299  def take_d_dz(self,model_instance,input_field = 'UVEL',output_field='dU_dz'):
300  """! Take the z derivative of the field given on u-points, using the spacings in grid object.
301 
302  Performs centred second-order differencing everywhere except next to boundaries. First order is
303  used there (meaning the gradients at the boundary are evaluated half a grid point away from where
304  they should be)."""
305 
306  if input_field in self:
307  np.seterr(divide='ignore')
308  UVEL = self[input_field]
309  d_dz = np.zeros((UVEL.shape))
310 
311  for k in xrange(1,UVEL.shape[0]-1):
312  # model doesn't have overhangs, so only need this to work with fluid above and bathymetry below.
313  d_dz[k,:,:] = np.nan_to_num(model_instance.grid['wet_mask_U'][k,:,:]*(UVEL[k-1,:,:] -
314  (1-model_instance.grid['wet_mask_U'][k+1,:,:])*UVEL[k,:,:]-
315  model_instance.grid['wet_mask_U'][k+1,:,:]*UVEL[k+1,:,:])/(model_instance.grid['drC'][k] +
316  model_instance.grid['wet_mask_U'][k+1,:,:]*model_instance.grid['drC'][k+1]))
317 
318  k = 0
319  d_dz[k,:,:] = (UVEL[k,:,:] - UVEL[k+1,:,:])/(model_instance.grid['drC'][k+1])
320  k = UVEL.shape[0]-1
321  d_dz[k,:,:] = (UVEL[k-1,:,:] - UVEL[k,:,:])/(model_instance.grid['drC'][k])
322 
323  self[output_field] = d_dz
324  else:
325  raise KeyError('Chosen input array ' + str(input_field) + ' is not defined')
326  return
327 
328  def shift_to_tracer(self,field_name):
329  """! Shift the array on to the corresponding tracer point."""
330  shifted = (self[field_name][...,0:-1] + self[field_name][...,1:])/2
331 
332  return shifted
333 
335  """! This is the class for all fields on meridional velocity points."""
336 
337  def __init__(self,model_instance,netcdf_filename,variable,time_level=-1,empty=False,field_name=None,single_file=False):
338  """!Instantiate a field on the meridional velocity points."""
339  if empty:
340  pass
341  else:
342  self.load_field(model_instance,netcdf_filename,variable,time_level,field_name,grid_loc='V',single_file=False)
343 
344  return
345 
346  def load_field(self,model_instance, netcdf_filename,variable,time_level=-1,field_name=None,grid_loc='V',single_file=False):
347  """!Load a model field from NetCDF output. This function associates the field with the object it is called on.
348 
349  time_level can be an integer or an array of integers. If it's an array, then multiple time levels will be returned as a higher dimensional array.
350 
351  netcdf_filename can be a string with shell wildcards - they'll be expanded and all of the files loaded. BUT, this is intended as a way to take a quick look at the model. I would *strongly* recommend using something like "gluemncbig" to join the multiple tiles into one file before doing proper analysis."""
352  if field_name == None:
353  field_name = variable
354 
355  file_list = glob.glob(netcdf_filename)
356 
357  self[field_name] = self.load_from_file(model_instance,file_list,variable,time_level,grid_loc,single_file)
358  return
359 
360 
361  def take_d_dx(self,model_instance,input_field = 'VVEL',output_field='dV_dx'):
362  """!Take the x derivative of the field on v points using the spacings in model_instance.grid object.
363 
364  This function can be daisy-chained to get higher order derivatives.
365 
366  Performs centred second-order differencing everywhere except next to boundaries. First order is
367  used there (meaning the gradients at the boundary are evaluated half a grid point away from where
368  they should be)."""
369 
370  if input_field in self:
371  np.seterr(divide='ignore')
372  VVEL = self[input_field]
373  dV_dx = np.zeros((VVEL.shape))
374 
375  for i in xrange(1,VVEL.shape[2]-1):
376  dV_dx[:,1:,i] = np.nan_to_num(model_instance.grid['wet_mask_V'][:,1:,i]*
377  (model_instance.grid['wet_mask_V'][:,1:,i+1]*VVEL[:,1:,i+1] +
378  (1 - model_instance.grid['wet_mask_V'][:,1:,i+1])*VVEL[:,1:,i] -
379  (1 - model_instance.grid['wet_mask_V'][:,1:,i-1])*VVEL[:,1:,i] -
380  model_instance.grid['wet_mask_V'][:,1:,i-1]*VVEL[:,1:,i-1])/(
381  model_instance.grid['wet_mask_V'][:,1:,i-1]*model_instance.grid['dxC'][:,i] +
382  model_instance.grid['wet_mask_V'][:,1:,i+1]*model_instance.grid['dxC'][:,i+1]))
383  i = 0
384  dV_dx[:,1:,i] = (VVEL[:,1:,i+1] - VVEL[:,1:,i])/(model_instance.grid['dxC'][:,i+1])
385  i = VVEL.shape[2]-1
386  dV_dx[:,1:,i] = (VVEL[:,1:,i] - VVEL[:,1:,i-1])/(model_instance.grid['dxC'][:,i])
387 
388  self[output_field] = dV_dx
389  else:
390  raise KeyError('Chosen input array ' + str(input_field) + ' is not defined')
391  return
392 
393  def take_d_dy(self,model_instance,input_field = 'VVEL',output_field='dV_dy'):
394  """! Take the y derivative of the field given on v-points, using the spacings in grid object.
395 
396  Performs centred second-order differencing everywhere except next to boundaries. First order is
397  used there (meaning the gradients at the boundary are evaluated half a grid point away from where
398  they should be)."""
399 
400  if input_field in self:
401  np.seterr(divide='ignore')
402  VVEL = self[input_field]
403  dV_dy = np.zeros((VVEL.shape))
404 
405  for j in xrange(1,VVEL.shape[1]-2):
406  dV_dy[:,j,:] = np.nan_to_num(model_instance.grid['wet_mask_V'][:,j,:]*(
407  model_instance.grid['wet_mask_V'][:,j+1,:]*VVEL[:,j+1,:] +
408  (1 - model_instance.grid['wet_mask_V'][:,j+1,:])*VVEL[:,j,:] -
409  (1 - model_instance.grid['wet_mask_V'][:,j-1,:])*VVEL[:,j,:] -
410  model_instance.grid['wet_mask_V'][:,j-1,:]*VVEL[:,j-1,:])/(
411  model_instance.grid['wet_mask_V'][:,j-1,:]*model_instance.grid['dyF'][j-1,:] +
412  model_instance.grid['wet_mask_V'][:,j+1,:]*model_instance.grid['dyF'][j,:]))
413  j = 0
414  dV_dy[:,j,:] = (VVEL[:,j+1,:] - VVEL[:,j,:])/(model_instance.grid['dyF'][j,:])
415  j = VVEL.shape[1]-1
416  dV_dy[:,j,:] = (VVEL[:,j,:] - VVEL[:,j-1,:])/(model_instance.grid['dyF'][j-1,:])
417 
418  self[output_field] = dV_dy
419  else:
420  raise KeyError('Chosen input array ' + str(input_field) + ' is not defined')
421  return
422 
423 
424  def take_d_dz(self,model_instance,input_field = 'VVEL',output_field='dV_dz'):
425  """! Take the z derivative of the field given on v-points, using the spacings in grid object.
426 
427  Performs centred second-order differencing everywhere except next to boundaries. First order is
428  used there (meaning the gradients at the boundary are evaluated half a grid point away from where
429  they should be)."""
430 
431  if input_field in self:
432  np.seterr(divide='ignore')
433  VVEL = self[input_field]
434  d_dz = np.zeros((VVEL.shape))
435 
436  for k in xrange(1,VVEL.shape[0]-1):
437  # model doesn't have overhangs, so only need this to work with fluid above and bathymetry below.
438  d_dz[k,:,:] = np.nan_to_num(model_instance.grid['wet_mask_V'][k,:,:]*(VVEL[k-1,:,:] -
439  (1-model_instance.grid['wet_mask_V'][k+1,:,:])*VVEL[k,:,:]-
440  model_instance.grid['wet_mask_V'][k+1,:,:]*VVEL[k+1,:,:])/(model_instance.grid['drC'][k] +
441  model_instance.grid['wet_mask_V'][k+1,:,:]*model_instance.grid['drC'][k+1]))
442 
443  k = 0
444  d_dz[k,:,:] = (VVEL[k,:,:] - VVEL[k+1,:,:])/(model_instance.grid['drC'][k+1])
445  k = VVEL.shape[0]-1
446  d_dz[k,:,:] = (VVEL[k-1,:,:] - VVEL[k,:,:])/(model_instance.grid['drC'][k])
447 
448  self[output_field] = d_dz
449  else:
450  raise KeyError('Chosen input array ' + str(input_field) + ' is not defined')
451  return
452 
453  def shift_to_tracer(self,field_name):
454  """! Shift the array on to the corresponding tracer point."""
455  shifted = (self[field_name][...,0:-1,:] + self[field_name][...,1:,:])/2
456 
457  return shifted
458 
460  """! This is the class for all fields on vertical velocity points."""
461 
462  def __init__(self,model_instance,netcdf_filename,variable,time_level=-1,empty=False,field_name=None,single_file=False):
463  if empty:
464  pass
465  else:
466  self.load_field(model_instance,netcdf_filename,variable,time_level,field_name,grid_loc='W',single_file=False)
467 
468  return
469 
470  def load_field(self,model_instance, netcdf_filename,variable,time_level=-1,field_name=None,grid_loc='W',single_file=False):
471  """!Load a model field from NetCDF output. This function associates the field with the object it is called on.
472 
473  time_level can be an integer or an array of integers. If it's an array, then multiple time levels will be returned as a higher dimensional array."""
474  if field_name == None:
475  field_name = variable
476 
477 
478  file_list = glob.glob(netcdf_filename)
479 
480 
481  loaded_field = self.load_from_file(model_instance,file_list,variable,time_level,grid_loc,single_file)
482 
483 
484  if len(loaded_field.shape) == 4:
485  self[field_name] = np.append(loaded_field,np.zeros((loaded_field.shape[0],1,loaded_field.shape[-2],loaded_field.shape[-1])),axis=1)
486  else:
487  self[field_name] = np.append(loaded_field,np.zeros((1,loaded_field.shape[-2],loaded_field.shape[-1])),axis=0)
488 
489 
490  return
491 
492 
493  def take_d_dx(self,model_instance,input_field = 'WVEL',output_field='dW_dx'):
494  """!Take the x derivative of the field on w points, using spacings in grid object.
495 
496  Performs centred second-order differencing everywhere except next to boundaries. First order is
497  used there (meaning the gradients at the boundary are evaluated half a grid point away from where
498  they should be)."""
499 
500  if input_field in self:
501  np.seterr(divide='ignore')
502  WVEL = self[input_field]
503  d_dx = np.zeros((WVEL.shape))
504 
505  for i in xrange(1,WVEL.shape[2]-1):
506  d_dx[:,:,i] = np.nan_to_num(model_instance.grid['wet_mask_W'][:,:,i]*
507  (model_instance.grid['wet_mask_W'][:,:,i+1]*WVEL[:,:,i+1] +
508  (1 - model_instance.grid['wet_mask_W'][:,:,i+1])*WVEL[:,:,i] -
509  (1 - model_instance.grid['wet_mask_W'][:,:,i-1])*WVEL[:,:,i] -
510  model_instance.grid['wet_mask_W'][:,:,i-1]*WVEL[:,:,i-1])/(
511  model_instance.grid['wet_mask_W'][:,:,i-1]*model_instance.grid['dxC'][:,i] +
512  model_instance.grid['wet_mask_W'][:,:,i+1]*model_instance.grid['dxC'][:,i+1]))
513  i = 0
514  d_dx[:,:,i] = (WVEL[:,:,i+1] - WVEL[:,:,i])/(model_instance.grid['dxC'][:,i+1])
515  i = WVEL.shape[2]-1
516  d_dx[:,:,i] = (WVEL[:,:,i] - WVEL[:,:,i-1])/(model_instance.grid['dxC'][:,i])
517 
518  self[output_field] = d_dx
519 
520  else:
521  raise KeyError('Chosen input array ' + str(input_field) + ' is not defined')
522  return
523 
524 
525  def take_d_dy(self,model_instance,input_field = 'WVEL',output_field='dW_dy'):
526  """!Take the y derivative of the field on w points, using spacings in grid object.
527 
528  Performs centred second-order differencing everywhere except next to boundaries. First order is
529  used there (meaning the gradients at the boundary are evaluated half a grid point away from where
530  they should be)."""
531 
532  if input_field in self:
533  np.seterr(divide='ignore')
534  WVEL = self[input_field]
535  dW_dy = np.zeros((WVEL.shape))
536 
537  for j in xrange(1,WVEL.shape[2]-1):
538  dW_dy[:,j,:] = np.nan_to_num(model_instance.grid['wet_mask_W'][:,j,:]*
539  (model_instance.grid['wet_mask_W'][:,j+1,:]*WVEL[:,j+1,:] +
540  (1 - model_instance.grid['wet_mask_W'][:,j+1,:])*WVEL[:,j,:] -
541  (1 - model_instance.grid['wet_mask_W'][:,j-1,:])*WVEL[:,j,:] -
542  model_instance.grid['wet_mask_W'][:,j-1,:]*WVEL[:,j-1,:])/(
543  model_instance.grid['wet_mask_W'][:,j-1,:]*model_instance.grid['dyC'][j,:] +
544  model_instance.grid['wet_mask_W'][:,j+1,:]*model_instance.grid['dyC'][j+1,:]))
545  j = 0
546  dW_dy[:,j,:] = (WVEL[:,j+1,:] - WVEL[:,j,:])/(model_instance.grid['dyC'][j+1,:])
547  j = WVEL.shape[1]-1
548  dW_dy[:,j,:] = (WVEL[:,j,:] - WVEL[:,j-1,:])/(model_instance.grid['dyC'][j,:])
549 
550  self[output_field] = dW_dy
551 
552  else:
553  raise KeyError('Chosen input array ' + str(input_field) + ' is not defined')
554  return
555 
556  def take_d_dz(self,model_instance,input_field = 'WVEL',output_field='dW_dz'):
557  """! Take the z derivative of the field given on w-points, using the spacings in grid object.
558 
559  Performs centred second-order differencing everywhere except next to boundaries. First order is
560  used there (meaning the gradients at the boundary are evaluated half a grid point away from where
561  they should be)."""
562 
563  if input_field in self:
564  np.seterr(divide='ignore')
565  WVEL = self[input_field]
566  dWVEL_dz = np.zeros((WVEL.shape))
567 
568  for k in xrange(1,WVEL.shape[0]-2):
569  dWVEL_dz[k,:,:] = np.nan_to_num(model_instance.grid['wet_mask_TH'][k,:,:]*(WVEL[k-1,:,:] -
570  (1-model_instance.grid['wet_mask_TH'][k+1,:,:])*WVEL[k,:,:]-
571  model_instance.grid['wet_mask_TH'][k+1,:,:]*WVEL[k+1,:,:])/
572  (model_instance.grid['drF'][k-1]+
573  model_instance.grid['wet_mask_TH'][k+1,:,:]*model_instance.grid['drF'][k]))
574 
575  k = 0
576  dWVEL_dz[k,:,:] = (WVEL[k,:,:] - WVEL[k+1,:,:])/(model_instance.grid['drF'][k])
577  k = WVEL.shape[0]-2
578  dWVEL_dz[k,:,:] = (WVEL[k-1,:,:] - WVEL[k,:,:])/(model_instance.grid['drF'][k-1])
579  k = WVEL.shape[0]-1
580  dWVEL_dz[k,:,:] = (WVEL[k-1,:,:] - WVEL[k,:,:])/(model_instance.grid['drF'][k-1])
581 
582  self[output_field] = dWVEL_dz
583  else:
584  raise KeyError('Chosen input array ' + str(input_field) + ' is not defined')
585  return
586 
587  def shift_to_tracer(self,field_name):
588  """! Shift the array on to the corresponding tracer point."""
589  shifted = (self[field_name][...,0:-1,:,:] + self[field_name][...,1:,:,:])/2
590 
591  return shifted
592 
594  """!This is the base class for all model fields on the tracer points. It includes definitions for taking derivatives."""
595  def __init__(self,model_instance,netcdf_filename,variable,time_level=-1,empty=False,field_name=None,single_file=False):
596  if empty:
597  pass
598  else:
599  self.load_field(model_instance,netcdf_filename,variable,time_level,field_name,grid_loc='T',single_file=False)
600  return
601 
602 
603  def load_field(self,model_instance, netcdf_filename,variable,time_level=-1,field_name=None,grid_loc='T',single_file=False):
604  """!Load a model field from NetCDF output. This function associates the field with the object it is called on.
605 
606  time_level can be an integer or an array of integers. If it's an array, then multiple time levels will be returned as a higher dimensional array.
607 
608  netcdf_filename can be a string with shell wildcards - they'll be expanded and all of the files loaded. BUT, this is intended as a way to take a quick look at the model. I would *strongly* recommend using something like "gluemncbig" to join the multiple tiles into one file before doing proper analysis."""
609  if field_name == None:
610  field_name = variable
611 
612  file_list = glob.glob(netcdf_filename)
613 
614  self[field_name] = self.load_from_file(model_instance,file_list,variable,time_level,grid_loc,single_file)
615  return
616 
617 
618  def take_d_dx(self,model_instance,input_field = 'RHO',output_field='dRHO_dx'):
619  """!Take the x derivative of the field on tracer points, using spacings in grid object.
620 
621  Performs centred second-order differencing everywhere except next to boundaries. First order is
622  used there (meaning the gradients at the boundary are evaluated half a grid point away from where
623  they should be)."""
624 
625  if input_field in self:
626  np.seterr(divide='ignore')
627  rho = self[input_field]
628 
629  d_dx = (self.numerics_take_d_dx(rho[:],model_instance.grid['wet_mask_TH'][:],
630  model_instance.grid['dxC'][:],))
631  self[output_field] = np.nan_to_num(d_dx)
632  else:
633  raise KeyError('Chosen input array ' + str(input_field) + ' is not defined')
634  return
635 
636  def numerics_take_d_dx(self,rho,wet_mask_TH,dxC):
637 
638  d_dx = np.zeros((rho.shape))
639 
640  i = 0
641  d_dx[:,:,i] = (rho[:,:,i+1] - rho[:,:,i])/(dxC[:,i+1])
642  i = rho.shape[2]-1
643  d_dx[:,:,i] = (rho[:,:,i] - rho[:,:,i-1])/(dxC[:,i])
644 
645  for i in xrange(1,rho.shape[2]-1):
646  d_dx[:,:,i] = (wet_mask_TH[:,:,i]*
647  (wet_mask_TH[:,:,i+1]*rho[:,:,i+1] +
648  (1 - wet_mask_TH[:,:,i+1])*rho[:,:,i] -
649  (1 - wet_mask_TH[:,:,i-1])*rho[:,:,i] -
650  wet_mask_TH[:,:,i-1]*rho[:,:,i-1])/(
651  wet_mask_TH[:,:,i-1]*dxC[:,i] +
652  wet_mask_TH[:,:,i+1]*dxC[:,i+1]))
653  return d_dx
654 
655 
656  def take_d_dy(self,model_instance,input_field = 'RHO',output_field='dRHO_dy'):
657  """!Take the y derivative of the field on tracer points, using spacings in grid object.
658 
659  Performs centred second-order differencing everywhere except next to boundaries. First order is
660  used there (meaning the gradients at the boundary are evaluated half a grid point away from where
661  they should be)."""
662 
663  if input_field in self:
664  #np.seterr(divide='ignore')
665 
666  self[output_field] = np.nan_to_num(self.numerics_take_d_dy(self[input_field][:],
667  model_instance.grid['wet_mask_TH'][:],model_instance.grid['dyC'][:]))
668  else:
669  raise KeyError('Chosen input array ' + str(input_field) + ' is not defined')
670  return
671 
672  def numerics_take_d_dy(self,rho,wet_mask_TH,dyC):
673  """!The numerical bit of taking the y derivative. This has been separated out so that it can be accelerated with numba, but that isn't working yet."""
674 
675  d_dy = np.zeros((rho.shape))
676 
677  j = 0
678  d_dy[:,j,:] = (rho[:,j+1,:] - rho[:,j,:])/(dyC[j+1,:])
679  j = rho.shape[1]-1
680  d_dy[:,j,:] = (rho[:,j,:] - rho[:,j-1,:])/(dyC[j,:])
681 
682  for j in xrange(1,rho.shape[1]-1):
683  d_dy[:,j,:] = (wet_mask_TH[:,j,:]*
684  (wet_mask_TH[:,j+1,:]*rho[:,j+1,:] +
685  (1 - wet_mask_TH[:,j+1,:])*rho[:,j,:] -
686  (1 - wet_mask_TH[:,j-1,:])*rho[:,j,:] -
687  wet_mask_TH[:,j-1,:]*rho[:,j-1,:])/(
688  wet_mask_TH[:,j-1,:]*dyC[j,:] +
689  wet_mask_TH[:,j+1,:]*dyC[j+1,:]))
690  return d_dy
691 
692 
693  def take_d_dz(self,model_instance,input_field = 'RHO',output_field='dRHO_dz'):
694  """! Take the z derivative of the field given on tracer-points, using the spacings in grid object.
695 
696  Performs centred second-order differencing everywhere except next to boundaries. First order is
697  used there (meaning the gradients at the boundary are evaluated half a grid point away from where
698  they should be)."""
699 
700  if input_field in self:
701  np.seterr(divide='ignore')
702  rho = self[input_field]
703  d_dz = np.zeros((rho.shape))
704 
705  for k in xrange(1,rho.shape[0]-1):
706  # model doesn't have overhangs, so only need this to work with fluid above and bathymetry below.
707  d_dz[k,:,:] = (model_instance.grid['wet_mask_TH'][k,:,:]*(rho[k-1,:,:] -
708  (1-model_instance.grid['wet_mask_TH'][k+1,:,:])*rho[k,:,:]-
709  model_instance.grid['wet_mask_TH'][k+1,:,:]*rho[k+1,:,:])/(model_instance.grid['drC'][k] +
710  model_instance.grid['wet_mask_TH'][k+1,:,:]*model_instance.grid['drC'][k+1]))
711 
712  k = 0
713  d_dz[k,:,:] = (rho[k,:,:] - rho[k+1,:,:])/(model_instance.grid['drC'][k+1])
714  k = rho.shape[0]-1
715  d_dz[k,:,:] = (rho[k-1,:,:] - rho[k,:,:])/(model_instance.grid['drC'][k])
716 
717  self[output_field] = np.nan_to_num(d_dz)
718  else:
719  raise KeyError('Chosen input array ' + str(input_field) + ' is not defined')
720  return
721 
722 
723 
725  """!A class for fields on vorticity points."""
726  def __init__(self,model_instance,netcdf_filename,variable,time_level=-1,empty=False,field_name=None,single_file=False):
727  if empty:
728  pass
729  else:
730  self.load_field(model_instance,netcdf_filename,variable,time_level,field_name,grid_loc='Zeta',single_file=False)
731 
732  return
733 
734  def load_field(self,model_instance, netcdf_filename,variable,time_level=-1,field_name=None,grid_loc='Zeta',single_file=False):
735  """!Load a model field from NetCDF output. This function associates the field with the object it is called on.
736 
737  time_level can be an integer or an array of integers. If it's an array, then multiple time levels will be returned as a higher dimensional array.
738 
739  netcdf_filename can be a string with shell wildcards - they'll be expanded and all of the files loaded. BUT, this is intended as a way to take a quick look at the model. I would *strongly* recommend using something like "gluemncbig" to join the multiple tiles into one file before doing proper analysis."""
740  if field_name == None:
741  field_name = variable
742 
743  file_list = glob.glob(netcdf_filename)
744 
745  self[field_name] = self.load_from_file(model_instance,file_list,variable,time_level,grid_loc,single_file)
746  return
747 
748 
749  def take_d_dx(self,model_instance,input_field = 'momVort3',output_field='dmomVort3_dx'):
750  """! Take the x derivative of the field given on vorticity-points, using the spacings in grid object.
751 
752  Performs centred second-order differencing everywhere except next to boundaries. First order is
753  used there (meaning the gradients at the boundary are evaluated half a grid point away from where
754  they should be)."""
755 
756  if input_field in self:
757  np.seterr(divide='ignore')
758  momVort3 = self[input_field]
759  dmomVort3_dx = np.zeros((momVort3.shape))
760 
761  for i in xrange(1,momVort3.shape[2]-2):
762  dmomVort3_dx[:,:,i] = np.nan_to_num(model_instance.grid['wet_mask_U'][:,:,i]*
763  (model_instance.grid['wet_mask_U'][:,:,i+1]*momVort3[:,:,i+1] +
764  (1 - model_instance.grid['wet_mask_U'][:,:,i+1])*momVort3[:,:,i] -
765  (1 - model_instance.grid['wet_mask_U'][:,:,i-1])*momVort3[:,:,i] -
766  model_instance.grid['wet_mask_U'][:,:,i-1]*momVort3[:,:,i-1])/(
767  model_instance.grid['wet_mask_U'][:,:,i-1]*model_instance.grid['dxF'][:,i-1] +
768  model_instance.grid['wet_mask_U'][:,:,i+1]*model_instance.grid['dxF'][:,i]))
769  i = 0
770  dmomVort3_dx[:,:,i] = (momVort3[:,:,i+1] - momVort3[:,:,i])/(model_instance.grid['dxF'][:,i])
771  i = momVort3.shape[2]-1
772  dmomVort3_dx[:,:,i] = (momVort3[:,:,i] - momVort3[:,:,i-1])/(model_instance.grid['dxF'][:,i-1])
773 
774  self[output_field] = dmomVort3_dx
775  else:
776  raise KeyError('Chosen input array ' + str(input_field) + ' is not defined')
777 
778  return
779 
780  def take_d_dy(self,model_instance,input_field = 'momVort3',output_field='dmomVort3_dy'):
781  """! Take the y derivative of the field given on vorticity-points, using the spacings in grid object.
782 
783  Performs centred second-order differencing everywhere except next to boundaries. First order is
784  used there (meaning the gradients at the boundary are evaluated half a grid point away from where
785  they should be)."""
786 
787  if input_field in self:
788  np.seterr(divide='ignore')
789  momVort3 = self[input_field]
790  dmomVort3_dy = np.zeros((momVort3.shape))
791 
792  for j in xrange(1,momVort3.shape[1]-2):
793  dmomVort3_dy[:,j,:] = np.nan_to_num(model_instance.grid['wet_mask_V'][:,j,:]*(
794  model_instance.grid['wet_mask_V'][:,j+1,:]*momVort3[:,j+1,:] +
795  (1 - model_instance.grid['wet_mask_V'][:,j+1,:])*momVort3[:,j,:] -
796  (1 - model_instance.grid['wet_mask_V'][:,j-1,:])*momVort3[:,j,:] -
797  model_instance.grid['wet_mask_V'][:,j-1,:]*momVort3[:,j-1,:])/(
798  model_instance.grid['wet_mask_V'][:,j-1,:]*model_instance.grid['dyF'][j-1,:] +
799  model_instance.grid['wet_mask_V'][:,j+1,:]*model_instance.grid['dyF'][j,:]))
800  j = 0
801  dmomVort3_dy[:,j,:] = (momVort3[:,j+1,:] - momVort3[:,j,:])/(model_instance.grid['dyF'][j,:])
802  j = momVort3.shape[1]-1
803  dmomVort3_dy[:,j,:] = (momVort3[:,j,:] - momVort3[:,j-1,:])/(model_instance.grid['dyF'][j-1,:])
804 
805  self[output_field] = dmomVort3_dy
806  else:
807  raise KeyError('Chosen input array ' + str(input_field) + ' is not defined')
808  return
809 
810  def take_d_dz(self,model_instance,input_field = 'momVort3',output_field='dmomVort3_dz'):
811  """! Take the z derivative of the field given on vorticity-points, using the spacings in grid object.
812 
813  Performs centred second-order differencing everywhere except next to boundaries. First order is
814  used there (meaning the gradients at the boundary are evaluated half a grid point away from where
815  they should be)."""
816 
817  if input_field in self:
818  np.seterr(divide='ignore')
819  momVort3 = self[input_field]
820  d_dz = np.zeros((momVort3.shape))
821 
822  for k in xrange(1,momVort3.shape[0]-1):
823  # model doesn't have overhangs, so only need this to work with fluid above and bathymetry below.
824  d_dz[k,:,:] = np.nan_to_num(model_instance.grid['wet_mask_TH'][k,:,:]*(momVort3[k-1,:,:] -
825  (1-model_instance.grid['wet_mask_TH'][k+1,:,:])*momVort3[k,:,:]-
826  model_instance.grid['wet_mask_TH'][k+1,:,:]*momVort3[k+1,:,:])/(model_instance.grid['drC'][k] +
827  model_instance.grid['wet_mask_TH'][k+1,:,:]*model_instance.grid['drC'][k+1]))
828 
829  k = 0
830  d_dz[k,:,:] = (momVort3[k,:,:] - momVort3[k+1,:,:])/(model_instance.grid['drC'][k+1])
831  k = momVort3.shape[0]-1
832  d_dz[k,:,:] = (momVort3[k-1,:,:] - momVort3[k,:,:])/(model_instance.grid['drC'][k])
833 
834  self[output_field] = d_dz
835  else:
836  raise KeyError('Chosen input array ' + str(input_field) + ' is not defined')
837  return
838 
839 
840  def shift_to_tracer(self,field_name):
841  """! Shift the array on to the corresponding tracer point."""
842  shifted = (self[field_name][...,0:-1] + self[field_name][...,1:])/2
843  shifted = (shifted[...,0:-1,:] + shifted[...,1:,:])/2
844 
845  return shifted
846 
847 
849  """!This defines the class for the grid object.
850 
851  Currently requires a single grid file.
852 
853  This object holds all the information about the grid on which the simulation was run. It also holds masks for selecting only the boundary values of fields on the tracer points.
854  """
855 
856  def __init__(self, grid_netcdf_filename):
857  """!Define a single object that has all of the grid variables tucked away in it.
858  Each of the variables pulled directly from the netcdf file still has the
859  original description attached to it. The 2D and 3D arrays do not.
860 
861  Variables imported are:
862  * rAw: r-face area at U point
863  * rAs: r-face area at V point
864  * rA: r-face area at cell center
865  * HFacW: vertical fraction of open cell at West face
866  * HFacS: vertical fraction of open cell at South face
867  * HFacC: vertical fraction of open cell at cell center
868  * X: longitude of cell center
869  * Xp1: longitude of cell corner
870  * dxF: x cell face separation
871  * dxC: x cell center separation
872  * dxV: x v-velocity separation
873  * Y: latitude of cell center
874  * Yp1: latitude of cell corner
875  * dyU: y u-velocity separation
876  * dyC: y cell center separation
877  * dyF: y cell face separation
878  * Z: vertical coordinate of cell center
879  * Zl: vertical coordinate of upper cell interface
880  * Zu: vertical coordinate of lower cell interface
881  * drC: r cell center separation
882  * drF: r cell face separation
883  * fCoriG: Coriolis f at cell corner
884  * fCori: Coriolis f at cell center
885 
886  Variables computed and stored are:
887  * wet_mask_V : a 3d array that is one if the point is in the fluid, zero otherwise.
888  * wet_mask_U
889  * wet_mask_TH
890  * wet_mask_W
891 
892  **Cell volumes can be calculated and stored with the compute_cell_volume function**
893  * cell_volume
894 
895  **These boundary masks can be computed with the compute_boundary_masks function**
896  * west_mask : a 3d array that is one if the point has a boundary to the west
897  * east_mask
898  * south_mask
899  * north_mask
900  * bottom_mask
901 
902  -----
903  Notes: uses glob to expand wildcards in the file name. BUT, it will only use the first file that matches.
904 
905  """
906  # * (Z_y,Y_z)
907  # * (X_y,Y_x)
908  # * (Z_x,X_z)
909  # * (Z_3d,Y_3d,X_3d)
910 
911  # * (DZF,DYF, DXF): a 3d array of dxF,dyF and dzF
912 
913  grid_netcdf_file_list = glob.glob(grid_netcdf_filename)
914 
915  if not grid_netcdf_file_list:
916  raise RuntimeError('Grid file not found.')
917 
918  #print grid_netcdf_file_list
919  grid_netcdf_file = netCDF4.Dataset(grid_netcdf_file_list[0])
920 
921  self['rAw'] = grid_netcdf_file.variables['rAw']
922  self['rAs'] = grid_netcdf_file.variables['rAs']
923  self['rA'] = grid_netcdf_file.variables['rA']
924  self['HFacW'] = grid_netcdf_file.variables['HFacW']
925  self['HFacS'] = grid_netcdf_file.variables['HFacS']
926  self['HFacC'] = grid_netcdf_file.variables['HFacC']
927  self['X']= grid_netcdf_file.variables['X']
928  self['Xp1'] = grid_netcdf_file.variables['Xp1']
929  self['dxF'] = grid_netcdf_file.variables['dxF']
930  self['dxC'] = grid_netcdf_file.variables['dxC']
931  self['dxV'] = grid_netcdf_file.variables['dxV']
932  self['Y'] = grid_netcdf_file.variables['Y']
933  self['Yp1'] = grid_netcdf_file.variables['Yp1']
934  self['dyU'] = grid_netcdf_file.variables['dyU']
935  self['dyC'] = grid_netcdf_file.variables['dyC']
936  self['dyF'] = grid_netcdf_file.variables['dyF']
937  self['Z'] = grid_netcdf_file.variables['Z']
938  self['Zl'] = grid_netcdf_file.variables['Zl']
939  self['Zu'] = grid_netcdf_file.variables['Zu']
940  self['drC'] = grid_netcdf_file.variables['drC']
941  self['drF'] = grid_netcdf_file.variables['drF']
942  self['fCoriG'] = grid_netcdf_file.variables['fCoriG']
943  self['fCori'] = grid_netcdf_file.variables['fCori']
944 
945  #(self['Z_y'],self['Y_z']) = np.meshgrid(self['Z'][:],self['Y'][:],indexing='ij')
946  #(self['X_y'],self['Y_x']) = np.meshgrid(self['X'],self['Y'][:],indexing='ij')
947  #(self['Z_x'],self['X_z']) = np.meshgrid(self['Z'],self['X'][:],indexing='ij')
948  #(self['Z_3d'],self['Y_3d'],self['X_3d']) = np.meshgrid(self['Z'][:],self['Y'][:],self['X'][:],indexing='ij')
949 
950  #(self['DZF'],self['DYF'], self['DXF']) = np.meshgrid(self['drF'][:],self['dyF'][0,:],self['dxF'][:,0],indexing='ij')
951 
952  self['wet_mask_V'] = np.ones((np.shape(self['HFacS'][:])))
953  self['wet_mask_V'][self['HFacS'][:] == 0.] = 0.
954  self['wet_mask_U'] = np.ones((np.shape(self['HFacW'][:])))
955  self['wet_mask_U'][self['HFacW'][:] == 0.] = 0.
956  self['wet_mask_TH'] = np.ones((np.shape(self['HFacC'][:])))
957  self['wet_mask_TH'][self['HFacC'][:] == 0.] = 0.
958  self['wet_mask_W'] = np.append(self['wet_mask_TH'],np.ones((1,len(self['Y'][:]),len(self['X'][:]))),axis=0)
959 
960 
961 
962  #self.compute_masks(self['wet_mask_TH'][:])
963  # this isn't automatically done, since it's expensive, and only needed when computing boundary fluxes.
964 
965  return
966 
967  @numba.jit
969  """!This function does the computationally heavy job of looping through each dimension and creating masks that are one if the boundary is next to the grid point in the specified direction. This function is accelerated by numba, making it about 100 times faster."""
970 
971  wet_mask_TH = self['wet_mask_TH'][:]
972 
973  west_mask = np.zeros((wet_mask_TH.shape))
974  east_mask = np.zeros((wet_mask_TH.shape))
975  south_mask = np.zeros((wet_mask_TH.shape))
976  north_mask = np.zeros((wet_mask_TH.shape))
977  bottom_mask = np.zeros((wet_mask_TH.shape))
978 
979  # Find the fluxes through the boundary of the domain
980  for k in xrange(0,wet_mask_TH.shape[0]):
981  for j in xrange(0,wet_mask_TH.shape[1]):
982  for i in xrange(0,wet_mask_TH.shape[2]):
983  # find points with boundary to the west.
984  if wet_mask_TH[k,j,i] - wet_mask_TH[k,j,i-1] == 1:
985  west_mask[k,j,i] = 1
986 
987 
988  # find the eastern boundary points. Negative sign is to be consistent about fluxes into the domain.
989  if wet_mask_TH[k,j,i-1] - wet_mask_TH[k,j,i] == 1:
990  east_mask[k,j,i-1] = 1
991 
992 
993  # find the southern boundary points
994  if wet_mask_TH[k,j,i] - wet_mask_TH[k,j-1,i] == 1:
995  south_mask[k,j,i] = 1
996 
997 
998  # find the northern boundary points
999  if wet_mask_TH[k,j-1,i] - wet_mask_TH[k,j,i] == 1:
1000  north_mask[k,j,i-1] = 1
1001 
1002 
1003  # Fluxes through the bottom
1004  if wet_mask_TH[k-1,j,i] - wet_mask_TH[k,j,i] == 1:
1005  bottom_mask[k-1,j,i] = 1
1006 
1007  self['west_mask'] = west_mask
1008  self['east_mask'] = east_mask
1009  self['south_mask'] = south_mask
1010  self['north_mask'] = north_mask
1011  self['bottom_mask'] = bottom_mask
1012  return
1013 
1015  """Compute a 3D array that contains the volume of each cell."""
1016 
1017  self['U_cell_volume'] = copy.deepcopy(self['rAw'][:].reshape((1,self['rAw'][:].shape[0],self['rAw'][:].shape[1]))*
1018  self['drF'][:].reshape((self['drF'][:].shape[0],1,1)))
1019 
1020  self['V_cell_volume'] = copy.deepcopy(self['rAs'][:].reshape((1,self['rAs'][:].shape[0],self['rAs'][:].shape[1]))*
1021  self['drF'][:].reshape((self['drF'][:].shape[0],1,1)))
1022 
1023  self['T_cell_volume'] = copy.deepcopy(self['dxF'][:]*self['dyF'][:]*
1024  self['drF'][:].reshape((len(self['drF'][:]),1,1)))
1025 
1026 
1027 
1029  """!A tracer point field that contains methods for density fields. Only linear equation of state with temperature variations is supported at the moment.
1030 
1031  The linear equation of state is given by
1032 
1033  \f[
1034  \rho = \rho_{nil} (-\alpha_{T} (\theta - \theta_{0}) + \beta_{S} (S - S_{0})) + \rho_{nil}
1035  \f]
1036  where \f$ \rho_{nil} \f$ is the reference density, \f$ \alpha_{T} \f$ is the thermal expansion coefficient, \f$ \beta_{S} \f$ is the haline contraction coefficient, \f$ \theta \f$ and \f$ \beta \f$ are the temperature and salinity fields, and subscript zeros denote reference values.
1037  """
1038 
1039  def __init__(self,model_instance,Talpha=2e-4,Sbeta=0,RhoNil=1035,cp=4000,
1040  temp_field='THETA',salt_field='S',density_field='RHO',
1041  Tref=20,Sref=30,empty=False):
1042  if empty:
1043  pass
1044  else:
1045  self['cp'] = cp
1046  self['Talpha'] = Talpha
1047  self['Sbeta'] = Sbeta
1048  self['RhoNil'] = RhoNil
1049  self.calculate_density(model_instance,Talpha,Sbeta,RhoNil,cp,
1050  temp_field,salt_field,density_field,Tref,Sref)
1051 
1052 
1053  def calculate_density(self,model_instance,Talpha=2e-4,Sbeta=0,RhoNil=1035,cp=4000,
1054  temp_field='THETA',salt_field='S',density_field='RHO',Tref=20,Sref=30):
1055  """!Cacluate Density field given temperature and salinity fields, using the linear equation of state."""
1056  if model_instance['EOS_type'] == 'linear':
1057  if Sbeta == 0:
1058  self[density_field] = (RhoNil*( -Talpha*(model_instance.temperature[temp_field] - Tref))
1059  + RhoNil)
1060  else:
1061  self[density_field] = (RhoNil*( Sbeta*(model_instance.salinity[salt_field] - Sref) - Talpha*(model_instance.temperature[temp_field] - Tref))
1062  + RhoNil)
1063  print 'Warning: Linear equation of state with salinity is currently untested. Proceed with caution.'
1064  else:
1065  raise NotImplementedError('Only linear EOS supported at the moment. Sorry.')
1066 
1067  def calculate_TotRhoTend(self,model_instance):
1068  """!Calculate time rate of change of the Density field from the temperature tendency and the linear equation of state. Returned as 'TotRhoTend' associated with the density object.
1069 
1070  Differentiating the linear equation of state with respect to temperature, and assuming \f$ \beta_{S} \f$ equals zero, gives
1071  \f[
1072  \frac{\partial \rho}{\partial t} = - \rho_{nil} \alpha_{T} \frac{\partial \theta}{\partial t}
1073  \f]
1074  """
1075  if model_instance['EOS_type'] == 'linear':
1076  if self['Sbeta'] == 0:
1077  self['TotRhoTend'] = (-self['RhoNil']*self['Talpha']*model_instance.temperature['TOTTTEND'])
1078  else:
1079  raise NotImplementedError('This function only supports temperature variations at the moment. Sorry.')
1080  else:
1081  raise NotImplementedError('Only liner EOS supported at the moment.')
1082 
1083 
1084 
1086  """!The Bernoulli field, evaluated from velocity, Pressure and Density.
1087  \f[
1088  BP = \frac{P}{\rho_{0}} + g z + \frac{u^{2} + v^{2}}{2}
1089  \f]
1090  """
1091  def __init__(self,model_instance,density_field='RHO',UVEL_field='UVEL',VVEL_field='VVEL'):
1092  BP_pressure = ((model_instance.pressure['P'][:,:,:]/model_instance.density['RhoNil'] +
1093  model_instance.grid['Z'][:].reshape((len(model_instance.grid['Z'][:]),1,1))*model_instance['g']))
1094 
1095  BP_u = (model_instance.zonal_velocity[UVEL_field][:,:,1:]*model_instance.zonal_velocity[UVEL_field][:,:,1:] +
1096  model_instance.zonal_velocity[UVEL_field][:,:,:-1]*model_instance.zonal_velocity[UVEL_field][:,:,:-1])/2
1097 
1098  BP_v = (model_instance.meridional_velocity[VVEL_field][:,1:,:]*model_instance.meridional_velocity[VVEL_field][:,1:,:] +
1099  model_instance.meridional_velocity[VVEL_field][:,:-1,:]*model_instance.meridional_velocity[VVEL_field][:,:-1,:])/2
1100 
1101  self['BP'] = BP_pressure + (BP_u + BP_v)/2
1102  # decomposition into three components suggested by Craig.
1103 
1105  """!Calculates the pressure field from the density field using the hydrostatic approximation."""
1106  def __init__(self,model_instance,density_field='RHO',ETAN_field='ETAN',empty=False):
1107 
1108  if empty:
1109  pass
1110  else:
1111  # derive the hydrostatic pressure
1112  delta_P = np.zeros((np.shape(model_instance.density[density_field])))
1113  delta_P[:,:,:] = (model_instance['g']*model_instance.density[density_field][:,:,:]*
1114  model_instance.grid['drF'][:].reshape(len(model_instance.grid['drF'][:]),1,1))
1115 
1116 
1117  # add free surface contribution
1118  delta_P[0,:,:] = (delta_P[0,:,:] +
1119  model_instance.free_surface[ETAN_field]*model_instance['g']*model_instance.density[density_field][0,:,:])
1120 
1121  self['delta_P'] = delta_P
1122  self['P'] = np.cumsum(delta_P,0)
1123 
1124  return
1125 
1126 
1127 
1129  """!Evaluate the potential vorticity on the tracer points."""
1130  def __init__(self,model_instance,density_field='RhoNil',density_deriv_field='dRHO_dz',vort_field='omega_a'):
1131  self['Q'] = -model_instance.vorticity[vort_field]*model_instance.density[density_deriv_field]/model_instance.density[density_field]
1132 
1133 
def __div__(self, other)
A method that allows model objects to be divided by floating point numbers.
Definition: core.py:179
def take_d_dy
Take the y derivative of the field given on vorticity-points, using the spacings in grid object...
Definition: core.py:780
Calculates the pressure field from the density field using the hydrostatic approximation.
Definition: core.py:1104
def take_d_dx
Take the x derivative of the field on w points, using spacings in grid object.
Definition: core.py:493
def load_field
Load a model field from NetCDF output.
Definition: core.py:56
The simulation class is the main class of this package, and an instance of this class is a model obje...
Definition: core.py:20
def shift_to_tracer(self, field_name)
Shift the array on to the corresponding tracer point.
Definition: core.py:453
def numerics_take_d_dy(self, rho, wet_mask_TH, dyC)
The numerical bit of taking the y derivative.
Definition: core.py:672
def compute_cell_volume(self)
Definition: core.py:1014
def load_field
Load a model field from NetCDF output.
Definition: core.py:346
The Bernoulli field, evaluated from velocity, Pressure and Density.
Definition: core.py:1085
def load_field
Load a model field from NetCDF output.
Definition: core.py:470
def take_d_dx
Derivatives of model fields.
Definition: core.py:226
def take_d_dy
Take the y derivative of the field on w points, using spacings in grid object.
Definition: core.py:525
def compute_boundary_masks(self)
This function does the computationally heavy job of looping through each dimension and creating masks...
Definition: core.py:968
This defines the class for the grid object.
Definition: core.py:848
This is the class for all fields on meridional velocity points.
Definition: core.py:334
def shift_to_tracer(self, field_name)
Shift the array on to the corresponding tracer point.
Definition: core.py:328
def take_d_dy
Take the y derivative of the field on u points, using the spacings provided.
Definition: core.py:260
A tracer point field that contains methods for density fields.
Definition: core.py:1028
def numerics_take_d_dx(self, rho, wet_mask_TH, dxC)
Definition: core.py:636
def take_d_dx
Take the x derivative of the field on tracer points, using spacings in grid object.
Definition: core.py:618
def take_d_dz
Take the z derivative of the field given on v-points, using the spacings in grid object.
Definition: core.py:424
def take_d_dx
Take the x derivative of the field on v points using the spacings in model_instance.grid object.
Definition: core.py:361
def load_from_file(self, model_instance, file_list, variable, time_level, grid_loc, single_file)
Internal function to pull the data from the file(s).
Definition: core.py:71
This is the class for all fields on vertical velocity points.
Definition: core.py:459
def take_d_dz
Take the z derivative of the field given on u-points, using the spacings in grid object.
Definition: core.py:299
def __mul__(self, other)
A method that allows model objects to be multiplied by floating point numbers.
Definition: core.py:186
def shift_to_tracer(self, field_name)
Shift the array on to the corresponding tracer point.
Definition: core.py:587
def take_d_dz
Take the z derivative of the field given on tracer-points, using the spacings in grid object...
Definition: core.py:693
def __init__
Instantiate an MITgcm model instance.
Definition: core.py:25
def take_d_dy
Take the y derivative of the field on tracer points, using spacings in grid object.
Definition: core.py:656
def load_field
Load a model field from NetCDF output.
Definition: core.py:211
This is the base class for all model fields on the tracer points.
Definition: core.py:593
def __init__
Instantiate a field on the meridional velocity points.
Definition: core.py:337
def __init__(self, grid_netcdf_filename)
Define a single object that has all of the grid variables tucked away in it.
Definition: core.py:856
Evaluate the potential vorticity on the tracer points.
Definition: core.py:1128
def calculate_TotRhoTend(self, model_instance)
Calculate time rate of change of the Density field from the temperature tendency and the linear equat...
Definition: core.py:1067
def take_d_dz
Take the z derivative of the field given on w-points, using the spacings in grid object.
Definition: core.py:556
def calculate_density
Cacluate Density field given temperature and salinity fields, using the linear equation of state...
Definition: core.py:1054
def __rmul__(self, other)
A method that allows model objects to be multiplied by floating point numbers.
Definition: core.py:193
def load_field
Load a model field from NetCDF output.
Definition: core.py:603
def load_field
Load a model field from NetCDF output.
Definition: core.py:734
This is the class for all fields on zonal velocity points.
Definition: core.py:200
def shift_to_tracer(self, field_name)
Shift the array on to the corresponding tracer point.
Definition: core.py:840
def take_d_dz
Take the z derivative of the field given on vorticity-points, using the spacings in grid object...
Definition: core.py:810
def take_d_dy
Take the y derivative of the field given on v-points, using the spacings in grid object.
Definition: core.py:393
A class for fields on vorticity points.
Definition: core.py:724
def take_d_dx
Take the x derivative of the field given on vorticity-points, using the spacings in grid object...
Definition: core.py:749
def __add__(self, other)
A method that allows model objects to be added together.
Definition: core.py:170