!--
!----------------------------------------------------------------------
! Copyright(c) 2016 SPMDODEL Development Group. All rights reserved.
!----------------------------------------------------------------------
!表題  wq_mpi_module_supack
!
!    spml/wq_mpi_module_supack モジュールは球内での流体運動をスペクトル法と 
!    MPI 並列化によって数値計算するための Fortran90 関数を提供する
!    ものである. 
!
!    水平方向に球面調和函数変換および動径方向に
!    Matsushima and Marcus (1994) で提唱された多項式を用いた
!    スペクトル計算のためのさまざまな関数を提供する. 
!
!    内部で wa_mpi_module_supack, aq_module を用いている. 
!    最下部では球面調和変換変換のエンジンとして 
!    ISPACK の Fortran77 サブルーチンを用いている.
!
!    関数, サブルーチンの名前と機能は wq_mpi_module のものと同じである.
!    したがって use 文を wq_mpi_module から wq_mpi_module_supack に
!    変更するだけで SUPACK-MPI の機能が使えるようになる.
!
!    ただし l_nm, nm_l の使い方には注意されたい. wq_module の l_nm,
!    nm_l はwq_Initial で初期化しなくとも用いることができる(結果が切断
!    波数に依らない)が, wq_mpi_module_supack のものは初期化したのちにしか使
!    うことができない.
!
!    Matsushima and Marcus (1994) の多項式に関する説明は 
!    doc/spectral_radial.tex を参照のこと. 
!
!
!履歴  2016/03/31  竹広真一  wq_module_sjpack より改変
!
!凡例
!      データ種類と index
!        x : 経度         v : 緯度(分散格子)     r : 動径
!        w : 球面調和関数スペクトル
!        n : 球面調和関数スペクトル(水平全波数)
!        m : 球面調和関数スペクトル(帯状波数)
!        q : スペクトル関数スペクトル
!        a : 任意の次元
!
!        xvr : 3 次元分散格子点データ
!        xv  : 水平 2 次元分散格子点データ
!        vr  : 子午面 2 次元分散格子点データ
!        xr  : 緯度面 2 次元格子点データ
!
!        wr  : 水平スペクトル動径格子点データ
!        wq  : スペクトルデータ
!
!++
module wq_mpi_module_supack
  !
  != wq_mpi_module_supack
  !
  ! Authors:: Shin-ichi Takehiro, Youhei SASAKI
  ! Version:: $Id$
  ! Copyright&License:: See COPYRIGHT[link:../COPYRIGHT]
  !
  !== 概要
  !
  ! spml/wq_mpi_module_supack モジュールは球内での流体運動をスペクトル法と 
  ! MPI 並列化によって数値計算するための Fortran90 関数を提供する
  ! ものである. 
  !
  ! 水平方向に球面調和函数変換および動径方向に
  ! Matsushima and Marcus (1994) で提唱された多項式を用いた
  ! スペクトル計算のためのさまざまな関数を提供する. 
  !
  ! 内部で wa_mpi_module_supack, aq_module を用いている. 
  ! 最下部では球面調和変換のエンジンとして 
  ! ISPACK の Fortran77 サブルーチンを用いている.
  !
  !== 関数・変数の名前と型について
  !
  !=== 命名法
  !
  ! * 関数名の先頭 (wq_, nmr_, nr_, xyr_, wr_, w_, xy_, xv_, yr_, vr_, xr_, 
  !   x_, y_, v_, r_, a_) は, 返す値の形を示している.
  !   wq_  :: スペクトルデータ(球面調和函数・チェビシェフ変換)
  !   nmr_ :: 水平スペクトルデータ(全波数 n, 帯状波数各成分, 動径)
  !   nr_  :: 水平スペクトルデータ(全波数 n, 動径)
  !   xyr_ :: 3 次元格子点データ(経度・緯度・動径)
  !   xvr_ :: 3 次元分散格子点データ(経度・緯度・動径)
  !   wr_  :: 水平スペクトル, 動径格子点データ
  !   xy_  :: 2 次元格子点データ(経度・緯度)
  !   xv_  :: 2 次元分散格子点データ(経度・緯度)
  !   yr_  :: 2 次元格子点データ(緯度・動径)
  !   vr_  :: 2 次元分散格子点データ(緯度・動径)
  !   xr_  :: 2 次元格子点データ(経度・動径)
  !   x_   :: 1 次元格子点データ(経度)
  !   y_   :: 1 次元格子点データ(緯度)
  !   v_   :: 1 次元分散格子点データ(緯度)
  !   r_   :: 1 次元格子点データ(動径)
  !   a_   :: 任意の次元
  !
  ! * 関数名の間の文字列(DLon, GradLat, GradLat, DivLon, DivLat, Lapla,..)
  !   は, その関数の作用を表している.
  !
  ! * 関数名の最後 (_wq, _nmr, _nr, _xyr, _wr, _w, _xy, _xv, _yr, _vr, _xr, 
  !   _x, _y, _v, _r, _a) は, 入力変数の形がスペクトルデータおよび
  !   格子点データであることを示している. 
  !   _wq      :: スペクトルデータ
  !   _xyr     :: 3 次元格子点データ
  !   _xyr_xyr :: 2 つの3 次元格子点データ, ...
  !   _xvr     :: 3 次元分散格子点データ
  !   _xvr_xvr :: 2 つの3 次元分散格子点データ, ...
  !
  !=== 各データの種類の説明
  !
  ! * xyr : 3 次元格子点データ(経度・緯度・動径)
  !   * 変数の種類と次元は real(8), dimension(0:im-1,1:jm,km). 
  !   * im, jm, km はそれぞれ経度, 緯度, 動径座標の格子点数であり, 
  !     サブルーチン wq_mpi_Initial にてあらかじめ設定しておく.
  !
  ! * xvr : 3 次元分散格子点データ(経度・緯度・動径)
  !   * 変数の種類と次元は real(8), dimension(0:im-1,jc,km). 
  !   * im, km はそれぞれ経度, 動径座標の格子点数であり, 
  !   * jc はこのプロセスで保有する緯度格子点数である. 
  !     サブルーチン wq_mpi_Initial を呼ぶと jc が設定される. 
  !
  ! * wq : スペクトルデータ
  !   * 変数の種類と次元は real(8), dimension(nc,0:lm). 
  !   * nm は球面調和函数の最大全波数, lm はチェビシェフ多項式の最大次数
  !     であり, サブルーチン wq_mpi_Initial にてあらかじめ設定しておく. 
  !   * 水平スペクトルデータの格納のされ方は関数 l_nm, nm_l によって調べる
  !     ことができる. 
  !   * 動径スペクトルデータの格納方法については aq_module.f90 を
  !     参照のこと. km < 2*im でなければならない. 
  !
  ! * nmr : 水平スペクトルデータの並んだ 3 次元配列.
  !   * 変数の種類と次元は real(8), dimension(0:nm,-nm:nm,km). 
  !   * 第 1 次元が水平全波数, 第 2 次元が帯状波数, 第 3 次元が動径座標を表す. 
  !   * nm は球面調和函数の最大全波数であり, サブルーチン wq_Initial にて
  !     あらかじめ設定しておく.
  !
  ! * nr : スペクトルデータの並んだ 2 次元配列.
  !   * 変数の種類と次元は real(8), dimension(0:nm,km). 
  !   * 第 1 次元が水平全波数を表す. nm は球面調和函数の最大全波数であり, 
  !     サブルーチン wq_Initial にてあらかじめ設定しておく.
  !
  ! * wr : 水平スペクトル, 動径格子点データ.
  !   * 変数の種類と次元は real(8), dimension(nc,km).
  !
  ! * wq_ で始まる関数が返す値はスペクトルデータに同じ.
  !
  ! * xyr_ で始まる関数が返す値は 3 次元格子点データに同じ.
  !
  ! * xvr_ で始まる関数が返す値は 3 次元分散格子点データに同じ.
  !
  ! * wr_ で始まる関数が返す値は水平スペクトル, 動径格子点データに同じ.
  !
  ! * スペクトルデータに対する微分等の作用とは, 対応する格子点データに
  !   微分などを作用させたデータをスペクトル変換したものことである.
  ! 
  !
  !== 変数・手続き群の要約
  !
  !==== 初期化 
  !
  ! wq_mpi_Initial :: スペクトル変換の格子点数, 波数, 領域の大きさの設定
  ! 
  !==== 座標変数
  !
  ! x_Lon, x_Lon_Weight,  ::  格子点座標(経度)と重みを格納した 1 次元配列
  ! y_Lat, y_Lat_Weight   ::  格子点座標(緯度)と重みを格納した 1 次元配列
  ! v_Lat, v_Lat_Weight   ::  分散格子点座標(緯度)と重みを格納した 1 次元配列
  ! r_Rad, r_Rad_Weight   ::  格子点座標(同形)と重みを格納した 1 次元配列
  ! xyr_Lon, xyr_Lat, xyr_Rad :: 格子点データの経度・緯度・動径座標(X,Y,Z) (格子点データ型 3 次元配列)
  ! xvr_Lon, xvr_Lat, xvr_Rad :: 格子点データの経度・緯度・動径座標(X,Y,Z) (分散格子点データ型 3 次元配列)
  !
  !==== 基本変換
  !
  ! xyr_wq, wq_xyr :: スペクトルデータと 3 次元格子データの間の変換
  !                   (球面調和函数, チェビシェフ変換)
  !
  ! xvr_wq, wq_xvr :: スペクトルデータと 3 次元分散格子データの間の変換
  !                   (球面調和函数, チェビシェフ変換)
  !
  ! xyr_wr, wr_xyr :: 3 次元格子データと水平スペクトル・動径格子データとの
  !                   間の変換 (球面調和函数)
  !
  ! xvr_wr, wr_xvr :: 3 次元分散格子データと水平スペクトル・動径格子データとの
  !                   間の変換 (球面調和函数)
  !
  ! wr_wq, wq_wr   :: スペクトルデータと水平スペクトル・動径格子データとの
  !                   間の変換 (チェビシェフ変換)
  !
  ! w_xy, xy_w     :: スペクトルデータと 2 次元水平格子データの
  !                   間の変換(球面調和函数変換) 
  !
  ! w_xv, xv_w     :: スペクトルデータと 2 次元水平分散格子データの
  !                   間の変換(球面調和函数変換) 
  !
  ! l_nm, nm_l     :: スペクトルデータの格納位置と全波数・帯状波数の変換 
  !
  !==== 微分
  !
  ! wq_RadDRad_wq       :: スペクトルデータに動径微分 r∂/∂r を作用させる
  ! wr_DivRad_wq        :: スペクトルデータに発散型動径微分
  !                        1/r^2 ∂/∂r r^2 = ∂/∂r + 2/r を作用させる
  ! wr_DivRad_xyr       :: 格子点データに発散型動径微分
  !                        1/r^2 ∂/∂r r^2 = ∂/∂r + 2/r を作用させる
  ! wr_DivRad_xvr       :: 格子点データに発散型動径微分
  !                        1/r^2 ∂/∂r r^2 = ∂/∂r + 2/r を作用させる
  ! wr_RotDRad_wq       :: スペクトルデータに回転型動径微分
  !                        1/r ∂/∂rr = ∂/∂r + 1/r を作用させる
  ! wr_RotDRad_wr       :: スペクトルデータに回転型動径微分
  !                        1/r ∂/∂rr = ∂/∂r + 1/r を作用させる
  ! wq_RotDRad_wr       :: スペクトルデータに回転型動径微分
  !                        1/r ∂/∂rr = ∂/∂r + 1/r を作用させる
  ! wq_Lapla_wq         :: スペクトルデータにラプラシアンを作用させる
  ! xyr_GradLon_wq      :: スペクトルデータに勾配型経度微分
  !                        1/rcosφ・∂/∂λを作用させる
  ! xvr_GradLon_wq      :: スペクトルデータに勾配型経度微分
  !                        1/rcosφ・∂/∂λを作用させる
  ! xyr_GradLat_wq      :: スペクトルデータに勾配型緯度微分
  !                        1/r・∂/∂φを作用させる
  ! xvr_GradLat_wq      :: スペクトルデータに勾配型緯度微分
  !                        1/r・∂/∂φを作用させる
  ! wr_DivLon_xyr       :: 格子データに発散型経度微分
  !                        1/rcosφ・∂/∂λを作用させる
  ! wr_DivLon_xvr       :: 格子データに発散型経度微分
  !                        1/rcosφ・∂/∂λを作用させる
  ! wr_DivLat_xyr       :: 格子データに発散型緯度微分
  !                        1/rcosφ・∂(g cosφ)/∂φを作用させる
  ! wr_DivLat_xvr       :: 格子データに発散型緯度微分
  !                        1/rcosφ・∂(g cosφ)/∂φを作用させる
  ! wr_Div_xyr_xyr_xyr  :: ベクトル成分である 3 つの格子データに
  !                        発散を作用させる
  ! xyr_Div_xyr_xyr_xyr :: ベクトル成分である 3 つの格子データに
  !                        発散を作用させる
  ! xyr_RotLon_wq_wq    :: ベクトル場の回転の経度成分を計算する
  ! xvr_RotLon_wq_wq    :: ベクトル場の回転の経度成分を計算する
  ! xyr_RotLat_wq_wq    :: ベクトル場の回転の緯度成分を計算する
  ! xvr_RotLat_wq_wq    :: ベクトル場の回転の緯度成分を計算する
  ! wr_RotRad_xyr_xyr   :: ベクトル場の回転の動径成分を計算する
  ! wr_RotRad_xvr_xvr   :: ベクトル場の回転の動径成分を計算する
  !
  !==== トロイダルポロイダル計算用微分
  !
  ! wq_KxRGrad_wq     :: スペクトルデータに経度微分
  !                      k×r・▽ = ∂/∂λを作用させる
  ! xyr_KGrad_wq      :: スペクトルデータに軸方向微分
  !                      k・▽ = cosφ/r ∂/∂φ + sinφ∂/∂r を作用させる
  ! xvr_KGrad_wq      :: スペクトルデータに軸方向微分
  !                      k・▽ = cosφ/r ∂/∂φ + sinφ∂/∂r を作用させる
  ! wq_L2_wq          :: スペクトルデータに L2 演算子 = -水平ラプラシアンを
  !                      作用させる
  ! wq_L2Inv_wq       :: スペクトルデータに L2 演算子の逆 = -逆水平ラプラシアン
  !                      を作用させる
  ! wq_QOperator_wq   :: スペクトルデータに演算子
  !                      Q=(k・▽-1/2(L2 k・▽+ k・▽L2)) を作用させる
  ! wq_QOperatorMPI_wq:: スペクトルデータに演算子
  !                      Q=(k・▽-1/2(L2 k・▽+ k・▽L2)) を作用させる
  ! wr_RadRot_xyr_xyr :: ベクトル v の渦度と動径ベクトル r の内積 r・(▽×v) を
  !                      計算する
  ! wr_RadRot_xvr_xvr :: ベクトル v の渦度と動径ベクトル r の内積 r・(▽×v) を
  !                      計算する
  ! wr_RadRotRot_xyr_xyr_xyr :: ベクトルの v の r・(▽×▽×v) を計算する
  ! wr_RadRotRot_xvr_xvr_xvr :: ベクトルの v の r・(▽×▽×v) を計算する
  ! wq_RadRotRot_xyr_xyr_xyr :: ベクトルの v の r・(▽×▽×v) を計算する
  ! wq_RadRotRot_xvr_xvr_xvr :: ベクトルの v の r・(▽×▽×v) を計算する
  ! wq_Potential2Vector      :: トロイダルポロイダルポテンシャルから
  !                             ベクトル場を計算する
  ! wq_Potential2VectorMPI   :: トロイダルポロイダルポテンシャルから
  !                             ベクトル場を計算する
  ! wq_Potential2Rotation    :: トロイダルポロイダルポテンシャルで表される
  !                             非発散ベクトル場の回転の各成分を計算する
  ! wq_Potential2RotationMPI :: トロイダルポロイダルポテンシャルで表される
  !                             非発散ベクトル場の回転の各成分を計算する
  !
  !==== ポロイダル/トロイダルモデル用スペクトル解析
  !
  ! nmr_ToroidalEnergySpectrum_wq, nr_ToroidalEnergySpectrum_wq  ::
  !     トロイダルポテンシャルからエネルギーの球面調和函数各成分を計算する
  ! nmr_PoloidalEnergySpectrum_wq, nr_PoloidalEnergySpectrum_wq  ::
  !     ポロイダルポテンシャルからエネルギーの球面調和函数各成分を計算する
  !
  !==== 境界値問題
  !
  ! wq_BoundaryTau, wr_BoundaryGrid, wq_Boundary                         ::
  !     ディリクレ, ノイマン境界条件を適用する(タウ法, 選点法)
  ! wq_TorBoundaryTau, wr_TorBoundaryGrid, wq_TorBoundary                ::
  !     速度トロイダルポテンシャルの境界条件を適用する(タウ法,選点法)       
  ! wq_LaplaPol2Pol_wq, wq_LaplaPol2PolTau_wq                            ::
  !     速度ポロイダルポテンシャルΦを▽^2Φから求める
  !     (入出力がそれぞれ格子点およびスペクトル係数)
  ! wq_TorMagBoundaryTau, wr_TorMagBoundaryGrid, wq_TorMagBoundary       ::
  !     磁場トロイダルポテンシャルの境界条件を適用する(タウ法, 選点法)
  ! wq_PolMagBoundaryTau, wr_PolMagBoundaryGrid, wq_PolMagBoundary       ::
  !     磁場トロイダルポテンシャル境界の境界条件を適用する(タウ法, 選点法)
  !
  !==== 積分・平均(3 次元データ)
  !
  ! IntLonLatRad_xyr, AvrLonLatRad_xyr :: 3 次元格子点データの
  !                                       全領域積分および平均
  ! IntLonLatRad_xvr, AvrLonLatRad_xvr :: 3 次元格子点データの
  !                                       全領域積分および平均
  ! r_IntLonLat_xyr, r_AvrLonLat_xyr   :: 3 次元格子点データの
  !                                       緯度経度(水平・球面)積分および平均
  ! r_IntLonLat_xvr, r_AvrLonLat_xvr   :: 3 次元格子点データの
  !                                       緯度経度(水平・球面)積分および平均
  ! y_IntLonRad_xyr, y_AvrLonRad_xyr   :: 3 次元格子点データの
  !                                       緯度動径積分および平均
  ! v_IntLonRad_xvr, v_AvrLonRad_xvr   :: 3 次元格子点データの
  !                                       緯度動径積分および平均
  ! x_IntLatRad_xyr,xr_AvrLatRad_xyr   :: 3 次元格子点データの
  !                                       経度動径(子午面)積分および平均
  ! x_IntLatRad_xvr,xr_AvrLatRad_xvr   :: 3 次元格子点データの
  !                                       経度動径(子午面)積分および平均
  ! yr_IntLon_xyr, yr_AvrLon_xyr       :: 3 次元格子点データの
  !                                       経度方向積分および平均
  ! vr_IntLon_xvr, vr_AvrLon_xvr       :: 3 次元格子点データの
  !                                       経度方向積分および平均
  ! xr_IntLat_xyr, xr_AvrLat_xyr       :: 3 次元格子点データの
  !                                       緯度方向積分および平均
  ! xr_IntLat_xvr, xr_AvrLat_xvr       :: 3 次元格子点データの
  !                                       緯度方向積分および平均
  ! xr_IntRad_xyr, xr_AvrRad_xyr       :: 3 次元格子点データの
  !                                       動径方向積分および平均
  ! xr_IntRad_xvr, xr_AvrRad_xvr       :: 3 次元格子点データの
  !                                       動径方向積分および平均
  !
  !==== 積分・平均(2 次元データ)
  !
  ! IntLonLat_xy, AvrLonLat_xy :: 2 次元格子点データの水平(球面)積分および平均
  ! IntLonLat_xv, AvrLonLat_xv :: 2 次元格子点データの水平(球面)積分および平均
  ! IntLonRad_xr, AvrLonRad_xr :: 2 次元(XZ)格子点データの経度動径積分
  !                               および平均
  ! IntLatRad_yr, AvrLatRad_yr :: 2 次元(YZ)格子点データの緯度動径(子午面)
  !                               積分および平均 
  ! IntLatRad_vr, AvrLatRad_vr :: 2 次元(YZ)格子点データの緯度動径(子午面)
  !                               積分および平均 
  ! y_IntLon_xy, y_AvrLon_xy   :: 水平 2 次元(球面)格子点データの経度方向
  !                               積分および平均
  ! v_IntLon_xv, v_AvrLon_xv   :: 水平 2 次元(球面)格子点データの経度方向
  !                               積分および平均
  ! x_IntLat_xy, x_AvrLat_xy   :: 水平2 次元(球面)格子点データの緯度方向積分
  !                               および平均
  ! x_IntLat_xv, x_AvrLat_xv   :: 水平2 次元(球面)格子点データの緯度方向積分
  !                               および平均
  ! r_IntLon_xr, r_AvrLon_xr   :: 2 次元(XZ)格子点データの経度方向積分および
  !                               平均
  ! x_IntRad_xr, x_AvrRad_xr   :: 2 次元(XZ)格子点データの動径方向積分および
  !                               平均
  ! r_IntLat_yr, r_AvrLat_yr   :: 2 次元(YZ)格子点データの緯度方向積分および
  !                               平均
  ! r_IntLat_vr, r_AvrLat_vr   :: 2 次元(YZ)格子点データの緯度方向積分および
  !                               平均
  ! y_IntRad_yr, y_AvrRad_yr   :: 2 次元(YZ)格子点データの動径方向積分および
  !                               平均                  
  ! v_IntRad_vr, v_AvrRad_vr   :: 2 次元(YZ)格子点データの動径方向積分および
  !                               平均                  
  !
  !==== 積分・平均(1 次元データ)
  !
  ! IntLon_x, AvrLon_x  :: 1 次元(X)格子点データの経度方向積分および平均
  ! IntLat_y, AvrLat_y  :: 1 次元(Y)格子点データの緯度方向積分および平均
  ! IntLat_v, AvrLat_v  :: 1 次元(Y)格子点データの緯度方向積分および平均
  ! IntRad_r, AvrRad_r  :: 1 次元(Z)格子点データの動径方向積分および平均
  !
  ! 
  use dc_message
  use lumatrix
  use wa_mpi_module_supack
  use aq_module, r_Rad => g_R, r_RAD_WEIGHT => g_R_WEIGHT, &
                 aq_ar => aq_ag, ar_aq => ag_aq, &
                 q_RadDRad_q => q_rDr_q, wq_RadDRad_wq => aq_rDr_aq, &
                 wq_Rad2_wq => aq_r2_aq, q_Rad2_q => q_r2_q, &
                 wq_Rad2Inv_wq => aq_r2Inv_aq, q_Rad2Inv_q => q_r2Inv_q
  use mpi
  implicit none
  integer :: ierr

  private

  public wq_mpi_Initial
  public jc
  public nc

  public x_Lon, x_Lon_Weight
  public v_Lat, v_Lat_Weight
  public r_Rad, r_Rad_Weight
  public l_nm, nm_l
  public xv_Lon, xv_Lat
  public xvr_Lon, xvr_Lat, xvr_Rad
  public wr_Rad
  public wq_VMiss

  public w_xv, xv_w
  public wq_RadDRad_wq, q_RadDRad_q, wr_wq, wq_wr
  public wq_Rad2_wq, q_Rad2_q, wq_Rad2Inv_wq, q_Rad2Inv_q
  public xvr_wq, wq_xvr, xvr_wr, wr_xvr
  public wr_DivRad_wq, wr_RotDRad_wq, wr_RotDRad_wr, wq_Lapla_wq
  public wq_RotDRad_wr
  public xvr_GradLon_wq, xvr_GradLat_wq
  public wr_DivLon_xvr, wr_DivLat_xvr, wr_DivRad_xvr
  public wr_Div_xvr_xvr_xvr, xvr_Div_xvr_xvr_xvr
  public xvr_RotLon_wq_wq, xvr_RotLat_wq_wq, wr_RotRad_xvr_xvr

  public vr_IntLon_xvr, xr_IntLat_xvr, xv_IntRad_xvr
  public x_IntLatRad_xvr, v_IntLonRad_xvr, r_IntLonLat_xvr
  public IntLonLatRad_xvr

  public x_IntLat_xv, v_IntLon_xv, IntLonLat_xv
  public r_IntLat_vr, v_IntRad_vr, IntLatRad_vr
  public r_IntLon_xr, x_IntRad_xr, IntLonRad_xr
  public IntLon_x, IntLat_v, IntRad_r

  public vr_AvrLon_xvr, xr_AvrLat_xvr, xv_AvrRad_xvr
  public x_AvrLatRad_xvr, v_AvrLonRad_xvr, r_AvrLonLat_xvr
  public AvrLonLatRad_xvr

  public x_AvrLat_xv, v_AvrLon_xv, AvrLonLat_xv
  public r_AvrLat_vr, v_AvrRad_vr, AvrLatRad_vr
  public r_AvrLon_xr, x_AvrRad_xr, AvrLonRad_xr
  public AvrLon_x, AvrLat_v, AvrRad_r

  public wq_KxRGrad_wq, wq_L2_wq, wq_L2Inv_wq
  public xvr_KGrad_wq, wq_QOperatorMPI_wq
  public wr_RadRot_xvr_xvr, wr_RadRotRot_xvr_xvr_xvr
  public wq_RadRotRot_xvr_xvr_xvr
  public wq_Potential2vectorMPI, wq_Potential2RotationMPI

  public nmr_ToroidalEnergySpectrum_wq, nr_ToroidalEnergySpectrum_wq
  public nmr_PoloidalEnergySpectrum_wq, nr_PoloidalEnergySpectrum_wq

  public wq_Boundary, wq_TorBoundary, wq_LaplaPol2Pol_wq ! wr_LaplaPol2Pol_wr
  public wq_TormagBoundary, wq_PolmagBoundary

  public wq_BoundaryTau, wq_TorBoundaryTau, wq_LaplaPol2PolTau_wq
  public wq_TormagBoundaryTau, wq_PolmagBoundaryTau

  public wr_BoundaryGrid, wr_TorBoundaryGrid
  public wr_TormagBoundaryGrid, wr_PolmagBoundaryGrid

  interface wq_Boundary
     module procedure wq_BoundaryTau
  end interface

  interface wq_TorBoundary
     module procedure wq_TorBoundaryTau
  end interface

  interface wq_LaplaPol2Pol_wq
     module procedure wq_LaplaPol2PolTau_wq
  end interface

  interface wq_TorMagBoundary
     module procedure wq_TorMagBoundaryTau
  end interface

  interface wq_PolMagBoundary
     module procedure wq_PolMagBoundaryTau
  end interface

  integer            :: im=64, jm=32, km=16  ! 格子点の設定(経度, 緯度, 動径)
  integer            :: nm=21, lm=31         ! 切断波数の設定(水平, 動径)
  real(8)            :: ra=1.0               ! 球半径
  real(8), parameter :: pi=3.1415926535897932385D0

  real(8), parameter :: alpha = 1.0D0      ! 展開多項式パラメター  0 < α <= 1
  real(8), parameter :: beta  = 2.0D0      ! 展開多項式パラメター  0 < β

  real(8), dimension(:,:,:), allocatable :: xvr_LON, xvr_LAT, xvr_RAD ! 座標
  real(8), dimension(:,:), allocatable   :: wr_RAD                    ! 座標
  integer, dimension(:), allocatable     :: nd             ! 重み r^n の指数

  real(8) :: wq_VMiss = -999.0        ! 欠損値
  real(8) :: Lat_Weight_Sum
  
  save im, jm, km, nm, lm, ra, nd, xvr_Lon, xvr_Lat, xvr_Rad, wr_Rad, Lat_Weight_Sum

  contains
  !--------------- 初期化 -----------------
   subroutine wq_mpi_Initial(i,j,k,n,l,r,np,wa_init)
     !
     ! スペクトル変換の格子点数, 波数, 動径座標の範囲を設定する.
     !
     ! 他の関数を呼ぶ前に, 最初にこのサブルーチンを呼んで初期設定を
     ! しなければならない. 
     !
     ! np に 1 より大きな値を指定すれば ISPACK の球面調和函数変換 
     ! OPENMP 並列計算ルーチンが用いられる. 並列計算を実行するには, 
     ! 実行時に環境変数 OMP_NUM_THREADS を np 以下の数字に設定する等の
     ! システムに応じた準備が必要となる. 
     !
     ! np に 1 より大きな値を指定しなければ並列計算ルーチンは呼ばれない.
     !
     !
     integer,intent(in) :: i              ! 格子点数(経度λ)
     integer,intent(in) :: j              ! 格子点数(緯度φ)
     integer,intent(in) :: k              ! 格子点数(動径 r)
     integer,intent(in) :: n              ! 切断波数(水平全波数)
     integer,intent(in) :: l              ! 切断波数(動径波数)

     real(8),intent(in) :: r              ! 球半径

     integer,intent(in), optional :: np   ! OPENMP での最大スレッド数
     logical,intent(in), optional :: wa_init   ! wa_initial スイッチ

     logical    :: wa_initialize=.true.   ! wa_initial スイッチ

     integer :: nmary(2), nl

     
     im = i  ; jm = j ; km = k
     nm = n  ; lm = l
     ra = r

     if ( present(wa_init) ) then
        wa_initialize = wa_init
     else
        wa_initialize = .true.
     endif

     if ( present(np) ) then
        call MessageNotify('M','wq_mpi_Initial', &
             'Optional argument "NP_IN" is dummy in wq_mpi_module_supack')
     endif

     if ( wa_initialize ) then
        call wa_mpi_Initial(nm,im,jm,km)
     endif

     allocate(nd(nc))

     do nl=1,nc
        nmary = nm_l(nl)
        if ( nmary(1) .ge. 0 ) then
           nd(nl) = nmary(1)   ! 水平 n 次は r^n から
        else
           nd(nl) = 0
        endif
     enddo

     call aq_Initial(km,lm,ra,alpha,beta,nd)
     
     allocate(xvr_Lon(0:im-1,jc,km))
     allocate(xvr_Lat(0:im-1,jc,km))
     allocate(xvr_Rad(0:im-1,jc,km))

     allocate(wr_Rad(nc,km))

     xvr_Lon = spread(xv_Lon,3,km)
     xvr_Lat = spread(xv_Lat,3,km)
     xvr_Rad = spread(spread(r_Rad,1,jc),1,im)

     wr_Rad = spread(r_Rad,1,nc)

     CALL MPI_ALLREDUCE(sum(v_Lat_weight),Lat_Weight_Sum,1,MPI_REAL8, &
                        MPI_SUM,MPI_COMM_WORLD,IERR)
     
     call MessageNotify('M','wq_mpi_initial','wq_mpi_module_supack (2016/03/31) is initialized')

  end subroutine wq_mpi_Initial

  !--------------- 基本変換 -----------------

    function xvr_wq(wq)
      !
      ! スペクトルデータから 3 次元分散格子点データへ(逆)変換する.
      !
      real(8), dimension(0:im-1,1:jc,km)                       :: xvr_wq
      !(out) 3 次元経度緯度動径格子点データ

      real(8), dimension(nc,0:lm), intent(in) :: wq
      !(in) 2 次元球面調和函数チェビシェフスペクトルデータ

      xvr_wq = xva_wa(wr_wq(wq))

    end function xvr_wq

    function wq_xvr(xvr)
      !
      ! 3 次元格子点データからスペクトルデータへ(正)変換する.
      !
      real(8), dimension(nc,0:lm)             :: wq_xvr
      !(out) 2 次元球面調和函数チェビシェフスペクトルデータ

      real(8), dimension(0:im-1,1:jc,km), intent(in)           :: xvr
      !(in) 3 次元経度緯度動径格子点データ

      wq_xvr = wq_wr(wa_xva(xvr))

    end function wq_xvr

    function xvr_wr(wr)
      !
      ! 水平スペクトル・動径格子点データから 3 次元分散格子点データへ(逆)変換する.
      !
      real(8), dimension(0:im-1,1:jc,km)                     :: xvr_wr
      !(out) 3 次元経度緯度動径格子点データ

      real(8), dimension(nc,km), intent(in) :: wr
      !(in) 2 次元球面調和函数スペクトル・動径格子点データ

      xvr_wr = xva_wa(wr)

    end function xvr_wr

    function wr_xvr(xvr)
      !
      ! 3 次元格子データから水平スペクトル・動径格子点データへ(正)変換する.
      !
      real(8), dimension(nc,km)               :: wr_xvr
      !(out) 2 次元球面調和函数スペクトル・動径格子点データ

      real(8), dimension(0:im-1,1:jc,km), intent(in)         :: xvr
      !(in) 3 次元経度緯度動径格子点データ

      wr_xvr = wa_xva(xvr)

    end function wr_xvr

    function wr_wq(wq)
      !
      ! スペクトルデータから水平スペクトル・動径格子点データへ(正)変換する.
      !
      real(8), dimension(nc,km) :: wr_wq
      !(out) 2 次元球面調和函数スペクトル・動径格子点データ
      real(8), dimension(nc,0:lm), intent(in) :: wq
      !(in) 2 次元球面調和函数チェビシェフスペクトルデータ

      wr_wq = ar_aq(wq)

    end function wr_wq

    function wq_wr(wr)
      !
      ! 水平スペクトル・動径格子点データからスペクトルデータへ(正)変換する.
      !
      real(8), dimension(nc,0:lm) :: wq_wr
      !(out) 2 次元球面調和函数チェビシェフスペクトルデータ
      real(8), dimension(nc,km), intent(in) :: wr
      !(in) 2 次元球面調和函数スペクトル・動径格子点データ

      wq_wr = aq_ar(wr)

    end function wq_wr

   !--------------- 微分計算 -----------------
    function wr_DivRad_wq(wq)
      !
      ! 入力スペクトルデータに発散型動径微分
      !
      !       1/r^2 ∂/∂r (r^2 .)= ∂/∂r + 2/r
      !
      ! を作用する.
      !
      ! スペクトルデータの発散型動径微分とは, 対応する格子点データに
      ! 発散型動径微分を作用させたデータのスペクトル変換のことである.
      !
      real(8), dimension(nc,0:lm), intent(in) :: wq
      !(in) 2 次元球面調和函数チェビシェフスペクトルデータ

      real(8), dimension(nc,km)             :: wr_DivRad_wq
      !(out) 発散型動径微分を作用された水平スペクトル動径格子点データ

      wr_DivRad_wq = wr_wq(wq_RadDRad_wq(wq))/wr_Rad + 2/wr_Rad * wr_wq(wq)

    end function wr_DivRad_wq

    function wr_DivRad_xvr(xvr)
      ! 
      ! 格子点データに発散型動径微分
      !
      !       1/r^2 ∂/∂r (r^2  = ∂/∂r + 2/= 1/r∂/∂(r.) + 1/r
      !
      ! を作用する.
      !
      ! この関数の入力は動径次数と水平全波数の偶奇性が異なっていることを
      ! 仮定している. 偶奇性が一致している場合には wr_DigRad_wq を使用すること.
      !
      real(8), dimension(0:im-1,jc,km), intent(in) :: xvr
      !(in) 2 次元球面調和函数チェビシェフスペクトルデータ

      real(8), dimension(nc,km)             :: wr_DivRad_xvr
      !(out) 発散型動径微分を作用された水平スペクトル動径格子点データ

      wr_DivRad_xvr = wr_wq(wq_RadDRad_wq(wq_xvr(xvr_Rad*xvr)))/wr_Rad**2 &
                    + 1/wr_Rad * wr_xvr(xvr)

    end function wr_DivRad_xvr

    function wr_RotDRad_wq(wq)
      !
      ! 入力スペクトルデータに回転型動径微分
      !
      !      1/r ∂(r.)/∂r = ∂(.)/∂r + (.)/r
      !
      ! を作用する.
      !
      ! スペクトルデータの回転型動径微分とは, 対応する格子点データに
      ! 回転型動径微分を作用させたデータのスペクトル変換のことである.
      !
      real(8), dimension(nc,0:lm), intent(in) :: wq
      !(in) 2 次元球面調和函数チェビシェフスペクトルデータ

      real(8), dimension(nc,km)               :: wr_RotDRad_wq
      !(out) 回転型動径微分を作用された水平スペクトル動径格子点データ

      wr_RotDRad_wq = wr_wq(wq_RadDrad_wq(wq))/wr_Rad + wr_wq(wq)/wr_Rad

    end function wr_RotDRad_wq

    function wr_RotDRad_wr(wr)
      !
      ! 入力スペクトルデータに回転型動径微分
      !
      !      1/r ∂(r.)/∂r = ∂(.)/∂r + (.)/r
      !
      ! を作用する.
      !
      ! スペクトルデータの回転型動径微分とは, 対応する格子点データに
      ! 回転型動径微分を作用させたデータのスペクトル変換のことである.
      !
      real(8), dimension(nc,km), intent(in) :: wr
      !(in) 2 次元球面調和函数チェビシェフスペクトルデータ

      real(8), dimension(nc,km)               :: wr_RotDRad_wr
      !(out) 回転型動径微分を作用された水平スペクトル動径格子点データ

      wr_RotDRad_wr = wr_wq(wq_RadDRad_wq(wq_wr(wr*wr_Rad)))/wr_Rad**2

    end function wr_RotDRad_wr
 
    function wq_RotDRad_wr(wr)
      !
      ! 入力スペクトルデータに回転型動径微分
      !
      !      1/r ∂(r.)/∂r = ∂(.)/∂r + (.)/r
      !
      ! を作用する.
      !
      ! スペクトルデータの回転型動径微分とは, 対応する格子点データに
      ! 回転型動径微分を作用させたデータのスペクトル変換のことである.
      !
      real(8), dimension(nc,km), intent(in) :: wr
      !(in) 2 次元球面調和函数チェビシェフスペクトルデータ

      real(8), dimension(nc,0:lm)               :: wq_RotDRad_wr
      !(out) 回転型動径微分を作用された水平スペクトル動径格子点データ

      wq_RotDRad_wr = wq_Rad2Inv_wq(wq_RadDRad_wq(wq_wr(wr*wr_Rad)))

    end function wq_RotDRad_wr

    function wq_Lapla_wq(wq)
      ! 入力スペクトルデータにラプラシアン
      !
      !     ▽^2 =   1/r^2 cos^2φ・∂^2/∂λ^2
      !            + 1/r^2 cosφ・∂/∂φ(cosφ∂/∂φ)
      !            + 1/r^2 ∂/∂r (r^2 ∂/∂r)
      !              [1/r^2 (r∂/∂r)(r∂/∂r) + 1/r^2(r∂/∂r)]
      !
      ! を作用する.
      !
      ! スペクトルデータのラプラシアンとは, 対応する格子点データに
      ! ラプラシアンを作用させたデータのスペクトル変換のことである.
      !
      real(8), dimension(nc,0:lm), intent(in) :: wq
      !(in) 2 次元球面調和函数チェビシェフスペクトルデータ

      real(8), dimension(nc,0:lm)             :: wq_Lapla_wq
      !(out) ラプラシアンを作用された水平スペクトルデータ

      wq_Lapla_wq = wq_Rad2Inv_wq(  wq_RadDRad_wq(wq_RadDRad_wq(wq)) &
                                  + wq_RadDRad_wq(wq)+ wa_Lapla_wa(wq) )

    end function wq_Lapla_wq
    
    function xvr_GradLon_wq(wq)
      !
      ! スペクトルデータに勾配型経度微分 1/rcosφ・∂/∂λ
      ! を作用させる.
      !
      real(8), dimension(nc,0:lm), intent(in) :: wq
      !(in) 2 次元球面調和函数チェビシェフスペクトルデータ

      real(8), dimension(0:im-1,1:jc,km)    :: xvr_GradLon_wq
      !(out) 勾配型経度微分を作用された 2 次元スペクトルデータ

      xvr_GradLon_wq = xva_GradLon_wa(wr_wq(wq))/xvr_Rad

    end function xvr_GradLon_wq

    function xvr_GradLat_wq(wq) 
      !
      ! スペクトルデータに勾配型経度微分 1/r ∂/∂φ を作用させる.
      !
      real(8), dimension(nc,0:lm), intent(in) :: wq
      !(in) 2 次元球面調和函数チェビシェフスペクトルデータ

      real(8), dimension(0:im-1,1:jc,km)    :: xvr_GradLat_wq
      !(out) 勾配型緯度微分を作用された 2 次元スペクトルデータ

      xvr_GradLat_wq = xva_GradLat_wa(wr_wq(wq))/xvr_Rad

    end function xvr_GradLat_wq

    function wr_DivLon_xvr(xvr)
      ! 
      ! 格子点データに発散型経度微分 1/rcosφ・∂/∂λ を作用させた
      ! スペクトルデータを返す.
      !
      real(8), dimension(0:im-1,1:jc,km), intent(in)     :: xvr
      !(in) 3 次元経度緯度動径格子点データ

      real(8), dimension(nc,km)         :: wr_DivLon_xvr
      !(out) 発散型経度微分を作用された水平スペクトル動径格子点データ

      wr_DivLon_xvr = wa_DivLon_xva(xvr/xvr_Rad)

    end function wr_DivLon_xvr

    function wr_DivLat_xvr(xvr)
      !
      ! 格子データに発散型緯度微分 1/rcosφ・∂(f cosφ)/∂φ を
      ! 作用させたスペクトルデータを返す.
      !
      real(8), dimension(0:im-1,1:jc,km), intent(in)     :: xvr
      !(in) 3 次元経度緯度動径格子点データ

      real(8), dimension(nc,km)         :: wr_DivLat_xvr
      !(out) 発散型緯度微分を作用された水平スペクトル動径格子点データ

      wr_DivLat_xvr = wa_DivLat_xva(xvr/xvr_Rad)

    end function wr_DivLat_xvr

    function wr_Div_xvr_xvr_xvr(xvr_Vlon,xvr_Vlat,xvr_Vrad)
      !
      ! べクトル成分である 3 つの格子データに発散を作用させた
      ! スペクトルデータを返す.
      !
      ! 第 1, 2 ,3 引数(u,v,w)がそれぞれベクトルの経度成分, 緯度成分, 
      ! 動径成分を表し, 発散は 
      !
      !      1/rcosφ・∂u/∂λ + 1/rcosφ・∂(v cosφ)/∂φ 
      !    + 1/r^2 ∂/∂r (r^2 w)
      !
      ! と計算される.
      !
      ! 第3成分の動径次数と水平全波数の偶奇性がずれていることを仮定している.
      !
      real(8), dimension(0:im-1,1:jc,km), intent(in) :: xvr_Vlon
      !(in) ベクトル場の経度成分

      real(8), dimension(0:im-1,1:jc,km), intent(in) :: xvr_Vlat
      !(in) ベクトル場の緯度成分

      real(8), dimension(0:im-1,1:jc,km), intent(in) :: xvr_Vrad
      !(in) ベクトル場の動径成分

      real(8), dimension(nc,km)     :: wr_Div_xvr_xvr_xvr
      !(out) ベクトル場の発散

      wr_Div_xvr_xvr_xvr =   wr_DivLon_xvr(xvr_Vlon) &
                           + wr_DivLat_xvr(xvr_Vlat) &
                           + wr_DivRad_xvr(xvr_Vrad)

    end function wr_Div_xvr_xvr_xvr

    function xvr_Div_xvr_xvr_xvr(xvr_Vlon,xvr_Vlat,xvr_Vrad)
      !
      ! ベクトル成分である 3 つの格子データに発散を作用させる.
      !
      ! 第 1, 2 ,3 引数(u,v,w)がそれぞれベクトルの経度成分, 緯度成分, 
      ! 動径成分を表す.
      !
      ! 極の特異性を回避するためにベクトル場に cosφ/r の重みをかけて
      ! 計算している. 
      !
      !      div V = (r/cosφ)・div (Vcosφ/r) + V_φtanφ/r + V_r/r
      ! 
      real(8), dimension(0:im-1,1:jc,km), intent(in) :: xvr_Vlon
      !(in) ベクトル場の経度成分

      real(8), dimension(0:im-1,1:jc,km), intent(in) :: xvr_Vlat
      !(in) ベクトル場の緯度成分

      real(8), dimension(0:im-1,1:jc,km), intent(in) :: xvr_Vrad
      !(in) ベクトル場の動径成分

      real(8), dimension(0:im-1,1:jc,km)             :: xvr_Div_xvr_xvr_xvr
      !(out) ベクトル場の発散

      xvr_Div_xvr_xvr_xvr &
           = xvr_Rad/cos(xvr_Lat) &
                * xvr_wr(wr_Div_xvr_xvr_xvr(xvr_VLon*cos(xvr_Lat)/xvr_Rad,  &
                                            xvr_VLat*cos(xvr_Lat)/xvr_Rad,  &
                                            xvr_VRad*cos(xvr_Lat)/xvr_Rad ))&
             + xvr_VLat*tan(xvr_Lat)/xvr_Rad &
             + xvr_VRad/xvr_Rad

    end function xvr_Div_xvr_xvr_xvr

    function xvr_RotLon_wq_wq(wq_Vrad,wq_Vlat) 
      !
      ! ベクトル場の動径成分, 緯度成分である第 1, 2 引数 Vrad, Vlat から
      ! 回転の経度成分 
      !
      !    1/r ∂Vrad/∂φ-1/r ∂(r Vlat)/∂r を計算する.
      !
      ! を計算する
      !
      real(8), dimension(nc,0:lm), intent(in) :: wq_Vrad
      !(in) ベクトル場の動径成分

      real(8), dimension(nc,0:lm), intent(in) :: wq_Vlat
      !(in) ベクトル場の緯度成分

      real(8), dimension(0:im-1,1:jc,km)                 :: xvr_RotLon_wq_wq
      !(out) ベクトル場の回転の経度成分

      xvr_RotLon_wq_wq =   xvr_GradLat_wq(wq_Vrad) &
                         - xvr_wr(wr_RotDRad_wq(wq_Vlat))

    end function xvr_RotLon_wq_wq

    function xvr_RotLat_wq_wq(wq_Vlon,wq_Vrad) 
      !
      ! ベクトル場の経度成分, 動径成分である第 1, 2 引数 Vlon, Vrad から
      ! 回転の緯度成分 
      !
      !    1/r ∂(r Vlon)/∂r - 1/rcosφ・∂Vrad/∂λ
      !
      ! を計算する.
      !
      real(8), dimension(nc,0:lm), intent(in) :: wq_Vlon
      !(in) ベクトル場の経度成分

      real(8), dimension(nc,0:lm), intent(in) :: wq_Vrad
      !(in) ベクトル場の動径成分

      real(8), dimension(0:im-1,1:jc,km)                 :: xvr_RotLat_wq_wq
      !(out) ベクトル場の回転の緯度成分

      xvr_RotLat_wq_wq =   xvr_wr(wr_RotDRad_wq(wq_Vlon)) &
                         - xvr_GradLon_wq(wq_Vrad) 

    end function xvr_RotLat_wq_wq

    function wr_RotRad_xvr_xvr(xvr_Vlat,xvr_Vlon) 
      !
      ! ベクトルの緯度成分, 経度成分である第 1, 2 引数 Vlat, Vlon に対して
      ! ベクトル場の回転の動径成分 
      !
      !    1/rcosφ・∂Vlat/∂λ - 1/rcosφ・∂(Vlon cosφ)/∂φ
      !
      ! を計算する.
      !
      real(8), dimension(0:im-1,1:jc,km), intent(in) :: xvr_Vlat
      !(in) ベクトル場の緯度成分

      real(8), dimension(0:im-1,1:jc,km), intent(in) :: xvr_Vlon
      !(in) ベクトル場の経度成分

      real(8), dimension(nc,km)       :: wr_RotRad_xvr_xvr
      !(out) ベクトル場の回転の動径成分

      wr_RotRad_xvr_xvr =   wr_DivLon_xvr(xvr_Vlat) &
                          - wr_DivLat_xvr(xvr_Vlon)

    end function wr_RotRad_xvr_xvr

  !--------------- 積分計算 -----------------
    !----(入力データ xvr)---
    function vr_IntLon_xvr(xvr)  ! 経度(帯状)積分
      !
      ! 3 次元格子点データの経度方向(帯状)積分.
      !
      ! 3 次元データ f(λ,φ,r) に対して ∫f(λ,φ,r)dλ を計算する.
      !
      real(8), dimension(0:im-1,1:jc,km), intent(in) :: xvr
      !(in) 3 次元経度緯度動径格子点データ

      real(8), dimension(1:jc,km)  :: vr_IntLon_xvr
      !(out) 経度方向(帯状)積分された 2 次元子午面格子点データ

      integer :: i

      vr_IntLon_xvr = 0.0d0
      do i=0,im-1
         vr_IntLon_xvr(:,:) = vr_IntLon_xvr(:,:) &
                       + xvr(i,:,:) * x_Lon_Weight(i)
      enddo
    end function vr_IntLon_xvr

    function xr_IntLat_xvr(xvr)
      !
      ! 3 次元格子点データの緯度方向域積分.
      !
      ! 3 次元データ f(λ,φ,r) に対して∫f(λ,φ,r) cosφ dφ を計算する.
      !
      real(8), dimension(0:im-1,1:jc,km), intent(in) :: xvr
      !(in) 3 次元経度緯度動径格子点データ

      real(8), dimension(0:im-1,km)  :: xr_IntLat_xvr   
      !(out) 緯度積分された 2 次元緯度動径格子点データ
      ! 緯度円格子点データ

      real(8), dimension(0:im-1,km)  :: xr_IntLatTMP
      integer :: j

      xr_IntLat_xvr = 0.0d0
      do j=1,jc
         xr_IntLat_xvr(:,:) = xr_IntLat_xvr(:,:) &
                       + xvr(:,j,:) * v_Lat_Weight(j)
      enddo
      xr_IntLatTmp=xr_IntLat_xvr
      CALL MPI_ALLREDUCE(xr_IntLatTMP,xr_IntLat_xvr,im*km,MPI_REAL8, &
                         MPI_SUM,MPI_COMM_WORLD,IERR)

    end function xr_IntLat_xvr

    function xv_IntRad_xvr(xvr)  ! 動径積分
      !
      ! 3 次元格子点データの動径方向域積分.
      !
      ! 3 次元データ f(λ,φ,r) に対して∫f(λ,φ,r) r^2dr を計算する.
      !
      real(8), dimension(0:im-1,1:jc,km), intent(in) :: xvr
      !(in) 3 次元経度緯度動径格子点データ

      real(8), dimension(0:im-1,1:jc)  :: xv_IntRad_xvr
      !(out) 動径積分された 2 次元経度緯度(水平, 球面)格子点データ

      integer :: k

      xv_IntRad_xvr = 0.0d0
      do k=1,km
         xv_IntRad_xvr(:,:) = xv_IntRad_xvr(:,:) &
                       + xvr(:,:,k) * r_Rad_Weight(k) 
      enddo

    end function xv_IntRad_xvr

    function x_IntLatRad_xvr(xvr)
      !
      ! 3 次元格子点データの緯度動径(子午面)積分
      !
      ! 3 次元データ f(λ,φ,r) に対して
      !
      !    ∫f(λ,φ,r) r^2cosφ dφdr 
      !
      ! を計算する.
      !
      real(8), dimension(0:im-1,1:jc,km), intent(in) :: xvr
      !(in) 3 次元経度緯度動径格子点データ

      real(8), dimension(0:im-1)     :: x_IntLatRad_xvr
      !(out) 緯度動径(子午面)積分された 1 次元経度格子点データ

      real(8), dimension(0:im-1)     :: x_IntLatRadTMP
      integer :: j, k

      x_IntLatRad_xvr = 0.0D0
      do k=1,km
         do j=1,jc
            x_IntLatRad_xvr = x_IntLatRad_xvr &
                 + xvr(:,j,k) * v_Lat_Weight(j) * r_Rad_Weight(k)
         enddo
      enddo

      x_IntLatRadTmp=x_IntLatRad_xvr
      CALL MPI_ALLREDUCE(x_IntLatRadTMP,x_IntLatRad_xvr,im,MPI_REAL8, &
                         MPI_SUM,MPI_COMM_WORLD,IERR)

    end function x_IntLatRad_xvr

    function v_IntLonRad_xvr(xvr)
      !
      ! 3 次元格子点データの経度動径(緯度円)積分.
      !
      ! 3 次元データ f(λ,φ,r) に対して∫f(λ,φ,r) r^2dλdr を計算する.
      !
      real(8), dimension(0:im-1,1:jc,km), intent(in) :: xvr
      !(in) 3 次元経度緯度動径格子点データ

      real(8), dimension(1:jc)       :: v_IntLonRad_xvr
      !(out) 経度動径(緯度円)積分された 1 次元緯度格子点データ

      integer :: i, k

      v_IntLonRad_xvr = 0.0D0
      do k=1,km
         do i=0,im-1
            v_IntLonRad_xvr = v_IntLonRad_xvr &
                 + xvr(i,:,k) * x_Lon_Weight(i) * r_Rad_Weight(k)
         enddo
      enddo

    end function v_IntLonRad_xvr

    function r_IntLonLat_xvr(xvr)  ! 緯度経度(水平)積分
      !
      ! 3 次元格子点データの緯度経度(水平, 球面)積分
      ! 
      ! 3 次元データ f(λ,φ,r) に対して
      !
      !    ∫f(λ,φ,r) cosφ dλdφ 
      !
      ! を計算する.
      !
      real(8), dimension(0:im-1,1:jc,km), intent(in) :: xvr
      !(in) 3 次元経度緯度動径格子点データ

      real(8), dimension(km)     :: r_IntLonLat_xvr
      !(out) 緯度経度(水平, 球面)積分された 1 次元動径格子点データ

      real(8), dimension(km)     :: r_IntLonLatTMP
      integer :: i, j

      r_IntLonLat_xvr = 0.0D0
      do j=1,jc
         do i=0,im-1
            r_IntLonLat_xvr = r_IntLonLat_xvr &
                 + xvr(i,j,:) * x_Lon_Weight(i) * v_Lat_Weight(j)
         enddo
      enddo

      r_IntLonLatTmp=r_IntLonLat_xvr
      CALL MPI_ALLREDUCE(r_IntLonLatTMP,r_IntLonLat_xvr,km,MPI_REAL8, &
                         MPI_SUM,MPI_COMM_WORLD,IERR)

    end function r_IntLonLat_xvr

    function IntLonLatRad_xvr(xvr) ! 緯度経度動径(全球)積分
      !
      ! 3 次元格子点データの緯度経度動径(全球)積分
      !
      ! 3 次元データ f(λ,φ,r) に対して
      !
      !     ∫f(λ,φ,r) r^2cosφ dλdφdr 
      !
      ! を計算する.
      !
      real(8), dimension(0:im-1,1:jc,km), intent(in) :: xvr 
      !(in) 3 次元経度緯度動径格子点データ

      real(8)                     :: IntLonLatRad_xvr 
      !(out) 全球積分値

      real(8)                     :: IntLonLatRadTMP
      integer :: i, j, k

      IntLonLatRad_xvr = 0.0D0
      do k=1,km
         do j=1,jc
            do i=0,im-1
               IntLonLatRad_xvr = IntLonLatRad_xvr &
                    + xvr(i,j,k) * x_Lon_Weight(i) &
                         * v_Lat_Weight(j) * r_Rad_Weight(k)
            enddo
         enddo
      enddo

      IntLonLatRadTmp=IntLonLatRad_xvr
      CALL MPI_ALLREDUCE(IntLonLatRadTMP,IntLonLatRad_xvr,1,MPI_REAL8, &
                         MPI_SUM,MPI_COMM_WORLD,IERR)

    end function IntLonLatRad_xvr

    !----(入力データ vr)---
    function r_IntLat_vr(vr)  ! 緯度積分
      !
      ! 2 次元(VR)格子点データの緯度方向域積分.
      !
      ! 2 次元データ f(φ,r) に対して∫f(φ,r) cosφ dφ を計算する.
      !
      real(8), dimension(1:jc,km), intent(in) :: vr
      !(in) 2 次元緯度動径(子午面)格子点データ

      real(8), dimension(km)  :: r_IntLat_vr
      !(out) 緯度積分された 1 次元動径格子点データ

      real(8), dimension(km)  :: r_IntLatTMP
      integer :: j

      r_IntLat_vr = 0.0d0
      do j=1,jc
         r_IntLat_vr(:) = r_IntLat_vr(:) + vr(j,:) * v_Lat_Weight(j)
      enddo
      r_IntLatTmp=r_IntLat_vr
      CALL MPI_ALLREDUCE(r_IntLatTMP,r_IntLat_vr,km,MPI_REAL8, &
                         MPI_SUM,MPI_COMM_WORLD,IERR)

    end function r_IntLat_vr

    function v_IntRad_vr(vr)  ! 動径積分
      !
      ! 2 次元(VR)格子点データの動径方向域積分.
      !
      ! 2 次元データ f(φ,r) に対して∫f(φ,r) r^2dr を計算する.
      !
      real(8), dimension(1:jc,km), intent(in) :: vr
      !(in) 2 次元緯度動径(子午面)格子点データ

      real(8), dimension(1:jc)  :: v_IntRad_vr
      !(out) 動径積分された 1 次元緯度格子点データ

      integer :: k

      v_IntRad_vr = 0.0d0
      do k=1,km
         v_IntRad_vr(:) = v_IntRad_vr(:) &
                       + vr(:,k) * r_Rad_Weight(k) 
      enddo

    end function v_IntRad_vr

    function IntLatRad_vr(vr)
      !
      ! 2 次元(VR)格子点データの緯度動径積分(子午面)および平均
      !
      ! 2 次元データ f(φ,r) に対して ∫f(φ,r) r^2cosφ dφdr を計算する.
      !
      real(8), dimension(1:jc,km), intent(in) :: vr
      !(in) 2 次元緯度動径(子午面)格子点データ

      real(8)                   :: IntLatRad_vr
      !(out) 積分値

      real(8)                   :: IntLatRadTMP
      integer :: j, k

      IntLatRad_vr = 0.0D0
      do k=1,km
         do j=1,jc
            IntLatRad_vr = IntLatRad_vr &
                 + vr(j,k) * v_Lat_Weight(j) * r_Rad_Weight(k)
         enddo
      enddo

      IntLatRadTmp=IntLatRad_vr
      CALL MPI_ALLREDUCE(IntLatRadTMP,IntLatRad_vr,1,MPI_REAL8, &
                         MPI_SUM,MPI_COMM_WORLD,IERR)

    end function IntLatRad_vr

    !----(入力データ xr)---
    function r_IntLon_xr(xr)
      !
      ! 2 次元(XR)格子点データの経度方向積分.
      !
      ! 2 次元データ f(λ,r) に対して ∫f(λ,r)dλ を計算する.
      !
      real(8), dimension(0:im-1,km), intent(in) :: xr
      !(in) 2 次元緯度動径格子点データ

      real(8), dimension(km)  :: r_IntLon_xr
      !(out) 経度積分された 1 次元動径格子点データ

      integer :: i

      r_IntLon_xr = 0.0d0
      do i=0,im-1
         r_IntLon_xr(:) = r_IntLon_xr(:) + xr(i,:) * x_Lon_Weight(i)
      enddo

    end function r_IntLon_xr

    function x_IntRad_xr(xr)
      !
      ! 2 次元(XR)格子点データの動径方向域積分.
      !
      ! 2 次元データ f(λ,r) に対して ∫f(λ,r) r^2dr を計算する.
      !
      real(8), dimension(0:im-1,km), intent(in) :: xr
      !(in) 2 次元緯度動径格子点データ

      real(8), dimension(0:im-1)  :: x_IntRad_xr
      !(out) 動径積分された 1 次元経度格子点データ

      integer :: k

      x_IntRad_xr = 0.0d0
      do k=1,km
         x_IntRad_xr(:) = x_IntRad_xr(:) &
                       + xr(:,k) * r_Rad_Weight(k)
      enddo

    end function x_IntRad_xr

    function IntLonRad_xr(xr)  ! 経度動径(緯度円)積分
      !
      ! 2 次元(XR)格子点データの経度動径積分
      !
      ! 2 次元データ f(λ,r) に対して∫f(λ,r) r^2dλdr を計算する.
      !
      real(8), dimension(0:im-1,km), intent(in) :: xr
      !(in) 2 次元緯度動径格子点データ

      real(8)                                 :: IntLonRad_xr
      !(out) 積分値

      integer :: i, k

      IntLonRad_xr = 0.0D0
      do k=1,km
         do i=0,im-1
            IntLonRad_xr = IntLonRad_xr &
                 + xr(i,k) * x_Lon_Weight(i) * r_Rad_Weight(k)
         enddo
      enddo

    end function IntLonRad_xr

    !----(入力データ z)---
    function IntRad_r(z)  ! 動径積分
      !
      ! 1 次元(Z)格子点データの動径方向域積分.
      !
      ! 1 次元データ f(r) に対して ∫f(r) r^2dr を計算する.
      !
      real(8), dimension(km), intent(in) :: z
      !(in) 1 次元動径格子点データ

      real(8)                              :: IntRad_r
      !(out) 積分値

      integer :: k

      IntRad_r = 0.0d0
      do k=1,km
         IntRad_r = IntRad_r + z(k) * r_Rad_Weight(k)
      enddo

    end function IntRad_r
     
  !--------------- 平均計算 -----------------
    !----(入力データ xvr)---
    function vr_AvrLon_xvr(xvr)  ! 経度(帯状)積分
      !
      ! 3 次元格子点データの経度方向(帯状)平均.
      !
      ! 3 次元データ f(λ,φ,r) に対して ∫f(λ,φ,r)dλ/2π を計算する.
      !
      real(8), dimension(0:im-1,1:jc,km), intent(in) :: xvr
      !(in) 3 次元経度緯度動径格子点データ

      real(8), dimension(1:jc,km)  :: vr_AvrLon_xvr
      !(out) 経度方向(帯状)平均された 2 次元子午面格子点データ

      vr_AvrLon_xvr = vr_IntLon_xvr(xvr)/sum(x_Lon_Weight)

    end function vr_AvrLon_xvr

    function xr_AvrLat_xvr(xvr)  ! 緯度積分
      !
      ! 3 次元格子点データの緯度方向域平均.
      !
      ! 3 次元データ f(λ,φ,r) に対して ∫f(λ,φ,r)cosφ dφ/2 を計算する.
      !
      real(8), dimension(0:im-1,1:jc,km), intent(in) :: xvr
      !(in) 3 次元経度緯度動径格子点データ

      real(8), dimension(0:im-1,km)  :: xr_AvrLat_xvr
      !(out) 緯度平均された 2 次元緯度動径格子点データ

      xr_AvrLat_xvr = xr_IntLat_xvr(xvr)/Lat_Weight_Sum

    end function xr_AvrLat_xvr

    function xv_AvrRad_xvr(xvr)
      !
      ! 3 次元格子点データの動径方向域平均.
      !
      ! 3 次元データ f(λ,φ,r) に対して 
      !
      !    ∫f(λ,φ,r) r^2dr/((r[o]^3-r[i]^3)/3) 
      !
      ! を計算する.
      !
      real(8), dimension(0:im-1,1:jc,km), intent(in) :: xvr
      !(in) 3 次元経度緯度動径格子点データ

      real(8), dimension(0:im-1,1:jc)  :: xv_AvrRad_xvr
      !(out) 動径平均された 2 次元経度緯度(水平, 球面)格子点データ
      ! 水平格子点データ

      xv_AvrRad_xvr = xv_IntRad_xvr(xvr)/sum(r_Rad_Weight)

    end function xv_AvrRad_xvr

    function x_AvrLatRad_xvr(xvr)  ! 緯度動径(子午面)積分
      !
      ! 3 次元格子点データの緯度動径(子午面)平均
      !
      ! 3 次元データ f(λ,φ,r) に対して
      !
      !    ∫f(λ,,r) r^2cosφ dφdr /(2(r[o]^3-r[i]^3)/3) 
      !
      ! を計算する.
      !
      real(8), dimension(0:im-1,1:jc,km), intent(in) :: xvr
      !(in) 3 次元経度緯度動径格子点データ

      real(8), dimension(0:im-1)     :: x_AvrLatRad_xvr
      !(out) 緯度動径(子午面)平均された 1 次元経度格子点データ

      x_AvrLatRad_xvr = x_IntLatRad_xvr(xvr) &
                   /( Lat_Weight_Sum*sum(r_Rad_Weight) )

    end function x_AvrLatRad_xvr

    function v_AvrLonRad_xvr(xvr)  ! 経度動径(緯度円)積分
      !
      ! 3 次元格子点データの経度動径(緯度円)平均.
      !
      ! 3 次元データ f(λ,φ,r) に対して
      !
      !     ∫f(λ,φ,r) r^2dλdr /(2π(r[o]^3-r[i]^3)/3) 
      !
      ! を計算する.
      !
      real(8), dimension(0:im-1,1:jc,km), intent(in) :: xvr
      !(in) 3 次元経度緯度動径格子点データ

      real(8), dimension(1:jc)       :: v_AvrLonRad_xvr
      !(out) 経度動径(緯度円)平均された 1 次元緯度格子点データ

      v_AvrLonRad_xvr = v_IntLonRad_xvr(xvr) &
                 /(sum(x_Lon_Weight)*sum(r_Rad_Weight))

    end function v_AvrLonRad_xvr

    function r_AvrLonLat_xvr(xvr)  ! 緯度経度(水平)積分
      !
      ! 3 次元格子点データの緯度経度(水平, 球面)積分
      ! 
      ! 3 次元データ f(λ,φ,r) に対して
      !
      !    ∫f(λ,φ,r) cosφ dλdφ /4π 
      !
      ! を計算する.
      !
      real(8), dimension(0:im-1,1:jc,km), intent(in) :: xvr
      !(in) 3 次元経度緯度動径格子点データ

      real(8), dimension(km)     :: r_AvrLonLat_xvr
      !(out) 緯度経度(水平, 球面)平均された 1 次元動径格子点データ

      r_AvrLonLat_xvr = r_IntLonLat_xvr(xvr) &
                 /(sum(x_Lon_Weight)*Lat_Weight_Sum)

    end function r_AvrLonLat_xvr

    function AvrLonLatRad_xvr(xvr) ! 緯度経度動径(全球)積分
      !
      ! 3 次元格子点データの緯度経度動径(全球)積分
      !
      ! 3 次元データ f(λ,φ,r) に対して
      !
      !    ∫f(λ,φ,r) r^2cosφ dλdφdr /(4π(r[o]^3-r[i]^3)/3) 
      !
      ! を計算する.
      !
      real(8), dimension(0:im-1,1:jc,km), intent(in) :: xvr
      !(in) 3 次元経度緯度動径格子点データ

      real(8)                     :: AvrLonLatRad_xvr
      !(out) 全球平均値

      AvrLonLatRad_xvr = IntLonLatRad_xvr(xvr) &
            /(sum(x_Lon_Weight)*Lat_Weight_Sum * sum(r_Rad_Weight))

    end function AvrLonLatRad_xvr

    !----(入力データ vr)---
    function r_AvrLat_vr(vr)
      !
      ! 2 次元(VR)格子点データの緯度方向域平均.
      !
      ! 2 次元データ f(φ,r) に対して ∫f(φ,r) cosφ dφ/2 を計算する.
      !
      real(8), dimension(1:jc,km), intent(in) :: vr
      !(in) 2 次元緯度動径(子午面)格子点データ

      real(8), dimension(km)  :: r_AvrLat_vr
      !(out) 緯度平均された 1 次元動径格子点データ

      r_AvrLat_vr = r_IntLat_vr(vr)/Lat_Weight_Sum

    end function r_AvrLat_vr

    function v_AvrRad_vr(vr)
      !
      ! 2 次元(VR)格子点データの動径方向域平均.
      !
      ! 2 次元データ f(φ,r) に対して ∫f(φ,r) r^2dr /((r[o]^3-r[i]^3)/3) 
      ! を計算する.
      !
      real(8), dimension(1:jc,km), intent(in) :: vr
      !(in) 2 次元緯度動径(子午面)格子点データ

      real(8), dimension(1:jc)  :: v_AvrRad_vr
      !(out) 動径平均された 1 次元緯度格子点データ

      v_AvrRad_vr = v_IntRad_vr(vr)/sum(r_Rad_Weight)

    end function v_AvrRad_vr

    function AvrLatRad_vr(vr)  ! 緯度動径(子午面)積分
      !
      ! 2 次元(VR)格子点データの緯度動径(子午面)平均
      !
      ! 2 次元データ f(φ,r) に対して
      !
      !    ∫f(φ,r) r^2cosφ dφdr /(2(r[o]^3-r[i]^3)/3) 
      !
      ! を計算する.
      !
      real(8), dimension(1:jc,km), intent(in) :: vr
      !(in) 2 次元緯度動径(子午面)格子点データ

      real(8)                   :: AvrLatRad_vr
      !(out) 平均値

      AvrLatRad_vr = IntLatRad_vr(vr)/(Lat_Weight_Sum*sum(r_Rad_Weight))

    end function AvrLatRad_vr

    !----(入力データ xr)---
    function r_AvrLon_xr(xr)  ! 経度(帯状)積分
      !
      ! 2 次元(XR)格子点データの経度方向平均.
      !
      ! 2 次元データ f(λ,r) に対して ∫f(λ,r)dλ/2π を計算する.
      !
      real(8), dimension(0:im-1,km), intent(in) :: xr
      !(in) 2 次元緯度動径格子点データ

      real(8), dimension(km)  :: r_AvrLon_xr
      !(out) 経度平均された 1 次元動径格子点データ

      r_AvrLon_xr = r_IntLon_xr(xr)/sum(x_Lon_Weight)

    end function r_AvrLon_xr

    function x_AvrRad_xr(xr)  ! 動径積分
      !
      ! 2 次元(XR)格子点データの動径方向域平均.
      !
      ! 2 次元データ f(λ,r) に対して
      !
      !   ∫f(λ,r) r^2dr /((r[o]^3-r[i]^3)/3)
      !
      ! を計算する.
      !
      real(8), dimension(0:im-1,km), intent(in) :: xr
      !(in) 2 次元緯度動径格子点データ

      real(8), dimension(0:im-1)  :: x_AvrRad_xr
      !(out) 動径平均された 1 次元経度格子点データ

      x_AvrRad_xr = x_IntRad_xr(xr)/sum(r_Rad_Weight)

    end function x_AvrRad_xr
 
    function AvrLonRad_xr(xr)  ! 経度動径(緯度円)積分
      !
      ! 2 次元(XR)格子点データの経度動径平均
      !
      ! 2 次元データ f(λ,r) に対して
      !
      !    ∫f(λ,r) r^2dλdr /(2π(r[o]^3-r[i]^3)/3)
      !
      ! を計算する.
      !
      real(8), dimension(0:im-1,km), intent(in) :: xr
      ! (in) 2 次元格子点データ
      real(8)                                 :: AvrLonRad_xr
      ! 積分値

      AvrLonRad_xr = IntLonRad_xr(xr)/(sum(x_Lon_Weight)*sum(r_Rad_Weight))

    end function AvrLonRad_xr

    !----(入力データ z)---
    function AvrRad_r(z)
      !
      ! 1 次元(Z)格子点データの動径方向域平均.
      !
      ! 1 次元データ f(r) に対して ∫f(r) r^2dr /((r[o]^3-r[i]^3)/3) を
      ! 計算する.
      !
      real(8), dimension(km), intent(in) :: z
      !(in) 1 次元動径格子点データ
      real(8)                            :: AvrRad_r
      !(out) 平均値

      AvrRad_r = IntRad_r(z)/sum(r_Rad_Weight)

    end function AvrRad_r
     
  !--------------- ポロイダル/トロイダルモデル用微分 -----------------

    function wq_KxRGrad_wq(wq)
      !
      ! 入力スペクトルデータに経度微分 k×r・▽ = ∂/∂λを作用する.
      !
      real(8), dimension(nc,0:lm), intent(in) :: wq
      !(in) 2 次元球面調和函数チェビシェフスペクトルデータ

      real(8), dimension(nc,0:lm)             :: wq_KxRGrad_wq
      !(out) 経度微分を作用された 2 次元スペクトルデータ

      wq_KxRGrad_wq =  wa_Dlon_wa(wq)

    end function wq_KxRGrad_wq

    function xvr_KGrad_wq(wq)    ! k・▽ = cosφ/r ∂/∂φ + sinφ∂/∂r
      !
      ! 入力スペクトルデータに対応する格子データに軸方向微分 
      !
      !    k・▽ = cosφ/r ∂/∂φ + sinφ∂/∂r 
      !
      ! を作用させた格子データが返される. 
      ! ここでベクトル k は球の中心から北極向きの単位ベクトルである.
      !
      real(8), dimension(nc,0:lm), intent(in) :: wq
      !(in) 2 次元球面調和函数チェビシェフスペクトルデータ

      real(8), dimension(0:im-1,1:jc,km)                 :: xvr_KGrad_wq
      !(out) 軸方向微分を作用された 2 次元スペクトルデータ

      xvr_KGrad_wq =  cos(xvr_Lat)*xvr_GradLat_wq(wq) &
                    + sin(xvr_Lat)*xvr_wq(wq_RadDRad_wq(wq))/xvr_Rad

    end function xvr_KGrad_wq

    function wq_L2_wq(wq)
      !
      ! 入力スペクトルデータに L^2 演算子(=-水平ラプラシアン)を作用する.
      !
      ! L^2 演算子は単位球面上の水平ラプラシアンの逆符号にあたる.
      !  入力スペクトルデ ータに対応する格子点データに演算子
      !
      !     L^2 = -1/cos^2φ・∂^2/∂λ^2 - 1/cosφ・∂/∂φ(cosφ∂/∂φ)
      !
      ! を作用させたデータのスペクトル変換が返される.
      !
      real(8), dimension(nc,0:lm), intent(in) :: wq
      !(in) 2 次元球面調和函数チェビシェフスペクトルデータ

      real(8), dimension(nc,0:lm)             :: wq_L2_wq
      !(out) L^2 演算子を作用された 2 次元スペクトルデータ

      wq_L2_wq = -wa_Lapla_wa(wq)

    end function wq_L2_wq

    function wq_L2Inv_wq(wq)
      !
      ! 入力スペクトルデータに L^2 演算子の逆演算(-逆水平ラプラシアン)を
      ! 作用する.
      !
      ! スペクトルデータに L^2 演算子を作用させる関数 wq_L2_wq の逆計算を
      ! 行う関数である.
      !
      real(8), dimension(nc,0:lm), intent(in) :: wq
      !(in) 2 次元球面調和函数チェビシェフスペクトルデータ

      real(8), dimension(nc,0:lm)             :: wq_L2Inv_wq
      !(out) L^2 演算子の逆演算を作用された 2 次元スペクトルデータ

      wq_L2Inv_wq = -wa_LaplaInv_wa(wq)

    end function wq_L2Inv_wq
     
    function wq_QOperatorMPI_wq(wq)
      !
      ! 入力スペクトルデータに対応する格子点データに演算子
      !
      !    Q=(k・▽-1/2(L2 k・▽+ k・▽L2))
      !
      ! を作用させたデータのスペクトル変換が返される.
      !
      real(8), dimension(nc,0:lm), intent(in) :: wq
      !(in) 2 次元球面調和函数チェビシェフスペクトルデータ

      real(8), dimension(nc,0:lm)             :: wq_QOperatorMPI_wq
      !(out) Q 演算子を作用された 2 次元スペクトルデータ

      wq_QOperatorMPI_wq = &
             wq_xvr(xvr_KGrad_wq(wq) - xvr_KGrad_wq(wq_L2_wq(wq))/2) &
           - wq_L2_wq(wq_xvr(xvr_KGrad_wq(wq)))/2

    end function wq_QOperatorMPI_wq

    function wr_RadRot_xvr_xvr(xvr_VLON,xvr_VLAT)  ! r・(▽×v)
      !
      ! ベクトルの渦度と動径ベクトルの内積 r・(▽×v) を計算する.
      !
      ! 第 1, 2 引数(v[λ], v[φ])がそれぞれベクトルの経度成分, 緯度成分を表す.
      !
      !    r・(▽×v) = 1/cosφ・∂v[φ]/∂λ - 1/cosφ・∂(v[λ] cosφ)/∂φ
      !
      ! のスペクトル データが返される.
      !
      real(8), dimension(0:im-1,1:jc,km), intent(in) :: xvr_VLON
      !(in) ベクトルの経度成分

      real(8), dimension(0:im-1,1:jc,km), intent(in) :: xvr_VLAT
      !(in) ベクトルの緯度成分

      real(8), dimension(nc,km)     :: wr_RadRot_xvr_xvr
      !(out) ベクトルの渦度と動径ベクトルの内積

      wr_RadRot_xvr_xvr = wa_DivLon_xva(xvr_VLAT) - wa_DivLat_xva(xvr_VLON)
      
    end function wr_RadRot_xvr_xvr

    function wr_RadRotRot_xvr_xvr_xvr(xvr_VLON,xvr_VLAT,xvr_VRAD) 
      ! 
      ! ベクトル v に対して r・(▽×▽×v) を計算する.
      !
      ! 第 1, 2, 3 引数(v[λ], v[φ], v[r])がそれぞれベクトルの経度成分, 
      ! 緯度成分, 動径成分を表す. 
      !
      !    r・(▽×▽×v)  = 1/r ∂/∂r (r・( 1/cosφ・∂v[λ]/∂λ 
      !                                  + 1/cosφ・∂(v[φ] cosφ)/∂φ ) ) 
      !                     + L^2 v[r]/r 
      !
      ! のスペクトルデータが返される.
      !
      real(8), dimension(0:im-1,1:jc,km), intent(in) :: xvr_VLON
      !(in) ベクトルの経度成分

      real(8), dimension(0:im-1,1:jc,km), intent(in) :: xvr_VLAT
      !(in) ベクトルの緯度成分

      real(8), dimension(0:im-1,1:jc,km), intent(in) :: xvr_VRAD
      !(in) ベクトルの動径成分

      real(8), dimension(nc,km)     :: wr_RadRotRot_xvr_xvr_xvr
      !(out) ベクトル v の r・(▽×▽×v) 

      wr_RadRotRot_xvr_xvr_xvr = &
                   wr_RotDRad_wr( &
                      wa_DivLon_xva(xvr_VLON)+ wa_DivLat_xva(xvr_VLAT)) &
             - wa_Lapla_wa(wr_xvr(xvr_VRAD/xvr_RAD))

    end function wr_RadRotRot_xvr_xvr_xvr

    function wq_RadRotRot_xvr_xvr_xvr(xvr_VLON,xvr_VLAT,xvr_VRAD) 
      ! 
      ! ベクトル v に対して r・(▽×▽×v) を計算する.
      !
      ! 第 1, 2, 3 引数(v[λ], v[φ], v[r])がそれぞれベクトルの経度成分, 
      ! 緯度成分, 動径成分を表す. 
      !
      !    r・(▽×▽×v)  = 1/r ∂/∂r (r・( 1/cosφ・∂v[λ]/∂λ 
      !                                  + 1/cosφ・∂(v[φ] cosφ)/∂φ ) ) 
      !                     + L^2 v[r]/r 
      !
      ! のスペクトルデータが返される.
      !
      real(8), dimension(0:im-1,1:jc,km), intent(in) :: xvr_VLON
      !(in) ベクトルの経度成分

      real(8), dimension(0:im-1,1:jc,km), intent(in) :: xvr_VLAT
      !(in) ベクトルの緯度成分

      real(8), dimension(0:im-1,1:jc,km), intent(in) :: xvr_VRAD
      !(in) ベクトルの動径成分

      real(8), dimension(nc,0:lm)     :: wq_RadRotRot_xvr_xvr_xvr
      !(out) ベクトル v の r・(▽×▽×v) 

      wq_RadRotRot_xvr_xvr_xvr = &
                   wq_RotDRad_wr( &
                      wa_DivLon_xva(xvr_VLON)+ wa_DivLat_xva(xvr_VLAT)) &
             - wa_Lapla_wa(wq_xvr(xvr_VRAD/xvr_RAD))

    end function wq_RadRotRot_xvr_xvr_xvr

    subroutine wq_Potential2VectorMPI(&
         xvr_VLON,xvr_VLAT,xvr_VRAD,wq_TORPOT,wq_POLPOT)
      !
      ! トロイダルポロイダルポテンシャルΨ,Φで表される非発散ベクトル場
      !
      !     v = ▽x(Ψr) + ▽x▽x(Φr) 
      !
      ! の各成分を計算する
      !
      real(8), dimension(0:im-1,1:jc,km)     :: xvr_VLON
      !(out) ベクトル場の経度成分

      real(8), dimension(0:im-1,1:jc,km)     :: xvr_VLAT
      !(out) ベクトル場の緯度成分

      real(8), dimension(0:im-1,1:jc,km)     :: xvr_VRAD
      !(out) ベクトル場の動径成分

      real(8), dimension(nc,0:lm), intent(in) :: wq_TORPOT
      !(in) トロイダルポテンシャル

      real(8), dimension(nc,0:lm), intent(in) :: wq_POLPOT
      !(in) ポロイダルポテンシャル

      xvr_VLON =   xvr_RAD * xvr_GradLat_wq(wq_TORPOT) &
                 + xva_GradLon_wa(wr_RotDRad_wq(wq_POLPOT))
      xvr_VLAT = - xvr_RAD * xvr_GradLon_wq(wq_TORPOT) &
                 + xva_GradLat_wa(wr_RotDRad_wq(wq_POLPOT))
      xvr_VRAD = xvr_wq(wq_L2_wq(wq_POLPOT))/xvr_RAD

    end subroutine wq_Potential2VectorMPI

    subroutine wq_Potential2RotationMPI(&
       xvr_RotVLON,xvr_RotVLAT,xvr_RotVRAD,wq_TORPOT,wq_POLPOT)
      !
      ! トロイダルポロイダルポテンシャルΨ,Φで表される非発散ベクトル場
      !
      !     v = ▽x(Ψr) + ▽x▽x(Φr) 
      !
      ! に対して, その回転
      !
      !     ▽xv = ▽x▽x(Ψr) + ▽x▽x▽x(Φr) = ▽x▽x(Ψr) - ▽x((▽^2Φ)r)
      !
      ! を計算する. 
      
      ! ベクトル場の回転
      real(8), dimension(0:im-1,1:jc,km), intent(OUT) :: xvr_RotVLON
      !(out) 回転の経度成分

      real(8), dimension(0:im-1,1:jc,km), intent(OUT) :: xvr_RotVLAT
      !(out) 回転の緯度成分

      real(8), dimension(0:im-1,1:jc,km), intent(OUT) :: xvr_RotVRAD
      !(out) 回転の動径成分

      ! 入力ベクトル場を表すポテンシャル
      real(8), dimension(nc,0:lm), intent(in) :: wq_TORPOT
      !(in) トロイダルポテンシャル

      real(8), dimension(nc,0:lm), intent(in) :: wq_POLPOT
      !(in) ポロイダルポテンシャル

      call wq_Potential2VectorMPI( &
           xvr_RotVLON,xvr_RotVLAT,xvr_RotVRAD, &
           -wq_Lapla_wq(wq_POLPOT), wq_TORPOT)

    end subroutine wq_Potential2RotationMPI

  !--------------- ポロイダル/トロイダルモデル用スペクトル解析 ----------------

    function nmr_ToroidalEnergySpectrum_wq(wq_TORPOT)
      !
      ! トロイダルポテンシャルから, トロイダルエネルギーの
      ! 球面調和函数全波数 n, 帯状波数 m の各成分を計算する
      !
      !  * 全波数 n, 帯状波数 m のトロイダルポテンシャルのスペクトル成分
      !    ψ(n,m,r)から全波数 n, 帯状波数 m 成分のトロイダルエネルギー
      !    スペクトルは  (1/2)n(n+1)4πr^2|ψ(n,m,r)|^2  と計算される.
      !
      !  * 全てのエネルギースペクトル成分の和を動径積分したもの(r^2の重み無し)
      !    が球殻内での全エネルギーに等しい.
      !
      !  * データの存在しない全波数 n, 帯状波数 m の配列には欠損値が格納される.
      !    wq_VMiss によって設定できる (初期値は -999.0)
      !
      real(8), dimension(nc,0:lm), intent(in) :: wq_TORPOT
      !(in) トロイダルポテンシャル

      real(8), dimension(0:nm,-nm:nm,km) :: nmr_ToroidalEnergySpectrum_wq
      !(out) エネルギースペクトルトロイダル成分

      real(8), dimension(0:nm,-nm:nm,km) :: nmr_Work ! 作業領域

      real(8), dimension(nc,km) ::wr_DATA   ! 作業領域

      integer :: l, n, m, nm_ary(2)

      wr_DATA = wr_wq(wq_TORPOT)

      nmr_work = 0.0D0
      do l=1,nc
         nm_ary = nm_l(l)
         n = nm_ary(1) ; m = abs(nm_ary(2))
         if ( n .ge. 0 ) then
            if ( m == 0 ) then
               nmr_Work(n,0,:) = nmr_Work(n,0,:) &
                    +  0.5 * n*(n+1) * (4*pi) * r_Rad**2 * wr_Data(l,:)**2
            else
               nmr_Work(n,m,:) = nmr_Work(n,m,:) &
                    +  0.5 * n*(n+1) * (4*pi) * r_Rad**2 * wr_Data(l,:)**2
               nmr_Work(n,-m,:) = nmr_Work(n,-m,:) &
                    +  0.5 * n*(n+1) * (4*pi) * r_Rad**2 * wr_Data(l,:)**2
            endif
         endif
      enddo

      CALL MPI_ALLREDUCE(nmr_Work,nmr_ToroidalEnergySpectrum_wq, &
                         (nm+1)*(2*nm+1)*km, &
                         MPI_REAL8,MPI_SUM,MPI_COMM_WORLD,IERR)

      do m=1,nm
         do n=0,m-1
            nmr_ToroidalEnergySpectrum_wq(n,m,:)  = wq_Vmiss
            nmr_ToroidalEnergySpectrum_wq(n,-m,:) = wq_Vmiss
         enddo
      enddo

    end function nmr_ToroidalEnergySpectrum_wq
 
    function nr_ToroidalEnergySpectrum_wq(wq_TORPOT)
      !
      ! トロイダルポテンシャルから, トロイダルエネルギーの
      ! 球面調和函数全波数の各成分を計算する.
      !
      !  * 全波数 n, 帯状波数 m のトロイダルポテンシャルのスペクトル成分
      !    ψ(n,m,r)から全波数 n 成分のトロイダルエネルギースペクトルは
      !    Σ[m=-n]^n(1/2)n(n+1)4πr^2|ψ(n,m,r)|^2 と計算される.
      !
      ! * 全てのエネルギースペクトル成分の和を動径積分したもの(r^2の重み無し)
      !    が球殻内での全エネルギーに等しい.
      !
      real(8), dimension(nc,0:lm), intent(in) :: wq_TORPOT
      !(in) トロイダルポテンシャル

      real(8), dimension(0:nm,km) :: nr_ToroidalEnergySpectrum_wq
      !(out) エネルギースペクトルトロイダル成分

      real(8), dimension(nc,km)    :: wr_DATA   ! 作業領域
      real(8), dimension(0:nm,km)  :: nr_Work
      integer :: l, n, m, nm_ary(2)
      real(8) :: factor

      wr_DATA = wr_wq(wq_TORPOT)
      nr_Work = 0.0D0
      do l=1,nc
         nm_ary = nm_l(l)
         n = nm_ary(1) ; m = abs(nm_ary(2))
         if ( n .ge. 0 ) then
            if ( m == 0 ) then
               factor = 1.0D0
            else
               factor = 2.0D0
            endif
            nr_Work(n,:) = nr_Work(n,:) &
                 + factor * 0.5 * n*(n+1)* (4*pi) * r_Rad**2 * wr_Data(l,:)**2
!!$              + 0.5 * n*(n+1)* (4*pi) * z_Rad**2 * wz_Data(l,:)**2
         endif
      enddo

      CALL MPI_ALLREDUCE(nr_Work,nr_ToroidalEnergySpectrum_wq,(nm+1)*km, &
                         MPI_REAL8,MPI_SUM,MPI_COMM_WORLD,IERR)

    end function nr_ToroidalEnergySpectrum_wq

    function nmr_PoloidalEnergySpectrum_wq(wq_POLPOT)
      !
      ! ポロイダルポテンシャルから, ポロイダルエネルギーの
      ! 球面調和函数全波数 n, 帯状波数 m の各成分を計算する.
      !
      !  * 全波数 n, 帯状波数 m のポロイダルポテンシャルのスペクトル成分
      !    φ(n,m,r)から全波数 n, 帯状波数 m 成分のポロイダルエネルギー
      !    スペクトルは
      !
      !      (1/2)n(n+1)4π{|d(rφ(n,m,r))/dr|^2 + n(n+1)|φ(n,m,r)|^2}
      !
      !    と計算される.
      !
      !  * 全てのエネルギースペクトル成分の和を動径積分したもの(r^2の重み無し)
      !    が球殻内での全エネルギーに等しい.
      !
      !  * データの存在しない全波数 n, 帯状波数 m の配列には欠損値が格納される.
      !    欠損値の値はモジュール変数 wq_VMiss によって設定できる
      !    (初期値は -999.0)
      !
      real(8), dimension(nc,0:lm), intent(in) :: wq_POLPOT
      !(in) ポロイダルポテンシャル

      real(8), dimension(0:nm,-nm:nm,km) :: nmr_PoloidalEnergySpectrum_wq
      !(out) エネルギースペクトルポロイダル成分

      real(8), dimension(0:nm,-nm:nm,km) :: nmr_Work ! 作業領域

      real(8), dimension(nc,km) ::wr_DATA1   ! 作業領域
      real(8), dimension(nc,km) ::wr_DATA2   ! 作業領域
      integer :: l, n, m, nm_ary(2)

      integer :: ierr

      wr_Data1 = wr_wq(wq_POLPOT)
      wr_Data2 = wr_wq(wq_RadDRad_wq(wq_POLPOT)+wq_POLPOT)  ! d(rφ)/dr
                                                            ! = rdφ/dr+φ
      nmr_work = 0.0D0
      do l=1,nc
         nm_ary = nm_l(l)
         n = nm_ary(1) ; m = abs(nm_ary(2))
         if ( n .ge. 0 ) then
            if ( m == 0 ) then
               nmr_Work(n,0,:) = nmr_Work(n,0,:) &
                    +  0.5 * n*(n+1) * (4*pi) &
                    * ( wr_Data2(l,:)**2 +  n*(n+1)*wr_Data1(l,:)**2 )
            else
               nmr_Work(n,m,:) = nmr_Work(n,m,:) &
                    +  0.5 * n*(n+1) * (4*pi) &
                    * ( wr_Data2(l,:)**2 +  n*(n+1)*wr_Data1(l,:)**2 )
               nmr_Work(n,-m,:) = nmr_Work(n,-m,:) &
                    +  0.5 * n*(n+1) * (4*pi) &
                    * ( wr_Data2(l,:)**2 +  n*(n+1)*wr_Data1(l,:)**2 )
            endif
         endif
      enddo

      CALL MPI_ALLREDUCE(nmr_Work,nmr_PoloidalEnergySpectrum_wq, &
                         (nm+1)*(2*nm+1)*km, &
                         MPI_REAL8,MPI_SUM,MPI_COMM_WORLD,IERR)

      do m=1,nm
         do n=0,m-1
            nmr_PoloidalEnergySpectrum_wq(n,m,:)  = wq_Vmiss
            nmr_PoloidalEnergySpectrum_wq(n,-m,:) = wq_Vmiss
         enddo
      enddo

    end function nmr_PoloidalEnergySpectrum_wq

    function nr_PoloidalEnergySpectrum_wq(wq_POLPOT)
      !
      ! ポロイダルポテンシャルから, ポロイダルエネルギーの
      ! 球面調和函数全波数の各成分を計算する
      !
      !  * 全波数 n, 帯状波数 m のポロイダルポテンシャルのスペクトル成分
      !    φ(n,m,r)から全波数 n 成分のポロイダルエネルギースペクトルは
      !
      !      Σ[m=-n]^n ((1/2)n(n+1)4π{|d(rφ(n,m,r))/dr|^2
      !                 + n(n+1)|φ(n,m,r)|^2}
      !
      !    と計算される.
      !
      !  * 全ての全波数に対してのエネルギースペクトル成分の和を動径積分したもの
      !    (r^2の重み無し)が球殻内での全エネルギーに等しい.
      !
      real(8), dimension(nc,0:lm), intent(in) :: wq_POLPOT
      !(in) ポロイダルポテンシャル

      real(8), dimension(0:nm,km) :: nr_PoloidalEnergySpectrum_wq
      !(out) エネルギースペクトルポロイダル成分

      real(8), dimension(nc,km)     :: wr_DATA1   ! 作業領域
      real(8), dimension(nc,km)     :: wr_DATA2   ! 作業領域
      real(8), dimension(0:nm,km)   :: nr_Work
      integer :: l, n, m, nm_ary(2)
      real(8) :: factor

      wr_Data1 = wr_wq(wq_POLPOT)
      wr_Data2 = wr_wq(wq_RadDRad_wq(wq_POLPOT)+wq_POLPOT)  ! d(rφ)/dr
                                                            ! = rdφ/dr+φ
      nr_Work = 0.0D0

      do l=1,nc
         nm_ary = nm_l(l)
         n = nm_ary(1) ; m = abs(nm_ary(2))
         if ( n .ge. 0 ) then
            if ( m == 0 ) then
               factor = 1.0D0
            else
               factor = 2.0D0
            endif
            nr_Work(n,:) = nr_Work(n,:) &
                 + factor * 0.5* n*(n+1)* (4*pi) &
                 *( wr_Data2(l,:)**2 + n*(n+1)*wr_Data1(l,:)**2 )
         endif
      enddo

      CALL MPI_ALLREDUCE(nr_Work,nr_PoloidalEnergySpectrum_wq,(nm+1)*km, &
                         MPI_REAL8,MPI_SUM,MPI_COMM_WORLD,IERR)

    end function nr_PoloidalEnergySpectrum_wq

  !--------------- 境界値問題 -----------------

    subroutine wq_BoundaryTau(wq,value,cond)
      !
      ! スペクトルデータにディリクレ・ノイマン境界条件を適用する
      ! Chebyshev 空間での境界条件適用(タウ法)
      !
      ! チェビシェフ空間において境界条件を満たすべく高次の係数を
      ! 定める方法をとっている(タウ法).
      !
      real(8), dimension(nc,0:lm),intent(inout)      :: wq
              !(inout) 境界条件を適用するデータ. 修正された値を返す. 

      real(8), dimension(nc), intent(in), optional   :: value
              !(in) 境界での 値/勾配 分布を水平スペクトル変換したものを与える. 
              !     省略時は値/勾配 0 となる. 

      character(len=1), intent(in), optional                    :: cond
              !(in) 境界条件. 省略時は 'D'
              !        D : 外側ディリクレ条件
              !        N : 外側ノイマン条件

      if (.not. present(cond)) then
         if (present(value)) then
            call aq_BoundaryTau_D(wq,value)
         else
            call aq_BoundaryTau_D(wq)
         endif
         return
      endif

      select case(cond)
      case ('N')
         if (present(value)) then
            call aq_BoundaryTau_N(wq,value)
         else
            call aq_BoundaryTau_N(wq)
         endif
      case ('D')
         if (present(value)) then
            call aq_BoundaryTau_D(wq,value)
         else
            call aq_BoundaryTau_D(wq)
         endif
      case default
         call MessageNotify('E','wq_BoundaryTau','B.C. not supported')
      end select

    end subroutine wq_BoundaryTau

    subroutine wr_BoundaryGrid(wr,value,cond)
      !
      ! スペクトルデータにディリクレ・ノイマン境界条件を適用する
      ! 実空間での境界条件適用
      !
      ! 鉛直実格子点空間において内部領域の値と境界条件を満たすように
      ! 条件を課している(選点法). 
      !
      real(8), dimension(nc,km),intent(inout)      :: wr
              !(inout) 境界条件を適用するデータ. 修正された値を返す. 

      real(8), dimension(nc), intent(in), optional   :: value
              !(in) 境界での 値/勾配 分布を水平スペクトル変換したものを与える. 
              !    省略時は値/勾配 0 となる. 

      character(len=1), intent(in), optional             :: cond
              !(in) 境界条件. 省略時は 'D'
              !        D : 外側ディリクレ条件
              !        N : 外側ノイマン条件

      if (.not. present(cond)) then
         if (present(value)) then
            call ag_BoundaryGrid_D(wr,value)
         else
            call ag_BoundaryGrid_D(wr)
         endif
         return
      endif

      select case(cond)
      case ('N')
         if (present(value)) then
            call ag_BoundaryGrid_N(wr,value)
         else
            call ag_BoundaryGrid_N(wr)
         endif
      case ('D')
         if (present(value)) then
            call ag_BoundaryGrid_D(wr,value)
         else
            call ag_BoundaryGrid_D(wr)
         endif
      case default
         call MessageNotify('E','wr_BoundaryGrid','B.C. not supported')
      end select

    end subroutine wr_BoundaryGrid

    subroutine wq_TorBoundaryTau(wq_TORPOT,value,cond,new) 
      !
      ! 速度トロイダルポテンシャルに対して境界条件を適用する. 
      ! Chebyshev 空間での境界条件適用. 
      !
      ! 速度トロイダルポテンシャルΨに対して与えられる境界条件は
      !
      !   * 粘着条件 : Ψ = Ψb(lon,lat). Ψb は境界球面での速度分布. 
      !                                   default は 0(静止状態).
      !
      !   * 応力なし条件 : ∂(Ψ/r)/∂r = 0.
      !
      ! 最初に呼ばれるときはオプショナル引数 new に関係なく行列が設定される.
      !
      real(8), dimension(nc,0:lm),intent(inout)      :: wq_TORPOT
              !(inout) 境界条件を適用するデータ. 修正された値を返す. 

      real(8), dimension(nc), intent(in), optional :: value
              !(in) 両端境界でのトロイダルポテンシャル
              !     粘着条件の時のみ有効

      character(len=1), intent(in), optional  :: cond
              !(in) 境界条件スイッチ. 省略時は 'R'
              !     R    : 上側粘着条件
              !     F    : 上側応力なし条件

      logical, intent(IN), optional :: new
              !(in) true だと境界条件計算用行列を強制的に新たに作る.
              !     default は false.

      real(8), dimension(:,:,:), allocatable  :: alu
      integer, dimension(:,:), allocatable    :: kp
      real(8), dimension(nc,0:lm)  :: wq_data
      real(8), dimension(nc,km)    :: wr_data
      logical                                 :: rigid        ! 境界条件

      logical :: first = .true.
      logical :: new_matrix = .false.
      integer :: n, l, lend
      save    :: alu, kp, first

      if (.not. present(cond)) then
         rigid=.TRUE.
      else
         select case (cond)
         case ('R')
            rigid = .TRUE.
         case ('F')
            rigid = .FALSE.
         case default
            call MessageNotify('E','wq_TorBoundaryTau','B.C. not supported')
         end select
      endif

      if (.not. present(new)) then
         new_matrix=.false.
      else 
         new_matrix=new
      endif

      if ( first .OR. new_matrix ) then
         first = .false.

         if ( allocated(alu) ) deallocate(alu)
         if ( allocated(kp) ) deallocate(kp)
         allocate(alu(nc,0:lm,0:lm),kp(nc,0:lm))

         alu = 0.0D0
         do l=0,lm
            alu(:,l,l)=1.0D0
         enddo

         ! 力学的条件

         do l=0,lm
            wq_data = 0.0 ; wq_data(:,l) = 1.0D0

            if ( rigid ) then          ! 力学的条件粘着
               wr_data = wr_wq(wq_data)
            else                       ! 力学的条件自由すべり
               wr_data = wr_wq(wq_RadDRad_wq(wq_data)- wq_data)/wr_Rad
            endif

            do n=1,nc
               if ( mod(nd(n),2) .eq. mod(lm,2) ) then
                  alu(n,lm,l) = wr_data(n,km)
               else
                  alu(n,lm-1,l) = wr_data(n,km)
               endif
            end do
         enddo

         ! 関係ないところを 0 で埋める.
         do n=1,nc
            if ( mod(nd(n),2) .eq. mod(lm,2) ) then
               lend = lm
            else
               lend = lm-1
            endif

            do l=0,nd(n)-1
               alu(n,lend,l) = 0.0D0
            enddo
            do l=nd(n)+1,lm,2
               alu(n,lend,l) = 0.0D0
            enddo
         enddo

         call ludecomp(alu,kp)

         if ( rigid .AND. present(value) ) then 
            call MessageNotify('M','wq_TorBoundaryTau',&
                 'Toroidal potential at k=km was given by the optional variable.')
         else if ( rigid .AND. (.NOT.present(value)) ) then
            call MessageNotify('M','wq_TorBoundaryTau',&
                 'Toroidal potential at k=km was set to zero.')
         else if ( (.NOT. rigid) .AND. present(value) ) then
            call MessageNotify('W','wq_TorBoundaryTau',&
                 'Boundary value k=km cannot be set under stress-free condition.')
         endif

         call MessageNotify('M','wq_TorBoundaryTau',&
                           'Matrix to apply  b.c. newly produced.')
      endif

      do n=1,nc
         if ( mod(nd(n),2) .eq. mod(lm,2) ) then
            lend = lm
         else
            lend = lm-1
         endif

         if ( rigid .AND. present(value) ) then
            wq_torpot(n,lend) = value(n)
         else
            wq_torpot(n,lend) = 0.0D0
         endif
      enddo

      wq_torpot = lusolve(alu,kp,wq_TORPOT)

    end subroutine wq_TorBoundaryTau

    subroutine wr_TorBoundaryGrid(wr_TORPOT,value,cond,new) 
      !
      ! 速度トロイダルポテンシャルに対して境界条件を適用する.
      ! 実空間での境界条件適用
      !
      ! 鉛直実格子点空間において内部領域の値と境界条件を満たすように
      ! 条件を課している(選点法). 
      !
      ! 速度トロイダルポテンシャルΨに対して与えられる境界条件は
      !
      !   * 粘着条件 : Ψ = Ψb(lon,lat). Ψb は境界球面での速度分布. 
      !                                   default は 0 (静止状態).
      !
      !   * 応力なし条件 : ∂(Ψ/r)/∂r = 0.
      !
      ! 最初に呼ばれるときはオプショナル引数 new に関係なく行列が設定される.
      !
      real(8), dimension(nc,km),intent(inout)    :: wr_TORPOT
              !(inout) 境界条件を適用するデータ. 修正された値を返す. 

      real(8), dimension(nc), intent(in), optional :: value
              !(in) 両端境界でのトロイダルポテンシャル
              !     粘着条件の時のみ有効

      character(len=1), intent(in), optional  :: cond
              !(in) 境界条件スイッチ. 省略時は 'R'
              !     R    : 上側粘着条件
              !     F    : 上側応力なし条件

      logical, intent(IN), optional :: new
              !(in) true だと境界条件計算用行列を強制的に新たに作る.
              !     default は false.

      real(8), dimension(:,:,:), allocatable  :: alu
      integer, dimension(:,:), allocatable    :: kp
      real(8), dimension(nc,0:lm)  :: wq_data
      real(8), dimension(nc,km)    :: wr_data
      logical                                 :: rigid   ! 境界条件

      logical :: first = .true.
      logical :: new_matrix = .false.
      integer :: k
      save    :: alu, kp, first

      if (.not. present(cond)) then
         rigid=.TRUE.
      else
         select case (cond)
         case ('R')
            rigid = .TRUE.
         case ('F')
            rigid = .FALSE.
         case default
            call MessageNotify('E','wr_TorBoundaryGrid','B.C. not supported')
         end select
      endif

      if (.not. present(new)) then
         new_matrix=.false.
      else 
         new_matrix=new
      endif

      if ( first .OR. new_matrix ) then
         first = .false.

         if ( allocated(alu) ) deallocate(alu)
         if ( allocated(kp) ) deallocate(kp)
         allocate(alu(nc,km,km),kp(nc,km))

         alu = 0.0D0
         do k=1,km
            wr_data = 0.0D0
            wr_data(:,k)=1.0D0
            alu(:,:,k) = wr_data
         enddo

         if ( .not. rigid ) then
            do k=1,km
               wr_data = 0.0D0
               wr_data(:,k)=1.0D0
               wq_data = wq_wr(wr_data)
               wr_data = wr_wq(wq_RadDRad_wq(wq_data) - wq_data)/wr_Rad
               alu(:,km,k) = wr_data(:,km)
            enddo
         endif

         call ludecomp(alu,kp)

         if ( rigid .AND. present(value) ) then 
            call MessageNotify('M','wr_TorBoundaryGrid',&
                 'Toroidal potential at k=km was given by the optional variable.')
         else if ( rigid .AND. (.NOT.present(value)) ) then
            call MessageNotify('M','wr_TorBoundaryGrid',&
                 'Toroidal potential at k=km was set to zero.')
         else if ( (.NOT. rigid) .AND. present(value) ) then
            call MessageNotify('W','wr_TorBoundaryGrid',&
                 'Boundary value at k=km cannot be set under stress-free condition.')
         endif

         call MessageNotify('M','wr_TorBoundaryGrid',&
                           'Matrix to apply  b.c. newly produced.')
      endif

      if ( rigid .AND. present(value) ) then
         wr_TorPot(:,km)  = value
      else
         wr_TorPot(:,km)  = 0.0D0
      endif

      wr_TorPot = lusolve(alu,kp,wr_TorPot)

    end subroutine wr_TorBoundaryGrid

    function wq_LaplaPol2PolTau_wq(wq,cond,new)
      !
      ! 速度ポロイダルポテンシャルΦを▽^2Φから計算する.
      !
      ! スペクトル空間で境界条件を適用している(タウ法). 
      !
      ! 速度ポロイダルポテンシャルΦを f = ▽^2Φから定める式は
      !
      !   ▽^2Φ = f
      !     Φ = const. at Boundary.
      !     ∂Φ/∂r = 0 at Boundary           (粘着条件) 
      !     or ∂^2Φ/∂r^2 = 0 at Boundary    (応力なし条件)
      !
      ! 最初に呼ばれるときはオプショナル引数 new に関係なく行列が設定される.
      !
      real(8), dimension(nc,0:lm),intent(in)  :: wq
              !(in) 入力▽^2φ分布

      real(8), dimension(nc,0:lm)  :: wq_LaplaPol2PolTau_wq
              !(out) 出力ポロイダルポテンシャル分布

      character(len=1), intent(in), optional  :: cond
              !(in) 境界条件スイッチ. 省略時は 'R'
              !     R    : 上側粘着条件
              !     F    : 上側応力なし条件

      logical, intent(IN), optional :: new
              !(in) true だと境界条件計算用行列を強制的に新たに作る.
              !     default は false.

      real(8), dimension(:,:,:), allocatable  :: alu     ! 内部領域計算用
      integer, dimension(:,:), allocatable    :: kp      ! 内部領域計算用

      real(8), dimension(:,:,:), allocatable  :: alub    ! 境界条件計算用
      integer, dimension(:,:), allocatable    :: kpb     ! 境界条件計算用

      real(8), dimension(nc,km)    :: wr_work
      real(8), dimension(nc,0:lm)  :: wq_work
      logical                                 :: rigid   ! 境界条件

      logical :: first = .true.
      logical :: new_matrix = .false.
      integer :: l,n, lend
      save    :: alu, kp, first
      save    :: alub, kpb

      if (.not. present(cond)) then
         rigid=.TRUE. 
      else
         select case (cond)
         case ('R')
            rigid = .TRUE.
         case ('F')
            rigid = .FALSE.
         case default
            call MessageNotify('E','wq_laplapol2pol_wq','B.C. not supported')
         end select
      endif

      if (.not. present(new)) then
         new_matrix=.false.
      else 
         new_matrix=new
      endif

      if ( first .OR. new_matrix ) then
         first = .false.

         if ( allocated(alu) ) deallocate(alu)
         if ( allocated(kp) ) deallocate(kp)
         if ( allocated(alub) ) deallocate(alub)
         if ( allocated(kpb) ) deallocate(kpb)
         allocate(alu(nc,0:lm,0:lm),kp(nc,0:lm))
         allocate(alub(nc,0:lm,0:lm),kpb(nc,0:lm))


         !---- 内部領域計算用行列 -----
         do l=0,lm
            wq_work = 0.0
            wq_work(:,l) = 1.0D0
            alu(:,:,l) = wq_Lapla_wq(wq_work)
         enddo

         ! 0 成分のところを 1 で埋める.
         do n=1,nc
            do l=0,nd(n)-1
               alu(n,l,l) = 1.0D0
            enddo
            do l=nd(n)+1,lm,2
               alu(n,l,l) = 1.0D0
            enddo
         enddo

         ! alu(:,:,nd(n)) 列は 0 なので 1 をいれておく. 
         ! l=nd(n) 成分は境界条件で決める. 
         do n=1,nc
            if ( mod(nd(n),2) .eq. mod(lm,2) ) then
               alu(n,lm,nd(n)) = 1.0D0
            else
               alu(n,lm-1,nd(n)) = 1.0D0
            endif
         enddo

         call ludecomp(alu,kp)

         !---- 境界条件計算用行列 -----

         alub = 0.0D0
         do l=0,lm
            alub(:,l,l) = 1.0D0
         enddo

         do l=0,lm
            wq_work=0.0D0 ; wq_work(:,l)=1.0D0
            wr_work = wr_wq(wq_work)

            ! 運動学的条件. 流線は境界で一定
            !     l=nd(n) 成分を境界条件で決める. 
            do n=1,nc
               alub(n,nd(n),l) = wr_work(n,km)
            enddo

            ! 力学的条件粘着条件 
            !     l=lend 成分を境界条件で決める. 
            if ( rigid ) then
               wr_work=wr_wq(wq_RadDRad_wq(wq_work))/wr_Rad
            else
               wr_work=wr_wq(wq_RadDRad_wq(wq_RadDRad_wq(wq_work)) &
                                           -wq_RadDRad_wq(wq_work) )&
                            /wr_Rad**2
            endif
            
            do n=1,nc
               if ( mod(nd(n),2) .eq. mod(lm,2) ) then
                  lend = lm
               else
                  lend = lm-1
               endif
               alub(n,lend,l) = wr_work(n,km)
            enddo
         enddo

         ! 関係ないところを 0 で埋める.
         do n=1,nc
            if ( mod(nd(n),2) .eq. mod(lm,2) ) then
               lend = lm
            else
               lend = lm-1
            endif

            do l=0,nd(n)-1
               alub(n,nd(n),l) = 0.0D0
               alub(n,lend,l) = 0.0D0
            enddo
            do l=nd(n)+1,lm,2
               alub(n,nd(n),l) = 0.0D0
               alub(n,lend,l) = 0.0D0
            enddo
         enddo

         call ludecomp(alub,kpb)

         if ( rigid ) then
            call MessageNotify('M','wq_LaplaPol2PolTau_wq',&
                              'Matrix to apply rigid b.c. newly produced.')
         else
            call MessageNotify('M','wq_LaplaPol2PolTau_wq',&
                              'Matrix to apply stress-free b.c. newly produced.')
         endif
      endif

      ! 内部領域計算
      wq_work = wq

      wq_work = lusolve(alu,kp,wq_work)

      ! 境界条件計算
      do n=1,nc
         wq_work(n,nd(n)) = 0
         if ( mod(nd(n),2) .eq. mod(lm,2) ) then
            wq_work(n,lm)   = 0
         else
            wq_work(n,lm-1) = 0
         endif
      enddo

      wq_laplapol2polTau_wq = lusolve(alub,kpb,wq_work)

    end function wq_LaplaPol2PolTau_wq

    function wr_LaplaPol2Pol_wr(wr,cond,new)
      !
      ! 速度ポロイダルポテンシャルΦを▽^2Φから計算する.
      !
      ! 格子点空間で境界条件を適用している. 
      !
      ! 速度ポロイダルポテンシャルΦを f = ▽^2Φから定める式は
      !
      !   ▽^2Φ = f
      !     Φ = const. at Boundary.
      !     ∂Φ/∂r = 0 at Boundary           (粘着条件) 
      !     or ∂^2Φ/∂r^2 = 0 at Boundary    (応力なし条件)
      !
      ! 最初に呼ばれるときはオプショナル引数 new に関係なく行列が設定される.
      !
      ! 注意 : この関数は完成していない. 使用禁止. 
      !
      real(8), dimension(nc,km),intent(in)  :: wr
              !(in) 入力▽^2φ分布

      real(8), dimension(nc,km)         :: wr_LaplaPol2Pol_wr
              !(out) 出力ポロイダルポテンシャル分布

      character(len=1), intent(in), optional  :: cond
              !(in) 境界条件スイッチ. 省略時は 'R'
              !     R    : 上側粘着条件
              !     F    : 上側応力なし条件

      logical, intent(IN), optional :: new
              !(in) true だと境界条件計算用行列を強制的に新たに作る.
              !     default は false.

      real(8), dimension(:,:,:), allocatable  :: alu
      integer, dimension(:,:), allocatable    :: kp

      real(8), dimension(:,:,:), allocatable  :: alub
      integer, dimension(:,:), allocatable    :: kpb

      real(8), dimension(nc,km)    :: wr_work
      logical                                 :: rigid   ! 境界条件

      logical :: first = .true.
      logical :: new_matrix = .false.
      integer :: k
      save    :: alu, kp, first
      save    :: alub, kpb

      if (.not. present(cond)) then
         rigid=.TRUE. 
      else
         select case (cond)
         case ('R')
            rigid = .TRUE.
         case ('F')
            rigid = .FALSE.
         case default
            call MessageNotify('E','wr_laplapol2pol_wr','B.C. not supported')
         end select
      endif

      if (.not. present(new)) then
         new_matrix=.false.
      else 
         new_matrix=new
      endif

      if ( first .OR. new_matrix ) then
         first = .false.

         if ( allocated(alu) ) deallocate(alu)
         if ( allocated(kp) ) deallocate(kp)
         allocate(alu(nc,0:lm,0:lm),kp(nc,0:lm))

         if ( allocated(alub) ) deallocate(alub)
         if ( allocated(kpb) ) deallocate(kpb)
         allocate(alub(nc,km,km),kpb(nc,km))

         !---- 内部領域計算用行列 -----
         do k=1,km
            wr_work = 0.0D0 ; wr_work(:,k) = 1.0D0

            ! 各水平波数に関して独立の式
            alu(:,:,k) = wr_wq(wq_Lapla_wq(wq_wr(wr_work)))
         enddo

         do k=1,km
            wr_work=0.0D0 ; wr_work(:,k)=1.0D0
            alu(:,km,k)  = wr_work(:,km)
         enddo


         !---- 境界条件計算用 ----
         alub = 0.0D0
         do k=1,km
            alub(:,k,k) = 1.0D0
         enddo

         ! 運動学的条件. 流線は境界で一定
         !   k=km の値を定める
         do k=1,km
            wr_work=0.0D0 ; wr_work(:,k)=1.0D0
            alub(:,km,k)  = wr_work(:,km)
         enddo

         ! 力学的条件粘着条件 
         !   k=km-1 の値を定める
         if ( rigid ) then
            do k=1,km
               wr_work = 0.0D0 ; wr_work(:,k) = 1.0D0
               wr_work=wr_wq(wq_RadDRad_wq(wq_wr(wr_work)))/wr_Rad
               alub(:,km-1,k) = wr_work(:,km)
            enddo
         else
            do k=1,km
               wr_work = 0.0D0 ; wr_work(:,k) = 1.0D0
               wr_work=wr_wq(wq_RadDRad_wq(wq_RadDRad_wq(wq_wr(wr_work))) &
                                          -wq_RadDRad_wq(wq_wr(wr_work)))&
                       /wr_Rad**2
               alub(:,km-1,k) = wr_work(:,km)
            enddo
         endif

         call ludecomp(alub,kpb)

         if ( rigid ) then
            call MessageNotify('M','wr_LaplaPol2Pol_wr',&
                              'Matrix to apply rigid b.c. newly produced.')
         else
            call MessageNotify('M','wr_LaplaPol2Pol_wr',&
                              'Matrix to apply stress-free b.c. newly produced.')
         endif
      endif

      wr_work         = wr
      wr_work         = lusolve(alu,kp,wr_work)

      wr_work(:,km-1) = 0.0D0               ! 力学的条件
      wr_work(:,km)   = 0.0D0               ! 運動学的条件

      wr_laplapol2pol_wr = lusolve(alub,kpb,wr_work)

    end function wr_LaplaPol2Pol_wr

    subroutine wq_TormagBoundaryTau(wq_TOR,new)
      
      ! 磁場トロイダルポテンシャルに対して境界条件を適用する.
      ! Chebyshev 空間での境界条件適用
      !
      ! チェビシェフ空間において境界条件を満たすべく高次の係数を定める方法を
      ! とっている(タウ法). 現在のところ境界物質が非電気伝導体の場合のみ
      ! 対応している. その場合, 磁場トロイダルポテンシャルの境界条件は
      !
      ! 外側
      !    wq_psi = 0   at the outer boundary
      ! 
      ! であるから wq_Boundary で対応可能だが, 将来のため別途作成しておく.
      !
      ! 最初に呼ばれるときはオプショナル引数 new に関係なく行列が設定される.
      !
      real(8), dimension(nc,0:lm),intent(inout)   :: wq_TOR
              !(inout) 境界条件を適用するデータ. 修正された値を返す. 

      logical, intent(IN), optional :: new
              !(in) true だと境界条件計算用行列を強制的に新たに作る.
              !     default は false.

      real(8), dimension(:,:,:), allocatable  :: alu
      integer, dimension(:,:), allocatable    :: kp

      real(8), dimension(:,:), allocatable    :: wq_I
      real(8), dimension(:,:), allocatable    :: wr_PSI

      logical :: first = .true.
      logical :: new_matrix = .false.
      integer :: n, l, lend
      save    :: alu, kp, first

      if (.not. present(new)) then
         new_matrix=.false.
      else 
         new_matrix=new
      endif

      if ( first .OR. new_matrix ) then
         first = .false.

         if ( allocated(alu) ) deallocate(alu)
         if ( allocated(kp) ) deallocate(kp)
         if ( allocated(wq_I) ) deallocate(wq_I)
         if ( allocated(wr_PSI) ) deallocate(wr_PSI)
         allocate(alu(nc,0:lm,0:lm),kp(nc,0:lm))
         allocate(wq_I(nc,0:lm),wr_PSI(nc,km))

         alu = 0.0D0
         do l=0,lm
            alu(:,l,l) = 1.0D0
         enddo

         do l=0,lm
            wq_I = 0.0 ; wq_I(:,l) = 1.0
            ! 非電気伝導体
            wr_PSI = wr_wq(wq_I)

            do n=1,nc
               if ( mod(nd(n),2) .eq. mod(lm,2) ) then
                  alu(n,lm,l) = wr_Psi(n,km)
                  lend = lm
               else
                  alu(n,lm-1,l) = wr_Psi(n,km)
               endif
            enddo
         enddo

         ! 関係ないところを 0 で埋める.
         do n=1,nc
            if ( mod(nd(n),2) .eq. mod(lm,2) ) then
               lend = lm
            else
               lend = lm-1
            endif

            do l=0,nd(n)-1
               alu(n,lend,l) = 0.0D0
            enddo
            do l=nd(n)+1,lm,2
               alu(n,lend,l) = 0.0D0
            enddo
         enddo

         call ludecomp(alu,kp)

         deallocate(wq_I,wr_PSI)

         call MessageNotify('M','TormagBoundaryTau',&
                           'Matrix to apply  b.c. newly produced.')
      endif

      do n=1,nc
         if ( mod(nd(n),2) .eq. mod(lm,2) ) then
            wq_TOR(n,lm)   = 0.0
         else
            wq_TOR(n,lm-1) = 0.0
         endif
      enddo
      wq_TOR = lusolve(alu,kp,wq_TOR)

    end subroutine wq_TormagBoundaryTau

    subroutine wr_TormagBoundaryGrid(wr_TOR,new)
      !
      ! 磁場トロイダルポテンシャルに対して境界条件を適用する.
      ! 鉛直実空間での境界条件適用.
      !
      ! 鉛直実格子点空間において内部領域の値と境界条件を満たすように
      ! 条件を課している(選点法). 
      !
      ! 現在のところ境界物質が非電気伝導体の場合のみ対応している. 
      ! その場合, 磁場トロイダルポテンシャルの境界条件は
      !
      ! 外側
      !    wq_psi = 0   at the outer boundary
      ! 
      ! であるので wq_Boundary で対応可能だが, 将来のため別途作成しておく
      !
      ! 最初に呼ばれるときはオプショナル引数 new に関係なく行列が設定される.
      !
      real(8), dimension(nc,km),intent(inout)   :: wr_TOR
              !(inout) 境界条件を適用するデータ. 修正された値を返す. 
      
      logical, intent(IN), optional :: new
              !(in) (ダミー) true だと境界条件計算用行列を強制的に新たに作る.
              !     default は false.

      wr_TOR(:,km)  = 0.0D0

    end subroutine wr_TormagBoundaryGrid

    subroutine wq_PolmagBoundaryTau(wq_POL,new)
      !
      ! 磁場ポロイダルポテンシャルに対して境界条件を適用する.
      ! Chebyshev 空間での境界条件適用
      !
      ! チェビシェフ空間において境界条件を満たすべく高次の係数を定める方法を
      ! とっている(タウ法). 現在のところ境界物質が非電気伝導体の場合のみ
      ! 対応している. その場合, 磁場ポロイダルポテンシャルの各水平スペクトル
      ! 成分 h にたいして境界条件が与えられ,
      !
      !  * 外側境界 : dh/dr + (n+1)h/r = 0
      !
      ! である. ここで n は h の水平全波数である. 
      !
      ! 最初に呼ばれるときはオプショナル引数 new に関係なく行列が設定される.
      !
      real(8), dimension(nc,0:lm),intent(inout)   :: wq_POL
              !(inout) 境界条件を適用するデータ. 修正された値を返す. 

      logical, intent(IN), optional :: new
              !(in) true だと境界条件計算用行列を強制的に新たに作る.
              !     default は false.

      real(8), dimension(:,:,:), allocatable  :: alu
      integer, dimension(:,:), allocatable    :: kp

      real(8), dimension(:,:), allocatable    :: wq_I
      real(8), dimension(:,:), allocatable    :: wr_PSI
      real(8), dimension(:,:), allocatable    :: wr_DPSIDR

      logical :: first = .true.
      logical :: new_matrix = .false.
      integer  :: l, n, nn(2), lend
      save     :: alu, kp, first

      if (.not. present(new)) then
         new_matrix=.false.
      else 
         new_matrix=new
      endif

      if ( first .OR. new_matrix ) then
         first = .false.

         if ( allocated(alu) ) deallocate(alu)
         if ( allocated(kp) ) deallocate(kp)
         if ( allocated(wq_I) ) deallocate(wq_I)
         if ( allocated(wr_PSI) ) deallocate(wr_PSI)
         if ( allocated(wr_DPSIDR) ) deallocate(wr_DPSIDR)

         allocate(alu(nc,0:lm,0:lm),kp(nc,0:lm))
         allocate(wq_I(nc,0:lm))
         allocate(wr_PSI(nc,km),wr_DPSIDR(nc,km))

         alu = 0.0D0
         do l=0,lm
            alu(:,l,l) = 1.0D0
         enddo

         ! 非電気伝導体
         do l=0,lm
            wq_I = 0.0D0  ; wq_I(:,l) = 1.0D0
            wr_PSI = wr_wq(wq_I)
            wr_DPSIDR = wr_wq(wq_RadDRad_wq(wq_I))/wr_Rad

            do n=1,nc
               if ( mod(nd(n),2) .eq. mod(lm,2) ) then
                  lend = lm
               else
                  lend = lm-1
               endif
               nn=nm_l(n)
               alu(n,lend,l) = wr_DPSIDR(n,km) + (nn(1)+1) * wr_PSI(n,km)/r_RAD(km)
            enddo
         enddo

         ! 関係ないところを 0 で埋める.
         do n=1,nc
            if ( mod(nd(n),2) .eq. mod(lm,2) ) then
               lend = lm
            else
               lend = lm-1
            endif

            do l=0,nd(n)-1
               alu(n,lend,l) = 0.0D0
            enddo
            do l=nd(n)+1,lm,2
               alu(n,lend,l) = 0.0D0
            enddo
         enddo

         call ludecomp(alu,kp)

         deallocate(wq_I,wr_PSI,wr_DPSIDR)

         call MessageNotify('M','PolmagBoundaryTau',&
                           'Matrix to apply  b.c. newly produced.')
      endif

      do n=1,nc
         if ( mod(nd(n),2) .eq. mod(lm,2) ) then
            wq_POL(n,lm)   = 0.0
         else
            wq_POL(n,lm-1) = 0.0
         endif
      enddo
      wq_POL = lusolve(alu,kp,wq_POL)

    end subroutine wq_PolmagBoundaryTau

    subroutine wr_PolmagBoundaryGrid(wr_POL,new)
      !
      ! 磁場ポロイダルポテンシャルに対して境界条件を適用する.
      ! 鉛直実空間での境界条件適用. 
      !
      ! 鉛直実格子点空間において内部領域の値と境界条件を満たすように
      ! 条件を課している(選点法). 
      !
      ! 現在のところ境界物質が非電気伝導体の場合のみ対応している. 
      ! その場合, 磁場ポロイダルポテンシャルの各水平スペクトル成分 h に
      ! たいして境界条件が与えられ,
      !
      !  * 外側境界 : dh/dr + (n+1)h/r = 0
      !
      ! である. ここで n は h の水平全波数である. 
      !
      ! 最初に呼ばれるときはオプショナル引数 new に関係なく行列が設定される.
      !
      real(8), dimension(nc,km),intent(inout)   :: wr_POL
              !(inout) 境界条件を適用するデータ. 修正された値を返す. 

      logical, intent(IN), optional :: new
              !(in) true だと境界条件計算用行列を強制的に新たに作る.
              !     default は false.

      real(8), dimension(:,:,:), allocatable  :: alu
      integer, dimension(:,:), allocatable    :: kp

      real(8), dimension(:,:), allocatable    :: wr_I
      real(8), dimension(:,:), allocatable    :: wr_PSI
      real(8), dimension(:,:), allocatable    :: wr_DPSIDR

      logical :: first = .true.
      logical :: new_matrix = .false.
      integer  :: k, n, nn(2)
      save     :: alu, kp, first

      if (.not. present(new)) then
         new_matrix=.false.
      else 
         new_matrix=new
      endif

      if ( first .OR. new_matrix ) then
         first = .false.

         if ( allocated(alu) ) deallocate(alu)
         if ( allocated(kp) ) deallocate(kp)
         if ( allocated(wr_I) ) deallocate(wr_I)
         if ( allocated(wr_PSI) ) deallocate(wr_PSI)
         if ( allocated(wr_DPSIDR) ) deallocate(wr_DPSIDR)

         allocate(alu(nc,km,km),kp(nc,km))
         allocate(wr_I(nc,km))
         allocate(wr_PSI(nc,km),wr_DPSIDR(nc,km))

         do k=1,km
            wr_I = 0.0D0
            wr_I(:,k)=1.0D0
            alu(:,:,k) = wr_I                 ! 内部領域は値そのまま.
         enddo

         ! 非電気伝導体
         do k=1,km
            wr_I = 0.0D0
            wr_I(:,k) = 1.0D0
            wr_PSI = wr_I
            wr_DPSIDR = wr_wq(wq_RadDRad_wq(wq_wr(wr_I)))/wr_Rad

            do n=1,nc
               nn=nm_l(n)
               alu(n,km,k) = wr_DPSIDR(n,km) + (nn(1)+1) * wr_PSI(n,km)/r_RAD(km)
            enddo
         end do

         call ludecomp(alu,kp)

         deallocate(wr_I,wr_PSI,wr_DPSIDR)

         call MessageNotify('M','PolmagBoundaryGrid',&
                           'Matrix to apply  b.c. newly produced.')
      endif

      wr_POL(:,km)  = 0.0D0
      wr_POL = lusolve(alu,kp,wr_POL)

    end subroutine wr_PolmagBoundaryGrid

end module wq_mpi_module_supack
