在看到@Mario S.Mommer的建议之前,我遇到了
Finite Difference Method
。
对于热量方程:
ρcp T'(T)=k T'(z)
当alpha=k/(rho-cp),T(z1=0)=T1,T(z 2)=T2时,我们可以在Python中通过调整
online example
对于有限差分法,使用以下代码:
import numpy as np
import matplotlib.pyplot as plt
#assumes that z1 = 0
def find_T_arr(T1, T2, z2, alpha, t_final, dt, n = int(20)):
dz = z2/n
z_arr = np.linspace(dz, z2 - dz, n)
T_guess = (T2 - T1)/2
T_arr = np.ones(n)*T_guess
dTdt_arr = np.empty(n)
t_arr = np.arange(0, t_final + dt, dt)
for j in range(1, len(t_arr)):
for i in range(1, n-1):
dTdt_arr[i] = alpha*((T_arr[i+1]-T_arr[i])/dz**2 -(T_arr[i] - T_arr[i-1])/dz**2)
dTdt_arr[0] = alpha*((T_arr[1]-T_arr[0])/dz**2 -(T_arr[0] - T1)/dz**2)
dTdt_arr[n-1] = alpha*((T2-T_arr[n-1])/dz**2 -(T_arr[n-1] - T_arr[n-2])/dz**2)
T_arr = T_arr + dTdt_arr*dt
z_arr = np.insert(z_arr, 0, 0.) #assumes z1 = 0
z_arr = np.append(z_arr, z2)
T_arr = np.insert(T_arr, 0, T1)
T_arr = np.append(T_arr, T2)
return z_arr, T_arr
#assumes z1 = 0 and at steady-state (e.g., dT/dt = 0)
def get_T_exact(z_arr, T1, T2, z2):
Texact_arr = np.array([])
for z_val in z_arr:
T_val = T1 + ((T2 - T1)/(z2 - 0.))*z_val
Texact_arr = np.append(Texact_arr, T_val)
return Texact_arr
#z1 = 0. #assumed in the functions
z2 = 20.*1000
T1 = 100.
T2 = 1500.
alpha = 3/(1000*2800)
dt = 0.005*(365*24*60*60)*1e6
t_final = 10*(365*24*60*60)*1e6 #could be smaller
z_arr, T_arr = find_T_arr(T1, T2, z2, alpha, t_final, dt, n = int(30))
Texact_arr = get_T_exact(z_arr, T1, T2, z2)
fig, [ax1, ax2] = plt.subplots(nrows = 1, ncols = 2, sharex = False, sharey = False, figsize = (12, 10))
ax1.plot(z_arr/1000., Texact_arr, color = 'blue', linewidth = 2, label = 'Exact')
ax1.plot(z_arr/1000., T_arr, 'o', color = 'red', alpha = 0.95, markersize = 6, markeredgecolor = 'none', label = 'Finite Differences')
ax1.set_xlim(xmin = 0., xmax = z2/1000.)
ax1.set_ylim(ymin = 0., ymax = T1 + T2)
ax1.set_xlabel("Depth [km]", fontsize = 16)
ax1.set_ylabel("Temperature [K]", fontsize = 16)
ax1.hlines(y = T1, xmin = 0., xmax = z2/1000., linestyle = '--', color = 'grey', linewidth = 1)
ax1.hlines(y = T2, xmin = 0., xmax = z2/1000., linestyle = '--', color = 'grey', linewidth = 1)
ax1.legend(fontsize = 12, framealpha = 0.95)
ax2.plot(z_arr[1:-1]/1000., Texact_arr[1:-1] - T_arr[1:-1], color = 'purple', linewidth = 2)
ax2.set_xlim(xmin = 0., xmax = z2/1000.)
ax2.set_ylim(ymin = -2., ymax = 2.)
ax2.set_xlabel("Depth [km]", fontsize = 16)
ax2.set_ylabel("Temperature Residuals [K]", fontsize = 16)
ax2.hlines(y = 0., xmin = 0., xmax = z2/1000., linestyle = '--', color = 'grey', linewidth = 1)
fig.show()
这种方法产生以下结果:
有限差分法可能适用于我修改的热方程,使用这些中心差分近似值:
T'(z_i)~T(z_(i+1))-T(z_
T''(z_i)~(T(z_(i+1))-T(z_i))/dz^2-(T(z _(i))-T
尽管如此,马里奥对射击方法的建议显著提高了精度(与上述有限差分法的实现相比)。
对于这个稳态热方程:
k T''(z)+Q=0
当T(z1=0)=T1和T(z2)=T2边界条件时,我们可以通过调整
this Shooting Method example
,使用此代码:
import numpy as np
import matplotlib.pyplot as plt
from scipy.integrate import solve_ivp
from scipy.optimize import fsolve
def F_func(z, s, Q, k):
F_val = [s[1], -Q/k]
#z is the depth
#s[0] is T(z)
#s[1] is beta(z) = dT(z)/dz
#F_val[1] is T''(z)
return F_val
#assumes z1 = 0
def find_actual_beta0(beta0_val, Q, k, T1, T2, z2, z_eval):
sol = solve_ivp(lambda z,s: F_func(z, s, Q, k), t_span = [0., z2], y0 = [T1, beta0_val], t_eval = z_eval)
T_arr = sol.y[0]
return T_arr[-1] - T2
def find_T_arr(T1, T2, z2, Q, k, n = int(10)):
z_eval = np.linspace(0, z2, n) #assumes z1 = 0
beta0_guess = (T2 - T1/2.)/(z2 - 0.) #assumes z1 = 0
beta0 = fsolve(lambda beta0_val: find_actual_beta0(beta0_val, Q, k, T1, T2, z2, z_eval), x0 = beta0_guess)
sol = solve_ivp(lambda z,s: F_func(z, s, Q, k), t_span = [0, z2], y0 = [T1, beta0], t_eval = z_eval)
z_arr = sol.t
T_arr = sol.y[0]
return z_arr, T_arr
def get_Texact_val(z, z2, k, Q, c1, c2):
#assumes z1 = 0, and 0 <= z <= z2
Texact_val = -(Q*(z**2))/(2*k) + c1 + c2*z
return Texact_val
def get_Texact_arr(z_arr, z2, T1, T2, k, Q):
c1 = T1 #assumes z1 = 0
c2 = (z2*Q)/(2*k) + (T2 - T1)/z2 #assumes z1 = 0
Texact_arr = np.array([])
for z_val in z_arr:
T_val = get_Texact_val(z_val, z2, k, Q, c1, c2)
Texact_arr = np.append(Texact_arr, T_val)
return Texact_arr
#z1 = 0. #assumed in the functions
z2 = 20e3
T1 = 100.
T2 = 1500.
Q = 0. #possible values for the system are 0 <= Q <= 1e-5 W/m^3
k = 3.
BC_z = np.array([0., z2])
BC_T = np.array([T1, T2])
z_arr, T_arr = find_T_arr(T1, T2, z2, Q, k)
Texact_arr = get_Texact_arr(z_arr, z2, T1, T2, k, Q)
fig, [ax1, ax2] = plt.subplots(nrows = 1, ncols = 2, sharex = False, sharey = False, figsize = (12, 10))
ax1.plot(z_arr/1000., Texact_arr, color = 'blue', linewidth = 2, label = 'Exact')
ax1.plot(BC_z/1000., BC_T, 'o', color = 'yellow', alpha = 0.95, markersize = 12, markeredgecolor = 'none', label = 'Boundary Conditions')
ax1.plot(z_arr/1000., T_arr, 'o', color = 'r', alpha = 0.95, markersize = 6, markeredgecolor = 'none', label = 'Shooting Method')
ax1.set_xlabel("Depth [km]", fontsize = 16)
ax1.set_ylabel("Temperature [K]", fontsize = 16)
ax1.set_xlim(xmin = 0., xmax = z2/1000.)
ax1.set_ylim(ymin = 0., ymax = T1 + T2)
ax1.legend(fontsize = 12, framealpha = 0.95)
ax1.hlines(y = T1, xmin = 0., xmax = z2/1000., linestyle = '--', color = 'grey', linewidth = 1)
ax1.hlines(y = T2, xmin = 0., xmax = z2/1000., linestyle = '--', color = 'grey', linewidth = 1)
ax2.plot(z_arr/1000., Texact_arr - T_arr, color = 'purple', linewidth = 2)
ax2.set_xlim(xmin = 0., xmax = z2/1000.)
ax2.set_ylim(ymin = 0., ymax = 2.5e-12)
ax2.set_xlabel("Depth [km]", fontsize = 16)
ax2.set_ylabel("Temperature Residuals [K]", fontsize = 16)
fig.show()
这种方法产生以下结果:
将此Shooting Method Python实现应用于我修改的传导方程:
import numpy as np
from math import erf as err_func
import math
import matplotlib.pyplot as plt
from scipy.integrate import solve_ivp
from scipy.optimize import fsolve
def F_func(z, s, a, b, g, h):
F_val = [s[1], -h/g + (a/g)*s[0] + (a*z/g)*s[1] + (b/g)*s[1]]
#z is the depth
#s[0] is T(z)
#s[1] is beta(z) = dT(z)/dz
#F_val[1] is T''(z)
return F_val
#assumes z1 = 0
def find_actual_beta0(beta0_val, a, b, g, h, T1, T2, z2, z_eval):
sol = solve_ivp(lambda z,s: F_func(z, s, a, b, g, h), t_span = [0., z2], y0 = [T1, beta0_val], t_eval = z_eval, method = 'Radau')
#tried method = 'RK45' and 'LSODA', found that 'Radau' had better accuracy for this problem
T_arr = sol.y[0]
return T_arr[-1] - T2
def find_T_arr(T1, T2, z2, a, b, g, h, n = int(10)):
z_eval = np.linspace(0, z2, n) #assumes z1 = 0
beta0_guess = (T2 - T1/2.)/(z2 - 0.) #assumes z1 = 0
beta0 = fsolve(lambda beta0_val: find_actual_beta0(beta0_val, a, b, g, h, T1, T2, z2, z_eval), x0 = beta0_guess)
sol = solve_ivp(lambda z,s: F_func(z, s, a, b, g, h), t_span = [0, z2], y0 = [T1, beta0], t_eval = z_eval, method = 'Radau')
#tried method = 'RK45' and 'LSODA', found that 'Radau' had better accuracy for this problem
z_arr = sol.t
T_arr = sol.y[0]
return z_arr, T_arr
def get_Texact_val(z, c1, c2, a, b, g, h):
#assumes z1 = 0, and 0 <= z <= z2
Texact_val = c2*np.exp((z*(2*b + a*z))/(2*g)) + h/a + (math.sqrt(math.pi/(2*g*(a**3.))))*(h*b + g*a*c1)*err_func((b + a*z)/(math.sqrt(2*g*a)))*np.exp(((b + a*z)**2)/(2*g*a))
return Texact_val
def get_Texact_arr(z_arr, z2, T1, T2, a, b, g, h):
exp1 = np.exp(((2.*b + a*z2)/(2*g))*z2)
exp2 = np.exp(((b + a*z2)**2)/(2.*g*a))
exp3 = np.exp((b**2)/(2.*g*a))
erf1 = err_func((b + a*z2)/(math.sqrt(2.*g*a)))
erf2 = err_func(b/(math.sqrt(2.*g*a)))
c1 = (a*b*((-erf1)*exp2 + erf2*exp1*exp3)*h + (-1. + exp1)*math.sqrt(a**3*g)*h*math.sqrt(2/math.pi) + a*math.sqrt(a**3*g)*math.sqrt(2/math.pi)*(T2 - exp1*T1))/(a**2*(erf1*exp2 - erf2*exp1*exp3)*g)
c2 = (erf2*exp3*(h - a*T2) + erf1*exp2*(-h + a*T1))/(a*(erf1*exp2 - erf2*exp1*exp3))
Texact_arr = np.array([])
for z_val in z_arr:
T_val = get_Texact_val(z_val, c1, c2, a, b, g, h)
Texact_arr = np.append(Texact_arr, T_val)
return Texact_arr
#z1 = 0. #assumed in the functions
z2 = 20e3
T1 = 100.
T2 = 1500.
a = 7/1.5768e8
b = 7./7884
g = 3.
h = 7./140160
BC_z = np.array([0., z2])
BC_T = np.array([T1, T2])
z_arr, T_arr = find_T_arr(T1, T2, z2, a, b, g, h, n = int(20))
Texact_arr = get_Texact_arr(z_arr, z2, T1, T2, a, b, g, h)
fig, [ax1, ax2] = plt.subplots(nrows = 1, ncols = 2, sharex = False, sharey = False, figsize = (12, 10))
fig.subplots_adjust(wspace = 0.3)
ax1.plot(z_arr/1000., Texact_arr, color = 'blue', linewidth = 2, label = 'Exact')
ax1.plot(BC_z/1000., BC_T, 'o', color = 'yellow', alpha = 0.95, markersize = 12, markeredgecolor = 'none', label = 'Boundary Conditions')
ax1.plot(z_arr/1000., T_arr, 'o', color = 'r', alpha = 0.95, markersize = 6, markeredgecolor = 'none', label = 'Shooting Method')
ax1.set_xlabel("Depth [km]", fontsize = 16)
ax1.set_ylabel("Temperature [K]", fontsize = 16)
ax1.set_xlim(xmin = 0., xmax = z2/1000.)
ax1.set_ylim(ymin = 0., ymax = T1 + T2)
ax1.legend(fontsize = 12, framealpha = 0.95)
ax1.hlines(y = T1, xmin = 0., xmax = z2/1000., linestyle = '--', color = 'grey', linewidth = 1)
ax1.hlines(y = T2, xmin = 0., xmax = z2/1000., linestyle = '--', color = 'grey', linewidth = 1)
ax2.plot(z_arr/1000., Texact_arr - T_arr, color = 'purple', linewidth = 2)
ax2.set_xlim(xmin = 0., xmax = z2/1000.)
ax2.set_ylim(ymin = -0.0125, ymax = 0.0075)
ax2.set_xlabel("Depth [km]", fontsize = 16)
ax2.set_ylabel("Temperature Residuals [K]", fontsize = 16)
ax2.hlines(y = 0., xmin = 0., xmax = z2/1000., linestyle = '--', color = 'grey', linewidth = 1)
fig.show()
这将产生以下内容: