unit uMercadoSpotConDetalleHorarioSemanal;

interface

uses
  Classes,
  uFichasLPD,
  xMatDefs,
  math,
  uFechas,
  uCosa,
  uCosaConNombre,
  uFuentesAleatorias,
  uNodos,
  uGlobs,
  uSimplex,
  SysUtils,
  uAuxiliares,
  uConstantesSimSEE,
  uBaseMercadoSpot;

resourcestring
  rsSpotDeMercadoDetalleHorario = 'Spot de mercado con detalle horario semanal';

type

  { TFichaMercadoSpotDetalleHorarioSemanal }

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

    fDisp: NReal;
    Pmins, Pmaxs, deltaCosto: TDAofNReal;
    multPMin, multPMax: TFuenteAleatoria;
    borneMultPmin, borneMultPmax: string;
  (**************************************************************************)

    constructor Create(capa: integer; fecha: TFecha; periodicidad: TPeriodicidad;
      fDisp: NReal; multPMin, multPMax: TFuenteAleatoria;
      borneMultPmin, borneMultPmax: string; Pmins, Pmaxs, deltaCosto: TDAofNReal);

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

    function infoAd_: string; override;
    destructor destroy; override;

  end;

  { TMercadoSpotDetalleHorarioSemanal }

  TMercadoSpotDetalleHorarioSemanal = class(TBaseMercadoSpot)
  private
    iBorneMultPMin, iBorneMultPMax: integer;
  public
    pa: TFichaMercadoSpotDetalleHorarioSemanal;
    cv: TDAofNReal;

    nMaquinasDisponibles: integer;
    PMinDispPorPoste, PMaxDispPorPoste: TDAofNReal; //Previendo el postizado

    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;

    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;
    // 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;

    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;

    


  end;

procedure procCambioFichaMercadoSpotDetalleHorarioSemanal(Spot: TCosa);
procedure AlInicio;
procedure AlFinal;

implementation

//-------------------------------------------------
//Metodos de TFichaMercadoSpotDetalleHorarioSemanal
//=================================================

constructor TFichaMercadoSpotDetalleHorarioSemanal.Create(capa: integer;
  fecha: TFecha; periodicidad: TPeriodicidad; fDisp: NReal;
  multPMin, multPMax: TFuenteAleatoria; borneMultPmin, borneMultPmax: string;
  Pmins, Pmaxs, deltaCosto: TDAofNReal);
begin
  inherited Create(capa, fecha, periodicidad);
  self.fDisp := fDisp;
  self.multPMin := multPMin;
  self.borneMultPmin := borneMultPmin;
  self.multPMax := multPMax;
  self.borneMultPmax := borneMultPmax;
  self.Pmins := Pmins;
  self.Pmaxs := Pmaxs;
  self.deltaCosto := deltaCosto;
end;

function TFichaMercadoSpotDetalleHorarioSemanal.Rec: TCosa_RecLnk;
begin
  Result:=inherited Rec;
  Result.addCampoDef('fDisp', fDisp);
  Result.addCampoDef('Pmins', Pmins);
  Result.addCampoDef('Pmaxs', Pmaxs);
  Result.addCampoDef('deltaCosto', deltaCosto);
  Result.addCampoDef_ref('multPMin', TCosa(multPMin), self);
  Result.addCampoDef('borneMultPmin', borneMultPmin);
  Result.addCampoDef_ref('multPMax', TCosa(multPMax), self);
  Result.addCampoDef('borneMultPmax', borneMultPmax);

end;

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

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


function TFichaMercadoSpotDetalleHorarioSemanal.infoAd_: string;
begin
  Result := 'PMín media= ' + FloatToStrF(vprom(Pmins), ffGeneral, 10, 1) +
    ' MW, ' + 'PMáx media= ' + FloatToStrF(vprom(Pmaxs), ffGeneral, 10, 1) +
    ' MW, ' + 'fDisp= ' + FloatToStrF(fDisp, ffGeneral, 10, 2) +
    ' p.u. ' + 'deltaCostoMedio= ' + FloatToStrF(vprom(deltaCosto),
    ffGeneral, 10, 2) + ' p.u.';
end;

destructor TFichaMercadoSpotDetalleHorarioSemanal.destroy;
begin
  SetLength(Pmins, 0);
  setlength(Pmaxs, 0);
  SetLength(deltaCosto, 0);
  inherited destroy;
end;

















//--------------------------------------------
//Metodos de TMercadoSpotDetalleHorarioSemanal
//============================================

constructor TMercadoSpotDetalleHorarioSemanal.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 TMercadoSpotDetalleHorarioSemanal.Rec: TCosa_RecLnk;
begin
  Result:=inherited Rec;
end;

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

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



procedure TMercadoSpotDetalleHorarioSemanal.PrepararMemoria(
  Catalogo: TCatalogoReferencias; globs: TGlobs);
begin
  inherited prepararMemoria( Catalogo, globs);
  SetLength(PMinDispPorPoste, globs.NPostes);
  SetLength(PMaxDispPorPoste, globs.NPostes);
  SetLength(cv, globs.NPostes);
end;

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



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

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


procedure TMercadoSpotDetalleHorarioSemanal.SorteosDelPaso(sortear: boolean);
var
  i, iHora, iPosteDeLaHora: integer;
  valMultPMin, valMultPMax: NReal;
begin
  //Inicializo
  for i := 0 to globs.NPostes - 1 do
  begin
    PMinDispPorPoste[i] := 0;
    PMaxDispPorPoste[i] := 0;
  end;
  iHora := (globs.DiaDeLaSemanaInicioDelPaso - 1) * 24 + globs.HoraDeInicioDelPaso;

  //Sumo lo que corresponda según el tipo de sorteo
  if globs.ObligarDisponibilidad_1_ then
  begin
    nMaquinasDisponibles := paUnidades.nUnidades_Operativas[0];
    for i := 0 to ceil( globs.HorasDelPaso - 1) do
    begin
      iPosteDeLaHora := globs.kPosteHorasDelPaso[i];
      PMinDispPorPoste[iPosteDeLaHora] :=
        PMinDispPorPoste[iPosteDeLaHora] + pa.Pmins[iHora] * nMaquinasDisponibles;
      PMaxDispPorPoste[iPosteDeLaHora] :=
        PMaxDispPorPoste[iPosteDeLaHora] + pa.Pmaxs[iHora] * nMaquinasDisponibles;
      if iHora < 167 then
        iHora := iHora + 1
      else
        iHora := 0;
    end;
  end
  else if sortear then
  begin
    ActualizarProbabilidadesReparacionYRotura_(pa.fdisp, 8);
    nMaquinasDisponibles := Sorteos_RepRotUnidades;

    if nMaquinasDisponibles > 0 then
    begin
      nMaquinasDisponibles := paUnidades.nUnidades_Operativas[0];
      for i := 0 to ceil( globs.HorasDelPaso - 1 ) do
      begin
        iPosteDeLaHora := globs.kPosteHorasDelPaso[i];
        PMinDispPorPoste[iPosteDeLaHora] :=
          PMinDispPorPoste[iPosteDeLaHora] + pa.Pmins[iHora] * nMaquinasDisponibles;
        PMaxDispPorPoste[iPosteDeLaHora] :=
          PMaxDispPorPoste[iPosteDeLaHora] + pa.Pmaxs[iHora] * nMaquinasDisponibles;
        if iHora < 167 then
          iHora := iHora + 1
        else
          iHora := 0;
      end;
    end;
  end
  else
  begin
    nMaquinasDisponibles := paUnidades.nUnidades_Operativas[0];
    for i := 0 to ceil( globs.HorasDelPaso - 1 ) do
    begin
      iPosteDeLaHora := globs.kPosteHorasDelPaso[i];
      PMinDispPorPoste[iPosteDeLaHora] :=
        PMinDispPorPoste[iPosteDeLaHora] + pa.Pmins[iHora] * pa.fDisp;
      PMaxDispPorPoste[iPosteDeLaHora] :=
        PMaxDispPorPoste[iPosteDeLaHora] + pa.Pmaxs[iHora] * pa.fDisp;
      if iHora < 167 then
        iHora := iHora + 1
      else
        iHora := 0;
    end;
  end;

  if nMaquinasDisponibles > 0 then
  begin
    //Promedio y multiplico por las fuentes
    if pa.multPMin <> nil then
      valMultPMin := pa.multPMin.Bornera[iBorneMultPMin]
    else
      valMultPMin := 1;
    if pa.multPMax <> nil then
      valMultPMax := pa.multPMax.Bornera[iBorneMultPMax]
    else
      valMultPMax := 1;

    for i := 0 to globs.NPostes - 1 do
    begin
      PMinDispPorPoste[i] := PMinDispPorPoste[i] * globs.invDurPos[i] * valMultPMin;
      PMaxDispPorPoste[i] := PMaxDispPorPoste[i] * globs.invDurPos[i] * valMultPMax;
    end;
  end;
end;

procedure TMercadoSpotDetalleHorarioSemanal.PrepararPaso_ps;
var
  i, iHora, iPosteDeLaHora: integer;
  cvFuente: NReal;
begin
  if nMaquinasDisponibles > 0 then
  begin
    cvFuente := fuente.Bornera[numeroBorne];

    //Inicializo
    for i := 0 to globs.NPostes - 1 do
      cv[i] := 0;

    //Sumo
    iHora := (globs.DiaDeLaSemanaInicioDelPaso - 1) * 24 + globs.HoraDeInicioDelPaso;
    for i := 0 to ceil( globs.HorasDelPaso - 1 ) do
    begin
      iPosteDeLaHora := globs.kPosteHorasDelPaso[i];
      cv[iPosteDeLaHora] := cv[iPosteDeLaHora] + pa.deltaCosto[iHora];
      if iHora < 167 then
        iHora := iHora + 1
      else
        iHora := 0;
    end;

    //Promedio
    for i := 0 to globs.NPostes - 1 do
      cv[i] := cv[i] * globs.invDurPos[i] + cvFuente;
  end;
end;

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

procedure TMercadoSpotDetalleHorarioSemanal.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[iPoste] * globs.durpos[iposte]);
    end;
  end;
end;

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

procedure TMercadoSpotDetalleHorarioSemanal.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[iposte] * globs.durpos[iposte];
      costos[iposte] := m;
      costoDirectoDelPaso := costoDirectoDelPaso + m;
    end;
  end
  else
  begin
    vclear(P);
    //vclear( cv_Spot );
    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 TMercadoSpotDetalleHorarioSemanal.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;


procedure TMercadoSpotDetalleHorarioSemanal.dump_Variables(var f: TextFile;
  charIndentacion: char);
begin
  inherited dump_Variables(f, charIndentacion);

  writeln(f, charIndentacion, 'cv[USD/MWh]= ', TDAOfNRealToString(cv));
  writeln(f, charIndentacion, 'PMinDispPorPoste[MW]= ',
    TDAOfNRealToString(PMinDispPorPoste));
  writeln(f, charIndentacion, 'PMaxDispPorPoste[MW]= ',
    TDAOfNRealToString(PMaxDispPorPoste));

  writeln(f);
end;

procedure TMercadoSpotDetalleHorarioSemanal.PubliVars;
begin
  inherited PubliVars;
  PublicarVariableVR('Costo', '[USD]', 6, 1, costos, True, True);
  PublicarVariableVR('cv', '[USD/MWh]', 6, 1, cv, True, False);
  {$IFDEF DECLARAR_VARIABLES_SIMRES_DEF}
  declararVarsPSimResPorDefectoIntercalandoPostes(['P', 'Costo'], globs.NPostes);
  {$ENDIF}
end;

class function TMercadoSpotDetalleHorarioSemanal.TipoFichaLPD: TClaseDeFichaLPD;
begin
  Result := TFichaMercadoSpotDetalleHorarioSemanal;
end;

class function TMercadoSpotDetalleHorarioSemanal.DescClase: string;
begin
  Result := rsSpotDeMercadoDetalleHorario;
end;

destructor TMercadoSpotDetalleHorarioSemanal.destroy;
begin
  SetLength(cv, 0);
  inherited destroy;
end;









procedure procCambioFichaMercadoSpotDetalleHorarioSemanal(Spot: TCosa);
var
  spotCast: TMercadoSpotDetalleHorarioSemanal;
begin
  spotCast := TMercadoSpotDetalleHorarioSemanal(Spot);
  if spotCast.pa.multPMin <> nil then
    spotCast.iBorneMultPMin := spotCast.pa.multPMin.IdBorne(spotCast.pa.borneMultPmin);
  if spotCast.pa.multPMax <> nil then
    spotCast.iBorneMultPMax := spotCast.pa.multPMax.IdBorne(spotCast.pa.borneMultPmax);
end;

procedure AlInicio;
begin
  uCosa.registrarClaseDeCosa(TMercadoSpotDetalleHorarioSemanal.ClassName,
    TMercadoSpotDetalleHorarioSemanal);
  uCosa.registrarClaseDeCosa(TFichaMercadoSpotDetalleHorarioSemanal.ClassName,
    TFichaMercadoSpotDetalleHorarioSemanal);
end;

procedure AlFinal;
begin

end;

end.
