unit uGeneradores;

{xDEFINE DHQE_EXP}
{xDEFINE RESUMEN_POSTIZADO}
interface

uses
  Classes,
  uNodos,
  uglobs,
  SysUtils,
  uActorNodal, usimplex,
  ufichasLPD,
  uFuentesAleatorias,
  Math,
  uConstantesSimSEE,
  xMatDefs, uFechas, uCosaConNombre, uCosa,
  uversiones;

type
  //Clase Madre de todos los generadores.

  { TGenerador }

  TGenerador = class(TActorUniNodal)
  public
    (**************************************************************************)
    (*               A T R I B U T O S   P E R S I S T E N T E S              *)
    (**************************************************************************)
    TonCO2xMWh: NReal;
    LowCostMustRun_: boolean;
    CleanDevelopmentMechanism: boolean;
    flg_CalcularGradienteDeInversion: boolean;

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

    ValorDeEnergiaAlMarginal: NReal;
    GradienteDeInversion: NReal;
    // ( Ingreso_Spot - Ingreso_porEnergia - Ingreso_porDisponibilidad) / Ingreso_porDisponibilidad


    {$IFDEF RESUMEN_POSTIZADO}
    fdebug: textfile;
    {$ENDIF}


    constructor Create(capa: integer; nombre: string; nacimiento, muerte: TFecha;
      lpdUnidades: TFichasLPD; nodo: TNodo; TonCO2xMWh: NReal;
      LowCostMustRun, CleanDevelopmentMechanism, flg_CalcularGradienteDeInversion:
      boolean; xFuenteIdxP: TFuenteAleatoria; xBorneIdxP: string);

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


    {$IFDEF RESUMEN_POSTIZADO}
    procedure Free; override;
    {$ENDIF}

    procedure CalcularGradienteDeInversion; virtual;
    procedure sim_PrintResultados_UDisp(var fsal: textfile); virtual;
    function NGeneradoresElectricosDisponiblesFlucar: integer; virtual; abstract;
    procedure PubliVars; override;
  end;

  { TListGenerador }

  TListGenerador = class(TListaDeCosasConNombre)
  end;


  { TGeneradorPostizador }

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

    flg_RestarParaPostizado: boolean;
    flg_ResumirPromediando: boolean;
    (**************************************************************************)

    PHoraria_PreSorteosPorUnidad: TDAOfNreal;
    PHoraria_PostSorteosTodaLaCentral: TDAOfNreal;
    PPA: TDAOfNReal; // Potencia por Poste Recibida
    EnergiaDisponibleDelPaso: NReal;
    NMaquinasDisponibles: integer;

    constructor Create(capa: integer; nombre: string; nacimiento, muerte: TFecha;
      lpdUnidades: TFichasLPD; nodo: TNodo; TonCO2xMWh: NReal;
      LowCostMustRun, CleanDevelopmentMechanism, xflg_RestarParaPostizado,
      xflg_CalcularGradienteDeInversion: boolean; 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;

    // en este procedimiento llama a globs.restarParaPostizar.
    {$IFDEF CALC_DEMANDA_NETA}
    procedure prepararPaso_as; override;
    procedure SorteosDelPaso(sortear: boolean); override;
    procedure prepararPaso_ps_pre; override;
    procedure prepararPaso_ps; override;
    {$ENDIF}

    destructor Destroy; override;
  end;

  TDAOfGenerador = array of TGenerador;


  TListaCentralesAguasArriba = class;

  //Todos los generadores hidraulicos heredan de esta clase

  { TGeneradorHidraulico }

  TGeneradorHidraulico = class(TGenerador)
  public
    ivert: integer; //Indice de la variable de vertimiento en el simplex
    QErogado_, QTurbinado, QVertido: NReal;// Caudales erogados, turbinados y
    // vertidos en m3/s
    VErogado, VTurbinado, VVertido: NReal;// Volumenes erogados, turbinados y
    // vertidos en hm3

    QTurbinadoPorPoste: TDAOfNReal;
    QVertidoPorPoste: TDAOfNReal;

    VEro_Filt: NReal; //MC y LD

    Dual_Vertimiento: NReal; // Variable Dual de VVertido
    Dual_QErogadoMin: TDAOfNReal; // Duale de las restricciones de erogando mínimo.

    fEscurrimientoToCaudal: NReal; // factor de conversión de escurrimiento a caudal

    ce: NReal; // coeficiente energético de las Turbinas  MWh/m3
    inv_ce_sobre_3600: NReal; // 1/ce/3600 [m3/(s.MW)]
    ce_Bombeo: NReal; // coeficiente energético de la bomba MWh/m3

    QAportePropio: NReal; // Caudal de aportes propios en m3/s
    VAportePropio: NReal; // [hm3] volumen de aporte en el paso de tiempo

    EnergiaGenerada: NReal; // [MWh] Total de energía generada en el paso
    EnergiaConsumida: NReal;
    // [MWh] Total de energía consumida por el bombeo en el paso

    cv_agua_USD_Hm3_Inc: NReal; // valor del agua entrante en USD/Hm3

    DHE_A, DHE_alfa: NReal;
    {$IFNDEF DHQE_EXP}
    DHE_max_Q, DHE_max_dh: NReal;
    {$ENDIF}


    // Retorna la cota del lago para las centrales con embalse y  la cota
    // de toma para las centrales de pasada.
    function CotaAguasArriba: NReal; virtual; abstract;

    // Calcula la cota de la descarga para poder calcular el Salto
    function CotaDescarga: NReal; virtual; abstract;


    // Devuelve la cantidad de variables de Potencia usada por la Central.
    // Si la potencia puede ser diferente en cada poste retorna globs.NPostes
    // Si la potencia se impone igual en todos los postes debe retornar 1.
    function NVariablesPotenica: integer; virtual;

    // es llamada por ResolverEncadenamientos de las centrales aguas abajo
    // para cargar los coeficientes de aportes de la central que reflejan
    // los volúmenes erogados en la fila indicada por la central de aguas abajo
    // como su restricción de balance de volúmenes.
    // Este procedimiento, afecta la fila "iresDestino" del simplex agregando
    // los coeficientes para reflejar los aportes y modifica la última fila del
    // simplex agregando los coeficientes para valorizar el agua entregada si
    // el parámetro cv_agua_entregada > 0.
    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); virtual;


    // Resuelve los encadenamientos.
    procedure ResolverEncadenamientos(s: TSimplex); virtual; abstract;

    procedure recalcular_ValoresAgua; virtual;

    // Caclula la reducción del salto causada por el caudal Erogado
    function ReduccionDeCotaPorCaudalErogado(QErogado: NReal): NReal; virtual;

    // Calcula los parámetros DHE_A y DHE_alfa para que coincidan valor
    // y derivadas primera y sengunda con la parábola ( ca * QE + cb * QE² ) en cero
    procedure recalcular_parametros_dhqe(ca, cb: NReal);


    function centralLagoDescarga: TGeneradorHidraulico; virtual; abstract;

    function centralesAguasArriba: TListaCentralesAguasArriba; virtual; abstract;

    function tengoEmbalse: boolean; virtual; abstract;

    // retorna true si la central tiene vertimiento por poste y false si es porpaso
    function vertimientoPorPoste: boolean; virtual; abstract;

    // FP: calcula el coef energético acumulado hasta la siguiente central con embalse
    // aguas abajo y también devuelve el valor del agua de dicha central.
    procedure ceAcumycvAguahastaCCEmbalse(var ceAcum, cvAgua, coef_acum_centralAA: NReal;
      centralAA: TGeneradorHidraulico);

    procedure PrepararMemoria(Catalogo: TCatalogoReferencias; globs: TGlobs); override;
    destructor Destroy; override;

  end;

  { TFichaCentralAguasArriba }

  TFichaCentralAguasArriba = class(TCosa)
  public
    central: TGeneradorHidraulico;
    coef: NReal;

    constructor Create(capa: integer; central: TGeneradorHidraulico; coef: NReal);

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

  end;

  { TListaCentralesAguasArriba }

  TListaCentralesAguasArriba = class(TListaDeCosas)
  private
    function getFicha(i: Integer): TFichaCentralAguasArriba;
  public
    constructor Create(capa: integer); override;

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

    function add(coeficiente: NReal; central: TGeneradorHidraulico): integer;
      reintroduce;
    procedure replace(coeficienteNuevo: NReal;
      centralNueva, centralVieja: TGeneradorHidraulico);
    function find(clase, nombre: string; var ipos: integer): boolean; overload;
    function coef_of_CentralAguasArriba(centralAA: TGeneradorHidraulico): NReal;
    procedure WriteConsola;

    property items[i: Integer]:TFichaCentralAguasArriba read getFicha; default;

  end;

procedure AlInicio;
procedure AlFinal;

implementation

{ TListaDeGeneradores }

constructor TGenerador.Create(capa: integer; nombre: string;
  nacimiento, muerte: TFecha; lpdUnidades: TFichasLPD; nodo: TNodo;
  TonCO2xMWh: NReal; LowCostMustRun, CleanDevelopmentMechanism,
  flg_CalcularGradienteDeInversion: boolean; xFuenteIdxP: TFuenteAleatoria;
  xBorneIdxP: string);
begin
  inherited Create(capa, nombre, nacimiento, muerte, lpdUnidades, nodo,
    xFuenteIdxP, xBorneIdxP);
  self.TonCO2xMWh := TonCO2xMWh;
  self.LowCostMustRun_ := LowCostMustRun;
  self.CleanDevelopmentMechanism := CleanDevelopmentMechanism;

  Self.flg_CalcularGradienteDeInversion := flg_CalcularGradienteDeInversion;
  ValorDeEnergiaAlMarginal := 0;
  GradienteDeInversion := 0;


  {$IFDEF RESUMEN_POSTIZADO}
  assignfile(fdebug, getDir_Dbg + DirectorySeparator + nombre +
    '_debug_resumen_postizado.xlt');
  rewrite(fdebug);
  Write(fdebug, #9, 'hora', #9, 'Poste');
  Write(fdebug, #9, 'PHoraria_');
  Write(fdebug, #9, 'PHoraria_sort');
  Write(fdebug, #9, 'jRandom');
  Write(fdebug, #9, 'PPA');
  writeln(fdebug);
  {$ENDIF}

end;


{$IFDEF RESUMEN_POSTIZADO}
procedure TGenerador.Free;
begin
  closefile(fdebug);
  inherited Free;
end;

{$ENDIF}



function TGenerador.Rec: TCosa_RecLnk;
begin
  Result := inherited Rec;
  Result.addCampoDef('TonCO2xMWh', TonCO2xMWh, 62);
  Result.addCampoDef('LowCostMustRun', LowCostMustRun_, 62);
  Result.addCampoDef('CleanDevelopmentMechanism', CleanDevelopmentMechanism, 67);
  Result.addCampoDef('flg_CalcularGradienteDeInversion',
    flg_CalcularGradienteDeInversion, 112);
end;

procedure TGenerador.BeforeRead(version, id_hilo: integer);
begin
  inherited BeforeRead(version, id_hilo);
  TonCO2xMWh := 0.0;
  LowCostMustRun_ := True;
  CleanDevelopmentMechanism := False;
  flg_CalcularGradienteDeInversion := False;
end;

procedure TGenerador.AfterRead(f: TArchiTexto);
begin
  inherited AfterRead(f);
  {$IFDEF RESUMEN_POSTIZADO}
  assignfile(fdebug, getDir_Dbg + DirectorySeparator + nombre +
    '_debug_resumen_postizado.xlt');
  rewrite(fdebug);
  Write(fdebug, #9, 'PHoraria_');
  Write(fdebug, #9, 'PHoraria_sort');
  Write(fdebug, #9, 'jRandom');
  Write(fdebug, #9, 'PPA');
  writeln(fdebug);
  {$ENDIF}
end;

procedure TGenerador.CalcularGradienteDeInversion;
var
  k: integer;
begin
  ValorDeEnergiaAlMarginal := 0;

  for k := 0 to high(P) do
    ValorDeEnergiaAlMarginal :=
      ValorDeEnergiaAlMarginal + P[k] * globs.DurPos[k] * nodo.cmarg[k];

  if globs.flg_GradienteDeInversion_en_pu then // gradiente en pu
  begin
    if Abs(Ingreso_PorDisponibilidad_) > 1E-2 then
      GradienteDeInversion :=
        (ValorDeEnergiaAlMarginal - (costoDirectoDelPaso +
        Ingreso_PorDisponibilidad_ + Ingreso_PorEnergia_)) / Ingreso_PorDisponibilidad_
    else
      GradienteDeInversion := 0;
  end
  else
    GradienteDeInversion := ValorDeEnergiaAlMarginal -
      (costoDirectoDelPaso + Ingreso_PorDisponibilidad_ + Ingreso_PorEnergia_);
end;



procedure TGenerador.PubliVars;
begin
  inherited PubliVars;
  if flg_CalcularGradienteDeInversion then
  begin
    PublicarVariableNR('E*cmg', 'USD', 12, 1, ValorDeEnergiaAlMarginal, True);
    PublicarVariableNR('GradInv', '???', 12, 4, GradienteDeInversion, True);
  end;
end;



constructor TFichaCentralAguasArriba.Create(capa: integer;
  central: TGeneradorHidraulico; coef: NReal);
begin
  inherited Create(capa);
  self.central := central;
  self.coef := coef;
end;

function TFichaCentralAguasArriba.Rec: TCosa_RecLnk;
begin
  Result := inherited Rec;
  Result.addCampoDef_ref('central', TCosa(central), self);
  Result.addCampoDef('coef', coef);
end;

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

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


function TListaCentralesAguasArriba.getFicha(i: Integer
  ): TFichaCentralAguasArriba;
begin
  Result := TFichaCentralAguasArriba(inherited items[i]);
end;

constructor TListaCentralesAguasArriba.Create(capa: integer);
begin
  inherited Create(capa, 'CentralesAguasArriba');
end;

function TListaCentralesAguasArriba.Rec: TCosa_RecLnk;
begin
  Result := inherited Rec;
end;

procedure TListaCentralesAguasArriba.BeforeRead(version, id_hilo: integer);
begin
  inherited BeforeRead(version, id_hilo);
  if version < 3 then
    raise Exception.Create('Imposible leer el archivo, la versión es muy antigua.');
end;

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



procedure TGenerador.sim_PrintResultados_UDisp(var fsal: textfile);
begin
  Write(fsal, #9, paUnidades.nUnidades_Operativas[0]);
end;




constructor TGeneradorPostizador.Create(capa: integer; nombre: string;
  nacimiento, muerte: TFecha; lpdUnidades: TFichasLPD; nodo: TNodo;
  TonCO2xMWh: NReal; LowCostMustRun, CleanDevelopmentMechanism,
  xflg_RestarParaPostizado, xflg_CalcularGradienteDeInversion: boolean;
  xFuenteIdxP: TFuenteAleatoria; xBorneIdxP: string);
begin
  inherited Create(capa, nombre, nacimiento, muerte, lpdUnidades, nodo,
    TonCO2xMWh, LowCostMustRun, CleanDevelopmentMechanism,
    xflg_CalcularGradienteDeInversion, xFuenteIdxP, xBorneIdxP);

  flg_RestarParaPostizado := xflg_RestarParaPostizado;
  flg_ResumirPromediando := True;
end;

function TGeneradorPostizador.Rec: TCosa_RecLnk;
begin
  Result := inherited Rec;
  Result.addCampoDef('flg_RestarParaPostizado', flg_RestarParaPostizado, 110);
end;

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

procedure TGeneradorPostizador.AfterRead(f: TArchiTexto);
begin
  inherited AfterRead(f);
  flg_ResumirPromediando := True;
end;


procedure TGeneradorPostizador.PrepararMemoria(Catalogo: TCatalogoReferencias;
  globs: TGlobs);
begin
  inherited PrepararMemoria(Catalogo, globs);
  setlength(PHoraria_PreSorteosPorUnidad, ceil(globs.HorasDelPaso));
  setlength(PHoraria_PostSorteosTodaLaCentral, ceil(globs.HorasDelPaso));
  setlength(PPA, globs.NPostes);
end;

{$IFDEF CALC_DEMANDA_NETA}
procedure TGeneradorPostizador.prepararPaso_as;
begin
  vclear(PHoraria_PreSorteosPorUnidad);
end;

procedure TGeneradorPostizador.SorteosDelPaso(sortear: boolean);
begin
end;


procedure TGeneradorPostizador.prepararPaso_ps_pre;
var
  k: integer;
begin
  for k := 0 to high(PHoraria_PreSorteosPorUnidad) do
    PHoraria_PostSorteosTodaLaCentral[k] :=
      PHoraria_PreSorteosPorUnidad[k] * NMaquinasDisponibles;

  if self.flg_RestarParaPostizado then
    globs.restarPHoraria(PHoraria_PostSorteosTodaLaCentral);
end;


procedure TGeneradorPostizador.prepararPaso_ps;
var
  iPoste, jHora: integer;
  P: NReal;
begin
  if globs.SalaMinutal then
  begin
    PPA[0] := PHoraria_PostSorteosTodaLaCentral[0];
    EnergiaDisponibleDelPaso := PPA[0] * globs.HorasDelPaso;
  end
  else
  begin
    EnergiaDisponibleDelpaso := 0;
    vclear(PPA);
    if flg_resumirPromediando then
    begin
      for jHora := 0 to high(globs.idxHorasPostizadas) do
      begin
        iposte := globs.kPosteHorasDelPaso[jHora];
        PPA[iposte] := PPA[iposte] + PHoraria_PostSorteosTodaLaCentral[jHora];
      end;
      for iposte := 0 to globs.NPostes - 1 do
      begin
        EnergiaDisponibleDelPaso := EnergiaDisponibleDelPaso + PPA[iposte];
        PPA[iposte] := PPA[iposte] / globs.Durpos[iposte];
      end;
    end
    else
    begin
      for iposte := 0 to globs.NPostes - 1 do
      begin
        jHora := globs.idxHorasPostizadas[globs.jRnd_Poste_globs_[iposte]];
        P := PHoraria_PostSorteosTodaLaCentral[jHora];
        EnergiaDisponibleDelPaso := EnergiaDisponibleDelpaso + P * globs.DurPos[iposte];
        PPA[Iposte] := P;
      end;
    end;
  end;

  {$IFDEF RESUMEN_POSTIZADO}
  for jHora := 0 to high(PHoraria_PostSorteosTodaLaCentral) do
  begin
    Write(fdebug, #9, jHora, #9, globs.kPosteHorasDelPaso[jHora]);
    Write(fdebug, #9, PHoraria_PostSorteosTodaLaCentral[jHora]);
    Write(fdebug, #9, PHoraria_PostSorteosTodaLaCentral[
      globs.idxHorasPostizadas[jHora]]);
    if jHOra < globs.NPOstes then
    begin
      Write(fdebug, #9, globs.jRnd_Poste_globs[jHora]);
      Write(fdebug, #9, PPA[jHora]);
    end;
    writeln(fdebug);
  end;
 {$ENDIF}

end;

{$ENDIF}


destructor TGeneradorPostizador.Destroy;
begin
  setlength(PHoraria_PreSorteosPorUnidad, 0);
  setlength(PHoraria_PostSorteosTodaLaCentral, 0);
  inherited Destroy;
end;


function TListaCentralesAguasArriba.find(clase, nombre: string;
  var ipos: integer): boolean;
var
  k: integer;
  buscando: boolean;
  ficha: TFichaCentralAguasArriba;
  cosa: TCosaConNombre;
begin
  buscando := True;
  for k := 0 to lst.Count - 1 do
  begin
    ficha := lst.items[k];
    cosa := ficha.central;
    if (cosa.ClassName = clase) and (cosa.nombre = nombre) then
    begin
      buscando := False;
      ipos := k;
      break;
    end;
  end;
  Result := not buscando;
end;

function TListaCentralesAguasArriba.coef_of_CentralAguasArriba(
  centralAA: TGeneradorHidraulico): NReal;
var
  kcentral: integer;
  pf: TFichaCentralAguasArriba;
begin
  self.find(centralAA.ClassName, centralAA.nombre, kcentral);
  pf := TFichaCentralAguasArriba(lst[kcentral]);
  Result := pf.coef;
end;

procedure TListaCentralesAguasArriba.WriteConsola;
var
  pf: TFichaCentralAguasArriba;
  central_i: String;
  i: Integer;
begin
  Writeln('Centrales Aguas Arriba: ', IntToStr(lst.Count));
  for i := 0 to lst.Count - 1 do
  begin
    pf := TFichaCentralAguasArriba(lst[i]);
    central_i := pf.central.nombre;
    WriteLn(central_i);
  end;
end;

function TListaCentralesAguasArriba.add(coeficiente: NReal;
  central: TGeneradorHidraulico): integer;
var
  ficha: TFichaCentralAguasArriba;
begin
  ficha := TFichaCentralAguasArriba.Create(central.capa, central, coeficiente);
  Result := lst.Add(ficha);
end;

procedure TListaCentralesAguasArriba.replace(coeficienteNuevo: NReal;
  centralNueva, centralVieja: TGeneradorHidraulico);
var
  i: integer;
  pf: TFichaCentralAguasArriba;
begin
  for i := 0 to lst.Count - 1 do
  begin
    pf := TFichaCentralAguasArriba(lst[i]);
    if pf.central = centralVieja then
    begin
      pf.central := centralNueva;
      pf.coef := coeficienteNuevo;
      break;
    end;
  end;
end;



function TGeneradorHidraulico.NVariablesPotenica: integer;
begin
  Result := globs.NPostes;
end;

procedure TGeneradorHidraulico.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;
  icof: integer;
  iposte: integer;
  valor: NReal;
  flgValorizarEntrega: boolean;
  NPs: integer;

begin
  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.



  NPs := NVariablesPotenica;

  // consideramos los volúmenes turbinados de la central sacándolos
  // de la  restricción adicional de balance de volúmenes que será la de Vk+1 >=0 en el caso de
  // tratarse de una hidro con embalse o la de -Turbinado -Vertimiento + Aportes = 0
  // para el caso en que se trate de una hidro de pasada
  for iposte := 0 to NPs - 1 do
  begin
    icof := ivar + 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;

  if vertimientoPorPoste then
  begin
    // consideramos el vertimiento
    icof := ivar + NPs;
    for iposte := 0 to globs.NPostes - 1 do
    begin
      valor := s.e(ires, icof + iposte) * menosCofAporte;
      s.acum_e(iresDestino, icof + iposte, valor); // Vk+1 >= 0
      if flgValorizarEntrega then
        s.acum_e(s.nf, icof + iposte, valor * cv_agua_entregada);
    end;
  end
  else
  begin
    // consideramos el vertimiento
    icof := ivar + NPs;
    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 TGeneradorHidraulico.recalcular_ValoresAgua;
begin
  Exit;
end;

function TGeneradorHidraulico.ReduccionDeCotaPorCaudalErogado(QErogado: NReal): NReal;
begin
    {$IFDEF DHQE_EXP}
  Result := DHE_A * (1 - exp(-DHE_alfa * QErogado));
    {$ELSE}
  if QErogado > DHE_max_Q then
    Result := DHE_max_dh
  else
    Result := (DHE_alfa * QErogado + DHE_A) * QErogado;
    {$ENDIF}
end;

procedure TGeneradorHidraulico.recalcular_parametros_dhqe(ca, cb: NReal);
begin
  {$IFDEF DHQE_EXP}
  if ca > AsumaCero then
  begin
    DHE_alfa := -2 * cb / ca;
    DHE_A := ca / DHE_alfa;
  end
  else
  begin
    DHE_alfa := 0;
    DHE_A := 0;
  end;
  {$ELSE}
  DHE_A := ca;
  DHE_alfa := cb;
  if cb < -AsumaCero then
  begin
    DHE_max_Q := -0.5 * ca / cb;
    if DHE_max_Q > 0 then
      DHE_max_dh := (ca + cb * DHE_max_Q) * DHE_max_Q
    else
      DHE_max_dh := 0; // Los parámetros son un bolazo.
  end
  else
  begin // caso degenerado o es lineal cb = 0 o cb > 0 que implica que no tiene máx para Q > 0
    DHE_max_Q := 1e30;
    DHE_max_dh := 1e30; // infinito
  end;
  {$ENDIF}
end;


procedure TGeneradorHidraulico.ceAcumycvAguahastaCCEmbalse(
  var ceAcum, cvAgua, coef_acum_centralAA: NReal; centralAA: TGeneradorHidraulico);
var
  coef_CentralAA: NReal;
  iCentrales: integer;
begin
  if self = centralAA then
  begin
    coef_acum_centralAA := 1;
    ceAcum := ce * 1e6;
    cvAgua := 0;
    if centralLagoDescarga <> nil then
      centralLagoDescarga.ceAcumycvAguahastaCCEmbalse
      (ceAcum, cvAgua, coef_acum_centralAA, self);
  end
  else
  begin
    coef_CentralAA := centralesAguasArriba.coef_of_CentralAguasArriba(centralAA);
    coef_acum_centralAA := coef_acum_centralAA * coef_CentralAA;
    if not tengoEmbalse then
    begin
      ceAcum := ceAcum + ce * 1e6 * coef_acum_centralAA;
      if centralLagoDescarga <> nil then
        centralLagoDescarga.ceAcumycvAguahastaCCEmbalse
        (ceAcum, cvAgua, coef_acum_centralAA, self);
    end
    else
      cvAgua := cv_agua_USD_Hm3_Inc * coef_acum_centralAA;
  end;
end;


procedure TGeneradorHidraulico.PrepararMemoria(Catalogo: TCatalogoReferencias;
  globs: TGlobs);
begin
  inherited PrepararMemoria(Catalogo, globs);
  setlength(Dual_QErogadoMin, globs.NPostes);
  setlength(QTurbinadoPorPoste, globs.NPostes);
  setlength(QVertidoPorPoste, globs.NPostes);
end;

destructor TGeneradorHidraulico.Destroy;
begin
  setlength(Dual_QErogadoMin, 0);
  setlength(QTurbinadoPorPoste, 0);
  setlength(QVertidoPorPoste, 0);
  inherited Destroy;
end;



procedure AlInicio;
begin
  ucosa.registrarClaseDeCosa(TListaCentralesAguasArriba.ClassName,
    TListaCentralesAguasArriba);

  ucosa.registrarClaseDeCosa(TFichaCentralAguasArriba.ClassName,
    TFichaCentralAguasArriba);

  ucosa.registrarClaseDeCosa(TListGenerador.ClassName, TListGenerador);
end;

procedure AlFinal;
begin
end;

end.
