Step 11: Female Partnership Status

Overview

At this step we add a new module for maintaining the female partnership status according to observed partnership patterns by age (at last birth), age of youngest child, and education. The female partnership status is updated at the middle of each year according to model parameters. This is a base module working entirely due to alignment. It can be refined by adding union dissolution events on the micro level.

The new FemalePartnershipStatus.mpp Module

This module implements processes for maintaining the partnership status of women over the life course (union formation, disslolution, calls for matching a suitable partner). The female partnership status is updated at yearly events according to observed partnership patterns by education, age / age group at last birth, and age group of youngest child. The partnership status is modeled for all women within the age range 15-80, no more union formation events are modled thereafter when it is assumed the onlz union dissolution is due to widowhood.

Parameters:

  • Proportion of women living with dependent children who are in a partnership by education, age group at last birth and age group of youngest child
  • Proportion of women not living with dependent children who are in a partnership by education and age

The model maintaines the patterns contained in the parameters in the future, thus we assume that these patterns are stable and changes in aggregate partnership characteristics only result from compositional changes in the female population like childlessness and timing of births. The model follows a ‘minimum necessary corrections’ approach changing the union status of women only to meet aggregate numbers. In reality, unions are more unstable, i.e. the model does not move women out of a union and others in if the aggregate proportion does not change. It can be refined e.g. by adding a union dissolution module at the micro level if a higher life course consistency is important for model applications.

The core of this module is the Clock function UpdatePartnershipStatus() which is hooked to the Clock’s mid-year event. Alternatively, the function could be hooked to the mid-month event to add precision (at the cost of performance).

Code Changes in other modules:

  • The module requires a function FindPartner() which is declared and implemented in the module PartnerMatching.mpp.


link Person.lMother   Person.mlMothersChildren[];                       //EN Link between children and Mother
link Person.lFather   Person.mlFathersChildren[];                       //EN Link between children and Father

////////////////////////////////////////////////////////////////////////////////////////////////////
// Actor Sets
////////////////////////////////////////////////////////////////////////////////////////////////////

//EN Women in a partnership living with dependent children
actor_set Person asPopInUnionWithChildren[educ3_level][child_agegr][moth_agegr]
    filter is_alive && sex == FEMALE && in_projected_time && WITHIN(PART_AGE_RANGE, integer_age) &&
    in_union && lives_with_dependent_child;

//EN Women not in a partnership living with dependent children
actor_set Person asPopNotInUnionWithChildren[educ3_level][child_agegr][moth_agegr]
    filter is_alive && sex == FEMALE && in_projected_time && WITHIN(PART_AGE_RANGE, integer_age) &&
    !in_union && lives_with_dependent_child;

//EN Women living with dependent children
actor_set Person asPopWithChildren[educ3_level][child_agegr][moth_agegr]
    filter is_alive && sex == FEMALE && in_projected_time && WITHIN(PART_AGE_RANGE, integer_age) &&
    lives_with_dependent_child;

//EN Women not living with dependent children
actor_set Person PopNoChildren[educ3_level][integer_age]
    filter is_alive && sex == FEMALE && in_projected_time && WITHIN(PART_AGE_RANGE, integer_age) &&
    !lives_with_dependent_child;

//EN Women in a partnership not living with dependent children
actor_set Person asPopInUnionNoChildren[educ3_level][integer_age]
    filter is_alive && sex == FEMALE && in_projected_time && WITHIN(PART_AGE_RANGE, integer_age) &&
    in_union && !lives_with_dependent_child;

//EN Women not in a partnership not living with dependent children
actor_set Person asPopNotInUnionNoChildren[educ3_level][integer_age]
    filter is_alive && sex == FEMALE && in_projected_time && WITHIN(PART_AGE_RANGE, integer_age) &&
    !in_union && !lives_with_dependent_child;


////////////////////////////////////////////////////////////////////////////////////////////////////
// Dimensions
////////////////////////////////////////////////////////////////////////////////////////////////////

partition   CHILD_AGEGR_PART { 1,3,6,9,12,15 };        //EN Age of youngest child
partition   MOTH_AGEGR_PART { 20, 25, 30, 35, 40 };    //EN Age of mother at last birth
range       PART_AGE_RANGE { 15, 80 };            //EN Age

classification MOTH_AGEGR //EN Age group mothers at birth
{
    CMA20,  //EN Below 20
    CMA25,  //EN 20 to 24
    CMA30,  //EN 25 to 19
    CMA35,  //EN 30 to 34
    CMA40,  //EN 35 to 39
    CMA40P  //EN 40+
};

classification CHILD_AGEGR //EN Age group child
{
    CA00,  //EN 0
    CA01,  //EN 1 to 2
    CA03,  //EN 3 to 5
    CA06,  //EN 6 to 8
    CA09,  //EN 9 to 11
    CA12,  //EN 12 to 14
    CA15   //EN 15 to 17
};

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

parameters
{
    //EN Probability to be in a partnership - Females living with children
    double      InUnionProbWithChildren[EDUC_LEVEL3][CHILD_AGEGR][MOTH_AGEGR];

    //EN Probability to be in a partnership - Females not living with children
    double      InUnionProbNoChildren[PART_AGE_RANGE][EDUC_LEVEL3];
};

parameter_group PG_FemalePartnerships           //EN Female Partnership Status
{
    InUnionProbWithChildren,
    InUnionProbNoChildren
};

////////////////////////////////////////////////////////////////////////////////////////////////////
// Actor States & Functions
////////////////////////////////////////////////////////////////////////////////////////////////////

actor Person
{
    int int_age = integer_age;
    EDUC_LEVEL3 educ3_level = aggregate(educ_level, EDUC_LEVEL3);

    //EN Person currently in a union
    logical in_union = (lSpouse != NULL) ? TRUE : FALSE;

    //EN Age of youngest child of women
    double  age_youngest_child = (count(mlMothersChildren) > 0 && sex == FEMALE) ?
        min_over(mlMothersChildren, int_age) : TIME_INFINITE;

    //EN Age group of youngest child of women
    int child_agegr_part = split(age_youngest_child, CHILD_AGEGR_PART);

    //EN Woman lives with a child afe < 18
    logical lives_with_dependent_child = (age_youngest_child < 18) ? TRUE : FALSE;

    //EN Woman's age at last birth if living with children < 18
    double age_last_birth = (lives_with_dependent_child) ?
        integer_age - age_youngest_child : TIME_INFINITE;

    //EN Age group at last birth
    int moth_agegr_part = split(age_last_birth, MOTH_AGEGR_PART);

    //EN Age group at last birth
    MOTH_AGEGR moth_agegr = (moth_agegr_part == 0) ? CMA20 :
        (moth_agegr_part == 1) ? CMA25 :
        (moth_agegr_part == 2) ? CMA30 :
        (moth_agegr_part == 3) ? CMA35 :
        (moth_agegr_part == 4) ? CMA40 : CMA40P;

    //EN Age group child
    CHILD_AGEGR child_agegr = (child_agegr_part == 0) ? CA00 :
        (child_agegr_part == 1) ? CA01 :
        (child_agegr_part == 2) ? CA03 :
        (child_agegr_part == 3) ? CA06 :
        (child_agegr_part == 4) ? CA09 :
        (child_agegr_part == 5) ? CA12 : CA15;
};

actor Clock
{
    void UpdatePartnershipStatus();                     //EN Update Female Partnership Status
    hook UpdatePartnershipStatus, MidYearClockEvent;    //EN hook to mid year
};

void Clock::UpdatePartnershipStatus()
{
    long nTarget;
    if (clock_year >= MIN(SIM_YEAR_RANGE))
    {
        // Women with children
        for (int nEduc = 0; nEduc < SIZE(EDUC_LEVEL3); nEduc++)
        {
            for (int nChildAge = 0; nChildAge < SIZE(CHILD_AGEGR); nChildAge++)
            {
                for (int nMothAge = 0; nMothAge < SIZE(MOTH_AGEGR); nMothAge++)
                {
                    nTarget = round(InUnionProbWithChildren[nEduc][nChildAge][nMothAge] *
                        asPopWithChildren[nEduc][nChildAge][nMothAge]->Count());

                    if (nTarget > asPopInUnionWithChildren[nEduc][nChildAge][nMothAge]->Count())
                    {
                        long nEmptyRun = 0;
                        while (nTarget > asPopInUnionWithChildren[nEduc][nChildAge][nMothAge]->Count() &&
                            asPopNotInUnionWithChildren[nEduc][nChildAge][nMothAge]->Count() > 0 && nEmptyRun < 10000)
                        {
                            auto prFam = asPopNotInUnionWithChildren[nEduc][nChildAge][nMothAge]->GetRandom(RandUniform(55));
                            if (!prFam->FindPartner()) nEmptyRun++;
                        }
                    }
                    else if (nTarget < asPopInUnionWithChildren[nEduc][nChildAge][nMothAge]->Count())
                    {
                        while (nTarget < asPopInUnionWithChildren[nEduc][nChildAge][nMothAge]->Count() &&
                            asPopInUnionWithChildren[nEduc][nChildAge][nMothAge]->Count() > 0)
                        {
                            auto prFam = asPopInUnionWithChildren[nEduc][nChildAge][nMothAge]->GetRandom(RandUniform(57));
                            prFam->lSpouse = NULL;
                        }
                    }
                }
            }
        }
        //Targets for women without children in hh
        for (int nEduc = 0; nEduc < SIZE(EDUC_LEVEL3); nEduc++)
        {
            for (int nAge = 0; nAge < SIZE(PART_AGE_RANGE); nAge++)
            {
                nTarget = round(InUnionProbNoChildren[nAge][nEduc] *
                    PopNoChildren[nEduc][nAge + MIN(PART_AGE_RANGE)]->Count());

                if (nTarget > asPopInUnionNoChildren[nEduc][nAge + MIN(PART_AGE_RANGE)]->Count())
                {
                    long nEmptyRun = 0;
                    while (nTarget > asPopInUnionNoChildren[nEduc][nAge + MIN(PART_AGE_RANGE)]->Count() &&
                        asPopNotInUnionNoChildren[nEduc][nAge + MIN(PART_AGE_RANGE)]->Count() > 0 && nEmptyRun < 10000)
                    {
                        auto prFam = asPopNotInUnionNoChildren[nEduc][nAge + MIN(PART_AGE_RANGE)]->GetRandom(RandUniform(58));
                        if (!prFam->FindPartner()) nEmptyRun++;
                    }
                }
                else if (nTarget < asPopInUnionNoChildren[nEduc][nAge + MIN(PART_AGE_RANGE)]->Count())
                {
                    while (nTarget < asPopInUnionNoChildren[nEduc][nAge + MIN(PART_AGE_RANGE)]->Count() &&
                        asPopInUnionNoChildren[nEduc][nAge + MIN(PART_AGE_RANGE)]->Count() > 0)
                    {
                        auto prFam = asPopInUnionNoChildren[nEduc][nAge + MIN(PART_AGE_RANGE)]->GetRandom(RandUniform(61));
                        prFam->lSpouse = NULL;
                    }
                }
            }
        }
    }
}


////////////////////////////////////////////////////////////////////////////////////////////////////
// Tables
////////////////////////////////////////////////////////////////////////////////////////////////////

table_group TabValidationPartnershipStatus  //EN Female partnership status
{
    TabTestPartnershipStatusWithKids2020,
    TabTestPartnershipStatusNoKids2020,
    TabParaPartnershipStatusWithKids,
    TabParaPartnershipStatusNoKids
};

table Person TabTestPartnershipStatusWithKids2020
[lives_with_dependent_child && in_projected_time && calendar_year == 2020
&& sex == FEMALE && WITHIN(PART_AGE_RANGE, integer_age)]
{
    educ3_level + *
{
    duration(in_union,TRUE) / duration() //EN Proportion in partnership decimals=4
}
*moth_agegr +
*child_agegr +
};

table Person TabTestPartnershipStatusNoKids2020
[!lives_with_dependent_child && in_projected_time && calendar_year == 2020
&& sex == FEMALE && WITHIN(PART_AGE_RANGE, integer_age)]
{
    {
        duration(in_union, TRUE) / duration() //EN Proportion in partnership decimals=4
    }
    *integer_age +
    *educ3_level +
};


////////////////////////////////////////////////////////////////////////////////////////////////////
// Tables for parameter generation

partition SECOND_AFTER{ 2010.01, 2010.02 };
actor Person
{
    logical second_after = (self_scheduling_split(time,SECOND_AFTER) == 1) ? TRUE : FALSE;
};

//EN Proportion of women living with dependent children who are in a partnership
table Person TabParaPartnershipStatusWithKids
[trigger_entrances(second_after, TRUE) && lives_with_dependent_child && in_projected_time
&& sex == FEMALE && WITHIN(PART_AGE_RANGE, integer_age)]
{
    educ3_level + *
{
    value_in(in_union) / unit //EN Proportion in partnership decimals=4
}
*child_agegr +
*moth_agegr +
};

//EN Proportion of women not living with dependent children who are in a partnership
table Person TabParaPartnershipStatusNoKids
[trigger_entrances(second_after, TRUE) && !lives_with_dependent_child && in_projected_time
&& sex == FEMALE && WITHIN(PART_AGE_RANGE, integer_age)]
{
    {
        value_in(in_union) / unit //EN Proportion in partnership decimals=4
    }
    *integer_age +
    *educ3_level +
};

The new module PartnerMatching.mpp

At the current step 11 of model development, this module is a dummy module only. It implements the function FindPartner which randomly chooses and links an available partner of the same age. If non is found, the function returns FALSE. This module is to be refined at a later step.



////////////////////////////////////////////////////////////////////////////////////////////////////
// Actor Sets
////////////////////////////////////////////////////////////////////////////////////////////////////

//EN Males not in a partnershio by age
actor_set Person asAvailableMale[integer_age] filter is_alive && sex == MALE && !lSpouse;

////////////////////////////////////////////////////////////////////////////////////////////////////
// Actor states, events and functions
////////////////////////////////////////////////////////////////////////////////////////////////////

actor Person
{
    logical FindPartner();
};

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

logical Person::FindPartner()
{
    if (asAvailableMale[integer_age]->Count() > 0)
    {
        auto prPartner = asAvailableMale[integer_age]->GetRandom(RandUniform(26));
        lSpouse = prPartner;
        return TRUE;
    }
    else return FALSE;
}