unit usolartermico;

interface

uses
  Math,
  SysUtils, Classes, xmatdefs, uGeneradores, uNodos,
  uglobs,
  usimplex,
  ufichasLPD,
  ufechas,
  ucosa, ucosaConNombre,
  uconstantesSimSEE,
  uFuentesAleatorias;

resourcestring
  rsGeneradorSolarTermico =
    'Generador Solar Térmico';


{T_SolarTermico es un generador térmico on-off por poste
cuya potencia para cada poste, es tomada de una fuente aleatoria.}

type

  { TFichaSolarTermico }

  TFichaSolarTermico = class(TFichaLPD)
  public
  (**************************************************************************)
  (*              A T R I B U T O S   P E R S I S T E N T E S               *)
  (**************************************************************************)

    PMin, PMax: NReal; //[MW] Potencias Mínima y Máxima por máquina.
    cv_min, cv: NReal; // Costo:=cv_min* Pmin + cv*(P-Pmin)
    indicePreciosPorCombustible: TFuenteAleatoria;
    bornePreciosPorCombustible: string;
    disp: NReal; //Probabilidad de estar en el estado disponible
    HayAlmacenDeEnergia: boolean; // indica si se utiliza almacenamiento de energia
    tRepHoras: NReal;   //tiempo promedio de reparación en horas
    PagoPorDisponibilidad_USD_MWh: NReal; // [USD/u-h] Pago por Potencia
    PagoPorEnergia_USD_MWh: NReal; // [USD/MWh] Pago por Potencia
    famp: NReal;  //multiplicador de la fuente aleatoria de potencia solar
    rendimiento_almacen: TDAofNReal; //rendimientos de almacenamiento por postes
  (**************************************************************************)


    nroBornePreciosPorCombustible: integer;

    constructor Create(capa: integer; fecha: TFecha; periodicidad: TPeriodicidad;
      PMin, PMax: NReal; cv_min, cv: NReal;
      indicePreciosPorCombustible: TFuenteAleatoria; bornePreciosPorCombustible: string;
      disp: NReal; HayAlmacenDeEnergia: boolean; tRepHoras: NReal;
      PagoPorDisponibilidad_USD_MWh: NReal;
      PagoPorEnergia_USD_MWh: NReal; FAmp: NReal; rendimiento_almacen: TDAofNReal);

     
    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;
    procedure Free; override;
private
    aux_b: boolean;
    aux_r: NReal;
  end;

  // T_SolarTermico tiene una potencia mínima y máxima constante en todos los postes,
  // un costo variable cv [USD/MWh] también 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 mínimo.

  { TSolarTermico }

  TSolarTermico = class(TGenerador)
  public
   //Array de fuentes aleatorias de potencias
   fuentesAleatoriasPotenciasPorPoste: TListaDeCosas;

    pa: TFichaSolarTermico;

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

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

    cve: NReal; // costo por la energía adicional a CV

    PMin: NReal; //MW minimo

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

    NMaquinasDisponibles: integer;
    PMaxDisponiblePorPoste: TDAofNReal;
    BMax: TDAofNReal; //maximo ente 0 y PMaxDisponiblePorPoste-PMin
    factor_P: NReal;  //PMax disponible
    EMaxPasoDeTiempo: NReal; // auxiliar para almacenar energía del paso

    constructor Create(capa: integer; nombre: string; nacimiento, muerte: TFecha;
      lpdUnidades, lpd: TFichasLPD; nodo: TNodo;
  flg_CalcularGradienteDeInversion: boolean; TonCO2xMWh: NReal; LowCostMustRun,
  CleanDevelopmentMechanism: boolean;
  fuentesAleatoriasPotenciasPorPoste: TListaDeCosas;
  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;
    //crea la memoria, inicializa lo que se necesite para empezar.
    procedure RegistrarParametrosDinamicos( CatalogoReferencias: TCatalogoReferencias  ); override;
    //prepara todas las fichas de parametros dinámicos

    function PotenciaFirme: NReal; override;
    //se usa para calcular la potencia firme del sistema.

    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 Sim_Cronica_Inicio; override;
    procedure SorteosDelPaso(sortear: boolean); override;
    procedure PrepararPaso_ps; override;

    procedure Sim_Paso_Fin; override;

    procedure opt_nvers(var ivar, ivae, ires: integer); override;

    procedure opt_cargue(s: TSimplex); override;
    procedure opt_fijarRestriccionesDeCaja(s: TSimplex); override;
    procedure opt_leerSolucion(s: TSimplex); override;


    function getNombreVar(ivar: integer; var nombre: string): boolean; override;
    function getNombreRes(ires: integer; var nombre: string): boolean; override;

    procedure Free; 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 AlInicio;
procedure AlFinal;

implementation

//----------------------------------
// Métodos de TFichaSolarTermico
//==================================

//No se verifica que el largo del rendimiento en la ficha sea igual a ipostes;
//Sino se puede caer fuera de rango, lo mismo para versiones anteriores.
constructor TFichaSolarTermico.Create(capa: integer; fecha: TFecha;
  periodicidad: TPeriodicidad; PMin, PMax: NReal; cv_min, cv: NReal;
  indicePreciosPorCombustible: TFuenteAleatoria; bornePreciosPorCombustible: string;
  disp: NReal; HayAlmacenDeEnergia: boolean; tRepHoras: NReal;
  PagoPorDisponibilidad_USD_MWh: NReal;
  PagoPorEnergia_USD_MWh: NReal; FAmp: NReal; rendimiento_almacen: TDAofNReal);
begin
  inherited Create(capa, fecha, periodicidad);
  self.PMin := PMin;
  self.PMax := PMax;
  self.cv_min := cv_min;
  self.cv := cv;
  self.indicePreciosPorCombustible := indicePreciosPorCombustible;
  self.bornePreciosPorCombustible := bornePreciosPorCombustible;
  self.disp := disp;
  self.HayAlmacenDeEnergia := HayAlmacenDeEnergia;
  self.tRepHoras := tRepHoras;
  self.PagoPorDisponibilidad_USD_MWh := PagoPorDisponibilidad_USD_MWh;
  self.PagoPorEnergia_USD_MWh := PagoPorEnergia_USD_MWh;
  self.FAmp := FAmp;
  self.rendimiento_almacen := rendimiento_almacen;
end;

function TFichaSolarTermico.Rec: TCosa_RecLnk;
begin
  Result:=inherited Rec;
  Result.addCampoDef('PMin', PMin);
  Result.addCampoDef('PMax', PMax);
  Result.addCampoDef('cv_min', cv_min);
  Result.addCampoDef('cv', cv);
  Result.addCampoDef_ref('indicePreciosPorCombustible', TCosa(indicePreciosPorCombustible), self);
  Result.addCampoDef('bornePreciosPorCombustible', bornePreciosPorCombustible);
  Result.addCampoDef('disp', disp);
  Result.addCampoDef('HayRestriccionEmaxPasoDeTiempo', aux_b, 0, 89 );
  Result.addCampoDef('EmaxPasoDeTiempo', aux_r, 0, 89 );
  Result.addCampoDef('HayAlmacenDeEnergia', HayAlmacenDeEnergia, 89 );
  Result.addCampoDef('tRepHoras', tRepHoras);
  Result.addCampoDef('PagoPorDisponibilidad_USD_uh', PagoPorDisponibilidad_USD_MWh, 0, 98 );
  Result.addCampoDef('PagoPorEnergia', PagoPorEnergia_USD_MWh, 0, 98 );
  Result.addCampoDef('rendimiento_almacen', rendimiento_almacen, 78 );
  Result.addCampoDef('FAmp', famp, 78 );
  Result.addCampoDef('rendimiento_almacen', rendimiento_almacen, 78 );
  Result.addCampoDef('FAmp', famp, 78 );
end;

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

procedure TFichaSolarTermico.AfterRead(f:TArchiTexto);
var
  i: integer;
begin
  inherited AfterRead(f);
  if f.Version < 89 then
    HayAlmacenDeEnergia := False;
  if f.Version < 78 then
  begin
    for i := 0 to length(rendimiento_almacen) - 1 do
    begin
      rendimiento_almacen[i] := 1.0;
    end;
    famp := 1.0;
  end;
end;

procedure TFichaSolarTermico.generarLineaResumen(var archi: TextFile);
begin
  Write(archi, '-', #9,       //PMín
    '-', #9,       //PMáx
    '-', #9,       //CV_Mín
    '-', #9,       //CV_Medio
    '-', #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 TFichaSolarTermico.infoAd_: string;
begin
  Result := 'PMín= ' + FloatToStrF(PMin, ffGeneral, 10, 1) + ' MW, ' +
    'PMáx= ' + FloatToStrF(PMax, ffGeneral, 10, 1) + ' MW, ' +
    'cv_min= ' + FloatToStrF(cv_min, ffGeneral, 10, 1) + ' USD/MWh, ' +
    'cv= ' + FloatToStrF(cv, ffGeneral, 10, 1) + ' USD/MWh, ' + 'fDisp= ' +
    FloatToStrF(disp, ffGeneral, 10, 2) + ' p.u., ' + 'tRep= ' +
    FloatToStrF(tRepHoras, ffGeneral, 10, 1) + ' hs';
end;

procedure TFichaSolarTermico.Free;
begin
  inherited Free;
end;


























//-----------------------------
// Métodos de TSolarTermico
//=============================
procedure TSolarTermico.dump_Variables(var f: TextFile; charIndentacion: char);
var
  iPoste: integer;
  cantPostes: integer;
  fuenteAleatoria: TFuenteAleatoria_Borne;
begin
  inherited dump_Variables(f, charIndentacion);

  cantPostes := globs.NPostes;
  for iPoste := 0 to cantPostes - 1 do
  begin
    fuenteAleatoria := TFuenteAleatoria_Borne(
      fuentesAleatoriasPotenciasPorPoste[iPoste]);
    writeln(f, charIndentacion, 'NombreFuentePotencia[Poste=', iPoste,
      ']= ', fuenteAleatoria.fuente.nombre);
  end;

  writeln(f, charIndentacion, 'c0[USD/MWh]= ', FloatToStrF(c0, ffFixed, 10, 3));
  writeln(f, charIndentacion, 'cv[USD/MWh]= ', FloatToStrF(cv, ffFixed, 10, 3));

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

  for iPoste := 0 to cantPostes - 1 do
  begin
    writeln(f, charIndentacion, 'PMaxDisponible[MW][Poste=', iPoste, ']= ',
      FloatToStrF(PMaxDisponiblePorPoste[iPoste], ffFixed, 10, 3));
  end;

  writeln(f, charIndentacion, 'disp= ', pa.disp);

  writeln(f, charIndentacion, 'HayRestrEMaxPasoDeTiempo= ',
    pa.hayAlmacenDeEnergia);

  writeln(f);
end;

class function TSolarTermico.TipoFichaLPD: TClaseDeFichaLPD;
begin
  Result := TFichaSolarTermico;
end;


{$IFDEF BOSTA}
procedure TSolarTermico.AfterInstantiation;
begin
  inherited AfterInstantiation;
  lpd.Propietario := self;
  P := nil;
  pa := nil;
  nodo := nil;
end;
{$ENDIF}

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

function TSolarTermico.Rec: TCosa_RecLnk;
begin
  Result:=inherited Rec;
  Result.addCampoDef('fuentesAleatoriasPotenciasPorPoste', TCosa(fuentesAleatoriasPotenciasPorPoste));
  Result.addCampoDef('lpd', TCosa(lpd));
end;

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

procedure TSolarTermico.AfterRead(f:TArchiTexto);
begin
  inherited AfterRead(f);
  lpd.Propietario := self;
end;



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

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

function TSolarTermico.PotenciaFirme: NReal;
begin
  Result := 0; // ojo, esto es una convensión.
  // Si este actor lo usamos para Generación Distribuída o Importación
  // devolvemos CERO pues la potencia firme debe ser calculada por algún otro
  // método.

  //result:= (paUnidades.nUnidades[0]) * pa.PMax * pa.disp;
end;

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

class function TSolarTermico.DescClase: string;
begin
  Result := rsGeneradorSolarTermico;
end;

procedure TSolarTermico.Sim_Cronica_Inicio;
begin
  inherited Sim_Cronica_Inicio;
end;


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

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


procedure TSolarTermico.SorteosDelPaso(sortear: boolean);
begin
  if hayForzamientos or globs.ObligarDisponibilidad_1_ then
  begin
    NMaquinasDisponibles := paUnidades.nUnidades_Operativas[0];
    factor_P := pa.PMax * NMaquinasDisponibles;
  end
  else if sortear then
  begin
    ActualizarProbabilidadesReparacionYRotura_(pa.disp, pa.tRepHoras);
    NMaquinasDisponibles := Sorteos_RepRotUnidades;
    factor_P := pa.PMax * NMaquinasDisponibles;
  end
  else
  begin
    NMaquinasDisponibles := paUnidades.nUnidades_Operativas[0];
    factor_P := pa.PMax * pa.disp * NMaquinasDisponibles;
  end;
end;

procedure TSolarTermico.PrepararPaso_ps;
var
  iPoste: integer;
  fuenteAleatoria: TFuenteAleatoria_Borne;
  valp: NReal;
  indice: NReal;
begin

  PMin := pa.PMin;
  if factor_P = 0 then
  begin
    vclear(PMaxDisponiblePorPoste);   //esto es necesario?
    vclear(BMax);
    cv := 0;
    exit;
  end;

  EMaxPasoDeTiempo := 0;
  for iPoste := 0 to globs.NPostes - 1 do
  begin
    fuenteAleatoria := TFuenteAleatoria_Borne(
      fuentesAleatoriasPotenciasPorPoste.items[iPoste]);
    valp := fuenteAleatoria.fuente.Bornera[fuenteAleatoria.DarIdBorne( self ) ] * pa.FAmp;
    EMaxPasoDeTiempo := EMaxPasoDeTiempo + valp * globs.DurPos[iPoste];
    if not pa.HayAlmacenDeEnergia then
      BMax[iposte] := max(0, valp - pa.PMin);
  end;
  //end;

  if pa.indicePreciosPorCombustible <> nil then
  begin
    indice := pa.indicePreciosPorCombustible.bornera[pa.nroBornePreciosPorCombustible];
    cv := pa.cv * indice;
    c0 := pa.PMin * pa.cv_min * indice;
    cve := pa.PagoPorEnergia_USD_MWh * indice;
  end
  else
  begin
    cv := pa.cv;
    c0 := pa.PMin * pa.cv_min;
    cve := pa.PagoPorEnergia_USD_MWh;
  end;

end;

procedure TSolarTermico.Sim_Paso_Fin; //lee la solución al fin de cada paso
var
  iposte: integer;
begin
  potMedia_despachada := 0;
  if NMaquinasDisponibles > 0 then
  begin
    for iposte := 0 to high(P) do
      potMedia_despachada := potMedia_despachada + P[iposte] * globs.durpos[iposte];
    potMedia_despachada := potMedia_despachada * globs.invHorasDelPaso;
  end
  else
  begin
    potMedia_despachada := 0;
  end;
end;





procedure TSolarTermico.opt_nvers(var ivar, ivae, ires: integer);
//cantidad de variables de control que usa (cant postes, potencia y potencias acoples)
begin
  if factor_P = 0 then
    exit;

  Self.ivar := ivar;
  ivar := ivar + globs.NPostes * 2; //n postes * dos estados, on/off

  Self.ivae := ivae;   //variables enteras para programación entera mixta
  ivae := ivae + globs.NPostes;

  Self.ires := ires;  //restricciones adicionales a las de caja
  ires := ires + globs.NPostes;

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

procedure TSolarTermico.opt_cargue(s: TSimplex);   //carga el modelo en el simplex
var
  inodores: integer;
  iposte: integer;
  jres: integer;
  BMax_r: NReal;

begin
  if factor_P = 0 then
    exit; // si no hay máquinas no juego


  inodores := nodo.ires; //restricciones de nodo
  // aporte a las restricciones del nodo
  for iposte := 0 to globs.NPostes - 1 do
  begin
    s.pon_e(inodores + iposte, ivar + iposte, 1);
    // coeficiente de la B[iposte] (despacho por arriba del minimo tecnico)
    s.pon_e(inodores + iposte, ivar + iposte + globs.NPostes, Pmin);
    // coef A (variable de acople)
  end;

  BMax_r := pa.PMax - pa.PMin;

  // restricciones adicionales impuestas por la variable de Acople
  jres := ires;
  for iposte := 0 to globs.NPostes - 1 do
  begin
    s.pon_e(jres, ivar + iposte, -1); // coef B[iposte]

    if pa.HayAlmacenDeEnergia then
      s.pon_e(jres, ivar + globs.NPostes + iposte, BMax_r) //-B+A(PMax-Pmin)>=0
    else
      s.pon_e(jres, ivar + globs.NPostes + iposte, min(BMax[iposte], BMax_r));
    //-B+A(minimo(Pmax,PMaxDisponiblePorPoste)-Pmin)>=0

    Inc(jres);
  end;

  //Restriccion a la energía máxima generable en el paso
  if pA.HayAlmacenDeEnergia then
  begin
    for iposte := 0 to globs.NPostes - 1 do
    begin
      s.pon_e(jres, ivar + iposte, -globs.durpos[iposte] /
        pa.rendimiento_almacen[iposte]);
      //resta energia generada por arriba del minimo técnico
      s.pon_e(jres, ivar + globs.NPostes + iposte, -globs.durpos[iposte] *
        pa.PMin / pa.rendimiento_almacen[iposte]);
    end;
    s.pon_e(jres, s.nc, EmaxPasoDeTiempo);
  end;

  // aportes a la función de utilidad
  for iposte := 0 to globs.NPostes - 1 do
  begin
    s.pon_e(s.nf, ivar + iposte, -cv * globs.DurPos[iposte]);
    s.pon_e(s.nf, ivar + globs.NPostes + iposte, -c0 * globs.DurPos[iposte]);
  end;
end;

procedure TSolarTermico.opt_fijarRestriccionesDeCaja(s: TSimplex);
var
  iposte: integer;
begin
  if factor_P = 0 then
    exit; // si no hay máquinas no juego

  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 *)
    for iposte := 0 to globs.NPostes - 1 do
      s.cota_sup_set(ivar + iposte, (pa.PMax - pa.PMin) * NMaquinasDisponibles * 1.1);

    // Restricciones de caja de las A y las declaramos enteras
    for iposte := 0 to globs.NPostes - 1 do
      s.set_EnteraConAcople(
        ivae + iposte, ivar + globs.NPostes + iposte, NMaquinasDisponibles,
        ivar + iposte, ires + iposte);
  end
  else
  begin
    for iposte := 0 to globs.NPostes - 1 do
      s.FijarVariable(ivar + iposte, paForzamiento.P[0] - pa.Pmin);

    // Restricciones de caja de las A y las declaramos enteras
    for iposte := 0 to globs.NPostes - 1 do
    begin
      s.set_EnteraConAcople(
        ivae + iposte, ivar + globs.NPostes + iposte, NMaquinasDisponibles,
        ivar + iposte, ires + iposte);
      s.FijarVariable(ivar + globs.NPostes + iposte, NMaquinasDisponibles);
    end;
  end;
end;

procedure TSolarTermico.opt_leerSolucion(s: TSimplex);
var
  iposte: integer;
  m, energiaDespachada: NReal;
  nmaqs: integer;
begin
  maxNMaquinasDespachadas := 0;
  Ingreso_PorDisponibilidad_ := 0;
  Ingreso_PorEnergia_ := 0;
  costoDirectoDelPaso := 0;
  if factor_P = 0 then
  begin
    vclear(P);
    //vclear( cv_Spot );
    for iposte := 0 to globs.NPostes - 1 do
    begin
      Lambda_P[iPoste]:= 0;
      cv_Spot[iPoste]:= get_cv_falla; // para que en el ordenamiento quede último, con costo variable igual a la falla;
    end;
    vclear(NMaquinasDespachadas);
    vclear(costos);
    exit;
  end;

  Ingreso_PorDisponibilidad_ := EMaxPasoDeTiempo * pa.PagoPorDisponibilidad_USD_MWh;


  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] > 1E-6 then
    begin
      nmaqs := trunc(s.xval(ivar + globs.NPostes + iposte) + 0.2);
      NMaquinasDespachadas[iposte] := nmaqs;
      m := s.xval(ivar + iposte);
      P[iposte] := m + nmaqs * PMin;

      if nmaqs > 0 then
      begin
        costos[iposte] := (m * cv + nmaqs * c0) * globs.DurPos[iposte];
        CostoDirectoDelPaso := CostoDirectoDelpaso + costos[iposte];
        EnergiaDespachada := EnergiaDespachada + P[iposte] * globs.DurPos[iposte];
        if nmaqs > maxNMaquinasDespachadas then
          maxNMaquinasDespachadas := nmaqs;
      end
      else
        costos[iposte] := 0;
    end;
  end;

  Ingreso_PorEnergia_ := EnergiaDespachada * cve;
end;



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

function TSolarTermico.getNombreRes(ires: integer; var nombre: string): boolean;
begin
  if NMaquinasDisponibles = 0 then
    Result := False
  else
  if (ires >= self.ires) and (ires < self.ires + globs.NPostes) then
  begin
    nombre := self.nombre + '_res-A' + IntToStr(ires - self.ires + 1);
    Result := True;
  end
  else if pa.HayAlmacenDeEnergia and (ires = self.ires + globs.NPostes) then
  begin
    nombre := self.nombre + '_res-ETotal';
    Result := True;
  end
  else
    Result := False;
end;

procedure TSolarTermico.PubliVars;
begin
  inherited PubliVars;
  PublicarVariableVR('Costo', '[USD]', 6, 1, costos, True, True);
  PublicarVariableVI('NMaqsDespachadas', '-', NMaquinasDespachadas, True, True);
  PublicarVariableNR('c0', '[USD/h]', 6, 1, c0, False);
  PublicarVariableNR('cv', '[USD/MWh]', 6, 2, cv, True);
  PublicarVariableNR('cve', '[USD/MWh]', 6, 2, cve, True);
  PublicarVariableVR('PMax', '[MW]', 6, 1, PMaxDisponiblePorPoste, True, True);
  PublicarVariableNI('NMaquinasDisponibles', '-', NMaquinasDisponibles, True);
  PublicarVariableNR('PMediaDespachada', '[MW]', 6, 1, potMedia_despachada, False);
  PublicarVariableNI('MaxNMaqsDespachadasEnElPaso', '-', maxNMaquinasDespachadas, False);

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

procedure TSolarTermico.Free;
begin
  setlength(costos, 0);
  setlength(NMaquinasDespachadas, 0);
  setlength(PMaxDisponiblePorPoste, 0);
  inherited Free;
end;

procedure AlInicio;
begin
  registrarClaseDeCosa(TSolarTermico.ClassName, TSolarTermico);
  registrarClaseDeCosa(TFichaSolarTermico.ClassName, TFichaSolarTermico);
//  registrarClaseDeCosa(TFuenteAleatoria_Borne.ClassName, TFuenteAleatoria_Borne);
{  AssignFile(fdbg, uconstantesSimSEE.getDir_Dbg + 'RachasRoto.xlt');
  rewrite(fdbg);}
end;

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

end.
