在应用Adams处理工程问题时,尤其面对复杂工况条件下,需要对求解器在仿真过程中进行更为细致和灵活的设置,此时,基于软件界面上的功能已经很难满足实际需求。CBKSUB子程序作为Adams的回调函数,其主要作用是帮助用户优化仿真的执行,可以对仿真进行控制和相应内存分配,还可以缓存仿真过程中计算的数值。
回调函数子程序
Adams求解器每次运行,其实会划分很多细小的阶段,比如完成模型读入、仿真求解命令开始、迭代运行开始、迭代终止、静力分析开始、传感器触发等等,CBKSUB就可以这些事件作为其触发的条件。Adams回调函数的架构,旨在为高级用户定制执行自定义解决方案的数值密集型模型,通过该架构的辅助代码,实现初始化内存、运行各类仿真计算、生成供其它用户子程序共享的结果数据以及优化自定义模型等。可以基于Fortran或者C/C++编写回调函数子程序CBKSUB,另外,拥有回调函数子程序的计算消耗可以忽略不记,除非用户在其中执行冗长的数值计算。
CBKSUB/id,priority=I,function=user(0),routine=libname::subname
其中,
●Priority,描述多个回调子程序存在时,它们的调用次序,有高优先级的回调函数先调用,如果两个有相同的优先级,相当于没有次序。该值没有默认值,可设置为任意整数。
●Function,设置为当前模式Adams求解器不给回调函数传递输入参数。
●Routine,用于指定所调用的库及函数名称。
回调子程序在仿真中,当每个预定义事件触发时进行调用。在回调函数内部,用户可编写代码进行准备计算,运行自定义解决方案等进行优化复杂Adams模型的工作。主要内容如下:
●回调函数总是由主线程调用;
●回调函数总是在仿真过程中预定义事件触发时调用;
●每次调用所需的三个参数是当前仿真时间,事件识别号和基于事件类型的数据数组。
这里以动力学仿真中的CBKSUB调用次序为例说明,与其它类型仿真相仿。当执行动力学仿真时,Adams求解器将当前仿真时间和最终仿真时间之间的时间跨度划分为一系列的输出步长。系统的状态被推进到每个输出步骤的末尾,在这个步骤中,从最内层的代码层触发生成输出的请求。然而,为了达到每个输出步骤的结束,求解器可能需要执行一组更小的时间步骤,以达到当前输出步骤的结束。也就是分为两个循环,内层的时间步骤求解和外层的输出步骤求解,每一个外层输出步骤的求解又划分为多个时间步骤求解,更进一步,按照数值求解的一般规则,每个时间步的求解又会进行多次迭代计算。因此,后面两图共同描述了整个程序流的过程,显示了对CBKSUB子程序的调用次序,其中左侧为外层输出步骤循环右侧为内层时间步骤循环。
图 1动力学分析回调函数调用次序
在左侧图中,首先,标有“Process SIM/DYN command”的块表示读取仿真命令输入参数的代码,命令信息用于确定求解器需要执行的输出步骤的数量。右侧图中所示的流程图由两个循环组成。外环控制动态集成的“时间步长”的数量,直到达到“输出步长”。内部循环控制推进模拟状态所需的校正器迭代次数,直到按照算法要求达到“时间步长”。这样从内到外,完成一次动力学求解,会有很多可以触发回调函数的事件发生,因此,用户可以根据需要利用对应的事件进行编码实现我们所需的功能。
对于CBKSUB程序的使用,需要注意其有赖于slv_cbksub_util.inc\slv_sbksub.inc及对应C/C++版本的文件支持,这些文件存储了事件映射信息、功能函数、仿真模式、命令编号等内容,可以在Adams安装路径下找到。
另外,Adams所提供的系列功能子程序基本都是可以被CBKSUB调用的,比如sysfnc和sysary,并且使用这两者时需要注意,如果期望获得的数据没有相关性,用户可以在指定事件中计算和缓存对应数值。
本文以Adams自带的二阶弹簧振子模型为例进行CBKSUB的说明,主要实现回调CBKSUB时,基于缓存数据的方式完成对Vforce的计算。本例将CBKSUB,VFOSUB,STOPERR等子程序写到一篇源文件中,之间有相互调用和数据传递,方便对有多个子程序混编的用户提供借鉴。另外,随着源文件复杂度的提升,程序中利用了许多Fortran的函数和功能,比如在CBKSUB,VFOSUB之间传递数据时,采用了Common功能,将每次事件触发调用回调函数的信息展现出来使用了Write函数等。如下列出源程序部分代码进行关键行说明:
include 'slv_cbksub_util.inc'//改行是进行CBKSUB使用所必须的文件
SUBROUTINE VFOSUB(id, time, par, npar, dflag, iflag, result)//改行是进行Vforce子程序定义的函数
COMMON/ FORCES/ VALUE, FLAG//定义公共参数,用于接收CBKSUB的数值
RETURN
END
SUBROUTINE STOPERR(id, mesage)//完成终止计算及信息展示的程序
RETURN
END
SUBROUTINE CBKSUB(time, event, data)//完成回调函数程序
include 'slv_cbksub.inc'//改行是进行CBKSUB使用所必须的文件
COMMON/ FORCES/ VALUE, FLAG//完成力值计算
FLAG = 0
IF( event.eq.ev_ITERATION_BEG ) THEN//判断是否为迭代开始事件
WRITE(MESSAGE,'(A,A,A,A,A,A,e10.3)')'Event', EVTNAME(event),
*' ', ANLYMOD(data(1)), ' ', ANLYMOD(data(2)), time
//针对每次迭代开始事件,进行相关信息的展示
RETURN
END
二阶弹簧振子模型,通过滑移副与大地关联后,确保系统自由度为2,再通过三个弹簧力建立相互之间力连接,下侧小球为part_2,上侧小球为part_3,分别在二者上施加Sforce_4和Vforce_1。
图 2二阶弹簧振子模型
由于要进行有回调函数的模型计算,因此,利用命令导航器找到如下功能进行设置:
图 3回调函数创建
双击后打开如下对话框进行设置,其中Routine框中的yescallback为前面源程序编译后所形成的动态链接库文件名称,通过双冒号指向回调函数的名称。
图 4回调函数定义窗口参数设置
图 5用户子程序编译后动态链接库
为了看到回调函数的作用,求解时需要利用外部求解功能,基于脚本仿真,并且将展示信息选中。
图 6仿真设置
完成计算后,有如下信息,仿真开始阶段由于编写的用户子程序中有判断语句
IF( event.eq.ev_ITERATION_BEG ) THEN,
因此,在信息窗口中展现了:
Eventev_ITERATION_BEGam_DYNAMICSam_INITIAL_CONDITI 0.000E+00,
进入动力学仿真工况的初始化条件计算阶段,时间为0表明初始这一时刻。待到动力学分析开始后,进入迭代环节时,展现如下信息:Eventev_ITERATION_BEGam_DYNAMICS am_DYNAMICS 0.500E-03,
进入动力学仿真工况的动力学计算阶段,时间项开始有数据,标明每个迭代的终止时钟,由于这里采用了e10.3的数据格式,时间数据按科学计数法保留小数点后三位,而迭代周期有可能小于这个量,故此列表中有相同量的表现。
图 7仿真计算流程中回调函数调用
回调函数作为Adams用户子程序中的一种常用功能,主要是利用了其与求解器更紧密的关联以及衍生出的多种操作方法,可以深入到一个仿真流程中的各种细微阶段,直到每个迭代的开始和结束都可作为其响应的事件,极大地拓展了对仿真流程控制的应用广度和深度。上述实例仅仅展示了对迭代开始事件的操作和信息展示,类似这种可操作的事件还有40余种,包括用于变拓扑相关的传感器触发事件都可进行回调函数的使用,而传感器触发事件后的动作又有很多种,因此,从使用角度的丰富性而言,回调函数可以发掘出许多意想不到的应用场景。
后继更新基于回调函数以及传感器触发事件相结合在变拓扑分析方面的使用。