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

{$IFDEF FPC}
  {$MODE Delphi}
{$ENDIF}

interface

uses
  Math,
  SysUtils, Classes, xmatdefs, uGTer, uNodos,
  uglobs,
  usimplex,
  ufichasLPD,
  ufechas,
  ucosa, ucosaConNombre, uranddispos,
  uconstantesSimSEE,
  uFuentesAleatorias, uActores, uEstados;

resourcestring
  rsGeneradorTermicoConTiempoReparacion =
    'Generador térmico básico con tiempo de reparación';

{TGter_Basico_TRep es un generador térmico sin mínimo técnico y con estado.
Los valores posibles de este estado son Disponible (D) e Indisponible (I). Las
transiciones de estado se dan con las siguientes probabilidades.

D->D: 1-beta ; probabilidad de permanecer disponible
D->I: beta ; probabilidad de romperse
I->D: alfa ; probabilidad de repararse
I->I: 1-alfa ; probabilidad de permanecer indisponible

El generador recibe como parámetros la probabilidad acumulada de estar en el
estado disponible (disp) y el tiempo promedio de reparación en horas (tRepHoras)

Con la matriz de transición podemos ver que estando en el estado I, la
probabilidad de salir de el tras k pasos es

kPaso Prob
1     alfa
2     (1-alfa) * alfa
.
.
.
n     (1-alfa)^(n-1) * alfa

Siendo nRep la variable aleatoria que cuenta la cantidad de sorteos para volver
al estado D se tiene

<nRep> = alfa * sumatoria k=1..n (k * (1-alfa)^k-1) = alfa * (1 / alfa^2) = 1 / alfa
Como es un sorteo por paso se tiene
tRepHoras = <nRep> * horasDelPaso => tRepHoras = horasDelPaso / alfa =>
alfa = horasDelPaso / tRepHoras

Para concluir que sumatoria k=1..n (k * (1-alfa)^k-1) = 1 / alfa^2 usamos lo siguiente:
s(q) = q^0 + ... + q^n
q * s(q) = q^1 + ... + q^(n + 1)
s(q) - q * s(q) = q^0 - q^(n + 1)
(1 - q) * s(q) = 1 - q^(n + 1), si n -> infinto y q < 0 => q^(n+1) -> 0
s(q) = 1 / (1 - q) => s'(q) = 1 / (1 - q)^2
s'(q) = sumatoria k = 0..n (k * q^(k-1)) = sumatoria k = 1..n (k * q^(k-1))


Viendo la matriz de transiciones podemos concluir que la probabilidad de llegar
en un sorteo al estado disponible pD es:
pD = pD * (1-beta) + (pI) * alfa
pD = pD * (1-beta) + (1 - pD) * alfa
pD = pD * (1 - beta - alfa) + alfa
alfa * pD - alfa = - (pD * beta)
beta = alfa / pD - alfa
}

type

  { TFichaGTer_Basico_TRep }

  TFichaGTer_Basico_TRep = class(TFichaLPD)

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

    PMax: NReal; // [MW] Potencia Máxima Por maquina
    cv: NReal; // Costo:= cv* P
    indicePreciosPorCombustible: TFuenteAleatoria;
    bornePreciosPorCombustible: string;
    disp: NReal; //Probabilidad de estar en el estado disponible
    HayRestriccionEmaxPasoDeTiempo: boolean; // indica si se aplica la restricción
    EmaxPasoDeTiempo: NReal; // Energía maxima generable en un paso de tiempo
    tRepHoras: NReal;   //tiempo promedio de reparación en horas
    PagoPorDisponibilidad_MWh: NReal; // [USD/MWh] Pago por Potencia
    PagoPorEnergia_MWh: NReal; // [USD/MWh] Pago por Energía

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

    nroBornePreciosPorCombustible: integer;

    constructor Create(capa: integer; fecha: TFecha; periodicidad: TPeriodicidad;
      PMax: NReal; cv: NReal; indicePreciosPorCombustible: TFuenteAleatoria;
      bornePreciosPorCombustible: string; disp: NReal;
      HayRestriccionEmaxPasoDeTiempo: boolean; EmaxPasoDeTiempo: NReal;
      tRepHoras: NReal; PagoPorDisponibilidad_MWh: NReal; // [USD/MWh] Pago por Potencia
      PagoPorEnergia_MWh: NReal // [USD/MWh] Pago por Energía
      );

    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_Basico es una potencia minima y maxima constante en todos los postes
  // y un costo variable cv [USD/MWh] tambien igual en todos los postes
  // y un costo variable cv_min "hasta el minimo" igual al costo de produccion en USD/MWh cuando
  // la central esta generado en el minimo.
  TGTer_Basico_TRep = class(TGTer)
  public
    pa: TFichaGTer_Basico_TRep;

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

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

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


    // resultado de los sorteos de disponibilidad y del mantenimiento programado
    X_NMaquinasDisponibles: integer;
    //El estado siguiente
    Xs_NMaquinasDisponibles: integer;


    PMaxDisponible: NReal;

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

    alfa, beta: NReal;
    estadoInicial: integer; //Número de máquinas disponibles al inicio
    sorteo: NReal;          //Realiza un sorteo uniforme [0, 1) y guarda su valor
    //Al preparar paso se usa en conjunto con el número
    //de máquinas disponibles para saber el próximo número
    //de máquinas disponibles

    //Variación de costo futuro por transición de estado en USD
    dCF: NReal;

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


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

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

    function PotenciaFirme: NReal; override;

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

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

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

    procedure EvolucionarEstado; override;
    procedure Sim_Paso_Fin; override;

    //      procedure Sim_Cronica_Fin; override;

    procedure opt_nvers(var ivar, ivae, ires: integer); override;
    procedure optx_nvxs(var ixr, ixd, iauxNReal, iauxInt: integer); override;
    procedure optx_RegistrarVariablesDeEstado(adminEstados: TAdminEstados);
      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;

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

procedure cambioFichaPD_TGter_Basico_TRep(actor: TActor);
procedure cambioFichaUnidades_TGter_Basico_TRep(actor: TActor);

procedure AlInicio;
procedure AlFinal;

implementation

{var
  nPasosDisponible, nPasosNoDisponibleSeguidos: Integer;
  fdbg: TextFile;}

//----------------------------------
// Métodos de TFichaGTer_Basico_TRep
//==================================
constructor TFichaGTer_Basico_TRep.Create(capa: integer; fecha: TFecha;
  periodicidad: TPeriodicidad; PMax: NReal; cv: NReal;
  indicePreciosPorCombustible: TFuenteAleatoria; bornePreciosPorCombustible: string;
  disp: NReal; HayRestriccionEmaxPasoDeTiempo: boolean; EmaxPasoDeTiempo: NReal;
  tRepHoras: NReal; PagoPorDisponibilidad_MWh: NReal; // [USD/MWh] Pago por Potencia
  PagoPorEnergia_MWh: NReal // [USD/MWh] Pago por Energía
  );
begin
  inherited Create(capa, fecha, periodicidad);
  self.PMax := PMax;
  self.cv := cv;
  self.indicePreciosPorCombustible := indicePreciosPorCombustible;
  self.bornePreciosPorCombustible := bornePreciosPorCombustible;
  self.disp := disp;
  self.HayRestriccionEmaxPasoDeTiempo := HayRestriccionEmaxPasoDeTiempo;
  self.EmaxPasoDeTiempo := EmaxPasoDeTiempo;
  self.tRepHoras := tRepHoras;

  self.PagoPorDisponibilidad_MWh := PagoPorDisponibilidad_MWh;
  self.PagoPorEnergia_MWh := PagoPorEnergia_MWh;
end;

function TFichaGTer_Basico_TRep.Rec: TCosa_RecLnk;
begin
  Result := inherited Rec;
  Result.addCampoDef('PMax', PMax, 0, 6);
  Result.addCampoDef('cv', cv, 0, 6);
  Result.addCampoDef_ref('indicePreciosPorCombustible',
    TCosa(indicePreciosPorCombustible), self, 0, 6);
  Result.addCampoDef('disp', disp, 0, 6);
  Result.addCampoDef('HayRestriccionEmaxPasoDeTiempo',
    HayRestriccionEmaxPasoDeTiempo, 0, 6);
  Result.addCampoDef('EmaxPasoDeTiempo', EmaxPasoDeTiempo, 0, 6);
  Result.addCampoDef('tRepHoras', tRepHoras, 0, 6);
  Result.addCampoDef('PMax', PMax, 6);
  Result.addCampoDef('cv', cv, 6);
  Result.addCampoDef_ref('indicePreciosPorCombustible',
    TCosa(indicePreciosPorCombustible), self, 6);
  Result.addCampoDef('bornePreciosPorCombustible', bornePreciosPorCombustible, 6);
  Result.addCampoDef('disp', disp, 6);
  Result.addCampoDef('HayRestriccionEmaxPasoDeTiempo',
    HayRestriccionEmaxPasoDeTiempo, 6);
  Result.addCampoDef('EmaxPasoDeTiempo', EmaxPasoDeTiempo, 6);
  Result.addCampoDef('tRepHoras', tRepHoras, 6);
  Result.addCampoDef('PagoPorDisponibilidad_MWh', PagoPorDisponibilidad_MWh, 6);
  Result.addCampoDef('PagoPorEnergia_MWh', PagoPorEnergia_MWh, 6);
end;

procedure TFichaGTer_Basico_TRep.BeforeRead(version, id_hilo: integer);
begin
  inherited BeforeRead(version, id_hilo);
  self.PagoPorDisponibilidad_MWh := 0;
  self.PagoPorEnergia_MWh := 0;
end;

procedure TFichaGTer_Basico_TRep.AfterRead(f: TArchiTexto);
begin
  inherited AfterRead(f);
  if f.Version < 6 then
  begin
    self.indicePreciosPorCombustible := nil;
    self.bornePreciosPorCombustible := '';
  end;
end;



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

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

destructor TFichaGTer_Basico_TRep.Destroy;
begin
  inherited Destroy;
end;




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

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

  writeln(f, charIndentacion, 'NMaquinasDisponibles= ', X_NMaquinasDisponibles);
  writeln(f, charIndentacion, 'disp= ', pa.disp);
  writeln(f, charIndentacion, 'alfa= ', alfa);
  writeln(f, charIndentacion, 'beta= ', beta);

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

  writeln(f);
end;

class function TGTer_Basico_TRep.TipoFichaLPD: TClaseDeFichaLPD;
begin
  Result := TFichaGTer_Basico_TRep;
end;

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

{$ENDIF}

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

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

procedure TGTer_Basico_TRep.BeforeRead(version, id_hilo: integer);
begin
  inherited BeforeRead(version, id_hilo);
  P := nil;
  pa := nil;
  nodo := nil;
end;

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




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

procedure TGTer_Basico_TRep.RegistrarParametrosDinamicos(
  CatalogoReferencias: TCatalogoReferencias);
var
  i: integer;
  ficha: TFichaGTer_Basico_TRep;
begin
  uActores.procCambioFichaUnidades := @cambioFichaUnidades_TGter_Basico_TRep;
  inherited registrarParametrosDinamicos(CatalogoReferencias);
  uActores.procCambioFichaUnidades := nil;
  lpd.expandirFichas(CatalogoReferencias, globs);
  lpd.RegistrarFichasAActualizar(Self, globs.ActualizadorLPD, @pA,
    nil, @cambioFichaPD_TGter_Basico_TRep);

  for i := 0 to lpd.Count - 1 do
  begin
    ficha := TFichaGTer_Basico_TRep(lpd[i]);
    if ficha.indicePreciosPorCombustible <> nil then
      ficha.nroBornePreciosPorCombustible :=
        ficha.indicePreciosPorCombustible.IdBorne(ficha.bornePreciosPorCombustible);
  end;
end;

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

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

class function TGTer_Basico_TRep.DescClase: string;
begin
  Result := rsGeneradorTermicoConTiempoReparacion;
end;

procedure TGTer_Basico_TRep.PosicionarseEnEstrellita;
begin
  Self.X_NMaquinasDisponibles := globs.CF.xd[ixd];
  PMaxDisponible := pa.PMax * X_NMaquinasDisponibles;
end;

procedure TGTer_Basico_TRep.ActualizarEstadoGlobal(flg_Xs: boolean);
begin

  globs.CF.xd[ixd] := Self.X_NMaquinasDisponibles;
end;

procedure TGTer_Basico_TRep.Sim_Cronica_Inicio;
begin
  inherited Sim_Cronica_Inicio;
  X_NMaquinasDisponibles := estadoInicial;
end;

procedure TGTer_Basico_TRep.SorteosDelPaso(sortear: boolean);
begin
  if hayForzamientos or globs.ObligarDisponibilidad_1_ then
  begin
    sorteo := 1.1; //No hay chance de cambiar de estado
    PMaxDisponible := pa.PMax * paUnidades.nUnidades_Operativas[0];
  end
  else
  if sortear then
  begin
    sorteo := SorteadorUniforme.rnd;
    PMaxDisponible := pa.PMax * paUnidades.nUnidades_Operativas[0];
  end
  else
  begin
    sorteo := 1.1; //No hay chance de cambiar de estado
    PMaxDisponible := pa.PMax * pa.disp * paUnidades.nUnidades_Operativas[0];
  end;
end;

procedure TGTer_Basico_TRep.PrepararPaso_ps;
begin
  if paUnidades.nUnidades_Operativas[0] = 0 then
    exit;

  if X_NMaquinasDisponibles = 0 then   //Estoy en el estado I
  begin
    if (paUnidades.nUnidades_Operativas[0] <> 0) and (sorteo < alfa) then //I-->D
    begin
      Xs_NMaquinasDisponibles := 1;
      dCF := globs.CF.deltaCosto_vxd_continuo(ixd, globs.kPaso_Opt + 1, 1) *
        globs.fActPaso;
      assert(dcf <= 0);
    end
    else //I-->I
    begin
      Xs_NMaquinasDisponibles := 0;
      dCF := 0;
    end;
  end

  //Estoy en el estado D
  else if X_NMaquinasDisponibles = 1 then
  begin
    if (sorteo < beta) or (paUnidades.nUnidades_Operativas[0] = 0) then //D-->I
    begin
      Xs_NMaquinasDisponibles := 0;
      dCF := globs.CF.deltaCosto_vxd_continuo(ixd, globs.kPaso_Opt + 1, -1) *
        globs.fActPaso;
      assert(dcf >= 0);
    end
    else //D-->D
    begin
      Xs_NMaquinasDisponibles := 1;
      dCF := 0;
    end;
  end
  else
    raise Exception.Create(
      'TGTer_Basico_TRep aun no soporta mas de una unidad disponible');

  if pa.indicePreciosPorCombustible <> nil then
    cv := pa.cv * pa.indicePreciosPorCombustible.bornera[
      pa.nroBornePreciosPorCombustible]
  else
    cv := pa.cv;
end;

procedure TGTer_Basico_TRep.Sim_Paso_Fin;
var
  iposte: integer;
begin
  for iposte := 0 to high(P) do
    potMedia_despachada := potMedia_despachada + P[iposte] * globs.durpos[iposte];
  potMedia_despachada := potMedia_despachada * globs.invHorasDelPaso;
end;

procedure TGTer_Basico_TRep.opt_nvers(var ivar, ivae, ires: integer);
begin

  if paUnidades.nUnidades_Operativas[0] = 0 then
    exit; // si no hay máquinas no juego
  Self.ivar := ivar;
  ivar := ivar + globs.NPostes;
  if pA.HayRestriccionEmaxPasoDeTiempo then
  begin
    Self.ires := ires;
    Inc(ires);
  end;
end;

procedure TGTer_Basico_TRep.optx_nvxs(var ixr, ixd, iauxNReal, iauxInt: integer);
begin
  Self.ixd := ixd;
  ixd := ixd + 1;
end;

procedure TGTer_Basico_TRep.optx_RegistrarVariablesDeEstado(
  adminEstados: TAdminEstados);
begin
  adminEstados.Registrar_Discreta(ixd, 2, Self.Nombre + '_Disponible', 'bool');
end;

procedure TGTer_Basico_TRep.opt_cargue(s: TSimplex);
var
  inodores: integer;
  iposte: integer;
begin
  if paUnidades.nUnidades_Operativas[0] = 0 then
    exit; // si no hay máquinas no juego

  if X_NMaquinasDisponibles = 0 then
    exit // si no hay máquinas no juego
  else  // controlamos el uso correcto del modelo.
  if X_NMaquinasDisponibles > 1 then
    raise Exception.Create(
      'Por ahora TGer_ArranqueParada soporta solamente centrales de una sóla máquina');

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

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

procedure TGTer_Basico_TRep.opt_fijarRestriccionesDeCaja(s: TSimplex);
var
  iposte: integer;
begin
  if paUnidades.nUnidades_Operativas[0] = 0 then
    exit; // si no hay máquinas no juego
  if X_NMaquinasDisponibles = 0 then
    exit; // si no hay máquinas no juego

  if (not hayForzamientos) then
  begin
    // Le fijamos como cota superior PMax a la potencia en todos los postes
    for iposte := 0 to globs.NPostes - 1 do
      s.cota_sup_set(ivar + iposte, PMaxDisponible);
  end
  else
  begin            {
    for iposte := 0 to globs.NPostes - 1 do
      s.FijarVariable(ivar + iposte, paForzamiento.P[0]);
      }
    for iposte := 0 to globs.NPostes - 1 do
    begin
      if paForzamiento.flg_fijarP then
        s.FijarVariable(ivar + iposte, paForzamiento.P[0])
      else if paForzamiento.activar_Pmax then
        s.cota_sup_set(ivar + iposte, paForzamiento.P_max[0])
      else if paForzamiento.activar_Pmin then
        s.cota_inf_set(ivar + iposte, paForzamiento.P_min[0]);
    end;
  end;
end;

procedure TGTer_Basico_TRep.opt_leerSolucion(s: TSimplex);
var
  iposte: integer;
  m: NReal;
  EnergiaDespachada: NReal;

begin
  maxNMaquinasDespachadas := 0;
  Ingreso_PorEnergia_ := 0;
  Ingreso_PorDisponibilidad_ :=
    PMaxDisponible * pa.PagoPorDisponibilidad_MWh * globs.HorasDelPaso;
  CostoDirectoDelPaso := 0;
  if (paUnidades.nUnidades_Operativas[0] = 0) or (X_NMaquinasDisponibles = 0) then
  begin
    vclear(P);
    for iposte := 0 to globs.NPostes - 1 do
    begin
      Lambda_P[iPoste] := 0;
      cv_Spot[iPoste] := get_cv_falla;
      // para que en el ordenamiento quede último, con costo variable igual a la falla;
    end;
    vclear(NMaquinasDespachadas);
    vclear(costos);
    exit;
  end;

  EnergiaDespachada := 0;
  // recuperamos los valores de Potencia despachada
  for iposte := 0 to globs.NPostes - 1 do
  begin
    P[iposte] := s.xval(ivar + iposte);
    Lambda_P[iPoste] := s.xmult(ivar + iposte) / globs.DurPos[iposte];
    cv_Spot[iPoste] := Nodo.cmarg[iposte] - Lambda_P[iPoste];
    if P[iposte] > GTER_PRECISIONPOTENCIANMAQS then
    begin
      NMaquinasDespachadas[iposte] :=
        Math.Ceil((P[iposte] - GTER_PRECISIONPOTENCIANMAQS) / pa.PMax);
      m := P[iposte] * globs.DurPos[iposte];
      EnergiaDespachada := EnergiaDespachada + m;
      costos[iposte] := m * cv;
      CostoDirectoDelPaso := CostoDirectoDelpaso + costos[iposte];
      if NMaquinasDespachadas[iposte] > maxNMaquinasDespachadas then
        maxNMaquinasDespachadas := NMaquinasDespachadas[iposte];
    end
    else
    begin
      NMaquinasDespachadas[iposte] := 0;
      costos[iposte] := 0;
    end;
  end;
  Ingreso_PorEnergia_ := EnergiaDespachada * pa.PagoPorEnergia_MWh;
end;

procedure TGTer_Basico_TRep.EvolucionarEstado;
begin
  X_NMaquinasDisponibles := Xs_NMaquinasDisponibles;
end;


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

function TGTer_Basico_TRep.getNombreRes(ires: integer; var nombre: string): boolean;
begin
  if X_NMaquinasDisponibles = 0 then
    Result := False
  else
  if pa.HayRestriccionEmaxPasoDeTiempo and (ires = self.ires) then
  begin
    nombre := self.nombre + '_res-EMax';
    Result := True;
  end
  else
    Result := False;
end;

procedure TGTer_Basico_TRep.PubliVars;
begin
  inherited PubliVars;

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

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

  PublicarVariableNI('NMaqsDisponibles', '-', X_NMaquinasDisponibles, True);

  PublicarVariableNR('cv', '[USD/MW]', 6, 1, cv, False);
  PublicarVariableNI('MaxNMaqsDespachadasEnElPaso', '-', maxNMaquinasDespachadas, False);
  PublicarVariableNR('PMáxDisponible', '[MW]', 6, 1, PMaxDisponible, False);
  PublicarVariableNR('PMediaDespachada', '[MW]', 6, 1, potMedia_despachada, False);
end;

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

procedure cambioFichaPD_TGter_Basico_TRep(actor: TActor);
begin
  //alfa:= horasDelPaso / tRepHoras
  TGTer_Basico_TRep(actor).alfa :=
    TGTer_Basico_TRep(actor).globs.HorasDelPaso / TGTer_Basico_TRep(actor).pa.tRepHoras;
  //beta:= (alfa / probD) - alfa
  TGTer_Basico_TRep(actor).beta :=
    (TGTer_Basico_TRep(actor).alfa / TGTer_Basico_TRep(actor).pa.disp) -
    TGTer_Basico_TRep(actor).alfa;
end;

procedure cambioFichaUnidades_TGter_Basico_TRep(actor: TActor);
begin
end;

procedure AlInicio;
begin
  registrarClaseDeCosa(TGTer_Basico_TRep.ClassName, TGTer_Basico_TRep);
  registrarClaseDeCosa(TFichaGTer_Basico_TRep.ClassName, TFichaGTer_Basico_TRep);
{  AssignFile(fdbg, uconstantesSimSEE.getDir_Dbg + 'RachasRoto.xlt');
  rewrite(fdbg);}
end;

procedure AlFinal;
begin
  //  CloseFile(fdbg);
end;

end.
