/*============================================================================* Model for Tuberculosis in the UK This individual-based model (IBM) is to simulate tuberculosis dynamics in the UK. This version of the model stores individual strain types for each infection to simulate strain type clustering patterns seen in disease cases. This version was developed for use in the West Midlands, a region with a population size of around five million people. One major characteristic of individuals is their region of birth, UK or non-UK. In the 'SSAV' version of the model, the non-UK-born region of birth is divided into Sub-Saharan African born (SSA-born) and other non-UK born (ONUK-born) for some model parameters. 1. BACKGROUND ALGORITHMS. The core IBM algorithms, external to this simulation program, were written by Clarence Lehman (CL) in 2009. The method was first developed for simulation of HIV dynamics in the US. Beginning in January 2010, and with initial help from CL, Adrienne Keen (AK) adapted this IBM skeleton for modelling tuberculosis dynamics in the UK, first for fitting the model to tuberculosis notifications in England and Wales and then for simulating genotyping data from the West Midlands. 2. SUMMARY OF METHOD. This is an event-based simulation where continuous time is simulated directly. There is no arbitrary time step. Instead, events are processed one at a time, chronologically. The time variable 't' is time in years, with arbitrarily high resolution down to small fractions of a instant and all complexities and inaccuracies associated with multiple events during a finite time step vanish---such as undershooting zero when the sum of the rates times the width of the time step exceeds unity. Continuous time also allows all activities to occur in a single data array, rather than having to swap old and new arrays at each time step. Events are assumed to following probability distributions that vary through time and space. States of the system never change spontaneously---all changes are induced by some other event in the system and usually scheduled in advance. The scheduled times are determined stochastically from functions whose characteristics may depend on the state of the individual and the environment at the time. An individual's age, sex, infection history, or any other considerations can be incorporated into the functions. For example, death is scheduled at the time of birth, with the time chosen randomly from a life-span distribution for babies born in the simulated year. But the scheduled time of death is not immutable, nor are any other scheduled times in the system. If the individual develops disease, the scheduled time of death may be cancelled and a new time of death due to tuberculosis may be scheduled instead. At no time does the program visit an individual when it does not need to, and therein lies its speed. Many future events may apply to each individual and are saved for that individual, but only the earliest among each individual's events enters a global "list of future events." 3. MAIN DATA STRUCTURES. Each individual is assigned a number 1 through 'n' and recorded in a linear array 'A' of structures 'Indiv'. Each element of 'A' defines the state of the correspondng individual. For example, this would be defined as 'struct A[indiv+3]', the main array of individuals. Suppose there are 3 UK-born and 3 non-UK-born individuals, with a total maximum population size of 14 ('indiv'). Note, in the 'SSAV' version of the model, SSAs are stored as non-UK born and it is not possible to tell from their ID number alone whether they are SSA-born or ONUK-born. n A[n] A array index __________________________________________________ 0 [Reserved] (Null pointer for list) 1 (Non-UK-born) 2 (Non-UK-born) 3 (Non-UK-born) immid-1 4 [Empty] immid 5 [Empty] 6 [Empty] 7 [Empty] maximm -------------------(Imaginary separator, non-UK/UK born) 8 (UK-born) maximm+1 9 (UK-born) 10 (UK-born) ukbid-1 11 [Empty] ukbid 12 [Empty] 13 [Empty] 14 [Empty] indiv 15 External event, birth indiv+1 or BIRTH 16 External event, immigration indiv+2 or IMM __________________________________________________ 4. MODEL FITTING. Prior versions of the model were designed to work with an optimization algorithm for model fitting, currently found in 'fit5.c'. In this version of the model, the fitting routine is not needed. To run this program stand-alone, as opposed to inside the fitting routine, simply comment out the '#define main mainiac' and everything else will be handled automatically. Below is a sample program call when it is running as an individual executable, not linked with the fitting routine. A different syntax is used for the call inside 'fit5i.c'. tb36gen df=2.5 d1uk20=0.10 d2uk20=0.0003 d3uk20=0.05 When linked with the fitting routine, the model is called from the fitting routine, not as an independent executable, so that it is compatible with parallel runs using MPI commands. In this set up, the model accepts four variable disease risk parameters, 'df', 'd1uk20[M]', 'd2uk20[M]', and 'd3uk20[M]'. See the function 'Data' for information on these. Briefly, 'df' is the factor by which UK-born disease risks are multiplied to obtain non-UK born disease risks. The other three parameters are UK-born disease risks for Primary, Reactivation, and Reinfection Disease respectively, in adult males (those aged 20 years and over). In this version of the model, disease risk are fixed for children under ten years of age, allowing for fewer variable parameters. 5. OTHER. Note that the following program substitutes the term 'dec' for the C term 'double'. It is short for "decimal", in contrast and parallel with "integer", saving valuable coding columns at the left of the line and helping data names line up. The sequence of random numbers is specified on the command line with phrases like 'randseq=0', 'randseq=1', 'randseq=-6', 'randseq=239702397623', and so forth. Fixed sequences that are the same each time the program runs occur when 'randseq' is 0 or greater. Each integer gives a different sequence of random numbers. (Actually, it gives only a different starting point in a single long sequence of random numbers.) Positive integers, or zero, are typically used in testing because program results are precisely repeatable. Arbitrary sequences that are different, with high probability, each time the program runs occur when 'randseq' is negative. The date and time, measured to the nearest second, selects the starting sequence, then the negative value modifies that sequence. Thus if several instances of the program were started on separate processors at the same time, the first with 'randseq=-1', the second with 'randseq=-2', and so forth, each instance of the program is guaranteed a different random number sequence. Unlike the case with non-negative integers, however, the sequence be different each time the program runs, with very high probability. The actual starting seed, incorporating the time of day if requested by a negative value of 'randseq', is stored in 'rand0' and reported at the end of the run. That allows a run to be repeated exactly even if it was started with an arbitrary sequence. 6. NOTES ON GENETIC STRAINS. At model initialization, strains are randomly assigned from two different strain type distributions derived from empirical data, one for non-UK born and one for UK-born, saved in 'sdimm' and 'sduk', respectively. Also, the total number of available strains in each distribution is 'is0' and 'is1', respectively. These are set in define statements before the model is run to faciliate array initialization and contiguous number of strains. New mutant strains which appear as the simulation runs will be given strain IDs distinct from any in the 'sdimm' or 'sduk' distributions. These IDs will begin with integer 'is0+is1' and will always be greater than or equal to this value. The variable 'stid' holds the next available strain type ID for new mutants. The diagram below illustrates which strain IDs belong to which individuals in the simulation: StrainID Available for Generic Reference ________________________________________________________ 0 (Not used) 1 Initial non-UK, migrants 2 Initial non-UK, migrants 3 Initial non-UK, migrants 4 Initial non-UK, migrants is0 5 Initial UK is0+1 6 Initial UK 7 Initial UK 8 Initial UK is0+is1 9 New mutant strain is0+is1+1 10 New mutant strain 11 New mutant strain 14 Next available new mutant stid ________________________________________________________ #include #include #include #include "common.h" #include "fileio.h" #define PN (q1+1) //Number of elements in array 'N'. #define T0 1981 //Start time of model, years. #define T1 2012 //End time of model, years. The simulation //ends before reaching this year. #define TDATA 2007 //First year of observed data, for reporting times. #define SSAV 1 //Switch model version depending on existence //of separate SubSaharan African group, //0=non-SSA, 1=SSA. #define SUPER 1 //Flag for whether model is run on supercomputer, //0=no, 1=yes (changes population sizes). #define DPARAM 1 //Allows model to accept disease progression //parameters (4 in this version), 0=no, 1=yes. #define REC 1 //Flag for whether tallying recent transmissions. #define BIRTH (indiv+1) //Index used for scheduling births. #define IMM (indiv+2) //Index for scheduling arrival of immigrants. #define RT (T1-T0) //Running time of model, calendar years. #define NUK 0 //Array index for non-UK born. #define UK 1 //Array index for UK-born. #define HIV 2 //Array index for HIV+. #define SSA 2 //Array index for SSA-born. #define M 0 //Array index for males. #define F 1 //Array index for females. #define E 0.0000000001; //Small number added to some event times to //ensure they happen in the future. #define AC 122 //Age classes for mortality data. #define LAT 5 //Years to Remote from recent (re)infection. #define BY (2012-1870+1) //Number of birth cohorts for mortality data. #define IS0 5000 //Number of strains for migrants. #define IS1 1000 //Number of strains for UK-born at model //initialization. dec N[PN]; //Current number in each disease state. dec N2[4][2][2][RT]; //Population sizes in the model at end of year by //age, sex, rob and year. dec N3[4][2][2][RT]; //Population sizes observed, which are compared //with model population sizes and used to correct //case numbers produced by the model. int Np[4][2][2][RT]; //Infected persons by age, sex, rob, year. dec age1[2],age2[2],agec[2]; //Accumulators for 1st and 2nd moments of age. dec repc[4][2][2][2][RT]; //Reported cases by age category, sex, rob, //disease site and year. dec repc2[4][2][2][2][RT][5]; //Time/place of tranmission for reported cases, //indexed as 'repc' plus last index is 0 for //total cases, 1 for recent/UK, 2 for older/UK //3 for recent/NonUK and 4 for older/NonUK. dec repc3[15000][7]; //Data on typed cases, 0=age, 1=sex, 2=rob, 3=time //of report, 4=place/time of infection, 5=strain ID, //6=number of others with identical strain. int ari[2][RT]; //Number of successful transmissions, by rob //(UK/NUK) and year. int clust[4][2][2][5]; //Clustering data by age, sex, and rob, where //last index gives cases which are: 0=unique and //non-recent/non-UK,1=unique and recent/UK, //2=clustered and non-recent/non-UK, 3=clustered //and recent/UK, 4=total typed cases. int deaths; //Current number of deaths. int events; //Current number of events dispatched. int immid; //Next available ID number for immigrants. int ukbid; //Next available ID number for UK-born. int stid; //Next available ID for new strain types. int repid; //Next available ID for 'repc3' case report. extern dec t; //Current time (Managed by 'EventSchedule'). dec pt; //Time of previous report. dec t0 = T0; //Beginning time of simulation. dec t1 = T1; //End time of simulation. int tdata = TDATA; //First year of observed data. int lup; //Year of last update to birth and immigration //rates, which are sensitive to calendar year. dec runid; //ID number for printing output files. unsigned long startsec; //Starting clock time, seconds of Unix. unsigned long rand0; //Starting random number seed. struct Indiv *A; //State of each individual, including their //characterisitics, saved event times, etc. /*----------------------------------------------------------------------------* PARAMETERS AND CONTROL VARIABLES /* Population initialization */ int maximm; //Maximum immigrants in pop'n at any time. dec inf1981[121][3][2][9]; //Cumulative probabilities of the 9 disease //states for pop. initialization (by a,s,rob). dec n1981[121][2][2]; //Numbers in each age/sex/rob category at //population initialization, 1981. dec ssa1981[121][2]; //Proportion SSA by age/sex category. /* Infection transmission */ dec c[2][2]; //Effective contacts per year per pulmonary //case (smear+) by sex and region of birth. dec pcc; //Probability effective contact is close contact //(drawn from within own region of birth, //UK or non-UK). dec s2[2]; //Relative susceptibility to reinfection (s). dec smear[121]; //Proportion smear positive by age. /* Vaccination */ dec v1[2]; //Efficacy of vaccine (rob). dec v2[2]; //Portion vaccinated at designated age (rob). dec v3[2]; //Average age of vaccination (rob). /* Disease progression */ dec d1[2][3][121]; //Proportion Recently Infected who progress to //disease over first 5 years of infection //(by sex,rob,age) dec d3[2][3][121]; //Proportion Reinfected who progress to disease //over first 5 yrs of reinfection (by a,s,r). dec drr[6]; //Cumulative, relative risk of disease //progression by year since infection, used //with 'd1' and 'd3' (for first 5 years of infection). dec B1[6]; //Array of values for finding cumulative risk of //in first five years of infection/reinfection, //used with drr. dec d2[2][3][AC+2]; //Proportion of those Remotely Infected who progress //to disease, cumulative dsn by sex, rob //(where r=0, 1, or 2. 2=HIV+ and SSA in //SSA version of model), and age. dec A2[AC+2]; //Array of values for finding random time to //disease for Remote Infection. dec ehiv; //Factor by which non-UK born disease risks are //multiplied for HIV+ SSA individuals. dec df; //Factor by which UK-born disease progression //rates are multiplied to get immigrant rates. dec d1uk10[2]; //Rates of disease progression for primary (1), dec d1uk20[2]; //Reactivation (2), and Reinfection (3) disease dec d2uk10[2]; //for those aged 0-10 (10) and 20+ (20) by sex. dec d2uk20[2]; //These help construct 'd1', 'd2', and 'd3'. dec d3uk10[2]; dec d3uk20[2]; dec sdf1[2]; //Risk ratios for female:male disease dec sdf2[2]; //progression risks/rates (by age 0-10,20+). dec sdf3[2]; dec presp; //Proportion of all tuberculosis which is respiratory, //for correcting disease risks in Vynn. and Fine to //pulmonary disease risks in children. dec p1[121][2][2]; //Portion pulm -primary disease (a,s,rob) dec p2[121][2][2]; //Portion pulm -reactivation disease (a,s,rob) dec p3[121][2][2]; //Portion pulm -reinfection disease (a,s,rob) dec duk1p[2][2]; //Intermediate parameters for pulmonary-only dec duk2p[2][2]; //rates of disease progression. Used for dec duk3p[2][2]; //incorporating estimated rates from Vynnycky and //Fine into rates for this model, which are //combined pulmonary/non-pulmonary. Indexed by //age (0-10yrs, 20+yrs) and sex. dec d1p[121][2][2]; //Intermediate param's for pulmonary-only rates dec d2p[121][2][2]; //of disease progression. Used to get overall dec d3p[121][2][2]; //rates using those estimated by Vynn. and Fine //(which are pulmonary rates). Indexed by age, //sex, and rob. Precursors to 'd1', 'd2', 'd3'. /* Disease recovery, indexed by sex */ dec r3[2]; //Primary disease recovery rate dec r4[2]; //Reactivation disease recovery rate dec r5[2]; //Reinfection disease recovery rate dec r6[2]; //Primary non-pulmonary disease recovery rate dec r7[2]; //Reactivation non-pulm. disease recovery rate dec r8[2]; //Reinfection non-pulm. disease recovery rate /* Mortality */ dec A1[AC]; //Holds ages 0-121 which correspond to the //cumulative probabilities in M1. dec M1[BY][2][AC]; //Cumulative probabilities of death by a given //birth cohort, sex and age. dec cft[121][2][RT]; //Case fatality rate due to tuberculosis (a,type dis,y) /* Below are mortality rates used to generate lifetimes with exponential distribution, used for "reduction testing" of the model. */ dec m1 [2][RT]; //Mortality of uninf/vacc/inf ind's (sex, y) dec m6 [2][RT]; //Mortality of primary disease (sex,y) dec m7 [2][RT]; //Mortality of reactivation disease (sex,y) dec m8 [2][RT]; //Mortality of reinfection disease (sex,y) dec m9 [2][RT]; //Mortality of primary non-pulm. disease (sex,y) dec m10[2][RT]; //Mortality of reactivated non-pulm. disease (s,y) dec m11[2][RT]; //Mortality of reinfection non-pulm. disease (s,y) /* Birth and migration */ dec bcy[RT]; //Births by calendar year. dec pmale[RT]; //Portion of newborns who are male by year. dec immig[RT]; //Total (uk+non-uk-born) immigrants by year. dec pimm[RT]; //Proportion of immigrants non-UK born by year. dec ssaim[RT]; //Proportion of non-UK born immigrants born in //SubSaharan Africa by year. dec hivp[2][RT]; //HIV Prevalence in SubSaharan African born //immigrants by sex,year. dec immsex[RT][3]; //Proportion immigrants who are male by yr and //rob:0=non-UK,1=UK, 2=non-UK SSA. Note there //are different input files for SSA and non- //SSA version of the model. dec immage[RT][2][3][7]; //Cumulative proportion of immigrants (by yr, //sex,rob) in age classes, for use with RandF. dec immageX[RT][2][3][6]; //Probabilities of 6 age classes (by yr,sex, //rob) from ONS inflow data---as in 'immsex'. //Note there are two versions of the input file //for this array. Precursor to 'immage'. dec infimm[121][3][RT][9]; //Cumulative probabilities immigrants enter //disease states, by age,rob and year. dec Ax[9]; //State variables which accompany 'infimm'. dec ypb, ypi; //Years per birth, years per immigrant. dec em[2][3]; //Annual emigration rate by sex, rob. /* Strain type related */ dec md; //Mutations per year per strain type (diseased). dec mi; //Mutations per year per strain (infected). dec is0 = IS0; //Number of strains for migrants. dec is1 = IS1; //Number of strains for UK-born. dec S0[IS0+1]; //Strain IDs to accompany strain distribution //for migrants. dec S1[IS1+1]; //Strain IDs to accompany strain distribution //UK-born at initialization. dec sdimm[IS0+1]; //Cumulative probabilities of strain types //for non-UK born and migrants. dec sduk[IS1+1]; //Cumulative probabilities of strain types //for UK-born at initialization. dec ptyped[2]; //Proportion of cases with strain type, by //disease site, 0=non-pulm, 1=pulm. /* Assorted */ /* The following two recovery rates will not be used in version of model that defines Remote Infection as 'LAT' years after most recent infection. */ dec r1[2]; //Rate Recent Infection moves to remote (s) dec r2[2]; //Rate Reinfection moves to remote (s) dec proprep; //Proportion of cases reported. dec relativetime = 0; //Set for relative time reporting. dec randseq = 0; //Random number sequence (set with 'randseq=N'). dec tgap = 0.5; //Time between reports, years. dec kernel = 0; //Contagion kernel, 0=Panmictic, 1=Cauchy. dec sigma = 1; //Width of contagion kernel, where applicable. struct IO fmt[] = //Format statements for input/output. { /*00*/ { (dec*)bcy, {-'i',RT} }, /*01*/ { (dec*)immig, {-'i',RT} }, /*02*/ { (dec*)pimm, {-'i',RT} }, /*03*/ { (dec*)ssaim, {-'i',RT} }, /*04*/ { (dec*)pmale, {-'i',RT} }, /*05*/ { (dec*)hivp, {-'s',2,-'Y',RT}, {-'y',-'S'} }, /*06*/ { (dec*)infimm, {-'a',121,-'r',3,-'y',RT,-'q',9}, {-'R',0,SSAV+1,-'Y',-'Q',-'A'} }, /*--*/ { (dec*)inf1981, {-'a',121,-'r',2,-'q',9}, {-'a',120,0,-'r',UK,UK, -'q',1,7} }, /*--*/ { (dec*)inf1981, {-'a',121,-'r',2,-'q',9}, {-'a',120,0,-'r',NUK,NUK,-'q',1,7} }, /*09*/ { (dec*)ssa1981, {-'a',121,-'s',2}, {-'s',-'A'} }, /*10*/ { (dec*)n1981, {-'a',121,-'s',2,-'r',2}, {-'s',-'a',-'R',1,0,1} }, /*11*/ { (dec*)immsex, {-'i',RT,-'r',3}, {-'i',-'R',0,SSAV+1} }, /*12*/ { (dec*)immageX, {-'i',RT,-'s',2,-'r',3,-'a',6}, {-'i',-'r',0,SSAV+1,-'s',-'a'} }, /*13*/ { (dec*)immage, {-'i',RT,-'s',2,-'r',3,-'a',7}, {-'i',-'R',0,SSAV+1,-'A',-'s'} }, /*14*/ { (dec*)M1, {-'i',BY, -'s',2,-'a',AC}, {-'s',-'i',-'A'} }, /*15*/ { (dec*)cft, {-'a',121,-'d',2,-'i',RT} }, /*16*/ { (dec*)d1, {-'s',2,-'r',3,-'a',121}, {-'s',-'r',0,1,-'A'} }, /*17*/ { (dec*)d2, {-'s',2,-'r',3,-'a',124}, {-'s',-'r',0,2,-'A'} }, /*18*/ { (dec*)d3, {-'s',2,-'r',3,-'a',121}, {-'s',-'r',0,1,-'A'} }, /*19*/ { (dec*)inf1981, {-'a',121,-'s',2,-'r',3,-'q',9}, {-'r',-'s',-'A',120,0,-'Q',1,8} }, /*20*/ { (dec*)smear, {-'a',121} }, /*21*/ { (dec*)N3, {-'a',4, -'s',2,-'r',2,-'i',RT} }, /*22*/ { (dec*)repc, {-'a',4, -'s',2, -'r',2, -'d',2, -'i', RT} }, /*23*/ { (dec*)sdimm, {-'i',IS0+1} }, /*24*/ { (dec*)sduk, {-'i',IS1+1} }, { } }; /*----------------------------------------------------------------------------* MAIN INITIALIZATION This routine should be called each time the program is reused, to clear static variables for the next run. The function was added when 'tb30i.c' was made into a function of the fitting routine, to implement parallel, replicate runs of the tuberculosis program. This would not be necessary if the program were called as independent executable, as before. MainInit() { int i,j,k,l,m,n; for(i=0; i=0) RandStart(rand0); //from a specified or an arbitrary else rand0 = RandStartArb(rand0); //place. EventStartTime(t0); //Initialize the event queues. t = t0; //Set the starting time. stid = is0+is1; //Set first available new strain //strain ID for mutants. InitPop(); //Set up initial population. Report(argv[0]); pt = t; //Report initial conditions. BirthG(); //Start external event generators ImmigrateG(); //for birth and immigration. for(t=t0; tt1) return; //Advance time to the next event. tstep(tw, t); //Record the size of the time step. events += 1; //Increment the events counter. switch(A[n].pending) //Process the event. { case pVaccin: Vaccination(n); break; //[Vaccination] case pTransm: Transmission(n); break; //[Transmission of an infection] case pRemote: Remote(n); break; //[Transition to latency] case pDisease: Disease(n); break; //[Progression to disease] case pDeath: Death(n); break; //[Death] case pMutate: Mutate(n); break; //[Strain type mutation] case pEmigrate: Emigrate(n); break; //[Emigration] case pBirth: BirthG(); break; //[Birth generator] case pImmig: ImmigrateG(); break; //[Immigration generator] case pRep: Rep(n); break; //[Case report] default: Error2(921.2, //[System error] "`A[",n,"].pending=",A[n].pending); } } /*----------------------------------------------------------------------------* BIRTH This routine is dispatched when an individual is to be born. All newborns are Uninfected; exit from the Uninfected compartment is by vaccination to the Immune compartment, by infection to the Recent Infection compartment, and by emigration from the population or death. ENTRY: 'n' indexes an individual being born. 'b' contains the time of birth. Presently, this is the current time, though with some set up, it could be earlier than present (notably, 'pmale' would have to be indexed differently). 't' contains the current time. 'A[n].state' contains the present state of the record (can be any state, including 0, which is not a state but marks records not yet assigned). 'm1' contains the mortality rate for susceptible individuals, if applicable. 'em' contains the emigration rate. 'v1' contains vaccine efficacy. 'v2' contains the probability that an individual will be vaccinated. 'v3' contains the average age of vaccination. 'VTYPE' is zero if vaccinations are to match ODE conventions. No event is scheduled for individual 'n'. EXIT: 'Birth' contains a status code. 0 The individual would die before the current time so no birth has been recorded and no event scheduled. 1 Entry 'n' is initialized as a susceptible newborn and its first event is scheduled, either vaccination, emigration or death. 'A[n].state' marks a susceptible individual. Counters in 'N' are updated. #define VTYPE 1 //Vaccination type. int Birth(int n, dec b) { int y, s, v, e; dec wd, we, wv; if(nindiv) Error1(610.3, "n=",(dec)n); //Check for appropriate n. if(n<1) Error1(610.4, "n=",(dec)n); // SET UP BASIC, UNINFECTED IMMIGRANT NewState(n,qU); //Assign to Uninfected state //to start with. y = (int)t - (int)t0; //Get array index for year. A[n].tExit = 0; //Clear saved event A[n].tDisease = 0; //times or states. A[n].tTransm = 0; A[n].tMutate = 0; if(REC) A[n].inf = 1; if(SSAV) A[n].ssa = 0; A[n].strain = 0; if(n<=maximm) A[n].rob = rob = 0; //Assign rob=0 to all non-UK born. else A[n].rob = rob = 1; //Assign rob=1 to UK-born. s = 0; //Set sex as male to begin. if(rob==0 && SSAV==1) //If non-UK born and running { A[n].ssa = 0; //'SSA' version of model, check if(Rand()immsex[y][SSA]) s = 1; //Get sex of SSA. if(Rand()immsex[y][0]) s = 1; } //UK and assign sex. else //Assign sex to non-UK-born in if(Rand()>immsex[y][rob]) s = 1; //non-SSAV model and UK-born. A[n].sex = s; //Assign sex to record. age=GetAge(n,s,rob); //Assign age. a = (int) age; //Save integer age. rob2=rob; //Create 'rob2' (0,1 and 2) if(SSAV && A[n].ssa) rob2=2; //since here 'rob' is only 0 or 1). A[n].tBirth = t-age; //Save time of birth based on age. A[n].tDeath = wd //Assign time of death and check = t+LifeDsn(s,age,m1[s][y]); //death time is ok. if(wd5 && st<9) //Process Primary, Reactivation, { EventCancel(n); //Reinfection disease classes. A[n].strain = Strain(0); //Assign infection strain. NewState(n,st-3); //First put in correct infection Disease(n); //class and then send to 'Disease'. if(REC) //Assign 'inf' (time/place) { if(st==7) A[n].inf = 4; else A[n].inf = 3; } return 5; } else Error(618.1); return 0; //(Will never reach this). } /*----------------------------------------------------------------------------* VACCINATION This routine is dispatched when an individual is scheduled for an effective vaccination. Ineffective vaccinations are already accounted for---they are never scheduled for this routine. Effective vaccinations are assumed to impart lifelong immunity; therefore individuals will never leave this state, except by dying or emigrating from the population. ENTRY: 'n' indexes an individual being born. 't' contains the current time. 'A[n].state' contains the present state (always 'qU'). 'A[n].sex' contains the individual's sex. 'A[n].tEmigrate' contains time of emigration. 'A[n].tDeath' contains time of death. No event is scheduled for individual 'n'. EXIT: 'A[n].state' contains the new state, 'qV'. Counters in 'N' are updated. int Vaccination(int n) { NewState(n, qV); //Change states. if(A[n].tEmigrateindiv||n<1) Error1(610.3,"",n); //Check for appropriate n. if(strain>=stid) Error1(616.0,"",strain);//Check for appropriate strain ID. if(tinf>5||tinf<0) Error1(617.0,"",tinf); //Check for appropriate 'tinf'. if(tinf==5) tinf=tinf-E; //Correct 'tinf' if equal to 5. a = (int)(t-A[n].tBirth); //Retrieve integer age. s = A[n].sex; //Retrieve sex. rob = A[n].rob; //Retrieve region of birth. y = (int)t - (int)t0; //Get array index for year. switch(A[n].state) //Determine the new state and { //its associated parameters. case qI2: r=r2[s]; q=qI3; break; case qU: r=r1[s]; q=qI1; break; default: return 0; } //Avoid uninfectable states. EventCancel(n); //Else cancel the pending event and NewState(n, q); //mark this individual as infected. if(REC) A[n].inf = 1; //Save place of infection as UK //(will be changed outside routine //if infection is acquired abroad). if(type==1) //Increment ARI counter if infection ari[rob][y]++; //occurred in the UK. A[n].strain = strain; //Assign infecting strain type. wd = A[n].tDeath; //Retrieve time of death. we = A[n].tEmigrate; //Retrieve time of emigration. wr = t+LAT-tinf; //After 'LAT' years individual is //defined as remotely infected. wdis = t+Tdis(n,a,s,rob,tinf)+E; //Calculate time to disease. if(wdis<=t) Error2(620.0,"t=",t, " wdis=",wdis); wm = t+Expon(mi); //Calculate strain mutation time. if(wd=qD1) //Establish a new time for strain A[n].tMutate = t+Expon(mi); //mutation if the prior state //was disease. wdis = t+Tdis(n,a,s,rob,0); //Calculate time to disease. wd = A[n].tDeath; //Retrieve time of death. we = A[n].tEmigrate; //Retrieve time of emigration. wm = A[n].tMutate; //Retrieve time of mutation. if(wdp) //If this should be non-pulmonary { switch(A[n].state) //disease, update new state and { //associated parameters. case qI1: r=r6[s]; m= m9[s][y]; q=qD4; break; case qI2: r=r7[s]; m=m10[s][y]; q=qD5; break; case qI3: r=r8[s]; m=m11[s][y]; q=qD6; break; default: Error(922.0); } } NewState(n, q); //Mark the individual as diseased. wr = A[n].tExit = t+RecovDsn(s,age,r); //Establish time to remote. we = A[n].tEmigrate; //Retrieve emigration time. wd = A[n].tDeath; //Retrieve time of death. A[n].tMutate = wm = t+Expon(md); //Establish new mutation time. if(q>=qD4) ds = 0; //Set disease site to non-pulmonary else ds = 1; //or pulmonary. if(Rand()=immid) i = j+(maximm+1-immid); else i = j; } while (i==n); } //Avoid infecting self. Infect(i,0,A[n].strain,1); //Infect chosen individual. A[n].tTransm=t+Expon(c[A[n].sex][A[n].rob]);//Establish time to transmit again. switch(i=Earliest(A[n].t, v)) //Schedule the earliest event. { case iRep: SCHED(pRep, A[n].tRep, 6); case iTransm: SCHED(pTransm, A[n].tTransm, 1); case iExit: SCHED(pRemote, A[n].tExit, 2); case iMutate: SCHED(pMutate, A[n].tMutate, 4); case iEmigrate: SCHED(pEmigrate, A[n].tEmigrate, 5); case iDeath: SCHED(pDeath, A[n].tDeath, 3); default: Error1(922., "m=",(dec)i); } return 0; //(Will never reach this.) } /*----------------------------------------------------------------------------* MUTATION This routine is dispatched when the strain type of an infected or diseased individual is scheduled for mutation. The mutation does not affect any other event. After mutation is processed, the individual is scheduled for their next event, which will depend on disease state. ENTRY: 'n' indexes an individual whose strain type is to mutate 't' contains the current time. 'mi' contains the mutation rate for strains not in an active disease case (merely infection). 'md' contains the mutation rate for strains in a disease case. 'A[n].state' contains the current state (can be any infection or disease state). 'A[n].strain' contains the strain identification number of the current strain of infection or disease. 'A[n].tDeath' contains the saved time of death. 'A[n].tEmigrate' contains the saved time of emigration. 'A[n].tExit' contains the saved time to exit state. 'A[n].tTransm' contains the saved time to transmit again. No event is scheduled for individual 'n'. EXIT: The next event for individual 'n' is scheduled. 'A[n].tMutate' contains the time of next scheduled strain mutation. 'Mutation' contains a status code. 1 Recovery to remote infection is scheduled. 2 Progression to disease is scheduled. 3 Death is scheduled. 4 Strain mutation is scheduled. 5 Emigration is scheduled. 6 Case report is scheduled. Counters in 'N' are updated. Mutate(int n) { dec m, wm, wd, we, wdis, wr, wt, wrep; A[n].strain = stid++; //Assign new, mutant strain type //and update next available ID. if(A[n].state<=qI3) m = mi; //Determine appropriate mutation else m = md; //rate and calculate time to wm = t+Expon(m); //next mutation. wd = A[n].tDeath; //Get time of death. we = A[n].tEmigrate; //Get time of emigration. wdis = A[n].tDisease; //Get time of disease. wr = A[n].tExit; //Get time to remote infection. if(A[n].state==qI2) //Schedule events for remotely { //infected individuals ('qI2'). if(wdqU) //Reduce the number in the old state unless N[A[n].state] -= 1; //the individual is entering Uninfected, //which only happens at birth or immigration. if(N[A[n].state]<0) //Make sure the state has not become Error2(609.0, "q=",(dec)q, //negative. " n=",(dec)n); A[n].state = q; //Change state. N[A[n].state] += 1; //Increase the number in the new state. } /*----------------------------------------------------------------------------* TRANSFER This routine transfer all information about an individual, including saved event times, to a new identification number. The routine then cancels the pending event for that index number and re-schedules it using the new index number. The routine is used to keep the array of individuals contiguous for each region of birth. ENTRY: 'n' is the new index number to be assigned, which has no event scheduled 'n2' is the current index number of the individual. There is an event scheduled for 'n2'. EXIT: 'n' is the new index number of the individual. The event scheduled for n2 is now re-scheduled under 'n' and all other data from 'n2' are transferred to 'n'. 'n2' no longer has an event scheduled and the index number is free to be used again. Transfer(int n, int n2) { if(n!=n2) { A[n] = A[n2]; EventRenumber(n, n2); } //Copy data and reschedule as 'n'. } /*----------------------------------------------------------------------------* ADD REPORTED CASE This routine keeps track of the number of reported cases by age, sex, place of birth, disease site and calendar year. After a case is reported, they are scheduled for their next event. For the version of the model with a genetic component, this routine also allows for a stain to be genotyped and reports genotyping and other data for those cases, including the strain type identifier, and the age, sex, rob, etc for the case. ENTRY: 't' is the current time. 't0' is model start time. 't1' is the model end time. 'A[n].tBirth' contains the time of birth. 'A[n].sex' contains the sex of the individual. 'A[n].rob' contains the region of birth of the individual, 0=non-UK, 1=UK born. 'SSAV' is the version of the model running, 0=non-SSA, 1=SSA. 'repc' holds the numbers of reported cases. 'repc2' holds the numbers of cases by place and time of infection. 'ptyped' holds the proportion of cases with genotyping data, by rob. 'repc3' holds data on cases reported and genotyped. 'A[n].tDeath contains the individual's time of death. 'A[n].tEmigrate' contains the individual's time of emigration. 'A[n].tExit' contains the individual's time to remote infection. 'A[n].tMutate' contains the individual's strain mutation time. 'A[n].ssa' holds country of birth in SSA version of model, 0=UK and non-UK other, 1=SSA, 2=SSA,HIV+ 'A[n].tImm' contains the time of immigration to UK. 'A[n].tInfected' contains the time infection was acquired. 'A[n].inf' contains the time and place of infection 1=recent/UK, 2=older/UK, 3=recent/non-UK, 4=older/non-UK. 'A[n].strain' contains the strain ID for the case. 'A[n].state' contains the disease state of the individual. EXIT: 'repc' contains an additional case, individual 'n'. 'repc2' contains an additional case, if running 'REC' version of the model. 'repc3' contains an additional case, if 'n' was selected for genotyping. 'A[n].tRep' contains a time past the end of the simulation, so that individual 'n' will not be reported again. 'Rep' contains a status code. 1 A transmission is scheduled. 2 Recovery is scheduled. 3 Death is scheduled. 4 Strain mutation is scheduled. 5 Emigration is scheduled. Rep(int n) { int s,r,y,acl,d; dec age,wt, wd, we, wr, wm, wrep; age = t-A[n].tBirth; //Get age. if(age<15) acl=0; //Find age class (classes which match else if(age<45) acl=1; //notification rates). else if(age<65) acl=2; else acl=3; s = A[n].sex; //Get sex. r = A[n].rob; //Get region, 0=non-UK, 1=UK. y = (int)t - (int)t0; //Get year for array index. if(A[n].state>=qD4) d=0; //Get disease site (pulm/non-pulm) else d=1; //for arrray index. repc [acl][s][r][d][y] += 1; //Increment reported cases. if(REC) //Increment reported cases for recent { repc2[acl][s][r][d][y][0] += 1; //transmission stats, by 'inf'. repc2[acl][s][r][d][y][A[n].inf] += 1; } if(Rand()=2007) //If case is designated to be typed { repc3[repid][0] = age; //and is within correct time window, repc3[repid][1] = s; //store for genetic output. repc3[repid][2] = r; repc3[repid][3] = t; repc3[repid][4] = A[n].inf; repc3[repid][5] = A[n].strain; repid++; } A[n].tRep = t1*2+Rand(); //Set reporting time distant enough //that it cannot occur again. wd = A[n].tDeath; //Get time of death. we = A[n].tEmigrate; //Get time of emigration. wr = A[n].tExit; //Get time to remote infection. wm = A[n].tMutate; //Get strain mutation time. if(A[n].stateremaining life time (years until death) for the individual. static int lifedsn = 2; //Type of longevity distribution to be used. dec LifeDsn(int sex, dec age, dec mort) { int yb, y, n; dec w; switch(lifedsn) { case 0: return Expon(mort); //Constant probability of death. case 2: //Empirical life tables. { yb = (int)(t-age); //Get year of birth. y = yb-1870; if(y<0) y=0; //Get array index for birth year. w = RandF(A1, M1[y][sex], 122, age); return w; } default: Error(922.0); } //Incorrect life span selection. return 0; //(Will never reach this.) } /*----------------------------------------------------------------------------* EMIGRATION TIME DISTRIBUTION This routine assigns the number of years until time of emigration from the study population for an individual, based on the present year, the individual's sex and age, and other factors in the condition of the individual. Exponentially distributed times, with a constant chance of emigration in each year, are included for calibration of the model to an ordinary differential equation version of the model. ENTRY: 'rob' contains the individual's region of birth, 0=non-UK, 1=UK 'sex' contains the individual's sex, 0=male, 1=female. 'age' contains the individual's present age, years. 'em' contains the emigration rate. 'emdsn' defines the emigration time distribution computation: 0 Exponential 2 Empirical migrant flow data. 't' contains the present time. EXIT: 'EmDsn' contains the remaining time in the UK for the individual in the updated version of the program. Note that many individuals will have dates of emigration beyond the running time of the model or beyond their own death date; these individuals will never emigrate. static int emdsn = 0; //Type of longevity distribution to be used. dec EmDsn(int rob, int sex, dec age, dec em) { int yb, y, a, n; dec w; switch(emdsn) { case 0: return Expon(em); //Constant probability of //emigration. default: Error(922.0); //Incorrect life span selection. } return 0; //(Will never reach this.) } /*----------------------------------------------------------------------------* RECOVERY DISTRIBUTION This routine assigns a time to remote infection based on the present year, the individual's sex and age, and other factors in the condition of the individual. Various probability distributions may be selected. ENTRY: 'sex' contains the individual's sex, 0=male, 1=female. 'age' contains the individual's present age, years. 'r' contains a recovery parameter describing the individual. For testing this represents the proportion that would recover per year if recovery were strictly random (i.e., Poisson distributed in time). 't' contains the present time. EXIT: 'RecovDsn' contains the time until recovery, in years. static dec recovdsn = 0; //Type of recovery distribution to be used. static dec rmu = 0.0; //Centering of recovery distribution, years. static dec rsigma = 0.1; //Half-width of recovery distribution, years. dec RecovDsn(int s, dec age, dec r) { dec w; switch((int)recovdsn) //Select the type of recovery. { case 0: return Expon(r); //(completely random) case 1: w = 0; break; //(completely fixed) case 2: w = Uniform(-rsigma,rsigma); break; //(uniform variation) case 3: w = LogNormal(rmu, rsigma); break; //(log-normal variation) case 4: w = Gauss(.0, rsigma); break; //(truncated Gaussian variation) case 5: w = Cauchy(.0, rsigma); break; //(truncated Cauchy variation) default: Error(922.); } return max(1e-9, w+1./r); //Return the time for recovery, } //always after a slight delay. /*----------------------------------------------------------------------------* TIME TO DISEASE This routine assigns a time to disease based on the individual's age, sex, region of birth, infection status (Recent Infection, Remote Infection, Reinfection), and in the SSAV version of the model, HIV status. Note that Recent Infection and Reinfection states are handled similarly, whereas Remote Infection is handled somewhat differently. Notes on SSA version of model: Would like to get disease rates correct so things are comparable between the SSA and non-SSA models. I think it is best to leave them as a ratio ('ehiv') and then when fitting model, the non-SSA model should fit higher disease risk for non-UK born than in the SSA model. In the SSA model they should be lower since a portion of non-UK born individuals will have elevated risk due to HIV. ENTRY: 'n' contains the individual's identifier. 's' contains the individual's sex, 0=male, 1=female. 'a' contains the individual's integer age. 'rob' contains the individual's region of birth, 0=non-UK, 1=UK, 2=SSA 'tinf' contains the time since infection (years). 'd1' and 'd3' contain the probability of progressing to disease for 'qI1' and 'qI3' over the first five years of infection. 'd2' contains the probability per year of progressing to disease for 'qI2'. 'A[n].state' contains the present state of the individual, 'qI1', 'qI2', 'qI3'. 't' contains the present time. 'drr' gives the relative risk of disease over the first five years of infection. 'B1' contains the year of infection, for use with 'drr'. 'SSAV' contains 1 if SSA version of model is to be run. EXIT: 'Tdis' contains the number of years until disease development. dec Tdis(int n, int a, int s, int rob, dec tinf) { dec d,age,w; if(SSAV && A[n].ssa==2) //If running SSA version of model and rob=2; //individual is HIV+, adjust 'rob'. switch(A[n].state) { case qI1: //Process Recently Infection. { d = d1[s][rob][a] //First calculate overall disease risk *(1-Val(1,tinf,B1,drr,0,5)); //for the first five years, correcting //for 'tinf' greater than 0 if applicable. if(Rand()>d) //If disease should NOT occur, schedule { w = 2*RT+Rand(); //disease past the running time of return w; } //model so that it never occurs. else { w = RandF(B1,drr,6,tinf); //If disease should occur, randomly return w; } } //choose year, based on relative risk //over five years. case qI3: //Process Reinfection. { d = d3[s][rob][a] //First calculate overall disease risk *(1-Val(1,tinf,B1,drr,0,5)); //for the first five years, correcting //for 'tinf' greater than 0 if applicable. if(Rand()>d) //If disease should NOT occur, schedule { w = 2*RT+Rand(); //disease past the running time of return w; } //model so that it never occurrs. else { w = RandF(B1,drr,6,tinf); //If disease should occur, get year return w; } } //from relative risk over five years. case qI2: //Process Remote Infection. { age = t-A[n].tBirth; w = RandF(A2,d2[s][rob],AC+2,age); return w; } default: Error(922.); } return 0; //(Will never reach this.) } /*----------------------------------------------------------------------------* GET RANDOM AGE FOR IMMIGRANT This routine assigns an age to an individual who is immigrating into the population. Age is randomly assigned based on probabilities of age classes from data. Probabilities of age classes are conditional on sex and region of birth. ENTRY: 'n' contains the individual's identifier. 's' contains the individual's sex, 0=male 1=female. 'r' contains the individual's region of birth, 0=non-UK, 1=UK 't' contains the present time. 'immage' contains cumulative probabilities of the age classes; to be compatible with future calls to 'RandF', the first cumulative probability is 0. 'SSAV' contains 1 if SSA version of model is to be run. EXIT: 'GetAge' contains the age (in years) of the individual. dec GetAge(int n, int s, int r) { dec rn,age; int y; rn = Rand(); //Get random number. if(SSAV) //If running SSA version of model, if(A[n].ssa) r=2; //check if SSA and adjust 'r' ('rob') if so. y = (int)t - (int)t0; //Get array index for calendar year. if(rn=121) age=120+Rand(); //draw from exponential distribution with //mean of 10 years. return age; } /*----------------------------------------------------------------------------* PARAMETER CHANGING This function updates variables associated with parameters that change with each model run. This routine must come after any change to parameters, e.g. through the 'gparam' function. Currently four disease risk parameters are varied during model fitting: 'd1uk20[M]', 'd2uk20[M]', 'd3uk20[M]' and 'df'. 'd1uk20[M]' is the risk of developing Primary Disease for UK-born males aged 20 years above. 'd2uk20[M]' is the annual risk of developing Reactivation Disease for UK-born males aged 20 years and above. 'd3uk20[M]' is the risk of developing Reinfection Disease for UK-born males aged 20 years and above. 'df' is the factor by which UK-born disease risks are multiplied to get non-UK born risks. ENTRY: 'drr' contains relative cumulative rates of disease progression by time since infection, up to five years. 'd1uk20[M]' contains the risk of developing Primary Disease for UK-born males aged 20 years above 'd2uk20[M]' contains the annual risk of developing Reactivation Disease for UK-born males aged 20 years and above. 'd3uk20[M]' contains the risk of developing Reinfection Disease for UK-born males aged 20 years and above. 'df' contains the factor by which UK disease risk are multiplied to get non-UK born disease risks. 'sdf1' contains the sex ratios of female:male disease risk (Primary Disease). 'sdf2' contains the sex ratios of female:male disease risk (Reactivation Disease). 'sdf3' contains the sex ratios of female:male disease risk (Reinfection Disease). 'presp' contains the proportion of disease respiratory for children. EXIT: 'd1'contains Primary Disease risk by sex, rob (UK/nonUK/HIV) and age. 'd2'contains Reactivation Disease rate by sex, rob (UK/nonUK/HIV) and age. 'd3'contains Primary Disease risk by sex, rob (UK/nonUK/HIV) and age. /* Notes: Disease progression risks are specified separately for Recent, Remote, and Reinfected individuals ('d1', 'd2', 'd3'), and also allowed to vary by sex, rob and age. Following Vynnycky and Fine, the age-dependency of risk is specified by two parameters---one for risk ages 0-10 (constant) and one for 20+ (constant). Risk between ages 10-20 is assumed to increase linearly from the rate at 10 to the rate at age 20.For those over 10 and under 20 overall disease progression rate is: 'A0+(age-10)*((A20-A10)/10)'. For 'd1' and 'd3', these represent overall, cumulative risks of disease progression in the first five years of infection or reinfection for infection at a given age. The array 'drr' specifies the cumulative relative risk over these five years and is used along with 'd1/d2' to generate a time to disease, if applicable. For 'd2' the disease progression risks are annual rates at a given age (and sex/rob) for disease progression. 'd2' is converted to cumulative risk by age, and the cumulative distribution is used, along with current age of individual infected, to assign a time to disease. Disease progression risks estimated by Vynnycky and Fine 1997 for white ethnic males are used to set UK-born males and UK-born female risk for those under ten years of age. For all ages, female risks are calculated by multiplying male risks by the risk ratios for sex, 'sdf1', 'sdf2', and 'sdf3'. Risks for 20 year-olds are allowed to vary in the model (one for each disease types, so three parameters). For non-UK born risks, 'df' is multiplied by the UK-born risks to get non-UK born risks. 'df' is allowed to vary in the model. HIV-positive risks are further multiplied by 'ehiv'. This means as the three UK-born risks and 'df' are varied during model fitting, non-UK born disease progression risks would need to be re-generated each time the model is run. One complication regards pulmonary versus overall disease risk. In Vynnycky and Fine 1997, risks are for respiratory disease. However, disease risk needed for the model is overall disease risk, pulmonary plus non-pulmonary. For those fixed in the model (UK-born under 10), the respiratory risk is corrected to equal overall (pulmonary + non-pulmonary) risk. Param() { int a,s,r; dec ep = 0.00000000000001; //Check that 'ehiv' and 'df' are not if(ehiv1) d1[s][NUK][a] = 1; //annual rates ('d2') are not above 1 if(d2[s][NUK][a]>1) d2[s][NUK][a] = 1; //for non-UK born. if(d3[s][NUK][a]>1) d3[s][NUK][a] = 1; } if(SSAV) { for(a=0; a<121; a++) //Get disease risks for HIV-positive for(s=0; s<2; s++) //SSAs by multiplying non-UK born { d1[s][HIV][a] = ehiv*d1[s][NUK][a]; //risks/rates by factor 'ehiv'. d2[s][HIV][a] = ehiv*d2[s][NUK][a]; d3[s][HIV][a] = ehiv*d3[s][NUK][a]; } for(a=0; a<121; a++) //Make sure disease risks for HIV+ for(s=0; s<2; s++) //are not above 1. { if(d1[s][HIV][a]>1) d1[s][HIV][a]=1; if(d2[s][HIV][a]>1) d2[s][HIV][a]=1; if(d3[s][HIV][a]>1) d3[s][HIV][a]=1; } } for(s=0; s<2; s++) //Fix end of array for 'd2' (longer). for(r=0; r<3; r++) d2[s][r][121] = d2[s][r][120]; for(s=0; s<2; s++) //Redefine 'd2' as cumulative probability of for(r=0; r<3; r++) //disease progression; first translate risk d2[s][r][1] = d2[s][r][0]; //in first year of life ('d2[s][r][0]') as //cumulative risk experienced before age 1 for(a=2; a<=121; a++) //('d2[s][r][1]'). Then convert rest of array for(s=0; s<2; s++) //to cumulative risks. Indexed by sex, rob for(r=0; r<3; r++) //and age. d2[s][r][a] = d2[s][r][a-1]+(1-d2[s][r][a-1])*d2[s][r][a]; for(s=0; s<2; s++) //Make sure cumulative probability of disease for(r=0; r<3; r++) //has not gone beyond 1. { if(d2[s][r][121]>1) Error(754.1); } for(s=0; s<2; s++) //Finish cumulative distribution so that for(r=0; r<3; r++) //cumulative risk before age 0 is 0 and { d2[s][r][0] = 0.0; //those who should never progress to disease d2[s][r][122] = d2[s][r][121]; //are assigned times far into the future so d2[s][r][123] = 1.0; } //that disease progression does not happen. /* } /*----------------------------------------------------------------------------* INITIALIZE STARTING POPULATION This function sets up the intial population by looping through matrix 'n1981' which holds numbers in the initial population by age, sex, and rob. For the SSA version of model, 'ssa1981' is used for the proportions of SubSaharan Africans among non-UK born in 1981 by age and sex. Notes: In function 'Param', one could multiply 1981 (if they are proportions) by the initial population size, e.g. 'initpop'. Assignments could also be made deterministic since the numbers are sufficiently large. This would need some planning for dealing with remainders and other numeric issues. ENTRY: 'n1981' contains numbers by age, sex, and rob for individuals in the population at initialization in 1981. 'ssa1981' contains the proportion of SSAs among non-UK born at population initialization. EXIT: The intial population is set up; each individual is assigned attributes and scheduled for exactly one event (other event times may be stored for an individual. InitPop() { int a,s,i,n,st,rob; dec age,wd,we,wv,tinf; ukbid = maximm+1; //Initialize ID numbers to immid = 1; //correct values. /* NOTE: This function could be made so that UK-born, non-UK born and SSA-born are initialized with one function, called three times. Also so that states are assigned and processed in a function. */ for(a=0; a<121; a++) //First, initialize UK-born for(s=0; s<2; s++) //('rob=1') population for all age for(i=0; i0) k += 2; //fourth dimension, by recent/UK //and clustering. clust[ac][s][r][k]++; //Increment cases by category and clust[ac][s][r][4]++; } //accumulate the total. return 0; //Return to caller. } /*----------------------------------------------------------------------------* CHECK FOR MONOTONICITY This routine checks whether a table of cumulative probabilities is monotonically increasing and optionally whether it is bracketed by 0 and 1. ENTRY: 'p' is the table of cumulative probabilities. 'n' is the number of entries in the table. 'b' is set if the table should begin with 0 and end with 1. 'r1' and 'r2' contain two numbers that may help to identify the location of the error. If such numbers will not help, then either or both contain zero. EXIT: The routine returns if the table appears to be correct. If not, an error message is issued and the routine never returns. int monotone(dec p[], int n, int b, int r1, int r2) { int i; for(i=1; ip[i]) //decreases. Error3(621., "`",r1, "` ",r2, "` ",i); if(b && (p[0]!=0||p[n-1]!=1)) //If requested, make sure it begins Error2(622., "`",r1, "` ",r2); //with 0 and ends with 1. return 0; //(Will never reach this). } /*----------------------------------------------------------------------------* SERVICE ROUTINES Various service routines, such as parameter reading and report writing, which are peripheral to the operation of the program, are included in file 'service.c'. This becomes part of the main program but is kept separate for simplicity. The tables below define parameters that may vary each time the program is invoked, available to the parameter gathering routine 'gparam'. char *pntab[] = //Table of parameter names. { "s2[0]","s2[1]","c[0][0]","c[0][1]","c[1][0]", "c[1][1]", "v1[0]","v1[1]", "v2[0]", "v2[1]", "v3[0]", "v3[1]","ehiv", "r1[0]","r1[1]", "r2[0]", "r2[1]", "r3[0]", "r3[1]", "r4[0]","r4[1]", "r5[0]", "r5[1]", "r6[0]", "r6[1]", "r7[0]","r7[1]", "r8[0]", "r8[1]", "df", "runid", "d1uk20","d2uk20","d3uk20", "md", "mi", "pmale[0]", "randseq", 0 }; dec *patab[] = //Table of parameter addresses. { &s2[0], &s2[1], &c[0][0], &c[0][1], &c[1][0], &c[1][1], &v1[0], &v1[1], &v2[0], &v2[1], &v3[0], &v3[1], &ehiv, &r1[0], &r1[1], &r2[0], &r2[1], &r3[0], &r3[1], &r4[0], &r4[1], &r5[0], &r5[1], &r6[0], &r6[1], &r7[0], &r7[1], &r8[0], &r8[1], &df, &runid, &d1uk20[0], &d2uk20[0], &d3uk20[0], &md,&mi, &pmale[0], &randseq, 0 }; #include "service.c"