├── EconomicDispatch ├── Instructions for code.txt ├── Integer │ ├── CaseStudy_2gen.m │ ├── FR_limits.jpg │ └── setNadir.m └── Relaxed │ ├── CaseStudy_2gen.m │ ├── NOTE about NaN duals.txt │ ├── setNadir.m │ └── study_duals.m ├── README.md ├── UnitCommitment ├── Instructions for code.txt ├── Integer │ ├── CaseStudy_2gen.m │ └── setNadir.m └── Relaxed │ ├── CaseStudy_2gen.m │ ├── pricing │ ├── CaseStudy_2gen.m │ ├── NOTE about NaN duals.txt │ ├── setNadir.m │ └── study_duals.m │ └── setNadir.m └── simple_EconomicDispatch_YALMIP.m /EconomicDispatch/Instructions for code.txt: -------------------------------------------------------------------------------- 1 | 1. First solve the file in folder "Integer" 2 | 3 | 2. Then copy file "CaseStudy_2gen.m" to folder "Relaxed", and make sure that the right interval for nadir is fixed in "setNadir.m" (you can know the interval when nadir was chosen to happen from the solution of the Integer optimisation) 4 | 5 | 3. Make sure that you know which services were delivered after and before nadir when calculating the prices in file "study_duals.m" -------------------------------------------------------------------------------- /EconomicDispatch/Integer/CaseStudy_2gen.m: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/badber/MultiFR_optimisation/e5034bb2a7a92eede407e380403c3423eab4c7a5/EconomicDispatch/Integer/CaseStudy_2gen.m -------------------------------------------------------------------------------- /EconomicDispatch/Integer/FR_limits.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/badber/MultiFR_optimisation/e5034bb2a7a92eede407e380403c3423eab4c7a5/EconomicDispatch/Integer/FR_limits.jpg -------------------------------------------------------------------------------- /EconomicDispatch/Integer/setNadir.m: -------------------------------------------------------------------------------- 1 | function [DV_Total_FR_atTd,Inertia_term,PLoss_term,FR_term,Bounds,... 2 | Nadir_constraints] = ... 3 | setNadir(InputData,FR,H_total,DV_PLoss,Bounds) 4 | 5 | % Author: Luis Badesa 6 | 7 | Td = InputData.Td; 8 | Pmax = InputData.Pmax; 9 | PLossMax = InputData.PLossMax; 10 | nadir_req = InputData.nadir_req; 11 | FR_capacity = InputData.FR_capacity; 12 | H_const = InputData.H_const; 13 | H_const_Wind = InputData.H_const_Wind; 14 | H_const_Nuclear = InputData.H_const_Nuclear; 15 | Wind = InputData.Wind; 16 | 17 | %% First define the conditions for nadir happenning in different intervals: 18 | for i=1:length(Td) 19 | for j=1:length(Td) 20 | if Td(j)>Td(i) 21 | if j==1 22 | DV_Total_FR_atTd(i) = sdpvar(1); 23 | DV_Total_FR_atTd(i) = FR(j)*Td(i)/Td(j); 24 | else 25 | DV_Total_FR_atTd(i) = DV_Total_FR_atTd(i)... 26 | + FR(j)*Td(i)/Td(j); 27 | end 28 | else 29 | if j==1 30 | DV_Total_FR_atTd(i) = sdpvar(1); 31 | DV_Total_FR_atTd(i) = FR(j); 32 | else 33 | DV_Total_FR_atTd(i) = DV_Total_FR_atTd(i)... 34 | + FR(j); 35 | end 36 | end 37 | end 38 | end 39 | clear i j 40 | Bounds = [Bounds,... 41 | 0 <= DV_Total_FR_atTd <= sum(FR_capacity)*ones(1,length(Td))]; 42 | % Not the tightest bounds possible, but these bounds are good enough 43 | 44 | 45 | 46 | %% Define the left-hand and right-hand sides of each nadir constraint: 47 | 48 | Inertia_term = sdpvar(1,length(Td)); 49 | PLoss_term = sdpvar(1,length(Td)); 50 | FR_term = sdpvar(1,length(Td)); 51 | 52 | for i=1:length(Td) 53 | Inertia_term(i) = H_total; 54 | PLoss_term(i) = DV_PLoss; 55 | 56 | FR_term_defined = false; 57 | 58 | for j=1:length(Td) 59 | if Td(j)=DV_PLoss-tol,... 104 | [norm([Inertia_term(i)-FR_term(i);... 105 | 2*sqrt(1/(4*nadir_req))*PLoss_term(i)]);... 106 | -Inertia_term(i);... 107 | -FR_term(i)]... 108 | <= [Inertia_term(i)+FR_term(i);... 109 | 0;... 110 | 0]) ]; 111 | % This is to enforce nonnegativity of Inertia_term and 112 | % FR_term, but only for the SOC constraint that is actually 113 | % enforced by the conditional statements. Remember that 114 | % nonnegativity is a necessary condition for the rotated SOC to 115 | % be convex. 116 | else 117 | Nadir_constraints = [Nadir_constraints,... 118 | implies([DV_Total_FR_atTd(i-1)<=DV_PLoss+tol,... 119 | DV_Total_FR_atTd(i)>=DV_PLoss-tol],... 120 | [norm([Inertia_term(i)-FR_term(i);... 121 | 2*sqrt(1/(4*nadir_req))*PLoss_term(i)]);... 122 | -Inertia_term(i);... 123 | -FR_term(i)]... 124 | <= [Inertia_term(i)+FR_term(i);... 125 | 0;... 126 | 0]) ]; 127 | end 128 | end 129 | 130 | 131 | end 132 | -------------------------------------------------------------------------------- /EconomicDispatch/Relaxed/CaseStudy_2gen.m: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/badber/MultiFR_optimisation/e5034bb2a7a92eede407e380403c3423eab4c7a5/EconomicDispatch/Relaxed/CaseStudy_2gen.m -------------------------------------------------------------------------------- /EconomicDispatch/Relaxed/NOTE about NaN duals.txt: -------------------------------------------------------------------------------- 1 | If after solving the optimisation the duals take value "NaN", change the value of "tol" in "setNadir.m" until the duals work -------------------------------------------------------------------------------- /EconomicDispatch/Relaxed/setNadir.m: -------------------------------------------------------------------------------- 1 | function [DV_Total_FR_atTd,Inertia_term,PLoss_term,FR_term,Bounds,... 2 | Nadir_constraints] = ... 3 | setNadir(InputData,FR,H_total,DV_PLoss,Bounds) 4 | 5 | % Author: Luis Badesa 6 | 7 | Td = InputData.Td; 8 | Pmax = InputData.Pmax; 9 | PLossMax = InputData.PLossMax; 10 | nadir_req = InputData.nadir_req; 11 | FR_capacity = InputData.FR_capacity; 12 | H_const = InputData.H_const; 13 | H_const_Wind = InputData.H_const_Wind; 14 | H_const_Nuclear = InputData.H_const_Nuclear; 15 | Wind = InputData.Wind; 16 | 17 | %% First define the conditions for nadir happenning in different intervals: 18 | for i=1:length(Td) 19 | for j=1:length(Td) 20 | if Td(j)>Td(i) 21 | if j==1 22 | DV_Total_FR_atTd(i) = sdpvar(1); 23 | DV_Total_FR_atTd(i) = FR(j)*Td(i)/Td(j); 24 | else 25 | DV_Total_FR_atTd(i) = DV_Total_FR_atTd(i)... 26 | + FR(j)*Td(i)/Td(j); 27 | end 28 | else 29 | if j==1 30 | DV_Total_FR_atTd(i) = sdpvar(1); 31 | DV_Total_FR_atTd(i) = FR(j); 32 | else 33 | DV_Total_FR_atTd(i) = DV_Total_FR_atTd(i)... 34 | + FR(j); 35 | end 36 | end 37 | end 38 | end 39 | clear i j 40 | Bounds = [Bounds,... 41 | 0 <= DV_Total_FR_atTd <= sum(FR_capacity)*ones(1,length(Td))]; 42 | % Not the tightest bounds possible, but these bounds are good enough 43 | 44 | 45 | 46 | %% Define the left-hand and right-hand sides of each nadir constraint: 47 | 48 | Inertia_term = sdpvar(1,length(Td)); 49 | PLoss_term = sdpvar(1,length(Td)); 50 | FR_term = sdpvar(1,length(Td)); 51 | 52 | for i=1:length(Td) 53 | Inertia_term(i) = H_total; 54 | PLoss_term(i) = DV_PLoss; 55 | 56 | FR_term_defined = false; 57 | 58 | for j=1:length(Td) 59 | if Td(j)=DV_PLoss-tol,... 109 | % [norm([Inertia_term(i)-FR_term(i);... 110 | % 2*sqrt(1/(4*nadir_req))*PLoss_term(i)]);... 111 | % -Inertia_term(i);... 112 | % -FR_term(i)]... 113 | % <= [Inertia_term(i)+FR_term(i);... 114 | % 0;... 115 | % 0]) ]; 116 | % % This is to enforce nonnegativity of Inertia_term and 117 | % % FR_term, but only for the SOC constraint that is actually 118 | % % enforced by the conditional statements. Remember that 119 | % % nonnegativity is a necessary condition for the rotated SOC to 120 | % % be convex. 121 | % else 122 | % Nadir_constraints = [Nadir_constraints,... 123 | % implies([DV_Total_FR_atTd(i-1)<=DV_PLoss+tol,... 124 | % DV_Total_FR_atTd(i)>=DV_PLoss-tol],... 125 | % [norm([Inertia_term(i)-FR_term(i);... 126 | % 2*sqrt(1/(4*nadir_req))*PLoss_term(i)]);... 127 | % -Inertia_term(i);... 128 | % -FR_term(i)]... 129 | % <= [Inertia_term(i)+FR_term(i);... 130 | % 0;... 131 | % 0]) ]; 132 | 133 | 134 | % If nadir happens in time-interval i=1, enforce this 135 | % constraint: 136 | Nadir_constraints = [Nadir_constraints,... 137 | (DV_Total_FR_atTd(i)>=DV_PLoss-tol):'limits',... 138 | (cone([Inertia_term(i)-FR_term(i);... 139 | 2*sqrt(1/(4*nadir_req))*PLoss_term(i)],... 140 | Inertia_term(i)+FR_term(i))):'nadir',... 141 | Inertia_term(i)>=0,... 142 | FR_term(i)>=0]; 143 | % % Otherwise, enforce this constraint: 144 | % Nadir_constraints = [Nadir_constraints,... 145 | % (DV_Total_FR_atTd(i-1)<=DV_PLoss+tol):'limits',... 146 | % (DV_Total_FR_atTd(i)>=DV_PLoss-tol):'limits',... 147 | % (cone([Inertia_term(i)-FR_term(i);... 148 | % 2*sqrt(1/(4*nadir_req))*PLoss_term(i)],... 149 | % Inertia_term(i)+FR_term(i))):'nadir',... 150 | % Inertia_term(i)>=0,... 151 | % FR_term(i)>=0]; 152 | 153 | 154 | % It is better to use "cone" that using "norm", see why here: https://yalmip.github.io/command/cone/ 155 | % Also, as I want to obtain the vector duals of the SOCP, I 156 | % need to use "cone", as explained here: https://groups.google.com/forum/#!topic/yalmip/HEjNiiUm-5Q 157 | 158 | 159 | %end 160 | end 161 | 162 | 163 | end 164 | -------------------------------------------------------------------------------- /EconomicDispatch/Relaxed/study_duals.m: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/badber/MultiFR_optimisation/e5034bb2a7a92eede407e380403c3423eab4c7a5/EconomicDispatch/Relaxed/study_duals.m -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # MultiFR_optimisation 2 | Joint market clearing of energy and frequency services, including a pricing methodology. 3 | 4 | Frequency services include: 5 | - **Inertia**. 6 | - Different types of **Frequency Response (FR)**, allowing to consider any combination of FR dynamics. 7 | - An **optimised largest-loss** (the code considers the N-1 reliability requirement for scheduling frequency services). 8 | 9 | The code is self-explanatory. For further explanation, refer to [this paper]( 10 | http://arxiv.org/abs/1909.06671). 11 | 12 | The optimisation problem is solved via the toolbox **YALMIP**, you can find instructions for how to install it [here](https://yalmip.github.io/tutorial/installation/). You will also need to install some external MISOCP solver like Mosek or Gurobi, both of which have academic licenses available. Remember to also install the Matlab functionalities of that solver. 13 | 14 | YALMIP is very easy to use. Once installed, you can learn how to use it with file "simple_EconomicDispatch_YALMIP.m", contained in this repository, and through the documentation available in YALMIP's website. 15 | 16 | This code has been tested with MATLAB version 2017b. 17 | 18 | ---- 19 | 20 | If you use this code for your own work, please cite this paper: 21 |
    22 |
  1. L. Badesa, F. Teng, and G. Strbac, "Pricing inertia and frequency response with diverse dynamics in a Mixed-Integer Second-Order Cone Programming formulation," Applied Energy, vol. 260, article 114334, 2020. 23 |
24 | -------------------------------------------------------------------------------- /UnitCommitment/Instructions for code.txt: -------------------------------------------------------------------------------- 1 | 1. First solve the file in folder "Integer" 2 | 3 | 2. Then copy file "CaseStudy_2gen.m" to folder "Relaxed", and relax the integer commitment Decision Variable to continuous, by changing "y = binvar(num_Clusters,1);" to "y = sdpvar(num_Clusters,1);" 4 | 5 | 3. Then copy that relaxed "CaseStudy_2gen.m" file into the "pricing" folder, where the binary variables related to the conditional nadir constraints are removed because the time-interval for nadir to happen is fixed. Make sure that that the right interval for nadir is fixed in "setNadir.m" (you can know the interval when nadir was chosen to happen from the solution of the Relaxed optimisation) 6 | 7 | 4. Make sure that you know which services were delivered after and before nadir when calculating the prices in file "study_duals.m" -------------------------------------------------------------------------------- /UnitCommitment/Integer/CaseStudy_2gen.m: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/badber/MultiFR_optimisation/e5034bb2a7a92eede407e380403c3423eab4c7a5/UnitCommitment/Integer/CaseStudy_2gen.m -------------------------------------------------------------------------------- /UnitCommitment/Integer/setNadir.m: -------------------------------------------------------------------------------- 1 | function [DV_Total_FR_atTd,Inertia_term,PLoss_term,FR_term,Bounds,... 2 | Nadir_constraints] = ... 3 | setNadir(InputData,FR,H_total,DV_PLoss,Bounds) 4 | 5 | % Author: Luis Badesa 6 | 7 | Td = InputData.Td; 8 | Pmax = InputData.Gen_limits(:,2); 9 | PLossMax = InputData.PLossMax; 10 | nadir_req = InputData.nadir_req; 11 | FR_capacity = InputData.FR_capacity; 12 | H_const = InputData.H_const; 13 | H_const_Wind = InputData.H_const_Wind; 14 | H_const_Nuclear = InputData.H_const_Nuclear; 15 | P_Wind = InputData.P_Wind; 16 | num_gen = InputData.num_gen; 17 | 18 | %% First define the conditions for nadir happenning in different intervals: 19 | for i=1:length(Td) 20 | for j=1:length(Td) 21 | if Td(j)>Td(i) 22 | if j==1 23 | DV_Total_FR_atTd(i) = sdpvar(1); 24 | DV_Total_FR_atTd(i) = FR(j)*Td(i)/Td(j); 25 | else 26 | DV_Total_FR_atTd(i) = DV_Total_FR_atTd(i)... 27 | + FR(j)*Td(i)/Td(j); 28 | end 29 | else 30 | if j==1 31 | DV_Total_FR_atTd(i) = sdpvar(1); 32 | DV_Total_FR_atTd(i) = FR(j); 33 | else 34 | DV_Total_FR_atTd(i) = DV_Total_FR_atTd(i)... 35 | + FR(j); 36 | end 37 | end 38 | end 39 | end 40 | clear i j 41 | Bounds = [Bounds,... 42 | 0 <= DV_Total_FR_atTd <= (num_gen'*FR_capacity)*ones(1,length(Td))]; 43 | % Not the tightest bounds possible, but these bounds are good enough 44 | 45 | 46 | 47 | %% Define the left-hand and right-hand sides of each nadir constraint: 48 | 49 | Inertia_term = sdpvar(1,length(Td)); 50 | PLoss_term = sdpvar(1,length(Td)); 51 | FR_term = sdpvar(1,length(Td)); 52 | 53 | for i=1:length(Td) 54 | Inertia_term(i) = H_total; 55 | PLoss_term(i) = DV_PLoss; 56 | 57 | FR_term_defined = false; 58 | 59 | for j=1:length(Td) 60 | if Td(j)=DV_PLoss-tol,... 109 | % norm([Inertia_term(i)-FR_term(i);... 110 | % 2*sqrt(1/(4*nadir_req))*PLoss_term(i)])... 111 | % <= Inertia_term(i)+FR_term(i))]; 112 | Nadir_constraints = [Nadir_constraints,... 113 | implies(DV_Total_FR_atTd(i)>=DV_PLoss-tol,... 114 | [norm([Inertia_term(i)-FR_term(i);... 115 | 2*sqrt(1/(4*nadir_req))*PLoss_term(i)]);... 116 | -Inertia_term(i);... 117 | -FR_term(i)]... 118 | <= [Inertia_term(i)+FR_term(i);... 119 | 0;... 120 | 0]) ]; 121 | % This is to enforce nonnegativity of Inertia_term and 122 | % FR_term, but only for the SOC constraint that is actually 123 | % enforced by the conditional statements. Remember that 124 | % nonnegativity is a necessary condition for the rotated SOC to 125 | % be convex. 126 | else 127 | % Nadir_constraints = [Nadir_constraints,... 128 | % implies([DV_Total_FR_atTd(i-1)<=DV_PLoss+tol,... 129 | % DV_Total_FR_atTd(i)>=DV_PLoss-tol],... 130 | % norm([Inertia_term(i)-FR_term(i);... 131 | % 2*sqrt(1/(4*nadir_req))*PLoss_term(i)])... 132 | % <= Inertia_term(i)+FR_term(i))]; 133 | Nadir_constraints = [Nadir_constraints,... 134 | implies([DV_Total_FR_atTd(i-1)<=DV_PLoss+tol,... 135 | DV_Total_FR_atTd(i)>=DV_PLoss-tol],... 136 | [norm([Inertia_term(i)-FR_term(i);... 137 | 2*sqrt(1/(4*nadir_req))*PLoss_term(i)]);... 138 | -Inertia_term(i);... 139 | -FR_term(i)]... 140 | <= [Inertia_term(i)+FR_term(i);... 141 | 0;... 142 | 0]) ]; 143 | end 144 | end 145 | 146 | 147 | end 148 | -------------------------------------------------------------------------------- /UnitCommitment/Relaxed/CaseStudy_2gen.m: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/badber/MultiFR_optimisation/e5034bb2a7a92eede407e380403c3423eab4c7a5/UnitCommitment/Relaxed/CaseStudy_2gen.m -------------------------------------------------------------------------------- /UnitCommitment/Relaxed/pricing/CaseStudy_2gen.m: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/badber/MultiFR_optimisation/e5034bb2a7a92eede407e380403c3423eab4c7a5/UnitCommitment/Relaxed/pricing/CaseStudy_2gen.m -------------------------------------------------------------------------------- /UnitCommitment/Relaxed/pricing/NOTE about NaN duals.txt: -------------------------------------------------------------------------------- 1 | If after solving the optimisation the duals take value "NaN", change the value of "tol" in "setNadir.m" until the duals work -------------------------------------------------------------------------------- /UnitCommitment/Relaxed/pricing/setNadir.m: -------------------------------------------------------------------------------- 1 | function [DV_Total_FR_atTd,Inertia_term,PLoss_term,FR_term,Bounds,... 2 | Nadir_constraints] = ... 3 | setNadir(InputData,FR,H_total,DV_PLoss,Bounds) 4 | 5 | % Author: Luis Badesa 6 | 7 | Td = InputData.Td; 8 | Pmax = InputData.Gen_limits(:,2); 9 | PLossMax = InputData.PLossMax; 10 | nadir_req = InputData.nadir_req; 11 | FR_capacity = InputData.FR_capacity; 12 | H_const = InputData.H_const; 13 | H_const_Wind = InputData.H_const_Wind; 14 | H_const_Nuclear = InputData.H_const_Nuclear; 15 | P_Wind = InputData.P_Wind; 16 | num_gen = InputData.num_gen; 17 | 18 | %% First define the conditions for nadir happenning in different intervals: 19 | for i=1:length(Td) 20 | for j=1:length(Td) 21 | if Td(j)>Td(i) 22 | if j==1 23 | DV_Total_FR_atTd(i) = sdpvar(1); 24 | DV_Total_FR_atTd(i) = FR(j)*Td(i)/Td(j); 25 | else 26 | DV_Total_FR_atTd(i) = DV_Total_FR_atTd(i)... 27 | + FR(j)*Td(i)/Td(j); 28 | end 29 | else 30 | if j==1 31 | DV_Total_FR_atTd(i) = sdpvar(1); 32 | DV_Total_FR_atTd(i) = FR(j); 33 | else 34 | DV_Total_FR_atTd(i) = DV_Total_FR_atTd(i)... 35 | + FR(j); 36 | end 37 | end 38 | end 39 | end 40 | clear i j 41 | Bounds = [Bounds,... 42 | 0 <= DV_Total_FR_atTd <= (num_gen'*FR_capacity)*ones(1,length(Td))]; 43 | % Not the tightest bounds possible, but these bounds are good enough 44 | 45 | 46 | 47 | %% Define the left-hand and right-hand sides of each nadir constraint: 48 | 49 | Inertia_term = sdpvar(1,length(Td)); 50 | PLoss_term = sdpvar(1,length(Td)); 51 | FR_term = sdpvar(1,length(Td)); 52 | 53 | for i=1:length(Td) 54 | Inertia_term(i) = H_total; 55 | PLoss_term(i) = DV_PLoss; 56 | 57 | FR_term_defined = false; 58 | 59 | for j=1:length(Td) 60 | if Td(j)=DV_PLoss-tol,... 110 | % [norm([Inertia_term(i)-FR_term(i);... 111 | % 2*sqrt(1/(4*nadir_req))*PLoss_term(i)]);... 112 | % -Inertia_term(i);... 113 | % -FR_term(i)]... 114 | % <= [Inertia_term(i)+FR_term(i);... 115 | % 0;... 116 | % 0]) ]; 117 | % % This is to enforce nonnegativity of Inertia_term and 118 | % % FR_term, but only for the SOC constraint that is actually 119 | % % enforced by the conditional statements. Remember that 120 | % % nonnegativity is a necessary condition for the rotated SOC to 121 | % % be convex. 122 | % else 123 | % Nadir_constraints = [Nadir_constraints,... 124 | % implies([DV_Total_FR_atTd(i-1)<=DV_PLoss+tol,... 125 | % DV_Total_FR_atTd(i)>=DV_PLoss-tol],... 126 | % [norm([Inertia_term(i)-FR_term(i);... 127 | % 2*sqrt(1/(4*nadir_req))*PLoss_term(i)]);... 128 | % -Inertia_term(i);... 129 | % -FR_term(i)]... 130 | % <= [Inertia_term(i)+FR_term(i);... 131 | % 0;... 132 | % 0]) ]; 133 | 134 | 135 | % If nadir happens in time-interval i=1, enforce this 136 | % constraint: 137 | Nadir_constraints = [Nadir_constraints,... 138 | (DV_Total_FR_atTd(i)>=DV_PLoss-tol):'limits',... 139 | (cone([Inertia_term(i)-FR_term(i);... 140 | 2*sqrt(1/(4*nadir_req))*PLoss_term(i)],... 141 | Inertia_term(i)+FR_term(i))):'nadir',... 142 | Inertia_term(i)>=0,... 143 | FR_term(i)>=0]; 144 | % % Otherwise, enforce this constraint: 145 | % Nadir_constraints = [Nadir_constraints,... 146 | % (DV_Total_FR_atTd(i-1)<=DV_PLoss+tol):'limits',... 147 | % (DV_Total_FR_atTd(i)>=DV_PLoss-tol):'limits',... 148 | % (cone([Inertia_term(i)-FR_term(i);... 149 | % 2*sqrt(1/(4*nadir_req))*PLoss_term(i)],... 150 | % Inertia_term(i)+FR_term(i))):'nadir',... 151 | % Inertia_term(i)>=0,... 152 | % FR_term(i)>=0]; 153 | 154 | 155 | % It is better to use "cone" that using "norm", see why here: https://yalmip.github.io/command/cone/ 156 | % Also, as I want to obtain the vector duals of the SOCP, I 157 | % need to use "cone", as explained here: https://groups.google.com/forum/#!topic/yalmip/HEjNiiUm-5Q 158 | 159 | 160 | %end 161 | end 162 | 163 | 164 | end 165 | -------------------------------------------------------------------------------- /UnitCommitment/Relaxed/pricing/study_duals.m: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/badber/MultiFR_optimisation/e5034bb2a7a92eede407e380403c3423eab4c7a5/UnitCommitment/Relaxed/pricing/study_duals.m -------------------------------------------------------------------------------- /UnitCommitment/Relaxed/setNadir.m: -------------------------------------------------------------------------------- 1 | function [DV_Total_FR_atTd,Inertia_term,PLoss_term,FR_term,Bounds,... 2 | Nadir_constraints] = ... 3 | setNadir(InputData,FR,H_total,DV_PLoss,Bounds) 4 | 5 | % Author: Luis Badesa 6 | 7 | Td = InputData.Td; 8 | Pmax = InputData.Gen_limits(:,2); 9 | PLossMax = InputData.PLossMax; 10 | nadir_req = InputData.nadir_req; 11 | FR_capacity = InputData.FR_capacity; 12 | H_const = InputData.H_const; 13 | H_const_Wind = InputData.H_const_Wind; 14 | H_const_Nuclear = InputData.H_const_Nuclear; 15 | P_Wind = InputData.P_Wind; 16 | num_gen = InputData.num_gen; 17 | 18 | %% First define the conditions for nadir happenning in different intervals: 19 | for i=1:length(Td) 20 | for j=1:length(Td) 21 | if Td(j)>Td(i) 22 | if j==1 23 | DV_Total_FR_atTd(i) = sdpvar(1); 24 | DV_Total_FR_atTd(i) = FR(j)*Td(i)/Td(j); 25 | else 26 | DV_Total_FR_atTd(i) = DV_Total_FR_atTd(i)... 27 | + FR(j)*Td(i)/Td(j); 28 | end 29 | else 30 | if j==1 31 | DV_Total_FR_atTd(i) = sdpvar(1); 32 | DV_Total_FR_atTd(i) = FR(j); 33 | else 34 | DV_Total_FR_atTd(i) = DV_Total_FR_atTd(i)... 35 | + FR(j); 36 | end 37 | end 38 | end 39 | end 40 | clear i j 41 | Bounds = [Bounds,... 42 | 0 <= DV_Total_FR_atTd <= (num_gen'*FR_capacity)*ones(1,length(Td))]; 43 | % Not the tightest bounds possible, but these bounds are good enough 44 | 45 | 46 | 47 | %% Define the left-hand and right-hand sides of each nadir constraint: 48 | 49 | Inertia_term = sdpvar(1,length(Td)); 50 | PLoss_term = sdpvar(1,length(Td)); 51 | FR_term = sdpvar(1,length(Td)); 52 | 53 | for i=1:length(Td) 54 | Inertia_term(i) = H_total; 55 | PLoss_term(i) = DV_PLoss; 56 | 57 | FR_term_defined = false; 58 | 59 | for j=1:length(Td) 60 | if Td(j)=DV_PLoss-tol,... 109 | % norm([Inertia_term(i)-FR_term(i);... 110 | % 2*sqrt(1/(4*nadir_req))*PLoss_term(i)])... 111 | % <= Inertia_term(i)+FR_term(i))]; 112 | Nadir_constraints = [Nadir_constraints,... 113 | implies(DV_Total_FR_atTd(i)>=DV_PLoss-tol,... 114 | [norm([Inertia_term(i)-FR_term(i);... 115 | 2*sqrt(1/(4*nadir_req))*PLoss_term(i)]);... 116 | -Inertia_term(i);... 117 | -FR_term(i)]... 118 | <= [Inertia_term(i)+FR_term(i);... 119 | 0;... 120 | 0]) ]; 121 | % This is to enforce nonnegativity of Inertia_term and 122 | % FR_term, but only for the SOC constraint that is actually 123 | % enforced by the conditional statements. Remember that 124 | % nonnegativity is a necessary condition for the rotated SOC to 125 | % be convex. 126 | else 127 | % Nadir_constraints = [Nadir_constraints,... 128 | % implies([DV_Total_FR_atTd(i-1)<=DV_PLoss+tol,... 129 | % DV_Total_FR_atTd(i)>=DV_PLoss-tol],... 130 | % norm([Inertia_term(i)-FR_term(i);... 131 | % 2*sqrt(1/(4*nadir_req))*PLoss_term(i)])... 132 | % <= Inertia_term(i)+FR_term(i))]; 133 | Nadir_constraints = [Nadir_constraints,... 134 | implies([DV_Total_FR_atTd(i-1)<=DV_PLoss+tol,... 135 | DV_Total_FR_atTd(i)>=DV_PLoss-tol],... 136 | [norm([Inertia_term(i)-FR_term(i);... 137 | 2*sqrt(1/(4*nadir_req))*PLoss_term(i)]);... 138 | -Inertia_term(i);... 139 | -FR_term(i)]... 140 | <= [Inertia_term(i)+FR_term(i);... 141 | 0;... 142 | 0]) ]; 143 | end 144 | end 145 | 146 | 147 | end 148 | -------------------------------------------------------------------------------- /simple_EconomicDispatch_YALMIP.m: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/badber/MultiFR_optimisation/e5034bb2a7a92eede407e380403c3423eab4c7a5/simple_EconomicDispatch_YALMIP.m --------------------------------------------------------------------------------