!> \~japanese
!! \brief 正二十面体格子上の 3 次元に分布する物理場データを管理する構造型およびそれを操作するための手続きを提供するモジュール.
!!
!! \par Field_IcGrid3D_Manager モジュールについて
!!
!!
!! \~english
!! \brief This module provides the derived type which manages the data of a 3D physical field on a icosahedral grid and
!! some subroutines to manupulate the data.
!!
!! \par About Field_IcGrid3D_Manager module
!!
!! \~
!!
!! <br><br>
!! \b Copyright (C) GFD Dennou Club, 2011-2012. All rights reserved. <br>
!! \b license ?? <br>
!! \author Yuta Kawai
!!
module Field_IcGrid3D_Manager

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

  !* gtool ****************
  !*
  
  ! 種類型パラメータ
  ! Kind type parameter
  !
  use dc_types, only: DP,    &  ! 倍精度実数型. Double precision.
    &                 TOKEN, &
    &                 STRING

  ! 文字列操作
  ! String manupulation
  !
  use dc_string, only: &
    & CPrintf

  ! メッセージ出力
  ! Messgae dump
  !
  use dc_message, only: MessageNotify

  !* IGMBaseLib ****************
  !*

  ! 正二十面格子の管理
  ! Manager of icosahedral grid
  !
  use IcGrid2D_FVM_Manager, only: &
    & IcGrid2D_FVM, &
    & get_glevel, get_EffSize_Min, get_EffSize_Max, &
    & get_LocalProcRectInfo, &
    & RC_REGIONS_NUM

  ! 正二十面格子における広域矩形領域のサブドメインである局所矩形領域(各 MPI プロセスと対応)の情報管理
  ! Manager of information about a local rectangle region (corresponding to each MPI process)
  !
  use LocalProcRect_Manager, only: &
    & LocalProcRectInfo, &
    & get_InteriorRegion_IndexInfo

  ! 3 次元正二十面格子の管理
  ! Manager of 3D icosahedral grid
  !
  use IcGrid3D_FVM_Manager, only: &
    & IcGrid3D_FVM, &
    & IcGrid3D_FVM_Init, IcGrid3D_FVM_Final, &
    & get_vertical_level_num, get_icgrid2D

  ! 物理場データを管理する構造体のための基底モジュール
  ! Base module for some derived types to manage the physical field data
  !
  use Physical_Field_Manager, only: &
    & Physical_Field, &
    & Physical_Field_Init, &
    & get_name_ => get_field_name, &
    & get_long_name_ => get_long_field_name, &
    & get_dimension_ => get_field_rank, &
    & get_units_ => get_field_units, &
    & set_name_ => set_field_name, &
    & set_long_name_ => set_long_field_name, &
    & set_units_ => set_field_units

  ! 正二十面体格子上の水平 2 次元の物理場データの管理
  ! Manager of the physical field data on icosahedral grid
  !
  use Field_IcGrid2D_Manager, only: &
    & Field_IcGrid2D, &
    & Field_IcGrid2D_Init, Field_IcGrid2D_Final, &
    & paste_field2D_margin

  ! 宣言文 ; Declaration statements
  !
  implicit none
  private

  !> \~japanese
  !! \brief 3 次元の正二十面格子上の物理場データを管理する構造型.
  !!
  !! \~english
  !! \brief A derived type which manages data of physical field on 3D icosahedral grid. 
  !!
  type, public :: Field_IcGrid3D

    !> \~japanese 各レイヤーの 2 次元正二十面格子上の物理場データを保持する構造型 Field_IcGrid2D の配列.
    !! \~english
    type(Field_IcGrid2D), pointer :: field2Ds(:)

    !> \~japanese 3 次元正二十面格子データを管理する構造型 IcGrid3D_FVM の変数. 
    !! \~english A variable of derived type IcGrid3D_FVM which manages the 3D icosahedral grid data. 
    type(IcGrid3D_FVM), pointer :: icgrid3D

    !> \~japanese 構造型 Field_IcGrid3D の基底となる構造体 Physical_Field の変数.
    !! \~english A variable of derived type Physcal_Field which is a base type of Field_IcGrid3D.
    type(Physical_Field) ::  base

  end type Field_IcGrid3D

  ! 公開手続き
  ! Public procedures
  !
  public :: Field_IcGrid3D_Init, Field_IcGrid3D_Final
  
  public :: paste_field3D_margin, fill_value, copy_field3D

  public :: get_field3D_name, get_long_field3D_name, get_field3D_rank, get_field3D_units, &
    &       get_field_layer, get_icgrid3D, get_Physical_Field
  
  public :: set_field3D_name, set_long_field3D_name, set_field3D_units

  ! interface 文 ; interface statements
  !

  interface Field_IcGrid3D_Init
    module procedure initialize_Field_IcGrid3D1
    module procedure initialize_Field_IcGrid3D2
  end interface Field_IcGrid3D_Init

  ! 非公開変数
  ! Private variables
  !

  !> \~japanese
  !! \~english
  character(TOKEN) :: vertLvName_concate_sign = '-Lv'

contains

!
!> \~japanese
!! \brief 構造型 Field_IcGrid3D の変数の初期化を行う.
!!
!! @param[in,out] self       構造体 Field_IcGrid3D の変数.
!! @param[in]     icgrid3D   構造体 IcGrid3D_FVM の変数.
!! @param[in]     field_rank 物理場のランク.
!! @param[in]     long_name  物理場の長い名前(あるいは物理場の解説文).
!! @param[in]     units      物理場の単位.
!!
!! \~english
!! \brief Initialize a variable of derived type Field_IcGrid2D.
!!
!! @param[in,out] self       The variable of derived type Field_IcGrid3D.
!! @param[in]     icgrid3D   The variable of derived type IcGrid3D_FVM.
!! @param[in]     name       The (short) name of the physical field.
!! @param[in]     field_rank  The rank of the physical filed.
!! @param[in]     long_name  The long name of (or the explanation for) the physical field.
!! @param[in]     units      The unit of the physical field.
!!
subroutine initialize_Field_IcGrid3D1( &
 & self,                                  &  ! (inout) 
 & icgrid3D, name, rank, long_name, units &  ! (in)
 & )

  ! 宣言文 ; Declaration statement
  !
  type(Field_IcGrid3D), intent(inout) :: self
  type(IcGrid3D_FVM), intent(in), target :: icgrid3D
  character(*), intent(in) :: name
  integer, intent(in) :: rank
  character(*), intent(in) :: long_name
  character(*), intent(in) :: units

  ! 作業変数
  ! Work variable
  !
  integer vtLv
  integer verticalLv_num
  type(IcGrid2D_FVM), pointer :: icgrid2D
  character(TOKEN) :: fieldLv_name

  ! 実行文 ; Executable statements
  !

  ! 基底構造体の初期化関数を呼び出す.
  call Physical_Field_Init(self%base, name, rank, long_name, units)

  ! 正二十面格子の情報を取得する. 
  verticalLv_num = get_vertical_level_num(icgrid3D)
  icgrid2D => get_icgrid2D(icgrid3D)

  ! 各レイヤーの物理場データを保持する配列を初期化する.
  ! 物理場名は, 'PhysNameLv1' の規則で設定する.

  allocate(self%field2Ds(verticalLv_num))

  do vtLv=1, verticalLv_num
    fieldLv_name = trim(name) // CPrintf('%a%d', ca=(/vertLvName_concate_sign/), i=(/vtLv/))

    call Field_IcGrid2D_Init( &
      & self%field2Ds(vtLv), &
      & icgrid2D, &
      & fieldLv_name, rank, long_name, units )

  end do

  ! 3 次元正二十面格子データを保持する構造体をポインタとして保持する.
  self%icgrid3D => icgrid3D


end subroutine initialize_Field_IcGrid3D1

!
!> \~japanese
!! \brief
!!
!! @param[in,out] self 構造型 Field_IcGrid3D の変数.
!! @param[in] icgrid3D
!! @param[in] name
!! @param[in] rank
!!
!! \~english
!! \brief
!!
!! @param[in,out] self The variable of derived type Field_IcGrid3D.
!! @param[in] icgrid3D
!! @param[in] name
!! @param[in] rank
!!
subroutine initialize_Field_IcGrid3D2( & 
  & self,                             &  ! (inout)
  & icgrid3D, name, rank              &  ! (in)
  & )

  ! 宣言文 ; Declaration statement
  !
  type(Field_IcGrid3D), intent(inout) :: self
  type(IcGrid3D_FVM), intent(in), target :: icgrid3D
  character(*), intent(in) :: name
  integer, intent(in) :: rank

  ! 実行文 ; Executable statements
  !
  call initialize_Field_IcGrid3D1(self, icgrid3D, name, rank, '', '')

end subroutine initialize_Field_IcGrid3D2

subroutine Field_IcGrid3D_Final(self)

  ! 宣言文 ; Declaration statement
  !
  type(Field_IcGrid3D), intent(inout) :: self

  ! 作業変数
  ! Work variable
  !
  integer vtLv
  integer verticalLv_num

  ! 実行文 ; Executable statements
  !

  ! 鉛直レベル数を取得する. 
  verticalLv_num = get_vertical_level_num(self%icgrid3D)

  ! 各鉛直レベルと関係づけられている Field_IcGrid2D のオブジェクトを最終化する. 
  do vtLv=1, verticalLv_num
    call Field_IcGrid2D_Final(self%field2Ds(vtLv))
  end do

  deallocate(self%field2Ds)

end subroutine Field_IcGrid3D_Final

!
!> \~japanese
!! \brief
!!
!! @param[in] self 構造型 Field_IcGrid3D の変数.
!! @param[in] vertical_level
!! @return field_layer
!!
!! \~english
!! \brief
!!
!! @param[in] self The variable of derived type Field_IcGrid3D.
!! @param[in] vertical_level
!! @return field_layer
!!
function get_field_layer(self, vertical_level) result(field_layer)

  ! 宣言文 ; Declaration statement
  !
  type(Field_IcGrid3D), intent(in) :: self
  integer, intent(in) :: vertical_level
  type(Field_IcGrid2D), pointer :: field_layer

  ! 作業変数
  ! Work variable
  !
  integer verticalLv_num

  ! 実行文 ; Executable statements
  !

  ! 鉛直レベル数を取得する. 
  verticalLv_num = get_vertical_level_num(self%icgrid3D)

  ! vertical_level のインデックスをチェックする. 
  ! インデックス外のときは Null ポインタを返す. 
  if ( vertical_level < 1 .or. verticalLv_num < vertical_level ) then
    field_layer => null()
  else
    field_layer => self%field2Ds(vertical_level)
  end if
  
end function get_field_layer


function get_icgrid3D(self) result(icgrid3D)

  ! 宣言文 ; Declaration statement
  !
  type(Field_IcGrid3D), intent(in) :: self
  type(IcGrid3D_FVM), pointer :: icgrid3D

  ! 実行文 ; Executable statements
  !

  icgrid3D => self%icgrid3D

end function get_icgrid3D

!
!> \~japanese
!! \brief 構造型 Field_IcGrid3D の基底構造型 Physical_Field の変数へのポインタを取得する.
!!
!! @param[in] self 構造型 Field_IcGrid3D の変数.
!! @return 基底構造型 Physical_Field の変数へのポインタ.
!!
!! \~english
!! \brief Gets the pointer to a variable of derived type Physical_Field which is a base derived type of Field_IcGrid2D.
!!
!! @param[in] self A variable of derived type Field_IcGrid2D.
!! @return The pointer to a variable of base derived type  Physical_Field.
!!
function get_Physical_Field( &
  & self &  ! (inout)
  & ) result(val)

  ! 宣言文 ; Declaration statements
  !
  type(Field_IcGrid3D), intent(in), target :: self
  type(Physical_Field), pointer :: val

  ! 実行文 ; Executable statements
  !
  val => self%base

end function get_Physical_Field

subroutine paste_field3D_margin(self)

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

  ! 作業変数
  ! Work variables
  !
  integer vtLv
  integer verticalLv_num

  ! 実行文 ; Executable statements
  !

  !
  verticalLv_num = get_vertical_level_num(self%icgrid3D)

  !
  do vtLv=1, verticalLv_num
    call paste_field2D_margin(self%field2Ds(vtLv))
  end do

end subroutine paste_field3D_margin


subroutine fill_value( &
  & self,              &  ! (inout)
  & val                &  ! (in)
  & )

  ! 宣言文 ; Declaration statements
  !
  type(Field_IcGrid3D), intent(inout) :: self
  real(DP) :: val

  ! 作業変数
  ! Work variables
  !
  integer :: k, kMax
  type(Field_IcGrid2D), pointer :: field3D_k

  ! 実行文 ; Executable statements
  !

  kMax = get_vertical_level_num(get_icgrid3D(self))

  do k=1, kMax
    field3D_k => get_field_layer(self, k)

    field3D_k%val(:,:,:,:) = val
  end do

end subroutine fill_value

subroutine copy_field3D( &
  & self,                 &  ! (inout)
  & field3D               &  ! (in)
  & )
  
  ! 宣言文 ; Declaration statements
  !
  type(Field_IcGrid3D), intent(inout) :: self
  type(Field_IcGrid3D), intent(in) :: field3D

  ! 作業変数
  ! Work variables
  !
  type(IcGrid3D_FVM), pointer :: slf_icgrid3D, cp_icgrid3D
  type(IcGrid2D_FVM), pointer :: slf_icgrid2D, cp_icgrid2D
  type(LocalProcRectInfo), pointer :: lcRCInfo
  type(Field_IcGrid2D), pointer :: slf_field3D_k, cp_field3D_k
  
  integer :: rcID, i, j, k
  integer :: iEMin, iEMax, jEMin, jEMax, KMax, rcIDMin, rcIDMax

  ! 実行文 ; Executable statements
  !

  slf_icgrid3D => get_icgrid3D(self)
  slf_icgrid2D => get_icgrid2D(slf_icgrid3D)
  cp_icgrid3D => get_icgrid3D(field3D)
  cp_icgrid2D => get_icgrid2D(cp_icgrid3D)

  if ( &
    &      ( get_glevel(slf_icgrid2D) /= get_glevel(cp_icgrid2D) ) &
    & .or. ( get_vertical_level_num(slf_icgrid3D) /= get_vertical_level_num(cp_icgrid3D) ) &
    & ) then

    stop

  end if

  lcRCInfo => get_LocalProcRectInfo(slf_icgrid2D)
  
  call get_InteriorRegion_IndexInfo( &
    & lcRCInfo,            &                         ! (in)
    & iEMin, iEMax, jEMin, jEMax, rcIDMin, rcIDMax ) ! (out)

  KMax = get_vertical_level_num(slf_icgrid3D)

  do k=1, KMax
  
    slf_field3D_k => get_field_layer(self, k)
    cp_field3D_k => get_field_layer(field3D, k)
    
    do rcID=rcIDMin, rcIDMax
      do j=jEMin, jEMax
        do i=iEMin, iEMax
          slf_field3D_k%val(:, i,j,rcID) = cp_field3D_k%val(:, i,j,rcID)
         end do
      end do
    end do
  end do

end subroutine copy_field3D

!
!> \~japanese
!! \brief 物理場の長い名前(あるいは解説文)を取得する.
!!
!! @param[in] self 構造型 Field_IcGrid3D の変数.
!! @return  物理場の長い名前(あるいは解説文).
!!
!! \~english
!! \brief Get the long name of (or explanation for) the physcial field.
!!
!! @param[in] self The variable of derived type Field_IcGrid3D.
!! @return  The long name of (or explanation for) the physical field.
!!
function get_long_field3D_name(self) result(val)

  ! 宣言文 ; Declaration statement
  !
  type(Field_IcGrid3D), intent(in) :: self
  character(STRING) val

  ! 実行文 ; Executable statement
  !

  val = get_long_name_(self%base)

end function get_long_field3D_name

!
!> \~japanese
!! \brief 物理場の長い名前(あるいは解説文)を設定する.
!!
!! @param[in,out] self 構造型 Field_IcGrid3D の変数.
!! @param[in] long_name 物理場の長い名前(あるいは解説文).
!!
!! \~english
!! \brief Set the long name of (or explanation for) the physcial field.
!!
!! @param[in,out] self The variable of derived type Field_IcGrid3D.
!! @param[in] long_name The long name of (or explanation for) the physical field.
!!
subroutine set_long_field3D_name(self, long_name)
  ! 宣言文 ; Declaration statement
  !
  type(Field_IcGrid3D), intent(inout) :: self
  character(*), intent(in) ::  long_name

  ! 実行文 ; Executable statement
  !

  call set_long_name_(self%base, long_name)

end subroutine set_long_field3D_name

!
!> \~japanese
!! \brief 物理場の単位を取得する.
!!
!! @param[in] self 構造型 Field_IcGrid3D の変数.
!! @reeturn    物理場の単位.
!!
!! \~english
!! \brief Get the unit of the physical field.
!!
!! @param[in] self The variable of derived type Field_IcGrid2D.
!! @return    The unit of the physical field.
!!
function get_field3D_units(self) result(val)
  ! 宣言文 ; Declaration statement
  !
  type(Field_IcGrid3D), intent(in) :: self
  character(TOKEN) val

  ! 実行文 ; Executable statement
  !

  val = get_units_(self%base)

end function get_field3D_units

!
!> \~japanese
!! \brief 物理場の単位を設定する.
!!
!! @param[in,out] self 構造型 Field_IcGrid3D の変数.
!! @param[in] units 物理場の単位.
!!
!! \~english
!! \brief Sets the unit of the physical field.
!!
!! @param[in,out] self The variable of derived type Field_IcGrid3D.
!! @param[in] units The unit of the physical field.
!!
subroutine set_field3D_units(self, units)
  ! 宣言文 ; Declaration statement
  !
  type(Field_IcGrid3D), intent(inout) :: self
  character(*), intent(in) ::  units

  ! 実行文 ; Executable statement
  !

  call set_units_(self%base, units)

end subroutine set_field3D_units

!
!> \~japanese
!! \brief 物理場の次元を取得する.
!!
!! @param[in] self 構造型 Field_IcGrid3D の変数.
!! @reeturn    物理場のランク.
!!
!! \~english
!! \brief Get The rank of the physical field.
!!
!! @param[in] self The variable of derived type Field_IcGrid3D.
!! @reeturn    The rank of the physical field.
!!
function get_field3D_rank(self) result(val)
  ! 宣言文 ; Declaration statement
  !
  type(Field_IcGrid3D), intent(in) :: self
  integer val

  ! 実行文 ; Executable statement
  !

  val = get_dimension_(self%base)

end function get_field3D_rank


!
!> \~japanese
!! \brief 物理場の(短い)名前を取得する.
!!
!! @param[in] self 構造型 Field_IcGrid3D の変数.
!! @return 物理場の(短い)名前.
!!
!! \~english
!! \brief Get the (short) name of the physical field.
!!
!! @param[in] self The variable of derived type Field_IcGrid3D.
!! @return The (short) name of the physical field.
!!
function get_field3D_name( &
  & self &
  & ) result(name)

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

  ! 実行文 ; Executable statement
  !

  name = get_name_(self%base)

end function get_field3D_name

!
!> \~japanese
!! \brief 物理場の(短い)名前を設定する.
!!
!! @param[in,out] self 構造型 Field_IcGrid3D の変数.
!! @param[in] name 物理場の(短い)名前.
!!
!! \~english
!! \brief Sets the (short) name of the physical field.
!!
!! @param[in,out] self The variable of derived type Field_IcGrid3D.
!! @param[in] name The (short) name of the physical field.
!!
subroutine set_field3D_name( &
  & self, name )

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

  ! 実行文 ; Executable statement
  !

  call set_name_(self%base, name)

end subroutine set_field3D_name

end module Field_IcGrid3D_Manager
