!> \~japanese
!! \brief 正二十面体格子上の物理場データを管理する構造型およびそれを操作・解析するための手続きを提供するモジュール.
!!
!! \par Field_IcGrid2D_Manager モジュールについて
!!   - 本モジュールでは, 正二十面格子上に水平 2 次元的に分布する物理場データを管理する構造型 Field_IcGrid2D を提供している.
!!   - 物理場データを保持する配列のメモリ確保は, initialize_Field_IcGrid2D によって構造型 Field_IcGrid2D の変数が初期化される際に行われる.
!!     また, その配列のメモリ解放は finalize_Field_IcGrid2D によって行われる.
!!   - 構造型 Field_IcGrid2D の成分である構造型 IcGrid2D_FVM のポインタ icgrid は, この物理場と関連付けられる正二十面格子データを指している.
!!   - 本モジュールでは, 正二十面格子上の物理場データを操作あるいは解析するためのシンプルな手続き(全球平均など)を提供している.
!!
!! \~english
!! \brief This module provides the derived type which manages the data of a physical field on a icosahedral grid and
!! some subroutines to manupulate and analize the field data.
!!
!! \par About Field_IcGrid2D_Manager module
!!   - 本モジュールでは, 正二十面格子上に水平 2 時限的に分布する物理場データを管理する構造型 Field_IcGrid2D を提供している.
!!   - 物理場データを保持する配列のメモリ確保は, initialize_Field_IcGrid2D によって構造型 Field_IcGrid2D の変数が初期化される際に行われる.
!!     また, その配列のメモリ解放は finalize_Field_IcGrid2D によって行われる.
!!   - 構造型 Field_IcGrid2D の成分である構造型 IcGrid2D_FVM のポインタ icgrid は, この物理場と関連付けられる正二十面格子データを指している.
!!   - 本モジュールでは, 正二十面格子上の物理場データを操作あるいは解析するためのシンプルな手続き(全球平均など)を提供している.
!!
!! \~
!! \since IGMBaseLib 1.0
!!
!! <br><br>
!! \b Copyright (C) GFD Dennou Club, 2011-2012. All rights reserved. <br>
!! \b license ?? <br>
!! \author Yuta Kawai
!!
module Field_IcGrid2D_Manager

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

  ! Message Passing Interface Library
  !
  !
#ifdef LIB_MPI
  use mpi
#endif

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

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

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

  ! 数学
  ! Mathematic
  !
  use igmcore_math, only: PI

  ! 正二十面体格子データの管理
  ! Manager of Icosahedral grid data
  !
  use IcGrid2D_FVM_Manager, only: &
    & IcGrid2D_FVM, &
    & get_idMin, get_idMax, get_EffSize_Min, get_EffSize_Max, get_IcRadius, &
    & get_LocalProcRectInfo, &
    & RC_REGIONS_NUM

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

  ! 物理場データを管理する構造体のための基底モジュール
  ! Base module for some derived types to manage the physical field data
  !
  use Physical_Field_Manager, only: &
    & Physical_Field, &
    & Physical_Field_Init, Physical_Field_Final, &
    & 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

  ! 宣言文 ; Declaration statement
  !
  implicit none
  private

  !
  !> \~japanese
  !! \brief 水平 2 次元的に分布する正二十面体格子上の物理場データを管理する構造体.
  !!
  !!  本クラスは, 正二十面体格子上の物理場データ, 物理場の名前, 単位などを管理する.
  !! この物理場データと対応付けられる正二十面体格子座標データは, 構造体 IcGrid2D_FVM が持っている.
  !! 本構造体は, その成分として, この構造体 IcGrid2D_FVM の変数のポインタを保持している.
  !!
  !! \~english
  !! \brief The derived type which manages the data of a physical field on a icosahedral grid distributed horizontally.
  !!
  !!  This derived type manages the physical field data on icosahedral grid and the name and unit of the field.
  !! The vertex data of icosahedral grid coressponded to this physical field data is contained in derived type IcGrid2D_FVM.
  !! This type contains the pointer to a variable of derived type IcGrid2D_FVM as a component of derived type IcGrid2D_FVM.
  !!
  type, public :: Field_IcGrid2D

    !> \~japanese
    !! \brief 正二十面体格子上で水平 2 次元的に分布した物理場データを保持する配列.
    !!
    !! 配列添字の各次元は, 正二十面体格子管理クラス IcGrid2D_FVM のメンバー変数である配列 rcs_AGrid の各次元と対応している.
    !! すなわち, 配列の各次元は,
    !! 物理場の各次元の成分, 水平方向の添え字, 水平方向の添え字, 広域矩形領域の ID を意味する.
    !!
    !! \~english
    !! \brief The array containg the physical field horizontally distributed on icosahedral grid..
    !!
    !! 配列添字の各次元は, 正二十面体格子管理クラス IcGrid2D_FVM のメンバー変数である配列 rcs_AGrid の各次元と対応している.
    !! すなわち, 配列の各次元は,
    !! 物理場の各次元の成分, 水平方向の添え字, 水平方向の添え字, 広域矩形領域の ID を意味する.
    !!
    real(DP), pointer :: val(:,:,:,:) => null()
    ! f2003: real(DP), allocatable :: val(:,:,:,:)

    ! 非公開メンバ変数
    ! Private memeber variables
    !

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

    !> \~japanese 正二十面体格子を管理する構造体 IcGrid2D_FVM の変数へのポインタ.
    !! \~english The pointer to a variable of derived type IcGrid2D_FVM which manages the icosahedral grid.
    type(IcGrid2D_FVM), pointer :: icgrid
    ! f2003: type(IcGrid2D_FVM), pointer, private :: icgrid

    !> \~japanese
    !!  北極近傍の格子点上における物理場データを保持する配列.
    !!  この配列の各次元はそれぞれ,
    !! 物理場データの各成分, 水平方向の添え字, 水平方向の添え字, (北半球側の)広域矩形領域の ID　を意味する.
    !!
    !! \~english
    !!
    real(DP), pointer :: dataNearNPole(:,:,:,:)

    !> \~japanese 南極近傍の格子点上における物理場データを保持する配列.
    !!  この配列の各次元はそれぞれ,
    !! 物理場データの各成分, 水平方向の添え字, 水平方向の添え字, (南半球側の)広域矩形領域の ID　を意味する.
    !!
    !! \~english
    !!
    real(DP), pointer :: dataNearSPole(:,:,:,:)
    
  end type Field_IcGrid2D

  ! interface 文 ; interface statements
  !
  interface Field_IcGrid2D_Init
    module procedure init_Field_IcGrid2D1
    module procedure init_Field_IcGrid2D2
  end interface Field_IcGrid2D_Init

  ! 公開手続き
  ! Public procedures
  !

  public :: Field_IcGrid2D_Init, Field_IcGrid2D_Final

  public :: paste_field2D_margin, global_mean

  public :: get_field2D_name, get_long_field2D_name, get_field2D_rank, get_field2D_units, &
    &       get_icgrid, get_Physical_Field

  public :: set_field2D_name, set_long_field2D_name, set_field2D_units

  ! 非公開変数
  ! Private variables
  !

  !> \~japanese
  !! \~english
  integer :: northPole_comm

  !> \~japanese
  !! \~english
  integer :: southPole_comm

  !> \~japanese
  !! \~english
  logical :: NSPoleMPIComm_EnableFlag = .false.
  
contains

!
!> \~japanese
!! \brief 構造型 Field_IcGrid2D の変数の初期化を行う.
!!
!! @param[in,out] self       構造体 Field_IcGrid2D の変数.
!! @param[in]     icgrid     構造体 IcGrid2D_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_IcGrid2D.
!! @param[in]     icgrid     The variable of derived type IcGrid2D_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 init_Field_IcGrid2D0( &
  & self,                     &  ! (inout)
  & icgrid, name, field_rank, &  ! (in)
  & long_name, units          &  ! (in)
  & )

  ! 宣言文 ; Declaration statement
  !
  type(Field_IcGrid2D), intent(inout) :: self
  type(IcGrid2D_FVM), pointer :: icgrid
  character(*), intent(in) :: name
  integer, intent(in) :: field_rank
  character(*), intent(in) :: long_name
  character(*), intent(in) :: units

  ! 作業変数
  ! Work variables
  !
  type(LocalProcRectInfo), pointer :: lcRCInfo
  integer :: glEMin, glEMax
  
  integer :: rcID
  integer :: northPole_procList(5), southPole_procList(5)
  integer :: northPole_group, southPole_group
  integer :: group_world, ierr
  
  ! 実行文 ; Executable statement
  !

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

  ! 正二十面格子の広域矩形領域の情報を取得する.
  self%icgrid => icgrid

  glEMin = get_EffSize_Min(self%icgrid)
  glEMax = get_EffSize_Max(self%icgrid)

  ! 正二十面格子の局所矩形領域の情報を取得する.
  lcRCInfo => get_LocalProcRectInfo(self%icgrid)

  ! 物理場データを格納する配列のメモリを確保する.
  allocate( &
    & self%val( 1:field_rank, &
      & lcRCInfo%iIdMin:lcRCInfo%iIdMax, &
      & lcRCInfo%jIdMin:lcRCInfo%jIdMax, &
      & lcRCInfo%globalRCIDMin:lcRCInfo%globalRCIDMax) &
    & )

  ! 各局所矩形領域において, 共有する極近傍の物理場データを保持する配列のメモリを確保する.
  allocate(self%dataNearNPole(1:field_rank, glEMin:glEMin+1,glEMin:glEMin+1, 1:5) )
  allocate(self%dataNearSPole(1:field_rank, glEMax-1:glEMax,glEMax-1:glEMax, 6:10))

#ifdef LIB_MPI  
  ! MPI による並列化が有効である場合には,
  ! 各局所矩形領域と対応付けられる各プロセスにおいて極近傍の物理場データを同期するための通信グループを生成する.
  ! この処理は, Field_IcGrid2D_Init が始めて呼ばれたときに一度だけ行われる.
  if ( lcRCInfo%mpi_flag .and. ( .not. NSPoleMPIComm_EnableFlag ) ) then

    do rcID=1,5
      northPole_procList(rcID) = ( rcID     - 1 ) * 4**lcRCInfo%rlevel
      southPole_procList(rcID) = ( (rcID+5) ) * 4**lcRCInfo%rlevel - 1   
    end do

    call MPI_Comm_group(MPI_COMM_WORLD, group_world, ierr)
  
    call MPI_Group_incl(group_world, 5, northPole_procList, northPole_group, ierr)
    call MPI_Group_incl(group_world, 5, southPole_procList, southPole_group, ierr)

    call MPI_Comm_create(MPI_COMM_WORLD, northPole_group, northPole_comm, ierr)    
    call MPI_Comm_create(MPI_COMM_WORLD, southPole_group, southPole_comm, ierr)
    
    NSPoleMPIComm_EnableFlag = .true. 
    
  end if
#endif
  

end subroutine init_Field_IcGrid2D0

!
!> \~japanese
!! \brief 構造型 Field_IcGrid2D の変数の初期化を行う.
!!
!! @param[in,out] self       構造体 Field_IcGrid2D の変数.
!! @param[in]     icgrid     構造体 IcGrid2D_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_IcGrid2D.
!! @param[in]     icgrid     The variable of derived type IcGrid2D_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 init_Field_IcGrid2D1( &
  & self,                                &  ! (inout)
  & icgrid, name, rank, long_name, units &  ! (in)
  & )

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

  ! 作業変数
  ! Work variables
  !
  integer :: idMin, idMax
  type(IcGrid2D_FVM), pointer :: icgrid_ptr

  ! 実行文 ; Executable statement
  !

  icgrid_ptr => icgrid
  call init_Field_IcGrid2D0(self, icgrid_ptr, name, rank, long_name, units)

end subroutine init_Field_IcGrid2D1

!
!> \~japanese
!! \brief 構造型 Field_IcGrid2D の変数の初期化を行う.
!!
!! @param[in,out] self       構造体 Field_IcGrid2D の変数.
!! @param[in]     icgrid     構造体 IcGrid2D_FVM の変数.
!! @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_IcGrid2D.
!! @param[in]     icgrid     The variable of derived type IcGrid2D_FVM.
!! @param[in]     name       The (short) name of the physical field.
!! @param[in]     field_rank  The rank of the physical filed.
!!
subroutine init_Field_IcGrid2D2( &
  & self,              &  ! (inout)
  & icgrid, name, rank &  ! (in)
  & )

  ! 宣言文 ; Declaration statement
  !
  type(Field_IcGrid2D), intent(inout) :: self
  type(IcGrid2D_FVM), intent(in), target :: icgrid
  character(*), intent(in) :: name
  integer, intent(in) :: rank

  ! 作業変数
  ! Work variables
  !

  ! 実行文 ; Executable statement
  !
  call init_Field_IcGrid2D1(self, icgrid, name, rank, '', '')

end subroutine init_Field_IcGrid2D2

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

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

  ! 実行文　; Executable statement
  !

  ! 物理場データを保持している配列のメモリを解放する.
  deallocate(self%val)
  deallocate(self%dataNearNPole)
  deallocate(self%dataNearSPole)
  
  call Physical_Field_Final(self%base)

end subroutine Field_IcGrid2D_Final

!
!> \~japanese
!! \brief 構造型 Field_IcGrid2D の基底構造型 Physical_Field の変数へのポインタを取得する.
!!
!! @param[in,out] self 構造型 Field_IcGrid2D の変数.
!! @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,out] 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_IcGrid2D), intent(inout), target :: self
  type(Physical_Field), pointer :: val

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

end function get_Physical_Field

!
!> \~japanese
!! \brief 構造体 Field_IcGrid2D の成分である構造型 IcGrid2D_FVM の変数へのポインタを取得する.
!!
!! @param[in,out] self 構造型 Field_IcGrid2D の変数.
!! @return 構造型 IcGrid2D_FVM の変数へのポインタ.
!!
!! \~english
!! \brief Get the pointer to the variable of derived type IcGrid2D_FVM which is a component of derived type Field_IcGrid2D.
!!
!! @param[in,out] self Field_IcGrid2D A variable of derived type Field_IcGrid2D.
!! @return IcGrid2D_FVM The pointer to a variable of derived type IcGrid2D_FVM.
!!
function get_icgrid( &
  & self             &  ! (inout)
  & ) result(icgrid)

  ! 宣言文 ; Declaration statement
  !
  type(Field_IcGrid2D), intent(in) :: self       !
  type(IcGrid2D_FVM), pointer :: icgrid

  ! 実行文 ; Executable statement
  !

  icgrid => self%icgrid

end function get_icgrid

!
!> \~japanese
!! \brief 各局所矩形領域のハロー領域を, 隣り合う局所矩形領域の境界近傍の物理場データで埋める処理を行う.
!!
!!  MPI による並列化が有効である場合には, 近傍の局所矩形領域と 1 対 1 通信を行って他プロセス上にあるデータを収集し,
!!  各々プロセスのハロー領域を埋める(詳細は, 同モジュールの手続き paste_field2D_margin_parallelMode を参照).
!!
!! @param[in,out] self 構造型 Field_IcGrid2D の変数.
!!
!! \~english
!! \brief 各局所矩形領域のハロー領域を, 隣り合う局所矩形領域の境界近傍の物理場データで埋める処理を行う.
!!
!!  MPI による並列化が有効である場合には, 近傍の局所矩形領域と 1 対 1 通信を行って他プロセス上にあるデータを収集し,
!!  各々プロセスのハロー領域を埋める(詳細は, 同モジュールの手続き paste_field2D_margin_parallelMode を参照).
!!
!! @param[in,out] self Field_IcGrid2D A variable of derived type Field_IcGrid2D.
!!
subroutine paste_field2D_margin( &
  & self                         &  ! (inout)
  & )

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

  ! 作業変数
  ! Work variables
  !
  type(LocalProcRectInfo), pointer :: lpRCInfo

  ! 実行文 ; Executable statements
  !

  lpRCInfo => get_LocalProcRectInfo(self%icgrid)

  ! シリアルか MPI 並列かで, 呼び出す手続きを変える.
  if ( lpRCInfo%mpi_flag ) then
#ifdef LIB_MPI
    call paste_field2D_margin_parallelMode(self)
#endif
  else
    call paste_field2D_margin_serialMode(self)
  end if

end subroutine paste_field2D_margin

!
!> \~japanese
!! \brief 正二十面体格子点上の物理場の全球空間平均を計算する.
!!
!! @param[in,out] self 構造型 Field_IcGrid2D の変数.
!! @return 物理場の全球空間平均値.
!!
!! \~english
!! \brief Calculate the global spatial average of a physical field on icosahedral grid.
!!
!! @param[in,out] self A variable of derived type Field_IcGrid2D.
!! @return The global spatial average of a physical field.
!!
function global_mean( &
  & self &  ! (inout)
  & ) result(val)

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

  ! 作業変数
  ! Work variables
  !
  type(IcGrid2D_FVM), pointer :: icgrid
  type(LocalProcRectInfo), pointer :: lcRCInfo

  integer :: rcID, i, j
  integer :: glEMin, glEMax
  integer :: iEMin, iEMax, jEMin, jEMax, lcRCID, rcIDMin, rcIDMax
  integer :: jMaxAveBound, iMinAveBound
  
  real(DP) :: local_val
  real(DP) :: area
  integer :: ierr
  
  ! 実行文 ; Executable statement
  !

  ! 物理場のランクが 1 次元(スカラー場)であることを確認する.
  if( get_field2D_rank(self) /= 1 ) then
    call MessageNotify( 'E', 'IGMBaseLib IO::Field_IcGrid2D::global_mean', &
      & "The rank of Field_IcGrid2D(=%d) is invalid.", i=(/ get_field2D_rank(self) /) )
    stop
    return
  end if

  glEMin = get_EffSize_Min(self%icgrid)
  glEMax = get_EffSize_Max(self%icgrid)
  icgrid => self%icgrid
  lcRCInfo => get_LocalProcRectInfo(icgrid)
  lcRCID = lcRCInfo%globalRCID
  
  call get_InteriorRegion_IndexInfo( &
    & lcRCInfo,            &                         ! (in)
    & iEMin, iEMax, jEMin, jEMax, rcIDMin, rcIDMax ) ! (out)
     
  ! まず指定されたスカラー場の全球積分値を求めるために, 各局所矩形領域に置いて格子点上の値を総和する.
  !

  ! 隣り合う局所領域において, 格子点が重なる場合を考慮する.
  jMaxAveBound = jEMax 
  iMinAveBound = iEMin
  if ( jEMax == glEMax ) jMaxAveBound = jEMax - 1
  if ( iEMin == glEMin ) iMinAveBound = iEMin + 1
  
  ! 各プロセスに対応する局所領域において, 格子点上の物理場の値を総和する.
  !

  val = 0d0
  local_val = 0d0

  do rcID=rcIDMin, rcIDMax
    do j=jEMin, jMaxAveBound
      do i=iMinAveBound, iEMax
        local_val = local_val + self%val(1, i,j,rcID) * icgrid%rcs_CVInfo(i,j,rcID)%cv_area
        area = area+icgrid%rcs_CVInfo(i,j,rcID)%cv_area
   
      end do
    end do
  end do

  if ( lcRCInfo%mpi_flag ) then

    if ( lcRCID == 1 .and. iEMin == glEMin .and. jEMin == glEMin ) then
      local_val = local_val + self%val(1, glEMin,glEMin,1) * icgrid%rcs_CVInfo(glEMin,glEMin,1)%cv_area  
    end if

    if ( lcRCID == 10 .and. iEMax == glEMax .and. jEMax == glEMax ) then
      local_val = local_val + self%val(1, glEMax,glEMax,10) * icgrid%rcs_CVInfo(glEMax,glEMax,10)%cv_area
    end if

#ifdef LIB_MPI      
    ! MPI による並列化が有効である場合には, 各プロセスから local_val を収集して総和する.
    call MPI_AllReduce( local_val, val, 1, MPI_REAL8, &
      &                 MPI_SUM, MPI_COMM_WORLD, ierr )
#endif

  else
    
    local_val = local_val &
      &  + self%val(1,glEMin,glEMin, 1) * icgrid%rcs_CVInfo(glEMin,glEMin,1)%cv_area    &
      &  + self%val(1,glEMax,glEMax, 10)  * icgrid%rcs_CVInfo(glEMax,glEMax,10)%cv_area

    val = local_val

  end if
  
  ! 球の面積で規格化する.
  val = val / ( 4.0d0 * PI * get_IcRadius(icgrid)**2 )

end function global_mean

!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!
! Physical_Field_mod の手続きをオーバライド
!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!


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

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

  ! 実行文 ; Executable statement
  !

  val = get_long_name_(self%base)

end function get_long_field2D_name

!
!> \~japanese
!! \brief 物理場の長い名前(あるいは解説文)を設定する.
!!
!! @param[in,out] self 構造型 Field_IcGrid2D の変数.
!! @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_IcGrid2D.
!! @param[in] long_name The long name of (or explanation for) the physical field.
!!
subroutine set_long_field2D_name(self, long_name)
  ! 宣言文 ; Declaration statement
  !
  type(Field_IcGrid2D), intent(inout) :: self
  character(*), intent(in) ::  long_name

  ! 実行文 ; Executable statement
  !

  call set_long_name_(self%base, long_name)

end subroutine set_long_field2D_name

!
!> \~japanese
!! \brief 物理場の単位を取得する.
!!
!! @param[in] self 構造型 Field_IcGrid2D の変数.
!! @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_field2D_units(self) result(val)
  ! 宣言文 ; Declaration statement
  !
  type(Field_IcGrid2D), intent(in) :: self
  character(TOKEN) val

  ! 実行文 ; Executable statement
  !

  val = get_units_(self%base)

end function get_field2D_units

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

  ! 実行文 ; Executable statement
  !

  call set_units_(self%base, units)

end subroutine set_field2D_units

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

  ! 実行文 ; Executable statement
  !

  val = get_dimension_(self%base)

end function get_field2D_rank


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

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

  ! 実行文 ; Executable statement
  !

  name = get_name_(self%base)

end function get_field2D_name

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

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

  ! 実行文 ; Executable statement
  !

  call set_name_(self%base, name)

end subroutine set_field2D_name

!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!1
! 非公開手続き
!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

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

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

  ! 作業変数
  ! Work variables
  !
  integer :: rcIDList_NSemiSp(0:6) = (/ 5, 1, 2, 3, 4, 5, 1 /)
  integer :: rcIDList_SSemiSp(0:6) = (/ 10, 6, 7, 8, 9, 10, 6 /)
  integer :: glEMin, glEMax
  integer :: marginw, glIdMin
  type(LocalProcRectInfo), pointer :: lpRCInfo
  integer :: mylcRCID, mylcRC_i, mylcRC_j
  integer rcID, rcIDdum, k

  ! 実行文 ; Executable statements
  !

  lpRCInfo => get_LocalProcRectInfo(self%icgrid)
  glEMin = get_EffSize_Min(self%icgrid)
  glEMax = get_EffSize_Max(self%icgrid)

  ! ノリシロの幅を計算する.
  glIdMin = get_IdMin(self%icgrid)
  marginw = glEMin - glIdMin

  do rcID=lpRCInfo%globalRCIDMin, lpRCInfo%globalRCIDMax
    do k=1, marginw

      if ( rcID <= 5 ) then
         ! 北半球側の矩形領域ののりしろに値をコピーする.
         !

        self%val(:, glEMin:glEMax, glEMin-k, rcID) = self%val(:, glEMin+k, glEMin:glEMax, rcIDList_NSemiSp(rcID-1))
        self%val(:, glEMax+k, glEMin:glEMax, rcID)  = self%val(:, glEMin+k, glEMin:glEMax, rcIDList_SSemiSp(rcID-1))
        self%val(:, glEMin:glEMax, glEMax+k, rcID)  = self%val(:, glEMin:glEMax, glEMin+k, rcIDList_SSemiSp(rcID))
        self%val(:, glEMin-k, glEMin:glEMax, rcID) = self%val(:, glEMin:glEMax, glEMin+k, rcIDList_NSemiSp(rcID+1))
        
      else

         ! 南半球側の矩形領域ののりしろに値をコピーする.
         !

        rcIDdum = rcID - 5
        self%val(:, glEMin:glEMax, glEMin-k, rcID) = self%val(:, glEMin:glEMax, glEMax-k, rcIDList_NSemiSp(rcIDdum))
        self%val(:, glEMax+k, glEMin:glEMax, rcID)  = self%val(:, glEMin:glEMax, glEMax-k, rcIDList_SSemiSp(rcIDdum-1))
        self%val(:, glEMin:glEMax, glEMax+k, rcID)  = self%val(:, glEMax-k, glEMin:glEMax, rcIDList_SSemiSp(rcIDdum+1))
        self%val(:, glEMin-k, glEMin:glEMax, rcID) = self%val(:, glEMax-k, glEMin:glEMax, rcIDList_NSemiSp(rcIDdum+1))

      end if
    end do
  end do
  
  call collect_data_near_poles_serialMode(self)       
      
end subroutine paste_field2D_margin_serialMode

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

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

  ! 作業変数
  ! Work variables
  !
  integer :: glEMin, glEMax

  ! 実行文 ; Executable statements
  !
  glEMin = get_EffSize_Min(self%icgrid)
  glEMax = get_EffSize_Max(self%icgrid)

  self%dataNearNPole(:,:,:,:) = self%val(:, glEMin:glEMin+1, glEMin:glEMin+1, 1:5)
  self%dataNearSPole(:,:,:,:) = self%val(:, glEMax-1:glEMax, glEMax-1:glEMax, 6:10)

end subroutine collect_data_near_poles_serialMode

!> \~japanese
!! \brief
!!
!! @param[in,out] self 構造型 Field_IcGrid2D の変数.
!!
!! \~english
!! \brief
!!
!! @param[in,out] self The variable of derived type Field_IcGrid2D.
!!
#ifdef LIB_MPI
subroutine paste_field2D_margin_parallelMode( &
  & self                                      &  ! (inout)
  & )

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

  ! 作業変数
  ! Work variables
  !
  integer :: fieldRank
  integer :: glEMin, glEMax
  
  type(LocalProcRectInfo), pointer :: lpRCInfo
  integer :: iEMin, iEMax, jEMin, jEMax, rcIDMin, rcIDMax
  integer :: marginw, glIdMin

  integer :: lcRCID
  integer :: k
  integer :: ihlVecSize, jhlVecSize, sendVecSize, recvVecSize

  real(DP), allocatable :: send_haloVec(:,:), send_haloCorner(:,:)
  real(DP), allocatable :: recv_haloVec(:,:), recv_haloCorner(:,:)
  
  integer :: iRecHaloShape(2), jRecHaloShape(2)
  integer :: dist(4), source(4), tag(4), recv_tag(4), ireq_send(2), ireq_recv(2)
  integer :: ireq_send_count, ireq_recv_count

  integer :: myRank, myLeftRank, myRightRank, myLowerRank, myUpperRank
  integer :: send_mstatus(MPI_STATUS_SIZE, 4), recv_mstatus(MPI_STATUS_SIZE, 4)
  integer :: ierr, ireqID

  integer :: cutleftColId, cutRightColId, cutUpRowId, cutLowRowId
  
  ! 実行文 ; Executable statements
  !

  glIdMin = get_IdMin(self%icgrid)
  glEMin = get_EffSize_Min(self%icgrid)
  glEMax = get_EffSize_Max(self%icgrid)
  fieldRank = get_field2D_rank(self)

  lpRCInfo => get_LocalProcRectInfo(self%icgrid)
  lcRCID = lpRCInfo%globalRCID
  
  call get_InteriorRegion_IndexInfo( &
    & lpRCInfo,            &                         ! (in)
    & iEMin, iEMax, jEMin, jEMax, rcIDMin, rcIDMax ) ! (out) 

  ! ノリシロの幅を計算する.
  marginw = glEMin - glIdMin
  ihlVecSize = ( iEMax - iEMin + 1 ) * fieldRank
  jhlVecSize = ( jEMax - jEMin + 1 ) * fieldRank
  iRecHaloShape(:) = (/ fieldRank, iEMax - iEMin + 1 /)
  jRecHaloShape(:) = (/ fieldRank, jEMax - jEMin + 1 /)

  allocate(send_haloVec(max(ihlVecSize, jhlVecSize), 4) )
  allocate(recv_haloVec(max(ihlVecSize, jhlVecSize), 4) )
  allocate(send_haloCorner(fieldRank, 4))
  allocate(recv_haloCorner(fieldRank, 4))

  myRank = lpRCInfo%lcproc_MPIRank
  myLeftRank = get_LcProcRCLeft_MPIRank(lpRCInfo)
  myRightRank = get_LcProcRCRight_MPIRank(lpRCInfo)
  myUpperRank = get_LcProcRCUp_MPIRank(lpRCInfo)
  myLowerRank = get_LcProcRCLow_MPIRank(lpRCInfo)
  
  !write(*,*) myRank, myLeftRank, myRightRank, myUpperRank, myLowerRank
  do k=1, marginw
    ! 送信
    
    cutLeftColId = jEMin + k - 1
    if ( jEMin == glEMin ) cutLeftColId = jEMin + k
    
    cutRightColId = jEMax - k + 1
    if ( jEMax == glEMax ) cutRightColId = jEMax - k

    cutUpRowId = iEMin + k - 1
    if ( iEMin == glEMin ) cutUpRowId = iEMin + k

    cutLowRowId = iEMax - k + 1
    if ( iEMax == glEMax ) cutLowRowId = iEMax - k
    
    ! 局所矩形領域の右側のハロー領域を埋める.
    send_haloVec(1:ihlVecSize, 1) = reshape(self%val(:, iEMin:iEMax, cutLeftColId, lcRCID), (/ ihlVecSize /) )
    send_haloVec(1:jhlVecSize, 2) = reshape(self%val(:, cutLowRowId, jEMin:jEMax, lcRCID), (/ jhlVecSize /) )
    send_haloVec(1:ihlVecSize, 3) = reshape(self%val(:, iEMin:iEMax, cutRightColId, lcRCID), (/ ihlVecSize /) )
    send_haloVec(1:jhlVecSize, 4) = reshape(self%val(:, cutUpRowId, jEMin:jEMax, lcRCID), (/ jhlVecSize /) )
    dist(:) = (/ myLeftRank, myLowerRank, myRightRank, myUpperRank /)

    do ireqID=1, 4
      if ( mod(ireqID, 2) == 0 ) then
        sendVecSize = jhlVecSize
      else
        sendVecSize = ihlVecSize
      end if

      call MPI_iSend( &
         & send_haloVec(1:sendVecSize, ireqID), sendVecSize, MPI_REAL8, dist(ireqID), myRank,     &
         & MPI_COMM_WORLD, ireq_send(ireqID), ierr )
      
    end do

    source(:) = (/ myLeftRank, myLowerRank, myRightRank, myUpperRank /)

    do ireqID=1, 4
      if ( mod(ireqID, 2) == 0 ) then
        recvVecSize = jhlVecSize
      else
        recvVecSize = ihlVecSize
      end if

      call MPI_iRecv( &
         & recv_haloVec(1:recvVecSize, ireqID), recvVecSize, MPI_REAL8, source(ireqID), source(ireqID),     &
         & MPI_COMM_WORLD, ireq_recv(ireqID), ierr )  
      
    end do

    do ireqID=1,4
      call MPI_Wait(ireq_send(ireqID), send_mstatus(:,ireqID), ierr)
      call MPI_Wait(ireq_recv(ireqID), recv_mstatus(:,ireqID), ierr)
    end do
    
    !
    !
    self%val(:,iEMin:iEMax,jEMin-k,lcRCID) = reshape(recv_haloVec(1:ihlVecSize,1), (/ fieldRank, ihlVecSize/fieldRank /))
    self%val(:,iEMax+k,jEMin:jEMax,lcRCID) = reshape(recv_haloVec(1:jhlVecSize,2), (/ fieldRank, jhlVecSize/fieldRank /))
    self%val(:,iEMin:iEMax,jEMax+k,lcRCID) = reshape(recv_haloVec(1:ihlVecSize,3), (/ fieldRank, ihlVecSize/fieldRank /))
    self%val(:,iEMin-k,jEMin:jEMax,lcRCID) = reshape(recv_haloVec(1:jhlVecSize,4), (/ fieldRank, jhlVecSize/fieldRank /))

    !
    !
    call MPI_Barrier(MPI_COMM_WORLD, ierr)

    dist(:) = (/ myUpperRank, myLowerRank, myLowerRank, myUpperRank /)
    tag(1) = 1; tag(4) = 2
    tag(2) = 1; tag(3) = 2
    recv_tag(:) = tag(:)
    
    recv_haloCorner(:,:) = 0d0
     
    send_haloCorner(:,1) = self%val(:, iEMin, jEMin-k, lcRCID)
    send_haloCorner(:,2) = self%val(:, iEMax, jEMin-k, lcRCID)
    send_haloCorner(:,3) = self%val(:, iEMax, jEMax+k, lcRCID)
    send_haloCorner(:,4) = self%val(:, iEMin, jEMax+k, lcRCID)
    
    if ( lpRCInfo%rlevel == 0 ) then
      dist(:) = MPI_PROC_NULL
    
    else if( iEMin == glEMin ) then
      dist(:) = (/ myLeftRank, myLowerRank, myLowerRank, myRightRank /)
      send_haloCorner(:,1) = self%val(:, iEMin-k, jEMin, lcRCID)
      send_haloCorner(:,4) = self%val(:, iEMin-k, jEMax, lcRCID)
    
      if( jEMin == glEMin ) dist(1) = MPI_PROC_NULL; recv_tag(4) = 1;      
      if( jEMax == glEMax ) dist(4) = MPI_PROC_NULL; recv_tag(1) = 2;
            
    else if ( iEMax == glEMax ) then
      dist(:) = (/ myUpperRank, myLeftRank, myRightRank, myUpperRank /)
      send_haloCorner(:,2) = self%val(:, iEMax+k, jEMin, lcRCID)
      send_haloCorner(:,3) = self%val(:, iEMax+k, jEMax, lcRCID)

      if( jEMin == glEMin ) dist(2) = MPI_PROC_NULL; recv_tag(3) = 1      
      if( jEMax == glEMax ) dist(3) = MPI_PROC_NULL; recv_tag(2) = 2

    end if
    
    source(:) = dist(:)

    do ireqID=1, 4

      call MPI_iSend( &
         & send_haloCorner(:, ireqID), fieldRank, MPI_REAL8, dist(ireqID), tag(ireqID),     &
         & MPI_COMM_WORLD, ireq_send(ireqID), ierr )      
    end do

    do ireqID=1, 4
      call MPI_iRecv( &
         & recv_haloCorner(:, ireqID), fieldRank, MPI_REAL8, source(ireqID), recv_tag(ireqID),     &
         & MPI_COMM_WORLD, ireq_recv(ireqID), ierr )
    end do

    do ireqID=1,4
      call MPI_Wait(ireq_send(ireqID), send_mstatus(:,ireqID), ierr)
      call MPI_Wait(ireq_recv(ireqID), recv_mstatus(:,ireqID), ierr)
    end do

    self%val(:,iEMin-k,jEMin-k,lcRCID) = recv_haloCorner(:, 1)
    self%val(:,iEMax+k,jEMin-k,lcRCID) = recv_haloCorner(:, 2)
    self%val(:,iEMax+k,jEMax+k,lcRCID) = recv_haloCorner(:, 3)
    self%val(:,iEMin-k,jEMax+k,lcRCID) = recv_haloCorner(:, 4)
    
  end do
  
  call collect_data_near_poles_parallelMode(self)

  call MPI_Barrier(MPI_COMM_WORLD, ierr)

end subroutine paste_field2D_margin_parallelMode
#endif

!> \~japanese
!! \brief
!!
!! @param[in,out] self 構造型 Field_IcGrid2D の変数.
!!
!! \~english
!! \brief
!!
!! @param[in,out] self The variable of derived type Field_IcGrid2D.
!!
#ifdef LIB_MPI
subroutine collect_data_near_poles_parallelMode( &
  & self                                         & ! (inout)
  & )

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

  ! 作業変数
  ! Work variables
  !
  integer :: rcID
  integer :: glEMin, glEMax
  integer :: fieldRank
  
  type(LocalProcRectInfo), pointer :: lpRCInfo
  integer :: lcRCID, mylcRC_i, mylcRC_j
  real(DP), allocatable :: global_dataNearPole(:), local_dataNearPole(:)
  
  integer :: ierr
  
  ! 実行文 ; Executable statement
  !
  glEMin = get_EffSize_Min(self%icgrid)
  glEMax = get_EffSize_Max(self%icgrid)
  fieldRank = get_field2D_rank(self)

  lpRCInfo => get_LocalProcRectInfo(self%icgrid)

  call calc_LcProcRC_IndexInfo( &
    & lpRCInfo%lcproc_MPIRank, lpRCInfo%rlevel, & ! (in)
    & lcRCID, mylcRC_i, mylcRC_j              &   ! (out)
    & )

  allocate(local_dataNearPole(fieldRank*2*2))  
  allocate(global_dataNearPole(fieldRank*2*2*5))
  
  if ( lcRCID <= 5 .and. mylcRC_i == 1 .and. mylcRC_j == 1 ) then
    local_dataNearPole(:) = reshape(self%val(1:fieldRank, glEMin:glEMin+1, glEMin:glEMin+1, lcRCID), &
                                &    shape(local_dataNearPole) )
     
    call MPI_AllGather(local_dataNearPole, size(local_dataNearPole), MPI_REAL8,  & 
      &                global_dataNearPole, size(local_dataNearPole), MPI_REAL8, & 
      &                northPole_comm, ierr )
                                
    self%dataNearNPole(:,:,:,:) = reshape(global_dataNearPole, (/ fieldRank, 2, 2, 5 /) )
    
  else if ( lcRCID > 5 .and. mylcRC_i == 2**lpRCInfo%rlevel .and. mylcRC_j == 2**lpRCInfo%rlevel ) then

    local_dataNearPole(:) = reshape(self%val(1:fieldRank, glEMax-1:glEMax, glEMax-1:glEMax, lcRCID), &
                                &    shape(local_dataNearPole) )
     
    call MPI_AllGather(local_dataNearPole, size(local_dataNearPole), MPI_REAL8, & 
      &                global_dataNearPole, size(local_dataNearPole), MPI_REAL8, southPole_comm, ierr )
    
    self%dataNearSPole(:,:,:,:) = reshape(global_dataNearPole, (/ fieldRank, 2, 2, 5 /) )
     
  else
  
    return
    
  end if
  

end subroutine collect_data_near_poles_parallelMode
#endif

end module Field_IcGrid2D_Manager
