5.4. Step 4: Base Fertility

5.4.1. Model Description

At this step we add a module for fertility based on age-specific fertility profiles and projected total fertility rates (TFRs). This is the base version of fertility resembling a typical macro population projection approach. It will be complemented with refined models going beyond age and sex when modeling fertility. Besides the introduction of the new module FertilityAgeTFR.mpp, the Start() function of the PeronCore.mpp module is updated for initializing states at birth of new actors born in the simulation. These new baby actors can access characteristics of their mother what is used to set the current time and the state time_of_birth.

5.4.2. The new module: FertilityAgeTFR.mpp

This module implements fertility based on age-specific fertility rates calculated from two parameters: an age distribution of fertility and a projected total fertility rate (TFR) for future years. Another parameter is the sex-ratio. This module is a microsimulation implementation of a typical cohort component model.

The actual age-specific period fertility rates are calculated in the PreSimulation function to meet the projected TFR for each year.

The model is prepared for being complemented or over-ridden by an alternative refined fertility model. This is done by three logical states:

  • The state use_base_fertility_model is initialized as TRUE; it indicates that the base model is to be used. When adding another model choice this flag can be changed in another module.
  • The state use_base_fertility_for_alignment is initialized with FALSE; it indicates if another model is to be aligned to the fertility outcome of this base model.
  • The state baby_looking_for_mother is set to TRUE at a birth event if the base model is used for alignment only and the actual birth has to be assigned to another person of the population.


////////////////////////////////////////////////////////////////////////////////////////////////////
// Types
////////////////////////////////////////////////////////////////////////////////////////////////////

range FERTILE_AGE_RANGE { 10, 49 };                                 //EN Fertile age range
range PARITY_RANGE { 0, 15 };                                       //EN Parity range

////////////////////////////////////////////////////////////////////////////////////////////////////
// Parameters
////////////////////////////////////////////////////////////////////////////////////////////////////

parameters
{
    //EN Age distribution of fertility
    double  AgeSpecificFertility[FERTILE_AGE_RANGE][SIM_YEAR_RANGE];

    //EN Total fertility rate
    double  TotalFertilityRate[SIM_YEAR_RANGE];

    //EN Sex ratio (male per 100 female)
    double  SexRatio[SIM_YEAR_RANGE];

    //EN Age specific fertility rate
    model_generated     double  AgeSpecificFertilityRate[FERTILE_AGE_RANGE][SIM_YEAR_RANGE];
};

parameter_group PG03a_Fertility_Model_A  //EN Fertility Base Model
{
    AgeSpecificFertility, TotalFertilityRate, SexRatio
};


////////////////////////////////////////////////////////////////////////////////////////////////////
// Actor declarations
////////////////////////////////////////////////////////////////////////////////////////////////////

actor Person
{
    logical use_base_fertility_model = { TRUE };            //EN Use the base model
    logical use_base_fertility_for_alignment = { FALSE };   //EN Use the model for alignment
    logical baby_looking_for_mother = { FALSE };            //EN A birth is still to be created

    //EN Indicator that perion is a potential mother
    logical is_potential_mother = (sex == FEMALE && WITHIN(FERTILE_AGE_RANGE, integer_age)
    && parity < MAX(PARITY_RANGE) && in_projected_time && ever_resident) ? TRUE : FALSE;

    PARITY_RANGE parity;                                    //EN Parity
    event       timeBirthEvent, BirthEvent;                     //EN Birth event
};


////////////////////////////////////////////////////////////////////////////////////////////////////
// Event Implementations
////////////////////////////////////////////////////////////////////////////////////////////////////

TIME Person::timeBirthEvent()
{
    double dEventTime = TIME_INFINITE;
    double dHazard = 0.0;

    if (is_potential_mother && (use_base_fertility_model || use_base_fertility_for_alignment))
    {
        dHazard = AgeSpecificFertilityRate[RANGE_POS(FERTILE_AGE_RANGE, integer_age)]
                [RANGE_POS(SIM_YEAR_RANGE, calendar_year)];
        if (dHazard > 0.0) dEventTime = WAIT(-TIME(log(RandUniform(3)) / dHazard));
    }
    return dEventTime;
}

void Person::BirthEvent()
{
    // event applies to individual without alignment
    if (use_base_fertility_model)
    {
        parity++;                   // increment parity
        auto peChild = new Person;  // Create and point to a new actor
        peChild->Start(NULL, this);   // Call Start() function of baby and pass own address
    }
    else if (use_base_fertility_for_alignment)
    {
        baby_looking_for_mother = TRUE;
    }
}

////////////////////////////////////////////////////////////////////////////////////////////////////
// Pre-Simulation
////////////////////////////////////////////////////////////////////////////////////////////////////

void PreSimulation()
{
    double dSum;
    for (int nYear = 0; nYear < SIZE(SIM_YEAR_RANGE); nYear++)
    {
        dSum = 0.0;
        // check if distribution parameter adds up too 1
        for (int nAge = 0; nAge < SIZE(FERTILE_AGE_RANGE); nAge++)
        {
            dSum = dSum + AgeSpecificFertility[nAge][nYear];
        }
        // scale distribution to 1 and convert to fertility rates; copy to model generated parameter
        for (int nAge = 0; nAge < SIZE(FERTILE_AGE_RANGE); nAge++)
        {
            if (dSum > 0.0)
            {
                AgeSpecificFertilityRate[nAge][nYear]
                    = AgeSpecificFertility[nAge][nYear] / dSum * TotalFertilityRate[nYear];
            }
            else AgeSpecificFertilityRate[nAge][nYear] = 0.0;
        }
    }
}

5.4.3. Update of the Start() function in PersonCore.mpp

The Start() function is updated in order to initialize all states at the birth of a baby born in the simulation. In this case values do not come from the starting popiulation file but have to be derived otherwise, e.g. by accessing mother’s characteristics (e.g. for setting the time) or by sampling (e.g. sex according to a parameter for sex ratio).


void Person::Start(Observation *peObservation, Person *pePers)
{
    // Setting the actor weight
    Set_actor_weight(asGlobals->Item(0)->person_weight);
    Set_actor_subsample_weight(asGlobals->Item(0)->person_weight);

    // Determine the person type
    if (!peObservation && pePers)   person_type = PT_CHILD;   // Born in simulation
    else if (peObservation)         person_type = PT_START;   // From Starting Pop
    else person_type = PT_IMMIGRANT;                          // Immigrant

    // Initialize states
    if (person_type == PT_START) // Person comes from starting population
    {
        // (A) States from Starting population file
        time                = peObservation->pmc[PMC_BIRTH];
        sex                 = (SEX)(int)peObservation->pmc[PMC_SEX];
        family_role         = peObservation->fam_role;

        // (B) Other states
        time_of_birth       = time;
        calendar_year       = (int)time_of_birth;

        // (C) Links to head resp. spouse
        // (uncomment when modeling families) if (pePers) peHHead = pePers;
    }
    else if (person_type == PT_CHILD) // Person born in simulation
    {
        // (A) States corresponding to Starting population file variables
        if (RandUniform(4) < 100.0 / (100.0 + SexRatio[RANGE_POS(SIM_YEAR_RANGE, calendar_year)]))
        {
            sex  = FEMALE;
        }
        else sex = MALE;
        time                = pePers->time;
        family_role         = FR_CHILD;

        // (B) Other states
        time_of_birth       = time;
        calendar_year       = (int)time_of_birth;

        // (C) Links to head resp. spouse
        // (uncomment when modeling families) if (pePers) peHHead = pePers;
    }
    else // Person is an immigrant
    {
        // do nothing (immigrants not modeled yet)
    }
    time_set_alive = WAIT(0);
}
//