Реализация метода прогонки в решении краевой задачи

У меня есть краевая задача

y'' + p(x) * y' + q(x) * y = f(x),
y(0) = y0,
y(L) = yN.

Надо решить её методом конечных разностей. Иными словами,

  1. Ввести на заданном отрезке равномерную сетку,

  2. Заменить во внутренних точках разбиения отрезка приближённо производные,

  3. Подставить производные в исходное дифференциальное уравнение, получив систему линейных алгебраических уравнений с трёхдиагональной матрицей,

  4. Решить получившуюся систему линейных алгебраических уравнений относительно функции y(x), учитывая начальные краевые условия

    y(0) = y0, y(L) = yN.

Но у меня есть проблема. Я добиваюсь выполнения условия

y(0) = y0,

но не условия

y(L) = yN.

Прикладываю код своей собственной программы на языке программирования Python:

 import sympy as sp
 import numpy as np
 import matplotlib.pyplot as plt

 x = sp.Symbol("x")

 y = sp.Function("y(x)")
 precise_decision = sp.Function("u(x)")

 p = sp.Function("p(x)")
 q = sp.Function("q(x)")
 f = sp.Function("f(x)")

 x0 = 0.0

 def launch(p, q, f, xN, y0, yN, N, precise_decision):
     if xN is np.nan or xN is np.inf or xN <= x0:
         print("Некорректно введён правый конец отрезка краевой задачи")
         return
     if y0 is np.nan or y0 is np.inf:
         print("Некорректно введено первое начальное условие краевой задачи")
         return
     if yN is np.nan or yN is np.inf:
         print("Некорректно введено второе начальное условие краевой задачи")
         return
     if N is np.nan or N is np.inf or N <= 0:
         print("Некорректно введено число отрезков, на которые разбивается отрезок [{0}; {1}]".format(x0, xN))
         return

     display_input_data(p, q, f, xN, y0, yN)

     step = (xN - x0) / N
     x_values = np.arange(x0, xN + step, step)
     y_values = tridiagonal_matrix_algorithm(p, q, f, step, N, x_values, y0, yN)
     paint_graphic(x_values, y_values, precise_decision)

     return

 def display_input_data(p, q, f, xN, y0, yN):
     sp.pprint(p)
     sp.pprint(q)
     sp.pprint(f)
     print("Заданный отрезок:    [{0}; {1}]".format(x0, xN))
     print("Начальные условия:   y(0) = {0}, y(l) = {1}".format(y0, yN))
     print()

     return

 def tridiagonal_matrix_algorithm(p, q, f, delta, N, x_values, y0, yN):
     alphas = []
     alpha = 0.0

     betas = []
     beta = 0.0
     for i in range(1, N + 1):
         x_curr = x_values[i]
         A = (1.0 / np.power(delta, 2)) - p.subs(x, x_curr).evalf() / (2.0 * delta)
         C = (1.0 / np.power(delta, 2)) + p.subs(x, x_curr).evalf() / (2.0 * delta)
         B = -1.0 * (A + C) + q.subs(x, x_curr).evalf()
         F = f.subs(x, x_curr).evalf()

         if i == 1:
             alpha = -1.0 * C / B
             beta = (F - A * y0) / B
         elif i < N:
             denominator = A * alpha + B
             alpha = -1.0 * C / denominator
             beta = (F - A * beta) / denominator
         else:
             alpha = 0.0
             beta = (F - B * yN) / A

         alphas.append(alpha)
         betas.append(beta)

     y_value = yN
     y_values = np.zeros((N + 1))
     y_values[N] = y_value
     for i in range(N - 1, -1, -1):
         y_value = alphas[i] * y_value + betas[i]
         y_values[i] = y_value
     y_values[0] = y0

     return y_values

 def paint_graphic(x_values, y_values, precise_decision):
     plt.figure(figsize = (12, 12))

     plt.plot(x_values, y_values, marker = "o", markersize = 6, color = "black", label = "приближённое решение y(x)", linewidth = 1.0, linestyle = "--")

     h = sp.lambdify(x, precise_decision, "numpy")
     mapping = np.linspace(x_values[0], x_values[len(x_values) - 1], 100000)
     plt.plot(mapping, h(mapping), label = "точное решение u(x)", linewidth = 2, linestyle = "-", color = "red")

     plt.xlabel("x", color = "blue", fontsize = 20)
     plt.ylabel("y(x)", color = "blue", fontsize = 20)

     plt.legend()

     plt.title("Графики приближённого и точного решений краевой задачи", color = "blue", fontsize = 20)

     plt.grid(linewidth = 1, linestyle = "--")
     plt.xticks(fontsize = 10)
     plt.yticks(fontsize = 10)

     plt.savefig("graphic.png", format = "png")

     plt.show()

     return

`

В качестве примеров были предложены краевые задачи:

p = 0.0 * x
q = 4.0 + 0.0 * x
f = 5.0 * sp.exp(x)
y(0.0) = 1
y(np.pi / 4) = np.exp(np.pi / 4) + 1.0
precise_decision = sp.sin(2.0 * x) + sp.exp(x)

и

p = sp.tan(x)
q = sp.Pow(sp.cos(x), 2)
f = 0.0 + 0.0 * x
y(0.0) = 1
y(8.0) = 10.0
precise_decision = sp.cos(sp.sin(x)) + ((10.0 - sp.cos(sp.sin(8))) / sp.sin(sp.sin(8))) * sp.sin(sp.sin(x))

Ответы (1 шт):

Автор решения: Pak Uula

Я, признаюсь, поломался разбирать ваш код, и сделал свой решатель на numpy.

class Solver:
    "Решение краевой задачи для ОДУ второго порядка методом прогонки"
    def __init__(self, x0, x1, y0, y1, p,q,f):
        """Инициализация решателя. 
        x0,y0 - левая точка решения,
        x1,y1 - правая точка решения,
        p,q,f - функции от х, коэффициенты уравнения y'' + p(x)y' + q(x)y = f(x)
        """
        self.x0 = x0
        self.y0 = y0
        self.x1 = x1
        self.y1 = y1
        self.p = p
        self.q = q
        self.f = f
    def _mkSLA(self, N):
        """Создание матрицы СЛАУ.
        N - число точек сетки
        """
        self.N = N
        self.X = np.linspace(self.x0, self.x1, N)
        self.P = np.vectorize(self.p)(self.X)
        self.Q = np.vectorize(self.q)(self.X)
        self.F = np.vectorize(self.f)(self.X)
        self.Y = np.zeros(N)
        self.Y[0] = self.y0
        self.Y[-1] = self.y1
        self.h = self.X[1]-self.X[0]
        
        h = self.h
        self.A = (1-h/2*self.P)
        self.B = (h*h*self.Q-2)
        self.C = (1+h/2*self.P)
        self.D = h*h*self.F
        # краевые условия
        self.A[0], self.B[0], self.C[0], self.D[0] = 0.0, 1.0, 0.0, self.y0
        self.A[-1], self.B[-1], self.C[-1], self.D[-1] = 0.0, 1.0, 0.0, self.y1
        
    def solve(self, N):
        """Решение краевой задачи.
        N - число точек сетки
        """
        self._mkSLA(N)
        Y = self.Y
        A,B,C,D = self.A, self.B, self.C, self.D
        
        alpha = np.zeros(N)
        beta = np.zeros(N)
        
        u = B[0]
        alpha[0] = -C[0]/u
        beta[0] = D[0]/u
    
        for i in range(1,N):
            u = B[i] + A[i]*alpha[i-1]
            alpha[i] = -C[i]/u
            beta[i] = (D[i] - A[i]*beta[i-1])/u

        Y[-1] = beta[-1]
        for i in range(N-2,-1,-1):
            Y[i] = alpha[i]*Y[i+1] + beta[i]
        
        return Y

Успешно решает краевую задачу для уравнение свободного падения, уравнения малых колебаний маятника, и обоих примеров из вашей задачи.

Jupiter Notebook с решателем и примерами решения уравнений

Берите, пользуйтесь.

PS. Решение чисто питоновское, поэтому уступает решателю на фортране. Можно значительно ускорить циклы, практически до фортрановкой производительности, если загнать функцию solve в нумбу, но это уже будет немного слишком хорошо. Едва ли ваш преподаватель согласится принять такой продвинутый решатель ))

→ Ссылка