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];
}