unit uoddface_OptimA;

{rch@201409141237
Definición del problema de Optimización de Agendas de GNL
}


{$mode delphi}

interface

uses
  Classes, SysUtils,
  uoddface,
  Math,
  xmatdefs,
  matreal,
  uauxiliares,
  uactores,
  ufuentesintetizador,
  ufechas,
  usalasdejuego,
  uDataSetGenerico,
  urosx,
  uInicioYFinal,
  uregasificadora,
  uconstantessimsee;

type
  TOptimA_infoProblema = class
    ActorRegas: string; // Nombre del Actor Regasificadora  que tiene definida la agenda
    LargoAgenda: integer; // cantidad de semanas de la agenda
    class function ReadFromRec(var r: TDataRecord;
      tmp_rundir_: string): TOptimA_infoProblema;
    class function ReadFromDB(dbconx: TDBrosxCon; nid_problema: integer;
      tmp_rundir_: string): TOptimA_infoProblema;
    procedure Free;
  end;


  TSimCostos_archi = class
    tasa: NReal; // [p.u.] tasa de descuento en por unidad

    cad_VE: NReal; // [MUSD] valor esperado del CAD
    cad_VaR5pe: NReal; // [MUSD] VaR(5%) del CAD
    cad_VE_aux: NReal; // [MUSD] valor esperado del CAD considerando CF_aux al final
    cad_VaR5pe_aux: NReal; // [MUSD] VaR(5%) del CAD considerando CF_aux al final
    vcad: TDAOfNReal; // [MUSD] vector con los CAD de cada crónica
    vcad_aux: TDAOfNReal;
    // [MUSD] vector de los CAD de cada crónica, considerando CF_aux al final
    vcdp: TDAOfNReal;
    // [MUSD] vector con los costos directos del paso (CDP) acumulados de cada crónica.
    vcffja: TDAOfNReal;
    // [MUSD] vector con el Costo Futuro de Fin de Juego Actualizado de cad crónica.
    vcffja_aux: TDAOfNReal;
    // [MUSD] vector con el Costo Futuro (auxiliar) de Fin de Juego Actualizado de cad crónica.

    constructor CreateRead(archi_res: string; NCronicas: integer);
    procedure Free;

  end;


  TOptimA_Problema = class(TProblema)
    CarpetaSala: string;
    NombreSala: string;

    InfoProblema: TOptimA_infoProblema;

    constructor Create(dbconx: TDBrosxCon; recProblema: TDataRecord;
      idEjecutor_: integer; tmp_rundir_: string);
    class function CreateFromDB(dbconx: TDBrosxCon; nid_problema: integer;
      idEjecutor_: integer; tmp_rundir_: string): TOptimA_Problema;
    function evaluar_(Individuo: TIndividuo; SemillaAleatoria: integer): boolean;
      override;
    destructor destroy; override;

    function RunOptSim(sala: string;
      NCronicas, semillaAleatoria_: integer): TSimCostos_archi;

    // Crea la sala y retorna el camino completo a la misma.
    // En la variable MontoInversiones retorna el valor a sumar al CAD
    // para considerar el monto de inversiones al Inicio del a corrida.
    // En la variable EsFactible indica que la sala resultante está dentro de las factibles.
    function CrearSalaSimSEE(individuo: TIndividuo; var MontoInversiones: NReal;
      out EsFactible: boolean): string;

    function BajarCrearSalaSimSEE(nid: integer; var MontoInversiones: NReal): string;

  end;




implementation




constructor TOptimA_Problema.Create(
  dbconx: TDBrosxCon;
  recProblema: TDataRecord;
  idEjecutor_: integer; tmp_rundir_: string);

var
  kPar: integer;
  nf, nc: integer;
  npars: integer;

begin
  inherited Create(dbconx, recProblema, idEjecutor_, tmp_rundir_);

  // calculamos carpeta de la sala y nombre
  //  CarpetaSala := extractFilePath( ArchiSala );
  CarpetaSala := tmp_rundir_;
  NombreSala := extractFileName(ArchiSala);
  // acomodamos el directorio de la sala
  ArchiSala := tmp_rundir_ + DirectorySeparator + NombreSala;
  if (pos('.ese', NombreSala) > 0) then
    Delete(NombreSala, pos('.ese', NombreSala), length(NombreSala) -
      pos('.ese', NombreSala) + 1);


  // cargamos la informa
  InfoProblema := TOptimA_infoProblema.ReadFromDB(dbconx, nid_Problema, tmp_rundir);


  // Bien, ahora que tenemos la información del problema  creamos
  // los descriptores de ADN

  setlength(DescriptoresE, InfoProblema.LargoAgenda );
  setlength(DescriptoresR, 0 );

  for kPar := 0 to high(DescriptoresE) do
    DefinirParametroEntero( kPar, 'a_'+IntToStr(kPar), 0, 1 );
  CalcularLargosADN;
end;

class function TOptimA_Problema.CreateFromDB(
  dbconx: TDBrosxCon;
  nid_problema: integer;
  idEjecutor_: integer; tmp_rundir_: string): TOptimA_Problema;
var
  ds: TResultadoQuery;
  r: TDataRecord;
  ap: TOptimA_Problema;
begin
  ds := dbconx.sql_query('SELECT * FROM ofe_problemas WHERE nid = ' + IntToStr(
    nid_problema) );
  ds.First;
  r := ds.getFilaActiva;
  if r <> nil then
    ap := TOptimA_Problema.Create(dbconx, r, idEjecutor_, tmp_rundir_)
  else
    ap := nil;
  ds.Free;
  Result := ap;
end;


function nextpal_sep(var r: string; sep: string): string;
var
  res: string;
  i: integer;
begin
  r := trim(r);
  i := pos(sep, r);
  if i > 0 then
  begin
    res := trim(copy(r, 1, i - 1));
    Delete(r, 1, i);
  end
  else
  begin
    res := r;
    r := '';
  end;
  Result := res;
end;


function nextpal_tab(var r: string): string;
begin
  Result := nextpal_sep(r, #9);
end;


procedure readln_vect(var f: textfile; var v: TDAOfNreal; N: integer);
var
  r: string;
  pal: string;
  k: integer;
begin
  setlength(v, N);
  system.readln(f, r);
  pal := nextpal_tab(r); // caption
  for k := 0 to high(v) do
  begin
    pal := nextpal_tab(r);
    v[k] := StrToFloat(pal);
  end;
end;



class function TOptimA_infoProblema.ReadFromRec(var r: TDataRecord;
  tmp_rundir_: string): TOptimA_infoProblema;
var
  res: TOptimA_infoProblema;
begin
  res := TOptimA_infoProblema.Create;
  res.ActorRegas := r.GetByNameAsString('regasificadora');
  res.LargoAgenda:= r.GetByNameAsInt( 'largo');
  Result := res;
end;



procedure TOptimA_infoProblema.Free;
begin
  inherited Free;
end;



class function TOptimA_infoProblema.ReadFromDB(
  dbconx: TDBrosxCon;
  nid_problema: integer;
  tmp_rundir_: string): TOptimA_infoProblema;
var
  res: TOptimA_infoProblema;
  sql: string;
  rec: TDataRecord;

begin
  res := nil;
  sql := 'SELECT * FROM ofe_OptimA WHERE nid_problema = ' +
    IntToStr(NID_Problema);
  rec := dbconx.sql_ficha(sql);
  if rec <> nil then
  begin
    res := TOptimA_infoProblema.ReadFromRec( rec, tmp_rundir_ );
    rec.Free;
  end
  else
    raise Exception.Create( 'Error no pude encontrar el registro del problema: '+IntToStr( nid_problema )+' en la tabla: ofe_OptimA. Debe editar la información del problema y completar la información adicional.' );
  Result := res;
end;




constructor TSimCostos_archi.CreateRead(archi_res: string; NCronicas: integer);
var
  f: textfile;
begin
  inherited Create;

  Assign(f, archi_res);
  {$I-}
  reset(f);
  {$I+}
  if ioresult <> 0 then
    raise Exception.Create('No encontré archivo: ' + archi_res);

  system.readln(f, tasa);
  system.readln(f, cad_VE);
  system.readln(f, cad_VaR5pe);
  system.readln(f, cad_VE_aux);
  system.readln(f, cad_VaR5pe_aux);

  readln_vect(f, vcad, NCronicas);
  readln_vect(f, vcad_aux, NCronicas);
  readln_vect(f, vcdp, NCronicas);
  readln_vect(f, vcffja, NCronicas);
  readln_vect(f, vcffja_aux, NCronicas);

  closefile(f);
end;

procedure TSimCostos_archi.Free;
begin
  setlength(vcad, 0);
  setlength(vcad_aux, 0);
  setlength(vcdp, 0);
  setlength(vcffja, 0);
  setlength(vcffja_aux, 0);
  inherited Free;
end;




// retorna NIL si falla algo
function TOptimA_Problema.RunOptSim(sala: string;
  NCronicas, semillaAleatoria_: integer): TSimCostos_archi;
var
  cmd: string;
  params: array of string;
  archi_simcosto: string;

begin

  if tmp_rundir = '' then
    archi_simcosto := getDir_run + NombreSala + DirectorySeparator
  else
    archi_simcosto := getDir_run;

  limpiarCarpeta(archi_simcosto, 'simres_*');
  limpiarCarpeta(archi_simcosto, 'simcosto_*');
  limpiarCarpeta(archi_simcosto, 'estado_fin_cron_*');


  setlength(params, 3);
  params[0] := 'sala=' + sala;
  params[1] := 'ejecutor=' + IntToStr(idEjecutor);
  params[2] := 'semilla=' + IntToStr(semillaAleatoria_);

{$IFDEF LINUX}
  cmd := getDir_bin + 'cmdopt';
{$ELSE}
  cmd := getDir_bin + 'cmdopt.exe';
{$ENDIF}
  if not RunChildAndWAIT(cmd, params) then
    raise Exception.Create('no puede correr la cmdopt');


{$IFDEF LINUX}
  cmd := getDir_bin + 'cmdsim';
{$ELSE}
  cmd := getDir_bin + 'cmdsim.exe';
{$ENDIF}
  setlength(params, 4);
  params[0] := 'sala=' + sala;
  params[1] := 'ejecutor=' + IntToStr(idEjecutor);
  params[2] := 'semilla=' + IntToStr(semillaAleatoria_);
  params[3] := 'NCronicasSim=' + IntToStr(NCronicas);

  if not RunChildAndWAIT(cmd, params) then
    raise Exception.Create('no puede correr la cmdsim');
  setlength(params, 0);





  try
    if tmp_rundir = '' then
      archi_simcosto := getDir_run + NombreSala + DirectorySeparator
    else
      archi_simcosto := getDir_run;

    archi_simcosto := archi_simcosto + DirectorySeparator + 'simcosto_' + IntToStr(
      semillaAleatoria_) + 'x' + IntToStr(NCronicas) + '_base.xlt';

if not FileExists( archi_simcosto ) then
   writeln('OJO .... NO Existe el archivo: ', archi_simcosto);

    Result := TSimCostos_archi.CreateRead(archi_simcosto, NCronicas);
  except
    Result := nil;
    raise Exception.Create('Error al leer archivo de resultados : ' +
      archi_simcosto);
  end;

end;



function TOptimA_Problema.CrearSalaSimSEE(individuo: TIndividuo;
  var MontoInversiones: NReal; out EsFactible: boolean): string;
var
  sala: TSalaDejuego;
  Regasificadora: TRegasificadora;
  qActualizadorDiario: double;

  archiSalaModificada: string;
  MaxUnidadesGeneradorasActivas: integer;
  kTipoUnidad: integer;
  cargos_str: string;
  kPasoAgenda: integer;

begin
  EsFactible := True;
  uInicioYFinal.AlInicio;

  MontoInversiones:= 0;

  // cargamos la sala de juegos_base

  chdir(carpetaSala);
  sala := usalasdejuego.cargarSala(0, archiSala, '__principal__', True);

  setlength( cargos_str, Individuo.XE.n );
  for kPasoAgenda:= 1 to Individuo.XE.n do
    if Individuo.XE.e(kPasoAgenda ) = 1 then
      cargos_str[kPasoAgenda]:= '1'
    else
      cargos_str[kPasoAgenda]:= '0';

  Regasificadora := sala.ListaActores.find( self.InfoProblema.ActorRegas ) as TRegasificadora;
  Regasificadora.Agenda.SetCargos_FromBoolStr( cargos_str );

  archiSalaModificada := getDir_run + DirectorySeparator + self.NombreSala + '_oddface_.ese';
  sala.WriteToArchi(archiSalaModificada);
  sala.Free;

  uInicioYFinal.AlFinal;
  Result := archiSalaModificada;
end;




function TOptimA_Problema.BajarCrearSalaSimSEE(nid: integer;
  var MontoInversiones: NReal): string;
var
  a: TIndividuo;
  EsFactible: boolean;
begin
  a := LeerIndividuo(nid);
  decodificar_adn(a);
  Result := CrearSalaSimSEE(a, MontoInversiones, EsFactible);
end;


function TOptimA_Problema.evaluar_(Individuo: TIndividuo;
  SemillaAleatoria: integer): boolean;
var
  res_SimCostos: TSimCostos_archi;
  archiSalaModificada: string;
  MontoInversiones: double;
  EsFactible: boolean;
begin

  archiSalaModificada := crearSalaSimsee(Individuo, MontoInversiones, EsFactible);
  if EsFactible then
  begin
    res_SimCostos := RunOptSim(archiSalaModificada, NCronicasCronicasPorVez,
      SemillaAleatoria);
    if res_SimCostos <> nil then
    begin
      Individuo.f_histo := TVectR.Create_FromDAofR(res_SimCostos.vcad);
      Individuo.f_histo.MasReal(MontoInversiones);
      Individuo.f_VE := res_SimCostos.cad_VE + MontoInversiones;
      Individuo.f_VaR := Individuo.f_histo.pe_VaR(pe_var);
      Individuo.f_CVaR := Individuo.f_histo.pe_CVaR(pe_var);
      Individuo.f_MIN := individuo.f_histo.e(1);
      Individuo.f_MAX := individuo.f_histo.e(individuo.f_histo.n);
      Individuo.f_Objetivo := ro_VE * Individuo.f_VE + ro_VaR *
        Individuo.f_VaR + ro_CVaR * Individuo.f_CVaR;
      res_SimCostos.Free;
      Result := True;
    end
    else
      raise Exception.Create('Falló la simulación.');
  end
  else
    Result := False;
end;



destructor TOptimA_Problema.destroy;
begin
  infoProblema.Free;
  inherited destroy;
end;

end.
