#!/usr/bin/env ruby

=begin

=gpcat

複数ファイルに対して時空間方向に接続して X 平均を取る. 
指定されたディレクトリ以下の time* というディレクトリに対して再帰的に実行. 

==USAGE

  % arare-addmean-velz.rb --dir Directory


==OPTIONS

      --help         : print this message. 
      --dir dirvar   : directory name (required).

==HISTORY

  2009/04/23  K Sugiyama (created)

=end

require "numru/netcdf"
require "numru/dcl"
require "numru/ggraph"
require "getoptlong"
include NumRu

###
###引数処理
###
parser = GetoptLong.new
parser.set_options(
                   ###    global option   ###
                   ['--dir',  "-d", GetoptLong::REQUIRED_ARGUMENT],
                   ["--help", "-h", GetoptLong::NO_ARGUMENT ]
                   )
begin
  parser.each_option do |name, arg|
    eval "$OPT_#{name.sub(/^--/, '').gsub(/-/, '_')} = '#{arg}'" 
  end
    rescue
  exit(1)
end

if $OPT_HELP then
  help
  exit(0)
end  

###
### 初期設定
###
coords = ["x", "z", "t", "s"] # 軸の設定
dir = "#{$OPT_dir}"           # トップディレクトリ

# ファイル置き場
meanfiles = "meanfiles"	 
submeanfiles = "#{meanfiles}/#{dir.sub("./","")}"
Dir::mkdir(meanfiles) unless FileTest.exist?(meanfiles)
Dir::mkdir(submeanfiles) unless FileTest.exist?(submeanfiles)

# 変数名の保管用
varnames = Array.new

###
### 水平平均を行う
###   並列版の場合は, 水平方向に結合する. 
###   水平平均された量に関しては, ファイル名を変更するだけ.
###

Dir.glob("#{dir}/time*").each{ |subdir|
  p "TOPDIR: #{dir}, SUBDIR: #{subdir}"

  # ファイル名を保管する配列を用意する. 
  ncfiles = Array.new 
  mpi1 = Array.new
  mpi2 = Array.new
  mpi3 = Array.new

  Dir::glob("#{subdir}/*").each{ |file|
    next unless  /.VelZ*\.nc/ =~ file # velz 以外は無視
    next if /restart/ =~ file      # リスタートファイルは対象外.
    
    # 並列計算の場合は node* を外して保管
    if /^(.*)-node([0-9][0-9][0-9])_(.*.nc)$/=~ file
      mpi1.push("#{$1}")
      mpi2.push("#{$2}")
      mpi3.push("#{$3}")
    else
      ncfiles.push(file)
    end
  }
  
  # 重複を切る.
  mpi1.uniq!
  mpi2.uniq!
  mpi3.uniq!

  # 逐次版と並列版では処理を変える. 
  # GPhys で仮想的に 1 つのファイルとして扱うことはできるが, 
  # メモリを食い過ぎてしまうので仕方ない. 
  if mpi1.size == 0
    ncfiles.each{ |ncfile|
      GDir.top = '/'
      gdir = GDir.new(File.expand_path(ncfile))

      gdir.list_data_v.each{|gd| 
        vars = gd.split(/\t/)
        next if coords.index(vars[0]) # 軸定義の場合は除外.
        varnames.push(vars[0])        # 変数名を保存
        output1 = "#{submeanfiles}/#{File::basename(subdir)}_VelZRMS.nc"
        output2 = "#{submeanfiles}/#{File::basename(subdir)}_VelZSKW.nc"
        output3 = "#{submeanfiles}/#{File::basename(subdir)}_VelZKRT.nc"

        # 既に存在する場合
        if FileTest.exist?(output1)
          # 既に存在していても mtime が古い場合はスキップしない.
          mtime = File.mtime(output1)  - File.mtime(ncfile) 
          next if mtime > 0
          p "#{ncfile}, #{File.mtime(ncfile)}, #{output1}, #{File.mtime(output1)}"
        end

        gturl = "#{ncfile}@#{vars[0]}"
        gphys = GPhys::IO.open_gturl(gturl)
        
        MEAN = gphys.mean('x')
        DV   = gphys - MEAN
        RMS  = ((gphys ** 2.0e0).mean('x')).sqrt
        SDV  = ((DV ** 2.0e0).mean('x')).sqrt
        SKW  = ((DV ** 3.0e0).mean('x')) / (SDV ** 3.0e0)
        KRT  = ((DV ** 4.0e0).mean('x')) / (SDV ** 4.0e0) - 3.0e0

        outncfile=NetCDF.create(output1)
        GPhys::IO.write( outncfile, RMS.rename('VelZRMS') ) # Output GPhys variable
        outncfile.close
        p "#{gturl} is written to #{output1}."

        outncfile=NetCDF.create(output2)
        GPhys::IO.write( outncfile, SKW.rename('VelZSKW') ) # Output GPhys variable
        outncfile.close
        p "#{gturl} is written to #{output2}."

        outncfile=NetCDF.create(output3)
        GPhys::IO.write( outncfile, KRT.rename('VelZKRT') )  # Output GPhys variable
        outncfile.close
        p "#{gturl} is written to #{output3}."
      }
    }
  else
    mpi3.each{ |mpivar|
      ncfile = "#{mpi1[0]}-node000_#{mpivar}"  # ノード 0 で存在する変数を調べる.
      GDir.top = '/'
      gdir = GDir.new(File.expand_path(ncfile))
      
      # 変数をチェック
      gdir.list_data_v.each{|gd| 
        vars = gd.split(/\t/)
        next if coords.index(vars[0]) # 軸定義の場合は除外.
        varnames.push(vars[0])        # 変数名を保存

        # ファイルを用意.
        output1 = "#{submeanfiles}/#{File::basename(subdir)}_VelZRMS.nc"
        output2 = "#{submeanfiles}/#{File::basename(subdir)}_VelZSKW.nc"
        output3 = "#{submeanfiles}/#{File::basename(subdir)}_VelZKRT.nc"

        # 既に存在する場合
        if FileTest.exist?(output1)
          # 既に存在していても mtime が古い場合はスキップしない.
          mtime = File.mtime(output1)  - File.mtime(ncfile) 
          next if mtime > 0
          p "#{ncfile}, #{File.mtime(ncfile)}, #{output1}, #{File.mtime(output1)}"
        end
        
        # ノード毎にファイルを開いて平均をとる. 
        gphyss1 = Array.new
        gphyss2 = Array.new
        gphyss3 = Array.new
        mpi2.each{ |mpinum|           
          gturl = "#{mpi1[0]}-node#{mpinum}_#{mpivar}@#{vars[0]}"
          p "#{mpinum.to_i}/#{mpi2.size}, #{gturl}"
          gphys = GPhys::IO.open_gturl(gturl)

          MEAN = gphys.mean('x')
          DV   = gphys - MEAN
          RMS  = ((gphys ** 2.0e0).mean('x')).sqrt
          SDV  = ((DV ** 2.0e0).mean('x')).sqrt
          SKW  = ((DV ** 3.0e0).mean('x')) / (SDV ** 3.0e0)
          KRT  = ((DV ** 4.0e0).mean('x')) / (SDV ** 4.0e0) - 3.0e0

          gphyss1.push(RMS)
          gphyss2.push(SKW)
          gphyss3.push(KRT)
        }        
        gphys2 = 0.0 
        gphyss1.each{ |gp| gphys2 = gphys2 + gp}
        gphys2 = gphys2 / mpi2.size
        outncfile=NetCDF.create(output1)              
        GPhys::IO.write( outncfile, gphys2.rename('VelZRMS') )          # Output GPhys variable
        outncfile.close
        p "#{mpivar} is written to #{output1}."

        gphys2 = 0.0 
        gphyss2.each{ |gp| gphys2 = gphys2 + gp}
        gphys2 = gphys2 / mpi2.size
        outncfile=NetCDF.create(output2)              
        GPhys::IO.write( outncfile, gphys2.rename('VelZSKW') )          # Output GPhys variable
        outncfile.close
        p "#{mpivar} is written to #{output2}."

        gphys2 = 0.0 
        gphyss3.each{ |gp| gphys2 = gphys2 + gp}
        gphys2 = gphys2 / mpi2.size
        outncfile=NetCDF.create(output3)              
        GPhys::IO.write( outncfile, gphys2.rename('VelZKRT') )          # Output GPhys variable
        outncfile.close
        p "#{mpivar} is written to #{output3}."
      }      
    }
  end
}

###
### 時間方向に接続する. 
### ファイル名が正しく時系列に並んでいることを前提とする. 
###
varnames.uniq!
varnames.each{ |var|
  vars = ["VelZRMS", "VelZSKW", "VelZKRT"]
  prefix = "#{submeanfiles}/AllData"

  vars.each{ |var|
    output = "#{prefix}_#{var}.nc"

    # 既に存在する場合. 
    File.unlink(output) if FileTest.exist?(output)
    ncfiles2 = Dir::glob("#{submeanfiles}/*_#{var}.nc").sort
    p ncfiles2, var
    unless ncfiles2.size == 1 
      gphys3 = GPhys::IO.open(ncfiles2, var)
      print "GPhys variable '#{var}' in NetCDF files, " + ncfiles2.join(', ') +", was opened successfully.\n"
    else
      gphys3 = GPhys::IO.open(ncfiles2[0], var)
      print "GPhys variable '#{var}' in NetCDF files, " + ncfiles2[0] +", was opened successfully.\n"
    end
    
    outncfile=NetCDF.create(output)
    GPhys::IO.write( outncfile, gphys3 )
    p "#{var} is written to #{output}."
    outncfile.close
  } 
}
