讨论误差的影响

考虑一个单摆我们可以先解析的求解

解析解

这里 $\omega$ 是角速度

二阶常微分方程数值求解方式是类似的。

写出对应的程序

json文档

1
2
3
4
5
6
7
{
"pendulum1":{
"length":2.0,
"theta0":3.0,
"omega0": 0.0
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
# Simulation of pendulum
import pickle
import matplotlib.pyplot as plt
import numpy as np
import json

# declare contants
g = 9.8
dt = 0.1
max_steps = 1000

# declare pendulum class
class Simple_Pendulum:
def __init__(self, length, theta0=0, omega0=0):
self.length = length
self.theta = np.zeros(max_steps + 1, dtype = np.float64)
self.omega = np.zeros(max_steps + 1, dtype = np.float64)
self.t = np.zeros(max_steps + 1, dtype = np.float64)
self.theta[0] = theta0
self.omega[0] = omega0
def calculate(self):
for i in range(max_steps):
self.theta[i + 1], self.omega[i + 1], self.t[i + 1] = self.move_one_step(self.theta[i], self.omega[i], self.t[i])
def move_one_step(self, theta, omega, t):
_theta = theta + omega*dt
_omega = omega - (g/self.length)*theta*dt
_t = t + dt
return _theta, _omega, _t
def get_results(self):
return self.theta, self.omega, self.t

def show_pendulum():
with open("./computational-physics-project/lecture-3/pendulum.json", "r") as f:
data = json.load(f)
pendulum1 = Simple_Pendulum(data["pendulum1"]["length"],
data["pendulum1"]["theta0"],
data["pendulum1"]["omega0"])
pendulum1.calculate()
fig, ax = plt.subplots(2, 1, layout="constrained")
theta, omega, t = pendulum1.get_results()
ax[0].plot(t, theta, label=r"$\theta$")
ax[0].set(xlabel="t(s)", ylabel=r"$\theta$(m)")
ax[0].legend()
ax[0].set_title(r"Oscillation of $\theta$")
ax[1].plot(t, omega, label=r"$\omega$")
ax[1].set(xlabel="t(s)", ylabel=r"$\omega$(m/s)")
ax[1].legend()
ax[1].set_title(r"Oscillation of $\omega$")
plt.savefig("pendulum.png")
plt.show()

show_pendulum()

pendulum

很明显,这样处理会导致结果发散

结果发散的原因

是体系的能量不守恒

小 $\theta$ 有近似

使用欧拉方法进行转化

其实只需要使用 $x{i+1}=x_i+v{i+1}dt$ 代替 $x{i+1}=x_i+v{i}dt$ 就可以一定程度上解决

使用Euler-Cromer方法进行模拟

对于简谐运动,运动方程的一般形式为

采用 Euler-Cromer 方法求解 x 关于时间的函数,其中 $\alpha=1$ (为了方便,取 k=1 )。这是简谐运动的一个关键特征。然后将程序扩展到处理 $\alpha=3$ 的情况。这是一个非简谐振子的例子。计算了几个不同振幅(在范围 0.2 到 1 内)下的振动周期,并证明现在运动的周期取决于振幅。

$\alpha=1$ 的情况

谐振子的 json 文档为

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
{
"oscillator":
{
"oscillator1":{
"distance0":1.0,
"velocity0":0
},
"oscillator2":{
"distance0":0.2,
"velocity0":0
},
"oscillator3":{
"distance0":0.4,
"velocity0":0
},
"oscillator4":{
"distance0":0.6,
"velocity0":0
},
"oscillator5":{
"distance0":0.8,
"velocity0":0
}
}
}

我们要研究这个函数的图像

先只取 $\alpha=1$ ,并且 $k=1$

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
# Simulation of pendulum with Euler Cromer
import pickle
import matplotlib.pyplot as plt
import numpy as np
import json


# declare contants
k=1
dt = 0.1
max_steps = 1000

class simple_oscillator:
def __init__(self, distance0=0, velocity0=0):
self.distance = np.zeros(max_steps + 1, dtype = np.float64)
self.velocity = np.zeros(max_steps + 1, dtype = np.float64)
self.t = np.zeros(max_steps + 1, dtype = np.float64)
self.distance[0] = distance0
self.velocity[0] = velocity0
def calculate(self):
for i in range(max_steps):
self.distance[i + 1], self.velocity[i + 1], self.t[i + 1] = self.move_one_step(self.distance[i], self.velocity[i], self.t[i])
def move_one_step(self, distance, velocity, t):
_velocity = velocity - k*distance*dt
_distance = distance + _velocity*dt
_t = t + dt
return _distance, _velocity, _t
def get_results(self):
return self.distance, self.velocity, self.t

def show_oscillator_with_Euler_Cromer():
with open("oscillator.json", "r") as f:
data = json.load(f)
oscillator1 = simple_oscillator(data["oscillator1"]["distance0"],
data["oscillator1"]["velocity0"])
oscillator1.calculate()
fig, ax = plt.subplots(2, 1, layout="constrained")
distance, velocity, t = oscillator1.get_results()
ax[0].plot(t, distance, label=r"$x$")
ax[0].set(xlabel="t(s)", ylabel=r"$x$(m)")
ax[0].legend()
ax[0].set_title(r"Oscillation of $x$")
ax[1].plot(t, velocity, label=r"$v$")
ax[1].set(xlabel="t(s)", ylabel=r"$v$(m/s)")
ax[1].legend()
ax[1].set_title(r"Oscillation of $v$")
plt.savefig("osillator.png")
plt.show()

show_oscillator_with_Euler_Cromer()

当$\alpha=1$时得到图像1

"谐振子图像1"

当$\alpha=3$时得到图像2

"谐振子图像2"

考虑振幅与周期的关系

接下来我们给simple_oscillator类增加一个calculate_period()函数,用来计算谐振子的周期

calculate_period() 是通过判断$x$是否在初始位置附近来确定周期大小

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
# Simulation of oscillators with Euler Cromer
import pickle
import matplotlib.pyplot as plt
import numpy as np
import json


# declare contants
k=1
dt = 0.1
max_steps = 1000

class simple_oscillator:
def __init__(self, distance0=0, velocity0=0):
self.distance = np.zeros(max_steps + 1, dtype = np.float64)
self.velocity = np.zeros(max_steps + 1, dtype = np.float64)
self.t = np.zeros(max_steps + 1, dtype = np.float64)
self.distance[0] = distance0
self.velocity[0] = velocity0
def calculate(self):
for i in range(max_steps):
self.distance[i + 1], self.velocity[i + 1], self.t[i + 1] = self.move_one_step(self.distance[i], self.velocity[i], self.t[i])
def move_one_step(self, distance, velocity, t):
_velocity = velocity - k*distance**3*dt
_distance = distance + _velocity*dt
_t = t + dt
return _distance, _velocity, _t
def get_results(self):
return self.distance, self.velocity, self.t,self.period
def calculate_period(self):
period_list=[]
period_sum=0
for i in range(max_steps):
self.distance[i + 1], self.velocity[i + 1], self.t[i + 1] = self.move_one_step(self.distance[i], self.velocity[i], self.t[i])
if i>=5 and abs(self.distance[i]-self.distance[0]) <= 0.05 and abs(self.distance[i-1]-self.distance[0]) >= 0.05:
period_list.append(i)
for i in range(len(period_list)):
if i==0:
period_sum+=period_list[0]
else:
period_sum+=period_list[i]-period_list[i-1]
self.period=period_sum*dt/len(period_list)

def show_oscillator_with_Euler_Cromer():
with open("./computational-physics-project/lecture-3/oscillator.json", "r") as f:
data = json.load(f)
oscillator1 = simple_oscillator(data["oscillator"]["oscillator1"]["distance0"],
data["oscillator"]["oscillator1"]["velocity0"])
oscillator1.calculate_period()
fig, ax = plt.subplots(2, 1, layout="constrained")
distance, velocity, t , period= oscillator1.get_results()
ax[0].plot(t, distance, label=r"$x$")
ax[0].set(xlabel="t(s)", ylabel=r"$x$(m)")
ax[0].legend()
ax[0].set_title(r"Oscillation of $x$")
ax[1].plot(t, velocity, label=r"$v$")
ax[1].set(xlabel="t(s)", ylabel=r"$v$(m/s)")
ax[1].legend()
ax[1].set_title(r"Oscillation of $v$")
plt.savefig("osillator.png")
print(period)
plt.show()

show_oscillator_with_Euler_Cromer()

模拟得到

振幅 周期
0.2 35.20
0.4 18.28
0.6 12.26
0.8 9.22
1.0 7.38

随着振幅增大,周期变小。

一个直观的理解,当$\alpha>1 $ 时,$-kx^\alpha$ 对 $x$ 更敏感,当$x$增大,波动会更强烈

理论解释

对于前一个练习中 (3.9) 的非简谐振子,可以通过某些特殊函数来解析地获得振动周期关于 $\alpha$ 的一般值的关系。请进行这样的计算,并描述周期与振幅之间的关系如何取决于 $\alpha$ 的值。你能对这个结果给出一个物理解释吗?提示:如果将 (3.9) 的两边都乘以 $\frac{d x}{d t}$,然后对 $t$ 进行积分。这将导致速度和 x 之间的关系。

变换为

积分得

再利用$v=\frac{dx}{dt}$

这个积分无法解析表示,但是可以写成下式

其中$F(x)$是一个周期函数

那么$\sqrt{\frac{2k}{\alpha+1}}$随$\alpha$增大而减小,故周期增大