В данной работе автор ставит задачу численного решения уравнения спитцеровской теплопроводности плазмы. Для этого реализуется чисто неявная схема с нелинейностью на верхнем слое. Схема проверяется на решении модельной задачи Соболя-Самарского-Зельдовича, а так же на неоднородной постановке исходной задачи. Реализацию всех алгоритмов можно найти в github-репозитории.
Задача Коши для уравнения двухкомпонентной теплороводности плазмы:
Начальные условия представляют равномерно прогретый слой плазмы фиксированной длинны:
Граничные условия представляют собой теплоизолированную стенку слева и ноль на бесконечности:
Где
Как известно производная решения
Схема имеет первый порядок апроксимации по времени
Запишем разностную задачу:
Для реализации метода прогонки проведём линеаризацию:
Будем проверять схему на модельной задаче Соболя-Самарского-Зельдовича:
Для задачи известо аналитическое решение:
import numpy as np
from tqdm.notebook import tqdm
from numba import njit
from thomas import solve_equations, solve_blocks
from make_diagonal import zeldovich_nonlinear, make_block_diagonals
import matplotlib.pyplot as plt
from matplotlib.pyplot import axes
from celluloid import Camera
Реализация метода Ньютона
@njit
def newton_solver(y, y_left, tau, h, alpha=2.5, kappa=0.2, iter=10):
y_n, y_s = y, y
y_s[0] = y_left
for i in range(iter):
a, b, c, d = zeldovich_nonlinear(y_s, tau, h, y_n, alpha=alpha, kappa=kappa)
dy = solve_equations(a, b, c, d)
y_s = y_s + dy
return y_s
Зададим параметры и начальные значения
a = 0
b = 3
t_0 = 0
T = 2
To = 2
c = 3
alpha = 2.5
kappa = 0.2
print(f'>>> Скорость волны v = {np.sqrt(c ** (1 / alpha) * kappa / alpha): .4f}')
>>> Скорость волны v = 0.3523
Зададим параметры сетки
# Число узлов
N = 6000
M = 100
# Размер шага сетки
h = (b - a) / (M - 1)
tau = (T - t_0) / (N - 1)
print(f'>>> {h=: .8f}, {tau=: .8f}')
print(f'>>> Гиперболический аналог числа куранта sigma ={kappa * tau / h ** 2 * 0.5: .4f}')
>>> h= 0.03030303, tau= 0.00033339
>>> Гиперболический аналог числа куранта sigma = 0.0363
t = np.linspace(t_0, T, N)
x = np.linspace(a, b, M)
Инициализируем сетку, начальные и граничные условия
u = np.zeros((N, M), dtype=np.double)
u[:, 0] = c * t ** (1 / alpha)
При решении системы уравнений будем делать 10 итераций метода Ньютона
for n in tqdm(range(N - 1)):
u_sol = newton_solver(u[n, :], u[n + 1, 0], tau, h, alpha=2.5, kappa=0.2, iter=10)
u[n + 1, 1:] = u_sol[1:]
0%| | 0/5999 [00:00<?, ?it/s]
Аналитическое решение изображено зелёным, численное -- красным. Как можно видеть, волновой фронт численного решения несколько отстаёт от аналитического.
Проверим реализацию схемы на однородной постановке задачи:
Реализация метода Ньютона
def newton_solver(u1, u2, tau, h, alpha=(2.5, 1.5), kappa=(0.2, 0.3), iter=10):
u1_s, u2_s = u1, u2
for i in range(iter):
A, B, C, D = make_block_diagonals(u1_s, u2_s, tau, h, u1, u2, alpha=alpha, kappa=kappa, nonhomogen=True)
du = solve_blocks(A, B, C, D)
u1_s = u1_s + du[:, 0]
u2_s = u2_s + du[:, 1]
return u1_s, u2_s
Зададим параметры и начальные значения
# Начальные значения
a = 0
b = 3
t_0 = 0
T = 4
To = 2
alpha = [2.5, 1.5]
kappa = [0.2, 0.3]
Зададим параметры сетки
# Число узлов
N = 6000
M = 100
# Размер шага сетки
h = (b - a) / (M - 1)
tau = (T - t_0) / (N - 1)
print(f'>>> {h=: .8f}, {tau=: .8f}')
print(f'>>> Гиперболический аналог числа куранта: \n sigma_1 ={kappa[0] * tau / h ** 2 * 0.5: .4f} \n sigma_2 ={kappa[1] * tau / h ** 2 * 0.5: .4f}')
>>> h= 0.03030303, tau= 0.00066678
>>> Гиперболический аналог числа куранта:
sigma_1 = 0.0726
sigma_2 = 0.1089
t = np.linspace(t_0, T, N)
x = np.linspace(a, b, M)
Инициализируем сетку, начальные и граничные условия
def u_init(x, To):
u = np.zeros(x.size)
for i in range(len(x)):
if x[i] <= 1:
u[i] = To
return u
u1 = np.zeros((N, M), dtype=np.double)
u1[0, :] = u_init(x, 2)
u2 = np.zeros((N, M), dtype=np.double)
u2[0, :] = u_init(x, 2)
При решении системы уравнений будем делать 10 итераций метода Ньютона
for n in tqdm(range(N - 1)):
u1_sol, u2_sol = newton_solver(u1[n, :], u2[n, :], tau, h, alpha=(2.5, 1.5), kappa=(0.2, 0.3), iter=10)
u1[n + 1, :], u2[n + 1, :] = u1_sol, u2_sol
0%| | 0/5999 [00:00<?, ?it/s]
Синим обозначена электронная температура
Напомню постановку исходной задачи: Задача Коши для уравнения двухкомпонентной теплороводности плазмы:
Начальные условия представляют равномерно прогретый слой плазмы фиксированной длинны:
Граничные условия представляют собой теплоизолированную стенку слева и ноль на бесконечности:
Где
Реализация метода Ньютона
def newton_solver(u1, u2, tau, h, alpha=(2.5, 1.5), kappa=(0.2, 0.3), iter=10):
u1_s, u2_s = u1, u2
for i in range(iter):
A, B, C, D = make_block_diagonals(u1_s, u2_s, tau, h, u1, u2, alpha=alpha, kappa=kappa, nonhomogen=False)
du = solve_blocks(A, B, C, D)
u1_s = u1_s + du[:, 0]
u2_s = u2_s + du[:, 1]
return u1_s, u2_s
Зададим параметры и начальные значения
# Начальные значения
a = 0
b = 3
t_0 = 0
T = 4
To = 2
alpha = [2.5, 1.5]
kappa = [0.2, 0.3]
Зададим параметры сетки
# Число узлов
N = 6000
M = 100
# Размер шага сетки
h = (b - a) / (M - 1)
tau = (T - t_0) / (N - 1)
print(f'>>> {h=: .8f}, {tau=: .8f}')
print(f'>>> Гиперболический аналог числа куранта: \n sigma_1 ={kappa[0] * tau / h ** 2 * 0.5: .4f} \n sigma_2 ={kappa[1] * tau / h ** 2 * 0.5: .4f}')
>>> h= 0.03030303, tau= 0.00066678
>>> Гиперболический аналог числа куранта:
sigma_1 = 0.0726
sigma_2 = 0.1089
t = np.linspace(t_0, T, N)
x = np.linspace(a, b, M)
Инициализируем сетку, начальные и граничные условия
def u_init(x, To):
u = np.zeros(x.size)
for i in range(len(x)):
if x[i] <= 1:
u[i] = To
return u
u1 = np.zeros((N, M), dtype=np.double)
u1[0, :] = u_init(x, 2)
u2 = np.zeros((N, M), dtype=np.double)
u2[0, :] = u_init(x, 2)
np.seterr(divide='raise', invalid='raise')
{'divide': 'warn', 'over': 'warn', 'under': 'ignore', 'invalid': 'warn'}
При решении системы уравнений будем делать 2 итераций метода Ньютона
for n in tqdm(range(N - 1)):
u1_sol, u2_sol = newton_solver(u1[n, :], u2[n, :], tau, h, alpha=(2.5, 1.5), kappa=(0.2, 0.3), iter=2)
u1[n + 1, :], u2[n + 1, :] = u1_sol, u2_sol
0%| | 0/5999 [00:00<?, ?it/s]
Синим обозначена электронная температура