Documentation / Manuel utilisateur en C++ :
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).
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.
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.
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.
#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.
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.
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 destroyedtest.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
create the input (multiplier) and output (MultMean) attribute;
create the interface to the
multiply
function;create the interface for the runner;
create the
TDataServer
object that would contain the multiplier and the results. This object is created within theonMaster()
part because otherwise there would have been as many dataserver object as there are threads.create a sampler and a design-of-experiments to read 24 times the given dataset;
run the computations
At the end, once the macro is launched by using the command below, the
root -b -q relauncherCJitFunctionThreadTest.C
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 * ************************************************
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.
{
// 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.
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.
{
// 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);
*************************************************************************************************************** * 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 * ***************************************************************************************************************
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.
{
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.
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.
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.
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)
#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 theinitType
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).
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.
{
// 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.
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 * ************************************************************************************************************************************
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
{
// 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.
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
{
// 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.
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
{
// 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.
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 * ************************************
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
{
//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.
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 * ************************************
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.
{
//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.
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 * ************************************
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.
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);
****************************************************************************************************** * 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 * ******************************************************************************************************