unit ugter_onoffporposte;

interface

uses
  SysUtils, Classes, xmatdefs, uGTer, unodos,
  uglobs,
  umipsimplex,
  usimplex,
  ufichasLPD,
  ufechas,
  ucosa, ucosaConNombre,
  uconstantesSimSEE, uFuentesAleatorias;

resourcestring
  rsGeneradorTermicoEncYApXPoste =
    'Generador térmico con encendido y apagado por poste';

type

  { TFichaGTer_OnOffPorPoste }

  TFichaGTer_OnOffPorPoste = class(TFichaLPD)

  public

    (**************************************************************************)
    (*               A T R I B U T O S   P E R S I S T E N T E S              *)
    (**************************************************************************)

    PMin, PMax: NReal; // [MW] Potencias Mínima y Máxima Por maquina
    cv_min, cv: NReal; // Costo:= cv_min* Pmin+ cv* (P-Pmin)
    cv_NoCombustible: NReal;
    rendimiento_pmin: NReal; // Rendimiento energético en el mínimo técnico
    rendimiento_pmax: NReal; // Rendimiento energético en el máximo
    indicePreciosPorCombustible: TFuenteAleatoria;
    bornePreciosPorCombustible: string;
    disp: NReal; // disponibilidad (fortuita)
    tRepHoras: NReal;
    HayRestriccionEmaxPasoDeTiempo: boolean; // indica si se aplica la restricción
    EmaxPasoDeTiempo: NReal; // Energía maxima generable en un paso de tiempo
    PagoPorDisponibilidad_USD_MWh: NReal; // [USD/MWh] Pago por Potencia
    PagoPorEnergia_USD_MWh: NReal; // [USD/MWh] Pago por Potencia

    (**************************************************************************)

    nroBornePreciosPorCombustible: integer;

    constructor Create(capa: integer; fecha: TFecha; periodicidad: TPeriodicidad;
      PMin, PMax: NReal; cv_min, cv, cv_NoCombustible: NReal;
      indicePreciosPorCombustible: TFuenteAleatoria;
      bornePreciosPorCombustible: string; disp: NReal; tRepHoras: NReal;
      HayRestriccionEmaxPasoDeTiempo: boolean; EmaxPasoDeTiempo: NReal;
      PagoPorDisponibilidad_USD_MWh: NReal; PagoPorEnergia_USD_MWh: NReal);

    function Rec: TCosa_RecLnk; override;
    procedure BeforeRead(version, id_hilo: integer); override;
    procedure AfterRead(f:TArchiTexto); override;

    procedure generarLineaResumen(var archi: TextFile); override;
    function infoAd_: string; override;
    destructor destroy; override;
  end;


  // TGenSencillo es una potencia minima y maxima constante en todos los postes
  // y un costo variable cv [USD/MWh] tambien igual en todos los postes
  // y un costo variable cv_min "hasta el minimo" igual al costo de produccion en USD/MWh cuando
  // la central esta generado en el minimo.

  { TGTer_OnOffPorPoste }

  TGTer_OnOffPorPoste = class(TGTer)
  public
    pa: TFichaGter_onoffporposte;

    NMaquinasDespachadas: TDAOfNInt;
    // cantidad de máquinas despachadas por poste o por paso ( Acople )
    maxNMaquinasDespachadas: integer; // máximo del vector anterior
    costosPorPoste: TDAofNReal;

    c0: NReal; //Costo en USD/h por máquina por estar operando en el mínimo técnico
    cv: NReal; //USD/MWh usado para la optimización del paso.
    cv_medio: NReal; // Usado para sanción del precio

    cve: NReal; // costo por la energía adicional a CV

    PMin: NReal; //MW, mínimo
    PxMax: NReal; // MW, cota sup de P-Pmin

    // resultado de los sorteos de disponibilidad y del mantenimiento programado
    NMaquinasDisponibles: integer;
    PMaxDisponible: NReal;

    //Cuanta potencia despacho en cada hora el generador en promedio
    potMedia_despachada: NReal;

    constructor Create(capa: integer; nombre: string; nacimiento, muerte: TFecha;
      lpdUnidades, lpd: TFichasLPD; nodo: TNodo;
      flg_CalcularGradienteDeInversion: boolean;
      TonCO2xMWh: NReal;
      LowCostMustRun, CleanDevelopmentMechanism: boolean;
      xFuenteIdxP: TFuenteAleatoria; xBorneIdxP: string );

     
    function Rec: TCosa_RecLnk; override;
    procedure BeforeRead(version, id_hilo: integer); override;
    procedure AfterRead(f:TArchiTexto); override;

    procedure PrepararMemoria( Catalogo: TCatalogoReferencias; globs: TGlobs); override;
    procedure RegistrarParametrosDinamicos( CatalogoReferencias: TCatalogoReferencias ); override;

    function PotenciaFirme: NReal; override;

    function InfoAd_: string; override;
    class function DescClase: string; override;

    function get_pa_FD( kTipoUnidad: integer ): NReal; override;
    function get_pa_TMR( kTipoUnidad: integer ): NReal; override;

    procedure SorteosDelPaso(sortear: boolean); override;
    procedure PrepararPaso_ps; override;
    procedure Sim_Paso_Fin; override;

    procedure opt_nvers(var ivar, ivae, ires: integer); override;

    procedure opt_cargue(s: TSimplex); override;
    procedure opt_fijarRestriccionesDeCaja(s: TSimplex); override;
    procedure opt_leerSolucion(s: TSimplex); override;

    function getNombreVar(ivar: integer; var nombre: string): boolean; override;
    function getNombreRes(ires: integer; var nombre: string): boolean; override;

    destructor destroy; override;

    procedure PubliVars; override;
    procedure dump_Variables(var f: TextFile; charIndentacion: char); override;

    class function TipoFichaLPD: TClaseDeFichaLPD; override;

  end;

procedure AlInicio;
procedure AlFinal;

implementation

//------------------------------
// Métodos de TFichaGter_onoffporposte
//==============================
constructor TFichaGTer_OnOffPorPoste.Create(capa: integer; fecha: TFecha;
  periodicidad: TPeriodicidad; PMin, PMax: NReal; cv_min, cv,
  cv_NoCombustible: NReal; indicePreciosPorCombustible: TFuenteAleatoria;
  bornePreciosPorCombustible: string; disp: NReal; tRepHoras: NReal;
  HayRestriccionEmaxPasoDeTiempo: boolean; EmaxPasoDeTiempo: NReal;
  PagoPorDisponibilidad_USD_MWh: NReal; PagoPorEnergia_USD_MWh: NReal);

begin
  inherited Create(capa, fecha, periodicidad);
  self.PMin := PMin;
  self.PMax := PMax;
  self.cv_min := cv_min;
  self.cv := cv;
  self.cv_NoCombustible:= cv_NoCombustible;
  self.indicePreciosPorCombustible := indicePreciosPorCombustible;
  self.bornePreciosPorCombustible := bornePreciosPorCombustible;
  self.disp := disp;
  self.tRepHoras := tRepHoras;
  self.HayRestriccionEmaxPasoDeTiempo := HayRestriccionEmaxPasoDeTiempo;
  self.EmaxPasoDeTiempo := EmaxPasoDeTiempo;
  self.PagoPorDisponibilidad_USD_MWh := PagoPorDisponibilidad_USD_MWh;
  self.PagoPorEnergia_USD_MWh := PagoPorEnergia_USD_MWh;
end;

function TFichaGTer_OnOffPorPoste.Rec: TCosa_RecLnk;
begin
  Result:=inherited Rec;

  Result.addCampoDef('PMin', PMin, 0, 6 );
  Result.addCampoDef('PMax', PMax, 0, 6 );
  Result.addCampoDef('cv_min', cv_min, 0, 6 );
  Result.addCampoDef('cv', cv, 0, 6 );
  Result.addCampoDef_ref('indicePreciosPorCombustible',  TCosa(indicePreciosPorCombustible), self, 0, 6 );
  Result.addCampoDef('disp', disp, 0, 6 );
  Result.addCampoDef('HayRestriccionEmaxPasoDeTiempo', HayRestriccionEmaxPasoDeTiempo, 0, 6 );
  Result.addCampoDef('EmaxPasoDeTiempo', EmaxPasoDeTiempo, 0, 6 );
  Result.addCampoDef('PMin', PMin, 6, 14 );
  Result.addCampoDef('PMax', PMax, 6, 14 );
  Result.addCampoDef('cv_min', cv_min, 6, 14 );
  Result.addCampoDef('cv', cv, 6, 14 );
  Result.addCampoDef_ref('indicePreciosPorCombustible', TCosa(indicePreciosPorCombustible), self, 6, 14 );
  Result.addCampoDef('bornePreciosPorCombustible', bornePreciosPorCombustible, 6, 14 );
  Result.addCampoDef('disp', disp, 6, 14 );
  Result.addCampoDef('HayRestriccionEmaxPasoDeTiempo', HayRestriccionEmaxPasoDeTiempo, 6, 14 );
  Result.addCampoDef('EmaxPasoDeTiempo', EmaxPasoDeTiempo, 6, 14 );
  Result.addCampoDef('PMin', PMin, 14 );
  Result.addCampoDef('PMax', PMax, 14 );
  Result.addCampoDef('cv_min', cv_min, 14 );
  Result.addCampoDef('cv', cv, 14 );
  Result.addCampoDef( 'cv_NoCombustible', cv_NoCombustible, 111 );
  Result.addCampoDef('rendimiento_pmin', rendimiento_pmin, 80 );
  Result.addCampoDef('rendimiento_pmax', rendimiento_pmax, 80 );
  Result.addCampoDef_ref('indicePreciosPorCombustible',  TCosa(indicePreciosPorCombustible), self, 14 );
  Result.addCampoDef('bornePreciosPorCombustible', bornePreciosPorCombustible, 14 );
  Result.addCampoDef('disp', disp, 14 );
  Result.addCampoDef('tRepHoras', tRepHoras, 14 );
  Result.addCampoDef('HayRestriccionEmaxPasoDeTiempo', HayRestriccionEmaxPasoDeTiempo, 14 );
  Result.addCampoDef('EmaxPasoDeTiempo', EmaxPasoDeTiempo, 14 );
  Result.addCampoDef('PagoPorPotencia', PagoPorDisponibilidad_USD_MWh, 71, 98);
  Result.addCampoDef('PagoPorEnergia', PagoPorEnergia_USD_MWh, 71, 98);
  Result.addCampoDef('PagoPorDisponibilidad_USD_MWh', PagoPorDisponibilidad_USD_MWh, 98);
  Result.addCampoDef('PagoPorEnergia_USD_MWh', PagoPorEnergia_USD_MWh, 98);
end;

procedure TFichaGTer_OnOffPorPoste.BeforeRead(version, id_hilo: integer);
begin
  inherited BeforeRead(version, id_hilo);
  cv_NoCombustible:=0.0;
end;

procedure TFichaGTer_OnOffPorPoste.AfterRead(f:TArchiTexto);
begin
  inherited AfterRead(f);
  if f.Version < 6 then
  begin
    rendimiento_pmin := 0.0;
    rendimiento_pmax := 0.0;
    indicePreciosPorCombustible := nil;
    bornePreciosPorCombustible := '';
    tRepHoras := 15 * 24;
  end
  else if f.Version < 14 then
  begin
    rendimiento_pmin := 0.0;
    rendimiento_pmax := 0.0;
    self.tRepHoras := 15 * 24;
  end
  else
  begin
    if (f.Version < 80 ) then
    begin
      rendimiento_pmin := 0.0;
      rendimiento_pmax := 0.0;
    end;
    if (f.Version < 71) then
    begin
      PagoPorDisponibilidad_USD_MWh := 0;
      PagoPorEnergia_USD_MWh := 0;
    end;
  end;
end;


procedure TFichaGTer_OnOffPorPoste.generarLineaResumen(var archi: TextFile);
begin
  Write(archi, FloatToStrF(PMin, formatoReales, 8, 1), #9,
    //PMín
    FloatToStrF(PMax, formatoReales, 8, 1), #9,
    //PMáx
    FloatToStrF(cv_min, formatoReales, 8, 2), #9,
    //CV_Mín
    FloatToStrF(((cv_min * PMin + cv * (PMax - PMin)) / PMax), formatoReales, 8, 2),
    #9,    //CV_Medio
    FloatToStrF(cv, formatoReales, 8, 2), #9,
    //CV_Incremental
    FloatToStrF(disp, formatoReales, 8, 2), #9,
    //FDisp
    '-', #9,
    //Costo Arranque
    '-', #9,
    //Costo Parada
    '-', #9,
    //mínNPasosOn
    '-', #9,
    //mínNPasosOff
    '-', #9,
    //desiciónPasosOnPorCiclo
    '-', #9,
    //desiciónPasosOffPorCiclo
    '-', #9,
    //costoPorCicloOn
    '-', #9);
  //costoPorCicloOff
end;

function TFichaGTer_OnOffPorPoste.infoAd_: string;
begin
  Result := 'PMín= ' + FloatToStrF(PMin, ffGeneral, 10, 1) + ' MW, ' +
    'PMáx= ' + FloatToStrF(PMax, ffGeneral, 10, 1) + ' MW, ' +
    'cv_min= ' + FloatToStrF(cv_min, ffGeneral, 10, 1) + ' USD/MWh, ' +
    'cv= ' + FloatToStrF(cv, ffGeneral, 10, 1) + ' USD/MWh, ' + 'fDisp= ' +
    FloatToStrF(disp, ffGeneral, 10, 2) + ' p.u., ' + 'tRep= ' +
    FloatToStrF(tRepHoras, ffGeneral, 10, 1) + 'h';
end;

destructor TFichaGTer_OnOffPorPoste.destroy;
begin
  inherited destroy;
end;



































//------------------------
// Métodos de TGTer_OnOffPorPoste
//========================
procedure TGTer_OnOffPorPoste.dump_Variables(var f: TextFile; charIndentacion: char);
begin
  inherited dump_Variables(f, charIndentacion);
  writeln(f, charIndentacion, 'c0[USD/MWh]= ', FloatToStrF(c0, ffFixed, 10, 3));
  writeln(f, charIndentacion, 'cv[USD/MWh]= ', FloatToStrF(cv, ffFixed, 10, 3));

  writeln(f, charIndentacion, 'PMin[MW]= ', FloatToStrF(PMin, ffFixed, 10, 3));
  writeln(f, charIndentacion, 'PMAx[MW]= ', FloatToStrF(pa.PMax, ffFixed, 10, 3));
  writeln(f, charIndentacion, 'PxMax[MW]= ', FloatToStrF(PxMax, ffFixed, 10, 3));
  writeln(f, charIndentacion, 'PMaxDisponible[MW]= ',
    FloatToStrF(PMaxDisponible, ffFixed, 10, 3));

  writeln(f, charIndentacion, 'NMaquinasDisponibles= ', NMaquinasDisponibles);

  writeln(f, charIndentacion, 'HayRestrEMaxPasoDeTiempo= ',
    pa.hayRestriccionEmaxPasoDeTiempo);
  writeln(f, charIndentacion, 'EMaxPasoDeTiempo[MW/h]= ',
    FloatToStrF(pa.EmaxPasoDeTiempo, ffFixed, 10, 3));

  writeln(f);
end;

class function TGTer_OnOffPorPoste.TipoFichaLPD: TClaseDeFichaLPD;
begin
  Result := TFichaGTer_OnOffPorPoste;
end;

constructor TGTer_OnOffPorPoste.Create(capa: integer; nombre: string;
  nacimiento, muerte: TFecha; lpdUnidades, lpd: TFichasLPD; nodo: TNodo;
  flg_CalcularGradienteDeInversion: boolean; TonCO2xMWh: NReal; LowCostMustRun,
  CleanDevelopmentMechanism: boolean; xFuenteIdxP: TFuenteAleatoria;
  xBorneIdxP: string);
begin
  inherited Create(capa, nombre, nacimiento, muerte, lpdUnidades, lpd, nodo,
   flg_CalcularGradienteDeInversion,
    TonCO2xMWh, LowCostMustRun, CleanDevelopmentMechanism,
    xFuenteIdxP, xBorneIdxP );
end;

function TGTer_OnOffPorPoste.Rec: TCosa_RecLnk;
begin
  Result:=inherited Rec;
end;

procedure TGTer_OnOffPorPoste.BeforeRead(version, id_hilo: integer);
begin
  inherited BeforeRead(version, id_hilo);
end;

procedure TGTer_OnOffPorPoste.AfterRead(f:TArchiTexto);
begin
  inherited AfterRead(f);
  pa := nil;
  nodo := nil;
end;



procedure TGTer_OnOffPorPoste.PrepararMemoria(Catalogo: TCatalogoReferencias;
  globs: TGlobs);
begin
  inherited prepararMemoria(Catalogo, globs);
  SetLength(costosPorPoste, globs.NPostes);

(* Dimensionamoes estos vectores de tamaño NPostes aune si la central es del tipo
ONOFF Por paso bastaría con dimensión= 1 *)
  SetLength(NMaquinasDespachadas, globs.NPostes);
end;

procedure TGTer_OnOffPorPoste.RegistrarParametrosDinamicos(
  CatalogoReferencias: TCatalogoReferencias);
var
  i: integer;
  ficha: TFichaGTer_OnOffPorPoste;
begin
  inherited registrarParametrosDinamicos( CatalogoReferencias );
  lpd.expandirFichas(CatalogoReferencias, globs);
  lpd.RegistrarFichasAActualizar(Self, globs.ActualizadorLPD, @pA, nil);
  for i := 0 to lpd.Count - 1 do
  begin
    ficha := TFichaGTer_OnOffPorPoste(lpd[i]);
    if ficha.indicePreciosPorCombustible <> nil then
      ficha.nroBornePreciosPorCombustible :=
        ficha.indicePreciosPorCombustible.IdBorne(ficha.bornePreciosPorCombustible);
  end;
end;

function TGTer_OnOffPorPoste.PotenciaFirme: NReal;
begin
  Result := (paUnidades.nUnidades_Operativas[0]) * pa.PMax * pa.disp;
end;

function TGTer_OnOffPorPoste.InfoAd_: string;
begin
  Result := '';
end;

class function TGTer_OnOffPorPoste.DescClase: string;
begin
  Result := rsGeneradorTermicoEncYApXPoste;
end;



function TGTer_OnOffPorPoste.get_pa_FD( kTipoUnidad: integer ): NReal;
begin
  result:= pa.disp;
end;

function TGTer_OnOffPorPoste.get_pa_TMR( kTipoUnidad: integer ): NReal;
begin
  result:= pa.tRepHoras;
end;

procedure TGTer_OnOffPorPoste.SorteosDelPaso(sortear: boolean);
begin
  if hayForzamientos or globs.ObligarDisponibilidad_1_ then
  begin
    NMaquinasDisponibles := paUnidades.nUnidades_Operativas[0];
    PMaxDisponible := pa.PMax * NMaquinasDisponibles;
  end
  else
  if sortear then
  begin
    ActualizarProbabilidadesReparacionYRotura_(pa.disp, pa.tRepHoras);
    NMaquinasDisponibles := Sorteos_RepRotUnidades;
    PMaxDisponible := pa.PMax * NMaquinasDisponibles;
  end
  else
  begin
    NMaquinasDisponibles := paUnidades.nUnidades_Operativas[0];
    PMaxDisponible := pa.PMax * pa.disp * NMaquinasDisponibles;
  end;
end;

procedure TGTer_OnOffPorPoste.PrepararPaso_ps;
var
  indice: NReal;
begin
  PMin := pa.PMIn;
  PxMax := pa.PMax - pa.PMin;
  if pa.indicePreciosPorCombustible <> nil then
  begin
    indice := pa.indicePreciosPorCombustible.bornera[pa.nroBornePreciosPorCombustible];
    c0 := pa.PMin * ( pa.cv_min * indice +pa.cv_NoCombustible);
    cv := pa.cv * indice + pa.cv_NoCombustible;
    cve := pa.PagoPorEnergia_USD_MWh * indice;
  end
  else
  begin
    c0 := pa.PMin * ( pa.cv_min +pa.cv_NoCombustible);
    cv := pa.cv + pa.cv_NoCombustible;
    cve := pa.PagoPorEnergia_USD_MWh;
  end;
  if pa.PMax > AsumaCero then
    cv_medio:= ( c0 + cv * PxMax )/ pa.PMax
  else
    cv_medio:= cv;
end;

procedure TGTer_OnOffPorPoste.Sim_Paso_Fin;
var
  iposte: integer;
begin
  if NMaquinasDisponibles > 0 then
  begin
    for iposte := 0 to high(P) do
      potMedia_despachada := potMedia_despachada + P[iposte] * globs.durpos[iposte];
    potMedia_despachada := potMedia_despachada * globs.invHorasDelPaso;
  end
  else
  begin
    potMedia_despachada := 0;
  end;
end;

procedure TGTer_OnOffPorPoste.opt_nvers(var ivar, ivae, ires: integer);
begin
  if NMaquinasDisponibles = 0 then
    exit;
  Self.ivar := ivar;
  ivar := ivar + globs.NPostes * 2;
  Self.ivae := ivae;
  ivae := ivae + globs.NPostes;
  Self.ires := ires;
  ires := ires + globs.NPostes;
  if pA.HayRestriccionEmaxPasoDeTiempo then
    Inc(ires);
end;

procedure TGTer_OnOffPorPoste.opt_cargue(s: TSimplex);
var
  inodores: integer;
  iposte: integer;
  jres: integer;
begin
  if NMaquinasDisponibles = 0 then
    exit; // si no hay máquinas no juego

  inodores := nodo.ires;
  // aporte a las restricciones del nodo
  for iposte := 0 to globs.NPostes - 1 do
  begin
    s.pon_e(inodores + iposte, ivar + iposte, 1); // coeficiente de la B[iposte]
    s.pon_e(inodores + iposte, ivar + iposte + globs.NPostes, Pmin); // coef A
  end;

  // restricciones adicionales impuestas por la variable de Acople
  jres := ires;
  for iposte := 0 to globs.NPostes - 1 do
  begin
    s.pon_e(jres, ivar + iposte, -1); // coef B[iposte]
    s.pon_e(jres, ivar + globs.NPostes + iposte, PxMax);
    Inc(jres);
  end;
  //Restriccion a la energía máxima generable en el paso
  if pA.HayRestriccionEmaxPasoDeTiempo then
  begin
    for iposte := 0 to globs.NPostes - 1 do
    begin
      s.pon_e(jres, ivar + iposte, -globs.durpos[iposte]);
      s.pon_e(jres, ivar + globs.NPostes + iposte, -globs.durpos[iposte] * pa.PMin);
    end;
    s.pon_e(jres, s.nc, pa.EmaxPasoDeTiempo);
  end;

  // aportes a la función de utilidad
  for iposte := 0 to globs.NPostes - 1 do
  begin
    s.pon_e(s.nf, ivar + iposte, -cv * globs.DurPos[iposte]);
    s.pon_e(s.nf, ivar + globs.NPostes + iposte, -c0 * globs.DurPos[iposte]);
  end;
end;

procedure TGTer_OnOffPorPoste.opt_fijarRestriccionesDeCaja(s: TSimplex);
var
  iposte: integer;
begin
  if NMaquinasDisponibles = 0 then
    exit;

  if (not hayForzamientos) then
  begin
  (*
    // Restricciones de caja de las B (Esto no es necesario pues las restricciones adicionales
    // obligan que estas se cumplan. Por eso le pongo el 1.1 para dejarle la caja un poco floja
    for iposte:= 0 to globs.NPostes-1 do
      s.cota_sup_set( ivar+iposte, PxMax*NMaquinasDisponibles*1.1 );
  *)

    // Restricciones de caja de las A y las declaramos enteras
    for iposte := 0 to globs.NPostes - 1 do
      s.set_EnteraConAcople(
        ivae + iposte, ivar + globs.NPostes + iposte, NMaquinasDisponibles,
        ivar + iposte, ires + iposte);
  end
  else
  begin
    {
    for iposte := 0 to globs.NPostes - 1 do
      s.FijarVariable(ivar + iposte, paForzamiento.P[0] - pa.Pmin);
     }

       for iposte := 0 to globs.NPostes - 1 do
        begin
          if paForzamiento.flg_fijarP then
            s.FijarVariable(ivar + iposte, paForzamiento.P[0] - pa.Pmin)
          else if paForzamiento.activar_Pmax then
            s.cota_sup_set(ivar + iposte, paForzamiento.P_max[0] - pa.Pmin)
          else if paForzamiento.activar_Pmin then
            s.cota_inf_set(ivar + iposte, paForzamiento.P_min[0] - pa.Pmin);
        end;

    // Restricciones de caja de las A y las declaramos enteras
    for iposte := 0 to globs.NPostes - 1 do
    begin
      s.set_EnteraConAcople(
        ivae + iposte, ivar + globs.NPostes + iposte, NMaquinasDisponibles,
        ivar + iposte, ires + iposte);
      s.FijarVariable(ivar + globs.NPostes + iposte, NMaquinasDisponibles);
    end;
  end;

end;

procedure TGTer_OnOffPorPoste.opt_leerSolucion(s: TSimplex);
var
  iposte: integer;
  nmaqs: integer;
  m: NReal;
  EnergiaDelPaso: NReal;

begin
  maxNMaquinasDespachadas := 0;
  if NMaquinasDisponibles = 0 then
  begin
    vclear(P);
    //vclear( cv_Spot );
    for iposte := 0 to globs.NPostes - 1 do
    begin
      Lambda_P[iPoste]:= 0;
      cv_Spot[iPoste]:=get_cv_falla; // para que en el ordenamiento quede último, con costo variable igual a la falla;
    end;
    vclear(costosPorPoste);
    vclear(NMaquinasDespachadas);
    Ingreso_PorDisponibilidad_ := 0;
    Ingreso_PorEnergia_ := 0;
    costoDirectoDelPaso := 0;
    exit;
  end;

  Ingreso_PorDisponibilidad_ := self.PMaxDisponible * globs.HorasDelPaso *
    pa.PagoPorDisponibilidad_USD_MWh;
  CostoDirectoDelPaso := 0;

  EnergiaDelPaso := 0;

  for iposte := 0 to globs.NPostes - 1 do
  begin
    nmaqs := trunc(s.xval(ivar + globs.NPostes + iposte) + 0.2);
    NMaquinasDespachadas[iposte] := nmaqs;
    m := s.xval(ivar + iposte);
    P[iposte] := m + nmaqs * PMin;

    if nmaqs > 0 then
    begin
      costosPorPoste[iposte] := (m * cv + nmaqs * C0) * globs.DurPos[iposte];
      CostoDirectoDelPaso := CostoDirectoDelPaso + costosPorPoste[iposte];
      EnergiaDelPaso := EnergiaDelPaso + P[iposte] * globs.DurPos[iposte];

      if nmaqs > maxNMaquinasDespachadas then
        maxNMaquinasDespachadas := nmaqs;
      Lambda_P[iPoste]:= s.xmult( ires + iposte ) / globs.DurPos[iposte];
//      cv_Spot[iPoste]:= Nodo.cmarg[iposte] - Lambda_P[iPoste];
      if P[iPoste] > 0 then
        cv_Spot[iposte]:= costosPorPoste[iposte]/ (P[iPoste]* globs.DurPos[iPoste] )
      else
        cv_Spot[iPoste]:= cv_medio;
    end
    else
    begin
      costosPorPoste[iposte] := 0;
      Lambda_P[iPoste]:= s.xmult( ires + iposte ) / globs.DurPos[iposte];
      cv_Spot[iPoste]:= cv_medio;
    end;
  end;

  Ingreso_PorEnergia_ := EnergiaDelPaso * cve;

end;


function TGTer_OnOffPorPoste.getNombreVar(ivar: integer; var nombre: string): boolean;
begin
  if NMaquinasDisponibles = 0 then
    Result := False
  else
  if (ivar >= self.ivar) and (ivar < self.ivar + globs.NPostes) then
  begin
    nombre := self.Nombre + '_P[MW]' + IntToStr(ivar - self.ivar + 1);
    Result := True;
  end
  else if (ivar >= self.ivar + globs.NPostes) and
    (ivar < self.ivar + 2 * globs.NPostes) then
  begin
    nombre := self.Nombre + '_A[MW]' + IntToStr(ivar - (self.ivar + globs.NPostes) + 1);
    Result := True;
  end
  else
    Result := False;
end;

function TGTer_OnOffPorPoste.getNombreRes(ires: integer; var nombre: string): boolean;
begin
  if NMaquinasDisponibles = 0 then
    Result := False
  else
  if (ires >= self.ires) and (ires < self.ires + globs.NPostes) then
  begin
    nombre := self.nombre + '_res-A' + IntToStr(ires - self.ires + 1);
    Result := True;
  end
  else if pa.HayRestriccionEmaxPasoDeTiempo and (ires = self.ires + globs.NPostes) then
  begin
    nombre := self.nombre + '_res-EMax';
    Result := True;
  end
  else
    Result := False;
end;

procedure TGTer_OnOffPorPoste.PubliVars;
begin
  inherited PubliVars;

  PublicarVariableVR('Costo', '[USD]', 6, 1, costosPorPoste, True, True);
  PublicarVariableVI('NMaqsDespachadas', '-', NMaquinasDespachadas, True, True);

  {$IFDEF DECLARAR_VARIABLES_SIMRES_DEF}
  declararVarsPSimResPorDefectoIntercalandoPostes(['P', 'Costo', 'NMaqsDespachadas'],
    globs.NPostes);
  {$ENDIF}

  PublicarVariableNI('MaxNMaqsDespachadas', '-', maxNMaquinasDespachadas, False);
  PublicarVariableNR('c0', '[USD/h]', 6, 1, c0, False);
  PublicarVariableNR('cv', '[USD/MWh]', 6, 2, cv, True);
  PublicarVariableNR('cve', '[USD/MWh]', 6, 2, cve, True);
  PublicarVariableNI('NMaqsDisponibles', '-', NMaquinasDisponibles, False);
  PublicarVariableNR('PMaxDisponible', '[MW]', 6, 1, PMaxDisponible, False);
  PublicarVariableNR('PMediaDespachada', '[MW]', 6, 1, potMedia_despachada, False);
end;

destructor TGTer_OnOffPorPoste.destroy;
begin
  setlength(NMaquinasDespachadas, 0);
  SetLength(costosPorPoste, 0);
  inherited destroy;
end;

procedure AlInicio;
begin
  registrarClaseDeCosa(TGTer_OnOffPorPoste.ClassName, TGTer_OnOffPorPoste);
  registrarClaseDeCosa(TFichaGter_onoffporposte.ClassName, TFichaGter_onoffporposte);
end;

procedure AlFinal;
begin
end;

end.
