Sample behavior

This is a set of files called "MyBehavior.cpp" and "MyBehavior.h", with code for the class "clMyBehavior". It tells you where to put your code to create a new behavior. This code is not guaranteed to compile; it is just a guide.
MyBehavior.h
MyBehavior.cpp

MyBehavior.h


#ifndef MyBehaviorH
#define MyBehaviorH

#include "BehaviorBase.h"

/**
The slash and two asterisks, closed by an asterisk and a single slash, denotes a comment for automatic documentation by Doxygen. You are politely requested to comment your header files in Doxygen format, so that they can automatically go into the overall SORTIE documentation. The Doxygen web site has more on how to do that, and there are examples throughout this header file and in existing SORTIE code. (See the SORTIE help topic for more on SORTIE documentation.) Doxygen's C++ output for SORTIE is here, if you want to see what it looks like. Put this comment immediately before your class declaration.

Each behavior (in fact, each population and grid as well) has a name string. This string is a unique identifier. Other objects can use the name string to find and access this one. So document your name string where it can be seen. If you don't want other objects to access your code, don't skip the name string; just seal up your data. This behavior's name string will be "my behavior".
*/


class clMyBehavior : virtual public clBehaviorBase {
//note: you need the virtual keyword to avoid base class ambiguity.
public:

/**
Constructor. You must have a constructor, if only to initialize the base class constructors. Again, note the Doxygen commenting.

@param p_oSimManager Sim Manager object. The "@param" is the Doxygen format for commenting a function argument. "@return" documents a function's return value, if applicable. "@throws" documents errors thrown by the function. Remember those, and the fact that the special comment goes right before the thing commented in the header file, and you can write almost all, if not all, of your documentation Doxygen-compliant.
*/

clMyBehavior(clSimManager *p_oSimManager);

/**
Destructor. You are responsible for freeing your behavior's memory here. This function is not required if you don't need it.
*/

~clMyBehavior();

/**
This function is overridden from clBehaviorBase. It automatically executes once per timestep, at this behavior's turn in the behavior order. This is probably where the bulk of your behavior's functionality will go. Write your code into the function body and it will get called at the right time. I haven't written a sample body for action in this file.
*/

void Action();

/**
This function is overridden from clWorkerBase. It automatically executes at the end of the timestep, after Action() has been called for all behaviors. You could use this to, for instance, take a grid that is used by several different behaviors and re-initialize it, ready for the next timestep. You don't have to override this function if you don't need it. I haven't written a function body for this in this example.
*/

void TimestepCleanup();

/**
This function is overridden from clWorkerBase. It automatically executes at the end of the run, before any objects have been destroyed. You might use it to, for example, compile log statistics and write them to a file. You don't have to override this function if you don't need it. I haven't written a function body for this in this example.
*/

void EndOfRunCleanup();

protected:

/**
This function is overridden from clBehaviorBase. It automatically executes at the beginning of the run. Behaviors can assume that the tree population and grid objects have already been set up from the parameter file when this function is called.

The p_oDoc parameter is the parsed parameter file. Each behavior is responsible for extracting what it needs from the parameter file, and throwing errors if it gets bad data. (The functions defined in ParsingFunctions.h are there to help you read data from the parameter file.) You can find out more about behavior data definition in the behaviors topic.

All behavior setup code should go here, or in functions called from this function. This function is protected because it doesn't have to be public; it is called by the parent class's clBehaviorBase::DoSetup() function.

@param p_oDoc DOM tree of the parsed input file.
*/

void GetData(DOMDocument *p_oDoc);

/**
Our hypothetical behavior needs a grid, and so we'll write this function to set it up and call it from GetData(). If someone is using a parameter file that contains setup information for this grid, such as changing the default resolution or reading in a map of initial conditions values, the grid may already exist. This function will take that into account and create the grid if it's not already created.
*/

void SetUpGrid();

/**
This is a pointer to a grid that this behavior needs. Obviously, some behaviors don't need grids. Variable naming conventions are described here.

Sometimes, a behavior just needs to find a grid that another behavior created. But our behavior plans to create this one. If you do, be sure to document the namestring of the grid, what data members it has, what the default grid cell resolution is, etc.

This grid is called "mygrid". It contains one bool data member called "the_bool". The default master grid resolution is fine here.
*/

clGridBase *mp_oMyGrid;

/**This is an example of some species-specific data that is needed.*/
float *mp_fMySpeciesData;

/**This is an example of a single value that is needed.*/
int m_iMySingleData;

/**This is the data return code for our grid's data member, so we can easily access it later.*/
short int m_iTheBoolCode;

};
#endif

MyBehavior.cpp



#include "MyBehavior.h"
#include "GridBase.h"
#include "ParsingFunctions.h"

/*
Constructor. This is how I do headers in the actual file. You can do yours however you want. Once you have released your code back to the SORTIE source, make sure it has an edit history there so we can track changes.
Edit history:
-------------
April 28, 2004 - Submitted in beta version (LEM)
*/

clMyBehavior::clMyBehavior(clSimManager *p_oSimManager) : clBehaviorBase(p_oSimManager), clWorkerBase(p_oSimManager) {
try {
//Set the name string
strcpy(m_cNameString, "my behavior");

/*Set the behavior versions. There is a behavior version number in the parameter file, which will be validated against these numbers. This happens automatically; all you have to do is set the numbers. These numbers are defined in clBehaviorBase. See more about versioning in the behaviors topic.*/

mp_fVersionNumber = 1;
mp_fMinimumVersionNumber = 1;

/*Set the allowed file types that are allowed as setup to this behavior. All behaviors should accept parameter files and rundata files. In addition, perhaps this behavior is able to accept another kind of file as setup as well, but almost certainly not. These variables are defined in clWorkerBase.*/

m_iNumAllowedTypes = 2;
mp_iAllowedFileTypes = new int[m_iNumAllowedTypes];
mp_iAllowedFileTypes[0] = parfile;
mp_iAllowedFileTypes[1] = rundata;

/*Set the number of new tree data members that this behavior intends to register. In our case, we're not registering any (in which case we don't need these lines because they default to 0). But if your behavior was going to register a new data member, put how many of each you want here, then register them in GetData(). A new data member of the appropriate type will be added for each species/type combination that your behavior applies to, ready to be registered with a name. The species/type combinations to which the behavior is applied are accessed through m_iNumSpeciesTypeCombos and mp_whatSpeciesTypeCombos, both defined in clBehaviorBase.*/

m_iNumNewTreeInts = 0;
m_iNumNewTreeFloats = 0;
m_iNumNewTreeChars = 0;
m_iNumNewTreeBools = 0;

/*Standard memory setup so that we can fail gracefully if the destructor is called before the object is set up*/
mp_oMyGrid = NULL;
mp_fMySpeciesData = NULL;

}
/*Most functions should be encapsulated in a try-catch block. Certainly all the major ones should. You can use your discretion with smaller functions unlikely to produce errors. The idea here is, in the case of unexpected errors, to fail gracefully in a way that allows the clSimManager object to clean up, producing a SORTIE error message in the process that can be reported to the user in a standard way.

The first two catch statements simply make sure that any errors or messages thrown lower down in the stack are not caught here and accidentally re-thrown as unknown errors produced by this function. The last catch statement is for unexpected errors.*/


catch(modelErr &err) {throw(err);}
catch (modelMsg &msg) {throw(msg);} //non-fatal error
catch(...) {
modelErr stcErr;
stcErr.iErrorCode = UNKNOWN;
strcpy(stcErr.cFunction, "clMyBehavior::clMyBehavior");
throw(stcErr);
}
}

/*
Destructor
Edit history:
-------------
April 28, 2004 - Submitted in beta version (LEM)
*/
clMyBehavior::~clMyBehavior() {

/*Delete array*/
delete[] mp_fMySpeciesData;
/*No need to delete the grid object - that will happen automatically*/
}

/*
GetData()
Edit history:
-------------
April 28, 2004 - Submitted in beta version (LEM)
*/

void clMyBehavior::GetData(DOMDocument *p_oDoc) {
try {
DOMElement = p_oDoc->getDocumentElement();
/*Get the tree population object*/
clPopulationBase *p_oTemp = mp_oSimManager->GetPopulationObject("treepopulation");
clTreePopulation *p_oPop = (clTreePopulation*) p_oTemp;
floatVal *p_fTempValues;
int iNumSpecies = p_oPop->GetNumberOfSpecies(), i;

/*First, let's read some parameter file values*/

/*Start with our single value. Assume that all tag names have been properly defined. We can call a function to fill this value for us, and tell the function that this value is required. Then, an error will be thrown if the value is not present in the parameter file.*/

FillSingleValue(p_oElement, "my_tag", &m_iMySingleData, true);

/*Now for the species-specific value. The important thing to remember with species-specific values is that you cannot assume that your behavior will be applied to all species. It is important to make sure that you only try to load and work with values for species to which the behavior applies. You can have quick access to the species to which this behavior has been applied through m_NumBehaviorSpecies and mp_iWhatSpecies, both defined in clBehaviorBase.*/

/*First, declare our array. Again, there are a few different options. Since this behavior only has one array, I will declare it to be sized the total number of species; then, a species' number will be the array index for its value. If there are some species not used, then we'll just be careful not to access those array indexes. If you had many species-specific arrays, it would save some memory to declare them to only be as large as m_iNumBehaviorSpecies, and have one master array that translated species numbers to array indexes.*/

mp_fMySpeciesData = new float[iNumSpecies];

/*We can automatically collect values only for those species to which this behavior is applied. To do that, we will engage the help of a temporary array. What we do is declare an array of floatVals. This structure packages together a value and the species code associated with it. (You could use this structure to hold species-specific data in your behavior, if you wanted.) In order to use our array to read values from the parameter file, declare it, then load all the species codes to which this behavior applies into it. We can then call a function that will match those codes to values from the parameter file and load those in.*/

p_fTempValues = new floatVal[m_iNumBehaviorSpecies];
for (i = 0; i < m_iNumBehaviorSpecies; i++)
p_fTempValues[i].code = mp_iWhatSpecies[i];

/*Now we can load values from the file - make them required so an error will be thrown if they aren't all there*/
FillSpeciesSpecificValue(p_oElement, "my_parent_tag", "my_child_tag", p_fTempValues, m_iNumBehaviorSpecies, p_oPop, true);

/*Now that we've read our parameter file data, let's finish setup by setting up our grid*/

SetUpGrid();

} //end of try block
catch (modelErr &err) {throw(err);}
catch (modelMsg &msg) {throw(msg);} //non-fatal error
catch(...) {
modelErr stcErr;
stcErr.iErrorCode = UNKNOWN;
strcpy(stcErr.cFunction, "clMyBehavior::GetData");
throw(stcErr);
}
}
SetUpGrid()
Edit history:
-------------
April 28, 2004 - Submitted in beta version (LEM)
*/
void clMyBehavior::SetUpGrid() {

/*Check first to see if our grid was defined in the parameter file. If you didn't want to allow the user to enter any setup information for your grid, just create it again. Any existing grid with the same name will be overwritten.*/

mp_oMyGrid = mp_oSimManager->GetGridObject( "mygrid" );

if ( !mp_oMyGrid )
{
/*Our grid was not created in the parameter file so create it now*/

mp_oMyGrid = mp_oSimManager->CreateGrid( "ygrid"", 0, 0, 0, 1 );

/*Register our data member*/
m_iTheBoolCode = mp_oMyGrid->RegisterBool( "the_bool" );
}
else
{
/*Our grid had been created in the parameter file. Make sure that our data member was registered. If it wasn't, that's a fatal error since we can't register it now that the grid's been created.*/

m_iTheBoolCode = mp_oMyGrid->GetBoolDataCode( "the_bool" );
if ( -1 == m_iTheBoolCode )
{
modelErr stcErr;
stcErr.iErrorCode = BAD_DATA;
strcpy( stcErr.cFunction, "clMyBehavior::SetUpGrid" );
strcpy( stcErr.cMoreInfo, "The \"mygrid\" grid was incorrectly set up in the parameter file. The bool \"the_bool\" is missing." );
throw( & stcErr );
}
}
}
04-May-2004 02:22 PM