1e_inputs.gms 32.7 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
* If input_file excel has been set in the command line arguments, then Gdxxrw will be run to convert the Excel into a GDX file
*   using the sheet defined by input_excel_index command line argument (default: 'INDEX').
24
$if set input_file_excel $call 'gdxxrw Input="%input_dir%/%input_file_excel%" Output="%input_dir%/%input_file_gdx%" Index=%input_excel_index%! %input_excel_checkdate%'
25
$ife %system.errorlevel%>0 $abort gdxxrw failed! Check that your input Excel is valid and that your file path and file name are correct.
26

27
28
29
30
* --input_file_gdx=nameOfInputFile.gdx for input_file_gdx in input_dir
$ifthen exist '%input_dir%/%input_file_gdx%'
    $$gdxin  '%input_dir%/%input_file_gdx%'
* --input_file_gdx=ABSOLUTE/PATH/nameOfInputFile.gdx for input_file_gdx not in input_dir
31
$elseif exist '%input_file_gdx%'
32
    $$gdxin  '%input_file_gdx%'
33
34
$else
    $$goto no_input_gdx
35
$endif
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
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
$loaddcm grid
$loaddc node
$loaddc flow
$loaddc unittype
$loaddc unit
$loaddc unitUnittype
$loaddc unit_fail
$loaddc commodity
$loaddc unitUnitEffLevel
$loaddc effLevelGroupUnit
$loaddc group
$loaddc p_gn
$loaddc p_gnn
$loaddc ts_gnn
$loaddc p_gnu_io
$loaddc p_gnuBoundaryProperties
$loaddc p_unit
$loaddc ts_unit
$loaddc p_unitConstraint
$loaddc p_unitConstraintNode
$loaddc restype
$loaddc restypeDirection
$loaddc restypeReleasedForRealization
$loaddc restype_inertia
$loaddc p_groupReserves
$loaddc p_groupReserves3D
$loaddc p_groupReserves4D
$loaddc p_gnuReserves
$loaddc p_gnnReserves
$loaddc p_gnuRes2Res
$loaddc ts_reserveDemand
$loaddc p_gnBoundaryPropertiesForStates
$loaddc p_uStartupfuel
$loaddc flowUnit
$loaddc emission
$loaddc p_nEmission
$loaddc ts_cf
*$loaddc p_price // Disabled for convenience, see line 278-> ("Determine Fuel Price Representation")
$loaddc ts_priceChange
$loaddc ts_influx
$loaddc ts_node
$loaddc p_s_discountFactor
$loaddc t_invest
$loaddc utAvailabilityLimits
$loaddc p_storageValue
$loaddc ts_storageValue
$loaddc uGroup
$loaddc gnuGroup
$loaddc gn2nGroup
$loaddc gnGroup
$loaddc sGroup
$loaddc p_groupPolicy
$loaddc p_groupPolicyUnit
$loaddc p_groupPolicyEmission
$loaddc gnss_bound
$loaddc uss_bound

$gdxin

$label no_input_gdx

98

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

102
* Reads changes or additions to the inputdata through changes.inc file.
103
104
105
$ifthen exist '%input_dir%/changes.inc'
   $$include '%input_dir%/changes.inc'
$endif
106

107

108
109
110
111
112
113
114
115
116
117

$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
118
p_gnnReserves
119
120
121
122
123
124
125
126
127
128
129
130
131
p_gnuBoundaryProperties
ts_node
ts_reserveDemand
ts_unit
p_storageValue
group
gnuGroup
gnGroup
gn2nGroup
gnss_bound
$offtext


132
133
134
135
136
137
138
* =============================================================================
* --- Initialize Unit Related Sets & Parameters Based on Input Data -----------
* =============================================================================


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

139
140
p_gnu(grid, node, unit, param_gnu) = sum(input_output, p_gnu_io(grid, node, unit, input_output, param_gnu));

141
// Set of all existing gnu
142
gnu(grid, node, unit)${ not sameas(grid, 'empty')
143
144
145
                        and (   p_gnu(grid, node, unit, 'capacity')
                                or p_gnu(grid, node, unit, 'unitSize')
                                or p_gnu(grid, node, unit, 'conversionCoeff')
146
                                )
147
                      }
148
149
150
    = yes;
// Reduce the grid dimension
nu(node, unit) = sum(grid, gnu(grid, node, unit));
151
//p_gnu(grid, node, unit, 'capacity')$(p_gnu(grid, node, unit, 'capacity') = 0) = inf;
152
153

// Separation of gnu into inputs and outputs
154
155
156
gnu_output(gnu(grid, node, unit))${ p_gnu_io(grid, node, unit, 'output', 'capacity')
                                    or p_gnu_io(grid, node, unit, 'output', 'unitSize')
                                    or p_gnu_io(grid, node, unit, 'output', 'conversionCoeff')
157
158
                                    }
    = yes;
159
160
161
gnu_input(gnu(grid, node, unit))${  p_gnu_io(grid, node, unit, 'input', 'capacity')
                                    or p_gnu_io(grid, node, unit, 'input', 'unitSize')
                                    or p_gnu_io(grid, node, unit, 'input', 'conversionCoeff')
162
163
164
165
166
167
168
169
170
171
172
173
174
175
                                    }
    = 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;
176

177
// Units with flows/commodities
178
179
unit_flow(unit)${ sum(flow, flowUnit(flow, unit)) }
    = yes;
180
un_commodity(unit, commodity)$sum(grid, gnu(grid, commodity, unit)) = yes;
181
182
un_commodity_in(unit, commodity)$sum(grid, gnu_input(grid, commodity, unit)) = yes;
un_commodity_out(unit, commodity)$sum(grid, gnu_output(grid, commodity, unit)) = yes;
183
unit_commodity(unit)${ sum(node, un_commodity(unit, node)) }
184
185
    = yes;

186
187
188
189
190
191
192
193
194
195
// 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;

196
197
// Units with special startup properties
// All units can cold start (default start category)
198
unitStarttype(unit, 'cold') = yes;
199
// Units with parameters regarding hot/warm starts
200
unitStarttype(unit, starttypeConstrained)${ p_unit(unit, 'startWarmAfterXhours')
201
202
203
204
                                            or p_unit(unit, 'startCostHot')
                                            or p_unit(unit, 'startFuelConsHot')
                                            or p_unit(unit, 'startCostWarm')
                                            or p_unit(unit, 'startFuelConsWarm')
205
                                            or p_unit(unit, 'startColdAfterXhours')
206
207
                                            }
    = yes;
208
209
// Units consuming energy from particular nodes in start-up
nu_startup(node, unit)$p_uStartupfuel(unit, node, 'fixedFuelFraction') = yes;
210

211
212
213
// Units with time series data enabled
unit_timeseries(unit)${ p_unit(unit, 'useTimeseries') }
    = yes;
214
215
216
217
218
219
220
221

* --- 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;

222
// In case number of units has not been defined it is 1 except for units with investments allowed.
223
p_unit(unit, 'unitCount')${ not p_unit(unit, 'unitCount')
224
225
                            and not unit_investMIP(unit)
                            and not unit_investLP(unit)
226
227
228
229
230
                            }
    = 1;

// By default add outputs in order to get the total capacity of the unit
p_unit(unit, 'outputCapacityTotal')${ not p_unit(unit, 'outputCapacityTotal') }
231
    = sum(gnu_output(grid, node, unit), p_gnu(grid, node, unit, 'capacity'));
232
p_unit(unit, 'unitOutputCapacityTotal')
233
    = sum(gnu_output(grid, node, unit), p_gnu(grid, node, unit, 'unitSize'));
234
235

// Assume unit sizes based on given maximum capacity parameters and unit counts if able
236
237
238
p_gnu(gnu(grid, node, unit), 'unitSize')
    ${  not p_gnu(grid, node, unit, 'unitSize')
        and p_gnu(grid, node, unit, 'capacity')
239
240
        and p_unit(unit, 'unitCount')
        }
241
    = p_gnu(grid, node, unit, 'capacity') / p_unit(unit, 'unitCount'); // If capacity and unitCount are given, calculate unitSize based on them.
242
243
244

// Determine unit startup parameters based on data
// Hot startup parameters
245
p_uNonoperational(unitStarttype(unit, 'hot'), 'min')
246
    = p_unit(unit, 'minShutdownHours');
247
p_uNonoperational(unitStarttype(unit, 'hot'), 'max')
248
    = p_unit(unit, 'startWarmAfterXhours');
249
p_uStartup(unitStarttype(unit, 'hot'), 'cost')
250
    = p_unit(unit, 'startCostHot')
251
        * sum(gnu_output(grid, node, unit), p_gnu(grid, node, unit, 'unitSize'));
252
p_uStartup(unitStarttype(unit, 'hot'), 'consumption')
253
    = p_unit(unit, 'startFuelConsHot')
254
        * sum(gnu_output(grid, node, unit), p_gnu(grid, node, unit, 'unitSize'));
255
256

// Warm startup parameters
257
p_uNonoperational(unitStarttype(unit, 'warm'), 'min')
258
    = p_unit(unit, 'startWarmAfterXhours');
259
p_uNonoperational(unitStarttype(unit, 'warm'), 'max')
260
    = p_unit(unit, 'startColdAfterXhours');
261
p_uStartup(unitStarttype(unit, 'warm'), 'cost')
262
    = p_unit(unit, 'startCostWarm')
263
        * sum(gnu_output(grid, node, unit), p_gnu(grid, node, unit, 'unitSize'));
264
p_uStartup(unitStarttype(unit, 'warm'), 'consumption')
265
    = p_unit(unit, 'startFuelConsWarm')
266
        * sum(gnu_output(grid, node, unit), p_gnu(grid, node, unit, 'unitSize'));
267
268

// Cold startup parameters
269
270
p_uNonoperational(unitStarttype(unit, 'cold'), 'min')
    = p_unit(unit, 'startColdAfterXhours');
271
p_uStartup(unit, 'cold', 'cost')
272
    = p_unit(unit, 'startCostCold')
273
        * sum(gnu_output(grid, node, unit), p_gnu(grid, node, unit, 'unitSize'));
274
p_uStartup(unit, 'cold', 'consumption')
275
    = p_unit(unit, 'startFuelConsCold')
276
        * sum(gnu_output(grid, node, unit), p_gnu(grid, node, unit, 'unitSize'));
277

278
279
280
281
// Start-up fuel consumption per fuel
p_unStartup(unit, node, starttype)$p_uStartupfuel(unit, node, 'fixedFuelFraction')
    = p_uStartup(unit, starttype, 'consumption')
        * p_uStartupfuel(unit, node, 'fixedFuelFraction');
282
283
284

//shutdown cost parameters
p_uShutdown(unit, 'cost')
285
    = p_unit(unit, 'shutdownCost')
286
        * sum(gnu_output(grid, node, unit), p_gnu(grid, node, unit, 'unitSize'));
287

288
// Determine unit emission costs
289
290
p_unitEmissionCost(unit, node, emission)${nu(node, unit) and p_nEmission(node, emission)}
    = p_nEmission(node, emission)
291
        / 1e3 // NOTE!!! Conversion to t/MWh from kg/MWh in data
292
293
        * sum(gnGroup(grid, node, group)$gnu_input(grid, node, unit),
            + p_groupPolicyEmission(group, 'emissionTax', emission)
294
          )
295
;
296

297

298
// Unit lifetime
299
300
loop(utAvailabilityLimits(unit, t, availabilityLimits),
    p_unit(unit, availabilityLimits) = ord(t)
301
302
); // END loop(ut)

303
* =============================================================================
304
* --- Determine Commodity Price Representation -------------------------------------
305
* =============================================================================
306
// Use time series for commodity prices depending on 'ts_priceChange'
307

308
309
// Determine if commodity prices require a time series representation or not
loop(commodity,
310
311
    // Find the steps with changing fuel prices
    option clear = tt;
312
    tt(t)${ ts_priceChange(commodity, t) } = yes;
313
314
315

    // If only up to a single value
    if(sum(tt, 1) <= 1,
316
317
        p_price(commodity, 'useConstant') = 1; // Use a constant for commodity prices
        p_price(commodity, 'price') = sum(tt, ts_priceChange(commodity, tt)) // Determine the price as the only value in the time series
318
319
    // If multiple values found, use time series
    else
320
        p_price(commodity, 'useTimeSeries') = 1;
Topi Rasku's avatar
Topi Rasku committed
321
        ); // END if(sum(tt))
322
323
); // END loop(fuel)

324

325
326
327
328
329
330
331
332
333
334
335
336
* =============================================================================
* --- 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')
337
                                    or p_gnn(grid, from_node, to_node, 'portion_of_transfer_to_reserve')
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
                                    }
    = 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
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
// 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;

376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
* --- 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
391
392
393
gn(grid, node)${    sum(unit, gnu(grid, node, unit))
                    or gn_state(grid, node)
                    or sum((f, t), ts_influx(grid, node, f, t))
394
395
396
397
                    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))
398
399
400
401
402
403
404
405
406
                    }
    = 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
407
408
409
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
410

411
412
413
* --- Other Node Properties ---------------------------------------------------

// Nodes with flows
414
415
416
flowNode(flow, node)${  sum((f, t), ts_cf(flow, node, f, t))
                        and sum(grid, gn(grid, node))
                        }
417
418
    = yes;

419
420
* --- Timeseries parameters for node-node connections -------------------------

421
// Transfer links with time series enabled for certain parameters
422
423
gn2n_timeseries(grid, node, node_, 'availability')${p_gnn(grid, node, node_, 'useTimeseriesAvailability')}
    = yes;
424
425
gn2n_timeseries(grid, node, node_, 'transferLoss')${p_gnn(grid, node, node_, 'useTimeseriesLoss')}
    = yes;
426

427
428
429
* =============================================================================
* --- Reserves Sets & Parameters ----------------------------------------------
* =============================================================================
430
431
432
// NOTE! Reserves can be disabled through the model settings file.
// The sets are disabled in "3a_periodicInit.gms" accordingly.

433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
* --- Correct values for critical reserve related parameters - Part 1 ---------

// Reserve activation duration assumed to be 1 hour if not provided in data
p_groupReserves(group, restype, 'reserve_activation_duration')
    ${  not p_groupReserves(group, restype, 'reserve_activation_duration')
        and p_groupReserves(group, restype, 'reserve_length')
        }
    = 1;
// Reserve reactivation time assumed to be 1 hour if not provided in data
p_groupReserves(group, restype, 'reserve_reactivation_time')
    ${  not p_groupReserves(group, restype, 'reserve_reactivation_time')
        and p_groupReserves(group, restype, 'reserve_length')
        }
    = 1;

* --- Copy reserve data and create necessary sets -----------------------------

450
// Copy data from p_groupReserves to p_gnReserves
451
loop(gnGroup(grid, node, group)${sum(restype, p_groupReserves(group, restype, 'reserve_length'))},
452
    p_gnReserves(grid, node, restype, param_policy) = p_groupReserves(group, restype, param_policy);
453
454
);

455
// Units with reserve provision capabilities
456
457
gnuRescapable(restypeDirection(restype, up_down), gnu(grid, node, unit))
    $ { p_gnuReserves(grid, node, unit, restype, up_down)
458
459
460
      }
  = yes;

Ciara O'Dwyer's avatar
Ciara O'Dwyer committed
461
// Units with offline reserve provision capabilities
462
463
gnuOfflineRescapable(restype, gnu(grid, node, unit))
    $ { p_gnuReserves(grid, node, unit, restype, 'offlineReserveCapability')
Ciara O'Dwyer's avatar
Ciara O'Dwyer committed
464
465
466
467
468
      }
  = yes;

// Restypes with offline reserve provision possibility
offlineRes(restype)
469
    $ {sum(gnu(grid, node, unit),  p_gnuReserves(grid, node, unit, restype, 'offlineReserveCapability'))
Ciara O'Dwyer's avatar
Ciara O'Dwyer committed
470
471
472
473
474
      }
  = yes;

// Units with offline reserve provision possibility
offlineResUnit(unit)
475
    $ {sum((gn(grid, node), restype),  p_gnuReserves(grid, node, unit, restype, 'offlineReserveCapability'))
Ciara O'Dwyer's avatar
Ciara O'Dwyer committed
476
477
478
      }
  = yes;

479
// Node-node connections with reserve transfer capabilities
480
481
restypeDirectionGridNodeNode(restypeDirection(restype, up_down), gn2n(grid, node, node_))
    $ { p_gnnReserves(grid, node, node_, restype, up_down)
482
483
484
      }
  = yes;

485
// Nodes with reserve requirements, units capable of providing reserves, or reserve capable connections
486
487
restypeDirectionGridNode(restypeDirection(restype, up_down), gn(grid, node))
    $ { p_gnReserves(grid, node, restype, up_down)
488
        or p_gnReserves(grid, node, restype, 'useTimeSeries')
489
490
491
        or sum(gnu(grid, node, unit), p_gnuReserves(grid, node, unit, restype, 'portion_of_infeed_to_reserve'))
        or sum(gnu(grid, node, unit), gnuRescapable(restype, up_down, grid, node, unit))
        or sum(gn2n(grid, node, to_node), restypeDirectionGridNodeNode(restype, up_down, grid, node, to_node))
492
493
      }
  = yes;
494

495
496
497
498
499
500
501
502
503
504
// Groups with reserve requirements
restypeDirectionGroup(restypeDirection(restype, up_down), group)
    $ { p_groupReserves(group, restype, 'reserve_length')
      }
  = yes;
restypeDirectionGridNodeGroup(restypeDirection(restype, up_down), gnGroup(grid, node, group))
    $ { p_groupReserves(group, restype, 'reserve_length')
      }
  = yes;

505
* --- Correct values for critical reserve related parameters - Part 2 ---------
506

507
// Reserve reliability assumed to be perfect if not provided in data
508
509
510
p_gnuReserves(gnu(grid, node, unit), restype, 'reserveReliability')
    ${  not p_gnuReserves(grid, node, unit, restype, 'reserveReliability')
        and sum(up_down, gnuRescapable(restype, up_down, grid, node, unit))
511
        }
512
513
    = 1;

514
// Reserve provision overlap decreases the capacity of the overlapping category
Ciara O'Dwyer's avatar
Ciara O'Dwyer committed
515
loop(restype,
516
517
518
519
520
521
p_gnuReserves(gnu(grid, node, unit), restype, up_down)
    ${ gnuRescapable(restype, up_down, grid, node, unit) }
    = p_gnuReserves(grid, node, unit, restype, up_down)
        - sum(restype_${ p_gnuRes2Res(grid, node, unit, restype_, up_down, restype) },
            + p_gnuReserves(grid, node, unit, restype_, up_down)
                * p_gnuRes2Res(grid, node, unit, restype_, up_down, restype)
522
        ); // END sum(restype_)
Ciara O'Dwyer's avatar
Ciara O'Dwyer committed
523
);
524

525
526
527
* =============================================================================
* --- Data Integrity Checks ---------------------------------------------------
* =============================================================================
528

529
530
531
532
533
534
535
536
537
538
* --- 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)

539
540
* --- Check the integrity of node connection related data ---------------------

541
Option clear = count;
542
543
544
545
546
547
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'),
548
            put log '!!! Error occurred on gn2n link ' node.tl:0 '-' node_.tl:0 /;
549
            put log '!!! Abort: Conflicting transferCapBidirectional parameters!' /;
550
551
552
553
            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')),
554
            put log '!!! Error occurred on gn2n link ' node.tl:0 '-' node_.tl:0 /;
555
            put log '!!! Abort: Parameter transferCapBidirectional must be greater than or equal to defined one-directional transfer capacities!' /;
556
557
558
559
560
            abort "Parameter 'transferCapBidirectional' must be greater than or equal to defined one-directional transfer capacities!"
        );
    );
);

561
562
563
564
565
566
* --- 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));

567
568
569
// Expand the effLevelGroupUnit when possible, abort if impossible
loop(effLevel${ord(effLevel)<=tmp},
    effLevelGroupUnit(effLevel, effSelector, unit)
570
        ${not sum(effLevelGroupUnit(effLevel, effSelector_, unit), 1)}
571
        = effLevelGroupUnit(effLevel - 1, effSelector, unit) // Expand previous (effLevel, effSelector) when applicable
572
    loop(unit${not unit_flow(unit) and not sameas(unit, 'empty')},
573
574
575
576
577
578
579
580
        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!"
        );
    );
);

581
loop( unit,
582
    // Check that 'op' is defined correctly
583
    Option clear = count; // Initialize the previous op to zero
584
    loop( op,
585
        if (p_unit(unit, op) + 1${not p_unit(unit, op)} < count,
586
587
588
            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!";
589
        ); // END if(p_unit)
590
        count = p_unit(unit, op);
591
    ); // END loop(op)
592
593
    // Check that efficiency approximations have sufficient data
    loop( effLevelGroupUnit(effLevel, effSelector, unit),
594
595
596
        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'),
597
598
599
                    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'!";
600
601
602
603
                ); // END if(effDirectOn)
            ); // END loop(op_)
        ); // END loop(op__)
    ); // END loop(effLevelGroupUnit)
604
605
);

606
* --- Check startupfuel fraction related data ----------------------------------------
607

608
609
loop( unit${sum(starttype$p_uStartup(unit, starttype, 'consumption'), 1)},
    if(sum(node, p_uStartupfuel(unit, node, 'fixedFuelFraction')) <> 1,
610
        put log '!!! Error occurred on unit ' unit.tl:0 /;
611
        put log '!!! Abort: The sum of fixedFuelFraction over start-up fuels needs to be one for all units using start-up fuels!' /;
612
        abort "The sum of 'fixedFuelFraction' over start-up fuels needs to be one for all units using start-up fuels!"
613
614
    );
);
615

616
617
loop( unit${sum((constraint, node)$p_unitConstraintNode(unit, constraint, node), 1)},
    if(sum((constraint, node)$p_unitConstraintNode(unit, constraint, node), 1) < 2,
618
        put log '!!! Error occurred on unit ' unit.tl:0 /;
619
620
        put log '!!! Abort: constraint requires at least two inputs or outputs!' /;
        abort "a constraint has to have more tha one input or output!"
621
622
623
    );
);

624
625
* --- Check the shutdown time related data ------------------------------------

626
loop( unitStarttype(unit, starttypeConstrained),
627
628
    if(p_unit(unit, 'minShutdownHours') > p_unit(unit, 'startWarmAfterXhours')
        or p_unit(unit, 'startWarmAfterXhours') > p_unit(unit, 'startColdAfterXhours'),
629
        put log '!!! Error occurred on unit ', unit.tl:0 /;
630
        put log '!!! Abort: Units should have p_unit(unit, minShutdownHours) <= p_unit(unit, startWarmAfterXhours) <= p_unit(unit, startColdAfterXhours)!' /;
631
632
633
        abort "Units should have p_unit(unit, 'minShutdownHours') <= p_unit(unit, 'startWarmAfterXhours') <= p_unit(unit, 'startColdAfterXhours')!"
    );
);
634

635
* --- Check reserve related data ----------------------------------------------
636

637
638
639
640
loop( restypeDirectionGroup(restype, up_down, group),
    // Check that reserve_length is long enough for proper commitment of reserves
    if(p_groupReserves(group, restype, 'reserve_length') < p_groupReserves(group, restype, 'update_frequency') + p_groupReserves(group, restype, 'gate_closure'),
        put log '!!! Error occurred on group ', group.tl:0 /;
641
        put log '!!! Abort: The reserve_length parameter should be longer than update_frequency + gate_closure to fix the reserves properly!' /;
642
643
        abort "The 'reserve_length' parameter should be longer than 'update_frequency' + 'gate_closure' to fix the reserves properly!"
    ); // END if
644
645
646
647
648
649
650
651
652
    // Check that the duration of reserve activation is less than the reserve reactivation time
    if(p_groupReserves(group, restype, 'reserve_reactivation_time') < p_groupReserves(group, restype, 'reserve_activation_duration'),
        put log '!!! Error occurred on group ', group.tl:0 /;
        put log '!!! Abort: The reserve_reactivation_time should be greater than or equal to the reserve_activation_duration!' /;
        abort "The reserve_reactivation_time should be greater than or equal to the reserve_activation_duration!"
    ); // END if
); // END loop(restypeDirectionGroup)

loop( restypeDirectionGridNode(restype, up_down, grid, node),
653
654
655
656
657
658
659
660
661
662
663
664
    // Check for each restype that a node does not belong to multiple groups
    if(sum(restypeDirectionGridNodeGroup(restype, up_down, grid, node, group), 1) > 1,
        put log '!!! Error occurred on node ', node.tl:0 /;
        put log '!!! Abort: For each reserve type, a node can belong to at maximum one reserve node group!' /;
        abort "For each reserve type, a node can belong to at maximum one reserve node group!"
    ); // END if
    // Check if there are units/interconnections connected to a node that does not belong to any restypeDirectionGroup
    if(sum(restypeDirectionGridNodeGroup(restype, up_down, grid, node, group), 1) < 1,
        put log '!!! Error occurred on node ', node.tl:0 /;
        put log '!!! Abort: A node with reserve provision/transfer capability has to belong to a reserve node group!' /;
        abort "A node with reserve provision/transfer capability has to belong to a reserve node group!"
    ); // END if
665
); // END loop(restypeDirectionGridNode)
666

667
// Check that reserve overlaps are possible
668
669
loop( (gnu(grid, node, unit), restypeDirection(restype, up_down)),
    if( p_gnuReserves(grid, node, unit, restype, up_down) < 0,
670
        put log '!!! Error occurred on unit ', unit.tl:0 /;
671
672
673
674
        put log '!!! Abort: Overlapping reserve capacities in p_gnuRes2Res can result in excess reserve production!' /;
        abort "Overlapping reserve capacities in p_gnuRes2Res can result in excess reserve production!"
    ); // END if(p_gnuReserves)
); // END loop((gnu,restypeDirection))
675

676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
* --- Check investment related data -------------------------------------------

// Check that units with LP investment possibility have unitSize
loop( unit_investLP(unit),
    if(not sum(gnu(grid, node, unit), abs(p_gnu(grid, node, unit, 'unitSize'))),
        put log '!!! Error occurred on unit ', unit.tl:0 /;
        put log '!!! Abort: Unit is listed as an investment option but it has no unitSize!' /;
        abort "All units with investment possibility should have 'unitSize' in p_gnu!"
    ); // END if
); // END loop(unit_investLP)
// Check that units with MIP investment possibility have unitSize
loop( unit_investMIP(unit),
    if(not sum(gnu(grid, node, unit), abs(p_gnu(grid, node, unit, 'unitSize'))),
        put log '!!! Error occurred on unit ', unit.tl:0 /;
        put log '!!! Abort: Unit is listed as an investment option but it has no unitSize!' /;
        abort "All units with investment possibility should have 'unitSize' in p_gnu!"
    ); // END if
); // END loop(unit_investMIP)

695
696
697
* --- Check consistency of inputs for superposed node states -------------------

* no checking yet because node_superpos is not given in the gdx input
698

699
700
701
702

* =============================================================================
* --- Default values  ---------------------------------------------------------
* =============================================================================
703
704
705
loop(timeseries$(not sameas(timeseries, 'ts_cf')),
    p_tsMinValue(gn, timeseries) = -Inf;
    p_tsMaxValue(gn, timeseries) = Inf;
706
);
707
708
p_tsMinValue(flowNode, 'ts_cf') = 0;
p_tsMaxValue(flowNode, 'ts_cf') = 1;
709
710
711
712
713
714

* By default all nodes use forecasts for all timeseries
gn_forecasts(gn, timeseries) = yes;
gn_forecasts(flowNode, timeseries) = yes;
gn_forecasts(restype, node, 'ts_reserveDemand') = yes;