本人目前初三,能力所限,如有不足之处,还望多多指教。
一周前看到了一个视频,于是我便想用python来求解这个问题。
假设在平面内有一带电粒子q,质量为m。空间内存在匀强磁场B,方向垂直于平面向内即沿z轴负半轴,以及一个沿y轴负半轴的重力场。带电粒子从磁场内O点释放。
则可直接列出粒子的运动方程
将这个方程分解成x和y两个方向
联立即可求得该方程组的解。
#导入 from sympy import * import numpy as np import matplotlib.pyplot as plt init_printing()
首先声明符号x,y,q,m,B,g
#参量 q,m,B,t,g = symbols('q m B t g',real=True,nonzero=True,positive=True) #函数 x,y = symbols('x y',real=True,nonzero=True,positive=True,cls=Function)
再将微分方程表示出来
eq1 = Eq(x(t).diff(t,t),-q*B*y(t).diff(t)/m) eq2 = Eq(y(t).diff(t,t),-g+q*B*x(t).diff(t)/m) sol = dsolve([eq1,eq2])
现在打印出sol(用jupyter notebook效果更好)
sol
很明显,这个式子非常冗杂,用trigsimp()方法化简
x = trigsimp(sol[0].rhs) y = trigsimp(sol[1].rhs) x,y
然后,可以将里面的积分常数算出
#定义积分变量,避免报错 var('C1 C2 C3 C4') #x(0)=0 e1 = Eq(x.subs({t:0}),0) #x'(0)=0,要将subs放在diff后面 e2 = Eq(x.diff(t).subs({t:0}),0) #y(0)=0 e3 = Eq(y.subs({t:0}),0) #y'(0)=0 e4 = Eq(y.diff(t).subs({t:0}),0) l = solve([e1,e2,e3,e4],[C1,C2,C3,C4]) l
紧接着,将积分常量替换到x和y里面,我们就得到了解的最终形式
x = x.subs(l) y = y.subs(l) x,y
当然,这个解还可以写成这种形式
用plt画图来看看
ts = np.linspace(0,15,1000) consts = {q:1,B:1,g:9.8,m:1} fx = lambdify(t,x.subs(consts),'numpy') fy = lambdify(t,y.subs(consts),'numpy') plt.plot(fx(ts),fy(ts)) plt.grid() plt.show()
但是sympy有一个缺点,当微分方程很复杂时,它会直接罢工。
于是另一个新的方法替我们解决了这个问题
#导入 import numpy as np from scipy.integrate import odeint import matplotlib.pyplot as plt import matplotlib.animation as animation from math import e
我们首先要创建一个函数,使它可以表示这个微分方程。
对于这个函数应该具备怎样的形式,先从一阶微分方程开始
比如说,我们要求解下面这个方程
它的解通过一些简单的分离变量即得
下面用odeint来解出它,令y(1)=1
#一阶 def f(x,y): #将一阶导数用y和x构成的函数来表示 dydx = -y/x return dydx #初始条件 y0 = 1 #初值条件是在自变量x的下限取到。如y(1)就要生成一个从1开始的范围 x = np.linspace(1,5,100) plt.xlim(0,5) plt.ylim(0,5) #odeint()方法,tfirst属性指的是函数f的第一个参数是自变量 sol = odeint(f,y0,x,tfirst=True) plt.plot(x,sol[:,0]) plt.grid() plt.show()
同理,对于下面的一阶微分方程组
此时我们设向量u=(x,y) (列向量),方程组化为
其实对于任意的一阶微分方程,都可以写成这样的形式
f的含义就是
此时微分方程就可以化为
而f(t,u)正是我们要找的那个函数
odeint中支持向量输入,因此,可以这样构造这个函数
def f(t,u): #x1,x2...xn = u x,y = u #dx1dt=...,dx2dt=...,dxndt=... dxdt = 3*x-x*y dydt = 2*x-y+e**t #dudt = [dx1dt,dx2dt,...dxndt] dudt = [dxdt,dydt] return dudt #x1(0)=...,x2(0)=...,xn(0)=... u0=[0,0] t = np.linspace(0,10,100) sol = odeint(f,u0,t,tfirst=True)
因此,对于二阶常微分方程
我们首先把这个方程化解成这样的形式
此时这是一个关于dy/dx和y的一个微分方程组
令u=(y,dy/dx),我们有
def f(x,u): y,dydx = u d2ydx2 = np.exp(x)-4*dydx-4*y dudx = [dydx,d2ydx2] return dudx u0=[0,0] x = np.linspace(0,10,100) sol = odeint(f,u0,x,tfirst=True)
对于更高阶的微分方程也是同样处理
此时我们在回过头来看最初的问题,可以轻松的得到
def f(t,r,k,g): #k = B*q/m x,y,dxdt,dydt = r d2xdt2 = -k*dydt d2tdt2 = -g + k*dxdt return [dxdt,dydt,d2xdt2,d2ydt2]
定义一些常量
t = np.linspace(0,15,1000) k = 1 g = 9.8
使用odeint方法
r0 = [0,0,0,0] sol = odeint(f,r0,t,tfirst = True,args=(k,g))
将图像画出
plt.plot(sol[:,0],sol[:,1]) plt.grid() plt.show()
odeint解这个方程也十分精确,与sympy相差无几。
最后我们还可以让他实现动画效果
def update_points(num): point_ani.set_data(s[:,0][num], s[:,1][num]) return point_ani, plt.xlabel('X') plt.ylabel('Y') fig,ax = plt.subplots() plt.plot(s[:,0],s[:,1]) point_ani, = plt.plot(s[:,0][0], s[:,1][0], "ro") plt.grid(ls="--") # 生成动画 ani = animation.FuncAnimation(fig, update_points,range(1, len(t)), interval=5, blit=True) plt.show()
如果要深度学习这两个库的话,还是推荐官方文档
sympy:https://docs.sympy.org/latest/tutorial/index.html
scipy:https://docs.scipy.org/doc/scipy/reference/tutorial/index.html
最近还准备在更新几篇(如果期末不挂的话)
注:我在b站上也发布了这篇文章