Пример. Нелинейное уравнение Пуассона#
Или как работать с нелинейностями?
Есть два простых метода решения этой задачи. Оба основаны на последовательном приближении к ответу.
1. Метод итераций (метод Пикарда)#
Идея: по текущему прилижению определить следующее
В слабой форме получим:
Продолжаем итерацию, пока решение на новом шаге будет слабо отличатся от текущего решения.
2. Метод Ньютона#
Идея: по текущему приближению определить возмущение, которое улучшит приближение.
Подставим это в исходное уравнение, отбрасывая члены второго порядка.
Домножая на тестовую функцию и проводя интегрирование по частям, приходим к слабой форме.
Видно, что уравнение имеет вид:
Билинейная форма \((\delta u, v)=\) Линейная форма \((v)\).
Важные детали:
Начальное приближение мы не знаем, поэтому придется угадывать. Чем лучше догадка, тем быстрее мы сойдемся к ответу.
Возмущение должно зануляться на границе
mesh = fe.UnitIntervalMesh(40)
V = fe.FunctionSpace(mesh, "Lagrange", 1)
tol = 1e-14
def left_boundary(x, on_boundary):
return on_boundary and abs(x[0]) < tol
def right_boundary(x, on_boundary):
return on_boundary and abs(x[0]-1) < tol
Gamma_0 = fe.DirichletBC(V, fe.Constant(0.0), left_boundary)
Gamma_1 = fe.DirichletBC(V, fe.Constant(1.0), right_boundary)
bcs = [Gamma_0, Gamma_1]
# Найдем начальное приближение (q(u)=1, m=0)
u = fe.TrialFunction(V)
v = fe.TestFunction(V)
a = fe.inner(fe.nabla_grad(u), fe.nabla_grad(v))*fe.dx
f = fe.Constant(0.0)
L = f*v*fe.dx
A, b = fe.assemble_system(a, L, bcs)
u_k = fe.Function(V)
U_k = u_k.vector()
fe.solve(A, U_k, b)
1
fe.plot(u_k)
plt.title('Inital guess')
Text(0.5, 1.0, 'Inital guess')
#добавим граничные условия для возмущения du
Gamma_0_du = fe.DirichletBC(V, fe.Constant(0), left_boundary)
Gamma_1_du = fe.DirichletBC(V, fe.Constant(0), right_boundary)
bcs_du = [Gamma_0_du, Gamma_1_du]
m = 8
def q(u):
return (1+u)**m
#производная от q
def Dq(u):
return m*(1+u)**(m-1)
du = fe.TrialFunction(V) # u = u_k + omega*du
a = fe.inner(q(u_k)*fe.nabla_grad(du), fe.nabla_grad(v))*fe.dx + \
fe.inner(Dq(u_k)*du*fe.nabla_grad(u_k), fe.nabla_grad(v))*fe.dx
L = -fe.inner(q(u_k)*fe.nabla_grad(u_k), fe.nabla_grad(v))*fe.dx
du = fe.Function(V)
u = fe.Function(V) # u = u_k + omega*du
omega = 1.0 # relaxation parameter
eps = 1.0
tol = 1.0E-5
iter = 0
maxiter = 25
while eps > tol and iter < maxiter:
iter += 1
A, b = fe.assemble_system(a, L, bcs_du)
fe.solve(A, du.vector(), b)
eps = np.linalg.norm(du.vector(), ord=np.Inf)
print("Norm:", eps)
u.vector()[:] = u_k.vector() + omega*du.vector()
u_k.assign(u)
Norm: 2.6926580821284225
Norm: 0.42678052234021463
Norm: 0.3790764929916502
Norm: 0.3362295391794988
Norm: 0.29701382866741033
Norm: 0.25931623334952986
Norm: 0.2189422903508294
Norm: 0.1703542478525539
Norm: 0.10525359212091752
Norm: 0.03666912499970038
Norm: 0.0037568546653569818
Norm: 3.590420381359272e-05
Norm: 3.2438027096345295e-09
fe.plot(u_k)
[<matplotlib.lines.Line2D at 0x7c4deb1707c0>]