Rev 3 | Blame | Compare with Previous | Last modification | View Log | Download | RSS feed
PROGRAM clustering! *********************************************************************************! * Interpolate fields to tropopause and assign labels with clustering algorithm *! * Michael Sprenger / Spring 2006: initial version *! * Michael Sprenger / 2012: horizontal propagation of LABEL 2 prohibited in *! * lower troposphere *! * Bojan Skerlak / Spring 2012 / Fall 2012: advanced LABEL 1-5 handling *! * Jan 2013: - change horiz. prop. decision method to height(log(pressure)) *! * - perform check of label for exotic cases where the pressure *! * is higher than 800 hPa (deep folds) *! * - additional cutoff-special-treatment prohibits identification *! * of strat. air as cutoffs if below horizontal prop. limit *! * (e.g. deep 'funnels' with 'bumps' on the side) *! * - additional check in strat. (TH>380) for 'wrong' sign of PV *! * that can lead to errors in calculating TROPO *! * - additional check for PV towers (whole column gets label 2) *! * July 2013: - replace fixed tropo_pv,tropo_th by user-defined values *! * Jan 2014: - v2.0 Add specific humidity criterion for diabatic PV *! * Feb 2014: - v2.1 Correct 'filling' when labels 2/3 or 2/5 meet *! * Mar 2014: - v2.2 Filling of 2/3 and 2/5 also propagates downward via *! * 'connect' subroutine. Thus, now also dird/diru have to be *! * specified. *! * - v2.3 Identify and write out tropopause folds (FOLD) 1/0 *! * Nov 2014: - remove some commented sections and delete temporary stuff *! * Dec 2014: - simply the code to only contain the core algorithm and no *! * input/output section (which depends on data format and *! * model setup) *! * Andrea Pozzer (MPIC):! * Dec 2014: - Implementation for netcdf I/O, Makefile and namelist reading *! *********************************************************************************!????????????????????????????????????????????????????????????????????????????????!????????????????????????????????????????????????????????????????????????????????!????????????????????????????????????????????????????????????????????????????????!??????????????? ??????????????????????????!??????????????? 1) 3D-LABELLING ??????????????????????????!??????????????? 2) TROPOPAUSE FOLD IDENTIFICATION ??????????????????????????!??????????????? ??????????????????????????!????????????????????????????????????????????????????????????????????????????????!????????????????????????????????????????????????????????????????????????????????!MMM?????????????????????????????????????????????????????????????????????????????!.....DMMI???????????????????????????????????????????MMMMMMMMMMM?I???????????????!.........IMO????????????????????????????????????IMM ........... 8MM8???????????!............MM?????????????????????????????????M$ ..............MMMI??????!............. MD?????????????????????????????IM. PMIN .................OMMI??!................MI???????????????????????????M . ^ ......................NM!.................MO?????????????????????????M... | .............................!.................. M???????????????????????M.... | .............................!....................M8?????????????????????M.... | .........................!.....................:M????????????????????M ... | DP .........................!.......................MO???????????????????M... | .........................!........................ M??????????????????M~.. | .............................!..........................MM????????????????IM.. | .............................!............................MI????????????????M. v .............................!.............................:MI???????????????M. .............................!...............................DMI??????????????M$ .............................!................................ MM??????????????DM ............................!.................................. MMI????????????IM ...........................!.....................................MMI????????????M ..........................!........................................MM??????????IM..........................!......................................... $MMI???????M .........................!........................................... MMMMMMM7. ..........................!............................................. .............................!............................................. PMAX .............................!............................................. .............................!................................................................................USE mo_f2kcli ! command line interfaceUSE netcdf_toolsimplicit none! VERSIONcharacter(len=*), parameter :: VERSION = '1.4'! ... INTERNAL parameterinteger, parameter :: str_short = 30 ! length of short stringsinteger, parameter :: str_long = 100 ! length of long stringsinteger, parameter :: str_vlong = 500 ! length of very long stringsinteger, parameter :: iou = 21 ! I/O unit! FOR COMMAND LINECHARACTER(LEN=256) :: EXE ! program nameCHARACTER(LEN=80) :: CMD ! argumentINTEGER :: NARG ! number of arguments! VARIABLESINTEGER :: status ! status flagLOGICAL :: l_invert = .FALSE. ! invert axisLOGICAL :: l_Pa = .FALSE. ! convert units Pa->hPa! Set tropopause thresholdsreal :: tropo_pv,tropo_th! diabatically produced PV (Q-threshold), Feb 2014:real :: q_th! masking numberreal, parameter :: mdv =-999.0! DATA/VARIABLE! - input datareal, allocatable, dimension(:) :: x_data, y_data,z_data,t_datareal, allocatable, dimension(:) :: ak,bkreal, allocatable, dimension(:,:,:) :: apsreal, allocatable, dimension(:,:,:,:) :: pressreal, allocatable, dimension(:,:,:,:) :: q_in,pv_in,pt_inreal, allocatable, dimension(:,:,:,:) :: q,pv,ptcharacter(len=str_long) :: x_units,y_units, z_units,t_unitscharacter(len=str_long) :: aps_units! - final resultsreal, allocatable, dimension(:,:,:) :: fold ! fold yes/noreal, allocatable, dimension(:,:,:) :: sfold ! shallow fold yes/noreal, allocatable, dimension(:,:,:) :: mfold ! medium fold yes/noreal, allocatable, dimension(:,:,:) :: dfold ! deep fold yes/noreal, allocatable, dimension(:,:,:) :: tp ! tropopausereal, allocatable, dimension(:,:,:) :: dp ! folding depth (Pa)real, allocatable, dimension(:,:,:) :: pmin ! folding depth (Pa)real, allocatable, dimension(:,:,:) :: pmax ! folding depth (Pa)real, allocatable, dimension(:,:,:,:) :: label ! folding classificationreal, allocatable, dimension(:,:,:,:) :: label_out! Auxiliary variablesinteger :: statinteger :: i,j,k,treal :: lev(100)!namelist...character(len=str_long) file_inputcharacter(len=str_long) file_outputcharacter(len=str_short) x_name,y_name,z_name,t_namecharacter(len=str_short) aps_name,hyam_name, hybm_namecharacter(len=str_short) q_name, pv_name, pt_name! DATA DIMENSIONinteger :: nx,ny,nz,nt! -----------------------------------------------------------------! Initialisation paramaters! -----------------------------------------------------------------! Humidity criterion for diabatically produced PV, BOJAN: this is the threshold Q=0.1g/kgq_th=0.0001tropo_pv = 2.0tropo_th = 380.0! -----------------------------------------------------------------! Read command line! -----------------------------------------------------------------narg = command_argument_count() ! number of argumentscall get_command_argument(0,exe) ! program name!if (narg > 1) thenwrite(*,*) 'command-line error: too many arguments !'call usage(trim(exe))stopend if!if (narg == 0) thencall usage(trim(exe))stopend if!call get_command_argument(1,cmd)! -----------------------------------------------------------------! Read namelist file! -----------------------------------------------------------------CALL read_nml(status, iou, TRIM(CMD))IF (status /= 0) STOP! -----------------------------------------------------------------! Inquire netcdf file! -----------------------------------------------------------------CALL inquire_file(TRIM(file_input), &x_name, y_name, z_name,t_name, &nx, ny, nz, nt )! IF (status > 0) STOP ! ERRORWRITE(*,*) '==========================================================='WRITE(*,*) " FILE DIMENSION: "WRITE(*,*) " X :", nxWRITE(*,*) " Y :", nyWRITE(*,*) " Z :", nzWRITE(*,*) " T :", ntWRITE(*,*) '==========================================================='! -----------------------------------------------------------------! allocate data file! -----------------------------------------------------------------ALLOCATE(x_data(nx))ALLOCATE(y_data(ny))ALLOCATE(z_data(nz))ALLOCATE(t_data(nt))ALLOCATE(aps(nx,ny,nt))ALLOCATE(ak(nz))ALLOCATE(bk(nz))ALLOCATE(press(nx,ny,nz,nt))!ALLOCATE(q(nx,ny,nz,nt))ALLOCATE(pt(nx,ny,nz,nt))ALLOCATE(pv(nx,ny,nz,nt))ALLOCATE(q_in(nx,ny,nz,nt))ALLOCATE(pt_in(nx,ny,nz,nt))ALLOCATE(pv_in(nx,ny,nz,nt))ALLOCATE(label(nx,ny,nz,nt))ALLOCATE(fold(nx,ny,nt))ALLOCATE(sfold(nx,ny,nt))ALLOCATE(mfold(nx,ny,nt))ALLOCATE(dfold(nx,ny,nt))ALLOCATE(tp(nx,ny,nt))ALLOCATE(dp(nx,ny,nt))ALLOCATE(pmin(nx,ny,nt))ALLOCATE(pmax(nx,ny,nt))ALLOCATE(label_out(nx,ny,nz,nt))! -----------------------------------------------------------------! read netcdf file! -----------------------------------------------------------------call read_file(TRIM(file_input), TRIM(x_name), x_data)call read_file(TRIM(file_input), TRIM(y_name), y_data)call read_file(TRIM(file_input), TRIM(z_name), z_data)call read_file(TRIM(file_input), TRIM(t_name), t_data)call read_att(TRIM(file_input), TRIM(x_name), "units", x_units)call read_att(TRIM(file_input), TRIM(y_name), "units", y_units)call read_att(TRIM(file_input), TRIM(z_name), "units", z_units)call read_att(TRIM(file_input), TRIM(t_name), "units", t_units)! pressure datacall read_file(TRIM(file_input), TRIM(aps_name), aps)call read_file(TRIM(file_input), TRIM(hyam_name), ak)call read_file(TRIM(file_input), TRIM(hybm_name), bk)! presure unitscall read_att(TRIM(file_input), TRIM(aps_name), "units", aps_units)! humiditycall read_file(TRIM(file_input), TRIM(q_name), q_in)! potential temperaturecall read_file(TRIM(file_input), TRIM(pt_name), pt_in)! potential vorticitycall read_file(TRIM(file_input), TRIM(pv_name), pv_in)! -----------------------------------------------------------------! create pressure field and! invert vertical axis (if needed)! -----------------------------------------------------------------! bottom => high pressure = 1! top => low pressure = nzif ((ak(1)+bk(1)*101325).lt.(ak(2)+bk(2)*101325)) thenl_invert=.TRUE.write (*,*) 'Invert vertical axis'endifif (aps_units == "Pa") thenwrite (*,*) 'convert pressure from Pa to hPa'l_Pa=.TRUE.endifif (l_invert) then! to be inverteddo i=1,nxdo j=1,nydo k=1,nzdo t=1,ntif (l_Pa) thenpress(i,j,nz-k+1,t)= (ak(k)+aps(i,j,t)*bk(k))/100.0 ! Pa -> hPaelsepress(i,j,nz-k+1,t)= (ak(k)+aps(i,j,t)*bk(k))endifq(i,j,nz-k+1,t) = q_in(i,j,k,t)pt(i,j,nz-k+1,t)= pt_in(i,j,k,t)pv(i,j,nz-k+1,t)= pv_in(i,j,k,t)enddoenddoenddoenddoELSE! NOT to be inverteddo i=1,nxdo j=1,nydo k=1,nzdo t=1,ntif (l_Pa) thenpress(i,j,k,t)= (ak(k)+aps(i,j,t)*bk(k))/100.0 ! Pa -> hPaelsepress(i,j,k,t)= ak(k)+aps(i,j,t)*bk(k)endifenddoenddoenddoenddoq(1:nx,1:ny,1:nz,1:nt) = q_in(1:nx,1:ny,1:nz,1:nt)pt(1:nx,1:ny,1:nz,1:nt) = pt_in(1:nx,1:ny,1:nz,1:nt)pv(1:nx,1:ny,1:nz,1:nt) = pv_in(1:nx,1:ny,1:nz,1:nt)ENDIF! -----------------------------------------------------------------! perform calculation for each timestep! -----------------------------------------------------------------! TIME LOOPdo t=1,ntwrite(*,*) "time step",t,"/", nt! -------------------------------------------------------------------! Part 1) Run clustering algorithm, get labels and interpolate to tropopause! -------------------------------------------------------------------! ------------------------------! Running clustering algorithm! ------------------------------call tropopause (tp(:,:,t),label(:,:,:,t),press(:,:,:,t),pv(:,:,:,t),pt(:,:,:,t),q(:,:,:,t), &mdv,y_data(:),nx,ny,nz,tropo_pv,tropo_th,q_th)! -------------------------------------------------------------------! Part 2) Identify tropopause folds from vertical cross-sections! -----------------------------------------------------------------! ------------------------------! Identifying tropopause folds! ------------------------------call tropofold (label(:,:,:,t),press(:,:,:,t),pv(:,:,:,t),pt(:,:,:,t), &fold(:,:,t), dp(:,:,t), pmin(:,:,t), pmax(:,:,t), sfold(:,:,t), mfold(:,:,t), dfold(:,:,t), &mdv,nx,ny,nz,tropo_pv,tropo_th)enddo! END OF TIME LOOP! -----------------------------------------------------------------! re-invert vertical axis (if needed)! -----------------------------------------------------------------if (l_invert) then! to be inverteddo i=1,nxdo j=1,nydo k=1,nzdo t=1,ntlabel_out(i,j,nz-k+1,t) = label(i,j,k,t)enddoenddoenddoenddoELSE! NOT to be invertedlabel_out(1:nx,1:ny,1:nz,1:nt) = label(1:nx,1:ny,1:nz,1:nt)ENDIF! -----------------------------------------------------------------! write netcdf files! -----------------------------------------------------------------call nc_dump (TRIM(file_output), x_data, y_data, z_data, t_data, &x_units,y_units, z_units,t_units, aps, ak, bk, label_out, fold, &sfold, mfold, dfold, tp, dp, pmin, pmax)! -----------------------------------------------------------------! deallocate data file! -----------------------------------------------------------------DEALLOCATE(x_data)DEALLOCATE(y_data)DEALLOCATE(z_data)DEALLOCATE(t_data)DEALLOCATE(aps)DEALLOCATE(ak)DEALLOCATE(bk)DEALLOCATE(press)!DEALLOCATE(q_in)DEALLOCATE(pt_in)DEALLOCATE(pv_in)DEALLOCATE(q)DEALLOCATE(pt)DEALLOCATE(pv)DEALLOCATE(label)DEALLOCATE(label_out)DEALLOCATE(fold)DEALLOCATE(sfold)DEALLOCATE(mfold)DEALLOCATE(dfold)DEALLOCATE(tp)DEALLOCATE(dp)DEALLOCATE(pmin)DEALLOCATE(pmax)CONTAINS! ------------------------------------------------------------------------SUBROUTINE USAGE(EXE)CHARACTER (LEN=*) :: EXEWRITE(*,*) '--------------------------------------------'WRITE(*,*) '3d_labelling_and_fold_id, version: ',VERSIONWRITE(*,*) 'Authors: Andrea Pozzer, MPICH 'WRITE(*,*) ' Bojan Skerlak, ETH 'WRITE(*,*) ' Michael Sprenger, ETH 'WRITE(*,*) '--------------------------------------------'WRITE(*,*) 'Usage: '//TRIM(EXE)//' <namelist-file>'WRITE(*,*) 'See README.txt or EMAC.nml for example'WRITE(*,*) '--------------------------------------------'END SUBROUTINE USAGE! ------------------------------------------------------------------------! ------------------------------------------------------------------------SUBROUTINE read_nml(status, iou, fname)IMPLICIT NONE! I/OINTEGER, INTENT(OUT) :: statusINTEGER, INTENT(IN) :: iouCHARACTER(LEN=*), INTENT(IN) :: fname! LOCALCHARACTER(LEN=*), PARAMETER :: substr = 'read_nml'LOGICAL :: lex ! file existsINTEGER :: fstat ! file statusNAMELIST /CTRL/ file_input,file_output,X_name,Y_name,Z_name,T_name, &APS_name,HYAM_name,HYBM_name,Q_name,PV_name,PT_namestatus = 1 ! ERRORWRITE(*,*) '==========================================================='! CHECK IF FILE EXISTSINQUIRE(file=TRIM(fname), exist=lex)IF (.NOT.lex) THENWRITE(*,*) substr,': FILE DOES NOT EXIST (',TRIM(fname),')'status = 1RETURNEND IF! OPEN FILEOPEN(iou,file=TRIM(fname))! READ NEMELISTWRITE(*,*) 'READING NAMELIST ''CTRL'''//&&' FROM '''//TRIM(fname),''' (unit ',iou,') ...'!READ(iou, NML=CTRL, IOSTAT=fstat)!IF (fstat /= 0) THENWRITE(*,*) substr,': READ ERROR IN NAMELIST ''CTRL'' (',TRIM(fname),')'status = 3 ! READ ERROR IN NAMELISTRETURNEND IFWRITE(*,*) ' FILE INPUT : ', TRIM(file_input)WRITE(*,*) ' FILE OUTPUT : ', TRIM(file_output)WRITE(*,*) ' X_NAME : ', TRIM(x_name)WRITE(*,*) ' Y_NAME : ', TRIM(y_name)WRITE(*,*) ' Z_NAME : ', TRIM(z_name)WRITE(*,*) ' T_NAME : ', TRIM(t_name)WRITE(*,*) ' APS_NAME : ', TRIM(aps_name)WRITE(*,*) ' HYAM_NAME : ', TRIM(hyam_name)WRITE(*,*) ' HYBM_NAME : ', TRIM(hybm_name)WRITE(*,*) ' Q_NAME : ', TRIM(q_name)WRITE(*,*) ' PV_NAME : ', TRIM(pv_name)WRITE(*,*) ' PT_NAME : ', TRIM(pt_name)! CLOSE FILECLOSE(iou)WRITE(*,*) '==========================================================='status = 0END SUBROUTINE read_nml! ------------------------------------------------------------------------end program clustering! *******************************************************************************! * Subroutine Section *! *******************************************************************************! --------------------------------------------------------! Get grid points which are connected to a grid point: BOJAN: this is the 3D-connectedness criterion of the 3D-labelling algorithm! --------------------------------------------------------SUBROUTINE connect (outar,label,inx,iny,inz,dirh,dird,diru,nx,ny,nz)! The input array <outar(nx,ny,nz)> is 0/1 labeled. Given a starting point! <inx,iny,inz> where the label is 1, find all points with label 1 which are! connected and attribute to all these points the label <label>.! The 3D arrays <dir*(nx,ny,nz)> specifie whether an expansion is allowed! dirh=1 where horizontal propagation is allowed,! dird=1 where vertical propagation is allowed downward,! diru=1 where vertical propagation is allowed upward, and! a value of dir*=0 prohibits the respective propagation! Declaration of subroutine parametersinteger :: nx,ny,nzinteger :: inx,iny,inzinteger :: outar(nx,ny,nz)integer :: labelinteger :: dirh(nx,ny,nz)integer :: diru(nx,ny,nz)integer :: dird(nx,ny,nz)! Auxiliary variablesinteger :: il,ir,jb,jf,ku,kd,im,jm,kminteger :: i,j,kinteger :: stackinteger :: indx(nx*ny*nz),indy(nx*ny*nz),indz(nx*ny*nz)! Push the starting elements on the stackstack=1indx(stack)=inxindy(stack)=inyindz(stack)=inzoutar(inx,iny,inz)=label! Define the indices of the neighbouring elements100 continueil=indx(stack)-1if (il.lt.1) il=nxir=indx(stack)+1if (ir.gt.nx) ir=1jb=indy(stack)-1if (jb.lt.1) jb=1jf=indy(stack)+1if (jf.gt.ny) jf=nyku=indz(stack)+1if (ku.gt.nz) ku=nzkd=indz(stack)-1if (kd.lt.1) kd=1im=indx(stack)jm=indy(stack)km=indz(stack)stack=stack-1! Check for index overflowif (stack.ge.(nx*ny*nz-nx)) thenprint*,'Stack overflow while clustering...'stopendif! Mark all connected elements (build up the stack)! Mark level below/above if dird/diru and dirh allowed! BOJAN: U=up, D=down, R=right, L=left, M=mid, F=front, B=back (positions in a 27-element cube of nearest neighbours)if ((dirh(im,jm,km).eq.1).and.(dird(im,jm,km).eq.1)) then! below:if ( outar(il,jf,kd).eq.1) thenoutar(il,jf,kd)=labelstack=stack+1indx(stack)=ilindy(stack)=jfindz(stack)=kdendifif ( outar(im,jf,kd).eq.1) thenoutar(im,jf,kd)=labelstack=stack+1indx(stack)=imindy(stack)=jfindz(stack)=kdendifif ( outar(ir,jf,kd).eq.1) thenoutar(ir,jf,kd)=labelstack=stack+1indx(stack)=irindy(stack)=jfindz(stack)=kdendifif (outar(il,jm,kd).eq.1) thenoutar(il,jm,kd)=labelstack=stack+1indx(stack)=ilindy(stack)=jmindz(stack)=kdendifif (outar(ir,jm,kd).eq.1) thenoutar(ir,jm,kd)=labelstack=stack+1indx(stack)=irindy(stack)=jmindz(stack)=kdendifif (outar(il,jb,kd).eq.1) thenoutar(il,jb,kd)=labelstack=stack+1indx(stack)=ilindy(stack)=jbindz(stack)=kdendifif (outar(im,jb,kd).eq.1) thenoutar(im,jb,kd)=labelstack=stack+1indx(stack)=imindy(stack)=jbindz(stack)=kdendifif (outar(ir,jb,kd).eq.1) thenoutar(ir,jb,kd)=labelstack=stack+1indx(stack)=irindy(stack)=jbindz(stack)=kdendifendifif ((dirh(im,jm,km).eq.1).and.(diru(im,jm,km).eq.1)) thenif (outar(il,jf,ku).eq.1) thenoutar(il,jf,ku)=labelstack=stack+1indx(stack)=ilindy(stack)=jfindz(stack)=kuendifif (outar(im,jf,ku).eq.1) thenoutar(im,jf,ku)=labelstack=stack+1indx(stack)=imindy(stack)=jfindz(stack)=kuendifif (outar(ir,jf,ku).eq.1) thenoutar(ir,jf,ku)=labelstack=stack+1indx(stack)=irindy(stack)=jfindz(stack)=kuendifif (outar(il,jm,ku).eq.1) thenoutar(il,jm,ku)=labelstack=stack+1indx(stack)=ilindy(stack)=jmindz(stack)=kuendifif (outar(ir,jm,ku).eq.1) thenoutar(ir,jm,ku)=labelstack=stack+1indx(stack)=irindy(stack)=jmindz(stack)=kuendifif (outar(il,jb,ku).eq.1) thenoutar(il,jb,ku)=labelstack=stack+1indx(stack)=ilindy(stack)=jbindz(stack)=kuendifif (outar(im,jb,ku).eq.1) thenoutar(im,jb,ku)=labelstack=stack+1indx(stack)=imindy(stack)=jbindz(stack)=kuendifif (outar(ir,jb,ku).eq.1) thenoutar(ir,jb,ku)=labelstack=stack+1indx(stack)=irindy(stack)=jbindz(stack)=kuendifendif! Mark level directly below and above if allowed (dird/u=1)if (dird(im,jm,km).eq.1) thenif (outar(im,jm,kd).eq.1) thenoutar(im,jm,kd)=labelstack=stack+1indx(stack)=imindy(stack)=jmindz(stack)=kdendifendifif (diru(im,jm,km).eq.1) thenif (outar(im,jm,ku).eq.1) thenoutar(im,jm,ku)=labelstack=stack+1indx(stack)=imindy(stack)=jmindz(stack)=kuendifendif! Mark other points on same level if allowed (dirh=1)if (dirh(im,jm,km).eq.1) thenif (outar(il,jf,km).eq.1) thenoutar(il,jf,km)=labelstack=stack+1indx(stack)=ilindy(stack)=jfindz(stack)=kmendifif (outar(im,jf,km).eq.1) thenoutar(im,jf,km)=labelstack=stack+1indx(stack)=imindy(stack)=jfindz(stack)=kmendifif (outar(ir,jf,km).eq.1) thenoutar(ir,jf,km)=labelstack=stack+1indx(stack)=irindy(stack)=jfindz(stack)=kmendifif (outar(il,jm,km).eq.1) thenoutar(il,jm,km)=labelstack=stack+1indx(stack)=ilindy(stack)=jmindz(stack)=kmendifif (outar(ir,jm,km).eq.1) thenoutar(ir,jm,km)=labelstack=stack+1indx(stack)=irindy(stack)=jmindz(stack)=kmendifif (outar(il,jb,km).eq.1) thenoutar(il,jb,km)=labelstack=stack+1indx(stack)=ilindy(stack)=jbindz(stack)=kmendifif (outar(im,jb,km).eq.1) thenoutar(im,jb,km)=labelstack=stack+1indx(stack)=imindy(stack)=jbindz(stack)=kmendifif (outar(ir,jb,km).eq.1) thenoutar(ir,jb,km)=labelstack=stack+1indx(stack)=irindy(stack)=jbindz(stack)=kmendifendifif (stack.gt.0) goto 100! Exit point700 continuereturnend subroutine connect! ---------------------------------------------------------! Calculate the height of the tropopause: BOJAN: although this subroutine is called tropopause for historical reasons (long story), it actually is the core of the 3D-labelling algorithm :-)! ---------------------------------------------------------SUBROUTINE tropopause (f2,f3,p3,pv3,th3,q3,mdv,xlat,nx,ny,nz,tropo_pv,tropo_th,q_th)! Get the pressure height of the tropopause (f2) and! cluster the atmosphere into labels 1-5 (f3).! Given: 3d field: Pressure <p3(nx,ny,nz)>,! potential vorticity <pv3(nx,ny,nz)>, and potential! temperature <th3(nx,ny,nz)>. The parameters <nx,ny,nz>! characterize the grid. The missing data value is <mdv>.! Bojan July 2013: tropo_pv and tropo_th have to be specified!implicit none! Declaration of subroutine parametersinteger,intent(in) :: nx,ny,nzreal,intent(out) :: f3(nx,ny,nz)real,intent(out) :: f2(nx,ny)real,intent(in) :: p3(nx,ny,nz)real,intent(in) :: q3(nx,ny,nz)real,intent(inout) :: pv3(nx,ny,nz)real,intent(in) :: th3(nx,ny,nz)real,intent(in) :: xlat(ny)integer :: dirsh(nx,ny,nz),dirsd(nx,ny,nz),dirsu(nx,ny,nz)integer :: dirth(nx,ny,nz),dirtd(nx,ny,nz),dirtu(nx,ny,nz)integer :: dirdh(nx,ny,nz),dirdd(nx,ny,nz),dirdu(nx,ny,nz)integer :: dirfh(nx,ny,nz),dirfd(nx,ny,nz),dirfu(nx,ny,nz)real :: mdv! Set tropopause thresholdsreal :: tropo_pv,tropo_th! diabatically produced PV (Q-threshold), Feb 2014:real :: q_th! Set permission for expansion of stratospheric label. A horizontal! expansion is forbidden if less than <forbid_h> of the grid column! below a grid point is troposphericreal :: forbid_hparameter (forbid_h=0.5)! Internal variablesinteger :: i,j,k,lreal :: pv2(nx,ny)real :: th2(nx,ny)real :: pvn(nx,ny),pvs(nx,ny)real :: latinteger :: st(nx,ny,nz),tr(nx,ny,nz),de(nx,ny,nz),fi(nx,ny,nz)real :: signreal :: lev(nx,ny)integer :: tschk,kabove,kbelow,vertpvchkreal :: below, total! ----- STEP 1a ----- BOJAN: these steps should be the same as described in the appendix of my PhD thesis! Mark all points above 50 hPa as stratosphericdo i=1,nxdo j=1,nydo k=1,nzif (xlat(j).ge.0.) thensign=1.elsesign=-1.endifif (p3(i,j,k).lt.50.) thenpv3(i,j,k)=sign*1.5*tropo_pvendifenddoenddoenddo! Mark points with PV > PV threshold (e.g. 2 pvu) OR TH > TH threshold (e.g. 380 K)do i=1,nxdo j=1,nydo k=1,nzif ((abs(pv3(i,j,k)).ge.tropo_pv).or.(th3(i,j,k).ge.tropo_th)) thenst(i,j,k)=1 ! 'stratosphere'tr(i,j,k)=0 ! 'tropopsphere'de(i,j,k)=1 ! additional array for de-treatmentelsest(i,j,k)=0tr(i,j,k)=1de(i,j,k)=0endifenddoenddoenddo! ----- STEP 1b -----! Set the expansion permissions for the individual labels (1=allowed, 0=forbidden)! h=horizontal, v=verticaldo i=1,nxdo j=1,nydo k=1,nz! Set permissions for stratospheric (st) label: always vertical, never horizontaldirsh(i,j,k) = 0dirsd(i,j,k) = 1dirsu(i,j,k) = 1! Set permissions for fill up thing label (fi): always horizontal, only downward, never upwarddirfh(i,j,k) = 1dirfd(i,j,k) = 1dirfu(i,j,k) = 0! Set permissions for tropospheric label (can always propagate)dirth(i,j,k) = 1dirtd(i,j,k) = 1dirtu(i,j,k) = 1! Set permissions for deep fold (de) label: always horizontal, vertical only if air is drydirdh(i,j,k) = 1! Allow vertical propagation of de label only for dry air (Q < Q-threshold)if ( q3(i,j,k).le.q_th ) thendirdd(i,j,k) = 1dirdu(i,j,k) = 1elsedirdd(i,j,k) = 0dirdu(i,j,k) = 0endif!endifenddoenddoenddo! ----- STEP 2a -----do i=1,nxdo j=1,ny! Determine stratosphere by connectivity criterion: for all points (x/y) at highest! model level with st=1, find all connected points with st=1 => they get label st=2! this label can only propagate vertically and it is important that this happens before! label st=5 can propagate from the bottom and overwrite st=1 with st=5if ((st(i,j,nz).eq.1).and.(p3(i,j,nz).lt.500.)) thencall connect (st,2,i,j,nz,dirsh,dirsd,dirsu,nx,ny,nz)endifenddoenddodo i=1,nxdo j=1,ny! ----- STEP 2b -----! for de array, do the same thing. The big difference is that the horizontal propagation is! always allowed. In the vertical direction, it is only allowed if RH<50%, ensuring that! very deep tropopause folds, which are not connected by st are reached by de.! This is needed because for some cases, stratospheric air (e.g. at the boundary of a deep-reaching! streamer) is not identified as such (the label st=2 is not allowed to propagate! horizontally). The de=2 label can always propagate horizontally and is in the end! used to distinguish 'real' cutoffs (with st=1 and de=1) from! deep reaching stratospheric air (with st=1 and de=2)if ((de(i,j,nz).eq.1).and.(p3(i,j,nz).lt.500.)) thencall connect (de,2,i,j,nz,dirdh,dirdd,dirdu,nx,ny,nz)endif! ----- STEP 2c -----! Determine troposphere analogeously: for all points (x/y) at lowest model! level with tr=1 => all connected points get label tr=2if ((tr(i,j,1).eq.1).and.(p3(i,j,1).gt.300.)) thencall connect (tr,2,i,j,1,dirth,dirtd,dirtu,nx,ny,nz)endif! ----- STEP 2d -----! Determine surface-bound PV blobs (|PV|>2): for all points (x/y) at lowest! model level with st=1 => all connected points get label st=5! This label can always propagate! (use dirth,dirtv)if ((st(i,j,1).eq.1).and.(p3(i,j,1).gt.300.)) thencall connect (st,5,i,j,1,dirth,dirtd,dirtu,nx,ny,nz)endifenddoenddo! ----- STEP 2e -----! Remove tropospheric blobs in surface-bound PV blobs (tr=1 surrounded by st=5)do i=1,nxdo j=1,nydo k=1,nzif (tr(i,j,k).eq.1) thentschk=0do l=k,nzif (tschk.eq.0) then! Check if air above is stratosphere (st=2) or top of atmosphere (nz)if ( (st(i,j,l).eq.2) .or. (l.eq.nz) ) thentschk=2! Check if air above is surface-bound PV blob (st=5)elseif (st(i,j,l).eq.5) thentschk=5endifendifenddo! Mark this blob as tr=5if (tschk.eq.5) thencall connect (tr,5,i,j,k,dirth,dirtd,dirtu,nx,ny,nz)endifendifenddoenddoenddo! ----- STEP 3 -----! --------------------------------------------------------------------------------------------------! Set the stratospheric and troposhperic labels (commented out version is 1=strat/0=everything else)! --------------------------------------------------------------------------------------------------do i=1,nxdo j=1,nydo k=1,nzf3(i,j,k)=0.if (th3(i,j,k).gt.tropo_th) thenf3(i,j,k) = 2. ! stratosphere TH (2)! f3(i,j,k) = 1. ! stratosphere TH (1)else if (st(i,j,k).eq.2) thenf3(i,j,k) = 2. ! stratosphere PV (2)! f3(i,j,k) = 1. ! stratosphere PV (1)else if (tr(i,j,k).eq.2) thenf3(i,j,k) = 1. ! troposphere (1)! f3(i,j,k) = 0. ! troposphere (0)else if ((st(i,j,k).eq.1).and.(de(i,j,k).eq.1)) thenf3(i,j,k) = 3. ! strat cutoff (3)! f3(i,j,k) = 0. ! strat cutoff (0)else if ((st(i,j,k).eq.1).and.(de(i,j,k).eq.2)) thenf3(i,j,k) = 2. ! deep reaching stratospheric air (2)! f3(i,j,k) = 1. ! deep reaching stratospheric air (1)else if (tr(i,j,k).eq.1) thenf3(i,j,k) = 4. ! trop cutoff (4)! f3(i,j,k) = 0. ! trop cutoff (0)else if (st(i,j,k).eq.5) thenf3(i,j,k) = 5. ! surface-bound PV blob (5)! f3(i,j,k) = 0. ! surface-bound PV blob (0)else if (tr(i,j,k).eq.5) thenf3(i,j,k) = 1. ! tropospheric air within surface-bound PV blob (1)! f3(i,j,k) = 0. ! tropospheric air within surface-bound PV blob (0)endifenddoenddoenddo! --------------------------------------------------------------------------------------------------! --------------------------------------------------------------------------------------------------! ----- STEP 6 -----do i=1,nxdo j=1,nydo k=1,nzfi(i,j,k)=0if (( f3(i,j,k).eq.2 ) .or. ( f3(i,j,k).eq.3 ).or.( f3(i,j,k).eq.5 )) thenfi(i,j,k)=1 ! mark point as possible horizontal fill-up pointendifend doend doend do! Horizontally fill up 2/3 and 2/5 patches (only aestetically important ;-))do i=1,nxdo j=1,nydo k=1,nzif ( f3(i,j,k).eq.3 ) then ! label 3call connect (fi,3,i,j,k,dirfh,dirfd,dirfu,nx,ny,nz)endifif ( f3(i,j,k).eq.5 ) then ! label 3call connect (fi,5,i,j,k,dirfh,dirfd,dirfu,nx,ny,nz)endifenddoenddoenddodo i=1,nxdo j=1,nydo k=1,nzif ( fi(i,j,k).eq.3 ) then ! label 3 that was horizontally filled upf3(i,j,k)=3.do l=1,k-1 ! vertically fill up points below if label is 2if ( f3(i,j,l).eq.2 ) thenf3(i,j,l)=3.end ifend doendifif ( fi(i,j,k).eq.5 ) then ! label 5 that was horizontally filled upf3(i,j,k)=5.endifenddoenddoenddo! CHECK that all points obtained a label!do i=1,nxdo j=1,nydo k=1,nzif ( f3(i,j,k).eq.0 ) then ! no label assignedprint*,'Error: no label assigned'print*,'i,j,k = ',i,j,kprint*,'p3(i,j,k) = ',p3(i,j,k)print*,'st(i,j,k) = ',st(i,j,k)print*,'tr(i,j,k) = ',tr(i,j,k)print*,'de(i,j,k) = ',de(i,j,k)stopendifenddoenddoenddo! --------------------------------------------------------------------------------------------------! DONE with assigning labels, now interpolate field to tropopause pressure! --------------------------------------------------------------------------------------------------! BOJAN: here, we calculate the height of the tropopause (folds are ignored, see PhD thesis)! Remove stratospheric blobs in troposphere and tropospheric blobs in stratosphere! Brute force: Change PV values above and below tropo thresholddo i=1,nxdo j=1,nydo k=1,nzif (xlat(j).ge.0.) thensign=1.elsesign=-1.endif! Set PV of tropo. cutoff in stratosphereif ( (st(i,j,k).eq.0).and.(tr(i,j,k).eq.1) ) thenpv3(i,j,k)=sign*1.5*tropo_pv! Set PV of exotic cases where PV < -2 pvu (PV > +2 pvu) in NH (SH) => would otherwise be detected as TPelse if ( ((sign .gt. 0.).and.(pv3(i,j,k).lt.-tropo_pv).or.(sign .lt. 0.).and.(pv3(i,j,k).gt.tropo_pv)).and.(th3(i,j,k).gt.tropo_pv) ) thenpv3(i,j,k)=sign*1.5*tropo_pv! Set PV of strat. cutoff in trop. or surface bound PV (only below 380K)!else if ( (((st(i,j,k).eq.1).and.(de(i,j,k).eq.1)).or.(st(i,j,k).eq.5)).and.(th3(i,j,k).le.tropo_th) ) thenelse if ( (f3(i,j,k).eq.3).or.(f3(i,j,k).eq.5) ) thenpv3(i,j,k)=sign*0.5*tropo_pvendifenddoenddoenddo! Calculate the height of the dynamical tropopause (NH:2pvu, SH:-2pvu)do i=1,nxdo j=1,nylev(i,j)=tropo_pvenddoenddocall thipo_lev(p3,pv3,lev,pvn,nx,ny,nz,mdv,0) ! for NHdo i=1,nxdo j=1,nydo k=1,nzpv3(i,j,k)=-pv3(i,j,k) ! change sign of PVenddoenddoenddocall thipo_lev(p3,pv3,lev,pvs,nx,ny,nz,mdv,0) ! for SHdo i=1,nxdo j=1,nydo k=1,nzpv3(i,j,k)=-pv3(i,j,k) ! change sign of PV backenddoenddoenddodo j=1,nydo i=1,nxif (xlat(j).ge.0.) thenpv2(i,j)=pvn(i,j) ! NHelsepv2(i,j)=pvs(i,j) ! SHendifenddoenddo! Special treatment for PV towers (in whole column, |PV|>2)do i=1,nxdo j=1,nyif (pv2(i,j).eq.mdv) thenvertpvchk=0do k=1,nzif (abs(pv3(i,j,k)).ge.tropo_pv) vertpvchk=vertpvchk+1enddoif(vertpvchk.eq.nz) then!print*,'PV tower! Tropopause reaches ground: ',p3(i,j,1)pv2(i,j)=p3(i,j,1)endifendifenddoenddo! Calculate the height of the 380 K surfacedo i=1,nxdo j=1,nylev(i,j)=tropo_thenddoenddocall thipo_lev(p3,th3,lev,th2,nx,ny,nz,mdv,0)! Set unreasonable values to mdvdo i=1,nxdo j=1,nyif ((th2(i,j).lt.40.).or.(th2(i,j).gt.800.)) then ! Out of 'regular' limits => check labelif (th2(i,j).ne.mdv) then!print*,'Warning: p(TP) (TH) out of limits:',th2(i,j)!print*,'i,j = ',i,j!print*,'LABEL profile:',f3(i,j,:)do k=1,nzif (p3(i,j,k).ge.th2(i,j)) thenkbelow=kendifif (p3(i,j,nz+1-k).le.th2(i,j)) thenkabove=nz+1-kendifenddoif ((f3(i,j,kbelow).eq.1).and.(f3(i,j,kabove).eq.2)) then!print*,'Point is okay!'else!print*,'Point is not okay => set to mdv'th2(i,j)=mdvendifendifendifif ((pv2(i,j).lt.40.).or.(pv2(i,j).gt.800.)) then ! Out of 'regular' limits => check labelif (pv2(i,j).ne.mdv) then!print*,'Warning: p(TP) (PV) out of limits:',pv2(i,j)!print*,'i,j = ',i,j!print*,'LABEL profile:',f3(i,j,:)do k=1,nzif (p3(i,j,k).ge.pv2(i,j)) thenkbelow=kendifif (p3(i,j,nz+1-k).le.pv2(i,j)) thenkabove=nz+1-kendifenddoif ((f3(i,j,kbelow).eq.1).and.(f3(i,j,kabove).eq.2)) then!print*,'Point is okay!'elseif ((kabove.eq.1).and.(kbelow.eq.1)) then!print*,'PV tower! Tropopause reaches ground: ',pv2(i,j)else!print*,'Point is not okay => set to mdv'pv2(i,j)=mdvendifendifendifenddoenddo! --------------------------------------------------------------------------------------------------! Set the resulting tropopause height (maximum pressure: 380 K, 2 PVU)! --------------------------------------------------------------------------------------------------do i=1,nxdo j=1,nyif ((th2(i,j).ne.mdv).and.(pv2(i,j).eq.mdv)) thenf2(i,j)=th2(i,j)else if ((th2(i,j).eq.mdv).and.(pv2(i,j).ne.mdv)) thenf2(i,j)=pv2(i,j)else if (th2(i,j).gt.pv2(i,j)) thenf2(i,j)=th2(i,j)elsef2(i,j)=pv2(i,j)endifenddoenddo! --------------------------------------------------------------------------------------------------! DONE! --------------------------------------------------------------------------------------------------end subroutine tropopauseSUBROUTINE tropofold (label3,p3,pv3,th3,fold,dp, pmin, pmax, sfold,mfold,dfold,mdv,nx,ny,nz,tropo_pv,tropo_th)! -------------------------------------------------------------------------------! Part 2) Identify tropopause folds (multiple crossings of the tropopause in one column)! -------------------------------------------------------------------------------! Given the label field, determine whether a tropopause fold is present.! The parameters <nx,ny,nx> characterize the grid. The missing data value is <mdv>.implicit none! Declaration of subroutine parametersinteger :: nx,ny,nzreal :: mdvreal :: label3(nx,ny,nz)real :: p3(nx,ny,nz)real :: pv3(nx,ny,nz)real :: th3(nx,ny,nz)real :: pre0,pre1,pre2,ok1,ok2real :: tropo_pv,tropo_threal :: fold(nx,ny)real :: dp(nx,ny)real :: pmin(nx,ny)real :: pmax(nx,ny)real :: sfold(nx,ny)real :: mfold(nx,ny)real :: dfold(nx,ny)! Aux integersinteger :: i,j,k,l,k0,k1,k2,ncross! Adjust fields (integer labels, absolute value of PV)do i=1,nxdo j=1,nydo k=1,nzpv3(i,j,k) = abs(pv3(i,j,k))enddoenddoenddo! Definition of labels! 1 : troposphere! 2 : stratosphere! 3 : stratosperic cutoff! 4 : tropospheric cutoff! 5 : surface-bound PV blobsdo i=1,nxdo j=1,nyk0 = 0k1 = 0k2 = 0ncross = 0do k=nz-1,1,-1! strat-trop transitionif ( (label3(i,j,k+1).eq.2).and.(label3(i,j,k).eq.1) ) thenncross = ncross + 1if ( k2.eq.0 ) thenk2 = kelseif ( k1.ne.0 ) thenk0 = kendifendif! special case for folds that are affected by the q-criterion (q<0.0001 only)! example (top to bottom): 222211122231111 need to recognize the 2-3-1 transition! transitions like 222222223111 should not be recognized, thus only if k2.ne.0if ( (label3(i,j,k+1).eq.2).and.(label3(i,j,k).eq.3) ) thenncross = ncross + 1if (( k2.ne.0 ) .and. ( k1.ne.0 )) thenk0 = kendifendif! trop-strat transitionif ( (label3(i,j,k+1).eq.1).and.(label3(i,j,k).eq.2) ) thenncross = ncross + 1if ( ( k2.ne.0 ).and.( k1.eq.0 ) ) thenk1 = kendifendifenddo! Skip further steps if we have a single tropopauseif ( ncross.le.2 ) goto 100! Get the exact pressures at the crossingspre0 = 0.pre1 = 0.pre2 = 0.! lowest point (0) => pre0ok1 = ( pv3(i,j,k0+1)-tropo_pv ) * ( pv3(i,j,k0)-tropo_pv )ok2 = ( th3(i,j,k0+1)-tropo_th ) * ( th3(i,j,k0)-tropo_th )if ( ok1.le.0. ) thenpre0 = p3(i,j,k0) + ( p3(i,j,k0+1) - p3(i,j,k0) ) * &( tropo_pv - pv3(i,j,k0) )/( pv3(i,j,k0+1) - pv3(i,j,k0) )elseif ( ok2.le.0. ) thenpre0 = p3(i,j,k0) + ( p3(i,j,k0+1) - p3(i,j,k0) ) * &( tropo_th - th3(i,j,k0) )/( th3(i,j,k0+1) - th3(i,j,k0) )endif! middle point (1) => pre1ok1 = ( pv3(i,j,k1+1)-tropo_pv ) * ( pv3(i,j,k1)-tropo_pv )ok2 = ( th3(i,j,k1+1)-tropo_th ) * ( th3(i,j,k1)-tropo_th )if ( ok1.le.0. ) thenpre1 = p3(i,j,k1) + ( p3(i,j,k1+1) - p3(i,j,k1) ) * &( tropo_pv - pv3(i,j,k1) )/( pv3(i,j,k1+1) - pv3(i,j,k1) )elseif ( ok2.le.0. ) thenpre1 = p3(i,j,k1) + ( p3(i,j,k1+1) - p3(i,j,k1) ) * &( tropo_th - th3(i,j,k1) )/( th3(i,j,k1+1) - th3(i,j,k1) )endif! upper point (2) => pre2ok1 = ( pv3(i,j,k2+1)-tropo_pv ) * ( pv3(i,j,k2)-tropo_pv )ok2 = ( th3(i,j,k2+1)-tropo_th ) * ( th3(i,j,k2)-tropo_th )if ( ok1.le.0. ) thenpre2 = p3(i,j,k2) + ( p3(i,j,k2+1) - p3(i,j,k2) ) * &( tropo_pv - pv3(i,j,k2) )/( pv3(i,j,k2+1) - pv3(i,j,k2) )elseif ( ok2.le.0. ) thenpre2 = p3(i,j,k2) + ( p3(i,j,k2+1) - p3(i,j,k2) ) * &( tropo_th - th3(i,j,k2) )/( th3(i,j,k2+1) - th3(i,j,k2) )endif! Decide whether all pressure values are okif ( ( pre0.lt.p3(i,j,k0+1) ).or. &( pre0.gt.p3(i,j,k0 ) ).or. &( pre1.lt.p3(i,j,k1+1) ).or. &( pre1.gt.p3(i,j,k1 ) ).or. &( pre2.lt.p3(i,j,k2+1) ).or. &( pre2.gt.p3(i,j,k2 ) ) ) goto 100! Everything is fine: remember the foldfold(i,j) = 1.dp(i,j) = pre1 - pre2pmin(i,j) = pre2pmax(i,j) = pre0! Exit point for loop100 continueenddo ! yenddo ! xwhere((fold .gt. 0.0) .and. (dp .ge. 50.0) .and. (dp .lt. 200.0))sfold=1.0elsewhere((fold .gt. 0.0) .and. (dp .ge. 200.0) .and. (dp .lt. 350.0))mfold=1.0elsewhere((fold .gt. 0.0) .and. (dp .ge. 350.0))dfold=1.0end whereend subroutine tropofold! -----------------------------------------------------------! Interpolation onto theta surface (top -> down): BOJAN: not sure you have all the interpolation subroutines used here, contact us or feel free to use any other interpolation routine that does the job :-)! -----------------------------------------------------------subroutine thipo_lev(var3d,th3d,lev,var,nx,ny,nz,mdv,mode)! Interpolates the 3d variable var3d on the isentropic surface! defined by lev. The interpolated field is returned as var.! th3d denotes the 3d theta array.! mode determines the way of vertical interpolation:! mode=0 is for linear interpolation! mode=1 is for logarithmic interpolationinteger :: nx,ny,nz,modereal :: lev(nx,ny),mdvreal :: var3d(nx,ny,nz),th3d(nx,ny,nz),var(nx,ny)integer :: i,j,kreal :: kindreal :: int3dmdo i=1,nxdo j=1,nyif (lev(i,j).ne.mdv) thenkind=0.do k=nz-1,1,-1if ((th3d(i,j,k).le.lev(i,j)).and.(th3d(i,j,k+1).ge.lev(i,j))) thenkind=float(k)+(th3d(i,j,k)-lev(i,j))/(th3d(i,j,k)-th3d(i,j,k+1))goto 100endifenddo100 continueif (kind.eq.0) thenvar(i,j)=mdvelsevar(i,j)=int3dm(var3d,nx,ny,nz,float(i),float(j),kind,mdv)endifelsevar(i,j)=mdvendifenddoenddoreturnend subroutine thipo_lev! -----------------------------------------------------------! Interpolation onto theta surface (bottom -> up)! -----------------------------------------------------------subroutine pipo_lev(var3d,p3d,lev,var,nx,ny,nz,mdv,mode)! Interpolates the 3d variable var3d on the pressure surface! defined by lev. The interpolated field is returned as var.! p3d denotes the 3d pressure array.! mode determines the way of vertical interpolation:! mode=0 is for linear interpolation! mode=1 is for logarithmic interpolationinteger :: nx,ny,nz,modereal :: lev(nx,ny),mdvreal :: var3d(nx,ny,nz),p3d(nx,ny,nz),var(nx,ny)integer :: i,j,kreal :: kindreal :: int3dmdo i=1,nxdo j=1,nyif (lev(i,j).ne.mdv) thenkind=0.do k=1,nz-1if ((p3d(i,j,k).ge.lev(i,j)).and.(p3d(i,j,k+1).le.lev(i,j))) thenkind=float(k)+(p3d(i,j,k)-lev(i,j))/(p3d(i,j,k)-p3d(i,j,k+1))goto 100endifenddo100 continueif (kind.eq.0.) thenvar(i,j)=mdvelsevar(i,j)=int3dm(var3d,nx,ny,nz,float(i),float(j),kind,mdv)endifelsevar(i,j)=mdvendifenddoenddoreturnend subroutine pipo_lev! ------------------------------------------------------------------! Auxiliary routines! ------------------------------------------------------------------