unit urestriccionerogados;
interface

uses
  Classes,
  Math, xMatDefs, uSimplex,
  ucosa, uCosaConNombre,
  uGlobs, uFechas, uNodos,
  SysUtils,
  uFichasLPD,
  uGeneradores,
  uestados,
  uconstantesSimSEE,
  uparseadorsupersimple, ufuncionesbasicas,
  uFuentesAleatorias;

resourcestring
  rsRestriccionErogados = 'Restricción de Erogados';

type
  TRestriccionErogados = class; // forward declaration

{
  Este actor permite definir contribuciones de aportes de los erogados de
  Centrales HIdráulicas (centrales aguas arriba) y en base a los mismos y
  a una fórmula de cálculo de evolución de una COTA_Controlada aplicar una
  restricción del erogado que penaliza la superación de la COTA_Controlada

  COTA_Controlada := Def0( X );
  X_s = a_0 X + sum a_j inv_Def_j( QE_j ) + b u;


}

  { TFichaRestriccionErogados }

  TFichaRestriccionErogados = class(TFichaLPD)
  public

    (**************************************************************************)
    (*              A T R I B U T O S   P E R S I S T E N T E S               *)
    (**************************************************************************)


    CVpen: NReal;  // Costo Variable de la penalidad. [USD/(m*dia)]
    hPen : NReal;  // Cota Maxima [m]



    centralesAguasArriba: TListaCentralesAguasArriba{of TGeneradorHidraulico};

    constructor Create(capa: integer; fecha: TFecha; periodicidad: TPeriodicidad;
      hPen, CVpen: NReal; centralesAguasArriba: TListaCentralesAguasArriba);

    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;


  { TRestriccionErogados }

  TRestriccionErogados = class(TGeneradorHidraulico)
  public

    (**************************************************************************)
    (*              A T R I B U T O S   P E R S I S T E N T E S               *)
    (**************************************************************************)

    fuenteDeRuido: TFuenteAleatoria;
    nombreBorne: string;
    ALFA : TDAofNReal;
    (**************************************************************************)

    pa: TFichaRestriccionErogados;

    numeroBorne: integer;

    X_h, Xs_h: NReal; // [m] (COTA)
    dh_Pen: NReal;       // [USD]
    Ruido: NReal;     // [m]
    K_m: NReal;       // [pu] factor de memoria
    K_0: NReal;       // [m] termino independiente
    hini: NReal;
    NumDisc: integer;
    hMinimo: NReal;
    hMaximo: NReal;

    constructor Create(capa: integer; nombre: string; nacimiento, muerte: TFecha;
      lpdUnidades, lpd: TFichasLPD; nodo: TNodo;
  fuenteDeRuido: TFuenteAleatoria; nombreBorne: string; factorMem, terIndep,
  h_ini: NReal; ND: integer; hMin, hMax: NReal);

    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 Sim_Inicio; override;
    procedure Sim_Cronica_Inicio; override;
//    procedure SorteosDelPaso(sortear: boolean); override;
    procedure PrepararPaso_ps; override;

    procedure optx_RegistrarVariablesDeEstado(adminEstados: TAdminEstados); 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 optx_nvxs(var ixr, ixd, iauxr, iauxd: integer); override;
    procedure PosicionarseEnEstrellita; override;

    function opt_necesitoIterar(kIteracion: integer; var errRelativo: NReal): boolean;
      override;

    destructor Destroy; override;
    procedure PubliVars; override;

    procedure ResolverEncadenamientos(s: TSimplex); override;

    procedure dump_Variables(var f: TextFile; charIndentacion: char); override;
    function vertimientoPorPoste: boolean; override;

    function tengoEmbalse: boolean; override;

    function centralLagoDescarga: TGeneradorHidraulico; override;

    {$IFDEF ENGANCHE_CE}
    function Ce_Enganche(Vol: Nreal): Nreal;
    procedure AgregarDefinicionesEvaluador(eval: TEvaluadorExpresionesSimples); override;
    {$ENDIF}


    procedure EvolucionarEstado; override;

    function centralesAguasArriba: TListaCentralesAguasArriba; override;

  end;
procedure TRestriccionErogados_cambioFichaPD(Actor: TCosa);

procedure AlInicio;

procedure AlFinal;

implementation


procedure TRestriccionErogados.dump_Variables(var f: TextFile; charIndentacion: char);
begin
  inherited dump_Variables(f, charIndentacion);
  writeln(f, charIndentacion, 'X_h[m]= ', FloatToStrF(X_h, ffFixed, 10, 3));
  writeln(f, charIndentacion, 'dh_pen[m]= ', FloatToStrF(dh_Pen, ffFixed, 10, 6));
  writeln(f);
end;

constructor TRestriccionErogados.Create(capa: integer; nombre: string;
  nacimiento, muerte: TFecha; lpdUnidades, lpd: TFichasLPD; nodo: TNodo;
  fuenteDeRuido: TFuenteAleatoria; nombreBorne: string; factorMem, terIndep,
  h_ini: NReal; ND: integer; hMin,hMax: NReal);
begin
  inherited Create(capa, nombre, nacimiento, muerte, lpdUnidades, nodo,
    0, false, false, false, nil, '');
  Self.lpd := lpd;
  self.fuenteDeRuido  := fuenteDeRuido;
  self.nombreBorne := nombreBorne;
  self.K_m := factorMem;
  self.K_0 := terIndep;
  self.hini := h_ini;
  self.NumDisc := ND;
  Self.hMaximo := hMax;
  self.hMinimo := hMin;

end;

function TRestriccionErogados.Rec: TCosa_RecLnk;
begin
  Result := inherited Rec;
  Result.addCampoDef('lpd', TCosa(lpd));
  Result.addCampoDef_ref('fuenteDeRuido', TCosa(fuenteDeRuido), Self);
  Result.addCampoDef('nombreBorne', nombreBorne);
  Result.addCampoDef('K_0', K_0);
  Result.addCampoDef('K_m', K_m);
  Result.addCampoDef('hini',hini);
  Result.addCampoDef('NumDisc',NumDisc);
  Result.addCampoDef('hMinimo',hMinimo);
  Result.addCampoDef('hMaximo',hMaximo);
end;

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

end;

procedure TRestriccionErogados.AfterRead(f: TArchiTexto);
begin
  inherited AfterRead(f);
  nodo := nil;
  lpd.Propietario := Self;
end;

procedure TRestriccionErogados.PrepararMemoria(Catalogo: TCatalogoReferencias; globs: TGlobs);
begin
  inherited prepararMemoria(Catalogo, globs);
  numeroBorne := fuenteDeRuido.idBorne(nombreBorne);
end;

procedure TRestriccionErogados.RegistrarParametrosDinamicos(
  CatalogoReferencias: TCatalogoReferencias);
begin
  inherited registrarParametrosDinamicos(CatalogoReferencias);
  lpd.expandirFichas(CatalogoReferencias, globs);
  lpd.RegistrarFichasAActualizar(Self, globs.ActualizadorLPD, @pA, nil,
    TRestriccionErogados_cambioFichaPD);
end;

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


class function TRestriccionErogados.DescClase: string;
begin
  Result := rsRestriccionErogados;
end;



function TRestriccionErogados.vertimientoPorPoste: boolean;
begin
  Result := False;
end;


function TRestriccionErogados.tengoEmbalse: boolean;
begin
  Result := False;
end;

function TRestriccionErogados.centralLagoDescarga: TGeneradorHidraulico;
begin
  Result := nil;
end;

procedure TRestriccionErogados.EvolucionarEstado;
begin
  X_h := Xs_h;
end;

function TRestriccionErogados.centralesAguasArriba: TListaCentralesAguasArriba;
begin
  Result := pa.centralesAguasArriba;
end;

{$IFDEF ENGANCHE_CE}
function TRestriccionErogados.Ce_Enganche(Vol: Nreal): Nreal;
var
  dh_RedQE, Qmax, h_actual, salto_actual, ce_MAXTURB0: NReal;
  NMaquinasInstaladas: integer;
begin
  h_actual := pa.hToma;
  NMaquinasInstaladas := paUnidades.nUnidades_Instaladas[0];
  Qmax := pA.Qmax_Turb;

  dh_RedQE := ReduccionDeCotaPorCaudalErogado(NMaquinasInstaladas * Qmax);
  salto_actual := max(0.1, h_actual - CotaDescarga - dh_RedQE);
  ce_MAXTURB0 := salto_actual * ro_g_ren;

  Result := ce_MAXTURB0;
end;

procedure TRestriccionErogados.AgregarDefinicionesEvaluador(
  eval: TEvaluadorExpresionesSimples);
var
  af: TFunc_F_F_OfObject;
begin
  af := TFunc_F_F_OfObject.Create('CE_' + self.nombre, self.Ce_Enganche);
  eval.CatalogoFuncionesDeUsuario.Add(af);
end;

{$ENDIF}


procedure TRestriccionErogados.ResolverEncadenamientos(s: TSimplex);
var
  iCentrales: integer;
  gh: TFichaCentralAguasArriba;
begin
  // Aportes de las centrales aguas arriba
  // sumamos los aportes de las centrales aguas arriba
  // sobre la restricción de vertimiento
  for iCentrales := 0 to pa.centralesAguasArriba.Count - 1 do
  begin
    gh := TFichaCentralAguasArriba(pa.centralesAguasArriba[iCentrales]);

    gh.Central.CargarAportesAFilaCentralAguasAbajo(
      ires, // fila en la que cargar los aportes
      gh.coef, // coeficiente de llegada por el que multiplicar los aportes
      0,   // valor del agua a considerar en la central aguas abajo.
      s);
  end;
end;


procedure TRestriccionErogados.PrepararPaso_ps;
begin
//  QAportePropio := Self.fuenteDeAportes.Bornera[Self.numeroBorne];
//  VAportePropio := QAportePropio*globs.SegundosDelPaso/1.0E6;
//  ce := 0;
  if fuenteDeRuido<>nil then
    Ruido := self.fuenteDeRuido.bornera[ self.numeroBorne ]
  else
    Ruido := 0;

end;

procedure TRestriccionErogados.optx_RegistrarVariablesDeEstado(
  adminEstados: TAdminEstados);
begin
    adminEstados.Registrar_Continua(
    ixr,
    hMinimo,
    hMaximo,
    NumDisc,
    nombre + '_h', // nombre de la variable
    'm' // unidades
    );
end;

procedure TRestriccionErogados.opt_nvers(var ivar, ivae, ires: integer);
begin
  inherited;
  self.ivar := ivar;
  self.ires := ires;

  ivar := ivar + 2; // Una para la cota, otra para la penalización

  ires := ires + 2; // Una restriccion para el balance de cota,
                    // otra para calcular la penalización
end;

procedure TRestriccionErogados.opt_cargue(s: TSimplex);
var
  ires_: integer;
  iposte, Ncentrales, kcentral: integer;
begin
  // Tenemos que aportar a las restricciones de demanda del nodo al que está
  // conectado el generador

  // restricción dinámica de evolución de la cota controlada.
  // K_m es el factor de memoria de un paso al siguiente
  // K_0 termino independiente. QE_i es el erogado de la central i.
  // -Xs_h + K_m*X_h + sum (alfa_i*QE_i) + K_0 + Ruido = 0
  s.pon_e(ires, ivar, -1);
  s.pon_e(ires, s.nc, K_m*X_h);

//  s.acum_e( ires, s.nc, Ruido );
//  s.acum_e( ires, s.nc, K_0 );

  // Penalidad  Pen = ( Xs_h - hMax ) * cvPen * durPaso
  // Lo modelamos como ( -Xs_h + hMax ) * cvPen * durPaso + Pen >= 0
  // con Pen una variable de control en la caja Pen >= 0
//  if globs.EstadoDeLaSala = CES_OPTIMIZANDO then
//  begin
    s.pon_e( ires+1, ivar, -1 );
    s.pon_e( ires+1, s.nc, pa.hPen );
    s.pon_e( ires+1, ivar+1, 1 );


    s.pon_e( s.nf, ivar+1, -pa.cvPen * globs.dt_DelPaso );
//  end;
//  else
//  begin
////    s.pon_e( ires+1, ivar, -pa.cvPen * globs.dt_DelPaso );
//    s.pon_e( ires+1, s.nc, (-X_h + pa.hPen) * pa.cvPen * globs.dt_DelPaso );
//    s.pon_e( ires+1, ivar+1, 1 );
//    s.pon_e( s.nf, ivar+1, -1 );
//  end;

end;

procedure TRestriccionErogados.opt_fijarRestriccionesDeCaja(s: TSimplex);
var
  iposte: integer;
  NPs: integer;
begin
  s.FijarRestriccionIgualdad(ires);

  s.cota_sup_set( ivar, self.hMaximo );
  s.cota_inf_set( ivar, -self.hMaximo);

  s.cota_sup_set( ivar+1,self.hMaximo );
  s.cota_inf_set( ivar+1, 0 );

end;

procedure TRestriccionErogados.Sim_Cronica_Inicio;
begin
  inherited Sim_Cronica_Inicio;
  X_h:= hini;
end;

procedure TRestriccionErogados.opt_leerSolucion(s: TSimplex);
begin
  Xs_h:= s.xval( ivar );
  dh_Pen:= s.xval( ivar+1 );

  Ingreso_PorDisponibilidad_:= 0;
  Ingreso_PorEnergia_:=0;
  EnergiaConsumida:=0;
  EnergiaGenerada:=0;

  costoDirectoDelPaso := dh_Pen * pa.CVpen * globs.dt_DelPaso;

end;


function TRestriccionErogados.opt_necesitoIterar(kIteracion: integer;
  var errRelativo: NReal): boolean;
begin
  result:= false;
  // por ahora, si necesitamos mejorar la linealización va acá.
end;


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

function TRestriccionErogados.getNombreRes(ires: integer; var nombre: string): boolean;
begin
  if (ires = self.ires) then
  begin
    nombre := self.nombre + '_Xs_h';
    Result := True;
  end
  else if (ires = self.ires + 1) then
  begin
    nombre := self.nombre + '_Pen';
    Result := True;
  end
  else
    Result := False;
end;

procedure TRestriccionErogados.PubliVars;
begin
  inherited PubliVars;
  PublicarVariableNR('Ruido', '[m]', 12, 6, Ruido, True);
  PublicarVariableNR('X_h', '[m]', 12, 6, X_h, True);
  PublicarVariableNR('dh_Pen', '[m]', 12, 6, dh_Pen, True);
  PublicarVariableNR('hini', '[m]', 12, 6, hini, True);
end;

procedure TRestriccionErogados.optx_nvxs(var ixr, ixd, iauxr, iauxd: integer);
begin
  Self.ixr := ixr;
  inc( ixr );
end;

procedure TRestriccionErogados.PosicionarseEnEstrellita;
begin

  if globs.EstadoDeLaSala = CES_OPTIMIZANDO then
  begin
    X_h:= globs.CF.xr[ixr];
    Xs_h:= X_h;
  end;
end;


destructor TRestriccionErogados.Destroy;
begin
  if lpd <> nil then
    lpd.Free;
  inherited Destroy;
end;




{*********************************
*Métodos de TFichaRestriccionErogados*
*********************************}

constructor TFichaRestriccionErogados.Create(capa: integer; fecha: TFecha;
  periodicidad: TPeriodicidad; hPen, CVpen: NReal;
  centralesAguasArriba: TListaCentralesAguasArriba);
begin
  inherited Create(capa, fecha, periodicidad);
  self.hPen := hPen;
  self.CVpen := CVpen;
  self.centralesAguasArriba := centralesAguasArriba;
end;

function TFichaRestriccionErogados.Rec: TCosa_RecLnk;
begin
  Result := inherited Rec;
  Result.addCampoDef('hPen', hPen);
  Result.addCampoDef('CVpen', CVpen);
  Result.addCampoDef('centralesAguasArriba', TCosa(centralesAguasArriba));

//  Result.addCampoDef('hmax', hmax, 14, 35);
//  Result.addCampoDef('CVpen', CVpen, 14, 35);
//  Result.addCampoDef('centralesAguasArriba', TCosa(centralesAguasArriba), 14, 35);
//
//  Result.addCampoDef('hmax', hmax, 35);
//  Result.addCampoDef('CVpen', CVpen, 35);
//  Result.addCampoDef('centralesAguasArriba', TCosa(centralesAguasArriba), 35);
end;

procedure TFichaRestriccionErogados.BeforeRead(version, id_hilo: integer);
begin
  inherited BeforeRead(version, id_hilo);
  hPen := 0.0;
  CVpen := 0.0;
end;

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




function TFichaRestriccionErogados.infoAd_: string;
begin
  Result := 'hmax= ' + FloatToStrF(hPen, ffGeneral, 10, 1) +
    ' m, ' + 'CVpen= ' + FloatToStrF(CVpen, ffGeneral, 10, 1) +
    ' USD/(m*dia)';
end;


destructor TFichaRestriccionErogados.Destroy;
begin
  if centralesAguasArriba <> nil then
    centralesAguasArriba.Free;
  inherited Destroy;
end;


procedure TRestriccionErogados_cambioFichaPD(Actor: TCosa);
begin
  (Actor as TRestriccionErogados).cambioFichaPD;
end;


procedure AlInicio;
begin
  registrarClaseDeCosa(TRestriccionErogados.ClassName, TRestriccionErogados);
  registrarClaseDeCosa(TFichaRestriccionErogados.ClassName, TFichaRestriccionErogados);
end;


procedure AlFinal;
begin
end;

end.
