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