Step 10: Education Pattern

Model Description

At this step we add a module for education pattern: current school attendance, school type, and fulltime/part-time status. Pattern are sampled for a given highest educational attainment ‘fate’ and gender.

The new EducationPattern.mpp Module

The module implements school patterns of school attendance, school type, and enrolment type for a given education fate. School types correspond to the levels low, medium, high but additionally differentiate between dual, vocational, and general tracks. Attendence can be fulltime or parttime. The model is a ‘fate-model’ where for a given final outcome a pattern is assigned early in life. Patterns depend on final outcome (which itself depends on year of birth, sex and parents’s characteristics) and sex. For people of the starting population, educational pattern are imputed using the same model, but if possible respecting the variables on current school attendance. This is archieved in two steps. First for a given fate a pattern is assigned. When all perspms of a birth cohort have their fates assigned, these fates and pattern can be ‘traded’ with others in order to match the school attendance status in the starting population. These trades are within groups by sex and parents’ education thus don not affect the aggregate modeled school attendance.

Once assigned, the actual school attendance pattern are then updated in yearly schoolyear steps. For a given final fate and education pattern, three states are set:

  • educ_level: the current highest education attainment
  • educ_status: the current education status (ful-time, part-time, dual, pause etc.)
  • educ_pattern_status: the current school type and attendence pattern (place in assigned pattern)


//TODO check if for all who need it parents education is available

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

//TODO set exact years, move to _CountryContext
//EN Year of birth for which education patterns have to be checked against school attendance status in startpop
range YOB_CHECK_SCHOOL{ 1980,1990 };

classification EDUC_STATUS                              //EN Education Status
{
    ES_NEVER,                                           //EN Not entered school
    ES_FULLTIME,                                        //EN Fulltime Student
    ES_PARTTIME,                                        //EN Parttime Student
    ES_DUAL,                                            //EN Dual system student
    ES_PAUSE,                                           //EN Pause (interruption e.g. military)
    ES_FIN                                              //EN Finished schooling
};

classification EDUC_PATTERN                             //EN Education pattern
{
    EP_LOW,                                             //EN Low
    EP_MED_DUAL,                                        //EN Medium Dual
    EP_MED_VOC,                                         //EN Medium Vocational
    EP_MED_GEN,                                         //EN Medium General
    EP_OUT1,                                            //EN Out of school spell 1
    EP_HIGH1_FT,                                        //EN High Episode 1 (Full-time)
    EP_HIGH1_PT,                                        //EN High Episode 1 (Part-time)
    EP_OUT2,                                            //EN Out of school spell 2
    EP_HIGH2_FT,                                        //EN High Episode 2 (Full-time)
    EP_HIGH2_PT,                                        //EN High Episode 2 (Part-time)
    EP_OUT3,                                            //EN Out of school spell 3
    EP_HIGH3_FT,                                        //EN High Episode 3 (Full-time)
    EP_HIGH3_PT                                         //EN High Episode 3 (Part-time)
};

range EDUC_PATTERN_RANGE{ 0, 11 };                      //EN Education Pattern

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

parameters
{
    //EN Education Pattern
    int        EducPattern[EDUC_LEVEL3][EDUC_PATTERN_RANGE][EDUC_PATTERN];

    //EN Education Pattern Distribution
    cumrate[1] EducPatternDist[SEX][EDUC_LEVEL3][EDUC_PATTERN_RANGE];

    int        SchoolEntryAge;                          //EN School entry age
    double     StartSchoolYear;                         //EN Start of school year
};

parameter_group PG_Education                            //EN Education
{
    SchoolEntryAge, StartSchoolYear,
    EducPattern, EducPatternDist
};

///////////////////////////////////////////////////////////////////////////////////////////////////
// Actor sets
///////////////////////////////////////////////////////////////////////////////////////////////////

//EN Actor set of all potential students
actor_set Person asAllPotentialStudents
    filter is_alive && integer_age >= SchoolEntryAge && year_finish_school >= calendar_year;

//EN Persons who study in startpop and have no matching fate
actor_set Person asWantTradeEducPatternToInSchool[educ_group][sex]
    filter integer_age == 2 && want_trade_educ_pattern_to_inschool;

//EN Persons who do not study in startpop and have no matching fate
actor_set Person asWantTradeEducPatternToOutSchool[educ_group][sex]
    filter integer_age == 2 && want_trade_educ_pattern_to_outschool;

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

actor Person
{
    EDUC_LEVEL5 educ_level = { EL5_LOW };               //EN Current education level
    EDUC_STATUS educ_status = { ES_NEVER };             //EN Current Education Status
    logical in_school_startpop = { FALSE };             //EN Attending school in starting population

    int     year_finish_school  = { 9999 };             //EN Year finishing school

    EDUC_PATTERN_RANGE educ_pattern_number = { 0 };     //EN Educ Pattern Number
    EDUC_PATTERN educ_pattern_status = { EP_LOW };      //EN Education pattern status

    void SampleEducPattern();                           //EN Sample education fate and pattern
    hook SampleEducPattern, YearEnd;

    //EN Set Education states for given spell
    void SetCurrentEducLevelPatternStatus(int nSchoolSpell);

    void SchoolYearChange();                            //EN School year change

    logical want_trade_educ_pattern_to_inschool  = { FALSE };
    logical want_trade_educ_pattern_to_outschool = { FALSE };
};

actor Clock
{
    void SetNextSchoolYear();
    hook SetNextSchoolYear, StartYearClock;

    void TradeEducationFatesAndPatterns();
    hook TradeEducationFatesAndPatterns, StartYearClock;

    TIME    next_school_year = { TIME_INFINITE };       //EN Next School Year
    event   timeSchoolYearClock, SchoolYearClock;       //EN School year clock event
};

///////////////////////////////////////////////////////////////////////////////////////////////////
// Implementation Clock Functions
///////////////////////////////////////////////////////////////////////////////////////////////////


TIME Clock::timeSchoolYearClock() { return next_school_year; }
void Clock::SchoolYearClock()
{
    long nPopSize = asAllPotentialStudents->Count();
    for (long nIndex = 0; nIndex < nPopSize; nIndex++)
    {
        Person *prPerson = asAllPotentialStudents->Item(nIndex);
        prPerson->SchoolYearChange();
    }
    next_school_year = TIME_INFINITE;
}

void Clock::TradeEducationFatesAndPatterns()
{
    for (int nGroup = 0; nGroup < SIZE(EDUC_GROUP); nGroup++)
    {
        for (int nSex = 0; nSex < SIZE(SEX); nSex++)
        {
            while (asWantTradeEducPatternToInSchool[nGroup][nSex]->Count() > 0 && asWantTradeEducPatternToOutSchool[nGroup][nSex]->Count() > 0)
            {
                auto prPersonA = asWantTradeEducPatternToInSchool[nGroup][nSex]->GetRandom(RandUniform(24));
                auto prPersonB = asWantTradeEducPatternToOutSchool[nGroup][nSex]->GetRandom(RandUniform(25));

                EDUC_LEVEL3 cFate = prPersonA->educ_fate;
                EDUC_PATTERN_RANGE cPattern = prPersonA->educ_pattern_number;

                prPersonA->educ_fate = prPersonB->educ_fate;
                prPersonA->educ_pattern_number = prPersonB->educ_pattern_number;
                prPersonA->want_trade_educ_pattern_to_inschool = FALSE;

                prPersonB->educ_fate = cFate;
                prPersonB->educ_pattern_number = cPattern;
                prPersonB->want_trade_educ_pattern_to_outschool = FALSE;
            }
        }
    }
}

void Clock::SetNextSchoolYear()
{
    next_school_year = WAIT(StartSchoolYear); // set school year clock
}

///////////////////////////////////////////////////////////////////////////////////////////////////
// Implementation Person Functions
///////////////////////////////////////////////////////////////////////////////////////////////////

void Person::SampleEducPattern()
{
    if (integer_age == 1)
    {
        int nPattern;
        Lookup_EducPatternDist(RandUniform(6), (int)sex, (int)educ_fate, &nPattern);
        educ_pattern_number = (EDUC_PATTERN_RANGE)nPattern;
        // checks if in_school_startpop contradicts the fate
        if (person_type == PT_START && WITHIN(YOB_CHECK_SCHOOL, year_of_birth))
        {
            int     nSchoolTerm = MIN(SIM_YEAR_RANGE) - year_of_birth - SchoolEntryAge - 1;
            SetCurrentEducLevelPatternStatus(nSchoolTerm);
            if (educ_status == ES_FULLTIME || educ_status == ES_PARTTIME || educ_status == ES_DUAL)
            {
                if (!in_school_startpop) want_trade_educ_pattern_to_outschool = TRUE;
            }
            else if (in_school_startpop) want_trade_educ_pattern_to_inschool = TRUE;
        }
    }
}

void Person::SetCurrentEducLevelPatternStatus(int nSchoolSpell)
{
    EDUC_PATTERN    cPreviousPatternStatus = educ_pattern_status; //EN Store the current status

    //Calculate maximal number of spells for a given educ_fate and educ_pattern_number
    int nSumSpells = 0;
    for (int nIndex = 0; nIndex < SIZE(EDUC_PATTERN); nIndex++)
    {
        nSumSpells = nSumSpells + EducPattern[educ_fate][educ_pattern_number][nIndex];
    }

    if (nSchoolSpell == 0)                  // Not Started School
    {
        educ_status = ES_NEVER;
        educ_level  = EL5_LOW;
    }
    else if (nSchoolSpell > nSumSpells)     // Beyond max spells
    {
        educ_status = ES_FIN;               // Finished school
    }
    else                                    // Somewhere in system
    {
        // Find current educ_pattern_status
        bool bFound = FALSE;  int nColumn = 0;  int nItem = 0;  int nCount = 0;
        while (!bFound)
        {
            nItem++; nCount++;
            while (nItem > EducPattern[educ_fate][educ_pattern_number][nColumn])
            {
                nItem = 1; nColumn++;
            }
            if (nCount == nSchoolSpell)     // found current educ_pattern_status
            {
                bFound = TRUE;
                educ_pattern_status = ( EDUC_PATTERN )nColumn;
            }
        }
        // Update educ_status
        if (educ_pattern_status == EP_HIGH1_PT || educ_pattern_status == EP_HIGH2_PT
            || educ_pattern_status == EP_HIGH3_PT )
        {
            educ_status = ES_PARTTIME;
        }
        else if (educ_pattern_status == EP_MED_DUAL )
        {
            educ_status = ES_DUAL;
        }
        else if (educ_pattern_status == EP_OUT1 || educ_pattern_status == EP_OUT2
            || educ_pattern_status == EP_OUT3)
        {
            educ_status = ES_PAUSE;
        }
        else educ_status = ES_FULLTIME;
    }

    // Update current education level
    if (educ_status == ES_FIN) //End of studies reached
    {
        EDUC_LEVEL5 elEducLevel;
        if (educ_fate == EL3_LOW)
        {
            elEducLevel = EL5_LOW;
        }
        else if (educ_fate == EL3_HIGH)
        {
            elEducLevel = EL5_HIGH;
        }
        else if (educ_fate == EL3_MEDIUM && educ_level == EL5_LOW && cPreviousPatternStatus == EP_MED_DUAL)
        {
            elEducLevel = EL5_MEDIUMD;
        }
        else if (educ_fate == EL3_MEDIUM && educ_level == EL5_LOW && cPreviousPatternStatus == EP_MED_VOC)
        {
            elEducLevel = EL5_MEDIUMV;
        }
        else if (educ_fate == EL3_MEDIUM && educ_level == EL5_LOW && cPreviousPatternStatus == EP_MED_GEN)
        {
            elEducLevel = EL5_MEDIUMG;
        }
        else elEducLevel = educ_level;
        educ_level = elEducLevel;
    }
    // Change of school type to high or pause (must have finished medium)
    else if (educ_level == EL5_LOW && (educ_status == ES_PAUSE
        || educ_pattern_status == EP_HIGH1_FT || educ_pattern_status == EP_HIGH1_PT))
    {
        if (cPreviousPatternStatus == EP_MED_DUAL) educ_level = EL5_MEDIUMD;
        else if (cPreviousPatternStatus == EP_MED_VOC) educ_level = EL5_MEDIUMV;
        else if (cPreviousPatternStatus == EP_MED_GEN) educ_level = EL5_MEDIUMG;
    }
}

void Person::SchoolYearChange()
{
    if (integer_age >= SchoolEntryAge) SetCurrentEducLevelPatternStatus(integer_age - SchoolEntryAge + 1);
    if (educ_status == ES_FIN && year_finish_school > calendar_year) year_finish_school = calendar_year;
}

table Person TabEducTrade2  //EN TEST Trading school age 2
[integer_age == 2]
{
    educ_group + *
    sex + *
{
    duration(want_trade_educ_pattern_to_inschool,TRUE) / duration(), //EN Wants trade to inschool decimals=4
        duration(want_trade_educ_pattern_to_outschool,TRUE) / duration() //EN Wants trade to outschool decimals=4
}
*year_of_birth
};

table Person TabEducTrade3  //EN TEST Trading school age 3
[integer_age == 4]
{
    educ_group + *
    sex + *
    {
        duration(want_trade_educ_pattern_to_inschool,TRUE) / duration(), //EN Wants trade to inschool decimals=4
        duration(want_trade_educ_pattern_to_outschool,TRUE) / duration() //EN Wants trade to outschool decimals=4
    }
    *year_of_birth
};

table Person TabEducParents2  //EN TEST Education of parents
[integer_age == 2]
{
{
        duration(educ_parents,EL3_LOW) / duration(), //EN LOW decimals=4
        duration(educ_parents,EL3_MEDIUM) / duration(), //EN MED decimals=4
        duration(educ_parents,EL3_HIGH) / duration() //EN HIG decimals=4
}
*year_of_birth
};

Initialisations in the Start() function in PersonCore.mpp

The state in_school_startpop os initialized in the Start function for those coming from the starting population.


    // Initialize states
    if (person_type == PT_START) // Person comes from starting population
    {
        // (A) States from Starting population file
        time                = peObservation->pmc[PMC_BIRTH] + RandUniform(2);
        sex                 = (SEX)(int)peObservation->pmc[PMC_SEX];
        family_role         = (FAM_ROLE)( int )peObservation->pmc[PMC_ROLE];
        educ_startpop       = (EDUC_LEVEL5)(int)peObservation->pmc[PMC_EDUC];
        in_school_startpop  = (bool)(int)peObservation->pmc[PMC_INSCHOOL];
//