CMS 3D CMS Logo

ExternalLHEProducer.cc
Go to the documentation of this file.
1 // -*- C++ -*-
2 //
3 // Package: ExternalLHEProducer
4 // Class: ExternalLHEProducer
5 //
13 //
14 // Original Author: Brian Paul Bockelman,8 R-018,+41227670861,
15 // Created: Fri Oct 21 11:37:26 CEST 2011
16 //
17 //
18 
19 // system include files
20 #include <cstdio>
21 #include <memory>
22 #include <vector>
23 #include <string>
24 #include <fstream>
25 #include <unistd.h>
26 #include <dirent.h>
27 #include <fcntl.h>
28 #include <sys/wait.h>
29 #include <sys/time.h>
30 #include <sys/resource.h>
31 
32 #include "boost/bind.hpp"
33 
34 #include "boost/ptr_container/ptr_deque.hpp"
35 
36 // user include files
40 
44 
47 
52 
56 
59 
61 
62 //
63 // class declaration
64 //
65 
66 class ExternalLHEProducer : public edm::one::EDProducer<edm::BeginRunProducer, edm::EndRunProducer> {
67 public:
68  explicit ExternalLHEProducer(const edm::ParameterSet& iConfig);
69  ~ExternalLHEProducer() override;
70 
71  static void fillDescriptions(edm::ConfigurationDescriptions& descriptions);
72 
73 private:
74  void produce(edm::Event&, const edm::EventSetup&) override;
75  void beginRunProduce(edm::Run& run, edm::EventSetup const& es) override;
76  void endRunProduce(edm::Run&, edm::EventSetup const&) override;
77  void preallocThreads(unsigned int) override;
78 
79  int closeDescriptors(int preserve);
80  void executeScript();
81  std::unique_ptr<std::string> readOutput();
82 
83  void nextEvent();
84 
85  // ----------member data ---------------------------
88  std::vector<std::string> args_;
89  uint32_t npars_;
90  uint32_t nEvents_;
91  bool storeXML_;
92  unsigned int nThreads_{1};
94 
95  // Used only if nPartonMapping is in the configuration
96  std::map<unsigned, std::pair<unsigned, unsigned>> nPartonMapping_{};
97 
98  std::unique_ptr<lhef::LHEReader> reader_;
99  std::shared_ptr<lhef::LHERunInfo> runInfoLast;
100  std::shared_ptr<lhef::LHERunInfo> runInfo;
101  std::shared_ptr<lhef::LHEEvent> partonLevel;
102  boost::ptr_deque<LHERunInfoProduct> runInfoProducts;
103  bool wasMerged;
104 
105  class FileCloseSentry : private boost::noncopyable {
106  public:
107  explicit FileCloseSentry(int fd) : fd_(fd){};
108 
109  ~FileCloseSentry() { close(fd_); }
110 
111  private:
112  int fd_;
113  };
114 };
115 
116 //
117 // constructors and destructor
118 //
120  : scriptName_((iConfig.getParameter<edm::FileInPath>("scriptName")).fullPath()),
121  outputFile_(iConfig.getParameter<std::string>("outputFile")),
122  args_(iConfig.getParameter<std::vector<std::string>>("args")),
123  npars_(iConfig.getParameter<uint32_t>("numberOfParameters")),
124  nEvents_(iConfig.getUntrackedParameter<uint32_t>("nEvents")),
125  storeXML_(iConfig.getUntrackedParameter<bool>("storeXML")) {
126  if (npars_ != args_.size())
127  throw cms::Exception("ExternalLHEProducer")
128  << "Problem with configuration: " << args_.size() << " script arguments given, expected " << npars_;
129 
130  if (iConfig.exists("nPartonMapping")) {
131  auto& processMap(iConfig.getParameterSetVector("nPartonMapping"));
132  for (auto& cfg : processMap) {
133  unsigned processId(cfg.getParameter<unsigned>("idprup"));
134 
135  auto orderStr(cfg.getParameter<std::string>("order"));
136  unsigned order(0);
137  if (orderStr == "LO")
138  order = 0;
139  else if (orderStr == "NLO")
140  order = 1;
141  else
142  throw cms::Exception("ExternalLHEProducer")
143  << "Invalid order specification for process " << processId << ": " << orderStr;
144 
145  unsigned np(cfg.getParameter<unsigned>("np"));
146 
147  nPartonMapping_.emplace(processId, std::make_pair(order, np));
148  }
149  }
150 
151  produces<LHEXMLStringProduct, edm::Transition::BeginRun>("LHEScriptOutput");
152 
153  produces<LHEEventProduct>();
154  produces<LHERunInfoProduct, edm::Transition::BeginRun>();
155  produces<LHERunInfoProduct, edm::Transition::EndRun>();
156 }
157 
159 
160 //
161 // member functions
162 //
163 
164 // ------------ method called with number of threads in job --
165 void ExternalLHEProducer::preallocThreads(unsigned int iThreads) { nThreads_ = iThreads; }
166 
167 // ------------ method called to produce the data ------------
169  nextEvent();
170  if (!partonLevel) {
172  << "No lhe event found in ExternalLHEProducer::produce(). "
173  << "The likely cause is that the lhe file contains fewer events than were requested, which is possible "
174  << "in case of phase space integration or uneweighting efficiency problems.";
175  }
176 
177  std::unique_ptr<LHEEventProduct> product(
178  new LHEEventProduct(*partonLevel->getHEPEUP(), partonLevel->originalXWGTUP()));
179  if (partonLevel->getPDF()) {
180  product->setPDF(*partonLevel->getPDF());
181  }
182  std::for_each(partonLevel->weights().begin(),
183  partonLevel->weights().end(),
184  boost::bind(&LHEEventProduct::addWeight, product.get(), _1));
185  product->setScales(partonLevel->scales());
186  if (nPartonMapping_.empty()) {
187  product->setNpLO(partonLevel->npLO());
188  product->setNpNLO(partonLevel->npNLO());
189  } else {
190  // overwrite npLO and npNLO values by user-specified mapping
191  unsigned processId(partonLevel->getHEPEUP()->IDPRUP);
192  unsigned order(0);
193  unsigned np(0);
194  try {
195  auto procDef(nPartonMapping_.at(processId));
196  order = procDef.first;
197  np = procDef.second;
198  } catch (std::out_of_range&) {
199  throw cms::Exception("ExternalLHEProducer")
200  << "Unexpected IDPRUP encountered: " << partonLevel->getHEPEUP()->IDPRUP;
201  }
202 
203  switch (order) {
204  case 0:
205  product->setNpLO(np);
206  product->setNpNLO(-1);
207  break;
208  case 1:
209  product->setNpLO(-1);
210  product->setNpNLO(np);
211  break;
212  default:
213  break;
214  }
215  }
216 
217  std::for_each(partonLevel->getComments().begin(),
218  partonLevel->getComments().end(),
219  boost::bind(&LHEEventProduct::addComment, product.get(), _1));
220 
221  iEvent.put(std::move(product));
222 
223  if (runInfo) {
224  std::unique_ptr<LHERunInfoProduct> product(new LHERunInfoProduct(*runInfo->getHEPRUP()));
225  std::for_each(runInfo->getHeaders().begin(),
226  runInfo->getHeaders().end(),
227  boost::bind(&LHERunInfoProduct::addHeader, product.get(), _1));
228  std::for_each(runInfo->getComments().begin(),
229  runInfo->getComments().end(),
230  boost::bind(&LHERunInfoProduct::addComment, product.get(), _1));
231 
232  if (!runInfoProducts.empty()) {
233  runInfoProducts.front().mergeProduct(*product);
234  if (!wasMerged) {
235  runInfoProducts.pop_front();
236  runInfoProducts.push_front(product.release());
237  wasMerged = true;
238  }
239  }
240 
241  runInfo.reset();
242  }
243 
244  partonLevel.reset();
245  return;
246 }
247 
248 // ------------ method called when starting to processes a run ------------
250  // pass the number of events as previous to last argument
251 
252  std::ostringstream eventStream;
253  eventStream << nEvents_;
254  // args_.push_back(eventStream.str());
255  args_.insert(args_.begin() + 1, eventStream.str());
256 
257  // pass the random number generator seed as last argument
258 
260 
261  if (!rng.isAvailable()) {
262  throw cms::Exception("Configuration")
263  << "The ExternalLHEProducer module requires the RandomNumberGeneratorService\n"
264  "which is not present in the configuration file. You must add the service\n"
265  "in the configuration file if you want to run ExternalLHEProducer";
266  }
267  std::ostringstream randomStream;
268  randomStream << rng->mySeed();
269  // args_.push_back(randomStream.str());
270  args_.insert(args_.begin() + 2, randomStream.str());
271 
272  // args_.emplace_back(std::to_string(nThreads_));
273  args_.insert(args_.begin() + 3, std::to_string(nThreads_));
274 
275  for (unsigned int iArg = 0; iArg < args_.size(); iArg++) {
276  LogDebug("LHEInputArgs") << "arg [" << iArg << "] = " << args_[iArg];
277  }
278 
279  executeScript();
280 
281  //fill LHEXMLProduct (streaming read directly into compressed buffer to save memory)
282  std::unique_ptr<LHEXMLStringProduct> p(new LHEXMLStringProduct);
283 
284  //store the XML file only if explictly requested
285  if (storeXML_) {
286  std::ifstream instream(outputFile_);
287  if (!instream) {
288  throw cms::Exception("OutputOpenError") << "Unable to open script output file " << outputFile_ << ".";
289  }
290  instream.seekg(0, instream.end);
291  int insize = instream.tellg();
292  instream.seekg(0, instream.beg);
293  p->fillCompressedContent(instream, 0.25 * insize);
294  instream.close();
295  }
296  run.put(std::move(p), "LHEScriptOutput");
297 
298  // LHE C++ classes translation
299  // (read back uncompressed file from disk in streaming mode again to save memory)
300 
301  std::vector<std::string> infiles(1, outputFile_);
302  unsigned int skip = 0;
303  reader_ = std::make_unique<lhef::LHEReader>(infiles, skip);
304 
305  nextEvent();
306  if (runInfoLast) {
308 
309  std::unique_ptr<LHERunInfoProduct> product(new LHERunInfoProduct(*runInfo->getHEPRUP()));
310  std::for_each(runInfo->getHeaders().begin(),
311  runInfo->getHeaders().end(),
312  boost::bind(&LHERunInfoProduct::addHeader, product.get(), _1));
313  std::for_each(runInfo->getComments().begin(),
314  runInfo->getComments().end(),
315  boost::bind(&LHERunInfoProduct::addComment, product.get(), _1));
316 
317  // keep a copy around in case of merging
318  runInfoProducts.push_back(new LHERunInfoProduct(*product));
319  wasMerged = false;
320 
321  run.put(std::move(product));
322 
323  runInfo.reset();
324  }
325 }
326 
327 // ------------ method called when ending the processing of a run ------------
329  if (!runInfoProducts.empty()) {
330  std::unique_ptr<LHERunInfoProduct> product(runInfoProducts.pop_front().release());
331  run.put(std::move(product));
332  }
333 
334  nextEvent();
335  if (partonLevel) {
337  << "Error in ExternalLHEProducer::endRunProduce(). "
338  << "Event loop is over, but there are still lhe events to process."
339  << "This could happen if lhe file contains more events than requested. This is never expected to happen.";
340  }
341 
342  reader_.reset();
343 
344  if (unlink(outputFile_.c_str())) {
345  throw cms::Exception("OutputDeleteError") << "Unable to delete original script output file " << outputFile_
346  << " (errno=" << errno << ", " << strerror(errno) << ").";
347  }
348 }
349 
350 // ------------ Close all the open file descriptors ------------
352  int maxfd = 1024;
353  int fd;
354 #ifdef __linux__
355  DIR* dir;
356  struct dirent* dp;
357  maxfd = preserve;
358  if ((dir = opendir("/proc/self/fd"))) {
359  errno = 0;
360  while ((dp = readdir(dir)) != nullptr) {
361  if ((strcmp(dp->d_name, ".") == 0) || (strcmp(dp->d_name, "..") == 0)) {
362  continue;
363  }
364  if (sscanf(dp->d_name, "%d", &fd) != 1) {
365  //throw cms::Exception("closeDescriptors") << "Found unexpected filename in /proc/self/fd: " << dp->d_name;
366  return -1;
367  }
368  if (fd > maxfd) {
369  maxfd = fd;
370  }
371  }
372  if (errno) {
373  //throw cms::Exception("closeDescriptors") << "Unable to determine the number of fd (errno=" << errno << ", " << strerror(errno) << ").";
374  return errno;
375  }
376  closedir(dir);
377  }
378 #endif
379  // TODO: assert for an unreasonable number of fds?
380  for (fd = 3; fd < maxfd + 1; fd++) {
381  if (fd != preserve)
382  close(fd);
383  }
384  return 0;
385 }
386 
387 // ------------ Execute the script associated with this producer ------------
389  // Fork a script, wait until it finishes.
390 
391  int rc = 0, rc2 = 0;
392  int filedes[2], fd_flags;
393  unsigned int argc;
394 
395  if (pipe(filedes)) {
396  throw cms::Exception("Unable to create a new pipe");
397  }
398  FileCloseSentry sentry1(filedes[0]), sentry2(filedes[1]);
399 
400  if ((fd_flags = fcntl(filedes[1], F_GETFD, NULL)) == -1) {
401  throw cms::Exception("ExternalLHEProducer")
402  << "Failed to get pipe file descriptor flags (errno=" << rc << ", " << strerror(rc) << ")";
403  }
404  if (fcntl(filedes[1], F_SETFD, fd_flags | FD_CLOEXEC) == -1) {
405  throw cms::Exception("ExternalLHEProducer")
406  << "Failed to set pipe file descriptor flags (errno=" << rc << ", " << strerror(rc) << ")";
407  }
408 
409  argc = 1 + args_.size();
410  // TODO: assert that we have a reasonable number of arguments
411  char** argv = new char*[argc + 1];
412  argv[0] = strdup(scriptName_.c_str());
413  for (unsigned int i = 1; i < argc; i++) {
414  argv[i] = strdup(args_[i - 1].c_str());
415  }
416  argv[argc] = nullptr;
417 
418  pid_t pid = fork();
419  if (pid == 0) {
420  // The child process
421  if (!(rc = closeDescriptors(filedes[1]))) {
422  execvp(argv[0], argv); // If execv returns, we have an error.
423  rc = errno;
424  }
425  while ((write(filedes[1], &rc, sizeof(int)) == -1) && (errno == EINTR)) {
426  }
427  _exit(1);
428  }
429 
430  // Free the arg vector ASAP
431  for (unsigned int i = 0; i < args_.size() + 1; i++) {
432  free(argv[i]);
433  }
434  delete[] argv;
435 
436  if (pid == -1) {
437  throw cms::Exception("ForkException")
438  << "Unable to fork a child (errno=" << errno << ", " << strerror(errno) << ")";
439  }
440 
441  close(filedes[1]);
442  // If the exec succeeds, the read will fail.
443  while (((rc2 = read(filedes[0], &rc, sizeof(int))) == -1) && (errno == EINTR)) {
444  rc2 = 0;
445  }
446  if ((rc2 == sizeof(int)) && rc) {
447  throw cms::Exception("ExternalLHEProducer")
448  << "Failed to execute script (errno=" << rc << ", " << strerror(rc) << ")";
449  }
450  close(filedes[0]);
451 
452  int status = 0;
453  errno = 0;
454  do {
455  if (waitpid(pid, &status, 0) < 0) {
456  if (errno == EINTR) {
457  continue;
458  } else {
459  throw cms::Exception("ExternalLHEProducer")
460  << "Failed to read child status (errno=" << errno << ", " << strerror(errno) << ")";
461  }
462  }
463  if (WIFSIGNALED(status)) {
464  throw cms::Exception("ExternalLHEProducer") << "Child exited due to signal " << WTERMSIG(status) << ".";
465  }
466  if (WIFEXITED(status)) {
467  rc = WEXITSTATUS(status);
468  break;
469  }
470  } while (true);
471  if (rc) {
472  throw cms::Exception("ExternalLHEProducer") << "Child failed with exit code " << rc << ".";
473  }
474 }
475 
476 // ------------ Read the output script ------------
477 #define BUFSIZE 4096
478 std::unique_ptr<std::string> ExternalLHEProducer::readOutput() {
479  int fd;
480  ssize_t n;
481  char buf[BUFSIZE];
482 
483  if ((fd = open(outputFile_.c_str(), O_RDONLY)) == -1) {
484  throw cms::Exception("OutputOpenError") << "Unable to open script output file " << outputFile_
485  << " (errno=" << errno << ", " << strerror(errno) << ").";
486  }
487 
488  std::stringstream ss;
489  while ((n = read(fd, buf, BUFSIZE)) > 0 || (n == -1 && errno == EINTR)) {
490  if (n > 0)
491  ss.write(buf, n);
492  }
493  if (n == -1) {
494  throw cms::Exception("OutputOpenError") << "Unable to read from script output file " << outputFile_
495  << " (errno=" << errno << ", " << strerror(errno) << ").";
496  }
497 
498  if (unlink(outputFile_.c_str())) {
499  throw cms::Exception("OutputDeleteError") << "Unable to delete original script output file " << outputFile_
500  << " (errno=" << errno << ", " << strerror(errno) << ").";
501  }
502 
503  return std::unique_ptr<std::string>(new std::string(ss.str()));
504 }
505 
506 // ------------ method fills 'descriptions' with the allowed parameters for the module ------------
508  //The following says we do not know what parameters are allowed so do no validation
509  // Please change this to state exactly what you do use, even if it is no parameters
511  desc.setComment("Executes an external script and places its output file into an EDM collection");
512 
513  edm::FileInPath thePath;
514  desc.add<edm::FileInPath>("scriptName", thePath);
515  desc.add<std::string>("outputFile", "myoutput");
516  desc.add<std::vector<std::string>>("args");
517  desc.add<uint32_t>("numberOfParameters");
518  desc.addUntracked<uint32_t>("nEvents");
519  desc.addUntracked<bool>("storeXML", false);
520 
521  edm::ParameterSetDescription nPartonMappingDesc;
522  nPartonMappingDesc.add<unsigned>("idprup");
523  nPartonMappingDesc.add<std::string>("order");
524  nPartonMappingDesc.add<unsigned>("np");
525  desc.addVPSetOptional("nPartonMapping", nPartonMappingDesc);
526 
527  descriptions.addDefault(desc);
528 }
529 
531  if (partonLevel)
532  return;
533 
534  if (not reader_) {
535  return;
536  }
537  partonLevel = reader_->next();
538  if (!partonLevel)
539  return;
540 
541  std::shared_ptr<lhef::LHERunInfo> runInfoThis = partonLevel->getRunInfo();
542  if (runInfoThis != runInfoLast) {
543  runInfo = runInfoThis;
544  runInfoLast = runInfoThis;
545  }
546 }
547 
548 //define this as a plug-in
#define LogDebug(id)
void beginRunProduce(edm::Run &run, edm::EventSetup const &es) override
VParameterSet const & getParameterSetVector(std::string const &name) const
void endRunProduce(edm::Run &, edm::EventSetup const &) override
std::shared_ptr< lhef::LHEEvent > partonLevel
OrphanHandle< PROD > put(std::unique_ptr< PROD > product)
Put a new product.
Definition: Event.h:131
std::unique_ptr< std::string > readOutput()
std::shared_ptr< lhef::LHERunInfo > runInfoLast
ParameterDescriptionBase * addUntracked(U const &iLabel, T const &value)
void addHeader(const Header &header)
void addComment(const std::string &line)
ParameterDescriptionBase * addVPSetOptional(U const &iLabel, ParameterSetDescription const &validator, std::vector< ParameterSet > const &defaults)
void produce(edm::Event &, const edm::EventSetup &) override
void addWeight(const WGT &wgt)
bool exists(std::string const &parameterName) const
checks if a parameter exists
#define NULL
Definition: scimark2.h:8
std::vector< std::string > args_
static void fillDescriptions(edm::ConfigurationDescriptions &descriptions)
void setComment(std::string const &value)
int closeDescriptors(int preserve)
int iEvent
Definition: GenABIO.cc:224
#define DEFINE_FWK_MODULE(type)
Definition: MakerMacros.h:16
void addDefault(ParameterSetDescription const &psetDescription)
int np
Definition: AMPTWrapper.h:43
std::unique_ptr< lhef::LHEReader > reader_
bool isAvailable() const
Definition: Service.h:40
#define BUFSIZE
std::map< unsigned, std::pair< unsigned, unsigned > > nPartonMapping_
def pipe(cmdline, input=None)
Definition: pipe.py:5
ParameterDescriptionBase * add(U const &iLabel, T const &value)
virtual std::uint32_t mySeed() const =0
std::shared_ptr< lhef::LHERunInfo > runInfo
void addComment(const std::string &line)
void preallocThreads(unsigned int) override
void put(std::unique_ptr< PROD > product)
Put a new product.
Definition: Run.h:108
HLT enums.
boost::ptr_deque< LHERunInfoProduct > runInfoProducts
fd
Definition: ztee.py:136
ExternalLHEProducer(const edm::ParameterSet &iConfig)
def move(src, dest)
Definition: eostools.py:511
Definition: Run.h:45