mitgcm
Analysis of MITgcm output using python
functions.py
Go to the documentation of this file.
1 """!
2 Useful functions that don't belong elsewhere.
3 
4 A collection of functions for analysing model output. These include functions to extract surfaces at a constant value of some variable and extract a variable on a surface.
5 
6 Each function has a detailed docstring.
7 """
8 
9 import numpy as np
10 import netCDF4
11 import numba
12 import sys
13 import matplotlib.pyplot as plt
14 import glob
15 import scipy.interpolate
16 import warnings
17 import copy
18 import mitgcm
19 
20 
21 def calc_surface(input_array,surface_value,axis_values,method='linear'):
22  """! Calculate an isosurface of input_array. This function is fast, but it is not designed to deal well with non-monotonicity in the search direction.
23 
24  There are two search options:
25  * 'nearest' finds the index just before the search value
26  * 'linear' uses linear interpolation to find the location between grid points.
27 
28  May give silly answers if input_array is not monotonic in the search direction."""
29 
30 
31  axis=0
32  monoton_test = np.diff(input_array,axis=axis)
33 
34  if np.all(monoton_test <= 0) or np.all(monoton_test >= 0):
35  pass
36  else:
37  warnings.warn("input field is not strictly monotonic in search direction. Strange results may occur.", RuntimeWarning)
38 
39  indsz = np.repeat(np.arange(input_array.shape[0]-1).reshape((input_array.shape[0]-1,1,1)),input_array.shape[1],axis=1)
40  indsz = np.repeat(indsz.reshape((input_array.shape[0]-1,input_array.shape[1],1)),input_array.shape[2],axis=2)
41 
42  dist = (input_array - surface_value)
43 
44  sign= np.sign(dist)
45  sign[sign==0] = -1 # replace zeros with -1
46  indices_min = np.where(np.diff(sign,axis=axis),indsz,0)
47  indices_min = (np.argmax(indices_min,axis=axis)) # to deal with multiple crossings
48 
49  if method =='nearest':
50  z_surface = np.take(axis_values,indices_min[:,:])
51 
52  elif method == 'linear':
53  z_nearest = np.take(axis_values,indices_min[:,:])
54 
55  indsy = np.repeat(np.arange(indices_min.shape[0]).reshape((indices_min.shape[0],1)),indices_min.shape[1],axis=1)
56  indsx = np.repeat(np.arange(indices_min.shape[1]).reshape((1,indices_min.shape[1])),indices_min.shape[0],axis=0)
57 
58  above = input_array[indices_min[:,:]-1,indsy,indsx]
59  nearest = input_array[indices_min[:,:],indsy,indsx]
60  below = input_array[indices_min[:,:]+1,indsy,indsx]
61 
62  direction = np.zeros(indices_min.shape,dtype='int64')
63 
64  # python refuses to index with a two-component conditional, so it needs to be done in two parts.
65  test1 = above > surface_value
66  test2 = surface_value > nearest
67  direction[test1 == test2] = -1
68 
69  test1 = above < surface_value
70  test2 = surface_value < nearest
71  direction[test1 == test2] = -1
72 
73  test1 = nearest > surface_value
74  test2 = surface_value > below
75  direction[test1 == test2] = 1
76 
77  test1 = nearest < surface_value
78  test2 = surface_value < below
79  direction[test1 == test2] = 1
80 
81 
82 
83  z_surface = np.take(axis_values,indices_min[:,:] + direction) + np.nan_to_num(
84  ((z_nearest - np.take(axis_values,indices_min[:,:] + direction))/
85  (input_array[indices_min[:,:],indsy,indsx] -
86  input_array[indices_min[:,:] + direction,indsy,indsx]))*
87  (surface_value - input_array[indices_min[:,:] + direction,indsy,indsx]))
88  else:
89  raise RuntimeError(str(method), ' not set correctly. Must be "nearest" or "linear".')
90 
91 
92  input_array_masked = np.ma.masked_where(input_array==0,input_array)
93 
94 
95  test1 = np.nanmax(input_array_masked,axis=0) < surface_value
96  test2 = np.nanmin(input_array_masked,axis=0) > surface_value
97 
98  mask_condition = test1 + test2
99 
100  z_surface = np.ma.masked_where(mask_condition, z_surface)
101 
102 
103 
104  return z_surface
105 
106 
107 def extract_on_surface(input_array,surface_locations,axis_values):
108  """!Extract the value of a 3D field on a 2D surface.
109  This function takes an 3 dimensional matrix 'input_array' and an 2 dimensional
110  matrix 'surface_locations' and returns a 2 dimensional matrix that contains the
111  values of input_array at the location specified by surface_locations along the third dimension using
112  the values for that axis contained in 'axis_values'. Linear interpolation is used to find the values.
113 
114  """
115  if hasattr(surface_locations, "__len__"):
116  pass
117  else:
118  surface_locations = surface_locations*np.ones((input_array.shape[1],input_array.shape[2]))
119 
120  axis_array = np.repeat(axis_values.reshape((axis_values.shape[0],1,1)),surface_locations.shape[0],axis=1)
121  axis_array = np.repeat(axis_array.reshape((axis_array.shape[0],axis_array.shape[1],1)),surface_locations.shape[1],axis=2)
122 
123  indsz = np.repeat(np.arange(input_array.shape[0]-1).reshape((input_array.shape[0]-1,1,1)),input_array.shape[1],axis=1)
124  indsz = np.repeat(indsz.reshape((input_array.shape[0]-1,input_array.shape[1],1)),input_array.shape[2],axis=2)
125 
126  dist = (axis_array - surface_locations)
127 
128  sign= np.sign(dist)
129  sign[sign==0] = -1 # replace zeros with -1
130  indices_above = np.where(np.diff(sign,axis=0),indsz,0)
131  indices_above = (np.nanmax(indices_above,axis=0)) # to deal with multiple crossings (shouldn't be needed)
132 
133  indsy = np.repeat(np.arange(indices_above.shape[0]).reshape((indices_above.shape[0],1)),indices_above.shape[1],axis=1)
134  indsx = np.repeat(np.arange(indices_above.shape[1]).reshape((1,indices_above.shape[1])),indices_above.shape[0],axis=0)
135 
136  values_above = input_array[indices_above[:,:],indsy,indsx]
137  values_below = input_array[indices_above[:,:]+1,indsy,indsx]
138 
139  axis_above = axis_array[indices_above[:,:],indsy,indsx]
140  axis_below = axis_array[indices_above[:,:]+1,indsy,indsx]
141 
142  surface_values = values_above + ((values_below - values_above)/(axis_below - axis_above))*(surface_locations - axis_above)
143  # value above + linear interpolation of final partial cell.
144 
145  return surface_values
146 
147 
148 def layer_integrate(upper_contour, lower_contour, grid_obect, integrand = 'none', interp_method='none'):
149  """!Integrate between two non-trivial surfaces, 'upper_contour' and 'lower_contour'.
150  At the moment this only works if all the inputs are defined at the same grid location.
151 
152  The input array 'integrand' is optional. If it is not included then the output is the volume per unit area (difference in depth) between the two surfaces at each grid point,
153 
154  -------------------
155  ## Parameters
156  * upper_contour - the higher surface
157  * lower_contour - the lower surface
158  * axis_values - the vertical axis.
159  ** defined at the cell centres if using interp_method = 'linear'
160  ** defined at the cell faces if using interp_method = 'none'
161  * interp_method - defines whether the function interpolates values of the integrand. Possible options are 'none' or 'linear'.
162  * integrand - the field to be integrated between the contours
163  """
164 
165  if interp_method == 'none':
166  axis_values = grid_obect['Zl'][:]
167  elif interp_method == 'linear':
168  axis_values = grid_obect['Z'][:]
169 
170  total = np.ma.zeros((upper_contour.shape))
171 
172 
173  if integrand == 'none':
174  total = np.absolute(upper_contour - lower_contour)
175 
176  else:
177  axis_array = np.repeat(axis_values.reshape((axis_values.shape[0],1,1)),upper_contour.shape[0],axis=1)
178  axis_array = np.repeat(axis_array.reshape((axis_array.shape[0],axis_array.shape[1],1)),upper_contour.shape[1],axis=2)
179 
180  indsz = np.repeat(np.arange(integrand.shape[0]-1).reshape((integrand.shape[0]-1,1,1)),integrand.shape[1],axis=1)
181  indsz = np.repeat(indsz.reshape((integrand.shape[0]-1,integrand.shape[1],1)),integrand.shape[2],axis=2)
182 
183  # these code blocks always return the index of the cell above the zero crossing
184  dist = (axis_array - upper_contour)
185  sign= np.sign(dist)
186  sign[sign==0] = -1 # replace zeros with -1
187  indices_upper = np.where(np.diff(sign,axis=0),indsz,0)
188  indices_upper = (np.nanmax(indices_upper,axis=0)) # to flatten array and deal with multiple crossings
189 
190  dist = (axis_array - lower_contour)
191  sign= np.sign(dist)
192  sign[sign==0] = -1 # replace zeros with -1
193  indices_lower = np.where(np.diff(sign,axis=0),indsz,0)
194  indices_lower = (np.nanmax(indices_lower,axis=0)) # to flatten array and deal with multiple crossings
195 
196  values_upper = mitgcm.functions.extract_on_surface(integrand,upper_contour,axis_values)
197  values_lower = mitgcm.functions.extract_on_surface(integrand,lower_contour,axis_values)
198 
199 
200  if interp_method == 'none':
201  for j in xrange(0,upper_contour.shape[0]):
202  for i in xrange(0,upper_contour.shape[1]):
203  if (values_upper[j,i] is not np.ma.masked) and (values_lower[j,i] is not np.ma.masked):
204  if indices_upper[j,i] == indices_lower[j,i]:
205  # in the same cell. find midvalue and mulitply by thickness
206  total[j,i] = (integrand[indices_upper[j,i],j,i])*(upper_contour[j,i] - lower_contour[j,i])
207  #if total[j,i]<0:
208  # print 'Problem! total less than zero from same cell'
209  else:
210  # not in the same cell. Have at least an upper and lower partial cell to compute
211  upper_partial = (integrand[indices_upper[j,i],j,i]*
212  (upper_contour[j,i] - axis_values[indices_upper[j,i]+1]))
213  lower_partial = (integrand[indices_lower[j,i],j,i]*
214  (axis_values[indices_lower[j,i]] - lower_contour[j,i]))
215 
216  total[j,i] = upper_partial + lower_partial
217  #if total[j,i]<0:
218  # print 'Problem! value less than zero from two partials'
219 
220  if indices_lower[j,i] - indices_upper[j,i] > 1:
221  # have at least one whole cell between them (the same cell case has already been captured)
222  for k in xrange(indices_upper[j,i],indices_lower[j,i]-1):
223  total[j,i] += integrand[k,j,i]*(axis_values[k] - axis_values[k+1]) #
224  #if (integrand[k,j,i]*(axis_values[k] - axis_values[k+1]))<0:
225  # print 'Problem! intermediate cell contribution is less than zero.'
226  elif interp_method == 'linear':
227  for j in xrange(0,upper_contour.shape[0]):
228  for i in xrange(0,upper_contour.shape[1]):
229  if (values_upper[j,i] is not np.ma.masked) and (values_lower[j,i] is not np.ma.masked):
230  # both contours defined at this location
231  if indices_upper[j,i] == indices_lower[j,i]:
232  # in the same cell. find midvalue and mulitply by thickness
233  total[j,i] = (values_upper[j,i] + values_lower[j,i])*(upper_contour[j,i] - lower_contour[j,i])/2
234  else:
235  # not in the same cell. Have at least an upper and lower partial cell to compute
236  upper_partial = ((values_upper[j,i] + integrand[indices_upper[j,i]+1,j,i])*
237  (upper_contour[j,i] - axis_values[indices_upper[j,i]+1]))/2
238  lower_partial = ((values_lower[j,i] + integrand[indices_lower[j,i],j,i])*
239  (axis_values[indices_lower[j,i]] - lower_contour[j,i]))/2
240 
241  total[j,i] = upper_partial + lower_partial
242 
243  if indices_lower[j,i] - indices_upper[j,i] > 1:
244  # have at least one whole cell between them (the same cell case has already been captured)
245  for k in xrange(indices_upper[j,i]+1,indices_lower[j,i]):
246  total[j,i] += (integrand[k,j,i] + integrand[k+1,j,i])*(axis_values[k] - axis_values[k+1])/2 #
247 
248  elif (values_upper[j,i] is np.ma.masked) and (values_lower[j,i] is not np.ma.masked):
249  # upper contour undefined at this location (has outcropped)
250 
251  total[j,i] += integrand[0,j,i]*grid_obect['drF'][0]/2 # surface half-cell
252 
253  lower_partial = ((values_lower[j,i] + integrand[indices_lower[j,i],j,i])*
254  (axis_values[indices_lower[j,i]] - lower_contour[j,i]))/2
255 
256  total[j,i] += lower_partial
257 
258  for k in xrange(indices_lower[j,i]):
259  # whole cells
260  total[j,i] += (integrand[k,j,i] + integrand[k+1,j,i])*(axis_values[k] - axis_values[k+1])/2
261 
262 
263  elif (values_upper[j,i] is not np.ma.masked) and (values_lower[j,i] is np.ma.masked):
264  # lower contour is undefined at this location (has incropped)
265  total[j,i] += integrand[-1,j,i]*grid_obect['drF'][-1]/2 # bottom half-cell
266 
267 
268  upper_partial = ((values_upper[j,i] + integrand[indices_upper[j,i]+1,j,i])*
269  (upper_contour[j,i] - axis_values[indices_upper[j,i]+1]))/2
270 
271  total[j,i] += upper_partial
272 
273  for k in xrange(indices_upper[j,i],len(axis_values)-1):
274  total[j,i] += (integrand[k,j,i] + integrand[k+1,j,i])*(axis_values[k] - axis_values[k+1])/2
275 
276  return total
277 
278 
279 
280 
282  """Test function to check that the layer integrate function is working correctly."""
283  upper = -1 * np.array([[1,1,1],[1,1,1],[1,1,1]])
284  lower = -1 * (np.array([[-0.9,1,1],[1,1,1],[1,1,1]]) + 2)
285  axis = -1 * np.array([0.5,1.2,1.6,2.1,2.6,3.1])
286  integrand = np.ones((len(axis),upper.shape[0],upper.shape[1]))
287  assert np.sum(layer_integrate(upper,lower,axis,integrand=integrand)) == np.sum(upper - lower)
288 
289 
290 
291 
292 
293 
294 
295 def interp_field(field,old_x,old_y,new_x,new_y,interp_order,fill_nans='no',max_its=5):
296  """!Interpolate a given field onto a different grid. Only performs interpolation in the horizontal directions.
297 
298  ----
299  ##Parameters##
300  * field - the variable to be interpolated
301  * old_x, old_y - the axis on which the original field is defined.
302  * new_x, new_y - the axis onto which the field will be interpolated.
303  * interp_order - the order of the interpolation function, integer between 1 and 5 inclusive. 1 -> linear, 3 -> cubic, &c..
304  * fill_nans - if 'no' values in field are not altered. If 'yes', then NaNs in 'field'
305  are replace with the mean of surrounding non-NaNs.
306  * max_its - maximum number of iterations to perform when healing NaNs in field."""
307 
308  # reshape input field if it's only given as a 2D array
309  if len(field.shape) == 2:
310  field = field.reshape((1,field.shape[0],field.shape[1]))
311  elif len(field.shape) == 3:
312  pass
313  else:
314  error_message = 'Input field is must be either 2D or 3D.'
315  raise RuntimeError(error_message)
316 
317 
318  field_interp = np.zeros((field.shape[0],
319  len(new_y),
320  len(new_x)))
321 
322  for k in xrange(field.shape[0]):
323  if fill_nans == 'yes':
324  field_slice = replace_nans(field[k,:,:], max_its,0.5,1,'localmean')
325  n = 0
326  while (field_slice != field_slice).any():
327  field_slice = replace_nans(field_slice[:,:], max_its,0.5,1,'localmean')
328  # repeat the replace_nans call since it can sometimes miss ones in the corners.
329  # need a way to prevent hanging in the while loop
330  if n > max_its:
331  error_message = 'Tried ' + str(max_its) + ' iterations to heal NaNs in the input field, and failed.'
332  raise RuntimeError(error_message)
333 
334  n += 1
335  elif fill_nans == 'no':
336  field_slice = field[k,:,:]
337  else:
338  raise ValueError('fill_nans not set correctly. Should be "yes" or "no". You gave "' + str(fill_nans) + '"')
339 
340  interp_object = scipy.interpolate.RectBivariateSpline(old_y,old_x,field_slice,kx=interp_order,ky=interp_order)
341  field_interp[k,:,:] = interp_object(new_y,new_x)
342 
343 
344  return field_interp
345 
346 
347 
348 
349 def export_binary(filename,field,dtype='float64'):
350  """!Export binary files that can be imported into the MITgcm.
351  The files are big endian, and the datatype can either be 'float64' (= double precision), or 'float32' (=single precision).
352 
353  Might not work for very large datasets."""
354 
355  data = np.array(field,dtype=dtype) # with defined precision, either float32 or float64
356  if sys.byteorder == 'little': data.byteswap(True)
357  fid = open(filename,"wb")
358  data.tofile(fid) # this does not work for very large data sets
359  fid.close()
360 
361 
362 
363 def show_variables(netcdf_filename):
364  """!A shortcut function to display all of the variables contained within a netcdf file.
365 
366  If the filename contains wildcards, they'll be expanded and only the first file used."""
367  files = glob.glob(netcdf_filename)
368  netcdf_file = netCDF4.Dataset(files[0])
369  print netcdf_file.variables.keys()
370  netcdf_file.close()
371 
372 
373 
374 
375 def plt_mon_stats(netcdf_filename,
376  variables=['advcfl_uvel_max','advcfl_vvel_max','advcfl_wvel_max'],
377  time_units='days',
378  output_filename=None):
379  """!Plot some monitor file variables. 'netcdf_filename' can contain shell wild cards, but only the first matching file will be used.
380 
381  Options include:
382  * advcfl_uvel_max
383  * advcfl_vvel_max
384  * advcfl_wvel_max
385  * ke_mean
386  * dynstat_theta_mean
387  * dynstat_sst_mean
388  * dynstat_sst_sd
389  * dynstat_salt_max
390  * dynstat_salt_min
391  * dynstat_uvel_mean
392  * dynstat_vvel_mean
393  * dynstat_wvel_mean
394  * ...
395  * and many others.
396 
397  """
398  files = glob.glob(netcdf_filename)
399  monitor_output = netCDF4.Dataset(files[0])
400 
401  if time_units == 'days':
402  time = monitor_output.variables['time_secondsf'][:]/(86400)
403  elif time_units == 'years':
404  time = monitor_output.variables['time_secondsf'][:]/(86400*365)
405  else:
406  raise ValueError(str(time_units) + ' is not a valid option for time_units')
407  data = {}
408  for stat in variables:
409  data[stat] = monitor_output.variables[stat][:]
410  plt.plot(time,data[stat],label=stat)
411  plt.xlabel('Model '+ time_units)
412  plt.legend()
413 
414  data['time'] = time
415 
416  if output_filename != None:
417  plt.savefig(output_filename)
418 
419  return data
420 
421 
422 def replace_nans(array, max_iter, tol, kernel_size, method='localmean'):
423  """!Replace NaN elements in an array using an iterative image inpainting algorithm.
424 
425  The algorithm is the following:
426 
427  1) For each element in the input array, replace it by a weighted average
428  of the neighbouring elements which are not NaN themselves. The weights depends
429  of the method type. If ``method=localmean`` weight are equal to 1/( (2*kernel_size+1)**2 -1 )
430 
431  2) Several iterations are needed if there are adjacent NaN elements.
432  If this is the case, information is "spread" from the edges of the missing
433  regions iteratively, until the variation is below a certain threshold.
434 
435  Parameters
436  ----------
437 
438  array : 2d np.ndarray
439  an array containing NaN elements that have to be replaced
440 
441  max_iter : int
442  the number of iterations
443 
444  tol: float
445  the difference between subsequent iterations for stopping the algorithm
446 
447  kernel_size : int
448  the size of the kernel, default is 1
449 
450  method : str
451  the method used to replace invalid values. Valid options are
452  `localmean`.
453 
454  Returns
455  --------
456 
457  filled : 2d np.ndarray
458  a copy of the input array, where NaN elements have been replaced.
459 
460 
461 
462  ---------
463  ##Acknowledgements
464 
465  Code for this function is (very slightly modified) from
466  https://github.com/gasagna/openpiv-python/commit/81038df6d218b893b044193a739026630238fb22#diff-9b2f4f9bb8180e4451e8f85164df7217
467  which is part of the OpenPIV project.
468  * docs here: http://alexlib.github.io/openpiv-python/index.html
469  * code here: https://github.com/gasagna/openpiv-python
470 
471  """
472 
473  filled = np.empty( [array.shape[0], array.shape[1]], dtype=np.float64)
474  kernel = np.empty( (2*kernel_size+1, 2*kernel_size+1), dtype=np.float64 )
475 
476  # indices where array is NaN
477  inans, jnans = np.nonzero( np.isnan(array) )
478 
479  # number of NaN elements
480  n_nans = len(inans)
481 
482  # arrays which contain replaced values to check for convergence
483  replaced_new = np.zeros( n_nans, dtype=np.float64)
484  replaced_old = np.zeros( n_nans, dtype=np.float64)
485 
486  # depending on kernel type, fill kernel array
487 
488  if method == 'localmean':
489  for i in range(2*kernel_size+1):
490  for j in range(2*kernel_size+1):
491  kernel[i,j] = 1.0
492  else:
493  raise ValueError( 'method not valid. Should be one of `localmean`.')
494 
495  # fill new array with input elements
496  for i in range(array.shape[0]):
497  for j in range(array.shape[1]):
498  filled[i,j] = array[i,j]
499 
500 
501  #filled = numerics_replace_nans(max_iter,n_nans,inans,jnans,filled,kernel,kernel_size,tol,replaced_new,replaced_old)
502  # make several passes
503  # until we reach convergence
504  for it in range(max_iter):
505 
506  # for each NaN element
507  for k in range(n_nans):
508  i = inans[k]
509  j = jnans[k]
510 
511  # initialize to zero
512  filled[i,j] = 0.0
513  n = 0
514 
515  # loop over the kernel
516  for I in range(2*kernel_size+1):
517  for J in range(2*kernel_size+1):
518 
519  # if we are not out of the boundaries
520  if i+I-kernel_size < filled.shape[0] and i+I-kernel_size >= 0:
521  if j+J-kernel_size < filled.shape[1] and j+J-kernel_size >= 0:
522 
523  # if the neighbour element is not NaN itself.
524  if filled[i+I-kernel_size, j+J-kernel_size] == filled[i+I-kernel_size, j+J-kernel_size] :
525 
526  # do not sum itself
527  if I-kernel_size != 0 and J-kernel_size != 0:
528 
529  # convolve kernel with original array
530  filled[i,j] += filled[i+I-kernel_size, j+J-kernel_size]*kernel[I, J]
531  n = n + 1
532 
533  # divide value by effective number of added elements
534  if n != 0:
535  filled[i,j] = filled[i,j] / n
536  replaced_new[k] = filled[i,j]
537  else:
538  filled[i,j] = np.nan
539 
540  # check if mean square difference between values of replaced
541  #elements is below a certain tolerance
542  if np.mean( (replaced_new-replaced_old)**2 ) < tol:
543  break
544  else:
545  for l in range(n_nans):
546  replaced_old[l] = replaced_new[l]
547 
548 
549  # replace remaining nans with global mean
550  inans, jnans = np.nonzero( np.isnan(filled) )
551  n_nans = len(inans)
552  # for each NaN element
553  fill_value = np.nanmean(filled)
554  for k in range(n_nans):
555  i = inans[k]
556  j = jnans[k]
557 
558  filled[i,j] = fill_value
559 
560  # Check the corners - these are the tricky bits
561  #if np.nonzero(np.isnan(filled[0,0])):
562  # filled[0,0] = (filled[0,1] + filled[1,0] + filled[1,1])/3
563 
564  #if np.nonzero(np.isnan(filled[0,-1])):
565  # filled[0,-1] = (filled[0,-2] + filled[1,-1] + filled[1,-2])/3
566 
567  #if np.nonzero(np.isnan(filled[0,0])):
568  # filled[-1,0] = (filled[-1,1] + filled[-2,0] + filled[-2,1])/3
569 
570  #if np.nonzero(np.isnan(filled[0,0])):
571  # filled[-1,-1] = (filled[-1,-2] + filled[-2,-1] + filled[-2,-2])/3
572 
573 
574  # now go around the edge and fill in any nans found there
575  # indices where array is NaN
576  #jnans = np.nonzero( np.isnan(filled[0,:]) )
577  # number of NaN elements
578  #n_nans = len(jnans)
579  # for each NaN element
580  #for k in range(n_nans):
581  # j = jnans[k]
582  # filled[0,j] = np.nanmean(filled[0,j-1:j+2])
583 
584  #jnans = np.nonzero( np.isnan(filled[-1,:]) )
585  #n_nans = len(jnans)
586  #for k in range(n_nans):
587  # j = jnans[k]
588  # filled[-1,j] = np.nanmean(filled[-1,j-1:j+2])
589 
590  #inans = np.nonzero( np.isnan(filled[:,0]) )
591  # number of NaN elements
592  #n_nans = len(inans)
593  # for each NaN element
594  #for k in range(n_nans):
595  # i = inans[k]
596  # filled[i,0] = np.nanmean(filled[i-1:i+2,0])
597 
598  #inans = np.nonzero( np.isnan(filled[:,-1]) )
599  # number of NaN elements
600  #n_nans = len(inans)
601  # for each NaN element
602  #for k in range(n_nans):
603  # i = inans[k]
604  # filled[i,-1] = np.nanmean(filled[i-1:i+2,-1])
605 
606  return filled
607 
608 
609 #@numba.jit
610 def numerics_replace_nans(max_iter,n_nans,inans,jnans,filled,kernel,kernel_size,tol,replaced_new,replaced_old):
611  # make several passes
612  # until we reach convergence
613  for it in range(max_iter):
614 
615  # for each NaN element
616  for k in range(n_nans):
617  i = inans[k]
618  j = jnans[k]
619 
620  # initialize to zero
621  filled[i,j] = 0.0
622  n = 0
623 
624  # loop over the kernel
625  for I in range(2*kernel_size+1):
626  for J in range(2*kernel_size+1):
627 
628  # if we are not out of the boundaries
629  if i+I-kernel_size < filled.shape[0] and i+I-kernel_size >= 0:
630  if j+J-kernel_size < filled.shape[1] and j+J-kernel_size >= 0:
631 
632  # if the neighbour element is not NaN itself.
633  if filled[i+I-kernel_size, j+J-kernel_size] == filled[i+I-kernel_size, j+J-kernel_size] :
634 
635  # do not sum itself
636  if I-kernel_size != 0 and J-kernel_size != 0:
637 
638  # convolve kernel with original array
639  filled[i,j] += filled[i+I-kernel_size, j+J-kernel_size]*kernel[I, J]
640  n = n + 1
641 
642  # divide value by effective number of added elements
643  if n != 0:
644  filled[i,j] = filled[i,j] / n
645  replaced_new[k] = filled[i,j]
646  else:
647  filled[i,j] = np.nan
648 
649  # check if mean square difference between values of replaced
650  #elements is below a certain tolerance
651  if np.mean( (replaced_new-replaced_old)**2 ) < tol:
652  break
653  else:
654  for l in range(n_nans):
655  replaced_old[l] = replaced_new[l]
656 
657  return filled
658 
659 
660 
661 
662 def shift_vort_to_T(array):
663  """! Shift the array from vorticity points to the corresponding tracer point."""
664  shifted = (array[...,0:-1] + array[...,1:])/2
665  shifted = (shifted[...,0:-1,:] + shifted[...,1:,:])/2
666  return shifted
667 
668 def shift_U_to_T(array):
669  """! Shift the array from UVEL points to the corresponding tracer point."""
670  shifted = (array[...,0:-1] + array[...,1:])/2
671 
672  return shifted
673 
674 def shift_V_to_T(array):
675  """! Shift the array from VVEL points to the corresponding tracer point."""
676  shifted = (array[...,0:-1,:] + array[...,1:,:])/2
677 
678  return shifted
679 
680 def shift_W_to_T(array):
681  """! Shift the array from WVEL points to the corresponding tracer point."""
682  shifted = (array[...,0:-1,:,:] + array[...,1:,:,:])/2
683 
684  return shifted
685 
def interp_field
Interpolate a given field onto a different grid.
Definition: functions.py:295
def shift_vort_to_T(array)
Shift the array from vorticity points to the corresponding tracer point.
Definition: functions.py:662
def show_variables(netcdf_filename)
A shortcut function to display all of the variables contained within a netcdf file.
Definition: functions.py:363
def test_layer_integrate()
Definition: functions.py:281
def plt_mon_stats
Plot some monitor file variables.
Definition: functions.py:378
def shift_V_to_T(array)
Shift the array from VVEL points to the corresponding tracer point.
Definition: functions.py:674
def extract_on_surface(input_array, surface_locations, axis_values)
Extract the value of a 3D field on a 2D surface.
Definition: functions.py:107
def shift_W_to_T(array)
Shift the array from WVEL points to the corresponding tracer point.
Definition: functions.py:680
def export_binary
Export binary files that can be imported into the MITgcm.
Definition: functions.py:349
def layer_integrate
Integrate between two non-trivial surfaces, 'upper_contour' and 'lower_contour'.
Definition: functions.py:148
def numerics_replace_nans(max_iter, n_nans, inans, jnans, filled, kernel, kernel_size, tol, replaced_new, replaced_old)
Definition: functions.py:610
def calc_surface
Calculate an isosurface of input_array.
Definition: functions.py:21
def shift_U_to_T(array)
Shift the array from UVEL points to the corresponding tracer point.
Definition: functions.py:668
def replace_nans
Replace NaN elements in an array using an iterative image inpainting algorithm.
Definition: functions.py:422