Skip to content
Snippets Groups Projects
post_tracking.f90 43.40 KiB
module definitions
    type tracking_cloud
      integer np,ntracks
      real, dimension(:), pointer :: time
      real, dimension(:,:),pointer :: x,y,z,press,temp,velo,veloZ
      integer,dimension(:),pointer :: step
      integer,dimension(:,:),pointer :: tracks
    end type tracking_cloud

end module definitions


!==============================================================================!
!==============================================================================!
!                                                                              |
!              ||===\\                                                         | 
!              ||    \\                                                        |
!              ||     ||   //==\\   ||  ||   //==||  ||/==\\                   |
!              ||     ||  ||    ||  ||  ||  ||   ||  ||    ||                  |
!              ||    //   ||    ||  ||  ||  ||   ||  ||                        |
!              ||===//     \\==//    \\==\\  \\==\\  ||                        |
!                                                                              |
!==============================================================================!
!==============================================================================!
!                                                                              |
!              TRACKING CLOUD POST PROCESSING   Jan. 2015                      |
!              written by Matthias Schmiddunser                                |
!              using age calculation form Pecube and Terra                     |
!                                                                              |
!==============================================================================!
!==============================================================================!
!                                                                              |
! This routine reads in the tracking output files, extracts the surfaced       |
! tracks, does variable calculation and writes ASCII vtk files.                |
!                                                                              |
! CAUTION: Particle ID (Douar) and Point Number (VTK) differ: DOUAR numbering  |
!     starts at 1, VTK numbering starts at 0.                                  |

program post_tracking

use definitions
!use m_ageCalculation

implicit none

include 'mpif.h'

type (tracking_cloud) :: trcl
 
!input parameters from input_tracking_output.txt
character(150) :: input_dir
character(150) :: output_dir
integer :: output_mode
integer :: output_scaled
integer :: output_first
integer :: output_last
integer :: output_step
integer :: keep_nstep
integer :: min_track_length
integer :: input_first
integer :: input_step
real    :: pad_age
integer :: output_AHe
integer :: output_ZHe
integer :: output_AFT
integer :: output_ZFT
integer :: output_Muscovite
integer :: output_velo
integer :: output_veloz
integer :: output_temp
integer :: output_press
integer :: output_time
integer :: output_istep
integer :: output_ID

!buffer variables for reading
real :: read_time,lref,veloref,rhoref,tempscale
integer :: read_step,read_ntracks
integer,dimension(:,:),allocatable :: read_tracks
integer,dimension(:),allocatable :: keep_tracks

!mpi and data transfer variables
logical :: flag
integer :: provided,iproc,nproc,ierr,maxtag
integer,dimension(MPI_STATUS_SIZE) :: mpistatus
integer :: sender,tracksent,send_nstep,endstep
real,dimension(:),allocatable :: send_time,send_temp
real,dimension(:),allocatable :: ages_AHe,ages_ZHe,ages_AFT,ages_ZFT,ages_Muscovite
real,dimension(:),allocatable :: allages

!age calculation
real(8) :: apatiteHe, zirconHe
real(8) :: zirconFT, apatiteFT
real(8) :: muscovite_age


!other variables
integer :: i,k,l,err,dummyint,ipoint,itrack
integer :: output_nstep,input_nstep,input_last,out_track_any,out_nages
integer :: write_nstep,write_points,write_tracks
real :: dummyreal
logical :: keepme

character(4) :: cistep

!------------------------------------------------------------------------------|
!------------------------------------------------------------------------------|

provided=0
call mpi_init_thread(MPI_THREAD_MULTIPLE,provided,ierr)
call mpi_comm_size (mpi_comm_world,nproc,ierr)
call mpi_comm_rank (mpi_comm_world,iproc,ierr)

if (iproc==0) then
    write (*,'(a)') '!------------------------------------------------------------------------------|'
    write (*,'(a)') '!------------------------------------------------------------------------------|'
    write (*,'(a)') '!                                                                              |'
    write (*,'(a)') '!          8888888b.   .d88888b.  888     888       d8888 8888888b.            |'
    write (*,'(a)') '!          888  "Y88b d88P" "Y88b 888     888      d88888 888   Y88b           |'
    write (*,'(a)') '!          888    888 888     888 888     888     d88P888 888    888           |'
    write (*,'(a)') '!          888    888 888     888 888     888    d88P 888 888   d88P           |'
    write (*,'(a)') '!          888    888 888     888 888     888   d88P  888 8888888P"            |'
    write (*,'(a)') '!          888    888 888     888 888     888  d88P   888 888 T88b             |'
    write (*,'(a)') '!          888  .d88P Y88b. .d88P Y88b. .d88P d8888888888 888  T88b            |'
    write (*,'(a)') '!          8888888P"   "Y88888P"   "Y88888P" d88P     888 888   T88b           |'
    write (*,'(a)') '!                                                                              |'
    write (*,'(a)') '!             DOUAR-WSMP post-processor for the tracking cloud                 |'
    write (*,'(a)') '!------------------------------------------------------------------------------|'
    write (*,'(a)') '!------------------------------------------------------------------------------|'
    write (*,*) ''
end if

!------------------------------------------------------------------------------|
!    Input parameters
!------------------------------------------------------------------------------|

!Reading input parameters from post-input-file:

if (iproc==0) then
    open(unit=10, file='input_tracking_outputs.txt',status='old')

    read (10,*) input_dir
    read (10,*) output_dir
    read (10,*) output_mode
    read (10,*) output_scaled
    read (10,*) output_first
    read (10,*) output_last
    read (10,*) output_step
    read (10,*) keep_nstep
    read (10,*) min_track_length
    !keep_nstep has to be less than output_step
    keep_nstep = min(keep_nstep,output_step-1)
    read (10,*) input_first
    read (10,*) input_step
    read (10,*)  !here come the age parameters
    read (10,*) pad_age
    read (10,*) output_AHe
    read (10,*) output_ZHe
    read (10,*) output_AFT
    read (10,*) output_ZFT
    read (10,*) output_Muscovite
    read (10,*)  !here come the track parameters
    read (10,*) output_velo
    read (10,*) output_veloz
    read (10,*) output_temp
    read (10,*) output_press
    read (10,*) output_time
    read (10,*) output_istep
    read (10,*) output_ID

    close (10)

    !calculating and andjusting input and output timesteps
    output_nstep = (output_last-output_first)/output_step+1
    output_last = output_first+(output_nstep-1)*output_step

    input_last = output_last
    input_nstep = (input_last-input_first)/input_step+1
    input_last = input_first+(input_nstep-1)*input_step

    !Checking for slash at end of input and output dir
    l = len(trim(input_dir))
    if (input_dir(l:l) .ne. '/') then
        input_dir = input_dir(1:l)//'/'
    end if
    l = len(trim(output_dir))
    if (output_dir(l:l) .ne. '/') then
        output_dir = output_dir(1:l)//'/'
    end if
end if

!Distribute output parameters to other processes
call mpi_bcast(output_mode,1,mpi_integer,0,mpi_comm_world,ierr)
call mpi_bcast(output_AHe,1,mpi_integer,0,mpi_comm_world,ierr)
call mpi_bcast(output_ZHe,1,mpi_integer,0,mpi_comm_world,ierr)
call mpi_bcast(output_AFT,1,mpi_integer,0,mpi_comm_world,ierr)
call mpi_bcast(output_ZFT,1,mpi_integer,0,mpi_comm_world,ierr)
call mpi_bcast(output_Muscovite,1,mpi_integer,0,mpi_comm_world,ierr)
call mpi_bcast(output_velo,1,mpi_integer,0,mpi_comm_world,ierr)
call mpi_bcast(output_veloz,1,mpi_integer,0,mpi_comm_world,ierr)
call mpi_bcast(output_temp,1,mpi_integer,0,mpi_comm_world,ierr)
call mpi_bcast(output_press,1,mpi_integer,0,mpi_comm_world,ierr)
call mpi_bcast(output_time,1,mpi_integer,0,mpi_comm_world,ierr)
call mpi_bcast(output_istep,1,mpi_integer,0,mpi_comm_world,ierr)
call mpi_bcast(output_ID,1,mpi_integer,0,mpi_comm_world,ierr)

!determine number of ages that will be calculated
out_nages = output_AHe+output_ZHe+output_AFT+output_ZFT+output_Muscovite
allocate (allages(out_nages),stat=err); if (err.ne.0) call stop_run ('Error alloc allages')
write (*,*) iproc, out_nages

!writing output parameters to screen
if (iproc==0) then
    write (*,'(a,a)') 'Simulation Directory: ',input_dir
    write (*,'(a,i2,a,i3,a,i3,a,i3)') 'Input Timesteps: Reading ',input_nstep,' input files '// & 
    'from step ',input_first,' to ',input_last,' in steps of ',input_step
    write (*,'(a,a)') 'Output Directory: ',output_dir
    write (*,'(a,i2,a,i3,a,i3,a,i3)') 'Output Timesteps: Generating ',output_nstep,' output files '// &
    'from step ',output_first,' to ',output_last,' in steps of ',output_step
    write (*,'(a,i2,a)') 'accumulating ',keep_nstep,' steps for surfaced tracks'
    write (*,'(a,i1)') 'Output mode: ',output_mode
    write (*,'(a,l)') 'Write natural units:     ',(output_scaled==1)
    write (*,'(a)') ''
    write (*,'(a)') ' Age variables: '
    write (*,'(a,l)') 'Write AHe Ages:          ',(output_AHe==1)
    write (*,'(a,l)') 'Write ZHe Ages:          ',(output_ZHe==1)
    write (*,'(a,l)') 'Write AFT Ages:          ',(output_AFT==1)
    write (*,'(a,l)') 'Write ZFT Ages:          ',(output_ZFT==1)
    write (*,'(a,l)') 'Write Muscovite Ages:    ',(output_Muscovite==1)
    write (*,'(a)') ''
    write (*,'(a)') 'Track variables: '
    write (*,'(a,l)') 'Write track velocity:    ',(output_velo==1)
    write (*,'(a,l)') 'Write track z velocity:  ',(output_veloz==1)
    write (*,'(a,l)') 'Write track temperature: ',(output_temp==1)
    write (*,'(a,l)') 'Write track pressure:    ',(output_press==1)
    write (*,'(a,l)') 'Write track time:        ',(output_time==1)
    write (*,'(a,l)') 'Write track step number: ',(output_istep==1)
    write (*,'(a,l)') 'Write particle/track ID: ',(output_ID==1)
    write (*,'(a)') ''
    write (*,'(a)') '!------------------------------------------------------------------------------|'
    write (*,'(a)') ''
end if

!------------------------------------------------------------------------------|
!    Reading Tracking Cloud Files
!------------------------------------------------------------------------------|

if (iproc==0) then
    !reading input file header for array sizes
    call int_to_char (cistep,4,input_last)

    open (unit=20,file=trim(input_dir)//'TROUT/tracking_t'//cistep//'.bin',status='old',form='unformatted')
    read (20) trcl%np,read_ntracks,read_step,read_time
    read (20) lref,veloref,rhoref,tempscale
    close (20)

    !allocating master arrays for input
    allocate (trcl%x(trcl%np,input_nstep),stat=err); if (err.ne.0) call stop_run ('Error alloc trcl%x')
    allocate (trcl%y(trcl%np,input_nstep),stat=err); if (err.ne.0) call stop_run ('Error alloc trcl%y')
    allocate (trcl%z(trcl%np,input_nstep),stat=err); if (err.ne.0) call stop_run ('Error alloc trcl%z')
    allocate (trcl%temp(trcl%np,input_nstep),stat=err); if (err.ne.0) call stop_run ('Error alloc trcl%temp')
    allocate (trcl%press(trcl%np,input_nstep),stat=err); if (err.ne.0) call stop_run ('Error alloc trcl%press')
    allocate (trcl%step(input_nstep),stat=err); if (err.ne.0) call stop_run ('Error alloc trcl%step')
    allocate (trcl%time(input_nstep),stat=err); if (err.ne.0) call stop_run ('Error alloc trcl%time')
    allocate (read_tracks(read_ntracks,3),stat=err); if (err.ne.0) call stop_run ('Error alloc read_tracks')

    !read all input files and save in collective arrays
    k = 1
    do i = input_first,input_last,input_step
        !read all information for this time step
        call int_to_char (cistep,4,i)
        open (unit=20,file=trim(input_dir)//'TROUT/tracking_t'//cistep//'.bin',status='old',form='unformatted')
        
        read (20) dummyint,dummyint,trcl%step(k),trcl%time(k)
        read (20) 
        read (20) trcl%x(:,k)
        read (20) trcl%y(:,k)
        read (20) trcl%z(:,k)
        read (20) trcl%temp(:,k)
        read (20) trcl%press(:,k)
        
        !read track information only from last timestep
        if (i==input_last) then
            write (*,'(a)') 'All surfaced tracks:'
            write (*,'(a)') 'track ID, Particle ID, start, end'
            do l=1,read_ntracks 
                read (20) read_tracks(l,:)
                write (*,'(4i6)') l, read_tracks(l,:)
            end do
            write (*,'(a)') ''
        end if

        !advance column in master array
        k = k+1

        close (20)
    end do

    write (*,'(a)') 'Finished reading input files'
    write (*,'(a)') ''

    !select only tracks that surface at an output timestep or up to keep_nstep
    !timesteps before each output timestep and are at least min_track_lenght 
    !timesteps long.
    !Then adjust track timesteps to actual position in master array.
    !The offset k has to be recorded in order to associate the track with the
    !correct timestep.

    allocate (keep_tracks(read_ntracks),stat=err); if (err.ne.0) call stop_run ('Error alloc keep_tracks')
    keep_tracks = 0
    trcl%ntracks = 0
    do k=0,keep_nstep
        do i=1,read_ntracks
            keepme = read_tracks(i,3) >= output_first-k
            keepme = keepme .AND. (mod((read_tracks(i,3)+k-output_first),output_step)==0)
            keepme = keepme .AND. read_tracks(i,3)-read_tracks(i,2)+1 >= min_track_length
            if (keepme) then
                keep_tracks(i) = 1000+k
                trcl%ntracks = trcl%ntracks+1
            end if
        end do
    end do

    !allocate tracks array of needed length

    if (trcl%ntracks > 0) then
        allocate (trcl%tracks(trcl%ntracks,7),stat=err); if (err.ne.0) call stop_run ('Error alloc trcl%tracks')
        trcl%tracks = 0

        !copy relevant tracks into new track array
        !adjust track timsteps to position in new track array
        !always round up for start and round down for end in order not to include parts of other tracks
        itrack=1
        write (*,'(a)') 'Selected surfaced tracks:'
        write (*,'(a)') '*: in master array'
        write (*,'(a)') 'Particle ID, start*, end*, length*, output timestep, output step*, track ID'
        do i=1,read_ntracks
            if (keep_tracks(i)>0) then
                !1st column is particle ID
                trcl%tracks(itrack,1) = read_tracks(i,1)
                !2nd column is start in master array, round up
                trcl%tracks(itrack,2)= max(1,ceiling(1.0+(read_tracks(i,2)-input_first)/(1.0*input_step)))
                !3rd columd is end in master array, round down
                trcl%tracks(itrack,3)= max(1,1+(read_tracks(i,3)-input_first)/input_step) !integer division
                !4th column is track length (number of points in master array)
                trcl%tracks(itrack,4) = trcl%tracks(itrack,3)-trcl%tracks(itrack,2)+1
                !5th column is Douar timestep of surfacing, upshifted for those tracks 
                !within keep_nstep
                trcl%tracks(itrack,5) = read_tracks(i,3)+(keep_tracks(i)-1000)
                !6th column is output timestep in master array
                trcl%tracks(itrack,6)= max(1,1+(trcl%tracks(itrack,5)-input_first)/input_step) !integer division
                !7th column is track ID, i.e. the row of the original track array from input file
                trcl%tracks(itrack,7) = i

                write (*,'(7i6)') trcl%tracks(itrack,:)
                itrack = itrack+1
            end if
        end do
    else
        write (*,'(a)') 'No surfaced tracks'
    end if

    deallocate (read_tracks)
    deallocate (keep_tracks)
end if

!call MPI_barrier (mpi_comm_world, ierr)
call mpi_bcast(trcl%ntracks,1,mpi_integer,0,mpi_comm_world,ierr)

!------------------------------------------------------------------------------|
!    Calculate Thermochron Ages
!------------------------------------------------------------------------------|
ages: if ((output_mode==1 .or. output_mode==2) .AND. trcl%ntracks>0) then
    call MPI_COMM_GET_ATTR (MPI_COMM_WORLD, MPI_TAG_UB, maxtag, flag, ierr)
    if (trcl%ntracks > maxtag) call stop_run ('Too many tracks for safe communications')

    workshare: if (iproc==0) then
    !master distributing work
        if (output_AHe==1) allocate (ages_AHe(trcl%ntracks))
        if (output_ZHe==1) allocate (ages_ZHe(trcl%ntracks))
        if (output_AFT==1) allocate (ages_AFT(trcl%ntracks))
        if (output_ZFT==1) allocate (ages_ZFT(trcl%ntracks))
        if (output_Muscovite==1) allocate (ages_Muscovite(trcl%ntracks))

        tracksent = 0
        !wait for answer, distribute new track
        workdistr: do i=2-nproc,trcl%ntracks
            !for first nproc-1 times, send to processes without receive
            !distribute one track to each processor

            if (i>0) then
                !get result from other process
                
                call MPI_RECV (allages,out_nages,MPI_REAL, MPI_ANY_SOURCE,MPI_ANY_TAG, &
                               MPI_COMM_WORLD,mpistatus,ierr)
                !Identify process and track ID
                sender = mpistatus(MPI_SOURCE)
                itrack = mpistatus(MPI_TAG)

                !write (*,*) iproc, ': received track',itrack,' from proc ',sender, 'length = ',out_nages

                !Write into result arrays
                k = 1
                if (output_AHe==1) then
                    ages_AHe(itrack) = allages(k)
                    call correct_number(ages_AHe(itrack))
                    k = k+1
                end if
                if (output_ZHe==1) then
                    ages_ZHe(itrack) = allages(k)
                    call correct_number(ages_ZHe(itrack))
                    k = k+1
                end if
                if (output_AFT==1) then
                    ages_AFT(itrack) = allages(k)
                    call correct_number(ages_AFT(itrack))
                    k = k+1
                end if
                if (output_ZFT==1) then
                    ages_ZFT(itrack) = allages(k)
                    call correct_number(ages_ZFT(itrack))
                    k = k+1
                end if
                if (output_Muscovite==1) then
                    ages_Muscovite(itrack) = allages(k)
                    call correct_number(ages_Muscovite(itrack))
                end if
            else
                !effecively loops from 1 to nproc-1 for sender
                sender = i+nproc-1
            end if

            !Send new information
            if (tracksent < trcl%ntracks) then
                !Length of time and temperature arrays 
                !Add two entrys at front for initalisation
                !If surfaced earlier than output timestep, add another entry at end
                endstep = min(1,trcl%tracks(tracksent+1,6)-trcl%tracks(tracksent+1,3)) 
                send_nstep = 2 + trcl%tracks(tracksent+1,4)+endstep

                allocate (send_time(send_nstep))
                allocate (send_temp(send_nstep))

                !pad send-arrays at beginning, part 1
                send_time(3:send_nstep-endstep) = &
                    trcl%time(trcl%tracks(tracksent+1,2):trcl%tracks(tracksent+1,3))
                send_time(1:2) = (/0.0,0.0/)

                send_temp(3:send_nstep-endstep) = trcl%temp(trcl%tracks(tracksent+1,1), &
                    trcl%tracks(tracksent+1,2):trcl%tracks(tracksent+1,3))
                send_temp(1) = send_temp(3)
                send_temp(2) = send_temp(3)
                
                !pad send arrays at end if necessary
                if (endstep>0) then
                    send_time(send_nstep) = trcl%time(trcl%tracks(tracksent+1,6))
                    send_temp(send_nstep) = send_temp(send_nstep-1)
                end if

                !Calculate relative Age in Ma
                send_time = send_time(send_nstep) - send_time
                send_time = send_time*lref/veloref 
                !scale temperature
                send_temp = send_temp*tempscale

                !pad age array at the beginning, part 2
                send_time(1) = pad_age
                send_time(2) = 0.5*(send_time(1)+send_time(3))

                !write (*,*) iproc, ': send track',tracksent+1,' of length',send_nstep,' to proc ',sender
                call MPI_SEND (send_time,send_nstep,MPI_REAL,sender,tracksent+1,MPI_COMM_WORLD,ierr)
                call MPI_SEND (send_temp,send_nstep,MPI_REAL,sender,tracksent+1,MPI_COMM_WORLD,ierr)
                tracksent = tracksent+1

                deallocate (send_time)
                deallocate (send_temp)

            else
                !send end signal
                call MPI_SEND (0.0,1,MPI_REAL,sender,0,MPI_COMM_WORLD,ierr)
            end if
        end do workdistr


    else workshare
    !slaves doing actual calculation
        agecalc: do
            call MPI_probe (0, MPI_ANY_TAG, MPI_COMM_WORLD, mpistatus, ierr)
            itrack = mpistatus(MPI_TAG)
            !write (*,*) iproc, ': probed track', itrack

            !tag 0 is end signal
            if (itrack==0) then
                !write (*,*) iproc, ': receive end signal'
                !empty send buffer - necessary?
                call MPI_RECV (dummyreal,1,MPI_REAL,0,MPI_ANY_TAG,MPI_COMM_WORLD,mpistatus,ierr)
                exit
            end if

            call MPI_get_count (mpistatus,MPI_REAL,send_nstep,ierr)

            allocate (send_time(send_nstep))
            allocate (send_temp(send_nstep))

            !write (*,*) iproc, ': receiving track', itrack, ' of length', send_nstep
            call MPI_RECV (send_time,send_nstep,MPI_REAL,0,MPI_ANY_TAG,MPI_COMM_WORLD,mpistatus,ierr)
            call MPI_RECV (send_temp,send_nstep,MPI_REAL,0,MPI_ANY_TAG,MPI_COMM_WORLD,mpistatus,ierr)

            !do age calculation
            k=1
            if (output_AHe==1 .or. output_ZHe==1) then
                !call Mad_He(dble(send_time), dble(send_temp), send_nstep, apatiteHe, zirconHe)
                apatiteHe = 1.d0
                zirconHe = 2.d0
                if (output_AHe==1) then
                    allages(k) = real(apatiteHe)
                    k = k+1
                end if
                if (output_ZHe==1)  then
                    allages(k) = real(zirconHe)
                    k = k+1
                end if
            end if

            if (output_ZFT==1) then
                !call ZFT(dble(send_time), dble(send_temp), send_nstep, zirconFT)
                zirconFT = 3.d0
                allages(k) = real(zirconFT)
                k = k+1
            end if

            if (output_AFT==1) then
                !call get_aft_age(dble(send_time), dble(send_temp), send_nstep, apatiteFT)
                apatiteFT = 4.d0
                allages(k) = real(apatiteFT)
                k = k+1
            end if

            if (output_Muscovite==1) then
                !call Muscovite(dble(send_time), dble(send_temp), send_nstep, muscovite_age)
                muscovite_age = 5.d0
                allages(k) = real(muscovite_age)
                k = k+1
            end if

            !send results
            !write (*,*) iproc, ': send results of track', itrack, 'length = ',out_nages
            call MPI_SEND (allages,out_nages,MPI_REAL,0,itrack, MPI_COMM_WORLD,ierr)
             
            deallocate (send_time)
            deallocate (send_temp)

        end do agecalc
    end if workshare

end if ages

!------------------------------------------------------------------------------|
!    Scaling Values to natural units
!------------------------------------------------------------------------------|
!check what age prediction needs...
!units of scaling parameters:
!lref: km
!veloref: mm/yr = km/Myr
!rhoref: kg/m^3
!tempscale: °C

if (iproc==0) then
    if (output_scaled==1) then
        do k=1,input_nstep 
            do i=1,trcl%np
                trcl%x(i,k) = trcl%x(i,k)*lref !pos in km
                trcl%y(i,k) = trcl%y(i,k)*lref
                trcl%z(i,k) = trcl%z(i,k)*lref
                trcl%temp(i,k) = trcl%temp(i,k)*tempscale !temp in C
                trcl%press(i,k) = trcl%press(i,k)*1.0e-3*9.81*rhoref*lref !pressure in MPa
            end do
            trcl%time(k) = trcl%time(k)*lref/veloref !time in Myr
        end do
    end if
end if


!------------------------------------------------------------------------------|
!    Calculating Output values
!------------------------------------------------------------------------------|

if (iproc==0) then
    !track values
    velo: if (output_velo==1) then
        allocate (trcl%velo(trcl%np,input_nstep),stat=err); if (err.ne.0) call stop_run ('Error alloc trcl%velo')
        do i=1,trcl%np
                trcl%velo(i,1) = sqrt( & 
                    (trcl%x(i,2)-trcl%x(i,1))**2+(trcl%y(i,2)-trcl%y(i,1))**2+(trcl%z(i,2)-trcl%z(i,1))**2) &
                    /(trcl%time(2)-trcl%time(1))
        end do
        do i=1,trcl%np
            do k=2,input_nstep
                trcl%velo(i,k) = sqrt( & 
                    (trcl%x(i,k)-trcl%x(i,k-1))**2+(trcl%y(i,k)-trcl%y(i,k-1))**2+(trcl%z(i,k)-trcl%z(i,k-1))**2) &
                    /(trcl%time(k)-trcl%time(k-1))
            end do
        end do
    end if velo

    veloz: if (output_veloz==1) then
        allocate (trcl%veloz(trcl%np,input_nstep),stat=err); if (err.ne.0) call stop_run ('Error alloc trcl%veloz')
        do i=1,trcl%np
            trcl%veloz(i,1) = (trcl%z(i,2)-trcl%z(i,1))/(trcl%time(2)-trcl%time(1))
        end do
        do i=1,trcl%np
            do k=2,input_nstep
                trcl%veloz(i,k) = (trcl%z(i,k)-trcl%z(i,k-1))/(trcl%time(k)-trcl%time(k-1))
            end do
        end do
    end if veloz
end if


!------------------------------------------------------------------------------|
!    Checking for NaNs and variable ranges
!------------------------------------------------------------------------------|


if (iproc==0) then
    do k=1,input_nstep
        call correct_number(trcl%time(k))
        do i=1,trcl%np
            call correct_number(trcl%temp(i,k))
            call correct_number(trcl%press(i,k))
            if (output_velo==1) call correct_number(trcl%velo(i,k))
            if (output_veloz==1) call correct_number(trcl%veloz(i,k))
        end do
    end do
end if

!------------------------------------------------------------------------------|
!    Writing Output Files
!------------------------------------------------------------------------------|
if (iproc==0) then
    if ((output_mode==1.or.output_mode==2) .and. trcl%ntracks>0) then
    writeages: do l=output_first,output_last,output_step
        !count number of tracks for this timestep
        write_tracks = 0
        do i=1,trcl%ntracks
            if (trcl%tracks(i,5)==l) then
                write_tracks = write_tracks+1
            end if
        end do

        if (write_tracks>0) then
            !create output file age calculation results
            call int_to_char (cistep,4,l)
            open (unit=40,file=trim(output_dir)//'ages_t'//cistep//'.vtk',status='replace')

            !write header
            write(40,'(a)') '# vtk DataFile Version 3.1'
            write(40, '(a,i5,a,i4,a)') 'DOUAR thermochron ages for timestep',l, &
                ', accumulating',keep_nstep,' timesteps'
            write(40, '(a)') 'ASCII'
            write(40, '(a)') 'DATASET UNSTRUCTURED_GRID'
            write(40, '(a)') ''

            !write point coordinates
            write(40, '(a,i10,a)') 'POINTS ', write_tracks, ' FLOAT'
            do i=1,trcl%ntracks
            !go through all tracks
                if (trcl%tracks(i,5)==l) then
                !if track surfaces at this timestep: write last point of track (at surface)
                    write(40,'(3ES12.5E2)') trcl%x(trcl%tracks(i,1),trcl%tracks(i,3)), &
                        trcl%y(trcl%tracks(i,1),trcl%tracks(i,3)), & 
                        trcl%z(trcl%tracks(i,1),trcl%tracks(i,3))
                end if
            end do
            write(40, '(a)') ''

            !write cell data
            write(40, '(a,i10,i10)') 'CELLS ', write_tracks, 2*write_tracks
            ipoint = 0
            do i=1,trcl%ntracks
                if (trcl%tracks(i,5)==l) then
                    !write length of track
                    write(40, '(i1,i6)') 1, ipoint
                    !advance point ID
                    ipoint = ipoint+1
                end if
            end do
            write (40, '(a)') '' 
            

            !write cell data type, 1=single vertex
            write(40, '(a,i10)') 'CELL_TYPES ',write_tracks
            do i = 1,write_tracks
                write(40, '(i2)') 1
            end do
            write (40, '(a)') '' 

            !write point datasets
            if (out_nages > 0) write (40,'(a,i10)') 'POINT_DATA ',write_tracks
            
            if (output_AHe==1) then
                write (40,'(a)') 'SCALARS ApatiteHe FLOAT 1'
                write (40,'(a)') 'LOOKUP_TABLE default'
                do i=1,trcl%ntracks
                    if (trcl%tracks(i,5)==l) then
                        write(40,'(3ES12.5E2)') ages_AHe(i)
                    end if
                end do
                write(40, '(a)') ''
            end if
            
            if (output_ZHe==1) then
                write (40,'(a)') 'SCALARS ZirconHe FLOAT 1'
                write (40,'(a)') 'LOOKUP_TABLE default'
                do i=1,trcl%ntracks
                    if (trcl%tracks(i,5)==l) then
                        write(40,'(3ES12.5E2)') ages_ZHe(i)
                    end if
                end do
                write(40, '(a)') ''
            end if
            
            if (output_AFT==1) then
                write (40,'(a)') 'SCALARS ApatiteFT FLOAT 1'
                write (40,'(a)') 'LOOKUP_TABLE default'
                do i=1,trcl%ntracks
                    if (trcl%tracks(i,5)==l) then
                        write(40,'(3ES12.5E2)') ages_AFT(i)
                    end if
                end do
                write(40, '(a)') ''
            end if
            
            if (output_ZFT==1) then
                write (40,'(a)') 'SCALARS ZirconFT FLOAT 1'
                write (40,'(a)') 'LOOKUP_TABLE default'
                do i=1,trcl%ntracks
                    if (trcl%tracks(i,5)==l) then
                        write(40,'(3ES12.5E2)') ages_ZFT(i)
                    end if
                end do
                write(40, '(a)') ''
            end if
            
            if (output_Muscovite==1) then
                write (40,'(a)') 'SCALARS Muscovite FLOAT 1'
                write (40,'(a)') 'LOOKUP_TABLE default'
                do i=1,trcl%ntracks
                    if (trcl%tracks(i,5)==l) then
                        write(40,'(3ES12.5E2)') ages_Muscovite(i)
                    end if
                end do
                write(40, '(a)') ''
            end if

            close (40) 
        end if
    end do writeages
    end if

    out_track_any = output_velo+output_veloz+output_temp+output_press+output_time+output_istep+output_ID

    if (output_mode==2 .and. trcl%ntracks>0) then
    !output ages and surfaced tracks for each output timestep
        do l=output_first,output_last,output_step
            !count number of tracks and number of points in those for this timestep
            write_tracks = 0
            write_points = 0
            do i=1,trcl%ntracks
                if (trcl%tracks(i,5)==l) then
                    write_tracks = write_tracks+1
                    write_points = write_points+trcl%tracks(i,4)
                end if
            end do

            if (write_tracks>0) then
                call int_to_char (cistep,4,l)
                open (unit=30,file=trim(output_dir)//'tracks_t'//cistep//'.vtk',status='replace')

                !write header
                write(30,'(a)') '# vtk DataFile Version 3.1'
                write(30, '(a)') 'DOUAR tracking cloud output of surfaced tracks'
                write(30, '(a)') 'ASCII'
                write(30, '(a)') 'DATASET UNSTRUCTURED_GRID'
                write(30, '(a)') ''

                !write point coordinates
                write(30, '(a,i10,a)') 'POINTS ', write_points, ' FLOAT'
                do i=1,trcl%ntracks
                !go through all tracks
                    if (trcl%tracks(i,5)==l) then
                    !if track surfaces at this timestep:
                        do k=trcl%tracks(i,2),trcl%tracks(i,3)
                        !write all point positions within track extent
                            write(30,'(3ES12.5E2)') trcl%x(trcl%tracks(i,1),k), &
                                trcl%y(trcl%tracks(i,1),k),trcl%z(trcl%tracks(i,1),k)
                        end do
                    end if
                end do
                write(30, '(a)') ''

                !write cell data
                write(30, '(a,i10,i10)') 'CELLS ', write_tracks, write_points+write_tracks
                ipoint = 0
                do i=1,trcl%ntracks
                    if (trcl%tracks(i,5)==l) then
                        !write length of track
                        write(30, '(i4)',advance='no') trcl%tracks(i,4)
                        do k=trcl%tracks(i,2),trcl%tracks(i,3)
                            !write point ID
                            write(30, '(i9)', advance='no') ipoint
                            !advance point ID
                            ipoint = ipoint+1
                        end do
                        !end of track
                        write (30, '(a)') '' 
                    end if
                end do
                write (30, '(a)') '' 
                

                !write cell data type, 4=polyline
                write(30, '(a,i10)') 'CELL_TYPES ',write_tracks
                do i = 1,write_tracks
                    write(30, '(i2)') 4
                end do
                write (30, '(a)') '' 

                !write point datasets
                if (out_track_any > 0) write (30,'(a,i10)') 'POINT_DATA ',write_points
                
                if (output_velo==1) then
                    write (30,'(a)') 'SCALARS Velo FLOAT 1'
                    write (30,'(a)') 'LOOKUP_TABLE default'
                    do i=1,trcl%ntracks
                        if (trcl%tracks(i,5)==l) then
                            do k=trcl%tracks(i,2),trcl%tracks(i,3)
                                write(30,'(3ES12.5E2)') trcl%velo(trcl%tracks(i,1),k)
                            end do
                        end if
                    end do
                    write(30, '(a)') ''
                end if

                if (output_veloz==1) then
                    write (30,'(a)') 'SCALARS VeloZ FLOAT 1'
                    write (30,'(a)') 'LOOKUP_TABLE default'
                    do i=1,trcl%ntracks
                        if (trcl%tracks(i,5)==l) then
                            do k=trcl%tracks(i,2),trcl%tracks(i,3)
                                write(30,'(3ES12.5E2)') trcl%veloz(trcl%tracks(i,1),k)
                            end do
                        end if
                    end do
                    write(30, '(a)') ''
                end if

                if (output_temp==1) then
                    write (30,'(a)') 'SCALARS Temperature FLOAT 1'
                    write (30,'(a)') 'LOOKUP_TABLE default'
                    do i=1,trcl%ntracks
                        if (trcl%tracks(i,5)==l) then
                            do k=trcl%tracks(i,2),trcl%tracks(i,3)
                                write(30,'(3ES12.5E2)') trcl%temp(trcl%tracks(i,1),k)
                            end do
                        end if
                    end do
                    write(30, '(a)') ''
                end if

                if (output_press==1) then
                    write (30,'(a)') 'SCALARS Pressure FLOAT 1'
                    write (30,'(a)') 'LOOKUP_TABLE default'
                    do i=1,trcl%ntracks
                        if (trcl%tracks(i,5)==l) then
                            do k=trcl%tracks(i,2),trcl%tracks(i,3)
                                write(30,'(3ES12.5E2)') trcl%press(trcl%tracks(i,1),k)
                            end do
                        end if
                    end do
                    write(30, '(a)') ''
                end if

                if (output_time==1) then
                    write (30,'(a)') 'SCALARS Time FLOAT 1'
                    write (30,'(a)') 'LOOKUP_TABLE default'
                    do i=1,trcl%ntracks
                        if (trcl%tracks(i,5)==l) then
                            do k=trcl%tracks(i,2),trcl%tracks(i,3)
                                write(30,'(3ES12.5E2)') trcl%time(k)
                            end do
                        end if
                    end do
                    write(30, '(a)') ''
                end if

                if (output_istep==1) then
                    write (30,'(a)') 'SCALARS Step INTEGER 1'
                    write (30,'(a)') 'LOOKUP_TABLE default'
                    do i=1,trcl%ntracks
                        if (trcl%tracks(i,5)==l) then
                            do k=trcl%tracks(i,2),trcl%tracks(i,3)
                                write(30,'(i5)') trcl%step(k)
                            end do
                        end if
                    end do
                    write(30, '(a)') ''
                end if
                if (output_ID==1) then
                    write (30,'(a)') 'SCALARS Track_ID INTEGER 1'
                    write (30,'(a)') 'LOOKUP_TABLE default'
                    do i=1,trcl%ntracks
                        if (trcl%tracks(i,5)==l) then
                            do k=trcl%tracks(i,2),trcl%tracks(i,3)
                                write(30,'(i5)') trcl%tracks(i,6)
                            end do
                        end if
                    end do
                    write(30, '(a)') ''
                end if

                close (30)
            else
                write (*,'(a,i4)') 'No surfaced tracks for step ', l
            end if
        end do
    end if

    if (output_mode==3) then
    !output all tracks
        do l=output_first,output_last,output_step
            call int_to_char (cistep,4,l)
            open (unit=30,file=trim(output_dir)//'alltracks_t'//cistep//'.vtk',status='replace')

            !calculate part of array to write
            write_nstep = (l-input_first)/input_step+1

            !write header
            write(30,'(a)') '# vtk DataFile Version 3.1'
            write(30, '(a)') 'DOUAR tracking cloud output of all tracks'
            write(30, '(a)') 'ASCII'
            write(30, '(a)') 'DATASET UNSTRUCTURED_GRID'
            write(30, '(a)') ''

            !write point coordinates
            write(30, '(a,i8,a)') 'POINTS ', trcl%np*write_nstep, ' FLOAT'
            do i=1,trcl%np
                do k=1,write_nstep
                    write(30,'(3ES12.5E2)') trcl%x(i,k),trcl%y(i,k),trcl%z(i,k)
                end do
            end do
            write(30, '(a)') ''

            !write cell data
            write(30, '(a,i10,i10)') 'CELLS ',trcl%np, trcl%np*(write_nstep+1)
            do i = 0,trcl%np-1
                write(30, '(i4)',advance='no') write_nstep
                do k = 1,write_nstep
                    write(30, '(i8)', advance='no') i*write_nstep+k-1
                end do
                write (30, '(a)') '' 
            end do
            write (30, '(a)') '' 

            !write cell data type, 4=polyline
            write(30, '(a,i10)') 'CELL_TYPES ',trcl%np
            do i = 1,trcl%np
                write(30, '(i2)') 2
            end do
            write (30, '(a)') '' 

            !write point datasets
            if (out_track_any > 0) write (30,'(a,i10)') 'POINT_DATA ',trcl%np*write_nstep
            
            if (output_velo==1) then
                write (30,'(a)') 'SCALARS Velo FLOAT 1'
                write (30,'(a)') 'LOOKUP_TABLE default'
                do i=1,trcl%np
                    do k=1,write_nstep
                        write(30,'(ES12.5E2)') trcl%velo(i,k)
                    end do
                end do
                write(30, '(a)') ''
            end if

            if (output_veloz==1) then
                write (30,'(a)') 'SCALARS VeloZ FLOAT 1'
                write (30,'(a)') 'LOOKUP_TABLE default'
                do i=1,trcl%np
                    do k=1,write_nstep
                        write(30,'(ES12.5E2)') trcl%veloz(i,k)
                    end do
                end do
                write(30, '(a)') ''
            end if

            if (output_temp==1) then
                write (30,'(a)') 'SCALARS Temperature FLOAT 1'
                write (30,'(a)') 'LOOKUP_TABLE default'
                do i=1,trcl%np
                    do k=1,write_nstep
                        write(30,'(ES12.5E2)') trcl%temp(i,k)
                    end do
                end do
                write(30, '(a)') ''
            end if

            if (output_press==1) then
                write (30,'(a)') 'SCALARS Pressure FLOAT 1'
                write (30,'(a)') 'LOOKUP_TABLE default'
                do i=1,trcl%np
                    do k=1,write_nstep
                        write(30,'(ES12.5E2)') trcl%press(i,k)
                    end do
                end do
                write(30, '(a)') ''
            end if

            if (output_time==1) then
                write (30,'(a)') 'SCALARS Time FLOAT 1'
                write (30,'(a)') 'LOOKUP_TABLE default'
                do i=1,trcl%np
                    do k=1,write_nstep
                        write(30,'(ES12.5E2)') trcl%time(k)
                    end do
                end do
                write(30, '(a)') ''
            end if

            if (output_istep==1) then
                write (30,'(a)') 'SCALARS Step INTEGER 1'
                write (30,'(a)') 'LOOKUP_TABLE default'
                do i=1,trcl%np
                    do k=1,write_nstep
                        write(30,'(i5)') trcl%step(k)
                    end do
                end do
                write(30, '(a)') ''
            end if

            if (output_ID==1) then
                write (30,'(a)') 'SCALARS Particle_ID INTEGER 1'
                write (30,'(a)') 'LOOKUP_TABLE default'
                do i=1,trcl%np
                    do k=1,write_nstep
                        write(30,'(i6)') i
                    end do
                end do
                write(30, '(a)') ''
            end if

            close (30)
        end do
    end if

    deallocate (trcl%x)   
    deallocate (trcl%y)
    deallocate (trcl%z)
    deallocate (trcl%temp)
    deallocate (trcl%press)
    deallocate (trcl%step)
    deallocate (trcl%time)
    if (trcl%ntracks>0) then
        deallocate (trcl%tracks)
        if (output_mode==1 .or. output_mode==2) then
            deallocate (ages_AHe)
            deallocate (ages_ZHe)
            deallocate (ages_AFT)
            deallocate (ages_ZFT)
            deallocate (ages_Muscovite)
        end if
    end if
    if (output_velo==1) deallocate (trcl%velo)
    if (output_veloz==1) deallocate (trcl%veloz)
end if

deallocate (allages)

call mpi_finalize (ierr)

end program post_tracking

subroutine correct_number (x)
!'fix' Nan, Inf or underflow values so that they lie in float (=real(4)) range.
! new NaN Value: 1e38
! new -inf value: -1e37
! new +inf value: 1e37
! underflow set to 0

implicit none

real(4), intent(inout) :: x

if (isnan(x)) then
    x=1.0e38
else
    if ( x > 1.0e37) x=1.0e37
    if ( x < -1.0e37) x=-1.0e37
    if (abs(x) < 1.0e-37) x=0.0
end if

end subroutine correct_number