Blame | Last modification | View Log | Download | RSS feed
program contrack
! ----------------------------------------------------------------------
!
! Date: 1st January 2011
!
!
! ------->> Purpose: Spatial and temporal contour tracking programm
!
! This programm is a completely re-written new version of the tracking
! programm used for the studies in e.g. Schwierz et al. (2005),
! Croci-Maspoli et al. (2007a,b), Croci-Maspoli et al. (2009).
!
! ------->> What is necessary to compile contrack?
! - contrack has been compiled with gfortran
! (included in the GCC family, http://gcc.gnu.org/ ). Feel free
! to test other fortran compilers.
! - contrack requires the netcdf-4.0.1 library (also compiled with gfortran).
! - contrack comes with a simple make-file (you have to adjust this file
! before compiling).
!
! ------->> What netCDF-file is necessary as input?
!
! - a single CF-netCDF-file (http://cf-pcmdi.llnl.gov/) including all required
! time steps.
! - a longitude, latitude and time dimension.
! - contrack does not calculate a climatological anomaly field. This needs to
! be calculated before running contrack.
!
! ------->> How does the output file look like?
!
! - the output CF-netCDF file has the same dimensions as the input netCDF file
! - the output has a binary format (0 and 1), whereas 1 captures regions of
! the blocks.
!
! ------->> Known issues or features not implemented yet in contrack 0.8:
!
! 1) The calculation and saving of common statistical blocking features
! (e.g. blocking size, blocking length, blocking path, ...) is not
! implemented yet. My intention is to save these information directly
! on the netCDF file.
! 2) Only two-dimensional netCDF-files can be used as input file (longitude,
! latitude and time). The code can not handle a third dimension (e.g.
! vertical) on the same netCDF-file yet.
! 3) The asymmetry of the blocking features is not implemented yet.
! 3) ... more to come!
!
! ------->> Version control
! Version 0.1-0.8: May 2010 - fortran 90 compatible
! - CF-netCDF compatible (instead of IVE-netCDF)
! - new tracking algorithm
! Oct 2011 - started with handling also different vertical
! levels but not finished yet.
! Jan 2011 - added these explanations
!
! If you have any comments please send them to mischa.croci-maspoli@alumni.ethz.ch.
!
! This programm code comes with no guarantee and is currently still
! under construction. Be aware that no extensive testing and comparison to the
! old code has been performed yet.
!
! Copyright: mischa.croci-maspoli@alumni.ethz.ch
!
! Modifications SP: omit first and last n time steps in output,
! where n=persistence;
! comment out allocation etc. related to vertical dimension
!
! ----------------------------------------------------------------------
USE netcdf
implicit none
integer, external :: iargc
integer :: i,j,t,k,ii,jj,tt,counter
integer :: persistence, verbose, calcmeranom, cont
integer :: titleLength, commentLength, sourceLength, institLength
integer :: errmsg, outarr
integer :: nrcontours
integer :: ttbegin, ttend
real :: fraction, areacon, areaover, overlap, threshold
character*(100) infilename,outfilename,arg
character*(100) outvarname,outstandvarname, outvarunit
character*(100) invarname, inlat, inlon, intime, inver
character*(100) contrackversion
character*(5) gorl
character*(1000) cfcomment, cftitle, cfsource, cfinstit
character*(1000) cfoutcomment, cfouttitle, cfoutsource, cfoutinstit
integer :: ncid, status, nDims, nVars, nGlobalAtts, unlimDimID
integer :: label, nrsmooth, nrg, nrgc
integer :: LatDimID,LonDimID, TimeDimID,timeVarID,lonVarID,latVarID,vorpotVarID
integer :: VerDimID
integer :: varLatID,varLonID,varPVID,varTimeID
integer :: nLats,nLong,nTimes,nVert
integer, dimension(:), allocatable :: times, latitude, longitude, longi
integer, dimension(:), allocatable :: vertical
integer, dimension(:), allocatable :: iiv, jjv, ttv
integer, dimension(:), allocatable :: time_array
real, dimension(:,:,:), allocatable :: inarr, arr, arr1, arr2
real, dimension(:,:), allocatable :: latmean
! --------------------------------------------------------------
! input handling
! --------------------------------------------------------------
contrackversion = 'version 0.8'
status = 0
print*, '-------------------------------------------------------'
print*, 'contrack ', trim(contrackversion)
print*, '-------------------------------------------------------'
999 continue
if ( (iargc().ne.1).or.(status.gt.0) ) then
print*, '-------------------------------------------------------'
print*, 'contrack ', trim(contrackversion)
print*, '-------------------------------------------------------'
print*, 'Usage: contrack inputfile'
print*, '-------------------------------------------------------'
print*, 'The inputfile needs the following structure:'
print*, ''
print*, 'infilename -> name of the input CF-netCDF file'
print*, 'invarname -> name of the netCDF variable name'
print*, 'inlat -> name of the netCDF latitude variable'
print*, 'inlon -> name of the netCDF longitude variable'
print*, 'intime -> name of the netCDF time variable'
print*, 'threshold -> threshold value to detect contours'
print*, 'gorl -> find contours that are greater or '
print*, ' lower threshold value [ge,le,gt,lt]'
print*, 'persistence -> temporal persistence (in time steps)'
print*, ' of the contour life time'
print*, 'overlap -> overlapping fraction of two contours'
print*, ' between two time steps [0-1]'
print*, 'nrsmooth -> temporal smoothing of the infilename'
print*, ' (in time steps)'
print*, 'outarr -> values of the contours in the output array'
print*, ' 0: contours are numbered by each contour'
print*, ' 1: contours are labeled 0 and 1'
print*, 'outfilename -> name of the output CF-netCDF file'
print*, 'outvarname -> name of the output netCDF variable name'
print*, 'outstvarname -> name of the output netCDF standard'
print*, ' variable name'
print*, 'outvarunit -> name of the outuput netCDF unit'
print*, 'verbose -> [0 or 1]'
print*, 'calcmeranom -> flag whether to calc meriodonal anomalies'
print*, ' or not [0 or 1]'
print*, '-------------------------------------------------------'
print*, 'Please send any comments / bugs to:'
print*, 'mischa.croci-maspoli@alumni.ethz.ch'
print*, '-------------------------------------------------------'
print*, ''
print*, '-->> contrack not executed, an error occured!'
print*, ''
call exit(1)
endif
! Read inputfile
! --------------
call getarg(1,arg)
infilename=trim(arg)
! Open inputfile and read variables
! ---------------------------------
open(2,file=infilename,status='old',iostat=status)
if ( status.gt.0 ) then
print*, ''
print*, '-> Problem in contrack: Your ASCII input file "', trim(infilename), &
'" can not be found'
print*, '-> For further information use ./contrack only'
print*, ''
stop
endif
read(2,*,iostat=status) infilename
if ( status.gt.0 ) goto 999
read(2,*,iostat=status) invarname
if ( status.gt.0 ) goto 999
read(2,*,iostat=status) inlat
if ( status.gt.0 ) goto 999
read(2,*,iostat=status) inlon
if ( status.gt.0 ) goto 999
read(2,*,iostat=status) intime
if ( status.gt.0 ) goto 999
read(2,*,iostat=status) threshold
if ( status.gt.0 ) goto 999
read(2,*,iostat=status) gorl
if ( status.gt.0 ) goto 999
read(2,*,iostat=status) persistence
if ( status.gt.0 ) goto 999
read(2,*,iostat=status) overlap
if ( status.gt.0 ) goto 999
read(2,*,iostat=status) nrsmooth
if ( status.gt.0 ) goto 999
read(2,*,iostat=status) outarr
if ( status.gt.0 ) goto 999
read(2,*,iostat=status) outfilename
if ( status.gt.0 ) goto 999
read(2,*,iostat=status) outvarname
if ( status.gt.0 ) goto 999
read(2,*,iostat=status) outstandvarname
if ( status.gt.0 ) goto 999
read(2,*,iostat=status) outvarunit
if ( status.gt.0 ) goto 999
read(2,*,iostat=status) verbose
if ( status.gt.0 ) goto 999
read(2,*,iostat=status) calcmeranom
if ( status.gt.0 ) goto 999
close(2)
! --------------------------------------------------------------
! read CF-netCDF file
! --------------------------------------------------------------
allocate(time_array(8))
call date_and_time (values=time_array)
write(*,100), "-> Read ", trim(infilename), time_array(5), ':', time_array(6), ':', time_array(7)
100 format(A,A,I5,A,I2,A,I2)
! Initially set error to indicate no errors.
status = 0
! open netCDF file
status = nf90_open(infilename, nf90_nowrite, ncid)
! get dimensions
status = nf90_inq_dimid(ncid, inlat, LatDimID)
status = nf90_inquire_dimension(ncid, LatDimID, len = nLats)
status = nf90_inq_dimid(ncid, inlon, LonDimID)
status = nf90_inquire_dimension(ncid, LonDimID, len = nLong)
!status = nf90_inq_dimid(ncid, inver, VerDimID)
!status = nf90_inquire_dimension(ncid, VerDimID, len = nVert)
status = nf90_inq_dimid(ncid, intime, TimeDimID)
status = nf90_inquire_dimension(ncid, TimeDimID, len = nTimes)
! allocate arrays
allocate(times(nTimes))
allocate(latitude(nLats))
allocate(longitude(nLong))
!allocate(vertical(nVert))
allocate(inarr(nLong,nLats,nTimes))
allocate(arr(nLong,nLats,nTimes))
allocate(arr1(nLong,nLats,nTimes))
allocate(arr2(nLong,nLats,nTimes))
allocate(latmean(nLats,nTimes))
allocate(iiv(nLats*nLong*nTimes))
allocate(jjv(nLats*nLong*nTimes))
allocate(ttv(nLats*nLong*nTimes))
! get variables
status = nf90_inq_varid(ncid, intime, timeVarID)
status = nf90_get_var(ncid, timeVarID, times)
status = nf90_inq_varid(ncid, inlon, lonVarID)
status = nf90_get_var(ncid, lonVarID, longitude)
status = nf90_inq_varid(ncid, inlat, latVarID)
status = nf90_get_var(ncid, latVarID, latitude)
status = nf90_inq_varid(ncid, invarname, vorpotVarID)
status = nf90_get_var(ncid, vorpotVarID, inarr)
status = nf90_inquire_attribute(ncid, nf90_global, "comment", len = commentLength)
status = nf90_get_att(ncid, NF90_GLOBAL, 'comment', cfcomment)
status = nf90_inquire_attribute(ncid, nf90_global, "title", len = titleLength)
status = nf90_get_att(ncid, NF90_GLOBAL, 'title', cftitle)
status = nf90_inquire_attribute(ncid, nf90_global, "source", len = sourceLength)
status = nf90_get_att(ncid, NF90_GLOBAL, 'source', cfsource)
status = nf90_inquire_attribute(ncid, nf90_global, "institution", len = institLength)
status = nf90_get_att(ncid, NF90_GLOBAL, 'institution', cfinstit)
status = nf90_close(ncid)
! --------------------------------------------------------------
! Calculate meridional anomaly
! --------------------------------------------------------------
if ( calcmeranom.eq.1 ) then
print*, '-->> calculate meridional mean anomaly'
! Calculate latitudinal mean
! --------------------------------------------------------------
do t = 1,nTimes
do j = 1,nLats
do i = 1,nLong
latmean(j,t) = latmean(j,t) + inarr(i,j,t)
enddo
enddo
enddo
! Subtract latitudinal mean
! --------------------------------------------------------------
do t = 1,nTimes
do j = 1,nLats
do i = 1,nLong
arr(i,j,t) = inarr(i,j,t) - latmean(j,t)/nLong
enddo
enddo
enddo
! Calculate running means (centered at the first file)
! --------------------------------------------------------------
do t = 1,nTimes
do j = 1,nLats
do i = 1,nLong
do k = 0,nrsmooth
arr1(i,j,t) = arr1(i,j,t) + arr(i,j,t+k)
enddo
enddo
enddo
enddo
arr = arr1/nrsmooth
else
print*, '-->> no anomaly calculated'
arr(:,:,:) = inarr(:,:,:)
endif
! Define closed contours by a threshold value
! --------------------------------------------------------------
print*, '-->> define contours with threshold value'
do t = 1,nTimes
do j = 1,nLats
do i = 1,nLong
if ( trim(gorl).eq.'ge' ) then
if ( arr(i,j,t).ge.threshold) then
arr(i,j,t) = 1
else
arr(i,j,t) = 0
endif
endif
if ( trim(gorl).eq.'le' ) then
if ( arr(i,j,t).le.threshold) then
arr(i,j,t) = 1
else
arr(i,j,t) = 0
endif
endif
if ( trim(gorl).eq.'gt' ) then
if ( arr(i,j,t).gt.threshold) then
arr(i,j,t) = 1
else
arr(i,j,t) = 0
endif
endif
if ( trim(gorl).eq.'lt' ) then
if ( arr(i,j,t).lt.threshold) then
arr(i,j,t) = 1
else
arr(i,j,t) = 0
endif
endif
enddo
enddo
enddo
! Identify individual contour areas on a 2D-space (only x-y, no time)
! -------------------------------------------------------------------
print*, '-->> identify individual contours'
arr2(:,:,:) = 0 ! set arr2 to zero
do t = 1,nTimes
label = 1 ! begin labeling with 1
do j = 1,nLats
do i = 1,nLong
if ( (arr(i,j,t).eq.1).and.(arr2(i,j,t).lt.1) ) then
arr2(i,j,t) = label ! first identified grid point in countour
nrg = 1 ! number of grid points
nrgc = 1 ! number of grid points counter
iiv(nrgc) = i ! x-dir coordinates within contour
jjv(nrgc) = j ! y-dir coordinates within contour
do while ( nrgc.le.nrg )
do ii=-1,1
do jj=-1,1
if ( (arr(iiv(nrgc)+ii,jjv(nrgc)+jj,t).eq.1).and. &
(arr2(iiv(nrgc)+ii,jjv(nrgc)+jj,t).ne.label) ) then
arr2(iiv(nrgc)+ii,jjv(nrgc)+jj,t) = label
nrg = nrg + 1
iiv(nrg) = iiv(nrgc)+ii
jjv(nrg) = jjv(nrgc)+jj
endif
enddo
enddo
nrgc = nrgc + 1
enddo
label = label + 1
endif
enddo
enddo
enddo
! Define temporal overlapping
! --------------------------------------------------------------
print*, '-->> overlapping'
do t = 1,nTimes
do ii = 1,int(maxval(arr2(:,:,t))) ! loop over the number of individual contours
areacon = 0.
areaover = 0.
fraction = 0.
do i = 1,nLong
do j = 1,nLats
if ( ( arr2(i,j,t).eq.ii) ) then
areacon = areacon+(111*(111*cos(2*3.14159/360*j)))
endif
if ( ( arr2(i,j,t).eq.ii).and.(arr2(i,j,t+1).ge.1) ) then
areaover = areaover+(111*(111*cos(2*3.14159/360*j)))
endif
enddo
enddo
fraction = 1 / areacon * areaover
!print*, ii, int(maxval(arr2(:,:,t))), fraction
do i = 1,nLong
do j = 1,nLats
if ( (fraction.lt.overlap).and.(arr2(i,j,t).eq.ii) ) then
arr2(i,j,t) = 0.
endif
enddo
enddo
enddo
enddo
! Identify individual contour areas (3D including time)
! --------------------------------------------------------------
print*, '-->> identify 3D contours'
arr(:,:,:) = arr2(:,:,:)
do t = 1,nTimes
do j = 1,nLats
do i = 1,nLong
if ( arr(i,j,t).ge.1 ) then
arr(i,j,t) = 1
endif
enddo
enddo
enddo
arr2(:,:,:) = 0 ! set arr2 to zero
label = 1 ! begin labeling with 1
do t = 1,nTimes
do j = 1,nLats
do i = 1,nLong
if ( (arr(i,j,t).eq.1).and.(arr2(i,j,t).lt.1) ) then
arr2(i,j,t) = label ! first identified grid point in countour
nrg = 1 ! number of grid points
nrgc = 1 ! number of grid points counter
iiv(nrgc) = i ! x-dir coordinates within contour
jjv(nrgc) = j ! y-dir coordinates within contour
ttv(nrgc) = t ! t-dir coordinates within contour
ttbegin = t ! genesis time
! print*, i,j,t,label
do while ( nrgc.le.nrg )
do ii=-1,1
do jj=-1,1
do tt=-1,1
if ( (arr(iiv(nrgc)+ii,jjv(nrgc)+jj,ttv(nrgc)+tt).eq.1).and. &
(arr2(iiv(nrgc)+ii,jjv(nrgc)+jj,ttv(nrgc)+tt).ne.label) ) then
arr2(iiv(nrgc)+ii,jjv(nrgc)+jj,ttv(nrgc)+tt) = label
nrg = nrg + 1
iiv(nrg) = iiv(nrgc)+ii
jjv(nrg) = jjv(nrgc)+jj
ttv(nrg) = ttv(nrgc)+tt
! get time of lysis
if (ttv(nrg).gt.ttend) then
ttend = ttend + 1
endif
endif
enddo
enddo
enddo
! print*, iiv(nrg),jjv(nrg),ttv(nrg), label
! print*, nrg, nrgc, ttv(nrgc)
! if (nrgc.gt.20) stop
nrgc = nrgc + 1
enddo
print*, label, ttbegin, ttend,ttend-ttbegin
label = label + 1
endif
enddo
enddo
enddo
if ( verbose.eq.1 ) print*, ' -> number of individual contours: ', label-1
! Temporal persistence
! --------------------------------------------------------------
print*, '-->> temporal persistence'
do ii = 1,label
counter = 0
do t = 1,nTimes
do j = 1,nLats
do i = 1,nLong
if ( arr2(i,j,t).eq.ii) then
counter = counter + 1
goto 234
endif
enddo
enddo
234 continue
enddo
do t = 1,nTimes
do j = 1,nLats
do i = 1,nLong
if ( (counter.lt.persistence ).and.(arr2(i,j,t).eq.ii) ) then
arr2(i,j,t) = 0.
endif
enddo
enddo
enddo
enddo
! get contour information
! --------------------------------------------------------------
print*, '-->> get contour information'
! number of individual contours
! length of individual contours
! mean area size of individual contours
! genesis/lysis date
! genesis/lysis area at specific date
nrcontours = 0
do t = 1,nTimes
do j = 1,nLats
do i = 1,nLong
if ( arr2(i,j,t).gt.nrcontours ) then
nrcontours = nrcontours + 1
endif
enddo
enddo
enddo
print*, ' -> number of tracked contours: ', nrcontours
! change output array to a 0/1 array
! --------------------------------------------------------------
if ( outarr.eq.1 ) then
do t = 1,nTimes
do j = 1,nLats
do i = 1,nLong
if ( arr2(i,j,t).gt.0 ) then
arr2(i,j,t) = 1.
endif
enddo
enddo
enddo
endif
arr = arr2
11 continue
! --------------------------------------------------------------
! write CF-netCDF file
! --------------------------------------------------------------
call date_and_time (values=time_array)
write(*,100), "-> Write ", trim(outfilename), time_array(5), ':', time_array(6), ':', time_array(7)
! definitions
! --------------------------------------------------------------
cfoutinstit = cfinstit
cfouttitle = 'contrack '//trim(contrackversion)
cfoutsource = cfsource
write(cfoutcomment, 101) 'contrack is based upon the following input file attributes: ', &
trim(cfcomment), &
' -->> Contrack specifications:: ',&
'contours identified that are -', trim(gorl), '- threshold value, ', &
'threshold value for contour:', threshold, &
', overlapping fraction:', overlap, &
', persistence time steps:', persistence, &
', anomalies calculated by subtracting meridional mean [1] or ', &
'from the input file directly [0]: ', calcmeranom
101 format(A,A,A,A,A,A,A,F8.3,A,F7.3,A,I5,A,A,I3)
! start writing
! ----------------------------------------------------------------
! Initially set error to indicate no errors.
status = 0
! create the netCDF file
status = nf90_create(trim(outfilename), NF90_CLOBBER, ncID)
! IF (ierr.NE.0) GOTO 920
! Dimensions -----------------------------------------------------
status=nf90_def_dim(ncID,'longitude',nLong,LonDimID)
status=nf90_def_dim(ncID,'latitude',nLats,LatDimID)
status=nf90_def_dim(ncID,'time',nf90_unlimited, TimeDimID)
! Variables ------------------------------------------------------
status = nf90_def_var(ncID,'longitude',NF90_FLOAT,(/ LonDimID /),varLonID)
status = nf90_put_att(ncID, varLonID, "standard_name", "longitude")
status = nf90_put_att(ncID, varLonID, "units", "degree_east")
status = nf90_def_var(ncID,'latitude',NF90_FLOAT,(/ LatDimID /),varLatID)
status = nf90_put_att(ncID, varLatID, "standard_name", "latitude")
status = nf90_put_att(ncID, varLatID, "units", "degree_north")
status = nf90_def_var(ncID,'time',NF90_FLOAT, (/ TimeDimID /), varTimeID)
status = nf90_put_att(ncID, varTimeID, "axis", "T")
status = nf90_put_att(ncID, varTimeID, "calendar", "standard")
status = nf90_put_att(ncID, varTimeID, "long_name", "time")
status = nf90_put_att(ncID, varTimeID, "units", "hours since &
1950-01-01 00:OO UTC")
! Global Attributes -----------------------------------------------
status = nf90_put_att(ncID, NF90_GLOBAL, 'Conventions', 'CF-1.0')
status = nf90_put_att(ncID, NF90_GLOBAL, 'title', cfouttitle)
status = nf90_put_att(ncID, NF90_GLOBAL, 'source', cfoutsource)
status = nf90_put_att(ncID, NF90_GLOBAL, 'institution', cfoutinstit)
status = nf90_put_att(ncID, NF90_GLOBAL, 'comment', cfoutcomment)
! Specific variable -----------------------------------------------
status = nf90_def_var(ncID,trim(outvarname),NF90_FLOAT,&
(/ LonDimID, LatDimID, varTimeID /),varPVID)
!status = nf90_def_var(ncID,trim(outvarname),NF90_FLOAT,&
! (/ LatDimID, varTimeID /),varPVID)
status = nf90_put_att(ncID, varPVID, "standard_name",trim(outstandvarname))
status = nf90_put_att(ncID, varPVID, "units", trim(outvarunit))
status = nf90_put_att(ncID, varPVID, '_FillValue', -999.99)
! END variable definitions.
status = nf90_enddef(ncID)
IF (status.GT.0) THEN
print*, 'An error occurred while attempting to ', &
'finish definition mode.'
!GOTO 920
ENDIF
! write DATA
! -------------------------------------------------------------------
status = nf90_put_var(ncID,varLonID,longitude)
status = nf90_put_var(ncID,varLatID,latitude)
!status = nf90_put_var(ncID,varTimeID, times)
status = nf90_put_var(ncID,varTimeID, &
times((persistence+1):(nTimes-persistence)))
! Write block
!status = nf90_put_var(ncID,varPVID, arr(:,:,:))
status = nf90_put_var(ncID,varPVID, &
arr(:,:,(persistence+1):(nTimes-persistence)))
status = nf90_close(ncID)
IF (status.NE.0) THEN
WRITE(0,*) trim(nf90_strerror(status))
WRITE(0,*) 'An error occurred while attempting to ', &
'close the netcdf file.'
WRITE(0,*) 'in clscdf_CF'
ENDIF
close(ncID)
END