Step 16: Immigration
Overview
At this step we add an immigration module. Immigration is optional and can be deactivated. Immigrant children try to find an appropriate mother destined to immigrate in the same calendar year. Before entering the country, immigrants are excluded from many processes like education, fertility or partnership formation. Missing characteristics are cloned from a randomly chosen resident of the same age and sex at the moment of entry.
The new Immigration.mpp Module
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. While the immigration module is very simple concerning scenario creation allowing to easily reproduce macro projections, it requires adaptation of code in various other modules:
Most tables and actor-sets throughout the model were adapted to only include the resident population (is_resident)
Various processes, e.g. education, require additional filters to only include residents.
The Start() function has to be adapted to take the time of immigration as additional parameter - and to initialize states of immigrants.
The SetAliveEvent() calls a function for children to identify and link to a mother immigrating in the same year
In the Simulation() function, immigrants have to be created
Various characteristis 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 look like residents concerning the distributions of most characteristics.
////////////////////////////////////////////////////////////////////////////////////////////////////
// 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
{
logical ModelImmigration; //EN Switch immigration on/off
long ImmigrationTotal[SIM_YEAR_RANGE]; //EN Total number of immigrants
cumrate [2] ImmigrationAgeSexAll[SEX][AGE_RANGE]; //EN Age-Sex distribution of immigrants
cumrate [1] AgeOfImmigrantMother[FERTILE_AGE_RANGE]; //EN Age distribution of mothers at birth
};
parameter_group PG_ImmigrationBase //EN Immigration Base Version
{
ModelImmigration, 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;
}
////////////////////////////////////////////////////////////////////////////////////////////////////
// Tables
////////////////////////////////////////////////////////////////////////////////////////////////////
table Person tabNumberImmigrants //EN Number of Immigrants
[is_alive && in_projected_time]
{
sex + *
{
transitions(ever_resident,FALSE,TRUE) //EN Number Immigrants
}
* integer_age+
* sim_year
};
table Person tabRoleValidTab //EN Validation Family Role by Age
[is_resident && in_projected_time && is_resident]
{
person_type+ *
{
duration(family_role,FR_CHILD) / duration(), //EN Child decimals=4
duration(family_role,FR_SPOUSE) / duration(), //EN Spouse decimals=4
duration(family_role,FR_HEAD) / duration() //EN Head decimals=4
}
* integer_age
* sim_year
};
Changes in the Simulation() function
// Create Immigrants
if (ModelImmigration)
{
for (int nYear = MIN(SIM_YEAR_RANGE); nYear < int(SIMULATION_END()); nYear++)
{
int nCohortSize = int(ImmigrationTotal[RANGE_POS(SIM_YEAR_RANGE, nYear)] / (asGlobals->Item(0)->person_weight));
double dCohortSize = ImmigrationTotal[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);
}
}
}
Changes in the PersonCore.mpp module
Changes affect the Start() function and the SetAliveEvent()
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_ImmigrationAgeSexAll(RandUniform(41),&nSex, &nAge);
sex = (SEX)nSex;
time = dTimeOfImmigration - nAge - RandUniform(44);
// (B) Other states
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;
}