2026a

# 初值问题积分算法的接口规范


  1. 本章所介绍的数据结构、算法函数、函数值计算函数、雅可比计算函数的接口都定义在头文件:%Sysplorer安装目录%\Simulator\Src\mws_ivp_solver.h

  2. 本章所介绍的内存分配、内存释放以及日志输出回调函数的接口都定义在头文件:%Sysplorer安装目录%\Simulator\Src\ mws_common_decl.h

# 数据结构的定义

数据结构由求解器规范定义,扩展自定义算法时可直接使用。

# 积分算法对象

typedef void* MwsIVPSolverType;

MwsIVPSolverType 是积分算法对象(实例)的专用类型,因此积分器实例化函数将其作为返回类型。MwsIVPSolverType 实际上是一个通用指针类型 void*,因此可以转换为任意其他类型的指针。例如,在 MyEuler 算法的实现中,其实例化函数实际上返回的是 UserEulerData*,并由 C 语言编译器自动将其隐式转换为 MwsIVPSolverType。

MwsIVPSolverType 类型的积分算法对象 inst 还会作为所有其他函数的输入参数。然而,在函数内部需要获取积分算法对象的值时,需要显式地将其转换为实际的积分算法类型。例如,在 MyEuler 算法的实现中,有很多地方进行了这样的类型转换:

UserEulerData* eulerData = (UserEulerData*)(inst);

# 积分算法工作数据

typedef void* MwsIVPWorkData;

MwsIVPWorkData 是积分器的工作数据专用类型,仅在获取/设置积分算法工作数据,以及序列化/反序列化积分算法工作数据的算法函数中出现。若用户无接续仿真的功能需求,则无需关心此类型。

# 求解器提供的回调函数

typedef struct
{
MwsIVPFncEvaluation  fncEvaluation;
MwsIVPJacEvaluation  jacEvaluation;
MwsLogger              logger;
MwsAllocateMemory    allocateMemory;
MwsFreeMemory         freeMemory;
}MwsIVPCallbackFns;
功能 属性
由求解器提供的,积分算法可直接使用的回调函数 fncEvaluation 函数值计算的函数
jacEvaluation 雅可比计算的函数
logger 日志输出的函数
allocateMemory 堆内存分配的函数
freeMemory 堆内存释放的函数

以上各回调函数的详细接口说明,请参见回调函数的标准接口。其中,allocateMemoryfreeMemory是搭配使用的,分别用于分配和释放内存。例如,在 MyEuler 算法中为长度为 n 的 MwsReal 类型数组 ynew 分配和释放内存:

eulerData->callbackFns = *cbfns;

eulerData->ynew = (*cbfns->allocateMemory)(userContext, n, sizeof(MwsReal));

eulerData->callbackFns.freeMemory(eulerData->userContext, eulerData->ynew);

# 积分算法的特性

typedef struct
{
MwsBoolean  fixedStepMethod;
MwsBoolean  implicitMethod;
MwsBoolean  canSolveImplicitEquation;
MwsBoolean  canSolveDAE;
MwsBoolean  cloumnMajorMatrix;
MwsBoolean  canSetMaximalStepSize;
MwsBoolean  canHitExpectedOutputTime;
MwsBoolean  canResetWorkData;
MwsBoolean  canSerializeWorkData;
}MwsIVPSolverFeature;
MwsIVPSolverFeature用于描述积分算法的特性,其所有的成员均为MwsBoolean类型,只有两种取值:mwsTrue和mwsFalse。
功能 属性
积分算法的特性 fixedStepMethod 是否为定步长方法。例如,MyEuler 算法为定步长算法,Dopri5 为变步长算法
implicitMethod 是否为隐式方法。例如,Mebdf 为隐式方法,MyEuler 为显式方法。显式方法仅适用于非刚性问题,而隐式方法适用于刚性问题
canSolveImplicitEquation 是否能求解隐式 ODE:f(t,y,y ̇ )=0
canSolveDAE 是否能求解微分代数方程(DAE),例如:f(t,y,y ̇,z)=0
cloumnMajorMatrix 矩阵是否为列主排列(针对隐式方法的雅可比矩阵)
canSetMaximalStepSize 是否可设置最大积分步长(针对变步长方法)
canHitExpectedOutputTime 是否可通过一步或多步精确积分到(命中)预期输出时间,而不是越过输出时间然后插值(针对变步长方法)
canResetWorkData 是否可获取/设置工作数据,若用户实现了获取/设置工作数据的算法函数,则设置为 mwsTrue
canSerializeWorkData 是否可序列化/反序列化工作数据,若用户实现了序列化/反序列化工作数据的算法函数,则设置为 mwsTrue

# 积分算法指令

typedef struct
{
     MwsBoolean  returnDerivative
MwsBoolean  hitExpectedOutputTime;
MwsBoolean  maximalStepSizeDefined;
MwsReal      maximalStepSize;
}MwsIVPSolverCommand;
功能 属性
积分算法指令 returnDerivative 是否要求返回积分到达时间的导数值
hitExpectedOutputTime 是否要求通过 1 步或多步精确积分到(命中)预期输出时间,仅针对变步长方法
maximalStepSizeDefined 是否指定最大积分步长,仅针对变步长方法
maximalStepSize 最大积分步长,仅针对变步长方法

# 积分算法设置

typedef struct
{
MwsReal       initialStepSize;
MwsReal*      relativeTolerance;
MwsReal*      absoluteTolerance;
MwsBoolean    scalarTolerance;
}MwsIVPExperiment;

MwsIVPExperiment 用于描述积分算法的设置(实验参数),主要针对变步长算法。

功能 属性
积分算法设置 initialStepSize 初始积分步长,即第一个积分步的步长,仅适用于变步长方法。如果用户在仿真设置界面中指定了初始积分步长,则仿真时使用用户设定的值,否则由求解器内部提供默认值
relativeTolerance 变步长算法进行步长控制时的相对误差容限
absoluteTolerance 变步长算法进行步长控制时的绝对误差容限
scalarTolerance 是否为标量误差。若为标量误差,则 relativeTolerance 和 absoluteTolerance 是单一值;若为向量误差,则它们都是数组

# 积分算法的算法函数集

typedef struct
{
MwsIVPSolverInstantiate           solverInstantiate;
MwsIVPSolverGetFeature            solverGetFeature;
MwsIVPSolverInitialize            solverInitialize;
MwsIVPSolverDoStep                 solverDoStep;
MwsIVPSolverInterpolate           solverInterpolate;
MwsIVPSolverSetDebugLogging      solverSetDebugLogging;
MwsIVPSolverFreeInstance          solverFreeInstance;
MwsIVPSolverGetWorkData           solverGetWorkData;
MwsIVPSolverSetWorkData           solverSetWorkData;
MwsIVPSolverSerializeWorkData    solverSerializeWorkData;
MwsIVPSolverDeSerializeWorkData solverDeSerializeWorkData;
} MwsIVPSolverFcns;

MwsIVPSolverFcns 描述了积分算法包括哪些算法函数,用于注册积分算法。如何实现算法函数,请参见算法函数的标准接口节对各个函数的详细介绍。

功能 属性
积分算法使用的算法函数集,用于注册积分算法 solverInstantiate 创建积分算法实例的函数
solverGetFeature 获取积分算法特性的函数
solverInitialize 积分算法初始化的函数
solverDoStep 进行单步积分的函数
solverInterpolate 状态变量插值的函数
solverSetDebugLogging 设置调试日志输出选项的函数
solverFreeInstance 释放积分算法实例的函数
solverGetWorkData 获取工作数据的函数
solverSetWorkData 设置工作数据的函数
solverSerializeWorkData 工作数据序列化的函数
solverDeSerializeWorkData 工作数据反序列化的函数

# 算法函数的标准接口

算法函数由用户实现,其中必须实现的函数由求解器按如图所示的流程进行调用。

# 创建积分算法的实例

typedef MwsIVPSolverType (*MwsIVPSolverInstantiate)(MwsSize n, const MwsIVPCallbackFns* cbfns, MwsBoolean loggingOn, MwsLocale locale, MwsUserContext ctx);

以上是创建积分算法实例函数的标准接口,其中所有参数均为输入参数。

功能 参数 返回 必须 备注
创建积分算法实例 [in] n 状态变量个数 若成功,返回创建的实例;若失败,返回空指针 MWSnullptr 用户实现
[in] cbfns 求解器提供的回调函数
[in] loggingOn 日志是否输出(若为 mwsFalse,日志输出失效,无需提供日志输出回调函数)
[in] locale 区域设置,表示中文或其他语言日志输出。实际为枚举类型,只有两个枚举成员:mwsChinese 和 mwsEnglish
[in] ctx 用户数据环境,是调用求解器提供的回调函数时必须的输入参数

# 获取积分算法的特性

typedef MwsInteger (*MwsIVPSolverGetFeature)( MwsIVPSolverType inst, MwsIVPSolverFeature* solverFeature, void* reserve);

以上是获取积分算法特性函数的标准接口。一般情况下,用户可以直接以 MyEuler 算法实现的 UserIVPSolverGetFeature 函数为模板。

功能 参数 返回 必须 备注
获取积分算法的特性 [in] inst 积分算法实例 0(正常)、< 0(错误,提供错误编号)、> 0(正常,提供信息编号) 用户实现
[out] solverFeature 积分算法特性
[out] reserve[in] loggingOn 保留参数

# 初始化积分算法实例

typedef MwsInteger (*MwsIVPSolverInitialize)( MwsIVPSolverType inst, MwsReal t0, const MwsReal y0[], const MwsReal yp0[], MwsBoolean reinit, const MwsIVPExperiment* setting, void* reserve);

以上是算法实例初始化函数的标准接口。

功能 参数 返回 必须 备注
向前积分一步 [in] inst 积分算法实例 0(正常)、< 0(错误,提供错误编号)、> 0(正常,提供信息编号) 用户实现
[in] t0 初始时间
[in] y0 状态变量初始值
[in] yp0 导数变量初始值
[in] reinit 是否为重新初始化(例如事件会触发积分器重启)
[in] setting 积分设置信息(定步长算法忽略此参数,可传入空指针)
[in] reserve 保留参数

该函数主要用于为积分器的工作数据赋初值。参数 setting 包含初始步长和误差容限等积分设置信息,可用于初始化积分器。参数 reinit 用于标记是否为重新初始化,当积分器被重新初始化时,reinit 的值为 mwsTrue。

一种典型的积分器重新初始化场景是 Modelica 模型中包含以下语句:

when abs(x) > 2 then 
reinit(x, 0); 
end when;

该 Modelica 代码的含义是当状态变量 x 的绝对值大于 2 时,就会执行 reinit 函数,将 x 的值重新初始化为 0。

# 执行单步积分

typedef MwsInteger (*MwsIVPSolverDoStep)( MwsIVPSolverType inst, const MwsIVPSolverCommand* cmd, MwsReal tcur, MwsReal tout, MwsReal* tret, MwsReal yret[], MwsReal ypret[], void* reserve);

以上是算法实例执行单步积分函数的标准接口。该函数让算法实例从 tur 时刻出发,向前积分一步,到达 tret 时刻,并计算 tret 时刻的状态变量值 yret。此外,还要根据积分指令 cmd 的内容,判断是否需要输出 tret 时刻的状态变量导数值 ypret。

对于变步长算法,tout 只是求解器期望的向前积分一步到达的时间,实际到达时间 tret 可能与 tout 不相等。因此在实现单步积分时,用户可以忽略 tout。而对于定步长算法,tret 必须严格等于 tout,可通过 h=tout-tur 来计算积分步长。

功能 参数 返回 必须 备注
积分算法初始化 [in] inst 积分算法对象 0(正常)、< 0(错误,提供错误编号)、> 0(正常,提供信息编号) 用户实现
[in] cmd 积分指令
[in] tcur 当前时间,即向前积分的起始时间
[in] tout 预期输出时间
[out] tret 向前积分一步实际到达(返回)时间(对于定步长方法 *tret=tout)
[in | out] yret 状态变量值(in:tcur 时刻的状态变量;out:*tret 时刻的状态变量)
[in | out] ypret 导数变量值(由 cmd 参数中的 returnDerivative 决定是否需要计算并返回 *tret 时刻的导数)
[in] reserve 保留参数

# 状态变量插值

typedef MwsInteger (*MwsIVPSolverInterpolate)( MwsIVPSolverType inst, MwsReal tout, MwsReal yout[], MwsReal ypout[], void* reserve);

以上是状态变量插值函数的标准接口。该函数插值计算 tout 时刻的状态变量值 yout。若实参 ypout 不是空指针 MWSnullptr,则还需计算 tout 时刻的状态变量导数值 ypout。

插值必须确保为内插,因此在积分器 inst 中需要定义相应的成员,用于缓存上一积分步到达的时间及对应的状态变量值,以及当前积分步到达的时间和状态变量值。换句话说,需要缓存积分区间端点的数据,以构造插值函数。插值函数的精度决定了仿真结果的精度,因此需要用户精心实现。

功能 参数 返回 必须 备注
获取状态变量的插值结果 [in] inst 积分算法实例 0(正常)、< 0(错误,提供错误编号)、> 0(正常,提供信息编号) 用户实现
[in] tout 插值输出时间
[out] yout 插值输出时间的状态变量值
[out] ypout 插值输出时间的导数变量值
[in] reserve 保留参数

# 设置调试日志输出选项

typedef MwsInteger (*MwsIVPSolverSetDebugLogging) ( MwsIVPSolverType inst, MwsBoolean loggingOn, MwsSzie nCategories, const MwsString categories[]);

以上是状态变量插值函数的标准接口,用于获取求解器传下来的调试日志输出选项。

功能 参数 返回 注意 必须 备注
设置调试日志输出的选项 [in | out] inst 积分算法实例 0(正常)、< 0(错误,提供错误编号)、> 0(正常,提供信息编号) logging == mwsTrue && nCategories == 0,全部调试日志均输出;
logging == mwsTrue && nCategories > 0,categories 指定的调试日志类型输出
用户实现
[in] loggingOn 日志是否输出
[in] nCategories 日志类别数量
[in] categories 日志类别

其中,日志类别 categories 是字符串数组,nCategories 是categories 的数组长度。当用户在界面仿真设置的调试选项卡中勾选任意选项时,categories 数组就会自动添加相应的字符串,二者之间具体对应关系如下表所示。

勾选的界面选项 对应的日志类别字符串
正常的警告信息 “logStatusWarning”
仿真中的事件 “logEvents”
动态状态变量的选择 “logDynamicStateSelection”
非线性求解结果 “logNonlinearSystems”
非线性迭代过程 “logNonlinearIterations”
非线性统计信息 “logNonlinearStatistics”
线性求解结果 “logLinearSolutions”
线性奇异解 “logSingularLinearSystems”
混合方程迭代过程 “logMixedIterations”
错误信息中包含函数调用环境 “logFunctionCall”

图2 2仿真设置的调试选项

若用户有自定义日志输出的需求,可以在此函数内拿到 categories 数组的值,并将其保存在积分算法实例 inst 的成员中。然后,用户可以在其他算法函数中,按需调用回调函数 logger 来输出日志。logger 函数的接口请参见日志输出函数

例如,假设用户创建的积分算法对象为结构体 userData,并且已在本函数中将 categories 数组的值存储在算法实例的成员 logSet 中,用户可以在自定义算法中适当的位置实现类似以下的代码段。

int i = 0;
int length = sizeof(userData->logSet) / sizeof(userData->logSet[0]);
for (i=0;i<length;i++)
{
if (strcmp(userData->logSet[i], "logStatusWarning")
{
    userData->cbfns.logger(userData->userContext,mwsOK, "logStatusWarning", "Hello, Logger");
}
}

该代码段用于检查用户是否启用了仿真设置中的“正常的警告信息”选项。如果启用,则调用 logger 打印自定义的日志信息。

# 释放积分算法实例

typedef void (*MwsIVPSolverFreeInstance) ( MwsIVPSolverType inst);
功能 参数 必须 备注
释放积分算法实例 [in] inst 积分算法实例 用户实现

该函数用于释放之前为积分算法实例分配的内存。

# 获取积分器工作数据

typedef MwsInteger (*MwsIVPSolverGetWorkData) ( MwsIVPSolverType inst, MwsIVPWorkData* workData);
功能 参数 返回 必须 备注
获取积分器的工作数据,用于求解状态的备份 [in] inst 积分算法实例 0(正常)、< 0(错误,提供错误编号)、> 0(正常,提供信息编号) 用户实现
[out] workData 积分器的工作数据

此函数用于将积分算法实例中的必要工作数据复制到新创建的实例,并保存在内存中,以便后续恢复求解状态。对于用户来说,若无使用此功能的需求,直接返回 0 即可。

# 设置积分器工作数据

typedef MwsInteger (*MwsIVPSolverSetWorkData) ( MwsIVPSolverType inst, MwsIVPWorkData workData);
功能 参数 返回 必须 备注
设置积分器的工作数据,用于求解状态的回退 [in] inst 积分算法实例 0(正常)、< 0(错误,提供错误编号)、> 0(正常,提供信息编号) 用户实现
[in] workData 积分器的工作数据

此函数用于将前述获取积分算法工作数据函数中保存的数据恢复到积分算法实例中,以重建求解状态。对于用户来说,若无使用此功能的需求,直接返回 0 即可。

# 积分器工作数据序列化

typedef MwsInteger (*MwsIVPSolverSerializeWorkData) ( MwsIVPSolverType inst, MwsIVPWorkData workData, MWsSize* size, MwsByte serializedData[]);
功能 参数 返回 注意 必须 备注
将积分器工作数据序列化为字节数组 [in] inst 积分算法实例 0(正常)、< 0(错误,提供错误编号)、> 0(正常,提供信息编号) 输入 serializedData 为空时(要求 size 非空),用于获取变量总数,由 *size 返回;
输入 serializedData 非空时(要求 size 亦非空,且 *size 存储变量总数),用于获取变量索引值,由 *serializedData 返回
用户实现
[in] workData 工作数据
[in | out]size 序列化生成的字节数组的长度
[out]serializedData 序列化生成的字节数组

此函数与获取积分器工作数据的函数类似,但主要区别在于,此函数获取的数据需要序列化并保存在磁盘文件中。

此函数专为 Sysplorer 的接续仿真功能设计。如果用户不需要该功能,可以直接让此函数返回 0。否则,需要实现该函数,将积分算法实例的工作数据序列化并存储为文件。通常情况下,仅需序列化算法实例中的必要工作数据,即在接续仿真时读取后能够完全恢复算法实例的工作状态并顺利继续仿真的数据。

# 积分器工作数据反序列化

typedef MwsInteger (*MwsIVPSolverDeSerializeWorkData) ( MwsIVPSolverType inst, MWsSize size, const MwsByte serializedData[], MwsIVPWorkData* workData);
功能 参数 返回 必须 备注
将字节数组反序列化为积分器工作数据 [in] inst 积分算法实例 0(正常)、< 0(错误,提供错误编号)、> 0(正常,提供信息编号) 用户实现
[in] size 序列化生成的字节数组的长度
[in] serializedData 序列化生成的字节数组
[out]workData 工作数据

此函数与设置积分器工作数据的函数类似,但主要区别在于,此函数读取的数据来自于磁盘文件。

此函数专为 Sysplorer 的接续仿真功能设计。如果用户不需要该功能,可以直接让此函数返回 0。否则,需要实现此函数,读取序列化函数保存的数据文件,反序列化之后赋给积分算法实例。

# 回调函数的标准接口

本节介绍积分算法可直接使用的回调函数的标准接口。

# 函数值计算

typedef MwsInteger (*MwsIVPFncEvaluation)(MwsUserContext ctx, MwsReal t, const MwsReal y[], const MwsReal yp[], MwsReal fval[]);

以上是函数值计算函数的标准接口,该函数可计算出以下两种形式的 f 函数值。

显式:

隐式:

功能 参数 返回 备注
函数值计算函数 [in] ctx 用户数据环境 0(正常)、-1(计算出错) 求解器提供
[in] t 时间
[in] y 状态变量值
[in] yp 导数值(对于显式问题,该参数为空指针)
[out]fval 函数值(对于显式问题,函数值即为导数值)

若输入参数 yp 为空指针,则计算显式的 f 函数;否则,计算隐式的 f 函数。

# 雅可比计算

typedef MwsInteger (*MwsIVPJacEvaluation)(MwsUserContext ctx, MwsReal t, const MwsReal y[], const MwsReal yp[], MwsReal cj, MwsReal pd[]);
功能 参数 返回 备注
雅可比计算函数 [in] ctx 用户数据环境 0(正常)、-1(计算出错) 求解器提供
[in] t 时间
[in] y 状态变量值
[in] yp 导数值(对于显式问题,该参数为空指针)
[in]cj 针对隐式问题的系数,pd=df/dy+cj×df/(dy ̇ )
[out]pd 偏导矩阵(对于显式问题,pd=df/dy,对于隐式问题,pd=df/dy+cj×df/(dy ̇ ))

# 内存分配函数

typedef void* (*MwsAllocateMemory)(MwsUserContext ctx, MwsSize nobj, MwsSize size);
功能 参数 备注
分配一段长为 nobj*size 字节的内存,并将返回该内存的指针 [in] ctx 用户数据环境 求解器提供
[in] nobj 元素的个数
[in] size 每个元素占用的字节数

使用示例:

eulerData->ynew=(*cbfns->allocateMemory)(userContext,n, sizeof(MwsReal));

ynew 是 MwsReal 类型的数组,长度为 n,上述语句为 ynew 分配相应的内存空间。

# 内存释放函数

typedef void (*MwsFreeMemory)(MwsUserContext ctx, void* obj);
功能 参数 备注
释放之前分配给 obj 的内存 [in] ctx 用户数据环境 求解器提供
[in] obj 被释放的对象

使用示例:

eulerData->callbackFns.freeMemory(eulerData->userContext, eulerData->ynew);

上述语句释放了分配给 ynew 的内存。

# 日志输出函数

typedef void (*MwsLogger)(MwsUserContext ctx, MwsStatus status, MwsString category, MwsString message);
功能 参数 备注
输出日志信息 [in] ctx 用户数据环境 求解器提供
[in] status 函数的返回状态
[in] category 日志类别字符串
[in] message 日志要输出的信息

其中,函数的返回状态属于枚举类型 MwsStatus,也定义在头文件%Sysplorer安装目录%\Simulator\Src\mws_common_decl.h中:

功能 枚举项
返回状态 mwsOK 正常
mwsWarning 警告。函数未按预期执行,但仿真可以继续。如:函数调用时机不恰当
mwsDiscard 丢弃。函数未按预期执行,但仿真可以继续。调整参数再次执行,可能达到预期效果。如:参数错误或参数值不合理
mwsError 错误,仿真过程不能继续,但可以重置到初始状态或某个正常求解状态,再继续仿真
mwsFatal 致命性错误。无论如何处理,仿真均不可继续

# 注册函数的模板

MwsStatus MwsRegisterExternalIVPSolver(void* inst)
{
    /* Register user defined IVP solver by calling following function. */
    /* MwsStatus MwsRegisterUserIVPSolver(void* inst, MwsString solverName, const MwsIVPSolverFcns* ivpSolverFcns) */
    /* you must provide valid callback functions in ivpSolverFcns */
    /* you can register several different IVP solvers */

    MwsStatus rst = mwsOK;
    MwsIVPSolverFcns ivpSolverFcns;
    MwsString solverName = "MyEuler";
    ivpSolverFcns.solverInstantiate = UserIVPSolverInstantiate;
    ivpSolverFcns.solverInitialize = UserIVPSolverInitialize;
    ivpSolverFcns.solverGetFeature = UserIVPSolverGetFeature;
    ivpSolverFcns.solverDoStep = UserIVPSolverDostep;
    ivpSolverFcns.solverInterpolate = UserIVPSolverInterpolate;
    ivpSolverFcns.solverSetDebugLogging= UserIVPSolverSetDebugLogging;
    ivpSolverFcns.solverFreeInstance= UserIVPSolverFreeInstance;
    ivpSolverFcns.solverGetWorkData = UserIVPSolverGetWorkData;
    ivpSolverFcns.solverSetWorkData = UserIVPSolverSetWorkData;
    ivpSolverFcns.solverSerializeWorkData= UserIVPSolverSerializeWorkData;
    ivpSolverFcns.solverDeSerializeWorkData= UserIVPSolverDeSerializeWorkData;

    rst = MwsRegisterUserIVPSolver(inst, solverName, &ivpSolverFcns);
    return rst;
}

以上是 MyEuler 算法实现的自定义算法注册函数,用户自定义其他算法时可直接套用此模板。注册积分算法主要包含四个步骤:

  1. 创建 MwsIVPSolverFcns 类型的结构体 ivpSolverFcns,该结构体用来存放积分算法实现的所有算法函数,参见积分算法的算法函数集

  2. 为积分算法命名,将算法名 "MyEuler" 赋给 solverName

  3. 将 MyEuler 算法实现的所有算法函数名,赋给ivpSolverFcns的相应成员。

  4. 调用求解器提供的 MwsRegisterUserIVPSolver 函数,将算法实例 inst、算法名称 solverName 和结构体 ivpSolverFcns 作为实参传入,以注册积分算法。

如果用户自定义了多个算法,不同算法的函数名不能重复,因此需要用户为每个自定义算法指定唯一的函数名。此外,用户还需要分别实现多个注册函数,每个函数负责注册一个算法,并在函数内部将 ivpSolverFcns 的成员与相应的算法函数名关联起来。