TRNSYS新建部件 C++实现计算 VS编译静态库 测试
转载请注明出处。
笔者最近在参加一个与自己专业几乎不相关的竞赛,这个竞赛project的选题与中央空调运行参数的节能优化有关。由于笔者及他的项目组并无财力购置一套中央空调系统,且几乎不会有某个单位的中央空调会出借给一个极其不专业的团队做实地试验,因此,对于空调系统的数学建模和仿真是必要的。
在前期文献调研的过程中,注意到有两篇论文完美地做出了我们要做的东西与我们的方向比较吻合,而它们均用一个名为TRNSYS的软件实现他们的数学模型。于是笔者和他的项目组一拍脑袋,决定也用这个软件实现我们抄下来的数学模型。在安装、配置好这个TRNSYS软件后,笔者和他的项目组遇到了他们宿命的问题:这个软件咋用?
【摘要】
本文主要包含如下内容:
- 如何在TRNSYS下新建一个部件?怎样设置部件的I/O变量?
- 如何使用C++为部件编写计算流程并导入TRNSYS?
看到fortran我头都大了 - 如何测试新建的部件是否可以运行?(之后更)
【0. 运行环境】
操作系统:Windows10 专业版 20H2 x64 19042.928
TRNSYS:18.02.0002
Visual Studio:Visual Studio Community 2019 版本 16.8.6
【1. 新建部件】
打开Simulation Studio,点击File->New
,显示如下对话框。选择New Component(TRNSYS TYPE)
,这代表新建一个TRNSYS部件。
事实上,这里的“部件”就是一个数学模型,它可以类比为面向对象程序设计中“类”的概念。而在具体的仿真项目中,如果指定仿真所需的参数,这些数学模型就可以被实例化为一个个“对象”,而不同的“对象”之间可以通过input和output实现交互,完成仿真任务。
设置TYPE NUMBER
如下所示就是新建部件的窗口,在这里我们需要关注的地方是TYPE NUMBER,其实就是数学模型的识别号码。识别号码的选择是随意的,但是需要注意的是:不要与TRNSYS库中已有模型的识别号码重复,否则在后续的运行过程,可能无法正确调用你自己编写的模型。
如果有闲心,把部件名,作者,创建日期啥的改一改也挺好。
提示:可以利用Windows自带的文件搜索功能查看TRNSYS内置的模型的TYPE NUMBER。首先转到TRNSYS安装目录下,搜索TYPE*.tmf
,即可列出所有内置类型的TYPE NUMBER,自定义时小心避开即可。
设置变量
接下来,就要设置这个模型包含的变量了。如上图所示,单击Variable选项卡顶端的Variables (Parameters, Inputs, Outputs and Derivatives)
。出现如下图所示的变量定义窗口,此时就可以为模型添加变量了。
笔者使用一种较为原始的方法添加变量——那就是一个一个地输入。如果有什么更好的方法欢迎留言qaq。
还是把笨办法讲一遍吧:点击add
新建一个变量,然后点击modify
来修改变量的属性。
各个属性的说明如下:
-
Name:变量名
-
Symbol:变量符号(没有改过)
-
Role:控制变量的输入输出性质。我们关注变量有三类:input(输入)、output(输出)和parameter(内部参数)。输入和输出是不同模型之间交互的桥梁,而内部参数则代表了模型本身的特性。
-
Type:变量的数据类型。可选实数型(real),整数型(integer),布尔型(boolean)或是字符串(string)。
-
Dimension:变量所代表的物理量。如pic所示,这一栏有很多可选项,例如温度、流量、功率等。
-
Units:描述Dimension物理量所用的单位制。感觉Dimension和Units主要是方便人类使用者记忆而设置的属性。可能或许还带一点类型检查和单位转换的功能在里面。毕竟对于机器而言,数据只是数字而已。
-
Minimum,Maximum和Default Value:字面意思啦。
保存
把上述Type Number和Variables设置好,就可以保存了。点击File->Save
,将部件的tmf
文件保存在方便调用的地方。保存完成后不要急着关闭窗口,下一步有用。
【2. 编写计算程序】
在上一步创建模型的过程中,我们只是定义了模型的外部框架,而内部计算过程则没有涉及。
那么如何把计算过程嵌入模型呢?TRNSYS通过从外部加载函数库(DLL)的方式完成模型内部的计算。当然,对于TRNSYS库中自带的模型,官方已经写好并打包好函数库了,因此在软件中可以直接使用这些库。而自己建立的模型就必须由自己来完成写代码、编译打包的工作了。
导出程序骨架
在上一步保存文件之后,不要关闭(当然关闭了再打开也没问题),点击File->Export as...->C++
就可以生成该模型的C++骨架。为了方便,我们可以把生成的C++骨架文件改名为TYPEX.cpp
。其中X
即为设置的TYPE NUMBER。
在开始编程之前,我们可以先来研究一下这个骨架的结构。
程序骨架的结构
忽略掉注释、函数体等细节,我们的文件结构大概是这样的:
#include <cmath>
#include <fstream>
#include <algorithm>
#include "TRNSYS.h"
extern "C" __declspec(dllexport) void TYPEX(void) {...}
我们可以发现,模型导出的C++骨架实质上就是一个函数库的源代码。函数名TYPEX中的X就是在上一步设置的TYPE NUMBER。
据此我们也可以猜测,当模型需要进行运算时,TRNSYS就会在库中寻找对应的TYPEX()
函数,完成运算过程。这也解释了为什么TYPE NUMBER不能重复。因为如果模型的TYPE NUMBER相同,对应函数名也会相同,TRNSYS就可能会调用错误的函数。
然后,我们展开函数细读,发现函数体就是做了一些准备工作。比如说当第一次调用该函数(仿真开始)时做好初始化,最后一次调用函数(仿真结束)时要做什么清理工作之类的。
在函数体的末尾,我们可以发现输入变量和输出变量已经被定义而且获取了:
//ReRead the Parameters if Another Unit of This Type Has Been Called Last
index = 1; param1 = getParameterValue(&index);
index = 2; param2 = getParameterValue(&index);
//Get the Current Inputs to the Model
index = 1; input1 = getInputValue(&index);
index = 2; input2 = getInputValue(&index);
于是我们就可以愉快地直接使用定义好的input
和parameter
型的变量了。
之后可以看到这样一段注释:
// Perform All of the Calculations Here
// TODO:add algorithm
// double result1=42;
// index = 1;
// setOutputValue(&index, &result1);
这段话的意思是:在这里进行模型内部所有的运算。还给了一段很简单的示例代码,示范了如何设置模型的输出:
double result1 = 42;
index = 1;
setOutPutValue(&index, &result1);
可以看到,关键点就是setOutputValue()
函数,其中index
参数为需要设置输出的序号,result1
参数为需要设置的值。
接下来就愉快地搬砖吧~
【3. 编译计算程序】
创建VS项目
首先打开Visual Studio,选择“创建新项目”,在接下来进入的页面中,选择“空项目”,设置好名称,项目就建好了。为了方便,建议设置项目名称为“TYPEX”。
细心的读者可能会发现,在创建新项目时,我们也可以选择“动态链接库(DLL)”这一选项来新建一个目标为dll文件的项目。可能这些读者会有这样的疑问:为什么不选择这一项呢?当然,选择这一类项目也能达到我们的目标。但是在建好的项目中,微软会给你“贴心”地准备一些不需要的文件。例如预编译头pch.h
,如果保留它,项目就显得不那么整洁;如果不保留它,还要在项目选项中额外选择一项“不使用预编译头”选项,否则就可能会编译错误。因此,与其接受冗余的方便,不如自己配置一个空项目来得清晰。当然这只是笔者的一种偏好。
准备必要的文件
由于选择时选择的是“空项目”,因此我们得到的项目中没有头文件,也没有源文件。此时就需要我们自己导入或是新建源文件和头文件。在这里笔者选择的方式是新建空的源文件和头文件,之后再用自己的文件替换掉这些空文件。
首先,我们需要添加空的TRNSYS.h
和TYPEX.cpp
文件到VS项目中。这么做的目的是让Visual Studio为这两个文件建立索引,以便在编译时找到它们。
-
添加头文件
在右侧“解决方案资源管理器”窗口中,右键单击“头文件”,选择
添加->新建项
(如pic) 。然后在弹出的窗口之中选择“头文件”,在“名称”一栏中,改名叫TRNSYS.h
即可。
-
添加源文件
在右侧“解决方案资源管理器”窗口中,右键单击“源文件”,选择
添加->新建项
(如pic) 。然后在弹出的窗口之中选择“C++文件”,在“名称”一栏中,改名叫TYPEX.cpp
即可,注意TYPEX.cpp
就是之前生成的C++骨架文件名,X
是TYPE NUMBER。
然后,就是复制文件的过程了。将刚刚由TRNSYS生成的TYPEX.cpp
骨架和TRNSYS自带的TRNSYS.h
头文件拷贝到VS项目下,覆盖掉Visual Studio生成的两个同名空文件。TYPEX.cpp
好找,而TRNSYS.h
不好找。为了查找TRNSYS.h
,我们来到TRNSYS的安装目录下,利用Windows自带的文件搜索功能搜索TRNSYS.h
。最终,在TRNSYS18/SourceCode/Templates/
下找到了。直接在搜索结果里复制就行啦。
复制完两个文件后,项目目录就长这样。
为了正确编译我们的库,还需要复制TRNSYS的二进制库文件。因为TRNSYS.h
中只有一些函数接口的声明(例如setOutputValue()
),而这些函数接口的定义依赖于外部的运行库文件。为了调用这些函数接口,我们还需要找到对应的运行库,并把它们引入到项目中来。
费尽九牛二虎之力,笔者终于了解到需要引入的运行库是TRNDll64.lib
和TRNDll64.dll
这两个文件。通过文件搜索,笔者查找到这两个文件位于TRNSYS18/Compilers/TRNSYS/TRNDll/x64/Release
和TRNSYS18/Compilers/TRNSYS/TRNDll/x64/Debug
目录下。两者的区别是前者不能用于Debug而速度快,而后者能用于Debug而速度慢。由于我们编写的都是简单的数值计算,所以笔者建议选择Release目录下的文件。将这些文件拷贝到项目目录下即可。
完成后项目目录就长这样。
设置项目编译、链接、生成选项
在Visual Studio中打开TYPEX.cpp
,点击代码编辑区域,选中顶层菜单中的“项目”菜单,再选择最下方的一项“TYPEX属性”,其中“TYPEX”为你的VS项目名称。在弹出的窗口中,做如下改动:
-
在最上方的“配置”选项中,选择“所有配置”。
-
左侧选择
配置属性->常规
,右侧的“常规属性”栏下,将“配置类型”改为“动态库(dll) “
- 左侧选择
链接器->高级
,右侧找到“目标计算机”,选择“MachineX64“。
- 左侧选择
链接器->输入
,右侧选择”附加依赖项“,点击”编辑“,在弹出窗口的文本编辑区域中键入”TRNDll64.lib”。确定。
- 完成上述操作后,点击“确定”,保存设置。
接下来,选中顶层菜单中的“生成”菜单,选择“配置管理器”,先在弹出窗口“活动解决方案配置“中选择“Release”,再在“活动解决方案平台”中选择“X64”。
开始编译
在完成上述步骤后,就可以开始编译了。在顶层菜单中的“生成”菜单中,选择第一项“生成解决方案“,即开始编译。
若编译成功,则会在底部的“输出”窗口中输出生成信息。其中会包含目标dll的文件地址。
如果出现“无法解析的外部符号”问题,请再次检查上述选项是否已经设置正确。对于32位的电脑,请把上述带有“X64”的选项转换为带”X86“、“32”的类似选项。
【4. 测试运行】
(之后更)
欢迎关注我的GitHub账号:@W-Hsu