MBS-CD MBS : The CDROM

advertisement
MBS-CD: The MBS CDROM
James W. Haefner
James.Haefner@usu.edu
Version 0.1 on March 3, 2005
This document describes the contents of MBS-CD: the CDROM that accompanies Modeling Biological Systems:
Principles and Applications. Second Edition. 2005. Springer Publishers. New York.
To begin using this CDROM, click on the Contents button to the right. Chapters 2–6 present a variety of general
computing and computer simulation topics: Unix/Linux, Integrated Development Environments, and so on. Chapter 7
contains code relevant to the text’s chapters. Each section of Chapter 7 corresponds to a chapter in the MBS textbook.
Not all chapters have code for both C and octave/Matlab. Some chapters have no code at all. More is forthcoming
in future editions of this CD.
The latest version can be obtained by contacting the author at the above email address or on the WWW at the MBSCDROM webpage. Various other supporting libraries are also available at this website.
Contents
1
Preliminaries
1.1 Caveat . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.2 Acknowledgements . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.3 License . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
5
5
5
5
2
How to Build a Model Without Reading the Manual
2.1 Models in C . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
2.2 Models in Octave/Matlab . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
7
7
8
3
A Brief Primer on C Programming
3.1 Preprocessor . . . . . . . . . . . . . . . . .
3.2 Using Defines for Array Elements . . . . .
3.3 Reading From Console . . . . . . . . . . .
3.4 Reading Data Files With Comments . . . .
3.5 Pointers to Arrays . . . . . . . . . . . . . .
3.6 Dynamic Allocation and Use of 2D Arrays .
3.7 Structures . . . . . . . . . . . . . . . . . .
3.8 Linked Lists . . . . . . . . . . . . . . . . .
.
.
.
.
.
.
.
.
9
9
10
10
11
11
12
13
13
4
An Even Briefer Primer on octave/Matlab Programming
4.1 Matlab . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
4.2 octave . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
14
14
14
5
How-To Do Computer Simulations the MBS Way (Linux Version)
5.1 Using Linux and Knoppix . . . . . . . . . . . . . . . . . . . .
5.1.1 Linux . . . . . . . . . . . . . . . . . . . . . . . . . . .
5.1.2 Booting from Knoppix . . . . . . . . . . . . . . . . . .
5.1.3 Knoppix . . . . . . . . . . . . . . . . . . . . . . . . .
5.1.4 Logon to Another Linux Box . . . . . . . . . . . . . . .
5.1.5 Print and Emailing Files . . . . . . . . . . . . . . . . .
5.1.6 Basic Unix Commands . . . . . . . . . . . . . . . . . .
5.1.7 Exiting from Knoppix . . . . . . . . . . . . . . . . . .
5.2 The Anjuta Programming Environment . . . . . . . . . . . .
5.2.1 Integrated Programming Environments . . . . . . . . .
5.2.2 The Basic IDEa . . . . . . . . . . . . . . . . . . . . . .
5.2.3 Starting a New Anjuta IDE Session . . . . . . . . . . .
5.2.4 Using External Libraries in anjuta . . . . . . . . . .
5.2.5 Setting-Up anjuta to Use Libraries . . . . . . . . . .
5.3 Plotting . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
5.3.1 Background . . . . . . . . . . . . . . . . . . . . . . . .
5.3.2 How to use SimPlot: All Sim**() Functions . . . .
5.3.3 Example . . . . . . . . . . . . . . . . . . . . . . . . .
5.3.4 Plotting Many Curves . . . . . . . . . . . . . . . . . .
15
17
17
18
18
19
20
20
21
22
22
22
22
29
29
33
33
33
38
38
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
2
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
5.4
5.3.5 Plotting Grids . . . . . . . . . . . . . . . . . . .
5.3.6 Plotting Histograms . . . . . . . . . . . . . . .
5.3.7 How SimPlot Works . . . . . . . . . . . . . .
How to Use the SimGSL Templates . . . . . . . . . . .
5.4.1 GSL . . . . . . . . . . . . . . . . . . . . . . . .
5.4.2 Template Overview . . . . . . . . . . . . . . . .
5.4.3 Arrays . . . . . . . . . . . . . . . . . . . . . . .
5.4.4 Interfacing with GSL functions . . . . . . . . .
5.4.5 Detailed Explanation of the Template . . . . . .
5.4.6 Complete Code for SimTemplate-Empty.c
6
Using MBS Modeling in Microsoft Windows
7
Source Code
7.1 MBS Chapter 1: Models of Systems . . . . . . . .
7.1.1 Code Links . . . . . . . . . . . . . . . . .
7.1.2 Explanation . . . . . . . . . . . . . . . . .
7.2 MBS Chapter 2: The Modeling Process . . . . . .
7.3 MBS Chapter 3: Qualitative Model Formulation . .
7.4 MBS Chapter 4: Quantitative Model Formulation: I
7.4.1 Code Links . . . . . . . . . . . . . . . . .
7.4.2 Explanation . . . . . . . . . . . . . . . . .
7.5 MBS Chapter 5: Quantitative Model Formulation:II
7.5.1 Code Links . . . . . . . . . . . . . . . . .
7.5.2 Explanation . . . . . . . . . . . . . . . . .
7.6 MBS Chapter 6: Numerical Techniques . . . . . .
7.6.1 Code Links . . . . . . . . . . . . . . . . .
7.7 MBS Chapter 7: Parameter Estimation . . . . . . .
7.7.1 Code Links . . . . . . . . . . . . . . . . .
7.7.2 Linear Regression . . . . . . . . . . . . .
7.7.3 Levenberg-Marquardt . . . . . . . . . . .
7.7.4 Simplex . . . . . . . . . . . . . . . . . . .
7.7.5 Calibrating Models Using Simplex . . . . .
7.7.6 Examples and Hints . . . . . . . . . . . .
7.8 MBS Chapter 8: Model Validation . . . . . . . . .
7.8.1 Code Links . . . . . . . . . . . . . . . . .
7.8.2 Explanation . . . . . . . . . . . . . . . . .
7.8.3 SimValidate() Output . . . . . . . . .
7.9 MBS Chapter 9: Model Analysis . . . . . . . . . .
7.9.1 Code Links . . . . . . . . . . . . . . . . .
7.9.2 Error Analysis . . . . . . . . . . . . . . .
7.9.3 Nullclines . . . . . . . . . . . . . . . . . .
7.10 MBS Chapter 10: Stochastic Models . . . . . . . .
7.10.1 Code Links . . . . . . . . . . . . . . . . .
7.10.2 Explanation . . . . . . . . . . . . . . . . .
7.11 MBS Chapter 11: Photosynthesis and Plant Growth
7.12 MBS Chapter 12: Hormonal Control in Mammals .
7.13 MBS Chapter 13: Populations and Individuals . . .
7.13.1 Code Links . . . . . . . . . . . . . . . . .
7.13.2 Population-level Models . . . . . . . . . .
7.13.3 Individual-based Models . . . . . . . . . .
7.13.4 Linked Lists . . . . . . . . . . . . . . . .
7.13.5 Coding Doubly Linked Lists . . . . . . . .
7.14 MBS Chapter 14: Chemostats . . . . . . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
39
39
39
43
43
43
43
44
44
50
55
3
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
56
59
59
59
59
59
59
59
60
62
62
62
63
64
64
64
65
66
68
70
73
73
73
74
74
76
76
76
78
79
79
80
80
80
80
80
81
81
81
82
87
8
9
7.14.1 Code Links . . . . . . . . . . . . . . . . . . . . . .
7.15 MBS Chapter 15: Diseases . . . . . . . . . . . . . . . . . .
7.15.1 Code Links . . . . . . . . . . . . . . . . . . . . . .
7.16 MBS Chapter 16: Spatial Patterns and Processes . . . . . . .
7.17 MBS Chapter 17: Scaling Models . . . . . . . . . . . . . .
7.18 MBS Chapter 18: Chaos in Biology . . . . . . . . . . . . .
7.19 MBS Chapter 19: Cellular Automata and Recursive Growth
7.19.1 Code Links . . . . . . . . . . . . . . . . . . . . . .
7.19.2 Data Structures . . . . . . . . . . . . . . . . . . . .
7.19.3 Swapping Grids . . . . . . . . . . . . . . . . . . . .
7.20 MBS Chapter 20: Evolutionary Computation . . . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
87
88
88
88
88
88
88
88
89
89
90
Plotting and Analysis Code
8.1 Code Links . . . . . . . . .
8.2 How to Compile the Library
8.2.1 Compiling . . . . .
8.2.2 Installing . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
91
91
91
91
92
Installing Ancillary Libraries: Linux and Windows
9.1 Linux Users . . . . . . . . . . . . . . . . . . . .
9.1.1 DISLIN . . . . . . . . . . . . . . . . . .
9.1.2 GSL . . . . . . . . . . . . . . . . . . . .
9.2 Windows Users . . . . . . . . . . . . . . . . . .
9.2.1 Programming with an IDE . . . . . . . .
9.2.2 Programming with Make . . . . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
93
94
94
94
94
94
97
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
4
Chapter 1
Preliminaries
Contents
1.1
1.2
1.3
1.1
Caveat . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Acknowledgements . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
License . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
5
5
5
Caveat
The document you are reading is what is commonly known as a “beta” version; it is, so to say, under development.
The author, being a C programmer at heart, has made most of the available code for that language. The octave and
Matlab code for model analysis (e.g., validation, model discrimination, Monte Carlo analysis, and so on) is currently
being developed. There is more code for the models of the textbook Part II, but much is still lacking.
To get the latest version, go to the URL: the MBS-CDROM webpage. There you will find ISO9660 format CD
images you can download. They will be identified by version number and date.
1.2
Acknowledgements
Here is my non-commercial for free, open-source software. I gratefully acknowledge the many contributors to the
following open-source software: Linux; LATEXand pdfLATEX, and especially the memoir class, pdfscreen, and
fancyvrb.sty; the Gnu Scientific Library (GSL); octave and associated open-source applications including;
anjuta; Dev-C++; and the multitude of free code available for Matlab and octave. Although not open-source,
the free DISLIN plotting library and Acrobat Reader software are significant. Another fine commercial program
that I use extensively and which deserves wider recognition is the drawing and plotting package from Cohort Software
(www.cohort.com).
Aside: You will often see the expression “GNU” associated with open-source software. GNU stands for “Gnu is
Not Unix.” It’s a long story, but the term stems from yet another futile attempt to stifle creativity when AT&T/Bell
Labs attempted to patent, copyright, and license its version of Unix. This mindset led to Linux and we all know what
happened to Bell Labs.
1.3
License
All of the source code for functions of the SimPlot graphing library on the CD is released under the GPL license for
free, open-source software and carry the appropriate GPL disclaimer. (A complete statement of the license is available
at: http://www.gnu.org/copyleft/gpl.html.) These functions use the free DISLIN plotting libraries for
the Linux gcc compiler and the MinGW/MingW32 implementations of the gcc compiler for Microsoft Windows.
The DISLIN license statement can be found at http://www.mps.mpg.de/dislin/.
5
Other source code files not carrying the GPL license statement (e.g., code for particular models), are free and not
covered by the GPL license. None of the software on this CD is warranted to (a) work, (b) be useful, or (c) have any
enduring educational value.
6
Chapter 2
How to Build a Model Without Reading the
Manual
Contents
2.1
2.2
Models in C . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Models in Octave/Matlab . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
7
8
For the impatient, here is the shortcut. For those seeking understanding and enlightenment, you can start at Chapter 5.
2.1
Models in C
1. The hardest step: One time: install the GSL (subsection 9.1.2), DISLIN (subsection 9.1.1), and SimPlot libraries
(chapter 8).
2. Leave the PDF file you are currently reading open, and open a new terminal window. (Linux/Unix users don’t
need help with this; but Window users can try: Start->Run: cmd.exe.)
3. Create a subdirectory to hold the files for a model; make it the current directory. (Hint: in Unix, try mkdir
foomodel;cd foomodel, in Windows/(DOS) try: md foomodel; cd foomodel.) (Interesting how
similar the commmands are, isn’t it? Guess which operating system came first.)
4. Go to the link Michaelis-Menten Chemostat Model and from the newly opened web browser window, copy three
files to your new subdirectory:
(a) The SimChemo-00.c file of model source code (e.g., example in link).
(b) The SimChemo-00.ctrl file of model inputs (e.g., example in link)
(c) The mkSimChemo-00 file that compiles the program (e.g., example in link).
Windows users: clicking on the provided link will launch InternetExplorer if you have Acrobat
Reader 6.0 (other versions not tested). Linux users: if you use acroread version 5.0.6 or
5.0.10, clicking on the link will work with Mozilla or Firefox provided the browser is already
launched somewhere in your workspace. If not, the launch may fail on the first try but will succeed
if you click on the link a second time with the browser open. Other users: leave this PDF file and
manually copy the code files to your local directories.
Note that there are two versions of the C source on the webpage. The label VIEW Colorized: indicates an
html file with C keywords colored and highlighted. This file will NOT compile. Download the file labeled:
DOWNLOAD Source:; this one will compile.
5. Edit the mkSimChemo-00 file for the correct compiler paths on your system in which to search for libraries
and include files.
7
6. Go to the open terminal window and, at the system commandline prompt, type:
make -f mkSimChemo-00
If you properly installed the libraries, several new files will be present in your directory, including chemo00.
This is the executeable simulation model.
7. At the command line prompt, type:
chemo00
8. At the first prompt, type
SimChemo-00.ctrl
9. When the SimPlot menu appears, type the number 1 (“one”). A plot appears. (If you get the error message:
“You chose poorly.”, type the menu item twice before pressing Enter (i.e., type “11”).)
10. Exercise the model by editing the SimChemo-00.ctrl file with new parameter values.
2.2
Models in Octave/Matlab
1. Do steps 2 and 3 above.
2. Go to the link having an Octave or Matlab file and save the *Run.m and *Func.m files to your new modeling
directory.
E.g., Basic Chemostat Model in Octave.
If you will be using octave, you will also probably need the file DoOnePlot.m. Close the browser windows.
(The Octave/Matlab files are stored with file extensions of .m . This is necessary to avoid the browser automatically launching an application to execute the script file.)
3. In your modeling directory, execute either Matlab or octave, according to which file you captured. Once the
computing interpreter is running and you have its prompt sign (for either Matlab or octave), type in the name of
the file whose name contains the string Run.m, (e.g., MChemostatRun.m).
4. If Matlab, a plot will appear. If octave, after the model executes, type PlotY1(1,"",ty); and a plot will
happen.
8
Chapter 3
A Brief Primer on C Programming
Contents
3.1
3.2
3.3
3.4
3.5
3.6
3.7
3.8
Preprocessor . . . . . . . . . . . . . . . .
Using Defines for Array Elements . . . . .
Reading From Console . . . . . . . . . . .
Reading Data Files With Comments . . .
Pointers to Arrays . . . . . . . . . . . . .
Dynamic Allocation and Use of 2D Arrays
Structures . . . . . . . . . . . . . . . . . .
Linked Lists . . . . . . . . . . . . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
9
10
10
11
11
12
13
13
For the latest version of the MBS-CD consult the MBS-CDROM webpage. This CD is still in development; however,
there are many tutorials on the C language available online. Try a web search for C programming tutorial.
Also, there is the fine book by Will Wilson (2000. Simulating Ecological and Evolutionary Systems in C. Cambridge
University Press). A more complete, interactive tutorial may be available here in the future, but for now here are some
explanations of special constructs and coding practices that are used in the simulation and plotting package supplied
on the MBS-CD.
3.1
Preprocessor
The C preprocessor runs before the C compiler and basically acts like a text editor: e.g., it can substitute C code for
text strings in the original source code. This is an extremely useful concept, as illustrated by some examples below.
9
#include <filename>
#include "filename"
#define SOMETHNG 2bSomething
#define macro(argument)
3.2
By using the angle brackets around “filename”,
#include <systemheader.h> inserts into the
source code file at the point that it occurs a file that is
part of the C “system”; the file is located in a standard
directory that is searched by the compiler and linker.
This is standard practice both for built-in files as well
as third-party libraries such as GSL.
By
using
double-quotation
symbols,
#include "myheader.h" inserts into the
source code file at the point that it occurs a file that is
not part of the C system, but is a local file found on the
current working directory. This file is usually supplied
by the programmer.
Substitutes in the source code file the string “2bSomething” whenever the preprocessor encounters “SOMETHING.” E.g. #define PI 3.1415926 replaces
“PI” with 3.1415926, the latter of which will be
interpreted as a number. Some programmers use the
convention of writing defines such as this in uppercase.
#define can also be used to write simple in-line
functions to which arguments can be passed.. E.g.,
#define MIN(x,y) (x<=y?x:y) returns the
minimum of two numbers. Whenever the preprocessor
encounters MIN(x,y), it substitutes (x<=y?x:y).
Using Defines for Array Elements
Arrays are essential elements of scientific computation. Among other uses, they simplify passing arguments to functions. Unfortunately, to use such arrays, numerical values must be used for array indices. This implies code that looks
like z=p[37]+p[17]/A[3]. Debugging this requires a knowledge of the meaning of the numerical indices (e.g.,
“17”). Better would be: z=p[CARBON]+p[LIGHT]/A[SOIL] The preprocessor can do this:
#define CARBON
#define LIGHT
#define SOIL
37
17
3
and so on for other elements of arrays.
3.3
Reading From Console
Reading input from the console usually implies the user typing the desired input and then pressing the Enter key.
Usually this leaves the value of Enter in the input buffer. On Unix systems this value is the linefeed symbol
(ASCII character 0x0A), in MSDOS it is two characters: carriage-return (0x0D) and linefeed (0x0A).
Having this character(s) in the buffer is usually bad because subseqent reads from the console will see the character
and may not behave correctly.
It is good practise to eat the Enter character after each read:
scanf("%d%*c",&i);
which reads an integer into variable i and then reads, but discards, the next character in the input stream (%*c).
10
3.4
Reading Data Files With Comments
When reading from files, it is good practise to create files that have text documentation to remind the user what the
numbers mean. A structure used in the MBS-CD code is:
fscanf(file_in,"%*s%lf%*[ˆ\n]c\n",&p[LITTLE_R]);
which reads and discards a string, then reads and stores in array element p[LITTLE R] a double, and then reads and
discards all characters up to the end of the file line. A line in a file might look like this:
LITTLE_R
0.03
% maximum per capita growth rate
The first field is the name used in the source code, the second field is the actual number to read, and the third field is
an explanatory description.
3.5
Pointers to Arrays
There are two types of arrays: statically declared arrays and dynamically allocated arrays. The bounds and types of
static arrays are declared when the program is compiled. If the array is global, the memory is allocated when the
program is loaded into memory, before execution begins. If the array is local to a function, the memory is allocated
when program execution enters the function, but before the first line of function code is executed. The size and type
of dynamic arrays are declared and created at whatever point in program execution the code to allocate memory is
executed.
Static arrays are easier to work with than dynamic arrays (next section below). Static arrays are declared as:
double BigA[1000];
A problem is that any given program, during execution, may require that the array to be larger than its static allocation.
If this happens, the program must terminate and the code be recompiled with larger values. As a result, since computer
memory is plentiful, it is typical to allocate arrays to be much larger than needed, and then maintain a variable that
stores the number of array elements that are actually being used. This is the strategy used at many points in the
SimPlot library. It is helpful to use preprocessor defines:
#define MAXSIZE 20000
float PlotVals[MAXSIZE];
int
numpoints_saved;
Another trick is to use aliases for global arrays in functions to reduce the number of arguments that are passed to
functions. This is a design decision weighing the complexity of function use with many parameters against the power
of passing arrays directly. SimPlot uses the former approach to simplify the use of the library. Here is how it is
done:
Globally, we declare pointers to arrays:
float *(PlotData[25]);
#define PLOTTIME
(PlotData[0])
#define PLOTVAR1
(PlotData[1])
/* array of addresses of global plotting arrays*/
/* defines for each of the array elements */
/* defines for each of the array elements */
PlotData[] is an array of addresses to float arrays. PLOTVAR1 is one of these addresses; no actual memory is
allocated to the contents of the arrays, only their addresses are allocated.
In the user’s program, we declare global storage to our actual plotting arrays:
float PlotVals1[MAXSIZE];
float PlotVals2[MAXSIZE];
These lines allocate MAXSIZE floats to the 2 arrays. We then assign the global array address to the actual array of
floats:
PLOTVAR1 = PlotVals1;
11
PLOTVAR1 is now an alias for PlotVals1 and a function can assign values to the latter using the former:
PLOTVAR1[3] = 2.17159;
When we wish to use the same function to write to different array, we re-assign PLOTVAR1 to the new array:
PLOTVAR1 = PlotVals2;
3.6
Dynamic Allocation and Use of 2D Arrays
Memory for dynamically allocated arrays is allocated using the built-in C function malloc(). For example, a single
dimension array of floats is allocated by:
float *float_array_ptr;
/* array name */
int NumRow;
/* number of elements in array */
if((float_array_ptr=(float *)malloc(sizeof(float)*NumRow))==NULL) {
fprintf(stderr,"!!float_array_ptr[] not allocated. Size requested = %d\n",
sizeof(float)*NumRow);
exit(1);
}
Always check that the memory has been successfully allocated (i.e., float_array_ptr != NULL).
Once allocated, dynamic one-dimensional array elements can be addressed:
float_array_ptr[3] = 3.14159;
my_pi=float_array_ptr[3];
Two dimensional arrays are another story. Computer memory is not organized as tables or matrices, but rather as a
possibly long “column” of values. C is a row major language, meaning that the column of memory locations is created
by stacking rows vertically, one on “top” of the other. When we access elements using the BigA[3][17] notation,
it is implied that we know the number of columns in the 2D array so that we can locate the proper memory location
in the column of values. Dynamically allocating a true 2D array requires that we allocate NumRow 1D arrays each
having NumCol number of columns. It’s all very messy. A simpler method is to allocate the entire 2D array as a 1D
column of numbers and then abandon the BigA[][] notation and access the 1D column of addresses directly with
pointers. To allocate:
int NumRow=15, NumCol=5;
float *Y;
if((Y=(float *)malloc(sizeof(float)*NumRow*NumCol))==NULL) {
fprintf(stderr,"!!Y[] not allocated. Size requested = %d\n",
sizeof(float)*NumRow*NumCol);
exit(1);
}
To fill the Y “matrix” with values:
float *yp;
/* pointer to value in Y[] */
for(i=0;i<NumRow;i++){
for(j=0;j<NumCol;j++){
yp = Y + i*yp + j;
*yp = 0.0;
}
}
/* use: retrieve Y[3][17]*/
z= Y + 3*NumCol + 17;
/* use: assign to Y[3][17]*/
yp = Y + 3*NumCol + 17;
*yp = 1.17;
When dynamically allocated memory is no longer needed (e.g., when leaving the function in which it was allocated), it must be explicitly de-allocated or “freed”. This is done as follows for the matrix y[]:
free(Y);
12
3.7
Structures
Arrays are collections of a simple C datatype (e.g., double) in contiguous memory locations. Structures are collections of multiple, different datatypes in contiguous memory locations. Arrays of structures are collections of these
hetergeneous objects in contiguous memory.
Casting the structure as a new data type (analogous to double) using the C reserved word typedef is the most
flexible use:
typedef struct data {
int state;
float age;
char name[20];
long int soc_sec_num;
} A_Person;
Declares A_Person to be a new type of data containing the information listed. To create a static array of “persons”
and assign values to the 18th element:
A_Person Group[30];
Group[17].state=0;
Group[17].age=34;
Group[17].name="Dr. Dobbs";
Group[17].soc_sec_num=000112222;
3.8
Linked Lists
A linked list is a collection of (usually) dynamically allocated memory that is not contiguous in memory, but still
connected to each other in a series through a system of inter-related pointers. This concept is used extensively in
individual-based models. See subsection 7.13.4.
13
Chapter 4
An Even Briefer Primer on octave/Matlab
Programming
Contents
4.1
4.2
Matlab . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
octave . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
14
14
For the latest version of the MBS-CD consult the MBS-CDROM webpage. There are many tutorials on Matlab and
octave on the WWW. Here I only sketch how to use some of the models provided in later chapters.
4.1 Matlab
The basic design strategy for Matlab code that implements dynamic models (as opposed to utility programs for
parameter estimation, etc.) is to create two files. The first file is run at the Matlab prompt; this file defines the
parameters and runs the ODE solver (nominally, ode45()). It is the run control file. The second file is called by
ode45() and contains the actual model in the form of computation of the derivatives of the state variables. It is the
model function file.
In MBS-CD, the files have a standard naming convention; the run control files are named M_foobar_Run.m,
where _foobar_ is the name of the model (e.g., MChemostatRun.m). The second files contain a single Matlab
function and are named M_foobar_Func.m, where again _foobar_ is replaced with the model name. Thus,
the pair of files that must be created are: M_foobar_Run.m and M_foobar_Func.m: MChemostatRun. and
MChemostatFunc.m.
After M_foobar_Run.m has been invoked, plots, as defined in the run control file, are produced and control
returns to the Matlabprompt. At this point, the user may change parameters, re-execute and re-plot the results
without leaving Matlab or editing a file. The plotting window, when present after the plot() command, allows the
user to save or export the plot to a variety of graphical formats.
4.2 octave
Although there are many similarities between Matlab and Octave there are enough differences that the files must
be handled differently. There are, again, two files: the control file, and the model function file. The two files are
called: O_foobar_Run.m and O_foobar_Func.m, respectively, with the leading ‘O’ distinquishing octave
code from Matlab code. Both applications use a file extension of .m.
The octave control file, as in Matlab, is run from the octave prompt, sets up a simulation and calls the ODE
solver (lsode(), in octave). When the simulation is complete, control returns to the user at the octave prompt,
and the user may execute a plot of the results. The plotting functions are defined in the control file. The plotting
window is not as sophisticated as in Matlab, so alternatives to plotting to the screen or to a PDF or Windows EMF
files are handled in the plotting function (called DoOnePlot()). See the examples in Chapter 7.13.
14
Chapter 5
How-To Do Computer Simulations the MBS
Way (Linux Version)
Summary: Following a table of contents, we herein describe the basic operations and invocations needed for successful simulation modeling in an academic environment wherein student computer access is distributed across a wide
area with heterogeneous platforms (i.e., a campus). In which, however, the learned instructor requires that all students
use the same computer so as to simplify his life and to accelerate the acquistion by supplicants of the MBS way. In
so doing, we learn how to: boot into Linux from the Knoppix CD, create programming projects using Anjuta, invoke
publication quality plots from simulations, and use the arcane application programming interface of external libraries
such as those various and sundry numerical routines provided by GSL: the GNU Scientific Library.
15
Contents
5.1
5.2
5.3
5.4
Using Linux and Knoppix . . . . . . . . . . . . . . .
5.1.1 Linux . . . . . . . . . . . . . . . . . . . . . .
5.1.2 Booting from Knoppix . . . . . . . . . . . . .
5.1.3 Knoppix . . . . . . . . . . . . . . . . . . . . .
5.1.4 Logon to Another Linux Box . . . . . . . . . .
5.1.5 Print and Emailing Files . . . . . . . . . . . . .
5.1.6 Basic Unix Commands . . . . . . . . . . . . .
5.1.7 Exiting from Knoppix . . . . . . . . . . . . . .
The Anjuta Programming Environment . . . . . .
5.2.1 Integrated Programming Environments . . . . .
5.2.2 The Basic IDEa . . . . . . . . . . . . . . . . .
5.2.3 Starting a New Anjuta IDE Session . . . . . . .
5.2.4 Using External Libraries in anjuta . . . . . .
5.2.5 Setting-Up anjuta to Use Libraries . . . . . .
Plotting . . . . . . . . . . . . . . . . . . . . . . . . .
5.3.1 Background . . . . . . . . . . . . . . . . . . .
5.3.2 How to use SimPlot: All Sim**() Functions
5.3.3 Example . . . . . . . . . . . . . . . . . . . . .
5.3.4 Plotting Many Curves . . . . . . . . . . . . . .
5.3.5 Plotting Grids . . . . . . . . . . . . . . . . . .
5.3.6 Plotting Histograms . . . . . . . . . . . . . . .
5.3.7 How SimPlot Works . . . . . . . . . . . . .
How to Use the SimGSL Templates . . . . . . . . . .
5.4.1 GSL . . . . . . . . . . . . . . . . . . . . . . .
5.4.2 Template Overview . . . . . . . . . . . . . . .
5.4.3 Arrays . . . . . . . . . . . . . . . . . . . . . .
5.4.4 Interfacing with GSL functions . . . . . . . . .
5.4.5 Detailed Explanation of the Template . . . . . .
5.4.6 Complete Code for SimTemplate-Empty.c
16
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
17
17
18
18
19
20
20
21
22
22
22
22
29
29
33
33
33
38
38
39
39
39
43
43
43
43
44
44
50
5.1
Using Linux and Knoppix
This document describes the background and minimal procedures to using Linux to create models in a university
setting. While the WindowsTM operating system certainly allows individuals to do computer modeling, the software
cost can be large as there are few free applications that facilitate programming. One solution is to use the open source
Linux operating system with the extensive set of free software. But this raises the problem of how to provide students
access to Linux in a university dominated by Windows. Universities could establish computer laboratories dedicated
to Linux so that students have ready access to a common platform. Since most of the available computer labs will
already have Windows machines, this is not economical, although dual-booting machines are easy to maintain. A
second solution is for a modeling course to maintain a single Linux server, on which all modeling and computing by
students is performed, and to also provide a mechanism by which students can access the server from any workstation
on campus. This is the approach I have adopted for my classes and it seems to work well.
5.1.1
Linux
5.1.1.1
Multi-User versus Distributed Computing
An operating system, like Windows or Linux, is nothing more than a program that runs on the computer hardware. The
hardware doesn’t care. The Linux operating system is based on a multi-user philosophy that permits many users to
use a single set of hardware (the computer) simultaneously. Windows or Macintosh OSes do not permit this; two users
can not logon to a single computer and share its resources. Windows is designed to implement distributed computing
where individuals use separate workstations each with its own copy of the operating system, but networked to take
advantage of shared hardware resources (printers). This distinction between distributed and multi-user computing
underlies the use of Linux for a modeling class.
5.1.1.2
Why Linux?
Linux (see this Linux webpage for more information), as a variant of the Unix operating system, provides as standard
components, a large number of tools that facilitate programming simulation models. Since Linux is also free, it is
possible to cheaply and legally provide these tools to all users. Being a multi-user OS, we can provide these tools
to users who logon to a central server from many different machines on campus. But this strategy is not without its
problems. A major problem is the fact that users expect (and are more productive when using) graphical user interfaces
(GUIs) to the applications. Making GUIs that run on a remote server function correctly on the local machine from
which a user has logged on, is not a trivial problem. Simply sending every pixel of the GUI that is being displayed on
the remote server to the local workstation would require unacceptable delays in refreshing the local screen.
5.1.1.3
Multi-user GUIs
The solution that Unix uses is to require that the local machine provide most of the resources (software and hardware)
needed to display the GUIs (as opposed to the resources needed to actually execute the application). The remote server
then transmits not the image, but instructions on how to re-create the image at the local site. This means that the local
machine must provide such things as fonts and the software libraries that can interpret and perform the high-level
graphical instructions that are supplied by the server.
5.1.1.4
Xwindows
The standard collection of graphical elements that make up Unix GUIs is known as Xwindows. This is actually a set
of protocols, libraries, and GUIs that have developed over time. Its use means that for a local machine to display the
GUIs of applications running on a remote server, the local workstation must possess Xwindows display capability.
This is a problem, since these facilities are certainly not supplied as part of the standard Microsoft Windows OS.
Several companies provide MSWindows applications that provide minimal Xwindows capabilities. A few are free, but
most cost several hundred dollars. Moreover, they must be installed on the local harddrive, which is often not possible
or convenient on public computers in a university setting.
17
5.1.1.5
Knoppix
A solution using the Knoppix CD is to exploit two important features of CD-ROMS: (1) they are almost universally
available as standard equipment on MSWindows machines, and (2) almost all computers that run MSWindows are
able to boot the OS from sources other than the harddrive, including the CD-ROM drive. The Knoppix CD is a very
nearly complete version of the Linux OS as well as much of the important auxilliary software that supports Linux.
Knoppix does this by using compressed files, and uncompressing them as they are needed by applications. The fact
that Knoppix runs from the CD-ROM is both a blessing and a curse: a blessing because almost any machine that
can boot from a CD can make it work; a curse because we can not easily write new data to the CD; thus, we can
not save any of models or text files that we create. (Linux and MSWindows use very different harddrive formats and
can not safely communicate; also, system adminstrators usually discourage users of foreign OSes being able to write
corrupting data to their harddrives.) We can, however, save data to the harddrive of the remote server.
5.1.1.6
Using Knoppix
Thus, the strategy we use in the MBS way is to use the Knoppix CD as the source of the local Xwindows display
capability and use the remote central server as the engine that performs our simulations. This is a bit of a waste of the
potential of Knoppix, because, as stated, it is a complete Linux, but without the ability to write to a harddrive (easily).
What follows is a guided exercise taking you from the process of booting Linux to creating and displaying the results
of a biological simulation model.
5.1.2
Booting from Knoppix
5.1.2.1
Requirements
Any computer that can boot from either the CD drive (preferred) or from the floppy diskdrive, can make Knoppix run.
If this capability is not presently available on a given machine, it can be acquired by altering the BIOS through the
setup screen during the booting process (often obtained by pressing the F2 key during booting). In the setup screen,
a list is provided of the order of hardware components that are searched for a suitable OS. To successfully use the
Knoppix CD, we simply need to have the CD drive listed before the harddrive.
5.1.2.2
Booting from CD
Normally, you will be faced either with a computer that is off, or already booted in Windows. If the latter, simply open
the CD drive, insert the Knoppix CD and restart the computer. Restarting is usually done by pressing CTRL-ALTDELETE and at the Windows login screen, selecting the “Shutdown. . . ” button, and then selecting “Restart.” When
the computer reboots, it will find Linux in the CD drive and not Windows on the harddrive.
If the computer is off, it is frequently possible to start the Windows boot by turning on the power, open the CD
drive, insert the Knoppix CD, then type CTRL-ALT-DELETE to restart the boot process. If this does not work, simply
let Windows boot, then insert the Knoppix CD and restart as described above.
5.1.2.3
When the BIOS Setup Can Not Be Altered
In some (many) cases, the user is not allowed to alter the BIOS (for many very good reasons!). We work around this
by putting part of the Linux boot process on a floppy disk. For reasons that are not clear, a bootable floppy in the drive
causes the network boot process in the Open Access labs to fail, allowing booting from the floppy and then from the
CDROM.
If you will be using such a machine, obtain the necessary floppy disk as well as the Knoppix CD. Insert both into
their drives and restart the machine. After some attempts to boot, the machine will give the error dialog: “Network
boot failed. Type Return” (or words to that effect). Press the Enter key, and booting will take place from the floppy.
Ultimately, the Knoppix CD will be used in the boot process as well and from there, “Bob’s Your Uncle!”
5.1.3
Knoppix
After Knoppix checks the local hardware available and a bunch of other things, it stops with the terse command:
Boot: Just press the Enter key. (Or, you can just wait for time-out and Knoppix will continue to boot.) Ultimately,
18
Knoppix starts (as default) a popular window manager called KDE. Unlike Windows that has only 1 window manager
(i.e., “Windows” itself), Linux allows the user to choose 1 of many different managers. I prefer one called Gnome, but
Knoppix uses KDE as its default. They all provide similar facilities, but have a different “look-and-feel.”
Notice that the GUI is generally similar to Windows. (And why not? The original windowing GUI was developed
on Unix machines at Xerox, from which everyone subsequently stole ideas, Apple Macintosh included.) There is a
desktop with icons and a panel at the bottom. Left-click on the panel items and they will either run an application or
expand a menu. The left-most panel item is analogous to the Windows Start menu.
There is one significant improvement over Windows that is worth mentioning. In the center of the panel is an icon
of 4 small squares. In KDE, these are numbered, with number 1 being of a light color, while 2, 3, and 4 are black.
Each square represents a “virtual desktop”, and it convenient to use these desktops for different activities. Click on
each and new desktop appears. I use 1 for browsing and email, another for writing text, a third for programming, etc.
This is a very powerful concept that MSWindows should definitely copy.
While KDE on the Knoppix CD is useful and interesting, even it you can not save files permanently, our interest
lies elsewhere. I will leave it to you to explore at your leisure: OpenOffice (a Microsoft Office clone), the Konqueror
web-browser, the many games, etc. As stated, we only want to use Knoppix to get to other Linux machines on which
we have accounts and can save data and models.
Before beginning, however, it is worth changing the desktop background so it is less distracting.
• Right click any where in the open desktop; a menu appears
• Select Configure desktop. . . .
• On the left frame, click on Background.
• In the new right frame, choose the Wallpaper tab, then select the No Wallpaper button
• Choose the Background tab, and choose a Mode and Color in the usual way.
5.1.4
Logon to Another Linux Box
• Close the Knoppix Info screen. (You can follow the links to learn more about Knoppix at your leisure.)
• On the bottom panel, approximately 6 icons from the left, is an icon showing a computer monitor with a seashell superimposed. This starts a terminal shell, which is just a space where we can enter text or command-lines
and have them interpreted by the OS. This is directly analogous to the MSWindows DOS prompt and we can
use these commands to start programs (as opposed to clicking on them in the window manager GUI).
Left-click on this icon; a blank text area appears. You can resize the area by clicking and dragging the corners. The Settings tab at the top allows you to configure colors and fonts. But we will not be using this
very much, once we are logged into the remote machine. Notice that the message to the left of the cursor
says: knoppix@ttyp0 indicating you are talking to the local machine (Knoppix) terminal shell. (The exact
commandline prompt may change with new versions of Knoppix.)
I refer to this shell as the Entry shell.
• Logon to a Linux machine by typing
ssh -l your_remote_username foo.bar.pu.edu
(That is ssh, a space, then a minus sign and an “el”; NOT the number one (“1”) .) Type yes when asked if you
want to add foobar.bnr.usu.edu to the authentification list. Of course, you will have to use the correct
name for the server in place of foo.bar.pu.edu.
Supply your password on the remote machine when prompted. You will be given your username and password
in class.
There is potential for becoming very confused at this point. What you see now is the display of the terminal shell
that is running on the remote machine. Notice the message to the left of the cursor; it shows what machine you are
talking to: username@foo.
We will learn more Unix commands as time goes on, but an important one allows you to change your password:
19
passwd
Then supply the new password when prompted.
5.1.5
Print and Emailing Files
Printing to PDF To print a file of C code to a PDF file and then email the file to yourself, first go to the commandline
cursor and change to the subdirectory that contains the file (use the cd command. Then type:
enscript -q -M Letter -C -Ec -T 4 -z --margins=::40: -L 75
--font=Courier8 -p - helloworld.c | ps2pdf - helloworld.c.pdf
where helloworld.c is the file to print and the above is typed all on one line.
This is an impossible incantation to remember, so it is best to use a text editor such as that contained in the IDE
anjuta (below) to create a file called printc2pdf with these commands:
#!/bin/sh
echo $1 converted to $1.pdf
enscript -q -M Letter -C -Ec -T 4 -z --margins=::40: -L 75
--font=Courier8 -p - $1 | ps2pdf - $1.pdf
and used by typing this:
printc2pdf helloworld.c
(Your instructor may provide printc2pdf to you so that you can execute from any directory on the Linux server.)
The above command will work for *.h files as well. It will also work on text files, but C reserved words [e..g, for]
will be in bold font.)
To view a PDF file in Unix: acroread your_file_name.pdf where acroread is the Linux equivalent
of the Windows Acrobat Reader.
Email a File to Someone To mail a file, you could use Netscape or mozilla which are supplied with most
distributions of Linux, but a simpler approach is to use a Linux mail application call mutt (it retrieves mail!). The
complete command is:
mutt -s "MY FILE" -a helloworld.c.pdf myemailname@foo.bar.edu </dev/null
but this is another handful, so your instructor may make available another shell script called mailfile that
contains the above Unix commands.
5.1.6
Basic Unix Commands
Here are a few useful Unix commands.
• acroread filename.pdf: the Acrobat PDF reader for Unix (.pdf extension required).
• alias: create a shorthand command; (e.g., printc is an alias. alias lsl="ls -ltraL" creates a
short-hand for a long directory listing that shows all files and follows links).
• bg: reactivate a sleeping program to run in the background (see, fg, CTRL-Z)
• cd: change directory; used to change your current working directory to another
• cp: copy the contents of a file from 1 place to another (e.g., cp main.c main-00.c) To copy a file from
another directory to your current directory:
cp /path/filename .
Yes, that period is important; it means “my current working directory”. Example to copy a supplied C program
from the directory for this course to your current directory:
cp /usr/local/share/MBS/sum.c .
• CTRL-Z: puts the job you are running in the foreground to sleep; usually followed by bg. (E.g., at the commandline, typing (for example) acroread runs the PDF reader in the foreground, and you cannot type in new
commands on the commandline. To fix this, type CTRL-Z (send acroread to sleep); then type bg (reactivate
the sleeping program in the background, while you type new commands).
• find: find a file, e.g., find . -name ".c", (see man find)
• fg: run a background program in the foreground
20
• grep: search for a phrase in a set of files; often combined with other programs (see locate, below) E.g.,
grep -i printf *.c
• info subject: display information on a subject; similar to man, but with more sophisticated indexing
and searching capabilities. To see documentation of all C functions (not the language syntax, but the built-in
functions), type info libc. Once you are viewing the info for libc, there are nodes for indices to functions
arranged by concepts and by name. To see a particular function (e.g., floor()) do: i floor. Type ‘q’ to
quit. You can get information on info by typing info info.
• less filename: display a file on the screen with page and arrow control; pagedown/up moves by page;
up/down arrow moves by line. Type q to quit.
• locate: find a file using partial name fast; combine with grep to limit output (e.g., locate disln |
grep -i lib (The vertical bar “|” is the “pipe” command; it sends output from locate not to the normal
screen, but to the program that follows (grep in this case.)
• ls: list directory; prints short form of files
• ls -a: list directory including the “hidden” files that begin with a period
• ls -ltr: list directory long and sorted by modification time (most recent last)
• man: manual (help) pages (e.g., man ls). Not all programs are documented in this system
• mv: move a file from 1 filename to another, commonly used to rename a file (e.g., mv main.c main-00.c)
• nedit: a nice, menu-driven text editor suitable for programming in C.
• printc2pdf: a shell script (macro) for a long command to format a file using C syntax and converting and
saving the result as a PDF file. You will need this for your homework.
• ps -u yourusername: show process status; used to find the PID of programs you are running (see below
on runaway programs).
• pwd: print working directory; used in determine where you are in your directory
• nautilus: a graphical file manager, like you’re used to in M$Windows
• mozilla: popular mail manager (netscape) and WWW browser. You can use it to check your email on
cc.usu.edu
• rm: remove (delete) a file (NB: not recoverable)
5.1.7
Exiting from Knoppix
1. If you have been programming using the anjuta IDE, exit from anjuta using the File tab at the top menu.
2. Kill the terminal that ran anjuta, by typing exit at the cursor.
3. Logoff from the Linux running on the remote computer. In the original Entry shell (the one that says: username@foo),
type exit and press Enter. This puts you back talking to your local Knoppix shell (prompt: knoppix@ttyp0)
4. To stop Knoppix, go to the KDE left-most icon at the bottom panel (i.e., in same position as the MSWindows
Start icon), select the Logoff menu item, and click the Logoff button on the subsequent dialog box.
Eventually, Knoppix will halt and prompt you to remove the CD. Do so, and press the Enter key. The computer
may shutdown, or reboot Windows.
21
5.2
5.2.1
The Anjuta Programming Environment
Integrated Programming Environments
There are many repetitive steps in programming:
idea → edit source code → compile → build (link libraries) → run → debug → edit →
compile . . . → debug . . . etc, etc, etc
These can all be done by typing commands on a command line, but with the advent of GUIs, these steps have been
integrated into GUI-based IDEs. Anjuta is one of many IDEs available. While not a clone of the Microsoft VisualC
(Basic, etc) IDE, it has all the same features based on the Linux operating system. This short tutorial gets you started
in using Anjuta. (“Anjuta” is the name of the primary programmer’s girl-friend.)
5.2.2
The Basic IDEa
The basic unit of an IDE is the Project. A project is simply a subdirectory (folder) that contains all the files you
need to edit, compile, and run a program (application) you are creating. The name of a project is the name of the
subdirectory that contains your files.
The IDE creates these subdirectories and files, compiles them when you command it, and displays results.
Do not confuse opening a file with opening a project. Usually, you will want to open a project, and once opened,
manipulate the constituent files.
5.2.3
Starting a New Anjuta IDE Session
The anjuta IDE is an open source Linux application. If you are running Linux as a dedicated workstation, then you
can start anjuta by locating and clicking on its icon. If you access a Linux server remotely through a remote secure
shell, then you will use the Linux command line in a “terminal” emulation to start anjuta.
On the command line for the terminal running on the Linux server, type anjuta &. (The & means: “run the
program in the background and return to the command line.”)
The following window will appear:
Give the project a name, and author; you can leave the Project Target field blank. The programming language is always C with an Executable target.
Next, provide a short description, when confronted with This is self-explanatory. If you want to create a new
project, click on Application Wizard, and the following window appears.
22
Click on Forward and see:
The code in MBS will use a Generic/Terminal project. Unspeakable horrors will befall you, should you
try anything else.
Clicking Forward asks for some information about the project:
23
Give the project a name, and author; you can change the Project Target field if you want the executeable
program (application) to have a different name than your project name. Usually, it’s simpler if they have the same
name. The programming language is always C with an Executable target.
Next, provide a short description, when confronted with this window:
Finally, a few more project options are possible in this window:
24
CLEAR or UNclick or UNset the first two options.
Inside a Project
Let’s suppose you have created a project called hello and have started Anjuta so you have this view:
On the top menu bar, click on File, then Open Project.... NOT Open ... (that is for opening plain old
files, not cool, super-advanced projects).
When the list of your available projects appears,
25
choose hello. Notice the path shown near the top of the window.
You will next see this window:
This is IMPORTANT: double left-click on the project file shown in the right window: hello.prj. This is the
actual project, and you will not have opened a project until you click here.
When you do click there, you will see:
26
These three panes are called “windows” by Anjuta. The left window is the Project Window, the right window
is the Source Window and the bottom window is the Message Window. You can close these windows in the
View drop-down menu, or by clicking on the red X associated with the window.
With Left-click and drag, you can resize the windows.
Compile and Link
To compile your source code into intermediate (non-human readable) code, use the drop-down Build menu and
choose Compile. Messages will appear in the message window, hopefully like this:
27
To create a working program, you also need to link (or “load”) in compiler-supplied code for C commands you use
(e.g., printf()). To do this, use the drop-down choice Build → Build.
More messages:
28
Running the Program
Build → Execute (near the bottom of the drop-down menu; also, F3). Anjuta executes terminal-based projects
in terminals, so it will start a terminal, run the program, and then wait for you to stop the program and return to Anjuta
and your project.
5.2.4
Using External Libraries in anjuta
A library is a collection of binary, compiled files that provide some function. For example, you have already used a
library of files when you use the input/output functions of standard C. Being compiled, you can not edit or alter the
code. This is often a bad thing; but, on the other hand, you don’t have to compile or debug the code. Hopefully, the
author has found all the bugs before creating the library. Generally, the files included in the libraries are not complete,
stand-alone programs. Rather they are simply code that has been compiled but not linked with other code to create an
executeable file. Usually, they have no main() function. (You supply that when you create a program that uses the
libraries.)
There are two problems associated with using these libraries. First, you have to know where they are so that you
can instruct the compiler how to include them in your project or program. Second, the libraries are composed of
functions that take arguments and return or change values. To properly invoke (use) these functions, you have to know
how to use the so-called API (Application Programmer’s Interface). This knowledge is imparted to you by means of
something called “documentation.”
5.2.5
Setting-Up anjuta to Use Libraries
After you create a project, tell the compiler and linker where you want them to look for files.
1. On top menu bar, select Settings. A drop-down menu appears with items including:
• Compiler and Linker Settings . . .
• Source Paths . . .
• Commands . . .
• ...
Select Compiler and Linker Settings . . . , and the following window appears.
29
2. Click on the tab labeled Include Paths
In the editing field, type in the following and after each, click on Add:
• ../include
• /usr/local/dislin
• /usr/local/share/MBS
The order does not matter. When all have been entered, click Update
The window should look like this:
3. Select Library Paths. In the editing field type and after each add the following paths:
• /usr/local/share/MBS
• /usr/local/dislin
Then click Update.
The window should look like this:
30
4. Select Libraries. In the editing field, type in the following and after each, click on Add:
• xyplot
• dislnc
• gsl
• gslcblas
The first 2 are libraries needed for plotting. The last 2 are GSL numerical methods libraries. We’ll use these
again in the near future.
Click Update,
The window should look like this:
Then Click Close. You may then see the following window:
31
Click No.
If you mess this up by clicking Yes, go to the top-level drop-down menu bar and do:
Build → Auto Generate ...
Anjuta will then reconstruct your project files; it will take a few seconds.
Save your project: these settings will be stored. Compile and build as in the past. If you create a new project using
these libraries, you will have to repeat the process.
32
5.3
5.3.1
Plotting
Background
SimPlot is a small library of plotting and model analysis functions that can be used in computer simulation programs
written in C. It is based on another library called DISLIN, written by Helmüt Michels. Versions of the DISLIN library
are available for many compilers on several platforms including Linux, Solaris, and Windows. Many versions
of DISLIN are free, but those for the Microsoft compilers are not.
Each call to one of several plotting functions produces one plot in a separate window or pdf page. Multiple
(sequential) calls to SimPlot (or the other functions) during a single simulation run produce separate windows
(pages), each with the plot requested. SimPlot can plot multiple variables on the y-axis versus a single x-axis. In
this way, the dynamics of up to 10 state variables can be shown simultaneously. The x-axis can be any variable; it need
not represent time. Thus, phase-space plots are possible.
There are four main plotting functions available to the user. They differ by the number of different arrays containing
data for the abscissa (x-axis) and the type of plot (x-y vs grid). (The former functions allow multiple ordinate (y-axis)
data arrays to be plotted on a single graph.) If there is a single array of abscissa data (e.g., time values from a
simulation), then the function to use is SimPlot(). If you wish to plot curves which have different abscissa data
arrays (e.g., model predictions over simulation time and observed data values at times specified by a dataset), then
you should use SimPlotManyX(). The syntax of the use of the two functions is similar, and the differences are
described below.
5.3.2
How to use SimPlot: All Sim**() Functions
In terms of the user, there are three main graphing functions that will be used frequently. (The library contains
additional, supporting and utility functions. There are also a few other specialized plotting functions described below.)
SimPlot() and SimPlotManyX() calls the scaling and plotting functions. SimPlotFinish() cleans up some
details and allows the user to use the mouse button to terminate the plots. Below, is the minimum needed to use the
functions. The documents describing various simulation model code elsewhere on this CDROM provide examples.
5.3.2.1
Include Header Files
All of the MBS simulation code uses this C header file:
#include <SimMBS.h>
The angle-brackets presume that the header file is installed in a directory searched by the compiler. See a separate
document describing how to do this.
This header file contains all the declarations and global variables needed by the MBS simulation code (i.e., for
plotting, ODE solutions, random number generation, validation, etc.).
5.3.2.2
Global Arrays
To simplify the user interface, the data to be plotted is saved in global arrays of float. These arrays can have any
data in them that the user chooses to save and plot, but in simulation models, the usual arrangement is to save time
values and the values of state variables over time. So a typical declaration in a user model is
1
2
3
4
5
6
/* Globals */
#define MAXPLOTSAVE
20000
float Time[MAXPLOTSAVE],
/*
SV1[MAXPLOTSAVE],
/*
SV2[MAXPLOTSAVE];
/*
int numpt;
/*
time values, paired with ... */
state variable 1 */
state variable 2 */
number of points saved in the arrays */
To minimize coding changes from one simulation program to the next, we employ the inelegant approach of declaring
the global arrays to be much larger than we will normally need. Line 2 creates a defined variable, MAXPLOTSAVE,
that is the size of the plotting arrays. Lines 3–5 declare the arrays. Note that all these plotting arrays must be declared
float. Since the array size does not correspond to the actual number of data points saved, numpt must be declared
33
and set in the function Simulate() to the number of points actually saved during the simulation. Note also, that not
every solution must be saved for plotting. Long runs with small time steps will generate more points than needed for
clear plots. Below, we show how save a subset of the solutions.
5.3.2.3
Saving Data for Plotting
The simulation solutions to be plotted must be saved in these global arrays during the simulation loop. Here is a
fragment from a larger program:
100
for(t=start_t;t<stop_t;t+=dt) {
101
/* integrate */
Euler(p,y,dydt,t,dt);
102
103
104
/* save results every "save_delt" solution steps */
if(t>=save_t) {
/* time to save results? */
/* debug for our sanity: comment out later */
/*printf("simulate(): t=%f n=%d\n",t,*numpt);*/
Time[i]=t;
Prey[i]=y[PREY];
Predator[i]=y[PREDATOR];
numpt++;
/* increment number points saved */
i++;
/* increment output array indices */
save_t += save_delt;
/* increment next time to save */
}
105
106
107
108
109
110
111
112
113
114
115
116
}
(For a more complete description see the document Simulation Program.) Line 100 is the top of the simulation loop:
t and dt are the current time and time-step (delta-t), respectively. The system of differential equations is integrated
to the next solution point in line 103; the array y[] has updated values after this line. Line 106 begins saving the
solutions to the global plotting arrays. Usually, we do not want to save and plot every solution; every n-th solution
suffices. save_t is a local variable that contains the next time to save. The saving to the global arrays begins on
line 109. Notice that we save both the current time value as well as the current state variable solution. The variables
controlling saving are incremented in lines 112–114
5.3.2.4
Invoking the Package
In main(), include a call to SimPlotMenu().
int main(void)
{
InitSystem(...);
Simulate(...);
SimPlotMenu();
}
This function writes a simple menu on the screen, allowing the user to choose were to display the graphs. The
SimPlot plotting package allows plotting to several different devices. Implemented so far are output to: (1) screen,
(2) Adobe pdf format files, (3) Microsoft wmf (windows meta-file) format, and (4) a text file of numerical values.
User input is obtained from a menu as shown in Figure 5.1. The user is prompted for file names if options 2, 3, or 4 is
chosen. The menu is recycled until the user chooses “q”.
If option “1” is chosen, the plots appear in color on the monitor. Each call to SimPlot() produces a separate
window with one graph. At the top of each window is a title bar and instructions to close the screen display and return
to the menu. To close all the graphs, locate and focus the window whose title says:
SimPlot 1: TO CLEAR: Click Middle|Right Button INSIDE "SimPlot 1". Do as the title bar instructs:
click with either the middle or right button inside the “SimPlot 1” window to close all the SimPlot windows. Clicking
inside any one of the other SimPlot windows will do nothing. At the bottom of the monitor screen, you should see a
list of the open windows as rectangular tabs; clicking on one of these will focus that window.
The file formats offered are PDF and WMF. If you just want a hardcopy, or you use Unix and LATEX, PDF is best. If
want to include the graphs in a Microsoft Word document, WMF is your only reasonable option. All of the Microsoft
34
Figure 5.1: The text menu produced by the SimPlot package displayed in the Anjuta execution window.
Figure 5.2: The title bar for the first SimPlot window. Clicking inside this window (in the black region) will terminate
the plotting on the screen.
Office applications can insert this file format and, unlike bitmapped formats like JPEG, WMF files can be resized
without losing resolution. Unfortunately, for WMF files produced by Linux, the Microsoft fonts are not available, but
the built-in SimPlot fonts don’t look too bad. If you use the package on a Windows platform, the typical Windows
fonts can be used.
A fourth option permits the user to produce a text file of the numerical values saved. This option prompts the user
for a file name and then writes the results to the file in columns in the order specified by the user. (see Figure 5.2).
5.3.2.5
Programming the Graphs
To actually make the plots, the user must declare and define two functions: void DoPlots(void) and void
DoPrints(). Both of the these are called by SimPlotMenu().
SimPlot() and SimPlotManyX() To plot an array S containing n values of a state variable S against an
array Time containing n values of simulation time:
SimPlot("PlotTitle",n,Time,"time",S,"State Variable",END_PLOT_LIST);
35
The respective arguments are as follows:
1. The title of the plot at the top of the graph.
2. The number of points to plot.
3. The array of abscissa values.
4. The label (title) of the abscissa.
5. The array of values of the first ordinate variable.
6. The label (title) of the first ordinate variable.
7. A token to end the list.
Notice that the last argument looks strange: END_PLOT_LIST. This is explained below, but note for now that this
must always be the last argument passed to SimPlot.
If you wish to plot a second array V against time just add it and its legend label to the list of arguments:
SimPlot("PlotTitle",n,Time,"time",S,"State Variable",
V,"Second Variable",END_PLOT_LIST);
For each array to be plotted, you must include the name of the array and a title either enclosed in double quotes (as
above) or stored in a C string. You can plot up to 6 arrays (lines) on a single graph. If you really have to do more than
6, re-compile the source code changing the value of NUMCURVES_PLOT in xySimPlot.h.
Simple text formatting of the axes titles is possible; subscripts are created by inserting “]” before the text to be
subscripted and “$” at the end. Superscripts are created prefixing the text with “[”.
After all graphs have been displayed in separate windows, in order to exit gracefully, the user must do:
SimPlotFinish();
5.3.2.6
DoPlot() Control Variables
The behavior of the plots can be controlled by setting certain global variables shown in Tables 5.1 and 5.2.
These variables are used immediately before a call to SimPlot(). Once set, they remain in effect for all subsequent calls to SimPlot(), until reset. For example:
void DoPlots(void)
{
ABSPADDED=0.0;
DOTITLE=1;
GRFS_PER_FILE=3;
SimPlot(...);
SimPlot(...);
DOTITLE=0;
SimPlot(...);
LINETYPES("PPP");
SimPlot(...);
SimPlotFinish();
}
produces 4 plots. The first 3 are on a single page (in a single file). The fourth is on a second file (page), but scaled
similarly as the first three. If you want the last one to be scaled to a smaller page size, insert GRFS_PER_FILE=1
before the fourth call to SimPlot();. The fourth plot has three curves each of which is plotted using points (markers)
only, no connecting lines. The first three plots have curves that use the default: points and lines.
Between calls to SimPlot(), the default values can be reset for all user variables by invoking:
SimPlotDefaults(); For example,
36
void DoPlots(void)
{
ABSPADDED=0.05;
DOTITLE=0;
GRFS_PER_FILE=3;
SimPlot(...);
SimPlotDefaults();
SimPlot(...);
SimPlotFinish();
}
The second plot, will use the default values: no padding, print a title, and one graph per file.
Most of the variables in the Table of Globals are self-explanatory. The default mode has SimPlot perform
autoscaling in which the maximum and minimum of both axes and the intervals between tick marks are chosen based
on data ranges and on esthetic considerations. This may be changed by specifying X_MAX to be larger than X_MIN,
or Y_MAX > Y_MIN. In either of these cases, the values specified are the bounds for the plots, regardless of the actual
bounds in the data. This allows the user to create a set of plots with the same axis scales. It also allows the user to
“zoom” in on segments of the plot, but choosing plotting bounds smaller than the data bounds. Choosing arbitrary
axis bounds may result in squirrely tick mark intervals.
The following variables are only effective when output is re-directed to a file: PAGE_X(Y)_INCH, GRFS_PER_FILE,
PDF_ON_PAGES, OUTFILE_COLOR, and DARKER.
5.3.2.7
SimPlot() Arguments
The first of two plotting functions, SimPlot(), assumes you wish to plot one or more ordinate arrays against a single
abscissa array. SimPlot() requires a minimum of 3 arguments: (1) the title for the plot, (2) the number of points to
plot, and (3) END_PLOT_LIST.
SimPlot("PlotTitle",int n,END_PLOT_LIST);
This is the minimum to satisfy the syntax, but if that is all that is passed, then nothing will be plotted, because
the invocation does not pass in either the values for abscissa (x-axis) or ordinate (y-axis). So, the minimally useful
invocation is:
SimPlot("PlotTitle",n,Time,"time",S,"State Variable",END_PLOT_LIST);
which includes an abscissa array and its legend label followed by an ordinate array and its legend label. SimPlot
assumes that the first data or simulation model array passed in will be used as the abscissa; any others will be used as
the ordinate arrays.
5.3.2.8
SimPlotManyX() Arguments
The second of two plotting functions, SimPlotManyX(), assumes you wish to plot together two or more ordinate
data arrays each with their own abscissa arrays.
SimPlotManyX() requires a minimum of 3 arguments: (1) the title for the plot, (2) the title for the x-axis, and
(3) END_MANYX_LIST.
SimPlotManyX("PlotTitle","Time (Days)",END_MANYX_LIST);
END MANYX LIST is required to terminate the variable argument list (see Section 5.3.7, How SimPlot Works,
below). This is the minimum to satisfy the syntax, but if that is all that is passed, then nothing will be plotted, because
the invocation does not pass in either the values for abscissa (x-axis) or ordinate (y-axis). So, the minimally useful
invocation is:
SimPlotManyX("PlotTitle","X-Title",nX1,Xaxis,S,"State Variable",END_MANYX_LIST);
37
Where nX1 is the number of data points to plot, Xaxis is the array of abscissa values, S is the array of ordinate
values, and END MANYX LIST terminates the list. But even this working example does not illustrate the purpose of
the function: multiple x-axes on the same graph. Here is an example that creates a graph of two curves, each with a
different number of data points and abscissa values.
SimPlotManyX("PlotTitle","X-Title",nX1,Xaxis1,S,"State Variable",
nX2,Xaxis2,S2,"Variable 2",END_MANYX_LIST);
5.3.2.9
Special Ordinate Title
The strings associated with specific plotting arrays (e.g., in the above fragment: "State Variable" and S) are
used for the legend that identifies and distinquishes multiple curves on a single graph. Often (usually), we want the
ordinate title (i.e., y-axis label) to be different from the legend of the first ordinate array. The following special notation
is used to separate the first ordinate legend from the ordinate title.
The string format is "Legend_string | Ordinate_Title". The string is composed of either legend
string only or the legend string followed by the vertical bar symbol (|) and the ordinate title. If no vertical bar is
present, the title and the legend are the same. If you wish to use the vertical bar symbol in the legend, then preceed
it with 2 backslashes: "\\|". For example, "Legend1\\|Legend2|Ordinate Title". Other placements of
the vertical bar are handled appropriately. Here is a table of the possibilities; Program String signifies the string used
in the call to SimPlot(...) included in the user’s program.
Program String
Legend
Y-axis Title
"Variable 1"
"Variable 1|Amount of Variable 1 (gm)"
"Variable 1\\|2|Either 1 or 2 (gm/mˆ2)
"|Variable 1"
"Variable 1|"
Variable 1
Variable 1
Variable 1 |2a
Variable 1
Variable 1
Variable 1
Amount of Variable 1 (gm)
Either 1 or 2 (gm/mˆ2)
Variable 1
(empty title)b
a Unfortunately,
b Not
the space before | is not easily eliminated.
something you probably want.
Here is an example that illustrates some of these options. It plots three curves against time.
SimPlot("Lotka-Volterra: Predator and Prey",numpt,Time,"Time",
Prey1,"Victims1\\| Prey|Victims or Predators (Numbers)",
Prey2,"|Victims 2",Predator,"Predators",END_PLOT_LIST);
This will produce a y-axis label that reads: Victims or Predators (Numbers)
The legend for the first variable (Prey1) will read:
Victims | Prey
The legend for the second variable (note the leading ‘|’):
Victims 2
And the third variable legend is:
Predators
5.3.3
Example
Most of the source code in the Code section of the MBS-CD contains examples, but here is one simple example:
PLOT Example Code.
5.3.4
Plotting Many Curves
Although there is a limit of the number of curves (data sets) that one can plot on a single graph, this can be changed by
altering the variable NUMCURVES_PLOT in the SimPlot library source code file xySimPlot.h. In some cases we
wish to plot a family of curves, for example, when we wish to see the effect of a parameter on the shape of a function,
or when we do a sensitivity analysis by systematically varying a model parameter. In these cases, it is tedious to create
many one-dimensional plotting arrays and then listing each one in a call to SimPlot(). It is easier and more efficient
to put all the results in a two-dimensional array, with the columns being different datasets to plot. A special version of
SimPlot() that graphs a matrix of values is SimPlotFunction(). Its use is described in Section 7.5.
38
5.3.5
Plotting Grids
Another special plotting function is GridPlot(). Its use is featured in Section 7.19, but it plots a rectangular grid
of cells each of which has a color associated with the state of the cell. It is useful for plotting cellular automata or the
movement of objects in a discrete space.
5.3.6
Plotting Histograms
Another special plotting function is SimPlotBars(). Its use is featured in section 7.10, but, briefly, it plots histograms (surprise!). It can do grouped histograms (bars of different variables side-by-side in the same bins).
Create the histograms using
void SimHistogram(int nbins,double ranges[],double minbin,double maxbin,int nvals,
double binify[],float vals[],float lb[], float mb[],float ub[]);
storing the output in global plotting arrays, as usual. The arguments of this function are:
nbins
Number of bins for histogram
ranges
An array of the bin boundaries to use. The first element is the
smallest lower boundary and the last element is the largest upper
boundary. If the array address passed is NULL, then GSL makes
equally spaced bins between minbin and maxbin.
minbin
The smallest bin boundary to use, if ranges[] is NULL.
maxbin
The largest bin boundary to use, if ranges[] is NULL.
nvals
The number of data points in the distribution.
binify[] The array of data points from which to create a histogram.
vals
Returned from the function. The height of the bars in each bin,
usually the number of occurrences of the distribution in the bins.
These are the values to plot on the y-axis against the bin boundaries (either ul[], mb, or ub on the x-axis.
lb
Returned from the function. The array of lower boundaries for
all the bins.
mb
Returned from the function. The array of mid-points of the bins.
ub
Returned from the function. The array of upper boundaries for
all the bins.
Then plot the histogram in DoPlots() with something like:
SimPlotBars("Random Logistic Population", nbins, UBnd, "Population Sizes", BinVals,
"StDev=0.44|Number of Points", BinVals2,"StDev=0.1",END_PLOT_LIST);
where
• nbins = number of bins
• UBnd = array of bin boundaries (upper-bounds of bins in this case, but could be lower or middle values)
• BinVals = array of values in first histogram
• BinVals2 = array of values in second histogram
This code produces a grouped histogram.
5.3.7
How SimPlot Works
SimPlot is an example of a variable argument list function. Another familiar example is printf(). A variable
argument function is one in which the user can provide any number of arguments when invoking the function. The
details are not necessary to use SimPlot, but a few comments may clarify the declarations and invocations.
39
5.3.7.1
The Stack
When a function is invoked, temporary variables are created. When the function is complete, these temporary variables
are destroyed. Among these temporary variables are the arguments passed to the function and any local variables that
are declared within the function. Because they are temporary and are only created when they are needed (i.e., when
the function is invoked), there is no way for the compiler to know if the memory will actually be needed. So, when the
function is invoked, a special area of memory called the stack is reserved for these temporary variables. The memory
is reclaimed by the “system” when the function is done. Another function, when invoked subsequently, can re-use this
stack memory for its temporary variables.
5.3.7.2
The Stack in Variable Argument Functions
This is a nice system for variable argument functions because a fixed amount of stack memory is not required for each
invocation of the function. If there are many arguments, the stack only has to get a little bigger. However, there are
some constraints put upon the programmer. To gain access to the arguments, the programmer must add code to pick
off the arguments one-by-one from the stack. This is not hard and there are special built-in C library functions to do
it. But because the programmer does not know how many arguments will be present in any given invocation, he or she
needs to know when the list of arguments is complete. This is signalled by our dangerous friend, the NULL pointer.
As the arguments are being picked off the stack in a while() loop in the programmer’s code, when the null pointer
is encountered, the loop terminates. Therefore, every list of arguments in a variable argument function must have as
its last argument (void *)NULL. Without this, we will have an infinite loop, and that is not a good thing.
As a means of improving the code appearance, the header file for the SimPlot package (xySimPlot.h) contains these lines:
#define END_PLOT_LIST (void *)NULL
#define END_PRINT_LIST (void *)NULL
40
Table 5.1: DoPlot Control Variables
PAGE_X_INCH = n
PAGE_Y_INCH = m
Sets the page dimensions in inches.
Default:
PAGE_X_INCH=6
PAGE_Y_INCH=4 (page length along x-axis is 6 inches; page length along yaxis is 4 inches). Affects only output directed to a file (pdf or wmf).
NUM_X_DIGITS = n
NUM_Y_DIGITS = m
n and m integers for the number of digits to the right of the decimal point on the
x and y axes, respecitively. Overrides automatic generation of nice digits based
on the size of the numbers plotted. Default: Automatic digits.
X_MIN=n
Y_MIN=n
Set min and max of axes. X(Y)_MAX <= X(Y)_MIN means let auto-scaling
set bounds (Default).
X_MAX=n
Y_MAX=n
ABSPADDED = n
Pads the x-axis by an amount proportional to the maximum of the abscissa. n =
0.0 produces no padding (curve is tight against the y-axis). n = x.y produces
padding, where large x.y (float) means more padding. Default=0.0.
LEGEND_POS = n
Positions the legend in 1 of 8 positions. To put the legend in one of the corners,
use n as: 5 (lower-left), 6 (lower-right), 7 (upper-right), 8 (upper-left). To put the
legend centered along one of the 4 axes, use n as: -5 (left-center), -6 (bottomcenter), -7 (right-center), or -8 (top-center). NO Legend=0. Default=7.
DOTITLE = [0|1]
([0|1] means “0” or “1”). Controls printing of a graph title. If 0, no title is
printed; if 1, the supplied title is printed. Default=1
GRFS_PER_FILE = n
Number (n > 1) of graphs to put in a single file when output is directed to a
file. Graphs are stacked vertically on a single page. n > 4 creates very vertically
short graphs. Default=1. (Only operational if output is directed to a graphics file
(pdf or wmf).)
PDF_ON_PAGES = [0|1]
Controls placement of PDF graphs in files. When producing PDF files,
PDF_ON_PAGES=0, means put each graph in a separate file (may be several).
If 1, a single file is produced and the pdf graphs are on separate pages in that file.
(Only operational if output is directed to a graphics file (pdf or wmf).) Default=0.
YLABEL_PAD = n
A number (n) of pixels separating the ordinate title from the numbers on the
y-axis. Prevents large numbers from overlapping the title. Default=60.
LINETYPES("s")
String “s” defines the curves as points only (markers and no connecting lines),
line only (no markers), or both (lines and markers). “s” may have up to 6 characters (one for each curve) from the case-insensitive set (‘P’,‘L’,‘B’), where ‘P’
= points only, ‘L’ = lines only, ‘B’ = both. E.g., LINETYPES("PLBL") means
curve 1 is points only, curve 2 is lines (no markers), curve 3 has both markers
and lines, and curve 4 has lines only. Default=“BBBBBB”
LINECYCLES("s")
Determines the color and nature of lines for multiple curves per graph. “s”
may be one of the following (upper or lower case). COLORS: lines are colored, solid (not dashed), and each curve has a different color. DASHES: lines
are black, with different dot-dash sequences. COLORDASHES: lines are colored and dashed. SOLID: all lines are B/W and solid. In the last case, unless LINETYPES is ’P’ or ’B’, the curves will be hard to distinquish. Example: LINECYCLES("colordashes") sets curves to have different colors
and dot-dashes. But see OUTFILE_COLOR, below. Default= “colors”.
OUTFILE_COLOR = [0|1] Force the output files (pdf or wmf) to have colors (1), or (0) to be black/white
and have varying dot-dashes. This option allows the user to see colors
on the computer monitor, but have B/W output for printing. Example:
OUTFILE_COLOR=0 makes pdf and wmf files have black and white dashes.
Default=1.
DARKER = [0|1]
Controls thickness of lines and fonts in WMF files. ‘0’: single lines, ‘1’: double
lines. Improves printed appearance. Default=0.
41
Table 5.2: DoPlot Control Variables. Continued.
MAKE_SQUARE = [0|1]
Forces grid plots (GridPLots()) to be square. Default=0.
GRID_LINES = [-1|0|1] -1 ⇒ put grid lines unless grid squares too small. 0 ⇒ force no lines, 1 ⇒ force
lines. Default = -1.
GRID_VISUALIZE = [0|1|2]
How to display grids. 0 ⇒ no display, 1 ⇒ static display, 2 ⇒ animate display
within a simulation loop. Default = 0.
PDFFONT_BOLD = [0|1]
Controls fonts in PDF file output. 0 ⇒ use non-bold Helvetica, 1 ⇒ Bold
Helvetica. Default = 0.
BAR_STYLE("s")
String ”s” controls bar patterns in histograms. ”s” may be one of the following
(upper or lower case). OPEN: open bars, PATTERNS: fill (hatching) according
to a cycle for grouped histograms. Default = PATTERNS.
VALS_ON_BARS = [0|1]
Controls if histogram y-axis values are placed on top of bars. 0 ⇒ no values,
1 ⇒ put numbers. Default = 0.
BAR_WIDTH = n
The fraction of x-axis bin width occupied by histogram bars. n = 1.0 fills the bin
so that adjacent bars are contiguous. n < 1.0 puts gaps between bars. Default =
0.75.
X_TICK_INTERVAL = n
X_TICK_INTERVAL = n
Interval in axis units between major tick marks. n = −1 means autoscale. Default = -1.
MARKER_SIZE = n
Line marker size as a fraction of the fontsize of the axis label (numbers). Default
= 0.667.
42
5.4
How to Use the SimGSL Templates
This document describes how to use a template for C code that solves ordinary differential equations using the GNU
Scientific Library (GSL) and the SimPlot plotting package based on the DISLIN graphing library.
Code for simulation models is very similar from one model to the next. In designing a template, we would like
to exploit this common core of similar code as much as possible. This template introduces some new programming
concepts that are necessary to achieve a general approach to simulation modeling.
5.4.1
GSL
The GNU Scientific Library is a large collection of numerical routines implemented in C and compiled into a single
library. The code is free and released under the GNU General Public License. Basically, this means that you can
use and adapt the code for any purpose as long as you continue to make the source code freely available by releasing
your adaptations under the GNU GPL. The GNU copyright agreement is the heart of the “open-source” movement in
application programming.
The GSL provides a huge set of functions in many different areas of computational mathematics including: ordinary differential equations, parameter estimation, basic statistics, probability distributions, random number generation,
linear algebra (especially matrix manipulations), etc. In this class, we will use only a small fraction of these routines:
ordinary differential equations, random number generation, and parameter estimation.
The complete GSL documentation is available online at:
http://sources.redhat.com/gsl/ref/gsl-ref_toc.html
5.4.2
Template Overview
The name of the basic template code is:
SimTemplate-Empty.c
The basic steps in using this simulation template are:
1. Include SimMBS.h header file that declares ODE solver and plotting functions.
2. Define model-specific constants for model size and variable names.
3. Declare plotting arrays
4. Define the main() function
(a)
(b)
(c)
(d)
(e)
(f)
(g)
Declare local variables
Read parameters, etc. in InitSystem()
Setup ODE solver and plotting arrays
Simulate the system by invoking Simulate()
Do a new simulation with different values, if desired
Terminate the ODE solver
Do plots with SimPLotMenu()
5. Define the model in deriv()
6. Define the plots in DoPlots()
5.4.3
Arrays
One important method of making the code general is to put variables that need to be passed to functions into arrays.
When that is done, one does not pass long lists of state variables and model parameters that change with each model.
Instead, one simply passes a single array of parameters (the name of which is the same for all models) and a single array
of state variables (again, the name of which does not change). A downside of this method is that the names of the parameters and state variables are not like “Species” or “Volume”, but obscure references such as: Parameters[13].
It is very hard to debug code that uses numbers and not mnemonic names. So, we will introduce a simple scheme that
allows us to use names for array indices.
43
5.4.4
Interfacing with GSL functions
When we use any third-party library of functions, we must know how to invoke those functions, just as if we wrote
them ourselves. The only way we can know this information is to read and understand the documentation for the
library. GSL is very well documented, so this is not a problem.
An important use of GSL for simulation modeling is solving ODEs. GSL contains many different algorithms, some
simple, some complex. We will only use a simple version of the fourth-order Runge-Kutta. To insulate the beginner,
the template has hidden almost all of the details in the header file: SimMBS.h. (If you want examine this file, you can
click on this webpage URL link to SimMBS header file. If you are sufficiently masochistic, you might also enjoy the
header file for the plotting routines called SimPlot header file.)
5.4.5
Detailed Explanation of the Template
The complete template is found on the final pages of this document. Here we give a blow-by-blow description of what
is going on in the template. Following a complete template source code, is the complete code for a real example.
5.4.5.1
15
16
Include
#define MAIN
#include <SimMBS.h>
/* DO NOT REMOVE */
/* DO NOT REMOVE */
#define MAIN is needed so that SimPlot() variables are not declared multiple times when Simplot.h is used
multiple times.
#include <SimMBS.h> includes all the SimPlot() variables and functions as well as the GSL functions we
need.
5.4.5.2
21
22
23
24
25
26
Global Model Defines
/* number of state variables */
#define NUMVARS 1
/* number of state variables in this model */
/* max number of parameters */
#define NUMPARMS 12
/* max time steps : large prevents Segmentation Faults... */
#define MAXT
11000
/* plotting array storage */
27
28
29
30
31
32
33
34
35
/* useful defines for programming clarity */
/* MEANINGFUL names for state variables in arrays y0[], y[], dydt[]*/
#define YOUR_CHOICE 0
/* ... more as needed */
/* MEANINGFUL names for parameters in array p[]*/
#define PARM1
0
/* verbal definition here */
#define PARM2
1
/* verbal definition here */
/* ... more as needed */
Since we will be using arrays to hold variables, we must define the size of these arrays by defining the number of
state variables and a reasonable upper limit of the number of parameters. MAXT should be large in order to plot lots of
points, but as seen below we will not save every solution point.
In addition to the size the arrays in the model, we must also give understandable names to the locations of parameters and state variables in the arrays. If we do not do this, we must refer to parameters and variables as p[8], and this
is not a good thing. So we give names to parameter array indices and variable array indices. For example, suppose we
have a model of HIV/AIDS with three state variables. These values will be stored in an array y[]. If we define:
#define HEALTHY
#define HIV
#define AIDS
0
1
2
we can refer in our code to the values of the state variables as:
y[HEALTHY]=10000.0;
y]AIDS]=0.0;
44
since the #define command causes a symbolic substitution of 0, 1, 2 for the labels prior to the compile stage. This
allows the code to be much more readable compared to:
y[0]=10000.0;
y]1]=0.0;
We use the same approach to parameters. So it is important that you choose meaningful names in lines 30 and 33 and
similar lines. Short, meaningful names are the ideal.
5.4.5.3
37
38
39
40
41
42
Global Plotting Arrays
/* GLOBALS */
/* Simulation Run 1 result arrays for plotting, printing,etc */
/* Use your own variable names here ... these are dummies */
float Time1[MAXT],
/* save time values here */
SV1A[MAXT];
/* save state variable values here */
int
plotpt1;
/* number of solutions saved in result arrays */
43
44
45
46
47
/* Simulation Run 2 result arrays for plotting, printing,etc */
float Time2[MAXT],
/* save time values here */
SV2A[MAXT];
/* save state variable values here */
int
plotpt2;
/* number of solutions saved in result arrays */
48
49
/* Add more arrays for more runs here ...*/
These lines are similar to previous programs, except now we want to allow plotting of the results of multiple simulation
runs. We will want to do this when we do parameter sensitivity analysis: set the parameters, run the model, change
parameters, run model again, and so on. Then plot all results on a single graph so we can visualize the effects of the
changes. We need to provide multiple sets of arrays for each run, where each set includes arrays for each state variable
we wish to save and later plot. We can use as many sets as we have different runs.
45
5.4.5.4
51
52
53
54
55
main() Local Variables
int main(void)
{
float TMax;
float dt;
float save_delt;
/* End time of simulation */
/* DeltaT: time step of solution */
/* time between results save */
56
57
58
double p[NUMPARMS];
double y0[NUMVARS];
/* array of parameters */
/* array of initial conditions */
The only change here is that we need to explicitly save the initial conditions (y0[]) so they can be re-set for new
simulation runs.
5.4.5.5
60
61
Read and Initialize System Variables
/* Read Parameters (p); Initial Conditions (y0), simulation control */
InitSystem(p,y0,&TMax,&dt, &save_delt);
The invocation of the initialization function is the same except we pass in y0[] and not y[] (which is no longer
declared in main()). This function is described below.
5.4.5.6
63
64
Simulate the System
/* Simulate the system
SIMULATION_BEGIN;
*/
/* DO NOT REMOVE: creates ODE-solver variables */
65
66
67
68
69
70
/* Do Run 1 with original parameters */
/* save "plotpt" number of results in Time and Species */
PLOTTIME
= Time1;
/* "PLOTTIME"
declared in <SimMBS.h> */
PLOTVAR1
= SV1A;
/* "PLOTVAR1,2-16" declared in <SimMBS.h> */
PointsSavedp = &plotpt1; /* "PointsSavedp"
pointer declared in <SimMBS.h> */
71
72
73
/* Do the first run */
Simulate(0.0,TMax,dt,p,y0,save_delt);
Line 64 is a C macro that is defined in SimMBS.h. A macro is C code that is substituted exactly at the point that the
macro name is used (i.e., on line 64). The SIMULATION_BEGIN; macro creates the variables needed by the GSL
ODE Runge-Kutta solver. If time and interest permits, we will discuss the details later. Suffice it to say that this line
must be present.
In lines 68 – 70 we prepare the Simulate() function to be able to save our results in arrays. The header
SimMBS.h declares the name PLOTTIME to hold the saved time values, and 16 additional names PLOTVAR1 to
PLOTVAR16 to hold the saved state variable results. As seen below, these are the names that Simulate() will use
to save the values, but we must associate these generic names with specific arrays that our code declares for plotting
in line 40, above.
The actual simulation is done in line 73, where the only change is that we pass in the array y0[], not y[].
46
5.4.5.7
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
Repeat a Simulation
/* OPTIONAL: Do Run 2 with new parameters and plotting arrays */
/* new parameters: e.g. increase PARM1 by 50% */
p[PARM1] *= 1.5;
/* new initial conditions (if desired) */
y0[SV1name] += 100.0;
/* sanity check...
printf("PARAMETERS: ??=%f ??=%f ??=%f\n",
p[??],p[??],p[??]);
printf("INITIAL CONDITIONS: SV1name??=%f \n",y[SV1name??]);
*/
/* new plotting arrays */
/* save "plotpt2" number of results in Time2 and SV2A */
PLOTTIME
= Time2;
PLOTVAR1
= SV2A;
PointsSavedp = &plotpt2;
90
91
92
/* Do the second run */
Simulate(0.0,TMax,dt,p,y0,save_delt);
To do another run of the same model and to save the results in new plotting arrays, we first change whatever
parameters and/or initial conditions that we wish (lines 77 and 79). We must also associate the appropriate global
plotting arrays with the generic names used in Simulate(). This is done in line 87 and the following 2 lines.
Finally, the second simulation run is done in the same way as the first (line 92).
5.4.5.8
96
97
Finish the Simulations
/* End all simulation runs with the following line */
SIMULATION_DONE;
/* DO NOT REMOVE: releases ODE-solver variables */
98
99
100
/* plot or print results */
SimPlotMenu();
SIMULATION_DONE; is another C macro needed by GSL to remove the memory used by the ODE solver. Lastly,
we call the plotting menu as before.
5.4.5.9
InitSystem()
InitSystem() reads from a control file, instead of the keyboard. The control file has the general form:
1
2
3
4
5
P1
SV1
MaxT
dt
sdelt
0.009
0.02
2001
0.1
1
%
%
%
%
%
pop growth rate
initial pop size
sim stop time
delta-t
save plot delta-t
Each line specifies a variable needed in the model with the form: the name used in the C code (a documentation string
for the reader); the value to be used (the important part); and a descriptive string for more complete documentation of
the variable and the value. This last field can be as long as desired, but must be contained on a single line (the line can
be really long).
The programmer can use any order he or she desires, but the standard order that I use is: parameters, initial
conditions, simulation end time, simulation time step, and time interval between values saved for plotting. Additional
parameters and state variables are inserted in the appropriate place of the file.
To read from a file, we must declare and reserve memory for a special block of memory called the file handle.
Such a block of memory is needed for every file we open to read from or write to. The memory allows the operating
system to keep track of details of the open file such as from where in the file the last information was read, how full an
internal buffer is, and so on. The details do not concern us. The declaration of the file handle in InitSystem() is:
111
FILE *fcontrol;
/* declare a "file handle" for the file to read */
47
fcontrol is a pointer to (the address of) this block of memory. We read from the file in a manner similar to reading
from the keyboard, for example:
116
117
/* Read parameters from control file */
fscanf(fcontrol,"%*s%lf%*[ˆ\n]c\n",&p[PARM1name]);
/* use your name for parm 1 */
The only difference to the user is the scanning function name is fscanf(), for “file formatted scan.” There is an
additional first argument to pass to the function: the name of the file handle address: fcontrol in this case.
The remaining scans are similar.
5.4.5.10
Simulate()
Local Variables
144
145
146
147
148
double y[NUMVARS];
double yerr[NUMVARS];
double t,
save_t;
int i;
/*
/*
/*
/*
/*
array of dynamic state variables */
error in integration (for GSL) */
current simulation time */
next time to save results */
index in save_results arrays */
In addition to those used in previous programs, we declare the set array of state variables here (line 144). y0[] declared in main() contains the current set of initial conditions. The variable save t is the next time in the simulation
to save the results for plotting.
Initialize the Simulation Run We initialize the simulation run by copying the ICs into the array of state variables:
150
151
152
153
/* initialize the state variables */
for(i=0;i<NUMVARS;i++){
y[i]=y0[i];
/* copy initial conditions to st. vars.*/
}
And save the initial values for later plotting using the standard generic plotting array names, which have been associated with specific global arrays in the main() (line 68):
154
155
156
157
158
/* save initial conditions for plotting */
PLOTTIME[0]=start_t;
PLOTVAR1[0]=y[SV1name]; /* substitute your name for "SV1name" */
/* ... more as needed by the current model ... */
*PointsSaved=1;
where y[SV1name] is the element in the array of state variables to be stored in plotting array PLOTVAR1. The
programmer must choose good names for the generic (and uninformative) “SV1name.” Line 156 is repeated with new
names for each variable to save.
The Simulation Loop
161
162
163
164
165
166
167
168
169
170
171
/* loop from t=start to t=end ... */
for(t=start_t;t<=stop_t;t+=dt) {
/* DO NOT REMOVE : GSL integrate */
SIMULATE_ONE_STEP;
/* save results as floats for plotting routines */
/* save results every "save_delt" solution steps */
if(t>=save_t) {
/* ? time to save results? */
PLOTTIME[i]=t;
/* save time values */
PLOTVAR1[i]=y[SV1name];
/* save state variables */
/* ... more as needed ... */
(*PointsSaved)++;
/* increment number of pts saved */
172
173
174
175
176
/* SANITY CHECK: comment out after debugging; change as needed */
/*
printf("simulate(solved): t=%f y=%f n=%d\n",
t,y[SV1],*PointsSaved);
48
printf("simulate(saved): t=%f y1=%f y2=%f n=%d\n",
PLOTVAR1[i],PLOTVAR2[i],*PointsSaved);
fflush(stdout);
*/
i++;
/* increment output array indices */
save_t += save_delt;
/* increment next time to save */
177
178
179
180
181
182
}
183
184
}
Line 164 is another macro for invoking the GSL ODE solver to obtain a solution over one time step. The macro is in
SimMBS.h, but we can ignore the details for now. Once we have a new solution, we must save it, if it is time. The
local variable save t is the time to save another value; if the simulation has advanced beyond that time (line 167),
then we store the results. Again, the user will substitute useful names for variable array elements (SV1name). When
the save is complete, we increment the points saved and the time to save the next values: line 182.
5.4.5.11
Plotting
Plotting is done as before using the global plotting arrays. The complete code below shows how to use the simple and
complex (more than a single array of X-axis values) plotting functions. Refer to the SimPlot() documentation for
more details.
49
5.4.6
1
2
3
Complete Code for SimTemplate-Empty.c
/* SimTemplate.c -Template for Simulating and Plotting multiple simulation runs
using GSL RK4 library and Dislin plotting
4
This code, which has no model implemented, is used as a template only.
When a specific model is provided, it uses Runge-Kutta 4 to solve a
system of ODEs. It has the capability to make and plot multiple runs
of a single model with different parameter values. This would be
useful in doing error analysis and sensitivity analyses.
5
6
7
8
9
10
The model reads a file "SimTemplate-Empty.ctrl" for parameter values and
simulation control.
11
12
13
*/
14
15
16
#define MAIN
#include <SimMBS.h>
/* DO NOT REMOVE */
/* DO NOT REMOVE */
17
18
19
/* ========================================================================*/
/* THE FOLLOWING NEEDS TO BE MODIFIED FOR DIFFERENT MODELS */
20
21
22
23
24
25
26
/* number of state variables */
#define NUMVARS 1
/* number of state variables in this model */
/* max number of parameters */
#define NUMPARMS 12
/* max time steps : large prevents Segmentation Faults... */
#define MAXT
11000
/* plotting array storage */
27
28
29
30
31
32
33
34
35
/* useful defines for programming clarity */
/* MEANINGFUL names for state variables in arrays y0[], y[], dydt[]*/
#define YOUR_CHOICE 0
/* ... more as needed */
/* MEANINGFUL names for parameters in array p[]*/
#define PARM1
0
/* verbal definition here */
#define PARM2
1
/* verbal definition here */
/* ... more as needed */
36
37
38
39
40
41
42
/* GLOBALS */
/* Simulation Run 1 result arrays for plotting, printing,etc */
/* Use your own variable names here ... these are dummies */
float Time1[MAXT],
/* save time values here */
SV1A[MAXT];
/* save state variable values here */
int
plotpt1;
/* number of solutions saved in result arrays */
43
44
45
46
47
/* Simulation Run 2 result arrays for plotting, printing,etc */
float Time2[MAXT],
/* save time values here */
SV2A[MAXT];
/* save state variable values here */
int
plotpt2;
/* number of solutions saved in result arrays */
48
49
/* Add more arrays for more runs here ...*/
50
51
52
53
54
55
int main(void)
{
float TMax;
float dt;
float save_delt;
/* End time of simulation */
/* DeltaT: time step of solution */
/* time between results save */
56
57
58
double p[NUMPARMS];
double y0[NUMVARS];
/* array of parameters */
/* array of initial conditions */
59
60
61
/* Read Parameters (p); Initial Conditions (y0), simulation control */
InitSystem(p,y0,&TMax,&dt, &save_delt);
62
63
64
/* Simulate the system
SIMULATION_BEGIN;
*/
/* DO NOT REMOVE: creates ODE-solver variables */
65
66
/* Do Run 1 with original
parameters
*/
50
/* save "plotpt" number
PLOTTIME
= Time1;
/*
PLOTVAR1
= SV1A;
/*
PointsSavedp = &plotpt1; /*
67
68
69
70
of results in Time and Species */
"PLOTTIME"
declared in <SimMBS.h> */
"PLOTVAR1,2-16" declared in <SimMBS.h> */
"PointsSavedp"
pointer declared in <SimMBS.h> */
71
/* Do the first run */
Simulate(0.0,TMax,dt,p,y0,save_delt);
72
73
74
/* OPTIONAL: Do Run 2 with new parameters and plotting arrays */
/* new parameters: e.g. increase PARM1 by 50% */
p[PARM1] *= 1.5;
/* new initial conditions (if desired) */
y0[SV1name] += 100.0;
/* sanity check...
printf("PARAMETERS: ??=%f ??=%f ??=%f\n",
p[??],p[??],p[??]);
printf("INITIAL CONDITIONS: SV1name??=%f \n",y[SV1name??]);
*/
/* new plotting arrays */
/* save "plotpt2" number of results in Time2 and SV2A */
PLOTTIME
= Time2;
PLOTVAR1
= SV2A;
PointsSavedp = &plotpt2;
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
/* Do the second run */
Simulate(0.0,TMax,dt,p,y0,save_delt);
91
92
93
/* Do More Runs if desired ... */
94
95
/* End all simulation runs with the following line */
SIMULATION_DONE;
/* DO NOT REMOVE: releases ODE-solver variables */
96
97
98
/* plot or print results */
SimPlotMenu();
return 0;
99
100
101
102
}
103
104
105
106
107
108
/*
InitSystem -- initialize system for simulation: read parms, ICs, init GSL
*/
void InitSystem(double p[], double y0[], float *TMax, float *dt, float *sdelt)
{
109
110
111
112
/* DO NOT REMOVE next 2 code lines: open file to read */
FILE *fcontrol;
/* declare a "file handle" for the file to read */
fcontrol=OpenFilePrompt("Enter name of simulation control file: ","r");
113
114
/* DO CHANGE the following fscanf() lines to fit the model ... */
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
/* Read parameters from control file */
fscanf(fcontrol,"%*s%lf%*[ˆ\n]c\n",&p[PARM1name]);
/* use your name for parm 1 */
fscanf(fcontrol,"%*s%lf%*[ˆ\n]c\n",&p[??]);
/* ... more parameters as needed... */
/* Read Initial Conditions from control file */
fscanf(fcontrol,"%*s%lf%*[ˆ\n]c\n",&y0[??]);
/* ... more ICs as needed... */
/* Read simulation run control variables */
fscanf(fcontrol,"%*s%f%*[ˆ\n]c\n",TMax);
/* simulation end time */
fscanf(fcontrol,"%*s%f%*[ˆ\n]c\n",dt);
/* simulation time step */
fscanf(fcontrol,"%*s%f%*[ˆ\n]c\n",sdelt);
/* plotting time interval */
/* Sanity Check: echo the input */
printf("\n");
printf("PARAMETERS: ??=%f ??=%f ??=%f\n",
p[??],p[??],p[??]);
printf("INITIAL CONDITIONS: SV1=%f \n",y[SV1name]);
printf("SIMULATION CONTROL: TMax=%f delta_t=%f savedt=%f\n",
*TMax,*dt,*sdelt);
134
51
fclose(fcontrol);
135
136
/* DO NOT REMOVE: close the control file */
}
137
138
139
140
141
142
143
144
145
146
147
148
/*
Simulate -- step through ODEs, save results every "save_delt" time steps
*/
void Simulate(float start_t,float stop_t, float dt, double p[],
double y0[], float save_delt)
{
double y[NUMVARS];
/* array of dynamic state variables */
double yerr[NUMVARS];
/* error in integration (for GSL) */
double t,
/* current simulation time */
save_t;
/* next time to save results */
int i;
/* index in save_results arrays */
149
/* initialize the state variables */
for(i=0;i<NUMVARS;i++){
y[i]=y0[i];
/* copy initial conditions to st. vars.*/
}
/* save initial conditions for plotting */
PLOTTIME[0]=start_t;
PLOTVAR1[0]=y[SV1name]; /* substitute your name for "SV1name" */
/* ... more as needed by the current model ... */
*PointsSaved=1;
150
151
152
153
154
155
156
157
158
159
160
i=1;
161
/* loop from t=start to t=end ... */
for(t=start_t;t<=stop_t;t+=dt) {
/* DO NOT REMOVE : GSL integrate */
SIMULATE_ONE_STEP;
/* save results as floats for plotting routines */
/* save results every "save_delt" solution steps */
if(t>=save_t) {
/* ? time to save results? */
PLOTTIME[i]=t;
/* save time values */
PLOTVAR1[i]=y[SV1name];
/* save state variables */
/* ... more as needed ... */
(*PointsSaved)++;
/* increment number of pts saved */
162
163
164
165
166
167
168
169
170
171
172
/* SANITY CHECK: comment out after debugging; change as needed */
/*
printf("simulate(solved): t=%f y=%f n=%d\n",
t,y[SV1],*PointsSaved);
printf("simulate(saved): t=%f y1=%f y2=%f n=%d\n",
PLOTVAR1[i],PLOTVAR2[i],*PointsSaved);
fflush(stdout);
*/
i++;
/* increment output array indices */
save_t += save_delt;
/* increment next time to save */
173
174
175
176
177
178
179
180
181
182
}
183
}
184
185
}
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
/*
deriv -- calculate the rates of change for all state vars
DO NOT CHANGE the next line.
*/
int deriv(double t,double y[],double dydt[],double p[])
{
/* t
= current time
y[]
= current solutions
p[]
= array of parameters
dydt[] = output: rates of change (derivatives)
*/
/*
...declare and compute any driving or auxilliary variables here ...
*/
201
202
/* the model derivatives ...use your names for st. vars and parameters */
52
/* any C code can go here */
dydt[??] = p[??] - y[??]*p[??] - y[??]*p[??];
/* example */
dydt[??] = ?? ;
/* additional state variables */
203
204
205
206
return GSL_SUCCESS;
207
208
/* DO NOT REMOVE */
}
209
210
211
212
213
214
215
216
217
218
219
220
/*
DoPlots -- call the plotting package wrapper
*/
void DoPlots(void)
{
/* o Different models will use 0 or more invocations of SimPlot() and
0 or more invocations of SimPlotManyX(), depending on what is needed.
o The basic syntax of each is shown below; see the SimPLot() documentation
for an explanation.
o These functions can be used any number of times and in any order.
*/
221
222
/* SimPlot(): plot several variables on Y axis vs. 1 X axis array
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
SimPlot(GRAPH_TITLE(a_string), NUMBER_POINTS(an_int), XAXIS_VALUES(a_float[]),
XAXIS_TITLE(a_string), YAXIS_VALUES(a_float[]), YAXIS_TITLE(a_string),
YAXIS_VALUES_2(a_float[]), YAXIS_TITLE_2(a_string),
...more YAXES ..., END_PLOT_LIST);
*/
/* SimPlotManyX(): plot several vaiables on Y axis vs many different
X axis arrays
SimPlotManyX(GRAPH_TITLE(a_string), XAXIS_TITLE(a_string),
NUMBER_POINTS(an_int), XAXIS_VALUES(a_float[]),
YAXIS_VALUES(a_float[]), YAXIS_TITLE(a_string),
NUMBER_POINTS_2(an_int), XAXIS_VALUES_2(a_float[]),
YAXIS_VALUES_2(a_float[]), YAXIS_TITLE_2(a_string),
... more X-Y pairs of arrays ...
END_MANYX_LIST);
*/
/* change plotting defaults if desired ... examples */
PAGE_X_INCH = 4;
PAGE_Y_INCH = 4;
LEGEND_POS = 6;
NUM_Y_DIGITS = 2;
NUM_X_DIGITS = 0;
LINECYCLES("colordashes");
OUTFILE_COLOR = 0;
DARKER = 0;
DOTITLE=1;
249
250
251
252
253
254
255
256
257
/* examples, optional */
SimPlot("??Atitle??", plotpt1, Time1, "??Time??", SV1, "??SV1??",
SV2, "??SV2??", END_PLOT_LIST);
/* more SimPlot()’s ... if desired */
SimPlotManyX("??Atitle 2", "??Time??", plotpt1, Time1,"??Time??", SV1,"??Run 1|???",
plotpt2,Time2,SV1,"??Run 2??",
END_MANYX_LIST);
/* more SimPlotManyX()’s ... if desired */
258
259
260
261
262
263
264
265
266
267
268
269
270
SimPlotFinish();
/* DO NOT REMOVE */
}
/*
DoPrint -- call the printing package wrapper
*/
void DoPrint(void)
{
/* SimPrint() prints numbers in labeled columns to a text file. Multiple calls
sends output to different files.
*/
SimPrint("??Atitle",plotpt1,Time1,"??Time??",SV1, "??SV1??",SV2,"??SV2??",
END_PRINT_LIST);
53
SimPrint("??Atitle2",plotpt2,Time2,"??Time??",SV3, "??SV3??",
END_PRINT_LIST);
271
272
273
}
274
275
#undef MAIN
54
Chapter 6
Using MBS Modeling in Microsoft Windows
The models and supporting software were developed using the Linux operating system. All of the code and applications are either open-source or free. Nevertheless, these excellent modeling and programming tools can still be used
on the proprietary operating system for those that feel that they can not trust a program they haven’t paid for. Briefly,
here, in lieu of a nickel, are the instructions.
1. Go to section 9.2 for instructions on installing the Windows Dev-Cpp IDE, the Windows versions of the GSL
numerical library, the DISLIN plotting library, the the MBS SimPlot graphing/modeling library.
2. The use of Dev-Cpp to create and use programming projects is essentially the same as anjuta (section 5.2).
The concepts are the same and reading the anjuta instructions will prepare you for Dev-Cpp.
3. Creating, running, and analyzing models the MBS way are identical in Windows as in Linux, since the MBS
SimPlot code is identical. See section 5.3.
55
Chapter 7
Source Code
Summary: Following a table of contents, this chapter provides links to code examples described in the textbook as
well as explications of some of the more important elements of the code.
56
Contents
7.1
7.2
7.3
7.4
7.5
7.6
7.7
7.8
7.9
7.10
7.11
7.12
7.13
7.14
7.15
7.16
7.17
7.18
7.19
MBS Chapter 1: Models of Systems . . . . . . . . . . . . . .
7.1.1 Code Links . . . . . . . . . . . . . . . . . . . . . . .
7.1.2 Explanation . . . . . . . . . . . . . . . . . . . . . . .
MBS Chapter 2: The Modeling Process . . . . . . . . . . . .
MBS Chapter 3: Qualitative Model Formulation . . . . . .
MBS Chapter 4: Quantitative Model Formulation: I . . . .
7.4.1 Code Links . . . . . . . . . . . . . . . . . . . . . . .
7.4.2 Explanation . . . . . . . . . . . . . . . . . . . . . . .
MBS Chapter 5: Quantitative Model Formulation:II . . . .
7.5.1 Code Links . . . . . . . . . . . . . . . . . . . . . . .
7.5.2 Explanation . . . . . . . . . . . . . . . . . . . . . . .
MBS Chapter 6: Numerical Techniques . . . . . . . . . . .
7.6.1 Code Links . . . . . . . . . . . . . . . . . . . . . . .
MBS Chapter 7: Parameter Estimation . . . . . . . . . . . .
7.7.1 Code Links . . . . . . . . . . . . . . . . . . . . . . .
7.7.2 Linear Regression . . . . . . . . . . . . . . . . . . . .
7.7.3 Levenberg-Marquardt . . . . . . . . . . . . . . . . . .
7.7.4 Simplex . . . . . . . . . . . . . . . . . . . . . . . . .
7.7.5 Calibrating Models Using Simplex . . . . . . . . . . .
7.7.6 Examples and Hints . . . . . . . . . . . . . . . . . . .
MBS Chapter 8: Model Validation . . . . . . . . . . . . . .
7.8.1 Code Links . . . . . . . . . . . . . . . . . . . . . . .
7.8.2 Explanation . . . . . . . . . . . . . . . . . . . . . . .
7.8.3 SimValidate() Output . . . . . . . . . . . . . . .
MBS Chapter 9: Model Analysis . . . . . . . . . . . . . . .
7.9.1 Code Links . . . . . . . . . . . . . . . . . . . . . . .
7.9.2 Error Analysis . . . . . . . . . . . . . . . . . . . . . .
7.9.3 Nullclines . . . . . . . . . . . . . . . . . . . . . . . .
MBS Chapter 10: Stochastic Models . . . . . . . . . . . . .
7.10.1 Code Links . . . . . . . . . . . . . . . . . . . . . . .
7.10.2 Explanation . . . . . . . . . . . . . . . . . . . . . . .
MBS Chapter 11: Photosynthesis and Plant Growth . . . . .
MBS Chapter 12: Hormonal Control in Mammals . . . . .
MBS Chapter 13: Populations and Individuals . . . . . . . .
7.13.1 Code Links . . . . . . . . . . . . . . . . . . . . . . .
7.13.2 Population-level Models . . . . . . . . . . . . . . . .
7.13.3 Individual-based Models . . . . . . . . . . . . . . . .
7.13.4 Linked Lists . . . . . . . . . . . . . . . . . . . . . . .
7.13.5 Coding Doubly Linked Lists . . . . . . . . . . . . . .
MBS Chapter 14: Chemostats . . . . . . . . . . . . . . . . .
7.14.1 Code Links . . . . . . . . . . . . . . . . . . . . . . .
MBS Chapter 15: Diseases . . . . . . . . . . . . . . . . . . .
7.15.1 Code Links . . . . . . . . . . . . . . . . . . . . . . .
MBS Chapter 16: Spatial Patterns and Processes . . . . . .
MBS Chapter 17: Scaling Models . . . . . . . . . . . . . . .
MBS Chapter 18: Chaos in Biology . . . . . . . . . . . . . .
MBS Chapter 19: Cellular Automata and Recursive Growth
7.19.1 Code Links . . . . . . . . . . . . . . . . . . . . . . .
57
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
59
59
59
59
59
59
59
60
62
62
62
63
64
64
64
65
66
68
70
73
73
73
74
74
76
76
76
78
79
79
80
80
80
80
80
81
81
81
82
87
87
88
88
88
88
88
88
88
7.19.2 Data Structures . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
7.19.3 Swapping Grids . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
7.20 MBS Chapter 20: Evolutionary Computation . . . . . . . . . . . . . . . . . . . . . . . . . . . .
58
89
89
90
7.1
MBS Chapter 1: Models of Systems
7.1.1
Code Links
Here are the links to the example code. Explanations follow in later sections.
Windows users: clicking on the provided link will launch InternetExplorer if you have Acrobat
Reader 6.0 (other versions not tested). Linux users: if you use acroread version 5.0.6 or 5.0.10,
clicking on the link will work with Mozilla or Firefox provided the browser is already launched
somewhere in your workspace. If not, the launch may fail on the first try but will succeed if you click on
the link a second time with the browser open. Other users: leave this PDF file and manually copy the
code files to your local directories.
C Code
• Island Biogeography - Finite Difference:
Click on this link: IslandBiogeog-FDC
• Island Biogeography - Finite Difference (Plots):
Click on this link: IslandBiogeog-FDplotsC
• Island Biogeography - Finite Difference Matlab (Plots):
Click on this link: IslandBiogeog-FDMatlab
• Island Biogeography - Finite Difference Octave (Plots):
Click on this link: IslandBiogeog-FDOctave
7.1.2
Explanation
These examples are basic C and do not really require explanation. See section 5.3 for an explanation of the plotting
arrays and functions.
7.2
MBS Chapter 2: The Modeling Process
There is no code associated with this chapter.
7.3
MBS Chapter 3: Qualitative Model Formulation
There is no code associated with this chapter.
7.4
7.4.1
MBS Chapter 4: Quantitative Model Formulation: I
Code Links
Here are the links to the example code mentioned in the textbook. Explanations follow in later sections.
Windows users: clicking on the provided link will launch InternetExplorer if you have Acrobat
Reader 6.0 (other versions not tested). Linux users: if you use acroread version 5.0.6 or 5.0.10,
clicking on the link will work with Mozilla or Firefox provided the browser is already launched
somewhere in your workspace. If not, the launch may fail on the first try but will succeed if you click on
the link a second time with the browser open. Other users: leave this PDF file and manually copy the
code files to your local directories.
• SimTemplate-Empty
Click on this link: SimTemplate-Empty
• SimTemplate-Multi
Click on this link: SimTemplateMultirun
59
7.4.2
Explanation
The following line numbers correspond to SimTemplate-Empty.c. This program reads data and does a single
run of the model.
7.4.2.1
Defines and Globals
Include the SimMBS.h header file that declares the SimPlot functions and variables as well as brings in the C header
files and the DISLIN and GSL header files.
13
14
#define MAIN
#include <SimMBS.h>
/* DO NOT REMOVE */
/* DO NOT REMOVE */
Define the number of state variables, parameters, and the maximum size of plotting arrays.
20
21
22
23
24
#define NUMVARS 2
/* number of state variables in this model */
/* max number of parameters */
#define NUMPARMS 12
/* max time steps (more than you could possibly want...) */
#define MAXT
11000
/* plotting array storage */
NUMVARS is indirectly used by the GSL ODE solver, and needs to be the correct number of state variables. NUMPARMS
is a number for the p[] array that can store both model parameters as well as non-state variable output results such as
auxiliary variables.
Provide meaningful names for the state variables and parameters.
27
28
29
30
31
32
33
34
/* MEANINGFUL names for
#define YOUR_NAME1 0
#define YOUR_NAME2 1
/* ... more as needed */
/* MEANINGFUL names for
#define PARM1
0
#define PARM2
1
/* ... more as needed */
state variables in arrays y[], dydt[]*/
parameters in array p[]*/
/* verbal definition here
/* verbal definition here
*/
*/
The last requirement in the global data area is to declare the plotting arrays:
38
39
40
41
float Time[MAXT],
SV1[MAXT],
SV2[MAXT];
int
plotpt;
/*
/*
/*
/*
save time values here */
save state variable values here */
save state variable values here */
number of solutions saved in result arrays */
where, MAXT was defined earlier to be a value large enough to hold all of the output time series that we save.
7.4.2.2
main()
Within the main() function we define local variables for parameters and state variables, using the defines for the size
of these arrays. We also declare the variables needed for the control of the simulation run (TMax, etc).
46
47
48
float TMax;
float dt;
float save_delt;
/* End time of simulation */
/* DeltaT: time step of solution */
/* time between results save */
double p[NUMPARMS];
double y0[NUMVARS];
/* array of parameters */
/* array of intital conditions */
49
50
51
Data for parameters, initial conditions, and run control variables are read with InitSystem():
54
InitSystem(p,y0,&TMax,&dt, &save_delt);
The details for creating the data structures needed by the GSL ordinary differential equation (ODE) solver is hidden
in a preprocessor macro defined in SimMBS.h.
60
58
SIMULATION_BEGIN;
/* DO NOT REMOVE: creates ODE-solver variables */
Next, we assign the aliases for the plotting arrays and number of plotting points saved during the simulation.
62
63
64
PLOTTIME=Time;
PLOTVAR1=SVname;
PointsSavedp=&plotpt;
The model itself is solved and output saved by invoking
71
Simulate(0.0,TMax,dt,p,y0,save_delt);
where the starting time is set at 0.0.
In creating data structures for solving the ODEs, GSL allocated dynamic memory that needs to be freed. This is
done in the macro
73
SIMULATION_DONE;
/* DO NOT REMOVE: releases ODE-solver variables */
Finally, main() creates plots that are presented through user input in the SimPlot plotting menu.
76
7.4.2.3
Simulate()
Most of the work is done by Simulate(). This function initializes state variables and plotting arrays:
132
133
134
135
136
137
138
139
for(i=0;i<NUMVARS;i++){
y[i]=y0[i];
/* copy initial conditions to st. vars.*/
}
/* save initial conditions */
PLOTTIME[0]=start_t;
PLOTVAR1[0]=y[SPECIES];
/* ... more as needed by the current model ... */
*PointsSavedp=1;
The main simulation loop repeatedly solves the ODEs for one time step, and saves the results if the time is right.
47
48
49
for(t=start_t;t<=stop_t;t+=dt) {
/* integrate: DO NOT REMOVE */
SIMULATE_ONE_STEP;
if(t>=save_t) {
/* ? time to save results? */
PLOTTIME[i]=t;
/* save time values */
PLOTVAR1[i]=y[SPECIES];
/* save state variables */
(*PointsSavedp)++;
47
48
49
50
7.4.2.4
The Derivatives
The derivatives are evaluated multiple times by the GSL ODE solver. The function to do this must be named deriv(),
and have the assigned arguments as shown:
173
int deriv(double t,double y[],double dydt[],double p[])
The rate of change of each state variable is stored in an array called dydt[]. It is written in an interpretable
manner by making frequent use of the names for indices in the parameter (p[]) and state variable (y[]) arrays.
47
48
dydt[??] = p[??] - y[??]*p[??] - y[??]*p[??];
/* example */
dydt[??] = ?? ;
/* additional state variables */
where the question marks and use of the p[] and y[] arrays will be those defined in the global area.
61
7.4.2.5
Plotting
The plotting menu is created by SimPlotMenu(). Based on user input, this menu calls DoPlots() (or, the
printing function, DoPrint()).
The conditions of the plot are controlled by global variables and macros:
PAGE_X_INCH = 6;
PAGE_Y_INCH = 9;
LEGEND_POS = 8;
NUM_Y_DIGITS = 1;
NUM_X_DIGITS = 0;
LINECYCLES("colordashes");
OUTFILE_COLOR = 0;
DARKER = 0;
DOTITLE=1;
GRFS_PER_FILE=3;
224
225
226
227
228
229
230
231
232
233
/*
/*
/*
/*
/*
/*
/*
/*
/*
/*
printed size: 6 inches wide */
printed size: 9 inches high */
legend in upper-left */
Y-axis numbers: 999.1 */
X-axis numbers: 999 */
curves on graph are different colors and shapes */
printed graph is black and white only */
don’t make WMF output lines double width (blacker)*/
Put a graph title at top, centered */
Stack 3 graphs on top of each other */
Once these are set, the plot is done with SimPlot() and the plot is terminated by a user mouse click after
SimPlotFinish() is invoked:
SimPlot("??Atitle??", plotpt, Time, "??Time??", SV1, "??SVname??",
SV2, "??SV2??", END_PLOT_LIST);
236
237
238
SimPlotFinish();
239
7.5
/* required */
MBS Chapter 5: Quantitative Model Formulation:II
7.5.1
Code Links
The links for code mentioned in the textbook are here:
Windows users: clicking on the provided link will launch InternetExplorer if you have Acrobat
Reader 6.0 (other versions not tested). Linux users: if you use acroread version 5.0.6 or 5.0.10,
clicking on the link will work with Mozilla or Firefox provided the browser is already launched
somewhere in your workspace. If not, the launch may fail on the first try but will succeed if you click on
the link a second time with the browser open. Other users: leave this PDF file and manually copy the
code files to your local directories.
C Code
• Curve (Function) Display
Click on this link: SimCurveDisplay
• Distribution Display
Click on this link: SimDistributionDisplay
7.5.2
Explanation
The above links provide two programs that are useful in visualizing functions for modeling. The first allows the user
to display functions of their design with different parameter values. A typical application would be to display the
curves in Figure 5.4, such as those for the power function. The second (plotting distributions) is basically the same,
but the program illustrates plotting multiple curves for probability distributions using the GSL probability distribution
functions. Since it uses a slightly different interface to the DoPlots() function, we highlight below some of the new
features and approaches.
Since we wish to display a relatively large number of plots on a single x-y graph, creating separate one-dimensional
arrays manually would be tedious. Instead, we use dynamic array allocation to create a two-dimensional array in which
each row will contain the values for different parameter values for the function, and each column will correspond to
the function (y-axis value) evaluated at each x-axis value. To dynamically allocate, we create a global pointer that
reference the 2D array.
62
25
26
27
28
float X[MAX_XVALS];
float *Y;
int NumX,
NumG;
where X[] is statically allocated at compile time and holds x-axis values to plot. Y is the pointer to the 2D array whose
columns will be stored in NumX and rows in NumG. The X[] array is created in main() with:
delX=(maxX-minX)/(NumX-1);
for(i=0;i<NumX;i++){
X[i] = minX + i*delX;
}
53
54
55
56
where minX, maxX, and NumX are user inputs.
The 2D array is allocated using a standard C function that dynamically allocates storage for NumG*NumX floats.
if((Y=(float *)malloc(sizeof(float)*NumG*NumX))==NULL) {
fprintf(stderr,"!!Y[] not allocated. Size requested = %d\n",
sizeof(float)*NumG*NumX);
exit(1);
}
59
60
61
62
63
When memory for 2D arrays is allocated in this way, the usual array notation to address array elements (A[i][j])
does not work, so we do the usual index arithmetic ourselves, explicitly:
yp = Y + i*NumX;
47
where i is number of the row to fill with function values. Y points to the beginning of the array of floats, NumX is the
number of columns, so i*NumX is the beginning of the the i-th row.
A new function MakePlot() loops over the columns of the i-th row, calling the function or distribution we wish
to plot, inserting the returned values in the columns.
94
95
96
void MakePlot(double p[], double currX, int NumX, double delX, float *g)
{
int i;
97
for(i=0;i<NumX;i++){
*g = ToPlot(p,currX);
currX += delX;
g++;
}
98
99
100
101
102
103
}
108
float ToPlot(double p[], double x)
{
/* GSL gamma pdf */
return (float) gsl_ran_gamma_pdf(x,p[A],p[B]);
}
109
110
111
112
Finally, since previous plots have used one-dimensional arrays of results, we use a new plotting function that uses
2D arrays.
SimPlotFunctions(Gtitle,NumX,X,"Xaxis",NumG, Y,otitle);
47
This function could also be used to plot simulation results from (for example) from multiple simulations during sensitivity analysis.
7.6
MBS Chapter 6: Numerical Techniques
For the latest version of the MBS-CD consult the MBS-CDROM webpage.
63
7.6.1
Code Links
Here are the links to the example code. These examples are explained in other sections of the MBS-CD. See subsection 7.4.2 and chapter 4.
Windows users: clicking on the provided link will launch InternetExplorer if you have Acrobat
Reader 6.0 (other versions not tested). Linux users: if you use acroread version 5.0.6 or 5.0.10,
clicking on the link will work with Mozilla or Firefox provided the browser is already launched
somewhere in your workspace. If not, the launch may fail on the first try but will succeed if you click on
the link a second time with the browser open. Other users: leave this PDF file and manually copy the
code files to your local directories.
C Code
• Euler ODE Solver Template for C
Click on this link: SimTemplate-EulerC
• Runge-Kutta 4th Order ODE Solver Template for C
Click on this link: SimTemplate-RK4C
• Runge-Kutta 4th Order ODE Solver with Error Correction (Octave) Template
Click on this link: SimTemplate-RK45RunO
• Runge-Kutta 4th Order ODE Solver with Error Correction (Matlab) Template
Click on this link: SimTemplate-RK45RunM
7.7
MBS Chapter 7: Parameter Estimation
7.7.1
Code Links
Here are the links to the example code. Explanations follow in later sections.
Windows users: clicking on the provided link will launch InternetExplorer if you have Acrobat
Reader 6.0 (other versions not tested). Linux users: if you use acroread version 5.0.6 or 5.0.10,
clicking on the link will work with Mozilla or Firefox provided the browser is already launched
somewhere in your workspace. If not, the launch may fail on the first try but will succeed if you click on
the link a second time with the browser open. Other users: leave this PDF file and manually copy the
code files to your local directories.
C Code
• Linear Regression:
Click on this link: SimEst-LinearReg
• Levenberg-Marquardt Nonlinear Parameter Estimation:
Click on this link: SimEst-LevenbergMarquardtC
• Nelder-Mead Simplex Nonlinear Parameter Estimation:
Click on this link: SimEst-SimplexC
• Simplex Model Calibration:
Click on this link: SimCalibrate-LogisticC
• Simplex Model Calibration:
Click on this link: SimCalibrate-LotkaVolterraC
64
7.7.2
Linear Regression
Many packages can do linear regression on externally saved data, but it is sometimes useful to be able to do it inside
the simulation program without the bother of writing files. Here is how to do it on the log transformed data of a power
function:
y = a1 xa2
Y = log(y) = log(a1 ) + a2 log(x)
The following description applies to the file SimFit_Linear_Log.c. As is usual in this CD, the code is
accessible from the above webpage links and below, we describe the significant features. It will be helpful to open the
link and view the entire source code while reading the explications of code segments.
Global Plotting Arrays Ultimately, we wish to plot the predictions of the regression model against the data, so first
we declare the global plotting arrays and variables.
1
2
3
4
5
6
7
float Obsrvd_X[MAXDATA],
Obsrvd_Y[MAXDATA];
float Predict_Y[MAXDATA];
int
plotpts;
float BT_Predict_Y[MAXDATA],
BT_Obsrvd_X[MAXDATA],
BT_Obsrvd_Y[MAXDATA];
/* observed x’s as float */
/* observed y’s as float */
/* predicted y’s*/
/* Back-Transformed data */
We assume the primary data (e.g., Obsrvd_X) has already been log-transformed. Back-transformed data and coefficients refer to the inverse operation (anti-log-transformed).
Inputting Data For a general program, data can be read from a file, using this a function like
1
InitLinearFit(&ndata,X,Y);
where X[] and Y[] are data arrays. An example InitLinearFit() function is provided with the code.
A quick-and-dirty method is to use C array initialization:
1
2
3
4
double logX[]={0,0.3,0.48,0.6,0.7,0.78,0.85,0.9,0.95,1.0};
double logY[]={-0.25,0.65,1.33,1.75,1.85,2.03,2.03,2.11,2.6,2.97};
/* number of data points */
ndata=10;
Note the empty square brackets, braces, the commas, and the fact that we must explicitly provide the size of the array.
The regression is done invoking a GSL function:
1
2
gsl_fit_linear(logX,1,logY,1,ndata,
&Estimated_Intercept,&Estimated_Slope,&cov00,&cov01,&cov11,&sumsq);
where the arguments are the input x data (log-transformed in this example), the number ’1’ (GSL permits using
every nth element of the array (the stride), we choose to use every element), the input y data, the stride factor (‘1’),
and the addresses of the values that are computed by the function. The covariance matrix (with elements cov00,
cov01, cov11) represents the variance associated with each estimated coefficient of the linear equation (cov00
for the intercept, and cov11 for the slope). Refer to a statistics text for more details. The sum of squared differences
between data and model is returned in sumsq.
Saving for Plotting To plot, we write the data on which the regression was performed and the back-transformed
data (if appropriate).
1
2
3
4
5
/* Copy results to arrays for SimPlot() */
for(plotpts=0,j=0;j<ndata;j++,plotpts++){
Predict_Y[j]=b + m*logX[j];
/* regression equation */
Obsrvd_X[j]=logX[j];
/* original *transformed* data */
Obsrvd_Y[j]=logY[j];
/* original *transformed* data */
65
BT_Obsrvd_X[j]=pow(10,logX[j]);
/* anti-log_base10 */
BT_Obsrvd_Y[j]=pow(10,logY[j]);
BT_Predict_Y[j]=pow(10,b)*pow(BT_Obsrvd_X[j],m);
6
7
8
9
}
recalling that BT signifies back-transformed. Not all uses of linear regression will require back-transformation; if
the relationship is already a straight line with only the constants to be estimated, no transformation is needed.
After printing the results from the GSL function, we compute and print the sum of squares for the back-transformed
data and model:
1
2
3
4
5
/* Sum of squares for back-transformed data */
for(sumsq=0.0,j=0;j<ndata;j++){
sumsq += pow( (BT_Predict_Y[j] - BT_Obsrvd_Y[j]), 2);
}
printf("Sum of Squares (of back-transformed data) = %f\n\n",sumsq);
Plotting To plot data and regression lines together define DoPlots() as follows.
47
48
49
50
51
52
53
54
55
56
57
void DoPlots(void)
{
LINETYPES("LP");
ABSPADDED=3.0;
LEGEND_POS=8;
SimPlot("UnTransformed Fit",plotpts,Obsrvd_X,"X",Predict_Y,"Predicted",
Obsrvd_Y,"Observed",END_PLOT_LIST);
SimPlot("Back Transformed Fit",plotpts,BT_Obsrvd_X,"X",BT_Predict_Y,"Predicted",
BT_Obsrvd_Y,"Observed",END_PLOT_LIST);
SimPlotFinish();
}
7.7.3
Levenberg-Marquardt
GSL provides several nonlinear regression methods; their use requires slightly more coding than simple linear regression. For user-supplied functions that are nonlinear in the parameters, these iterative methods attempt to determine
parameters that minimize the sum of squared differences between user-supplied observations and the function predictions using parameters that are improved in each iteration. (The method is also applicable to functions that are linear
in the parameters, but standard linear regression is preferred in those situations.) Here we describe the LevenbergMarquardt (LM) method, as discussed extensively in the MBS textbook. The file SimFit_LM_Power.c illustrates
its use.
As is usual in this CD, the code is accessible from the above webpage links and below, we describe the significant
features. It will be helpful to open the link and view the entire source code while reading the explications of code
segments.
7.7.3.1
main(): Input Data Control Variables
To begin, the top matter (#includes, #defines, and global variables) are similar to that for linear regression. The
new local variables in main() are:
1
2
3
4
5
6
7
double Guesses[MAXPARMS];
double Y[MAXDATA],
X[MAXDATA],
Sigma[MAXDATA];
int p[MAXPARMS];
double FittedParams[MAXPARMS];
double k1,k2,k3;
/*
/*
/*
/*
/*
/*
initial guesses of parameters, big array */
y data */
x data */
sigma of y data */
parameters to control this run */
the function parameters */
The p[] array contains parameters that control the LM algorithm. These variables are read in the user-supplied
function InitFit(). The variables include the amount of data used in the parameter evaluations, the number of
parameters in the function, the number of replicates at each x value, and the maximum number of iterations the fitting
algorithm should use. These control variables are stored in p[] (line 5, above) using the usual #define mechanism
66
to provide mnemonic names to the array indices. All iterative methods, including LM, require initial guesses for the
parameters (line 1, above). The observed data are stored in the arrays X[] and Y[], while estimates of the standard
deviation for each Y[i] value is stored in Sigma[]. The variables FittedParams and k1,k2,k3 hold the
current best estimates and are provided for convenience of use.
The control variables and data are read with a function call:
InitFit(p,Guesses,X,Y,Sigma);
This function is supplied by the user, although it need not change from that supplied in SimFit_LM_Power.c.
Note that the top matter contains the function’s prototype. Here are the important segments of InitFit() function
(some lines have been omitted, line numbers do not apply to the source code lines):
1
2
3
4
InitFit()
void InitFit(int p[], double Guess[], double X[], double Y[], double Sigma[])
{
FILE *fcontrol;
/* declare a "file handle" for the file to read */
fcontrol=OpenFilePrompt("Enter name of estimation control file: ","r");
5
6
7
8
9
fscanf(fcontrol,"%*s%d%*[ˆ\n]c\n",&p[NDATA]);
/* number of data points */
fscanf(fcontrol,"%*s%d%*[ˆ\n]c\n",&p[NPARMS]); /* number of parameters */
fscanf(fcontrol,"%*s%d%*[ˆ\n]c\n",&p[NREPS]); /* number of reps per x */
fscanf(fcontrol,"%*s%d%*[ˆ\n]c\n",&p[NITER]); /* number of fitting iterations */
10
11
12
/* Parameter Guesses */
fscanf(fcontrol,"%*s%*[ˆ\n]c\n");
/* comment line before guesses */
13
14
15
16
17
/* read parameter Guesses, must be correct number */
for(i=0;i<p[NPARMS];i++){
fscanf(fcontrol,"%lf",&Guess[i]);
}
18
We use the usual function to open the control file (line 3, above). The four main control variables are read in lines 6 – 9.
To clarify and document the contents of the control file, in line 12 we scan and discard a 1-line comment. We follow this
scan with a loop that reads the initial guesses. Note in the included example control file (SimFit_LM_Power.ctrl
that these numbers need only be separated by white space; they need not be listed on separate lines, although that is
satsifactory.
After the parameters are read, the data is read from a loop following another 1-line comment:
fscanf(fcontrol,"%*s%*[ˆ\n]c\n"); /* comment line before data */
/* read data as X[i] Y[i] Sigma[i], must be correct number */
for(i=0;i<p[NDATA];i++){
fscanf(fcontrol,"%lf%lf%lf",&X[i],&Y[i],&Sigma[i]);
}
Note that 3 values (usually columns) are needed: the x data, the y data, and the standard deviation of the y values.
In the absence of knowledge of the latter value, we use 1.0. Again, the input values need only be separated by white
space, but the most readable format is to use 3 columns. After the data is read, we include code to print the values to
the terminal for verification.
7.7.3.2
main(): Initialize the GSL Functions
Following reading the control variables and data in main(), we continue by initializing the GSL functions:
GSL_InitializeFit_LM(p[NDATA],p[NPARMS],p[NREPS],Guesses,X,Y,Sigma,
MY_LM_FUNCTIONS);
This is a function provided as part of the SimPlot package and simplifies assigning values to a complex GSL data
structure. The arguments are self-explanatory, except MY_LM_FUNCTIONS which is a #define in SimMBS.h.
This macro is a list of three functions names that inform the GSL LM functions of the locations of the three usersupplied functions required by the LM algorithm (see below). This argument to GSL_InitializeFit_LM()
should never be changed.
The LM iteration to find the best parameters is done with a single function invocation (line 47:
67
status=GSL_IterateFit_LM(p[NITER]);
GSL_OutputFit_LM(FittedParams);
Followed by the results printed to the terminal (line 49). Each iteration of the LM shows the current parameter values
and the error between the data and function:
iter:
status
iter:
status
iter:
status
iter:
status
iter:
status
0, parms:
= success
1, parms:
= success
2, parms:
= success
3, parms:
= success
4, parms:
= success
1.00000000,
2.00000000,
3.00000000,
|f(x)| = 1239.49
2.91528250,
0.25459189,
3.06645500,
|f(x)| = 196.627
2.84247959,
0.28056892,
3.41034822,
|f(x)| = 103.58
2.89186099,
0.29412529,
3.29414303,
|f(x)| = 19.1359
2.80825751,
0.30510853,
3.26660325,
|f(x)| = 17.0898
The algorithm terminates either after it converges within tolerances or because it reached the maximum number of
iterations specified by the user and prints:
Parameter 0 = 2.79691 Error
Parameter 1 = 0.30599 Error
Parameter 2 = 3.26545 Error
ChiSquare=292.01 Degrees of
ChiSq/DF = 41.7157
= +/- 0.56462
= +/- 0.01780
= +/- 0.02774
Freedom (DF) = 7
which lists the final value of the parameters and an approximation of the standard deviation of the parameter estimte. The ChiSquare is an estimate of the goodness of fit between the data and the best parameters. Using the
ChiSquare value and its degrees of freedom, the probability that the null hypothesis is false can be determined from
a statistical table of chi-square probabilities. In this case, the null hypothesis is that there is no relation between the
data and the function and the probability is < 0.001 implying that the parameters produced a significantly good fit to
the data. Here we describe the former file.
7.7.4
Simplex
GSL also provides many function minimization routines. These can be used to find the independent axis values at
which the function is locally minimum. Since parameter estimation and calibration are minimization problems, these
GSL functions can also be used in those situations. The Nelder-Mead Simplex method is described in the textbook.
The files SimFit_Simplex_Exp.c and SimFit_Simplex_Linear.c are two examples of using this method
to estimate parameters.
As is usual in this CD, the code is accessible from the above webpage links and below, we describe the significant
features. It will be helpful to open the link and view the entire source code while reading the explications of code
segments.
7.7.4.1
main()
The top matter for the program SimFit_Simplex_Exp.c is standard for these programs, so is not explained again.
The local variables declared in main() are also similar to the Levenberg-Marquardt (LM) code described above:
1
2
3
4
5
6
7
double Guesses[MAXPARMS];
double Y[MAXDATA],
X[MAXDATA];
double Error;
double SimpStepSize;
int
p[MAXPARMS];
double FittedParams[MAXPARMS];
/*
/*
/*
/*
/*
/*
/*
initial guesses of parameters, big array */
y data */
x data */
stopping error */
min simplex vertex step size */
parameters to control this run */
the function parameters after fitting */
The only new variable is SimpStepSize (line 5) which represents the size of trial displacements of the simplex
vertices. Large values allow the algorithm to search large spaces rapidly, but may over-step the solution and may also
cause the algorithm to try illegal values (e.g., negative).
Initialization and iteration of the estimation run is similar to the LM code, but with different names.
68
1
InitFit_Simplex(p,&SimpStepSize,&Error,Guesses,X,Y);
2
3
4
GSL_InitializeFit_Simplex(p[NDATA],p[NPARMS],p[NREPS],Guesses,X,Y,
SimpStepSize, Error, MY_SIMPLEX_FUNCTION);
5
6
GSL_IterateFit_Simplex(p[NITER], FittedParams);
InitFit_Simplex() takes the additional argument &SimpStepSize. GSL_InitializeFit_Simplex()
is also identical except for the SimpStepSize and a different macro for passing the function to be minimized to the
GSL routines (MY_SIMPLEX_FUNCTION). GSL_IterateFit_Simplex() performs the minimization until the
accuracy condition is met or the allowed number of iterations is exceeded. The best estimates of the parameters are
returned in the FittedParams[] array.
The user must supply InitFit_Simplex(), but no new concepts are used there so the reader should consult
the similar function described in the LM method.
7.7.4.2
Plotting
Plotting arrays are initialized in main() when the fitting is completed. Two sets of arrays are initialized: one set for
the observed data and another set for the predictions based on the fitted parameters. These two sets use different arrays
for abscissa so that the predictions can be plotted at higher resolution than the data. That is, the data sets are usually
small with large distances between observations on the x-axis. The function, however, is usually continuous, so the
distance between x-axis values can be (and give a better appearance if they are) small. Creating the observation arrays
is a simple matter of copying the data read in the control file into the plotting arrays:
1
2
3
4
5
for(plotpts=0,j=0;j<p[NDATA];j++,plotpts++){
/* store data as floats for dislin */
Obsrvd_X[j]=X[j];
Obsrvd_Y[j]=Y[j];
}
The plotting prediction arrays are a little more complex. We must find the largest and smallest x values for data and
divide that into a large number of steps (2000 used in this example).
1
2
3
4
5
6
7
8
9
10
11
12
13
14
float firstx=X[0];
/* first X value */
float lastx=X[p[NDATA]-1];
/* last X value */
float delx=(lastx-firstx)/2000;
/* increment for 2000 predicted points */
float px=firstx;
/* current x value for prediction */
predpts=0;
double intcpt=FittedParams[INTCPT];
double shape=FittedParams[SHAPE];
while(px<lastx) {
/* Create predictions using same function as in MySimpFunction*/
Predict_X[predpts]=px;
Predict_Y[predpts]= (intcpt*exp(shape*px));
px += delx;
predpts++;
}
Lines 1 and 2 assign the extreme x values from the data and are used to determine the step size (line 3) used to
generate 2000 plotting points. We then loop over the prediction x values using the fitted parameters to generate
predicted y values (line 11), storing them in arrays as we go. The user-supplied function DoPlots() uses the
SimPlotManyX() function to plot these two curves that have different arrays of x values.
7.7.4.3
Function to Minimize
The GSL Simplex minimzation function requires that the user supply a function that accepts two arguments: an array
containing the current best estimate of the variable to be minimized and an array of parameters. These arguments are
defined this way because the primary use in GSL of these minimization routines is to find the minimum of functions
with known (user-supplied) parameters. However, we are using these routines in a different way: the variable to be
minimized in our problem is the set of parameters. The array of GSL “parameters” is not used in parameter estimation.
The function (which must be called MySimpFunction) is defined as:
69
double MySimpFunction(const gsl_vector *v, void *params)
where v is the address of a vector to be minimized. In our case, this is the vector of current best estimates for the
parameters. The variable params is the address of a vector that we do not use when using the GSL Simplex library
for nonlinear parameter estimation. But in order to satisfy the Simplex API (application programming interface), we
must include it in the list of the function’s formal arguments.
This purpose of this function is to loop over the data comparing the data values with the predicted values of an
exponential function using the current set of parameters that the simplex method has identified. The function computes
and returns to the GSL Simplex library the square of the differences between the data and the function. Because we
are not able to pass in all of the information necessary to perform this calculation, we have declared this information
to be global. The function local variables are:
180
181
182
183
int i;
double sumsq=0.0,
diff=0.0;
double shape,intcpt;
/* value to minimize: sum of squared differences */
Variables sumsq and diff retain the running sum of squared differences between data and function and the differences, respectively. Variables shape and intcpt are convenient names for the two parameters of the exponential
function:
y = IeSx
where I is intcpt and S is shape. The loop is:
189
190
191
192
for(i=0;i<p[NDATA];i++){
diff= (_MBS_fitd.DataY[i] - (intcpt*exp(shape*_MBS_fitd.DataX[i])));
sumsq += diff*diff;
}
where _MBS_fitd.DataX[i] is the global value of the ith x-axis data point. The final value of sumsq is returned
to the Simplex library and represents the error associated with one of the simplex’s vertices in the current iteration of
the algorithm.
7.7.5
Calibrating Models Using Simplex
Calibration is a form of parameter estimation that uses time series of the state variables as the observed data to fit. This
means that for each trial set of parameters, the model must be simulated so that a time series of model predictions can
be produced to compare with the data. In this instance, the Simplex function MySimpFunction() is complicated
and must perform significant preliminary variable initialization to correctly simulate the model. Below, we briefly
examine some of the new features of the Simplex fitting code.
7.7.5.1
Globals and main()
One of the examples fits parameters r and K from the logistic model to fake data created from another program that
simulated the logistic model using known parameter values. Thus, it is possible for a perfect fit.
Since we must invoke the simulation model (function Simulate()) from MySimpFunction() and we wish
to simplify as much as possible integration of our requirements with the the API of the GSL simplex minimization
functions, we choose to make the simulation control variables global. (In normal, non-fitting uses of the simulation
package, these variables were local to main(). The new global simulation variables are:
1
2
3
4
5
6
double Sp[NUMPARMS];
double Sy0[NUMVARS];
double Sy[NUMVARS];
float STMax;
float Sdt;
float Ssave_delt;
/*
/*
/*
/*
/*
/*
array of parameters */
array of intital conditions */
array of dynamic state variables */
End time of simulation */
DeltaT: time step of solution */
time between results save */
70
Note that the names are preceded by ‘S’ to remind us that these are not the usual simulation parameters. These names
can be altered by the user, but must match names used in main() and MySimpFunction().
The main() function now drives the Simplex iteration functions, which in turn calls the simulation model. So
the local variables in main() relate to simplex:
1
2
3
4
5
6
7
8
9
10
11
int main(void)
{
/* Simplex variables ... */
double Guesses[MAXPARMS];
double Y[MAXDATA],
X[MAXDATA];
double Error;
int
Cp[MAXPARMS];
double FittedParams[MAXPARMS];
double SimpStepSize;
int i;
/*
/*
/*
/*
/*
/*
/*
initial guesses of parameters, big array */
y data */
x data */
stopping error */
parameters to control this run */
the function parameters after fitting */
min simplex vertex step size */
These are the usual Simplex variables and the comments explain their meaning.
Since we wish to read the simulation model control variables from a disk file only once (not each time MySimpFunction()
calls Simulation(), that would slow execution), we read these variable once at the beginning and save the results
in global variables:
1
2
3
4
5
InitSystem(Sp,Sy,&STMax,&Sdt, &Ssave_delt);
/* save IC’s for future run */
for(i=0;i<NUMVARS;i++){
Sy0[i]=Sy[i];
}
Invoking the functions to initialize and run the Simplex code is as before:
1
2
/* read data and initial parameters to fit */
InitFit_Simplex(Cp,&SimpStepSize,&Error,Guesses,X,Y);
3
4
5
6
/* initialize the GSL L-M fitting system */
GSL_InitializeFit_Simplex(Cp[NDATA],Cp[NPARMS],Cp[NREPS],Guesses,X,Y,
SimpStepSize,Error, MY_SIMPLEX_FUNCTION);
7
8
9
/* do up to NITER iterations choosing parameters to minimize the differences */
GSL_IterateFit_Simplex(Cp[NITER], FittedParams);
7.7.5.2
MySimpFunction()
The function to evaluate the accuracy of simplex proposed parameters performs two tasks: run the simulation model
and compute the squared difference for each time value and state variable between the data and model prediction.
To run the model, we must recover the simulation control variables and substitute the new simplex-chosen parameters for the default values we initially read from the control file.
1
2
3
4
5
6
7
8
double MySimpFunction(const gsl_vector *v, void *params)
{
int i;
double Tsumsq=0.0,
/* total sum of squared difference: to be minimized */
sumsqV=0.0,
/* Victim SSQ */
sumsqP=0.0,
/* Predator SSQ */
diffV=0.0,
/* victim diff */
diffP=0.0;
/* predator diff */
9
10
11
12
float TMax;
float dt;
float save_delt;
/* End time of simulation */
/* DeltaT: time step of solution */
/* time between results save */
double p[NUMPARMS];
double y0[NUMVARS];
double y[NUMVARS];
/* array of parameters */
/* array of intital conditions */
/* array of dynamic state variables */
13
14
15
16
71
The latter 6 local variables are just those that we previously used in those non-fitted simulations, where we ran the
model from main(). Now they are used in this function that Simplex invokes. The other local variables are needed
for the data-model comparison.
Recover the original model parameters that are not being fit by Simplex as well as the current Simplex solutions.
Store in the usual p[] array.
1
2
3
4
5
6
/* get back original non-fitted parameters */
p[C]=Sp[C];
p[D]=Sp[D];
/* get current values of fitted parameters */
p[R] = gsl_vector_get(v, sR);
p[A] = gsl_vector_get(v, sA);
Most parameters of biological models are constrained to positive values. Constrained optimization is a seriously
difficult problem and the GSL Simplex minimization function does it by penalizing Simplex chosen parameter sets
that violate the constraints with large error terms. However, it still remains to provide new parameter sets for the
simplex vertices that are in the prohibited region. I have included two possible solutions: take the absolute value of
negative parameters (fabs()), or set negative parameter to 0. Use with extreme caution; examine Simplex parameter
dynamics carefully during the minimization process. Before using these lines, experiment with Simplex step-size and
initial guesses (see Hints, below). It is better to have slow convergence using small Simplex step sizes, than to have to
artificially alter parameter values.
1
2
3
4
5
/* check and correct negative parameters */
if(p[R]<0.0) p[R]=fabs(p[R]);
/*OR: p[R]=0.0;*/
gsl_vector_set(v,sR,p[R]);
/* not the best solution */
if(p[A]<0.0) p[A]=fabs(p[A]);
/*OR: p[A]=0.0;*/
gsl_vector_set(v,sA,p[A]);
Recover the original initial conditions:
1
2
3
for(i=0;i<NUMVARS;i++){
y[i]=Sy0[i];
}
And run the model in the usual way:
1
SIMULATION_BEGIN;
/* DO NOT REMOVE: creates ODE-solver variables */
2
3
4
5
6
7
8
/* Do Run 1 with original parameters */
/* save "plotpt" number of results in Time and Species */
PLOTTIME=Time;
PLOTVAR1=Prey;
PLOTVAR2=Predator;
PointsSavedp=&numpt;
9
10
11
TMax=STMax; dt=Sdt; save_delt=Ssave_delt;
Simulate((float)0,TMax,dt,p,y,save_delt);
12
13
SIMULATION_DONE;
/* DO NOT REMOVE: releases ODE-solver variables */
To evaluate the ability of the current parameters to match the data, compute the sum of squared differences. The
following lines assume that the time step between observations equals that of saved model values. If the observed time
step is constant, set the model control variable save delt to the same value. If the observations are not regularly
spaced in time, you’ll have to do some creative programming to only compare model output that is “close” in time to
the observations. For now, this problem is left as an exercise for the user, but future releases of MBS-CD will provide
this function.
1
2
3
4
5
6
7
for(i=0;i<numpt;i++){
diffV = (_MBS_fitd.DataY[i] - (Prey[i]));
diffP = (_MBS_fitd2.DataY[i] - (Predator[i]));
sumsqV += diffV*diffV;
sumsqP += diffP*diffP;
Tsumsq += diffV*diffV + diffP*diffP;
}
72
where numpt is the number of model solution points that saved during the simulation.
Finally, bad parameters can produce bad solutions (e.g., really big predicted numbers). This can result in bad sum
of squares. Another kludge to keep the minimization process going is to catch these bad numbers and reset the total
sum of squares to a valid but large number (signifying a bad Simplex parameter set).
if(gsl_isnan(Tsumsq) || gsl_isinf(Tsumsq)) {
Tsumsq=1e200;
printf("!!min_data(2): trial r,a =%f,%f. ssV=%e ssP=%e.
p[R],p[A],sumsqV,sumsqP,Tsumsq);
}
1
2
3
4
5
TSumsq=%e\n",
Again, as with all kludges, use with caution and examine the Simplex output carefully for bogus solutions.
7.7.6
Examples and Hints
Below are a few things to keep in mind.
1. In the current implementation, the occurrences of time values for data must be the same as the model output.
The data need not be regular, but there must be a model prediction at the same time as an observation. This will
be rectified in future versions.
2. As a first cut, set the Simplex step size to be twice the smallest initial parameter guess.
3. If the fit is poor, try decreasing or increasing the step size.
4. Small changes in initial guesses can have big effects on the quality of the fit.
• When trying different initial guesses, change one parameter at a time.
• Use graphical plots (model and data against time) to examine what is happening to the predicted state
variables.
• Focus on the behavior of model and data during early times in the simulation. E.g., if parameters cause the
predator to go extinct, start the prey with higher birth rates, or increase the predator attack rate.
• Either through plots or text files, examine the time course of parameter changes.
7.8
MBS Chapter 8: Model Validation
For the latest version of the MBS-CD consult the MBS-CDROM webpage.
7.8.1
Code Links
Windows users: clicking on the provided link will launch InternetExplorer if you have Acrobat
Reader 6.0 (other versions not tested). Linux users: if you use acroread version 5.0.6 or 5.0.10,
clicking on the link will work with Mozilla or Firefox provided the browser is already launched
somewhere in your workspace. If not, the launch may fail on the first try but will succeed if you click on
the link a second time with the browser open. Other users: leave this PDF file and manually copy the
code files to your local directories.
Here are the links to the example code and data sets. Explanations follow in later sections.
C Code
• Validation Test:
Click on this link: ValidationTestC
• Simulation Model Validation Template (same as above):
Click on this link: SimValidation TemplateC
• Validation of Model Results (no simulation model):
Click on this link: SimValidation BudyJackknifeC
• Luckinbill Microcosm Data:
Click on this link: Luckinbill Data Sets
73
7.8.2
Explanation
There are three main parts to code that statistically compares simulation model predictions to data: reading observed
data, running the model, and comparing models with data.
7.8.2.1
Data Import
Data can be read from the control file or, if the data set is not large, initialized in the code directly. In the former case,
one must re-write the InitSystem() function to pass local data arrays or use globals. In the latter case, one can
use C array initialization:
40
41
42
43
44
45
46
47
48
49
50
51
/* Array of Time values for validation data */
float X[]={
0.000000e+00,2.000000e+00,4.000000e+00,6.000000e+00,8.000000e+00,1.000000e+01,
1.200000e+01,1.400000e+01,1.600000e+01,1.800000e+01,2.000000e+01,2.200000e+01,
2.400000e+01,2.600000e+01,2.800000e+01
};
/* Array of validation data */
float Y[]={
1.000000e+01,1.873004e+01,3.545864e+01,6.358394e+01,8.644087e+01,9.317464e+01,
9.432439e+01,9.449688e+01,9.452222e+01,9.452593e+01,9.452647e+01,9.452656e+01,
9.452657e+01,9.452657e+01,9.452657e+01
};
7.8.2.2
Model Simulation
There are no new concepts with doing model simulation in the context of validation, except to ensure that time values
of data match those of model output. It is assumed that the model parameters have been estimated prior to doing
validation.
7.8.2.3
Model/Data Comparison
The bulk of validation is done in the SimPlot function SimValidate.c. It is convenient to wrap this function in
one written by the user (e.g., SimStats():
187
188
189
190
void SimStats(void)
{
/* next line is required */
NumRegressResults=0;
/* DO NOT REMOVE */
191
SimValidate(0,numpt,Y,Nplot,3,stdout);
192
/* ’stdout’= terminal, or to a file */
193
printf("\n JACKKNIFING BEGINS \n");
SimValidate_Jackknife(numpt,Y,Nplot,3,stdout);
194
195
196
}
SimValidate() requires the index (argument 1) in a matrix (RegressResults[][]) into which regression
results are stored. Multiple regressions can be performed in any given simulation model; these are maintained in
separate rows of RegressResults[][]. The second argument is the number of data and model points to compare.
Arguments 3 and 4 are the data and model predictions, respectively. AIC analysis requires the number of parameters
in the model; this number is provided in argument 5. Finally, the results can be written to the screen (stdout) or to
a file, which the user must open prior to calling SimValidate().
SimValidate Jackknife() basically does the same analyses as SimValidate(), but with one of the
data-model pairs removed. The validation statistics and indices are saved, and the process is repeated with a different
pair of points removed. Finally, the mean and standard deviations of the validation indices are printed.
7.8.3 SimValidate() Output
The output from the validation analysis is as follows.
74
REGRESSION----------n: 12
++Model++
Mean= 1.351687
Sum of x=16.220248
Sum of xˆ2=25.156438
++Data++
Mean= 1.184242
Sum of y=14.210901
Sum of yˆ2=20.524400
Sum of data_i * model_i=20.324428
Sum of Squares: Total = 3.695259e+00
Regress=3.851995e-01
Mean: Residual = 3.310060e-01
Intercept = 0.717581
Slope = 0.345243
r = 0.322865
Rˆ2 = 0.104242
1:1 F=2.601045
DF=(2,10)
Residual=3.310060e+00
This section gives basic quantities used in generating the 1:1 regression line. The quantities are defined in basic
statistics textbooks. The regression constants are given in lines 10 and 11 with the basic correlation coefficients (line
12). The F statistic to test for the signicance of a slope of 1.0 and an intercept of 0.0 is given in line 13 along with the
degrees of freedom needed for the test of significance.
The output also includes an abbreviated table of critical F values, a portion of which are listed below:
Critical Values for
P
5
6
0.10
3.78 3.46
0.05
5.79 5.14
0.01
13.3 10.9
1-tailed F distribution (2,Denom DF) at P=0.1, 0.05, 0.01
7
8
9
10
11
12
13
14
15
16
3.26 3.11 3.01 2.92 2.86 2.81 2.76 2.73 2.70 2.67
4.74 4.46 4.26 4.10 3.98 3.89 3.81 3.74 3.68 3.63
9.55 8.65 8.02 7.56 7.21 6.93 6.70 6.51 6.36 6.23
A second test statistic, the paired Student’s t, is also calculated and presented with a partial table of critical values:
PAIRED t TEST--------t (paired) = -0.259369
Mean Diff=-0.167446
SE Diff=0.645587
DF=11
Critical Values for 2-tailed t distribution at P=0.1, 0.05, 0.01
P
5
6
7
8
9
10
11
12
13
14
15
16
0.10
2.015 1.943 1.895 1.860 1.833 1.812 1.796 1.782 1.771 1.761 1.753 1.746
0.05
2.571 2.447 2.365 2.306 2.262 2.228 2.201 2.179 2.160 2.145 2.131 2.120
0.01
4.032 3.707 3.499 3.355 3.250 3.169 3.106 3.055 3.012 2.977 2.947 2.921
Next a number of indices, as described in the text, are calculated, followed by the the log-likelihood and AIC values:
INDICES-------Theil’s U = 0.234989
MSE = 0.419332
and
RMSE = 0.647558
MAE = 0.485290
MA%E = 51.1159
EF = -0.361740
MC=0.066864
SC=0.275332
RC=0.657804
Sum=1.000000
LIKELIHOODS-------ln(L) = 5.2145558e+00
AIC
= -2.4291117e+00
Finally, since the above single indices do not allow hypothesis tests for significance, a jackknife analysis is performed. This is done by removing a single observation and recomputing the indices. This is repeated until all observations have been removed (once). Descriptive statistics and confidence intervals are then computed and reported.
47
+++ JACKKNIFE RESULTS +++
48
49
50
51
52
53
Jackknife Stats-------MSE: JK N=11 mean=4.1607086e-01 bias=-3.2610037e-02 var=3.9693694e-03 SE=1.9923276e-01
95% CI_low = -2.7839660e-02 CI_up= 8.5998138e-01
AIC: JK N=11 mean=-9.1162759e-01 bias=1.5174841e+01 var=3.1858234e+00 SE=5.6443099e+00
95% CI_low = -1.3487714e+01 CI_up= 1.1664459e+01
75
54
55
56
57
58
59
60
61
lnLike: JK N=11 mean=4.4558138e+00 bias=-7.5874204e+00 var=7.9645585e-01 SE=2.8221549e+00
95% CI_low = -1.8322296e+00 CI_up= 1.0743857e+01
Rslope: JK N=11 mean=3.7433848e-01 bias=2.9095413e-01 var=3.3862828e-02 SE=5.8191776e-01
95% CI_low = -9.2223248e-01 CI_up= 1.6709094e+00
Rintercept: JK N=11 mean=6.8307604e-01 bias=-3.4505008e-01 var=4.1139112e-02 SE=6.4139779e-01
95% CI_low = -7.4602238e-01 CI_up= 2.1121744e+00
F: JK N=11 mean=3.1294533e+00 bias=5.2840842e+00 var=1.0520122e+01 SE=1.0256765e+01
95% CI_low = -1.9723644e+01 CI_up= 2.5982551e+01
7.9
MBS Chapter 9: Model Analysis
For the latest version of the MBS-CD consult the MBS-CDROM webpage.
7.9.1
Code Links
Here are the links to the example code and data sets. Explanations follow in later sections.
Windows users: clicking on the provided link will launch InternetExplorer if you have Acrobat
Reader 6.0 (other versions not tested). Linux users: if you use acroread version 5.0.6 or 5.0.10,
clicking on the link will work with Mozilla or Firefox provided the browser is already launched
somewhere in your workspace. If not, the launch may fail on the first try but will succeed if you click on
the link a second time with the browser open. Other users: leave this PDF file and manually copy the
code files to your local directories.
C Code
• Error Analysis - Island Biogeography:
Click on this link: ErrorAnalysisIslBiogC
• Nullclines:
Click on this link: Nullclines
7.9.2
Error Analysis
As described in the text, Monte Carlo error analysis produces a distribution of model predictions obtained by repeatedly
running the model with parameters selected from probability distributions. The program in the above link (Section
7.9) provides a function that puts the standard MBS Simulate() function in a loop and after the loop termination,
sorts the Monte Carlo results and prints quantiles of the user’s choosing.
For statistical analysis, the Monte Carlo must be saved and we continue our simplifying convention of using static
arrays whose dimensions are defined by the user to be large. So, in the program’s globle variable region, we need:
17
#define MAXMCREPS 1000
which will be used in the new function MonteCarloSimulate().
The new elements of the main() function are:
51
52
53
54
/* read parameters and Init.Cond.s */
InitSystem(p,y0,&TMax,&dt, &save_delt);
/* Simulate the system */
MonteCarloSimulate((float)0,TMax,dt,p,y0,save_delt);
The new function MonteCarloSimulate() uses the same arguments as the usual Simulate().
The important local variables of MonteCarloSimulate() are:
96
97
98
float MCresult[MAXT][MAXMCREPS];
double Original_p[NUMPARMS];
double y[NUMVARS];
/* save MC results: 2d array: [row=time x col=MCreps] */
/* original parameters */
/* original initital conditions */
76
MCresult[MAXT][MAXMCREPS] is a two-dimensional array which contains model predictions for each time
(rows) and Monte Carlo run (columns). Since the Monte Carlo loop will repeatedly alter the parameters and initial
conditions, we need to create variables to save the original values (lines 97 and 98).
The loop to repeatedly call the model Simulate() function begins:
117
118
119
for(j=0;j<(int)p[NUMREPS];j++){
/* extinction slope is a log-normal deviate */
p[EXoP]=gsl_ran_lognormal(r,log(Original_p[EXoP]),0.5*log(Original_p[EXoP]));
Line 119 draws a value for the extinction parameter from a log-normal distribution. The user will determine the
number and identity of the parameters to alter as well as the probability distribution to use.
The model Simulate() function is called in the normal manner, followed by saving the predictions of interest
in the Monte Carlo results array:
128
129
130
131
132
Simulate(start_t,stop_t,dt,p,y,save_delt);
/* save results */
for(i=0;i<numpt;i++){
MCresult[i][j]=Species[i];
}
The user will determine which model output to save for statistical analysis. If more than one variable needs to be
saved, then additional MCresult[][] arrays must be defined as local variables.
Following the completion of all Monte Carlo runs, each row of the 2D results array are sorted in place using a GSL
function.
139
140
141
for(i=0;i<numpt;i++){
gsl_sort_float(&MCresult[i][0],1,(int)p[NUMREPS]);
}
Finally, any quantiles of interest (e.g. 0.50 for the median or 0.025 and 0.975, for 95% confidence intervals) can be
obtained with another GSL function:
152
153
154
155
for(i=0;i<numpt;i++){
Species[i]=gsl_stats_float_quantile_from_sorted_data(&(MCresult[i][0]),1,(int)p[NUMREPS],0.5);
SpLow[i]=gsl_stats_float_quantile_from_sorted_data(&(MCresult[i][0]),1,(int)p[NUMREPS],0.1);
SpHigh[i]=gsl_stats_float_quantile_from_sorted_data(&(MCresult[i][0]),1,(int)p[NUMREPS],0.9);
}
By putting these calls into a loop, we obtain the quantiles for each of the simulation times. We store these in global
arrays for plotting:
Island Biogeography Error Analysis
0.1Q
Species
0.9Q
20.00
15.00
Species
151
10.00
5.00
0.00
0.00
2.00
4.00
6.00
8.00
10.00 12.00 14.00 16.00 18.00
Time
77
7.9.3
Nullclines
As defined in the text, nullclines for a state variable are the locus of points in phase space at which the rate of
change of the state variable is zero. The code presented in the link at the top of this section (Section 7.9) calls a
function in the SimPlot package that numerically finds the location of two nullclines for a two state variable model.
The model is specified in the deriv() function defined in the usual way for MBS simulation. The user invokes
only a single function SimMakeNullclines(). For a set of points in phase space (axes composed of the state
variables), this function evaluates the derivatives of the model. From these derivatives, the nullclines are determined
from the points where the sign of the derivatives change (from positive to negative, or from negative to positive). The
points of derivative sign change are written to global arrays and plotted using the MBS SimPlot plotting function
SimPlotManyX(). The user must provide several pieces of data (variables) for the nullcline functions.
31
32
33
float preyNCprey[MAXT],preyNCpred[MAXT],
predNCprey[MAXT],predNCpred[MAXT];
int preynumNCpts,prednumNCpts;
/* prey nullcline, x,y-axis values */
/* pred nullcline, x,y-axis values */
/* number of points for plotting */
First, the plotting arrays: four are required, two for each state variable. For the nullcline of state variable 1 an array
of the x-axis (representing state variable 1) values (preyNCprey[]) and the associated y-axis values (representing
state variable 2, or the ‘predator’ to give it name) in array preyNCpred[]. The second state variable (has a similar
pair of arrays for its nullcline (predNCprey[] and predNCpred[]). The number of points found for plotting
each nullcline are contained in the global variables preynumNCpts and prednumNCpts. Of course, the user is
free to substitute any names he or she wishes for these 4 arrays and two variables.
In addition, the user must define the grid of phase space points over which to search for the nullclines. These six
variables are local to the main() function and can have any names. In the example code, they are:
45
46
47
int numsv1,numsv2;
double begsv1,endsv1,
begsv2,endsv2;
/* num of sv1,sv2 points to evaluate */
/* beg, end of sv1 grid region to test */
/* beg, end of sv2 grid region to test */
The number of grid points along the two phase space dimensions are numsv1 and numsv2 for the x-axis and yaxis, respectively. The number of points evaluated by the nullcline function is the product of these two values. The
rectangular region to search is defined by the beginning and end points along the two axes (begsv1, endsv1,
begsv2, endsv2, respectively).
For simplicity, these values are assigned directly in the main() function, but for ease of searching the space, a
small user prompt can be added:
62
63
64
65
66
67
68
69
70
71
72
73
/* ask if user wants to change grid region searched for nullclines */
printf("\nCurrent nullcline grid ranges: Xaxis=[%.2f - %.2f] Yaxis=[%.2f - %.2f].
begsv1,endsv1,begsv2,endsv2);
scanf("%c",ans);
if(ans[0]==’Y’ || ans[0]==’y’) {
printf("Enter X axis range: (eg: 10.0 20.0) ");
scanf("%lf%lf",&begsv1,&endsv1);
printf("Enter Y axis range: (eg: 10.0 20.0) ");
scanf("%lf%lf%*c",&begsv2,&endsv2);
printf("Enter number of grid points (X and Y) (eg: 100 200): ");
scanf("%d%d%*c",&numsv1,&numsv2);
}
Override (Y/N)? [N]",
Simply pressing the Enter key on the first prompt causes the program to use the coded (default) values. These few
lines of code are useful when initially exploring phase space when the location of the nullclines are not known. In this
situation, the user would code a large, coarse region, and then, after the plots revealed the crudely drawn nullclines,
more appropriate corners of the region and a larger number of grid points can be entered at the prompt.
The nullcline function that does the work is invoked as:
47
48
49
NCstatus = SimMakeNullclines(numsv1,begsv1,endsv1,numsv2,begsv2,endsv2, p,
&preynumNCpts,preyNCprey,preyNCpred,
&prednumNCpts,predNCprey,predNCpred);
with the following arguments:
78
1. numsv1: number of grid points for state variable 1 (x-axis)
2. begsv1: beginning value of grid region along x-axis (state variable 1)
3. endsv1: ending value of grid region along x-axis (state variable 1)
4. numsv2: number of grid points for state variable 2 (y-axis)
5. begsv2: beginning value of grid region along y-axis (state variable 2)
6. endsv2: ending value of grid region along y-axis (state variable 2)
7. p: the parameters of the model as read from the model control file
8. &preynumNCpts: the address of number of plotting points saved for state variable 1
9. preyNCprey: the array of x-axis values for state variable 1
10. preyNCpred: the array of y-axis values for state variable 1
11. &prednumNCpts: the address of number of plotting points saved for state variable 1
12. predNCprey: the array of x-axis values for state variable 1
13. predNCpred: the array of y-axis values for state variable 1
The function returns a status flag of 0 if the function found enough nullcline points to plot (i.e., more than 1) and a flag
of 1 if it found no points. The user can use this flag to control subsequent program flow.
Here is an example of the plots:
Nullclines
100.0
Predator
80.0
60.0
V=0
P=0
40.0
20.0
0.0
0.0
100.0
200.0
300.0
400.0
500.0
600.0
Victims
7.10
MBS Chapter 10: Stochastic Models
7.10.1
Code Links
Here are the links to the example code. Consult the textbook for explanations.
Windows users: clicking on the provided link will launch InternetExplorer if you have Acrobat
Reader 6.0 (other versions not tested). Linux users: if you use acroread version 5.0.6 or 5.0.10,
clicking on the link will work with Mozilla or Firefox provided the browser is already launched
somewhere in your workspace. If not, the launch may fail on the first try but will succeed if you click on
the link a second time with the browser open. Other users: leave this PDF file and manually copy the
code files to your local directories.
C Code
79
• Generate and Plot Uniform Deviates:
Click on this link: SimRandomNum
• Generate and Plot Deviates from Probability Distributions:
Click on this link: SimRandomGen
• Generate and Plot Deviates from a Triangular Distribution:
Click on this link: SimRandom-triangular
• Sample from an Empirical Distribution:
Click on this link: SimRandomTemp
• Simulate Random Logistic Population Dynamics:
Click on this link: SimRandomPop
7.10.2
Explanation
The key to analyzing stochastic models is to perform Monte Carlo simulations, followed by visual and statistical
analysis of the frequency distributions produced by the many independent simulations. Quantile analysis for statistical
analysis is described in subsection 7.9.2. An important visualization tool in this regard is histograms. The creation
and plotting of histograms is described in subsection 5.3.6.
7.11
MBS Chapter 11: Photosynthesis and Plant Growth
For the latest version of the MBS-CD consult the MBS-CDROM webpage.
7.12
MBS Chapter 12: Hormonal Control in Mammals
For the latest version of the MBS-CD consult the MBS-CDROM webpage.
7.13
MBS Chapter 13: Populations and Individuals
7.13.1
Code Links
Windows users: clicking on the provided link will launch InternetExplorer if you have Acrobat
Reader 6.0 (other versions not tested). Linux users: if you use acroread version 5.0.6 or 5.0.10,
clicking on the link will work with Mozilla or Firefox provided the browser is already launched
somewhere in your workspace. If not, the launch may fail on the first try but will succeed if you click on
the link a second time with the browser open. Other users: leave this PDF file and manually copy the
code files to your local directories.
C Code
• Age Structured Population Model: Click on this link: SimAgeStructure.
• Basic Chemostat Model: Click on this link: Basic Chemostat: Michaelis-Menten.
• Monod Chemostat Competition Model: Click on this link: Monod Competition.
• Individual-based Models: Click on this link: Fish Growth IBM.
• Spruce-Budworm Model and Nullcline: Click on this link: Spruce-Budworm.
Octave Code
• Age Structured Population Model (Matrix Multiplication): Click on this link: SimAgeStructure-Octave-Matrix.
• Age Structured Population Model (Finite Difference Updating): Click on this link: SimAgeStructure-OctaveFDE.
80
• Basic Chemostat Model: Click on this link: Basic Chemostat-Octave.
• Monod Chemostat Competition Model: Click on this link: Monod Competition-Octave.
• Spruce-Budworm Model: Click on this link: Spruce-Budworm-Octave.
Matlab Code
• Age Structured Population Model (Matrix Multiplication): Click on this link: SimAgeStructure-Matlab-Matrix.
• Age Structured Population Model (Finite Difference Updating): Click on this link: SimAgeStructure-MatlabFDE.
• Basic Chemostat Model: Click on this link: Basic Chemostat-Matlab.
• Monod Chemostat Competition Model: Click on this link: Monod Competition-Matlab.
• Spruce-Budworm Model: Click on this link: Spruce-Budworm-Matlab.
7.13.2
Population-level Models
No new coding concepts here.
7.13.3
Individual-based Models
IBMs use dynamic allocation and a new coding construct called linked-lists extensively. Here is a brief introduction.
7.13.4
Linked Lists
Static arrays are undesirable for IBMs because (1) they are static and defined at compile-time, not run-time. Changing
parameters in a run might require a larger array than declared, thereby causing a crash and the need to re-compile.
Compiling is a bad thing; general, adaptive programs is a good thing. This is especially important when we model
birth/death processes, when the population (number of individuals) can grow to large values. (2) Individuals that die
are still “in” the population array; only their state has been changed to indicate they are dead. This means that when
processing living individuals we must always search the entire array for a living individual, regardless of how many
there are. The worst case scenario is a population of one individual that happens (due to lots of births and then lots of
death) to be in the last location of the array. We could fix this by “compacting” the array after each simulation iteration
so that all living individuals are at the beginning of the array, but that is considerable programming and execution
overhead. Linked lists are the solution.
An array is a glob of elemental data types (e.g., integers or struct) that reside in contiguous memory locations.
Being contiguous, if one knows the starting location of the array and the size of each element, one can easily find any
other element. That is a good thing and very fast. A linked list is a glob of elemental data types each of which resides
anywhere in memory. To make it a “glob” (as opposed to just isolated data) we need a mechanism to allow them to
interact as a single entity. In other words, we need a mechanism that allows us to address the data and change its
contents for each of the items in the glob. This is done using our favorite C construct: the dreaded pointer.
The simplest linked list is a singly linked list. It consists of the data and a pointer to the next item in the list. The
next figure shows this graphically.
*head
Item
Data
Item
Data
Item
Data
*Next
*Next
*Next
*NULL
As with arrays, we must know where the list starts (the head pointer). And because there is no concept of the size of
the list (it’s content is dynamically altered), we must signify the end of the list (the NULL pointer which points to no
address). The linkage is created by maintaining the address of the next item in the list as part of the information of
each item.
81
While this is a useful structure, we can only go through the list from the head to the end. We can not traverse the
list from the end to the beginning. The next most complex list is the double linked list. This is the structure we’ll work
with; graphically it is:
*head
*NULL
Item
Data
Item
Data
Item
Data
*Prev
*Next
*Prev
*Next
*Prev
*Next
*NULL
We must maintain more information (more pointers), but it simplifies other aspects of the coding.
To avoid the limitations of static arrays, we wish to dynamically add (births) and delete (deaths) items from the
list. To delete an item, we must: (1) maintain the correct linkages by manipulating the pointers, and (2) release the
computer system memory that we allocated when we created the item. Graphically pointer manipulation for deletion
is:
*head
Item
Data
*NULL
*Prev
*Next
Item
Data
Item
Data
*Prev
*Next
*NULL
*Prev
*Next
Adding an item is the reverse: we must create the item in memory (allocation) and we must add prev and next
linkages.
Linked lists are fundamental to modern computer algorithms. The next most complicated concept is the circular
doubly linked list: the last item’s next pointer points to the head. Lists are also used to describe trees.
7.13.5
Coding Doubly Linked Lists
The program tlinklist2.c is a simple program that creates a linked list of the objects we used in ibmpop-01.c.
The complete code is at the end of this document and significant bits are explicated here.
7.13.5.1
Stuctures
We use the previous individual structure for an item’s data. This will become more complicated in later programs.
6
7
8
typedef struct AOne {
int state;
} OBJ;
/* dead or alive or whatever*/
The list is a collection of items, each of which contains the individual’s structure and the addresses.
11
12
13
14
15
typedef struct list_elem {
OBJ org;
struct list_elem *next;
struct list_elem *prev;
} ITEM;
/* organism in list */
Note the basic self-referential aspect of this structure. The structure is a list element (list elem), but it contains
addresses to other instances of itself (*next).
Prototypes are needed to declare the functions’ structures to the compiler; we’ll hide these in SimMBS.h later.
82
17
18
19
20
21
/* prototypes */
void NewItem(ITEM **head);
int DeleteItem(ITEM **head, ITEM *curr);
int DeleteList(ITEM **head);
void PrintList(ITEM *head);
7.13.5.2
23
24
25
main()
int main() {
ITEM *curr, *head, *prev;
int i;
/* local pointers to list items */
26
27
head = NULL;
/* the list is empty */
The primary local variables are pointers to items. Remember: ITEM *curr means that the contents of curr (i.e.,
*curr) is an ITEM. This implies that curr itself is the address of an ITEM.
To create an entire list of some number of items, we begin with an empty list and create items as we go. This is
done in the function NewItem():
29
30
31
32
33
for(i=0;i<10;i++){
NewItem(&head);
}
/* print info about each item */
PrintList(head);
This function adds all new items at the head of the list. As a sanity check, we print the entire list and all the addresses.
To illustrate deletion, let’s delete the organism with state 5.
36
37
38
39
40
41
42
43
44
curr=head;
while(curr) {
if( (curr->org).state == 5) {
DeleteItem(&head,curr);
break;
}
curr = curr->next;
}
PrintList(head);
Line 36 starts the walk to find the item with state 5. while(curr) is short-hand for while(curr!=NULL,
i.e., continue searching as long as we are not at the end of the list. In line 38 we test for the state of the item.
(curr->org) refers to the data that resides in the contents of address curr. We use -> to refer to an element of a structure when we use the address of the structure, not the structure itself. This is a synonym for
(*curr).org. But since curr->org is an actual piece of data (a structure), the state is obtained using the period
(curr->org.state).
The break command in line 40 terminates the walk through the list; implying that we will delete only the first
one we encounter. In future programs there may be several organisms in the same state.
It is important to free all memory we have allocated before exiting the program. Therefore, we use a single function
to delete the entire list.
47
DeleteList(&head);
7.13.5.3
NewItem()
First, we attempt to allocate memory for the new item:
47
48
49
50
51
if((newI=(ITEM *)malloc(sizeof(ITEM)))==NULL) {
printf("NewItem(): failed to malloc. Head =%p\n",head);
/* fatal: bail */
exit(0);
}
83
It is essential to check that the built-in function malloc() was able to find sufficient memory. If not, subsequent
attempts to refer to this memory will proceed, but incorrectly. malloc() returns a non-existent memory location
(NULL) when it fails.
Given that the memory was allocated, we intialize the item by assigning an integer to the state.
74
75
76
77
78
79
if(*head!=NULL) {
newI->org.state = (*head)->org.state +1;
}
else {
newI->org.state = 1;
}
If this is the first item to be allocated, then we give it state 1, otherwise, we increment the state of the last added item.
This initialization should be done in a separate function.
Pointers are assigned next
80
81
82
83
newI->next = (*head);
newI->prev = NULL;
if(*head) (*head)->prev = newI;
(*head) = newI;
The new next points to the (old) head: add the item at the head of the list. The new item, being the head, does not have
a previous address. But the (old) head now has a previous address, and that is the address of the new item. Finally, we
have a new head (newI.
7.13.5.4
DeleteItem()
If we want to delete the head of the list then the new head is the (old) head’s next.
99
100
if( ! curr->prev)
(*head) = curr->next;
In line 99, if the current item has a previous address, curr->prev will have a nonNULL value. In C, the exclamation
point is logical negation in which a zero (e.g., NULL) value is converted to non-zero, and a zero value is converted to
non-zero. Thus, !curr->prev is true if curr->prev==NULL.
If the current item has a previous address defined, then the item to which that address points must have its next
address set to the next address of current (to-be-deleted) item, as follows.
102
103
104
if((curr->prev)) {
(curr->prev)->next = curr->next;
}
Similarly, for the current item’s previous address: it must be set to be the previous address of the next item in the list:
106
107
108
if((curr->next)) {
(curr->next)->prev = curr->prev;
}
Finally, after all these pointers have been shuffled, the actual memory of the current item is still associated with that
item. So, we free it and return success:
109
110
free(curr);
return (1);
/* free the item’s memory */
/* clean delete: good return */
Deleting the entire list is easier, since we don’t have to rearrange all the pointers.
125
126
127
128
129
130
131
curr=(*head);
(*head) = NULL;
while(curr) {
tmp=curr->next;
free(curr);
curr=tmp;
}
/* can’t lose this when we free the memory */
/* free the item’s memory */
84
Line 125 locates the head; line 126 wipes out the head of the list, but the memory is still allocated. Walking through the
list, we save the location of the next item (line 128, so we can continue after we free the memory of the current item.
After the save, we can re-set the pointer to next current item (line 130)and continue deleting until curr==NULL.
7.13.5.5
Printing
There are no new concepts in printing the list: initialize a pointer to the head, print stuff, and continue through the list
until the current pointer equals NULL. However, examining the set of addresses that are stored is useful. Below is one
example output; the numers are in hexadecimal notation (base 16, 0. . . 9A. . . F). Even without being able to translate
to decimal notation, small digits and low alphabetical characters signify small numbers.
1
2
3
4
5
6
Item
Item
Item
Item
Item
Item
#=0
#=1
#=2
#=3
#=4
#=5
currAddr=0x8049c38
currAddr=0x8049c28
currAddr=0x8049c18
currAddr=0x8049c08
currAddr=0x8049bf8
currAddr=0x8049be8
currPrevAddr=(nil) currNextAddr=0x8049c28 currState=10
currPrevAddr=0x8049c38 currNextAddr=0x8049c18 currState=9
currPrevAddr=0x8049c28 currNextAddr=0x8049c08 currState=8
currPrevAddr=0x8049c18 currNextAddr=0x8049bf8 currState=7
currPrevAddr=0x8049c08 currNextAddr=0x8049be8 currState=6
currPrevAddr=0x8049bf8 currNextAddr=0x8049bd8 currState=5
Item 0 is the head and it has a (nil) (NULL) address. It’s next address is the address of item 1. Item 1’s previous
address is in the head’s address. And so on, through the list.
7.13.5.6
1
2
3
Complete Code
/* tlinklist2.c -- doubly linked list */
#include<stdlib.h>
#include<stdio.h>
4
5
6
7
8
/* structure for an individual */
typedef struct AOne {
int state;
/* dead or alive or whatever*/
} OBJ;
9
10
11
12
13
14
15
/* structure for a doubly linked list of individuals */
typedef struct list_elem {
OBJ org;
/* organism in list */
struct list_elem *next;
struct list_elem *prev;
} ITEM;
16
17
18
19
20
21
/* prototypes */
void NewItem(ITEM **head);
int DeleteItem(ITEM **head, ITEM *curr);
int DeleteList(ITEM **head);
void PrintList(ITEM *head);
22
23
24
25
int main() {
ITEM *curr, *head, *prev;
int i;
/* local pointers to list items */
26
27
28
29
30
31
32
33
head = NULL;
/* the list is empty */
/* create a list of 10 items */
for(i=0;i<10;i++){
NewItem(&head);
}
/* print info about each item */
PrintList(head);
34
35
36
37
38
39
40
41
42
/* delete the first item with state=5 */
curr=head;
while(curr) {
if(curr->org.state == 5) {
DeleteItem(&head,curr);
break;
}
curr = curr->next;
85
}
PrintList(head);
43
44
45
/* before exiting the program, we must free all memory */
DeleteList(&head);
PrintList(head);
/* should be empty */
46
47
48
49
/* try to delete a non-existent list */
DeleteList(&head);
/* should complain */
50
51
52
return 0;
53
54
}
55
56
57
58
59
60
/*
NewItem -- add need ITEM to head of list
*/
void NewItem(ITEM **head)
{
61
ITEM *newI;
62
63
/* allocate memory for an item */
if((newI=(ITEM *)malloc(sizeof(ITEM)))==NULL) {
printf("NewItem(): failed to malloc. Head =%p\n",head);
/* fatal: bail */
exit(0);
}
64
65
66
67
68
69
70
/* the ’state’ is just one more than the last state */
/* later we’ll make a separate function to initialize
organisms */
if(*head!=NULL) {
newI->org.state = (*head)->org.state +1;
}
else {
newI->org.state = 1;
}
newI->next = (*head);
newI->prev = NULL;
if(*head) (*head)->prev = newI;
(*head) = newI;
71
72
73
74
75
76
77
78
79
80
81
82
83
84
}
85
86
87
88
89
90
/*
DeleteItem -- delete an item given its address
*/
int DeleteItem(ITEM **head, ITEM *curr)
{
91
92
93
94
95
96
/* warning */
if ((*head) == NULL) {
fprintf(stderr, "DeleteItem(): Attempt to delete from empty list!\n");
return (0);
}
97
98
99
100
101
102
103
104
105
106
107
108
109
110
/* check if current item is head */
if( ! curr->prev)
(*head) = curr->next;
/* previous item’s next points to deleted’s next */
if((curr->prev)) {
(curr->prev)->next = curr->next;
}
/* next item’s previous points to deleted’s previous */
if((curr->next)) {
(curr->next)->prev = curr->prev;
}
free(curr);
/* free the item’s memory */
return (1);
/* clean delete: good return */
86
111
}
112
113
114
115
int DeleteList(ITEM **head)
{
ITEM *curr, *tmp;
116
/* warning */
if ((*head) == NULL) {
fprintf(stderr, "DeleteList(): Attempt to delete from empty list!\n");
return (0);
}
117
118
119
120
121
122
/* walk through the list, deleting/freeing as we go */
curr=(*head);
(*head) = NULL;
while(curr) {
tmp=curr->next;
/* can’t lose this when we free the memory */
free(curr);
/* free the item’s memory */
curr=tmp;
}
return(1);
123
124
125
126
127
128
129
130
131
132
}
133
134
135
136
137
void PrintList(ITEM *head)
{
int i;
ITEM *curr = head;
138
printf("Start List Print:\n");
i=0;
/* walk through the list printing stuff */
while(curr) {
printf("Item #=%d currAddr=%p currPrevAddr=%p currNextAddr=%p currState=%d\n",
i,curr,curr->prev,curr->next,curr->org.state);
curr = curr->next ;
i++;
}
printf("End List Print.\n");
139
140
141
142
143
144
145
146
147
148
149
}
7.14
MBS Chapter 14: Chemostats
7.14.1
Code Links
Windows users: clicking on the provided link will launch InternetExplorer if you have Acrobat
Reader 6.0 (other versions not tested). Linux users: if you use acroread version 5.0.6 or 5.0.10,
clicking on the link will work with Mozilla or Firefox provided the browser is already launched
somewhere in your workspace. If not, the launch may fail on the first try but will succeed if you click on
the link a second time with the browser open. Other users: leave this PDF file and manually copy the
code files to your local directories.
C Code
• Simple Chemostat Mode with Michael-Menten Nutrient Uptake: Click on this link: Chemostat Model.
• Monod Growth with Two Competitors: Click on this link: Monod Competition.
Octave Code
• Basic Chemostat Model: Click on this link: Basic Chemostat-Octave.
• Monod Chemostat Competition Model: Click on this link: Monod Competition-Octave.
Matlab Code
• Basic Chemostat Model: Click on this link: Basic Chemostat-Octave.
87
• Monod Chemostat Competition Model: Click on this link: Monod Competition-Octave.
The Monod model is intended to duplicate the results of Grover (1990) as described in the textbook (Section
14.2.2). However, the code, while giving approximately similar results, does not quantitatively duplicate the published
results using published parameter values. Nevertheless, an interesting exercise is to run the model with T = 4.0 and
T = 8.0.
7.15
MBS Chapter 15: Diseases
7.15.1
Code Links
Windows users: clicking on the provided link will launch InternetExplorer if you have Acrobat
Reader 6.0 (other versions not tested). Linux users: if you use acroread version 5.0.6 or 5.0.10,
clicking on the link will work with Mozilla or Firefox provided the browser is already launched
somewhere in your workspace. If not, the launch may fail on the first try but will succeed if you click on
the link a second time with the browser open. Other users: leave this PDF file and manually copy the
code files to your local directories.
C Code
• SIR Bombay Model: Click on this link: SimSIR-Bombay.
• SIR Theory: Click on this link: SimSIR-Theory.
• SIR Validation: English Boarding School: Click on this link: SimSIR-valid.
• sIC-AIDS: Click on this link: sIC-Aids.
There are no new coding ideas in these programs. sIC AIDS.c shows an ODE with a relatively large number of state
variables (12) and parameters (12);
7.16
MBS Chapter 16: Spatial Patterns and Processes
There is no code associated with this chapter.
7.17
MBS Chapter 17: Scaling Models
There is no code associated with this chapter.
7.18
MBS Chapter 18: Chaos in Biology
For the latest version of the MBS-CD consult the MBS-CDROM webpage.
7.19
MBS Chapter 19: Cellular Automata and Recursive Growth
7.19.1
Code Links
Windows users: clicking on the provided link will launch InternetExplorer if you have Acrobat
Reader 6.0 (other versions not tested). Linux users: if you use acroread version 5.0.6 or 5.0.10,
clicking on the link will work with Mozilla or Firefox provided the browser is already launched
somewhere in your workspace. If not, the launch may fail on the first try but will succeed if you click on
the link a second time with the browser open. Other users: leave this PDF file and manually copy the
code files to your local directories.
C Code
88
• Plant Competition Cellular Automaton Model:
Click on this link: SimCAPlant
• Heart Fibrillation Cellular Automaton Model:
Click on this link: SimCAHeart
7.19.2
Data Structures
A cellular automaton (CA) is a grid of cells in which each cell is in one of a finite number of states. Prescriptive rules
define the change of a cell from one state to another. The rules are usually probabilistic and usually based on the state
of a cell’s neighbors. CA dynamics are typically visualized by graphing a color-coded version of the grid over time in
which the state of each cell is represented with a unique color. A moderately general data structure for a CA is:
1
2
3
4
5
6
/* structure for one cell in the grid */
typedef struct acell{
int state;
/* state of the cell */
int stime;
/* time the cell has been in that state */
int ap;
/* random duration of absolute refractory period */
} CAcell;
7
8
9
10
11
12
13
14
15
16
17
18
/* structure for the entire grid */
typedef struct agrid {
int grid_row, grid_col;
/* rows/columns of the interior, simulated grid*/
int row_border, col_border; /* number of top/bottom and left/right borders of grid*/
int gr,gc;
/* rows/columns of whole grid (sim.lengths+borders)*/
int srow,erow,scol,ecol;
/* indices of start and end rows(cols) of sim. grid */
CAcell *gd;
/* address of dynamically allocated grid (the cells) */
int StateColors[NUMSTATES]; /* plotting colors for each state */
int simtime;
/* current simulation time: useful for printing, etc */
float ecg;
/* ecg at time of snapshot */
} CAgrid;
A grid has two components: a descriptor of the geometry of the grid, and a 2D array of cells with various properties.
The grid structure (lines 9 – 18 is cast as a new type (typedef called CAgrid. The grid has two type of cells,
those on the border which do not participate in the simulation and the interior cells whose states are updated. The
organization of the grid that this structure depicts is:
++++++++++++
++cccccccc++
++cccccccc++
++cccccccc++
++cccccccc++
++cccccccc++
++++++++++++
where the border cells are represented by ‘+’ and the active, simulated cells are represented by ‘c’. The row and column
variables in the structure above describe this arrangement of cells. An array representing a mapping from grid cell state
to displayed color is stored with the grid (StateColors[]). Other properties of the grid, that are not possessed by
individual cells are also stored in the grid structure. In the above example, simtime and ecg are appropriate to the
fibrillating heart model. Finally, the actual 2D array of cells is stored as a pointer to dynamically allocated memory
(CAcell *gd), where CAcell is another structure type. In this example, each cell is characterized by its state, the
amount of time the cell has been in its current state, and a parameter of the dynamics: a random duration of absolute
refractory period (ap).
7.19.3
Swapping Grids
Simuating a CA involves looping over time and the rows and columns of the cells changing states according to the
rules. This raises the problem of whether the updating should be synchronous or asynchronous. The usual approach
is synchronous, where all the changes to the states of all the cells are recorded in another grid before the real grid is
changed. In this way, the cells are all updated simultaneously (synchronously) and the order in which the code loops
through the grid does not affect the outcome.
89
Synchronous updating requires two copies of the grid. The worst method to manipulating these copies is to use a
nested for() loop to copy the current copy to a second grid, make the changes for the next time step in the new copy,
then use another nested for() loop to copy the new grid into the current grid. The second worst method is to do the
same, but use C built-in functions memcpy(), in place of the nested for() loops. The best approach is to use the
magic of pointers. Here’s a sketch of the approach used in the CA simulations linked above.
1. Dynamically allocate two grids to two grid pointers
2. Declare two more pointers (old and new) to the grids.
3. Use old to access the state of the current grid and to use as input to the CA rules.
4. Write the state changes into the grid referenced by new. E.g.,
if(old->cellp.state == FOO) new->cellp.state = BAR
5. After all cells have been changed, swap the pointers:
CAcell tmp = old;
old = new;
new = tmp;
Bob’s your uncle!
7.20
MBS Chapter 20: Evolutionary Computation
For the latest version of the MBS-CD consult the MBS-CDROM webpage.
90
Chapter 8
Plotting and Analysis Code
Summary:Following a table of contents, this chapter provides links to the SimPlot C library of modeling
functions. These include plotting, validation, parameter estimation, and so. Other than comments in the code, there is
not yet a great deal of documentation for this library. The most current version of the MBS-CD is available at the
MBS-CDROM webpage.
Contents
8.1
8.2
8.1
Code Links . . . . . . . . .
How to Compile the Library
8.2.1 Compiling . . . . . .
8.2.2 Installing . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
91
91
91
92
Code Links
Windows users: clicking on the provided link will launch InternetExplorer if you have Acrobat
Reader 6.0 (other versions not tested). Linux users: if you use acroread version 5.0.6 or 5.0.10,
clicking on the link will work with Mozilla or Firefox provided the browser is already launched
somewhere in your workspace. If not, the launch may fail on the first try but will succeed if you click on
the link a second time with the browser open. Other users: leave this PDF file and manually copy the
code files to your local directories.
• SimPlot Simulation and Plotting Library Source Code (Linux):
Click on this link: SimPlotLinux
• SimPlot Simulation and Plotting Library Source Code (Windows):
Click on this link: SimPlotWindows
8.2
How to Compile the Library
If sufficient demand for the library persists, then I will attempt to formalize the installation procedure using autoconfigure
and/or a Windows installer application. Until there is such a need, we’ll do it the hard way, by hand. Once compiled,
there is only one file to install/relocate, so that part of the problem is relatively easy.
8.2.1
Compiling
1. If you do not have available the GNU gcc compiler (version 3.2 or higher), either you are a Linux user in
desparate need of upgrading or you are a Windows user. If you are a Windows user, then you need to install a
Linux/X11 emulator of some kind. Read Chapter 6 to learn what you need to do.
91
2. Once the gcc compiler is in your search paths, de-compress the library files into a temporary work directory
(tar zxf MBS-CD.tgz or unzip MBS-CD.tgz).
3. Compile with: make -f mkMBS-CD
8.2.2
Installing
Move the resultant file libxyplot.a to the directory searched by gcc for libraries (e.g., /usr/local/lib).
Move the two header files SimMBS.h and xySimPlot.h to a directory search by gcc for include files (e.g.,
/usr/local/include).
92
Chapter 9
Installing Ancillary Libraries: Linux and
Windows
Summary: Instructions for installing the Gnu Scientific Library (GSL) and the Dislin plotting library. Instructions
for making this work on Windows.
Contents
9.1
9.2
Linux Users . . . . . . . . . . . .
9.1.1 DISLIN . . . . . . . . . .
9.1.2 GSL . . . . . . . . . . . .
Windows Users . . . . . . . . . .
9.2.1 Programming with an IDE
9.2.2 Programming with Make .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
93
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
94
94
94
94
94
97
9.1
Linux Users
Your life is easier, since these packages were designed for Linux and many of the requisite support libraries and
utilities are a part of standard Linux. Your life is also harder since there are many flavors and distributions of Linux.
For better or worse, I use the Redhat/Fedora family of distributions and these instructions are based on this.
9.1.1
DISLIN
The DISLIN homepage URL is: http://www.mps.mpg.de/dislin/. There you can find manuals and other
information. The simplest approach to obtaining Dislin is to download the RPM for Linux at this URL of the DISLIN
homepage: http://www.mps.mpg.de/dislin/redhat9.html. You want dislin-8.3.linux.rpm.
You might also like to have the interactive scripting package that allows you to enter plotting commands on the fly
from an octave-like environment. The RPM is: disgcl-4.3.linux.rpm
You can also find a recent version at the MBS-CDROM webpage.
The installation is that of the usual RPM package.
9.1.2
GSL
Open source software can change rapidly, especially the appearance of new RPM packaging. So it is best to do a
search. As of this writing, here is a source for GSL version 1.5.
http://rpmfind.net/linux/RPM/fedora/3/i386/gsl-1.5-1.i386.html
The usual methods for installing RPMs apply.
The latest GSL version is 1.6 and the source can be obtained at:
http://www.gnu.org/software/gsl/
You can also find a recent version at the MBS-CDROM webpage.
9.2
9.2.1
Windows Users
Programming with an IDE
The software libraries used here are designed for Unix computers, but they have been ported in part to Windows. You
will need to install a GNU gcc compatible compiler and the DISLIN and GSL libraries. Follow these steps.
1. Get the Bloodshed combination compiler and IDE GUI:
Go to http://www.bloodshed.net/dev/devcpp.html for the homepage. Bloodshed Dev-C++ is
a full-featured Integrated Development Environment (IDE) for the C/C++ programming language. It uses the
MinGW/Mingw32 port of GCC (GNU Compiler Collection) as its compiler. It creates native Win32 executables,
either console or GUI.
As of this writing, the package you want can be obtained from:
http://devpaks.org/show.php?devpak=71
or at the MBS-CDROM webpage
2. Unzip the zip file to a temporary directory and run setup.exe. Use the default install directory of C:\Dev-Cpp
You will need to make some internal declarations inside Dev-C++, but first you need to install GSL and Dislin.
3. GSL: A packaged binary is difficult to find; try here:
http://prdownloads.sourceforge.net/gnuwin32/gsl-1.6.exe?download
for the file /gnuwin32/gsl-1.6.exe. If the US mirrors fail, try Australia.
Don’t be tempted by the many GSL binaries for Cygwin, you want the Mingw32 version that will be compatible
with Dev-C++.
A copy is also available at the MBS-CDROM webpage
Install GSL into C:\gsl
94
4. Get and install DISLIN. For the latest version, go to:
http://www.mps.mpg.de/dislin/windows.html
and download dl_83_mg.zip (the graphing library) and gcl_43_w.zip (the command line interpreter).
A recent version is also available at the MBS-CDROM webpage.
Install Dislin to C:\dislin
5. Get and install the MBS-SimPlot package for Windows from this CD here, or
from the MBS-CDROM webpage.
6. Setup the Dev-C++ IDE to work with the libraries. This is similar to the anjuta setup process for each new
project.
(a) Start Dev-C++, create a project, and do the following using the drop-down menu:
(b) Project->Project Options->Parameters->Linker
type: -lsimplotWin c:\dislin\dismgc.a -lgsl -lgslcblas -lgdi32 -lcomdlg32
(c) Project->Project->Options->Directories->Library Directories
Type: C:\Dev-Cpp\lib C:\gsl\lib C:\dislin\win C:\dislin
(d) Project->Project->Options->Directories->Include Directories
Type: C:\Dev-Cpp\include C:\gsl\include C:\dislin\win C:\dislin
7. Alternative setup: Using the drop-down menu, go to:
Tools->Compiler Options.
Do the following.
(a) In the open window titled “Compiler Options” , click on the tab labeled “Compiler”. Click on the the box
labeled “Add these commands . . . ” and type in the line shown in the following figure.
(b) Next, click on the tab labeled “Directories”, then on the tab labeled “Libraries”. Using the following figure
as a guide, add the directories to use for searching for librarie by typing the path in the bottom editing field,
or use the browse icon to the right.
95
(c) In the same window, click on the tab labeled “C Includes” and make that window look like the following
figure.
8. IMPORTANT NOTE: The GSL may be supplied in both a static and dynamic linked library (DLL) form. For
proper functioning in the present context, you want to use only the static form. You have two options. (1) Add
the following option to the linker step of build; i.e., add -static to the window:
Tools->Compiler Options->Compiler. This is untested. Or (2) in the directory ...\gsl\lib\
are two DLL files: libgsl.dll.a and libgslcblas.dll.a. Rename these to: ORGlibgsl.dll.a
and ORGlibgslcblas.dll.a. This action will force the compiler to use the static forms. (Hey, it works for
me.)
96
9.2.2
Programming with Make
To compile model code using the make program (and not the Dev-C++ IDE), you may have to modify the make files
supplied on CD. You will need to define the LIBS and INCS macros to list the directories for GSL and DISLIN. For
example, this may work (untested):
1
2
COMPILER = C:\Dev-Cpp\bin\mingw32-c++.exe
CFLAGS = -g -Wall -O0
3
4
5
LIBS = -L C:\Dev-Cpp\lib -L C:\gsl\lib -L C:\dislin\win -L C:\dislin
INCS = -I C:\Dev-Cpp\include -I C:\gsl\include -I C:\dislin\win -I C:\dislin
6
7
8
foobar00:
FooBar-00.o
$(COMPILER) $(CFLAGS) -o foobar00 FooBar-00.o $(LIBS) -lxyplot -ldislnc -lgsl -lgslcblas -lm
9
10
11
FooBar-00.o: FooBar-00.c
$(COMPILER) $(CFLAGS) $(INCS) -c FooBar-00.c
97
Download