unit uMercadoSpot_postizado;

interface

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

resourcestring
  rsSpotDeMercado_postizado = 'Spot de mercado postizado';

type

  { TFichaMercadoSpot_postizado }

  TFichaMercadoSpot_postizado = 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;
    tRepHoras: NReal; // Tiempo Medio de Reparación en Horas.
    Pmin, Pmax: TDAOfNReal;
    fuentesAleatoriasCostosVariablesPorPoste: TListaDeCosas;
    // si el precio del mercado supera este precio se bloquea la extracción
    // poniendo las potencias máximas a CERO
    topeSale: NReal;
    topeSaleActivo: boolean;
    // Si el precio del mercado supera este valor a los efectos de su carga
    // en la optimización del paso se topea al precio máximo de Inyección,
    // para que sea anti-económico inyectar con recursos de mayor valor.
    topeEntra: NReal;
    topeEntraActivo: boolean;
    activarMargenExportador: boolean;
    DeltaExportador, DeltaImportador: NReal;
  (**************************************************************************)

    constructor Create(capa: integer; fecha: TFecha; periodicidad: TPeriodicidad;
      disp: NReal; tRepHoras: NReal; Pmin, Pmax: TDAOfNReal;
      listaFuentesAleatoriasCostosVariables: TListaDeCosas; topeEntra: NReal;
      topeEntraActivo: boolean; topeSale: NReal; topeSaleActivo: boolean;
      activarMargenExportador: boolean; deltaExportador, deltaImportador: NReal);

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

    function infoAd_: string; override;
  end;

  { TMercadoSpot_postizado }

  TMercadoSpot_postizado = class(TComercioInternacional)
  public
    pa: TFichaMercadoSpot_postizado;

    cv: TDAOfNReal; // costo marginal del MERCADO
    cv_Entra: TDAOfNreal; // cv eventualmente reducido para no incentivar ingresos
    // por encima de un precio tope.

    costos: TDAofNReal;
    OtroMercado_BeneficiosImpo: TDAofNREal;
    otroMercado_BeneficiosExpo: TDAofNReal;

    //costos[iposte]:= P[iposte] * cv * globs.durpos[iposte];
    PMinDisp, PMaxDisp: TDAOfNReal;
    //Potencia mínima y máxima disponibles luego de los sorteos

    nMaquinasDisponibles: integer;

    constructor Create(capa: integer; nombre: string; nacimiento, muerte: TFecha;
      lpdUnidades, lpd: TFichasLPD; Nodo: TNodo; xFuenteIdxP: TFuenteAleatoria;
  xBorneIdxP: string);

     
    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;
    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;
    class function DescClase: string; override;
    procedure Free; override;
  end;

procedure AlInicio;
procedure AlFinal;

implementation

uses uActores;

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

constructor TFichaMercadoSpot_postizado.Create(capa: integer;
  fecha: TFecha; periodicidad: TPeriodicidad; disp: NReal; tRepHoras: NReal;
  Pmin, Pmax: TDAOfNReal; listaFuentesAleatoriasCostosVariables: TListaDeCosas;
  topeEntra: NReal; topeEntraActivo: boolean; topeSale: NReal;
  topeSaleActivo: boolean; activarMargenExportador: boolean;
  deltaExportador, deltaImportador: NReal);
begin
  inherited Create(capa, fecha, periodicidad);
  self.fdisp := disp;
  self.tRepHoras := tRepHoras;
  self.Pmin := Pmin;
  self.Pmax := Pmax;
  self.fuentesAleatoriasCostosVariablesPorPoste := listaFuentesAleatoriasCostosVariables;
  self.topeEntra := topeEntra;
  self.topeEntraActivo := topeEntraActivo;
  self.topeSale := topeSale;
  self.topeSaleActivo := topeSaleActivo;
  self.activarMargenExportador := activarMargenExportador;
  self.DeltaExportador := deltaExportador;
  self.DeltaImportador := deltaImportador;
end;

function TFichaMercadoSpot_postizado.Rec: TCosa_RecLnk;
begin
  Result:=inherited Rec;
  Result.addCampoDef('fdisp', fdisp);
  Result.addCampoDef('tRepHoras', tRepHoras);
  Result.addCampoDef('Pmin', Pmin);
  Result.addCampoDef('Pmax', Pmax);
  Result.addCampoDef('fuentesAleatoriasCostosVariablesPorPoste', TCosa(fuentesAleatoriasCostosVariablesPorPoste));
  Result.addCampoDef('topeSale', topeSale, 50, 0, '10000' );
  Result.addCampoDef('topeSaleActivo', topeSaleActivo, 50 );
  Result.addCampoDef('topeEntra', topeEntra, 50, 0, '10000' );
  Result.addCampoDef('topeEntraActivo', topeEntraActivo, 50 );
  Result.addCampoDef('activarMargenExportador', activarMargenExportador, 76 );
  Result.addCampoDef('deltaExportador', deltaExportador, 76 );
  Result.addCampoDef('deltaImportador', deltaImportador, 77 );
end;

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

procedure TFichaMercadoSpot_postizado.AfterRead(f:TArchiTexto);
begin
  inherited AfterRead(f);
   if f.version < 77 then
     deltaImportador := deltaExportador;
  end;


function TFichaMercadoSpot_postizado.infoAd_: string;
begin
  Result := 'Spot_postizado';
end;


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

constructor TMercadoSpot_postizado.Create(capa: integer; nombre: string;
  nacimiento, muerte: TFecha; lpdUnidades, lpd: TFichasLPD; Nodo: TNodo;
  xFuenteIdxP: TFuenteAleatoria; xBorneIdxP: string );

begin
  inherited Create(capa, nombre, nacimiento, muerte, lpdUnidades, Nodo,
  xFuenteIdxP, xBorneIdxP );
  self.lpd := lpd;
  pa := nil;
end;

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

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

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



procedure TMercadoSpot_postizado.PrepararMemoria(
  Catalogo: TCatalogoReferencias; globs: TGlobs);
begin
  inherited prepararMemoria( Catalogo, globs );
  setlength(cv, globs.Npostes);
  setlength(cv_entra, globs.Npostes);
  setlength(costos, globs.NPostes);
  setlength(OtroMercado_BeneficiosImpo, globs.NPostes);
  setlength(OtroMercado_BeneficiosExpo, globs.NPostes);
  setlength(PMinDisp, globs.NPostes);
  setlength(PMaxDisp, globs.NPostes);
end;

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


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

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


procedure TMercadoSpot_postizado.SorteosDelPaso(sortear: boolean);
var
  kposte: integer;

begin
  if globs.ObligarDisponibilidad_1_ then
  begin
    nMaquinasDisponibles := paUnidades.nUnidades_Operativas[0];
    for kposte := 0 to globs.NPostes - 1 do
    begin
      PMinDisp[kposte] := pa.Pmin[kposte] * nMaquinasDisponibles;
      PMaxDisp[kposte] := pa.Pmax[kposte] * nMaquinasDisponibles;
    end;
  end
  else if sortear then
  begin
    ActualizarProbabilidadesReparacionYRotura_(pa.fdisp, pa.tRepHoras);
    nMaquinasDisponibles := Sorteos_RepRotUnidades;
    for kposte := 0 to globs.NPostes - 1 do
    begin
      PMaxDisp[kposte] := pa.Pmax[kposte] * nMaquinasDisponibles;
      PMinDisp[kposte] := pa.Pmin[kposte] * nMaquinasDisponibles;
    end;
  end
  else
  begin
    nMaquinasDisponibles := paUnidades.nUnidades_Operativas[0];
    for kposte := 0 to globs.NPostes - 1 do
    begin
      PMinDisp[kposte] := pa.Pmin[kposte] * pa.fdisp;
      PMaxDisp[kposte] := pa.Pmax[kposte] * pa.fdisp;
    end;
  end;
end;


procedure TMercadoSpot_postizado.PrepararPaso_ps;
var
  kPoste: integer;
  fuenteAleatoria: TFuenteAleatoria_Borne;
begin

  for kPoste := 0 to globs.NPostes - 1 do
  begin
    fuenteAleatoria := TFuenteAleatoria_Borne(
      pa.fuentesAleatoriasCostosVariablesPorPoste.items[kPoste]);
    cv[kposte] := fuenteAleatoria.fuente.Bornera[fuenteAleatoria.DarIdBorne(self)];
  end;

  if pa.topeSaleActivo then
    for kPoste := 0 to globs.NPostes - 1 do
    begin
      if cv[kposte] >= pa.topeSale then
        PMaxDisp[kposte] := 0; // eliminamos la posibilidad de extracción
    end;

  if pa.topeEntraActivo then
    for kPoste := 0 to globs.NPostes - 1 do
    begin
      if cv[kposte] > pa.topeEntra then
        cv_entra[kposte] := pa.topeEntra
      // eliminamos la posibilidad de pagar más que topeEntra por lo cual n
      else
        cv_entra[kposte] := cv[kposte];
    end
  else
    for kPoste := 0 to globs.NPostes - 1 do
      cv_entra[kposte] := cv[kposte];

end;

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

procedure TMercadoSpot_postizado.opt_cargue(s: TSimplex);
var
  inodores, iPoste: integer;
  delta_e, delta_i: NReal;

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);
      s.pon_e(inodores + iposte, ivar + iposte + globs.NPostes, 1);

      // Ahora agregamos a la función de UTILIDAD (-costo)
      if pa.activarMargenExportador then
      begin
        delta_e := pa.DeltaExportador;
        delta_i := pa.DeltaImportador;
      end
      else
      begin
        delta_e := 0.001;
        delta_i := 0.001;
      end;
      s.pon_e(s.nf, ivar + iposte, -(cv_entra[iposte] + delta_i) *
        globs.durpos[iposte]);
      s.pon_e(s.nf, ivar + iposte + globs.NPostes, -(cv_entra[iposte] - delta_e) *
        globs.durpos[iposte]);
    end;
  end;
end;

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

        s.cota_inf_set(ivar + iposte + globs.NPostes, min(PMinDisp[iposte], 0));
        s.cota_sup_set(ivar + iposte + globs.NPostes, min(PMaxDisp[iposte], 0));
      end;
    end;
  end;
end;

procedure TMercadoSpot_postizado.opt_leerSolucion(s: TSimplex);
var
  iposte: integer;
  m: NReal;
  energia: NReal;
  precio_del_intercambio: NReal;
begin
  CostoDirectoDelPaso := 0;
  vclear(OtroMercado_BeneficiosImpo);
  vclear(otroMercado_BeneficiosExpo);

  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) + s.xval(ivar + iposte + globs.NPostes);
      Lambda_P[iPoste]:= s.xmult( ivar + iposte ) / globs.durpos[iposte];
      cv_Spot[iPoste]:= Nodo.cmarg[iposte] - Lambda_P[iPoste]; //multiplicador de Gen
      if not pa.activarMargenExportador then
      begin
        m := P[iposte] * cv[iposte] * globs.durpos[iposte];
        costos[iposte] := m;
      end
      else
      begin
        energia := P[iposte] * globs.durpos[iposte];
        if (energia > 0) then
        begin
          // es una importación.
          precio_del_intercambio := cv[iposte] + pa.DeltaImportador;
          m := energia * precio_del_intercambio;
          costos[iposte] := m;
          otroMercado_BeneficiosExpo[iposte] := energia * pa.DeltaImportador;
        end
        else
        begin
          // es una exportación.
          precio_del_intercambio := self.Nodo.cmarg[iposte] + pa.DeltaExportador;
          m := energia * precio_del_intercambio;
          costos[iposte] := m;
          OtroMercado_BeneficiosImpo[iposte] :=
            -energia * (cv[iposte] - precio_del_intercambio);
        end;
      end;
      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 TMercadoSpot_postizado.getNombreVar(ivar: integer; var nombre: string): boolean;
begin
  if (ivar >= self.ivar) and (ivar < (self.ivar +2* globs.NPostes)) then
  begin
    if ( ivar < self.ivar + globs.NPostes ) then
      nombre := self.Nombre + '_PImp[MW]' + IntToStr(ivar - self.ivar + 1)
    else
      nombre := self.Nombre + '_PExp[MW]' + IntToStr(ivar - globs.NPostes - self.ivar + 1);
    Result := True;
  end
  else
    Result := False;
end;

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

procedure TMercadoSpot_postizado.dump_Variables(var f: TextFile; charIndentacion: char);
var
  iposte: integer;
begin
  inherited dump_Variables(f, charIndentacion);
  for iposte := 0 to globs.npostes - 1 do
  begin
    writeln(f, charIndentacion, 'Poste: ', iposte + 1);
    writeln(f, charIndentacion, 'cv[USD/MWh]= ',
      FloatToStrF(cv[iposte], ffFixed, 10, 3));
    writeln(f, charIndentacion, 'PMin[MW]= ',
      FloatToStrF(pa.Pmin[iposte], ffFixed, 10, 3));
    writeln(f, charIndentacion, 'PMAx[MW]= ',
      FloatToStrF(pa.PMax[iposte], ffFixed, 10, 3));
  end;
  writeln(f);
end;

procedure TMercadoSpot_postizado.PubliVars;
begin
  inherited PubliVars;
  PublicarVariableVR('Costo', '[USD]', 6, 1, costos, True, True);
  PublicarVariableVR('cv', '[USD/MWh]', 6, 1, cv, True, True);
  PublicarVariableNI('NMaquinasDisponibles', '[u]', nMaquinasDisponibles, True);
  PublicarVariableVR('otroMercado_BeneficiosImpo', '[USD]', 6, 1,
    otroMercado_BeneficiosImpo, True, True);
  PublicarVariableVR('otroMercado_BeneficiosExpo', '[USD]', 6, 1,
    otroMercado_BeneficiosExpo, True, True);
end;

class function TMercadoSpot_postizado.TipoFichaLPD: TClaseDeFichaLPD;
begin
  Result := TFichaMercadoSpot_postizado;
end;

class function TMercadoSpot_postizado.DescClase: string;
begin
  Result := rsSpotDeMercado_postizado;
end;

procedure TMercadoSpot_postizado.Free;
begin
  setlength(costos, 0);
  setlength(PMinDisp, 0);
  setlength(PMaxDisp, 0);
  setlength(cv, 0);
  setlength(cv_entra, 0);
  if lpd <> nil then
    lpd.Free;
  inherited Free;

end;

procedure AlInicio;
begin
  uCosa.registrarClaseDeCosa(TMercadoSpot_postizado.ClassName, TMercadoSpot_postizado);
  uCosa.registrarClaseDeCosa(TFichaMercadoSpot_postizado.ClassName, TFichaMercadoSpot_postizado);
end;

procedure AlFinal;
begin
end;

end.
