cms.diffeq_simpson のソースコード

import numpy as np
from math import exp, sqrt, sin, cos, pi


"""
シンプソン法を用いて1階常微分方程式を解くモジュール。

このモジュールは、指定された1階常微分方程式をオイラー法、ホイン法、
シンプソン法(Runge-Kutta法にシンプソン則の重みを適用したもの)を用いて
数値的に解くための関数群を提供します。また、特定の微分方程式に対する
解析解も含まれており、数値解との比較が可能です。

:doc:`diffeq_simpson_usage`
"""


# dx/dt = dxdt(x,t)
# define function to be integrated
[ドキュメント] def dxdt(t, x): """評価対象となる微分方程式 dx/dt = -x^2 の右辺を計算します。 詳細説明: 時刻 `t` と変数 `x` の値を受け取り、微分方程式の右辺である `-x*x` を計算します。 :param t: 時刻 `t`。 :type t: float :param x: 変数 `x` の値。 :type x: float :returns: 微分方程式の右辺 `dx/dt` の値。 :rtype: float """ return -x*x
# solution: x = 1 / (C + t), C = 1 for x(0) = 1.0
[ドキュメント] def fsolution(t): """微分方程式 dx/dt = -x^2 の解析解を計算します。 詳細説明: 初期条件 x(0)=1.0 の場合の解析解 `x = 1 / (1 + t)` を返します。 :param t: 時刻 `t`。 :type t: float :returns: 時刻 `t` における解析解 `x` の値。 :rtype: float """ return 1.0 / (1.0 + t)
[ドキュメント] def diffeq_euler(diff1func, t0, x0, dt): """オイラー法を用いて1階常微分方程式を1ステップ進めます。 詳細説明: `x(t+dt) = x(t) + dt * f(t, x(t))` の式に基づき、 次の時刻における `x` の値を計算します。 :param diff1func: 評価対象となる微分方程式の右辺を計算する関数 (`f(t, x)` の形式)。 :type diff1func: callable :param t0: 現在の時刻 `t`。 :type t0: float :param x0: 現在の変数 `x` の値。 :type x0: float :param dt: 時間刻み幅。 :type dt: float :returns: 次の時刻 `t + dt` における `x` の値。 :rtype: float """ k1 = dt * diff1func(t0, x0) x1 = x0 + k1 return x1
[ドキュメント] def diffeq_heun(diff1func, t0, x0, dt): """ホイン法(修正オイラー法)を用いて1階常微分方程式を1ステップ進めます。 詳細説明: オイラー法で求めた予測値と、その予測値を用いて再計算した傾きの平均を取ることで、 より精度の高い次の時刻の `x` の値を計算します。 :param diff1func: 評価対象となる微分方程式の右辺を計算する関数 (`f(t, x)` の形式)。 :type diff1func: callable :param t0: 現在の時刻 `t`。 :type t0: float :param x0: 現在の変数 `x` の値。 :type x0: float :param dt: 時間刻み幅。 :type dt: float :returns: 次の時刻 `t + dt` における `x` の値。 :rtype: float """ k0 = dt * diff1func(t0, x0) k1 = dt * diff1func(t0+dt, x0+k0) x1 = x0 + (k0 + k1) / 2.0 return x1
[ドキュメント] def diffeq_simpson(diff1func, t0, x0, dt): """シンプソン法(に似た重みを持つRunge-Kutta法)を用いて1階常微分方程式を1ステップ進めます。 詳細説明: 3つの傾き `k0, k1, k2` を計算し、シンプソン則の重み `(1/3, 4/3, 1/3)` に似た 加重平均 `(k0 + 4*k1 + k2) / 3` を用いて、次の時刻における `x` の値を計算します。 この実装はRunge-Kutta法のバリアントとして、シンプソン則の重み付けを適用しています。 :param diff1func: 評価対象となる微分方程式の右辺を計算する関数 (`f(t, x)` の形式)。 :type diff1func: callable :param t0: 現在の時刻 `t`。 :type t0: float :param x0: 現在の変数 `x` の値。 :type x0: float :param dt: 時間刻み幅。 :type dt: float :returns: 次の時刻 `t + dt` における `x` の値。 :rtype: float """ k0 = dt * dxdt(t0, x0 ) k1 = dt * dxdt(t0+dt, x0+k0) k2 = dt * dxdt(t0+2.0*dt, x0+k0+k1) x1 = x0 + 1.0 / 3.0 * (k0 + 4.0 * k1 + k2) return x1
#=================== # parameters #=================== x0 = 1.0 dt = 0.1 nt = 501 iprint_interval = 20 #=================== # main routine #===================
[ドキュメント] def main(x0, dt, nt): """1階常微分方程式をシンプソン法で数値的に解き、解析解と比較して結果を表示します。 詳細説明: シミュレーションパラメータに基づいて、時刻 `t`、数値計算結果 `x(cal)`、 および解析解 `x(exact)` をコンソールに出力します。 初期値と時間刻み幅を用いて、ループ内でシンプソン法により `x` を更新していきます。 `iprint_interval` で指定された間隔で結果を出力します。 :param x0: 初期時刻 `t=0` における `x` の初期値。 :type x0: float :param dt: 時間刻み幅。 :type dt: float :param nt: 総ステップ数。 :type nt: int :returns: None :rtype: None """ print("Solve first order diffrential equation by Simpson method") print("{:^5} {:^12} {:^12}".format('t', 'x(cal)', 'x(exact)')) t0 = 0.0 f0 = dxdt(t0, x0) x1 = diffeq_euler(dxdt, t0, x0, dt) # x1 = diffeq_heun(dxdt, t0, x0, dt) xexact = fsolution(t0) print("t={:5.2f} {:12.6f} {:12.6f}".format(t0, x0, xexact)) for i in range(1, nt): t1 = i * dt xexact = fsolution(t1) if i % iprint_interval == 0: print("t={:5.2f} {:12.6f} {:12.6f}".format(t1, x1, xexact)) x2 = diffeq_simpson(dxdt, t0, x0, dt) t0 = t1 x0 = x1 x1 = x2
if __name__ == '__main__': main(x0, dt, nt)