简体   繁体   中英

Solving a boundary value problem (diffusion-reaction equation) with scipy solve_bvp

I'm struggling to solve the following 2nd order boundary value problem:

y'' + 2/x*y' + k**2.0*F(y) = 0

y(x=1)=1,  y'(x=0)=0

F(y) = -y or F(y) = -y*exp(AB*(1-y)/(1+B(1-y))

I somehow fail to set the boundary conditions right. I defined the function for F(y)=y and boundary conditions the following way:

def fun(x, y, p):
 k = p[0]
 return np.vstack((y[1], -2.0/x*y[1] + k**2.0*y[0]))

def bc(ya, yb, p):
     return np.array([ya[0], yb[0],ya[1]])

y[0,:] = 1
y[1,0] = 0
sol = solve_bvp(fun, bc, x, y, p=[40])

The results should I get are definitely wrong, changing the initial conditions doesn't make things better. I think my problem is some how related to the zero gradient boundary condition at x=0. Does anybody know what I'm doing wrong?

EDIT: Here a MWE, which should give a constant value of 1 for k=0.01. but for ak=5, the value at x=0 should be approx. 0.06:

def fun(x, y, p):
     k = p[0]
     return np.vstack((y[1], -2.0/x*y[1] + k**2.0*y[0]))

def bc(ya, yb, p):
     return np.array([ya[0], yb[0]-1.0,yb[1]])

x = np.linspace(1e-3, 1, 100)
y = np.zeros((2, x.size))
y[0,:] = 1

from scipy.integrate import solve_bvp
sol = solve_bvp(fun, bc, x, y, p=[1000])
x_plot = np.linspace(0, 1, 100)
y_plot = sol.sol(x_plot)[0]
plt.figure()
plt.plot(x_plot, y_plot)

Consider the case F(y)=y . Then it is easy to see that the basis solutions of this linear ODE are sin(k*x)/x and cos(kx/x) . Similarly for F(y)=-y one gets sinh(k*x)/x and cosh(k*x)/x . This means that most solutions have a singularity at x=0 . Such a singularity is almost impossible to handle out-of-the-box for standard ODE solvers. One has to help the solver at the singularity, normal procedure applies again at some distance from the singularity.

What you can do is to analyze the situation at x=0 and move away a little bit. You get via limit of difference quotients

y''(0) + 2*y''(0) + k^2*F(y(0)) = 0

which allows you to compute a quadratic Taylor polynomial. Thus solve the problem on [a, 1] with the ODE solver using the continuation to the singularity y(x)=y(0)-k**2/6*F(y(0))*x**2 on [0,a] .

The boundary condition at x=a is easiest to establish with treating y0=y(0) as parameter. The ODE and BC functions then are

def ode(x,y,y0): return [ y[1], -2*y[1]/x - k**2*F(y[0]) ]
def bc(ya,yb,y0): y2 = -k**2*F(y0)/6; return [ ya[0] - y0 - y2*a**2, ya[1] - 2*y2*a, yb[0]-1 ]

In the cases discussed in the question, this gives

a = 1e-2
def F(y): return -y

for k in [0.01, 5]:
    res = solve_bvp(ode, bc, [a,1], [[1,1], [0,0]], p=[1], tol=1e-5)
    print(f'k={k}: {res.message}, y0={res.p[0]}, theory: {k/np.sinh(k)}')
    if res.success:
        y0 = res.p[0]
        x = np.linspace(a,1,61);
        plt.plot(x, res.sol(x)[0])
        plt.plot([0], [y0],'o', res.x, res.y[0],'+', ms=4)
        plt.title(f'k={k}'); plt.grid(); plt.show()

with the result

k=0.01: The algorithm converged to the desired accuracy., y0=0.9999833335277726, theory: 0.9999833335277757

解决方案图

k=5: The algorithm converged to the desired accuracy., y0=0.06738256929427147, theory: 0.06738252915294544

解决方案图

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM