unit uHidroDePasada;

{$DEFINE ENGANCHE_CE}
interface

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

resourcestring
  rsGHidraulicoPasada = 'Generador hidráulico de pasada';

const
  ro_Agua = 1000; // kg/m3 densidad del agua
  g_Gravitacion = 9.8; // N/kg constante gravitatoria

type
  THidroDePasada = class; // forward declaration

{Las cotas están medidas desde algún nivel (por ejemplo el del mar).
Para poder calcular el salto se suministra también la cota de aguas abajo
o de descarga de la turbina referida al mismo nivel. (hDescarga [m])
El nivel de descarga puede ser especificado dando su valor con la variable
hDescarga o mediante la referencia a otra central en cuyo caso se considera
que el nivel de descarga es el del lago de la otra central más el valor
hDescarga. En este caso la variable hDescarga se utiliza para tener en
cuenta cambios en la referencia de niven entre una central y otra.
Si todas las cotas están referidas al nivel del mar no hay cambios de
referencias y hDescarga se pondrá en cero.

                                    uparseadorsupersimple,ufuncionesbasicas, uFuncionesReales,
Pérdida de Salto por Caudal Erogado.
====================================
El caudal erogado QErogado [m3/s] es calculado al final de cada paso
de simulación como la suma del caudal vertido más el caudal turbinado.
Dicho caudal puede afectar el nivel de aguas abajo reduciendo el salto
efectivo en la central. Para tener en cuenta este efecto se usan los
parámetros: caQE y cbQE calculados para modelar la pérdida de salto
de acuerdo a la siguiente ecuación:

dh(QE) = caQE* QE + cbQE* QE^2
*)}

  { TFichaHidroDePasada }

  TFichaHidroDePasada = class(TFichaLPD)
  public

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

    // Si la fuente en lugar de caudal en m3/s es de escurrimiento
    // en mm/mes hay que especificar el área de la cuenca a considerar
    AreaCuenca_ha: NReal;
    cv_agua_USD_Hm3: NReal; // valor del agua en USD/Hm3
    hDescarga: NReal; //[m] cota de la descarga para cálculo del salto
    hToma: NReal; //[m] cota de la toma, la altura efectiva sera hToma - hDescarga
    // Coeficientes de afectación del salto por caudal erogado
    // dh(QE) = caQE* QE + cbQE* QE^2
    caQE: NReal;
    cbQE: NReal;
    ren: NReal; //[pu]= 0.95; // rendimiento complexivo de turbina y generador
    Pmax_Gen: NReal; //[MW]= 240; // Potencia maxima hidraulica
    Qmax_Turb: NReal; //[m3/s]
    SaltoMinimoOperativo: NReal; // salto mínimo para funcionamiento de las turbinas
    fDispo: NReal; //[pu]  factor de disponibilidad fortuito
    tRepHoras: NReal;
    Qmin_Vert: NReal;
    // [m3/s] Si es posible, obliga este erogado POR EL VERTEDERO aunque pueda turbinar.
    cvFallaQmin_Vert: NReal; // USD/Hm3
    central_lagoDescarga: TGeneradorHidraulico;

         (** DATOS CENTRALES URUGUAY
         BONETE= Dr. Gabriel Terra
         =========================
         hmax= 86.5
         hmin= 70
         Vmax= 1.667E+16
         Vhmed= 8.335E+15
         hDescarga= 54.5394
         caQE= 4.689E-04
         cbQE= 5.077E-07

         ren= 0.85 ??? ojo ver mejor este número
         Pmax_Gen= 38.8
         Qmax_Turb= 170.0
         fDispo= 0.995

         *)
    centralesAguasArriba: TListaCentralesAguasArriba{of TGeneradorHidraulico};

    HayRestriccionEmaxPasoDeTiempo: boolean; // indica si se aplica la restricción
    EmaxPasoDeTiempo: NReal; // Energía maxima generable en un paso de tiempo
    // Pagos a los generadores
    PagoPorDisponibilidad_USD_MWh: NReal; // [USD/MWh] Pago por Potencia
    PagoPorEnergia_USD_MWh: NReal; // [USD/MWh] Pago por Potencia

    (**************************************************************************)
    fReserva: NReal;
    flg_IgualPotenciaEnTodosLosPostes: boolean;

    constructor Create(capa: integer; fecha: TFecha; periodicidad: TPeriodicidad;
      AreaCuenca_ha, hDescarga, hToma: NReal; cv_agua_USD_Hm3: NReal;
      central_lagoDescarga: TGeneradorHidraulico;
      centralesAguasArriba: TListaCentralesAguasArriba;
      caQE, cbQE, ren_, Pmax_Gen_, Qmax_Turb_: NReal; SaltoMinimoOperativo: NReal;
      fReserva: NReal; fDispo_: NReal; tRepHoras: NReal;
      HayRestriccionEmaxPasoDeTiempo: boolean; EmaxPasoDeTiempo: NReal;
      PagoPorDisponibilidad_USD_MWh: NReal; PagoPorEnergia_USD_MWh: NReal;
      Qmin_Vert, cvFallaQmin_Vert: NReal;
      flg_IgualPotenciaEnTodosLosPostes: boolean);

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

    function infoAd_: string; override;
    destructor Destroy; override;

  private
    px_cv_agua_USD_MWh: NReal;

  end;


  { THidroDePasada }

  THidroDePasada = class(TGeneradorHidraulico)
  public

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

    fuenteDeAportes: TFuenteAleatoria;
    nombreBorne: string;
    // Si es TRUE, la fuente suministra el escurrimiento en mm/mes
    // y para calcular el caudal hay que multiplicar por el área
    // y dividir por los segundos del mes.  (mm/mes) /1000 * Area[km2]*1000*1000 /(730*3600)
    // si es false, la fuente
    // suministra el caudal medio en m3/s.
    flg_FuenteDeEscurrimientos: boolean;
    flg_IgualPotenciaEnTodosLosPostes_BORRADO_: boolean;

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

    pa: TFichaHidroDePasada;

    numeroBorne: integer;

    //----------- variables auxiliares
    ro_g_ren: NReal; // ro_Agua * g_gravitacion * ren /(1E6*3600)
    VTmin: NReal; // volumen turbinado mínimo en el paso para cumplir con QTmin

    // variables calculadas por paso de tiempo durante la simulación
    h_actual: NReal;
    Salto: NReal; // salto actual efectivo

    QErogado_IterAnterior: NReal;
    // Usamos esta variable para almacenar el QErogado en la
    // iteración anterior. Esto se hace en leerResultadosDelPaso
    // antes de cargar el nuevo valor de QErogado.


    PmaxParaQmax: NReal; // Pontecia máxima por máquina considerando Qmax y la cota
    NMaquinasDisponibles: integer;
    PDisponible_Central: NReal; // Disponibilidad MW para remuneración.
    Pmax_Central: NReal; // [MW] Potencia Máxima disponible en la central
    Pmax_Gen: NReal; // [MW] Potencia máxima generable por turbina-generador
    Qmax: NReal; // [m3/s] Caudal máximo turbinable por turbina-generador
    PuedoGenerar: boolean;
    // indica si la Central está en condiciones de ofrecer potencia o no
    cv_USD_MWh: NReal; // valor del Agua expresado en USD/MWh
    cv_agua_USD_Hm3_MLRB: NReal;
    // multiplicador de Lagrange de la restricción de balance.
    dh_RedQE: NReal; // [m] pérdida de salto por caulda erogado.

    Vmin_Vert: NReal; // Volumen [hm3] que se pretende imponer como vertido (si es posible)


    constructor Create(capa: integer; nombre: string; nacimiento, muerte: TFecha;
      lpdUnidades, lpd: TFichasLPD; nodo: TNodo;
      flg_CalcularGradienteDeInversion: boolean; fuenteDeAportes: TFuenteAleatoria;
      nombreBorne: string; flg_FuenteDeEscurrimientos: boolean;
      TonCO2xMWh: NReal; LowCostMustRun, CleanDevelopmentMechanism: 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;
    procedure RegistrarParametrosDinamicos(CatalogoReferencias: TCatalogoReferencias);
      override;

    function PotenciaFirme: NReal; 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 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;
    procedure AcumAux1(peso: NReal); override;
    procedure SetAux1; override;

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

    destructor Destroy; override;
    function get_pa_FD(kTipoUnidad: integer): NReal; override;
    function get_pa_TMR(kTipoUnidad: integer): NReal; override;

    procedure CambioFichaPD; override;

    procedure PubliVars; override;

    function CotaAguasArriba: NReal; override;
    // Calcula la cota de la descarga para poder calcular el Salto
    function CotaDescarga: NReal; override;
    // Caclula la reducción del salto causada por el caudal Erogado
    function ReduccionDeCotaPorCaudalErogado(QErogado: NReal): NReal; override;
    function centralLagoDescarga: TGeneradorHidraulico; override;

    procedure ResolverEncadenamientos(s: TSimplex); override;

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

    function tengoEmbalse: boolean; override;

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

    function centralesAguasArriba: TListaCentralesAguasArriba; override;

  end;

procedure THidroDePasada_cambioFichaPD(Actor: TCosa);

procedure AlInicio;
procedure AlFinal;

implementation


procedure THidroDePasada.dump_Variables(var f: TextFile; charIndentacion: char);
begin
  inherited dump_Variables(f, charIndentacion);
  writeln(f, charIndentacion, 'h_actual[m]= ', FloatToStrF(h_actual, ffFixed, 10, 3));
  writeln(f, charIndentacion, 'salto_actual[m]= ', FloatToStrF(salto, ffFixed, 10, 3));

  writeln(f, charIndentacion, 'QAportePropio[m^3/s]= ',
    FloatToStrF(QAportePropio, ffFixed, 10, 3));
  writeln(f, charIndentacion, 'VAportePropio[hm^3]= ',
    FloatToStrF(VAportePropio, ffFixed, 10, 3));

  writeln(f, charIndentacion, 'cv_USD_MWh[USD/MWh]= ',
    FloatToStrF(cv_USD_MWh, ffFixed, 10, 3));
  writeln(f);
end;

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

begin
  inherited Create(capa, nombre, nacimiento, muerte, lpdUnidades, nodo,
    TonCO2xMWh, LowCostMustRun, CleanDevelopmentMechanism,
    flg_CalcularGradienteDeInversion, xFuenteIdxP, xBorneIdxP);
  Self.lpd := lpd;
  self.fuenteDeAportes := fuenteDeAportes;
  self.nombreBorne := nombreBorne;
  self.flg_FuenteDeEscurrimientos := flg_FuenteDeEscurrimientos;
end;

function THidroDePasada.Rec: TCosa_RecLnk;
begin
  Result := inherited Rec;
  Result.addCampoDef('lpd', TCosa(lpd));
  Result.addCampoDef_ref('fuenteDeAportes', TCosa(fuenteDeAportes), Self);
  Result.addCampoDef('nombreBorne', nombreBorne);
  Result.addCampoDef('flg_FuenteDeEscurrimientos', flg_FuenteDeEscurrimientos, 104);
  Result.addCampoDef('flg_IgualPotenciaEnTodosLosPostes',
    flg_IgualPotenciaEnTodosLosPostes_BORRADO_, 105, 199);
end;

procedure THidroDePasada.BeforeRead(version, id_hilo: integer);
begin
  inherited BeforeRead(version, id_hilo);
  flg_FuenteDeEscurrimientos := False;
end;

procedure THidroDePasada.AfterRead(f: TArchiTexto);
var
  af: TFichaHidroDePasada;
  k: integer;

begin
  inherited AfterRead(f);
  pa := nil;
  nodo := nil;
  lpd.Propietario := self;
  if f.Version < 199 then
  begin
    for k := 0 to lpd.Count - 1 do
    begin
      af := lpd[k] as TFichaHidroDePasada;
      af.flg_IgualPotenciaEnTodosLosPostes := flg_IgualPotenciaEnTodosLosPostes_BORRADO_;
    end;
  end;
end;


procedure THidroDePasada.PrepararMemoria(Catalogo: TCatalogoReferencias; globs: TGlobs);
begin
  inherited prepararMemoria(Catalogo, globs);
  numeroBorne := fuenteDeAportes.idBorne(nombreBorne);
  cv_agua_USD_Hm3_Inc := 0;
  dh_RedQE := 0;
end;

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

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

function THidroDePasada.PotenciaFirme: NReal;
begin
  Result := (paUnidades.nUnidades_Operativas[0]) * pa.Pmax_Gen * pa.ren;
end;

class function THidroDePasada.DescClase: string;
begin
  Result := rsGHidraulicoPasada;
end;

procedure THidroDePasada.SorteosDelPaso(sortear: boolean);
begin
  if hayForzamientos or globs.ObligarDisponibilidad_1_ then
  begin
    NMaquinasDisponibles := paUnidades.nUnidades_Operativas[0];
    Pmax_Gen := pA.Pmax_gen;
    Qmax := pA.Qmax_Turb;
  end
  else if (sortear) then
  begin
    NMaquinasDisponibles := Sorteos_RepRotUnidades;
    Pmax_Gen := pA.Pmax_gen;
    Qmax := pA.Qmax_Turb;
  end
  else
  begin
    NMaquinasDisponibles := paUnidades.nUnidades_Operativas[0];
    Pmax_Gen := pA.Pmax_gen * pA.fdispo;
    Qmax := pA.Qmax_Turb * pA.fdispo;
  end;
  PDisponible_Central := NMaquinasDisponibles * Pmax_Gen;
end;

function THidroDePasada.ReduccionDeCotaPorCaudalErogado(QErogado: NReal): NReal;
begin
  //Si no tengo centrales aguas arriba se que lo que voy a erogar son todos mis
  //aportes
  if self.pa.centralesAguasArriba.Count = 0 then
    dh_RedQE := inherited ReduccionDeCotaPorCaudalErogado(QAportePropio)
  else
    dh_RedQE := inherited ReduccionDeCotaPorCaudalErogado(QErogado);
  Result := dh_RedQE;
end;

function THidroDePasada.centralLagoDescarga: TGeneradorHidraulico;
begin
  Result := pa.central_lagoDescarga;
end;

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

function THidroDePasada.NVariablesPotenica: integer;
begin
  if pa.flg_IgualPotenciaEnTodosLosPostes then
    Result := 1
  else
    Result := globs.NPostes;
end;

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

{$IFDEF ENGANCHE_CE}
function THidroDePasada.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 THidroDePasada.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;

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

{$ENDIF}


procedure THidroDePasada.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
      pa.cv_agua_USD_Hm3,   // valor del agua a considerar en la central aguas abajo.
      s);
  end;
end;


function THidroDePasada.CotaAguasArriba: NReal;
begin
  Result := pa.hToma;
end;

function THidroDePasada.CotaDescarga: NReal;
begin
  if pa.central_lagoDescarga = nil then
    Result := pa.hDescarga
  else
    Result := pa.central_LagoDescarga.cotaAguasArriba;
end;

procedure THidroDePasada.PrepararPaso_ps;
begin
  h_actual := pa.hToma;
  QAportePropio := Self.fuenteDeAportes.Bornera[Self.numeroBorne];

  if flg_FuenteDeEscurrimientos then
    QAportePropio := QAportePropio * fEscurrimientoToCaudal;


(*
    Assert(QAportePropio >= 0, 'El generador hidráulico de pasada ' + nombre +
    ' tiene caudal de aportes propios negativo. Fuente: ' + fuenteDeAportes.nombre
    + ', Borne: ' + nombreBorne);
*)
  if QAportePropio < 0 then
    QAportePropio := 0;



  VAportePropio := QAportePropio * globs.SegundosDelPaso / 1.0E6;
  Salto := max(0.1, h_actual - CotaDescarga - ReduccionDeCotaPorCaudalErogado(
    QErogado_));
  ce := Salto * ro_g_ren;
  inv_ce_sobre_3600 := 1 / ce / 3600;

  cv_USD_MWh := pa.cv_agua_USD_Hm3 / (1.0E6 * ce) * globs.fActPaso;
  PmaxParaQmax := Qmax * ce * 3600;
  Pmax_Central := NMaquinasDisponibles * min(Pmax_Gen, PmaxParaQmax);
  PuedoGenerar := (NMaquinasDisponibles > 0) and (Salto >= pa.SaltoMinimoOperativo);
end;

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

  if not pa.flg_IgualPotenciaEnTodosLosPostes then
    ivar := ivar + globs.NPostes + 1//Potencias por poste y Vertimiento
  else
    ivar := ivar + 2; // Una Potenica y el Vertimiento.

  Inc(ires);// -Turbinado -Vertimiento + Aportes = 0
  // ponemos una restricción que equivale a decir
  // que todos los Aportes son Erogados, ya sea como
  // Turbinado o Vertimiento

  if pA.HayRestriccionEmaxPasoDeTiempo then
    Inc(ires);

  if pa.Qmin_Vert > 0.0001 then
    Inc(ires);
end;

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

  if not pa.flg_IgualPotenciaEnTodosLosPostes then
  begin
    for iposte := 0 to globs.NPostes - 1 do
      s.pon_e(ibaseres + iposte, ivar + iposte, 1);
    ivert := ivar + globs.NPostes;
    //Restricción -Turbinado -Vertimiento + Aportes = 0
    for iposte := 0 to globs.NPostes - 1 do
      s.pon_e(ires, ivar + iposte, -globs.durpos[iposte] / (ce * 1.0E6));

    // 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_USD_MWh * globs.durpos[iposte]);
  end
  else
  begin
    for iposte := 0 to globs.NPostes - 1 do
      s.pon_e(ibaseres + iposte, ivar, 1);
    ivert := ivar + 1;
    //Restricción -Turbinado -Vertimiento + Aportes = 0
    s.pon_e(ires, ivar, -globs.HorasDelPaso / (ce * 1.0E6));

    // Ahora agregamos a la función de UTILIDAD (-costo)
    s.pon_e(s.nf, ivar, -cv_USD_MWh * globs.HorasDelPaso);
  end;

  s.pon_e(ires, ivert, -1); // -Vertimiento
  s.pon_e(ires, s.nc, VAportePropio);
  s.FijarRestriccionIgualdad(ires);
  s.pon_e(s.nf, ivert, -pa.cv_agua_USD_Hm3);

  ires_ := ires;
  //Restriccion a la energía máxima generable
  if pA.HayRestriccionEmaxPasoDeTiempo then
  begin
    Inc(ires_);
    if not pa.flg_IgualPotenciaEnTodosLosPostes then
    begin
      for iposte := 0 to globs.NPostes - 1 do
        s.pon_e(ires_, ivar + iposte, -globs.durpos[iposte]);
    end
    else
      s.pon_e(ires_, ivar, -globs.HorasDelPaso);
    s.pon_e(ires_, s.nc, pA.EmaxPasoDeTiempo);
  end;

  if Vmin_Vert > 0.0001 then
  begin
    Inc(ires_);
    // QVert - Qmin_Vert + Holgura_Qmin_Vert > 0
    s.pon_e(ires_, iVert, 1);
    s.pon_e(ires_, s.nc, -Vmin_Vert);
    s.pon_e(ires_, iVert + 1, 1);
    s.pon_e(s.nf, iVert + 1, -pa.cvFallaQmin_Vert * 1E6);
  end;

end;

procedure THidroDePasada.opt_fijarRestriccionesDeCaja(s: TSimplex);
var
  iposte: integer;
  NPs: integer;
begin

  if not pa.flg_IgualPotenciaEnTodosLosPostes then
    NPs := globs.NPostes
  else
    NPs := 1;

  // Le fijamos como cota máxima  de la potencia en cada poste al mínimo
  // entre la PMax del generador y lo que puede generar con el caudal máximo
  // para el ce
  if PuedoGenerar then
  begin
    if (not hayForzamientos) then
    begin
      for iposte := 0 to NPs - 1 do
        s.cota_sup_set(ivar + iposte, PMax_Central);
    end
    else
    begin
      {
      for iposte := 0 to NPs - 1 do
        s.FijarVariable(ivar + iposte, paForzamiento.P[0]);

       }
      for iposte := 0 to globs.NPostes - 1 do
      begin
        if paForzamiento.flg_fijarP then
          s.FijarVariable(ivar + iposte, paForzamiento.P[0])
        else if paForzamiento.activar_Pmax then
          s.cota_sup_set(ivar + iposte, paForzamiento.P_max[0])
        else if paForzamiento.activar_Pmin then
          s.cota_inf_set(ivar + iposte, paForzamiento.P_max[0]);
      end;
    end;
  end
  else
  begin
    for iposte := 0 to NPs - 1 do
      s.FijarVariable(ivar + iposte, 0);
  end;

  if Vmin_Vert > 0.0001 then
  begin
    s.cota_inf_set(ivert, Vmin_Vert);
    s.cota_sup_set(iVert + 1, Vmin_Vert + 0.1);
  end;
  s.cota_sup_set(ivert, 9000000); //Qmax * globs.SegundosDelPaso / 1E2);

end;

procedure THidroDePasada.Sim_Cronica_Inicio;
begin
  inherited Sim_Cronica_Inicio;
  QErogado_ := 0;

  QErogado_IterAnterior := 0; // para que de igual al correrlo dos veces seguidas

  VVertido := 0;
  VTurbinado := 0;
  VErogado := 0;
  EnergiaGenerada := 0;
end;

procedure THidroDePasada.opt_leerSolucion(s: TSimplex);
var
  iposte: integer;
begin

  if PuedoGenerar then
  begin
    // recuperamos los valores de Potencia despachada
    EnergiaGenerada := 0;

    if not pa.flg_IgualPotenciaEnTodosLosPostes then
    begin
      for iposte := 0 to globs.NPostes - 1 do
      begin
        P[iposte] := s.xval(ivar + iposte);
        QTurbinadoPorPoste[iPoste] := P[iposte] * inv_ce_sobre_3600;
        Lambda_P[iPoste] := s.xmult(ivar + iposte) / globs.durpos[iposte];
        cv_Spot[iPoste] := Nodo.cmarg[iposte] - Lambda_P[iPoste];
        EnergiaGenerada := EnergiaGenerada + P[iposte] * globs.durpos[iposte];
      end;
    end
    else
    begin
      P[0] := s.xval(ivar);
      QTurbinadoPorPoste[0] := P[0] * inv_ce_sobre_3600;
      EnergiaGenerada := P[0] * globs.HorasDelPaso;
      Lambda_P[0] := s.xmult(ivar) / globs.HorasDelPaso;
      for iposte := 1 to globs.NPostes - 1 do
      begin
        P[iposte] := P[0];
        QTurbinadoPorPoste[iPoste] := QTurbinadoPorPoste[0];
        Lambda_P[iPoste] := Lambda_P[0];
        cv_Spot[iPoste] := Nodo.cmarg[iposte] - Lambda_P[iPoste];
      end;
    end;
  end
  else
  begin
    vclear(QTurbinadoPorPoste);
    for iposte := 0 to globs.NPostes - 1 do
    begin
      P[iposte] := 0;
      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;
    EnergiaGenerada := 0;
  end;

  Ingreso_PorDisponibilidad_ :=
    PDisponible_Central * pa.PagoPorDisponibilidad_USD_MWh * globs.HorasDelPaso;
  Ingreso_PorEnergia_ := EnergiaGenerada * pa.PagoPorEnergia_USD_MWh;
  costoDirectoDelPaso := 0;

  cv_agua_USD_Hm3_MLRB := -s.ymult(ires);

  VVertido := s.xval(ivert);
  Dual_Vertimiento := -s.xmult(ivert);

  VTurbinado := EnergiaGenerada / (ce * 1.0E6);
  VErogado := VTurbinado + VVertido;

  QVertido := VVertido * (1.0E6 * globs.invSegundosDelPaso);
  QTurbinado := VTurbinado * (1.0E6 * globs.invSegundosDelPaso);
  QErogado_ := VErogado * (1.0E6 * globs.invSegundosDelPaso);
end;


function THidroDePasada.opt_necesitoIterar(kIteracion: integer;
  var errRelativo: NReal): boolean;
var
  qm: NReal;
  qz: NReal;
  alfa: NReal;
begin
  qm := max(QErogado_, QErogado_IterAnterior);
  if qm > 1 then
    errRelativo := abs(QErogado_ - QErogado_IterAnterior) / qm
  else
    errRelativo := 0;

  {$IFDEF NECES_ITER_VERBOSO}
  if ((errRelativo > 0.1) and (globs.kPaso_Opt = 13) and
    (globs.CF.ordinalEstrellaActual = 102)) then
  begin
    Writeln('Iteramos porque ' + nombre + ' tiene Qerogado = ' +
      FloatToStr(QErogado_) + ' y Querogado Anterior =  ' +
      FloatToStr(QErogado_IterAnterior));

    Writeln('HIDRO - kPaso: ' + IntToStr(globs.kPaso_Opt) + ' kEstrella: ' +
      IntToStr(globs.CF.ordinalEstrellaActual) + ' kIter: ' +
      IntToStr(globs.cntIteracionesDelPaso));

  end;
  {$ENDIF}




  alfa := 0.7;
  QErogado_ := alfa * QErogado_IterAnterior + (1 - alfa) * QErogado_;

  qz := QErogado_;
  QErogado_IterAnterior := qz;

  Result := errRelativo > 0.1;

end;



function THidroDePasada.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 if (ivar = ivert) then
  begin
    nombre := self.Nombre + '_Vertimiento[Hm3]';
    Result := True;
  end
  else
    Result := False;
end;

function THidroDePasada.getNombreRes(ires: integer; var nombre: string): boolean;
begin
  if (ires = self.ires) then
  begin
    nombre := self.nombre + '_-VErogado+VAportes=0';
    Result := True;
  end
  else if pa.HayRestriccionEmaxPasoDeTiempo and (ires = self.ires + 1) then
  begin
    nombre := self.nombre + '_res-EMax';
    Result := True;
  end
  else
    Result := False;
end;

procedure THidroDePasada.PubliVars;
begin
  inherited PubliVars;
  PublicarVariableNR('QAportesP', '[m3/s]', 8, 1, QAportePropio, True);
  PublicarVariableNR('QTurbinado', '[m3/s]', 8, 1, QTurbinado, True);
  PublicarVariableNR('QVertido', '[m3/s]', 8, 1, QVertido, True);
  PublicarVariableNR('Salto', '[m]', 6, 1, Salto, True);
  PublicarVariableNR('ce', '[MWh/m3]', 12, 6, ce, True);
  PublicarVariableNR('dh_RedQE', '[m]', 6, 2, dh_RedQE, True);
  PublicarVariableNR('cv_USD_MWh', '[USD/MWh]', 6, 1, cv_USD_MWh, True);
  PublicarVariableNR('cv_agua_MLRB', '[USD/Hm3]', 6, 1, cv_agua_USD_Hm3_MLRB, True);
  PublicarVariableNR('Cota', '[m]', 6, 1, h_actual, False);
  PublicarVariableNR('VVertido', '[Hm3]', 8, 1, VVertido, False);
  PublicarVariableNR('Dual_Vertimiento', '[USD/Hm3]', 8, 1, Dual_Vertimiento, True);
  PublicarVariableNR('Pmax_Central', '[MW]', 8, 1, Pmax_Central, True);

  PublicarVariableNR('VTurbinado', '[Hm3]', 8, 1, VTurbinado, False);
  PublicarVariableNI('NMaqsDisponibles', '-', NMaquinasDisponibles, False);
  PublicarVariableVR('QTrubinadoPorPoste', '[m3/s]', 6, 2, QTurbinadoPorPoste,
    True, True);

end;

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

procedure THidroDePasada.PosicionarseEnEstrellita;
begin
  if globs.EstadoDeLaSala = CES_OPTIMIZANDO then
  begin
    Self.QErogado_ := globs.Auxs_r0[iauxr][globs.CF.ordinalEstrellaActual];
    //Para que tenga el mismo estado de iteraciones al posicionarse en la estrella y
    //de igual en la optimización monohilo y multihilo
    Self.QErogado_IterAnterior := QErogado_;
  end;
end;

procedure THidroDePasada.AcumAux1(peso: NReal);
begin
  globs.Auxs_r1[iauxr][globs.CF.ordinalEstrellaActual] :=
    globs.Auxs_r1[iauxr][globs.CF.ordinalEstrellaActual] + Self.QErogado_ * peso;
end;

procedure THidroDePasada.SetAux1;
begin
  globs.Auxs_r1[iauxr][globs.CF.ordinalEstrellaActual] := Self.QErogado_;
end;

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


function THidroDePasada.get_pa_FD(kTipoUnidad: integer): NReal;
begin
  Result := pa.fDispo;
end;

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


procedure THidroDePasada.CambioFichaPD;
begin
  ActualizarProbabilidadesReparacionYRotura_(pa.fDispo, pa.tRepHoras);
  ro_g_ren := pA.ren * ro_Agua * g_gravitacion / (1.0E6 * 3600.0);
  if flg_FuenteDeEscurrimientos then
    fEscurrimientoToCaudal := 1.0 / 1000 * pa.AreaCuenca_ha * 100 * 100 / (730 * 3600);
  recalcular_parametros_dhqe(pa.caQE, pa.cbQE);
  Vmin_Vert := pa.Qmin_Vert * globs.SegundosDelPaso / 1.0E6;
end;

{*********************************
*Métodos de TFichaHidroDePasada*
*********************************}

constructor TFichaHidroDePasada.Create(capa: integer; fecha: TFecha;
  periodicidad: TPeriodicidad; AreaCuenca_ha, hDescarga, hToma: NReal;
  cv_agua_USD_Hm3: NReal; central_lagoDescarga: TGeneradorHidraulico;
  centralesAguasArriba: TListaCentralesAguasArriba;
  caQE, cbQE, ren_, Pmax_Gen_, Qmax_Turb_: NReal; SaltoMinimoOperativo: NReal;
  fReserva: NReal; fDispo_: NReal; tRepHoras: NReal;
  HayRestriccionEmaxPasoDeTiempo: boolean; EmaxPasoDeTiempo: NReal;
  PagoPorDisponibilidad_USD_MWh: NReal; PagoPorEnergia_USD_MWh: NReal;
  Qmin_Vert, cvFallaQmin_Vert: NReal; flg_IgualPotenciaEnTodosLosPostes: boolean);
begin
  inherited Create(capa, fecha, periodicidad);
  self.AreaCuenca_ha := AreaCuenca_ha;
  self.hDescarga := hDescarga;
  self.hToma := hToma;
  self.cv_agua_USD_Hm3 := cv_agua_USD_Hm3;
  self.central_lagoDescarga := central_lagoDescarga;
  self.caQE := caQE;
  self.cbQE := cbQE;
  self.ren := ren_;
  self.Pmax_Gen := Pmax_Gen_;
  self.Qmax_Turb := Qmax_Turb_;
  self.SaltoMinimoOperativo := SaltoMinimoOperativo;
  self.fReserva := fReserva;
  self.fDispo := fDispo_;
  self.tRepHoras := tRepHoras;
  self.centralesAguasArriba := centralesAguasArriba;
  self.HayRestriccionEmaxPasoDeTiempo := HayRestriccionEmaxPasoDeTiempo;
  self.SaltoMinimoOperativo := saltoMinimoOperativo;
  self.EmaxPasoDeTiempo := EmaxPasoDeTiempo;
  self.PagoPorDisponibilidad_USD_MWh := PagoPorDisponibilidad_USD_MWh;
  self.PagoPorEnergia_USD_MWh := PagoPorEnergia_USD_MWh;
  self.Qmin_Vert := Qmin_Vert;
  self.cvFallaQmin_Vert := cvFallaQmin_Vert;
  self.flg_IgualPotenciaEnTodosLosPostes := flg_IgualPotenciaEnTodosLosPostes;
end;

function TFichaHidroDePasada.Rec: TCosa_RecLnk;
begin
  Result := inherited Rec;
  Result.addCampoDef('hDescarga', hDescarga, 0, 14);
  Result.addCampoDef('cv_agua_USD_MWh', px_cv_agua_USD_MWh, 0, 14);
  Result.addCampoDef('hToma', hToma, 0, 14);
  Result.addCampoDef('caQE', caQE, 0, 14);
  Result.addCampoDef('cbQE', cbQE, 0, 14);
  Result.addCampoDef('ren', ren, 0, 14);
  Result.addCampoDef('Pmax_Gen', Pmax_Gen, 0, 14);
  Result.addCampoDef('Qmax_Turb', Qmax_Turb, 0, 14);
  Result.addCampoDef('fDispo', fDispo, 0, 14);
  Result.addCampoDef_ref('central_lagoDescarga', TCosa(central_lagoDescarga),
    Self, 0, 14);
  Result.addCampoDef('centralesAguasArriba', TCosa(centralesAguasArriba), 0, 14);
  Result.addCampoDef('HayRestriccionEmaxPasoDeTiempo',
    HayRestriccionEmaxPasoDeTiempo, 0, 14);
  Result.addCampoDef('EmaxPasoDeTiempo', EmaxPasoDeTiempo, 0, 14);
  Result.addCampoDef('hDescarga', hDescarga, 14, 35);
  Result.addCampoDef('cv_agua_USD_MWh', px_cv_agua_USD_MWh, 14, 35);
  Result.addCampoDef('hToma', hToma, 14, 35);
  Result.addCampoDef('caQE', caQE, 14, 35);
  Result.addCampoDef('cbQE', cbQE, 14, 35);
  Result.addCampoDef('ren', ren, 14, 35);
  Result.addCampoDef('Pmax_Gen', Pmax_Gen, 14, 35);
  Result.addCampoDef('Qmax_Turb', Qmax_Turb, 14, 35);
  Result.addCampoDef('SaltoMinimoOperativo', saltoMinimoOperativo, 30, 35, '0.1');
  Result.addCampoDef('fDispo', fDispo, 14, 35);
  Result.addCampoDef('tRepHoras', tRepHoras, 14, 35);
  Result.addCampoDef_ref('central_lagoDescarga', TCosa(central_lagoDescarga),
    Self, 14, 35);
  Result.addCampoDef('centralesAguasArriba', TCosa(centralesAguasArriba), 14, 35);
  Result.addCampoDef('HayRestriccionEmaxPasoDeTiempo',
    HayRestriccionEmaxPasoDeTiempo, 14, 35);
  Result.addCampoDef('EmaxPasoDeTiempo', EmaxPasoDeTiempo, 14, 35);
  Result.addCampoDef('AreaCuenca_ha', AreaCuenca_ha, 104);
  Result.addCampoDef('hDescarga', hDescarga, 35);
  Result.addCampoDef('cv_agua_USD_Hm3', cv_agua_USD_Hm3, 35);
  Result.addCampoDef('hToma', hToma, 35);
  Result.addCampoDef('caQE', caQE, 35);
  Result.addCampoDef('cbQE', cbQE, 35);
  Result.addCampoDef('ren', ren, 35);
  Result.addCampoDef('Pmax_Gen', Pmax_Gen, 35);
  Result.addCampoDef('Qmax_Turb', Qmax_Turb, 35);
  Result.addCampoDef('SaltoMinimoOperativo', saltoMinimoOperativo, 35);
  Result.addCampoDef('fDispo', fDispo, 35);
  Result.addCampoDef('tRepHoras', tRepHoras, 35);
  Result.addCampoDef_ref('central_lagoDescarga', TCosa(central_lagoDescarga), Self, 35);
  Result.addCampoDef('centralesAguasArriba', TCosa(centralesAguasArriba), 35);
  Result.addCampoDef('HayRestriccionEmaxPasoDeTiempo',
    HayRestriccionEmaxPasoDeTiempo, 35);
  Result.addCampoDef('EmaxPasoDeTiempo', EmaxPasoDeTiempo, 35);
  Result.addCampoDef('PagoPorDisponibilidad_USD_MWh',
    PagoPorDisponibilidad_USD_MWh, 98);
  Result.addCampoDef('PagoPorEnergia_USD_MWh', PagoPorEnergia_USD_MWh, 98);
  Result.addCampoDef('fReserva', fReserva, 192);
  Result.addCampoDef('Qmin_Vert', Qmin_Vert, 198);
  Result.addCampoDef('cvFallaQmin_Vert', cvFallaQmin_Vert, 198);
  Result.addCampoDef('flg_IgualPotenciaEnTodosLosPostes',
    flg_IgualPotenciaEnTodosLosPostes, 199);
end;

procedure TFichaHidroDePasada.BeforeRead(version, id_hilo: integer);
begin
  inherited BeforeRead(version, id_hilo);
  PagoPorDisponibilidad_USD_MWh := 0.0;
  PagoPorEnergia_USD_MWh := 0.0;
  AreaCuenca_ha := 0;
end;

procedure TFichaHidroDePasada.AfterRead(f: TArchiTexto);
begin
  inherited AfterRead(f);
  if f.Version < 35 then
  begin
    if f.Version < 14 then
    begin
      self.tRepHoras := 48;
      self.SaltoMinimoOperativo := 0.1;
    end;
    cv_agua_USD_Hm3 := 0;
  end;
end;




function TFichaHidroDePasada.infoAd_: string;
begin
  Result := 'PMáxGen= ' + FloatToStrF(Pmax_Gen, ffGeneral, 10, 1) +
    ' MW, ' + 'QMáxTurb= ' + FloatToStrF(Qmax_Turb, ffGeneral, 10, 1) +
    ' m^3/s, ' + 'ren= ' + FloatToStrF(ren, ffGeneral, 10, 2) +
    ' p.u., ' + 'fDisp= ' + FloatToStrF(fDispo, ffGeneral, 10, 2) +
    ' p.u., ' + 'tRep= ' + FloatToStrF(tRepHoras, ffGeneral, 10, 1) +
    'h, ' + 'hToma= ' + FloatToStrF(hToma, ffGeneral, 10, 2) +
    ' m, ' + 'hDescarga= ' + FloatToStrF(hDescarga, ffGeneral, 10, 2) + 'm';
end;




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

procedure THidroDePasada_cambioFichaPD(Actor: TCosa);
begin
  (Actor as THidroDePasada).cambioFichaPD;
end;

procedure AlInicio;
begin
  registrarClaseDeCosa(THidroDePasada.ClassName, THidroDePasada);
  registrarClaseDeCosa(TFichaHidroDePasada.ClassName, TFichaHidroDePasada);
end;

procedure AlFinal;
begin
end;

end.
