3章 CommonMPの要素モデルを作成する
3.4 要素モデルを作成する(2章のモデルをCommonMP要素モデルに移植する)
スタブが完成した所で、要素モデル内に計算論理を組み込みます。 本章では、2章で作成したDynamicWaveモデル/KinematicWaveモデル(RiverMDL2.lzh)を、CommonMPの要素モデルに移植します。
(演算中データクラス:MyDynamicWaveCalInfo の実装)
CommonMPでは、計算中の情報を「演算中データ」に入れることを説明しました。 2章で作成したモデル:DynamicWaveModel
のメンバー変数を 演算中データ:MyDynamicWaveCalInfo にコピーします。 尚、メンバー変数は すべて public
宣言しておきます。
[Serializable] |
演算中データクラスでスタブの中身を実装します。
・public override McCalInfo
Clone() メソッドは そのままで動作しますので、改めてコーディングする必要はありません。
・public
override bool CopyInfo(McCalInfo
csOrgInfo) メソッドは 引数で与えられたクラス内のすべてのメンバー変数値を自クラスのメンバー変数に代入します。
public override bool
CopyInfo(McCalInfo
csOrgInfo)
|
(注意)
MyDynamicWaveCalInfo csOrdDt = csOrgInfo as MyDynamicWaveCalInfo; でも良い
とすることで、インスタンス csOrgInfo を そのまま csOrdDt
という変数に置き換えて、派生クラス:MyDynamicWaveCalInfo として扱うことができるようになります。
キャストされたcsOrdDt
を用いて
XL =
csOrdDt.XL;
NX = csOrdDt.NX;
SLOPE = csOrdDt.SLOPE;
WIDTH = csOrdDt.WIDTH;
等のように派生クラスのメンバー変数に直接アクセスできるようになります。
変数X,H,H1,Q,Q1,V
は配列なので、コピー時に配列を確保( new double[NXX] )
して使用しています。
(要素モデルクラス:MyDynamicWaveModel の実装)
要素モデルのメンバー変数としては、「演算中データクラス」に纏めて持つ事を説明しました。
要素モデルのメンバー変数として 演算中データクラスは 既に親クラスに
McCalInfo
m_csCalInfo;
として、定義されています。 しかし、このままでは、派生クラス:MyDynamicWaveCalInfo
としては使用出来ません。
そこで、此処でもキャスを行います。
先ず、キャストのターゲットとなる 派生クラス側のメンバー変数を 要素モデル内に定義しておきます。
public class MyDynamicWaveModel :
McForecastModelBase { /// <summary> 演算データ(キャスト用) </summary> MyDynamicWaveCalInfo m_csMyInf = null; // 便利の為、キャスト用に定義しておく |
しかし、このままでは、実体が無いので、何処かで 派生クラス側に対して 親クラスの csCalInfo
を代入しておく必要があります。
プロパティ設定メソッド
・public override
bool SetProperty(McCellModelPropertyIF
csCellMdlPropertyInfo)
プロパティ設定メソッドは、要素モデルのインスタンスが生成された時(すなわち、インスタンスが生成された後 一番最初に)呼ばれます。 そこで、先の 「演算データ」のキャストは此処で行います。
public override bool
SetProperty(McCellModelPropertyIF
csCellMdlPropertyInfo) { bool bRtn = false; // 使用しやすいようにキャストしておく m_csMyInf = (MyDynamicWaveCalInfo)m_csCalInfo; |
また、第2章で作成したDynamicWaveLineの INPUT(PrptyInf
csPrpty)内の処理をコピーします。
// To
Do
m_csMyInf.XL = 100.0 *
1000.0;
m_csMyInf.SLOPE = 1.0 /
2000.0;
m_csMyInf.QB =
200.0; |
但し、各変数は 演算中データ内に存在しているので、 m_csMyInf.(変数) と記述します。
さらに、 計算で使用する配列を此処で確保します。
m_csMyInf.X = new
double[m_csMyInf.NXX]; m_csMyInf.H = new double[m_csMyInf.NXX]; m_csMyInf.H1 = new double[m_csMyInf.NXX]; m_csMyInf.Q = new double[m_csMyInf.NXX]; m_csMyInf.Q1 = new double[m_csMyInf.NXX]; m_csMyInf.V = new double[m_csMyInf.NXX]; |
入力/出力チェック
・protected override bool
ReceiveConnectionCheck(ref McStructErrorInfo csErrorInf, long lInputDataNum,
McReceiveCellDataIF[] csInputCellData)
・protected override bool
SendConnectionCheck(ref McStructErrorInfo csErrorInf, long lOutputDataNum,
McSendCellDataIF[]
csOutputCellData)
最初のモデルは、入出力を持たないので、スタブのままとしておきます。
初期値設定
・protected
override bool Initialize(ref McPropertyInfoRoot csInitialData, long
lInputDataNum, ref McReceiveCellDataIF[]
csInputCellData)
第2章で作成したDynamicWaveLineの INITIAL( )内の処理をコピーします。
// To Do
double QINIT =
QIN(0.0); |
SetProperty()メソッドの場合と同様に m_csMyInf.(変数)
と記述します。
計算直前/計算終了直後処理
・public override bool
ReadyCalculation()
・public override bool
CompleteCalculation()
スタブのままにしておきます。
計算処理
・protected
override long Calculate(long lInputDataNum, ref McReceiveCellDataIF[]
csInputCellData)
第2章で作成したDynamicWaveLineの Calculate( )内の処理をコピーします。
ここで、第2章で作成したDynamicWaveLine内部で使用されていた δT: m_DT
は CommonMP要素モデルでは、m_csDltTime
に相当しますので、
double m_DT =
m_csDltTime.GetTime();
とします。
同様に、第2章で作成したDynamicWaveLine経過時間 m_T
は CommonMP要素モデルでは、m_csTotalPassingTime
に相当しますので、
double m_T = m_csTotalPassingTime.GetTime();
とします。
CommonMP 要素モデル内の時間管理変数について
CommonMPの演算要素モデルは以下のメンバー変数で時間を管理しています。
@ m_csSimTime ・・・・・・・・・・・ シミュレーション時刻
A m_csDltTime ・・・・・・・・・・・ 要素モデルのδT(時間増分)
B m_csTotalPassingTime ・・・・・ 計算開始時刻からの経過時間
C m_csStartTime ・・・・・・・・・・・ シミュレーション開始時時刻
D m_csTgtTime ・・・・・・・・・・・・ 当面の目標計算時刻
(いずれも実時間ではない事に注意して下さい)
m_csSimTimeには、2013/06/16/12:00 等の時刻が入っています。
一方 m_csTotalPassingTime は、計算が開始してからの経過時間が秒単位で格納されています。
m_csStartTime は計算開始の時刻であり、 m_csTotalPassingTime = m_csSimTime - m_csStartTime です。
m_csTgtTime は、要素モデルの当面の計算目標時刻です。目標時刻は 計算中に自動的に変化するので、派生クラス側で参照してもあまり意味がありません。
//
To
Do
double dQIN =
QIN(m_T); for
(long I = 1; I <= m_csMyInf.NX;
I++)
m_csMyInf.H1[I] = m_csMyInf.H[I] - FQ *
m_DT; for
(long I = 0; I <= m_csMyInf.NX;
I++) |
CommonMPの時間クラス:HySTime について
CommonMPの内部では、時間は全て、特有の時間クラス:HySTime で管理されています。 内部には、double 型の変数が入っており、1.0 は1秒に相当します。(3600.0 は1時間、 60.0 は1分、 0.001 は1ミリ秒 に相当します。) HySCalendar クラスを用いて、文字列等に変換できます。
計算結果出力処理
・protected override long DataFusion(long
lOutputDataNum, ref McSendCellDataIF[]
csOutputCellData)
第2章で作成したDynamicWaveLineの OUTPUT( )内の処理をコピーします。 Calculate(
)メソッドの場合と同様に、
double m_DT =
m_csDltTime.GetTime();
double m_T = m_csTotalPassingTime.GetTime();
とします。
double m_DT =
m_csDltTime.GetTime(); if
((3600 * (long)(m_T / 3600.0) - (long)m_T) ==
0) |
追加メソッド
第2章で作成したDynamicWaveLine は
独自のメソッドを持っていました。そこで、要素モデル:MyDynamicWaveModel
にも同様のメソッドを追加します。
・private double QIN(double
T)
・private void COEFF(long I,ref double FQ, ref double FV,
ref double FP, ref double FF)
DynamicWaveLine
のQIN( )、 COEFF( ) メソッドをそのままコピーします。 但し、m_csMyInf.(変数)と記述します。
//------------- // FUNCTION QIN private double QIN(double T) { double TAU = T / m_csMyInf.TP; // QIN=(QB+(QP-QB) * (TAU*EXP(1-TAU))**CP)/WIDTH double dQin = (m_csMyInf.QB + (m_csMyInf.QP - m_csMyInf.QB) * Math.Pow((TAU * Math.Exp(1 - TAU)), m_csMyInf.CP)) / m_csMyInf.WIDTH; return dQin; } // end of QIN // ------------- // FUNCTION COEFF private void COEFF(long I, ref double FQ, ref double FV, ref double FP, ref double FF) { if (I < m_csMyInf.NX) { FQ = (m_csMyInf.Q[I + 1] - m_csMyInf.Q[I - 1]) / 2.0 / m_csMyInf.DX; FP = m_csMyInf.G * m_csMyInf.H[I] * (m_csMyInf.SLOPE - (m_csMyInf.H[I + 1] - m_csMyInf.H[I - 1]) / 2.0 / m_csMyInf.DX); } else { FQ = (m_csMyInf.Q[I] - m_csMyInf.Q[I - 1]) / m_csMyInf.DX; FP = m_csMyInf.G * m_csMyInf.H[I] * (m_csMyInf.SLOPE - (m_csMyInf.H[I] - m_csMyInf.H[I - 1]) / m_csMyInf.DX); } FV = (m_csMyInf.V[I] * m_csMyInf.Q[I] - m_csMyInf.V[I - 1] * m_csMyInf.Q[I - 1]) / m_csMyInf.DX; FF = m_csMyInf.G * Math.Pow(m_csMyInf.ROUGH, 2.0) * m_csMyInf.Q[I] / Math.Pow(m_csMyInf.H[I], (7.0 / 3.0)); } |
protected virtual void
FileOut(string csString)
csStreamWriter.WriteLine(csString);
csStreamWriter.Close(); } |
キャストについて
CopyInfo(McCalInfo csOrgInfo) の説明で「キャスト」という言葉が出てきました。 「キャスト」は FORTRANのモデルをC#で制作する(その3) の章でも使用しています。 そこで 「キャスト」について整理してみます。
次の様な喩えで説明します。
今、「生徒」クラスがあり、その派生として「サッカー部員」クラスと「合唱部員」クラスを作成したとします。
また、「生徒」クラスに対して「クラブ活動」クラスがあり、そのメソッドに 活動() というメソッドがあります。
メソッド: クラブ.活動( 人 );
ここで サッカー部.活動(人) と、 合唱部.活動(人)という メソッドがコールされた時、メソッド:活動 のコーディングを考えてみます。
class サッカー部 : クラブ
{
public void 活動( 人 )
{
}
}
class 合唱部 : クラブ
{
public void 活動( 人 )
{
}
}
サッカー部のメソッド活動(人)の引数には 当然 「サッカー部員」が入ることが期待されます。 一方、合唱部では、引数として「合唱部員」が入ることが期待されます。
同じ「活動()」メソッドであっても、その中で サッカー部ならば、準備体操、基礎トレーニング、練習試合 等を行い、合唱部では 発声練習を行うでしょう。 この時 引数で与えられた「人」に対して それぞれ サッカー部員固有、合唱部員固有のメソッドが呼ばれます。 このため、「活動()」メソッドの中では、引数として与えられた 「人」が「サッカー部員」か、「合唱部員」かを認識しなければなりません。 ここで、「キャスト」を使って、引数で与えられた「人」が実は「サッカー部員」であるとプログラムに知らせます。
class サッカー部 : クラブ
{
public void 活動( 人 )
{
// キャストの方法その1
サッカー部員 buin = 人 as サッカー部員;
// キャストの方法その2
サッカー部員 buin = (サッカー部員)人;
}
}
親クラスである 「人」 に対して
人.ドリブル練習();
は一般的では有りません。 このため、人.ドリブル(); とコーディングすると コンパイルエラーになります。
しかし、キャストにより 一度、サッカー部員として認識すれば、以降はサッカー部員として
buin.ドリブル練習();
buin.ランニング();
等、サッカー部員として固有のメソッドを行わせることができます。
class サッカー部 : クラブ
{
public void 活動( 人 )
{
サッカー部員 buin = 人 as サッカー部員; // キャストの方法その1
if( buin != null )
{ // キャスト成功ならば
// サッカー部員固有のメソッドがコールできる
buin.ドリブル練習(); // 人.ドリブル練習();はコンパイルエラーとなる
buin.ランニング();
buin.紅白戦();
}
}
}
キャストの方法は 2種類ありますが、 もしも 引数「人」に「合唱部員」が入ってきた時、 方法1では、 buin = null が入ります。 しかし、方法2では、その場でプログラムが異常終了してしまいます。 このため、引数が100%確実に「サッカー部員」の確証が無い場合には 方法1を用いるべきです。
合唱部の場合も同様に行えます。
public void 活動( 人 )
{
合唱部員 buin = 人 as 合唱部員; // キャストの方法その1
if( buin != null )
{ // キャスト成功ならば
// 合唱部員固有のメソッドがコールできる
buin.発生練習(); // 人.発声練習(); はコンパイルエラーとなる。
buin.コンクール曲練習();
}
}}
ダウンロードしたら解凍して生成されたフォルダー「MyExerciseModel」を \CommonMP\Source\HYMCO\OptionImpl\ 下に置きます。
解凍して生成されたプロジェクト:MyExerciseModel.csproj を ディバッグ用ソリューションに追加し、スタートアッププロジェクト(TestModelDeveloperMainExp)の依存関係に追加します。
またテスト用のCommonMP計算プロジェクト(Ex03_CommonMPData.lzh)
解凍して生成された、「MyExerciseStep1」フォルダーを \CommonMP\CommonMPData\ 下に置きます。
初版:2013/06/17 |