unit uArcos;

interface

uses
  Classes,
  SysUtils, xmatdefs, usimplex, uglobs,
  uNodos, uHidroConEmbalse,
  uActorNodal,
  ucosa,
  uCosaConNombre,
  ufechas, uconstantesSimSEE,
  uFichasLPD,
  uFuentesAleatorias;

resourcestring
  rsArcoSimple = 'Arco';

type

  TArco = class;

  { TFichaArco }

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

    rendimiento: TDAofNReal;
    peaje: TDAofNReal;
    PMax: TDAofNreal;
    fd: NReal; // factor de disponibilidad
    tRepHoras: NReal;
    flg_ConsiderarPeajeEnElDespacho: boolean;
    flg_SumarPeajeAlCDP: boolean;
    factorPeajeCDP: NReal;
    PagoPorDisponibilidad_USD_MWh: NReal; // [USD/MWh] Pago por Potencia

    flg_usarFuentePmax: Boolean;
    fuentePmax:TFuenteAleatoria;
    bornePmax: string;
    numeroBornePmax:Integer;

    {$IFDEF ARCO_GEMELOS}
    flg_ConArgoGemelo: boolean;
    ArcoGemelo: TArco;
    PMaxConjunto: NReal;
    {$ENDIF}
    (**************************************************************************)


    constructor Create(capa: integer; fecha: TFecha; periodicidad: TPeriodicidad;
      rendimiento, peaje, PMax: TDAofNReal; fd: NReal; tRepHoras: NReal;
      ConsidearrPeajeEnElDespacho, SumarPeajeAlCDP: boolean;
      FactorPeajeCDP: NReal; PagoPorDisponibilidad_USD_MWh: NReal;
      ArcoGemelo: TArco; flg_ConArcoGemelo: boolean; PMaxConjunto: integer;
      flg_usarFuentePmax:Boolean; fuentePmax: TFuenteAleatoria; bornePmax:String);

    constructor Create_dummy; override;
    procedure Free_dummy; override;

    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;



  { TArco }

  TArco = class(TActorBiNodal)
  public
    NLineasDisponibles: integer;
    NLineasDisponiblesPorfd: NReal;//FP@201808081412: Agrego para que este
    //disponible en prepararPaso_ps_pre

    PMaxDisponible_: TDAofNReal;
    // resultados del despacho
    P_Entrante: TDAOfNReal; //[MW] Potencia Entrante

    //Costo por poste de la transmisión
    //costo[iposte]:= P[iposte] * pa.peaje * globs.durpos[iposte];
    costo: TDAofNReal;

    // valor por poste del multiplicador de Lagrange de la restrición de PMax
    costo_congestion: TDAOfNReal; //[USD/MW]

    pa: TFichaArco;
    Condicion_Fuente: TFuenteAleatoria;
    Condicion_nombreBorne: string;
    Condicion_numeroBorne: integer;

    Condicion_HidroConEmbalse: THidroConEmbalse;
    cv_Hidro: NReal;

    constructor Create(capa: integer; nombre: string;
      lpdUnidades, lpd: TFichasLPD; Entrada, Salida: TNodo;
  Condicion_Fuente: TFuenteAleatoria; Condicion_NombreBorne: string;
  Condicion_HidroConEmbalse: THidroConEmbalse; cv_Hidro: NReal;
  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(CatalogoReferencias:
      TCatalogoReferencias);
      override;
    function InfoAd_: string; override;
    class function DescClase: string; override;

    procedure SorteosDelPaso(sortear: boolean); override;
    procedure prepararPaso_ps_pre; override;
    procedure prepararPaso_ps; 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 dump_Variables(var f: TextFile; charIndentacion: char); override;

    procedure PubliVars; 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;


//-------------------
// Métodos de TArco
//===================

constructor TArco.Create(capa: integer; nombre: string;
  lpdUnidades, lpd: TFichasLPD; Entrada, Salida: TNodo;
  Condicion_Fuente: TFuenteAleatoria; Condicion_NombreBorne: string;
  Condicion_HidroConEmbalse: THidroConEmbalse; cv_Hidro: NReal;
  xFuenteIdxP: TFuenteAleatoria; xBorneIdxP: string );
begin
  inherited Create(capa, nombre, nacimiento, muerte, lpdUnidades, Entrada, Salida,
  xFuenteIdxP, xBorneIdxP );
  self.lpd := lpd;
  self.Condicion_Fuente := Condicion_Fuente;
  self.Condicion_NombreBorne := Condicion_NombreBorne;
  self.Condicion_numeroBorne := -1;
  self.Condicion_HidroConEmbalse := Condicion_HidroConEmbalse;
  self.cv_Hidro := cv_Hidro;
end;

function TArco.Rec: TCosa_RecLnk;
begin
  Result := inherited Rec;
  Result.addCampoDef('lpd', TCosa(lpd));
  Result.addCampoDef_ref('Condicion_fuente', TCosa(Condicion_fuente), Self, 139);
  Result.addCampoDef('Condicion_nombreBorne', Condicion_nombreBorne, 139);
  {$IFDEF ARCO_GEMELOS}
  Result.addCampoDef_ref('Condicion_HidroConEmbalse',
    TCosa(Condicion_HidroConEmbalse), Self, 150);
  Result.addCampoDef('cv_Hidro', cv_Hidro, 150);
  {$ENDIF}
end;

procedure TArco.BeforeRead(version, id_hilo: integer);
begin
  inherited BeforeRead(version, id_hilo);
  Condicion_numeroBorne := -1;
end;

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


procedure TArco.PrepararMemoria(Catalogo: TCatalogoReferencias; globs: TGlobs);
var
  n, k, j: integer;
  af: TFichaArco;

begin
  inherited prepararMemoria(Catalogo, globs);
  setlength(P_Entrante, globs.NPostes);
  SetLength(costo, globs.NPostes);
  setlength(costo_congestion, globs.NPostes);
  setlength(PMaxDisponible_, globs.NPostes);


  if Condicion_fuente <> nil then
    Condicion_numeroBorne := Condicion_fuente.idBorne(Condicion_nombreBorne);


  for j := 0 to self.lpd.Count - 1 do
  begin
    af := self.lpd.items[j] as TFichaArco;
    if (length(af.rendimiento) <> globs.NPostes) then
    begin
      n := length(af.rendimiento);
      setlength(af.rendimiento, globs.NPostes);
      setlength(af.peaje, globs.NPostes);
      setlength(af.PMax, globs.NPostes);
      for k := n to high(af.rendimiento) do
        af.rendimiento[k] := af.rendimiento[n - 1];
      for k := n to high(af.peaje) do
        af.peaje[k] := af.peaje[n - 1];
      for k := n to high(af.PMax) do
        af.PMax[k] := af.PMax[n - 1];
    end;
  end;

end;

procedure TArco.RegistrarParametrosDinamicos(
  CatalogoReferencias: TCatalogoReferencias);
var
  i:Integer;
  ficha: TFichaArco;
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 := TFichaArco(lpd[i]);
    if ficha.flg_usarFuentePmax then
      ficha.numeroBornePmax := ficha.fuentePmax.IdBorne(ficha.bornePmax);
  end;

end;

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

class function TArco.DescClase: string;
begin
  Result := rsArcoSimple;
end;


function TArco.get_pa_FD(kTipoUnidad: integer): NReal;
begin
  Result := pa.fd;
end;

function TArco.get_pa_TMR(kTipoUnidad: integer): NReal;
begin
  Result := pa.tRepHoras;
end;


procedure TArco.SorteosDelPaso(sortear: boolean);
var
  fd: NReal;
begin
//FP@201808081412: Como el actor utiliza fuentes que deben estar sorteadas,
// paso el calculo de PMaxDisponible_ a prepararPaso_ps_pre

  if globs.ObligarDisponibilidad_1_ then
  begin
    NLineasDisponibles := paUnidades.nUnidades_Instaladas[0] -
      paUnidades.nUnidades_EnMantenimiento[0];
    fd:=1;
  end
  else if sortear then
  begin
    ActualizarProbabilidadesReparacionYRotura_(pa.fd, pa.tRepHoras);
    NLineasDisponibles := Sorteos_RepRotUnidades;
    fd:=1;
  end
  else
  begin
    NLineasDisponibles := paUnidades.nUnidades_Instaladas[0] -
      paUnidades.nUnidades_EnMantenimiento[0];
    fd:=pa.fd;
  end;

  NLineasDisponiblesPorfd:=NLineasDisponibles * fd;

end;

procedure TArco.prepararPaso_ps_pre;
var
  kPoste: Integer;
begin
  inherited prepararPaso_ps_pre;

  for kPoste := 0 to globs.NPostes - 1 do
  if pa.flg_usarFuentePmax then
    PMaxDisponible_[kPoste] := pa.fuentePmax.Bornera[pa.numeroBornePmax]* NLineasDisponiblesPorfd
  else
    PMaxDisponible_[kPoste] := pa.PMax[kPoste]* NLineasDisponiblesPorfd;

end;

procedure TArco.prepararPaso_ps;
var
  flg_Cortar: boolean;
  cvBon: NReal;
  ce: NReal;

begin
  flg_Cortar := False;
  if (Condicion_HidroConEmbalse <> nil) then
  begin
    ce := 1000 * 9.8 * (Condicion_HidroConEmbalse.h_actual - 7.05) * 0.867 / 3600;
    cvBon := Condicion_HidroConEmbalse.cv_agua_USD_Hm3_Dec / ce;
    flg_Cortar := cv_Hidro < cvBon;
  end;

  if ((Condicion_Fuente <> nil) and
    (Condicion_Fuente.Bornera[Condicion_numeroBorne] <= 0)) or flg_Cortar then
  begin
    Vclear(PMaxDisponible_);
    // Vclear(pa.PMax);
    NLineasDisponibles := 0;
  end;
end;

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

  {$IFDEF ARCO_GEMELOS}
  if pa.flg_ConArgoGemelo then
  begin
    self.ires := ires;
    ires := ires + globs.NPostes;
  end;
  {$ENDIF}

end;

procedure TArco.opt_cargue(s: TSimplex);
var
  ibaseResSal, ibaseResEnt: integer;
  iposte: integer;
begin
  if NLineasDisponibles > 0 then
  begin
    // Tenemos que aportar a las restricciones de
    // demanda de los nodos de
    ibaseResEnt := NodoA.ires;
    ibaseResSal := NodoB.ires;
    for iposte := 0 to globs.NPostes - 1 do
    begin
      s.pon_e(ibaseResEnt + iposte, ivar + iposte, -1);
      s.pon_e(ibaseResSal + iposte, ivar + iposte, pa.rendimiento[iposte]);

      // Ahora agregamos a la función de UTILIDAD (-costo)
      if pa.flg_ConsiderarPeajeEnElDespacho then
        s.pon_e(s.nf, ivar + iposte, -pa.peaje[iposte] * globs.durpos[iposte]);
    end;
  end;


  {$IFDEF ARCO_GEMELOS}
  if pa.flg_ConArgoGemelo then
  begin
    if NLineasDisponibles > 0 then
    begin
      for iposte := 0 to globs.NPostes - 1 do
      begin
        s.pon_e(ires + iposte, ivar + iposte, -1);
      end;
    end;


    if (pa.ArcoGemelo <> nil) and (pa.ArcoGemelo.NLineasDisponibles > 0) then
    begin
      for iposte := 0 to globs.NPostes - 1 do
      begin
        s.pon_e(ires + iposte, pa.ArcoGemelo.ivar + iposte, -1);
      end;
    end;

    for iposte := 0 to globs.NPostes - 1 do
      s.pon_e(ires + iposte, s.nc, pa.PMaxConjunto);
  end;
  {$ENDIF}
end;

procedure TArco.opt_fijarRestriccionesDeCaja(s: TSimplex);
var
  iposte: integer;
begin
  if NLineasDisponibles > 0 then
    // Le fijamos como cota máxima PMax a la potencia en todos los postes
    for iposte := 0 to globs.NPostes - 1 do
      if PMaxDisponible_[iposte] >= 0 then
        s.cota_sup_set(ivar + iposte, PMaxDisponible_[iposte])
      else
        s.cota_inf_set(ivar + iposte, PMaxDisponible_[iposte])

end;

procedure TArco.opt_leerSolucion(s: TSimplex);
var
  iposte: integer;
  m: NReal;
  P: NReal;
  ccg: NReal;
  aflgx: shortint;
begin
  CostoDirectoDelPaso := 0;
  Ingreso_PorDisponibilidad_ := 0;
  Ingreso_PorEnergia_ := 0;
  vclear(P_Entrante);
  vclear(P_NodoA);
  vclear(P_NodoB);
  vclear(costo_congestion);

  if NLineasDisponibles = 0 then
  begin
    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;
    exit;
  end;


  if pa.PagoPorDisponibilidad_USD_MWh <> 0 then
    for iposte := 0 to globs.NPostes - 1 do
      Ingreso_PorDisponibilidad_ :=
        Ingreso_PorDisponibilidad_ + pa.PagoPorDisponibilidad_USD_MWh *
        PMaxDisponible_[iposte] * globs.DurPos[iposte];

  // recuperamos los valores de Potencia despachada
  for iposte := 0 to globs.NPostes - 1 do
  begin
    P := s.xval(ivar + iposte);
    P_Entrante[iposte] := P;
    if pa.flg_SumarPeajeAlCDP then
    begin
      m := P * pa.peaje[iposte] * globs.DurPos[iposte];
      costo[iposte] := m;
      Ingreso_PorEnergia_ := Ingreso_PorEnergia_ + m;
    end;


    ccg:= s.xmult_caja( ivar + iposte, aflgx );

    Lambda_P[iPoste] := ccg / globs.DurPos[iposte] / pa.rendimiento[iposte];

    if P > AsumaCero then
    begin
      costo_congestion[iposte] := -ccg;
      P_NodoA[iposte] := -P;
      P_NodoB[iposte] := P * pa.rendimiento[iposte];
      cv_Spot[iPoste] := NodoB.cmarg[iposte] - Lambda_P[iPoste];
    end
    else
    begin
      costo_congestion[iposte] := 0;
      P_NodoA[iposte] := 0;
      P_NodoB[iposte] := 0;
      cv_Spot[iPoste] := NodoB.cmarg[iposte] - Lambda_P[iPoste];
    end;


  end;

  if pa.flg_SumarPeajeAlCDP then
  begin
    costoDirectoDelPaso := costoDirectoDelPaso + Ingreso_PorEnergia_ *
      pa.factorPeajeCDP;
    Ingreso_PorEnergia_ := (1 - pa.factorPeajeCDP) * Ingreso_PorEnergia_;
  end;

end;

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

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

procedure TArco.dump_Variables(var f: TextFile; charIndentacion: char);
var
  iposte: integer;

begin
  inherited dump_Variables(f, charIndentacion);

  Write(f, charIndentacion, 'peaje=');
  for iposte := 0 to high(pa.peaje) do
  begin
    if iposte > 0 then
      Write(f, ',');
    Write(f, ' ' + FloatToStrF(pa.peaje[iposte], ffFixed, 10, 3));
  end;
  writeln(f);

  Write(f, charIndentacion, 'PMax=');
  for iposte := 0 to high(pa.PMax) do
  begin
    if iposte > 0 then
      Write(f, ',');
    Write(f, ' ', FloatToStrF(pa.PMax[iposte], ffFixed, 10, 3));
  end;
  writeln(f);

  writeln(f);
end;

procedure TArco.PubliVars;
begin
  inherited PubliVars;
  PublicarVariableVR('P', '[MW]', 6, 1, P_Entrante, True, True);
  PublicarVariableVR('Costo', '[USD]', 6, 1, costo, True, True);
  PublicarVariableVR('CostoCongestion', '[USD/MW]', 6, 1, costo_congestion, True, True);
  PublicarVariableNI('NLineasDisponibles', '[u]', NLineasDisponibles, True);
end;

destructor TArco.destroy;
begin
  if lpd <> nil then
    lpd.Free;
  setlength(P_Entrante, 0);
  SetLength(costo, 0);
  SetLength(costo_congestion, 0);
  setlength(PMaxDisponible_, 0);
  inherited destroy;
end;

//---------------------
//Metodos de TFichaArco
//=====================

constructor TFichaArco.Create(capa: integer; fecha: TFecha;
  periodicidad: TPeriodicidad; rendimiento, peaje, PMax: TDAofNReal; fd: NReal;
  tRepHoras: NReal; ConsidearrPeajeEnElDespacho, SumarPeajeAlCDP: boolean;
  FactorPeajeCDP: NReal; PagoPorDisponibilidad_USD_MWh: NReal;
  ArcoGemelo: TArco; flg_ConArcoGemelo: boolean; PMaxConjunto: integer;
  flg_usarFuentePmax: Boolean; fuentePmax: TFuenteAleatoria; bornePmax: String);
begin
  self.rendimiento := rendimiento;
  self.peaje := peaje;
  self.PMax := PMax;
  self.fd := fd;
  self.tRepHoras := tRepHoras;
  self.flg_ConsiderarPeajeEnElDespacho := ConsidearrPeajeEnElDespacho;
  self.flg_SumarPeajeAlCDP := SumarPeajeAlCDP;
  self.factorPeajeCDP := FactorPeajeCDP;
  self.PagoPorDisponibilidad_USD_MWh := PagoPorDisponibilidad_USD_MWh;

  self.flg_usarFuentePmax:=flg_usarFuentePmax;
  self.fuentePmax:=fuentePmax;
  self.bornePmax:=bornePmax;


  {$IFDEF ARCO_GEMELOS}
  self.ArcoGemelo := ArcoGemelo;
  self.flg_ConArgoGemelo := flg_ConArcoGemelo;
  self.PMaxConjunto := PmaxConjunto;
  {$ENDIF}

  // FP y FB 31/7/18: Cambiamos el orden para que estén asignadas las variables
  inherited Create(capa, fecha, periodicidad);


end;



constructor TFichaArco.Create_dummy;
begin
  setlength(rendimiento, 1);
  setlength(peaje, 1);
  setlength(PMax, 1);
end;

procedure TFichaArco.Free_dummy;
begin
  setlength(rendimiento, 0);
  setlength(peaje, 0);
  setlength(PMax, 0);
  inherited Free;
end;

function TFichaArco.Rec: TCosa_RecLnk;
begin
  Result := inherited Rec;
  Result.addCampoDef('rendimiento', rendimiento[0], 0, 14);
  Result.addCampoDef('peaje', peaje[0], 0, 14);
  Result.addCampoDef('fd', fd, 0, 14);
  Result.addCampoDef('PMax', PMax[0], 0, 14);
  Result.addCampoDef('rendimiento', rendimiento[0], 14, 69);
  Result.addCampoDef('peaje', peaje[0], 14, 69);
  Result.addCampoDef('fd', fd, 14, 69);
  Result.addCampoDef('tRepHoras', tRepHoras, 14, 69);
  Result.addCampoDef('PMax', PMax[0], 14, 69);
  Result.addCampoDef('rendimiento', rendimiento, 69);
  Result.addCampoDef('peaje', peaje, 69);
  Result.addCampoDef('PMax', PMax, 69);
  Result.addCampoDef('fd', fd, 69);
  Result.addCampoDef('tRepHoras', tRepHoras, 69);
  Result.addCampoDef('ConsiderarPeajeEnElDespacho',
    flg_ConsiderarPeajeEnElDespacho, 70);
  Result.addCampoDef('SumarPeajeAlCDP', flg_SumarPeajeAlCDP, 70);
  Result.addCampoDef('factorPeajeCDP', factorPeajeCDP, 70);
  Result.addCampoDef('PagoPorDisponibilidad_USD_MWh',
    PagoPorDisponibilidad_USD_MWh, 98);
  {$IFDEF ARCO_GEMELOS}
  Result.addCampoDef('flg_ConArgoGemelo', flg_ConArgoGemelo, 150);
  Result.addCampoDef_ref('ArcoGemelo', TCosa(ArcoGemelo), self, 150);
  Result.addCampoDef('PMaxConjunto', PMaxConjunto, 150);
  {$ENDIF}

  Result.addCampoDef('flg_usarFuentePmax', flg_usarFuentePmax, 186);
  Result.addCampoDef_Ref('fuentePmax', TCosa(fuentePmax), self, 186);
  Result.addCampoDef('bornePmax', bornePmax, 186);

end;

procedure TFichaArco.BeforeRead(version, id_hilo: integer);
begin
  inherited BeforeRead(version, id_hilo);
  flg_ConsiderarPeajeEnElDespacho := True;
  flg_SumarPeajeAlCDP := True;
  factorPeajeCDP := 1.0;
  setlength(rendimiento, 1);
  setlength(peaje, 1);
  setlength(PMax, 1);
end;

procedure TFichaArco.AfterRead(f:TArchiTexto);
begin
  inherited AfterRead(f);

  if f.Version < 186 then
  begin
    flg_usarFuentePmax:=False;
    fuentePmax:=nil;
    bornePmax:='';
  end;
end;


function TFichaArco.InfoAd_: string;
begin
  Result := 'PMáx= ' + DAOfNRealToStr_(PMax, 10, 1, ';') + ' MW, ' +
    'fDisp= ' + FloatToStrF(fd, ffGeneral, 10, 2) + ' p.u., ' +
    'tRep= ' + FloatToStrF(tRepHoras, ffGeneral, 10, 1) + 'h, ' +
    'Rendimiento= ' + DAOfNRealToStr_(rendimiento, 10, 2, ';') +
    ' p.u., ' + 'peaje= ' + DAOfNRealToStr_(peaje, 10, 1, ';') + ' USD/MWh';
end;

destructor TFichaArco.destroy;
begin
  setlength(rendimiento, 0);
  setlength(peaje, 0);
  setlength(PMax, 0);
  inherited destroy;
end;


procedure AlInicio;
begin
  registrarClaseDeCosa(TArco.ClassName, TArco);
  registrarClaseDeCosa(TFichaArco.ClassName, TFichaArco);
end;

procedure AlFinal;
begin
end;

end.
