2011年7月4日 星期一

如何利用VB6呼叫VENSIM模式?

如何利用VB6呼叫VENSIM模式?
劉子明
台灣大學生物環境系統工程學系
博士

如有引用,請寫出處:劉子明,「如何利用VB6呼叫VENSIM模式?」,http://ntusdlab.blogspot.com/2011/07/vb6vensim.html


本說明必須搭配我的範例,請下載我寫的範例檔

範例內的Vensim模式乃依據GWLF集水區流量模式的理論,我利用Vensim寫出來的,在此稱之為GWLF_ven。不過,模式內的CN值仍無法考慮臨前降雨的影響,所以,該模式僅供參考。寫這個模式的重點在於展示如何利用VBCall Vensim的模式,因此我才會寫一個簡單的模式來做Demo
Vensim雖然提供了簡單易操作的介面,讓使用者能夠利用Vensim去進行系統動力學的相關研究與模擬,但卻有點將使用者受限於只能利用Vensim所提供的功能進行分析與繪圖。雖然可以針對每個變數輸出,但是輸出的格式實在不好用,雖然能繪圖,但是繪圖標示的功能根本沒有。使用者難免都需要匯出結果之變數資料,再進一步利用其他軟體,如Excel進行分析與繪圖,因此顯得Vensim似乎綁手綁腳的。其實,Vensim背後有許多的技巧與方法,可以讓Vensim方便又好用,只是你必須花一些時間去摸索,才能漸漸了解那些好用的技巧。
Vensim模式可以讓許多種程式語言呼叫,如VBExcel VBACJavaDelphi等,本例主要便是藉由VB程式,去Call我已經寫好的GWLF_ven模式,除了改變模式內原本的一些Constant參數,也可以將原本是ASCII格式的輸入檔(*.dat)轉成Vensim需要的格式(vdf),並讓Vensim去模擬。進而取出任何想要知道的變數結果,由VB程式進一步分析。
程式的表單配置如下:
圖一、VB表單配置圖

GWLF_ven的內容如圖二、圖三所示。其中的CN2便是集水區CN值,Area便是集水區面積,r為退水係數。在Vensim中,這三個變數都是給定Constant值。而需要外部輸入Data的變數只有氣溫T跟日降雨量Rt。本案例便是展示如何利用VB改變集水區面積AreaCN值以及退水係數r值進去GWLF_ven模式,並將原本需要的輸入檔-溫度T以及降雨量Rt,由VBDat檔轉換成Vdf檔,並進一步帶入GWLF_ven模擬。
圖二、GWLF_ven的地表模組

圖三、GWLF_ven的地層模組

利用VBCall Vensim模式之前,必須先將該模式打包,也就是利用功能表的FileàPublish,將模式打包,如圖四。本案例之模式檔為GWLF.mdl,經過打包之後,會產生GWLF.vpm檔。而VB要呼叫的便是GWLF.vpm
圖四、以功能表下的Publish將模式打包成可供其他程式呼叫的vpm檔。

以下便是主要程式內容。一開始必須對VensimDLL檔宣告,你可以直接copy所有宣告貼上,建議你不要去更動該宣告,除非你對所有的dll檔都很了解。程式內容都在附件當中,你可以依照內容,適當的應用在你自己的模式,並做變化。

Private Declare Function vensim_be_quiet Lib "vendll32.dll" (ByVal quietflag As Long) As Long
Private Declare Function vensim_check_status Lib "vendll32.dll" () As Long
Private Declare Function vensim_command Lib "vendll32.dll" (ByVal Vcommand$) As Long
Private Declare Function vensim_continue_simulation Lib "vendll32.dll" (ByVal number_time_step As Long) As Long
Private Declare Function vensim_finish_simulation Lib "vendll32.dll" () As Long
Private Declare Function vensim_get_data Lib "vendll32.dll" (ByVal filename$, ByVal varname$, ByVal timename$, varvals As Single, timevals As Single, ByVal maxpoints As Integer) As Long
Private Declare Function vensim_get_dpval Lib "vendll32.dll" (ByVal varname$, varval As Double) As Long
Private Declare Function vensim_get_dpvecvals Lib "vendll32.dll" (vecoff As Long, varvals As Double, ByVal veclen As Long) As Long
Private Declare Function vensim_get_info Lib "vendll32.dll" (ByVal infowanted As Long, ByVal buf$, ByVal maxbuflen As Long) As Long
Private Declare Function vensim_get_sens_at_time Lib "vendll32.dll" (ByVal filename$, ByVal varname$, ByVal timename$, attime As Single, vals As Single, ByVal maxpoint As Long) As Long
Private Declare Function vensim_get_substring Lib "vendll32.dll" (ByVal fullstring$, ByVal frompos As Long, ByVal buf$, ByVal maxbuflen As Long) As Long
Private Declare Function vensim_get_val Lib "vendll32.dll" (ByVal varname$, varval As Single) As Integer
Private Declare Function vensim_get_varattrib Lib "vendll32.dll" (ByVal varname$, ByVal attrib As Long, ByVal buf$, ByVal maxbuflen As Long) As Long
Private Declare Function vensim_get_varnames Lib "vendll32.dll" (ByVal filter$, ByVal vartype As Long, ByVal buf$, ByVal maxbuflen As Long) As Long
Private Declare Function vensim_get_varoff Lib "vendll32.dll" (ByVal varname$) As Long
Private Declare Function vensim_get_vecvals Lib "vendll32.dll" (vecoff As Long, varvals As Single, ByVal nelm As Long) As Long
Private Declare Function vensim_set_parent_window Lib "vendll32.dll" (ByVal vwidnow As Long, ByVal r1 As Long, ByVal r2 As Long) As Long
Private Declare Function vensim_show_sketch Lib "vendll32.dll" (ByVal viewnum As Long, ByVal wantscroll As Long, ByVal zoompercent As Long, ByVal Vwindow As Long) As Long
Private Declare Function vensim_start_simulation Lib "vendll32.dll" (ByVal loadfirst As Integer, ByVal game As Long, ByVal overwrite As Long) As Long
Private Declare Function vensim_tool_command Lib "vendll32.dll" (ByVal Vcommand$, ByVal Vwindow As Long, ByVal iswip As Long) As Long
'以上的宣告請不要改更動
'任何Vensim的模式都必須經過"Publish"才能產生VB可以callvpm
'本例乃利用VB使用Vensim所提供的DLL去進行變數修改以及模擬
'案例的模式為自行撰寫的GWLF集水區流量模式
'僅供測試使用
'欲了解可以使用的變數以及語法
'請上Vensim的網站下載Vensim DSS Reference Supplement
'有任何疑問與建議
'歡迎跟我聯繫
'台灣大學生物環境系統工程學系博士生
'劉子明
'tedliu13@gmail.com
Dim infl, fl, outfl, flvds As String
Dim result, ms, ss, ts, vs, i As Integer

Private Sub Command1_Click()
'Vensim要用的變數最好分行宣告
Dim Rval(3660) As Single
Dim Tval(3660) As Single
Dim maxpoints As Long
Dim tpoints As Long
Dim area, r, CN As Single
Dim ave(2, 12) As Single
Dim n, yr1, yr2, chk, TotalDay As Integer
Dim vvr(2), VVAR As String
Open "CMD.dat" For Output As #1
Open "CMS.dat" For Output As #2
'輸出變數
vvr(1) = "CMD"
vvr(2) = "CMS"
If infl = "" Then
MsgBox "請先選擇輸入檔"
Exit Sub
End If
'計算模擬時間 TotalDay
yr1 = Val(Text3.Text)
yr2 = Val(Text4.Text)
TotalDay = 0
For yr = yr1 To yr2
chk = yr Mod 4
If chk = 0 Then
days = 366
Else
days = 365
End If
TotalDay = TotalDay + days
Next yr
'輸入變數
area = Val(Text2.Text)
CN = Val(Text5.Text)
r = Val(Text6.Text)
'將輸入檔(dat) 轉換成vensim的格式
result = vensim_command("MENU>DAT2VDF|" & infl)
infl = Replace(infl, "dat", "vdf")
'開啟模式
fl = App.Path & "\GWLF.vpm" 'Vensim模式
result = vensim_command("SPECIAL>LOADMODEL|" & fl)
If result = 0 Then Exit Sub
'加入input
result = vensim_command("SIMULATE>ADDDATA|" & infl)
If result = 0 Then Exit Sub
'run name CMD
result = vensim_command("SIMULATE>RUNNAME|CMD")
If result = 0 Then Exit Sub
'改變模式內變數值
comstr$ = "SIMULATE>SETVAL|INITIAL TIME = 1" '起始時間
result = vensim_command(comstr$)
comstr$ = "SIMULATE>SETVAL|FINAL TIME = " + CStr(TotalDay) '結束時間
result = vensim_command(comstr$)
comstr$ = "SIMULATE>SETVAL|Area = " + CStr(area) '集水區面積
result = vensim_command(comstr$)
comstr$ = "SIMULATE>SETVAL|CN2 = " + CStr(CN) 'CN
result = vensim_command(comstr$)
comstr$ = "SIMULATE>SETVAL|r = " + CStr(r) '退水係數
result = vensim_command(comstr$)
'開始模擬並輸出結果
result = vensim_command("MENU>RUN")
If result = 0 Then Exit Sub
'讀取結果
For i = 1 To 2
VVAR = vvr(i)
maxpoints = TotalDay
tpoints = vensim_get_data("CMD.vdf", VVAR, "Time", Rval(1), Tval(1), maxpoints) '讀取變數至Rval(1)
'利用迴圈讀取結果
n = 0
For j = yr1 To yr2
For m = 1 To 12
If m = 2 Then
If j Mod 4 = 0 Then
days = 29
Else
days = 28
End If
ElseIf m = 4 Or m = 6 Or m = 9 Or m = 11 Then
days = 30
Else
days = 31
End If
For d = 1 To days
n = n + 1
Print #i, n, Rval(n)
ave(i, m) = ave(i, m) + Rval(n) / days
Next d
Next m
Next j
Close (i)
Next i
將月平均流量繪成折線圖
For m = 1 To 12
ave(2, m) = ave(2, m) / (yr2 - yr1 + 1)
With MSChart1
.Row = m
.RowLabel = CStr(m)
.Data = ave(2, m)
End With
Next m
MsgBox "完成!!~"
End Sub

沒有留言:

張貼留言