;+ ; ; @file_comments ; averages a 2- or 3-d field over a selected ; geographical area and along one or more ; selected axes (x, y or z) ; ; @categories ; ; @param TAB {in}{required} ; 2 or 3d field ; ; @param DIREC {in}{required} ; 'x' 'y' 'z' 'xy' 'xz' 'yz' or 'xyz' ; ; @keyword BOXZOOM ; [xmin,xmax,ymin,ymax (,(zmin,)zmax)] to more details, see domdef ; boxzoom can have 5 forms: ; [vert2], ; [vert1, vert2], ; [lon1, lon2, lat1, lat2], ; [lon1, lon2, lat1, lat2, vert2], ; [lon1, lon2, lat1, lat2, vert1,vert2] ; ; @keyword KEEPBOTTOM {default=0}{type=scalar: 0 or 1} ; used only with partial steps (key_partialstep /= 0). In partial ; steps, bottom points are not located at the same depth => they ; should not be averaged together along x and/or y direction if there ; is no average along z. In this case bottom ponts are set to ;!values.f_nan before doing any call to total. ; ; @keyword NAN ; not a number, we activate it if we want to average without considerate some masked values of TAB. ; If masked values of TAB are values consecrated by IDL(!values.f_nan), we just have to put NAN. ; If masked values of TAB are valued a (a must be different of 1, corresponding to nan = ; !values.f_nan and of 0, which desactivate nan). We have to put NAN=a. ; Comment: In output, result points which are NAN will be valued a or !values.f_nan. ; ; @keyword NODOMDEF ; We activate it if we do not want to pass in domdef even if the ; keyword boxzoom is present (like when grossemoyenne is called ; via checkfield) ; ; @keyword INTEGRATION ; To make an integral rather than an average ; ; @keyword WDEPTH ; to specify that the field is at W depth instead of T ; depth (automatically activated if vargrid eq 'W') ; ; @returns ; An array ; ; @uses ; common ; domdef ; ; @restrictions ; Put values corresponding to land at 1.e20 ; ; @history ; Jerome Vialard (jv\@lodyc.jussieu.fr) ; 2/7/98 ; Sebastien Masson (smasson\@lodyc.jussieu.fr) ; 14/8/98 ; 15/1/98 ; 11/3/99 adaptation for NAN ; 28/7/99 Averages 1d arrays ; ; @version ; $Id$ ; ;- FUNCTION moyenne, tab, direc, BOXZOOM=boxzoom, INTEGRATION=integration, KEEPBOTTOM = keepbottom $ , NAN=nan, NODOMDEF=nodomdef, WDEPTH=wdepth, _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) ; To key_performance ;------------------------------------------------------------ ;I) preliminaries ;------------------------------------------------------------ dirt = 0 dirx = 0 diry = 0 dirz = 0 ;------------------------------------------------------------ ; I.1) Directions(s) we follow to integrate ;------------------------------------------------------------ if ( strpos(direc, 't') ge 0 ) then dirt = 1 if ( strpos(direc, 'x') ge 0 ) then dirx = 1 if ( strpos(direc, 'y') ge 0 ) then diry = 1 if ( strpos(direc, 'z') ge 0 ) then dirz = 1 if (dirx eq 0 and diry eq 0 and dirz eq 0) then return, tab ;------------------------------------------------------------ ; I.2) verification of the input array's size ;------------------------------------------------------------ taille = size(tab) case 1 of taille[0] eq 1 :dim = '1d' taille[0] eq 2 :BEGIN dim = '2d' if dirx eq 0 and diry eq 0 then return, tab END taille[0] eq 3 :BEGIN dim = '3d' if dirx eq 0 and diry eq 0 and dirz eq 0 then return, tab END else : return, report('Array must have 2 or 3 dimensions if there is a time dimension use grossemoyenne') endcase ;------------------------------------------------------------ ; I.3) Obtainment of scale's factors and of the mask on the subdomain concernedby the average. ; Redefinition of the domain ajusted at boxzoom (at 6 elements) ; This will allowed us to calculate only in the domain concerned by the average. ; Domdef, followed by grid give us all arrays of the grid on the subdomain ;------------------------------------------------------------ if keyword_set(boxzoom) then BEGIN Case 1 Of N_Elements(Boxzoom) Eq 1:bte = [lon1, lon2, lat1, lat2, 0., boxzoom[0]] N_Elements(Boxzoom) Eq 2:bte = [lon1, lon2, lat1, lat2, boxzoom[0], boxzoom[1]] N_Elements(Boxzoom) Eq 4:bte = [Boxzoom, vert1, vert2] N_Elements(Boxzoom) Eq 5:bte = [Boxzoom[0:3], 0, Boxzoom[4]] N_Elements(Boxzoom) Eq 6:bte = Boxzoom Else: return, report('Bad definition of Boxzoom') endcase if NOT keyword_set(nodomdef) then BEGIN savedbox = 1b saveboxparam, 'boxparam4moyenne.dat' domdef, bte, GRIDTYPE = vargrid, _extra = ex ENDIF ENDIF ;--------------------------------------------------------------- ; attribution of the mask and of longitude and latitude arrays... ;--------------------------------------------------------------- IF vargrid EQ 'W' THEN wdepth = 1 grille, mask, glam, gphi, gdep, nx, ny, nz, firstx, firsty, firstz, lastx, lasty, lastz, e1, e2, e3, WDEPTH = wdepth ;------------------------------------------------------------ ;------------------------------------------------------------ ; II) Case of the 1d array ;------------------------------------------------------------ ;------------------------------------------------------------ if dim EQ '1d' then BEGIN if n_elements(tab) NE nx*ny AND n_elements(tab) NE nx*ny*nz then BEGIN if keyword_set(savedbox) THEN restoreboxparam, 'boxparam4moyenne.dat' return, report('Probleme d''adequation entre les tailles du domaine et de la boxzoom.') ENDIF case 1 of nx EQ 1 AND ny EQ 1:BEGIN ;vector following z case n_elements(tab) of jpk:res = tab[firstz:lastz] nz:res = tab ELSE:BEGIN if keyword_set(savedbox) THEN restoreboxparam, 'boxparam4moyenne.dat' return, report('Probleme d''adequation entre les tailles du domaine et de la boxzoom.') END ENDCASE if dirz EQ 1 then BEGIN dim = '3d' taille = size(reform(res, nx, ny, nz)) ENDIF ELSE BEGIN if keyword_set(savedbox) THEN restoreboxparam, 'boxparam4moyenne.dat' return, res ENDELSE END ny EQ 1:BEGIN ;vector following x case n_elements(tab) of jpi:res = tab[firstx:lastx] nx:res = tab ELSE:BEGIN if keyword_set(savedbox) THEN restoreboxparam, 'boxparam4moyenne.dat' return, report('Probleme d''adequation entre les tailles du domaine et de la boxzoom.') END ENDCASE if dirx EQ 1 then BEGIN dim = '2d' taille = size(reform(res, nx, ny)) ENDIF ELSE BEGIN if keyword_set(savedbox) THEN restoreboxparam, 'boxparam4moyenne.dat' return, res ENDELSE END nx EQ 1:BEGIN ;vector following y case n_elements(tab) of jpj:res = tab[firsty:lasty] ny:res = tab ELSE:BEGIN if keyword_set(savedbox) THEN restoreboxparam, 'boxparam4moyenne.dat' return, report('Probleme d''adequation entre les tailles du domaine et de la boxzoom.') END ENDCASE if diry EQ 1 then BEGIN dim = '2d' taille = size(reform(res, nx, ny)) ENDIF ELSE BEGIN if keyword_set(savedbox) THEN restoreboxparam, 'boxparam4moyenne.dat' return, res ENDELSE END endcase endif ;------------------------------------------------------------ ;------------------------------------------------------------ ; II) Case of the 2d array ;------------------------------------------------------------ ;------------------------------------------------------------ if (dim eq '2d') then begin ;--------------------------------------------------------------- ; II.1) verification of the coherence of the array's size to average ; Verification of the coherence between the array's size and the domain defined by domdef ; The input array must have either the total domain's size (jpi,jpj) or this ; one of the reduced domain (nx,ny) ;--------------------------------------------------------------- case 1 of taille[1] eq jpi and taille[2] eq jpj: $ res = tab[firstx:lastx, firsty:lasty] taille[1] eq nx and taille[2] eq ny:res = tab else:BEGIN if keyword_set(savedbox) THEN restoreboxparam, 'boxparam4moyenne.dat' return, report('Probleme d''adequation entre les tailles du domaine nx*ny '+strtrim(nx, 1)+'*'+strtrim(ny, 1)+' et du tableau '+strtrim(taille[1], 1)+'*'+strtrim(taille[2], 1)) END ENDCASE if keyword_set(nan) NE 0 then BEGIN if nan NE 1 then BEGIN ; If nan is not !values.f_nan ; we put it at !values.f_nan if abs(nan) LT 1e6 then notanumber = where(res EQ nan) $ ELSE notanumber = where(abs(res) GT abs(nan)/10.) if notanumber[0] NE -1 then res[temporary(notanumber)] = !values.f_nan ENDIF ENDIF ;--------------------------------------------------------------- ; Comment : WE HAVE TO BE CAREFUL ABOUT CASES WHERE THE DIMENSION TO AVERAGE = 1, ; AND MAKE SURE THAT IT EXIST. THAT IS WHY WE USE reform(...,nx,ny,...) WHICH CAN ; LOOK USELESS AT THE BEGINNING ;--------------------------------------------------------------- if nx EQ 1 OR ny EQ 1 then BEGIN res = reform(res, nx, ny, /over) e1 = reform(e1, nx, ny, /over) e2 = reform(e2, nx, ny, /over) endif if nx EQ 1 OR ny EQ 1 OR nz EQ 1 THEN $ mask = reform(mask, nx, ny, nz, /over) ;--------------------------------------------------------------- ; II.3) Different types of average ;--------------------------------------------------------------- mask = mask[*, *, 0] if keyword_set(nan) NE 0 then msknan = finite(res) ELSE msknan = -1 case 1 of (dirx eq 1) and (diry eq 0) : begin e = e1*mask if keyword_set(integration) then divi = 1 $ else begin divi = e IF msknan[0] NE -1 THEN divi = temporary(divi)*msknan if ny EQ 1 then divi = reform(divi, nx, ny, /over) divi = total(divi, 1) endelse res = res*e if ny EQ 1 then res = reform(res, nx, ny, /over) res = total(res, 1, nan = nan)/(divi > 1.) if msknan[0] NE -1 then begin testnan = msknan*mask if ny EQ 1 then testnan = reform(testnan, nx, ny, /over) testnan = total(testnan, 1)+(total(mask, 1) EQ 0) endif end (dirx eq 0) and (diry eq 1) : begin e = e2*mask if keyword_set(integration) then divi = 1 $ else begin divi = e IF msknan[0] NE -1 THEN divi = temporary(divi)*msknan if ny EQ 1 then divi = reform(divi, nx, ny, /over) divi = total(divi, 2) endelse res = res*e if ny EQ 1 then res = reform(res, nx, ny, /over) res = total(res, 2, nan = nan)/(divi > 1.) if msknan[0] NE -1 then begin testnan = msknan*mask if ny EQ 1 then testnan = reform(testnan, nx, ny, /over) testnan = total(testnan, 2)+(total(mask, 2) EQ 0) endif end (dirx eq 1) and (diry eq 1) : begin if keyword_set(integration) then divi = 1 else BEGIN IF msknan[0] NE -1 THEN divi = total(e1*e2*mask*msknan) $ ELSE divi = total(e1*e2*mask) ENDELSE res = total(res*e1*e2*mask, nan = nan)/(divi > 1.) if msknan[0] NE -1 then begin testnan = msknan*mask testnan = total(testnan)+(total(mask) EQ 0) endif end endcase endif ;------------------------------------------------------------ ;------------------------------------------------------------ ; III) Case 3d arrays series (tab4d) ;------------------------------------------------------------ ;------------------------------------------------------------ if (dim eq '3d') then begin ;--------------------------------------------------------------- ; III.1) Verification of the coherence of the array to average size ; Verification of the coherence between the array's size and the domain ; defind by domdef ; The input array must have either the total domain size (jpi,jpj,jpk) ; or this one of the reduced domain (nx,ny,ny) ;--------------------------------------------------------------- case 1 of taille[1] eq jpi and taille[2] eq jpj and taille[3] eq jpk: $ res = tab[firstx:lastx, firsty:lasty, firstz:lastz] taille[1] eq jpi and taille[2] eq jpj and taille[3] eq nz: $ res = tab[firstx:lastx, firsty:lasty, *] taille[1] EQ nx and taille[2] eq ny and taille[3] eq nz :res = tab taille[1] EQ nx and taille[2] eq ny and taille[3] eq jpk : $ res = tab[*, *, firstz:lastz] else:BEGIN if keyword_set(savedbox) THEN restoreboxparam, 'boxparam4moyenne.dat' return, report('Probleme d''adequation entre les tailles du domaine nx*ny*nz '+strtrim(nx, 1)+'*'+strtrim(ny, 1)+'*'+strtrim(nz, 1)+' et du tableau '+strtrim(taille[1], 1)+'*'+strtrim(taille[2], 1)+'*'+strtrim(taille[3], 1)) END endcase if keyword_set(nan) NE 0 then BEGIN if nan NE 1 then BEGIN ; if nan is not !values.f_nan ; we put it at !values.f_nan if abs(nan) LT 1e6 then notanumber = where(res EQ nan) $ ELSE notanumber = where(abs(res) GT abs(nan)/10.) if notanumber[0] NE -1 then res[temporary(notanumber)] = !values.f_nan ENDIF ENDIF ;--------------------------------------------------------------- ; Comment : WE HAVE TO BE CAREFUL ABOUT CASES WHERE THE DIMENSION TO AVERAGE = 1, ; AND MAKE SURE THAT IT EXIST. THAT IS WHY WE USE reform(...,nx,ny,...) WHICH CAN ; LOOK USELESS AT THE BEGINNING ;--------------------------------------------------------------- if nx EQ 1 OR ny EQ 1 OR nz EQ 1 then BEGIN res = reform(res, nx, ny, nz, /over) e1 = reform(e1, nx, ny, /over) e2 = reform(e2, nx, ny, /over) endif if nx EQ 1 OR ny EQ 1 OR nz EQ 1 THEN $ mask = reform(mask, nx, ny, nz, /over) IF keyword_set(key_partialstep) THEN BEGIN ; the top of the ocean floor is IF vargrid EQ 'T' OR vargrid EQ 'W' THEN bottom = total(mask, 3) $ ELSE bottom = total(tmask[firstx:lastx, firsty:lasty, firstz:lastz], 3) ; we suppress columns with only ocean or land good = where(bottom NE 0 AND bottom NE nz) ; the bottom of the ocean in 3D index is: bottom = lindgen(nx*ny)+(temporary(bottom)-1L)*nx*ny IF good[0] NE -1 THEN bottom = bottom[good] $ ELSE bottom = -1 ENDIF ELSE bottom = -1 ;--------------------------------------------------------------- ; III.2) different average types ;--------------------------------------------------------------- if keyword_set(nan) NE 0 then msknan = finite(res) ELSE msknan = -1 case 1 of (dirx eq 1) and (diry eq 0) and (dirz eq 0) : begin e13 = e1[*]#replicate(1., nz) e13 = reform(e13, nx, ny, nz, /over) IF keyword_set(key_partialstep) AND bottom[0] NE -1 $ AND nx NE 1 AND NOT keyword_set(keepbottom) THEN BEGIN IF msknan[0] EQ -1 THEN BEGIN msknan = replicate(1b, nx, ny, nz) nan = 1 endif msknan[bottom] = 0 res[bottom] = !values.f_nan ENDIF if keyword_set(integration) then divi = 1 else begin divi = e13*mask IF msknan[0] NE -1 THEN divi = temporary(divi)*msknan if nz EQ 1 then divi = reform(divi, nx, ny, nz, /over) divi = total(divi, 1) ENDELSE res = res*e13*mask if nz EQ 1 then res = reform(res, nx, ny, nz, /over) res = total(res, 1, nan = nan)/(divi > 1.) e13 = 1 if msknan[0] NE -1 then begin testnan = msknan*mask if nz EQ 1 then testnan = reform(testnan, nx, ny, nz, /over) testnan = total(testnan, 1)+(total(mask, 1) EQ 0) endif end (dirx eq 0) and (diry eq 1) and (dirz eq 0) : begin e23 = e2[*]#replicate(1., nz) e23 = reform(e23, nx, ny, nz, /over) IF keyword_set(key_partialstep) AND bottom[0] NE -1 $ AND ny NE 1 AND NOT keyword_set(keepbottom) THEN BEGIN IF msknan[0] EQ -1 THEN BEGIN msknan = replicate(1b, nx, ny, nz) nan = 1 endif msknan[bottom] = 0 res[bottom] = !values.f_nan ENDIF if keyword_set(integration) then divi = 1 else begin divi = e23*mask IF msknan[0] NE -1 THEN divi = temporary(divi)*msknan if nz EQ 1 then divi = reform(divi, nx, ny, nz, /over) divi = total(divi, 2) ENDELSE res = res*e23*mask if nz EQ 1 then res = reform(res, nx, ny, nz, /over) res = total(res, 2, nan = nan)/(divi > 1.) e23 = 1 if msknan[0] NE -1 then begin testnan = msknan*mask if nz EQ 1 then testnan = reform(testnan, nx, ny, nz, /over) testnan = total(testnan, 2)+(total(mask, 2) EQ 0) endif end (dirx eq 0) and (diry eq 0) and (dirz eq 1) : begin e33 = replicate(1, 1.*nx*ny)#e3 e33 = reform(e33, nx, ny, nz, /over) IF keyword_set(key_partialstep) AND bottom[0] NE -1 THEN BEGIN IF keyword_set(wdepth) THEN $ e33[bottom] = (e3w_ps[firstx:lastx, firsty:lasty])[temporary(good)] $ ELSE e33[bottom] = (e3t_ps[firstx:lastx, firsty:lasty])[temporary(good)] ENDIF if keyword_set(integration) then divi = 1 else begin divi = e33*mask if msknan[0] NE -1 then divi = temporary(divi)*msknan if nz EQ 1 then divi = reform(divi, nx, ny, nz, /over) divi = total(divi, 3) ENDELSE res = res*e33*mask if nz EQ 1 then res = reform(res, nx, ny, nz, /over) res = total(res, 3, nan = nan)/(divi > 1.) e33 = 1 if msknan[0] NE -1 then begin testnan = msknan*mask if nz EQ 1 then testnan = reform(testnan, nx, ny, nz, /over) testnan = total(testnan, 3)+(total(mask, 3) EQ 0) endif end (dirx eq 1) and (diry eq 1) and (dirz eq 0) : begin e123 = (e1*e2)[*]#replicate(1., nz) e123 = reform(e123, nx, ny, nz, /over) IF keyword_set(key_partialstep) AND bottom[0] NE -1 $ AND nx*ny NE 1 AND NOT keyword_set(keepbottom) THEN BEGIN IF msknan[0] EQ -1 THEN BEGIN msknan = replicate(1b, nx, ny, nz) nan = 1 endif msknan[bottom] = 0 res[bottom] = !values.f_nan ENDIF if keyword_set(integration) then divi = 1 else BEGIN divi = e123*mask IF msknan[0] NE -1 THEN divi = temporary(divi)*msknan if nz EQ 1 then divi = reform(divi, nx, ny, nz, /over) divi = total(total(divi, 1), 1) ENDELSE res = res*e123*mask if nz EQ 1 then res = reform(res, nx, ny, nz, /over) res = total(total(res, 1, nan = nan), 1, nan = nan) / (divi > 1.) e123 = 1 if msknan[0] NE -1 then begin testnan = msknan*mask if nz EQ 1 then testnan = reform(testnan, nx, ny, nz, /over) testnan = total(total(testnan, 1), 1)+(total(total(mask, 1), 1) EQ 0) endif end (dirx eq 1) and (diry eq 0) and (dirz eq 1) : begin e133 = e1[*]#e3 e133 = reform(e133, nx, ny, nz, /over) IF keyword_set(key_partialstep) AND bottom[0] NE -1 THEN BEGIN IF keyword_set(wdepth) THEN $ e133[bottom] = (e1*e3w_ps[firstx:lastx, firsty:lasty])[temporary(good)] $ ELSE e133[bottom] = (e1*e3t_ps[firstx:lastx, firsty:lasty])[temporary(good)] ENDIF if keyword_set(integration) then divi = 1 else BEGIN divi = e133*mask if msknan[0] NE -1 then divi = temporary(divi)*msknan if nz EQ 1 then divi = reform(divi, nx, ny, nz, /over) divi = total(total(divi, 1), 2) ENDELSE res = res*e133*mask if nz EQ 1 then res = reform(res, nx, ny, nz, /over) res = total(total(res, 1, nan = nan), 2, nan = nan) / (divi > 1.) e133 = 1 if msknan[0] NE -1 then begin testnan = msknan*mask if nz EQ 1 then testnan = reform(testnan, nx, ny, nz, /over) testnan = total(total(testnan, 1), 2)+(total(total(mask, 1), 2) EQ 0) endif end (dirx eq 0) and (diry eq 1) and (dirz eq 1) : begin e233 = e2[*]#e3 e233 = reform(e233, nx, ny, nz, /over) IF keyword_set(key_partialstep) AND bottom[0] NE -1 THEN BEGIN IF keyword_set(wdepth) THEN $ e233[bottom] = (e2*e3w_ps[firstx:lastx, firsty:lasty])[temporary(good)] $ ELSE e233[bottom] = (e2*e3t_ps[firstx:lastx, firsty:lasty])[temporary(good)] ENDIF if keyword_set(integration) then divi = 1 else BEGIN divi = e233*mask if msknan[0] NE -1 then divi = temporary(divi)*msknan if nz EQ 1 then divi = reform(divi, nx, ny, nz, /over) divi = total(total(divi, 2), 2) ENDELSE res = res*e233*mask if nz EQ 1 then res = reform(res, nx, ny, nz, /over) res = total(total(res, 2, nan = nan), 2, nan = nan) / (divi > 1.) e233 = 1 if msknan[0] NE -1 then begin testnan = msknan*mask if nz EQ 1 then testnan = reform(testnan, nx, ny, nz, /over) testnan = total(total(testnan, 2), 2)+(total(total(mask, 2), 2) EQ 0) endif end (dirx eq 1) and (diry eq 1) and (dirz eq 1) : begin e1233 = (e1*e2)[*]#e3 e1233 = reform(e1233, nx, ny, nz, /over) IF keyword_set(key_partialstep) AND bottom[0] NE -1 THEN BEGIN IF keyword_set(wdepth) THEN $ e1233[bottom] = (e1*e2*e3w_ps[firstx:lastx, firsty:lasty])[temporary(good)] $ ELSE e1233[bottom] = (e1*e2*e3t_ps[firstx:lastx, firsty:lasty])[temporary(good)] ENDIF if keyword_set(integration) then divi = 1 else BEGIN if msknan[0] NE -1 then divi = total(e1233*mask*msknan) $ ELSE divi = total(e1233*mask) ENDELSE res = total(res*e1233*mask, nan = nan) / (divi > 1.) e1233 = 1 if msknan[0] NE -1 then begin testnan = msknan*mask testnan = total(testnan)+(total(mask) EQ 0) endif end endcase endif ;------------------------------------------------------------ ;------------------------------------------------------------ ;IV ) finishing ;------------------------------------------------------------ ;------------------------------------------------------------ ; IV.1) We mask land by a value equal to 1.e+20 ;------------------------------------------------------------ valmask = 1e+20 terre = where(divi EQ 0) IF terre[0] NE -1 THEN BEGIN res[terre] = 1e+20 ENDIF ;------------------------------------------------------------ ; IV.2) We replace, when nan equal 1, !values.f_nan by nan ;------------------------------------------------------------ if keyword_set(nan) NE 0 then BEGIN puttonan = where(testnan EQ 0) if puttonan[0] NE -1 then res[puttonan] = !values.f_nan if nan NE 1 then BEGIN notanumber = where(finite(res) eq 0) if notanumber[0] NE -1 then res[notanumber] = nan ENDIF ENDIF ;------------------------------------------------------------ ; IV.3) We replace in the domain whch was defined at the entry of average ;------------------------------------------------------------ if keyword_set(savedbox) THEN restoreboxparam, 'boxparam4moyenne.dat' ;------------------------------------------------------------ if keyword_set(key_performance) THEN print, 'temps moyenne', systime(1)-tempsun return, res ;------------------------------------------------------------ ;------------------------------------------------------------ end