Dear colleagues,
I wrote a Delphi 5.0 application which controls a small Vensim model. Everything (starting, stopping simulations, getting data from the model during and after simulation) is fine, except that I don't manage to override the initial values of the levels from the application.
Some lines of Delphi code to illustrate my problem:
// Declaration in the units' 'implementation' part
function vensim_command(command_string: PChar): Integer ; stdcall ; external 'vendll32.dll' ;
// Global variable, contains the initial values for the levels.
var InitSubstrate: array[1..20,1..20] of Single;
// This procedure is called after the model is loaded successfully
// and before simulation starts.
// Substrate Height[x1..x20, y1..y20] is a matrix of levels in the
// Vensim Model.
procedure InitializeLevels;
var result, x, y: Integer;
Text: PChar;
begin
for x := 1 to 20 do for y := 1 to 20 do begin
Text := PChar('"SIMULATE>SETVAL|initial Substrate Height[x' + IntToStr(x) + ',y' + IntToStr(y) + '] = ' + FloatToStr(InitSubstrate[x,y]) + '"');
result := vensim_command(Text);
end; // for x, y
end; // procedure
While vensim_command always returns 1 indicating successful execution of Text (which itself looks reasonable to me), the initial values provided with InitSubstrate don't seem to arrive at the model. Instead, it keeps using the original initial values I used when building the model in Vensim. Does anyone out there see what I'm doing wrong?
Greetings
Peter
Initializing Levels from a Delphi application
-
- Super Administrator
- Posts: 4590
- Joined: Wed Mar 05, 2003 3:10 am
-
- Senior Member
- Posts: 1107
- Joined: Wed Mar 12, 2003 2:46 pm
Assuming that initial Substrate Height[x,y] is the initial value for the level in the model this looks like it should work. This should be the last thing called before starting the simulation with either MENU>RUN or vensim_start_simulation. My best guess is you are doing something else that causes these values to be discarded.
Hi Tony, hi Bob,
thanks for your quick replies! In the meantime, I took some effort to check and reorganize everything (which improved my application quite a bit), but the problem persists. I don't load any CIN-file and I'm sure now that nothing happens between level initialization and simulation start.
I think, again, a piece of Delphi code will show best what I mean:
procedure TTestForm.SimulateBetterButtonClick(Sender: TObject);
// Simulate the model and fetch the level values during
// and after simulation.
var result, index: Integer;
Text, RunName: PChar;
begin
RunName := 'Current';
Text := PChar('SIMULATE>RUNNAME|' + RunName);
result := vensim_command(Text);
InitializeLevels; // Pretends to work, but initial values don't seem to arrive at the Vensim model.
result := vensim_start_simulation(1,0,1);
if result = 1 then begin
while result = 1 do begin
GetLevels_during_Simulation(RunName, index); // Uses vensim_get_val. Works.
Update_Diagrams(index);
result := vensim_continue_simulation(5);
end; // while
result := vensim_finish_simulation;
GetLevels_after_Simulation(RunName); // Uses vensim_get_data. Works.
// Show something to the user.
Update_Diagrams(400);
SpinEdit1.Value := 50;
end; // if result = 1;
end; //procedure TTestForm.SimulateBetterButtonClick;
The procedure InitializeLevels which is called here is the same as shown in my post above. What I didn't show there, was that the initial values I want to pass to Vensim are loaded from a textfile (this works) before going into the loop where setval is called.
The setval commands pretend to work, but when I look at the actual initial values of the levels as obtained via vensim_get_val or vensim_get_data they seem to have had no effect. The Vensim model itself does its job perfectly.
The model is loaded immediately after launching the application. It's a vpm-file.
Do you have an idea what I could try to locate the problem?
Greetings
Peter
thanks for your quick replies! In the meantime, I took some effort to check and reorganize everything (which improved my application quite a bit), but the problem persists. I don't load any CIN-file and I'm sure now that nothing happens between level initialization and simulation start.
I think, again, a piece of Delphi code will show best what I mean:
procedure TTestForm.SimulateBetterButtonClick(Sender: TObject);
// Simulate the model and fetch the level values during
// and after simulation.
var result, index: Integer;
Text, RunName: PChar;
begin
RunName := 'Current';
Text := PChar('SIMULATE>RUNNAME|' + RunName);
result := vensim_command(Text);
InitializeLevels; // Pretends to work, but initial values don't seem to arrive at the Vensim model.
result := vensim_start_simulation(1,0,1);
if result = 1 then begin
while result = 1 do begin
GetLevels_during_Simulation(RunName, index); // Uses vensim_get_val. Works.
Update_Diagrams(index);
result := vensim_continue_simulation(5);
end; // while
result := vensim_finish_simulation;
GetLevels_after_Simulation(RunName); // Uses vensim_get_data. Works.
// Show something to the user.
Update_Diagrams(400);
SpinEdit1.Value := 50;
end; // if result = 1;
end; //procedure TTestForm.SimulateBetterButtonClick;
The procedure InitializeLevels which is called here is the same as shown in my post above. What I didn't show there, was that the initial values I want to pass to Vensim are loaded from a textfile (this works) before going into the loop where setval is called.
The setval commands pretend to work, but when I look at the actual initial values of the levels as obtained via vensim_get_val or vensim_get_data they seem to have had no effect. The Vensim model itself does its job perfectly.
The model is loaded immediately after launching the application. It's a vpm-file.
Do you have an idea what I could try to locate the problem?
Greetings
Peter
-
- Senior Member
- Posts: 1107
- Joined: Wed Mar 12, 2003 2:46 pm
Hi Peter,
I can't see anything obviously wrong. The best way to get to the bottom of this is probably to create a separate Delphi program that loads a trivial model (eg population=INTEG( births-deaths,100)) set a nonsubscripted variable (eg SIMULATE>SETVAL|birthrate=0.08) then simulates. If that works add a subscript. If that works try your model in the trivial program. If that works try the trivial model with your program. Hopefully one of these steps will offer insight.
I can't see anything obviously wrong. The best way to get to the bottom of this is probably to create a separate Delphi program that loads a trivial model (eg population=INTEG( births-deaths,100)) set a nonsubscripted variable (eg SIMULATE>SETVAL|birthrate=0.08) then simulates. If that works add a subscript. If that works try your model in the trivial program. If that works try the trivial model with your program. Hopefully one of these steps will offer insight.
Hi Bob,
may I bother you again? I'm not sure, but maybe there is a communication problem between VensimDLL and Delphi.
Following your suggestion, I created a trivial Vensim model (Water Reservoir with constant inflow and Level-proportional outflow):
Outflow Rate=
0.01
~ 1/Second
~ |
Outflow=
Outflow Rate*Water
~ m*m*m/Second
~ |
Inflow=
10
~ m*m*m/Second
~ |
Water= INTEG (
Inflow-Outflow,
100)
~ m*m*m
~ |
Then, I wrote a trivial Delphi program (comprising three procedures):
//------------------------------------------------------------------------------
procedure TTestForm.FormCreate(Sender: TObject);
// Called once, after application is launched.
var result: Integer;
begin
Randomize;
result := vensim_command('SPECIAL>LOADMODEL|Water.vpm');
result := vensim_command('SPECIAL>READCUSTOM|WaterGraph.vgf') ;
end;
//------------------------------------------------------------------------------
procedure InitializeLevel;
// Procedure for testing different attempts to initialize the level.
var result: Integer;
Rand: Single;
Text: PChar;
begin
Rand := 5000 * random;
Text := PChar('"SIMULATE>SETVAL|INITIAL Water = ' + FloatToStr(Rand) + '"'); // pretends to work
// Text := PChar('"SIMULATE>SETVAL|INITIAL Water = ' + FloatToStr(Rand)); // pretends to work
// Text := PChar('"SIMULATE>SETVAL|Water INITIAL = ' + FloatToStr(Rand) + '"'); // pretends to work
// Text := PChar('"SIMULATE>SETVAL|Water INIT = ' + FloatToStr(Rand) + '"'); // Even this pretends to work
// Text := PChar('SIMULATE>SETVAL|INITIAL Water = ' + FloatToStr(Rand)); // Doesn't pretend to work, seemingly, because double quotes are missing. But shouldn't those be necessary for subscripted variables only?
// Text := PChar('"SIMULATE>SETVAL|Outflow Rate = 0.05"'); // pretends to work
result := vensim_command(Text);
end;
//------------------------------------------------------------------------------
procedure TTestForm.SimulateButton1Click(Sender: TObject);
// Called, when user wants to run the model.
var result: Integer;
Time, Water: array[0..2000] of Single;
begin
result := vensim_command('SIMULATE>RUNNAME|Current');
InitializeLevel;
result := vensim_command('MENU>RUN|O');
// Values from vensim_get_data and the custom graph match
result := vensim_get_data('Current', 'Water', 'Time', @Water[0], @Time[0], 2001);
result := vensim_tool_command('CUSTOM>WaterLevel', ChartPanel1.Handle, 0);
end;
//------------------------------------------------------------------------------
The problem already arose at the first step: Nearly all variations of SETVAL commands I tried (see out-commented lines in InitializeLevel) pretended to work (result = 1) except the one without double quotes ("), but the Vensim Model seems to ignore them. It keeps simulating, but stubbornly sticks to the 'builtin' initial value of 100 m³. I also can't change the value of the constant 'Outflow Rate' with SETVAL.
Could it be some country specific system settings (Germany, in my case)? I tried some changes in Windows' number format (decimal separator, etc.) but without effect so far.
Looking for a workaround:
In order to make the other (non-trivial) application work, I'm thinking about making the program write the desired initial values for the levels into a text file which is then taken by vensim in order to initialize the levels from there. Is that possible?
Greetings
Peter
may I bother you again? I'm not sure, but maybe there is a communication problem between VensimDLL and Delphi.
Following your suggestion, I created a trivial Vensim model (Water Reservoir with constant inflow and Level-proportional outflow):
Outflow Rate=
0.01
~ 1/Second
~ |
Outflow=
Outflow Rate*Water
~ m*m*m/Second
~ |
Inflow=
10
~ m*m*m/Second
~ |
Water= INTEG (
Inflow-Outflow,
100)
~ m*m*m
~ |
Then, I wrote a trivial Delphi program (comprising three procedures):
//------------------------------------------------------------------------------
procedure TTestForm.FormCreate(Sender: TObject);
// Called once, after application is launched.
var result: Integer;
begin
Randomize;
result := vensim_command('SPECIAL>LOADMODEL|Water.vpm');
result := vensim_command('SPECIAL>READCUSTOM|WaterGraph.vgf') ;
end;
//------------------------------------------------------------------------------
procedure InitializeLevel;
// Procedure for testing different attempts to initialize the level.
var result: Integer;
Rand: Single;
Text: PChar;
begin
Rand := 5000 * random;
Text := PChar('"SIMULATE>SETVAL|INITIAL Water = ' + FloatToStr(Rand) + '"'); // pretends to work
// Text := PChar('"SIMULATE>SETVAL|INITIAL Water = ' + FloatToStr(Rand)); // pretends to work
// Text := PChar('"SIMULATE>SETVAL|Water INITIAL = ' + FloatToStr(Rand) + '"'); // pretends to work
// Text := PChar('"SIMULATE>SETVAL|Water INIT = ' + FloatToStr(Rand) + '"'); // Even this pretends to work
// Text := PChar('SIMULATE>SETVAL|INITIAL Water = ' + FloatToStr(Rand)); // Doesn't pretend to work, seemingly, because double quotes are missing. But shouldn't those be necessary for subscripted variables only?
// Text := PChar('"SIMULATE>SETVAL|Outflow Rate = 0.05"'); // pretends to work
result := vensim_command(Text);
end;
//------------------------------------------------------------------------------
procedure TTestForm.SimulateButton1Click(Sender: TObject);
// Called, when user wants to run the model.
var result: Integer;
Time, Water: array[0..2000] of Single;
begin
result := vensim_command('SIMULATE>RUNNAME|Current');
InitializeLevel;
result := vensim_command('MENU>RUN|O');
// Values from vensim_get_data and the custom graph match
result := vensim_get_data('Current', 'Water', 'Time', @Water[0], @Time[0], 2001);
result := vensim_tool_command('CUSTOM>WaterLevel', ChartPanel1.Handle, 0);
end;
//------------------------------------------------------------------------------
The problem already arose at the first step: Nearly all variations of SETVAL commands I tried (see out-commented lines in InitializeLevel) pretended to work (result = 1) except the one without double quotes ("), but the Vensim Model seems to ignore them. It keeps simulating, but stubbornly sticks to the 'builtin' initial value of 100 m³. I also can't change the value of the constant 'Outflow Rate' with SETVAL.
Could it be some country specific system settings (Germany, in my case)? I tried some changes in Windows' number format (decimal separator, etc.) but without effect so far.
Looking for a workaround:
In order to make the other (non-trivial) application work, I'm thinking about making the program write the desired initial values for the levels into a text file which is then taken by vensim in order to initialize the levels from there. Is that possible?
Greetings
Peter
-
- Super Administrator
- Posts: 4590
- Joined: Wed Mar 05, 2003 3:10 am
Is that the exact model you are loading into your Delphi app?
It will not work at present as the equation for "water" has no initial constant.
You would need to change the equation for "Water" from
Water= INTEG (Inflow-Outflow,100)
to
Water= INTEG ( Inflow - Outflow , INITIAL WATER )
Can you try making that modification to the model. Then try the following command and see if it changes the value for INITIAL WATER.
Text := PChar('SIMULATE>SETVAL|INITIAL WATER=200');
Tony.
It will not work at present as the equation for "water" has no initial constant.
You would need to change the equation for "Water" from
Water= INTEG (Inflow-Outflow,100)
to
Water= INTEG ( Inflow - Outflow , INITIAL WATER )
Can you try making that modification to the model. Then try the following command and see if it changes the value for INITIAL WATER.
Text := PChar('SIMULATE>SETVAL|INITIAL WATER=200');
Tony.