!--
!----------------------------------------------------------------------
!     Copyright 2011 Shin-ichi Takehiro. All rights reserved.
!----------------------------------------------------------------------
!
!表題  eea_module
!
!      spml/eea_module モジュールは周期境界条件の下での 2 次元矩形領域の
!      流体運動をスペクトル法により数値計算するための Fortran90 関数を
!      提供する. 
!
!      内部で ee_module モジュールを呼んでいる. 
!      スペクトルデータおよび格子点データの格納方法については
!      ISPACK/P2PACK のマニュアルを参照されたい. 
!
!履歴  2011/12/10  竹広真一
!
!++
module eea_module
  !
  != eea_module
  !
  ! Authors:: Shin-ichi Takehiro, Youhei SASAKI
  ! Version:: $Id$
  ! Copyright&License:: See COPYRIGHT[link:../COPYRIGHT]
  !
  !== 概要
  !
  ! spml/eea_module モジュールは周期境界条件の下での 2 次元矩形領域の
  ! 流体運動をスペクトル法により数値計算するための Fortran90 関数を
  ! 提供する. 
  !
  ! 内部で ee_module モジュールを呼んでいる. 
  ! スペクトルデータおよび格子点データの格納方法については
  ! ISPACK/P2PACK のマニュアルを参照されたい. 
  !
  !== 関数・変数の名前と型について
  !
  !=== 命名法
  !  
  ! * 関数名の先頭 (ee_, yx_, x_, y_) は, 返す値の形を示している.
  !   eea_:: スペクトルデータ(第 1,2 次元がそれぞれ Y,X 方向波数, 第3次元は任意)
  !   yxa_:: 3 次元格子点データ(第 1,2 次元がそれぞれ Y,X 方向の格子点,, 第3次元は任意)
  !   xya_:: 3 次元格子点データ(第 1,2 次元がそれぞれ X,Y 方向の格子点,, 第3次元は任意)
  !   ee_ :: スペクトルデータ(第 1,2 次元がそれぞれ Y,X 方向波数)
  !   yx_ :: 2 次元格子点データ(第 1,2 次元がそれぞれ Y,X 方向の格子点)
  !   xy_ :: 2 次元格子点データ(第 1,2 次元がそれぞれ X,Y 方向の格子点)
  !   x_  :: X 方向 1 次元格子点データ, y_ : Y 方向 1 次元格子点データ
  !
  ! * 関数名の間の文字列(Dx, Dy, Lapla, LaplaInv, Jacobian)は, 
  !   その関数の作用を表している.
  !
  ! * 関数名の最後 (_ee_ee, _ee, _yx, _x, _y) は, 入力変数の形が
  !   スペクトルデータおよび格子点データであることを示している.
  !   _eea   :: 複数のスペクトルデータ
  !   _ee    :: スペクトルデータ
  !   _ee_ee :: 2 つのスペクトルデータ
  !   _yxa   :: 3 次元格子点データ
  !   _yx    :: 2 次元格子点データ
  !   _xy    :: 2 次元格子点データ
  !   _x     :: X 方向 1 次元格子点データ
  !   _y     :: Y 方向 1 次元格子点データ.
  !  
  !=== 各データの種類の説明
  !  
  ! * yxa : 3 次元格子点データ.
  !   * 変数の種類と次元は real(8), dimension(0:jm-1,0:im-1,:). 
  !   * im, jm はそれぞれ X, Y 座標の格子点数であり, サブルーチン 
  !     eea_initial にてあらかじめ設定しておく.
  !   * 第 1 次元が Y 座標の格子点位置番号, 第 2 次元が X 座標の
  !     格子点位置番号である (X, Y の順ではない)ことに注意.
  !
  ! * xya : 3 次元格子点データ.
  !   * 変数の種類と次元は real(8), dimension(0:im-1,0:jm-1,:). 
  !   * im, jm はそれぞれ X, Y 座標の格子点数であり, サブルーチン 
  !     eea_initial にてあらかじめ設定しておく.
  !   * 第 1 次元が X 座標の格子点位置番号, 第 2 次元が Y 座標の
  !     格子点位置番号である.
  !
  ! * eea : スペクトルデータ.
  !   * 変数の種類と次元は real(8), dimension(-lm:lm,-km:km,:). 
  !   * km, lm はそれぞれ X, Y 方向の最大波数であり, サブルーチン 
  !     ee_initial にてあらかじめ設定しておく.
  !     (X, Y 方向波数の順ではない)ことに注意. 
  !   * スペクトルデータの格納のされ方については...
  !
  ! * yx : 2 次元格子点データ.
  !   * 変数の種類と次元は real(8), dimension(0:jm-1,0:im-1). 
  !   * im, jm はそれぞれ X, Y 座標の格子点数であり, サブルーチン 
  !     ee_initial にてあらかじめ設定しておく.
  !   * 第 1 次元が Y 座標の格子点位置番号, 第 2 次元が X 座標の
  !     格子点位置番号である (X, Y の順ではない)ことに注意.
  !
  ! * ee : スペクトルデータ.
  !   * 変数の種類と次元は real(8), dimension(-lm:lm,-km:km). 
  !   * km, lm はそれぞれ X, Y 方向の最大波数であり, サブルーチン 
  !     ee_initial にてあらかじめ設定しておく.
  !     (X, Y 方向波数の順ではない)ことに注意. 
  !   * スペクトルデータの格納のされ方については...
  !
  ! * x, y : X, Y 方向 1 次元格子点データ.
  !   * 変数の種類と次元はそれぞれ
  !     real(8), dimension(0:im-1) および real(8), dimension(0:jm-1).
  !
  ! * ee_ で始まる関数が返す値はスペクトルデータに同じ.
  !
  ! * yx_ で始まる関数が返す値は 2 次元格子点データに同じ.
  !
  ! * x_, y_ で始まる関数が返す値は 1 次元格子点データに同じ.
  !
  ! * スペクトルデータに対する微分等の作用とは, 対応する格子点データに
  !   微分などを作用させたデータをスペクトル変換したものことである.
  !
  !== 変数・手続き群の要約
  !
  !==== 初期化 
  !
  ! eea_Initial :: スペクトル変換の格子点数, 波数, 領域の大きさの設定
  !
  !==== 座標変数
  !
  ! x_X, y_Y     ::  格子点座標(X,Y座標)を格納した 1 次元配列
  ! x_X_Weight, y_Y_Weight ::  重み座標を格納した 1 次元配列
  ! yx_X, yx_Y   :: 格子点データの XY 座標(X,Y)(格子点データ型 2 次元配列)
  ! xy_X, xy_Y   :: 格子点データの XY 座標(X,Y)(格子点データ型 2 次元配列)
  !
  !==== 基本変換
  !
  ! yxa_eea :: スペクトルデータから格子データへの変換
  ! eea_yxa :: 格子データからスペクトルデータへの変換
  ! xya_yxa :: 格子データの次元順序入換
  ! yxa_xya :: 格子データの次元順序入換
  ! yx_ee   :: スペクトルデータから格子データへの変換
  ! ee_yx   :: 格子データからスペクトルデータへの変換
  !
  !==== 微分
  !
  ! eea_Lapla_eea       :: スペクトルデータにラプラシアンを作用させる
  ! eea_LaplaInv_eea    :: スペクトルデータにラプラシアンの逆変換を作用させる
  ! eea_Dx_eea          :: スペクトルデータに X 微分を作用させる
  ! eea_Dy_eea          :: スペクトルデータに Y 微分を作用させる
  ! eea_Jacobian_eea_eea :: 2 つのスペクトルデータからヤコビアンを計算する
  !
  ! ee_Lapla_ee       :: スペクトルデータにラプラシアンを作用させる
  ! ee_LaplaInv_ee    :: スペクトルデータにラプラシアンの逆変換を作用させる
  ! ee_Dx_ee          :: スペクトルデータに X 微分を作用させる
  ! ee_Dy_ee          :: スペクトルデータに Y 微分を作用させる
  ! ee_Jacobian_ee_ee :: 2 つのスペクトルデータからヤコビアンを計算する
  !
  !==== 積分・平均
  !
  ! a_IntYX_yxa, a_AvrYX_yxa :: 2 次元格子点データの全領域積分および平均
  ! ya_IntX_yxa, ya_AvrX_yxa :: 2 次元格子点データの X 方向積分および平均
  ! a_IntX_xa, a_AvrX_xa     :: 1 次元(X)格子点データの X 方向積分および平均
  ! xa_IntY_yxa, xa_AvrY_yxa :: 2 次元格子点データの Y 方向積分および平均
  ! a_IntY_ya, a_AvrY_ya     :: 1 次元(Y)格子点データの Y 方向積分および平均
  !
  ! IntYX_yx, AvrYX_yx   :: 2 次元格子点データの全領域積分および平均
  ! y_IntX_yx, y_AvrX_yx :: 2 次元格子点データの X 方向積分および平均
  ! IntX_x, AvrX_x       :: 1 次元(X)格子点データの X 方向積分および平均
  ! x_IntY_yx, x_AvrY_yx :: 2 次元格子点データの Y 方向積分および平均
  ! IntY_y, AvrY_y       :: 1 次元(Y)格子点データの Y 方向積分および平均
  !
  !==== スペクトル解析
  !
  ! eea_EnergyFromStreamfunc_eea    :: 
  ! ee_EnergyFromStreamfunc_ee      :: 
  ! 流線関数からエネルギースペクトルを計算する
  !
  ! eea_EnstrophyFromStreamfunc_eea :: 
  ! ee_EnstrophyFromStreamfunc_ee   :: 
  ! 流線関数からエンストロフィースペクトルを計算する
  !
  !==== 補間計算
  !
  ! a_Interpolate_eea       :: 任意の点の値をスペクトルデータから計算する
  ! Interpolate_ee          :: 任意の点の値をスペクトルデータから計算する
  !
  use dc_message, only : MessageNotify
  use ee_module
  implicit none

  private
  public eea_Initial                                       ! 初期化ルーチン

  public yxa_eea, eea_yxa                                 ! 基本変換
  public yxa_xya, xya_yxa                                 ! 基本変換
  public yx_ee, ee_yx                                     ! 基本変換

  public eea_Lapla_eea, eea_LaplaInv_eea, eea_Dx_eea, eea_Dy_eea  ! 微分
  public ee_Lapla_ee, ee_LaplaInv_ee, ee_Dx_ee, ee_Dy_ee  ! 微分

  public eea_JacobianZ_eea, eea_Jacobian_eea_eea          ! 非線形計算
  public ee_JacobianZ_ee, ee_Jacobian_ee_ee               ! 非線形計算

  public a_IntYX_yxa, ya_IntX_yxa, xa_IntY_yxa, a_IntX_xa, a_IntY_ya   ! 積分
  public a_AvrYX_yxa, ya_AvrX_yxa, xa_AvrY_yxa, a_AvrX_xa, a_AvrY_ya   ! 平均
  public IntYX_yx, y_IntX_yx, x_IntY_yx, IntX_x, IntY_y   ! 積分
  public AvrYX_yx, y_AvrX_yx, x_AvrY_yx, AvrX_x, AvrY_y   ! 平均

  public eea_EnergyFromStreamfunc_eea                     ! エネルギー
  public ee_EnergyFromStreamfunc_ee                       ! エネルギー
  public eea_EnstrophyFromStreamfunc_eea                  ! エンストロフィー
  public ee_EnstrophyFromStreamfunc_ee                    ! エンストロフィー

  public a_Interpolate_eea                                ! 補間
  public Interpolate_ee                                   ! 補間

  public x_X, y_Y, x_X_Weight, y_Y_Weight, yx_X, yx_Y, xy_X, xy_Y   ! 座標変数

  integer   :: im=32, jm=32                      ! 格子点の設定(X,Y)
  integer   :: km=10, lm=10                      ! 切断波数の設定(X,Y)
  real(8)   :: xl=1.0, yl=1.0                    ! 領域の大きさ

  real(8), allocatable :: xy_X(:,:), xy_Y(:,:)

  real(8), parameter  :: pi=3.1415926535897932385D0

  save im, jm, km, lm, xy_X, xy_Y

  contains
  !--------------- 初期化 -----------------
    subroutine eea_Initial(i,j,k,l,xmin,xmax,ymin,ymax)
      !
      ! スペクトル変換の格子点数, 波数, 領域の大きさを設定する.
      !
      ! 他の関数や変数を呼ぶ前に, 最初にこのサブルーチンを呼んで
      ! 初期設定をしなければならない.
      !
      integer,intent(in) :: i           ! 格子点数(X)
      integer,intent(in) :: j           ! 格子点数(Y)
      integer,intent(in) :: K           ! 切断波数(X)
      integer,intent(in) :: l           ! 切断波数(Y)

      real(8),intent(in) :: xmin, xmax     ! X 座標範囲
      real(8),intent(in) :: ymin, ymax     ! Y 座標範囲

      im = i ; jm = j ; km = k ; lm = l

      call ee_Initial(i,j,k,l,xmin,xmax,ymin,ymax)

      allocate(xy_X(0:im-1,0:jm-1),xy_Y(0:im-1,0:jm-1))
      xy_X = transpose(yx_X) ;  xy_Y = transpose(yx_Y)

      call MessageNotify('M','eea_initial','eae_module (2011/12/10) is initialized')
    end subroutine eea_Initial

  !--------------- 基本変換 -----------------
    function yxa_eea(eea)
      !
      ! スペクトルデータから格子データへ変換する.
      !
      real(8), dimension(-lm:,-km:,:), intent(in) :: eea
      !(in)  スペクトルデータ

      real(8), dimension(0:jm-1,0:im-1,size(eea,3))             :: yxa_eea
      !(out) 格子点データ

      integer :: n
      ! DO LOOP 変数

      do n = 1, size(eea,3)
         yxa_eea(:,:,n) = yx_ee(eea(:,:,n))
      end do

    end function yxa_eea

    function eea_yxa(yxa)
      !
      ! 格子データからスペクトルデータへ変換する.
      !
      real(8), dimension(0:,0:,:), intent(in)  :: yxa
      !(in) 格子点データ

      real(8), dimension(-lm:lm,-km:km,size(yxa,3))              :: eea_yxa
      !(out)  スペクトルデータ

      integer :: n
      ! DO LOOP 変数

      do n = 1, size(yxa,3)
         eea_yxa(:,:,n) = ee_yx(yxa(:,:,n))
      end do

    end function eea_yxa

    function xya_yxa(yxa)
      !
      ! 格子データの次元入換
      !
      real(8), dimension(0:,0:,:), intent(in)          :: yxa
      !(in) 格子点データ

      real(8), dimension(0:im-1,0:jm-1,size(yxa,3))    :: xya_yxa
      !(out)  スペクトルデータ

      integer :: n
      ! DO LOOP 変数

      do n = 1, size(yxa,3)
         xya_yxa(:,:,n) = transpose(yxa(:,:,n))
      end do

    end function xya_yxa

    function yxa_xya(xya)
      !
      ! 格子データの次元入換
      !
      real(8), dimension(0:,0:,:), intent(in)        :: xya
      !(in) 格子点データ

      real(8), dimension(0:jm-1,0:im-1,size(xya,3))  :: yxa_xya
      !(out)  スペクトルデータ

      integer :: n
      ! DO LOOP 変数

      do n = 1, size(xya,3)
         yxa_xya(:,:,n) = transpose(xya(:,:,n))
      end do

    end function yxa_xya

  !--------------- 微分計算 -----------------
    function eea_Lapla_eea(eea)
      !
      ! 入力スペクトルデータにラプラシアン(∂xx+∂yy)を作用する.
      !
      ! スペクトルデータのラプラシアンとは, 対応する格子点データに
      ! ラプラシアンを作用させたデータのスペクトル変換のことである.
      !
      ! 実際にはスペクトルデータに全波数 (k**2 + l**2) をかける
      ! 計算を行っている. 
      !
      real(8), dimension(-lm:,-km:,:), intent(in)    :: eea
      !(in) 入力スペクトルデータ

      real(8), dimension(-lm:lm,-km:km,size(eea,3))  :: eea_Lapla_eea
      !(out) スペクトルデータのラプラシアン

      integer n
      ! 作業変数

      do n=1,size(eea,3)
         eea_Lapla_eea(:,:,n) = ee_Lapla_ee(eea(:,:,n))
      enddo
    end function eea_Lapla_eea

    function eea_LaplaInv_eea(eea)
      !
      ! 入力スペクトルデータに逆ラプラシアン(∂xx+∂yy)**(-1)を作用する.
      !
      ! スペクトルデータの逆ラプラシアンとは, 対応する格子点データに
      ! 逆ラプラシアンを作用させたデータのスペクトル変換のことである.
      !
      ! 実際にはスペクトルデータに全波数 (k**2 + l**2) で割る
      ! 計算を行っている. k=l=0 成分には 0 を代入している. 
      !
      real(8), dimension(-lm:,-km:,:), intent(in)   :: eea
      !(in) スペクトルデータ

      real(8), dimension(-lm:lm,-km:km,size(eea,3)) :: eea_LaplaInv_eea
      !(out) スペクトルデータの逆ラプラシアン

      integer n

      do n=1,size(eea,3)
         eea_LaplaInv_eea(:,:,n) = ee_LaplaInv_ee(eea(:,:,n))
      enddo
    end function eea_LaplaInv_eea

    function eea_Dx_eea(eea)
      !
      ! 入力スペクトルデータに X 微分(∂x)を作用する.
      !
      ! スペクトルデータの X 微分とは, 対応する格子点データに X 微分を
      ! 作用させたデータのスペクトル変換のことである.
      !
      ! 実際にはスペクトルデータに X 方向波数 k をかけて
      ! sin(kx) <-> cos(kx) 成分に入れ換える計算を行っている.
      !
      real(8), dimension(-lm:,-km:,:), intent(in)   :: eea
      !(in) 入力スペクトルデータ

      real(8), dimension(-lm:lm,-km:km,size(eea,3)) :: eea_Dx_eea
      !(out) スペクトルデータの X 微分

      integer n
      ! 作業変数

      do n=1,size(eea,3)
         eea_Dx_eea(:,:,n) = ee_Dx_ee(eea(:,:,n))
      enddo
    end function eea_Dx_eea

    function eea_Dy_eea(eea)
      !
      ! 入力スペクトルデータに Y 微分(∂y)を作用する.
      !
      ! スペクトルデータの X 微分とは, 対応する格子点データに Y 微分を
      ! 作用させたデータのスペクトル変換のことである.
      !
      ! 実際にはスペクトルデータに X 方向波数 l をかけて
      ! sin(ky) <-> cos(ky) 成分に入れ換える計算を行っている.
      !
      real(8), dimension(-lm:,-km:,:), intent(in)   :: eea
      !(in) 入力スペクトルデータ

      real(8), dimension(-lm:lm,-km:km,size(eea,3)) :: eea_Dy_eea
      !(out) スペクトルデータの Y 微分

      integer n
      ! 作業変数

      do n=1,size(eea,3)
         eea_Dy_eea(:,:,n) = ee_Dy_ee(eea(:,:,n))
      enddo

    end function eea_Dy_eea

    function eea_Jacobian_eea_eea(eea_a,eea_b)
      !
      !  2 つのスペクトルデータからヤコビアン
      !
      !     J(A,B)=(∂xA)(∂yB)-(∂yA)(∂xB)
      !
      !  を計算する.
      !
      !  2 つのスペクトルデータのヤコビアンとは, 対応する 2 つの
      !  格子点データのヤコビアンのスペクトル変換のことである.
      !
      real(8), dimension(-lm:,-km:,:), intent(in)  :: eea_a
      !(in) 1つ目の入力スペクトルデータ

      real(8), dimension(-lm:,-km:,:), intent(in)  :: eea_b
      !(in) 2つ目の入力スペクトルデータ

      real(8), dimension(-lm:lm,-km:km,size(eea_a,3)) :: eea_Jacobian_eea_eea
      !(out) 2 つのスペクトルデータのヤコビアン

      integer n
      ! 作業変数

      do n=1,size(eea_a,3)
         eea_Jacobian_eea_eea(:,:,n) = ee_Jacobian_ee_ee(eea_a(:,:,n),eea_b(:,:,n))
      end do

    end function eea_Jacobian_eea_eea

    function eea_JacobianZ_eea(eea_zeta)
      !
      ! 渦度スペクトルデータ ζ から流線関数と渦度のヤコビアン
      !
      !     J(ψ,ζ)=(∂xψ)(∂yζ)-(∂yψ)(∂xζ)
      !
      !  を計算する. ただしψ は (∂xx+∂yy)ψ=ζ を満たす流線関数である.
      !
      real(8), dimension(-lm:,-km:,:), intent(in)  :: eea_Zeta
      !(in) 渦度スペクトルデータ

      real(8), dimension(-lm:lm,-km:km,size(eea_Zeta,3)) :: eea_JacobianZ_eea
      !(out) 流線関数と渦度のヤコビアン

      integer n
      ! 作業変数

      do n=1,size(eea_Zeta,3)
         eea_JacobianZ_eea(:,:,n) = ee_JacobianZ_ee(eea_Zeta(:,:,n))
      end do

    end function eea_JacobianZ_eea

  !--------------- 積分計算 -----------------
    function a_IntYX_yxa(yxa)
      !
      ! 3 次元格子点データの水平領域積分および平均.
      !
      ! 実際には格子点データ各点毎に x_X_Weight, y_Y_Weight をかけた
      ! 総和を計算している. 
      !
      real(8), dimension(0:,0:,:)     :: yxa
      !(in)  2 次元格子点データ

      real(8), dimension(size(yxa,3)) :: a_IntYX_yxa
      !(out) 積分値

      integer :: n
      ! 作業変数

      do n=1,size(yxa,3)
         a_IntYX_yxa(n) = IntYX_yx(yxa(:,:,n))
      enddo
    end function a_IntYX_yxa

    function ya_IntX_yxa(yxa)
      !
      ! 3 次元格子点データの X 方向積分
      !
      ! 実際には格子点データ各点毎に x_X_Weight をかけた総和を計算している. 
      !
      real(8), dimension(0:,0:,:)   :: yxa
      !(in) 2 次元格子点データ

      real(8), dimension(0:jm-1,size(yxa,3))  :: ya_IntX_yxa
      !(out) 積分された 1 次元(Y)格子点データ

      integer :: n
      ! 作業変数

      do n=1,size(yxa,3)
         ya_IntX_yxa(:,n) = y_IntX_yx(yxa(:,:,n))
      enddo
    end function ya_IntX_yxa

    function xa_IntY_yxa(yxa)
      !
      ! 3 次元格子点データの Y 方向積分
      !
      ! 実際には格子点データ各点毎に y_Y_Weight をかけた総和を計算している. 
      !
      real(8), dimension(0:,0:,:)             :: yxa    
      !(in)  3 次元格子点データ

      real(8), dimension(0:im-1,size(yxa,3))  :: xa_IntY_yxa
      !(out) 積分された 2 次元(X)格子点データ

      integer :: n
      ! 作業変数

      do n=1,size(yxa,3)
         xa_IntY_yxa(:,n) = x_IntY_yx(yxa(:,:,n))
      enddo
    end function xa_IntY_yxa

    function a_IntX_xa(xa)
      !
      ! 2 次元(X)格子点データの X 方向積分
      !
      ! 実際には格子点データ各点毎に x_X_Weight をかけた総和を計算している. 
      !
      real(8), dimension(0:,:)       :: xa            !(in)  1 次元格子点データ
      !(in)  2 次元格子点データ

      real(8), dimension(size(xa,2)) :: a_IntX_xa     !(out) 積分値
      !(out) 積分された 1 次元格子点データ

      integer :: n
      ! 作業変数

      do n=1,size(xa,2)
         a_IntX_xa(n) = IntX_x(xa(:,n))
      enddo

    end function a_IntX_xa

    function a_IntY_ya(ya)      ! Y 方向積分
      !
      ! 2 次元(Y)格子点データの Y 方向積分
      !
      ! 実際には格子点データ各点毎に y_Y_Weight をかけた総和を計算している. 
      !
      real(8), dimension(0:,:)       :: ya            !(in)  1 次元格子点データ
      !(in)  2 次元格子点データ

      real(8), dimension(size(ya,2)) :: a_IntY_ya   !(out) 積分値
      !(out) 積分された 1 次元格子点データ

      integer :: n
      ! 作業変数

      do n=1,size(ya,2)
         a_IntY_ya(n) = IntY_y(ya(:,n))
      enddo

    end function a_IntY_ya

  !--------------- 平均計算 -----------------
    function a_AvrYX_yxa(yxa)
      !
      ! 3 次元格子点データの水平領域平均
      !
      ! 実際には格子点データ各点毎に x_X_Weight, y_Y_Weight をかけた
      ! 総和を計算し, x_X_Weight*y_Y_Weight の総和で割ることで平均している. 
      !
      real(8), dimension(0:,0:,:)     :: yxa
      !(in)  2 次元格子点データ

      real(8), dimension(size(yxa,3)) :: a_AvrYX_yxa
      !(out) 平均値

      integer :: n
      ! 作業変数

      do n=1,size(yxa,3)
         a_AvrYX_yxa(n) = AvrYX_yx(yxa(:,:,n))
      end do
    end function a_AvrYX_yxa

    function ya_AvrX_yxa(yxa)
      !
      ! 3 次元格子点データの X 方向平均
      !
      ! 実際には格子点データ各点毎に x_X_Weight をかけた総和を計算し, 
      ! x_X_Weight の総和で割ることで平均している. 
      !
      real(8), dimension(0:,0:,:)   :: yxa
      !(in) 2 次元格子点データ

      real(8), dimension(0:jm-1,size(yxa,3)) :: ya_AvrX_yxa
      !(out) 平均された 1 次元(Y)格子点データ

      integer :: n
      ! 作業変数

      do n=1,size(yxa,3)
         ya_AvrX_yxa(:,n) = y_AvrX_yx(yxa(:,:,n))
      end do
    end function ya_AvrX_yxa

    function xa_AvrY_yxa(yxa)
      !
      ! 3 次元格子点データの Y 方向平均
      !
      ! 実際には格子点データ各点毎に y_Y_Weight をかけた総和を計算し, 
      ! y_Y_Weight の総和で割ることで平均している. 
      !
      real(8), dimension(0:,0:,:)            :: yxa
      !(in) 2 次元格子点データ

      real(8), dimension(0:im-1,size(yxa,3)) :: xa_AvrY_yxa
      !(out) 平均された 2 次元(X)格子点データ

      integer :: n
      ! 作業変数

      do n=1,size(yxa,3)
         xa_AvrY_yxa(:,n) = x_AvrY_yx(yxa(:,:,n))
      end do

    end function xa_AvrY_yxa

    function a_AvrX_xa(xa)
      !
      ! 2 次元(X)格子点データの X 方向平均
      !
      ! 実際には格子点データ各点毎に x_X_Weight をかけた総和を計算し, 
      ! x_X_Weight の総和で割ることで平均している. 
      !
      real(8), dimension(0:,:)       :: xa            !(in)  1 次元格子点データ
      real(8), dimension(size(xa,2)) :: a_AvrX_xa     !(out) 平均値

      integer :: n
      ! 作業変数

      do n=1,size(xa,2)
         a_AvrX_xa(n) = AvrX_x(xa(:,n))
      end do

    end function a_AvrX_xa

    function a_AvrY_ya(ya)
      !
      ! 2 次元(Y)格子点データの Y 方向平均
      !
      ! 実際には格子点データ各点毎に y_Y_Weight をかけた総和を計算し, 
      ! y_Y_Weight の総和で割ることで平均している. 
      !
      real(8), dimension(0:,:)       :: ya          !(in)  1 次元格子点データ
      real(8), dimension(size(ya,2)) :: a_AvrY_ya     !(out) 平均値

      integer :: n
      ! 作業変数

      do n=1,size(ya,2)
         a_AvrY_ya(n) = AvrY_y(ya(:,n))
      end do

    end function a_AvrY_ya

  !--------------- スペクトル計算 -----------------
    function eea_EnergyFromStreamfunc_eea(eea_StrFunc)
      !
      ! 流線関数からエネルギースペクトルを計算する. 
      !
      !   E_kl = (1/2)(k^2+l^2)|\psi_kl|^2
      !
      ! * E_kl の総和が場の平均運動エネルギーとなる. 
      ! * それに領域の面積をかけると全運動エネルギーとなる. 
      !
      real(8), dimension(-lm:,-km:,:), intent(in) :: eea_StrFunc
      ! 流線関数

      real(8), dimension(-lm:lm,-km:km,size(eea_StrFunc,3))  :: eea_EnergyFromStreamfunc_eea
      ! エネルギースペクトル

      integer n
      ! 作業変数

      do n=1,size(eea_StrFunc,3)
         eea_EnergyFromStreamfunc_eea(:,:,n) &
              = ee_EnergyFromStreamfunc_ee(eea_StrFunc(:,:,n))
      enddo

    end function eea_EnergyFromStreamfunc_eea

    function eea_EnstrophyFromStreamfunc_eea(eea_StrFunc)
      !
      ! 流線関数からエンストロフィースペクトルを計算する. 
      !
      !   Q_kl = (1/2)(k^2+l^2)^2|\psi_kl|^2
      !
      ! * Q_kl の総和が場の平均エンストロフィーとなる. 
      ! * それに領域の面積をかけると全エンストロフィーとなる. 
      !
      real(8), dimension(-lm:,-km:,:), intent(in) :: eea_StrFunc
      ! 流線関数

      real(8), dimension(-lm:lm,-km:km,size(eea_StrFunc,3)) :: eea_EnstrophyFromStreamfunc_eea
      ! エンストロフィーースペクトル

      integer n
      ! 作業変数

      do n=1,size(eea_StrFunc,3)
         eea_EnstrophyFromStreamfunc_eea(:,:,n) &
              = ee_EnstrophyFromStreamfunc_ee(eea_StrFunc(:,:,n))
      enddo

    end function eea_EnstrophyFromStreamfunc_eea

  !--------------- 補間計算 -----------------
    function a_Interpolate_eea( eea_Data, xa, ya )
      real(8), intent(IN)  :: eea_data(-lm:,-km:,:)  ! スペクトルデータ
      real(8), intent(IN)  :: xa(:)                   ! 補間する点の x 座標 
      real(8), intent(IN)  :: ya(:)                   ! 補間する点の y 座標 
      real(8)              :: a_Interpolate_eea(size(xa)) ! 補間した値

      integer :: n

      do n=1,size(xa)
         a_Interpolate_eea(n) = Interpolate_ee(eea_Data(:,:,n),xa(n),ya(n))
      end do

    end function a_Interpolate_eea

end module eea_module
