一维浅水方程
cancan123456 · · 个人记录
顾名思义,浅水方程只有在水很“浅”的时候成立,这个假设意味着我们假设水在垂直方向没有速度。
那么我们就要求出两个函数:
众所周知,在搞这种物理模型的时候我们要找守恒量。
物理中有能量守恒、动量守恒、角动量守恒,看上去能量和角动量没什么用,所以只有动量守恒再加上水的质量守恒。
先假设水的密度为
考虑分析
显然,我们需要考察在
先看
再看
所以,我们可以得到:
这个形式被称作积分形式。
常识告诉我们,一般来说微分形式更简单。
所以:
再次分析
众所周知,这次在
先分析
显然,来源一所带来的动量为
考虑求出压力的总和,众所周知压力是压强的积分,且压强的表达式为
所以
所以我们可以得到:
这就是积分形式,通过一通化简我们可以得到微分形式:
这样,我们就得到了两个方程:
微分形式:
积分形式:
然后就是解方程组了,这里我选择有限体积法进行数值解。
首先,假设我们要在
然后,对于每个小区间,我们存储守恒量(在 1D SWE 中就是质量和动量)的平均值,也就是:
代入积分形式的方程组中,可得:
但是有一个小问题:我们不知道
显然,对于
但是在边界处怎么办呢?
这里我使用 Lax-Friedrich 通量,即:
其中
还有就是
有两种边界条件:
-
假设这是一个水箱,也就是说边界可以“反射”水,那么
M_{-1}=M_{0},P_{-1}=-P_0,M_n=M_{n-1},P_n=-P_{n-1} 。 -
假设边界是循环的,也就是说
0 和1 实际上是拼在一起的,那么M_{-1}=M_{n-1},P_{-1}=P_{n-1},M_n=M_0,M_n=P_0 。
现在,我们就有:
其中:
然后我们就要对其进行数值积分。
数值积分的要求是:给定一个带有初值的微分方程:
求数值解。
一个显然的想法是:
这个方法被称作欧拉法。
但是更常用的是四阶 Runge-Kutta 法,又叫 RK4,其公式为:
其中:
下面是欧拉法的代码:
import turtle
from math import sin, cos, pi
n = 100
dx = 1 / n
g = 1
c = 0.2
M = [0.0 for i in range(n)]
P = [0.0 for i in range(n)]
for i in range(n):
if i < n // 2:
M[i] = 2
else:
M[i] = 0.1
'''
for i in range(n):
M[i] = sin(i / n * 1 * pi) / 5 + 0.3
'''
'''
for i in range(n):
M[i] = 0.1 + i / n
'''
'''
for i in range(n):
if i < n // 2:
M[i] = sin(i / n * 2 * pi) / 5 + 0.3
else:
M[i] = 0.3
'''
turtle.tracer(False)
def draw():
turtle.clear()
turtle.penup()
turtle.goto(0, 0)
turtle.pendown()
turtle.pencolor((0, 0, 1))
for i in range(n):
turtle.goto(i / n * 300, M[i] * 100)
turtle.update()
dt = 0.0001
def next_time():
old_M = []
for i in range(n):
old_M.append(M[i])
old_P = []
for i in range(n):
old_P.append(P[i])
for i in range(n):
if i - 1 == -1:
hvi = (-old_P[0] + old_P[i]) / 2 + c * (old_M[0] - old_M[i]) / 2
#hvi = (old_P[n - 1] + old_P[i]) / 2 + c * (old_M[n - 1] - old_M[i]) / 2
else:
hvi = (old_P[i - 1] + old_P[i]) / 2 + c * (old_M[i - 1] - old_M[i]) / 2
if i + 1 == n:
hvi1 = (old_P[i] - old_P[n - 1]) / 2 + c * (old_M[i] - old_M[n - 1]) / 2
#hvi1 = (old_P[i] + old_P[0]) / 2 + c * (old_M[i] - old_M[0]) / 2
else:
hvi1 = (old_P[i] + old_P[i + 1]) / 2 + c * (old_M[i] - old_M[i + 1]) / 2
M[i] += (hvi - hvi1) / dx * dt
if i - 1 == -1:
hv2i = (old_P[0] * old_P[0] / old_M[0] + old_P[i] * old_P[i] / old_M[i]) / 2 + g * (old_M[0] * old_M[0] + old_M[i] * old_M[i]) / 4 + c * (-old_P[0] - old_P[i]) / 2
#hv2i = (old_P[n - 1] * old_P[n - 1] / old_M[n - 1] + old_P[i] * old_P[i] / old_M[i]) / 2 + g * (old_M[n - 1] * old_M[n - 1] + old_M[i] * old_M[i]) / 4 + c * (old_P[n - 1] - old_P[i]) / 2
else:
hv2i = (old_P[i - 1] * old_P[i - 1] / old_M[i - 1] + old_P[i] * old_P[i] / old_M[i]) / 2 + g * (old_M[i - 1] * old_M[i - 1] + old_M[i] * old_M[i]) / 4 + c * (old_P[i - 1] - old_P[i]) / 2
if i + 1 == n:
hv2i1 = (old_P[i] * old_P[i] / old_M[i] + old_P[n - 1] * old_P[n - 1] / old_M[n - 1]) / 2 + g * (old_M[i] * old_M[i] + old_M[n - 1] * old_M[n - 1]) / 4 + c * (old_P[i] + old_P[n - 1]) / 2
#hv2i1 = (old_P[i] * old_P[i] / old_M[i] + old_P[0] * old_P[0] / old_M[0]) / 2 + g * (old_M[i] * old_M[i] + old_M[0] * old_M[0]) / 4 + c * (old_P[i] + old_P[0]) / 2
else:
hv2i1 = (old_P[i] * old_P[i] / old_M[i] + old_P[i + 1] * old_P[i + 1] / old_M[i + 1]) / 2 + g * (old_M[i] * old_M[i] + old_M[i + 1] * old_M[i + 1]) / 4 + c * (old_P[i] - old_P[i + 1]) / 2
P[i] += (hv2i - hv2i1) / dx * dt
while True:
for i in range(100):
next_time()
draw()
RK4 的代码:
import turtle
from time import sleep
from math import sin, cos, pi, exp
n = 100
dx = 1 / n
g = 1
c = 0.2
y = [[0.0 for i in range(n)], [0.0 for i in range(n)]]
# y[0] = M
# y[1] = P
for i in range(n):
y[0][i] = exp(-64 * (i / n - 0.25) * (i / n - 0.25)) * 0.1 + 0.1
'''
for i in range(n):
y[0][i] = 0.1
for i in range(40, 60):
y[0][i] = 1
'''
'''
for i in range(n):
if i < n // 2:
y[0][i] = 2
else:
y[0][i] = 0.1
'''
'''
for i in range(n):
y[0][i] = sin(i / n * 2 * pi) + 1.1
'''
'''
for i in range(n):
y[0][i] = 0.1 + i / n
'''
'''
for i in range(n):
if i < n // 2:
y[0][i] = sin(i / n * 2 * pi) / 5 + 0.3
else:
y[0][i] = 0.3
'''
turtle.tracer(False)
def draw():
turtle.clear()
turtle.penup()
turtle.goto(0, 0)
turtle.pendown()
turtle.pencolor((0, 0, 1))
for i in range(n):
turtle.goto(i / n * 300, y[0][i] * 250)
turtle.update()
dt = 0.01
def dydt():
M = y[0]
P = y[1]
dydt = [[0.0 for i in range(n)], [0.0 for i in range(n)]]
for i in range(n):
if i - 1 == -1:
hvi = (-P[0] + P[i]) / 2 + c * (M[0] - M[i]) / 2
#hvi = (P[n - 1] + P[i]) / 2 + c * (M[n - 1] - M[i]) / 2
else:
hvi = (P[i - 1] + P[i]) / 2 + c * (M[i - 1] - M[i]) / 2
if i + 1 == n:
hvi1 = (P[i] - P[n - 1]) / 2 + c * (M[i] - M[n - 1]) / 2
#hvi1 = (P[i] + P[0]) / 2 + c * (M[i] - M[0]) / 2
else:
hvi1 = (P[i] + P[i + 1]) / 2 + c * (M[i] - M[i + 1]) / 2
dydt[0][i] = (hvi - hvi1) / dx
if i - 1 == -1:
hv2i = (P[0] * P[0] / M[0] + P[i] * P[i] / M[i]) / 2 + g * (M[0] * M[0] + M[i] * M[i]) / 4 + c * (-P[0] - P[i]) / 2
#hv2i = (P[n - 1] * P[n - 1] / M[n - 1] + P[i] * P[i] / M[i]) / 2 + g * (M[n - 1] * M[n - 1] + M[i] * M[i]) / 4 + c * (P[n - 1] - P[i]) / 2
else:
hv2i = (P[i - 1] * P[i - 1] / M[i - 1] + P[i] * P[i] / M[i]) / 2 + g * (M[i - 1] * M[i - 1] + M[i] * M[i]) / 4 + c * (P[i - 1] - P[i]) / 2
if i + 1 == n:
hv2i1 = (P[i] * P[i] / M[i] + P[n - 1] * P[n - 1] / M[n - 1]) / 2 + g * (M[i] * M[i] + M[n - 1] * M[n - 1]) / 4 + c * (P[i] + P[n - 1]) / 2
#hv2i1 = (P[i] * P[i] / M[i] + P[0] * P[0] / M[0]) / 2 + g * (M[i] * M[i] + M[0] * M[0]) / 4 + c * (P[i] + P[0]) / 2
else:
hv2i1 = (P[i] * P[i] / M[i] + P[i + 1] * P[i + 1] / M[i + 1]) / 2 + g * (M[i] * M[i] + M[i + 1] * M[i + 1]) / 4 + c * (P[i] - P[i + 1]) / 2
dydt[1][i] = (hv2i - hv2i1) / dx
return dydt
def next_time():
k1 = dydt()
for i in range(n):
y[0][i] += k1[0][i] * dt / 2
y[1][i] += k1[1][i] * dt / 2
k2 = dydt()
for i in range(n):
y[0][i] -= k1[0][i] * dt / 2
y[1][i] -= k1[1][i] * dt / 2
y[0][i] += k2[0][i] * dt / 2
y[1][i] += k2[1][i] * dt / 2
k3 = dydt()
for i in range(n):
y[0][i] -= k2[0][i] * dt / 2
y[1][i] -= k2[1][i] * dt / 2
y[0][i] += k3[0][i] * dt
y[1][i] += k3[1][i] * dt
k4 = dydt()
for i in range(n):
y[0][i] -= k3[0][i] * dt
y[1][i] -= k3[1][i] * dt
for i in range(n):
y[0][i] += dt * (k1[0][i] + 2 * k2[0][i] + 2 * k3[0][i] + k4[0][i]) / 6
if abs(y[0][i]) < 0.01:
y[0][i] = -0.01
y[1][i] += dt * (k1[1][i] + 2 * k2[1][i] + 2 * k3[1][i] + k4[1][i]) / 6
while True:
for i in range(1):
next_time()
draw()
sleep(0.02)