Эллиптический тип#
Обычно нет временной зависимости - несколько пространственных координат. Нет эволюции во времени - процесс стационарный.
Решаются как краевые задачи при заданных краевых условиях.
Решения гладкие, если позволяют краевые условия.
Примеры:
Уравнение Пуассона (Лапласа при \(f=0\)):
Пример решения уравнения Пуассона в 2d. Прямой метод.#
Мы хотим решить уравнение Пуассона:
в \([0,1] \times [0,1]\) - квадратная область с граничными условиями
Примечание. Даже если область другая, можем произвести замену координат к квадрату. Если же это уничтожаем устойчивость, необходима дискретизация нетривиальных граничных условий.
Дискретизуем производные. Возьмём формулы второго порядка аппроксимации по каждой из осей.
Отсюда получаем уравнение на центральную точку (т.к. знаем края логично двигаться в центр)
Решаем наш «диффур» на сетке \((i, j)\). При этом знаем все граничные точки на сетке, и есть уравнение связи точек на «кресте» (т.н. шаблон крест).
Примечание. Зная 4 точки из «креста», можно найти пятую, выразив из уравнения. При другой дискретизации производных получили бы другие шаблоны.
Такая схема обладает 2-м порядком аппроксимации (достаточно просто разложить по Тейлору вокруг точек \(x_i\) и \(y_j\) и убедиться, что дифференциальное уравнение выполняется с точностью \(O(h^2)\)).
Для решения задачи (определения всех внутренних точек, зная только краевые) можно составить большую СЛАУ на все внутренние точки. Грубо говоря, будем идти нашим крестом по всем внутренним точкам начиная с нижнего ряда и до верхнего по порядку. В каждом случае, будем запоминать, какая функциональная связь между коэффициентами. Когда дойдём до верхнего ряда, пойдём назад и «расшифруем» все пройдённые точки.
Это называется прямой метод. Объясним его подробнее.
Хотим свести дифференциальную задачу к дискретной, заданной в тензорном (дискретизованном) виде (с учётом краевых условий):
Во всех «внутренних» точках квадрата оно имеет тот самый дискретизованный вид, который мы написали ранее:
А тогда просто вспомнив, как работает сумма, для внутренних \(i,j \in [1, \cdots,n-1]\) выполнено
Также в это тензорное уравнение мы хотим занести краевые условия. Достаточно следующего:
что эквивалентно
Для остальных трёх краевых условий аналогично.
Теперь, когда мы определили все элементы \(L_{i,j,k,l}\) и \(b_{i,j}\), остаётся только решить тензорное уравнение относительно \(u_{i,j}\):
В этом могут помочь методы numpy ;).
import numpy as np
import matplotlib as mpl
import matplotlib.pyplot as plt
def boundary(grid):
x = np.linspace(0,1,len(grid))
grid[0,:] = -x
grid[:,-1] = x-1
grid[-1,:] = 1-x
grid[:,0] = x
def poisson_direct(gridsize, set_boundary):
L = np.zeros(shape=(gridsize, gridsize, gridsize, gridsize),
dtype='d')
b = np.zeros(shape=(gridsize, gridsize),
dtype='d')
dx = 1.0 / (gridsize - 1)
# discretized differential operator
for i in range(1,gridsize-1):
for j in range(1,gridsize-1):
L[i,j,i-1,j] = L[i,j,i+1,j] = L[i,j,i,j-1] = L[i,j,i,j+1] = 1/dx**2
L[i,j,i,j] = -4/dx**2
# boundary conditions
for i in range(0,gridsize):
L[0,i,0,i] = L[-1,i,-1,i] = L[i,0,i,0] = L[i,-1,i,-1] = 1
# set the boundary values on the right side
set_boundary(b)
return np.linalg.tensorsolve(L,b)
def showsol(sol):
plt.imshow(sol.T,cmap=mpl.cm.Blues,interpolation='none',origin='lower')
sol = poisson_direct(25, boundary) # Сетка из 25 точек
showsol(sol)
showsol(poisson_direct(50,boundary)) # Уменьшим сетку
Итерационный метод - сведение к уравнению теплопроводности#
Вообще, прямой подход достаточно сложный - нужно обращать сильно разряженную матрицу. Можно решить эту же задачу итерационно.
На основе нашего уравнения Пуассона запишем параболическое уравнение диффузии (технически, оно уже от трёх переменных - см. увеличенную классификацию):
Идея заключается в том, что на бесконечности по \(t\) эволюция системы должна остановится (система достигнет равновесия), а тогда левая часть занулится и полученное состояние будет решением уравнения Пуассона в чистом виде. Стартовать при этом «казалось бы» можно из любой начальной конфигурации, отвечающей граничным условиям. Естественно, скорость сходимости будет зависеть от близости к истинному решению.
Такое уже решать умеем - это же параболическое уравнение. Решим его явной схемой для простоты.
from numba import njit
@njit()
def boundary(grid):
x = np.linspace(0,1,len(grid))
grid[0,:] = -x
grid[:,-1] = x-1
grid[-1,:] = 1-x
grid[:,0] = x
@njit()
def step(grid):
newgrid = grid.copy()
for i in range(1,newgrid.shape[0]-1):
for j in range(1,newgrid.shape[1]-1):
newgrid[i,j] = 0.25 * (newgrid[i,j+1] + newgrid[i,j-1] +
newgrid[i+1,j] + newgrid[i-1,j])
return newgrid
@njit() # Начальная случайная сетка
def initgrid(gridsize):
x = np.random.randn(gridsize,gridsize)
boundary(x)
return x
x = initgrid(100)
plt.figure(figsize=(12,12))
for i in range(1500):
if i % 100 == 0:
plt.subplot(4,4,i//100+1)
showsol(x)
plt.title('iter = %s' % i)
x = step(x)
plt.tight_layout()
# Посмотрим также на ошибку от временной итерации
x = initgrid(25)
sol = poisson_direct(25, boundary)
err = []
for i in range(1000):
err.append((i,np.linalg.norm(x - sol)))
x = step(x)
err = np.array(err)
plt.semilogy(err[:,0],err[:,1])
plt.title('Error after iteration');