QHG Tutorial 05 - Sexual Reproduction¶
In this chapter we describe a class whose agent can, in addition to dying and moving, perform sexual reproduction..
For this we need to add one new action: RandomPair.
RandomPair
finds fertile males and females in each cell and pairs off as many as possible (by each agent’s mate index to index of its partner).
All othe mechanisms are the same as in the previous chapter, with the exception that agents are not their own mates.
Population code:
Action code:
The Header File¶
This class must include the header files for the new action, but the agent structure remains the same as in the previous example.
#ifndef __TUT_SEXUALPOP_H__
#define __TUT_SEXUALPOP_H__
#include "GetOld.h"
#include "OldAgeDeath.h"
#include "RandomMove.h"
#include "Fertility.h"
#include "Verhulst.h"
#include "RandomPair.h"
#include "SPopulation.h"
struct tut_SexualAgent : Agent {
float m_fAge;
float m_fLastBirth;
int m_iMateIndex;
};
The class gets an additional pointer for the new action:
class tut_SexualPop : public SPopulation<tut_SexualAgent> {
public:
tut_SexualPop(SCellGrid *pCG, PopFinder *pPopFinder, int iLayerSize, IDGen **apIDG, uint32_t *aulState, uint *aiSeeds);
virtual ~tut_SexualPop();
int addPopSpecificAgentData(int iAgentIndex, char **ppData);
void addPopSpecificAgentDataTypeQDF(hid_t *hAgentDataType);
int makePopSpecificOffspring(int iAgent, int iMother, int iFather);
protected:
GetOld<tut_SexualAgent> *m_pGO;
OldAgeDeath<tut_SexualAgent> *m_pOAD;
RandomMove<tut_SexualAgent> *m_pRM;
Verhulst<tut_SexualAgent> *m_pVerhulst;
Fertility<tut_SexualAgent> *m_pFert;
RandomPair<tut_SexualAgent> *m_pPair;
};
The input/output auxiliary functions addPopSpecificAgentData()
and addPopSpecificAgentDataTypeQDF()
are identical to the ones in the previous chapterhave to be modified to take into account the new agent member fields.
The method makePopSpecificOffspring()
which allows the population to perform specific operations after an agent is born (e.g. initialize member fields to particular values).
Code: tut_SexualPop.h
The Implementation¶
In the implementation we have to include hdf5.h
because we need to modify some HDF5 structures:
#include <omp.h>
#include <hdf5.h>
Furthermore, we have to add the includes of the cpp file for the new action and include the header file of this class:
#include "SPopulation.cpp"
#include "LayerBuf.cpp"
#include "Prioritizer.cpp"
#include "Action.cpp"
#include "GetOld.cpp"
#include "OldAgeDeath.cpp"
#include "RandomMove.cpp"
#include "Fertility.cpp"
#include "Verhulst.cpp"
#include "RandomPair.cpp"
#include "tut_SexualPop.h"
The constructor must create instances of all actions and add them to the priority list.
//----------------------------------------------------------------------------
// constructor
//
tut_SexualPop::tut_SexualPop(SCellGrid *pCG, PopFinder *pPopFinder, int iLayerSize, IDGen **apIDG, uint32_t *aulState, uint *aiSeeds)
: SPopulation<tut_SexualAgent>(pCG, pPopFinder, iLayerSize, apIDG, aulState, aiSeeds) {
m_pGO = new GetOld<tut_SexualAgent>(this, m_pCG, "");
m_pOAD = new OldAgeDeath<tut_SexualAgent>(this, m_pCG, "", m_apWELL);
m_pRM = new RandomMove<tut_SexualAgent>(this, m_pCG, "", m_apWELL);
m_pFert = new Fertility<tut_SexualAgent>(this, m_pCG, "");
m_pVerhulst = new Verhulst<tut_SexualAgent>(this, m_pCG, "", m_apWELL);
m_pPair = new RandomPair<tut_SexualAgent>(this, m_pCG, "", m_apWELL);
m_prio.addAction(m_pGO);
m_prio.addAction(m_pOAD);
m_prio.addAction(m_pRM);
m_prio.addAction(m_pFert);
m_prio.addAction(m_pVerhulst);
m_prio.addAction(m_pPair);
}
The destructor must delete all instances created in the constructor:
//----------------------------------------------------------------------------
// destructor
//
tut_SexualPop::~tut_SexualPop() {
if (m_pGO != NULL) {
delete m_pGO;
}
if (m_pOAD != NULL) {
delete m_pOAD;
}
if (m_pRM != NULL) {
delete m_pRM;
}
if (m_pFert != NULL) {
delete m_pFert;
}
if (m_pVerhulst != NULL) {
delete m_pVerhulst;
}
if (m_pPair != NULL) {
delete m_pPair;
}
}
The methods addPopSpecificAgentData()
and addPopSpecificAgentDataTypeQDF()
are practically identical to the ones in the previous chapter, so there’s no need to look at them in more detail.
The method makePopSpecificOffspring()
initializes the agents’ new fields. It can leaabve the gender field unchanged, because Spopulation
sets the gender randomly to male or female.
//----------------------------------------------------------------------------
// makePopSpecificOffspring
//
int tut_SexualPop::makePopSpecificOffspring(int iAgent, int iMother, int iFather) {
int iResult = 0;
m_aAgents[iAgent].m_fAge = 0.0;
m_aAgents[iAgent].m_fLastBirth = 0.0;
m_aAgents[iAgent].m_iMateIndex = -3;
// SPopulation assigns random genders to a baby, ehich is ok here
return iResult;
}
The mate index is set to a negative number to denote that this agent is not paired (yet).
Code: tut_SexualPop.cpp
XML and DAT¶
The xml file for the population attributes does not contain a new <module>
entry, because RandomPair
does not need any parameters.
It must, however, be registerd for a priority level.
<class name="tut_SexualPop" 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>
<module name="RandomMove">
<param name="RandomMove_prob" value="0.07"/>
</module>
<module name="Verhulst">
<param name="Verhulst_b0" value="0.8" />
<param name="Verhulst_d0" value="0.001" />
<param name="Verhulst_theta" value="0.1" />
<param name="Verhulst_K" value="20" />
</module>
<module name="Fertility">
<param name="Fertility_interbirth" value="2.0"/>
<param name="Fertility_max_age" value="50.0"/>
<param name="Fertility_min_age" value="15.0"/>
</module>
<priorities>
<prio name="GetOld" value="8"/>
<prio name="OldAgeDeath" value="10"/>
<prio name="RandomMove" value="7"/>
<prio name="Fertility" value="2"/>
<prio name="RandomPair" value="3"/>
<prio name="Verhulst" value="6"/>
</priorities>
</class>
The dat file tut_Sexual.dat
defines postions and attributes for 20 agents, the last attribute pertainng to the new member m_fAge
.
It is similar to the dat file in the previous example, except that the agents’ genders are not exclusively female:
#Longitude;Latitude;LifeState;AgentID;BirthTime;Gender;Age;LastBirth
8;47;1; 1;-10.0;0;10.0;-1.0
8;47;1; 2;-10.0;1;10.0;-1.0
8;47;1; 3;-10.0;0;10.0;-1.0
8;47;1; 4;-10.0;1;10.0;-1.0
...
8;47;1;18;-10.0;1;10.0;-1.0
8;47;1;19;-10.0;0;10.0;-1.0
8;47;1;20;-10.0;1;10.0;-1.0
Feel free to modify the values in the xml file and see how this changes the outcome. But keep in mind that high calue for RandomMove_prob
can cause the population to disperse and render pairing and reproduction impossible.
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_05 tut_SexualPop
This builds alternative population directories tut_05_pop
and tut_05_mod
with the files needed to compile tut_ParthenoPop
.
To compile, change to the QHG4 root directory and type:
OPT=1 SHORT=tut_05 make clean QHGMain
Here we set the environemnt variable OPT
to get optimized compilation (see Compiling QHG)
Now cd to the tutorial subdirectory “tutorial_05’ and start QHGMain
${QHG4_DIR}/app/QHGMain --read-config=tutorial_05.cfg > tutorial_05.out
This tells QHGMain to read the parameters from the file tutorial_05.cfg
and write all output to the file tutorial_05.out
(this output contains more information than the logfile)
The config file tutorial_05.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_05.log
--num-iters=10000
--output-dir=output/tutorial_05
--output-prefix=tut
--pops='tut_SexualPop.xml:tut_SexualPop.dat'
--shuffle=92244
--start-time=-10000
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 (10000 steps instead of 3000 steps, because this population grows more slowly).
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_SexualPop.xml
) and agent attributes (tut_SexualPop.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_05.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 that the population grows slower than the one in the previous example, and the population stabilizes at a lower level. This has to do with the fact that only half of the population can have offspring, whereas in the previous chapter, every inidividual had offspring.
After step 9995 (0.002099 s): total 94852 agents
sapiens 4993 births r
sapiens 4968 deaths r
sapiens 18 births p
sapiens 5368 moves
After step 9996 (0.002057 s): total 94877 agents
sapiens 4890 births r
sapiens 5012 deaths r
sapiens 78 deaths p
sapiens 5286 moves
After step 9997 (0.002287 s): total 94755 agents
sapiens 4974 births r
sapiens 4833 deaths r
sapiens 38 deaths p
sapiens 5382 moves
The summary:
-----
Finished Simulation
Number of threads: 8
Number of iterations: 10000
Used time: 25.914365 (00:00:25)
Number of agents after last step
sapiens: 94801
total: 94801