Solving the Unit Commitment Problem using optimproblem
37 vues (au cours des 30 derniers jours)
Afficher commentaires plus anciens
Micah Mungal
le 29 Mai 2018
Commenté : Walter Roberson
le 28 Oct 2025 à 2:28
I am trying to solve the Unit Commitment problem using the optimproblem function. I will explain my problem with an example. Lets say I have three generators which will be operated to meet various loads for a 24 hours period. Now the cost curves for these units are quadratic, so piecewise linearization is required if I am going to use the intlinprog function. I have successfully linearized the system and the problem now involves minimizing:
Total C(P) = C_1(P_1min)T_1 + s_11*P_11*T_1 + s_12*P_12*T_1 + … + s_1n*P_1n*T_1
+ C_2(P_2min)T_2 + s_21*P_21*T_2 + s_22*P_22*T_2 + … + s_2n*P_2n*T_2
+ C_3(P_3min)T_3 + s_31*P_31*T_3 + s_32*P_32*T_3 + … + s_3n*P_3n*T_3
Where Sik is the slope for each segment of the linearized curve, Pik are the power increments (which I need to determine) for each segment. T_i is a binary variable which keeps track of the state of unit and Ci(Pimin) is the cost at Pimin. Now based on my research, the problem should now be formulated as minimize:
Total C(P) = C_1(P_1min)T_1 + s_11*Z_11 + s_12*Z_12 + … + s_1n*Z_1n
+ C_2(P_2min)T_2 + s_21*Z_21 + s_22*Z_22 + … + s_2n*Z_2n
+ C_3(P_3min)T_3 + s_31*Z_31 + s_32*Z_32 + … + s_3n*Z_3n
Where Ti = 0 implies that Zik = 0 and Ti = 1 implies that Zik = Pik.
Now I have two optimization variable:
- Pik
- Ti
With the optimization problem being made up of two part:
- Sum of ALL Sik*Pik values
- Sum of Ci(Pimin)*Ti values.
The following code illustrates this (with the addition of another optimization variable which isn't so important):
%Amount of power generated in an hour by a plant
power = optimvar('power',nHours,plant,'LowerBound',0,'UpperBound',maxGenConst);
%Indicator if plant is operating during an hour
isOn = optimvar('isOn',nHours,actual_plant,'Type','integer','LowerBound',0,'UpperBound',1);
%Indicator if plant is starting up during an hour
startup = optimvar('startup',nHours,actual_plant,'Type','integer','LowerBound',0,'UpperBound',1);
%Costs
powerCost = sum(power*f,1);
isOnCost = sum(isOn*OperatingCost',1);
startupCost = sum(startup*StartupCost',1);
% set objective
powerprob.Objective = powerCost + isOnCost + startupCost;
% power generation over all plans meets hourly demand
powerprob.Constraints.isDemandMet = sum(power,2) >= Load;
% only gen power when plant is on
% if isOn=0 power must be zero, if isOn=1 power must be less than maxGenConst
powerprob.Constraints.powerOnlyWhenOn = power <= maxGenConst.*isOn;
% if on, meet MinGenerationLevel
% if isOn=0 power >= 0, if isOn=1 power must be more than minGenConst
powerprob.Constraints.meetMinGenLevel = power >= maxGenConst.*isOn;
My problem lies in this calculation: maxGenConst.*isOn. These have different sizes as maxGenConst is a nHours * (num_of_gen*num_of_segments) whereas isOn is a nHour * num_of_gen. The reason this occurs is because after linearization, the cost equation becomes (num_of_segments + 1) equations. I would appreciate some help in formulating the problem so I can use the optimproblem function. I have attached my full code and data.
4 commentaires
Sivateja Maturu
le 16 Juin 2021
Can you please explain how you have defined the values of a, b, c for the power plants defined in your Gen_Data?
Réponse acceptée
Micah Mungal
le 11 Juin 2018
6 commentaires
Walter Roberson
le 28 Oct 2025 à 2:28
The code needs Optimization Toolbox, R2017b or later.
Plus de réponses (2)
amrit singh
le 19 Mai 2019
Undefined function or variable 'optimproblem'.
Error in ELD_LP (line 168)
powerprob = optimproblem;
1 commentaire
praveen kumar
le 28 Oct 2025 à 1:34
Modifié(e) : Walter Roberson
le 28 Oct 2025 à 1:47
%==========================================================================
% ECONOMIC LOAD DISPATCH - DIAGNOSTIC AND WORKING VERSION
%==========================================================================
clc;
clear;
close all;
%% ==================== DATA INPUT ====================
Load = [140; 166; 180; 196; 220; 240; 267; 283.4; 308; 323; 340; 350; ...
300; 267; 220; 196; 220; 240; 267; 300; 267; 235; 196; 166];
nHours = numel(Load);
% Generator Data: [b, a, c, Pmax, Pmin, MinUpTime, MinDownTime, StartupCost]
gen_data = [2 200 0.00375 200 50 1 1 176;
1.75 257 0.0175 80 20 2 2 187;
1 300 0.0625 40 15 1 1 113;
3.25 400 0.00834 35 10 1 2 267;
3 515 0.025 30 10 2 1 180;
3 515 0.05 25 12 1 1 113];
num_of_gen = size(gen_data, 1);
b_coeff = gen_data(:, 1);
a_coeff = gen_data(:, 2);
c_coeff = gen_data(:, 3);
Pmax = gen_data(:, 4);
Pmin = gen_data(:, 5);
fprintf('========== FEASIBILITY CHECK ==========\n');
fprintf('Peak Load: %.2f MW\n', max(Load));
fprintf('Total Capacity: %.2f MW\n', sum(Pmax));
fprintf('Min Load: %.2f MW\n', min(Load));
fprintf('Total Pmin (all ON): %.2f MW\n\n', sum(Pmin));
% THE ISSUE: Min load (140) is greater than what some generators can provide alone
% but less than total Pmin if all units are ON (117 MW is fine)
% The problem is the PIECEWISE LINEARIZATION creating infeasibility
%% ========== SIMPLIFIED APPROACH WITHOUT PIECEWISE ==========
% Use quadratic costs directly - simpler and more reliable
fprintf('========== USING SIMPLIFIED FORMULATION ==========\n');
% Create optimization problem
prob = optimproblem('ObjectiveSense', 'minimize');
% Decision Variables
% P(t,g) = power output of generator g at hour t
P = optimvar('P', nHours, num_of_gen, 'LowerBound', 0);
% u(t,g) = 1 if generator g is ON at hour t
u = optimvar('u', nHours, num_of_gen, 'Type', 'integer', 'LowerBound', 0, 'UpperBound', 1);
% v(t,g) = 1 if generator g starts up at hour t
v = optimvar('v', nHours, num_of_gen, 'Type', 'integer', 'LowerBound', 0, 'UpperBound', 1);
fprintf('Variables: %d continuous, %d binary\n', nHours*num_of_gen, 2*nHours*num_of_gen);
%% ========== OBJECTIVE FUNCTION ==========
% For optimization, we'll use linear approximation of costs
% Cost ≈ marginal cost * power + fixed cost when on
% Calculate marginal cost at mid-point
marginal_cost = zeros(num_of_gen, 1);
fixed_cost = zeros(num_of_gen, 1);
startup_cost = gen_data(:, 8);
for g = 1:num_of_gen
P_mid = (Pmin(g) + Pmax(g)) / 2;
marginal_cost(g) = 2 * a_coeff(g) * P_mid + b_coeff(g);
fixed_cost(g) = a_coeff(g) * Pmin(g)^2 + b_coeff(g) * Pmin(g) + c_coeff(g);
end
% Objective: minimize total cost
fuel_cost = sum(P * marginal_cost, 'all');
operating_cost = sum(u * fixed_cost, 'all');
startup_cost_total = sum(v * startup_cost, 'all');
prob.Objective = fuel_cost + operating_cost + startup_cost_total;
fprintf('Objective function defined\n');
%% ========== CONSTRAINTS ==========
fprintf('Adding constraints...\n');
% 1. Meet demand
prob.Constraints.demand = sum(P, 2) >= Load;
fprintf(' Demand: %d constraints\n', nHours);
% 2. Minimum generation when ON
for g = 1:num_of_gen
prob.Constraints.(['min_' num2str(g)]) = P(:, g) >= Pmin(g) * u(:, g);
end
fprintf(' Min generation: %d constraints\n', num_of_gen * nHours);
% 3. Maximum generation when ON
for g = 1:num_of_gen
prob.Constraints.(['max_' num2str(g)]) = P(:, g) <= Pmax(g) * u(:, g);
end
fprintf(' Max generation: %d constraints\n', num_of_gen * nHours);
% 4. Startup definition (relaxed)
prob.Constraints.startup = v(2:end, :) >= u(2:end, :) - u(1:end-1, :);
fprintf(' Startup: %d constraints\n', (nHours-1) * num_of_gen);
fprintf('Total constraints: %d\n', nHours + 2*num_of_gen*nHours + (nHours-1)*num_of_gen);
%% ========== SOLVE ==========
fprintf('\n========== SOLVING ==========\n');
options = optimoptions('intlinprog', ...
'MaxTime', 600, ...
'Display', 'iter', ...
'RelativeGapTolerance', 0.1, ...
'MaxNodes', 1e6);
fprintf('Starting solver...\n\n');
tic;
[sol, fval, exitflag, output] = solve(prob, 'Options', options);
solve_time = toc;
%% ========== DISPLAY RESULTS ==========
fprintf('\n========== RESULTS ==========\n');
fprintf('Exit flag: %d\n', exitflag);
fprintf('Solve time: %.2f seconds\n', solve_time);
if exitflag >= 0
fprintf('STATUS: SUCCESS!\n');
fprintf('Total Cost: $%.2f\n', fval);
P_sol = sol.P;
u_sol = sol.u;
v_sol = sol.v;
% Round binary variables
u_sol = round(u_sol);
v_sol = round(v_sol);
fprintf('\n========== GENERATION SCHEDULE ==========\n');
fprintf('Hour\tLoad\tTotal\tUnits\t G1\t G2\t G3\t G4\t G5\t G6\n');
fprintf('----\t----\t-----\t-----\t----\t----\t----\t----\t----\t----\n');
for t = 1:nHours
fprintf('%2d\t%4.0f\t%5.1f\t %d\t', t, Load(t), sum(P_sol(t,:)), sum(u_sol(t,:)));
for g = 1:num_of_gen
if u_sol(t,g) > 0.5
fprintf('%5.1f\t', P_sol(t,g));
else
fprintf(' - \t');
end
end
fprintf('\n');
end
% Calculate actual costs
actual_gen_cost = 0;
actual_op_cost = 0;
for t = 1:nHours
for g = 1:num_of_gen
if u_sol(t,g) > 0.5
P_val = P_sol(t,g);
actual_gen_cost = actual_gen_cost + a_coeff(g)*P_val^2 + b_coeff(g)*P_val + c_coeff(g);
end
end
end
actual_startup_cost = sum(sum(v_sol)) * mean(startup_cost);
fprintf('\n========== COST BREAKDOWN ==========\n');
fprintf('Generation Cost: $%.2f\n', actual_gen_cost);
fprintf('Startup Cost: $%.2f (approx)\n', actual_startup_cost);
fprintf('Total: $%.2f (approx)\n', actual_gen_cost + actual_startup_cost);
fprintf('\n========== STARTUPS ==========\n');
for g = 1:num_of_gen
fprintf('Generator %d: %d startups\n', g, sum(v_sol(:,g)));
end
% Verify demand is met
fprintf('\n========== VERIFICATION ==========\n');
for t = 1:nHours
total_gen = sum(P_sol(t,:));
if total_gen < Load(t) - 0.01
fprintf('WARNING: Hour %d - Generation %.2f < Load %.2f\n', t, total_gen, Load(t));
end
end
fprintf('All demands satisfied!\n');
% Plot results
figure('Position', [100, 100, 1400, 700]);
% Subplot 1: Stacked generation
subplot(2,2,1);
area(1:nHours, P_sol);
hold on;
plot(1:nHours, Load, 'k-', 'LineWidth', 2);
xlabel('Hour');
ylabel('Power (MW)');
title('Generation Dispatch');
legend('G1','G2','G3','G4','G5','G6','Load','Location','eastoutside');
grid on;
% Subplot 2: Unit commitment
subplot(2,2,2);
imagesc(u_sol');
colormap([1 1 1; 0 0.5 0]);
xlabel('Hour');
ylabel('Generator');
title('Unit Commitment Status');
yticks(1:num_of_gen);
yticklabels({'G1','G2','G3','G4','G5','G6'});
colorbar('Ticks',[0.25 0.75],'TickLabels',{'OFF','ON'});
% Subplot 3: Load vs Generation
subplot(2,2,3);
plot(1:nHours, Load, 'b-o', 'LineWidth', 2, 'MarkerFaceColor', 'b');
hold on;
plot(1:nHours, sum(P_sol,2), 'r-s', 'LineWidth', 2, 'MarkerFaceColor', 'r');
xlabel('Hour');
ylabel('Power (MW)');
title('Load vs Total Generation');
legend('Load','Generation','Location','best');
grid on;
% Subplot 4: Generator utilization
subplot(2,2,4);
utilization = zeros(num_of_gen, 1);
for g = 1:num_of_gen
hours_on = sum(u_sol(:,g));
avg_output = sum(P_sol(:,g)) / hours_on;
utilization(g) = avg_output / Pmax(g) * 100;
end
bar(utilization);
xlabel('Generator');
ylabel('Average Utilization (%)');
title('Generator Utilization When ON');
xticklabels({'G1','G2','G3','G4','G5','G6'});
grid on;
sgtitle('Economic Load Dispatch Results', 'FontWeight', 'bold', 'FontSize', 14);
else
fprintf('STATUS: FAILED\n');
fprintf('Message: %s\n', output.message);
% Additional diagnostics
fprintf('\n========== DIAGNOSTICS ==========\n');
fprintf('The problem is infeasible. Possible reasons:\n');
fprintf('1. Check if any hour load > total capacity\n');
for t = 1:nHours
if Load(t) > sum(Pmax)
fprintf(' Hour %d: Load %.0f > Capacity %.0f\n', t, Load(t), sum(Pmax));
end
end
fprintf('2. All capacity checks passed - issue is with constraints\n');
end
fprintf('\nDone.\n');
0 commentaires
Voir également
Catégories
En savoir plus sur Linear Least Squares dans Help Center et File Exchange
Community Treasure Hunt
Find the treasures in MATLAB Central and discover how the community can help you!
Start Hunting!
