Загрузка данных
import numpy as np
import sympy as sp
import matplotlib.pyplot as plt
from matplotlib.widgets import TextBox
# ============================================================
# Условие (вариант 10):
# u_t = u_xx + f(x,t), f(x,t)= pi*t*sin(pi*x/2) + x*cos(pi*x/2)*(1+t*pi^2/4)
# u(x,0)=0, u(0,t)=u(1,t)=0
# ============================================================
n0, m0, t0 = 20, 25, 1.0
def f(x, t):
return np.pi * t * np.sin(np.pi * x / 2) + x * np.cos(np.pi * x / 2) * (1 + t * np.pi ** 2 / 4)
def u_exact(x, t):
"""Точное решение, найденное подбором (проверено символьно: u_t-u_xx=f тождественно)."""
return t * x * np.cos(np.pi * x / 2)
# ============================================================
# МЕТОД 1. Сеточная краевая задача: неявная схема (sigma=1) + прямая прогонка
# (формулы (7.11)-(7.12) методички Матвеева, метод прогонки — формулы (6.7)-(6.10))
# ============================================================
def solve_grid(n, m, t_final):
h = 1.0 / n
tau = t_final / m
x = np.linspace(0, 1, n + 1)
u = np.zeros(n + 1) # u(x,0) = 0
d = -(2 + h ** 2 / tau)
e = 1.0
for j in range(m):
t_next = (j + 1) * tau
q = np.zeros(n + 1)
phi = np.zeros(n + 1)
q[0], phi[0] = 0.0, 0.0 # chi1=mu1=0 (левое условие u(0,t)=0)
for i in range(1, n):
g_i = -(h ** 2 * f(x[i], t_next) + (h ** 2 / tau) * u[i])
denom = q[i - 1] + d
q[i] = -e / denom
phi[i] = (g_i - phi[i - 1]) / denom
u_new = np.zeros(n + 1)
u_new[n] = 0.0 # chi2=mu2=0 (правое условие u(1,t)=0)
for i in range(n - 1, -1, -1):
u_new[i] = q[i] * u_new[i + 1] + phi[i]
u = u_new
return x, u
# ============================================================
# МЕТОД 2. Метод Фурье (разложение по sin(k*pi*x), до 3-го члена)
# ============================================================
def solve_fourier_symbolic(num_terms=3):
x_sym, t_sym, s_sym, k_sym = sp.symbols('x t s k', real=True)
f_sym = sp.pi * t_sym * sp.sin(sp.pi * x_sym / 2) + \
x_sym * sp.cos(sp.pi * x_sym / 2) * (1 + t_sym * sp.pi ** 2 / 4)
vk_funcs = []
for k in range(1, num_terms + 1):
ek = sp.sin(k * sp.pi * x_sym)
gk = sp.simplify(2 * sp.integrate(f_sym * ek, (x_sym, 0, 1)))
vk = sp.Function('v')
lam = -(k * sp.pi) ** 2
eq = sp.Eq(sp.diff(vk(t_sym), t_sym), lam * vk(t_sym) + gk)
sol = sp.dsolve(eq, vk(t_sym), ics={vk(0): 0})
vk_expr = sp.simplify(sol.rhs)
vk_funcs.append((k, sp.lambdify(t_sym, vk_expr, 'numpy'), vk_expr))
return vk_funcs
VK_FUNCS = solve_fourier_symbolic(num_terms=3)
for k, _, expr in VK_FUNCS:
print(f"v_{k}(t) = {expr}")
def u_fourier(x, t, vk_funcs=VK_FUNCS):
x = np.asarray(x, dtype=float)
result = np.zeros_like(x)
for k, vk_func, _ in vk_funcs:
result = result + vk_func(t) * np.sin(k * np.pi * x)
return result
# ============================================================
# Сравнение
# ============================================================
def print_deviations(n, m, t_final):
xs, u_grid = solve_grid(n, m, t_final)
ue = u_exact(xs, t_final)
uf = u_fourier(xs, t_final)
err_grid = np.max(np.abs(u_grid - ue))
err_fourier = np.max(np.abs(uf - ue))
print(f"--- n={n}, m={m}, t0={t_final} ---")
print(f" Сеточный метод (неявная схема + прогонка): max|u-u_точн| = {err_grid:.4e}")
print(f" Метод Фурье (3 члена): max|u-u_точн| = {err_fourier:.4e}\n")
# ============================================================
# Один график, x от 0 до 1, момент времени t=t0
# ============================================================
N_PLOT_POINTS = 500
def refresh_exact_curve():
x0, x1 = ax.get_xlim()
x0c, x1c = max(x0, 0.0), min(x1, 1.0)
if x1c <= x0c:
return
x_local = np.linspace(x0c, x1c, N_PLOT_POINTS)
line_exact.set_data(x_local, u_exact(x_local, t0))
fig, ax = plt.subplots(figsize=(9, 6))
plt.subplots_adjust(bottom=0.28)
line_exact, = ax.plot([], [], '-', color='black', linewidth=2, label='Точное решение')
xs0, ug0 = solve_grid(n0, m0, t0)
line_grid, = ax.plot(xs0, ug0, linestyle='None', marker='o', markersize=7,
markerfacecolor='none', markeredgewidth=1.8, color='tab:red',
label=f'Сеточный метод, n={n0}, m={m0}')
x_fine = np.linspace(0, 1, N_PLOT_POINTS)
line_fourier, = ax.plot(x_fine, u_fourier(x_fine, t0), linestyle=(0, (6, 3)),
linewidth=2, color='tab:blue', label='Метод Фурье (3 члена)')
ax.set_xlim(0, 1)
refresh_exact_curve()
ax.set_xlabel('x')
ax.set_ylabel('u(x, t0)')
ax.set_title(f"u_t=u_xx+f(x,t), u(x,0)=0, u(0,t)=u(1,t)=0\nвариант 10, момент времени t0={t0}")
ax.legend(loc='best')
ax.grid(alpha=0.3)
print_deviations(n0, m0, t0)
# ---------- Поля ввода n, m ----------
axbox_n = plt.axes([0.25, 0.10, 0.08, 0.06])
text_n = TextBox(axbox_n, "n: ", initial=str(n0))
axbox_m = plt.axes([0.55, 0.10, 0.08, 0.06])
text_m = TextBox(axbox_m, "m: ", initial=str(m0))
state = {'n': n0, 'm': m0}
def update(_text):
try:
n = int(text_n.text)
m = int(text_m.text)
if n < 3 or m < 1:
raise ValueError
except ValueError:
print("Некорректные n или m — введите целые числа (n>=3, m>=1).")
return
state['n'], state['m'] = n, m
xs, ug = solve_grid(n, m, t0)
line_grid.set_xdata(xs)
line_grid.set_ydata(ug)
line_grid.set_label(f'Сеточный метод, n={n}, m={m}')
ax.legend(loc='best')
fig.canvas.draw_idle()
print_deviations(n, m, t0)
text_n.on_submit(update)
text_m.on_submit(update)
MIN_RANGE = 1e-6
def on_scroll(event):
if event.inaxes != ax:
return
scale = 1.2
factor = (1 / scale) if event.button == 'up' else scale
xlim = ax.get_xlim()
ylim = ax.get_ylim()
xdata, ydata = event.xdata, event.ydata
if xdata is None or ydata is None:
return
new_w = (xlim[1] - xlim[0]) * factor
new_h = (ylim[1] - ylim[0]) * factor
if new_w < MIN_RANGE or new_h < MIN_RANGE:
return
relx = (xlim[1] - xdata) / (xlim[1] - xlim[0])
rely = (ylim[1] - ydata) / (ylim[1] - ylim[0])
ax.set_xlim([xdata - new_w * (1 - relx), xdata + new_w * relx])
ax.set_ylim([ydata - new_h * (1 - rely), ydata + new_h * rely])
refresh_exact_curve()
fig.canvas.draw_idle()
fig.canvas.mpl_connect('scroll_event', on_scroll)
pan_state = {'active': False, 'x0': None, 'y0': None, 'xlim0': None, 'ylim0': None}
def on_press(event):
if event.inaxes != ax:
return
if event.button not in (1, 3):
return
pan_state['active'] = True
pan_state['x0'] = event.xdata
pan_state['y0'] = event.ydata
pan_state['xlim0'] = ax.get_xlim()
pan_state['ylim0'] = ax.get_ylim()
def on_release(_event):
if pan_state['active']:
refresh_exact_curve()
fig.canvas.draw_idle()
pan_state['active'] = False
def on_motion(event):
if not pan_state['active'] or event.inaxes != ax:
return
if event.xdata is None or event.ydata is None:
return
dx = event.xdata - pan_state['x0']
dy = event.ydata - pan_state['y0']
xlim0, ylim0 = pan_state['xlim0'], pan_state['ylim0']
ax.set_xlim(xlim0[0] - dx, xlim0[1] - dx)
ax.set_ylim(ylim0[0] - dy, ylim0[1] - dy)
fig.canvas.draw_idle()
fig.canvas.mpl_connect('button_press_event', on_press)
fig.canvas.mpl_connect('button_release_event', on_release)
fig.canvas.mpl_connect('motion_notify_event', on_motion)
plt.show()