#include "cppdefs.h"

      MODULE sed_biodiff_mod

#if defined NONLINEAR && defined SEDIMENT && defined SED_BIODIFF
!
!svn $Id: sed_bed_mod.F 2011 2009-12-20 17:34:23Z arango $
!==================================================== C. R. Sherwood ===
!  Copyright (c) 2002-2017 The ROMS/TOMS Group      Hernan G. Arango   !
!    Licensed under a MIT/X style license                              !
!    See License_ROMS.txt                                              !
!=======================================================================
!                                                                      !
!  This routine computes sediment bed layer mixing (biodiffusion)      !
!                                                                      !
!                                                                      !
!=======================================================================
!
      implicit none

      PRIVATE
      PUBLIC  :: sed_biodiff

      CONTAINS
!
!***********************************************************************
      SUBROUTINE sed_biodiff (ng, tile)
!***********************************************************************
!
      USE mod_param
      USE mod_forces
      USE mod_grid
      USE mod_ocean
      USE mod_sedbed
      USE mod_stepping
# ifdef BBL_MODEL
      USE mod_bbl
# endif
!
!  Imported variable declarations.
!
      integer, intent(in) :: ng, tile
!
!  Local variable declarations.
!
# include "tile.h"
!
# ifdef PROFILE
      CALL wclock_on (ng, iNLM, 16)
# endif
      CALL sed_biodiff_tile (ng, tile,                                  &
     &                   LBi, UBi, LBj, UBj,                            &
     &                   IminS, ImaxS, JminS, JmaxS,                    &
     &                   nstp(ng), nnew(ng),                            &
# ifdef WET_DRY
     &                   GRID(ng) % rmask_wet,                          &
# endif
     &                   OCEAN(ng) % t,                                 &
# if defined SED_MORPH
     &                   SEDBED(ng) % bed_thick,                        &
# endif
     &                   SEDBED(ng) % bed,                              &
     &                   SEDBED(ng) % bed_frac,                         &
     &                   SEDBED(ng) % bed_mass,                         &
     &                   SEDBED(ng) % bottom)
# ifdef PROFILE
      CALL wclock_off (ng, iNLM, 16)
# endif
      RETURN
      END SUBROUTINE sed_biodiff
!
!***********************************************************************
      SUBROUTINE sed_biodiff_tile (ng, tile,                            &
     &                         LBi, UBi, LBj, UBj,                      &
     &                         IminS, ImaxS, JminS, JmaxS,              &
     &                         nstp, nnew,                              &
# ifdef WET_DRY
     &                         rmask_wet,                               &
# endif
     &                         t,                                       &
# if defined SED_MORPH
     &                         bed_thick,                               &
# endif
     &                         bed, bed_frac, bed_mass,                 &
     &                         bottom)
!***********************************************************************
!
      USE mod_param
      USE mod_scalars
      USE mod_sediment
!
      USE bc_3d_mod, ONLY : bc_r3d_tile
# ifdef DISTRIBUTE
      USE mp_exchange_mod, ONLY : mp_exchange3d, mp_exchange4d
# endif
!
!  Imported variable declarations.
!
      integer, intent(in) :: ng, tile
      integer, intent(in) :: LBi, UBi, LBj, UBj
      integer, intent(in) :: IminS, ImaxS, JminS, JmaxS
      integer, intent(in) :: nstp, nnew
!
# ifdef ASSUMED_SHAPE
#  ifdef WET_DRY
      real(r8), intent(in) :: rmask_wet(LBi:,LBj:)
#  endif
#  if defined SED_MORPH
      real(r8), intent(inout):: bed_thick(LBi:,LBj:,:)
#  endif
      real(r8), intent(inout) :: t(LBi:,LBj:,:,:,:)
      real(r8), intent(inout) :: bed(LBi:,LBj:,:,:)
      real(r8), intent(inout) :: bed_frac(LBi:,LBj:,:,:)
      real(r8), intent(inout) :: bed_mass(LBi:,LBj:,:,:,:)
      real(r8), intent(inout) :: bottom(LBi:,LBj:,:)
# else
#  ifdef WET_DRY
      real(r8), intent(in) :: rmask_wet(LBi:UBi,LBj:UBj)
#  endif
#  if defined SED_MORPH
      real(r8), intent(inout):: bed_thick(LBi:UBi,LBj:UBj,2)
#  endif
      real(r8), intent(inout) :: t(LBi:UBi,LBj:UBj,N(ng),3,NT(ng))
      real(r8), intent(inout) :: bed(LBi:UBi,LBj:UBj,Nbed,MBEDP)
      real(r8), intent(inout) :: bed_frac(LBi:UBi,LBj:UBj,Nbed,NST)
      real(r8), intent(inout) :: bed_mass(LBi:UBi,LBj:UBj,Nbed,1:2,NST)
      real(r8), intent(inout) :: bottom(LBi:UBi,LBj:UBj,MBOTP)
# endif
!
!  Local variable declarations.
!
      integer :: i, j, k, ised
      real(r8), parameter :: eps = 1.0E-14_r8
      real(r8), parameter :: cmy2ms = 3.1688765E-12_r8 ! multiply cm2/yr by this to get m2/s

      real(r8) :: cff, cff1, cff2, cff3

      real(r8), dimension(IminS:ImaxS,NST) :: dep_mass

      integer :: iu,il,lp,ii
!      real(r8) :: rtemp, Dbmx, Dbmm, zs, zm, zp
      real(r8) :: rtemp
      real(r8), dimension(Nbed) :: zb
      real(r8), dimension(Nbed) :: zc
      real(r8), dimension(Nbed) :: dzui
      real(r8), dimension(Nbed) :: dzli
      real(r8), dimension(Nbed) :: dzmi
      real(r8), dimension(Nbed) :: Db
      real(r8), dimension(Nbed) :: Dc
      real(r8), dimension(Nbed) :: a
      real(r8), dimension(Nbed) :: d
      real(r8), dimension(Nbed) :: b
      real(r8), dimension(Nbed) :: cc
      real(r8), dimension(Nbed) :: dd


# include "set_bounds.h"

!
!-----------------------------------------------------------------------
! Compute sediment bed layer stratigraphy.
!-----------------------------------------------------------------------
!
!      print *, 'Mixing...'
      J_LOOP : DO j=Jstr,Jend
        I_LOOP : DO i=Istr,Iend
!
!  Set mixing coefficient profile
!  (hardwire uniform mixing)
!
!          DO k=1,Nbed
!             Db(k)=1.0E-8_r8
!          ENDDO

          IF (Nbed.GT.2) THEN
!  Compute cumulative depth 
            zb(1)=bed(i,j,1,ithck)
            DO k=2,Nbed
              zb(k)=zb(k-1)+bed(i,j,k,ithck)
            END DO
!  Compute depths of bed centers 
            zc(1)=0.5_r8*(bed(i,j,1,ithck))
            DO k=2,Nbed
              zc(k)=zb(k-1)+0.5_r8*bed(i,j,k,ithck)
            END DO
!
!  Set biodiffusivity profile
#  if defined DB_PROFILE
!  Depth-varying biodiffusivity profile
!   
!ALF            Dbmx = bottom(i,j,idbmx)
!ALF            Dbmm = bottom(i,j,idbmm)
!ALF            zs = bottom(i,j,idbzs)
!ALF            zm = bottom(i,j,idbzm)
!ALF            zp = bottom(i,j,idbzp)
            DO k=1,Nbed
              Db(k)= Dbmx(ng)
!              IF( zb(k).GT.Dbzp(ng))THEN          ! should be .GE. ?
              IF( zb(k).GE.Dbzp(ng))THEN          
                Db(k)=0.0_r8
              ELSE
                IF((zb(k) .GT. Dbzs(ng)).AND.                                 &
     &           (zb(k) .LE. Dbzm(ng))) THEN
                  rtemp= LOG(Dbmm(ng)/Dbmx(ng))/(-Dbzm(ng)-Dbzs(ng))
                  Db(k)=Dbmx(ng)*exp(rtemp*(-zb(k)-Dbzs(ng)))
                ELSEIF((zb(k).GT.Dbzm(ng)).AND.                               &
     &           (zb(k).LT.Dbzp(ng)) ) THEN
                  Db(k)=(Dbmm(ng)-(Dbmm(ng)/(Dbzp(ng)-Dbzm(ng))))*            &
     &                  (zb(k)-Dbzm(ng))
                ENDIF
              ENDIF
            END DO
#  else
!    Uniform biodiffusivity profile at max value 
!
            DO k=1,Nbed
              Db(k)=Dbmx(ng)
!              Db(k)=bottom(i,j,idbmx)
            ENDDO
#  endif /* defined DB_PROFILE */
!           write(*,*) 'Db',Db
!  Calculate finite differences
            dzui(1)=1.0_r8/(zc(2)-zc(1))
            dzli(1)=1.E35_r8       ! should not be needed
            dzmi(1)=1.0_r8/bed(i,j,1,ithck)
            DO k=2,Nbed-1
              dzui(k)=1.0_r8/(zc(k+1)-zc(k))
              dzli(k)=1.0_r8/(zc(k)-zc(k-1))
              !dzmi(k)=1.0_r8/(zb(k+1)-zb(k))
              ! equivalent:
              dzmi(k)=1.0/bed(i,j,k,ithck)
!  Tridiagonal terms
              b(k)= -dt(ng)*dzmi(k)*Db(k-1)*dzli(k)
              d(k)=1.0_r8+dt(ng)*dzmi(k)*                               &
     &          ( Db(k-1)*dzli(k)+Db(k)*dzui(k) )
              a(k)= -dt(ng)*dzmi(k)*Db(k)*dzui(k)
            ENDDO
            dzui(Nbed)=1.0E35_r8 ! should not be needed
            dzli(Nbed)=1.0_r8/(zc(Nbed)-zc(Nbed-1))
            dzmi(Nbed)=1.0_r8/bed(i,j,Nbed,ithck)
!  No-flux boundary conditions
            b(1) = 999.9_r8  ! should not be needed
            d(1)=   1.0_r8 +dt(ng)*dzmi(1)*Db(1)*dzui(1)
            a(1)=      -dt(ng)*dzmi(1)*Db(1)*dzui(1)
            b(Nbed)=   -dt(ng)*dzmi(Nbed)*Db(Nbed-1)*dzli(Nbed)
            d(Nbed)=1.0_r8+dt(ng)*dzmi(Nbed)*Db(Nbed-1)*dzli(Nbed)
            a(Nbed)=999.9_r8 ! should not be needed
!
!   Calculate mixing for each size fraction
            DO ised=1,NST
!   ...make working copies 
              DO k=1,Nbed
                cc(k) = bed_frac(i,j,k,ised)
                dd(k)= d(k)
              ENDDO
!   Solve a tridiagonal system of equations using Thomas' algorithm
!   Anderson, Tannehill, and Pletcher (1984) pp. 549-550
!   ...establish upper triangular matrix
              il = 1
              iu = Nbed
              lp = il+1
              DO k = lp,iu
                rtemp = b(k)/dd(k-1)
                dd(k)= dd(k)-rtemp*a(k-1);
                cc(k)= cc(k)-rtemp*cc(k-1);
              ENDDO
!   ...back substitution
              cc(iu) = cc(iu)/dd(iu)
              DO k  = lp,iu
                ii = iu-k+il;
                cc(ii) = (cc(ii)-a(ii)*cc(ii+1))/dd(ii);
              ENDDO
!   ...solution stored in cc; copy out
              DO k = 1,Nbed
                bed_frac(i,j,k,ised)=cc(k)
              ENDDO           
            ENDDO
! TODO - Mix porosity or assign it as f(depth)?
! TODO - Mix age?

! Recompute bed masses
            DO k=1,Nbed
!             write(*,*) i,j,k,(bed_frac(i,j,k,ised),ised=1,NST)
!            debugging: ensure fracs add up to 1
              cff3 = 0.0_r8
              DO ised=1,NST
                cff3 = cff3+bed_frac(i,j,k,ised)
              ENDDO
              if( abs(1.0_r8-cff3).GT.1e-6 )                            &
     &         write(*,*) 'error: sum_frac: ',cff3
              cff3=0.0_r8
              DO ised=1,NST
                cff3=cff3+bed_mass(i,j,k,nnew,ised)
              ENDDO
              DO ised=1,NST
                bed_mass(i,j,k,nnew,ised)=bed_frac(i,j,k,ised)*cff3
              ENDDO
            ENDDO


          END IF !NBED.GT.2
        END DO I_LOOP
      END DO J_LOOP
!
!-----------------------------------------------------------------------
!  Apply periodic or gradient boundary conditions to property arrays.
!-----------------------------------------------------------------------
!
      DO ised=1,NST
        CALL bc_r3d_tile (ng, tile,                                     &
     &                    LBi, UBi, LBj, UBj, 1, Nbed,                  &
     &                    bed_frac(:,:,:,ised))
        CALL bc_r3d_tile (ng, tile,                                     &
     &                    LBi, UBi, LBj, UBj, 1, Nbed,                  &
     &                    bed_mass(:,:,:,nnew,ised))
      END DO
# ifdef DISTRIBUTE
      CALL mp_exchange4d (ng, tile, iNLM, 2,                            &
     &                    LBi, UBi, LBj, UBj, 1, Nbed, 1, NST,          &
     &                    NghostPoints, EWperiodic, NSperiodic,         &
     &                    bed_frac,                                     &
     &                    bed_mass(:,:,:,nnew,:))
# endif

      DO i=1,MBEDP
        CALL bc_r3d_tile (ng, tile,                                     &
     &                    LBi, UBi, LBj, UBj, 1, Nbed,                  &
     &                    bed(:,:,:,i))
      END DO
# ifdef DISTRIBUTE
      CALL mp_exchange4d (ng, tile, iNLM, 1,                            &
     &                    LBi, UBi, LBj, UBj, 1, Nbed, 1, MBEDP,        &
     &                    NghostPoints, EWperiodic, NSperiodic,         &
     &                    bed)
# endif

      RETURN
      END SUBROUTINE sed_biodiff_tile
#endif
      END MODULE sed_biodiff_mod