
Model Code
This section is a technical documentation of the microWELT model. It is organized module by module, the modules being grouped by types (see below). The documentation is generated automatically from the most recent version of the model code. Each module documentation contains a text description, followed by the model code.
Welcome to microWELT Version 1.22. This is the model version used in the studies of the final WELTRANSIM Work Package 2 report. It is also the model version available for download at the project website. microWELT Version 1.22. consolidates all previous modelling steps.
microWELT is highly modular. Modules can be classified into five groups:
The simulation engine comprising modules implementing the base functionality of an interacting population, continuous-time, dynamic microsimulation model. These modules are highly generic and typically do not require any changes when implementing a concrete model of this type.
Actor core modules: these modules introduce model actors like persons or the welfare state. It contains code specific to the actor, but not corresponding to a specific bahaviour.
Behavioural modules: these are the modules implementing specific processes and events like fertility, mortality, partnership formations and dissolutions, and education careers.
Accounting modules: these modules implement the accounting framework of the model. The accounting modules of microWELT implement the framework of National Transfer Accounts (NTAs) capturing the system of national accounts broken down by age, gender, education, and family type.
Output modules: These modules produce the rich table and micro-data output of the model.
microWELT is implemented in Modgen, a freely available generic microsimulation programming language developed and maintained at Statistics Canada. Each module corresponds to a .mpp code file containing its own documentation. Full model documentation is also available from the help menu of the microWELT application.
The Simulation Engine
model_core.mpp
This module contains general model settings and key elements of the simulation engine of the model. Developers typically are not required to modify or understand its 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.
The creation if immigrants.
////////////////////////////////////////////////////////////////////////////////////////////////////
// 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 Clock
auto prClock = new Clock();
prClock->Start();
// Create the Actor Globals
auto prGlobals = new Globals();
prGlobals->Start();
// Create the State
auto prState = new State();
prState->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, TIME_INFINITE);
// 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, TIME_INFINITE);
}
paObservation->obs_weight--;
}
// Delete all observation actors
while (asObservationAll->Count() > 0) asObservationAll->Item(0)->Finish();
// Create Immigrants
if (ModelImmigration || ModelNetMigration)
{
for (int nYear = MIN(SIM_YEAR_RANGE); nYear < int(SIMULATION_END()); nYear++)
{
int nCohortSize = int(MgImmigrationTotal[RANGE_POS(SIM_YEAR_RANGE, nYear)] / (asGlobals->Item(0)->person_weight));
double dCohortSize = MgImmigrationTotal[RANGE_POS(SIM_YEAR_RANGE, nYear)] / (asGlobals->Item(0)->person_weight);
if (RandUniform(42) < dCohortSize - nCohortSize) nCohortSize++;
for (int nIndex = 0; nIndex < nCohortSize; nIndex++)
{
auto prPerson = new Person();
prPerson->Start(NULL, NULL, RandUniform(43) + nYear);
}
}
}
// 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.
}
modgen_time_based.mpp
This module implements core global functions for time-based Mogen models. It also includes some string constants of the graphical user interface. This module is part of the simulation engine, and it should not be necessary to modify any code.
////////////////////////////////////////////////////////////////////////////////////////////////////
// Some String Contants used in the User-Interface
////////////////////////////////////////////////////////////////////////////////////////////////////
string S_MODEL_FINISH; //EN Finish
string S_MODEL_REPLICATE; //EN Replicate
string S_MODEL_SIMULATION; //EN Simulation
string S_MODEL_START; //EN Start
////////////////////////////////////////////////////////////////////////////////////////////////////
// SimulateEvents() simulates all events for all entities in the simulation.
////////////////////////////////////////////////////////////////////////////////////////////////////
void SimulateEvents()
{
// Buffer for messages
const size_t nBufSize = 255;
TCHAR szBuffer[nBufSize];
// Simulation member ordinal for progress reporting
int nSimulationMember = GetReplicate();
_stprintf_s(szBuffer, nBufSize, _T("%s %d: %s"), (LPCTSTR)ModelString("S_MODEL_REPLICATE"),
nSimulationMember, (LPCTSTR)ModelString("S_MODEL_SIMULATION"));
ProgressMessage( szBuffer );
double dCurrentTime = TIME_INFINITE;
double dStartTime = TIME_INFINITE;
int nLastProgressPercent = -1;
int nThisProgressPercent = -1;
while ( !gpoEventQueue->Empty() )
{
// get the time of next event, verify against the simulation end
dCurrentTime = gpoEventQueue->NextEvent();
// Note the start time (time of first event) for progress indicator
if ( dStartTime == TIME_INFINITE )
{
dStartTime = dCurrentTime;
}
if ( dCurrentTime > SIMULATION_END() || gbInterrupted || gbCancelled || gbErrors )
{
if (dCurrentTime > SIMULATION_END())
{
// age all actors to the simulation end time
gpoEventQueue->WaitUntilAllActors( SIMULATION_END() );
}
_stprintf_s(szBuffer, nBufSize, _T("%s %d: %s"),
(LPCTSTR)ModelString("S_MODEL_REPLICATE"),
nSimulationMember, (LPCTSTR)ModelString("S_MODEL_FINISH"));
ProgressMessage( szBuffer );
gpoEventQueue->FinishAllActors();
}
else
{
// age all actors to the time of next event
gpoEventQueue->WaitUntil( dCurrentTime );
// implement the next event
gpoEventQueue->Implement();
}
// Update progress indicator only if the integer percentage complete changes
// (updates to the progress bar at every event are expensive).
nThisProgressPercent = (int)( 100 * ( dCurrentTime - dStartTime ) /
( SIMULATION_END() - dStartTime ) );
if ( nThisProgressPercent > nLastProgressPercent )
{
TimeReport( dCurrentTime ); // update simulation progress
nLastProgressPercent = nThisProgressPercent;
}
}
}
////////////////////////////////////////////////////////////////////////////////////////////////////
// LogSimulationStart() writes a start message for the simulation member in the log.
////////////////////////////////////////////////////////////////////////////////////////////////////
void LogSimulationStart()
{
// Buffer for messages
const size_t nBufSize = 255;
TCHAR szBuffer[nBufSize];
// Simulation member ordinal for progress reporting
int nSimulationMember = GetReplicate();
_stprintf_s(szBuffer, nBufSize, _T("%s %d: %s"), (LPCTSTR)ModelString("S_MODEL_REPLICATE"),
nSimulationMember, (LPCTSTR)ModelString("S_MODEL_START"));
ProgressMessage( szBuffer );
}
_CountryContext.mpp
This module contains country and starting year specific code. This is the only place to be adapded when porting the model to a new country or when changing the starting year of the simulation.
////////////////////////////////////////////////////////////////////////////////////////////////////
// Country / Context: 2010
////////////////////////////////////////////////////////////////////////////////////////////////////
range ALL_YEAR_RANGE { 1899, 2150 }; //EN All calendar years
range SIM_YEAR_RANGE { 2010, 2150 }; //EN Projected calendar years
range YOB_EDUC_PROG1{ 1990, 2050 }; //EN Year of birth
range YOB_EDUC_PROG2{ 1980, 2050 }; //EN Year of birth
range YOB_BIRTH1 { 1960, 2050 }; //EN Year of birth
range YOB_START15{ 1900, 1994 }; //EN Year of birth (in startpop age 15+)
range YOB_START50{ 1900, 1959 }; //EN Year of birth (in startpop age 50+)
range YOB_MALEFERT{ 1920, 2050 }; //EN Year of birth
range YOB_CHECK_SCHOOL{ 1980,1990 }; //EN Yob school status to be checked
Actor Core Modules
PersonCore.mpp
This module introduces the main actor of the model: Person. It contains the core functionality and general states of the actor Person. 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.
////////////////////////////////////////////////////////////////////////////////////////////////////
// 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
};
////////////////////////////////////////////////////////////////////////////////////////////////////
// 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
void Finish(); //EN Finishes the actor
logical ever_resident = { TRUE }; //EN Ever Resident
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
Person *peHHead; //EN Pointer to household head
//EN Starts the actor
void Start(Observation *peObservation, Person *pePers, TIME dTimeOfImmigration);
//EN The simulation has entered the projected time
logical in_projected_time = (calendar_year >= MIN(SIM_YEAR_RANGE));
};
////////////////////////////////////////////////////////////////////////////////////////////////////
// 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 househod 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, TIME dTimeOfImmigration)
{
// 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 == NULL && pePers != NULL ) person_type = PT_CHILD; // Born in simulation
else if (peObservation != NULL ) 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] + RandUniform(2);
sex = (SEX)(int)peObservation->pmc[PMC_SEX];
family_role = (FAM_ROLE)( int )peObservation->pmc[PMC_ROLE];
educ_startpop = (EDUC_LEVEL3)(int)peObservation->pmc[PMC_EDUC];
in_school_startpop = (bool)(int)peObservation->pmc[PMC_INSCHOOL];
// (B) Other states
time_of_birth = time;
calendar_year = (int)time_of_birth;
// (C) Links to head resp. spouse
if (pePers) peHHead = pePers;
}
else if (person_type == PT_CHILD) // Person born in simulation
{
// (A) States corresponding to Starting population file variables
if (RandUniform(4) < 100.0 / (100.0 + SexRatio[RANGE_POS(SIM_YEAR_RANGE, calendar_year)]))
{
sex = FEMALE;
}
else sex = MALE;
time = pePers->time;
family_role = FR_CHILD;
// (B) Other states
time_of_birth = time;
calendar_year = ( int )time_of_birth;
// (C) Links to head resp. spouse
peHHead = pePers;
}
else // Person is an immigrant
{
is_resident = FALSE;
ever_resident = FALSE;
time_of_immigration = dTimeOfImmigration;
year_of_immigration = (int)dTimeOfImmigration;
family_role = FR_HEAD;
int nSex, nAge, nNation;
Lookup_MgImmigrationAgeSexAll(RandUniform(41),RANGE_POS(SIM_YEAR_RANGE,year_of_immigration),&nSex, &nAge);
sex = (SEX)nSex;
time = dTimeOfImmigration - nAge - RandUniform(44);
time_of_birth = time;
calendar_year = (int)time_of_birth;
}
time_set_alive = WAIT(0);
}
/* 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
IMPLEMENT_HOOK();
if (person_type == PT_IMMIGRANT)
{
if (FindImmigrantMother()) family_role = FR_CHILD;
}
peHHead = NULL;
}
/* 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(){}
StateCore.mpp
This module introduces the welfare state actor ‘State’. Various accounting modules use the actor in connection with the modelling of transfers.
actor State //EN State Actor
{
void Start(); //EN Start Function
int state_year = lClock->clock_year; //EN Calendar year
void StateYearStart(); //EN Year End Function
};
link State.lClock Clock.lState; //EN Link State to Clock
actor_set State asState; //EN The State Actor Set
void State::Start()
{
// Setting the actor weight
Set_actor_weight(asGlobals->Item(0)->person_weight);
Set_actor_subsample_weight(asGlobals->Item(0)->person_weight);
lClock = asTheClock->Item(0);
time = lClock->time;
lStateToGlobals = lClock->lClockToGlobals;
}
ClockCore.mpp
This module introduces a Clock actor which handles calendar clock events like calendar year changes and mid-year events. This avoids inefficiencies as otherwise, all Person actors would have to schedule these regular events individually. Instead of that, the Clock announces these clock events to all Person actors.
////////////////////////////////////////////////////////////////////////////////////////////////////
// Links and actor sets
////////////////////////////////////////////////////////////////////////////////////////////////////
link Person.lClock Clock.mlPersons[]; //EN Clock to person link
actor_set Clock asClock; //EN Actor set to make the clock easyly accessible
////////////////////////////////////////////////////////////////////////////////////////////////////
// Actor sets
////////////////////////////////////////////////////////////////////////////////////////////////////
actor_set Clock asTheClock; //EN Actor Set Clock Actor
////////////////////////////////////////////////////////////////////////////////////////////////////
// Clock Actor
////////////////////////////////////////////////////////////////////////////////////////////////////
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
TIME time_midyear_clock = { TIME_INFINITE }; //EN Next midyear clock event
event timeMidYearClockEvent, MidYearClockEvent; //EN Mid year clock event
};
////////////////////////////////////////////////////////////////////////////////////////////////////
// Implementation
////////////////////////////////////////////////////////////////////////////////////////////////////
TIME Clock::timeMidYearClockEvent() { return time_midyear_clock; }
void Clock::MidYearClockEvent() { time_midyear_clock = WAIT(1.0); }
void Clock::Start()
{
time = MIN(ALL_YEAR_RANGE);
clock_year = MIN(ALL_YEAR_RANGE);
next_clock_year_end = WAIT(1);
time_midyear_clock = WAIT(0.5);
};
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()
{
SetEducAdjustmentFactors();
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;
}
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. Observations 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 simulation engine then uses the new observation weights to decide if and how often an observation is used when creating Person actors (which clone the observation characteristics). Note that the weight of the household head is used for all members of a family. After being used for creating the starting population, the observation actors are destroyed to free up the memory space.
Model developers typically are not required to modify – or understand – the code of this module. 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’ by a NEW_FIELD. 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 to initialize a corresponding state. Its value is accessible in the array pmc[NEW_FIELD].
////////////////////////////////////////////////////////////////////////////////////////////////////
// Dimensions
////////////////////////////////////////////////////////////////////////////////////////////////////
classification PERSON_MICRODATA_COLUMNS //EN List of Starting population variables
{
PMC_HHID, //EN Household ID
PMC_WEIGHT, //EN Weight
PMC_BIRTH, //EN Time of birth
PMC_SEX, //EN Sex
PMC_EDUC, //EN Education level
PMC_ROLE, //EN Role in family
PMC_INSCHOOL //EN Currently attending school
};
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 P0_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 heatds) 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];
}
fam_id = (int)pmc[PMC_HHID];
fam_role = (FAM_ROLE)(int)pmc[PMC_ROLE];
};
void Observation::Finish(){};
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 its states. The actor is used to store information which applies to all Person actors avoiding redundancies. This functionality is typically used to store alignment factors calculated and updated 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
link Globals.lGlobalsToClock Clock.lClockToGlobals; //EN Link between Clock and Globals
link Globals.lGlobalsToState State.lStateToGlobals; //EN Link between State and Globals
actor_set Person MaleResidentsEducAgePartner[educ_fate][integer_age][has_partner]
filter is_resident&& sex == MALE;
////////////////////////////////////////////////////////////////////////////////////////////////////
// Actor States
////////////////////////////////////////////////////////////////////////////////////////////////////
actor Globals //EN Actor Globals
{
double person_weight = { 1.0 }; //EN Person weight
//EN Table of proportion singles is initialized
logical is_initialized_prob_single_men = { FALSE };
//EN Initialize table of proportion singles
event timeInitPropSingleMenEvent, InitPropSingleMenEvent;
//EN Proportion single men
double prob_single_men[AGE_RANGE][EDUC_LEVEL3];
//EN Groups of men unavailable for partnership
logical blocked_single_men[AGE_RANGE][EDUC_LEVEL3];
//EN Update groups of men unavailable for partnership
void UpdateBlockedSingleMen();
int global_year = lGlobalsToClock->clock_year; //EN Calendar Year
void Start(); //EN Start Function
//EN Calendar Year
SIM_YEAR_RANGE global_tab_sim_year = COERCE(SIM_YEAR_RANGE, global_year);
//EN In projected time
logical globals_in_projected_time = (global_year >= MIN(SIM_YEAR_RANGE));
};
////////////////////////////////////////////////////////////////////////////////////////////////////
// Implementation
////////////////////////////////////////////////////////////////////////////////////////////////////
void Globals::Start()
{
lGlobalsToClock = asTheClock->Item(0);
time = lGlobalsToClock->time;
}
TIME Globals::timeInitPropSingleMenEvent()
{
if (!is_initialized_prob_single_men && globals_in_projected_time) return WAIT(0);
else return TIME_INFINITE;
}
void Globals::InitPropSingleMenEvent()
{
for (int nAge = 0; nAge < MAX(AGE_RANGE); nAge++)
{
for (int nEduc = 0; nEduc < SIZE(EDUC_LEVEL3); nEduc++)
{
double dAll = MaleResidentsEducAgePartner[nEduc][nAge][FALSE]->Count() + MaleResidentsEducAgePartner[nEduc][nAge][TRUE]->Count();
if (dAll > 0)
{
prob_single_men[nAge][nEduc] = MaleResidentsEducAgePartner[nEduc][nAge][FALSE]->Count() / dAll;
}
else prob_single_men[nAge][nEduc] = 0.05; // some low value
}
}
is_initialized_prob_single_men = TRUE;
}
void Globals::UpdateBlockedSingleMen()
{
for (int nAge = 0; nAge < MAX(AGE_RANGE); nAge++)
{
for (int nEduc = 0; nEduc < SIZE(EDUC_LEVEL3); nEduc++)
{
double dAll = MaleResidentsEducAgePartner[nEduc][nAge][FALSE]->Count() + MaleResidentsEducAgePartner[nEduc][nAge][TRUE]->Count();
double dProp = MaleResidentsEducAgePartner[nEduc][nAge][FALSE]->Count() / dAll;
if (dAll > 0 && dProp < prob_single_men[nAge][nEduc]) blocked_single_men[nAge][nEduc] = TRUE;
else blocked_single_men[nAge][nEduc] = FALSE;
}
}
}
Behavioural Modules
FertilityAgePeriod.mpp
This module implements fertility based on age-specific fertility rates. This module is a microsimulation implementation of a typical cohort component model based on published population projection data.
The model is prepared for being complemented or over-ridden by an alternative refined fertility model. This is done by three logical states:
The state use_base_fertility_model is initialized as TRUE; it indicates that the base model is to be used. When adding another model choice, this flag can be changed in another module.
The state use_base_fertility_for_alignment is initialized with FALSE; it indicates if another model is to be aligned to the fertility outcome of this base model.
The state baby_looking_for_mother is set to TRUE at a birth event if the base model is used for alignment only, and the actual birth has to be assigned to another person of the population.
////////////////////////////////////////////////////////////////////////////////////////////////////
// Types
////////////////////////////////////////////////////////////////////////////////////////////////////
range FERTILE_AGE_RANGE { 15, 49 }; //EN Fertile age range
////////////////////////////////////////////////////////////////////////////////////////////////////
// Parameters
////////////////////////////////////////////////////////////////////////////////////////////////////
parameters
{
//EN Age specific fertility rates
double AgeSpecificFertility[FERTILE_AGE_RANGE][SIM_YEAR_RANGE];
//EN Sex ratio (male per 100 female)
double SexRatio[SIM_YEAR_RANGE];
};
parameter_group PG03a_Fertility_Model_A //EN Fertility Base Model
{
AgeSpecificFertility, SexRatio
};
////////////////////////////////////////////////////////////////////////////////////////////////////
// Actor declarations
////////////////////////////////////////////////////////////////////////////////////////////////////
actor Person
{
logical use_base_fertility_model = { TRUE }; //EN Use the base model
logical use_base_fertility_for_alignment = { FALSE }; //EN Use the model for alignment
logical baby_looking_for_mother = { FALSE }; //EN A birth is still to be created
//EN Indicator that perion is a potential mother
logical is_potential_mother = (sex == FEMALE && WITHIN(FERTILE_AGE_RANGE, integer_age)
&& in_projected_time && ever_resident) ? TRUE : FALSE;
int parity = { 0 }; //EN Parity
event timeBirthEvent, BirthEvent; //EN Birth event
};
////////////////////////////////////////////////////////////////////////////////////////////////////
// Event Implementations
////////////////////////////////////////////////////////////////////////////////////////////////////
TIME Person::timeBirthEvent()
{
double dEventTime = TIME_INFINITE;
double dHazard = 0.0;
if (is_potential_mother && (use_base_fertility_model || use_base_fertility_for_alignment))
{
dHazard = AgeSpecificFertility[RANGE_POS(FERTILE_AGE_RANGE, integer_age)]
[RANGE_POS(SIM_YEAR_RANGE, calendar_year)];
if (dHazard > 0.0) dEventTime = WAIT(-TIME(log(RandUniform(3)) / dHazard));
}
return dEventTime;
}
void Person::BirthEvent()
{
// event applies to individual without alignment
if (use_base_fertility_model)
{
MakeBaby();
}
else if (use_base_fertility_for_alignment)
{
baby_looking_for_mother = TRUE;
}
}
FertilityByEducation.mpp
This module is an extension of the base fertility model allowing to control for educational differences in first births, both concerning the timing and outcome, i.e. it allows to model childlessness by education, which is an essential variable for various model applications. Users can select to use or not to use the module by a selection parameter. Aggregate outcomes (births by age) are identical as in the base model. Besides the on/off switch, the model has two parameters, namely
Cohort first birth rates by education for birth cohorts in reproductive age.
Childlessness by education for older birth cohorts.
If the module is switched on, the algorithm is as follows:
The base module produces birth events but does not assign the babies to a mother yet. The baby knows the age of the mother.
First birth events of this module flag all women expecting the first birth.
The babies are first given to women of the appropriate age flagged for a first birth.
The remaining babies are distributed randomly to women of suitable age who are already mothers.
The module initialized the state is_mother at the start of the simulation using the information from links in the starting population file as well as the parameters which allow calculating the expected rates of childlessness by year of birth, education and age. The state is_mother is imputed by the function ImputeIsMother() in the following way:
Using family links from the starting population file, all women living with children in the family are marked as mothers.
The number of expected mothers by age and education is calculated from the population sizes in the starting population and the first birth and childlessness parameters.
If the number of expected mothers is higher than the number of recorded mothers (which should always be the case, as some children are expected to have moved out already) childless women are selected randomly (for a given age and education) and set to be mothers.
////////////////////////////////////////////////////////////////////////////////////////////////////
// Actor Sets
////////////////////////////////////////////////////////////////////////////////////////////////////
//EN Potential mothers for first birth by age
actor_set Person asWomenWaitingFirstBirth[fertile_age]
filter is_alive && is_potential_mother && waits_for_first_baby && is_resident;
//EN Potential mothers for higher order births by age
actor_set Person asPotentialMothersHigherBirths[fertile_age]
filter is_potential_mother && is_mother && is_resident;
//EN Childless women by year of birt and education
actor_set Person asChildlessWomen[year_of_birth][birth1_group] filter !is_mother && sex==FEMALE && is_resident;
////////////////////////////////////////////////////////////////////////////////////////////////////
// Types
////////////////////////////////////////////////////////////////////////////////////////////////////
classification BIRTH1_GROUP //EN Population Groups
{
B1G_00, //EN Education Low
B1G_01, //EN Education Medium
B1G_02 //EN Education High
};
classification SELECTED_FERTILITY_MODEL //EN Fertility model options
{
SFM_MACRO, //EN Macro model (age only)
SFM_ALIGNE_AGE //EN Micro model with aligned number of births by age
};
////////////////////////////////////////////////////////////////////////////////////////////////////
// Parameters
////////////////////////////////////////////////////////////////////////////////////////////////////
parameters
{
//EN Fertility model selection
SELECTED_FERTILITY_MODEL selected_fertility_model;
//EN First Birth Rates
double FirstBirthCohortRates[BIRTH1_GROUP][FERTILE_AGE_RANGE][YOB_BIRTH1];
//EN Calibrated First Birth Rates
model_generated double mgFirstBirthCohortRates[BIRTH1_GROUP][FERTILE_AGE_RANGE][YOB_BIRTH1];
//EN Childlessness in older population (female)
double ChildlessnessYob[YOB_START50][BIRTH1_GROUP];
//EN Calibration Targets Cohort Childlessness
double ChildlessnessYobTargets[YOB_BIRTH1][BIRTH1_GROUP];
//EN Calibrate cohort childlessness to targets y/n
logical CalibrateChildlessness;
};
parameter_group PG03_Fertility_Top //EN Fertility
{
selected_fertility_model,
PG03a_Fertility_Model_A,
PG03b_Fertility_Model_B
};
parameter_group PG03b_Fertility_Model_B //EN Fertility: Refined Version
{
CalibrateChildlessness,
FirstBirthCohortRates,
ChildlessnessYob,
ChildlessnessYobTargets
};
////////////////////////////////////////////////////////////////////////////////////////////////////
// Actor States and Functions
////////////////////////////////////////////////////////////////////////////////////////////////////
actor Person
{
//EN Population group for modeling of births
BIRTH1_GROUP birth1_group = (educ_fate == EL3_LOW) ? B1G_00 :
(educ_fate == EL3_MEDIUM) ? B1G_01 : B1G_02;
FERTILE_AGE_RANGE fertile_age = COERCE(FERTILE_AGE_RANGE, integer_age); //EN Age
logical is_mother = { FALSE }; //EN Is Mother
logical waits_for_first_baby = { FALSE }; //EN Waiting for first birth
void SetFertilityModelSwitches(); //EN Initializing model choices
hook SetFertilityModelSwitches, Start; //EN hooked to Start function
void MakeBaby(); //EN Function creating a baby
event timeRefineModelBirthEvent, RefineModelBirthEvent; //EN Birth event
event timeFirstBirthFlagEvent, FirstBirthFlagEvent; //EN First birth flag event
//int linked_kids = count(mlChild);
//EN Children in household
};
actor Clock
{
void ImputeIsMother();
hook ImputeIsMother, StartYearClock;
};
//////////////////////////////////////////////////////////////////////////////////////////////////////
//// Implementation
//////////////////////////////////////////////////////////////////////////////////////////////////////
void Person::SetFertilityModelSwitches()
{
if (selected_fertility_model == SFM_MACRO) // Macro model (age only)
{
use_base_fertility_model = TRUE ; // Use the base model
use_base_fertility_for_alignment = FALSE ; // Base model not used for alignment
}
else // Refined model with aligned births
{
use_base_fertility_model = FALSE ; // Do not use the base model
use_base_fertility_for_alignment = TRUE ; // Base model used for alignment
}
}
TIME Person::timeFirstBirthFlagEvent()
{
TIME dEventTime = { TIME_INFINITE };
double dHazard = { 0.0 };
if (is_potential_mother && !use_base_fertility_model && use_base_fertility_for_alignment
&& !is_mother && !waits_for_first_baby)
{
dHazard = mgFirstBirthCohortRates[birth1_group][RANGE_POS(FERTILE_AGE_RANGE, fertile_age)][RANGE_POS(YOB_BIRTH1, year_of_birth)];
if (dHazard > 0.0 ) dEventTime = WAIT(-log(RandUniform(16)) / dHazard);
}
return dEventTime;
}
void Person::FirstBirthFlagEvent()
{
waits_for_first_baby = TRUE;
}
void Person::MakeBaby()
{
parity++; // increment parity
is_mother = TRUE;
auto peChild = new Person; // Create and point to a new actor
peChild->Start(NULL, this, TIME_INFINITE); // Call Start() function of the new actor and pass own address
}
TIME Person::timeRefineModelBirthEvent()
{
double dEventTime = TIME_INFINITE;
if (baby_looking_for_mother) //EN The base model to which the model is aligned produced a baby
{
dEventTime = WAIT(0);
}
return dEventTime;
}
void Person::RefineModelBirthEvent()
{
baby_looking_for_mother = FALSE;
if (asWomenWaitingFirstBirth[RANGE_POS(FERTILE_AGE_RANGE,integer_age)]->Count() > 0)
{
auto peMother = asWomenWaitingFirstBirth[RANGE_POS(FERTILE_AGE_RANGE, integer_age)]->GetRandom(RandUniform(17));
peMother->MakeBaby();
peMother->is_mother = TRUE;
peMother->waits_for_first_baby = FALSE;
}
else if ( asPotentialMothersHigherBirths[RANGE_POS(FERTILE_AGE_RANGE, integer_age)]->Count() > 0)
{
auto peMother = asPotentialMothersHigherBirths[RANGE_POS(FERTILE_AGE_RANGE, integer_age)]->GetRandom(RandUniform(18));
peMother->MakeBaby();
}
else
{
//this should not happen
//get the baby herself
MakeBaby();
is_mother = TRUE;
waits_for_first_baby = FALSE;
}
}
void Clock::ImputeIsMother()
{
if (clock_year == MIN(SIM_YEAR_RANGE))
{
int nPopSize;
int nPersons[SIZE(YOB_START15)][SIZE(BIRTH1_GROUP)];
int nExpected[SIZE(YOB_START15)][SIZE(BIRTH1_GROUP)];
int nRecorded[SIZE(YOB_START15)][SIZE(BIRTH1_GROUP)];
// initialize with 0
for (int nJ = 0; nJ < SIZE(YOB_START15); nJ++)
{
for (int nGroup = 0; nGroup < SIZE(BIRTH1_GROUP); nGroup++)
{
nPersons[nJ][nGroup] = 0; nExpected[nJ][nGroup] = 0; nRecorded[nJ][nGroup] = 0;
}
}
nPopSize = asAllResidents->Count();
for (int nI = 0; nI < nPopSize; nI++)
{
auto prPerson = asAllResidents->Item(nI);
if (WITHIN(YOB_START15, prPerson->year_of_birth) && prPerson->sex == FEMALE)
{
int nYOB = prPerson->year_of_birth - MIN(YOB_START15);
nPersons[nYOB][prPerson->birth1_group] = nPersons[nYOB][prPerson->birth1_group] + 1;
if (prPerson->children_in_household > 0 || (prPerson->lSpouse && prPerson->lSpouse->children_in_household >0))
{
nRecorded[nYOB][prPerson->birth1_group] = nRecorded[nYOB][prPerson->birth1_group] + 1;
prPerson->is_mother = TRUE;
prPerson->parity++;
}
}
}
//calculate expected number of mothers by yob
for (int nGroup = 0; nGroup < SIZE(BIRTH1_GROUP); nGroup++)
{
for (int nJ = 0; nJ < SIZE(YOB_START15); nJ++)
{
if (nJ < SIZE(YOB_START50)) // older population
{
nExpected[nJ][nGroup] = round(nPersons[nJ][nGroup] * (1 - ChildlessnessYob[nJ][nGroup]));
}
else // still in childbearing age
{
double dChildless = 1.0;
for (int nAgeIndex = 0; nAgeIndex < SIZE(FERTILE_AGE_RANGE) && nJ + MIN(YOB_START15) + MIN(FERTILE_AGE_RANGE) + nAgeIndex < MIN(SIM_YEAR_RANGE); nAgeIndex++)
{
dChildless = dChildless * exp(-mgFirstBirthCohortRates[nGroup][nAgeIndex][nJ - SIZE(YOB_START50)]);
}
nExpected[nJ][nGroup] = round(nPersons[nJ][nGroup] * (1 - dChildless));
}
}
}
//randomly choose childless women to become mothers in order to meet target numbers
for (int nGroup = 0; nGroup < SIZE(BIRTH1_GROUP); nGroup++)
{
for (int nJ = 0; nJ < SIZE(YOB_START15); nJ++)
{
if (nExpected[nJ][nGroup] > nRecorded[nJ][nGroup])
{
for (int nI = 0; nI < nExpected[nJ][nGroup] - nRecorded[nJ][nGroup]; nI++)
{
if (asChildlessWomen[RANGE_POS(ALL_YEAR_RANGE, MIN(YOB_START15) + nJ)][nGroup]->Count() > 0)
{
auto prMother = asChildlessWomen[RANGE_POS(ALL_YEAR_RANGE, MIN(YOB_START15) + nJ)][nGroup]->GetRandom(RandUniform(22));
prMother->is_mother = TRUE;
prMother->parity++;
}
}
}
}
}
}
IMPLEMENT_HOOK();
}
////////////////////////////////////////////////////////////////////////////////////////////////////
// Actor Sets
////////////////////////////////////////////////////////////////////////////////////////////////////
void PreSimulation()
{
if (selected_fertility_model != SFM_MACRO)
{
// Copy over values
for (int nYob = 0; nYob < SIZE(YOB_BIRTH1); nYob++)
{
for (int nGroup = 0; nGroup < SIZE(BIRTH1_GROUP); nGroup++)
{
for (int nAge = 0; nAge < SIZE(FERTILE_AGE_RANGE); nAge++)
{
mgFirstBirthCohortRates[nGroup][nAge][nYob] =
FirstBirthCohortRates[nGroup][nAge][nYob];
}
}
}
// Calibrate to targets if selected
if (CalibrateChildlessness)
{
for (int nYob = 0; nYob < SIZE(YOB_BIRTH1); nYob++)
{
for (int nGroup = 0; nGroup < SIZE(BIRTH1_GROUP); nGroup++)
{
double dTargetChildless = ChildlessnessYobTargets[nYob][nGroup];
double dCurrentChildless = 1.0;
int nIterations = 100000;
double dLower = 0.1;
double dUpper = 10.0;
double dCenter;
while (abs(dCurrentChildless - dTargetChildless) > 0.001 && nIterations > 0)
{
nIterations--;
dCenter = (dLower + dUpper) / 2.0;
dCurrentChildless = 1.0;
for (int nAge = 0; nAge < SIZE(FERTILE_AGE_RANGE); nAge++)
{
dCurrentChildless = dCurrentChildless * exp(-mgFirstBirthCohortRates[nGroup][nAge][nYob] * dCenter);
}
if (dCurrentChildless > dTargetChildless) dLower = dCenter;
else dUpper = dCenter;
}
for (int nAge = 0; nAge < SIZE(FERTILE_AGE_RANGE); nAge++)
{
mgFirstBirthCohortRates[nGroup][nAge][nYob] = mgFirstBirthCohortRates[nGroup][nAge][nYob] * dCenter;
double ddd = 1;
}
}
}
}
}
}
MaleChildlessness.mpp
This module decides the lifetime-fate of male childlessness based on two pieces of information: (1) living with children in the starting population file, and (2) a parameter of male cohort childlessness by education fate. Men of the starting population currently living with children are assumed to be parents, thus not childless. In a second step, for men from the starting population who are currently not living with children, a status is assigned randomly in order to meet the cohort targets. For those born in the simulation, the status is assigned by sampling according to the cohort parameter. This assignment happens at the first birthday, as at this time, the final education fate is known.
////////////////////////////////////////////////////////////////////////////////////////////////////
// Actor Sets
////////////////////////////////////////////////////////////////////////////////////////////////////
//EN Men fated to stay childless by year of birth and education
actor_set Person asMenNeverFather[year_of_birth][birth1_group] filter never_father && sex == MALE && is_resident;
////////////////////////////////////////////////////////////////////////////////////////////////////
// Parameters
////////////////////////////////////////////////////////////////////////////////////////////////////
parameters
{
double MaleCohortChildlessness[YOB_MALEFERT][BIRTH1_GROUP]; //EN Male cohort childlessness
};
parameter_group PG_MaleChildlessness //EN Male Childlessness
{
MaleCohortChildlessness
};
////////////////////////////////////////////////////////////////////////////////////////////////////
// Actor states and functions
////////////////////////////////////////////////////////////////////////////////////////////////////
actor Person
{
logical never_father = { TRUE }; //EN Person fated never to become father
void SetEverFatherFate(); //EN Set fate of ever becoming father
hook SetEverFatherFate, BirthdayEvent; //EN Decide at 1st birthday (education fate known)
};
actor Clock
{
void ImputeEverFather(); //EN Person is father
hook ImputeEverFather, ImputeIsMother; //EN To be run immediately after female imputation
};
////////////////////////////////////////////////////////////////////////////////////////////////////
// Implementation
////////////////////////////////////////////////////////////////////////////////////////////////////
void Person::SetEverFatherFate()
{
if (person_type != PT_START && integer_age == 1 && sex==MALE && is_resident)
{
never_father = (RandUniform(36)
< MaleCohortChildlessness[RANGE_POS(YOB_MALEFERT, year_of_birth)][birth1_group]);
}
}
void Clock::ImputeEverFather()
{
if (clock_year == MIN(SIM_YEAR_RANGE))
{
int nPopSize;
int nPersons[SIZE(YOB_START15)][SIZE(BIRTH1_GROUP)];
int nExpected[SIZE(YOB_START15)][SIZE(BIRTH1_GROUP)];
int nRecorded[SIZE(YOB_START15)][SIZE(BIRTH1_GROUP)];
// initialize with 0
for (int nJ = 0; nJ < SIZE(YOB_START15); nJ++)
{
for (int nGroup = 0; nGroup < SIZE(BIRTH1_GROUP); nGroup++)
{
nPersons[nJ][nGroup] = 0; nExpected[nJ][nGroup] = 0; nRecorded[nJ][nGroup] = 0;
}
}
// assign status is_father where there are children in hh
// count men and known fathers by yob
nPopSize = asAllResidents->Count();
for (int nI = 0; nI < nPopSize; nI++)
{
auto prPerson = asAllResidents->Item(nI);
if (WITHIN(YOB_START15, prPerson->year_of_birth) && prPerson->sex == MALE)
{
int nYOB = prPerson->year_of_birth - MIN(YOB_START15);
nPersons[nYOB][prPerson->birth1_group] = nPersons[nYOB][prPerson->birth1_group] + 1;
if (prPerson->children_in_household > 0 || (prPerson->lSpouse && prPerson->lSpouse->children_in_household >0))
{
nRecorded[nYOB][prPerson->birth1_group] = nRecorded[nYOB][prPerson->birth1_group] + 1;
prPerson->never_father = FALSE;
}
}
else if (prPerson->sex == MALE) // those below 15
{
prPerson->never_father = (RandUniform(37) < MaleCohortChildlessness[RANGE_POS(YOB_MALEFERT, prPerson->year_of_birth)][prPerson->birth1_group]);
}
}
//calculate expected number of fathers by yob
for (int nGroup = 0; nGroup < SIZE(BIRTH1_GROUP); nGroup++)
{
for (int nJ = 0; nJ < SIZE(YOB_START15); nJ++)
{
nExpected[nJ][nGroup] = round(nPersons[nJ][nGroup] * (1 - MaleCohortChildlessness[RANGE_POS(YOB_MALEFERT, nJ+MIN(YOB_START15))][nGroup]));
}
}
//randomly choose childless men to (ever) become fathers in order to meet target numbers
for (int nGroup = 0; nGroup < SIZE(BIRTH1_GROUP); nGroup++)
{
for (int nJ = 0; nJ < SIZE(YOB_START15); nJ++)
{
if (nExpected[nJ][nGroup] > nRecorded[nJ][nGroup])
{
for (int nI = 0; nI < nExpected[nJ][nGroup] - nRecorded[nJ][nGroup]; nI++)
{
if (asMenNeverFather[RANGE_POS(ALL_YEAR_RANGE, MIN(YOB_START15) + nJ)][nGroup]->Count() > 0)
{
auto prFather = asMenNeverFather[RANGE_POS(ALL_YEAR_RANGE, MIN(YOB_START15) + nJ)][nGroup]->GetRandom(RandUniform(38));
prFather->never_father = FALSE;
}
}
}
}
}
}
}
MortalityPeriodLifeTable.mpp
This module implements a simple model of mortality. People can die at any moment of time based on age-specific period mortality rates. At death, the state is_alive is set to FALSE, and the Modgen function Finish() is called which clears up memory space.
The module is prepared for being combined with or replaced by a refined model accounting for more detailed personal characteristics than age and sex. For this purpose, a logical state use_base_mortality is introduced and initialized with TRUE; Other modules can override the waiting time function of this module by setting the state to FALSE whenever an alternative model is applied.
////////////////////////////////////////////////////////////////////////////////////////////////////
// Parameters
////////////////////////////////////////////////////////////////////////////////////////////////////
parameters
{
double MortalityTable[SEX][AGE_RANGE][SIM_YEAR_RANGE]; //EN Mortality hazard by age
};
parameter_group PG02_OverallMortality //EN Overall mortality
{
MortalityTable
};
////////////////////////////////////////////////////////////////////////////////////////////////////
// Actor states and functions
////////////////////////////////////////////////////////////////////////////////////////////////////
actor Person
{
logical use_base_mortality = { TRUE }; //EN Use the base version
event timeMortalityEvent, MortalityEvent; //EN Mortality event
};
////////////////////////////////////////////////////////////////////////////////////////////////////
// Implementation of functions and events
////////////////////////////////////////////////////////////////////////////////////////////////////
TIME Person::timeMortalityEvent()
{
TIME dEventTime = TIME_INFINITE;
double dMortalityHazard
= MortalityTable[sex][integer_age][RANGE_POS(SIM_YEAR_RANGE, calendar_year)];
// check if a person is at risk
if (dMortalityHazard > 0.0 && in_projected_time && ever_resident && use_base_mortality)
{
// determine the event time
// the formula [ -log(rand) / hazard ] calculates an exponentially distributed waiting time
// based on a uniform distributed random number and a given hazard rate
dEventTime = WAIT(-log(RandUniform(14)) / dMortalityHazard);
}
// return the event time, if the maximum age is not reached at that point
if (dEventTime < time_of_birth + MAX(AGE_RANGE) + 1.0) return dEventTime;
// otherwise, return the moment, at which the maximum age is reached
else return time_of_birth + MAX(AGE_RANGE) + 0.9999;
}
void Person::MortalityEvent()
{
IMPLEMENT_HOOK();
//lPartner = NULL;
is_alive = FALSE;
Finish(); // Remove the actor from the simulation.
}
MortalityByEducation.mpp
This module implements differential mortality by population groups. While the grouping is generic, in the current implementation, it refers to educational attainment, but the list can easily be extended or changed. The module is optional, and the user can choose between various options:
Disable the module: the overall mortality rates of the base model apply, and this module is switched off.
Model without alignment: The base rates are adjusted to meet life expectancy parameters by population group. Parameters are the remaining period life expectancy at age 25 and age 65 by sex. This adjustment is made in the pre-simulation phase, where new parameter tables are generated.
Model with alignment: after the initial calibration of rates to meet life expectancy targets by group, the resulting rates are proportionally scaled back in order to keep the overall mortality rates of the base model while accounting for the relative mortality differences by population group. This alignment is performed in yearly intervals during the simulation accounting for the changing population composition by risk group.
How to change or add population groups:
The list of population groups for which relative risks are applied is generic and can be modified and extended easily.
The classification CHILD_MORTALITY_GROUP lists all population groups distinguished.
The state mortality_group contains the individual group membership and has to be adapted if the list of categories is modified.
Besides these two changes, no coding is required. The calibration routines automatically adjust the hazards according to the population composition by group membership.
////////////////////////////////////////////////////////////////////////////////////////////////////
// Types
////////////////////////////////////////////////////////////////////////////////////////////////////
classification SELECTED_MORTALITY_MODEL //EN Mortality Model Options
{
SMM_MACRO, //EN Base model
SMM_MICRO_NOT_ALIGNED, //EN Model by education
SMM_MICRO_ALIGNED //EN Model by education aligned to base model
};
classification MORTALITY_GROUP //EN Population group for mortality
{
MG_00, //EN Low education
MG_01, //EN Medium education
MG_02 //EN High education
};
classification LIFE_EXPECT //EN Life Expectancy Parameters
{
LE_25, //EN Life expectancy at 25
LE_65 //EN Life expectancy at 65
};
////////////////////////////////////////////////////////////////////////////////////////////////////
// Parameters
////////////////////////////////////////////////////////////////////////////////////////////////////
parameters
{
//EN Mortality model selection
SELECTED_MORTALITY_MODEL SelectedMortalityModel;
//EN Period life expectancy
double LifeExpectancy[SEX][LIFE_EXPECT][SIM_YEAR_RANGE][MORTALITY_GROUP];
//EN Mortality Trend
model_generated double MortalityByGroup[SEX][MORTALITY_GROUP][AGE_RANGE][SIM_YEAR_RANGE];
};
parameter_group PG02_RefinedMortality //EN Mortality by Education
{
LifeExpectancy
};
parameter_group PG_Mortality //EN Mortality
{
SelectedMortalityModel,
PG02_OverallMortality,
PG02_RefinedMortality
};
////////////////////////////////////////////////////////////////////////////////////////////////////
// Actor Globals
////////////////////////////////////////////////////////////////////////////////////////////////////
actor Globals
{
void MortalityCalibration(); //EN Mortality calibration
double CurrentAlignedMortality[MORTALITY_GROUP][AGE_RANGE][SEX];
};
////////////////////////////////////////////////////////////////////////////////////////////////////
// Actor Clock
////////////////////////////////////////////////////////////////////////////////////////////////////
actor Clock
{
void CallMortalityCalibration(); //EN Call mortality calibration event
hook CallMortalityCalibration, StartYearClock;
};
////////////////////////////////////////////////////////////////////////////////////////////////////
// Actor Person
////////////////////////////////////////////////////////////////////////////////////////////////////
actor Person
{
//EN Child mortality risk group
MORTALITY_GROUP mortality_group = (educ_fate == EL3_LOW) ? MG_00 :
(educ_fate == EL3_MEDIUM) ? MG_01 : MG_02;
// Current aligned mortality
double current_aligned_mortality = { 0.0 };
//EN Activates Module at birth if selected
void ActivateRefinedMortalityModule(); hook ActivateRefinedMortalityModule, Start;
event timeRefinedMortalityEvent, RefinedMortalityEvent; //EN Mortality Event
};
////////////////////////////////////////////////////////////////////////////////////////////////////
// Person functions implementation
////////////////////////////////////////////////////////////////////////////////////////////////////
void Person::ActivateRefinedMortalityModule()
{
if ( SelectedMortalityModel != SMM_MACRO) use_base_mortality = FALSE;
}
TIME Person::timeRefinedMortalityEvent()
{
double dEventTime = TIME_INFINITE;
if ( !use_base_mortality && in_projected_time && ever_resident)
{
double dHazard = MortalityByGroup[sex][mortality_group][integer_age]
[RANGE_POS(SIM_YEAR_RANGE, calendar_year)];
// aligned model
if (SelectedMortalityModel == SMM_MICRO_ALIGNED)
{
dHazard = current_aligned_mortality;
}
// if hazard is positive calculate event time
if (dHazard > 0) dEventTime = WAIT(-log(RandUniform(15)) / dHazard);
}
// return the event time, if the maximum age is not reached at that point
if (dEventTime < time_of_birth + MAX(AGE_RANGE) + 1.0) return dEventTime;
// otherwise, return the moment, at which the maximum age is reached
else if (!use_base_mortality) return time_of_birth + MAX(AGE_RANGE) + 0.9999;
else return TIME_INFINITE;
}
void Person::RefinedMortalityEvent()
{
MortalityEvent();
}
////////////////////////////////////////////////////////////////////////////////////////////////////
// Clock functions implementation
////////////////////////////////////////////////////////////////////////////////////////////////////
void Clock::CallMortalityCalibration()
{
if (clock_year >= MIN(SIM_YEAR_RANGE) && SelectedMortalityModel == SMM_MICRO_ALIGNED)
{
asGlobals->Item(0)->MortalityCalibration();
}
}
////////////////////////////////////////////////////////////////////////////////////////////////////
// Globals functions implementation
////////////////////////////////////////////////////////////////////////////////////////////////////
void Globals::MortalityCalibration()
{
// local matrices
double dDeaths[SIZE(AGE_RANGE)][SIZE(SEX)]; // Number expected deaths
double dProb[SIZE(AGE_RANGE)][SIZE(SEX)]; // Death probability
long nPop[SIZE(AGE_RANGE)][SIZE(SEX)][SIZE(MORTALITY_GROUP)]; // Pop sizes
double dCalibrator[SIZE(AGE_RANGE)][SIZE(SEX)]; // Baseline Hazard in simulation
Clock * prClock = asClock->Item(0);
int nYear = prClock->clock_year- MIN(SIM_YEAR_RANGE);
//int nYear = asClock->Item(0)->clock_year - MIN(SIM_YEAR_RANGE); // Current Yyear since start
// Initialize matrices
// Set population sizes nPop and expected deaths nDeaths to 0
// sets death probabilities dProb by age and sex for the year in which the model is calibrated
for (int nAge = 0; nAge < SIZE(AGE_RANGE); nAge++)
{
for (int nSex = 0; nSex < SIZE(SEX); nSex++)
{
dProb[nAge][nSex] = 1.0 - exp(-MortalityTable[nSex][nAge][nYear]);
dDeaths[nAge][nSex] = 0.0;
for (int nGroup = 0; nGroup < SIZE(MORTALITY_GROUP); nGroup++)
{
nPop[nAge][nSex][nGroup] = 0.0;
}
}
}
// Population Count
// calculates population sizes nPop by age, sex, risk group
// calculates expected deaths dDeaths by age and sex
for (long nJ = 0; nJ < asAllResidents->Count(); nJ++)
{
auto prPerson = asAllResidents->Item(nJ);
dDeaths[prPerson->integer_age][prPerson->sex] = dDeaths[prPerson->integer_age][prPerson->sex]
+ dProb[prPerson->integer_age][prPerson->sex];
nPop[prPerson->integer_age][prPerson->sex][prPerson->mortality_group]++;
}
// find calibrated baselines
for (int nAge = 0; nAge < SIZE(AGE_RANGE); nAge++)
{
for (int nSex = 0; nSex < SIZE(SEX); nSex++)
{
double dUpper = 2.0;
double dLower = 0.0;
double dCenter = 1.0;
double dNumberDeaths = 0.0;
int nIterations = 10000;
while (abs(dNumberDeaths - dDeaths[nAge][nSex]) > 0.0001 && nIterations > 0)
{
nIterations--;
dCalibrator[nAge][nSex] = (dLower + dUpper) / 2.0;
dNumberDeaths = 0.0;
//Calculate numer of deaths for given dCalibrator
for (int nGroup = 0; nGroup < SIZE(MORTALITY_GROUP); nGroup++)
{
dNumberDeaths = dNumberDeaths + nPop[nAge][nSex][nGroup]
* (1 - exp(-dCalibrator[nAge][nSex] * MortalityByGroup[nSex][nGroup][nAge][nYear]));
}
// shrink search interval
if (dNumberDeaths > dDeaths[nAge][nSex]) dUpper = dCalibrator[nAge][nSex];
else dLower = dCalibrator[nAge][nSex];
}
}
}
// set global mortality by group, age and sex
for (int nAge = 0; nAge < SIZE(AGE_RANGE); nAge++)
{
for (int nSex = 0; nSex < SIZE(SEX); nSex++)
{
for (int nGroup = 0; nGroup < SIZE(MORTALITY_GROUP); nGroup++)
{
CurrentAlignedMortality[nGroup][nAge][nSex]
= MortalityByGroup[nSex][nGroup][nAge][nYear] * dCalibrator[nAge][nSex];
}
}
}
for (long nJ = 0; nJ < asAllResidents->Count(); nJ++)
{
auto prPerson = asAllResidents->Item(nJ);
prPerson->current_aligned_mortality
= CurrentAlignedMortality[prPerson->mortality_group][prPerson->integer_age][prPerson->sex];
}
}
////////////////////////////////////////////////////////////////////////////////////////////////////
// Pre-Simulation
////////////////////////////////////////////////////////////////////////////////////////////////////
void PreSimulation()
{
// In the pre-simulation phase mortality rates by population group are found by binary search.
// For each simulated year, the overall mortality table is adjusted to obtain target life
// expectancies at age 65 and at age 25 for each distinguished population group.
// Local variables
double dTarget, dCenter, dResult, dAlive, dDeaths, dLower, dUpper;
int nIterations;
if (SelectedMortalityModel != SMM_MACRO)
{
// Find trend factors to fit the life expectancy at 65
for (int nSex = 0; nSex < SIZE(SEX); nSex++)
{
for (int nYear = 0; nYear < SIZE(SIM_YEAR_RANGE); nYear++)
{
for (int nGroup = 0; nGroup < SIZE(MORTALITY_GROUP); nGroup++)
{
dTarget = LifeExpectancy[nSex][LE_65][nYear][nGroup]; // Target life expectancy
dResult = 0.0; // Search result: life expectancy
dLower = 0.1; // Lower limit of calibration factor
dUpper = 10.0; // Upper limit of calibration factor
nIterations = 100000; // Maximal iterations
while (abs(dResult - dTarget) > 0.0001 && nIterations > 0)
{
nIterations--;
dCenter = (dLower + dUpper) / 2.0; // New calibration factor for probing
dResult = 0.0;
dAlive = 1.0; // Proportion of people still alive
// Life expectancy calculated applying calibration factor
for (int nAge = 65; nAge < SIZE(AGE_RANGE); nAge++)
{
// proportion of deaths in year: survival = exp(-hazard)
dDeaths = dAlive * (1 - exp(-MortalityTable[nSex][nAge][nYear] * dCenter));
dAlive = dAlive - dDeaths;
// people dying in this year are assumed to die in the middle of the year
dResult = dResult + dDeaths * 0.5 + dAlive;
}
// Moving the search limits for next iteration
if (dTarget < dResult) dLower = dCenter;
else dUpper = dCenter;
}
// applying the best solution to the mortality parameter
for (int nAge = 65; nAge < SIZE(AGE_RANGE); nAge++)
{
MortalityByGroup[nSex][nGroup][nAge][nYear] = dCenter * MortalityTable[nSex][nAge][nYear];
}
}
}
}
// Find trend factors to fit the life expectancy at 25 (while keeping the trend for 65+)
for (int nSex = 0; nSex < SIZE(SEX); nSex++)
{
for (int nYear = 0; nYear < SIZE(SIM_YEAR_RANGE); nYear++)
{
for (int nGroup = 0; nGroup < SIZE(MORTALITY_GROUP); nGroup++)
{
dTarget = LifeExpectancy[nSex][LE_25][nYear][nGroup]; // Target life expectancy
dResult = 0.0; // Search result: life expectancy
dLower = 0.1; // Lower limit of calibration factor
dUpper = 10.0; // Upper limit of calibration factor
nIterations = 100000; // Maximal iterations
while (abs(dResult - dTarget) > 0.0001 && nIterations > 0)
{
nIterations--;
dCenter = (dLower + dUpper) / 2.0; // New calibration factor for probing
dResult = 0.0;
dAlive = 1.0; // Proportion of people still alive
// Life expectancy calculated applying calibration factor
for (int nAge = 25; nAge < SIZE(AGE_RANGE); nAge++)
{
// proportion of deaths in year: survival = exp(-hazard)
if (nAge < 65) // Apply searched adjustment factor only for ages below 65
{
dDeaths = dAlive * (1 - exp(-MortalityTable[nSex][nAge][nYear] * dCenter));
}
else // apply known factor for ages 65+
{
dDeaths = dAlive * (1 - exp(-MortalityByGroup[nSex][nGroup][nAge][nYear]));
}
dAlive = dAlive - dDeaths;
// people dying in this year are assumed to die in the middle of the year
dResult = dResult + dDeaths * 0.5 + dAlive;
}
// Moving the search limits for next iteration
if (dTarget < dResult) dLower = dCenter;
else dUpper = dCenter;
}
// applying the best solution to the mortality parameter
for (int nAge = 25; nAge < 65; nAge++)
{
MortalityByGroup[nSex][nGroup][nAge][nYear] = dCenter * MortalityTable[nSex][nAge][nYear];
}
}
}
}
// Copy over parameters for age < 25
for (int nSex = 0; nSex < SIZE(SEX); nSex++)
{
for (int nYear = 0; nYear < SIZE(SIM_YEAR_RANGE); nYear++)
{
for (int nAge = 0; nAge < 25; nAge++)
{
for (int nGroup = 0; nGroup < SIZE(MORTALITY_GROUP); nGroup++)
{
MortalityByGroup[nSex][nGroup][nAge][nYear] = MortalityTable[nSex][nAge][nYear];
}
}
}
}
}
}
EducBaseFate.mpp
This module sets the base education fate and stores the underlying individual probabilities of education progressions. The education fate is decided at birth. There are three different outcomes: low, medium, high. The module sets three states at birth:
educ_fate: the individual outcome in 3 levels low, medium, high.
educ_prob1: the individual probability to move from low to medium.
educ_prob2: the individual probability to move from medium to high.
Two parameters contain the probabilities to progress from low to medium and from medium to high by year of birth and sex. The way how the education fate is decided depends on the year of birth and the person type.
Persons from the starting population: the education outcome is taken from the starting population file if the person was born before the years of birth covered by the parameters. This means, up to two age cut-offs, the information of the starting population is ignored at all or only used to decide the first progression (low to medium, but not medium to high which is modelled).
Persons born in the simulation: the education fate is decided based on the parameters.
The function SetEducBaseFate() which decides the fate is hooked to the SetAlive() Event of the PersonCore.mpp module, i.e. is called directly at birth after the actor enters the simulation and all actor sets are available, and family links are operational.
This module is the base version for the education fate. As the probabilities behind the decision are stored, the outcome can be adjusted by a refined model in order to account for additional individual-level characteristics (like parents’ education) and/or to align the aggregated outcome to target rates.
////////////////////////////////////////////////////////////////////////////////////////////////////
// Types
////////////////////////////////////////////////////////////////////////////////////////////////////
classification EDUC_LEVEL3 //EN Education level
{
EL3_LOW, //EN Low
EL3_MEDIUM, //EN Medium
EL3_HIGH //EN High
};
classification EDUC_LEVEL5 //EN Education Level
{
EL5_LOW, //EN Low
EL5_MEDIUMD, //EN Medium - dual
EL5_MEDIUMV, //EN Medium - vocational
EL5_MEDIUMG, //EN Medium - general
EL5_HIGH //EN High
};
aggregation EDUC_LEVEL3, EDUC_LEVEL5 //EN Aggregation of Education Levels
{
EL3_LOW, EL5_LOW,
EL3_MEDIUM, EL5_MEDIUMD,
EL3_MEDIUM, EL5_MEDIUMV,
EL3_MEDIUM, EL5_MEDIUMG,
EL3_HIGH, EL5_HIGH
};
////////////////////////////////////////////////////////////////////////////////////////////////////
// Parameters
////////////////////////////////////////////////////////////////////////////////////////////////////
parameters
{
//EN Education progression probability low -> medium
double EducProg1[SEX][YOB_EDUC_PROG1];
//EN Education progression probability medium -> high
double EducProg2[SEX][YOB_EDUC_PROG2];
};
parameter_group PG_EducBaseFate //EN Education Base Fate Model
{
EducProg1, EducProg2
};
////////////////////////////////////////////////////////////////////////////////////////////////////
// Actor States and Events
////////////////////////////////////////////////////////////////////////////////////////////////////
actor Person
{
EDUC_LEVEL3 educ_fate = { EL3_LOW }; //EN Primary Education Fate
EDUC_LEVEL3 educ_startpop = { EL3_LOW }; //EN Primary Education Fate
double educ_prob1 = { 0.0 }; //EN Base prob. progressing low medium
double educ_prob2 = { 0.0 }; //EN Base prob. progressing medium high
void SetEducBaseFate(); //EN Setting the education fate
hook SetEducBaseFate, SetAliveEvent, 3; //EN Hook SetEducBaseFate to SetAlive
};
////////////////////////////////////////////////////////////////////////////////////////////////////
// Implementation
////////////////////////////////////////////////////////////////////////////////////////////////////
void Person::SetEducBaseFate()
{
// Person born in simulation
if ( person_type == PT_CHILD )
{
educ_prob1 = EducProg1[sex][RANGE_POS(YOB_EDUC_PROG1,year_of_birth)];
educ_prob2 = EducProg2[sex][RANGE_POS(YOB_EDUC_PROG2,year_of_birth)];
EDUC_LEVEL3 eolFate = EL3_LOW;
if ( RandUniform(5) < educ_prob1 ) eolFate = EL3_MEDIUM;
if ( eolFate == EL3_MEDIUM && RandUniform(19) < educ_prob2 ) eolFate = EL3_HIGH;
educ_fate = eolFate;
}
// Person from starting population
else if ( person_type == PT_START )
{
// Case 1: take the education from the starting population
if ( year_of_birth < MIN(YOB_EDUC_PROG2))
{
educ_fate = educ_startpop;
}
// Case 2: take first progression from the starting population
else if ( year_of_birth < MIN(YOB_EDUC_PROG1))
{
educ_prob2 = EducProg2[sex][RANGE_POS(YOB_EDUC_PROG2,year_of_birth)];
EDUC_LEVEL3 eolFate = EL3_LOW;
if ( educ_startpop != EL3_LOW ) eolFate = EL3_MEDIUM;
if ( eolFate == EL3_MEDIUM && RandUniform(7) < educ_prob2 ) eolFate = EL3_HIGH;
educ_fate = eolFate;
}
// Case 3: model from parameters ignoring starting population values
else
{
educ_prob1 = EducProg1[sex][RANGE_POS(YOB_EDUC_PROG1,year_of_birth)];
educ_prob2 = EducProg2[sex][RANGE_POS(YOB_EDUC_PROG2,year_of_birth)];
EDUC_LEVEL3 eolFate = EL3_LOW;
if ( RandUniform(8) < educ_prob1 ) eolFate = EL3_MEDIUM;
if ( eolFate == EL3_MEDIUM && RandUniform(9) < educ_prob2 ) eolFate = EL3_HIGH;
educ_fate = eolFate;
}
}
}
EducRefinedFate.mpp
This module allows to add relative factors (odds ratios) to the modeling of education outcomes. Currently, the mother’s education by sex is introduced. The relative factors are used to modify the base probabilities of school progressions. Aggregated outcomes by sex are calibrated in order to match the base model’s outcome. This calibration is performed either (1) for all years of birth, or (2) just once. In the first case, aggregate outcomes by sex are identical, but the model picks different children entering and graduating school, accounting for the relative differences (odds ratios) by mother’s education. In the second case, the calibration is done for a selected year of birth. For all following cohorts, parameters are frozen, and all trends result from composition effects due to the changing educational composition of mothers. The module only affects persons born in the simulation as the mother’s education is unknown for others.
Parameters:
Model selection: the three choices are (1) Use Base Model, (2) Use the refined model calibrated for all years of birth, (3) Use the refined model calibrated once.
Odds for progressing from low to medium by parents education
Odds for progressing from medium to high education by parents education
The first birth cohort from which onwards the refined model is used, which can be any year beginning from the starting year of the simulation.
In its core, this module consists of two functions.
The function SetEduc1AdjustmentFactors() calculates the required adjustment factors (in the form of log odds by district and sex) to be applied in addition to the relative factors. The calculations are performed at the end of the calendar year. This is when the educational composition of mothers of all born in this year is known. Adjustment factors are either calculated once or updated each year according to the user’s model selection.
The function AdjustEducOne() applies the adjustments at the Person level at the first end of the year after birth. Thereby this module modifies the three “fate” states introduced in the base model, namely the two individual progression probabilities educ_prob1 and educ_prob2, as well as random outcome educ_fate based on the new probabilities.
How to change or expand the list of relative factors:
The list of population groups is declared in the classification educ_group. The classification can be changed or expanded whereby a person can belong only to one of the groups. (For example, when adding ethnicity, all combinations of ethnicity and mother’s education have to be listed)
The individual group membership is a derived state educ_group. When changing the levels of educ_group, the calculation of the derived state has to be updated accordingly.
No other code changes are required
////////////////////////////////////////////////////////////////////////////////////////////////////
// Actor Sets
////////////////////////////////////////////////////////////////////////////////////////////////////
actor_set Person asSimBornAge0[sex][educ_group]
filter is_alive && is_resident && integer_age == 0;
////////////////////////////////////////////////////////////////////////////////////////////////////
// Types
////////////////////////////////////////////////////////////////////////////////////////////////////
classification EDUC_GROUP //EN Population Groups
{
EG_00, //EN Parents education low
EG_01, //EN Parents education medium
EG_02 //EN Parents education high
};
classification EDUC_MODEL //EN Education Model Selection
{
EM_BASE, //EN Use base model
EM_REFINED_ALIGNALL, //EN Use refined model aligned to base for all birth cohorts
EM_REFINED_ALIGNONCE //EN Use refined model aligned to base once
};
////////////////////////////////////////////////////////////////////////////////////////////////////
// Parameters
////////////////////////////////////////////////////////////////////////////////////////////////////
parameters
{
double EducProg1Odds[EDUC_GROUP][SEX]; //EN Odds of progressing low->medium
double EducProg2Odds[EDUC_GROUP][SEX]; //EN Odds of progressing medium->high
EDUC_MODEL EducModel; //EN Model Selection
int EducFirstCohortRefinedModel; //EN First birth cohort to apply refined model
};
parameter_group PG_EducRefinedFate //EN Education Fate - Refined Model
{
EducProg1Odds, EducProg2Odds,
EducFirstCohortRefinedModel
};
parameter_group PG_SchoolFate //EN Education Fate
{
EducModel,
PG_EducBaseFate,
PG_EducRefinedFate
};
////////////////////////////////////////////////////////////////////////////////////////////////////
// Actor states and functions
////////////////////////////////////////////////////////////////////////////////////////////////////
actor Globals //EN Actor Globals
{
double alignment_educ_medium[SEX]; //EN Education alignment low to medium
double alignment_educ_high[SEX]; //EN Education alignment medium to high
};
actor Clock
{
void SetEducAdjustmentFactors(); //EN Function calculating calibration factors
double AdjustedProbability(double dProb, double dLogOddEduc, double dLogOddAdjust);
};
actor Person
{
EDUC_LEVEL3 educ_parents = { EL3_LOW }; //EN Parents education
logical educ_parents_is_known = { FALSE }; //EN Parents education is known
//EN Education risk group
EDUC_GROUP educ_group = (educ_parents == EL3_LOW) ? EG_00 :
(educ_parents == EL3_MEDIUM) ? EG_01 : EG_02;
void AdjustEduc(); hook AdjustEduc, YearEnd;
void SetEducParents(); hook SetEducParents, SetAliveEvent, 2;
};
////////////////////////////////////////////////////////////////////////////////////////////////////
// Implementation
////////////////////////////////////////////////////////////////////////////////////////////////////
void Person::SetEducParents()
{
EDUC_LEVEL3 cEducParents = EL3_LOW;
if (is_resident && (lHHMother || lHHFather))
{
educ_parents_is_known = TRUE;
if (lHHMother) cEducParents = lHHMother->educ_fate;
if (lHHFather && lHHFather->educ_fate > cEducParents) cEducParents = lHHFather->educ_fate;
educ_parents = cEducParents;
}
}
void Clock::SetEducAdjustmentFactors()
{
if (clock_year >= MIN(SIM_YEAR_RANGE) && (
(EducModel == EM_REFINED_ALIGNALL && clock_year >= EducFirstCohortRefinedModel) ||
(EducModel == EM_REFINED_ALIGNONCE && clock_year == EducFirstCohortRefinedModel)))
{
for (int nSex = 0; nSex < SIZE(SEX); nSex++)
{
// calculate total population for given sex
double nTotalPop = 0.0;
for (int dGroup = 0; dGroup < SIZE(EDUC_GROUP); dGroup++)
{
nTotalPop = nTotalPop + asSimBornAge0[nSex][dGroup]->Count();
}
// Run Adjustment for school entry
double dFactorEntry = 0.0;
if (nTotalPop > 0.0)
{
int nIterations = 0;
double dResultProb = 10.0;
double dTargetProb = EducProg1[nSex][RANGE_POS(YOB_EDUC_PROG1, clock_year)];
double dLower = -10.0;
double dUpper = 10.0;
double dCenter = 0.0;
while (abs(dResultProb - dTargetProb) > 0.0001 && nIterations < 10000)
{
nIterations++;
dCenter = (dLower + dUpper) / 2.0;
dResultProb = 0.0;
for (int nGroup = 0; nGroup < SIZE(EDUC_GROUP); nGroup++)
{
dResultProb = dResultProb + (asSimBornAge0[nSex][nGroup]->Count() / nTotalPop) *
AdjustedProbability(dTargetProb, log(EducProg1Odds[nGroup][nSex]), dCenter);
}
if (dTargetProb > dResultProb) dLower = dCenter;
else dUpper = dCenter;
}
dFactorEntry = dCenter;
}
// set factor
asGlobals->Item(0)->alignment_educ_medium[nSex] = dFactorEntry;
// Run Adjustment for medium high progression
double dFactorGrad = 0.0;
if (nTotalPop > 0.0)
{
int nIterations = 0;
double dResultProb = 10.0;
double dTargetProb = EducProg2[nSex][RANGE_POS(YOB_EDUC_PROG2, clock_year)];
double dLower = -10.0;
double dUpper = 10.0;
double dCenter = 0.0;
while (abs(dResultProb - dTargetProb) > 0.0001 && nIterations < 10000)
{
nIterations++;
dCenter = (dLower + dUpper) / 2.0;
dResultProb = 0.0;
for (int nGroup = 0; nGroup < SIZE(EDUC_GROUP); nGroup++)
{
dResultProb = dResultProb + (asSimBornAge0[nSex][nGroup]->Count() / nTotalPop) *
AdjustedProbability(dTargetProb, log(EducProg2Odds[nGroup][nSex]), dCenter);
}
if (dTargetProb > dResultProb) dLower = dCenter;
else dUpper = dCenter;
}
dFactorGrad = dCenter;
}
// set factor
asGlobals->Item(0)->alignment_educ_high[nSex] = dFactorGrad;
}
}
}
double Clock::AdjustedProbability(double dProb, double dLogOddEduc, double dLogOddAdjust)
{
if (dProb >= 0.9999) dProb = 0.9999;
double dExp = exp(log(dProb / (1 - dProb)) + dLogOddEduc + dLogOddAdjust);
return dExp / (1 + dExp);
}
void Person::AdjustEduc()
{
// Adjustment for selection of refined model aligned to base for all birth cohorts
if (person_type == PT_CHILD && integer_age == 0 && calendar_year >= MIN(SIM_YEAR_RANGE) &&
calendar_year >= EducFirstCohortRefinedModel && EducModel == EM_REFINED_ALIGNALL)
{
educ_prob1 = lClock->AdjustedProbability(
educ_prob1,
log(EducProg1Odds[educ_group][sex]),
asGlobals->Item(0)->alignment_educ_medium[sex]);
educ_prob2 = lClock->AdjustedProbability(
educ_prob2,
log(EducProg2Odds[educ_group][sex]),
asGlobals->Item(0)->alignment_educ_high[sex]);
EDUC_LEVEL3 eolFate = EL3_LOW;
if (RandUniform(10) < educ_prob1) eolFate = EL3_MEDIUM;
if (eolFate == EL3_MEDIUM && RandUniform(11) < educ_prob2) eolFate = EL3_HIGH;
educ_fate = eolFate;
}
else if (person_type == PT_CHILD && integer_age == 0 && calendar_year >= MIN(SIM_YEAR_RANGE) &&
calendar_year >= EducFirstCohortRefinedModel && EducModel == EM_REFINED_ALIGNONCE)
{
educ_prob1 = lClock->AdjustedProbability(
EducProg1[sex][RANGE_POS(YOB_EDUC_PROG1, EducFirstCohortRefinedModel)],
log(EducProg1Odds[educ_group][sex]),
asGlobals->Item(0)->alignment_educ_medium[sex]);
educ_prob2 = lClock->AdjustedProbability(
EducProg2[sex][RANGE_POS(YOB_EDUC_PROG2, EducFirstCohortRefinedModel)],
log(EducProg2Odds[educ_group][sex]),
asGlobals->Item(0)->alignment_educ_high[sex]);
EDUC_LEVEL3 eolFate = EL3_LOW;
if (RandUniform(12) < educ_prob1) eolFate = EL3_MEDIUM;
if (eolFate == EL3_MEDIUM && RandUniform(13) < educ_prob2) eolFate = EL3_HIGH;
educ_fate = eolFate;
}
}
//table Person PareducTabTest //EN Parents education
//[is_resident]
//{
// {
// value_out(educ_parents_is_known) / unit
// }
// * year_of_birth
//};
EducationPattern.mpp
The module implements 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. Attendance can be fulltime or parttime. The model is a ‘fate-model’ where for a given outcome, a pattern is assigned early in life. Patterns depend on the outcome (which itself depends on the year of birth, sex and parents’ characteristics) and sex. For people of the starting population, educational a pattern is imputed using the same model, but if possible, respecting the variables on current school attendance. This is achieved in two steps. First, for a given fate, a pattern is assigned. When all persons 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 happen within groups by sex and parents’ education, thus do not affect the aggregate modelled school attendance.
Once assigned, the actual school career is updated in yearly schoolyear steps. For a given final fate and education pattern, three states are set:
educ_level: the current highest educational attainment.
educ_status: the current education status (full-time, part-time, dual, pause etc.)
educ_pattern_status: the current school type and level
///////////////////////////////////////////////////////////////////////////////////////////////////
// Dimensions
///////////////////////////////////////////////////////////////////////////////////////////////////
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 Pattern
{
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 && is_resident;
//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 && is_resident;
//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 && is_resident;
///////////////////////////////////////////////////////////////////////////////////////////////////
// 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;
IMPLEMENT_HOOK();
}
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 && is_resident)
{
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;
// Reset pattern status to never entered
SetCurrentEducLevelPatternStatus(0);
}
}
}
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;
}
EducationAlignment.mpp
This module allows aligning school attendance to external school enrolment parameters. The module is optional and can be switched off by the user. The module works on top of the education pattern module and does not interfere with the school attendance patterns produced there. Alignment of education rates works in one direction only - namely increasing the number of students. This is to add studies which are not essential to reach the highest grade and to reproduce external enrolment targets. If the expected number of students by age and sex is higher in the alignment targets than produced by the pattern model, additional people are flagged as students by setting the state in_other_education to TRUE. The second state affected is in_school which can be true due to ‘normal’ school attendance (in_regular_school) or due to the additional attendance.
The algorithm simple: eligible for becoming a student in_other_education are those who have been in this status the school year before or those just finishing regular education. A second criterion is family: those living with children are excluded from the pool. People available for additional studies are defined in the actor set asAvailableForAdditionalStudies which can be easily modified to add realism. Alignments are made by the function AlignAfterSchoolYearChange(). The function does not force alignment if no additional students are found. This could be changed by increasing the pool of potential students, e.g. allowing people resuming studies after having left school before, or by allowing people with parenting obligations continuing or resuming studies.
////////////////////////////////////////////////////////////////////////////////////////////////////
// Actor sets
////////////////////////////////////////////////////////////////////////////////////////////////////
//EN People eligible for additional studies after finishing school
actor_set Person asAvailableForAdditionalStudies[sex][integer_age]
filter (year_finish_school == calendar_year || in_other_education)
&& children_in_household == 0 && !is_flagged_enrol_other_school && is_resident;
//EN All Persons by age and sex
actor_set Person asAllPersonsSexAge[sex][integer_age] filter is_alive && is_resident;
//EN All in regular education
actor_set Person asAllInRegularSchool[sex][integer_age] filter in_regular_school && is_resident;
range AGE_EDUC_ALIGN { 15,30 }; //EN Age
////////////////////////////////////////////////////////////////////////////////////////////////////
// Parameters
////////////////////////////////////////////////////////////////////////////////////////////////////
parameters
{
//EN School Enrolment rates (for alignment)
double SchoolEnrolmentRates[SEX][AGE_EDUC_ALIGN][SIM_YEAR_RANGE];
//EN Align school enrolment rates
logical AlignSchoolEnrolmentRates;
};
parameter_group PG_EducAlignment //EN Education Alignment
{
SchoolEnrolmentRates, AlignSchoolEnrolmentRates
};
////////////////////////////////////////////////////////////////////////////////////////////////////
// Actor states and functions
////////////////////////////////////////////////////////////////////////////////////////////////////
actor Person
{
//EN Person gegularely enrolled in school
logical in_regular_school = (educ_status == ES_FULLTIME || educ_status == ES_PARTTIME
|| educ_status == ES_DUAL) ? TRUE : FALSE;
//EN Person in other education (used to align in_school rates to higher targets)
logical in_other_education = { FALSE };
//EN Person enrolled in school incl. other (alignment)
logical in_school = (in_regular_school || in_other_education) ? TRUE : FALSE;
//EN Person flagged for enrolling in other education
logical is_flagged_enrol_other_school = { FALSE };
};
actor Clock
{
void AlignAfterSchoolYearChange(); //EN Align school attendance
hook AlignAfterSchoolYearChange, SchoolYearClock; //EN Hook school alignment to clock
};
////////////////////////////////////////////////////////////////////////////////////////////////////
// Implementation
////////////////////////////////////////////////////////////////////////////////////////////////////
void Clock::AlignAfterSchoolYearChange()
{
//if (AlignSchoolEnrolmentRates)
if (AlignSchoolEnrolmentRates)
{
for (int nAge = MIN(AGE_EDUC_ALIGN); nAge <= MAX(AGE_EDUC_ALIGN); nAge++)
{
for (int nSex = 0; nSex < SIZE(SEX); nSex++)
{
long nPopAvailable = asAvailableForAdditionalStudies[nSex][nAge]->Count();
long nPop = asAllPersonsSexAge[nSex][nAge]->Count();
long nAllInRegularSchool = asAllInRegularSchool[nSex][nAge]->Count();
long nMissing = long(nPop * SchoolEnrolmentRates[nSex]
[RANGE_POS(AGE_EDUC_ALIGN, nAge)][RANGE_POS(SIM_YEAR_RANGE, clock_year)]
- nAllInRegularSchool);
while (nMissing > 0 && asAvailableForAdditionalStudies[nSex][nAge]->Count() > 0)
{
auto prNewStudent = asAvailableForAdditionalStudies[nSex][nAge]
->GetRandom(RandUniform(34));
prNewStudent->is_flagged_enrol_other_school = TRUE;
nMissing--;
}
for (long nIndex = 0; nIndex < nPop; nIndex++)
{
auto prPerson = asAllPersonsSexAge[nSex][nAge]->Item(nIndex);
if (prPerson->is_flagged_enrol_other_school) prPerson->in_other_education = TRUE;
else prPerson->in_other_education = FALSE;
prPerson->is_flagged_enrol_other_school = FALSE;
}
}
}
// kick those too old out of school
for (int nAge = MAX(AGE_EDUC_ALIGN) + 1; nAge <= MAX(AGE_RANGE); nAge++)
{
for (int nSex = 0; nSex < SIZE(SEX); nSex++)
{
long nPop = asAllPersonsSexAge[nSex][nAge]->Count();
for (long nIndex = 0; nIndex < nPop; nIndex++)
{
asAllPersonsSexAge[nSex][nAge]->Item(nIndex)->in_other_education = FALSE;
}
}
}
}
}
Emigration.mpp
This module implements emigration. Users can choose three approaches:
Emigration based on time-invariant emigration rates by age and sex
Emigration based on a parameter of total emigration: if this option is chosen, the age- and sex-specific rates of the first model option are proportionally adjusted to meet aggregate totals.
Family emigration: This option keeps families together and meets the overall total emigration targets. In the current implementation, the person with the shortest waiting time is chosen and takes the whole family abroad until the total target is met. This approach does not maintain the age-specific rates.
The module is prepared to include individual-level differences in emigration rates (e.g. by migration background) and automatically adjusts individual rates in order to meet aggregate targets. Emigration is decided each mid-year and happens annually at this point of time. Emigrants leave the country and are immediately removed from the simulation.
Emigration is optional and can be switched off by the user. Also, users can choose to model net migration only, which is implemented in a separate module.
////////////////////////////////////////////////////////////////////////////////////////////////////
// Actor sets
////////////////////////////////////////////////////////////////////////////////////////////////////
//EN Residents by sex and age group
actor_set Person asResidentSexAge5[sex][age5_index]
filter is_alive && is_resident && in_projected_time;
//EN Residents by sex and age group - ordered by waiting time
actor_set Person asResidentOrderedSexAge5[sex][age5_index]
filter is_alive && is_resident && in_projected_time
order emigr_wait;
//EN Residents by sex - ordered by waiting time
actor_set Person asResidentOrdered
filter is_alive && is_resident && in_projected_time
order emigr_wait;
////////////////////////////////////////////////////////////////////////////////////////////////////
// Types
////////////////////////////////////////////////////////////////////////////////////////////////////
partition AGE5_PART //EN Age Groups
{
5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60,
65, 70, 75, 80, 85, 90
};
range AGE5_INDEX { 0,18 }; //EN Age group
classification EMIGR_SET //EN Emigration settings
{
ES_AGERATES, //EN Meet rates by age group and sex
ES_TOTALS, //EN Meet aggregate totals
ES_FAMILY //EN Family Emigration
};
////////////////////////////////////////////////////////////////////////////////////////////////////
// Parameters
////////////////////////////////////////////////////////////////////////////////////////////////////
parameters
{
model_generated logical ModelEmigration; //EN Switch emigration on/off
EMIGR_SET EmigrationSettings; //EN Emigration model choice
double EmigrationRates[SEX][AGE5_PART]; //EN Emigration rates by sex and age
long EmigrationTotal[SIM_YEAR_RANGE]; //EN Total number of emigrants
};
parameter_group PG_Emigration //EN Emigration
{
EmigrationSettings,
EmigrationRates, EmigrationTotal
};
////////////////////////////////////////////////////////////////////////////////////////////////////
// Actor states and Events
////////////////////////////////////////////////////////////////////////////////////////////////////
actor Person
{
AGE5_INDEX age5_index = split(integer_age, AGE5_PART); //EN Age Group
logical is_resident = { TRUE }; //EN Is resident
logical has_emigrated = { FALSE }; //EN Person has emigrated
double emigr_prob = { 0.0 }; //EN Emigration Probabilty
void DoEmigrate(); //EN Do emigrate
TIME emigr_wait = { TIME_INFINITE }; //EN Hypothetical waiting time
integer DoEmigrateChildren(); //EN Send children abroad
};
actor Clock
{
void DoEmigration(); //EN Do Emigration
hook DoEmigration, MidYearClockEvent, 5; //EN hook to mid year
double AdjustedProb(double dProb, double dFactor); //EN Adjust Probability
double ProbFromOdds(double dOdds); //EN Get Probability from Odds
double OddsFromProb(double dProb); //EN Get Odds from Probability
};
actor Globals //EN Actor Globals
{
//EN Emigration alignment factors
double emigration_alignment[SIM_YEAR_RANGE][SEX][AGE5_PART];
};
////////////////////////////////////////////////////////////////////////////////////////////////////
// Implementation
////////////////////////////////////////////////////////////////////////////////////////////////////
void Clock::DoEmigration()
{
if (clock_year >= MIN(SIM_YEAR_RANGE) && ModelEmigration == TRUE && ModelNetMigration == FALSE)
{
double dExpectedEmigrants = 0.0;
long nExpectedEmigrants = 0;
double dExpectedEmigrationProb = 0.0;
double dCurrentEmigrationProb = 0.0;
long nGroupSize = 0;
// for each age group by sex
// find, record and apply an alignment factor to meet target group emigration rates
for (int nSex = 0; nSex < SIZE(SEX); nSex++)
{
for (int nGroup = 0; nGroup < SIZE(AGE5_INDEX); nGroup++)
{
// The expected emigration pribability in this group
dExpectedEmigrationProb = EmigrationRates[nSex][nGroup];
// Update the individual emigration probabilities of all members of this group
dCurrentEmigrationProb = 0.0;
nGroupSize = asResidentSexAge5[nSex][nGroup]->Count();
for (long nIndex = 0; nIndex < nGroupSize; nIndex++)
{
auto prPerson = asResidentSexAge5[nSex][nGroup]->Item(nIndex);
double dPersonalEmiProb = 0.0;
// Calculate the current personal probability to emigrate
// /////////////////////////////////////////////////////////////////////////////////
// At this place, individual level odds can be introduced do adjust individual rates
// =================================================================================
/* dPersonalEmiProb = ProbFromOdds(OddsFromProb(EmigrationRates[prPerson->sex][prPerson->age5_index])
* EmigrationOdds[prPerson->sex][prPerson->nation_agg][prPerson->age5_index] ); */
// //////////////////////////////////////////////////////////////////////////////////
dPersonalEmiProb = EmigrationRates[prPerson->sex][prPerson->age5_index];
prPerson->emigr_prob = dPersonalEmiProb;
dCurrentEmigrationProb = dCurrentEmigrationProb + dPersonalEmiProb;
}
// This is the current overall probability in the group
if (nGroupSize > 0) dCurrentEmigrationProb = dCurrentEmigrationProb / nGroupSize;
// Find alignment factor for individual emigration probabilities
double dLower = 0.0;
double dUpper = 200.0;
double dCenter = 100.0;
int nMaxIter = 1000000;
bool bEnterAlignment = FALSE;
while (abs(dCurrentEmigrationProb - dExpectedEmigrationProb) > 0.00001 && nMaxIter > 0 && nGroupSize > 0)
{
bEnterAlignment = TRUE;
dCurrentEmigrationProb = 0.0;
dCenter = (dLower + dUpper) / 2.0;
for (long nIndex = 0; nIndex < nGroupSize; nIndex++)
{
auto prPerson = asResidentSexAge5[nSex][nGroup]->Item(nIndex);
dCurrentEmigrationProb = dCurrentEmigrationProb + AdjustedProb(prPerson->emigr_prob, dCenter);
}
dCurrentEmigrationProb = dCurrentEmigrationProb / nGroupSize;
nMaxIter--;
if (dCurrentEmigrationProb > dExpectedEmigrationProb) dUpper = dCenter;
else dLower = dCenter;
}
// Record it
if (bEnterAlignment) asGlobals->Item(0)->emigration_alignment[RANGE_POS(SIM_YEAR_RANGE, clock_year)][nSex][nGroup] = dCenter;
else asGlobals->Item(0)->emigration_alignment[RANGE_POS(SIM_YEAR_RANGE, clock_year)][nSex][nGroup] = 1.0;
// Apply it to update all individual emigration probabilities
if (bEnterAlignment)
{
for (long nIndex = 0; nIndex < nGroupSize; nIndex++)
{
auto prPerson = asResidentSexAge5[nSex][nGroup]->Item(nIndex);
prPerson->emigr_prob = AdjustedProb(prPerson->emigr_prob, dCenter);
}
}
// set hypothetical waiting times
for (long nIndex = 0; nIndex < nGroupSize; nIndex++)
{
auto prPerson = asResidentSexAge5[nSex][nGroup]->Item(nIndex);
double dProb = prPerson->emigr_prob;
if (dProb == 1) prPerson->emigr_wait = 0.0;
else if (dProb == 0) prPerson->emigr_wait = TIME_INFINITE;
else prPerson->emigr_wait = -log(RandUniform(20)) / -log(1 - dProb);
}
}
}
// Make people emigrate
// The model used can be chosen by user
// (1) Option 1: Individual Emigration meeting rates by age group and sex
if (EmigrationSettings == ES_AGERATES)
{
// for each age group by sex
for (int nSex = 0; nSex < SIZE(SEX); nSex++)
{
for (int nGroup = 0; nGroup < SIZE(AGE5_INDEX); nGroup++)
{
// integer number of emigrants in group
dExpectedEmigrants = EmigrationRates[nSex][nGroup] * (asResidentSexAge5[nSex][nGroup]->Count());
nExpectedEmigrants = int(dExpectedEmigrants);
if (RandUniform(21) < dExpectedEmigrants - (double)nExpectedEmigrants) nExpectedEmigrants++;
// Call emigration for persons with shortest waiting time
for (int nIndex = 0; nIndex < nExpectedEmigrants; nIndex++)
{
auto prPerson = asResidentOrderedSexAge5[nSex][nGroup]->Item(0);
prPerson->DoEmigrate();
}
}
}
}
// (2) Option 2: Individual emigration meeting aggregate yearly targets
else if (EmigrationSettings == ES_TOTALS)
{
// integer number of emigrants in group
dExpectedEmigrants = (double)EmigrationTotal[RANGE_POS(SIM_YEAR_RANGE, clock_year)] / asGlobals->Item(0)->person_weight;
nExpectedEmigrants = int(dExpectedEmigrants);
if (RandUniform(23) < dExpectedEmigrants - (double)nExpectedEmigrants) nExpectedEmigrants++;
// Call emigration for persons with shortest waiting time
for (int nIndex = 0; nIndex < nExpectedEmigrants; nIndex++)
{
auto prPerson = asResidentOrdered->Item(0);
prPerson->DoEmigrate();
}
}
// (3) Option 3: Family Emigration
else if (EmigrationSettings == ES_FAMILY)
{
// integer number of emigrants in group
dExpectedEmigrants = (double)EmigrationTotal[RANGE_POS(SIM_YEAR_RANGE, clock_year)] / asGlobals->Item(0)->person_weight;
nExpectedEmigrants = int(dExpectedEmigrants);
if (RandUniform(35) < dExpectedEmigrants - (double)nExpectedEmigrants) nExpectedEmigrants++;
// Call emigration for persons with shortest waiting time
int nIndex = 0;
while ( nIndex < nExpectedEmigrants )
{
auto prPerson = asResidentOrdered->Item(0);
// find family head
if (prPerson->family_role == FR_SPOUSE && prPerson->lSpouse) prPerson = prPerson->lSpouse;
else if (prPerson->family_role == FR_CHILD)
{
if (prPerson->lHHFather && prPerson->lHHFather->family_role == FR_HEAD) prPerson = prPerson->lHHFather;
else if (prPerson->lHHMother && prPerson->lHHMother->family_role == FR_HEAD) prPerson = prPerson->lHHMother;
}
// send children abroad
if (prPerson->family_role != FR_CHILD)
{
nIndex = nIndex + prPerson->DoEmigrateChildren();
}
// send spouse abroad
if (prPerson->lSpouse)
{
prPerson->lSpouse->DoEmigrate();
nIndex++;
}
// leave country
prPerson->DoEmigrate();
nIndex++;
}
}
}
}
integer Person::DoEmigrateChildren()
{
int ReturnValue = 0;
int nChildIndex;
auto prChild = (sex == FEMALE) ? mlHHMotherChildren->GetNext(0, &nChildIndex) : mlHHFatherChildren->GetNext(0, &nChildIndex);
while (prChild != NULL)
{
prChild->DoEmigrate();
ReturnValue++;
prChild = (sex == FEMALE) ? mlHHMotherChildren->GetNext(nChildIndex + 1, &nChildIndex) : mlHHFatherChildren->GetNext(nChildIndex + 1, &nChildIndex);
}
return ReturnValue;
}
double Clock::AdjustedProb(double dProb, double dFactor)
{
return ProbFromOdds(OddsFromProb(dProb) * dFactor);
}
// Get Probability from Odds
double Clock::ProbFromOdds(double dOdds)
{
return dOdds / (1 + dOdds);
}
// Get Odds from Probability
double Clock::OddsFromProb(double dProb)
{
if (dProb < 1) return dProb / (1 - dProb);
else return TIME_INFINITE;
}
void Person::DoEmigrate()
{
if (has_partner && lSpouse->family_role == FR_SPOUSE) lSpouse->family_role = FR_HEAD;
has_emigrated = TRUE;
is_resident = FALSE;
Finish();
}
Immigration.mpp
This module implements immigration. It is parameterized by the total number of immigrants and the age-sex distribution of immigrants. Children younger than 18 try to identify a mother in the population of women destined to immigrate in the same year.
Various characteristic, including a set of education states, are sampled from residents of the same age and sex at the moment of immigration in the ImmigrationEvent(). In this model implementation, immigrants do not differ from residents concerning the distributions of most characteristics.
This module is optional and can be switched off by the user. Users can also choose to model net-migration only, which is implemented in a separate module.
////////////////////////////////////////////////////////////////////////////////////////////////////
// Actor sets
////////////////////////////////////////////////////////////////////////////////////////////////////
//EN Potential immigrant mothers
actor_set Person asPotentialImmigrantMothers[integer_age][year_of_immigration]
filter is_alive && sex==FEMALE && parity < 6 && person_type == PT_IMMIGRANT;
//EN All Residents
actor_set Person asAllResidents filter is_alive && is_resident; //EN Entire resident population
////////////////////////////////////////////////////////////////////////////////////////////////////
// Parameters
////////////////////////////////////////////////////////////////////////////////////////////////////
parameters
{
//EN Total number of immigrants
long ImmigrationTotal[SIM_YEAR_RANGE];
//EN Age-Sex distribution of immigrants
double ImmigrationAgeSexAll[SEX][AGE_RANGE];
//EN Switch immigration on/off
model_generated logical ModelImmigration;
//EN Age distribution of mothers at birth
cumrate [1] AgeOfImmigrantMother[FERTILE_AGE_RANGE];
};
parameter_group PG_Immigration //EN Immigration
{
ImmigrationTotal,
ImmigrationAgeSexAll,
AgeOfImmigrantMother
};
////////////////////////////////////////////////////////////////////////////////////////////////////
// Actor states and functions
////////////////////////////////////////////////////////////////////////////////////////////////////
actor Person
{
//EN Immigrant
logical is_immigrant = (person_type == PT_IMMIGRANT) ? TRUE : FALSE;
SIM_YEAR_RANGE year_of_immigration = { 2050 }; //EN Year of immigration
TIME time_of_immigration = {TIME_INFINITE}; //EN Time of immigration
event timeImmigrationEvent, ImmigrationEvent; //EN Immigration Event
logical FindImmigrantMother(); //EN Find a mother immigrating same year
};
////////////////////////////////////////////////////////////////////////////////////////////////////
// Implementation
////////////////////////////////////////////////////////////////////////////////////////////////////
TIME Person::timeImmigrationEvent()
{
if (!ever_resident) return time_of_immigration;
else return TIME_INFINITE;
}
logical Person::FindImmigrantMother()
{
bool bFoundMother = FALSE;
double dAgeImmi = time_of_immigration - time_of_birth;
if (dAgeImmi < 18.0)
{
int nAge;
int nCount = 0;
bool bFound = FALSE;
while (!bFound && nCount < 100)
{
Lookup_AgeOfImmigrantMother(RandUniform(39), &nAge);
nAge = nAge + MIN(FERTILE_AGE_RANGE);
if (asPotentialImmigrantMothers[nAge][RANGE_POS(SIM_YEAR_RANGE, year_of_immigration)]->Count() > 0) bFound = TRUE;
nCount++;
}
if (asPotentialImmigrantMothers[nAge][RANGE_POS(SIM_YEAR_RANGE, year_of_immigration)]->Count() > 0)
{
auto prMother = asPotentialImmigrantMothers[nAge][RANGE_POS(SIM_YEAR_RANGE, year_of_immigration)]->Item(RandUniform(40));
lBioMother = prMother;
lHHMother = prMother;
prMother->parity = prMother->parity + 1;
prMother->is_mother = TRUE;
bFoundMother = TRUE;
}
}
return bFoundMother;
}
void Person::ImmigrationEvent()
{
// Find a resident host of same sex and age and clone characteristics
if (asAllPersonsSexAge[sex][integer_age]->Count() > 0)
{
auto prHostPerson = asAllPersonsSexAge[sex][integer_age]->GetRandom(RandUniform(45));
// Clone education states from host
educ_level = prHostPerson->educ_level;
educ_status = prHostPerson->educ_status;
educ_pattern_status = prHostPerson->educ_pattern_status;
in_other_education = prHostPerson->in_other_education;
year_finish_school = prHostPerson->year_finish_school;
educ_pattern_number = prHostPerson->educ_pattern_number;
educ_fate = prHostPerson->educ_fate;
educ_prob1 = prHostPerson->educ_prob1;
educ_prob2 = prHostPerson->educ_prob2;
educ_parents = prHostPerson->educ_parents;
educ_parents_is_known = prHostPerson->educ_parents_is_known;
// Cloneo other characteristics from host
never_father = prHostPerson->never_father;
}
// Residence status
is_resident = TRUE;
ever_resident = TRUE;
}
NetMigration.mpp
This module gives the user an additional option of modeling international migration - net migration - based on a parameter of total net migration by age, sex, and period. This option is useful if only data on net migration are available, as is the case for Eurostat population projections. The module introduces a new selection switch for modelling international migration:
Do not model migration
Immigration only
Emigration only
Immigration & Emigration
Net Migration
If net migration is chosen, the immigration parameters are overwritten by values based on the new net migration parameter. For emigration, a new Clock function DoNetEmigration() was added which - when selected - replaces the DoEmigration() function.
////////////////////////////////////////////////////////////////////////////////////////////////////
// Types
////////////////////////////////////////////////////////////////////////////////////////////////////
classification MIGRATION_SETTINGS //EN Migration Settings
{
MSE_NON, //EN Do not model migration
MSE_IMMI, //EN Immigration only
MSE_EMI, //EN Emigration only
MSE_ALL, //EN Immigration & Emigration
MSE_NET //EN Net Migration
};
////////////////////////////////////////////////////////////////////////////////////////////////////
// Parameters
////////////////////////////////////////////////////////////////////////////////////////////////////
parameters
{
//EN Migration Settings
MIGRATION_SETTINGS MigrationSettings;
//EN Model net migration
model_generated logical ModelNetMigration;
//EN Net migration by age and sex
double NetMigrationSexPeriodAge[SEX][SIM_YEAR_RANGE][AGE_RANGE];
//EN Total number of immigrants
model_generated long MgImmigrationTotal[SIM_YEAR_RANGE];
//EN Age-Sex distribution of immigrants
model_generated cumrate [2] MgImmigrationAgeSexAll[SIM_YEAR_RANGE][SEX][AGE_RANGE];
};
parameter_group PG_NetMigration //EN Net Migration
{
NetMigrationSexPeriodAge
};
parameter_group PG_Migration //EN International Migration
{
MigrationSettings,
PG_Immigration,
PG_Emigration,
PG_NetMigration
};
////////////////////////////////////////////////////////////////////////////////////////////////////
// States & Functions
////////////////////////////////////////////////////////////////////////////////////////////////////
actor Clock
{
void DoNetEmigration(); //EN Net Emigration
hook DoNetEmigration, MidYearClockEvent, 6; //EN hook to mid year
};
////////////////////////////////////////////////////////////////////////////////////////////////////
// Implementation
////////////////////////////////////////////////////////////////////////////////////////////////////
void Clock::DoNetEmigration()
{
if (clock_year >= MIN(SIM_YEAR_RANGE) && ModelNetMigration == TRUE)
{
// for each age by sex
for (int nSex = 0; nSex < SIZE(SEX); nSex++)
{
for (int nAge = 0; nAge < SIZE(AGE_RANGE); nAge++)
{
double dExpectedEmigrants = 0.0;
long nExpectedEmigrants = 0;
if (NetMigrationSexPeriodAge[nSex][RANGE_POS(SIM_YEAR_RANGE, clock_year)][nAge] < 0) // there is net emigration
{
// determine number of emigrants
dExpectedEmigrants = -NetMigrationSexPeriodAge[nSex][RANGE_POS(SIM_YEAR_RANGE, clock_year)][nAge]
/ asGlobals->Item(0)->person_weight;
nExpectedEmigrants = int(dExpectedEmigrants);
if (RandUniform(46) < dExpectedEmigrants - (double)nExpectedEmigrants) nExpectedEmigrants++;
// Make somebody emigrate
for (int nIndex = 0; nIndex < nExpectedEmigrants; nIndex++)
{
if (asAllPersonsSexAge[nSex][nAge]->Count() > 0)
{
auto prPerson = asAllPersonsSexAge[nSex][nAge]->GetRandom(RandUniform(47));
prPerson->DoEmigrate();
}
}
}
}
}
}
}
////////////////////////////////////////////////////////////////////////////////////////////////////
// Pre-Simulation
////////////////////////////////////////////////////////////////////////////////////////////////////
void PreSimulation()
{
// Settings
if (MigrationSettings == MSE_NON) // Do not model migration
{
ModelNetMigration = FALSE;
ModelImmigration = FALSE;
ModelEmigration = FALSE;
}
else if (MigrationSettings == MSE_IMMI) //EN Immigration only
{
ModelNetMigration = FALSE;
ModelImmigration = TRUE;
ModelEmigration = FALSE;
}
else if (MigrationSettings == MSE_EMI) //EN Emigration only
{
ModelNetMigration = FALSE;
ModelImmigration = FALSE;
ModelEmigration = TRUE;
}
else if (MigrationSettings == MSE_ALL) //EN Immigration & Emigration
{
ModelNetMigration = FALSE;
ModelImmigration = TRUE;
ModelEmigration = TRUE;
}
else //EN Net Migration
{
ModelNetMigration = TRUE;
ModelImmigration = FALSE;
ModelEmigration = FALSE;
}
// Parameters
for (int nYear = 0; nYear < SIZE(SIM_YEAR_RANGE); nYear++)
{
MgImmigrationTotal[nYear] = 0.0;
}
if (ModelNetMigration)
{
for (int nYear = 0; nYear < SIZE(SIM_YEAR_RANGE); nYear++)
{
double dSumImmigrants = 0;
for (int nSex = 0; nSex < SIZE(SEX); nSex++)
{
for (int nAge = 0; nAge < SIZE(AGE_RANGE); nAge++)
{
if (NetMigrationSexPeriodAge[nSex][nYear][nAge] > 0)
{
MgImmigrationAgeSexAll[nYear][nSex][nAge] = NetMigrationSexPeriodAge[nSex][nYear][nAge];
dSumImmigrants = dSumImmigrants + NetMigrationSexPeriodAge[nSex][nYear][nAge];
}
else
{
MgImmigrationAgeSexAll[nYear][nSex][nAge] = 0.0;
}
}
}
MgImmigrationTotal[nYear] = dSumImmigrants;
}
}
else
{
for (int nYear = 0; nYear < SIZE(SIM_YEAR_RANGE); nYear++)
{
MgImmigrationTotal[nYear] = ImmigrationTotal[nYear];
}
for (int nYear = 0; nYear < SIZE(SIM_YEAR_RANGE); nYear++)
{
for (int nSex = 0; nSex < SIZE(SEX); nSex++)
{
for (int nAge = 0; nAge < SIZE(AGE_RANGE); nAge++)
{
MgImmigrationAgeSexAll[nYear][nSex][nAge] = ImmigrationAgeSexAll[nSex][nAge];
}
}
}
}
}
FemalePartnershipStatus.mpp
This module implements processes for maintaining the partnership status of women over the life course (union formation, dissolution, calls for matching a suitable partner). The female partnership status is updated at yearly according to observed partnership patterns by education, age, and age of the youngest child. The partnership status is modelled for all women within the age range 15-80; no more union formation events are modelled after 80, it is assumed the only union dissolution is due to widowhood.
Parameters:
Proportion of women living with dependent children who are in a partnership by education, age group, and age group of the youngest child.
Proportion of women not living with dependent children who are in a partnership by education and age.
The model maintains 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 could be refined e.g. by adding a union dissolution module at the micro-level if longitudinal consistency is essential for model applications. The current version is longitudinally consistent only on the cohort level and by lifetime childlessness versus motherhood.
////////////////////////////////////////////////////////////////////////////////////////////////////
// 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) &&
has_partner && lives_with_dependent_child && is_resident;
//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) &&
!has_partner && lives_with_dependent_child && is_resident;
//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 && is_resident;
//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 && is_resident;
//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) &&
has_partner && !lives_with_dependent_child && is_resident;
//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) &&
!has_partner && !lives_with_dependent_child && is_resident;
////////////////////////////////////////////////////////////////////////////////////////////////////
// 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;
//EN Current education level
EDUC_LEVEL3 educ3_level = aggregate(educ_level, EDUC_LEVEL3);
//EN Age of youngest child of women
double age_youngest_child = (sex == FEMALE && count(mlHHMotherChildren) > 0) ?
min_over(mlHHMotherChildren, int_age) : TIME_INFINITE;
//EN Age group of youngest child of women
int child_agegr_part = split(age_youngest_child, CHILD_AGEGR_PART);
//EN Person lives with a child
logical lives_with_dependent_child = ((sex == FEMALE && count(mlHHMotherChildren) > 0) ||
(sex == MALE && count(mlHHFatherChildren) > 0)) ? 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
};
////////////////////////////////////////////////////////////////////////////////////////////////////
// Implementation
////////////////////////////////////////////////////////////////////////////////////////////////////
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 < 100)
{
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->DissolveUnion();
}
}
}
}
}
//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 < 100)
{
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->DissolveUnion();
}
}
}
}
}
}
PartnerMatching.mpp
Partner matching is modeled by age, education and childlessness. We only model heterosexual couples. Concerning age differences, we follow a “photo approach”, meaning that we assume that the patterns in observed age differences of couples by age persist over time. One difficulty in assigning a partner lies in the changing distribution of age differences by the age at union formation. For example, a young man cannot have a much younger spouse (or vice versa), while the spread of observed age differences is widening with age. As the information on union duration is typically not available in most surveys, and admin data exist only for marriages, we follow an indirect approach oriented on the observed age patterns in existing partnerships. The algorithm is as follows:
Based on a parameter on the distribution of the age differences of couples for a given age of the searching female and the cohort size of women in partnerships in the simulation, we calculate the expected number of partners by age.
We then calculate the number of existing partners by age in the simulated population.
By comparing the expected with the observed numbers, we identify the age with the largest negative gap for which at least one available male spouse is available.
After having identified the pool of available partners, a second criterion is education. In contrast to age, we use the education patterns of currently young couples (age 25-45) for deciding on the spouse’s education. It is assumed that current patterns are persistent and maintainable over time.
////////////////////////////////////////////////////////////////////////////////////////////////////
// Actor Sets
////////////////////////////////////////////////////////////////////////////////////////////////////
//EN Males not in a partnership by age
actor_set Person asAvailableMale[integer_age]
filter is_alive && sex == MALE && !has_partner && in_projected_time
&& WITHIN(MALE_PART_AGE_RANGE, integer_age) && is_resident;
//EN Males not in a partnership by age and education
actor_set Person asAvailableMaleEduc[integer_age][educ_fate]
filter is_alive && sex == MALE && !has_partner && in_projected_time
&& WITHIN(MALE_PART_AGE_RANGE, integer_age) && is_resident;
//EN Males not in a partnership by age, education and cohort childlessness
actor_set Person asAvailableMaleEducChildless[integer_age][educ_fate][never_father]
filter is_alive && sex == MALE && !has_partner && in_projected_time
&& WITHIN(MALE_PART_AGE_RANGE, integer_age) && is_resident;
//EN Women in a partnership
actor_set Person asFamInUnion[integer_age][integer_age_partner]
filter is_alive && sex == FEMALE && in_projected_time && WITHIN(PART_AGE_RANGE, integer_age)
&& has_partner && is_resident;
//EN Single males not never_father
actor_set Person asSingleMaleEverFatherEduc[integer_age][educ_fate]
filter is_alive && sex == MALE && !has_partner && !never_father && in_projected_time && is_resident;
//EN Couple males not never_father
actor_set Person asCoupleMaleEverFatherEduc[integer_age][educ_fate]
filter is_alive && sex == MALE && has_partner && !never_father && in_projected_time && !is_blocked && is_resident;
////////////////////////////////////////////////////////////////////////////////////////////////////
// Dimensions
////////////////////////////////////////////////////////////////////////////////////////////////////
range MALE_PART_AGE_RANGE { 15, 105 }; //EN Age
////////////////////////////////////////////////////////////////////////////////////////////////////
// Parameters
////////////////////////////////////////////////////////////////////////////////////////////////////
parameters
{
//EN Distribution of partner ages by age of female partner
double PartnerAgeDistribution[PART_AGE_RANGE][MALE_PART_AGE_RANGE];
//EN Partner Education
cumrate PartnerEducation[EDUC_LEVEL3][EDUC_LEVEL3];
};
parameter_group PG_PartnerMatching //EN Partner Matching
{
PartnerAgeDistribution, PartnerEducation
};
////////////////////////////////////////////////////////////////////////////////////////////////////
// Actor states, events and functions
////////////////////////////////////////////////////////////////////////////////////////////////////
actor Person
{
AGE_RANGE integer_age_partner = (has_partner) ? lSpouse->integer_age : 0;
EDUC_LEVEL3 educ_partner = (has_partner) ? lSpouse->educ_fate : EL3_LOW;
logical FindPartner();
//EN Person currently in a union
logical has_partner = (lSpouse) ? TRUE : FALSE;
void CheckFatherChildlessStatus(); hook CheckFatherChildlessStatus, MakeBaby;
logical is_blocked = { FALSE };
logical ever_father_in_sim = { FALSE }; //MARS TEST
};
////////////////////////////////////////////////////////////////////////////////////////////////////
// Implementation
////////////////////////////////////////////////////////////////////////////////////////////////////
void Person::CheckFatherChildlessStatus()
{
if (has_partner && lSpouse->never_father)
{
if (asSingleMaleEverFatherEduc[lSpouse->integer_age][lSpouse->educ_fate]->Count() > 0)
{
auto prPerson = asSingleMaleEverFatherEduc[lSpouse->integer_age][lSpouse->educ_fate]->GetRandom(RandUniform(30));
prPerson->never_father = TRUE;
lSpouse->never_father = FALSE;
}
else if (asCoupleMaleEverFatherEduc[lSpouse->integer_age][lSpouse->educ_fate]->Count() > 1)
{
lSpouse->is_blocked = TRUE;
auto prPerson = asCoupleMaleEverFatherEduc[lSpouse->integer_age][lSpouse->educ_fate]->GetRandom(RandUniform(31));
prPerson->never_father = TRUE;
lSpouse->never_father = FALSE;
lSpouse->is_blocked = FALSE;
}
}
if (has_partner) lSpouse->ever_father_in_sim = TRUE; //MARS TEST
}
logical Person::FindPartner()
{
bool bFoundSpouse = FALSE;
double dExpectedPartners[SIZE(MALE_PART_AGE_RANGE)];
double dObservedPartners[SIZE(MALE_PART_AGE_RANGE)];
double dSumExpectedPartners = 0;
double dSumObservedPartners = 0;
double dGap = 0.0;
double dLargestGap = 0.0;
int nAgePartner;
int nEducPartner;
//(1) Partner Age
for (long nI = 0; nI < SIZE(MALE_PART_AGE_RANGE); nI++)
{
dExpectedPartners[nI] = PartnerAgeDistribution[RANGE_POS(PART_AGE_RANGE, integer_age)][nI];
dObservedPartners[nI] = asFamInUnion[integer_age][MIN(MALE_PART_AGE_RANGE) + nI]->Count();
dSumExpectedPartners = dSumExpectedPartners + dExpectedPartners[nI];
dSumObservedPartners = dSumObservedPartners + dObservedPartners[nI];
}
for (long nI = 0; nI < SIZE(MALE_PART_AGE_RANGE); nI++)
{
if (dSumObservedPartners == 0.0) dSumObservedPartners = 1.0;
dExpectedPartners[nI] = 1.001 * dExpectedPartners[nI] / dSumExpectedPartners;
dObservedPartners[nI] = dObservedPartners[nI] / dSumObservedPartners;
dGap = dExpectedPartners[nI] - dObservedPartners[nI];
if (dExpectedPartners[nI] > 0.0 && dGap > dLargestGap && asAvailableMale[MIN(MALE_PART_AGE_RANGE)+nI] -> Count() > 0 )
{
dLargestGap = dGap;
bFoundSpouse = TRUE;
nAgePartner = MIN(MALE_PART_AGE_RANGE) + nI;
}
}
//(2) Partner Education
if (bFoundSpouse)
{
bool bFoundSpouseByEduc = FALSE;
int nTrials = 25;
lGlobals->UpdateBlockedSingleMen();
while (!bFoundSpouseByEduc && nTrials > 0)
{
Lookup_PartnerEducation(RandUniform(27), (int)educ_fate, &nEducPartner);
if (asAvailableMaleEduc[nAgePartner][nEducPartner]->Count() > 0 && !lGlobals->blocked_single_men[nAgePartner][nEducPartner])
{
bFoundSpouseByEduc = TRUE;
}
nTrials--;
}
if (!bFoundSpouseByEduc)
{
nEducPartner = asAvailableMale[nAgePartner]->GetRandom(RandUniform(28))->educ_fate;
}
if (asAvailableMaleEduc[nAgePartner][nEducPartner]->Count() > 0)
{
lSpouse = asAvailableMaleEduc[nAgePartner][nEducPartner]->GetRandom(RandUniform(26));
}
}
IMPLEMENT_HOOK();
return bFoundSpouse;
}
FamilyLinks.mpp
This module handles and maintains family links. Family links are links between spouses, links between children and their biological parents, and links between children and their current cohabiting social parents. We model nuclear family households, consisting of a household head, and - if present - a spouse and dependent children.
Dependent children are:
Children below age 18 who are not in a union or parents themselves
Children below age 26 as long as they are continuously enrolled in education and not in a union or parents themselves - and who have not left home.
Maintenance of links at death is handled in the function MaintainLinksAtDeath() linked to the mortality event. A surviving spouse becomes the household head. If there is no spouse but children in the household, each child checks if she has a biological mother or father, or a grandmother or grandfather who is still alive, in which case she moves to a new guardian and updates all links. If not, and at age 18+, she becomes a household head.
Maintenance of links at union formations is handled in the function MaintainLinksAtUnionFormation() called at union formation. Partners become a household head and spouse and all children who lived with any of the two partners before in a household now update their family links as they now live with two social parents. (The link to biological parents is kept over life and can be different from the links to the social parents). If a union formation happens in the first year after giving birth and the baby has no biological father assigned, it is assumed that the male partner is the biological father of the baby.
Maintenance of links at union dissolution is handled in the function DissolveUnion() which is called when a union is dissolved. Both former partners now become household heads and children have to choose with whom to live. The choice is modelled by a set of simple rules and a probability to stay with the female guardian. If only one of the two guardians is a biological parent, children choose to stay with the biological parent. Otherwise, the choice is random depending on the parameter.
Leaving home is modelled as an event LeavingHomeEvent() which is called immediately after any union formation or increase of parity, at age 18 if not enrolled in school, at the moment of leaving school after age 18, or at age 26. Also, children in education age 18-25 can leave home at any point in time based on parameters of age-specific home-leaving rates of students. Once a person has left the household, she is not moving back. When leaving home, the family status changes from child to household head or spouse.
The creation of links at birth is handled in the function LinkFamilyAtBirth() hooked to the SetAliveEvent(). For children in the starting population, it is assumed that both parents - if present in the household - are biological parents.
////////////////////////////////////////////////////////////////////////////////////////////////////
// Family Links
////////////////////////////////////////////////////////////////////////////////////////////////////
link Person.lBioFather Person.mlBioFatherChildren[]; //EN Biological Father - Children
link Person.lBioMother Person.mlBioMotherChildren[]; //EN Biological Mother - Children
link Person.lHHFather Person.mlHHFatherChildren[]; //EN HH Father - Children
link Person.lHHMother Person.mlHHMotherChildren[]; //EN HH Mother - Children
link Person.lSpouse; //EN Spouses
////////////////////////////////////////////////////////////////////////////////////////////////////
// Dimensions
////////////////////////////////////////////////////////////////////////////////////////////////////
classification FAM_ROLE //EN Role in family
{
FR_HEAD, //EN Head
FR_SPOUSE, //EN Spouse of head
FR_CHILD //EN Child
};
range AGE_18_25 { 18, 25 }; //EN Age
////////////////////////////////////////////////////////////////////////////////////////////////////
// Parameters
////////////////////////////////////////////////////////////////////////////////////////////////////
parameters
{
double ProbStayWithMother; //EN Probability to stay with mother after union dissolution
double ProbLeaveHome[AGE_18_25]; //EN Probability to leave home (students)
};
parameter_group PG_FamilyLinks //EN Family Links
{
ProbStayWithMother, ProbLeaveHome
};
////////////////////////////////////////////////////////////////////////////////////////////////////
// Actor States and Functions
////////////////////////////////////////////////////////////////////////////////////////////////////
actor Person
{
FAM_ROLE family_role = { FR_HEAD }; //EN Family Role
void LinkFamilyAtBirth(); //EN Establish family links at birth
hook LinkFamilyAtBirth, SetAliveEvent, 1; //EN Called at set alive event
event timeLeavingHomeEvent, LeavingHomeEvent; //EN Leaving Home
void MaintainLinksAtUnionFormation(); //EN Family links at union formation
hook MaintainLinksAtUnionFormation, FindPartner; //EN Update at end of FindPartner()
void MaintainLinksAtDeath(); //EN Family links at death
hook MaintainLinksAtDeath, MortalityEvent; //EN Update at Death
void DissolveUnion(); //EN Union Dissolution
//EN Children in Household
short children_in_household = (sex == FEMALE) ?
sum_over(mlHHMotherChildren, is_alive) :
sum_over(mlHHFatherChildren, is_alive);
};
////////////////////////////////////////////////////////////////////////////////////////////////////
// Implementation
////////////////////////////////////////////////////////////////////////////////////////////////////
void Person::MaintainLinksAtDeath()
{
if (lSpouse) lSpouse->family_role = FR_HEAD;
else if (children_in_household > 0)
{
//try to find somebody for children
int nIndex;
auto prChild = (sex==FEMALE) ? mlHHMotherChildren->GetNext( 0, &nIndex ) : mlHHFatherChildren->GetNext( 0, &nIndex );
while ( prChild != NULL )
{
Person * prGuardian = NULL;
if (sex == FEMALE && prChild->lBioFather) // own biological father
{
prGuardian = prChild->lBioFather;
}
else if (sex == MALE && prChild->lBioMother) // own biological mother
{
prGuardian = prChild->lBioMother;
}
else if (lBioMother) // grandmother
{
prGuardian = lBioMother;
}
else if (lBioFather) // grandfather
{
prGuardian = lBioFather;
}
else if (prChild->integer_age >= 18) // nobody
{
prChild->family_role = FR_HEAD;
}
if (prGuardian && prGuardian->sex == MALE)
{
prChild->lHHFather = prGuardian;
if (prGuardian->lSpouse) prChild->lHHMother = prGuardian->lSpouse;
}
else if (prGuardian && prGuardian->sex == FEMALE)
{
prChild->lHHMother = prGuardian;
if (prGuardian->lSpouse) prChild->lHHFather = prGuardian->lSpouse;
}
prChild = (sex==FEMALE) ? mlHHMotherChildren->GetNext( nIndex+1, &nIndex ) : mlHHFatherChildren->GetNext( nIndex+1, &nIndex );
}
}
}
void Person::DissolveUnion()
{
if (lSpouse)
{
//For all children in household decide with whom to stay
if (children_in_household)
{
int nIndex;
bool bStayWithMother = (RandUniform(32) < ProbStayWithMother);
//Stay with mother
auto prChild = mlHHMotherChildren->GetNext( 0, &nIndex );
while ( prChild != NULL )
{
if (bStayWithMother && ((prChild->lBioMother && prChild->lBioMother == this)
|| !prChild->lBioFather || (prChild->lBioFather && prChild->lBioFather != lSpouse)))
{
prChild->lHHFather = NULL;
}
prChild = mlHHMotherChildren->GetNext(nIndex + 1, &nIndex);
}
//Stay with father
if (lSpouse->children_in_household)
{
auto prChild = lSpouse->mlHHFatherChildren->GetNext( 0, &nIndex );
while ( prChild != NULL )
{
prChild->lHHMother = NULL;
prChild = lSpouse->mlHHFatherChildren->GetNext(nIndex + 1, &nIndex);
}
}
}
lSpouse->family_role = FR_HEAD;
family_role = FR_HEAD;
lSpouse = NULL;
}
}
void Person::MaintainLinksAtUnionFormation()
{
// Partners children in HH now live also with this woman
if (lSpouse && lSpouse->children_in_household)
{
int nIndex;
auto prChild = lSpouse->mlHHFatherChildren->GetNext( 0, &nIndex );
while ( prChild != NULL )
{
prChild->lHHMother = this;
prChild = lSpouse->mlHHFatherChildren->GetNext(nIndex + 1, &nIndex);
}
}
if (lSpouse)
{
// Own children in HH also live with partner now
int nIndex;
auto prChild = mlHHMotherChildren->GetNext( 0, &nIndex );
while ( prChild != NULL )
{
prChild->lHHFather = lSpouse;
// if babies without biological father, the spouse becomes biological father
if (!prChild->lBioFather && prChild->integer_age == 0)
{
prChild->lBioFather = lSpouse;
}
prChild = mlHHMotherChildren->GetNext(nIndex + 1, &nIndex);
}
family_role = FR_HEAD;
lSpouse->family_role = FR_SPOUSE;
// own household
lHHFather = NULL;
lHHMother = NULL;
lSpouse->lHHFather = NULL;
lSpouse->lHHMother = NULL;
}
}
TIME Person::timeLeavingHomeEvent()
{
if (in_projected_time && family_role == FR_CHILD
&& (integer_age >= 26 || lSpouse || (integer_age >= 18 && !in_school) || parity > 0))
{
return WAIT(0);
}
else if (family_role == FR_CHILD && WITHIN(AGE_18_25, integer_age))
{
double dProb = ProbLeaveHome[RANGE_POS(AGE_18_25, integer_age)];
if (dProb >= 1.0) return WAIT(0);
else if (dProb > 0.0) return WAIT(-log(RandUniform(33)) / -log(1 - dProb));
else return TIME_INFINITE;
}
else return TIME_INFINITE;
}
void Person::LeavingHomeEvent()
{
// become a head or a spouse
if (lSpouse && lSpouse->family_role == FR_HEAD) family_role = FR_SPOUSE;
else family_role = FR_HEAD;
// remove links to household
lHHFather = NULL;
lHHMother = NULL;
}
void Person::LinkFamilyAtBirth()
{
if (person_type == PT_START && family_role != FR_HEAD && peHHead != NULL)
{
if (family_role == FR_CHILD)
{
if (peHHead->sex == MALE)
{
lBioFather = peHHead;
if (lBioFather->lSpouse) lBioMother = lBioFather->lSpouse;
}
else
{
lBioMother = peHHead;
if (lBioMother->lSpouse) lBioFather = lBioMother->lSpouse;
}
lHHFather = lBioFather;
lHHMother = lBioMother;
}
else
{
lSpouse = peHHead;
if (sex == lSpouse->sex) // FIX: do not allow same sex couples
{
if (sex == FEMALE) sex = MALE; else sex = FEMALE;
}
}
}
else if (person_type == PT_CHILD)
{
lBioMother = peHHead;
if (lBioMother->lSpouse) lBioFather = lBioMother->lSpouse;
lHHFather = lBioFather;
lHHMother = lBioMother;
}
}
BirthDays.mpp
This module implements birthday events of the person actors. This is a very simple module kept separately for showcasing the implementation of a clock event, as documented in the step-by-step implementation guide.
////////////////////////////////////////////////////////////////////////////////////////////////////
// Types
////////////////////////////////////////////////////////////////////////////////////////////////////
range AGE_RANGE { 0, 105 }; //EN Age Range
////////////////////////////////////////////////////////////////////////////////////////////////////
// Actor states and functions
////////////////////////////////////////////////////////////////////////////////////////////////////
actor Person
{
AGE_RANGE integer_age = { 0 }; //EN Age
TIME time_next_birthday = { TIME_INFINITE }; //EN Time of next birthday
event timeBirthdayEvent, BirthdayEvent; //EN Birthday Event
};
////////////////////////////////////////////////////////////////////////////////////////////////////
// Event Implementation
////////////////////////////////////////////////////////////////////////////////////////////////////
/* NOTE(Person.BirthdayEvent, EN)
At each birthday integer age is incremented and all code to be performed at birthdays is executed
*/
TIME Person::timeBirthdayEvent()
{
if ( integer_age == 0 ) return time_of_birth + 1.0;
else return time_next_birthday;
}
void Person::BirthdayEvent()
{
// Increment integer age
if (integer_age < MAX(AGE_RANGE)) integer_age++;
// Code to be performed at each birthday can be entered here
// Set clock for next birthday
time_next_birthday = WAIT(1);
}
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 the model updates accounts and performs other end-of-year routines. Immediately after the year-end, the YearStart() routine is called. This is when the calendar_year state is changed. Other modules use the events of this module as targets for hooks.
////////////////////////////////////////////////////////////////////////////////////////////////////
// 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.YearEnd, 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;
}
Accounting Modules
NtaBase.mpp
This module implements the base NTA parameters and variables by sex, education and family type. For children and students, education refers to parent’s education, for all others the own educational attainment. By age and school enrolment, we distinguish five person-types:
Children 0-16
Students 17-25
Non-students 17-25
Adults 26-59
Adults 60+
For the non-student population 17-25 and adults 26-59, we distinguish four family-types:
Single not living with children
Single living with child(ren)
Couple not living with children
Couple living with child(ren)
For the population 60+ we distinguish four family types:
Single childless
Single ever parent
Couple childless
Couple ever parent
The module maintains the individual base NTA variables depending on the current individual characteristics from the corresponding parameters. The list of variables is:
Private Consumption Education (CFE)
Private Consumption Health (CFH)
Private Consumption other than Education and Health (CFX)
Public Consumption Education (CGE)
Public Consumption Health (CGH)
Public Consumption other than Education and Health (CGX)
Public Transfers Pensions, Inflows (TGSOAI)
Public Transfers Other Cash Inflows (TGXCI)
Public Transfers Other In-Kind Inflows (TGXII)
Public Transfers Education Inflows (TGEI)
Public Transfers Health Inflows (TGHI)
Public Transfers Outflows (TGO)
Net Interhousehold Transfers (TFB)
Net Intrahousehold Transfers (TFW)
Private Saving (SF)
Public Saving (SG)
Labor Income (LY)
Private Asset Income (YAF)
Public Asset Income (YAG)
The module also contains parameters for published NTA data (by age only, and by age and sex) allowing for comparing simulation results. Users can choose which NTA data to use in the simulation.
////////////////////////////////////////////////////////////////////////////////////////////////////
// Dimensions
////////////////////////////////////////////////////////////////////////////////////////////////////
classification NTA_POP_GROUP //EN NTA Population Group
{
NPG_CHILD, //EN Child 0-16
NPG_YOUNG_STUDENT, //EN Student 17-25
NPG_YOUNG_WORK, //EN Non-student 17-25
NPG_ADULT_WORK, //EN Adult 26-59
NPG_OLD //EN Adult 60+
};
classification NTA_FAMILY_ACTIVE //EN Family type of adult non-students
{
NFA_SINGLE_NOKI, //EN Single no (co-resident) children
NFA_SINGLE_KI, //EN Single with (co-resident) children
NFA_COUPLE_NOKI, //EN Couple no (co-resident) children
NFA_COUPLE_KI //EN Couple with (co-resident) childen
};
classification NTA_FAMILY_OLD //EN Family type of adults 60+
{
NFR_SINGLE_NOKI, //EN Single childless
NFR_SINGLE_KI, //EN Single ever parent
NFR_COUPLE_NOKI, //EN Couple childless
NFR_COUPLE_KI //EN Couple ever parent
};
range NTA_AGE_CHILD { 0, 16 }; //EN NTA child age range
range NTA_AGE_YOUNG { 17, 25 }; //EN NTA young age range
range NTA_AGE_WORK { 26, 59 }; //EN NTA prime work age range
range NTA_AGE_OLD { 60, 105 }; //EN NTA old age range
classification NTA_VARIABLES //EN NTA Variables
{
CFE, //EN Private Consumption Education (CFE)
CFH, //EN Private Consumption Health (CFH)
CFX, //EN Private Consumption other than Education and Health (CFX)
CGE, //EN Public Consumption Education (CGE)
CGH, //EN Public Consumption Health (CGH)
CGX, //EN Public Consumption other than Education and Health (CGX)
TGSOAI, //EN Public Transfers Pensions, Inflows (TGSOAI)
TGXCI, //EN Public Transfers Other Cash Inflows (TGXCI)
TGXII, //EN Public Transfers Other In-Kind Inflows (TGXII)
TGEI, //EN Public Transfers Education Inflows (TGEI)
TGHI, //EN Public Transfers Health Inflows (TGHI)
TGO, //EN Public Transfers Outflows (TGO)
TFB, //EN Net Interhousehold Transfers (TFB)
TFW, //EN Net Intrahousehold Transfers (TFW)
SF, //EN Private Saving (SF)
SG, //EN Public Saving (SG)
YL, //EN Labor Income (YL)
YAF, //EN Private Asset Income (YAF)
YAG //EN Public Asset Income (YAG)
};
classification NTA_OWN_EDUC //EN Own education
{
NOE_LOW, //EN Low
NOE_MEDIUM, //EN Medium
NOE_HIGH //EN High
};
classification NTA_PAR_EDUC_CHILD //EN Parents education
{
NPEC_LOW, //EN Low
NPEC_MEDIUM, //EN Medium
NPEC_HIGH //EN High
};
classification NTA_PAR_EDUC_STUDENT //EN Parents education
{
NPES_LOW, //EN Low
NPES_MEDIUM, //EN Medium
NPES_HIGH, //EN High
NPES_NN //EN Unknown (not living with parents)
};
classification NTA_EDUC
{
NE_LOW, //EN Low
NE_MEDIUM, //EN Medium
NE_HIGH, //EN High
NE_NN //EN Unknown (not living with parents)
};
classification NTA_EDUC3
{
NE3_LOW, //EN Low
NE3_MEDIUM, //EN Medium
NE3_HIGH //EN High
};
classification NTA_SCENARIO //EN NTA scenario
{
NS_AGE, //EN NTA by age
NS_SEX, //EN NTA by age and sex
NS_FAM //EN NTA by age, sex, education and family type
};
////////////////////////////////////////////////////////////////////////////////////////////////////
// Parameters
////////////////////////////////////////////////////////////////////////////////////////////////////
parameters
{
//EN NTA Scenario Selection
NTA_SCENARIO NtaScenario;
//EN NTA Children 0-16
double NtaChild[NTA_PAR_EDUC_CHILD][NTA_AGE_CHILD][NTA_VARIABLES];
//EN NTA Students 17-25
double NtaYoungStudent[NTA_PAR_EDUC_STUDENT][NTA_AGE_YOUNG][NTA_VARIABLES];
//EN NTA Non-Students 17-25
double NtaYoungWork[SEX][NTA_OWN_EDUC][NTA_FAMILY_ACTIVE][NTA_AGE_YOUNG][NTA_VARIABLES];
//EN NTA Adults 26-59
double NtaAdultWorkAge[SEX][NTA_OWN_EDUC][NTA_FAMILY_ACTIVE][NTA_AGE_WORK][NTA_VARIABLES];
//EN NTA Adults 60+
double NtaAdultOldAge[SEX][NTA_OWN_EDUC][NTA_FAMILY_OLD][NTA_AGE_OLD][NTA_VARIABLES];
//EN NTA by age only
double NtaAge[AGE_RANGE][NTA_VARIABLES];
//EN NTA by age and sex only
double NtaSex[SEX][AGE_RANGE][NTA_VARIABLES];
};
parameter_group PG_NTA_Published //EN Published NTA data
{
NtaAge, NtaSex
};
parameter_group PG_NTA_Refined //EN NTA by family and education
{
NtaChild, NtaYoungStudent, NtaYoungWork,
NtaAdultWorkAge, NtaAdultOldAge
};
parameter_group PG_NTA //EN NTA Variables
{
NtaScenario,
PG_NTA_Published,
PG_NTA_Refined
};
///////////////////////////////////////////////////////////////////////////////////////////////////
// Actor States
////////////////////////////////////////////////////////////////////////////////////////////////////
actor Person
{
//EN NTA Education
NTA_EDUC nta_educ = (nta_pop_group == NPG_CHILD || (nta_pop_group == NPG_YOUNG_STUDENT && family_role == FR_CHILD)) ?
(
(educ_parents == EL3_LOW) ? NE_LOW :
(educ_parents == EL3_MEDIUM) ? NE_MEDIUM : NE_HIGH
) :
(nta_pop_group == NPG_YOUNG_STUDENT && family_role != FR_CHILD) ? NE_NN :
(
(educ_fate == EL3_LOW) ? NE_LOW :
(educ_fate == EL3_MEDIUM) ? NE_MEDIUM : NE_HIGH
);
//EN NTA Education 3 Levels
NTA_EDUC3 nta_educ3 = (nta_educ == NE_MEDIUM) ? NE3_MEDIUM :
(nta_educ == NE_HIGH) ? NE3_HIGH : NE3_LOW;
//EN NTA Population Group
NTA_POP_GROUP nta_pop_group =
(WITHIN(NTA_AGE_CHILD,integer_age)) ? NPG_CHILD :
(WITHIN(NTA_AGE_YOUNG,integer_age) && is_student) ? NPG_YOUNG_STUDENT :
(WITHIN(NTA_AGE_YOUNG,integer_age)) ? NPG_YOUNG_WORK :
(WITHIN(NTA_AGE_WORK,integer_age)) ? NPG_ADULT_WORK : NPG_OLD;
//EN Student
logical is_student = (educ_status == ES_FULLTIME || educ_status == ES_PARTTIME
|| educ_status == ES_DUAL || in_other_education) ? TRUE : FALSE;
//EN Age Index
integer nta_age_index = (WITHIN(NTA_AGE_CHILD, integer_age)) ? integer_age :
(WITHIN(NTA_AGE_YOUNG, integer_age)) ? integer_age - MIN(NTA_AGE_YOUNG) :
(WITHIN(NTA_AGE_WORK, integer_age)) ? integer_age - MIN(NTA_AGE_WORK)
: integer_age - MIN(NTA_AGE_OLD);
//EN NTA Family Type Index Workers
integer nta_fam_index = (nta_pop_group == NPG_YOUNG_WORK || nta_pop_group == NPG_ADULT_WORK) ?
(
(!has_partner) ?
(
(lives_with_dependent_child) ? NFA_SINGLE_KI : NFA_SINGLE_NOKI
)
:
(lives_with_dependent_child) ? NFA_COUPLE_KI : NFA_COUPLE_NOKI
) : 0;
//EN Family Type active age
NTA_FAMILY_ACTIVE nta_family_active = (!has_partner) ?
(
(lives_with_dependent_child) ? NFA_SINGLE_KI : NFA_SINGLE_NOKI
) : (lives_with_dependent_child) ? NFA_COUPLE_KI : NFA_COUPLE_NOKI;
//EN Family Type old
NTA_FAMILY_OLD nta_family_old = (!has_partner) ?
(((sex == MALE && never_father) || (sex == FEMALE && !is_mother)) ? NFR_SINGLE_NOKI : NFR_SINGLE_KI) :
(((sex == MALE && never_father) || (sex == FEMALE && !is_mother)) ? NFR_COUPLE_NOKI : NFR_COUPLE_KI);
//EN Family index old
integer nta_old_index = (nta_pop_group == NPG_OLD) ?
(
(!has_partner) ?
(
((sex == MALE && never_father) || (sex == FEMALE && !is_mother)) ? NFR_SINGLE_NOKI : NFR_SINGLE_KI
)
:
(
((sex == MALE && never_father) || (sex == FEMALE && !is_mother)) ? NFR_COUPLE_NOKI : NFR_COUPLE_KI
)
)
: 0;
//EN Private Consumption Education (CFE)
double base_CFE =
(NtaScenario == NS_AGE) ? NtaAge[integer_age][CFE] :
(NtaScenario == NS_SEX) ? NtaSex[sex][integer_age][CFE] :
(nta_pop_group == NPG_CHILD) ? NtaChild[nta_educ3][nta_age_index][CFE] :
(nta_pop_group == NPG_YOUNG_STUDENT) ? NtaYoungStudent[nta_educ][nta_age_index][CFE] :
(nta_pop_group == NPG_YOUNG_WORK) ? NtaYoungWork[sex][nta_educ3][nta_fam_index][nta_age_index][CFE] :
(nta_pop_group == NPG_ADULT_WORK) ? NtaAdultWorkAge[sex][nta_educ3][nta_fam_index][nta_age_index][CFE] :
NtaAdultOldAge[sex][nta_educ3][nta_old_index][nta_age_index][CFE];
//EN Private Consumption Health (CFH)
double base_CFH =
(NtaScenario == NS_AGE) ? NtaAge[integer_age][CFH] :
(NtaScenario == NS_SEX) ? NtaSex[sex][integer_age][CFH] :
(nta_pop_group == NPG_CHILD) ? NtaChild[nta_educ3][nta_age_index][CFH] :
(nta_pop_group == NPG_YOUNG_STUDENT) ? NtaYoungStudent[nta_educ][nta_age_index][CFH] :
(nta_pop_group == NPG_YOUNG_WORK) ? NtaYoungWork[sex][nta_educ3][nta_fam_index][nta_age_index][CFH] :
(nta_pop_group == NPG_ADULT_WORK) ? NtaAdultWorkAge[sex][nta_educ3][nta_fam_index][nta_age_index][CFH] :
NtaAdultOldAge[sex][nta_educ3][nta_old_index][nta_age_index][CFH];
//EN Private Consumption other than Education and Health (CFX)
double base_CFX =
(NtaScenario == NS_AGE) ? NtaAge[integer_age][CFX] :
(NtaScenario == NS_SEX) ? NtaSex[sex][integer_age][CFX] :
(nta_pop_group == NPG_CHILD) ? NtaChild[nta_educ3][nta_age_index][CFX] :
(nta_pop_group == NPG_YOUNG_STUDENT) ? NtaYoungStudent[nta_educ][nta_age_index][CFX] :
(nta_pop_group == NPG_YOUNG_WORK) ? NtaYoungWork[sex][nta_educ3][nta_fam_index][nta_age_index][CFX] :
(nta_pop_group == NPG_ADULT_WORK) ? NtaAdultWorkAge[sex][nta_educ3][nta_fam_index][nta_age_index][CFX] :
NtaAdultOldAge[sex][nta_educ3][nta_old_index][nta_age_index][CFX];
//EN Public Consumption Education (CGE)
double base_CGE =
(NtaScenario == NS_AGE) ? NtaAge[integer_age][CGE] :
(NtaScenario == NS_SEX) ? NtaSex[sex][integer_age][CGE] :
(nta_pop_group == NPG_CHILD) ? NtaChild[nta_educ3][nta_age_index][CGE] :
(nta_pop_group == NPG_YOUNG_STUDENT) ? NtaYoungStudent[nta_educ][nta_age_index][CGE] :
(nta_pop_group == NPG_YOUNG_WORK) ? NtaYoungWork[sex][nta_educ3][nta_fam_index][nta_age_index][CGE] :
(nta_pop_group == NPG_ADULT_WORK) ? NtaAdultWorkAge[sex][nta_educ3][nta_fam_index][nta_age_index][CGE] :
NtaAdultOldAge[sex][nta_educ3][nta_old_index][nta_age_index][CGE];
//EN Public Consumption Health (CGH)
double base_CGH =
(NtaScenario == NS_AGE) ? NtaAge[integer_age][CGH] :
(NtaScenario == NS_SEX) ? NtaSex[sex][integer_age][CGH] :
(nta_pop_group == NPG_CHILD) ? NtaChild[nta_educ3][nta_age_index][CGH] :
(nta_pop_group == NPG_YOUNG_STUDENT) ? NtaYoungStudent[nta_educ][nta_age_index][CGH] :
(nta_pop_group == NPG_YOUNG_WORK) ? NtaYoungWork[sex][nta_educ3][nta_fam_index][nta_age_index][CGH] :
(nta_pop_group == NPG_ADULT_WORK) ? NtaAdultWorkAge[sex][nta_educ3][nta_fam_index][nta_age_index][CGH] :
NtaAdultOldAge[sex][nta_educ3][nta_old_index][nta_age_index][CGH];
//EN Public Consumption other than Education and Health (CGX)
double base_CGX =
(NtaScenario == NS_AGE) ? NtaAge[integer_age][CGX] :
(NtaScenario == NS_SEX) ? NtaSex[sex][integer_age][CGX] :
(nta_pop_group == NPG_CHILD) ? NtaChild[nta_educ3][nta_age_index][CGX] :
(nta_pop_group == NPG_YOUNG_STUDENT) ? NtaYoungStudent[nta_educ][nta_age_index][CGX] :
(nta_pop_group == NPG_YOUNG_WORK) ? NtaYoungWork[sex][nta_educ3][nta_fam_index][nta_age_index][CGX] :
(nta_pop_group == NPG_ADULT_WORK) ? NtaAdultWorkAge[sex][nta_educ3][nta_fam_index][nta_age_index][CGX] :
NtaAdultOldAge[sex][nta_educ3][nta_old_index][nta_age_index][CGX];
//EN Public Transfers Pensions, Inflows (TGSOAI)
double base_TGSOAI =
(NtaScenario == NS_AGE) ? NtaAge[integer_age][TGSOAI] :
(NtaScenario == NS_SEX) ? NtaSex[sex][integer_age][TGSOAI] :
(nta_pop_group == NPG_CHILD) ? NtaChild[nta_educ3][nta_age_index][TGSOAI] :
(nta_pop_group == NPG_YOUNG_STUDENT) ? NtaYoungStudent[nta_educ][nta_age_index][TGSOAI] :
(nta_pop_group == NPG_YOUNG_WORK) ? NtaYoungWork[sex][nta_educ3][nta_fam_index][nta_age_index][TGSOAI] :
(nta_pop_group == NPG_ADULT_WORK) ? NtaAdultWorkAge[sex][nta_educ3][nta_fam_index][nta_age_index][TGSOAI] :
NtaAdultOldAge[sex][nta_educ3][nta_old_index][nta_age_index][TGSOAI];
//EN Public Transfers Other Cash Inflows (TGXCI)
double base_TGXCI =
(NtaScenario == NS_AGE) ? NtaAge[integer_age][TGXCI] :
(NtaScenario == NS_SEX) ? NtaSex[sex][integer_age][TGXCI] :
(nta_pop_group == NPG_CHILD) ? NtaChild[nta_educ3][nta_age_index][TGXCI] :
(nta_pop_group == NPG_YOUNG_STUDENT) ? NtaYoungStudent[nta_educ][nta_age_index][TGXCI] :
(nta_pop_group == NPG_YOUNG_WORK) ? NtaYoungWork[sex][nta_educ3][nta_fam_index][nta_age_index][TGXCI] :
(nta_pop_group == NPG_ADULT_WORK) ? NtaAdultWorkAge[sex][nta_educ3][nta_fam_index][nta_age_index][TGXCI] :
NtaAdultOldAge[sex][nta_educ3][nta_old_index][nta_age_index][TGXCI];
//EN Public Transfers Other In-Kind Inflows (TGXII)
double base_TGXII =
(NtaScenario == NS_AGE) ? NtaAge[integer_age][TGXII] :
(NtaScenario == NS_SEX) ? NtaSex[sex][integer_age][TGXII] :
(nta_pop_group == NPG_CHILD) ? NtaChild[nta_educ3][nta_age_index][TGXII] :
(nta_pop_group == NPG_YOUNG_STUDENT) ? NtaYoungStudent[nta_educ][nta_age_index][TGXII] :
(nta_pop_group == NPG_YOUNG_WORK) ? NtaYoungWork[sex][nta_educ3][nta_fam_index][nta_age_index][TGXII] :
(nta_pop_group == NPG_ADULT_WORK) ? NtaAdultWorkAge[sex][nta_educ3][nta_fam_index][nta_age_index][TGXII] :
NtaAdultOldAge[sex][nta_educ3][nta_old_index][nta_age_index][TGXII];
//EN Public Transfers Health Outflows (TGEI)
double base_TGEI =
(NtaScenario == NS_AGE) ? NtaAge[integer_age][TGEI] :
(NtaScenario == NS_SEX) ? NtaSex[sex][integer_age][TGEI] :
(nta_pop_group == NPG_CHILD) ? NtaChild[nta_educ3][nta_age_index][TGEI] :
(nta_pop_group == NPG_YOUNG_STUDENT) ? NtaYoungStudent[nta_educ][nta_age_index][TGEI] :
(nta_pop_group == NPG_YOUNG_WORK) ? NtaYoungWork[sex][nta_educ3][nta_fam_index][nta_age_index][TGEI] :
(nta_pop_group == NPG_ADULT_WORK) ? NtaAdultWorkAge[sex][nta_educ3][nta_fam_index][nta_age_index][TGEI] :
NtaAdultOldAge[sex][nta_educ3][nta_old_index][nta_age_index][TGEI];
//EN Public Transfers Health Outflows (TGHI)
double base_TGHI =
(NtaScenario == NS_AGE) ? NtaAge[integer_age][TGHI] :
(NtaScenario == NS_SEX) ? NtaSex[sex][integer_age][TGHI] :
(nta_pop_group == NPG_CHILD) ? NtaChild[nta_educ3][nta_age_index][TGHI] :
(nta_pop_group == NPG_YOUNG_STUDENT) ? NtaYoungStudent[nta_educ][nta_age_index][TGHI] :
(nta_pop_group == NPG_YOUNG_WORK) ? NtaYoungWork[sex][nta_educ3][nta_fam_index][nta_age_index][TGHI] :
(nta_pop_group == NPG_ADULT_WORK) ? NtaAdultWorkAge[sex][nta_educ3][nta_fam_index][nta_age_index][TGHI] :
NtaAdultOldAge[sex][nta_educ3][nta_old_index][nta_age_index][TGHI];
//EN Public Transfers Education Outflows (TGO)
double base_TGO =
(NtaScenario == NS_AGE) ? NtaAge[integer_age][TGO] :
(NtaScenario == NS_SEX) ? NtaSex[sex][integer_age][TGO] :
(nta_pop_group == NPG_CHILD) ? NtaChild[nta_educ3][nta_age_index][TGO] :
(nta_pop_group == NPG_YOUNG_STUDENT) ? NtaYoungStudent[nta_educ][nta_age_index][TGO] :
(nta_pop_group == NPG_YOUNG_WORK) ? NtaYoungWork[sex][nta_educ3][nta_fam_index][nta_age_index][TGO] :
(nta_pop_group == NPG_ADULT_WORK) ? NtaAdultWorkAge[sex][nta_educ3][nta_fam_index][nta_age_index][TGO] :
NtaAdultOldAge[sex][nta_educ3][nta_old_index][nta_age_index][TGO];
//EN Net Interhousehold Transfers (TFB)
double base_TFB =
(NtaScenario == NS_AGE) ? NtaAge[integer_age][TFB] :
(NtaScenario == NS_SEX) ? NtaSex[sex][integer_age][TFB] :
(nta_pop_group == NPG_CHILD) ? NtaChild[nta_educ3][nta_age_index][TFB] :
(nta_pop_group == NPG_YOUNG_STUDENT) ? NtaYoungStudent[nta_educ][nta_age_index][TFB] :
(nta_pop_group == NPG_YOUNG_WORK) ? NtaYoungWork[sex][nta_educ3][nta_fam_index][nta_age_index][TFB] :
(nta_pop_group == NPG_ADULT_WORK) ? NtaAdultWorkAge[sex][nta_educ3][nta_fam_index][nta_age_index][TFB] :
NtaAdultOldAge[sex][nta_educ3][nta_old_index][nta_age_index][TFB];
//EN Net Intrahousehold Transfers (TFW)
double base_TFW =
(NtaScenario == NS_AGE) ? NtaAge[integer_age][TFW] :
(NtaScenario == NS_SEX) ? NtaSex[sex][integer_age][TFW] :
(nta_pop_group == NPG_CHILD) ? NtaChild[nta_educ3][nta_age_index][TFW] :
(nta_pop_group == NPG_YOUNG_STUDENT) ? NtaYoungStudent[nta_educ][nta_age_index][TFW] :
(nta_pop_group == NPG_YOUNG_WORK) ? NtaYoungWork[sex][nta_educ3][nta_fam_index][nta_age_index][TFW] :
(nta_pop_group == NPG_ADULT_WORK) ? NtaAdultWorkAge[sex][nta_educ3][nta_fam_index][nta_age_index][TFW] :
NtaAdultOldAge[sex][nta_educ3][nta_old_index][nta_age_index][TFW];
//EN Private Saving (SF)
double base_SF =
(NtaScenario == NS_AGE) ? NtaAge[integer_age][SF] :
(NtaScenario == NS_SEX) ? NtaSex[sex][integer_age][SF] :
(nta_pop_group == NPG_CHILD) ? NtaChild[nta_educ3][nta_age_index][SF] :
(nta_pop_group == NPG_YOUNG_STUDENT) ? NtaYoungStudent[nta_educ][nta_age_index][SF] :
(nta_pop_group == NPG_YOUNG_WORK) ? NtaYoungWork[sex][nta_educ3][nta_fam_index][nta_age_index][SF] :
(nta_pop_group == NPG_ADULT_WORK) ? NtaAdultWorkAge[sex][nta_educ3][nta_fam_index][nta_age_index][SF] :
NtaAdultOldAge[sex][nta_educ3][nta_old_index][nta_age_index][SF];
//EN Public Saving (SG)
double base_SG =
(NtaScenario == NS_AGE) ? NtaAge[integer_age][SG] :
(NtaScenario == NS_SEX) ? NtaSex[sex][integer_age][SG] :
(nta_pop_group == NPG_CHILD) ? NtaChild[nta_educ3][nta_age_index][SG] :
(nta_pop_group == NPG_YOUNG_STUDENT) ? NtaYoungStudent[nta_educ][nta_age_index][SG] :
(nta_pop_group == NPG_YOUNG_WORK) ? NtaYoungWork[sex][nta_educ3][nta_fam_index][nta_age_index][SG] :
(nta_pop_group == NPG_ADULT_WORK) ? NtaAdultWorkAge[sex][nta_educ3][nta_fam_index][nta_age_index][SG] :
NtaAdultOldAge[sex][nta_educ3][nta_old_index][nta_age_index][SG];
//EN Labor Inclome (LY)
double base_YL =
(NtaScenario == NS_AGE) ? NtaAge[integer_age][YL] :
(NtaScenario == NS_SEX) ? NtaSex[sex][integer_age][YL] :
(nta_pop_group == NPG_CHILD) ? NtaChild[nta_educ3][nta_age_index][YL] :
(nta_pop_group == NPG_YOUNG_STUDENT) ? NtaYoungStudent[nta_educ][nta_age_index][YL] :
(nta_pop_group == NPG_YOUNG_WORK) ? NtaYoungWork[sex][nta_educ3][nta_fam_index][nta_age_index][YL] :
(nta_pop_group == NPG_ADULT_WORK) ? NtaAdultWorkAge[sex][nta_educ3][nta_fam_index][nta_age_index][YL] :
NtaAdultOldAge[sex][nta_educ3][nta_old_index][nta_age_index][YL];
//EN Private Asset Income (YAF)
double base_YAF =
(NtaScenario == NS_AGE) ? NtaAge[integer_age][YAF] :
(NtaScenario == NS_SEX) ? NtaSex[sex][integer_age][YAF] :
(nta_pop_group == NPG_CHILD) ? NtaChild[nta_educ3][nta_age_index][YAF] :
(nta_pop_group == NPG_YOUNG_STUDENT) ? NtaYoungStudent[nta_educ][nta_age_index][YAF] :
(nta_pop_group == NPG_YOUNG_WORK) ? NtaYoungWork[sex][nta_educ3][nta_fam_index][nta_age_index][YAF] :
(nta_pop_group == NPG_ADULT_WORK) ? NtaAdultWorkAge[sex][nta_educ3][nta_fam_index][nta_age_index][YAF] :
NtaAdultOldAge[sex][nta_educ3][nta_old_index][nta_age_index][YAF];
//EN Public Asset Income (YAG)
double base_YAG =
(NtaScenario == NS_AGE) ? NtaAge[integer_age][YAG] :
(NtaScenario == NS_SEX) ? NtaSex[sex][integer_age][YAG] :
(nta_pop_group == NPG_CHILD) ? NtaChild[nta_educ3][nta_age_index][YAG] :
(nta_pop_group == NPG_YOUNG_STUDENT) ? NtaYoungStudent[nta_educ][nta_age_index][YAG] :
(nta_pop_group == NPG_YOUNG_WORK) ? NtaYoungWork[sex][nta_educ3][nta_fam_index][nta_age_index][YAG] :
(nta_pop_group == NPG_ADULT_WORK) ? NtaAdultWorkAge[sex][nta_educ3][nta_fam_index][nta_age_index][YAG] :
NtaAdultOldAge[sex][nta_educ3][nta_old_index][nta_age_index][YAG];
};
NtaIndicators.mpp
This module calculates a set of yearly NTA indicators following the approach presented by Ron Lee (2017) “Some Economic Impacts of Changing Population Age Distributions—Capital, Labor and Transfers”. We model a simple economy with a Cobb Douglas production function without productivity growth in two scenarios: a closed economy where the interest rate and wages are endogenous, and an open economy where wages and interest rates stay constant. The two indicators calculated are the “Support Ratio” and the “Impact Index”. Both measures have the “Effective consumers” - i.e. the age re-weighted consumption based on the reference year - in the denominator. The nominator of the Support Ratio is the projected total labour. In contrast, the “Impact Index” puts the total consumption assuming constant saving rates by age and accounting for the changes in wages and interest rates into the nominator.:
Variables:
- yl(x) Average labor income age x
- yk(x) Average capital income age x
- P(x) Population age x
- i(x) Average savings age x
- s(x) Saving rate age x - constant
- c(x) Average Consumption age x (reference values for calculation of N) - reference
- L Labor
- K Capital
- I Saving
- S Saving rate
- C Consumption
- r Interest rate
- w Wage
- Yl Labor Income
- YK Capital Income
- Y Total Income Yl+Yk
- α Alpha – constant
- N Effective Consumers (population-weighted base-year consumption)
- l(x) Average labor age x – constant
- k(x) Average capital age x – constant
Cobb Douglas:
Y = L^α K^(1- α)
Y = wL + rK
w = αY / L
r = (1- α) Y / K
Yl = wL = αY
Yk = rK = (1- α)Y
Known:
yl(x) NTA data of reference year
yk(x) NTA data of reference year
i(x) NTA data of reference year
c(x) NTA data of reference year
P(x) NTA data of reference year
r parameter for the reference year (also used to estimate stock from capital income flow)
Calculated for the initial year:
Yl = ∑ yl(x) * P(x)
Yk = ∑ yk(x) * P(x)
Y = Yl + Yk
α = Yl / Y
K = Yk / r
L = ( Y / K^(1- α))^(1/α)
w = Yl / L
s(x) = i(x) / (yl(x) + ykx))
l(x) = yl (x) / w
k(x) = yk (x) / r
N = ∑ cl(x) * P(x)
Simulation: calculate for an updated population by age P(x)
Closed Economy
L = ∑ l(x)* P(x)
K = ∑ k(x)* P(x)
Y = L^α K^(1- α)
Yl = αY
YK = (1- α)Y
w = αY / L
r = (1- α) Y / K
N = ∑ c(x) * P(x)
yl(x) = w * l(x)
yk(x) = r * k(x)
C = ∑ (1-s(x)) * (yl(x) + yk(x) ) * P(x)
Open Economy (difference to closed)
Y = w * L + r * K
Yl = w * L
YK = r * K
Indices
SR = L / N Support Ratio (same for open and closed economy)
IMP = C / N Impact Index (different for closed and open economy)
This module is optional. It feeds on the NTA variables (by age, sex, education, family) calculated and maintained in the NtaBase.mpp module. The only model parameters are the reference year of the NTA data and the initial interest rate. All calculations are performed in a yearly update event of the actor State.
////////////////////////////////////////////////////////////////////////////////////////////////////
// Parameters
////////////////////////////////////////////////////////////////////////////////////////////////////
parameter_group PG_NTA_CONST //EN NTA Indicators
{
NtaInitialInterestRate, NtaBaseYear
};
parameters
{
int NtaBaseYear; //EN NTA Base Year
double NtaInitialInterestRate; //EN Initial Interest Rate
};
////////////////////////////////////////////////////////////////////////////////////////////////////
// State Actor states and functions
////////////////////////////////////////////////////////////////////////////////////////////////////
actor State
{
double nta_initial_Y = { 0.0 }; //EN Initial year total income Y
double nta_initial_Yl = { 0.0 }; //EN Initial year labor income Yl
double nta_initial_Yk = { 0.0 }; //EN Initial year capital income Yk
double nta_initial_K = { 0.0 }; //EN Initial year capital stock
double nta_alpha = { 0.0 }; //EN Alpha of Cobb Douglas Function
double nta_initial_L = { 0.0 }; //EN Initial year total labor L
double nta_initial_w = { 0.0 }; //EN Initial year wage w
double nta_initial_SR = { 1.0 }; //EN Initial Support Ratio
double nta_initial_closed_IMP = { 1.0 }; //EN Initial Impact Factor closed economy
double nta_initial_open_IMP = { 1.0 }; //EN Initial Impact Factor open economy
double nta_current_N = { 0.0 }; //EN Current reference consumers N
double nta_current_L = { 0.0 }; //EN Current total Labor N
double nta_current_C = { 0.0 }; //EN Current total Consumption
double nta_current_C_open = { 0.0 }; //EN Current total Consumption (open economy)
double nta_current_Y = { 0.0 }; //EN Current total Y
double nta_current_K = { 0.0 }; //EN Current total Capital K
double nta_current_w = { 0.0 }; //EN Current wage w
double nta_current_r = { 0.0 }; //EN Current interest rate r
double nta_current_SR = { 1.0 }; //EN Current Support Ratio
double nta_current_closed_IMP = { 1.0 }; //EN Current Impact Factor closed economy
double nta_current_open_IMP = { 1.0 }; //EN Current Impact Factor open economy
logical nta_is_updated = { FALSE };
TIME time_nta_update = { 0.0 }; //EN Time of next NTA update
event timeUpdateNtaEvent, UpdateNtaEvent; //EN Update NTA production function
SIM_YEAR_RANGE tab_state_year = COERCE(SIM_YEAR_RANGE, state_year); //EN Calendar Year
};
////////////////////////////////////////////////////////////////////////////////////////////////////
// Implementation
////////////////////////////////////////////////////////////////////////////////////////////////////
TIME State::timeUpdateNtaEvent()
{
if (time_nta_update < NtaBaseYear ) return NtaBaseYear + 0.5;
else return time_nta_update;
}
void State::UpdateNtaEvent()
{
nta_is_updated = FALSE;
// Population size
long nPersons = asAllResidents->Count();
if (state_year == NtaBaseYear)
{
// Values of initial year
for (long nI = 0; nI < nPersons; nI++)
{
auto prPerson = asAllResidents->Item(nI);
nta_initial_Yl = nta_initial_Yl + prPerson->base_YL;
nta_initial_Yk = nta_initial_Yk + prPerson->base_YAF + prPerson->base_YAG;
}
nta_initial_Y = nta_initial_Yl + nta_initial_Yk;
nta_initial_K = nta_initial_Yk / NtaInitialInterestRate;
nta_alpha = nta_initial_Yl / nta_initial_Y;
nta_initial_L = pow(nta_initial_Y / pow(nta_initial_K,1-nta_alpha), 1.0 / nta_alpha);
nta_initial_w = nta_initial_Yl / nta_initial_L;
}
if (state_year >= NtaBaseYear)
{
double dNta_sx = 0.0; // applicable individual saving rate as in initial year
double dNta_lx = 0.0; // applicable individual labor input as in initial year
double dNta_kx = 0.0; // applicable individual capital as in initial year
double dNta_cx = 0.0; // applicable individual consumption as in initial year
nta_current_L = 0.0; // Current total Labor N
nta_current_K = 0.0; // Current total Capital K
nta_current_N = 0.0; // Current reference consumers N
nta_current_C = 0.0; // Current total Consumption
nta_current_C_open = 0.0; // Current total Consumption in open economy
nta_current_Y = 0.0; // Current total Y
for (long nI = 0; nI < nPersons; nI++)
{
auto prPerson = asAllResidents->Item(nI);
dNta_lx = prPerson->base_YL / nta_initial_w;
dNta_kx = (prPerson->base_YAF + prPerson->base_YAG) / NtaInitialInterestRate;
dNta_cx = prPerson->base_CFE + prPerson->base_CFH + prPerson->base_CFX + prPerson->base_CGE
+ prPerson->base_CGH + prPerson->base_CGX;
nta_current_L = nta_current_L + dNta_lx;
nta_current_K = nta_current_K + dNta_kx;
nta_current_N = nta_current_N + dNta_cx;
}
nta_current_Y = pow(nta_current_L, nta_alpha) * pow(nta_current_K, 1 - nta_alpha);
nta_current_w = nta_alpha * nta_current_Y / nta_current_L;
nta_current_r = (1 - nta_alpha) * nta_current_Y / nta_current_K;
for (long nI = 0; nI < nPersons; nI++)
{
auto prPerson = asAllResidents->Item(nI);
dNta_lx = prPerson->base_YL / nta_initial_w;
dNta_kx = (prPerson->base_YAF + prPerson->base_YAG) / NtaInitialInterestRate;
dNta_sx = 0.0;
if (prPerson->base_YL + prPerson->base_YAF + prPerson->base_YAG > 0.0 && prPerson->base_SF + prPerson->base_SG > 0.0 &&
prPerson->base_YL + prPerson->base_YAF + prPerson->base_YAG > prPerson->base_SF + prPerson->base_SG)
{
dNta_sx = (prPerson->base_SF + prPerson->base_SG) / (prPerson->base_YL + prPerson->base_YAF + prPerson->base_YAG);
}
nta_current_C = nta_current_C + (1 - dNta_sx) * (nta_current_w * dNta_lx + nta_current_r * dNta_kx);
nta_current_C_open = nta_current_C_open + (1 - dNta_sx) * (nta_initial_w * dNta_lx + NtaInitialInterestRate * dNta_kx);
}
}
// Indices
if (state_year == NtaBaseYear)
{
nta_initial_SR = nta_initial_L / nta_current_N;
nta_initial_closed_IMP = nta_current_C / nta_current_N;
nta_initial_open_IMP = nta_current_C_open / nta_current_N;
}
if (state_year >= NtaBaseYear)
{
nta_current_SR = ( nta_current_L / nta_current_N ) / nta_initial_SR;
nta_current_closed_IMP = ( nta_current_C / nta_current_N ) / nta_initial_closed_IMP;
nta_current_open_IMP = ( nta_current_C_open / nta_current_N ) / nta_initial_open_IMP;
}
// Update Clock
nta_is_updated = TRUE;
time_nta_update = WAIT(1.0);
}
NtaFullGenAccounts.mpp
This module introduces Full Generational Accounts (FGAs) as suggested by Lee et.al. 2017 in: Full Generational Accounts: What Do We Give to the Next Generation? Full Generational Accounts are used to study the effect of population ageing on the inter- and intra-generational redistribution of income from a longitudinal perspective, comparing lifetime measures of income and transfers by generation, gender, education and family characteristics. We project, for each generation and sociodemographic group, the net present value of expected transfers. Also, the model incorporates a mechanism to balance budgets over time in response to population ageing. These adjustments are assumed to be symmetric, i.e. taxes and contributions are increased by the same amount as benefits are cut to balance budgets.
Full Generational Accounts are calculated based on the detailed population projections produced by the model, disaggregated NTAs, and a set of parameters, namely economic growth, a discount factor, and cohort weights. Cohort weights are used to determine how many persons a person alive at the start of the simulation represents. The weight is the relation between the size of the birth cohort to the size of the surviving population of this birth cohort.
////////////////////////////////////////////////////////////////////////////////////////////////////
// Parameters
////////////////////////////////////////////////////////////////////////////////////////////////////
parameters
{
double FgaEconomicGrowth; //EN Economic growth
double FgaDiscountFactor; //EN Discount factor
double CohortWeight[YOB_1905_2009][SEX]; //EN Cohort Weight
model_generated double mgFgaUprateFactor[SIM_YEAR_RANGE]; //EN Uprate factor
//EN Discount factor for present value at birth
model_generated double mgFgaDeflationFactor[AGE_RANGE];
//EN Discount factor for present value 2010
model_generated double mgFgaDeflation2010[SIM_YEAR_RANGE];
//EN Discount factor for present value 2040
model_generated double mgFgaDeflation2040[SIM_YEAR_RANGE];
};
parameter_group PG_FGA //EN Full Generational Accounts
{
FgaEconomicGrowth, FgaDiscountFactor, CohortWeight
};
////////////////////////////////////////////////////////////////////////////////////////////////////
// Actor Person
////////////////////////////////////////////////////////////////////////////////////////////////////
range YEAR_2010_2100 { 2010,2100 }; //EN Year Range 2010-2100
range YOB_1905_2009{ 1905, 2009 }; //EN Year of birth
actor Person
{
//EN Cohort Weight
real cohort_weight = (WITHIN(YOB_1905_2009,year_of_birth)) ?
CohortWeight[RANGE_POS(YOB_1905_2009,year_of_birth)][sex] : 1.0;
//EN Ever Parent
logical ever_parent = ((sex == MALE && !never_father) || (sex == FEMALE && is_mother)) ? TRUE : FALSE;
logical yob_in_2010_2100 = (year_of_birth >= 2010 && year_of_birth <= 2100);
YEAR_2010_2100 yob_2010_2100 = COERCE(YEAR_2010_2100, year_of_birth); //EN Year of birth
// Age in tables
AGE_RANGE age_2010 = COERCE(AGE_RANGE,2010 - year_of_birth); //EN Age
AGE_RANGE age_2040 = COERCE(AGE_RANGE,2040 - year_of_birth); //EN Age
real fga_YL = base_YL * mgFgaUprateFactor[RANGE_POS(SIM_YEAR_RANGE,calendar_year)];
real fga_TF = (base_TFB + base_TFW) * mgFgaUprateFactor[RANGE_POS(SIM_YEAR_RANGE,calendar_year)];
real fga_TGI = (base_TGSOAI + base_TGXCI + base_TGXII + base_TGEI + base_TGHI)
* mgFgaUprateFactor[RANGE_POS(SIM_YEAR_RANGE,calendar_year)];
real fga_TGO = base_TGO * mgFgaUprateFactor[RANGE_POS(SIM_YEAR_RANGE,calendar_year)];
real fga_TF_adj = (fga_TF > 0.0) ? fga_TF * lGlobals->adj_factor_TFI
: fga_TF * lGlobals->adj_factor_TFO;
real fga_TGI_adj = fga_TGI * lGlobals->adj_factor_TGI;
real fga_TGO_adj = fga_TGO * lGlobals->adj_factor_TGO;
real deflation_factor = mgFgaDeflationFactor[integer_age];
// PV Age 0
real fga_YL_defl = deflation_factor * fga_YL;
real fga_YL_pv = weighted_duration(fga_YL_defl);
real fga_TF_defl = deflation_factor * fga_TF;
real fga_TF_pv = weighted_duration(fga_TF_defl);
real fga_TGI_defl = deflation_factor * fga_TGI;
real fga_TGI_pv = weighted_duration(fga_TGI_defl);
real fga_TGO_defl = deflation_factor * fga_TGO;
real fga_TGO_pv = weighted_duration(fga_TGO_defl);
real fga_TF_adj_defl = deflation_factor * fga_TF_adj;
real fga_TF_adj_pv = weighted_duration(fga_TF_adj_defl);
real fga_TGI_adj_defl = deflation_factor * fga_TGI_adj;
real fga_TGI_adj_pv = weighted_duration(fga_TGI_adj_defl);
real fga_TGO_adj_defl = deflation_factor * fga_TGO_adj;
real fga_TGO_adj_pv = weighted_duration(fga_TGO_adj_defl);
// PV 2010 (only transfers 2010+)
real deflation_2010 = (calendar_year >= MIN(SIM_YEAR_RANGE)) ?
mgFgaDeflation2010[RANGE_POS(SIM_YEAR_RANGE,calendar_year)] : 0.0;
real fga_YL_defl_2010 = deflation_2010 * fga_YL;
real fga_YL_pv_2010 = weighted_duration(fga_YL_defl_2010);
real fga_TF_defl_2010 = deflation_2010 * fga_TF;
real fga_TF_pv_2010 = weighted_duration(fga_TF_defl_2010);
real fga_TGI_defl_2010 = deflation_2010 * fga_TGI;
real fga_TGI_pv_2010 = weighted_duration(fga_TGI_defl_2010);
real fga_TGO_defl_2010 = deflation_2010 * fga_TGO;
real fga_TGO_pv_2010 = weighted_duration(fga_TGO_defl_2010);
real fga_TF_adj_defl_2010 = deflation_2010 * fga_TF_adj;
real fga_TF_adj_pv_2010 = weighted_duration(fga_TF_adj_defl_2010);
real fga_TGI_adj_defl_2010 = deflation_2010 * fga_TGI_adj;
real fga_TGI_adj_pv_2010 = weighted_duration(fga_TGI_adj_defl_2010);
real fga_TGO_adj_defl_2010 = deflation_2010 * fga_TGO_adj;
real fga_TGO_adj_pv_2010 = weighted_duration(fga_TGO_adj_defl_2010);
// PV 2040 (only transfers 2040+)
real deflation_2040 = (calendar_year >= 2040) ?
mgFgaDeflation2040[RANGE_POS(SIM_YEAR_RANGE,calendar_year)] : 0.0;
real fga_YL_defl_2040 = deflation_2040 * fga_YL;
real fga_YL_pv_2040 = weighted_duration(fga_YL_defl_2040);
real fga_TF_defl_2040 = deflation_2040 * fga_TF;
real fga_TF_pv_2040 = weighted_duration(fga_TF_defl_2040);
real fga_TGI_defl_2040 = deflation_2040 * fga_TGI;
real fga_TGI_pv_2040 = weighted_duration(fga_TGI_defl_2040);
real fga_TGO_defl_2040 = deflation_2040 * fga_TGO;
real fga_TGO_pv_2040 = weighted_duration(fga_TGO_defl_2040);
real fga_TF_adj_defl_2040 = deflation_2040 * fga_TF_adj;
real fga_TF_adj_pv_2040 = weighted_duration(fga_TF_adj_defl_2040);
real fga_TGI_adj_defl_2040 = deflation_2040 * fga_TGI_adj;
real fga_TGI_adj_pv_2040 = weighted_duration(fga_TGI_adj_defl_2040);
real fga_TGO_adj_defl_2040 = deflation_2040 * fga_TGO_adj;
real fga_TGO_adj_pv_2040 = weighted_duration(fga_TGO_adj_defl_2040);
};
////////////////////////////////////////////////////////////////////////////////////////////////////
// Actor Globals
////////////////////////////////////////////////////////////////////////////////////////////////////
actor Globals
{
real adj_factor_TFI = {1.0};
real adj_factor_TFO = {1.0};
real adj_factor_TGI = {1.0};
real adj_factor_TGO = {1.0};
};
////////////////////////////////////////////////////////////////////////////////////////////////////
// Actor State
////////////////////////////////////////////////////////////////////////////////////////////////////
actor State
{
TIME time_update_fga = { 0.0 }; //EN Time of next FGA udjustment update
event timeUpdateFgaAdjustEvent, UpdateFgaAdjustEvent; //EN FGA adjustment update event
};
////////////////////////////////////////////////////////////////////////////////////////////////////
// Implementation
////////////////////////////////////////////////////////////////////////////////////////////////////
TIME State::timeUpdateFgaAdjustEvent()
{
if (time_update_fga < MIN(SIM_YEAR_RANGE)) return MIN(SIM_YEAR_RANGE) + 0.5;
else return time_update_fga;
}
void State::UpdateFgaAdjustEvent()
{
// Population size
long nPersons = asAllResidents->Count();
double dTotalTFI = 0.0;
double dTotalTFO = 0.0;
double dTotalTGI = 0.0;
double dTotalTGO = 0.0;
double dAverage;
// Loop adding individual NTAs
for (long nI = 0; nI < nPersons; nI++)
{
auto prPerson = asAllResidents->Item(nI);
if (prPerson->fga_TF > 0.0) dTotalTFI = dTotalTFI + prPerson->fga_TF;
else dTotalTFO = dTotalTFO - prPerson->fga_TF;
dTotalTGI = dTotalTGI + prPerson->fga_TGI;
dTotalTGO = dTotalTGO + prPerson->fga_TGO;
}
// Adjust TG
dAverage = (dTotalTGI + dTotalTGO) / 2.0;
if (dTotalTGI <= 0.0 || dTotalTGO <= 0.0) // Problem div 0
{
lStateToGlobals->adj_factor_TGI = 0.0;
lStateToGlobals->adj_factor_TGO = 0.0;
}
else // OK
{
lStateToGlobals->adj_factor_TGI = dAverage / dTotalTGI;
lStateToGlobals->adj_factor_TGO = dAverage / dTotalTGO;
}
// Adjust TF
dAverage = (dTotalTFI + dTotalTFO) / 2.0;
if (dTotalTFI <= 0.0 || dTotalTFO <= 0.0) // Problem div 0
{
lStateToGlobals->adj_factor_TFI = 0.0;
lStateToGlobals->adj_factor_TFO = 0.0;
}
else // OK
{
lStateToGlobals->adj_factor_TFI = dAverage / dTotalTFI;
lStateToGlobals->adj_factor_TFO = dAverage / dTotalTFO;
}
// Program next update event
time_update_fga = WAIT(1.0);
}
////////////////////////////////////////////////////////////////////////////////////////////////////
// Pre-Simulation
////////////////////////////////////////////////////////////////////////////////////////////////////
void PreSimulation()
{
for (int nJ = 0; nJ < SIZE(SIM_YEAR_RANGE); nJ++)
{
mgFgaUprateFactor[nJ] = 1.0 * pow((1.0 + FgaEconomicGrowth), nJ);
}
for (int nJ = 0; nJ < SIZE(AGE_RANGE); nJ++)
{
mgFgaDeflationFactor[nJ] = 1.0 * pow((1.0 / (1.0 + FgaDiscountFactor)), nJ);
}
for (int nJ = 0; nJ < SIZE(SIM_YEAR_RANGE); nJ++)
{
if (MIN(SIM_YEAR_RANGE) + nJ >= 2010) mgFgaDeflation2010[nJ] = 1.0
* pow((1.0 / (1.0 + FgaDiscountFactor)), 2010-MIN(SIM_YEAR_RANGE)+nJ);
else mgFgaDeflation2010[nJ] = 0.0;
}
for (int nJ = 0; nJ < SIZE(SIM_YEAR_RANGE); nJ++)
{
if (MIN(SIM_YEAR_RANGE) + nJ >= 2040) mgFgaDeflation2040[nJ] = 1.0
* pow((1.0 / (1.0 + FgaDiscountFactor)), 2040-MIN(SIM_YEAR_RANGE)+nJ);
else mgFgaDeflation2040[nJ] = 0.0;
}
}
Output Modules
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()
{
// create output variables for linked actors
long nSpouseID = -1; if (has_partner) nSpouseID = lSpouse->actor_id;
long nBioMotherID = -1; if (lBioMother) nBioMotherID = lBioMother->actor_id;
long nBioFatherID = -1; if (lBioFather) nBioFatherID = lBioFather->actor_id;
long nHHMotherID = -1; if (lHHMother) nHHMotherID = lHHMother->actor_id;
long nHHFatherID = -1; if (lHHFather) nHHFatherID = lHHFather->actor_id;
// 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 << nSpouseID; // Spouse ID
out_csv << nHHMotherID; // Mother in HH
out_csv << nHHFatherID; // Father in HH
out_csv << time_of_birth; // Time of birth
out_csv << (int)sex; // Sex
out_csv << (int)educ3_level; // Education
out_csv << (int)family_role; // Family Role
out_csv << (int)in_school; // Any school
out_csv << (int)in_regular_school; // Regular school
// All fields have been pushed, now write the record.
if (is_resident) 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,"; // Time
myString = myString + "SPOUSE_ID,"; // Spouse
myString = myString + "MOTHER_ID,"; // Parent
myString = myString + "FATHER_ID,"; // Parent
myString = myString + "BIRTH,"; // Time of birth
myString = myString + "MALE,"; // Sex
myString = myString + "EDUC,"; // Education
myString = myString + "ROLE,"; // Role
myString = myString + "ANYSCHOOL,"; // In School
myString = myString + "REGSCHOOL,"; // In regularSchool
out_csv.open(MicroRecordFileName);
out_csv.write_header(myString);
}
}
void PostSimulation()
{
if (WriteMicrodata) { out_csv.close(); } // closing the file
}
TablesPopulation.mpp
This module implements table output related to the total population and its composition
////////////////////////////////////////////////////////////////////////////////////////////////////
// Table Groups
////////////////////////////////////////////////////////////////////////////////////////////////////
table_group TG_POPULATION_TABLES //EN Population
{
tabTotalPopulationAgeSexEduc
};
////////////////////////////////////////////////////////////////////////////////////////////////////
// Tables
////////////////////////////////////////////////////////////////////////////////////////////////////
table Person tabTotalPopulationAgeSexEduc //EN Population by age, sex and education
[is_resident && is_alive && in_projected_time]
{
sex + *
educ3_level+ *
{
duration() //EN Population
}
* integer_age
* sim_year
};
TablesEducation.mpp
This module implements a set of tables related to education.
////////////////////////////////////////////////////////////////////////////////////////////////////
// Table Dimensions
////////////////////////////////////////////////////////////////////////////////////////////////////
partition AGE_530 { 5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30 }; //EN Age
classification YEAR10 //EN Year
{
YEAR_2010, //EN 2010
YEAR_2020, //EN 2020
YEAR_2030, //EN 2030
YEAR_2040, //EN 2040
YEAR_2050, //EN 2050
YEAR_2060, //EN 2060
YEAR_2070, //EN 2070
YEAR_2080, //EN 2080
YEAR_2090, //EN 2090
YEAR_2100 //EN 2100
};
classification YEAR05 //EN Year
{
YEAR5_2010, //EN 2010
YEAR5_2015, //EN 2015
YEAR5_2020, //EN 2020
YEAR5_2025, //EN 2025
YEAR5_2030, //EN 2030
YEAR5_2035, //EN 2035
YEAR5_2040, //EN 2040
YEAR5_2045, //EN 2045
YEAR5_2050, //EN 2050
YEAR5_2055, //EN 2055
YEAR5_2060, //EN 2060
YEAR5_2065, //EN 2065
YEAR5_2070, //EN 2070
YEAR5_2075, //EN 2075
YEAR5_2080, //EN 2080
YEAR5_2085, //EN 2085
YEAR5_2090, //EN 2090
YEAR5_2095, //EN 2095
YEAR5_2100 //EN 2100
};
partition AGE_5100 //EN Age Groups
{
5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60,
65, 70, 75, 80, 85, 90, 95, 100
};
////////////////////////////////////////////////////////////////////////////////////////////////////
// Table States
////////////////////////////////////////////////////////////////////////////////////////////////////
actor Person
{
//EN Year
YEAR10 year10 = (calendar_year == 2010) ? YEAR_2010 :
(calendar_year == 2020) ? YEAR_2020 :
(calendar_year == 2030) ? YEAR_2030 :
(calendar_year == 2040) ? YEAR_2040 :
(calendar_year == 2050) ? YEAR_2050 :
(calendar_year == 2060) ? YEAR_2060 :
(calendar_year == 2070) ? YEAR_2070 :
(calendar_year == 2080) ? YEAR_2080 :
(calendar_year == 2090) ? YEAR_2090 : YEAR_2100;
//EN Year
YEAR05 year05 = (calendar_year == 2010) ? YEAR5_2010 :
(calendar_year == 2015) ? YEAR5_2015 :
(calendar_year == 2020) ? YEAR5_2020 :
(calendar_year == 2025) ? YEAR5_2025 :
(calendar_year == 2030) ? YEAR5_2030 :
(calendar_year == 2035) ? YEAR5_2035 :
(calendar_year == 2040) ? YEAR5_2040 :
(calendar_year == 2045) ? YEAR5_2045 :
(calendar_year == 2050) ? YEAR5_2050 :
(calendar_year == 2055) ? YEAR5_2055 :
(calendar_year == 2060) ? YEAR5_2060 :
(calendar_year == 2065) ? YEAR5_2065 :
(calendar_year == 2070) ? YEAR5_2070 :
(calendar_year == 2075) ? YEAR5_2075 :
(calendar_year == 2080) ? YEAR5_2080 :
(calendar_year == 2085) ? YEAR5_2085 :
(calendar_year == 2090) ? YEAR5_2090 :
(calendar_year == 2095) ? YEAR5_2095 : YEAR5_2100;
logical is_year05 = (calendar_year == 2010 || calendar_year == 2020 || calendar_year == 2030 || calendar_year == 2040
|| calendar_year == 2050 || calendar_year == 2060 || calendar_year == 2070
|| calendar_year == 2080 || calendar_year == 2090 || calendar_year == 2100
|| calendar_year == 2015 || calendar_year == 2025 || calendar_year == 2035 || calendar_year == 2045
|| calendar_year == 2055 || calendar_year == 2065 || calendar_year == 2075
|| calendar_year == 2085 || calendar_year == 2095) ? TRUE : FALSE;
logical is_year10 = (calendar_year == 2010 || calendar_year == 2020 || calendar_year == 2030 || calendar_year == 2040
|| calendar_year == 2050 || calendar_year == 2060 || calendar_year == 2070
|| calendar_year == 2080 || calendar_year == 2090 || calendar_year == 2100) ? TRUE : FALSE;
};
////////////////////////////////////////////////////////////////////////////////////////////////////
// Table Groups
////////////////////////////////////////////////////////////////////////////////////////////////////
table_group TG_EDUCATION_TABLES //EN Education
{
tabEducSchoolEnrolment,
tabPopulationByEducation,
tabPopulationByEducation2059,
tabEducationYob,
tabEducationParentsYob
};
////////////////////////////////////////////////////////////////////////////////////////////////////
// Tables
////////////////////////////////////////////////////////////////////////////////////////////////////
table Person tabEducSchoolEnrolment //EN School enrolment
[is_resident && in_projected_time]
{
{
duration(in_school, TRUE) / duration(), //EN In any school decimals=2
duration(in_regular_school, TRUE) / duration(), //EN In regular school decimals=2
duration(in_other_education, TRUE) / duration() //EN In other school decimals=2
}
* split(integer_age, AGE_530) //EN Age
* sim_year
};
table Person tabPopulationByEducation //EN Population by age group and education
[is_resident && is_year10]
{
year10 *
sex+ *
{
duration()
}
* split(integer_age, AGE_5100)+ //EN Age
* educ3_level+
};
table Person tabPopulationByEducation2059 //EN Population 20-59 by education
[is_resident && is_year05 && integer_age > 19 && integer_age < 60]
{
sex+ *
{
duration()
}
* year05
* educ3_level+
};
table Person tabEducationYob //EN Education distribution at 30 by year of birth
[is_resident && integer_age == 30]
{
sex+ *
{
duration()
}
* year_of_birth
* educ_fate+
};
table Person tabEducationParentsYob //EN Education of parents by year of birth
[is_resident && integer_age == 0]
{
sex+ *
{
duration()
}
* year_of_birth
* educ_parents+
};
TablesFamily.mpp
This module implements a set of tables related to families
////////////////////////////////////////////////////////////////////////////////////////////////////
// Table Dimensions
////////////////////////////////////////////////////////////////////////////////////////////////////
partition SHORT_TIME{ 0.01 };
partition AGE_1025{ 10, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26 }; //EN Age
////////////////////////////////////////////////////////////////////////////////////////////////////
// Table States
////////////////////////////////////////////////////////////////////////////////////////////////////
actor Person
{
//EN Single, no dependent children at home
logical is_single_alone = (!has_partner && children_in_household == 0) ? TRUE : FALSE;
//EN Single, dependent children at home
logical is_single_child = (!has_partner && children_in_household > 0) ? TRUE : FALSE;
//EN In partnership, no dependent children at home
logical is_couple_alone = (has_partner && children_in_household == 0) ? TRUE : FALSE;
//EN In partnership, dependent children at home
logical is_couple_child = (has_partner && children_in_household > 0) ? TRUE : FALSE;
//EN Person ever parent
logical is_ever_parent = (entrances(lives_with_dependent_child,TRUE) > 0) ;
//EN Lifetime spent single, no dependent children at home
real time_is_single_alone = duration(is_single_alone, TRUE);
//EN Lifetime spent single, with dependent children at home
real time_is_single_child = duration(is_single_child, TRUE);
//EN Lifetime spent in partnership, no dependent children at home
real time_is_couple_alone = duration(is_couple_alone, TRUE);
//EN Lifetime spent in partnership, with dependent children at home
real time_is_couple_child = duration(is_couple_child, TRUE);
};
////////////////////////////////////////////////////////////////////////////////////////////////////
// Table Groups
////////////////////////////////////////////////////////////////////////////////////////////////////
table_group TG_FAMILY_TABLES //EN Family
{
tabFamilyType,
tabCohortFamilyExperience20102014,
tabLivingWithParents
};
////////////////////////////////////////////////////////////////////////////////////////////////////
// Tables
////////////////////////////////////////////////////////////////////////////////////////////////////
table Person tabFamilyType //EN Population by age and family type
[is_alive && is_resident && is_year10]
{
year10 *
sex + *
educ_fate+ *
split(integer_age, AGE5_PART) *
{
duration(is_single_alone,TRUE) / duration(), //EN Single, no dependent children at home decimals=3
duration(is_single_child,TRUE) / duration(), //EN Single, dependent children at home decimals=3
duration(is_couple_alone,TRUE) / duration(), //EN In partnership, no dependent children at home decimals=3
duration(is_couple_child,TRUE) / duration() //EN In partnership, dependent children at home decimals=3
}
};
table Person tabCohortFamilyExperience20102014 //EN Cohort 2010-2014 family experience
[is_resident && trigger_transitions(is_alive,TRUE,FALSE) && year_of_birth >= 2010 && year_of_birth < 2015 && person_type==PT_CHILD]
{
sex + *
is_ever_parent+ *
educ_fate+ *
{
value_in(time_is_single_alone) / unit, //EN Average time lived single, no dependent children at home decimals=3
value_in(time_is_single_child) / unit, //EN Average time lived single, dependent children at home decimals=3
value_in(time_is_couple_alone) / unit, //EN Average time lived in partnership, no dependent children at home decimals=3
value_in(time_is_couple_child) / unit //EN Average time lived in partnership, dependent children at home decimals=3
}
};
table Person tabLivingWithParents //EN Living with parents
[is_resident && in_projected_time && is_year10]
{
sex + *
year10 *
{
duration(family_role,FR_CHILD) / duration() //EN Living with parent(s) decimals=3
}
* split(integer_age, AGE_1025)
* educ_fate +
};
TablesFertility.mpp
This module implements a collection of tables related to fertility.
////////////////////////////////////////////////////////////////////////////////////////////////////
// Table Groups
////////////////////////////////////////////////////////////////////////////////////////////////////
table_group TG_FERTILITY_TABLES //EN Fertility
{
tabFertilityBirths,
tabFertilityAgeBirth,
tabFertilityCohortChildlessness,
tabFertilityMaleCohortChildlesness,
tabFertilityFemaleChildlessness
};
////////////////////////////////////////////////////////////////////////////////////////////////////
// Tables
////////////////////////////////////////////////////////////////////////////////////////////////////
table Person tabFertilityBirths //EN Births
[is_alive && in_projected_time && is_resident && sex==FEMALE && WITHIN(FERTILE_AGE_RANGE,integer_age)]
{
{
parity, //EN Births
transitions(parity,0,1), //EN First births
parity/duration(), //EN Birth rate decimals=4
transitions(parity,0,1)/duration() //EN First birth rate decimals=4
}
* educ_fate+
* fertile_age+
* sim_year
};
table Person tabFertilityAgeBirth //EN Age at birth
[is_alive && in_projected_time && is_resident && sex==FEMALE && WITHIN(FERTILE_AGE_RANGE,integer_age)]
{
{
value_at_changes(parity,age) / changes(parity), //EN Average age birth decimals=2
value_at_transitions(parity,0,1,age) / transitions(parity,0,1) //EN Average age 1st birth decimals=2
}
* educ_fate+
* sim_year
};
actor Person
{
YOB_BIRTH1 yob1 = COERCE(YOB_BIRTH1,year_of_birth); //EN Year of birth
};
table Person tabFertilityCohortChildlessness //EN Cohort childlessness
[is_alive && integer_age==50 && is_resident && sex==FEMALE && WITHIN(YOB_BIRTH1,year_of_birth)]
{
{
duration(parity,0) / duration() //EN Female cohort Childlessness decimals=2
}
* educ_fate+
* yob1
};
table Person tabFertilityFemaleChildlessness //EN Female period childlessness
[is_alive && is_resident && sex==FEMALE && is_year10]
{
year10 *
{
duration(parity,0) / duration() //EN Cohort Childlessness decimals=2
}
* integer_age
* educ_fate+
};
table Person tabFertilityMaleCohortChildlesness //EN Male Cohort Childlessness (at death - run to 2150!)
[sex == MALE && is_resident && trigger_transitions(is_alive,TRUE,FALSE) && WITHIN(YOB_BIRTH1,year_of_birth)]
{
{
value_in(never_father) / unit //EN Childlessenss decimals=2
}
* yob1
* educ_fate +
};
TablesMigration.mpp
This module implements table output related to international migration
////////////////////////////////////////////////////////////////////////////////////////////////////
// Table Groups
////////////////////////////////////////////////////////////////////////////////////////////////////
table_group TG_MIGRATION_TABLES //EN Migration
{
tabNumberMigrants
};
////////////////////////////////////////////////////////////////////////////////////////////////////
// Tables
////////////////////////////////////////////////////////////////////////////////////////////////////
table Person tabNumberMigrants //EN Number of migrants
[is_alive && in_projected_time]
{
sex + *
{
//EN Immigrants
transitions(ever_resident,FALSE,TRUE),
//EN Emigrants
transitions(is_resident,TRUE,FALSE),
//EN Net migration
transitions(ever_resident,FALSE,TRUE) - transitions(is_resident,TRUE,FALSE)
}
* integer_age+
* sim_year
};
TablesMortality.mpp
This module implements a collection of tables related to mortality.
////////////////////////////////////////////////////////////////////////////////////////////////////
// Table States
////////////////////////////////////////////////////////////////////////////////////////////////////
actor Person
{
logical is_ever_60 = (entrances(integer_age,60) > 0); //EN Ever 60
logical is_ever_65 = (entrances(integer_age,65) > 0); //EN Ever 65
logical is_ever_70 = (entrances(integer_age,70) > 0); //EN Ever 70
logical is_ever_75 = (entrances(integer_age,75) > 0); //EN Ever 75
logical is_ever_80 = (entrances(integer_age,80) > 0); //EN Ever 80
logical is_ever_85 = (entrances(integer_age,85) > 0); //EN Ever 85
logical is_ever_90 = (entrances(integer_age,90) > 0); //EN Ever 90
logical is_ever_95 = (entrances(integer_age,95) > 0); //EN Ever 95
logical is_ever_100 = (entrances(integer_age,100) > 0); //EN Ever 100
};
////////////////////////////////////////////////////////////////////////////////////////////////////
// Table Groups
////////////////////////////////////////////////////////////////////////////////////////////////////
table_group TG_MORTALITY_TABLES //EN Mortality
{
tabPeriodMortality,
tabCohortLifeExpectancy20102014,
tabMortalityNumberDeaths
};
////////////////////////////////////////////////////////////////////////////////////////////////////
// Tables
////////////////////////////////////////////////////////////////////////////////////////////////////
table Person tabCohortLifeExpectancy20102014 //EN Cohort 2010-2014 Life Expectancy
[is_resident && trigger_transitions(is_alive,TRUE,FALSE)
&& year_of_birth >= 2010 && year_of_birth < 2015 && person_type==PT_CHILD]
{
sex + *
educ_fate+ *
{
value_in(age) / unit, //EN Life expectancy decimals=3
value_in(is_ever_60) / unit, //EN Proportion surviving until 60 decimals=3
value_in(is_ever_65) / unit, //EN Proportion surviving until 65 decimals=3
value_in(is_ever_70) / unit, //EN Proportion surviving until 70 decimals=3
value_in(is_ever_75) / unit, //EN Proportion surviving until 75 decimals=3
value_in(is_ever_80) / unit, //EN Proportion surviving until 80 decimals=3
value_in(is_ever_85) / unit, //EN Proportion surviving until 85 decimals=3
value_in(is_ever_90) / unit, //EN Proportion surviving until 90 decimals=3
value_in(is_ever_95) / unit, //EN Proportion surviving until 95 decimals=3
value_in(is_ever_100) / unit //EN Proportion surviving until 100 decimals=3
}
};
table Person tabPeriodMortality //EN Period mortality rates
[is_resident && in_projected_time && is_year10]
{
sex + *
year10 *
{
transitions(is_alive,TRUE,FALSE) / duration() //EN Mortality rate decimals=3
}
* integer_age
* educ_fate+
};
table Person tabMortalityNumberDeaths //EN Number of deaths
[is_resident && in_projected_time]
{
{
transitions(is_alive,TRUE,FALSE) //EN Deaths
}
* sim_year
};
TablesNtaBasics.mpp
This module implements a set of output tables related to National Transfer Accounts (NTAs). Tables include national totlas for all NTA variables, average age profiles, and measures of the Life Course Deficit.
////////////////////////////////////////////////////////////////////////////////////////////////////
// Table Dimensions
////////////////////////////////////////////////////////////////////////////////////////////////////
partition AGE5105_PART //EN Age Groups
{
5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60,
65, 70, 75, 80, 85, 90, 95, 100
};
////////////////////////////////////////////////////////////////////////////////////////////////////
// Table States
////////////////////////////////////////////////////////////////////////////////////////////////////
actor Person
{
SIM_YEAR_RANGE sim_year = COERCE(SIM_YEAR_RANGE, calendar_year); //EN Year
};
////////////////////////////////////////////////////////////////////////////////////////////////////
// Groups
////////////////////////////////////////////////////////////////////////////////////////////////////
table_group TG_BasicNtaTabs //EN Basic NTA Tables
{
TabBasicNta, TabBasicNtaByAge, tabLCDperCap, tabPopulationByAgeNta
};
////////////////////////////////////////////////////////////////////////////////////////////////////
// Tables
////////////////////////////////////////////////////////////////////////////////////////////////////
table Person TabBasicNta //EN Basic NTA totals
[WITHIN(SIM_YEAR_RANGE, calendar_year) && is_resident]
{
{
duration(), //EN Population
weighted_duration(base_CFE), //EN Private Consumption Education (CFE)
weighted_duration(base_CFH), //EN Private Consumption Health (CFH)
weighted_duration(base_CFX), //EN Private Consumption other than Education and Health (CFX)
weighted_duration(base_CGE), //EN Public Consumption Education (CGE)
weighted_duration(base_CGH), //EN Public Consumption Health (CGH)
weighted_duration(base_CGX), //EN Public Consumption other than Education and Health (CGX)
weighted_duration(base_TGSOAI), //EN Public Transfers Pensions, Inflows (TGSOAI)
weighted_duration(base_TGXCI), //EN Public Transfers Other Cash Inflows (TGXCI)
weighted_duration(base_TGXII), //EN Public Transfers Other In-Kind Inflows (TGXII)
weighted_duration(base_TGEI), //EN Public Transfers Education Inflows (TGEI)
weighted_duration(base_TGHI), //EN Public Transfers Health Inflows (TGHI)
weighted_duration(base_TGO), //EN Public Transfers Outflows (TGO)
weighted_duration(base_TFB), //EN Net Interhousehold Transfers (TFB)
weighted_duration(base_TFW), //EN Net Intrahousehold Transfers (TFW)
weighted_duration(base_SF), //EN Private Saving (SF)
weighted_duration(base_SG), //EN Public Saving (SG)
weighted_duration(base_YL), //EN Labor Income (LY)
weighted_duration(base_YAF), //EN Private Asset Income (YAF)
weighted_duration(base_YAG) //EN Public Asset Income (YAG)
}
* sim_year
};
table Person TabBasicNtaByAge //EN NTA by age
[WITHIN(SIM_YEAR_RANGE, calendar_year) && is_resident]
{
sim_year*
{
duration(), //EN Population
weighted_duration(base_CFE) / duration(), //EN Private Consumption Education (CFE)
weighted_duration(base_CFH) / duration(), //EN Private Consumption Health (CFH)
weighted_duration(base_CFX) / duration(), //EN Private Consumption other than Education and Health (CFX)
weighted_duration(base_CGE) / duration(), //EN Public Consumption Education (CGE)
weighted_duration(base_CGH) / duration(), //EN Public Consumption Health (CGH)
weighted_duration(base_CGX) / duration(), //EN Public Consumption other than Education and Health (CGX)
weighted_duration(base_TGSOAI) / duration(), //EN Public Transfers Pensions, Inflows (TGSOAI)
weighted_duration(base_TGXCI) / duration(), //EN Public Transfers Other Cash Inflows (TGXCI)
weighted_duration(base_TGXII) / duration(), //EN Public Transfers Other In-Kind Inflows (TGXII)
weighted_duration(base_TGEI) / duration(), //EN Public Transfers Education Inflows (TGEI)
weighted_duration(base_TGHI) / duration(), //EN Public Transfers Health Inflows (TGHI)
weighted_duration(base_TGO) / duration(), //EN Public Transfers Outflows (TGO)
weighted_duration(base_TFB) / duration(), //EN Net Interhousehold Transfers (TFB)
weighted_duration(base_TFW) / duration(), //EN Net Intrahousehold Transfers (TFW)
weighted_duration(base_SF) / duration(), //EN Private Saving (SF)
weighted_duration(base_SG) / duration(), //EN Public Saving (SG)
weighted_duration(base_YL) / duration(), //EN Labor Income (LY)
weighted_duration(base_YAF) / duration(), //EN Private Asset Income (YAF)
weighted_duration(base_YAG) / duration() //EN Public Asset Income (YAG)
}
* integer_age
};
table Person tabLCDperCap //EN Life Course Deficit
[WITHIN(SIM_YEAR_RANGE, calendar_year) && is_resident]
{
{
(weighted_duration(base_CFE) + weighted_duration(base_CFH) + weighted_duration(base_CFX) + weighted_duration(base_CGE) + weighted_duration(base_CGH) + weighted_duration(base_CGX)) / duration(), //EN average consumption
weighted_duration(base_YL) / duration(), //EN Average labor Income (LY)
-(weighted_duration(base_CFE) + weighted_duration(base_CFH) + weighted_duration(base_CFX) + weighted_duration(base_CGE) + weighted_duration(base_CGH) + weighted_duration(base_CGX) - weighted_duration(base_YL)) / duration() //EN Average gap
}
* sim_year
};
table Person tabPopulationByAgeNta //EN Population by age group and NTA type
[is_resident && is_year10]
{
year10 *
tab_fam_index *
sex *
{
duration()
}
* nta_pop_group //EN Population Group
* nta_educ+
};
TablesNtaIndicators.mpp
This modules produces output tables related to NTA indicators, including the Support Ration, the Imapct Index, and aggregate measures of effective labour, consumers, income, capital, as well as wages in dthe interest rate in a closed economy.
////////////////////////////////////////////////////////////////////////////////////////////////////
// Groups
////////////////////////////////////////////////////////////////////////////////////////////////////
table_group TG_NTAIndicators //EN NTA Indicators and Impact Index Lee 2017
{
TabNTAIndicators
};
////////////////////////////////////////////////////////////////////////////////////////////////////
// Tables
////////////////////////////////////////////////////////////////////////////////////////////////////
table State TabNTAIndicators //EN NTA Indicators and Impact Index Lee 2017
[WITHIN(SIM_YEAR_RANGE,state_year) && nta_is_updated]
{
{
max_value_in(nta_current_SR), //EN Support Ratio SR decimals=4
max_value_in(nta_current_closed_IMP), //EN Impact Index IMP closed economy decimals=4
max_value_in(nta_current_open_IMP), //EN Impact Index IMP open economy decimals=4
max_value_in(nta_current_N), //EN Current reference consumers N
max_value_in(nta_current_L), //EN Current total Labor L
max_value_in(nta_current_C), //EN Current total Consumption
max_value_in(nta_current_Y), //EN Current total Income Y
max_value_in(nta_current_K), //EN Current total Capital K
max_value_in(nta_current_w), //EN Current wage w closed economy decimals=4
max_value_in(nta_current_r) //EN Current interest rate r closed economy decimals=4
}
* tab_state_year
};
TablesNtaGenAccounts.mpp
This module implements table output related to Full General Accounts (FGAs).
////////////////////////////////////////////////////////////////////////////////////////////////////
// Table Groups
////////////////////////////////////////////////////////////////////////////////////////////////////
table_group TG_FullGenerationalAccounts //EN NTA Full Generational Accounts
{
tabFgaUnadjustedTotals,
tabFgaPV,
tabFgaPVDetail,
tabAdjustmentFga,
tabFgaPV_2010CW,
tabFgaPV_2040CW,
tabFgaPV_2010CWdetail,
tabFgaPV_2040CWdetail
};
////////////////////////////////////////////////////////////////////////////////////////////////////
// Tables
////////////////////////////////////////////////////////////////////////////////////////////////////
table Person tabFgaUnadjustedTotals //EN FGA Totals
[is_resident && in_projected_time]
{
{
weighted_duration(fga_YL), //EN YL total
weighted_duration(fga_TF), //EN TF total
weighted_duration(fga_TGI), //EN TGI total
weighted_duration(fga_TGO), //EN TGO total
weighted_duration(fga_TF_adj), //EN TF total adjusted
weighted_duration(fga_TGI_adj), //EN TGI total adjusted
weighted_duration(fga_TGO_adj) //EN TGO total adjusted
}
* sim_year
};
table Person tabFgaPV //EN FGA PV
[is_resident && person_type == PT_CHILD
&& trigger_transitions(is_alive,TRUE,FALSE)]
{
educ_fate+ *
{
value_in(fga_YL_pv) / unit, //EN Present Value YL at birth
value_in(fga_TF_pv) / unit, //EN Present Value TF at birth
value_in(fga_TGI_pv) / unit, //EN Present Value TGI at birth
value_in(fga_TGO_pv) / unit, //EN Present Value TGO at birth
value_in(fga_TF_adj_pv) / unit, //EN Present Value TF adjusted at birth
value_in(fga_TGI_adj_pv) / unit, //EN Present Value TGI adjsuted at birth
value_in(fga_TGO_adj_pv) / unit //EN Present Value TGO adjusted at birth
}
* year_of_birth
};
table Person tabFgaPVDetail //EN FGA PV by detailed Group
[is_resident && person_type == PT_CHILD && yob_in_2010_2100
&& trigger_transitions(is_alive,TRUE,FALSE)]
{
educ_fate+ *
sex+ *
ever_parent+ *
{
value_in(fga_YL_pv) / unit, //EN Present Value YL at birth
value_in(fga_TF_pv) / unit, //EN Present Value TF at birth
value_in(fga_TGI_pv) / unit, //EN Present Value TGI at birth
value_in(fga_TGO_pv) / unit, //EN Present Value TGO at birth
value_in(fga_TF_adj_pv) / unit, //EN Present Value TF adjusted at birth
value_in(fga_TGI_adj_pv) / unit, //EN Present Value TGI adjsuted at birth
value_in(fga_TGO_adj_pv) / unit, //EN Present Value TGO adjusted at birth
unit //EN Persons
}
* yob_2010_2100
};
table Globals tabAdjustmentFga //EN FGA Adjustment factors
[globals_in_projected_time]
{
{
max_value_in(adj_factor_TFI), //EN Adjustment TFI decimals=3
max_value_in(adj_factor_TFO), //EN Adjustment TFO decimals=3
max_value_in(adj_factor_TGI), //EN Adjustment TGI decimals=3
max_value_in(adj_factor_TGO) //EN Adjustment TGO decimals=3
}
* global_tab_sim_year
};
table Person tabFgaPV_2010CW //EN FGA PV 2010
[is_resident && person_type != PT_IMMIGRANT
&& trigger_transitions(is_alive,TRUE,FALSE)
&& year_of_birth <= 2010]
{
educ_fate+ *
{
value_in(fga_YL_pv_2010) / value_in(cohort_weight), //EN Present Value YL 2010
value_in(fga_TF_pv_2010) / value_in(cohort_weight), //EN Present Value TF 2010
value_in(fga_TGI_pv_2010) / value_in(cohort_weight), //EN Present Value TGI 2010
value_in(fga_TGO_pv_2010) / value_in(cohort_weight), //EN Present Value TGO 2010
value_in(fga_TF_adj_pv_2010) / value_in(cohort_weight), //EN Present Value TF adjusted 2010
value_in(fga_TGI_adj_pv_2010) / value_in(cohort_weight), //EN Present Value TGI adjusted 2010
value_in(fga_TGO_adj_pv_2010) / value_in(cohort_weight) //EN Present Value TGO adjusted 2010
}
* age_2010
};
table Person tabFgaPV_2040CW //EN FGA PV 2040
[is_resident && person_type != PT_IMMIGRANT
&& trigger_transitions(is_alive,TRUE,FALSE)
&& year_of_birth <= 2040]
{
educ_fate+ *
{
value_in(fga_YL_pv_2040) / value_in(cohort_weight), //EN Present Value YL 2040
value_in(fga_TF_pv_2040) / value_in(cohort_weight), //EN Present Value TF 2040
value_in(fga_TGI_pv_2040) / value_in(cohort_weight), //EN Present Value TGI 2040
value_in(fga_TGO_pv_2040) / value_in(cohort_weight), //EN Present Value TGO 2040
value_in(fga_TF_adj_pv_2040) / value_in(cohort_weight), //EN Present Value TF adjusted 2040
value_in(fga_TGI_adj_pv_2040) / value_in(cohort_weight), //EN Present Value TGI adjusted 2040
value_in(fga_TGO_adj_pv_2040) / value_in(cohort_weight) //EN Present Value TGO adjusted 2040
}
* age_2040
};
table Person tabFgaPV_2010CWdetail //EN FGA PV 2010 - detailed
[is_resident && person_type != PT_IMMIGRANT
&& trigger_transitions(is_alive,TRUE,FALSE)
&& year_of_birth <= 2010]
{
educ_fate+ *
sex+ *
ever_parent+ *
{
value_in(fga_YL_pv_2010) / value_in(cohort_weight), //EN Present Value YL 2010
value_in(fga_TF_pv_2010) / value_in(cohort_weight), //EN Present Value TF 2010
value_in(fga_TGI_pv_2010) / value_in(cohort_weight), //EN Present Value TGI 2010
value_in(fga_TGO_pv_2010) / value_in(cohort_weight), //EN Present Value TGO 2010
value_in(fga_TF_adj_pv_2010) / value_in(cohort_weight), //EN Present Value TF adjusted 2010
value_in(fga_TGI_adj_pv_2010) / value_in(cohort_weight), //EN Present Value TGI adjusted 2010
value_in(fga_TGO_adj_pv_2010) / value_in(cohort_weight) //EN Present Value TGO adjusted 2010
}
* age_2010
};
table Person tabFgaPV_2040CWdetail //EN FGA PV 2040 - detailed
[is_resident && person_type != PT_IMMIGRANT
&& trigger_transitions(is_alive,TRUE,FALSE)
&& year_of_birth <= 2040]
{
educ_fate+ *
sex+ *
ever_parent+ *
{
value_in(fga_YL_pv_2040) / value_in(cohort_weight), //EN Present Value YL 2040
value_in(fga_TF_pv_2040) / value_in(cohort_weight), //EN Present Value TF 2040
value_in(fga_TGI_pv_2040) / value_in(cohort_weight), //EN Present Value TGI 2040
value_in(fga_TGO_pv_2040) / value_in(cohort_weight), //EN Present Value TGO 2040
value_in(fga_TF_adj_pv_2040) / value_in(cohort_weight), //EN Present Value TF adjusted 2040
value_in(fga_TGI_adj_pv_2040) / value_in(cohort_weight), //EN Present Value TGI adjusted 2040
value_in(fga_TGO_adj_pv_2040) / value_in(cohort_weight) //EN Present Value TGO adjusted 2040
}
* age_2040
};
TablesNtaValidation.mpp
The NTA Validation Module is an optional module introducing a set of validation tables comparing the aggregate outcome of NTA variables by education and family type with published aggregates by sex a/o age. For each of the 19 NTA variables, a table is produced comparing aggregated simulated values in the starting year with published NTA values and providing aggregate simulated outcomes by education group. Also a table of the population composition by all considered dimensions (age, sex, education, family type) is produced. This module can be removed to save memory space.
////////////////////////////////////////////////////////////////////////////////////////////////////
// Dimensions
////////////////////////////////////////////////////////////////////////////////////////////////////
range TAB_FAM_INDEX { 0,5 }; //EN Family Index
////////////////////////////////////////////////////////////////////////////////////////////////////
// Actor states
////////////////////////////////////////////////////////////////////////////////////////////////////
actor Person
{
//EN Family Index
TAB_FAM_INDEX tab_fam_index = (nta_pop_group == NPG_OLD) ?
COERCE(TAB_FAM_INDEX, nta_old_index) : COERCE(TAB_FAM_INDEX, nta_fam_index);
// NTA by age only
double age_CFE = NtaAge[integer_age][CFE]; //EN CFE by age
double age_CFH = NtaAge[integer_age][CFH]; //EN CFH by age
double age_CFX = NtaAge[integer_age][CFX]; //EN CFX by age
double age_CGE = NtaAge[integer_age][CGE]; //EN CGE by age
double age_CGH = NtaAge[integer_age][CGH]; //EN CGH by age
double age_CGX = NtaAge[integer_age][CGX]; //EN CGX by age
double age_TGSOAI = NtaAge[integer_age][TGSOAI]; //EN TGSOAI by age
double age_TGXCI = NtaAge[integer_age][TGXCI]; //EN TGXCI by age
double age_TGXII = NtaAge[integer_age][TGXII]; //EN TGXII by age
double age_TGEI = NtaAge[integer_age][TGEI]; //EN TGEI by age
double age_TGHI = NtaAge[integer_age][TGHI]; //EN TGHI by age
double age_TGO = NtaAge[integer_age][TGO]; //EN TGO by age
double age_TFB = NtaAge[integer_age][TFB]; //EN TFB by age
double age_TFW = NtaAge[integer_age][TFW]; //EN TFW by age
double age_SF = NtaAge[integer_age][SF]; //EN SF by age
double age_SG = NtaAge[integer_age][SG]; //EN SG by age
double age_YL = NtaAge[integer_age][YL]; //EN LY by age
double age_YAF = NtaAge[integer_age][YAF]; //EN YAF by age
double age_YAG = NtaAge[integer_age][YAG]; //EN YAG by age
// NTA by age and sex
double sex_CFE = NtaSex[sex][integer_age][CFE]; //EN CFE by age and sex
double sex_CFH = NtaSex[sex][integer_age][CFH]; //EN CFH by age and sex
double sex_CFX = NtaSex[sex][integer_age][CFX]; //EN CFX by age and sex
double sex_CGE = NtaSex[sex][integer_age][CGE]; //EN CGE by age and sex
double sex_CGH = NtaSex[sex][integer_age][CGH]; //EN CGH by age and sex
double sex_CGX = NtaSex[sex][integer_age][CGX]; //EN CGX by age and sex
double sex_TGSOAI = NtaSex[sex][integer_age][TGSOAI]; //EN TGSOAI by age and sex
double sex_TGXCI = NtaSex[sex][integer_age][TGXCI]; //EN TGXCI by age and sex
double sex_TGXII = NtaSex[sex][integer_age][TGXII]; //EN TGXII by age and sex
double sex_TGEI = NtaSex[sex][integer_age][TGEI]; //EN TGEI by age and sex
double sex_TGHI = NtaSex[sex][integer_age][TGHI]; //EN TGHI by age and sex
double sex_TGO = NtaSex[sex][integer_age][TGO]; //EN TGO by age and sex
double sex_TFB = NtaSex[sex][integer_age][TFB]; //EN TFB by age and sex
double sex_TFW = NtaSex[sex][integer_age][TFW]; //EN TFW by age and sex
double sex_SF = NtaSex[sex][integer_age][SF]; //EN SF by age and sex
double sex_SG = NtaSex[sex][integer_age][SG]; //EN SG by age and sex
double sex_YL = NtaSex[sex][integer_age][YL]; //EN LY by age and sex
double sex_YAF = NtaSex[sex][integer_age][YAF]; //EN YAF by age and sex
double sex_YAG = NtaSex[sex][integer_age][YAG]; //EN YAG by age and sex
};
////////////////////////////////////////////////////////////////////////////////////////////////////
// Validation Tables
////////////////////////////////////////////////////////////////////////////////////////////////////
table_group TG_NTA_ValidationTables //EN NTA Validation Tables
{
TabNtaValidation_CFE, TabNtaValidation_CFH, TabNtaValidation_CFX, TabNtaValidation_CGE,
TabNtaValidation_CGH, TabNtaValidation_CGX, TabNtaValidation_TGSOAI, TabNtaValidation_TGXCI,
TabNtaValidation_TGXII, TabNtaValidation_TGEI, TabNtaValidation_TGHI, TabNtaValidation_TGO,
TabNtaValidation_TFB, TabNtaValidation_TFW, TabNtaValidation_SF, TabNtaValidation_SG,
TabNtaValidation_YL, TabNtaValidation_YAF, TabNtaValidation_YAG, TabNtaPopulation2010
};
table Person TabNtaValidation_CFE //EN CFE Validation
[calendar_year == MIN(SIM_YEAR_RANGE) && is_resident]
{
{
weighted_duration(base_CFE) / duration(), //EN CFE
weighted_duration(sex,FEMALE, base_CFE) / duration(sex,FEMALE), //EN CFE female
weighted_duration(sex,MALE, base_CFE) / duration(sex,MALE), //EN CFE male
weighted_duration(nta_educ,NE_LOW, base_CFE) / duration(nta_educ,NE_LOW), //EN CFE educ low
weighted_duration(nta_educ,NE_MEDIUM, base_CFE) / duration(nta_educ,NE_MEDIUM), //EN CFE educ medium
weighted_duration(nta_educ,NE_HIGH, base_CFE) / duration(nta_educ,NE_HIGH), //EN CFE educ high
weighted_duration(nta_educ,NE_NN, base_CFE) / duration(nta_educ,NE_NN), //EN CFE educ nn
weighted_duration(sex,FEMALE,sex_CFE) / duration(sex,FEMALE), //EN Agenta CFE female
weighted_duration(sex,MALE,sex_CFE) / duration(sex,MALE), //EN Agenta CFE male
weighted_duration(age_CFE) / duration() //EN Agenta CFE by age
}
* integer_age
};
table Person TabNtaValidation_CFH //EN CFH Validation
[calendar_year == MIN(SIM_YEAR_RANGE) && is_resident]
{
{
weighted_duration(base_CFH) / duration(), //EN CFH
weighted_duration(sex,FEMALE, base_CFH) / duration(sex,FEMALE), //EN CFH female
weighted_duration(sex,MALE, base_CFH) / duration(sex,MALE), //EN CFH male
weighted_duration(nta_educ,NE_LOW, base_CFH) / duration(nta_educ,NE_LOW), //EN CFE educ low
weighted_duration(nta_educ,NE_MEDIUM, base_CFH) / duration(nta_educ,NE_MEDIUM), //EN CFE educ medium
weighted_duration(nta_educ,NE_HIGH, base_CFH) / duration(nta_educ,NE_HIGH), //EN CFE educ high
weighted_duration(nta_educ,NE_NN, base_CFH) / duration(nta_educ,NE_NN), //EN CFE educ nn
weighted_duration(sex,FEMALE,sex_CFH) / duration(sex,FEMALE), //EN Agenta CFH female
weighted_duration(sex,MALE,sex_CFH) / duration(sex,MALE), //EN Agenta CFH male
weighted_duration(age_CFH) / duration() //EN Agenta CFH by age
}
* integer_age
};
table Person TabNtaValidation_CFX //EN CFX Validation
[calendar_year == MIN(SIM_YEAR_RANGE) && is_resident]
{
{
weighted_duration(base_CFX) / duration(), //EN CFX
weighted_duration(sex,FEMALE, base_CFX) / duration(sex,FEMALE), //EN CFX female
weighted_duration(sex,MALE, base_CFX) / duration(sex,MALE), //EN CFX male
weighted_duration(nta_educ,NE_LOW, base_CFX) / duration(nta_educ,NE_LOW), //EN CFE educ low
weighted_duration(nta_educ,NE_MEDIUM, base_CFX) / duration(nta_educ,NE_MEDIUM), //EN CFE educ medium
weighted_duration(nta_educ,NE_HIGH, base_CFX) / duration(nta_educ,NE_HIGH), //EN CFE educ high
weighted_duration(nta_educ,NE_NN, base_CFX) / duration(nta_educ,NE_NN), //EN CFE educ nn
weighted_duration(sex,FEMALE,sex_CFX) / duration(sex,FEMALE), //EN Agenta CFX female
weighted_duration(sex,MALE,sex_CFX) / duration(sex,MALE), //EN Agenta CFX male
weighted_duration(age_CFX) / duration() //EN Agenta CFX by age
}
* integer_age
};
table Person TabNtaValidation_CGE //EN CGE Validation
[calendar_year == MIN(SIM_YEAR_RANGE) && is_resident]
{
{
weighted_duration(base_CGE) / duration(), //EN CGE
weighted_duration(sex,FEMALE, base_CGE) / duration(sex,FEMALE), //EN CGE female
weighted_duration(sex,MALE, base_CGE) / duration(sex,MALE), //EN CGE male
weighted_duration(nta_educ,NE_LOW, base_CGE) / duration(nta_educ,NE_LOW), //EN CFE educ low
weighted_duration(nta_educ,NE_MEDIUM, base_CGE) / duration(nta_educ,NE_MEDIUM), //EN CFE educ medium
weighted_duration(nta_educ,NE_HIGH, base_CGE) / duration(nta_educ,NE_HIGH), //EN CFE educ high
weighted_duration(nta_educ,NE_NN, base_CGE) / duration(nta_educ,NE_NN), //EN CFE educ nn
weighted_duration(sex,FEMALE,sex_CGE) / duration(sex,FEMALE), //EN Agenta CGE female
weighted_duration(sex,MALE,sex_CGE) / duration(sex,MALE), //EN Agenta CGE male
weighted_duration(age_CGE) / duration() //EN Agenta CGE by age
}
* integer_age
};
table Person TabNtaValidation_CGH //EN CGH Validation
[calendar_year == MIN(SIM_YEAR_RANGE) && is_resident]
{
{
weighted_duration(base_CGH) / duration(), //EN CGH
weighted_duration(sex,FEMALE, base_CGH) / duration(sex,FEMALE), //EN CGH female
weighted_duration(sex,MALE, base_CGH) / duration(sex,MALE), //EN CGH male
weighted_duration(nta_educ,NE_LOW, base_CGH) / duration(nta_educ,NE_LOW), //EN CFE educ low
weighted_duration(nta_educ,NE_MEDIUM, base_CGH) / duration(nta_educ,NE_MEDIUM), //EN CFE educ medium
weighted_duration(nta_educ,NE_HIGH, base_CGH) / duration(nta_educ,NE_HIGH), //EN CFE educ high
weighted_duration(nta_educ,NE_NN, base_CGH) / duration(nta_educ,NE_NN), //EN CFE educ nn
weighted_duration(sex,FEMALE,sex_CGH) / duration(sex,FEMALE), //EN Agenta CGH female
weighted_duration(sex,MALE,sex_CGH) / duration(sex,MALE), //EN Agenta CGH male
weighted_duration(age_CGH) / duration() //EN Agenta CGH by age
}
* integer_age
};
table Person TabNtaValidation_CGX //EN CGX Validation
[calendar_year == MIN(SIM_YEAR_RANGE) && is_resident]
{
{
weighted_duration(base_CGX) / duration(), //EN CGX
weighted_duration(sex,FEMALE, base_CGX) / duration(sex,FEMALE), //EN CGX female
weighted_duration(sex,MALE, base_CGX) / duration(sex,MALE), //EN CGX male
weighted_duration(nta_educ,NE_LOW, base_CGX) / duration(nta_educ,NE_LOW), //EN CFE educ low
weighted_duration(nta_educ,NE_MEDIUM, base_CGX) / duration(nta_educ,NE_MEDIUM), //EN CFE educ medium
weighted_duration(nta_educ,NE_HIGH, base_CGX) / duration(nta_educ,NE_HIGH), //EN CFE educ high
weighted_duration(nta_educ,NE_NN, base_CGX) / duration(nta_educ,NE_NN), //EN CFE educ nn
weighted_duration(sex,FEMALE,sex_CGX) / duration(sex,FEMALE), //EN Agenta CGX female
weighted_duration(sex,MALE,sex_CGX) / duration(sex,MALE), //EN Agenta CGX male
weighted_duration(age_CGX) / duration() //EN Agenta CGX by age
}
* integer_age
};
table Person TabNtaValidation_TGSOAI //EN TGSOAI Validation
[calendar_year == MIN(SIM_YEAR_RANGE) && is_resident]
{
{
weighted_duration(base_TGSOAI) / duration(), //EN TGSOAI
weighted_duration(sex,FEMALE, base_TGSOAI) / duration(sex,FEMALE), //EN TGSOAI female
weighted_duration(sex,MALE, base_TGSOAI) / duration(sex,MALE), //EN TGSOAI male
weighted_duration(nta_educ,NE_LOW, base_TGSOAI) / duration(nta_educ,NE_LOW), //EN CFE educ low
weighted_duration(nta_educ,NE_MEDIUM, base_TGSOAI) / duration(nta_educ,NE_MEDIUM), //EN CFE educ medium
weighted_duration(nta_educ,NE_HIGH, base_TGSOAI) / duration(nta_educ,NE_HIGH), //EN CFE educ high
weighted_duration(nta_educ,NE_NN, base_TGSOAI) / duration(nta_educ,NE_NN), //EN CFE educ nn
weighted_duration(sex,FEMALE,sex_TGSOAI) / duration(sex,FEMALE), //EN Agenta TGSOAI female
weighted_duration(sex,MALE,sex_TGSOAI) / duration(sex,MALE), //EN Agenta TGSOAI male
weighted_duration(age_TGSOAI) / duration() //EN Agenta TGSOAI by age
}
* integer_age
};
table Person TabNtaValidation_TGXCI //EN TGXCI Validation
[calendar_year == MIN(SIM_YEAR_RANGE) && is_resident]
{
{
weighted_duration(base_TGXCI) / duration(), //EN TGXCI
weighted_duration(sex,FEMALE, base_TGXCI) / duration(sex,FEMALE), //EN TGXCI female
weighted_duration(sex,MALE, base_TGXCI) / duration(sex,MALE), //EN TGXCI male
weighted_duration(nta_educ,NE_LOW, base_TGXCI) / duration(nta_educ,NE_LOW), //EN CFE educ low
weighted_duration(nta_educ,NE_MEDIUM, base_TGXCI) / duration(nta_educ,NE_MEDIUM), //EN CFE educ medium
weighted_duration(nta_educ,NE_HIGH, base_TGXCI) / duration(nta_educ,NE_HIGH), //EN CFE educ high
weighted_duration(nta_educ,NE_NN, base_TGXCI) / duration(nta_educ,NE_NN), //EN CFE educ nn
weighted_duration(sex,FEMALE,sex_TGXCI) / duration(sex,FEMALE), //EN Agenta TGXCI female
weighted_duration(sex,MALE,sex_TGXCI) / duration(sex,MALE), //EN Agenta TGXCI male
weighted_duration(age_TGXCI) / duration() //EN Agenta TGXCI by age
}
* integer_age
};
table Person TabNtaValidation_TGXII //EN TGXII Validation
[calendar_year == MIN(SIM_YEAR_RANGE) && is_resident]
{
{
weighted_duration(base_TGXII) / duration(), //EN TGXII
weighted_duration(sex,FEMALE, base_TGXII) / duration(sex,FEMALE), //EN TGXII female
weighted_duration(sex,MALE, base_TGXII) / duration(sex,MALE), //EN TGXII male
weighted_duration(nta_educ,NE_LOW, base_TGXII) / duration(nta_educ,NE_LOW), //EN CFE educ low
weighted_duration(nta_educ,NE_MEDIUM, base_TGXII) / duration(nta_educ,NE_MEDIUM), //EN CFE educ medium
weighted_duration(nta_educ,NE_HIGH, base_TGXII) / duration(nta_educ,NE_HIGH), //EN CFE educ high
weighted_duration(nta_educ,NE_NN, base_TGXII) / duration(nta_educ,NE_NN), //EN CFE educ nn
weighted_duration(sex,FEMALE,sex_TGXII) / duration(sex,FEMALE), //EN Agenta TGXII female
weighted_duration(sex,MALE,sex_TGXII) / duration(sex,MALE), //EN Agenta TGXII male
weighted_duration(age_TGXII) / duration() //EN Agenta TGXII by age
}
* integer_age
};
table Person TabNtaValidation_TGEI //EN TGEI Validation
[calendar_year == MIN(SIM_YEAR_RANGE) && is_resident]
{
{
weighted_duration(base_TGEI) / duration(), //EN TGEI
weighted_duration(sex,FEMALE, base_TGEI) / duration(sex,FEMALE), //EN TGEI female
weighted_duration(sex,MALE, base_TGEI) / duration(sex,MALE), //EN TGEI male
weighted_duration(nta_educ,NE_LOW, base_TGEI) / duration(nta_educ,NE_LOW), //EN CFE educ low
weighted_duration(nta_educ,NE_MEDIUM, base_TGEI) / duration(nta_educ,NE_MEDIUM), //EN CFE educ medium
weighted_duration(nta_educ,NE_HIGH, base_TGEI) / duration(nta_educ,NE_HIGH), //EN CFE educ high
weighted_duration(nta_educ,NE_NN, base_TGEI) / duration(nta_educ,NE_NN), //EN CFE educ nn
weighted_duration(sex,FEMALE,sex_TGEI) / duration(sex,FEMALE), //EN Agenta TGEI female
weighted_duration(sex,MALE,sex_TGEI) / duration(sex,MALE), //EN Agenta TGEI male
weighted_duration(age_TGEI) / duration() //EN Agenta TGEI by age
}
* integer_age
};
table Person TabNtaValidation_TGHI //EN TGHI Validation
[calendar_year == MIN(SIM_YEAR_RANGE) && is_resident]
{
{
weighted_duration(base_TGHI) / duration(), //EN TGHI
weighted_duration(sex,FEMALE, base_TGHI) / duration(sex,FEMALE), //EN TGHI female
weighted_duration(sex,MALE, base_TGHI) / duration(sex,MALE), //EN TGHI male
weighted_duration(nta_educ,NE_LOW, base_TGHI) / duration(nta_educ,NE_LOW), //EN CFE educ low
weighted_duration(nta_educ,NE_MEDIUM, base_TGHI) / duration(nta_educ,NE_MEDIUM), //EN CFE educ medium
weighted_duration(nta_educ,NE_HIGH, base_TGHI) / duration(nta_educ,NE_HIGH), //EN CFE educ high
weighted_duration(nta_educ,NE_NN, base_TGHI) / duration(nta_educ,NE_NN), //EN CFE educ nn
weighted_duration(sex,FEMALE,sex_TGHI) / duration(sex,FEMALE), //EN Agenta TGHI female
weighted_duration(sex,MALE,sex_TGHI) / duration(sex,MALE), //EN Agenta TGHI male
weighted_duration(age_TGHI) / duration() //EN Agenta TGHI by age
}
* integer_age
};
table Person TabNtaValidation_TGO //EN TGO Validation
[calendar_year == MIN(SIM_YEAR_RANGE) && is_resident]
{
{
weighted_duration(base_TGO) / duration(), //EN TGO
weighted_duration(sex,FEMALE, base_TGO) / duration(sex,FEMALE), //EN TGO female
weighted_duration(sex,MALE, base_TGO) / duration(sex,MALE), //EN TGO male
weighted_duration(nta_educ,NE_LOW, base_TGO) / duration(nta_educ,NE_LOW), //EN CFE educ low
weighted_duration(nta_educ,NE_MEDIUM, base_TGO) / duration(nta_educ,NE_MEDIUM), //EN CFE educ medium
weighted_duration(nta_educ,NE_HIGH, base_TGO) / duration(nta_educ,NE_HIGH), //EN CFE educ high
weighted_duration(nta_educ,NE_NN, base_TGO) / duration(nta_educ,NE_NN), //EN CFE educ nn
weighted_duration(sex,FEMALE,sex_TGO) / duration(sex,FEMALE), //EN Agenta TGO female
weighted_duration(sex,MALE,sex_TGO) / duration(sex,MALE), //EN Agenta TGO male
weighted_duration(age_TGO) / duration() //EN Agenta TGO by age
}
* integer_age
};
table Person TabNtaValidation_TFB //EN TFB Validation
[calendar_year == MIN(SIM_YEAR_RANGE) && is_resident]
{
{
weighted_duration(base_TFB) / duration(), //EN TFB
weighted_duration(sex,FEMALE, base_TFB) / duration(sex,FEMALE), //EN TFB female
weighted_duration(sex,MALE, base_TFB) / duration(sex,MALE), //EN TFB male
weighted_duration(nta_educ,NE_LOW, base_TFB) / duration(nta_educ,NE_LOW), //EN CFE educ low
weighted_duration(nta_educ,NE_MEDIUM, base_TFB) / duration(nta_educ,NE_MEDIUM), //EN CFE educ medium
weighted_duration(nta_educ,NE_HIGH, base_TFB) / duration(nta_educ,NE_HIGH), //EN CFE educ high
weighted_duration(nta_educ,NE_NN, base_TFB) / duration(nta_educ,NE_NN), //EN CFE educ nn
weighted_duration(sex,FEMALE,sex_TFB) / duration(sex,FEMALE), //EN Agenta TFB female
weighted_duration(sex,MALE,sex_TFB) / duration(sex,MALE), //EN Agenta TFB male
weighted_duration(age_TFB) / duration() //EN Agenta TFB by age
}
* integer_age
};
table Person TabNtaValidation_TFW //EN TFW Validation
[calendar_year == MIN(SIM_YEAR_RANGE) && is_resident]
{
{
weighted_duration(base_TFW) / duration(), //EN TFW
weighted_duration(sex,FEMALE, base_TFW) / duration(sex,FEMALE), //EN TFW female
weighted_duration(sex,MALE, base_TFW) / duration(sex,MALE), //EN TFW male
weighted_duration(nta_educ,NE_LOW, base_TFW) / duration(nta_educ,NE_LOW), //EN CFE educ low
weighted_duration(nta_educ,NE_MEDIUM, base_TFW) / duration(nta_educ,NE_MEDIUM), //EN CFE educ medium
weighted_duration(nta_educ,NE_HIGH, base_TFW) / duration(nta_educ,NE_HIGH), //EN CFE educ high
weighted_duration(nta_educ,NE_NN, base_TFW) / duration(nta_educ,NE_NN), //EN CFE educ nn
weighted_duration(sex,FEMALE,sex_TFW) / duration(sex,FEMALE), //EN Agenta TFW female
weighted_duration(sex,MALE,sex_TFW) / duration(sex,MALE), //EN Agenta TFW male
weighted_duration(age_TFW) / duration() //EN Agenta TFW by age
}
* integer_age
};
table Person TabNtaValidation_SF //EN SF Validation
[calendar_year == MIN(SIM_YEAR_RANGE) && is_resident]
{
{
weighted_duration(base_SF) / duration(), //EN SF
weighted_duration(sex,FEMALE, base_SF) / duration(sex,FEMALE), //EN SF female
weighted_duration(sex,MALE, base_SF) / duration(sex,MALE), //EN SF male
weighted_duration(nta_educ,NE_LOW, base_SF) / duration(nta_educ,NE_LOW), //EN CFE educ low
weighted_duration(nta_educ,NE_MEDIUM, base_SF) / duration(nta_educ,NE_MEDIUM), //EN CFE educ medium
weighted_duration(nta_educ,NE_HIGH, base_SF) / duration(nta_educ,NE_HIGH), //EN CFE educ high
weighted_duration(nta_educ,NE_NN, base_SF) / duration(nta_educ,NE_NN), //EN CFE educ nn
weighted_duration(sex,FEMALE,sex_SF) / duration(sex,FEMALE), //EN Agenta SF female
weighted_duration(sex,MALE,sex_SF) / duration(sex,MALE), //EN Agenta SF male
weighted_duration(age_SF) / duration() //EN Agenta SF by age
}
* integer_age
};
table Person TabNtaValidation_SG //EN SG Validation
[calendar_year == MIN(SIM_YEAR_RANGE) && is_resident]
{
{
weighted_duration(base_SG) / duration(), //EN SG
weighted_duration(sex,FEMALE, base_SG) / duration(sex,FEMALE), //EN SG female
weighted_duration(sex,MALE, base_SG) / duration(sex,MALE), //EN SG male
weighted_duration(nta_educ,NE_LOW, base_SG) / duration(nta_educ,NE_LOW), //EN CFE educ low
weighted_duration(nta_educ,NE_MEDIUM, base_SG) / duration(nta_educ,NE_MEDIUM), //EN CFE educ medium
weighted_duration(nta_educ,NE_HIGH, base_SG) / duration(nta_educ,NE_HIGH), //EN CFE educ high
weighted_duration(nta_educ,NE_NN, base_SG) / duration(nta_educ,NE_NN), //EN CFE educ nn
weighted_duration(sex,FEMALE,sex_SG) / duration(sex,FEMALE), //EN Agenta SG female
weighted_duration(sex,MALE,sex_SG) / duration(sex,MALE), //EN Agenta SG male
weighted_duration(age_SG) / duration() //EN Agenta SG by age
}
* integer_age
};
table Person TabNtaValidation_YL //EN YL Validation
[calendar_year == MIN(SIM_YEAR_RANGE) && is_resident]
{
{
weighted_duration(base_YL) / duration(), //EN YL
weighted_duration(sex,FEMALE, base_YL) / duration(sex,FEMALE), //EN YL female
weighted_duration(sex,MALE, base_YL) / duration(sex,MALE), //EN YL male
weighted_duration(nta_educ,NE_LOW, base_YL) / duration(nta_educ,NE_LOW), //EN CFE educ low
weighted_duration(nta_educ,NE_MEDIUM, base_YL) / duration(nta_educ,NE_MEDIUM), //EN CFE educ medium
weighted_duration(nta_educ,NE_HIGH, base_YL) / duration(nta_educ,NE_HIGH), //EN CFE educ high
weighted_duration(nta_educ,NE_NN, base_YL) / duration(nta_educ,NE_NN), //EN CFE educ nn
weighted_duration(sex,FEMALE,sex_YL) / duration(sex,FEMALE), //EN Agenta YL female
weighted_duration(sex,MALE,sex_YL) / duration(sex,MALE), //EN Agenta YL male
weighted_duration(age_YL) / duration() //EN Agenta YL by age
}
* integer_age
};
table Person TabNtaValidation_YAF //EN YAF Validation
[calendar_year == MIN(SIM_YEAR_RANGE) && is_resident]
{
{
weighted_duration(base_YAF) / duration(), //EN YAF
weighted_duration(sex,FEMALE, base_YAF) / duration(sex,FEMALE), //EN YAF female
weighted_duration(sex,MALE, base_YAF) / duration(sex,MALE), //EN YAF male
weighted_duration(nta_educ,NE_LOW, base_YAF) / duration(nta_educ,NE_LOW), //EN CFE educ low
weighted_duration(nta_educ,NE_MEDIUM, base_YAF) / duration(nta_educ,NE_MEDIUM), //EN CFE educ medium
weighted_duration(nta_educ,NE_HIGH, base_YAF) / duration(nta_educ,NE_HIGH), //EN CFE educ high
weighted_duration(nta_educ,NE_NN, base_YAF) / duration(nta_educ,NE_NN), //EN CFE educ nn
weighted_duration(sex,FEMALE,sex_YAF) / duration(sex,FEMALE), //EN Agenta YAF female
weighted_duration(sex,MALE,sex_YAF) / duration(sex,MALE), //EN Agenta YAF male
weighted_duration(age_YAF) / duration() //EN Agenta YAF by age
}
* integer_age
};
table Person TabNtaValidation_YAG //EN YAG Validation
[calendar_year == MIN(SIM_YEAR_RANGE) && is_resident]
{
{
weighted_duration(base_YAG) / duration(), //EN YAG
weighted_duration(sex,FEMALE, base_YAG) / duration(sex,FEMALE), //EN YAG female
weighted_duration(sex,MALE, base_YAG) / duration(sex,MALE), //EN YAG male
weighted_duration(nta_educ,NE_LOW, base_YAG) / duration(nta_educ,NE_LOW), //EN CFE educ low
weighted_duration(nta_educ,NE_MEDIUM, base_YAG) / duration(nta_educ,NE_MEDIUM), //EN CFE educ medium
weighted_duration(nta_educ,NE_HIGH, base_YAG) / duration(nta_educ,NE_HIGH), //EN CFE educ high
weighted_duration(nta_educ,NE_NN, base_YAG) / duration(nta_educ,NE_NN), //EN CFE educ nn
weighted_duration(sex,FEMALE,sex_YAG) / duration(sex,FEMALE), //EN Agenta YAG female
weighted_duration(sex,MALE,sex_YAG) / duration(sex,MALE), //EN Agenta YAG male
weighted_duration(age_YAG) / duration() //EN Agenta YAG by age
}
* integer_age
};
table Person TabNtaPopulation2010 //EN NTA POPULATION
[calendar_year == MIN(SIM_YEAR_RANGE) && is_resident]
{
nta_pop_group *
tab_fam_index + *
sex + *
{
duration() //EN Duration
}
* integer_age
* nta_educ +
};
table Person TabTestRole //EN ROLE OF STUDENTS
[in_school && is_resident && integer_age>=17 && integer_age <=25]
{
{
duration() //EN Duration
}
* calendar_year
* family_role+
};
table Person TabRoleTest2 //EN Role test 2
[calendar_year == MIN(SIM_YEAR_RANGE) && is_resident && nta_pop_group==NPG_YOUNG_STUDENT]
{
{
duration() //EN Duration
}
* nta_educ+
};
table Person TabRoleTest3 //EN Role test 3
[calendar_year == MIN(SIM_YEAR_RANGE) && is_resident && integer_age>=17 && integer_age <=25]
{
in_school+ *
in_school_startpop+ *
{
duration() //EN Duration
}
* nta_educ+
};
table Person TabRoleTest4 //EN Role test 4
[calendar_year == MIN(SIM_YEAR_RANGE) && is_resident && integer_age>=17 && integer_age <=25]
{
in_school_startpop+ *
{
duration() //EN Duration
}
* in_school+
* family_role+
};
table Person TabRoleTest6 //EN Role test 6
[calendar_year == MIN(SIM_YEAR_RANGE) && is_resident && integer_age>=18 && integer_age <=25 && person_type==PT_START]
{
{
duration() //EN Duration
}
* in_school_startpop+
* in_school+
};
TablesNtaVisualisation.mpp
This module implements table output as base of the online NTA visualisation tool. The module is optional and can be removed from the application. Table output is produced for two points in time - the starting year and the calendar year 50 years later. Currently, the output for the future is a dummy output only.
////////////////////////////////////////////////////////////////////////////////////////////////////
// Dimensions
////////////////////////////////////////////////////////////////////////////////////////////////////
classification NTA_VISULAL_YEAR //EN Year
{
NVY_BASE, //EN Base year
NVY_BASEP50 //EN Projected year
};
////////////////////////////////////////////////////////////////////////////////////////////////////
// States used in the table output
////////////////////////////////////////////////////////////////////////////////////////////////////
actor Person
{
double current_CFE = (calendar_year == MIN(SIM_YEAR_RANGE)) ? base_CFE : base_CFE * 1.5; //EN Private Consumption Education (CFE)
double current_CFH = (calendar_year == MIN(SIM_YEAR_RANGE)) ? base_CFH : base_CFH * 1.5; //EN Private Consumption Health (CFH)
double current_CFX = (calendar_year == MIN(SIM_YEAR_RANGE)) ? base_CFX : base_CFX * 1.5; //EN Private Consumption other than Education and Health (CFX)
double current_CGE = (calendar_year == MIN(SIM_YEAR_RANGE)) ? base_CGE : base_CGE * 1.5; //EN Public Consumption Education (CGE)
double current_CGH = (calendar_year == MIN(SIM_YEAR_RANGE)) ? base_CGH : base_CGH * 1.5; //EN Public Consumption Health (CGH)
double current_CGX = (calendar_year == MIN(SIM_YEAR_RANGE)) ? base_CGX : base_CGX * 1.5; //EN Public Consumption other than Education and Health (CGX)
double current_TGSOAI = (calendar_year == MIN(SIM_YEAR_RANGE)) ? base_TGSOAI : base_TGSOAI * 1.5; //EN Public Transfers Pensions, Inflows (TGSOAI)
double current_TGXCI = (calendar_year == MIN(SIM_YEAR_RANGE)) ? base_TGXCI : base_TGXCI * 1.5; //EN Public Transfers Other Cash Inflows (TGXCI)
double current_TGXII = (calendar_year == MIN(SIM_YEAR_RANGE)) ? base_TGXII : base_TGXII * 1.5; //EN Public Transfers Other In-Kind Inflows (TGXII)
double current_TGEI = (calendar_year == MIN(SIM_YEAR_RANGE)) ? base_TGEI : base_TGEI * 1.5; //EN Public Transfers Education Inflows (TGEI)
double current_TGHI = (calendar_year == MIN(SIM_YEAR_RANGE)) ? base_TGHI : base_TGHI * 1.5; //EN Public Transfers Health Inflows (TGHI)
double current_TGO = (calendar_year == MIN(SIM_YEAR_RANGE)) ? base_TGO : base_TGO * 1.5; //EN Public Transfers Outflows (TGO)
double current_TFB = (calendar_year == MIN(SIM_YEAR_RANGE)) ? base_TFB : base_TFB * 1.5; //EN Net Interhousehold Transfers (TFB)
double current_TFW = (calendar_year == MIN(SIM_YEAR_RANGE)) ? base_TFW : base_TFW * 1.5;//EN Net Intrahousehold Transfers (TFW)
double current_SF = (calendar_year == MIN(SIM_YEAR_RANGE)) ? base_SF : base_SF * 1.5; //EN Private Saving (SF)
double current_SG = (calendar_year == MIN(SIM_YEAR_RANGE)) ? base_SG : base_SG * 1.5; //EN Public Saving (SG)
double current_YL = (calendar_year == MIN(SIM_YEAR_RANGE)) ? base_YL : base_YL * 1.5; //EN Labor Income (LY)
double current_YAF = (calendar_year == MIN(SIM_YEAR_RANGE)) ? base_YAF : base_YAF * 1.5; //EN Private Asset Income (YAF)
double current_YAG = (calendar_year == MIN(SIM_YEAR_RANGE)) ? base_YAG : base_YAG * 1.5; //EN Public Asset Income (YAG)
logical in_nta_visualisation_year = (calendar_year == MIN(SIM_YEAR_RANGE) || calendar_year == MIN(SIM_YEAR_RANGE) + 50);
NTA_VISULAL_YEAR nta_visual_year = (calendar_year == MIN(SIM_YEAR_RANGE)) ? NVY_BASE : NVY_BASEP50; //EN Year
};
////////////////////////////////////////////////////////////////////////////////////////////////////
// Table Groups
////////////////////////////////////////////////////////////////////////////////////////////////////
table_group TG_NTA_VISAL //EN NTA Visualisations
{
tabNtaVisualisationChild,
tabNtaVisualisationStudent,
tabNtaVisualisationYoung,
tabNtaVisualisationAdults,
tabNtaVisualisationOld
};
////////////////////////////////////////////////////////////////////////////////////////////////////
// Tables
////////////////////////////////////////////////////////////////////////////////////////////////////
table Person tabNtaVisualisationChild //EN NTA Visualisation Children
[ in_nta_visualisation_year && nta_pop_group == NPG_CHILD && is_resident]
{
nta_visual_year*
{
duration(), //EN Population
weighted_duration(current_CFE) / duration(), //EN Private Consumption Education (CFE)
weighted_duration(current_CFH) / duration(), //EN Private Consumption Health (CFH)
weighted_duration(current_CFX) / duration(), //EN Private Consumption other than Education and Health (CFX)
weighted_duration(current_CGE) / duration(), //EN Public Consumption Education (CGE)
weighted_duration(current_CGH) / duration(), //EN Public Consumption Health (CGH)
weighted_duration(current_CGX) / duration(), //EN Public Consumption other than Education and Health (CGX)
weighted_duration(current_TGSOAI) / duration(), //EN Public Transfers Pensions, Inflows (TGSOAI)
weighted_duration(current_TGXCI) / duration(), //EN Public Transfers Other Cash Inflows (TGXCI)
weighted_duration(current_TGXII) / duration(), //EN Public Transfers Other In-Kind Inflows (TGXII)
weighted_duration(current_TGEI) / duration(), //EN Public Transfers Education Inflows (TGEI)
weighted_duration(current_TGHI) / duration(), //EN Public Transfers Health Inflows (TGHI)
weighted_duration(current_TGO) / duration(), //EN Public Transfers Outflows (TGO)
weighted_duration(current_TFB) / duration(), //EN Net Interhousehold Transfers (TFB)
weighted_duration(current_TFW) / duration(), //EN Net Intrahousehold Transfers (TFW)
weighted_duration(current_SF) / duration(), //EN Private Saving (SF)
weighted_duration(current_SG) / duration(), //EN Public Saving (SG)
weighted_duration(current_YL) / duration(), //EN Labor Income (LY)
weighted_duration(current_YAF) / duration(), //EN Private Asset Income (YAF)
weighted_duration(current_YAG) / duration() //EN Public Asset Income (YAG)
}
*educ_parents +
};
table Person tabNtaVisualisationStudent //EN NTA Visualisation Young Students
[ in_nta_visualisation_year && nta_pop_group == NPG_YOUNG_STUDENT && is_resident]
{
nta_visual_year*
{
duration(), //EN Population
duration(sex,FEMALE) / duration(), //EN Proportion Female
weighted_duration(current_CFE) / duration(), //EN Private Consumption Education (CFE)
weighted_duration(current_CFH) / duration(), //EN Private Consumption Health (CFH)
weighted_duration(current_CFX) / duration(), //EN Private Consumption other than Education and Health (CFX)
weighted_duration(current_CGE) / duration(), //EN Public Consumption Education (CGE)
weighted_duration(current_CGH) / duration(), //EN Public Consumption Health (CGH)
weighted_duration(current_CGX) / duration(), //EN Public Consumption other than Education and Health (CGX)
weighted_duration(current_TGSOAI) / duration(), //EN Public Transfers Pensions, Inflows (TGSOAI)
weighted_duration(current_TGXCI) / duration(), //EN Public Transfers Other Cash Inflows (TGXCI)
weighted_duration(current_TGXII) / duration(), //EN Public Transfers Other In-Kind Inflows (TGXII)
weighted_duration(current_TGEI) / duration(), //EN Public Transfers Education Inflows (TGEI)
weighted_duration(current_TGHI) / duration(), //EN Public Transfers Health Inflows (TGHI)
weighted_duration(current_TGO) / duration(), //EN Public Transfers Outflows (TGO)
weighted_duration(current_TFB) / duration(), //EN Net Interhousehold Transfers (TFB)
weighted_duration(current_TFW) / duration(), //EN Net Intrahousehold Transfers (TFW)
weighted_duration(current_SF) / duration(), //EN Private Saving (SF)
weighted_duration(current_SG) / duration(), //EN Public Saving (SG)
weighted_duration(current_YL) / duration(), //EN Labor Income (LY)
weighted_duration(current_YAF) / duration(), //EN Private Asset Income (YAF)
weighted_duration(current_YAG) / duration() //EN Public Asset Income (YAG)
}
* nta_educ +
};
table Person tabNtaVisualisationYoung //EN NTA Visualisation Young Workers
[ in_nta_visualisation_year && nta_pop_group == NPG_YOUNG_WORK && is_resident]
{
nta_visual_year*
sex+ *
nta_family_active+ *
{
duration(), //EN Population
weighted_duration(current_CFE) / duration(), //EN Private Consumption Education (CFE)
weighted_duration(current_CFH) / duration(), //EN Private Consumption Health (CFH)
weighted_duration(current_CFX) / duration(), //EN Private Consumption other than Education and Health (CFX)
weighted_duration(current_CGE) / duration(), //EN Public Consumption Education (CGE)
weighted_duration(current_CGH) / duration(), //EN Public Consumption Health (CGH)
weighted_duration(current_CGX) / duration(), //EN Public Consumption other than Education and Health (CGX)
weighted_duration(current_TGSOAI) / duration(), //EN Public Transfers Pensions, Inflows (TGSOAI)
weighted_duration(current_TGXCI) / duration(), //EN Public Transfers Other Cash Inflows (TGXCI)
weighted_duration(current_TGXII) / duration(), //EN Public Transfers Other In-Kind Inflows (TGXII)
weighted_duration(current_TGEI) / duration(), //EN Public Transfers Education Inflows (TGEI)
weighted_duration(current_TGHI) / duration(), //EN Public Transfers Health Inflows (TGHI)
weighted_duration(current_TGO) / duration(), //EN Public Transfers Outflows (TGO)
weighted_duration(current_TFB) / duration(), //EN Net Interhousehold Transfers (TFB)
weighted_duration(current_TFW) / duration(), //EN Net Intrahousehold Transfers (TFW)
weighted_duration(current_SF) / duration(), //EN Private Saving (SF)
weighted_duration(current_SG) / duration(), //EN Public Saving (SG)
weighted_duration(current_YL) / duration(), //EN Labor Income (LY)
weighted_duration(current_YAF) / duration(), //EN Private Asset Income (YAF)
weighted_duration(current_YAG) / duration() //EN Public Asset Income (YAG)
}
* educ_fate +
};
table Person tabNtaVisualisationAdults //EN NTA Visualisation Adult Workers
[ in_nta_visualisation_year && nta_pop_group == NPG_ADULT_WORK && is_resident]
{
nta_visual_year*
sex+ *
nta_family_active+ *
{
duration(), //EN Population
weighted_duration(current_CFE) / duration(), //EN Private Consumption Education (CFE)
weighted_duration(current_CFH) / duration(), //EN Private Consumption Health (CFH)
weighted_duration(current_CFX) / duration(), //EN Private Consumption other than Education and Health (CFX)
weighted_duration(current_CGE) / duration(), //EN Public Consumption Education (CGE)
weighted_duration(current_CGH) / duration(), //EN Public Consumption Health (CGH)
weighted_duration(current_CGX) / duration(), //EN Public Consumption other than Education and Health (CGX)
weighted_duration(current_TGSOAI) / duration(), //EN Public Transfers Pensions, Inflows (TGSOAI)
weighted_duration(current_TGXCI) / duration(), //EN Public Transfers Other Cash Inflows (TGXCI)
weighted_duration(current_TGXII) / duration(), //EN Public Transfers Other In-Kind Inflows (TGXII)
weighted_duration(current_TGEI) / duration(), //EN Public Transfers Education Inflows (TGEI)
weighted_duration(current_TGHI) / duration(), //EN Public Transfers Health Inflows (TGHI)
weighted_duration(current_TGO) / duration(), //EN Public Transfers Outflows (TGO)
weighted_duration(current_TFB) / duration(), //EN Net Interhousehold Transfers (TFB)
weighted_duration(current_TFW) / duration(), //EN Net Intrahousehold Transfers (TFW)
weighted_duration(current_SF) / duration(), //EN Private Saving (SF)
weighted_duration(current_SG) / duration(), //EN Public Saving (SG)
weighted_duration(current_YL) / duration(), //EN Labor Income (LY)
weighted_duration(current_YAF) / duration(), //EN Private Asset Income (YAF)
weighted_duration(current_YAG) / duration() //EN Public Asset Income (YAG)
}
* educ_fate +
};
table Person tabNtaVisualisationOld //EN NTA Visualisation Old
[ in_nta_visualisation_year && nta_pop_group == NPG_OLD && is_resident]
{
nta_visual_year*
sex+ *
nta_family_old+ *
{
duration(), //EN Population
weighted_duration(current_CFE) / duration(), //EN Private Consumption Education (CFE)
weighted_duration(current_CFH) / duration(), //EN Private Consumption Health (CFH)
weighted_duration(current_CFX) / duration(), //EN Private Consumption other than Education and Health (CFX)
weighted_duration(current_CGE) / duration(), //EN Public Consumption Education (CGE)
weighted_duration(current_CGH) / duration(), //EN Public Consumption Health (CGH)
weighted_duration(current_CGX) / duration(), //EN Public Consumption other than Education and Health (CGX)
weighted_duration(current_TGSOAI) / duration(), //EN Public Transfers Pensions, Inflows (TGSOAI)
weighted_duration(current_TGXCI) / duration(), //EN Public Transfers Other Cash Inflows (TGXCI)
weighted_duration(current_TGXII) / duration(), //EN Public Transfers Other In-Kind Inflows (TGXII)
weighted_duration(current_TGEI) / duration(), //EN Public Transfers Education Inflows (TGEI)
weighted_duration(current_TGHI) / duration(), //EN Public Transfers Health Inflows (TGHI)
weighted_duration(current_TGO) / duration(), //EN Public Transfers Outflows (TGO)
weighted_duration(current_TFB) / duration(), //EN Net Interhousehold Transfers (TFB)
weighted_duration(current_TFW) / duration(), //EN Net Intrahousehold Transfers (TFW)
weighted_duration(current_SF) / duration(), //EN Private Saving (SF)
weighted_duration(current_SG) / duration(), //EN Public Saving (SG)
weighted_duration(current_YL) / duration(), //EN Labor Income (LY)
weighted_duration(current_YAF) / duration(), //EN Private Asset Income (YAF)
weighted_duration(current_YAG) / duration() //EN Public Asset Income (YAG)
}
* educ_fate +
};