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