unit uMercadoSpot;

interface

uses
  Classes,
  SysUtils,
  uFichasLPD,
  uFechas,
  xMatDefs,
  uNodos,
  uFuentesAleatorias,
  uCosa,
  uCosaConNombre,
  uGlobs,
  uConstantesSimSEE,
  uSimplex,
  uranddispos,
  uComercioInternacional,
  uBaseMercadoSpot,
  uresourcestring;

resourcestring
  rsSpotDeMercado = 'Spot de mercado';

type

  { TFichaMercadoSpot }

  TFichaMercadoSpot = 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;
    fdisp: NReal;
  (**************************************************************************)

    constructor Create(capa: integer; fecha: TFecha; periodicidad: TPeriodicidad;
      Pmin, Pmax, fdisp: NReal);

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

    function InfoAd_: string; override;
  private
    px_oldFDispPMin, px_oldFDispPMax: NReal;
  end;

  { TMercadoSpot }

  TMercadoSpot = class(TBaseMercadoSpot)
  public
    pa: TFichaMercadoSpot;

    cv: NReal;
    nMaquinasDisponibles: integer;
    PMinDisp, PMaxDisp: NReal;
    //Potencia mínima y máxima disponibles luego de los sorteos

    constructor Create(capa: integer; nombre: string; lpdUnidades,
      lpd: TFichasLPD; Nodo: TNodo; fuente: TFuenteAleatoria;
      nombreBorne: string;
      xFuenteIdxP: TFuenteAleatoria; xBorneIdxP: string); override;

     
    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( Catalogo: TCatalogoReferencias ); override;

    procedure SorteosDelPaso(sortear: boolean); override;
    procedure PrepararPaso_ps; override;
    // aquí leer el valor de las variables de estado y
    // del resultado de SorteosDelPaso para calcular lo necesario
    // para jugar en el paso de tiempo que se inicia

    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 dump_Variables(var f: TextFile; charIndentacion: char); override;

    procedure PubliVars; override;
    class function TipoFichaLPD: TClaseDeFichaLPD; override;
    class function DescClase: string; override;
    destructor destroy; override;

    function get_pa_FD( kTipoUnidad: integer ): NReal; override;
    function get_pa_TMR( kTipoUnidad: integer ): NReal; override;

  end;

procedure AlInicio;
procedure AlFinal;

implementation

uses uActores;

//----------------------------
//Metodos de TFichaMercadoSpot
//============================

constructor TFichaMercadoSpot.Create(capa: integer; fecha: TFecha; periodicidad: TPeriodicidad;
  Pmin, Pmax, fdisp: NReal);
begin
  inherited Create(capa, fecha, periodicidad);
  self.Pmin := Pmin;
  self.Pmax := Pmax;
  self.fdisp := fdisp;
end;

function TFichaMercadoSpot.Rec: TCosa_RecLnk;
begin
  Result:=inherited Rec;
  Result.addCampoDef('Pmin', Pmin, 0, 16 );
  Result.addCampoDef('fdispPMin', px_oldFDispPMin, 0, 16 );
  Result.addCampoDef('Pmax', Pmax, 0, 16 );
  Result.addCampoDef('fdispPMax', px_oldFDispPMax, 0, 16 );
  Result.addCampoDef('Pmin', Pmin, 16 );
  Result.addCampoDef('Pmax', Pmax, 16 );
  Result.addCampoDef('fdisp', fdisp, 16 );
end;

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

procedure TFichaMercadoSpot.AfterRead(f:TArchiTexto);
begin
  inherited AfterRead(f);
  if f.Version < 16 then
    fdisp := px_oldFDispPMax;
end;


function TFichaMercadoSpot.InfoAd_: string;
begin
  Result := rs_PMin+'= ' + FloatToStrF(PMin, ffGeneral, 10, 1) + ' MW, ' +
    rs_PMax + '= ' + FloatToStrF(Pmax, ffGeneral, 10, 1) + ' MW, ' +
    rs_FD+'= ' + FloatToStrF(fdisp, ffGeneral, 10, 2) + ' p.u.';
end;





















//-----------------------
//Metodos de TMercadoSpot
//=======================

constructor TMercadoSpot.Create(capa: integer; nombre: string; lpdUnidades, lpd: TFichasLPD; Nodo: TNodo;
  fuente: TFuenteAleatoria; nombreBorne: string; xFuenteIdxP: TFuenteAleatoria;
  xBorneIdxP: string);
begin
  inherited Create(capa, nombre, lpdUnidades, lpd, Nodo,
    fuente, nombreBorne, xFuenteIdxP, xBorneIdxP );
  pa := nil;
end;

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

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

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



procedure TMercadoSpot.PrepararMemoria(Catalogo: TCatalogoReferencias;
  globs: TGlobs);
begin
  inherited prepararMemoria(Catalogo, globs);
end;

procedure TMercadoSpot.RegistrarParametrosDinamicos(
  Catalogo: TCatalogoReferencias);
begin
  inherited registrarParametrosDinamicos( Catalogo );
  lpd.expandirFichas( Catalogo, globs);
  lpd.RegistrarFichasAActualizar(Self, globs.ActualizadorLPD, @pa, nil);
end;



function TMercadoSpot.get_pa_FD( kTipoUnidad: integer ): NReal;
begin
  result:= pa.fdisp;
end;

function TMercadoSpot.get_pa_TMR( kTipoUnidad: integer ): NReal;
begin
  result:= 8;
end;

procedure TMercadoSpot.SorteosDelPaso(sortear: boolean);
begin
  if globs.ObligarDisponibilidad_1_ then
  begin
    nMaquinasDisponibles := paUnidades.nUnidades_Operativas[0];
    PMinDisp := pa.Pmin * nMaquinasDisponibles;
    PMaxDisp := pa.Pmax * nMaquinasDisponibles;
  end
  else if sortear then
  begin
    ActualizarProbabilidadesReparacionYRotura_(pa.fdisp, 8);
    nMaquinasDisponibles := Sorteos_RepRotUnidades;

    PMaxDisp := pa.Pmax * nMaquinasDisponibles;
    PMinDisp := pa.Pmin * nMaquinasDisponibles;
  end
  else
  begin
    nMaquinasDisponibles := paUnidades.nUnidades_Operativas[0];
    PMinDisp := pa.Pmin * pa.fdisp;
    PMaxDisp := pa.Pmax * pa.fdisp;
  end;
end;

procedure TMercadoSpot.PrepararPaso_ps;
begin
  cv := fuente.Bornera[numeroBorne];
end;

procedure TMercadoSpot.opt_nvers(var ivar, ivae, ires: integer);
begin
  if nMaquinasDisponibles > 0 then
  begin
    Self.ivar := ivar;
    ivar := ivar + globs.NPostes;
  end;
end;

procedure TMercadoSpot.opt_cargue(s: TSimplex);
var
  inodores, iPoste: integer;
begin
  if nMaquinasDisponibles > 0 then
  begin
    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;
  end;
end;

procedure TMercadoSpot.opt_fijarRestriccionesDeCaja(s: TSimplex);
var
  iposte: integer;
begin
  if nMaquinasDisponibles > 0 then
  begin
    if abs(PMaxDisp - PMinDisp) < 1.0 then
      for iposte := 0 to globs.NPostes - 1 do
        s.FijarVariable(ivar + iposte, PMaxDisp)
    else
      for iposte := 0 to globs.NPostes - 1 do
      begin
        s.cota_inf_set(ivar + iposte, PMinDisp);
        s.cota_sup_set(ivar + iposte, PMaxDisp);
      end;
  end;
end;

procedure TMercadoSpot.opt_leerSolucion(s: TSimplex);
var
  iposte: integer;
  m: NReal;
begin
  CostoDirectoDelPaso := 0;
  if nMaquinasDisponibles > 0 then
  begin
    // 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];
      m := P[iposte] * cv * globs.durpos[iposte];
      costos[iposte] := m;
      CostoDirectoDelPaso := CostoDirectoDelPaso + m;
    end;
  end
  else
  begin
    vclear(P);
    vclear(costos);
    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;
  end;
end;


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

function TMercadoSpot.getNombreRes(ires: integer; var nombre: string): boolean;
begin
  Result := False;
end;

procedure TMercadoSpot.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, 'PMin[MW]= ', FloatToStrF(pa.Pmin, ffFixed, 10, 3));
  writeln(f, charIndentacion, 'PMAx[MW]= ', FloatToStrF(pa.PMax, ffFixed, 10, 3));

  writeln(f);
end;

procedure TMercadoSpot.PubliVars;
begin
  inherited PubliVars;
  PublicarVariableVR('Costo', '[USD]', 6, 1, costos, True, True);
  PublicarVariableNR('cv', '[USD/MWh]', 6, 1, cv, False);
end;

class function TMercadoSpot.TipoFichaLPD: TClaseDeFichaLPD;
begin
  Result := TFichaMercadoSpot;
end;

class function TMercadoSpot.DescClase: string;
begin
  Result := rsSpotDeMercado;
end;

destructor TMercadoSpot.destroy;
begin
  inherited destroy;
end;









procedure AlInicio;
begin
  uCosa.registrarClaseDeCosa(TMercadoSpot.ClassName, TMercadoSpot);
  uCosa.registrarClaseDeCosa(TFichaMercadoSpot.ClassName, TFichaMercadoSpot);
end;

procedure AlFinal;
begin
end;

end.
