unit udemandaCombustibleAnioBaseEIndices;

{$MODE Delphi}

interface

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

resourcestring
  rsDemandaGenererada =
    'Demanda Combustible, año base y consumos anuales';

type

  { TDemandaCombustibleAnioBaseEIndices }

  TDemandaCombustibleAnioBaseEIndices = class(TDemandaCombustible)
  private
    CacheActivo: boolean;
    procedure InitFromFile;

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

    ArchiDatos: TArchiRef_Nubeseable;
    dummy_ArchiDatos: TArchiRef_;
    anioIni: integer;
    anioFin: integer;
    // Primer y último año para el que se especificaron valores
    Mm3_anios: TDAofNReal; // la cantidad de Mm3 demandada para cada año
    // siguiente al de inicio de la simulación

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

    fIniAnioBase: TFecha;

    Q_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 [Mm3] del año base considerando 365 y
    // 366 días



    constructor Create(capa: integer; nombre: string;
      lpdUnidades: TFichasLPD; nodocomb: TNodoCombustible; falla_profundidad,
  falla_costo: TDAOfNReal; ArchiDatos: string; fuente: TFuenteAleatoria;
  nombreBorne: string; SumarCaudalHr: boolean; xAnioIni, xAnioFin: integer;
  xMm3_anios: TDAofNReal; icf_Fuente: TFuenteAleatoria;
  icf_NombreBorne: string; 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 CaudalFirme: NReal;

    function getArchiRefLst: TListaDeArchiRef_Nubeseable; override;

    //      procedure prepararSim; override;
    procedure prepararPaso_as; override;
    destructor destroy; override;
  end;

procedure AlInicio;
procedure AlFinal;

implementation

//------------------------------------
// Métodos de TDemandaCombustibleAnioBaseEIndices
//====================================


constructor TDemandaCombustibleAnioBaseEIndices.Create(capa: integer;
  nombre: string; lpdUnidades: TFichasLPD;
  nodocomb: TNodoCombustible; falla_profundidad, falla_costo: TDAOfNReal;
  ArchiDatos: string; fuente: TFuenteAleatoria; nombreBorne: string;
  SumarCaudalHr: boolean; xAnioIni, xAnioFin: integer; xMm3_anios: TDAofNReal;
  icf_Fuente: TFuenteAleatoria; icf_NombreBorne: string;
  xFuenteIdxP: TFuenteAleatoria; xBorneIdxP: string);
begin
  inherited Create(capa, nombre, nacimiento, muerte, lpdUnidades, nodocomb,
    falla_profundidad, falla_costo, fuente, nombreBorne, icf_Fuente,
    icf_NombreBorne, SumarCaudalHr, xFuenteIdxP, xBorneIdxP );

  if FileExists(ArchiDatos) { *Converted from FileExists*  } then
    self.ArchiDatos := TArchiRef_Nubeseable.Create(capa,0,0,false, TArchiRef_.Create(ArchiDatos))
  else
    raise ExceptionFileNotFound.Create(ArchiDatos, 'Creando el actor ' + ClaseNombre);
  AnioIni := xAnioIni;
  AnioFin := xAnioFin;
  Mm3_anios := xMm3_anios;
  CacheActivo := False;
  InitFromFile;
end;

function TDemandaCombustibleAnioBaseEIndices.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('AnioIni', AnioIni);
  Result.addCampoDef('AnioFin', AnioFin);
  Result.addCampoDef('xMm3_anios', Mm3_anios);
end;

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

procedure TDemandaCombustibleAnioBaseEIndices.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, ArchiDatos);
  end ;


end;

procedure TDemandaCombustibleAnioBaseEIndices.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(Mm3_anios) < (AnioFin - AnioIni + 1) then
      AnioFin := AnioIni + high(Mm3_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 TDemandaCombustibleAnioBaseEIndices.InfoAd_: string;
begin
  Result := ArchiDatos.ArchiRef.get_archi;
end;

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

function TDemandaCombustibleAnioBaseEIndices.CaudalFirme: NReal;
begin
  Result := paUnidades.nUnidades_Operativas[0] * vmax(self.QHoraria);
end;

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

procedure TDemandaCombustibleAnioBaseEIndices.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;
  Q1, Q2: 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(Mm3_anios) then
      kAnio := high(Mm3_anios);

    if not IsLeapYear(anio) then
    begin
      factCrecimiento := Mm3_anios[kAnio] * invSDem365;
      jHoraDelAnio_UltimaHoraDelAnio := 365 * 24 - 1;
    end
    else
    begin
      factCrecimiento := Mm3_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);
    Q1 := Q_demAnioBase[ih];
    Inc(ih);
    if ih <= high(Q_demAnioBase) then
      Q2 := Q_demAnioBase[ih]
    else
      Q2 := Q_demAnioBase[0];
    QHoraria[0] := (Q1 * (1 - alfa) + Q2 * 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(QHoraria) do
        QHoraria[jHoraDelPaso] :=
          Q_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
        QHoraria[jHoraDelPaso] :=
          Q_demAnioBase[jHoraDelAnio_desde + jHoraDelPaso] * factCrecimiento;

      ActualizarFactorYUltimaHoraDelAnio(globs.AnioInicioDelPaso + 1);

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

  inherited prepararPaso_as;
end;

destructor TDemandaCombustibleAnioBaseEIndices.destroy;
begin
  fIniAnioBase.Free;
  if Q_demAnioBase <> nil then
    SetLength(Q_demAnioBase, 0);
  SetLength(Mm3_anios, 0);
  inherited destroy;
end;


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

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

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

  invSDem365 := 1.0E6 / invSDem365;
  invSDem366 := 1.0E6 / invSDem366;

  for i := 0 to high(Q_demAnioBase) do
    Q_demAnioBase[i] := Q_demAnioBase[i] / 3600.0; // pasamos de m3/h a m3/s

  datos.Free;
end;

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

procedure AlFinal;
begin
end;

end.
