!--
!----------------------------------------------------------------------
!     Copyright (c) 2020 Shin-ichi Takehiro. All rights reserved.
!----------------------------------------------------------------------
!表題  wb_mpi_module
!
!   spml/wb_mpi_module モジュールは球面上での 2 次元流体運動を
!   球面調和函数を用いたスペクトル法によって数値計算するための
!   Fortran90 関数を提供する.
!
!   球面上の 1 層モデル用 w_module モジュールを多層モデル用に
!   拡張したものであり, 同時に複数個のスペクトルデータ, 格子点データに
!   対する変換が行える.
!
!   wb_mpi_module は実際には基本変換, 微分計算, 積分・平均計算,
!   スペクトル解析をそれぞれ担っている下部モジュール wb_base_module_sjpack,
!   wh_deriv_module, wa_integral_module,
!   wb_spectrum_module および 1 層用のモジュール w_module からなっている.
!
!   内部で ISPACK3 の SYPACK の Fortran77 サブルーチンを呼んでいる.
!   スペクトルデータおよび格子点データの格納方法や変換の詳しい計算法に
!   ついては ISPACK3/SYPACK のマニュアルを参照されたい.
!
!履歴  2020/08/02  竹広真一  ua_mpi_module よリ改造
!
!++
module wb_mpi_module
  !
  != wb_mpi_module
  !
  ! Authors:: Shin-ichi Takehiro, Youhei SASAKI
  ! Version:: $Id$
  ! Copyright&License:: See COPYRIGHT[link:../COPYRIGHT]
  !
  !== 概要
  !
  !   spml/wb_mpi_module モジュールは球面上での 2 次元流体運動を
  !   球面調和函数を用いたスペクトル法によって数値計算するための
  !   Fortran90 関数を提供する.
  !
  !   球面上の 1 層モデル用 w_module モジュールを多層モデル用に
  !   拡張したものであり, 同時に複数個のスペクトルデータ, 格子点データに
  !   対する変換が行える.
  !
  !   wb_mpi_module は実際には基本変換, 微分計算, 積分・平均計算,
  !   スペクトル解析をそれぞれ担っている下部モジュール wb_base_module_sjpack,
  !   wh_deriv_module, wa_integral_module,
  !   wb_spectrum_module および 1 層用のモジュール w_module からなっている.
  !
  !   内部で ISPACK3 の SYPACK の Fortran77 サブルーチンを呼んでいる.
  !   スペクトルデータおよび格子点データの格納方法や変換の詳しい計算法に
  !   ついては ISPACK3/SYPACK のマニュアルを参照されたい.
  !
  !=== 命名法
  !
  ! * 関数名の先頭 (wb_, nma_, na_, pva_, xb_, yb_, w_, xy_, x_, y_, a_) は,
  !   返す値の形を示している.
  !   wb_  :: 水平スペクトルデータの並んだ 2 次元配列(水平波数, 層分割)
  !           (スペクトルデータの並びは SYPACK/ISPACK に従ったもの)
  !   nmb_ :: スペクトルデータの並んだ 3 次元配列(層分割)
  !           (スペクトルデータの並びは全波数 n, 帯状波数 m で指定される
  !             2 次元配列)
  !   nb_  :: スペクトルデータの並んだ 2 次元配列(層分割)
  !           (スペクトルデータの並びは全波数 n で指定される 1 次元配列)
  !   pva_ :: 3 次元格子点データ(経度, 層分割)
  !   pa_  :: 経度方向 1 次元格子点データ(分割)が層方向に並んだ 2 次元配列
  !   va_  :: 緯度方向 1 次元格子点データ(分割)に層方向に並んだ 2 次元配列
  !
  ! * 関数名の間の文字列(DLon, GradLat, GradLat, DivLon, DivLat, Lapla,
  !   LaplaInv, Jacobian)は, その関数の作用を表している.
  !
  ! * 関数名の最後 (_wb_wb, _wb, _pva, _pa, _va, _w_w, _w, _pv, _p, _v) は,
  !   入力変数の形スペクトルデータおよび格子点データであることを示している.
  !   _wb    :: スペクトルデータの並んだ 2 次元配列(水平波数, 層分割)
  !   _wb_wb :: 2 つのスペクトルデータの並んだ 2 次元配列(水平波数, 層分割)
  !   _pva   :: 2 次元格子点データの並んだ 3 次元配列(経度, 層分割)
  !   _pa    :: 経度方向 1 次元格子点データ(分割)が層方向に並んだ 2 次元配列
  !   _va    :: 緯度方向 1 次元格子点データ(分割)が層方向に並んだ 2 次元配列
  !
  !=== 各データの種類の説明
  !
  ! * pva : 2 次元格子点データの並んだ 3 次元配列.
  !   * 変数の種類と次元は real(8), dimension(ic,jc,km).
  !   * ic, jc はそれぞれ経度, 緯度座標の分割格子点数であり, サブルーチン
  !     wb_mpi_Initial を call した後に設定される. 
  !   * 第 3 次元(層方向)の大きさ km を wb_mpi_Initial で設定しておく.
  !
  ! * wb : スペクトルデータの並んだ 2 次元配列.
  !   * 変数の種類と次元は real(8), dimension(nc,kc)
  !   * nc は球面調和函数成分の分割された数であり, 
  !     サブルーチン wb_mpi_Initial にてあらかじめ設定しておく.
  !   * スペクトルデータの格納のされ方は関数
  !     l_nm, nm_l によって調べることができる.
  !   * 扱う第 3 次元の分割大きさ kc は
  !     wb_mpi_Initial を call した後に設定される. 
  !
  ! * pa, va : 経度, 緯度方向 1 次元格子点データ(分割).
  !   * 変数の種類と次元はそれぞれ real(8), dimension(ic,km)
  !     および real(8), dimension(jc,km).
  !
  ! * nmb : スペクトルデータの並んだ 3 次元配列.
  !   * 変数の種類と次元は real(8), dimension(0:nm,-nm:nm,kc).
  !   * 第 1 次元が水平全波数, 第 2 次元が帯状波数を表す.
  !   * nm は球面調和函数の最大全波数であり,
  !     サブルーチン wa_mpi_Initial にてあらかじめ設定しておく.
  !
  ! * nb : スペクトルデータの並んだ 2 次元配列.
  !   * 変数の種類と次元は real(8), dimension(0:nm,kc).
  !   *  第 1 次元が水平全波数を表す.nm は球面調和函数の最大全波数であり,
  !      サブルーチン wb_mpi_Initial にてあらかじめ設定しておく.
  !
  ! * wb_ で始まる関数が返す値はスペクトルデータの並んだ 2 次元配列に同じ.
  !
  ! * nmb_ で始まる関数が返す値はスペクトルデータの並んだ 3 次元配列に同じ.
  !
  ! * nb_ で始まる関数が返す値はスペクトルデータの並んだ 2 次元配列に同じ.
  !
  ! * pva_ で始まる関数が返す値は 2 次元格子点データの並んだ 3 次元配列に
  !   同じ.
  !
  ! * pa_, va_ で始まる関数が返す値は 1 次元格子点データの並んだ 2 次元
  !   配列に同じ.
  !
  ! * スペクトルデータに対する微分等の作用とは, 対応する格子点データに
  !   微分などを作用させたデータをスペクトル変換したものことである.
  !
  !
  !== 変数・手続き群の要約
  !
  !==== 初期化
  !
  ! wb_mpi_Initial :: スペクトル変換の格子点数, 波数, 領域の大きさ,
  !                :: および同時に計算するデータの個数の最大値設定
  !
  !==== 終了処理
  !
  ! wb_mpi_Finalize :: モジュールの終了処理(割り付け配列の解放)をおこなう.
  !
  !==== 座標変数
  !
  ! p_Lon, v_Lat     ::  格子点座標(緯度, 経度座標)を格納した 1 次元配列
  ! p_Lon_Weight, v_Lat_Weight ::  重み座標を格納した 1 次元配列
  ! pv_Lon, pv_Lat   :: 格子点データの経度・緯度座標(X,Y)
  !                     (格子点データ型 2 次元配列)
  !
  !==== 基本変換
  !
  ! pva_wb, pv_w :: スペクトルデータから格子データへの変換(多層, 1 層用)
  ! wb_pva, w_py :: 格子データからスペクトルデータへの変換(多層, 1 層用)
  ! l_nm, nm_l :: スペクトルデータの格納位置と全波数・帯状波数の変換
  ! w_StreamPotential2Vector :: 流線ポテンシャルから速度場計算
  ! w_Vector2VorDiv          :: 速度場から渦度発散を計算
  ! w_VectorCosLat2VorDiv    :: 速度場から渦度発散を計算
  ! wb_StreamPotential2VectorMPI :: 流線ポテンシャルから速度場計算
  ! wb_Vector2VorDivMPI          :: 速度場から渦度発散を計算
  ! wb_VectorCosLat2VorDivMPI    :: 速度場から渦度発散を計算
  !
  !==== 微分
  !
  ! wb_Lapla_wb, w_Lapla_w        :: スペクトルデータにラプラシアンを
  !                                  作用させる(多層, 1 層用)
  ! rn                            :: スペクトルデータのラプラシアンを
  !                                  計算するための係数
  ! irm                           :: 経度微分演算用配列(未定義)
  ! wb_LaplaInv_wb, w_LaplaInv_w  :: スペクトルデータにラプラシアンの逆変換を
  !                                  作用させる(多層, 1 層用)
  ! wb_DLon_wb, w_DLon_w          :: スペクトルデータに
  !                                  経度微分∂/∂λを作用させる(多層, 1 層用)
  ! pva_GradLon_wb, pv_GradLon_w  :: スペクトルデータに
  !                                  勾配型経度微分 1/cosφ・∂/∂λ を
  !                                  作用させる(多層, 1 層用)
  ! pva_GradLat_wb, pv_GradLat_w  :: スペクトルデータに
  !                                  勾配型緯度微分 ∂/∂φ を
  !                                  作用させる(多層, 1 層用)
  ! wb_DivLon_pva, w_DivLon_pv    :: 格子データに
  !                                  発散型経度微分 1/cosφ・∂/∂λを
  !                                  作用させる(多層, 1 層用)
  ! wb_DivLat_pva, w_DivLat_pv    :: 格子データに
  !                                  発散型緯度微分 1/cosφ・∂(g cosφ)/∂φを
  !                                  作用させる (多層, 1 層用)
  ! wb_Div_pva_pva, w_Div_pv_pv   :: ベクトル成分である 2 つの格子データに
  !                                  発散を作用させる(多層, 1 層用)
  ! wb_Jacobian_wb_wb,w_Jacobian_w_w :: 2つのスペクトルデータからヤコビアンを
  !                                     計算する(多層, 1 層用)
  !
  !==== 微分(λ,μ=sinφ 座標)
  !
  ! pva_GradLambda_wb, pv_GradLambda_w  :: スペクトルデータに
  !                                        勾配型経度微分∂/∂λを作用させる
  !                                        (多層, 1 層用)
  ! pva_GradMu_wb, pv_GradMu_w          :: スペクトルデータに
  !                                        勾配型緯度微分(1-μ^2)∂/∂μ
  !                                        を作用させる
  !                                        (多層, 1 層用)
  ! wb_DivLambda_pva,w_DivLambda_pv     :: 格子データに発散型経度微分
  !                                        1 /(1-μ^2)・∂/∂λを作用させる
  !                                        (多層, 1 層用)
  !
  ! wb_DivMu_pva,w_DivMu_pv             :: 格子データに発散型緯度微分
  !                                        ∂/∂μを作用させる(多層, 1 層用)
  !
  !==== 補間計算
  !
  ! a_Interpolate_wb, Interpolate_w :: スペクトルデータから
  !                                    任意の点の関数値を計算する.
  !
  !==== 積分・平均
  !
  ! a_IntLonLat_pva, a_AvrLonLat_pva :: 2 次元格子点データの
  !                                     全領域積分および平均(多層用)
  ! IntLonLat_pv, AvrLonLat_pv       :: 2 次元格子点データの
  !                                     全領域積分および平均(1 層用)
  ! va_IntLon_pva, va_AvrLon_pva     :: 2 次元格子点データの
  !                                     経度方向積分および平均(多層用)
  ! v_IntLon_pv, v_AvrLon_pv         :: 2 次元格子点データの
  !                                     経度方向積分および平均(1 層用)
  ! a_IntLon_pa, a_AvrLon_pa         :: 1 次元(X)格子点データの
  !                                     経度方向積分および平均(多層用)
  ! IntLon_p, AvrLon_p               :: 1 次元(X)格子点データの
  !                                     経度方向積分および平均(1 層用)
  ! pa_IntLat_pva, p_AvrLat_pv       :: 2 次元格子点データの
  !                                     緯度方向積分および平均(多層用)
  ! p_IntLat_pv, p_AvrLat_pv         :: 2 次元格子点データの
  !                                     緯度方向積分および平均(1 層用)
  ! a_IntLat_va, a_AvrLat_va         :: 1 次元(Y)格子点データの
  !                                     緯度方向積分および平均(多層用)
  ! IntLat_v, AvrLat_v               :: 1 次元(Y)格子点データの
  !                                     緯度方向積分および平均(1 層用)
  !
  !==== スペクトル解析
  !
  ! nmb_EnergyFromStreamfunc_wb,nm_EnergyFromStreamfunc_w   ::
  ! 流線関数からエネルギースペクトルを計算する
  ! (水平全波数 n, 帯状波数 m 空間)(多層, 1 層用)
  !
  ! nb_EnergyFromStreamfunc_wb,n_EnergyFromStreamfunc_w     ::
  ! 流線関数からエネルギースペクトルを計算する
  ! (水平全波数 n 空間) (多層, 1 層用)
  !
  ! nmb_EnstrophyFromStreamfunc_wb,nm_EnstrophyFromStreamfunc_w   ::
  ! 流線関数からエンストロフィースペクトルを計算する
  ! (水平全波数 n, 帯状波数 m 空間)(多層, 1 層用)
  !
  ! nb_EnstrophyFromStreamfunc_wb,n_EnstrophyFromStreamfunc_w ::
  ! 流線関数からエンストロフィースペクトルを計算する(水平全波数 n 空間)
  ! (多層, 1 層用)
  !
  ! wb_spectrum_VMiss, w_spectrum_VMiss          ::  欠損値
  !
  !
  use dc_message, only : MessageNotify
  use w_mpi_module_integral, only : IntLat_v, AvrLat_v
  use w_mpi_module_interpolate
  use w_mpi_module_spectrum
  use wb_mpi_module_base
  use wb_mpi_module_deriv
  use wb_mpi_module_integral
  use wb_mpi_module_interpolate
  use wb_mpi_module_spectrum

  implicit none

  private
  public wb_mpi_Initial                       ! 初期化
  public wb_mpi_Finalize                      ! 終了処理

  public nproc_h, nproc_v
  public iproc_h, iproc_v

  public jc, nc
  public is, ie, ic
  public ks, ke, kc

  public l_nm, nm_l
  public kp_kl

  public xv_Lon, xv_Lat
  public x_Lon, v_Lat                          ! 格子座標
  public x_Lon_Weight, v_Lat_Weight            ! 格子座標重み
  public pv_Lon, pv_Lat
  public p_Lon                                 ! 格子座標
  public p_Lon_Weight                          ! 格子座標重み

  public wb_pva, pva_wb                        ! 変換関数
  public wb_xvb, xvb_wb                        ! 変換関数
  public xvb_pva, pva_xvb                      ! 変換関数
  public pv_w, w_pv                            ! 変換関数
  public xv_w, w_xv                            ! 変換関数

  public xa_pa, pa_xa                          ! 変換関数
  public aa_ab, ab_aa                          ! 変換関数
  public wb_StreamPotential2VectorMPI          ! 流線ポテンシャルから速度場計算
  public wb_Vector2VorDivMPI                   ! 速度場から渦度発散を計算
  public wb_VectorCosLat2VorDivMPI             ! 速度場から渦度発散を計算

  public rn                                   ! ラプラシアンの係数配列

  public wb_Lapla_wb, wb_LaplaInv_wb          ! ラプラシアンと逆演算
  public wb_DLon_wb                           ! 経度微分
  public pva_GradLon_wb, pva_GradLat_wb       ! 勾配型微分
  public wb_DivLon_pva, wb_DivLat_pva         ! 発散型微分
  public wb_Div_pva_pva                       ! 発散型微分
  public wb_JacobianMPI_wb_wb                 ! ヤコビアン
  public pva_GradLambda_wb, pva_GradMu_wb     ! 勾配型微分(λ,μ座標)
  public wb_DivLambda_pva, wb_DivMu_pva       ! 発散型微分(λ,μ座標)

  public w_Lapla_w, w_LaplaInv_w              ! ラプラシアンと逆演算
  public w_DLon_w                             ! 経度微分
  public pv_GradLon_w, pv_GradLat_w           ! 勾配型微分
  public w_DivLon_pv, w_DivLat_pv             ! 発散型微分
  public w_Div_pv_pv                          ! 発散型微分
  public w_JacobianMPI_w_w                    ! ヤコビアン
  public pv_GradLambda_w, pv_GradMu_w         ! 勾配型微分(λ,μ座標)
  public w_DivLambda_pv, w_DivMu_pv           ! 発散型微分(λ,μ座標)

  public b_Interpolate_wb                     ! 補間関数
  public Interpolate_w                        ! 補間関数

  public a_IntLonLat_pva, IntLonLat_pv        ! 緯度経度積分
  public va_IntLon_pva, a_IntLon_pa, IntLon_p ! 経度積分
  public pa_IntLat_pva, a_IntLat_va, IntLat_v ! 緯度積分
  public a_AvrLonLat_pva, AvrLonLat_pv        ! 緯度経度平均
  public va_AvrLon_pva, a_AvrLon_pa, AvrLon_p ! 経度平均
  public pa_AvrLat_pva, a_AvrLat_va, AvrLat_v ! 緯度平均

  public nmb_EnergyFromStreamfunc_wb        ! エネルギースペクトル
                                            ! (水平全波数 n, 帯状波数 m 空間)
  public nb_EnergyFromStreamfunc_wb         ! エネルギースペクトル
                                            ! (水平全波数 n 空間)
  public nmb_EnstrophyFromStreamfunc_wb     ! エンストロフィースペクトル
                                            ! (水平全波数 n, 帯状波数 m 空間)
  public nb_EnstrophyFromStreamfunc_wb      ! エンストロフィースペクトル
                                            !  (水平全波数 n 空間)
  public wb_spectrum_VMiss                  ! 欠損値

  public nm_EnergyFromStreamfunc_w          ! エネルギースペクトル
                                            ! (水平全波数 n, 帯状波数 m 空間)
  public n_EnergyFromStreamfunc_w           ! エネルギースペクトル
                                            ! (水平全波数 n 空間)
  public nm_EnstrophyFromStreamfunc_w       ! エンストロフィースペクトル
                                            ! (水平全波数 n, 帯状波数 m 空間)
  public n_EnstrophyFromStreamfunc_w        ! エンストロフィースペクトル
                                            !  (水平全波数 n 空間)
  public w_spectrum_VMiss                   ! 欠損値

contains
  subroutine wb_mpi_Initial(n_in,i_in,j_in,k_in,np,npv)
    !
    ! スペクトル変換の格子点数, 波数, 最大データ数(層数)および
    ! OPENMP 使用時の最大スレッド数を設定する.
    !
    ! 他の関数を呼ぶ前に, 最初にこのサブルーチンを呼んで初期設定を
    ! しなければならない.
    !
    integer,intent(in) :: i_in                ! 格子点数(東西)
    integer,intent(in) :: j_in                ! 格子点数(南北)
    integer,intent(in) :: n_in                ! 切断波数
    integer,intent(in) :: k_in                ! 最大データ数(層数)
    integer,intent(in), optional :: np        ! OPENMP での最大スレッド数(dummy)
    integer,intent(in), optional :: npv       ! OPENMP での最大スレッド数

    if ( present(np) ) &
      call MessageNotify('M','wb_mpi_Initial','Optional argument np is dummy')
    if ( present(npv) ) then
      call wb_base_mpi_Initial(n_in,i_in,j_in,k_in,npv=npv)
    else
      call wb_base_mpi_Initial(n_in,i_in,j_in,k_in)
    endif
    call wb_deriv_mpi_Initial

    call MessageNotify('M',&
      & 'wb_mpi_initial', &
      & 'wb_mpi_module (2020/08/02) is initialized')

  end subroutine wb_mpi_Initial

  !--------------- 終了処理 -----------------
  subroutine wb_mpi_Finalize
    !
    ! モジュールの終了処理(割り付け配列の解放)をおこなう.
    !
    ! 解像度を変更する際にはこのサブルーチンを呼んで終了処理を
    ! おこなったのちに再度 ua_mpi_Initial で初期設定しなければ
    ! ならない.
    !
    call wb_base_mpi_Finalize
    call wb_deriv_mpi_Finalize

    call MessageNotify('M','wb_mpi_Finalize', &
      'wb_mpi_module (2020/08/02) is finalized')

  end subroutine wb_mpi_Finalize

end module wb_mpi_module

