UA MATH575B 数值分析下VI 统计物理的随机模拟方法2
扩散过程的停时问题
简单一点,考虑一维的随机扩散问题,假设扩散系数D=21,粒子源的初始位置为x0=1,0≤xt≤1。假设T是粒子第一次扩散到x=0的时间,求T的分布。
理论解
粒子的分布ρ(t,x)同样可以用Fokker-Planck方程:
∂t∂ρ=D∂x2∂2ρ
初始条件可以写成ρ(0,x)=δ(x−1),边界条件可以写成ρ(t,0)=0,从而这个PDE的解为
ρ(t,x)=2πt1[exp(−2t(x−1)2)−exp(−2t(x+1)2)]
现在考虑T的分布,因为T=mint{t:xt≤0},所以
FT(t)=1−∫0∞ρ(t,x)dx
这里用的思路其实是:如果Ac,Bc独立,则
P[A∩B]=1−P[(A∩B)c]=1−P[Ac⊔Bc]=1−(P[Ac]+P[Bc])
所以T的密度函数为
fT(t)=∂t∂FT(t)=−∫01∂t∂ρ(t,x)dx=−D∫01∂x2∂2ρ(t,x)dx=D∂x∂ρ(t,0)
这个量又叫diffusive flux to the wall x=0。带入PDE的解:
fT(t)=2πt3exp(−t/2)
模拟解
一个比较naive的想法是,模拟n次粒子第一次打到x=0的过程,这样就有n个T的样本了,根据这n个样本可以画出T的经验密度函数。贴一张我老师slides上的code示例:
第二行i是for循环第i次模拟,这段code模拟了一万次粒子第一次打到x=0的过程。第三行选取的τ=0.0001并设置初始值x0=1。第四行用while做判断,x>0的样本是还没打到x=0的,我们不需要,当x≤0时会退出这个while循环,这时的t就是第一次打到x=0的一个样本。3是设置的一个时间上限,超过这个上限还没打到x=0就认为粒子可能移动到x>1那段了。While循环内部做更新:
xi+1=xi+τζ
这个就是RK2导出的递推式。
这个是我老师的输出,实线是解析解,空心点是模拟一万个样本的结果,显然这一万个样本方差有点大;实心点是模拟一百万个样本的结果,倒是与解析解接近了很多,但模拟样本数量太大了,耗时会很久,所以这种naive的算法不推荐。
我们再来审视一下这个naive算法,如果粒子不会打到x=0上,那么while循环要做3000次直到t≥3才会退出,这会浪费很多时间。为了提高计算速度,可以考虑更灵活一点的步长。
这一段代码替换的是naive算法中for循环的内容。第二行定义初值,第三行做while循环的判断,如果x>0,粒子还没有到x=0处,就做第四行开始的循环体。循环体第一句做时间的更新:
ti+1=ti+dt,dt=xi2
这里的时间步长就不是固定的,这句code也不是对所有问题都能用的,因为dt=xi2,说明粒子越靠近x=0的时候,步长会变得越来越小,抽取的样本会变多;粒子越远离x=0的时候,步长会按偏离x=0的程度的二次方扩大,减少退出while循环的计算次数。循环体第二句做位置的更新,
xi+1=xi+dtζ
接下来的一个当t不少于一个元素时开始执行的while循环比较有意思,先介绍一下这个while循环背后的直觉。因为dt=xi2,再加上x0=1,所以一开始的时候步长是相当大的,因为终点x=0也就离初始值一个单位。第一步就跨过x=0的概率正好等于1−Φ(1),为了让这些点不浪费,我们定义一个更宽松的标准:在循环中记录下粒子的轨迹,如果轨迹上相邻两点的中点满足P(x=0)比较小,我们就不再考虑这条轨迹了。内层的while循环只有一个if else结构,if后面的条件就是在执行这个标准,只是这里把这条标准更细化了一下:and前半句要求时间步长 不能太短,and后半句的含义是相邻两个点偏离x=0不太远。如果不满足这条标准,就不再考虑这条轨迹,执行else后面的删除命令。如果满足这条轨迹,就取相邻两点的中点,从中点出发重新抽样。