import sys import numpy as np from netCDF4 import Dataset import os import time from pyproj import CRS, Transformer # C. Pelletier july 2021. # Example of using PYPROJ to manage projections. # I highly recommend using this tool, as you're then relying on something documented from projecting (i.e. from lat/lon to stereographic) # This is pretty useful for ice sheet / ocean coupling and pre/post processing. since most ice sheet models are defined on polar stereographic grids. # This script just gets the grid corners of the BISICLE grid. # But it's an illustration of how to use PYPROJ. # # grid_bisicles = "/afast/pelletie/totten/BISICLES-smaller_grid.nc" output_file = "/afast/pelletie/totten/BISICLES-smaller_grid_corners.nc" # description of the projection. proj_polster = "+proj=stere +lat_0=-90.0 +lat_ts=-71. +lon_0=-90." # The projection python object crs_polster = CRS.from_proj4(proj_polster) tmpdat = Dataset(grid_bisicles, mode='r') # Center of cells in stereographic projection. No idea why X and Y are interverted. x_ctn = tmpdat.variables['Y'][:] y_ctn = tmpdat.variables['X'][:] mask = tmpdat.variables['MASK'][:].astype(int) tmpdat.close() nx = np.size(x_ctn) ny = np.size(y_ctn) # Edges of cell in stereographics projection. This is just the middle of two consecutive cells. And a bit of symmetry for the edges. x_stg = np.ndarray(shape=[nx+1], dtype=float) y_stg = np.ndarray(shape=[ny+1], dtype=float) x_stg[1:-1] = .5 * ( x_ctn[:-1] + x_ctn[1:] ) x_stg[0] = 2 * x_ctn[0] - x_stg[1] x_stg[-1] = 2 * x_ctn[-1] - x_stg[-2] y_stg[1:-1] = .5 * ( y_ctn[:-1] + y_ctn[1:] ) y_stg[0] = 2 * y_ctn[0] - y_stg[1] y_stg[-1] = 2 * y_ctn[-1] - y_stg[-2] # PYPROJ transformation object, polster -> latlon # .geodetic_crs is always the latlon projection, regardless of polster_to_latlon = Transformer.from_crs(crs_polster, crs_polster.geodetic_crs) # Get lon, lat of centers from x,y (which are broadcasted with [None,:] etc.) lon_ctn, lat_ctn = polster_to_latlon.transform(x_ctn[None,:] * (np.ones(shape=[ny], dtype=float))[:,None], \ y_ctn[:,None] * (np.ones(shape=[nx], dtype=float))[None,:]) # No idea why I'm doing this. I guess everything's corked and dirty. lon_ctn = - lon_ctn # Reformat corners in (y,x,4) instead of (y+1,x+1). x_crn = np.ndarray(shape=[ny, nx, 4], dtype=float) y_crn = np.ndarray(shape=[ny, nx, 4], dtype=float) x_crn[:,:,0] = (x_stg[1:])[None,:] * (np.ones(shape=[ny], dtype=float))[:,None] y_crn[:,:,0] = (y_stg[1:])[:,None] * (np.ones(shape=[nx], dtype=float))[None,:] x_crn[:,:,1] = (x_stg[1:])[None,:] * (np.ones(shape=[ny], dtype=float))[:,None] y_crn[:,:,1] = (y_stg[:-1])[:,None] * (np.ones(shape=[nx], dtype=float))[None,:] x_crn[:,:,2] = (x_stg[:-1])[None,:] * (np.ones(shape=[ny], dtype=float))[:,None] y_crn[:,:,2] = (y_stg[:-1])[:,None] * (np.ones(shape=[nx], dtype=float))[None,:] x_crn[:,:,3] = (x_stg[:-1])[None,:] * (np.ones(shape=[ny], dtype=float))[:,None] y_crn[:,:,3] = (y_stg[1:])[:,None] * (np.ones(shape=[nx], dtype=float))[None,:] lon_crn = np.ndarray(shape=[ny, nx, 4], dtype=float) lat_crn = np.ndarray(shape=[ny, nx, 4], dtype=float) for m in range(0,4): lon_crn[:,:,m], lat_crn[:,:,m] = polster_to_latlon.transform(x_crn[:,:,m], y_crn[:,:,m]) lon_crn = - lon_crn out_nc = Dataset(output_file, mode='w') out_nc.createDimension('y', ny) out_nc.createDimension('x', nx) out_nc.createDimension('crn', 4) tmp = out_nc.createVariable('lat', datatype='f8', dimensions=['y', 'x']) tmp[:] = lat_ctn tmp.standard_name = 'latitude' tmp.units = 'degrees North' tmp.bounds = 'lat_corn' tmp = out_nc.createVariable('lat_corn', datatype='f8', dimensions=['y', 'x','crn']) tmp[:] = lat_crn tmp = out_nc.createVariable('lon', datatype='f8', dimensions=['y', 'x']) tmp[:] = lon_ctn tmp.standard_name = 'longitude' tmp.units = 'degrees East' tmp.bounds = 'lon_corn' tmp = out_nc.createVariable('lon_corn', datatype='f8', dimensions=['y', 'x','crn']) tmp[:] = lon_crn tmp = out_nc.createVariable('mask', datatype='i2', dimensions=['y', 'x']) tmp[:] = mask tmp.coordinates = 'lat lon' out_nc.close()