LoginSignup
4
2

More than 5 years have passed since last update.

Runge-Kutta-Fehlberg法による数値解析

Posted at

Runge-Kutta-Fehlberg法とは

前回にRunge-Kutta法による数値解析を投稿しました。また、前々回においては、Euler法による数値解析を投稿しました。これらの方法のでは、シミュレーション時間を固定幅の刻み幅で分割し、分割した単位時間をそれぞれの方式の演算区間としていました。対して、Runge-Kutta-Fehlberg法では、シミュレーション時間の刻み幅を可変にすることが出来ます。これにより、数値の変化が基準より少ない場合には、刻み幅を大きくし、数値の変化が基準値より大きい場合には、刻み幅を小さくする制御として使用することが出来ます。従って、Runge-Kutta法よりも精度が高くなります。今回においても対象とする回路は、直流電源のRC回路とし、入力を電源電圧、出力をコンデンサ電圧とします。次にC言語によるソースコードを示します。

#include<stdio.h>
#include<math.h>

double func(double t, double Vc) {

    return 100000 - 1000 * Vc;

}

int main(void) {

    //変数設定//

    long double Vc, t, step, R, C, end, r;
    long double k1, k2, k3, k4, k5, k6;
    int FLAG = 0;
    long double step_MIN, step_MAX, tol, e, Vc_o5, Vc_o4, diff;
    int error;
    FILE *fi;

    //変数初期化//
    R = 10;
    C = 0.0001;
    r = 0.0;
    step = ((R*C) / 100.0) * 100;
    //step = 0.00001; //step数は関数に応じて任意に設定する
    diff = 0.0;
    Vc = 0.0;
    Vc_o4 = 0.0;
    Vc_o5 = 0.0;
    step_MIN = 0.00001;
    step_MAX = 0.005;
    //tol = 2.059999; //偏差の基準は関数に応じて任意に決定する
    tol = 2.06;
    e = 0;
    t = 0.0;
    end = 0.01;

    //textファイルの作成//
    if ((error = fopen_s(&fi, "Runge_Kutta_Fehlberg_data.txt", "w")) != 0) {

        printf("\nThis file can't opened \n\n");

        return 0;

    }

    //初期値の書き込み//
    fprintf(fi, "%f %f\n", t, Vc);

    //Runge-Kutta-Fehlberg法の演算//
    while (FLAG != 1) {

    /*  k1からk6までを求める演算では、コメントアウトしている式が正しい式なのですが、
        分数のまま演算すると後で0になってしまうので、わざわざ小数にしています*/

        k1 = step*func(t, Vc); printf("k1 = %lf\n", k1);
        k2 = step*func(t + step*0.25, Vc + (k1*0.25)); printf("k2 = %lf\n", k2);
        //k3 = step*func(t + step*(3/8), Vc + ( (k1*(3/32)) + (k2*(9/32)))); 
        k3 = step*func(t + step*(0.375), Vc + ((k1*(0.09375)) + (k2*(0.28125))));
        //k4 = step*func(t + step*(12/13), Vc +  (((1932/2197)*k1) - ((7200/2197)*k2) + ((7296/2197) * k3) )); 
        k4 = step*func(t + step*(0.92307), Vc + (((0.87938)*k1) - ((3.27719)*k2) + ((3.320892) * k3))); printf("k4 = %lf\n", k4);
        //k5 = step*func(t + step, Vc +  (((439 / 216)*k1) - (8 * k2) + ((3680 / 513)*k3) - ((845 / 4104)*k4)));
        k5 = step*func(t + step, Vc + (((2.032407)*k1) - (8 * k2) + ((7.173489)*k3) - ((0.20589)*k4))); printf("k5 = %lf\n", k5);
        //k6 = step*func(t + step*0.5, Vc + (-((8 / 27)*k1) + (2 * k2) - ((3544 / 2565)*k3) + ((1859 / 4104)*k4) - ((11/40)*k5)));
        k6 = step*func(t + step*0.5, Vc + (-((0.29629)*k1) + (2 * k2) - ((1.38167)*k3) + ((0.45330)*k4) - ((0.275)*k5))); printf("k6 = %lf\n", k6);

        Vc_o5 = ((0.1185 * k1) + (0.51898 * k3) + (0.50613 * k4) - (0.18 * k5) + (0.03636 * k6));
        //Vc_o5 = (((16 / 135) * k1) + ((6656 / 12825) * k3) + ((28561 / 56430) * k4) - ((9 / 50) * k5) + ((2 / 55) * k6));
        //Vc_o4 = Vc + (((25 / 216)*k1) + ((1408 / 2565)*k3) + ((2197 / 4104)*k4) - (1 / 5)*k5);
        Vc_o4 = ((0.11574 * k1) + (0.54892 * k3) + (0.53533 * k4) - (0.2)*k5);

        diff = Vc_o5 - Vc_o4;

        if (diff < 0) { 

            diff = diff*(-1);

        }

        r = (diff / step); printf("r = %lf\n", r);

        if (r <= tol) {

            t = t + step;
            //Vc = Vc + (25/216)*k1 + (1408/2565)*k3 + (2197/4104)*k4 - (1/5)*k5;
            Vc = Vc + (0.11574) * k1 + (0.54892)*k3 + (0.535722)*k4 - (0.2)*k5;
            fprintf(fi, "%f %f\n", t, Vc);

        }

        e = pow((tol / (2 * r)), 0.25);

        //次のif文の基準は、関数に応じて任意に設定する
        if (e <= 0.1) {

            step = step * 0.1;  //step幅を小さくする

        }
        else if (e >= 1.5) {

            //step = step * 100000;
            step = step * 2; //step幅を大きくする

        }
        else {

            step = e*step;

        }

        if (step > step_MAX) {

            step = step_MAX;

        }

        printf("t = %lf\n", t);

        if (t >= end) {

            FLAG = 1;

        }
        else if ((t + step) > end) {

            step = 0.01 - t;

            if (step < step_MIN) {

                FLAG = 1;
                printf("\nMINIMUM step exceeded !!\n");

            }
        }
    }

    //textファイルの保存//
    fclose(fi);

    return 0;
}

上記のソースコードを実行すると、時間領域におけるRC回路のコンデンサ電圧の値がtextファイルとして出てきます。
この出てきたファイルをgunuplotで実行させるとRC回路のコンデンサ電圧におけるEuler法を用いた数値解をプロットする事が出来ます。

以下に、簡単なGunuplotのソースコードを示します。


se grid
se xrange [0:0.01]
se yrange [0:110]
se xlabel "s[sec]"
se ylabel "Vc[V]"
plot "Euler_data.txt" using 1:2 with lines



4
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
2