English Français

Documentation / Manuel utilisateur en C++ : PDF version

XIV.8. Macros Relauncher

XIV.8. Macros Relauncher

The idea of this section is to show the basic usage of many of the classes defined in Chapter VIII, applied either on the flowrate functions or the flowrate code, whose purpose and behaviour have been already introduced in Section IV.1.2.1. All the following examples will load a tiny set of points which is gathered in the file called flowrateUniformDesign.dat (already introduced in Section XIV.2.4.1).

XIV.8.1. Macro "relauncherFunctionFlowrateCInt.C"

XIV.8.1.1. Objective

The goal of this macro is to show how to handle (in the most simple way) a C++-written function, compliant with the ROOT (CINT) format. This function has been presented, at least its equation (see Equation IV.1) and would be interfaced through the TCIntEval class in the Relauncher module (which means that we'll use the function database from ROOT's catalog, see Section I.2.5 for more explanations). As this class is usually considered not thread-safe, it can only be used with a TSequentialRun runner.

XIV.8.1.2. Macro



void flowrateModel(double *x, double *y)
{
  double drw = x[0], dr  = x[1];
  double dtu = x[2], dtl = x[3];
  double dhu = x[4], dhl = x[5];
  double dl  = x[6], dkw = x[7];

  double dnum = 2.0 * TMath::Pi() * dtu * ( dhu -dhl);
  double dlnronrw = TMath::Log( dr / drw);
  double dden = dlnronrw * ( 1.0 +  ( 2.0 * dl * dtu ) / ( dlnronrw * drw * drw * dkw) + dtu / dtl );
  
  y[0] = dnum / dden;
}

void relauncherFunctionFlowrateCInt()
{

  // Create the TDataServer
  TDataServer *tds = new TDataServer("foo","test");
  tds->fileDataRead("flowrateUniformDesign.dat");

  // Get the attributes
  TAttribute *rw = tds->getAttribute("rw");
  TAttribute *r = tds->getAttribute("r");
  TAttribute *tu = tds->getAttribute("tu");
  TAttribute *tl = tds->getAttribute("tl");
  TAttribute *hu = tds->getAttribute("hu");
  TAttribute *hl = tds->getAttribute("hl");
  TAttribute *l = tds->getAttribute("l");
  TAttribute *kw = tds->getAttribute("kw");

  // Create the output attribute
  TAttribute *yhat = new TAttribute("yhat");
  
  // Constructing the code
  TCIntEval mycode("flowrateModel");    
  mycode.setInputs(8, rw, r, tu, tl, hu, hl, l, kw); // Adding the input attributes
  mycode.addOutput(yhat); // Adding the output attributes

  // Create the sequential runner
  TSequentialRun run(&mycode);
  run.startSlave(); //Start the master (necessary even for a sequential)
  if (run.onMaster())
  {
      TLauncher2 lanceur(tds, &run);

      // resolution
      lanceur.solverLoop();
      run.stopSlave(); // Stop the slaves (necessary even for a sequential)
  }

  // Draw the result
  TCanvas *can = new TCanvas("pouet","foo",1);
  tds->Draw("yhat:rw","","colZ");  
    
   
}

The first part of the macro is the definition of the flowrateModel function, already discussed throughout this documentation. The dataserver object is then created and filled using the database file and pointers to the corresponding input attributes are created, along with the new attribute for the output provided by the function. The following part is then specific to the Relauncher organisation: a TCIntEval object is created with the function as only argument. Both the input and output attributes are provided (here in a contracted way for input, but it could have been done one-by-one, as for output).

// Constructing the code
TCIntEval mycode(flowrateModel);
mycode.setInputs(8, rw, r, tu, tl, hu, hl, l, kw); // Adding the input attributes
mycode.addOutput(yhat); // Adding the output attributes

The following part is the heart of the relauncher strategy: the assessor is provided to the chosen runner, which should always start the slaves (even in the case of a sequential one like here). On the main CPU, the master is created as well (with the dataserver and the runner) and the resolution is requested.

// Create the sequential runner
TSequentialRun run(&mycode);
run.startSlave(); //Start the master (necessary even for a sequential)
if (run.onMaster())
{
  TLauncher2 lanceur(tds, &run);

  // resolution
  lanceur.solverLoop();
  run.stopSlave(); // Stop the slaves (necessary even for a sequential)
}

Once this is done, the slaves are stopped and the results is displayed for cross-check in the following subsection.

XIV.8.1.3. Graph

Figure XIV.82. Representation of the output as a function of the first input with a colZ option

Representation of the output as a function of the first input with a colZ option

XIV.8.2. Macro "relauncherFunctionFlowrateCJit.C"

XIV.8.2.1. Objective

The goal of this macro is to show how to handle the C++-written function using a pointer to the function (and not the name as for the macro in Section XIV.8.1). This function has been presented, at least its equation (see Equation IV.1) and would be interface through the TCJitEval class in the Relauncher module. A special discussion will be held in the next few lines about the way the compilation is done.

XIV.8.2.2. Macro

#include "TCanvas.h"
#include "TMath.h"
#include "TSystem.h"
#include "TDataServer.h"
#include "TSequentialRun.h"
#include "TAttribute.h"
#include "TLauncher2.h"
#include "TCJitEval.h"

using namespace URANIE::DataServer;
using namespace URANIE::Relauncher;

void flowrateModel(double *x, double *y)
{
  double drw = x[0], dr  = x[1];
  double dtu = x[2], dtl = x[3];
  double dhu = x[4], dhl = x[5];
  double dl  = x[6], dkw = x[7];

  double dnum = 2.0 * TMath::Pi() * dtu * ( dhu -dhl);
  double dlnronrw = TMath::Log( dr / drw);
  double dden = dlnronrw * ( 1.0 +  ( 2.0 * dl * dtu ) / ( dlnronrw * drw * drw * dkw) + dtu / dtl );
  
  y[0] = dnum / dden;
}

// void relauncherFunctionFlowrateCJit() // For ACLIC (meaning CINT) compilation
int main() // For standalone compilation
{
    
  // Create the TDataServer
  TDataServer *tds = new TDataServer("foo","test");
  tds->fileDataRead("flowrateUniformDesign.dat");

  // Get the attributes
  TAttribute *rw = tds->getAttribute("rw");
  TAttribute *r = tds->getAttribute("r");
  TAttribute *tu = tds->getAttribute("tu");
  TAttribute *tl = tds->getAttribute("tl");
  TAttribute *hu = tds->getAttribute("hu");
  TAttribute *hl = tds->getAttribute("hl");
  TAttribute *l = tds->getAttribute("l");
  TAttribute *kw = tds->getAttribute("kw");

  // Create the output attribute
  TAttribute *yhat = new TAttribute("yhat");
  
  // Constructing the code
  TCJitEval mycode(flowrateModel);  
  mycode.setInputs(8, rw, r, tu, tl, hu, hl, l, kw); // Adding the input attribute
  mycode.addOutput(yhat); // Adding the output attribute

  // Create the sequential runner
  TSequentialRun run(&mycode);
  run.startSlave(); //Start the master (necessary even for a sequential)
  if (run.onMaster())
  {
    TLauncher2 lanceur(tds, &run);

    // resolution
    lanceur.solverLoop();
    run.stopSlave(); // Stop the slaves (necessary even for a sequential)
  }

  // Export the data
  tds->exportData("_outputFile_functionflowrate_cjit_.dat");
    
}

The very first part of the macro is slightly different from Section XIV.8.1.2 as it is now compulsory to write explicitly the include line for the pre-processors. The use of namespaces is declared along, but this time only because this is convenient not to recall these long names every time.

#include "TCanvas.h"
#include "TMath.h"
#include "TSystem.h"
#include "TDataServer.h"
#include "TSequentialRun.h"
#include "TAttribute.h"
#include "TLauncher2.h"
#include "TCJitEval.h"

using namespace URANIE::DataServer;
using namespace URANIE::Relauncher;

The definition of the flowrateModel function is then made right before entering the main function. At this level a break is needed to explain the risen antagonism between the two possible compilation and the way to call them. This issue is entirely described by the following two lines:

// void relauncherFunctionFlowrateCJit() // For ACLIC (meaning CINT) compilation
int main() // For standalone compilation	    

Let's talk about the two cases:

  • ACLIC: one should remove the second line and put the first one instead (after commenting it out of course). The compilation is then done by calling:

    root -l relauncherFunctionFlowrateCJit.C+

    • PROS: this will give you the hand at the end of execution, leaving the display opened to handle plots for instance.
    • CONS: this will create several useless files and will need non-trivial manipulation if extra headers and libraries are needed.

  • Standalone: leaving the macro as it is, the compilation on Linux is done by writing a line such as:

    g++ -o CJitTest relauncherFunctionFlowrateCJit.C `root-config --cflags --libs` -L$URANIESYS/lib -lUranieDataServer  -lUranieRelauncher -I$URANIESYS/include/

    An equivalent command on Windows would be:

    cl /Fe%cd%\CJitTest /Tp relauncherFunctionFlowrateCJit.C /I%ROOTSYS%\include %ROOTSYS%\lib\lib*.lib %URANIESYS%\lib\libUranieDataServer.lib %URANIESYS%\lib\libUranieRelauncher.lib

    • PROS: pure C++ compilation resulting in a single executable file (here CJitTest). It's easy to include more headers and libraries if your code needs them.
    • CONS: this will not leave the display opened, unless you do so through a TApplication (ROOT-class).

Whatever the chosen solution, the only difference with previous macro is the assessor creation:

TCJitEval mycode(flowrateModel);

The rest it completely transparent and leads to the creation of the following plot.

XIV.8.2.3. Graph

Figure XIV.83. Representation of the output as a function of the first input with a colZ option

Representation of the output as a function of the first input with a colZ option

XIV.8.3. Macro "relauncherCJitFunctionThreadTest.C"

XIV.8.3.1. Objective

The goal of this macro is to show how to handle thread-safe compiled function (or code) that would contain TDataServer objects (this is not particularly recommended, but has been requested to us). This example is only written in C++ as the CJit interface only works for this, but the idea is the same if one has a code and use the python interface. This will be further discussed below. The function is pointless, a pure illustrative toy and the results is not important as long as one sees that in one case, the macro runs smoothly while on the other hand, it crashes.

XIV.8.3.2. Macro

void multiply(double *x, double *y)
{
    // New dataserver reading all points in flowrate 
    URANIE::DataServer::TDataServer test("test","notindir");
    test.keepFinalTuple(false); // Remove the tuple from ROOT internal list
    test.fileDataRead("flowrateUniformDesign.dat",false);
     
    // Dummy functions with a loop to slow down the function
    double max=-1000000;
    for(int i=0; i<test.getNPatterns(); i++)
    {
        double val = test.getValue("ystar",i);
        max = ((val>=max) ? val : max); 
    }
    y[0] = max * x[0];

}

void relauncherCJitFunctionThreadTest()
{
   
    /* This macro can be used in two modes:
    */
    bool threaded=true;

    // If thread-safe, call this method that will take out the dataserver from ROOT internal list
    if(threaded)
        ROOT::EnableThreadSafety();  // part of the solution

    // input and output attributes
    URANIE::DataServer::TUniformDistribution x("multiplier",1,10);
    URANIE::DataServer::TAttribute    MultMean("MultMean");

    // Interface to the compiled function above
    URANIE::Relauncher::TCJitEval eval(multiply);
    eval.addInput(&x);
    eval.addOutput(&MultMean);

    // Threaded runner
    URANIE::Relauncher::TThreadedRun run(&eval,4);
    run.startSlave();

    if( run.onMaster() )
    {
        // Global dataserver
        URANIE::DataServer::TDataServer tds("pouet","pouet_notindir");
        tds.addAttribute(&x);
    
        // Doe for the multiplier
        URANIE::Sampler::TSampling sam(&tds,"lhs",24);
        sam.generateSample();

        // Run the code
        URANIE::Relauncher::TLauncher2 launch(&tds, &run);
        launch.solverLoop();
        tds.getTuple()->SetScanField(-1);
        tds.scan();

        run.stopSlave();
 
    }
}

The very first part of the macro is a function that would be applied to all points of our design-of-experiments. The general context is simple: let's assume one wants to find the maximum value of a variable in a given dataset, and let's assume that this maximum should have to be scaled by some factor. We create the multiply function to do so, as done here:

void multiply(double *x, double *y)
{
    // New dataserver reading all points in flowrate 
    URANIE::DataServer::TDataServer test("test","notindir");
    test.keepFinalTuple(false); // Remove the tuple from ROOT internal list
    test.fileDataRead("flowrateUniformDesign.dat",false);
     
    // Dummy functions with a loop to slow down the function
    double max=-1000000;
    for(int i=0; i<test.getNPatterns(); i++)
    {
        double val = test.getValue("ystar",i);
        max = ((val>=max) ? val : max); 
    }
    y[0] = max * x[0];

}

In this function the dataset is always the same (flowrateUniformDesign.dat) and the multiplier is the only input attribute. The maximum of the dataset is found by creating a TDataServer object, by calling the fileDataRead method to read the dataset and by looping over the events (this is not at all the best way to do it, but it is a toy model to show what problems can arise when a TDataServer object is created in an evaluator use in multi-thread approach). Few important points in this function to prevent from race condition (not thread-safe behaviour):

  • the TDataServer object is created statistically so that at the end of the function it is automatically destroyed;

  • the line below is used to tell the TDataServer object not to write his data tree in the ROOT internal list and not to dump it in the archive file when the object is destroyed

    test.keepFinalTuple(false);

  • the line below is used to tell the fileDataRead method not to create the archive ROOT file once the dataset is read (thanks to the optional boolean set to false here)

    test.fileDataRead("flowrateUniformDesign.dat",false);

The main function starts then with a block that allows to test the main point here: the method develop by ROOT to prevent the internal list to store object which would lead to race conditions. This block is commented to explain how to run the use-case macro discuss here. The important part is, if one wants to run the macro properly, to call

ROOT::EnableThreadSafety();

Once this is done, then the macro can be briefly described in the few key steps

  1. create the input (multiplier) and output (MultMean) attribute;

  2. create the interface to the multiply function;

  3. create the interface for the runner;

  4. create the TDataServer object that would contain the multiplier and the results. This object is created within the onMaster() part because otherwise there would have been as many dataserver object as there are threads.

  5. create a sampler and a design-of-experiments to read 24 times the given dataset;

  6. run the computations

At the end, once the macro is launched by using the command below, the

root -b -q relauncherCJitFunctionThreadTest.C

XIV.8.3.3. Console

Processing relauncherCJitFunctionThreadTest.C...

--- Uranie v0.0/0 --- Developed with ROOT (6.32.02)
                      Copyright (C) 2013-2024 CEA/DES 
                      Contact: support-uranie@cea.fr 
                      Date: Tue Jan 09, 2024

************************************************
*    Row   * pouet__n_ * multiplie * MultMean. *
************************************************
*        0 *         2 * 9.4179988 * 1551.8978 *
*        1 *         1 * 8.1562084 * 1343.9800 *
*        2 *         0 * 4.1412811 * 682.40030 *
*        3 *         3 * 6.5650956 * 1081.7964 *
*        4 *         5 * 4.7243584 * 778.47978 *
*        5 *         4 * 7.0461633 * 1161.0667 *
*        6 *         6 * 2.5976785 * 428.04546 *
*        7 *         8 * 5.3607876 * 883.35059 *
*        8 *         7 * 7.4193739 * 1222.5644 *
*        9 *         9 * 5.6692661 * 934.18168 *
*       10 *        10 * 1.0476136 * 172.62578 *
*       11 *        11 * 3.5864637 * 590.97748 *
*       12 *        12 * 1.5680924 * 258.39027 *
*       13 *        13 * 4.8760831 * 803.48098 *
*       14 *        14 * 6.0024502 * 989.08374 *
*       15 *        15 * 9.0195417 * 1486.2400 *
*       16 *        16 * 9.9852106 * 1645.3630 *
*       17 *        17 * 3.1396825 * 517.35689 *
*       18 *        18 * 2.4617970 * 405.65491 *
*       19 *        19 * 6.7059225 * 1105.0019 *
*       20 *        20 * 3.9170611 * 645.45333 *
*       21 *        21 * 7.9536348 * 1310.5999 *
*       22 *        22 * 8.6398795 * 1423.6793 *
*       23 *        23 * 2.1125191 * 348.10091 *
************************************************

XIV.8.4. Macro "relauncherCodeFlowrateSequential.C"

XIV.8.4.1. Objective

The goal of this macro is to show how to handle a code with a sequential runner. The flowrate code is provided with Uranie and has been also used and discussed throughout these macros.

XIV.8.4.2. Macro

{

  // Create the TDataServer
  TDataServer *tds = new TDataServer("foo","test");
  tds->fileDataRead("flowrateUniformDesign.dat");

  // Get the attributes
  TAttribute *rw = tds->getAttribute("rw");
  TAttribute *r = tds->getAttribute("r");
  TAttribute *tu = tds->getAttribute("tu");
  TAttribute *tl = tds->getAttribute("tl");
  TAttribute *hu = tds->getAttribute("hu");
  TAttribute *hl = tds->getAttribute("hl");
  TAttribute *l = tds->getAttribute("l");
  TAttribute *kw = tds->getAttribute("kw");

  // Create the output attribute
  TAttribute *yhat = new TAttribute("yhat");
  TAttribute *d = new TAttribute("d");
  
  // Set the reference input file and the key for each input attributes
  TFlatScript fin("flowrate_input_with_values_rows.in");
  fin.setInputs(8, rw, r, tu, tl, hu, hl, l, kw);

  // The output file of the code
  TFlatResult fout("_output_flowrate_withRow_.dat");
  fout.setOutputs(2, yhat, d);// Passing the attributes to the output file

  // Constructing the code
  TCodeEval mycode( "flowrate -s -r" );    
  mycode.setOldTmpDir();
  mycode.addInputFile(&fin); // Adding the input file
  mycode.addOutputFile(&fout); // Adding the output file

  // Create the sequential runner
  TSequentialRun run(&mycode);
  run.startSlave(); //Start the master (necessary even for a sequential)
  if (run.onMaster())
  {
      TLauncher2 lanceur(tds, &run);

      // resolution
      lanceur.solverLoop();
      run.stopSlave(); // Stop the slaves (necessary even for a sequential)
  }

  // Draw the result
  TCanvas *can = new TCanvas("pouet","foo",1);
  tds->Draw("yhat:rw","","colZ");
  
   
}

Here again, a comparison is drawn with the first Relauncher macro (see Section XIV.8.1.2) and only the differences are pointed out. The first obvious one, in the very first steps in defining the dataserver and the attributes, is that there are two output attributes. The second one (called 'd') will not be used here. The second (and only other difference) with respect to the CINT function code, is the assessor creation shown below:

// Set the reference input file and the key for each input attributes
TFlatScript fin("flowrate_input_with_values_rows.in");
fin.setInputs(8, rw, r, tu, tl, hu, hl, l, kw);

// The output file of the code
TFlatResult fout("_output_flowrate_withRow_.dat");
fout.setOutputs(2, yhat, d);// Passing the attributes to the output file

// Constructing the code
TCodeEval mycode( "flowrate -s -r" );    
mycode.setOldTmpDir();
mycode.addInputFile(&fin); // Adding the input file
mycode.addOutputFile(&fout); // Adding the output file	    

The first three lines create the input file instance. It is here a TFlatScript object which can basically be compared to a DataServer (or Salome-table) format of the Launcher module for its organisation (particularly with vectors and strings) but without the compulsory header: the order in which you introduce the attribute is then of uttermost importance. The second block of lines is creating the output file object from the TFlatResult class (the same remark applies to this object).

Finally the assessor itself is created as an instance of the TCodeEval class. The only argument is the command to be run, and it needs at least one input and output file. Apart from that, the runner is created and the rest is crystal clear, leading to the following plot.

XIV.8.4.3. Graph

Figure XIV.84. Representation of the output as a function of the first input with a colZ option

Representation of the output as a function of the first input with a colZ option

XIV.8.5. Macro "relauncherCodeFlowrateSequential_ConstantVar.C"

XIV.8.5.1. Objective

The goal of this macro is to show how to set one of the evaluator's input attribute to a constant value, with a sequential runner. The flowrate code is provided with Uranie and has been also used and discussed throughout these macros.

XIV.8.5.2. Macro


{
  // Create the TDataServer
  TDataServer *tds = new TDataServer("foo","test");

  // Define the attribute that should be considered as constant
  TAttribute r("r");
  
  // Add the study attributes ( min, max and nominal values)
  tds->addAttribute( new TUniformDistribution("rw", 0.05, 0.15));
  tds->addAttribute( new TUniformDistribution("tu", 63070.0, 115600.0));
  tds->addAttribute( new TUniformDistribution("tl", 63.1, 116.0));
  tds->addAttribute( new TUniformDistribution("hu", 990.0, 1110.0));
  tds->addAttribute( new TUniformDistribution("hl", 700.0, 820.0));
  tds->addAttribute( new TUniformDistribution("l", 1120.0, 1680.0));
  tds->addAttribute( new TUniformDistribution("kw", 9855.0, 12045.0));

  // The reference input file
  TString sIn = TString("flowrate_input_with_keys.in");

  int nS=15;
  // Generate the Design of Experiments
  TSampling *sampling = new TSampling(tds, "lhs", nS);
  sampling->generateSample();   

  // Create the input files
  TKeyScript inputFile( sIn.Data() );
  inputFile.addInput(tds->getAttribute("rw"),"Rw");
  inputFile.addInput(&r,"R"); // Add the constant attribute as an input
  inputFile.addInput(tds->getAttribute("tu"),"Tu");
  inputFile.addInput(tds->getAttribute("tl"),"Tl");
  inputFile.addInput(tds->getAttribute("hu"),"Hu");
  inputFile.addInput(tds->getAttribute("hl"),"Hl");
  inputFile.addInput(tds->getAttribute("l"),"L");
  inputFile.addInput(tds->getAttribute("kw"),"Kw"); 

  // Create the output attribute
  TAttribute *yhat = new TAttribute("yhat");
  TAttribute *d = new TAttribute("d");

  // Create the output files
  TKeyResult outputFile("_output_flowrate_withKey_.dat");
  outputFile.addOutput(yhat, "yhat");
  outputFile.addOutput(d, "d");
  
  // Create the user's evaluation function
  TCodeEval eval("flowrate -s -k");
  eval.addInputFile(&inputFile);
  eval.addOutputFile(&outputFile);
  
  // Create the sequential runner
  TSequentialRun run(&eval);
  run.startSlave(); //Start the master (necessary even for a sequential)
  if (run.onMaster())
  {
      TLauncher2 lanceur(tds, &run);
      // State to the master : r is constant with value 108
      // By default the value is not kept in the tds.
      // The third argument says : yes, keep it for bookkeeping
      lanceur.addConstantValue(&r,108,true);

      // resolution
      lanceur.solverLoop();
      run.stopSlave(); // Stop the slaves (necessary even for a sequential)
  }

  tds->scan("*");
  
   
}

Here again, a comparison is drawn with the first Relauncher macro (see Section XIV.8.4.2) and only the differences are pointed out. The first obvious one, in the very first steps in defining the dataserver and the attributes, is that instead of reading a database-file, we are generating a design-of-experiments with one big specificity: all the input attributes are properly defined, but r.

// Define the attribute that should be considered as constant
TAttribute r("r");
  
// Add the study attributes ( min, max and nominal values)
tds->addAttribute( new TUniformDistribution("rw", 0.05, 0.15));
tds->addAttribute( new TUniformDistribution("tu", 63070.0, 115600.0));
//  ....

A simple design-of-experiments is generated and all the input attributes are provided to the input file of the assessor, event the constant one r.

// Create the input files
TKeyScript inputFile( sIn.Data() );
inputFile.addInput(tds->getAttribute("rw"),"Rw");
inputFile.addInput(&r,"R"); // Add the constant attribute as an input
inputFile.addInput(tds->getAttribute("tu"),"Tu");
//...

The rest is fairly common, up to the TMaster-inheriting object specification: the addConstantValue method is called to specify that r is about to be constant for all ongoing estimation, and it provides it value. The last argument states that the value under consideration should be stored in the ntuple of the dataserver object, as shown in the next section (from the scan method).

TLauncher2 lanceur(tds, &run);
// State to the master: r is constant with value 108
// By default the value is not kept in the tds.
// The third argument says: yes, keep it for bookkeeping
lanceur.addConstantValue(&r,108,true);

XIV.8.5.3. Console

***************************************************************************************************************
*    Row   * foo__n *  rw.rw *  tu.tu *  tl.tl *  hu.hu *  hl.hl *    l.l *  kw.kw * yhat.y *    d.d *    r.r *
***************************************************************************************************************
*        0 *      0 * 0.1495 * 111790 * 73.820 * 990.90 * 779.83 * 1474.3 * 11220. * 112.01 * 3588.9 *    108 *
*        1 *      1 * 0.1394 * 104140 * 95.150 * 1101.5 * 707.21 * 1422.7 * 11493. * 193.62 * 6597.5 *    108 *
*        2 *      2 * 0.0557 * 95387. * 84.809 * 1056.2 * 752.94 * 1184.6 * 11967. * 29.880 * 330.58 *    108 *
*        3 *      3 * 0.0836 * 74144. * 103.17 * 1051.6 * 819.27 * 1587.4 * 11031. * 35.400 * 2431.1 *    108 *
*        4 *      4 * 0.0586 * 65396. * 72.161 * 1003.1 * 710.34 * 1327.5 * 10484. * 24.990 *   5757 *    108 *
*        5 *      5 * 0.1203 * 92149. * 65.263 * 1031.6 * 797.34 * 1265.5 * 11638. * 97.386 * 1084.8 *    108 *
*        6 *      6 * 0.1319 * 67464. * 93.378 * 1039.4 * 722.54 * 1514.3 * 10996. * 125.33 * 2362.4 *    108 *
*        7 *      7 * 0.1059 * 80448. * 112.87 * 1027.1 * 794.32 * 1555.4 *  11846 * 62.403 * 1116.3 *    108 *
*        8 *      8 * 0.0784 * 100260 * 105.79 * 1072.7 * 767.70 * 1304.1 * 10152. * 45.867 * 521.41 *    108 *
*        9 *      9 * 0.0697 * 105158 * 82.544 * 1020.4 * 726.05 * 1640.3 * 10380. * 28.412 * 2802.5 *    108 *
*       10 *     10 * 0.1252 * 89522. * 100.65 * 1006.2 * 742.35 * 1123.9 * 10743. * 123.70 * 2676.1 *    108 *
*       11 *     11 * 0.1165 * 73139. * 69.083 * 1108.5 * 809.59 * 1199.0 * 10620. * 112.27 * 4991.3 *    108 *
*       12 *     12 * 0.0992 * 86004. * 112.12 * 1069.4 * 763.84 * 1345.2 * 9951.0 * 69.808 * 414.71 *    108 *
*       13 *     13 * 0.0718 * 113775 * 90.580 * 1079.1 * 782.95 * 1416.6 * 10076. * 34.135 * 1016.8 *    108 *
*       14 *     14 * 0.0902 * 83779. * 80.244 * 1090.6 * 734.33 * 1644.2 *  11443 * 63.236 * 2922.3 *    108 *
***************************************************************************************************************

XIV.8.6. Macro "relauncherCodeFlowrateThreaded.C"

XIV.8.6.1. Objective

The goal of this macro is to show how to handle a code run on several threads. In order to this, the usual sequential runner will be removed and another runner will be called to do the job. The flowrate code is provided with Uranie and has been also used and discussed throughout these macros.

XIV.8.6.2. Macro

{

  TAttribute *rw = new TAttribute("rw");
  TAttribute *r = new TAttribute("r");
  TAttribute *tu = new TAttribute("tu");
  TAttribute *tl = new TAttribute("tl");
  TAttribute *hu = new TAttribute("hu");
  TAttribute *hl = new TAttribute("hl");
  TAttribute *l = new TAttribute("l");
  TAttribute *kw = new TAttribute("kw");

  // Create the output attribute
  TAttribute *yhat = new TAttribute("yhat");
  TAttribute *d = new TAttribute("d");
  
  // Set the reference input file and the key for each input attributes
  TFlatScript fin("flowrate_input_with_values_rows.in");
  fin.setInputs(8, rw, r, tu, tl, hu, hl, l, kw);

  // The output file of the code
  TFlatResult fout("_output_flowrate_withRow_.dat");
  fout.setOutputs(2, yhat, d);// Passing the attributes to the output file

  // Constructing the code
  TCodeEval mycode( "flowrate -s -r" );    
  mycode.setOldTmpDir();
  mycode.addInputFile(&fin); // Adding the input file
  mycode.addOutputFile(&fout); // Adding the output file

  // Fix the number of threads
  int nthread = 3;
  // Create the Threaded runner
  TThreadedRun run(&mycode, nthread);
  run.startSlave(); // Start the master 
  if (run.onMaster())
  {
      // Create the TDataServer
      TDataServer *tds = new TDataServer("foo","test");
      mycode.addAllInputs(tds);
      tds->fileDataRead("flowrateUniformDesign.dat", kFALSE, kTRUE);

      TLauncher2 lanceur(tds, &run);

      // resolution
      lanceur.solverLoop();
      run.stopSlave(); // Stop the slaves (necessary even for a sequential)
  
      // Draw the result
      TCanvas *can = new TCanvas("pouet","foo",1);
      tds->Draw("yhat:rw","","colZ");  }  
  
}

The only difference when comparing this macro to the previous one (see Section XIV.8.4.2) is the runner creation:

// Fix the number of threads
int nthread = 3;
// Create the Threaded runner
TThreadedRun run(&mycode, nthread);

The TSequentialRun object becomes a TThreadedRun object whose construction request on top of the assessor, the number of threads to be used. Apart from that, the master is created and the rest is crystal clear, leading to the following plot.

XIV.8.6.3. Graph

Figure XIV.85. Representation of the output as a function of the first input with a colZ option

Representation of the output as a function of the first input with a colZ option

XIV.8.7. Macro "relauncherCodeFlowrateMPI.C"

XIV.8.7.1. Objective

The goal of this macro is to show how to handle a code run on several threads with another memory paradigm: when the TThreadedRun instance is relying on shared memory (leading to possible thread-safe problem, as discussed in Section VIII.4.2), the MPI implementation is based on the separation of the memory. The communication is made through messages. In order to this, the usual sequential runner will be removed and another runner will be called to do the job. The flowrate code is provided with Uranie and has been also used and discussed throughout these macros.

XIV.8.7.2. Macro

using namespace URANIE::DataServer;
using namespace URANIE::Relauncher;
using namespace URANIE::MpiRelauncher;

void relauncherCodeFlowrateMPI()
{

    TAttribute *rw = new TAttribute("rw");
    TAttribute *r = new TAttribute("r");
    TAttribute *tu = new TAttribute("tu");
    TAttribute *tl = new TAttribute("tl");
    TAttribute *hu = new TAttribute("hu");
    TAttribute *hl = new TAttribute("hl");
    TAttribute *l = new TAttribute("l");
    TAttribute *kw = new TAttribute("kw");

    // Create the output attribute
    TAttribute *yhat = new TAttribute("yhat");
    TAttribute *d = new TAttribute("d");
    
    // Set the reference input file and the key for each input attributes
    TFlatScript fin("flowrate_input_with_values_rows.in");
    fin.setInputs(8, rw, r, tu, tl, hu, hl, l, kw);

    // The output file of the code
    TFlatResult fout("_output_flowrate_withRow_.dat");
    fout.setOutputs(2, yhat, d);

    // Instanciation de mon code
    TCodeEval mycode("flowrate -s -r");
    //mycode.setOldTmpDir();
    mycode.addInputFile(&fin);
    mycode.addOutputFile(&fout);

    // Create the MPI runner
    TMpiRun run(&mycode);
    run.startSlave();
    if (run.onMaster())
    {
	// Define the DataServer
	TDataServer tds("tdsflowrate", "Design of Experiments for Flowrate");
	mycode.addAllInputs(&tds);
	tds.fileDataRead("flowrateUniformDesign.dat", kFALSE, kTRUE);

	TLauncher2 lanceur(&tds, &run);

        // resolution
        lanceur.solverLoop();

        tds.exportData("_output_testFlowrateMPI_.dat");

        run.stopSlave();
    }

    delete rw;
    delete r;
    delete tl;
    delete tu;
    delete hl;
    delete hu;
    delete l;
    delete kw;
    delete yhat;
    delete d;
}

Here the first difference when comparing this macro to the previous one (see Section XIV.8.6.2) is the runner creation:

// Create the MPI runner
TMpiRun run(&mycode); 

The TThreadedRun object becomes a TMpiRun object whose construction only requests a pointer to the assessor. Apart from that, the code is very similar, the only difference being the way to call this macro. It should not be run with the usual command:

root -l relauncherCodeFlowrateMPI.C

Instead, the command line should start with the mpirun command as such:

mpirun -np N root -l -b -q relauncherCodeFlowrateMPI.C

where the N part should be replaced by the number of requested threads. Once run, this macro also leads to the following plots.

XIV.8.7.3. Graph

Figure XIV.86. Representation of the output as a function of the first input with a colZ option

Representation of the output as a function of the first input with a colZ option

XIV.8.8. Macro "relauncherCodeFlowrateMpiStandalone.C"

XIV.8.8.1. Objective

The goal of this macro is to show how to handle a code run on several threads with another memory paradigm: when the TThreadedRun instance is relying on shared memory (leading to possible thread-safe problem, as discussed in Section VIII.4.2), the MPI implementation is based on the separation of the memory. The communication is made through messages. In order to this, the usual sequential runner will be removed and another runner will be called to do the job. The flowrate code is provided with Uranie and has been also used and discussed throughout these macros.

Warning

This macro is different from the one discussed previously in Section XIV.8.7 as here, one wants to handle MPI through standalone compilation (so C++ without using the ROOT interface)

XIV.8.8.2. Macro

#include "TAttribute.h"
#include "TCodeEval.h"
#include "TFlatScript.h"
#include "TDataServer.h"
#include "TFlatResult.h"
#include "TMpiRun.h"
#include "TLauncher2.h"

void usage(const char *scmd)
{
    printf("\n");
    printf("Usage: %s initType Type \n", scmd);
    printf("\n");
    printf("  Specific method to test standalone MPI distribution with reoptimizer dummy case.\n");
    printf("\t   initType     : The MPI initialisation chosen (explicit <-> 0 or implicit <-> 1) \n");    
    printf("\n");
}

int main(int argc, char **argv)
{    

    int initType=0; // explicit <-> 0 or implicit <-> 1
    if(argc>1)
    {
        for (int iarg=1; iarg<argc; iarg++)
        {
            if( string(argv[iarg]) == "initType") { initType=atof(argv[iarg+1]); iarg++;}
            else
            {
                cout<<"==============================================================="<<endl;
                cout<<"Don't know the following option ? "<<string(argv[iarg])<<endl;
                usage("mpirun -np N ./${EXECNAME}");
                cout<<"==============================================================="<<endl;
                return 0;
            }
        }
    }

    // variables
    URANIE::DataServer::TAttribute *rw = new URANIE::DataServer::TAttribute("rw");
    URANIE::DataServer::TAttribute *r = new URANIE::DataServer::TAttribute("r");
    URANIE::DataServer::TAttribute *tu = new URANIE::DataServer::TAttribute("tu");
    URANIE::DataServer::TAttribute *tl = new URANIE::DataServer::TAttribute("tl");
    URANIE::DataServer::TAttribute *hu = new URANIE::DataServer::TAttribute("hu");
    URANIE::DataServer::TAttribute *hl = new URANIE::DataServer::TAttribute("hl");
    URANIE::DataServer::TAttribute *l = new URANIE::DataServer::TAttribute("l");
    URANIE::DataServer::TAttribute *kw = new URANIE::DataServer::TAttribute("kw");

    // Create the output attribute
    URANIE::DataServer::TAttribute *yhat = new URANIE::DataServer::TAttribute("yhat");
    URANIE::DataServer::TAttribute *d = new URANIE::DataServer::TAttribute("d");

    // Set the reference input file and the key for each input attributes
    URANIE::Relauncher::TFlatScript fin("flowrate_input_with_values_rows.in");
    fin.setInputs(8, rw, r, tu, tl, hu, hl, l, kw);

    // The output file of the code
    URANIE::Relauncher::TFlatResult fout("_output_flowrate_withRow_.dat");
    fout.setOutputs(2, yhat, d);

    // Instanciation de mon code
    URANIE::Relauncher::TCodeEval mycode("flowrate -s -r");
    //mycode.setOldTmpDir();
    mycode.addInputFile(&fin);
    mycode.addOutputFile(&fout);

    // Create a runner
    URANIE::MpiRelauncher::TMpiRun *run=NULL;    
    if(initType==0)
    {
        MPI_Init(&argc, &argv);
        run = new URANIE::MpiRelauncher::TMpiRun(&mycode);
    }
    else if(initType==1)
        run = new URANIE::MpiRelauncher::TMpiRun(&mycode, &argc, &argv);

    run->startSlave();    
    if(run->onMaster())
    {
        // Create the TDS
        URANIE::DataServer::TDataServer tds("launchFlowrate", "launching flowrate with mpi");
        mycode.addAllInputs(&tds);
	tds.fileDataRead("flowrateUniformDesign.dat", kFALSE, kTRUE);

	URANIE::Relauncher::TLauncher2 lanceur(&tds, run);

        // resolution
        lanceur.solverLoop();

        std::stringstream outname; outname<<"_output_testFlowrateMPIStandalone_"<<initType<<"_.dat";
        tds.exportData( outname.str().c_str() );

        run->stopSlave();
    }

    delete run;
    
    return 0;
}

Here there are few differences the most obvious one being the proper C++ structure:

  • there are several includes that has to be included on top the macro in order to provided the needed headers;

  • the main function has to follow a classical form int main(int argc, char **argv) which allows to modify parameters on the fly once compiled (as for the initType integer here through a dedicated loop). The consequence of this signature will be discussed later on;

  • a usage function has been written in order to provide interactive guidelines on how to deal with this code.

Apart from this, from the variable definition to the assessor description, the structure is similar to the one discussed previously (see Section XIV.8.6.2). Once this point is reached there are two ways to construct the runner:

  • with the classical constructor. In this case, as one is not running a ROOT session, the constructor will not found the interactive command parameters and the MPI_Init will not automatically be called, thus meaning that one needs to call this method with the proper parameters as done below:

    MPI_Init(&argc, &argv);
    run = new URANIE::MpiRelauncher::TMpiRun(&mycode);

  • with the new constructor provided with v4.6.0 whose argument are the usual assessor and the interactive parameters:

    run = new URANIE::MpiRelauncher::TMpiRun(&mycode, &argc, &argv);

The final difference is the fact that this code has to be properly compiled, using MPI-complient compilor in order to be able to produce an executable that will be run later on. This can be done like this:

mpicc -o relaunStand relauncherCodeFlowrateMpiStandalone.C `echo $URANIECPPFLAG $URANIELDFLAG` -lstdc++

where mpicc is the c++ mpi-complient compilor, the argument after -o is the executable name, then one can see the input file while the rest is provinding all compiling and linking flag in order to reach the goal. Finally, instead of using the classical root -l command, one will use this line

mpirun -np N ./relaunStand initType XX

where the N part should be replaced by the number of requested threads (always strickly greater than 1) and XX should be replaced either by 0 or 1 depending on whether one wants to use the classical TMpiRun constructor. Once run, this macro also leads to the following plots (both initialisation are shown).

XIV.8.8.3. Graph

Figure XIV.87. Representation of the output as a function of the first input with a colZ option when using either the classical or dedicated constructor

Representation of the output as a function of the first input with a colZ option when using either the classical or dedicated constructor

XIV.8.9. Macro "relauncherCodeFlowrateSequentialFailure.C"

XIV.8.9.1. Objective

The goal of this macro is to show how to handle when a code is returning an error status. Up to version v4.5.0, the input configuration was simply discarded while from any version now, there are discarded but they can be retrieved and store in a dedicated TDataServer object. The code used here is the usual flowrate model which has been modified to return a non zero exit status without producing an output file.

XIV.8.9.2. Macro

{
  // Create the TDataServer
  TDataServer *tds = new TDataServer("foo","test");
  tds->fileDataRead("flowrateUniformDesign.dat");

  // Get the attributes
  TAttribute *rw = tds->getAttribute("rw");
  TAttribute *r = tds->getAttribute("r");
  TAttribute *tu = tds->getAttribute("tu");
  TAttribute *tl = tds->getAttribute("tl");
  TAttribute *hu = tds->getAttribute("hu");
  TAttribute *hl = tds->getAttribute("hl");
  TAttribute *l = tds->getAttribute("l");
  TAttribute *kw = tds->getAttribute("kw");

  // Create the output attribute
  TAttribute *yhat = new TAttribute("yhat");
  TAttribute *d = new TAttribute("d");
  
  // Set the reference input file and the key for each input attributes
  TFlatScript fin("flowrate_input_with_values_rows.in");
  fin.setInputs(8, rw, r, tu, tl, hu, hl, l, kw);

  // The output file of the code
  TFlatResult fout("_output_flowrate_withRow_.dat");
  fout.setOutputs(2, yhat, d);// Passing the attributes to the output file

  // Constructing the code
  TCodeEval mycode( "flowrate -s -rf" );    
  mycode.addInputFile(&fin); // Adding the input file
  mycode.addOutputFile(&fout); // Adding the output file

  // Create the sequential runner
  TSequentialRun run(&mycode);
  run.startSlave(); //Start the master (necessary even for a sequential)

  if (run.onMaster())
  {
      TLauncher2 lanceur(tds, &run);

      // Store the wrong calculation
      TDataServer error("WrongComputations","pouet");
      lanceur.setSaveError(&error);

      // resolution
      lanceur.solverLoop();
      run.stopSlave(); // Stop the slaves (necessary even for a sequential)

      // dump all wrong configurations
      error.getTuple()->SetScanField(-1);
      error.scan("*");
  }
  
  // Draw the result
  TCanvas *can = new TCanvas("pouet","foo",1);
  tds->Draw("hu:hl");
  
   
}

Here there are very few differences with the one already introduced in Section XIV.8.4.2. The first one is obviously the command line which is called using "-rf" argument, the f being introduced for failure.

TCodeEval mycode( "flowrate -s -rf" );

The second difference is the creation of the failure dataserver object in which all wrong configurations will be stored. Once created, it is simply passed to the launcher object through the dedicated method setSaveError:

// Store the wrong calculation
TDataServer error("WrongComputations","pouet");
lanceur.setSaveError(&error)

Once done the code is run and two things are looked at: the fact that in a peculiar area of the input space there are no data anymore (by construction, as shown in Figure XIV.88) and the fact that all configurations are now stored in a dedicated TDataServer object which one can dump on screen with the command line below to obtain the second part of the console output seen in Section XIV.8.9.4

// dump all wrong configurations
error.getTuple()->SetScanField(-1);
error.scan("*");

The first part of the console output shown in Section XIV.8.9.4 is a perfect illustration of the way the relauncher module is discussion failure: the first part is stating that a non-zero return value has been detected

Command cd ${RUNNINGDIR}/URA_XXXXXX ; flowrate -s -rf has returned non-zero exit code (255).
 If any different from 127 (usually for unknown command) and 139 (usually for SIGSEV), the exit code meaning is "command" dependent.

The second part is letting the user know that no output file has been found (a second reason to consider this configuration as a failure).

Cannot open :: ${RUNNINGDIR}/URA_XXXXXX/_output_flowrate_withRow_.dat

This pattern is repeated every time a configuration is wrong.

XIV.8.9.3. Graph

Figure XIV.88. Representation of the output data point when the code is asked to fail on purpose.

Representation of the output data point when the code is asked to fail on purpose.

XIV.8.9.4. Console

Processing relauncherCodeFlowrateSequentialFailure.C...

--- Uranie v0.0/0 --- Developed with ROOT (6.32.02)
                      Copyright (C) 2013-2024 CEA/DES 
                      Contact: support-uranie@cea.fr 
                      Date: Tue Jan 09, 2024

Command cd ${RUNNINGDIR}/URA_XXXXXX ; flowrate -s -rf has returned non-zero exit code (255).
  If any different from 127 (usually for unknown command) and 139 (usually for SIGSEV), the exit code meaning is "command" dependent.
Cannot open :: ${RUNNINGDIR}/URA_XXXXXX/_output_flowrate_withRow_.dat
Command cd ${RUNNINGDIR}/URA_XXXXXX ; flowrate -s -rf has returned non-zero exit code (255).
  If any different from 127 (usually for unknown command) and 139 (usually for SIGSEV), the exit code meaning is "command" dependent.
Cannot open :: ${RUNNINGDIR}/URA_XXXXXX/_output_flowrate_withRow_.dat
Command cd ${RUNNINGDIR}/URA_XXXXXX ; flowrate -s -rf has returned non-zero exit code (255).
  If any different from 127 (usually for unknown command) and 139 (usually for SIGSEV), the exit code meaning is "command" dependent.
Cannot open :: ${RUNNINGDIR}/URA_XXXXXX/_output_flowrate_withRow_.dat
Command cd ${RUNNINGDIR}/URA_XXXXXX ; flowrate -s -rf has returned non-zero exit code (255).
  If any different from 127 (usually for unknown command) and 139 (usually for SIGSEV), the exit code meaning is "command" dependent.
Cannot open :: ${RUNNINGDIR}/URA_XXXXXX/_output_flowrate_withRow_.dat
Command cd ${RUNNINGDIR}/URA_XXXXXX ; flowrate -s -rf has returned non-zero exit code (255).
  If any different from 127 (usually for unknown command) and 139 (usually for SIGSEV), the exit code meaning is "command" dependent.
Cannot open :: ${RUNNINGDIR}/URA_XXXXXX/_output_flowrate_withRow_.dat
************************************************************************************************************************************
*    Row   * WrongComp *     rw.rw *       r.r *     tu.tu *     tl.tl *     hu.hu *     hl.hl *       l.l *     kw.kw * ystar.yst *
************************************************************************************************************************************
*        0 *         4 *    0.0633 *       100 *    115600 *     80.73 *   1075.71 *    751.43 *      1600 *  11106.43 *     28.33 *
*        1 *         5 *    0.0633 *  16733.33 *     80580 *     80.73 *   1058.57 *    785.71 *      1680 *     12045 *      24.6 *
*        2 *         8 *    0.0767 *       100 *    115600 *     80.73 *   1075.71 *    751.43 *      1520 *  10793.57 *     42.44 *
*        3 *        12 *      0.09 *  16733.33 *     63070 *       116 *   1075.71 *    751.43 *      1120 *  11419.29 *     83.77 *
*        4 *        23 *    0.1233 *  16733.33 *     63070 *      63.1 *   1041.43 *    785.71 *      1680 *     12045 *     86.73 *
************************************************************************************************************************************

XIV.8.10. Macro "relauncherCodeMultiTypeKey.C"

XIV.8.10.1. Objective

The objective of this macro is to test the case where vectors and strings are produced as outputs, using the code described in Section XIV.4.22.1, with a Key format, obtained by doing:

multitype -mtKey

The resulting output file, named _output_multitype_mt_Key_.dat looks like:

w1 = nine
v1 = -0.512095
v1 = 0.039669
v1 = -1.3834
v1 = 1.37667
v1 = 0.220672
v1 = 0.633267
v1 = 1.37027
v1 = -0.765636
v2 = 14.1981
v2 = 14.0855
v2 = 10.7848
v2 = 9.45476
v2 = 9.17308
v2 = 6.60804
v2 = 10.0711
v2 = 14.1761
v2 = 10.318
v2 = 12.5095
v2 = 15.6614
v2 = 10.3452
v2 = 9.41101
v2 = 7.47887
f1 = 32.2723
w2 = eight

XIV.8.10.2. Macro Uranie

{

  // Create the TDataServer and create the seed attribute
  TDataServer *tds = new TDataServer("foo", "multitype usecase");
  tds->addAttribute( new TUniformDistribution("seed",0,100000));

  //Create DOE
  TSampling *tsam = new TSampling(tds,"lhs",100);
  tsam->generateSample();

  // Create output attribute pointers
  TAttribute *w1 = new TAttribute("w1", TAttribute::kString);
  TAttribute *w2 = new TAttribute("w2", TAttribute::kString);
  TAttribute *v1 = new TAttribute("v1", TAttribute::kVector);
  TAttribute *v2 = new TAttribute("v2", TAttribute::kVector);
  TAttribute *f1 = new TAttribute("f1");
  
  // Create the input files
  TFlatScript inputFile("multitype_input.dat");
  inputFile.setInputs(1, tds->getAttribute("seed"), "seed");    

  // Create the output files
  TKeyResult outputFile("_output_multitype_mt_Key_.dat");
  outputFile.addOutput(w1, "w1");
  outputFile.addOutput(v1, "v1");
  outputFile.addOutput(v2, "v2");
  outputFile.addOutput(f1, "f1");
  outputFile.addOutput(w2, "w2");
  
  // Create the user's evaluation function
  TCodeEval eval("multitype -mtKey");
  eval.addInputFile(&inputFile); // Add the input file
  eval.addOutputFile(&outputFile); // Add the output file

  //Create the runner
  TSequentialRun runner(&eval);

  // Start the slaves
  runner.startSlave();                                           
  if (runner.onMaster())
  {
  
      // Create the launcher
      TLauncher2 lanceur(tds, &runner);
      lanceur.solverLoop();
        
      // Stop the slave processes
      runner.stopSlave();
      
  }        

  //Produce control plot
  TCanvas *Can = new TCanvas("Can","Can",10,10,1000,800);
  TPad *pad = new TPad("pad","pad",0, 0.03, 1, 1); pad->Draw(); pad->cd();
  tds->drawPairs("w1:v1:v2:f1:w2");
  
}
  

The beginning of the code is pretty common to many other macros: creating a dataserver and input attributes (here the only one is the seed, needed for the random generator to produce vectors and strings). A sampling object is created as well to produce a 100-points design-of-experiments and the output attributes are created, as such:

// Create output attribute pointers
TAttribute *w1 = new TAttribute("w1", TAttribute::kString);
TAttribute *w2 = new TAttribute("w2", TAttribute::kString);
TAttribute *v1 = new TAttribute("v1", TAttribute::kVector);
TAttribute *v2 = new TAttribute("v2", TAttribute::kVector);
TAttribute *f1 = new TAttribute("f1");  

This is where the specificity of the vector and string is precised. It will be passed on to the rest of the code automatically. The rest is common to many relauncher job (for instance Section XIV.8.4) with the only difference being that the output file is a key type one. It results in the following plots.

XIV.8.10.3. Graph

Figure XIV.89. Graph of the macro "relauncherCodeMultiTypeKey.C"

Graph of the macro "relauncherCodeMultiTypeKey.C"

XIV.8.11. Macro "relauncherCodeMultiTypeKeyEmptyVectors.C"

XIV.8.11.1. Objective

The objective of this macro is to test the case where vectors and strings are produced as outputs, using the code described in Section XIV.4.22.1, with a Key format, obtained by doing:

multitype -mtKey -empty

Unlike what's done to in Section XIV.8.10, the "-empty" allows the code to generate empty vectors and not only vectors whose size would be between 1 and 15 elements. The resulting output file used is a key-format one in a condensate form, named _output_multitype_mt_Key_condensate_.dat looks like:

w1 = nine
v1 = [ -0.512095,0.039669,-1.3834,1.37667,0.220672,0.633267,1.37027,-0.765636 ]
v2 = [ 14.1981,14.0855,10.7848,9.45476,9.17308,6.60804,10.0711,14.1761,10.318,12.5095,15.6614,10.3452,9.41101,7.47887 ]
f1 = 32.2723
w2 = eight

XIV.8.11.2. Macro Uranie

{

  // Create the TDataServer and create the seed attribute
  TDataServer *tds = new TDataServer("foo", "multitype usecase");
  tds->addAttribute( new TUniformDistribution("seed",0,100000));

  //Create DOE
  TSampling *tsam = new TSampling(tds,"lhs",100);
  tsam->generateSample();

  // Create output attribute pointers
  TAttribute *w1 = new TAttribute("w1", TAttribute::kString);
  TAttribute *w2 = new TAttribute("w2", TAttribute::kString);
  TAttribute *v1 = new TAttribute("v1", TAttribute::kVector);
  TAttribute *v2 = new TAttribute("v2", TAttribute::kVector);
  TAttribute *f1 = new TAttribute("f1");
  
  // Create the input files
  TFlatScript inputFile("multitype_input.dat");
  inputFile.setInputs(1, tds->getAttribute("seed"), "seed");    

  // Create the output files
  TKeyResult outputFile("_output_multitype_mt_Key_condensate_.dat");
  outputFile.addOutput(w1, "w1");
  outputFile.addOutput(v1, "v1");
  outputFile.addOutput(v2, "v2");
  outputFile.addOutput(f1, "f1");
  outputFile.addOutput(w2, "w2");
  outputFile.setVectorProperties("[",",","]");
  
  // Create the user's evaluation function
  TCodeEval eval("multitype -mtKey -empty");
  eval.addInputFile(&inputFile); // Add the input file
  eval.addOutputFile(&outputFile); // Add the output file

  //Create the runner
  TSequentialRun runner(&eval);

  // Start the slaves
  runner.startSlave();                                           
  if (runner.onMaster())
  {
  
      // Create the launcher
      TLauncher2 lanceur(tds, &runner);
      lanceur.solverLoop();

      // Stop the slave processes
      runner.stopSlave();
      
  }        
  
  //Produce control plot
  TCanvas *Can = new TCanvas("Can","Can",10,10,1000,800);
  TPad *pad = new TPad("pad","pad",0, 0.03, 1, 1); pad->Draw(); pad->Divide(1,2);
  pad->cd(1);
  tds->getTuple()->SetLineColor(2); tds->getTuple()->SetLineWidth(2);
  tds->Draw("size__v1");
  pad->cd(2);
  tds->Draw("size__v2");
  
}
  

The beginning of the code is pretty common to the macro already discussed in Section XIV.8.10.2. Apart from the command difference discussed in the objective above through the "-empty" argument, the main difference with previous macro is the way the output file is declared. Despite from changing the name, the vector properties are set by calling the setVectorProperties method to emphasize how to read the information.

// Create the output files
TKeyResult outputFile("_output_multitype_mt_Key_condensate_.dat");
outputFile.addOutput(w1, "w1");
outputFile.addOutput(v1, "v1");
outputFile.addOutput(v2, "v2");
outputFile.addOutput(f1, "f1");
outputFile.addOutput(w2, "w2");
outputFile.setVectorProperties("[",",","]");

Apart from this, the code is smooth and the final results one can be interested in the size of the vectors produced when empty vectors are allowed. This is produced though the following lines, and the resulting plots are shown in Figure XIV.90.

//Produce control plot
TCanvas *Can = new TCanvas("Can","Can",10,10,1000,800);
TPad *pad = new TPad("pad","pad",0, 0.03, 1, 1); pad->Draw(); pad->Divide(1,2);
pad->cd(1);
tds->getTuple()->SetLineColor(2); tds->getTuple()->SetLineWidth(2);
tds->Draw("size__v1");
pad->cd(2);
tds->Draw("size__v2");

If the output file was not properly formatted, then one can have issues with this specific case (empty vectors). The consequences are shown in Section XIV.8.12.

XIV.8.11.3. Graph

Figure XIV.90. Graph of the macro "relauncherCodeMultiTypeKeyEmptyVectors.C"

Graph of the macro "relauncherCodeMultiTypeKeyEmptyVectors.C"

XIV.8.12. Macro "relauncherCodeMultiTypeKeyEmptyVectorsAsFailure.C"

XIV.8.12.1. Objective

The objective of this macro is to test the case where vectors and strings are produced as outputs, using the code described in Section XIV.4.22.1, with a Key format, obtained by doing:

multitype -mtKey -empty

Unlike what's done to in Section XIV.8.10, the "-empty" allows the code to generate empty vectors and not only vectors whose size would be between 1 and 15 elements. The resulting output file used is a key-format in a very row form, meaning that evey new element of the vectors are written as a new key-line. This file, named _output_multitype_mt_Key_.dat could looks like this:

w1 = nine
v1 = -0.512095
v1 = 0.039669
v1 = -1.3834
v1 = 1.37667
v1 = 0.220672
v1 = 0.633267
v1 = 1.37027
v1 = -0.765636
v2 = 14.1981
v2 = 14.0855
v2 = 10.7848
v2 = 9.45476
v2 = 9.17308
v2 = 6.60804
v2 = 10.0711
v2 = 14.1761
v2 = 10.318
v2 = 12.5095
v2 = 15.6614
v2 = 10.3452
v2 = 9.41101
v2 = 7.47887
f1 = 32.2723
w2 = eight

XIV.8.12.2. Macro Uranie

{
  // Create the TDataServer and create the seed attribute
  TDataServer *tds = new TDataServer("foo", "multitype usecase");
  tds->addAttribute( new TUniformDistribution("seed",0,100000));

  //Create DOE
  TSampling *tsam = new TSampling(tds,"lhs",100);
  tsam->generateSample();

  // Create output attribute pointers
  TAttribute *w1 = new TAttribute("w1", TAttribute::kString);
  TAttribute *w2 = new TAttribute("w2", TAttribute::kString);
  TAttribute *v1 = new TAttribute("v1", TAttribute::kVector);
  TAttribute *v2 = new TAttribute("v2", TAttribute::kVector);
  TAttribute *f1 = new TAttribute("f1");
  
  // Create the input files
  TFlatScript inputFile("multitype_input.dat");
  inputFile.setInputs(1, tds->getAttribute("seed"), "seed");    

  // Create the output files
  TKeyResult outputFile("_output_multitype_mt_Key_.dat");
  outputFile.addOutput(w1, "w1");
  outputFile.addOutput(v1, "v1");
  outputFile.addOutput(v2, "v2");
  outputFile.addOutput(f1, "f1");
  outputFile.addOutput(w2, "w2");
  
  // Create the user's evaluation function
  TCodeEval eval("multitype -mtKey -empty");
  eval.addInputFile(&inputFile); // Add the input file
  eval.addOutputFile(&outputFile); // Add the output file

  //Create the runner
  TSequentialRun runner(&eval);

  // Start the slaves
  runner.startSlave();                                           
  if (runner.onMaster())
  {
  
      // Create the launcher
      TLauncher2 lanceur(tds, &runner);
      
      // Store the wrong calculation
      TDataServer error("WrongComputations","pouet");
      lanceur.setSaveError(&error);

      lanceur.solverLoop();

      // dump all wrong configurations
      cout<<"\nFailed configurations: "<<endl;
      error.getTuple()->SetScanField(-1);
      error.scan("*");
        
      // Stop the slave processes
      runner.stopSlave();
      
  }          
  //Produce control plot
  TCanvas *Can = new TCanvas("Can","Can",10,10,1000,800);
  TPad *pad = new TPad("pad","pad",0, 0.03, 1, 1); pad->Draw(); pad->cd();
  tds->drawPairs("w1:v1:v2:f1:w2");
  
}
  

The beginning of the code is pretty common to the macro already discussed in Section XIV.8.10.2. Apart from the command difference discussed in the objective above through the "-empty" argument, the main difference with previous macro is the failure dataserver declaration and the output console that would be discussed later-on. The former is done through the following lines:

// Store the wrong calculation
TDataServer error("WrongComputations","pouet");
lanceur.setSaveError(&error);

Once the code is run, the configuration leading to empty vectors are gathered in the failure dataserver and dumped on screen through the following lines:

// dump all wrong configurations
error.getTuple()->SetScanField(-1);
error.scan("*");

The final part is the way to represent the results: as for the use-case macro discussed in Section XIV.8.10, all data are plotted in a pair plot and this is summarised in Figure XIV.91. From this picture one should really pay attention to the number of entries to spot that some configuration are missing. Luckily when looking at the console in Section XIV.8.12.4. This time (unlike the failure in Section XIV.8.9) the code is returning a zero output status (because the code actually worked fine) but as from time to time one the two vectors is empty, no entry is written in the output whose format is too simple (as it consist only in dumping vector elements by elements) this is why the only message is the fact that, from time to time, one vector information is missing.

XIV.8.12.3. Graph

Figure XIV.91. Graph of the macro "relauncherCodeMultiTypeKeyEmptyVectorsAsFailure.C"

Graph of the macro "relauncherCodeMultiTypeKeyEmptyVectorsAsFailure.C"

XIV.8.12.4. Console

Processing relauncherMultiTypeKeyEmptyVectorsAsFailure.C...

--- Uranie v0.0/0 --- Developed with ROOT (6.32.02)
                      Copyright (C) 2013-2024 CEA/DES 
                      Contact: support-uranie@cea.fr 
                      Date: Tue Jan 09, 2024

TKeyResult(_output_multitype_mt_Key_.dat): v1 Not found
TKeyResult(_output_multitype_mt_Key_.dat): v2 Not found
TKeyResult(_output_multitype_mt_Key_.dat): v1 Not found
TKeyResult(_output_multitype_mt_Key_.dat): v2 Not found
TKeyResult(_output_multitype_mt_Key_.dat): v1 Not found
TKeyResult(_output_multitype_mt_Key_.dat): v2 Not found
TKeyResult(_output_multitype_mt_Key_.dat): v1 Not found
TKeyResult(_output_multitype_mt_Key_.dat): v1 Not found
TKeyResult(_output_multitype_mt_Key_.dat): v2 Not found
TKeyResult(_output_multitype_mt_Key_.dat): v2 Not found
TKeyResult(_output_multitype_mt_Key_.dat): v1 Not found
TKeyResult(_output_multitype_mt_Key_.dat): v1 Not found
TKeyResult(_output_multitype_mt_Key_.dat): v1 Not found
TKeyResult(_output_multitype_mt_Key_.dat): v2 Not found

Failed configurations: 
************************************
*    Row   * WrongComp * seed.seed *
************************************
*        0 *         9 *  93759.29 *
*        1 *        33 * 74051.957 *
*        2 *        35 * 71909.957 *
*        3 *        50 * 4183.9188 *
*        4 *        54 * 41806.234 *
*        5 *        57 * 28298.703 *
*        6 *        66 * 64903.722 *
*        7 *        69 * 47690.947 *
*        8 *        73 * 89415.222 *
*        9 *        79 * 30656.411 *
*       10 *        84 * 31627.094 *
*       11 *        86 * 63698.481 *
*       12 *        89 * 13461.926 *
*       13 *        99 * 52994.014 *
************************************

XIV.8.13. Macro "relauncherCodeReadMultiType.C"

XIV.8.13.1. Objective

The objective of this macro is to test the case where vectors and strings are used as inputs, using the code described in Section XIV.4.22.2, with a Key format, obtained by doing:

multitype -ReadmtKey

The input values will be read from a database which is produced with the multitype -mt code, as no sampling is available yet to produce vectors and strings. The database file is readmultitype_sampling.dat which looks like this:

#NAME: foo
#TITLE: TDS for flowrate
#DATE: Mon Oct  3 23:50:34 2016
#COLUMN_NAMES: v1| w1| v2| w2| f1| foo__n__iter__
#COLUMN_TYPES: V|S|V|S|D|D

-6.901933299378e-02,-1.292435959913e-01,4.558876683004e-01,5.638486368789e-01,-4.767582766745e-02,7.102109543136e-03,2.819049677902e-01,-2.019788081790e+00,-2.604401028584e+00,-1.617682380292e+00,2.894560949798e-02,-3.493905850261e-01 six 1.142449011404e+01,7.318948216271e+00,1.502260859231e+01,6.041193793062e+00,6.729445145907e+00,1.128096968597e+01 zero 3.425632316777e+01 0.000000000000e+00 
-6.923200061823e-01,-4.798721931875e-01,-1.329893204384e+00,1.292933726829e+00 zero 1.249911290435e+01,6.309239169117e+00,1.596653626442e+01,5.500878012739e+00,1.322535550082e+01,7.070984389647e+00,1.708574150702e+00,1.265915339220e+01 two 4.295175025115e+01 1.000000000000e+00 
5.773813268848e-01,-3.512405673973e-01,-6.870089014992e-01,1.273074555211e-01 nine 1.242682578759e+01,1.109680842701e+01,1.670410641828e+01,7.296321908492e+00,8.732800753443e+00,1.262906549132e+01,8.882310687564e+00,1.104280818003e+01 five 5.591437936893e+01 2.000000000000e+00 
5.518508915499e-01,2.438158138873e-01,1.111784497742e+00,-1.517566514667e+00,7.146879916125e-01,2.328439269321e+00,-1.251913839951e+00,8.876684186954e-01,-1.383023165632e+00,-8.192089693621e-01,-1.079524713568e-01,6.595650273375e-01,-2.275345802432e-03,1.304354557600e+00 nine 1.021975159505e+01,4.995433740783e+00,1.108628156181e+01,1.041110604995e+01,1.111365770153e+01,6.365695806343e+00,6.374053973239e+00,6.854423942510e+00,7.144262333164e+00 two 4.093776591421e+01 3.000000000000e+00 
2.403942476958e-01,6.868091212609e-01,-1.561012830108e+00,1.937806684989e+00,-1.465851888061e+00,5.367279844359e-02,-1.263005327899e+00,-1.132259472701e+00 two 7.382048319627e+00,5.874867917970e+00,1.158191378461e+01,1.073321314846e+01 six 6.980549752305e+01 4.000000000000e+00 
2.220485143391e+00,-5.787212569267e-01,8.843648237689e-01,2.020662891124e+00,1.066403357312e+00,-5.817432767992e-01,3.063023900800e-01,-7.393588637933e-01 two 2.049656723853e+00,9.679003878866e+00,7.338089623518e+00,1.235630702472e+01,1.509238505697e+01,1.034077492413e+01,1.116077550501e+01,7.179221834787e+00,1.582041236432e+01,9.204085091129e+00,4.707490792498e+00,1.618155764288e+01 five 3.507773555061e+01 5.000000000000e+00 
8.908373817765e-01,-2.446355046704e-01,-1.900125532005e+00 seven 1.351254851860e+01,9.297087139459e+00,1.130966904782e+01,1.219245848701e+01,1.012996566249e+01,7.150071600452e+00,1.097549218518e+01,1.443074761657e+01 five 4.464560504112e+01 6.000000000000e+00 
-2.514644600888e+00,1.633579305804e+00 one 1.229098312451e+01,1.013486836958e+01,1.243386772880e+01,1.071783135260e+01,1.453735777922e+01,7.995593455015e+00,9.753966962919e+00,5.924583770352e+00,6.187713988125e+00,1.061975242996e+01,6.650425922126e+00 four 4.553396475968e+01 7.000000000000e+00 
-1.347811599520e+00,-1.259450135534e+00,1.812553405758e+00 five 7.717018655412e+00,1.053283796180e+01,7.404059210327e+00 eight 6.695868880279e+01 8.000000000000e+00 
-1.258360863204e-01,-9.000566818602e-01,7.039146852797e-01,1.015917277706e+00,-2.397650482929e-01 four 4.346717386417e+00,1.033024889324e+01,7.183787459050e+00,8.742095837835e+00,1.277095440277e+01,8.685683828779e+00,9.321006265935e+00,6.353438157123e+00,8.552570119034e+00 six 4.381313066586e+01 9.000000000000e+00 

For every pattern, an input file is created with the Key condensate format, as the other key format is not practical (and usable). This input file looks like this:

w1 = nine
v1 = [ -0.512095,0.039669,-1.3834,1.37667,0.220672,0.633267,1.37027,-0.765636 ]
v2 = [ 14.1981,14.0855,10.7848,9.45476,9.17308,6.60804,10.0711,14.1761,10.318,12.5095,15.6614,10.3452,9.41101,7.47887 ]
f1 = 32.2723
w2 = eight

The resulting output file, named _output_multitype_readmt_Key_.dat looks like:

thev1 = -0.2397650482929
thev2 = 9.321006265935

XIV.8.13.2. Macro Uranie

{
  //inputs
  TDataServer tds("foo","TDS for flowrate");
  tds.fileDataRead("readmultitype_sampling.dat");

  // Input attribute
  TAttribute *w1 = tds.getAttribute("w1");
  TAttribute *w2 = tds.getAttribute("w2");
  TAttribute *v1 = tds.getAttribute("v1");
  TAttribute *v2 = tds.getAttribute("v2");
  TAttribute *f1 = tds.getAttribute("f1");
          
  // output attribute
  TAttribute *thev1 = new TAttribute("thev1");
  TAttribute *thev2 = new TAttribute("thev2");
  gSystem->Exec("multitype -mtKey");
   // Create the output files
  TKeyScript inputFile("_output_multitype_mt_Key_condensate_.dat");
  inputFile.setInputs(5, w1, "w1", v1, "v1", v2, "v2", f1, "f1", w2, "w2");

  // Create the output files
  TKeyResult outputFile("_output_multitype_readmt_Key_.dat");
  outputFile.addOutput(thev1, "thev1");
  outputFile.addOutput(thev2, "thev2");

  // Create the user's evaluation function
  TCodeEval eval("multitype -ReadmtKey");
  eval.addInputFile(&inputFile);
  eval.addOutputFile(&outputFile);

  TSequentialRun runner(&eval);
  //TThreadedRun runner(&eval,2);
    
  runner.startSlave();                                           
  if (runner.onMaster())
    {      
      
      // Create the launcher
      TLauncher2 lanceur(&tds, &runner);
      lanceur.solverLoop();
        
      // Stop the slave processes
      runner.stopSlave();

    }

  tds.Scan("thev1:thev2");
  
}

The code is pretty straightforward, the fact that input attributes are vectors and strings is explained in the input file readmultitype_sampling.dat. One line is added to be sure that an example of input file is present (the file _output_multitype_mt_Key_condensate_.dat) by calling:

gSystem->Exec("multitype -mtKey");

The rest is very common and a screenshot of the result displayed in console is provided in the following subsection.

XIV.8.13.3. Console

Processing relauncherCodeReadMultiType.C...
************************************
*    Row   *     thev1 *     thev2 *
************************************
*        0 * 0.2819049 * 11.424490 *
*        1 * -0.692320 * 15.966536 *
*        2 * -12345678 * 12.629065 *
*        3 * -0.819208 * 11.086281 *
*        4 * -1.561012 * -12345678 *
*        5 * 0.8843648 * 10.340774 *
*        6 * -12345678 * 7.1500716 *
*        7 * 1.6335793 * 14.537357 *
*        8 * -12345678 * -12345678 *
*        9 * -0.239765 * 9.3210062 *
************************************

XIV.8.14. Macro "relauncherComposeMultitypeAndReadMultiType.C"

XIV.8.14.1. Objective

The objective of this macro is to to combine two different assessor in a chain, so that output attributes of the first assessor is the input attributes of the second one. This example combined the multitype code to produce vectors and strings as outputs (as explained in Section XIV.4.22.1) and use these vectors and strings as inputs, using the code described in Section XIV.4.22.2.

XIV.8.14.2. Macro Uranie

{
  //inputs
  TDataServer tds("foo","TDS for multitype");
  tds.fileDataRead("multitype_sampling.dat");

  //output attributes...
  // ... for code 1
  TAttribute *w1 = new TAttribute("w1", URANIE::DataServer::TAttribute::kString);
  TAttribute *w2 = new TAttribute("w2", URANIE::DataServer::TAttribute::kString);
  TAttribute *v1 = new TAttribute("v1", URANIE::DataServer::TAttribute::kVector);
  TAttribute *v2 = new TAttribute("v2", URANIE::DataServer::TAttribute::kVector);
  TAttribute *f1 = new TAttribute("f1");

  // ... for code 2
  TAttribute *thev1 = new TAttribute("thev1");
  TAttribute *thev2 = new TAttribute("thev2");

  // ==================================================================
  // ========================== Code 1 ================================
  // ==================================================================

  // Create the input files
  TFlatScript inputFile1("multitype_input.dat");
  inputFile1.setInputs(1, tds.getAttribute("seed"), "seed");

  // Create the output files
  TKeyResult outputFile1("_output_multitype_mt_Key_.dat");
  outputFile1.addOutput(w1, "w1");
  outputFile1.addOutput(v1, "v1");
  outputFile1.addOutput(v2, "v2");
  outputFile1.addOutput(f1, "f1");
  outputFile1.addOutput(w2, "w2");

  // Create the user's evaluation function
  TCodeEval eval1("multitype -mtKey");
  eval1.addInputFile(&inputFile1);
  eval1.addOutputFile(&outputFile1);

  // ==================================================================
  // ========================== Code 2 ================================
  // ==================================================================

  // Create the output files
  TKeyScript inputFile2("_output_multitype_mt_Key_condensate_.dat");
  inputFile2.setInputs(5, w1, "w1", v1, "v1", v2, "v2", f1, "f1", w2, "w2");

  // Create the output files
  TKeyResult outputFile2("_output_multitype_readmt_Key_.dat");
  outputFile2.addOutput(thev1, "thev1");
  outputFile2.addOutput(thev2, "thev2");

  // Create the user's evaluation function
  TCodeEval eval2("multitype -ReadmtKey");
  eval2.addInputFile(&inputFile2);
  eval2.addOutputFile(&outputFile2);

  // ==================================================================
  // ======================= Composition ==============================
  // ==================================================================

  // Create the composition
  TComposeEval eval;
  // Add the code one-by-one, in the right order
  eval.addEval(&eval1);
  eval.addEval(&eval2);  

  // Create the runner by providing the TComposeEval
  TSequentialRun runner(&eval);
    
  runner.startSlave();                                           
  if (runner.onMaster())
  {      
      
    // Create the launcher
    TLauncher2 lanceur(&tds, &runner);
    lanceur.solverLoop();
    
    // Stop the slave processes
    runner.stopSlave();
    
    tds.exportData("pouet.dat");
    
  }

  tds.Scan("thev1:thev2");
  
}

The code looks very much as the one in two previous examples. First a sample of 10 seed values are read from an input file. Then, output attributes are defined for the first code, as in Section XIV.8.10.2.

TAttribute *w1 = new TAttribute("w1", URANIE::DataServer::TAttribute::kString);
TAttribute *w2 = new TAttribute("w2", URANIE::DataServer::TAttribute::kString);
TAttribute *v1 = new TAttribute("v1", URANIE::DataServer::TAttribute::kVector);
TAttribute *v2 = new TAttribute("v2", URANIE::DataServer::TAttribute::kVector);
TAttribute *f1 = new TAttribute("f1");

The output attributes are defined for the first code, as in in Section XIV.8.13.2.

TAttribute *thev1 = new TAttribute("thev1");
TAttribute *thev2 = new TAttribute("thev2"); 

The assessor are then defined with input and output files and the composition is finally done: it is an assessor in in which we store the other assessors that should be run, in the correct order, as follows:

TComposeEval eval;
// Add the code one-by-one, in the right order
eval.addEval(&eval1);
eval.addEval(&eval2);  

The rest is very common and a screenshot of the result displayed in console is provided in the following subsection.

XIV.8.14.3. Console

Processing relauncherComposeMultitypeAndReadMultiType.C...
************************************
*    Row   *     thev1 *     thev2 *
************************************
*        0 * 0.2819049 * 11.424490 *
*        1 * -0.692320 * 15.966536 *
*        2 * -12345678 * 12.629065 *
*        3 * -0.819208 * 11.086281 *
*        4 * -1.561012 * -12345678 *
*        5 * 0.8843648 * 10.340774 *
*        6 * -12345678 * 7.1500716 *
*        7 * 1.6335793 * 14.537357 *
*        8 * -12345678 * -12345678 *
*        9 * -0.239765 * 9.3210062 *
************************************

XIV.8.15. Macro "relauncherCodeFlowrateSequential_TemporaryVar.C"

XIV.8.15.1. Objective

The goal of this macro is to show how to hide one of the evaluator's attribute and not to store it in the final dataserver. This is considered when a composition is done for instance, in which many variables might be intermediate needed ones, resulting from an assessor and used as input to one of the following, but of no interest to the user at the end. The flowrate code is provided with Uranie and has been also used and discussed throughout these macros.

XIV.8.15.2. Macro


void IncreaseD(double *x, double *y)
{
  y[0] = x[0] + 1; 
}

void relauncherCodeFlowrateSequential_TemporaryVar()
{
  // Create the TDataServer
  TDataServer *tds = new TDataServer("foo","test");

  // Define the attribute that should be considered as constant
  TAttribute r("r");
  
  // Add the study attributes ( min, max and nominal values)
  tds->addAttribute( new TUniformDistribution("rw", 0.05, 0.15));
  tds->addAttribute( new TUniformDistribution("tu", 63070.0, 115600.0));
  tds->addAttribute( new TUniformDistribution("tl", 63.1, 116.0));
  tds->addAttribute( new TUniformDistribution("hu", 990.0, 1110.0));
  tds->addAttribute( new TUniformDistribution("hl", 700.0, 820.0));
  tds->addAttribute( new TUniformDistribution("l", 1120.0, 1680.0));
  tds->addAttribute( new TUniformDistribution("kw", 9855.0, 12045.0));

  // The reference input file
  TString sIn = TString("flowrate_input_with_keys.in");

  int nS=15;
  // Generate the Design of Experiments
  TSampling *sampling = new TSampling(tds, "lhs", nS);
  sampling->generateSample();   

  // Create the input files
  TKeyScript inputFile( sIn.Data() );
  inputFile.addInput(tds->getAttribute("rw"),"Rw");
  inputFile.addInput(&r,"R");
  inputFile.addInput(tds->getAttribute("tu"),"Tu");
  inputFile.addInput(tds->getAttribute("tl"),"Tl");
  inputFile.addInput(tds->getAttribute("hu"),"Hu");
  inputFile.addInput(tds->getAttribute("hl"),"Hl");
  inputFile.addInput(tds->getAttribute("l"),"L");
  inputFile.addInput(tds->getAttribute("kw"),"Kw"); 

  // Create the output attributes
  TAttribute *yhat = new TAttribute("yhat");
  TAttribute *d = new TAttribute("d"); 

  // Create the output files
  TKeyResult outputFile("_output_flowrate_withKey_.dat");
  outputFile.addOutput(yhat, "yhat");
  outputFile.addOutput(d, "d");
  
  // Create the user's evaluation function
  TCodeEval eval1("flowrate -s -k");
  eval1.addInputFile(&inputFile);
  eval1.addOutputFile(&outputFile);

  // Create a second evaluation function that uses d to change it slightly
  TAttribute *incd = new TAttribute("incd"); 
  TCIntEval eval2("IncreaseD");
  eval2.addInput(d);
  eval2.addOutput(incd);

  // Create the composition
  TComposeEval eval;
  // Add the code one-by-one, in the right order
  eval.addEval(&eval1);
  eval.addEval(&eval2);  

  // Create the sequential runner
  TSequentialRun run(&eval);
  run.startSlave(); //Start the master (necessary even for a sequential)
  if (run.onMaster())
  {
      TLauncher2 lanceur(tds, &run);
      // State to the master : d is an output attribute and I'm interested in its value
      // but I don't want to keep it in the end. It might be usefull for another evaluator
      lanceur.addTemporary(d);
      lanceur.addConstantValue(&r,108);
      
      // resolution
      lanceur.solverLoop();
      run.stopSlave(); // Stop the slaves (necessary even for a sequential)
  }

  tds->scan("*");
  
   
}

Here again, a comparison is drawn with the macro in which we set an attribute to a constant value (see Section XIV.8.5), so only the differences are pointed out. The very first one is contained in the beginning lines: a new dummy function, so that we can have a composition of two assessors, this function only adding one to the provided parameter.

void IncreaseD(double *x, double *y)
{
  y[0] = x[0] + 1; 
}

The rest is exactly as for Section XIV.8.5, up to the interface with the newy create function:

// Create a second evaluation function that uses d to change it slightly
TAttribute *incd = new TAttribute("incd"); 
TCIntEval eval2("IncreaseD");
eval2.addInput(d);
eval2.addOutput(incd);

// Create the composition
TComposeEval eval;
// Add the code one-by-one, in the right order
eval.addEval(&eval1);
eval.addEval(&eval2);

A new output attribute is created, called incd for increased d, and the dummy function is defined as taking d as input and incd as output. Then the composition is done by chaining flowrate with the new dummy function. The rest is fairly common, up to the TMaster-inheriting object specification: the addTemporary method is called to specify that d is read from the output of flowrate and can be pass to the rest of the chain, but it will not be kept in the final dataserver. The addConstantValue is also used just changing the final parameters to show that if nothing is specified, then the value of r is not stored and this might be tricky for bookkeeping. The results is shown in the next section (from the scan method) and can be compared to Section XIV.8.5.3 for consistency check.

TLauncher2 lanceur(tds, &run);
// State to the master: d is an output attribute and I'm interested in its value
// but I don't want to keep it in the end. It might be usefull for another evaluator
lanceur.addTemporary(d);
lanceur.addConstantValue(&r,108);

XIV.8.15.3. Console

******************************************************************************************************
*    Row   * foo__n *  rw.rw *  tu.tu *  tl.tl *  hu.hu *  hl.hl *    l.l *  kw.kw * yhat.y * incd.i *
******************************************************************************************************
*        0 *      0 * 0.1495 * 111790 * 73.820 * 990.90 * 779.83 * 1474.3 * 11220. * 112.01 * 3589.9 *
*        1 *      1 * 0.1394 * 104140 * 95.150 * 1101.5 * 707.21 * 1422.7 * 11493. * 193.62 * 6598.5 *
*        2 *      2 * 0.0557 * 95387. * 84.809 * 1056.2 * 752.94 * 1184.6 * 11967. * 29.880 * 331.58 *
*        3 *      3 * 0.0836 * 74144. * 103.17 * 1051.6 * 819.27 * 1587.4 * 11031. * 35.400 * 2432.1 *
*        4 *      4 * 0.0586 * 65396. * 72.161 * 1003.1 * 710.34 * 1327.5 * 10484. * 24.990 *   5758 *
*        5 *      5 * 0.1203 * 92149. * 65.263 * 1031.6 * 797.34 * 1265.5 * 11638. * 97.386 * 1085.8 *
*        6 *      6 * 0.1319 * 67464. * 93.378 * 1039.4 * 722.54 * 1514.3 * 10996. * 125.33 * 2363.4 *
*        7 *      7 * 0.1059 * 80448. * 112.87 * 1027.1 * 794.32 * 1555.4 *  11846 * 62.403 * 1117.3 *
*        8 *      8 * 0.0784 * 100260 * 105.79 * 1072.7 * 767.70 * 1304.1 * 10152. * 45.867 * 522.41 *
*        9 *      9 * 0.0697 * 105158 * 82.544 * 1020.4 * 726.05 * 1640.3 * 10380. * 28.412 * 2803.5 *
*       10 *     10 * 0.1252 * 89522. * 100.65 * 1006.2 * 742.35 * 1123.9 * 10743. * 123.70 * 2677.1 *
*       11 *     11 * 0.1165 * 73139. * 69.083 * 1108.5 * 809.59 * 1199.0 * 10620. * 112.27 * 4992.3 *
*       12 *     12 * 0.0992 * 86004. * 112.12 * 1069.4 * 763.84 * 1345.2 * 9951.0 * 69.808 * 415.71 *
*       13 *     13 * 0.0718 * 113775 * 90.580 * 1079.1 * 782.95 * 1416.6 * 10076. * 34.135 * 1017.8 *
*       14 *     14 * 0.0902 * 83779. * 80.244 * 1090.6 * 734.33 * 1644.2 *  11443 * 63.236 * 2923.3 *
******************************************************************************************************
/language/en