unit ucontratomodalidaddevolucion;

// Durante la optmización usa el mismo método de derviación
// cotinua que durante la simlación. Si no está definido, usa
// un método de derivada en "estrellita" que puede ser más eficiente
{$DEFINE OPT_DERIV_CONTINUA}


interface

uses
  Classes,
  xMatDefs, uSimplex,
  ucosa, uCosaConNombre,
  uGlobs, uFechas,
  uComercioInternacional,
  uNodos,
  SysUtils,
  uEstados,
  uFichasLPD,
  uFuentesAleatorias,
  uconstantesSimSEE,
  uevapUruguay;

resourcestring
  rsContratoModalidadDevolucion = 'Contrato modalidad devolución';

type
  TContratoModalidadDevolucion = class; // forward declaration

  { TFichaContratoModalidadDevolucion }

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

    // Definicion de las ventanas de tiempo.
    // Se supone que dtImpoIni < dtImpFin < dtDevoIni < dtDevoFin
    dtImpoIni, dtImpoFin, dtDevoIni, dtDevoFin: TFecha;
    EMaxImp: NReal; //Monto máximo de energía que es posible importar en esta modalidad.

    PMaxImp: NReal; //Potencia máxima usable para importación.
    cvImp: NReal;
    //Costo variable asociado a la importación para reflejar eventuales peajes.
    renImp: NReal; //Rendimiento de la Importación en p.u. (1-renImp) son pérdidas.
    fdImp: NReal; // factor de disponibilidad para importación

    PMaxExp: NReal; //Potencia máxima para exportación (negativa).
    cvExp: NReal; //Costo variable asociado a la exportación para reflejar peajes
    renExp: NReal; //Rendimiento de la Exportación.
    cvDevolucion: NReal;
    //Costo variable aplicable a la energía que no halla sido devuelta al cierre de la ventana de devolución.
    fdExp: NReal; // factor de disponibilidad para exportación.

    fi: NReal;
    //factor de incremento. La energía que retiro del otro mercado, la tengo de devolver multiplicada por este factor.

    (**************************************************************************)


    constructor Create(capa: integer; fecha: TFecha; periodicidad: TPeriodicidad;
      dtImpoIni_, dtImpoFin_, dtDevoIni_, dtDevoFin_: TFecha;
      EMaxImp_, PMaxImp_, cvImp_, renImp_, fdImp_, fi_, PMaxExp_,
      cvExp_, renExp_, cvDevolucion_, fdExp_: NReal);

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

  { TContratoModalidadDevolucion }

  TContratoModalidadDevolucion = class(TComercioInternacional)
  public
    E_Credito_ini: NReal; // valor inicial
    //Variable de Estado
    NDisc: integer; // discretizacion del estad

    pa: TFichaContratoModalidadDevolucion;

    X_E_Credito: NReal;
    Xs_E_Credito: NReal;

    dE_Credito: NReal;
    cv: NReal; // costo variable visto por el despacho

    cvDirecto: NReal;

    // derivada del costo futuro respecto al Crédito de Energía
    dCFdC: NReal;

    Disponible: boolean;
    PMax: NReal;

    fase: integer; // 1 = Impo; 2= Devolucion; 3=UltimoPasoDevolucion; 0= Inerte

    constructor Create(capa: integer; nombre: string; nacimiento, muerte: TFecha;
      lpdUnidades, lpd: TFichasLPD; nodo: TNodo; E_Credito_ini_: NReal;
  NDisc_: integer; 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 PotenciaFirme: NReal; override;
    function InfoAd_: string; override;
    class function DescClase: string; override;


    //      procedure Sim_Inicio; override;
    procedure Sim_Cronica_Inicio; 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;
    procedure opt_nvers(var ivar, ivae, ires: integer); override;
    procedure opt_cargue(s: TSimplex); override;
    procedure EvolucionarEstado; 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 PosicionarseEnEstrellita; override;
    procedure ActualizarEstadoGlobal(flg_Xs: boolean); override;
    procedure optx_nvxs(var ixr, ixd, iauxr, iauxd: integer); override;
    procedure optx_RegistrarVariablesDeEstado(adminEstados: TAdminEstados); override;
    destructor destroy; override;
    procedure PubliVars; override;

    procedure dump_Variables(var f: TextFile; charIndentacion: char); override;
    {$IFDEF BOSTA}
    procedure AfterInstantiation; override;
    {$ENDIF}
  end;

procedure AlInicio;
procedure AlFinal;

implementation


constructor TContratoModalidadDevolucion.Create(capa: integer;
  nombre: string; nacimiento, muerte: TFecha; lpdUnidades, lpd: TFichasLPD;
  nodo: TNodo; E_Credito_ini_: NReal; NDisc_: integer;
  xFuenteIdxP: TFuenteAleatoria; xBorneIdxP: string );
begin
  inherited Create(capa, nombre, nacimiento, muerte, lpdUnidades, nodo,
  xFuenteIdxP, xBorneIdxP );
  Self.lpd := lpd;
  self.E_Credito_ini := E_Credito_ini_;
  self.NDisc := NDisc_;
end;

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

procedure TContratoModalidadDevolucion.BeforeRead(version, id_hilo: integer);
begin
  lpd := nil;
  inherited BeforeRead(version, id_hilo);
end;

procedure TContratoModalidadDevolucion.AfterRead(f:TArchiTexto);
begin
  inherited AfterRead(f);
  pa := nil;
  nodo := nil;
  if lpd <> nil then
    lpd.Propietario := self;
end;


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

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

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

function TContratoModalidadDevolucion.PotenciaFirme: NReal;
begin
  Result := 0;
end;

class function TContratoModalidadDevolucion.DescClase: string;
begin
  Result := rsContratoModalidadDevolucion;
end;

function TContratoModalidadDevolucion.get_pa_FD(kTipoUnidad: integer): NReal;
begin
  case fase of
    1: Result := pa.fdImp;
    2, 3: Result := pa.fdExp;
    else
      Result := 0;
  end;
end;

function TContratoModalidadDevolucion.get_pa_TMR(kTipoUnidad: integer): NReal;
begin
  Result := 8;
end;



procedure TContratoModalidadDevolucion.SorteosDelPaso(sortear: boolean);
begin

  // calculamos la fase correspondiente al paso.
  if (globs.FechaInicioDelpaso.dt >= pa.dtImpoIni.dt) and
    (globs.FechaInicioDelpaso.dt <= pa.dtImpoFin.dt) then
    fase := 1
  else
  if (globs.FechaInicioDelpaso.dt >= pa.dtDevoIni.dt) and
    (globs.FechaInicioDelpaso.dt <= pa.dtDevoFin.dt) then
    if (globs.FechaFinDelPaso.dt < pa.dtDevoFin.dt) then
      fase := 2
    else
      fase := 3
  else
    fase := 0;

  // 1 = Impo; 2= Devolucion; 3=UltimoPasoDevolucion; 0= Inerte

  // según la fase calculamos si hay potencia disponible y ponemos su valor
  // en PMax. Este es el límite de caja de las potencias.

  if fase = 0 then
  begin
    Disponible := False;
    PMax := 0;
    exit;
  end;

  if globs.ObligarDisponibilidad_1_ then
  begin
    Disponible := True;
    if fase = 1 then
      PMax := pa.PMaxImp
    else
      PMax := pa.PMaxExp;
  end
  else
  begin
    if (sortear) then
    begin
      if fase = 1 then
      begin
        ActualizarProbabilidadesReparacionYRotura_(pa.fdImp, 8);
        Disponible := Sorteos_RepRotUnidades > 0;
        if Disponible then
          PMax := pa.PMaxImp
        else
          PMax := 0;
      end
      else
      begin
        ActualizarProbabilidadesReparacionYRotura_(pa.fdExp, 8);
        Disponible := Sorteos_RepRotUnidades > 0;
        if Disponible then
          PMax := pa.PMaxExp
        else
          PMax := 0;
      end;
    end
    else
    begin
      Disponible := True;
      if fase = 1 then
        PMax := pa.PMaxImp * pa.fdImp
      else
        PMax := pa.PMaxExp * pa.fdExp;
    end;
  end;
end;

procedure TContratoModalidadDevolucion.dump_Variables(var f: TextFile;
  charIndentacion: char);
begin
  inherited dump_Variables(f, charIndentacion);
  writeln(f, charIndentacion, 'E_Credito[m]= ', FloatToStrF(
    X_E_Credito, ffFixed, 10, 3));
  writeln(f, charIndentacion, 'fase= ', IntToStr(fase));
  writeln(f);
end;


{$IFDEF BOSTA}
procedure TContratoModalidadDevolucion.AfterInstantiation;
begin
  inherited AfterInstantiation;

  pa := nil;
  nodo := nil;
  if lpd <> nil then
    lpd.Propietario := self;

end;
{$ENDIF}

procedure TContratoModalidadDevolucion.PrepararPaso_ps;
var
  dCFdC_Inc, dCFdC_Dec: NReal;
  resCode:Integer;
  aux:NReal;
begin
  if fase = 0 then
  begin
    cvDirecto := 0;
  end;

  {$IFDEF DERIV_20}
      if globs.EstadoDeLaSala = CES_OPTIMIZANDO then
        globs.CF.devxr_estrella_20(ixr, globs.kPaso_Opt + 1, dCFdC_Inc, dCFdC_Dec)
      else
        globs.CF.devxr_continuo_20(ixr, globs.kPaso_Opt + 1, dCFdC_Inc, dCFdC_Dec);
  {$ELSE}
      if globs.EstadoDeLaSala = CES_OPTIMIZANDO then
      begin
  {$IFDEF OPT_DERIV_CONTINUA}
        globs.CF.devxr_continuo(ixr, globs.kPaso_Opt + 1, dCFdC_Inc, dCFdC_Dec, resCode, aux);
  {$ELSE}
        globs.CF.devxr_estrella(ixr, globs.kPaso_Opt + 1, dCFdC_Inc, dCFdC_Dec, resCode);
  {$ENDIF}
      end
      else
      begin
        globs.CF.devxr_continuo(ixr, globs.kPaso_Opt + 1, dCFdC_Inc,
          dCFdC_Dec, resCode, aux);
      end;
  {$ENDIF}


  //globs.CF.devxr_continuo_20(ixr, globs.kPaso_Opt + 1, dCFdC_Inc, dCFdC_Dec);

  if fase = 1 then
  begin
    dCFdC := dCFdC_Dec;
    cvDirecto := pa.cvImp / pa.renImp;
    cv := cvDirecto - dCFdC * pa.fi * globs.fActPaso / pa.renImp;
  end
  else
  begin
    dCFdC := dCFdC_Inc;
    cvDirecto := -pa.cvExp;
    cv := cvDirecto - dCFdC * pa.renExp * globs.fActPaso;
    if fase = 3 then
      cv := cv + pa.cvDevolucion;
  end;
end;

procedure TContratoModalidadDevolucion.opt_nvers(var ivar, ivae, ires: integer);
begin
  self.ivar := ivar;
  self.ires := ires;
  if (fase > 0) and Disponible then
  begin
    ivar := ivar + globs.NPostes; // PGen por poste
    ires := ires + 1;  // 0 <= EImp <= EImpMax  (segun la fase una de estas)
  end;
end;

procedure TContratoModalidadDevolucion.opt_cargue(s: TSimplex);
var
  ibaseres: integer;
  iposte: integer;
  jres: integer;
  maux: NReal;
begin

  if (fase = 0) then
    exit;
  if not Disponible then
  begin
    if fase = 3 then // último paso de la ventana de devolución.
      s.acum_e(s.nf, s.nc, -(pa.EMaxImp - X_E_Credito) * pa.cvDevolucion);
    // Pago lo que no halla devuelto
    exit;
  end;

  ibaseres := nodo.ires;
  // Tenemos que aportar a las restricciones de demanda del nodo al que está
  // conectado el generador
  for iposte := 0 to globs.NPostes - 1 do
    s.pon_e(ibaseres + iposte, ivar + iposte, 1);

  jres := ires;

  if fase = 1 then
  begin
    // si estoy en la fase de IMPORTACION, la restricción a respetar es
    // que  E_Credito[k+1]= E_Credito[k] - sum( P_i*dt_i*fi/renImp) >= 0
    maux := pa.fi / pa.renImp;
    for iposte := 0 to globs.NPostes - 1 do
      s.pon_e(jres, ivar + iposte, -globs.durpos[iposte] * maux);
    s.pon_e(jres, s.nc, X_E_Credito);
  end
  else
  begin
    // si estoy en la fase de DEVOLUCION, la restricción a respetar es
    // EmaxImp - E_Credito[k+1] >= 0
    maux := pa.renExp;
    for iposte := 0 to globs.NPostes - 1 do
      s.pon_e(jres, ivar + iposte, globs.durpos[iposte] * maux);
    s.pon_e(jres, s.nc, pa.EMaxImp - X_E_Credito);
  end;

  // Ahora agregamos a la función de UTILIDAD (-costo)
  for iposte := 0 to globs.NPostes - 1 do
    s.pon_e(s.nf, ivar + iposte, -cv * globs.durpos[iposte]);

  if fase = 3 then // último paso de la ventana de devolución.
    s.acum_e(s.nf, s.nc, -(pa.EMaxImp - X_E_Credito) * pa.cvDevolucion);
  // Pago lo que no halla devuelto
end;

procedure TContratoModalidadDevolucion.opt_fijarRestriccionesDeCaja(s: TSimplex);
var
  iposte: integer;
begin
  // Le fijamos como cota máxima PMax a la potencia en todos los postes
  if Disponible then
  begin
    if fase = 1 then
      for iposte := 0 to globs.NPostes - 1 do
        s.cota_sup_set(ivar + iposte, PMax)
    else
    begin
      for iposte := 0 to globs.NPostes - 1 do
      begin  // si fase > 1 se PMax <= P_i <= 0
        s.cota_inf_set(ivar + iposte, PMax);
        s.cota_sup_set(ivar + iposte, 0);
      end;
    end;
  end;
end;

procedure TContratoModalidadDevolucion.Sim_Cronica_Inicio;
begin
  // calculamos la fase correspondiente al paso.
  if (globs.FechaInicioDelpaso.dt >= pa.dtImpoIni.dt) and
    (globs.FechaInicioDelpaso.dt <= pa.dtImpoFin.dt) then
    fase := 1
  else
  if (globs.FechaInicioDelpaso.dt >= pa.dtDevoIni.dt) and
    (globs.FechaInicioDelpaso.dt <= pa.dtDevoFin.dt) then
    if (globs.FechaFinDelPaso.dt < pa.dtDevoFin.dt) then
      fase := 2
    else
      fase := 3
  else
    fase := 0;

  inherited Sim_Cronica_Inicio;
  X_E_Credito := E_Credito_ini;

end;

procedure TContratoModalidadDevolucion.opt_leerSolucion(s: TSimplex);
var
  iposte: integer;
  E_Credito_: NReal;
begin

  if (fase = 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;
    dE_Credito := 0;
    CostoDirectoDelPaso := 0;
    Xs_E_Credito := X_E_Credito;
    exit;
  end;



  if Disponible then
  begin
    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]; //Multiplicador de Gen
    end;
    if fase = 1 then
      E_Credito_ := s.yval(ires) // y=  E_Credito >= 0
    else
      E_Credito_ := pa.EMaxImp - s.yval(ires); // y=  EImpMax - E_Credito >= 0

    dE_Credito := E_Credito_ - X_E_Credito;

    CostoDirectoDelPaso := -cvDirecto * dE_Credito;

    if fase < 3 then
      Xs_E_Credito := E_Credito_
    else
    begin
      CostoDirectoDelPaso := CostoDirectoDelPaso + (pa.EMaxImp - E_Credito_) *
        pa.cvDevolucion;
      Xs_E_Credito := pa.EMaxImp;
    end;
  end
  else
  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;
    dE_Credito := 0;
    if fase < 3 then
    begin
      CostoDirectoDelPaso := 0;
      Xs_E_Credito := X_E_Credito;
    end
    else
    begin
      // pago y recupero todo el crédito
      CostoDirectoDelPaso := (pa.EMaxImp - X_E_Credito) * pa.cvDevolucion;
      Xs_E_Credito := pa.EMaxImp;
    end;
  end;
end;

procedure TContratoModalidadDevolucion.EvolucionarEstado;
begin
  X_E_Credito := Xs_E_Credito;
end;


function TContratoModalidadDevolucion.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 TContratoModalidadDevolucion.getNombreRes(ires: integer;
  var nombre: string): boolean;
begin
  if (ires = self.ires) then
  begin
    if fase = 1 then
      nombre := self.nombre + '_EImp <= EMaxImp'
    else
      nombre := self.nombre + '_EImp >= 0';
    Result := True;
  end
  else
    Result := False;
end;

procedure TContratoModalidadDevolucion.PubliVars;
begin
  inherited PubliVars;
  PublicarVariableNR('cv', '[USD/MWh]', 6, 1, cv, True);
  PublicarVariableNR('E_Credito', '[MWh]', 6, 1, X_E_Credito, True);
  PublicarVariableNI('fase', '-', fase, True);

  PublicarVariableNR('dE_Credito', '[MWh]', 6, 1, dE_Credito, False);
  PublicarVariableB('Disponible', '-', Disponible, False);
end;

procedure TContratoModalidadDevolucion.optx_nvxs(var ixr, ixd, iauxr, iauxd: integer);
begin
  self.ixr := ixr;
  ixr := ixr + 1;
end;

procedure TContratoModalidadDevolucion.PosicionarseEnEstrellita;
begin
  X_E_Credito := globs.CF.xr[ixr];
  Xs_E_Credito:= X_E_Credito;
end;

procedure TContratoModalidadDevolucion.ActualizarEstadoGlobal(flg_Xs: boolean);
begin
  globs.CF.xr[ixr] := X_E_Credito;
end;

procedure TContratoModalidadDevolucion.optx_RegistrarVariablesDeEstado(
  adminEstados: TAdminEstados);
begin
  adminEstados.Registrar_Continua(
    ixr,
    0.0,
    pa.EMaxImp,
    ndisc,
    nombre + '_E_Credito', // nombre de la variable
    'MWh' // unidades
    );
end;

destructor TContratoModalidadDevolucion.destroy;
begin
  if lpd <> nil then
    lpd.Free;
  inherited destroy;
end;

{*********************************
*Métodos de TFichaHidroConEmbalse*
*********************************}

constructor TFichaContratoModalidadDevolucion.Create(capa: integer;
  fecha: TFecha; periodicidad: TPeriodicidad;
  dtImpoIni_, dtImpoFin_, dtDevoIni_, dtDevoFin_: TFecha;
  EMaxImp_, PMaxImp_, cvImp_, renImp_, fdImp_, fi_, PMaxExp_, cvExp_,
  renExp_, cvDevolucion_, fdExp_: NReal);

begin
  inherited Create(capa, fecha, periodicidad);
  dtImpoIni := dtImpoIni_;
  dtImpoFin := dtImpoFin_;
  dtDevoIni := dtDevoIni_;
  dtDevoFin := dtDevoFin_;

  EMaxImp := EMaxImp_;
  PMaxImp := PMaxImp_;
  cvImp := cvImp_;
  renImp := renImp_;
  fi := fi_;
  fdImp := fdImp_;
  PMaxExp := PMaxExp_;
  cvExp := cvExp_;
  renExp := renExp_;
  cvDevolucion := cvDevolucion_;
  fdExp := fdExp_;

end;

function TFichaContratoModalidadDevolucion.Rec: TCosa_RecLnk;
begin
  Result:=inherited Rec;
  Result.addCampoDef('dtImpoIni', dtImpoIni);
  Result.addCampoDef('dtImpoFin', dtImpoFin);
  Result.addCampoDef('dtDevoIni', dtDevoIni);
  Result.addCampoDef('dtDevoFin', dtDevoFin);
  Result.addCampoDef('EMaxImp', EMaxImp);
  Result.addCampoDef('PMaxImp', PMaxImp);
  Result.addCampoDef('cvImp', cvImp);
  Result.addCampoDef('renImp', renImp);
  Result.addCampoDef('fi', fi);
  Result.addCampoDef('fdImp', fdImp);
  Result.addCampoDef('PMaxExp', PMaxExp);
  Result.addCampoDef('cvExp', cvExp);
  Result.addCampoDef('renExp', renExp);
  Result.addCampoDef('cvDevolucion', cvDevolucion);
  Result.addCampoDef('fdExp', fdExp);
end;

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

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

destructor TFichaContratoModalidadDevolucion.destroy;
begin
  inherited destroy;
end;

procedure AlInicio;
begin
  registrarClaseDeCosa(TContratoModalidadDevolucion.ClassName,
    TContratoModalidadDevolucion);
  registrarClaseDeCosa(TFichaContratoModalidadDevolucion.ClassName,
    TFichaContratoModalidadDevolucion);
end;

procedure AlFinal;
begin
end;

end.
