Entity Tables - openmpp/openmpp.github.io GitHub Wiki
Home > Model Development Topics > Entity Tables
This topic describes entity tables in depth. This topic is under construction and consists mostly of stub subtopics.
- Introduction and concepts
- Table properties
- Snapshot tables
- Duration tables
- Run results
- Increments
- Accumulators
- Shorthand
- Increment Validity Increment validity
- Operators
Content to follow. Topic outline is incomplete. The only complete subtopic is Increment Validity.
Table properties, ordered arbitrarily,
follow the table
keyword and precede the kind of entity which the table tabulates.
Possible values are
-
untransformed
Indicates that the table is to be computed ignoring weights and population scaling. -
snapshot
Indicates that the table uses a filter which istrue
only instantaneously and that increments are values observed at those instantaneous moments. See section snapshot tables below for more. -
screened[1-4]
Indicates that, after the simulation of a sub completes, table values are to be transformed using a model developer-supplied transformation function. See Screened Tables for more.
under construction.
under construction.
Entity tables are computed for each sub,
and run-level results computed from the sub-level results.
Several methods are available to compute run-level results
through the measures_method
option, as follows:
measures_method |
Description of run result |
---|---|
average |
Measures are averaged across subs. ex #1: measure count run: AVG(count) ex #2: measure income run: AVG(income) ex #3: measure: income/count run: AVG(income/count)
|
aggregate |
accumulators are averaged over subs, then the measure calculated using those. ex #1: measure: count run: AVG(count) ex #2: measure: income run: AVG(income) ex #3: measure: income/count run: AVG(income)/(AVG(count)
|
assemble |
accumulators are summed over subs, and the measure computed using those ex #1: measure: count run: SUM(count) ex #2: measure: income run: SUM(income) ex #2: measure: income/count run: SUM(income)/SUM(count)
|
In these examples, income
and count
are sums which vary over subs,
and the run result shows the operations performed on those subs to produce the run result.
An increment is based on the value of an attribute when an entity enters a table cell and the value when it exits the cell.
An entity enters a table cell at simulation entry or when the table filter becomes true
.
An entity leaves a table cell at simulation exit or when the table filter becomes false
.
An entity changes cell when attributes used in the classification dimensions of the table change value.
Keyword | Description |
---|---|
value_in | The attribute value when the entity enters the table cell. |
value_out | The attribute value when the entity leaves the table cell. |
delta | The difference in the attribute value between cell exit and cell entrance. |
nz_value_in | The non-zero count of attribute value on entrance. |
nz_value_out | The non-zero count of attribute value on exit. |
nz_delta | The non-zero count of the difference in value between exit and entrance. |
value_in2 | The square of the value on entrance. |
value_out2 | The square of the value on exit. |
delta2 | The square of the difference in value between exit and entrance. |
Also referred to in this wiki as statistics.
A table is weighted if the model was built with weighted_tabulation
enabled
and the table is not untransformed
.
Keyword | Description | Notes |
---|---|---|
unit | Count of increments, weighted if the table is weighted | |
sum | Sum of increments, weighted if the table is weighted | |
minimum | ||
maximum | ||
mean | If unweighted, a numerically stable mean is computed. If weighted, a 'naive' estimate (quotient) is computed. | |
variance | Unweighted tables only. A numerically stable variance is computed. | |
stdev | Unweighted tables only. Derived from a numerically stable computed variance. | |
P2 | Unweighted tables only. | |
P5 | " | |
P10 | " | |
P20 | " | |
P25 | Q1 | " |
P30 | " | |
P40 | " | |
P50 | Median | " |
P60 | " | |
P70 | " | |
P75 | Q3 | " |
P80 | " | |
P90 | " | |
P95 | " | |
P98 | " | |
P99 | " | |
gini | " |
These keywords can be a more compact way of specifying commonly-used combinations of increments and accumulators.
Keyword | Equivalent |
---|---|
delta(x) | sum(delta(x)) |
delta2(x) | sum(delta2(x)) |
nz_delta(x) | sum(nz_delta(x)) |
value_in(x) | sum(value_in(x)) |
value_in2(x) | sum(value_in2(x)) |
nz_value_in(x) | sum(nz_value_in(x)) |
value_out(x) | sum(value_out(x)) |
value_out2(x) | sum(value_out2(x)) |
nz_value_out(x) | sum(nz_value_out(x)) |
max_delta(x) | maximum(delta(x)) |
max_value_in(x) | maximum(value_in(x)) |
max_value_out(x) | maximum(value_out(x)) |
min_delta(x) | minimum(delta(x)) |
min_value_in(x) | minimum(value_in(x)) |
min_value_out(x) | minimum(value_out(x)) |
This subtopic contains the following sections:
- Non-numeric floating point values
- Non-numeric values in OpenM++
- Increments and accumulators
- Invalid table increments
- Disabling table increment errors
A floating point number in OpenM++ is a C++ IEEE double
or float
, which is supported on most CPU hardware.
It can hold an exact value like 123
or 0.5
or an approximation to a Real number like 0.1
or pi
.
A floating point number can also hold one of three special non-numeric values: +inf
, -inf
, or NaN
(indeterminate).
Non-numeric values arise naturally from arithmetic operations or function calls, e.g.
Expression | Result |
---|---|
1.0 / 0.0 |
+inf |
-1.0 / 0.0 |
-inf |
0.0 / 0.0 |
NaN |
log(0.0) |
-inf |
log(-1.0) |
NaN |
sqrt(-1.0) |
NaN |
exp(710.0) |
+inf |
Note: The C++ specification for std::exp
guarantees a result of +inf if the argument is greater than 709.8
.
Numeric values can sometimes result from floating point operations or function calls with non-numeric arguments, e.g.
Expression | Result |
---|---|
1.0 / +inf |
0.0 |
atan(+inf) |
pi/2 |
exp(-inf) |
0.0 |
exp(log(0.0)) |
0.0 |
Logical comparison operators can have non-numeric arguments, e.g.
Expression | Result |
---|---|
+inf > 42.0 |
true |
-inf < +inf |
true |
NaN < +inf
|
false |
NaN == NaN
|
false |
NaN != NaN
|
false |
Note: The C++ specification states that all operators with NaN return NaN, including all comparison operators,
notably the ==
operator in the preceding table.
The C++ library contains functions to determine non-numeric values:
std::isnan
determines if a floating point number is NaN,
std::isinf
if it is +inf or -inf,
and
std::isfinite()
if it is finite, i.e. a garden variety floating point value.
Arithmetic operations involving +inf or -inf typically result in non-numeric values, e.g.
Expression | Result |
---|---|
+inf + 1.0 |
+inf |
+inf - 1.0 |
+inf |
+inf + +inf |
+inf |
sqrt(+inf) |
+inf |
inf / inf |
NaN |
This is notably the case for NaN
, which propagates in arithmetic operations and mathematical functions, e.g.
Expression | Result |
---|---|
NaN + 1.0 |
NaN |
+inf + NaN |
NaN |
sqrt(NaN) |
NaN |
Sometimes operations involving +inf
or -inf
can produce NaN
, e.g.
Expression | Result |
---|---|
+inf - +inf |
NaN |
+inf / +inf |
NaN |
+inf / 0.0 |
NaN |
A general rule of thumb is that non-numeric floating point values propagate to results in arithmetic operations and mathematical functions.
[back to increment validity]
[back to topic contents]
Non-numeric values are used in several ways in OpenM++:
- Global time is initialized to
-inf
before each case or replicate/sub. - All event times are initialized to
+inf
before each case or replicate/sub. - Derived parameters of floating point type are initialized to
NaN
(all cells). - An attribute of floating point type (
float
,double
,real
, orTime
) can have a non-numeric value, depending on model logic. - A
maximum
table accumulator is initialized to-inf
, and aminimum
table accumulator to+inf
.
[back to increment validity]
[back to topic contents]
Each cell of an entity table contains one or more accumulators specified in the expression dimension of the table.
As attributes change value during a run,
increments
are pushed to
accumulators
of the current cell of the table.
An accumulator might be a running count of increments, the running sum of increments,
the current maximum value,
or a collection of all pushed values.
When a run completes,
statistics are extracted from the table accumulators for each cell to compute final values for that table cell.
For example,
the median P50
for an attribute in a table cell is extracted from the accumulator underlying P50
,
which is a collection of all increments pushed to the cell during the run.
The P50
statistic is computed by sorting that collection and finding the middle value
(or the average of the two middle values if the number of increments in the collection is even).
[back to increment validity]
[back to topic contents]
Some accumulators handle increments of +inf or -inf in an expected and natural way, e.g.
an increment of +inf to a maximum
accumulator,
or an increment of +inf or -inf to a P50
accumulator.
However, a non-numeric increment can cause an accumulator to become pegged to a non-numeric value. This is particularly true for a NaN increment.
Specifically,
- Pushing an increment with value
+inf
or-inf
to asum
orgini
accumulator nullifies the effect of any previous or subsequent increment. - Pushing an increment with value
NaN
to any accumulator nullifies the effect of any previous or subsequent increments.
In other words, an increment of one single entity, perhaps the result of a rarely occurring corner condition in model code, can cause an entire table cell to become empty. OpenM++ treats that as an error in table design or model logic, halts the run, and writes a log message like
Simulation error: Invalid increment -inf in table 'IncrementTestTable' using attribute 'my_dbl' on or after event 'MortalityEvent' in entity_id 208 in simulation member 0 with combined seed 1637697257 when current time is 88.06346668067070
This particular error message manifested in a version of the Alpha2
model
which was modified to deliberately produce an increment error in the table
table Person IncrementTestTable
[integer_age >= 50]
{
{
value_out(my_dbl)
}
};
The root cause of an invalid increment often occurs in a different event than the one responsible for pushing the increment.
In the error message above, the invalid increment was detected when the entity was exiting the simulation after the MortalityEvent
.
The attribute my_dbl
likely assumed a non-numeric value earlier in the simulation causing the invalid increment later.
The root cause of an invalid increment can be probed using Event Trace to examine the evolution of the specific attribute in the specific entity given in the runtime error message.
To enable event trace in the model, the following statement must be added to model code:
options event_trace = on;
and the model executable must be invoked with the argument -OpenM.IniAnyKey
.
The following EventTrace
settings (in an ini
file) output all events and all changes in my_dbl
in entity 208:
[OpenM]
LogToFile = true
TraceToFile = true
[EventTrace]
; format
ReportStyle = readable
NameColumnWidth = 20
; filters
SelectedEntities = 208
; events
ShowEvents = yes
; attributes
ShowAttributes = yes
SelectedAttributes = my_dbl
This produces the following trace file output:
Time Entity Age Id Trace Value Name Remarks
0.000000 Person 0.000000 208 ENTER
0.000000 Person 0.000000 208 attr 0 my_dbl initial
0.000000 Person 0.000000 208 EVENT SpawnEvent
0.500000 Person 0.500000 208 EVENT EyeColourChangeEvent
1.000000 Person 1.000000 208 EVENT FirstBirthdayEvent
2.632435 Person 2.632435 208 EVENT HappinessReversalEvent
2.632435 Person 2.632435 208 attr -inf my_dbl was 0
10.105425 Person 10.105425 208 EVENT MoveEvent
25.755745 Person 25.755745 208 EVENT HappinessReversalEvent
26.741666 Person 26.741666 208 EVENT StartPlayingEvent
30.141256 Person 30.141256 208 EVENT MoveEvent
32.641206 Person 32.641206 208 EVENT MoveEvent
34.927079 Person 34.927079 208 EVENT HappinessReversalEvent
54.778775 Person 54.778775 208 EVENT HappinessReversalEvent
59.496134 Person 59.496134 208 EVENT StartPlayingEvent
60.500447 Person 60.500447 208 EVENT HappinessReversalEvent
68.935493 Person 68.935493 208 EVENT MoveEvent
76.110163 Person 76.110163 208 EVENT HappinessReversalEvent
78.448787 Person 78.448787 208 EVENT StartPlayingEvent
79.197382 Person 79.197382 208 EVENT MoveEvent
87.282644 Person 87.282644 208 EVENT HappinessReversalEvent
88.063467 Person 88.063467 208 EVENT MortalityEvent
88.063467 Person 88.063467 208 EXIT
This trace output shows that the my_dbl
attribute first assumed the non-numeric value -inf
during the HappinessReversalEvent
at an early age,
before the entity was in scope of the table filter.
Here's the model code responsible for the invalid increment error later in the simulation:
entity Person
{
double my_dbl;
void update_funny_numbers(void);
hook update_funny_numbers, HappinessReversalEvent, 43;
};
void Person::update_funny_numbers(void)
{
double x = 0.0;
my_dbl = std::log(x); // is -inf
}
If necessary, a Debug version of the model can be built and run with a conditional break point added to the first line of code in the event identified by Event Trace.
In this example, the break point could be set to the first line of HappinessReversalEvent
,
with condition entity_id == 208
.
Here's the model source code of the event which was the root cause of the invalid increment.
void Person::HappinessReversalEvent()
{
happy = !happy;
if ( !happy && playing ) {
// stop playing if unhappy
playing = FALSE;
}
if (happy && my_first_happy_time == TIME_INFINITE) {
my_first_happy_time = time;
}
hook_HappinessReversalEvent();
}
When the break point is hit,
execution can be stepped line by line in the debugger
until the code responsible for setting my_dbl
to a non-numeric value is found.
[back to increment validity]
[back to topic contents]
Normal handling of an invalid table increment can be controlled by the following option:
options verify_valid_table_increment = off; // default is on
If this option is off
, a warning like the following will be written to the log on each run:
Warning : invalid table increment is not detected with verify_valid_table_increment = off
A table with a non-numeric cell will display as empty in the UI and in csv
export.
[back to increment validity]
[back to topic contents]
Keyword | Description |
---|---|
interval | |
event |