4b_outputInvariant.gms 36.5 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
// for performance, get rid of any zeros in r_gen and r_reserve. Many zero values missing anyway.
20
r_gen(gnu, f, t)$((r_gen(gnu, f, t)=0)$r_gen(gnu, f, t))=0;
21
r_reserve(restype, up_down, gnu, f, t)$((r_reserve(restype, up_down, gnu, f, t)=0)$r_reserve(restype, up_down, gnu, f, t))=0;
22

23
* =============================================================================
24
* --- Time Step Dependent Results ---------------------------------------------
25
* =============================================================================
26

27
28
// Need to loop over the model dimension, as this file is no longer contained in the modelSolves loop...
loop(m,
29

30
    option clear=startp; startp(t)$(ord(t) > mSettings(m, 't_start') + mSettings(m, 't_initializationPeriod'))=yes;
Topi Rasku's avatar
Topi Rasku committed
31
32
33
* --- Realized Individual Costs ----------------------------------------------

    // Variable O&M costs
34
    r_gnuVOMCost(gnu(grid, node, unit), ft_realizedNoReset(f,startp(t)))
Topi Rasku's avatar
Topi Rasku committed
35
36
        = 1e-6 // Scaling to MEUR
            * p_stepLengthNoReset(m, f, t)
37
            * abs(r_gen(grid, node, unit, f, t))
38
            * p_gnu(grid, node, unit, 'vomCosts');
Topi Rasku's avatar
Topi Rasku committed
39
40

    // Fuel and emission costs during normal operation
41
42
43
44
    // Note that this result calculation uses ts_price directly while the
    // objective function uses ts_price average over the interval. There can
    // be differences if realized intervals contain several time steps.
    r_uFuelEmissionCost(gnu(grid, node, unit), ft_realizedNoReset(f,startp(t)))
Topi Rasku's avatar
Topi Rasku committed
45
46
        = 1e-6 // Scaling to MEUR
            * p_stepLengthNoReset(m, f, t)
47
48
49
50
51
            * abs(r_gen(grid, node, unit, f, t))
            * [ + p_price(node, 'price')${p_price(node, 'useConstant') and gnu_input(grid, node, unit)}
                + ts_price(node, t)${p_price(node, 'useTimeSeries') and gnu_input(grid, node, unit)}
                - p_price(node, 'price')${p_price(node, 'useConstant') and gnu_output(grid, node, unit)}
                - ts_price(node, t)${p_price(node, 'useTimeSeries') and gnu_output(grid, node, unit)}
Topi Rasku's avatar
Topi Rasku committed
52
                // Emission costs
53
                + sum(emission, p_unitEmissionCost(unit, node, emission))
54
              ];
Topi Rasku's avatar
Topi Rasku committed
55
56

    // Unit startup costs
57
    r_uStartupCost(unit, ft_realizedNoReset(f,startp(t)))$sum(starttype, unitStarttype(unit, starttype))
Topi Rasku's avatar
Topi Rasku committed
58
59
60
        = 1e-6 // Scaling to MEUR
            * sum(unitStarttype(unit, starttype),
                + r_startup(unit, starttype, f, t)
61
                    * [
62
                        // Fuel costs
63
64
                        + p_uStartup(unit, starttype, 'cost') // CUR/start-up
                        // Start-up fuel and emission costs
65
                        + sum(nu_startup(node,unit),
66
67
68
69
                            + p_unStartup(unit, node, starttype) // MWh/start-up
                              * [
                                  + p_price(node, 'price')$p_price(node, 'useConstant') // CUR/MWh
                                  + ts_price(node, t)$p_price(node, 'useTimeseries') // CUR/MWh
70
71
                                  // Emission costs
                                  + sum(emission$p_nEmission(node, emission),
72
                                       + p_nEmission(node, emission) // kg/MWh
73
74
75
76
77
78
79
                                          / 1e3 // NOTE!!! Conversion to t/MWh from kg/MWh in data
                                          * sum(gnGroup(grid, node, group),
                                              + p_groupPolicyEmission(group, 'emissionTax', emission) // CUR/t
                                              ) // END sum(gnGroup)
                                      ) // END sum(emission)
                                ] // END * p_unStartup
                            ) // END sum(nu_startup)
80
                      ] // END * r_startup
81
              ); // END sum(starttype)
Topi Rasku's avatar
Topi Rasku committed
82

83
    //Variable Trnasfer Costs
84
    r_gnnVariableTransCost(gn2n_directional(grid, node_, node), ft_realizedNoReset(f,startp(t)))
85
86
        = 1e-6 // Scaling to MEUR
            * p_stepLengthNoReset(m, f, t)
87
                    *[+ p_gnn(grid, node, node_, 'variableTransCost')
88
                    * r_transferLeftward(grid, node_, node, f, t)
89
                    + p_gnn(grid, node_, node, 'variableTransCost')
90
91
                    * r_transferRightward(grid, node_, node, f, t)];

92
   // Transfer marginal value (Me) calculated from r_transfer * balanceMarginal * transferLosses
93
   r_gnnTransferValue(gn2n_directional(grid, node_, node), ft_realizedNoReset(f,startp(t)))
94
95
96
97
98
99
100
101
102
        = p_stepLengthNoReset(m, f, t)
            * [ r_transferRightward(grid, node_, node, f, t)
                * r_balanceMarginal(grid, node, f, t)
                - r_transferLeftward(grid, node_, node, f, t)
                * r_balanceMarginal(grid, node_, f, t)
              ]
            * [ 1 - p_gnn(grid, node_, node, 'transferLoss')${not gn2n_timeseries(grid, node_, node, 'transferLoss')}
                - ts_gnn_(grid, node_, node, 'transferLoss', f, t)${gn2n_timeseries(grid, node_, node, 'transferLoss')}
              ]
103
104
    ;

Topi Rasku's avatar
Topi Rasku committed
105
    // Node state slack costs
106
    r_gnStateSlackCost(gn_stateSlack(grid, node), ft_realizedNoReset(f,startp(t)))
Topi Rasku's avatar
Topi Rasku committed
107
108
109
110
111
112
113
114
        = 1e-6 // Scaling to MEUR
            * p_stepLengthNoReset(m, f, t)
            * sum(slack${ p_gnBoundaryPropertiesForStates(grid, node, slack, 'slackCost') },
                + r_stateSlack(grid, node, slack, f, t)
                    * p_gnBoundaryPropertiesForStates(grid, node, slack, 'slackCost')
                ); // END sum(slack)

    // Storage Value Change
115
    r_gnStorageValueChange(gn_state(grid, node))${ active(m, 'storageValue') }
Topi Rasku's avatar
Topi Rasku committed
116
117
118
        = 1e-6
            * [
                + sum(ft_realizedNoReset(f,t)${ ord(t) = mSettings(m, 't_end') + 1 },
119
                    + [
Topi Rasku's avatar
Topi Rasku committed
120
                        + p_storageValue(grid, node)${ not p_gn(grid, node, 'storageValueUseTimeSeries') }
121
122
                        + ts_storageValue(grid, node, f, t)${ p_gn(grid, node, 'storageValueUseTimeSeries') }
                      ]
Topi Rasku's avatar
Topi Rasku committed
123
124
                        * r_state(grid, node, f, t)
                    ) // END sum(ft_realizedNoReset)
125
                - sum(ft_realizedNoReset(f,t)${ ord(t) = mSettings(m, 't_start') + mSettings(m, 't_initializationPeriod') }, // INITIAL v_state NOW INCLUDED IN THE RESULTS
126
                    + [
Topi Rasku's avatar
Topi Rasku committed
127
                        + p_storageValue(grid, node)${ not p_gn(grid, node, 'storageValueUseTimeSeries') }
128
129
                        + ts_storageValue(grid, node, f, t)${ p_gn(grid, node, 'storageValueUseTimeSeries') }
                      ]
Topi Rasku's avatar
Topi Rasku committed
130
131
132
133
                        * r_state(grid, node, f, t)
                    ) // END sum(ft_realizedNoReset)
                ]; // END * 1e-6

134
    // Diffusion from node to node_
135
136
137
    // Note that this result paramater does not necessarily consider the
    // implicit node state variable dynamics properly if energyStoredPerUnitOfState
    // is not equal to 0
138
139
140
141
142
143
    r_gnnDiffusion(gn_state(grid, node), node_, ft_realizedNoReset(f,startp(t)))
        ${gnn_state(grid, node, node_) or gnn_state(grid, node_, node)}
        = p_gnn(grid, node, node_, 'diffCoeff') * r_state(grid, node, f, t)
            - p_gnn(grid, node_, node, 'diffCoeff') * r_state(grid, node_, f, t)
            ;

144
* --- Total Cost Components (discounted) --------------------------------------
Topi Rasku's avatar
Topi Rasku committed
145
146

    // Total VOM costs
147
    r_gnuTotalVOMCost(gnu(grid, node, unit))
148
        = sum(ft_realizedNoReset(f,startp(t)),
Topi Rasku's avatar
Topi Rasku committed
149
            + r_gnuVOMCost(grid, node, unit, f, t)
150
                * sum(msft_realizedNoReset(m, s, f, t), p_msProbability(m, s) * p_msWeight(m, s) * p_s_discountFactor(s))
Topi Rasku's avatar
Topi Rasku committed
151
152
            );

153
154
    // Total Variable Transfer costs
    r_gnnTotalVariableTransCost(gn2n_directional(grid, node_, node))
155
        = sum(ft_realizedNoReset(f,startp(t)),
156
            + r_gnnVariableTransCost(grid, node_, node, f, t)
157
158
159
                * sum(msft_realizedNoReset(m, s, f, t), p_msProbability(m, s) * p_msWeight(m, s) * p_s_discountFactor(s))
            );

160
    // Total transfer marginal value over the simulation
161
    r_gnnTotalTransferValue(gn2n_directional(grid, node_, node))
162
        = sum(ft_realizedNoReset(f,startp(t)),
163
            + r_gnnTransferValue(grid, node_, node, f, t)
164
165
166
167
                * sum(msft_realizedNoReset(m, s, f, t), p_msProbability(m, s) * p_msWeight(m, s) * p_s_discountFactor(s))
            )
    ;

Topi Rasku's avatar
Topi Rasku committed
168
    // Total fuel & emission costs
169
    r_uTotalFuelEmissionCost(gnu(grid, node, unit))
170
        = sum(ft_realizedNoReset(f,startp(t)),
171
            + r_uFuelEmissionCost(grid, node, unit, f, t)
172
                * sum(msft_realizedNoReset(m, s, f, t), p_msProbability(m, s) * p_msWeight(m, s) * p_s_discountFactor(s))
Topi Rasku's avatar
Topi Rasku committed
173
174
175
            );

    // Total unit startup costs
176
177
    r_uTotalStartupCost(unit)$sum(starttype, unitStarttype(unit, starttype))
        = sum(ft_realizedNoReset(f,startp(t)),
Topi Rasku's avatar
Topi Rasku committed
178
            + r_uStartupCost(unit, f, t)
179
                * sum(msft_realizedNoReset(m, s, f, t), p_msProbability(m, s) * p_msWeight(m, s) * p_s_discountFactor(s))
Topi Rasku's avatar
Topi Rasku committed
180
181
182
183
            );

    // Total state variable slack costs
    r_gnTotalStateSlackCost(gn_stateSlack(grid, node))
184
        = sum(ft_realizedNoReset(f,startp(t)),
Topi Rasku's avatar
Topi Rasku committed
185
            + r_gnStateSlackCost(grid, node, f, t)
186
                * sum(msft_realizedNoReset(m, s, f, t), p_msProbability(m, s) * p_msWeight(m, s) * p_s_discountFactor(s))
Topi Rasku's avatar
Topi Rasku committed
187
188
            );

Niina Helistö's avatar
Niina Helistö committed
189
190
191
    // Fixed O&M costs
    r_gnuFOMCost(gnu(grid, node, unit))
        = 1e-6 // Scaling to MEUR
192
193
            * sum(ms(m, s)${ sum(msft_realizedNoReset(m, s, f, t_), 1) }, // consider ms only if it has active msft_realizedNoReset
                + [
194
                    + p_gnu(grid, node, unit, 'capacity')$sum(msft_realizedNoReset(m, s, f, t_), uft(unit, f, t_)) // Not in v_obj; only units active in msft_realizedNoReset
195
                    + r_invest(unit)$sum(msft_realizedNoReset(m, s, f, t_), uft(unit, f, t_)) // only units active in msft_realizedNoReset
196
                        * p_gnu(grid, node, unit, 'unitSize')
197
198
                    ]
                    * p_msAnnuityWeight(m, s) // Sample weighting to calculate annual costs
199
                    * p_s_discountFactor(s) // Discount costs
200
                ) // END * sum(ms)
Niina Helistö's avatar
Niina Helistö committed
201
202
203
204
205
            * p_gnu(grid, node, unit, 'fomCosts');

    // Unit investment costs
    r_gnuUnitInvestmentCost(gnu(grid, node, unit))
        = 1e-6 // Scaling to MEUR
206
            * sum(ms(m, s)${ sum(msft_realizedNoReset(m, s, f, t_), 1) }, // consider ms only if it has active msft_realizedNoReset
207
                + r_invest(unit)$sum(msft_realizedNoReset(m, s, f, t_), uft(unit, f, t_)) // only units active in msft_realizedNoReset
208
                    * p_msAnnuityWeight(m, s) // Sample weighting to calculate annual costs
209
                    * p_s_discountFactor(s) // Discount costs
210
                ) // END * sum(ms)
211
            * p_gnu(grid, node, unit, 'unitSize')
Niina Helistö's avatar
Niina Helistö committed
212
213
214
215
            * p_gnu(grid, node, unit, 'invCosts')
            * p_gnu(grid, node, unit, 'annuity');

    // Transfer link investment costs
216
    r_gnnLinkInvestmentCost(gn2n_directional(grid, from_node, to_node)) // gn2n_directional only, as in q_obj
Niina Helistö's avatar
Niina Helistö committed
217
        = 1e-6 // Scaling to MEUR
218
219
220
221
222
            * sum(ms(m, s)${ sum(msft_realizedNoReset(m, s, f, t_), 1) }, // consider ms only if it has active msft_realizedNoReset
                + sum(t_invest(t)${ord(t) <= msEnd(m, s)}, // only if investment was made before or during the sample
                    + r_investTransfer(grid, from_node, to_node, t)
                    )
                    * p_msAnnuityWeight(m, s) // Sample weighting to calculate annual costs
223
                    * p_s_discountFactor(s) // Discount costs
224
                ) // END * sum(ms)
Niina Helistö's avatar
Niina Helistö committed
225
226
227
228
229
230
231
            * [
                + p_gnn(grid, from_node, to_node, 'invCost')
                    * p_gnn(grid, from_node, to_node, 'annuity')
                + p_gnn(grid, to_node, from_node, 'invCost')
                    * p_gnn(grid, to_node, from_node, 'annuity')
                ]; // END * r_investTransfer;

232
233
* --- Realized Nodal System Costs ---------------------------------------------

Niina Helistö's avatar
Niina Helistö committed
234
    // Total realized gn operating costs
235
    r_gnRealizedOperatingCost(gn(grid, node), ft_realizedNoReset(f, startp(t)))
236
237
238
        = + sum(gnu(grid, node, unit),
              // VOM costs
              + r_gnuVOMCost(grid, node, unit, f, t)
239
              + r_uFuelEmissionCost(grid, node, unit, f, t)
240
            )
241
242

          // Allocate startup costs on energy basis, but for output nodes only
243
244
245
246
247
          + sum(unit$(r_gen(grid, node, unit, f, t)$gnu_output(grid, node, unit)),
              + abs{r_gen(grid, node, unit, f, t)}  // abs is due to potential negative outputs like energy from a cooling unit. It's the energy contribution that matters, not direction.
                   / sum(gnu_output(grid_output, node_output, unit),
                       + abs{r_gen(grid_output, node_output, unit, f, t)}
                     ) // END sum(gnu_output)
248
                * r_uStartupCost(unit, f, t)
249
            )
250
          + sum(gn2n_directional(grid, node_, node),
251
252
              // Variable Transfer costs
              + r_gnnVariableTransCost(grid, node_, node, f, t)
253
            )
254
255
          // Node state slack costs
          + r_gnStateSlackCost(grid, node, f, t);
256
257
258
259

* --- Realized Nodal Energy Consumption ---------------------------------------
// !!! NOTE !!! This is a bit of an approximation at the moment !!!!!!!!!!!!!!!

260
    r_gnConsumption(gn(grid, node), ft_realizedNoReset(f, startp(t)))
261
262
263
264
265
266
267
268
        = p_stepLengthNoReset(m, f, t)
            * [
                + min(ts_influx(grid, node, f, t), 0) // Not necessarily a good idea, as ts_influx contains energy gains as well...
                + sum(gnu_input(grid, node, unit),
                    + r_gen(grid, node, unit, f, t)
                    ) // END sum(gnu_input)
                ];

269
* --- Energy Generation -------------------------------------------------------
270

271
    // Energy output to a node based on inputs from another node or flows
272
273
    r_genFuel(gn(grid, node), node_, ft_realizedNoReset(f, startp(t)))$sum(gnu_input(grid_, node_, unit)$gnu_output(grid, node, unit),r_gen(grid_, node_, unit, f, t))
        = sum(gnu_output(grid, node, unit)$sum(gnu_input(grid_, node_, unit), 1),
274
            + r_gen(grid, node, unit, f, t)
275
276
277
278
279
280
281
282
283
          );
// The calculation with multiple inputs needs to be fixed below (right share for different commodities - now units with multiple input commodities will get the same amount allocated which will then be too big
//          * sum((grid_, unit)$gnu_output(grid, node, unit),
//                r_gen(grid_, commodity, unit, f, t))
//                  / sum(gnu_input(grid__, node_, unit), r_gen(grid__, node_, unit, f, t));

    r_genFuel(gn(grid, node), flow, ft_realizedNoReset(f, t))$flowNode(flow, node)
        = sum(gnu_output(grid, node, unit)$flowUnit(flow, unit),
            + r_gen(grid, node, unit, f, t));
284

285
    // Energy generation for each unittype
286
    r_genUnittype(gn(grid, node), unittype, ft_realizedNoReset(f,startp(t)))
287
        = sum(gnu(grid, node, unit)$unitUnittype(unit, unittype),
288
            + r_gen(grid, node, unit, f, t)
Juha Kiviluoma's avatar
Juha Kiviluoma committed
289
290
            ); // END sum(unit)

291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
    // Unit start-up consumption
    r_nuStartupConsumption(nu_startup(node, unit), ft_realizedNoReset(f,startp(t)))
        ${sum(starttype, unitStarttype(unit, starttype))}
        = sum(unitStarttype(unit, starttype),
            + r_startup(unit, starttype, f, t)
                * p_unStartup(unit, node, starttype) // MWh/start-up
            ); // END sum(unitStarttype)

* --- Total Energy Generation -------------------------------------------------

    // Total energy generation in gnu
    r_gnuTotalGen(gnu(grid, node, unit))
        = sum(ft_realizedNoReset(f, startp(t)),
            + r_gen(grid, node, unit, f, t)
                * p_stepLengthNoReset(m, f, t)
                * sum(msft_realizedNoReset(m, s, f, t), p_msProbability(m, s) * p_msWeight(m, s))
            ); // END sum(ft_realizedNoReset)

    // Total energy generation in gnu by unit type
    r_gnuTotalGen_unittype(gn(grid, node), unittype)$sum(unit$unitUnittype(unit, unittype), 1)
      = sum(gnu(grid,node,unit)$unitUnittype(unit, unittype),
             + r_gnuTotalGen(grid, node, unit)
            );

315
316
317
318
    // energy generation for each gridnode (MW)
    r_gnGen(grid, node, f, t)
        = sum(unit, r_gen(grid, node, unit, f, t));

319
320
321
322
323
324
325
326
    // Total generation in gn
    r_gnTotalGen(gn(grid, node))
        = sum(unit, r_gnuTotalGen(grid, node, unit));

    // Total generation in g
    r_gTotalGen(grid)
       = sum(gn(grid, node), r_gnTotalGen(grid, node));

327
    // Total energy generation in gn per input type over the simulation
328
    r_gnTotalGenFuel(gn(grid, node), node_)
329
        = sum(ft_realizedNoReset(f, startp(t)),
330
            + r_genFuel(grid, node, node_, f, t)
331
                * p_stepLengthNoReset(m, f, t)
332
                * sum(msft_realizedNoReset(m, s, f, t), p_msProbability(m, s) * p_msWeight(m, s))
333
            ); // END sum(ft_realizedNoReset)
334

335
    // Total dummy generation/consumption in gn
336
    r_gnTotalqGen(inc_dec, gn(grid, node))
337
        = sum(ft_realizedNoReset(f,startp(t)),
338
339
            + r_qGen(inc_dec, grid, node, f, t)
                * p_stepLengthNoReset(m, f, t)
340
                * sum(msft_realizedNoReset(m, s, f, t), p_msProbability(m, s) * p_msWeight(m, s))
341
342
            ); // END sum(ft_realizedNoReset)

343
344
345
346
347
348
349
350
351
    // Total generation gnu/gn shares
    r_gnuTotalGenShare(gnu_output(grid, node, unit))${ r_gnTotalGen(grid, node) > 0 }
       = r_gnuTotalGen(grid, node, unit)
           / r_gnTotalGen(grid, node);

    // Total generation gn/g shares
    r_gnTotalGenShare(gn(grid, node))${ r_gTotalGen(grid) > 0 }
       = r_gnTotalGen(grid, node)
           / r_gTotalGen(grid);
352

353
354
* --- Emission Results --------------------------------------------------------

355
356
357
358
359
    // Emissions of units (not including start-up fuels)
    // Only taking into account emissions from input because emissions from output
    // do not cause costs and are not considered in emission cap
    r_emissions(grid, node, emission, unit, ft_realizedNoReset(f,startp(t)))
       $gnu_input(grid, node, unit)
360
        =   + p_stepLengthNoReset(m, f, t)
361
362
363
364
365
366
367
368
369
370
371
            * abs(r_gen(grid, node, unit, f, t))
            * p_nEmission(node, emission)
            / 1e3 // NOTE!!! Conversion to t/MWh from kg/MWh in data
    ;

    // Emissions from unit outputs
    r_emissionsFromOutput(grid, node, emission, unit, ft_realizedNoReset(f,startp(t)))
        $gnu_output(grid, node, unit)
        =   + p_stepLengthNoReset(m, f, t)
            * r_gen(grid, node, unit, f, t)
            * p_nEmission(node, emission)
372
373
374
            / 1e3 // NOTE!!! Conversion to t/MWh from kg/MWh in data
    ;

375
376
    // Emissions from unit start-ups
    r_emissionsStartup(node, emission, unit, ft_realizedNoReset(f,startp(t)))
377
        ${sum(starttype, p_unStartup(unit, node, starttype))
378
379
380
381
382
383
384
385
          and p_nEmission(node, emission)}
        = sum(unitStarttype(unit, starttype),
            + r_startup(unit, starttype, f, t)
                * p_unStartup(unit, node, starttype) // MWh/start-up
                * p_nEmission(node, emission) // kg/MWh
                / 1e3 // NOTE!!! Conversion to t/MWh from kg/MWh in data
            ); // END sum(starttype)

386
    // Emission sums from normal operation input
387
    r_nuTotalEmissionsOperation(nu(node, unit), emission)
388
389
390
391
392
393
394
        = sum(ft_realizedNoReset(f, startp(t)),
            + sum(gn(grid, node), r_emissions(grid, node, emission, unit, f, t))
                 * sum(msft_realizedNoReset(m, s, f, t), p_msProbability(m, s) * p_msWeight(m, s))
            )
    ;

    // Emission sums from unit outputs
395
    r_nuTotalEmissionsFromOutput(nu(node, unit), emission)
396
397
398
399
400
        = sum(ft_realizedNoReset(f, startp(t)),
            + sum(gn(grid, node), r_emissionsFromOutput(grid, node, emission, unit, f, t))
                 * sum(msft_realizedNoReset(m, s, f, t), p_msProbability(m, s) * p_msWeight(m, s))
            )
    ;
401

402
    // Emission sums from start-ups
403
    r_nuTotalEmissionsStartup(nu_startup(node, unit), emission)
404
        = sum(ft_realizedNoReset(f, startp(t)),
405
            + r_emissionsStartup(node, emission, unit, f, t)
406
407
408
409
                 * sum(msft_realizedNoReset(m, s, f, t), p_msProbability(m, s) * p_msWeight(m, s))
            )
    ;

410
411
412
413
414
415
416
417
    // Emission sums (normal operation input and start-ups)
    r_nuTotalEmissions(node, unit, emission)
        = r_nuTotalEmissionsOperation(node, unit, emission)
            + r_nuTotalEmissionsStartup(node, unit, emission)
    ;

    r_nTotalEmissions(node, emission)
        = sum(unit, r_nuTotalEmissions (node, unit, emission))
418
419
420
    ;

    r_uTotalEmissions(unit, emission)
421
        = sum(node, r_nuTotalEmissions (node, unit, emission))
422
423
424
    ;

    r_totalEmissions (emission)
425
        = sum(node, r_nTotalEmissions(node, emission))
426
427
    ;

428
* --- Total Unit Online Results -----------------------------------------------
429

430
431
    // Total sub-unit-hours for units over the simulation
    r_uTotalOnline(unit)
432
        = sum(ft_realizedNoReset(f, startp(t)),
433
            + r_online(unit, f, t)
434
                * p_stepLengthNoReset(m, f, t)
435
                * sum(msft_realizedNoReset(m, s, f, t), p_msProbability(m, s) * p_msWeight(m, s))
436
            ); // END sum(ft_realizedNoReset)
437

438
    // Approximate utilization rates for gnus over the simulation
439
    r_gnuUtilizationRate(gnu(grid, node, unit))${ r_gnuTotalGen(grid, node, unit)
440
                                                         and ( p_gnu(grid, node, unit, 'capacity')
441
                                                               or r_invest(unit)
442
443
                                                               )
                                                         }
444
445
        = r_gnuTotalGen(grid, node, unit)
            / [
446
                + (p_gnu(grid, node, unit, 'capacity') + r_invest(unit)*p_gnu(grid, node, unit, 'unitSize'))
447
                    * (mSettings(m, 't_end') - (mSettings(m, 't_start') + mSettings(m, 't_initializationPeriod')) + 1)
448
                    * mSettings(m, 'stepLengthInHours')
449
450
451
452
                ]; // END division

* --- Total Reserve Provision -------------------------------------------------

453
    // Total reserve provisions over the simulation
454
    r_gnuTotalReserve(gnuRescapable(restype, up_down, grid, node, unit))
455
        = sum(ft_realizedNoReset(f, startp(t)),
456
            + r_reserve(restype, up_down, grid, node, unit, f, t)
457
                * p_stepLengthNoReset(m, f, t)
458
                * sum(msft_realizedNoReset(m, s, f, t), p_msProbability(m, s) * p_msWeight(m, s))
459
460
            ); // END sum(ft_realizedNoReset)

461
    // Total dummy reserve provisions over the simulation
462
    r_groupTotalqResDemand(restypeDirectionGroup(restype, up_down, group))
463
        = sum(ft_realizedNoReset(f, startp(t)),
464
            + r_qResDemand(restype, up_down, group, f, t)
465
                * p_stepLengthNoReset(m, f, t)
466
                * sum(msft_realizedNoReset(m, s, f, t), p_msProbability(m, s) * p_msWeight(m, s))
467
468
            ); // END sum(ft_realizedNoReset)

469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
    // Total reserve provisions over the simulation
    r_gnTotalReserve(restype, up_down, grid, node)
        =  sum(unit, r_gnuTotalReserve(restype, up_down, grid, node, unit))
    ;

* --- Total Reserve Transfers -------------------------------------------------

    // Total reserve transfer leftward over the simulation
    r_gnnTotalResTransferLeftward(restype, up_down, grid, node, to_node)
        = sum(ft_realizedNoReset(f, startp(t)),
            + r_resTransferLeftward(restype, up_down, grid, node, to_node, f, t)
                * p_stepLengthNoReset(m, f, t)
                * sum(msft_realizedNoReset(m, s, f, t), p_msProbability(m, s) * p_msWeight(m, s))
            ); // END sum(ft_realizedNoReset)

    // Total reserve transfer rightward over the simulation
    r_gnnTotalResTransferRightward(restype, up_down, grid, node, to_node)
        = sum(ft_realizedNoReset(f, startp(t)),
            + r_resTransferRightward(restype, up_down, grid, node, to_node, f, t)
                * p_stepLengthNoReset(m, f, t)
                * sum(msft_realizedNoReset(m, s, f, t), p_msProbability(m, s) * p_msWeight(m, s))
            ); // END sum(ft_realizedNoReset)

492
493
494
495
* --- Total Transfer and Spill ------------------------------------------------

    // Total transfer of energy between nodes
    r_gnnTotalTransfer(gn2n(grid, from_node, to_node))
496
        = sum(ft_realizedNoReset(f, startp(t)),
497
            + r_transfer(grid, from_node, to_node, f, t)
498
                * p_stepLengthNoReset(m, f, t)
499
                * sum(msft_realizedNoReset(m, s, f, t), p_msProbability(m, s) * p_msWeight(m, s))
500
501
            ); // END sum(ft_realizedNoReset)

502
503
    // Total energy spill from nodes
    r_gnTotalSpill(grid, node_spill(node))
504
        = sum(ft_realizedNoReset(f, startp(t)),
505
506
            + r_spill(grid, node, f, t)
                * p_stepLengthNoReset(m, f, t)
507
                * sum(msft_realizedNoReset(m, s, f, t), p_msProbability(m, s) * p_msWeight(m, s))
508
            ); // END sum(ft_realizedNoReset)
509

510
511
512
513
* =============================================================================
* --- Futher Time Step Independent Results ------------------------------------
* =============================================================================

514
* --- Scaling Marginal Values to EUR/MWh from MEUR/MWh ------------------------
515
516

// Energy balance
517
r_balanceMarginal(gn(grid, node), ft_realizedNoReset(f, startp(t)))
518
519
520
    = 1e6 * r_balanceMarginal(grid, node, f, t);

// Reserve balance
521
r_resDemandMarginal(restypeDirectionGroup(restype, up_down, group), ft_realizedNoReset(f, startp(t)))
522
    = 1e6 * r_resDemandMarginal(restype, up_down, group, f, t);
523

524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
* --- Annual averages of marginal values --------------------------------------

r_balanceMarginalAverage(grid, node)
    = sum(ft_realizedNoReset(f, startp(t)),
         + r_balanceMarginal(grid, node, f, t)
            // * p_stepLengthNoReset(m, f, t)   // not including steplength due to division by number of timesteps
            * sum(msft_realizedNoReset(m, s, f, t), p_msProbability(m, s) * p_msWeight(m, s))
        ) // END sum(ft_realizedNoReset)
        / sum(t, t_realized(t)*1) // divided by number of realized time steps
        ;

r_resDemandMarginalAverage(restype, up_down, group)
    = sum(ft_realizedNoReset(f, startp(t)),
         + r_resDemandMarginal(restype, up_down, group, f, t)
            // * p_stepLengthNoReset(m, f, t)   // not including steplength due to division by number of timesteps
            * sum(msft_realizedNoReset(m, s, f, t), p_msProbability(m, s) * p_msWeight(m, s))
        ) // END sum(ft_realizedNoReset)
        / sum(t, t_realized(t)*1) // divided by number of realized time steps
        ;

544
545
546
547
548
549
* --- Total Dummy Generation Results ------------------------------------------

// Total dummy generaion in g
r_gTotalqGen(inc_dec, grid)
    = sum(gn(grid, node), r_gnTotalqGen(inc_dec, grid, node));

550
551
* --- Total Energy Consumption Results ----------------------------------------

552
553
// Total consumption on each gn over the simulation
r_gnTotalConsumption(gn(grid, node))
554
    = sum(ft_realizedNoReset(f, startp(t)),
Niina Helistö's avatar
Niina Helistö committed
555
        + r_gnConsumption(grid, node, f ,t)
556
            * sum(msft_realizedNoReset(m, s, f, t), p_msProbability(m, s) * p_msWeight(m, s))
Niina Helistö's avatar
Niina Helistö committed
557
        );
558

559
560
561
// Total consumption in each grid over the simulation
r_gTotalConsumption(grid)
    = sum(gn(grid, node), r_gnTotalConsumption(grid, node));
562

563
// Total consumption gn/g share
564
r_gnTotalConsumptionShare(gn(grid, node))${ r_gTotalConsumption(grid) > 0 }
565
566
567
    = r_gnTotalConsumption(grid, node)
        / r_gTotalConsumption(grid);

568
* --- Total Energy Generation Results Per Input Type --------------------------
569

570
// Total energy generation in grids per input type over the simulation
571
572
r_gTotalGenFuel(grid, node_)
    = sum(gn(grid, node), r_gnTotalGenFuel(grid, node, node_));
573

574
// Total overall energy generation per input type over the simulation
575
576
r_totalGenFuel(node_)
    = sum(gn(grid, node), r_gnTotalGenFuel(grid, node, node_));
577

578
// Total energy generation in gn per input type as a share of total energy generation in gn across all input types
579
580
r_gnTotalGenFuelShare(gn(grid, node), node_)${ r_gnTotalGen(grid, node) }
    = r_gnTotalGenFuel(grid, node, node_)
581
        / r_gnTotalGen(grid, node);
582
583
584

* --- Total Spilled Energy Results --------------------------------------------

585
586
587
// Total spilled energy in each grid over the simulation
r_gTotalSpill(grid)
    = sum(gn(grid, node_spill(node)), r_gnTotalSpill(grid, node));
588

589
// Total spilled energy gn/g share
590
r_gnTotalSpillShare(gn(grid, node_spill))${ r_gTotalSpill(grid) > 0 }
591
592
593
    = r_gnTotalSpill(grid, node_spill)
        / r_gTotalSpill(grid);

594
* --- Total Costs Results (discounted) ----------------------------------------
595

Niina Helistö's avatar
Niina Helistö committed
596
597
// Total realized operating costs on each gn over the simulation
r_gnTotalRealizedOperatingCost(gn(grid, node))
598
    = sum(ft_realizedNoReset(f, startp(t)),
Niina Helistö's avatar
Niina Helistö committed
599
        + r_gnRealizedOperatingCost(grid, node, f ,t)
600
            * sum(msft_realizedNoReset(m, s, f, t), p_msProbability(m, s) * p_msWeight(m, s) * p_s_discountFactor(s))
Niina Helistö's avatar
Niina Helistö committed
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
        );

// Total realized net operating costs on each gn over the simulation
r_gnTotalRealizedNetOperatingCost(gn(grid, node))
    = r_gnTotalRealizedOperatingCost(grid, node) - r_gnStorageValueChange(grid, node);

// Total realized operating costs on each grid over the simulation
r_gTotalRealizedOperatingCost(grid)
    = sum(gn(grid, node), r_gnTotalRealizedOperatingCost(grid, node));

// Total realized net operating costs on each grid over the simulation
r_gTotalRealizedNetOperatingCost(grid)
    = sum(gn(grid, node), r_gnTotalRealizedNetOperatingCost(grid, node));

// Total realized operating costs gn/g share
616
r_gnTotalRealizedOperatingCostShare(gn(grid, node))${ r_gTotalRealizedOperatingCost(grid) > 0 }
Niina Helistö's avatar
Niina Helistö committed
617
618
619
620
621
622
623
624
625
626
627
    = r_gnTotalRealizedOperatingCost(grid, node)
        / r_gTotalRealizedOperatingCost(grid);

// Total realized operating costs over the simulation
r_totalRealizedOperatingCost
    = sum(gn(grid, node), r_gnTotalRealizedOperatingCost(grid, node));

// Total realized net operating costs over the simulation
r_totalRealizedNetOperatingCost
    = sum(gn(grid, node), r_gnTotalRealizedNetOperatingCost(grid, node));

628
629
// Total realized costs on each gn over the simulation
r_gnTotalRealizedCost(gn(grid, node))
Niina Helistö's avatar
Niina Helistö committed
630
631
632
633
634
635
636
    = r_gnTotalRealizedOperatingCost(grid, node)
        + sum(gnu(grid, node, unit),
            + r_gnuFOMCost(grid, node, unit)
            + r_gnuUnitInvestmentCost(grid, node, unit)
            )
        + sum(gn2n_directional(grid, from_node, node),
            + r_gnnLinkInvestmentCost(grid, from_node, node)
637
                / 2 // Half of the link costs are allocated to the receiving end
Niina Helistö's avatar
Niina Helistö committed
638
639
640
            )
        + sum(gn2n_directional(grid, node, to_node),
            + r_gnnLinkInvestmentCost(grid, node, to_node)
641
                / 2 // Half of the link costs are allocated to the sending end
Niina Helistö's avatar
Niina Helistö committed
642
            );
643

644
645
646
647
// Total realized net costs on each gn over the simulation
r_gnTotalRealizedNetCost(gn(grid, node))
    = r_gnTotalRealizedCost(grid, node) - r_gnStorageValueChange(grid, node);

648
649
650
// Total realized costs on each grid over the simulation
r_gTotalRealizedCost(grid)
    = sum(gn(grid, node), r_gnTotalRealizedCost(grid, node));
651

652
653
654
655
// Total realized net costs on each grid over the simulation
r_gTotalRealizedNetCost(grid)
    = sum(gn(grid, node), r_gnTotalRealizedNetCost(grid, node));

656
// Total realized costs gn/g share
657
r_gnTotalRealizedCostShare(gn(grid, node))${ r_gTotalRealizedCost(grid) > 0 }
658
659
660
    = r_gnTotalRealizedCost(grid, node)
        / r_gTotalRealizedCost(grid);

661
662
// Total realized costs over the simulation
r_totalRealizedCost
663
    = sum(gn(grid, node), r_gnTotalRealizedCost(grid, node));
664

Niina Helistö's avatar
Niina Helistö committed
665
// Total realized net operating costs over the simulation
666
667
668
r_totalRealizedNetCost
    = sum(gn(grid, node), r_gnTotalRealizedNetCost(grid, node));

669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
// Total realized fixed costs on each gn over the simulation
r_gnTotalRealizedFixedCost(gn(grid, node))
    = r_gnTotalRealizedCost(grid, node)
        - r_gnTotalRealizedOperatingCost(grid, node);

// Total realized fixed costs on each grid over the simulation
r_gTotalRealizedFixedCost(grid)
    = r_gTotalRealizedCost(grid)
        - r_gTotalRealizedOperatingCost(grid);

// Total realized fixed costs gn/g share
r_gnTotalRealizedFixedCostShare(gn(grid, node))${ r_gTotalRealizedFixedCost(grid) > 0 }
    = r_gnTotalRealizedFixedCost(grid, node)
        / r_gTotalRealizedFixedCost(grid);

// Total realized fixed costs over the simulation
r_totalRealizedFixedCost
    = r_totalRealizedCost
        - r_totalRealizedOperatingCost;

689
690
691
* --- Reserve Provision Overlap Results ---------------------------------------

// Calculate the overlapping reserve provisions
692
693
694
695
r_reserve2Reserve(gnuRescapable(restype, up_down, grid, node, unit), restype_, ft_realizedNoReset(f, t))
    ${ p_gnuRes2Res(grid, node, unit, restype, up_down, restype_) }
    = r_reserve(restype, up_down, grid, node, unit, f, t)
        * p_gnuRes2Res(grid, node, unit, restype, up_down, restype_);
696

697
698
* --- Total Reserve Provision Results -----------------------------------------

699
// Group sum of reserves of specific types (MW)
700
701
r_groupReserve(restypeDirectionGroup(restype, up_down, group), f, t)
    = sum(gnu(grid, node, unit)${ gnGroup(grid, node, group)
702
703
704
705
706
                                          and groupRestype(group, restype)
                                          },
        + r_reserve(restype, up_down, grid, node, unit, f, t)
      ); // END sum(gnuft)

707
708
709
710
711
// Total reserve provision in groups over the simulation
r_groupTotalReserve(restypeDirectionGroup(restype, up_down, group))
    = sum(gnuRescapable(restype, up_down, grid, node, unit)${gnGroup(grid, node, group)},
        + r_gnuTotalReserve(restype, up_down, grid, node, unit)
    ); // END sum(gnuRescapable)
712

713
714
715
716
r_gnuTotalReserveShare(gnuRescapable(restype, up_down, grid, node, unit))
    ${ sum(gnGroup(grid, node, group), r_groupTotalReserve(restype, up_down, group)) > 0 }
    = r_gnuTotalReserve(restype, up_down, grid, node, unit)
        / sum(gnGroup(grid, node, group), r_groupTotalReserve(restype, up_down, group));
717
718
719

* --- Total Unit Online State Results -----------------------------------------

720
// Total unit online hours per sub-unit over the simulation
721
r_uTotalOnlinePerUnit(unit)${ p_unit(unit, 'unitCount') > 0 }
722
723
724
    = r_uTotalOnline(unit)
        / p_unit(unit, 'unitCount');

725
726
// Total sub-unit startups over the simulation
r_uTotalStartup(unit, starttype)
727
    = sum(ft_realizedNoReset(f, startp(t)),
728
        + r_startup(unit, starttype, f, t)
729
            * sum(msft_realizedNoReset(m, s, f, t), p_msProbability(m, s) * p_msWeight(m, s))
730
731
732
733
        ); // END sum(ft_realizedNoReset)

// Total sub-unit shutdowns over the simulation
r_uTotalShutdown(unit)
734
    = sum(ft_realizedNoReset(f, startp(t)),
735
        + r_shutdown(unit, f, t)
736
            * sum(msft_realizedNoReset(m, s, f, t), p_msProbability(m, s) * p_msWeight(m, s))
737
738
        ); // END sum(ft_realizedNoReset)

739
740
741
742
743
744
745
746
* --- Sum results for groups --------------------------------------------------

// gnTotalgen in units that belong to gnuGroups over the simulation
r_gnTotalGenGnuGroup(grid, node, group)
    = sum(unit $ {gnuGroup(grid, node, unit, group)},
        + r_gnuTotalGen(grid, node, unit)
         ); // END sum(unit)

747
748
* --- Diagnostic Results ------------------------------------------------------

749
// Only include these if '--diag=yes' given as a command line argument
750
$iftheni.diag '%diag%' == yes
751
// Estimated coefficients of performance
752
d_cop(unit, ft_realizedNoReset(f, startp(t)))$sum(gnu_input(grid, node, unit), 1)
753
754
755
756
757
758
759
    = sum(gnu_output(grid, node, unit),
        + r_gen(grid, node, unit, f, t)
        ) // END sum(gnu_output)
        / [ sum(gnu_input(grid_, node_, unit),
                -r_gen(grid_, node_, unit, f, t)
                ) // END sum(gnu_input)
            + 1${not sum(gnu_input(grid_, node_, unit), -r_gen(grid_, node_, unit, f, t))}
760
761
            ]
        + Eps; // Eps to correct GAMS plotting (zeroes are not skipped)
762

763
764
// Estimated efficiency, calculated from inputs
d_eff(unit(unit), ft_realizedNoReset(f, t))$[ord(t) > mSettings(m, 't_start') + mSettings(m, 't_initializationPeriod')]
765
766
767
    = sum(gnu_output(grid, node, unit),
        + r_gen(grid, node, unit, f, t)
        ) // END sum(gnu_output)
768
769
        / [ sum(gnu_input(grid, node, unit),
                + abs(r_gen(grid, node, unit, f, t))
770
                ) // END sum(gnu_input)
771
            + 1${not sum(gnu_input(grid, node, unit), abs(r_gen(node, unit, f, t)))}
772
773
            ]
        + Eps; // Eps to correct GAMS plotting (zeroes are not skipped)
774
$endif.diag
775

776
); // END loop(m)
777