QHG Tutorial 02 - Aging and Death¶
In this chapter we describe a class for a slightly more complex population class. In this class the agents have an age and die if they reach a certain maximum age.
For this we need to add two actions: GetOld and AtanDeath.
GetOld
increases the age at every simulation step, and OldAgeDeath
kills the agents if their age is within a random interval of a specified maximum age.
Population code:
Action code:
The Header File¶
This class must include the header files for the two actions, and it also defines a derived agent structure which has an additional file for the agent’s age. This member is changed by GetOld
, and read by OldAgeDeath
.
#ifndef __TUT_OLDAGEDIEPOP_H__
#define __TUT_OLDAGEDIEPOP_H__
// the actions we will use
#include "AtanDeath.h"
#include "GetOld.h"
#include "SPopulation.h"
// our agents need to have an age
struct tut_OldAgeDieAgent : Agent {
float m_fAge;
};
class tut_OldAgeDiePop : public SPopulation<tut_OldAgeDieAgent> {
public:
tut_OldAgeDiePop(SCellGrid *pCG, PopFinder *pPopFinder, int iLayerSize, IDGen **apIDG, uint32_t *aulState, uint *aiSeeds);
virtual ~tut_OldAgeDiePop();
int addPopSpecificAgentData(int iAgentIndex, char **ppData);
void addPopSpecificAgentDataTypeQDF(hid_t *hAgentDataType);
protected:
GetOld<tut_OldAgeDieAgent> *m_pGO;
AtanDeath<tut_OldAgeDieAgent> *m_pAD;
};
#endif
Because of the new field in the agent, we need to override the methods addPopSpecificAgentData()
and addPopSpecificAgentDataTypeQDF
,
to make sure the new member is read and written together with the rest of the agent data.
Code: tut_OldAgeDiePop.h
The Implementation¶
In the implementation we have to include hdf5.h
because we need to modify some HDF5 structures:
#include <hdf5.h>
Furthermore, we have to add includes of the cpp files for the new actions and include the header file of this class:
#include "SPopulation.cpp"
#include "LayerBuf.cpp"
#include "Prioritizer.cpp"
#include "Action.cpp"
#include "GetOld.cpp"
#include "AtanDeath.cpp"
#include "tut_OldAgeDiePop.h"
The constructor now must create instances of the two actions and add them to the priority list.
//----------------------------------------------------------------------------
// constructor
//
tut_OldAgeDiePop::tut_OldAgeDiePop(SCellGrid *pCG, PopFinder *pPopFinder, int iLayerSize, IDGen **apIDG, uint32_t *aulState, uint *aiSeeds)
: SPopulation<tut_OldAgeDieAgent>(pCG, pPopFinder, iLayerSize, apIDG, aulState, aiSeeds) {
m_pGO = new GetOld<tut_OldAgeDieAgent>(this, m_pCG, "");
m_pAD = new AtanDeath<tut_OldAgeDieAgent>(this, m_pCG, "", m_apWELL);
m_prio.addAction(m_pAD);
m_prio.addAction(m_pGO);
}
The destructor must delete the two action instances:
//----------------------------------------------------------------------------
// destructor
//
tut_OldAgeDiePop::~tut_OldAgeDiePop() {
delete m_pGO;
delete m_pAD;
}
The method addPopSpecificAgentData()
must read the age from the input string:
//----------------------------------------------------------------------------
// addPopSpecificAgentData
// read additional data from pop file
//
int tut_OldAgeDiePop::addPopSpecificAgentData(int iAgentIndex, char **ppData) {
int iResult = 0;
iResult = addAgentDataSingle(ppData, &m_aAgents[iAgentIndex].m_fAge);
return iResult;
}
The method addPopSpecificAgentDataTypeQDF()
extends the agent’s HDF data type with the age field:
//----------------------------------------------------------------------------
// addPopSpecificAgentDataTypeQDF
// extend the agent data type to include additional data in the output
//
void tut_OldAgeDiePop::addPopSpecificAgentDataTypeQDF(hid_t *hAgentDataType) {
tut_OldAgeDieAgent oada;
H5Tinsert(*hAgentDataType, "Age", qoffsetof(oada, m_fAge), H5T_NATIVE_FLOAT);
}
Code: tut_OldAgeDiePop.cpp
XML and DAT¶
The xml file for the population attributes now contains entries for the new actions:
<class name="tut_OldAgeDiePop" species_name="sapiens" species_id="104">
<module name="OldAgeDeath">
<param name="OAD_max_age" value="60.0"/>
<param name="OAD_uncertainty" value="0.1"/>
</module>
<priorities>
<prio name="GetOld" value="8"/>
<prio name="OldAgeDeath" value="10"/>
</priorities>
</class>
The action GetOld
needs no parameters; for details about the parameters of OldAgeDeath
look at the corresponding code description.
The dat file tut_OldAgeDie.dat
defines postions and attributes for 20 agents, the last attribute pertaining to the new member m_fAge
.
#Longitude;Latitude;LifeState;AgentID;BirthTime;Gender;Age
8;47;1; 1;-10.0;0;10.0
8;47;1; 2;-10.0;0;10.0
8;47;1; 3;-10.0;0;10.0
...
8;47;1;20;-10.0;0;10.0
In this case all agents are placed at the same location, and all agents are female (Gender: 0=female, 1=male)
Feel free to modify the values in the xml file and see how this changes the outcome.
Grid¶
For this example we use a subdivided icosahedron
Compiling and Running¶
Again, we assume that you have created the tutorial directories with the script build_tut_dirs.py.
First lets create alternative population and action directories with the script build_alt_modpop.py:
${QHG4_DIR}/useful_stuff/build_alt_modpop.py tut_02 tut_OldAgeDiePop
This builds alternative population directories tut_02_pop
and tut_02_mod
with the files needed to compile tut_OldAgeDiePop
.
To compile, change to the QHG4 root directory and start the compilation:
cd ${QHG4_DIR}
SHORT=tut_02 make clean QHGMain
This is the simplest way to compile QHG (for more information see Compiling QHG)
Now cd to the tutorial subdirectory “tutorial_02” and start QHGMain
${QHG4_DIR}/app/QHGMain --read-config=tutorial_02.cfg > tutorial_02.out
This tells QHGMain to read the parameters from the file tutorial_02.cfg
and write all output to the file tutorial_02.out
(this output contains more information than the logfile)
The config file tutorial_02.cfg
was created when the tutorial directories were created with build_tut_dirs.py
.
--data-dirs=${QHG4_DIR}/tutorial_data/grids/
--events='write|grid+geo+pop:sapiens@20000'
--grid=ico32s.qdf
--log-file=tutorial_02.log
--num-iters=200
--output-dir=output/tutorial_02
--output-prefix=tut
--pops='tut_OldAgeDie.xml:tut_OldAgeDie.dat'
--shuffle=92244
--start-time=0
data-dirs
A list of directories to search for data (not relevant for the OldAgeDie populations).
events
A list of events (see Events). Here a single event is defined: write output at step 20000 (or at the end of the simulation run).
grid
Path to the grid on which to run the simulation.
log-file
Log file for ‘high level’ messages.
num-iters
Number of iterations for the simulation.
output-dir
Output directory for qdf files.
output-prefix
A prefix prepended to the names of all output files
pops
The files for population attributes (
tut_OldAgeDie.xml
) and agent attributes (tut_OldAgeDie.dat
). These files were created bybuild_tut_dirs.py
.shuffle
A kind of seed for the random number generators. Same
shuffle
values result in identical simulations.start-time
The ‘real’ time corresponding to step 0.
The Output File¶
The output files, in this case tutorial_02.out
, always have the same structure.
First there are messages concerning the start of the program and intialisation.
If the simulation does not run, there’s a chance that you find th readoon why here.
The second part is a list of every step describing the numbers of births, deaths and moves, as well as the time the step took to execute.
At the end there is a short summary of the state at the end of the program: number of iterations, total time of execution, nuber of surviving agents. This is follwed by the contents of the log file.
If you look at the output for the steps, you see tat there are nobirths, and no moves, but only deaths:
After step 45 (0.000419 s): total 20 agents
sapiens 0 births r
sapiens 0 deaths r
sapiens 0 moves
After step 46 (0.000416 s): total 20 agents
sapiens 0 births r
sapiens 2 deaths r
sapiens 0 moves
After step 47 (0.000436 s): total 18 agents
sapiens 0 births r
sapiens 3 deaths r
sapiens 2 deaths p
sapiens 0 moves
And at the beginning of the last part:
-----
Stopped Simulation
Number of threads: 36
Number of iterations: 55
Used time: 0.034377 (00:00:00)
Number of agents after last step
sapiens: 0
total: 0
+++success+++