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

interface

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

resourcestring
  rsGeneradorConectableASuministro =
    'Generador térmico conectable a suministro de combustible.';

type

  { TFichaGTer_ConectableASuministro }

  TFichaGTer_ConectableASuministro = 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
    rendimiento_pmin, rendimiento_pmax: NReal;
    // Rendimiento a minima y a maxima carga unidades volumen/kWh
    cv_no_combustible: NReal; //Costo variable No combustible (USD/MWh)
    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
    PagoPorPotencia: NReal; // [USD/MWh] Pago por Potencia
    PagoPorEnergia: NReal; // [USD/MWh] Pago por Potencia

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

    nroBornePreciosPorCombustible: integer;

    constructor Create(capa: integer; fecha: TFecha; periodicidad: TPeriodicidad;
      PMin, PMax: NReal; rendimiento_pmin: NReal; rendimiento_pmax: NReal;
  cv_no_combustible: NReal; indicePreciosPorCombustible: TFuenteAleatoria;
  bornePreciosPorCombustible: string; disp: NReal; tRepHoras: NReal;
  HayRestriccionEmaxPasoDeTiempo: boolean; EmaxPasoDeTiempo: NReal);

     
    constructor Create_ReadFromText(f: TArchiTexto); override;
    procedure WriteToText(f: TArchiTexto); override;
    
    procedure generarLineaResumen(var archi: TextFile); override;
    function infoAd_: string; override;
    procedure Free; 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_ConectableASuministro }

  TGTer_ConectableASuministro = class(TGTer)
  private
    function InterpolacionLineal(x, x1, x2, y1, y2: double): double;

  public

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

    suministroCombustible: TSuministroCombustible;

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

    pa: TFichaGter_ConectableASuministro;

    // Potencias que resultaron despachadas

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

    PagoPorPotenciaDisponible: NReal;

    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.
    cve: NReal; // costo por la energía adicional a CV

    // auxiliares. Caluladas en Preparar Paso
    combustible: TCombustibleSGE;
    cvm: NReal; //USD/MWh costo variable medio aplicable para el despacho a PLENA CARGA.

    ConsumoXPoste: TDAofNReal; //Consumo por poste del generador
    volumenPMin: double; //Volumen consumido en el mínimo
    coef_volumenMayMin: double;
    //Coeficiente del volumen consumido por mega por encima del mínimo

    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); override;

     
    constructor Create_ReadFromText(f: TArchiTexto); override;
    procedure WriteToText(f: TArchiTexto); override;
    
    procedure PrepararMemoria(globs: TGlobs); override;
    procedure RegistrarParametrosDinamicos; 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;
{$IFDEF SPXCONLOG}
    procedure spx_NombrarVariables(s: TSimplex); override;
{$ENDIF}
    function getNombreVar(ivar: integer; var nombre: string): boolean; override;
    function getNombreRes(ires: integer; var nombre: string): boolean; override;

    procedure Free; override;

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

    class function TipoFichaLPD: TClaseDeFichaLPD; override;

    


    procedure AfterInstantiation; override;

  published
    



  end;

procedure AlInicio;
procedure AlFinal;

implementation

//-----------------------------------
// Métodos de TFichaGTer_OnOffPorPaso
//===================================
constructor TFichaGTer_ConectableASuministro.Create(capa: integer; fecha: TFecha;
  periodicidad: TPeriodicidad; PMin, PMax: NReal; rendimiento_pmin: NReal;
  rendimiento_pmax: NReal; cv_no_combustible: NReal;
  indicePreciosPorCombustible: TFuenteAleatoria; bornePreciosPorCombustible: string;
  disp: NReal; tRepHoras: NReal; HayRestriccionEmaxPasoDeTiempo: boolean;
  EmaxPasoDeTiempo: NReal);
begin
  inherited Create(capa, fecha, periodicidad);
  self.PMin := PMin;
  self.PMax := PMax;
  self.rendimiento_pmin := rendimiento_pmin;
  self.rendimiento_pmax := rendimiento_pmax;
  self.cv_no_combustible := cv_no_combustible;
  self.indicePreciosPorCombustible := indicePreciosPorCombustible;
  self.bornePreciosPorCombustible := bornePreciosPorCombustible;
  self.disp := disp;
  self.tRepHoras := tRepHoras;
  self.HayRestriccionEmaxPasoDeTiempo := HayRestriccionEmaxPasoDeTiempo;
  self.EmaxPasoDeTiempo := EmaxPasoDeTiempo;
end;

 
constructor TFichaGTer_ConectableASuministro.Create_ReadFromText(f: TArchiTexto);
begin
  inherited Create_ReadFromText(f);
  f.IniciarLecturaRetrasada;
  f.rd('PMin', PMin);
  f.rd('PMax', PMax);
  f.rd('rendimiento_pmin', rendimiento_pmin);
  f.rd('rendimiento_pmax', rendimiento_pmax);
  f.rd('cv_no_combustible', cv_no_combustible);
  f.rdReferencia('indicePreciosPorCombustible',
    TCosa(indicePreciosPorCombustible), Self);
  f.rd('bornePreciosPorCombustible', bornePreciosPorCombustible);
  f.rd('disp', disp);
  f.rd('tRepHoras', tRepHoras);
  f.rd('HayRestriccionEmaxPasoDeTiempo', HayRestriccionEmaxPasoDeTiempo);
  f.rd('EmaxPasoDeTiempo', EmaxPasoDeTiempo);
  f.rd('PagoPorPotencia', PagoPorPotencia);
  f.rd('PagoPorEnergia', PagoPorEnergia);
  f.EjecutarLectura;

end;

procedure TFichaGTer_ConectableASuministro.WriteToText(f: TArchiTexto);
begin
  inherited WriteToText(f);
  f.wr('PMin', PMin, uconstantesSimSEE.CF_PRECISION, uconstantesSimSEE.CF_DECIMALES);
  f.wr('PMax', PMax, uconstantesSimSEE.CF_PRECISION, uconstantesSimSEE.CF_DECIMALES);
  f.wr('rendimiento_pmin', rendimiento_pmin, uconstantesSimSEE.CF_PRECISION,
    uconstantesSimSEE.CF_DECIMALES);
  f.wr('rendimiento_pmax', rendimiento_pmax, uconstantesSimSEE.CF_PRECISION,
    uconstantesSimSEE.CF_DECIMALES);
  f.wr('cv_no_combustible', cv_no_combustible, uconstantesSimSEE.CF_PRECISION,
    uconstantesSimSEE.CF_DECIMALES);
  f.wrReferencia('indicePreciosPorCombustible', indicePreciosPorCombustible);
  f.wr('bornePreciosPorCombustible', bornePreciosPorCombustible);
  f.wr('disp', disp, uconstantesSimSEE.CF_PRECISION, uconstantesSimSEE.CF_DECIMALESPU);
  f.wr('tRepHoras', tRepHoras, uconstantesSimSEE.CF_PRECISION,
    uconstantesSimSEE.CF_DECIMALES);
  f.wr('HayRestriccionEmaxPasoDeTiempo', HayRestriccionEmaxPasoDeTiempo);
  f.wr('EmaxPasoDeTiempo', EmaxPasoDeTiempo);
  f.wr('PagoPorPotencia', PagoPorPotencia);
  f.wr('PagoPorEnergia', PagoPorEnergia);
end;


procedure TFichaGTer_ConectableASuministro.generarLineaResumen(var archi: TextFile);
begin
  Write(archi, FloatToStrF(PMin, formatoReales, 8, 1), #9,
    //PMín
    FloatToStrF(PMax, formatoReales, 8, 1), #9,
    //PMáx
    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_ConectableASuministro.infoAd_: string;
begin
  Result := 'PMín= ' + FloatToStrF(PMin, ffGeneral, 10, 1) + ' MW, ' +
    'PMáx= ' + FloatToStrF(PMax, ffGeneral, 10, 1) + ' MW, ' +
    'RendimientoMin=' + FloatToStrF(rendimiento_pmin, ffGeneral, 10, 1) +
    ' , ' + 'RendimientoMax=' + FloatToStrF(rendimiento_pmax, ffGeneral, 10, 1) +
    ' , ' + 'cv_no_combustible=' + FloatToStrF(cv_no_combustible, ffGeneral, 10, 1) +
    ' gr/kWh, ' + 'fDisp= ' + FloatToStrF(disp, ffGeneral, 10, 2) +
    ' p.u., ' + 'tRep= ' + FloatToStrF(tRepHoras, ffGeneral, 10, 1) + 'h';
end;

procedure TFichaGTer_ConectableASuministro.Free;
begin
  inherited Free;
end;
























//------------------------
// Métodos de TGTer_ConectableASuministro
//========================
procedure TGTer_ConectableASuministro.dump_Variables(var f: TextFile;
  charIndentacion: char);
begin
  inherited dump_Variables(f, charIndentacion);
  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_ConectableASuministro.TipoFichaLPD: TClaseDeFichaLPD;
begin
  Result := TFichaGTer_ConectableASuministro;
end;










procedure TGTer_ConectableASuministro.AfterInstantiation;
begin
  inherited AfterInstantiation;
  pa := nil;
  nodo := nil;
end;

constructor TGTer_ConectableASuministro.Create(capa: integer;
  nombre: string; nacimiento, muerte: TFecha; lpdUnidades, lpd: TFichasLPD;
  nodo: TNodo;
  flg_CalcularGradienteDeInversion: boolean;

  TonCO2xMWh: NReal; LowCostMustRun, CleanDevelopmentMechanism: boolean);
begin
  inherited Create(capa, nombre, nacimiento, muerte, lpdUnidades, lpd, nodo,
    flg_CalcularGradienteDeInversion,
    TonCO2xMWh, LowCostMustRun, CleanDevelopmentMechanism);
end;

 
constructor TGTer_ConectableASuministro.Create_ReadFromText(f: TArchiTexto);
begin
  inherited Create_ReadFromText(f);
  pa := nil;
  nodo := nil;
  f.IniciarLecturaRetrasada;
  f.rdReferencia('suministroCombustible', TCosa(suministroCombustible), Self);
  f.EjecutarLectura;
end;

procedure TGTer_ConectableASuministro.WriteToText(f: TArchiTexto);
begin
  inherited WriteToText(f);
  f.wrReferencia('suministroCombustible', suministroCombustible);
end;


procedure TGTer_ConectableASuministro.PrepararMemoria(globs: TGlobs);
begin
  inherited prepararMemoria(globs);
  setlength(P, globs.NPostes);
  SetLength(costosPorPoste, globs.NPostes);
  setlength(ConsumoXPoste, 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_ConectableASuministro.RegistrarParametrosDinamicos;
var
  i: integer;
  ficha: TFichaGTer_ConectableASuministro;
begin
  inherited registrarParametrosDinamicos;
  lpd.expandirFichas(globs);
  lpd.RegistrarFichasAActualizar(Self, globs.ActualizadorLPD, @pA, nil);
  for i := 0 to lpd.Count - 1 do
  begin
    ficha := TFichaGTer_ConectableASuministro(lpd[i]);
    if ficha.indicePreciosPorCombustible <> nil then
      ficha.nroBornePreciosPorCombustible :=
        ficha.indicePreciosPorCombustible.IdBorne(ficha.bornePreciosPorCombustible);
  end;
end;

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

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

class function TGTer_ConectableASuministro.DescClase: string;
begin
  Result := rsGeneradorConectableASuministro;
end;

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

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



procedure TGTer_ConectableASuministro.SorteosDelPaso(sortear: boolean);
begin
  if 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_ConectableASuministro.PrepararPaso_ps;
var
  indice: NReal;

begin
  PMin := pa.PMIn;
  PxMax := pa.PMax - pa.PMin;

  if pa.indicePreciosPorCombustible <> nil then
    indice := pa.indicePreciosPorCombustible.bornera[pa.nroBornePreciosPorCombustible]
  else
    indice := 1;

  //En este caso el costo variable y el minimo es el cv_no_combustible;
  c0 := pa.PMin * pa.cv_no_combustible * indice;
  cv := pa.cv_no_combustible * indice;
  cve := pa.PagoPorEnergia * indice;


  //En el caso de existir un suministro de combustible, en la restriccion impuesta por este
  //para el balance del volumen, cada generador debe colocar sus términos que permitan calcular
  //el volumen consumido en función de la potencia despachada
  //Para ello se requiere de el rendimiento de la maquina, de los PCI, de la constante de
  //conversión a KWHora (en función del tipo de combustible)
  //el Termino a incluir será: 1/k_conversion * 1/rendimiento * duración del poste
  //Lo primero es obtener la fila correspondiente a la restricción:
  combustible := self.suministroCombustible.get_combustible();
  //La ficha tiene el consumo específico. Entonces el 1/rendimiento = consumo especifico

  if (self.PMin = 0) then //Es generador basico
  begin
    coef_volumenMayMin :=
      1 / (pa.rendimiento_pmax * combustible.kConversion * combustible.PCI);
  end
  else
  begin
    volumenPMin :=
      pa.PMin / (pa.rendimiento_pmin * combustible.kConversion * combustible.PCI);

    if (pa.PMax - pa.PMin) = 0 then
    begin
      coef_volumenMayMin := volumenPMin / pa.PMin;
    end
    else
    begin
      coef_volumenMayMin :=
        (1 / (pa.PMax - pa.PMin)) * (pa.PMax / pa.rendimiento_pmax -
        pa.PMin / pa.rendimiento_pmin) * 1 / (combustible.kConversion * combustible.PCI);
    end;
  end;
  cvm := (cv + suministroCombustible.get_precio_combustible /
    (pa.rendimiento_pmax * combustible.kConversion * combustible.PCI));
end;

procedure TGTer_ConectableASuministro.Sim_Paso_Fin;
var
  iposte: integer;
begin
  if (self.suministroCombustible.get_VolCombustibleDisponible = 0) then
  begin
    potMedia_despachada := 0;
    exit;
  end;

  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_ConectableASuministro.opt_nvers(var ivar, ivae, ires: integer);
begin
  if self.suministroCombustible.get_VolCombustibleDisponible = 0 then
    exit;

  if NMaquinasDisponibles = 0 then
    exit;

  Self.ivar := ivar;
  Self.ires := ires;

  if (self.PMin = 0) then //Es generador basico
  begin
    ivar := ivar + globs.NPostes;
  end
  else //Es un generador con mínimo técnico
  begin
    ivar := ivar + globs.NPostes * 2;
    Self.ivae := ivae;
    ivae := ivae + globs.NPostes;
    ires := ires + globs.NPostes;
  end;



  if pA.HayRestriccionEmaxPasoDeTiempo then
    Inc(ires);
end;

procedure TGTer_ConectableASuministro.opt_cargue(s: TSimplex);
var
  inodores: integer;
  iposte: integer;
  jres: integer;
  iresvolumen: integer;
  //  coef_volumen:NReal;
begin
  if (self.suministroCombustible.get_VolCombustibleDisponible = 0) then
    exit;

  if NMaquinasDisponibles = 0 then
    exit; // si no hay máquinas no juego
{$IFDEF SPXCONLOG}
  spx_NombrarVariables(s);
{$ENDIF}

  inodores := nodo.ires;
  iresvolumen := self.suministroCombustible.get_ires_volumen();


  if (self.PMin = 0) then //Es generador basico
  begin
    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)
      s.pon_e(s.nf, ivar + iposte, -cv * globs.durpos[iposte]);
    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;


(***RCH esto lo paso para prepararPaso_ps

    //En el caso de existir un suministro de combustible, en la restriccion impuesta por este
    //para el balance del volumen, cada generador debe colocar sus términos que permitan calcular
    //el volumen consumido en función de la potencia despachada
    //Para ello se requiere de el rendimiento de la maquina, de los PCI, de la constante de
    //conversión a KWHora (en función del tipo de combustible)
    //el Termino a incluir será: 1/k_conversion * 1/rendimiento * duración del poste
    //Lo primero es obtener la fila correspondiente a la restricción:
    iresvolumen := self.suministroCombustible.get_ires_volumen();
    combustible := self.suministroCombustible.get_combustible();
    //La ficha tiene el consumo específico. Entonces el 1/rendimiento = consumo especifico
    //coef_volumen := (1/(pa.PMax-pa.PMin)) * (pa.PMax/pa.rendimiento_pmax - pa.PMin/pa.rendimiento_pmin) *  1/(combustible.kConversion*combustible.PCI);

//Pongo los terminos para calcular el volumen
    coef_volumenMayMin :=
      1 / (pa.rendimiento_pmax * combustible.kConversion * combustible.PCI);
***)


    for iposte := 0 to globs.NPostes - 1 do
    begin
      if self.suministroCombustible.restricciones_postizadas() then
        s.pon_e(iresvolumen + iposte, ivar + iposte,
          globs.durpos[iposte] * coef_volumenMayMin)
      //Volumen despachado por encima del mínimo técnico en el poste(Ptotal-Pmin)
      else
        s.pon_e(iresvolumen, ivar + iposte, globs.durpos[iposte] *
          coef_volumenMayMin);
      //Volumen despachado por encima del mínimo técnico(Ptotal-Pmin)
    end;
  end
  else //Es un generador con mínimo técnico
  begin
    // 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;


(***RCH esto lo paso para prepararPaso_ps

    //En el caso de existir un suministro de combustible, en la restriccion impuesta por este
    //para el balance del volumen, cada generador debe colocar sus términos que permitan calcular
    //el volumen consumido en función de la potencia despachada
    //Para ello se requiere de el rendimiento de la maquina, de los PCI, de la constante de
    //conversión a KWHora (en función del tipo de combustible)
    //el Termino a incluir será: 1/k_conversion * 1/rendimiento * duración del poste
    //Lo primero es obtener la fila correspondiente a la restricción:
    iresvolumen := self.suministroCombustible.get_ires_volumen();
    combustible := self.suministroCombustible.get_combustible();
    //La ficha tiene el consumo específico. Entonces el 1/rendimiento = consumo especifico

    coef_volumenPMin :=
      pa.PMin / (pa.rendimiento_pmin * combustible.kConversion * combustible.PCI);

    if (pa.PMax - pa.PMin) = 0 then
      coef_volumenMayMin := coef_volumenPMin
    else
      coef_volumenMayMin :=
        (1 / (pa.PMax - pa.PMin)) * (pa.PMax / pa.rendimiento_pmax - pa.PMin / pa.rendimiento_pmin) *
        1 / (combustible.kConversion * combustible.PCI);

****)


    for iposte := 0 to globs.NPostes - 1 do
    begin
      if self.suministroCombustible.restricciones_postizadas() then
      begin
        //Pongo los terminos para calcular el volumen por encima del mínimo técnico
        s.pon_e(iresvolumen + iposte, ivar + iposte,
          globs.durpos[iposte] * coef_volumenMayMin);
        //Volumen despachado para el mínimo técnico
        s.pon_e(iresvolumen + iposte, ivar + globs.NPostes + iposte,
          globs.durpos[iposte] * volumenPMin);
      end
      else
      begin
        //Pongo los terminos para calcular el volumen por encima del mínimo técnico
        s.pon_e(iresvolumen, ivar + iposte, globs.durpos[iposte] *
          coef_volumenMayMin);
        //Volumen despachado para el mínimo técnico
        s.pon_e(iresvolumen, ivar + globs.NPostes + iposte,
          globs.durpos[iposte] * volumenPMin);
      end;

      //Si no hay combustible no deja generar nada optimización
      //if self.suministroCombustible.get_VolCombustibleDisponible=0 then begin //no va a poder consumir nada
      //     s.FijarVariable(ivar+iposte, 0); //No puedo generar nada
      //     s.FijarVariable(ivar+globs.NPostes + iposte, 0); //No puedo generar nada
      //end;
    end;

    //Pongo el aporte para el mínimo técnico

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

end;

procedure TGTer_ConectableASuministro.opt_fijarRestriccionesDeCaja(s: TSimplex);
var
  iposte: integer;
begin
  if (self.suministroCombustible.get_VolCombustibleDisponible = 0) then
    exit;

  if NMaquinasDisponibles = 0 then
    exit;
  (*
    // 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 );
  *)

  if (self.PMin = 0) then //Es generador basico
  begin
    // Le fijamos como cota superior PMax a la potencia en todos los postes
    for iposte := 0 to globs.NPostes - 1 do
      s.cota_sup_set(ivar + iposte, PMaxDisponible);

  end
  else //Es un generador con mínimo técnico
  begin
    // Restricciones de caja de las A y las declaramos enteras
    for iposte := 0 to globs.NPostes - 1 do
      TMIPSimplex(s).set_EnteraConAcople(
        ivae + iposte,
        ivar + globs.NPostes + iposte, NMaquinasDisponibles,
        ivar + iposte, ires + iposte);
  end;
end;

procedure TGTer_ConectableASuministro.opt_leerSolucion(s: TSimplex);
var
  iposte: integer;
  nmaqs: integer;
  m, me, mc: NReal;
begin

  CostoDirectoDelPaso := 0;
  maxNMaquinasDespachadas := 0;
  vclear(ConsumoXPoste);


  if ((self.suministroCombustible.get_VolCombustibleDisponible = 0) or
    (NMaquinasDisponibles = 0)) then
  begin
    vclear(P);
    vclear(costosPorPoste);
    vclear(NMaquinasDespachadas);
    PagoPorPotenciaDisponible := 0;
    exit;
  end;

  if (self.PMin = 0) then //Es generador basico
  begin
    for iposte := 0 to globs.NPostes - 1 do
    begin
      P[iposte] := s.xval(ivar + iposte);
      if P[iposte] > GTER_PRECISIONPOTENCIANMAQS then
      begin
        NMaquinasDespachadas[iposte] :=
          Math.Ceil((P[iposte] - GTER_PRECISIONPOTENCIANMAQS) / pa.PMax);
        //if suministroCombustible<>NIL then //Calcula el volúmen consumido por paso según rendimiento
        ConsumoXPoste[iposte] :=
          P[iposte] * coef_volumenMayMin * globs.DurPos[iposte];

        me := P[iposte] * globs.DurPos[iposte];
        mc := me * (cv + cve);
        costosPorPoste[iposte] := mc;
        CostoDirectoDelPaso := CostoDirectoDelPaso + mc;

        if NMaquinasDespachadas[iposte] > maxNMaquinasDespachadas then
          maxNMaquinasDespachadas := NMaquinasDespachadas[iposte];
      end
      else
      begin
        NMaquinasDespachadas[iposte] := 0;
        costosPorPoste[iposte] := 0;
      end;
    end;

    PagoPorPotenciaDisponible :=
      self.PMaxDisponible * globs.HorasDelPaso * pa.PagoPorPotencia;
    CostoDirectoDelPaso := CostoDirectoDelPaso + PagoPorPotenciaDisponible;
  end
  else //Es un generador con mínimo técnico
  begin
    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;
      ConsumoXPoste[iposte] :=
        (nmaqs * volumenPMin + m * coef_volumenMayMin) * globs.DurPos[iposte];

      CostoDirectoDelPaso :=
        CostoDirectoDelPaso + (m * cv + nmaqs * C0) * globs.DurPos[iposte];
      if nmaqs > maxNMaquinasDespachadas then
        maxNMaquinasDespachadas := nmaqs;

      if NMaquinasDespachadas[iposte] > 0 then
        costosPorPoste[iposte] :=
          ((P[iposte] - PMin * NMaquinasDespachadas[iposte]) * cv +
          NMaquinasDespachadas[iposte] * c0) * globs.durpos[iposte]
      else
        costosPorPoste[iposte] := 0;
    end;
  end;
end;


function TGTer_ConectableASuministro.InterpolacionLineal(x, x1, x2,
  y1, y2: double): double;
begin
  if x2 <> x1 then
    Result := (((x - x1) / (x2 - x1)) * (y2 - y1)) + y1
  else
    Result := y1;
end;



{$IFDEF SPXCONLOG}
procedure TGTer_ConectableASuministro.spx_NombrarVariables(s: TSimplex);
var
  iposte: integer;
begin
  if (self.suministroCombustible.get_VolCombustibleDisponible = 0) then
  begin
    exit;
  end;
  if NMaquinasDisponibles = 0 then
    exit;

  if (self.PMin = 0) then //Es generador basico
  begin
    for iposte := 0 to globs.NPostes - 1 do
      s.set_NombreVar(ivar + iposte, Nombre + '_P[MW]' + IntToStr(iposte + 1));

    if pa.HayRestriccionEmaxPasoDeTiempo then
      s.set_NombreRest(ires, nombre + '_res-EMax');
  end
  else //Es un generador con mínimo técnico
  begin
    for iposte := 0 to globs.NPostes - 1 do
    begin
      s.set_NombreVar(ivar + iposte, Nombre + '_P[MW]' + IntToStr(iposte + 1));
      s.set_NombreVar(ivar + iposte + globs.NPostes, Nombre + '_A' +
        IntToStr(iposte + 1));
      s.set_NombreRest(ires + iposte, Nombre + '_res-A' + IntToStr(iposte + 1));
    end;
    if pA.HayRestriccionEmaxPasoDeTiempo then
      s.set_NombreRest(ires + globs.NPostes, Nombre + '_res-EMax');
  end;
end;

{$ENDIF}

function TGTer_ConectableASuministro.getNombreVar(ivar: integer;
  var nombre: string): boolean;
begin
  if (self.suministroCombustible.get_VolCombustibleDisponible = 0) then
  begin
    Result := False;
    exit;
  end;
  if NMaquinasDisponibles = 0 then
    Result := False
  else
  begin
    if (self.PMin = 0) then //Es generador basico
    begin
      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
        Result := False;

    end
    else //Es un generador con mínimo técnico
    begin
      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;
  end;
end;

function TGTer_ConectableASuministro.getNombreRes(ires: integer;
  var nombre: string): boolean;
begin
  if (self.suministroCombustible.get_VolCombustibleDisponible = 0) then
  begin
    Result := False;
    exit;
  end;
  if NMaquinasDisponibles = 0 then
    Result := False
  else
  begin
    if (self.PMin = 0) then //Es generador basico
    begin
      if pa.HayRestriccionEmaxPasoDeTiempo and (ires = self.ires) then
      begin
        nombre := self.nombre + '_res-EMax';
        Result := True;
      end
      else
        Result := False;
    end
    else //Es un generador con mínimo técnico
    begin
      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;
  end;
end;

procedure TGTer_ConectableASuministro.PubliVars;
begin
  inherited PubliVars;

  //PublicarVariableVR('P', '[MW]', 6, 1, P, true, true);
  PublicarVariableVR('Costo', '[USD]', 6, 1, costosPorPoste, True, True);
  PublicarVariableVI('NMaqsDespachadas', '-', NMaquinasDespachadas, True, True);
  PublicarVariableNR('cvm', '[USD/MWh]', 6, 2, cvm, True);

  PublicarVariableNR('PPD', '[USD]', 6, 2, PagoPorPotenciaDisponible, True);
  PublicarVariableNI('MaxNMaqsDespachadas', '-', maxNMaquinasDespachadas, False);
  PublicarVariableNR('c0', '[USD/h]', 6, 1, c0, False);
  PublicarVariableNI('NMaqsDisponibles', '-', NMaquinasDisponibles, False);

  PublicarVariableNR('PMaxDisponible', '[MW]', 6, 1, PMaxDisponible, False);
  PublicarVariableNR('PMediaDespachada', '[MW]', 6, 1, potMedia_despachada, False);

  PublicarVariableVR('VConsumido', self.suministroCombustible.combustible.Unidades,
    6, 1, self.ConsumoXPoste, True, True);
end;




procedure TGTer_ConectableASuministro.Free;
begin
  setlength(P, 0);
  setlength(NMaquinasDespachadas, 0);
  SetLength(costosPorPoste, 0);
  inherited Free;
end;

procedure AlInicio;
begin
  registrarClaseDeCosa(TGTer_ConectableASuministro.ClassName,
    TGTer_ConectableASuministro);
  registrarClaseDeCosa(TFichaGTer_ConectableASuministro.ClassName,
    TFichaGTer_ConectableASuministro);
end;

procedure AlFinal;
begin
end;

end.
