def run(self): """ Run this step of the testcase """ with xarray.open_dataset('restart.nc') as ds: mesh_short_name = ds.attrs['MPAS_Mesh_Short_Name'] mesh_prefix = ds.attrs['MPAS_Mesh_Prefix'] prefix = 'MPAS_Mesh_{}'.format(mesh_prefix) creation_date = ds.attrs['{}_Version_Creation_Date'.format(prefix)] try: os.makedirs('../assembled_files/inputdata/ocn/mpas-o/{}'.format( mesh_short_name)) except OSError: pass source_filename = 'restart.nc' dest_filename = 'mpaso.{}.{}.nc'.format(mesh_short_name, creation_date) with xarray.open_dataset(source_filename) as ds: ds.load() ds = ds.drop_vars('xtime') write_netcdf(ds, dest_filename) symlink( '../../../../../ocean_initial_condition/{}'.format(dest_filename), '../assembled_files/inputdata/ocn/mpas-o/{}/{}'.format( mesh_short_name, dest_filename))
def run(self): """ Run this step of the test case """ config = self.config logger = self.logger section = config['gotm'] nx = section.getint('nx') ny = section.getint('ny') dc = section.getfloat('dc') dsMesh = make_planar_hex_mesh(nx=nx, ny=ny, dc=dc, nonperiodic_x=False, nonperiodic_y=False) write_netcdf(dsMesh, 'grid.nc') dsMesh = cull(dsMesh, logger=logger) dsMesh = convert(dsMesh, graphInfoFileName='graph.info', logger=logger) write_netcdf(dsMesh, 'mesh.nc') replacements = dict() replacements['config_periodic_planar_vert_levels'] = \ config.get('gotm', 'vert_levels') replacements['config_periodic_planar_bottom_depth'] = \ config.get('gotm', 'bottom_depth') self.update_namelist_at_runtime(options=replacements) run_model(self)
def _compute_barotropic_streamfunction(dsMesh, ds, out_dir, show_progress): """ compute the barotropic streamfunction for the given mesh and monthly-mean data set """ bsfFileName = '{}/barotropicStreamfunction.nc'.format(out_dir) if file_complete(ds, bsfFileName): return bsfVertex = _compute_barotropic_streamfunction_vertex( dsMesh, ds, show_progress) bsfCell = _compute_barotropic_streamfunction_cell(dsMesh, bsfVertex) dsBSF = xarray.Dataset() dsBSF['xtime_startMonthly'] = ds.xtime_startMonthly dsBSF['xtime_endMonthly'] = ds.xtime_endMonthly dsBSF['bsfVertex'] = bsfVertex dsBSF.bsfVertex.attrs['units'] = 'Sv' dsBSF.bsfVertex.attrs['description'] = 'barotropic streamfunction ' \ 'on vertices' dsBSF['bsfCell'] = bsfCell dsBSF.bsfCell.attrs['units'] = 'Sv' dsBSF.bsfCell.attrs['description'] = 'barotropic streamfunction ' \ 'on cells' dsBSF = dsBSF.transpose('Time', 'nCells', 'nVertices') write_netcdf(dsBSF, bsfFileName)
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 run(self): """ Run this step of the test case """ logger = self.logger section = self.config['enthalpy_benchmark'] nx = section.getint('nx') ny = section.getint('ny') dc = section.getfloat('dc') levels = section.get('levels') dsMesh = make_planar_hex_mesh(nx=nx, ny=ny, dc=dc, nonperiodic_x=True, nonperiodic_y=True) write_netcdf(dsMesh, 'grid.nc') dsMesh = cull(dsMesh, logger=logger) dsMesh = convert(dsMesh, logger=logger) write_netcdf(dsMesh, 'mpas_grid.nc') args = ['create_landice_grid_from_generic_MPAS_grid.py', '-i', 'mpas_grid.nc', '-o', 'landice_grid.nc', '-l', levels, '--thermal'] check_call(args, logger) make_graph_file(mesh_filename='landice_grid.nc', graph_filename='graph.info') _setup_initial_conditions(section, 'landice_grid.nc')
def make_moc_basins_and_transects(gf, mesh_filename, mask_and_transect_filename, geojson_filename=None, mask_filename=None, logger=None, dir=None): """ Builds features defining the ocean basins and southern transects used in computing the meridional overturning circulation (MOC) Parameters ---------- gf : geometric_features.GeometricFeatures An object that knows how to download and read geometric features mesh_filename : str A file with MPAS mesh information mask_and_transect_filename : str A file to write the MOC region masks and southern-boundary transects to geojson_filename : str, optional A file to write MOC regions to mask_filename : str, optional A file to write MOC region masks to logger : ``logging.Logger``, optional A logger for the output if not stdout dir : str, optional A directory in which a temporary directory will be added with files produced during conversion and then deleted upon completion. Returns ------- fc : geometric_features.FeatureCollection The new feature collection """ # Authors # ------- # Xylar Asay-Davis fcMOC = moc(gf) if geojson_filename is not None: fcMOC.to_geojson(geojson_filename) dsMesh = xarray.open_dataset(mesh_filename) dsMasks = mpas_tools.mesh.conversion.mask(dsMesh=dsMesh, fcMask=fcMOC, logger=logger, dir=dir) if mask_filename is not None: write_netcdf(dsMasks, mask_filename, char_dim_name='StrLen') dsMasksAndTransects = add_moc_southern_boundary_transects(dsMasks, dsMesh, logger=logger) write_netcdf(dsMasksAndTransects, mask_and_transect_filename, char_dim_name='StrLen')
def add_depth(inFileName, outFileName, coordFileName=None): """ Add a 1D depth coordinate to an MPAS-Ocean file. Parameters ---------- inFileName : str An input MPAS-Ocean file that depth should be added to, used for coords if another file is not provided via ``coordFileName``. outFileName : str An output MPAS-Ocean file with depth added coordFileName : str, optional An MPAS-Ocean file with ``refBottomDepth`` """ if coordFileName is None: coordFileName = inFileName ds = xarray.open_dataset(inFileName, mask_and_scale=False) if 'nVertLevels' in ds.dims: ds = ds.rename({'nVertLevels': 'depth'}) dsCoord = xarray.open_dataset(coordFileName, mask_and_scale=False) dsCoord = dsCoord.rename({'nVertLevels': 'depth'}) depth, depth_bnds = compute_depth(dsCoord.refBottomDepth) ds.coords['depth'] = ('depth', depth) ds.depth.attrs['long_name'] = 'reference depth of the center of ' \ 'each vertical level' ds.depth.attrs['standard_name'] = 'depth' ds.depth.attrs['units'] = 'meters' ds.depth.attrs['axis'] = 'Z' ds.depth.attrs['positive'] = 'down' ds.depth.attrs['valid_min'] = depth_bnds[0, 0] ds.depth.attrs['valid_max'] = depth_bnds[-1, 1] ds.depth.attrs['bounds'] = 'depth_bnds' ds.coords['depth_bnds'] = (('depth', 'nbnd'), depth_bnds) ds.depth_bnds.attrs['long_name'] = 'Gridcell depth interfaces' for varName in ds.data_vars: var = ds[varName] if 'depth' in var.dims: var = var.assign_coords(depth=ds.depth) ds[varName] = var time = datetime.now().strftime('%c') history = '{}: {}'.format(time, ' '.join(sys.argv)) if 'history' in ds.attrs: ds.attrs['history'] = '{}\n{}'.format(history, ds.attrs['history']) else: ds.attrs['history'] = history write_netcdf(ds, outFileName)
def write_time_varying_zmid(inFileName, outFileName, coordFileName=None, prefix=''): """ Add a 3D, time-independent depth coordinate to an MPAS-Ocean file. Parameters ---------- inFileName : str An input MPAS-Ocean file with some form of ``layerThickness``, and also ``bottomDepth`` and ``maxLevelCell`` if no ``coordFileName`` is provided. outFileName : str An output MPAS-Ocean file with ``zMid`` for each time in the input file coordFileName : str, optional An MPAS-Ocean file with ``bottomDepth`` and ``maxLevelCell`` prefix : str, optional A prefix on ``layerThickness`` (in) and ``zMid`` (out), such as ``timeMonthly_avg_`` """ if coordFileName is None: coordFileName = inFileName dsCoord = xarray.open_dataset(coordFileName) dsCoord = dsCoord.rename({'nVertLevels': 'depth'}) dsIn = xarray.open_dataset(inFileName) dsIn = dsIn.rename({'nVertLevels': 'depth'}) inVarName = '{}layerThickness'.format(prefix) outVarName = '{}zMid'.format(prefix) layerThickness = dsIn[inVarName] zMid = compute_zmid(dsCoord.bottomDepth, dsCoord.maxLevelCell, layerThickness, depth_dim='depth') dsOut = xarray.Dataset() dsOut[outVarName] = zMid fillValue = netCDF4.default_fillvals['f8'] dsOut[outVarName] = dsOut[outVarName].where(dsOut[outVarName].notnull(), other=fillValue) dsOut[outVarName].attrs['units'] = 'meters' dsOut[outVarName].attrs['positive'] = 'up' dsOut[outVarName].attrs['_FillValue'] = fillValue time = datetime.now().strftime('%c') history = '{}: {}'.format(time, ' '.join(sys.argv)) dsOut.attrs['history'] = history write_netcdf(dsOut, outFileName)
def add_zmid(inFileName, outFileName, coordFileName=None): """ Add a 3D, time-independent depth coordinate to an MPAS-Ocean file. Parameters ---------- inFileName : str An input MPAS-Ocean file that ``zMid`` should be added to, used for coords if another file is not provided via ``coordFileName``. outFileName : str An output MPAS-Ocean file with ``zMid`` added coordFileName : str, optional An MPAS-Ocean file with ``bottomDepth``, ``maxLevelCell`` and ``layerThickness`` but not ``zMid`` """ if coordFileName is None: coordFileName = inFileName ds = xarray.open_dataset(inFileName, mask_and_scale=False) if 'nVertLevels' in ds.dims: ds = ds.rename({'nVertLevels': 'depth'}) # dsCoord doesn't have masking disabled because we want it for zMid dsCoord = xarray.open_dataset(coordFileName) dsCoord = dsCoord.rename({'nVertLevels': 'depth'}) if 'Time' in dsCoord.dims: dsCoord = dsCoord.isel(Time=0) ds.coords['zMid'] = compute_zmid(dsCoord.bottomDepth, dsCoord.maxLevelCell, dsCoord.layerThickness, depth_dim='depth') fillValue = netCDF4.default_fillvals['f8'] ds.coords['zMid'] = ds.zMid.where(ds.zMid.notnull(), other=fillValue) ds.zMid.attrs['units'] = 'meters' ds.zMid.attrs['positive'] = 'up' ds.zMid.attrs['_FillValue'] = fillValue for varName in ds.data_vars: var = ds[varName] if 'nCells' in var.dims and 'depth' in var.dims: var = var.assign_coords(zMid=ds.zMid) ds[varName] = var time = datetime.now().strftime('%c') history = '{}: {}'.format(time, ' '.join(sys.argv)) if 'history' in ds.attrs: ds.attrs['history'] = '{}\n{}'.format(history, ds.attrs['history']) else: ds.attrs['history'] = history write_netcdf(ds, outFileName)
def _make_moc_masks(mesh_short_name, logger, cores): gf = GeometricFeatures() mesh_filename = 'restart.nc' function, prefix, date = get_aggregator_by_name('MOC Basins') fcMask = function(gf) suffix = '{}{}'.format(prefix, date) geojson_filename = '{}.geojson'.format(suffix) mask_filename = '{}_{}.nc'.format(mesh_short_name, suffix) fcMask.to_geojson(geojson_filename) # 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 args = ['compute_mpas_region_masks', '-m', mesh_filename, '-g', geojson_filename, '-o', mask_filename, '-t', 'cell', '--process_count', '{}'.format(cores), '--format', netcdf_format, '--engine', netcdf_engine] check_call(args, logger=logger) mask_and_transect_filename = '{}_mocBasinsAndTransects{}.nc'.format( mesh_short_name, date) dsMesh = xarray.open_dataset(mesh_filename) dsMask = xarray.open_dataset(mask_filename) dsMasksAndTransects = add_moc_southern_boundary_transects( dsMask, dsMesh, logger=logger) write_netcdf(dsMasksAndTransects, mask_and_transect_filename, char_dim_name='StrLen') # make links in output directories (both inputdata and diagnostics) output_dir = '../assembled_files/inputdata/ocn/mpas-o/{}'.format( mesh_short_name) symlink( '../../../../../diagnostics_files/{}'.format( mask_and_transect_filename), '{}/{}'.format(output_dir, mask_and_transect_filename)) output_dir = '../assembled_files/diagnostics/mpas_analysis/' \ 'region_masks' symlink( '../../../../diagnostics_files/{}'.format( mask_and_transect_filename), '{}/{}'.format(output_dir, mask_and_transect_filename))
def make_moc_basins_and_transects(gf, mesh_filename, mask_and_transect_filename, geojson_filename=None, mask_filename=None, logger=None): """ Builds features defining the ocean basins and southern transects used in computing the meridional overturning circulation (MOC) Parameters ---------- gf : ``GeometricFeatures`` An object that knows how to download and read geometric featuers mesh_filename : str A file with MPAS mesh information mask_and_transect_filename : str A file to write the MOC region masks and southern-boundary transects to geojson_filename : str, optional A file to write MOC regions to mask_filename : str, optional A file to write MOC region masks to logger : ``logging.Logger``, optional A logger for the output if not stdout Returns ------- fc : ``FeatureCollection`` The new feature collection """ # Authors # ------- # Xylar Asay-Davis fcMOC = build_moc_basins(gf, logger) if geojson_filename is not None: fcMOC.to_geojson(geojson_filename) dsMesh = xarray.open_dataset(mesh_filename) dsMasks = mpas_tools.conversion.mask(dsMesh=dsMesh, fcMask=fcMOC, logger=logger) if mask_filename is not None: write_netcdf(dsMasks, mask_filename) dsMasksAndTransects = add_moc_southern_boundary_transects(dsMasks, dsMesh, logger=logger) write_netcdf(dsMasksAndTransects, mask_and_transect_filename)
def convert(dsIn, graphInfoFileName=None, logger=None, dir=None): """ Use ``MpasMeshConverter.x`` to convert an input mesh to a valid MPAS mesh that is fully compliant with the MPAS mesh specification. https://mpas-dev.github.io/files/documents/MPAS-MeshSpec.pdf Parameters ---------- dsIn : xarray.Dataset A data set to convert graphInfoFileName : str, optional A file path (relative or absolute) where the graph file (typically ``graph.info`` should be written out. By default, ``graph.info`` is not saved. logger : logging.Logger, optional A logger for the output if not stdout dir : str, optional A directory in which a temporary directory will be added with files produced during conversion and then deleted upon completion. Returns ------- dsOut : xarray.Dataset The MPAS mesh """ if dir is not None: dir = os.path.abspath(dir) with TemporaryDirectory(dir=dir) as tempdir: inFileName = '{}/mesh_in.nc'.format(tempdir) write_netcdf(dsIn, inFileName) outFileName = '{}/mesh_out.nc'.format(tempdir) if graphInfoFileName is not None: graphInfoFileName = os.path.abspath(graphInfoFileName) # go into the directory of the output file so the graph.info file ends # up in the same place owd = os.getcwd() outDir = os.path.dirname(outFileName) os.chdir(outDir) _call_subprocess(['MpasMeshConverter.x', inFileName, outFileName], logger) os.chdir(owd) dsOut = xarray.open_dataset(outFileName) dsOut.load() if graphInfoFileName is not None: shutil.copyfile('{}/graph.info'.format(outDir), graphInfoFileName) return dsOut
def mask(dsMesh, fcMask=None, fcSeed=None, logger=None, dir=None): """ Use ``MpasMaskCreator.x`` to create a set of region masks either from mask feature collections or from seed points to be used to flood fill Parameters ---------- dsMesh : xarray.Dataset, optional An MPAS mesh on which the masks should be created fcMask : geometric_features.FeatureCollection, optional A feature collection containing features to use to create the mask fcSeed : geometric_features.FeatureCollection, optional A feature collection with points to use a seeds for a flood fill that will create a mask of all cells connected to the seed points logger : logging.Logger, optional A logger for the output if not stdout dir : str, optional A directory in which a temporary directory will be added with files produced during mask creation and then deleted upon completion. Returns ------- dsMask : xarray.Dataset The masks """ if dir is not None: dir = os.path.abspath(dir) with TemporaryDirectory(dir=dir) as tempdir: inFileName = '{}/mesh_in.nc'.format(tempdir) write_netcdf(dsMesh, inFileName) outFileName = '{}/mesh_out.nc'.format(tempdir) args = ['MpasMaskCreator.x', inFileName, outFileName] if fcMask is not None: fileName = '{}/mask.geojson'.format(tempdir) fcMask.to_geojson(fileName) args.extend(['-f', fileName]) if fcSeed is not None: fileName = '{}/seed.geojson'.format(tempdir) fcSeed.to_geojson(fileName) args.extend(['-s', fileName]) _call_subprocess(args, logger) dsOut = xarray.open_dataset(outFileName) dsOut.load() return dsOut
def mask(dsMesh, fcMask=None, fcSeed=None, positiveLon=False, logger=None): ''' Use ``MpasMaskCreator.x`` to create a set of region masks either from mask feature collecitons or from seed points to be used to flood fill Parameters ---------- dsMesh : ``xarray.Dataset``, optional An MPAS mesh on which the masks should be created fcMask : ``geometric_features.FeatureCollection``, optional A feature collection containing features to use to create the mask fcSeed : ``geometric_features.FeatureCollection``, optional A feature collection with points to use a seeds for a flood fill that will create a mask of all cells connected to the seed points logger : ``logging.Logger``, optional A logger for the output if not stdout Returns ------- dsMask : ``xarray.Dataset`` The masks ''' with TemporaryDirectory() as tempdir: inFileName = '{}/mesh_in.nc'.format(tempdir) write_netcdf(dsMesh, inFileName) outFileName = '{}/mesh_out.nc'.format(tempdir) args = ['MpasMaskCreator.x', inFileName, outFileName] if fcMask is not None: fileName = '{}/mask.geojson'.format(tempdir) fcMask.to_geojson(fileName) args.extend(['-f', fileName]) if fcSeed is not None: fileName = '{}/seed.geojson'.format(tempdir) fcSeed.to_geojson(fileName) args.extend(['-s', fileName]) if positiveLon: args.append('--positive_lon') _call_subprocess(args, logger) dsOut = xarray.open_dataset(outFileName) dsOut.load() return dsOut
def build_mesh(preserve_floodplain=False, floodplain_elevation=20.0, do_inject_bathymetry=False): print('Step 1. Build cellWidth array as function of latitude and ' 'longitude') cellWidth, lon, lat = define_base_mesh.cellWidthVsLatLon() da = xarray.DataArray(cellWidth, dims=['lat', 'lon'], coords={ 'lat': lat, 'lon': lon }, name='cellWidth') da.to_netcdf('cellWidthVsLatLon.nc') print('Step 2. Generate mesh with JIGSAW') jigsaw_driver(cellWidth, lon, lat) print('Step 3. Convert triangles from jigsaw format to netcdf') jigsaw_to_netcdf(msh_filename='mesh-MESH.msh', output_name='mesh_triangles.nc', on_sphere=True) print('Step 4. Convert from triangles to MPAS mesh') write_netcdf(convert(xarray.open_dataset('mesh_triangles.nc')), 'base_mesh.nc') print('Step 5. Inject correct meshDensity variable into base mesh file') inject_meshDensity(cw_filename='cellWidthVsLatLon.nc', mesh_filename='base_mesh.nc') if do_inject_bathymetry: print('Step 6. Injecting bathymetry') inject_bathymetry(mesh_file='base_mesh.nc') if preserve_floodplain: print('Step 7. Injecting flag to preserve floodplain') inject_preserve_floodplain(mesh_file='base_mesh.nc', floodplain_elevation=floodplain_elevation) print('Step 8. Create vtk file for visualization') args = [ 'paraview_vtk_field_extractor.py', '--ignore_time', '-l', '-d', 'maxEdges=0', '-v', 'allOnCells', '-f', 'base_mesh.nc', '-o', 'base_mesh_vtk' ] print("running", ' '.join(args)) subprocess.check_call(args, env=os.environ.copy()) print("***********************************************") print("** The global mesh file is base_mesh.nc **") print("***********************************************")
def main(args): earth_radius = 6371.0e3 out_dir = os.path.abspath(args.output) out_base = os.path.basename(args.output) out_basepath = out_dir + "/" + out_base out_filename = out_dir + "/" + out_base + "_mpas.nc" p = bool(args.plots) print(p) if not os.path.exists(out_dir): os.makedirs(out_dir) else: print("Base dir already exists: ", out_dir) if (args.opt == "unif" or args.opt == "localref"): #Density based grid if (args.opt == "unif"): cellWidth, lon, lat = jutil.cellWidthVsLatLon(args.r) elif (args.opt == "localref"): cellWidth, lon, lat = jutil.localrefVsLatLon(args.r, p=p) mesh_file = jutil.jigsaw_gen_sph_grid(cellWidth, lon, lat, basename=out_basepath) elif (args.opt == "icos"): #Icosahedral grid mesh_file = jutil.jigsaw_gen_icos_grid(basename=out_basepath, level=4) else: print("Unknown option") exit(1) #Convert jigsaw mesh to netcdf jigsaw_to_netcdf(msh_filename=mesh_file, output_name=out_basepath + '_triangles.nc', on_sphere=True, sphere_radius=1.0) #convert to mpas grid specific format write_netcdf( convert(xarray.open_dataset(out_basepath + '_triangles.nc'), dir=out_dir, graphInfoFileName=out_basepath + "_graph.info"), out_filename)
def _vertical_cumsum_horizontal_transport(ds, outFileName): """ compute the cumsum in the vertical of the horizontal transport """ if file_complete(ds, outFileName): return chunks = {'Time': 1, 'nInternalEdges': 32768} ds = ds.chunk(chunks) nTime = ds.sizes['Time'] nInternalEdges = ds.sizes['nInternalEdges'] nz = ds.sizes['nz'] transport = ds.transport.rename({'nzM1': 'nz'}) transportSumTop = xarray.DataArray(numpy.zeros((nTime, nInternalEdges, 1)), dims=('Time', 'nInternalEdges', 'nz')).chunk(chunks) # zeros on top and then the cumsum for the rest transportSum = xarray.concat( [transportSumTop, transport.cumsum(dim='nz')], dim='nz') # mask out locations on the output where no input-grid layers overlap # with either the output layer above or the one below mask = ds.mask.rename({'nzM1': 'nz'}) maskTop = mask.isel(nz=0) maskBot = mask.isel(nz=nz - 2) outMask = xarray.concat( [maskTop, numpy.logical_or(mask[:, 0:-1, :], mask[:, 1:, :]), maskBot], dim='nz') dsOut = xarray.Dataset() dsOut['xtime_startMonthly'] = ds.xtime_startMonthly dsOut['xtime_endMonthly'] = ds.xtime_endMonthly dsOut['z'] = ds.z dsOut['transportSum'] = transportSum dsOut['mask'] = outMask dsOut = dsOut.transpose('Time', 'nz', 'nInternalEdges') print('compute and caching vertical transport sum on z-level grid:') write_netcdf(dsOut, outFileName)
def run(self): """ Run this step of the testcase """ with_ice_shelf_cavities = self.with_ice_shelf_cavities with xarray.open_dataset('restart.nc') as ds: mesh_short_name = ds.attrs['MPAS_Mesh_Short_Name'] mesh_prefix = ds.attrs['MPAS_Mesh_Prefix'] prefix = 'MPAS_Mesh_{}'.format(mesh_prefix) creation_date = ds.attrs['{}_Version_Creation_Date'.format(prefix)] try: os.makedirs( '../assembled_files/inputdata/ocn/mpas-seaice/{}'.format( mesh_short_name)) except OSError: pass dest_filename = 'mpassi.{}.{}.nc'.format(mesh_short_name, creation_date) keep_vars = [ 'areaCell', 'cellsOnCell', 'edgesOnCell', 'fCell', 'indexToCellID', 'latCell', 'lonCell', 'meshDensity', 'nEdgesOnCell', 'verticesOnCell', 'xCell', 'yCell', 'zCell', 'angleEdge', 'cellsOnEdge', 'dcEdge', 'dvEdge', 'edgesOnEdge', 'fEdge', 'indexToEdgeID', 'latEdge', 'lonEdge', 'nEdgesOnCell', 'nEdgesOnEdge', 'verticesOnEdge', 'weightsOnEdge', 'xEdge', 'yEdge', 'zEdge', 'areaTriangle', 'cellsOnVertex', 'edgesOnVertex', 'fVertex', 'indexToVertexID', 'kiteAreasOnVertex', 'latVertex', 'lonVertex', 'xVertex', 'yVertex', 'zVertex' ] if with_ice_shelf_cavities: keep_vars.append('landIceMask') with xarray.open_dataset('restart.nc') as ds: ds.load() ds = ds[keep_vars] write_netcdf(ds, dest_filename) symlink( '../../../../../seaice_initial_condition/{}'.format(dest_filename), '../assembled_files/inputdata/ocn/mpas-seaice/{}/{}'.format( mesh_short_name, dest_filename))
def convert(dsIn, graphInfoFileName=None): ''' Use ``MpasMeshConverter.x`` to convert an input mesh to a valid MPAS mesh that is fully compliant with the MPAS mesh specification. https://mpas-dev.github.io/files/documents/MPAS-MeshSpec.pdf Parameters ---------- dsIn : ``xarray.Dataset`` A data set to convert graphInfoFileName : str, optional A file path (relative or absolute) where the graph file (typically ``graph.info`` should be written out. By default, ``graph.info`` is not saved. Returns ------- dsOut : ``xarray.Dataset`` The MPAS mesh ''' with TemporaryDirectory() as tempdir: inFileName = '{}/mesh_in.nc'.format(tempdir) write_netcdf(dsIn, inFileName) outFileName = '{}/mesh_out.nc'.format(tempdir) if graphInfoFileName is not None: graphInfoFileName = os.path.abspath(graphInfoFileName) # go into the directory of the output file so the graph.info file ends # up in the same place owd = os.getcwd() outDir = os.path.dirname(outFileName) os.chdir(outDir) subprocess.check_call(['MpasMeshConverter.x', inFileName, outFileName]) os.chdir(owd) dsOut = xarray.open_dataset(outFileName) dsOut.load() if graphInfoFileName is not None: shutil.copyfile('{}/graph.info'.format(outDir), graphInfoFileName) return dsOut
def run(self): """ Run this step of the test case """ logger = self.logger section = self.config['eismint2'] nx = section.getint('nx') ny = section.getint('ny') dc = section.getfloat('dc') dsMesh = make_planar_hex_mesh(nx=nx, ny=ny, dc=dc, nonperiodic_x=False, nonperiodic_y=False) dsMesh = convert(dsMesh, logger=logger) write_netcdf(dsMesh, 'mpas_grid.nc') dsMesh.close() radius = section.get('radius') args = [ 'define_cullMask.py', '-f', 'mpas_grid.nc', '-m', 'radius', '-d', radius ] check_call(args, logger) dsMesh = xarray.open_dataset('mpas_grid.nc') dsMesh = cull(dsMesh, logger=logger) dsMesh = convert(dsMesh, logger=logger) write_netcdf(dsMesh, 'mpas_grid2.nc') levels = section.get('levels') args = [ 'create_landice_grid_from_generic_MPAS_grid.py', '-i', 'mpas_grid2.nc', '-o', 'landice_grid.nc', '-l', levels, '--thermal', '--beta' ] check_call(args, logger) make_graph_file(mesh_filename='landice_grid.nc', graph_filename='graph.info')
def make_region_masks(mesh_name, suffix, fcMask): # {{{ mesh_filename = '../mesh.nc' geojson_filename = '{}.geojson'.format(suffix) mask_filename = '{}_{}.nc'.format(mesh_name, suffix) fcMask.to_geojson(geojson_filename) dsMesh = xr.open_dataset(mesh_filename) dsMask = mpas_tools.conversion.mask(dsMesh, fcMask=fcMask) write_netcdf(dsMask, mask_filename) # make links in output directory output_dir = '../assembled_files_for_upload/diagnostics/mpas_analysis/' \ 'region_masks' make_link('../../../../transects_and_regions/{}'.format(mask_filename), '{}/{}'.format(output_dir, mask_filename))
def _write_time_varying_forcing(self, ds_init): """ Write time-varying land-ice forcing and update the initial condition """ config = self.config dates = config.get('isomip_plus_forcing', 'dates') dates = [date.ljust(64) for date in dates.replace(',', ' ').split()] scales = config.get('isomip_plus_forcing', 'scales') scales = [float(scale) for scale in scales.replace(',', ' ').split()] ds_out = xarray.Dataset() ds_out['xtime'] = ('Time', dates) ds_out['xtime'] = ds_out.xtime.astype('S') landIceDraft = list() landIcePressure = list() landIceFraction = list() for scale in scales: landIceDraft.append(scale * ds_init.landIceDraft) landIcePressure.append(scale * ds_init.landIcePressure) landIceFraction.append(ds_init.landIceFraction) ds_out['landIceDraftForcing'] = xarray.concat(landIceDraft, 'Time') ds_out.landIceDraftForcing.attrs['units'] = 'm' ds_out.landIceDraftForcing.attrs['long_name'] = \ 'The approximate elevation of the land ice-ocean interface' ds_out['landIcePressureForcing'] = \ xarray.concat(landIcePressure, 'Time') ds_out.landIcePressureForcing.attrs['units'] = 'm' ds_out.landIcePressureForcing.attrs['long_name'] = \ 'Pressure from the weight of land ice at the ice-ocean interface' ds_out['landIceFractionForcing'] = \ xarray.concat(landIceFraction, 'Time') ds_out.landIceFractionForcing.attrs['units'] = 'unitless' ds_out.landIceFractionForcing.attrs['long_name'] = \ 'The fraction of each cell covered by land ice' write_netcdf(ds_out, 'land_ice_forcing.nc') ds_init['landIceDraft'] = scales[0] * ds_init.landIceDraft ds_init['ssh'] = ds_init.landIceDraft ds_init['landIcePressure'] = scales[0] * ds_init.landIcePressure
def run(self): """ Run this step of the test case """ config = self.config section = config['soma'] options = dict( config_eos_linear_alpha=section.get('eos_linear_alpha'), config_soma_density_difference=section.get('density_difference'), config_soma_surface_temperature=section.get('surface_temperature'), config_soma_surface_salinity=section.get('surface_salinity'), config_soma_salinity_gradient=section.get('salinity_gradient'), config_soma_thermocline_depth=section.get('thermocline_depth'), config_soma_density_difference_linear=section.get( 'density_difference_linear'), config_soma_phi=section.get('phi'), config_soma_shelf_depth=section.get('shelf_depth'), config_soma_bottom_depth=section.get('bottom_depth')) for out_name in ['namelist_mark_land.ocean', 'namelist.ocean']: self.update_namelist_at_runtime(options=options, out_name=out_name) ds_mesh = convert(xarray.open_dataset('base_mesh.nc'), graphInfoFileName='base_graph.info', logger=self.logger) write_netcdf(ds_mesh, 'mesh.nc') run_model(self, namelist='namelist_mark_land.ocean', streams='streams_mark_land.ocean', graph_file='base_graph.info') ds_mesh = cull(xarray.open_dataset('masked_initial_state.nc'), graphInfoFileName='graph.info', logger=self.logger) write_netcdf(ds_mesh, 'culled_mesh.nc') run_model(self, namelist='namelist.ocean', streams='streams.ocean', graph_file='graph.info')
def run(self): """ Run this step of the test case """ mesh_type = self.mesh_type logger = self.logger config = self.config section = config['dome'] if mesh_type == '2000m': nx = section.getint('nx') ny = section.getint('ny') dc = section.getfloat('dc') dsMesh = make_planar_hex_mesh(nx=nx, ny=ny, dc=dc, nonperiodic_x=True, nonperiodic_y=True) write_netcdf(dsMesh, 'grid.nc') dsMesh = cull(dsMesh, logger=logger) dsMesh = convert(dsMesh, logger=logger) write_netcdf(dsMesh, 'mpas_grid.nc') levels = section.get('levels') args = [ 'create_landice_grid_from_generic_MPAS_grid.py', '-i', 'mpas_grid.nc', '-o', 'landice_grid.nc', '-l', levels ] check_call(args, logger) make_graph_file(mesh_filename='landice_grid.nc', graph_filename='graph.info') _setup_dome_initial_conditions(config, logger, filename='landice_grid.nc')
def _compute_and_write_haney_number(dsMesh, ds, folder, showProgress=False): """ compute the Haney number rx1 for each edge, and interpolate it to cells. """ haneyFileName = '{}/haney.nc'.format(folder) if file_complete(ds, haneyFileName): return haneyEdge, haneyCell = compute_haney_number( dsMesh, ds.timeMonthly_avg_layerThickness, ds.timeMonthly_avg_ssh, showProgress) dsHaney = xarray.Dataset() dsHaney['xtime_startMonthly'] = ds.xtime_startMonthly dsHaney['xtime_endMonthly'] = ds.xtime_endMonthly dsHaney['haneyEdge'] = haneyEdge dsHaney.haneyEdge.attrs['units'] = 'unitless' dsHaney.haneyEdge.attrs['description'] = 'Haney number on edges' dsHaney['haneyCell'] = haneyCell dsHaney.haneyCell.attrs['units'] = 'unitless' dsHaney.haneyCell.attrs['description'] = 'Haney number on cells' dsHaney = dsHaney.transpose('Time', 'nCells', 'nEdges', 'nVertLevels') write_netcdf(dsHaney, haneyFileName)
def run(self): """ Run this step of the test case """ config = self.config resolution = float(self.resolution) section = config['planar_convergence'] nx_1km = section.getint('nx_1km') ny_1km = section.getint('ny_1km') nx = int(nx_1km / resolution) ny = int(ny_1km / resolution) dc = resolution * 1e3 ds_mesh = make_planar_hex_mesh(nx=nx, ny=ny, dc=dc, nonperiodic_x=False, nonperiodic_y=False) center(ds_mesh) write_netcdf(ds_mesh, 'mesh.nc') make_graph_file('mesh.nc', 'graph.info')
def make_diff(mesh, refMeshFileName, diffFileName): refMesh = xarray.open_dataset(refMeshFileName) diff = xarray.Dataset() for variable in mesh.data_vars: if variable in refMesh: diff[variable] = mesh[variable] - refMesh[variable] print(diff[variable].name, float(numpy.abs(diff[variable]).max())) else: print('mesh has extra variable {}'.format(mesh[variable].name)) for variable in refMesh.data_vars: if variable not in mesh: print('mesh mising variable {}'.format(refMesh[variable].name)) for attr in refMesh.attrs: if attr not in mesh.attrs: print('mesh mising attribute {}'.format(attr)) for attr in mesh.attrs: if attr not in refMesh.attrs: print('mesh has extra attribute {}'.format(attr)) write_netcdf(diff, diffFileName)
def run(self): """ Run this step of the test case """ config = self.config logger = self.logger section = config['ziso'] nx = section.getint('nx') ny = section.getint('ny') dc = section.getfloat('dc') dsMesh = make_planar_hex_mesh(nx=nx, ny=ny, dc=dc, nonperiodic_x=False, nonperiodic_y=True) write_netcdf(dsMesh, 'base_mesh.nc') dsMesh = cull(dsMesh, logger=logger) dsMesh = convert(dsMesh, graphInfoFileName='culled_graph.info', logger=logger) write_netcdf(dsMesh, 'culled_mesh.nc') ds = _write_initial_state(config, dsMesh, self.with_frazil) _write_forcing(config, ds.yCell, ds.zMid)
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 remap_rignot(inFileName, meshFileName, meshName, outFileName, mappingDirectory='.', method='conserve', renormalizationThreshold=None, inVarName='melt_actual', mpiTasks=1): # {{{ """ Remap the Rignot et al. (2013) melt rates at 1 km resolution to an MPAS mesh Parameters ---------- inFileName : str The original Rignot et al. (2013) melt rates meshFileName : str The MPAS mesh meshName : str The name of the mesh (e.g. oEC60to30wISC), used in the name of the mapping file outFileName : str The melt rates interpolated to the MPAS mesh with ocean sensible heat fluxes added on (assuming insulating ice) mappingDirectory : str The directory where the mapping file should be stored (if it is to be computed) or where it already exists (if not) method : {'bilinear', 'neareststod', 'conserve'}, optional The method of interpolation used, see documentation for `ESMF_RegridWeightGen` for details. renormalizationThreshold : float, optional The minimum weight of a denstination cell after remapping, below which it is masked out, or ``None`` for no renormalization and masking. inVarName : {'melt_actual', 'melt_steadystate'} Whether to use the melt rate for the time period covered in Rignot et al. (2013) with observed thinning/thickening or the melt rates that would be required if ice shelves were in steady state. mpiTasks : int, optional The number of MPI tasks to use to compute the mapping file """ ds = xr.open_dataset(inFileName) lx = np.abs(1e-3 * (ds.xaxis.values[-1] - ds.xaxis.values[0])) ly = np.abs(1e-3 * (ds.yaxis.values[-1] - ds.yaxis.values[0])) inGridName = '{}x{}km_1.0km_Antarctic_stereo'.format(lx, ly) 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') inDescriptor = ProjectionGridDescriptor.read(projection, inFileName, xVarName='xaxis', yVarName='yaxis', meshName=inGridName) # convert to the units and variable names expected in MPAS-O rho_fw = 1000. s_per_yr = 365. * 24. * 60. * 60. latent_heat_of_fusion = 3.337e5 ds['prescribedLandIceFreshwaterFlux'] = ds[inVarName] * rho_fw / s_per_yr ds['prescribedLandIceHeatFlux'] = (latent_heat_of_fusion * ds['prescribedLandIceFreshwaterFlux']) ds = ds.drop_vars(['melt_actual', 'melt_steadystate', 'lon', 'lat']) outDescriptor = MpasMeshDescriptor(meshFileName, meshName) mappingFileName = '{}/map_{}_to_{}.nc'.format(mappingDirectory, inGridName, meshName) remapper = Remapper(inDescriptor, outDescriptor, mappingFileName) remapper.build_mapping_file(method=method, mpiTasks=mpiTasks) dsRemap = remapper.remap(ds, renormalizationThreshold=renormalizationThreshold) for field in [ 'prescribedLandIceFreshwaterFlux', 'prescribedLandIceHeatFlux' ]: # zero out the field where it's currently NaN dsRemap[field] = dsRemap[field].where(dsRemap[field].nonnull(), 0.) dsRemap.attrs['history'] = ' '.join(sys.argv) write_netcdf(dsRemap, outFileName) # }}}