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

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