ATLAS Offline Software
Loading...
Searching...
No Matches
BatchDriver.cxx File Reference
#include <EventLoop/BatchDriver.h>
#include <EventLoop/BatchJob.h>
#include <EventLoop/BatchSample.h>
#include <EventLoop/BatchSegment.h>
#include <EventLoop/ManagerData.h>
#include <EventLoop/ManagerStep.h>
#include <EventLoop/MessageCheck.h>
#include <EventLoop/OutputStream.h>
#include <RootCoreUtils/Assert.h>
#include <RootCoreUtils/ShellExec.h>
#include <RootCoreUtils/StringUtil.h>
#include <RootCoreUtils/hadd.h>
#include <SampleHandler/DiskOutputLocal.h>
#include <SampleHandler/MetaFields.h>
#include <SampleHandler/MetaVector.h>
#include <SampleHandler/Sample.h>
#include <TFile.h>
#include <TSystem.h>
#include <cmath>
#include <cstdlib>
#include <fstream>
#include <memory>
#include <sstream>
#include <stdexcept>

Go to the source code of this file.

Functions

 ClassImp (EL::BatchDriver) namespace EL

Function Documentation

◆ ClassImp()

ClassImp ( EL::BatchDriver )
Author
Nils Krumnack

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

Definition at line 42 of file BatchDriver.cxx.

45{
46 namespace
47 {
52 void fillJob (BatchJob& myjob, const Job& job,
53 const std::string& submitDir)
54 {
55 myjob.job = job;
56 myjob.location = submitDir;
57 }
58
59
60
66 void fillSample (BatchSample& mysample,
67 const SH::Sample& sample, const SH::MetaObject& meta)
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 {
87 RCU_REQUIRE (!segments.empty());
88
89 sample.begin_segments = job.segments.size();
90
91 RCU_ASSERT (segments[0].begin_file == 0);
92 RCU_ASSERT (segments[0].begin_event == 0);
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;
100 myname << sample.name << "-" << iter;
101 segment.fullName = myname.str();
102 }
103 {
104 std::ostringstream myname;
105 myname << iter;
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 // RCU_REQUIRE (!sample.files.empty());
144 RCU_REQUIRE (numJobs == rint (numJobs));
145 RCU_REQUIRE (numJobs <= sample.files.size());
146
147 for (std::size_t index = 0, end = numJobs;
148 index < end; ++ index)
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 {
172 RCU_REQUIRE (!sample.files.empty());
173 RCU_REQUIRE (sample.files.size() == eventsFile.size());
174 RCU_REQUIRE (numJobs == rint (numJobs));
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 {
180 RCU_ASSERT_SOFT (*nevents >= 0);
181 eventsSum += *nevents;
182 }
183 if (eventsSum == 0)
184 {
185 // an all-empty sample still needs a single segment so that its
186 // (empty) files get processed, e.g. for metadata; there is no
187 // per-event split to compute and the division by numJobs below
188 // would be undefined for numJobs == 0.
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 // eventsMax is rounded up, so the number of segments produced can be
212 // fewer than the requested number of jobs (never more) for inputs
213 // that do not divide evenly.
214 RCU_ASSERT (segments.size() <= numJobs);
215 }
216
217
218
219
220
227 void splitSample (const BatchSample& sample,
228 std::vector<BatchSegment>& segments)
229 {
230 // RCU_REQUIRE (!sample.files.empty());
231
232 // rationale: I'm now calculating the number of jobs here, so
233 // that independent of whether we divide on a per-file or
234 // per-event basis we can get the same number of jobs. also,
235 // it will balance out things slightly if we only have a few
236 // files and multiple files per job.
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
247 const TObject *meta
249 if (meta)
250 {
251 const SH::MetaVector<Long64_t> *const meta_nentries
252 = dynamic_cast<const SH::MetaVector<Long64_t> *>(meta);
253 RCU_ASSERT_SOFT (meta == meta_nentries);
254 RCU_ASSERT_SOFT (meta_nentries->value.size() == sample.files.size());
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 // rationale: this isn't really the proper thing to do. if a
265 // sample is empty I should just run the job locally.
266 BatchSegment empty;
267 segments.push_back (empty);
268 }
269
270 RCU_PROVIDE (!segments.empty());
271 }
272
273
278 void fillFullJob (BatchJob& myjob, const Job& job,
279 const std::string& location,
280 const SH::MetaObject& meta)
281 {
282 fillJob (myjob, job, location);
283 *myjob.job.options() = meta;
284
285 for (const SH::Sample *sample : job.sampleHandler())
286 {
287 BatchSample mysample;
288 fillSample (mysample, *sample, meta);
289
290 std::vector<BatchSegment> subsegments;
291 splitSample (mysample, 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 {
309 RCU_NEW_INVARIANT (this);
310 }
311
312
313
314 ::StatusCode BatchDriver ::
315 doManagerStep (Detail::ManagerData& data) const
316 {
317 using namespace msgEventLoop;
318 ANA_CHECK (Driver::doManagerStep (data));
319 switch (data.step)
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) // Shared file-system, write to output
331 data.batchWriteLocation = data.submitDir;
332 else
333 data.batchWriteLocation = ".";
334
335 if (data.sharedFileSystem) // Shared file-system, use local path
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 {
350 out->output (new SH::DiskOutputLocal
351 (writeLocation + "/fetch/data-" + out->label() + "/"));
352 }
353 }
354 }
355 break;
356
357 case Detail::ManagerStep::batchCreateDirectories:
358 {
359 ANA_MSG_DEBUG ("submitting batch job in location " << data.submitDir);
360 const std::string submitDir = data.submitDir + "/submit";
361 if (gSystem->MakeDirectory (submitDir.c_str()) != 0)
362 {
363 ANA_MSG_ERROR ("failed to create directory " + submitDir);
364 return ::StatusCode::FAILURE;
365 }
366 const std::string runDir = data.submitDir + "/run";
367 if (gSystem->MakeDirectory (runDir.c_str()) != 0)
368 {
369 ANA_MSG_ERROR ("failed to create directory " + runDir);
370 return ::StatusCode::FAILURE;
371 }
372 const std::string fetchDir = data.submitDir + "/fetch";
373 if (gSystem->MakeDirectory (fetchDir.c_str()) != 0)
374 {
375 ANA_MSG_ERROR ("failed to create directory " + fetchDir);
376 return ::StatusCode::FAILURE;
377 }
378 const std::string statusDir = data.submitDir + "/status";
379 if (gSystem->MakeDirectory (statusDir.c_str()) != 0)
380 {
381 ANA_MSG_ERROR ("failed to create directory " + statusDir);
382 return ::StatusCode::FAILURE;
383 }
384 }
385 break;
386
387 case Detail::ManagerStep::batchCreateJob:
388 {
389 data.batchJob = std::make_unique<BatchJob> ();
390 fillFullJob (*data.batchJob, *data.job, data.submitDir, data.options);
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 {
397 ANA_MSG_ERROR ("failed to open " << path << " for writing");
398 return ::StatusCode::FAILURE;
399 }
400 // write into this file explicitly; TObject::Write would target
401 // the current gDirectory, silently dropping the config if the
402 // open above had failed.
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 {
414 file << iter << " " << data.batchJob->segments[iter].fullName << "\n";
415 }
416 }
417 }
418 break;
419
420 case Detail::ManagerStep::batchScriptVar:
421 {
422 data.batchName = "run";
423 data.batchInit = "";
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 {
436 for (std::size_t index = 0; index != data.batchJob->segments.size(); ++ index)
437 data.batchJobIndices.push_back (index);
438 }
439 break;
440
441 case Detail::ManagerStep::readConfigResubmit:
442 case Detail::ManagerStep::readConfigRetrieve:
443 {
444 ANA_MSG_INFO ("retrieving batch job in location " << data.submitDir);
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 {
450 ANA_MSG_ERROR ("failed to read config.root");
451 return ::StatusCode::FAILURE;
452 }
453 data.batchJob.reset (dynamic_cast<BatchJob*>(file->Get ("job")));
454 if (data.batchJob == nullptr)
455 {
456 ANA_MSG_ERROR ("failed to get job object from config.root");
457 return ::StatusCode::FAILURE;
458 }
459 data.job = &data.batchJob->job;
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 {
506 ANA_MSG_ERROR ("unknown resubmit option " + data.resubmitOption);
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 {
525 ANA_MSG_INFO ("found no jobs to resubmit");
526 data.nextStep = Detail::ManagerStep::final;
527 return ::StatusCode::SUCCESS;
528 }
529
530 for (std::size_t segment : data.batchJobIndices)
531 {
532 std::ostringstream command;
533 command << "rm -rf";
534 command << " " << RCU::Shell::quote (data.submitDir) << "/status/completed-" << segment;
535 command << " " << RCU::Shell::quote (data.submitDir) << "/status/fail-" << segment;
536 command << " " << RCU::Shell::quote (data.submitDir) << "/status/done-" << segment;
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; // safe to do nothing
557 }
558 return ::StatusCode::SUCCESS;
559 }
560
561
562
563 std::string BatchDriver ::
564 defaultReleaseSetup (const Detail::ManagerData& data) const
565 {
566 RCU_READ_INVARIANT (this);
567
568 // name of tarball being made (this needs to match CondorDriver.cxx)
569 const std::string tarballName("AnalysisPackage.tar.gz");
570
571 std::ostringstream file;
572
573 // <path of build dir>/x86_64-slc6-gcc62-opt (comes from CMake, we need this)
574 const char *WORKDIR_DIR = getenv ("WorkDir_DIR");
575 // As a backup, keep the CMAKE_PREFIX_PATH (only needed, and only read,
576 // when $WorkDir_DIR is not set; std::string(nullptr) would be UB)
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 // Instead, build from the first path in CMAKE_PREFIX_PATH
585 CMAKE_DIR_str = CMAKE_PREFIX_PATH;
586 if (CMAKE_DIR_str.find(":") != std::string::npos){
587 // Erase everything from the colon onwards
588 CMAKE_DIR_str.erase( CMAKE_DIR_str.find(":") , std::string::npos );
589 }
590 // Provide the remainder of the string to the workdir
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";
597 file << "\n";
598 }
599
600
601 file << "\n";
602 // /cvmfs/atlas.cern.ch/repo/ATLASLocalRootBase/x86_64/AtlasSetup/.config/.asetup.site
603 if(getenv("AtlasSetupSite")) file << "export AtlasSetupSite=" << getenv("AtlasSetupSite") << "\n";
604 // /cvmfs/atlas.cern.ch/repo/ATLASLocalRootBase/x86_64/AtlasSetup/V00-07-75/AtlasSetup
605 if(getenv("AtlasSetup")) file << "export AtlasSetup=" << getenv("AtlasSetup") << "\n";
606 // for now, needed because of errors like:
607 // /cvmfs/atlas.cern.ch/repo/ATLASLocalRootBase/swConfig/asetup/asetupEpilog.sh: line 38:
608 // /swConfig/python/pythonFix-Linux.sh: No such file or directory
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 // default setup command
613 std::ostringstream defaultSetupCommand;
614 {
615 // AnalysisBase
616 if(getenv("AtlasProject")) defaultSetupCommand << "export AtlasProject=" << getenv("AtlasProject") << "\n";
617 // 21.2.3
618 if(getenv("AtlasVersion")) defaultSetupCommand << "export AtlasVersion=" << getenv("AtlasVersion") << "\n";
619 // 2017-08-16T2249 (only set if using a nightly release)
620 if(getenv("AtlasBuildStamp")) defaultSetupCommand << "export AtlasBuildStamp=" << getenv("AtlasBuildStamp") << "\n";
621 // 21.2
622 if(getenv("AtlasBuildBranch")) defaultSetupCommand << "export AtlasBuildBranch=" << getenv("AtlasBuildBranch") << "\n";
623 // stable vs nightly
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";
636 file << "\n";
637
638 if(!data.sharedFileSystem)
639 {
640 std::ostringstream cmd;
641 //cmd << "tar --dereference -C " << WORKDIR_DIR << " -czf " << tarballName << " .";
642 cmd << "cpack -D CPACK_INSTALL_PREFIX=. -G TGZ --config $TestArea/CPackConfig.cmake";
643
644 // suppress the output from the command
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
661 return file.str();
662 }
663
664
665
666 void BatchDriver ::
667 makeScript (Detail::ManagerData& data,
668 std::size_t njobs) const
669 {
670 RCU_READ_INVARIANT (this);
671
672 std::string name = data.batchName;
673 bool multiFile = (name.find ("{JOBID}") != std::string::npos);
674
675 // Build the release setup once, outside the per-script loop. The text
676 // is the same for every script, and defaultReleaseSetup also builds the
677 // release tarball as a side effect (for a non-shared file system), which
678 // must not be repeated once per generated script.
679 const std::string releaseSetup =
680 data.batchSkipReleaseSetup ? std::string() : defaultReleaseSetup (data);
681
682 for (std::size_t index = 0, end = multiFile ? njobs : 1; index != end; ++ index)
683 {
684 std::ostringstream str;
685 str << index;
686 const std::string fileName = data.submitDir + "/submit/" + RCU::substitute (name, "{JOBID}", str.str());
687
688 {
689 std::ofstream file (fileName.c_str());
690 file << "#!/bin/bash\n";
691 file << "echo starting batch job initialization\n";
692 file << RCU::substitute (data.batchInit, "{JOBID}", str.str()) << "\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";
701 file << " exit 1\n";
702 file << "}\n\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";
708 file << "pwd\n";
709 file << "whoami\n";
710 file << shellInit << "\n";
711
712 if(!data.sharedFileSystem)
713 { // Create output transfer directories
714 file << "mkdir \"fetch\" || abortJob\n";
715 file << "mkdir \"status\" || abortJob\n";
716 file << "\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;
740 cmd << "chmod +x " << RCU::Shell::quote (fileName);
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 // This picks up the DiskOutput object used to write out our
755 // histograms, we will then use that to locate the output files.
756 // this is not really the best way of doing this, but there are
757 // bigger rewrites of this code excepted, so I don't want to spend
758 // a lot of time on this now (06 Feb 19).
759 std::unique_ptr<SH::DiskOutput> origHistOutputMemory;
760 const SH::DiskOutput *origHistOutput = nullptr;
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 }
773 RCU_ASSERT (origHistOutput != nullptr);
774
775 bool result = true;
776
777 ANA_MSG_DEBUG ("merging histograms in location " << data.submitDir);
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;
785 output << data.submitDir << "/hist-" << data.batchJob->samples[sample].name << ".root";
786 if (gSystem->AccessPathName (output.str().c_str()) != 0)
787 {
788 ANA_MSG_VERBOSE ("merge files for sample " << data.batchJob->samples[sample].name);
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
797 const std::string hist_file = origHistOutput->targetURL
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 {
806 std::ostringstream message;
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 {
816 RCU::hadd (output.str(), input);
817
818 // Merge output data directories
819 for (Job::outputIter out = data.batchJob->job.outputBegin(),
820 end = data.batchJob->job.outputEnd(); out != end; ++ out)
821 {
822 output.str("");
823 output << data.submitDir << "/data-" << out->label();
824
825 if(gSystem->AccessPathName(output.str().c_str()))
826 gSystem->mkdir(output.str().c_str(),true);
827
828
829 output << "/" << data.batchJob->samples[sample].name << ".root";
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
843 RCU::hadd(output.str(), dataInput);
844 }
845 }
846 }
847 }
848 return result;
849 }
850}
#define RCU_ASSERT(x)
Definition Assert.h:210
#define RCU_NEW_INVARIANT(x)
Definition Assert.h:221
#define RCU_ASSERT_SOFT(x)
Definition Assert.h:155
#define RCU_PROVIDE(x)
Definition Assert.h:203
#define RCU_REQUIRE(x)
Definition Assert.h:196
#define RCU_READ_INVARIANT(x)
Definition Assert.h:217
#define ANA_MSG_ERROR(xmsg,...)
Macro printing error messages.
#define ANA_MSG_DEBUG(xmsg,...)
Macro printing debug messages.
#define ANA_MSG_VERBOSE(xmsg,...)
Macro printing verbose messages.
#define ANA_MSG_INFO(xmsg,...)
Macro printing info messages.
#define ANA_CHECK(EXP)
check whether the given expression was successful
static const Attributes_t empty
an implementation of DiskOutput for local disks
a class/interface representing an output location for files
Definition DiskOutput.h:46
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 class that manages meta-data to be associated with an object.
Definition MetaObject.h:48
This class defines a templatized version of the meta-data in vector form.
Definition MetaVector.h:20
std::vector< T > value
the value contained
Definition MetaVector.h:57
a base class that manages a set of files belonging to a particular data set and the associated meta-d...
Definition Sample.h:49
void exec(const std::string &cmd)
effects: execute the given command guarantee: strong failures: out of memory II failures: system fail...
Definition ShellExec.cxx:27
std::string quote(const std::string &name)
effects: quote the given name to protect it from the shell returns: the quoted name guarantee: strong...
Definition ShellExec.cxx:65
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...
Definition hadd.cxx:29
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 --------------------------------------—
Definition athena.py:130
Definition index.py:1
output
Definition merge.py:16
-diff
eventsFile
Events files.
static const std::string numEventsPerFile
the number of events in each file
Definition MetaFields.h:59
MsgStream & msg
Definition testRead.cxx:32
TFile * file