FMI1.0 CS のCo-Simulation を行う

上述の FMI_BouncingBall.ipynb では、BouncingBallモデルの入力信号である反発係数 $e$ をPythonの関数で作成しました。 この反発係数を出力する部分をMSLに含まれる Modelica.Blocks.Sources.Step でモデル化し、FMUを作成します。

FMI1.0 for Co-Simulationの簡単な例として、この反発係数を出力するモデル(model1)とBouncingBallモデル(model2)の2つのサブシステムモデルがデータ交換を行うCo-SimulationモデルをPythonスクリプトで構成します。

JModelica.org で生成できる FMU はスタンドアロン型と呼ばれ、FMUの中にサブシステムモデルとソルバーが含まれています。FMI1.0 for Co-Simulation のサブシステムには次のような状態があります。

  • Instantiated (インスタンス化終了)
  • Initialized (初期化終了)
  • StepInProgress (コミュニケーションステップの計算中)
  • StepComplete (コミュニケーションステップの計算終了)
  • StepFailed または StepInComplete (コミュニケーションステップの計算がうまくいかない状態)
  • Terminated (シミュレーション終了)

Pythonスクリプトで、load_fmu でサブシステムモデルをインスタンス化し、モデルの状態を遷移させる関数(model.initialize, model.do_step など)やデータ交換関数(model.get, model.set など)を使用して Co-Simulationプロセスを実現します。

BouncingBall モデル

BouncingBall モデルのソースコードを作成します。入力信号は反発係数$e$、出力信号は高さ$y=h$です。

In [1]:
%%writefile BouncingBall.mo
model BouncingBall
  import SI = Modelica.SIunits;
  Modelica.Blocks.Interfaces.RealInput e(start=0.8);
  Modelica.Blocks.Interfaces.RealOutput y;
  SI.Length h;
  SI.Velocity v;
  parameter SI.Acceleration g = Modelica.Constants.g_n;
  parameter SI.Height h0 = 1.0;
initial equation
  h = h0;
equation
  v = der(h);
  der(v) = -g;
  y = h;
  when h < 0 then
    reinit(v, -e * pre(v));
  end when;
end BouncingBall;
Overwriting BouncingBall.mo

FMI1.0 for Co-Simualtion の規格に従う BouncingBall モデルの FMU を生成します。

In [2]:
from pymodelica import compile_fmu
fmu = compile_fmu("BouncingBall","BouncingBall.mo", version='1.0', target='cs',compile_to = "BouncingBallCS10.fmu")

反発係数を出力するモデル

同様に、Modelica.Blocks.Souces.Step から入力信号モデルの FMU を生成します。

In [3]:
fmu = compile_fmu("Modelica.Blocks.Sources.Step", version='1.0', target='cs', compile_to = "Step.fmu")

Co-Simulation モデル

シミュレーションの開始時刻、終了時刻、communication step を決めます。

In [4]:
t_start = 0.0
t_end = 3.0
dt = (t_end - t_start)/500

Co-Simualation モデルを構成し、シミュレーションを実行します。このモデルでは、model1のコミュニケーションステップが終了してからmodel2のコミュニケーションステップを実行しています。より実用的なモデルでは、サブモデルのコミュニケーションステップが同時に進行するように非同期的な処理が必要です。また、StepFailed や StepIncomplete に対する処理も省いてエラーで停止します。

In [5]:
# load models (instantiate)
import pyfmi
model1 = pyfmi.load_fmu("Step.fmu")
model2 = pyfmi.load_fmu("BouncingBallCS10.fmu")
t = t_start

# initialize result data array
t_res = []
e_res = []
h_res = []

# initialize model1 with input parameters
model1.set("height", -0.3)
model1.set("offset", 1.0)
model1.set("startTime", 1.0)
model1.initialize()
y1 = model1.get("y")

# initialize model2 with initial input data
model2.set("e",y1[0])
model2.initialize()
y2 = model2.get("y")

t_res.append(t)
e_res.append(y1[0])
h_res.append(y2[0])

while t <= t_end:
    status = model1.do_step(t,dt)
    if status == pyfmi.fmi.FMI_OK:
        status = model2.do_step(t,dt)
        if status == pyfmi.fmi.FMI_OK:
            ## succeed to simulate communication step!
            
            # get data
            y1 = model1.get("y")
            y2 = model2.get("y")
            
            # set data
            model2.set("e",y1[0])
            
            # calculate next communication step
            t += dt
            
            # restore result data
            e_res.append(y1[0])
            h_res.append(y2[0])
            t_res.append(t)
        else:
            print("model2 error")
            break
    else:
        print("model1 error")
        break

シミュレーション結果

In [6]:
%matplotlib inline
import matplotlib.pyplot as plt
plt.figure(1)
plt.plot(t_res,e_res,t_res,h_res)
Out[6]:
[<matplotlib.lines.Line2D at 0x7f1a3e684490>,
 <matplotlib.lines.Line2D at 0x7f1a3e6845d0>]