NDSolve

NDSolve[eqns,u,{x,xmin,xmax}]

求解函数 u 的常微分方程 eqns 的数值解,自变量 x 位于从 xminxmax 的范围内.

NDSolve[eqns,u,{x,xmin,xmax},{y,ymin,ymax}]

在矩形区域上求解偏微分方程 eqns.

NDSolve[eqns,u,{x,y}Ω]

在区域 Ω 上求解偏微分方程 eqns.

NDSolve[eqns,u,{t,tmin,tmax},{x,y}Ω]

在区域 Ω 上求解时间相关偏微分方程 eqns.

NDSolve[eqns,{u1,u2,},]

求解函数 ui.

更多信息和选项

  • NDSolve 是一个数字微分方程求解器,用 InterpolatingFunction 对象给出结果.
  • NDSolve[eqns,u[x],{x,xmin,xmax}] 给出 u[x] 而不是函数 u 本身的解.
  • 微分方程必须使用诸如 u'[x] 的导数表示,用 D 求解,而不是用 Dt 求得的总导数.
  • 偏微分方程也可能使用微分算子 Grad ()、Div (.)、Laplacian (2) 和 Curl () 指定. 通常这些算子用于 Inactive[op] 中以使得算子形式不用计算.
  • NDSolve 可以求解广泛的常微分方程,以及许多偏微分方程.
  • NDSolve 还可以用来求解时滞微分方程.
  • 在常微分方程中函数 ui 只能依赖与单个变量 t. 在偏微分方程中它们可以依赖于多个变量.
  • WhenEvent[event,action] 可能包含在方程 eqns 中,以指定当 event 变成 True 时,出现的 action.
  • 微分方程必须包括足够的初始条件或边界条件来完全确定 ui 的解.
  • 初始和边界条件通常用格式 u[x0]==c0u'[x0]==dc0 等表示,但是可能包含更复杂的方程.
  • c0dc0 等可以是列表,指定 u[x] 是一个具有向量值或者普通列表值的函数.
  • 周期性边界条件可以使用 u[x0]==u[x1] 指定.
  • 出现在初始或者边界条件中的点 x0 不需要位于搜索解的从 xminxmax 的范围内.
  • 边界值也可以使用 DirichletConditionNeumannValue 指定.
  • 在时滞微分方程中,初始历史函数以格式 u[x/;x<x0]==c0 给出,其中 c0 通常是关于 x 的函数.
  • NDSolve 中的微分方程可以包含复数.
  • NDSolve 可以求解许多微分代数方程,即某些 eqns 是纯代数的,或某些变量可以是隐式代数的.
  • ui 可以使有应变量的函数,不需要包含所有这样的变量.
  • 可以给出以下选项:
  • AccuracyGoalAutomatic所寻求的绝对精度的位数
    CompiledAutomatic表达式是否应该自动编译
    DependentVariables Automatic所有应变量的列表
    EvaluationMonitor None每当计算函数时需要计算的表达式
    InitialSeeding{}用于某些算法的播种方程
    InterpolationOrder Automatic最终输出的连续性度数
    MaxStepFraction 1/10每个步骤所覆盖求解范围的最大比例
    MaxSteps Automatic采用步骤的最大数目
    MaxStepSize Automatic每个步骤的最大步长
    Method Automatic使用的方法
    NormFunction Automatic误差估计使用的范数
    PrecisionGoalAutomatic所寻求的精度的位数
    StartingStepSize Automatic初始步长
    StepMonitor None每进行一个步骤时,要计算的表达式
    WorkingPrecision MachinePrecision内部计算使用的精度
  • NDSolve 会调整步长,使解中的估计误差在 PrecisionGoalAccuracyGoal 指定的容差内.
  • 选项 NormFunction->f 指定每个 ui 的估计误差需要用 f[{e1,e2,}] 进行组合.
  • AccuracyGoal 实际上指定了求解时每一步所允许的绝对局部误差,而 PrecisionGoal 指定相对局部误差.
  • 如果解的值接近 0,而且要准确地逼近该解, 则 AccuracyGoal 的设置应该较大,或是 Infinity.
  • AccuracyGoalPrecisionGoal 的缺省设置 Automatic 等于 WorkingPrecision/2.
  • MaxSteps 的默认设置 Automatic 估计 NDSolve 所采用的最大步骤数目,取决于开始和结束时间,以及步长的估计值. 如果无法得到这些,就采用固定的步骤数目.
  • MaxStepFraction 的设置指定 NDSolve 采用的最大步长应该是每个独立变量值范围的几分之几.
  • DependentVariables->Automatic 设置下,NDSolve 通过分析给定的方程尽可能的确定应变量.
  • 取决于方程的类型,NDSolve 通常通过几个不同的阶段求解微分方程. 设置 Method->{s1->m1,s2->m2,},阶段 si 由方法 mi 处理. 使用的实际阶段和它们的阶数基于所要解决的问题,由 NDSolve 决定.
  • 可能的求解步骤是:
  • "TimeIntegration"微分方程组的时间积分
    "BoundaryValues"常微分方程边界值的解
    "DiscontinuityProcessing"用于处理不连续微分方程的符号式处理
    "EquationSimplification"用于数值计算的方程的化简
    "IndexReduction"用于微分代数方程的符号式指标化简
    "DAEInitialization"用于微分代数方程的一致初始化
    "PDEDiscretization"用于偏微分方程离散化
  • 设置 Method->m1 或者 Method->{m1,s2->m2,},假定方法 m1 用于时间积分,因此 Method->m1 等价于 Method->{"TimeIntegration"->m1}.
  • Method 选项的可能显式时间积分设置包括:
  • "Adams"阶数从 1 到 12 的预估-校正 Adams 方法
    "BDF"阶数从 1 到 5 的隐式向后微分公式
    "ExplicitRungeKutta"从 2(1) 到 9(8) 的 RungeKutta 方法的自适应嵌入对
    "IDA"DAE 的隐式反向微分公式
    "ImplicitRungeKutta"任意次数的隐式 RungeKutta 方法系列
    "SymplecticPartitionedRungeKutta"对可分 Hamiltonian 系统交叉存取的 RungeKutta 方法
  • 在设置 Method->{"controller",Method->"submethod"}Method->{"controller",Method->{m1,m2,}} 下,可能的控制方法包括:
  • "Composition"组建一个子方法列表
    "DoubleStep"用双倍步长方法来自适应步长
    "EventLocator"对指定事件的响应
    "Extrapolation"用多项式插值方法来自适应阶数与步长
    "FixedStep"用一个固定的步长
    "OrthogonalProjection"投影解来满足正交约束条件
    "Projection"投影解来满足普通约束条件
    "Splitting"分割方程并用不同的子方法
    "StiffnessSwitching"检测到刚性时,从显式到隐式的切换
  • 主要用作子方法的方法包括:
  • "ExplicitEuler"向前欧拉方法
    "ExplicitMidpoint"中点法则
    "ExplicitModifiedMidpoint"有 Gragg 平滑化的中点法则
    "LinearlyImplicitEuler"线性隐式 Euler 方法
    "LinearlyImplicitMidpoint"线性隐式中点法则
    "LinearlyImplicitModifiedMidpoint"线性隐式 Bader平滑化的中点法则
    "LocallyExact"对局部精确符号解的数值近似
  • 设置 InterpolationOrder->All 指定 NDSolve 所产生解的插值阶数要与所用方法的阶数相同. »

范例

打开所有单元关闭所有单元

基本范例  (7)

求解一阶常微分方程:

画出解的图形:

画出函数和它的导数的图形:

求出特定值:

二阶非线性常微分方程:

绘制函数和它的前两阶导数:

常微分方程组:

求解一维热方程:

方程的另一种形式:

Disk 上求解泊松方程:

Disk 上具有正弦边界条件的最小曲面.

求解一个区域上耦合非线性正弦-戈登方程:

范围  (28)

常微分方程  (8)

指定任意阶的方程. 自动约化成正规形式:

直接对解微分,来制作一个相图:

直接指定一个方程组:

求解向量值函数:

绘制解的 4 个分量:

用不同而等价的方法将一个谐振子描述为一个二阶方程:

或描述为一个一阶方程组:

用从初值条件推导出维度的向量变量:

用矩阵值变量计算基本矩阵解:

与具体解的比较:

定义一个范德波尔方程:

默认求解器自动处理的解的刚性行为:

其它方法可能不能求解:

方程可能有多个不同的解:

y[x] 是连续的,因为它对分段函数积分一次:

y[x] 是可微的,而 y'[x] 仅仅是连续的:

偏微分方程  (5)

非线性一维平流扩散方程:

定义抛物型和双曲型混合的偏微分方程组:

周期性边界条件下的空间二维非线性正弦-高登方程:

绘制最后时刻的解:

绘制解的径向截面的时间演化图:

在有缝隙的区域上求解波动方程:

求解在弧形边界上具有周期性边界条件的泊松方程:

可视化解:

边界值问题  (5)

一个非线性中点边界值问题:

求解非线性扩散方程 ,其中狄利克雷和诺伊曼条件开始于 的初始种子:

可视化结果:

求解非线性方程 ,其中狄利克雷和诺伊曼条件开始于 的初始种子:

可视化结果:

求解带有狄利克雷边界条件的复值非线性反应方程

可视化结果:

求解带有非线性负荷项 的边界值问题:

可视化结果:

时滞微分方程  (2)

解一个有两个常数时滞和初始历史函数为 的时滞微分方程:

不连续性从 以与时滞相等的间隔传播:

考查一个线性时滞微分方程的稳定性:

混合与不连续方程  (4)

使用自动事件生成,具有不连续右侧的微分方程:

右侧在规则时间区间内改变的微分方程:

每当它穿过负 轴时,将解沿着 轴反射:

具有滑动模式的周期性解:

微分-代数方程  (4)

为代数约束条件,求解微分方程:

求解含有奇异质量矩阵的 ode 方程组:

雅可比矩阵的行列式为零; 的导数不存在:

将方程重写为含有代数约束条件的 DAE:

求解含有奇异质量矩阵的 DAE 微分方程组:

检查质量矩阵的奇异性:

通过将 DAE 转换为指标为 0 的方程组来求解 DAE,并将其作为具有不变量的 ODE 进行求解:

推广和延伸  (1)

函数的名称不一定是符号:

选项  (31)

AccuracyGoal 和 PrecisionGoal  (1)

默认情况下求解天体力学方程,它对初始条件很敏感:

较高的准确度和精度目标给出不同的结果:

增加目标值使正确的解进一步扩展:

DependentVariables  (1)

建立一个较大的方程组:

求解所有应变量,但仅保存 x1 的解:

EvaluationMonitor  (2)

计算的全部数量:

相邻运算间的距离;负距离表示一个被拒绝的步骤:

InitialSeedings  (2)

为边界值问题指定初始种子 0:

指定取决于空间坐标的初始种子:

InterpolationOrder  (1)

InterpolationOrder->All 获得与方法阶数相同的插值:

这比使用默认的插值阶数,耗费更多的时间:

但它在步骤之间表现更好:

MaxStepFraction  (1)

相对小的积分间隔上的特征可能会丢失:

MaxStepFraction 确保这些特征不会丢失,使其不依赖于积分区间的大小:

MaxSteps  (1)

积分在未达到所需要的积分区间时停止:

需要用更多步骤来使解更精确:

在相平面上绘制解:

求一个振荡器的无限积分时达到了最大步骤数:

可以要求更多步骤:

MaxStepSize  (2)

默认的步长控制可能会导致一个突变性特征的遗失:

一个较小的 MaxStepSize 设置确保 NDSolve 捕捉到这个特征:

尝试计算小于 的正整数的数目时遗漏了一些事件:

设置足够小的 MaxStepSize 来确保没有事件遗漏:

Method  (14)

TimeIntegration  (5)

指定使用显式 RungeKutta 法求解微分方程的时间积分:

指定使用8阶显式 RungeKutta 法求解时间积分:

指定使用显式 Euler 法求解微分方程的时间积分:

使用默认求解方法显示相邻步骤中 x 值之间的差异:

显式 RungeKutta 法更改步长的频率较低:

插补法倾向采用很大的步长:

PDEDiscretization  (3)

Burgers 方程的解有可能会变得很陡峭,导致数值不稳定性:

指定足够精细的空间离散化来求解波的前端:

前端形成后,解快速衰减:

指定使用有限元法进行空间离散化:

在细化网格上求解瞬态薛定谔方程:

绘制更高精度的解:

BoundaryValues  (1)

求解边界问题:

在默认选项下,该方法找到平凡解:

指定 "Shooting" 方法的不同初始条件,以求得不同解:

DiscontinuityProcessing  (1)

NDSolve 自动对诸如 Sign 的不连续函数进行处理:

如果关闭该处理过程,NDSolve 在不连续点可能失败:

在一些时间积分方法下,解可能是相当不准确的:

求解的一个等价方式是使用 "DiscontinuitySignature":

实际上解是相同的:

当解处于滑动模式,不连续性特征(signature)是0:

EquationSimplification  (2)

因为平方根函数不够平滑,因此求解无法完成:

通过形成残差并且以微分代数方程组求解,可以找到一个解:

通过指定 的一致值,可以给出另一个解分支:

设置子选项 "SimplifySystem"->True 下,NDSolve 对具有充分简单格式的分量使用符号解:

IndexReduction  (1)

使用指标化简的约束钟摆的指标为 3 的公式:

默认方法只能求解指标为1的问题:

由符号式指标化简产生的问题可以被解决:

使用化简为指标0和投影方法求解,以满足约束条件:

绘制时间步骤上两个解的隐式能量约束条件的图线:

DAEInitialization  (1)

对初始化,使用向前搭配(collocation),以避免0处 Abs 项的问题:

NormFunction  (1)

使用不同误差估计范数,绘制确切的解的误差:

最佳解的图形:

StartingStepSize  (1)

对于较大的间隔,起点附近的短期特征可能会遗漏:

在起始时设置足够小的步长,这样确保输入不会丢失:

StepMonitor  (3)

绘制求解过程中的每个步骤对应的点处的解:

求解步骤的全部数目:

相邻步骤间 x 值的差别:

WorkingPrecision  (1)

在 100 个周期上谐振子的解的误差:

当工作精度增加时,局部容差也相应增加:

在较大的工作精度下,某些时候 "Extrapolation" 方法十分有效:

100 个周期内谐振器的解中的误差:

当工作精度提高后,局部公差也相应增大:

工作精度较大时,"Extrapolation" 方法非常有效:

应用  (35)

常微分方程  (5)

对于双势阱中的一个例子,模拟 Duffing 方程:

解强烈依赖于初值条件:

Lorenz 方程 [更多详情]:

LotkaVolterra 捕食-食饵方程 [更多信息]:

相平面图形:

查看 GavrilovShilnikov 模型中蓝天灾难轨道的外观:

化简的三体问题 [更多详情]:

适用于大量不同初始条件的公式:

偏微分方程  (10)

PDE models overview 中可以找到各个领域的大量 PDE 模型以及详细的说明.

表面周期性加热时,土壤在深度 x 处的温度的简单模型:

周期边界条件下简单波的演化:

绘制解:

Wolfram 非线性波动方程 [更多信息]:

在二维空间中,Wolfram 非线性波动方程:

在非线性薛定谔方程中由一个周期性的潜在势阱扰动的孤子的配置文件:

使用 Stokes 方程计算一个变窄的通道内的流速场:

热源位于棒中,模拟温度场:

解 PDE:

可视化解:

定义瞬态声压场的模型变量 vars,模型参数为 pars

定义向右传播的声波 的初始条件 ics

设置方程,右侧为声硬边界条件:

解 PDE:

可视化时域中的声场:

给定化学物种在一种材料内的反应速率,模拟一维化学物种穿过不同材料的过程. 右侧和左侧分别受到质量浓度和流入条件的影响:

 del .(-d del c(x))+a c(x)^(︷^(           mass transport model              )) =|_(Gamma_(x=0))q(x)^(︷^( mass flux value  ))

设置稳态传质模型变量 vars

设置区域

指定传质模型参数物质扩散率 和在 区域中的反应速率

指定物种通量边界条件:

指定质量浓度边界条件:

设置方程:

解 PDE:

可视化解:

时滞微分方程  (1)

查看呼吸动力问题下 MackeyGlass 时滞微分方程的解:

混合微分方程  (5)

模拟一个弹跳器,在每次弹跳时,速度为上一次的 95%:

对球的弹跳步骤建模:

每次一个线性振荡子的解穿过负 轴时,沿着 轴求反射:

该复位振荡器的解表现出混乱的行为:

在反射点组成的直方图下,绘制负 轴上的解:

模拟一个正弦激励下的单自由度碰振系统:

模拟定时得到激励的阻尼振荡器:

轨迹最终趋于一个固定的轨道:

机械系统  (3)

在正交坐标系中对钟摆运动建模. 在力图下,使用牛顿第二运动定律推导控制方法

模拟系统:

对钟摆添加阻尼,以使得它随着时间减速下来:

钟摆在垂直固定点稳定下来:

通过修改约束条件 ,把拉杆变成一个刚性弹簧:

该解包含高频弹簧振荡:

对一个从水平面释放的单位质量和长度的双摆建模. 使用牛顿第二定律推导运动方程:

通过强制执行方程并且以不变量约束,模拟该系统:

可视化双摆运动:

一个位于移动的传送带上的盒状物体,一端被一根弹簧固定在墙上,使用不同的摩擦力 模型来模拟该系统,包括粘滞摩擦、库仑摩擦、Stribeck 摩擦和静摩擦. 比较使用不同模型所得的位置和速度:

物体的 Newton 方程:

粘滞摩擦与相对速度成正比

物体于弹簧的自然长度1处达到稳定状态:

库仑摩擦与相对速度的符号成正比

物体与传送带一起移动,直到弹簧的拉力足够大为止:

Stribeck 摩擦是精细化的库仑摩擦 F_(str)=gamma sgn(v) e^(-2 TemplateBox[{v}, Abs])

低速时变化幅度略微减小:

静摩擦使得物体处于不动状态,直到弹簧的拉力大于某个值 μ,该值取决于平面的粗糙程度. 使用离散变量 stuck,物体不动时将其设为1,其他情况下设为0:

检查如果物体相对于传送带处于静止状态,弹簧的拉力是否小于 μ

物体反复被粘在传送带上,又因为弹簧的拉力而滑开:

比较不同的模型:

电子系统  (6)

模拟时间 处 RLC 电路对电压 的阶跃的响应:

对连接,使用分量法与基尔霍夫定律:

模拟一个阶跃响应:

模拟时间 处 RLC 电路对电压 的阶跃的响应:

模拟阶跃响应:

显示阶跃响应:

模拟并联 RLC 电路的行为:

显示在恒定输入电流下的响应:

显示 R、L、C 元件中的电流和所得电压:

对晶体管放大器电路建模:

输入电压 发生正弦变化:

取决于 ,晶体管以非线性方式调度:

使用欧姆定律和基尔霍夫定律来确定每个节点的控制方程:

模拟奇异方程组:

晶体管相对 将电压 放大:

模拟一个利用脉宽调制的反馈控制信号 q[t] 将输入电压 vi 升压至想要的输出电压 vd升压直流-直流变换器

利用基尔霍夫定律得到上述电路的模型:

控制信号 q[t] 将定时把晶体管置于导通状态,导通时间长度为每个周期 τvi/vd

从低压 vi=24 升至高压 vd=36

模拟一个利用脉宽调制的反馈控制信号 q[t] 将输入电压 vi 升压至想要的输出电压 vo降压-升压复合型直流-直流变换器

利用基尔霍夫定律得到上述电路的模型:

控制信号 q[t] 将定时把晶体管置于导通状态,导通时间长度为每个周期的 vd/(vi+vd)

从低压 vi=24 升至高压 vo=36

从高压 vi=24 降至低压 vo=16

液压系统  (3)

当水流从一个水箱通过管子到另一个水箱时,对两个圆柱形水箱的水位高度变化建模:

使用压力关系 和质量守恒:

在 HagenPoiseuille 关系下,对穿过水管的水流建模:

模拟系统:

把第二个水箱按照漏水水箱建模:

由于第二个水箱漏水,两个水箱最终都会排光水:

当水通过管道从一个水箱流导另一个水箱时,对两个半球形水箱中的水位高度变化建模:

对三个水箱中的水位变化建模,以满足一个水箱对其他水箱输水:

使用 HagenPoiseuille 关系对穿过管道的水流建模:

来自第一个水管的水流速率等于另外两个水管的水流速率之和:

模拟系统:

把第三个水箱按照漏水水箱建模:

前两个水箱达到平衡状态,并且以相同速率排水:

化学系统  (2)

催化反应的动力学模型:

    

速率方程如下所示:

品种 abc 的浓度应该总是常量:

求解并且可视化三个品种的演变:

对持续不断和二氧化碳混合的两个品种 FLB 和 ZHU 的化学过程建模:

每单位体积的二氧化碳流入表示为:

速率方程给定为:

平衡方程给定为:

求解方程,并且判断 FLBZHUCO2ZLA 中的浓度改变:

属性和关系  (7)

微分方程的符号求解与数值求解:

JacobiSN 的定义方程:

数值计算在一个区间内不同点的积分值:

对于自变量的函数,NDSolve 实际上给出不定积分:

找出一个事件与找出解函数的一个根相关:

事件定位(Event location) 可准确而有效地找到根:

这使得 作为一个微分方程的 的函数:

的一个根:

求解等价的边界值问题:

使用 NDSolve 作为 SystemModel 的求解器:

根据仿真结果绘制变量:

使用 SystemModel 模拟更大型的分层模型:

绘制随着时间的推移储罐系统中的储罐液位:

可能存在的问题  (14)

针对许多 NDSolve 消息,有特定的消息参考页面. 请到理解出错消息工作流程中了解怎样访问这些页面.

数值误差  (4)

误差随着远离初始条件而趋于增加:

求数值解和精确解之间的差:

一个非线性方程的误差:

对高阶方法,默认的插值在步骤间可能有较大误差:

阶数与方法相应的插值会减少步骤间的误差:

有限元方法使用的一些算法不是确定性的. 这意味着在这些算法中使用了一些随机性,如果对相同的输入进行计算,结果会略有不同:

求解同一个 PDE 两次. 解可能存在细微的差别:

微分代数方程  (3)

NDSolve 不能自动处理指标大于1的方程组:

可以在降低方程组的指标后再求解高指标方程组:

这里是一个微分代数方程组:

,求解方程组:

如果在 的条件下无法找到解,NDSolve 可以改变指定的初始条件:

改变迭代的起始点以避免这种情况的发生:

NDSolve 仅限于指标1方程,但 时解的指标为2:

想要求解高指标方程组,将指标降至1:

默认方法可能无法收敛到默认的容差:

在较低的 AccuracyGoalPrecisionGoal 设置下,可求得一个解:

在默认的容差下,"StateSpace" 时间积分方法可以求解该问题:

偏微分方程  (4)

PDE models overview 中可以找到各个领域的大量 PDE 模型以及详细的说明.

定义一个非线性的偏微分方程:

空间离散化的基础是初始值,其变化小于终了值:

通过增加空间网格点的最小数量,您可以准确地计算出终了值:

以下图形显示了空间上更为复杂解的初始情形:

定义一个热方程,其初值是一个阶梯函数:

初值的不连续性可能导致过多的空间网格点:

设置较小的空间网格点数目导致一个基本上同样好的解:

定义一个有初值的拉普拉斯(Laplace)方程:

求解器仅对作为初始值 (柯西) 问题适定的方程可行:

不适定性表现为数值解的不稳定性:

边界值问题  (1)

这里求出边界值问题的一个平庸解:

您可以通过提供搜索解的初始条件来获得其它解:

定义未知函数  (1)

未知函数的定义可能影响计算:

清除未知函数的定义解决问题:

Wolfram Research (1991),NDSolve,Wolfram 语言函数,https://reference.wolfram.com/language/ref/NDSolve.html (更新于 2019 年).

文本

Wolfram Research (1991),NDSolve,Wolfram 语言函数,https://reference.wolfram.com/language/ref/NDSolve.html (更新于 2019 年).

CMS

Wolfram 语言. 1991. "NDSolve." Wolfram 语言与系统参考资料中心. Wolfram Research. 最新版本 2019. https://reference.wolfram.com/language/ref/NDSolve.html.

APA

Wolfram 语言. (1991). NDSolve. Wolfram 语言与系统参考资料中心. 追溯自 https://reference.wolfram.com/language/ref/NDSolve.html 年

BibTeX

@misc{reference.wolfram_2024_ndsolve, author="Wolfram Research", title="{NDSolve}", year="2019", howpublished="\url{https://reference.wolfram.com/language/ref/NDSolve.html}", note=[Accessed: 21-November-2024 ]}

BibLaTeX

@online{reference.wolfram_2024_ndsolve, organization={Wolfram Research}, title={NDSolve}, year={2019}, url={https://reference.wolfram.com/language/ref/NDSolve.html}, note=[Accessed: 21-November-2024 ]}