(* En esta unidad se implementa el actor TDemandaDetallada
básicamente una demanda detallada está descripta por los valores de la demanda
para cada hora y para cada día en el horizonte de tiempo.

Los datos de la demanda son leidos de un archivo binario que tiene
al inicio un record con : el primer día de datos indicado como un TDateTime
y la fecha del día siguiente al último día de datos.

Luego en el binario hay un recor para cada día con los 24 valores correspondientes
a las potencias horarias en MW.

*)(*Se agrega la capacidad de cargar al simplex requerimientos de reserva
desde el 8/5/15 por Micho mvarela@adme.com.uy*)
unit udemandadetallada;

  {$MODE Delphi}

interface

uses
  SysUtils,
  uauxiliares,
  xmatdefs,
  uglobs,
  uDemandas, unodos, usimplex,
  ufechas, ucosanubeseable,
  ucosa, ucosaConNombre, udatoshorariosdetallados,
  uconstantesSimSEE, uFuentesAleatorias, Classes, uFichasLPD;

resourcestring
  rs_DemandaDetallada = 'Demanda Detallada';
  rs_DemandaDetalladaComienzaEn = 'Demanda detallada, comienza en';
  rs_LaSimulacionTieneFechaDeInicio = 'la simulación tiene fecha de inicio';
  rs_LaOptimizacionTieneFechaDeInicio = 'la optimización tiene fecha de inicio';
  rs_DemandaDetalladaTerminaEn = 'Demanda detallada, termina en';
  rs_LaSimulacionTieneFechaDeFin = 'la simulación tiene fecha de fin';
  rs_LaOptimizacionTieneFechaDeFin = 'la optimización tiene fecha de fin';

type

  { TDemandaDetallada }

  TDemandaDetallada = class(TDemanda)
  private
    CacheActivo: boolean;
  public

    ArchiDatos: TArchiRef_Nubeseable;
    dummy_ArchiDatos: TArchiRef_;

    datos: TDatosHorariosDetallados;

    constructor Create(capa: integer; nombre: string;
      lpdUnidades: TFichasLPD; nodo: TNodo; falla_profundidad,
  falla_costo: TDAOfNReal; ArchiDatos: string; fuente: TFuenteAleatoria;
  nombreBorne: string; SumarEnergiaHr, flg_SumarParaPostizado: boolean;
  icf_Fuente: TFuenteAleatoria; icf_NombreBorne: string; fReserva: NReal;
  Prioridad_TipoDemSpot: 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;
    procedure set_P_horario;

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

    function PotenciaFirme: NReal; override;

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


    destructor destroy; override;
    {===Se agregan con la reserva===}
    procedure opt_cargue(s: TSimplex); override;
    {=======Micho===================}

    function getArchiRefLst: TListaDeArchiRef_Nubeseable; override;


  end;

(* Escalones de falla por defecto
( Uruguay-invierno 2006 )
escf [pu]  0.05  0.075  0.075  0.8
cvf [USD/MWh]  140  400  1200  2000
*)

(* Deforma una curva de potencia horaria aplicando factores de crecimiento de
energía (fce) y del pico (fcp).
Por ejemplo, si el pico decrese 10% y la energía debe aumentar 5% los factores
serán fce= 1.05 y fcp= 0.9

El resultado es TRUE si todas las potencias resultantes son positivas y false
si alguna quedó negativa.
*)

procedure AlInicio;
procedure AlFinal;

implementation

(* Escalones de falla por defecto
( Uruguay-invierno 2006 )
escf [pu]  0.05  0.075  0.075  0.8
cvf [USD/MWh]  140  400  1200  2000
*)

//-----------------------------
// Métodos de TDemandaDetallada
//=============================

constructor TDemandaDetallada.Create(capa: integer; nombre: string;
   lpdUnidades: TFichasLPD; nodo: TNodo;
  falla_profundidad, falla_costo: TDAOfNReal; ArchiDatos: string;
  fuente: TFuenteAleatoria; nombreBorne: string;
  SumarEnergiaHr, flg_SumarParaPostizado: boolean; icf_Fuente: TFuenteAleatoria;
  icf_NombreBorne: string; fReserva: NReal; Prioridad_TipoDemSpot: 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, Prioridad_TipoDemSpot,
    xFuenteIdxP, xBorneIdxP );
  datos := nil;

  if FileExists(ArchiDatos) then
    self.ArchiDatos := TArchiRef_Nubeseable.Create(capa,0,0,false, TArchiRef_.Create(ArchiDatos))
  else
    raise ExceptionFileNotFound.Create(ArchiDatos, 'Creando el actor ' + ClaseNombre);

  CacheActivo := False;
  Self.fReserva := fReserva;
end;

function TDemandaDetallada.Rec: TCosa_RecLnk;
begin
  Result:=inherited Rec;
  Result.addCampoDef_archRef('ArchiDatos', dummy_ArchiDatos, 0, 181 );
  Result.addCampoDef( 'nb_ArchiDatos', TCosa(ArchiDatos), 176,182 );
  Result.addCampoDef_ref( 'nb_ArchiDatos_ref', TCosa(ArchiDatos),self, 182 );
end;

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

procedure TDemandaDetallada.AfterRead(f:TArchiTexto);
begin
  inherited AfterRead(f);
  CacheActivo := False;
  if f.version < 176 then
    ArchiDatos:= f.Create_add_ArchiRefNubeseable_(self.capa, self.dummy_ArchiDatos) as TArchiRef_Nubeseable;

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


 { if not ArchiDatos.testearYResolver then
    raise ExceptionFileNotFound.Create(ArchiDatos.ArchiRef.archi, 'Creando el actor ' +
      ClaseNombre + 'No se encontro el archivo'); }
end;

procedure TDemandaDetallada.PrepararMemoria(Catalogo: TCatalogoReferencias;
  globs: TGlobs);
begin
  inherited prepararMemoria(Catalogo, globs);

  if archiDatos.ArchiRef.get_archi = '' then
     exit;

  if datos = nil then
    datos := TDatosHorariosDetallados.Create(archiDatos, ucosa.lista_caminos);

  if globs.EstadoDeLaSala <> CES_SIN_PREPARAR then
  begin
    if (globs.fechaIniSim.AsDt < datos.fechaPrimerDia) then
      raise Exception.Create(rs_DemandaDetalladaComienzaEn + ': ' +
        DateTimeToStr(datos.FechaPrimerDia) + ' ' +
        rs_LaSimulacionTieneFechaDeInicio + ': ' + globs.fechaIniSim.AsStr);
    if (globs.fechaIniOpt.AsDt < datos.fechaPrimerDia) then
      raise Exception.Create(rs_DemandaDetalladaComienzaEn + ': ' +
        DateTimeToStr(datos.FechaPrimerDia) + ' ' +
        rs_LaOptimizacionTieneFechaDeInicio + ': ' + globs.fechaIniOpt.AsStr);

    if globs.fechaFinSim.asDt >= (datos.fechaUltimodia + 1) then
      raise Exception.Create(rs_DemandaDetalladaTerminaEn + ': ' +
        DateTimeToStr(datos.FechaUltimoDia + 23.0 / 24.0) + ' ' +
        rs_LaSimulacionTieneFechaDeFin + ': ' + globs.fechaFinSim.AsStr);
    if globs.fechaFinOpt.asDt >= (datos.fechaUltimodia + 1) then
      raise Exception.Create(rs_DemandaDetalladaTerminaEn + ': ' +
        DateTimeToStr(datos.FechaUltimoDia + 23.0 / 24.0) + ' ' +
        rs_LaOptimizacionTieneFechaDeFin + ': ' + globs.fechaFinOpt.AsStr);
  end;
end;

procedure TDemandaDetallada.set_P_horario;
var
  hora,iposte:integer;
begin
    for hora := 0 to Length(self.P_horaria) - 1 do
      begin
           iposte := globs.kPosteHorasDelPaso[hora];
           self.P_horaria[hora]:= self.P[iposte]
      end;
end;

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

class function TDemandaDetallada.DescClase: string;
begin
  Result := rs_DemandaDetallada;
end;

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


procedure TDemandaDetallada.prepararPaso_as;
begin
  if globs.SalaMinutal then
    PHoraria_SinAfectar[0] := datos.interpol(globs.FechaInicioDelPaso.AsDt)
  else
    datos.ReadBuff_horario( PHoraria_SinAfectar, globs.FechaInicioDelpaso.AsDt);
  inherited prepararPaso_as;
end;


destructor TDemandaDetallada.destroy;
begin
  datos.Free;
  inherited destroy;
end;


{===Lo podría agregar si quiero agregar un despacho de falla de reserva==}{
procedure TDemandaDetallada.opt_nvers(var ivar, ivae, ires: integer);
begin
end;
 }

procedure TDemandaDetallada.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;

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


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

procedure AlFinal;
begin
end;

end.
