effects: fill the job object from the original job information except for the actual samples submitted guarantee: basic failures: out of memory II
effects: fill the given sample object with the information from the SampleHandler object, except for the segments that get filled separately guarantee: basic failures: out of memory II
effects: add the given sample with the given segments to the job guarantee: basic failures: out of memory II failures: segment misconfiguration requires: !segments.empty()
effects: generate the splits for the sample by file guarantee: basic failures: out of memory II failures: i/o errors requires: !sample.files.empty() requires: numJobs == rint (numJobs) postcondition: !segments.empty()
effects: generate the splits for the sample guarantee: basic failures: out of memory II failures: i/o errors requires: !sample.files.empty() requires: numJobs == rint (numJobs) postcondition: !segments.empty()
effects: generate the splits for the sample guarantee: basic failures: out of memory II failures: i/o errors requires: !sample.files.empty() postcondition: !segments.empty()
effects: fill the job object from the original job information including the actual samples submitted guarantee: basic failures: out of memory II
45{
46 namespace
47 {
52 void fillJob (BatchJob& myjob, const Job& job,
53 const std::string& submitDir)
54 {
57 }
58
59
60
66 void fillSample (BatchSample& mysample,
68 {
69 mysample.name =
sample.name();
70 mysample.meta = *
sample.meta();
71 mysample.meta.fetchDefaults (
meta);
72
73 mysample.files =
sample.makeFileList ();
74 }
75
76
77
84 void addSample (BatchJob& job, BatchSample sample,
85 const std::vector<BatchSegment>& segments)
86 {
88
89 sample.begin_segments =
job.segments.size();
90
93 for (std::size_t iter = 0, end = segments.size(); iter != end; ++ iter)
94 {
95 BatchSegment segment = segments[
iter];
96
97 segment.sampleName =
sample.name;
98 {
99 std::ostringstream myname;
101 segment.fullName = myname.str();
102 }
103 {
104 std::ostringstream myname;
106 segment.segmentName = myname.str();
107 }
108
109 segment.sample =
job.samples.size();
110 segment.job_id =
job.segments.size();
111 if (iter+1 < end)
112 {
113 segment.end_file = segments[
iter+1].begin_file;
114 segment.end_event = segments[
iter+1].begin_event;
115 } else
116 {
117 segment.end_file =
sample.files.size();
118 segment.end_event = 0;
119 }
120 RCU_ASSERT (segment.begin_file < segment.end_file || (segment.begin_file == segment.end_file && segment.begin_event <= segment.end_event));
121 job.segments.push_back (segment);
122 }
123
124 sample.end_segments =
job.segments.size();
125 job.samples.push_back (sample);
126 }
127
128
129
130
131
139 void splitSampleByFile (const BatchSample& sample,
140 std::vector<BatchSegment>& segments,
141 double numJobs)
142 {
143
146
147 for (std::size_t
index = 0, end = numJobs;
149 {
150 BatchSegment segment;
151 segment.begin_file = rint (
index * (
sample.files.size() / numJobs));
152 segments.push_back (segment);
153 }
154 }
155
156
157
158
159
167 void splitSampleByEvent (const BatchSample& sample,
168 std::vector<BatchSegment>& segments,
169 const std::vector<Long64_t>& eventsFile,
170 double numJobs)
171 {
175
176 Long64_t eventsSum = 0;
177 for (std::vector<Long64_t>::const_iterator nevents =
eventsFile.begin(),
178 end =
eventsFile.end(); nevents != end; ++ nevents)
179 {
182 }
183 if (eventsSum == 0)
184 {
185
186
187
188
189 segments.push_back (BatchSegment ());
190 return;
191 }
192 if (numJobs > eventsSum)
193 numJobs = eventsSum;
194 Long64_t eventsMax
195 =
sample.meta.castDouble (Job::optEventsPerWorker);
196 if (eventsMax > 0)
197 numJobs = ceil (double (eventsSum) / eventsMax);
198 eventsMax = Long64_t (ceil (double (eventsSum) / numJobs));
199
200 BatchSegment segment;
201 while (std::size_t (segment.begin_file) <
eventsFile.size())
202 {
203 while (segment.begin_event < eventsFile[segment.begin_file])
204 {
205 segments.push_back (segment);
206 segment.begin_event += eventsMax;
207 }
208 segment.begin_event -=
eventsFile[segment.begin_file];
209 ++ segment.begin_file;
210 }
211
212
213
215 }
216
217
218
219
220
228 std::vector<BatchSegment>& segments)
229 {
230
231
232
233
234
235
236
237 const double filesPerWorker
238 =
sample.meta.castDouble (Job::optFilesPerWorker, 1);
239 if (filesPerWorker < 1)
240 {
241 std::ostringstream
msg;
242 msg <<
"invalid number of files per worker: " << filesPerWorker;
243 throw std::runtime_error (
msg.str());
244 }
245 double numJobs = ceil (
sample.files.size() / filesPerWorker);
246
250 {
255 splitSampleByEvent (sample, segments, meta_nentries->
value, numJobs);
256 } else
257 {
258 splitSampleByFile (sample, segments, numJobs);
259 }
260
261
262 if (segments.empty())
263 {
264
265
267 segments.push_back (
empty);
268 }
269
271 }
272
273
278 void fillFullJob (BatchJob& myjob, const Job& job,
279 const std::string& location,
281 {
282 fillJob (myjob, job, location);
283 *myjob.job.options() =
meta;
284
286 {
287 BatchSample mysample;
288 fillSample (mysample, *sample,
meta);
289
290 std::vector<BatchSegment> subsegments;
292
293 addSample (myjob, mysample, subsegments);
294 }
295 }
296 }
297
298
299
300 void BatchDriver ::
301 testInvariant () const
302 {}
303
304
305
306 BatchDriver ::
307 BatchDriver ()
308 {
310 }
311
312
313
314 ::StatusCode BatchDriver ::
315 doManagerStep (Detail::ManagerData& data) const
316 {
317 using namespace msgEventLoop;
318 ANA_CHECK (Driver::doManagerStep (data));
320 {
321 case Detail::ManagerStep::fillOptions:
322 {
323 data.sharedFileSystem
324 =
data.options.castBool (Job::optBatchSharedFileSystem,
true);
325 }
326 break;
327
328 case Detail::ManagerStep::createSubmitDir:
329 {
330 if (
data.sharedFileSystem)
331 data.batchWriteLocation =
data.submitDir;
332 else
333 data.batchWriteLocation =
".";
334
335 if (
data.sharedFileSystem)
336 data.batchSubmitLocation =
data.submitDir+
"/submit";
337 else
338 data.batchSubmitLocation =
".";
339 }
340 break;
341
342 case Detail::ManagerStep::updateOutputLocation:
343 {
344 const std::string writeLocation=
data.batchWriteLocation;
345 for (Job::outputMIter out =
data.job->outputBegin(),
346 end =
data.job->outputEnd(); out != end; ++ out)
347 {
348 if (
out->output() ==
nullptr)
349 {
351 (writeLocation +
"/fetch/data-" +
out->label() +
"/"));
352 }
353 }
354 }
355 break;
356
357 case Detail::ManagerStep::batchCreateDirectories:
358 {
361 if (gSystem->MakeDirectory (
submitDir.c_str()) != 0)
362 {
364 return ::StatusCode::FAILURE;
365 }
366 const std::string runDir =
data.submitDir +
"/run";
367 if (gSystem->MakeDirectory (runDir.c_str()) != 0)
368 {
370 return ::StatusCode::FAILURE;
371 }
372 const std::string fetchDir =
data.submitDir +
"/fetch";
373 if (gSystem->MakeDirectory (fetchDir.c_str()) != 0)
374 {
376 return ::StatusCode::FAILURE;
377 }
378 const std::string statusDir =
data.submitDir +
"/status";
379 if (gSystem->MakeDirectory (statusDir.c_str()) != 0)
380 {
382 return ::StatusCode::FAILURE;
383 }
384 }
385 break;
386
387 case Detail::ManagerStep::batchCreateJob:
388 {
389 data.batchJob = std::make_unique<BatchJob> ();
391 data.batchJob->location=
data.batchWriteLocation;
392 {
393 std::string
path =
data.submitDir +
"/submit/config.root";
394 std::unique_ptr<TFile>
file (TFile::Open (
path.c_str(),
"RECREATE"));
395 if (
file ==
nullptr ||
file->IsZombie())
396 {
398 return ::StatusCode::FAILURE;
399 }
400
401
402
403 if (
file->WriteObject (
data.batchJob.get(),
"job") <= 0)
404 {
405 ANA_MSG_ERROR (
"failed to write job configuration to " << path);
406 return ::StatusCode::FAILURE;
407 }
408 }
409 {
410 std::ofstream
file ((
data.submitDir +
"/submit/segments").c_str());
411 for (std::size_t iter = 0, end =
data.batchJob->segments.size();
412 iter != end; ++ iter)
413 {
415 }
416 }
417 }
418 break;
419
420 case Detail::ManagerStep::batchScriptVar:
421 {
422 data.batchName =
"run";
424 data.batchJobId =
"EL_JOBID=$1\n";
425 }
426 break;
427
428 case Detail::ManagerStep::batchMakeScript:
429 {
430 makeScript (data,
data.batchJob->segments.size());
431 }
432 break;
433
434 case Detail::ManagerStep::batchMakeIndices:
435 {
438 }
439 break;
440
441 case Detail::ManagerStep::readConfigResubmit:
442 case Detail::ManagerStep::readConfigRetrieve:
443 {
445
446 std::unique_ptr<TFile>
file
447 {TFile::Open ((
data.submitDir +
"/submit/config.root").c_str(),
"READ")};
448 if (
file ==
nullptr ||
file->IsZombie())
449 {
451 return ::StatusCode::FAILURE;
452 }
453 data.batchJob.reset (
dynamic_cast<BatchJob*
>(
file->Get (
"job")));
454 if (
data.batchJob ==
nullptr)
455 {
457 return ::StatusCode::FAILURE;
458 }
460 }
461 break;
462
463 case Detail::ManagerStep::batchJobStatusResubmit:
464 case Detail::ManagerStep::batchJobStatusRetrieve:
465 {
466 for (std::size_t job = 0;
job !=
data.batchJob->segments.size(); ++
job)
467 {
468 std::ostringstream completedFile;
469 completedFile <<
data.submitDir <<
"/status/completed-" <<
job;
470 const bool hasCompleted =
471 (gSystem->AccessPathName (completedFile.str().c_str()) == 0);
472
473 std::ostringstream failFile;
474 failFile <<
data.submitDir <<
"/status/fail-" <<
job;
475 const bool hasFail =
476 (gSystem->AccessPathName (failFile.str().c_str()) == 0);
477
478 if (hasCompleted)
479 {
480 if (hasFail)
481 {
482 ANA_MSG_ERROR (
"sub-job " << job <<
" reported both success and failure");
483 return ::StatusCode::FAILURE;
484 } else
485 data.batchJobSuccess.insert (job);
486 } else
487 {
488 if (hasFail)
489 data.batchJobFailure.insert (job);
490 else
491 data.batchJobUnknown.insert (job);
492 }
493 }
494 ANA_MSG_INFO (
"current job status: " <<
data.batchJobSuccess.size() <<
" success, " <<
data.batchJobFailure.size() <<
" failure, " <<
data.batchJobUnknown.size() <<
" running/unknown");
495 }
496 break;
497
498 case Detail::ManagerStep::batchPreResubmit:
499 {
500 bool all_missing = false;
501 if (
data.resubmitOption ==
"ALL_MISSING")
502 {
503 all_missing = true;
504 }
else if (!
data.resubmitOption.empty())
505 {
507 return ::StatusCode::FAILURE;
508 }
509
510 for (std::size_t segment = 0; segment !=
data.batchJob->segments.size(); ++ segment)
511 {
512 if (all_missing)
513 {
514 if (
data.batchJobSuccess.find (segment) ==
data.batchJobSuccess.end())
515 data.batchJobIndices.push_back (segment);
516 } else
517 {
518 if (
data.batchJobFailure.find (segment) !=
data.batchJobFailure.end())
519 data.batchJobIndices.push_back (segment);
520 }
521 }
522
523 if (
data.batchJobIndices.empty())
524 {
526 data.nextStep = Detail::ManagerStep::final;
527 return ::StatusCode::SUCCESS;
528 }
529
530 for (std::size_t segment :
data.batchJobIndices)
531 {
538 }
539 data.options = *
data.batchJob->job.options();
540 }
541 break;
542
543 case Detail::ManagerStep::doRetrieve:
544 {
545 bool merged = mergeHists (data);
546 if (merged)
547 {
548 diskOutputSave (data);
549 }
550 data.retrieved =
true;
551 data.completed = merged;
552 }
553 break;
554
555 default:
556 (void) true;
557 }
558 return ::StatusCode::SUCCESS;
559 }
560
561
562
563 std::string BatchDriver ::
564 defaultReleaseSetup (const Detail::ManagerData& data) const
565 {
567
568
569 const std::string tarballName("AnalysisPackage.tar.gz");
570
571 std::ostringstream
file;
572
573
574 const char *WORKDIR_DIR =
getenv (
"WorkDir_DIR");
575
576
577 std::string CMAKE_DIR_str;
578 if (WORKDIR_DIR == nullptr){
579 msgEventLoop::ANA_MSG_INFO ("Could not find environment variable $WorkDir_DIR");
580 const char *CMAKE_PREFIX_PATH =
getenv (
"CMAKE_PREFIX_PATH");
581 if (CMAKE_PREFIX_PATH == nullptr){
582 throw std::runtime_error ("neither $WorkDir_DIR nor $CMAKE_PREFIX_PATH is set, cannot locate the release");
583 }
584
585 CMAKE_DIR_str = CMAKE_PREFIX_PATH;
586 if (CMAKE_DIR_str.find(":") != std::string::npos){
587
588 CMAKE_DIR_str.erase( CMAKE_DIR_str.find(":") , std::string::npos );
589 }
590
591 WORKDIR_DIR = CMAKE_DIR_str.data();
592 }
593
594 if(!
data.sharedFileSystem)
595 {
596 file <<
"mkdir -p build && tar -C build/ -xf " << tarballName <<
" || abortJob\n";
598 }
599
600
602
603 if(
getenv(
"AtlasSetupSite"))
file <<
"export AtlasSetupSite=" <<
getenv(
"AtlasSetupSite") <<
"\n";
604
605 if(
getenv(
"AtlasSetup"))
file <<
"export AtlasSetup=" <<
getenv(
"AtlasSetup") <<
"\n";
606
607
608
609 file <<
"export ATLAS_LOCAL_ROOT_BASE=/cvmfs/atlas.cern.ch/repo/ATLASLocalRootBase\n";
610 file <<
"source $ATLAS_LOCAL_ROOT_BASE/user/atlasLocalSetup.sh --quiet\n";
611
612
613 std::ostringstream defaultSetupCommand;
614 {
615
616 if(
getenv(
"AtlasProject")) defaultSetupCommand <<
"export AtlasProject=" <<
getenv(
"AtlasProject") <<
"\n";
617
618 if(
getenv(
"AtlasVersion")) defaultSetupCommand <<
"export AtlasVersion=" <<
getenv(
"AtlasVersion") <<
"\n";
619
620 if(
getenv(
"AtlasBuildStamp")) defaultSetupCommand <<
"export AtlasBuildStamp=" <<
getenv(
"AtlasBuildStamp") <<
"\n";
621
622 if(
getenv(
"AtlasBuildBranch")) defaultSetupCommand <<
"export AtlasBuildBranch=" <<
getenv(
"AtlasBuildBranch") <<
"\n";
623
624 if(
getenv(
"AtlasReleaseType")) defaultSetupCommand <<
"export AtlasReleaseType=" <<
getenv(
"AtlasReleaseType") <<
"\n";
625 defaultSetupCommand << "if [ \"${AtlasReleaseType}\" == \"stable\" ]; then\n";
626 defaultSetupCommand << " source ${AtlasSetup}/scripts/asetup.sh ${AtlasProject},${AtlasVersion} || abortJob\n";
627 defaultSetupCommand << "else\n";
628 defaultSetupCommand << " source ${AtlasSetup}/scripts/asetup.sh ${AtlasProject},${AtlasBuildBranch},${AtlasBuildStamp} || abortJob\n";
629 defaultSetupCommand << "fi\n";
630 defaultSetupCommand << "echo \"Using default setup command\"";
631 }
632
633 file <<
data.options.castString(Job::optBatchSetupCommand, defaultSetupCommand.str()) <<
" || abortJob\n";
634 if(
data.sharedFileSystem)
file <<
"source " << WORKDIR_DIR <<
"/setup.sh || abortJob\n";
635 else file <<
"source build/setup.sh || abortJob\n";
637
638 if(!
data.sharedFileSystem)
639 {
640 std::ostringstream
cmd;
641
642 cmd <<
"cpack -D CPACK_INSTALL_PREFIX=. -G TGZ --config $TestArea/CPackConfig.cmake";
643
644
645 if (gSystem->Exec (
cmd.str().c_str()) != 0){
646 throw std::runtime_error (
"failed to execute: " +
cmd.str());
647 }
648
649 const char *WorkDir_VERSION = std::getenv ("WorkDir_VERSION");
650 const char *WorkDir_PLATFORM = std::getenv ("WorkDir_PLATFORM");
651 if (WorkDir_VERSION == nullptr || WorkDir_PLATFORM == nullptr){
652 throw std::runtime_error ("$WorkDir_VERSION and $WorkDir_PLATFORM must both be set to build the release tarball");
653 }
654 std::ostringstream mv_command;
655 mv_command << "mv WorkDir_" << WorkDir_VERSION << "_" << WorkDir_PLATFORM << ".tar.gz " << tarballName;
656 if (gSystem->Exec (mv_command.str().c_str()) != 0){
657 throw std::runtime_error ("failed to execute: " + mv_command.str());
658 }
659 }
660
662 }
663
664
665
666 void BatchDriver ::
667 makeScript (Detail::ManagerData& data,
668 std::size_t njobs) const
669 {
671
673 bool multiFile = (
name.find (
"{JOBID}") != std::string::npos);
674
675
676
677
678
679 const std::string releaseSetup =
680 data.batchSkipReleaseSetup ? std::string() : defaultReleaseSetup (
data);
681
683 {
684 std::ostringstream
str;
687
688 {
690 file <<
"#!/bin/bash\n";
691 file <<
"echo starting batch job initialization\n";
693 file <<
"echo batch job user initialization finished\n";
694 if (multiFile)
file <<
"EL_JOBID=" <<
index <<
"\n\n";
695 else file <<
data.batchJobId <<
"\n";
696
697 file <<
"function abortJob {\n";
698 file <<
" echo \"abort EL_JOBID=${EL_JOBID}\"\n";
699 file <<
" touch \"" <<
data.batchWriteLocation <<
"/status/fail-$EL_JOBID\"\n";
700 file <<
" touch \"" <<
data.batchWriteLocation <<
"/status/done-$EL_JOBID\"\n";
703
704
705 file <<
"EL_JOBSEG=`grep \"^$EL_JOBID \" \"" <<
data.batchSubmitLocation <<
"/segments\" | awk ' { print $2 }'`\n";
706 file <<
"test \"$EL_JOBSEG\" != \"\" || abortJob\n";
707 file <<
"hostname\n";
710 file << shellInit <<
"\n";
711
712 if(!
data.sharedFileSystem)
713 {
714 file <<
"mkdir \"fetch\" || abortJob\n";
715 file <<
"mkdir \"status\" || abortJob\n";
717 }
718
719 if(
data.sharedFileSystem)
720 {
721 file <<
"test \"$TMPDIR\" == \"\" && TMPDIR=/tmp\n";
722 file <<
"RUNDIR=${TMPDIR}/EventLoop-Worker-$EL_JOBSEG-`date +%s`-$$\n";
723 file <<
"mkdir \"$RUNDIR\" || abortJob\n";
724 file <<
"cd \"$RUNDIR\" || abortJob\n";
725 }
726
727 if (!
data.batchSkipReleaseSetup)
728 file << releaseSetup;
729
730 file <<
"eventloop_batch_worker $EL_JOBID '" <<
data.batchSubmitLocation <<
"/config.root' || abortJob\n";
731
732 file <<
"test -f \"" <<
data.batchWriteLocation <<
"/status/completed-$EL_JOBID\" || "
733 <<
"touch \"" <<
data.batchWriteLocation <<
"/status/fail-$EL_JOBID\"\n";
734 file <<
"touch \"" <<
data.batchWriteLocation <<
"/status/done-$EL_JOBID\"\n";
735 if(
data.sharedFileSystem)
file <<
"cd .. && rm -rf \"$RUNDIR\"\n";
736 }
737
738 {
739 std::ostringstream
cmd;
741 if (gSystem->Exec (
cmd.str().c_str()) != 0)
742 throw std::runtime_error (
"failed to execute: " +
cmd.str());
743 }
744 }
745 }
746
747
748
749 bool BatchDriver ::
750 mergeHists (Detail::ManagerData& data)
751 {
752 using namespace msgEventLoop;
753
754
755
756
757
758
759 std::unique_ptr<SH::DiskOutput> origHistOutputMemory;
761 for (
auto iter =
data.job->outputBegin(), end =
data.job->outputEnd();
762 iter != end; ++ iter)
763 {
764 if (
iter->label() == Job::histogramStreamName)
765 origHistOutput =
iter->output();
766 }
767 if (origHistOutput == nullptr)
768 {
769 origHistOutputMemory = std::make_unique<SH::DiskOutputLocal>
770 (
data.submitDir +
"/fetch/hist-");
771 origHistOutput = origHistOutputMemory.get();
772 }
774
776
778
779 for (std::size_t sample = 0, end =
data.batchJob->samples.size();
780 sample != end; ++ sample)
781 {
782 const BatchSample& mysample (
data.batchJob->samples[sample]);
783
784 std::ostringstream
output;
786 if (gSystem->AccessPathName (
output.str().c_str()) != 0)
787 {
789
790 bool complete = true;
791 std::vector<std::string>
input;
792 for (std::size_t segment = mysample.begin_segments,
793 end = mysample.end_segments; segment != end; ++ segment)
794 {
795 const BatchSegment& mysegment =
data.batchJob->segments[segment];
796
798 (mysegment.sampleName, mysegment.segmentName, ".root");
799
800 ANA_MSG_VERBOSE (
"merge segment " << segment <<
" completed=" << (
data.batchJobSuccess.find(segment)!=
data.batchJobSuccess.end()) <<
" fail=" << (
data.batchJobFailure.find(segment)!=
data.batchJobFailure.end()) <<
" unknown=" << (
data.batchJobUnknown.find(segment)!=
data.batchJobUnknown.end()));
801
802 input.push_back (hist_file);
803
804 if (
data.batchJobFailure.find(segment)!=
data.batchJobFailure.end())
805 {
807 message <<
"subjob " << segment <<
"/" << mysegment.fullName
808 << " failed";
809 throw std::runtime_error (
message.str());
810 }
811 else if (
data.batchJobSuccess.find(segment)==
data.batchJobSuccess.end())
812 complete =
false,
result =
false;
813 }
814 if (complete)
815 {
817
818
819 for (Job::outputIter out =
data.batchJob->job.outputBegin(),
820 end =
data.batchJob->job.outputEnd(); out != end; ++ out)
821 {
824
825 if(gSystem->AccessPathName(
output.str().c_str()))
826 gSystem->mkdir(
output.str().c_str(),
true);
827
828
830
831 std::vector<std::string> dataInput;
832 for (std::size_t segment = mysample.begin_segments,
833 dataEnd = mysample.end_segments; segment != dataEnd; ++ segment)
834 {
835 const BatchSegment& mysegment =
data.batchJob->segments[segment];
836
837 const std::string
infile =
838 data.submitDir +
"/fetch/data-" +
out->label() +
"/" + mysegment.fullName +
".root";
839
840 dataInput.push_back (infile);
841 }
842
844 }
845 }
846 }
847 }
849 }
850}
#define RCU_NEW_INVARIANT(x)
#define RCU_ASSERT_SOFT(x)
#define RCU_READ_INVARIANT(x)
static const Attributes_t empty
an implementation of DiskOutput for local disks
a class/interface representing an output location for files
std::string targetURL(const std::string &sampleName, const std::string &segmentName, const std::string &suffix) const
the final output location for the given segment
a base class that manages a set of files belonging to a particular data set and the associated meta-d...
void exec(const std::string &cmd)
effects: execute the given command guarantee: strong failures: out of memory II failures: system fail...
std::string quote(const std::string &name)
effects: quote the given name to protect it from the shell returns: the quoted name guarantee: strong...
void hadd(const std::string &output_file, const std::vector< std::string > &input_files, unsigned max_files)
effects: perform the hadd functionality guarantee: basic failures: out of memory III failures: i/o er...
std::string substitute(std::string_view str, std::string_view pattern, std::string_view with)
effects: substitute all occurences of "pattern" with "with" in the string "str" returns: the substitu...
std::string getenv(const std::string &variableName)
get an environment variable
SampleHandler splitSample(Sample &sample, const Long64_t nevt)
effects: split the given sample into a set of samples, with each sample containing either exactly one...
path
python interpreter configuration --------------------------------------—