module radconstants

! This module contains constants that are specific to the radiative transfer
! code used in the CAM3 model.

use shr_kind_mod,   only: r8 => shr_kind_r8
use cam_abortutils, only: endrun

implicit none
private

! public routines

   public :: get_number_sw_bands
   public :: get_sw_spectral_boundaries
   public :: get_lw_spectral_boundaries
   public :: get_ref_solar_band_irrad
   public :: get_true_ref_solar_band_irrad
   public :: get_ref_total_solar_irrad
   public :: get_solar_band_fraction_irrad
   public :: radconstants_init
   public :: rad_gas_index

! optics files specify a type.  What length is it?
integer, parameter, public :: ot_length = 32

! SHORTWAVE DATA

! number of shorwave spectral intervals
integer, parameter, public :: nswbands = 19 

integer, parameter, public :: idx_sw_diag = 8 ! index to sw visible band

! *** For interface consistency -- used only by modal_aero_optics and assumes use of RRTMG
! Need to provide a function interface to avoid this hack.
integer, parameter, public :: idx_nir_diag = 999 ! index to sw near infrared (778-1240 nm) band
integer, parameter, public :: idx_uv_diag = 999  ! index to sw uv (345-441 nm) band
! *** For interface consistency


integer, parameter, public :: idx_lw_diag = 2 ! index to (H20 window) LW band


! Number of evenly spaced intervals in rh
! The globality of this mesh may not be necessary
! Perhaps it could be specific to the aerosol
! But it is difficult to see how refined it must be
! for lookup.  This value was found to be sufficient
! for Sulfate and probably necessary to resolve the
! high variation near rh = 1.  Alternative methods
! were found to be too slow.
! Optimal approach would be for cam to specify size of aerosol
! based on each aerosol's characteristics.  Radiation 
! should know nothing about hygroscopic growth!
integer, parameter, public :: nrh = 1000  

! LONGWAVE DATA

! number of lw bands
integer, public, parameter  :: nlwbands = 7
! Index of volc. abs., H2O non-window
integer, public, parameter :: idx_LW_H2O_NONWND=1
! Index of volc. abs., H2O window
integer, public, parameter :: idx_LW_H2O_WINDOW=2
! Index of volc. cnt. abs. 0500--0650 cm-1
integer, public, parameter :: idx_LW_0500_0650=3
! Index of volc. cnt. abs. 0650--0800 cm-1
integer, public, parameter :: idx_LW_0650_0800=4
! Index of volc. cnt. abs. 0800--1000 cm-1
integer, public, parameter :: idx_LW_0800_1000=5
! Index of volc. cnt. abs. 1000--1200 cm-1
integer, public, parameter :: idx_LW_1000_1200=6
! Index of volc. cnt. abs. 1200--2000 cm-1
integer, public, parameter :: idx_LW_1200_2000=7

! GASES TREATED BY RADIATION (line spectrae)

! gasses required by radiation
integer, public, parameter :: gasnamelength = 5
integer, public, parameter :: nradgas = 8
character(len=gasnamelength), public, parameter :: gaslist(nradgas) &
   = (/'H2O  ','O3   ', 'O2   ', 'CO2  ', 'N2O  ', 'CH4  ', 'CFC11', 'CFC12'/)

! what is the minimum mass mixing ratio that can be supported by radiation implementation?
real(r8), public, parameter :: minmmr(nradgas) &
   = epsilon(1._r8)

! Solar and SW data for CAMRT

   ! Set index for cloud particle properties based on the wavelength,
   ! according to A. Slingo (1989) equations 1-3:
   ! Use index 1 (0.25 to 0.69 micrometers) for visible
   ! Use index 2 (0.69 - 1.19 micrometers) for near-infrared
   ! Use index 3 (1.19 to 2.38 micrometers) for near-infrared
   ! Use index 4 (2.38 to 4.00 micrometers) for near-infrared
   integer, public, parameter :: indxsl(nswbands) = &
        (/  1,        1,          1,          1,          1,        &
            1,        1,          1,          1,          2,          3, &
            3,        3,          3,          4,          4,        &
            4,        4,          4/)

   ! minimum wavelength of band in micrometers
   real(r8), parameter :: wavmin(nswbands) = &
        (/  0.200_r8, 0.245_r8,   0.265_r8,   0.275_r8,   0.285_r8, &
            0.295_r8, 0.305_r8,   0.350_r8,   0.640_r8,   0.700_r8,   0.700_r8, &
            0.700_r8, 0.700_r8,   0.700_r8,   0.700_r8,   0.700_r8, &
            2.630_r8, 4.160_r8,   4.160_r8/)

   ! maximum wavelength of band in micrometers
   real(r8), parameter :: wavmax(nswbands) = &
        (/  0.245_r8, 0.265_r8,   0.275_r8,   0.285_r8,   0.295_r8, &
            0.305_r8, 0.350_r8,   0.640_r8,   0.700_r8,   5.000_r8,   5.000_r8, &
            5.000_r8, 5.000_r8,   5.000_r8,   5.000_r8,   5.000_r8, &
            2.860_r8, 4.550_r8,   4.550_r8/)

   ! Fraction of solar flux in each stated spectral interval
   real(r8), public, parameter :: frcsol(nswbands) = &
     (/ .001488_r8, .001389_r8, .001290_r8, .001686_r8, .002877_r8, &
        .003869_r8, .026336_r8, .360739_r8, .065392_r8, .526861_r8, &
        .526861_r8, .526861_r8, .526861_r8, .526861_r8, .526861_r8, &
        .526861_r8, .006239_r8, .001834_r8, .001834_r8/)

   ! Weight of h2o in spectral interval
   real(r8), public, parameter :: ph2o(nswbands) = &
             (/    .000_r8,    .000_r8,    .000_r8,    .000_r8,    .000_r8, &
        .000_r8,   .000_r8,    .000_r8,    .000_r8,    .505_r8,     &
        .210_r8,   .120_r8,    .070_r8,    .048_r8,    .029_r8,     &
        .018_r8,   .000_r8,    .000_r8,    .000_r8/)

   ! Weight of co2 in spectral interval
   real(r8), public, parameter :: pco2(nswbands) = &
             (/    .000_r8,    .000_r8,    .000_r8,    .000_r8,    .000_r8, &
        .000_r8,   .000_r8,    .000_r8,    .000_r8,    .000_r8,     &
        .000_r8,   .000_r8,    .000_r8,    .000_r8,    .000_r8,     &
        .000_r8,  1.000_r8,    .640_r8,    .360_r8/)

   ! Weight of o2  in spectral interval
   real(r8), public, parameter :: po2(nswbands) = &
             (/    .000_r8,    .000_r8,    .000_r8,    .000_r8,    .000_r8, &
        .000_r8,   .000_r8,    .000_r8,   1.000_r8,   1.000_r8,     &
        .000_r8,   .000_r8,    .000_r8,    .000_r8,    .000_r8,     &
        .000_r8,   .000_r8,    .000_r8,    .000_r8/)

   real(r8) :: solfrac_true(nswbands)

   ! Longwave spectral band limits (cm-1)
   real(r8), private, parameter :: wavenumber1_longwave(nlwbands) = &
        (/10._r8,350._r8,500._r8,650._r8,800._r8,1000._r8,1200._r8/)

   ! Longwave spectral band limits (cm-1)
   real(r8), private, parameter :: wavenumber2_longwave(nlwbands) = & 
        (/350._r8,500._r8,650._r8,800._r8,1000._r8,1200._r8,2000._r8/)

contains


!------------------------------------------------------------------------------
subroutine get_number_sw_bands(number_of_bands)
   ! number of solar (shortwave) bands in the radiation code
   integer, intent(out) :: number_of_bands

   number_of_bands = nswbands

end subroutine get_number_sw_bands

!------------------------------------------------------------------------------
subroutine get_lw_spectral_boundaries(low_boundaries, high_boundaries, units)
   ! provide spectral boundaries of each longwave band

   real(r8), intent(out) :: low_boundaries(nlwbands), high_boundaries(nlwbands)
   character(*), intent(in) :: units ! requested units

   select case (units)
   case ('inv_cm','cm^-1','cm-1')
      low_boundaries  = wavenumber1_longwave
      high_boundaries = wavenumber2_longwave
   case('m','meter','meters')
      low_boundaries  = 1.e-2_r8/wavenumber2_longwave
      high_boundaries = 1.e-2_r8/wavenumber1_longwave
   case('nm','nanometer','nanometers')
      low_boundaries  = 1.e7_r8/wavenumber2_longwave
      high_boundaries = 1.e7_r8/wavenumber1_longwave
   case('um','micrometer','micrometers','micron','microns')
      low_boundaries  = 1.e4_r8/wavenumber2_longwave
      high_boundaries = 1.e4_r8/wavenumber1_longwave
   case('cm','centimeter','centimeters')
      low_boundaries  = 1._r8/wavenumber2_longwave
      high_boundaries = 1._r8/wavenumber1_longwave
   case default
      call endrun('get_lw_spectral_boundaries: spectral units not acceptable'//units)
   end select

end subroutine get_lw_spectral_boundaries

!------------------------------------------------------------------------------
subroutine get_sw_spectral_boundaries(low_boundaries, high_boundaries, units)
   ! provide spectral boundaries of each shortwave band

   real(r8), intent(out) :: low_boundaries(nswbands), high_boundaries(nswbands)
   character(*), intent(in) :: units ! requested units

   select case (units)
   case ('inv_cm','cm^-1','cm-1')
      low_boundaries = 1.e4_r8/wavmax
      high_boundaries = 1.e4_r8/wavmin
   case('m','meter','meters')
      low_boundaries = 1.e-6_r8*wavmin
      high_boundaries = 1.e-6_r8*wavmax
   case('nm','nanometer','nanometers')
      low_boundaries = 1.e3_r8*wavmin
      high_boundaries = 1.e3_r8*wavmax
   case('um','micrometer','micrometers','micron','microns')
      low_boundaries = wavmin
      high_boundaries = wavmax
   case('cm','centimeter','centimeters')
      low_boundaries = 1.e-4_r8*wavmin
      high_boundaries = 1.e-4_r8*wavmax
   case default
      call endrun('get_sw_spectral_boundaries: spectral units not acceptable'//units)
   end select

end subroutine get_sw_spectral_boundaries

!------------------------------------------------------------------------------
subroutine get_ref_solar_band_irrad( band_irrad )

   ! solar irradiance in each band (W/m^2)
   real(r8), intent(out) :: band_irrad(nswbands)

   band_irrad = frcsol

end subroutine get_ref_solar_band_irrad

!------------------------------------------------------------------------------
subroutine radconstants_init()
! The last bands are implemented as scalings to the solar flux
! so the corresponding actual flux applied to the heating
! is different from the solar in that band.  These are the
! actual solar flux applied to each band

   integer :: ns
   real(r8):: psf(nswbands)      !  scaled fractional solar spectrum in each band applied to unitary heating

   do ns = 1, nswbands
      psf(ns) = 1.0_r8
      if(ph2o(ns)/=0._r8) psf(ns) = psf(ns)*ph2o(ns)
      if(pco2(ns)/=0._r8) psf(ns) = psf(ns)*pco2(ns)
      if(po2 (ns)/=0._r8) psf(ns) = psf(ns)*po2 (ns)
      solfrac_true(ns)   = frcsol(ns)*psf(ns) 
    enddo

end subroutine radconstants_init


!------------------------------------------------------------------------------
subroutine get_true_ref_solar_band_irrad( solfrac_true_out )

   ! solar irradiance in each band (W/m^2)

   real(r8), intent(out) :: solfrac_true_out(nswbands)

   solfrac_true_out(:) = solfrac_true(:)

end subroutine get_true_ref_solar_band_irrad

!------------------------------------------------------------------------------
subroutine get_ref_total_solar_irrad(tsi)
   ! provide Total Solar Irradiance assumed by radiation

   real(r8), intent(out) :: tsi
   real(r8) :: solfrac_true(nswbands)

   call get_true_ref_solar_band_irrad( solfrac_true )
   tsi = sum(solfrac_true)

end subroutine get_ref_total_solar_irrad

!------------------------------------------------------------------------------
subroutine get_solar_band_fraction_irrad(fractional_irradiance)
   ! provide fractional solar irradiance in each band

   ! fraction of solar irradiance in each band
   real(r8), intent(out) :: fractional_irradiance(1:nswbands)
   real(r8) :: tsi ! total solar irradiance

   fractional_irradiance = frcsol

end subroutine get_solar_band_fraction_irrad

!------------------------------------------------------------------------------
integer function rad_gas_index(gasname)

   ! return the index in the gaslist array of the specified gasname

   character(len=*),intent(in) :: gasname
   integer :: igas

   rad_gas_index = -1
   do igas = 1, nradgas
      if (trim(gaslist(igas)).eq.trim(gasname)) then
         rad_gas_index = igas
         return
      endif
   enddo
   call endrun ("rad_gas_index: can not find gas with name "//gasname)
end function rad_gas_index

end module radconstants
