module mo_fstrat
  !---------------------------------------------------------------
  !	... variables for the upper boundary values
  !---------------------------------------------------------------

  use shr_kind_mod, only : r8 => shr_kind_r8
  use ppgrid,       only : pcols, pver,pverp, begchunk, endchunk
  use chem_mods,    only : gas_pcnst
  use cam_abortutils,   only : endrun
  use cam_pio_utils, only : cam_pio_openfile
  use pio
  use cam_logfile,  only : iulog

  implicit none

  private
  public  :: fstrat_inti
  public  :: set_fstrat_vals
  public  :: set_fstrat_h2o
  public  :: has_fstrat

  save

  real(r8), parameter :: taurelax = 864000._r8        ! 10 days
  integer  :: gndx = 0
  integer  :: table_nox_ndx = -1
  integer  :: table_h2o_ndx = -1
  integer  :: table_ox_ndx  = -1
  integer  :: table_synoz_ndx  = -1
  integer  :: no_ndx
  integer  :: no2_ndx
  integer  :: h2o_ndx
  integer  :: ox_ndx
  integer  :: o3s_ndx
  integer  :: o3inert_ndx
  integer  :: o3a_ndx
  integer  :: xno_ndx
  integer  :: xno2_ndx
  integer  :: synoz_ndx
  integer  :: o3rad_ndx
  real(r8) :: facrelax
  real(r8) :: days(12)
  real(r8), allocatable   :: ub_plevs(:)         ! table midpoint pressure levels (Pa)
  real(r8), allocatable   :: ub_plevse(:)        ! table edge pressure levels (Pa)
  integer                 :: ub_nlevs            ! # of levs in ubc file
  integer                 :: ub_nlat             ! # of lats in ubc file
  integer                 :: ub_nspecies         ! # of species in ubc file
  integer                 :: ub_nmonth           ! # of months in ubc file
  real(r8), allocatable   :: mr_ub(:,:,:,:,:)      ! vmr
  integer,  allocatable   :: map(:)              ! species indices for ubc species
  logical :: sim_has_nox
  integer :: dtime               ! model time step (s)
  logical :: has_fstrat(gas_pcnst)

contains

  subroutine fstrat_inti( fstrat_file, fstrat_list )
    !------------------------------------------------------------------
    !	... initialize upper boundary values
    !------------------------------------------------------------------

    use mo_constants,  only : d2r
    use phys_grid,     only : get_ncols_p, get_rlat_all_p

    use time_manager,  only : get_step_size
    use time_manager,  only : get_calday
    use ioFileMod,     only : getfil
    use spmd_utils,    only : masterproc
#ifdef SPMD
    use mpishorthand,  only : mpicom,mpiint,mpir8
#endif
    use mo_tracname,  only : solsym
    use chem_mods,    only : gas_pcnst
    use mo_chem_utls, only : get_spc_ndx, get_inv_ndx
    use constituents, only : pcnst
    use dyn_grid,     only : get_dyn_grid_parm
    use interpolate_data, only : interp_type, lininterp_init, lininterp
    implicit none

    character(len=*), intent(in) :: fstrat_file
    character(len=*), intent(in) :: fstrat_list(:)

    !------------------------------------------------------------------
    !	... local variables
    !------------------------------------------------------------------
    real(r8), parameter :: mb2pa = 100._r8

    integer :: i, j, nchar
    integer :: spcno, lev, month, ierr
    integer :: vid, ndims
    type(file_desc_t) :: ncid
    integer :: dimid_lat, dimid_lev, dimid_species, dimid_month
    integer :: dimid(4)
    integer :: start(4)
    integer :: count(4)
    integer, parameter :: dates(12) = (/ 116, 214, 316, 415,  516,  615, &
         716, 816, 915, 1016, 1115, 1216 /)
    integer :: ncols, c
    real(r8), allocatable :: mr_ub_in(:,:,:,:)
    real(r8), allocatable :: lat(:)
    real(r8) :: to_lats(pcols)
    character(len=80) :: attribute
    character(len=8)  :: wrk_name
    character(len=25), allocatable :: ub_species_names(:)
    character(len=256) :: locfn
    type(interp_type) :: lat_wgts


    !-----------------------------------------------------------------------
    !       ... get species indicies
    !-----------------------------------------------------------------------    
    no_ndx      = get_spc_ndx( 'NO' )
    no2_ndx     = get_spc_ndx( 'NO2' )
    sim_has_nox = no_ndx > 0 .or. no2_ndx > 0
    ox_ndx      = get_spc_ndx( 'OX' )
    if( ox_ndx < 1 ) then
       ox_ndx = get_spc_ndx( 'O3' )
    end if
    o3s_ndx     = get_spc_ndx( 'O3S' )
    o3inert_ndx = get_spc_ndx( 'O3INERT' )
    o3rad_ndx   = get_spc_ndx( 'O3RAD' )
    synoz_ndx   = get_spc_ndx( 'SYNOZ' )
    o3a_ndx     = get_spc_ndx( 'O3A' )
    xno_ndx      = get_spc_ndx( 'XNO' )
    xno2_ndx      = get_spc_ndx( 'XNO2' )

    if (.not.((o3rad_ndx > 0) .eqv. (synoz_ndx > 0))) then
       call endrun('fstrat_inti: Both SYNOZ and O3RAD are required in chemical mechanism.')
    endif
    if(masterproc) then
       if (synoz_ndx > 0) then
          if ( .not. any(fstrat_list == 'O3RAD') ) then
             write(iulog,*) 'fstrat_inti: ***WARNING*** O3RAD is not include in fstrat_list namelist variable'
          endif
       else if (ox_ndx > 0) then
          if ( .not. any(fstrat_list == 'O3') ) then
             write(iulog,*) 'fstrat_inti: ***WARNING*** O3 is not include in fstrat_list namelist variable'
          endif
       endif
    end if
    h2o_ndx     = get_spc_ndx( 'H2O' )
    if( h2o_ndx < 0 ) then
       h2o_ndx  = get_inv_ndx( 'H2O' )
    end if

    has_fstrat(:) = .false.

    do i = 1,pcnst

       if ( len_trim(fstrat_list(i))==0 ) exit

       j = get_spc_ndx(fstrat_list(i))

       if ( j > 0 ) then
          has_fstrat(j) = .true.
       else
          write(iulog,*) 'fstrat_inti: '//trim(fstrat_list(i))//' is not included in species set'
          call endrun('fstrat_inti: invalid fixed stratosphere species')
       endif

    enddo

    if (.not. any(has_fstrat(:))) return

    !-----------------------------------------------------------------------
    !       ... open netcdf file
    !-----------------------------------------------------------------------
    call getfil (fstrat_file, locfn, 0)
    call cam_pio_openfile (ncid, trim(locfn), PIO_NOWRITE)
    !-----------------------------------------------------------------------
    !       ... get latitude
    !-----------------------------------------------------------------------
    ierr = pio_inq_dimid( ncid, 'lat', dimid_lat )
    ierr = pio_inq_dimlen( ncid, dimid_lat, ub_nlat )
    allocate( lat(ub_nlat), stat=ierr )
    if( ierr /= 0 ) then
       write(iulog,*) 'fstrat_inti: lat allocation error = ',ierr
       call endrun
    end if
    ierr = pio_inq_varid( ncid, 'lat', vid )
    ierr = pio_get_var( ncid, vid, lat )
    lat(:ub_nlat) = lat(:ub_nlat) * d2r

    if( ierr /= 0 ) then
       write(iulog,*) 'fstrat_inti: failed to deallocate lat; ierr = ',ierr
       call endrun
    end if

    !-----------------------------------------------------------------------
    !       ... get vertical coordinate (if necessary, convert units to pa)
    !-----------------------------------------------------------------------
    ierr = pio_inq_dimid( ncid, 'lev', dimid_lev )
    ierr = pio_inq_dimlen( ncid, dimid_lev, ub_nlevs )
    allocate( ub_plevs(ub_nlevs), stat=ierr )
    if( ierr /= 0 ) then
       write(iulog,*) 'fstrat_inti: ub_plevs allocation error = ',ierr
       call endrun
    end if
    ierr = pio_inq_varid( ncid, 'lev', vid )
    ierr = pio_get_var( ncid, vid, ub_plevs )
    attribute(:) = ' '
    call pio_seterrorhandling(ncid, pio_BCAST_error)
    ierr = pio_get_att( ncid, vid, 'units', attribute )
    call pio_seterrorhandling(ncid, pio_INTERNAL_error)
    if( ierr == PIO_noerr )then
       if( trim(attribute) == 'mb' .or. trim(attribute) == 'hpa' )then
          if (masterproc) write(iulog,*) 'fstrat_inti: units for lev = ',trim(attribute),'... converting to pa'
          ub_plevs(:) = mb2pa * ub_plevs(:)
       else if( trim(attribute) /= 'pa' .and. trim(attribute) /= 'pa' )then
          write(iulog,*) 'fstrat_inti: unknown units for lev, units=*',trim(attribute),'*'
          write(iulog,*) 'fstrat_inti: ',attribute=='mb',trim(attribute)=='mb',attribute(1:2)=='mb'
          call endrun
       end if
    else
       write(iulog,*) 'fstrat_inti: warning! units attribute for lev missing, assuming mb'
       ub_plevs(:) = mb2pa * ub_plevs(:)
    end if
    !-----------------------------------------------------------------------
    !       ... get time and species dimensions
    !-----------------------------------------------------------------------
    ierr = pio_inq_dimid( ncid, 'month', dimid_month )
    ierr = pio_inq_dimlen( ncid, dimid_month, ub_nmonth )
    if( ub_nmonth /= 12 )then
       write(iulog,*) 'fstrat_inti: error! number of months = ',ub_nmonth,', expecting 12'
       call endrun
    end if
    ierr = pio_inq_dimid( ncid, 'species', dimid_species )
    ierr = pio_inq_dimlen( ncid, dimid_species, ub_nspecies )

    !------------------------------------------------------------------
    !	... allocate arrays
    !------------------------------------------------------------------
    allocate( mr_ub_in(ub_nlat,ub_nspecies,ub_nmonth,ub_nlevs), stat=ierr )
    if( ierr /= 0 ) then
       write(iulog,*) 'fstrat_inti: mr_ub_in allocation error = ',ierr
       call endrun
    end if
    allocate( mr_ub(pcols,ub_nspecies,ub_nmonth,ub_nlevs,begchunk:endchunk), stat=ierr )
    if( ierr /= 0 ) then
       write(iulog,*) 'fstrat_inti: mr_ub allocation error = ',ierr
       call endrun
    end if

    !------------------------------------------------------------------
    !	... read in the species names
    !------------------------------------------------------------------

    ierr = pio_inq_varid( ncid, 'specname', vid )
    ierr = pio_inq_vardimid( ncid, vid, dimid )
    ierr = pio_inq_dimlen( ncid, dimid(1), nchar )
    allocate( ub_species_names(ub_nspecies), stat=ierr )
    if( ierr /= 0 ) then
       write(iulog,*) 'fstrat_inti: ub_species_names allocation error = ',ierr
       call endrun
    end if
    allocate( map(ub_nspecies), stat=ierr )
    if( ierr /= 0 ) then
       write(iulog,*) 'fstrat_inti: map allocation error = ',ierr
       call endrun
    end if

    table_loop :  do i = 1,ub_nspecies
       start(:2) = (/ 1, i /)
       count(:2) = (/ nchar, 1 /)
       ub_species_names(i)(:) = ' '
       ierr = pio_get_var( ncid, vid, start(:2), count(:2), ub_species_names(i:i))
       if( trim(ub_species_names(i)) == 'NOX' ) then
          table_nox_ndx = i
       else if( trim(ub_species_names(i)) == 'H2O' ) then
          table_h2o_ndx = i
       else if( trim(ub_species_names(i)) == 'OX' ) then
          table_ox_ndx = i
       else if( trim(ub_species_names(i)) == 'SYNOZ' ) then
          table_synoz_ndx = i
       end if
       map(i) = 0
       do j = 1,gas_pcnst 
          if( trim(ub_species_names(i)) == trim(solsym(j)) ) then
             if( has_fstrat(j) ) then 
                map(i) = j
                if( masterproc ) write(iulog,*) 'fstrat_inti: '//trim(solsym(j))//' is fixed in stratosphere'
                exit
             end if
          endif
       end do
       if( map(i) == 0 ) then
          if( trim(ub_species_names(i)) == 'OX' ) then
             if( o3rad_ndx > 0 ) then
                wrk_name = 'O3RAD'
             else
                wrk_name = 'O3'
             end if
             do j = 1,gas_pcnst
                if( trim(wrk_name) == trim(solsym(j)) ) then
                   if( has_fstrat(j) ) then 
                      if( masterproc ) write(iulog,*) 'fstrat_inti: '//trim(solsym(j))//' is fixed in stratosphere'
                      map(i) = j
                      exit
                   end if
                end if
             end do
             if( map(i) == 0 ) then
                if (masterproc) write(iulog,*) 'fstrat_inti: ubc table species ',trim(ub_species_names(i)), ' not used'
             end if
          else if( (trim(ub_species_names(i)) /= 'NOX') .and. (trim(ub_species_names(i)) /= 'H2O') ) then
             if (masterproc) write(iulog,*) 'fstrat_inti: ubc table species ',trim(ub_species_names(i)), ' not used'
          end if
       end if
    end do table_loop

    if( table_nox_ndx > 0 ) then
       if ( any(fstrat_list(:) == 'NO') .or. any(fstrat_list(:) == 'NO2') ) then
          map(table_nox_ndx) = gas_pcnst + 1
       else
          if (masterproc) write(iulog,*) 'fstrat_inti: ubc table species ',trim(ub_species_names(table_nox_ndx)), ' not used'
       endif
    end if
    if( table_h2o_ndx > 0 ) then
       if ( h2o_ndx > 0 ) then
          map(table_h2o_ndx) = gas_pcnst + 2
       else
          if (masterproc) write(iulog,*) 'fstrat_inti: ubc table species ',trim(ub_species_names(table_h2o_ndx)), ' not used'
       endif
    end if

    if (masterproc) write(iulog,*) 'fstrat_inti: h2o_ndx, table_h2o_ndx  = ', h2o_ndx, table_h2o_ndx

    !------------------------------------------------------------------
    !	... check dimensions for vmr variable
    !------------------------------------------------------------------
    ierr = pio_inq_varid( ncid, 'vmr', vid )
    ierr = pio_inq_varndims( ncid, vid, ndims )
    if( ndims /= 4 ) then
       write(iulog,*) 'fstrat_inti: error! variable vmr has ndims = ',ndims,', expecting 4'
       call endrun
    end if
    ierr = pio_inq_vardimid( ncid, vid, dimid )
    if( dimid(1) /= dimid_lat .or. dimid(2) /= dimid_species .or. &
         dimid(3) /= dimid_month .or. dimid(4) /= dimid_lev )then
       write(iulog,*) 'fstrat_inti: error! dimensions in wrong order for variable vmr,'// &
            'expecting (lat,species,month,lev)'
       call endrun
    end if

    !------------------------------------------------------------------
    !	... read in the ub mixing ratio values
    !------------------------------------------------------------------
    start = (/ 1, 1, 1, 1 /)
    count = (/ ub_nlat, ub_nspecies, ub_nmonth, ub_nlevs /)

    ierr = pio_get_var( ncid, vid, start, count, mr_ub_in )
    call pio_closefile (ncid)
    !--------------------------------------------------------------------
    !	... regrid
    !--------------------------------------------------------------------
    do c=begchunk,endchunk
       ncols = get_ncols_p(c)
       call get_rlat_all_p(c, pcols, to_lats)
       call lininterp_init(lat,ub_nlat, to_lats, ncols, 1, lat_wgts)

       do lev = 1,ub_nlevs
          do month = 1,ub_nmonth
             do spcno = 1,ub_nspecies
                if( map(spcno) > 0 ) then

                   call lininterp(mr_ub_in(:,spcno, month, lev), ub_nlat, mr_ub(:,spcno,month,lev,c), &
                        ncols, lat_wgts)

                   !                call regrid_1d( mr_ub_in(:,spcno,month,lev), mr_ub(:,spcno,month,lev), gndx, &
                   !                     do_lat=.true.,to_lat_min=1, to_lat_max=plat )
#ifdef DEBUG
                   if( lev == 25 .and. month == 1 .and. spcno == 1 ) then
                      write(iulog,*) 'mr_ub_in='
                      write(iulog,'(10f7.1)') mr_ub_in(:,spcno,month,lev)*1.e9_r8
                      write(iulog,*) 'mr_ub='
                      write(iulog,'(10f7.1)') mr_ub(:,spcno,month,lev)*1.e9_r8
                   end if
#endif
                   !                mr_ub(1,spcno,month,lev) = mr_ub(2,spcno,month,lev)
                   !                mr_ub(plat,spcno,month,lev) = mr_ub(plat-1,spcno,month,lev)

                end if

             end do
          end do
       end do
    end do
    !--------------------------------------------------------
    !	... initialize the monthly day of year times
    !--------------------------------------------------------
    do month = 1,12
       days(month) = get_calday( dates(month), 0 )
    end do

    !--------------------------------------------------------
    !   	... set up the relaxation for lower stratosphere
    !--------------------------------------------------------
    ! 	... taurelax = relaxation timescale (in sec)
    !           facrelax = fractional relaxation towards ubc
    !            1 => use ubc
    !            0 => ignore ubc, use model concentrations
    !--------------------------------------------------------
    dtime = get_step_size()
    facrelax = 1._r8 - exp( -real(dtime)/taurelax )

    !--------------------------------------------------------
    ! 	... setup conserving interp for OX
    !--------------------------------------------------------
    if( table_ox_ndx > 0 ) then
       allocate( ub_plevse(ub_nlevs-1), stat=ierr )
       if( ierr /= 0 ) then
          write(iulog,*) 'fstrat_inti: ub_plevse allocation error = ',ierr
          call endrun
       end if
       ub_plevse(1:ub_nlevs-1) = .5_r8*(ub_plevs(1:ub_nlevs-1) + ub_plevs(2:ub_nlevs))
    end if

  end subroutine fstrat_inti

  subroutine set_fstrat_vals( vmr, pmid, pint, ltrop, calday, ncol,lchnk )

    !--------------------------------------------------------------------
    !	... set the upper boundary values for :
    !           ox, nox, hno3, ch4, co, n2o, n2o5 & stratospheric o3
    !--------------------------------------------------------------------

    use mo_synoz, only : synoz_region => po3

    implicit none

    !--------------------------------------------------------------------
    !	... dummy args
    !--------------------------------------------------------------------
    integer,  intent(in)    :: lchnk             ! chunk number
    integer,  intent(in)    :: ncol              ! columns in chunk
    integer,  intent(in)    :: ltrop(pcols)      ! tropopause vertical index
    real(r8), intent(in)    :: calday            ! day of year including fraction
    real(r8), intent(in)    :: pmid(pcols,pver)  ! midpoint pressure (Pa)
    real(r8), intent(in)    :: pint(pcols,pverp) ! interface pressure (Pa)
    real(r8), intent(inout) :: vmr(ncol,pver,gas_pcnst) ! species concentrations (mol/mol)

    !--------------------------------------------------------------------
    !	... local variables
    !--------------------------------------------------------------------
    integer, parameter :: zlower = pver
    real(r8), parameter    :: synoz_thres = 100.e-9_r8      ! synoz threshold
    real(r8), parameter    :: o3rad_relax = 0.5_r8*86400._r8 ! 1/2 day relaxation constant
    real(r8), parameter    :: synoz_relax = 2._r8*86400._r8 ! 2 day relaxation constant
    real(r8), parameter    :: synoz_strat_relax = 5._r8*86400._r8 ! 5 day relaxation constant

    integer  ::  m, last, next, i, k, k1, km
    integer  ::  astat
    integer  ::  kmax(ncol)
    integer  ::  levrelax
    integer  ::  kl(ncol,zlower)
    integer  ::  ku(ncol,zlower)
    real(r8) ::  vmrrelax
    real(r8) ::  fac_relax
    real(r8) ::  pinterp
    real(r8) ::  nox_ubc, xno, xno2, rno
    real(r8) ::  dels
    real(r8) ::  delp(ncol,zlower)
    real(r8) ::  pint_vals(2)
    real(r8), allocatable :: table_ox(:)
    logical  ::  found_trop
    integer  :: lat

    if (.not. any(has_fstrat(:))) return

    !--------------------------------------------------------
    !	... setup the time interpolation
    !--------------------------------------------------------
    if( calday < days(1) ) then
       next = 1
       last = 12
       dels = (365._r8 + calday - days(12)) / (365._r8 + days(1) - days(12))
    else if( calday >= days(12) ) then
       next = 1
       last = 12
       dels = (calday - days(12)) / (365._r8 + days(1) - days(12))
    else
       do m = 11,1,-1
          if( calday >= days(m) ) then
             exit
          end if
       end do
       last = m
       next = m + 1
       dels = (calday - days(m)) / (days(m+1) - days(m))
    end if
    dels = max( min( 1._r8,dels ),0._r8 )

    !--------------------------------------------------------
    !	... setup the pressure interpolation
    !--------------------------------------------------------
    do k = 1,zlower
       do i = 1,ncol
          if( pmid(i,k) <= ub_plevs(1) ) then
             kl(i,k) = 1
             ku(i,k) = 1
             delp(i,k) = 0._r8
          else if( pmid(i,k) >= ub_plevs(ub_nlevs) ) then
             kl(i,k) = ub_nlevs
             ku(i,k) = ub_nlevs
             delp(i,k) = 0._r8
          else
             pinterp = pmid(i,k)
             do k1 = 2,ub_nlevs
                if( pinterp <= ub_plevs(k1) ) then
                   ku(i,k) = k1
                   kl(i,k) = k1 - 1
                   delp(i,k) = log( pinterp/ub_plevs(kl(i,k)) ) &
                        / log( ub_plevs(ku(i,k))/ub_plevs(kl(i,k)) )
                   exit
                end if
             end do
          end if
       end do
    end do

    !--------------------------------------------------------
    !	... find max level less than 50 mb
    !           fix UB vals from top of model to this level
    !--------------------------------------------------------
    do i = 1,ncol
       do k = 2,pver
          if( pmid(i,k) > 50.e2_r8 ) then
             kmax(i) = k
             exit
          end if
       end do
    end do

    !--------------------------------------------------------
    !	... setup for ox conserving interp
    !--------------------------------------------------------
    if( table_ox_ndx > 0 ) then
       if( map(table_ox_ndx) > 0 ) then
          allocate( table_ox(ub_nlevs-2),stat=astat )
          if( astat /= 0 ) then
             write(iulog,*) 'set_fstrat_vals: table_ox allocation error = ',astat
             call endrun
          end if
#ifdef UB_DEBUG
          write(iulog,*) ' '
          write(iulog,*) 'set_fstrat_vals: ub_nlevs = ',ub_nlevs
          write(iulog,*) 'set_fstrat_vals: ub_plevse'
          write(iulog,'(1p5g15.7)') ub_plevse(:)
          write(iulog,*) ' '
#endif
       end if
    end if

    !--------------------------------------------------------
    !	... set the mixing ratios at upper boundary
    !--------------------------------------------------------
    species_loop : do m = 1,ub_nspecies
       ub_overwrite : if( map(m) > 0 ) then
          if( m == table_ox_ndx ) then
             do i = 1,ncol

                table_ox(1:ub_nlevs-2) = mr_ub(i,m,last,2:ub_nlevs-1,lchnk) &
                     + dels*(mr_ub(i,m,next,2:ub_nlevs-1,lchnk) &
                     - mr_ub(i,m,last,2:ub_nlevs-1,lchnk))
#ifdef UB_DEBUG
                write(iulog,*) 'set_fstrat_vals: table_ox @ lat = ',lat
                write(iulog,'(1p5g15.7)') table_ox(:)
                write(iulog,*) ' '
#endif

                km = kmax(i)
#ifdef UB_DEBUG
                write(iulog,*) 'set_fstrat_vals: pint with km = ',km
                write(iulog,'(1p5g15.7)') pint(i,:km+1)
                write(iulog,*) ' '
                write(iulog,*) 'set_fstrat_vals: pmid with km = ',km
                write(iulog,'(1p5g15.7)') pmid(i,:km)
                write(iulog,*) ' '
#endif
                call rebin( ub_nlevs-2, km, ub_plevse, pint(i,:km+1), table_ox, vmr(i,:km,map(m)) )
#ifdef UB_DEBUG
                write(iulog,*) 'set_fstrat_vals: ub o3 @ lat = ',lat
                write(iulog,'(1p5g15.7)') vmr(i,:km,map(m))
#endif
             end do
             deallocate( table_ox )
             cycle species_loop
          end if
          do i = 1,ncol
             do k = 1,kmax(i)
                pint_vals(1) = mr_ub(i,m,last,kl(i,k),lchnk) &
                     + delp(i,k) &
                     * (mr_ub(i,m,last,ku(i,k),lchnk) &
                     - mr_ub(i,m,last,kl(i,k),lchnk))
                pint_vals(2) = mr_ub(i,m,next,kl(i,k),lchnk) &
                     + delp(i,k) &
                     * (mr_ub(i,m,next,ku(i,k),lchnk) &
                     - mr_ub(i,m,next,kl(i,k),lchnk))
                if( m /= table_nox_ndx .and. m /= table_h2o_ndx .and. m /= table_synoz_ndx ) then
                   vmr(i,k,map(m)) = pint_vals(1) &
                        + dels * (pint_vals(2) - pint_vals(1))
                else if( m == table_nox_ndx .and. sim_has_nox ) then

                   nox_ubc = pint_vals(1) + dels * (pint_vals(2) - pint_vals(1))
                   if( no_ndx > 0 ) then
                      xno  = vmr(i,k,no_ndx)
                   else
                      xno  = 0._r8
                   end if
                   if( no2_ndx > 0 ) then
                      xno2 = vmr(i,k,no2_ndx)
                   else
                      xno2 = 0._r8
                   end if
                   rno  = xno / (xno + xno2)
                   if( no_ndx > 0 ) then
                      vmr(i,k,no_ndx)  = rno * nox_ubc
                   end if
                   if( no2_ndx > 0 ) then
                      vmr(i,k,no2_ndx) = (1._r8 - rno) * nox_ubc
                   end if
                end if
             end do
          end do
       end if ub_overwrite
    end do species_loop

    col_loop2 : do i = 1,ncol
       !--------------------------------------------------------
       ! 	... relax lower stratosphere to extended ubc
       !           check to make sure ubc is not being imposed too low
       !           levrelax = lowest model level (highest pressure)
       !                      in which to relax to ubc
       !--------------------------------------------------------
       levrelax = ltrop(i)
       do while( pmid(i,levrelax) > ub_plevs(ub_nlevs) )
          levrelax = levrelax - 1
       end do
#ifdef DEBUG
       if( levrelax /= ltrop(i) ) then
          write(iulog,*) 'warning -- raised ubc: ',lat,i,
          ltrop(i)-1,nint(pmid(i,ltrop(i)-1)/mb2pa),'mb -->',
          levrelax,nint(pmid(i,levrelax)/mb2pa),'mb'
       end if
#endif
       level_loop2 : do k = kmax(i)+1,levrelax
          if( sim_has_nox ) then
             if( no_ndx > 0 ) then
                xno  = vmr(i,k,no_ndx)
             else
                xno  = 0._r8
             end if
             if( no2_ndx > 0 ) then
                xno2 = vmr(i,k,no2_ndx)
             else
                xno2 = 0._r8
             end if
             rno     = xno / (xno + xno2)
             nox_ubc = xno + xno2
          end if
          do m = 1,ub_nspecies
             if( map(m) < 1 ) then
                cycle
             end if
             pint_vals(1) = mr_ub(i,m,last,kl(i,k),lchnk) &
                  + delp(i,k) &
                  * (mr_ub(i,m,last,ku(i,k),lchnk) &
                  - mr_ub(i,m,last,kl(i,k),lchnk))
             pint_vals(2) = mr_ub(i,m,next,kl(i,k),lchnk) &
                  + delp(i,k) &
                  * (mr_ub(i,m,next,ku(i,k),lchnk) &
                  - mr_ub(i,m,next,kl(i,k),lchnk))
             vmrrelax = pint_vals(1) &
                  + dels * (pint_vals(2) - pint_vals(1))
             if( m /= table_nox_ndx .and. m /= table_h2o_ndx  .and. m /= table_synoz_ndx ) then
                vmr(i,k,map(m)) = vmr(i,k,map(m)) &
                     + (vmrrelax - vmr(i,k,map(m))) * facrelax
             else if( m == table_nox_ndx .and. sim_has_nox) then

                nox_ubc = nox_ubc + (vmrrelax - nox_ubc) * facrelax
             end if
          end do
          if( sim_has_nox ) then
             if( no_ndx > 0 ) then
                vmr(i,k,no_ndx)  = rno * nox_ubc
             end if
             if( no2_ndx > 0 ) then
                vmr(i,k,no2_ndx) = (1._r8 - rno) * nox_ubc
             end if
          end if
       end do level_loop2

       has_synoz : if( synoz_ndx > 0 ) then
          if ( synoz_ndx > 0 .and. table_synoz_ndx > 0 ) then
             fac_relax = 1._r8 - exp( -real(dtime) / synoz_strat_relax )
             do k = 1,pver
                m = table_synoz_ndx
                if ( synoz_region(i,k,lchnk) > 0._r8 ) then
                   pint_vals(1) = mr_ub(i,m,last,kl(i,k),lchnk) &
                        + delp(i,k) &
                        * (mr_ub(i,m,last,ku(i,k),lchnk) &
                        - mr_ub(i,m,last,kl(i,k),lchnk))
                   pint_vals(2) = mr_ub(i,m,next,kl(i,k),lchnk) &
                        + delp(i,k) &
                        * (mr_ub(i,m,next,ku(i,k),lchnk) &
                        - mr_ub(i,m,next,kl(i,k),lchnk))
                   vmrrelax = pint_vals(1) &
                        + dels * (pint_vals(2) - pint_vals(1))
                   vmr(i,k,map(m)) = vmr(i,k,map(m)) &
                        + (vmrrelax - vmr(i,k,map(m))) * fac_relax
                endif
             enddo
          endif
 
          !--------------------------------------------------------
          ! 	... special assignments if synoz is present
          !           update ox, o3s, o3inert in the stratosphere
          !--------------------------------------------------------
          if( ox_ndx > 0 ) then
             do k = 1,levrelax
                if( vmr(i,k,synoz_ndx) >= synoz_thres ) then
                   vmr(i,k,ox_ndx) = vmr(i,k,synoz_ndx)
                end if
             end do
          end if
          if( o3s_ndx > 0 ) then
             do k = 1,levrelax
                if( vmr(i,k,synoz_ndx) >= synoz_thres ) then
                   vmr(i,k,o3s_ndx) = vmr(i,k,synoz_ndx)
                end if
             end do
          end if
          if( o3rad_ndx > 0 .and. o3inert_ndx > 0 ) then
             vmr(i,:ltrop(i),o3inert_ndx) = vmr(i,:ltrop(i),o3rad_ndx)
          end if
          !--------------------------------------------------------
          ! 	... O3RAD is relaxed to climatology in the stratosphere
          !           (done above) and OX in the troposphere
          !--------------------------------------------------------
          if( o3rad_ndx > 0 .and. ox_ndx > 0 ) then
             fac_relax = 1._r8 - exp( -real(dtime) / o3rad_relax )
             do k = levrelax+1,pver
                vmr(i,k,o3rad_ndx) = vmr(i,k,o3rad_ndx) &
                     + (vmr(i,k,ox_ndx) - vmr(i,k,o3rad_ndx)) * fac_relax
             end do
          end if
          !--------------------------------------------------------
          ! 	... relax synoz to 25 ppbv in lower troposphere
          !           (p > 500 hPa) with an e-fold time of 2 days
          !           (Mc Linden et al., JGR, p14,660, 2000)
          !--------------------------------------------------------
          fac_relax = 1._r8 - exp( -real(dtime) / synoz_relax )
          vmrrelax = 25.e-9_r8
          do k = levrelax+2,pver
             if( pmid(i,k) >= 50000._r8 ) then
                vmr(i,k,synoz_ndx) = vmr(i,k,synoz_ndx) &
                     + (vmrrelax - vmr(i,k,synoz_ndx)) * fac_relax
             end if
          end do
       else has_synoz

          !--------------------------------------------------------
          !       ... set O3S and O3INERT to OX when no synoz
          !--------------------------------------------------------
          if( ox_ndx > 0 ) then
             if( o3s_ndx > 0 ) then
                vmr(i,:ltrop(i),o3s_ndx)     = vmr(i,:ltrop(i),ox_ndx)
             end if
             if( o3inert_ndx > 0 ) then
                vmr(i,:ltrop(i),o3inert_ndx) = vmr(i,:ltrop(i),ox_ndx)
             end if
          end if

       end if has_synoz

       if ( o3a_ndx > 0 ) then
          vmr(i,:ltrop(i),o3a_ndx) = (1._r8 - facrelax ) * vmr(i,:ltrop(i),o3a_ndx)
       endif
       if ( xno_ndx > 0 ) then
          vmr(i,:ltrop(i),xno_ndx) = (1._r8 - facrelax ) * vmr(i,:ltrop(i),xno_ndx)
       endif
       if ( xno2_ndx > 0 ) then
          vmr(i,:ltrop(i),xno2_ndx) = (1._r8 - facrelax ) * vmr(i,:ltrop(i),xno2_ndx)
       endif

    end do col_loop2

  end subroutine set_fstrat_vals

  subroutine set_fstrat_h2o( h2o, pmid, ltrop, calday, ncol, lchnk )
    !--------------------------------------------------------------------
    !	... set the h2o upper boundary values
    !--------------------------------------------------------------------


    implicit none

    !--------------------------------------------------------------------
    !	... dummy args
    !--------------------------------------------------------------------
    integer,  intent(in)    :: ncol              ! columns in chunk
    integer,  intent(in)    :: ltrop(pcols)      ! tropopause vertical index
    integer,  intent(in)    :: lchnk
    real(r8), intent(in)    :: calday            ! day of year including fraction
    real(r8), intent(in)    :: pmid(pcols,pver)  ! midpoint pressure (Pa)
    real(r8), intent(inout) :: h2o(ncol,pver)    ! h2o concentration (mol/mol)

    !--------------------------------------------------------------------
    !	... local variables
    !--------------------------------------------------------------------
    integer, parameter :: zlower = pver

    integer  ::  m, last, next, i, k, k1
    integer  ::  kmax(ncol)
    integer  ::  levrelax
    integer  ::  kl(ncol,zlower)
    integer  ::  ku(ncol,zlower)
    real(r8) ::  vmrrelax
    real(r8) ::  fac_relax
    real(r8) ::  pinterp
    real(r8) ::  dels
    real(r8) ::  delp(ncol,zlower)
    real(r8) ::  pint_vals(2)
    logical  ::  found_trop
    integer  ::  lat

    h2o_overwrite : if( h2o_ndx > 0 .and. table_h2o_ndx > 0 ) then
       !--------------------------------------------------------
       !	... setup the time interpolation
       !--------------------------------------------------------
       if( calday < days(1) ) then
          next = 1
          last = 12
          dels = (365._r8 + calday - days(12)) / (365._r8 + days(1) - days(12))
       else if( calday >= days(12) ) then
          next = 1
          last = 12
          dels = (calday - days(12)) / (365._r8 + days(1) - days(12))
       else
          do m = 11,1,-1
             if( calday >= days(m) ) then
                exit
             end if
          end do
          last = m
          next = m + 1
          dels = (calday - days(m)) / (days(m+1) - days(m))
       end if
       dels = max( min( 1._r8,dels ),0._r8 )

       !--------------------------------------------------------
       !	... setup the pressure interpolation
       !--------------------------------------------------------
       do k = 1,zlower
          do i = 1,ncol
             if( pmid(i,k) <= ub_plevs(1) ) then
                kl(i,k) = 1
                ku(i,k) = 1
                delp(i,k) = 0._r8
             else if( pmid(i,k) >= ub_plevs(ub_nlevs) ) then
                kl(i,k) = ub_nlevs
                ku(i,k) = ub_nlevs
                delp(i,k) = 0._r8
             else
                pinterp = pmid(i,k)
                do k1 = 2,ub_nlevs
                   if( pinterp <= ub_plevs(k1) ) then
                      ku(i,k) = k1
                      kl(i,k) = k1 - 1
                      delp(i,k) = log( pinterp/ub_plevs(kl(i,k)) ) &
                           / log( ub_plevs(ku(i,k))/ub_plevs(kl(i,k)) )
                      exit
                   end if
                end do
             end if
          end do
       end do

       !--------------------------------------------------------
       !	... Find max level less than 50 mb
       !           fix UB vals from top of model to this level
       !--------------------------------------------------------
       do i = 1,ncol
          do k = 2,pver
             if( pmid(i,k) > 50.e2_r8 ) then
                kmax(i) = k
                exit
             end if
          end do
       end do
       !--------------------------------------------------------
       !	... set the mixing ratio at upper boundary
       !--------------------------------------------------------
       m = table_h2o_ndx
       do i = 1,ncol
          do k = 1,kmax(i)
             pint_vals(1) = mr_ub(i,m,last,kl(i,k),lchnk) &
                  + delp(i,k) &
                  * (mr_ub(i,m,last,ku(i,k),lchnk) &
                  - mr_ub(i,m,last,kl(i,k),lchnk))
             pint_vals(2) = mr_ub(i,m,next,kl(i,k),lchnk) &
                  + delp(i,k) &
                  * (mr_ub(i,m,next,ku(i,k),lchnk) &
                  - mr_ub(i,m,next,kl(i,k),lchnk))
             h2o(i,k) = pint_vals(1) &
                  + dels * (pint_vals(2) - pint_vals(1))
          end do
       end do

       col_loop2 : do i = 1,ncol
          !--------------------------------------------------------
          ! 	... relax lower stratosphere to extended ubc
          !           check to make sure ubc is not being imposed too low
          !           levrelax = lowest model level (highest pressure)
          !                      in which to relax to ubc
          !--------------------------------------------------------
          levrelax = ltrop(i)
          do while( pmid(i,levrelax) > ub_plevs(ub_nlevs) )
             levrelax = levrelax - 1
          end do
#ifdef DEBUG
          if( levrelax /= ltrop(i) ) then
             write(iulog,*) 'warning -- raised ubc: ',lat,i,
             ltrop(i)-1,nint(pmid(i,ltrop(i)-1)/100._r8),'mb -->',
             levrelax,nint(pmid(i,levrelax)/100._r8),'mb'
          end if
#endif
          do k = kmax(i)+1,levrelax
             pint_vals(1) = mr_ub(i,m,last,kl(i,k),lchnk) &
                  + delp(i,k) &
                  * (mr_ub(i,m,last,ku(i,k),lchnk) &
                  - mr_ub(i,m,last,kl(i,k),lchnk))
             pint_vals(2) = mr_ub(i,m,next,kl(i,k),lchnk) &
                  + delp(i,k) &
                  * (mr_ub(i,m,next,ku(i,k),lchnk) &
                  - mr_ub(i,m,next,kl(i,k),lchnk))
             vmrrelax = pint_vals(1) &
                  + dels * (pint_vals(2) - pint_vals(1))
             h2o(i,k) = h2o(i,k) + (vmrrelax - h2o(i,k)) * facrelax
          end do
       end do col_loop2
    end if h2o_overwrite

  end subroutine set_fstrat_h2o

  subroutine rebin( nsrc, ntrg, src_x, trg_x, src, trg )
    !---------------------------------------------------------------
    !	... rebin src to trg
    !---------------------------------------------------------------

    implicit none

    !---------------------------------------------------------------
    !	... dummy arguments
    !---------------------------------------------------------------
    integer, intent(in)   :: nsrc                  ! dimension source array
    integer, intent(in)   :: ntrg                  ! dimension target array
    real(r8), intent(in)  :: src_x(nsrc+1)         ! source coordinates
    real(r8), intent(in)  :: trg_x(ntrg+1)         ! target coordinates
    real(r8), intent(in)  :: src(nsrc)             ! source array
    real(r8), intent(out) :: trg(ntrg)             ! target array

    !---------------------------------------------------------------
    !	... local variables
    !---------------------------------------------------------------
    integer  :: i, l
    integer  :: si, si1
    integer  :: sil, siu
    real(r8) :: y
    real(r8) :: sl, su
    real(r8) :: tl, tu

    !---------------------------------------------------------------
    !	... check interval overlap
    !---------------------------------------------------------------
    !     if( trg_x(1) < src_x(1) .or. trg_x(ntrg+1) > src_x(nsrc+1) ) then
    !        write(iulog,*) 'rebin: target grid is outside source grid'
    !        write(iulog,*) '       target grid from ',trg_x(1),' to ',trg_x(ntrg+1)
    !        write(iulog,*) '       source grid from ',src_x(1),' to ',src_x(nsrc+1)
    !        call endrun
    !     end if

    do i = 1,ntrg
       tl = trg_x(i)
       if( tl < src_x(nsrc+1) ) then
          do sil = 1,nsrc+1
             if( tl <= src_x(sil) ) then
                exit
             end if
          end do
          tu = trg_x(i+1)
          do siu = 1,nsrc+1
             if( tu <= src_x(siu) ) then
                exit
             end if
          end do
          y   = 0._r8
          sil = max( sil,2 )
          siu = min( siu,nsrc+1 )
          do si = sil,siu
             si1 = si - 1
             sl  = max( tl,src_x(si1) )
             su  = min( tu,src_x(si) )
             y   = y + (su - sl)*src(si1)
          end do
          trg(i) = y/(trg_x(i+1) - trg_x(i))
       else
          trg(i) = 0._r8
       end if
    end do

  end subroutine rebin

end module mo_fstrat
