倭算数理研究所

科学・数学・学習関連の記事を、「倭マン日記」とは別に書いていくのだ!

シミュレーションをビルダーで構築する SimulationBuilder

さて、前回までで反復シミュレーションを構成するコンポーネントの説明が一通り終わりました。 なので、今回はそれらを使って反復シミュレーションを構築する方法を見ていきます。

コンポーネントをコンストラクタでインスタンス生成して、アクセッサ・メソッド等でセットアップしていくこともできますが、Groovy を使ってるなら、やはりビルダーを使って構築できるべきですヨネ。 ってことで SimulationBuilder ってクラスを実装したので、それを使って反復シミュレーションを構築していきましょう。

ビルド・スクリプト


まずは反復シミュレーションを構築するスクリプトがどんな感じなのかを見てみましょう。 前提として次の2つのクラスが既に定義されているとします:

  • 物理系 : CoolSystem ・・・ ニュートンの冷却の法則に従う系
  • 観測量 : CoolObservableSet ・・・ CoolSystem の支配変数を観測する観測量。

コードの詳細はこの記事最後の「サンプルコード : 完成版」を参照のこと。 これらが引数なしのコストラクタを持つとします。 このとき、反復シミュレーションは SimulationBuilder を用いて以下のように構築します:

import org.waman.groops.builder.SimulationBuilder

def builder = new SimulationBuilder()
def sim = builder.simulation{
    system(new CoolSystem(), dt:0.1, T_room:17.0d, r:1.0d)
    initialConditions(time:0.0, T:82.3d)
    observableSet(new CoolObservableSet())

    iterationCondition(type:'count', total:10)
    dataOutputter(type:'console')
}

// シミュレーションの実行
sim.simulate()

ここで構築されている反復シミュレーションは「物理系 CoolSystem、観測量 CoolObservableSet で、10回反復し、結果を標準出力に書き出す」というものになります。 所々に現れている Map の引数はプロパティの設定です。 各ノードをもう少し詳しく見ていきましょう。

シミュレーションのビルドに使用できるノード


以前の記事

  • 反復シミュレーション (IterativeSimulator) は
    • 1つの物理系 (PhysicalSystem) を持つ
    • 0個以上の初期条件 (InitialConditions)を持つ
    • 0個以上の物理量(観測可能量 Observable)のセットを持つ
    • 1つの反復条件 (IterationCondition) を持つ
    • 0個以上のデータ出力 (DataOutputter) を持つ

と書きましたが、これらそれぞれのコンポーネントに対応するノードが定義されています:

ノード 説明
simulation (反復)シミュレーション*1
system 物理系
initialConditions 初期条件
observableSet 観測量のセット
iterationCondition 反復条件
dataOutputter データ出力

物理系や観測量のようにスクリプト中でコンストラクタからインスタンス生成しているコンポーネントは、対応するノード(system, observableSet)に () でインスタンスを渡しています(ただし Map ではない):

    system(new CoolSystem(), ...)
    observableSet(new CoolObservableSet())

一方、反復条件やデータ出力のように Groops で提供されているコンポーネントを使う場合は type 属性としてタイプを表す文字列(以下、タイプ・キーワード)を指定すれば、あとはプロパティの設定をするだけです:

    iterationCondition(type:'count', total:10)
    dataOutputter(type:'console')

使用できるタイプは以前の記事「反復条件 IterationCondition」と「データ出力 DataOutputter」を参照のこと。 初期条件を設定する initialConditions ノードは属性にキーと値を設定します。 まぁ、Groops で反復シミュレーションをどうモデル化してるかが頭にあれば難しくはないと思います。

少々の簡略化


上記のようなスクリプトで特に問題ないと思いますが、Groops 側でサポートされているコンポーネント iterationCondition と dataOutputter は常に タイプ・キーワードも指定しないといけないのでちょっと面倒でもあります。 なので、それらのタイプ・キーワードをノード名にして、以下のようにも書けるようにしてあります:

import org.waman.groops.builder.SimulationBuilder

def builder = new SimulationBuilder()
def sim = builder.simulation{
    system(new CoolSystem(), dt:0.1, T_room:17.0d, r:1.0d)
    initialConditions(time:0.0, T:82.3d)
    observableSet(new CoolObservableSet())

    count(total:10)
    console()
}

// シミュレーションの実行
sim.simulate()

これだと、タイプ・キーワードが「反復条件」を表しているのか「データ出力」を表しているのか、知っていないと判断できませんが、反復条件、データ出力を合わせてもタイプ・キーワードの数は大して多くないので問題ないかと思います。

サンプルコード : 完成版


以上をまとめると、反復シミュレーションを構築して実行するスクリプトは以下のようになります(動かすには Java 7 SE、Groovy 2.0.0-beta-2 のインストールが必要):

@GrabResolver('https://github.com/waman/groops-core/tree/master/repo')
@Grab('org.waman.groops:groops-core:0.1-beta')

import org.waman.groops.builder.SimulationBuilder
import org.waman.groops.simulation.DataStore
import org.waman.groops.simulation.system.*
import org.waman.groops.simulation.observable.*

def builder = new SimulationBuilder()
def sim = builder.simulation{
    system(new CoolSystem(), dt:0.1, T_room:17.0d, r:1.0d)
    initialConditions(time:0.0, T:82.3d)
    observableSet(new CoolObservableSet())

    count(total:10)
    console()
}

sim.simulate()

/** Physical System subject to Newton's low of cooling. */
class CoolSystem extends AnnotationPhysicalSystem{

    @State BigDecimal time
    @State double T

    @Parameter BigDecimal dt
    @Parameter T_room
    @Parameter r

    @Override
    void evolve(Map<String, Object> params){
        time += dt
        T += -r*(T - T_room)*dt.doubleValue()
    }
}

/** ObservableSet for CoolSystem */
class CoolObservableSet extends AnnotationObservableSet<CoolSystem>{

    @DataEntry(BigDecimal.class)
    static final String TIME = 'Time'

    @DataEntry(Double.class)
    static final String TEMPERATURE = 'Temperature'

    @Override
    protected void observe(CoolSystem system, DataStore store){
        store[TIME] = system.time
        store[TEMPERATURE] = system.T
    }
}

関連ファイルは以下からダウンロード可能:

また、Groops は GitHub のリポジトリで開発してます:

計算物理学入門

計算物理学入門

  • 作者: ハーベイゴールド,ジャントボチニク,Harvey Gould,Jan Tobochnik,鈴木増雄,石川正勝,溜渕継博,宮島佐介
  • 出版社/メーカー: ピアソンエデュケーション
  • 発売日: 2000/12
  • メディア: 単行本
  • クリック: 5回
  • この商品を含むブログ (45件) を見る

*1:実際には反復シミュレーションを表すインターフェース IterativeSimulator のスーパーインターフェース Simulator のオブジェクトを返します。