unit uHidroConBombeo;

{$IFNDEF FORZAR_NO_RDIN}
  {$DEFINE RDIN} // OJO, este define tiene que cambiarse aquí y en la Central de Bombeo
{$ENDIF}
interface

uses
  Classes,
  Math, xMatDefs, MatReal, uSimplex, ucosa, uGlobs, uFechas, uNodos,
  SysUtils,
  uFichasLPD,
  uGeneradores,
  uconstantesSimSEE,
  uFuentesAleatorias, uCosaConNombre, uevapUruguay,
  uHidroConEmbalse;

resourcestring
  rsHidroelectricaBombeo = 'Hidroeléctrica con bombeo';

type
  THidroConBombeo = class; // forward declaration

  { TFichaHidroConBombeo }

  TFichaHidroConBombeo = class(TFichaHidroConEmbalse)
  public
    PMaxBombeo: NReal;
    QMaxBombeo: NReal;
    renBombeo: NReal;

    constructor Create(capa: integer; fecha: TFecha; periodicidad: TPeriodicidad;
      hmin_, hmax_: NReal; PuntosCotaVolumen_h,
  PuntosCotaVolumen_V: TDAOfNReal; AreaCuenca_ha: NReal; hDescarga: NReal;
  central_lagoDescarga: TGeneradorHidraulico;
  centralesAguasArriba: TListaCentralesAguasArriba; caQE, cbQE, ren_,
  Pmax_Gen_, Qmax_Turb_: NReal; fDispo_: NReal; tRepHoras: NReal;
  filtracion_Ca, filtracion_Cb, QaMuySeco: NReal; cotaMV0, cotaMV1, QMV1,
  QE_ControlDeCrecidaInicio, QE_ControlDeCrecidaMedio,
  QE_ControlDeCrecidaAPleno: NReal; HayRestriccionEmaxPasoDeTiempo: boolean;
  EmaxPasoDeTiempo: NReal; HayRestriccionQTmin: boolean; QTmin: NReal;
  flg_Aplica_cv_Spot_Acordado: boolean; cv_Spot_acordado: NReal;
  PuntosControlCrecidaPorCotaYAportes_h: TDAOfNReal;
  PuntosControlCrecidaPorCotaYAportes_QA: TDAOfNReal;
  flg_ControlCrecidaPOrCotaYAportes, flg_ControlCrecidaPorCota: boolean;
  ImponerQminPorPoste: boolean; ControlarCotaPorDebajoDeObjetivo_sim,
  ControlarCotaPorEnciaDeObjetivo_sim: boolean;
  ControlarCotaPorDebajoDeObjetivo_opt,
  ControlarCotaPorEnciaDeObjetivo_opt: boolean;
  flg_ValorAguaExacto_hObjetivo: boolean; flg_ControlCondicional: boolean;
  DAofhObjetivo: TDAofNReal; DAofDelta_cvaUSD_Hm3ParaControlDeCota: TDAofNReal;
  IndiceAjusteControlDeCota: TFuenteAleatoria;
  borneAjusteControlDeCota: string; cv_USD_Hm3_ValorizadoManual: NReal;
  tomarCotaDeLaFuente: boolean; fuenteCota: TFuenteAleatoria;
  borneCota: string; saltoMinimoOperativo: NReal;
  cotaControlCrecida_Inicio: NReal; cotaControlCrecida_Medio: NReal;
  cotaControlCrecida_Pleno: NReal; PMaxBombeo: NReal; QMaxBombeo: NReal;
  renBombeo: NReal; flg_CalcularEvaporacionDelLago: boolean;
  flg_CalcularFiltracionDelLago: boolean; PorcentajeFiltracion: NReal;
  PagoPorDisponibilidad_USD_uh: NReal; PagoPorEnergia: NReal;
  QErogadoMin_Falla, cv_USD_hm3_falla_ErogadoMin: Nreal; flg_ErogadoMin_Falla,
  flg_cva_ParaControlDeCota_USD_Hm3: boolean);

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

  end;

  { THidroConBombeo }

  THidroConBombeo = class(THidroConEmbalse)
  public
    jPBombeado: integer; // coluna del simplex donde comienzan las PBombeado

    PBombeado: TDAOfNReal; // potencias de bombeo por poste
    ce_Bombeo: NReal; // coeficiente energético del bombeo
    QBombeado: NReal; // m3/s
    VBombeado: NReal; // hm3

    PMaxParaQMaxBombeo: NReal;
    // potencia correspondienta al QMaxBombeo con el salto dado
    PMaxBombeoDelPaso: NReal;
    // el mínimo entre la potencia de la bomba y el valor anterior
    cv_USD_MWh_Inc: NReal;
    ro_g_inv_renBombeo: NReal;

    constructor Create(capa: integer; nombre: string; nacimiento, muerte: TFecha;
      lpdUnidades, lpd: TFichasLPD; nodo: TNodo; hIni: NReal;
      NDisc_: integer; fuenteDeAportes: TFuenteAleatoria; nombreBorne: string;
      flg_FuenteDeEscurrimientos: boolean; flg_ValorizadoManual: boolean;
      flg_CalcularGradienteDeInversion: boolean; TonCO2xMWh: NReal;
      LowCostMustRun, CleanDevelopmentMechanism: boolean; xFuenteIdxP: TFuenteAleatoria;
      xBorneIdxP: string);

    procedure PrepararMemoria(Catalogo: TCatalogoReferencias; globs: TGlobs); override;
    class function DescClase: string; override;
    procedure Sim_Cronica_Inicio; override;
    procedure PrepararPaso_ps; override;
    procedure opt_nvers(var ivar, ivae, ires: integer); override;
    procedure opt_cargue(s: TSimplex); override;
    procedure CargarAportesAFilaCentralAguasAbajo(iresDestino: integer;
    // fila en la que cargar los aportes
      coefLlegada: NReal; // coeficiente de llegada por el que multiplicar los aportes
      cv_agua_entregada: NReal;
    // valor del agua a considerar en la central aguas abajo.
      s: TSimplex); override;
    procedure opt_fijarRestriccionesDeCaja(s: TSimplex); override;
    procedure opt_leerSolucion(s: TSimplex); override;

    function getNombreVar(ivar: integer; var nombre: string): boolean; override;
    destructor destroy; override;
    procedure CambioFichaPD; override;
    procedure PubliVars; override;


  end;

procedure THidroConBombeo_cambioFichaPD(Actor: TCosa);
procedure AlInicio;
procedure AlFinal;


var
  cnt_dbg: integer;


implementation

uses uActores;

constructor THidroConBombeo.Create(capa: integer; nombre: string;
  nacimiento, muerte: TFecha; lpdUnidades, lpd: TFichasLPD; nodo: TNodo;
  hIni: NReal; NDisc_: integer; fuenteDeAportes: TFuenteAleatoria;
  nombreBorne: string; flg_FuenteDeEscurrimientos: boolean;
  flg_ValorizadoManual: boolean; flg_CalcularGradienteDeInversion: boolean;
  TonCO2xMWh: NReal; LowCostMustRun, CleanDevelopmentMechanism: boolean;
  xFuenteIdxP: TFuenteAleatoria; xBorneIdxP: string);
begin
  inherited Create(capa, nombre, lpdUnidades, lpd, nodo,
    hIni, 0.0, False, False, NDisc_, fuenteDeAportes, nombreBorne,
    flg_FuenteDeEscurrimientos, flg_ValorizadoManual,
    flg_CalcularGradienteDeInversion, TonCO2xMWh, LowCostMustRun,
    CleanDevelopmentMechanism, xFuenteIdxP, xBorneIdxP);
end;

procedure THidroConBombeo.PrepararMemoria(Catalogo: TCatalogoReferencias;
  globs: TGlobs);
begin
  inherited prepararMemoria(Catalogo, globs);
  setlength(PBombeado, globs.NPostes);
end;


class function THidroConBombeo.DescClase: string;
begin
  Result := rsHidroelectricaBombeo;
end;


procedure THidroConBombeo.PrepararPaso_ps;
var
  Q_maxBombeo: double;
  cv_aux: double;
begin
  inherited prepararPaso_ps;

  // calculamos el bombeo máximo para que no desborde.

  {$IFDEF CONCONTROLCRECIDA}
  Q_maxBombeo := max(0, 0.99 * (min(V_ControlCrecida_Inicio, self.Vmax) -
    Vs_SinErogado - VAportePropio) / (globs.SegundosDelPaso / 1.0E6));
  {$ELSE}
  Q_maxBombeo := max(0, 0.99 * (V_hmax - Vs_SinErogado - VAportePropio) /
    (globs.SegundosDelPaso / 1.0E6));
  {$ENDIF}
  Q_maxBombeo := min(Q_maxBombeo, TFichaHidroConBombeo(pa).QMaxBombeo);
  ce_Bombeo := salto_actual * ro_g_inv_renBombeo;
  PmaxParaQMaxBombeo := Q_maxBombeo * ce_Bombeo * 3600;
  PMaxBombeoDelPaso := min(TFichaHidroConBombeo(pa).PMaxBombeo, PMaxParaQMaxBombeo);


  // NO puede pasar que estemos dispuesto a pagar más por el agua
  // bombeada que lo que estemos dispuesto a cobrar por el agua turbinada
  // esto tiene que ver con la concavidad de la función de costo futuro.
  if (cv_agua_USD_Hm3_Inc > cv_agua_USD_Hm3_Dec) then
  begin
    cv_aux := (cv_agua_USD_Hm3_Dec + cv_agua_USD_Hm3_Inc) / 2.0;
    cv_agua_USD_Hm3_Dec := cv_aux;
    cv_agua_USD_Hm3_Inc := cv_aux;

    cv_USD_MWh_Inc := cv_aux / (1.0E6 * ce_Bombeo);
    cv_USD_MWh := cv_aux * auxParaCalculos_cv;
  end
  else
    cv_USD_MWh_Inc := cv_agua_USD_Hm3_Inc / (1.0E6 * ce_Bombeo);

end;

procedure THidroConBombeo.opt_nvers(var ivar, ivae, ires: integer);
begin
  inherited opt_nvers(ivar, ivae, ires);
  jPBombeado := ivar;
  ivar := ivar + globs.NPostes; // las potencias de bombeo en cada poste
end;


procedure THidroConBombeo.opt_cargue(s: TSimplex);
var
  iposte: integer;
  jres: integer;
  invCEPorUnoESeis: NReal;

begin
  inherited opt_cargue(s);

  invCEPorUnoESeis := 1 / (ce_Bombeo * 1.0E6);

  // demanda del bombeo sobre la restricción de nodo
  // los coeficientes son 1 pues las potencias son negativas.
  for iposte := 0 to globs.NPostes - 1 do
    s.pon_e(nodo.ires + iposte, jPBombeado + iposte, 1);


  {$IFNDEF RDIN}
  // valorización del agua bombeada
  for iposte := 0 to globs.NPostes - 1 do
    s.pon_e(s.nf, jPBombeado + iposte, -cv_USD_MWh_Inc * globs.durpos[iposte]);
  {$ENDIF}

  jres := ires;
  // restriccion de volumen >= 0
  for iposte := 0 to globs.NPostes - 1 do
    s.pon_e(jres, jPBombeado + iposte, -globs.durpos[iposte] * invCEPorUnoESeis);


  // si la central cargó restricción de energía máxima en el paso la salteo
  if (PuedoGenerar) and pA.HayRestriccionEmaxPasoDeTiempo then
    Inc(jres);

  // si es necesario agrego el bombeo a las restricciones de erogado mínimo.
  // no parece lógico que si hay erogado mínimo se admita bombear, pero lo escribo
  // igual
  if AplicarRestriccionDeErogadoMinimo then
  begin
    Inc(jres);
    if pa.ImponerQminPorPoste then
    begin
      for iposte := 0 to globs.NPostes - 1 do
        s.pon_e(jres + iposte, jPBombeado + iposte, globs.durpos[iposte] *
          invCEPorUnoESeis); // bombeo
    end
    else
      for iposte := 0 to globs.NPostes - 1 do
        s.pon_e(jres, jPBombeado + iposte, globs.durpos[iposte] * invCEPorUnoESeis);
    // bombeo
  end;
end;


procedure THidroConBombeo.CargarAportesAFilaCentralAguasAbajo(iresDestino: integer;
  // fila en la que cargar los aportes
  coefLlegada: NReal; // coeficiente de llegada por el que multiplicar los aportes
  cv_agua_entregada: NReal; // valor del agua a considerar en la central aguas abajo.
  s: TSimplex);

var
  menosCofAporte: NReal;
  flgValorizarEntrega: boolean;
  iposte: integer;
  icof: integer;
  valor: NReal;
begin
  inherited CargarAportesAFilaCentralAguasAbajo(
    iresDestino, coefLLegada, cv_agua_entregada, s);

  flgValorizarEntrega := cv_agua_entregada > 0;
  menosCofAporte := -coefLlegada; // esto es para tener en cuenta que en la
  // restricción de donde sacamos los volumens los mismos son extracciones y
  // en la que los sumamos son aportes.

  for iposte := 0 to globs.NPostes - 1 do
  begin
    icof := jPBombeado + iposte;
    valor := s.e(ires, icof) * menosCofAporte;
    s.acum_e(iresDestino, icof, valor); // Vk+1 >= 0
    if flgValorizarEntrega then
      s.acum_e(s.nf, icof, valor * cv_agua_entregada);
  end;
end;


procedure THidroConBombeo.opt_fijarRestriccionesDeCaja(s: TSimplex);
var
  iposte: integer;
begin
  inherited opt_FijarRestriccionesDeCaja(s);
  for iposte := 0 to globs.NPostes - 1 do
  begin
    s.cota_inf_set(jPBombeado + iposte, -self.PMaxBombeoDelPaso);
    s.cota_sup_set(jPBombeado + iposte, 0);
  end;
end;

procedure THidroConBombeo.Sim_Cronica_Inicio;
begin
  inherited Sim_Cronica_Inicio;
  QBombeado := 0;
  VBombeado := 0;
end;

procedure THidroConBombeo.opt_leerSolucion(s: TSimplex);
var
  iposte: integer;
begin
  Inc(cnt_dbg);
  inherited opt_leerSolucion(s);

  EnergiaConsumida := 0;
  for iposte := 0 to globs.NPostes - 1 do
  begin
    PBombeado[iposte] := s.xval(jPBombeado + iposte);
    EnergiaConsumida := EnergiaConsumida - PBombeado[iposte] * globs.durpos[iposte];

    if (PBombeado[iposte] < -0.1) and (P[iposte] > 0.1) then
      raise Exception.Create(
        'THidroConBombeo erogando y bombeando a la vez!!!, cnt_dbg: ' +
        IntToStr(cnt_dbg));
  end;

  VBombeado := EnergiaConsumida / (ce_Bombeo * 1.0E6);
  QBombeado := VBombeado * (1.0E6 * globs.invSegundosDelPaso);
end;



function THidroConBombeo.getNombreVar(ivar: integer; var nombre: string): boolean;
begin
  if ivar < jPBombeado then
    Result := inherited getNombreVar(ivar, nombre)
  else
  begin
    nombre := self.Nombre + '_PBombeado[MW]' + IntToStr(ivar - self.ivar + 1);
    Result := True;
  end;
end;

procedure THidroConBombeo.PubliVars;
begin
  inherited PubliVars;
  PublicarVariableVR('PBombeado', '[MW]', 6, 1, PBombeado, True, True);
  PublicarVariableNR('QBombeado', '[m3/s]', 8, 1, QBombeado, True);
  PublicarVariableNR('cva_dec', '[USD/MWh]', 8, 1, self.cv_USD_MWh, True);
  PublicarVariableNR('cva_inc', '[USD/MWh]', 8, 1, self.cv_USD_MWh_inc, True);
end;



destructor THidroConBombeo.destroy;
begin
  setlength(PBombeado, 0);
  inherited destroy;
end;


procedure THidroConBombeo.CambioFichaPD;
begin
  inherited cambioFichaPD;
  ro_g_inv_renBombeo := ro_Agua * g_gravitacion / TFichaHidroConBombeo(pA).renBombeo /
    (1.0E6 * 3600.0);
end;

{*********************************
*Métodos de TFichaHidroConBombeo*
*********************************}

constructor TFichaHidroConBombeo.Create(capa: integer; fecha: TFecha;
  periodicidad: TPeriodicidad; hmin_, hmax_: NReal;
  PuntosCotaVolumen_h, PuntosCotaVolumen_V: TDAOfNReal; AreaCuenca_ha: NReal;
  hDescarga: NReal; central_lagoDescarga: TGeneradorHidraulico;
  centralesAguasArriba: TListaCentralesAguasArriba;
  caQE, cbQE, ren_, Pmax_Gen_, Qmax_Turb_: NReal; fDispo_: NReal;
  tRepHoras: NReal; filtracion_Ca, filtracion_Cb, QaMuySeco: NReal;
  cotaMV0, cotaMV1, QMV1, QE_ControlDeCrecidaInicio, QE_ControlDeCrecidaMedio,
  QE_ControlDeCrecidaAPleno: NReal; HayRestriccionEmaxPasoDeTiempo: boolean;
  EmaxPasoDeTiempo: NReal; HayRestriccionQTmin: boolean; QTmin: NReal;
  flg_Aplica_cv_Spot_Acordado: boolean; cv_Spot_acordado: NReal;
  PuntosControlCrecidaPorCotaYAportes_h: TDAOfNReal;
  PuntosControlCrecidaPorCotaYAportes_QA: TDAOfNReal;
  flg_ControlCrecidaPOrCotaYAportes, flg_ControlCrecidaPorCota: boolean;
  ImponerQminPorPoste: boolean;
  ControlarCotaPorDebajoDeObjetivo_sim, ControlarCotaPorEnciaDeObjetivo_sim: boolean;
  ControlarCotaPorDebajoDeObjetivo_opt, ControlarCotaPorEnciaDeObjetivo_opt: boolean;
  flg_ValorAguaExacto_hObjetivo: boolean; flg_ControlCondicional: boolean;
  DAofhObjetivo: TDAofNReal; DAofDelta_cvaUSD_Hm3ParaControlDeCota: TDAofNReal;
  IndiceAjusteControlDeCota: TFuenteAleatoria; borneAjusteControlDeCota: string;
  cv_USD_Hm3_ValorizadoManual: NReal; tomarCotaDeLaFuente: boolean;
  fuenteCota: TFuenteAleatoria; borneCota: string; saltoMinimoOperativo: NReal;
  cotaControlCrecida_Inicio: NReal; cotaControlCrecida_Medio: NReal;
  cotaControlCrecida_Pleno: NReal; PMaxBombeo: NReal; QMaxBombeo: NReal;
  renBombeo: NReal; flg_CalcularEvaporacionDelLago: boolean;
  flg_CalcularFiltracionDelLago: boolean; PorcentajeFiltracion: NReal;
  PagoPorDisponibilidad_USD_uh: NReal; PagoPorEnergia: NReal;
  QErogadoMin_Falla, cv_USD_hm3_falla_ErogadoMin: Nreal; flg_ErogadoMin_Falla,
  flg_cva_ParaControlDeCota_USD_Hm3: boolean);
begin
  inherited Create(
    capa,
    fecha, periodicidad,
    hmin_, hmax_,
    PuntosCotaVolumen_h, PuntosCotaVolumen_V,
    AreaCuenca_ha,
    hDescarga,
    central_lagoDescarga,
    centralesAguasArriba,
    caQE, cbQE, ren_, Pmax_Gen_, Qmax_Turb_,
    fDispo_, tRepHoras,
    filtracion_Ca, filtracion_Cb, QaMuySeco,
    cotaMV0, cotaMV1, QMV1, QE_ControlDeCrecidaInicio, QE_ControlDeCrecidaMedio,
    QE_ControlDeCrecidaAPleno,
    HayRestriccionEmaxPasoDeTiempo,
    EmaxPasoDeTiempo,
    HayRestriccionQTmin,
    QTmin,
    flg_Aplica_cv_Spot_Acordado,
    cv_Spot_acordado, 0,

    PuntosControlCrecidaPorCotaYAportes_h,
    PuntosControlCrecidaPorCotaYAportes_QA,
    flg_ControlCrecidaPOrCotaYAportes,
    flg_ControlCrecidaPorCota,

    ImponerQminPorPoste,
    ControlarCotaPorDebajoDeObjetivo_sim,
    ControlarCotaPorEnciaDeObjetivo_sim,
    ControlarCotaPorDebajoDeObjetivo_opt,
    ControlarCotaPorEnciaDeObjetivo_opt,
    flg_ValorAguaExacto_hObjetivo,
    flg_ControlCondicional,
    DAOfhObjetivo,
    DAOfDelta_cvaUSD_Hm3ParaControlDeCota,

    IndiceAjusteControlDeCota, borneAjusteControlDeCota,

    cv_USD_Hm3_ValorizadoManual,
    tomarCotaDeLaFuente,
    fuenteCota,
    borneCota,
    saltoMinimoOperativo,
    0.0, // por ahora pongo Factor de Reserva en CERO.
    cotaControlCrecida_Inicio,
    cotaControlCrecida_Medio,
    cotaControlCrecida_Pleno,
    flg_CalcularEvaporacionDelLago,
    flg_CalcularFiltracionDelLago,
    PorcentajeFiltracion,
    PagoPorDisponibilidad_USD_uh, PagoPorEnergia,
    QErogadoMin_Falla,
    cv_USD_hm3_falla_ErogadoMin, flg_ErogadoMin_Falla, flg_cva_ParaControlDeCota_USD_Hm3,
    indiceAjusteErogadoFalla,borneAjusteErogadoFalla);

  self.PMaxBombeo := PMaxBombeo;
  self.QMaxBombeo := QMaxBombeo;
  self.renBombeo := renBombeo;
end;

function TFichaHidroConBombeo.Rec: TCosa_RecLnk;
begin
  Result := inherited Rec;
  Result.addCampoDef('PMaxBombeo', PMaxBombeo);
  Result.addCampoDef('QMaxBombeo', QMaxBombeo);
  Result.addCampoDef('renBombeo', renBombeo);
end;

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

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







procedure THidroConBombeo_cambioFichaPD(Actor: TCosa);
begin
  (Actor as THidroConBombeo).cambioFichaPD;
end;

procedure AlInicio;
begin
  registrarClaseDeCosa(THidroConBombeo.ClassName, THidroConBombeo);
  registrarClaseDeCosa(TFichaHidroConBombeo.ClassName, TFichaHidroConBombeo);
end;

procedure AlFinal;
begin
end;

begin
  cnt_dbg := 0;
end.
