Step 13: Family Links
Overview
At this step we re-organize, revise, and maintain all family links by introducing a new dedicated module FamilyLinks.mpp. Family links are links between spouses, links between children and their biological parents, and of children with their parents or guardians with whom they currently live in a household. Family links are first established at birth and maintained at union formation, union dissolution, when leaving home, and at death. At each event the family status is updated (i.e. being a household head, spouse, or child) and all links are updated. For leaving home we create a new event. The update of links includes the children’s decision with whom to stay after a union dissolution and the search for a suitable guardian if a lone parent dies.
The new FamilyLinks.mpp Module
This module handles and maintains family links. Family links are links between spouses, links between children and their biological parents, and of children with their parents or guardians with whom they currently live in a household. We model nuclear family households, consisting of a household head, and - if present - a spouse and dependent children.
Dependent children are:
Children below age 18 who are not in a union or parents themselves
Children below age 26 as long as they are continuously enrolled in education and not in a union or parents themselves - and who have not left home
Maintenance of links at death is handled in the function MaintainLinksAtDeath() linked to the mortality event. A spouse becomes the household head. If there is no spouse but children in the household, each child checks if she has a biological mother or father, or a grand mother or grand father alive, in which case she moves to a new guardian and updates all links. If not, and at age 18+, she becomes a household head.
Maintenance of links at union formations is handled in the function MaintainLinksAtUnionFormation() called at union formation. Partners become a household head and spouse and all children who lived with any of the two partners before in a household now update their family links as they now live with two “guardians”. (The link to biological parents is kept over life and can be different from the links to the household head and the spouse of the household head). If a union formation happens in the first year after giving birth and the baby has no biological father assigned, it is assumed that the male partner is the biological father of the baby.
Maintenance of links at union dissolution is handled in the function DissolveUnion() which is called when a union is dissolved. Both former partners now become household heads and children have to choose with whom to live. The choice is modeled by a set of simple rules and a probability to stay with the famale guardian. If only one of the two guardians is a biological parent, children choose to stay with the biological parent. Otherwise, the choice is random dependent on the parameter. All children who randomly decide make the same choice.
Leaving home is modelled as an event LeavingHomeEvent() which is called immediately after any union formation or increase of parity, at age 18 if not enrolled in school, at the moment of leaving school after age 18, or at age 26. In addition, children in education age 18-25 can leave home at any point in time following a parameter of age-specific home-leaving rates of students. Once a person has left the household, she is not moving back. The family status changes from child to household head or to spouse.
The creation of links at birth is handled in the function LinkFamilyAtBirth() hooked to the SetAliveEvent(). For children in the starting population it is assumed that both parents - if present in the household - are biological parents.
////////////////////////////////////////////////////////////////////////////////////////////////////
// Family Links
////////////////////////////////////////////////////////////////////////////////////////////////////
link Person.lBioFather Person.mlBioFatherChildren[]; //EN Biological Father - Children
link Person.lBioMother Person.mlBioMotherChildren[]; //EN Biological Mother - Children
link Person.lHHFather Person.mlHHFatherChildren[]; //EN HH Father - Children
link Person.lHHMother Person.mlHHMotherChildren[]; //EN HH Mother - Children
link Person.lSpouse; //EN Spouses
////////////////////////////////////////////////////////////////////////////////////////////////////
// Dimensions
////////////////////////////////////////////////////////////////////////////////////////////////////
classification FAM_ROLE //EN Role in family
{
FR_HEAD, //EN Head
FR_SPOUSE, //EN Spouse of head
FR_CHILD //EN Child
};
range AGE_18_25 { 18, 25 }; //EN Age
////////////////////////////////////////////////////////////////////////////////////////////////////
// Parameters
////////////////////////////////////////////////////////////////////////////////////////////////////
parameters
{
double ProbStayWithMother; //EN Probability to stay with mother after union dissolution
double ProbLeaveHome[AGE_18_25]; //EN Probability to leave home (students)
};
////////////////////////////////////////////////////////////////////////////////////////////////////
// Actor States and Functions
////////////////////////////////////////////////////////////////////////////////////////////////////
actor Person
{
FAM_ROLE family_role = { FR_HEAD }; //EN Family Role
void LinkFamilyAtBirth(); //EN Establish family links at birth
hook LinkFamilyAtBirth, SetAliveEvent; //EN Called at set alive event
event timeLeavingHomeEvent, LeavingHomeEvent; //EN Leaving Home
void MaintainLinksAtUnionFormation(); //EN Family links at union formation
hook MaintainLinksAtUnionFormation, FindPartner; //EN Update at end of FindPartner()
void MaintainLinksAtDeath(); //EN Family links at death
hook MaintainLinksAtDeath, MortalityEvent; //EN Update at Death
void DissolveUnion(); //EN Union Dissolution
//EN Children in Household
short children_in_household = (sex == FEMALE) ?
sum_over(mlHHMotherChildren, is_alive) :
sum_over(mlHHFatherChildren, is_alive);
};
////////////////////////////////////////////////////////////////////////////////////////////////////
// Implementation
////////////////////////////////////////////////////////////////////////////////////////////////////
void Person::MaintainLinksAtDeath()
{
if (lSpouse) lSpouse->family_role = FR_HEAD;
else if (children_in_household > 0)
{
//try to find somebody for children
int nIndex;
auto prChild = (sex==FEMALE) ? mlHHMotherChildren->GetNext( 0, &nIndex ) : mlHHFatherChildren->GetNext( 0, &nIndex );
while ( prChild != NULL )
{
Person * prGuardian = NULL;
if (sex == FEMALE && prChild->lBioFather) // own biological father
{
prGuardian = prChild->lBioFather;
}
else if (sex == MALE && prChild->lBioMother) // own biological mother
{
prGuardian = prChild->lBioMother;
}
else if (lBioMother) // grandmother
{
prGuardian = lBioMother;
}
else if (lBioFather) // grandfather
{
prGuardian = lBioFather;
}
else if (prChild->integer_age >= 18) // nobody
{
prChild->family_role = FR_HEAD;
}
if (prGuardian && prGuardian->sex == MALE)
{
prChild->lHHFather = prGuardian;
if (prGuardian->lSpouse) prChild->lHHMother = prGuardian->lSpouse;
}
else if (prGuardian && prGuardian->sex == FEMALE)
{
prChild->lHHMother = prGuardian;
if (prGuardian->lSpouse) prChild->lHHFather = prGuardian->lSpouse;
}
prChild = (sex==FEMALE) ? mlHHMotherChildren->GetNext( nIndex+1, &nIndex ) : mlHHFatherChildren->GetNext( nIndex+1, &nIndex );
}
}
}
void Person::DissolveUnion()
{
if (lSpouse)
{
//For all children in household decide with whom to stay
if (children_in_household)
{
int nIndex;
bool bStayWithMother = (RandUniform(32) < ProbStayWithMother);
//Stay with mother
auto prChild = mlHHMotherChildren->GetNext( 0, &nIndex );
while ( prChild != NULL )
{
if (bStayWithMother && ((prChild->lBioMother && prChild->lBioMother == this)
|| !prChild->lBioFather || (prChild->lBioFather && prChild->lBioFather != lSpouse)))
{
prChild->lHHFather = NULL;
}
prChild = mlHHMotherChildren->GetNext(nIndex + 1, &nIndex);
}
//Stay with father
if (lSpouse->children_in_household)
{
auto prChild = lSpouse->mlHHFatherChildren->GetNext( 0, &nIndex );
while ( prChild != NULL )
{
prChild->lHHMother = NULL;
prChild = lSpouse->mlHHFatherChildren->GetNext(nIndex + 1, &nIndex);
}
}
}
lSpouse->family_role = FR_HEAD;
family_role = FR_HEAD;
lSpouse = NULL;
}
}
void Person::MaintainLinksAtUnionFormation()
{
// Partners children in HH now live also with this woman
if (lSpouse && lSpouse->children_in_household)
{
int nIndex;
auto prChild = lSpouse->mlHHFatherChildren->GetNext( 0, &nIndex );
while ( prChild != NULL )
{
prChild->lHHMother = this;
prChild = lSpouse->mlHHFatherChildren->GetNext(nIndex + 1, &nIndex);
}
}
if (lSpouse)
{
// Own children in HH also live with partner now
int nIndex;
auto prChild = mlHHMotherChildren->GetNext( 0, &nIndex );
while ( prChild != NULL )
{
prChild->lHHFather = lSpouse;
// if babies without biological father, the spouse becomes biological father
if (!prChild->lBioFather && prChild->integer_age == 0)
{
prChild->lBioFather = lSpouse;
}
prChild = mlHHMotherChildren->GetNext(nIndex + 1, &nIndex);
}
family_role = FR_HEAD;
lSpouse->family_role = FR_SPOUSE;
// own household
lHHFather = NULL;
lHHMother = NULL;
lSpouse->lHHFather = NULL;
lSpouse->lHHMother = NULL;
}
}
TIME Person::timeLeavingHomeEvent()
{
if (in_projected_time && family_role == FR_CHILD
&& (integer_age >= 26 || lSpouse || (integer_age >= 18 && !in_school) || parity > 0))
{
return WAIT(0);
}
else if (family_role == FR_CHILD && WITHIN(AGE_18_25, integer_age))
{
double dProb = ProbLeaveHome[RANGE_POS(AGE_18_25, integer_age)];
if (dProb >= 1.0) return WAIT(0);
else if (dProb > 0.0) return WAIT(-log(RandUniform(33)) / -log(1 - dProb));
else return TIME_INFINITE;
}
else return TIME_INFINITE;
}
void Person::LeavingHomeEvent()
{
// become a head or a spouse
if (lSpouse && lSpouse->family_role == FR_HEAD) family_role = FR_SPOUSE;
else family_role = FR_HEAD;
// remove links to household
lHHFather = NULL;
lHHMother = NULL;
}
void Person::LinkFamilyAtBirth()
{
if (person_type == PT_START && family_role != FR_HEAD && peHHead != NULL)
{
if (family_role == FR_CHILD)
{
if (peHHead->sex == MALE)
{
lBioFather = peHHead;
if (lBioFather->lSpouse) lBioMother = lBioFather->lSpouse;
}
else
{
lBioMother = peHHead;
if (lBioMother->lSpouse) lBioFather = lBioMother->lSpouse;
}
lHHFather = lBioFather;
lHHMother = lBioMother;
}
else
{
lSpouse = peHHead;
if (sex == lSpouse->sex) // FIX: do not allow same sex couples
{
if (sex == FEMALE) sex = MALE; else sex = FEMALE;
}
}
}
else if (person_type == PT_CHILD)
{
lBioMother = peHHead;
if (lBioMother->lSpouse) lBioFather = lBioMother->lSpouse;
lHHFather = lBioFather;
lHHMother = lBioMother;
}
}
////////////////////////////////////////////////////////////////////////////////////////////////////
// Validation Tables
////////////////////////////////////////////////////////////////////////////////////////////////////
table Person TabFamilyRoleTest //EN FAMILY ROLE
[WITHIN(SIM_YEAR_RANGE, calendar_year)]
{
sex+ *
{
duration(family_role, FR_CHILD) / duration(), //EN Child
duration(family_role, FR_HEAD) / duration(), //EN Head
duration(family_role, FR_SPOUSE) / duration() //EN Spouse
}
* integer_age
* sim_year
};