{+doc
+NOMBRE:   uParqueEolico
+CREACION: 22/12/2006
+AUTORES:  rch
+REGISTRO:
+TIPO: Unidad Pascal.
+PROPOSITO:   Definición del actor TParqueEolico
+PROYECTO: SimSEEU

+REVISION:
+AUTOR:
+DESCRIPCION:

-doc}

unit uParqueEolico_vxy;


interface

uses
  Classes,
  SysUtils,
  uFechas,
  uGeneradores,
  uFuentesAleatorias,
  uGlobs,
  uNodos,
  uCosa, uCosaConNombre,
  usimplex,
  uFichasLPD,
  uFuncionesReales,
  uConstantesSimSEE,
  xMatDefs;

resourcestring
  mesFuenteAleatConPaso1h_ =
    'TParqueEolico, necesita una fuente aleatoria con paso de sorteo = 1h.';
  mesElParque_ = 'El parque';
  mesConectadoAFuente_ = 'está conectado a la fuente';
  mesConPasoDeSorteo_ = 'con paso de sorteo';
  rsParqueEolico_vxy = 'Parque eólico_vxy';

type

  { TParqueEolico_vxy }

  TParqueEolico_vxy = class(TGeneradorPostizador)
  public

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

    // parámetros editables
    fdisp: NReal;
    tRepHoras: NReal;
    fPerdidasAerodinamicas: TDAofNReal;  // por dirección.

    fMultV: NReal;
    CurvaVP: TArrayOfFVectR;

    // fuente de viento
    fuenteDeViento: TFuenteAleatoria;
    nombreBorne_vx, nombreBorne_vy: string;

    PagoPorEnergiaDisponible_USD_MWh: NReal;
    PagoPorEnergiaEntregada_USD_MWh: NReal; // [USD/MWh] para cálculo de CAD
  (**************************************************************************)

    kBorneVelocidad_vx, kBorneVelocidad_vy: integer;

    {$IFDEF CALC_DEMANDA_NETA}
    kBornePotencia: integer;
    {$ELSE}
    kBornePPA: TDAofNInt; // Potencia del Poste Actual
    {$ENDIF}
    //parametros editables
    fxPerdidasAerodinamicas: TDAofNReal; // para guadar fPerdiasAerodinamicas * MultV

    VVel_x, VVel_y: NReal;

    constructor Create(capa: integer; nombre: string; nacimiento, muerte: TFecha;
      lpdUnidades: TFichasLPD; nodo: TNodo;
  flg_CalcularGradienteDeInversion: boolean; xFDV: TFuenteAleatoria;
  xNombreBorne_vx, xNombreBorne_vy: string; xfdisp: NReal; tRepHoras: NReal;
  xfPerdidasAerodinamicas: TDAOfNReal; xfMultV: NReal;
  xCurvaVelocidadPotencia_Pot: TArrayOfFVectR;
  xPagoPorEnergiaDisponible_USD_MWh, xPagoPorEnergiaEntregada_USD_MWh: NReal;
  xflg_RestarParaPostizado: boolean; TonCO2xMWh: NReal; LowCostMustRun,
  CleanDevelopmentMechanism: boolean; xFuenteIdxP: TFuenteAleatoria;
  xBorneIdxP: string);

    constructor Create_dummy; override;
    procedure Free_dummy; override;

    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 PotenciaFirme: NReal; override;

    function InfoAd_: string; override;

    class function DescClase: string; override;

    function get_pa_FD( kTipoUnidad: integer ): NReal; override;
    function get_pa_TMR( kTipoUnidad: integer ): NReal; 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;

    destructor destroy; override;
    procedure PubliVars; override;
    procedure dump_Variables(var f: TextFile; charIndentacion: char); override;

    function getNombreVar(ivar: integer; var nombre: string): boolean; override;

    procedure CalcularPagoServicioDeConfiabilidaddelSist; override;

    private
        tvr: TFVectR;

  end;

procedure AlInicio;
procedure AlFinal;

implementation


constructor TParqueEolico_vxy.Create(capa: integer; nombre: string; nacimiento, muerte: TFecha;
  lpdUnidades: TFichasLPD; nodo: TNodo;
  flg_CalcularGradienteDeInversion: boolean;
  xFDV: TFuenteAleatoria;
  xNombreBorne_vx, xNombreBorne_vy: string; xfdisp: NReal; tRepHoras: NReal;
  xfPerdidasAerodinamicas: TDAOfNReal; xfMultV: NReal;
  xCurvaVelocidadPotencia_Pot: TArrayOfFVectR;
  xPagoPorEnergiaDisponible_USD_MWh, xPagoPorEnergiaEntregada_USD_MWh: NReal;
  xflg_RestarParaPostizado: boolean;
  TonCO2xMWh: NReal; LowCostMustRun, CleanDevelopmentMechanism: boolean;
  xFuenteIdxP: TFuenteAleatoria; xBorneIdxP: string );
begin
  inherited Create(capa, nombre, nacimiento, muerte, lpdUnidades, nodo,
    TonCO2xMWh, LowCostMustRun, CleanDevelopmentMechanism,
    xflg_RestarParaPostizado, flg_CalcularGradienteDeInversion,
    xFuenteIdxP, xBorneIdxP );

  fdisp := xfdisp;
  self.tRepHoras := tRepHoras;
  fPerdidasAerodinamicas := xfPerdidasAerodinamicas;
  fMultV := xfMultV;
  CurvaVP := xCurvaVelocidadPotencia_Pot;
  FuenteDeViento := xFDV;
  NombreBorne_vx := xNombreBorne_vx;
  NombreBorne_vy := xNombreBorne_vy;
  kBorneVelocidad_vx := -1;
  kBorneVelocidad_vy := -1;
  PagoPorEnergiaDisponible_USD_MWh:= xPagoPorEnergiaDisponible_USD_MWh;
  PagoPorEnergiaEntregada_USD_MWh := xPagoPorEnergiaEntregada_USD_MWh;
end;

constructor TParqueEolico_vxy.Create_dummy;
begin
  inherited Create_dummy;
  setlength(CurvaVP, 16);
end;

procedure TParqueEolico_vxy.Free_dummy;
begin
  setlength( CUrvaVP, 0 );
  inherited Free_dummy;
end;

function TParqueEolico_vxy.Rec: TCosa_RecLnk;
begin
  Result:=inherited Rec;
  Result.addCampoDef('fdisp', fdisp);
  Result.addCampoDef('tRepHoras', tRepHoras);
  Result.addCampoDef('fPerdidasAerodinamicas', fPerdidasAerodinamicas);
  Result.addCampoDef('fMultV', fMultV, 39, 0, '1.0' );
  Result.addCampoDef('CurvaVP', TCosa(tvr), 0, 40 );
  Result.addCampoDef('CurvaVP_N', TCosa(CurvaVP[0]), 40, 41 );
  Result.addCampoDef('CurvaVP_NNE', TCosa(CurvaVP[1]), 40, 41 );
  Result.addCampoDef('CurvaVP_NE', TCosa(CurvaVP[2]), 40, 41 );
  Result.addCampoDef('CurvaVP_ENE', TCosa(CurvaVP[3]), 40, 41 );
  Result.addCampoDef('CurvaVP_ESE', TCosa(CurvaVP[4]), 40, 41 );
  Result.addCampoDef('CurvaVP_SE', TCosa(CurvaVP[5]), 40, 41 );
  Result.addCampoDef('CurvaVP_SSE', TCosa(CurvaVP[6]), 40, 41 );
  Result.addCampoDef('CurvaVP_S', TCosa(CurvaVP[7]), 40, 41 );
  Result.addCampoDef('CurvaVP_SSO', TCosa(CurvaVP[8]), 40, 41 );
  Result.addCampoDef('CurvaVP_SO', TCosa(CurvaVP[9]), 40, 41 );
  Result.addCampoDef('CurvaVP_OSO', TCosa(CurvaVP[10]), 40, 41 );
  Result.addCampoDef('CurvaVP_O', TCosa(CurvaVP[11]), 40, 41 );
  Result.addCampoDef('CurvaVP_ONO', TCosa(CurvaVP[12]), 40, 41 );
  Result.addCampoDef('CurvaVP_NO', TCosa(CurvaVP[13]), 40, 41 );
  Result.addCampoDef('CurvaVP_NNO', TCosa(CurvaVP[14]), 40, 41 );
  Result.addCampoDef('CurvaVP_N', TCosa(CurvaVP[15]), 40, 41 );
  Result.addCampoDef('CurvaVP_N', TCosa(CurvaVP[0]), 41 );
  Result.addCampoDef('CurvaVP_NNE', TCosa(CurvaVP[1]), 41 );
  Result.addCampoDef('CurvaVP_NE', TCosa(CurvaVP[2]), 41 );
  Result.addCampoDef('CurvaVP_ENE', TCosa(CurvaVP[3]), 41 );
  Result.addCampoDef('CurvaVP_E', TCosa(CurvaVP[4]), 41 );
  Result.addCampoDef('CurvaVP_ESE', TCosa(CurvaVP[5]), 41 );
  Result.addCampoDef('CurvaVP_SE', TCosa(CurvaVP[6]), 41 );
  Result.addCampoDef('CurvaVP_SSE', TCosa(CurvaVP[7]), 41 );
  Result.addCampoDef('CurvaVP_S', TCosa(CurvaVP[8]), 41 );
  Result.addCampoDef('CurvaVP_SSO', TCosa(CurvaVP[9]), 41 );
  Result.addCampoDef('CurvaVP_SO', TCosa(CurvaVP[10]), 41 );
  Result.addCampoDef('CurvaVP_OSO', TCosa(CurvaVP[11]), 41 );
  Result.addCampoDef('CurvaVP_O', TCosa(CurvaVP[12]), 41 );
  Result.addCampoDef('CurvaVP_ONO', TCosa(CurvaVP[13]), 41 );
  Result.addCampoDef('CurvaVP_NO', TCosa(CurvaVP[14]), 41 );
  Result.addCampoDef('CurvaVP_NNO', TCosa(CurvaVP[15]), 41 );
  Result.addCampoDef_ref('fuenteDeViento', TCosa(fuenteDeViento), Self);
  Result.addCampoDef('nombreBorne_vx', nombreBorne_vx);
  Result.addCampoDef('nombreBorne_vy', nombreBorne_vy);
  Result.addCampoDef('pagoPorEnergia', PagoPorEnergiaEntregada_USD_MWh, 0, 98 );
  Result.addCampoDef('PagoPorDisponibilidad_USD_MWh', PagoPorEnergiaDisponible_USD_MWh, 98, 103 );
  Result.addCampoDef('PagoPorEnergia_USD_MWh', PagoPorEnergiaEntregada_USD_MWh, 98, 103 );
  Result.addCampoDef('PagoPorEnergiaDisponible_USD_MWh', PagoPorEnergiaDisponible_USD_MWh, 103 );
  Result.addCampoDef('PagoPorEnergiaEntregada_USD_MWh', PagoPorEnergiaEntregada_USD_MWh,  103 )
end;

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

    PagoPorEnergiaDisponible_USD_MWh:= 0;
    PagoPorEnergiaEntregada_USD_MWh:= 0;

    setlength(CurvaVP, 16);


end;

procedure TParqueEolico_vxy.AfterRead(f:TArchiTexto);
var
  kDir: integer;
begin
  inherited AfterRead(f);
    if f.Version < 40 then
    begin
      for kdir := 0 to 15 do
        CurvaVP[kdir] := tvr.Create_Clone( nil, 0 ) as TFVectR;
      tvr.Free;
    end;
    kBorneVelocidad_vx := -1;
    kBorneVelocidad_vy := -1;
end;




destructor TParqueEolico_vxy.destroy;
var
  k: integer;
begin
  for k := 0 to high( CurvaVP ) do
    CurvaVP[k].Free;
  {$IFNDEF CALC_DEMANDA_NETA}
  SetLength(kBornePPA, 0);
  {$ENDIF}
  SetLength(fxPerdidasAerodinamicas, 0);
  SetLength(fPerdidasAerodinamicas, 0);
  SetLength(PPA, 0);
  inherited destroy;
end;

procedure TParqueEolico_vxy.PubliVars;
begin
  inherited PubliVars;
  PublicarVariableVR('PotenciaGenerable', '[MW]', 6, 1, PPA, True, False);
  PublicarVariableNR('VVel_x', '[m/s]', 6, 2, VVel_x, True);
  PublicarVariableNR('VVel_y', '[m/s]', 6, 2, VVel_y, True);
  PublicarVariableNI('NMaqsDisponibles', '-', NMaquinasDisponibles, True);
end;

 

procedure TParqueEolico_vxy.PrepararMemoria(Catalogo: TCatalogoReferencias;
  globs: TGlobs);
var
  jdir: integer;
  auxf: TFR2enR;
  {$IFNDEF CALC_DEMANDA_NETA}
  iposte: integer;
  {$ENDIF}
begin
  inherited prepararMemoria(Catalogo, globs);
  setlength(PPA, globs.NPostes);

  if not ((fuenteDeViento.durPasoDeSorteoEnHoras = 1) or
    ((fuenteDeViento.durPasoDeSorteoEnHoras = 0) and (globs.HorasDelPaso = 1))) then
    raise Exception.Create( mesFuenteAleatConPaso1h_ +' ' + mesElParque_ +' ' +
      nombre + ' '+ mesConectadoAFuente_+ ' ' + fuenteDeViento.nombre +
      ' '+mesConPasoDeSorteo_+' = ' + IntToStr(fuenteDeViento.durPasoDeSorteoEnHoras));

  if kBorneVelocidad_vx = -1 then // la primera vez definimos los bornes
  begin
    kBorneVelocidad_vx := fuenteDeViento.idBorne(nombreBorne_vx);
    kBorneVelocidad_vy := fuenteDeViento.idBorne(nombreBorne_vy);

    setlength(fxPerdidasAerodinamicas, length(fPerdidasAerodinamicas));
    for jdir := 0 to high(fPerdidasAerodinamicas) do
      fxPerdidasAerodinamicas[jdir] := fPerdidasAerodinamicas[jdir] * fMultV;


    {$IFDEF CALC_DEMANDA_NETA}
    auxf := TFf_xmult_ConSelectorYBuffer_vxy.Create(capa, CurvaVP,
      fxPerdidasAerodinamicas, 1, @globs.MesInicioDelPaso,
      @PHoraria_PreSorteosPorUnidad, @globs.kSubPaso_);
    kBornePotencia := fuenteDeViento.registrarFuncion_biborne(
      auxf, nombreBorne_vx, nombreBorne_vy, -1);

    if fuenteDeViento.ResumirMaxVar( globs ) <> nil then
    begin
       fuenteDeViento.ResumirMaxVar( globs ).flg_ResumirBorneras:= false;
       flg_ResumirPromediando:= false;
    end
    else
        flg_ResumirPromediando:= true;
    {$ELSE}
    SetLength(kBornePPA, globs.NPostes);
    for iposte := 0 to high(kBornePPA) do
    begin
       (*
       Para cada poste definimos un borne auxiliar que llevará la cuenta
       de la energía generada por el parque en ese poste y así puede calcular
       la potencia media equivalente del parque para ese poste.
       Se supone que la fuente de viento tiene paso de sorteo HORARIO.
       *)

      auxf := TFf_xmult_conselector_vxy.Create(capa, CurvaVP,
        fxPerdidasAerodinamicas, 1, @globs.MesInicioDelPaso, iposte,
        @globs.kPosteHorasDelPaso, @globs.kSubPaso_);

      kBornePPA[iposte] := fuenteDeViento.registrarFuncion_biborne(
        auxf, nombreBorne_vx, nombreBorne_vy, iposte);
    end;
    {$ENDIF}
    end;
end;

function TParqueEolico_vxy.PotenciaFirme: NReal;
begin
  Result := 0;
end;

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

class function TParqueEolico_vxy.DescClase: string;
begin
  Result := rsParqueEolico_vxy;
end;


function TParqueEolico_vxy.get_pa_FD( kTipoUnidad: integer ): NReal;
begin
  result:= fdisp;
end;

function TParqueEolico_vxy.get_pa_TMR( kTipoUnidad: integer ): NReal;
begin
  result:= tRepHoras;
end;


procedure TParqueEolico_vxy.SorteosDelPaso(sortear: boolean);
begin
  if globs.ObligarDisponibilidad_1_ then
    NMaquinasDisponibles := paUnidades.nUnidades_Operativas[0]
  else if (sortear) then
  begin
    ActualizarProbabilidadesReparacionYRotura_(fdisp, tRepHoras);
    NMaquinasDisponibles := Sorteos_RepRotUnidades;
  end
  else
    NMaquinasDisponibles := paUnidades.nUnidades_Operativas[0];
  inherited SorteosDelPaso( sortear );
end;

procedure TParqueEolico_vxy.PrepararPaso_ps;
var
  iposte: integer;
  jHora: integer;


begin
  inherited PrepararPaso_ps;

  VVel_x := Self.fuenteDeViento.bornera[Self.kBorneVelocidad_vx];
  VVel_y := Self.fuenteDeViento.bornera[Self.kBorneVelocidad_vy];

  {$IFNDEF CALC_DEMANDA_NETA}
  EnergiaDisponibleDelPaso:= 0;
  for iposte := 0 to high(PPA) do
  begin
    PPA[iposte] := Self.fuenteDeViento.bornera[Self.kBornePPA[iposte]] *
      NMaquinasDisponibles;
    EnergiaDisponibleDelPaso:=  EnergiaDisponibleDelPaso + PPA[iposte] * globs.DurPos[iposte];
  end;
  {$ENDIF}

end;

procedure TParqueEolico_vxy.opt_nvers(var ivar, ivae, ires: integer);
begin
  self.ivar := ivar;
  self.ivae := ivae;
  self.ires := ires;
  ivar := ivar + globs.NPostes;
end;

function TParqueEolico_vxy.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
    Result := False;
end;

procedure TParqueEolico_vxy.CalcularPagoServicioDeConfiabilidaddelSist;
var
   iPoste: integer;
   CmgMenosTecho, TechoMenosCV: NReal;
begin
    ParticipacionSCS:= 0;
    ForzamientoSCS:=0;
    for iPoste:= 0 to high( P )  do
    begin
       CmgMenosTecho:= nodo.cmarg[iposte] - globs.TechoDelSpot;
       if  CmgMenosTecho > 0  then
           ParticipacionSCS:= ParticipacionSCS + P[iposte]* globs.durpos[iposte] * CmgMenosTecho // al ltriangulito.
    end
end;

procedure TParqueEolico_vxy.opt_cargue(s: TSimplex);
var
  iposte: integer;
begin
  if NMaquinasDisponibles = 0 then
    exit;

  for iposte := 0 to globs.NPostes - 1 do
    s.pon_e(nodo.ires + iposte, ivar + iposte, 1);
end;

procedure TParqueEolico_vxy.opt_fijarRestriccionesDeCaja(s: TSimplex);
var
  iposte: integer;
begin
  if NMaquinasDisponibles = 0 then
    exit;
  // Le fijamos como cota superior PMax a la potencia en todos los postes
  for iposte := 0 to globs.NPostes - 1 do
    if PPA[iposte] > AsumaCero then
      s.cota_sup_set(ivar + iposte, PPA[iposte])
    else
      s.FijarVariable(ivar + iposte, 0);
end;

procedure TParqueEolico_vxy.opt_leerSolucion(s: TSimplex);
var
  iposte: integer;
  e, eacum: NReal;
begin
  costoDirectoDelPaso := 0;
  if NMaquinasDisponibles = 0 then
  begin
    vclear( P );
    //vclear( cv_Spot );
    for iposte := 0 to globs.NPostes - 1 do
    begin
      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;
    Ingreso_PorDisponibilidad_ := 0;
    Ingreso_PorEnergia_ := 0;
    costoDirectoDelPaso := 0;
    exit;
  end;

  // recuperamos los valores de Potencia despachada
  eacum := 0;
  for iposte := 0 to globs.NPostes - 1 do
  begin
    e := s.xval(ivar + iposte);
    P[iposte] := e;
    Lambda_P[iPoste]:= s.xmult( ivar + iposte ) / globs.DurPos[iposte];
    cv_Spot[iPoste]:= Nodo.cmarg[iposte] - Lambda_P[iPoste];
    eacum := eacum + e * globs.DurPos[iposte];
  end;

  Ingreso_PorDisponibilidad_:= EnergiaDisponibleDelPaso * PagoPorEnergiaDisponible_USD_MWh;
  Ingreso_PorEnergia_:= eacum * PagoPorEnergiaEntregada_USD_MWh;
end;



procedure TParqueEolico_vxy.dump_Variables(var f: TextFile; charIndentacion: char);
var
  i: integer;
begin
  inherited dump_Variables(f, charIndentacion);
  writeln(f, charIndentacion, 'VVel_vx[m/s]= ', FloatToStrF(VVel_x, ffFixed, 10, 3));
  writeln(f, charIndentacion, 'VVel_vy[m/s]= ', FloatToStrF(VVel_y, ffFixed, 10, 3));
  for i := 0 to high(PPA) do
    writeln(f, charIndentacion, 'PPA[', i, '][MW]: ',
      FloatToStrF(PPA[i], ffFixed, 10, 3));
  writeln(f);
  //  writeln(f, charIndentacion, 'PotMediaGenerable[MW]= ', FloatToStrF(PotMediaGenerable, ffFixed, 10, 3));
  writeln(f, charIndentacion, 'NMaquinasDisponibles= ', NMaquinasDisponibles);
  writeln(f);
end;

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

procedure AlFinal;
begin
end;

end.
