// <summary>ソースコード：サンプル演算モデルクラス</summary>
// <author>CommonMP</author>

using System;
using System.Collections.Generic;
using System.Text;

using CommonMP.HYSSOP.CoreImpl;
using CommonMP.HYSSOP.CoreImpl.HSData;
using CommonMP.HYSSOP.CoreImpl.HSTools;

using CommonMP.HYMCO.Interface;
using CommonMP.HYMCO.Interface.Model;
using CommonMP.HYMCO.Interface.Data;
using CommonMP.HYMCO.CoreImpl.Data;
using CommonMP.HYMCO.CoreImpl.Tool;



namespace CommonMP.HYMCO.OptionImple.McHyHdFormulaSampleH13
{
    /// <summary><para>class outline:</para>
    /// <para> 水理公式集例題６－２より
    /// 河川における移流・拡散・分散の計算モデルクラス
    ///   １次元移流拡散（分散）方程式の計算 </para>
    /// </summary>
    /// <remarks><para>history:</para>
    /// <para>[CommonMP][ver 1.2.0][2011/11/11][新規作成]</para>
    /// <para>remarks</para>
    /// <para> </para>
    /// </remarks>
    public class McQUICKSchemeRivDiff : McForecastModelBase
    {
        /// <summary> 演算データ（キャスト用） </summary>
        McQUICKSchemeRivDiffCalInfo m_KMdlInf = null; // 便利の為、キャスト用に定義しておく

        //=========================
        // 演算実行前処理関連メソッド
        //=========================

        /// <summary><para>method outline:</para>
        /// <para>入力側の接続情報チェック</para>
        /// </summary>
        /// <example><para>usage:</para>
        /// <para>bool bRtn = ReceiveConnectionCheck(ref csErrorInf)</para>
        /// </example>
        /// <param name="csErrorInf">エラー出力</param>
        /// <param name="lInputDataNum">入力情報数</param>
        /// <param name="csInputCellData">演算に必要な入力情報配列</param>
        /// <returns>=true:正常、=false:異常</returns>
        /// <exception cref="">無し</exception>
        /// <remarks><para>remarks:</para>
        /// <para>受信するデータが自モデルが期待している情報か否かをチェックする</para>
        /// </remarks>
        protected override bool ReceiveConnectionCheck(ref McStructErrorInfo csErrorInf, long lInputDataNum, McReceiveCellDataIF[] csInputCellData)
        {
            bool bRtn = true;

            long lQHVInputNum = 0;
            if (lInputDataNum == 0)
            {
                // 入力が無い場合
                // エラー
                csErrorInf.AddCheckErrorData(this.GetID(), McHyHdFormulaSampleH13Define.RIVDIF_QUICKS_KIND, "No receive data.");
                bRtn = false;
            }
            for (long lP = 0; lP < lInputDataNum; lP++)
            {   // 入力データ数分
                if (McHyHdFormulaSampleH13Define.IN_PATTERN_C_TOP.Equals(csInputCellData[lP].GetReceivePatternID()) == true)
                {   //河川上流端濃度入力
                    // Do Nothing
                }
                else if (McHyHdFormulaSampleH13Define.IN_PATTERN_C_CELL.Equals(csInputCellData[lP].GetReceivePatternID()) == true)
                {   //物質セル毎流入濃度入力
                    long lD1 = 0; long lD2 = 0; long lD3 = 0; long lCellDataNum = 0;
                    long lCellDim = csInputCellData[lP].GetDimension(ref lD1, ref lD2, ref lD3, ref lCellDataNum);

                    if (lCellDim == 1 && lD1 > 0)
                    {   // 1次元の１セルでならば
                        if (m_KMdlInf.m_lCelNumber + 1 > lD1)
                        {
                            csErrorInf.AddCheckErrorData(this.GetID(), McHyHdFormulaSampleH13Define.RIVDIF_QUICKS_KIND,
                                                    "Cell dimention(" + csInputCellData[lP].GetTranInfoKind().ToString() + ") is too short. (send to " + csInputCellData[lP].GetUpperElementID().ToString() + ")");
                            bRtn = false;
                        }
                    }

                }
                else if (McHyHdFormulaSampleH13Define.IN_PATTERN_RIVER_QHV.Equals(csInputCellData[lP].GetReceivePatternID()) == true)
                {   //流量・水位・流速入力
                    lQHVInputNum += 1;
                    long lD1 = 0; long lD2 = 0; long lD3 = 0; long lCellDataNum = 0;
                    long lCellDim = csInputCellData[lP].GetDimension(ref lD1, ref lD2, ref lD3, ref lCellDataNum);
                    if (lCellDim == 1 && lD1 > 0)
                    {   // 1次元の１セルでならば
                        if (m_KMdlInf.m_lCelNumber + 1 > lD1)
                        {
                            csErrorInf.AddCheckErrorData(this.GetID(), McHyHdFormulaSampleH13Define.RIVDIF_QUICKS_KIND,
                                                    "Cell dimention(" + csInputCellData[lP].GetTranInfoKind().ToString() + ") is too short. (send to " + csInputCellData[lP].GetUpperElementID().ToString() + ")");
                            bRtn = false;
                        }
                        if (lCellDataNum < 3)
                        {
                            csErrorInf.AddCheckErrorData(this.GetID(), McHyHdFormulaSampleH13Define.RIVDIF_QUICKS_KIND,
                                "Variable number in the Cells is too short. (Received from " + csInputCellData[lP].GetUpperElementID().ToString() + ")");
                            bRtn = false;
                        }
                    }
                }
                else
                {
                    csErrorInf.AddCheckErrorData(this.GetID(), McHyHdFormulaSampleH13Define.RIVDIF_QUICKS_KIND,
                        "Unexpected receive data type. (Received from " + csInputCellData[lP].GetUpperElementID().ToString() + ")");
                    bRtn = false;
                }
            }
            if (lQHVInputNum > 1)
            {
                csErrorInf.AddCheckErrorData(this.GetID(), McHyHdFormulaSampleH13Define.RIVDIF_QUICKS_KIND,
                    "River Q,H,V input number must be one.");
                bRtn = false;
            }
            return bRtn;
        }
        /// <summary><para>method outline:</para>
        /// <para>出力側の接続情報チェック</para>
        /// </summary>
        /// <example><para>usage:</para>
        /// <para>bool bRtn = SendConnectionCheck(ref csErrorInf)</para>
        /// </example>
        /// <param name="csErrorInf">エラー出力</param>
        /// <param name="lOutputDataNum">出力情報数</param>
        /// <param name="csOutputCellData">出力情報配列</param>
        /// <returns>=true:正常、=false:異常</returns>
        /// <exception cref="">無し</exception>
        /// <remarks><para>remarks:</para>
        /// <para>送信端子に設定されている伝送データが自モデルが期待している情報か否かをチェックする</para>
        /// </remarks>
        protected override bool SendConnectionCheck(ref McStructErrorInfo csErrorInf, long lOutputDataNum, McSendCellDataIF[] csOutputCellData)
        {
            bool bRtn = true;

            if (lOutputDataNum == 0)
            {
                // 警告（エラーではない）
                csErrorInf.AddCheckWarningData(this.GetID(), McHyHdFormulaSampleH13Define.RIVDIF_QUICKS_KIND, "No send port.");
            }
            for (long lP = 0; lP < lOutputDataNum; lP++)
            {   // 出力側接続線数分チェックを行う
                if (McHyHdFormulaSampleH13Define.OUT_PATTERN_C_BOTTOM.Equals(csOutputCellData[lP].GetSendPatternID()) == true)
                {   // 最終セルだけの出力ならば
                    // Do Nothing
                }
                else if (McHyHdFormulaSampleH13Define.OUT_PATTERN_C_ALL.Equals(csOutputCellData[lP].GetSendPatternID()) == true)
                {   // 全体出力ならば
                    // Do Nothing
                }
                else
                {
                    // エラー
                    csErrorInf.AddCheckErrorData(this.GetID(), McHyHdFormulaSampleH13Define.RIVDIF_QUICKS_KIND,
                        "Unexpected send data type.");
                    bRtn = false;
                }

                long lD1 = 0; long lD2 = 0; long lD3 = 0; long lCellDataNum = 0;
                long lCellDim = csOutputCellData[lP].GetDimension(ref lD1, ref lD2, ref lD3, ref lCellDataNum);

                if (lCellDim == 1 && lD1 == 1)
                {   // １次元配列の１セルならば
                    // Do Nothing
                }
                else if (lCellDim == 1 && lD1 > 1)
                {   // １次元配列で複数セルならば
                    // セル配列数が複数ならば、全セルの出力とする
                    // 伝送セルの配列数が　モデル内のセルの配列数よりも小さいならば
                    if (m_KMdlInf.m_lCelNumber+1 > lD1)
                    {
                        csErrorInf.AddCheckErrorData(this.GetID(), McHyHdFormulaSampleH13Define.RIVDIF_QUICKS_KIND,
                                                "Cell dimention(" + csOutputCellData[lP].GetTranInfoKind().ToString() + ") is too short. (send to " + csOutputCellData[lP].GetLowerElementID().ToString() + ")");
                        bRtn = false;
                    }
                }
            }
            
            return bRtn;
        }

        /// <summary><para>method outline:</para>
        /// <para>モデルを初期化する</para>
        /// </summary>
        /// <example><para>usage:</para>
        /// <para>Initialize(csInitialData)</para>
        /// </example>
        /// <param name="csInitialData">初期化設定情報</param>
        /// <param name="lInputDataNum">入力情報数</param>
        /// <param name="csInputCellData">演算に必要な入力情報配列</param>
        /// <returns>無し</returns>
        /// <exception cref="">無し</exception>
        /// <remarks><para>remarks:</para>
        /// <para>無し</para>
        /// </remarks>
        protected override bool Initialize(ref McPropertyInfoRoot csInitialData, long lInputDataNum, ref McReceiveCellDataIF[] csInputCellData)
        {
            McInitialInfo csInDt = csInitialData as McInitialInfo;
            if (csInDt != null)
            {   // 初期化情報有り
                double dDtH = 0.001;
                csInDt.GetInfo("H_Initial", ref dDtH);
                double dDtQ = 0.0;
                csInDt.GetInfo("Q_Initial", ref dDtQ);

                double dDtV = dDtQ / dDtH;

                for (long lLp = 0; lLp <= m_KMdlInf.m_lCelNumber; lLp++)
                {
                    m_KMdlInf.m_dH[lLp] = dDtH;
                    m_KMdlInf.m_dV[lLp] = dDtV;
                    m_KMdlInf.m_dQ[lLp] = dDtQ;

                    m_KMdlInf.m_dCNow[lLp] = 0.0;
                    m_KMdlInf.m_dCNext[lLp] = 0.0;
                    m_KMdlInf.m_dAdvold[lLp] = 0.0;

                }
                for (long lLp = 0; lLp <= m_KMdlInf.m_lCelNumber; lLp++)
                {
                    m_KMdlInf.m_dD[lLp] = m_KMdlInf.m_dUstar * m_KMdlInf.m_dV[lLp];
                }

                m_KMdlInf.m_bFirstCalFlg = true;
            }
            
            return true;
        }

        //=======================
        // 演算実行処理関連メソッド
        //=======================

        /// <summary><para>method outline:</para>
        /// <para>モデル演算</para>
        /// </summary>
        /// <example><para>usage:</para>
        /// <para>long lRtn = Calculate(ref csInputCellData)</para>
        /// </example>
        /// <param name="lInputDataNum">入力情報数</param>
        /// <param name="csInputCellData">演算に必要な入力情報配列</param>
        /// <returns>=0:正常 -1:異常</returns>
        /// <exception cref="">無し</exception>
        /// <remarks><para>remarks:</para>
        /// <para>無し</para>
        /// </remarks>
        protected override long Calculate(long lInputDataNum, ref McReceiveCellDataIF[] csInputCellData)
        {
            double dDELT = this.m_csDltTime.GetTime();// δT（秒）
            long lCIdx = 0;
            long lQIdx = 0;
            long lHIdx = 1;
            long lVIdx = 2;

            HySCellData csCell = null;
            HySCellData[] csCellArry = null;

            for (long lP = 0; lP < lInputDataNum; lP++)
            {   // 入力データ数分
                if (McHyHdFormulaSampleH13Define.IN_PATTERN_RIVER_QHV.Equals(csInputCellData[lP].GetReceivePatternID()) == true)
                {   // 各セルの 流量、水位、流速入力ならば
                    csCellArry = csInputCellData[lP].GetInterpolatedCellD1();
                    // ★★この部分公式集には無い部分
                    // ★★別モデルで計算した　Q,H,Vを使用してよいか良く考える必要がある。
                    for (long lLp = 0; lLp <= m_KMdlInf.m_lCelNumber; lLp++)
                    {
                        m_KMdlInf.m_dQ[lLp] = csCellArry[lLp].m_dData[lQIdx]  / m_KMdlInf.m_dWidth;
                        m_KMdlInf.m_dH[lLp] = csCellArry[lLp].m_dData[lHIdx];
                        m_KMdlInf.m_dV[lLp] = csCellArry[lLp].m_dData[lVIdx];

                        m_KMdlInf.m_dD[lLp] = m_KMdlInf.m_dUstar *Math.Abs( m_KMdlInf.m_dV[lLp] );
                    }
                }
                if (McHyHdFormulaSampleH13Define.IN_PATTERN_C_TOP.Equals(csInputCellData[lP].GetReceivePatternID()) == true)
                {   // 上流への濃度入力ならば
                    csCell = csInputCellData[lP].GetInterpolatedCell(0);
                    m_KMdlInf.m_dCNext[0] = csCell.m_dData[lCIdx];
                }
                if (McHyHdFormulaSampleH13Define.IN_PATTERN_C_CELL.Equals(csInputCellData[lP].GetReceivePatternID()) == true)
                {   // 各セルへの濃度入力ならば
                    csCellArry = csInputCellData[lP].GetInterpolatedCellD1();
                    for (long lLp = 0; lLp <= m_KMdlInf.m_lCelNumber; lLp++)
                    {
                        m_KMdlInf.m_dCNext[lLp] += csCellArry[lLp].m_dData[lCIdx];
                    }
                }
            }

            // 例題６－２　Rivdif.f 内 SUBROUTINE SHIFTに相当
            for (long lLp = 0; lLp <= m_KMdlInf.m_lCelNumber; lLp++)
            {
                m_KMdlInf.m_dCNow[lLp] = m_KMdlInf.m_dCNext[lLp];
            }

            // 例題６－２　Rivdif.f 内 SUBROUTINE QUICKに相当
            double dQR = 0.0;
            double dQL = 0.0;
            double dCR = 0.0;
            double dCL = 0.0;
            double dADV = 0.0;
            double dDR=0.0;
            double dDL=0.0;
            double dDIF = 0.0;
            for (long lLp = 1; lLp < m_KMdlInf.m_lCelNumber; lLp++)
            {
                long lIP1 = lLp + 1; if (lIP1 > m_KMdlInf.m_lCelNumber) { lIP1 = m_KMdlInf.m_lCelNumber; }
                long lIP2 = lLp + 2; if (lIP2 > m_KMdlInf.m_lCelNumber) { lIP2 = m_KMdlInf.m_lCelNumber; }
                long lIM1 = lLp - 1; if (lIM1 < 0) { lIM1 = 0; }
                long lIM2 = lLp - 2; if (lIM2 < 0) { lIM2 = 0; }


                dQR = 0.5 * (m_KMdlInf.m_dQ[lIP1] + m_KMdlInf.m_dQ[lLp]);
                dQL = 0.5 * (m_KMdlInf.m_dQ[lIM1] + m_KMdlInf.m_dQ[lLp]);

                // --- 移流項の計算
                dCR = 0.5 * (m_KMdlInf.m_dCNow[lLp] + m_KMdlInf.m_dCNow[lIP1])
                    - (m_KMdlInf.m_dCNow[lIP1] + m_KMdlInf.m_dCNow[lIM1] - 2.0 * m_KMdlInf.m_dCNow[lLp]) / 8.0;
                if (dCR < 0.0)
                {
                    dCR = 0.5 * (m_KMdlInf.m_dCNow[lLp] + m_KMdlInf.m_dCNow[lIP1])
                        - (m_KMdlInf.m_dCNow[lIP2] + m_KMdlInf.m_dCNow[lLp] - 2.0 * m_KMdlInf.m_dCNow[lIP1]) / 8.0;
                }

                dCL = 0.5 * (m_KMdlInf.m_dCNow[lLp] + m_KMdlInf.m_dCNow[lIM1])
                    - (m_KMdlInf.m_dCNow[lLp] + m_KMdlInf.m_dCNow[lIM2] - 2.0 * m_KMdlInf.m_dCNow[lIM1]) / 8.0;
                if (dCL < 0.0)
                {
                    dCL = 0.5 * (m_KMdlInf.m_dCNow[lLp] + m_KMdlInf.m_dCNow[lIM1])
                        - (m_KMdlInf.m_dCNow[lIP1] + m_KMdlInf.m_dCNow[lIM1] - 2.0 * m_KMdlInf.m_dCNow[lLp]) / 8.0;
                }

                dADV = (dQR * dCR - dQL * dCL) / m_KMdlInf.m_dCelLength;
            
                // --- 拡散（分散）項の計算
                dDR = 0.5 * (m_KMdlInf.m_dH[lIP1] + m_KMdlInf.m_dH[lLp])
                    * 0.5 * (m_KMdlInf.m_dD[lIP1] + m_KMdlInf.m_dD[lLp])
                    * (m_KMdlInf.m_dCNow[lIP1] - m_KMdlInf.m_dCNow[lLp]) / m_KMdlInf.m_dCelLength;
                dDL = 0.5 * (m_KMdlInf.m_dH[lLp] + m_KMdlInf.m_dH[lIM1])
                    * 0.5 * (m_KMdlInf.m_dD[lLp] + m_KMdlInf.m_dD[lIM1])
                    * (m_KMdlInf.m_dCNow[lLp] - m_KMdlInf.m_dCNow[lIM1]) / m_KMdlInf.m_dCelLength;
                dDIF = (dDR - dDL) / m_KMdlInf.m_dCelLength;


                if (m_KMdlInf.m_dH[lLp] > 0.0)
                {
                    // --- 新しい時刻の濃度の計算
                    m_KMdlInf.m_dCNext[lLp] = m_KMdlInf.m_dH[lLp]*m_KMdlInf.m_dCNow[lLp]
                        +dDELT*( -1.5*dADV-0.5*m_KMdlInf.m_dAdvold[lLp] + dDIF );
                    if (m_KMdlInf.m_bFirstCalFlg==true)
                    {   // 最初の計算時のみ
                        m_KMdlInf.m_dCNext[lLp] = m_KMdlInf.m_dH[lLp]*m_KMdlInf.m_dCNow[lLp]
                            +dDELT*( -dADV +dDIF );
                    }
                    m_KMdlInf.m_dCNext[lLp] = m_KMdlInf.m_dCNext[lLp] / m_KMdlInf.m_dH[lLp];
                }
                m_KMdlInf.m_dAdvold[lLp] = -1.0*dADV;
            }
            if (m_KMdlInf.m_bFirstCalFlg == true) { m_KMdlInf.m_bFirstCalFlg = false; }
            // --------- 流出境界条件 -------
            m_KMdlInf.m_dCNext[0] = m_KMdlInf.m_dCNext[1];
            m_KMdlInf.m_dCNext[m_KMdlInf.m_lCelNumber] = m_KMdlInf.m_dCNext[m_KMdlInf.m_lCelNumber-1];
            // ------------------------------

            return 0;
        }

        /// <summary><para>method outline:</para>
        /// <para>モデル演算結果を公開する</para>
        /// </summary>
        /// <example><para>usage:</para>
        /// <para>long lRtn = DataFusion( lOutputDataNum, ref csOutputCellData)</para>
        /// </example>
        /// <param name="lOutputDataNum">出力情報数</param>
        /// <param name="csOutputCellData">出力情報配列</param>
        /// <returns>=0:正常 -1:異常</returns>
        /// <exception cref="">無し</exception>
        /// <remarks><para>remarks:</para>
        /// <para>無し</para>
        /// </remarks>
        protected override long DataFusion(long lOutputDataNum, ref McSendCellDataIF[] csOutputCellData)
        {
            HySCellData[] csSndCellData = null; // 送信セル
            for (long lP = 0; lP < m_lOutputDataNum; lP++)
            {   // 送信データ数繰り返し
                csSndCellData = csOutputCellData[lP].PrepareSendCellD1();

                if (McHyHdFormulaSampleH13Define.OUT_PATTERN_C_BOTTOM.Equals(csOutputCellData[lP].GetSendPatternID()) == true)
                {   // 最終セルだけの出力ならば
                    csSndCellData[0].m_dData[0] = m_KMdlInf.m_dCNext[m_KMdlInf.m_lCelNumber - 1];
                }
                else if (McHyHdFormulaSampleH13Define.OUT_PATTERN_C_ALL.Equals(csOutputCellData[lP].GetSendPatternID()) == true)
                {   // 全体出力ならば
                    for (long lLp = 0; lLp < m_KMdlInf.m_lCelNumber; lLp++)
                    {
                        csSndCellData[lLp].m_dData[0] = m_KMdlInf.m_dCNext[lLp];
                    }
                }
            }
            
            return 0;
        }

        //====================
        // その他必要なメソッド
        //====================

        /// <summary><para>method outline:</para>
        /// <para>プロパティ情報を設定する</para>
        /// </summary>
        /// <example><para>usage:</para>
        /// <para>SetProperty(csCellMdlPropertyInfo)</para>
        /// </example>
        /// <param name="csCellMdlPropertyInfo">セル型プロパティ情報</param>
        /// <returns>無し</returns>
        /// <exception cref="">無し</exception>
        /// <remarks><para>remarks:</para>
        /// <para>無し</para>
        /// </remarks>
        public override bool SetProperty(McCellModelPropertyIF csCellMdlPropertyInfo)
        {
            bool bRtn = false;
            // 使用しやすいようにキャストしておく
            m_KMdlInf = (McQUICKSchemeRivDiffCalInfo)m_csCalInfo;

            // プロパティ設定
            McCellModelPropertyInfo csPrptyInfo = csCellMdlPropertyInfo as McCellModelPropertyInfo;
            if (csPrptyInfo != null)
            {
                bRtn = true;

                // 演算ステップ時刻設定
                this.m_csDltTime = new HySTime(csPrptyInfo.GetStepTime());

                // 各種係数設定
                csPrptyInfo.GetInfo("m_lCelNumber", ref m_KMdlInf.m_lCelNumber);

                m_KMdlInf.m_dH = new double[m_KMdlInf.m_lCelNumber+1];
                m_KMdlInf.m_dD = new double[m_KMdlInf.m_lCelNumber+1];
                m_KMdlInf.m_dV = new double[m_KMdlInf.m_lCelNumber+1];
                m_KMdlInf.m_dQ = new double[m_KMdlInf.m_lCelNumber+1];
                m_KMdlInf.m_dCNow = new double[m_KMdlInf.m_lCelNumber+1];
                m_KMdlInf.m_dCNext = new double[m_KMdlInf.m_lCelNumber+1];
                m_KMdlInf.m_dAdvold = new double[m_KMdlInf.m_lCelNumber+1];

                for (long lLp = 0; lLp < m_KMdlInf.m_lCelNumber; lLp++)
                {
                    m_KMdlInf.m_dH[lLp] = 0.0;
                    m_KMdlInf.m_dD[lLp] = 0.0;
                    m_KMdlInf.m_dV[lLp] = 0.0;
                    m_KMdlInf.m_dQ[lLp] = 0.0;
                    m_KMdlInf.m_dCNow[lLp] = 0.0;
                    m_KMdlInf.m_dCNext[lLp] = 0.0;
                    m_KMdlInf.m_dAdvold[lLp] = 0.0;
                }
                csPrptyInfo.GetInfo("m_dLength", ref m_KMdlInf.m_dLength);
                m_KMdlInf.m_dCelLength = m_KMdlInf.m_dLength / (double)(m_KMdlInf.m_lCelNumber);

                csPrptyInfo.GetInfo("m_dWidth", ref m_KMdlInf.m_dWidth);

                csPrptyInfo.GetInfo("m_dUstar", ref m_KMdlInf.m_dUstar);
            }
            
            return bRtn;
        }
    }
}
