module ac_merge

  use vtype_module

  implicit none


  public :: ACMergeWaveNumber
  public :: ACMergeVerticalDomain

  !----------------------------------------------------------------------------

contains

  !----------------------------------------------------------------------------

  subroutine ACMergeWaveNumber( &
    & NProcs, InputFileName0, OutputFileName &
    & )

    use ni3_module

    integer         , intent(in) :: NProcs
    character(len=*), intent(in) :: InputFileName0
    character(len=*), intent(in) :: OutputFileName


    !
    ! Local variables
    !
    integer                        :: InNcID
    integer                        :: OutNcID
    character(stdstr)              :: Mode
    integer                        :: NDims
    character(stdstr), allocatable :: a_DimNames( : )
    character(stdstr)              :: Name

    integer                   :: NLev
    integer                   :: NMolNum

    character(len=extstr)     :: InputFileName

    integer     , allocatable :: m_MolNum( : )
    real(dp)    , allocatable :: r_Press(:)
    real(dp)    , allocatable :: r_Temp(:)
    real(dp)    , allocatable :: rm_VMR(:,:)
    real(dp)    , allocatable :: rm_AbsCoef(:,:)
    real(dp)                  :: WaveNum


    integer :: iRank
    integer :: NWaveNum
    integer :: NWaveNumEach

    integer :: iWaveNum

    integer :: l


    NWaveNum = 0

    do iRank = 0, NProcs-1

      write( InputFileName, '(a,a,i6.6,a)' ) &
        & InputFileName0(1:len_trim(InputFileName0)-3), &
        & "_", &
        & iRank, &
        & ".nc"

      Mode = "read"
      call ni3_open( InputFileName, Mode, InNcID )

      if ( iRank == 0 ) then
        call ni3_inq_dimlen( InNcID, "MolNum", NMolNum )
        call ni3_inq_dimlen( InNcID, "Press" , NLev    )

        allocate( m_MolNum( NMolNum ) )
        allocate( r_Press ( NLev    ) )
        allocate( r_Temp  ( NLev    ) )
        allocate( rm_VMR  ( NLev, NMolNum ) )

        call ni3_get_var( InNcID, "MolNum", m_MolNum )
        call ni3_get_var( InNcID, "Press" , r_Press  )
        call ni3_get_var( InNcID, "Temp"  , r_Temp   )
        call ni3_get_var( InNcID, "VMR"   , rm_VMR   )
      end if

      call ni3_inq_dimlen( InNcID, "WaveNum", NWaveNumEach )
      NWaveNum = NWaveNum + NWaveNumEach

      call ni3_close( InNcID )

    end do


    allocate( rm_AbsCoef( NLev, NMolNum ) )


    Mode = "new"
    call ni3_open( OutputFileName, Mode, OutNcID )

    iWaveNum = 0

    do iRank = 0, NProcs-1

      write( InputFileName, '(a,a,i6.6,a)' ) &
        & InputFileName0(1:len_trim(InputFileName0)-3), &
        & "_", &
        & iRank, &
        & ".nc"

      Mode = "read"
      call ni3_open( InputFileName, Mode, InNcID )


      if ( iRank == 0 ) then
        Name = "_GLOBAL_"
        call ni3_cp_atts( InNcID, OutNcID, Name )

        Name = "MolNum"
        call ni3_set_dim( OutNcID, Name, NI3_INT, m_MolNum )
        call ni3_cp_atts( InNcID, OutNcID, Name )

        Name = "Press"
        call ni3_set_dim( OutNcID, Name, NI3_DOUBLE, r_Press )
        call ni3_cp_atts( InNcID, OutNcID, Name )

        Name = "WaveNum"
        call ni3_def_dim( OutNcID, Name, NI3_DOUBLE, NI3_UNLIMITED )
        call ni3_cp_atts( InNcID, OutNcID, Name )

        NDims = 1
        allocate( a_DimNames( NDims ) )
        a_DimNames(1) = 'Press'
        !
        Name = "Temp"
        call ni3_def_var( OutNcID, Name, NI3_DOUBLE, NDims, a_DimNames )
        call ni3_cp_atts( InNcID, OutNcID, Name )
        call ni3_put_var( OutNcID, Name, r_Temp )
        !
        deallocate( a_DimNames )

        NDims = 2
        allocate( a_DimNames( NDims ) )
        a_DimNames(1) = 'Press'
        a_DimNames(2) = 'MolNum'
        !
        Name = "VMR"
        call ni3_def_var( OutNcID, Name, NI3_DOUBLE, NDims, a_DimNames )
        call ni3_cp_atts( InNcID, OutNcID, Name )
        call ni3_put_var( OutNcID, Name, rm_VMR )
        !
        deallocate( a_DimNames )

        NDims = 3
        allocate( a_DimNames( NDims ) )
        a_DimNames(1) = 'Press'
        a_DimNames(2) = 'MolNum'
        a_DimNames(3) = 'WaveNum'
        !
        Name = "AbsCoef"
        call ni3_def_var( OutNcID, Name, NI3_DOUBLE, NDims, a_DimNames )
        call ni3_cp_atts( InNcID, OutNcID, Name )
        !
        deallocate( a_DimNames )

      end if


      Name = "WaveNum"
      call ni3_inq_dimlen( InNcID, Name, NWaveNumEach )

      do l = 1, NWaveNumEach
        iWaveNum = iWaveNum + 1

        Name = "WaveNum"
        call ni3_get_varss( InNcID, Name, l, WaveNum )
        call ni3_put_varss( OutNcID, Name, iWaveNum, WaveNum )

        Name = "AbsCoef"
        call ni3_get_varss( InNcID, Name, l, rm_AbsCoef )
        call ni3_put_varss( OutNcID, Name, iWaveNum, rm_AbsCoef )
      end do


      call ni3_close( InNcID )

    end do

    call ni3_close( OutNcID )


    deallocate( m_MolNum   )
    deallocate( r_Press    )
    deallocate( r_Temp     )
    deallocate( rm_VMR     )
    deallocate( rm_AbsCoef )


  end subroutine ACMergeWaveNumber

  !----------------------------------------------------------------------------

  subroutine ACMergeVerticalDomain( &
    & NProcs, InputFileName0, OutputFileName &
    & )

    use ni3_module

    integer         , intent(in) :: NProcs
    character(len=*), intent(in) :: InputFileName0
    character(len=*), intent(in) :: OutputFileName


    !
    ! Local variables
    !
    integer                        :: InNcID
    integer                        :: OutNcID
    character(stdstr)              :: Mode
    integer                        :: NDims
    character(stdstr), allocatable :: a_DimNames( : )
    character(stdstr)              :: Name

    integer                   :: NLev
    integer                   :: NMolNum

    character(len=extstr)     :: InputFileName

    integer     , allocatable :: m_MolNum(:)
    real(dp)    , allocatable :: w_WaveNum(:)
    real(dp)    , allocatable :: r_Press(:)
    real(dp)    , allocatable :: r_Temp(:)
    real(dp)    , allocatable :: rm_VMR(:,:)
    real(dp)    , allocatable :: rm_AbsCoef(:,:)

    real(dp)    , allocatable :: r_PressEach(:)
    real(dp)    , allocatable :: r_TempEach (:)
    real(dp)    , allocatable :: rm_VMREach (:,:)
    real(dp)    , allocatable :: rm_AbsCoefEach(:,:)

    integer     , allocatable :: a_InNcID(:)
    integer     , allocatable :: a_NLevEach(:)
    integer     , allocatable :: a_kStartIndex(:)

    integer :: iRank
    integer :: NWaveNum

    integer :: NLevEach

    integer :: k
    integer :: l
    integer :: m


    allocate( a_InNcID     (0:NProcs-1) )
    allocate( a_NLevEach   (0:NProcs-1) )
    allocate( a_kStartIndex(0:NProcs-1) )


    do iRank = 0, NProcs-1
      write( InputFileName, '(a,a,i6.6,a)' ) &
        & InputFileName0(1:len_trim(InputFileName0)-3), &
        & "_", &
        & iRank, &
        & ".nc"
      !
      Mode = "read"
      call ni3_open( InputFileName, Mode, InNcID )
      a_InNcID(iRank) = InNcID
    end do

    do iRank = 0, NProcs-1
      InNcID = a_InNcID(iRank)
      call ni3_inq_dimlen( InNcID, "Press", NLevEach )
      a_NLevEach(iRank) = NLevEach
    end do

    a_kStartIndex(0) = 1
    do iRank = 0+1, NProcs-1
      a_kStartIndex(iRank) = a_kStartIndex(iRank-1) + a_NLevEach(iRank-1)
    end do

    iRank = NProcs
    NLev = a_kStartIndex(iRank-1) + a_NLevEach(iRank-1) - 1

    iRank  = 0
    InNcID = a_InNcID  (iRank)
    !
    call ni3_inq_dimlen( InNcID, "MolNum" , NMolNum  )
    call ni3_inq_dimlen( InNcID, "WaveNum", NWaveNum )
    !
    allocate( m_MolNum ( NMolNum  ) )
    allocate( w_WaveNum( NWaveNum ) )
    !
    call ni3_get_var( InNcID, "MolNum" , m_MolNum  )
    call ni3_get_var( InNcID, "WaveNum", w_WaveNum )


    allocate( r_Press(NLev) )
    allocate( r_Temp ( NLev     ) )
    allocate( rm_VMR ( NLev, NMolNum ) )

    do iRank = 0, NProcs-1
      InNcID   = a_InNcID  (iRank)
      NLevEach = a_NLevEach(iRank)

      allocate( r_PressEach(NLevEach) )
      allocate( r_TempEach (NLevEach) )
      allocate( rm_VMREach (NLevEach, NMolNum) )
      call ni3_get_var( InNcID, "Press", r_PressEach )
      call ni3_get_var( InNcID, "Temp" , r_TempEach  )
      call ni3_get_var( InNcID, "VMR"  , rm_VMREach  )
      do k = 1, NLevEach
        r_Press(a_kStartIndex(iRank)+k-1) = r_PressEach(k)
        r_Temp (a_kStartIndex(iRank)+k-1) = r_TempEach (k)
      end do
      do m = 1, NMolNum
        do k = 1, NLevEach
          rm_VMR(a_kStartIndex(iRank)+k-1,m) = rm_VMREach(k,m)
        end do
      end do
      deallocate( r_PressEach )
      deallocate( r_TempEach  )
      deallocate( rm_VMREach  )
    end do


    Mode = "new"
    call ni3_open( OutputFileName, Mode, OutNcID )
    !
    Name = "_GLOBAL_"
    call ni3_cp_atts( InNcID, OutNcID, Name )
    !
    Name = "MolNum"
    call ni3_set_dim( OutNcID, Name, NI3_INT, m_MolNum )
    call ni3_cp_atts( InNcID, OutNcID, Name )
    !
    Name = "Press"
    call ni3_set_dim( OutNcID, Name, NI3_DOUBLE, r_Press )
    call ni3_cp_atts( InNcID, OutNcID, Name )
    !
    Name = "WaveNum"
    call ni3_def_dim( OutNcID, Name, NI3_DOUBLE, NI3_UNLIMITED )
    call ni3_cp_atts( InNcID, OutNcID, Name )
    call ni3_put_var( OutNcID, Name, w_WaveNum )

    NDims = 1
    allocate( a_DimNames( NDims ) )
    a_DimNames(1) = 'Press'
    !
    Name = "Temp"
    call ni3_def_var( OutNcID, Name, NI3_DOUBLE, NDims, a_DimNames )
    call ni3_cp_atts( InNcID, OutNcID, Name )
    call ni3_put_var( OutNcID, Name, r_Temp )
    !
    deallocate( a_DimNames )

    NDims = 2
    allocate( a_DimNames( NDims ) )
    a_DimNames(1) = 'Press'
    a_DimNames(2) = 'MolNum'
    !
    Name = "VMR"
    call ni3_def_var( OutNcID, Name, NI3_DOUBLE, NDims, a_DimNames )
    call ni3_cp_atts( InNcID, OutNcID, Name )
    call ni3_put_var( OutNcID, Name, rm_VMR )
    !
    deallocate( a_DimNames )

    NDims = 3
    allocate( a_DimNames( NDims ) )
    a_DimNames(1) = 'Press'
    a_DimNames(2) = 'MolNum'
    a_DimNames(3) = 'WaveNum'
    !
    Name = "AbsCoef"
    call ni3_def_var( OutNcID, Name, NI3_DOUBLE, NDims, a_DimNames )
    call ni3_cp_atts( InNcID, OutNcID, Name )
    !
    deallocate( a_DimNames )

    deallocate( m_MolNum   )
    deallocate( w_WaveNum  )
    deallocate( r_Press    )
    deallocate( r_Temp     )
    deallocate( rm_VMR     )


    allocate( rm_AbsCoef( NLev, NMolNum ) )

    do l = 1, NWaveNum

      do iRank = 0, NProcs-1

        InNcID   = a_InNcID  (iRank)
        NLevEach = a_NLevEach(iRank)

        allocate( rm_AbsCoefEach(NLevEach, NMolNum) )
        !
        Name = "AbsCoef"
        call ni3_get_varss( InNcID, Name, l, rm_AbsCoefEach )
        !
        do m = 1, NMolNum
          do k = 1, NLevEach
            rm_AbsCoef(a_kStartIndex(iRank)+k-1,m) = rm_AbsCoefEach(k,m)
          end do
        end do
        !
        deallocate( rm_AbsCoefEach )

        call ni3_put_varss( OutNcID, Name, l, rm_AbsCoef )

      end do

    end do

    deallocate( rm_AbsCoef )

    call ni3_close( OutNcID )


    do iRank = 0, NProcs-1
      InNcID = a_InNcID(iRank)
      call ni3_close( InNcID )
    end do

    deallocate( a_kStartIndex )
    deallocate( a_NLevEach )
    deallocate( a_InNcID   )


  end subroutine ACMergeVerticalDomain

  !----------------------------------------------------------------------------

  subroutine ACMergeVerticalDomain2( &
    & NProcs, InputFileName0, OutputFileName &
    & )

    use ni3_module

    integer         , intent(in) :: NProcs
    character(len=*), intent(in) :: InputFileName0
    character(len=*), intent(in) :: OutputFileName


    !
    ! Local variables
    !
    integer                        :: InNcID
    integer                        :: OutNcID
    character(stdstr)              :: Mode
    integer                        :: NDims
    character(stdstr), allocatable :: a_DimNames( : )
    character(stdstr)              :: Name

    integer                   :: NLev
    integer                   :: NMolNum

    character(len=extstr)     :: InputFileName

    integer     , allocatable :: m_MolNum(:)
    real(dp)    , allocatable :: w_WaveNum(:)
    real(dp)    , allocatable :: r_Press(:)
    real(dp)    , allocatable :: r_Temp(:)
    real(dp)    , allocatable :: rm_VMR(:,:)

    real(dp)    , allocatable :: r_PressEach(:)
    real(dp)    , allocatable :: r_TempEach (:)
    real(dp)    , allocatable :: rm_VMREach (:,:)

    real(dp)    , allocatable :: rmw_AbsCoef(:,:,:)
    integer                   :: St(3)
    integer                   :: Co(3)

    integer     , allocatable :: a_InNcID(:)
    integer     , allocatable :: a_NLevEach(:)
    integer     , allocatable :: a_kStartIndex(:)

    integer :: iRank
    integer :: NWaveNum

    integer :: NLevEach

    integer :: k
    integer :: m


    allocate( a_InNcID     (0:NProcs-1) )
    allocate( a_NLevEach   (0:NProcs-1) )
    allocate( a_kStartIndex(0:NProcs-1) )


    do iRank = 0, NProcs-1
      write( InputFileName, '(a,a,i6.6,a)' ) &
        & InputFileName0(1:len_trim(InputFileName0)-3), &
        & "_", &
        & iRank, &
        & ".nc"
      !
      Mode = "read"
      call ni3_open( InputFileName, Mode, InNcID )
      a_InNcID(iRank) = InNcID
      !
      call ni3_inq_dimlen( InNcID, "Press", NLevEach )
      a_NLevEach(iRank) = NLevEach
    end do

    a_kStartIndex(0) = 1
    do iRank = 0+1, NProcs-1
      a_kStartIndex(iRank) = a_kStartIndex(iRank-1) + a_NLevEach(iRank-1)
    end do

    iRank = NProcs
    NLev = a_kStartIndex(iRank-1) + a_NLevEach(iRank-1) - 1

    iRank  = 0
    InNcID = a_InNcID  (iRank)
    !
    call ni3_inq_dimlen( InNcID, "MolNum" , NMolNum  )
    call ni3_inq_dimlen( InNcID, "WaveNum", NWaveNum )
    !
    allocate( m_MolNum ( NMolNum  ) )
    allocate( w_WaveNum( NWaveNum ) )
    !
    call ni3_get_var( InNcID, "MolNum" , m_MolNum  )
    call ni3_get_var( InNcID, "WaveNum", w_WaveNum )


    allocate( r_Press(NLev) )
    allocate( r_Temp ( NLev     ) )
    allocate( rm_VMR ( NLev, NMolNum ) )

    do iRank = 0, NProcs-1
      InNcID   = a_InNcID  (iRank)
      NLevEach = a_NLevEach(iRank)

      allocate( r_PressEach(NLevEach) )
      allocate( r_TempEach (NLevEach) )
      allocate( rm_VMREach (NLevEach, NMolNum) )
      call ni3_get_var( InNcID, "Press", r_PressEach )
      call ni3_get_var( InNcID, "Temp" , r_TempEach  )
      call ni3_get_var( InNcID, "VMR"  , rm_VMREach  )
      do k = 1, NLevEach
        r_Press(a_kStartIndex(iRank)+k-1) = r_PressEach(k)
        r_Temp (a_kStartIndex(iRank)+k-1) = r_TempEach (k)
      end do
      do m = 1, NMolNum
        do k = 1, NLevEach
          rm_VMR(a_kStartIndex(iRank)+k-1,m) = rm_VMREach(k,m)
        end do
      end do
      deallocate( r_PressEach )
      deallocate( r_TempEach  )
      deallocate( rm_VMREach  )
    end do


    Mode = "new"
    call ni3_open( OutputFileName, Mode, OutNcID )
    !
    Name = "_GLOBAL_"
    call ni3_cp_atts( InNcID, OutNcID, Name )
    !
    Name = "MolNum"
    call ni3_set_dim( OutNcID, Name, NI3_INT, m_MolNum )
    call ni3_cp_atts( InNcID, OutNcID, Name )
    !
    Name = "Press"
    call ni3_set_dim( OutNcID, Name, NI3_DOUBLE, r_Press )
    call ni3_cp_atts( InNcID, OutNcID, Name )
    !
    Name = "WaveNum"
    call ni3_def_dim( OutNcID, Name, NI3_DOUBLE, NI3_UNLIMITED )
    call ni3_cp_atts( InNcID, OutNcID, Name )
    call ni3_put_var( OutNcID, Name, w_WaveNum )

    NDims = 1
    allocate( a_DimNames( NDims ) )
    a_DimNames(1) = 'Press'
    !
    Name = "Temp"
    call ni3_def_var( OutNcID, Name, NI3_DOUBLE, NDims, a_DimNames )
    call ni3_cp_atts( InNcID, OutNcID, Name )
    call ni3_put_var( OutNcID, Name, r_Temp )
    !
    deallocate( a_DimNames )

    NDims = 2
    allocate( a_DimNames( NDims ) )
    a_DimNames(1) = 'Press'
    a_DimNames(2) = 'MolNum'
    !
    Name = "VMR"
    call ni3_def_var( OutNcID, Name, NI3_DOUBLE, NDims, a_DimNames )
    call ni3_cp_atts( InNcID, OutNcID, Name )
    call ni3_put_var( OutNcID, Name, rm_VMR )
    !
    deallocate( a_DimNames )

    NDims = 3
    allocate( a_DimNames( NDims ) )
    a_DimNames(1) = 'Press'
    a_DimNames(2) = 'MolNum'
    a_DimNames(3) = 'WaveNum'
    !
    Name = "AbsCoef"
    call ni3_def_var( OutNcID, Name, NI3_DOUBLE, NDims, a_DimNames )
    call ni3_cp_atts( InNcID, OutNcID, Name )
    !
    deallocate( a_DimNames )

    deallocate( m_MolNum   )
    deallocate( w_WaveNum  )
    deallocate( r_Press    )
    deallocate( r_Temp     )
    deallocate( rm_VMR     )


    Name = "AbsCoef"
    Co(1) = 1
    Co(2) = 1
    Co(3) = NWaveNum
    allocate( rmw_AbsCoef(Co(1),Co(2),Co(3)) )
    do iRank = 0, NProcs-1
      InNcID   = a_InNcID  (iRank)
      NLevEach = a_NLevEach(iRank)
      do m = 1, NMolNum
        do k = 1, NLevEach
          St(1) = k
          St(2) = m
          St(3) = 1
          call ni3_get_varpart( InNcID , Name, St, Co, rmw_AbsCoef )
          St(1) = a_kStartIndex(iRank)+k-1
          St(2) = m
          St(3) = 1
          call ni3_put_varpart( OutNcID, Name, St, Co, rmw_AbsCoef )
        end do
      end do
    end do
    !
    deallocate( rmw_AbsCoef )

    call ni3_close( OutNcID )


    do iRank = 0, NProcs-1
      InNcID = a_InNcID(iRank)
      call ni3_close( InNcID )
    end do

    deallocate( a_kStartIndex )
    deallocate( a_NLevEach )
    deallocate( a_InNcID   )


  end subroutine ACMergeVerticalDomain2

  !----------------------------------------------------------------------------

end module ac_merge
