Search code examples
pythonmetpy

Error computing frontogenesis with mpcalc


My goal is to compute frontogenesis at a given pressure level using the mpcalc.frontogenesis method.

I have read in the relevant data from NOMADS:

t7 = data['temp'].sel(lev=700.0,lat=lats,lon=lons)
u7 = data['u'].sel(lev=700.0,lat=lats,lon=lons).squeeze()*1.94384449
v7 = data['v'].sel(lev=700.0,lat=lats,lon=lons).squeeze()*1.94384449
h7 = data['gph'].sel(lev=700.0,lat=lats,lon=lons).squeeze()

Computed the relevant coordinate/grid spacing values:

x, y = t7.metpy.coordinates('x', 'y')
dx, dy = mpcalc.lat_lon_grid_deltas(lons, lats)
lat, lon = xr.broadcast(y, x)

Tagged each array with units:

t7u = t7.values * units.degC
u7u = u7.values * units.knots
v7u = v7.values * units.knots

Then I go to compute FGEN:

h7_fgen = mpcalc.frontogenesis(t7u,u7u,v7u,dx,dy,dim_order='xy')

which produces the following error:

ValueError: operands could not be broadcast together with shapes (359,219) (358,220)

The shapes of the temperature, u, and v arrays respectively are:

(220, 360)
(220, 360)
(220, 360)

So while dx and dy will understandably have lengths n-1, I'm not sure why this isn't working.


Solution

  • If pulling data directly from the NOMADS GFS source, it looks like your data should be in yx dimension order (which is also the order assumed by MetPy's lat_lon_grid_deltas function). And so, I would presume that something got messed up in the script with regards to dimension ordering. Resolving this, and making additional use of some of MetPy's xarray integration features to clean up the implementation, we have:

    Pre MetPy v1.0

    # Imports
    import metpy.calc as mpcalc
    from metpy.units import units
    import xarray as xr
    
    # Open data and parse for CRS
    data = xr.open_dataset('http://nomads.ncep.noaa.gov/dods/gfs_0p25_1hr/gfs20201230/gfs_0p25_1hr_12z').metpy.parse_cf()
    
    # Subset out the desired data
    # Note that this leaves in time, but that can also be selected out if only
    # one time is needed and you want to have smaller data arrays to compute on
    t7 = data['tmpprs'].sel(lev=700.0,lat=slice(15, 70),lon=slice(220, 310))
    u7 = data['ugrdprs'].sel(lev=700.0,lat=slice(15, 70),lon=slice(220, 310)).squeeze()
    v7 = data['vgrdprs'].sel(lev=700.0,lat=slice(15, 70),lon=slice(220, 310)).squeeze()
    h7 = data['hgtprs'].sel(lev=700.0,lat=slice(15, 70),lon=slice(220, 310)).squeeze()
    
    # Clean up the xarray metadata for MetPy's use, since provided data is missing
    # the units attribute
    t7.attrs['units'] = 'K'
    u7.attrs['units'] = v7.attrs['units'] = 'm/s'
    h7.attrs['units'] = 'm'
    
    # Get grid deltas
    dx, dy = mpcalc.grid_deltas_from_dataarray(t7)
    
    # Compute frontogenesis
    h7_fgen = mpcalc.frontogenesis(t7, u7, v7, dx, dy)
    

    Post MetPy v1.0

    In MetPy v1.0, the grid arguments are automatically extracted from DataArray inputs, and so, this can be simplified to

    # Imports
    import metpy.calc as mpcalc
    from metpy.units import units
    import xarray as xr
    
    # Open data and parse for CRS
    data = xr.open_dataset('http://nomads.ncep.noaa.gov/dods/gfs_0p25_1hr/gfs20201230/gfs_0p25_1hr_12z').metpy.parse_cf()
    
    # Subset out the desired data
    t7 = data['tmpprs'].sel(lev=700.0,lat=slice(15, 70),lon=slice(220, 310))
    u7 = data['ugrdprs'].sel(lev=700.0,lat=slice(15, 70),lon=slice(220, 310)).squeeze()
    v7 = data['vgrdprs'].sel(lev=700.0,lat=slice(15, 70),lon=slice(220, 310)).squeeze()
    h7 = data['hgtprs'].sel(lev=700.0,lat=slice(15, 70),lon=slice(220, 310)).squeeze()
    
    # Clean up the xarray metadata for MetPy's use, since provided data is missing
    # the units attribute
    t7.attrs['units'] = 'K'
    u7.attrs['units'] = v7.attrs['units'] = 'm/s'
    h7.attrs['units'] = 'm'
    
    # Compute frontogenesis
    h7_fgen = mpcalc.frontogenesis(t7, u7, v7)