InitialData Silently Leaves a Parameter Empty

Two parameters, the same InitialData, and only one of them has values at startup.

Example

Take two sets. s_Periods gets its elements from its own definition, while s_Products is filled by a procedure called from MainInitialization:

 1Set s_Periods {
 2    Index      : i_t;
 3    Definition : data { t1, t2, t3 };
 4}
 5
 6Set s_Products {
 7    Index : i_p;
 8}
 9
10Procedure MainInitialization {
11    Body : {
12        s_Products := data { productA, productB, productC };
13    }
14}

Now declare one parameter over each set, both with the same InitialData:

1Parameter p_PeriodFactor {
2    IndexDomain : i_t;
3    InitialData : 1;              ! 1 for t1, t2 and t3
4}
5
6Parameter p_ProductFactor {
7    IndexDomain : i_p;
8    InitialData : 1;              ! empty at startup
9}

After the project opens, p_PeriodFactor holds the value 1 for every period, but p_ProductFactor has no data at all, even though s_Products now contains three elements.

Nothing distinguishes the two declarations. What differs is when each index set gets its contents.

The Order of Initialization

  1. Definitions and InitialData attributes are worked first, in declaration order, before any procedure runs.

  2. MainInitialization and PostMainInitialization run afterwards.

So when p_ProductFactor is initialized in step 1, s_Products is still empty, because it is only filled inside MainInitialization. There is nothing for the InitialData value to be assigned to. The parameter is not initialized and then cleared; it is initialized over an empty domain, which produces no data at all.

s_Periods works because it has its elements before step 1. Here they come from its own definition, but a literal or data read at load time would work the same way.

No error is reported, because nothing went wrong. The assignment covered every tuple in the domain, and there were none.

What To Do Instead

Assign it in a procedure, after the set is filled. If the set is populated in MainInitialization, the assignment belongs after that point, and PostMainInitialization is the natural home:

1Procedure PostMainInitialization {
2    Body : {
3        p_ProductFactor(i_p) := 1;
4    }
5}

Or give the parameter a definition rather than initial data, if the value is genuinely constant. A definition is re-evaluated when its domain changes, so it follows the set instead of predating it:

1Parameter p_ProductFactor {
2    IndexDomain : i_p;
3    Definition  : 1;
4}

The one thing not to do is move the declaration, hoping declaration order will fix it. Declaration order decides the sequence within step 1; it cannot move an InitialData attribute after a procedure.