13. Python & Batch: Python Calculator, Programmable Source, Filter

13.1. はじめに

ParaView には、基礎となるVTKライブラリと同様にpythonの数学関数にアクセスするための3つのフィルタがあります。これらは、Python Calculator、Programmable Source、Filterです。このチュートリアルでは、これらについて説明します。

13.2. Python Calculator

Python Calculatorは、Pythonで利用可能な計算を適用することができます。これらは、セルの体積を取得し、セルの面積を取得し、クロス積、ドット積、カールなどを取得するような関数が含まれています。計算式はすべて1行に収まらなければなりません。Python Calculator を例として見てみましょう。

  • 新しいポイント変数を作成し、5をロードすることができます。

    • can.ex2を開きます。

    • すべての変数をオンにします。

    • Apply を実行します。

    • Filters → Alphabetical → Python Calculator.

    • Expressionを 5 に変更します。

    • Array AssociationPoint Data に設定します。

    • Array NameCalculated Variable に変更します。

    • Apply を実行します。

    • Calculated Variable で色付けします。

  • 変位量の2倍に相当する変数を作成しましょう。

    • Expressionを DISPL*2 に変更します。

    • Apply を実行します。

    • 最後のタイムステップへ移動します。

    • データ範囲に再スケールします。

    • 注:変位データにアクセスする、より完全な方法。フィルタへの最初の入力から明示的に DISPL を引き出しています。最初の入力は[0]、2番目は[1]、などであることを忘れないでください。

    • Expressionを (inputs[0].PointData['DISPL']) * 2 に変更します。

    • Apply を実行します。

    • データ範囲に再スケールします。

  • ここでは、ベクトルとグローバル変数の掛け算の方法を説明します。DISPLにタイムステップ(TMSTEP)を掛けてみます。

    • Expressionを inputs[0].FieldData['NSTEPS'][time_index] * DISPL に変更します。

    • Apply を実行します。

    • データ範囲に再スケールします。

    • 要約すると、関数が変数の入力を必要とする場合、上記の文字列を使用します。関数が入力メッシュを必要とする場合は、inputs[]を使用します。

    Did you know?

    Pythonでは、配列には角括弧を、数式の一部をグループ化するには括弧を使用します。また、文字列(変数名)を指定する場合は、シングルクォーテーションまたはダブルクォーテーションを使用します。

  • セルの体積を格納する変数を作成しましょう。

    • Expressionを volume(inputs[0]) に変更します。

    • Apply を実行します。

    • データ範囲に再スケールします。

  • 多数の関数を1つの式にまとめることができます。例えば、カールの発散のsinを取るというのはナンセンスな表現です。以下はその式です。

    • Expressionを sin(divergence(curl('ACCL'))) に変更します。Apply -> Rescale to Data Rangeを実行します。

Python Calculatorから利用できる関数が興味深いです。

  • area(dataset)

  • aspect(dataset)

  • cos(array)

  • cross(X,Y) where X and Y are two 3D vector arrays

  • curl(array)

  • divergence(array)

  • dot(a1,a2)

  • eigenvalue and eigenvector(array)

  • gradient(array)

  • max(array)

  • mean(array)

  • min(array)

  • norm(array)

  • sin(array)

  • strain(array)

  • volume(array)

  • vorticity(array)

完全なリストはユーザーズガイドの 5.9.3 章 で見ることができます。

13.3. Programmable Filter

Programmable Filterは、Pythonを使ってパイプラインのデータを変更するために使用されます。変数を2で割るProgrammable Filterの例。

  • ACCL を2で割ります。

    • can.ex2を開きます。

    • すべての変数をオンにします。

    • Apply を実行します。

    • Filters → Alphabetical → Programmable Filter を選択します。

    • 出力データを残します。

    • Typeを Same as Input に設定します。

    • スクリプトウィンドウに以下を入力します。

      input0 = inputs[0]
      dataArray = input0.PointData["ACCL"] / 2.0
      output.PointData.append(dataArray, "ACCL_half")
      
    • ColoringACCL_half で設定する。

  • 2つのデータセットから互いに引き算をします。disk_out_ref.ex2の2つのインスタンスを2つのデータセットとして使用し、AsH3 から GaMe3 を引き算する。

    • disk_out_ref.ex2を開きます。

    • すべての変数をオンにします。

    • Apply を実行します。

    • disk_out_ref.ex2を開きます。

    • すべての変数をオンにします。

    • Apply を実行します。

    • 両方のデータセットをハイライト表示します。マウスでキーを使って、複数の入力を選択します。

    • Filters → Alphabetical → Programmable Filter を選択します。

    • 出力データを残します。

    • Typeを Same as Input に設定します。

    • スクリプトウィンドウに以下を入力します。

      v_0 = inputs[0].PointData['AsH3']
      v_1 = inputs[1].PointData['GaMe3']
      output.PointData.append(v_1 - v_0, 'difference')
      
    • Coloringdifference に設定します。

  • テンソルを作成する

    from paraview.vtk.numpy_interface import dataset_adapter as dsa
    import numpy
    def make_tensor(xx,yy,zz, xy, yz, xz):
    
          t = numpy.vstack([xx,yy,zz,xy, yz,
          xz]).transpose().view(dsa.VTKArray)
          t.DataSet = xx.DataSet
          t.Association = xx.Association
          return t
    
    xx = inputs[0].PointData["sigma_xx"]
    yy = inputs[0].PointData["sigma_yy"]
    zz = inputs[0].PointData["sigma_zz"]
    xy = inputs[0].PointData["sigma_xy"]
    yz = inputs[0].PointData["sigma_yz"]
    xz = inputs[0].PointData["sigma_xz"]
    output.PointData.append(make_tensor(xx,yy,zz,xy,yz,xz), "tensor")
    
  • 2つのタイムステップを互いに引き算する

    • can.ex2の読み込み

    • Filters → Alphabetical → Force Time を選択します。

    • タイムステップには、変位をゼロにしたいものを設定します。

    • このフィルタの出力は、選択したタイムステップで "凍結された" can.ex2 データセットです。これは、時間を進めても変化しない。

    • パイプラインブラウザで、can.ex2 (CTRL キーを使用) と ForceTime1 ソースの両方を選択します。元のcan.ex2ソースはタイムステップの変更に伴って更新されますが、ForceTime1 ソースは変更されないことに注意してください。

    • Filters → Alphabetical → Python Calculator に選択します。

    • Substract the displacement in the "frozen" dataset from the current timestep in can.ex2.

    • Expressionを inputs[0].PointData['DISPL'] - inputs[1].PointData['DISPL'] にセットします。

    • Python Calculator への入力の順序はうまく定義されていないので、結果の符号を正しくするためにinputs[0]とinputs[1]のインデックスを入れ替える必要があるかもしれませんが、これでうまくいくはずです。

    • (必要であれば、) 最後に、Plot Selection over Timeフィルタを追加します。このフィルタは、すべてのタイムステップにわたって実行され、現在のタイムステップのデータから、時間強制フィルタで生成された "凍結" タイムステップのデータを引き、その結果をグラフにプロットします。最初のタイムステップの値は0であるべきです。

  • 固有ベクトルを読み、固有値を計算し、3変数に入れる

    # ParaView Programmable Filter script.  ParaView 5.0.1.
    #
    # This code will read in an eigenvector, calculate an
    #    eigenvalue, and place it into three variables.
    #
    # Be sure to correct the input variables below.  Also, note that
    #   the code uses ZX, not XZ.
    #
    # This code only works with any multiblock or vtk
    #   datasets (including ones with only one block - i.e.,
    #   Exodus datasets as input).
    #
    # Usage:  Run the Programmable Filter.
    #   Cut and paste this file in the section named "Script".
    #   Leave "Output Data Set Type" as "Same as Input".
    #   Click Apply button
    #
    # Written by Jeff Mauldin and Alan Scott
    #
    
    import numpy as np
    
    def process_composite_dataset(input0):
      # Pick up input arrays
      xxar = input0.CellData["EPSXX"]
      xyar = input0.CellData["EPSXY"]
      zxar = input0.CellData["EPSZX"]
      yyar = input0.CellData["EPSYY"]
      yzar = input0.CellData["EPSYZ"]
      zzar = input0.CellData["EPSZZ"]
    
      #print `xxar`
      #print len(xxar.Arrays)
    
      # Set output arrays to same type as input array.
      # Do a multiply to make sure we don't just have a
      # pointer to the original.
      outarray0 = xxar*0.5
      outarray1 = xxar*0.5
      outarray2 = xxar*0.5
    
    
      # Run a for loop over all blocks
      numsubarrays = len(xxar.Arrays)
      for ii in range(0, numsubarrays):
        # pick up input arrays for each block.
        xxarsub = xxar.Arrays[ii]
        xyarsub = xyar.Arrays[ii]
        zxarsub = zxar.Arrays[ii]
        yyarsub = yyar.Arrays[ii]
        yzarsub = yzar.Arrays[ii]
        zzarsub = zzar.Arrays[ii]
    
        #print `xxarsub`
    
        # Transpose and calculate the principle strain.
        strain = np.transpose(
            np.array(
              [ [xxarsub, xyarsub, zxarsub],
                [xyarsub, yyarsub, yzarsub],
                [zxarsub, yzarsub, zzarsub] ] ),
                  (2,0,1))
    
        principal_strain = np.linalg.eigvalsh(strain)
    
        # Move principle strain to temp output arrays for this block
        outarray0.Arrays[ii] = principal_strain[:,0]
        outarray1.Arrays[ii] = principal_strain[:,1]
        outarray2.Arrays[ii] = principal_strain[:,2]
    
      #ps0 = principal_strain[:,0]
      #print "ps0 len: " + str(len(ps0))
    
      # Finally, move the temp arrays to output arrays
      output.CellData.append(outarray0, "principal_strain_0")
      output.CellData.append(outarray1, "principal_strain_1")
      output.CellData.append(outarray2, "principal_strain_2")
    
    
    def process_unstructured_dataset(input0):
      # Pick up input arrays
      xxar = input0.CellData["EPSXX"]
      xyar = input0.CellData["EPSXY"]
      zxar = input0.CellData["EPSZX"]
      yyar = input0.CellData["EPSYY"]
      yzar = input0.CellData["EPSYZ"]
      zzar = input0.CellData["EPSZZ"]
    
      #print `xxar`
      #print len(xxar.Arrays)
    
      # Transpose and calculate the principle strain.
      strain = np.transpose(
            np.array(
              [ [xxar, xyar, zxar],
                [xyar, yyar, yzar],
                [zxar, yzar, zzar] ] ),
                  (2,0,1))
    
      principal_strain = np.linalg.eigvalsh(strain)
    
      #ps0 = principal_strain[:,0]
      #print "ps0 len: " + str(len(ps0))
    
      # Finally, move the temp arrays to output arrays
      output.CellData.append(principal_strain[:,0],
         "principal_strain_0")
      output.CellData.append(principal_strain[:,1],
         "principal_strain_1")
      output.CellData.append(principal_strain[:,2],
         "principal_strain_2")
    
    input0 = inputs[0]
    
    if input0.IsA("vtkCompositeDataSet"):
      process_composite_dataset(input0)
    elif input0.IsA("vtkUnstructuredGrid"):
      process_unstructured_dataset(input0)
    else:
      print "Bad dataset type for this script"
    

重要: Programmable SourceProgrammable Filter はサーバーレベルで動作するため、paraview.simple はロードまたは使用できません。

Programmable Filter に関するモードの詳細は、リファレンスマニュアルの 5 章 に記載されています。

Programmable Filter に関連するブログ記事から興味深いものを選んでみました。