unit ugter_combinado_horario;

interface

{xDEFINE dbgc}
uses
  SysUtils, Classes, xmatdefs, uGTer, unodos,
  Math,
  uglobs,
  umipsimplex,
  usimplex,
  ufichasLPD,
  ufechas,
  ucosa, uCosaConNombre,
  ugter_combinado,
  uEstados,
  {$IFDEF INTERMEDIARIO_DE_COSTO_FUTURO}
  uintermediariocostofuturo,
  {$ENDIF}
  uconstantesSimSEE, uFuentesAleatorias;

resourcestring
  rsGeneradorTermicoCombinadoHorario = 'Generador térmico combinado horario';

type

  { TFichaGTer_combinado_horario }

  TFichaGTer_combinado_horario = class(TFichaGTer_combinado)

  public

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

    horasPurga: NReal;
    horasTVPleno: NReal;
    horasEnfriarCaldera: NReal;
    NDisc: integer;
    NDisc_cald: integer;
    costoComb: NReal;
    disp_cald: Nreal;
    tRepHoras_cald: NReal;

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

    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; factorPotenciaTVdivTG: NReal;
      indicePreciosPorCombustible: TFuenteAleatoria; bornePreciosPorCombustible: string;
      HayRestriccionEmaxPasoDeTiempo: boolean; EmaxPasoDeTiempo: NReal;
      PagoPorDisponibilidad_USD_MWh, PagoPorEnergia_USD_MWh: NReal;
      horasPurga: NReal; horasTVPleno: NReal; NDisc: integer; costoComb: NReal;
      disp_cald: NReal; tRepHoras_cald: NReal; horasEnfriarCaldera: NReal; NDisc_cald: integer);

    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_horario }

  TGTer_combinado_horario = class(TGTer)
  public

    pa: TFichaGTer_combinado_horario;
    EstadoTimer: NReal;
    EstadoCaldera: NReal;
    NDisc, NDisc_cald: integer;
    horasEnfriarCaldera: Nreal;
    dCFdX, dCFdX_inc, dCFdx_dec: TDAofNReal;
    // afectación del costo futuro por movimiento del estado.

    (******** Estas son las variables de estado ******)
    X_timer_: TDAofNReal;
    X_caldera_: TDAofNReal;
    // timer que empieza a correr cuando se cierra el ciclo al inicio del paso
    Xs_timer_: TDAofNReal;
    Xs_caldera_: TDAofNReal;
    // timer que empieza a correr cuando se cierra el ciclo al final del paso

    horasPurga_: NReal; //horas que demora en purgar antes de poder prender la TV
    horasTVPleno_: NReal; //horas que demora en llegar a pleno la TV

    costoComb: NReal; //costo por combinar el ciclo
   // disp_cald: NReal; //factor de disponibilidad de las calderas del ciclo
   // tRep

    umbralDespachoTV: NReal;
    factorTV_: TDAofNReal;   //factor TV/TG si se puede despachar TV o 0 sino

    //variable para saber en que parte del ciclo estoy
    //0 cuando se esta purgando, si purga_TV es 0 y X_timer es 0, el ciclo esta abierto
    //1 cuando se esta subiendo las TG con ciclo cerrado
    //2 cuando puedo estar a pleno
    purga_TV_: TDAofNInt;

    //variable para saber si arranca frio, tibio o caliente y con que pendiente de subida
    pendTV_: TDAofNReal;

    //_SC sin calentar (sin calentar calderas) _CC calentando caldera
    //divido la variable de la potencia de las TG en 2, para diferenciar
    //si se calienta o no la caldera
    BMaxTG_SC, BMaxTG_CC, BMaxTV: NReal; // MW, cota sup de P-Pmin
    PMaxTG: NReal;
    PMinTG: NReal; //MW, mínimo
    PMinTV: NReal; // MW, minimo de las TV

    NmaxTG: integer;
    NmaxCal: integer;
    NmaxTV: integer;

    NMaquinasDespachadasTG, NMaquinasDespachadasTV: integer;

    PdespachTG, PdespachTV: NReal;

    //pendiente con la que sube y baja la temperatura de la caldera
    //    pendTempSubida, pendTempBajada: NReal;
    pendTV_frio, pendTV_tibio, pendTV_caliente: NReal;

    c0TG, c0TV: NReal;
    horasCalienteCaldera: 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.
   // cAcople: NReal; //Costo por acoplar

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

    // indices a los tramos de variables
    ivar_ATG_SC, ivar_BTG_SC, ivar_ATG_CC, ivar_BTG_CC,
    ivar_ATV, ivar_BTV, ivar_AAcople, ivar_Xs_timer, ivar_Xs_caldera: integer;

    // indices a las restricciones
    jres_dinTimer, jres_Acople_BTG_SC, jres_Acople_BTG_CC,
    jres_Acople_BTV, jres_Acople_TVxTG, jres_Acople_TimerxBTG_SC,
    jres_Acople_TimerxBTG_CC, jres_extTimer, jres_EMaxPasoDeTiempo,
    jres_Ultima, jaux, jres_dinCaldera: 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; EstadoTimer: NReal; EstadoCaldera: NReal; NmaxTG, NmaxCal, NmaxTV: integer);


    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_nvers_TG_indisponible(var ivar, ivae, ires: integer);
    procedure opt_nvers_TV_indisponible(var ivar, ivae, ires: integer);
    procedure opt_nvers_combinado(var ivar, ivae, ires: integer);

    procedure opt_cargue(s: TSimplex); override;
    procedure opt_cargue_TG_indisponible(s: TSimplex);
    procedure opt_cargue_TV_indisponible(s: TSimplex);
    procedure opt_cargue_combinado(s: TSimplex);

    procedure opt_fijarRestriccionesDeCaja(s: TSimplex); override;
    procedure opt_fijarRestriccionesDeCaja_TG_indisponible(s: TSimplex);
    procedure opt_fijarRestriccionesDeCaja_TV_indisponible(s: TSimplex);
    procedure opt_fijarRestriccionesDeCaja_combinado(s: TSimplex);

    procedure opt_leerSolucion(s: TSimplex); override;
    procedure opt_leerSolucion_TG_indisponible(s: TSimplex);
    procedure opt_leerSolucion_TV_indisponible(s: TSimplex);
    procedure opt_leerSolucion_combinado(s: TSimplex);
    procedure ChequearYActualizarTimers;

    procedure Sim_Cronica_Inicio; override;

    function getNombreVar(ivar: integer; var nombre: string): boolean; override;
    function getNombreVar_TG_indisponible(ivar: integer; var nombre: string): boolean;
    function getNombreVar_TV_indisponible(ivar: integer; var nombre: string): boolean;
    function getNombreVar_combinado(ivar: integer; var nombre: string): boolean;

    function getNombreRes(ires: integer; var nombre: string): boolean; override;
    function getNombreRes_TG_indisponible(ires: integer; var nombre: string): boolean;
    function getNombreRes_TV_indisponible(ires: integer; var nombre: string): boolean;
    function getNombreRes_combinado(ires: integer; var nombre: string): boolean;

    destructor destroy; override;

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

    class function TipoFichaLPD: TClaseDeFichaLPD; override;

    procedure EvolucionarEstado; override;

    procedure PosicionarseEnEstrellita; override;
    procedure ActualizarEstadoGlobal(flg_Xs: boolean); override;

    procedure optx_nvxs(var ixr, ixd, iauxr, iauxd: integer); override;
    procedure optx_RegistrarVariablesDeEstado(adminEstados: TAdminEstados); override;

    {$IFDEF INTERMEDIARIO_DE_COSTO_FUTURO}
    procedure registrarVarYResEnIntermediario(intermediario: TIntermediarioCostoFuturo);
      override;
    {$ENDIF}


  end;

procedure AlInicio;
procedure AlFinal;

implementation

//------------------------------
// Métodos de TFichaGter_combinado_horario
//==============================
constructor TFichaGTer_combinado_horario.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; factorPotenciaTVdivTG: NReal;
  indicePreciosPorCombustible: TFuenteAleatoria;
  bornePreciosPorCombustible: string; HayRestriccionEmaxPasoDeTiempo: boolean;
  EmaxPasoDeTiempo: NReal; PagoPorDisponibilidad_USD_MWh,
  PagoPorEnergia_USD_MWh: NReal; horasPurga: NReal; horasTVPleno: NReal;
  NDisc: integer; costoComb: NReal; disp_cald: NReal; tRepHoras_cald: NReal;
  horasEnfriarCaldera: NReal; NDisc_cald: integer);

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;


  self.horasPurga := horasPurga;
  self.horasTVPleno := horasTVPleno;
  self.horasEnfriarCaldera := horasEnfriarCaldera;
  self.NDisc := NDisc;
  self.NDisc_cald := NDisc_cald;
  self.costoComb := costoComb;
  self.disp_cald := disp_cald;
  self.tRepHoras_cald := tRepHoras_cald;

end;

function TFichaGTer_combinado_horario.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);
  Result.addCampoDef('horasPurga', horasPurga, 189);
  Result.addCampoDef('horasTVPleno', horasTVPleno, 189);
  Result.addCampoDef('NDisc', NDisc, 189);
  Result.addCampoDef('costoComb', costoComb, 193);
  Result.addCampoDef('disp_cald', disp_cald, 193);
  Result.addCampoDef('tRepHoras_cald', tRepHoras_cald, 193);
  Result.addCampoDef('horasEnfriarCaldera', horasEnfriarCaldera, 200);
  Result.addCampoDef('NDisc_cald', NDisc_cald, 200);
end;

procedure TFichaGTer_combinado_horario.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_horario.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;

  if f.Version < 189 then
  begin
    horasPurga := 0;
    horasTVPleno := 0;
    NDisc := 0;
  end;

  if f.Version < 193 then
  begin
    costoComb := 0;
    disp_cald := 0;
    tRepHoras_cald := 0;
  end;

  if f.Version < 200 then
  begin
    horasEnfriarCaldera := 0;
    NDisc_cald := 0;
  end;

end;



procedure TFichaGTer_combinado_horario.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_horario.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_horario.destroy;
begin
  inherited destroy;
end;

//------------------------
// Métodos de TGTer_Combinado_horario
//========================
procedure TGTer_combinado_horario.dump_Variables(var f: TextFile; charIndentacion: char);
begin
  inherited dump_Variables(f, charIndentacion);
  writeln(f, charIndentacion, 'BMaxTG_SC[MW]= ', FloatToStrF(BMaxTG_SC, ffFixed, 10, 3));
  writeln(f, charIndentacion, 'BMaxTG_CC[MW]= ', FloatToStrF(BMaxTG_CC, ffFixed, 10, 3));
  writeln(f);
end;

class function TGTer_combinado_horario.TipoFichaLPD: TClaseDeFichaLPD;
begin
  Result := TFichaGTer_combinado_horario;
end;


procedure TGTer_combinado_horario.EvolucionarEstado;
var
  i: integer;
begin
  inherited EvolucionarEstado;
  for i := 0 to NmaxTG - 1 do
  begin
    X_timer_[i] := Xs_timer_[i];
    X_caldera_[i] := Xs_caldera_[i];
  end;
end;

procedure TGTer_combinado_horario.PosicionarseEnEstrellita;
var
  i: integer;
begin
  {$IFDEF CC_dbgx}
  if (globs.CF.ordinalEstrellaActual = 21) or (globs.CF.ordinalEstrellaActual = 57) then
     i := 0;
  {$ENDIF}
  for i := 0 to NmaxTG - 1 do
  begin
    X_timer_[i] := globs.CF.xr[ixr + i];
    X_caldera_[i] := globs.CF.xr[ixr + NmaxTG + i];

    Xs_timer_[i] := X_timer_[i];
    Xs_caldera_[i] := X_caldera_[i];
  end;
end;

procedure TGTer_combinado_horario.ActualizarEstadoGlobal(flg_Xs: boolean);
var
  i: integer;
begin
 {$IFDEF CC_dbg}
 if (globs.CF.ordinalEstrellaActual = 21) or (globs.CF.ordinalEstrellaActual = 57) then
    i := 0;
 {$ENDIF}
  if flg_Xs then
    for i := 0 to NmaxTG - 1 do
    begin
      globs.CF.xr[ixr + i] := Xs_timer_[i];
      globs.CF.xr[ixr + NmaxTG + i] := Xs_caldera_[i];
    end
  else
    for i := 0 to NmaxTG - 1 do
    begin
      globs.CF.xr[ixr + i] := X_timer_[i];
      globs.CF.xr[ixr + NmaxTG + i] := X_caldera_[i];
    end;
end;

procedure TGTer_combinado_horario.optx_nvxs(var ixr, ixd, iauxr, iauxd: integer);
begin
  inherited optx_nvxs(ixr, ixd, iauxr, iauxd);
  self.ixr := ixr;
  ixr := ixr + 2*NmaxTG;
end;

procedure TGTer_combinado_horario.optx_RegistrarVariablesDeEstado(
  adminEstados: TAdminEstados);
var
  i: integer;
begin

  for i := 0 to NmaxTG - 1 do
  begin
    adminEstados.Registrar_Continua(
      self.ixr + i,
      0,
      pa.horasPurga + pa.horasTVPleno - 1,
      pa.NDisc,
      nombre + '_timer_' + IntToStr(i), // nombre de la variable
      'h' // unidades
      );
   end;

  for i := 0 to NmaxTG - 1 do
  begin
  adminEstados.Registrar_Continua(
    self.ixr + NmaxTG + i,
    0,
    pa.horasEnfriarCaldera,
    pa.NDisc_cald,
    nombre + '_caldera_' + IntToStr(i), // nombre de la variable
    'h' // unidades
    );
  end;


end;
{$IFDEF INTERMEDIARIO_DE_COSTO_FUTURO}
procedure TGTer_combinado_horario.registrarVarYResEnIntermediario(
  intermediario: TIntermediarioCostoFuturo);
var
  kVariable: Integer;
  metodoDerivada_Timer: TMetodoDerivada;
  metodoDerivada_Caldera: TMetodoDerivada;
begin
  for kVariable := 0 to NmaxTG - 1 do
  begin

    metodoDerivada_Caldera := TMetodoDerivada.Create(DEVXR_CONTINUO, 1, 1, 1, 1);

    if X_timer_[kVariable] < ( horasPurga_ + horasTVPleno_ - 1 ) then
    begin
      metodoDerivada_Timer := TMetodoDerivada.Create(DELTACOSTO_VXR_CONTINUO, 1,  globs.HorasDelPaso, -1, - X_timer_[kVariable]);
    end
    else
      metodoDerivada_Timer := TMetodoDerivada.Create(DELTACOSTO_VXR_CONTINUO, 1, 1, -1, -5);
//    if (globs.kPaso_Opt = 94) then
//    begin
//       writeln('globs.kPaso_Opt' + #9 + 'NMaquinasDisponiblesTV' + #9 + IntToStr(NMaquinasDisponiblesTV));
//    end;

    intermediario.registrarVariableContinua( ixr + kVariable, ivar_Xs_timer +
    kVariable, jres_dinTimer + kVariable, nil, nombre + '_timer_' + IntToStr(kVariable),'h', metodoDerivada_Timer);

    intermediario.registrarVariableContinua( ixr + NmaxTG + kVariable,
      ivar_Xs_caldera + kVariable, jres_dinCaldera + kVariable, nil, nombre + '_caldera_' +
      IntToStr(kVariable), 'h', metodoDerivada_Caldera);
  end;

end;
{$ENDIF}

constructor TGTer_combinado_horario.Create(capa: integer; nombre: string;
  lpdUnidades, lpd: TFichasLPD; nodo: TNodo;
  flg_CalcularGradienteDeInversion: boolean; TonCO2xMWh: NReal; LowCostMustRun,
  CleanDevelopmentMechanism: boolean; xFuenteIdxP: TFuenteAleatoria;
  xBorneIdxP: string; EstadoTimer: NReal; EstadoCaldera: NReal; NmaxTG,
  NmaxCal, NmaxTV: integer);
begin
  inherited Create(capa, nombre, nacimiento, muerte, lpdUnidades, lpd, nodo,
    flg_CalcularGradienteDeInversion, TonCO2xMWh, LowCostMustRun,
    CleanDevelopmentMechanism,
    xFuenteIdxP, xBorneIdxP);
  self.EstadoTimer := EstadoTimer;
  self.EstadoCaldera := EstadoCaldera;
  self.NmaxTG := NmaxTG;
  self.NmaxCal := NmaxCal;
  self.NmaxTV := NmaxTV;
end;

function TGTer_combinado_horario.Rec: TCosa_RecLnk;
begin
  Result := inherited Rec;
  Result.addCampoDef('EstadoTimer', EstadoTimer, 190);
  Result.addCampoDef('EstadoCaldera', EstadoCaldera, 200);
  Result.addCampoDef('NmaxTG', NmaxTG, 200);
  Result.addCampoDef('NmaxCal', NmaxCal, 200);
  Result.addCampoDef('NmaxTV', NmaxTV, 200);
end;

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

procedure TGTer_combinado_horario.AfterRead(f: TArchiTexto);
begin
  inherited AfterRead(f);
end;

procedure TGTer_combinado_horario.PrepararMemoria(Catalogo: TCatalogoReferencias;
  globs: TGlobs);
begin
  inherited prepararMemoria(Catalogo, globs);
  NMaquinasDespachadasTG := 0;
  NMaquinasDespachadasTV := 0;
 // NmaxTG := 2; //paUnidades.nUnidades_Operativas[0]; //PRUEBA
  SetLength(X_timer_, NmaxTG);
  SetLength(Xs_timer_, NmaxTG);
  SetLength(X_caldera_, NmaxTG);
  SetLength(Xs_caldera_, NmaxTG);
  SetLength(factorTV_, NmaxTG);
  SetLength(purga_TV_, NmaxTG);
  SetLength(pendTV_, NmaxTG);
  SetLength(dCFdX, 2*NmaxTG);
  SetLength(dCFdx_dec, 2*NmaxTG);
  SetLength(dCFdX_inc, 2*NmaxTG);
end;

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

function TGTer_combinado_horario.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_horario.InfoAd_: string;
begin
  Result := '';
end;

class function TGTer_combinado_horario.DescClase: string;
begin
  Result := rsGeneradorTermicoCombinadoHorario;
end;

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

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


procedure TGTer_combinado_horario.SorteosDelPaso(sortear: boolean);
begin
  if hayForzamientos or globs.ObligarDisponibilidad_1_ then
  begin
    NMaquinasDisponiblesTG := paUnidades.nUnidades_Operativas[0];
    NMaquinasDisponiblesTV := paUnidades.nUnidades_Operativas[1];
    NCalderasDisponibles := NMaquinasDisponiblesTG;
    PMaxDisponible_TG := pa.PMax_TG * NMaquinasDisponiblesTG;
    PMaxDisponible_TV := min(pa.PMax_TV * NMaquinasDisponiblesTV,
    PMaxDisponible_TG * pa.factorPotenciaTVdivTG);
  end
  else
  if sortear then
  begin
    if (globs.EstadoDeLaSala = CES_OPTIMIZANDO) then
    begin
      ActualizarProbabilidadesReparacionYRotura_(1, 0, 0);
      NMaquinasDisponiblesTG := paUnidades.nUnidades_Operativas[0];
      ActualizarProbabilidadesReparacionYRotura_(1, 0, 1);
      NMaquinasDisponiblesTV := paUnidades.nUnidades_Operativas[1];
      NCalderasDisponibles := NMaquinasDisponiblesTG;
    end
    else
    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);
      ActualizarProbabilidadesReparacionYRotura_(pa.disp_cald, pa.tRepHoras_cald, 1);
      NCalderasDisponibles := Sorteos_RepRotUnidades_k(2);
    end;
    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];
    NCalderasDisponibles := paUnidades.nUnidades_Operativas[2];

    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_horario.PrepararPaso_ps;
var
  indice: NReal;
  i: integer;
  resCod: integer;
  xrpos: NReal;
  f: textfile;
  archi: string;
begin
  {$IFDEF dbgc}
  if ( globs.EstadoDeLaSala =   CES_SIMULANDO) and (globs.kPaso_Sim = 19 ) then
     writeln('a');
  {$ENDIF}

  PMinTG := pa.PMin_TG;
  BMaxTG_SC := pa.PMax_TG - pa.PMin_TG;
  BMaxTG_CC := BMaxTG_SC;
  PMinTV := pa.PMin_TV;
  BMaxTV := pa.PMax_TV - pa.PMin_TV;
  PMaxTG := pa.PMax_TG;
  horasPurga_ := pa.horasPurga;
  horasTVPleno_ := pa.horasTVPleno;
  NDisc := pa.NDisc;
  costoComb := pa.costoComb;
  NDisc_cald := pa.NDisc_cald;
  horasEnfriarCaldera := pa.horasEnfriarCaldera;

//  SetLength(X_timer_, NMaquinasDisponiblesTG);
//  SetLength(Xs_timer_, NMaquinasDisponiblesTG);
//  SetLength(purga_TV_, NMaquinasDisponiblesTG);

  //pendTV es la pendiente para prender a pleno la TV (que depende de las TG a traves de factorTV)
  pendTV_frio := (PMaxTG - PMinTG) / (horasTVPleno_);
  pendTV_tibio := 1.2*(PMaxTG - PMinTG) / (horasTVPleno_);
  pendTV_caliente := (PMaxTG - PMinTG);// / (horasTVPleno_);// (PMaxTG - PMinTG);  //para que se prenda de golpe
  horasCalienteCaldera := 120;

  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);
    //este dsp va multiplicado por algo adimensionado (A)
    cvTG := pa.cv_TG * indice + pa.cv_NoCombustible_TG;
    //este va por algo con dimension MW (coef B)
  end
  else
  begin
    c0TG := pa.PMin_TG * (pa.cv_min_TG + pa.cv_NoCombustible_TG);
    cvTG := pa.cv_TG + pa.cv_NoCombustible_TG;
  end;
  c0TV := pa.PMin_TV * (pa.cv_min_TV + pa.cv_NoCombustible_TV);
  cvTV := pa.cv_TV + pa.cv_NoCombustible_TV;

  for i := 0 to NmaxTG - 1 do
  begin
    if X_timer_[i] < horasPurga_ then //tiempo purga
    begin
      factorTV_[i] := 0;
      purga_TV_[i] := 0;
    end
    else
    begin
      if (X_timer_[i]) < (horasTVPleno_ + horasPurga_ - 1) then //prendiendo TV despacito
      begin
        factorTV_[i] := pa.factorPotenciaTVdivTG;
        purga_TV_[i] := 1;
      end
      else
      begin
        factorTV_[i] := pa.factorPotenciaTVdivTG;
        purga_TV_[i] := 2;  //TV a pleno
      end;
    end;
  end;
  //defino que tipo de arranque es
  for i := 0 to NmaxTG - 1 do
  begin
    if (X_caldera_[i]) < round(horasCalienteCaldera/3) then //caldera fria
    begin
       pendTV_[i] := pendTV_frio;
    end
    else
    begin
      if (X_caldera_[i]) < round(2*horasCalienteCaldera/3) then //caldera tibia
      begin
         pendTV_[i] := pendTV_tibio;
      end
      else
      begin
        pendTV_[i] := pendTV_tibio;// pendTV_caliente/(X_timer_[i] - horasPurga_);   //caldera caliente
      end;
    end;
  end;
  // Cálculo del al afectación del futuro por movimiento de estado.
  // Timer de PURGAS TGs.
{$IFDEF ArribaAbajoDeaUNO}
  for i := 0 to NmaxTG - 1 do
  begin
    globs.CF.devxr_continuo(ixr, globs.kPaso_Opt + 1, dCFdX_Inc[i],
          dCFdX_Dec[i], resCod, xrpos );
    case resCod of
    -1:  dCFdX[i]:= dCFdX_Inc[i];
     0:  dCFdX[i] := ( dCFdX_Inc[i] + dCFdX_dec[i] ) / 2;
     1:  dCFdX[i]:= dCFdX_dec[i];
    end;
  end;
{$ELSE}

{$IFDEF CC_DBG}
   if (globs.EstadoDeLaSala = CES_OPTIMIZANDO) and
      (globs.kPaso_Opt = 23) and
     (globs.CF.ordinalEstrellaActual = 6505) and
     (globs.cntIteracionesDelPaso = 1)  then
    writeln('Paso: ' + IntToStr(globs.kPaso_Opt) +
      '_kEstrellita_' + IntToStr(globs.CF.ordinalEstrellaActual));
{$ENDIF}

{$IFDEF imprimirderivadas}
if globs.kPaso_Opt > 40 then
begin
  archi := 'derivadas_clasic' + IntToStr(globs.kPaso_Opt) + '.xlt';
  AssignFile(f, archi);
  if FileExists(archi) then
    Append(f)
  else
    Rewrite(f);
  writeln(f, 'kEstrellita:  '+ #9 + IntToStr(globs.CF.ordinalEstrellaActual));
end;
{$ENDIF}

for i := 0 to NmaxTG - 1 do
begin
    if X_timer_[i] < ( horasPurga_ + horasTVPleno_ - 1 ) then
    begin
      dCFdX_Inc[i] := globs.CF.deltaCosto_vxr_continuo(ixr + i, globs.kPaso_Opt + 1, globs.HorasDelPaso );
      dCFdx_dec[i] := - globs.CF.deltaCosto_vxr_continuo(ixr + i, globs.kPaso_Opt + 1, - X_timer_[i]);
      dCFdX[i] := ( dCFdX_Inc[i] + dCFdX_dec[i] ) / 2;
    end
    else
      dCFdX[i] := -globs.CF.deltaCosto_vxr_continuo(ixr + i, globs.kPaso_Opt + 1, - ( horasPurga_ + horasTVPleno_ - 1));

    {$IFDEF imprimirderivadas}
    if globs.kPaso_Opt > 40 then
       writeln(f, 'variable:  ' + #9 + 'X_timer_' + IntToStr(i) + #9 + 'valor_variable: ' +
       #9 + FloatToStr(X_timer_[i]) + #9 + 'valor_gradiente: ' + #9 + FloatToStr(dCFdX[i]) );
    {$ENDIF}

end;
{$ENDIF}

  // Temperatura de Caldera
  for i := NmaxTG to 2*NmaxTG - 1 do
  begin
    globs.CF.devxr_continuo(ixr + i, globs.kPaso_Opt + 1, dCFdX_Inc[i],
          dCFdX_Dec[i], resCod, xrpos );
    case resCod of
    -1:  dCFdX[i]:= dCFdX_Inc[i];
     0:  dCFdX[i] := ( dCFdX_Inc[i] + dCFdX_dec[i] ) / 2;
     1:  dCFdX[i]:= dCFdX_dec[i];
    end;

    {$IFDEF imprimirderivadas}
    if globs.kPaso_Opt > 40 then
       writeln(f, 'variable:  ' + #9 + 'X_caldera_' + IntToStr(i - NmaxTG) + #9 + 'valor_variable: ' +
       #9 + FloatToStr(X_caldera_[i - NmaxTG]) + #9 + 'valor_gradiente: ' + #9 + FloatToStr(dCFdX[i]) );
    {$ENDIF}
  end;

{$IFDEF imprimirderivadas}
  if globs.kPaso_Opt > 40 then
     closefile(f);
{$ENDIF}

end;

procedure TGTer_combinado_horario.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_horario.opt_nvers(var ivar, ivae, ires: integer);
begin


//  if (globs.kPaso_Opt = 94) then
//  begin
//     writeln('globs.kPaso_Opt' + #9 + 'ivar' + #9 + IntToStr(ivar));
//  end;

  if NMaquinasDisponiblesTG = 0 then
  begin
    opt_nvers_TG_indisponible(ivar, ivae, ires);
  end
  else
  if ((NMaquinasDisponiblesTV = 0)  or (NCalderasDisponibles = 0)) then
  begin
    opt_nvers_TV_indisponible(ivar, ivae, ires);
  end
  else
  begin
    opt_nvers_combinado(ivar, ivae, ires);
  end;

  {
  if NMaquinasDisponiblesTV = 0 then
  begin
    opt_nvers_TV_indisponible(ivar, ivae, ires);
  end
  else
  begin
    case purga_TV_[0] of
      0:
      begin
        opt_nvers_abierto_purga(ivar, ivae, ires);
      end;
      1, 2:
      begin
        opt_nvers_combinado(ivar, ivae, ires);
      end;
    end;
  end;
  }

end;

procedure TGTer_combinado_horario.opt_nvers_TG_indisponible(var ivar, ivae,
  ires: integer);
begin
  //variables Xs_Timer_i, Xs_Caldera_i
  //Por cada TG hay ATG_SC Xs_timer Xs_caldera
  Self.ivar := ivar;
  ivar := ivar + NmaxTG + NmaxTG;

  ivar_Xs_timer := Self.ivar;
  ivar_Xs_caldera := ivar_Xs_timer + NmaxTG;

  Self.ires := ires;
  ires := ires + NmaxTG + NmaxTG;
  // Están las restricciones de extinsión de BTG_SC < (PMax-PMin)*ATG_SC
  //BTG_CC < (PMax-PMin)*ATG_SC, BTV < (PMax-PMin)*ATV, XTemp < Atemp*temp_max
  // y también que ( B_TV + PMin_TV ) < f_TvxTG * (B_TG + PMin_TG )

  jres_dinTimer := self.ires;
  jres_dinCaldera := jres_dinTimer + NmaxTG;
  jaux := jres_dinCaldera + NmaxTG;
  jres_Ultima := jaux;
end;

procedure TGTer_combinado_horario.opt_nvers_TV_indisponible(var ivar, ivae,
  ires: integer);
begin

  //variables ATG_SC_i, BTG_SC_i, Xs_Timer_i, Xs_Caldera_i
  //Por cada TG hay ATG_SC Xs_timer Xs_caldera
  Self.ivar := ivar;
  ivar := ivar + 2*NmaxTG + NmaxTG + NmaxTG;

  ivar_ATG_SC := self.ivar;
  ivar_BTG_SC := ivar_ATG_SC + 1;

  ivar_Xs_timer := ivar_ATG_SC + 2*NmaxTG;
  ivar_Xs_caldera := ivar_Xs_timer + NmaxTG;

  // Las variables ATG_SC son enteras
  Self.ivae := ivae;
  ivae := ivae + NmaxTG;

  Self.ires := ires;
  ires := ires + NmaxTG + NmaxTG + NmaxTG + NmaxTG;
  // Están las restricciones de extinsión de BTG_SC < (PMax-PMin)*ATG_SC
  //BTG_CC < (PMax-PMin)*ATG_SC, BTV < (PMax-PMin)*ATV, XTemp < Atemp*temp_max
  // y también que ( B_TV + PMin_TV ) < f_TvxTG * (B_TG + PMin_TG )

  jres_dinTimer := self.ires;
  jres_Acople_BTG_SC := jres_dinTimer + NmaxTG;
  jres_Acople_TimerxBTG_SC := jres_Acople_BTG_SC + NmaxTG;
  jres_dinCaldera := jres_Acople_TimerxBTG_SC + NmaxTG;
  jaux := jres_dinCaldera + NmaxTG;

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

   {
    //variables ATG_SC, BTG_SC, Xs_Timer
  Self.ivar := ivar;
  ivar := ivar + 3;

  ivar_ATG_SC := self.ivar;
  ivar_BTG_SC := ivar_ATG_SC + 1;

  ivar_Xs_timer := ivar_BTG_SC + 1;

  // Las variables ATG_SC, ATG_CC y Aacople son enteras
  Self.ivae := ivae;
  ivae := ivae + 1;

  Self.ires := ires;
  ires := ires + 3;
  // Están las restricciones de extinsión de BTG_SC < (PMax-PMin)*ATG_SC
  //BTG_CC < (PMax-PMin)*ATG_CC, BTV < (PMax-PMin)*ATV, Xs_Timer = AAcople*(X_timer + durPaso)
  // y también que ( B_TV + PMin_TV ) < f_TvxTG * (B_TG + PMin_TG )

  jres_dinTimer := self.ires;
  jres_Acople_BTG_SC := jres_dinTimer + 1;
  jres_Acople_TimerxBTG_SC := jres_Acople_BTG_SC + 1;

  jaux := jres_Acople_TimerxBTG_SC;

  if pA.HayRestriccionEmaxPasoDeTiempo then
  begin
    Inc(ires);
    jres_EMaxPasoDeTiempo := jaux + 1;
    jres_Ultima := jres_EMaxPasoDeTiempo;
  end
  else
  begin
    jres_Ultima := jaux + 1;
  end;
    }
end;

procedure TGTer_combinado_horario.opt_nvers_combinado(var ivar, ivae,
  ires: integer);
begin
//  if (globs.kPaso_Opt = 94) then
//  begin
//     writeln('ivar' + #9 + IntToStr(ivar) + #9);
//  end;

  //variables ATG_SC_i, BTG_SC_i, ATG_CC_i, BTG_CC_i, ATV, BTV, AAcople_i, Xs_Timer_i, Xs_Caldera_i
  //Por cada TG hay ATG_SC ATG_CC AAcople y Xs_timer
  Self.ivar := ivar;
  ivar := ivar + 2*NmaxTG + 2*NmaxTG + 2 + 3*NmaxTG;

  ivar_ATG_SC := self.ivar;
  ivar_BTG_SC := ivar_ATG_SC + 1;

  ivar_ATG_CC := ivar_ATG_SC + 2*NmaxTG;
  ivar_BTG_CC := ivar_ATG_CC + 1;

  ivar_ATV := ivar_ATG_CC + 2*NmaxTG;
  ivar_BTV := ivar_ATV + 1;

  ivar_AAcople := ivar_BTV + 1;
  ivar_Xs_timer := ivar_AAcople + NmaxTG;
  ivar_Xs_caldera := ivar_Xs_timer + NmaxTG;

  // Las variables ATG_SC, ATG_CC, ATV y AAcople son enteras
  Self.ivae := ivae;
  ivae := ivae + NmaxTG + NmaxTG + NmaxTG + 1;

  Self.ires := ires;
  ires := ires + NmaxTG + NmaxTG + NmaxTG + 2 + NmaxTG + NmaxTG + NmaxTG;
  // Están las restricciones de extinsión de BTG_SC < (PMax-PMin)*ATG_SC
  //BTG_CC < (PMax-PMin)*ATG_SC, BTV < (PMax-PMin)*ATV, XTemp < Atemp*temp_max
  // y también que ( B_TV + PMin_TV ) < f_TvxTG * (B_TG + PMin_TG )

  jres_dinTimer := self.ires;
  jres_Acople_BTG_SC := jres_dinTimer + NmaxTG;
  jres_Acople_BTG_CC := jres_Acople_BTG_SC + NmaxTG;
  jres_Acople_BTV := jres_Acople_BTG_CC + NmaxTG;
  jres_Acople_TVxTG := jres_Acople_BTV + 1;
  jres_Acople_TimerxBTG_SC := jres_Acople_TVxTG + 1;
  jres_Acople_TimerxBTG_CC := jres_Acople_TimerxBTG_SC + NmaxTG;
  jres_dinCaldera := jres_Acople_TimerxBTG_CC + NmaxTG;
  jaux := jres_dinCaldera + NmaxTG;

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

end;

procedure TGTer_combinado_horario.opt_cargue(s: TSimplex);
begin

  if NMaquinasDisponiblesTG = 0 then
  begin
    opt_cargue_TG_indisponible(s);
  end
  else
  if ((NMaquinasDisponiblesTV = 0) or (NCalderasDisponibles = 0)) then
  begin
    opt_cargue_TV_indisponible(s);
  end
  else
  begin
    opt_cargue_combinado(s);
  end;

  {
  if NMaquinasDisponiblesTV = 0 then
  begin
    opt_cargue_TV_indisponible(s);
  end
  else
  begin
    case purga_TV_[0] of
      0:
      begin
        opt_cargue_abierto_purga(s);
      end;
      1, 2:
      begin
        opt_cargue_combinado(s);
      end;
    end;
  end;
  }

end;

procedure TGTer_combinado_horario.opt_cargue_TG_indisponible(s: TSimplex);
var
   inodores: integer;
   i: integer;
   termino_indep: NReal;
begin

 //restriccion de timer
 for i := 0 to NmaxTG - 1 do
 begin
   s.pon_e(jres_dinTimer + i, ivar_Xs_timer + i, -1);
   termino_indep := max(X_timer_[i] - globs.HorasDelPaso, 0);
   s.pon_e(jres_dinTimer + i, s.nc, termino_indep);
 end;

 //restriccion de caldera
 for i := 0 to NmaxTG - 1 do
 begin
   s.pon_e(jres_dinCaldera + i, ivar_Xs_caldera + i, -1);
   termino_indep := max(X_caldera_[i] - globs.HorasDelPaso, 0);
   s.pon_e(jres_dinCaldera + i, s.nc, termino_indep);
 end;

 {$IFNDEF INTERMEDIARIO_DE_COSTO_FUTURO}
 // Cargamos la afectación del costo futuro por movimiento del estado
 // -dC = -dCFdX ( Xs - X ) = -dCFdX * Xs + dCFdX * X
 for i := 0 to NmaxTG - 1 do
 begin
   s.pon_e(s.nf, ivar_Xs_timer + i, -dCFdX[i]);
   s.acum_e(s.nf, s.nc, dCFdX[i] * X_timer_[i]);
 end;
 for i := 0 to NmaxTG - 1 do
 begin
   s.pon_e(s.nf, ivar_Xs_caldera + i, -dCFdX[i + NmaxTG]);
   s.acum_e(s.nf, s.nc, dCFdX[i + NmaxTG] * X_caldera_[i]);
 end;
 {$ENDIF}
end;

procedure TGTer_combinado_horario.opt_cargue_TV_indisponible(s: TSimplex);
var
   inodores: integer;
   i: integer;
   termino_indep: NReal;
begin
  inodores := nodo.ires;
  // aporte a las restricciones del nodo
  for i := 0 to NmaxTG - 1 do
  begin
    s.pon_e(inodores, ivar_ATG_SC + 2*i, PMinTG); // coef A TG sin calentar caldera
    s.pon_e(inodores, ivar_BTG_SC + 2*i, 1); // coef de la B TG sin calentar caldera
  end;

  //restriccion de timer
  for i := 0 to NmaxTG - 1 do
  begin
    s.pon_e(jres_dinTimer + i, ivar_Xs_timer + i, -1);
    termino_indep := max(X_timer_[i] - globs.HorasDelPaso, 0);
    s.pon_e(jres_dinTimer + i, s.nc, termino_indep);
  end;

  // coef B de las TG
  for i := 0 to NmaxTG - 1 do
  begin
    s.pon_e(jres_Acople_BTG_SC + i, ivar_ATG_SC + 2*i, BMaxTG_SC);
    s.pon_e(jres_Acople_BTG_SC + i, ivar_BTG_SC + 2*i, -1);
  end;

  //restriccion PTGSC menor que el max (cuando esta Aacople apagado)
  for i := 0 to NmaxTG - 1 do
  begin
    s.pon_e(jres_Acople_TimerxBTG_SC + i, ivar_ATG_SC + 2*i, -1);
    s.pon_e(jres_Acople_TimerxBTG_SC + i, s.nc, 1 + 0.1);
  end;

  //restriccion de caldera
  for i := 0 to NmaxTG - 1 do
  begin
    s.pon_e(jres_dinCaldera + i, ivar_Xs_caldera + i, -1);
    termino_indep := max(X_caldera_[i] - globs.HorasDelPaso, 0);
    s.pon_e(jres_dinCaldera + i, s.nc, termino_indep);
  end;

  //Restriccion a la energía máxima generable en el paso
  if pA.HayRestriccionEmaxPasoDeTiempo then
  begin
    //por ahora nada
  end;

  // aportes a la función de utilidad
  for i := 0 to NmaxTG - 1 do
  begin
    s.pon_e(s.nf, ivar_ATG_SC + 2*i, -c0TG * globs.HorasDelPaso);
    s.pon_e(s.nf, ivar_BTG_SC + 2*i, -cvTG * globs.HorasDelPaso);
  end;

  {$IFNDEF INTERMEDIARIO_DE_COSTO_FUTURO}
  // Cargamos la afectación del costo futuro por movimiento del estado
  // -dC = -dCFdX ( Xs - X ) = -dCFdX * Xs + dCFdX * X
  for i := 0 to NmaxTG - 1 do
  begin
    s.pon_e(s.nf, ivar_Xs_timer + i, -dCFdX[i]);
    s.acum_e(s.nf, s.nc, dCFdX[i] * X_timer_[i]);
  end;
  for i := 0 to NmaxTG - 1 do
  begin
    s.pon_e(s.nf, ivar_Xs_caldera + i, -dCFdX[i + NmaxTG]);
    s.acum_e(s.nf, s.nc, dCFdX[i + NmaxTG] * X_caldera_[i]);
  end;
  {$ENDIF}


end;


procedure TGTer_combinado_horario.opt_cargue_combinado(s: TSimplex);
var
   inodores: integer;
   i: integer;
begin

  inodores := nodo.ires;

  // aporte a las restricciones del nodo
  for i := 0 to NmaxTG - 1 do
  begin
    s.pon_e(inodores, ivar_ATG_SC + 2*i, PMinTG); // coef A TG sin calentar caldera
    s.pon_e(inodores, ivar_BTG_SC + 2*i, 1); // coef de la B TG sin calentar caldera
    s.pon_e(inodores, ivar_ATG_CC + 2*i, PMinTG);   // coef A TG calentando caldera
    s.pon_e(inodores, ivar_BTG_CC + 2*i, 1); // coef de la B TG calentando caldera
  end;

  s.pon_e(inodores, ivar_ATV, PMinTV);   // coef A TG calentando caldera
  s.pon_e(inodores, ivar_BTV, 1); // coef de la B TG calentando caldera

 // jres_tempXBTG := self.ires;
 // jres_Acople_BTG_SC := jres_tempXBTG + 1;
 // jres_Acople_BTG_CC := jres_Acople_BTG_SC + 1;
 // jres_Acople_BTV := jres_Acople_BTG_CC + 1;
 // jres_Acople_TVxTG := jres_Acople_BTV + 1;
 // jres_Acople_tempxBTG_SC := jres_Acople_TVxTG + 1;
 // jres_Acople_tempxBTG_CC := jres_Acople_tempxBTG_SC +1;

 {$IFDEF ArribaAbajoDeaUNO}
  //restriccion de timer
  for i := 0 to NmaxTG - 1 do
  begin
    //para que no salte a 2 cuando acopla y el timer era 0
    if X_timer_[i] > AsumaCero then
    begin
      s.pon_e(jres_dinTimer + i, ivar_AAcople + i, 2*globs.HorasDelPaso);
      s.pon_e(jres_dinTimer + i, s.nc, X_timer_[i] - globs.HorasDelPaso );
    end
    else
      s.pon_e(jres_dinTimer + i, ivar_AAcople + i, globs.HorasDelPaso);
    s.pon_e(jres_dinTimer + i, ivar_Xs_timer + i, -1);
  end;
 {$ELSE}
 //restriccion de timer
 for i := 0 to NmaxTG - 1 do
 begin
   s.pon_e(jres_dinTimer + i, ivar_AAcople + i,  X_timer_[i] + globs.HorasDelPaso );
   s.pon_e(jres_dinTimer + i, ivar_Xs_timer + i, -1);
 end;

  {$ENDIF}

  // coef B de las TG
  for i := 0 to NmaxTG - 1 do
  begin
    s.pon_e(jres_Acople_BTG_SC + i, ivar_ATG_SC + 2*i, BMaxTG_SC);
    s.pon_e(jres_Acople_BTG_SC + i, ivar_BTG_SC + 2*i, -1);
  end;

  for i := 0 to NmaxTG - 1 do
  begin
    s.pon_e(jres_Acople_BTG_CC + i, ivar_ATG_CC + 2*i, BMaxTG_CC);
    s.pon_e(jres_Acople_BTG_CC + i, ivar_BTG_CC + 2*i, -1);
  end;

  // coef B[iposte] de las TV
  s.pon_e(jres_Acople_BTV, ivar_ATV, BMaxTV);
  s.pon_e(jres_Acople_BTV, ivar_BTV, -1);


  // restricción de acople de las centrales TG y TV
  if not hayForzamientos then
  begin
    for i := 0 to NmaxTG - 1 do
    begin
    s.pon_e(jres_Acople_TVxTG, ivar_ATG_CC + 2*i, factorTV_[i] * PMinTG);
    s.pon_e(jres_Acople_TVxTG, ivar_BTG_CC + 2*i, factorTV_[i]);
    end;
  end
  else
  begin
    for i := 0 to NmaxTG - 1 do
    begin
    s.pon_e(jres_Acople_TVxTG, ivar_ATG_CC + 2*i, factorTV_[i] * PMinTG * 1.3);
    s.pon_e(jres_Acople_TVxTG, ivar_BTG_CC + 2*i, factorTV_[i] * 1.3);
    end;
  end;
  s.pon_e(jres_Acople_TVxTG, ivar_ATV, -PMinTV);
  s.pon_e(jres_Acople_TVxTG, ivar_BTV, -1);

  //restriccion PTGSC menor que el max (cuando esta Aacople apagado)
  for i := 0 to NmaxTG - 1 do
  begin
    s.pon_e(jres_Acople_TimerxBTG_SC + i, ivar_ATG_SC + 2*i, -1);
    s.pon_e(jres_Acople_TimerxBTG_SC + i, ivar_AAcople + i, -1);
    s.pon_e(jres_Acople_TimerxBTG_SC + i, s.nc, 1 + 0.1);
  end;

  //restriccion PTGCC menor que el max (cuando esta Atemp prendido)
  for i := 0 to NmaxTG - 1 do
  begin
    s.pon_e(jres_Acople_TimerxBTG_CC + i, ivar_ATG_CC + 2*i, -1);
    //  s.pon_e(jres_Acople_tempxBTG_CC, ivar_BTG_CC, -1);
    s.pon_e(jres_Acople_TimerxBTG_CC + i, ivar_AAcople + i, 1);
  end;

  {$IFDEF CC_dbg}
  if (globs.kPaso_Opt = 790) and ((globs.CF.ordinalEstrellaActual = 21) or (globs.CF.ordinalEstrellaActual = 57)) then
     i := 0;
  {$ENDIF}

  //restriccion de las calderas
  for i := 0 to NmaxTG - 1 do
  begin
    //para que no salte a 2 cuando acopla y el timer era 0
    if X_caldera_[i] > AsumaCero then
    begin
//      s.pon_e(jres_dinCaldera + i, ivar_AAcople + i, (horasCalienteCaldera/(horasPurga_ + horasTVPleno_) )*globs.HorasDelPaso);
      s.pon_e(jres_dinCaldera + i, s.nc, X_caldera_[i] - globs.HorasDelPaso );
    end
    else
      s.pon_e(jres_dinCaldera + i, ivar_AAcople + i, globs.HorasDelPaso);
    s.pon_e(jres_dinCaldera + i, ivar_AAcople + i, (horasCalienteCaldera/(horasPurga_ + horasTVPleno_) )*globs.HorasDelPaso);
    s.pon_e(jres_dinCaldera + i, ivar_Xs_caldera + i, -1);
  end;

  //Restriccion a la energía máxima generable en el paso
  if pA.HayRestriccionEmaxPasoDeTiempo then
  begin
    //por ahora nada
  end;

  // aportes a la función de utilidad
  for i := 0 to NmaxTG - 1 do
  begin
    s.pon_e(s.nf, ivar_ATG_SC + 2*i, -c0TG * globs.HorasDelPaso);
    s.pon_e(s.nf, ivar_BTG_SC + 2*i, -cvTG * globs.HorasDelPaso);
    s.pon_e(s.nf, ivar_BTG_CC + 2*i, -cvTG * globs.HorasDelPaso);
    s.pon_e(s.nf, ivar_ATG_CC + 2*i, -c0TG * globs.HorasDelPaso);
    s.pon_e(s.nf, ivar_AAcople + i, -costoComb * globs.HorasDelPaso);
  end;
  s.pon_e(s.nf, ivar_BTV, -cvTV * globs.HorasDelPaso);
  s.pon_e(s.nf, ivar_ATV, -c0TV * globs.HorasDelPaso);


  {$IFNDEF INTERMEDIARIO_DE_COSTO_FUTURO}
  // Cargamos la afectación del costo futuro por movimiento del estado
  // -dC = -dCFdX ( Xs - X ) = -dCFdX * Xs + dCFdX * X
  for i := 0 to NmaxTG - 1 do
  begin
    s.pon_e(s.nf, ivar_Xs_timer + i, -dCFdX[i]);
    s.acum_e(s.nf, s.nc, dCFdX[i] * X_timer_[i]);
  end;
  for i := 0 to NmaxTG - 1 do
  begin
    s.pon_e(s.nf, ivar_Xs_caldera + i, -dCFdX[i + NmaxTG]);
    s.acum_e(s.nf, s.nc, dCFdX[i + NmaxTG] * X_caldera_[i]);
  end;
  {$ENDIF}
end;


procedure TGTer_combinado_horario.opt_fijarRestriccionesDeCaja(s: TSimplex);
var
  cotaBTG_CC: NReal;
begin

  if NMaquinasDisponiblesTG = 0 then
  begin
    opt_fijarRestriccionesDeCaja_TG_indisponible(s);
  end
  else
  if ((NMaquinasDisponiblesTV = 0) or (NCalderasDisponibles = 0)) then
  begin
    opt_fijarRestriccionesDeCaja_TV_indisponible(s);
  end
  else
  begin
    opt_fijarRestriccionesDeCaja_combinado(s);
  end;
  {
  if NMaquinasDisponiblesTV = 0 then
  begin
    opt_fijarRestriccionesDeCaja_TV_indisponible(s);
  end
  else
  begin
    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
      case purga_TV_[0] of
        0:
        begin
           opt_fijarRestriccionesDeCaja_abierto_purga(s);
        end;
        1:
        begin
           opt_fijarRestriccionesDeCaja_transicion(s);
        end;
        2:
        begin
           opt_fijarRestriccionesDeCaja_combinado(s);
        end;
      end;
    end;
  end;

  }

end;

procedure TGTer_combinado_horario.opt_fijarRestriccionesDeCaja_TG_indisponible(
  s: TSimplex);
var
   i: integer;
begin
   for i := 0 to NmaxTG - 1 do
   begin
     s.cota_sup_set(ivar_Xs_timer + i, 5 * 3);
     s.FijarRestriccionIgualdad(jres_dinTimer + i);
     s.cota_sup_set(ivar_Xs_caldera + i, horasCalienteCaldera);
     s.FijarRestriccionIgualdad(jres_dinCaldera + i);
  end;

end;

procedure TGTer_combinado_horario.opt_fijarRestriccionesDeCaja_TV_indisponible(
  s: TSimplex);
var
   i: integer;
begin
   for i := 0 to NmaxTG - 1 do
   begin
     s.cota_sup_set(ivar_Xs_timer + i, 5 * 3);
     s.FijarRestriccionIgualdad(jres_dinTimer + i);
     s.cota_sup_set(ivar_Xs_caldera + i, horasCalienteCaldera);
     s.FijarRestriccionIgualdad(jres_dinCaldera + i);

     s.set_entera(ivae + i, ivar_ATG_SC + NmaxTG * i, 1);

    if (not hayForzamientos) then
    begin
     s.cota_sup_set(ivar_BTG_SC + NmaxTG * i, BMaxTG_SC * NMaquinasDisponiblesTG * 1.1);
    end;
  end;

  {
  if NMaquinasDisponiblesTG = 0 then
  begin
    exit;
  end;

  s.cota_sup_set(ivar_Xs_timer, 5 * 2);
  s.FijarRestriccionIgualdad(jres_dinTimer);

  s.set_entera(ivae, ivar_ATG_SC, NMaquinasDisponiblesTG);

  if (not hayForzamientos) then
  begin
    s.cota_sup_set(ivar_BTG_SC, BMaxTG_SC * NMaquinasDisponiblesTG * 1.1);
  end;
   }

end;


procedure TGTer_combinado_horario.opt_fijarRestriccionesDeCaja_combinado(
  s: TSimplex);
var
   i: integer;
   cotaBTG_CC, pend_TV: NReal;
begin
   for i := 0 to NmaxTG - 1 do
   begin
     s.cota_sup_set(ivar_Xs_timer + i, (horasPurga_ + horasTVPleno_ - 1) * 3);
     {$IFNDEF xPRUEBA_SPX_REDUNDANTE}
     s.FijarRestriccionIgualdad(jres_dinTimer + i);
     s.FijarRestriccionIgualdad(jres_Acople_TimerxBTG_CC + i);
     {$ENDIF}
     s.cota_sup_set(ivar_Xs_caldera + i, horasCalienteCaldera*2);
     {$IFNDEF xPRUEBA_SPX_REDUNDANTE}
     s.FijarRestriccionIgualdad(jres_dinCaldera + i);
     {$ENDIF}

     s.set_entera(ivae + i, ivar_ATG_SC + NmaxTG * i, 1); // SC
     s.set_entera(ivae + i + NmaxTG, ivar_ATG_CC + NmaxTG * i, 1); // CC
     s.set_entera(ivae + 2*NmaxTG + NmaxTV + i, ivar_AAcople +  i, 1);  //  había un 3

    if (not hayForzamientos) then
    begin
     s.cota_sup_set(ivar_BTG_SC + NmaxTG * i, BMaxTG_SC * 1.1);
     if X_timer_[i] >= horasPurga_ then
        cotaBTG_CC := Min((pendTV_[i] * (X_timer_[i] - horasPurga_) + PMinTG), BMaxTG_CC)
     else
        cotaBTG_CC := 0;

     s.cota_sup_set(ivar_BTG_CC + NmaxTG * i, cotaBTG_CC);
    end;
  end;
 {$IFNDEF PRUEBA_SPX_REDUNDANTE}
  s.FijarRestriccionIgualdad(jres_Acople_TVxTG);
 {$ENDIF}
  s.set_entera(ivae + 2*NmaxTG, ivar_ATV, NMaquinasDisponiblesTV);
  s.cota_sup_set(ivar_BTV, BMaxTV * NMaquinasDisponiblesTV * 1.1);
end;


procedure TGTer_combinado_horario.opt_leerSolucion(s: TSimplex);
var
//  ATG_SC, ATG_CC, ATV: integer;
  // AAcople: integer;
//   BTV, BTG_SC, BTG_CC: NReal;
//  PDelPaso: NReal;
//  EnergiaDespachada: NReal;
  i: integer;
begin

  Ingreso_PorDisponibilidad_ :=
    PMaxDisponible_Central * pa.PagoPorDisponibilidad_USD_MWh * globs.HorasDelPaso;

  CostoDirectoDelPaso :=0;
  Ingreso_PorEnergia_:= 0;

  if NMaquinasDisponiblesTG = 0 then
  begin
    vclear(P);
  //  NMaquinasDespachadasTG := 0;
  //  NMaquinasDespachadasTV := 0;
 //   for i := 0 to NmaxTG - 1 do
 //     Xs_timer_[i] := 0;
    opt_leerSolucion_TG_indisponible(s)
  end
  else
    if ((NMaquinasDisponiblesTV = 0) or (NCalderasDisponibles = 0)) then
      opt_leerSolucion_TV_indisponible(s)
    else
      opt_leerSolucion_combinado(s);


  if globs.EstadoDeLaSala = CES_SIMULANDO then
     ChequearYActualizarTimers;

  {
  if NMaquinasDisponiblesTG = 0 then
  begin
    vclear(P);
    NMaquinasDespachadasTG := 0;
    NMaquinasDespachadasTV := 0;
    Xs_timer_[0] := 0;
    exit;
  end
  else if NMaquinasDisponiblesTV = 0 then
  begin
     opt_leerSolucion_TV_indisponible(s);
  end
  else
  begin
    case purga_TV_[0] of
      0:
      begin
         opt_leerSolucion_abierto_purga(s);
      end;
      1,2:
      begin
         opt_leerSolucion_combinado(s);
      end;
    end;
  end;
   }

end;

procedure TGTer_combinado_horario.opt_leerSolucion_TG_indisponible(s: TSimplex);
var
  ATG_SC: integer;
  BTG_SC: NReal;
  PDelPaso: NReal;
  EnergiaDespachada: NReal;
  i: integer;
begin

 PDelPaso := 0;
 EnergiaDespachada := 0;
 NMaquinasDespachadasTG := 0;
 costoDirectoDelPaso:= 0;

 for i := 0 to NmaxTG - 1 do
 begin
  Xs_timer_[i] :=  s.xval( ivar_Xs_timer + i);
  Xs_caldera_[i] :=  s.xval( ivar_Xs_caldera + i);

  if Xs_timer_[i] >= (5*0.9) then
     Xs_timer_[i] := 5
  else
  if Xs_timer_[i] <= AsumaCero then
     Xs_timer_[i] := 0;

 end;

  P[0] := PDelPaso;   //Como este modelo siempre es horario esta bien esto

  Ingreso_PorEnergia_ := EnergiaDespachada * pa.PagoPorEnergia_USD_MWh;

end;

procedure TGTer_combinado_horario.opt_leerSolucion_TV_indisponible(s: TSimplex);
var
  ATG_SC: integer;
  BTG_SC: NReal;
  PDelPaso: NReal;
  EnergiaDespachada: NReal;
  i: integer;
begin

 PDelPaso := 0;
 EnergiaDespachada := 0;
 NMaquinasDespachadasTG := 0;
 costoDirectoDelPaso:= 0;

 for i := 0 to NmaxTG - 1 do
 begin
  Xs_timer_[i] :=  s.xval( ivar_Xs_timer + i);
  Xs_caldera_[i] :=  s.xval( ivar_Xs_caldera + i);

  if Xs_timer_[i] >= (5*0.9) then
     Xs_timer_[i] := 5
  else
  if Xs_timer_[i] <= AsumaCero then
     Xs_timer_[i] := 0;

  ATG_SC := trunc(s.xval(ivar_ATG_SC + 2*i) + 0.2);

  NMaquinasDespachadasTG := NMaquinasDespachadasTG + ATG_SC;

  if ATG_SC > 0 then
  begin
    BTG_SC := s.xval(ivar_BTG_SC + 2*i);
    PDelPaso := PDelPaso + BTG_SC + ATG_SC * PMinTG;
  end
  else
    BTG_SC := 0;

  if NMaquinasDespachadasTG > 0 then
  begin
    CostoDirectoDelPaso := CostoDirectoDelPaso + ( BTG_SC * cvTG + ATG_SC * c0TG) * globs.HorasDelPaso;  //100 * AAcople +
  end;

  end;

  PdespachTG := PDelPaso;

  PdespachTV := PDelPaso - PdespachTG;


  EnergiaDespachada := EnergiaDespachada + PDelPaso * globs.HorasDelPaso;
  P[0] := PDelPaso;   //Como este modelo siempre es horario esta bien esto

  Ingreso_PorEnergia_ := EnergiaDespachada * pa.PagoPorEnergia_USD_MWh;
  {
 Xs_timer_[0] :=  s.xval( ivar_Xs_timer );
 if Xs_timer_[0] >= (5*0.9) then
      Xs_timer_[0] := 5
 else
 if Xs_timer_[0] <= AsumaCero then
      Xs_timer_[0] := 0;

 EnergiaDespachada := 0;

   ATG_SC := trunc(s.xval(ivar_ATG_SC) + 0.2);

   NMaquinasDespachadasTG := ATG_SC;
   NMaquinasDespachadasTV := 0;

   PDelPaso := 0;
   if ATG_SC > 0 then
   begin
     BTG_SC := s.xval(ivar_BTG_SC);
     PDelPaso := BTG_SC + ATG_SC * PMinTG;
   end
   else
     BTG_SC := 0;

   EnergiaDespachada := EnergiaDespachada + PDelPaso * globs.HorasDelPaso;
   P[0] := PDelPaso;   //Como este modelo siempre es horario esta bien esto

   PdespachTG := PDelPaso;

   if NMaquinasDespachadasTG > 0 then
   begin
     CostoDirectoDelPaso := ( BTG_SC * cvTG + ATG_SC * c0TG) *
       globs.HorasDelPaso;  //100 * AAcople +
   end
   else
     CostoDirectoDelPaso := 0;

 Ingreso_PorEnergia_ := EnergiaDespachada * pa.PagoPorEnergia_USD_MWh;
  }
end;


procedure TGTer_combinado_horario.opt_leerSolucion_combinado(s: TSimplex);
var
  ATG_SC, ATG_CC, ATV: integer;
  AAcople: integer;
  BTV, BTG_SC, BTG_CC: NReal;
  PDelPaso: NReal;
  EnergiaDespachada: NReal;
  i: integer;
  f: textfile;
  archi: string;
  pasoaux: integer;
  despachada: boolean;

begin
 PDelPaso := 0;
 EnergiaDespachada := 0;
 NMaquinasDespachadasTG := 0;
 costoDirectoDelPaso:= 0;
 {$IFDEF CC_dbgx}
 if (globs.kPaso_Opt = 790) and ((globs.CF.ordinalEstrellaActual = 21) or (globs.CF.ordinalEstrellaActual = 56) or (globs.CF.ordinalEstrellaActual = 57) or (globs.CF.ordinalEstrellaActual = 58) ) then
    i := 0;
 {$ENDIF}
 {$IFDEF CC_DBGx}
 pasoaux := globs.kPaso_Opt;
 archi := 'solucion_kPaso_' + IntToStr(pasoaux) + '.xlt';
{ if not FileExists(archi) then
 begin
    Assign(f, 'solucion_kPaso_' + IntToStr(pasoaux + 1) + '.xlt');
    rewrite(f);
    if FileExists('solucion_kPaso_' + IntToStr(pasoaux + 2) + '.xlt') then
      CloseFile(f);
 end; }
 AssignFile(f, archi);
 if FileExists(archi) then
   Append(f)
 else
   Rewrite(f);
 writeln(f, 'kEstrellita:  '+ #9 + IntToStr(globs.CF.ordinalEstrellaActual));
 {$ENDIF}

 for i := 0 to NmaxTG - 1 do
 begin
  Xs_timer_[i] :=  s.xval(ivar_Xs_timer + i);
  Xs_caldera_[i] :=  s.xval(ivar_Xs_caldera + i);

  if Xs_timer_[i] >= ((horasPurga_ + horasTVPleno_ - 1)*0.9) then
     Xs_timer_[i] := (horasPurga_ + horasTVPleno_ - 1)
  else
  if Xs_timer_[i] <= CasiCero_Simplex then
     Xs_timer_[i] := 0;

  if Xs_caldera_[i] >= (horasCalienteCaldera) then
     Xs_caldera_[i] := horasCalienteCaldera
  else
  if Xs_caldera_[i] <= CasiCero_Simplex then
     Xs_caldera_[i] := 0;

  ATG_SC := trunc(s.xval(ivar_ATG_SC + 2*i) + 0.2);
  ATG_CC := trunc(s.xval(ivar_ATG_CC + 2*i) + 0.2);
  AAcople := trunc(s.xval(ivar_AAcople + i) + 0.2);

  if ATG_SC > 0 then
  begin
    inc( NMaquinasDespachadasTG );
    BTG_SC := s.xval(ivar_BTG_SC + 2*i);
    PDelPaso := PDelPaso + BTG_SC + ATG_SC * PMinTG;
    CostoDirectoDelPaso := CostoDirectoDelPaso +
     ( BTG_SC * cvTG + c0TG ) * globs.HorasDelPaso;  //100 * AAcople +
  end
  else
    BTG_SC := 0;

  if ATG_CC > 0 then
  begin
    inc( NMaquinasDespachadasTG );
    BTG_CC := s.xval(ivar_BTG_CC + 2*i);
    PDelPaso := PDelPaso + BTG_CC + ATG_CC * PMinTG;
    CostoDirectoDelPaso := CostoDirectoDelPaso +
     (  BTG_CC * cvTG + c0TG + costoComb ) * globs.HorasDelPaso;  //100 * AAcople +
  end
  else
    BTG_CC := 0;

  {$IFDEF CC_DBGx}
  writeln(f, 'AAcople_' + IntToStr(i) + ': ' + #9 + IntToStr(AAcople));
  writeln(f, 'ATSC_' + IntToStr(i) + ': '+ #9 + IntToStr(ATG_SC));
  writeln(f, 'BTSC_' + IntToStr(i) + ': '+ #9 + FloatToStr(BTG_SC));
  writeln(f, 'ATCC_' + IntToStr(i) + ': '+ #9 + IntToStr(ATG_CC));
  writeln(f, 'BTCC_' + IntToStr(i) + ': '+ #9 + FloatToStr(BTG_CC));
  writeln(f, 'X_timer_' + IntToStr(i) + ': '+ #9 + FloatToStr(X_timer_[i]));
  writeln(f, 'Xs_timer_' + IntToStr(i) + ': '+ #9 + FloatToStr(Xs_timer_[i]));
  {$ENDIF}
  end;

  PdespachTG := PDelPaso;

  ATV := trunc(s.xval(ivar_ATV) + 0.2);
  NMaquinasDespachadasTV := ATV;

  if ATV > 0 then
  begin
    BTV := s.xval(ivar_BTV);
    PDelPaso := PDelPaso + BTV + ATV * PMinTV;
  end
  else
  begin
    BTV := 0;
  end;
  {$IFDEF CC_DBGx}
  writeln(f, 'ATV_' + IntToStr(i) + ': ' + #9 + IntToStr(ATV));
  writeln(f, 'BTV_' + IntToStr(i) + ': ' + #9 + FloatToStr(BTV));
  {$ENDIF}

  PdespachTV := PDelPaso - PdespachTG;

  CostoDirectoDelPaso := CostoDirectoDelPaso + (BTV * cvTV + ATV * c0TV ) * globs.HorasDelPaso;

  EnergiaDespachada := EnergiaDespachada + PDelPaso * globs.HorasDelPaso;
  P[0] := PDelPaso;   //Como este modelo siempre es horario esta bien esto

  Ingreso_PorEnergia_ := EnergiaDespachada * pa.PagoPorEnergia_USD_MWh;

  {$IFDEF CC_DBGx}
  writeln(f, 'PDelPaso: '+ #9 + FloatToStr(PDelPaso) + ';');
  closefile(f);
  {$ENDIF}
end;
//funcion para no permitir que se combinen distintas TG en distintos momentos
//se pueden combinar de a una, o todas juntas, pero no puede estar combinada una
//y dsp entrar otra
procedure TGTer_combinado_horario.ChequearYActualizarTimers;
var
  aux: NReal;
  aux_ant: NReal;
  i, k: integer;
  flg_TimerActivo: TDAOfBoolean;
begin
  //se puede estar enfriando y no querer prenderse CHEQUEAR ESTO
  //si Xs timer es mayor que X timer es porque esta calentando

  SetLength(flg_TimerActivo, NmaxTG);

  aux := 0;
  aux_ant:= 0;

  // busco timers activo
  for i := 0 to (NmaxTG - 1) do
  begin
    flg_TimerActivo[i] := false;
    if Xs_timer_[i] > 0 then
    begin
       aux := Xs_timer_[i];
       aux_ant := X_timer_[i];
       flg_TimerActivo[i] := aux >= aux_ant;
       if Xs_timer_[i] >= (horasPurga_ + horasTVPleno_ - 1) then
         Xs_caldera_[i] := horasCalienteCaldera;
    end;
  end;

  // desprolijo
  for i := 0 to NmaxTG - 1 do
  begin
   if flg_TimerActivo[i] then
   begin
    aux := round(X_timer_[i]);
    for k := 0 to NmaxTG - 1 do
      // si se quieren prender en distintos momentos los mando a todos a purga
      if flg_TimerActivo[k] then
        if Xs_timer_[k] > 0 then
          if round(X_timer_[k]) <> aux then
          begin
            Xs_timer_[k] := 1;
            Xs_timer_[i] := 1;
            aux := 1;
          end;
    end;
  end;

  SetLength(flg_TimerActivo, 0);

end;

procedure TGTer_combinado_horario.Sim_Cronica_Inicio;
var
  i: integer;
begin
  inherited Sim_Cronica_Inicio;
 // SetLength(X_timer_, 2);       //PRUEBA
 // SetLength(Xs_timer_, 2);
 for i := 0 to NmaxTG - 1 do
 begin
  X_timer_[i] := EstadoTimer;
  Xs_timer_[i] := X_timer_[i];
  X_caldera_[i] := EstadoCaldera;
  Xs_caldera_[i] := X_caldera_[i];
 end;

end;

function TGTer_combinado_horario.getNombreVar(ivar: integer; var nombre: string): boolean;
begin
  if (NMaquinasDisponiblesTG = 0) then
  begin
      getNombreVar_TG_indisponible(ivar, nombre);
  end
  else if ((NMaquinasDisponiblesTV = 0) or (NCalderasDisponibles = 0)) then
  begin
    getNombreVar_TV_indisponible(ivar, nombre);
  end
  else
  begin
    getNombreVar_combinado(ivar, nombre);
  end;

  {
  if (NMaquinasDisponiblesTG = 0) then
  begin
    Result := False;
  end
  else if (NMaquinasDisponiblesTV = 0) then
  begin
    getNombreVar_TV_indisponible(ivar, nombre);
  end
  else
  begin
    case purga_TV_[0] of
       0:
       begin
          getNombreVar_abierto_purga(ivar, nombre);
       end;
       1, 2:
       begin
          getNombreVar_combinado(ivar, nombre);
       end;
     end;
  end;
   }
end;

function TGTer_combinado_horario.getNombreVar_TG_indisponible(ivar: integer;
  var nombre: string): boolean;
begin

  if (ivar < ivar_Xs_timer) or (ivar >= (ivar_Xs_caldera + NmaxTG)) then
  begin
      Result := False;
  end
  else
  begin
    if (ivar < ivar_Xs_caldera) then
      nombre := self.Nombre + '_Xs_timer' + IntToStr(ivar - ivar_Xs_timer + 1)
    else
      nombre := self.Nombre + '_Xs_caldera' + IntToStr(ivar - ivar_Xs_caldera + 1);

    Result := True;
  end;

end;

function TGTer_combinado_horario.getNombreVar_TV_indisponible(ivar: integer;
  var nombre: string): boolean;
begin

  if (NMaquinasDisponiblesTG = 0) then
  begin
    Result := False;
  end
  else if (ivar < ivar_ATG_SC) or (ivar >= (ivar_Xs_caldera + NmaxTG)) then
  begin
      Result := False;
  end
  else
  begin
    if ivar < ivar_BTG_SC then
      nombre := self.Nombre + '_A_TG_SC[MW]' + IntToStr(ivar - ivar_ATG_SC + 1)
    else if (ivar < ivar_Xs_timer) and (((ivar - ivar_ATG_SC) mod (NmaxTG) = 0)) then
      nombre := self.Nombre + '_A_TG_SC[MW]' + IntToStr((ivar - ivar_ATG_SC) div (NmaxTG) + 1)
    else if (ivar < ivar_Xs_timer) and (((ivar - ivar_BTG_SC) mod (NmaxTG) = 0)) then
      nombre := self.Nombre + '_B_TG_SC' + IntToStr((ivar - ivar_BTG_SC) div (NmaxTG) + 1)
    else if (ivar < ivar_Xs_caldera) then
      nombre := self.Nombre + '_Xs_timer' + IntToStr(ivar - ivar_Xs_timer + 1)
    else
      nombre := self.Nombre + '_Xs_caldera' + IntToStr(ivar - ivar_Xs_caldera + 1);

    Result := True;
  end;

  {

  if (ivar < ivar_ATG_SC) or (ivar >= (ivar_Xs_timer + 1)) then
  begin
      Result := False;
  end
  else
  begin
    if ivar < ivar_BTG_SC then
      nombre := self.Nombre + '_A_TG_SC[MW]' + IntToStr(ivar - ivar_ATG_SC + 1)
    else if ivar < ivar_Xs_timer then
      nombre := self.Nombre + '_B_TG_SC' + IntToStr(ivar - ivar_BTG_SC + 1)
    else
      nombre := self.Nombre + '_Xs_timer' + IntToStr(ivar - ivar_Xs_timer + 1);

    Result := True;
  end;
   }
end;


function TGTer_combinado_horario.getNombreVar_combinado(ivar: integer;
  var nombre: string): boolean;
begin

  if (NMaquinasDisponiblesTG = 0) then
  begin
    Result := False;
  end
  else if (ivar < ivar_ATG_SC) or (ivar >= (ivar_Xs_caldera + NmaxTG)) then
  begin
      Result := False;
  end
  else
  begin
    if ivar < ivar_BTG_SC then
      nombre := self.Nombre + '_A_TG_SC[MW]' + IntToStr(ivar - ivar_ATG_SC + 1)
    else if (ivar < ivar_ATG_CC) and (((ivar - ivar_ATG_SC) mod (NmaxTG) = 0)) then
      nombre := self.Nombre + '_A_TG_SC[MW]' + IntToStr((ivar - ivar_ATG_SC) div (NmaxTG) + 1)
    else if (ivar < ivar_ATG_CC) and (((ivar - ivar_BTG_SC) mod (NmaxTG) = 0)) then
      nombre := self.Nombre + '_B_TG_SC' + IntToStr((ivar - ivar_BTG_SC) div (NmaxTG) + 1)
    else if ivar < ivar_BTG_CC then
      nombre := self.Nombre + '_A_TG_CC[MW]' + IntToStr(ivar - ivar_ATG_CC + 1)
    else if (ivar < ivar_ATV) and (((ivar - ivar_ATG_CC) mod (NmaxTG) = 0)) then
      nombre := self.Nombre + '_A_TG_CC[MW]' + IntToStr(((ivar - ivar_ATG_CC) div (NmaxTG)) + 1)
    else if (ivar < ivar_ATV) and (((ivar - ivar_BTG_CC) mod (NmaxTG) = 0)) then
      nombre := self.Nombre + '_B_TG_CC' + IntToStr(((ivar - ivar_BTG_CC) div (NmaxTG)) + 1)
    else if ivar < ivar_BTV then
      nombre := self.Nombre + '_ATV[MW]' + IntToStr(ivar - ivar_ATV + 1)
    else if ivar < ivar_AAcople then
      nombre := self.Nombre + '_BTV' + IntToStr(ivar - ivar_BTV + 1)
    else if ivar < ivar_Xs_timer then
      nombre := self.Nombre + '_AAcople' + IntToStr(ivar - ivar_AAcople + 1)
    else if ivar < ivar_Xs_caldera then
      nombre := self.Nombre + '_Xs_timer' + IntToStr(ivar - ivar_Xs_timer + 1)
    else
      nombre := self.Nombre + '_Xs_caldera' + IntToStr(ivar - ivar_Xs_caldera + 1);

    Result := True;
  end;

end;

function TGTer_combinado_horario.getNombreRes(ires: integer; var nombre: string): boolean;
begin
  if (NMaquinasDisponiblesTG = 0) then
    Result := getNombreRes_TG_indisponible(ivar, nombre)
  else
    if ((NMaquinasDisponiblesTV = 0) or (NCalderasDisponibles = 0)) then
      Result:= getNombreRes_TV_indisponible(ivar, nombre)
    else
      Result:= getNombreRes_combinado(ires, nombre);
end;

function TGTer_combinado_horario.getNombreRes_TG_indisponible(ires: integer;
  var nombre: string): boolean;
begin
  if (ires < jres_dinTimer) or (ires > jres_Ultima) then
      Result := False
  else
  begin
    if ires < jres_dinCaldera then
      nombre := self.nombre + '_res-dinTimer' + IntToStr(ires - jres_dinTimer + 1)
    else
      nombre := self.nombre + '_res-dinCaldera' + IntToStr(ires - jres_dinCaldera + 1);
    Result := True;
  end;
end;

function TGTer_combinado_horario.getNombreRes_TV_indisponible(ires: integer;
  var nombre: string): boolean;
begin
  if (NMaquinasDisponiblesTG = 0) then
    Result := False
  else
    if (ires < jres_dinTimer) or (ires > jres_Ultima) then
      Result := False
  else
  begin
    if ires < jres_Acople_BTG_SC then
      nombre := self.nombre + '_res-dinTimer' + IntToStr(ires - jres_dinTimer + 1)
    else if ires < jres_Acople_TimerxBTG_SC then
      nombre := self.nombre + '_res-Acople_BTG_SC' + IntToStr(ires - jres_Acople_BTG_SC + 1)
    else if ires < jres_dinCaldera then
      nombre := self.nombre + '_res-Acople_TimerxBTG_SC' + IntToStr(ires - jres_Acople_TimerxBTG_SC + 1)
    else
      nombre := self.nombre + '_res-dinCaldera' + IntToStr(ires - jres_dinCaldera + 1);
    Result := True;
  end;
end;

function TGTer_combinado_horario.getNombreRes_combinado(ires: integer;
  var nombre: string): boolean;
begin
  if (NMaquinasDisponiblesTG = 0) then
    Result := False
  else
    if (ires < jres_dinTimer) or (ires > jres_Ultima) then
      Result := False
    else
    begin
      if ires < jres_Acople_BTG_SC then
        nombre := self.nombre + '_res-dinTimer' + IntToStr(ires - jres_dinTimer + 1)
      else if ires < jres_Acople_BTG_CC then
        nombre := self.nombre + '_res-Acople_BTG_SC' + IntToStr(ires - jres_Acople_BTG_SC + 1)
      else if ires < jres_Acople_BTV then
        nombre := self.nombre + '_res-Acople_BTG_CC' + IntToStr(ires - jres_Acople_BTG_CC + 1)
      else if ires < jres_Acople_TVxTG then
        nombre := self.nombre + '_res-Acople_BTV' + IntToStr(ires - jres_Acople_BTV + 1)
      else if ires < jres_Acople_TimerxBTG_SC then
        nombre := self.nombre + '_res-Acople_TVxTG' + IntToStr(ires - jres_Acople_TVxTG + 1)
      else if ires < jres_Acople_TimerxBTG_CC then
        nombre := self.nombre + '_res-Acople_TimerxBTG_SC' + IntToStr(ires - jres_Acople_TimerxBTG_SC + 1)
      else if ires < jres_dinCaldera then
        nombre := self.nombre + '_res-Acople_TimerxBTG_CC' + IntToStr(ires - jres_Acople_TimerxBTG_CC + 1)
      else if ires < jres_Ultima then
        nombre := self.nombre + '_res-dinCaldera' + IntToStr(ires - jres_dinCaldera + 1)
      else if (pa.HayRestriccionEmaxPasoDeTiempo) then
      begin
      end;//por ahora nada
    Result := True;
  end;
end;

procedure TGTer_combinado_horario.PubliVars;
begin
  inherited PubliVars;
  PublicarVariableNI('NDespachadasTGs', '-', NMaquinasDespachadasTG, True);
  PublicarVariableNI('NDespachadasTVs', '-', NMaquinasDespachadasTV, 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);
  PublicarVariableVR('Timer', '[h]', 6, 1, X_timer_, False, True);
  PublicarVariableVR('EstadoCaldera', '[h]', 6, 1, X_caldera_, False, True);
  PublicarVariableNR('PTG', '[MW]', 6, 1, PdespachTG, False);
  PublicarVariableNR('PTV', '[MW]', 6, 1, PdespachTV, False);

end;

destructor TGTer_combinado_horario.destroy;
begin

  setlength( X_timer_, 0 );
  setlength( Xs_timer_, 0 );
  setlength( X_caldera_, 0 );
  setlength( Xs_caldera_, 0 );
  setlength( purga_TV_, 0 );
  setlength( pendTV_, 0 );
  setlength( factorTV_, 0 );

  inherited destroy;
end;


procedure AlInicio;
begin
  registrarClaseDeCosa(TGTer_combinado_horario.ClassName, TGTer_combinado_horario);
  registrarClaseDeCosa(TFichaGTer_combinado_horario.ClassName, TFichaGTer_combinado_horario);
end;

procedure AlFinal;
begin
end;

end.
