{+doc
+NOMBRE: ucalibrarconopronosticos
+CREACION: 2011-11-22
+AUTORES: rch
+REGISTRO:
+TIPO: Unidad Pascal.
+PROPOSITO:procedimiento de calibración de un cono de pronosticos
+PROYECTO:SimSEE

+REVISION:
+AUTOR:
+DESCRIPCION:
-doc}
unit ucalibrarconopronosticos;

{$mode delphi}

{x$DEFINE IMPONGA_FECHAINICONO_SIM}


interface

uses
{$IFDEF CALIBRAR_PRONOSTICOS_TRAZOSXY}
  Graphics, utrazosxy, autoesca, Dialogs,
{$ENDIF}
  uauxiliares, Math,
  umodelosintcegh, upronostico, xmatdefs, ufechas,
  ucosaConNombre, ucosa, ucosanubeseable, uInicioYFinal,
  Classes, SysUtils, matreal, uFuenteSintetizador, usalasdejuego;


(*+doc
Este procedimiento recibe un modelo CEGH y una descripción de los pronosticos asociados
a sus bornes de salida tal como es utilizado porp las FuentesSintetizador.
Si bien los pronosticos pueden especificar la Probabilidad de Excedencia de la GUIA este
procedimiento ignora dicha PE y calcula los vectores "sesgos" y "factor" para lograr
un cono centrado sobre la guía. Se entiende por "Centrado" que la guía tenga probabilidad
de excedencia 50% en el cono.
-doc*)
procedure CalibrarConoCentrado(cegh: TModeloCEGH; escenarios: TEscenarioDePronosticos;
  fechaIniSim: TFecha);

type
(*+doc
Esta clase permite calcular las series de un cono de pronóstico dado un modelo CEGH, un pronótico para una fecha
de inicio. La determinación del cono se hace mediante una simulación de la salida del CEGH de la cantidad de
crónicas especificadas en NCronicas. La simulación se realiza desde FechaIniSim y se finaliza en FechaFinSim
-doc*)

  { TSeriesConoPronostico }

  TSeriesConoPronostico = class
    NPasosT: integer;

    speInf, spe50, speSup: TMatR;
    sVE: TMatR;
    fcegh_ptr: TFuenteSintetizadorCEGH; // referencia a la fuente de creación

    pe_inf, pe_sup: NReal;

    //FP: Cronicas ordenadas. Se usa para calibrar CEGH segun ensembles
    preview_datos_ordenado: array of TMatR;
    preview_datos_desordenados: array of TMatR;

    constructor CreateFromSim(idHilo: integer; fcegh_ptrx: TFuenteSintetizadorCEGH;
      fechaIniSim, fechaFinSim: TFecha; NCronicas: integer; pe_inf,
  pe_sup: NReal);
    destructor destroy; override;
    {$IFDEF CALIBRAR_PRONOSTICOS_TRAZOSXY}
    procedure plot(Sender: TComponent);
    {$ENDIF}
  end;


{$IFDEF CALIBRAR_PRONOSTICOS_TRAZOSXY}
procedure CerrarTodosLosGraficos;
{$ENDIF}

implementation

// extiende con CEROS las guias de pronósticos en espacios gaussianos.
// la hipótesis es que luego de finalizado el cono, la guía es el CERO
// en el espacio gaussiano.
function fguia_extendida_(guia_eg: TDAOfNreal; jPaso: integer; DefVal: NReal): NReal;
begin
  if jPaso < length(guia_eg) then
    Result := guia_eg[jPaso]
  else
    Result := DefVal;
end;



// Inicializa el estado X a partir de las guias gaussianas de un escenario.
// Si no hay suficientes valores en las guias asume un CERO.
procedure CargarEstadoCompleto(X: TVectR; aEscenario: TPronosticos; NRetardos: integer);
var
  kRetardo, iSerie: integer;
  xdet: NReal;
begin
  for kretardo := 0 to (NRetardos - 1) do
  begin
    for iSerie := 1 to aEscenario.NSeries do
    begin
      xdet := fguia_extendida_(aEscenario[iSerie - 1].guia_eg,
        (NRetardos - 1) - kretardo, 0);
      X.pon_e(kRetardo * aEscenario.NSeries + iSerie, xDet);
    end;
  end;
end;

procedure ActualizamosEstado(X, Xs: TVectR);
var
  k: integer;
  NSeries: integer;
begin
  NSeries := Xs.n;
  for k := X.n downto (NSeries + 1) do
    X.pon_e(k, X.e(k - NSeries));
  for k := 1 to NSeries do
    X.pon_e(k, Xs.e(k));
end;

procedure CalibrarConoCentrado(cegh: TModeloCEGH; escenarios: TEscenarioDePronosticos;
  fechaIniSim: TFecha);
var
  NSeries, NEscenarios: integer;

  aEscenario: TPronosticos;
  aProno: TPronostico;

  // auxiliares para cálculo de aperturas
  AperturaRestantePorPaso_pu, AperturaInicial_pu: NReal;

  j: integer; // indice general.

  kSerie, kEscenario, kPaso: integer;

  ydet, xdet: NReal;
  fecha: TFecha;

  jbase: integer;

  jcol: integer;

  X, Xs, factores: TVectR;

  kselector: integer;

  // Largo máximo de sesgos a generar.
  Max_NPCC_NPCL: integer;

  xs_guia: NReal;

begin



  escenarios.prepararse(cegh.durPasoDeSorteoEnHoras);
  NEscenarios := escenarios.Count;
  NSeries := cegh.nBornesSalida;


  for kEscenario := 0 to NEscenarios - 1 do
  begin
    aEscenario := escenarios[kEscenario];

    if aEscenario.fechaIniCono = nil then
      aEscenario.fechaIniCono := TFecha.Create_Dt(fechaIniSim.dt)
    else if aEscenario.fechaIniCono.dt = 0 then
      aEscenario.fechaIniCono.dt := fechaIniSim.dt;
  end;

  // ajustamos dimensiones y limpiamos sesgos y factor
  for kEscenario := 0 to NEscenarios - 1 do
  begin
    aEscenario := escenarios[kEscenario];
    for kSerie := 0 to NSeries - 1 do
    begin
      aProno := aEscenario[kSerie];
//      GFlieller@20190712 cambio de usar NPSA = -1 a flg_modo_cono = 1
      if aProno.flg_modo_cono <> 1 then    // GFlieller@20181213  Para el ajuste de cono por lambdas explicitos

      begin

        if (cegh.nRetardos + aProno.NPCC) > length(aProno.guia) then
            aProno.NPCC := max(0, length(aProno.guia) - cegh.nRetardos);

          setlength(aProno.sesgo, aProno.NPCC + aProno.NPLC);
          vclear(aProno.sesgo);
          setlength(aProno.factor, aProno.NPSA + aProno.NPAC);
          vclear(aProno.factor);

      end;
    end;
  end;

  // llenamos los factores de apertura del cono
  // hasta NPSA dejamos los ceros y a partir de ahí abrimos el cono aumentando
  // linealmente el factor.
  for kEscenario := 0 to NEscenarios - 1 do
  begin
    aEscenario := escenarios[kEscenario];
    for kSerie := 0 to NSeries - 1 do
    begin
      aProno := aEscenario[kSerie];

      if aProno.flg_modo_cono <> 1 then    // GFlieller@20190712 actualizado para usar el flg.  GF@13-12-2018  Para el ajuste de cono por lambdas explicitos
      begin

          AperturaInicial_pu := aProno.AperturaInicial_pu;
          if aProno.NPAC > 0 then
            AperturaRestantePorPaso_pu := max(0.0, 1 - AperturaInicial_pu) / aProno.NPAC
          else
            AperturaRestantePorPaso_pu := 0;

          for j := 0 to aProno.NPSA - 1 do
            aProno.factor[j] := AperturaInicial_pu;
          for j := max(0, aProno.NPSA) to high(aProno.factor) do
            aProno.factor[j] := (j - aProno.NPSA) * AperturaRestantePorPaso_pu +
              AperturaInicial_pu;

      end;                        // GFlieller@20181213

    end;
  end;



  // ahora creamos la imagen de las guías de los pronósticos en el espacio
  // gaussiano.

  for kEscenario := 0 to NEscenarios - 1 do
  begin
    aEscenario := escenarios[kEscenario];
    for kSerie := 0 to NSeries - 1 do
    begin
      aProno := aEscenario[kSerie];


      setlength(aProno.guia_eg, length(aProno.guia));

      fecha := TFecha.Create_Clone(aProno.escenario.fechaIniCono);

      // retrocedemos para la cantidad de retardos por la duración del paso de sorteo.
      fecha.addHoras(-(cegh.nRetardos - 1) * cegh.durPasoDeSorteoEnHoras);

      for kpaso := 0 to high(aProno.guia_eg) do
      begin
        ydet := aProno.guia[kpaso];
        kselector := cegh.kSelectorDeformador(fecha);
        xdet := cegh.xTog(ydet, kSerie + 1, kselector + 1);
        aProno.guia_eg[kpaso] := xdet;
        fecha.addHoras(cegh.durPasoDeSorteoEnHoras);
      end;
      fecha.Free;

    end;
  end;


  if aProno.flg_modo_cono <> 1 then    // GFlieller@20190712 actualizado para usar el flg. GF@28-12-2018  Para el ajuste de cono por lambdas explicitos
  begin


  // ahora, proyectamos la salida del filtro para cada serie conociendo
  // los estados anteriores y la diferencia entre el proyectado y el valor
  // siguiente en los determinismos gaussianos es el sesgo en la fuente.

  // Vectores para soporte del estado y su proyección
  X := TVectR.Create_Init(nSeries * cegh.NRetardos);
  Xs := TVectR.Create_Init(nSeries);

  for kEscenario := 0 to NEscenarios - 1 do
  begin
    aEscenario := escenarios[kEscenario];

    // Determinación de la guia de sesgos más larga e inicialización de los
    // estados del cálculo
    Max_NPCC_NPCL := 0;

    for kSerie := 0 to NSeries - 1 do
    begin
      aProno := aEscenario[kSerie];

      if aProno.NPCC + aProno.NPLC > Max_NPCC_NPCL then
        Max_NPCC_NPCL := aProno.NPCC + aProno.NPLC;
    end;


    if cegh.A_cte = nil then
      // Necesita calcular kselector para seleccionar matriz mcA
    begin
      // neceisto la fecha para calcular el kSelector
      fecha := TFecha.Create_Clone(aProno.escenario.fechaIniCono);
      // retrocedemos para la cantidad de retardos por la duración del paso de sorteo.
      fecha.addHoras(-cegh.NRetardos * cegh.durPasoDeSorteoEnHoras);
    end;

    jbase := cegh.NRetardos - 1;

    for kpaso := 0 to Max_NPCC_NPCL - 1 do
    begin
      if cegh.A_cte = nil then
        // Necesita calcular kselector para seleccionar matriz mcA
      begin
        kselector := cegh.kSelectorDeformador(fecha);
        fecha.addHoras(cegh.durPasoDeSorteoEnHoras);
      end;



      // Cargamos el vector de estado
      if kPaso = 0 then
        CargarEstadoCompleto(X, aEscenario, cegh.NRetardos)
      else
        ActualizamosEstado(X, Xs);

      // Proyectamos la salida del filtro A
      if cegh.A_cte <> nil then
        cegh.A_cte.Transformar(Xs, X)
      else
        cegh.mcA[kSelector].Transformar(Xs, X);

      for jCol := 1 to NSeries do
      begin
        aProno := aEscenario[jCol - 1];
        if kPaso < aProno.NPCC then
        begin
          xs_guia := fguia_extendida_(aProno.guia_eg, jbase + 1, 0);
          aProno.sesgo[kPaso] := xs_guia - Xs.e(jCol);
          Xs.pon_e(jCol, xs_guia);
        end
        else if kPaso < (aProno.NPCC + aProno.NPLC) then
        begin
          if (aProno.NPLC > 0) and (aProno.NPCC > 0) then
            aProno.sesgo[kPaso] :=
              aProno.sesgo[aProno.NPCC - 1] * (1.0 -
              (kPaso + 1 - aProno.NPCC) / aProno.NPLC);

          // corrigo la proyección acorde con el sesgo
          xs_guia := Xs.e(jCol) + aProno.sesgo[kPaso];
          Xs.pon_e(jCol, xs_guia);
        end;
        // else ... ya terminó la guia de sesgos y dejo el Xs tal cual

      end;

      Inc(jbase);
    end;

    if cegh.A_cte = nil then
      fecha.Free;

    X.Free;
    Xs.Free;
  end;

end;             // GFlieller@20181228

end;


var
  //Es un arreglo de boneras, donde cada bornera es una matriz
  //Las filas de esta matriz son los pasos y las columnas las cronicas
  preview_datos: array of TMatR;
  preview_fuente: TFuenteSintetizadorCEGH;
  preview_kCronica, preview_jPaso, preview_nseries, preview_DimEstadoReducido: integer;
  preview_despBornera: integer; // desplazamiento en la bornera para acceder a la salida

  // del filtro en el mundo real. = nSeries + (nSeries*NRetardos)
  preview_NCronicas, preview_NPasos: integer;
  {$IFDEF CALIBRAR_PRONOSTICOS_TRAZOSXY}
  trx: array of TfrmDllForm;
  {$ENDIF}

procedure preview_inicioCronica;
begin
  preview_jPaso := 1;
  Inc(preview_kCronica);
end;

procedure preview_finpaso;
var
  jBornera, j: integer;
  MRed: TMatR;
  kSel: integer;
begin

  for jBornera := 0 to preview_nseries - 1 do
  begin
    preview_datos[jBornera].pon_e(preview_jPaso, preview_kCronica,
      preview_fuente.bornera[preview_despBornera + jBornera]);
  end;


  if preview_DimEstadoReducido > 0 then
  begin
    if preview_fuente.datosModelo_Opt.A_cte <> nil then
      MRed := preview_fuente.datosModelo_Opt.MRed_cte
    else
    begin
      kSel := preview_fuente.kSelectorDesformador(
        preview_fuente.datosModelo_Opt, preview_fuente.globs.FechaInicioDelpaso);
      MRed := preview_fuente.datosModelo_Opt.MRed_mc[kSel];
    end;

    for j := 0 to preview_DimEstadoReducido - 1 do
    begin
      preview_fuente.ReducirEstado(preview_fuente.XRed, preview_fuente.jPrimer_X_x,
        preview_fuente.datosModelo_Opt, MRed, preview_fuente.Bornera);
      preview_datos[preview_nseries + j].pon_e(preview_jPaso, preview_kCronica,
        preview_fuente.XRed[j]);
    end;

  end;
  Inc(preview_jPaso);
end;



function AddArchivoToSala( aArchiRef_:TArchiRef_Nubeseable; sala: TSalaDeJuego ): boolean;
var
  aux: integer;
  lista: TListaDeArchiRef_Nubeseable;
  ArchiRef: TArchiRef_Nubeseable;
  archi: string;
begin
  if aArchiRef_ = nil then
  begin
    result:= false;
    exit;
  end;

  archi:= aArchiRef_.GetArchi;

  ArchiRef := TArchiRef_Nubeseable.Create( 0, 0, 0, False,
 	    TArchiRef_.Create(archi));
  lista := sala.archs;
  if not lista.find(archiref.ArchiRef.get_archi, aux) then
  begin
    lista.Add(archiref);
    Result := True;
  end
  else
  begin
    ArchiRef.Free;
    Result := False;
  end;
end;



constructor TSeriesConoPronostico.CreateFromSim(idHilo: integer;
  fcegh_ptrx: TFuenteSintetizadorCEGH; fechaIniSim, fechaFinSim: TFecha;
  NCronicas: integer; pe_inf, pe_sup: NReal);

var
  durpos: TDAOfNReal;
  jPaso, kSerie: integer;
  jpeInf, jpe50, jpeSup: integer;
  vdatos: TVectR;

  sala: TSalaDeJuego; // auxiliar para la calibración

  kCronica: integer;
  sal: textfile;
  old_dir: string;

  CatalogoRefs: TCatalogoReferencias;
begin
  inherited Create;

  old_dir := getcurrentdir;

  //  fcegh.WriteToArchi('fcegh_calibrador.txt');

  self.fcegh_ptr := fcegh_ptrx;

  self.pe_inf := pe_inf;
  self.pe_sup := pe_sup;



  // creamos un clon para que cuando haga el FREE de la Sala no se me pierda
  // la fuente.
  CatalogoRefs := TCatalogoReferencias.Create;
  preview_fuente := fcegh_ptrx.Create_Clone(CatalogoRefs, idHilo) as
    TFuenteSintetizadorCEGH;

  setlength(durpos, 1);
  durpos[0] := fcegh_ptrx.datosModelo_Sim.durPasoDeSorteoEnHoras;

  sala := TSalaDeJuego.Create( 0, 'Calibrador',
    // rango de simulación
    fechaIniSim, fechaFinSim, TFecha.Create_Dt(0),
    // rango de optimización (pongo lo mismo que sim - no importa )
    fechaIniSim, fechaFinSim, durpos);

  sala.setDirCorrida('_calibrador_.ese');
  
  //Agregamos fuente a la sala
  sala.listaFuentes_.Add(preview_fuente);
  //Agregamos los archivos a la sala
  AddArchivoToSala( fcegh_ptrx.nombreArchivoModelo, sala );
  AddArchivoToSala( fcegh_ptrx.nombreArchivoDatosHistoricos, sala );
  AddArchivoToSala( fcegh_ptrx.nombreArchivoModeloAuxiliar, sala );

  CatalogoRefs.resolver_referenciasDeArch( sala.archs );

  if CatalogoRefs.referenciasSinResolver > 0 then
  begin
    CatalogoRefs.DumpReferencias('errRefs.txt');
    raise Exception.Create(
      'TSeriesConoPronostico.CreateFromSim - Quedan referencias sin resolver');
  end;

  CatalogoRefs.Free;
  CatalogoRefs:= nil;


  //Inicializamos todas las variables
  preview_NPasos := sala.globs.calcNPasosSim;
  preview_kCronica := 0;

  preview_nseries := fcegh_ptrx.datosModelo_Sim.nBornesSalida;
  preview_DimEstadoReducido := fcegh_ptrx.datosModelo_Sim.MRed_cte.nf;

  preview_despBornera := fcegh_ptrx.dim_RB + fcegh_ptrx.dim_Wa +
    fcegh_ptrx.datosModelo_Sim.nRetardos * preview_nseries;

  NPasosT := preview_NPasos;
  preview_NCronicas := NCronicas;

  speInf := TMatR.Create_Init(preview_NSeries + preview_DimEstadoReducido,
    preview_NPasos);
  spe50 := TMatR.Create_Init(preview_NSeries + preview_DimEstadoReducido,
    preview_NPasos);
  speSup := TMatR.Create_Init(preview_NSeries + preview_DimEstadoReducido,
    preview_NPasos);
  sVE := TMatR.Create_Init(preview_NSeries + preview_DimEstadoReducido,
    preview_NPasos);

  sala.globs.procNot_InicioCronica := preview_inicioCronica;
  sala.globs.procNot_FinPaso := preview_finpaso;


  //Inicializo array para el tamaño de series
  setlength(preview_datos, preview_nseries + preview_DimEstadoReducido);
  //Inicializo array para el tamaño de series (ORDENADAS y DESORDENADA)
  setlength(preview_datos_ordenado, preview_nseries + preview_DimEstadoReducido);
  setlength(preview_datos_desordenados, preview_nseries + preview_DimEstadoReducido);

  //Inicializo matriz como num de pasos x num de cronicas
  for kSerie := 0 to high(preview_datos) do
  begin
    preview_datos[kSerie] := TMatR.Create_Init(preview_NPasos, preview_NCronicas);
    preview_datos_ordenado[kSerie] :=
      TMatR.Create_Init(preview_NPasos, preview_NCronicas);
  end;

  (**** OJO hago como que optmicé para que exista globs.CF ****)
  sala.inicializarOptimizacion_subproc01;
  sala.inicializarOptimizacion_subproc02_(nil, nil);
  sala.globs.abortarSim := False;
  sala.globs.NCronicasSim_global_ := NCronicas;
  sala.globs.NEstadosIniciales_global := 1;
  {$IFDEF MODO_DEBUG_ENSAMBLES}
  sala.globs.semilla_inicial_sim := RandomRange(10,1000);
  {$ELSE}
  sala.globs.semilla_inicial_sim := 31;
  {$ENDIF}
  // !!!!!!!!!!!!! SIMULAR !!!!!!!!!!!!!!!!!!
  setSeparadoresGlobales; // para que escriba en global
  sala.Simular(0, True);
  setSeparadoresLocales;
  // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

  jpeInf := min(trunc(pe_inf * NCronicas) + 1, NCronicas);
  jpe50 := min(trunc(0.5 * NCronicas) + 1, NCronicas);
  jpeSup := min(trunc(pe_sup * NCronicas) + 1, NCronicas);

  assignfile(sal, 'ucalibrador.xlt');
  rewrite(sal);
  writeln(sal, 'NSeries: ', preview_NSeries);
  writeln(sal, 'NPasos: ', preview_NPasos);
  writeln(sal, 'DimEstadoReducido: ', preview_DimEstadoReducido);

  for kSerie := 1 to length(preview_datos) do
  begin
    writeln(sal, 'Serie: ', kSerie);
    for jPaso := 1 to preview_NPasos do
    begin
      Write(sal, jPaso);
      //datos[kSerie][jPaso][jCronica]
      vdatos := preview_datos[kSerie - 1].Fila(jPaso);
      for kCronica := 1 to vdatos.n do
        Write(sal, #9, vdatos.e(kCronica));
      writeln(sal);
    end;
    writeln(sal);
  end;

  writeln(sal, '**********CONO RESUMEN*********');
  writeln(sal, 'jpeInf: ', jpeInf, ', jpe50: ', jpe50, ', jpeSup: ', jpeSup);

  for kSerie := 1 to length(preview_datos) do
      preview_datos_desordenados[kSerie - 1]:= TMatR.Create_Clone(preview_datos[kSerie-1]);

  //Tenemos que ordenar los vectores de la matriz para poder imprimir el cono
  for jPaso := 1 to preview_NPasos do
  begin
    Write(sal, jPaso);
    for kSerie := 1 to length(preview_datos) do
    begin
      //datos[kSerie][jPaso][jCronica]
      vdatos := preview_datos[kSerie - 1].Fila(jPaso);
      vdatos.Sort(True);

      preview_datos_ordenado[kSerie - 1].Fila(jPaso).Create_Clone(vdatos);


      sVE.pon_e(kSerie, jPaso, vdatos.promedio);
      speInf.pon_e(kSerie, jPaso, vdatos.e(jpeInf));
      spe50.pon_e(kSerie, jPaso, vdatos.e(jpe50));
      speSup.pon_e(kSerie, jPaso, vdatos.e(jpeSup));

      Write(sal, #9, sVE.e(kSerie, jPaso));
      Write(sal, #9, speInf.e(kSerie, jPaso));
      Write(sal, #9, spe50.e(kSerie, jPaso));
      Write(sal, #9, speInf.e(kSerie, jPaso));
    end;
    writeln(sal);
  end;

  closefile(sal);

  sala.Free;

  preview_fuente := nil; // ya la liberó la sala.

  for kSerie := 0 to high(preview_datos) do
    preview_datos[kSerie].Free;
  setlength(preview_datos, 0);

  setcurrentdir(old_dir);
end;

destructor TSeriesConoPronostico.destroy;
var
  kserie: integer;
begin
  speInf.Free;
  spe50.Free;
  speSup.Free;
  sVE.Free;

  for kSerie := 0 to high(preview_datos_ordenado) do
    preview_datos_ordenado[kSerie].Free;
  for kSerie := 0 to high(preview_datos_desordenados) do
    preview_datos_desordenados[kSerie].Free;
  setlength(preview_datos_ordenado, 0);
  setlength(preview_datos_desordenados, 0);
  inherited Destroy;
end;


{$IFDEF CALIBRAR_PRONOSTICOS_TRAZOSXY}

procedure CerrarTodosLosGraficos;
var
  k: integer;
  aform: TfrmDllForm;
begin
  if trx = nil then exit;
  for k:= 0 to high( trx ) do
  begin
    aform:= trx[k];
    aform.free;
  end;
  setlength( trx, 0 );
end;

procedure TSeriesConoPronostico.plot(Sender: TComponent);
var
  kSerie, jPaso: integer;
  colores: array of TColor;
  nombreBorne: string;

  minVal, maxVal, dVal: NReal;

  NDivY: integer;
  guia: TDAOfNReal;
  jPasoGuia, jPasoGuia_Ultimo: integer;

  iserie_VE, iserie_Guia, iserie_pe05, iserie_pe50, iserie_pe95: integer;

begin
  if NPasosT <= 0 then
  begin
    ShowMessage('No hay cono de pronósticos especificado.');
    exit;
  end;

  (*
  if length(trx) <> 0 then
    for kSerie := 0 to high(trx) do
      if trx[kSerie] <> nil then
        trx[kSerie].Close;
    *)

  SetLength(trx, preview_nseries + preview_DimEstadoReducido);
  //Tamaño del vector de graficos
  //Creo un array con los colores para todas las posibles series
  setlength(colores, 12);
  colores[0] := clBlue;
  colores[1] := clOlive;
  colores[2] := clTeal;
  colores[3] := clGreen;
  colores[4] := clNavy;
  colores[5] := clPurple;
  colores[6] := clBlack;
  colores[7] := clGray;
  colores[8] := clLime;
  colores[9] := clFuchsia;
  colores[10] := clMaroon;
  colores[11] := clRed;

  //Comienza loop de imprimir
  for kSerie := 0 to preview_nseries + preview_DimEstadoReducido - 1 do
  begin
    if kSerie < preview_nseries then
      nombreBorne := fcegh_ptr.NombresDeBornes_Publicados[kSerie]
    else
      nombreBorne := fcegh_ptr.datosModelo_Sim.nombreVarE[kSerie - preview_nseries];

    minVal := speInf.fila(kSerie + 1).minval;
    maxVal := speSup.fila(kSerie + 1).maxval;
    if maxVal - minVal < 1e-6 then
    begin
      maxval:= minval +1e-3;
      minval:= minval -1e-3;
      dVal:= 2e-3;
    end;

    NDivY := 8;
    Escala125N(minVal, maxVal, dVal, NDivY, 1);

    (**
    if trx[kSerie] <> nil then
      trx[kSerie].Free;

      // esto no tendría que ser necesario
      **)
    trx[kSerie] := TfrmDllForm.Create( Sender );

    iserie_VE := 1;
    trx[kSerie].CrearDiagramaXY(
      nombreBorne, // Nombres
      NPasosT,      // MaxNPuntos
      False,       // ciruclar
      'paso',         // nombre_sx
      'VE',  // nombre_sy1: pchar;
      colores[0],  //  color_sy1: TColor;
      1, NPasosT,   //  x1, x2,
      minVal, maxVal, //y1, y2
      10, NDivY        // NDivX, NDivY
      );

    //    trx[kSerie].nilOnClose := @trx[kSerie];

    trx[kSerie].Show;

    //Creo las series
    if kSerie < preview_nseries then
      iserie_Guia := trx[kSerie].CrearSerieXY('Guía', NPasosT, False,
        colores[1], TM_Cuadrado, colores[1], colores[1], 3);

    iserie_pe05 := trx[kSerie].CrearSerieXY('pe_' +
      FloatToStr(trunc(self.pe_inf * 100 + 0.5)) + '%', NPasosT,
      False, colores[2], TM_Triangulo, colores[2], colores[2], 3);
    iserie_pe50 := trx[kSerie].CrearSerieXY('pe_50%', NPasosT, False,
      colores[3], TM_Circulo, colores[3], colores[3], 3);
    iserie_pe95 := trx[kSerie].CrearSerieXY('pe_' +
      FloatToStr(trunc(self.pe_sup * 100 + 0.5)) + '%', NPasosT,
      False, colores[4], TM_Cruz45, colores[4], colores[4], 3);

    trx[kSerie].dbj_gridx;
    trx[kSerie].dbj_gridy;
    trx[kSerie].dbj_borde;
    trx[kSerie].titulo(nombreBorne);
    trx[kSerie].etiquetas_x(0, NPasosT);
    trx[kSerie].etiquetas_y(MinVal, maxVal);
    trx[kSerie].xlabel('PasoT');
    //    trx[kSerie].ylabel('M^3/s');


    if kSerie < preview_nseries then
    begin
      guia := fcegh_ptr.escenariosDePronosticos[0][kSerie].guia;
      jPasoGuia := fcegh_ptr.datosModelo_Sim.nRetardos - 1;
      jPasoGuia_Ultimo := jPasoGuia + fcegh_ptr.escenariosDePronosticos[0][kSerie].NPCC;
    end;

    //Aca se imprimen los valores de cada serie
    for jPaso := 1 to NPasosT do
    begin
      trx[kSerie].tr1.PlotNuevo_x(jPaso);
      trx[kSerie].tr1.PlotNuevo_y(iserie_VE, sve.fila(kserie + 1).e(jPaso));
      if kSerie < preview_nseries then
      begin
        if jPasoGuia <= high(guia) then
          trx[kSerie].tr1.PlotNuevo_y(iserie_Guia, guia[jPasoGuia]);
        Inc(jPasoGuia);
      end;
      trx[kSerie].tr1.PlotNuevo_y(iserie_pe05, speInf.fila(kserie + 1).e(jPaso));
      trx[kSerie].tr1.PlotNuevo_y(iserie_pe50, spe50.fila(kserie + 1).e(jPaso));
      trx[kSerie].tr1.PlotNuevo_y(iserie_pe95, speSup.fila(kserie + 1).e(jPaso));
    end;

  end;//End del loop que imprime cada serie

  SetLength(colores, 0);
end;

{$ENDIF}


procedure AlFinal;
{$IFDEF CALIBRAR_PRONOSTICOS_TRAZOSXY}
var
  k: integer;
  {$ENDIF}
begin
  {$IFDEF CALIBRAR_PRONOSTICOS_TRAZOSXY}
  (*
  if length( trx ) > 0 then
    for k:= 0 to high( trx ) do
      if trx[k] <> nil then
        trx[k].Close;
        *)
  setlength(trx, 0);
  {$ENDIF}
end;

initialization
  preview_datos := nil;
  preview_fuente := nil;
  {$IFDEF CALIBRAR_PRONOSTICOS_TRAZOSXY}
  setlength(trx, 0);
  {$ENDIF}
finalization
  AlFinal;
end.
