unit uoddface_pdet;

{$mode delphi}

interface

uses
  Classes, SysUtils,
  uoddface,
  xmatdefs,
  matreal,
  uauxiliares,
  uactores,
  ufechas,
  usalasdejuego,
  uDataSetGenerico,
  uoddface_roscon_cfg,
  urosx,
  uInicioYFinal,
  uconstantessimsee,
  ucontroladordeterminista;


(*

=================================================================
      PDET  Optimizador de Programación Dinámica Determinística.
=================================================================

Se trata de encontrar un juego de gradientes dCF/dX que lleven a la política
óptima en la resolución del problema de programación dinámcia

min ( sum{ ce( X_k, r_k, u_k, k ); k = 1, 2, ... N }  + CF(X_N+1) )
u_k

@ X_k+1 = f( X_k, r_k, u_k, k )
  k = 1, 2, ... , N

En el que la secuencia de entrada r_k es conocida. (por eso el problema es determinísico).

Para ello se debe crear la Sala SimSEE que con el sistema a optimizar.
Utilizando la aplicación oddface_prepare se debe crear el problema de optmización,
subir la sala.

El parámetro de otimización, es el gradiente de CF(X, k ) para las etapas
k = 1, 2, ..., N.
*)


type
  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;


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

    u: TDAofNReal; // auxiliar para llenado de los gradientes.
    controlador: TControladorDeterministico;

    constructor Create( recProblema: TDataRecord; idEjecutor_: integer; tmp_rundir_: string );
    class function CreateFromDB(dbcon: TDBrosxCon; nid_problema: integer;
      idEjecutor_: integer; tmp_rundir_: string): TPDET_Problema;
    function evaluar_( Individuo: TIndividuo; SemillaAleatoria: integer ): boolean; override;
    procedure Free; override;

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

    // Crea la sala y retorna el camino completo a la misma.
     function CrearSalaSimSEE( individuo: TIndividuo ): string;
     function BajarCrearSalaSimSEE( nid: integer ): string;
  end;




implementation


(** Constructor de un problema tipo PDET **)
constructor TPDET_Problema.Create(
    recProblema: TDataRecord; idEjecutor_: integer; tmp_rundir_: string );

var
  controlador: TControladorDeterministico;
  i: Integer;

begin
  // LLamamos al método de la clase ancestral para que se cree lo que se tenga que crear.
  inherited Create(dbcon_rosx,  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 el controlador deterministico para dimensionar el problema.
// y fijar rangos de variación.
controlador:= TControladorDeterministico.Create_LoadFromArchi( tmp_rundir_ + DirectorySeparator + 'controladorDeterminista.bin' );
u:= controlador.create_vect_u;

  // Bien, ahora que tenemos las ordenes  definimos los parámetros
  // Los problemas tienen juegos de parámetros Enteros y Reales.
  // En este caso solo usaremos los reales.
  setlength( DescriptoresE, 0 );
  setlength( DescriptoresR, length( u ) );

   for i:= 0 to high( u ) do
      DefinirParametroReal( i, 'i'+IntToStr(i), 0.5 * u[i], 1.5 * u[i], 16 );

   CalcularLargosADN;
end;


class function TPDET_Problema.CreateFromDB(dbcon: TDBrosxCon; nid_problema: integer;  idEjecutor_: integer; tmp_rundir_: string ): TPDET_Problema;
var
  ds: TResultadoQuery;
  r: TDataRecord;
  ap: TPDET_Problema;
begin
  ds:= dbcon.query( 'SELECT * FROM ofe_problemas WHERE nid = '+IntToStr( nid_problema ) +' LIMIT 1 ');
  r:= ds.first;
  if r <> nil then
    ap:= TPDET_Problema.Create( 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;



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;


function TPDET_Problema.CrearSalaSimSEE( individuo: TIndividuo ): string;
var
  sala: TSalaDejuego;
  kEtapa, kOrden: integer;
  actor: TActor;
  nUnidades: TDAofNInt;
  fecha: TFecha;
  archiSalaModificada: string;

{$IFDEF DEBUG_CrearSalaSimSEE}
  s: string;
{$ENDIF}

begin
  uInicioYFinal.AlInicio;

  // cargamos la sala de juegos_base
  chdir(carpetaSala);
  sala:= TSalaDeJuego.cargarSala( 0, archiSala, '__principal__',  true );

  for kOrden:= 0 to ordenes.lst.count - 1 do
  begin
    orden:= ordenes[kOrden];
    actor:= sala.ListaActores.find( Orden.Nombre ) as TActor;

    {$IFDEF DEBUG_CrearSalaSimSEE}
    s:= IntToStr( kOrden );
    {$ENDIF}

    kEtapa:= Individuo.XE.e( kOrden + 1 );
    fecha:= TFecha.Create_Clone( fecha_primer_etapa );
    fecha.addDias( kEtapa * dias_por_etapa );
    nUnidades[0]:= - Orden.unidades_a_mantener;

    actor.lpdUnidades.delta_unidades_( fecha, nUnidades );

    {$IFDEF DEBUG_CrearSalaSimSEE}
    s:= s+', '+Orden.Nombre+ ',du: '+ IntToStr( Orden.unidades_a_mantener )+', '+fecha.AsISOStr+', '+ IntToStr( nUnidades[0] );
    {$ENDIF}

//    kEtapa := kEtapa + trunc( orden.dias_parada / dias_por_etapa +0.2 );
    fecha:= TFecha.Create_Clone( fecha_primer_etapa );
//    fecha.addDias( kEtapa * dias_por_etapa );
    fecha.addDias( kEtapa * dias_por_etapa +  orden.dias_parada );
    nUnidades[0]:= Orden.unidades_a_mantener;
    actor.lpdUnidades.delta_unidades_( fecha, nUnidades );

    {$IFDEF DEBUG_CrearSalaSimSEE}
     s:= s+', '+fecha.AsISOStr+', '+ IntToStr( nUnidades[0] );
     writeln( s );
    {$ENDIF}
  end;

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

  uInicioYFinal.AlFinal;

  result:=  archiSalaModificada;
end;

function TPDET_Problema.BajarCrearSalaSimSEE( nid: integer ): string;
var
  a: TIndividuo;
begin
  a:= LeerIndividuo( nid );
  decodificar_adn( a );
  result:= CrearSalaSimSEE( a );
end;



// retorna NIL si falla algo
function TPDET_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 )+'.xlt';
    result:= TSimCostos_archi.CreateRead( archi_simcosto, NCronicas );
  except
    result:= nil;
    raise Exception.Create('Error al leer archivo de resultados uplanes.425: ' + archi_simcosto );
  end;

end;

function TPDET_Problema.evaluar_( Individuo: TIndividuo; SemillaAleatoria: integer  ): boolean;
var
  archiSalaModificada: string;
  res_SimCostos: TSimCostos_archi;

begin
  archiSalaModificada:= crearSalaSimsee( Individuo );
  result:= true;
  res_SimCostos:= RunOptSim( archiSalaModificada, NCronicasCronicasPorVez, SemillaAleatoria );
  if res_simCostos <> nil then
  begin
  Individuo.f_histo:= TVectR.Create_FromDAofR( res_SimCostos.vcad );
  Individuo.f_VE:= res_SimCostos.cad_VE;
  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;
  end
  else
    raise Exception.Create('Falló la simulación');
end;


procedure TPDET_Problema.Free;
begin
  setlength( u, 0 );
  controlador.Free;
  Ordenes.Free;
  inherited Free;
end;

end.

