# Create a SCRIP file from an MPAS mesh.
# See for details: http://www.earthsystemmodeling.org/esmf_releases/public/ESMF_5_2_0rp1/ESMF_refdoc/node3.html#SECTION03024000000000000000
import sys
import netCDF4
import numpy as np
from optparse import OptionParser
from mpas_tools.cime.constants import constants
[docs]
def scrip_from_mpas(mpasFile, scripFile, useLandIceMask=False):
    """
    Create a SCRIP file from an MPAS mesh
    Parameters
    ----------
    mpasFile : str
        The path to a NetCDF file with the MPAS mesh
    scripFile : str
        The path to the output SCRIP file
    useLandIceMask : bool
        Whether to use the landIceMask field for masking
    """
    if useLandIceMask:
        print(" -- Landice Masks are enabled")
    else:
        print(" -- Landice Masks are disabled")
    # make a space in stdout before further output
    print('')
    fin = netCDF4.Dataset(mpasFile, 'r')
    # This will clobber existing files
    fout = netCDF4.Dataset(scripFile, 'w')
    # Get info from input file
    latCell = fin.variables['latCell'][:]
    lonCell = fin.variables['lonCell'][:]
    latVertex = fin.variables['latVertex'][:]
    lonVertex = fin.variables['lonVertex'][:]
    verticesOnCell = fin.variables['verticesOnCell'][:] - 1
    nEdgesOnCell = fin.variables['nEdgesOnCell'][:]
    nCells = len(fin.dimensions['nCells'])
    maxVertices = len(fin.dimensions['maxEdges'])
    areaCell = fin.variables['areaCell'][:]
    sphereRadius = float(fin.sphere_radius)
    on_a_sphere = str(fin.on_a_sphere)
    # check the longitude convention to use positive values [0 2pi]
    if np.any(np.logical_or(lonCell < 0, lonCell > 2.0 * np.pi)):
       raise ValueError("lonCell is not in the desired range (0, 2pi)")
    if np.any(np.logical_or(lonVertex < 0, lonVertex > 2.0 * np.pi)):
       raise ValueError("lonVertex is not in the desired range (0, 2pi)")
    if sphereRadius <= 0:
       sphereRadius =  constants['SHR_CONST_REARTH']
       print(f" -- WARNING: sphereRadius<0 so setting sphereRadius = "
             f"{constants['SHR_CONST_REARTH']}")
    if on_a_sphere == "NO":
        print(" -- WARNING: 'on_a_sphere' attribute is 'NO', which means that "
              "there may be some disagreement regarding area between the "
              "planar (source) and spherical (target) mesh")
    if useLandIceMask:
        landIceMask = fin.variables['landIceMask'][:]
    else:
        landIceMask = None
    # Write to output file
    # Dimensions
    fout.createDimension("grid_size", nCells)
    fout.createDimension("grid_corners", maxVertices)
    fout.createDimension("grid_rank", 1)
    # Variables
    grid_center_lat = fout.createVariable('grid_center_lat', 'f8',
                                          ('grid_size',))
    grid_center_lat.units = 'radians'
    grid_center_lon = fout.createVariable('grid_center_lon', 'f8',
                                          ('grid_size',))
    grid_center_lon.units = 'radians'
    grid_corner_lat = fout.createVariable('grid_corner_lat', 'f8',
                                          ('grid_size', 'grid_corners'))
    grid_corner_lat.units = 'radians'
    grid_corner_lon = fout.createVariable('grid_corner_lon', 'f8',
                                          ('grid_size', 'grid_corners'))
    grid_corner_lon.units = 'radians'
    grid_area = fout.createVariable('grid_area', 'f8', ('grid_size',))
    grid_area.units = 'radian^2'
    grid_imask = fout.createVariable('grid_imask', 'i4', ('grid_size',))
    grid_imask.units = 'unitless'
    grid_dims = fout.createVariable('grid_dims', 'i4', ('grid_rank',))
    grid_center_lat[:] = latCell[:]
    grid_center_lon[:] = lonCell[:]
    # SCRIP uses square radians
    grid_area[:] = areaCell[:]/(sphereRadius**2)
    grid_dims[:] = nCells
    # grid corners:
    grid_corner_lon_local = np.zeros((nCells, maxVertices))
    grid_corner_lat_local = np.zeros((nCells, maxVertices))
    cellIndices = np.arange(nCells)
    lastValidVertex = verticesOnCell[cellIndices, nEdgesOnCell-1]
    for iVertex in range(maxVertices):
        mask = iVertex < nEdgesOnCell
        grid_corner_lat_local[mask, iVertex] = \
            
latVertex[verticesOnCell[mask, iVertex]]
        grid_corner_lon_local[mask, iVertex] = \
            
lonVertex[verticesOnCell[mask, iVertex]]
        mask = iVertex >= nEdgesOnCell
        grid_corner_lat_local[mask, iVertex] = latVertex[lastValidVertex[mask]]
        grid_corner_lon_local[mask, iVertex] = lonVertex[lastValidVertex[mask]]
    if useLandIceMask:
        # If useLandIceMask are enabled, mask out ocean under land ice.
        grid_imask[:] = 1 - landIceMask[0, :]
    else:
        # If landiceMasks are not enabled, don't mask anything out.
        grid_imask[:] = 1
    grid_corner_lat[:] = grid_corner_lat_local[:]
    grid_corner_lon[:] = grid_corner_lon_local[:]
    print("Input latCell min/max values (radians): {}, {}".format(
        latCell[:].min(), latCell[:].max()))
    print("Input lonCell min/max values (radians): {}, {}".format(
        lonCell[:].min(), lonCell[:].max()))
    print("Calculated grid_center_lat min/max values (radians): {}, {}".format(
         grid_center_lat[:].min(), grid_center_lat[:].max()))
    print("Calculated grid_center_lon min/max values (radians): {}, {}".format(
        grid_center_lon[:].min(), grid_center_lon[:].max()))
    print("Calculated grid_area min/max values (sq radians): {}, {}".format(
        grid_area[:].min(), grid_area[:].max()))
    fin.close()
    fout.close()
    print("Creation of SCRIP file is complete.") 
def main():
    print("== Gathering information.  (Invoke with --help for more details. All"
          " arguments are optional)")
    parser = OptionParser()
    parser.description = "This script takes an MPAS grid file and generates " \
                         "a SCRIP grid file."
    parser.add_option("-m", "--mpas", dest="mpasFile",
                      help="MPAS grid file name used as input.",
                      default="grid.nc", metavar="FILENAME")
    parser.add_option("-s", "--scrip", dest="scripFile",
                      help="SCRIP grid file to output.", default="scrip.nc",
                      metavar="FILENAME")
    parser.add_option("-l", "--landice", dest="landiceMasks",
                      help="If flag is on, landice masks will be computed "
                           "and used.",
                      action="store_true")
    for option in parser.option_list:
        if option.default != ("NO", "DEFAULT"):
            option.help += (" " if option.help else "") + "[default: %default]"
    options, args = parser.parse_args()
    if not options.mpasFile:
        sys.exit('Error: MPAS input grid file is required.  Specify with -m '
                 'command line argument.')
    if not options.scripFile:
        sys.exit('Error: SCRIP output grid file is required.  Specify with -s '
                 'command line argument.')
    if not options.landiceMasks:
        options.landiceMasks = False
    scrip_from_mpas(options.mpasFile, options.scripFile, options.landiceMasks)