5.12. Step 12: First Union Formation

5.12.1. Model Description

At this step we add a base module for first union formation. We have chosen a parametric Coale McNeil model as base module as is allows very intuitive parameterization and empirically proved very adequate in the context of developing countries, at least for the past. Due to the high impact of education on first union formation age, the base model is parameterized separately by primary education outcome.

5.12.2. The new FirstUnionCoaleMcNeil.mpp Module

This module implements a Coale McNeil model for first union formation of women. This allows parameterizing union formation by three very intuitive parameters:

  • The youngest age of first union formation (this is where about the first 1% have entered a first union)
  • The average age of first union formation
  • The proportion of the population ever entering a union

The parameterization of first union formation is by year of birth and a classification of population groups UNION1_GROUP. The classification is generic, in the current implementation it corresponds with the primary education fate. The list can be extended or changed as long as the derived state union1_group which stores the individual group membership is adapted accordingly.

Internally the Coale McNeil model is used to calculate period union formation rates by calendar year, age and population group in the pre-simulation phase. These rates are then used in the simulation. For the population of the starting population, the time of union formation comes from the file, thus the modeled rates are only applied in the simulated time, while past events are based on the information on file. For immigrants, modeled rates are used also in the past.

The Coale McNeil model typically gives a very good fit for developing countries and allows easy scenario building. At the other hand, it might perform poor for developed countries therefore loosing its attractivity as countries develop. The technical implementation of this module allows its easy combination with alternative models which can substitute the period rates calculated in pre-simulation (e.g. replacing this module from a certain year onwards) by rates obtained from other models.



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

classification UNION1_GROUP                 //EN Population Groups
{
    U1G_00,                                 //EN Never entered school
    U1G_01,                                 //EN Primary dropout
    U1G_02                                  //EN Primary graduate
};

range AGE_UNION { 10, 60 };                 //EN Age

classification UNION_PARA                   //EN Union formation parameters
{
    UP_MINAGE,                              //EN Age at which first 1% enter a union
    UP_AVERAGE,                             //EN Average age at first union formation
    UP_EVER                                 //EN Proportion ow femailes ever entering a union
};

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

parameters
{
    //EN Union formation
    double Union1ParametersCMN[UNION1_GROUP][YOB_UNION][UNION_PARA];

    //EN Union formation hazards
    model_generated double Union1FormationHazard[UNION1_GROUP][ALL_YEAR_RANGE][AGE_UNION];
};

parameter_group PG_UnionFormation           //EN Union formation
{
    Union1ParametersCMN
};

////////////////////////////////////////////////////////////////////////////////////////////////////
// Actor states and functions
////////////////////////////////////////////////////////////////////////////////////////////////////

actor Person
{
    //EN Population group
    UNION1_GROUP union1_group = (educ_one_fate == EOL_LOW ) ? U1G_00 :
                                (educ_one_fate == EOL_MEDIUM ) ? U1G_01 : U1G_02;

    AGE_UNION   age_union = COERCE(AGE_UNION, integer_age);             //EN Age

    //EN Union formation hazard
    double          union1_formation_hazard = (WITHIN(AGE_UNION, integer_age) && sex==FEMALE) ?
                Union1FormationHazard[union1_group][RANGE_POS(ALL_YEAR_RANGE, calendar_year)]
                [RANGE_POS(AGE_UNION, integer_age)] : 0.0;

    TIME        union1_startpop = { TIME_INFINITE };                //EN Startpop information
    logical             in_union = { FALSE };                               //EN Ever in union
    event               timeUnion1FormationEvent, Union1FormationEvent;     //EN First union formation event
};

////////////////////////////////////////////////////////////////////////////////////////////////////
// Implementation
////////////////////////////////////////////////////////////////////////////////////////////////////


TIME Person::timeUnion1FormationEvent()
{
    if ( sex == FEMALE && !in_union && WITHIN(AGE_UNION, integer_age)
        && union1_formation_hazard > 0.0
        && (calendar_year >= MIN(SIM_YEAR_RANGE) || person_type == PT_IMMIGRANT ))
    {
        return WAIT(-TIME(log(RandUniform(26)) / union1_formation_hazard));
    }
    else if (union1_startpop < MIN(SIM_YEAR_RANGE)  && sex == FEMALE
        && !in_union && calendar_year < MIN(SIM_YEAR_RANGE))
    {
        return union1_startpop;
    }
    else return TIME_INFINITE;
}

void Person::Union1FormationEvent()
{
    in_union = TRUE;
}

/*
GetUnionFormationHazard() returns the union formation hazard for given education, age and period based
on a Coale McNeil model. This is a parametric model with three parameters
*/
double GetUnionFormationHazard(int nEduc, int nYear, int nAge);
double GetUnionFormationHazard(int nEduc, int nYear, int nAge)
{
    double      dReturnValue = 0.0;
    double      g[SIZE(AGE_UNION)];
    double      a0 = Union1ParametersCMN[nEduc][RANGE_POS(YOB_UNION, nYear - nAge)][UP_MINAGE];
    double      my = Union1ParametersCMN[nEduc][RANGE_POS(YOB_UNION, nYear - nAge)][UP_AVERAGE];
    double      C = Union1ParametersCMN[nEduc][RANGE_POS(YOB_UNION, nYear - nAge)][UP_EVER];
    double      k = (my - a0) / 11.36;
    double dThisYearCumulative = 0.0;
    if (nAge < a0) dReturnValue = 0.0;
    else if ( nAge >= a0 )
    {
        for (int nX = 0; nX < SIZE(AGE_UNION); nX++)
        {
            g[nX] = 0.19465*exp(-0.174*(nX - 6.06) - exp(-0.288*(nX - 6.06)));
        }
        double  index = ((double)nAge - a0) / k;
        int             nIndex = int(index);            // integer part
        double  dIndex = index - nIndex;                // after comma part
        double G = 0.0;
        for (int nI = 0; nI <= nIndex; nI++)
        {
            if (nI < SIZE(AGE_UNION)) G = G + g[nI];
        }
        if (nIndex < SIZE(AGE_UNION) - 1) G = G + dIndex*g[nIndex + 1];
        dThisYearCumulative = G * C;
    }
    if (nAge == a0) dReturnValue = dThisYearCumulative;
    else if ( nAge > a0 )
    {
        double  index = ((double)nAge - a0 -1) / k;
        int             nIndex = int(index);            // integer part
        double  dIndex = index - nIndex;                // after comma part
        double G = 0.0;
        for (int nI = 0; nI <= nIndex; nI++)
        {
            if (nI < SIZE(AGE_UNION)) G = G + g[nI];
        }
        if (nIndex < SIZE(AGE_UNION) - 1) G = G + dIndex*g[nIndex + 1];
        if (1.0 - G * C > 0.0001) dReturnValue = (dThisYearCumulative - G * C) / (1 - G * C);
        else dReturnValue = 0.0;

    }
    // convert probability to hazard
    if (dReturnValue < 1.0)  dReturnValue = -log(1 - dReturnValue);
    return dReturnValue;
}


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

/*
In the presimulation of the union formation module, age specific hazards for union formation
are calculated and copied into a model-generated parameter.
*/
void PreSimulation()
{
    for (int nEduc = 0; nEduc < SIZE(UNION1_GROUP); nEduc++)
    {
        for (int nYear = MIN(ALL_YEAR_RANGE); nYear <= MAX(ALL_YEAR_RANGE); nYear++)
        {
            for (int nAge = MIN(AGE_UNION); nAge <= MAX(AGE_UNION); nAge++)
            {
                Union1FormationHazard[nEduc][RANGE_POS(ALL_YEAR_RANGE,nYear)][RANGE_POS(AGE_UNION,nAge)]
                    = GetUnionFormationHazard(nEduc,nYear,nAge);
            }
        }
    }
}

table Person TestUnion
[sex == FEMALE]
{
    district_birth+ *
    {
        duration(in_union,TRUE)/duration()
    }
    * integer_age
    * calendar_year
};

5.12.3. Defing a new parameter range in _CountryContext.mpp


range YOB_UNION{ 1951, 2037 };                  //EN Year of birth

5.12.4. Initializations in the Start() Function

The state union1_startpop is added to the list of variables initiated in the Start() Function.


    // 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;
        district_int        = (DISTRICT_INT)(int)peObservation->pmc[PMC_DISTRICT];
        district_birth      = (DISTRICT_INT)(int)peObservation->pmc[PMC_DOB];
        educ_one_startpop   = (EDUC_ONE_LEVEL)(int)peObservation->pmc[PMC_EDUC];
        ethnicity           = (ETHNICITY)(int)peObservation->pmc[PMC_ETHNO];

        if (peObservation->pmc[PMC_UNION] > time && peObservation->pmc[PMC_UNION] < MIN(SIM_YEAR_RANGE))
        {
            union1_startpop = peObservation->pmc[PMC_UNION];
        }