轉載請注明出處。
筆者最近在參加一個與自己專業幾乎不相關的競賽,這個競賽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