pdeSolver

首发版本:3.00.6.1

语法

pdeSolver(coeff, initial, domain, tGrid, [boundary], [method], [setting])

详情

使用有限差分法(FDM)求解一维或二维线性抛物型偏微分方程(PDE)。支持均匀或非均匀结构化网格、Dirichlet/Neumann/Robin 边界条件、一维 obstacle 约束、一维 theta 格式、二维显式格式和 ADI 格式。

一维情况下,求解如下方程:

M(x,t)ut=A(x,t)2ux2+B(x,t)ux+C(x,t)u+D(x,t)M(x,t)\frac{\partial u}{\partial t}=A(x,t)\frac{\partial^2u}{\partial x^2}+B(x,t)\frac{\partial u}{\partial x}+C(x,t)u+D(x,t)

数学量 coeff 中的字段 含义

M

mass

时间导数项系数

A

diff

二阶扩散项系数

B

conv

一阶对流项系数

C

react

反应项系数

D

source

源项

二维情况下,求解如下方程:

M(x,y,t)ut=A(x,y,t)2ux2+B(x,y,t)2uy2+C(x,y,t)2uxy+D(x,y,t)ux+E(x,y,t)uy+F(x,y,t)u+G(x,y,t)M(x,y,t)\frac{\partial u}{\partial t}=A(x,y,t)\frac{\partial^2u}{\partial x^2}+B(x,y,t)\frac{\partial^2u}{\partial y^2}+C(x,y,t)\frac{\partial^2u}{\partial x\partial y}+D(x,y,t)\frac{\partial u}{\partial x}+E(x,y,t)\frac{\partial u}{\partial y}+F(x,y,t)u+G(x,y,t)

数学量 coeff 中的字段 含义

M

mass

时间导数项系数

A

xx

x 方向二阶扩散项系数

B

yy

y 方向二阶扩散项系数

C

xy

xy 交叉二阶导数项系数

D

x

x 方向一阶对流项系数

E

y

y 方向一阶对流项系数

F

u

反应项系数

G

source

源项

注意: 当前版本不支持 3D PDE、FEM、FVM、非结构网格、强非线性 PDE、二维 obstacle、完整二维隐式格式、定义域外插值和任意非网格时间点的时间插值。

参数

coeff 字典,描述 PDE 方程及其系数。一维情况下由 pdeCoeff1D 构造,二维情况下由 pdeCoeff2D 构造。函数型系数必须在每个求值点返回数值标量,且 mass 必须为正数。

initial 数值标量、数值向量、数值矩阵或函数,表示 tGrid[0] 时刻的初始条件。在金融场景中通常表示 payoff:

  • 一维情况下,数值标量会广播到全部空间节点;数值向量的长度必须等于 x 网格节点数;函数签名为 def(x)

  • 二维情况下,数值标量会广播到全部空间节点;数值矩阵的逻辑形状必须为 nx × ny;函数签名为 def(x, y)。不支持用一维向量描述二维初值。

若使用 Dirichlet 边界,initial 在相应边界节点上的值必须与 tGrid[0] 时刻的边界值一致。二维 Neumann 或 Robin 边界上的节点由求解器根据边界关系回填。

domain 字典,描述空间网格和求解区域。一维情况下由 pdeDomain1D 构造,二维情况下由 pdeDomain2D 构造。

tGrid 一维数值向量,表示时间推进网格。至少包含两个时间点;所有元素必须为有限的非 NULL 值,并严格递增。

boundary 可选参数,表示边界条件。默认值为 NULL,即所有边界均采用值为 0 的 Dirichlet 边界。也可以传入数值标量,为所有边界指定相同的常量 Dirichlet 值。

一维情况下,可传入按 [left, right] 排列的二元向量。每个元素可以是:

  • 数值标量,表示常量 Dirichlet 边界;

  • 签名为 def(t) 的函数,表示随时间变化的 Dirichlet 边界;

  • 显式描述 Dirichlet、Neumann 或 Robin 边界的字典。

二维情况下,可传入按 [xMin, xMax, yMin, yMax] 排列的四元向量。xMin/xMax 边界函数的签名为 def(y, t)yMin/yMax 边界函数的签名为 def(x, t)

边界字典的字段如下:

type 必需字段 含义

"dirichlet"

value

指定边界上的函数值。

"neumann"

value

指定边界上的外法向导数。

"robin"

alpha、beta、value

  • 指定 αu+βun=value\alpha u+\beta\frac{\partial u}{\partial n}=\mathrm{value}

  • alpha 与 beta 不能同时为 0。

边界字典中的值可以是数值标量或符合相应边界函数签名的函数。二维两条 Dirichlet 边在角点相交时,边界值必须在相对容差 1e-12 内一致;一条 Dirichlet 边与 Neumann 或 Robin 边相交时,角点采用 Dirichlet 值。

method 可选参数,STRING 或 SYMBOL 键的字典,表示数值方法和计算策略。默认值为 NULL。不允许包含未定义字段,也不能包含 setting 的字段。

通用字段如下:

字段 类型 说明

type

STRING

求解器类型。可选值为 "FDM"。默认值为 "FDM"。

spaceOrder

INT

必须为正整数。Neumann/Robin 边界使用的一侧空间差分阶数。可选值为:

  • 1

  • 2

默认值为 2。二阶边界差分要求相应方向至少有 4 个节点。

convectionScheme

STRING

一阶对流项离散格式。可选值为:

  • "central"

  • "upwind"

  • "hybrid"

默认值为 "central"。

rannacherSteps

INT

必须为非负整数。

  • 使用两个半步 backward-Euler/Douglas 子步平滑初始不连续的公开时间区间数。

  • 不能超过公开时间区间数。

  • 二维显式格式必须为 0。

默认值为 0。

stabilityCheck

BOOL

是否执行稳定性和网格检查。关闭后不生成相应 warning,但仍执行必要的输入检查。默认值为 true。

jit

BOOL

是否请求将符合条件的具名用户自定义函数编译为 raw DOUBLE JIT 回调。失败时回退到低延迟回调和普通函数调用,并在诊断 warning 中说明。函数上的 @jit 注解可独立请求 JIT。默认值为 false。

precompute

STRING

系数预计算策略。可选值为:

  • "auto"

  • "all"

  • "timeSlice"

默认值为 "auto"。

pivotTolerance

DOUBLE

必须为非负数。Thomas 求解和边界消元时的相对主元阈值。默认值为 1e-12。

memoryLimitMB

DOUBLE

必须为正数。本次求解允许使用的有效内存上限。默认值为任务默认限制。

precomputeLimitMB

DOUBLE

必须为正数。系数预计算缓存上限。默认值为自动计算。

一维情况下还支持以下字段:

字段 类型 说明

scheme

STRING

时间离散格式。可选值为:

  • "theta"

  • "explicit"

  • "implicit"

默认值为 "theta"。

theta

DOUBLE

theta 格式权重。可选值范围为 [0, 1]。显式格式要求为 0,隐式格式要求为 1。默认值为 0.5。

linearSolver

STRING

一维三对角线性系统求解器。仅支持 "thomas"。默认值为 "thomas"。

obstacle

数值向量或函数

障碍值向量或签名为 def(x)/def(x, t) 的函数。提供后启用 obstacle 求解。默认值为 NULL。

obstacleSolver

STRING

obstacle 求解器。可选值为:

  • "PSOR"

  • "penalty"

默认值为 "PSOR"。

psorOmega

DOUBLE

PSOR 松弛因子。可选值范围为 (0, 2)。默认值为 1.2。

psorTolerance

DOUBLE

必须为正数。PSOR 收敛容差。默认值为 1e-8。

psorMaxIterations

INT

必须为正整数。PSOR 在单个时间步的最大迭代次数。默认值为 10000。

penaltyFactor

DOUBLE

必须为有限正数,penalty 约束惩罚因子。默认值为 1e8。

penaltyTolerance

DOUBLE

必须为正数。penalty 收敛容差。默认值为 1e-8。

penaltyMaxIterations

INT

必须为正整数。penalty 在单个时间步的最大迭代次数。默认值为 100。

一维显式格式不能与 obstacle 或 Rannacher smoothing 组合;PSOR 字段和 penalty 字段不能交叉使用。

二维情况下还支持以下字段:

字段 类型 说明

scheme

STRING

时间格式。可选值为:

  • "ADI"

  • "explicit"

完整二维 "implicit" 当前未实现。默认值为 "ADI"。

adiScheme

STRING

ADI 方案。可选值为:

  • "Douglas"

  • "CS"

  • "MCS"

  • "HV"

默认值为 "MCS"。

adiTheta

DOUBLE

  • Douglas:默认值为 0.5,可选值范围为 [0.5, 1]。

  • CS:固定值为 0.5。

  • MCS:默认值为 1/3,可选值范围为 [1/3, 1]。

  • HV:默认值为 0.5+360.5+\frac{\sqrt{3}}{6},可选值范围为 [0.5+36,1]\left[0.5+\frac{\sqrt{3}}{6},1\right]

crossTermTreatment

STRING

  • Douglas:必须为 "explicit"。

  • CS、MCS 和 HV:必须为 "explicitCorrection"。

linearSolver

STRING

ADI 方向线求解器。仅支持 "batchedThomas"。默认值为 "batchedThomas"。

parallel

BOOL

是否请求并行处理多组方向线。请求并不保证实际启用。默认值为 true。

numThreads

INT

  • 请求线程数,可选值范围为 [1, 256]。

  • 仅在 parallel=true 且采用 ADI 时有效。

默认值为 min(coreCount, 8)。

二维显式格式不能设置 adiSchemeadiThetacrossTermTreatmentlinearSolvernumThreads 或非零 rannacherSteps。二维不支持 obstacleparallel=false 时不能显式设置 numThreads

setting 可选参数,STRING 或 SYMBOL 键的字典,只控制输出形式、插值和诊断。默认值为 NULL。不允许包含未定义字段,也不能包含 method 的字段。

字段 类型 说明

resultMode

STRING

返回模式。可选值为:

  • "value"

  • "detail"

默认值为 "value"。

output

STRING

时间层输出范围。可选值为:

  • "final"

  • "all"

  • "selected"

默认值为 "final"。

outputTimes

一维数值向量

仅在 output="selected" 时使用。必须严格递增、无重复,并精确匹配 tGrid 中的公开时间点。默认值为 NULL。

interpAt

数值标量、数值向量或矩阵

  • 一维为数值标量或向量。

  • 二维为 [x, y] 或 n 行 2 列矩阵。

  • 坐标必须位于定义域内,不进行外推。

默认值为 NULL。

calcGreeks

BOOL

是否计算 Greeks。

  • 一维计算 Delta、Gamma、Theta。

  • 二维计算五个空间 Greek。

默认值为 false。

diagnostics

BOOL

是否返回诊断信息。仅在 resultMode="detail" 时可设为 true。默认值为 false。

返回值

返回值由 setting 中的 resultModeoutputinterpAtcalcGreeks 决定。

resultMode="value"(默认值),且没有指定 interpAtcalcGreeks=false 时,直接返回 PDE 数值解:

维度 output="final" output="all" output="selected"

1D

长度为 nx 的 DOUBLE 向量

time × x DOUBLE 矩阵

selectedTime × x DOUBLE 矩阵

2D

nx × ny DOUBLE 矩阵

ANY 向量,每个元素为 nx × ny 矩阵

ANY 向量,每个元素为 nx × ny 矩阵

二维矩阵的行对应 x 网格,列对应 y 网格,采用 x-fastest:index=i+nx*j 布局。

resultMode="value" 下请求插值或 Greeks 时,返回一个轻量字典,按需包含以下字段:

字段 说明

value

PDE 数值解。

interpValue

interpAt 指定位置的插值结果。

greeks

  • Greeks tuple。一维顺序为 [delta, gamma, theta]。

  • 二维顺序为 [deltaX, deltaY, gammaXX, gammaYY, crossGammaXY]。

resultMode="detail" 时返回字典。始终包含 value;指定 interpAt 时增加 interpValue 和 interpAt;指定 calcGreeks=true 时增加 greeks 和 greekNames;output"all""selected" 时增加 outputTimes。

当同时指定 diagnostics=true 时,字典还包含:

字段 说明

solution

完整空间网格上的求解结果。

meta

实际采用的维度、数值格式、线性求解器、并行状态、资源策略和输出设置。

diagnostics

本次运行的成功状态、warning、耗时、时间步数、空间节点数、稳定性检查、迭代统计和内存估算。

在 final 输出且只有一个插值点时,interpValue 为 DOUBLE 标量;多个插值点时为 DOUBLE 向量;all/selected 输出时按时间和插值点组成向量或矩阵。

例子

例1. 求解一维热方程 ut=0.2uxxu_t=0.2u_{xx}。空间范围为 [0,1][0,1],左右两端采用零 Dirichlet 边界,初值为 u(x,0)=x(1x)u(x,0)=x(1-x)

heatXGrid = (0..40) / 40.0
heatTGrid = (0..800) * (0.05 / 800.0)
heatCoeff = pdeCoeff1D(diff=0.2)
heatInitial = heatXGrid * (1.0 - heatXGrid)
heatDomain = pdeDomain1D(xGrid=heatXGrid)

heatResult = pdeSolver(
    coeff=heatCoeff,
    initial=heatInitial,
    domain=heatDomain,
    tGrid=heatTGrid,
    boundary=0.0
)

heatResult

例2. 求解二维热方程 ut=0.05(uxx+uyy)u_t=0.05(u_{xx}+u_{yy})。区域为 [0,1]×[0,1][0,1]\times[0,1],四条边均采用零 Dirichlet 边界。

plateXGrid = (0..10) / 10.0
plateYGrid = (0..10) / 10.0
plateTGrid = (0..100) / 1000.0
plateCoeff = pdeCoeff2D(xx=0.05, yy=0.05)

def plateInitial(x, y) {
    return x * (1.0 - x) * y * (1.0 - y)
}

plateDomain = pdeDomain2D(xGrid=plateXGrid, yGrid=plateYGrid)
plateResult = pdeSolver(
    coeff=plateCoeff,
    initial=plateInitial,
    domain=plateDomain,
    tGrid=plateTGrid,
    boundary=0.0
)

plateResult

例3. 求解二维泊松型问题。边界向量按 xMinxMaxyMinyMax 排列,其中 xMinyMin 使用 Robin 边界,xMaxyMax 使用 Neumann 边界。

boundaryXGrid = 0.0 0.2 0.8 1.5
boundaryYGrid = 0.0 0.3 1.0 1.8
boundaryTGrid = 0.0 0.05 0.1 0.15 0.2

boundaryDomain = pdeDomain2D(xGrid=boundaryXGrid, yGrid=boundaryYGrid)
boundaryCoeff = pdeCoeff2D(xx=1.0, yy=1.0, source=-6.0)
def boundaryInitial(x, y) {
    return x * x + 2.0 * y * y + 3.0 * x + 4.0 * y + 5.0
}

xMinRobin = dict(STRING, ANY)
xMinRobin["type"] = "robin"
xMinRobin["alpha"] = 2.0
xMinRobin["beta"] = 0.5
xMinRobin["value"] = def(y, t) {
    return 2.0 * (2.0 * y * y + 4.0 * y + 5.0) - 1.5
}

xMaxNeumann = dict(STRING, ANY)
xMaxNeumann["type"] = "neumann"
xMaxNeumann["value"] = 6.0

yMinRobin = dict(STRING, ANY)
yMinRobin["type"] = "robin"
yMinRobin["alpha"] = 1.5
yMinRobin["beta"] = 0.75
yMinRobin["value"] = def(x, t) {
    return 1.5 * (x * x + 3.0 * x + 5.0) - 3.0
}

yMaxNeumann = dict(STRING, ANY)
yMaxNeumann["type"] = "neumann"
yMaxNeumann["value"] = 11.2

boundaryConditions = [xMinRobin, xMaxNeumann, yMinRobin, yMaxNeumann]

boundaryResult = pdeSolver(
    coeff=boundaryCoeff,
    initial=boundaryInitial,
    domain=boundaryDomain,
    tGrid=boundaryTGrid,
    boundary=boundaryConditions
)

boundaryResult

例4. 在 method 中将 jit 设为 true,为系数、初值和边界回调请求 JIT 编译。先定义具名用户自定义函数,再将函数名传入相应参数。

jitXGrid = (0..40) / 40.0
jitTGrid = (0..200) * (0.05 / 200.0)

def jitDiff(x, t) {
    return 0.2
}

def jitInitial(x) {
    return x * (1.0 - x)
}

def jitBoundary(t) {
    return 0.0
}

jitCoeff = pdeCoeff1D(diff=jitDiff)
jitDomain = pdeDomain1D(xGrid=jitXGrid)
jitMethod = dict(keyType=STRING, valueType=ANY)
jitMethod["jit"] = true

jitResult = pdeSolver(
    coeff=jitCoeff,
    initial=jitInitial,
    domain=jitDomain,
    tGrid=jitTGrid,
    boundary=[jitBoundary, jitBoundary],
    method=jitMethod
)

jitResult

例5. 设置随时间变化的 Dirichlet 边界。求解 ut=0.22ux2+1\frac{\partial u}{\partial t}=0.2\frac{\partial^2u}{\partial x^2}+1,空间范围为 [0,1],初值为 u(x,0)=xu(x,0)=x,左右边界分别为 u(0,t)=tu(0,t)=tu(1,t)=1+tu(1,t)=1+t。左端直接传入 def(t) 函数,右端通过字典设置,展示两种等价的描述方式。初值与 t=0 时的边界值一致,解析解为 u(x,t)=x+tu(x,t)=x+t

rampXGrid = (0..4) / 4.0
rampTGrid = 0.0 0.05 0.1
rampDomain = pdeDomain1D(xGrid=rampXGrid)
rampCoeff = pdeCoeff1D(diff=0.2, source=1.0)

def rampLeftValue(t) {
    return t
}
def rampRightValue(t) {
    return 1.0 + t
}
rampRight = dict(keyType=STRING, valueType=ANY)
rampRight["type"] = "dirichlet"
rampRight["value"] = rampRightValue

rampResult = pdeSolver(
    coeff=rampCoeff,
    initial=rampXGrid,
    domain=rampDomain,
    tGrid=rampTGrid,
    boundary=[rampLeftValue, rampRight]
)
round(rampResult, 6)
// output: [0.1,0.35,0.6,0.85,1.1]

例6. 设置非零 Neumann 边界,并组合 Dirichlet 与 Neumann 边界。求解 ut=0.22ux20.4\frac{\partial u}{\partial t}=0.2\frac{\partial^2u}{\partial x^2}-0.4,空间范围为 [0,1.5],初值为 u(x,0)=x2+3x+5u(x,0)=x^2+3x+5,解析解不随时间变化。Neumann 的 value 指定外法向导数:左端为 ux|x=0=3-\left.\frac{\partial u}{\partial x}\right|_{x=0}=-3,右端为 ux|x=1.5=6\left.\frac{\partial u}{\partial x}\right|_{x=1.5}=6。以下使用非均匀网格;默认的二阶边界差分要求至少 4 个节点。第二次求解将左端改为固定值 5,右端仍使用 Neumann 边界。

gradientXGrid = 0.0 0.2 0.8 1.5
gradientTGrid = 0.0 0.05 0.1
gradientInitial = gradientXGrid * gradientXGrid + 3.0 * gradientXGrid + 5.0
gradientDomain = pdeDomain1D(xGrid=gradientXGrid)
gradientCoeff = pdeCoeff1D(diff=0.2, source=-0.4)

gradientLeft = dict(keyType=STRING, valueType=ANY)
gradientLeft["type"] = "neumann"
gradientLeft["value"] = -3.0
gradientRight = dict(keyType=STRING, valueType=ANY)
gradientRight["type"] = "neumann"
gradientRight["value"] = 6.0

gradientResult = pdeSolver(
    coeff=gradientCoeff,
    initial=gradientInitial,
    domain=gradientDomain,
    tGrid=gradientTGrid,
    boundary=[gradientLeft, gradientRight]
)
round(gradientResult, 6)
// output: [5,5.64,8.04,11.75]

mixedResult = pdeSolver(
    coeff=gradientCoeff,
    initial=gradientInitial,
    domain=gradientDomain,
    tGrid=gradientTGrid,
    boundary=[5.0, gradientRight]
)
round(mixedResult, 6)
// output: [5,5.64,8.04,11.75]

例7. 用函数设置 Robin 边界字典中的 alphabetavalue。求解 ut=0.22ux2+1\frac{\partial u}{\partial t}=0.2\frac{\partial^2u}{\partial x^2}+1,初值为 u(x,0)=xu(x,0)=x,解析解为 u(x,t)=x+tu(x,t)=x+t。两端均取 α(t)=1+t\alpha(t)=1+tβ(t)=0.5+0.1t\beta(t)=0.5+0.1t。按照 α(t)u+β(t)un=value(t)\alpha(t)u+\beta(t)\frac{\partial u}{\partial n}=\mathrm{value}(t),左端的 value(t)=(1+t)t(0.5+0.1t)\mathrm{value}(t)=(1+t)t-(0.5+0.1t),右端的 value(t)=(1+t)2+(0.5+0.1t)\mathrm{value}(t)=(1+t)^2+(0.5+0.1t)。减号和加号分别来自左、右端的外法向方向。

robinXGrid = (0..4) / 4.0
robinTGrid = 0.0 0.05 0.1
robinDomain = pdeDomain1D(xGrid=robinXGrid)
robinCoeff = pdeCoeff1D(diff=0.2, source=1.0)

def robinAlpha(t) {
    return 1.0 + t
}
def robinBeta(t) {
    return 0.5 + 0.1 * t
}
def robinLeftValue(t) {
    return (1.0 + t) * t - (0.5 + 0.1 * t)
}
def robinRightValue(t) {
    return (1.0 + t) * (1.0 + t) + (0.5 + 0.1 * t)
}

robinLeft = dict(keyType=STRING, valueType=ANY)
robinLeft["type"] = "robin"
robinLeft["alpha"] = robinAlpha
robinLeft["beta"] = robinBeta
robinLeft["value"] = robinLeftValue
robinRight = dict(keyType=STRING, valueType=ANY)
robinRight["type"] = "robin"
robinRight["alpha"] = robinAlpha
robinRight["beta"] = robinBeta
robinRight["value"] = robinRightValue

robinResult = pdeSolver(
    coeff=robinCoeff,
    initial=robinXGrid,
    domain=robinDomain,
    tGrid=robinTGrid,
    boundary=[robinLeft, robinRight]
)
round(robinResult, 6)
// output: [0.1,0.35,0.6,0.85,1.1]

相关函数: pdeCoeff1DpdeCoeff2DpdeDomain1DpdeDomain2DpdeGrid1DpdeInfo