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

interface

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

resourcestring
  rsGeneradorTermicoCombinado = 'Generador térmico combinado';

type

  { TFichaGTer_combinado }

  TFichaGTer_combinado = class(TFichaLPD)

  public

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

    //Atributos para las TurbinaGas
    PMin_TG, PMax_TG: NReal; // [MW] Potencias Mínima y Máxima Por maquina
    cv_min_TG, cv_TG: NReal; // Costo:= cv_min* Pmin+ cv* (P-Pmin)
    cv_NoCombustible_TG: NReal;
    disp_TG: NReal; // disponibilidad (fortuita)
    tRepHoras_TG: NReal;
    //Atributos para las TurboVapor
    PMin_TV, PMax_TV: NReal; // [MW] Potencias Mínima y Máxima Por maquina
    cv_min_TV, cv_TV: NReal; // Costo:= cv_min* Pmin+ cv* (P-Pmin)
    cv_NoCombustible_TV: NReal;
    disp_TV: NReal; // disponibilidad (fortuita)
    tRepHoras_TV: NReal;
    flg_TV_OnOffPorPaso: boolean; //La tv es ON/Off POr Paso si es false es por poste
    factorPotenciaTVdivTG: NReal; //factor de potencia en el que funciona el CC
    indicePreciosPorCombustible: TFuenteAleatoria;
    bornePreciosPorCombustible: string;
    HayRestriccionEmaxPasoDeTiempo: boolean; // indica si se aplica la restricción
    EmaxPasoDeTiempo: NReal; // Energía maxima generable en un paso de tiempo
    PagoPorDisponibilidad_USD_MWh: NReal;
    PagoPorEnergia_USD_MWh: NReal;


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

    nroBornePreciosPorCombustible: integer;

    constructor Create(capa: integer; fecha: TFecha; periodicidad: TPeriodicidad;
      PMin_TG, PMax_TG: NReal; cv_min_TG, cv_TG, cv_NoCombustible_TG: NReal;
      disp_TG: NReal; tRepHoras_TG: NReal; PMin_TV, PMax_TV: NReal;
      cv_min_TV, cv_TV, cv_NoCombustible_TV: NReal; disp_TV: NReal;
      tRepHoras_TV: NReal; flg_TV_OnOffPorPaso: boolean;
      factorPotenciaTVdivTG: NReal; indicePreciosPorCombustible: TFuenteAleatoria;
      bornePreciosPorCombustible: string; HayRestriccionEmaxPasoDeTiempo: boolean;
      EmaxPasoDeTiempo: NReal;
      PagoPorDisponibilidad_USD_MWh, 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;


  published

  end;


  { TGTer_combinado }

  TGTer_combinado = class(TGTer)
  public
    pa: TFichaGTer_combinado;

    NMaquinasDespachadasTG, NMaquinasDespachadasTV: TDAOfNInt;
    // cantidad de máquinas despachadas por poste o por paso ( Acople )
    costosPorPoste: TDAofNReal;
    costosPorPaso: NReal;


    c0TG, c0TV: NReal;
    //Costo en USD/h por máquina por estar operando en el mínimo técnico
    cvTG, cvTV: NReal; //USD/MWh usado para la optimización del paso.

    PMaxTG: NReal;
    PMinTG: NReal; //MW, mínimo
    PMinTV: NReal; // MW, minimo de las TV
    BMaxTG, BMaxTV: NReal; // MW, cota sup de P-Pmin


    // resultado de los sorteos de disponibilidad y del mantenimiento programado
    NMaquinasDisponiblesTG, NMaquinasDisponiblesTV: integer;
    PMaxDisponible_TG, PMaxDisponible_TV: NReal;
    PMaxDisponible_Central: NReal;

    // indices a los tramos de variables
    ivar_BTG, ivar_BTV, ivar_ATG, ivar_ATV: integer;

    // indices a las restricciones
    jres_Acople_BTG, jres_Acople_BTV, jres_Acople_TVxTG, jres_EMaxPasoDeTiempo: integer;
    jres_Ultima: integer;

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


    constructor Create(capa: integer; nombre: string;
      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_combinado
//==============================
constructor TFichaGTer_combinado.Create(capa: integer; fecha: TFecha;
  periodicidad: TPeriodicidad; PMin_TG, PMax_TG: NReal;
  cv_min_TG, cv_TG, cv_NoCombustible_TG: NReal; disp_TG: NReal;
  tRepHoras_TG: NReal; PMin_TV, PMax_TV: NReal;
  cv_min_TV, cv_TV, cv_NoCombustible_TV: NReal; disp_TV: NReal;
  tRepHoras_TV: NReal; flg_TV_OnOffPorPaso: boolean; factorPotenciaTVdivTG: NReal;
  indicePreciosPorCombustible: TFuenteAleatoria; bornePreciosPorCombustible: string;
  HayRestriccionEmaxPasoDeTiempo: boolean; EmaxPasoDeTiempo: NReal;
  PagoPorDisponibilidad_USD_MWh, PagoPorEnergia_USD_MWh: NReal);

begin
  inherited Create(capa, fecha, periodicidad);

  self.PMin_TG := PMin_TG;
  self.PMax_TG := PMax_TG;
  self.cv_min_TG := cv_min_TG;
  self.cv_TG := cv_TG;
  self.cv_NoCombustible_TG := cv_NoCombustible_TG;
  self.disp_TG := disp_TG;
  self.tRepHoras_TG := tRepHoras_TG;

  self.disp_TV := disp_TV;
  self.tRepHoras_TV := tRepHoras_TV;

  self.PMin_TV := PMin_TV;
  self.PMax_TV := PMax_TV;
  self.cv_min_TV := cv_min_TV;
  self.cv_TV := cv_TV;
  self.cv_NoCombustible_TV := cv_NoCombustible_TV;
  self.flg_TV_OnOffPorPaso := flg_TV_OnOffPorPaso;

  self.factorPotenciaTVdivTG := factorPotenciaTVdivTG;

  self.indicePreciosPorCombustible := indicePreciosPorCombustible;
  self.bornePreciosPorCombustible := bornePreciosPorCombustible;
  self.HayRestriccionEmaxPasoDeTiempo := HayRestriccionEmaxPasoDeTiempo;
  self.EmaxPasoDeTiempo := EmaxPasoDeTiempo;
  self.PagoPorDisponibilidad_USD_MWh := PagoPorDisponibilidad_USD_MWh;
  self.PagoPorEnergia_USD_MWh := PagoPorEnergia_USD_MWh;

end;

function TFichaGTer_combinado.Rec: TCosa_RecLnk;
begin
  Result:=inherited Rec;
    Result.addCampoDef('PMin_TG', PMin_TG);
    Result.addCampoDef('PMax_TG', PMax_TG);
    Result.addCampoDef('cv_min_TG', cv_min_TG);
    Result.addCampoDef('cv_TG', cv_TG);
    Result.addCampoDef('cv_NoCombustible_TG', cv_NoCombustible_TG, 111);
    Result.addCampoDef('disp_TG', disp_TG);
    Result.addCampoDef('tRepHoras_TG', tRepHoras_TG);
    Result.addCampoDef('disp_TV', disp_TV, 0, 98 );
    Result.addCampoDef('tRepHoras_TV', tRepHoras_TV, 0, 98 );
    Result.addCampoDef('PMin_CC', PMin_TV, 0, 98 );
    Result.addCampoDef('PMax_CC', PMax_TV, 0, 98 );
    Result.addCampoDef('cv_min_CC', cv_min_TV, 0, 98 );
    Result.addCampoDef('cv_CC', cv_TV, 0, 98 );
    Result.addCampoDef('PMin_TV', PMin_TV, 98 );
    Result.addCampoDef('PMax_TV', PMax_TV, 98 );
    Result.addCampoDef('cv_min_TV', cv_min_TV, 98 );
    Result.addCampoDef('cv_TV', cv_TV, 98 );
    Result.addCampoDef('cv_NoCombustible_TV', cv_NoCombustible_TV, 111 );
    Result.addCampoDef('disp_TV', disp_TV, 98 );
    Result.addCampoDef('tRepHoras_TV', tRepHoras_TV, 98 );
    Result.addCampoDef('flg_TV_OnOffPorPaso', flg_TV_OnOffPorPaso, 113 );
    Result.addCampoDef('factorPotenciaTVdivTG', factorPotenciaTVdivTG);
    Result.addCampoDef_ref('indicePreciosPorCombustible',  TCosa(indicePreciosPorCombustible), self);
    Result.addCampoDef('bornePreciosPorCombustible', bornePreciosPorCombustible);
    Result.addCampoDef('HayRestriccionEmaxPasoDeTiempo', HayRestriccionEmaxPasoDeTiempo);
    Result.addCampoDef('EmaxPasoDeTiempo', EmaxPasoDeTiempo);
    Result.addCampoDef('PagoPorDisponibilidad_USD_MWh', PagoPorDisponibilidad_USD_MWh, 98 );
    Result.addCampoDef('PagoPorEnergia_USD_MWh', PagoPorEnergia_USD_MWh, 98 );
end;

procedure TFichaGTer_combinado.BeforeRead(version, id_hilo: integer);
begin
  inherited BeforeRead(version, id_hilo);
  flg_TV_OnOffPorPaso := False;
  cv_NoCombustible_TG := 0.0;
  cv_NoCombustible_TV := 0.0;
end;

procedure TFichaGTer_combinado.AfterRead(f:TArchiTexto);
begin
  inherited AfterRead(f);
    if f.version < 113 then
      flg_TV_OnOffPorPaso := False;
    if f.Version < 98 then
    begin
      PagoPorDisponibilidad_USD_MWh := 0;
      PagoPorEnergia_USD_MWh := 0;
    end;
    if f.Version < 98 then
    begin
      PagoPorDisponibilidad_USD_MWh := 0;
      PagoPorEnergia_USD_MWh := 0;
    end;

end;



procedure TFichaGTer_combinado.generarLineaResumen(var archi: TextFile);
begin
  Write(archi, FloatToStrF(PMin_TG, formatoReales, 8, 1), #9,
    //PMín_TG
    FloatToStrF(PMax_TG, formatoReales, 8, 1), #9,
    //PMáx_TG
    FloatToStrF(cv_min_TG, formatoReales, 8, 2), #9,
    //CV_Mín_TG
    FloatToStrF(((cv_min_TG * PMin_TG + cv_TG * (PMax_TG - PMin_TG)) / PMax_TG),
    formatoReales, 8, 2),
    #9,    //CV_Medio_TG
    FloatToStrF(cv_TG, formatoReales, 8, 2), #9,
    //CV_Incremental_TG
    FloatToStrF(disp_TG, formatoReales, 8, 2), #9,
    //FDisp_TG
    '-', #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_combinado.infoAd_: string;
begin
  Result := 'PMín_TG= ' + FloatToStrF(PMin_TG, ffGeneral, 10, 1) +
    ' MW, ' + 'PMáx_TG= ' + FloatToStrF(PMax_TG, ffGeneral, 10, 1) +
    ' MW, ' + 'cv_min_TG= ' + FloatToStrF(cv_min_TG, ffGeneral, 10, 1) +
    ' USD/MWh, ' + 'cv_TG= ' + FloatToStrF(cv_TG, ffGeneral, 10, 1) +
    ' USD/MWh, ' + 'fDisp_TG= ' + FloatToStrF(disp_TG, ffGeneral, 10, 2) +
    ' p.u., ' + 'tRep_TG= ' + FloatToStrF(tRepHoras_TG, ffGeneral, 10, 1) + 'h';
end;

destructor TFichaGTer_combinado.destroy;
begin
  inherited destroy;
end;

//------------------------
// Métodos de TGTer_Combinado
//========================
procedure TGTer_combinado.dump_Variables(var f: TextFile; charIndentacion: char);
begin
  inherited dump_Variables(f, charIndentacion);
  writeln(f, charIndentacion, 'c0TG[USD/MWh]= ', FloatToStrF(c0TG, ffFixed, 10, 3));
  writeln(f, charIndentacion, 'cvTG[USD/MWh]= ', FloatToStrF(cvTG, ffFixed, 10, 3));
  writeln(f, charIndentacion, 'PMinTG[MW]= ', FloatToStrF(pa.PMin_TG, ffFixed, 10, 3));
  writeln(f, charIndentacion, 'PMAxTG[MW]= ', FloatToStrF(pa.PMax_TG, ffFixed, 10, 3));
  writeln(f, charIndentacion, 'BMaxTG[MW]= ', FloatToStrF(BMaxTG, ffFixed, 10, 3));

  writeln(f, charIndentacion, 'PMaxDisponible_Central[MW]= ',
    FloatToStrF(PMaxDisponible_Central, ffFixed, 10, 3));

  writeln(f, charIndentacion, 'NMaquinasDisponiblesTG= ', NMaquinasDisponiblesTG);
  writeln(f, charIndentacion, 'NMaquinasDisponiblesTV= ', NMaquinasDisponiblesTV);

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

  writeln(f);
end;

class function TGTer_combinado.TipoFichaLPD: TClaseDeFichaLPD;
begin
  Result := TFichaGTer_combinado;
end;


constructor TGTer_combinado.Create(capa: integer; nombre: string; 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_combinado.Rec: TCosa_RecLnk;
begin
  Result:=inherited Rec;
end;

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

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

procedure TGTer_combinado.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(NMaquinasDespachadasTG, globs.NPostes);
  SetLength(NMaquinasDespachadasTV, globs.NPostes);

end;

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

function TGTer_combinado.PotenciaFirme: NReal;
var
  PTG, PTVa, PTVb: NReal;
begin
  PTG := paUnidades.nUnidades_Operativas[0] * pa.PMax_TG * pa.disp_TG;
  PTVa := paUnidades.nUnidades_Operativas[1] * pa.PMax_TV * pa.disp_TV;
  PTVb := PTG * pa.factorPotenciaTVdivTG;
  Result := PTG + min(PTVb, PTVa);
end;

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

class function TGTer_combinado.DescClase: string;
begin
  Result := rsGeneradorTermicoCombinado;
end;



function TGTer_combinado.get_pa_FD(kTipoUnidad: integer): NReal;
begin
  if kTipoUnidad = 0 then
    Result := pa.disp_TG
  else
    Result := pa.disp_TV;
end;

function TGTer_combinado.get_pa_TMR(kTipoUnidad: integer): NReal;
begin
  if kTipoUnidad = 0 then
    Result := pa.tRepHoras_TG
  else
    Result := pa.tRepHoras_TV;
end;


procedure TGTer_combinado.SorteosDelPaso(sortear: boolean);
begin
  if hayForzamientos or globs.ObligarDisponibilidad_1_ then
  begin
    NMaquinasDisponiblesTG := paUnidades.nUnidades_Operativas[0];
    NMaquinasDisponiblesTV := paUnidades.nUnidades_Operativas[1];
    PMaxDisponible_TG := pa.PMax_TG * NMaquinasDisponiblesTG;
    PMaxDisponible_TV := min(pa.PMax_TV * NMaquinasDisponiblesTV,
      PMaxDisponible_TG * pa.factorPotenciaTVdivTG);
  end
  else
  if sortear then
  begin
    ActualizarProbabilidadesReparacionYRotura_(pa.disp_TG, pa.tRepHoras_TG, 0);
    NMaquinasDisponiblesTG := Sorteos_RepRotUnidades_k(0);
    ActualizarProbabilidadesReparacionYRotura_(pa.disp_TV, pa.tRepHoras_TV, 1);
    NMaquinasDisponiblesTV := Sorteos_RepRotUnidades_k(1);
    PMaxDisponible_TG := pa.PMax_TG * NMaquinasDisponiblesTG;
    PMaxDisponible_TV := min(pa.PMax_TV * NMaquinasDisponiblesTV,
      PMaxDisponible_TG * pa.factorPotenciaTVdivTG);
  end
  else
  begin
    NMaquinasDisponiblesTG := paUnidades.nUnidades_Operativas[0];
    NMaquinasDisponiblesTV := paUnidades.nUnidades_Operativas[1];
    PMaxDisponible_TG := pa.PMax_TG * NMaquinasDisponiblesTG * pa.disp_TG;
    PMaxDisponible_TV := min(pa.PMax_TV * NMaquinasDisponiblesTV,
      PMaxDisponible_TG * pa.factorPotenciaTVdivTG) * pa.disp_TV;
  end;
  PMaxDisponible_Central := PMaxDisponible_TG + PMaxDisponible_TV;
end;

procedure TGTer_combinado.PrepararPaso_ps;
var
  indice: NReal;
  Pasosim: integer;
begin
  PMinTG := pa.PMin_TG;
  BMaxTG := pa.PMax_TG - pa.PMin_TG;
  PMinTV := pa.PMin_TV;
  BMaxTV := pa.PMax_TV - pa.PMin_TV;

  if pa.indicePreciosPorCombustible <> nil then
  begin
    indice := pa.indicePreciosPorCombustible.bornera[pa.nroBornePreciosPorCombustible];
    c0TG := pa.PMin_TG * (pa.cv_min_TG * indice + pa.cv_NoCombustible_TG);
    cvTG := pa.cv_TG * indice + pa.cv_NoCombustible_TG;
    c0TV := pa.PMin_TV * (pa.cv_min_TV * indice + pa.cv_NoCombustible_TV);
    cvTV := pa.cv_TV * indice + pa.cv_NoCombustible_TV;
  end
  else
  begin
    c0TG := pa.PMin_TG * (pa.cv_min_TG + pa.cv_NoCombustible_TG);
    cvTG := pa.cv_TG + pa.cv_NoCombustible_TG;
    c0TV := pa.PMin_TV * (pa.cv_min_TV + pa.cv_NoCombustible_TV);
    cvTV := pa.cv_TV + pa.cv_NoCombustible_TV;
  end;

end;

procedure TGTer_combinado.Sim_Paso_Fin;
var
  iposte: integer;
begin
  if NMaquinasDisponiblesTG > 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_combinado.opt_nvers(var ivar, ivae, ires: integer);
begin
  if NMaquinasDisponiblesTG = 0 then
    exit;

  Self.ivar := ivar;
  if pa.flg_TV_OnOffPorPaso then
    ivar := ivar + globs.NPostes * 3 + 1
  else
    ivar := ivar + globs.NPostes * 4;
  // Las variables de control son B_TG, A_TG, B_TV y A_TV en cada poste

  Self.ivae := ivae;

  if pa.flg_TV_OnOffPorPaso then
    ivae := ivae + globs.NPostes + 1
  else
    ivae := ivae + globs.NPostes * 2;
  // Las variables A_TG y A_TV son enteras


  Self.ires := ires;
  ires := ires + globs.NPostes * 3;
  // En cada poste están las restricciones de extinsión de B_TG < (PMax-PMin)*A_TG
  // y también que ( B_TV + PMin_TV ) < f_TvxTG * (B_TG + PMin_TG )

  ivar_BTG := self.ivar;
  ivar_ATG := ivar_BTG + globs.NPostes;
  ivar_BTV := ivar_ATG + globs.NPostes;
  ivar_ATV := ivar_BTV + globs.NPostes;

  jres_Acople_BTG := self.ires;
  jres_Acople_BTV := jres_Acople_BTG + globs.NPostes;
  jres_Acople_TVxTG := jres_Acople_BTV + globs.NPostes;

  if pA.HayRestriccionEmaxPasoDeTiempo then
  begin
    Inc(ires);
    jres_EMaxPasoDeTiempo := jres_Acople_TVxTG + globs.NPostes + 1;
    jres_Ultima := jres_EMaxPasoDeTiempo;
  end
  else
  begin
    jres_Ultima := jres_Acople_TVxTG + globs.NPostes;
  end;
end;

procedure TGTer_combinado.opt_cargue(s: TSimplex);
var
  inodores: integer;
  iposte: integer;
  jres: integer;

begin
  if NMaquinasDisponiblesTG = 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_BTG + iposte, 1); // coeficiente de la B[iposte] TG
    s.pon_e(inodores + iposte, ivar_ATG + iposte, PMinTG); // coef A TG
    s.pon_e(inodores + iposte, ivar_BTV + iposte, 1);   // coeficiente de la B[iposte] TV
    if pa.flg_TV_OnOffPorPaso then
      s.pon_e(inodores + iposte, ivar_ATV, PMinTV) // coef A TV
    else
      s.pon_e(inodores + iposte, ivar_ATV + iposte, PMinTV); // coef A TV
  end;


  //n:=NMaquinasDespachadas[iposte];
  for iposte := 0 to globs.NPostes - 1 do
  begin
    // coef B[iposte] de las TG
    s.pon_e(jres_Acople_BTG + iposte, ivar_BTG + iposte, -1);
    s.pon_e(jres_Acople_BTG + iposte, ivar_ATG + iposte, BMaxTG);

    // coef B[iposte] de las TV
    s.pon_e(jres_Acople_BTV + iposte, ivar_BTV + iposte, -1);
    if pa.flg_TV_OnOffPorPaso then
      s.pon_e(jres_Acople_BTV + iposte, ivar_ATV, BMaxTV)
    else
      s.pon_e(jres_Acople_BTV + iposte, ivar_ATV + iposte, BMaxTV);

    // restricción de acople de las centrales TG y TV
    if not hayForzamientos then
    begin
      s.pon_e(jres_Acople_TVxTG + iposte, ivar_BTG + iposte, pa.factorPotenciaTVdivTG);
      s.pon_e(jres_Acople_TVxTG + iposte, ivar_ATG + iposte, PminTG *
        pa.factorPotenciaTVdivTG);
    end
    else
    begin
      // relajamos la relación de potencia TV/TG  para que el usuario pueda poner el forzamiento y
      // no cumplir exactamente con la condicion
      s.pon_e(jres_Acople_TVxTG + iposte, ivar_BTG + iposte,
        pa.factorPotenciaTVdivTG * 1.3);
      s.pon_e(jres_Acople_TVxTG + iposte, ivar_ATG + iposte, PminTG *
        pa.factorPotenciaTVdivTG * 1.3);
    end;
    s.pon_e(jres_Acople_TVxTG + iposte, ivar_BTV + iposte, -1);
    if pa.flg_TV_OnOffPorPaso then
      s.pon_e(jres_Acople_TVxTG + iposte, ivar_ATV, -PMinTV)
    else
      s.pon_e(jres_Acople_TVxTG + iposte, ivar_ATV + iposte, -PMinTV);
  end;


  //Restriccion a la energía máxima generable en el paso
  if pA.HayRestriccionEmaxPasoDeTiempo then
  begin
    if pa.flg_TV_OnOffPorPaso then
      s.pon_e(jres_EMaxPasoDeTiempo, ivar_ATV, -globs.HorasDelPaso * pa.PMin_TV);


    for iposte := 0 to globs.NPostes - 1 do
    begin
      s.pon_e(jres_EMaxPasoDeTiempo, ivar_BTG, -globs.durpos[iposte]);
      s.pon_e(jres_EMaxPasoDeTiempo, ivar_ATG + iposte, -globs.durpos[iposte] *
        pa.PMin_TG);
      s.pon_e(jres_EMaxPasoDeTiempo, ivar_BTV + iposte, -globs.durpos[iposte]);

      if not pa.flg_TV_OnOffPorPaso then
        s.pon_e(jres_EMaxPasoDeTiempo, ivar_ATV + iposte, -globs.durpos[iposte] *
          pa.PMin_TV);
    end;
    s.pon_e(jres_EMaxPasoDeTiempo, s.nc, pa.EmaxPasoDeTiempo); //JFP y FB 30_1_17:
    //Antes cargaba el t.i en jres (variable local no inicializada)
    //lo cual entendemos estaba mal.
  end;

  // aportes a la función de utilidad

  if pa.flg_TV_OnOffPorPaso then
    s.pon_e(s.nf, ivar_ATV, -c0TV * globs.HorasDelPaso);

  for iposte := 0 to globs.NPostes - 1 do
  begin
    s.pon_e(s.nf, ivar_BTG + iposte, -cvTG * globs.DurPos[iposte]);
    s.pon_e(s.nf, ivar_ATG + iposte, -c0TG * globs.DurPos[iposte]);
    s.pon_e(s.nf, ivar_BTV + iposte, -cvTV * globs.DurPos[iposte]);
    if not pa.flg_TV_OnOffPorPaso then
      s.pon_e(s.nf, ivar_ATV + iposte, -c0TV * globs.DurPos[iposte]);
  end;
end;




procedure TGTer_combinado.opt_fijarRestriccionesDeCaja(s: TSimplex);
var
  iposte: integer;
  nTVs, nTGs: integer;

begin
  if NMaquinasDisponiblesTG = 0 then
    exit;

  if (not hayForzamientos) then
  begin
    if pa.flg_TV_OnOffPorPaso then
      s.set_entera(ivae + globs.NPostes, ivar_ATV, NMaquinasDisponiblesTV);

    for iposte := 0 to globs.NPostes - 1 do
    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
      s.cota_sup_set(ivar_BTG + iposte, BMaxTG * NMaquinasDisponiblesTG * 1.1);
      s.cota_sup_set(ivar_BTV + iposte, BMaxTV * NMaquinasDisponiblesTV * 1.1);

      // Restricciones de caja de las A y las declaramos enteras
      s.set_entera(ivae + iposte, ivar_ATG + iposte, NMaquinasDisponiblesTG);

      if not pa.flg_TV_OnOffPorPaso then
        s.set_entera(ivae + iposte + globs.NPostes, ivar_ATV + iposte,
          NMaquinasDisponiblesTV);
    end;
  end
  else
  begin

    nTGs := ceil(paForzamiento.P[0] / pa.PMax_TG);
    nTVs := ceil(paForzamiento.P[1] / pa.PMax_TV);
                       {
    for iposte := 0 to globs.NPostes - 1 do
    begin
      s.FijarVariable(ivar_BTG + iposte, paForzamiento.P[0] - pa.PMin_TG * nTGs);
      s.FijarVariable(ivar_BTV + iposte, paForzamiento.P[1] - pa.PMin_TV * nTVs);
    end;                }
       for iposte := 0 to globs.NPostes - 1 do
        begin
          if paForzamiento.flg_fijarP then
          begin
            s.FijarVariable(ivar_BTG + iposte, paForzamiento.P[0] - pa.PMin_TG * nTGs);
            s.FijarVariable(ivar_BTV + iposte, paForzamiento.P[1] - pa.PMin_TV * nTVs);
          end
          else if paForzamiento.activar_Pmax then
          begin
            s.cota_sup_set(ivar_BTG + iposte, paForzamiento.P_max[0] - pa.PMin_TG * nTGs);
            s.cota_sup_set(ivar_BTV + iposte, paForzamiento.P_max[1] - pa.PMin_TV * nTVs);
          end
          else if paForzamiento.activar_Pmin then
          begin
            s.cota_inf_set(ivar_BTG + iposte, paForzamiento.P_min[0] - pa.PMin_TG * nTGs);
            s.cota_inf_set(ivar_BTV + iposte, paForzamiento.P_min[1] - pa.PMin_TV * nTVs);
          end
        end;

    if pa.flg_TV_OnOffPorPaso then
    begin
      s.set_entera(ivae + globs.NPostes, ivar_ATV, nTVs);
      s.FijarVariable(ivar_ATV, nTVs);
    end;

    for iposte := 0 to globs.NPostes - 1 do
    begin
      s.set_entera(ivae + iposte, ivar_ATG + iposte, nTGs);
      s.FijarVariable(ivar_ATG + iposte, nTGs);
      if not pa.flg_TV_OnOffPorPaso then
      begin
        s.set_entera(ivae + iposte + globs.NPostes, ivar_ATV + iposte, nTVs);
        s.FijarVariable(ivar_ATV + iposte, nTVs);
      end;
    end;
  end;
end;

procedure TGTer_combinado.opt_leerSolucion(s: TSimplex);
var
  iposte: integer;
  ATG, ATV: integer;
  BTV, BTG: NReal;
  PDelPoste: NReal;
  CostoDelPoste: NReal;
  EnergiaDespachada: NReal;
begin
  Ingreso_PorDisponibilidad_ :=
    PMaxDisponible_Central * pa.PagoPorDisponibilidad_USD_MWh * globs.HorasDelPaso;
  CostoDirectoDelPaso :=0;
  Ingreso_PorEnergia_:= 0;

  if NMaquinasDisponiblesTG = 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(NMaquinasDespachadasTG);
    vclear(NMaquinasDespachadasTV);
    exit;
  end;

  if pa.flg_TV_OnOffPorPaso then
    ATV := trunc(s.xval(ivar_ATV) + 0.2);

  EnergiaDespachada := 0;
  for iposte := 0 to globs.NPostes - 1 do
  begin
    ATG := trunc(s.xval(ivar_ATG + iposte) + 0.2);
    if not pa.flg_TV_OnOffPorPaso then
      ATV := trunc(s.xval(ivar_ATV + iposte) + 0.2);
    NMaquinasDespachadasTG[iposte] := ATG;
    NMaquinasDespachadasTV[iposte] := ATV;
    PDelPoste := 0;
    if ATG > 0 then
    begin
      BTG := s.xval(ivar_BTG + iposte);
      PDelPoste := PDelPoste + BTG + ATG * PMinTG;
    end
    else
      BTG := 0;

    if ATV > 0 then
    begin
      BTV := s.xval(ivar_BTV + iposte);
      PDelPoste := PDelPoste + BTV + ATV * PMinTV;
    end
    else
      BTV := 0;

    EnergiaDespachada := EnergiaDespachada + PDelPoste * globs.DurPos[iposte];
    P[iposte] := PDelPoste;

    //??? rch ojo , pensar esto me parece que no es el promedio... capaz que es el máximo
    //??? xc re_ojo, capaz que es la combinación lineal de los multiplicadores teniendo en cuenta
    //          la relación de generación entre TG y TV. Para aumentar 1 MW de TV tengo que aumentr x de TG...mmmm....
    Lambda_P[iPoste]:= mean([s.xmult( ivar_BTG + iposte ), s.xmult( ivar_ATV + iposte )]) / globs.DurPos[iposte];

    cv_Spot[iPoste]:= Nodo.cmarg[iposte] - Lambda_P[iPoste];

    if NMaquinasDespachadasTG[iposte] > 0 then
    begin
      CostoDelPoste := (BTG * cvTG + ATG * c0TG + BTV * cvTV + ATV * c0TV) *
        globs.DurPos[iposte];
      CostoDirectoDelPaso := CostoDirectoDelPaso + CostoDelPoste;
      costosPorPoste[iposte] := CostoDelPoste;
    end
    else
      costosPorPoste[iposte] := 0;
  end;
  Ingreso_PorEnergia_ := EnergiaDespachada * pa.PagoPorEnergia_USD_MWh;

end;


function TGTer_combinado.getNombreVar(ivar: integer; var nombre: string): boolean;
begin
  if (NMaquinasDisponiblesTG = 0) or (ivar < ivar_BTG) or
    (ivar >= (ivar_ATV + globs.NPOSTES)) then
    Result := False
  else
  begin
    if ivar < ivar_ATG then
      nombre := self.Nombre + '_B_TG[MW]' + IntToStr(ivar - ivar_BTG + 1)
    else if ivar < ivar_BTV then
      nombre := self.Nombre + '_A_TG' + IntToStr(ivar - ivar_ATG + 1)
    else if ivar < ivar_ATV then
      nombre := self.Nombre + '_B_TV[MW]' + IntToStr(ivar - ivar_BTV + 1)
    else
      nombre := self.Nombre + '_A_TV' + IntToStr(ivar - ivar_ATV + 1);
    Result := True;
  end;
end;

function TGTer_combinado.getNombreRes(ires: integer; var nombre: string): boolean;
begin
  if (NMaquinasDisponiblesTG = 0) or (ires < jres_Acople_BTG) or
    (ires > jres_Ultima) then
    Result := False
  else
  begin
    if ires < jres_Acople_BTV then
      nombre := self.nombre + '_res-A_TG' + IntToStr(ires - jres_Acople_BTG + 1)
    else if ires < jres_Acople_TVxTG then
      nombre := self.nombre + '_res-A_TV' + IntToStr(ires - jres_Acople_BTV + 1)
    else if (pa.HayRestriccionEmaxPasoDeTiempo) and
      (ires = jres_EMaxPasoDeTiempo) then
      nombre := self.nombre + '_res-EMax'
    else
      nombre := self.nombre + '_res-TV/TG' + IntToStr(ires - jres_Acople_TVxTG + 1);
    Result := True;
  end;
end;

procedure TGTer_combinado.PubliVars;
begin
  inherited PubliVars;
  PublicarVariableVR('Costo', '[USD]', 6, 1, costosPorPoste, True, True);
  PublicarVariableVI('NDespachadasTGs', '-', NMaquinasDespachadasTG, True, True);
  PublicarVariableVI('NDespachadasTVs', '-', NMaquinasDespachadasTV, True, True);

  {$IFDEF DECLARAR_VARIABLES_SIMRES_DEF}
  declararVarsPSimResPorDefectoIntercalandoPostes(['P', 'Costo', 'NMaqsDespachadas'],
    globs.NPostes);
  {$ENDIF}
  PublicarVariableNR('c0TG', '[USD/h]', 6, 1, c0TG, False);
  PublicarVariableNI('NMaqsDisponiblesTG', '-', NMaquinasDisponiblesTG, False);
  PublicarVariableNI('NMaqsDisponiblesTV', '-', NMaquinasDisponiblesTV, False);
  PublicarVariableNR('PMaxDisponible_Central', '[MW]', 6, 1,
    PMaxDisponible_Central, False);
  PublicarVariableNR('PMediaDespachada', '[MW]', 6, 1, potMedia_despachada, False);
end;

destructor TGTer_combinado.destroy;
begin
  setlength(NMaquinasDespachadasTG, 0);
  setlength(NMaquinasDespachadasTV, 0);
  SetLength(costosPorPoste, 0);
  inherited destroy;
end;


procedure AlInicio;
begin
  registrarClaseDeCosa(TGTer_combinado.ClassName, TGTer_combinado);
  registrarClaseDeCosa(TFichaGter_combinado.ClassName, TFichaGter_combinado);
end;

procedure AlFinal;
begin
end;

end.
