source: trunk/FACT++/src/root2csv.cc@ 20056

Last change on this file since 20056 was 20011, checked in by tbretz, 4 years ago
Had to change to pointers, emplace_back and ofstream do not work in gcc 4.8(\!) ... ARGH\! CentOS 7 technology from the 80s
File size: 25.8 KB
Line 
1#include <boost/regex.hpp>
2#include <boost/filesystem.hpp>
3#include <boost/algorithm/string/join.hpp>
4
5#include "tools.h"
6#include "Time.h"
7#include "Splitting.h"
8
9#include <TROOT.h>
10#include <TSystem.h>
11#include <TChain.h>
12#include <TLeaf.h>
13#include <TError.h>
14#include <TTreeFormula.h>
15#include <TTreeFormulaManager.h>
16
17#include "FileEntry.h"
18
19using namespace std;
20namespace fs = boost::filesystem;
21
22// ------------------------------------------------------------------------
23
24void SetupConfiguration(Configuration &conf)
25{
26 po::options_description control("Root to SQL");
27 control.add_options()
28 ("file", vars<string>()->required(),"The root files to read from")
29 ("out,o", var<string>()->required(), "Output file name")
30 ("force,f", po_switch(), "Force overwrite if output file already exists.")
31 ("append,a", po_switch(), "Append to an existing file (not check for the format is done!)")
32 ("tree,t", var<string>("Events"), "Name of the root tree to convert")
33 ("ignore", vars<string>(), "Ignore the given leaf, if the given regular expression matches")
34 ("alias.*", var<string>(), "Define an alias")
35 ("auto-alias", vars<Configuration::Map>(),"Regular expression to define aliases from the branch names automatically")
36 ("header", var<uint16_t>(uint16_t(0)),"Type of header line (0: preceeding #, 1: without preceeding #, 2: none)")
37 ("add.*", var<string>(), "Define an additional column")
38 ("selector,s", var<string>("1"), "Define a selector for the columns (colums where this evaluates to a value <=0 are discarded)")
39 ("skip", po_switch(), "Discards all default leaves and writes only the columns defined by --add.*")
40 ("first", var<int64_t>(int64_t(0)), "First event to start with (default: 0), mainly for test purpose")
41 ("max", var<int64_t>(int64_t(0)), "Maximum number of events to process (0: all), mainly for test purpose")
42 //("const.*", var<string>(), "Insert a constant number into the given column (--const.mycolumn=5). A special case is `/.../.../`")
43 ("dry-run", po_switch(), "Do not create or manipulate any output file")
44 ;
45
46 po::options_description debug("Debug options");
47 debug.add_options()
48 ("print-ls", po_switch(), "Calls TFile::ls()")
49 ("print-branches", po_switch(), "Print the branches found in the tree")
50 ("print-leaves", po_switch(), "Print the leaves found in the tree (this is what is processed)")
51 ("verbose,v", var<uint16_t>(1), "Verbosity (0: quiet, 1: default, 2: more, 3, ...)")
52 ;
53
54 po::positional_options_description p;
55 p.add("file", -1); // All positional options
56
57 conf.AddOptions(control);
58 conf.AddOptions(Tools::Splitting::options());
59 conf.AddOptions(debug);
60 conf.SetArgumentPositions(p);
61}
62
63void PrintUsage()
64{
65 cout <<
66 "root2csv - Reads data from a root tree and writes a csv file\n"
67 "\n"
68 "For convenience, this documentation uses the extended version of the options, "
69 "refer to the output below to get the abbreviations.\n"
70 "\n"
71 "Similar functionaliy is also provided by root2sql. In addition to root2sql, "
72 "this tool is more flexible in the slection of columns and adds the possibility "
73 "to use formulas (implemented through TTreeFormula) to calculate values for "
74 "additional columns. Note that root can even write complex data like a TH1F "
75 "into a file. Here, only numeric columns are supported.\n"
76 "\n"
77 "Input files are given as positional arguments or with --file. "
78 "As files are read by adding them through TChain::Add, wildcards are "
79 "supported in file names. Note that on the command lines, file names "
80 "with wildcards have to be escaped in quotation marks if the wildcards "
81 "should be evaluated by the program and not by the shell. The output base "
82 "name of the output file(s) is given with --out.\n"
83 "\n"
84 "The format of the first line on the file is defined with the --header option:\n"
85 " 0: '# Col1 Col2 Col3 ...'\n"
86 " 1: 'Col1 Col2 Col3 ...'\n"
87 " 2: first data row\n"
88 "\n"
89 "As default, existing files are not overwritten. To force overwriting use "
90 "--force. To append data to existing files use --append. Note that no "
91 "check is done if this created valid and reasonable files.\n"
92 "\n"
93 "Each root tree has branches and leaves (the basic data types). These leaves can "
94 "be read independently of the classes which were used to write the root file. "
95 "The default tree to read from is 'Events' but the name can be overwritten "
96 "using --tree. The default table name to fill the data into is identical to "
97 "the tree name. It can be overwritten using --table.\n"
98 "\n"
99 "To get a list of the contents (keys and trees) of a root file, you can use --print-ls. "
100 "The name of each column to which data is filled from a leave is obtained from "
101 "the leaves' names. The leave names can be checked using --print-leaves. "
102 "A --print-branches exists for convenience to print only the high-level branches.\n"
103 "\n"
104 "Assuming a leaf with name MHillas.fWidth and a leaf with MHillas.fLength, "
105 "a new column can be added with name Area by\n"
106 " --add.Area='TMath::TwoPi()*MHillas.fWidth*MHillas.fLength'\n"
107 "\n"
108 "To simplify expression, root allows to define aliases, for example\n"
109 " --alias.Width='MHillas.fWidth'\n"
110 " --alias.Length='MHillas.fLength'\n"
111 "\n"
112 "This can then be used to simplyfy the above expression as\n"
113 " --add.Area='TMath::TwoPi()*Width*Length'\n"
114 "\n"
115 "Details on interpretation can be found in the documentation of TFormula::Analyze. "
116 "Random numbers are supported by rndm().\n"
117 "\n"
118#if ROOT_VERSION_CODE < ROOT_VERSION(6,18,00)
119 "Note that functions which require two arguments (e.g. atan2) can be used in "
120 "a column (i.e. with --add) but can not be references in an alias.\n"
121 "\n"
122#endif
123 "Sometimes leaf names might be quite unconvenient like MTime.fTime.fMilliSec or "
124 "just MHillas.fWidth. To allow to simplify column names, regular expressions "
125 "(using boost's regex) can be defined to change the names. Note that these regular "
126 "expressions are applied one by one on each leaf's name. A valid expression could "
127 "be:\n"
128 " --auto-alias=MHillas\\.f/\n"
129 "which would remove all occurances of 'MHillas.f'. This option can be used more than "
130 "once. They are applied in sequence. A single match does not stop the sequence. "
131 "In addition to replacing the column names accordingly, a alias is created "
132 "automatically allowing to access the columns in a formula with the new name.\n"
133 "\n"
134 "Sometimes it might also be convenient to skip a leaf, i.e. not writing the "
135 "coresponding column in the output file. This can be done with "
136 "the --ignore resource. If the given regular expresion yields a match, the "
137 "leaf will be ignored. An automatic alias would still be created and the "
138 "leaf could still be used in a formula. Example\n"
139 " --ignore=ThetaSq\\..*\n"
140 "will skip all leaved which start with 'ThetaSq.'. This directive can be given "
141 "more than once. The so defined ignore list is applied entry-wise, first to the "
142 "raw leaf names, then to the aliased names.\n"
143 "\n"
144 "To select only certain extries from the file, a selector (cut) can be defined "
145 "in the same style as the --add directives, for exmple:\n"
146 " --selector='MHillas.fLength*Width<0'\n"
147 "Note that the selctor is not evaluated to a boolean expression (==0 or !=0) "
148 "but all positive none zero values are considered 'true' (select the entry) "
149 "and all negative values are considered 'fales' (discard the entry).\n"
150 "\n"
151 << Tools::Splitting::usage() <<
152 "\n"
153 "In case of success, 0 is returned, a value>0 otherwise.\n"
154 "\n"
155 "Usage: root2csv input1.root [input2.root ...] -o output.csv [-t tree] [-u] [-f] [-n] [-vN] [-cN]\n"
156 "\n"
157 ;
158 cout << endl;
159}
160
161void ErrorHandlerAll(Int_t level, Bool_t abort, const char *location, const char *msg)
162{
163 if (string(msg).substr(0,24)=="no dictionary for class ")
164 return;
165 if (string(msg).substr(0,15)=="unknown branch ")
166 return;
167
168 DefaultErrorHandler(level, abort, location, msg);
169}
170
171// --------------------------- Write Header --------------------------------
172void WriteHeader(ostream &out, const vector<FileEntry::Container> &vec, const vector<TTreeFormula*> &form, bool skip, uint16_t header)
173{
174 if (header>1)
175 return;
176 if (header==0)
177 out << "# ";
178
179 vector<string> join;
180
181 if (!skip)
182 {
183 for (auto v=vec.cbegin(); v!=vec.cend(); v++)
184 {
185 const size_t N = v->num;
186 for (size_t i=0; i<N; i++)
187 {
188 string name = v->column;
189 if (N!=1)
190 name += "["+to_string(i)+"]";
191 join.emplace_back(name);
192 }
193 }
194 }
195
196 for (auto v=form.cbegin(); v!=form.cend(); v++)
197 join.emplace_back((*v)->GetName());
198
199 out << boost::join(join, " ") << "\n";
200}
201
202int CheckFile(TString &path, bool force, int verbose)
203{
204 gSystem->ExpandPathName(path);
205
206 FileStat_t stat;
207 const Int_t exist = !gSystem->GetPathInfo(path, stat);
208 const Bool_t _write = !gSystem->AccessPathName(path, kWritePermission) && R_ISREG(stat.fMode);
209
210 if (exist)
211 {
212 if (!_write)
213 {
214 cerr << "File '" << path << "' is not writable." << endl;
215 return 8;
216 }
217
218 if (!force)
219 {
220 cerr << "File '" << path << "' already exists." << endl;
221 return 9;
222 }
223
224 if (verbose>0)
225 cerr << "File '" << path << "' will be overwritten." << endl;
226 }
227 return exist ? 0 : -1;
228}
229
230class Formula : public TTreeFormula
231{
232public:
233 void CollectLeaves(set<string> &list, Formula *formula)
234 {
235 if (!formula)
236 return;
237
238 TObject *o = 0;
239
240 TIter NextN(&formula->fLeafNames);
241 while ((o=NextN()))
242 list.emplace(o->GetName());
243
244 TIter NextF(&formula->fAliases);
245 while ((o=NextF()))
246 CollectLeaves(list, static_cast<Formula*>(o));
247 }
248
249 void CollectLeaves(set<string> &list)
250 {
251 CollectLeaves(list, this);
252 }
253
254 Formula(const char* name, const char* formula, TTree* tree)
255 : TTreeFormula(name, formula, tree)
256 {
257 }
258};
259
260int main(int argc, const char* argv[])
261{
262 Time start;
263
264 gROOT->SetBatch();
265 SetErrorHandler(ErrorHandlerAll);
266
267 Configuration conf(argv[0]);
268 conf.SetPrintUsage(PrintUsage);
269 SetupConfiguration(conf);
270
271 if (!conf.DoParse(argc, argv))
272 return 127;
273
274 // ----------------------------- Evaluate options --------------------------
275 const vector<string> files = conf.Vec<string>("file");
276 const string out = conf.Get<string>("out");
277 const string tree = conf.Get<string>("tree");
278
279 const bool force = conf.Get<bool>("force");
280 const bool append = conf.Get<bool>("append");
281 const bool dryrun = conf.Get<bool>("dry-run");
282 const bool skip = conf.Get<bool>("skip");
283
284 const uint16_t verbose = conf.Get<uint16_t>("verbose");
285 const int64_t first = conf.Get<int64_t>("first");
286 const int64_t max = conf.Get<int64_t>("max");
287 const uint16_t header = conf.Get<uint16_t>("header");
288
289 const bool print_ls = conf.Get<bool>("print-ls");
290 const bool print_branches = conf.Get<bool>("print-branches");
291 const bool print_leaves = conf.Get<bool>("print-leaves");
292
293 const auto _ignore = conf.Vec<string>("ignore");
294 const auto autoalias = conf.Vec<Configuration::Map>("auto-alias");
295
296 if (max && first>=max)
297 cerr << "WARNING: Resource `first` (" << first << ") exceeds `max` (" << max << ")" << endl;
298
299 // -------------------------------------------------------------------------
300
301 /*const*/ Tools::Splitting split(conf);
302
303 if (verbose>0)
304 {
305 cout << "\n-------------------------- Evaluating input ------------------------\n";
306 cout << "Start Time: " << Time::sql << Time(Time::local) << endl;
307 }
308
309 if (verbose>0)
310 cout << "Processing Tree: " << tree << endl;
311
312 TChain c(tree.c_str());
313
314 uint64_t cnt = 0;
315 for (const auto &file : files)
316 {
317 const auto add = c.Add(file.c_str(), 0);
318 if (verbose>0)
319 cout << file << ": " << add << " file(s) added." << endl;
320 cnt += add;
321 }
322
323 if (cnt==0)
324 {
325 cerr << "No files found." << endl;
326 return 1;
327 }
328
329 if (verbose>0)
330 cout << cnt << " file(s) found." << endl;
331
332 if (print_ls)
333 {
334 cout << '\n';
335 c.ls();
336 cout << '\n';
337 }
338
339 c.SetMakeClass(1);
340
341 TObjArray *branches = c.GetListOfBranches();
342 TObjArray *leaves = c.GetListOfLeaves();
343
344 if (print_branches)
345 {
346 cout << '\n';
347 branches->Print();
348 }
349
350 const auto entries = c.GetEntriesFast();
351
352 if (verbose>0)
353 cout << branches->GetEntries() << " branches found." << endl;
354
355 if (print_leaves)
356 {
357 cout << '\n';
358 leaves->Print();
359 }
360 if (verbose>0)
361 {
362 cout << leaves->GetEntries() << " leaves found." << endl;
363 cout << entries << " events found." << endl;
364 }
365
366 // ----------------------------------------------------------------------
367
368 if (verbose>0)
369 cout << "\n-------------------------- Evaluating output -----------------------" << endl;
370
371 vector<FileEntry::Container> vec;
372
373/*
374 const auto fixed = conf.GetWildcardOptions("const.*");
375
376 string where;
377 vector<string> vindex;
378 for (auto it=fixed.cbegin(); it!=fixed.cend(); it++)
379 {
380 const string name = it->substr(6);
381 string val = conf.Get<string>(*it);
382
383 boost::smatch match;
384 if (boost::regex_match(val, match, boost::regex("\\/(.+)(?<!\\\\)\\/(.*)(?<!\\\\)\\/")))
385 {
386 const string reg = match[1];
387 const string fmt = match[2];
388
389 val = boost::regex_replace(file, boost::regex(reg), fmt.empty()?"$0":fmt,
390 boost::regex_constants::format_default|boost::regex_constants::format_no_copy);
391
392 if (verbose>0)
393 {
394 cout << "Regular expression detected for constant column `" << *it << "`\n";
395 cout << "Filename converted with /" << reg << "/ to /" << fmt << "/\n";
396 cout << "Filename: " << file << '\n';
397 cout << "Result: " << val << endl;
398 }
399 }
400
401 if (verbose>2)
402 cout << "\n" << val << " [-const-]";
403 if (verbose>1)
404 cout << " (" << name << ")";
405
406 string sqltype = "INT UNSIGNED";
407
408 for (auto m=sqltypes.cbegin(); m!=sqltypes.cend(); m++)
409 if (m->first==name)
410 sqltype = m->second;
411
412 if (!vec.empty())
413 query += ",\n";
414 query += " `"+name+"` "+sqltype+" NOT NULL COMMENT '--user--'";
415
416 vec.emplace_back(name, val);
417 where += " AND `"+name+"`="+val;
418 vindex.emplace_back(name);
419 }
420 */
421
422 set<string> leaflist;
423
424 // ------------------------- Setup all branches in tree -------------------
425
426 TIter Next(leaves);
427 TObject *o = 0;
428 while ((o=Next()))
429 {
430 TLeaf *L = dynamic_cast<TLeaf*>(o);//c.GetLeaf(o->GetName());
431 if (!L)
432 continue;
433
434 string name = L->GetName();
435 if (verbose>2)
436 cout << '\n' << L->GetTitle() << " {" << L->GetTypeName() << "}";
437
438 const string tn = L->GetTypeName();
439
440 // Check if this is a basic type, otherwise skip
441 const auto it = FileEntry::LUT.root(tn);
442 if (it==FileEntry::LUT.cend())
443 {
444 if (verbose>2)
445 cout << " (-n/a-)";
446 continue;
447 }
448
449 // Check if this is an array, otherwise skip
450 if (L->GetLenStatic()!=L->GetLen())
451 {
452 if (verbose>2)
453 cout << " (-skipped-)";
454 continue;
455 }
456
457 // Check for renaming via auto-alias
458 for (auto m=autoalias.cbegin(); m!=autoalias.cend(); m++)
459 name = boost::regex_replace(name, boost::regex(m->first), m->second);
460
461 // if renaming has changed name, define alias
462 if (name!=L->GetName())
463 {
464 if (!c.SetAlias(name.c_str(), L->GetName()))
465 {
466 cerr << "\nERROR - Alias could not be established!\n";
467 cerr << " " << name << " = " << L->GetName() << endl;
468 return 1;
469 }
470 if (verbose==1 || verbose==2)
471 cout << "\nAuto-alias: " << name << " = " << L->GetName();
472 if (verbose>2)
473 cout << " <alias:" << name << ">";
474 }
475
476 // Check whether the un-aliased column is in the ignore list
477 bool found = skip;
478 for (auto b=_ignore.cbegin(); b!=_ignore.cend(); b++)
479 {
480 if (boost::regex_match(L->GetName(), boost::regex(*b)))
481 {
482 found = true;
483 break;
484 }
485 }
486
487 // Check whether the aliased column is in the ignore list
488 for (auto b=_ignore.cbegin(); b!=_ignore.cend(); b++)
489 {
490 if (boost::regex_match(name.c_str(), boost::regex(*b)))
491 {
492 found = true;
493 break;
494 }
495 }
496
497 if (found)
498 {
499 if (verbose>2)
500 cout << " (-ignored-)";
501 continue;
502 }
503
504 vec.emplace_back(L->GetName(), name, it->type, L->GetLenStatic());
505 c.SetBranchAddress(L->GetName(), vec.back().ptr);
506 }
507
508 if (verbose>0)
509 {
510 cout << '\n';
511 if (skip)
512 cout << "Default columns skipped: ";
513 cout << vec.size() << " default leaf/leaves setup for reading." << endl;
514 }
515
516 // ------------------- Configure manual aliases ----------------------------
517
518 const auto valiases = conf.GetWildcardOptions("alias.*");
519 if (verbose>0 && valiases.size()>0)
520 cout << '\n';
521 for (auto it=valiases.cbegin(); it!=valiases.cend(); it++)
522 {
523 const string name = it->substr(6);
524 const string val = conf.Get<string>(*it);
525
526#if ROOT_VERSION_CODE < ROOT_VERSION(6,18,00)
527 if (val.find_first_of(',')!=string::npos)
528 {
529 cerr << "\nERROR - Alias contains comma: Functions with two arguments";
530 cerr << "\ndo not work as alias with root versions less than v6.18!\n";
531 cerr << " " << name << " = " << val << endl;
532 return 2;
533 }
534#endif
535
536 if (verbose>0)
537 cout << "Alias: " << name << " = " << val << endl;
538
539 if (!c.SetAlias(name.c_str(), val.c_str()))
540 {
541 cerr << "\nERROR - Alias could not be established!\n";
542 cerr << " " << name << " = " << val << endl;
543 return 3;
544 }
545 }
546
547 // -------------------------- Configure Selector --------------------------
548
549 TTreeFormulaManager *manager = new TTreeFormulaManager;
550
551 if (verbose>0)
552 cout << "\nSelector: " << conf.Get<string>("selector") << endl;
553
554 Formula selector("Selector", conf.Get<string>("selector").c_str(), &c);
555 if (selector.GetNdim()==0)
556 {
557 cerr << "Compilation of Selector failed!" << endl;
558 return 4;
559 }
560 selector.SetQuickLoad(kTRUE);
561 selector.CollectLeaves(leaflist);
562 manager->Add(&selector);
563
564 // -------------------- Configure additional columns ----------------------
565
566 vector<TTreeFormula*> formulas;
567
568 const auto vform = conf.GetWildcardOptions("add.*");
569 if (verbose>0 && vform.size()>0)
570 cout << '\n';
571 for (auto it=vform.cbegin(); it!=vform.cend(); it++)
572 {
573 const string name = it->substr(4);
574 const string val = conf.Get<string>(*it);
575
576 if (verbose>0)
577 cout << "Adding column: " << name << " = " << val << endl;
578
579 Formula *form = new Formula(name.c_str(), val.c_str(), &c);
580 if (form->GetNdim()==0)
581 {
582 cerr << "Compilation of Column failed!" << endl;
583 return 5;
584 }
585 form->SetQuickLoad(kTRUE);
586 form->CollectLeaves(leaflist);
587 formulas.emplace_back(form);
588 manager->Add(form);
589 }
590 manager->Sync();
591
592 // ------- Set a branch address for all leaves referenced by formulas -------
593
594 for (auto ileaf=leaflist.cbegin(); ileaf!=leaflist.cend(); ileaf++)
595 {
596 // Get Leaf
597 TLeaf *L = c.GetLeaf(ileaf->c_str());
598 if (!L)
599 continue;
600
601 // Adress already set
602 if (L->GetBranch()->GetAddress())
603 continue;
604
605 if (verbose>2)
606 cout << '\n' << L->GetName() << " {" << L->GetTypeName() << "}";
607
608 const string tn = L->GetTypeName();
609
610 // Check if this is a basic type, otherwise skip
611 const auto it = FileEntry::LUT.root(tn);
612 if (it==FileEntry::LUT.cend())
613 {
614 cerr << "Failed to enable branch '" << L->GetName() << "' (type unknown)" << endl;
615 return 6;
616 }
617
618 // Check if this is an array, otherwise skip
619 if (L->GetLenStatic()!=L->GetLen())
620 {
621 cerr << "Failed to enable branch '" << L->GetName() << "' (strange array size)" << endl;
622 return 7;
623 }
624
625 vec.emplace_back(L->GetName(), L->GetName(), it->type, L->GetLenStatic());
626 c.SetBranchAddress(L->GetName(), vec.back().ptr);
627 }
628
629 if (verbose>0)
630 cout << '\n' << formulas.size() << " additional columns setup for writing." << endl;
631
632 // ------------------------- Enable branch reading ------------------------
633
634 UInt_t datatype = 0;
635 const bool has_datatype = c.SetBranchAddress("DataType.fVal", &datatype) >= 0;
636 if (has_datatype && verbose>0)
637 cout << "\nRows with DataType.fVal!=1 will be skipped." << endl;
638
639
640 // Simply switch on all bracnhs for which an address was set
641 c.SetBranchStatus("*", 0);
642
643 Next.Reset();
644 while ((o=Next()))
645 {
646 const TLeaf *L = dynamic_cast<TLeaf*>(o);//c.GetLeaf(o->GetName());
647 if (!L)
648 continue;
649
650 const TBranch *B = L->GetBranch();
651 if (!B)
652 continue;
653
654 if (!B->GetAddress())
655 continue;
656
657 c.SetBranchStatus(B->GetName(), 1);
658 if (verbose>2)
659 cout << "\nEnable Branch: " << B->GetName();
660 }
661 if (verbose>2)
662 cout << endl;
663
664 // -------------------------------------------------------------------------
665
666 if (verbose>0)
667 {
668 cout << '\n';
669 split.print();
670 }
671
672 if (dryrun)
673 {
674 cout << "\nDry run: file output skipped!" << endl;
675 return 0;
676 }
677
678 if (verbose>0)
679 cout << "\n-------------------------- Converting file -------------------------" << endl;
680
681 vector<unique_ptr<ofstream>> outfiles;
682
683 if (split.empty())
684 {
685 TString path(out.c_str());
686 const int rc = CheckFile(path, force, verbose);
687 if (rc>0)
688 return rc;
689
690 outfiles.emplace_back(new ofstream(path.Data(), append ? ios::app : ios::trunc));
691 if (rc==-1 || (force && rc==0 && !append))
692 WriteHeader(*outfiles.back(), vec, formulas, skip, header);
693 }
694 else
695 {
696 for (size_t i=0; i<split.size(); i++)
697 {
698 TString path(out.c_str());
699 path += "-";
700 path += i;
701
702 const int rc = CheckFile(path, force, verbose);
703 if (rc>0)
704 return rc;
705 outfiles.emplace_back(new ofstream(path.Data(), append ? ios::app : ios::trunc));
706 if (rc==-1 || (force && rc==0 && !append))
707 WriteHeader(*outfiles.back(), vec, formulas, skip, header);
708 }
709 }
710
711 // ---------------------------- Write Body --------------------------------
712 size_t count = 0;
713 vector<size_t> ncount(split.empty()?1:split.size());
714
715 auto itree = c.GetTreeNumber();
716
717 const size_t num = max>0 && (max-first)<entries ? (max-first) : entries;
718 for (size_t j=first; j<num; j++)
719 {
720 c.GetEntry(j);
721 if (has_datatype && datatype!=1)
722 continue;
723
724 if (itree != c.GetTreeNumber())
725 {
726 manager->UpdateFormulaLeaves();
727 itree = c.GetTreeNumber();
728 }
729
730 if (selector.GetNdim() && selector.EvalInstance(0)<=0)
731 continue;
732
733 const size_t index = split.index(count++);
734 ncount[index]++;
735
736 vector<string> join;
737
738 if (!skip)
739 {
740 for (auto v=vec.cbegin(); v!=vec.cend(); v++)
741 {
742 const size_t N = v->num;
743 for (size_t i=0; i<N; i++)
744 join.emplace_back(v->fmt(i));
745 }
746 }
747
748 for (auto v=formulas.cbegin(); v!=formulas.cend(); v++)
749 join.emplace_back(to_string((*v)->EvalInstance(0)));
750
751 *outfiles[index] << boost::join(join, " ") << "\n";
752 }
753
754 if (verbose>0)
755 {
756 cout << "\nTotal: N=" << count << " out of " << num << " row(s) written [N=" << first << ".." << num-1 << "]." << endl;
757 for (size_t i=0; i<split.size(); i++)
758 cout << "File " << i << ": nrows=" << ncount[i] << '\n';
759 cout << '\n';
760 }
761
762 if (verbose>0)
763 {
764 cout << "Total execution time: " << Time().UnixTime()-start.UnixTime() << "s.\n";
765 cout << "Success!\n" << endl;
766 }
767 return 0;
768}
Note: See TracBrowser for help on using the repository browser.