//====Modificado metodos: CreateDataColumnList y CreateDataConversionList===
//====Micho@28/5==mvarela@adme.com.uy=======================================
unit ugter_basico;

{$MODE Delphi}

interface

uses
  Math,
  SysUtils, Classes, xmatdefs, uGTer, uNodos,
  uglobs,
  usimplex,
  ufichasLPD,
  ufechas,
  ucosa,
  uCosaConNombre,
  uconstantesSimSEE,
  uFuentesAleatorias;

resourcestring
  rsGeneradorTermicoBasico = 'Generador térmico básico';

type

  { TFichaGTer_Basico }

  TFichaGTer_Basico = class(TFichaLPD)
  public
    (**************************************************************************)
    (*               A T R I B U T O S   P E R S I S T E N T E S              *)
    (**************************************************************************)
    PMax: NReal; // [MW] Potencia Máxima Por maquina
    cv: NReal; // Costo:= cv* P
    cv_noCombustible: NReal; // no indexa

    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
    fReserva: NReal;
    // factor de reserva. Porcentaje de la potencia total del generador que se puede usar como reserva

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

    nroBornePreciosPorCombustible: integer;
    constructor Create(capa: integer; fecha: TFecha; periodicidad: TPeriodicidad;
      PMax: NReal; cv, cv_NoCombustible: NReal;
      indicePreciosPorCombustible: TFuenteAleatoria;
      bornePreciosPorCombustible: string; disp: NReal; tRepHoras: NReal;
      HayRestriccionEmaxPasoDeTiempo: boolean; EmaxPasoDeTiempo: NReal;
      PagoPorDisponibilidad_USD_MWh: NReal; PagoPorEnergia_USD_MWh: NReal;
      fReserva: double = 0);//se agregó la variable fReserva seteada en 0


    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;

  // TGTer_Basico 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.
  (*Se agrega la capacidad de entregar reserva
  desde el 8/5/15 por Facundo fartagaveytia@adme.com.uy*)
  TGTer_Basico = class(TGTer)
  public
    pa: TFichaGTer_Basico;

    NMaquinasDespachadas: TDAOfNInt;
    // cantidad de máquinas despachadas por poste o por paso ( Acople )
    costos: TDAofNReal;              // costos por máquina por poste
    // costos[i]:= P[i] * durpos[i] * ( cv + cve )

    maxNMaquinasDespachadas: integer; // máximo del vector anterior

    cv: NReal; // USD/MWh usado para la optimización del paso.
    cve: NReal; // costo por la energía adicional a CV

    // 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;

    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;

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

    {$IFDEF BOSTA}
    procedure AfterInstantiation; override;
    {$ENDIF}
  end;


procedure AlInicio;
procedure AlFinal;

implementation

//------------------------------
// Métodos de TFichaGTer_Basico
//==============================
constructor TFichaGTer_Basico.Create(capa: integer; fecha: TFecha;
  periodicidad: TPeriodicidad; PMax: NReal; cv, cv_NoCombustible: NReal;
  indicePreciosPorCombustible: TFuenteAleatoria; bornePreciosPorCombustible: string;
  disp: NReal; tRepHoras: NReal; HayRestriccionEmaxPasoDeTiempo: boolean;
  EmaxPasoDeTiempo: NReal; PagoPorDisponibilidad_USD_MWh: NReal;
  PagoPorEnergia_USD_MWh: NReal; fReserva: double = 0);
begin
  inherited Create(capa, fecha, periodicidad);
  self.PMax := PMax;
  self.cv := cv;
  self.cv_NoCombustible := cv_NoCombustible;
  self.indicePreciosPorCombustible := indicePreciosPorCombustible;
  self.disp := disp;
  self.tRepHoras := tRepHoras;
  self.HayRestriccionEmaxPasoDeTiempo := HayRestriccionEmaxPasoDeTiempo;
  self.EmaxPasoDeTiempo := EmaxPasoDeTiempo;
  self.indicePreciosPorCombustible := indicePreciosPorCombustible;
  self.bornePreciosPorCombustible := bornePreciosPorCombustible;
  self.PagoPorDisponibilidad_USD_MWh := PagoPorDisponibilidad_USD_MWh;
  self.PagoPorEnergia_USD_MWh := PagoPorEnergia_USD_MWh;
  self.fReserva := fReserva;
end;

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

  // Versión < 6
  Result.addCampoDef('PMax', PMax, 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);

  // 6 <= Versión < 14
  Result.addCampoDef('PMax', PMax, 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);

  // 14 <= Versión < 34
  Result.addCampoDef('PMax', PMax, 14, 34);
  Result.addCampoDef('cv', cv, 14, 34);
  Result.addCampoDef_ref('indicePreciosPorCombustible',
    TCosa(indicePreciosPorCombustible), self, 14, 34);
  Result.addCampoDef('bornePreciosPorCombustible',
    bornePreciosPorCombustible, 14, 34);
  Result.addCampoDef('disp', disp, 14, 34);
  Result.addCampoDef('tRepHoras', tRepHoras, 14, 34);
  Result.addCampoDef('HayRestriccionEmaxPasoDeTiempo',
    HayRestriccionEmaxPasoDeTiempo, 14, 34);
  Result.addCampoDef('EmaxPasoDeTiempo', EmaxPasoDeTiempo, 14, 34);

  // 34 <= Versión
  Result.addCampoDef('PMax', PMax, 34);
  Result.addCampoDef('cv', cv, 34);
  Result.addCampoDef('cv_NoCombustible', cv_NoCombustible, 111);
  Result.addCampoDef_ref('indicePreciosPorCombustible',
    TCosa(indicePreciosPorCombustible), self, 34);
  Result.addCampoDef('bornePreciosPorCombustible', bornePreciosPorCombustible, 34);
  Result.addCampoDef('disp', disp, 34);
  Result.addCampoDef('tRepHoras', tRepHoras, 34);
  Result.addCampoDef('HayRestriccionEmaxPasoDeTiempo',
    HayRestriccionEmaxPasoDeTiempo, 34);
  Result.addCampoDef('EmaxPasoDeTiempo', EmaxPasoDeTiempo, 34);
  Result.addCampoDef('PagoPorPotencia', PagoPorDisponibilidad_USD_MWh, 34, 98);
  Result.addCampoDef('PagoPorEnergia', PagoPorEnergia_USD_MWh, 34, 98);
  Result.addCampoDef('PagoPorDisponibilidad_USD_MWh',
    PagoPorDisponibilidad_USD_MWh, 98);
  Result.addCampoDef('PagoPorEnergia_USD_MWh', PagoPorEnergia_USD_MWh, 98);
  Result.addCampoDef('fReserva', fReserva, 130);
end;

procedure TFichaGTer_Basico.BeforeRead(version, id_hilo: integer);
begin
  inherited BeforeRead(version, id_hilo);
  self.PagoPorDisponibilidad_USD_MWh := 0;
  self.PagoPorEnergia_USD_MWh := 0;
  cv_NoCombustible := 0.0;
end;

procedure TFichaGTer_Basico.AfterRead(f: TArchiTexto);
begin
  inherited AfterRead(f);
  if f.Version < 6 then
  begin
    self.indicePreciosPorCombustible := nil;
    self.bornePreciosPorCombustible := '';
    self.tRepHoras := 15 * 24;
  end
  else if f.version < 14 then
  begin
    self.tRepHoras := 15 * 24;
  end;
end;


procedure TFichaGTer_Basico.generarLineaResumen(var archi: TextFile);
begin
  Write(archi, '-', #9,                                     //PMín
    FloatToStrF(PMax, formatoReales, 8, 1), #9,  //PMáx
    '-', #9,                                     //CV_Mín
    FloatToStrF(cv, 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_Basico.infoAd_: string;
begin
  Result := 'PMax= ' + FloatToStrF(PMax, ffGeneral, 10, 1) + ' MW, ' +
    '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_Basico.Destroy;
begin
  inherited Destroy;
end;


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

  writeln(f, charIndentacion, 'PMax[MW]= ', FloatToStrF(pa.PMax, 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_Basico.TipoFichaLPD: TClaseDeFichaLPD;
begin
  Result := TFichaGTer_Basico;
end;

constructor TGTer_Basico.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);
  P := nil;
  costos := nil;
  NMaquinasDespachadas := nil;
end;

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

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

procedure TGTer_Basico.AfterRead(f: TArchiTexto);
begin
  inherited AfterRead(f);
  P := nil;
  costos := nil;
  NMaquinasDespachadas := nil;
  pa := nil;
  nodo := nil;
end;




procedure TGTer_Basico.PrepararMemoria(Catalogo: TCatalogoReferencias; globs: TGlobs);
begin
  inherited prepararMemoria(Catalogo, globs);
  setlength(P, globs.NPostes);
  SetLength(costos, globs.NPostes);
  SetLength(NMaquinasDespachadas, globs.NPostes);
end;

procedure TGTer_Basico.RegistrarParametrosDinamicos(
  CatalogoReferencias: TCatalogoReferencias);
var
  i: integer;
  ficha: TFichaGTer_Basico;
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_Basico(lpd[i]);
    if ficha.indicePreciosPorCombustible <> nil then
      ficha.nroBornePreciosPorCombustible :=
        ficha.indicePreciosPorCombustible.IdBorne(ficha.bornePreciosPorCombustible);
  end;
end;

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

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

class function TGTer_Basico.DescClase: string;
begin
  Result := rsGeneradorTermicoBasico;
end;


function TGTer_Basico.get_pa_FD(kTipoUnidad: integer): NReal;
begin
  Result := pa.disp;
end;

function TGTer_Basico.get_pa_TMR(kTipoUnidad: integer): NReal;
begin
  Result := pa.tRepHoras;
end;

{$IFDEF BOSTA}
procedure TGTer_Basico.AfterInstantiation;
begin
  inherited AfterInstantiation;
  P := nil;
  costos := nil;
  NMaquinasDespachadas := nil;
  pa := nil;
  nodo := nil;
end;

{$ENDIF}

procedure TGTer_Basico.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_Basico.PrepararPaso_ps;
{$IFDEF CALC_MMEE}
var
  k: integer;
{$ENDIF}
begin
  if pa.indicePreciosPorCombustible <> nil then
  begin
    cv := pa.cv * pa.indicePreciosPorCombustible.bornera[
      pa.nroBornePreciosPorCombustible];
    cve := pa.PagoPorEnergia_USD_MWh * pa.indicePreciosPorCombustible.bornera
      [pa.nroBornePreciosPorCombustible];
  end
  else
  begin
    cv := pa.cv;
    cve := pa.PagoPorEnergia_USD_MWh;
  end;

  cv := cv + pa.cv_noCombustible;

  {$IFDEF CALC_MMEE}
  for k := 0 to globs.NPOstes - 1 do
    mmee_cv_paraPrecioSpot[k] := cv;
  {$ENDIF}

  flg_ReservaRotanteDisponible :=
    globs.flg_ReservaRotante and (pa.fReserva > AsumaCero) and
    (PMaxDisponible > 0);
end;

procedure TGTer_Basico.Sim_Paso_Fin;
var
  iposte: integer;
  ePoste: NReal;
begin

  potMedia_despachada := 0;


  if NMaquinasDisponibles = 0 then
  begin
  {$IFDEF CALC_MMEE}
    vclear(mmee_IngresosSpot_USD);
    vclear(mmee_CargosAlSeguimientoDeLaDemanda_USD);
  {$ENDIF}
  end
  else
  begin
    for iposte := 0 to high(P) do
    begin
      ePoste := P[iposte] * globs.durpos[iposte];
      potMedia_despachada := potMedia_despachada + ePoste;

      {$IFDEF CALC_MMEE}
      if (mmee_cv_paraPrecioSpot[iposte] > globs.mmee_topePrecioSpot) then
      begin
        mmee_CargosAlSeguimientoDeLaDemanda_USD[iposte] :=
          ePoste * (mmee_cv_paraPrecioSpot[iposte] - globs.mmee_topePrecioSpot);
        mmee_IngresosSpot_USD[iposte] := ePoste * globs.mmee_topPrecioSpot;
      end
      else
      begin
        mmee_CargosAlSeguimientoDeLaDemanda_USD[iposte] := 0;
        mmee_IngresosSpot_USD[iposte] := ePoste * globs.mmee_PrecioSpot[iposte];
      end;
      {$ENDIF}
    end;
    potMedia_despachada := potMedia_despachada * globs.invHorasDelPaso;
  end;
end;

procedure TGTer_Basico.opt_nvers(var ivar, ivae, ires: integer);
begin
  if NMaquinasDisponibles = 0 then
    exit; // si no hay máquinas no juego
  Self.ivar := ivar;
  //guardo en ivar el lugar donde empiezan las variables de optimización del generador
  ivar := ivar + globs.NPostes;

  //agrego una variable por cada poste de tiempo, la cual representa la potencia
  if flg_ReservaRotanteDisponible then
    //si el modo reserva esta activado agrego una variable más por cada poste, la cual representa la reserva
  begin
    ivar_ReservaRotante := ivar;
    ivar := ivar + globs.NPostes;
  end;

  if flg_ReservaRotanteDisponible or (pa.HayRestriccionEmaxPasoDeTiempo) then
    //cualquiera de estas dos condiciones implica que hay restricciones asociadas al generador
    Self.ires := ires;

  if flg_ReservaRotanteDisponible then
    // si el modo reserva esta activado agrego dos restricciones asociadas al generador por cada poste
  begin
    ires_ReservaRotante := ires;
    ires := ires + 2 * globs.NPostes;
  end;

  if pA.HayRestriccionEmaxPasoDeTiempo then
    //si se cumple esta condición se agrega una restricción
  begin
    //Self.ires := ires;
    Inc(ires);
  end;
end;

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

  inodores := nodo.ires;
  for iposte := 0 to globs.NPostes - 1 do
  begin
    // cargamos la potencia en la restricción de balance de potencia del nodo
    s.pon_e(inodores + iposte, ivar + iposte, 1);
    // Ahora agregamos a la función de UTILIDAD (-costo) los campos relacionados a la potencia
    s.pon_e(s.nf, ivar + iposte, -cv * globs.durpos[iposte]);
  end;

  if flg_ReservaRotanteDisponible then
  begin
    for iposte := 0 to globs.NPostes - 1 do
    begin
      // cargamos la reserva en la restricción de balance de reserva del nodo
      s.pon_e(nodo.ires_ReservaRotante + iposte, ivar_ReservaRotante + iposte, 1);

      // Ahora agregamos a la función de UTILIDAD (-costo) los campos relacionados a la reserva
      // De momento el costo variable de reserva se setea en 0
      s.pon_e(s.nf, ivar_ReservaRotante + iposte, 0);

      // cargo restriccion limite potencia nominal
      s.pon_e(ires_ReservaRotante + iposte, ivar_ReservaRotante + iposte, 1);
      s.pon_e(ires_ReservaRotante + iposte, ivar + iposte, -1);// potencia
      s.pon_e(ires_ReservaRotante + iposte, ivar_ReservaRotante + iposte, -1);// reserva
      s.pon_e(ires_ReservaRotante + iposte, s.nc, PMaxDisponible);
      // termino independiente

      // cargo restriccion encendido para poder generar reserva
      s.pon_e(ires_ReservaRotante + globs.NPostes + iposte, ivar + iposte, 100);
      // potencia
      s.pon_e(ires_ReservaRotante + globs.NPostes + iposte,
        ivar_ReservaRotante + iposte, -1);
      // reserva
    end;

  end;


  //Restriccion a la energía máxima generable
  if pA.HayRestriccionEmaxPasoDeTiempo then
  begin
    for iposte := 0 to globs.NPostes - 1 do
      s.pon_e(ires, ivar + iposte, -globs.durpos[iposte]);
    s.pon_e(ires, s.nc, pA.EmaxPasoDeTiempo);
  end;
end;

procedure TGTer_Basico.opt_fijarRestriccionesDeCaja(s: TSimplex);
var
  iposte: integer;
begin
  if NMaquinasDisponibles = 0 then
    exit;
  // Le fijamos como cota superior PMax o la Pforzada a la potencia en todos los postes

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

  if flg_ReservaRotanteDisponible then
    //si el modo reserva esta activado agrego la restriccion de caja que le pone una cota superior a la
  begin
    //reserva a despachar. Dicha cota es igual al producto de la potencia maxima que puede ser despachada con el factor de reserva
    for iposte := 0 to globs.NPostes - 1 do
      s.cota_sup_set(ivar_ReservaRotante + iposte, PMaxDisponible * pa.fReserva);
  end;
end;

procedure TGTer_Basico.opt_leerSolucion(s: TSimplex);
var
  iposte: integer;
  mc, me, meAcum: NReal;

begin
  maxNMaquinasDespachadas := 0;


  if NMaquinasDisponibles = 0 then
  begin
    if globs.flg_ReservaRotante then
      vclear(ReservaRotante);

    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(NMaquinasDespachadas);
    vclear(costos);
    Ingreso_PorDisponibilidad_ := 0;
    Ingreso_PorEnergia_ := 0;
    costoDirectoDelPaso := 0;
    exit;
  end;

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

  // recuperamos los valores de Potencia despachada
  meAcum := 0;
  for iposte := 0 to globs.NPostes - 1 do
  begin
    P[iposte] := s.xval(ivar + iposte);
    Lambda_P[iPoste] := s.xmult(ivar + iposte) / globs.DurPos[iposte];
    //    cv_Spot[iPoste]:= Nodo.cmarg[iposte] - Lambda_P[iPoste];
    if not hayForzamientos then
      cv_Spot[iPoste] := cv
    else
      cv_Spot[iPoste] := 0; // si hay forzamiento, no marca precio

    if P[iposte] > GTER_PRECISIONPOTENCIANMAQS then
    begin
      NMaquinasDespachadas[iposte] :=
        Math.Ceil((P[iposte] - GTER_PRECISIONPOTENCIANMAQS) / pa.PMax);
      me := P[iposte] * globs.DurPos[iposte];
      mc := me * cv;
      costos[iposte] := mc;
      CostoDirectoDelPaso := CostoDirectoDelPaso + mc;
      meAcum := meAcum + me;
      if NMaquinasDespachadas[iposte] > maxNMaquinasDespachadas then
        maxNMaquinasDespachadas := NMaquinasDespachadas[iposte];
    end
    else
    begin
      NMaquinasDespachadas[iposte] := 0;
      costos[iposte] := 0;
    end;
  end;

  if flg_ReservaRotanteDisponible then
    for iposte := 0 to globs.NPostes - 1 do
      ReservaRotante[iposte] := s.xval(ivar_ReservaRotante + iposte);


  Ingreso_PorEnergia_ := meAcum * cve;
end;


function TGTer_Basico.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 flg_ReservaRotanteDisponible and (ivar >= self.ivar_ReservaRotante) and
    (ivar < (self.ivar_ReservaRotante + globs.NPostes)) then
  begin
    nombre := self.Nombre + '_RR[MW]' + IntToStr(ivar - self.ivar_ReservaRotante + 1);
    Result := True;
  end
  else
    Result := False;
end;

function TGTer_Basico.getNombreRes(ires: integer; var nombre: string): boolean;
var
  iresx: integer;

begin
  if NMaquinasDisponibles = 0 then
  begin
    Result := False;
    exit;
  end;


  if flg_ReservaRotanteDisponible then
  begin
    if (ires >= ires_ReservaRotante) and
      (ires < ires_ReservaRotante + 2 * globs.NPostes) then
    begin
      if ires < ires_ReservaRotante + globs.NPostes then
        nombre := self.nombre + '_ReservaRot_LimPot_P_' + IntToStr(
          ires - ires_ReservaRotante + 1)
      else
        nombre := self.nombre + '_ReservaRot_GenPos_P_' + IntToStr(
          ires - ires_ReservaRotante + 1);
      Result := True;
      exit;
    end;
    iresx := self.ires + 2 * globs.NPostes;
  end
  else
    iresx := Self.ires;


  if pa.HayRestriccionEmaxPasoDeTiempo and (ires = iresx) then
  begin
    nombre := self.nombre + '_res-EMax';
    Result := True;
  end
  else
    Result := False;
end;

procedure TGTer_Basico.PubliVars;
begin
  inherited PubliVars;
  PublicarVariableVR('Costo', '[USD]', 6, 2, costos, True, True);
  PublicarVariableVI('NMaqsDespachadas', '-', NMaquinasDespachadas, True, True);

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

  PublicarVariableNR('cv', '[USD/MWh]', 6, 2, cv, True);
  PublicarVariableNR('cve', '[USD/MWh]', 6, 2, cve, True);

  PublicarVariableNI('MaxNMaqsDespachadasEnElPaso', '-', maxNMaquinasDespachadas, False);
  PublicarVariableNI('NMaqsDisponibles', '-', NMaquinasDisponibles, False);
  PublicarVariableNR('PMaxDisponible', '[MW]', 6, 2, PMaxDisponible, False);
  PublicarVariableNR('PMediaDespachada', '[MW]', 6, 2, potMedia_despachada, False);

end;

destructor TGTer_Basico.Destroy;
begin
  setlength(NMaquinasDespachadas, 0);
  setlength(costos, 0);
  inherited Destroy;
end;

procedure AlInicio;
begin
  registrarClaseDeCosa(TGTer_Basico.ClassName, TGTer_Basico);
  registrarClaseDeCosa(TFichaGTer_Basico.ClassName, TFichaGTer_Basico);
end;

procedure AlFinal;
begin
end;

end.
