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
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)
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)
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)
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)
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
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
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)
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))
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)
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
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
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:
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)
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)
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
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)
# 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
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)
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',
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)
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
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)
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