My first SME model


Step 1. Build your Stella model

Let us put together a very basic predator-prey model in a spatial context. We start with going to Stella and constructing the model and then making sure that it runs.

Suppose we have ended up with a simple model described in Stella by the following system of equations:

Grass(t) = Grass(t - dt) + (G_growth - Grazing) * dt
INIT Grass = 2

INFLOWS:
G_growth = alpha*Grass
OUTFLOWS:
Grazing = beta*Grass*Rabbits
Rabbits(t) = Rabbits(t - dt) + (Uptake - R_mortality) * dt
INIT Rabbits = 1

INFLOWS:
Uptake = k*Grazing
OUTFLOWS:
R_mortality = mu*Rabbits
alpha = 1
beta = 1
k = 0.1
mu = 0.1

Once we have understood how this model performs in Stella and have generated the expected cycling pattern of the two species, we can go to Stella equations, do <Edit -> Select All> and then <Edit -> Copy>.

Next we open a Text Editor that we have on our computer (on a Macintosh it will be BBEdit, or TextEdit; in Windows it is probably the NotePad), and then paste the equations into the file.

We can now save the file using the .eqns extension. Let us name this file R_G1.eqns.

Step 2. Set up SME project

Let us now choose a name for our SME project. Suppose it will be R_G. Stands for Rabiits&Grass.

Open the Terminal window and do

SME project R_G

I get:

Current project directory is /Documents/SME/Projects/
Current project is xxx
Current model is xxx
Current scenario is xxx
Using /Documents/SME/Projects/ as project base
Project workspace /Documents/SME/Projects/ does not exist. Creating.
Creating directory /Documents/SME/Projects//R_G/Config
Creating directory /Documents/SME/Projects//R_G/Data
Creating directory /Documents/SME/Projects//R_G/Driver
Creating directory /Documents/SME/Projects//R_G/DriverOutput
Creating directory /Documents/SME/Projects//R_G/Load
Creating directory /Documents/SME/Projects//R_G/Models
Creating directory /Documents/SME/Projects//R_G/VarList
Creating directory /Documents/SME/Projects//R_G/DriverOutput/Maps
Creating directory /Documents/SME/Projects//R_G/DriverOutput/Animation
Creating directory /Documents/SME/Projects//R_G/DriverOutput/UserData
Creating directory /Documents/SME/Projects//R_G/UserCode
Creating directories /Documents/SME/Projects//R_G/Data/cache.*
Current project directory set to /Documents/SME/Projects/
Current project set to R_G

This sets up the project directory.

Now I can put the equations file that I created in Stella into the directory Models.

I will also call my model R_G1 and do the SME command:

SME model R_G1

Now I get:

Current project directory is /Documents/SME/Projects/
Current project is R_G
Current model is xxx
Current scenario is xxx
Current model set to R_G1
Current project set to R_G

It is not important at this time, but let me also choose a scenarion name. We will see what that is later on. I do the command

SME scenario xxx

and get:

Current project directory is /Documents/SME/Projects/
Current project is R_G
Current model is R_G1
Current scenario is xxx

Step 3. Import and configure the model

Now we are all set.

I can already do

SME import.

This will take my equation file, and translate it into the MML (modular modeling language) specification. You probably will never need to see the result, but if you are curious you can take a look at the file Models/R_G1.MML for the MML specification and look at Models/R_G1/R_G1_module.xml, which is the same file in an intermediate XML specification.

At this same time the first config file has been generated in Config/R_G1.MML.config

This file still contains just a list of all variables and parameters of the model.

Let me do the SME build command now.

Something is processed, you get some messages, and at the end you may notice that some C++ code is already compiled. This is not important at this time, since most likely you will still need to do some more configuration before you will get something meaningful. What is important is that a couple of more config files are generated. See what is in the Config directory now:

R_G1.biflows
R_G1.conf
R_G1.xxx
R_G1.xxx.conf.out

The most important file is R_G1.conf. This will be the config file that we will be working with most of the time. At this time it has:

# global                             DS(1.0,0) n(1) s(4332) ngl(0) op(0) OT(1,0,20)  d( 0) UTM(0,0.0,0.0) UTM(1,1.0,1.0)
$ R_G1_module                       
* ALPHA                              pm(1)
* BETA                               pm(1)
* GRASS                              s(1) sC(C)
* GRAZING                            ft(u)
* G_GROWTH                           ft(u)
* K                                  pm(0.100000)
* MU                                 pm(0.100000)
* RABBITS                            s(1) sC(C)
* R_MORTALITY                        ft(u)
* TIME                              
* UPTAKE                             ft(u)

If we compare this file with the Stella equations above, we see that it contains information about all the parameters that we had there. In the equations:

alpha = 1
beta = 1
k = 0.1
mu = 0.1

In the R_G1.conf file:

* ALPHA                              pm(1)
* BETA                               pm(1)
* K                                  pm(0.100000)
* MU                                 pm(0.100000)

Step 4. Tweak your Stella model

What we did loose are the initial conditions. That is because in Stella we defined initial conditions in the state variables boxes, rather than as parameters. SME does not like that. Let me quickly go back to Stella and fix it by defining initial conditions in terms of some auxiliary parameters.

Grass(t) = Grass(t - dt) + (G_growth - Grazing) * dt
INIT Grass = G_init

INFLOWS:
G_growth = alpha*Grass
OUTFLOWS:
Grazing = beta*Grass*Rabbits
Rabbits(t) = Rabbits(t - dt) + (Uptake - R_mortality) * dt
INIT Rabbits = R_init

INFLOWS:
Uptake = k*Grazing
OUTFLOWS:
R_mortality = mu*Rabbits
alpha = 1
beta = 1
G_init = 2
k = 0.1
mu = 0.1
R_init = 1

Note the tiny difference between this set of equations and what we had above. This was easy. We will now have to do another SME import and SME build. Keep in mind that whenever we touch the equations we need to do a reimport and a rebuild. We do not need to reimport and rebuild if we only modify the parameters in the config file. However if any of the parameters are redefined as spatial a rebuild is needed. We will get back to this later.

So another SME import modifies the R_G1.MML.config file. But when we run SME build the R_G1.conf file will not be changed. This is a level of protection to make sure that you do not overwrite inadvertently your config file with all the valuable spatial information, by reimporting and rerunning the Stella equations that do not contain this data. This might be a little confusing, however it is important to protect the spatial version of your config file.

The output from the last rebuild can be found in R_G1.xxx.conf.out and if this is really what you want to do, you can delete your R_G1.conf file and rename the R_G1.xxx.conf.out into R_G1.conf. This is what I will do now to get the following as the config file for the model.

# global                             DS(1.0,48) n(1) s(4332) ngl(0) op(0) OT(1.0,0.0,20.0) d( 0) UTM(0,0.0,0.0) UTM(1,1.0,1.0)
$ R_G1_module                       
* ALPHA                              pm(1)
* BETA                               pm(1)
* GRASS                              s(1) sC(C)
* GRAZING                            ft(u)
* G_GROWTH                           ft(u)
* G_INIT                             pm(2)
* K                                  pm(0.100000)
* MU                                 pm(0.100000)
* RABBITS                            s(1) sC(C)
* R_INIT                             pm(1)
* R_MORTALITY                        ft(u)
* TIME                              
* UPTAKE                             ft(u)

Note that the initial conditions are now properly defined in this file. We are ready to run the model in SME. But first let us take another look at the config file. So we already guessed that pm() is a parameter in Stella. Whatever value the parameter had in Stella, it got automatically transfered into the config file. Also the state variables (GRASS and RABBITS in this case) are described by two commands s() and sC(C). What are they? The best available documentation for SME is on the web at http://www.uvm.edu/giee/SME3/ftp/Docs/UsersGuide.html. Most of the commands are described there, though not in the most fool-proof way. For the state variables we learn that s(1) means that we will be using the first order precision numeric method. We might also learn that the two commands that were generated by the SME build command are actually not quite consistent with the latest documentation: the sC(C) command could be erased and instead the s command should be s(C1C). However, SME will still run with the sC(C) command. C means that the variable should be clamped, that is it will not be allowed to become negative.

Step 5. Configure the output

The only other thing we want to take care of before we run the model is where does the output go. So far this is undefined. Let us use the P(0,0) command to see how the state variables change. The lines for GRASS and RABBITS will now be:

* GRASS                              P(0,0) s(C1C)
* RABBITS                            P(0,0) s(C1C)

Note that we also got rid of the outdated sC(C) command. Just one more thing before we run the model. Take a look at the first line in the config file, the one that starts with #global. This is a set of general configuration commands that are placed there by default by the translator. The two important ones that you may want to change right away are the OT() and the d() commands. Check out the SME documentation to learn more about them. The d() command sets up the debug level, that is the amount if information that will be provided into your command line interface. When we have d(0) - that is the minimal amount. If you want to see what equations are solved in which order and what actually happens during your model run, you probably want to bump up the debug level, making it d(1) or d(2).

The OT command defines the time step, the start and the end of the simulation. Right now we have OT(1.0,0.0,20.0), which means that we will run the model with a time step of 1, starting from day 0 and finishing on day 20. This will not allow us to go beyond 20. If we wish to have a longer simulation time, we need to change it to, say, OT(1.0,0.0,100.0). Now we can make 100 steps or less.

Step 6. Run the model

Finally we can do SME run and see what we get.

In the command line interface you get:

[AV-Computer:SME/Projects/R_G] voinov% SME run
*** Spatial Modeling Environment, Copyright (C) 1995 (TXU-707-542), Tom Maxwell
*** SME comes with ABSOLUTELY NO WARRANTY
*** This is free software, and you are welcome to redistribute it
*** under the terms of the GNU General Public License.
Current project directory is /Documents/SME/Projects/
Current project is R_G
Current model is R_G1
Current scenario is xxx
Running SME model R_G1 in serial mode, cmd:
/Documents/SME/Projects//R_G/Driver/R_G1 
-ppath /Documents/SME/Projects/ -p R_G -m R_G1  
-ci /Documents/SME/Projects//R_G/Config/R_G1.conf -pause 0 -scen xxx 

info: Setting Project Name to  R_G 
info: 
Allocating module R_G1_module; ignorable: 0

info: Reading Config Files
info: Opening config file: /Documents/SME/Projects/R_G/Config/R_G1.conf: 
info: Reading config file
warning: this program uses gets(), which is unsafe.
SME>

Here the driver stops and waits for you to tell what to do next. To run the model for 5 days you do

SME>r 5

If you have the debug level at d(1) you will probably get:

info: Setup Events
info: CreateEventLists
info: ProcessTemporalDependencies
info: ProcessSpatialDependencies
info: CreateEventLists
info: FillInitializationList
info: Split & Sort Lists
info: Setup Variables
info: Setting Up Frames & Schedules
info: Allocating Memory
info: Posting Events
info: Opened xml File: /Documents/SME/Projects/R_G/Models/R_G1/xxx/R_G1_module.xml.
info:  ******************** Executing Event R_G1_module:StateVarInit at time 0.000000
info:  ******************** Executing Event R_G1_module:StateVarInit__S__ at time 0.000000
info:  ******************** Executing Event R_G1_module:FirstUpdate at time 1.000000
info:  ******************** Executing Event R_G1_module:FirstUpdate__S__ at time 1.000000
info:  ******************** Executing Event R_G1_module:StateVarIntegrate at time 1.000000
info:  ******************** Executing Event R_G1_module:StateVarIntegrate__S__ at time 1.000000
info:  ******************** Executing Event R_G1_module:FinalUpdate at time 1.000000
info:  ******************** Executing Event R_G1_module:FinalUpdate__S__ at time 1.000000
info:  ******************** Executing Event R_G1_module:FirstUpdate at time 2.000000
info:  ******************** Executing Event R_G1_module:FirstUpdate__S__ at time 2.000000
info:  ******************** Executing Event R_G1_module:StateVarIntegrate at time 2.000000
info:  ******************** Executing Event R_G1_module:StateVarIntegrate__S__ at time 2.000000
info:  ******************** Executing Event R_G1_module:FinalUpdate at time 2.000000
info:  ******************** Executing Event R_G1_module:FinalUpdate__S__ at time 2.000000
info:  ******************** Executing Event R_G1_module:FirstUpdate at time 3.000000
info:  ******************** Executing Event R_G1_module:FirstUpdate__S__ at time 3.000000
info:  ******************** Executing Event R_G1_module:StateVarIntegrate at time 3.000000
info:  ******************** Executing Event R_G1_module:StateVarIntegrate__S__ at time 3.000000
info:  ******************** Executing Event R_G1_module:FinalUpdate at time 3.000000
info:  ******************** Executing Event R_G1_module:FinalUpdate__S__ at time 3.000000
info:  ******************** Executing Event R_G1_module:FirstUpdate at time 4.000000
info:  ******************** Executing Event R_G1_module:FirstUpdate__S__ at time 4.000000
info:  ******************** Executing Event R_G1_module:StateVarIntegrate at time 4.000000
info:  ******************** Executing Event R_G1_module:StateVarIntegrate__S__ at time 4.000000
info:  ******************** Executing Event R_G1_module:FinalUpdate at time 4.000000
info:  ******************** Executing Event R_G1_module:FinalUpdate__S__ at time 4.000000
info:  ******************** Executing Event R_G1_module:FirstUpdate at time 5.000000
info:  ******************** Executing Event R_G1_module:FirstUpdate__S__ at time 5.000000
info:  ******************** Executing Event R_G1_module:StateVarIntegrate at time 5.000000
info:  ******************** Executing Event R_G1_module:StateVarIntegrate__S__ at time 5.000000
info:  ******************** Executing Event R_G1_module:FinalUpdate at time 5.000000
info:  ******************** Executing Event R_G1_module:FinalUpdate__S__ at time 5.000000
TCL> 5 
SME>

And the model stops again. You have to issue another r command to continue. Let us run it till day 100:

SME>r 100

Now it stops and waits again. To quit we do

SME>X

Make sure you press Enter after each of these commands.

This is it. Now where are the results? Go to Projects/R_G1/DriverOutput. Here you might notice that two more files have been generated:

GRASS.PTS.P_p_0
RABBITS.PTS.P_p_1

Do not start looking for these files until you have quit the model run. They apeear only after the X command is issued. Now that we have exited SME, the files should be there. These are simple timeseries with output for GRASS and RABBITS respectively. The first column is the time, the second column is the value of the state variable. One way to look at these results is to simply copy and paste these files into Excel or another spreadsheet program. We can draw the graph and see that after a couple of oscillations the GRASS population crashed followed by the slow die off of the RABBITS. This is not exactly what we would expect from a standard predator-prey model. Where are those nice population numbers going up and down indefinitely?

But of course, I am running the model with the first order Euler method. That is a pretty rough approximation. Let me go to a more accurate numeric method. I open the config file and change to the fourth order method:

* GRASS                              P(0,0) s(C4C)
* RABBITS                            P(0,0) s(C4C)

Note that previously we had s(C1C), now we have s(C4C). This does it. If I rerun the model (SME run, then r 100), exit (X), go to the DriverOutput directory, and paste the output files into Excel - then I will get something I was expecting: nice lasting oscillations of both variables.

Step 7. Make it spatial

Well, this is good. But where is the spatial dynamics? We could get all this in Stella without all the troubles of setting up the model in SME. But how would we expect anything spatial if we have not defined anything spatial in our model. So far we were simply replicating the Stella model.

Now let us go spatial. First of all we will need some maps to describe the areals for grass and rabbits. Suppose we chose an area shown on this map:

These days the simplest way to generate these maps would be ArcInfo or ArcGIS, the monopolist on the GIS market. However if you can run GRASS that would also work. Anyway what we need to do is to generate a simple ascii file that would first of all describe the study area in our model. This one will have 1 inside the study area and a 0 everywhere else. It may look like this in one of the formats that SME would take, that is the MapII format:

FILETYPE=INTERCHANGE
ROWS=62
COLUMNS=67
CELLSIZE=200.000000
FORMAT=DEC
INFO="hunt.wsh"
DATA=0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 1 1 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 1 1 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 0 0 0 1 1 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 1 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 
0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 
0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 
0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 
0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 
0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 
0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 
0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 
0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 
0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 
0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 
0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 
0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 
0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 
0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 
0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 1 1 1 0 0 0 0 0 
0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 1 1 1 1 1 1 1 1 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 1 1 1 1 1 1 1 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 1 1 1 1 1 1 1 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 0 0 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 
LABELS

This is very similar with the ArcInfo ascii format. Everything will be the same except for the first header lines:

ncols         62
nrows         67
xllcorner     443396.06231037
yllcorner     241834.36232137
cellsize      30
NODATA_value  -9999
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 
...

The bottom line is that it does not matter what software is used as long as we can get our map files into one of these ascii formats. One thing to watch out, especially if you are preparing your data in Windows, is the EOL - end of line - symbol. It is different in Windows and Unix. Since SME is using Unix conventions it will most likely choke when it has to read files with the "wrong" EOL symbol. You can try the dos2unix command to convert your files - usually it comes with the standard distribution of Unix or Linux. However it does not come with Darwin, the Mac OSX Unix kernel. Luckily on a Macintosh there are other ways to handle this. For example, BBEdit allows you to choose the format you need for your text files.

Now that we have the map let us get it into the model. As we remember it is mostly the config file that SME uses to link the model with the data. We define the model as spatial using the g() command:

$ R_G1_module     g(A,/Documents/SME/Projects/R_G/Data/Maps/Area.arc,default,
                                /Documents/SME/Projects/R_G/Data/Maps/Area.arc)  AL(0,0)

The long path /Documents/SME/Projects/R_G/Data/Maps/Area.arc points to the Area.arc map that is the ArcInfo ascii format for the study area that we chose. Now we have initialized the model as a spatial one, but we have not identified any spatial variables: all of them are still treated as single numbers. To do that we can use another SME command - oi(). It is called "override initialiization" and it has two parameters. If the first parameter is positive, then the variable is assumed constant. If the second parameter is positive then the variable is assumed spatially distributed. So if we configure, say, GRASS as

* GRASS                              s(C4C) oi(0,1)
we should get what we want - a spatially distributed variable.

Step 8. Startup Viewserver

Let us add yet another command to this line:

* GRASS                              DD() s(C4C) oi(0,1)
The DD() command establishes a connection with the Viewserver - a very important piece of software used to display results of spatial simulation. The Viewserver should be started using the command startup_viewserver. I recommend that you do it from a separate terminal window, since the Viewserver generates a lot of command line output that will clog the terminal that you use to run SME.

I will also generate spatial output for the other model variable, RABBITS:

* RABBITS                            DD() s(C4C)
Note that in this case we do not even need to declare the variable as spatial. Since in the model it is dependent upon an already spatial variable (GRASS), it will become spatial automatically.

Once you have started the viewserver, and done another SME run, you can see that the viewserver receives output from the running model and a new data set gets added to the list on the left pannel of the Viewserver. If you highlight one of the data sets and then choose a 2D animation viewer, and click the "Create" button you will get an image of the map that is now dynamically changing as the variables change their values across the whole area. You can watch how the Grass and Rabbits alternate their bimasses changing from minimal (blue) to maximal (red) numbers.

GRASS - max: 2.043, min: 0.3279 RABBITS - max: 1.285, min: 0.7642

Step 9. Make it spatially heterogeneous

This may be pretty but still quite useless. While the variables are spatial there is no spatial heterogeneity. Let us try to add some.

Suppose we have a spatially heterogeneous initial condition for the GRASS biomass. Suppose grass is not uniformly distributed but has different biomass in different locations. We will initialze the GRASS variable with a map that has different values in different cells. Let us use this map:

Note that the spatial extend of this map is different from the map above. That is OK. SME will crop this map to match it to the area defined above by the Study Area map. How do we input this new map? Back to the config file. This time instead of defining the initial condition for GRASS as a constant parameter pm(2), we will use a map:

* G_INIT         d(A,/Documents/SME/Projects/R_G/Data/Maps/Biomass.arc,
                              /Documents/SME/Projects/R_G/Data/Maps/Area.arc)

Again we have to provide the full path to the map file that we want to use. There is actually a better way to do it using the Environment file. This file should reside in the Data directory and it contains all the paths that we may wish to use in the configuration files. For this model we will put the following two lines into the Projects/R_G/Data/Environment file:

MAPS=/Documents/SME/Projects/R_G/Data/Maps
RMAP=/Documents/SME/Projects/R_G/Data/Maps/Area.asc

The first line defines the Maps directory, which we seem to be constantly referring to. The other line is the full name of the reference map, or the Study Area map, which is used to crop all the other maps in the project.

Now some of the lines in the configuration file can look much shorter:

$ R_G1_module            g(A,${RMAP},default,${RMAP})  AL(0,0)
...
* G_INIT                 d(A,${MAPS}/Biomass.arc,${RMAP})
...

Moreover, the Biomass map that I use to initialize the model has values between 0 and 61. The initial condition that we used before was 2. It would be nice if I could scale the map to some values that would be closer to what I had originally. Let us use the S() command to do it. The syntaxis of this command is S(a,b), which means that if x is the input value then the result of this command is y = a*x + b. So finally if I use the command

* G_INIT                 d(A,${MAPS}/Biomass.arc,${RMAP}) S(.01e+00,1.0)
this means that I will input the map from the Biomass.arc file, then each value will be multiplied by 0.01 and then added to 1. That will be the result used in the simulations. Also note that we no longer need the oi(0,1) command, since now we have the initial condition that initialized the variable as a spatial one, which ensures that all the rest of the variables connected to the spatial one, will also be spatial.

GRASS - max: 1.490, min: 0.5 RABBITS - max: 1.142, min: 0.86
Note that the white space that we see is probably a bug with saving gif images, not real model output. In the Viewserver itself the image is nice and smooth!

This is a little more interesting, there are some spatial variations, there are some differences in how different cells evolve. However, still there is no interaction between cells and the real spatial context is not present. We simply have a whole bunch of models running in sync, but they do not interact with each other.

Step 10. Make things move spatially

To make cells talk to each other is a little more complex than what we have been doing before. Whereas so far we were simply using some predefined commands, and the model we built in Stella, from now on if we are to define some meaningful spatial interaction we will need to do some programming.

There are some modules that we can use in the Library of Hydro-Ecological Modules, however there are not too many things we can do with those predesigned modules. If you really want to be able to build complex spatial models you probably will need to be capable of some level of c++ programming. SME supports so called User Code and offers full access to its classes and methods, which can significantly help you design your own code for spatial dynamics.

Suppose for our Rabbits & Grass model we wish to allow rabbits to move between cells in search of better grazing conditions. We will assume that whenever rabbits find that there is more grass in the neighbor cell a certain proportion of rabbits from the current cell will move to the cell with more grass. Let us write the code that will describe this behavior of the predator.

/**************************************************************************/
#include "Rabbit.h"
/**************************************************************************/

void MoveRabbits( CVariable& Rabbits, CVariable& Grass, CVariable& Rate )
// moves rabbits toward more grass, if there are less rabbits there		  
// arguments come from MML.config file, first arg is always variable being configured.
{
               Grid_Direction il;
	float fr, R_moved = 0.;

	DistributedGrid& grid = Rabbits.Grid();
	grid.SetPointOrdering(0);
	// sets grid ordering to default ordering (row-col) (ordering #0)

	Rabbits.LinkEdges();
	Grass.LinkEdges();

	static CVariable* R_Flux = NULL;	
	if(R_Flux == NULL ) 
	           R_Flux = Grass.GetSimilarVariable("R_Flux"); 
	
	// intermediate increment to Rabbits
	R_Flux->Set(0.0);

	for( Pix p = grid.first(); p; grid.next(p) ) 
	{ 
	      const OrderedPoint& pt = grid.GetPoint(p); 
	      // sets currentPoint

	      if( !grid.onGrid(pt) ) continue;  
	      // (onGrid == False) -> Ghost Point
	      
	      float g_max = Grass(pt);
	      Pix p_max = p;
	      
                     // for each point calculate where is the max Grass 
                     // in the vicinity
	      for( il = firstGD(); moreGD(il); incrGD(il) ) 
                     { 
	      // enum Grid_Direction {NE=2, EE, SE, SS, SW, WW, NW, NN};

                           Pix rp = grid.NeighborPix( p, il ); 
	            // relative to pt, takes enum Grid_Direction as arg

	            if( rp )  
	                {
	                    const OrderedPoint& rpt = grid.GetPoint(rp); 
	                    if ( Grass(rpt) > g_max ) 
	                    {         g_max = Grass(rpt);
                                             p_max = rp;
                                   }
	                } 
	      }
	      
	      const OrderedPoint& pt_max = grid.GetPoint(p_max); 
	      // sets currentPoint

	      if ( g_max > Grass(pt) )
	         fr = ( Rabbits(pt) > Rabbits(pt_max) ) ? 
	              (Rabbits(pt) - Rabbits(pt_max)) * Rate(pt) : 0;
	      
	      (*R_Flux)(pt_max) += fr;
	      (*R_Flux)(pt) -= fr;
	      R_moved += fr;
	
	}// end area loop
	
	Rabbits.AddData(*R_Flux);
	printf ("\ninfo: Rabbits moved = %f", R_moved);

}
/**************************************************************************/

So here we have only Rabbits moving horizontally from one cell to another in search of a better life. How do we tell SME that there is something new that the model wants to take into account?

First we go all the way back to the MML.config file that we can find in the Config directory. In this file we add a command for Rabbits:

* RABBITS                            UF( Rabbit,MoveRabbits,GRASS,RATE)

Here Rabbit is the name of the file that contains the above c++ code. Actually its name is Rabbit.cc and it lives in the UserCode directory. MoveRabbits is the name of the function in this file that we use. GRASS and RATE are two variables that are passed to this function. While GRASS was always there, RATE is actually new. The way I got into this config file is by modifying the Stella model and adding another variable into there. Then once again I had to export the equations, and then I had to do the SME import command. Alternatively I could have modified the equation file at the very beginning of this manual. I simply needed to add one line:

rate = 0.5

And then I could also do this by hand in the R_G1.MML.config file. I would caution you about this however, since it is very easy to forget about some of these small modification to your equation file. There is no way you can import these modifications from the equations to the Stella model. As a result once you finally decide that you wish to modify the Stella model for some other reason later on, most likely you will forget then about this modification of the equations file that you have just done. You will then take the eqations from Stella, create a new equtions file and loose all these previous changes. When you start running the new model you will find out that it performs quite contrary to your expectations, and will take you quite a while to figure out why and to do all the little updates once again. So while every now and then it seems very simple to modify just the equation file, actually you will be much better off if all the modifications are done directly to the Stella model.

As we remember, whenever the equations or the MML.config file is changed we need to do the SME import command. Now we can do the SME build command, and update the config file to add the RATE parameter to it as well. Remember - either you do it by hand, or rename the file to use the R_G1.xxx.conf.out instead. As a result you get:

* RATE                               pm(0.5)

Are we ready to run? We can try, but I bet that the driver will choke, most likely with a Buss Error. This will leave you quite perplexed and frustrated because it is pretty hard to debug the code at this stage. However, I will give you a hint.

The variables that you have been passing to the newly designed function to move Rabbits were all assumed to be spatial.

MoveRabbits( CVariable& Rabbits, CVariable& Grass, CVariable& Rate )

However the RATE parameter as we defined above is a scalar. There is an easy fix. Just add the override command:

* RATE                               pm(0.5) oi(0,1)
and you will be back in game. Alternatively you could also define this parameter as a map:
* RATE                               d(A,${RMAP},${RMAP}) S(.5e+00,0.0)

Here I used the Study Area map to initialize this parmater, which with this scaling factor is identical to what we did above. However, this could be any map, which would probably be the only reasonable way to define this parameter if we wanted it to be spatially heterogeneous.

Or if you do not want this parameter to be spatial, do not refer to it as if it were spatial in the code. Replace Rate(pt) for Rate.Value(). Rate.Value() is a scalar, it will not need to be initialized by a map or a spatial variable. It will take pm(0.5). That's it.

Finally we are ready to hit the SME run button and watch something moving across the landscape, Rabbits hoping from one place to another, Grass dying and regrowing back when the predators leave, and so on. Lots of fun!

GRASS - max: 2.0, min: 0.0 RABBITS - max: 2.0, min: 0.62
Note that the white space that we see is probably a bug with saving gif images, not real model output. In the Viewserver itself the image is nice and smooth!

Click HERE to download the project directory.


E-mail to Alexey Voinov