# -*- Mode: python -*- ### =========================================================================== ### ### Compute runoff weights. ### For LMDZ only. Not suitable for DYNAMICO ### ### =========================================================================== ## ## Warning, to install, configure, run, use any of Olivier Marti's ## software or to read the associated documentation you'll need at least ## one (1) brain in a reasonably working order. Lack of this implement ## will void any warranties (either express or implied). ## O. Marti assumes no responsability for errors, omissions, ## data loss, or any other consequences caused directly or indirectly by ## the usage of his software by incorrectly or partially configured ## personal. ## # SVN information __Author__ = "$Author$" __Date__ = "$Date$" __Revision__ = "$Revision$" __Id__ = "$Id$" __HeadURL__ = "$HeadURL$" __SVN_Date__ = "$SVN_Date: $" ## ## Modules import netCDF4 import numpy as np import nemo from scipy import ndimage import sys, os, platform, argparse, textwrap, time ## Userful constants zero = np.dtype('float64').type(0.0) zone = np.dtype('float64').type(1.0) epsfrac = np.dtype('float64').type(1.0E-10) pi = np.pi rad = pi/np.dtype('float64').type(180.0) ra = np.dtype('float64').type(6371229.0) # Earth radius ## Functions def geodist (plon1, plat1, plon2, plat2) : """Distance between two points (on sphere)""" zs = np.sin (rad*plat1) * np.sin (rad*plat2) + np.cos (rad*plat1) * np.cos (rad*plat2) * np.cos(rad*(plon2-plon1)) zs = np.maximum (-zone, np.minimum (zone, zs)) geodist = np.arccos (zs) return geodist ### ===== Reading command line parameters ================================================== # Creating a parser parser = argparse.ArgumentParser ( description = """Compute calving weights""", epilog='-------- End of the help message --------') # Adding arguments parser.add_argument ('--oce' , help='oce model name', type=str, default='eORCA1.2', choices=['ORCA2.3', 'eORCA1.2', 'eORCA025'] ) parser.add_argument ('--atm' , help='atm model name (LMD*)', type=str, default='LMD9695' ) parser.add_argument ('--atmCoastWidth', help='width of the coastal band in atmosphere (in grid points)', type=int, default=1 ) parser.add_argument ('--oceCoastWidth', help='width of the coastal band in ocean (in grid points)' , type=int, default=2 ) parser.add_argument ('--searchRadius' , help='max distance to connect a land point to an ocean point (in km)', type=float, default=600000.0) parser.add_argument ('--grids' , help='grids file name', default='grids.nc' ) parser.add_argument ('--areas' , help='masks file name', default='areas.nc' ) parser.add_argument ('--masks' , help='areas file name', default='masks.nc' ) parser.add_argument ('--o2a' , help='o2a file name' , default='o2a.nc' ) parser.add_argument ('--output', help='output rmp file name', default='rmp_tlmd_to_torc_runoff_64bit.nc' ) parser.add_argument ('--fmt' , help='NetCDF file format, using nco syntax', default='64bit', choices=['classic', 'netcdf3', '64bit', '64bit_data', '64bit_data', 'netcdf4', 'netcdf4_classsic'] ) # Parse command line myargs = parser.parse_args() # grids = myargs.grids areas = myargs.areas masks = myargs.masks o2a = myargs.o2a # Model Names atm_Name = myargs.atm oce_Name = myargs.oce # Width of the coastal band (land points) in the atmopshere atmCoastWidth = myargs.atmCoastWidth # Width of the coastal band (ocean points) in the ocean oceCoastWidth = myargs.oceCoastWidth searchRadius = myargs.searchRadius * 1000.0 # From km to meters # Netcdf format if myargs.fmt == 'classic' : FmtNetcdf = 'CLASSIC' if myargs.fmt == 'netcdf3' : FmtNetcdf = 'CLASSIC' if myargs.fmt == '64bit' : FmtNetcdf = 'NETCDF3_64BIT_OFFSET' if myargs.fmt == '64bit_data' : FmtNetcdf = 'NETCDF3_64BIT_DATA' if myargs.fmt == '64bit_offset' : FmtNetcdf = 'NETCDF3_64BIT_OFFSET' if myargs.fmt == 'netcdf4' : FmtNetcdf = 'NETCDF4' if myargs.fmt == 'netcdf4_classic' : FmtNetcdf = 'NETCDF4_CLASSIC' ### Read coordinates of all models ### diaFile = netCDF4.Dataset ( o2a ) gridFile = netCDF4.Dataset ( grids ) areaFile = netCDF4.Dataset ( areas ) maskFile = netCDF4.Dataset ( masks ) o2aFrac = diaFile ['OceFrac'][:].squeeze() o2aFrac = np.where ( np.abs(o2aFrac) < 1E10, o2aFrac, 0.0) atm_grid_center_lat = gridFile['tlmd.lat'][:] atm_grid_center_lon = gridFile['tlmd.lon'][:] atm_grid_corner_lat = gridFile['tlmd.cla'][:] atm_grid_corner_lon = gridFile['tlmd.clo'][:] atm_grid_area = areaFile['tlmd.srf'][:] atm_grid_imask = 1-maskFile['tlmd.msk'][:].squeeze() atm_grid_dims = atm_grid_area.shape (atm_nvertex, atm_jpj, atm_jpi) = atm_grid_corner_lat.shape atm_perio = 0 atm_grid_pmask = nemo.lbc_mask (atm_grid_imask, 'T', atm_perio) atm_address = np.reshape ( np.arange(atm_jpj*atm_jpi), (atm_jpj, atm_jpi) ) atm_grid_size = atm_jpj*atm_jpi oce_grid_center_lat = gridFile['torc.lat'][:] oce_grid_center_lon = gridFile['torc.lon'][:] oce_grid_corner_lat = gridFile['torc.cla'][:] oce_grid_corner_lon = gridFile['torc.clo'][:] oce_grid_area = areaFile['torc.srf'][:] oce_grid_imask = 1-maskFile['torc.msk'][:] oce_grid_dims = oce_grid_area.shape (oce_nvertex, oce_jpj, oce_jpi) = oce_grid_corner_lat.shape ; jpon=oce_jpj*oce_jpj if oce_jpi == 182 : oce_perio = 4 # ORCA 2 if oce_jpi == 362 : oce_perio = 6 # ORCA 1 if oce_jpi == 1442 : oce_perio = 6 # ORCA 025 oce_grid_pmask = nemo.lbc_mask (oce_grid_imask, 'T', oce_perio) oce_address = np.reshape ( np.arange(oce_jpj*oce_jpi), (oce_jpj, oce_jpi) ) oce_grid_size = oce_jpj*oce_jpi ## Fill closed sea with image processing library oce_grid_imask = nemo.lbc_mask ( 1-ndimage.binary_fill_holes (1-nemo.lbc(oce_grid_imask, nperio=oce_perio, cd_type='T')), nperio=oce_perio, cd_type='T' ) ## print ("Determination d'une bande cotiere ocean") oceLand = np.where (oce_grid_pmask[:] < 0.5, True, False) oceOcean = np.where (oce_grid_pmask[:] > 0.5, True, False) NNocean = 1+2*oceCoastWidth oceOceanFiltered = ndimage.uniform_filter(oceOcean.astype(float), size=NNocean) oceCoast = np.where (oceOceanFiltered<(1.0-0.5/(NNocean**2)),True,False) & oceOcean oceCoast = nemo.lbc_mask (oceCoast, oce_perio, 'T') print ('Number of points in oceLand : ' + str(oceLand.sum()) ) print ('Number of points in oceOcean : ' + str(oceOcean.sum()) ) print ('Number of points in oceCoast : ' + str(oceCoast.sum()) ) # Arrays with coastal points only oceCoast_grid_center_lon = oce_grid_center_lon[oceCoast] oceCoast_grid_center_lat = oce_grid_center_lat[oceCoast] oceCoast_grid_area = oce_grid_area [oceCoast] oceCoast_grid_imask = oce_grid_imask [oceCoast] oceCoast_grid_pmask = oce_grid_pmask [oceCoast] oceCoast_address = oce_address [oceCoast] print ("Determination d'une bande cotiere atmosphere " ) atmLand = np.where (o2aFrac[:] < epsfrac , True, False) atmLandFrac = np.where (o2aFrac[:] < zone-epsfrac , True, False) atmFrac = np.where (o2aFrac[:] > epsfrac , True, False) & np.where (o2aFrac[:] < (zone-epsfrac), True, False) atmOcean = np.where (o2aFrac[:] < (zone-epsfrac), True, False) atmOceanFrac = np.where (o2aFrac[:] > epsfrac , True, False) NNatm = 1+2*atmCoastWidth atmLandFiltered = ndimage.uniform_filter(atmLand.astype(float), size=NNatm) atmCoast = np.where (atmLandFiltered<(1.0-0.5/(NNatm**2)),True,False) & atmLandFrac atmCoast = nemo.lbc_mask (atmCoast, 1, 'T') print ('Number of points in atmLand : ' + str(atmLand.sum()) ) print ('Number of points in atmOcean : ' + str(atmOcean.sum()) ) print ('Number of points in atmCoast : ' + str(atmCoast.sum()) ) # Arrays with coastal points only atmCoast_grid_center_lon = atm_grid_center_lon[atmCoast] atmCoast_grid_center_lat = atm_grid_center_lat[atmCoast] atmCoast_grid_area = atm_grid_area [atmCoast] atmCoast_grid_imask = atm_grid_imask [atmCoast] atmCoast_grid_pmask = atm_grid_pmask [atmCoast] atmCoast_address = atm_address [atmCoast] remap_matrix = np.empty ( shape=(0), dtype=np.float64 ) atm_address = np.empty ( shape=(0), dtype=np.int32 ) oce_address = np.empty ( shape=(0), dtype=np.int32 ) ## Loop on atmosphere coastal points for ja in np.arange(len(atmCoast_grid_pmask)) : z_dist = geodist ( atmCoast_grid_center_lon[ja], atmCoast_grid_center_lat[ja], oceCoast_grid_center_lon, oceCoast_grid_center_lat) z_mask = np.where ( z_dist*ra < searchRadius, True, False) num_links = z_mask.sum() if num_links == 0 : continue z_area = oceCoast_grid_area[z_mask].sum() poids = 1.0 / z_area #print ( num_links, z_mask.sum(), z_area ) # matrix_local = np.ones ((num_links),dtype=np.float64) * poids # address on source grid : all links points to the same LMDZ point. atm_address_local = np.ones(num_links, dtype=np.int32 ) * atmCoast_address[ja] # address on destination grid oce_address_local = oceCoast_address[z_mask] # Append to global tabs remap_matrix = np.append ( remap_matrix, matrix_local ) atm_address = np.append ( atm_address , atm_address_local ) oce_address = np.append ( oce_address , oce_address_local ) print ('End of loop') num_links = remap_matrix.shape[0] ### Output file runoff = myargs.output f_runoff = netCDF4.Dataset ( runoff, 'w', format=FmtNetcdf ) print ('Output file: ' + runoff ) f_runoff.Conventions = "CF-1.6" f_runoff.source = "IPSL Earth system model" f_runoff.group = "ICMC IPSL Climate Modelling Center" f_runoff.Institution = "IPSL https.//www.ipsl.fr" f_runoff.Ocean = oce_Name + " https://www.nemo-ocean.eu" f_runoff.Atmosphere = atm_Name + " http://lmdz.lmd.jussieu.fr" f_runoff.associatedFiles = grids + " " + areas + " " + masks f_runoff.directory = os.getcwd () f_runoff.description = "Generated with cotes_etal.py" f_runoff.title = runoff f_runoff.Program = "Generated by " + sys.argv[0] + " with flags " + str(sys.argv[1:]) f_runoff.atmCoastWidth = str(atmCoastWidth) + " grid points" f_runoff.oceCoastWidth = str(oceCoastWidth) + " grid points" f_runoff.searchRadius = str(searchRadius/1000.) + " km" f_runoff.gridsFile = grids f_runoff.areasFile = areas f_runoff.masksFile = masks f_runoff.o2aFile = o2a f_runoff.timeStamp = time.asctime() f_runoff.uuid = areaFile.uuid f_runoff.HOSTNAME = platform.node() #f_runoff.LOGNAME = os.getlogin() f_runoff.Python = "Python version " + platform.python_version() f_runoff.OS = platform.system() f_runoff.release = platform.release() f_runoff.hardware = platform.machine() f_runoff.conventions = "SCRIP" f_runoff.source_grid = "curvilinear" f_runoff.dest_grid = "curvilinear" f_runoff.Model = "IPSL CM6" f_runoff.SVN_Author = "$Author$" f_runoff.SVN_Date = "$Date$" f_runoff.SVN_Revision = "$Revision$" f_runoff.SVN_Id = "$Id$" f_runoff.SVN_HeadURL = "$HeadURL$" d_num_links = f_runoff.createDimension ('num_links' , num_links ) d_num_wgts = f_runoff.createDimension ('num_wgts' , 1 ) d_atm_grid_size = f_runoff.createDimension ('src_grid_size' , atm_grid_size ) d_atm_grid_corners = f_runoff.createDimension ('src_grid_corners', atm_grid_corner_lon.shape[0] ) d_atm_grid_rank = f_runoff.createDimension ('src_grid_rank' , 2 ) d_oce_grid_size = f_runoff.createDimension ('dst_grid_size' , oce_grid_size ) d_oce_grid_corners = f_runoff.createDimension ('dst_grid_corners', oce_grid_corner_lon.shape[0] ) d_oce_grid_rank = f_runoff.createDimension ('dst_grid_rank' , 2 ) v_remap_matrix = f_runoff.createVariable ( 'remap_matrix', 'f8', ('num_links', 'num_wgts') ) v_atm_address = f_runoff.createVariable ( 'src_address' , 'i4', ('num_links',) ) v_oce_address = f_runoff.createVariable ( 'dst_address' , 'i4', ('num_links',) ) v_remap_matrix[:] = remap_matrix v_atm_address [:] = atm_address + 1 # OASIS uses Fortran style indexing v_oce_address [:] = oce_address + 1 v_atm_grid_dims = f_runoff.createVariable ( 'src_grid_dims' , 'i4', ('src_grid_rank',) ) v_atm_grid_center_lon = f_runoff.createVariable ( 'src_grid_center_lon', 'i4', ('src_grid_size',) ) v_atm_grid_center_lat = f_runoff.createVariable ( 'src_grid_center_lat', 'i4', ('src_grid_size',) ) v_atm_grid_center_lon.units='degrees_east' ; v_atm_grid_center_lon.long_name='Longitude' ; v_atm_grid_center_lon.long_name='longitude' ; v_atm_grid_center_lon.bounds="src_grid_corner_lon" v_atm_grid_center_lat.units='degrees_north' ; v_atm_grid_center_lat.long_name='Latitude' ; v_atm_grid_center_lat.long_name='latitude ' ; v_atm_grid_center_lat.bounds="src_grid_corner_lat" v_atm_grid_corner_lon = f_runoff.createVariable ( 'src_grid_corner_lon', 'f8', ('src_grid_size', 'src_grid_corners') ) v_atm_grid_corner_lat = f_runoff.createVariable ( 'src_grid_corner_lat', 'f8', ('src_grid_size', 'src_grid_corners') ) v_atm_grid_corner_lon.units="degrees_east" v_atm_grid_corner_lat.units="degrees_north" v_atm_grid_area = f_runoff.createVariable ( 'src_grid_area' , 'f8', ('src_grid_size',) ) v_atm_grid_area.long_name="Grid area" ; v_atm_grid_area.standard_name="cell_area" ; v_atm_grid_area.units="m2" v_atm_grid_imask = f_runoff.createVariable ( 'src_grid_imask' , 'i4', ('src_grid_size',) ) v_atm_grid_imask.long_name="Land-sea mask" ; v_atm_grid_imask.units="Land:1, Ocean:0" v_atm_grid_pmask = f_runoff.createVariable ( 'src_grid_pmask' , 'i4', ('src_grid_size',) ) v_atm_grid_pmask.long_name="Land-sea mask (periodicity removed)" ; v_atm_grid_pmask.units="Land:1, Ocean:0" v_atm_grid_dims [:] = atm_grid_dims v_atm_grid_center_lon[:] = atm_grid_center_lon[:].ravel() v_atm_grid_center_lat[:] = atm_grid_center_lat[:].ravel() v_atm_grid_corner_lon[:] = atm_grid_corner_lon.reshape( (atm_jpi*atm_jpj, d_atm_grid_corners.__len__()) ) v_atm_grid_corner_lat[:] = atm_grid_corner_lat.reshape( (atm_jpi*atm_jpj, d_atm_grid_corners.__len__()) ) v_atm_grid_area [:] = atm_grid_area[:].ravel() v_atm_grid_imask [:] = atm_grid_imask[:].ravel() v_atm_grid_pmask [:] = atm_grid_pmask[:].ravel() # -- v_oce_grid_dims = f_runoff.createVariable ( 'dst_grid_dims' , 'i4', ('dst_grid_rank',) ) v_oce_grid_center_lon = f_runoff.createVariable ( 'dst_grid_center_lon', 'i4', ('dst_grid_size',) ) v_oce_grid_center_lat = f_runoff.createVariable ( 'dst_grid_center_lat', 'i4', ('dst_grid_size',) ) v_oce_grid_center_lon.units='degrees_east' ; v_oce_grid_center_lon.long_name='Longitude' ; v_oce_grid_center_lon.long_name='longitude' ; v_oce_grid_center_lon.bounds="dst_grid_corner_lon" v_oce_grid_center_lat.units='degrees_north' ; v_oce_grid_center_lat.long_name='Latitude' ; v_oce_grid_center_lat.long_name='latitude' ; v_oce_grid_center_lat.bounds="dst_grid_corner_lat" v_oce_grid_corner_lon = f_runoff.createVariable ( 'dst_grid_corner_lon', 'f8', ('dst_grid_size', 'dst_grid_corners') ) v_oce_grid_corner_lat = f_runoff.createVariable ( 'dst_grid_corner_lat', 'f8', ('dst_grid_size', 'dst_grid_corners') ) v_oce_grid_corner_lon.units="degrees_east" v_oce_grid_corner_lat.units="degrees_north" v_oce_grid_area = f_runoff.createVariable ( 'dst_grid_area' , 'f8', ('dst_grid_size',) ) v_oce_grid_area.long_name="Grid area" ; v_oce_grid_area.standard_name="cell_area" ; v_oce_grid_area.units="m2" v_oce_grid_imask = f_runoff.createVariable ( 'dst_grid_imask' , 'i4', ('dst_grid_size',) ) v_oce_grid_imask.long_name="Land-sea mask" ; v_oce_grid_imask.units="Land:1, Ocean:0" v_oce_grid_pmask = f_runoff.createVariable ( 'dst_grid_pmask' , 'i4', ('dst_grid_size',) ) v_oce_grid_pmask.long_name="Land-sea mask (periodicity removed)" ; v_oce_grid_pmask.units="Land:1, Ocean:0" v_oce_grid_dims [:] = oce_grid_dims v_oce_grid_center_lon[:] = oce_grid_center_lon[:].ravel() v_oce_grid_center_lat[:] = oce_grid_center_lat[:].ravel() v_oce_grid_corner_lon[:] = oce_grid_corner_lon.reshape( (oce_jpi*oce_jpj, d_oce_grid_corners.__len__()) ) v_oce_grid_corner_lat[:] = oce_grid_corner_lon.reshape( (oce_jpi*oce_jpj, d_oce_grid_corners.__len__()) ) v_oce_grid_area [:] = oce_grid_area[:].ravel() v_oce_grid_imask [:] = oce_grid_imask[:].ravel() v_oce_grid_pmask [:] = oce_grid_pmask[:].ravel() v_atm_lon_addressed = f_runoff.createVariable ( 'src_lon_addressed' , 'f8', ('num_links',) ) v_atm_lat_addressed = f_runoff.createVariable ( 'src_lat_addressed' , 'f8', ('num_links',) ) v_atm_area_addressed = f_runoff.createVariable ( 'src_area_addressed' , 'f8', ('num_links',) ) v_atm_imask_addressed = f_runoff.createVariable ( 'src_imask_addressed', 'i4', ('num_links',) ) v_atm_pmask_addressed = f_runoff.createVariable ( 'src_pmask_addressed', 'i4', ('num_links',) ) v_oce_lon_addressed = f_runoff.createVariable ( 'dst_lon_addressed' , 'f8', ('num_links',) ) v_oce_lat_addressed = f_runoff.createVariable ( 'dst_lat_addressed' , 'f8', ('num_links',) ) v_oce_area_addressed = f_runoff.createVariable ( 'dst_area_addressed' , 'f8', ('num_links',) ) v_oce_imask_addressed = f_runoff.createVariable ( 'dst_imask_addressed', 'i4', ('num_links',) ) v_oce_pmask_addressed = f_runoff.createVariable ( 'dst_pmask_addressed', 'i4', ('num_links',) ) v_atm_lon_addressed.long_name="Longitude" ; v_atm_lon_addressed.standard_name="longitude" ; v_atm_lon_addressed.units="degrees_east" v_atm_lat_addressed.long_name="Latitude" ; v_atm_lat_addressed.standard_name="latitude" ; v_atm_lat_addressed.units="degrees_north" v_atm_lon_addressed [:] = atm_grid_center_lon.ravel()[atm_address].ravel() v_atm_lat_addressed [:] = atm_grid_center_lat.ravel()[atm_address].ravel() v_atm_area_addressed [:] = atm_grid_area.ravel()[atm_address].ravel() v_atm_imask_addressed[:] = 1-atm_grid_imask.ravel()[atm_address].ravel() v_atm_pmask_addressed[:] = 1-atm_grid_pmask.ravel()[atm_address].ravel() v_oce_lon_addressed.long_name="Longitude" ; v_oce_lon_addressed.standard_name="longitude" ; v_oce_lon_addressed.units="degrees_east" v_oce_lat_addressed.long_name="Latitude" ; v_oce_lat_addressed.standard_name="latitude" ; v_oce_lat_addressed.units="degrees_north" v_oce_lon_addressed [:] = oce_grid_center_lon.ravel()[oce_address].ravel() v_oce_lat_addressed [:] = oce_grid_center_lat.ravel()[oce_address].ravel() v_oce_area_addressed [:] = oce_grid_area.ravel()[oce_address].ravel() v_oce_imask_addressed[:] = 1-oce_grid_imask.ravel()[oce_address].ravel() v_oce_pmask_addressed[:] = 1-oce_grid_pmask.ravel()[oce_address].ravel() v_atmLand = f_runoff.createVariable ( 'atmLand' , 'i4', ('src_grid_size',) ) v_atmLandFiltered = f_runoff.createVariable ( 'atmLandFiltered', 'f4', ('src_grid_size',) ) v_atmLandFrac = f_runoff.createVariable ( 'atmLandFrac' , 'i4', ('src_grid_size',) ) v_atmFrac = f_runoff.createVariable ( 'atmFrac' , 'i4', ('src_grid_size',) ) v_atmOcean = f_runoff.createVariable ( 'atmOcean' , 'i4', ('src_grid_size',) ) v_atmOceanFrac = f_runoff.createVariable ( 'atmOceanFrac' , 'i4', ('src_grid_size',) ) v_atmCoast = f_runoff.createVariable ( 'atmCoast' , 'i4', ('src_grid_size',) ) v_atmLand[:] = atmLand.ravel() v_atmLandFrac[:] = atmLandFrac.ravel() v_atmLandFiltered[:] = atmLandFiltered.ravel() v_atmFrac[:] = atmFrac.ravel() v_atmOcean[:] = atmOcean.ravel() v_atmOceanFrac[:] = atmOceanFrac.ravel() v_atmCoast[:] = atmCoast.ravel() v_oceLand = f_runoff.createVariable ( 'oceLand' , 'i4', ('dst_grid_size',) ) v_oceOcean = f_runoff.createVariable ( 'oceOcean' , 'i4', ('dst_grid_size',) ) v_oceOceanFiltered = f_runoff.createVariable ( 'oceOceanFiltered', 'f4', ('dst_grid_size',) ) v_oceCoast = f_runoff.createVariable ( 'oceCoast' , 'i4', ('dst_grid_size',) ) v_oceLand[:] = oceLand.ravel() v_oceOcean[:] = oceOcean.ravel() v_oceOceanFiltered[:] = oceOceanFiltered.ravel() v_oceCoast[:] = oceCoast.ravel() f_runoff.sync () ## f_runoff.close() print ('The end') ## ======================================================================================