!> \~japanese
!! \brief 3 次元に対応した正二十面体格子における格子点およびそのコントロールボリュームの頂点の座標データを管理する構造型と
!!それらのデータを操作するための手続きを提供するモジュール.
!!
!! \par IcGrid3D_FVM_Manager モジュールについて
!!   - 水平正二十面格子データおよび鉛直格子データを保持し, 正二十面格子モデルにおける 3 次元格子を管理する. 
!!   - 鉛直格子は不等間隔に配置することができる. 
!!  - util/grid_ex パッケージ内のモジュールと組み合わせることで, 境界適合格子を利用できる.　
!!    - TFCoord_IcGrid3D_helper モジュールと組み合わせることにより, 地形に沿う座標系を取り扱うことができる.     
!!
!! \~english
!! \brief This module provides a derived type, which manages the coordinate data for grid points and their control volume in 3D icosahedral gird,
!!and some subroutines to manupulate their data.
!!
!! \par About IcGrid3D_FVM_Manager module
!!   - This module manages data of 3D grid on icosahedral grid model, 
!!     which is constructed by horizontal icosahedral grid and vertical grid.  
!!   - 鉛直格子は不等間隔に配置することができる. 
!!  - util/grid_ex パッケージ内のモジュールと組み合わせることで, 境界適合格子を利用できる.　
!!    - TFCoord_IcGrid3D_helper モジュールと組み合わせることにより, 地形に沿う座標系を取り扱うことができる.     
!!
!! \~
!! \since IGMBaseLib 2.0
!!
!! <br><br>
!! \b Copyright (C) GFD Dennou Club, 2011-2012. All rights reserved. <br>
!! \b License ?? <br>
!! \author Yuta Kawai
!!
module IcGrid3D_FVM_Manager

  ! モジュール引用 ; Use statements
  !

  ! 種類型パラメータ
  ! Kind type parameter
  !
  use dc_types, only: &
    & DP, TOKEN, STRING

  ! 正二十面格子データの管理
  ! Manager of icosahedral grid
  !
  use IcGrid2D_FVM_Manager, only: &
    & IcGrid2D_FVM, &
    & IcGrid2D_FVM_Init, IcGrid2D_FVM_Final

  ! 宣言文 ; Declaration statements
  !
  implicit none
  private

  !
  !> \~japanese
  !! \breif 3 次元に拡張した正二十面体格子における格子点およびそのコントロールボリュームの頂点の座標データを管理する構造型. 
  !!
  !! IcGrid3D_FVM_Manager モジュールにおいて, 水平正二十面格子の情報の管理は,  
  !! 構造型 IcGrid3D_FVM の成分 icgrid2D と関連付られる構造型 IcGrid2D_FVM の変数によって行われる.  
  !!
  !! \~english
  !! \breif 3 次元に拡張した正二十面体格子における格子点およびそのコントロールボリュームの頂点の座標データを管理する構造型. 
  !!
  !! IcGrid3D_FVM_Manager モジュールにおいて, 水平正二十面格子の情報の管理は,  
  !! 構造型 IcGrid3D_FVM の成分 icgrid2D と関連付られる構造型 IcGrid2D_FVM の変数によって行われる.  
  !!
  type, public :: IcGrid3D_FVM

    !> \~japanese 正二十面格子の水平方向の格子情報を管理するための構造型 IcGrid2D_FVM の変数. 
    !! \~english A variable of derived type IcGrid2D_FVM which manages the information about horizontal grid in icosahedral grid.  
    type(IcGrid2D_FVM), pointer :: icgrid2D

    !> \~japanese 鉛直格子の座標データを保持するための配列. 
    !! \~english An array containig vertex data of vertical grid. 
    real(DP), pointer :: verticalGrid(:)

    !> \~japanese 鉛直格子点数. 
    !! \~english  The number of vertical grid.   
    integer :: verticalLv_num

    !> \~japanese 鉛直格子の名前. 
    !! \~english The name of vertical grid. 
    character(TOKEN) :: vertical_coord_name

    !> \~japanese 鉛直格子の(長い)名前. 
    !! \~english  The long name of vertical grid.   
    character(STRING) :: vertical_coord_long_name

    !> \~japanese 鉛直格子の単位. 
    !! \~english The unit of vertical grid.    
    character(TOKEN) :: vertical_coord_units

  end type IcGrid3D_FVM

  ! 公開手続き
  ! Public procedures
  !
  public :: IcGrid3D_FVM_Init, IcGrid3D_FVM_Final
  
  public :: redistribute_verticalGrid
  
  public :: get_vertical_level_num, get_icgrid2D, &
    &       get_IcGrid3D_VerticalCoord_name, get_IcGrid3D_VerticalCoord_long_name, &
    &       get_IcGrid3D_VerticalCoord_units
  
  public :: set_IcGrid3D_VerticalCoord_name, set_IcGrid3D_VerticalCoord_long_name, &
    &       set_IcGrid3D_VerticalCoord_units


  ! interface 文 ; interface statements
  !

  interface IcGrid3D_FVM_Init
    module procedure initialize_IcGrid3D_FVM1
    module procedure initialize_IcGrid3D_FVM2
  end interface IcGrid3D_FVM_Init


contains

!
!> \~japanese
!! \brief 構造型 IcGrid3D_FVM の変数を初期化する.
!!
!! 構造型 IcGrid3D_FVM の成分の一つである, 構造型 IcGrid2D_FVM の変数 icgrid2D の初期化を, IcGrid2D_FVM_Init によって行う.
!! 実際に, 正二十面格子を生成したい(正二十面格子やコントロールボリュームの座標データなどを計算する)際は,
!! IcGrid2D_FVM_Builder モジュールを用いる必要がある.
!! なお, この icgrid2D の参照を取得したい場合は, 本モジュールの手続き get_IcGrid2D によって を取得できる.
!!
!! \param[in,out] self 構造型 IcGrid3D_FVM の変数.
!! \param[in] glevel   水平分割レベル.
!! \param[in] ic_radius 正二十面体を内包する球の半径. 
!! \param[in] vertical_level_num  鉛直方向の層数. 
!!
!! \~english
!! \brief Initializes the variable of derived type IcGrid3D_FVM.
!!
!! 構造型 IcGrid3D_FVM の成分の一つである, 構造型 IcGrid2D_FVM の変数 icgrid2D の初期化を, IcGrid2D_FVM_Init によって行う.
!! 実際に, 正二十面格子を生成したい(正二十面格子やコントロールボリュームの座標データなどを計算する)際は,
!! IcGrid2D_FVM_Builder モジュールを用いる必要がある.
!! なお, この icgrid2D の参照を取得したい場合は, 本モジュールの手続き get_IcGrid2D によって を取得できる.
!!
!! \param[in,out] self The vvariable of derived type IcGrid3D_FVM.
!! \param[in] glevel Horizontal division level.
!! \param[in] ic_radius The radius of a sphere which an icosahedron is embedded into.
!! \param[in] vertical_level_num  The number of vertical layers. 
!!
!!
subroutine initialize_IcGrid3D_FVM1( &
  & self,                                      &  ! (inout)
  & glevel, ic_radius, vertical_level_num      &  ! (in)
  )

  ! 宣言文 ; Declaration statements
  !
  type(IcGrid3D_FVM), intent(inout) :: self
  integer, intent(in) :: glevel
  real(DP), intent(in) :: ic_radius
  integer, intent(in) :: vertical_level_num
  
  ! 作業変数
  ! Work variables
  !
  type(IcGrid2D_FVM), pointer :: icgrid2D

  ! 実行文 ; Executable statements
  !

  ! (正二十面格子の)水平格子情報の管理するための構造型 IcGrid2D_FVM の変数を初期化する. 
  ! そしてそれを使って, 構造型 IcGrid3D_FVM の変数を初期化する. 
  
  allocate(icgrid2D)
  
  call IcGrid2D_FVM_Init(icgrid2D, glevel, ic_radius)
  call initialize_IcGrid3D_FVM2(self, icgrid2D, vertical_level_num)

end subroutine initialize_IcGrid3D_FVM1

!
!> \~japanese
!! \brief  既存の構造型 IcGrid2D_FVM を使って, 構造型 IcGrid3D_FVM の変数を初期化する.
!!
!! \param[in,out] self            構造型 IcGrid3D_FVM の変数.
!! \param[in] icgrid2D            構造型 IcGrid2D_FVM の変数. 
!! \param[in] vertical_level_num  鉛直方向の層数. 
!!
!! \~english
!! \brief  既存の構造型 IcGrid2D_FVM を使って, 構造型 IcGrid3D_FVM の変数を初期化する.
!!
!! \param[in,out] self            The variable of derived type IcGrid3D_FVM. 
!! \param[in] icgrid2D            The variable of derived type IcGrid2D_FVM.
!! \param[in] vertical_level_num  The number of vertical layers. 
!!
subroutine initialize_IcGrid3D_FVM2( &
  & self,                          &  ! (inout)
  & icgrid2D, vertical_level_num   &  ! (in)
  )

  ! 宣言文 ; Declaration statements
  !
  type(IcGrid3D_FVM), intent(inout) :: self
  type(IcGrid2D_FVM), target :: icgrid2D
  integer, intent(in) :: vertical_level_num

  ! 作業変数
  ! Work variables
  !
  integer :: vtLv
  real(DP) :: DelZ

  ! 実行文 ; Executable statements
  !

  ! 引数で指定された格子情報を, 構造体 'self' の要素に設定する. 
  ! Set the grid information specified by arguments into the elements of derived type 'self'. 
  !
  
  self%icgrid2D => icgrid2D
  self%verticalLv_num = vertical_level_num

  ! 鉛直方向の格子座標データを保持する配列のメモリを確保する. 
  ! Allocate the memory into stored the data of vertical grid vertex.  
  allocate(self%verticalGrid(vertical_level_num))

  ! デフォルトとして, 鉛直格子を一様に配置する. 
  ! Set uniform grid as defalut distribution of vertical grid. 
  !
   
  DelZ = 1.0d0 / ( vertical_level_num - 1 )
  do vtLv=1, vertical_level_num
    self%verticalGrid(vtLv) = DelZ * ( vtLv - 1.0d0 )
  end do

  ! 鉛直格子のメタ情報を指定する. 
  ! Specify meta-information about vertical grid.  
  !

  call set_IcGrid3D_VerticalCoord_name(self, 'z')
  call set_IcGrid3D_VerticalCoord_long_name(self, 'altitude')
  call set_IcGrid3D_VerticalCoord_units(self, 'm')

end subroutine initialize_IcGrid3D_FVM2

!
!> \~japanese
!! \brief 構造型 IcGrid3D_FVM の変数を最終化する.
!!
!!
!! \param[in,out] self 構造型 IcGrid3D_FVM の変数.
!!
!! \~english
!! \brief Finalizes the variable of derived type IcGrid3D_FVM.
!!
!!
!! \param[in,out] self The variable of derived type IcGrid3D_FVM.
!!
subroutine IcGrid3D_FVM_Final( &
  & self & ! (inout)
  & )

  ! 宣言文 ; Declaration statements
  !
  type(IcGrid3D_FVM), intent(inout) :: self

  ! 実行文 ; Executable statements
  !

  ! 動的に確保した配列のメモリを開放する.
  deallocate(self%verticalGrid)

end subroutine IcGrid3D_FVM_Final

!> \~japanese
!! \brief 構造型 IcGrid3D_FVM の変数と関係づけられている構造型 IcGrid2D_FVM の変数を取得する. 
!!
!! \param[in] self 構造型 IcGrid3D_FVM の変数.
!! \return         self と関係づけられている2 次元正二十面格子データの管理オブジェクト.
!!
!! \~english
!! \brief Get the variable of derived type IcGrid2D_FVM which associates with a variable of derived type IcGrid3D_FVM. 
!!
!! \param[in] self 構造型 IcGrid3D_FVM の変数.
!! \return         self と関係づけられている2 次元正二十面格子データの管理オブジェクト.
!!
function get_icgrid2D( & 
	& self               &  ! (in) 
	& ) result(icgrid2D)

  ! 宣言文 ; Declaration statements
  !
  type(IcGrid3D_FVM), intent(in) :: self
  type(IcGrid2D_FVM), pointer :: icgrid2D

  ! 実行文 ; Executable statements
  !

  icgrid2D => self%icgrid2D

end function get_icgrid2D

!
!> \~japanese
!! \brief 鉛直層数を取得する. 
!!
!! この鉛直層数とは, 鉛直格子点数のことである.
!!
!! \param[in] self 構造型 IcGrid3D_FVM の変数.
!! \return         鉛直層数. 
!!
!! \~english
!! \brief Get the number of vertical layers. 
!!
!! The number of vertical layers means the number of vertical grid points.
!!
!! \param[in] self The variable of derived type IcGrid3D_FVM.
!! \return         The number of vertical layers. 
!!
function get_vertical_level_num( &
  & self                          & ! (in)
  & ) result(vtLv_num)

  ! 宣言文 ; Declaration statements
  !
  type(IcGrid3D_FVM), intent(in) :: self
  integer vtLv_num

  ! 実行文 ; Executable statements
  !
  vtLv_num = self%verticalLv_num

end function get_vertical_level_num

!
!> \~japanese
!! \brief 鉛直格子点を再配置する.
!!
!! 引数によって, 鉛直座標の定義域の上端下端を指定する.
!! また, 定義域内部で鉛直格子点をどのような間隔で配置するかを割合で指定する. 
!!
!! 例えば, 格子点数が 6 である鉛直格子に対して, VMin=0, VMax=10000, interval_ratio=[2.0,4.0,3.0,1.0] とした場合には, 
!! 鉛直座標格子の高度値は, 配列インデックスの小さい方から順に, [ 0, 2000, 6000, 9000, 1000 ] となる. 
!!
!! \param[inout] self        構造型 IcGrid3D_FVM の変数.
!! \param[in] VMin           数値モデルの下端境界の高度. 
!! \param[in] VMax           数値モデルの上端境界の高度.
!! \param[in] interval_ratio 鉛直格子点間の距離比を保持している一次元配列. 
!!
!! \~english
!! \brief Redistribute the points of vertical grid.   
!!
!! 引数によって, 鉛直座標の定義域の上端下端を指定する.
!! また, 定義域内部で鉛直格子点をどのような間隔で配置するかを割合で指定する. 
!!
!! 例えば, 格子点数が 6 である鉛直格子に対して, VMin=0, VMax=10000, interval_ration=[2.0,4.0,3.0,1.0] とした場合には, 
!! 鉛直座標格子の高度値は, 配列インデックスの小さい方から順に, [ 0, 2000, 6000, 9000, 1000 ] となる. 
!!
!! \param[inout] self The variable of derived type IcGrid3D_FVM.
!! \param[in] VMin    The height of the bottom boundary of a numerical model. 
!! \param[in] VMax    The height of the top boundary of a numerical model.
!! \param[in] interval_ratio A one-dimensional array to store data of the distance ratio between vertical grid points.   
!!
subroutine redistribute_verticalGrid( &
  & self,                      &  ! (inout)
  & VMin, VMax, interval_ratio &  ! (in)
  & )

  ! 宣言文 ; Declaration statements
  !
  type(IcGrid3D_FVM), intent(inout) :: self
  real(DP), intent(in) :: VMin
  real(DP), intent(in) :: VMax
  real(DP), intent(in) :: interval_ratio(:)

  ! 作業変数
  ! Work variables
  !
  real(DP), allocatable :: DelZ(:)
  integer :: k
  integer :: vtLv_num

  ! 実行文 ; Executable statements
  !

  vtLv_num = get_vertical_level_num(self)

	! 格子点間の距離比データを保管している配列の要素数が, 
	! 鉛直分割数と整合的か調べる. 
  if ( size(interval_ratio) /= vtLv_num - 1 ) then
    ! Error
    write(*,*) 'The number of interval layers(=', size(interval_ratio), &
      & ') is not consistent with the number of interval layer(=', &
      & get_vertical_level_num(self), ').'
    write(*,*) 'The expected number of interval layers is',  get_vertical_level_num(self)-1

    stop
  end if

  ! 与えられた距離比データから各格子間の距離(DelZ)を求める. 
  allocate(DelZ(size(interval_ratio)))
  DelZ(:) = ( VMax - VMin ) * interval_ratio(:) / sum(interval_ratio(:))

  ! DelZ を使って, 鉛直格子点の位置を再配置する. 
  self%verticalGrid(1) = VMin
  do k=2, vtLv_num
    self%verticalGrid(k) = self%verticalGrid(k-1) + DelZ(k-1)
  end do

end subroutine redistribute_verticalGrid


!
!
!
subroutine set_IcGrid3D_VerticalCoord_name( &
  & self, & ! (inout)
  & name  & ! (in)
  & )

  ! 宣言文 ; Declaration statements
  !
  type(IcGrid3D_FVM), intent(inout) :: self
  character(*), intent(in) :: name

  ! 実行文 ; Executable statements
  !

  self%vertical_coord_name = name

end subroutine set_IcGrid3D_VerticalCoord_name

subroutine set_IcGrid3D_VerticalCoord_long_name( &
  & self,     & ! (inout)
  & long_name & ! (in)
  & )

  ! 宣言文 ; Declaration statements
  !
  type(IcGrid3D_FVM), intent(inout) :: self
  character(*), intent(in) :: long_name

  ! 実行文 ; Executable statements
  !

  self%vertical_coord_long_name = long_name

end subroutine set_IcGrid3D_VerticalCoord_long_name

subroutine set_IcGrid3D_VerticalCoord_units( &
  & self, &  ! (inout)
  & units &  ! (in)
  & )

  ! 宣言文 ; Declaration statements
  !
  type(IcGrid3D_FVM), intent(inout) :: self
  character(*), intent(in) :: units

  ! 実行文 ; Executable statements
  !

  self%vertical_coord_units = units

end subroutine set_IcGrid3D_VerticalCoord_units


function get_IcGrid3D_VerticalCoord_name( &
  & self & ! (in)
  & ) result(name)

  ! 宣言文 ; Declaration statements
  !
  type(IcGrid3D_FVM), intent(in) :: self
  character(TOKEN) name

  ! 実行文 ; Executable statements
  !

  name = self%vertical_coord_name

end function get_IcGrid3D_VerticalCoord_name

function get_IcGrid3D_VerticalCoord_long_name( &
  & self & ! (in)
  & ) result(long_name)

  ! 宣言文 ; Declaration statements
  !
  type(IcGrid3D_FVM), intent(in) :: self
  character(STRING) long_name

  ! 実行文 ; Executable statements
  !

  long_name = self%vertical_coord_long_name

end function get_IcGrid3D_VerticalCoord_long_name

function get_IcGrid3D_VerticalCoord_units( &
  & self &  ! (in)
  & ) result(units)

  ! 宣言文 ; Declaration statements
  !
  type(IcGrid3D_FVM), intent(in) :: self
  character(TOKEN) units

  ! 実行文 ; Executable statements
  !

  units = self%vertical_coord_units

end function get_IcGrid3D_VerticalCoord_units


end module IcGrid3D_FVM_Manager
