fcCritPassages.merge( gf.read(componentName='ocean', objectType='transect', tags=['Critical_Passage'])) if options.custom_critical_passages is not None: fcCritPassages.merge( read_feature_collection(options.custom_critical_passages)) # create masks from the transects dsCritPassMask = conversion.mask(dsBaseMesh, fcMask=fcCritPassages) # 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 options.preserve_floodplain: dsPreserve.append(dsBaseMesh) # cull the mesh based on the land mask dsCulledMesh = conversion.cull(dsBaseMesh, dsMask=dsLandMask, dsPreserve=dsPreserve) # create a mask for the flood fill seed points dsSeedMask = conversion.mask(dsCulledMesh, fcSeed=fcSeed)
def saveesm(path, geom, mesh, preserve_floodplain=False, floodplain_elevation=20.0, do_inject_elevation=False, with_cavities=False, lat_threshold=43.00, with_critical_passages=True): """ SAVEESM: export a jigsaw mesh obj. to MPAS-style output. 1. Writes "mesh_triangles.nc" and "base_mesh.nc" files. 2. (Optionally) injects elevation + floodplain data. 3. Calls MPAS-Tools + Geometric-Data to cull mesh into ocean/land partitions. 4. Writes "culled_mesh.nc" (ocean) and "invert_mesh.nc" (land) MPAS-spec. output files. Data is written to "../path/out/" and/or "../path/tmp/". """ # Authors: Darren Engwirda ttic = time.time() print("") print("Running MPAS mesh-tools...") inject_edge_tags(mesh) # adapted from BUILD_MESH.py if (geom.mshID.lower() == "ellipsoid-mesh"): print("Forming mesh_triangles.nc") jigsaw_mesh_to_netcdf(mesh=mesh, on_sphere=True, sphere_radius=np.mean(geom.radii) * 1e3, output_name=os.path.join(path, "tmp", "mesh_triangles.nc")) if (geom.mshID.lower() == "euclidean-mesh"): print("Forming mesh_triangles.nc") jigsaw_mesh_to_netcdf(mesh=mesh, on_sphere=False, output_name=os.path.join(path, "tmp", "mesh_triangles.nc")) print("Forming base_mesh.nc") write_netcdf(convert( xarray.open_dataset(os.path.join(path, "tmp", "mesh_triangles.nc"))), fileName=os.path.join(path, "out", "base_mesh.nc")) """ if do_inject_elevation: print("Injecting cell elevations") inject_elevation( cell_elev=mesh.value, mesh_file=os.path.join( path, "out", "base_mesh.nc")) """ if preserve_floodplain: print("Injecting floodplain flag") inject_preserve_floodplain(mesh_file=os.path.join( path, "out", "base_mesh.nc"), floodplain_elevation=floodplain_elevation) args = [ "paraview_vtk_field_extractor.py", "--ignore_time", "-l", "-d", "maxEdges=0", "-v", "allOnCells", "-f", os.path.join(path, "out", "base_mesh.nc"), "-o", os.path.join(path, "out", "base_mesh_vtk") ] print("") print("running:", " ".join(args)) subprocess.check_call(args, env=os.environ.copy()) # adapted from CULL_MESH.py # required for compatibility with MPAS netcdfFormat = "NETCDF3_64BIT" gf = GeometricFeatures(cacheLocation="{}".format( os.path.join(HERE, "..", "data", "geometric_data"))) # 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="bedmap2", objectType="region", featureNames=["AntarcticGroundedIceCoverage"]) else: fcAntarcticLand = gf.read(componentName="bedmap2", objectType="region", featureNames=["AntarcticIceCoverage"]) fcLandCoverage.merge(fcAntarcticLand) # save the feature collection to a geojson file fcLandCoverage.to_geojson( os.path.join(path, "tmp", "land_coverage.geojson")) # Create the land mask based on the land coverage, # i.e. coastline data. dsBaseMesh = xarray.open_dataset(os.path.join(path, "out", "base_mesh.nc")) dsLandMask = mask(dsBaseMesh, fcMask=fcLandCoverage) dsLandMask = add_land_locked_cells_to_mask( dsLandMask, dsBaseMesh, latitude_threshold=lat_threshold, nSweeps=20) if with_critical_passages: # merge transects for critical passages into # critical_passages.geojson fcCritPassages = gf.read(componentName="ocean", objectType="transect", tags=["Critical_Passage"]) # create masks from the transects dsCritPassMask = \ mask(dsBaseMesh, fcMask=fcCritPassages) # Alter critical passages to be at least two # cells wide, to avoid sea ice blockage. dsCritPassMask = widen_transect_edge_masks( dsCritPassMask, dsBaseMesh, latitude_threshold=lat_threshold) dsLandMask = subtract_critical_passages(dsLandMask, dsCritPassMask) # merge transects for critical land blockages # into critical_land_blockages.geojson fcCritBlockages = gf.read(componentName="ocean", objectType="transect", tags=["Critical_Land_Blockage"]) # create masks from the transects for critical # land blockages dsCritBlockMask = \ mask(dsBaseMesh, fcMask=fcCritBlockages) dsLandMask = add_critical_land_blockages(dsLandMask, dsCritBlockMask) # 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"]) # update the land mask to ensure all ocean cells really # are "reachable" from the rest of the global ocean dsLandMask = mask_reachable_ocean(dsMesh=dsBaseMesh, dsMask=dsLandMask, fcSeed=fcSeed) # cull the (ocean) mesh based on the land mask, and a # cull the (land) mesh using the inverse mask if preserve_floodplain: # with "preserve_floodplains", the (ocean) mesh will # contain overlap with the (land) mesh, otherwise the # two are "perfectly" disjoint dsCulledMesh = cull(dsBaseMesh, dsMask=dsLandMask, dsPreserve=dsBaseMesh, graphInfoFileName=os.path.join( path, "out", "culled_graph.info")) dsInvertMesh = cull(dsBaseMesh, dsInverse=dsLandMask, graphInfoFileName=os.path.join( path, "out", "invert_graph.info")) else: dsCulledMesh = cull(dsBaseMesh, dsMask=dsLandMask, graphInfoFileName=os.path.join( path, "out", "culled_graph.info")) dsInvertMesh = cull(dsBaseMesh, dsInverse=dsLandMask, graphInfoFileName=os.path.join( path, "out", "invert_graph.info")) write_netcdf(dsCulledMesh, os.path.join(path, "out", "culled_mesh.nc"), netcdfFormat) write_netcdf(dsInvertMesh, os.path.join(path, "out", "invert_mesh.nc"), netcdfFormat) args = [ "paraview_vtk_field_extractor.py", "--ignore_time", "-d", "maxEdges=", "-v", "allOnCells", "-f", os.path.join(path, "out", "culled_mesh.nc"), "-o", os.path.join(path, "out", "culled_mesh_vtk") ] print("") print("running", " ".join(args)) subprocess.check_call(args, env=os.environ.copy()) args = [ "paraview_vtk_field_extractor.py", "--ignore_time", "-d", "maxEdges=", "-v", "allOnCells", "-f", os.path.join(path, "out", "invert_mesh.nc"), "-o", os.path.join(path, "out", "invert_mesh_vtk") ] print("running", " ".join(args)) subprocess.check_call(args, env=os.environ.copy()) ttoc = time.time() print("CPUSEC =", (ttoc - ttic)) return
metavar="MASKFILE", required=True) parser.add_argument("-m", "--mesh_file", dest="mesh_filename", help="MPAS Mesh filename.", metavar="MESHFILE", required=True) parser.add_argument("-o", "--out_file", dest="out_filename", help="Output mask file,different from input filename.", metavar="MASKFILE", required=True) parser.add_argument("-l", "--latitude_threshold", dest="latitude_threshold", help="Minimum latitude, degrees, for transect " "widening.", required=False, type=float, default=43.0) args = parser.parse_args() dsMask = xarray.open_dataset(args.mask_filename) dsMesh = xarray.open_dataset(args.mesh_filename) dsMask = widen_transect_edge_masks(dsMask, dsMesh, args.latitude_threshold) dsMask.to_netcdf(args.out_filename)
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)