;+ ; ; @file_comments ; read NetCDF grid file created by ROMS ; ; @categories ; Grid ; ; @examples ; ; IDL> ncdf_meshroms [,' filename'] ; ; @param filename {in}{optional}{default='roms_grd.nc'}{type=scalar string} ; Name of the meshmask file to read. If this name does not contain any "/" ; and if iodirectory keyword is not specify, then the common variable ; iodir will be used to define the mesh file path. ; ; @keyword GLAMBOUNDARY {default=those defined in the file}{type=2 elements vector} ; Longitude boundaries that should be used to visualize the data. ; lon2 > lon1 ; lon2 - lon1 le 360 ; By default, the common (cm_4mesh) variable key_shift will be automatically ; defined according to GLAMBOUNDARY. ; ; @keyword ONEARTH {default=1}{type=scalar: 0 or 1} ; Force the manual definition of data localization on the earth or not ; 0) if the data are not on the earth ; 1) if the data are on earth (in that case we can for example use ; the labels 'longitude', 'latitude' in plots). ; The resulting value will be stored in the common (cm_4mesh) variable key_onearth ; ONEARTH=0 forces PERIODIC=0, SHIFT=0 and is cancelling GLAMBOUNDARY ; ; @keyword GETDIMENSIONS {default=0}{type=scalar: 0 or 1} ; Activate this keywords if you only want to know the dimension ; of the domain stored in the mesh file. This dimension will be ; defined in jpiglo, jpjglo, jpkglo (cm_4mesh common variables) ; ; @keyword PERIODIC {default=computed by using the first line of glamt}{type=scalar: 0 or 1} ; Force the manual definition of the grid zonal periodicity. ; The resulting value will be stored in the common (cm_4mesh) variable key_periodic ; PERIODIC=0 forces SHIFT=0 ; ; @keyword NRHO {default=1}{type=scalar} ; Specify the number of rho level that contain the data we want to explore. ; This is mainly useful when using xxx to get access to the deeper levers and vertical sections. ; ; @keyword SHIFT {default=computed according to glamboundary}{type=scalar} ; Force the manual definition of the zonal shift that must be apply to the data. ; The resulting value will be stored in the common (cm_4mesh) variable key_shift ; Note that if key_periodic=0 then in any case key_shift=0. ; ; @keyword STRCALLING {type=scalar string} ; the calling command used to call computegrid (this is used by xxx) ; ; @keyword STRIDE {default=[1, 1, 1]}{type=3 elements vector} ; Specify the stride in x, y and z direction. The resulting ; value will be stored in the common (cm_4mesh) variable key_stride ; ; @keyword _EXTRA ; Used to pass keywords to isafile ; ; @uses ; cm_4mesh ; cm_4data ; cm_4cal ; ; @restrictions ; ixminmesh, ixmaxmesh, iyminmesh, iymaxmesh, izminmesh, izmaxmesh must ; be defined before calling ncdf_meshread. If some of those values ; are equal to -1 they will be automatically defined ; ; In the original ROMS grid, if F grid has (jpi,jpj) points then T ; grid will have (jpi+1,jpj+1) points, U grid will have (jpi,jpj+1) ; points and V grid will have (jpi+1,jpj) points. ; By default C-grid used in this package needs the same number of ; points for T,U,V and F grid, with a T point at the bottom left ; corner of the grid. We therefore ignore the last column of T and ; V points and the last line of T and U points. ; ; Scale factors are computed using the distance between the points ; (which is not the exact definition for irregular grid). ; ; @history ; Sebastien Masson (smasson\@lodyc.jussieu.fr) September 2006 ; ; @version ; $Id$ ; ;- PRO ncdf_meshroms, filename, NRHO=nrho, GLAMBOUNDARY=glamboundary $ , ONEARTH=onearth, GETDIMENSIONS=getdimensions $ , PERIODIC=periodic, SHIFT=shift, STRIDE=stride $ , STRCALLING=strcalling, _EXTRA=ex ; compile_opt idl2, strictarrsubs ; @cm_4mesh @cm_4data @cm_4cal IF NOT keyword_set(key_forgetold) THEN BEGIN @updatenew @updatekwd ENDIF ;--------------------------------------------------------- ; tempsun = systime(1) ; for key_performance ;------------------------------------------------------- ; find meshfile name and open it! ;------------------------------------------------------- ; def of filename by default IF n_params() EQ 0 then filename = 'roms_grd.nc' meshname = isafile(file = filename, iodirectory = iodir, _EXTRA = ex) meshname = meshname[0] ; noticebase = xnotice('Reading file !C '+meshname+'!C ...') ; if the meshmask is on tape archive ... get it back IF !version.OS_FAMILY EQ 'unix' THEN spawn, '\file '+meshname+' > /dev/null' cdfid = ncdf_open(meshname) inq = ncdf_inquire(cdfid) ;------------------------------------------------------------ ; dimensions ;------------------------------------------------------------ ncdf_diminq, cdfid, 'xi_rho', name, jpiglo ncdf_diminq, cdfid, 'eta_rho', name, jpjglo IF n_elements(nrho) NE 0 THEN jpkglo = long(nrho[0]) $ ELSE jpkglo = 1L ; if keyword_set(getdimensions) then begin widget_control, noticebase, bad_id = nothing, /destroy ncdf_close, cdfid return endif ;------------------------------------------------------- ; check that all i[xyz]min[ax]mesh are well defined ;------------------------------------------------------- if n_elements(ixminmesh) EQ 0 THEN ixminmesh = 0 if n_elements(ixmaxmesh) EQ 0 then ixmaxmesh = jpiglo-1 if ixminmesh EQ -1 THEN ixminmesh = 0 IF ixmaxmesh EQ -1 then ixmaxmesh = jpiglo-1 if n_elements(iyminmesh) EQ 0 THEN iyminmesh = 0 IF n_elements(iymaxmesh) EQ 0 then iymaxmesh = jpjglo-1 if iyminmesh EQ -1 THEN iyminmesh = 0 IF iymaxmesh EQ -1 then iymaxmesh = jpjglo-1 if n_elements(izminmesh) EQ 0 THEN izminmesh = 0 IF n_elements(izmaxmesh) EQ 0 then izmaxmesh = jpkglo-1 if izminmesh EQ -1 THEN izminmesh = 0 IF izmaxmesh EQ -1 then izmaxmesh = jpkglo-1 ; definition of jpi,jpj,jpj jpi = long(ixmaxmesh-ixminmesh+1) jpj = long(iymaxmesh-iyminmesh+1) jpk = long(izmaxmesh-izminmesh+1) ;------------------------------------------------------- ; check onearth and its consequences ;------------------------------------------------------- IF n_elements(onearth) EQ 0 THEN key_onearth = 1 $ ELSE key_onearth = keyword_set(onearth) IF NOT key_onearth THEN BEGIN periodic = 0 shift = 0 ENDIF ;------------------------------------------------------- ; automatic definition of key_periodic ;------------------------------------------------------- IF n_elements(periodic) EQ 0 THEN BEGIN IF jpi GT 1 THEN BEGIN ncdf_varget, cdfid, 'lon_rho', xaxis $ , offset = [ixminmesh, iyminmesh], count = [jpi, 1] xaxis = (xaxis+720) MOD 360 xaxis = xaxis[sort(xaxis)] key_periodic = (xaxis[jpi-1]+2*(xaxis[jpi-1]-xaxis[jpi-2])) $ GE (xaxis[0]+360) ENDIF ELSE key_periodic = 0 ENDIF ELSE key_periodic = keyword_set(periodic) ;------------------------------------------------------- ; automatic definition of key_shift ;------------------------------------------------------- IF n_elements(shift) EQ 0 THEN BEGIN key_shift = long(testvar(var = key_shift)) ; key_shift will be defined according to the first line of glamt. if keyword_set(glamboundary) AND jpi GT 1 AND key_periodic EQ 1 $ THEN BEGIN ncdf_varget, cdfid, 'lon_rho', xaxis $ , offset = [ixminmesh, iyminmesh], count = [jpi, 1] ; xaxis between glamboundary[0] and glamboundary[1] xaxis = xaxis MOD 360 smaller = where(xaxis LT glamboundary[0]) if smaller[0] NE -1 then xaxis[smaller] = xaxis[smaller]+360 bigger = where(xaxis GE glamboundary[1]) if bigger[0] NE -1 then xaxis[bigger] = xaxis[bigger]-360 ; key_shift = (where(xaxis EQ min(xaxis)))[0] IF key_shift NE 0 THEN BEGIN key_shift = jpi-key_shift xaxis = shift(xaxis, key_shift) ENDIF ; IF array_equal(sort(xaxis), lindgen(jpi)) NE 1 THEN BEGIN ras = report (['the x axis (1st line of glamt) is not sorted in the increasing order after the automatic definition of key_shift', $ 'please use the keyword shift (and periodic) to suppress the automatic definition of key_shift (and key_periodic) and define by hand a more suitable value...']) widget_control, noticebase, bad_id = nothing, /destroy return ENDIF ; ENDIF ELSE key_shift = 0 ENDIF ELSE key_shift = long(shift)*(key_periodic EQ 1) ;------------------------------------------------------- ; check key_stride and related things ;------------------------------------------------------- if n_elements(stride) eq 3 then key_stride = stride if n_elements(key_stride) LE 2 then key_stride = [1, 1, 1] key_stride = 1l > long(key_stride) IF total(key_stride) NE 3 THEN BEGIN IF key_shift NE 0 THEN BEGIN ; for explanation, see header of read_ncdf_varget.pro jpiright = key_shift jpileft = jpi - key_shift - ( (key_stride[0]-1)-((key_shift-1) MOD key_stride[0]) ) jpi = ((jpiright-1)/key_stride[0]+1) + ((jpileft-1)/key_stride[0]+1) ENDIF ELSE jpi = (jpi-1)/key_stride[0]+1 jpj = (jpj-1)/key_stride[1]+1 jpk = (jpk-1)/key_stride[2]+1 ENDIF ;------------------------------------------------------- ; for the variables related to the partial steps ;------------------------------------------------------- key_partialstep = 0 hdept = -1 hdepw = -1 ;------------------------------------------------------- ; default definitions to be able to use read_ncdf_varget ;------------------------------------------------------- ; default definitions to be able to use read_ncdf_varget ixmindtasauve = testvar(var = ixmindta) iymindtasauve = testvar(var = iymindta) izmindtasauve = testvar(var = izmindta) ; ixmindta = 0l iymindta = 0l izmindta = 0l ; jpt = 1 time = 1 firsttps = 0 ; firstx = 0 lastx = jpi-1 firsty = 0 lasty = jpj-1 firstz = 0 lastz = jpk-1 nx = jpi ny = jpj nz = 1 izminmeshsauve = izminmesh izminmesh = 0 ; key_yreverse = 0 key_zreverse = 1 key_gridtype = 'c' ;------------------------------------------------------- ; 2d arrays: ;------------------------------------------------------- ; list the 2d variables that must be read namebase = ['lon_', 'lat_', 'mask_', 'x_', 'y_'] namebase2 = ['glam', 'gphi', 'mask' , 'd1', 'd2'] ; ; read all grid T variables ; for i = 0, n_elements(namebase)-1 do begin varinq = ncdf_varinq(cdfid, namebase[i]+'rho') name = varinq.name @read_ncdf_varget command = namebase2[i]+'t = float(temporary(res))' nothing = execute(command) ENDFOR d1t = 1.e3*(shift(d1t, -1, 0) - d1t) d2t = 1.e3*(shift(d2t, 0, -1) - d2t) for i = 0, n_elements(namebase2)-1 do begin command = namebase2[i]+'t = '+namebase2[i]+'t[0:jpi-2, 0:jpj-2]' nothing = execute(command) ENDFOR tmask = byte(temporary(maskt)) IF jpk GT 1 THEN tmask = reform(tmask[*]#replicate(1b, jpk), jpi-1, jpj-1, jpk, /overwrite) e1u = temporary(d1t) e2v = temporary(d2t) ; h: Final bathymetry at RHO-points varinq = ncdf_varinq(cdfid, 'h') name = varinq.name @read_ncdf_varget hroms = float(temporary(res)) hroms = hroms[0:jpi-2, 0:jpj-2] ; ; read all grid U variables ; jpiglo = jpiglo - 1 jpi = jpi - 1 ixmaxmesh = ixmaxmesh - 1 firstx = 0 lastx = jpi-1 nx = jpi ; for i = 0, n_elements(namebase)-1 do begin varinq = ncdf_varinq(cdfid, namebase[i]+'u') name = varinq.name @read_ncdf_varget command = namebase2[i]+'u = float(temporary(res))' nothing = execute(command) ENDFOR tmpsave = 2. * 1.e3 * d1u[0, 0:jpj-2] d1u = 1.e3*(shift(d1u, -1, 0) - d1u) d2u = 1.e3*(shift(d2u, 0, -1) - d2u) for i = 0, n_elements(namebase2)-1 do begin command = namebase2[i]+'u = '+namebase2[i]+'u[*, 0:jpj-2]' nothing = execute(command) ENDFOR umaskred = byte((temporary(masku))[jpi-1, *]) IF jpk GT 1 THEN umaskred = reform(umaskred[*]#replicate(1b, jpk), 1, jpj-1, jpk, /overwrite) e1t = temporary(d1u) e1t = shift(temporary(e1t), 1, 0) e1t[0, *] = temporary(tmpsave) e2f = temporary(d2u) ; ; read all grid V variables ; jpiglo = jpiglo + 1 jpi = jpi + 1 ixmaxmesh = ixmaxmesh + 1 firstx = 0 lastx = jpi-1 nx = jpi jpjglo = jpjglo - 1 jpj = jpj - 1 iymaxmesh = iymaxmesh - 1 firsty = 0 lasty = jpj-1 ny = jpj ; for i = 0, n_elements(namebase)-1 do begin varinq = ncdf_varinq(cdfid, namebase[i]+'v') name = varinq.name @read_ncdf_varget command = namebase2[i]+'v = float(temporary(res))' nothing = execute(command) ENDFOR d1v = 1.e3*(shift(d1v, -1, 0) - d1v) tmpsave = 2. * 1.e3 * d2v[0:jpi-2, 0] d2v = 1.e3*(shift(d2v, 0, -1) - d2v) for i = 0, n_elements(namebase2)-1 do begin command = namebase2[i]+'v = '+namebase2[i]+'v[0:jpi-2, *]' nothing = execute(command) ENDFOR vmaskred = byte((temporary(maskv))[*, jpj-1]) IF jpk GT 1 THEN vmaskred = reform(vmaskred[*]#replicate(1b, jpk), jpi-1, 1, jpk, /overwrite) e1f = temporary(d1v) e2t = temporary(d2v) e2t = shift(temporary(e2t), 0, 1) e2t[*, 0] = temporary(tmpsave) ; ; read all grid F variables ; jpiglo = jpiglo - 1 jpi = jpi - 1 ixmaxmesh = ixmaxmesh - 1 firstx = 0 lastx = jpi-1 nx = jpi ; for i = 0, n_elements(namebase)-1 do begin varinq = ncdf_varinq(cdfid, namebase[i]+'psi') name = varinq.name @read_ncdf_varget command = namebase2[i]+'f = float(temporary(res))' nothing = execute(command) ENDFOR tmpsave1 = 2. * 1.e3 * d1f[0, *] d1f = 1.e3*(shift(d1f, -1, 0) - d1f) tmpsave2 = 2. * 1.e3 * d2f[*, 0] d2f = 1.e3*(shift(d2f, 0, -1) - d2f) fmaskredy = byte(maskf[jpi-1, *]) IF jpk GT 1 THEN fmaskredy = reform(fmaskredy[*]#replicate(1b, jpk), 1, jpj, jpk, /overwrite) fmaskredx = byte((temporary(maskf))[*, jpj-1]) IF jpk GT 1 THEN fmaskredx = reform(fmaskredx[*]#replicate(1b, jpk), jpi, 1, jpk, /overwrite) e1v = temporary(d1f) e1v = shift(temporary(e1v), 1, 0) e1v[0, *] = temporary(tmpsave1) e2u = temporary(d2f) e2u = shift(temporary(e2u), 0, 1) e2u[*, 0] = temporary(tmpsave2) ;------------------------------------------------------- ; in the case of key_stride ne [1, 1, 1] redefine f points ; coordinates: they must be in the middle of 3 T points ;------------------------------------------------------- if key_stride[0] NE 1 OR key_stride[1] NE 1 then BEGIN ; we must recompute glamf and gphif... IF jpi GT 1 THEN BEGIN if (keyword_set(key_onearth) AND keyword_set(xnotsorted)) $ OR (keyword_set(key_periodic) AND key_irregular) then BEGIN stepxf = (glamt + 720) MOD 360 stepxf = shift(stepxf, -1, -1) - stepxf stepxf = [ [[stepxf]], [[stepxf + 360]], [[stepxf - 360]] ] stepxf = min(abs(stepxf), dimension = 3) IF NOT keyword_set(key_periodic) THEN $ stepxf[jpi-1, *] = stepxf[jpi-2, *] ENDIF ELSE BEGIN stepxf = shift(glamt, -1, -1) - glamt IF keyword_set(key_periodic) THEN $ stepxf[jpi-1, *] = 360 + stepxf[jpi-1, *] $ ELSE stepxf[jpi-1, *] = stepxf[jpi-2, *] ENDELSE IF jpj GT 1 THEN BEGIN stepxf[*, jpj-1] = stepxf[*, jpj-2] stepxf[jpi-1, jpj-1] = stepxf[jpi-2, jpj-2] ENDIF glamf = glamt + 0.5 * stepxf ENDIF ELSE glamf = glamt + 0.5 IF jpj GT 1 THEN BEGIN ; we must compute stepyf: y distance between T(i,j) T(i+1,j+1) stepyf = shift(gphit, -1, -1) - gphit stepyf[*, jpj-1] = stepyf[*, jpj-2] IF jpi GT 1 THEN BEGIN if NOT keyword_set(key_periodic) THEN $ stepyf[jpi-1, *] = stepyf[jpi-2, *] stepyf[jpi-1, jpj-1] = stepyf[jpi-2, jpj-2] ENDIF gphif = gphit + 0.5 * stepyf ENDIF ELSE gphif = gphit + 0.5 ENDIF ;------------------------------------------------------- ; 1d arrays ;------------------------------------------------------- gdept = findgen(jpk) gdepw = findgen(jpk) e3t = replicate(1., jpk) e3w = replicate(1., jpk) ;------------------------------------------------------- ncdf_close, cdfid ;------------------------------------------------------- ; Apply Glamboudary ;------------------------------------------------------- if keyword_set(glamboundary) AND key_onearth then BEGIN if glamboundary[0] NE glamboundary[1] then BEGIN glamt = temporary(glamt) MOD 360 smaller = where(glamt LT glamboundary[0]) if smaller[0] NE -1 then glamt[smaller] = glamt[smaller]+360 bigger = where(glamt GE glamboundary[1]) if bigger[0] NE -1 then glamt[bigger] = glamt[bigger]-360 glamu = temporary(glamu) MOD 360 smaller = where(glamu LT glamboundary[0]) if smaller[0] NE -1 then glamu[smaller] = glamu[smaller]+360 bigger = where(glamu GE glamboundary[1]) if bigger[0] NE -1 then glamu[bigger] = glamu[bigger]-360 glamv = temporary(glamv) MOD 360 smaller = where(glamv LT glamboundary[0]) if smaller[0] NE -1 then glamv[smaller] = glamv[smaller]+360 bigger = where(glamv GE glamboundary[1]) if bigger[0] NE -1 then glamv[bigger] = glamv[bigger]-360 glamf = temporary(glamf) MOD 360 smaller = where(glamf LT glamboundary[0]) if smaller[0] NE -1 then glamf[smaller] = glamf[smaller]+360 bigger = where(glamf GE glamboundary[1]) if bigger[0] NE -1 then glamf[bigger] = glamf[bigger]-360 toosmall = where(glamu EQ glamboundary[0]) IF toosmall[0] NE -1 THEN glamu[toosmall] = glamu[toosmall] + 360 toosmall = where(glamf EQ glamboundary[0]) IF toosmall[0] NE -1 THEN glamf[toosmall] = glamf[toosmall] + 360 endif endif ;------------------------------------------------------- ; make sure we do have 2d arrays when jpj eq 1 ;------------------------------------------------------- IF jpj EQ 1 THEN BEGIN glamt = reform(glamt, jpi, jpj, /over) gphit = reform(gphit, jpi, jpj, /over) e1t = reform(e1t, jpi, jpj, /over) e2t = reform(e2t, jpi, jpj, /over) glamu = reform(glamu, jpi, jpj, /over) gphiu = reform(gphiu, jpi, jpj, /over) e1u = reform(e1u, jpi, jpj, /over) e2u = reform(e2u, jpi, jpj, /over) glamv = reform(glamv, jpi, jpj, /over) gphiv = reform(gphiv, jpi, jpj, /over) e1v = reform(e1v, jpi, jpj, /over) e2v = reform(e2v, jpi, jpj, /over) glamf = reform(glamf, jpi, jpj, /over) gphif = reform(gphif, jpi, jpj, /over) e1f = reform(e1f, jpi, jpj, /over) e2f = reform(e2f, jpi, jpj, /over) ENDIF ;------------------------------------------------------- ixmindta = ixmindtasauve iymindta = iymindtasauve izmindta = izmindtasauve ;------------------------------------------------------- widget_control, noticebase, bad_id = nothing, /destroy ; ;==================================================== ; grid parameters used by xxx ;==================================================== ; IF NOT keyword_set(strcalling) THEN BEGIN IF n_elements(ccmeshparameters) EQ 0 THEN strcalling = 'ncdf_meshroms' $ ELSE strcalling = ccmeshparameters.filename ENDIF IF n_elements(glamt) GE 2 THEN BEGIN glaminfo = moment(glamt) IF finite(glaminfo[2]) EQ 0 THEN glaminfo = glaminfo[0:1] gphiinfo = moment(gphit) IF finite(gphiinfo[2]) EQ 0 THEN gphiinfo = gphiinfo[0:1] ENDIF ELSE BEGIN glaminfo = glamt gphiinfo = gphit ENDELSE romszinfos = {h:hroms, zeta:replicate(0., jpi, jpj), theta_s:-1, theta_b:-1, hc:-1} ccmeshparameters = {filename:strcalling $ , glaminfo:float(string(glaminfo, format = '(E11.4)')) $ , gphiinfo:float(string(gphiinfo, format = '(E11.4)')) $ , jpiglo:jpiglo, jpjglo:jpjglo, jpkglo:jpkglo $ , jpi:jpi, jpj:jpj, jpk:jpk $ , ixminmesh:ixminmesh, ixmaxmesh:ixmaxmesh $ , iyminmesh:iyminmesh, iymaxmesh:iymaxmesh $ , izminmesh:izminmesh, izmaxmesh:izmaxmesh $ , key_shift:key_shift, key_periodic:key_periodic $ , key_stride:key_stride, key_gridtype:key_gridtype $ , key_yreverse:key_yreverse, key_zreverse:key_zreverse $ , key_partialstep:key_partialstep, key_onearth:key_onearth} ; if keyword_set(key_performance) THEN $ print, 'time ncdf_meshread', systime(1)-tempsun ;------------------------------------------------------- @updateold ;------------------------------------------------------- return end