python - 具有非线性源项/势项的扩散反应 pde
问题描述
我第一次尝试用 FiPy 写几个扩散反应方程。
我的方程式适用于三种不同的浓度 c_i,仅包含扩散部分和来源(LateX 输入):
\partial_t c_1 = \nabla \cdot (D_1 \nabla c_1) + A_1 c_{1} + B_1 c_2^4 + W_1 \\
\partial_t c_2 = \nabla \cdot (D_2 \nabla c_2) + c_{1}(A_2 + 2c_1) + c_2^4 + W_2\\
\partial_t c_3 = \nabla \cdot (D_3 \nabla c_3) + A_3 c_{1} - W_3 \\
系数D_i、A_i、B_i、P_i和W_1是常数。我已经用 de Diffusion 项和非线性源项编写了代码。但是对于 100 的范围,我对 c2 有一些奇怪的行为。也许是我写错的非线性源项?我使用 ImplicitSourceTerm 命令,我认为这将线性化该术语。我错过了什么吗?我必须通过 myslef 进行线性化吗?(就像泰勒一样?)
如何将平流扩散反应 PDE 与 FiPy和 https://www.ctcms.nist.gov/fipy/examples/diffusion/generated/examples.diffusion.coupled.html耦合
from fipy import *
from fipy import CellVariable, Variable, Grid1D, TransientTerm, DiffusionTerm, ImplicitSourceTerm, LinearLUSolver, Viewer
import math
from fipy.tools import numerix
# Konstanten
F = 96485.3399 #C /mol
R = 8.314472 # J/(mol*K)
T = 273.18 # K
alpha = 0.5
c_std = 1. # mol/s
c1_sat = 1. # mol/s
eta_Zn = 1. # V
kIc = 6.3 # mol/s Von Reaktion I an der Kathode
kIa = 5. # mol/s Von Reaktion I an der Anode
kII = 0.25 # mol/s von Reaktion II
mu_1I = 1. # Von Zinkat in Reaktion I
mu_1II = -2. # Von Zinkat in Reaktion II
mu_2I = -4. # Von OH in Realtion I
mu_2II = 2. # Von OH in Reaktion II
mu_3II = 1. # Von H2O in Reaktion II
ep1 = 1. # Von Zinkat
ep2 = 1. # Von OH
ep3 = 1. # Von H20
D1 = .5 # Von Zinkat
D2 = 1. # Von OH
D3 = 0.1 # Von H20
## Mesh
L = 10.
nx = 1000
mesh = Grid1D(dx=L/1000, nx=nx)
x = mesh.cellCenters[0]
## Initial Conditions
c1 = CellVariable(name="c1", mesh=mesh, value=1., hasOld=True)
c2 = CellVariable(name="c2", mesh=mesh, value=1., hasOld=True)
c3 = CellVariable(name="c3", mesh=mesh, value=1., hasOld=True)
## Boundary Conditions
c1.constrain(2., mesh.facesLeft)
c1.constrain(0., mesh.facesRight)
c2.constrain(0., mesh.facesLeft)
c2.constrain(2., mesh.facesRight)
c3.constrain(0., mesh.facesLeft)
c3.constrain(2., mesh.facesRight)
c2.faceGrad.constrain(1., where=mesh.facesRight)
# Definition Konstanten fuer SourceTerm
Zn1 = (kIc/c_std)*math.exp(-(1-alpha)*(F/(R*T))*eta_Zn)+kII
Zn2 = (kIa/(c_std)**4)*math.exp(alpha*(F/(R*T))*eta_Zn)
Zn3 = kII*c1_sat
OH1 = 4*(kIc/c_std)*math.exp(-(1-alpha)*(F/(R*T))*eta_Zn)+2*kII
OH2 = 4*(kIa/(c_std)**4)*math.exp(alpha*(F/(R*T))*eta_Zn)
OH3 = 2*kII*c1_sat
H1 = kII
H2 = 0.
H3 = kII*c1_sat
sourceCoeff1 = (Zn1*c1) + (Zn1*c2**4) + Zn3
sourceCoeff2 = c1*(OH1 + 2*c1) + c2**4 + OH3
sourceCoeff3 = c1*H1 - H3
eq1 = (TransientTerm(var=c1) == DiffusionTerm(coeff=D1, var=c1) + ImplicitSourceTerm(coeff=sourceCoeff1, var=c1))
eq2 = (TransientTerm(var=c2) == DiffusionTerm(coeff=D2, var=c2) + ImplicitSourceTerm(coeff=sourceCoeff2, var=c2))
eq3 = (TransientTerm(var=c3) == DiffusionTerm(coeff=D3, var=c3) + ImplicitSourceTerm(coeff=sourceCoeff3, var=c3))
eqn = eq1 & eq2 & eq3
vi = Viewer((c1, c2, c3))
for t in range(50):
c1.updateOld()
c2.updateOld()
c3.updateOld()
eqn.solve(dt=1.e-3)
vi.plot()
我也在尝试添加潜在的术语,但我不明白这是否可能。类似的东西(与上面相同,但有潜在的部分)
\partial_t c_1 = \nabla \cdot (D_1 \nabla c_1) + \nabla \cdot \biggl(P_1c_1\nabla\Phi\biggr) + A_1 c_{1} + B_1 c_2^4 + W_1 \\
\partial_t c_2 = \nabla \cdot (D_2 \nabla c_2) + \nabla \cdot \biggl(P_2c_2\nabla\Phi\biggr) + c_{1}(A_2 + 2c_1) + c_2^4 + W_2\\
\partial_t c_3 = \nabla \cdot (D_3 \nabla c_3) + \nabla \cdot \biggl(P_3c_3\nabla\Phi\biggr) + A_3 c_{1} - W_3 \\
我分析了来自 https://www.ctcms.nist.gov/fipy/examples/phase/generated/examples.phase.binaryCoupled.html的示例, 以尝试添加像 Diffusionterm 这样的潜在术语,但我总是得到错误:
SolutionVariableNumberError:不同数量的解变量和方程。
我所期望的,但也许我错过了一些东西。
继承那部分代码:
eq1 = (TransientTerm(var=c1) == DiffusionTerm(coeff=D1, var=c1) + ImplicitSourceTerm(coeff=sourceCoeff1, var=c1)) + DiffusionTerm(coeff=D1phi, var=phi
eq2 = (TransientTerm(var=c2) == DiffusionTerm(coeff=D2, var=c2) + ImplicitSourceTerm(coeff=sourceCoeff2, var=c2)) + DiffusionTerm(coeff=D2phi, var=phi)
eq3 = (TransientTerm(var=c3) == DiffusionTerm(coeff=D3, var=c3) + DiffusionTerm(coeff=D3phi, var=phi) + ImplicitSourceTerm(coeff=sourceCoeff3, var=c3))
也尝试使用命令扫描做一些数值分析,如残差和范数。我在这里看到了关于扫描命令的一个很好的解释: Solver tolerance and residual error when using sweep function in FiPy
那部分代码(仅用于带有源的扩散术语)
dt = 1.e5
solver = LinearLUSolver(tolerance=1e-10)
c1.updateOld()
c2.updateOld()
c3.updateOld()
res = 1.
initialRes = None
while res > 1e-4:
res = eq.sweep(dt=dt, solver=solver)
if initialRes is None:
initialRes = res
res = res / initialRes
尽管如此,我没有得到任何结果或错误,必须手动停止该过程。
总而言之,我的问题是:有可能使用具有扩散项和潜在项的 FiPy 对 pdes 来解决吗?是否也可以对耦合的 pdes 执行命令扫描?
或者我错过了什么?
我非常感谢任何帮助或建议。我希望,我写的很清楚。十分感谢。
解决方案
这是您的代码的工作版本。我不确定它是否正确,但它似乎更明智。
使用显式来源
所做的主要更改是删除隐式来源术语并用显式来源替换它们。例如第一个方程
eq1 = (TransientTerm(var=c1) == DiffusionTerm(coeff=D1, var=c1) + ImplicitSourceTerm(coeff=sourceCoeff1, var=c1))
就是现在
eq1 = (TransientTerm() == DiffusionTerm(coeff=D1) + sourceCoeff1)
在 OP 的代码中使用ImplicitSourceTerm
所有三个等式是不正确的。要了解它的用途,请获取要解决的变量的形式value * phi
的来源。phi
在这种情况下,正确的来源是ImplicitSourceTerm(value)
. 乘以phi
是隐含的假设。此外,这仅在value
为负数时才能正常工作[编辑]。有关更多说明,请参阅此示例。
[编辑]:实际上,ImplicitSourceTerm
处理正值和负值,因此如果值为正,则显式添加源。
解耦方程
最好首先解耦方程,以便更好地理解发生了什么。当空间导数中有多个变量在单个方程中耦合时,耦合方程的效果最好。
在这种情况下,由于源项而耦合方程可能很有用,但仅出于效率目的且仅在验证问题解决方案之后。新代码现在以非耦合形式求解方程。
for t in range(100):
c1.updateOld()
c2.updateOld()
c3.updateOld()
for sweep in range(5):
res1 = eq1.sweep(c1, dt=dt, solver=solver)
res2 = eq2.sweep(c2, dt=dt, solver=solver)
res3 = eq3.sweep(c3, dt=dt, solver=solver)
print('res1', res1)
print('res2', res2)
print('res3', res3)
vi.plot()
使用扫描循环
由于方程在源中是非线性的,因此最好使用内部循环并打印残差以检查每个时间步的收敛性,如上面的代码中所示。
使用不同的求解器
为了使事情顺利进行,它似乎需要一个与 FiPy 选择的默认值不同的求解器。这通常取决于所使用的求解器套件。在这种情况下,我使用的是 Python 3,所以我可能使用的是 Scipy 求解器。为了让它工作,我使用了它,LinearLUSolver
否则它运行得非常慢。
未来
从长远来看,线性化源和使用ImplicitSourceTerm
s 以及耦合方程以提高效率可能是一个好主意,但只有在完全理解和验证解决方案之后。请参阅此示例和此说明以获取帮助。
推荐阅读
- java - CORS 在客户端和服务器位于相同 url 的情况下无法使用 CORS 过滤器
- html - HTML Chrome Favicon 不显示
- c# - .Net Core 5 Web Api - Swagger POST ok Xunit POST 错误
- java - Spring 不在 Config 子文件夹中查找
- kql - 如何改进热图的大型数据集的 KQL 查询
- ios - 适合初学者的 Metal iOS
- eclipse - 为什么生成 Getter 和 Setter 在 eclispe 中不起作用?
- python - PYSPARK - ROW_NUMBER() PARTITION BY 以获得最大购买金额
- redis - Redis 和谷歌云函数
- c++ - 如果我将流用于基类构造函数,我可以将流的其余部分用于派生吗?