! koeppen.f90
! module procedure written by Tim Mitchell on 02.10.03

module Koeppen

implicit none

contains

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

subroutine GetKoeppen (Tmp,Pre,Boreal,KopNumb,KopAcro,KopName)

logical,dimension (12)		:: Winter,Summer
real,dimension (12),intent(in)	:: Tmp,Pre	! monthly tmp(degC),pre(mm)

logical,intent(in)		:: Boreal	! .TRUE. if boreal, else austral
integer,intent(out)		:: KopNumb	! Koeppen class as a number
character (len=3),intent(out)	:: KopAcro	! Koeppen class as an acronym
character (len=30),intent(out)	:: KopName	! Koeppen class as a name

real, parameter 	:: MissVal = -999.0

real :: TmpMin,PreMin,TmpMax,PreMax
real :: WinterPre,SummerPre,WinterPreMin,SummerPreMin,WinterPreMax,SummerPreMax
real :: Desert,Steppe,AnnualPre,AnnualTmp

integer :: Kop,XMonth

Winter=.FALSE. ; Summer=.FALSE.

if (Boreal) then
  Summer(4)=.TRUE. ; Summer(5)=.TRUE. ; Summer(6)=.TRUE. 
  Summer(7)=.TRUE. ; Summer(8)=.TRUE. ; Summer(9)=.TRUE. 
  Winter(1)=.TRUE. ; Winter(2)=.TRUE. ; Winter(3)=.TRUE. 
  Winter(10)=.TRUE. ; Winter(11)=.TRUE. ; Winter(12)=.TRUE. 
else
  Winter(4)=.TRUE. ; Winter(5)=.TRUE. ; Winter(6)=.TRUE. 
  Winter(7)=.TRUE. ; Winter(8)=.TRUE. ; Winter(9)=.TRUE. 
  Summer(1)=.TRUE. ; Summer(2)=.TRUE. ; Summer(3)=.TRUE. 
  Summer(10)=.TRUE. ; Summer(11)=.TRUE. ; Summer(12)=.TRUE. 
end if

! TmpMin=minval(Tmp) ; PreMin=minval(Pre) ; TmpMax=maxval(Tmp) ; PreMax=maxval(Pre)
! WinterPre=sum(Pre,Winter) ; SummerPre=sum(Pre,Summer)
! WinterPreMin=minval(Pre,Winter) ; SummerPreMin=minval(Pre,Summer)
! WinterPreMax=maxval(Pre,Winter) ; SummerPreMax=maxval(Pre,Summer)
! AnnualPre=sum(Pre) ; AnnualTmp=sum(Tmp)/12.0

TmpMin=100000 ; TmpMax=-100000 ; PreMin=100000 ; PreMax=-100000
AnnualTmp=0 ; AnnualPre=0 ; WinterPre=0 ; SummerPre=0
WinterPreMin=100000 ; WinterPreMax=-100000 ; SummerPreMin=100000 ; SummerPreMax=-100000
do XMonth=1,12
  if (Tmp(XMonth).LT.TmpMin) TmpMin=Tmp(XMonth)
  if (Tmp(XMonth).GT.TmpMax) TmpMax=Tmp(XMonth)
  if (Pre(XMonth).LT.PreMin) PreMin=Pre(XMonth)
  if (Pre(XMonth).GT.PreMax) PreMax=Pre(XMonth)
  
  AnnualTmp=AnnualTmp+Tmp(XMonth)
  AnnualPre=AnnualPre+Pre(XMonth)
  
  if (Winter(XMonth)) then
    WinterPre=WinterPre+Pre(XMonth)
    if (Pre(XMonth).LT.WinterPreMin) WinterPreMin=Pre(XMonth)
    if (Pre(XMonth).GT.WinterPreMax) WinterPreMax=Pre(XMonth)
  else
    SummerPre=SummerPre+Pre(XMonth)
    if (Pre(XMonth).LT.SummerPreMin) SummerPreMin=Pre(XMonth)
    if (Pre(XMonth).GT.SummerPreMax) SummerPreMax=Pre(XMonth)
  end if
end do
AnnualTmp=AnnualTmp/12.0

if (TmpMin.EQ.MissVal.OR.PreMin.EQ.MissVal) then	! data missing
  Kop=MissVal ; KopAcro="" ; KopName="unclassifiable"
else
  if      (WinterPre.GE.(0.7*AnnualPre)) then
    Steppe = AnnualTmp * 20.0
    Desert = AnnualTmp * 10.0
  else if (SummerPre.GE.(0.7*AnnualPre)) then
    Steppe = (AnnualTmp * 20.0) + 280
    Desert = (AnnualTmp * 10.0) + 140
  else
    Steppe = (AnnualTmp * 20.0) + 140
    Desert = (AnnualTmp * 10.0) +  70
  end if
  
  if      (AnnualPre.LE.Steppe) then			! Dry
    if (AnnualPre.LE.Desert) then				! Dry desert
      Kop=1 ; KopAcro="Bw" ; KopName="desert"
    else							! Dry steppe
      Kop=9 ; KopAcro="Bs" ; KopName="steppe"
    end if
    
    if (AnnualTmp.GT.18.0) then					! Dry warm 
      Kop=Kop+0	; KopAcro(2:2)="h" ; KopName="Dry warm "//KopName(1:6)		
    else							! Dry cool 
      Kop=Kop+4	; KopAcro(2:2)="k" ; KopName="Dry cool "//KopName(1:6)			
    end if
  else if (TmpMin.GT.18.0) then				! Tropical
    if      (PreMin.GT.60.0) then
      Kop=17 ; KopAcro="Af" ; KopName="Tropical always-wet"	! Tropical always-wet
    else if (AnnualPre.LT.(2540.0-(25.0*PreMin))) then
      Kop=21 ; KopAcro="Aw" ; KopName="Tropical wet-and-dry"	! Tropical wet-and-dry
    else							
      Kop=25 ; KopAcro="Am" ; KopName="Tropical monsoon"	! Tropical monsoon
    end if
  else if (TmpMax.GT.10.0) then	
    if (TmpMin.GT.-3.0) then
      Kop=29 ; KopAcro="C" ; KopName="Temp"		! Temperate
    else
      Kop=61 ; KopAcro="D" ; KopName="Cold"		! Cold
    end if
    
    if      (SummerPreMin.LT.40.0.AND.SummerPreMin.LT.(0.333*WinterPreMax)) then
      Kop=Kop+0	 ; KopAcro(2:2)="s" ; KopName=KopName(1:4)//" dry-summer"	! summer-dry 
    else if (WinterPreMin.LT.(0.1*SummerPreMax)) then
      Kop=Kop+12 ; KopAcro(2:2)="w" ; KopName=KopName(1:4)//" dry-winter"	! winter-dry 
    else
      Kop=Kop+24 ; KopAcro(2:2)="f" ; KopName=KopName(1:4)//" always-wet"	! always-wet 
    end if
    
    if      (TmpMax.GT.22.0) then
      Kop=Kop+0	; KopAcro(3:3)="a"  				! Temp/Cold warm-summer
      if (KopAcro(2:2).EQ."s") then
        KopName=KopName(1:4)//" warm-dry-summer"
      else
        KopName=KopName(1:15)//" warm-summer"
      end if
    else if (TmpMin.GT.-3.0.OR.count(Tmp.GT.10.0).GE.4) then
      Kop=Kop+4 ; KopAcro(3:3)="b"  				! Temp/Cold cool-summer
      if (KopAcro(2:2).EQ."s") then
        KopName=KopName(1:4)//" cool-dry-summer"
      else
        KopName=KopName(1:15)//" cool-summer"
      end if
    else
      Kop=Kop+8	; KopAcro(3:3)="c"  				! Temp/Cold brief-ssummer
      if (KopAcro(2:2).EQ."s") then
        KopName=KopName(1:4)//" brief-dry-summer"
      else
        KopName=KopName(1:15)//" brief-summer"
      end if
    end if
  else							! Polar
    if (TmpMax.GT.0) then
      Kop=97 ; KopAcro="Et" ; KopName="Polar tundra"		! Polar tundra
    else
      Kop=99 ; KopAcro="Ef" ; KopName="Polar permafrost"	! Polar permafrost
    end if
  end if
end if

KopNumb=MissVal
if (Kop.NE.MissVal) KopNumb=100-Kop

end subroutine GetKoeppen

!*******************************************************************************
! ths was originally used
! it was then modified into the form in GetKoeppen when it was found that on the
!   0.5deg grid some categories were only used for very small percentage of the
!   land surface; to simplify the classification these were removed

subroutine GetKoeppenOld (Tmp,Pre,Boreal,KopNumb,KopAcro,KopName)

logical,dimension (12)		:: Winter,Summer
real,dimension (12),intent(in)	:: Tmp,Pre	! monthly tmp(degC),pre(mm)

logical,intent(in)		:: Boreal	! .TRUE. if boreal, else austral
integer,intent(out)		:: KopNumb	! Koeppen class as a number
character (len=3),intent(out)	:: KopAcro	! Koeppen class as an acronym
character (len=30),intent(out)	:: KopName	! Koeppen class as a name

real, parameter 	:: MissVal = -999.0

real :: TmpMin,PreMin,TmpMax,PreMax
real :: WinterPre,SummerPre,WinterPreMin,SummerPreMin,WinterPreMax,SummerPreMax
real :: Desert,Steppe,AnnualPre,AnnualTmp

integer :: Kop,XMonth

Winter=.FALSE. ; Summer=.FALSE.

if (Boreal) then
  Summer(4)=.TRUE. ; Summer(5)=.TRUE. ; Summer(6)=.TRUE. 
  Summer(7)=.TRUE. ; Summer(8)=.TRUE. ; Summer(9)=.TRUE. 
  Winter(1)=.TRUE. ; Winter(2)=.TRUE. ; Winter(3)=.TRUE. 
  Winter(10)=.TRUE. ; Winter(11)=.TRUE. ; Winter(12)=.TRUE. 
else
  Winter(4)=.TRUE. ; Winter(5)=.TRUE. ; Winter(6)=.TRUE. 
  Winter(7)=.TRUE. ; Winter(8)=.TRUE. ; Winter(9)=.TRUE. 
  Summer(1)=.TRUE. ; Summer(2)=.TRUE. ; Summer(3)=.TRUE. 
  Summer(10)=.TRUE. ; Summer(11)=.TRUE. ; Summer(12)=.TRUE. 
end if

! TmpMin=minval(Tmp) ; PreMin=minval(Pre) ; TmpMax=maxval(Tmp) ; PreMax=maxval(Pre)
! WinterPre=sum(Pre,Winter) ; SummerPre=sum(Pre,Summer)
! WinterPreMin=minval(Pre,Winter) ; SummerPreMin=minval(Pre,Summer)
! WinterPreMax=maxval(Pre,Winter) ; SummerPreMax=maxval(Pre,Summer)
! AnnualPre=sum(Pre) ; AnnualTmp=sum(Tmp)/12.0

TmpMin=100000 ; TmpMax=-100000 ; PreMin=100000 ; PreMax=-100000
AnnualTmp=0 ; AnnualPre=0 ; WinterPre=0 ; SummerPre=0
WinterPreMin=100000 ; WinterPreMax=-100000 ; SummerPreMin=100000 ; SummerPreMax=-100000
do XMonth=1,12
  if (Tmp(XMonth).LT.TmpMin) TmpMin=Tmp(XMonth)
  if (Tmp(XMonth).GT.TmpMax) TmpMax=Tmp(XMonth)
  if (Pre(XMonth).LT.PreMin) PreMin=Pre(XMonth)
  if (Pre(XMonth).GT.PreMax) PreMax=Pre(XMonth)
  
  AnnualTmp=AnnualTmp+Tmp(XMonth)
  AnnualPre=AnnualPre+Pre(XMonth)
  
  if (Winter(XMonth)) then
    WinterPre=WinterPre+Pre(XMonth)
    if (Pre(XMonth).LT.WinterPreMin) WinterPreMin=Pre(XMonth)
    if (Pre(XMonth).GT.WinterPreMax) WinterPreMax=Pre(XMonth)
  else
    SummerPre=SummerPre+Pre(XMonth)
    if (Pre(XMonth).LT.SummerPreMin) SummerPreMin=Pre(XMonth)
    if (Pre(XMonth).GT.SummerPreMax) SummerPreMax=Pre(XMonth)
  end if
end do
AnnualTmp=AnnualTmp/12.0

if (TmpMin.EQ.MissVal.OR.PreMin.EQ.MissVal) then	! data missing
  Kop=MissVal ; KopAcro="" ; KopName="unclassifiable"
else
  if      (WinterPre.GE.(0.7*AnnualPre)) then
    Steppe = AnnualTmp * 20.0
    Desert = AnnualTmp * 10.0
  else if (SummerPre.GE.(0.7*AnnualPre)) then
    Steppe = (AnnualTmp * 20.0) + 280
    Desert = (AnnualTmp * 10.0) + 140
  else
    Steppe = (AnnualTmp * 20.0) + 140
    Desert = (AnnualTmp * 10.0) +  70
  end if
  
  if      (AnnualPre.LE.Steppe) then			! Dry
    if (AnnualPre.LE.Desert) then				! Dry desert
      Kop=1 ; KopAcro="Bw" ; KopName="desert"
    else							! Dry steppe
      Kop=9 ; KopAcro="Bs" ; KopName="steppe"
    end if
    
    if (AnnualTmp.GT.18.0) then					! Dry warm 
      Kop=Kop+0	; KopAcro(2:2)="h" ; KopName="Dry warm "//KopName(1:6)		
    else							! Dry cool 
      Kop=Kop+4	; KopAcro(2:2)="k" ; KopName="Dry cool "//KopName(1:6)			
    end if
  else if (TmpMin.GT.18.0) then				! Tropical
    if      (PreMin.GT.60.0) then
      Kop=20 ; KopAcro="Af" ; KopName="Tropical always-wet"	! Tropical always-wet
    else if (AnnualPre.LT.(2540.0-(25.0*PreMin))) then
      Kop=25 ; KopAcro="Aw" ; KopName="Tropical wet-and-dry"	! Tropical wet-and-dry
    else							
      Kop=30 ; KopAcro="Am" ; KopName="Tropical monsoon"	! Tropical monsoon
    end if
  else if (TmpMax.GT.10.0) then	
    if (TmpMin.GT.-3.0) then
      Kop=36 ; KopAcro="C" ; KopName="Temp"		! Temperate
    else
      Kop=60 ; KopAcro="D" ; KopName="Cold"		! Cold
    end if
    
    if      (SummerPreMin.LT.40.0.AND.SummerPreMin.LT.(0.333*WinterPreMax)) then
      Kop=Kop+0	 ; KopAcro(2:2)="s" ; KopName=KopName(1:4)//" dry-summer"	! summer-dry 
    else if (WinterPreMin.LT.(0.1*SummerPreMax)) then
      Kop=Kop+8	 ; KopAcro(2:2)="w" ; KopName=KopName(1:4)//" dry-winter"	! winter-dry 
    else
      Kop=Kop+16 ; KopAcro(2:2)="f" ; KopName=KopName(1:4)//" always-wet"	! always-wet 
    end if
    
    if      (TmpMax.GT.22.0) then
      Kop=Kop+0	; KopAcro(3:3)="a"  				! Temp/Cold warm-summer
      if (KopAcro(2:2).EQ."s") then
        KopName=KopName(1:4)//" warm-dry-summer"
      else
        KopName=KopName(1:15)//" warm-summer"
      end if
    else if (count(Tmp.GT.10.0).GE.4) then
      Kop=Kop+2 ; KopAcro(3:3)="b"  				! Temp/Cold cool-summer
      if (KopAcro(2:2).EQ."s") then
        KopName=KopName(1:4)//" cool-dry-summer"
      else
        KopName=KopName(1:15)//" cool-summer"
      end if
    else if (TmpMin.GT.-38.0) then
      Kop=Kop+4	; KopAcro(3:3)="c"  				! Temp/Cold brief-ssummer
      if (KopAcro(2:2).EQ."s") then
        KopName=KopName(1:4)//" brief-dry-summer"
      else
        KopName=KopName(1:15)//" brief-summer"
      end if
    else
      Kop=Kop+6	; KopAcro(3:3)="d"  				! Cold 	    hard-winter
      if (KopAcro(2:2).EQ."w") then
        KopName=KopName(1:4)//" hard-dry-winter"
      else
        KopName=KopName(1:15)//" hard-winter"
      end if
    end if
  else							! Polar
    if (TmpMax.GT.0) then
      Kop=93 ; KopAcro="Et" ; KopName="Polar tundra"		! Polar tundra
    else
      Kop=97 ; KopAcro="Ef" ; KopName="Polar permafrost"	! Polar permafrost
    end if
    
    if (TmpMin.LT.-38.0) then					! Polar hard-winter
      Kop=Kop+2	; KopAcro(3:3)="h" ; KopName=trim(KopName)//" hard-winter"			
    end if
  end if
end if

KopNumb=MissVal
if (Kop.NE.MissVal) KopNumb=100-Kop

end subroutine GetKoeppenOld

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

end module Koeppen
