Пример #1
0
def cellWidthVsLatLon():
    """
    Create cell width array for this mesh on a regular latitude-longitude grid.
    Returns
    -------
       cellWidth : ndarray
            m x n array, entries are desired cell width in km

       lat : ndarray
            latitude, vector of length m, with entries between -90 and 90,
            degrees

       lon : ndarray
            longitude, vector of length n, with entries between -180 and 180,
            degrees
    """

    dlon = 0.1
    dlat = dlon
    nlon = int(360./dlon) + 1
    nlat = int(180./dlat) + 1
    lon = np.linspace(-180., 180., nlon)
    lat = np.linspace(-90., 90., nlat)

    cellWidthVsLat = mdt.EC_CellWidthVsLat(lat)

    _, cellWidth = np.meshgrid(lon, cellWidthVsLat)

    fc = read_feature_collection('north_mid_res_region.geojson')

    earth_radius = constants['SHR_CONST_REARTH']

    mr_signed_distance = signed_distance_from_geojson(fc, lon, lat,
                                                      earth_radius,
                                                      max_length=0.25)

    fc = read_feature_collection('arctic_high_res_region.geojson')

    hr_signed_distance = signed_distance_from_geojson(fc, lon, lat,
                                                      earth_radius,
                                                      max_length=0.25)

    frac = (-mr_signed_distance / (-mr_signed_distance + hr_signed_distance))

    frac = np.maximum(0., np.minimum(1., frac))

    dx_min = 15.
    dx_max = 30.

    arctic_widths = dx_max + (dx_min - dx_max) * frac

    trans_width = 1000e3
    trans_start = 0.

    weights = 0.5 * (1 + np.tanh((mr_signed_distance - trans_start) /
                                 trans_width))

    cellWidth = arctic_widths * (1 - weights) + cellWidth * weights

    return cellWidth, lon, lat
Пример #2
0
def entry_point_compute_mpas_flood_fill_mask():
    """ Entry point for ``compute_mpas_flood_fill_mask()``"""

    parser = argparse.ArgumentParser()
    parser.add_argument("-m", "--mesh_file_name", dest="mesh_file_name",
                        type=str, required=True,
                        help="An MPAS mesh file")
    parser.add_argument("-g", "--geojson_file_name",
                        dest="geojson_file_name", type=str, required=True,
                        help="An Geojson file containing points at which to "
                             "start the flood fill")
    parser.add_argument("-o", "--mask_file_name", dest="mask_file_name",
                        type=str, required=True,
                        help="An output MPAS region masks file")
    parser.add_argument("--format", dest="format", type=str,
                        help="NetCDF file format")
    parser.add_argument("--engine", dest="engine", type=str,
                        help="NetCDF output engine")
    args = parser.parse_args()

    dsMesh = xr.open_dataset(args.mesh_file_name, decode_cf=False,
                             decode_times=False)
    fcSeed = read_feature_collection(args.geojson_file_name)

    with LoggingContext('compute_mpas_flood_fill_mask') as logger:
        dsMasks = compute_mpas_flood_fill_mask(
            dsMesh=dsMesh, fcSeed=fcSeed, logger=logger)

    write_netcdf(dsMasks, args.mask_file_name, format=args.format,
                 engine=args.engine)
Пример #3
0
def plot_regions(ax):

    ax.set_extent([0, 359.9, 50, 90], crs=ccrs.PlateCarree())
    ax.add_feature(cfeature.LAND, alpha=1, zorder=100)
    #ax.add_feature(cfeature.LAKES, alpha=1, zorder=101)
    ax.add_feature(cfeature.COASTLINE, zorder=101)
    ax.gridlines(linestyle='dotted')

    for i, region in enumerate(regions):

        path = regions_path + region
        fc = read_feature_collection(path + '/region.geojson')
        #pprint.pprint(fc.features)

        shape = []
        for s in fc.features[0]['geometry']['coordinates']:
            if len(s) == 1:
                shp = shapely.geometry.Polygon(s[0])
            else:
                shp = shapely.geometry.Polygon(s)
            shape.append(shp)

        ax.add_geometries(shape, crs=ccrs.PlateCarree(), facecolor=cmap[i])
        center = list(shape[0].centroid.coords)[0]

        ax.text(center[0] + offset[region][0],
                center[1] + offset[region][1],
                region.replace('_NSIDC', '').replace('_', '\n'),
                transform=ccrs.PlateCarree(),
                ha='center',
                va='center',
                zorder=102,
                fontsize=9)
def compute_mpas_region_masks(geojsonFileName, meshFileName, maskFileName,
                              featureList=None, logger=None, processCount=1,
                              chunkSize=1000, showProgress=True,
                              useMpasMaskCreator=False, dir=None):
    """
    Build a region mask file from the given MPAS mesh and geojson file defining
    a set of regions.
    """
    if os.path.exists(maskFileName):
        return

    if useMpasMaskCreator:
        dsMesh = xr.open_dataset(meshFileName)
        fcMask = read_feature_collection(geojsonFileName)
        dsMasks = mpas_tools.conversion.mask(dsMesh=dsMesh, fcMask=fcMask,
                                             logger=logger, dir=dir)

    else:
        with xr.open_dataset(meshFileName) as dsMesh:
            dsMesh = dsMesh[['lonCell', 'latCell']]
            latCell = numpy.rad2deg(dsMesh.latCell.values)

            # transform longitudes to [-180, 180)
            lonCell = numpy.mod(numpy.rad2deg(dsMesh.lonCell.values) + 180.,
                                360.) - 180.

        # create shapely geometry for lonCell and latCell
        cellPoints = [shapely.geometry.Point(x, y) for x, y in
                      zip(lonCell, latCell)]

        regionNames, masks, properties, nChar = compute_region_masks(
            geojsonFileName, cellPoints, maskFileName, featureList, logger,
            processCount, chunkSize, showProgress)

        nCells = len(cellPoints)

        # create a new data array for masks and another for mask names
        if logger is not None:
            logger.info('  Creating and writing masks dataset...')
        nRegions = len(regionNames)
        dsMasks = xr.Dataset()
        dsMasks['regionCellMasks'] = (('nRegions', 'nCells'),
                                      numpy.zeros((nRegions, nCells), dtype=bool))
        dsMasks['regionNames'] = (('nRegions'),
                                  numpy.zeros((nRegions),
                                              dtype='|S{}'.format(nChar)))

        for index in range(nRegions):
            regionName = regionNames[index]
            mask = masks[index]
            dsMasks['regionCellMasks'][index, :] = mask
            dsMasks['regionNames'][index] = regionName

        for propertyName in properties:
            dsMasks[propertyName] = (('nRegions'), properties[propertyName])

    write_netcdf(dsMasks, maskFileName)
Пример #5
0
def test_conversion():
    dsMesh = xarray.open_dataset(
        'mesh_tools/mesh_conversion_tools/test/mesh.QU.1920km.151026.nc')
    dsMesh = convert(dsIn=dsMesh)
    write_netcdf(dsMesh, 'mesh.nc')

    dsMask = xarray.open_dataset(
        'mesh_tools/mesh_conversion_tools/test/land_mask_final.nc')
    dsCulled = cull(dsIn=dsMesh, dsMask=dsMask)
    write_netcdf(dsCulled, 'culled_mesh.nc')

    fcMask = read_feature_collection(
        'mesh_tools/mesh_conversion_tools/test/Arctic_Ocean.geojson')
    dsMask = mask(dsMesh=dsMesh, fcMask=fcMask)
    write_netcdf(dsMask, 'antarctic_mask.nc')
def compute_mpas_transect_masks(geojsonFileName,
                                meshFileName,
                                maskFileName,
                                logger=None):
    """
    Build a transect mask file from the given MPAS mesh and geojson file \
    defining a set of transects.
    """
    if os.path.exists(maskFileName):
        return

    dsMesh = xr.open_dataset(meshFileName)
    fcMask = read_feature_collection(geojsonFileName)
    dsMask = mpas_tools.conversion.mask(dsMesh=dsMesh,
                                        fcMask=fcMask,
                                        logger=logger)

    write_netcdf(dsMask, maskFileName)
Пример #7
0
    def run_task(self):  # {{{
        """
        Plots time-series output of Antarctic sub-ice-shelf melt rates.
        """
        # Authors
        # -------
        # Xylar Asay-Davis, Stephen Price

        self.logger.info("\nPlotting Antarctic melt rate time series for "
                         "{}...".format(self.iceShelf))

        self.logger.info('  Load melt rate data...')

        config = self.config
        calendar = self.calendar

        iceShelfMasksFile = self.iceShelfMasksFile

        fcAll = read_feature_collection(iceShelfMasksFile)

        fc = FeatureCollection()
        for feature in fcAll.features:
            if feature['properties']['name'] == self.iceShelf:
                fc.add_feature(feature)
                break

        totalMeltFlux, meltRates = self._load_ice_shelf_fluxes(config)

        plotControl = self.controlConfig is not None
        if plotControl:
            controlRunName = self.controlConfig.get('runs', 'mainRunName')

            refTotalMeltFlux, refMeltRates = \
                self._load_ice_shelf_fluxes(self.controlConfig)

        # Load observations from multiple files and put in dictionary based
        # on shelf keyname
        observationsDirectory = build_obs_path(config, 'ocean',
                                               'meltSubdirectory')
        obsFileNameDict = {'Rignot et al. (2013)':
                           'Rignot_2013_melt_rates_20200623.csv',
                           'Rignot et al. (2013) SS':
                           'Rignot_2013_melt_rates_SS_20200623.csv'}

        obsDict = {}  # dict for storing dict of obs data
        for obsName in obsFileNameDict:
            obsFileName = '{}/{}'.format(observationsDirectory,
                                         obsFileNameDict[obsName])
            obsDict[obsName] = {}
            obsFile = csv.reader(open(obsFileName, 'rU'))
            next(obsFile, None)  # skip the header line
            for line in obsFile:  # some later useful values commented out
                shelfName = line[0]
                if shelfName != self.iceShelf:
                    continue

                # surveyArea = line[1]
                meltFlux = float(line[2])
                meltFluxUncertainty = float(line[3])
                meltRate = float(line[4])
                meltRateUncertainty = float(line[5])
                # actualArea = float( line[6] )  # actual area here is in sq km

                # build dict of obs. keyed to filename description
                # (which will be used for plotting)
                obsDict[obsName] = {
                    'meltFlux': meltFlux,
                    'meltFluxUncertainty': meltFluxUncertainty,
                    'meltRate': meltRate,
                    'meltRateUncertainty': meltRateUncertainty}
                break

        # If areas from obs file used need to be converted from sq km to sq m

        mainRunName = config.get('runs', 'mainRunName')
        movingAverageMonths = config.getint('timeSeriesAntarcticMelt',
                                            'movingAverageMonths')

        outputDirectory = build_config_full_path(config, 'output',
                                                 'timeseriesSubdirectory')

        make_directories(outputDirectory)

        self.logger.info('  Make plots...')

        # get obs melt flux and unc. for shelf (similar for rates)
        obsMeltFlux = []
        obsMeltFluxUnc = []
        obsMeltRate = []
        obsMeltRateUnc = []
        for obsName in obsDict:
            if len(obsDict[obsName]) > 0:
                obsMeltFlux.append(
                    obsDict[obsName]['meltFlux'])
                obsMeltFluxUnc.append(
                    obsDict[obsName]['meltFluxUncertainty'])
                obsMeltRate.append(
                    obsDict[obsName]['meltRate'])
                obsMeltRateUnc.append(
                    obsDict[obsName]['meltRateUncertainty'])
            else:
                # append NaN so this particular obs won't plot
                self.logger.warning('{} observations not available for '
                                    '{}'.format(obsName, self.iceShelf))
                obsMeltFlux.append(None)
                obsMeltFluxUnc.append(None)
                obsMeltRate.append(None)
                obsMeltRateUnc.append(None)

        title = self.iceShelf.replace('_', ' ')

        xLabel = 'Time (yr)'
        yLabel = 'Melt Flux (GT/yr)'

        timeSeries = totalMeltFlux.isel(nRegions=self.regionIndex)

        filePrefix = 'melt_flux_{}'.format(self.iceShelf.replace(' ', '_'))
        outFileName = '{}/{}.png'.format(self.plotsDirectory, filePrefix)

        fields = [timeSeries]
        lineColors = ['k']
        lineWidths = [2.5]
        legendText = [mainRunName]
        if plotControl:
            fields.append(refTotalMeltFlux.isel(nRegions=self.regionIndex))
            lineColors.append('r')
            lineWidths.append(1.2)
            legendText.append(controlRunName)

        fig = timeseries_analysis_plot(config, fields, calendar=calendar,
                                       title=title, xlabel=xLabel,
                                       ylabel=yLabel,
                                       movingAveragePoints=movingAverageMonths,
                                       lineColors=lineColors,
                                       lineWidths=lineWidths,
                                       legendText=legendText,
                                       obsMean=obsMeltFlux,
                                       obsUncertainty=obsMeltFluxUnc,
                                       obsLegend=list(obsDict.keys()))

        # do this before the inset because otherwise it moves the inset
        # and cartopy doesn't play too well with tight_layout anyway
        plt.tight_layout()

        add_inset(fig, fc, width=2.0, height=2.0)

        savefig(outFileName)

        caption = 'Running Mean of Total Melt Flux  under Ice ' \
                  'Shelves in the {} Region'.format(title)
        write_image_xml(
            config=config,
            filePrefix=filePrefix,
            componentName='Ocean',
            componentSubdirectory='ocean',
            galleryGroup='Antarctic Melt Time Series',
            groupLink='antmelttime',
            gallery='Total Melt Flux',
            thumbnailDescription=title,
            imageDescription=caption,
            imageCaption=caption)

        xLabel = 'Time (yr)'
        yLabel = 'Melt Rate (m/yr)'

        timeSeries = meltRates.isel(nRegions=self.regionIndex)

        filePrefix = 'melt_rate_{}'.format(self.iceShelf.replace(' ', '_'))
        outFileName = '{}/{}.png'.format(self.plotsDirectory, filePrefix)

        fields = [timeSeries]
        lineColors = ['k']
        lineWidths = [2.5]
        legendText = [mainRunName]
        if plotControl:
            fields.append(refMeltRates.isel(nRegions=self.regionIndex))
            lineColors.append('r')
            lineWidths.append(1.2)
            legendText.append(controlRunName)

        if config.has_option(self.taskName, 'firstYearXTicks'):
            firstYearXTicks = config.getint(self.taskName,
                                            'firstYearXTicks')
        else:
            firstYearXTicks = None

        if config.has_option(self.taskName, 'yearStrideXTicks'):
            yearStrideXTicks = config.getint(self.taskName,
                                             'yearStrideXTicks')
        else:
            yearStrideXTicks = None

        fig = timeseries_analysis_plot(config, fields, calendar=calendar,
                                       title=title, xlabel=xLabel,
                                       ylabel=yLabel,
                                       movingAveragePoints=movingAverageMonths,
                                       lineColors=lineColors,
                                       lineWidths=lineWidths,
                                       legendText=legendText,
                                       firstYearXTicks=firstYearXTicks,
                                       yearStrideXTicks=yearStrideXTicks,
                                       obsMean=obsMeltRate,
                                       obsUncertainty=obsMeltRateUnc,
                                       obsLegend=list(obsDict.keys()))

        # do this before the inset because otherwise it moves the inset
        # and cartopy doesn't play too well with tight_layout anyway
        plt.tight_layout()

        add_inset(fig, fc, width=2.0, height=2.0)

        savefig(outFileName)

        caption = 'Running Mean of Area-averaged Melt Rate under Ice ' \
                  'Shelves in the {} Region'.format(title)
        write_image_xml(
            config=config,
            filePrefix=filePrefix,
            componentName='Ocean',
            componentSubdirectory='ocean',
            galleryGroup='Antarctic Melt Time Series',
            groupLink='antmelttime',
            gallery='Area-averaged Melt Rate',
            thumbnailDescription=title,
            imageDescription=caption,
            imageCaption=caption)
Пример #8
0
def cellWidthVsLatLon():
    """
    Create cell width array for this mesh on a regular latitude-longitude grid.
    Returns
    -------
       cellWidth : numpy.ndarray
            m x n array, entries are desired cell width in km
       lat : numpy.ndarray
            latitude, vector of length m, with entries between -90 and 90,
            degrees
       lon : numpy.ndarray
            longitude, vector of length n, with entries between -180 and 180,
            degrees
    """
    # To speed up for testing, set following line to 1.0 degrees
    dlon = 1.0
    dlat = dlon
    nlon = int(360. / dlon) + 1
    nlat = int(180. / dlat) + 1
    lon = np.linspace(-180., 180., nlon)
    lat = np.linspace(-90., 90., nlat)

    # Create cell width vs latitude for Atlantic and Pacific basins
    QU1 = np.ones(lat.size)
    EC60to30 = mdt.EC_CellWidthVsLat(lat)
    RRS30to10 = mdt.RRS_CellWidthVsLat(lat, 30, 10)
    AtlNH = RRS30to10
    AtlVsLat = mdt.mergeCellWidthVsLat(lat, EC60to30, AtlNH, 0, 6)
    PacNH = mdt.mergeCellWidthVsLat(lat, 30 * QU1, RRS30to10, 50, 10)
    PacVsLat = mdt.mergeCellWidthVsLat(lat, EC60to30, PacNH, 0, 6)

    # Expand from 1D to 2D
    _, AtlGrid = np.meshgrid(lon, AtlVsLat)
    _, PacGrid = np.meshgrid(lon, PacVsLat)

    # Signed distance of Atlantic region
    fc = read_feature_collection('Atlantic_region.geojson')
    signedDistance = signed_distance_from_geojson(fc,
                                                  lon,
                                                  lat,
                                                  max_length=0.25)

    # Merge Atlantic and Pacific distrubutions smoothly
    transitionWidth = 500.0e3  # [m]
    maskSmooth = 0.5 * (1 + np.tanh(signedDistance / transitionWidth))
    cellWidthSmooth = PacGrid * maskSmooth + AtlGrid * (1 - maskSmooth)

    # Merge Atlantic and Pacific distrubutions with step function
    maskSharp = 0.5 * (1 + np.sign(signedDistance))
    cellWidthSharp = PacGrid * maskSharp + AtlGrid * (1 - maskSharp)

    # Create a land mask that is 1 over land
    fc = read_feature_collection('Americas_land_mask.geojson')
    Americas_land_mask = mask_from_geojson(fc, lon, lat)
    fc = read_feature_collection('Europe_Africa_land_mask.geojson')
    Europe_Africa_land_mask = mask_from_geojson(fc, lon, lat)
    landMask = np.fmax(Americas_land_mask, Europe_Africa_land_mask)

    # Merge: step transition over land, smooth transition over water
    cellWidth = cellWidthSharp * landMask + cellWidthSmooth * (1 - landMask)

    # save signed distance to a file
    # da = xarray.DataArray(signedDistance,
    #                      dims=['y', 'x'],
    #                      coords={'y': lat, 'x': lon},
    #                      name='signedDistance')
    #cw_filename = 'signedDistance.nc'
    # da.to_netcdf(cw_filename)

    print('plotting ...')
    fig = plt.figure()
    plt.clf()
    fig.set_size_inches(10.0, 10.0)

    ax = plt.subplot(4, 2, 1)
    ax.plot(lat, AtlVsLat, label='Atlantic')
    ax.plot(lat, PacVsLat, label='Pacific')
    ax.grid(True)
    plt.title('Grid cell size [km] versus latitude')
    plt.legend()

    varNames = [
        'signedDistance', 'maskSmooth', 'cellWidthSmooth', 'maskSharp',
        'cellWidthSharp', 'landMask', 'cellWidth'
    ]
    j = 2
    for varName in varNames:
        plot_cartopy(j, varName, vars()[varName])
        j += 1

    plt.savefig('mesh_construction.png')

    return cellWidth, lon, lat
Пример #9
0
    def build_cell_width_lat_lon(self):
        """
        Create cell width array for this mesh on a regular latitude-longitude
        grid

        Returns
        -------
        cellWidth : numpy.array
            m x n array of cell width in km

        lon : numpy.array
            longitude in degrees (length n and between -180 and 180)

        lat : numpy.array
            longitude in degrees (length m and between -90 and 90)
        """

        dlon = 0.1
        dlat = dlon
        earth_radius = constants['SHR_CONST_REARTH']
        nlon = int(360./dlon) + 1
        nlat = int(180./dlat) + 1
        lon = np.linspace(-180., 180., nlon)
        lat = np.linspace(-90., 90., nlat)

        cellWidthSouth = mdt.EC_CellWidthVsLat(lat, cellWidthEq=30.,
                                               cellWidthMidLat=45.,
                                               cellWidthPole=45.,
                                               latPosEq=7.5, latWidthEq=3.0)

        cellWidthNorth = mdt.EC_CellWidthVsLat(lat, cellWidthEq=30.,
                                               cellWidthMidLat=60.,
                                               cellWidthPole=35.,
                                               latPosEq=7.5, latWidthEq=3.0)

        # Transition at Equator
        latTransition = 0.0
        latWidthTransition = 2.5
        cellWidthVsLat = mdt.mergeCellWidthVsLat(
            lat,
            cellWidthSouth,
            cellWidthNorth,
            latTransition,
            latWidthTransition)

        _, cellWidth = np.meshgrid(lon, cellWidthVsLat)

        cellWidthAtlantic = mdt.EC_CellWidthVsLat(lat, cellWidthEq=30.,
                                                  cellWidthMidLat=30.,
                                                  cellWidthPole=35.,
                                                  latPosEq=7.5, latWidthEq=3.0)

        cellWidthAtlantic = mdt.mergeCellWidthVsLat(
            lat,
            cellWidthSouth,
            cellWidthAtlantic,
            latTransition,
            latWidthTransition)

        _, cellWidthAtlantic = np.meshgrid(lon, cellWidthAtlantic)

        fc = read_feature_collection('atlantic.geojson')

        atlantic_signed_distance = signed_distance_from_geojson(
            fc, lon, lat, earth_radius, max_length=0.25)

        trans_width = 400e3
        trans_start = 0.
        weights = 0.5 * (1 + np.tanh((atlantic_signed_distance - trans_start) /
                                     trans_width))

        cellWidth = cellWidthAtlantic * (1 - weights) + cellWidth * weights

        fc = read_feature_collection('high_res_region.geojson')

        so_signed_distance = signed_distance_from_geojson(fc, lon, lat,
                                                          earth_radius,
                                                          max_length=0.25)

        # Equivalent to 20 degrees latitude
        trans_width = 1600e3
        trans_start = 500e3
        dx_min = 12.

        weights = 0.5 * (1 + np.tanh((so_signed_distance - trans_start) /
                                     trans_width))

        cellWidth = dx_min * (1 - weights) + cellWidth * weights

        return cellWidth, lon, lat
Пример #10
0
def entry_point_compute_projection_grid_region_masks():
    """ Entry point for ``compute_projection_grid_region_masks()``"""

    parser = argparse.ArgumentParser()
    parser.add_argument("-i", "--grid_file_name", dest="grid_file_name",
                        type=str, required=True,
                        help="An input lon/lat grid file")
    parser.add_argument("--lon", dest="lon", default="lon", type=str,
                        help="The name of the 2D longitude coordinate")
    parser.add_argument("--lat", dest="lat", default="lat", type=str,
                        help="The name of the 2D latitude coordinate")
    parser.add_argument("-g", "--geojson_file_name",
                        dest="geojson_file_name", type=str, required=True,
                        help="An Geojson file containing mask regions")
    parser.add_argument("-o", "--mask_file_name", dest="mask_file_name",
                        type=str, required=True,
                        help="An output MPAS region masks file")
    parser.add_argument("-c", "--chunk_size", dest="chunk_size", type=int,
                        default=1000,
                        help="The number of grid points that are "
                             "processed in one operation")
    parser.add_argument("--show_progress", dest="show_progress",
                        action="store_true",
                        help="Whether to show a progress bar")
    parser.add_argument("-s", "--subdivision", dest="subdivision", type=float,
                        default=30.,
                        help="A threshold in degrees (lon or lat) above which "
                             "the mask region will be subdivided into smaller "
                             "polygons for faster intersection checking")
    parser.add_argument(
        "--process_count", required=False, dest="process_count", type=int,
        help="The number of processes to use to compute masks.  The "
             "default is to use all available cores")
    parser.add_argument(
        "--multiprocessing_method", dest="multiprocessing_method",
        default='forkserver',
        help="The multiprocessing method use for python mask creation "
             "('fork', 'spawn' or 'forkserver')")
    parser.add_argument("--format", dest="format", type=str,
                        help="NetCDF file format")
    parser.add_argument("--engine", dest="engine", type=str,
                        help="NetCDF output engine")
    args = parser.parse_args()

    dsGrid = xr.open_dataset(args.grid_file_name, decode_cf=False,
                             decode_times=False)
    lon = dsGrid[args.lon]
    lat = dsGrid[args.lat]

    ydim, xdim = lon.dims

    fcMask = read_feature_collection(args.geojson_file_name)

    pool = create_pool(process_count=args.process_count,
                       method=args.multiprocessing_method)

    with LoggingContext('compute_lon_lat_region_masks') as logger:
        dsMasks = compute_projection_grid_region_masks(
            lon=lon.values, lat=lat.values, fcMask=fcMask, logger=logger,
            pool=pool, chunkSize=args.chunk_size,
            showProgress=args.show_progress,
            subdivisionThreshold=args.subdivision, xdim=xdim, ydim=ydim)

    write_netcdf(dsMasks, args.mask_file_name, format=args.format,
                 engine=args.engine)
Пример #11
0
    def run_task(self):  # {{{
        """
        Plot a depth profile with variability
        """
        # Authors
        # -------
        # Xylar Asay-Davis

        config = self.config
        startYear = self.startYear
        endYear = self.endYear

        regionMaskFile = self.masksSubtask.geojsonFileName

        fcAll = read_feature_collection(regionMaskFile)

        fc = FeatureCollection()
        for feature in fcAll.features:
            if feature['properties']['name'] == self.regionName:
                fc.add_feature(feature)
                break

        inDirectory = build_config_full_path(config, 'output',
                                             'profilesSubdirectory')
        timeSeriesName = self.timeSeriesName
        inFileName = '{}/{}_{}_{:04d}-{:04d}.nc'.format(
            inDirectory, timeSeriesName, self.season,
            self.startYear, self.endYear)

        regionGroup = self.masksSubtask.regionGroup
        regionGroupSection = 'profiles{}'.format(
            regionGroup.replace(' ', ''))

        ds = xr.open_dataset(inFileName)
        allRegionNames = decode_strings(ds.regionNames)

        regionIndex = allRegionNames.index(self.regionName)
        ds = ds.isel(nRegions=regionIndex)
        meanFieldName = '{}_mean'.format(self.field['prefix'])
        stdFieldName = '{}_std'.format(self.field['prefix'])

        mainRunName = config.get('runs', 'mainRunName')
        profileGalleryGroup = config.get(regionGroupSection,
                                         'profileGalleryGroup')

        titleFieldName = self.field['titleName']
        regionName = self.regionName.replace('_', ' ')

        xLabel = '{} ({})'.format(titleFieldName, self.field['units'])
        yLabel = 'depth (m)'
        outFileName = '{}/{}.png'.format(self.plotsDirectory, self.filePrefix)
        lineColors = ['k']
        lineWidths = [1.6]
        zArrays = [ds.z.values]
        fieldArrays = [ds[meanFieldName].values]
        errArrays = [ds[stdFieldName].values]
        if self.controlConfig is None:
            title = '{} {}, years {:04d}-{:04d}\n{}'.format(
                regionName, self.season, startYear, endYear, mainRunName)
            legendText = [None]
        else:
            controlStartYear = self.controlConfig.getint('climatology',
                                                         'startYear')
            controlEndYear = self.controlConfig.getint('climatology',
                                                       'endYear')
            controlRunName = self.controlConfig.get('runs', 'mainRunName')

            if controlStartYear == startYear and controlEndYear == endYear:
                title = '{} {}, years {:04d}-{:04d}'.format(
                    regionName, self.season, startYear, endYear)
                legendText = [mainRunName, controlRunName]
            elif mainRunName == controlRunName:
                title = '{} {}\n{}'.format(
                    regionName, self.season, mainRunName)
                legendText = ['{:04d}-{:04d}'.format(startYear, endYear),
                              '{:04d}-{:04d}'.format(controlStartYear,
                                                     controlEndYear)]
            else:
                title = '{} {}   '.format(regionName, self.season)
                legendText = ['{} {:04d}-{:04d}'.format(mainRunName, startYear,
                                                        endYear),
                              '{} {:04d}-{:04d}'.format(controlRunName,
                                                        controlStartYear,
                                                        controlEndYear)]

            controlDirectory = build_config_full_path(
                self.controlConfig, 'output',
                'profilesSubdirectory')

            controlFileName = \
                '{}/{}_{}_{:04d}-{:04d}.nc'.format(
                    controlDirectory, timeSeriesName, self.season,
                    controlStartYear, controlEndYear)

            dsControl = xr.open_dataset(controlFileName)
            allRegionNames = decode_strings(dsControl.regionNames)
            regionIndex = allRegionNames.index(self.regionName)
            dsControl = dsControl.isel(nRegions=regionIndex)

            lineColors.append('r')
            lineWidths.append(1.2)
            zArrays.append(dsControl.z.values)
            fieldArrays.append(dsControl[meanFieldName].values)
            errArrays.append(dsControl[stdFieldName].values)

        depthRange = config.getExpression(regionGroupSection, 'depthRange')
        if len(depthRange) == 0:
            depthRange = None

        fig = self.plot(zArrays, fieldArrays, errArrays,
                        lineColors=lineColors, lineWidths=lineWidths,
                        legendText=legendText, title=title, xLabel=xLabel,
                        yLabel=yLabel, yLim=depthRange)

        # do this before the inset because otherwise it moves the inset
        # and cartopy doesn't play too well with tight_layout anyway
        plt.tight_layout()

        add_inset(fig, fc, width=1.0, height=1.0)

        savefig(outFileName, tight=False)

        caption = '{} {} vs depth'.format(regionName, titleFieldName)
        write_image_xml(
            config=config,
            filePrefix=self.filePrefix,
            componentName='Ocean',
            componentSubdirectory='ocean',
            galleryGroup=profileGalleryGroup,
            groupLink='ocnregprofs',
            imageDescription=caption,
            imageCaption=caption,
            gallery=titleFieldName,
            thumbnailDescription='{} {}'.format(regionName, self.season))
Пример #12
0
    def run_task(self):  # {{{
        """
        Plots time-series output of properties in an ocean region.
        """
        # Authors
        # -------
        # Xylar Asay-Davis

        self.logger.info("\nPlotting TS diagram for {}"
                         "...".format(self.regionName))

        register_custom_colormaps()

        config = self.config
        sectionName = self.sectionName

        startYear = self.mpasClimatologyTask.startYear
        endYear = self.mpasClimatologyTask.endYear

        regionMaskSuffix = config.getExpression(sectionName,
                                                'regionMaskSuffix')

        regionMaskFile = get_region_mask(config,
                                         '{}.geojson'.format(regionMaskSuffix))

        fcAll = read_feature_collection(regionMaskFile)

        fc = FeatureCollection()
        for feature in fcAll.features:
            if feature['properties']['name'] == self.regionName:
                fc.add_feature(feature)
                break

        self.logger.info('  Make plots...')

        groupLink = 'tsDiag' + self.regionGroup[0].lower() + \
            self.regionGroup[1:].replace(' ', '')

        nSubplots = 1 + len(self.obsDicts)
        if self.controlConfig is not None:
            nSubplots += 1

        if nSubplots == 4:
            nCols = 2
            nRows = 2
        else:
            nCols = min(nSubplots, 3)
            nRows = (nSubplots - 1) // 3 + 1

        axisIndices = numpy.reshape(numpy.arange(nRows * nCols),
                                    (nRows, nCols))[::-1, :].ravel()

        titleFontSize = config.get('plot', 'titleFontSize')
        axis_font = {'size': config.get('plot', 'axisFontSize')}
        title_font = {
            'size': titleFontSize,
            'color': config.get('plot', 'titleFontColor'),
            'weight': config.get('plot', 'titleFontWeight')
        }

        width = 3 + 4.5 * nCols
        height = 2 + 4 * nRows

        # noinspection PyTypeChecker
        fig, axarray = plt.subplots(nrows=nRows,
                                    ncols=nCols,
                                    sharey=True,
                                    figsize=(width, height))

        if nSubplots == 1:
            axarray = numpy.array(axarray)

        if nRows == 1:
            axarray = axarray.reshape((nRows, nCols))

        T, S, zMid, volume, zmin, zmax = self._get_mpas_t_s(self.config)
        mainRunName = config.get('runs', 'mainRunName')
        plotFields = [{
            'S': S,
            'T': T,
            'z': zMid,
            'vol': volume,
            'title': mainRunName
        }]

        if self.controlConfig is not None:
            T, S, zMid, volume, _, _ = self._get_mpas_t_s(self.controlConfig)
            controlRunName = self.controlConfig.get('runs', 'mainRunName')
            plotFields.append({
                'S': S,
                'T': T,
                'z': zMid,
                'vol': volume,
                'title': 'Control: {}'.format(controlRunName)
            })

        for obsName in self.obsDicts:
            obsT, obsS, obsZ, obsVol = self._get_obs_t_s(
                self.obsDicts[obsName])
            plotFields.append({
                'S': obsS,
                'T': obsT,
                'z': obsZ,
                'vol': obsVol,
                'title': obsName
            })

        Tbins = config.getExpression(sectionName, 'Tbins', usenumpyfunc=True)
        Sbins = config.getExpression(sectionName, 'Sbins', usenumpyfunc=True)

        normType = config.get(sectionName, 'normType')

        PT, SP = numpy.meshgrid(Tbins, Sbins)
        SA = gsw.SA_from_SP(SP, p=0., lon=0., lat=-75.)
        CT = gsw.CT_from_t(SA, PT, p=0.)

        neutralDensity = sigma0(SA, CT)
        rhoInterval = config.getfloat(sectionName, 'rhoInterval')
        contours = numpy.arange(24., 29. + rhoInterval, rhoInterval)

        diagramType = config.get(sectionName, 'diagramType')
        if diagramType not in ['volumetric', 'scatter']:
            raise ValueError('Unexpected diagramType {}'.format(diagramType))

        lastPanel = None
        volMinMpas = None
        volMaxMpas = None
        for index in range(len(axisIndices)):
            panelIndex = axisIndices[index]

            row = nRows - 1 - index // nCols
            col = numpy.mod(index, nCols)

            if panelIndex >= nSubplots:
                plt.delaxes(axarray[row, col])
                continue

            plt.sca(axarray[row, col])
            T = plotFields[index]['T']
            S = plotFields[index]['S']
            z = plotFields[index]['z']
            volume = plotFields[index]['vol']
            title = plotFields[index]['title']

            CS = plt.contour(SP,
                             PT,
                             neutralDensity,
                             contours,
                             linewidths=1.,
                             colors='k',
                             zorder=2)
            plt.clabel(CS, fontsize=12, inline=1, fmt='%4.2f')

            if diagramType == 'volumetric':
                lastPanel, volMin, volMax = \
                    self._plot_volumetric_panel(T, S, volume)

                if index == 0:
                    volMinMpas = volMin
                    volMaxMpas = volMax
                if normType == 'linear':
                    norm = colors.Normalize(vmin=0., vmax=volMaxMpas)
                elif normType == 'log':
                    if volMinMpas is None or volMaxMpas is None:
                        norm = None
                    else:
                        norm = colors.LogNorm(vmin=volMinMpas, vmax=volMaxMpas)
                else:
                    raise ValueError(
                        'Unsupported normType {}'.format(normType))
                if norm is not None:
                    lastPanel.set_norm(norm)
            else:
                lastPanel = self._plot_scatter_panel(T, S, z, zmin, zmax)

            CTFreezing = freezing.CT_freezing(Sbins, 0, 1)
            PTFreezing = gsw.t_from_CT(gsw.SA_from_SP(Sbins,
                                                      p=0.,
                                                      lon=0.,
                                                      lat=-75.),
                                       CTFreezing,
                                       p=0.)
            plt.plot(Sbins,
                     PTFreezing,
                     linestyle='--',
                     linewidth=1.,
                     color='k')

            plt.ylim([Tbins[0], Tbins[-1]])
            plt.xlim([Sbins[0], Sbins[-1]])

            plt.xlabel('Salinity (PSU)', **axis_font)
            if col == 0:
                plt.ylabel(r'Potential temperature ($^\circ$C)', **axis_font)
            plt.title(title)

        # do this before the inset because otherwise it moves the inset
        # and cartopy doesn't play too well with tight_layout anyway
        plt.tight_layout()

        fig.subplots_adjust(right=0.91)
        if nRows == 1:
            fig.subplots_adjust(top=0.85)
        else:
            fig.subplots_adjust(top=0.88)

        suptitle = 'T-S diagram for {} ({}, {:04d}-{:04d})\n' \
                   ' {} m < z < {} m'.format(self.regionName, self.season,
                                             startYear, endYear, zmin, zmax)
        fig.text(0.5,
                 0.9,
                 suptitle,
                 horizontalalignment='center',
                 **title_font)

        inset = add_inset(fig, fc, width=1.5, height=1.5)

        # move the color bar down a little ot avoid the inset
        pos0 = inset.get_position()
        pos1 = axarray[-1, -1].get_position()
        pad = 0.04
        top = pos0.y0 - pad
        height = top - pos1.y0
        cbar_ax = fig.add_axes([0.92, pos1.y0, 0.02, height])
        cbar = fig.colorbar(lastPanel, cax=cbar_ax)

        if diagramType == 'volumetric':
            cbar.ax.get_yaxis().labelpad = 15
            cbar.ax.set_ylabel(r'volume (m$^3$)', rotation=270)
        else:
            cbar.ax.set_ylabel('depth (m)', rotation=270)

        outFileName = '{}/TS_diagram_{}_{}.png'.format(self.plotsDirectory,
                                                       self.prefix,
                                                       self.season)
        savefig(outFileName, tight=False)

        caption = 'Regional mean of {}'.format(suptitle)
        write_image_xml(config=config,
                        filePrefix='TS_diagram_{}_{}'.format(
                            self.prefix, self.season),
                        componentName='Ocean',
                        componentSubdirectory='ocean',
                        galleryGroup='T-S Diagrams',
                        groupLink=groupLink,
                        gallery=self.regionGroup,
                        thumbnailDescription=self.regionName,
                        imageDescription=caption,
                        imageCaption=caption)
Пример #13
0
    def build_cell_width_lat_lon(self):
        """
        Create cell width array for this mesh on a regular latitude-longitude
        grid

        Returns
        -------
        cellWidth : numpy.array
            m x n array of cell width in km

        lon : numpy.array
            longitude in degrees (length n and between -180 and 180)

        lat : numpy.array
            longitude in degrees (length m and between -90 and 90)
        """

        dlon = 0.1
        dlat = dlon
        earth_radius = constants['SHR_CONST_REARTH']
        print('\nCreating cellWidth on a lat-lon grid of: {0:.2f} x {0:.2f} '
              'degrees'.format(dlon, dlat))
        print('This can be set higher for faster test generation\n')
        nlon = int(360. / dlon) + 1
        nlat = int(180. / dlat) + 1
        lon = np.linspace(-180., 180., nlon)
        lat = np.linspace(-90., 90., nlat)
        km = 1.0e3

        print('plotting ...')
        plt.switch_backend('Agg')
        fig = plt.figure()
        plt.clf()
        fig.set_size_inches(10.0, 14.0)
        register_sci_viz_colormaps()

        # Create cell width vs latitude for Atlantic and Pacific basins
        EC60to30 = mdt.EC_CellWidthVsLat(lat)
        EC60to30Narrow = mdt.EC_CellWidthVsLat(lat, latPosEq=8.0,
                                               latWidthEq=3.0)

        # Expand from 1D to 2D
        _, cellWidth = np.meshgrid(lon, EC60to30Narrow)
        _plot_cartopy(2, 'narrow EC60to30', cellWidth, '3Wbgy5')
        plotFrame = 3

        # global settings for regionally refines mesh
        highRes = 14.0  # [km]

        fileName = 'region_Central_America'
        transitionWidth = 800.0 * km
        transitionOffset = 0.0
        fc = read_feature_collection('{}.geojson'.format(fileName))
        signedDistance = signed_distance_from_geojson(fc, lon, lat,
                                                      earth_radius,
                                                      max_length=0.25)
        mask = 0.5 * (1 + np.tanh((transitionOffset - signedDistance) /
                                  (transitionWidth / 2.)))
        cellWidth = 30.0 * mask + cellWidth * (1 - mask)

        fileName = 'coastline_CUSP'
        distanceToTransition = 600.0 * km
        # transitionWidth is distance from 0.07 to 0.03 of transition within
        # tanh
        transitionWidth = 600.0 * km
        transitionOffset = distanceToTransition + transitionWidth / 2.0
        fc = read_feature_collection('{}.geojson'.format(fileName))
        signedDistance = signed_distance_from_geojson(fc, lon, lat,
                                                      earth_radius,
                                                      max_length=0.25)
        mask = 0.5 * (1 + np.tanh((transitionOffset - signedDistance) /
                                  (transitionWidth / 2.)))
        cellWidth = highRes * mask + cellWidth * (1 - mask)
        _plot_cartopy(plotFrame, fileName + ' mask', mask, 'Blues')
        _plot_cartopy(plotFrame + 1, 'cellWidth ', cellWidth, '3Wbgy5')
        plotFrame += 2

        fileName = 'region_Gulf_of_Mexico'
        transitionOffset = 600.0 * km
        transitionWidth = 600.0 * km
        fc = read_feature_collection('{}.geojson'.format(fileName))
        signedDistance = signed_distance_from_geojson(fc, lon, lat,
                                                      earth_radius,
                                                      max_length=0.25)
        maskSmooth = 0.5 * (1 + np.tanh((transitionOffset - signedDistance) /
                                        (transitionWidth / 2.)))
        maskSharp = 0.5 * (1 + np.sign(-signedDistance))
        fc = read_feature_collection('land_mask_Mexico.geojson')
        signedDistance = signed_distance_from_geojson(fc, lon, lat,
                                                      earth_radius,
                                                      max_length=0.25)
        landMask = 0.5 * (1 + np.sign(-signedDistance))
        mask = maskSharp * landMask + maskSmooth * (1 - landMask)
        cellWidth = highRes * mask + cellWidth * (1 - mask)
        _plot_cartopy(plotFrame, fileName + ' mask', mask, 'Blues')
        _plot_cartopy(plotFrame + 1, 'cellWidth ', cellWidth, '3Wbgy5')
        plotFrame += 2

        fileName = 'region_Bering_Sea'
        transitionOffset = 0.0 * km
        transitionWidth = 600.0 * km
        fc = read_feature_collection('{}.geojson'.format(fileName))
        signedDistance = signed_distance_from_geojson(fc, lon, lat,
                                                      earth_radius,
                                                      max_length=0.25)
        maskSmoothEast = 0.5 * (
                    1 + np.tanh((transitionOffset - signedDistance) /
                                (transitionWidth / 2.)))

        fc = read_feature_collection('region_Bering_Sea_reduced.geojson')
        signedDistance = signed_distance_from_geojson(fc, lon, lat,
                                                      earth_radius,
                                                      max_length=0.25)
        maskSmoothWest = 0.5 * (
                    1 + np.tanh((transitionOffset - signedDistance) /
                                (transitionWidth / 2.)))

        fc = read_feature_collection('land_mask_Kamchatka.geojson')
        maskWest = mask_from_geojson(fc, lon, lat)
        mask = maskSmoothWest * maskWest + maskSmoothEast * (1 - maskWest)
        cellWidth = highRes * mask + cellWidth * (1 - mask)
        _plot_cartopy(plotFrame, fileName + ' mask', mask, 'Blues')
        _plot_cartopy(plotFrame + 1, 'cellWidth ', cellWidth, '3Wbgy5')
        plotFrame += 2

        fileName = 'region_Arctic_Ocean'
        transitionOffset = 0.0 * km
        transitionWidth = 600.0 * km
        fc = read_feature_collection('{}.geojson'.format(fileName))
        signedDistance = signed_distance_from_geojson(fc, lon, lat,
                                                      earth_radius,
                                                      max_length=0.25)
        mask = 0.5 * (1 + np.tanh((transitionOffset - signedDistance) /
                                  (transitionWidth / 2.)))
        cellWidth = highRes * mask + cellWidth * (1 - mask)
        _plot_cartopy(plotFrame, fileName + ' mask', mask, 'Blues')
        _plot_cartopy(plotFrame + 1, 'cellWidth ', cellWidth, '3Wbgy5')
        plotFrame += 2

        fileName = 'region_Gulf_Stream_extension'
        transitionOffset = 0.0 * km
        transitionWidth = 600.0 * km
        fc = read_feature_collection('{}.geojson'.format(fileName))
        signedDistance = signed_distance_from_geojson(fc, lon, lat,
                                                      earth_radius,
                                                      max_length=0.25)
        mask = 0.5 * (1 + np.tanh((transitionOffset - signedDistance) /
                                  (transitionWidth / 2.)))
        cellWidth = highRes * mask + cellWidth * (1 - mask)
        _plot_cartopy(plotFrame, fileName + ' mask', mask, 'Blues')
        _plot_cartopy(plotFrame + 1, 'cellWidth ', cellWidth, '3Wbgy5')
        plotFrame += 2

        ax = plt.subplot(6, 2, 1)
        ax.plot(lat, EC60to30, label='original EC60to30')
        ax.plot(lat, EC60to30Narrow, label='narrow EC60to30')
        ax.grid(True)
        plt.title('Grid cell size [km] versus latitude')
        plt.legend(loc="upper left")

        plt.savefig('mesh_construction.png', dpi=300)

        return cellWidth, lon, lat
Пример #14
0
def cellWidthVsLatLon():
    """
    Create cell width array for this mesh on a regular latitude-longitude grid.
    Returns
    -------
       cellWidth : numpy.ndarray
            m x n array, entries are desired cell width in km
       lat : numpy.ndarray
            latitude, vector of length m, with entries between -90 and 90,
            degrees
       lon : numpy.ndarray
            longitude, vector of length n, with entries between -180 and 180,
            degrees
    """
    dlon = 0.1
    dlat = dlon
    nlon = int(360. / dlon) + 1
    nlat = int(180. / dlat) + 1
    lon = np.linspace(-180., 180., nlon)
    lat = np.linspace(-90., 90., nlat)

    cellWidthSouth = 30. * np.ones((len(lat)))

    # Transition at Equator
    cellWidthNorth = mdt.EC_CellWidthVsLat(lat)
    latTransition = 0.0
    latWidthTransition = 5.0
    cellWidthVsLat = mdt.mergeCellWidthVsLat(lat, cellWidthSouth,
                                             cellWidthNorth, latTransition,
                                             latWidthTransition)

    _, cellWidth = np.meshgrid(lon, cellWidthVsLat)

    # now, add the high-res region
    fc = read_feature_collection('high_res_region.geojson')

    signed_distance = signed_distance_from_geojson(fc,
                                                   lon,
                                                   lat,
                                                   max_length=0.25)

    da = xarray.DataArray(signed_distance,
                          dims=['y', 'x'],
                          coords={
                              'y': lat,
                              'x': lon
                          },
                          name='signed_distance')
    cw_filename = 'signed_distance.nc'
    da.to_netcdf(cw_filename)

    # multiply by 5 because transition_width gets multiplied by 0.2 in
    # compute_cell_width
    # Equivalent to 10 degrees latitude
    trans_width = 5 * 1100e3
    # The last term compensates for the offset in compute_cell_width.
    # The middle of the transition is ~2.5 degrees (300 km) south of the
    # region boundary to best match previous transition at 48 S. (The mean lat
    # of the boundary is 45.5 S.)
    trans_start = -300e3 - 0.5 * trans_width
    dx_min = 10.

    cellWidth = compute_cell_width(signed_distance,
                                   cellWidth,
                                   lon,
                                   lat,
                                   dx_min,
                                   trans_start,
                                   trans_width,
                                   restrict_box={
                                       'include': [],
                                       'exclude': []
                                   })

    # Uncomment to plot the cell size distribution.
    # Lon, Lat = np.meshgrid(lon, lat)
    # ax = plt.subplot(111)
    # plt.pcolormesh(Lon, Lat, cellWidth)
    # plt.colorbar()
    # ax.set_aspect('equal')
    # ax.autoscale(tight=True)
    # plt.tight_layout()
    # plt.savefig('cellWidthVsLat.png', dpi=200)

    return cellWidth, lon, lat
Пример #15
0
for fname in average_error_files:
  with open(fname,'rb') as f:
    run,xv,yv,metrics,station_list_data,station_depth_data = pickle.load(f)
    average_errors.append({'run_name':run,'xv':xv,'yv':yv,'metrics':metrics,'stations':station_list_data,'depth':station_depth_data})


columns = list(errors.keys()) 
columns.remove('station')
fsize = (3,9)
opac = 0.5

pprint.pprint(errors_label)
pprint.pprint(len(errors_label))
pprint.pprint(max(errors['station']))

fc = read_feature_collection('NDBC_regions.geojson')

regions = [ 
           'East_Coast_South',
           'East_Coast_North',
           'HI',
           'Gulf',
           'West_Coast_South',
           'West_Coast_North',
           'Caribbean_Atlantic',
           'AK'
         ]

exclude_stations = ['tybg1']

for region in regions:
Пример #16
0
    def run_task(self):  # {{{
        """
        Make the Hovmoller plot from the time series.
        """
        # Authors
        # -------
        # Xylar Asay-Davis, Milena Veneziani, Greg Streletz

        self.logger.info("\nPlotting {} time series vs. depth...".format(
            self.fieldNameInTitle))

        config = self.config

        mainRunName = config.get('runs', 'mainRunName')

        self.logger.info('  Load ocean data...')
        ds = xr.open_dataset(self.inFileName)

        if 'regionNames' in ds.coords:

            allRegionNames = decode_strings(ds.regionNames)
            regionIndex = allRegionNames.index(self.regionName)
            regionNameInTitle = self.regionName.replace('_', ' ')
            regionDim = ds.regionNames.dims[0]
        else:
            plotTitles = config.getExpression('regions', 'plotTitles')
            allRegionNames = config.getExpression('regions', 'regions')
            regionIndex = allRegionNames.index(self.regionName)
            regionNameInTitle = plotTitles[regionIndex]
            regionDim = 'nOceanRegionsTmp'

        ds = ds.isel(**{regionDim: regionIndex})

        # Note: restart file, not a mesh file because we need refBottomDepth,
        # not in a mesh file
        try:
            restartFile = self.runStreams.readpath('restart')[0]
        except ValueError:
            raise IOError('No MPAS-O restart file found: need at least one '
                          'restart file for plotting time series vs. depth')

        # Define/read in general variables
        self.logger.info('  Read in depth...')
        with xr.open_dataset(restartFile) as dsRestart:
            # reference depth [m]
            depths = dsRestart.refBottomDepth.values
            z = np.zeros(depths.shape)
            z[0] = -0.5 * depths[0]
            z[1:] = -0.5 * (depths[0:-1] + depths[1:])

        Time = ds.Time.values
        field = ds[self.mpasFieldName].values.transpose()

        xLabel = 'Time (years)'
        yLabel = 'Depth (m)'

        title = '{}, {}'.format(self.fieldNameInTitle, regionNameInTitle)

        outFileName = '{}/{}.png'.format(self.plotsDirectory, self.filePrefix)

        if config.has_option(self.sectionName, 'firstYearXTicks'):
            firstYearXTicks = config.getint(self.sectionName,
                                            'firstYearXTicks')
        else:
            firstYearXTicks = None

        if config.has_option(self.sectionName, 'yearStrideXTicks'):
            yearStrideXTicks = config.getint(self.sectionName,
                                             'yearStrideXTicks')
        else:
            yearStrideXTicks = None

        movingAverageMonths = config.getWithDefault(self.sectionName,
                                                    'movingAverageMonths', 1)

        if config.has_option(self.sectionName, 'yLim'):
            yLim = config.getExpression(self.sectionName, 'yLim')
        else:
            yLim = None

        if self.controlConfig is None:
            refField = None
            diff = None
            refTitle = None
            diffTitle = None
        else:
            controlConfig = self.controlConfig
            dsRef = xr.open_dataset(self.controlFileName)

            if 'regionNames' in dsRef.coords:
                allRegionNames = decode_strings(dsRef.regionNames)
                regionIndex = allRegionNames.index(self.regionName)
                regionNameInTitle = self.regionName.replace('_', ' ')
                regionDim = dsRef.regionNames.dims[0]
            else:
                plotTitles = controlConfig.getExpression(
                    'regions', 'plotTitles')
                allRegionNames = controlConfig.getExpression(
                    'regions', 'regions')
                regionIndex = allRegionNames.index(self.regionName)
                regionNameInTitle = plotTitles[regionIndex]
                regionDim = 'nOceanRegionsTmp'

            dsRef = dsRef.isel(**{regionDim: regionIndex})
            refField = dsRef[self.mpasFieldName].values.transpose()
            assert (field.shape == refField.shape)
            diff = field - refField
            refTitle = self.controlConfig.get('runs', 'mainRunName')
            diffTitle = 'Main - Control'

        fig, _, suptitle = plot_vertical_section_comparison(
            config,
            Time,
            z,
            field,
            refField,
            diff,
            self.sectionName,
            colorbarLabel=self.unitsLabel,
            title=title,
            modelTitle=mainRunName,
            refTitle=refTitle,
            diffTitle=diffTitle,
            xlabel=xLabel,
            ylabel=yLabel,
            lineWidth=1,
            xArrayIsTime=True,
            movingAveragePoints=movingAverageMonths,
            calendar=self.calendar,
            firstYearXTicks=firstYearXTicks,
            yearStrideXTicks=yearStrideXTicks,
            yLim=yLim,
            invertYAxis=False)

        if self.regionMaskFile is not None:

            # shift the super-title a little to the left to make room for the
            # inset
            pos = suptitle.get_position()
            suptitle.set_position((pos[0] - 0.05, pos[1]))

            fcAll = read_feature_collection(self.regionMaskFile)

            fc = FeatureCollection()
            for feature in fcAll.features:
                if feature['properties']['name'] == self.regionName:
                    fc.add_feature(feature)
                    break

            add_inset(fig, fc, width=1.0, height=1.0, xbuffer=0.1, ybuffer=0.1)

            savefig(outFileName, tight=False)

        else:
            savefig(outFileName)

        write_image_xml(config=config,
                        filePrefix=self.filePrefix,
                        componentName='Ocean',
                        componentSubdirectory='ocean',
                        galleryGroup=self.galleryGroup,
                        groupSubtitle=self.groupSubtitle,
                        groupLink=self.groupLink,
                        gallery=self.galleryName,
                        thumbnailDescription='{} {}'.format(
                            regionNameInTitle, self.thumbnailSuffix),
                        imageDescription=self.imageCaption,
                        imageCaption=self.imageCaption)
Пример #17
0
def cellWidthVsLatLon():
    """
    Create cell width array for this mesh on a regular latitude-longitude grid.
    Returns
    -------
       cellWidth : ndarray
            m x n array, entries are desired cell width in km

       lat : ndarray
            latitude, vector of length m, with entries between -90 and 90,
            degrees

       lon : ndarray
            longitude, vector of length n, with entries between -180 and 180,
            degrees
    """
    dlon = 0.1
    dlat = dlon
    earth_radius = constants['SHR_CONST_REARTH']
    nlon = int(360./dlon) + 1
    nlat = int(180./dlat) + 1
    lon = np.linspace(-180., 180., nlon)
    lat = np.linspace(-90., 90., nlat)

    cellWidthSouth = mdt.EC_CellWidthVsLat(lat, cellWidthEq=30.,
                                           cellWidthMidLat=45.,
                                           cellWidthPole=45.,
                                           latPosEq=7.5, latWidthEq=3.0)

    # Transition at Equator
    cellWidthNorth = mdt.EC_CellWidthVsLat(lat, cellWidthEq=30.,
                                           cellWidthMidLat=60.,
                                           cellWidthPole=60.,
                                           latPosEq=7.5, latWidthEq=3.0)
    latTransition = 0.0
    latWidthTransition = 2.5
    cellWidthVsLat = mdt.mergeCellWidthVsLat(
        lat,
        cellWidthSouth,
        cellWidthNorth,
        latTransition,
        latWidthTransition)

    _, cellWidth = np.meshgrid(lon, cellWidthVsLat)

    # now, add the high-res region
    fc = read_feature_collection('high_res_region.geojson')

    so_signed_distance = signed_distance_from_geojson(fc, lon, lat,
                                                      earth_radius,
                                                      max_length=0.25)

    # Equivalent to 20 degrees latitude
    trans_width = 1600e3
    trans_start = -500e3
    dx_min = 12.

    weights = 0.5 * (1 + np.tanh((so_signed_distance - trans_start) /
                                 trans_width))

    cellWidth = dx_min * (1 - weights) + cellWidth * weights

    fc = read_feature_collection('north_mid_res_region.geojson')

    ar_signed_distance = signed_distance_from_geojson(fc, lon, lat,
                                                      earth_radius,
                                                      max_length=0.25)

    fc = read_feature_collection('greenland.geojson')

    gr_signed_distance = signed_distance_from_geojson(fc, lon, lat,
                                                      earth_radius,
                                                      max_length=0.25)

    frac = (-ar_signed_distance/(-ar_signed_distance + gr_signed_distance))

    frac = np.maximum(0., np.minimum(1., frac))

    dx_min = 15.
    dx_max = 35.

    arctic_widths = dx_max + (dx_min - dx_max)*frac

    trans_width = 1000e3
    trans_start = 0.

    weights = 0.5 * (1 + np.tanh((ar_signed_distance - trans_start) /
                                 trans_width))

    cellWidth = arctic_widths * (1 - weights) + cellWidth * weights

    return cellWidth, lon, lat
    def run_task(self):  # {{{
        """
        Plots time-series output of transport through transects.
        """
        # Authors
        # -------
        # Xylar Asay-Davis, Stephen Price

        self.logger.info("\nPlotting time series of transport through "
                         "{}...".format(self.transect))

        self.logger.info('  Load transport data...')

        obsDict = {
            'Drake Passage': [120, 175],
            'Tasmania-Ant': [147, 167],
            'Africa-Ant': None,
            'Antilles Inflow': [-23.1, -13.7],
            'Mona Passage': [-3.8, -1.4],
            'Windward Passage': [-7.2, -6.8],
            'Florida-Cuba': [30, 33],
            'Florida-Bahamas': [30, 33],
            'Indonesian Throughflow': [-21, -11],
            'Agulhas': [-90, -50],
            'Mozambique Channel': [-20, -8],
            'Bering Strait': [0.6, 1.0],
            'Lancaster Sound': [-1.0, -0.5],
            'Fram Strait': [-4.7, 0.7],
            'Davis Strait': [-1.6, -3.6],
            'Barents Sea Opening': [1.4, 2.6],
            'Nares Strait': [-1.8, 0.2]
        }

        config = self.config
        calendar = self.calendar

        fcAll = read_feature_collection(self.transportTransectFileName)

        fc = FeatureCollection()
        for feature in fcAll.features:
            if feature['properties']['name'] == self.transect:
                fc.add_feature(feature)
                break

        transport, trans_mean, trans_std = self._load_transport(config)

        if self.transect in obsDict:
            bounds = obsDict[self.transect]
        else:
            bounds = None

        plotControl = self.controlConfig is not None

        mainRunName = config.get('runs', 'mainRunName')
        movingAverageMonths = config.getint('timeSeriesTransport',
                                            'movingAverageMonths')

        self.logger.info('  Plotting...')

        transectName = self.transect.replace('_', ' ')
        title = transectName
        thumbnailDescription = transectName

        xLabel = 'Time (yr)'
        yLabel = 'Transport (Sv)'

        filePrefix = 'transport_{}'.format(self.transect.replace(' ', '_'))
        outFileName = '{}/{}.png'.format(self.plotsDirectory, filePrefix)

        fields = [transport]
        lineColors = ['k']
        lineWidths = [2.5]
        meanString = 'mean={:.2f} $\pm$ {:.2f}'.format(trans_mean, trans_std)
        if plotControl:
            controlRunName = self.controlConfig.get('runs', 'mainRunName')
            ref_transport, ref_mean, ref_std = \
                self._load_transport(self.controlConfig)
            refMeanString = 'mean={:.2f} $\pm$ {:.2f}'.format(
                ref_mean, ref_std)
            fields.append(ref_transport)
            lineColors.append('r')
            lineWidths.append(1.2)
            legendText = [
                '{} ({})'.format(mainRunName, meanString),
                '{} ({})'.format(controlRunName, refMeanString)
            ]

        else:
            legendText = [mainRunName]
            title = '{} ({})'.format(title, meanString)

        if config.has_option(self.taskName, 'firstYearXTicks'):
            firstYearXTicks = config.getint(self.taskName, 'firstYearXTicks')
        else:
            firstYearXTicks = None

        if config.has_option(self.taskName, 'yearStrideXTicks'):
            yearStrideXTicks = config.getint(self.taskName, 'yearStrideXTicks')
        else:
            yearStrideXTicks = None

        fig = timeseries_analysis_plot(config,
                                       fields,
                                       calendar=calendar,
                                       title=title,
                                       xlabel=xLabel,
                                       ylabel=yLabel,
                                       movingAveragePoints=movingAverageMonths,
                                       lineColors=lineColors,
                                       lineWidths=lineWidths,
                                       legendText=legendText,
                                       firstYearXTicks=firstYearXTicks,
                                       yearStrideXTicks=yearStrideXTicks)

        if bounds is not None:
            t = transport.Time.values
            plt.gca().fill_between(t,
                                   bounds[0] * numpy.ones_like(t),
                                   bounds[1] * numpy.ones_like(t),
                                   alpha=0.3,
                                   label='observations')
            plt.legend(loc='lower left')

        # do this before the inset because otherwise it moves the inset
        # and cartopy doesn't play too well with tight_layout anyway
        plt.tight_layout()

        add_inset(fig, fc, width=2.0, height=2.0)

        savefig(outFileName)

        caption = 'Transport through the {} Transect'.format(transectName)
        write_image_xml(config=config,
                        filePrefix=filePrefix,
                        componentName='Ocean',
                        componentSubdirectory='ocean',
                        galleryGroup='Transport Time Series',
                        groupLink='transporttime',
                        thumbnailDescription=thumbnailDescription,
                        imageDescription=caption,
                        imageCaption=caption)
Пример #19
0
def cellWidthVsLatLon():
    """
    Create cell width array for this mesh on a regular latitude-longitude grid.
    Returns
    -------
       cellWidth : ndarray
            m x n array, entries are desired cell width in km

       lat : ndarray
            latitude, vector of length m, with entries between -90 and 90,
            degrees

       lon : ndarray
            longitude, vector of length n, with entries between -180 and 180,
            degrees
    """
    # To speed up for testing, set following line to 1.0 degrees
    dlon = 0.1
    dlat = dlon
    earth_radius = constants['SHR_CONST_REARTH']
    print('\nCreating cellWidth on a lat-lon grid of: {0:.2f} x {0:.2f} '
          'degrees'.format(dlon,dlat))
    print('This can be set higher for faster test generation\n')
    nlon = int(360. / dlon) + 1
    nlat = int(180. / dlat) + 1
    lon = np.linspace(-180., 180., nlon)
    lat = np.linspace(-90., 90., nlat)
    km = 1.0e3

    print('plotting ...')
    fig = plt.figure()
    plt.clf()
    fig.set_size_inches(10.0, 14.0)
    register_sci_viz_colormaps()

    # Create cell width vs latitude for Atlantic and Pacific basins
    QU1 = np.ones(lat.size)
    EC60to30 = mdt.EC_CellWidthVsLat(lat)
    EC60to30Narrow = mdt.EC_CellWidthVsLat(lat, latPosEq = 8.0, latWidthEq = 3.0)

    # Expand from 1D to 2D
    _, cellWidth = np.meshgrid(lon, EC60to30Narrow)
    plot_cartopy(2, 'narrow EC60to30', cellWidth, '3Wbgy5')
    plotFrame = 3

    # global settings for regionally refines mesh
    highRes = 14.0 #[km]

    fileName = 'region_Central_America'
    transitionWidth = 800.0*km
    transitionOffset = 0.0
    fc = read_feature_collection('{}.geojson'.format(fileName))
    signedDistance = signed_distance_from_geojson(fc, lon, lat, earth_radius,
                                                  max_length=0.25)
    mask = 0.5 * (1 + np.tanh((transitionOffset-signedDistance) /
                              (transitionWidth/2.)))
    cellWidth = 30.0 * mask + cellWidth * (1 - mask)

    fileName = 'coastline_CUSP'
    distanceToTransition = 600.0*km
    # transitionWidth is distance from 0.07 to 0.03 of transition within tanh
    transitionWidth = 600.0*km
    transitionOffset = distanceToTransition + transitionWidth/2.0
    fc = read_feature_collection('{}.geojson'.format(fileName))
    signedDistance = signed_distance_from_geojson(fc, lon, lat, earth_radius,
                                                  max_length=0.25)
    mask = 0.5 * (1 + np.tanh((transitionOffset-signedDistance) /
                              (transitionWidth/2.)))
    cellWidth = highRes * mask + cellWidth * (1 - mask)
    plot_cartopy(plotFrame, fileName + ' mask', mask, 'Blues')
    plot_cartopy(plotFrame+1, 'cellWidth ', cellWidth, '3Wbgy5')
    plotFrame += 2

    fileName = 'region_Gulf_of_Mexico'
    transitionOffset = 600.0*km
    transitionWidth = 600.0*km
    fc = read_feature_collection('{}.geojson'.format(fileName))
    signedDistance = signed_distance_from_geojson(fc, lon, lat, earth_radius,
                                                  max_length=0.25)
    maskSmooth = 0.5 * (1 + np.tanh((transitionOffset-signedDistance) /
                                    (transitionWidth/2.)))
    maskSharp = 0.5 * (1 + np.sign(-signedDistance))
    fc = read_feature_collection('land_mask_Mexico.geojson')
    signedDistance = signed_distance_from_geojson(fc, lon, lat, earth_radius,
                                                  max_length=0.25)
    landMask = 0.5 * (1 + np.sign(-signedDistance))
    mask = maskSharp * landMask + maskSmooth * (1-landMask)
    cellWidth = highRes * mask + cellWidth * (1 - mask)
    plot_cartopy(plotFrame, fileName + ' mask', mask, 'Blues')
    plot_cartopy(plotFrame+1, 'cellWidth ', cellWidth, '3Wbgy5')
    plotFrame += 2

    fileName = 'region_Bering_Sea'
    transitionOffset = 0.0*km
    transitionWidth = 600.0*km
    fc = read_feature_collection('{}.geojson'.format(fileName))
    signedDistance = signed_distance_from_geojson(fc, lon, lat, earth_radius,
                                                  max_length=0.25)
    maskSmooth = 0.5 * (1 + np.tanh((transitionOffset-signedDistance) /
                                    (transitionWidth/2.)))
    maskSharp = 0.5 * (1 + np.sign(-signedDistance))
    fc = read_feature_collection('land_mask_Kamchatka.geojson')
    signedDistance = signed_distance_from_geojson(fc, lon, lat, earth_radius,
                                                  max_length=0.25)
    landMask = 0.5 * (1 + np.sign(-signedDistance))
    mask = maskSharp * landMask + maskSmooth * (1-landMask)
    cellWidth = highRes * mask + cellWidth * (1 - mask)
    plot_cartopy(plotFrame, fileName + ' mask', mask, 'Blues')
    plot_cartopy(plotFrame+1, 'cellWidth ', cellWidth, '3Wbgy5')
    plotFrame += 2

    fileName = 'region_Arctic_Ocean'
    transitionOffset = 0.0*km
    transitionWidth = 600.0*km
    fc = read_feature_collection('{}.geojson'.format(fileName))
    signedDistance = signed_distance_from_geojson(fc, lon, lat, earth_radius,
                                                  max_length=0.25)
    mask = 0.5 * (1 + np.tanh((transitionOffset-signedDistance) /
                              (transitionWidth/2.)))
    cellWidth = highRes * mask + cellWidth * (1 - mask)
    plot_cartopy(plotFrame, fileName + ' mask', mask, 'Blues')
    plot_cartopy(plotFrame+1, 'cellWidth ', cellWidth, '3Wbgy5')
    plotFrame += 2

    fileName = 'region_Gulf_Stream_extension'
    transitionOffset = 0.0*km
    transitionWidth = 600.0*km
    fc = read_feature_collection('{}.geojson'.format(fileName))
    signedDistance = signed_distance_from_geojson(fc, lon, lat, earth_radius,
                                                  max_length=0.25)
    mask = 0.5 * (1 + np.tanh((transitionOffset-signedDistance) /
                              (transitionWidth/2.)))
    cellWidth = highRes * mask + cellWidth * (1 - mask)
    plot_cartopy(plotFrame, fileName + ' mask', mask, 'Blues')
    plot_cartopy(plotFrame+1, 'cellWidth ', cellWidth, '3Wbgy5')
    plotFrame += 2

    # save signed distance to a file
    # da = xarray.DataArray(signedDistance,
    #                      dims=['y', 'x'],
    #                      coords={'y': lat, 'x': lon},
    #                      name='signedDistance')
    #cw_filename = 'signedDistance.nc'
    # da.to_netcdf(cw_filename)

    ax = plt.subplot(6, 2, 1)
    ax.plot(lat, EC60to30, label='original EC60to30')
    ax.plot(lat, EC60to30Narrow, label='narrow EC60to30')
    ax.grid(True)
    plt.title('Grid cell size [km] versus latitude')
    plt.legend(loc="upper left")

    plt.savefig('mesh_construction.png')

    return cellWidth, lon, lat
Пример #20
0
def _cull_mesh_with_logging(logger, with_cavities, with_critical_passages,
                            custom_critical_passages, custom_land_blockages,
                            preserve_floodplain, use_progress_bar,
                            process_count):
    """ Cull the mesh once the logger is defined for sure """

    critical_passages = with_critical_passages or \
        (custom_critical_passages is not None)

    land_blockages = with_critical_passages or \
        (custom_land_blockages is not None)

    gf = GeometricFeatures()

    # start with the land coverage from Natural Earth
    fcLandCoverage = gf.read(componentName='natural_earth',
                             objectType='region',
                             featureNames=['Land Coverage'])

    # remove the region south of 60S so we can replace it based on ice-sheet
    # topography
    fcSouthMask = gf.read(componentName='ocean', objectType='region',
                          featureNames=['Global Ocean 90S to 60S'])

    fcLandCoverage = fcLandCoverage.difference(fcSouthMask)

    # Add "land" coverage from either the full ice sheet or just the grounded
    # part
    if with_cavities:
        fcAntarcticLand = gf.read(
            componentName='bedmachine', objectType='region',
            featureNames=['AntarcticGroundedIceCoverage'])
    else:
        fcAntarcticLand = gf.read(
            componentName='bedmachine', objectType='region',
            featureNames=['AntarcticIceCoverage'])

    fcLandCoverage.merge(fcAntarcticLand)

    # save the feature collection to a geojson file
    fcLandCoverage.to_geojson('land_coverage.geojson')

    # these defaults may have been updated from config options -- pass them
    # along to the subprocess
    netcdf_format = mpas_tools.io.default_format
    netcdf_engine = mpas_tools.io.default_engine

    # Create the land mask based on the land coverage, i.e. coastline data
    args = ['compute_mpas_region_masks',
            '-m', 'base_mesh.nc',
            '-g', 'land_coverage.geojson',
            '-o', 'land_mask.nc',
            '-t', 'cell',
            '--process_count', '{}'.format(process_count),
            '--format', netcdf_format,
            '--engine', netcdf_engine]
    check_call(args, logger=logger)

    dsBaseMesh = xarray.open_dataset('base_mesh.nc')
    dsLandMask = xarray.open_dataset('land_mask.nc')
    dsLandMask = add_land_locked_cells_to_mask(dsLandMask, dsBaseMesh,
                                               latitude_threshold=43.0,
                                               nSweeps=20)

    # create seed points for a flood fill of the ocean
    # use all points in the ocean directory, on the assumption that they are,
    # in fact, in the ocean
    fcSeed = gf.read(componentName='ocean', objectType='point',
                     tags=['seed_point'])

    if land_blockages:
        if with_critical_passages:
            # merge transects for critical land blockages into
            # critical_land_blockages.geojson
            fcCritBlockages = gf.read(
                componentName='ocean', objectType='transect',
                tags=['Critical_Land_Blockage'])
        else:
            fcCritBlockages = FeatureCollection()

        if custom_land_blockages is not None:
            fcCritBlockages.merge(read_feature_collection(
                custom_land_blockages))

        # create masks from the transects
        fcCritBlockages.to_geojson('critical_blockages.geojson')
        args = ['compute_mpas_transect_masks',
                '-m', 'base_mesh.nc',
                '-g', 'critical_blockages.geojson',
                '-o', 'critical_blockages.nc',
                '-t', 'cell',
                '-s', '10e3',
                '--process_count', '{}'.format(process_count),
                '--format', netcdf_format,
                '--engine', netcdf_engine]
        check_call(args, logger=logger)
        dsCritBlockMask = xarray.open_dataset('critical_blockages.nc')

        dsLandMask = add_critical_land_blockages(dsLandMask, dsCritBlockMask)

    fcCritPassages = FeatureCollection()
    dsPreserve = []

    if critical_passages:
        if with_critical_passages:
            # merge transects for critical passages into fcCritPassages
            fcCritPassages.merge(gf.read(componentName='ocean',
                                         objectType='transect',
                                         tags=['Critical_Passage']))

        if custom_critical_passages is not None:
            fcCritPassages.merge(read_feature_collection(
                custom_critical_passages))

        # create masks from the transects
        fcCritPassages.to_geojson('critical_passages.geojson')
        args = ['compute_mpas_transect_masks',
                '-m', 'base_mesh.nc',
                '-g', 'critical_passages.geojson',
                '-o', 'critical_passages.nc',
                '-t', 'cell', 'edge',
                '-s', '10e3',
                '--process_count', '{}'.format(process_count),
                '--format', netcdf_format,
                '--engine', netcdf_engine]
        check_call(args, logger=logger)
        dsCritPassMask = xarray.open_dataset('critical_passages.nc')

        # Alter critical passages to be at least two cells wide, to avoid sea
        # ice blockage
        dsCritPassMask = widen_transect_edge_masks(dsCritPassMask, dsBaseMesh,
                                                   latitude_threshold=43.0)

        dsPreserve.append(dsCritPassMask)

    if preserve_floodplain:
        dsPreserve.append(dsBaseMesh)

    # cull the mesh based on the land mask
    dsCulledMesh = cull(dsBaseMesh, dsMask=dsLandMask,
                        dsPreserve=dsPreserve, logger=logger)

    # create a mask for the flood fill seed points
    dsSeedMask = compute_mpas_flood_fill_mask(dsMesh=dsCulledMesh,
                                              fcSeed=fcSeed,
                                              logger=logger)

    # cull the mesh a second time using a flood fill from the seed points
    dsCulledMesh = cull(dsCulledMesh, dsInverse=dsSeedMask,
                        graphInfoFileName='culled_graph.info', logger=logger)
    write_netcdf(dsCulledMesh, 'culled_mesh.nc')

    if critical_passages:
        # make a new version of the critical passages mask on the culled mesh
        fcCritPassages.to_geojson('critical_passages.geojson')
        args = ['compute_mpas_transect_masks',
                '-m', 'culled_mesh.nc',
                '-g', 'critical_passages.geojson',
                '-o', 'critical_passages_mask_final.nc',
                '-t', 'cell',
                '-s', '10e3',
                '--process_count', '{}'.format(process_count),
                '--format', netcdf_format,
                '--engine', netcdf_engine]
        check_call(args, logger=logger)

    if with_cavities:
        fcAntarcticIce = gf.read(
            componentName='bedmachine', objectType='region',
            featureNames=['AntarcticIceCoverage'])

        fcAntarcticIce.to_geojson('ice_coverage.geojson')
        args = ['compute_mpas_region_masks',
                '-m', 'culled_mesh.nc',
                '-g', 'ice_coverage.geojson',
                '-o', 'ice_coverage.nc',
                '-t', 'cell',
                '--process_count', '{}'.format(process_count),
                '--format', netcdf_format,
                '--engine', netcdf_engine]
        check_call(args, logger=logger)
        dsMask = xarray.open_dataset('ice_coverage.nc')

        landIceMask = dsMask.regionCellMasks.isel(nRegions=0)
        dsLandIceMask = xarray.Dataset()
        dsLandIceMask['landIceMask'] = landIceMask

        write_netcdf(dsLandIceMask, 'land_ice_mask.nc')

        dsLandIceCulledMesh = cull(dsCulledMesh, dsMask=dsMask, logger=logger)
        write_netcdf(dsLandIceCulledMesh, 'no_ISC_culled_mesh.nc')

    extract_vtk(ignore_time=True, dimension_list=['maxEdges='],
                variable_list=['allOnCells'],
                filename_pattern='culled_mesh.nc',
                out_dir='culled_mesh_vtk',
                use_progress_bar=use_progress_bar)

    if with_cavities:
        extract_vtk(ignore_time=True, dimension_list=['maxEdges='],
                    variable_list=['allOnCells'],
                    filename_pattern='no_ISC_culled_mesh.nc',
                    out_dir='no_ISC_culled_mesh_vtk',
                    use_progress_bar=use_progress_bar)
Пример #21
0
# use all points in the ocean directory, on the assumption that they are, in
# fact, in the ocean
fcSeed = gf.read(componentName='ocean', objectType='point',
                 tags=['seed_point'])

if land_blockages:
    if options.with_critical_passages:
        # merge transects for critical land blockages into
        # critical_land_blockages.geojson
        fcCritBlockages = gf.read(componentName='ocean', objectType='transect',
                                  tags=['Critical_Land_Blockage'])
    else:
        fcCritBlockages = FeatureCollection()

    if options.custom_land_blockages is not None:
        fcCritBlockages.merge(read_feature_collection(
            options.custom_land_blockages))

    # create masks from the transects
    dsCritBlockMask = conversion.mask(dsBaseMesh, fcMask=fcCritBlockages)

    dsLandMask = add_critical_land_blockages(dsLandMask, dsCritBlockMask)

fcCritPassages = FeatureCollection()
dsPreserve = []

if critical_passages:
    if options.with_critical_passages:
        # merge transects for critical passages into critical_passages.geojson
        fcCritPassages.merge(gf.read(componentName='ocean',
                                     objectType='transect',
                                     tags=['Critical_Passage']))
            inIndex += 1
            outIndex += 1

        poly = Polygon([(i[0], i[1]) for i in zip(lons, lats)])
        if poly.is_valid:
            polys.append(poly)
        else:
            print("invalid shape with {} vertices".format(contour.shape[0]))

    return mapping(cascaded_union(polys))


out_file_name = "AntarcticIceCoverage.geojson"

if os.path.exists(out_file_name):
    fc = read_feature_collection(out_file_name)
else:
    inFileName = 'BedMachineAntarctica_2019-11-05_v01.nc'
    ds = xarray.open_dataset(inFileName)

    # reverse the y direction
    ds = ds.isel(y=slice(None, None, -1))

    projection = pyproj.Proj('+proj=stere +lat_ts=-71.0 +lat_0=-90 +lon_0=0.0 '
                             '+k_0=1.0 +x_0=0.0 +y_0=0.0 +ellps=WGS84')

    lat_lon_projection = pyproj.Proj(proj='latlong', datum='WGS84')

    x = ds.x.values
    y = ds.y.values
Пример #23
0
def entry_point_compute_mpas_region_masks():
    """ Entry point for ``compute_mpas_region_masks()``"""

    parser = argparse.ArgumentParser()
    parser.add_argument("-m", "--mesh_file_name", dest="mesh_file_name",
                        type=str, required=True,
                        help="An MPAS mesh file")
    parser.add_argument("-g", "--geojson_file_name",
                        dest="geojson_file_name", type=str, required=True,
                        help="An Geojson file containing mask regions")
    parser.add_argument("-o", "--mask_file_name", dest="mask_file_name",
                        type=str, required=True,
                        help="An output MPAS region masks file")
    parser.add_argument("-t", "--mask_types", nargs='+', dest="mask_types",
                        type=str,
                        help="Which type(s) of masks to make: cell, edge or "
                             "vertex.  Default is cell and vertex.")
    parser.add_argument("-c", "--chunk_size", dest="chunk_size", type=int,
                        default=1000,
                        help="The number of cells, vertices or edges that are "
                             "processed in one operation")
    parser.add_argument("--show_progress", dest="show_progress",
                        action="store_true",
                        help="Whether to show a progress bar")
    parser.add_argument("-s", "--subdivision", dest="subdivision", type=float,
                        default=30.,
                        help="A threshold in degrees (lon or lat) above which "
                             "the mask region will be subdivided into smaller "
                             "polygons for faster intersection checking")
    parser.add_argument(
        "--process_count", required=False, dest="process_count", type=int,
        help="The number of processes to use to compute masks.  The "
             "default is to use all available cores")
    parser.add_argument(
        "--multiprocessing_method", dest="multiprocessing_method",
        default='forkserver',
        help="The multiprocessing method use for python mask creation "
             "('fork', 'spawn' or 'forkserver')")
    parser.add_argument("--format", dest="format", type=str,
                        help="NetCDF file format")
    parser.add_argument("--engine", dest="engine", type=str,
                        help="NetCDF output engine")
    args = parser.parse_args()

    dsMesh = xr.open_dataset(args.mesh_file_name, decode_cf=False,
                             decode_times=False)
    fcMask = read_feature_collection(args.geojson_file_name)

    pool = create_pool(process_count=args.process_count,
                       method=args.multiprocessing_method)

    if args.mask_types is None:
        args.mask_types = ('cell', 'vertex')

    with LoggingContext('compute_mpas_region_masks') as logger:
        dsMasks = compute_mpas_region_masks(
            dsMesh=dsMesh, fcMask=fcMask, maskTypes=args.mask_types,
            logger=logger, pool=pool, chunkSize=args.chunk_size,
            showProgress=args.show_progress,
            subdivisionThreshold=args.subdivision)

    write_netcdf(dsMasks, args.mask_file_name, format=args.format,
                 engine=args.engine)
Пример #24
0
                 objectType='point',
                 tags=['seed_point'])

if land_blockages:
    if options.with_critical_passages:
        # merge transects for critical land blockages into
        # critical_land_blockages.geojson
        fcCritBlockages = gf.read(componentName='ocean',
                                  objectType='transect',
                                  tags=['Critical_Land_Blockage'])
    else:
        fcCritBlockages = FeatureCollection()

    if options.custom_land_blockages is not None:
        fcCritBlockages.merge(
            read_feature_collection(options.custom_land_blockages))

    # create masks from the transects
    dsCritBlockMask = conversion.mask(dsBaseMesh, fcMask=fcCritBlockages)

    dsLandMask = add_critical_land_blockages(dsLandMask, dsCritBlockMask)

fcCritPassages = FeatureCollection()
dsPreserve = []

if critical_passages:
    if options.with_critical_passages:
        # merge transects for critical passages into critical_passages.geojson
        fcCritPassages.merge(
            gf.read(componentName='ocean',
                    objectType='transect',
Пример #25
0
def entry_point_compute_mpas_transect_masks():
    """ Entry point for ``compute_mpas_transect_masks()``"""

    parser = argparse.ArgumentParser()
    parser.add_argument("-m", "--mesh_file_name", dest="mesh_file_name",
                        type=str, required=True,
                        help="An MPAS mesh file")
    parser.add_argument("-g", "--geojson_file_name",
                        dest="geojson_file_name", type=str, required=True,
                        help="An Geojson file containing transects")
    parser.add_argument("-o", "--mask_file_name", dest="mask_file_name",
                        type=str, required=True,
                        help="An output MPAS transect masks file")
    parser.add_argument("-t", "--mask_types", nargs='+', dest="mask_types",
                        type=str,
                        help="Which type(s) of masks to make: cell, edge or "
                             "vertex.  Default is cell, edge and vertex.")
    parser.add_argument("-c", "--chunk_size", dest="chunk_size", type=int,
                        default=1000,
                        help="The number of cells, vertices or edges that are "
                             "processed in one operation")
    parser.add_argument("--show_progress", dest="show_progress",
                        action="store_true",
                        help="Whether to show a progress bar")
    parser.add_argument("-s", "--subdivision", dest="subdivision", type=float,
                        help="The maximum resolution (in meters) of segments "
                             "in a transect.  If a transect is too coarse, it "
                             "will be subdivided.  Default is no subdivision.")
    parser.add_argument(
        "--process_count", required=False, dest="process_count", type=int,
        help="The number of processes to use to compute masks.  The "
             "default is to use all available cores")
    parser.add_argument(
        "--multiprocessing_method", dest="multiprocessing_method",
        default='forkserver',
        help="The multiprocessing method use for python mask creation "
             "('fork', 'spawn' or 'forkserver')")
    parser.add_argument("--add_edge_sign", dest="add_edge_sign",
                        action="store_true",
                        help="Whether to add the transectEdgeMaskSigns "
                             "variable")
    parser.add_argument("--format", dest="format", type=str,
                        help="NetCDF file format")
    parser.add_argument("--engine", dest="engine", type=str,
                        help="NetCDF output engine")
    args = parser.parse_args()

    dsMesh = xr.open_dataset(args.mesh_file_name, decode_cf=False,
                             decode_times=False)
    fcMask = read_feature_collection(args.geojson_file_name)

    pool = create_pool(process_count=args.process_count,
                       method=args.multiprocessing_method)

    if args.mask_types is None:
        args.mask_types = ('cell', 'edge', 'vertex')

    earth_radius = constants['SHR_CONST_REARTH']

    with LoggingContext('compute_mpas_transect_masks') as logger:
        dsMasks = compute_mpas_transect_masks(
            dsMesh=dsMesh, fcMask=fcMask, earthRadius=earth_radius,
            maskTypes=args.mask_types, logger=logger, pool=pool,
            chunkSize=args.chunk_size, showProgress=args.show_progress,
            subdivisionResolution=args.subdivision,
            addEdgeSign=args.add_edge_sign)

    write_netcdf(dsMasks, args.mask_file_name, format=args.format,
                 engine=args.engine)
    def run_task(self):  # {{{
        """
        Plots time-series output of properties in an ocean region.
        """
        # Authors
        # -------
        # Xylar Asay-Davis

        self.logger.info("\nPlotting time series of ocean properties of {}"
                         "...".format(self.regionName))

        self.logger.info('  Load time series...')

        config = self.config
        calendar = self.calendar

        regionMaskSuffix = config.getExpression(self.sectionName,
                                                'regionMaskSuffix')

        regionMaskFile = get_region_mask(config,
                                         '{}.geojson'.format(regionMaskSuffix))

        fcAll = read_feature_collection(regionMaskFile)

        fc = FeatureCollection()
        for feature in fcAll.features:
            if feature['properties']['name'] == self.regionName:
                fc.add_feature(feature)
                break

        baseDirectory = build_config_full_path(config, 'output',
                                               'timeSeriesSubdirectory')

        startYear = config.getint('timeSeries', 'startYear')
        endYear = config.getint('timeSeries', 'endYear')
        regionGroup = self.regionGroup
        timeSeriesName = regionGroup[0].lower() + \
            regionGroup[1:].replace(' ', '')

        inFileName = '{}/{}/{}_{:04d}-{:04d}.nc'.format(
            baseDirectory, timeSeriesName, timeSeriesName, startYear, endYear)

        dsIn = xarray.open_dataset(inFileName).isel(nRegions=self.regionIndex)

        zbounds = dsIn.zbounds.values

        controlConfig = self.controlConfig
        plotControl = controlConfig is not None
        if plotControl:
            controlRunName = controlConfig.get('runs', 'mainRunName')
            baseDirectory = build_config_full_path(controlConfig, 'output',
                                                   'timeSeriesSubdirectory')

            startYear = controlConfig.getint('timeSeries', 'startYear')
            endYear = controlConfig.getint('timeSeries', 'endYear')

            inFileName = '{}/{}/{}_{:04d}-{:04d}.nc'.format(
                baseDirectory, timeSeriesName, timeSeriesName, startYear,
                endYear)
            dsRef = xarray.open_dataset(inFileName).isel(
                nRegions=self.regionIndex)

            zboundsRef = dsRef.zbounds.values

        mainRunName = config.get('runs', 'mainRunName')
        movingAverageMonths = 1

        self.logger.info('  Make plots...')

        groupLink = self.regionGroup[0].lower() + \
            self.regionGroup[1:].replace(' ', '')

        for var in self.variables:
            varName = var['name']
            mainArray = dsIn[varName]
            is3d = mainArray.attrs['is3d'] == 'True'
            if is3d:
                title = 'Volume-Mean {} in {}'.format(var['title'],
                                                      self.regionName)
            else:
                title = 'Area-Mean {} in {}'.format(var['title'],
                                                    self.regionName)

            if plotControl:
                refArray = dsRef[varName]
            xLabel = 'Time (yr)'
            yLabel = '{} ({})'.format(var['title'], var['units'])

            filePrefix = '{}_{}'.format(self.prefix, varName)
            outFileName = '{}/{}.png'.format(self.plotsDirectory, filePrefix)

            fields = [mainArray]
            lineColors = ['k']
            lineWidths = [2.5]
            legendText = [mainRunName]
            if plotControl:
                fields.append(refArray)
                lineColors.append('r')
                lineWidths.append(1.2)
                legendText.append(controlRunName)

            if is3d:
                if not plotControl or numpy.all(zbounds == zboundsRef):
                    title = '{} ({} < z < {} m)'.format(
                        title, zbounds[0], zbounds[1])
                else:
                    legendText[0] = '{} ({} < z < {} m)'.format(
                        legendText[0], zbounds[0], zbounds[1])
                    legendText[1] = '{} ({} < z < {} m)'.format(
                        legendText[1], zboundsRef[0], zboundsRef[1])

            fig = timeseries_analysis_plot(
                config,
                fields,
                calendar=calendar,
                title=title,
                xlabel=xLabel,
                ylabel=yLabel,
                movingAveragePoints=movingAverageMonths,
                lineColors=lineColors,
                lineWidths=lineWidths,
                legendText=legendText)

            # do this before the inset because otherwise it moves the inset
            # and cartopy doesn't play too well with tight_layout anyway
            plt.tight_layout()

            add_inset(fig, fc, width=2.0, height=2.0)

            savefig(outFileName, tight=False)

            caption = 'Regional mean of {}'.format(title)
            write_image_xml(config=config,
                            filePrefix=filePrefix,
                            componentName='Ocean',
                            componentSubdirectory='ocean',
                            galleryGroup='{} Time Series'.format(
                                self.regionGroup),
                            groupLink=groupLink,
                            gallery=var['title'],
                            thumbnailDescription=self.regionName,
                            imageDescription=caption,
                            imageCaption=caption)
Пример #27
0
def cellWidthVsLatLon():
    """
    Create cell width array for this mesh on a regular latitude-longitude grid.
    Returns
    -------
       cellWidth : numpy.ndarray
            m x n array, entries are desired cell width in km
       lat : numpy.ndarray
            latitude, vector of length m, with entries between -90 and 90,
            degrees
       lon : numpy.ndarray
            longitude, vector of length n, with entries between -180 and 180,
            degrees
    """
    dlon = 0.1
    dlat = dlon
    nlon = int(360. / dlon) + 1
    nlat = int(180. / dlat) + 1
    lon = np.linspace(-180., 180., nlon)
    lat = np.linspace(-90., 90., nlat)

    cellWidthSouth = mdt.EC_CellWidthVsLat(lat,
                                           cellWidthEq=30.,
                                           cellWidthMidLat=45.,
                                           cellWidthPole=45.,
                                           latPosEq=7.5,
                                           latWidthEq=3.0)

    # Transition at Equator
    cellWidthNorth = mdt.EC_CellWidthVsLat(lat,
                                           cellWidthEq=30.,
                                           cellWidthMidLat=60.,
                                           cellWidthPole=60.,
                                           latPosEq=7.5,
                                           latWidthEq=3.0)
    latTransition = 0.0
    latWidthTransition = 2.5
    cellWidthVsLat = mdt.mergeCellWidthVsLat(lat, cellWidthSouth,
                                             cellWidthNorth, latTransition,
                                             latWidthTransition)

    _, cellWidth = np.meshgrid(lon, cellWidthVsLat)

    # now, add the high-res region
    fc = read_feature_collection('high_res_region.geojson')

    so_signed_distance = signed_distance_from_geojson(fc,
                                                      lon,
                                                      lat,
                                                      max_length=0.25)

    # Equivalent to 20 degrees latitude
    trans_width = 1600e3
    trans_start = -500e3
    dx_min = 12.

    weights = 0.5 * (1 + np.tanh(
        (so_signed_distance - trans_start) / trans_width))

    cellWidth = dx_min * (1 - weights) + cellWidth * weights

    fc = read_feature_collection('north_mid_res_region.geojson')

    ar_signed_distance = signed_distance_from_geojson(fc,
                                                      lon,
                                                      lat,
                                                      max_length=0.25)

    fc = read_feature_collection('greenland.geojson')

    gr_signed_distance = signed_distance_from_geojson(fc,
                                                      lon,
                                                      lat,
                                                      max_length=0.25)

    frac = (-ar_signed_distance / (-ar_signed_distance + gr_signed_distance))

    frac = np.maximum(0., np.minimum(1., frac))

    dx_min = 15.
    dx_max = 35.

    arctic_widths = dx_max + (dx_min - dx_max) * frac

    trans_width = 1000e3
    trans_start = 0.

    weights = 0.5 * (1 + np.tanh(
        (ar_signed_distance - trans_start) / trans_width))

    cellWidth = arctic_widths * (1 - weights) + cellWidth * weights

    # fig, (ax1, ax2) = plt.subplots(1, 2, figsize=[12, 4.5])
    # index = np.argmin(np.abs(lon - (-30.5)))
    # ax1.plot(lat, cellWidth[:, index])
    # ax1.set_ylim([12., 60.])
    # ax1.set_title('lon = {}'.format(lon[index]))
    # index = np.argmin(np.abs(lon - (179.5)))
    # ax2.plot(lat, cellWidth[:, index])
    # ax2.set_ylim([12., 60.])
    # ax2.set_title('lon = {}'.format(lon[index]))
    # plt.tight_layout()
    # plt.savefig('res_vs_lat.png', dpi=200)
    #
    # fig, ax1 = plt.subplots(1, 1, figsize=[6, 4.5])
    # index = np.argmin(np.abs(lon - (-62.5)))
    # ax1.plot(lat, cellWidth[:, index])
    # ax1.set_ylim([12., 60.])
    # ax1.set_title('Drake Passage lon = {}'.format(lon[index]))
    # plt.tight_layout()
    # plt.savefig('drake_res_vs_lat.png', dpi=200)

    return cellWidth, lon, lat
Пример #28
0
        dsOut = xarray.concat(datasets, 'nRegions')
        dsOut['refBottomDepth'] = refBottomDepth

        write_netcdf(dsOut, timeSeriesFile)
    else:
        print('Time series file already exists for year {}. Skipping it...'.
              format(year))

# Make plot
timeSeriesFiles = []
for year in years:
    timeSeriesFile = '{}_year{:04d}.nc'.format(timeSeriesFile0, year)
    timeSeriesFiles.append(timeSeriesFile)

if os.path.exists(featureFile):
    fcAll = read_feature_collection(featureFile)
else:
    raise IOError('No feature file found')

for regionIndex, regionName in enumerate(regionNames):
    print('    region: {}'.format(regionName))
    fc = FeatureCollection()
    for feature in fcAll.features:
        if feature['properties']['name'] == regionName:
            fc.add_feature(feature)
            break

    dsIn = xarray.open_mfdataset(timeSeriesFiles,
                                 combine='nested',
                                 concat_dim='Time',
                                 decode_times=False).isel(nRegions=regionIndex)
Пример #29
0
def cellWidthVsLatLon():
    """
    Create cell width array for this mesh on a regular latitude-longitude grid.
    Returns
    -------
       cellWidth : ndarray
            m x n array, entries are desired cell width in km

       lat : ndarray
            latitude, vector of length m, with entries between -90 and 90,
            degrees

       lon : ndarray
            longitude, vector of length n, with entries between -180 and 180,
            degrees
    """
    # To speed up for testing, set following line to 1.0 degrees
    dlon = 1.0
    dlat = dlon
    earth_radius = constants['SHR_CONST_REARTH']
    print('\nCreating cellWidth on a lat-lon grid of: {0:.2f} x {0:.2f} '
          'degrees'.format(dlon, dlat))
    print('This can be set higher for faster test generation\n')
    nlon = int(360. / dlon) + 1
    nlat = int(180. / dlat) + 1
    lon = np.linspace(-180., 180., nlon)
    lat = np.linspace(-90., 90., nlat)
    km = 1.0e3

    print('plotting ...')
    fig = plt.figure()
    plt.clf()
    fig.set_size_inches(10.0, 10.0)
    register_sci_viz_colormaps()

    # Create cell width vs latitude for Atlantic and Pacific basins
    QU1 = np.ones(lat.size)
    EC60to30 = mdt.EC_CellWidthVsLat(lat)
    RRS30to10 = mdt.RRS_CellWidthVsLat(lat, 30, 10)
    AtlNH = RRS30to10
    AtlVsLat = mdt.mergeCellWidthVsLat(lat, EC60to30, AtlNH, 0, 6)
    PacNH = mdt.mergeCellWidthVsLat(lat, 30 * QU1, RRS30to10, 50, 10)
    PacVsLat = mdt.mergeCellWidthVsLat(lat, EC60to30, PacNH, 0, 6)

    # Expand from 1D to 2D
    _, AtlGrid = np.meshgrid(lon, AtlVsLat)
    _, PacGrid = np.meshgrid(lon, PacVsLat)

    # Signed distance of Atlantic region
    fc = read_feature_collection('Atlantic_region.geojson')
    signedDistance = signed_distance_from_geojson(fc,
                                                  lon,
                                                  lat,
                                                  earth_radius,
                                                  max_length=0.25)

    # Merge Atlantic and Pacific distrubutions smoothly
    transitionWidth = 500.0 * km
    maskSmooth = 0.5 * (1 + np.tanh(signedDistance / transitionWidth))
    cellWidthSmooth = PacGrid * maskSmooth + AtlGrid * (1 - maskSmooth)

    # Merge Atlantic and Pacific distrubutions with step function
    maskSharp = 0.5 * (1 + np.sign(signedDistance))
    cellWidthSharp = PacGrid * maskSharp + AtlGrid * (1 - maskSharp)

    # Create a land mask that is 1 over land
    fc = read_feature_collection('Americas_land_mask.geojson')
    Americas_land_mask = mask_from_geojson(fc, lon, lat)
    fc = read_feature_collection('Europe_Africa_land_mask.geojson')
    Europe_Africa_land_mask = mask_from_geojson(fc, lon, lat)
    landMask = np.fmax(Americas_land_mask, Europe_Africa_land_mask)

    # Merge: step transition over land, smooth transition over water
    cellWidth = cellWidthSharp * landMask + cellWidthSmooth * (1 - landMask)

    ax = plt.subplot(4, 2, 1)
    ax.plot(lat, AtlVsLat, label='Atlantic')
    ax.plot(lat, PacVsLat, label='Pacific')
    ax.grid(True)
    plt.title('Grid cell size [km] versus latitude')
    plt.legend()

    varNames = [
        'signedDistance', 'maskSmooth', 'cellWidthSmooth', 'maskSharp',
        'cellWidthSharp', 'landMask', 'cellWidth'
    ]
    j = 2
    for varName in varNames:
        plot_cartopy(j, varName, vars()[varName], '3Wbgy5')
        j += 1
    fig.canvas.draw()
    plt.tight_layout()

    plt.savefig('mesh_construction.png')

    return cellWidth, lon, lat