! grid.f90
! module to hold standard routines for manipulating grids
! contains: GrabGrid,MakeHiResGrid,DefineShrinking
! FileNames,GrimFiles, must be further up the call line

module Grid

implicit none

contains

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

subroutine GrabGrid (CallMan,Grid,Bounds,BoxN,Quiet)

use FileNames
use GrimFiles

real, pointer, dimension (:,:,:) 	:: Data

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

real, dimension(4) 			:: Bounds

integer, intent (in), optional 	:: Quiet		! minimises verbosity
integer, intent (in) 		:: CallMan		! 1,2,MissVal
integer, intent (out) 		:: BoxN			! will be MissVal if QMan=2

real, parameter :: MissVal = -999.0

integer :: ReadStatus, AllocStat
integer :: QMan
integer :: ExeN,WyeN,XBound

character (len=80) :: Path,Info
character (len= 4) :: Suffix

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

if (CallMan.EQ.MissVal) then
  print*, "  > Define from grim (=1), or manually (=2) ?"
  do
	read (*,*,iostat=ReadStatus), QMan
	if (ReadStatus.LE.0.AND.QMan.GE.1.AND.QMan.LE.2) exit
  end do
else
  QMan = CallMan
end if

if (QMan.EQ.1) then
  print*, "  > Enter the grim filepath: "
  do
	read (*,*,iostat=ReadStatus), Path
	if (ReadStatus.LE.0.AND.Path.NE."") exit
  end do

  if (present(Quiet)) then
  	call LoadGrim (Data,Grid,YearAD,Bounds,Info,Path,"    ",Suffix,Quiet=1)
  else
  	call LoadGrim (Data,Grid,YearAD,Bounds,Info,Path,"    ",Suffix)
  end if
  
  ExeN = size(Grid,1) ; WyeN = size (Grid,2) ; BoxN = size (Data,3)

  print "(a,3i8)", "   > Grid dimensions and domain size: ", ExeN, WyeN, BoxN

  deallocate (Data,YearAD,stat=AllocStat)
  if (AllocStat.NE.0) print*, "  > ##### ERROR: GrabGrid: Deallocation failure #####"
else
  print*, "  > Enter the long and lat dimensions (boxes): "
  do
	read (*,*,iostat=ReadStatus), ExeN, WyeN
	if (ReadStatus.LE.0.AND.ExeN.GE.1.AND.WyeN.GE.1) exit
  end do
  
  allocate (Grid(ExeN,WyeN),stat=AllocStat)
  if (AllocStat.NE.0) print*, "  > ##### ERROR: GrabGrid: Allocation failure #####"
  Grid   = MissVal
  Bounds = MissVal
  BoxN   = MissVal

  print*, "  > Enter the min,max long and min,max lat: "
  do
	read (*,*,iostat=ReadStatus), (Bounds(XBound),XBound=1,4)
	
	if (Bounds(1).LT.-180.OR.Bounds(2).GT.180.OR.Bounds(1).GE.Bounds(2)) then
	  print*, "  > There is a problem with the longitudes. Try again."
	  ReadStatus = 1
	end if
	
	if (Bounds(3).LT. -90.OR.Bounds(4).GT. 90.OR.Bounds(3).GE.Bounds(4)) then
	  print*, "  > There is a problem with the latitudes.  Try again."
	  ReadStatus = 1
	end if
	
	if (ReadStatus.LE.0) exit
  end do
  
end if

end subroutine GrabGrid

!*******************************************************************************
! the Call integers refer to the magnification and may be set to MissVal
! GridLo must be allocated in the call
! GridHi and LoToHi are returned from the subroutine
! GridHi (ExeHiN,WyeHiN) has no missing values, containing indices 1...(ExeHiN*WyeHiN)
! LoToHi (ExeHiN,WyeHiN) contains the most-contributing box from GridLo
!   and will be set to MissVal if there is no single most-contributing box

subroutine MakeHiResGrid (CallExeMulti,CallWyeMulti,GridLo,GridHi,LoToHi)

integer, pointer, dimension (:,:) 	:: GridLo,GridHi,LoToHi

real, intent (in) :: CallExeMulti,CallWyeMulti

real, parameter :: MissVal = -999.0

real :: ExeMulti,WyeMulti

integer :: AllocStat,ReadStatus
integer :: ExeLoN,WyeLoN,ExeHiN,WyeHiN
integer :: XExeLo,XWyeLo,XExeHi,XWyeHi,XExeAdd,XWyeAdd

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

ExeLoN = size (GridLo,1) ; WyeLoN = size (GridLo,2)

if (CallExeMulti.EQ.MissVal.OR.CallWyeMulti.EQ.MissVal) then
  print*, "  > Enter the magnification ratio for the long,lat axes: "
  do
	read (*,*,iostat=ReadStatus), ExeMulti, WyeMulti
	if (ReadStatus.LE.0.AND.ExeMulti.GE.1.AND.WyeMulti.GE.1) exit
  end do
else
  ExeMulti = CallExeMulti
  WyeMulti = CallWyeMulti
end if

ExeHiN = nint (ExeMulti * ExeLoN) ; WyeHiN = nint (WyeMulti * WyeLoN)

if (real(ExeHiN).EQ.(ExeMulti*real(ExeLoN)).AND.real(WyeHiN).EQ.(WyeMulti*real(WyeLoN))) then
  allocate (GridHi(ExeHiN,WyeHiN), &
  	    LoToHi(ExeHiN,WyeHiN), stat=AllocStat)
  if (AllocStat.NE.0) print*, "  > ##### ERROR: MakeHiResGrid: Allocation failure #####"
  
  do XExeHi = 1, ExeHiN
    do XWyeHi = 1, WyeHiN
      GridHi(XExeHi,XWyeHi) = ((XExeHi-1) * WyeHiN) + XWyeHi 
    end do
  end do

  do XExeLo = 1, ExeLoN
    do XExeAdd = 1, floor(ExeMulti)
      XExeHi = nint(real(XExeAdd) + real(XExeLo-1)*real(ExeMulti))
      
      do XWyeLo = 1, WyeLoN
        do XWyeAdd = 1, floor(WyeMulti)
          XWyeHi = nint(real(XWyeAdd) + real(XWyeLo-1)*real(WyeMulti))
          
          LoToHi(XExeHi,XWyeHi) = GridLo(XExeLo,XWyeLo)
        end do
      end do
    end do
  end do
else
  print*, "  > MakeHiResGrid: no grid created: incompatible with bounds."
end if

end subroutine MakeHiResGrid

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

subroutine DefineShrinking (OrigBounds,ShrinkBounds,OrigGrid,ShrinkGrid,ReGrid,ValidShrink)

integer, pointer, dimension (:,:)	:: OrigGrid,ShrinkGrid,ReGrid

real, dimension (4) 			:: OrigBounds,ShrinkBounds

integer, intent (out) 	:: ValidShrink

real, parameter :: MissVal = -999.0

real :: OrigLonPerBox,OrigLatPerBox
real :: ShrinkLonPerBox,ShrinkLatPerBox

integer :: AllocStat,ReadStatus 
integer :: TestBoxN,OrigExeN,OrigWyeN,OrigGlobalN
integer :: ShrinkExeN,ShrinkWyeN,ShrinkGlobalN,ShrinkExeCept,ShrinkWyeCept,ShrinkBoxN
integer :: XShrinkExe,XShrinkWye,XShrinkBox
integer :: QChangeLat,QChangeLon

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

OrigExeN = size (OrigGrid,1) ; OrigWyeN = size (OrigGrid,2)
ShrinkExeN = size (ShrinkGrid,1) ; ShrinkWyeN = size (ShrinkGrid,2)
ShrinkBoxN = maxval (ShrinkGrid)

OrigLonPerBox = (OrigBounds(2) - OrigBounds(1)) / real(OrigExeN)
OrigLatPerBox = (OrigBounds(4) - OrigBounds(3)) / real(OrigWyeN)

ShrinkLonPerBox = (ShrinkBounds(2) - ShrinkBounds(1)) / real(ShrinkExeN)
ShrinkLatPerBox = (ShrinkBounds(4) - ShrinkBounds(3)) / real(ShrinkWyeN)

OrigGlobalN   = nint (360.0 / OrigLonPerBox)   * nint (180.0 / OrigLatPerBox)  
ShrinkGlobalN = nint (360.0 / ShrinkLonPerBox) * nint (180.0 / ShrinkLatPerBox)

ValidShrink = 0

if (OrigGlobalN.NE.ShrinkGlobalN) then
  QChangeLat = 2 ; QChangeLon = 2
  print*, "  > The original and shrunken grids do not precisely match."
  
  if (OrigLonPerBox.NE.ShrinkLonPerBox) then
    print "(a,f12.4)", "   > The shrunken longitude box dimension is: ", ShrinkLonPerBox
    print "(a,f12.4)", "   > Decide (1=no,2=yes) whether to make it:  ", OrigLonPerBox
    do
	read (*,*,iostat=ReadStatus), QChangeLon
	if (ReadStatus.LE.0.AND.QChangeLon.GE.1.AND.QChangeLon.LE.2) exit
    end do
    if (QChangeLon.EQ.2) ShrinkLonPerBox = OrigLonPerBox
  end if
  
  if (OrigLatPerBox.NE.ShrinkLatPerBox) then
    print "(a,f12.4)", "   > The shrunken latitude box dimension is:  ", ShrinkLatPerBox
    print "(a,f12.4)", "   > Decide (1=no,2=yes) whether to make it:  ", OrigLatPerBox
    do
	read (*,*,iostat=ReadStatus), QChangeLat
	if (ReadStatus.LE.0.AND.QChangeLat.GE.1.AND.QChangeLat.LE.2) exit
    end do
    if (QChangeLat.EQ.2) ShrinkLatPerBox = OrigLatPerBox
  end if
  
  if (QChangeLat.EQ.1.OR.QChangeLon.EQ.1) then 
  	ValidShrink = 1
  	print*, "  > The original and shrunken grids do not match. The End."
  end if
end if

if (ValidShrink.NE.1) then
  if ((ShrinkBounds(1)-OrigBounds(1)).NE.0) then
  	ShrinkExeCept = nint ((ShrinkBounds(1)-OrigBounds(1)) / OrigLonPerBox)
  else
  	ShrinkExeCept = 0
  end if

  if ((ShrinkBounds(3)-OrigBounds(3)).NE.0) then
  	ShrinkWyeCept = nint ((ShrinkBounds(3)-OrigBounds(3)) / OrigLatPerBox)
  else
  	ShrinkWyeCept = 0
  end if
  
  print "(a,i8)", "   > Globalised original and shrunk grid sizes are both: ", OrigGlobalN

  allocate (ReGrid (ShrinkExeN,ShrinkWyeN), stat=AllocStat)
  if (AllocStat.NE.0) print*, "  > ##### ERROR: DefineReGrid: Allocation failure #####"

  TestBoxN = 0
  do XShrinkExe = 1, ShrinkExeN
   do XShrinkWye = 1, ShrinkWyeN
    ReGrid(XShrinkExe,XShrinkWye) = OrigGrid((XShrinkExe+ShrinkExeCept),(XShrinkWye+ShrinkWyeCept))
    if (ReGrid(XShrinkExe,XShrinkWye).NE.MissVal) TestBoxN = TestBoxN + 1
   end do
  end do

  if (ShrinkBoxN.EQ.MissVal) then					! shrink grid not yet defined
   ShrinkBoxN = TestBoxN
  
   XShrinkBox = 0							! ...so we define it
   do XShrinkExe = 1, ShrinkExeN
    do XShrinkWye = 1, ShrinkWyeN
      if (ReGrid(XShrinkExe,XShrinkWye).NE.MissVal) then
        XShrinkBox = XShrinkBox + 1
        ShrinkGrid(XShrinkExe,XShrinkWye) = XShrinkBox
      end if
    end do
   end do
  
   print "(a,i8)", "   > Shrink grids defined. Shrunken domain size: ", ShrinkBoxN
  else									! shrink grid already defined
   if (ShrinkBoxN.NE.TestBoxN) then					! ...and domain size does not match 
    print*, ShrinkBoxN,TestBoxN
    
    print*, "  > Mismatch in size of shrunken domain. File not shrunk."    
    ValidShrink = 1
   else									! ...and domain size does match
    XShrinkBox = 0							! ...so we check the domain
    do XShrinkExe = 1, ShrinkExeN
      do XShrinkWye = 1, ShrinkWyeN
        if (ReGrid(XShrinkExe,XShrinkWye).NE.MissVal) then
          XShrinkBox = XShrinkBox + 1
          if (ShrinkGrid(XShrinkExe,XShrinkWye).NE.XShrinkBox) ValidShrink = 1
        else
          if (ShrinkGrid(XShrinkExe,XShrinkWye).NE.MissVal)    ValidShrink = 1
        end if
      end do
    end do
    
    if (ValidShrink.EQ.1) then
    	print*, "  > Mismatch in cells of shrunken domain. File not shrunk."    	
    else
        print "(a,i8)", "   > Shrink grids checked v. original grid. Shrunken domain size: ", ShrinkBoxN
    end if
   end if
  end if
end if

end subroutine DefineShrinking

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

end module Grid
