!> \~japanese
!! \brief 正二十面体格子上の物理量データを NetCDF ファイルから読み込むための情報を管理する構造体および手続きを提供するモジュール.
!!
!! \par IcGrid2D_FVM_Manager モジュールについて
!! - 読み込む NetCDF ファイルは, IcGrid_ncWriter モジュールを用いて書き出された NetCDF ファイルでなければならない.
!!
!! \~english
!! \brief This module provides the derived type to manage the information nesessary for reading the physical field data on icosahedral grid
!! from NetCDF file, and some subroutines to read the grid and field data.
!!
!! \par About IcGrid2D_FVM_Manager module
!! - The NetCDF file which you would like to read must be written into by IcGrid_ncWriter module.
!!
!! \~
!!
!! <br><br>
!! \b Copyright (C) GFD Dennou Club, 2011-2012. All rights reserved. <br>
!! \b license ?? <br>
!! \author Yuta Kawai
!!
!!
module IcGrid_ncReader_mod

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

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

  !* gtool *********************
  !*

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

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

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

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

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

  ! 3 次元正二十面格子データの管理
  ! Manager of 3D icosahedral grid data
  !
  use IcGrid3D_FVM_Manager, only: &
    & IcGrid3D_FVM, &
    & get_icgrid2D, get_vertical_level_num, &
    & set_IcGrid3D_VerticalCoord_name, &
    & set_IcGrid3D_VerticalCoord_long_name, &
    & set_IcGrid3D_VerticalCoord_units

  ! 正二十面体格子上の物理場データの管理
  ! Manager of the physical field data on icosahedral grid
  !
  use Field_IcGrid2D_Manager, only: &
    & Field_IcGrid2D, paste_field2D_margin, &
    & set_long_field2D_name, set_field2D_units

  ! 3 次元正二十面体格子上の物理場データの管理
  ! Manager of the physical field data on 3D icosahedral grid
  !
  use Field_IcGrid3D_Manager, only: &
    & Field_IcGrid3D, &
    & paste_field3D_margin, &
    & set_long_field3D_name, set_field3D_units, &
    & get_field_layer

  ! 非構造格子データモデルのための構造型の提供
  ! Providing the derived types for unstructured grid data model
  !
  use IcGrid_ncStream_helper, only: &
    & Mesh2_ncInfo, Mesh2_ncInfo_Init, &
    & Mesh3_ncInfo, Mesh3_ncInfo_Init, &
    & Mesh_dim_element, &
    & Mesh_coord_element, Mesh_coord_element_Init, &
    & check_nf90_status

  ! NetCDF
  !
  !
  use netcdf

  ! 宣言文 ; Declaration statements
  !
  implicit none
  private

  !
  !> \~japanese
  !! \brief IcGrid_ncWriter_mod モジュールを用いて書き出された netCDF 形式の正二十面体格子上の物理量データを読み込むための
  !! 情報を管理するための構造型.
  !!
  !! \~english
  !! \brief IcGrid_ncWriter_mod モジュールを用いて書き出された netCDF 形式の正二十面体格子上の物理量データを読み込むための
  !! 情報を管理するための構造型.
  !!
  !!
  type, public :: IcGrid_ncReader
    ! 公開メンバー変数
    ! Public member variable
    !

    !> \~japanese 開いた NetCDF ファイルと関連付けられる ID.
    !! \~english The ID associated with an open NetCDF file.
    integer :: ncID

    !> \~japanese
    !! \~english
    type(Mesh2_ncInfo) :: mesh2Info

    !> \~japanese
    !! \~english
    type(Mesh3_ncInfo) :: mesh3Info

    !> \~japanese 構造体 IcGrid2D_FVM の変数を指すポインタ.
    !! \~english The pointer to the variable of derived type IcGrid2D_FVM.
    type(IcGrid2D_FVM), pointer :: icgrid

    !> \~japanese 構造体 IcGrid3D_FVM の変数を指すポインタ.
    !! \~japanese The pointer to the variable of derived type IcGrid3D_FVM.
    type(IcGrid3D_FVM), pointer :: icgrid3D => null()

    !> \~japanese NetCDF ファイルから読み込まれる物理場データの種類.
    !! \~english　Kind of physical field data read from a NetCDF file.
    integer :: readFieldDataType = -1

    !> \~japanese NetCDF ファイルが閉じられているかを示すフラグ.
    !! \~english  A flag which specifies whether to close or open a NetCDF file.
    logical :: closed_flag = .false.

    !> \~japanese (NetCDF の) Parallel IO を利用するかのフラグ.
    !! \~english  A Flag which specifies whether to use Parallel IO with NetCDF.
    logical :: parallel_IO_flag = .false.

  end type IcGrid_ncReader

  ! 公開手続き
  ! Public procedures
  !
  public :: IcGrid_ncReader_Init, IcGrid_ncReader_Final

  public :: open_ncFile, close_ncFile, &
    &       read_Simulation_Parameter, read_GridData, read_FieldData

  ! interface 文; interface statements
  !
  !
  interface IcGrid_ncReader_Init
    module procedure IcGrid2D_ncReader_Init
    module procedure IcGrid3D_ncReader_Init
  end interface IcGrid_ncReader_Init

  interface read_FieldData
    module procedure read_Field2D_Data
    module procedure read_Field3D_Data
  end interface read_FieldData

  ! 非公開変数
  ! Private variables
  !

  ! \~japanese 2 次元正二十面格子上の物理場データを読み込む状態にあることを表す定数.
  ! \~english
  integer, public, parameter :: ICGRID2D_MODE = 1

  ! \~japanese 3 次元正二十面格子上の物理場データを読み込む状態にあることを表す定数.
  ! \~english
  integer, public, parameter :: ICGRID3D_MODE = 2

contains

!
!> \~japanese
!! \brief 構造型 IcGrid_ncReader の変数を初期化する.
!!
!! このサブルーチンを呼ぶ前に, 第二引数の ref_icgrid は, initialize_IcGrid2D_FVM によって初期化されていなければならない.
!!
!! @param[in,out] self 構造型 IcGrid_ncReader の変数.
!! @param[in]     ref_icgrid 構造型 IcGrid2D_FVM の変数.
!!
!! \~english
!! \brief Initializes the variable of derived type IcGrid_ncReader.
!!
!! The second argument ref_icgrid must be initialized by init_IcGrid2D_FVM before calling this subroutine.
!!
!! @param[in,out] self The variable of derived IcGrid_ncReader.
!! @param[in]     ref_icgrid The variable of derived type IcGrid2D_FVM.
!!
subroutine IcGrid2D_ncReader_Init( &
  & self,                          &  ! (inout)
  & ref_icgrid, parallel_IO_flag   &  ! (in)
  & )

  ! 宣言文 ; Declaration statements
  !

  type(IcGrid_ncReader), intent(inout) :: self
  type(IcGrid2D_FVM), intent(in), target :: ref_icgrid
  logical, optional :: parallel_IO_flag

  ! 作業変数
  ! Work variables
  !

  ! 実行文 ; Executable statements
  !

  self%icgrid => ref_icgrid

  ! 構造型 Mesh2_Info の変数を初期化する.
  call Mesh2_ncInfo_Init(self%mesh2Info, self%icgrid)

  ! NetCDF の Parallel IO を利用するかを調べる.
  if ( present(parallel_IO_flag) .and. parallel_IO_flag ) then
    self%parallel_IO_flag = .true.
  else
    self%parallel_IO_flag = .false.
  end if

  ! 正二十面格子上の物理場データの読み込みモードを決定する.
  if ( associated(self%icgrid3D) ) then
    self%readFieldDataType = ICGRID3D_MODE
  else
    self%readFieldDataType = ICGRID2D_MODE
  end if

end subroutine IcGrid2D_ncReader_Init

subroutine IcGrid3D_ncReader_Init( &
  & self,                            &   ! (inout)
  & ref_icgrid3D, parallel_IO_flag   &   ! (in)
  & )

  ! 宣言文 ; Declaration statements
  !

  type(IcGrid_ncReader), intent(inout) :: self
  type(IcGrid3D_FVM), intent(in), target :: ref_icgrid3D
  logical, optional :: parallel_IO_flag

  ! 作業変数
  ! Work variables
  !
  logical :: parallelIOFlag = .false.

  ! 実行文 ; Executable statements
  !

  self%icgrid3D => ref_icgrid3D

  ! 構造型 Mesh3_Info の変数を初期化する.
  call Mesh3_ncInfo_Init(self%mesh3Info, self%icgrid3D)

  if ( present(parallel_IO_flag) .and. parallel_IO_flag ) parallelIOFlag = .true.

  call IcGrid2D_ncReader_Init(self, get_icgrid2D(ref_icgrid3D), parallel_IO_flag=parallelIOFlag)

end subroutine IcGrid3D_ncReader_Init

!
!> \~japanese
!! \brief 構造型 IcGrid_ncReader の変数を最終化する.
!!
!! 事前に NetCDF ファイルを開いていてかつ close_ncfile() を呼び出していない場合には, この最終化の際に呼び出される.
!!
!! @param[in,out] self 構造型 IcGrid_ncReader の変数.
!!
!! \~english
!! \brief Finalizes the variable of derived type IcGrid_ncReader.
!!
!! If a NetCDF file is opened and close_ncfile() is not called in advance,
!! this finalization will call close_ncfile().
!!
!! @param[in,out] self The variable of derived type IcGrid_ncReader.
!!
subroutine IcGrid_ncReader_Final( &
  & self                          & ! (inout)
  & )

  ! 宣言文 ; Declaration statements
  !

  type(IcGrid_ncReader), intent(inout) :: self

  ! 作業変数
  ! Work variables
  !

  ! 実行文 ; Executable statements
  !

  if ( .not. self%closed_flag ) then
    call close_ncFile(self)
  end if

end subroutine IcGrid_ncReader_Final

!
!> \~japanese
!! \brief 指定された NetCDF ファイルを開く.
!!
!! このとき, netCDF ファイル内で定義されている次元情報を読み込み,
!! Mesh2_ncInfo クラスのメンバー変数に格納しておく.
!!
!! @param[in,out] self 構造型 IcGrid_ncReader の変数.
!! @param[in]     file_name NetCDF ファイルの名前.
!!
!! \~english
!! \brief Opens the specified NetCDF file.
!!
!! このとき, netCDF ファイル内で定義されている次元情報を読み込み,
!! Mesh2_ncInfo クラスのメンバー変数に格納しておく.
!!
!! @param[in,out] self The variable of derived type IcGrid_ncReader.
!! @param[in]     file_name The name of NetCDF file.
!!
subroutine open_ncFile( &
  & self,               & ! (inout)
  & file_name )           ! (in)

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

  ! 実行文 ; Executable statement
  !

  if ( self%parallel_IO_flag ) then

    ! netCDF ファイルを作成する(パラレルモード).
    call check_nf90_status( &
      & nf90_open_par(file_name, IOR(NF90_NOWRITE, NF90_MPIIO), MPI_COMM_WORLD, MPI_INFO_NULL, self%ncID),  &
      & message='Open NetCDF file `' // trim(file_name) //  '` (Parallel IO Mode)' )

  else

    ! netCDF ファイルを作成する(シリアルモード).
    call check_nf90_status( &
      & nf90_open(file_name, NF90_NOWRITE, self%ncID),  &
      & message='Open NetCDF file `' // trim(file_name) //  '`' )

  end if

  if( self%readFieldDataType == ICGRID2D_MODE ) then

    ! Mesh2 の次元要素である node (節) に対応する次元 ID を netCDF ファイルから取得する.
    call check_nf90_status( &
      & nf90_inq_dimid(self%ncID, self%mesh2Info%node%element_name, self%mesh2Info%node%dimID),   &
      & message='Read dimension information from NetCDF file' )

    ! Mesh2 の次元要素である node (節) の総数を netCDF ファイルから取得する.
    call check_nf90_status( &
      & nf90_inquire_dimension(self%ncID, self%mesh2Info%node%dimID, len=self%mesh2Info%node%num), &
      & message='Read the number of node from NetCDF file' )

  else if ( self%readFieldDataType == ICGRID3D_MODE ) then

    ! Mesh3 の次元要素である node (節) に対応する次元 ID を netCDF ファイルから取得する.
    call check_nf90_status( &
      & nf90_inq_dimid(self%ncID, self%mesh3Info%node%element_name, self%mesh3Info%node%dimID), &
      & message='Read horizontal dimension information from NetCDF file' )

    ! Mesh3 の次元要素である node (節) の総数を netCDF ファイルから取得する.
    call check_nf90_status( &
      & nf90_inquire_dimension(self%ncID, self%mesh3Info%node%dimID, len=self%mesh3Info%node%num), &
      & message='Read the number of node from NetCDF file' )

    ! Mesh3 の次元要素である vlayers に対応する次元 ID を netCDF ファイルから取得する.
    call check_nf90_status( &
      & nf90_inq_dimid(self%ncID, self%mesh3Info%vlayers%element_name, self%mesh3Info%vlayers%dimID), &
      & message='read vertical dimension information from NetCDF file' )

    ! Mesh3 の次元要素である vlayers の総数を netCDF ファイルから取得する.
    call check_nf90_status( &
      & nf90_inquire_dimension(self%ncID, self%mesh3Info%vlayers%dimID, len=self%mesh3Info%vlayers%num), &
      & message='Read the number of node from NetCDF file' )

  end if

end subroutine open_ncFile

!
!> \~japanese
!! \brief NetCDF ファイルから, シミュレーションパラメタを読み込む.
!!
!! \param[inout] self 構造型 IcGrid_ncReader の変数.
!! \param[out] integration_time 積分時間 [sec].
!! \param[out] time_step 時間ステップ [sec].
!! \param[out] output_tick 出力の時間間隔 [sec].
!!
!! \~english
!! \brief Reads the simulation parameters from NetCDF file.
!!
!! \param[inout] self The variable of derived type IcGrid_ncReader.
!! \param[out] integration_time Integration time [sec].
!! \param[out] time_step Time step[sec].
!! \param[out] output_tick Output time interval [sec].
!!
subroutine read_Simulation_Parameter( &
  & self,                                    &  ! (inout)
  & integration_time, time_step, output_tick &  ! (in)
  & )

  ! 宣言文 ; Declaration statements
  !
  type(IcGrid_ncReader), intent(inout) :: self
  real(DP), intent(out) :: integration_time
  real(DP), intent(out) :: time_step
  real(DP), intent(out) :: output_tick

  ! 実行文 ; Executable statements
  !

  ! 各シミュレーションパラメータのデータを, NetCDF のグローバル領域から読み込む.
  call check_nf90_status( &
    & nf90_get_att(self%ncID, NF90_GLOBAL, 'integration_time', integration_time) &
    & )

  call check_nf90_status( &
    & nf90_get_att(self%ncID, NF90_GLOBAL, 'time_step', time_step) &
    & )

  call check_nf90_status( &
    & nf90_get_att(self%ncID, NF90_GLOBAL, 'output_tick', output_tick) &
    & )


end subroutine read_Simulation_Parameter

!
!> \~japanese
!! \brief NetCDF ファイルから正二十面格子の格子データを読み込む.
!!
!! @param[in,out] self 構造型 IcGrid_ncReader の変数.
!!
!! \~english
!! \brief Reads icosahedral grid data from NetCDF file.
!!
!! @param[in,out] self The variable of derived type IcGrid_ncReader.
!!
subroutine read_GridData( &
  & self )                  ! (inout)

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

  ! 座標変換
  ! Coordinate conversion
  !
  use igmcore_coordinate_conversion, only: &
    & geo_to_orth_pos

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

  ! 作業変数
  ! Work variable
  !
  real(DP), allocatable :: s_rcs_node_x(:), s_rcs_node_y(:)
  real(DP), allocatable :: vertical_layers(:)
  integer :: rcs_AGrid_shape(3)
  integer :: rcID, i,j
  real(DP) :: radius
  integer :: glEMax, glEMin, EffSize

  ! 実行文 ; Executable statements
  !

  ! 正二十面格子の矩形領域の配列のインデックス情報を取得する.
  glEMin = get_EffSize_Min(self%icgrid)
  glEMax = get_EffSize_Max(self%icgrid)
  EffSize = glEMax - glEMin + 1

  ! 緯度経度座標データのままひとまず構造体 IcGrid2D_FVM の変数に保存する.
  !

  if ( self%readFieldDataType == ICGRID2D_MODE ) then

    allocate( s_rcs_node_x(self%Mesh2Info%node%num) )
    allocate( s_rcs_node_y(self%Mesh2Info%node%num) )

    ! Mesh2_node_x, Mesh2_node_y の変数 ID を取得する.
    call check_nf90_status( &
      & nf90_inq_varid(self%ncID, self%mesh2Info%node_x%element_name, self%mesh2Info%node_x%varID) &
      & )
    call check_nf90_status( &
      & nf90_inq_varid(self%ncID, self%mesh2Info%node_y%element_name, self%mesh2Info%node_y%varID) &
      & )

    ! 格子点の経度・緯度についての情報を保持している Mesh2_node_x, Mesh2_node_y のデータを読み込み,
    ! 作業用の 1 次元配列に取り込む

    call check_nf90_status( &
      & nf90_get_var(self%ncID, self%mesh2Info%node_x%varID, s_rcs_node_x) &
      & )

    call read_mesh_coordinate_att(self, self%mesh2Info%node_x)

    call check_nf90_status( &
      & nf90_get_var(self%ncID, self%mesh2Info%node_y%varID, s_rcs_node_y) &
      & )

    call read_mesh_coordinate_att(self, self%mesh2Info%node_y)

  else if ( self%readFieldDataType == ICGRID3D_MODE ) then

    allocate( s_rcs_node_x(self%Mesh3Info%node%num) )
    allocate( s_rcs_node_y(self%Mesh3Info%node%num) )
    allocate( vertical_layers(self%Mesh3Info%vlayers%num) )

    ! Mesh3_node_x, Mesh3_node_y, Mesh3_layers の変数 ID を取得する.
    call check_nf90_status( &
      & nf90_inq_varid(self%ncID, self%mesh3Info%node_x%element_name, self%mesh3Info%node_x%varID) &
      & )
    call check_nf90_status( &
      & nf90_inq_varid(self%ncID, self%mesh3Info%node_y%element_name, self%mesh3Info%node_y%varID) &
      & )
    call check_nf90_status( &
      & nf90_inq_varid(self%ncID, self%mesh3Info%layers%element_name, self%mesh3Info%layers%varID) &
      & )

    ! 水平格子点の経度・緯度についての情報を保持している Mesh3_node_x, Mesh3_node_y のデータを読み込み,
    ! 作業用の 1 次元配列に取り込む
    call check_nf90_status( &
      & nf90_get_var(self%ncID, self%mesh3Info%node_x%varID, s_rcs_node_x) &
      & )
    call read_mesh_coordinate_att(self, self%mesh3Info%node_x)

    call check_nf90_status( &
      & nf90_get_var(self%ncID, self%mesh3Info%node_y%varID, s_rcs_node_y) &
      & )
    call read_mesh_coordinate_att(self, self%mesh3Info%node_y)

    ! 鉛直格子点の高度についての情報を保持している Mesh3_layers のデータを読み込み,
    ! 作業用の 1 次配列に取り込む.
    call check_nf90_status( &
      & nf90_get_var(self%ncID, self%mesh3Info%layers%varID, vertical_layers) &
      & )

    call read_mesh_coordinate_att(self, self%mesh3Info%layers)
    
    call set_IcGrid3D_VerticalCoord_name(self%icgrid3D, self%mesh3Info%layers%standard_name)
    call set_IcGrid3D_VerticalCoord_long_name(self%icgrid3D, self%mesh3Info%layers%long_name)
    call set_IcGrid3D_VerticalCoord_units(self%icgrid3D, self%mesh3Info%layers%units)
    
  end if

  ! 1 次元配列に格納した水平格子の経度・緯度の座標データを, 構造体 IcGrid2D_FVM の成分 rcs_AGrid に格納する.
  ! この際, 読み込んだ配列データの形状を構造型 IcGrid2D_FVM の成分の配列 rcs_AGrid の形状へ変換する.
  radius = get_IcRadius(self%icgrid)
  rcs_AGrid_shape = shape(self%icgrid%rcs_AGrid(1,glEMin:glEMax,glEMin:glEMax,1:RC_REGIONS_NUM))
  self%icgrid%rcs_AGrid(1,glEMin:glEMax,glEMin:glEMax,:) = reshape( s_rcs_node_x, rcs_AGrid_shape )
  self%icgrid%rcs_AGrid(2,glEMin:glEMax,glEMin:glEMax,:) = reshape( s_rcs_node_y, rcs_AGrid_shape )
  self%icgrid%rcs_AGrid(3,glEMin:glEMax,glEMin:glEMax,:) = radius

  ! rcs_AGrid に格納した格子位置に関する緯度経度座標データを, デカルト座標データに変換する.
  !

  do rcID=1, RC_REGIONS_NUM
    do j=glEMin, glEMax
      do i=glEMin, glEMax
        !
        if ( rcID <= 5 .and. i==glEMin .and. j==glEMin ) then
          self%icgrid%rcs_AGrid(:, i,j,rcID) = (/ 0.0d0, 0.0d0, radius /)

        else if( rcID > 5 .and. i==glEMax .and. j==glEMax ) then
          self%icgrid%rcs_AGrid(:, i,j,rcID) = (/ 0.0d0, 0.0d0, -radius /)

        else
          self%icgrid%rcs_AGrid(:, i,j,rcID) = geo_to_orth_pos( self%icgrid%rcs_AGrid(:,i,j,rcID) )
        end if

      end do
    end do
  end do

  ! ハロー領域の物理場データを貼り合わせる.
  call paste_margin_width(self%icgrid)

  ! IcGrid3D モードの場合には,
  ! IcGrid3D_FVM の成分に鉛直格子データを格納する.
  !
  if ( self%readFieldDataType == ICGRID3D_MODE ) then
    self%icgrid3D%verticalGrid(:) = vertical_layers(:)
  end if

end subroutine read_GridData

!
!> \~japanese
!! \brief 指定された時間レベルの物理場データを NetCDF ファイルから読み込む.
!!
!! @param[in]    self   構造型 IcGrid_ncReader の変数.
!! @param[in]    name   物理場の名前.
!! @param[in,out] field 物理場データを保存する構造型 Field_IcGrid2D の変数.
!! @param[in]    time_level タイムレベル.
!!
!! \~english
!! \brief Reads the physical field data at specified time level time_level from NetCDF file.
!!
!! @param[in]    self The variable of derived type IcGrid_ncReader.
!! @param[in]    name The name of the physical field.
!! @param[in,out] field  The variable of derived type Field_IcGrid2D in which the physical field data stored.
!! @param[in]    time_level Time level.
!!
subroutine read_Field2D_Data(&
  & self,                 & ! (in)
  & name,                 & ! (in)
  & field,                & ! (inout)
  & time_level )            ! (in)

  ! 宣言文 ; Declaration statements
  !
  type(IcGrid_ncReader), intent(in) :: self
  character(*), intent(in) :: name
  type(Field_IcGrid2D), intent(inout) :: field
  integer, intent(in) :: time_level

  ! 作業変数
  ! Work variables
  !
  integer :: field_shape(3)
  integer :: varID

  integer :: glEMax, glEMin, glEffSize
  integer :: iEMin, iEMax, jEMin, jEMax, rcIDMin, rcIDMax
  type(LocalProcRectInfo), pointer :: lcRCInfo
  integer :: myrcID, mylcpcRC_i, mylcpcRC_j

  character(STRING) :: long_name
  character(TOKEN) :: units

  integer :: startSeek
  integer :: dataNum

  ! 実行文 ; Executable statement
  !

  ! 読み出す物理場変数に対応する NetCDF における変数 ID を取得する.
  call check_nf90_status( &
    & nf90_inq_varid(self%ncID, name, varID), &
    & message='inqure variable ID: ' //  trim(name) )

  ! 時間レベル time_level の一次元配列化された空間二次元の物理場データを読み込み,
  ! Field_IcGrid2D の成分 val に取り込む.
  call read_myLocalProcRC_fieldData(self, field, varID, time_level)

  ! 物理場の長い名前を取得する.
  call check_nf90_status( &
    & nf90_get_att(self%ncID, varID, "long_name", long_name), &
    & message = 'get attribute: long_name' )

  ! 物理場の単位を取得する.
  call check_nf90_status( &
    & nf90_get_att(self%ncID, varID, "units", units), &
    & message = 'get attribute: units' )

  ! 読み込んだ物理場の長い名前・単位を Field_IcGrid2D の変数に設定する.
  call set_long_field2D_name(field, long_name)
  call set_field2D_units(field, units)

  ! 各矩形領域の袖領域部分を隣の矩形領域端のデータで埋める.
  call paste_field2D_margin(field)

end subroutine read_Field2D_Data

!
!> \~japanese
!! \brief 指定された時間レベルの物理場データを NetCDF ファイルから読み込む.
!!
!! @param[in]    self   構造型 IcGrid_ncReader の変数.
!! @param[in]    name   物理場の名前.
!! @param[in,out] field 物理場データを保存する構造型 Field_IcGrid2D の変数.
!! @param[in]    time_level タイムレベル.
!!
!! \~english
!! \brief Reads the physical field data at specified time_level from NetCDF file.
!!
!! @param[in]    self The variable of derived type IcGrid_ncReader.
!! @param[in]    name The name of the physical field.
!! @param[in,out] field  The variable of derived type Field_IcGrid2D in which the physical field data stored.
!! @param[in]    time_level Time level.
!!
subroutine read_Field3D_Data(&
  & self,                 & ! (in)
  & name,                 & ! (in)
  & field3D,              & ! (inout)
  & time_level )            ! (in)

  ! 宣言文 ; Declaration statements
  !
  type(IcGrid_ncReader), intent(in) :: self
  character(*), intent(in) :: name
  type(Field_IcGrid3D), intent(inout) :: field3D
  integer, intent(in) :: time_level

  ! 作業変数
  ! Work variables
  !
  integer :: field2D_shape(3)
  integer :: varID

  integer :: glEMax, glEMin
  integer :: k
  integer :: verticalLv_num
  real(DP), allocatable :: field_temp(:,:)
  character(STRING) :: long_name
  character(TOKEN) :: units

  type(Field_IcGrid2D), pointer :: field_k

  ! 実行文 ; Executable statement
  !

  ! 正二十面格子の矩形領域の配列のインデックス情報を取得する.
  glEMin = get_EffSize_Min(self%icgrid)
  glEMax = get_EffSize_Max(self%icgrid)
  verticalLv_num = get_vertical_level_num(self%icgrid3D)

  allocate( field_temp(self%Mesh3Info%node%num, verticalLv_num) )

  call check_nf90_status( &
    & nf90_inq_varid(self%ncID, name, varID), &
    & message='inqure variable ID: ' //  trim(name) )

  ! 時間レベル time_level の一次元配列化された空間三次元の物理場データを読み込み, 作業配列に取り込む.
  call check_nf90_status( &
    & nf90_get_var(self%ncID, varID, field_temp, &
    & start=(/ 1, 1, time_level /), &
    & count=(/ self%Mesh3Info%node%num, self%Mesh3Info%vlayers%num, 1 /) ), &
    & message='get variable: ' // trim(name) )

  ! 物理場の長い名前を取得する.
  call check_nf90_status( &
    & nf90_get_att(self%ncID, varID, "long_name", long_name), &
    & message = 'get attribute: long_name' )

  ! 物理場の単位を取得する.
  call check_nf90_status( &
    & nf90_get_att(self%ncID, varID, "units", units), &
    & message = 'get attribute: units' )

  ! 2 次元配列に格納されている空間 3 次元物理場データを, Field_IcGrid3D の成分 val に格納する.
  !

  field2D_shape = shape(self%icgrid%rcs_AGrid(1,glEMin:glEMax,glEMin:glEMax,:))

  do k=1, verticalLv_num
    field_k => get_field_layer(field3D, k)
    field_k%val(1,glEMin:glEMax,glEMin:glEMax,:) = reshape( field_temp(:, k), field2D_shape )
  end do

  ! 取得した物理場データのメタ情報を Field_IcGrid3D オブジェクトに設定する.
  call set_long_field3D_name(field3D, long_name)
  call set_field3D_units(field3D, units)

  ! 各矩形領域の袖領域部分を隣の矩形領域端のデータで埋める.
  call paste_field3D_margin(field3D)

end subroutine read_Field3D_Data

!
!> \~japanese
!! \brief NetCDF ファイルを閉じる.
!!
!! @param[inout] self 構造型 IcGrid_ncReader の変数.
!!
!! \~english
!! \brief Closes the NetCDF file.
!!
!! @param[inout] self The variable of derived type IcGrid_ncReader.
!!
subroutine close_ncFile(self)

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

  ! 実行文 ; Executable statement
  !

  ! NetCDF ファイルを閉じる.
  call check_nf90_status( nf90_close(self%ncID) )
  self%closed_flag = .true.

end subroutine close_ncFile

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

subroutine read_myLocalProcRC_fieldData( &
  & self,                 &  ! (in)
  & field2D,              &  ! (inout)
  & varID, time_level     &  ! (in)
  & )

  ! 宣言文 ; Declaration statements
  !
  type(IcGrid_ncReader), intent(in) :: self
  type(Field_IcGrid2D), intent(inout) :: field2D
  integer, intent(in) :: varID
  integer, intent(in) :: time_level

  ! 作業変数
  ! Work variables
  !
  integer :: glEMax, glEMin, glEffSize
  integer :: iEMin, iEMax, jEMin, jEMax, rcIDMin, rcIDMax
  type(LocalProcRectInfo), pointer :: lcRCInfo
  integer :: myrcID, mylcpcRC_i, mylcpcRC_j

  real(DP), allocatable :: send_field_temp(:)
  real(DP), allocatable :: send_field_data(:,:)
  real(DP), allocatable :: field_temp(:)

  integer :: startSeek
  integer :: dataNum, sendDataIDSize

  integer :: i, j
  integer :: chunk_size, i_chunk_size, i_chunk_size_mod
  integer :: sIdStart, sIdEnd, stIdStart, stIdEnd
  integer :: dest, source, recv_status(MPI_STATUS_SIZE), ierr

  ! 実行文 ; Executable statement
  !

  ! 正二十面格子の局所矩形領域のインデックス情報を取得する.
  glEMin = get_EffSize_Min(self%icgrid)
  glEMax = get_EffSize_Max(self%icgrid)

  lcRCInfo => get_LocalProcRectInfo(self%icgrid)

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

  call  calc_LcProcRC_IndexInfo( &
      & lcRCInfo%lcproc_MPIRank, lcRCInfo%rlevel, &
      & myrcID, mylcpcRC_i, mylcpcRC_j     &
       & )

  if ( mylcpcRC_i == 1 ) then

     ! 取得した局所矩形領域のインデックス情報を使って,
    ! NetCDF ファイルからデータを読み込む際の, シークの位置を計算する.
    glEffSize = glEMax - glEMin + 1
    startSeek = ( ( rcIDMin - 1 ) * glEffSize + ( jEMin - glEMin ) ) * glEffSize + 1

    if ( lcRCInfo%mpi_flag ) then
      dataNum = glEffSize * ( jEMax - jEMin + 1 )
    else
      dataNum = glEffSize**2 * RC_REGIONS_NUM
    end if

    ! 一次元配列化された二次元物理場データを NetCDF から読み込むときに,
    ! 一時的にそのデータを格納するための作業配列のメモリを確保する.
    allocate( send_field_temp(dataNum) )

   ! 時間レベル time_level の一次元配列化された空間二次元の物理場データを読み込み, 作業配列に取り込む.
    call check_nf90_status( &
      & nf90_get_var(self%ncID, varID, send_field_temp, &
         & start=(/ startSeek, time_level /), count=(/ dataNum, 1 /) ), &
     & message='get variable' )

  end if


  if ( lcRCInfo%mpi_flag .and. lcRCInfo%rlevel > 0 ) then

    i_chunk_size = int(( glEMax - glEMin + 1 ) / 2**lcRCInfo%rlevel) + 1
    i_chunk_size_mod = ( glEMax - glEMin + 1 ) - ( 2**lcRCInfo%rlevel - 1 ) * i_chunk_size
    allocate(field_temp((jEMax-jEMin+1)*(iEMax-iEMin+1)))

    if ( mylcpcRC_i == 1 ) then

      allocate(send_field_data(i_chunk_size * ( jEMax - jEMin + 1), 1:2**lcRCInfo%rlevel))

      do j=jEMin, jEMax
        chunk_size = i_chunk_size

        do i=1, 2**lcRCInfo%rlevel

          if ( i == 2**lcRCInfo%rlevel ) chunk_size = i_chunk_size_mod

          sIdStart = ( j - jEMin ) * chunk_size + 1
          sIdEnd = sIdStart + chunk_size - 1
          stIdStart = ( j - jEMin ) * glEffSize + ( i - 1 ) * i_chunk_size + 1
          stIdEnd = stIdStart + chunk_size - 1

          send_field_data(sIdStart:sIdEnd, i) = send_field_temp(stIdStart:stIdEnd)
        end do
      end do

      sendDataIDSize = i_chunk_size * ( jEMax - jEMin + 1)
      field_temp(:) = send_field_data(1:sendDataIDSize, 1)

      do i=2, 2**lcRCInfo%rlevel

        if( i == 2**lcRCInfo%rlevel ) sendDataIDSize = i_chunk_size_mod * ( jEMax - jEMin + 1 )
        dest = calc_LcProcRC_MPIRank(lcRCInfo%rlevel, myrcID, i, mylcpcRC_j)

        call MPI_send(send_field_data(1:sendDataIDSize, i), sendDataIDSize, MPI_REAL8, dest, 1, &
          &           MPI_COMM_WORLD, ierr)
      end do

    else
      source = calc_LcProcRC_MPIRank(lcRCInfo%rlevel, myrcID, 1, mylcpcRC_j)

      call MPI_Recv(field_temp(:), size(field_temp), MPI_REAL8, source, 1, &
          &           MPI_COMM_WORLD, recv_status, ierr)

    end if

  else
    allocate(field_temp(size(send_field_temp)))
    field_temp(:) = send_field_temp(:)
  end if

  field2D%val(1, iEMin:iEMax, jEMin:jEMax, rcIDMin:rcIDMax) = &
    & reshape(field_temp, (/ iEMax-iEMin+1, jEMax-jEMin+1, rcIDMax-rcIDMin+1 /))

end subroutine read_myLocalProcRC_fieldData

!
!> \~japanese
!! \brief NetCDF ファイルのヘッダー部分に, 座標情報を定義する.
!!
!! @param[in]     self                構造型 IcGrid_ncWriter の変数.
!! @param[in,out] coordinate_element 座標情報を保持する構造型 Mesh_dim_element の変数.
!!
!! \~english
!! \brief  Defines the informaion about the coordinate in the header part of the NetCDF file.
!!
!! @param[in]     self                The variable of derived type IcGrid_ncWriter.
!! @param[in,out] coordinate_element The variable of derived type Mesh_dim_element containing the informaion about the coordinate.
!!
subroutine read_mesh_coordinate_att(self, coordinate_element)

  ! 宣言文 ; Declaration statements
  !
  type(IcGrid_ncReader), intent(inout) :: self
  type(Mesh_coord_element), intent(inout) :: coordinate_element

  ! 作業変数
  ! Work variables
  !

  ! 実行文 ; Executable statements
  !

  ! 座標変数の属性を読み込む. 
  !
  
  call check_nf90_status( &
    & nf90_get_att(self%ncID, coordinate_element%varID, "name", coordinate_element%standard_name), &
    & message = 'get attribute: standard name' )

  call check_nf90_status( &
    & nf90_get_att(self%ncID, coordinate_element%varID, "long_name", coordinate_element%long_name), &
    & message = 'get attribute: long name' )
      
  call check_nf90_status( &
    & nf90_get_att(self%ncID, coordinate_element%varID, "units", coordinate_element%units), &
    & message = 'get attribute: units' )


end subroutine read_mesh_coordinate_att

end module IcGrid_ncReader_mod
