Modgen and the application RiskPaths from the model developer's view
- Date modified:
When installed on a computer, Modgen integrates itself into the (required) Microsoft Visual Studio C++ environment. The visual components of Modgen are a separate toolbar as well as additional items under the Tools and Help menus of Visual Studio. Modgen also appears as an option in the file dialog box for creating a new project as well as in the dialog box for adding a file to an existing project.
Figure 1 displays a screenshot of the programming interface as it appears after opening the Modgen application 'RiskPaths.sln'. The Modgen toolbar consists of several icons for running Modgen, accessing help, opening the BioBrowser tool, and switching the language (between English and French).
Figure 1: The programming interface

Modgen code is organized into several files, each with the file extension .mpp. As can be seen in the Solution Explorer window (Figure 1), RiskPaths consists of eight .mpp files grouped in the "Models (mpp)" folder. These are the essential files of RiskPaths, i.e. the files containing all Modgen code written by the model developer.
When invoking the Modgen tool (which can be accessed from the toolbar, or from the first item under the "Tools" menu), these .mpp files are translated into C++ code. Thus Modgen acts as a pre-compiler, creating one .cpp source code file for each .mpp file and putting the resulting .cpp files in the "C++ Files" folder. The Modgen tool also adds model-independent C++ code components to the "C++ Files" folder; these additional filesFootnote 1 should not be changed by the model developer and are essential in order to use the C++ compiler to build the Modgen application.
The model parameters are contained in one or more .dat files organized in a folder labelled "Scenarios". These files are loaded at runtime and contain the actual values assigned to the parameters.
When running the Modgen tool, Modgen - like the C++ compiler - produces log output that is displayed in the Output window. Any error messages are also displayed in this window, and clicking on a particular error message leads you directly to the corresponding Modgen code that produced the error.
Two steps are required to create a Modgen application from the Visual Studio environment. First, Modgen has to translate the Modgen code in the .mpp files; this is done when invoking the Modgen tool. Second, the resulting C++ application has to be built and started. This can be done in one step by selecting "Start Debugging" in the "Debug" menu or by clicking the corresponding icon on the toolbar.
ACTORS.CPP, ACTORS.H, app.ico, model.h, model.RC, PARSE.INF, TABINIT.CPP, TABINT.H.
BioBrowser, the Modgen Biography Browser is a stand-alone software product which supplements the Modgen language used for dynamic longitudinal microsimulation modeling. BioBrowser allows the analyst to graph the microdata generated by the model. Its purpose is to aid in uncovering possible algorithmic errors in the model, or to study some particularly interesting cases with respect to the specified Modgen model.
Microsimulation models written in the Modgen language generate synthetic lifetimes of individual actors. Each actor is defined as a set of states which describe the characteristics of the actor. For example, an actor could be a male individual whose attributes are described through the following states: age, sex, marital status, and health status. The values of these states change as the actor progresses through his lifetime. In our example, the individual’s age would change on each birthday while the marital status would change at the time point at which he/she was married, divorced, etc.
BioBrowser is a tool which allows the analyst to graphically examine the characteristics and attributes of an actor over the course of his/her lifetime. BioBrowser can graphically present one or many states for one or many simulated lifetimes. In this way, BioBrowser complements the other reporting features inherent in Modgen which are designed to provide detailed cross-sectional information on a collection of actors at a given reference time or state.
The graphical representations produced by BioBrowser originate from a special database file which is the product of a Modgen model simulation run. Once this file has been created there exists a variety of possible graphics which the analyst can create with BioBrowser. The specifications of these graphics are controlled by the user through drop-down menus and options. Therefore, an analyst with a limited knowledge of the Modgen modeling environment can create an impressive array of longitudinal graphics showing the characteristics of the actors at different points in time. All of these graphical representations can be saved for editing at some future time and/or routed to a printer or clipboard.
Specifying the contents of the database file requires the analyst to have some knowledge of the Modgen simulation environment. A section below describes the components of Modgen with which one must be familiar to successfully create a database file. Further details can be found in the Modgen Developer’s Guide.
The Modgen Biography Browser (BioBrowser) installation package consists of a sample database file demo(trk).mdb and a sample biography file demo.bbr. These files are referred to extensively to provide worked examples of the browser. The nature of these files is discussed in How to Use BioBrowser: The Basics.
BioBrowser has been tested on Windows XP and does not have substantial requirements for CPU, disk or memory.
Users with questions or problems with any aspect of this software are welcome to contact the development team at microsimulation@statcan.gc.ca.
Before using BioBrowser, it is important for the analyst to understand some of the essential components of Modgen.
database (.mdb) files
These files are created by Modgen during the simulation phase of the model. They contain the raw data necessary to construct the graphical representation created by BioBrowser. Although the database files can be read by BioBrowser, BioBrowser can never modify the contents of these files. All BioBrowser sessions begin by opening a pre-existing database file.
dominant actors
These elements are at the core of any Modgen simulation exercise. Dominant actors are usually persons or households which are created at the beginning of the simulation process and undergo changes to their characteristics as they proceed through their lives. Dominant actors are defined by their characteristics (states) and by the events which transform their states.
non-dominant actors
Modgen simulates one case at a time where a set of dominant actors undergoes changes to its states. One possible change to a person actor’s state is a marriage or a common-law union. When this event has occurred, Modgen generates an appropriate spouse. This spouse, another person actor, is termed a non-dominant person actor. Once created, non-dominant actors undergo the same possible events as the dominant actors of the same type. Non-dominant actors are linked to their dominant actor.
tentative actors
The process of generating a non-dominant actor in Modgen involves generating a sequence of potential candidates. The candidates who are not chosen are termed tentative actors since they have no links to any of the dominant actors in the model.
states
These elements define the characteristics of the actors over the span of their lifetimes. Examples of states might include age, employment status, or educational attainment. States can be scalars or arrays.
Before beginning to use BioBrowser, a database file needs to be created using Modgen. If you want to examine states which are not in the database, a new Modgen simulation must be run and a new database file needs to be created. A sample database demo(trk).mdb was included with this software package. For more information on creating new databases in Modgen please refer to the Modgen Developer's Guide or, for a quick overview/refresher, see Appendix: Creating a new Modgen database file.
BioBrowser takes the database and creates graphics of the characteristics of the actors. In addition to the above Modgen concepts, there are other concepts which relate specifically to BioBrowser.
biography (.bbr) files
These files contain the graphical representations which the analyst has created during a BioBrowser session. The biography files can be created, saved, and edited by the analyst during a BioBrowser session.
display band
The graphical display of a state or linked actor.
filter
The criteria used to narrow or refine the set of actors to be used in a biography..
navigation band
A type of display band which also includes a set of buttons which allows the user to go from the display bands of one actor to another and add new states to the biography. The buttons resemble the control buttons on the front of a CD player.
The BioBrowser menu bar contains a set of standard menus available in most Microsoft Office applications, as well as some application specific commands. Some of the same functions may be available as Toolbar buttons or through keyboard equivalents.

Pop-up menus: Some commands are only available from pop-up menus or by double-clicking on the chart area over the display bands of the desired state. Use the right mouse button click to access the pop-up menus. These menus will differ depending on whether or not the state is a simple state or a linked actor. For simple states such as “employed” below, the following commands are available:

For the filter tracking band and linked actors, access is also provided to the navigation band commands, as shown below:

The toolbar provides quick access to the most frequently used menu items and commands in the BioBrowser application. Each button is described by a Tool-Tip or status bar description. If you have a small screen at low resolution you may choose not to display the Toolbar. Choose Tools/Options and Click on the View Toolbar Option.
| Icon | Description | Menu equivalent |
|---|---|---|
| Create new biography | File / New | |
| Open saved biography | File / Open | |
| Save biography | File / Save | |
|
|
Print active biography | File / Print |
| Copy active biography to clipboard | Edit / Copy | |
| Undo last add | Edit / Undo Last Add | |
| Show or hide grid lines | Format / Grid Lines | |
| Show or hide guide lines | Format / Guide Lines | |
| Show or hide navigation bands | Format / Navigation Bands | |
| Change background colour | Format / Background colour | |
| Change chart colour | Format / Chart Colour | |
| Invoke BioBrowser Help | Help / Contents |
When a state is added various defaults for chart presentation are used depending on its Modgen state type. At present, five chart types are permitted:
| Line | Level | Horizontal Bar | Point |
|---|---|---|---|
![]() |
![]() |
![]() |
![]() |
| Event |
|---|
![]() |
BioBrowser recognizes the following Modgen state types and plots them by default according to their state type:
| State Type | Default Chart Type |
|---|---|
| Integer | Level |
| Long | Level |
| Floating Point | Line |
| Double | Line |
| Time | Line |
| Logical | Horizontal Bar |
| Classification | Event |
| Range | Level |
The Line style draws one line between 2 adjacent points, whereas the Level style draws two lines (a vertical then a horizontal) between 2 points. The Horizontal Bar, although most appropriate for logical, classification and range type of Modgen states can be used on all states. For continuous states such as float or double, the horizontal bar uses colour interpolation from a start and end colour defined by the user. No legend is available for Horizontal Bar.
The default colour for Line, Level and Point plots is blue. The default colours for Horizontal Bar are white and gray. You can control the line thickness, band width and point size for the biography but not at the level of a single state. At present, these settings are global to the biography window. All such settings are saved with the biography.
Double-click on a chart within a biography, or right click on it and select State Properties from the pop-up menu, to re-format it for chart type and colour. A format string for the Y-Axis labels where appropriate can be changed at this time as well. The following dialog box is used to set or adjust these properties:

If the chart type selected is Horizontal Bar a second colour will be presented for selection. For logical states, these will be the False and True colours. For all other states types these will be used as a start colour and end colour in a colour interpolation process.
Use the Format menu commands to change display options for the active biography window. The View menu, which includes the ToolBar and Status Bar display options, are application global.
The Tools/Options menu can be used to set and save session defaults and display default options for new biographies. The Options dialog box consists of three tabs: General, Chart Defaults and Axes Defaults. The General options will take effect immediately, whereas the two Default tabs are used only with new biography creation. The OK button will save these defaults to your application ini file.
The General tab below sets and saves session defaults used at application startup. To change them during the session, use the View and Tools menus.

The chart defaults used for new biography creation consists of the following display options. To change these options for an already open biography, use the Format menu.

Axes defaults for new biography creation are set and saved within the third options tab. Axes properties for an open biography can be set by double clicking the axes area of the chart window or by using the Axes Properties command from the Format menu.

Note that all display options and axes properties currently in effect for the open biography are saved with the biography file.
To print a biography, use File/Print or the Print button on the toolbar.
The printed biographies are sized to fit the page while maintaining their aspect ratio. The orientation used will depend on the aspect ratio of the biography window being printed, i.e., if the window is wider than tall, landscape will be used.
To send a biography to the clipboard, use Edit/Copy, Ctrl-C or the Toolbar copy button
What can simulation add to statistical analysis?
Desired features of a RiskPaths microsimulation model
Before we can answer the question of what simulation can add to statistical analysis, we first need a good understanding of what the statistical results presented in the previous section reveal. The estimation results for the two countries and two cohorts allow us to study similarities and differences between the countries, as well as the changes in parameters over time separately for each of the individual processes. We see a remarkable similarity in parameters across the two countries especially for the pre-transition cohorts. Bulgaria differs from Russia basically only in the three times lower union dissolution risks and the slower speed of second union formation. Accordingly, comparing the pre- and post-transition cohorts, we find dramatic changes in most processes. The risk of first births was halved in the first three years of the first union with no later recovery, although the parameters stayed relatively unchanged after three years in a union. Also, in second unions, fertility dropped by more than 50%. The biggest difference between the two countries after the transition is in first union formation--rates halved in Bulgaria but stayed stable in Russia. For first union dissolution we see the opposite picture--union dissolution risks increased by around 40% in Russia while staying almost unchanged in Bulgaria.
These are typical examples of insights we can gain by single process analysis. We have separated a complex system into its component processes and studied the changes within those processes. In the case of fertility we have introduced relative risks--we study how certain factors (here, different union statuses) influence a single process. This is a very typical analytical question; scientific literature is rich of this kind of research.
The power of microsimulation unfolds when we study various processes simultaneously. Even in our very simple demographic example, results are difficult to interpret when we are interested in the effect of changes in single processes on aggregate outcomes. For example, what is the effect of Russia's 40% increase in union dissolution risks on childlessness? The effect will depend on fertility out of unions and in second unions as well as the speed of second union formation. The relative risk of fertility is higher in second unions than after three years in the first union, but second union formation takes time (during which fertility is very low) and not all women enter a second union. Do these effects cancel themselves out or does union dissolution affect fertility - and in which direction? Such questions invite us to use microsimulation for sensitivity analysis. How do aggregate outcomes change in response to the change of a single parameter? Note that we now have moved analysis from the level of a single process to an analysis of system behaviour.
A comparison of the two cohorts invites a further type of system analysis--what is the relative contribution of the change in single processes to the aggregated outcome? Comparing the two simulated cohorts we see that childlessness has increased considerably in both countries but even more so in Bulgaria. We can use microsimulation to decompose the contributions of the changes in the various processes to the aggregate change. How much would childlessness have changed if only fertility parameters changed? What is the contribution of changes in union formation? Has the increase in union dissolution risk contributed to the increase in childlessness in Russia? Of course, the aggregate change is not the simple arithmetic sum of partial effects. Some process changes might have a stronger or weaker effect in the presence of changes in other processes. For example, the effect of the change in fertility in second unions will heavily depend on the likelihood of being in a second union which is subject to first union formation and dissolution risks. Microsimulation can help us to identify and better understand such interactions.
Looking at the post-transition cohort, we have already entered the domain of predictions. As data were collected 14 years after the transition, in reality no post-transition cohort has gone through its whole reproductive period. Thus, for cohort measures like childlessness, the assessment of consistency with other data sources is limited to a comparison with other projections. But we can also use our model for predictions under alternative assumptions on future changes in processes. We might have a theory that leads to the assumption that only parts of the observed changes are of a permanent nature (e.g. caused by cultural change) while others are transitory (e.g. resulting from economic crisis, therefore reversible with economic recovery). What would happen if fertility rates moved back to their initial values while slower (later) union formation persisted--or vice versa? Such an analysis can produce surprising results, as it is not always a reversal of the process which initially had the biggest overall impact that will generate the biggest opposite effect.
Are there policy implications? While our model is of course too simple for policy analysis, it does not require much imagination to see how microsimulation can support policy making.
Even being a very simple model, RiskPaths has around 130 parameter values which users should be able to set and store conveniently. We would expect these parameters to be well-organized in the microsimulation application, appearing as easy-to-access (or navigate) labelled tables which could be read or modified as required
When using a model we typically create different scenarios, i.e. different parameterizations of the model. We need to be able to save these scenarios so that certain simulations can be reproduced in future. Scenarios contain all parameter tables and, ideally, supplementary text descriptions or notes that outline the specific changes embedded in each scenario. Additionally, scenarios should include scenario settings, such as the number of simulated cases (given that RiskPaths is a case-based model), A large sample size will reduce Monte Carlo variation but comes at the cost of slower simulation runs. If we are only interested in broad aggregates, then smaller sample sizes might suffice. On the other hand, a detailed analysis of rare events or a detailed breakdowns of results (e.g. by age groups) would require large samples. Additionally, users might not wish to produce all available output. Narrowing down the desired output can again speed up simulations but also leads to a more concise and focused presentation of results according to user needs.
All of the above (parameter tables, descriptive notes, number of cases, choice of output to produce) is part of a scenario. For our RiskPaths applications, we would expect all this information to be stored together for a given scenario and we would expect it to be easily retrieved, viewed, and modified.
Microsimulation models can produce output on two levels: micro and macro. A microsimulation application could conceivably write all individual level characteristics and all their changes over time into a file and leave it to the user to analyse the resulting data file with statistical software. In the RiskPaths case, this would lead to a file storing the dates of all simulated events that occur over the simulated life course of each single individual. Only six events can happen in a simulated life, so each data record would contain at most six variables: four union formation / dissolution events, conception, and death. For more complex applications, file size and complexity could be enormous.
As well as such a longitudinal file, we might also be interested in cross-sectional output, recording the states of all individuals at a certain point in time. While the use of such a file is rather limited when simulating a single cohort, it would resemble a cross-sectional survey or population census in a population model.
Usually, a model user will not be interested in micro files per se but in the analysis that is performed on them. The user will typically aggregate data and produce summary indicators and tables. If model developers already know how simulated data will or should be analyzed, such measures and tables can already be calculated and produced within a microsimulation application run. In this case, users would not need to run additional statistical routines; they could see results immediately after a simulation was performed. In our RiskPaths model, output does not exceed a small number of tables and summary indicators which we expect to be produced within the application. We are interested in age-specific fertility rates, childlessness, the mean age at first conception, first conception by union status, and some mortality measures.
Just as with parameter tables, aggregated model output also requires organization. We might want to present some summary measures of one or several related behaviours together in a table and we surely want to order table output in a meaningful way. Additionally, as with parameters, we would expect table results to be labelled for easy reading and understanding.
Because all microsimulation results are subject to Monte Carlo variation, aggregated numbers are only one view of the results. We might also be interested in getting distributional information on each table value. Such information would help us to set an appropriate population size sufficient for a desired level of result precision.
A special type of micro-data output is the graphical display of individual careers. This can be a helpful feature, as it provides users with a window to the simulated individuals, and thus a way to see the operation of the statistical models. This can also be useful for model developers as it supports model debugging. Since RiskPaths is a training tool, we are interested in displaying how individual biographies result from statistical processes. Thus, besides life course events, we might also want to see how the risks of the alternative events change over time and life course situations.
So far, we have formed expectations about the content, display, and organization of model input and output data. From the user perspective, do we just have to add a start button to complete the microsimulation application? Almost all contemporary software applications contain help files. As users of microsimulation models, we should expect access to detailed online help, not only on the use of the modeling software itself but also on the model's specific elements and the interrelationships amongst those elements.
The user interface
Parameter tables
Performing a simulation run
Table output: aggregates and distributions
Model help and documentation
Graphical output of individual histories
In the remainder of this discussion we provide a quick explorative tour of the visual interface provided by Modgen for the RiskPaths model. To run RiskPaths, both the Modgen Prerequisites application and the RiskPaths executable have to be installed on your computer. As is true for all Modgen applications, RiskPaths contains a help system including documentation on the (model-independent) Modgen user interface and the actual RiskPaths model itself. Accordingly, in the description below, we concentrate on the central steps of running RiskPaths, leaving it to you to explore the model and software in depth with the assistance of the detailed help files.
All Modgen applications have the same graphical user interface (Figure 1) which consists of the following parts:
When starting the RiskPaths.exe application, the selection window and table frame are empty, as we first have to load (or create) a simulation scenario. To do so, follow the following steps:

Users of a Modgen application have control over all parameters contained in the model's parameter tables. An individual parameter table can be selected by clicking its list entry in the selection window. The table is then displayed in the display frame in which it can also be edited. Modgen parameter tables can have any number of dimensions, ranging from a parameter with a single checkbox to parameters with numerous characteristics or dimensions (e.g.region, sex, age, time).

Click the 'Run/resume' button or select 'Run/resume' from the 'Scenario' menu. The progress of the simulation is displayed in a progress dialog box. A small sample of 10,000 actors takes around 20 seconds to run. After the model run is complete, all output tables will have been updated by Modgen.
Simulation results are written to predefined output tables. Note that the values displayed in the output table represent only one of several possible views on the results. By right-clicking a table, a properties sheet for the table can be accessed. Among other things, this allows the display of distributional information (standard errors and the coefficient of variation) of all simulated values. Table contents can also be copied and pasted. You have the choice to copy the table as displayed, or all dimensions of the table at once (if there are more than two dimensions).
As is true with all Modgen applications, RiskPaths provides help files of various types. Two are related to Modgen itself--a general user guide for the visual interface plus release notes for Modgen. The other help files are model-specific. All Modgen applications contain a detailed encyclopaedic model documentation file. This documentation is automatically created from properly commented code.

The Modgen Biography Browser (BioBrowser) application is a tool for the graphical display of individual life courses. This view on the simulation results is especially useful for model debugging. In order to use the tool, the tracking feature has to be switched on in the scenario settings. The list of variables to be tracked also has to be declared by the model developer in the model code via a tracking statement. Modgen than tracks all changes of those variables included in the tracking statement for a sample of simulated actors (where the size of this sample is specified as one of the scenario settings).
To display biographies created by RiskPaths, just start the BioBrowser application and load the tracking-file of your simulation scenario, e.g.Base(trk).mdb.

RiskPaths.mpp (the main simulation file)
PersonCore.mpp
Behavioural Files
Tables.mpp
Tracking.mpp
Language translation file RiskPathsFR.mpp
The Modgen code of RiskPaths is organized into eight separate .mpp files, while all RiskPaths parameter values (because RiskPaths is a simple model) are contained in just a single .dat file. In principle, a model developer has complete freedom to decide how to organize the Modgen code in different files, but a modular organization as found in RiskPaths is recommended.
Figure 2: RiskPaths file organization
| Generated modules | Filename |
|---|---|
| Simulation engine | RiskPaths.mpp |
| Core actor file | PersonCore.mpp |
| Table definitions | Tables.mpp |
| Output tracking | Tracking.mpp |
| French language translations | RiskPathsFR.mpp |
| Behavioural modules | Filename |
| Mortality | Mortality.mpp |
| Fertility | Fertility.mpp |
| Union formations and dissolutions | Unions.mpp |
| Parameter File | Filename |
| Parameters of Baseline Scenario | Base(RiskPaths).dat |
Note that the .mpp code files often contain comments that resemble labels. Such comments are placed beside the declarations of symbols such as states, state levels, parameters, tables and table dimensions. Modgen does in fact interpret these comments as labels and subsequently uses them when tables or parameters are displayed within Modgen's visual interface. These labels are also used in the model's automatically generated encyclopaedic help file. Code comments that are used as labels begin with a two-character language identifier; an example is:
//EN Union status
Many such comments can be seen in the code examples that follow for RiskPaths.
For more detailed descriptions of modules, functions and events, notes that use the following syntax can also be placed in the code.
/*NOTE(Person.Finish, EN)
The Finish function terminates the simulation of an actor.
*/
These notes - besides documenting the code - are additionally used in the automatically generated encyclopaedic help file.
This file contains the code essential for the definition of the model type (e.g.case-based, continuous time) as well as the simulation engine, i.e. the code that runs the entire simulation. Because RiskPaths is a case-based model, the simulation engine code loops through all cases and processes the event queues of each case. The file also identifies the languages of the model. The code in this file is mostly model independent within a class of models (e.g.continuous time, case-based) and a version of it is provided automatically when using Modgen's built-in wizard to start a new Modgen project.
For the development of our case-based, continuous time cohort RiskPaths model with an actor 'Person', the code provided by the wizard requires very few modifications. The full code of this .mpp file is less than one page in length.
The only actor in RiskPaths is a person. In the file PersonCore.mpp, we have organized the code which is part of the actor declaration but not directly related to a specific behaviour. The file contains two age clocks defined as self-scheduling states (integer_age and age_status) and two actor functions, Start() and Finish(), which are performed at the creation of an actor and at her death, respectively.
In the Start() function we initialize the states time and age to 0. Both states are automatically created and maintained by Modgen and can only be changed in the Start() function. Their types depend on the model type; because RiskPaths is a continuous time model, time and age are continuous states.
The Finish() function must be called at the death event of an actor. Its role is to remove the actor from tables and from the simulation, and to recuperate any computer memory used by the actor.
All states and actor functions are declared in an "actor Person { };" block. To allow modularity in the organization of code by different life course domains, there can be multiple actor blocks in a project, typically one for each behavioural file.
The first code section of this module contains three type definitions. We first define a range LIFE.
range LIFE //EN Simulated age range
{
0,100
};
Range is a Modgen type which defines a range of integer values. RiskPaths limits the possible age range of persons to 100 years. This type is used to declare a derived state containing the age of a person in completed years.
The second type definition is used to divide continuous ages into 2.5 year age intervals starting at age 15.
partition AGEINT_STATE//EN 2.5 year age intervals
{
15, 17.5, 20, 22.5, 25, 27.5, 30, 32.5, 35, 37.5, 40
};
The third definition is a classification of union types. In general, if a range, partition, or classification is used in several files, it is good practice to define it in the core actor file.
classification UNION_STATE //EN Union status
{
US_NEVER_IN_UNION,//EN Never in union
US_FIRST_UNION_PERIOD1,//EN First union < 3 years
US_FIRST_UNION_PERIOD2,//EN First Union > 3 years
US_AFTER_FIRST_UNION,//EN After first union
US_SECOND_UNION,//EN Second union
US_AFTER_SECOND_UNION//EN After second union
};
In the following code segment we declare two derived actor states and two functions. The derived states for time intervals are used to change the values of parameters that vary over time. In our model integer_age is needed because mortality risks are dependent on age in years, whereas age_status comes into play because baseline risks for first conception and first union formation are modelled to change in 2.5 year intervals after the 15th birthday.
Both integer_age and age_status have to be maintained over the simulation. The Modgen concept of derived states allows us to have them maintained automatically. Both are derived from the state, age, (which is a special state that is generated and maintained automatically by Modgen). In order to split up age into the time intervals defined in the AGEINT_STATE partition, we make use of the Modgen function self_scheduling_split. The second derived state, integer_age, can be directly obtained using the Modgen function self_scheduling_int. In order to ensure that its value stays in the possible range of LIFE we convert it to type LIFE, which is done by the Modgen macro COERCE.
actor Person
{
//EN Current age interval
int age_status = self_scheduling_split(age, AGEINT_STATE);
//EN Current integer age
LIFE integer_age = COERCE( LIFE, self_scheduling_int(age) );
//EN Function starting the life of an actor
void Start();
//EN Function finishing the life of an actor
void Finish();
}
The remaining code of this module is the implementation of the Start() and Finish() functions. The Finish() function is left empty as we do not require any actions, other than those automatically performed by Modgen, to take place when an actor dies.
void Person::Start()
{
// Age and time are variables automatically maintained by
// Modgen. They can be set only in the Start function
age = 0;
time = 0;
}
/*NOTE(Person.Finish, EN) The Finish function terminates the simulation of an actor.
*/
void Person::Finish()
{
// After the code in this function (if any) is executed,
// Modgen removes the actor from tables and from the simulation.
// Modgen also recuperates any memory used by the actor.
}
In RiskPaths we distinguish three groups of behaviours: mortality, fertility and union formation/dissolution. Accordingly we have organized the code into three .mpp files: Mortality.mpp, Fertility.mpp and Unions.mpp. Each behavioural file is typically arranged in three sections:
Declaration of parameters (including any type definitions that are required, first)
Declarations of actor states and events
Implementation of events
This file defines the mortality event that ends the life of the simulated actor. Mortality.mpp is a typical behavioural module, and we follow a standard organization of the code: parameter declarations (with type definitions), actor declarations and event implementations.
Mortality is parameterized by death probabilities by age; thus, the probability to survive another year changes at each birthday. We also introduce a parameter which allows us to 'switch off' mortality. When it is used, every actor reaches the maximum age of 100 years (which can be useful for some types of fertility analysis). Figure 3 displays the mortality parameter tables of the RiskPaths application.
Figure 3: Mortality parameters

Parameters are declared within a "parameters {.};" code block. Modgen supports standard C++ numeric types, such as int, long, float, double, or Boolean ("logical" in Modgen's terminology), as well as the Modgen-specific range, partition and classification types that were introduced in PersonCore.mpp. The dimensionality of the parameters in the RiskPaths model is defined by classifications and ranges. The following code generates the parameters for RiskPaths, as displayed in Figure 3. For the annual death probabilities we use the range LIFE that was defined in PersonCore.mpp. The (parameter_group) statement groups the two mortality parameters in order to provide an ordered hierarchical selection list in the user interface (again, as displayed in Figure 3).
parameters
{
logical CanDie;//EN Switch mortality on/off
double ProbMort[LIFE];//EN Death probabilities
};
parameter_group P01_Mortality//EN Mortality
{
CanDie, ProbMort
};
Actors are described by states which are changed in events. States can be both continuous (integer or real) or categorical. In the mortality module, the state of interest is whether a person is alive or not, thus making it categorical in nature. The levels of a categorical state are defined with the Modgen classification command.
We declare a state life_status of type LIFE_STATE, which is initialized with LS_ALIVE at birth and set to LS_NOT_ALIVE by the death event. It is good practice to initialize all states by assigning initial values. Each initial value, however, must be enclosed in braces, i.e. {}-otherwise, the state is implemented as a derived state.
classification LIFE_STATE //EN Life status
{
LS_ALIVE,//EN Alive
LS_NOT_ALIVE//EN Dead
};
actor Person
{
LIFE_STATE life_status = {LS_ALIVE};//EN Life Status
event timeDeathEvent, DeathEvent;//EN Death Event
};
Events are declared in the actor Person {..} block using the keyword event. All events consist of a function which returns the time of the next event and a function containing the code describing the consequences of the event.
When mortality is activated, the timeDeathEvent function returns a random time based on the mortality parameter for the given year of age. In order to obtain random durations from probabilities, we assume constant mortality hazards within each period, i.e. between birthdays. (The exception is a death probability of 1, which leads to death immediately at the start of the age year). Note that any time later than the next birthday will lead to the birthday event taking precedence over the mortality event; that is, the birthday event will censor the mortality event.
TIME Person::timeDeathEvent()
{
TIME event_time = TIME_INFINITE;
if (CanDie)
{
if (ProbMort[integer_age] >= 1)
{
event_time = WAIT(0);
}
else
{
event_time = WAIT(-log(RandUniform(3)) /
-log(1 - ProbMort[integer_age]));
}
}
// Death event can not occur after the maximum duration of life
if (event_time > MAX(LIFE))
{
event_time = MAX(LIFE);
}
return event_time;
}
The event implementation function DeathEvent is straightforward. It sets the life_status to LS_NOT_ALIVE and calls the function Finish(), the latter removing the actor from the simulation and recovering any memory used by that actor.
void Person::DeathEvent()
{
life_status = LS_NOT_ALIVE;
Finish();
}
This file defines and implements the first pregnancy event. As we are only interested in the study of childlessness in RiskPaths, no other fertility-related event is simulated. Fertility.mpp is a behavioural module, and again we follow the same standard organization of the code: type definitions, parameter declarations, actor declarations and event implementations.
Fertility is parameterized by both a baseline pregnancy risk by 2.5 year age intervals starting at the 15th birthday and a relative risk factor dependent on the union status and duration. We thus define two parameters: AgeBaselinePreg1 and UnionStatusPreg1.
Figure 4: Fertility parameters

Fertility risks use a time partition to define the columns. For the age baseline we use the partition AGEINT_STATE that was defined in PersonCore.mpp. The possible union states for the relative risk factors use the classification UNION_STATE which is declared in PersonCore.mpp as well.
parameters
{
//EN Age baseline for first pregnancy
double AgeBaselinePreg1[AGEINT_STATE];
//EN Relative risks of union status on first pregnancy
double UnionStatusPreg1[UNION_STATE];
};
parameter_group P02_Ferility //EN Fertility
{
AgeBaselinePreg1, UnionStatusPreg1
};
The only state of the fertility module is parity_status, which can only have two levels: 'childless' and 'pregnant'. (This is because RiskPaths no longer simulates an actor's fertility events after first conception).
In Fertility.mpp, we only model one event: pregnancy. The corresponding pair of event functions is timeFirstPregEvent and FirstPregEvent.
classification PARITY_STATE //EN Parity status
{
PS_CHILDLESS,//EN Childless
PS_PREGNANT//EN Pregnant
};
actor Person
{
//EN Parity status derived from the state parity
PARITY_STATE parity_status = {PS_CHILDLESS} ;
//EN First pregnancy event
event timeFirstPregEvent, FirstPregEvent;
};
As is true with all Modgen events, the first pregnancy event is implemented in two parts. The first determines the timing of the event, the second the consequences if the event happens. The timeFirstPregEvent function verifies if the actor is currently at risk and, if so, draws a random duration based on the underlying piecewise proportional constant hazard regression model parameterized by an age baseline and relative risk by union status. Accordingly, the hazard rate is calculated from the two parameters AgeBaselinePreg1 and UnionStatusPreg1. A random duration can be obtained from a uniform distributed random number by the transformation:
randdur=-log(RandUniform(1))/hazard.
The Modgen function RandUniform() returns a uniform distributed random number between 0-1. The function takes an integer argument used to assign a different independent random number stream to each random number function in the code. When omitted, Modgen automatically writes back a unique index into the .mpp file before translation into C++ code.
When the event happens, the state "parity" is increased by 1. (Note that the derived state parity_status is changed to "PS_PREGNANT" automatically).
TIME Person::timeFirstPregEvent()
{
double dHazard = 0;
TIME event_time = TIME_INFINITE;
if (parity_status == PS_CHILDLESS)
{
dHazard = AgeBaselinePreg1[age_status]
* UnionStatusPreg1[union_status];
if (dHazard > 0)
{event_time = WAIT(-log(RandUniform(1)) / dHazard);
}
}
return event_time;
}
void Person::FirstPregEvent()
{
parity++;
}
The programming of union transitions introduces only minor new concepts in Modgen programming--thus, the following code discussion is mainly limited to union dissolutions. The hazard rates for both first and second union dissolution events are stored in the same parameter table, as they each use the same time intervals of union duration.
In order to construct a parameter with the dimensions time and union order, we define a time partition and a classification:
partition UNION_DURATION//EN Duration of current union
{
1, 3, 5, 9, 13
};
classification UNION_ORDER //EN Union order
{
UO_FIRST,//EN First union
UO_SECOND//EN Second union
};
parameters
{
.
//EN Union Duration Baseline of Dissolution
double UnionDurationBaseline[UNION_ORDER][UNION_DUR];
.
};
Figure 5: Union dissolution parameters

In the timeUnion1DissolutionEvent() function, hazard rates for first union dissolution are obtained as:
dHazard = UnionDurationBaseline[UO_FIRST][union_duration];
Accordingly, timeUnion2DissolutionEvent() references the second row from the parameter:
dHazard = UnionDurationBaseline[UO_SECOND][union_duration];
As opposed to the processes discussed so far, the union dissolution processes do not start at a predefined time (e.g.the 15th birthday) but at union formation events. The union duration spell is defined as a derived self-scheduling state in the following form:
//EN Currently in an union
logical in_union = (union_status == US_FIRST_UNION_PERIOD1
|| union_status == US_FIRST_UNION_PERIOD2
|| union_status == US_SECOND_UNION);
//EN Time interval since union formation
intunion_duration = self_scheduling_split(
active_spell_duration( in_union, TRUE), UNION_DURATION);
With respect to union formation, the implementation of the clock which changes the union duration state union_status from US_FIRST_UNION_PERIOD1 to US_FIRST_UNION_PERIOD2 after three years in a first union deserves some discussion. In contrast to the self-scheduling derived states used for all other clocks of the model, here - mainly as an illustration of this alternative - we explicitly implement the clock as an event itself. This event occurs after three years in the first union. The clock is set at first union formation. The actor declaration includes a state which records the time of the status change as well as the event declaration.
actor Person
{
.
//EN Time of union period change
TIMEunion_period2_change = {TIME_INFINITE};
//EN Union period change event
eventtimeUnionPeriod2Event, UnionPeriod2Event;
};
The time for the state change is set in the first union formation event. In the code sample, WAIT is a built-in Modgen function that returns the time of the current event, plus a specified time (in our example, three years).
void Person::Union1FormationEvent()
{
unions++;
union_status = US_FIRST_UNION_PERIOD1;union_period2_change = WAIT(3);
}
The event implementation is straight forward:
TIME Person::timeUnionPeriod2Event()
{
return union_period2_change;
}
void Person::UnionPeriod2Event()
{
if (union_status == US_FIRST_UNION_PERIOD1)
{
union_status = US_FIRST_UNION_PERIOD2;
}
union_period2_change = TIME_INFINITE;
}
Modgen provides a very powerful and flexible cross-tabulation facility to report model results. The programming of each output table usually requires only a few lines of code. RiskPaths contains only one table file which contains the declarations of all of its output tables-however, for more detailed models, it is advisable to split up table declarations by behavioural groups.
The basic syntax for tables is displayed in Figure 6. The two central elements of a table declaration are the captured classificatory dimensions (defining when an actor enters and leaves a cell) and the analysis dimension (recording what happens while an actor is in that cell). Typical classificatory dimensions are age or time intervals (e.g.fertility by age), states (e.g.fertility by union status), or a combination of both. Modgen does not limit the number of dimensions.
The analysis dimension can contain many expressions, which can be states or derived states. Modgen provides a very useful list of special derived state functions which record, for example, the number of occurrences of certain events, the number of changes in states, or the duration in states. Two particularly helpful concepts are the keyword unit and the derived state function duration(). The former, i.e. unit, records the number of actors entering a table cell, whereas duration() records the total time an actor stayed in the cell.
Tables can contain filter criteria for defining if and under which conditions actor characteristics will be recorded. The Modgen table concepts are best understood by concrete examples as given below. As the full wealth of the Modgen table language goes beyond the scope of this chapter, you are also invited to consult the Modgen Developer's Guide.
Figure 6: Table Syntax
table actor_name table_name//EN table label
[filter_criteria]
{
dimension_a *//EN dimension label
.
{
analysis_dimension_expression_x,//EN expression label
.
}
* dimension_n//EN dimension label
.
};
The first table example contains summary values of our simulation and has no dimensions, i.e. cells apply to the entire population over the entire simulation period. We make use of the Modgen keyword unit, which counts the number of actors entering the cell of a table (in our example, the simulation itself), and the Modgen function duration() which sums up the time actors stay in this cell (in our example, the total years lived by all actors in the simulation). The average age at death of all actors in the simulation is then obtained by dividing duration() by unit. As for parameter declarations, comments placed in the code are used as labels in the application. (Note that in the table declaration below, the 'decimals=3' portion of the comment is used to determine the number of decimal places in the table; this part of the comment does not carry through to the label used in the report).
table Person T01_LifeExpectancy //EN 1) Life Expectancy
{
{
unit, //EN Total simulated cases
duration(), //EN Total duration
duration()/unit //EN Life expectancy decimals=3
}
};
In the second table we record the population by age. For output by age, we use integer_age as table dimension.
table Person T02_TotalPopulationByYear//EN Life table
{
//EN Age
integer_age *
{
unit,//EN Population start of year
duration()//EN Average population in year
}
};
unit and duration() now refer to the number of entrances into - and durations within - one year age intervals. unit thus counts the actors present at the beginning of each year, while duration() refers to the average population in the year.
As well as the keyword unit and the derived state function duration(), states and a set of other derived state functions can be used in tables. If using a state without a function, Modgen records the change of the state while in a particular cell, i.e. the value of the state when the cell is exited minus the value of the state when the cell was entered.
The expression transitions(parity_status, PS_CHILDLESS, PS_PREGNANT) / duration() therefore records the (age specific) fertility as the number of birth events divided by the average number of women by year of age.
The second expression is used to calculate the true rate, i.e. the number of birth events by exposure time. A woman is under exposure for first pregnancy when childless. We thus divide the number of events by the term 'duration( parity_status, PS_CHILDLESS )'.
The table dimension is age in full years. As fertility is 0 until age 15 and very low after 40, the age periods before 15 and after 40 are not further divided. We thus define a partition AGE_FERTILEYEARS which is used in the self_scheduling_split statement that defines the table dimension.
partition AGE_FERTILEYEARS //EN Fertile age partition
{
15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
32, 33, 34, 35, 36, 37, 38, 39, 40
};
table Person T03_FertilityByAge//EN Age-specific fertility
{
//EN Age
self_scheduling_split(age,AGE_FERTILEYEARS) *
{
//EN First birth rate all women decimals=4
transitions(parity_status, PS_CHILDLESS, PS_PREGNANT) / duration()
//EN First birth rate woman at risk decimals=4
transitions(parity_status, PS_CHILDLESS, PS_PREGNANT) / duration( parity_status, PS_CHILDLESS )
}
};
Table 4 produces first birth rates by the 2.5 year age groups used for parameterization. We also add an additional dimension, namely the union status; we thus obtain simulated values of the model parameters.
table Person T04_FertilityRatesByAgeGroup //EN Fertility rates by age group
[parity_status == PS_CHILDLESS]
{
{
parity / duration() //EN Fertility decimals=4
}
* self_scheduling_split(age, AGEINT_STATE)//EN Age interval
* union_status//EN Union Status
};
Table 5 calculates two cohort measures of fertility -- average age at first conception and childlessness. To obtain the age at pregnancy we use the Modgen derived state function value_at_transitions(parity_status, PS_CHILDLESS ,PS_PREGNANT, age) which returns the value of one state (age) at a specific transition of another state, namely when parity_status changes from PS_CHILDLESS to PS_PREGNANT.
table Person T05_CohortFertility//EN Cohort fertility
{
{
//EN Av. age at 1st pregnancy decimals=2
value_at_transitions(parity_status,PS_CHILDLESS,PS_PREGNANT,age)/
transitions(parity_status, PS_CHILDLESS, PS_PREGNANT),
//EN Childlessness decimals=4
1 - transitions(parity_status, PS_CHILDLESS, PS_PREGNANT) / unit,
//EN Percent one child decimals=4
transitions(parity_status, PS_CHILDLESS, PS_PREGNANT) / unit
}
};
In table 6 we use an example of a filter which triggers a person exactly at the entrance of a state, in our case at the occurrence of pregnancy. We are interested in the union status at first conception. Note that this filter also excludes women who stay childless.
table Person T06_BirthsByUnion //EN Pregnancies by union status & order
[trigger_entrances(parity_status, PS_PREGNANT)]
{
{
unit//EN Number of pregnancies
}
*union_status+//EN Union Status at pregnancy
};
Like table 4 this table reproduces a parameter table. While such an output table does not contain any information (for a sufficiently large sample size it will come close to the original model parameters) it is useful for model validation and to assess Monte Carlo variability.
table Person T07_FirstUnionFormation//EN First union formation
[parity_status == PS_CHILDLESS]
{
//EN Age group
self_scheduling_split(age, AGEINT_STATE) *
{
//EN First union formation risk decimals=4
entrances(union_status, US_FIRST_UNION_PERIOD1)
/ duration(union_status, US_NEVER_IN_UNION) }
};
Like parameters, output tables can also be grouped for a more meaningful presentation of results. In the application RiskPaths, we distinguish three groups of tables: life tables, fertility tables, and tables for union status.
table_group TG01_Life_Tables//EN Life tables
{
T01_LifeExpectancy, T02_TotalPopulationByYear
};
table_group TG02_Birth_Tables//EN Fertility
{
T03_FertilityByAge, T04_FertilityRatesByAgeGroup, T05_CohortFertility
};
table_group TG03_Union_Tables//EN Unions
{
T06_BirthsByUnion, T07_FirstUnionFormation
};
The track{} code block defines the list of states to be recorded longitudinally for visual BioBrowser output. This command is frequently placed in table files. In our model, however, we have decided to code a separate Tracking.mpp file, since we also track risk patterns calculated as derived states.
track Person
{
integer_age,
life_status,
age_status,
union_duration,
dissolution_duration,
unions,
parity_status,
union_status,
preg_hazard,
formation_hazard,
dissolution_hazard
};
The file also includes the declaration of three derived states. We have used the derived state concept to calculate the three main hazard rates (pregnancy, union formation, and union dissolution) for BioBrowser output. They are for illustrative purposes only, as all hazard rates, broken down by union order, are calculated in the event functions.
The declaration of the derived states preg_hazard, formation_hazard, and dissolution_hazard are also good syntax examples of how derived states can be built from simple states by if-else constructs.
actor Person
{
//EN Pregnancy hazard
double preg_hazard = (parity_status == PS_CHILDLESS) ?
AgeBaselinePreg1[age_status] *
UnionStatusPreg1[union_status] : 0;
//EN Union formation hazard
double formation_hazard = (union_status != US_NEVER_IN_UNION
&& union_status != US_AFTER_FIRST_UNION) ? 0 :
((union_status == US_NEVER_IN_UNION) ?
AgeBaselineForm1[age_status] :
SeparationDurationBaseline[dissolution_duration] );
//EN Union dissolution hazard
double dissolution_hazard = (union_status != US_FIRST_UNION_PERIOD1 && union_status != US_FIRST_UNION_PERIOD2
&& union_status != US_SECOND_UNION) ? 0 :
((union_status == US_SECOND_UNION) ?
UnionDurationBaseline[UO_SECOND][union_duration] :
UnionDurationBaseline[UO_FIRST][union_duration]);
};
This .mpp file only exists for models that are defined in Modgen to be multilingual (which for RiskPaths implies English and French). Even for a bilingual model, however, one of English or French is still deemed to be the first or primary language of the model. English was chosen as the primary language when RiskPaths was originally developed, and so the RiskPathsFR.mpp file essentially contains translations for the model's labels and notes in the other language, i.e. French. (If the original primary language of RiskPaths had been French, this translation file would have been called RiskPathsEN.mpp and it would have contained English translations of the labels and notes for the model.)
Normally, all notes and labels are entered as code comments in the source .mpp files, using the primary language of the model, as has been illustrated several times in the previous examples. The corresponding translations are subsequently placed in this separate .mpp file.
An add-in "Translation Assistant" software tool is available with Modgen to help the translation process by verifying the complete set of terms that require translation and by adding unique versions of the translated entities to the appropriate .mpp file (RiskPathsFR.mpp in our example).