1e_inputs.gms 26.6 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
$ontext
This file is part of Backbone.

Backbone is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

Backbone is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public License
along with Backbone.  If not, see <http://www.gnu.org/licenses/>.
$offtext

18
19
20
21
* =============================================================================
* --- Load Input Data ---------------------------------------------------------
* =============================================================================

22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
$ifthen exist '%input_dir%/inputData.gdx'
    $$gdxin  '%input_dir%/inputData.gdx'
    $$loaddcm grid
    $$loaddc node
    $$loaddc flow
    $$loaddc unittype
    $$loaddc unit
    $$loaddc unitUnittype
    $$loaddc unit_fail
    $$loaddc fuel
    $$loaddc unitUnitEffLevel
    $$loaddc uFuel
    $$loaddc effLevelGroupUnit
    $$loaddc p_gn
    $$loaddc p_gnn
    $$loaddc p_gnu
    $$loaddc p_gnuBoundaryProperties
    $$loaddc p_unit
    $$loaddc ts_unit
    $$loaddc restype
    $$loaddc restypeDirection
    $$loaddc restypeReleasedForRealization
    $$loaddc p_nReserves
    $$loaddc p_nuReserves
    $$loaddc p_nnReserves
    $$loaddc p_nuRes2Res
    $$loaddc ts_reserveDemand
    $$loaddc p_gnBoundaryPropertiesForStates
    $$loaddc p_gnPolicy
    $$loaddc p_uFuel
    $$loaddc flowUnit
    $$loaddc gngnu_fixedOutputRatio
    $$loaddc gngnu_constrainedOutputRatio
    $$loaddc emission
    $$loaddc p_fuelEmission
    $$loaddc ts_cf
*    $$loaddc p_fuelPrice // Disabled for convenience, see line 278-> ("Determine Fuel Price Representation")
    $$loaddc ts_fuelPriceChange
    $$loaddc ts_influx
    $$loaddc ts_node
    $$loaddc t_invest
    $$loaddc p_storageValue
    $$loaddc group
    $$loaddc uGroup
    $$loaddc gnuGroup
    $$loaddc gn2nGroup
    $$loaddc gnGroup
    $$loaddc p_groupPolicy
    $$loaddc p_groupPolicy3D
    $$loaddc gnss_bound
    $$loaddc uss_bound
    $$gdxin
$endif
75

76
* Reads changes or additions to the inputdata through changes.inc file.
77
78
79
$ifthen exist '%input_dir%/changes.inc'
   $$include '%input_dir%/changes.inc'
$endif
80

81
* Read changes to inputdata through gdx files (e.g. node2.gdx, unit2.gdx, unit3.gdx) - allows scenarios through Sceleton Titan Excel files.
82
83
$include 'inc/1e_scenChanges.gms'

84

85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108

$ontext
* --- sets with 'empty' to enable GDX imports in Sceleton Titan - currently removed when forming gnu (below) and uft (periodicLoop.gms) sets
* This list can be removed once this has been tested.
node
unit
gngnu_constrainedOutputRatio
restype
restypeReleasedForRealization
p_gnn
p_nnReserves
p_gnuBoundaryProperties
ts_node
ts_reserveDemand
ts_unit
p_storageValue
group
gnuGroup
gnGroup
gn2nGroup
gnss_bound
$offtext


109
110
111
112
113
114
115
116
* =============================================================================
* --- Initialize Unit Related Sets & Parameters Based on Input Data -----------
* =============================================================================


* --- Generate Unit Related Sets ----------------------------------------------

// Set of all existing gnu
117
118
119
120
121
122
gnu(grid, node, unit)${ not sameas(grid, 'empty')
                        and (   p_gnu(grid, node, unit, 'maxGen')
                                or p_gnu(grid, node, unit, 'maxCons')
                                or p_gnu(grid, node, unit, 'unitSizeGen')
                                or p_gnu(grid, node, unit, 'unitSizeCons')
                                )
123
                      }
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
    = yes;
// Reduce the grid dimension
nu(node, unit) = sum(grid, gnu(grid, node, unit));

// Separation of gnu into inputs and outputs
gnu_output(gnu(grid, node, unit))${ p_gnu(grid, node, unit, 'maxGen')
                                    or p_gnu(grid, node, unit, 'unitSizeGen')
                                    }
    = yes;
gnu_input(gnu(grid, node, unit))${  p_gnu(grid, node, unit, 'maxCons')
                                    or p_gnu(grid, node, unit, 'unitSizeCons')
                                    }
    = yes;

// Units connecting gn-gn pairs
gn2gnu(grid, node_input, grid_, node_output, unit)${    gnu_input(grid, node_input, unit)
                                                        and gnu_output(grid_, node_output, unit)
                                                        }
    = yes;

// Units with minimum load requirements
unit_minload(unit)${    p_unit(unit, 'op00') > 0 // If the first defined operating point is between 0 and 1, then the unit is considered to have a min load limit
                        and p_unit(unit, 'op00') < 1
                        }
    = yes;
149

150
151
152
153
154
155
// Units with flows/fuels
unit_flow(unit)${ sum(flow, flowUnit(flow, unit)) }
    = yes;
unit_fuel(unit)${ sum(fuel, uFuel(unit, 'main', fuel)) }
    = yes;

156
157
158
159
160
161
162
163
164
165
// Units with investment variables
unit_investLP(unit)${  not p_unit(unit, 'investMIP')
                       and p_unit(unit, 'maxUnitCount')
                        }
    = yes;
unit_investMIP(unit)${  p_unit(unit, 'investMIP')
                        and p_unit(unit, 'maxUnitCount')
                        }
    = yes;

166
167
// Units with special startup properties
// All units can cold start (default start category)
168
unitStarttype(unit, 'cold') = yes;
169
// Units with parameters regarding hot/warm starts
170
unitStarttype(unit, starttypeConstrained)${ p_unit(unit, 'startWarmAfterXhours')
171
172
173
174
                                            or p_unit(unit, 'startCostHot')
                                            or p_unit(unit, 'startFuelConsHot')
                                            or p_unit(unit, 'startCostWarm')
                                            or p_unit(unit, 'startFuelConsWarm')
175
                                            or p_unit(unit, 'startColdAfterXhours')
176
177
178
                                            }
    = yes;

179
180
181
// Units with time series data enabled
unit_timeseries(unit)${ p_unit(unit, 'useTimeseries') }
    = yes;
182
183
184
185
186
187
188
189

* --- Unit Related Parameters -------------------------------------------------

// Assume values for critical unit related parameters, if not provided by input data
// If the unit does not have efficiency set, it is 1
p_unit(unit, 'eff00')${ not p_unit(unit, 'eff00') }
    = 1;

190
// In case number of units has not been defined it is 1 except for units with investments allowed.
191
p_unit(unit, 'unitCount')${ not p_unit(unit, 'unitCount')
192
193
                            and not unit_investMIP(unit)
                            and not unit_investLP(unit)
194
195
196
197
198
199
                            }
    = 1;

// By default add outputs in order to get the total capacity of the unit
p_unit(unit, 'outputCapacityTotal')${ not p_unit(unit, 'outputCapacityTotal') }
    = sum(gnu_output(grid, node, unit), p_gnu(grid, node, unit, 'maxGen'));
200
201
p_unit(unit, 'unitOutputCapacityTotal')
    = sum(gnu_output(grid, node, unit), p_gnu(grid, node, unit, 'unitSizeGen'));
202
203

// Assume unit sizes based on given maximum capacity parameters and unit counts if able
204
p_gnu(gnu(grid, node, unit), 'unitSizeGen')${    p_gnu(grid, node, unit, 'maxGen')
205
206
207
                                            and p_unit(unit, 'unitCount')
                                            }
    = p_gnu(grid, node, unit, 'maxGen') / p_unit(unit, 'unitCount');  // If maxGen and unitCount are given, calculate unitSizeGen based on them.
208
p_gnu(gnu(grid, node, unit), 'unitSizeCons')${   p_gnu(grid, node, unit, 'maxCons')
209
210
211
                                            and p_unit(unit, 'unitCount')
                                            }
    = p_gnu(grid, node, unit, 'maxCons') / p_unit(unit, 'unitCount');  // If maxCons and unitCount are given, calculate unitSizeCons based on them.
212
p_gnu(gnu(grid, node, unit), 'unitSizeTot')
213
214
215
216
    = p_gnu(grid, node, unit, 'unitSizeGen') + p_gnu(grid, node, unit, 'unitSizeCons');

// Determine unit startup parameters based on data
// Hot startup parameters
217
p_uNonoperational(unitStarttype(unit, 'hot'), 'min')
218
    = p_unit(unit, 'minShutdownHours');
219
p_uNonoperational(unitStarttype(unit, 'hot'), 'max')
220
    = p_unit(unit, 'startWarmAfterXhours');
221
p_uStartup(unitStarttype(unit, 'hot'), 'cost')
222
223
    = p_unit(unit, 'startCostHot')
        * sum(gnu_output(grid, node, unit), p_gnu(grid, node, unit, 'unitSizeGen'));
224
p_uStartup(unitStarttype(unit, 'hot'), 'consumption')
225
226
227
228
    = p_unit(unit, 'startFuelConsHot')
        * sum(gnu_output(grid, node, unit), p_gnu(grid, node, unit, 'unitSizeGen'));

// Warm startup parameters
229
p_uNonoperational(unitStarttype(unit, 'warm'), 'min')
230
    = p_unit(unit, 'startWarmAfterXhours');
231
p_uNonoperational(unitStarttype(unit, 'warm'), 'max')
232
    = p_unit(unit, 'startColdAfterXhours');
233
p_uStartup(unitStarttype(unit, 'warm'), 'cost')
234
235
    = p_unit(unit, 'startCostWarm')
        * sum(gnu_output(grid, node, unit), p_gnu(grid, node, unit, 'unitSizeGen'));
236
p_uStartup(unitStarttype(unit, 'warm'), 'consumption')
237
238
239
240
    = p_unit(unit, 'startFuelConsWarm')
        * sum(gnu_output(grid, node, unit), p_gnu(grid, node, unit, 'unitSizeGen'));

// Cold startup parameters
241
242
p_uNonoperational(unitStarttype(unit, 'cold'), 'min')
    = p_unit(unit, 'startColdAfterXhours');
243
p_uStartup(unit, 'cold', 'cost')
244
    = p_unit(unit, 'startCostCold')
245
        * sum(gnu_output(grid, node, unit), p_gnu(grid, node, unit, 'unitSizeGen'));
246
p_uStartup(unit, 'cold', 'consumption')
247
    = p_unit(unit, 'startFuelConsCold')
248
249
        * sum(gnu_output(grid, node, unit), p_gnu(grid, node, unit, 'unitSizeGen'));

250
251
252

//shutdown cost parameters
p_uShutdown(unit, 'cost')
253
    = p_unit(unit, 'shutdownCost')
254
255
        * sum(gnu_output(grid, node, unit), p_gnu(grid, node, unit, 'unitSizeGen'));

256
// Determine unit emission costs
257
p_unitFuelEmissionCost(unit_fuel, fuel, emission)${ sum(param_fuel, uFuel(unit_fuel, param_fuel, fuel)) }
258
    = p_fuelEmission(fuel, emission)
259
        / 1e3 // NOTE!!! Conversion to t/MWh from kg/MWh in data
260
261
262
263
264
265
266
267
268
269
270
        * sum(gnu_output(grid, node, unit_fuel),
            + p_gnPolicy(grid, node, 'emissionTax', emission)  // Weighted average of emission costs from different output energy types
                * [ + p_gnu(grid, node, unit_fuel, 'maxGen')
                    + p_gnu(grid, node, unit_fuel, 'unitSizeGen')${not p_gnu(grid, node, unit_fuel, 'maxGen')}
                    ] // END * p_gnPolicy
        ) // END sum(gnu_output)
        / sum(gnu_output(grid, node, unit_fuel), // Weighted average of emission costs from different output energy types
            + p_gnu(grid, node, unit_fuel, 'maxGen')
            + p_gnu(grid, node, unit_fuel, 'unitSizeGen')$(not p_gnu(grid, node, unit_fuel, 'maxGen'))
        ) // END sum(gnu_output)
;
271

272
// If the start-up fuel fraction is not defined, it equals 1
273
p_uFuel(uFuel(unit_fuel, 'startup', fuel), 'fixedFuelFraction')${ not p_uFuel(unit_fuel, 'startup', fuel, 'fixedFuelFraction') }
274
275
    = 1;

276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
* =============================================================================
* --- Determine Fuel Price Representation -------------------------------------
* =============================================================================
// Use either constant or time series for fuel prices depending on 'ts_fuelPriceChange'
// Should be handled separately by 'p_fuelPrice' also being included in the input data,
// but this is more convenient for now as no changes to inputs are required

// Determine if fuel prices require a time series representation or not
loop(fuel,
    // Find the steps with changing fuel prices
    option clear = tt;
    tt(t)${ ts_fuelPriceChange(fuel, t) } = yes;

    // If only up to a single value
    if(sum(tt, 1) <= 1,
        p_fuelPrice(fuel, 'useConstant') = 1; // Use a constant for fuel prices
        p_fuelPrice(fuel, 'fuelPrice') = sum(tt, ts_fuelpriceChange(fuel, tt)) // Determine the price as the only value in the time series
    // If multiple values found, use time series
    else
        p_fuelPrice(fuel, 'useTimeSeries') = 1;
Topi Rasku's avatar
Topi Rasku committed
296
        ); // END if(sum(tt))
297
298
); // END loop(fuel)

299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
* =============================================================================
* --- Generate Node Related Sets Based on Input Data --------------------------
* =============================================================================

* --- Node Connectivity -------------------------------------------------------

// Node pairs connected via transfer links
gn2n(grid, from_node, to_node)${    p_gnn(grid, from_node, to_node, 'transferCap')
                                    or p_gnn(grid, from_node, to_node, 'transferLoss')
                                    or p_gnn(grid, from_node, to_node, 'transferCapBidirectional')
                                    or p_gnn(grid, to_node, from_node, 'transferCapBidirectional')
                                    or p_gnn(grid, from_node, to_node, 'transferCapInvLimit')
                                    }
    = yes;

// Node pairs with relatively bound states
gnn_boundState(grid, node, node_)${ p_gnn(grid, node, node_, 'boundStateMaxDiff') }
    = yes;

// Node pairs connected via energy diffusion
gnn_state(grid, node, node_)${  p_gnn(grid, node, node_, 'diffCoeff')
                                or gnn_boundState(grid, node, node_)
                                }
    = yes;

// Generate the set for transfer links where the order of the first node must be smaller than the order of the second node
Option clear = gn2n_directional;
gn2n_directional(gn2n(grid, node, node_))${ ord(node) < ord(node_) }
    = yes;
gn2n_directional(gn2n(grid, node, node_))${ ord(node) > ord(node_)
                                            and not gn2n(grid, node_, node)
                                            }
    = yes;

Niina Helistö's avatar
Niina Helistö committed
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
// Set for transfer links with investment possibility
Option clear = gn2n_directional_investLP;
Option clear = gn2n_directional_investMIP;
gn2n_directional_investLP(gn2n_directional(grid, node, node_))${ [p_gnn(grid, node, node_, 'transferCapInvLimit')
                                                                     or p_gnn(grid, node_, node, 'transferCapInvLimit')]
                                                                 and [not p_gnn(grid, node, node_, 'investMIP')
                                                                     and not p_gnn(grid, node_, node, 'investMIP')]
                                                                 }
    = yes;
gn2n_directional_investMIP(gn2n_directional(grid, node, node_))${ [p_gnn(grid, node, node_, 'transferCapInvLimit')
                                                                     or p_gnn(grid, node_, node, 'transferCapInvLimit')]
                                                                 and [p_gnn(grid, node, node_, 'investMIP')
                                                                     or p_gnn(grid, node_, node, 'investMIP')]
                                                                 }
    = yes;

349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
* --- Node States -------------------------------------------------------------

// States with slack variables
gn_stateSlack(grid, node)${ sum((slack, useConstantOrTimeSeries), p_gnBoundaryPropertiesForStates(grid, node, slack, useConstantOrTimeSeries)) }
    = yes;

// Nodes with states
gn_state(grid, node)${  gn_stateSlack(grid, node)
                        or p_gn(grid, node, 'energyStoredPerUnitOfState')
                        or sum((stateLimits, useConstantOrTimeSeries), p_gnBoundaryPropertiesForStates(grid, node, stateLimits, useConstantOrTimeSeries))
                        or sum(useConstantOrTimeSeries, p_gnBoundaryPropertiesForStates(grid, node, 'reference', useConstantOrTimeSeries))
                        }
    = yes;

// Existing grid-node pairs
364
365
366
gn(grid, node)${    sum(unit, gnu(grid, node, unit))
                    or gn_state(grid, node)
                    or sum((f, t), ts_influx(grid, node, f, t))
367
368
369
370
                    or sum(node_, gn2n(grid, node, node_))
                    or sum(node_, gn2n(grid, node_, node))
                    or sum(node_, gnn_state(grid, node, node_))
                    or sum(node_, gnn_state(grid, node_, node))
371
372
373
374
375
376
377
378
379
                    }
    = yes;

// Nodes with spill permitted
node_spill(node)${ sum((grid, spillLimits, useConstantOrTimeSeries), p_gnBoundaryPropertiesForStates(grid, node, spillLimits, useConstantOrTimeSeries)) }
    = yes;

// Assume values for critical node related parameters, if not provided by input data
// Boundary multiplier
380
381
382
p_gnBoundaryPropertiesForStates(gn(grid, node), param_gnBoundaryTypes, 'multiplier')${  not p_gnBoundaryPropertiesForStates(grid, node, param_gnBoundaryTypes, 'multiplier')
                                                                                        and sum(param_gnBoundaryProperties, p_gnBoundaryPropertiesForStates(grid, node, param_gnBoundaryTypes, param_gnBoundaryProperties))
    } = 1; // If multiplier has not been set, set it to 1 by default
383

384
385
386
* --- Other Node Properties ---------------------------------------------------

// Nodes with flows
387
388
389
flowNode(flow, node)${  sum((f, t), ts_cf(flow, node, f, t))
                        and sum(grid, gn(grid, node))
                        }
390
391
    = yes;

392
393
394
* =============================================================================
* --- Reserves Sets & Parameters ----------------------------------------------
* =============================================================================
395
396
397
// NOTE! Reserves can be disabled through the model settings file.
// The sets are disabled in "3a_periodicInit.gms" accordingly.

398
399
400
// Units with reserve provision capabilities
nuRescapable(restypeDirection(restype, up_down), nu(node, unit))
    $ { p_nuReserves(node, unit, restype, up_down)
401
402
403
      }
  = yes;

404
// Node-node connections with reserve transfer capabilities
405
406
407
408
409
restypeDirectionNodeNode(restypeDirection(restype, up_down), node, node_)
    $ { p_nnReserves(node, node_, restype, up_down)
      }
  = yes;

410
411
412
413
// Nodes with reserve requirements, units capable of providing reserves, or reserve capable connections
restypeDirectionNode(restypeDirection(restype, up_down), node)
    $ { p_nReserves(node, restype, up_down)
        or p_nReserves(node, restype, 'use_time_series')
414
        or sum(nu(node, unit), p_nuReserves(node, unit, restype, 'portion_of_infeed_to_reserve'))
415
416
        or sum(nu(node, unit), nuRescapable(restype, up_down, node, unit))
        or sum(gn2n(grid, node, to_node), restypeDirectionNodeNode(restype, up_down, node, to_node))
417
418
      }
  = yes;
419

420
421
* --- Correct values for critical reserve related parameters ------------------

422
423
424
425
426
// Reserve reliability assumed to be perfect if not provided in data
p_nuReserves(nu(node, unit), restype, 'reserveReliability')
    ${  not p_nuReserves(node, unit, restype, 'reserveReliability')
        and sum(up_down, nuRescapable(restype, up_down, node, unit))
        }
427
428
    = 1;

429
430
431
432
433
434
435
436
437
// Reserve provision overlap decreases the capacity of the overlapping category
p_nuReserves(nu(node, unit), restype, up_down)
    ${ nuRescapable(restype, up_down, node, unit) }
    = p_nuReserves(node, unit, restype, up_down)
        - sum(restype_${ p_nuRes2Res(node, unit, restype_, up_down, restype) },
            + p_nuReserves(node, unit, restype_, up_down)
                * p_nuRes2Res(node, unit, restype_, up_down, restype)
        ); // END sum(restype_)

438
439
440
* =============================================================================
* --- Data Integrity Checks ---------------------------------------------------
* =============================================================================
441

442
443
444
445
446
447
448
449
450
451
* --- Check that nodes aren't assigned to multiple grids ----------------------

loop(node,
    if(sum(gn(grid, node), 1) > 1,
        put log '!!! Error occurred on node ' node.tl:0 /;
        put log '!!! Abort: Nodes cannot be assigned to multiple grids!' /;
        abort "Nodes cannot be assigned to multiple grids!"
    ); // END if(sum(gn))
); // END loop(node)

452
453
* --- Check the integrity of node connection related data ---------------------

454
Option clear = count;
455
456
457
458
459
460
loop(gn2n(grid, node, node_),
    count = count + 1; // Count the gn2n indeces to make finding the errors easier.
    // Check if the bidirectional transfer parameter exists for this link.
    if(p_gnn(grid, node, node_, 'transferCapBidirectional'),
        // Check for conflicting bidirectional transfer capacities.
        if(p_gnn(grid, node, node_, 'transferCapBidirectional') <> p_gnn(grid, node_, node, 'transferCapBidirectional'),
461
            put log '!!! Error occurred on gn2n link ' node.tl:0 '-' node_.tl:0 /;
462
            put log '!!! Abort: Conflicting transferCapBidirectional parameters!' /;
463
464
465
466
            abort "Conflicting 'transferCapBidirectional' parameters!"
        );
        // Check for conflicting one-directional and bidirectional transfer capacities.
        if(p_gnn(grid, node, node_, 'transferCapBidirectional') < p_gnn(grid, node, node_, 'transferCap') OR (p_gnn(grid, node, node_, 'transferCapBidirectional') < p_gnn(grid, node_, node, 'transferCap')),
467
            put log '!!! Error occurred on gn2n link ' node.tl:0 '-' node_.tl:0 /;
468
            put log '!!! Abort: Parameter transferCapBidirectional must be greater than or equal to defined one-directional transfer capacities!' /;
469
470
471
472
473
            abort "Parameter 'transferCapBidirectional' must be greater than or equal to defined one-directional transfer capacities!"
        );
    );
);

474
475
476
477
478
479
* --- Check the integrity of efficiency approximation related data ------------

Option clear = tmp;
// Find the largest effLevel used in the data
tmp = smax(effLevelGroupUnit(effLevel, effSelector, unit), ord(effLevel));

480
481
482
// Expand the effLevelGroupUnit when possible, abort if impossible
loop(effLevel${ord(effLevel)<=tmp},
    effLevelGroupUnit(effLevel, effSelector, unit)
483
        ${not sum(effLevelGroupUnit(effLevel, effSelector_, unit), 1)}
484
        = effLevelGroupUnit(effLevel - 1, effSelector, unit) // Expand previous (effLevel, effSelector) when applicable
485
    loop(unit${not unit_flow(unit) and not sameas(unit, 'empty')},
486
487
488
489
490
491
492
493
        If(not sum(effLevelGroupUnit(effLevel, effSelector, unit), 1),
            put log '!!! Error on unit ' unit.tl:0 /;
            put log '!!! Abort: Insufficient effLevelGroupUnit definitions!' /;
            abort "Insufficient effLevelGroupUnit definitions!"
        );
    );
);

494
loop( unit,
495
    // Check that 'op' is defined correctly
496
    Option clear = count; // Initialize the previous op to zero
497
    loop( op,
498
        if (p_unit(unit, op) + 1${not p_unit(unit, op)} < count,
499
500
501
            put log '!!! Error occurred on unit ' unit.tl:0 /; // Display unit that causes error
            put log '!!! Abort: param_unit op must be defined as zero or positive and increasing!' /;
            abort "param_unit 'op's must be defined as zero or positive and increasing!";
502
        ); // END if(p_unit)
503
        count = p_unit(unit, op);
504
    ); // END loop(op)
505
506
    // Check that efficiency approximations have sufficient data
    loop( effLevelGroupUnit(effLevel, effSelector, unit),
507
508
509
        loop( op__${p_unit(unit, op__) = smax(op, p_unit(unit, op))}, // Loop over the 'op's to find the last defined data point.
            loop( op_${p_unit(unit, op_) = smin(op${p_unit(unit, op)}, p_unit(unit, op))}, // Loop over the 'op's to find the first nonzero 'op' data point.
                if(effDirectOn(effSelector) AND ord(op__) = ord(op_) AND not p_unit(unit, 'section') AND not p_unit(unit, 'opFirstCross'),
510
511
512
                    put log '!!! Error occurred on unit ' unit.tl:0 /; // Display unit that causes error
                    put log '!!! Abort: directOn requires two efficiency data points with nonzero op or section or opFirstCross!' /;
                    abort "directOn requires two efficiency data points with nonzero 'op' or 'section' or 'opFirstCross'!";
513
514
515
516
                ); // END if(effDirectOn)
            ); // END loop(op_)
        ); // END loop(op__)
    ); // END loop(effLevelGroupUnit)
517
518
);

519
520
* --- Check the start-up fuel fraction related data ---------------------------

521
loop( unit_fuel(unit)${sum(fuel, uFuel(unit_fuel, 'startup', fuel))},
522
    if(sum(fuel, p_uFuel(unit, 'startup', fuel, 'fixedFuelFraction')) <> 1,
523
        put log '!!! Error occurred on unit ' unit.tl:0 /;
524
        put log '!!! Abort: The sum of fixedFuelFraction over start-up fuels needs to be one for all units using start-up fuels!' /;
525
        abort "The sum of 'fixedFuelFraction' over start-up fuels needs to be one for all units using start-up fuels!"
526
527
    );
);
528

529
530
* --- Check the shutdown time related data ------------------------------------

531
loop( unitStarttype(unit, starttypeConstrained),
532
533
    if(p_unit(unit, 'minShutdownHours') > p_unit(unit, 'startWarmAfterXhours')
        or p_unit(unit, 'startWarmAfterXhours') > p_unit(unit, 'startColdAfterXhours'),
534
        put log '!!! Error occurred on unit ', unit.tl:0 /;
535
        put log '!!! Abort: Units should have p_unit(unit, minShutdownHours) <= p_unit(unit, startWarmAfterXhours) <= p_unit(unit, startColdAfterXhours)!' /;
536
537
538
        abort "Units should have p_unit(unit, 'minShutdownHours') <= p_unit(unit, 'startWarmAfterXhours') <= p_unit(unit, 'startColdAfterXhours')!"
    );
);
539

540
* --- Check reserve related data ----------------------------------------------
541
542
543
544

// Check that reserve_length is long enough for properly commitment of reserves
loop( restypeDirectionNode(restype, up_down, node),
    if(p_nReserves(node, restype, 'reserve_length') < p_nReserves(node, restype, 'update_frequency') + p_nReserves(node, restype, 'gate_closure'),
545
        put log '!!! Error occurred on node ', node.tl:0 /;
546
        put log '!!! Abort: The reserve_length parameter should be longer than update_frequency + gate_closure to fix the reserves properly!' /;
547
548
        abort "The 'reserve_length' parameter should be longer than 'update_frequency' + 'gate_closure' to fix the reserves properly!"
    ); // END if
549
); // END loop(restypeDirectionNode)
550

551
552
553
554
555
556
557
558
// Check that reserve overlaps are possible
loop( (nu(node, unit), restypeDirection(restype, up_down)),
    if( p_nuReserves(node, unit, restype, up_down) < 0,
        put log '!!! Error occurred on unit ', unit.tl:0 /;
        put log '!!! Abort: Overlapping reserve capacities in p_nuRes2Res can result in excess reserve production!' /;
        abort "Overlapping reserve capacities in p_nuRes2Res can result in excess reserve production!"
    ); // END if(p_nuReserves)
); // END loop((nu,restypeDirection))
559

560
561
562
563

* =============================================================================
* --- Default values  ---------------------------------------------------------
* =============================================================================
564
565
566
567
loop(timeseries$(not sameas(timeseries, 'ts_cf')),
    p_autocorrelation(gn, timeseries) = 0;
    p_tsMinValue(gn, timeseries) = -Inf;
    p_tsMaxValue(gn, timeseries) = Inf;
568
);
569
570
571
p_autocorrelation(flowNode, 'ts_cf') = 0;
p_tsMinValue(flowNode, 'ts_cf') = 0;
p_tsMaxValue(flowNode, 'ts_cf') = 1;