倭算数理研究所

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

シミュレーション結果をチャートで出力

以前の記事でいくつかのタイプの「データ出力」方法を見ました。 その時点では、シミュレーション結果をチャート(グラフ)で出力するのは別途 GUI アプリケーションを作成してサポートするつもりでしたが、やはりシミュレーション結果を手軽にチャートとして見ることができないのは苛立たしいので、JFreeChart の力を借りて画像ファイルとして出力できるようにしました。 あまり細かくチャートを設定できるようにはしてませんが、シミュレーション結果を手軽にチャートで確認するには充分かと。

チャート出力の基本


では、まず最低限必要なデータ出力の設定を見てみましょう。 SimulationBuilder の simulation ノードに以下のような png ノードを付加します:

new SimulationBuilder().simulation{
    ...
    png(fileName:'chart1.png', overwrite:true){
        chart(title:'Time-Temperature Chart'){
            point(label:'Temperature', x:'time', y:'T')
        }
    }
}

ちょっとノードが無駄にネストされてる感じもしますがご了承を。 以下、それぞれのノードを簡単に説明します:

  • png ノードは出力する画像ファイルに関する設定。 png ノードは PNG 形式の画像を出力します。 fileName, overwrite 属性は説明の必要なしかと。
  • chart ノードは出力するチャートに関する設定。 ここではチャートのタイトルを設定しています。
  • point ノードはプロットするデータに関する設定。 label 属性は凡例に表示されるラベル、x, y はプロットに使うデータです。

この設定でチャートを出力すると以下のような PNG 画像が chart1.png というファイルで作成されます:

f:id:waman:20110625022508p:image

なかなかいい感じでは? 以下では、各ノードで設定できる項目をもう少し詳しく見ていきます。

png, jpeg / jpg ノード


上記の例では PNG ファイルで出力するために png ノードを使用しましたが、他にも JPEG 形式の画像としても出力できます。 この場合は jpeg / jpg ノードに同様の設定を書くだけです。 png ノードだけ、もしくは jpeg / jpg ノードだけに特有の設定項目はありません。

chart ノード


chart ノードではチャートの設定を行います。 上記の例ではチャートのタイトルを設定していましたが、他にも軸の設定が行えます。 例えば 軸のラベルや表示範囲を設定するには以下のようにします:

new SimulationBuilder().simulation{
    ...
    png(fileName:'chart2.png', overwrite:true){
        chart(title:'Time-Temperature Chart',
                domainLabel:'time', domain:[0d, 2d],
                rangeLabel:'Temperature', range:[0d, 85d]){
            point(label:'Temperature', x:'time', y:'T')
        }
    }
}

ここでは

  • domainLabel 属性は x 軸のラベル(XLabel 属性でも可)、rangeLabel 属性は y 軸のラベル(YLabel でも可)です。
  • domain 属性は x 軸の表示範囲、range 属性は y 軸の表示範囲です(どちらも Double のリスト)。

の設定を行っています。 チャートを出力するとこうなります:

f:id:waman:20110625022908p:image

他に設定できる項目は、org.jfree.chart.ChartFactory#createScatterPlot() メソッドに設定できる項目です:

  • orientation : PlotOrientation
  • legend : boolean
  • tooltips : boolean
  • url : boolean

詳しくは JFreeChart の ChartFactory のドキュメントを参照してください(こちら)。

point ノード


いちばん最下層のノードである point ノードはチャートにプロットする点を設定します。 このノードは data ノードでも構いません。 基本的にはデータ系列のラベル(凡例に使用)と x, y 座標に用いるデータ(物理量の名前。 もちろん登録されている必要があります)を設定するだけですが、x, y 属性にはクロージャを設定して、プロットする点をイジることができます。 例えば、最初のチャートで、温度に 10 を加えた値をプロットしたい場合は以下のようにします(2つ目の point ノード):

new SimulationBuilder().simulation{
    ...
    png(fileName:'chart3.png', overwrite:true){
        chart(title:'Time-Temperature Chart'){
            point(label:'T', x:'time', y:'T')
            point(label:'T+10', x:'time', y:{ it.T+10d })
        }
    }
}

このクロージャには、観測量を保持している DataStore オブジェクトが渡されるので、プロパティアクセス(例では it.T)のようにして観測量のデータにアクセスできます。 これをプロットしたデータは以下のようになります:

f:id:waman:20110625022506p:image

言い忘れてましたが、point ノードを複数書けば、複数の系列のデータがプロットできます!

日本語を使う


最後に、ノードとは直接関係ありませんが、チャートのタイトルや軸ラベルに日本語を使いたい場合は、(通常の JFreeChart の場合と同様)ChartFatory.chartTheme プロパティを設定する必要があります:

import org.jfree.chart.StandardChartTheme
import org.jfree.chart.ChartFactory

// 日本語対応にするための設定
ChartFactory.chartTheme = StandardChartTheme.createLegacyTheme()

new SimulationBuilder().simulation{
    ...
    png(fileName:'japanese-chart.png', overwrite:true){
        chart(title:'時間-温度 図', domainLabel:'時間', rangeLabel:'温度', ){
            point(label:'温度', x:'time', y:'T')
        }
    }
}

チャートを出力するとこうなります:

f:id:waman:20110625024240p:image

動くサンプルコード


以上をまとめて動くサンプルをどうぞ。 groops-core のバージョンは 0.1-beta にアップしてください:

@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.system.*

new SimulationBuilder().simulation{
    system(new CoolSystem(), dt:0.1, T_room:17.0d, r:1.0d)
    initialConditions(time:0.0, T:82.3d)
    systemObservableSet()

    observe(continueWhile:{ it.T >= 30d })

    png(fileName:'chart1.png', overwrite:true){
        chart(title:'Time-Temperature Chart'){
            point(label:'Temperature', x:'time', y:'T')
        }
    }
    png(fileName:'chart2.png', overwrite:true){
        chart(title:'Time-Temperature Chart',
                domainLabel:'time', domain:[0d, 2d],
                rangeLabel:'Temperature', range:[0d, 85d]){
            point(label:'Temperature', x:'time', y:'T')
        }
    }
    png(fileName:'chart3.png', overwrite:true){
        chart(title:'Time-Temperature Chart'){
            point(label:'T', x:'time', y:'T')
            point(label:'T+10', x:'time', y:{ it.T+10d })
        }
    }
}.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()
    }
}

コードはこちらからダウンロードできます:

計算物理学入門

計算物理学入門

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