Calling Vensim from External Function
Calling Vensim from External Function
Hi, I am working with a vensim model (i.e. model1) that must call another vensim model (i.e. model2). At each time step, model1 sends a vector to model2, model2 runs from 1 to 100 and then returns a set of values. I need to analyze the returned values to find the best among all of them (kind of brute force search). Is there a simple way to have a Vensim model calling another Vensim model, or should I use an external function inside model1 that calls model2? Thanks, any help will be great.
-
- Senior Member
- Posts: 1107
- Joined: Wed Mar 12, 2003 2:46 pm
OK, thank you Bob.
I've found two examples, vddext.xls and ddeserv.xls, that are working fine, but I don't know how to call vensim from vensim using visual c++ -- I've been playing with few different external function in c++ that are working fine.
To have a simple example that works, and learn how to do it, I would like to have two random numbers being generate in model1.mdl, be able to pass them to model2.mdl, do few calculations (could be the sum of them integrated over time to start), and them have the result back to model1.mdl (in fact, I will have few vectors being passed to model2.mdl, and a vector as return).
My questions is:
Should I call model2.mdl in the vdde_initiate (at VENEXT.C), or should I load the model, run it and stop the way you have in DDESEV.XLS?
I've been trying few differents way to do it, but it is not working at all. I am getting the error message: " Stop from Vensim Problems encountered in the simulation"?
Thanks,
Rodney
I've found two examples, vddext.xls and ddeserv.xls, that are working fine, but I don't know how to call vensim from vensim using visual c++ -- I've been playing with few different external function in c++ that are working fine.
To have a simple example that works, and learn how to do it, I would like to have two random numbers being generate in model1.mdl, be able to pass them to model2.mdl, do few calculations (could be the sum of them integrated over time to start), and them have the result back to model1.mdl (in fact, I will have few vectors being passed to model2.mdl, and a vector as return).
My questions is:
Should I call model2.mdl in the vdde_initiate (at VENEXT.C), or should I load the model, run it and stop the way you have in DDESEV.XLS?
I've been trying few differents way to do it, but it is not working at all. I am getting the error message: " Stop from Vensim Problems encountered in the simulation"?
Thanks,
Rodney
-
- Senior Member
- Posts: 1107
- Joined: Wed Mar 12, 2003 2:46 pm
You won't be able to use the dde functionality for this. Start with venext.c and add a function to this to open a simulate a model. You will need to add vendll32.lib to the link statements for the project.
eg (untested)
double MYSIM(double x)
{
char buf[80] ;
float vals[101] ,times[101] ;
vensim_command("SPECIAL>LOADMODEL|c:\\temp\\mymodel.vpm") ;
vensim_command("SIMULATE>RUNNAME|base") ;
sprintf(buf,"SIMULATE>SETVAL|const=%lg",x) ;
vensim_command(buf) ;
vensim_command("MENU>RUN|O") ;
vensim_get_data("base.vds","outvar","Time",vals,times,101) ;
return vals[100] ;
}
eg (untested)
double MYSIM(double x)
{
char buf[80] ;
float vals[101] ,times[101] ;
vensim_command("SPECIAL>LOADMODEL|c:\\temp\\mymodel.vpm") ;
vensim_command("SIMULATE>RUNNAME|base") ;
sprintf(buf,"SIMULATE>SETVAL|const=%lg",x) ;
vensim_command(buf) ;
vensim_command("MENU>RUN|O") ;
vensim_get_data("base.vds","outvar","Time",vals,times,101) ;
return vals[100] ;
}
OK, thanks.
I see why it will not work the way I was doing -- it seems easier than I thought. However, I am trying to add the vendll32.lib to my project, but I keep having the " unresolved external symbol _vensim_get_data referenced in function _MYSIM" error message. Should I add one more .def file also?
Thank you
I see why it will not work the way I was doing -- it seems easier than I thought. However, I am trying to add the vendll32.lib to my project, but I keep having the " unresolved external symbol _vensim_get_data referenced in function _MYSIM" error message. Should I add one more .def file also?
Thank you
Hi Bob, sorry about asking you more questions, I know that you are busy, but my models are getting a little bit more complicated, and I can't find a solution for the following problem:
I am passing a set of values to a second vensim model (this part is working fine after your help), and I would like to have back all the values of a subscripted variable, i.e., I have the variable output[Subs] where Subs goes from 1 to 100, and I would like to have all values for time=0 to 100. I need to be able to do calculation among the values of the matrix vals[100][101] (or should be vals[101][100] ?) using "for" and " if else" commands.
Is there a way to have back the full matrix for a fully subscripted variable? Again, thanks a lot, I appreciate any help.
Rodney
I am passing a set of values to a second vensim model (this part is working fine after your help), and I would like to have back all the values of a subscripted variable, i.e., I have the variable output[Subs] where Subs goes from 1 to 100, and I would like to have all values for time=0 to 100. I need to be able to do calculation among the values of the matrix vals[100][101] (or should be vals[101][100] ?) using "for" and " if else" commands.
Is there a way to have back the full matrix for a fully subscripted variable? Again, thanks a lot, I appreciate any help.
Rodney
-
- Super Administrator
- Posts: 4838
- Joined: Wed Mar 05, 2003 3:10 am
You can do this fairly easily.
Take a look at the MATRIX_INVERT function in the Vensim examples.
static FUNC_DESC Flist[] = {
...
{"MATRIX_INVERT"," {matrix} ",1,1,INVERT_FUNC,2,0,0,0},
...
The "2" after the INVERT_FUNC identifier tells Vensim to expect the function to fill in two subscript ranges. So your function might be
static FUNC_DESC Flist[] = {
...
{"MY_FUNC","{passed variables}",1,1,MY_FUNC_FUNC,1,0,0,0},
...
This will tell Vensim to fill in just one subscript range.
Your call might look something like
output[Subs] = MY_FUNC(some vars)
Vensim would only call MY_FUNC once.
Hope this makes sense.
Tony.
Take a look at the MATRIX_INVERT function in the Vensim examples.
static FUNC_DESC Flist[] = {
...
{"MATRIX_INVERT"," {matrix} ",1,1,INVERT_FUNC,2,0,0,0},
...
The "2" after the INVERT_FUNC identifier tells Vensim to expect the function to fill in two subscript ranges. So your function might be
static FUNC_DESC Flist[] = {
...
{"MY_FUNC","{passed variables}",1,1,MY_FUNC_FUNC,1,0,0,0},
...
This will tell Vensim to fill in just one subscript range.
Your call might look something like
output[Subs] = MY_FUNC(some vars)
Vensim would only call MY_FUNC once.
Hope this makes sense.
Tony.
-
- Senior Member
- Posts: 1107
- Joined: Wed Mar 12, 2003 2:46 pm
In case your question was about the vensim_get_data part of the operation that answer is simpler. Just use something like (assumes subs : (sub 1-sub 100) in Vensim).
float vals[100][101] ;
int subs ;
char varname[80] ;
for(subs=0;subs<100;subs++) {
sprintf(varname,"var[sub %d]",subs+1) ;
vensim_get_data("base.vdf",varname,"Time",vals[sub],times,101) ;
}
float vals[100][101] ;
int subs ;
char varname[80] ;
for(subs=0;subs<100;subs++) {
sprintf(varname,"var[sub %d]",subs+1) ;
vensim_get_data("base.vdf",varname,"Time",vals[sub],times,101) ;
}
Hey everyone,
I am almost done with the tools I need to implement the full version of my models, but I am having trouble with the following:
1. I have a vector in my c++ code, i.e. double myvector[101] (at each model1 time step I have a different myvector), that is suppose to be part of a lookup(Time, myvector) at model2;
2. I've tried to set the value of a subscripted variable in model2, input2[TimeT], i.e. fprintf(buf,"SIMULATE>SETVAL|""input2[TimeT]=%lg", myvector); and then, at model2, have the lookup using the VECTOR LOOKUP function;
3. I've tried to set the value of the lookup at model 2, i.e. sprintf(buf,"SIMULATE>SETVAL|mylookup2(time[101],myvector[101])".
None of the above solutions are working. Basically, it runs, but the variables input2 or the lookup2 doesn't change at all in model 2. Also, I have a myfile.txt file (just to help in the development) where I can verify the values that I should be expecting in model2.
Any hint about how to solve this problem?
Thanks a lot,
Rodney
I am almost done with the tools I need to implement the full version of my models, but I am having trouble with the following:
1. I have a vector in my c++ code, i.e. double myvector[101] (at each model1 time step I have a different myvector), that is suppose to be part of a lookup(Time, myvector) at model2;
2. I've tried to set the value of a subscripted variable in model2, input2[TimeT], i.e. fprintf(buf,"SIMULATE>SETVAL|""input2[TimeT]=%lg", myvector); and then, at model2, have the lookup using the VECTOR LOOKUP function;
3. I've tried to set the value of the lookup at model 2, i.e. sprintf(buf,"SIMULATE>SETVAL|mylookup2(time[101],myvector[101])".
None of the above solutions are working. Basically, it runs, but the variables input2 or the lookup2 doesn't change at all in model 2. Also, I have a myfile.txt file (just to help in the development) where I can verify the values that I should be expecting in model2.
Any hint about how to solve this problem?
Thanks a lot,
Rodney
-
- Super Administrator
- Posts: 4838
- Joined: Wed Mar 05, 2003 3:10 am
Yep, here you have part of the code -- not all of them -- and this tests are for two small models so I can validate the code:
time=VENGV->time;
double myvector[101];
int i;
....
// I start reading values from a file appended in the last time step -- it is from this file that comes the vector that, for simplicity, has 101 terms always.
i=0;
pFile = fopen("myfile.txt", "r" );
while ( fgets( buff, sizeof buff, pFile) != NULL ) {
fscanf(pFile, "%lg", &n);
myvector=n;
i++;
}
...
// I have to send the vector to Model2, get an value back, and then append myfile.txt with that value
vensim_command("SPECIAL>LOADMODEL|Model2.vpm") ;
vensim_command("SIMULATE>RUNNAME|base") ;
sprintf(buf,"SIMULATE>SETVAL|mylookup2((time[101],myvector[101]))") ;
vensim_command(buf) ;
vensim_command("MENU>RUN|O") ;
vensim_get_data("base.vds","output","Time",vals,times,101) ;
return vals[100];
I simulated again. Nothing changes and I don't get an error message.
Basically, I need to set a vector, or a lookup (that is better) in Model2 before it runs.
Thanks,
Rodney
time=VENGV->time;
double myvector[101];
int i;
....
// I start reading values from a file appended in the last time step -- it is from this file that comes the vector that, for simplicity, has 101 terms always.
i=0;
pFile = fopen("myfile.txt", "r" );
while ( fgets( buff, sizeof buff, pFile) != NULL ) {
fscanf(pFile, "%lg", &n);
myvector=n;
i++;
}
...
// I have to send the vector to Model2, get an value back, and then append myfile.txt with that value
vensim_command("SPECIAL>LOADMODEL|Model2.vpm") ;
vensim_command("SIMULATE>RUNNAME|base") ;
sprintf(buf,"SIMULATE>SETVAL|mylookup2((time[101],myvector[101]))") ;
vensim_command(buf) ;
vensim_command("MENU>RUN|O") ;
vensim_get_data("base.vds","output","Time",vals,times,101) ;
return vals[100];
I simulated again. Nothing changes and I don't get an error message.
Basically, I need to set a vector, or a lookup (that is better) in Model2 before it runs.
Thanks,
Rodney
-
- Super Administrator
- Posts: 4838
- Joined: Wed Mar 05, 2003 3:10 am
It looks like you are not passing the lookup correctly.
This Venapp command will not work
"SIMULATE>SETVAL|mylookup2((time[101],myvector[101]))"
But if you create the venapp command like below, it should. You will need to check the command is in the correct format before passing it to Vensim.
int nNumValues = i; //store array size passed from Vensim
//start the venapp command
sprintf(buf,"SIMULATE>SETVAL|mylookup2((%f,%f)",time[0],myvector[0]) ;
char tempstring[20];
for ( i = 0 ; i < nNumValues ; i++ )
{
sprintf(tempstring,",(%f,%f)",time,myvector);
strcat(buf,tempstring);
}
//end the venapp command
strcat(buf,"))");
vensim_command("SPECIAL>LOADMODEL|Model2.vpm") ;
vensim_command("SIMULATE>RUNNAME|base") ;
vensim_command(buf) ;
vensim_command("MENU>RUN|O") ;
vensim_get_data("base.vds","output","Time",vals,times,101) ;
return vals[100];
buf should contain a venapp command something like the following (that you then pass to Vensim)
"SIMULATE>SETVAL|mylookup2((0,1),(1,2),(2,3),(3,4))"
This Venapp command will not work
"SIMULATE>SETVAL|mylookup2((time[101],myvector[101]))"
But if you create the venapp command like below, it should. You will need to check the command is in the correct format before passing it to Vensim.
int nNumValues = i; //store array size passed from Vensim
//start the venapp command
sprintf(buf,"SIMULATE>SETVAL|mylookup2((%f,%f)",time[0],myvector[0]) ;
char tempstring[20];
for ( i = 0 ; i < nNumValues ; i++ )
{
sprintf(tempstring,",(%f,%f)",time,myvector);
strcat(buf,tempstring);
}
//end the venapp command
strcat(buf,"))");
vensim_command("SPECIAL>LOADMODEL|Model2.vpm") ;
vensim_command("SIMULATE>RUNNAME|base") ;
vensim_command(buf) ;
vensim_command("MENU>RUN|O") ;
vensim_get_data("base.vds","output","Time",vals,times,101) ;
return vals[100];
buf should contain a venapp command something like the following (that you then pass to Vensim)
"SIMULATE>SETVAL|mylookup2((0,1),(1,2),(2,3),(3,4))"
Thanks, Tony.
In fact, it makes a lot of sense the way you solved the problem. However, I was getting the following error message when running vensim:
Run-Time Check Failure #2 - Stack around the variable ' buf' was corrupted.
So, I changed the size of ' buf' and it is working now.
Again, thank you for your help.
Rodney
In fact, it makes a lot of sense the way you solved the problem. However, I was getting the following error message when running vensim:
Run-Time Check Failure #2 - Stack around the variable ' buf' was corrupted.
So, I changed the size of ' buf' and it is working now.
Again, thank you for your help.
Rodney