unit uDemandaAnioBaseEIndices;

{$MODE Delphi}

interface

uses
  SysUtils,
  uauxiliares,
  xmatdefs,
  Math,
  uglobs, uDemandas, unodos, usimplex,
  ufechas,usalasdejuego,
  ucosa, uCosaConNombre,
  udatoshorariosdetallados,ucosanubeseable,
  uconstantesSimSEE, uFuentesAleatorias, uFichasLPD;

resourcestring
  rsDemandaGenererada =
    'Demanda generada a partir de un año base y vector de energías anuales';

type

  { TDemandaAnioBaseEIndices }

  TDemandaAnioBaseEIndices = class(TDemanda)
  private
    CacheActivo: boolean;
    procedure InitFromFile;

  public
    ArchiDatos: TArchiRef_Nubeseable;
    dummy_ArchiDatos: TArchiRef_;

    anioIni, anioFin: integer;
    // Primer y último año para el que se especificaron valores
    GWh_anios: TDAofNReal; // la cantidad de GWh demandada para cada año
    // siguiente al de inicio de la simulación

    fIniAnioBase: TFecha;

    demAnioBase: TDAofNReal; // La demanda hora a hora para un año
    // base de 366 días
    invSDem365, invSDem366: NReal; // 1 / la sumatoria de las demandas horarias
    // en [GWh] del año base considerando 365 y
    // 366 días



    constructor Create(capa: integer; nombre: string; lpdUnidades: TFichasLPD;
      nodo: TNodo; falla_profundidad, falla_costo: TDAOfNReal;
  ArchiDatos: TArchiRef_Nubeseable; fuente: TFuenteAleatoria;
  nombreBorne: string; SumarEnergiaHr, flg_SumarParaPostizado: boolean;
  xAnioIni, xAnioFin: integer; xGWh_anios: TDAofNReal;
  icf_Fuente: TFuenteAleatoria; icf_NombreBorne: string; fReserva: NReal;
  PrioridadParaSpot: integer; xFuenteIdxP: TFuenteAleatoria; xBorneIdxP: string
  );

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

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

    function InfoAd_: string; override;
    class function DescClase: string; override;

    function PotenciaFirme: NReal; override;

    function getArchiRefLst: TListaDeArchiRef_Nubeseable; override;

    //      procedure prepararSim; override;
    procedure prepararPaso_as; override;

    procedure opt_cargue(s: TSimplex); override;

    destructor destroy; override;


  end;

procedure AlInicio;
procedure AlFinal;

implementation

//------------------------------------
// Métodos de TDemandaAnioBaseEIndices
//====================================


constructor TDemandaAnioBaseEIndices.Create(capa: integer; nombre: string;
  lpdUnidades: TFichasLPD; nodo: TNodo; falla_profundidad,
  falla_costo: TDAOfNReal; ArchiDatos: TArchiRef_Nubeseable;
  fuente: TFuenteAleatoria; nombreBorne: string; SumarEnergiaHr,
  flg_SumarParaPostizado: boolean; xAnioIni, xAnioFin: integer;
  xGWh_anios: TDAofNReal; icf_Fuente: TFuenteAleatoria;
  icf_NombreBorne: string; fReserva: NReal; PrioridadParaSpot: integer;
  xFuenteIdxP: TFuenteAleatoria; xBorneIdxP: string);
begin
  inherited Create(capa, nombre, nacimiento, muerte, lpdUnidades, nodo,
    falla_profundidad, falla_costo,
    fuente, nombreBorne,
    icf_Fuente, icf_NombreBorne, SumarEnergiaHr, flg_SumarParaPostizado,
    PrioridadParaSpot, xFuenteIdxP, xBorneIdxP );

  self.ArchiDatos := ArchiDatos;
  self.fReserva := fReserva;

  AnioIni := xAnioIni;
  AnioFin := xAnioFin;
  GWh_anios := xGWh_anios;
  CacheActivo := False;
  InitFromFile;
end;

function TDemandaAnioBaseEIndices.Rec: TCosa_RecLnk;
begin
  Result := inherited Rec;
  Result.addCampoDef_archRef('ArchiDatos', dummy_ArchiDatos, 0, 181 );
  Result.addCampoDef('nb_ArchiDatos', TCosa(ArchiDatos),177,182 );
  Result.addCampoDef_ref('nb_ArchiDatos_ref', TCosa(ArchiDatos),self,182 );
  Result.addCampoDef('indices', GWh_anios, 0, 12);
  Result.addCampoDef('AnioIni', AnioIni, 12, 0, '-1');
  Result.addCampoDef('AnioFin', AnioFin, 12, 0, '-1');
  Result.addCampoDef('GWh_anios', GWh_anios, 12);
end;

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

procedure TDemandaAnioBaseEIndices.AfterRead(f:TArchiTexto);

begin
  inherited AfterRead(f);
  CacheActivo := False;

  if f.version < 177 then
    ArchiDatos:= f.Create_add_ArchiRefNubeseable_( self.capa,  dummy_ArchiDatos) as TArchiRef_Nubeseable;


   if f.version < 182 then
  //registrar el archivo en las referencias
    begin
      if (ArchiDatos  <> nil) then // and (ArchiDatos.ArchiRef.archi <> '')   then
        TCatalogoReferencias(f.evaluador.Catalogo).registrar_referencia(self,
            self.ArchiDatos.ClassName, self.ArchiDatos.ArchiRef.get_archi, self.ArchiDatos);
    end;


end;


procedure TDemandaAnioBaseEIndices.PrepararMemoria(Catalogo: TCatalogoReferencias;
  globs: TGlobs);

begin
  inherited prepararMemoria(Catalogo, globs);

  InitFromFile;

  if AnioIni < 0 then
  begin
    // esta arreglando el entuerto de la versión anterior a la 12 que no
    // tenía AnioIni y AnioFin

    AnioIni := globs.fechaIniOpt.anio;
    Aniofin := globs.fechaFinOpt.anio;
    if length(GWh_anios) < (AnioFin - AnioIni + 1) then
      AnioFin := AnioIni + high(GWh_anios);
    globs.Alerta('Ojo, revise el actor Demanda: ' + nombre +
      '. Fue transformado desde una versión anterior que no especificaba el año de inicio y de fin.');
  end;

  if (anioIni > globs.fechaIniSim.anio) then
    globs.Alerta('ojo, La demanda (' + nombre +
      ') no cubre el inicio de la Simulación, se usará el primer año para cubrir el hueco.');

  if (anioIni > globs.fechaIniOpt.anio) then
    globs.Alerta('ojo, La demanda (' + nombre +
      ') no cubre el inicio de la Optimización, se usará el primer año para cubrir el hueco.');

  if (globs.fechaFinSim.anio > anioFin) then
    globs.Alerta('ojo, La demanda (' + nombre +
      ') no cubre el final de la Simulación, se usará el último año para cubrir el hueco.');

  if (globs.fechaFinOpt.anio > anioFin) then
    globs.Alerta('ojo, La demanda (' + nombre +
      ') no cubre el final de la Optimización, se usará el último año para cubrir el hueco.');

end;

function TDemandaAnioBaseEIndices.InfoAd_: string;
begin
  Result := ArchiDatos.ArchiRef.get_archi;
end;

class function TDemandaAnioBaseEIndices.DescClase: string;
begin
  Result := rsDemandaGenererada;
end;

function TDemandaAnioBaseEIndices.PotenciaFirme: NReal;
begin
  Result := paUnidades.nUnidades_Operativas[0] * vmax(self.PPa);
end;

function TDemandaAnioBaseEIndices.getArchiRefLst: TListaDeArchiRef_Nubeseable;
var
   res: TListaDeArchiRef_Nubeseable;
begin
  res:= TListaDeArchiRef_Nubeseable.Create(capa,'DemandaAnioBaseEIndices');
  if ArchiDatos<>nil then
     res.Add(ArchiDatos);
  Result:=res;
end;

procedure TDemandaAnioBaseEIndices.prepararPaso_as;
var
  factCrecimiento: NReal;
  kAnio: integer; // indice del año del paso dentro del vector de años especificados
  jHoraDelPaso: integer; // hora desde el inicio delpaso 0 ... durpaso-1
  jHoraDelAnio_ultimaHoraDelAnio: integer;
  // auxiliar, se calcula al actualizar el factor
  jHoraDelAnio_desde, jHoraDelAnio_hasta: integer; // limites del paso
  jLimite: integer;

  dtInicioDelAnio: TDateTime;
  rh: NReal;
  ih: integer;
  alfa: NReal;
  P1, P2: NReal;

  procedure ActualizarFactorYUltimaHoraDelAnio(anio: integer);
  begin
    kAnio := anio - AnioIni;

    // si actua esta extensión ya se lo avisamos al preparar memoria.
    if kAnio < 0 then
      kAnio := 0
    else if kAnio > high(GWh_anios) then
      kAnio := high(GWh_anios);

    if not IsLeapYear(anio) then
    begin
      factCrecimiento := GWh_anios[kAnio] * invSDem365;
      jHoraDelAnio_UltimaHoraDelAnio := 365 * 24 - 1;
    end
    else
    begin
      factCrecimiento := GWh_anios[kAnio] * invSDem366;
      jHoraDelAnio_UltimaHoraDelAnio := 366 * 24 - 1;
    end;
  end;

begin
  ActualizarFactorYUltimahoraDelAnio(globs.AnioInicioDelPaso);
  if globs.SalaMinutal then
  begin
    rh := globs.FechaInicioDelpaso.diasDesdeElInicioDelAnio * 24.0;
    ih := trunc(rh);
    alfa := frac(rh);
    P1 := demAnioBase[ih];
    Inc(ih);
    if ih <= high(demAnioBase) then
      P2 := demAnioBase[ih]
    else
      P2 := demAnioBase[0];
    PHoraria_SinAfectar[0] := (P1 * (1 - alfa) + P2 * alfa) * factCrecimiento;
  end
  else
  begin
    // inicializamos puntero de lectura sobre el anio base.
    jHoraDelAnio_desde := globs.FechaInicioDelpaso.horasDesdeElInicioDelAnio;
    jHoraDelAnio_hasta := ceil(jHoraDelAnio_desde + globs.HorasDelPaso - 1);

    //Cargamos los datos que vamos a usar en el paso
    if jHoraDelAnio_hasta <= jHoraDelAnio_UltimaHoraDelAnio then
    begin // el paso está íntegro dentro del mismo año no necesita partir el for
      for jHoraDelPaso := 0 to high(PHoraria_SinAfectar) do
        PHoraria_SinAfectar[jHoraDelPaso] :=
          demAnioBase[jHoraDelAnio_desde + jHoraDelPaso] * factCrecimiento;
    end
    else // globs.FechaInicioDelpaso.anio < globs.FechaFinDelPaso.anio
    begin // el paso comienza en un año y termina en el siguiente

      jLimite := (jHoraDelAnio_UltimaHoraDelAnio - jHoraDelAnio_desde);
      for jHoraDelPaso := 0 to jLimite do
        PHoraria_SinAfectar[jHoraDelPaso] :=
          demAnioBase[jHoraDelAnio_desde + jHoraDelPaso] * factCrecimiento;

      ActualizarFactorYUltimaHoraDelAnio(globs.AnioInicioDelPaso + 1);

      for jHoraDelPaso := jLimite + 1 to high(PHoraria_SinAfectar) do
        PHoraria_SinAfectar[jHoraDelPaso] := demAnioBase[jHoraDelPaso] * factCrecimiento;
    end;
  end;

  inherited prepararPaso_as;
end;

procedure TDemandaAnioBaseEIndices.opt_cargue(s: TSimplex);
var
  iposte: integer;
  ibaseres: integer;
begin

  if NMaquinasDisponibles = 0 then
    exit;

  inherited opt_cargue(s);

  if (globs.flg_ReservaRotante) then
  begin
    ibaseres := nodo.ires;
    // Carga el término constante de las restricciones de demanda de Reserva
    for iposte := 0 to globs.NPostes - 1 do
      s.acum_e(ibaseres + globs.NPostes + iposte, s.nc, -PPa[iposte] * fReserva);
  end;
end;

destructor TDemandaAnioBaseEIndices.destroy;
begin
  fIniAnioBase.Free;
  if demAnioBase <> nil then
    SetLength(demAnioBase, 0);
  SetLength(GWh_anios, 0);
  inherited destroy;
end;


procedure TDemandaAnioBaseEIndices.InitFromFile;
var
  datos: TDatosHorariosDetallados;
  i: integer;
begin
  datos := TDatosHorariosDetallados.Create(self.ArchiDatos, uCosa.lista_caminos);
  SetLength(demAnioBase, 366 * 24);
  datos.ReadBuff_horario(demAnioBase, datos.fechaPrimerDia);

  fIniAnioBase := TFecha.Create_Dt(datos.fechaPrimerDia);

  invSDem365 := 0;
  for i := 0 to 365 * 24 - 1 do
  begin
    invSDem365 := invSDem365 + demAnioBase[i];
  end;
  invSDem366 := invSDem365;
  for i := 365 * 24 to 366 * 24 - 1 do
    invSDem366 := invSDem366 + demAnioBase[i];

  if abs( invSDem365 ) < AsumaCero then
  begin // esto es porque si ponen CERO en todas las horas daba error.
    invSDem365:= 365.0;
    invSDem366:= 366;
  end;

  invSDem365 := 1000 / invSDem365; //MWh --> GWh
  invSDem366 := 1000 / invSDem366;
  datos.Free;
end;


procedure AlInicio;
begin
  registrarClaseDeCosa(TDemandaAnioBaseEIndices.ClassName, TDemandaAnioBaseEIndices);
end;

procedure AlFinal;
begin
end;

end.
