Я пытаюсь решить три связанных дифференциальных уравнения на Python. Я использую технику РК-4 методом стрельбы. Я пытаюсь построить функции f и N.
import numpy as np
import matplotlib.pyplot as plt
from scipy.optimize import fsolve
alpha = 0.1
def F(r, f, v, N, sig):
g = np.sin(f)
h = np.cos(f)
i = np.sin(2 * f)
dfdr = v
dNdr = -(alpha*r**2 * N * v**2 *(r**2 + 2 * g**2)+alpha * g**2 * (g**2 +2* r**2) + r**2*(N-1))/r**3
dsigdr = ( alpha * sig * v**2 * (r**2 + 2 * g**2)) / r
f1= v*((2 * dsigdr * g**2)/(sig * r**2)+(2 * dNdr * g**2)/(N * r**2)+((dsigdr)/sig +dNdr / N + 2 / r))
dvdr = -(f1 -i/(N * r**2)+(-(2 * g**3 * h)/(N * r**4)+(v**2 * i)/r**2)) / (1 + (2 * g**2) / r**2)
return [dfdr, dNdr, dsigdr, dvdr]
def rk4(F, r_range, f0, v0, N0, s0, h):
r_values = np.arange(r_range[0], r_range[1], h)
f_values = np.linspace(f0, 0, len(r_values))
v_values = np.linspace(v0, 0, len(r_values))
N_values = np.linspace(N0, 1, len(r_values))
S_values = np.linspace(s0, 1, len(r_values))
for i in range(1, len(r_values)):
r = r_values[i-1]
f = f_values[i-1]
v = v_values[i-1]
N = N_values[i-1]
sig = S_values[i-1]
k1f, k1v, k1N, k1S = F(r, f, v, N, sig)
k2f, k2v, k2N, k2S = F(r + 0.5*h, f + 0.5*k1f*h, v + 0.5*k1v*h, N + 0.5*k1N*h, sig +0.5*k1S*h)
k3f, k3v, k3N, k3S = F(r + 0.5*h, f + 0.5*k2f*h, v + 0.5*k2v*h, N + 0.5*k2N*h, sig +0.5*k2S*h)
k4f, k4v, k4N, k4S = F(r + h, f + k3f*h, v + k3v*h, N + k3N*h, sig + k3S*h)
f_values[i] = f + ((k1f + 2*k2f + 2*k3f + k4f)*h) / 6
v_values[i] = v + ((k1v + 2*k2v + 2*k3v + k4v)*h) / 6
N_values[i] = N + ((k1N + 2*k2N + 2*k3N + k4N)*h) / 6
S_values[i] = sig + ((k1S + 2*k2S + 2*k3S + k4S)*h)/ 6
return r_values, f_values, v_values, N_values, S_values
rmin = 0.001
rmax = 4
r_range=[rmin, rmax]
h = 0.00001
def objective(u):
v0, s0 = u
r_values, f_values, v_values, N_values, S_values = rk4(F, r_range, np.pi, v0, 1, s0, h)
y1 = f_values[-1]
y2 = S_values[-1]
print(f"Objective results: y1 = {y1}, y2 = {y2}")
return [y1 , y2 - 1]
initial_guess=[-0.478, -1.216]
v1 = fsolve(objective, initial_guess, xtol=1e-8)
print("The initial values of v0 and sig0 given by fsolve:", v1[0], " and ", v1[1])
r_values, f_values, v_values, N_values, S_values = rk4(F, [rmin, rmax], np.pi, v1[0], 1, v1[1], h)
y3 = f_values[-1]
y4 = S_values[-1]
print("The value of f and sig at outer boundary is:", y3, y4)
if np.abs(y3) < 1e-6 and 1<= y4 <1.1:
print("Boundary condition f(rmax) = 0 is satisfied, and the value of f at outer boundar is:", y3)
print("Boundary condition sig(rmax) = 0 is satisfied, and the value of sig at outer boundar is:", y4)
else:
print("The solutions did not converge")
plt.figure(figsize=(14, 10))
plt.plot(r_values, f_values, label='f(r)')
plt.plot(r_values, N_values, label='N(r)')
plt.title(r"$\alpha=0.1$")
plt.xlabel('r')
plt.grid()
plt.legend()
plt.show()
Но характер сюжета совершенно отличается от моих ожиданий. Результат:
Objective results: y1=0.000372840219254553, y2=1.0001907509516712
Objective results: y1=0.000372840219254553, y2=1.0001907509516712
Objective results: y1=0.000372840219254553, y2=1.0001907509516712
Objective results: y1=0.00037294172854991653, y2=1.0001906882951273
Objective results: y1=0.000372824083910513, y2=1.0001907727358128
Objective results: y1=1.8398001535383646e-07, y2=0.9999999172810013
Objective results: y1=1.1072732970996606e-11, y2=1.0000000000024691
Objective results: y1=2.1284157569008075e-12, y2=0.9999999999969905
Objective results: y1=1.1784771089214975e-13, y2=0.9999999999997673
The initial values of v0 and sig0 given by fsolve: -0.4780664768861783 and -1.2166450055671914
The value of f and sig at the outer boundary is: 1.1784771089214975e-13 0.9999999999997673
The solutions did not converge
Как видите, значение sig на внешней границе почти равно единице (0,9999999999997673).
Это три связанных дифференциальных уравнения с граничными условиями
Забыл упомянуть дополнительное условие на функцию N, здесь дано дополнительное условие на функцию N
Я также попробовал приведенный выше код с меньшим значением h (= 0,000001) и другими значениями rmin, rmax, Alpha и Initial_guess, но результат был тот же.
Я также попытался решить проблему с помощьюsolve_bvp. Вот код, использующийsolve_bvp
import numpy as np
from scipy.integrate import solve_bvp
import matplotlib.pyplot as plt
alpha = 0.1
def F(r, f):
#f, v, N, sig = S
g = np.sin(f[0])
h = np.cos(f[0])
i = np.sin(2 * f[0])
#dfdr = v
dNdr = -( alpha * r**2 * f[2] * f[1]**2 * (r**2 + 2* g**2) + alpha * g**2 * (g**2 +2* r**2) + r**2 * (f[2] - 1)) / r**3
dsigdr = ( alpha * f[3] * f[1]**2 * (r**2 + 2 * g**2)) / r
ter1=((dsigdr) / f[3] + dNdr / f[2] + 2 / r +(2 * dsigdr * g**2) / (f[3] * r**2) + (2 * dNdr* g**2) / (f[2] * r**2))
dvdr = -((f[1] * ter1 - i / (f[2] * r**2)) + (-(2 * g**3 * h) / (f[2] * r**4) + (f[1]**2 * i) / r**2)) / (1 + (2 * g**2) / r**2)
return np.vstack((f[1], dvdr, dNdr, dsigdr))
def bc(fa, fb):
return np.array([fa[0]-np.pi, fb[0]-0, fa[2]-1, fb[3]-1])
rmin = 0.001
rmax = 5
x = np.linspace(rmin, rmax, 100000)
y=np.ones([4, x.size])
y[0, :] = np.linspace(np.pi, 0, x.size)
y[1, :] = np.linspace(-0.2, 0, x.size)
y[2, :] = np.linspace(1, 1, x.size)
y[3] = np.ones(x.size)
sol = solve_bvp(F, bc, x, y, tol=1e-8, max_nodes=100000, verbose=2)
if sol.success:
print("Solution converged")
else:
print("Solution did not converge")
print("Residuals:", sol.message)
print("Residuals max:", np.max(sol.rms_residuals))
print(sol)
#Plot the solutions
plt.figure(figsize=(14, 10))
plt.plot(sol.x, sol.y[0], label='f(r)')
#plt.plot(sol.x, sol.y[1], label='V(r)')
plt.plot(sol.x, sol.y[2], label='N(r)')
plt.xlabel('r')
plt.grid()
plt.legend()
plt.show()
Когда я используюsolve_bvp, я получаю следующую ошибку
Iteration Max residual Max BC residual Total nodes Nodes added
1 1.06e+00 3.41e-21 100000 (199998)
Number of nodes is exceeded after iteration 1.
Maximum relative residual: 1.06e+00
Maximum boundary residual: 3.41e-21
Solution did not converge
Residuals: The maximum number of mesh nodes is exceeded.
Residuals max: 1.0570652692628755
message: 'The maximum number of mesh nodes is exceeded.'
niter: 1
p: None
rms_residuals: array([4.25858920e-01, 2.54417888e-01, 1.98041640e-01, ...,
6.12622641e-05, 6.11022248e-05, 6.09430998e-05])
sol: <scipy.interpolate._interpolate.PPoly object at 0x000001B21E651DF0>
status: 1
success: False
График с использованиемsolve_bvp лучше, но статус успеха ложный.
Да, это краевая задача. Я также пытался решить с помощьюsolve_bvp, но получаю следующую ошибку: количество узлов превышено после итерации 1. Максимальный относительный остаток: 1.06e+00 Максимальный граничный остаток: 3.41e-21 Решение не сходится, сообщение: «Максимальное число» узлов сетки превышено.' niter: 1 p: Нет успех: False (как вы можете видеть, статус успеха не соответствует действительности). В пост я добавил код, используяsolve_bvp.
Ваша проблема в том, что у вас особые границы. Задача состоит в численном анализе, чтобы определить граничные условия для небольшой положительной и конечной границы с продолжениями, которые являются хорошими аппроксимациями точных решений. Иногда жестокий подход работает, но чаще всего он приводит к проблемам сходимости численного метода. Таким образом, ваш вопрос лучше подходит для scicomp.SE и, возможно, math.SE.
Если вы добавите дополнительное условие для N (пункт номер 4), то у вас будет слишком много граничных условий.
@LutzLehmann Спасибо. Я попробую жестокий подход и только что разместил вопросы на Scicomp.StackExchange.
@lastchance Спасибо. Дополнительное условие на N на внешней границе (т.е. бесконечности) не является необходимым, это условие можно игнорировать.
@Prosenjit Пол, пара предложений. Во-первых, вы можете использовать разложение в ряд Тейлора, чтобы отодвинуть начальную точку от кажущейся сингулярности r = 0. Многие из терминов «сингулярности» на самом деле имеют неоднозначную форму 0/0, с которой иногда можно справиться, например, с помощью правила Лопиталя. Во-вторых, тщательная перестановка предполагает, что вы ПОЧТИ можете удалить коэффициент 1+2.sin^2(f)/r^2 из большинства уравнений. Возможно, это даст вам замену переменных. В любом случае, есть ли у вас ссылка, откуда возникла эта проблема?
@lastchance Какой тип перестановки или замены переменных? Это проблема самогравитирующих скирмионов. Вот ссылка на статью: 1. Название статьи: Гравитирующие скирмионы, имя автора: П. Бизон, Ссылка: [sciencedirect.com/science/article/abs/pii/037026939291069L] . 2. Это обзорная статья, см. раздел 7.4, Название статьи: Гравитирующие неабелевы солитоны и черные дыры с полями Янга-Миллса, имя автора: Михаил С. Волков и Дмитрий В. Гальцов, Ссылка: [ sciencedirect.com/science/article/abs/pii/S0370157399000101]
@Prosenjit Пол, в вашем методе съемки есть ошибка в порядке возвращаемых вами производных. return [dfdr, dNdr, dsigdr, dvdr]
должно быть return [dfdr, dvdr, dNdr, dsigdr]
. Я знаю, что это, вероятно, будет раздражающим предложением, но вы абсолютно уверены, что (а) что предполагаемые вами уравнения являются правильными (вы используете совсем другие обозначения, чем в статье, на которую вы ссылаетесь) и (б) что вы их правильно закодировали (ваша производная функция сильно заключена в скобки).
Я имею в виду ваш «стрелковый» код.
У вас есть серьезная числовая ошибка в процедуре производной, где return [dfdr, dNdr, dsigdr, dvdr]
— неправильный порядок. Порядок возврата должен быть
return [dfdr, dvdr, dNdr, dsigdr]
У вас есть ошибка в уравнении (1), где во второй строке не хватает коэффициента 2 при умножении sin(2f)
. Что касается второй ссылки, которую вы цитируете, это происходит от дифференцирования 2.sin^2(f)
, что дает 4.sin(f)cos(f).df/dr
или 2.sin(2f).df/dr
.
Всю вашу процедуру производной трудно согласовать с исходными уравнениями, поэтому я взял на себя смелость переписать ее ниже.
Вы правильно сдвинули нижнюю границу от r=0
. Однако вы можете использовать разложение в ряд Тейлора/степенной ряд, чтобы улучшить значения rmin
в этом случае: f(rmin)=pi+(df/dr)*rmin
.
Я считаю, что вам следует увеличить rmax
(скажем, до 10), тогда как с помощью Рунге-Кутты вы легко можете увеличить h
как минимум в 10 раз, чтобы ускорить получение результатов.
Полный код ниже. Обратите внимание, что я вернулся к значению alpha=0.04
, чтобы вы могли напрямую сравнить его с ожидаемым графиком. (Я также разделил f
на pi
на картинке, как это сделано там.)
Окончательный код:
import math
import numpy as np
import matplotlib.pyplot as plt
from scipy.optimize import fsolve
alpha = 0.04
def F(r, f, v, N, sig):
S = math.sin(f)
S2 = math.sin(2 * f)
dfdr = v
dNdr = -alpha*N*(r**2+2*S**2)*v**2/r - alpha*S**2*(S**2+2*r**2)/r**3 - (N-1)/r
dsigdr = alpha*sig*v**2*(r**2+2*S**2)/r
dvdr = ( (dNdr/N + dsigdr/sig)*(1+2*S**2/r**2) + 2/r + 2*S2*v/r**2 ) * v -(S2/N/r**2)*(1+N*v**2+S**2/r**2)
dvdr = -dvdr/(1+2*S**2/r**2)
return [dfdr, dvdr, dNdr, dsigdr]
def rk4(F, r_range, f0, v0, N0, s0, h):
r_values = np.arange(r_range[0], r_range[1], h)
f_values = np.linspace(f0, 0, len(r_values))
v_values = np.linspace(v0, 0, len(r_values))
N_values = np.linspace(N0, 1, len(r_values))
S_values = np.linspace(s0, 1, len(r_values))
for i in range(1, len(r_values)):
r = r_values[i-1]
f = f_values[i-1]
v = v_values[i-1]
N = N_values[i-1]
sig = S_values[i-1]
k1f, k1v, k1N, k1S = F(r, f, v, N, sig)
k2f, k2v, k2N, k2S = F(r + 0.5*h, f + 0.5*k1f*h, v + 0.5*k1v*h, N + 0.5*k1N*h, sig +0.5*k1S*h)
k3f, k3v, k3N, k3S = F(r + 0.5*h, f + 0.5*k2f*h, v + 0.5*k2v*h, N + 0.5*k2N*h, sig +0.5*k2S*h)
k4f, k4v, k4N, k4S = F(r + h, f + k3f*h, v + k3v*h, N + k3N*h, sig + k3S*h)
f_values[i] = f + ((k1f + 2*k2f + 2*k3f + k4f)*h) / 6
v_values[i] = v + ((k1v + 2*k2v + 2*k3v + k4v)*h) / 6
N_values[i] = N + ((k1N + 2*k2N + 2*k3N + k4N)*h) / 6
S_values[i] = sig + ((k1S + 2*k2S + 2*k3S + k4S)*h)/ 6
return r_values, f_values, v_values, N_values, S_values
rmin = 0.0001
rmax = 10
r_range=[rmin, rmax]
h = 0.0001
def objective(u):
v0, s0 = u
r_values, f_values, v_values, N_values, S_values = rk4(F, r_range, np.pi + v0 * rmin, v0, 1, s0, h)
y1 = f_values[-1]
y2 = S_values[-1]
print(f"Objective results: y1 = {y1}, y2 = {y2}")
return [y1 , y2 - 1]
initial_guess=[-2.794, 0.471 ]
v1 = fsolve(objective, initial_guess, xtol=1e-8)
print("The initial values of v0 and sig0 given by fsolve:", v1[0], " and ", v1[1])
r_values, f_values, v_values, N_values, S_values = rk4(F, [rmin, rmax], np.pi + v1[0] * rmin, v1[0], 1, v1[1], h)
y3 = f_values[-1]
y4 = S_values[-1]
print("The value of f and sig at outer boundary is:", y3, y4)
if abs(y3) + abs( y4 - 1 ) < 1e-6:
print("Boundary condition f(rmax) = 0 is satisfied, and the value of f at outer boundary is:", y3)
print("Boundary condition sig(rmax) = 0 is satisfied, and the value of sig at outer boundary is:", y4)
else:
print("The solutions did not converge")
plt.figure(figsize=(8, 6))
plt.plot(r_values, f_values/np.pi, label='f(r)/pi')
plt.plot(r_values, N_values, label='N(r)')
plt.title(r"$\alpha=0.04$")
plt.xlabel('r')
plt.grid()
plt.legend()
plt.show()
Выход:
Objective results: y1=0.00019542960351076646, y2=0.9993130868376403
Objective results: y1=0.00019542960351076646, y2=0.9993130868376403
Objective results: y1=0.00019542960351076646, y2=0.9993130868376403
Objective results: y1=0.0001954504980834034, y2=0.9993130650610034
Objective results: y1=0.00019542960351076646, y2=0.9993131017285548
Objective results: y1=2.1493424939744863e-07, y2=1.000000127532956
Objective results: y1=5.273386299353826e-10, y2=1.0000000001353808
Objective results: y1=7.569440649813437e-13, y2=0.9999999999992873
The initial values of v0 and sig0 given by fsolve: -2.7943898358693358 and 0.47122759362590755
The value of f and sig at outer boundary is: 7.569440649813437e-13 0.9999999999992873
Boundary condition f(rmax) = 0 is satisfied, and the value of f at outer boundary is: 7.569440649813437e-13
Boundary condition sig(rmax) = 0 is satisfied, and the value of sig at outer boundary is: 0.9999999999992873
Спасибо за вашу помощь, все работает отлично. Я отметил ваш ответ как принятый.
Если это краевая задача, почему бы вам не использоватьsolve_bvp?