【数学建模之Python】13.手撕抛物型方程的差分解法(如一维热传导方程)

能不能别白嫖啊٩(๑❛ᴗ❛๑)۶

目录

一、前言

二、问题与定义

1.问题

 2.定义

三、求解格式

 1、向前欧拉格式(古典显格式)

(1)差分格式

(2)收敛性与稳定性 

2.向后欧拉格式(古典隐格式)

 (1)差分格式

(2)收敛性与稳定性

3.Crank-Nicolson格式

(1)差分格式

 (2)收敛性与稳定性

四、追赶法求解三对角线性方程组

五、实例+代码

1.题目1(《偏微分方程数值解法——孙志忠》书中题目)

2.题目2

(1)理论分析

(2)代码

(3)运行结果与分析

3.特殊的例子:题目3

(1)代码

(2)运行结果与分析


一、前言

1.本人在做建模比赛2020年A题时遇到热传导方程,然后我学习并总结了用有限差分法求解此类偏微分方程(抛物型方程)的解法。

2.本文借鉴《偏微分方程数值解法——孙志忠》,这本书写得浅显易懂。

3.本文不讲复杂的推理过程,只讲方法与结果。

4.相较其他博客而言(绝大多数都是用matlab),我的特点使用Python实现程序的

5.如有问题可在评论区讨论或是私信我

二、问题与定义

1.问题

考虑一维非齐次热传导方程的定解问题

 2.定义

对x轴[0,1]区间作m等分,将t轴[0,T]区间作n等分,并记h=1/m,τ=T/n,分别称h和τ为空间步长和时间步长。

网格比

记 

 网格剖分如下所示

定义网格函数 

其中

 的近似

三、求解格式

 1、向前欧拉格式(古典显格式)

(1)差分格式

 可以看出k+1层结点可由第k层得出,因此可以简单循环即可

圆圈是指用到的点,画×是指差分格式是在这点列出的

(2)收敛性与稳定性 

 当r<=1/2时是收敛与稳定的,但是当r>1/2时是不稳定的,也不一定收敛。

2.向后欧拉格式(古典隐格式)

 (1)差分格式

 可以看出第k层的结点由k+1层结点得出,不能直接迭代求解,必须解方程组

将如上差分格式写为矩阵形式(注意不写的地方为0)

​​​​​​​

 

 只需要在每一个时间层解这个三对角线性方程组即可,用AX=B代替以上方程组以方便叙述

有两种方法:第一种是直接将方程组两端乘上A逆即可得到X;第二种是用三对角线性方程组的特殊求解方法:追赶法来求解,当矩阵很大的时候速度比求逆矩阵要快得多,方法并不难,我放在最后

(2)收敛性与稳定性

对于任意步长比r必收敛与稳定

3.Crank-Nicolson格式

(1)差分格式

 同样是在每一个时间层解一个三对角线性方程组即可

 (2)收敛性与稳定性

对于任意步长比r必收敛与稳定

四、追赶法求解三对角线性方程组

追赶法

五、实例+代码

以下题目的结果均保存至excel中方便观察

1.题目1(《偏微分方程数值解法——孙志忠》书中题目)

时间步长与空间步长均取0.1

 代码:

import numpy as np
import pandas as pd
import datetime

start_time = datetime.datetime.now()

np.set_printoptions(suppress=True)


def left_boundary(t):  # 左边值
    return np.exp(t)


def right_boundary(t):  # 右边值
    return np.exp(t + 1)


def initial_T(x_max, t_max, delta_x, delta_t, m, n):  # 给温度T初始化
    T = np.zeros((n + 1, m + 1))
    for i in range(m + 1):  # 初值
        T[0, i] = np.exp(i * delta_x)

    for i in range(1, n + 1):  # 注意不包括T[0,0]与T[0,-1]
        T[i, 0] = left_boundary(i * delta_t)  # 左边值
        T[i, -1] = right_boundary(i * delta_t)  # 右边值
    return T


# 一、古典显格式
def one_dimensional_heat_conduction1(T, m, n, r):
    # 可以发现当r>=0.5时就发散了
    for k in range(1, n + 1):  # 时间层
        for i in range(1, m):  # 空间层
            T[k, i] = (1 - 2 * r) * T[k - 1, i] + r * (T[k - 1, i - 1] + T[k - 1, i + 1])
    return T.round(6)


# 二、古典隐格式(乘逆矩阵法)
def one_dimensional_heat_conduction2(T, m, n, r):
    A = np.eye(m - 1, k=0) * (1 + 2 * r) + np.eye(m - 1, k=1) * (-r) + np.eye(m - 1, k=-1) * (-r)
    a = np.ones(m - 1) * (-r)
    a[0] = 0
    b = np.ones(m - 1) * (1 + 2 * r)
    c = np.ones(m - 1) * (-r)
    c[-1] = 0

    F = np.zeros(m - 1)  # m-1个元素,索引0~(m-2)
    for k in range(1, n + 1):  # 时间层range(1, n + 1)
        F[0] = T[k - 1, 1] + r * T[k, 0]
        F[-1] = T[k - 1, m - 1] + r * T[k, m]
        for i in range(1, m - 2):  # 空间层
            F[i] = T[k - 1, i + 1]  # 给F赋值
        for i in range(1, m - 1):
            T[k, 1:-1] = np.linalg.inv(A) @ F  # 左乘A逆
    return T.round(6)


# 三、古典隐格式(追赶法)
def one_dimensional_heat_conduction3(T, m, n, r):
    a = np.ones(m - 1) * (-r)
    a[0] = 0
    b = np.ones(m - 1) * (1 + 2 * r)
    c = np.ones(m - 1) * (-r)
    c[-1] = 0

    F = np.zeros(m - 1)  # m-1个元素,索引0~(m-2)
    for k in range(1, n + 1):  # 时间层range(1, n + 1)
        F[0] = T[k - 1, 1] + r * T[k, 0]
        F[-1] = T[k - 1, m - 1] + r * T[k, m]
        y = np.zeros(m - 1)
        beta = np.zeros(m - 1)
        x = np.zeros(m - 1)
        y[0] = F[0] / b[0]
        d = b[0]
        for i in range(1, m - 2):  # 空间层
            F[i] = T[k - 1, i + 1]  # 给F赋值
        for i in range(1, m - 1):
            beta[i - 1] = c[i - 1] / d
            d = b[i] - a[i] * beta[i - 1]
            y[i] = (F[i] - a[i] * y[i - 1]) / d
        x[-1] = y[-1]
        for i in range(m - 3, -1, -1):
            x[i] = y[i] - beta[i] * x[i + 1]
        T[k, 1:-1] = x
    return T.round(6)


# 四、Crank-Nicolson(乘逆矩阵法)
def one_dimensional_heat_conduction4(T, m, n, r):
    A = np.eye(m - 1, k=0) * (1 + r) + np.eye(m - 1, k=1) * (-r * 0.5) + np.eye(m - 1, k=-1) * (-r * 0.5)
    C = np.eye(m - 1, k=0) * (1 - r) + np.eye(m - 1, k=1) * (0.5 * r) + np.eye(m - 1, k=-1) * (0.5 * r)

    for k in range(0, n):  # 时间层
        F = np.zeros(m - 1)  # m-1个元素,索引0~(m-2)
        F[0] = r / 2 * (T[k, 0] + T[k + 1, 0])
        F[-1] = r / 2 * (T[k, m] + T[k + 1, m])
        F = C @ T[k, 1:m] + F
        T[k + 1, 1:-1] = np.linalg.inv(A) @ F
    return T.round(6)


# 五、Crank-Nicolson(追赶法)
def one_dimensional_heat_conduction5(T, m, n, r):
    C = np.eye(m - 1, k=0) * (1 - r) + np.eye(m - 1, k=1) * (0.5 * r) + np.eye(m - 1, k=-1) * (0.5 * r)
    a = np.ones(m - 1) * (-0.5 * r)
    a[0] = 0
    b = np.ones(m - 1) * (1 + r)
    c = np.ones(m - 1) * (-0.5 * r)
    c[-1] = 0

    for k in range(0, n):  # 时间层
        F = np.zeros(m - 1)  # m-1个元素,索引0~(m-2)
        F[0] = r * 0.5 * (T[k, 0] + T[k + 1, 0])
        F[-1] = r * 0.5 * (T[k, m] + T[k + 1, m])
        F = C @ T[k, 1:m] + F
        y = np.zeros(m - 1)
        beta = np.zeros(m - 1)
        x = np.zeros(m - 1)
        y[0] = F[0] / b[0]
        d = b[0]
        for i in range(1, m - 1):
            beta[i - 1] = c[i - 1] / d
            d = b[i] - a[i] * beta[i - 1]
            y[i] = (F[i] - a[i] * y[i - 1]) / d
        x[-1] = y[-1]
        for i in range(m - 3, -1, -1):
            x[i] = y[i] - beta[i] * x[i + 1]
        T[k + 1, 1:-1] = x
    return T.round(6)


def exact_solution(T, m, n, r, delta_x, delta_t):  # 偏微分方程精确解
    for i in range(n + 1):
        for j in range(m + 1):
            T[i, j] = np.exp(i * delta_t + j * delta_x)
    return T.round(6)


a = 1  # 热传导系数
x_max = 1
t_max = 1
delta_x = 0.1  # 空间步长
delta_t = 0.1  # 时间步长
m = int((x_max / delta_x).__round__(4))  # 长度等分成m份
n = int((t_max / delta_t).__round__(4))  # 时间等分成n份
t_grid = np.arange(0, t_max + delta_t, delta_t)  # 时间网格
x_grid = np.arange(0, x_max + delta_x, delta_x)  # 位置网格
r = (a * delta_t / (delta_x ** 2)).__round__(6)  # 网格比
T = initial_T(x_max, t_max, delta_x, delta_t, m, n)
print('长度等分成{}份'.format(m))
print('时间等分成{}份'.format(n))
print('网格比=', r)

p = pd.ExcelWriter('有限差分法-一维热传导-题目1.xlsx')

T1 = one_dimensional_heat_conduction1(T, m, n, r)
T1 = pd.DataFrame(T1, columns=x_grid, index=t_grid)  # colums是列号,index是行号
T1.to_excel(p, '古典显格式')

T2 = one_dimensional_heat_conduction2(T, m, n, r)
T2 = pd.DataFrame(T2, columns=x_grid, index=t_grid)  # colums是列号,index是行号
T2.to_excel(p, '古典隐格式(乘逆矩阵法)')

T3 = one_dimensional_heat_conduction3(T, m, n, r)
T3 = pd.DataFrame(T3, columns=x_grid, index=t_grid)  # colums是列号,index是行号
T3.to_excel(p, '古典隐格式(追赶法)')

T4 = one_dimensional_heat_conduction4(T, m, n, r)
T4 = pd.DataFrame(T4, columns=x_grid, index=t_grid)  # colums是列号,index是行号
T4.to_excel(p, 'Crank-Nicolson格式(乘逆矩阵法)')

T5 = one_dimensional_heat_conduction5(T, m, n, r)
T5 = pd.DataFrame(T5, columns=x_grid, index=t_grid)  # colums是列号,index是行号
T5.to_excel(p, 'Crank-Nicolson格式(追赶法)')

T6 = exact_solution(T, m, n, r, delta_x, delta_t)
T6 = pd.DataFrame(T6, columns=x_grid, index=t_grid)  # colums是列号,index是行号
T6.to_excel(p, '偏微分方程精确解')

p.save()

end_time = datetime.datetime.now()
print('运行时间为', (end_time - start_time))

书中Crank-Nicolson的数值解

 可以看出与我的程序答案完全一致。

其余格式的结果与我的程序答案都是一样的。

结果分析大家就自己做了,我就不赘述了。

2.题目2

(1)理论分析

由生活实际知,温度不会无限制增大,当时间趋于一定的值时,温度将会稳定。

而由一维热传导方程知,

温度与距离将会呈线性关系,若以杆最左端为x=0则有:y=175+4x(x=5时y=195)

 在这个题目中,f(x,t)=0,差分格式用矩阵比较容易表达与求解

如果f(x,t)≠0,那么可以自己在程序中修改矩阵的值

(2)代码

import numpy as np
import pandas as pd
import datetime

start_time = datetime.datetime.now()

np.set_printoptions(suppress=True)


def left_boundary(t):  # 左边值
    T = 25 + 15 * t
    if T < 175:
        return T
    else:
        return 175


def right_boundary(t):  # 右边值
    T = 25 + 17 * t
    if T < 195:
        return T
    else:
        return 195


def initial_T(x_max, t_max, delta_x, delta_t, m, n):  # 给温度T初始化
    T = np.zeros((n + 1, m + 1))
    for i in range(m + 1):  # 初值
        T[0, i] = 25

    for i in range(1, n + 1):  # 注意不包括T[0,0]与T[0,-1]
        T[i, 0] = left_boundary(i * delta_t)  # 左边值
        T[i, -1] = right_boundary(i * delta_t)  # 右边值
    return T


# 一、古典显格式
def one_dimensional_heat_conduction1(T, m, n, r):
    # 可以发现当r>=0.5时就发散了
    for k in range(1, n + 1):  # 时间层
        for i in range(1, m):  # 空间层
            T[k, i] = (1 - 2 * r) * T[k - 1, i] + r * (T[k - 1, i - 1] + T[k - 1, i + 1])
    return T.round(6)


# 二、古典隐格式(乘逆矩阵法)
def one_dimensional_heat_conduction2(T, m, n, r):
    A = np.eye(m - 1, k=0) * (1 + 2 * r) + np.eye(m - 1, k=1) * (-r) + np.eye(m - 1, k=-1) * (-r)
    a = np.ones(m - 1) * (-r)
    a[0] = 0
    b = np.ones(m - 1) * (1 + 2 * r)
    c = np.ones(m - 1) * (-r)
    c[-1] = 0

    F = np.zeros(m - 1)  # m-1个元素,索引0~(m-2)
    for k in range(1, n + 1):  # 时间层range(1, n + 1)
        F[0] = T[k - 1, 1] + r * T[k, 0]
        F[-1] = T[k - 1, m - 1] + r * T[k, m]
        for i in range(1, m - 2):  # 空间层
            F[i] = T[k - 1, i + 1]  # 给F赋值
        for i in range(1, m - 1):
            T[k, 1:-1] = np.linalg.inv(A) @ F  # 左乘A逆
    return T.round(6)


# 三、古典隐格式(追赶法)
def one_dimensional_heat_conduction3(T, m, n, r):
    a = np.ones(m - 1) * (-r)
    a[0] = 0
    b = np.ones(m - 1) * (1 + 2 * r)
    c = np.ones(m - 1) * (-r)
    c[-1] = 0

    F = np.zeros(m - 1)  # m-1个元素,索引0~(m-2)
    for k in range(1, n + 1):  # 时间层range(1, n + 1)
        F[0] = T[k - 1, 1] + r * T[k, 0]
        F[-1] = T[k - 1, m - 1] + r * T[k, m]
        y = np.zeros(m - 1)
        beta = np.zeros(m - 1)
        x = np.zeros(m - 1)
        y[0] = F[0] / b[0]
        d = b[0]
        for i in range(1, m - 2):  # 空间层
            F[i] = T[k - 1, i + 1]  # 给F赋值
        for i in range(1, m - 1):
            beta[i - 1] = c[i - 1] / d
            d = b[i] - a[i] * beta[i - 1]
            y[i] = (F[i] - a[i] * y[i - 1]) / d
        x[-1] = y[-1]
        for i in range(m - 3, -1, -1):
            x[i] = y[i] - beta[i] * x[i + 1]
        T[k, 1:-1] = x
    return T.round(6)


# 四、Crank-Nicolson(乘逆矩阵法)
def one_dimensional_heat_conduction4(T, m, n, r):
    A = np.eye(m - 1, k=0) * (1 + r) + np.eye(m - 1, k=1) * (-r * 0.5) + np.eye(m - 1, k=-1) * (-r * 0.5)
    C = np.eye(m - 1, k=0) * (1 - r) + np.eye(m - 1, k=1) * (0.5 * r) + np.eye(m - 1, k=-1) * (0.5 * r)

    for k in range(0, n):  # 时间层
        F = np.zeros(m - 1)  # m-1个元素,索引0~(m-2)
        F[0] = r / 2 * (T[k, 0] + T[k + 1, 0])
        F[-1] = r / 2 * (T[k, m] + T[k + 1, m])
        F = C @ T[k, 1:m] + F
        T[k + 1, 1:-1] = np.linalg.inv(A) @ F
    return T.round(6)


# 五、Crank-Nicolson(追赶法)
def one_dimensional_heat_conduction5(T, m, n, r):
    C = np.eye(m - 1, k=0) * (1 - r) + np.eye(m - 1, k=1) * (0.5 * r) + np.eye(m - 1, k=-1) * (0.5 * r)
    a = np.ones(m - 1) * (-0.5 * r)
    a[0] = 0
    b = np.ones(m - 1) * (1 + r)
    c = np.ones(m - 1) * (-0.5 * r)
    c[-1] = 0

    for k in range(0, n):  # 时间层
        F = np.zeros(m - 1)  # m-1个元素,索引0~(m-2)
        F[0] = r * 0.5 * (T[k, 0] + T[k + 1, 0])
        F[-1] = r * 0.5 * (T[k, m] + T[k + 1, m])
        F = C @ T[k, 1:m] + F
        y = np.zeros(m - 1)
        beta = np.zeros(m - 1)
        x = np.zeros(m - 1)
        y[0] = F[0] / b[0]
        d = b[0]
        for i in range(1, m - 1):
            beta[i - 1] = c[i - 1] / d
            d = b[i] - a[i] * beta[i - 1]
            y[i] = (F[i] - a[i] * y[i - 1]) / d
        x[-1] = y[-1]
        for i in range(m - 3, -1, -1):
            x[i] = y[i] - beta[i] * x[i + 1]
        T[k + 1, 1:-1] = x
    return T.round(6)


a = 0.5  # 热传导系数
x_max = 5
t_max = 100
delta_x = 0.1  # 空间步长
delta_t = 0.1  # 时间步长
m = int((x_max / delta_x).__round__(4))  # 长度等分成m份
n = int((t_max / delta_t).__round__(4))  # 时间等分成n份
t_grid = np.arange(0, t_max + delta_t, delta_t)  # 时间网格
x_grid = np.arange(0, x_max + delta_x, delta_x)  # 位置网格
r = (a * delta_t / (delta_x ** 2)).__round__(6)  # 网格比
T = initial_T(x_max, t_max, delta_x, delta_t, m, n)
print('长度等分成{}份'.format(m))
print('时间等分成{}份'.format(n))
print('网格比=', r)

p = pd.ExcelWriter('有限差分法-一维热传导-题目2.xlsx')

T1 = one_dimensional_heat_conduction1(T, m, n, r)
T1 = pd.DataFrame(T1, columns=x_grid, index=t_grid)  # colums是列号,index是行号
T1.to_excel(p, '古典显格式')

T2 = one_dimensional_heat_conduction2(T, m, n, r)
T2 = pd.DataFrame(T2, columns=x_grid, index=t_grid)  # colums是列号,index是行号
T2.to_excel(p, '古典隐格式(乘逆矩阵法)')

T3 = one_dimensional_heat_conduction3(T, m, n, r)
T3 = pd.DataFrame(T3, columns=x_grid, index=t_grid)  # colums是列号,index是行号
T3.to_excel(p, '古典隐格式(追赶法)')

T4 = one_dimensional_heat_conduction4(T, m, n, r)
T4 = pd.DataFrame(T4, columns=x_grid, index=t_grid)  # colums是列号,index是行号
T4.to_excel(p, 'Crank-Nicolson格式(乘逆矩阵法)')

T5 = one_dimensional_heat_conduction5(T, m, n, r)
T5 = pd.DataFrame(T5, columns=x_grid, index=t_grid)  # colums是列号,index是行号
T5.to_excel(p, 'Crank-Nicolson格式(追赶法)')

p.save()

end_time = datetime.datetime.now()
print('运行时间为', (end_time - start_time))

(3)运行结果与分析

长度等分成50份
时间等分成1000份
网格比= 5.0
C:/Users/86189/Desktop/Technology/Python_File/数学建模/自己总结/有限差分法-一维热传导-题目1.py:43: RuntimeWarning: overflow encountered in double_scalars
  T[k, i] = (1 - 2 * r) * T[k - 1, i] + r * (T[k - 1, i - 1] + T[k - 1, i + 1])
C:/Users/86189/Desktop/Technology/Python_File/数学建模/自己总结/有限差分法-一维热传导-题目1.py:44: RuntimeWarning: overflow encountered in multiply
  return T.round(6)
运行时间为 0:00:06.094756

可以发现显格式已经不收敛了,其余格式都收敛;

最终的温度都稳定为y=175+4x,符合理论分析。

3.特殊的例子:题目3

 这里仅仅是修改了边值条件,但是情况却大有不同了!

(1)代码

注:结果会存到“有限差分法-一维热传导.xlsx”中,方便大家观察

import numpy as np
import pandas as pd
import datetime

start_time = datetime.datetime.now()

np.set_printoptions(suppress=True)


def left_boundary(t):  # 左边
    return 175


def right_boundary(t):  # 右边值
    return 195


def initial_T(x_max, t_max, delta_x, delta_t, m, n):  # 给温度T初始化
    T = np.zeros((n + 1, m + 1))
    for i in range(m + 1):  # 初值
        T[0, i] = 25

    for i in range(1, n + 1):  # 注意不包括T[0,0]与T[0,-1]
        T[i, 0] = left_boundary(i * delta_t)  # 左边值
        T[i, -1] = right_boundary(i * delta_t)  # 右边值
    return T


# 一、古典显格式
def one_dimensional_heat_conduction1(T, m, n, r):
    # 可以发现当r>=0.5时就发散了
    for k in range(1, n + 1):  # 时间层
        for i in range(1, m):  # 空间层
            T[k, i] = (1 - 2 * r) * T[k - 1, i] + r * (T[k - 1, i - 1] + T[k - 1, i + 1])
    return T.round(6)


# 二、古典隐格式(乘逆矩阵法)
def one_dimensional_heat_conduction2(T, m, n, r):
    A = np.eye(m - 1, k=0) * (1 + 2 * r) + np.eye(m - 1, k=1) * (-r) + np.eye(m - 1, k=-1) * (-r)
    a = np.ones(m - 1) * (-r)
    a[0] = 0
    b = np.ones(m - 1) * (1 + 2 * r)
    c = np.ones(m - 1) * (-r)
    c[-1] = 0

    F = np.zeros(m - 1)  # m-1个元素,索引0~(m-2)
    for k in range(1, n + 1):  # 时间层range(1, n + 1)
        F[0] = T[k - 1, 1] + r * T[k, 0]
        F[-1] = T[k - 1, m - 1] + r * T[k, m]
        for i in range(1, m - 2):  # 空间层
            F[i] = T[k - 1, i + 1]  # 给F赋值
        for i in range(1, m - 1):
            T[k, 1:-1] = np.linalg.inv(A) @ F  # 左乘A逆
    return T.round(6)


# 三、古典隐格式(追赶法)
def one_dimensional_heat_conduction3(T, m, n, r):
    a = np.ones(m - 1) * (-r)
    a[0] = 0
    b = np.ones(m - 1) * (1 + 2 * r)
    c = np.ones(m - 1) * (-r)
    c[-1] = 0

    F = np.zeros(m - 1)  # m-1个元素,索引0~(m-2)
    for k in range(1, n + 1):  # 时间层range(1, n + 1)
        F[0] = T[k - 1, 1] + r * T[k, 0]
        F[-1] = T[k - 1, m - 1] + r * T[k, m]
        y = np.zeros(m - 1)
        beta = np.zeros(m - 1)
        x = np.zeros(m - 1)
        y[0] = F[0] / b[0]
        d = b[0]
        for i in range(1, m - 2):  # 空间层
            F[i] = T[k - 1, i + 1]  # 给F赋值
        for i in range(1, m - 1):
            beta[i - 1] = c[i - 1] / d
            d = b[i] - a[i] * beta[i - 1]
            y[i] = (F[i] - a[i] * y[i - 1]) / d
        x[-1] = y[-1]
        for i in range(m - 3, -1, -1):
            x[i] = y[i] - beta[i] * x[i + 1]
        T[k, 1:-1] = x
    return T.round(6)


# 四、Crank-Nicolson(乘逆矩阵法)
def one_dimensional_heat_conduction4(T, m, n, r):
    A = np.eye(m - 1, k=0) * (1 + r) + np.eye(m - 1, k=1) * (-r * 0.5) + np.eye(m - 1, k=-1) * (-r * 0.5)
    C = np.eye(m - 1, k=0) * (1 - r) + np.eye(m - 1, k=1) * (0.5 * r) + np.eye(m - 1, k=-1) * (0.5 * r)

    for k in range(0, n):  # 时间层
        F = np.zeros(m - 1)  # m-1个元素,索引0~(m-2)
        F[0] = r / 2 * (T[k, 0] + T[k + 1, 0])
        F[-1] = r / 2 * (T[k, m] + T[k + 1, m])
        F = C @ T[k, 1:m] + F
        T[k + 1, 1:-1] = np.linalg.inv(A) @ F
    return T.round(6)


# 五、Crank-Nicolson(追赶法)
def one_dimensional_heat_conduction5(T, m, n, r):
    C = np.eye(m - 1, k=0) * (1 - r) + np.eye(m - 1, k=1) * (0.5 * r) + np.eye(m - 1, k=-1) * (0.5 * r)
    a = np.ones(m - 1) * (-0.5 * r)
    a[0] = 0
    b = np.ones(m - 1) * (1 + r)
    c = np.ones(m - 1) * (-0.5 * r)
    c[-1] = 0

    for k in range(0, n):  # 时间层
        F = np.zeros(m - 1)  # m-1个元素,索引0~(m-2)
        F[0] = r * 0.5 * (T[k, 0] + T[k + 1, 0])
        F[-1] = r * 0.5 * (T[k, m] + T[k + 1, m])
        F = C @ T[k, 1:m] + F
        y = np.zeros(m - 1)
        beta = np.zeros(m - 1)
        x = np.zeros(m - 1)
        y[0] = F[0] / b[0]
        d = b[0]
        for i in range(1, m - 1):
            beta[i - 1] = c[i - 1] / d
            d = b[i] - a[i] * beta[i - 1]
            y[i] = (F[i] - a[i] * y[i - 1]) / d
        x[-1] = y[-1]
        for i in range(m - 3, -1, -1):
            x[i] = y[i] - beta[i] * x[i + 1]
        T[k + 1, 1:-1] = x
    return T.round(6)


a = 0.5  # 热传导系数
x_max = 5
t_max = 100
delta_x = 0.1  # 空间步长
delta_t = 0.1  # 时间步长
m = int((x_max / delta_x).__round__(4))  # 长度等分成m份
n = int((t_max / delta_t).__round__(4))  # 时间等分成n份
t_grid = np.arange(0, t_max + delta_t, delta_t)  # 时间网格
x_grid = np.arange(0, x_max + delta_x, delta_x)  # 位置网格
r = (a * delta_t / (delta_x ** 2)).__round__(6)  # 网格比
T = initial_T(x_max, t_max, delta_x, delta_t, m, n)
print('长度等分成{}份'.format(m))
print('时间等分成{}份'.format(n))
print('网格比=', r)

p = pd.ExcelWriter('有限差分法-一维热传导-题目3.xlsx')

T1 = one_dimensional_heat_conduction1(T, m, n, r)
T1 = pd.DataFrame(T1, columns=x_grid, index=t_grid)  # colums是列号,index是行号
T1.to_excel(p, '古典显格式')

T2 = one_dimensional_heat_conduction2(T, m, n, r)
T2 = pd.DataFrame(T2, columns=x_grid, index=t_grid)  # colums是列号,index是行号
T2.to_excel(p, '古典隐格式(乘逆矩阵法)')

T3 = one_dimensional_heat_conduction3(T, m, n, r)
T3 = pd.DataFrame(T3, columns=x_grid, index=t_grid)  # colums是列号,index是行号
T3.to_excel(p, '古典隐格式(追赶法)')

T4 = one_dimensional_heat_conduction4(T, m, n, r)
T4 = pd.DataFrame(T4, columns=x_grid, index=t_grid)  # colums是列号,index是行号
T4.to_excel(p, 'Crank-Nicolson格式(乘逆矩阵法)')

T5 = one_dimensional_heat_conduction5(T, m, n, r)
T5 = pd.DataFrame(T5, columns=x_grid, index=t_grid)  # colums是列号,index是行号
T5.to_excel(p, 'Crank-Nicolson格式(追赶法)')

p.save()

end_time = datetime.datetime.now()
print('运行时间为', (end_time - start_time))

(2)运行结果与分析

长度等分成50份
时间等分成1000份
网格比= 5.0
C:/Users/86189/Desktop/Technology/Python_File/数学建模/自己总结/有限差分法-一维热传导-题目3.py:35: RuntimeWarning: overflow encountered in double_scalars
  T[k, i] = (1 - 2 * r) * T[k - 1, i] + r * (T[k - 1, i - 1] + T[k - 1, i + 1])
C:/Users/86189/Desktop/Technology/Python_File/数学建模/自己总结/有限差分法-一维热传导-题目3.py:36: RuntimeWarning: overflow encountered in multiply
  return T.round(6)
运行时间为 0:00:05.578520

显格式仍不收敛。

注意!Crank-Nicolson格式虽然最终收敛于y=175+4x,但是可以发现在最初的1秒内出现了数值伪振荡,这是因为我们给定的边值不合理导致的。通俗的讲,试想在生活实际中那能让一根杆在极短时间从25摄氏度跃变至175摄氏度,这合理吗?并不合理,这么大的变化率导致CN格式出现了数值伪振荡。并且只有当网格比稍大的时候才会出现这种情况,网格比小时仍然可用CN格式

  • 46
    点赞
  • 255
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
抛物方程是一类偏微分方程,其数值解法中常用的是差分解法。以下是一种使用matlab实现的抛物方程差分解法: 假设需要求抛物方程为: ∂u/∂t = D(∂^2u/∂x^2) 其中D为常数,u为未知函数,t和x分别为时间和空间变量。 首先对空间和时间进行离散化,即将x和t分别划分为N和M个等距的网格点。设Δx和Δt为网格间隔,则有: x(i) = iΔx (i=0,1,...,N) t(j) = jΔt (j=0,1,...,M) 然后将未知函数u在网格点上的值记为u(i,j),则有: u(i,j) ≈ u(x(i),t(j)) 接下来,使用中心差分法对空间和时间进行近似求导,并代入原方程,得到: (u(i,j+1) - u(i,j))/Δt = D(u(i+1,j) - 2u(i,j) + u(i-1,j))/Δx^2 将上式进行变形,得到: u(i,j+1) = u(i,j) + DΔt/Δx^2 (u(i+1,j) - 2u(i,j) + u(i-1,j)) 以上式子即为差分解法的迭代公式。根据迭代公式,可以依次求出每个时间步长上未知函数u在每个空间点上的值。在matlab中,可以使用循环语句实现迭代计算,具体实现方式可以参考以下代码: % 定义参数和边界条件 D = 1; % 常数D N = 100; % 空间网格点数 M = 1000; % 时间网格点数 L = 1; % 空间区间长度 T = 1; % 时间区间长度 dx = L/N; % 空间网格间隔 dt = T/M; % 时间网格间隔 r = D*dt/dx^2; % 离散化参数 u = zeros(N+1,M+1); % 初始化u % 设置边界条件 u(1,:) = 0; u(N+1,:) = 0; u(:,1) = 1; u(:,M+1) = 0; % 迭代计算 for j = 1:M for i = 2:N u(i,j+1) = u(i,j) + r*(u(i+1,j) - 2*u(i,j) + u(i-1,j)); end end % 绘制图像 [X,T] = meshgrid(0:dx:L,0:dt:T); surf(X,T,u') xlabel('x') ylabel('t') zlabel('u(x,t)') 注意,以上代码中的边界条件和初始条件需要根据具体问题进行设置。另外,差分解法的精度和稳定性还需要根据具体问题进行分析和优化。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

若oo尘

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值