5.21. Step 21: Analysis Files Output

5.21.1. Model Description

At this step we add modules to produce simulated analysis files of the same formats as used to create the starting population and to estimate all parameters. This output can be used for retrospective model validation as well as for the creation of synthetic model test data. The module is optional and can be deactivated by the user.

5.21.2. The AnalysisDataOutput.mpp Module

This module implements micro-data output of analysis files in the format required to create a model starting population and to estimate all parameters. Four files are created:

  • Residents: a synthetic future census
  • Emigrants: emigrants of the past 12 months
  • Births: retrospective birth histories
  • Children: retrospective child records of births in the past 5 years for the analysis of mortality and child vaccination

Model users have the following choices:

  • Switching the file output on/off
  • Setting a point in time for the output
  • Selecting file names for the four output files

This module is optional. For efficiency reasons, an additional actor AnalysisRecord is introduced which contains most states introduced in this module. If analysis data output is de-activated by the user, no such actors are created saving memory space.




////////////////////////////////////////////////////////////////////////////////////////////////////
// Links
////////////////////////////////////////////////////////////////////////////////////////////////////

link Person.lHHMother Person.mlHHMotherChildren[];      //EN HH Mother - Children
link Person.lAnalysisRecord AnalysisRecord.lPerson;     //EN Link between analysis record and person

////////////////////////////////////////////////////////////////////////////////////////////////////
// Files
////////////////////////////////////////////////////////////////////////////////////////////////////

output_csv residents_csv;               //EN Residents file
output_csv emigrants_csv;               //EN Emigrants file
output_csv births_csv;                  //EN Births records file
output_csv children_csv;                //EN Child history file

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

range BIRTH_INDEX { 0, 13 };            //EN Birth Index

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

parameters
{
    file    AnalysisFileNameResidents;                      //EN File name: Residents
    file    AnalysisFileNameEmigrants;                      //EN File name: Emigrants
    file    AnalysisFileNameBirths;                         //EN File name: Births
    file    AnalysisFileNameChildren;                       //EN File name: Children

    logical AnalysisFileOutputYN;                           //EN Analysis file output Y/N
    double  AnalysisFileOutputTime;                         //EN Time of analysis file output
};

parameter_group PG_AnalysisFileOutput                       //EN Analysis File Output
{
    AnalysisFileOutputYN, AnalysisFileOutputTime,
    AnalysisFileNameResidents, AnalysisFileNameEmigrants,
    AnalysisFileNameBirths, AnalysisFileNameChildren
};

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

actor Person
{

    logical analysis_output_is_done = { FALSE };            //EN Analysis file output is done
    event   timeAnalysisOutputEvent, AnalysisOutputEvent;   //EN Analysis file output event

    void    WriteAnalysisRecordEmigrant();                  //EN Write emigrants record
    void    WriteAnalysisRecordChild();                     //EN Write child record
    hook    WriteAnalysisRecordChild, MortalityEvent;       //EN Hook to Mortality

    void    CreateAnalysisRecord();                         //EN Create analysis record
    hook    CreateAnalysisRecord, SetAliveEvent;            //EN Hook to Set Alive

    void    RemoveAnalysisRecord();                         //EN Remove analysis record
    hook    RemoveAnalysisRecord, Finish;                   //EN Hook to Finish
};

actor AnalysisRecord
{
    long            pers_hh_id = { -1 };                    //EN Household ID
    REGION_INT      pers_rob = { REGI_ABROAD };             //EN Region of birth
    TIME            age_at_marriage = { 999 };              //EN Age at marriage
    TIME            age_last_birth = { 999 };               //EN Age at last birth
    TIME            age_last_move = { 999 };                //EN Age at last move
    REGION_INT      previous_region = { REGI_ABROAD };      //EN Previous Region
    DISTRICT_INT    previous_district = { DISTI_ABROAD };   //EN Previous District

    // Birth records variables
    int             month_birth[BIRTH_INDEX];               //EN Month of births 1..14

    void            Start(Person *prPerson);                //EN Start
    void            Finish();                               //EN Finish
};

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

void Person::CreateAnalysisRecord()
{
    if (AnalysisFileOutputYN)
    {
        auto prAnalysisRecord = new AnalysisRecord();
        prAnalysisRecord->Start(this);
    }
}

void Person::RemoveAnalysisRecord()
{
    if (AnalysisFileOutputYN && lAnalysisRecord) lAnalysisRecord->Finish();
}

void AnalysisRecord::Start(Person *prPerson)
{
    lPerson = prPerson;
    pers_rob = lPerson->region_int;
    for (int nIndex = 0; nIndex < SIZE(BIRTH_INDEX); nIndex++) month_birth[nIndex] = 9999;
}

void AnalysisRecord::Finish(){}

TIME Person::timeAnalysisOutputEvent()
{
    if (AnalysisFileOutputYN && !analysis_output_is_done && GetReplicate()==0
        && AnalysisFileOutputTime >= MIN(SIM_YEAR_RANGE) )
    {
        return AnalysisFileOutputTime;
    }
    else return TIME_INFINITE;
}

void Person::WriteAnalysisRecordEmigrant()
{
    emigrants_csv << actor_weight;                                  // weight
    emigrants_csv << (int)DISTI_ABROAD;                             // district (abroad)
    emigrants_csv << (int)district_nat;                             // previous district of residence
    emigrants_csv << (int)region_nat;                               // previous region of residence
    emigrants_csv << age;                                           // age
    emigrants_csv << (int)sex;                                      // sex

    emigrants_csv.write_record();                                   // write the record
}

void Person::WriteAnalysisRecordChild()
{
    if (is_alive && is_resident && lHHMother && lHHMother->age < 50 - (AnalysisFileOutputTime-time))
    {
        int nMonthDeath = 9999;
        if (!analysis_output_is_done) nMonthDeath = (int)((time - 1900) * 12);

        children_csv << actor_weight;                                   // weight
        children_csv << (int)((AnalysisFileOutputTime - 1900) * 12);    // Month of interview
        children_csv << (int)(lHHMother->region_nat);                   // Region
        children_csv << (int)((time - age - 1900) * 12);                // month of birth
        children_csv << nMonthDeath;                                    // month of death
        children_csv << (int)sex;                                       // sex
        children_csv << (int)(mother_age_at_birth * 12);                // Mothers age at birth (months)
        children_csv << (int)educ_mother;                               // Primary education of mother
        children_csv << (int)ethnicity;                                 // Ethnicity
        children_csv << (int)is_immunized;                              // Child is vaccinated
        children_csv << (int)(lHHMother->district_nat);                 // District
        children_csv << (int)got_prenat_care;                           // Mother received prenatal care
        children_csv << (int)is_stunted;                                // Stunted


        children_csv.write_record();                                    // write the record
    }
}



void Person::AnalysisOutputEvent()
{
    // Never do again
    analysis_output_is_done = TRUE;

    ///////////////////////////////////////////////////////////////////////////////////////////////
    // Write Resident File
    ///////////////////////////////////////////////////////////////////////////////////////////////

    if (is_resident && is_alive)
    {
        // Calculate some variables
        // Household ID
        long nHhId = actor_id;
        if (lHHMother && age < 18 && !in_union && parity == 0)    // A child knowing her mother
        {
            nHhId = lHHMother->actor_id;
        }
        // Age last birth (check if infor from staring population relevant)
        if (time_since_last_birth_start > 0.0)
        {
            lAnalysisRecord->age_last_birth = age - time_since_last_birth_start - (time - MIN(SIM_YEAR_RANGE));
        }
        // Birth past 12 months
        bool bBirth12 = FALSE;
        if (age - lAnalysisRecord->age_last_birth >= 0.0 && age - lAnalysisRecord->age_last_birth <= 1.0)
        {
            bBirth12 = TRUE;
        }
        // District and region 12 months age
        int nLastDistrict = (int)district_int;
        int nLastRegion = (int)region_int;
        if (age - lAnalysisRecord->age_last_move <= 1.0 && age - lAnalysisRecord->age_last_move >= 0.0)
        {
            nLastDistrict = (int)lAnalysisRecord->previous_district;
            nLastRegion = (int)lAnalysisRecord->previous_region;
        }
        // Push the fields into the output record.
        residents_csv << actor_id;                              // Actor ID
        residents_csv << nHhId;                                 // Household ID
        residents_csv << actor_weight;                          // Weight
        residents_csv << age;                                   // Age
        residents_csv << (int)sex;                              // Sex
        residents_csv << (int)district_birth;                   // District of birth
        residents_csv << (int)district_int;                     // District of residence
        residents_csv << nLastDistrict;                         // District 12 months ago
        residents_csv << (int)tab_primary_level;                // Education
        residents_csv << (int)parity;                           // Parity
        residents_csv << (int)bBirth12;                         // Births past 12 months
        residents_csv << lAnalysisRecord->age_at_marriage;      // Age at marriage
        residents_csv << lAnalysisRecord->age_last_birth;       // Age at most recent birth
        residents_csv << (int)lAnalysisRecord->pers_rob;        // Region of birth
        residents_csv << (int)region_int;                       // Region of residence
        residents_csv << nLastRegion;                           // Region 12 months ago
        residents_csv << (int)ethnicity;                        // Ethnicity

        residents_csv.write_record();
    }

    ///////////////////////////////////////////////////////////////////////////////////////////////
    // Write Births File
    ///////////////////////////////////////////////////////////////////////////////////////////////


    if (is_resident && is_alive && sex == FEMALE && age >= 15 && age < 50)
    {
        int nMonthMarriage = 9999;
        if (lAnalysisRecord->age_at_marriage > 5 && lAnalysisRecord->age_at_marriage < age)
        {
            nMonthMarriage = int((time - age + lAnalysisRecord->age_at_marriage - 1900) * 12);
        }

        for (int nIndex = 0; nIndex < SIZE(BIRTH_INDEX); nIndex++)
        {
            births_csv << lAnalysisRecord->month_birth[nIndex];     // Months of births
        }
        births_csv << actor_weight;                                 // Weight
        births_csv << int((time - age - 1900) * 12);                // Month of own birth
        births_csv << (int)tab_primary_level;                       // Primary education
        births_csv << (int)region_int;                              // Region of residence
        births_csv << (int)((time - 1900) * 12);                    // Month of interview
        births_csv << nMonthMarriage;                               // Age at marriage
        births_csv << (int)district_int;                            // District

        births_csv.write_record();
    }

    ///////////////////////////////////////////////////////////////////////////////////////////////
    // Write Child File
    ///////////////////////////////////////////////////////////////////////////////////////////////

    WriteAnalysisRecordChild();
}

////////////////////////////////////////////////////////////////////////////////////////////////////
// Pre- and Post-Simulation
////////////////////////////////////////////////////////////////////////////////////////////////////

void PreSimulation()
{
    // In the pre-simulation phase the micro-data file is prepared
    // for writing records in the simulation. If the user selects data output, a file is
    // opened and the header row is written containing all selected states

    if (AnalysisFileOutputYN)
        {
        // residents
        std_string        residentString = "M_ID,";             // ID
        residentString  = residentString + "M_HHID,";           // Household ID
        residentString  = residentString + "M_WEIGHT,";         // Weight
        residentString  = residentString + "M_AGE,";            // Age
        residentString  = residentString + "M_MALE,";           // Sex
        residentString  = residentString + "M_DOB,";            // District of birth
        residentString  = residentString + "M_DOR,";            // District of residence
        residentString  = residentString + "M_PDIST,";          // District 12 months ago
        residentString  = residentString + "M_EDUC,";           // Education
        residentString  = residentString + "M_PARITY,";         // Parity
        residentString  = residentString + "M_BIR12,";          // Births past 12 months
        residentString  = residentString + "M_AGEMAR,";         // Age at marriage
        residentString  = residentString + "M_AGEBIR,";         // Age at most recent birth
        residentString  = residentString + "M_ROB,";            // Region of birth
        residentString  = residentString + "M_ROR,";            // Region of residence
        residentString  = residentString + "M_PREG,";           // Region 12 months ago
        residentString  = residentString + "M_ETHNO,";          // Ethnicity

        residents_csv.open(AnalysisFileNameResidents);
        residents_csv.precision(9);
                residents_csv.write_header(residentString);

        // emigrants
        std_string        emigrantString = "M_WEIGHT,";         // weight
        emigrantString  = emigrantString + "M_DOR,";            // district of residence
        emigrantString  = emigrantString + "M_PDIST,";          // previous district
        emigrantString  = emigrantString + "M_PREG,";           // previous region
        emigrantString  = emigrantString + "M_AGE,";            // Age
        emigrantString  = emigrantString + "M_MALE,";           // Sex

        emigrants_csv.open(AnalysisFileNameEmigrants);
        emigrants_csv.precision(9);
        emigrants_csv.write_header(emigrantString);

        // births
        std_string        birthString = "M_B01,";               // Month birth 01
        birthString     = birthString + "M_B02,";               // Month birth 02
        birthString     = birthString + "M_B03,";               // Month birth 03
        birthString     = birthString + "M_B04,";               // Month birth 04
        birthString     = birthString + "M_B05,";               // Month birth 05
        birthString     = birthString + "M_B06,";               // Month birth 06
        birthString     = birthString + "M_B07,";               // Month birth 07
        birthString     = birthString + "M_B08,";               // Month birth 08
        birthString     = birthString + "M_B09,";               // Month birth 09
        birthString     = birthString + "M_B10,";               // Month birth 10
        birthString     = birthString + "M_B11,";               // Month birth 11
        birthString     = birthString + "M_B12,";               // Month birth 12
        birthString     = birthString + "M_B13,";               // Month birth 13
        birthString     = birthString + "M_B14,";               // Month birth 14
        birthString     = birthString + "M_WEIGHT,";            // Weight
        birthString     = birthString + "M_BIRTH,";             // Month of own birth
        birthString     = birthString + "M_EDUC,";              // Primary Education
        birthString     = birthString + "M_REG,";               // Region
        birthString     = birthString + "M_INTERV,";            // Month of interview
        birthString     = birthString + "M_MAR,";               // Month of first marriage
        birthString     = birthString + "M_DOR,";               // District

        births_csv.open(AnalysisFileNameBirths);
        births_csv.precision(9);
                births_csv.write_header(birthString);

        // Children
        std_string        childString = "M_WEIGHT,";            // Weight
        childString     = childString + "M_INTERV,";            // Month of interview
        childString     = childString + "M_REGION,";            // Region
        childString     = childString + "M_BIRTH,";             // Month of birth
        childString     = childString + "M_DEATH,";             // Month of death
        childString     = childString + "M_MALE,";              // Sex
        childString     = childString + "M_AGEMO,";             // Age of mother (months) at birth
        childString     = childString + "M_EDUCMO,";            // Education of mother
        childString     = childString + "M_ETHNO,";             // Ethnicity
        childString     = childString + "M_VACC,";              // Immunization
        childString     = childString + "M_DOR,";               // District
        childString     = childString + "M_PCARE,";             // Prenatal care
        childString     = childString + "M_STUNTED,";           // Stunted

        children_csv.open(AnalysisFileNameChildren);
        children_csv.precision(9);
        children_csv.write_header(childString);
    }
}

void PostSimulation()
{
    // close files
    if (AnalysisFileOutputYN)
    {
        residents_csv.close();
        emigrants_csv.close();
        births_csv.close();
        children_csv.close();
    }
}

5.21.3. Changes in PersonCore.mpp


    else if (person_type == PT_CHILD) // Person born in simulation
    {
        // Code added at step 21
        lHHMother = pePers; //EN Link Person to HH Mother
        if (lHHMother->lAnalysisRecord)
        {
            lHHMother->lAnalysisRecord->age_last_birth = lHHMother->age;
            if (lHHMother->parity <= SIZE(BIRTH_INDEX))
            {
                int nMonth = int((time - 1900) * 12);
                lHHMother->lAnalysisRecord->month_birth[lHHMother->parity - 1] = nMonth;
            }
        }

5.21.4. Changes in MigrationAgeSex.mpp


void Person::MigrationEvent()
{
    int nDestination;
    int nAge5 = SPLIT(integer_age, AGE5_PART);

    // Code added at step 21
    if (lAnalysisRecord)
    {
        lAnalysisRecord->age_last_move = age;
        lAnalysisRecord->previous_district = district_int;
        lAnalysisRecord->previous_region = region_int;
    }
    // End 21

    // Sample the destination
    Lookup_MigrationDestination(RandUniform(6), sex, district_nat, nAge5, &nDestination);

    // Move the actor to the destination
    district_int = (DISTRICT_INT)nDestination;

    // Updates indicators
    age_at_last_move = integer_age;
    number_migrations++;
}

5.21.5. Changes in ImmigrationAgeSexDistrict.mpp


void Person::ImmigrationEvent()
{
    // Code added at step 21
    if (lAnalysisRecord)
    {
        lAnalysisRecord->age_last_move = age;
        lAnalysisRecord->previous_district = district_int;
        lAnalysisRecord->previous_region = region_int;
    }
    // End 21

    district_int = (DISTRICT_INT)(int)district_immi;
    ever_resident = TRUE;
    time_of_immigration = TIME_INFINITE;
    IMPLEMENT_HOOK();
}

5.21.6. Changes in FirstUnionCoaleMcNeil.mpp


void Person::Union1FormationEvent()
{
    in_union = TRUE;
    // Code added at step 21
    if (lAnalysisRecord) lAnalysisRecord->age_at_marriage = age;
}

5.21.7. Changes in EmigrationAgeSexDistrict.mpp


void Person::EmigrationEvent()
{
    // Code added at step 20
    if (lAnalysisRecord && AnalysisFileOutputTime - time >= 0.0 && AnalysisFileOutputTime - time <= 1.0)
    {
        WriteAnalysisRecordEmigrant();
    }