(前頁)<<      >>(次頁)          (Up)

3章 CommonMPの要素モデルを作成する
3.4 要素モデルを作成する(2章のモデルをCommonMP要素モデルに移植する)


 スタブが完成した所で、要素モデル内に計算論理を組み込みます。 本章では、2章で作成したDynamicWaveモデル/KinematicWaveモデル(RiverMDL2.lzh)を、CommonMPの要素モデルに移植します。


(演算中データクラス:MyDynamicWaveCalInfo の実装)
 CommonMPでは、計算中の情報を「演算中データ」に入れることを説明しました。 2章で作成したモデル:DynamicWaveModel のメンバー変数を 演算中データ:MyDynamicWaveCalInfo にコピーします。 尚、メンバー変数は すべて public 宣言しておきます。

    [Serializable]
    public class MyDynamicWaveCalInfo : McCalInfo
    {
        // COMMON 領域内の変数をメンバー変数にする
        // PARAMETER(NXX=500)
        // COMMON /MESH/DT,DX,X(0:NXX)
        // *     H(0:NXX),H1(0:NXX),Q(0:NXX),Q1(Q:NXX),V(0,NXX)
        public long NXX = 500;
        public double DX;
        public double[] X;
        public double[] H;
        public double[] H1;
        public double[] Q;
        public double[] Q1;
        public double[] V;
        // COMMON /COND/TL,NT,NSKIP,XL,NX,SLOPE,WIDTH,ROUGH,QB,QP,TP,CP
        public double XL;
        public long NX;
        public double SLOPE;
        public double WIDTH;
        public double ROUGH;
        public double QB;
        public double QP;
        public double TP;
        public double CP;
        // COMMON /CNST/G
        public double G;
            .......... 


  演算中データクラスでスタブの中身を実装します。
・public override McCalInfo Clone() メソッドは そのままで動作しますので、改めてコーディングする必要はありません。

・public override bool CopyInfo(McCalInfo csOrgInfo) メソッドは 引数で与えられたクラス内のすべてのメンバー変数値を自クラスのメンバー変数に代入します。

        public override bool CopyInfo(McCalInfo csOrgInfo)
        {
            MyDynamicWaveCalInfo csOrdDt = (MyDynamicWaveCalInfo)csOrgInfo; // キャスト

            // 此処に、追加したメンバーをコピーする処理を入れます。
             DX=csOrdDt.DX;
             X=new double[NXX];
             H=new double[NXX];
             H1=new double[NXX];
             Q=new double[NXX];
             Q1=new double[NXX];
             V=new double[NXX];
             for (long lLp = 0; lLp < NXX; lLp++)
             {
                 X[lLp] = csOrdDt.X[lLp];
                 H[lLp] = csOrdDt.H[lLp];
                 H1[lLp] = csOrdDt.H1[lLp];
                 Q[lLp] = csOrdDt.Q[lLp];
                 Q1[lLp] = csOrdDt.Q1[lLp];
                 V[lLp] = csOrdDt.V[lLp];
             }
             XL=csOrdDt.XL;
             NX=csOrdDt.NX;
             SLOPE=csOrdDt.SLOPE;
             WIDTH=csOrdDt.WIDTH;
             ROUGH=csOrdDt.ROUGH;
             QB=csOrdDt.QB;
             QP=csOrdDt.QP;
             TP=csOrdDt.TP;
             CP=csOrdDt.CP;
             G=csOrdDt.G;

           
            return true;
        } 


 上記プログラムの中で、
 引数 csOrgInfo は 親クラス:McCalInfo として認識されています。 従ってプログラム中で、派生クラスのメンバー変数を取り扱うことはできません。
 例えば、DX = csOrgInfo.DX; 
 とすると、コンパイルエラーになります。

 そこで、下記のように「キャスト」という操作を行います。 すなわち、派生クラス:MyDynamicWaveCalInfoとして csOrdDt という変数を用意しておき、
 MyDynamicWaveCalInfo csOrdDt = (MyDynamicWaveCalInfo)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.G = 9.8;

                m_csMyInf.XL = 100.0 * 1000.0;
                m_csMyInf.NX = 50;
                m_csMyInf.DX = m_csMyInf.XL / ((double)m_csMyInf.NX);

                m_csMyInf.SLOPE = 1.0 / 2000.0;
                m_csMyInf.WIDTH = 200.0;
                m_csMyInf.ROUGH = 0.03;

                m_csMyInf.QB = 200.0;
                m_csMyInf.QP = 2000.0;
                m_csMyInf.TP = 12.0 * 3600.0;
                m_csMyInf.CP = 10.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);
                double HINIT = Math.Pow((Math.Pow(m_csMyInf.ROUGH, 2.0) * Math.Pow(QINIT, 2.0) / m_csMyInf.SLOPE), 0.3);
                double VINIT = QINIT / HINIT;
                for (long I = 0; I <= m_csMyInf.NX; I++)
                {
                    m_csMyInf.X[I] = I * m_csMyInf.DX;
                    m_csMyInf.Q[I] = QINIT;
                    m_csMyInf.H[I] = HINIT;
                    m_csMyInf.V[I] = VINIT;
                }

  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 m_DT = m_csDltTime.GetTime();
            double m_T = m_m_csTotalPassingTime.GetTime();

            double dQIN = QIN(m_T);
            m_csMyInf.Q1[0] = dQIN;
            m_csMyInf.H1[0] = m_csMyInf.H[0] + (1.5 * m_csMyInf.Q[0] - 2.0 * m_csMyInf.Q[1] + 0.5 * m_csMyInf.Q[2]) * m_DT / m_csMyInf.DX;

            for (long I = 1; I <= m_csMyInf.NX; I++)
            {
                double FQ = 0.0;
                double FV = 0.0;
                double FP = 0.0;
                double FF = 0.0;
                COEFF(I, ref FQ, ref FV, ref FP, ref FF);

                m_csMyInf.H1[I] = m_csMyInf.H[I] - FQ * m_DT;
                m_csMyInf.Q1[I] = (m_csMyInf.Q1[I] + (FP - FV) * m_DT) / (1.0 + FF * m_DT);
            }

            for (long I = 0; I <= m_csMyInf.NX; I++)
            {
                m_csMyInf.H[I] = m_csMyInf.H1[I];
                m_csMyInf.Q[I] = m_csMyInf.Q1[I];
                m_csMyInf.V[I] = m_csMyInf.Q[I] / m_csMyInf.H[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();
            double m_T = m_csTotalPassingTime.GetTime();

            if ((3600 * (long)(m_T / 3600.0) - (long)m_T) == 0)
            {   // シミュレーション時刻が正時になったら出力する
                string sMsg = "";
                sMsg = m_T.ToString();
                double outQ = 0.0;
                for (long I = 0; I <= m_csMyInf.NX; I++)
                {
                    outQ = m_csMyInf.Q[I] * m_csMyInf.WIDTH;
                    sMsg += "," + outQ.ToString();
                }
                FileOut(sMsg); // ファイル出力
            }

追加メソッド
 第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));
        }
 
 さらに、第2章で作成したDynamicWaveLine の親クラス RiverModel のFileOut( )は、CommonMP要素モデル:MyDynamicWaveModel の親クラスには無いので、独自メソッドとして追加します。
・protected virtual void FileOut(string csString)
 RiverModel の FileOutメソッドをそのままコピーします。

        protected virtual void FileOut(string csString)
        {
            FileStream csFile = new FileStream("RvrOut.csv", FileMode.Append, FileAccess.Write, FileShare.Read);
            StreamWriter csStreamWriter = new StreamWriter(csFile, Encoding.Default);

            csStreamWriter.WriteLine(csString);
            csStreamWriter.Flush();

            csStreamWriter.Close();
            csFile.Close();

        }

 但し、出力ファイル名を 「RvrOut.csv」に変更しました。

 これで、要素モデルの各メソッドの実装が完了しました。



(動作確認)
 
 要素モデルの制作が完了したので、動作を確認します。 
 動作確認用のプロジェクトとしては、スタブ用プロジェクトがあるので、これを使用します。 スタブの確認では、計算時間を2分としましたが、本番モデルの動作確認としては、計算時間を2日間とします。
 
 

 計算を開始すると 計算が正常終了しました。 計算結果は、 \CommonMP\Source\HYMCO\OptionImpl\ModelDeveloperExpressEdition\bin\Debug\下に 「RvrOut.csv」ファイルができています。 出力結果を調べると、第2章のモデルで計算した内容と同じに見えます。


キャストについて 
 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.コンクール曲練習();
       }
      }} 



 ここで作成した要素モデル開発プロジェクト(Ex03_MyExerciseModel.lzh

 ダウンロードしたら解凍して生成されたフォルダー「MyExerciseModel」を \CommonMP\Source\HYMCO\OptionImpl\ 下に置きます。   
 解凍して生成されたプロジェクト:MyExerciseModel.csproj を ディバッグ用ソリューションに追加し、スタートアッププロジェクト(TestModelDeveloperMainExp)の依存関係に追加します。

 

 またテスト用のCommonMP計算プロジェクト(Ex03_CommonMPData.lzh

 解凍して生成された、「MyExerciseStep1」フォルダーを \CommonMP\CommonMPData\ 下に置きます。

 


(前頁)<<      >>(次頁)

初版:2013/06/17 |