5.1. Step 1: Creating a Population

The DYNAMIS-POP model development starts from a very generic basic template for typical continuous-time interacting population models. The model creates a starting population from a weighted file of observations. While not having much functionality at this point, the model provides a starting point ‘hiding’ some of the more tricky coding. Newcomers to Modgen are not required to fully understand all the code at this point: the following steps of model development introduce the basic concepts from a modelers perspective and provide step-by-step instructions for model building using Modgen.

5.1.1. Model Description

DYNAMIS-POP Step 1 constitutes the starting template of the model. It implements some basic functionality common to most interacting population models like file handling, the creation of a starting population, and the handling of weights to automatically scale all output to the total population size. The model creates a starting population from a weighted file of observations. Depending on record weights and the desired simulation size, a population of simulation actors of equal weights is created from the observations, whereby individual observations might or might not be picked or picked various times. The template also supports linking people to families or households. Other functionality includes the production of sample output tables and a module for micro-data output for generating simulated cross-section and panel data. Most of the code does not require any change as the model is developed. Exceptions are:

  • The list of variables of the starting population file set in the ObservationCore file.
  • The use of the starting population variables to initialize the states of the simulated actors in the Start() function contained in the PersonCore file.
  • The list of states to be included in the model micro-data output file set in the MicroDataOutput file.

5.1.2. File input

The micro-data population input file is a csv-file which currently contains the following variables:

 List of Starting population variables
classification PERSON_MICRODATA_COLUMNS
{
    // (uncomment when modeling families)   PMC_HHID,   //EN Household ID
    // (uncomment when modeling families)   PMC_ROLE,   //EN Role in family
    PMC_WEIGHT,                                         //EN Weight
    PMC_BIRTH,                                          //EN Time of birth
    PMC_SEX,                                            //EN Sex
    PMC_PROVINCE,                                       //EN Province
    PMC_EDUC,                                           //EN Education
    PMC_POB,                                            //EN Province of birth
    PMC_UNION,                                          //EN Union formation time
    PMC_PARITY,                                         //EN Number of births
    PMC_LASTBIR,                                        //EN Time of last birth
    PMC_ETHNO                                           //EN Ethnicity
}

The variables, including their order, have to correspond with the variables in the starting population file. The list can simply be expanded as required.

5.1.3. Parameters

The model currently has 7 parameters organized into 2 groups: Starting population and Microdata output.

../_images/Step01_Para.png

Figure: Model Parameters

The starting population parameters are:

  • The file name
  • The file size
  • The simulated sample size (which can be bigger or smaller than the file)
  • The real population size (to which all output is automatically scaled)

The starting population is generated from a weighted file of observations. Depending on weights and the desired simulation size, a population of actors is created from the observations, whereby individual observations might or might not be picked or picked various times.

Parameters for micro-data output are:

  • A check-box to switch output on/off
  • A file-name for the output file
  • Parameters for the timing of the first and the last output and output intervals

Users can select when output should be written, which can be a single point in time or repeated panel waves with regular intervals.

5.1.4. Model Output

At this point, the model creates 3 output table as well as a micro-data output file. File output is generated in csv format including a header row containing variable names. Tables are part of the graphical user interface and there are various options to copy/paste or export tables.

5.1.5. Code organization and a classification of files

Modgen code is organized in .mpp files which can be classified into 4 groups.

Simulation engine: these files contain general model settings and the the code to run a simulation. Most of the code was automatically generated by the Modgen model wizard for time-based models. The code is mostly pure C++ and developers typically are not required to modify – or understand – it. The model is designed to run for various countries. All country and version specific code (like start year) are kept in a separate file _CountryContext.mpp.

  • model_core.mp
  • model_info.mpp
  • modgen_timebased
  • _CountryContext.mpp

Actor core files: Each Actor introduced in the model typically has a core file which contains general characteristics and functions of the actor. Our model at this point has four actor types, Observations (one for each record of the starting population file), Persons (the actual simulated entities representing the population), a Clock, and an actor Globals (for storing information easily accessible for all actors). Accordingly, there are four core files:

  • ObservationCore.mpp
  • PersonCore.mpp
  • ClockCore.mpp
  • GlobalsCore.mpp

Regular modules: These are the modules developers typically spend most of their time on. Usually there is one such module by modeled behavior (like fertility, mortality, labor force participation) and policy system. At the moment, our model contains just one such module:

  • CalendarYearChange.mpp

Output modules: Modgen models produce three types of output: tables, micro-data, and a database of tracked actors used for a visualization tool called BioBrowser. The template provides two output files:

  • Tables.mpp: contains all table output
  • MicroDataOutput.mpp: handles micro-data output

Besides the model code, modes come with one or mode parameter files, as well as micro-data input and output files.

Parameter .dat files: values are stored in one or more .dat files, which are readable text-files. Parameters are typically changed within the graphical user interface of the model.

  • Default(PersonCore).dat: contains all model parameters. The file-name consists of a scenario name (Default) and the parameter file name. A user creating and saving a new scenario would generate a new file, e.g. NewScenario(PersonCore).

Microdata csv input and output: Micro-data in/output is organized in csv (comma separated values) files, which are readable text files. File-names can be chosen by the model user, e.g.

  • startpop_YYYY_COUNTRY.csv
  • micro_output_YYYY_COUNTRY.csv

5.1.6. Model Documentation

Modgen models are self-documenting: Users can access a help file from the menu of the user interface.

Labels and notes for modules (as well as any symbols used in the program) are used for the automatically generated model help file for users, thus good documentation is not only best practice for model development, but also creates a detailed documentation for the user.

  • A label: a one line description
  • A note: a more detailed description

Example:

//LABEL (CalendarYearChange, EN) Calendar Year Change

/*
   NOTE (CalendarYearChange, EN)
   This module handles calendar year changes. Before a year ends, the Person function YearEnd()
   is called by the Calendar clock actor. This is a point in time in which models typically
   update accounts and perform other end of year routines. Immediately after the year end, the
   YearStart() routine is called. This is when the calendar_year state is changed.
*/

Placed above or in the same line of a newly introduced symbol, labels can also be written as

//EN The text of the label

Labels are introduced by a language code (EN for English in our case) Modgen also support multilingual applications, in which these labels are translated into (an)other language(s). Besides their purpose of code documentation, these labels are used in the user interface, e.g. for column headings in tables or for the labeling of parameters.

../_images/Step01_Help.png

Figure: Help System

5.1.7. Code Discussion

5.1.7.1. Model Information: model_info.mpp

This file is used for model documentation and contains a model description. The file is for documentation only and does not contain any model code.

5.1.7.2. The Simulation Engine: model_core.mpp

This module contains general model settings and the simulation engine of the model. It is part of the model template and developers typically are not required to modify or understand it’s code. Model-specific code includes:

  • The creation of Observation actors reading the starting population file
  • The calculation of integer weights corresponding with the selected number of simulated actors
  • The creation of a starting population of equal weight Person actors
  • The removal of all Observation actors as they are not used anymore after the starting population was built.


////////////////////////////////////////////////////////////////////////////////////////////////////
// General Model Settings
////////////////////////////////////////////////////////////////////////////////////////////////////

model_type time_based, just_in_time;        // The model type
options packing_level = 2;                  // Reduces memory use at the expense of speed
time_type double;                           // The data type used to represent time

// Other data types
real_type               float;
counter_type    ushort;
integer_type    short;
index_type              ulong;

languages                                   // Supported languages
{
    EN // English
};


////////////////////////////////////////////////////////////////////////////////////////////////////
// Simulation() is called by the simulation framework to simulate a replicate
////////////////////////////////////////////////////////////////////////////////////////////////////

void Simulation()
{
    extern void LogSimulationStart();
    extern void SimulateEvents();
    LogSimulationStart();                   // Write simulation start message to log

    ////////////////////////////////////////////////////////////////////
    // Start of the model-specific part of the simulation engine      //
    ////////////////////////////////////////////////////////////////////

    // Create and open the input data file
    input_csv in_csv;
    in_csv.open(MicroDataInputFile);
    in_csv.read_header();

    // Create the Actor Globals
    auto prGlobals = new Globals();
    prGlobals->Start();

    // Create the Clock
    auto prClock = new Clock();
    prClock->Start();

    // Create observations
    for (long nJ = 0; nJ < MicroDataInputFileSize; nJ++)
    {
        in_csv.read_record(nJ);
        auto paObservation = new Observation();
        paObservation->Start(in_csv);
    }

    // Set sample weights (obs_weight) in the observations to represent the (integer) number of
    // persons to be created out of each observation
    double dSumWeights = 0.0;

    for (long nJ = 0; nJ < MicroDataInputFileSize; nJ++)
    {
        dSumWeights = dSumWeights + asObservationAll->Item(nJ)->pmc[PMC_WEIGHT];
    }

    for (long nJ = 0; nJ < MicroDataInputFileSize; nJ++)
    {
        double  dWeight = asObservationAll->Item(nJ)->pmc[PMC_WEIGHT]
                    * StartPopSampleSize / dSumWeights;
        int     nWeight = (int)dWeight;
        if (RandUniform(1) < dWeight - nWeight) nWeight++;
        asObservationAll->Item(nJ)->obs_weight = nWeight;
    }

    // Calculate exact person weight: A small correction as all family members eventually inherit
    // the weight of the head which can be different from the integer weights assigned above
    long SumSimActors = 0.0;
    for (long nH = 0; nH < asObservationHeads->Count(); nH++)
    {
        // Number family members
        int nFamSize = 1 + asObservationNonHeads[asObservationHeads->Item(nH)->fam_id]->Count();
        // sum of person weights in family with all having same weight as head
        SumSimActors = SumSimActors + nFamSize * asObservationHeads->Item(nH)->obs_weight;
    }
    asGlobals->Item(0)->person_weight = StartPopSize/ SumSimActors;

    // Create the starting population
    while (asObservationHeads->Count() > 0)
    {
        // First create the head
        auto paObservation = asObservationHeads->Item(0);
        auto paPerson = new Person();
        paPerson->Start(paObservation, NULL);
        // And now all other members of this family
        for (int nJ = 0; nJ < asObservationNonHeads[paObservation->fam_id]->Count(); nJ++)
        {
            auto paOtherPerson = new Person();
            paOtherPerson->Start(asObservationNonHeads[paObservation->fam_id]->Item(nJ), paPerson);
        }
        paObservation->obs_weight--;
    }

    // Delete all observation actors
    while (asObservationAll->Count() > 0) asObservationAll->Item(0)->Finish();

    // Close the microdata input file
    in_csv.close();

    ////////////////////////////////////////////////////////////////////
    //   End of the model-specific part of the simulation engine      //
    ////////////////////////////////////////////////////////////////////


    SimulateEvents();                       // Simulate events until there are no more.
}

5.1.7.3. Modgen Simulation Framework: modgen_time_based.mpp

This module implements core global functions for time-based models. It should not be necessary to modify any code in this module.

5.1.7.4. The Country/Version-Specific Settings: _CountryContext

This file contains the country and starting year specific code. This is the only file to be adapted when porting the model to a new country context.



// COUNTRY: ABC 2000

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

range ALL_YEAR_RANGE { 1899, 2100 };             //EN All calendar years
range SIM_YEAR_RANGE { 2000, 2100 };             //EN Simulated years

5.1.7.5. Core file of the Observation Actor : ObservationCore.mpp

This module introduces an actor named ‘Observation’. Each Observation actor corresponds with a record of the starting population file and is created by the simulation engine before Person actors are created. Observation are weighted. According to the size of the simulated population selected by the user, the simulation engine creates integer-weights for each observation by rescaling the original weights and random-rounding. The new observation weights are then used by the simulation engine to decide if and how often an observation is used when creating Person actors (which clone the observation characteristics). After being used for creating the starting population, the observation actors are destroyed to free up teh memory space. Note that for this process, the weight of the household head is used for all members of a family.

This module is part of the model template and developers typically are not required to modify – or understand – it’s code. The exception is adding new variables to the starting population file which is a frequent requirement in model development:

How to add a new variable to the starting population?

  • Add a new column to the csv micro-data file
  • in this module, extend the list of fields in the classification ‘PERSON_MICRODATA_COLUMNS’ no more coding is required and the variable is read in automatically
  • the new variable will typically be used in the Start() function of the Person actor where a corresponding state is initialized by its value. It is accessible using the new dimension name added to the list of fields as an index of the person-micro-column array pmc[NEW_FIELD]. See Code in the Start() function in PersonCore.mpp


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

// LABEL(PERSON_MICRODATA_COLUMNS, EN) List of Starting population variables
classification PERSON_MICRODATA_COLUMNS
{
    // (uncomment when modeling families)   PMC_HHID,   //EN Household ID
    // (uncomment when modeling families)   PMC_ROLE,   //EN Role in family
    PMC_WEIGHT,                                         //EN Weight
    PMC_BIRTH,                                          //EN Time of birth
    PMC_SEX,                                            //EN Sex
    PMC_PROVINCE,                                       //EN Province
    PMC_EDUC,                                           //EN Education
    PMC_POB,                                            //EN Province of birth
    PMC_UNION,                                          //EN Union formation time
    PMC_PARITY,                                         //EN Number of births
    PMC_LASTBIR,                                        //EN Time of last birth
    PMC_ETHNO                                           //EN Ethnicity
}
;

range FAM_ID{ 0,220000 };                       //EN Range of Family IDs

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

parameters
{
    file    MicroDataInputFile;                 //EN File name of starting population
    long    MicroDataInputFileSize;             //EN File size of starting population
    double  StartPopSampleSize;                 //EN Simulated sample size of starting population
    double  StartPopSize;                       //EN Real population size
};

parameter_group PG_ModelSettings                //EN Starting population
{
    MicroDataInputFile, MicroDataInputFileSize,
    StartPopSampleSize, StartPopSize
};

////////////////////////////////////////////////////////////////////////////////////////////////////
// Actor-Sets
////////////////////////////////////////////////////////////////////////////////////////////////////

//EN Actor-set of all family heads in Observations
actor_set Observation asObservationHeads filter fam_role == FR_HEAD && obs_weight > 0;

//EN Actor-set of all family members (without heads) by family ID
actor_set Observation asObservationNonHeads[fam_id] filter fam_role != FR_HEAD;

//EN All observations
actor_set Observation asObservationAll;

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

/*  NOTE(Observation, EN)
    The Actor Observation is created as internal representation of the starting population file
    records. It is used to create Person actors of the starting population which can be smaller or
    larger as the starting population file. The weights of observations are used for determining if
    and how often a single observation is represented in the starting population.
*/


actor Observation                               //EN Actor Observations
{
    double      pmc[PERSON_MICRODATA_COLUMNS];  //EN Person micro record columns
    integer     obs_weight = { 1 };             //EN Observation integer weight
    FAM_ID      fam_id = { 0 };                 //EN Family ID
    FAM_ROLE    fam_role = { FR_HEAD };         //EN Role in family

    void        Start(const input_csv& input);  //EN Function starting the actor
    void        Finish();                       //EN Function destroying the actor
};

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

void Observation::Start(const input_csv& in_csv)
{
    for (int nJ = 0; nJ < SIZE(PERSON_MICRODATA_COLUMNS); nJ++)
    {
        pmc[nJ] = in_csv[nJ];
    }
    // (uncomment when modeling families) fam_id      = (int)pmc[PMC_HHID];
    // (uncomment when modeling families) fam_role    = (FAM_ROLE)(int)pmc[PMC_ROLE];
};

void Observation::Finish(){};

5.1.7.6. Core file of the Person Actor: PersonCore.mpp

This module introduces the main actor of the model: Person. It contains the core functionality and general states of the actor Person. Each actor typically has a ‘Core’ module for handling general functionalities of the actor. The most essential functions of this module are the Start() and Finish() functions of the actor. Start() is called when an actor is created and initializes all states. It is the only place where the automatically provided states age and time can be set.

As for any actor, its core file is a good place to declare states and corresponding types which do not belong to specific behaviors but are used by various modules (e.g. sex). Also, the Start() and Finish() functions of each actor are declared in its core file.



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

classification SEX                              //EN Sex
{
    FEMALE,                                     //EN Female
    MALE                                        //EN Male
};

classification PERSON_TYPE                      //EN Person Type
{
    PT_START,                                   //EN Person from Starting Population
    PT_CHILD,                                   //EN Person born in simulation
    PT_IMMIGRANT                                //EN Immigrant
};

classification FAM_ROLE                         //EN Role in family
{
    FR_HEAD,                                    //EN Head
    FR_SPOUSE,                                  //EN Spouse of head
    FR_CHILD                                    //EN Child
};

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

/* (uncomment when modeling families)
link Person.lParent Person.mlChild[];               //EN Link between Head and Children
link Person.lSpouse;                                    //EN Link between spouses
*/

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

actor_set Person asAllPersons filter is_alive;  //EN Entire population

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

/*      NOTE(Person, EN)
    The actors Person are the main actors of the simulation. All persons have the
    same weight and together represent the population.
*/

actor Person
{
    PERSON_TYPE     person_type = { PT_START };                     //EN Person type
    double          time_of_birth = { 1900 };                       //EN Time of birth
    ALL_YEAR_RANGE  year_of_birth = int(time_of_birth);             //EN Year of birth
    SEX             sex = { FEMALE };                               //EN Sex
    FAM_ROLE        family_role = { FR_HEAD };                      //EN Family Role
    logical         ever_resident = { TRUE };                       //EN Ever Resident

    void            Start(Observation *peObservation, Person *pePers);  //EN Starts the actor
          void            Finish();                                           //EN Finishes the actor

    logical         is_alive = { FALSE };                           //EN Person is alive
    double          time_set_alive = { TIME_INFINITE };             //EN Time setting actor alive
    event           timeSetAliveEvent, SetAliveEvent;               //EN Set Alive

    //EN The simulation has entered the projected time
    logical         in_projected_time = (calendar_year >= MIN(SIM_YEAR_RANGE));

    Person          *peHHead;                                       //EN Pointer to household head
};

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

/*      NOTE(Person.Start, EN)
    The Start() function initializes all states of an actor right at the moment of creation.
    The function has two parameters, namely pointers to an observation actor and to the household
    head. If the pointer to an observation is not NULL, the Person comes from the starting
    population (rather than being born or immigrating in the simulation). The pointer is used to
    access all states of the observation. The pointer to the household head is used to establish a
    link to the family head which can be a parent or a spouse. If the parameter is NULL, the Person
    is a family head herself.
*/

void Person::Start(Observation *peObservation, Person *pePers)
{
    // Setting the actor weight
    Set_actor_weight(asGlobals->Item(0)->person_weight);
    Set_actor_subsample_weight(asGlobals->Item(0)->person_weight);

    // Determine the person type
    if (!peObservation && pePers)   person_type = PT_CHILD;   // Born in simulation
    else if (peObservation)         person_type = PT_START;   // From Starting Pop
    else person_type = PT_IMMIGRANT;                          // Immigrant

    // 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;

        // (B) Other states
        time_of_birth       = time;
        calendar_year       = (int)time_of_birth;

        // (C) Links to head resp. spouse
        // (uncomment when modeling families) if (pePers) peHHead = pePers;
    }
    else if (person_type == PT_CHILD) // Person born in simulation
    {
        // do nothing (children not modeled yet)
    }
    else // Person is an immigrant
    {
        // do nothing (immigrants not modeled yet)
    }
    time_set_alive = WAIT(0);
}

/*      NOTE(Person.Finish, EN)
    Finish is a function to be called when an actor is to be destroyed. Modgen automatically
    performs memory clean-up routines. Code added here is the last thing executed before an actor
    is destroyed. Typical uses are the handling of inheritances and accounting routines.
*/
void Person::Finish(){}


/*  NOTE(Person.SetAliveEvent, EN)
    This function is called with waiting time 0 immediately after the Start of a Person actor.
    For people of the starting population the date of birth is only known after getting this
    information from the corresponding person record, thus after the person actor is created.
    The SetAliveEvent Event ensures that no person is visible and counted before its actual
    birth date.
*/

TIME Person::timeSetAliveEvent() { return time_set_alive; }
void Person::SetAliveEvent()
{
    lClock         = asTheClock->Item(0);       // link the person to the clock
    lGlobals       = asGlobals->Item(0);        // link the person to globals

    is_alive       = TRUE;                      // set the Person alive
    time_set_alive = TIME_INFINITE;             // ensure the event does not happen again


    /* (uncomment when modeling families)
    if (person_type == PT_START && family_role != FR_HEAD && peHHead)
    {
        if (family_role == FR_CHILD) lParent = peHHead;          // Person is a child
        else lSpouse = peHHead;                                  // Person is a spouse
    }
    */
}

5.1.7.7. Core file of the Clock Actor: ClockCore.mpp

This module introduces a Clock actor which handles calendar clock events like year changes. This avoids inefficiencies as otherwise all actors would having to schedule their own events individually. Instead of that the Clock announces each year end/start to all other actors



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

link Person.lClock Clock.mlPersons[];

////////////////////////////////////////////////////////////////////////////////////////////////////
// Clock Actor
////////////////////////////////////////////////////////////////////////////////////////////////////

/*  NOTE (Clock, EN)
    The Clock actor handles calendar year and other regular period changes.
*/

actor Clock                                                 //EN Clock Actor
{
    void    Start();                                        //EN Start Clock
    void    Finish();                                       //EN Finish
    int     clock_year = { 2000 };                          //EN Calendar Year
    int     next_clock_year_end = { TIME_INFINITE };        //EN Next end year clock tick
    int     next_clock_year_start = { TIME_INFINITE };      //EN Next start year clock tick
    event   timeStartYearClock, StartYearClock;             //EN Start year function of clock
    event   timeEndYearClock, EndYearClock;                 //EN End year function of clock
};

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

actor_set Clock asTheClock;                                 //EN Actor Set Clock Actor

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

void Clock::Start()
{
    time = MIN(ALL_YEAR_RANGE);
    clock_year = MIN(ALL_YEAR_RANGE);
    next_clock_year_end = WAIT(1);
};

void Clock::Finish() {}

/*      NOTE( Clock.EndYearClock, EN)
    The function EndYearClock creates an end of year event. It calls all other actors to announce
    that the year has ended so that all actors can perform their own end of year routines
*/

TIME Clock::timeEndYearClock() { return next_clock_year_end; }
void Clock::EndYearClock()
{
    long nPopSize = asAllPersons->Count();
    for (long nIndex = 0; nIndex < nPopSize; nIndex++)
    {
        auto prPerson = asAllPersons->Item(nIndex);
        prPerson->YearEnd();
    }
    next_clock_year_start = WAIT(0);
    next_clock_year_end = WAIT(1);
}

/*      NOTE( Clock.StartYearClock, EN)
    The function StartYearClock creates a new year event. It calls all other actors to announce
    that a new year has started so that all actors can call their own new year routines
*/

TIME Clock::timeStartYearClock() { return next_clock_year_start; }
void Clock::StartYearClock()
{
    clock_year++;
    long nPopSize = asAllPersons->Count();
    for (long nIndex = 0; nIndex < nPopSize; nIndex++)
    {
        auto prPerson = asAllPersons->Item(nIndex);
        prPerson->YearStart();
    }
    next_clock_year_start = TIME_INFINITE;
}

5.1.7.8. Core file of the Globals Actor: GlobalsCore.mpp

This module introduces an actor Globals which handles global variables and constants which are calculated or changed after the pre-simulation phase. All persons are linked to the actor Globals and can access it states. This is used to store information which applies to all Person actors on a central place avoiding redundancies. This functionality is typically used by modules which run calibrations after the simulation has been started.



////////////////////////////////////////////////////////////////////////////////////////////////////
// Actor Sets and linkages
////////////////////////////////////////////////////////////////////////////////////////////////////

actor_set Globals asGlobals;                    //EN Globals (Actor Set)
link Person.lGlobals Globals.mlPerson[];                //EN Link Person to to Globals

////////////////////////////////////////////////////////////////////////////////////////////////////
// Actor States
////////////////////////////////////////////////////////////////////////////////////////////////////

actor Globals                                   //EN Actor Globals
{
    double person_weight = { 1.0 };             //EN Person weight
};

5.1.7.9. Actions at Year End: CalendarYearChange.mpp

This module handles calendar year changes. Before a year ends, the Person function YearEnd() is called by the Calendar clock actor. This is a point in time in which models typically update accounts and perform other end of year routines. Immediately after the year end, the YearStart() routine is called. This is when the calendar_year state is changed.



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

actor Person
{
    ALL_YEAR_RANGE  calendar_year = { 2000 };                       //EN Calendar year
    void            YearEnd();                                      //EN Year End Function
    void            YearStart();                                    //EN Year Start Function
};

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

/*  NOTE(Person.YearEnd, EN)
    The function YearEnd is called by the Clock actor at the end of each year. The code in this
    function is executed last thing in a given year before the calendar year is incremented
*/

void Person::YearEnd()
{
    // empty for the moment
}

/*  NOTE(Person.YearStart, EN)
    The function YearStart is called by the Clock actor at the start of each year.
    The code in this function is executed first thing in new year
*/
void Person::YearStart()
{
    calendar_year = lClock->clock_year;
}

5.1.7.10. Micro Data Output: MicroDataOutput.mpp

This module implements micro data output written to a csv file. Users can specify the time at which a micro-data file is written out and choose a file name. Output can also be produced in fixed time intervals, e.g. every year or every 5 years. The output csv file includes a header line with variable names. The module can be switched on and off.

How to add a new variable to the output?

  • In WriteMicroRecord(), add a line which pushes a new variable into the output record
  • In PreSimulation() add a line to extend the list of variable names written to the file


output_csv out_csv;                                 //EN Microdata output csv object

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

classification OUTPUT_TIMES                         //EN Micro-data output times
{
    OT_FIRST,                                       //EN Time of first output
    OT_LAST,                                        //EN Time of last output
    OT_INTERVAL                                     //EN Time interval (0 for no repetition)
};

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

parameters
{
    logical             WriteMicrodata;                     //EN Write micro-data output file Y/N
    double              TimeMicroOutput[OUTPUT_TIMES];      //EN Time(s) of micro-data output
    file                MicroRecordFileName;                //EN File name micro-data output file
};

parameter_group PG05_Files                          //EN Microdata output
{
    WriteMicrodata, MicroRecordFileName,
    TimeMicroOutput
};

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

actor Person
{
    TIME  time_microdata_output = {TIME_INFINITE};  //EN Time for microdata output
    void  WriteMicroRecord_Start();                 //EN Initialization for microdata output event
    hook  WriteMicroRecord_Start, Start;            //EN Function Hook
    event timeWriteMicroRecord, WriteMicroRecord;   //EN Write micro-data record event
};

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

void Person::WriteMicroRecord_Start()
{
    double dFirstOutput = TimeMicroOutput[OT_FIRST];
    while (WriteMicrodata && dFirstOutput <= TimeMicroOutput[OT_LAST] && dFirstOutput < time)
    {
        dFirstOutput = dFirstOutput + TimeMicroOutput[OT_INTERVAL];
        if ( TimeMicroOutput[OT_INTERVAL] <= 0 ) dFirstOutput = TIME_INFINITE;
    }

    if (WriteMicrodata && dFirstOutput >= time && dFirstOutput <= TimeMicroOutput[OT_LAST])
    {
        time_microdata_output = dFirstOutput;
    }
    else time_microdata_output = TIME_INFINITE;
}

TIME Person::timeWriteMicroRecord()
{
    if (GetReplicate()==0)  return time_microdata_output;
    else return TIME_INFINITE;
}

void Person::WriteMicroRecord()
{
    // Push the fields into the output record.

    // ==============================================================
    // When adding additional variables, this list has to be extended
    // ==============================================================
    out_csv << actor_id;            // Actor ID
    out_csv << actor_weight;        // Weight
    out_csv << time;                // Time
    out_csv << time_of_birth;       // Time of birth
    out_csv << ( int )sex;          // Sex

    // All fields have been pushed, now write the record.
    out_csv.write_record();

    // set next output
    if (time_microdata_output + TimeMicroOutput[OT_INTERVAL] > time &&
        time_microdata_output + TimeMicroOutput[OT_INTERVAL] <= TimeMicroOutput[OT_LAST])
    {
        time_microdata_output = time_microdata_output + TimeMicroOutput[OT_INTERVAL];
    }
    else
    {
        time_microdata_output = TIME_INFINITE;
    }
}

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

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

    if (WriteMicrodata)
        {
        // ==============================================================
        // When adding additional variables, this list has to be extended
        // ==============================================================
        std_string  myString = "ID,";            // Actor ID
        myString  = myString + "WEIGHT,";        // Weight
        myString  = myString + "TIME,";          // Output Time
        myString  = myString + "BIRTH,";         // Time of birth
        myString  = myString + "MALE,";          // Sex

        out_csv.open(MicroRecordFileName);
                out_csv.write_header(myString);
        }
}

void PostSimulation()
{
    if (WriteMicrodata) { out_csv.close(); }    // closing the file
}

5.1.7.11. Micro Data Output: TablesPopulation.mpp

This module contains population output tables of the model



////////////////////////////////////////////////////////////////////////////////////////////////////
// Population Tables
////////////////////////////////////////////////////////////////////////////////////////////////////

table_group TG_PopulationTables             //EN Population
{
    TotalPopulation,
    StartingPopulationSummary
};

table Person TotalPopulation                //EN Total Population
{
    {
        unit,                               //EN Persons at the beginning of each year
        duration()                          //EN Average population (total time lived) in year
    }
    *calendar_year
    * sex +
};


table Person StartingPopulationSummary      //EN Starting Population Summary
[person_type == PT_START]
{
    {
        unit                                //EN Persons
    }
    *year_of_birth +
    *sex +
};