! cetgeneral.f90
! module in which various CET routines with general applicability are held
! contains: FillSeaAnnMean, FillSeaAnnMin, FillSeaAnnMax, FillSeaAnnSum, 
!       FillDailyMean, FillDailyMin, FillDailyMax, FillDailySum

module CETGeneral

use Time

implicit none

contains

!*******************************************************************************
! fill monthly array with means from the daily array

subroutine FillDailyMean (MissAccept,YearAD,Daily,Monthly)

real, dimension (:,:,:), pointer 	:: Daily
real, dimension (:,:), pointer 		:: Monthly

integer, dimension (:,:), pointer 	:: MonthLengths
integer, dimension (:), pointer 	:: YearAD

real, intent(in) :: MissAccept

real, parameter :: MissVal = -999.0

real :: OpTot, OpEn, OpMiss, OpCheck, MissThresh

integer :: ReadStatus, Abandon, AllocStat
integer :: YearN
integer :: XYear, XMonth, XDay

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

Abandon = 0

YearN = size (YearAD)					! get and check sizes
if      (YearN.NE.size(Monthly,1).OR.YearN.NE.size(Daily,1)) then
	Abandon = 1
else if (12.NE.size(Monthly,2).OR.12.NE.size(Daily,2).OR.31.NE.size(Daily,3)) then
	Abandon = 1
end if

allocate (MonthLengths(YearN,12), stat=AllocStat)
if (AllocStat.NE.0) print*, "  > ##### ERROR: FillDailySum #####"

call GetMonthLengths (YearAD,MonthLengths)

if (Abandon.EQ.0) then
  do XYear = 1, YearN
    do XMonth = 1, 12
      OpTot  = 0.0
      OpEn   = 0.0
      
      do XDay = 1, MonthLengths(XYear,XMonth)
        if (Daily(XYear,XMonth,XDay).NE.MissVal) then
          OpTot = OpTot + Daily(XYear,XMonth,XDay)
          OpEn  = OpEn  + 1
        end if
      end do
      
      OpMiss   = MonthLengths(XYear,XMonth) - OpEn
      MissThresh = (MissAccept/100)*MonthLengths(XYear,XMonth)
      
      if (OpMiss.LE.MissThresh) then
        Monthly(XYear,XMonth) = OpTot / OpEn
        OpCheck               = OpTot / 28			! ##############
      else
        Monthly(XYear,XMonth) = MissVal
      end if
      								! ##############
      if (XMonth.EQ.2) write (99,"(i6,2f12.4)"), YearAD(XYear), Monthly(XYear,XMonth), OpCheck
    end do
  end do
else
  print*, "  > FillDailyMean: array size mismatch."
end if

deallocate (MonthLengths, stat=AllocStat)
if (AllocStat.NE.0) print*, "  > ##### ERROR: FillDailyMean: Deallocation failure #####"

end subroutine FillDailyMean

!*******************************************************************************
! fill each month with min value from the daily array for that month

subroutine FillDailyMin (MissAccept,YearAD,Daily,Monthly)

real, dimension (:,:,:), pointer 	:: Daily
real, dimension (:,:), pointer 		:: Monthly

integer, dimension (:,:), pointer 	:: MonthLengths
integer, dimension (:), pointer 	:: YearAD

real, intent(in) :: MissAccept

real, parameter :: MissVal = -999.0

real :: OpMin, OpEn, OpMiss, MissThresh

integer :: ReadStatus, Abandon, AllocStat
integer :: YearN
integer :: XYear, XMonth, XDay

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

Abandon = 0

YearN = size (YearAD)					! get and check sizes
if      (YearN.NE.size(Monthly,1).OR.YearN.NE.size(Daily,1)) then
	Abandon = 1
else if (12.NE.size(Monthly,2).OR.12.NE.size(Daily,2).OR.31.NE.size(Daily,3)) then
	Abandon = 1
end if

allocate (MonthLengths(YearN,12), stat=AllocStat)
if (AllocStat.NE.0) print*, "  > ##### ERROR: FillDailySum #####"

call GetMonthLengths (YearAD,MonthLengths)

if (Abandon.EQ.0) then
  do XYear = 1, YearN
    do XMonth = 1, 12
      OpMin  = 1000000.0
      OpEn   = 0.0
      
      do XDay = 1, MonthLengths(XYear,XMonth)
        if (Daily(XYear,XMonth,XDay).NE.MissVal) then
          if (Daily(XYear,XMonth,XDay).LT.OpMin) OpMin = Daily(XYear,XMonth,XDay)
          OpEn  = OpEn  + 1
        end if
      end do
      
      OpMiss   = MonthLengths(XYear,XMonth) - OpEn
      MissThresh = (MissAccept/100)*MonthLengths(XYear,XMonth)
      
      if (OpMiss.LE.MissThresh) then
        Monthly(XYear,XMonth) = OpMin
      else
        Monthly(XYear,XMonth) = MissVal
      end if
    end do
  end do
else
  print*, "  > FillDailyMin: array size mismatch."
end if

deallocate (MonthLengths, stat=AllocStat)
if (AllocStat.NE.0) print*, "  > ##### ERROR: FillDailyMin: Deallocation failure #####"

end subroutine FillDailyMin

!*******************************************************************************
! fill each month with Max value from the daily array for that month

subroutine FillDailyMax (MissAccept,YearAD,Daily,Monthly)

real, dimension (:,:,:), pointer 	:: Daily
real, dimension (:,:), pointer 		:: Monthly

integer, dimension (:,:), pointer 	:: MonthLengths
integer, dimension (:), pointer 	:: YearAD

real, intent(in) :: MissAccept

real, parameter :: MissVal = -999.0

real :: OpMax, OpEn, OpMiss, MissThresh

integer :: ReadStatus, Abandon, AllocStat
integer :: YearN
integer :: XYear, XMonth, XDay

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

Abandon = 0

YearN = size (YearAD)					! get and check sizes
if      (YearN.NE.size(Monthly,1).OR.YearN.NE.size(Daily,1)) then
	Abandon = 1
else if (12.NE.size(Monthly,2).OR.12.NE.size(Daily,2).OR.31.NE.size(Daily,3)) then
	Abandon = 1
end if

allocate (MonthLengths(YearN,12), stat=AllocStat)
if (AllocStat.NE.0) print*, "  > ##### ERROR: FillDailySum #####"

call GetMonthLengths (YearAD,MonthLengths)

if (Abandon.EQ.0) then
  do XYear = 1, YearN
    do XMonth = 1, 12
      OpMax  = -1000000.0
      OpEn   = 0.0
      
      do XDay = 1, MonthLengths(XYear,XMonth)
        if (Daily(XYear,XMonth,XDay).NE.MissVal) then
          if (Daily(XYear,XMonth,XDay).GT.OpMax) OpMax = Daily(XYear,XMonth,XDay)
          OpEn  = OpEn  + 1
        end if
      end do
      
      OpMiss   = MonthLengths(XYear,XMonth) - OpEn
      MissThresh = (MissAccept/100)*MonthLengths(XYear,XMonth)
      
      if (OpMiss.LE.MissThresh) then
        Monthly(XYear,XMonth) = OpMax
      else
        Monthly(XYear,XMonth) = MissVal
      end if
    end do
  end do
else
  print*, "  > FillDailyMax: array size mismatch."
end if

deallocate (MonthLengths, stat=AllocStat)
if (AllocStat.NE.0) print*, "  > ##### ERROR: FillDailyMax: Deallocation failure #####"

end subroutine FillDailyMax

!*******************************************************************************
! fill monthly array with sums from the daily array

subroutine FillDailySum (MissAccept,YearAD,Daily,Monthly)

real, dimension (:,:,:), pointer 	:: Daily
real, dimension (:,:), pointer 		:: Monthly

integer, dimension (:,:), pointer 	:: MonthLengths
integer, dimension (:), pointer 	:: YearAD

real, intent(in) :: MissAccept

real, parameter :: MissVal = -999.0

real :: OpTot, OpEn, OpMiss, OpCheck, MissThresh

integer :: ReadStatus, Abandon, AllocStat
integer :: YearN
integer :: XYear, XMonth, XDay

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

Abandon = 0

YearN = size (YearAD)					! get and check sizes
if      (YearN.NE.size(Monthly,1).OR.YearN.NE.size(Daily,1)) then
	Abandon = 1
else if (12.NE.size(Monthly,2).OR.12.NE.size(Daily,2).OR.31.NE.size(Daily,3)) then
	Abandon = 1
end if

allocate (MonthLengths(YearN,12), stat=AllocStat)
if (AllocStat.NE.0) print*, "  > ##### ERROR: FillDailySum #####"

call GetMonthLengths (YearAD,MonthLengths)

if (Abandon.EQ.0) then
  do XYear = 1, YearN
    do XMonth = 1, 12
      OpTot  = 0.0
      OpEn   = 0.0
      
      do XDay = 1, MonthLengths(XYear,XMonth)
        if (Daily(XYear,XMonth,XDay).NE.MissVal) then
          OpTot = OpTot + Daily(XYear,XMonth,XDay)
          OpEn  = OpEn  + 1
        end if
      end do
      
      OpMiss     = MonthLengths(XYear,XMonth) - OpEn
      MissThresh = (MissAccept/100)*MonthLengths(XYear,XMonth)
      
      if      (OpMiss.EQ.0) then
        Monthly(XYear,XMonth) =  OpTot
      else if (OpMiss.LE.MissThresh) then
        Monthly(XYear,XMonth) = (OpTot / OpEn) * MonthLengths(XYear,XMonth)
      else
        Monthly(XYear,XMonth) = MissVal
      end if
    end do
  end do
else
  print*, "  > FillDailySum: array size mismatch."
end if

deallocate (MonthLengths, stat=AllocStat)
if (AllocStat.NE.0) print*, "  > ##### ERROR: FillDailySum: Deallocation failure #####"

end subroutine FillDailySum

!*******************************************************************************
! fill seasonal and annual arrays with means from the monthly array

subroutine FillSeaAnnMean (YearAD,Monthly,Seasonal,Annual)

real, dimension (:,:), pointer 		:: Monthly, Seasonal
real, dimension (:), pointer 		:: Annual

integer, dimension (:,:), pointer 	:: MonthLengths
integer, dimension (:), pointer 	:: YearAD

real, parameter :: MissVal = -999.0

real :: OpTot, OpEn, OpMiss

integer :: ReadStatus, Abandon, AllocStat
integer :: YearN
integer :: XYear, XMonth, XSeason

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

Abandon = 0

YearN = size (YearAD)					! get and check sizes
if      (YearN.NE.size(Monthly,1).OR.YearN.NE.size(Seasonal,1).OR.YearN.NE.size(Annual)) then
	Abandon = 1
else if (12.NE.size(Monthly,2).OR.4.NE.size(Seasonal,2)) then
	Abandon = 1
end if

allocate (MonthLengths(YearN,12), stat=AllocStat)
if (AllocStat.NE.0) print*, "  > ##### ERROR: FillSeaAnnMean: allocation failure #####"

call GetMonthLengths (YearAD,MonthLengths)

if (Abandon.EQ.0) then
  do XYear = 1, YearN
    do XSeason = 1, 4
      OpMiss = 0
      OpTot  = 0
      OpEn   = 0
      
      do XMonth = (XSeason*3), ((XSeason*3)+2)
        if (XMonth.LE.12) then
            if (Monthly(XYear,XMonth).NE.MissVal) then
              OpTot  = OpTot  + (Monthly(XYear,XMonth)*MonthLengths(XYear,XMonth))
              OpEn   = OpEn   +  MonthLengths(XYear,XMonth)
            else
              OpMiss = OpMiss +  MonthLengths(XYear,XMonth)
            end if
        else
          if (XYear.LT.YearN) then
            if (Monthly((XYear+1),(XMonth-12)).NE.MissVal) then
              OpTot  = OpTot  + (Monthly((XYear+1),(XMonth-12))*MonthLengths((XYear+1),(XMonth-12)))
              OpEn   = OpEn   +  MonthLengths((XYear+1),(XMonth-12))
            else
              OpMiss = OpMiss +  MonthLengths((XYear+1),(XMonth-12))
            end if
          else
              OpMiss = OpMiss + 30
          end if
        end if
      end do
      
      if (OpMiss.EQ.0) then
        Seasonal(XYear,XSeason) = OpTot / OpEn
      else
        Seasonal(XYear,XSeason) = MissVal
      end if
    end do
    
    OpMiss = 0
    OpTot  = 0
    OpEn   = 0
      
    do XMonth = 1, 12
      if (Monthly(XYear,XMonth).NE.MissVal) then
              OpTot  = OpTot  + (Monthly(XYear,XMonth)*MonthLengths(XYear,XMonth))
              OpEn   = OpEn   +  MonthLengths(XYear,XMonth)
      else
              OpMiss = OpMiss +  MonthLengths(XYear,XMonth)
      end if      
    end do

    if (OpMiss.EQ.0) then
        Annual(XYear) = OpTot / OpEn
    else
        Annual(XYear) = MissVal
    end if
  end do
else
  print*, "  > FillSeaAnnMean: array size mismatch."
end if

deallocate (MonthLengths, stat=AllocStat)
if (AllocStat.NE.0) print*, "  > ##### ERROR: FillSeaAnnMean: Deallocation failure #####"

end subroutine FillSeaAnnMean

!*******************************************************************************
! fill seasonal and annual arrays with minimum values from the monthly array

subroutine FillSeaAnnMin (YearAD,Monthly,Seasonal,Annual)

real, dimension (:,:), pointer 		:: Monthly, Seasonal
real, dimension (:), pointer 		:: Annual

integer, dimension (:), pointer 	:: YearAD

real, parameter :: MissVal = -999.0

real :: OpMin, OpMiss

integer :: ReadStatus, Abandon
integer :: YearN
integer :: XYear, XMonth, XSeason

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

Abandon = 0

YearN = size (YearAD)					! get and check sizes
if      (YearN.NE.size(Monthly,1).OR.YearN.NE.size(Seasonal,1).OR.YearN.NE.size(Annual)) then
	Abandon = 1
else if (12.NE.size(Monthly,2).OR.4.NE.size(Seasonal,2)) then
	Abandon = 1
end if

if (Abandon.EQ.0) then
  do XYear = 1, YearN
    do XSeason = 1, 4
      OpMin  = 100000.0
      OpMiss = 0
      
      do XMonth = (XSeason*3), ((XSeason*3)+2)
        if (XMonth.LE.12) then
            if (Monthly(XYear,XMonth).NE.MissVal) then
              if (Monthly(XYear,XMonth).LT.OpMin) OpMin = Monthly(XYear,XMonth)
            else
              OpMiss = OpMiss + 1
            end if
        else
          if (XYear.LT.YearN) then
            if (Monthly((XYear+1),(XMonth-12)).NE.MissVal) then
              if (Monthly((XYear+1),(XMonth-12)).LT.OpMin) OpMin = Monthly((XYear+1),(XMonth-12))
            else
              OpMiss = OpMiss + 1
            end if
          else
              OpMiss = OpMiss + 1
          end if
        end if
      end do
      
      if (OpMiss.EQ.0) then
        Seasonal(XYear,XSeason) = OpMin
      else
        Seasonal(XYear,XSeason) = MissVal
      end if
    end do
    
    OpMin  = 100000.0
    OpMiss = 0

    do XMonth = 1, 12
      OpMin  = 100000.0
      OpMiss = 0
      
      if (Monthly(XYear,XMonth).NE.MissVal) then
              if (Monthly(XYear,XMonth).LT.OpMin) OpMin = Monthly(XYear,XMonth)
      else
              OpMiss = OpMiss + 1
      end if
    end do

    if (OpMiss.EQ.0) then
        Annual(XYear) = OpMin
    else
        Annual(XYear) = MissVal
    end if
  end do
else
  print*, "  > FillSeaAnnMin: array size mismatch."
end if

end subroutine FillSeaAnnMin

!*******************************************************************************
! fill seasonal and annual arrays with Maximum values from the monthly array

subroutine FillSeaAnnMax (YearAD,Monthly,Seasonal,Annual)

real, dimension (:,:), pointer 		:: Monthly, Seasonal
real, dimension (:), pointer 		:: Annual

integer, dimension (:), pointer 	:: YearAD

real, parameter :: MissVal = -999.0

real :: OpMax, OpMiss

integer :: ReadStatus, Abandon
integer :: YearN
integer :: XYear, XMonth, XSeason

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

Abandon = 0

YearN = size (YearAD)					! get and check sizes
if      (YearN.NE.size(Monthly,1).OR.YearN.NE.size(Seasonal,1).OR.YearN.NE.size(Annual)) then
	Abandon = 1
else if (12.NE.size(Monthly,2).OR.4.NE.size(Seasonal,2)) then
	Abandon = 1
end if

if (Abandon.EQ.0) then
  do XYear = 1, YearN
    do XSeason = 1, 4
      OpMax  = -100000.0
      OpMiss = 0
      
      do XMonth = (XSeason*3), ((XSeason*3)+2)
        if (XMonth.LE.12) then
            if (Monthly(XYear,XMonth).NE.MissVal) then
              if (Monthly(XYear,XMonth).GT.OpMax) OpMax = Monthly(XYear,XMonth)
            else
              OpMiss = OpMiss + 1
            end if
        else
          if (XYear.LT.YearN) then
            if (Monthly((XYear+1),(XMonth-12)).NE.MissVal) then
              if (Monthly((XYear+1),(XMonth-12)).GT.OpMax) OpMax = Monthly((XYear+1),(XMonth-12))
            else
              OpMiss = OpMiss + 1
            end if
          else
              OpMiss = OpMiss + 1
          end if
        end if
      end do
      
      if (OpMiss.EQ.0) then
        Seasonal(XYear,XSeason) = OpMax
      else
        Seasonal(XYear,XSeason) = MissVal
      end if
    end do
    
    OpMax  = -100000.0
    OpMiss = 0
      
    do XMonth = 1, 12
      if (Monthly(XYear,XMonth).NE.MissVal) then
              if (Monthly(XYear,XMonth).GT.OpMax) OpMax = Monthly(XYear,XMonth)
      else
              OpMiss = OpMiss + 1
      end if
    end do

    if (OpMiss.EQ.0) then
        Annual(XYear) = OpMax
    else
        Annual(XYear) = MissVal
    end if
  end do
else
  print*, "  > FillSeaAnnMax: array size mismatch."
end if

end subroutine FillSeaAnnMax

!*******************************************************************************
! fill seasonal and annual arrays with sums from the monthly array

subroutine FillSeaAnnSum (YearAD,Monthly,Seasonal,Annual)

real, dimension (:,:), optional 	:: Monthly, Seasonal
real, dimension (:), optional 		:: Annual

integer, dimension (:), pointer 	:: YearAD

real, parameter :: MissVal = -999.0

real :: OpTot, OpEn, OpMiss

integer :: ReadStatus, Abandon, AllocStat
integer :: YearN
integer :: XYear, XMonth, XSeason

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

Abandon = 0

YearN = size (YearAD)					! get and check sizes
if      (YearN.NE.size(Monthly,1).OR.YearN.NE.size(Seasonal,1).OR.YearN.NE.size(Annual)) then
	Abandon = 1
else if (12.NE.size(Monthly,2).OR.4.NE.size(Seasonal,2)) then
	Abandon = 1
end if

if (Abandon.EQ.0) then
  do XYear = 1, YearN
    do XSeason = 1, 4
      OpMiss = 0
      OpTot  = 0
      
      do XMonth = (XSeason*3), ((XSeason*3)+2)
        if (XMonth.LE.12) then
            if (Monthly(XYear,XMonth).NE.MissVal) then
              OpTot  = OpTot  +  Monthly(XYear,XMonth)
            else
              OpMiss = OpMiss +  1
            end if
        else
          if (XYear.LT.YearN) then
            if (Monthly((XYear+1),(XMonth-12)).NE.MissVal) then
              OpTot  = OpTot  +  Monthly((XYear+1),(XMonth-12))
            else
              OpMiss = OpMiss +  1
            end if
          else
              OpMiss = OpMiss +  1
          end if
        end if
      end do
      
      if (OpMiss.EQ.0) then
        Seasonal(XYear,XSeason) = OpTot
      else
        Seasonal(XYear,XSeason) = MissVal
      end if
    end do
    
    OpMiss = 0
    OpTot  = 0
      
    do XMonth = 1, 12
      if (Monthly(XYear,XMonth).NE.MissVal) then
              OpTot  = OpTot  +  Monthly(XYear,XMonth)
      else
              OpMiss = OpMiss +  1
      end if      
    end do

    if (OpMiss.EQ.0) then
        Annual(XYear) = OpTot
    else
        Annual(XYear) = MissVal
    end if
  end do
else
  print*, "  > FillSeaAnnSum: array size mismatch."
end if

end subroutine FillSeaAnnSum

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

end module CETGeneral

