4

I'm very new to Python and currently trying to replicate plots etc that I previously used GrADs for. I want to calculate the divergence at each grid box using u and v wind fields (which are just scaled by specific humidity, q), from a netCDF climate model file.

From endless searching I know I need to use some combination of np.gradient and np.sum, but can't find the right combination. I just know that to do it 'by hand', the calculation would be divg = dqu/dx + dqv/dy I know the below is wrong, but it's the best I've got so far...

nc = Dataset(ifile)
q = np.array(nc.variables['hus'][0,:,:])
u = np.array(nc.variables['ua'][0,:,:])
v = np.array(nc.variables['va'][0,:,:])
lon=nc.variables['lon'][:]
lat=nc.variables['lat'][:]

qu = q*u
qv = q*v  

dqu/dx, dqu/dy = np.gradient(qu, [dx, dy])
dqv/dx, dqv/dy = np.gradient(qv, [dx, dy])

divg = np.sum(dqu/dx, dqv/dy)

This gives the error 'SyntaxError: can't assign to operator'.

Any help would be much appreciated.

ClimateUnboxed
  • 7,106
  • 3
  • 41
  • 86
amy712
  • 73
  • 2
  • 6
  • On a side note, `gradient` returns `d_row, d_column`. It depends on your input array, but this is typically `dy, dx`, not `dx, dy`. – Joe Kington Sep 02 '15 at 12:43

3 Answers3

3

try something like:

dqu_dx, dqu_dy = np.gradient(qu, [dx, dy])
dqv_dx, dqv_dy = np.gradient(qv, [dx, dy])

you can not assign to any operation in python; any of those are syntax errors:

a + b = 3
a * b = 7
# or, in your case:
a / b = 9

UPDATE

following Pinetwig's comment: a/b is not a valid identifier name; it is (the return value of) an operator.

Community
  • 1
  • 1
hiro protagonist
  • 44,693
  • 14
  • 86
  • 111
  • 1
    To clarify: you can't use a forward slash in a variable name, because in Python, forward slash means division. – Pinetwig Sep 02 '15 at 10:57
  • Thanks - this gets rid of that problem, but now I get the error NameError: name 'dx' is not defined – amy712 Sep 02 '15 at 13:22
  • Is this because I haven't defined dx as the change in lon/dy as the change in lat? – amy712 Sep 02 '15 at 13:26
1

Try removing the [dx, dy].

   [dqu_dx, dqu_dy] = np.gradient(qu)
   [dqv_dx, dqv_dy] = np.gradient(qv)

Also to point out if you are recreating plots. Gradient changed in numpy between 1.82 and 1.9. This had an effect for recreating matlab plots in python as 1.82 was the matlab method. I am not sure how this relates to GrADs. Here is the wording for both.

1.82

"The gradient is computed using central differences in the interior and first differences at the boundaries. The returned gradient hence has the same shape as the input array."

1.9

"The gradient is computed using second order accurate central differences in the interior and either first differences or second order accurate one-sides (forward or backwards) differences at the boundaries. The returned gradient hence has the same shape as the input array."

The gradient function for 1.82 is here.

def gradient(f, *varargs):
"""
Return the gradient of an N-dimensional array.

The gradient is computed using central differences in the interior
and first differences at the boundaries. The returned gradient hence has
the same shape as the input array.

Parameters
----------
f : array_like
  An N-dimensional array containing samples of a scalar function.
`*varargs` : scalars
  0, 1, or N scalars specifying the sample distances in each direction,
  that is: `dx`, `dy`, `dz`, ... The default distance is 1.


Returns
-------
gradient : ndarray
  N arrays of the same shape as `f` giving the derivative of `f` with
  respect to each dimension.

Examples
--------
>>> x = np.array([1, 2, 4, 7, 11, 16], dtype=np.float)
>>> np.gradient(x)
array([ 1. ,  1.5,  2.5,  3.5,  4.5,  5. ])
>>> np.gradient(x, 2)
array([ 0.5 ,  0.75,  1.25,  1.75,  2.25,  2.5 ])

>>> np.gradient(np.array([[1, 2, 6], [3, 4, 5]], dtype=np.float))
[array([[ 2.,  2., -1.],
       [ 2.,  2., -1.]]),
array([[ 1. ,  2.5,  4. ],
       [ 1. ,  1. ,  1. ]])]

"""
  f = np.asanyarray(f)
  N = len(f.shape)  # number of dimensions
  n = len(varargs)
  if n == 0:
      dx = [1.0]*N
  elif n == 1:
      dx = [varargs[0]]*N
  elif n == N:
      dx = list(varargs)
  else:
      raise SyntaxError(
              "invalid number of arguments")

# use central differences on interior and first differences on endpoints

  outvals = []

# create slice objects --- initially all are [:, :, ..., :]
  slice1 = [slice(None)]*N
  slice2 = [slice(None)]*N
  slice3 = [slice(None)]*N

  otype = f.dtype.char
  if otype not in ['f', 'd', 'F', 'D', 'm', 'M']:
      otype = 'd'

# Difference of datetime64 elements results in timedelta64
  if otype == 'M' :
    # Need to use the full dtype name because it contains unit information
      otype = f.dtype.name.replace('datetime', 'timedelta')
  elif otype == 'm' :
    # Needs to keep the specific units, can't be a general unit
      otype = f.dtype

  for axis in range(N):
    # select out appropriate parts for this dimension
      out = np.empty_like(f, dtype=otype)
      slice1[axis] = slice(1, -1)
      slice2[axis] = slice(2, None)
      slice3[axis] = slice(None, -2)
    # 1D equivalent -- out[1:-1] = (f[2:] - f[:-2])/2.0
      out[slice1] = (f[slice2] - f[slice3])/2.0
      slice1[axis] = 0
      slice2[axis] = 1
      slice3[axis] = 0
    # 1D equivalent -- out[0] = (f[1] - f[0])
      out[slice1] = (f[slice2] - f[slice3])
      slice1[axis] = -1
      slice2[axis] = -1
      slice3[axis] = -2
    # 1D equivalent -- out[-1] = (f[-1] - f[-2])
      out[slice1] = (f[slice2] - f[slice3])

    # divide by step size
      outvals.append(out / dx[axis])

    # reset the slice object in this dimension to ":"
      slice1[axis] = slice(None)
      slice2[axis] = slice(None)
      slice3[axis] = slice(None)

  if N == 1:
      return outvals[0]
  else:
      return outvals
Almidas
  • 379
  • 2
  • 6
  • 16
0

If your grid is Gaussian and the wind names in the file are "u" and "v" you can also calculate divergence directly using cdo:

cdo uv2dv in.nc out.nc

See https://code.mpimet.mpg.de/projects/cdo/embedded/index.html#x1-6850002.13.2 for more details.

ClimateUnboxed
  • 7,106
  • 3
  • 41
  • 86
  • while this answer might be helpful for the OP, it does not answer the question why the code throws a syntax error and is not python related – Fookatchu May 02 '18 at 16:33
  • If someone want to know how to calculate vorticity and divergence they are likely to find this page and may not know that they can do it from the command line this easily... This depends whether one considers stack-exchange as merely a means to post specific debugging advice to specific problems, or a wider font of information. I think the latter is more useful. Someone else already voted it up as useful, you voted it down as too general... that is what the voting system is for. – ClimateUnboxed May 03 '18 at 09:23
  • This cdo command only calculates divergence of a wind vector field. What if one has a vector field of e.g. ocean or ice velocity and needs to calculate their divergence? – Behnam Dec 12 '18 at 12:56
  • The function is fairly hardwired - see this thread here https://code.mpimet.mpg.de/boards/2/topics/4647 but I suppose you might fudge it by renaming the advective field with nco? Unfortunately CDO does not have more generic operators for flux fields... – ClimateUnboxed Dec 12 '18 at 18:23