Software for spatially-explicit simulation of forest dynamics

Part 4: GUI and user documentation

In this part, you make your new behavior accessible to the user.

Note: The Java GUI is complicated. If your needs cannot be covered with the procedure below, please seek Lora Murphy's help.

Step 1: Identify where to add the new behavior in the Java GUI

Individual behaviors are descended from class Behavior. These classes are packaged together by type and managed by classes descended from BehaviorTypeBase. For instance, there are the classes GrowthBehaviors, MortalityBehaviors, etc.

These classes descended from BehaviorTypeBase are responsible for triggering all the sorts of things that individual behaviors do: getting values from the parameter file, validating parameter values, etc. When you are adding a new behavior, you determine into which of these classes it fits.

In the case of our example biomass calculation behavior, we would create a new class descended from Behavior and add it to the class Analysis Behaviors.

Step 2: Create the new class for the behavior

We start by creating a descendant of class Behavior. Let's call it BiomassCalculator.

Step 3: Add any new parameters

Our new class should contain all of the parameters needed. Each parameter is defined by a class descended from the ModelData class. Each child class is tailored for a different type of data: a species-specific array (ModelVector), a single float (ModelFloat), etc. These classes contain everything you need to know about a parameter: what kind of data it is, a descriptive name to display to the user, its XML tags, etc.

The parameters become new members of the Behavior child class containing your new behavior. Look carefully at all your constructor options for the parameter's class to make sure you set it up correctly. There are often several flags you can set which precisely describe the parameter.

Our example biomass calculation behavior had two parameters: "slope of biomass equation (a)" and "exponent of biomass equation (d)". They are both species-specific, so we would add two new ModelVector objects to the BiomassCalculator class. Here's the code we could write:

/**Biomass behavior - slope of biomass equation (a) (look, I'm a Doxygen comment!)*/
protected ModelVector mp_fSlopeOfBiomassEq = new ModelVector(
"Biomass Calculator - Slope of Biomass Equation (a)", //descriptive name to display to user
"an_slopeBiomassEq", //XML tag for the parameter
"an_sbeVal", //XML tag for individual species values
0, //initial size of the java.util.Vector within this class - 0 is always a good choice
ModelVector.FLOAT, //type of data within the array
false); //optional flag indicating that the parameter is not forced to apply to all species

/**Biomass behavior - exponent of biomass equation (d)*/
protected ModelVector mp_fExpOfBiomassEq = new ModelVector(
"Biomass Calculator - Exponent of Biomass Equation (d)",

We also need to add our parameters to the required list for the behavior in the constructor:


Tips for your descriptive parameter name, which will be displayed to the user:

  • The name must be capitalized.
  • The name should clearly indicate to which behavior the parameter belongs (if it belongs to only one).
  • The name can contain variable names that match the equation you put in the user documentation.
  • The name cannot be too long or it won't be correctly displayed. Look at how the GUI displays your parameter name, and then go back and change it if necessary.

Step 4: Add any other function code

Behaviors have many functions that they can override in order to do what they need to.

Our behavior creates a grid object, so we need to create it and add it as available to our behavior. This way it will show up in the list of grids for the user. We would add the following code to the constructor:

//Add the "Biomass Map" grid

//Create the data member array for our two data members
DataMember[] p_oDataMembers = new DataMember[2];

//"count" int data member"
p_oDataMembers[0] = new DataMember("Adult Count", //data member name to display to user
"count", //data member name in the C++ code
DataMember.INTEGER); //data member type

//"avg" float data member"
p_oDataMembers[1] = new DataMember("Average Biomass",

//Create the new grid
Grid oNewGrid = new Grid("Biomass Map", //name of the grid
p_oDataMembers, //list of grid's data members
null, //list of package data members - none in our case
8, 8); //default grid cell resolution - 8 X 8 is always safe

//Assign this grid to our new behavior

We can also add parameter validation. As in the C++ code, you want to make sure that you reject invalid parameter values. To do this, add code to the validateData method of your BiomassCalculator class. This code runs when parameters are being entered in the parameter window, so users are not allowed to enter bad values, and before a run begins.

For our example biomass calculation behavior, negative values of our parameter a are illegal. We would add the following code to validateData:

//Get a list of species to which this behavior is applied so we only validate values for those species
boolean[] p_bAppliesTo = getWhichSpeciesUsed(oPop);
//There's a method available for making sure values are positive - use it
ValidationHelpers.makeSureAllPositive(mp_fSlopeOfBiomassEq, p_bAppliesTo);

Step 5: Add the new behavior to be managed

The class that manages objects of our new class is a descendant of BehaviorTypeBase called Analysis Behaviors. A class called BehaviorInstantiator is given the details of our new class, and will instantiate objects of that class at the required times. Here's the code for our new example biomass behavior, which we would put in the constructor of the AnalysisBehaviors class:

//Create the new behavior
sXMLRootString = "BiomassCalculator"; //String that will form the XML tag surrounding this behavior's parameters in the parameter file sParFileTag = "BiomassCalculator"; //How this behavior will be referred to in the parameter file's list of behaviors for the run sDescriptor = "Biomass Calculator"; //The name of the behavior that the user is shown mp_oAvailableBehaviors.add(new BehaviorInstantiator(BiomassCalculator.class, sDescriptor, sParFileTag, sXMLRootString));

Step 6: Update any tests

There are JUnit tests for many classes in the GUI. Run the JUnit test for the class to which you added. Fix anything that you broke. Add new test conditions for any tests of methods you added to, like validateData.

Step 7: User documentation

The very last step is to update the documentation so people know how to use your behavior. Add your new parameters and behavior to the page devoted to the type of behavior you've made. Tips:

  • The documentation is written in simple HTML. You can probably pick up what you need to know from example. The resulting HTML page should be nice-looking.
  • When referring to your behavior or parameters, the name must EXACTLY match the name you gave to them in the GUI.
  • When you mention a parameter, put the name of it in bold. (You remembered to capitalize it when you named it in the GUI code, right?)
  • It is better to say too much than too little. Write as much detail as necessary so the user knows exactly what a behavior does and how it does it.
  • Grids and tree data members have their own pages. If you have created new ones, be sure to update those pages.