3d_setVariableLimits.gms 12.4 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
* --- Variable limits ---------------------------------------------------------
// v_state absolute boundaries set according to p_gn parameters;
20
// When using constant values and to supplement time series with constant values (time series will override when data available)
21
22
v_state.up(gn_state(grid, node), ft_full(f, t))${   p_gnBoundaryPropertiesForStates(grid, node,   'upwardLimit', 'useConstant')
                                                    and [ft_dynamic(f, t) or ord(t) = tSolveFirst]
23
    } = p_gnBoundaryPropertiesForStates(grid, node,   'upwardLimit', 'constant') * p_gnBoundaryPropertiesForStates(grid, node,   'upwardLimit', 'multiplier');
24
25
v_state.lo(gn_state(grid, node), ft_full(f, t))${   p_gnBoundaryPropertiesForStates(grid, node, 'downwardLimit', 'useConstant')
                                                    and [ft_dynamic(f, t) or ord(t) = tSolveFirst]
26
    } = p_gnBoundaryPropertiesForStates(grid, node, 'downwardLimit', 'constant') * p_gnBoundaryPropertiesForStates(grid, node, 'downwardLimit', 'multiplier');
27
v_state.fx(gn_state(grid, node), ft_full(f, t))${   p_gn(grid, node, 'boundAll')
28
                                                    and p_gnBoundaryPropertiesForStates(grid, node, 'reference', 'useConstant')
29
                                                    and [ft_dynamic(f, t) or ord(t) = tSolveFirst]
30
31
32
    } = p_gnBoundaryPropertiesForStates(grid, node, 'reference', 'constant') * p_gnBoundaryPropertiesForStates(grid, node, 'reference', 'multiplier');

// When using time series
33
34
35
36
37
38
39
40
41
v_state.up(gn_state(grid, node), ft_full(f, t))${   p_gnBoundaryPropertiesForStates(grid, node,   'upwardLimit', 'useTimeSeries')
                                                    and not cf_Central(f,t)
                                                    and [ft_dynamic(f, t) or ord(t) = tSolveFirst]
    } = ts_nodeState_(grid, node,   'upwardLimit', f, t) * p_gnBoundaryPropertiesForStates(grid, node,   'upwardLimit', 'multiplier');
v_state.lo(gn_state(grid, node), ft_full(f, t))${   p_gnBoundaryPropertiesForStates(grid, node, 'downwardLimit', 'useTimeSeries')
                                                    and not cf_Central(f,t)
                                                    and [ft_dynamic(f, t) or ord(t) = tSolveFirst]
    } = ts_nodeState_(grid, node, 'downwardLimit', f, t) * p_gnBoundaryPropertiesForStates(grid, node, 'downwardLimit', 'multiplier');
v_state.fx(gn_state(grid, node), ft_full(f, t))${   p_gn(grid, node, 'boundAll')
42
                                                    and p_gnBoundaryPropertiesForStates(grid, node, 'reference', 'useTimeSeries')
43
44
45
                                                    and not cf_Central(f,t)
                                                    and [ft_dynamic(f, t) or ord(t) = tSolveFirst]
    } = ts_nodeState_(grid, node, 'reference', f, t) * p_gnBoundaryPropertiesForStates(grid, node, 'reference', 'multiplier');
46
47
48
49
50
51

// Other time dependent parameters and variable limits
// Max. energy generation
v_gen.up(gnu(grid, node, unit), ft(f,t))${not unit_flow(unit)
    } = p_gnu(grid, node, unit, 'maxGen') * p_unit(unit, 'availability');
v_gen.up(gnu(grid, node, unit_flow), ft(f,t))      // Should only be about variable generation
52
    = sum(flow$(flowUnit(flow, unit_flow) and nu(node, unit_flow)),
53
          ts_cf_(flow, node, f, t) *
54
55
56
57
58
          p_gnu(grid, node, unit_flow, 'maxGen') *
          p_unit(unit_flow, 'availability')
      );

// Min. generation to zero for units without consumption
59
60
v_gen.lo(gnu(grid, node, unit), ft(f,t))${not p_gnu(grid, node, unit, 'maxCons')
    } = 0;
61
// Max. consumption capacity
62
63
v_gen.lo(gnu(grid, node, unit), ft(f,t))${gnu_input(grid, node, unit)
    } = -p_gnu(grid, node, unit, 'maxCons');
64
65

// In the case of negative generation (currently only used for cooling equipment)
66
67
68
69
v_gen.lo(gnu(grid, node, unit), ft(f,t))${p_gnu(grid, node, unit, 'maxGen') < 0
    } = p_gnu(grid, node, unit, 'maxGen');
v_gen.up(gnu(grid, node, unit), ft(f,t))${p_gnu(grid, node, unit, 'maxGen') < 0
    } = 0;
70
71

// v_online cannot exceed unit count
72
73
74
75
v_online.up(unit, ft_full(f,t))${   uft_online(unit, f, t)
                                    or [    uft_online(unit, f, t+pt(t))
                                            and fRealization(f)
                                        ]
76
    } = p_unit(unit, 'unitCount');
77

78
79
80
81
// Startup and shutdown variables cannot exceed unit count
v_startup.up(uft_online(unit, f, t)) = p_unit(unit, 'unitCount');
v_shutdown.up(uft_online(unit, f, t)) = p_unit(unit, 'unitCount');

82
// Possible constraints for generator ramping speeds
83
84
85
86
87
88
v_genRamp.up(gnu(grid, node, unit), ft(f, t))${ gnuft_ramp(grid, node, unit, f, t)
                                                AND p_gnu(grid, node, unit, 'maxRampUp')
    } = p_gnu(grid, node, unit, 'maxRampUp');
v_genRamp.lo(gnu(grid, node, unit), ft(f, t))${ gnuft_ramp(grid, node, unit, f, t)
                                                AND p_gnu(grid, node, unit, 'maxRampDown')
    } = -p_gnu(grid, node, unit, 'maxRampDown');
89
90

// Max. & min. spilling
91
v_spill.lo(gn(grid, node), ft(f, t))${p_gnBoundaryPropertiesForStates(grid, node, 'minSpill', 'useConstant')
92
    } = p_gnBoundaryPropertiesForStates(grid, node, 'minSpill', 'constant') * p_gnBoundaryPropertiesForStates(grid, node, 'minSpill', 'multiplier');
93
v_spill.lo(gn(grid, node), ft(f, t))${p_gnBoundaryPropertiesForStates(grid, node, 'minSpill', 'useTimeSeries')
94
    } = ts_nodeState_(grid, node, 'minSpill', f, t) * p_gnBoundaryPropertiesForStates(grid, node, 'minSpill', 'multiplier');
95
v_spill.up(gn(grid, node), ft(f, t))${p_gnBoundaryPropertiesForStates(grid, node, 'maxSpill', 'useConstant')
96
    } = p_gnBoundaryPropertiesForStates(grid, node, 'maxSpill', 'constant') * p_gnBoundaryPropertiesForStates(grid, node, 'maxSpill', 'multiplier');
97
v_spill.up(gn(grid, node), ft(f, t))${p_gnBoundaryPropertiesForStates(grid, node, 'maxSpill', 'useTimeSeries')
98
    } = ts_nodeState_(grid, node, 'maxSpill', f, t) * p_gnBoundaryPropertiesForStates(grid, node, 'maxSpill', 'multiplier');
99
100

// Restrictions on transferring energy between nodes
101
v_transfer.up(gn2n(grid, from_node, to_node), ft(f, t))
102
    = p_gnn(grid, from_node, to_node, 'transferCap');
Topi Rasku's avatar
Topi Rasku committed
103
104
v_resTransfer.up(restypeDirectionNode(restype, up_down, from_node), to_node, ft(f, t))${    restypeDirectionNode(restype, up_down, to_node)
                                                                                            and sum(grid, gn2n(grid, from_node, to_node))
105
    } = p_gnn('elec', from_node, to_node, 'transferCap');
106
107

// Reserve provision limits based on resXX_range (or possibly available generation in case of unit_flow)
108
109
v_reserve.up(nuRescapable(restype, 'up', node, unit_elec), f+cf_nReserves(node, restype, f, t), t)${    nuft(node, unit_elec, f, t)
                                                                                                        and ord(t) < tSolveFirst + mSettings(mSolve, 't_reserveLength')
110
    } = min {   p_nuReserves(node, unit_elec, restype, 'up') * [ p_gnu('elec', node, unit_elec, 'maxGen') + p_gnu('elec', node, unit_elec, 'maxCons') ],  // Generator + consuming unit res_range limit
111
112
113
114
115
                v_gen.up('elec', node, unit_elec, f, t) - v_gen.lo('elec', node, unit_elec, f, t) // Generator + consuming unit available unit_elec. output delta
                }
            * ( + 1${ft_nReserves(node, restype, f+cf_nReserves(node, restype, f, t), t)} // reserveContribution limits the reliability of reserves locked ahead of time.
                + p_nuReserves(node, unit_elec, restype, 'reserveContribution')${not ft_nReserves(node, restype, f+cf_nReserves(node, restype, f, t), t)}
              );
116
117
v_reserve.up(nuRescapable(restype, 'down', node, unit_elec), f+cf_nReserves(node, restype, f, t), t)${  nuft(node, unit_elec, f, t)
                                                                                                        and ord(t) < tSolveFirst + mSettings(mSolve, 't_reserveLength')
118
    } = min {   p_nuReserves(node, unit_elec, restype, 'down') * [ p_gnu('elec', node, unit_elec, 'maxGen') + p_gnu('elec', node, unit_elec, 'maxCons') ],  // Generator + consuming unit res_range limit
119
                v_gen.up('elec', node, unit_elec, f, t) - v_gen.lo('elec', node, unit_elec, f, t) // Generator + consuming unit available unit_elec. output delta
120
121
122
123
                }
            * ( + 1${ft_nReserves(node, restype, f+cf_nReserves(node, restype, f, t), t)} // reserveContribution limits the reliability of reserves locked ahead of time.
                + p_nuReserves(node, unit_elec, restype, 'reserveContribution')${not ft_nReserves(node, restype, f+cf_nReserves(node, restype, f, t), t)}
              );
124

Topi Rasku's avatar
Topi Rasku committed
125
126
// Fix reserves between t_jump and gate_closure based on previous allocations
loop(restypeDirectionNode(restypeDirection(restype, up_down), node),
127
128
        v_reserve.fx(nuRescapable(restype, up_down, node, unit_elec), f, t)${   ft_nReserves(node, restype, f, t)
                                                                                and ord(t) >= mSettings(mSolve, 't_start') + p_nReserves(node, restype, 'update_frequency') // Don't lock reserves before the first update
129
                                                                                and not unit_flow(unit_elec) // NOTE! Units using flows can change their reserve (they might not have as much available in real time as they had bid)
130
131
            } = r_reserve(restype, up_down, node, unit_elec, f, t);
        v_resTransfer.fx(restype, up_down, node, to_node, f, t)${   ft_nReserves(node, restype, f, t)
132
133
134
                                                                    and restypeDirectionNode(restype, up_down, to_node)
                                                                    and ord(t) >= mSettings(mSolve, 't_start') + p_nReserves(node, restype, 'update_frequency') // Don't lock reserves before the first update
                                                                    and sum(grid, gn2n(grid, node, to_node))
135
            } = r_resTransfer(restype, up_down, node, to_node, f, t);
136
137
138
139
140
141

    // Free the tertiary reserves for the realization
    v_reserve.fx(nuRescapable('tertiary', up_down, node, unit_elec), ft_realized(ft(f,t)))${    nuft(node, unit_elec, f, t)
        } = 0;
    v_resTransfer.fx('tertiary', up_down, node, to_node, ft_realized(ft(f,t)))${    sum(grid, gn2n(grid, node, to_node))
        } = 0;
Topi Rasku's avatar
Topi Rasku committed
142
143
);

144
145
* --- Bounds overwritten for the first timestep --------------------------------
loop(mftStart(mSolve, f, t),
146
    // First solve, state variables (only if boundStart flag is true)
147
148
149
    v_state.fx(gn_state(grid, node), f, t)${    p_gn(grid, node, 'boundStart')
                                                and p_gnBoundaryPropertiesForStates(grid, node, 'reference', 'useConstant')
                                                and tSolveFirst = mSettings(mSolve, 't_start')
150
151
        } = p_gnBoundaryPropertiesForStates(grid, node, 'reference', 'constant') * p_gnBoundaryPropertiesForStates(grid, node, 'reference', 'multiplier');
    // Time series form boundary
152
153
154
155
    v_state.fx(gn_state(grid, node), f, t)${    p_gn(grid, node, 'boundStart')
                                                and p_gnBoundaryPropertiesForStates(grid, node, 'reference', 'useTimeSeries')
                                                and tSolveFirst = mSettings(mSolve, 't_start')
        } = ts_nodeState_(grid, node, 'reference', f, t) * p_gnBoundaryPropertiesForStates(grid, node, 'reference', 'multiplier');
156
157

    // State and online variables fixed for the subsequent solves
158
159
160
161
    v_state.fx(gn_state(grid, node), f, t)${    not ord(t) = mSettings(mSolve, 't_start')
        } = r_state(grid, node, f, t);
    v_online.fx(uft_online(unit, f, t))${  not ord(t) = mSettings(mSolve, 't_start')
        } = r_online(unit, f, t);
162
163
);

164
// BoundStartToEnd
165
166
v_state.fx(grid, node, ft_full(f,t))${  mftLastSteps(mSolve, f, t)
                                        and p_gn(grid, node, 'boundStartToEnd')
167
    } = r_state(grid, node, f, t);
168

169