unit uregasificadora;

interface

uses
  uCosa, SysUtils,
  uGlobs, uActores, uFechas, uCosaConNombre,
  uFichasLPD, usimplex, xMatDefs, uauxiliares, unodocombustible,
  uFuentesAleatorias, uTSumComb, uEstados, uAgendaGNL, Math, uDatosHorariosDetallados;

resourcestring
  rsRegasificadora =
    'Regasificadora';


const
  factor_GNL_a_GN = 610.0;


  {

**************************************************************
1 cubic meter liquid,  CuM,  600 cubic meter of gas
1 million metric ton/annum,  MMTPA,  4 MMSCMD
1 metric ton LNG,  MT,  1420 cubic meter of gas
1 metric ton LNG,  MT,  52 MMBTU
1000 cubic meter of gas,  MCM,  40 MMBTU
1 british thermal unit,  BTU,  252 calories
**************************************************************
MMSCMD stands for million standard cubic meters per day
MMBTU stands for million British thermal unit
MT stands for metric ton
MMTPA means million metric tons per annum
***************************************************************

  }



type

  { TFichaRegasificadora }

  TFichaRegasificadora = class(TFichaLPD)
  public
    (**************************************************************************)
    (*              A T R I B U T O S   P E R S I S T E N T E S               *)
    (**************************************************************************)

    fd: NReal;
    TMR: NReal;
    fuente_idxPrecioCombustible: TFuenteAleatoria;
    borne_idxPrecioCombustible: string;
    VGNL_max: NReal; //volumen maximo del tanque
    VGNL_min: NReal; //volumen minimo de GNL que debe quedar en el tanque.
    QGN_BOG_ca: NReal; // BOG [m3/s] constante
    QGN_BOG_cb: NReal; // BOG [1/s] factor aplicable al volumen almacenado
    // QGNL_BOG:= ( BOG_ca + BOG_cb * X_GNL )/ 600
    QGN_Max: NReal; // [m3/s] máximo de Senout de la regasificadora.
    QGN_ConsumosPropios_ca: NReal;
    // [m3/s] Consumos propios parte fija independiente del SendOut.  (ej. 0.67 m3/s para la RegasUy)
    QGN_ConsumosPropios_cb: NReal;
    // p.u. Parte variable dependiente del SendOut.  (ej. 1.76E-3 para la RegasUy)

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


    renRegas: NReal;
    // [p.u.] Rendimiento de regasificación  GN_m3/GN_GNL; del orden de: (1-1.76E-3)*600

    kborne_idxPrecioCombustible: integer;

    constructor Create(capa: integer; fecha: TFecha; periodicidad: TPeriodicidad;
      xFuente_idxPrecioCombustible: TFuenteAleatoria;
      xBorne_idxPrecioCombustible: string; fd, TMR: NReal;
      VGNL_min, VGNL_max: NReal;
      QGN_BOG_ca, QGN_BOG_cb, QGN_Max, QGN_ConsumosPropios_ca,
      QGN_ConsumosPropios_cb: NReal);


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

    function InfoAd_: string; override;
  end;


  { TRegasificadora }

  TRegasificadora = class(TSuministroCombustible)
  public
    (**************************************************************************)
    (*              A T R I B U T O S   P E R S I S T E N T E S               *)
    (**************************************************************************)

    Agenda: TAgendaCargosGNL;
    //volumen inicial del tanque, antes de empezar la simulacion
    VGNL_inicial: NReal;
    //precio al que voy a vender el GNL en caso de que no entre en la regasificadora
    cvVert: NReal;
    //Nro de discretizaciones del tanque
    Ndiscret: integer;
    // volumen del cargo de GNL [m3]
    VGNL_Cargo: NReal;
    // precio de los cargos Spot
    precio_Spot: NReal;
    // precio de los cargos Desviados
    precio_Desvio: NREal;
    // precio de los cargos de Agenda
    precio_Agenda: NReal;
    // pasos_preaviso (6 para test).
    PASOS_X_SpotYDevios: integer;
    X_SpotYDesvios_inicial: TDAOfBoolean;

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

    // variables
    pa: TFichaRegasificadora;

    //volumenes de GNL al inicio y al final del paso
    X_VGNL, Xs_VGNL: NReal; //Xs es final    10 discretizaciones

    //V de vertimiento
    Vvert: NReal;


    // Representación del estado de las decisiones.
    // El casillero CERO es la acción correspondiente a la semana en curso y que
    // fue tomada nPasosDePreAviso hacia atras.
    X_SpotYDesvios: TDAOfBoolean;   //  NPasos de preaviso Spot y Desvio

    // Vector con casillero TRUE si hay una acción (Compra Spot o Desvío)
    Xs_SpotYDesvios: TDAOfBoolean;
    // Vector al final del paso. Es un Shift del X más la decisión del paso.


    // variables auxiliares
    //    VGNL_minaux: NReal;
    //    VGNL_maxaux: NReal;

    kCasilleroAgenda: integer; // -1 indica que está fuera de la agenda especificada
    flg_CargoAgendado: boolean; // Indica si en la Agenda hay un cargo para esta semana.
    flg_Desvio: boolean; // en esta semana se produce un desvió de un cargo de Agenda
    flg_Spot: boolean; // en esta semana

    VGNL_Descargado, VGNL_Desviado: NReal; // Descargas de GNL en el paso de tiempo
    CostoCargo: NReal; // Costo directo por Descarga y/o Desvio imputable al paso.
    cv_GNL: NReal; // Valor futuro del GNL Almacenado
    dCF_Accion: NReal; // Valor del futuro por tomar una acción NPasosPrevisoAntes.

    QGN_BOG, VGNL_BOG: NReal;
    // Caudal de Boil Off Gas (BOG) GN y Volumen equivalente de GNL en el paso
    VGNL_ConsumosPropios: NReal;

    datos: TDatosHorariosDetallados;


    constructor Create(capa: integer; nombre: string; nacimiento, muerte: TFecha;
      lpdUnidades: TFichasLPD; lpd: TFichasLPD; NodoSumComb: TNodoCombustible;
      Agenda: TAgendaCargosGNL; VGNL_inicial: NReal; Ndiscret: integer;
      cvVert, VGNL_Cargo, precio_Spot, precio_Desvio, precio_Agenda: NReal;
      PASOS_X_SpotYDevios: integer; xFuenteIdxP: TFuenteAleatoria; xBorneIdxP: string);


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

    procedure PubliVars; override;

    class function DescClase: string; override;

    procedure Sim_Cronica_Inicio; override;

    procedure optx_RegistrarVariablesDeEstado(adminEstados: TAdminEstados); override;
    procedure EvolucionarEstado; override;

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

    procedure PrepararPaso_ps; override;

    procedure optx_nvxs(var ixr, ixd, {%H-}iauxr, {%H-}iauxd: integer); override;

    procedure ActualizarEstadoGlobal(flg_Xs: boolean); override;
    procedure PosicionarseEnEstrellita; override;

    procedure opt_nvers(var ivar, {%H-}ivae, ires: integer); override;
    procedure opt_cargue(s: TSimplex); override;
    procedure opt_fijarRestriccionesDeCaja(s: TSimplex); override;
    procedure opt_leerSolucion(s: TSimplex); override;

    procedure CambioFichaPD; override;


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



    destructor destroy; override;

    function X_SpotYDesvios_Inicial_AsBoolStr(nbinsPorLinea: integer): string;
    procedure Set_X_SpotYDesvios_inicial_FromBoolStr(XBoolStr: string);

    procedure InicioSim; override;
    procedure InicioOpt; override;

  end;


function ceros_str(n: integer): string;

procedure TRegasificadora_CambioFichaPD(actor: TCosa);

procedure AlInicio;
procedure ALFinal;

implementation

function ceros_str(n: integer): string;
var
  k: integer;
  s: string;
begin
  setlength(s, n);
  for k := 1 to n do
    s[k] := '0';
  Result := s;
end;


procedure TRegasificadora_CambioFichaPD(actor: TCosa);
begin
  (actor as TRegasificadora).CambioFichaPD;
end;

// Métodos de TFichaRegasificadora

constructor TFichaRegasificadora.Create(capa: integer; fecha: TFecha;
  periodicidad: TPeriodicidad; xFuente_idxPrecioCombustible: TFuenteAleatoria;
  xBorne_idxPrecioCombustible: string; fd, TMR: NReal; VGNL_min, VGNL_max: NReal;
  QGN_BOG_ca, QGN_BOG_cb, QGN_Max, QGN_ConsumosPropios_ca,
  QGN_ConsumosPropios_cb: NReal);

begin
  inherited Create(capa, fecha, periodicidad);
  self.fd := fd;
  self.TMR := TMR;
  self.fuente_idxPrecioCombustible := xFuente_idxPrecioCombustible;
  self.borne_idxPrecioCombustible := xBorne_idxPrecioCombustible;
  self.VGNL_min := VGNL_min;
  self.VGNL_max := VGNL_max;
  self.QGN_BOG_ca := QGN_BOG_CA;
  self.QGN_BOG_cb := QGN_BOG_cb;
  self.QGN_Max := QGN_Max;
  self.QGN_ConsumosPropios_ca := QGN_ConsumosPropios_ca;
  self.QGN_ConsumosPropios_cb := QGN_ConsumosPropios_cb;
  self.renRegas := (1 - QGN_ConsumosPropios_cb);
end;

function TFichaRegasificadora.Rec: TCosa_RecLnk;
begin
  Result := inherited Rec;
  Result.addCampoDef('fd', fd);
  Result.addCampoDef('TMR', TMR);
  Result.addCampoDef_ref('fuente_idxPrecioCombustible',
    TCosa(fuente_idxPrecioCombustible), self);
  Result.addCampoDef('borne_idxPrecioCombustible', borne_idxPrecioCombustible);
  Result.addCampoDef('VGNL_min', VGNL_min);
  Result.addCampoDef('VGNL_max', VGNL_max);
  Result.addCampoDef('QGN_BOG_ca', QGN_BOG_ca);
  Result.addCampoDef('QGN_BOG_cb', QGN_BOG_cb);
  Result.addCampoDef('renRegas', renRegas, 0, 122);
  Result.addCampoDef('QGN_Max', QGN_Max);
  Result.addCampoDef('QGN_ConsumosPropios', QGN_ConsumosPropios_ca, 0, 122);
  Result.addCampoDef('QGN_ConsumosPropios_ca', QGN_ConsumosPropios_ca, 122);
  Result.addCampoDef('QGN_ConsumosPropios_cb', QGN_ConsumosPropios_cb, 122);
end;

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

procedure TFichaRegasificadora.AfterRead(f: TArchiTexto);
begin
  inherited AfterRead(f);
  if f.Version < 122 then
  begin
    QGN_ConsumosPropios_ca := 0.67;
    QGN_ConsumosPropios_cb := 1.76E-3;
  end;
  renRegas := 1 - QGN_ConsumosPropios_cb;
end;

function TFichaRegasificadora.InfoAd_: string;
begin
  Result := 'QGN_Max: ' + FloatToStr(QGN_Max) + ', VGNL_max:' + FloatToStr(VGNL_max);
end;


//--------------------------
// Métodos de TRegasificadora
//==========================

constructor TRegasificadora.Create(capa: integer; nombre: string;
  nacimiento, muerte: TFecha; lpdUnidades: TFichasLPD; lpd: TFichasLPD;
  NodoSumComb: TNodoCombustible; Agenda: TAgendaCargosGNL; VGNL_inicial: NReal;
  //volumen inicial del tanque, antes de empezar la simulacion
  Ndiscret: integer;   //Nro de discretizaciones del tanque
  cvVert, VGNL_Cargo, precio_Spot, precio_Desvio, precio_Agenda: NReal;
  PASOS_X_SpotYDevios: integer; xFuenteIdxP: TFuenteAleatoria; xBorneIdxP: string);
begin
  inherited Create(capa, nombre, nacimiento, muerte, lpdUnidades, NodoSumComb,
    xFuenteIdxP, xBorneIdxP);
  self.lpd := lpd;
  self.Agenda := Agenda;
  self.VGNL_inicial := VGNL_inicial;
  self.Ndiscret := Ndiscret;
  self.cvVert := cvVert;
  self.VGNL_Cargo := VGNL_Cargo;
  self.precio_Spot := precio_Spot;
  self.precio_Desvio := precio_Desvio;
  self.precio_Agenda := precio_Agenda;
  self.PASOS_X_SpotYDevios := PASOS_X_SpotYDevios;
  setlength(X_SpotYDesvios_inicial, PASOS_X_SpotYDevios);
end;

function TRegasificadora.Rec: TCosa_RecLnk;
begin
  Result := inherited Rec;
  Result.addCampoDef('Agenda', TCosa(agenda));
  Result.addCampoDef('VGNL_inicial', VGNL_inicial);
  Result.addCampoDef('cvVert', cvVert);
  Result.addCampoDef('Ndiscret', Ndiscret);
  Result.addCampoDef('VGNL_Cargo', VGNL_Cargo, 117, 0, '145000');
  Result.addCampoDef('precio_Spot', precio_Spot, 117, 0, '18');
  Result.addCampoDef('precio_Desvio', precio_Desvio, 117, 0, '7');
  Result.addCampoDef('precio_Agenda', precio_Agenda, 117, 0, '13');
  Result.addCampoDef('PASOS_X_SpotYDevios', PASOS_X_SpotYDevios, 119);
  Result.addCampoDef('X_SpotYDesvios_inicial', X_SpotYDesvios_inicial);
  Result.addCampoDef('lpd', TCosa(lpd));
end;

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

procedure TRegasificadora.AfterRead(f: TArchiTexto);
begin
  inherited AfterRead(f);
  if f.version < 119 then
    PASOS_X_SpotYDevios := length(X_SpotYDesvios_inicial);

  setlength(X_SpotYDesvios_inicial, PASOS_X_SpotYDevios);
  lpd.Propietario := self;
end;


procedure TRegasificadora.PubliVars;
begin
  inherited PubliVars;
  PublicarVariableNR('X_VGNL', '[m3]', 6, 2, X_VGNL, True);
  PublicarVariableNR('Vvert', '[m3]', 6, 2, Vvert, True);
  PublicarVariableNR('VGNL_BOG', '[m3]', 6, 2, VGNL_BOG, True);
  PublicarVariableNR('VGNL_Descargado', '[m3]', 6, 2, VGNL_Descargado, True);
  PublicarVariableNR('VGNL_Desviado', '[m3]', 6, 2, VGNL_Desviado, True);
  PublicarVariableNR('cv_GNL', '[USD/m3]', 6, 2, cv_GNL, True);
  PublicarVariableNR('CostoCargo', '[USD]', 6, 2, CostoCargo, True);
  PublicarVariableNR('dCF_Accion', '[USD]', 6, 2, dCF_Accion, True);
end;




procedure TRegasificadora.PrepararMemoria(Catalogo: TCatalogoReferencias;
  globs: TGlobs);
begin
  inherited PrepararMemoria(Catalogo, globs);
  setlength(X_SpotYDesvios, PASOS_X_SpotYDevios);
  setlength(Xs_SpotYDesvios, PASOS_X_SpotYDevios);
end;

procedure TRegasificadora.InicioSim;
begin
  agenda.PrepararHorizonte_(globs.fechaIniSim, globs.HorasDelPaso);
end;

procedure TRegasificadora.InicioOpt;
begin
  agenda.PrepararHorizonte_(globs.fechaIniOpt, globs.HorasDelPaso);
end;


class function TRegasificadora.DescClase: string;
begin
  Result := rsRegasificadora;
end;

procedure TRegasificadora.RegistrarParametrosDinamicos(Catalogo: TCatalogoReferencias);
var
  i: integer;
  ficha: TFichaRegasificadora;

begin
  inherited registrarParametrosDinamicos(Catalogo);
  lpd.expandirFichas(Catalogo, globs);
  lpd.RegistrarFichasAActualizar(Self, globs.ActualizadorLPD, @pA,
    nil, TRegasificadora_CambioFichaPD);

  for i := 0 to lpd.Count - 1 do
  begin
    ficha := lpd[0] as TFichaRegasificadora;
    if ficha.fuente_idxPrecioCombustible <> nil then
      ficha.kborne_idxPrecioCombustible :=
        ficha.fuente_idxPrecioCombustible.IdBorne(ficha.borne_idxPrecioCombustible);
  end;
  pa := lpd[0] as TFichaRegasificadora;
  CambioFichaPD;
end;



procedure TRegasificadora.optx_RegistrarVariablesDeEstado(adminEstados: TAdminEstados);
var
  k: integer;
begin
  for k := high(Xs_SpotYDesvios) downto 0 do
    adminEstados.Registrar_Discreta(ixd + k, 2, nombre + '_SpotYDesvio_' +
      IntToStr(high(Xs_SpotYDesvios) - k), 'bool');

  adminEstados.Registrar_Continua(
    ixr,
    pa.VGNL_min,
    pa.VGNL_max,
    self.NDiscret,
    nombre + '_VolGNL', // nombre de la variable
    'm3' // unidades
    );
end;


procedure TRegasificadora.Sim_Cronica_Inicio;
begin
  X_VGNL := self.VGNL_inicial;
  Xs_VGNL := X_VGNL;
  X_SpotYDesvios := copy(X_SpotYDesvios_inicial);
  setlength(X_SpotYDesvios, PASOS_X_SpotYDevios);
end;

procedure TRegasificadora.Set_X_SpotYDesvios_inicial_FromBoolStr(XBoolStr: string);
var
  k, n: integer;
  cnt: integer;
begin
  cnt := 0;
  for k := 0 to high(X_SpotYDesvios_Inicial) do
    X_SpotYDesvios_Inicial[k] := False;

  n := length(X_SpotYDesvios_Inicial);
  k := 1;
  while (cnt < n) and (k <= length(XBoolStr)) do
  begin
    if (XBoolStr[k] = '0') then
    begin
      X_SpotYDesvios_Inicial[cnt] := False;
      Inc(cnt);
    end
    else if (XBoolStr[k] = '1') then
    begin
      X_SpotYDesvios_Inicial[cnt] := True;
      Inc(cnt);
    end;
    Inc(k);
  end;
end;


procedure TRegasificadora.opt_nvers(var ivar, ivae, ires: integer);
begin
  self.ivar := ivar;
  Inc(ivar, globs.npostes + 2); //una ivar para cada Q por poste,
  // una para el estado Xs_VGNL y una para el Vvert.

  //la primer restricción es la ecuación de evolución del estado: f(X,u,r) - Xs_VGNL = 0
  // la segunda restricción impone la evacuacion del BOG
  self.ires := ires;
  Inc(ires, 2);
end;


procedure TRegasificadora.PrepararPaso_ps;
var
  dCFdX_inc, dCFdX_dec: NReal;
  VGNL_Margen: NReal;
  rescod: integer;
  xrpos: NReal;
  dCFdX: NReal;
  k: integer;

begin

  // Ahora derivamos la función de CF(X, k+1) respecto al volumen almacenado
  globs.CF.devxr_continuo(ixr, globs.kPaso_Opt + 1, dCFdX_inc,
    dCFdX_dec, rescod, xrpos);

  case rescod of
    -1: dCFdX := dCFdX_inc;
    0: dCFdX := (dCFdX_dec + dCFdX_inc) / 2.0;
      //1:
    else
      dCFdX := dCFdX_dec;
  end;

  // El valor del GNL almacenado es menos la derivada del costo futuro respecto
  // al volumen almacenado y por el factor de actualización del paso.
  cv_GNL := -dCFdX * globs.fActPaso;


  // Consultamos si en La Agenda (original) hay un cargo agendado para
  // este paso.
  kCasilleroAgenda := Agenda.kCasilleroAgenda(globs.kPaso_Opt);
  flg_CargoAgendado := Agenda.HayCargoAgendado(kCasilleroAgenda);


  agenda.CalcularCostos_(NodoComb.combustible.pa.ro * factor_GNL_a_GN,
    NodoComb.combustible.pa.PCS);

  // Ahora según la tabla de deciciones anteriores que afectan este Casillero
  // determinamos si arriva o no un cargo si el mismo es de Agenda o Spot y si
  // hay o no desvió de un cargo.
  if flg_CargoAgendado then
  begin
    flg_Spot := False;
    // Hay un cargo en la Agenda por lo cual, si en la tabla de decisiones hay una
    // acción para este casillero, se trata de DESVIAR el cargo
    if (X_SpotYDesvios[0]) then
    begin  // El cargo fue desviado
      flg_Desvio := True;
      CostoCargo := Agenda.CostoDesvio_USD;
      VGNL_Descargado := 0;
      VGNL_Desviado := Agenda.VGNL_m3;
    end
    else
    begin // El cargo es descargado en la regasificadora
      flg_Desvio := False;
      CostoCargo := Agenda.CostoAgenda_USD;
      VGNL_Descargado := Agenda.VGNL_m3;
      VGNL_Desviado := 0;
    end;
  end
  else
  begin
    flg_Desvio := False;
    // NO hay un cargo Agendado, por lo cual, si en la tabla de decisiones hay una
    // acción para esta semana, se trata de una compra Spot. Sino no hay descarga.
    if (X_SpotYDesvios[0]) then
    begin   // Se descarga un cargo Spot
      flg_Spot := True;
      CostoCargo := Agenda.CostoSpot_USD;
      VGNL_Descargado := Agenda.VGNL_m3;
      VGNL_Desviado := 0;
    end
    else
    begin   // Semana sin descargas ni desvios
      flg_Spot := False;
      CostoCargo := 0;
      VGNL_Descargado := 0;
      VGNL_Desviado := 0;
    end;
  end;

  if pa.fuente_idxPrecioCombustible <> nil then
    CostoCargo := CostoCargo *
      pa.fuente_idxPrecioCombustible.Bornera[pa.kborne_idxPrecioCombustible];


  // Cálculo del BOIL OFF
  QGN_BOG := pa.QGN_BOG_ca + pa.QGN_BOG_cb * X_VGNL;
  VGNL_BOG := (QGN_BOG * globs.SegundosDelPaso) / factor_GNL_a_GN;

  //Si lo que queda de GNL es menor que el V_min no quiero sacar mas, para eso aumento el precio
  if (X_VGNL <= pa.VGNL_min) then
    cv_GNL := 10 * cv_GNL;      { TODO : -- revisar criterio ... }

  // Caudal de Boil Off Gas (BOG) y Volumen en el paso

  VGNL_margen := (X_VGNL + VGNL_Descargado - VGNL_BOG) - pa.VGNL_min * 1.05;
  if VGNL_margen < 0 then
    VGNL_BOG := max(VGNL_BOG + VGNL_margen, 0);

  VGNL_ConsumosPropios := pa.QGN_ConsumosPropios_ca * globs.SegundosDelPaso /
    factor_GNL_a_GN;

  VGNL_margen := (X_VGNL + VGNL_Descargado - VGNL_ConsumosPropios) - pa.VGNL_min * 1.05;
  if VGNL_margen < 0 then
    VGNL_ConsumosPropios := max(VGNL_ConsumosPropios + VGNL_margen, 0);

  // Cálculo de la afectación del futuro por decidir cambiar lo agendado para dentro de NPreavisoSemanas
  dCF_accion := globs.CF.deltaCosto_vxd_continuo(ixd, globs.kPaso_Opt + 1, 1) *
    globs.fActPaso;

  for k := 1 to high(X_SpotYDesvios) do
    Xs_SpotYDesvios[k - 1] := X_SpotYDesvios[k];

  //  if dCF_Accion < - (Agenda.CostoSpot_USD * power( globs.fActPaso, 6 )) then
  if dCF_Accion < 0 then
    Xs_SpotYDesvios[high(Xs_SpotYDesvios)] := True
  else
    Xs_SpotYDesvios[high(Xs_SpotYDesvios)] := False;

end;

procedure TRegasificadora.opt_cargue(s: TSimplex);
var
  iresnodo: integer;
  kposte: integer;

begin
  iresnodo := self.NodoComb.ires;
  for kposte := 0 to globs.npostes - 1 do
  begin
    // En cada restricción del nodo_combustible aportamos
    // el caudal despachado.
    s.pon_e(iresnodo + kposte, ivar + kposte, 1);

    // Consideramos las extracciones asociadas a los caudales despachados
    // en la restricción dinámica f(X, u, r ) - Xs = 0
    // ( Aportes - SUM(Qi*durposi/ 610)  - Xs_VGNL + (X_VGNL - Vvert +VGNL_Descargado -VGNL_ConsumosPropios) = 0)
    s.pon_e(ires, ivar + kposte, -globs.DurPos[kposte] * 3600 /
      factor_GNL_a_GN / pa.renRegas);
  end;

  //cargar termino de Xs_VGNL
  s.pon_e(ires, ivar + globs.npostes + 0, -1);

  //cargar termino de Vvert
  s.pon_e(ires, ivar + globs.npostes + 1, -1);

  //cargar termino constante
  s.pon_e(ires, s.nc, self.X_VGNL + VGNL_Descargado - VGNL_ConsumosPropios);

  // Restricción adicional que controla que los USOS sean >= que le BOG
  // Cargado de la restricción SUM( Qi*durposi/ 610 ) + VVert +VGNL_ConsumosPropios - VGNL_BOG >=0
  for kposte := 0 to globs.npostes - 1 do
  begin
    s.pon_e(ires + 1, ivar + kposte, -s.e(ires, ivar + kposte));
  end;
  s.pon_e(ires + 1, ivar + globs.npostes + 1, 1);
  s.pon_e(ires + 1, s.nc, VGNL_ConsumosPropios - VGNL_BOG);
  { TODO : ojo revisar ,, que pasa si hay vertimiento porque no entra el cargo en la regasificador cómo considero el BOG en ese caso Capaz que no importa pues se pierde igual }

  // cargar funcion objetivo -costo (derivada de CF)    -dCF = cv_GNL  * (Xs-X)
  s.pon_e(s.nf, ivar + globs.npostes + 0, cv_GNL);
  s.acum_e(s.nf, s.nc, -cv_GNL);

  // cargar funcion ingreso por ventas de vertimiento
  // mmm ... no vamos a poner que el vertimiento sea un costo no deseado
  //  s.pon_e(s.nf, ivar + globs.npostes + 1, cvVert);
  s.pon_e(s.nf, ivar + globs.npostes + 1, -(max(cv_GNL + 1000, 1000)));

  // cargar funcion objetivo -costoCargo (Agenda, Spot o Agenda-Desvio )
  s.acum_e(s.nf, s.nc, -CostoCargo);

end;




procedure TRegasificadora.opt_fijarRestriccionesDeCaja(s: TSimplex);
var
  kposte: integer;
  resto: NReal;
begin
  s.FijarRestriccionIgualdad(ires);

  for kposte := 0 to globs.npostes - 1 do
    s.cota_sup_set(ivar + kposte, pa.QGN_Max);

  s.cota_inf_set(ivar + globs.npostes, pa.VGNL_min);//  Xs_VGNL >= VGNL_min
  s.cota_sup_set(ivar + globs.npostes, pa.VGNL_max);//  Xs_VGNL <= VGNL_max

  {$IFNDEF ADMITO_VENDER_VERTIMIENTO}
  resto := (VGNL_Descargado + X_VGNL - pa.VGNL_max);
  if resto > AsumaCero then
    s.cota_sup_set(ivar + globs.npostes + 1, resto * 1.03)
  else
    //    s.FijarVariable(ivar + globs.npostes+1, 0.0 );
    // rch@201503270950 hay que permitir vertimiento por si nadie consume y hay que
    // evacuar el BOG
    s.cota_sup_set(ivar + globs.npostes + 1, VGNL_BOG * 1.03);
  {$ELSE}
  resto := (VGNL_Descargado + X_VGNL - pa.VGNL_min);
  s.cota_sup_set(ivar + globs.npostes + 1, resto * 0.9);
  {$ENDIF}
end;


procedure TRegasificadora.EvolucionarEstado;

begin
  X_VGNL := Xs_VGNL;
  vswap(X_SpotYDesvios, Xs_SpotYDesvios);
end;

procedure TRegasificadora.optx_nvxs(var ixr, ixd, iauxr, iauxd: integer);
begin
  self.ixr := ixr;
  ixr := ixr + 1;
  self.ixd := ixd;
  Inc(ixd, length(X_SpotYDesvios));
end;

procedure TRegasificadora.PosicionarseEnEstrellita;
var
  iRetardo: integer;

begin
  X_VGNL := globs.CF.xr[ixr];
  for iRetardo := 0 to high(X_SpotYDesvios) do
    X_SpotYDesvios[iRetardo] :=
      (globs.CF.xd[ixd + high(X_SpotYDesvios) - iRetardo]) <> 0;
end;



procedure TRegasificadora.ActualizarEstadoGlobal(flg_Xs: boolean);
var
  iRetardo: integer;
  flg_CargoAgendado: boolean;
begin
  if flg_Xs then
  begin
    for iRetardo := 0 to high(X_SpotYDesvios) - 1 do
    begin
      if X_SpotYDesvios[iRetardo + 1] then
        globs.CF.xd[ixd + high(X_SpotYDesvios) - iRetardo] := 1
      else
        globs.CF.xd[ixd + high(X_SpotYDesvios) - iRetardo] := 0;
    end;
    globs.CF.xd[ixd] := 0;
    //    globs.CF.xr[ixr] := Xs_VGNL;
    // le pongo kPaso_Sim porque funciona tanto para Opt como para Sim.
    flg_CargoAgendado := Agenda.HayCargoAgendado(
      Agenda.kCasilleroAgenda(globs.kPaso_Sim));
    if flg_CargoAgendado xor X_SpotYDesvios[0] then
      globs.CF.xr[ixr] := X_VGNL + agenda.VGNL_m3
    else
      globs.CF.xr[ixr] := X_VGNL;
  end
  else
  begin
    for iRetardo := 0 to high(X_SpotYDesvios) do
    begin
      if X_SpotYDesvios[iRetardo] then
        globs.CF.xd[ixd + high(X_SpotYDesvios) - iRetardo] := 1
      else
        globs.CF.xd[ixd + high(X_SpotYDesvios) - iRetardo] := 0;
    end;
    globs.CF.xr[ixr] := X_VGNL;
  end;

end;


procedure TRegasificadora.opt_leerSolucion(s: TSimplex);
var
  kposte: integer;
  VGN_InyectadoALaRed: NREal;
begin
  VGN_InyectadoALaRed := 0;
  for kposte := 0 to globs.npostes - 1 do
  begin
    Q[kposte] := s.xval(ivar + kposte);
    VGN_InyectadoALaRed := VGN_InyectadoALaRed + Q[kposte] * globs.DurPos[kposte] * 3600;
  end;
  self.Xs_VGNL := s.xval(ivar + globs.NPostes + 0);
  self.Vvert := s.xval(ivar + globs.NPostes + 1);
  CostoDirectoDelPaso := costoCargo;
end;

function TRegasificadora.X_SpotYDesvios_Inicial_AsBoolStr(nbinsPorLinea:
  integer): string;
var
  col, k: integer;
  res: string;
begin
  res := '';
  col := 0;
  for k := 0 to high(X_SpotYDesvios_inicial) do
  begin
    if X_SpotYDesvios_inicial[k] then
      res := res + '1'
    else
      res := res + '0';
    Inc(col);
    if col = nbinsPorLinea then
    begin
      res := res + #13#10;
      col := 0;
    end;
  end;
  Result := res;
end;




procedure TRegasificadora.CambioFichaPD;
begin
(*
   precio_USD_por_Q1h:= NodoComb.combustible.precio_USD_por_MWh_a_PCI(
    pa.precio_por_unidad, pa.unidades_del_precio, pa.flg_precio_a_PCS) * NodoComb.combustible.MWh_por_Q1h;
*)
end;


function TRegasificadora.getNombreVar(ivar: integer; var nombre: string): boolean;
var
  jvar: integer;
begin

  jvar := ivar - self.ivar;
  if (jvar >= 0) and (jvar < (globs.NPostes + 2)) then
  begin
    if jvar < globs.NPostes then
      nombre := self.Nombre + '_Q[m3/s]' + IntToStr(jvar + 1)
    else
    begin
      jvar := jvar - globs.NPostes;
      case jvar of
        0: nombre := self.Nombre + '_Xs_VGNL[m3]';
        1: nombre := self.Nombre + '_VVertGNL[m3]';
      end;
    end;
    Result := True;
  end
  else
    Result := False;
end;


function TRegasificadora.getNombreRes(ires: integer; var nombre: string): boolean;
var
  jRes: integer;
begin
  jRes := ires - self.ires;
  if (jres >= 0) and (jres < 2) then
  begin
    case jRes of
      0: nombre := self.nombre + '_DYN';
      1: nombre := self.nombre + '_BOG';
    end;
    Result := True;
  end
  else
    Result := False;
end;





destructor TRegasificadora.destroy;
begin
  setlength(X_SpotYDesvios, 0);
  setlength(Xs_SpotYDesvios, 0);
  inherited destroy;
end;

procedure AlInicio;
begin
  registrarClaseDeCosa(TRegasificadora.ClassName,
    TRegasificadora);
  registrarClaseDeCosa(TFichaRegasificadora.ClassName,
    TFichaRegasificadora);
end;

procedure AlFinal;
begin
end;

end.
