!> \~japanese
!! \brief 正二十面体格子上の物理場データを NetCDF ファイルに書き出すための手続きとその情報を管理するための構造型を提供するモジュール.
!!
!! \par IcGrid_ncWriter_mod モジュールについて
!! - このモジュールの手続きを使って書き出された NetCDF ファイルから格子点座標データや物理場データを読み込む際は,
!!   IcGrid_ncReader モジュールを用いなければならない.
!!
!! \~english
!! \brief This module provides the derived type to manage the information nesessary for writing the physical field data on icosahedral grid
!! to NetCDF file, and some subroutines to write the grid and field data.
!!
!! \par About IcGrid_ncWriter_mod module
!! - If you want to read grid vertex and physical field data from the NetCDF file written by some procedures in this module,
!!   IcGrid_ncReader module must be used.
!!
!! \~
!!
!! <br><br>
!! \b Copyright (C) GFD Dennou Club, 2011-2012. All rights reserved. <br>
!! \b license ?? <br>
!! \author Yuta Kawai
!!
module IcGrid_ncWriter_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

  
  !* IGMBaseLib **********
  !*
  
  ! 二次元正二十面体格子データの管理
  ! Manager of icosahedral grid data
  !
  use IcGrid2D_FVM_Manager, only: &
    & IcGrid2D_FVM, &
    & paste_margin_width, &
    & get_EffSize_Min, get_EffSize_Max, get_IcRadius, get_glevel, &
    & 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, calc_LcProcRC_IndexInfo, &
    & calc_LcProcRC_MPIRank
  
  ! 三次元正二十面体格子データの管理
  ! Manager of icosahedral grid data
  !
  use IcGrid3D_FVM_Manager, only: &
    & IcGrid3D_FVM, &
    & get_icgrid2D, get_vertical_level_num

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

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

  ! 非構造格子データモデルの構造型の提供
  ! 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, &
    & check_nf90_status

  ! NetCDF
  !
  !
  use netcdf

  ! 宣言文 ; Declaration statements
  !
  implicit none
  private

  !> \~japanese
  !! \brief 正二十面体格子上の物理場データを NetCDF ファイルに書き出すための情報を管理するための構造型.
  !! \~english
  !! \brief Derived type which manages the information nesessary for writing the physical field data on icosahedral grid to a NetCDF file.
  !!
  type, public :: IcGrid_ncWriter
     
    ! 公開メンバ変数
    ! Public member varables

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

    !> \~japanese 構造型 Mesh2_ncInfo の変数.
    !! \~english The variable of derived type Mesh2_ncInfo.
    type(Mesh2_ncInfo) :: mesh2Info

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

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

    !> \~japanese
    !! \~english
    type(IcGrid3D_FVM), pointer :: icgrid3D => null()

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

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

    !> \~japanese
    !! \~english
    integer :: write_mode = -1
    
    !> \~japanese ファイルの開閉を記録するフラグ. 
    !! \~english
    logical :: closed_flag = .true.

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

    integer :: LocalPrcRCCol_MPIComm = -1
     
  end type IcGrid_ncWriter

  ! 公開手続き
  ! Public procedures
  !
  public :: IcGrid_ncWriter_Init, IcGrid_ncWriter_Final
  public :: open_ncFile, close_ncFile
  public :: end_ncdef, ncdef_GridData, ncdef_FieldData, ncdef_Simulation_Parameter, &
    &        write_GridData, write_FieldData, increase_recorde_counter

  ! interface 文; interface statements
  !
  !
  interface IcGrid_ncWriter_Init
    module procedure IcGrid2D_ncWriter_Init
    module procedure IcGrid3D_ncWriter_Init
  end interface IcGrid_ncWriter_Init

  interface ncdef_FieldData
    module procedure ncdef_Field2D_Data
    module procedure ncdef_Field3D_Data
  end interface ncdef_FieldData

  interface write_FieldData
    module procedure write_Field2D_Data
    module procedure write_Field3D_Data
  end interface write_FieldData

  ! 公開変数
  ! Public variables
  !
  integer, public, parameter :: ICGRID2D_MODE = 1
  integer, public, parameter :: ICGRID3D_MODE = 2

  ! 非公開変数
  ! Private variables
  !

  !> \~japanese
  !! \~english
  character(TOKEN), parameter :: RECODE_NAME = 'time'


contains

!
!> \~japanese
!! \brief 構造型 IcGrid_ncWriter の変数の初期化を行う.
!!
!! 構造型 IcGrid2D_FVM の変数を保持する.
!! また, 正二十面体格子のメッシュ情報を保持する構造型 Mesh2_ncInfo の変数を初期化する.
!!
!! @param[in,out] self        構造型 IcGrid_ncWriter の変数.
!! @param[in]     ref_icgrid  構造型 IcGrid2D_FVM の変数.
!!
!! \~english
!! \brief Initializes the variable of derived type IcGrid_ncWriter.
!!
!! 構造型 IcGrid2D_FVM の変数を保持する.
!! また, 正二十面体格子のメッシュ情報を保持する構造型 Mesh2_ncInfo の変数を初期化する.
!!
!! @param[in,out] self        The variable of derived type IcGrid_ncWriter.
!! @param[in]     ref_icgrid  The variable of derived type IcGrid2D_FVM.
!!
subroutine IcGrid2D_ncWriter_Init( &
  & self,                          &  ! (inout)
  & ref_icgrid, parallel_IO_flag   &  ! (in)
  & )

  ! 宣言文 ; Declaration statements
  !

  type(IcGrid_ncWriter), intent(inout) :: self
  type(IcGrid2D_FVM), intent(in), target :: ref_icgrid
  logical, optional :: parallel_IO_flag
  
  ! 作業変数
  ! Work variables
  !
  type(LocalProcRectInfo), pointer :: lcRCInfo  
  integer :: rcID, j
  integer :: rlevel, myrcID, mylcpcRC_i, mylcpcRC_j, outputRank
  integer :: group_world, localProcRCCol_MPIGroup
  integer, allocatable :: globalRC_col_procList(:)
  integer :: i, ierr
  
  
  ! 実行文 ; Executable statements
  !

  self%icgrid => ref_icgrid  
  self%rec_counter = 1
  lcRCInfo => get_LocalProcRectInfo(self%icgrid)
  
  
  ! 正二十面体格子のメッシュ情報を保持する Mesh2_ncInfo クラスのインスタンスを初期化
  call Mesh2_ncInfo_Init(self%mesh2Info, self%icgrid)

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

  ! NetCDF の Parallel IO を利用するかを調べる.
  if ( present(parallel_IO_flag ) .and. parallel_IO_flag) then
    
    self%parallel_IO_flag = .true.
    
#ifdef LIB_MPI
    rlevel = lcRCInfo%rlevel
    self%participate_parallel_IO = .false.
      
    allocate(globalRC_col_procList(10 * 2**rlevel))
    
    do rcID=1, 10
      do j=1, 2**rlevel
        outputRank = calc_LcProcRC_MPIRank( rlevel, rcID, 1, j )
        globalRC_col_procList((rcID - 1) * 2**rlevel + j) = outputRank
         
        if ( outputRank == lcRCInfo%lcproc_MPIRank ) self%participate_parallel_IO = .true.
      
      end do
    end do
        
    call MPI_Comm_group(MPI_COMM_WORLD, group_world, ierr)
    call MPI_Group_incl(group_world, size(globalRC_col_procList), globalRC_col_procList, & 
      &                 localProcRCCol_MPIGroup, ierr )
    call MPI_Comm_create(MPI_COMM_WORLD, localProcRCCol_MPIGroup, self%LocalPrcRCCol_MPIComm, ierr)
    
#endif
  else
    ! Parallel IO を利用しない.
    self%parallel_IO_flag = .false.
    
  end if
  
end subroutine IcGrid2D_ncWriter_Init

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

  ! 宣言文 ; Declaration statements
  !

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

  ! 作業変数
  ! Work variables
  !
  logical :: parallelIO = .false.
  
  ! 実行文 ; Executable statements
  !

  !
  self%icgrid3D => ref_icgrid3D

  ! 正二十面体格子のメッシュ情報を保持する Mesh2_ncInfo クラスのインスタンスを初期化
  call Mesh3_ncInfo_Init(self%mesh3Info, self%icgrid3D)

  if ( present(parallel_IO_flag ) ) parallelIO = parallel_IO_flag

  call IcGrid2D_ncWriter_Init(self, get_icgrid2D(ref_icgrid3D), parallelIO)

end subroutine IcGrid3D_ncWriter_Init

!
!> \~japanese
!! \brief 構造型 IcGrid_ncWriter の変数を最終化する.
!!
!! 事前に close_ncfile() を呼び出していない場合には, この最終化の際に呼び出される.
!!
!! @param[in,out] self      構造型 IcGrid_ncWriter の変数.
!!
!! \~english
!! \brief Finalizes the variable of derived type IcGrid_Writer.
!!
!! If close_ncfile() is not called in advance, the procedure will be called in this finalization.
!!
!! @param[in,out] self      The variable of derived type IcGrid_ncWriter.
!!
subroutine IcGrid_ncWriter_Final( &
  & self                          & ! (inout)
  & )

  ! 宣言文 ; Declaration statements
  !

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

  ! 作業変数
  ! Work variables
  !

  ! 実行文 ; Executable statements
  !

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

end subroutine IcGrid_ncWriter_Final

!
!> \~japanese
!! \brief 指定されたファイル名の NetCDF ファイルを作成して, 開く.
!!
!! @param[in,out] self      構造型 IcGrid_ncWriter の変数.
!! @param[in]     file_name NetCDF ファイルの名前.
!!
!! \~english
!! \brief Creates and open the specifed NetCDF file.
!!
!! @param[in,out] self      The variable of derived type IcGrid_ncWriter.
!! @param[in]     file_name The name of NetCDF file.
!!
subroutine open_ncFile( &
  & self,               &  ! (inout)
  & file_name           &  ! (in)
  & )

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

  ! 作業変数
  ! Work variables
  !
  integer :: mode_flag
  integer :: mpi_info, ierr
  integer :: my_rank
  
  ! 実行文 ; Execuatble statement
  !

  ! netCDF ファイルを作成する. 
  ! Create the NetCDF.
  if ( self%parallel_IO_flag ) then

#ifdef LIB_MPI
    if ( self%participate_parallel_IO ) then
      call MPI_Info_create(mpi_info, ierr)
      call MPI_Info_set(mpi_info, "IGMBaseLib_IcGrid_ncWriter", "true", ierr)
 
      mode_flag = IOR(NF90_NETCDF4, NF90_MPIIO)! NF90_CLASSIC_MODEL)
     ! mode_flag = IOR(mode_flag, NF90_MPIIO)
   
   ! * Use parallel IO which is new future in netcdf4. 
     ! call check_nf90_status( &
     !   & nf90_create(file_name, mode_flag, self%ncID, comm=MPI_COMM_WORLD, info=mpi_info_null) &
      !   & )
    
      call check_nf90_status( &
       & nf90_create_par(file_name, mode_flag, self%LocalPrcRCCol_MPIComm, mpi_info_null, self%ncID) &
        & )
    
    end if
    
#endif
  
  else
    call check_nf90_status( &
      & nf90_create(file_name, NF90_CLOBBER, self%ncID) )
  end if
  
  self%closed_flag = .false.
  
end subroutine open_ncFile

!
!> \~japanese
!! \brief NetCDF ファイルのヘッダー部分に, 書き込む正二十面体格子情報を定義する.
!!
!! @param[in,out] self  構造型 IcGrid_ncWriter の変数.
!!
!! \~english
!! \brief Defines the information about written icosahedral grid data in the header part of the NetCDF file.
!!
!! @param[in,out] self  The variable of derived type IcGrid_ncWriter.
!!
subroutine ncdef_GridData( &
  & self                   &  ! (inout)
  & )

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

  ! 実行文 ; Executable statement
  !
  
  if ( self%parallel_IO_flag .and. .not. (self%participate_parallel_IO) ) return
      
  if ( self%write_mode == ICGRID2D_MODE ) then
    ! NetCDF ファイルに次元要素 node を定義する.
    call ncdef_dimension(self, self%Mesh2Info%node )

    ! NetCDF ファイルに座標要素 : node_x, node_y を定義する.
    call ncdef_mesh_coordinate(self, self%mesh2Info%node_x )
    call ncdef_mesh_coordinate(self, self%mesh2Info%node_y )

  else if ( self%write_mode == ICGRID3D_MODE ) then

    ! netCDF ファイルに次元要素 node を定義
    call ncdef_dimension(self, self%Mesh3Info%node )

    ! ICGRID3D_MODE の場合は, 鉛直座標の格子データも定義する.
    ! netCDF ファイルに次元要素 vlayers を定義.
    call ncdef_dimension(self, self%Mesh3Info%vlayers )

    ! NetCDF ファイルに座標要素 : node_x, node_y, layers を定義する.
    call ncdef_mesh_coordinate(self, self%mesh3Info%node_x )
    call ncdef_mesh_coordinate(self, self%mesh3Info%node_y )
    call ncdef_mesh_coordinate(self, self%mesh3Info%layers )

  end if

  ! NetCDF ファイルに時間座標を定義する.
  call check_nf90_status( &
    & nf90_def_dim(self%ncID, RECODE_NAME, NF90_UNLIMITED, self%rec_dimid), &
    message='define dimension.'  &
    & )

  ! 正二十面格子の水平解像度を NetCDF のグローバル領域に書きこむ.
  call check_nf90_status( nf90_put_att( self%ncID, NF90_GLOBAL, 'glevel', get_glevel(self%icgrid)) )

end subroutine ncdef_GridData


function ncdef_Field3D_Data( &
  & self, field_Rank1        &  ! (in)
  & ) result(varid)

  ! 宣言文 ; Declaration statements
  !
  type(IcGrid_ncWriter), intent(in) :: self
  type(Field_IcGrid3D), intent(in) :: field_Rank1
  integer varid

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

  ! 実行文 ; Executable statement
  !

  if ( self%parallel_IO_flag .and. .not. (self%participate_parallel_IO) ) return

  !
  dimids = (/ self%Mesh3Info%node%dimid, self%Mesh3Info%vlayers%dimid, self%rec_dimid /)

  ! NetCDF ファイルに, 後に物理場のデータを書き込むことになる物理場の情報を定義する.
  call check_nf90_status( &
    & nf90_def_var(self%ncID, get_field3D_name(field_Rank1), NF90_DOUBLE, dimids, varid), &
    message='Define the field `' // trim(get_field3D_name(field_Rank1)) // '`.' &
    & )

  ! 物理場の long_name を設定.
  call check_nf90_status( &
    & nf90_put_att( self%ncID, varid, 'long_name', get_long_field3D_name(field_Rank1) ) &
    & )

  ! 物理場の単位の設定.
  call check_nf90_status( &
    & nf90_put_att( self%ncID, varid, 'units', get_field3D_units(field_Rank1) ) &
    & )

end function ncdef_Field3D_Data

!
!> \~japanese
!! \brief NetCDF ファイルのヘッダー部分に, 指定された物理場の情報を定義する.
!!
!! @param[in,out] self   構造型 IcGrid_ncWriter の変数.
!! @param[in]     field_Rank1  ヘッダーに定義したい物理場データを管理する構造型 Field_IcGrid2D の変数.
!!
!! \~english
!! \brief Defines the information about specified physical filed data in the header part of the NetCDF file.
!!
!! @param[in,out] self   The variable of derived type IcGrid_ncWriter.
!! @param[in]     field_Rank1  The variable of derived type Field_IcGrid2D to manage physical field data which is defined in the header.
!!
function ncdef_Field2D_Data( &
  & self, field_Rank1        &  ! (in)
  & ) result(varid)

  ! 宣言文 ; Declaration statements
  !
  type(IcGrid_ncWriter), intent(in) :: self
  type(Field_IcGrid2D), intent(in) :: field_Rank1
  integer varid

  ! 作業変数
  ! Work variables
  !
  integer :: dimids(2)

  ! 実行文 ; Executable statement
  !

  if ( self%parallel_IO_flag .and. .not. (self%participate_parallel_IO) ) return

  !
  select case(self%write_mode)
    case(ICGRID2D_MODE)
      dimids = (/ self%Mesh2Info%node%dimid, self%rec_dimid /)
    case(ICGRID3D_MODE)
      dimids = (/ self%Mesh3Info%node%dimid, self%rec_dimid /)
  end select

  ! NetCDF ファイルに, 後に物理場のデータを書き込むことになる物理場の情報を定義する.
  call check_nf90_status( &
    & nf90_def_var(self%ncID, get_field2D_name(field_Rank1), NF90_DOUBLE, dimids, varid), &
    message='Define the field `' // trim(get_field2D_name(field_Rank1)) // '`.' &
    & )

  ! 物理場の long_name を設定.
  call check_nf90_status( &
    & nf90_put_att( self%ncID, varid, 'long_name', get_long_field2D_name(field_Rank1) ) &
    & )

  ! 物理場の単位の設定.
  call check_nf90_status( &
    & nf90_put_att( self%ncID, varid, 'units', get_field2D_units(field_Rank1) ) &
    & )

end function ncdef_Field2D_Data

!
!> \~japanese
!! \brief シミュレーションパラメータ(積分時間, タイムステップ, アウトプットの時間間隔)を NetCDF ファイルに定義する.
!!
!! @param[in,out] self             構造型 IcGrid_ncWriter の変数.
!! @param[in]     integration_time 積分時間 [sec]. Integration time.
!! @param[in]     time_step        タイムステップ [sec]. Time step.
!! @param[in]     output_tick      データのアウトプットを行う時間間隔 [sec]. Output time step.
!!
!! \~english
!! \brief シミュレーションパラメータ(積分時間, タイムステップ, アウトプットの時間間隔)を NetCDF ファイルに定義する.
!!
!! @param[in,out] self             The variable of derived type IcGrid_ncWriter.
!! @param[in]     integration_time Integration time [sec].
!! @param[in]     time_step        Time step [sec].
!! @param[in]     output_tick      Output time step [sec].
!!
subroutine ncdef_Simulation_Parameter( &
   & self,                                    &  ! (in) 
   & integration_time, time_step, output_tick &  ! (in)
   )
   
  ! 宣言文 ; Declaration statements
  !
  type(IcGrid_ncWriter), intent(in) :: self
  real(DP), intent(in) :: integration_time
  real(DP), intent(in) :: time_step
  real(DP), intent(in) :: output_tick

  ! 実行文 ; Executable statement
  !

  if ( self%parallel_IO_flag .and. .not. (self%participate_parallel_IO) ) return

  ! 積分時間, 時間ステップ, 出力間隔の情報を NetCDF のグローバル領域に書きこむ.
  call check_nf90_status( nf90_put_att( self%ncID, NF90_GLOBAL, 'integration_time', integration_time) )
  call check_nf90_status( nf90_put_att( self%ncID, NF90_GLOBAL, 'time_step', time_step) )
  call check_nf90_status( nf90_put_att( self%ncID, NF90_GLOBAL, 'output_tick', output_tick) )

  !
  call check_nf90_status( & 
    & nf90_put_att( self%ncID, NF90_GLOBAL, 'ic_radius', get_IcRadius(self%icgrid) ) &
    & )

end subroutine ncdef_Simulation_Parameter

!
!> \~japanese
!! \brief NetCDF ファイルの定義モードを終了する.
!!
!! @param[in] self 構造体 IcGrid_ncWriter の変数.
!!
!! \~english
!! \brief Leaves the define mode of NetCDF file.
!!
!! @param[in] self The variable of derived type IcGrid_ncWriter.
!!
subroutine end_ncdef(self)

  ! 宣言文 ; Declaration statements
  !
  type(IcGrid_ncWriter), intent(in) :: self

  ! 実行文 ; Executable statement
  !

  if ( self%parallel_IO_flag .and. .not. (self%participate_parallel_IO) ) return

  call check_nf90_status( nf90_enddef( self%ncID ) )

end subroutine end_ncdef

!
!> \~japanese
!! \brief netCDF ファイルのデータ部分に, 正二十面体格子の座標データを書き込む.
!!
!! @param[in] self 構造型 IcGrid_ncWriter の変数.
!!
!! \~english
!! \brief netCDF ファイルのデータ部分に, 正二十面体格子の座標データを書き込む.
!!
!! @param[in] self The variable of derived type IcGrid_ncWriter.
!!
subroutine write_GridData(self)

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

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

  ! 宣言文 ; Declaration statements
  !
  type(IcGrid_ncWriter), intent(in) :: self

  ! 作業変数
  ! Work variables
  !
  integer rcID, i, j
  integer glEMin, glEMax
  real(DP), allocatable :: s_rcs_node_x(:), s_rcs_node_y(:)
  real(DP), allocatable :: s_rcs_AGrid(:,:,:,:)

  ! 実行文 ; Executable statements
  !

  if ( self%parallel_IO_flag .and. .not. (self%participate_parallel_IO) ) return

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

  ! 作業変数のメモリを確保する.
  allocate( s_rcs_AGrid(3, glEMin:glEMax, glEMin:glEMax, RC_REGIONS_NUM) )
  allocate( s_rcs_node_x(self%Mesh2Info%node%num) )
  allocate( s_rcs_node_y(self%Mesh2Info%node%num) )

  ! 直交座標系で表現されている格子座標データを, 地理座標系での座標データに変換する.
  !
  do rcID=1,RC_REGIONS_NUM
    do j=glEMin, glEMax
      do i=glEMin, glEMax
        s_rcs_AGrid(:,i,j,rcID) = orth_to_geo_pos(self%icgrid%rcs_AGrid(:,i,j,rcID))
   
      end do
    end do
  end do

  ! 3 次元配列を 1 次元配列化する.
  !

  ! 各格子の経度の情報を一次元配列に格納する.
  s_rcs_node_x(:) = reshape( s_rcs_AGrid(1,:,:,:), shape(s_rcs_node_x) )
  ! 各格子の緯度の情報を一次元配列に格納する.
  s_rcs_node_y(:) = reshape( s_rcs_AGrid(2,:,:,:), shape(s_rcs_node_y) )

  ! 格子座標データの緯度・経度成分を, それぞれ Mesh2_node_x, Mesh2_node_y として,
  ! netCDF ファイルに書き込む.
  !

  select case (self%write_mode)
  case (ICGRID2D_MODE)

    call check_nf90_status( &
      & nf90_put_var( self%ncID, self%Mesh2Info%node_x%varID, s_rcs_node_x ) &
      & )

    call check_nf90_status( &
    & nf90_put_var( self%ncID, self%Mesh2Info%node_y%varID, s_rcs_node_y ) &
     & )

  case (ICGRID3D_MODE)

    call check_nf90_status( &
      & nf90_put_var( self%ncID, self%Mesh3Info%node_x%varID, s_rcs_node_x ) &
      & )

    call check_nf90_status( &
     & nf90_put_var( self%ncID, self%Mesh3Info%node_y%varID, s_rcs_node_y ) &
      & )

    ! ICGRID3D_MODE の場合は, 鉛直座標の格子データも出力する.
    call check_nf90_status( &
      & nf90_put_var( self%ncID, self%Mesh3Info%layers%varID, self%icgrid3D%verticalGrid(:) ) &
      & )

  end select

end subroutine write_GridData

!
!> \~japanese
!! \brief netCDF ファイルのデータ部分に, 物理場(スカラー場)データを書き込む.
!!
!! 書き込みたい正二十面体格子上の物理場データは, Field_IcGrid2D クラスのオブジェクトとして保持されている必要がある.
!!
!! @param[in] self  構造型 IcGrid_ncWriter の変数.
!! @param[in] varid ncdef_Field_Data の返り値である書き込まれる物理場に対応付けられた変数 ID.
!! @param[in] field_Rank1 書き込む物理場データを管理する構造型 Field_IcGrid2D の変数.
!!
!! \~english
!! \brief netCDF ファイルのデータ部分に, 物理場(スカラー場)データを書き込む.
!!
!! 書き込みたい正二十面体格子上の物理場データは, Field_IcGrid2D クラスのオブジェクトとして保持されている必要がある.
!!
!! @param[in] self  The variable of derived type IcGrid_ncWriter.
!! @param[in] varid The ID associated with written physical field. This ID is the value returned by ncdef_Field_Data.
!! @param[in] field_Rank1 The variable of derived type Field_IcGrid2D which manages written physical field data.
!!
subroutine write_Field2D_Data(self, varid, field_Rank1)

  ! 宣言文 ; Declaration statements
  !
  type(IcGrid_ncWriter), intent(in) :: self
  integer, intent(in) :: varid
  type(Field_IcGrid2D), intent(in) :: field_Rank1

  ! 作業変数
  ! Work variables
  !
  real(DP), pointer :: written_field_data(:) => null()
  
  integer :: glwIdStart, glwIdEnd
  integer :: start(2), count(2), ierr

  ! 実行文 ; Executable statement
  !
     
  call create_written_localRC_field_data( &
    & self, field_Rank1, &
    & written_field_data, glwIdStart, glwIdEnd )

  if ( self%parallel_IO_flag .and. .not. (self%participate_parallel_IO) ) return
  
  start = (/ glwIdStart, self%rec_counter /)
  count = (/ glwIdEnd - glwIdStart + 1, 1 /)

  call check_nf90_status( &
    & nf90_put_var( self%ncID, varID, written_field_data, start=start, count=count ) &
    & )
    
  deallocate(written_field_data)
   
end subroutine write_Field2D_Data

subroutine write_Field3D_Data(self, varid, field_Rank1)

  ! 宣言文 ; Declaration statements
  !
  type(IcGrid_ncWriter), intent(in) :: self
  integer, intent(in) :: varid
  type(Field_IcGrid3D), intent(in) :: field_Rank1

  ! 作業変数
  ! Work variables
  !
  integer :: EMin, EMax
  integer :: vtlv_num
  integer :: k  
  type(Field_IcGrid2D), pointer :: field2D_k

  real(DP), pointer :: written_field_k(:)  
  real(DP), allocatable :: written_field(:,:)
  integer :: glwIdStart, glwIdEnd
  integer :: start(3), count(3)

  ! 実行文 ; Executable statement
  !

  !
  EMin = get_EffSize_Min(self%icgrid)
  EMax = get_EffSize_Max(self%icgrid)
  vtlv_num = get_vertical_level_num(self%icgrid3D)

  do k=1, vtlv_num

    field2D_k => get_field_layer(field_Rank1, k)

    call create_written_localRC_field_data( &
      & self, field2D_k, &
      & written_field_k, glwIdStart, glwIdEnd )

    if ( k == 1 ) allocate( written_field(size(written_field_k), vtlv_num) )

    written_field(:,k) = written_field_k(:)
  
    deallocate(written_field_k)
    
  end do

  if ( self%parallel_IO_flag .and. .not. (self%participate_parallel_IO) ) return

  !
  start = (/ glwIDStart, 1, self%rec_counter /)
  count = (/ glwIDEnd - glwIDStart + 1, self%Mesh3Info%vlayers%num, 1 /)

  call check_nf90_status( &
    & nf90_put_var( self%ncID, varID, written_field(:,:), start=start, count=count ) &
    & )

end subroutine write_Field3D_Data

!
!> \~japanese
!! \brief 記録カウンタを更新して, 時刻を output_tick 分だけインクリメントする.
!!
!! @param[in,out] self 構造型 IcGrid_ncWriter の変数.
!!
!! \~english
!! \brief 記録カウンタを更新して, 時刻を output_tick 分だけインクリメントする.
!!
!! @param[in,out] self The variable of derived type IcGrid_ncWriter.
!!
subroutine increase_recorde_counter(self)

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

  ! 実行文 ; Executable statement
  !
  if ( self%parallel_IO_flag .and. .not. (self%participate_parallel_IO) ) return

  self%rec_counter = self%rec_counter + 1

end subroutine increase_recorde_counter

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

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

  ! 実行文 ; Executable statement
  !
  if ( self%parallel_IO_flag .and. .not. (self%participate_parallel_IO) ) return

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


end subroutine close_ncFile

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

subroutine create_written_localRC_field_data( &
  & self, field_Rank1,                                     &  ! (in)
  & written_field_data, globalIDStart, globalIDEnd         &  ! (out)
  & )
  
  ! 宣言文 ; Declaration statements
  !
  type(IcGrid_ncWriter), intent(in) :: self
  type(Field_IcGrid2D), intent(in) :: field_Rank1
  real(DP), pointer :: written_field_data(:)
  integer, intent(out) :: globalIDStart
  integer, intent(out) :: globalIDEnd   

  ! 作業変数
  ! Work variables
  !
  integer :: rcID, i, j, r
  integer :: glEMin, glEMax
  integer :: iEMin, iEMax, jEMin, jEMax, rcIDMin, rcIDMax
  type(LocalProcRectInfo), pointer :: lcRCInfo
  integer :: myrcID, mylcpcRC_i, mylcpcRC_j
    
  integer :: written_field_elemNum
  integer :: wIdStart, wIdEnd, i_chunk_size, i_chunk_size_mod
  integer :: recvIDStart, recvIDEnd

  real(DP), allocatable :: send_field_data(:)
  real(DP), allocatable :: recv_field_data(:,:)
  
  integer :: dist, source, my_rank
  integer :: 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     &
       & ) 
    
  !
  written_field_elemNum = ( rcIDMax - rcIDMin + 1 ) * ( jEMax - jEMin + 1 ) * ( glEMax - glEMin + 1 )  
      
  if ( ( .not. lcRCInfo%mpi_flag ) .or. mylcpcRC_i == 1 ) then
    
    allocate(written_field_data(written_field_elemNum))

    do rcID=rcIDMin, rcIDMax
      do j=jEMin, jEMax
        wIdStart = ( ( rcID - rcIDMin ) * ( glEMax - glEMin + 1 ) + ( j - jEMin ) ) * ( glEMax - glEMin + 1 ) + 1
        wIdEnd   = wIDStart + iEMax - iEMin
      
        written_field_data(wIdStart:wIdEnd) = field_Rank1%val(1, iEMin:iEMax,j,rcID)
      
      end do
    end do
  
  end if
  
  if ( lcRCInfo%mpi_flag .and. lcRCInfo%rlevel > 0 ) then
        
    i_chunk_size = int(( glEMax-glEMin+1 ) / 2**lcRCInfo%rlevel) + 1
    
    call MPI_COMM_Rank(MPI_COMM_WORLD, my_rank, ierr)
    
    if ( mylcpcRC_i == 1 ) then
      
      allocate(recv_field_data(i_chunk_size * (jEMax - jEMin + 1), 2:2**lcRCInfo%rlevel))
      
      do i=2, 2**lcRCInfo%rlevel
        source = calc_LcProcRC_MPIRank(lcRCInfo%rlevel, myrcID, i, mylcpcRC_j)

        call MPI_Recv(recv_field_data(:,i), size(recv_field_data, 1), MPI_REAL8, source, 1, &
          &           MPI_COMM_WORLD, recv_status, ierr)
                          
      end do
      
    else
      
      allocate(send_field_data((jEMax - jEMin + 1 ) * (iEMax - iEMin + 1 )))  
      send_field_data(:) = reshape(field_Rank1%val(1, iEMin:iEMax, jEMin:jEMax, myrcID), shape(send_field_data))

      dist = calc_LcProcRC_MPIRank(lcRCInfo%rlevel, myrcID, 1, mylcpcRC_j)

      call MPI_send(send_field_data, size(send_field_data), MPI_REAL8, dist, 1, &
        &           MPI_COMM_WORLD, ierr)
                   
    end if
    
    if ( mylcpcRC_i == 1 ) then
    
      i_chunk_size_mod = ( glEMax - glEMin + 1 ) - ( 2**lcRCInfo%rlevel - 1 ) * i_chunk_size
      
      do j=jEMin, jEMax
!         !
        
        do r=2, 2**lcRCInfo%rlevel
       
          wIdStart = ( j - jEMin ) * ( glEMax - glEMin + 1 ) + ( r - 1 ) * i_chunk_size + 1
          recvIDStart = ( j - jEMin ) * i_chunk_size + 1   
          wIdEnd   = wIDStart + i_chunk_size - 1
          
          if ( r == 2**lcRCInfo%rlevel ) then
            wIdEnd = ( j+1 - jEMin ) * ( glEMax - glEMin + 1 )
            recvIDStart = ( j - jEMin ) * i_chunk_size_mod + 1   
          end if
           
          do i=0, wIdEnd - wIdStart         
            written_field_data(wIdStart+i) = recv_field_data(recvIDStart+i, r)
          end do
    
        end do
      end do
    end if

  end if
  
  !
  globalIDStart = ( ( rcIDMin - 1 ) * ( glEMax - glEMin + 1 ) + ( jEMin - 1 ) ) * ( glEMax - glEMin + 1 ) + 1
  globalIDEnd   = globalIDStart + written_field_elemNum - 1
  
end subroutine create_written_localRC_field_data

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

  ! 宣言文 ; Declaration statements
  !
  type(IcGrid_ncWriter), intent(in) :: self
  type(Mesh_dim_element), intent(inout) :: dim_element

  ! 実行文 ; Executable statments
  !

  call check_nf90_status( &
    & nf90_def_dim(self%ncID, dim_element%element_name, dim_element%num, dim_element%dimID), &
    & message='define dimension. ' // trim(dim_element%element_name) &
    & )

end subroutine ncdef_dimension

!
!> \~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 ncdef_mesh_coordinate(self, coordinate_element)

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

  ! 作業変数
  ! Work variables
  !

  ! 実行文 ; Executable statements
  !

  ! 座標変数を定義する.
  call check_nf90_status( &
    & nf90_def_var( &
       & self%ncID, &
       & coordinate_element%element_name, NF90_DOUBLE, &
       & coordinate_element%dimension_element%dimID, coordinate_element%varID &
        & ) &
     & )

  ! 座標変数のメタ情報を加える.
  !

  call check_nf90_status( &
    & nf90_put_att( self%ncID, coordinate_element%varID, 'name', coordinate_element%standard_name ) &
    & )

  call check_nf90_status( &
    & nf90_put_att( self%ncID, coordinate_element%varID, 'long_name', coordinate_element%long_name ) &
    & )

  ! 座標の単位を設定.
  call check_nf90_status( &
    & nf90_put_att( self%ncID, coordinate_element%varID, 'units', coordinate_element%units ) &
    & )

end subroutine ncdef_mesh_coordinate

end module IcGrid_ncWriter_mod
