!-----------------------------------------------------------------------
! Common interfaces for the two ATM versions available:
!   ATM_1985  for the "Old" code
!   ATM_2009  for the latest code used starting from March 2009
!-----------------------------------------------------------------------
module atm_version
  !---------------------------------------------------------------------
  ! ATM parameters which are mapped on SIC variables.
  ! For portability, SAVE Fortran variables which are target of SIC
  ! variables.
  !---------------------------------------------------------------------
  character(len=4), save :: version='2009'  ! Default at startup
  !
  character(len=20), save :: ctype ! Atmosphere type as a string
  real(4), save :: humidity,wvsh,tlr,top,p_step,p_step_factor
  !
end module atm_version
!
subroutine atm_sicvariables(error)
  use gkernel_interfaces
  use atm_interfaces, except_this=>atm_sicvariables
  use atm_version
  !---------------------------------------------------------------------
  ! @ public
  ! Instantiate SIC variables
  !---------------------------------------------------------------------
  logical, intent(inout) :: error  ! Logical error flag
  !
  call sic_defstructure ('ATM',.true.,error)
  if (error) return
  call sic_defstructure ('ATM%MODEL',.true.,error)
  if (error) return
  call sic_defstructure ('ATM%MODEL%PROFILE',.true.,error)
  if (error) return
  !
  ! ATM internal values
  call sic_def_char ('ATM%MODEL%VERSION',version,.true.,error)
  if (error) return
  !
  ! Get internal default values of ATM library
  call atm_atmosp_variables_get(error)
  if (error) return
  call sic_def_char ('ATM%MODEL%PROFILE%TYPE',ctype,.false.,error)
  if (error) return
  call sic_def_real ('ATM%MODEL%PROFILE%HUMIDITY',humidity,0,1,.false.,error)
  if (error) return
  call sic_def_real ('ATM%MODEL%PROFILE%WVSH',wvsh,0,1,.false.,error)
  if (error) return
  call sic_def_real ('ATM%MODEL%PROFILE%TLR',tlr,0,1,.false.,error)
  if (error) return
  call sic_def_real ('ATM%MODEL%PROFILE%TOP',top,0,1,.false.,error)
  if (error) return
  call sic_def_real ('ATM%MODEL%PROFILE%DP',p_step,0,1,.false.,error)
  if (error) return
  call sic_def_real ('ATM%MODEL%PROFILE%DP_FACTOR',p_step_factor,0,1,.false.,error)
  if (error) return
  !
end subroutine atm_sicvariables
!
subroutine atm_print(error)
  use gbl_message
  use atm_version
  !---------------------------------------------------------------------
  ! @ public
  ! Displays the values of ATM parameters
  !---------------------------------------------------------------------
  logical, intent(inout) :: error  ! Logical error flag
  ! Local
  character(len=message_length) :: mess
  !
  write(mess,100) 'ATM%VERSION',version,'','Current version in use'
  call gagout(mess)
  !
  if (version.ne.'2009' .and. version.ne.'2016') return  ! Parameters below are used only in ATM 2009 or 2016
  !
  write(mess,100) 'ATM%PROFILE%TYPE',ctype,'','Atmospheric type'
  call gagout(mess)
  !
  write(mess,101) 'ATM%PROFILE%HUMIDITY',humidity,'[%]','Ground relative humidity (indication)'
  call gagout(mess)
  !
  write(mess,101) 'ATM%PROFILE%WVSH',wvsh,'[km]','Water vapor scale height'
  call gagout(mess)
  !
  write(mess,101) 'ATM%PROFILE%TLR',tlr,'[K/km]','Tropospheric lapse rate'
  call gagout(mess)
  !
  write(mess,101) 'ATM%PROFILE%TOP',top,'[km]','Upper atm. boundary for calculations'
  call gagout(mess)
  !
  write(mess,101) 'ATM%PROFILE%DP',p_step,'[hPa]','Primary pressure step'
  call gagout(mess)
  !
  write(mess,101) 'ATM%PROFILE%DP_FACTOR',p_step_factor,'[]','Pressure step ratio between two consecutive layers'
  call gagout(mess)
  !
100 format(A,T23,A12,  T36,A6,T43,A)
101 format(A,T23,F12.3,T36,A6,T43,A)
  !
end subroutine atm_print
!
subroutine atm_setup(inversion,error)
  use atm_version
  use gkernel_interfaces
  !---------------------------------------------------------------------
  ! @ public
  ! Set the ATM version to be used. Supported versions are 1985 and
  ! 2009. 'OLD' (resp. 'NEW') is an alias for 1985 (resp. 2009).
  !---------------------------------------------------------------------
  character(len=*), intent(in)    :: inversion  ! User input version
  logical,          intent(inout) :: error      ! Error flag
  ! Local
  character(len=4) :: versions(5)
  integer(kind=4) :: mversion,ikey
  character(len=5) :: key
  !
  mversion = 1
  versions(mversion) = 'OLD'
#if defined(ATM2009)
  mversion = mversion+1
  versions(mversion) = 'NEW'
#endif
  mversion = mversion+1
  versions(mversion) = '1985'
#if defined(ATM2009)
  mversion = mversion+1
  versions(mversion) = '2009'
#endif
#if defined(ATM2016)
  mversion = mversion+1
  versions(mversion) = '2016'
#endif
  !
  call sic_ambigs('ATM',inversion,key,ikey,versions,mversion,error)
  if (error)  return
  !
  if (key.eq.'OLD') then
    version = '1985'
  elseif (key.eq.'NEW') then
    version = '2009'
  else
    version = key
  endif
  !
end subroutine atm_setup
!
function atm_get_version()
  use atm_version
  !---------------------------------------------------------------------
  ! @ public
  ! get atm version
  !---------------------------------------------------------------------
  character(len=4) :: atm_get_version
  atm_get_version=version
end function atm_get_version
!
subroutine atm_atmosp(t0,p0,h0)
  use atm_interfaces, except_this=>atm_atmosp
  use atm_version
  !---------------------------------------------------------------------
  ! @ public
  ! Setup the atmospheric structure
  !---------------------------------------------------------------------
  real, intent(in) :: t0  ! Ground temperature [K]
  real, intent(in) :: p0  ! Ground pressure [hPa]
  real, intent(in) :: h0  ! Altitude of the site [km]
  ! Local
  logical :: error
  !
  ! Update with (possibly) new values always before calling 'atm_atmosp'
  error = .false.
  call atm_atmosp_variables_set(error)
  if (error) return
  !
  select case (version)
  case('1985')
     call atm_1985_atmosp(t0,p0,h0)
  !
#if defined(ATM2009)
  case('2009')
     call atm_2009_atmosp(t0,p0,h0)
#endif
  !
#if defined(ATM2016)
  case('2016')
     call atm_2016_atmosp(t0,p0,h0)
#endif
  !
  case default
     call gagout('E-ATM_ATMOSP,  '//trim(version)//' version not available on this system')
  end select
  !
end subroutine atm_atmosp
!
subroutine atm_transm_0d(water,airmass,freq,temi,tatm,tauox,tauw,taut,ier)
  use atm_interfaces, except_this=>atm_transm_0d
  use atm_version
  !---------------------------------------------------------------------
  ! @ public-generic atm_transm
  ! Return the atmospheric temperatures and opacities given the amount
  ! of water and the frequency, for current atmosphere previously set
  ! with 'atm_atmosp()'
  !---
  ! This version for scalar frequency as input
  !---------------------------------------------------------------------
  real(kind=4),    intent(in)  :: water    ! [mm]    Precipitable water vapo
  real(kind=4),    intent(in)  :: airmass  ! []      Number of air masses
  real(kind=4),    intent(in)  :: freq     ! [GHz]   Frequency
  real(kind=4),    intent(out) :: temi     ! [K]     Atmospheric emission
  real(kind=4),    intent(out) :: tatm     ! [K]     Mean temperature
  real(kind=4),    intent(out) :: tauox    ! [neper] Opacity (dry component)
  real(kind=4),    intent(out) :: tauw     ! [neper] Opacity (wet component)
  real(kind=4),    intent(out) :: taut     ! [neper] Total opacity
  integer(kind=4), intent(out) :: ier      ! []      Error status
  !
  select case (version)
  case('1985')
     call atm_1985_transm(water,airmass,freq,temi,tatm,tauox,tauw,taut,ier)
  !
#if defined(ATM2009)
  case('2009')
     call atm_2009_transm(water,airmass,freq,temi,tatm,tauox,tauw,taut,ier)
#endif
  !
#if defined(ATM2016)
  case('2016')
     call atm_2016_transm(water,airmass,freq,temi,tatm,tauox,tauw,taut,ier)
#endif
  !
  case default
     call gagout('E-ATM_TRANSM,  '//trim(version)//' version not available on this system')
  end select
  !
end subroutine atm_transm_0d
!
subroutine atm_transm_1d(water,airmass,freq,temi,tatm,tauox,tauw,taut,ier)
  use atm_interfaces, except_this=>atm_transm_1d
  use atm_version
  !---------------------------------------------------------------------
  ! @ public-generic atm_transm
  ! Return the atmospheric temperatures and opacities given the amount
  ! of water and the frequency, for current atmosphere previously set
  ! with 'atm_atmosp()'
  !---
  ! This version for vector frequency as input, and vector outputs
  !---------------------------------------------------------------------
  real(kind=4),    intent(in)  :: water     ! [mm]    Precipitable water vapor
  real(kind=4),    intent(in)  :: airmass   ! []      Number of air masses
  real(kind=4),    intent(in)  :: freq(:)   ! [GHz]   Frequency
  real(kind=4),    intent(out) :: temi(:)   ! [K]     Atmospheric emission
  real(kind=4),    intent(out) :: tatm(:)   ! [K]     Mean temperature
  real(kind=4),    intent(out) :: tauox(:)  ! [neper] Opacity (dry component)
  real(kind=4),    intent(out) :: tauw(:)   ! [neper] Opacity (wet component)
  real(kind=4),    intent(out) :: taut(:)   ! [neper] Total opacity
  integer(kind=4), intent(out) :: ier(:)    ! []      Error status
  ! Local
  integer(kind=4) :: ifreq,nfreq
  logical :: match
  !
  ! Sanity checks
  nfreq = size(freq)
  match = size(temi).eq.nfreq .and.  &
          size(tatm).eq.nfreq .and.  &
          size(tauox).eq.nfreq .and.  &
          size(tauw).eq.nfreq .and.  &
          size(taut).eq.nfreq .and.  &
          size(ier).eq.nfreq
  if (.not.match) then
    call gagout('E-ATM_TRANSM_1D,  Frequency array and output arrays do not match')
    ier(:) = 1  ! Not sure which output code should be used
    return
  endif
  !
  !*********************************************************************
  ! BEWARE! If you plan to parallelize this loop, beware there are
  ! global variables SAVE'd in atm_1985_transm. Have to check the other
  ! ATM versions too.
  !*********************************************************************
  !
  select case (version)
  case('1985')
    do ifreq=1,nfreq
      call atm_1985_transm(water,airmass,freq(ifreq),  &
        temi(ifreq),tatm(ifreq),tauox(ifreq),tauw(ifreq),taut(ifreq),ier(ifreq))
    enddo
  !
#if defined(ATM2009)
  case('2009')
    do ifreq=1,nfreq
      call atm_2009_transm(water,airmass,freq(ifreq),  &
        temi(ifreq),tatm(ifreq),tauox(ifreq),tauw(ifreq),taut(ifreq),ier(ifreq))
    enddo
#endif
  !
#if defined(ATM2016)
  case('2016')
    do ifreq=1,nfreq
      call atm_2016_transm(water,airmass,freq(ifreq),  &
        temi(ifreq),tatm(ifreq),tauox(ifreq),tauw(ifreq),taut(ifreq),ier(ifreq))
    enddo
#endif
  !
  case default
     call gagout('E-ATM_TRANSM,  '//trim(version)//' version not available on this system')
  end select
  !
end subroutine atm_transm_1d
!
subroutine atm_path(water,airmass,freq,path,ier)
  use atm_interfaces, except_this=>atm_path
  use atm_version
  !---------------------------------------------------------------------
  ! @ public
  ! Integrated optical length of current atmosphere
  !---------------------------------------------------------------------
  real,    intent(in)  :: water    ! Precipitable water vapor [mm]
  real,    intent(in)  :: airmass  ! Number of air masses []
  real,    intent(in)  :: freq     ! Frequency [GHz]
  real,    intent(out) :: path     ! Optical length [cm]
  integer, intent(out) :: ier      ! Error status
  !
  ier = 0
  !
  select case (version)
  case('1985')
     call atm_1985_path(water,airmass,freq,path,ier)
  !
#if defined(ATM2009)
  case('2009')
     call atm_2009_path(water,airmass,freq,path,ier)
#endif
  !
#if defined(ATM2016)
  case('2016')
     call atm_2016_path(water,airmass,freq,path,ier)
#endif
  !
  case default
     call gagout('E-ATM_PATH,  '//trim(version)//' version not available on this system')
  end select
  !
end subroutine atm_path
!
subroutine atm_atmosp_get(ctype_out,humidity_out,wvsh_out,tlr_out,top_out,  &
  p_step_out,p_step_factor_out,error)
  use atm_interfaces, except_this=>atm_atmosp_get
  use atm_version
  !---------------------------------------------------------------------
  ! @ public
  ! Public entry point to:
  !  1) get the C++ global variables into the Fortran-SIC ones,
  !  2) return these latter ones.
  ! Warning: these variables make sense only with ATM 2009 or 2016.
  ! Return ATM 2009 or 2016 values even if it is not currently used (not
  ! an error).
  !---------------------------------------------------------------------
  character(len=*), intent(out)    :: ctype_out          ! Atmospheric type (as a string)
  real(4),          intent(out)    :: humidity_out       ! [%]    Ground Relative Humidity (indication)
  real(4),          intent(out)    :: wvsh_out           ! [km]   Water vapor scale height
  real(4),          intent(out)    :: tlr_out            ! [K/km] Tropospheric lapse rate
  real(4),          intent(out)    :: top_out            ! [km]   Upper atm. boundary for calculations
  real(4),          intent(out)    :: p_step_out         ! [mb]   Primary pressure step
  real(4),          intent(out)    :: p_step_factor_out  ! []     Pressure step ratio between two consecutive layers
  logical,          intent(inout)  :: error              ! Logical error flag
  !
  ! 1) Get the C++ variables in the Fortran-SIC ones
  call atm_atmosp_variables_get(error)
  if (error)  return
  !
  ! 2) Return these values
  ctype_out         = ctype
  humidity_out      = humidity
  wvsh_out          = wvsh
  tlr_out           = tlr
  top_out           = top
  p_step_out        = p_step
  p_step_factor_out = p_step_factor
  !
end subroutine atm_atmosp_get
!
subroutine atm_atmosp_set(ctype_in,humidity_in,wvsh_in,tlr_in,top_in,  &
  p_step_in,p_step_factor_in,error)
  use atm_interfaces, except_this=>atm_atmosp_set
  use atm_version
  !---------------------------------------------------------------------
  ! @ public
  ! Public entry point to:
  !  1) set the Fortran-SIC global variables values,
  !  2) set the C++ global variables from these values.
  ! Warning: these variables make sense only with ATM 2009 or 2016.
  ! Nothing will be done if ATM 2009 or 2016 is not the version in use
  ! (not an error). NB: 'ctype_in' is not case-sensitive, and can be
  ! shorten as long as it is not ambiguous.
  !---------------------------------------------------------------------
  character(len=*), intent(in)    :: ctype_in          ! Atmospheric type (as a string)
  real(4),          intent(in)    :: humidity_in       ! [%]    Ground Relative Humidity (indication)
  real(4),          intent(in)    :: wvsh_in           ! [km]   Water vapor scale height
  real(4),          intent(in)    :: tlr_in            ! [K/km] Tropospheric lapse rate
  real(4),          intent(in)    :: top_in            ! [km]   Upper atm. boundary for calculations
  real(4),          intent(in)    :: p_step_in         ! [mb]   Primary pressure step
  real(4),          intent(in)    :: p_step_factor_in  ! []     Pressure step ratio between two consecutive layers
  logical,          intent(inout) :: error             ! Logical error flag
  !
  ! 1) Set the Fortran global values
  ctype         = ctype_in
  humidity      = humidity_in
  wvsh          = wvsh_in
  tlr           = tlr_in
  top           = top_in
  p_step        = p_step_in
  p_step_factor = p_step_factor_in
  !
  ! 2) Set the C++ global variables from the Fortran-SIC ones:
  call atm_atmosp_variables_set(error)
  if (error)  return
  !
end subroutine atm_atmosp_set
!
subroutine atm_atmosp_variables_get(error)
  use atm_interfaces, except_this=>atm_atmosp_variables_get
  use atm_version
  !---------------------------------------------------------------------
  ! @ private
  ! Get the atmospheric parameters currently in use in ATM, i.e. copy
  ! the C++ global variables in the Fortran-SIC global ones.
  ! Always get from 2009 to avoid mixing several set of parameters for
  ! each ATM version.
  !---------------------------------------------------------------------
  logical, intent(inout) :: error         ! Logical error flag
  ! Local
  integer :: itype
  !
#if defined(ATM2009)
  call atm_2009_atmosp_get(itype,humidity,wvsh,tlr,top,p_step,p_step_factor)
  call atm_20XX_atmosp_type(itype,ctype,.true.,error)
  if (error) return
#endif
  !
#if defined(ATM2016)
  call atm_2016_atmosp_get(itype,humidity,wvsh,tlr,top,p_step,p_step_factor)
  call atm_20XX_atmosp_type(itype,ctype,.true.,error)
  if (error) return
#endif
  !
end subroutine atm_atmosp_variables_get
!
subroutine atm_atmosp_variables_set(error)
  use atm_interfaces, except_this=>atm_atmosp_variables_set
  use atm_version
  !---------------------------------------------------------------------
  ! @ private
  ! Set the atmospheric parameters to be used in ATM, i.e. copy the
  ! Fortran-SIC global variables in the C++ ones.
  !---------------------------------------------------------------------
  logical, intent(inout) :: error         ! Logical error flag
  ! Local
  integer :: itype
  !
  select case (version)
  case('1985')
     continue
  !
#if defined(ATM2009)
  case('2009')
     call atm_20XX_atmosp_type(itype,ctype,.false.,error)
     if (error) return
     call atm_2009_atmosp_set(itype,humidity,wvsh,tlr,top,p_step,p_step_factor)
#endif
  !
#if defined(ATM2016)
  case('2016')
     call atm_20XX_atmosp_type(itype,ctype,.false.,error)
     if (error) return
     call atm_2016_atmosp_set(itype,humidity,wvsh,tlr,top,p_step,p_step_factor)
#endif
  !
  case default
     call gagout('E-ATM_ATMOSP_VARIABLES_SET,  '//trim(version)//' version not available on this system')
     ! error = .true.
  end select
  !
end subroutine atm_atmosp_variables_set
!
subroutine atm_20XX_atmosp_type(itype,ctype,int2str,error)
  use gbl_message
  use gkernel_interfaces
  !---------------------------------------------------------------------
  ! @ private
  ! Translate the atmosphere type either from integer to string or from
  ! string to integer.
  !---------------------------------------------------------------------
  integer,          intent(inout) :: itype   ! Type as integer
  character(len=*), intent(inout) :: ctype   ! Type as string
  logical,          intent(in)    :: int2str ! Convert integer to string or vice-versa?
  logical,          intent(inout) :: error   ! Logical error flag
  ! Local
  character(len=*), parameter :: rname='ATM_ATMOSP'
  character(len=*), parameter :: echain='E-'//rname//',  '
  integer, parameter :: ntypes=5
  character(len=16), save :: ctypes(ntypes)
  data ctypes /'TROPICAL','MIDLATSUMMER','MIDLATWINTER','SUBARCTICSUMMER','SUBARCTICWINTER' /
  character(len=20) :: ctype_u,cfound
  character(len=message_length) :: mess
  !
  if (int2str) then
    ! Convert 'itype' into 'ctype'
    if (itype.le.0 .or. itype.gt.ntypes) then
      write(mess,'(A,I0)')  &
        echain//'Unrecognized atmosphere type ',itype
      call gagout(mess)
      error = .true.
      return
    endif
    ctype=ctypes(itype)
  !
  else
    ! Convert 'ctype' into 'itype'
    itype = 0
    ctype_u = ctype
    call sic_upper(ctype_u)
    call sic_ambigs(rname,ctype_u,cfound,itype,ctypes,ntypes,error)
    if (error)  call gagout(echain//  &
      'Unrecognized atmosphere type "'//trim(ctype)//'"')
  endif
  !
end subroutine atm_20XX_atmosp_type
