!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!
!     This module uses F90 cpu time routines to allowing setting of
!     multiple CPU timers.
!
!-----------------------------------------------------------------------
!
!     CVS:$Id: timers.f 2826 2010-12-10 11:14:21Z valcke $
!
!     Copyright (c) 1997, 1998 the Regents of the University of 
!       California.
!
!     This software and ancillary information (herein called software) 
!     called SCRIP is made available under the terms described here.  
!     The software has been approved for release with associated 
!     LA-CC Number 98-45.
!
!     Unless otherwise indicated, this software has been authored
!     by an employee or employees of the University of California,
!     operator of the Los Alamos National Laboratory under Contract
!     No. W-7405-ENG-36 with the U.S. Department of Energy.  The U.S.
!     Government has rights to use, reproduce, and distribute this
!     software.  The public may copy and use this software without
!     charge, provided that this Notice and any statement of authorship
!     are reproduced on all copies.  Neither the Government nor the
!     University makes any warranty, express or implied, or assumes
!     any liability or responsibility for the use of this software.
!
!     If software is modified to produce derivative works, such modified
!     software should be clearly marked, so as not to confuse it with 
!     the version available from Los Alamos National Laboratory.
!
!***********************************************************************

      module timers

!-----------------------------------------------------------------------

      use kinds_mod

      implicit none

      integer (kind=int_kind), parameter ::  max_timers = 99  ! max number of timers allowed

      integer (kind=int_kind), save :: cycles_max       ! max value of clock allowed by system

      integer (kind=int_kind), dimension(max_timers), save :: cycles1,  &       ! cycle number at start for each timer
                                                              cycles2           ! cycle number at stop  for each timer

      real (kind=real_kind), save :: clock_rate       ! clock_rate in seconds for each cycle

      real (kind=real_kind), dimension(max_timers), save :: cputime          ! accumulated cpu time in each timer

      character (len=8), dimension(max_timers), save :: status           ! timer status string

      character (len=char_len), dimension(max_timers), save :: name

!***********************************************************************

      contains

!***********************************************************************

      subroutine timer_check(timer)

!-----------------------------------------------------------------------
!
!     This routine checks a given timer.  This is primarily used to
!     periodically accumulate time in the timer to prevent timer cycles
!     from wrapping around max_cycles.
!
!-----------------------------------------------------------------------

!-----------------------------------------------------------------------
!
!     Input Variables:
!
!-----------------------------------------------------------------------

      integer (kind=int_kind), intent(in) ::  timer            ! timer number

!-----------------------------------------------------------------------

      if (status(timer) .eq. 'running') then
        call timer_stop (timer)
        call timer_start(timer)
      endif

!-----------------------------------------------------------------------

      end subroutine timer_check

!***********************************************************************

      subroutine timer_clear(timer)

!-----------------------------------------------------------------------
!
!     This routine resets a given timer.
!
!-----------------------------------------------------------------------

!-----------------------------------------------------------------------
!
!     Input Variables:
!
!-----------------------------------------------------------------------

      integer (kind=int_kind), intent(in) ::  timer            ! timer number

!-----------------------------------------------------------------------

      cputime(timer) = 0.0_real_kind  ! clear the timer

!-----------------------------------------------------------------------

      end subroutine timer_clear

!***********************************************************************

      function timer_get(timer)

!-----------------------------------------------------------------------
!
!     This routine returns the result of a given timer.  This can be
!     called instead of timer_print so that the calling routine can 
!     print it in desired format.
!
!-----------------------------------------------------------------------

!-----------------------------------------------------------------------
!
!     Input Variables:
!
!-----------------------------------------------------------------------

      integer (kind=int_kind), intent(in) ::  timer            ! timer number

!-----------------------------------------------------------------------
!
!     Output Variables:
!
!-----------------------------------------------------------------------

      real (kind=real_kind) :: timer_get   ! accumulated cputime in given timer

!-----------------------------------------------------------------------

      if (status(timer) .eq. 'stopped') then
        timer_get = cputime(timer)
      else
        call timer_stop(timer)
        timer_get = cputime(timer)
        call timer_start(timer)
      endif

!-----------------------------------------------------------------------

      end function timer_get

!***********************************************************************

      subroutine timer_print(timer)

!-----------------------------------------------------------------------
!
!     This routine prints the accumulated cpu time in given timer.
!
!-----------------------------------------------------------------------

!-----------------------------------------------------------------------
!
!     Input Variables:
!
!-----------------------------------------------------------------------

      integer (kind=int_kind), intent(in) :: timer            ! timer number

!-----------------------------------------------------------------------

      !--- 
      !--- print the cputime accumulated for timer 
      !--- make sure timer is stopped
      !---

      if (status(timer) .eq. 'stopped') THEN
        if (nlogprt .GE. 2) THEN
          if (trim(name(timer)) .eq. 'noname') then
             write(nulou,"(' Elapsed time for timer',i3,':',1p,e16.8)") timer,cputime(timer)
          else
             write(nulou, "(' Elapsed time for timer ',A24,':',1x,f10.4)") name(timer),cputime(timer)
          end if
        end if
      else
        call timer_stop(timer)
        if (nlogprt .GE. 2) THEN
          if (trim(name(timer)) .eq. 'noname') then
             write(nulou,"(' Elapsed time for timer',i3,':',1p,e16.8)") timer,cputime(timer)
          else
             write(nulou,"(' Elapsed time for timer ',A24,':',1x,f10.4)") name(timer),cputime(timer)
          end if
        end if
        call timer_start(timer)
      endif

!-----------------------------------------------------------------------

      end subroutine timer_print

!***********************************************************************

      subroutine timer_start(timer,string)

!-----------------------------------------------------------------------
!
!     This routine starts a given timer.
!
!-----------------------------------------------------------------------

!-----------------------------------------------------------------------
!
!     Input Variables:
!
!-----------------------------------------------------------------------

      integer (kind=int_kind), intent(in) :: timer            ! timer number

      character (len=*), intent(in), optional :: string

!-----------------------------------------------------------------------

      !---
      !--- Start the timer and change timer status.
      !---

      if (status(timer) .eq. 'stopped') then
        call system_clock(count=cycles1(timer))
        status(timer) = 'running'
        if (present(string)) then
            name(timer) = trim(string)
        end if
      endif

!-----------------------------------------------------------------------

      end subroutine timer_start

!***********************************************************************

      subroutine timer_stop(timer)

!-----------------------------------------------------------------------
!
!     This routine stops a given timer.
!
!-----------------------------------------------------------------------

!-----------------------------------------------------------------------
!
!     Input Variables:
!
!-----------------------------------------------------------------------

      integer (kind=int_kind), intent(in) :: timer            ! timer number

!-----------------------------------------------------------------------

      if (status(timer) .eq. 'running') then

        !---
        !--- Stop the desired timer.
        !---

        call system_clock(count=cycles2(timer))

        !---
        !--- check and correct for cycle wrapping
        !---

        if (cycles2(timer) .ge. cycles1(timer)) then
          cputime(timer) = cputime(timer) + clock_rate*(cycles2(timer) - cycles1(timer))
        else
          cputime(timer) = cputime(timer) + clock_rate*(cycles2(timer) - cycles1(timer) + cycles_max)
        endif

        !---
        !--- Change timer status.
        !---

        status(timer)='stopped'

      endif

!-----------------------------------------------------------------------

      end subroutine timer_stop

!***********************************************************************

      subroutine timers_init

!-----------------------------------------------------------------------
!
!     This routine initializes some machine parameters necessary for
!     computing cpu time from F90 intrinsics.
!
!-----------------------------------------------------------------------

      integer (kind=int_kind) :: cycles ! count rate return by sys_clock

!-----------------------------------------------------------------------

      !---
      !--- Initialize timer arrays and clock_rate.
      !---

      clock_rate = 0.0_real_kind
      cycles1    = 0
      cycles2    = 0
      cputime    = 0.0_real_kind
      status     = 'stopped'
      name       = 'noname'

      !---
      !--- Call F90 intrinsic system_clock to determine clock rate
      !--- and maximum cycles.  If no clock available, print message.
      !---

      call system_clock(count_rate=cycles, count_max=cycles_max)

      if (cycles /= 0) then
        clock_rate = 1.0_real_kind/real(cycles)
      else
        clock_rate = 0.0_real_kind
        if (nlogprt .GE. 2) &
          WRITE(nulou,*) '--- No system clock available ---'
      endif

!-----------------------------------------------------------------------

      end subroutine timers_init

!***********************************************************************

      end module timers

!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
