Skip to content
Snippets Groups Projects
oxygen_mod.f90 28.7 KiB
Newer Older
  • Learn to ignore specific revisions
  • Victor Stepanenko's avatar
    Victor Stepanenko committed
    MODULE OXYGEN_MOD
    
    
    use NUMERICS, only : PROGONKA, STEP
    
    use NUMERIC_PARAMS, only : vector_length, small_value
    
    use ARRAYS_GRID, only : gridsize_type
    use ARRAYS_BATHYM, only : layers_type, bathym 
    use ARRAYS_GRID, only : gridspacing_type
    use ARRAYS_SOIL, only : lsh_type
    
    use METH_OXYG_CONSTANTS
    
    use DRIVING_PARAMS, only : carbon_model
    
    use DIFFUSION_MOD, only : DIFFUSION
    
    
    real(4), parameter :: k_PA = 1.7 ! n/d, coefficient in Poole&Atkins formula
    real(4), parameter :: extwat1 = k_PA/2., extwat2 = k_PA/3.5 ! Extinction coefficients
                                                              ! identifying eutrophic,
                                                              ! mesotrophic and oligotrophic lakes,
                                                              ! derived from Poole and Atkins (1929) 
                                                              ! formula and data from Stefan and Fang, 1994
    
    real(4), parameter :: chla_aver_oligo = 2.E-3 !2.E-3 ! mg/l, Mean chlorophyll density in oligotrophic lake, Stefan and Fang, 1994
    real(4), parameter :: chla_aver_meso  = 6.E-3 !6.E-3 ! mg/l, Mean chlorophyll density in mesotrophic lake, Stefan and Fang, 1994
    real(4), parameter :: chla_aver_eu    = 15.E-3 !15.E-3 ! mg/l, Mean chlorophyll density in eutrophic lake, Stefan and Fang, 1994
    
    Victor Stepanenko's avatar
    Victor Stepanenko committed
    contains
    
    !> Subroutine OXYGEN calculates vertical diffusion of dissolved oxygen and interaction with bubbles
    
    Victor Stepanenko's avatar
    Victor Stepanenko committed
    SUBROUTINE OXYGEN &
    
    & (gs, Twater, fbbleflx_o2_sum, fbbleflx_o2, lamoxyg, gsp, fracb0, pressure, wind10, &
    
    & o2_pres_atm, ls, bathymwater, lso, dt, febul0, eps_surf, sodbot, oxyg, soilswitch, Flux_atm)
    
    Victor Stepanenko's avatar
    Victor Stepanenko committed
    
    use PHYS_FUNC, only : &
    & HENRY_CONST, &
    & GAS_WATATM_FLUX
    
    use PHYS_CONSTANTS, only : kappa, z0_bot
    
    use PHYS_CONSTANTS, only : &
    & Kelvin0
    
    Victor Stepanenko's avatar
    Victor Stepanenko committed
    
    
    Victor Stepanenko's avatar
    Victor Stepanenko committed
    implicit none
    
    
    type(gridsize_type),    intent(in) :: gs
    
    type(gridspacing_type), intent(in) :: gsp
    
    !> Group containing depths of physical layers
    
    type(layers_type),      intent(in) :: ls
    
    !> Bathymetry characteristics of a water body
    
    type(bathym),           intent(in) :: bathymwater(1:gs%M+1)
    
    !> Oxygen flux at the water-sediments interface
    
    type(lsh_type),         intent(in) :: lso
    
    !> Water temperature profile, Celsius
    real(kind=ireals), intent(in) :: Twater(1:gs%M+1) 
    !> The bubble oxygen flux, horizontally integrated, normalized by bottom value, n/d
    
    real(kind=ireals), intent(inout) :: fbbleflx_o2_sum(0:gs%M+1)
    
    !> The bubble oxygen flux above the deepest soil column, normalized by bottom value, n/d
    real(kind=ireals), intent(in) :: fbbleflx_o2(0:gs%M+1) 
    !> Vertical diffusivity for oxygen, m**2/s
    
    real(kind=ireals), intent(in) :: lamoxyg(1:gs%M)
    
    !> Molar fractions of gases in a bubble
    real(kind=ireals), intent(in) :: fracb0(1:ngasb) 
    !> Bottom methane flux, mol/m**2/s
    
    real(kind=ireals), intent(in) :: febul0
    
    !> Atmospheric pressure, Pa
    real(kind=ireals), intent(in) :: pressure 
    !> Wind speed at 10 m above the surface
    real(kind=ireals), intent(in) :: wind10 
    !> Oxygen partial pressure in the atmosphere, Pa
    real(kind=ireals), intent(in) :: o2_pres_atm 
    !> Time step, s
    
    !> TKE dissipation rate at the water surface, m**2/s**3
    real(kind=ireals), intent(in) :: eps_surf 
    !> Sedimentary oxygen demand (oxygen flux at the bottom), mol/(m**2*s)
    real(kind=ireals), intent(in) :: sodbot 
    !> Oxygen concentration in water, mol/m**3
    
    real(kind=ireals), intent(inout) :: oxyg(1:gs%M+1,1:2)
    
    !> Switch for soil (sediments)
    
    integer(kind=iintegers), intent(in) :: soilswitch
    
    !> Flux to the atmosphere, mol/m**2/s
    
    real(kind=ireals), intent(out) :: Flux_atm
    
    
    Victor Stepanenko's avatar
    Victor Stepanenko committed
    ! Local variables
    
    integer(kind=iintegers), parameter :: water_oxygen_indic = 9
    
    Victor Stepanenko's avatar
    Victor Stepanenko committed
    
    
    real(kind=ireals), allocatable :: a(:), b(:), c(:), f(:), y(:)
    
    Victor Stepanenko's avatar
    Victor Stepanenko committed
    
    
    real(kind=ireals), save :: Foxyg1 = 0.d0 ! Bottom oxygen flux, mol/(m**2*s)
    
    Victor Stepanenko's avatar
    Victor Stepanenko committed
    
    
    real(kind=ireals) :: x, xx
    
    real(kind=ireals) :: bcs(1:2)
    
    real(kind=ireals), save :: botO2 = 0.    ! Bottom oxygen concentration, mol/m**3
    
    Victor Stepanenko's avatar
    Victor Stepanenko committed
    
    
    integer(kind=iintegers) :: i
    integer(kind=iintegers), parameter :: switchbotbc = 1 !1 - Neumann, 2 - Dirichlet
    
    integer(kind=iintegers) :: bctype(1:2)
    
    Victor Stepanenko's avatar
    Victor Stepanenko committed
    
    allocate (a(1:vector_length),b(1:vector_length),c(1:vector_length), &
    &         f(1:vector_length),y(1:vector_length))
    
    a(:) = 0.; b(:) = 0.; c(:) = 0.; f(:) = 0.; y(:) = 0.
    
    Victor Stepanenko's avatar
    Victor Stepanenko committed
    
    
    !Foxyg1 = kappa*oxyg(gs%M+1,1)/log(0.25*gsp%ddz(gs%M)*ls%h1/z0_bot) !note: explicit scheme for flux
    
    if (ls%h1 > 0) then
    
    ! 1-st step of splitting-up scheme - diffusion, explicit scheme for flux
    
      if (ls%l1 == 0) then
    
        x = HENRY_CONST(Henry_const0_o2, Henry_temp_dep_o2, Henry_temp_ref, &
        & Twater(1) + Kelvin0) 
    
        Flux_atm = - GAS_WATATM_FLUX &
    
        & (Twater(1),wind10,oxyg(1,1),o2_pres_atm,x,water_oxygen_indic,eps_surf)
    
    Victor Stepanenko's avatar
    Victor Stepanenko committed
      else
        Flux_atm = 0.
      endif
    
      bctype(1) = 2
      bcs   (1) = Flux_atm
    
      if (ls%ls1 > 0) then  
    
    !    ! Switching bottom b.c.
    
        select case (switchbotbc)
        case(1)
    
          bctype(2) = 2
          bcs   (2) = 0.
    
          bctype(2) = 1
          bcs   (2) = botO2
    
    Victor Stepanenko's avatar
    Victor Stepanenko committed
      else  
    
        ! Switching bottom b.c.
        select case (switchbotbc)
        case(1)
    
          bctype(2) = 2
          bcs   (2) = Foxyg1
    
          bctype(2) = 1
          bcs   (2) = botO2
    
      call DIFFUSION(gs%M+1,bctype,bcs,dt,ls%h1,ls%dhw,ls%dhw0, &
      & gsp%ddz,gsp%ddz05,gsp%dzeta_int,oxyg(1,1),oxyg(1,2),lamoxyg,&
      & lso%water,bathymwater)
    
    
      if (gs%nsoilcols == 1) then
        fbbleflx_o2_sum(0)      = febul0*fracb0(3)/fracb0(1)*fbbleflx_o2(0)     *bathymwater(1)     %area_int
        fbbleflx_o2_sum(1:gs%M) = febul0*fracb0(3)/fracb0(1)*fbbleflx_o2(1:gs%M)*bathymwater(1:gs%M)%area_half
        fbbleflx_o2_sum(gs%M+1) = febul0*fracb0(3)/fracb0(1)*fbbleflx_o2(gs%M+1)*bathymwater(gs%M+1)%area_int
    
    Victor Stepanenko's avatar
    Victor Stepanenko committed
      endif
    
      if (soilswitch == 1) then
        ! 2-d step of splitting-up scheme - source due to bubble dissolution
    
        !x = dt*febul0*fracb0(3)/fracb0(1)
    
        oxyg(1,2) = oxyg(1,2) + 2.d0*dt/(ls%h1*gsp%ddz(1))*(fbbleflx_o2_sum(1) - fbbleflx_o2_sum(0)) / &
        & bathymwater(1)%area_int
    
        oxyg(1,2) = max(oxyg(1,2),small_value)
    
        do i = 2, gs%M
          oxyg(i,2) = oxyg(i,2) + dt/(ls%h1*gsp%ddz05(i-1))*(fbbleflx_o2_sum(i) - fbbleflx_o2_sum(i-1)) / &
          & bathymwater(i)%area_int
    
          oxyg(i,2) = max(oxyg(i,2),small_value)
    
    Victor Stepanenko's avatar
    Victor Stepanenko committed
        enddo
    
        oxyg(gs%M+1,2) = oxyg(gs%M+1,2) + 2.d0*dt/(ls%h1*gsp%ddz(gs%M)) * &
        & (fbbleflx_o2_sum(gs%M+1) - fbbleflx_o2_sum(gs%M)) / &
        & bathymwater(gs%M+1)%area_int
        oxyg(gs%M+1,2) = max(oxyg(gs%M+1,2),small_value)
    
    Victor Stepanenko's avatar
    Victor Stepanenko committed
      endif
    
    !  x = oxyg(1,2)*ddz(1)/2.
    !  x = x + oxyg(M+1,2)*ddz(M)/2.
    !  do i = 2, M
    !    x = x + oxyg(i,2)*(ddz(i-1)+ddz(i))/2.
    !  enddo
      
    
    endif 
    
    
    deallocate (a, b, c, f, y)
    
    END SUBROUTINE OXYGEN 
    
    !> Calculates the rate of oxygen sources and sinks in the water column,
    !! generally following Stefan and Fang, Ecological Modelling, 71 (1994) 37-68
    !! V.S., 06.2014
    
    SUBROUTINE OXYGEN_PRODCONS(gs,gsp,wst,bathymsoil,gas,area_int,area_half,ddz05, &
    &                          h,dt,Tsoil,Chl_a,dzs,por,oxygsoil, &
    
    &                          itroph,prodox,resp,bod,sod,sodbot)
    
    use DRIVING_PARAMS, only : soilcolconjtype
    
    
    use PHYS_CONSTANTS, only : &
    
    & Wm22Em2, short2PAR, z0_bot
    
    
    use TIMEVAR, only : &
    
    & hour_sec, day_sec
    
    use METH_OXYG_CONSTANTS, only : &
    
    & tortuosity_coef, &
    
    & T0, &   ! Reference temperature, Celsius
    & T00, &  ! Another reference temperature, Celsius
    & c1_pmax, c2_pmax, &! Constants for temperature dependence,
                         ! (Stefan and Fang, 1994; Megard et al., 1984)
    & k1_c1, k1_c2, &    ! (Megard et al., 1984)
    & k2_c1, k2_c2, &
    & YCHO2, &   ! mass ratio of chlorophyll-a to oxygen utilized in respiration
    & k_r, &     ! day**(-1), (Brown and Barnwell, QUAL2E, 1987; Riley, 1988)
    & theta_r, & ! temperature dependence of respiration (Ambrose et al., 1988)
    & k_b, &     ! day**(-1), 1-st order decay coefficient, (Riley, 1988; Brown and Barnwell, 1987)
    & theta_b1, theta_b2, &   ! temperature-dependence coefficient for biochemical oxygen demand
    & mO2C_dec, mCChla_dec, & ! Stoichiometry constants for organics decay in the water column, 
                              ! for BOD, basing on carbonaceous BOD and neglecting 
                              ! nitropgenous BOD, see details in Stefan and Fang, 1994
    & Sb20, & ! estimates: 0.5E+3 for oligotrophic, 1.5E+3 for eutrophic lakes, Stefan and Fang, 1994,
              ! mg/(m**2*day), reference sedimentary oxygen demand (SOD)
    & theta_s1, theta_s2, &! temperature dependence coefficients for SOD, Stefan and Fang, 1994
    & T_md, &              ! the temperature of maximal density
    
    
    & alpha_POCL, tau_DOC_auto, tau_DOC_allo, tau_POCD, & !constants of Hanson et al. 2004 model
    
    
    & halfsat_DIP_photosynt !half-saturation constant for soluble reactive phosphorus
                            !(~dissolved inorganic phosphorus) limitation in photosynthesis rate
    
    use ARRAYS_GRID, only : &
    & gridsize_type, gridspacing_type
    
    use ARRAYS_WATERSTATE, only : waterstate_type
    
    use ARRAYS_BATHYM, only : bathym
    
    use ARRAYS, only : gas_type
    
    use PHYS_FUNC, only : DIFF_WATER_OXYGEN, LOGFLUX
    
    
    Victor Stepanenko's avatar
    Victor Stepanenko committed
    implicit none
    
    ! Input variables
    
    type(gridsize_type), intent(in) :: gs
    
    type(gridspacing_type), intent(in) :: gsp
    
    type(waterstate_type), intent(in) :: wst
    
    !> Bathymetry of soil columns
    
    type(bathym), intent(in) :: bathymsoil(1:gs%nsoilcols+1,1:gs%nx,1:gs%ny)
    
    !> Carbon species in water and oxygen
    type(gas_type), intent(in) :: gas
    
    !> Lake cross-section area at cell interfaces
    real(kind=ireals),    intent(in) :: area_int(1:gs%M+1) 
    !> Lake cross-section area at cell centers
    real(kind=ireals),    intent(in) :: area_half(1:gs%M)  
    !> Spacing between cell centers, n/d
    real(kind=ireals),    intent(in) :: ddz05(0:gs%M) 
    !> Soil temperature, deg. Celsius
    real(kind=ireals),    intent(in) :: Tsoil(1:gs%ns,1:gs%nsoilcols)  
    !> Chlorophyll-a concentration, mg * l**(-1)
    real(kind=ireals),    intent(in) :: Chl_a(1:gs%M+1) 
    !> Soil grid spacing, m
    real(kind=ireals),    intent(in) :: dzs  (1:gs%ns)  
    !> Soil porosity, n/d
    real(kind=ireals),    intent(in) :: por  (1:gs%ns)  
    !> Mean oxygen concentration in top soil layer
    real(kind=ireals),    intent(inout) :: oxygsoil (1:gs%nsoilcols)  
    !> Lake depth, m
    real(kind=ireals),    intent(in) :: h            
    !> Timestep, s
    real(kind=ireals),    intent(in) :: dt           
    !> Trophic status (1 - oligotrophic, 2 - mesotrophic, 3 - eutrophic)
    integer(kind=iintegers), intent(in) :: itroph 
    
    Victor Stepanenko's avatar
    Victor Stepanenko committed
    
    ! Output variables
    
    !> Production of O2 by photosynthesis
    real(kind=ireals), intent(out) :: prodox(1:gs%M+1) 
    !> Sink due to respiration
    real(kind=ireals), intent(out) :: resp(1:gs%M+1)   
    !> Biochemical oxygen demand
    real(kind=ireals), intent(out) :: bod(1:gs%M+1)    
    !> Sediment oxygen demand (sink)
    real(kind=ireals), intent(out) :: sod(1:gs%M+1)    
    !> Sediment oxygen demand at the bottom, positive downwards
    real(kind=ireals), intent(out) :: sodbot        
    
    Victor Stepanenko's avatar
    Victor Stepanenko committed
    
    ! Local variables
    
    
    integer(kind=iintegers), parameter :: nSOD = 2 ! The switch for SOD models
    
    
    real(4) :: Pmax  ! Maximum oxygen production by photosynthesis, h**(-1)
    
    real(4) :: minL  ! Light limiting factor for photosynthesis
    real(4) :: minN  ! Nutrient limiting factor for photosynthesis
    
    real(4) :: PAR   ! Photosynthetically active radiation, Einstein /(m*m*h)
    
    real(4) :: BOD_  ! detritus as oxygen equivalent, mg/l
    
    real(4) :: k1_minL, k2_minL, kc, mubeta, oxysurf
    real(kind=ireals) :: x, step10, step20, chla_aver, flux, diff
    
    real(kind=ireals), allocatable :: PARz(:)
    
    integer(kind=iintegers) :: i, j, k
    integer(kind=iintegers), allocatable :: kcompl(:)
    
    if (nSOD == 3) then
    
      allocate (kcompl(1:gs%nsoilcols))
    
    ! Calculating mean chlorophyll-a density
    if     (itroph == 3) then 
    ! Eutrophic lake
      chla_aver = chla_aver_eu
    elseif (itroph == 2) then
    ! Mesotrophic lake
      chla_aver = chla_aver_meso
    elseif (itroph == 1) then
    ! Oligotrophic lake
      chla_aver = chla_aver_oligo
    endif
    
    
    allocate(PARz(0:gs%M+1))
    if (nbands > 1) then
      PARz(0:gs%M+1) = RadWater%flux(0:gs%M+1,2) !2-d band assumed to be PAR
    else
      PARz(0:gs%M+1) = RadWater%flux(0:gs%M+1,1)
    endif
    
    
      ! Oxygen production by photosynthesis
    
      Pmax = c1_pmax*c2_pmax**(wst%Tw2(i) - T0)
      k1_minL = k1_c1*k1_c2**(wst%Tw2(i) - T0)
      step10 = STEP(wst%Tw2(i) - T00) ! step function around 10 Celsius
    
      k2_minL = step10*k2_c2 + (1 - step10)*k2_c1
    
        PAR = 0.5*(PARz(i-1) + PARz(i))*Wm22Em2*hour_sec !*short2PAR !converting to Einstein /(m**2*hour)
    
        PAR = PARz(0)*Wm22Em2*hour_sec !*short2PAR !converting to Einstein /(m**2*hour)
    
      minL = PAR*(1. + 2.*sqrt(k1_minL/k2_minL)) / (PAR + k1_minL + PAR*PAR/k2_minL)
    
      !minL = PAR/PAR_prod_max_warm*exp(1. - PAR/PAR_prod_max_warm)
    
      minN = gas%phosph(i)/(halfsat_DIP_photosynt + gas%phosph(i))
    
      prodox(i) = prodox(i)/(hour_sec*molmass_o2) ! Converting from mg/(l*h) to mol/(m**3*s)
    
      if     (carbon_model%par == 1) then ! Stefan and Fang model for respiration and BOD
        ! Oxygen sink by respiration
        resp(i) = 1./YCHO2*k_r*theta_r**(wst%Tw2(i) - T0)*Chl_a(i)
    
        resp(i) = resp(i)/(day_sec *molmass_o2) ! Converting from mg/(l*day) to mol/(m**3*s)
    
        ! Biochemical oxygen demand (BOD)
        BOD_ = Chla_aver*mO2C_dec*mCChla_dec ! stoichiometry details see in Stefan and Fang, 1994
        step20 = STEP(wst%Tw2(i) - T0) ! step function around 20 Celsius
        x = (theta_b1*step20 + theta_b2*(1-step20))*STEP(wst%Tw2(i) - T_md) ! using step function around 4 Celsius
        if (x == 0.) then
          bod(i) = 0.
        else
          bod(i) = k_b*x**(wst%Tw2(i) - T0)*BOD_
        endif
    
        bod(i) = bod(i)/(day_sec *molmass_o2) ! --||-- (Converting from mg/(l*day) to mol/(m**3*s))
    
      elseif (carbon_model%par == 2) then !Hanson et al. model for respiration and BOD
        resp(i) = alpha_POCL*prodox(i)
    
        !> @todo{introduce different timescale for degradation of allochtonous DOC}
        !Assuming all C atoms escaping POCD and DOC form CO2 
        bod(i) = (gas%POCD(i)/tau_POCD + & !kg_to_g/molmass_ch2o*
    
        & gas%DOC(i,1)/tau_DOC_auto + gas%DOC(i,2)/tau_DOC_allo) !Check units!
    
      ! Sedimentary oxygen demand (SOD)
    
      select case (nSOD)
      case(1) ! The model from Stefan and Fang (1994)
        x = step10*theta_s1 + (1 - step10)*theta_s2 !using step function
    
        sod(i) = Sb20*x**(wst%Tw2(i) - T0)/(day_sec *molmass_o2 * 1.E+3)
    
      case(2) ! The model from Walker and Sondgrass (1986)
    
        kc     = kc0*thetaC_SOD**(wst%Tw2(i) - 20.)
        mubeta = mubeta0*thetamu_SOD**(wst%Tw2(i) - 25.)
    
        sod(i) = kc*gas%oxyg(i,1) + mubeta*gas%oxyg(i,1)/(k_O2_SOD + gas%oxyg(i,1))
    
      case(3) ! New SOD model 
    
        k = gsp%ksoilcol(i) !The number of soil column intersecting i-th layer
        if (kcompl(k) == 0) then !checking if SOD calculated for k-th column
    
          if     (soilcolconjtype == 1) then
            j = bathymsoil(k,gs%ix,gs%iy)%icent
            x = bathymsoil(k,gs%ix,gs%iy)%dzSLc
          elseif (soilcolconjtype == 2) then
            j = bathymsoil(k,gs%ix,gs%iy)%itop
            x = bathymsoil(k,gs%ix,gs%iy)%dzSL
          endif
    
          ! Oxygen concentration at the soil surface from soil side
          diff = tortuosity_coef*DIFF_WATER_OXYGEN(Tsoil(1,k))
          oxysurf = oxygsoil(k) !OXSURF(Tsoil(1,k),0.5*dzs(1),diff,oxygsoil(k))
          ! Oxygen flux is calculated from logarithmic law
          sod(i) = LOGFLUX(sqrt(wst%u2(j)*wst%u2(j) + wst%v2(j)*wst%v2(j)), &
    
          & oxysurf/por(1) - gas%oxyg(j,1), x, z0_bot, z0_bot, 1._ireals, 1)
    
          ! Oxygen concentration is updated to the next time step
          call OXYGEN_TOPSOIL(Tsoil(1,k),sod(i),0.5*dzs(1),dt,oxygsoil(k))
          kcompl(k) = 1
        else
          sod(i) = sod(i-1) ! SOD flux is the same for all water layer intesecting the same soil column
        endif
    
      if (i > 1) then
        sod(i) = - sod(i) / area_int(i)*(area_half(i) - area_half(i-1)) / &
        & (h*ddz05(i-1))
      else
        sod(i) = - sod(i) / area_int(i)*(area_half(i) - area_int(i)) / &
        & (h*ddz05(i-1))
      endif
    
    Victor Stepanenko's avatar
    Victor Stepanenko committed
    enddo
    
    !sod   (:) = sod   (:)/(day_sec *molmass_o2 * 1.E+3) ! Converting from mg/(m*m*day) to mol/(m*m*s)
    
    
    ! Sedimentary oxygen demand at the bottom (flux)
    
    select case (nSOD)
    case(1) ! The model from Stefan and Fang (1994)
    
      step10 = STEP(wst%Tw2(gs%M+1) - 10.) ! step function around 10 Celsius
    
      x = step10*theta_s1 + (1 - step10)*theta_s2 ! using step function
    
      sodbot = Sb20*x**(wst%Tw2(gs%M+1) - T0)/(day_sec *molmass_o2 * 1.E+3) ! Converting from mg/(m*m*day) to mol/(m*m*s)
    
    case(2) ! The model from Walker and Sondgrass (1986)
    
      kc = kc0*thetaC_SOD**(wst%Tw2(gs%M+1) - 20.)
      mubeta = mubeta0*thetamu_SOD**(wst%Tw2(gs%M+1) - 25.)
    
      sodbot = kc*gas%oxyg(gs%M+1,1) + mubeta*gas%oxyg(gs%M+1,1)/(k_O2_SOD + gas%oxyg(gs%M+1,1))
    
    case(3) ! The new SOD model
    
      diff = tortuosity_coef*DIFF_WATER_OXYGEN(Tsoil(1,gs%nsoilcols))
      oxysurf = oxygsoil(gs%nsoilcols) !OXSURF(Tsoil(1,gs%nsoilcols),0.5*dzs(1),diff,oxygsoil(gs%nsoilcols))
      sodbot = LOGFLUX(sqrt(wst%u2(gs%M+1)*wst%u2(gs%M+1) + wst%v2(gs%M+1)*wst%v2(gs%M+1)), &
    
      & oxysurf/por(1) - gas%oxyg(gs%M+1,1), 0.25*gsp%ddz(gs%M)*h, z0_bot, z0_bot, 1._ireals, 1)
    
      call OXYGEN_TOPSOIL(Tsoil(1,gs%nsoilcols),sodbot,0.5*dzs(1),dt,oxygsoil(gs%nsoilcols))
    
    END SUBROUTINE OXYGEN_PRODCONS
    
    
    
    SUBROUTINE CHLOROPHYLLA(z_full, H_mixed_layer, H_photic_zone, extwat, RadWater, &
    & gas, M, Chl_a, itroph)
    
    ! This subroutine specifies the chlorophyll profile
    
    use METH_OXYG_CONSTANTS, only : &
    
    & C1_Chla_to_POCLphyto, C2_Chla_to_POCLphyto, &
    
    & PAR_prod_max_warm, halfsat_DIP_photosynt, &
    
    & molmass_ch2o, Chla_to_extinct, &
    & k1_c1, k2_c2
    
    use UNITS    , only : m3_to_l
    use RADIATION, only : rad_type, nbands
    use ARRAYS   , only : gas_type
    
    use DRIVING_PARAMS, only : carbon_model
    
    use TIMEVAR, only : hour_sec
    use PHYS_CONSTANTS, only : Wm22Em2
    
    
    implicit none
    
    ! Input variables
    
    real(kind=ireals), intent(in) :: z_full(1:M+1) ! Depth at cell interfaces, m
    real(kind=ireals), intent(in) :: H_mixed_layer ! Mixed layer depth, m
    real(kind=ireals), intent(in) :: H_photic_zone ! Photic zone depth, m
    real(kind=ireals), intent(in) :: extwat        ! Extinction coefficient, m**(-1)
    
    type(rad_type)   , intent(inout) :: RadWater   ! Radiation fluxes in water
    type(gas_type)   , intent(in) :: gas           ! Gases, organic and inorganic species
    
    integer(kind=iintegers), intent(in) :: M ! Number of grid layers (cells)
    
    real(kind=ireals), intent(out) :: Chl_a(1:M+1) ! Chlorophyll-a density, mg/l
    integer(kind=iintegers), intent(out) :: itroph ! Trophic status
    
    integer(kind=iintegers), save :: chlmodel = 2
    
    real(4) :: depthact
    real(4) :: chla_aver
    
    real(kind=iintegers) :: phi_n, phi_l, Chla_to_POCL_phyto, PAR
    
    logical, save :: firstcall = .true.
    
    if (firstcall) then
      if (carbon_model%par == 1) chlmodel = 1
    endif
    
    
    select case (chlmodel)
    case(1)
      ! Parameterization following Stefan and Fang (1994) with modifications:
      ! - no annual cycle
      if (extwat > extwat1) then 
      ! Eutrophic lake
        chla_aver = chla_aver_eu
        itroph = 3
      elseif (extwat <= extwat1 .and. extwat > extwat2) then
      ! Mesotrophic lake
        chla_aver = chla_aver_meso
        itroph = 2
      else
      ! Oligotrophic lake
        chla_aver = chla_aver_oligo
        itroph = 1
      endif
      depthact = max(H_mixed_layer,H_photic_zone)
      do i = 1, M+1
        Chl_a(i) = chla_aver*STEP(depthact - z_full(i)) ! step function
      enddo
    case(2)
      ! Parameterization following Chapra (2008) and 
      ! (Sadeghian et al., Envir.Mod.Soft. 2018) 
    
      if (nbands > 1) then
        nb = 2
      else
        nb = 1
      endif
      do i = 1, M+1
        ! Optimal PAR for photosynthesis is a reference value for warm period
        if (i > 1) then
          PAR = 0.5*(RadWater%flux(i-1,nb) + RadWater%flux(i,nb))
        else
          PAR = RadWater%flux(0,nb)
        endif
    
        PAR = PAR*Wm22Em2*hour_sec !convertion to einsten/(m**2*hour), the units of PAR_prod_max_warm
    
        !phi_l = PAR/PAR_prod_max_warm*exp(1. - PAR/PAR_prod_max_warm)
        phi_l = PAR*(1. + 2.*sqrt(k1_c1/k2_c2)) / (PAR + k1_c1 + PAR*PAR/k2_c2)
    
        phi_n = gas%phosph(i)/(halfsat_DIP_photosynt + gas%phosph(i)) !using half-saturation phosphorus for photosynthesis
        !>@todo{use here phytoplankton instead of POCL}
    
        !if (phi_l > 0.5) then
        !  print*, phi_l, PAR, PAR_prod_max_warm, i
        !endif
    
        Chla_to_POCL_phyto = C1_Chla_to_POCLphyto + C2_Chla_to_POCLphyto*phi_n*(1.-phi_l)
        !Assuming stoichiometry of POCL that of sugar
    
        Chl_a(i) = gas%POCL(i)*molmass_ch2o*Chla_to_POCL_phyto
    
        !> @todo{add to extinction CDOC, detritus, pure water value}
    
        !RadWater%extinct(i,nb) = 0.5 + Chla_to_extinct*Chl_a(i)*m3_to_l !1. -- extinction at zero chlorophyll-a
    
    if (firstcall) firstcall = .false.
    
    END SUBROUTINE CHLOROPHYLLA
    
    
    
    !> Subroutine ADDOXPRODCONS adds to O_2 and CO_2 tendencies associated
    !! with organics dynamics in a lake (photosynthesis, respiration,
    
    !! BOD and SOD), and the same for DOC, POCL, POCD
    
    SUBROUTINE ADDOXPRODCONS(M,i_maxN,H_mixed_layer,H_photic_zone, &
    
    &                        prodox,resp,bod,sod,RadWater,dt,ls,gas)
    
    
    use ARRAYS, only : gas_type
    use ARRAYS_BATHYM, only : layers_type
    
    
    use METH_OXYG_CONSTANTS, only : &
    
    & alpha_POCL, tau_POCD, tau_DOC_auto, tau_DOC_allo, &
    
    & beta_POCL, tau_Dh_epi, tau_Dh_hypo, &
    
    & DOMauto_PtoC, DOMallo_PtoC, POM_PtoC, &
    
    & sedoxid_PtoC, pH, &
    & SUVA_305, AQY, UV_weight, PAR_weight
    
    
    use DRIVING_PARAMS, only : &
    & carbon_model
    
    use RADIATION, only : rad_type, nbands, bandbounds
    
    
    implicit none
    
    ! Input variables
    
    !> Mixed layer depth, m
    real(kind=ireals), intent(in) :: H_mixed_layer 
    !> Photic zone depth, m
    real(kind=ireals), intent(in) :: H_photic_zone 
    
    !> Production of O2 by photosynthesis, mol/(m**3*s)
    
    real(kind=ireals), intent(inout) :: prodox(1:M+1) 
    
    !> Sink due to respiration, mol/(m**3*s)
    
    real(kind=ireals), intent(inout) :: resp(1:M+1)   
    
    !> Biochemical oxygen demand, mol/(m**3*s)
    real(kind=ireals), intent(in) :: bod(1:M+1)    
    !> Sediment oxygen demand (sink), mol/(m**3*s)
    real(kind=ireals), intent(in) :: sod(1:M+1)    
    
    !> Radiation fluxes in water and optical properties
    type(rad_type)   , intent(inout) :: RadWater
    
    !> Time step, s
    real(kind=ireals), intent(in) :: dt 
    !> Number of model layers (cells)
    integer(kind=iintegers), intent(in) :: M 
    
    !> The computational level of maximal Brunt-Vaisala frequency
    integer(kind=iintegers), intent(in) :: i_maxN
    !> Physical layers' thicknesses
    type(layers_type), intent(in) :: ls
    
    
    !Input/ouput variables
    
    !> Gas concentrations in water, carbon species content
    type(gas_type), intent(inout) :: gas
    
    real(kind=ireals) :: x, xx, hact, rad_CDOM, total_DOC
    real(kind=ireals) :: E_POCL, D_DOC1, D_DOC2, P_POCL, R_POCL, Dh_POCL, D_POCD, tau_Dh, &
    & Pd_DOC
    
    integer(kind=iintegers) :: i, nb
    
      !Limitation of photosynthesis by DIC availability
      x  = min(gas%DIC(i,1)/CO2O2_prod        , dt*prodox(i)) 
      !Limitation of photosynthesis by DIP availability
      xx = min(gas%phosph(i)/(POM_PtoC*CO2O2_prod), dt*prodox(i)) 
      x  = min(x,xx)
      gas%DIC(i,1)    = gas%DIC(i,1)  - x*CO2O2_resp
      gas%oxyg(i,1)   = gas%oxyg(i,1) + x
      prodox(i) = x/dt
    
      x = min(gas%oxyg(i,1),dt*resp(i))
    
      !Limitation of respiration by available oxygen
      gas%oxyg(i,1)   = gas%oxyg(i,1) - x
      gas%DIC(i,1)    = gas%DIC(i,1)  + x*CO2O2_resp
      resp(i) = x/dt
    
      x = min(gas%oxyg(i,1),dt*bod(i))
    
      gas%oxyg(i,1)   = gas%oxyg(i,1) - x
      gas%DIC(i,1)    = gas%DIC(i,1)  + x*CO2O2_bod
      bodcorr(i) = x/(dt*(bod(i)+small_value))
    
      x = min(gas%oxyg(i,1),dt*sod(i))
    
      gas%oxyg(i,1)   = gas%oxyg(i,1) - x
      gas%DIC(i,1)    = gas%DIC(i,1)  + x*CO2O2_sod
    
      gas%phosph(i)   = gas%phosph(i) + x*sedoxid_PtoC
    
    ! Hanson et al. model tendencies for carbon species
    if (carbon_model%par == 2) then
    
      hact = max(H_mixed_layer,H_photic_zone)
    
      do i = 1, M
        ! The death timescale assumed to be the same in hypolimion and during ice period
    
        if (ls%l1 > 0.) then
    
          tau_Dh = tau_Dh_hypo
    
        elseif (gsp%z_full(i) > hact) then
          x  = gsp%z_full(i) - hact
          xx = ls%h1 - hact
          tau_Dh = tau_Dh_epi - (tau_Dh_hypo - tau_Dh_epi)* &
          & ((x/xx)**2 - 2.*x/xx) !quadratic interpolation with zero derivative at bottom
    
        else
          tau_Dh = tau_Dh_epi
        endif
    
    
        !Photodissociation, assuming constant CDOM attenuation coefficient inside CDOC spectrum interval
        total_DOC = gas%DOC(i,1) + gas%DOC(i,2)
        RadWater%extinct_CDOC(i) = SUVA_305*total_DOC
        if (nbands > 1) then
          ! Summing UV and PAR parts overlapping with CDOM absorbance spectrum,
          ! assuming linear spectrumn in UV and constant value in PAR regions
          rad_CDOM = RadWater%flux(i-1,1)*UV_weight + &
          &          RadWater%flux(i-1,2)*PAR_weight    
        else
          rad_CDOM = RadWater%flux(i-1,1)
        endif
        Pd_DOC = rad_CDOM * &
        & (1. - exp( - RadWater%extinct_CDOC(i)*gsp%ddz05(i-1)*ls%h1)) / &
        & gsp%ddz05(i-1)*ls%h1 * AQY
    
        !Pd_DOC is subtracted from DOC but not taken into account in other
        !equations, as CO is the main product of photodegradation and there is
        !no enough evidence to assume it is fully added to DIC and not emitted to the atmosphere
        !(Zuo and Jones, Water Research, 1997)
    
        !print*, rad_CDOM, &
        !& (1. - exp( - RadWater%extinct_CDOC(i)*gsp%ddz05(i-1)*ls%h1))/ &
        !& gsp%ddz05(i-1)*ls%h1, AQY
    
    
        !print*, i, tau_Dh
    
        P_POCL = prodox(i) !*molmass_ch2o / kg_to_g
    
        R_POCL = resp(i)   !alpha_POCL*P_POCL
        E_POCL = beta_POCL*prodox(i) !*P_POCL
    
        !> @note{ using beta_POCL valid for mass concentrations is not strictly correct
        !! for molar concentrations, as E_POCL and P_POCL may have different C-to-mass ratios} 
    
        Dh_POCL = gas%POCL(i)/tau_Dh
    
        D_DOC1 = gas%DOC(i,1)/tau_DOC_auto*bodcorr(i)
        D_DOC2 = gas%DOC(i,2)/tau_DOC_allo*bodcorr(i)
    
        !print*, (Pd_DOC/(total_DOC+small_value))**(-1) 
        gas%DOC(i,1) = gas%DOC(i,1) + dt*(E_POCL - D_DOC1 - &
        & Pd_DOC*gas%DOC(i,1)/(total_DOC+small_value))
        gas%DOC(i,2) = gas%DOC(i,2) + dt*(       - D_DOC2 - &
        & Pd_DOC*gas%DOC(i,2)/(total_DOC+small_value))
    
        gas%POCL(i) = gas%POCL(i) + dt*(P_POCL - R_POCL - E_POCL - Dh_POCL)
        gas%POCD(i) = gas%POCD(i) + dt*(Dh_POCL - D_POCD)
    
    
        gas%phosph(i) = gas%phosph(i) + dt*(D_DOC1*DOMauto_PtoC + D_DOC2*DOMallo_PtoC + &
        & ( - P_POCL + R_POCL + D_POCD)*POM_PtoC)
    
    END SUBROUTINE ADDOXPRODCONS
    
    SUBROUTINE OXYGEN_TOPSOIL(Tsoil,influx,h,dt,oxygsoil)
    
    !A bulk model for oxygen in top of soil column,
    !calculates oxygen concentration in an aerobic top
    !soil layer at the next timestep
    
    
    use METH_OXYG_CONSTANTS, only : &
    
    k_O2_SOD, mubeta0, thetamu_SOD 
    
    
    implicit none
    
    !Input variables
    
    real(kind=ireals), intent(in) :: Tsoil  !Temperature of aerobic layer, C
    
    real(kind=ireals), intent(in) :: influx !Oxygen flux from water column, positive downwards
    
    real(kind=ireals), intent(in) :: h      !Thickness of top aerobic soil layer, m
    real(kind=ireals), intent(in) :: dt     !Timestep, s
    
    
    !Input/output variables
    
    real(kind=ireals), intent(inout) :: oxygsoil !Mean oxygen concentration in the top soil layer
    
    !Local variables
    real(kind=ireals) :: mubeta
    
    ! Michaelis-Menthen (Monod) oxygen depletion according to Walker and Snodgrass, 1986, 
    ! neglecting "chemical sediment oxygen demand"
    mubeta = mubeta0*thetamu_SOD**(Tsoil - 25.)/h
    
    oxygsoil = (oxygsoil + influx/h*dt)/(1. + dt*mubeta/(k_O2_SOD + oxygsoil))
    
    FUNCTION OXSURF(Tsoil,h,diff,oxmean)
    
    !Calculates surface oxygen concentration from
    
    !mean concentration in anaerobic layer. Mean is calculated
    !from analytical solution of problem for oxygen concentration
    !d^2 C/d^2 z - alpha*C=0 with Dirichlet b.c.s
    
    
    use METH_OXYG_CONSTANTS, only : &
    k_O2_SOD, mubeta0, thetamu_SOD 
    
    implicit none
    
    !Input variables
    real(kind=ireals), intent(in) :: Tsoil  !Temperature of aerobic layer, C
    real(kind=ireals), intent(in) :: h      !Thickness of anaerobic layer, m
    real(kind=ireals), intent(in) :: diff   !Oxygen diffusivity in anaerobic layer
    real(kind=ireals), intent(in) :: oxmean !Mean oxygen concentration in anaerobic layer
    
    !Output variables
    real(kind=ireals) :: OXSURF
    
    !Local variables
    real(kind=ireals) :: alpha, mubeta
    
    mubeta = mubeta0*thetamu_SOD**(Tsoil - 25.)/h
    alpha = sqrt(mubeta/(k_O2_SOD*diff))
    
    OXSURF = oxmean*alpha*h*(exp(alpha*h) + 1)/(exp(alpha*h) - 1.)
    
    END FUNCTION OXSURF
    
    
    
    Victor Stepanenko's avatar
    Victor Stepanenko committed
    END MODULE OXYGEN_MOD