Esta página aún no se ha traducido para esta versión. Puede ver la versión más reciente de esta página en inglés.

Envío óptimo de generadores de energía: basado en problemas

Este ejemplo muestra cómo programar dos generadores eléctricos de gas de forma óptima, lo que significa obtener la mayoría de los ingresos menos el costo. Si bien el ejemplo no es del todo realista, muestra cómo tomar en cuenta los costos que dependen del tiempo de decisión.

Para obtener el enfoque basado en Solver de este problema, consulte Envío óptimo de generadores de energía: basado en Solver.

Definición de problemas

El mercado de la electricidad tiene precios diferentes a diferentes horas del día. Si usted tiene generadores de suministro de electricidad, usted puede tomar ventaja de este precio variable mediante la programación de sus generadores para operar cuando los precios son altos. Suponga que controla dos generadores. Cada generador tiene tres niveles de potencia (apagado, bajo y alto). Cada generador tiene una tasa especificada de consumo de combustible y producción de energía a cada nivel de potencia. El consumo de combustible es 0 cuando el generador está apagado.

Puede asignar un nivel de potencia a cada generador para cada intervalo de media hora durante un día (24 horas, por lo que 48 intervalos). Basándose en registros históricos, suponga que conoce los ingresos por megavatios-hora (MWh) que recibe en cada intervalo de tiempo. Los datos para este ejemplo son del operador australiano del mercado de energía http://www.nemweb.com.au/REPORTS/CURRENT/ a mediados de 2013, y se utiliza bajo sus términos http://www.aemo.com.au/Privacy_and_Legal_Notices/Copyright_Permissions_Notice.

load dispatchPrice; % Get poolPrice, which is the revenue per MWh bar(poolPrice,.5) xlim([.5,48.5]) xlabel('Price per MWh at each period')

Hay un costo para iniciar un generador después de que se ha apagado. También, hay una restricción en el uso máximo del combustible para el día. Esta restricción existe porque usted compra su combustible un día antes de tiempo, así que usted puede utilizar solamente lo que usted acaba de comprar.

Notación de problemas y parámetros

Puede formular el problema de planificación como un problema de programación de enteros binarios. Defina los índices i, j, y k, y un vector de programación binario y, de la siguiente manera:

  • nPeriods = el número de periodos de tiempo, 48 en este caso.

  • i = un período de tiempo, 1 <= i <= 48.

  • j = un índice del generador, 1 <= j <= 2 para este ejemplo.

  • y(i,j,k) = 1 cuando el período i, el generador j está funcionando en el nivel de energía k. Deje la energía baja ser k = 1, y el poder más elevado sea k = 2. El generador está apagado cuando sum_k y(i,j,k) = 0.

Determine Cuándo un generador comienza después de estar apagado. Para ello, defina la variable binaria auxiliar z(i,j) que indica si se debe cargar para activar el generador j en el período i.

  • z(i,j) = 1 cuando el generador j está apagado en el período i, pero está encendido en el período i + 1. z(i,j) = 0 de otra manera. En otras palabras, z(i,j) = 1 cuando sum_k y(i,j,k) = 0 y sum_k y(i+1,j,k) = 1.

Necesita una forma de establecer z automáticamente basándose en la configuración de y. Una restricción lineal a continuación controla esta configuración.

También necesita los parámetros del problema para los costos, los niveles de generación de cada generador, los niveles de consumo de los generadores, y el combustible disponible.

  • poolPrice(i) --ingresos en dólares por MWh en intervalo i

  • gen(j,k) --MW generados por el generador j en el nivel de energía k

  • fuel(j,k) --combustible usado por el generador j en el nivel de energía k

  • totalFuel --combustible disponible en un día

  • startCost --costo en dólares para iniciar un generador después de que ha estado fuera

  • fuelPrice --costo para una unidad de combustible

Tienes poolPrice cuando ejecutaste load dispatchPrice;. Defina los otros parámetros de la siguiente manera.

fuelPrice = 3; totalFuel = 3.95e4; nPeriods = length(poolPrice); % 48 periods nGens = 2; % Two generators gen = [61,152;50,150]; % Generator 1 low = 61 MW, high = 152 MW fuel = [427,806;325,765]; % Fuel consumption for generator 2 is low = 325, high = 765 startCost = 1e4; % Cost to start a generator after it has been off

Eficacia del generador

Examine la eficiencia de los dos generadores en sus dos puntos de operación.

efficiency = gen./fuel; % Calculate electricity per unit fuel use rr = efficiency'; % for plotting h = bar(rr); h(1).FaceColor = 'g'; h(2).FaceColor = 'c'; legend(h,'Generator 1','Generator 2','Location','NorthEastOutside') ax = gca; ax.XTick = [1,2]; ax.XTickLabel = {'Low','High'}; ylim([.1,.2]) ylabel('Efficiency')

Observe que el generador 2 es un poco más eficiente que el generador 1 en sus correspondientes puntos de funcionamiento (bajo y alto), pero el generador 1 en su punto de funcionamiento alto es más eficiente que el generador 2 en su punto de funcionamiento bajo.

Variables para la solución

Para configurar el problema, es necesario codificar todos los datos del problema y las restricciones en el formulario de problema. Las variables y(i,j,k) representan la solución del problema, y las variables auxiliares z(i,j) indican si se debe cargar para encender un generador. y es un array nPeriods-by-nGens-by-2, y z es un array nPeriods-by-nGens. Todas las variables son binarias.

y = optimvar('y',nPeriods,nGens,{'Low','High'},'Type','integer','LowerBound',0,...     'UpperBound',1); z = optimvar('z',nPeriods,nGens,'Type','integer','LowerBound',0,...     'UpperBound',1);

Restricciones lineales

Para asegurarse de que el nivel de potencia no tiene más de un componente igual a 1, defina una restricción de desigualdad lineal.

powercons = y(:,:,'Low') + y(:,:,'High') <= 1;

El costo de funcionamiento por período es el costo del combustible para ese período. Para el generador j que funciona en el nivel k, el coste es fuelPrice * fuel(j,k).

Cree una expresión fuelUsed que tenga en cuenta todo el combustible utilizado.

yFuel = zeros(nPeriods,nGens,2); yFuel(:,1,1) = fuel(1,1); % Fuel use of generator 1 in low setting yFuel(:,1,2) = fuel(1,2); % Fuel use of generator 1 in high setting yFuel(:,2,1) = fuel(2,1); % Fuel use of generator 2 in low setting yFuel(:,2,2) = fuel(2,2); % Fuel use of generator 2 in high setting  fuelUsed = sum(sum(sum(y.*yFuel)));

La restricción es que el combustible utilizado no es más que el combustible disponible.

fuelcons = fuelUsed <= totalFuel;

Establecer las variables del indicador de inicio del generador

¿Cómo puede obtener el solucionador para establecer automáticamente las variables z para que coincidan con los períodos de activo/apagado de las variables y? Recuerde que la condición para satisfacer es z(i,j) = 1 exactamente cuando sum_k y(i,j,k) = 0 y sum_k y(i+1,j,k) = 1.

Note que sum_k ( - y(i,j,k) + y(i+1,j,k) ) > 0 exactamente cuando usted quiere z(i,j) = 1.

Por lo tanto, incluya estas limitaciones de la desigualdad lineal en la formulación del problema.

sum_k ( - y(i,j,k) + y(i+1,j,k) ) - z(i,j) < = 0.

Además, incluya las variables z en el costo de la función objetiva. Con las variables z en la función objetivo, el solucionador intenta bajar sus valores, lo que significa que intenta establecerlos todos iguales a 0. Pero para esos intervalos cuando un generador se enciende, la desigualdad lineal obliga a z(i,j) a ser igual a 1.

Cree una variable auxiliar w que represente y(i+1,j,k) - y(i,j,k). Representan la desigualdad de arranque del generador en términos de w.

w = optimexpr(nPeriods,nGens); % Allocate w idx = 1:(nPeriods-1); w(idx,:) = y(idx+1,:,'Low') - y(idx,:,'Low') + y(idx+1,:,'High') - y(idx,:,'High'); w(nPeriods,:) = y(1,:,'Low') - y(nPeriods,:,'Low') + y(1,:,'High') - y(nPeriods,:,'High'); switchcons = w - z <= 0;

Definir objetivo

La función objetiva incluye los costos de combustible para el funcionamiento de los generadores, los ingresos de la ejecución de los generadores, y los costos para el arranque de los generadores.

generatorlevel  = zeros(size(yFuel)); generatorlevel(:,1,1) = gen(1,1); % Fill in the levels generatorlevel(:,1,2) = gen(1,2); generatorlevel(:,2,1) = gen(2,1); generatorlevel(:,2,2) = gen(2,2); 

Ingreso entrante = y.*generatorlevel.*poolPrice.

revenue = optimexpr(size(y)); for ii = 1:nPeriods     revenue(ii,:,:) = poolPrice(ii)*y(ii,:,:).*generatorlevel(ii,:,:); end

El costo total de combustible = fuelUsed*fuelPrice.

fuelCost = fuelUsed*fuelPrice;

El generador de arranque cost = z*startCost.

startingCost = z*startCost;

El beneficio = ingreso entrante - costo total de combustible - el costo de inicio.

profit = sum(sum(sum(revenue))) - fuelCost - sum(sum(startingCost));

Resolver el problema

Crear un problema de optimización e incluir el objetivo y las restricciones.

dispatch = optimproblem('ObjectiveSense','maximize'); dispatch.Objective = profit; dispatch.Constraints.switchcons = switchcons; dispatch.Constraints.fuelcons = fuelcons; dispatch.Constraints.powercons = powercons;

Para ahorrar espacio, suprime la visualización iterativa.

options = optimoptions('intlinprog','Display','final');

Resolver el problema.

[dispatchsol,fval,exitflag,output] = solve(dispatch,'options',options);
Optimal solution found.  Intlinprog stopped because the objective value is within a gap tolerance of the optimal value, options.AbsoluteGapTolerance = 0 (the default value). The intcon variables are integer within tolerance, options.IntegerTolerance = 1e-05 (the default value). 

Examinar la solución

Trazar la solución en función del tiempo.

subplot(3,1,1) bar(dispatchsol.y(:,1,1)*gen(1,1)+dispatchsol.y(:,1,2)*gen(1,2),.5,'g') xlim([.5,48.5]) ylabel('MWh') title('Generator 1 Optimal Schedule','FontWeight','bold') subplot(3,1,2) bar(dispatchsol.y(:,2,1)*gen(1,1)+dispatchsol.y(:,2,2)*gen(1,2),.5,'c') title('Generator 2 Optimal Schedule','FontWeight','bold') xlim([.5,48.5]) ylabel('MWh') subplot(3,1,3) bar(poolPrice,.5) xlim([.5,48.5]) title('Energy Price','FontWeight','bold') xlabel('Period') ylabel('$ / MWh')

El generador 2 funciona más de largo que el generador 1, que usted esperaría porque es más eficiente. El generador 2 funciona a su nivel de alta potencia siempre que esté encendido. El generador 1 funciona principalmente en su alto nivel de potencia, pero desciende a baja potencia para una unidad de tiempo. Cada generador funciona por un conjunto contiguo de períodos diarios y, por lo tanto, sólo incurre en un costo inicial cada día.

Compruebe que la variable z es 1 para los períodos en que se inician los generadores.

starttimes = find(round(dispatchsol.z) == 1); % Use round for noninteger results [theperiod,thegenerator] = ind2sub(size(dispatchsol.z),starttimes)
theperiod = 2×1

    23
    16

thegenerator = 2×1

     1
     2

Los períodos en que los generadores empiezan a coincidir con las gráficas.

Comparar con menor pena para el inicio

Si especifica un valor más bajo para startCost, la solución implica varios períodos de generación.

startCost = 500; % Choose a lower penalty for starting the generators startingCost = z*startCost; profit = sum(sum(sum(revenue))) - fuelCost - sum(sum(startingCost)); dispatch.Objective = profit; [dispatchsolnew,fvalnew,exitflagnew,outputnew] = solve(dispatch,'options',options);
Optimal solution found.  Intlinprog stopped because the objective value is within a gap tolerance of the optimal value, options.AbsoluteGapTolerance = 0 (the default value). The intcon variables are integer within tolerance, options.IntegerTolerance = 1e-05 (the default value). 
 subplot(3,1,1) bar(dispatchsolnew.y(:,1,1)*gen(1,1)+dispatchsolnew.y(:,1,2)*gen(1,2),.5,'g') xlim([.5,48.5]) ylabel('MWh') title('Generator 1 Optimal Schedule','FontWeight','bold') subplot(3,1,2) bar(dispatchsolnew.y(:,2,1)*gen(1,1)+dispatchsolnew.y(:,2,2)*gen(1,2),.5,'c') title('Generator 2 Optimal Schedule','FontWeight','bold') xlim([.5,48.5]) ylabel('MWh') subplot(3,1,3) bar(poolPrice,.5) xlim([.5,48.5]) title('Energy Price','FontWeight','bold') xlabel('Period') ylabel('$ / MWh')

starttimes = find(round(dispatchsolnew.z) == 1); % Use round for noninteger results [theperiod,thegenerator] = ind2sub(size(dispatchsolnew.z),starttimes)
theperiod = 3×1

    22
    16
    45

thegenerator = 3×1

     1
     2
     2