EGSnrc C++ class library  Report PIRS-898 (2021)
Iwan Kawrakow, Ernesto Mainegra-Hing, Frederic Tessier, Reid Townson and Blake Walters
egs_run_control.cpp
Go to the documentation of this file.
1 /*
2 ###############################################################################
3 #
4 # EGSnrc egs++ run control
5 # Copyright (C) 2015 National Research Council Canada
6 #
7 # This file is part of EGSnrc.
8 #
9 # EGSnrc is free software: you can redistribute it and/or modify it under
10 # the terms of the GNU Affero General Public License as published by the
11 # Free Software Foundation, either version 3 of the License, or (at your
12 # option) any later version.
13 #
14 # EGSnrc is distributed in the hope that it will be useful, but WITHOUT ANY
15 # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
16 # FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for
17 # more details.
18 #
19 # You should have received a copy of the GNU Affero General Public License
20 # along with EGSnrc. If not, see <http://www.gnu.org/licenses/>.
21 #
22 ###############################################################################
23 #
24 # Author: Iwan Kawrakow, 2005
25 #
26 # Contributors: Frederic Tessier
27 # Hubert Ho
28 # Ernesto Mainegra-Hing
29 # Blake Walters
30 # Marc Chamberland
31 # Reid Townson
32 #
33 ###############################################################################
34 */
35 
36 
42 #include "egs_run_control.h"
43 #include "egs_application.h"
44 #include "egs_input.h"
45 #include "egs_functions.h"
46 #include "egs_library.h"
47 
48 #include <vector>
49 #include <ctime>
50 #include <cstdio>
51 
52 using namespace std;
53 
54 vector<EGS_Library *> rc_libs;
55 static int n_run_controls = 0;
56 
58  geomErrorMax(0), app(a), input(0), ncase(0), ndone(0), maxt(-1), accu(-1),
59  nbatch(10), resume(0), nchunk(1), cpu_time(0), previous_cpu_time(0),
60  rco_type(simple) {
61  n_run_controls++;
62  if (!app) egsFatal("EGS_RunControl::EGS_RunControl: it is not allowed\n"
63  " to construct a run control object on a NULL application\n");
64  input = app->getInput();
65  if (!input) {
66  egsWarning("EGS_RunControl::EGS_RunControl: the application has no"
67  " input\n");
68  return;
69  }
70  input = input->takeInputItem("run control");
71  if (!input) {
72  egsWarning("EGS_RunControl::EGS_RunControl: no 'run control' "
73  "input\n");
74  return;
75  }
76  double ncase_double;
77  int err = input->getInput("number of histories", ncase_double);
78  if (err) {
79  err = input->getInput("ncase", ncase_double);
80  if (err)
81  egsWarning("EGS_RunControl: missing/wrong 'ncase' or "
82  "'number of histories' input\n");
83  }
84  ncase = EGS_I64(ncase_double);
85  /*****************************************************
86  * Split histories into different parallel jobs.
87  * For the JCFO ncase reset to total as it is handled
88  * via the lock file mechanism dispatching smaller
89  * of histories chunks.
90  *****************************************************/
91  if (app->getNparallel()) {
92  ncase /= app->getNparallel();
93  }
94  err = input->getInput("nbatch",nbatch);
95  if (err) {
96  nbatch = 10;
97  }
98  err = input->getInput("max cpu hours allowed",maxt);
99  if (err) {
100  maxt = -1;
101  }
102  err = input->getInput("statistical accuracy sought",accu);
103  if (err) {
104  accu = -1;
105  }
106  err = input->getInput("geometry error limit", geomErrorMax);
107  if (err) {
108  geomErrorMax = 0;
109  }
110 
111  vector<string> ctype;
112  ctype.push_back("first");
113  ctype.push_back("resume");
114  ctype.push_back("analyze");
115  ctype.push_back("combine");
116  ctype.push_back("restart"); // Just for backwards compatibility, use "resume" instead
117  resume = input->getInput("calculation",ctype,0);
118 }
119 
121  if (input) {
122  delete input;
123  }
124  n_run_controls--;
125  if (!n_run_controls) {
126  while (rc_libs.size() > 0) {
127  delete rc_libs[rc_libs.size()-1];
128  rc_libs.pop_back();
129  }
130  }
131 }
132 
133 void EGS_RunControl::describeRCO() {
135  "Run Control Object (RCO):\n"
136  "=========================\n");
137  switch (rco_type) {
138  case simple:
139  egsInformation(" type = simple\n");
140  break;
141  case balanced:
142  egsInformation(" type = balanced (JCF)\n");
143  break;
144  case uniform:
145  egsInformation(" type = uniform\n");
146  break;
147  }
148 }
149 
150 bool EGS_RunControl::storeState(ostream &data) {
151  if (!egsStoreI64(data,ndone)) {
152  return false;
153  }
154  data << " " << (cpu_time+previous_cpu_time) << endl;
155  return data.good();
156 }
157 
158 bool EGS_RunControl::setState(istream &data) {
159  EGS_I64 ndone1;
160  if (!egsGetI64(data,ndone1)) {
161  return false;
162  }
163  ndone += ndone1;
164  ncase += ndone1;
165  data >> previous_cpu_time;
166  return data.good();
167 }
168 
169 bool EGS_RunControl::addState(istream &data) {
170  EGS_Float previous_cpu_time_save = previous_cpu_time;
171  if (!setState(data)) {
172  return false;
173  }
174  previous_cpu_time += previous_cpu_time_save;
175  return true;
176 }
177 
178 void EGS_RunControl::resetCounter() {
179  previous_cpu_time = 0;
180  cpu_time = 0;
181  timer.start();
182  ncase = 0;
183  ndone = 0;
184 }
185 
187  if (resume == 1 || resume == 2 || resume == 4) {
188  if (app->readData()) {
189  return -1;
190  }
191  if (resume == 2) {
192  ncase = ndone;
193  egsInformation("\n\nResult analysis only\n\n");
194  return 1;
195  }
196  }
197  else if (resume == 3) {
198  app->describeSimulation();
199  egsInformation("\n\nCombine results only\n\n");
200  egsInformation("calling combineResults()\n");
201  int err = app->combineResults();
202  ncase = ndone;
203  return err ? -1 : 2;
204  }
205  app->describeSimulation();
206  time_t tinfo = time(0);
207  egsInformation("\n\nStarting simulation on %s\n",
208  asctime(localtime(&tinfo)));
209  if (resume == 0) {
210  egsInformation(" Fresh simulation of %lld histories\n\n\n",ncase);
211  }
212  else {
213  egsInformation(" Resumed simulation with %lld old and %lld"
214  " new histories\n\n\n",ndone,ncase-ndone);
215  }
216  timer.start();
217  return 0;
218 }
219 
220 bool EGS_RunControl::startBatch(int ibatch, EGS_I64 ncase_per_batch) {
221  if (!ibatch) egsInformation(
222  " Batch CPU time Result Uncertainty(%c)\n"
223  "==========================================================\n",'%');
224  if (maxt > 0 && ndone > 0) {
225  EGS_Float time_per_shower = (cpu_time + previous_cpu_time)/ndone;
226  EGS_Float extra_time = time_per_shower*ncase_per_batch;
227  if (cpu_time + extra_time > maxt*3600) {
228  egsWarning("\n\n*** Not enough time to finish another batch\n"
229  " => terminating simulation.\n\n");
230  return false;
231  }
232  }
233  egsInformation("%7d",ibatch+1);
234  ndone += ncase_per_batch;
235  return true;
236 }
237 
239  cpu_time = timer.time();
240  int out = app->outputData();
241  if (out) {
242  egsWarning("\n\noutputData() returned error code %d ?\n",out);
243  }
244  double sum, sum2, norm, count;
245  app->getCurrentResult(sum,sum2,norm,count);
246  double f, df;
247  if (sum > 0 && sum2 > 0 && norm > 0 && count > 1) {
248  f = sum*norm/count;
249  df = count*sum2/(sum*sum)-1;
250  if (df > 0) {
251  df = 100*sqrt(df/(count-1));
252  }
253  else {
254  df = 100;
255  }
256  }
257  else {
258  f = 0;
259  df = 100;
260  }
261  egsInformation(" %12.2f %14g %14.2f\n",cpu_time,f,df);
262  if (df < 100 && accu > 0 && df < accu) {
263  char c = '%';
264  egsWarning("\n\n*** Reached the requested uncertainty of %g%c\n"
265  " => terminating simulation.\n\n",accu,c);
266  return false;
267  }
268  return true;
269 }
270 
271 EGS_UniformRunControl::EGS_UniformRunControl(EGS_Application *a) :
272  EGS_RunControl(a), njob(0), npar(app->getNparallel()),
273  ipar(app->getIparallel()), ifirst(app->getFirstParallel()),
274  milliseconds(1000), check_intervals(5), check_egsdat(true),
275  watcher_job(false) {
276 
277  rco_type = uniform;
278 
279  if (input) {
280 
281  /*Change waiting time to check for parallel run completion*/
282  int dummy;
283  int err = input->getInput("interval wait time", dummy);
284  if (!err) {
285  milliseconds = dummy;
286  }
287 
288  /*Change how many times to check for parallel run completion*/
289  err = input->getInput("number of intervals", dummy);
290  if (!err) {
291  check_intervals = dummy;
292  }
293 
294  /* Define watcher jobs to check for parallel run completion*/
295  vector<int> w_jobs;
296  err = input->getInput("watcher jobs", w_jobs);
297  if (!err) {
298  for (int i = 0; i < w_jobs.size(); i++) {
299  if (ipar == w_jobs[i]) {
300  watcher_job = true;
301  break;
302  }
303  }
304  }
305  else { // use defaults
306  /* last job is watcher job */
307  if (ipar == ifirst + npar - 1) {
308  watcher_job = true;
309  }
310  else {
311  watcher_job = false;
312  }
313  }
314 
315  /* Request checking parallel run completion */
316  vector<string> check_options;
317  check_options.push_back("yes");
318  check_options.push_back("no");
319  int ichk = input->getInput("check jobs completed",check_options,0);
320  if (ichk != 0) {
321  check_egsdat = false; // true by default
322  }
323 
324  }
325  else { // use defaults if no RCO input found
326  /* last job is watcher job */
327  if (ipar == ifirst + npar - 1) {
328  watcher_job = true;
329  }
330  }
331 }
332 
333 int EGS_UniformRunControl::startSimulation() {
334 
335 
336  /* Check run completion based on *egsdat files requires erasing
337  existing files from previous runs.
338  */
339  if (check_egsdat) {
340  char buf[512];
341  sprintf(buf,"%s_w%d.egsdat",app->getFinalOutputFile().c_str(), ipar);
342  string datFile = egsJoinPath(app->getAppDir(),buf);
343  if (remove(datFile.c_str()) == 0) {
344  egsWarning("EGS_UniformRunControl: %s deleted\n",
345  datFile.c_str());
346  }
347  }
348 
350 }
351 
352 void EGS_UniformRunControl::describeRCO() {
353 
354  EGS_RunControl::describeRCO();
355 
356  if (watcher_job) {
357  if (check_egsdat) {
359  " Watcher job: remains running after completion checking\n"
360  " for other jobs finishing every %d s for %d s!\n",
361  milliseconds/1000, check_intervals*milliseconds/1000);
362  }
363  else {
365  " Option to check for finishing jobs is OFF!\n\n");
366  }
367  }
368 
369 }
370 
371 #ifdef WIN32
372 
373  #include <io.h>
374  #include <stdio.h>
375  #include <fcntl.h>
376  #include <sys/types.h>
377  #include <sys/stat.h>
378  #include <sys/locking.h>
379  #include <windows.h>
380 
381  #define OPEN_FILE _open
382  #define CLOSE_FILE _close
383  #define CREATE_FLAGS _O_CREAT | _O_EXCL | _O_RDWR, _S_IREAD | _S_IWRITE
384  #define OPEN_FLAGS _O_RDWR,_S_IREAD | _S_IWRITE
385  #define WAIT_FOR_FILE Sleep(1000)
386  #define WRITE_FILE _write
387  #define READ_FILE _read
388 
389 #else
390 
391  #include <unistd.h>
392  #include <fcntl.h>
393  #include <sys/types.h>
394  #include <sys/stat.h>
395  #include <errno.h>
396  #include <string.h>
397  #include <stdio.h>
398 
399  #define OPEN_FILE open
400  #define CLOSE_FILE close
401  #define CREATE_FLAGS O_RDWR | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR
402  #define OPEN_FLAGS O_RDWR
403  #define WAIT_FOR_FILE sleep(1)
404  #define WRITE_FILE write
405  #define READ_FILE read
406 
407 #endif
408 
409 #ifndef SKIP_DOXYGEN
410 
415 class EGS_LOCAL EGS_FileLocking {
416 public:
417  int fd;
418  bool is_locked;
419  int ntry;
420 #ifndef WIN32
421  struct flock fl_write, fl_unlock;
422 #endif
423  EGS_FileLocking() : fd(-1), is_locked(false), ntry(15) {
424 #ifndef WIN32
425  fl_write.l_type = F_WRLCK;
426  fl_write.l_whence = SEEK_SET;
427  fl_write.l_start = 0;
428  fl_write.l_len = 0;
429  fl_unlock.l_type = F_UNLCK;
430  fl_unlock.l_whence = SEEK_SET;
431  fl_unlock.l_start = 0;
432  fl_unlock.l_len = 0;
433 #endif
434  };
435  ~EGS_FileLocking() {
436  if (fd > 0) {
437  CLOSE_FILE(fd);
438  }
439  };
440  bool createControlFile(const char *fname) {
441  is_locked = false;
442  if (fd > 0) {
443  CLOSE_FILE(fd);
444  }
445  fd = OPEN_FILE(fname,CREATE_FLAGS);
446  egsWarning("createControlFile: file=%s fd=%d\n",fname,fd);
447  if (fd < 0) {
448  egsWarning("createControlFile(): open failed! (fd=%d)\n",fd);
449 #ifndef WIN32
450  perror("System error was");
451 #endif
452  }
453  return lockControlFile();
454  };
455  bool openControlFile(const char *fname) {
456  is_locked = false;
457  if (fd > 0) {
458  CLOSE_FILE(fd);
459  }
460  for (int t=0; t<ntry; t++) {
461  fd = OPEN_FILE(fname,OPEN_FLAGS);
462  if (fd > 0) {
463  break;
464  }
465  WAIT_FOR_FILE;
466  }
467  return (fd > 0);
468  };
469  bool closeControlFile() {
470  if (fd > 0) {
471  int res = CLOSE_FILE(fd);
472  fd = -1;
473  return !res;
474  }
475  return true;
476  };
477  bool lockControlFile() {
478  if (is_locked) {
479  return true;
480  }
481  if (fd < 0) {
482  return false;
483  }
484 #ifdef WIN32
485  long np = _lseek(fd,0L,SEEK_SET);
486  if (np) {
487  egsWarning("lockControlFile: _lseek returned %d?\n",np);
488  return false;
489  }
490  int res = _locking(fd,_LK_LOCK,1000000L);
491  if (!res) {
492  is_locked = true;
493  return true;
494  }
495  return false;
496 #else
497  for (int i1=0; i1<5; i1++) {
498  for (int i2=0; i2<12; i2++) {
499  int res = fcntl(fd,F_SETLK,&fl_write);
500  if (!res) {
501  is_locked = true;
502  return true;
503  }
504  WAIT_FOR_FILE ;
505  }
506  egsWarning("lockControlFile: failed to lock file for "
507  "12 seconds...\n");
508  }
509  return false;
510 #endif
511  };
512  bool unlockControlFile() {
513  if (!is_locked) {
514  return true;
515  }
516  if (fd < 0) {
517  return false;
518  }
519 #ifdef WIN32
520  int np = _lseek(fd,0L,SEEK_SET);
521  if (np) {
522  egsWarning("unlockControlFile: _lseek returned %d?\n",np);
523  return false;
524  }
525  int res = _locking(fd,_LK_UNLCK,1000000L);
526 #else
527  int res = fcntl(fd,F_SETLKW,&fl_unlock);
528 #endif
529  if (!res) {
530  is_locked = false;
531  return true;
532  }
533  return false;
534  };
535  bool rewindControlFile() {
536  if (fd < 0) {
537  return false;
538  }
539  if (!is_locked) {
540  if (!lockControlFile()) {
541  return false;
542  }
543  }
544 #ifdef WIN32
545  return !_lseek(fd,0,SEEK_SET);
546 #else
547  return !lseek(fd,0,SEEK_SET);
548 #endif
549  };
550 };
551 
552 #endif
553 
554 EGS_JCFControl::EGS_JCFControl(EGS_Application *a, int Nbuf) :
555  EGS_RunControl(a), tsum(0), tsum2(0), tcount(0), norm(1), last_sum(0),
556  last_sum2(0), last_count(0), njob(0), npar(app->getNparallel()),
557  ipar(app->getIparallel()), ifirst(app->getFirstParallel()),
558  first_time(true), removed_jcf(false), nbuf(Nbuf), p(new EGS_FileLocking) {
559 
560  rco_type = balanced;
561 
562  /* Recover initial number of histories */
563  if (npar) {
564  ncase *= npar;
565  }
566 
567  if (input) {
568  int err = input->getInput("nchunk",nchunk);
569  if (err) {
570  nchunk = 10;
571  }
572  }
573  else {
574  nchunk = 10;
575  }
576  if (nbuf < 0) {
577  nbuf = 1024;
578  }
579  buf = new char [nbuf];
580  nleft = ncase;
581  ntot = 0;
582  //egsInformation("EGS_JCFControl::EGS_JCFControl:\n");
583  //egsInformation(" ncase = %lld nleft = %lld nchunk = %d\n",
584  // nleft,ncase,nchunk);
585 }
586 
587 bool EGS_JCFControl::createControlFile() {
588  string cfile = egsJoinPath(app->getAppDir(),app->getFinalOutputFile());
589  cfile += ".lock";
590  if (!p->createControlFile(cfile.c_str())) {
591  egsWarning("EGS_JCFControl: failed to create or lock the "
592  " job control file %s\n\n",cfile.c_str());
593  return false;
594  }
595  if (p->fd < 0) {
596  return false;
597  }
598  writeControlString();
599  int nwant = strlen(buf)+1;
600  int nwrite = WRITE_FILE(p->fd,buf,nwant);
601  if (nwrite != nwant) {
602  return false;
603  }
604  return p->unlockControlFile();
605 }
606 
607 bool EGS_JCFControl::openControlFile() {
608  string cfile = egsJoinPath(app->getAppDir(),app->getFinalOutputFile());
609  cfile += ".lock";
610  if (!p->openControlFile(cfile.c_str())) {
611  egsWarning("EGS_JCFControl: failed to open the "
612  " job control file %s\n\n",cfile.c_str());
613  return false;
614  }
615  return true;
616 }
617 
618 #ifdef NO_SSTREAM
619  #include <strstream>
620  #define MY_OSTREAM std::ostrstream
621  #define MY_ISTREAM std::istrstream
622 #else
623  #include <sstream>
624  #define MY_OSTREAM std::ostringstream
625  #define MY_ISTREAM std::istringstream
626 #endif
627 
628 bool EGS_JCFControl::writeControlString() {
629  //if( first_time ) { start_time = time(0); first_time = false; }
630  if (first_time) {
631  start_time = time(0);
632  }
633  /*
634  MY_OSTREAM data(buf);
635  //ostream &data = cout;
636  if( !egsStoreI64(data,ntot) ) return false;
637  if( !egsStoreI64(data,nleft) ) return false;
638  data << " " << njob << " " << tsum << " " << tsum2 << " " << tcount << " ";
639  double f = tsum*norm, df;
640  if( tsum > 0 && tsum2 > 0 && norm > 0 && tcount > 1 ) {
641  df = tcount*tsum2/(tsum*tsum)-1;
642  if( df > 0 ) df = 100*sqrt(df/(tcount-1)); else df = 100;
643  } else df = 100;
644  data << f << " " << df << " " << start_time << endl;
645  if( f > 0 && df < 100 ) egsInformation("\nCombined result from all "
646  "parallel jobs: %g +/- %g%%\n\n",f,df);
647  egsInformation("EGS_JCFControl::writeControlString: <%s>\n",buf);
648  return data.good();
649  */
650  double f = tsum*norm, df;
651  if (tsum > 0 && tsum2 > 0 && norm > 0 && tcount > 1) {
652  f = tsum*norm/tcount;
653  df = tcount*tsum2/(tsum*tsum)-1;
654  if (df > 0) {
655  df = 100*sqrt(df/(tcount-1));
656  }
657  else {
658  df = 100;
659  }
660  }
661  else {
662  df = 100;
663  }
664  sprintf(buf,"%lld %lld %d %lg %lg %lg %lg %lg %ld ",ntot,nleft,njob,tsum,
665  tsum2,tcount,f,df,start_time);
666  return true;
667 }
668 
669 bool EGS_JCFControl::getCombinedResult(double &f, double &df) const {
670  if (tsum > 0 && tsum2 > 0 && norm > 0 && tcount > 1) {
671  f = tsum*norm/tcount;
672  df = tcount*tsum2/(tsum*tsum)-1;
673  if (df > 0) {
674  df = 100*sqrt(df/(tcount-1));
675  }
676  else {
677  df = 100;
678  }
679  return true;
680  }
681  df = 100;
682  f = 0;
683  return false;
684 }
685 
686 bool EGS_JCFControl::readControlString() {
687  /*
688  MY_ISTREAM data(buf);
689  if( !egsGetI64(data,ntot) ) return false;
690  if( !egsGetI64(data,nleft) ) return false;
691  double f,df;
692  data >> njob >> tsum >> tsum2 >> tcount >> f >> df >> start_time;
693  return data.good();
694  */
695  double f,df;
696  int res = sscanf(buf,"%lld %lld %d %lg %lg %lg %lg %lg %ld",
697  &ntot,&nleft,&njob,&tsum,&tsum2,&tcount,&f,&df,&start_time);
698  if (res == EOF || res != 9) {
699  return false;
700  }
701  return true;
702 }
703 
704 int EGS_JCFControl::startSimulation() {
706  if (res) {
707  return res;
708  }
709  bool ok = (ipar == ifirst) ? createControlFile() : openControlFile();
710  if (ok) {
711  egsInformation(" Parallel run with %d jobs and %d chunks per "
712  "job\n\n\n",npar,nchunk);
713  return 0;
714  }
715  return -99;
716 }
717 
718 bool EGS_JCFControl::readControlFile() {
719  if (!p->rewindControlFile()) {
720  egsWarning("EGS_JCFControl: failed to rewind the job control file\n");
721  return false;
722  }
723  int res = READ_FILE(p->fd,buf,nbuf-1);
724  if (res <= 0) {
725  p->unlockControlFile();
726  egsWarning("EGS_JCFControl: failed to read the job control file\n");
727  return false;
728  }
729  buf[res] = 0;
730  if (!readControlString()) {
731  p->unlockControlFile();
732  egsWarning("EGS_JCFControl: failed to read from the control string"
733  " <%s>\n",buf);
734  return false;
735  }
736  return true;
737 }
738 
739 bool EGS_JCFControl::writeControlFile() {
740  if (!writeControlString()) {
741  egsWarning("EGS_JCFControl::writeControlFile: failed to write to the "
742  "control string\n");
743  return false;
744  }
745  if (!p->rewindControlFile()) {
746  egsWarning("EGS_JCFControl: failed to rewind the job control file\n");
747  return false;
748  }
749  int nwant = strlen(buf)+1;
750  int nwrite = WRITE_FILE(p->fd,buf,nwant);
751  if (!p->unlockControlFile()) {
752  egsWarning("EGS_JCFControl::writeControlFile: failed to unlock the "
753  "control file\n");
754  return false;
755  }
756  if (nwrite != nwant) {
757  egsWarning("EGS_JCFControl::getNextChunk: could write only %d "
758  "instead of %d chars to the job control file?\n",nwrite,nwant);
759  return false;
760  }
761  return true;
762 }
763 
764 EGS_I64 EGS_JCFControl::getNextChunk() {
765  if (!readControlFile()) {
766  return -1;
767  }
768  if (first_time) {
769  first_time = false;
770  njob++;
771  }
772  double sum, sum2, count;
773  app->getCurrentResult(sum,sum2,norm,count);
774  tsum += sum - last_sum;
775  tsum2 += sum2 - last_sum2;
776  tcount += count - last_count;
777  last_sum = sum;
778  last_sum2 = sum2;
779  last_count = count;
780  EGS_I64 nrun = ncase/(npar*nchunk);
781  if (nrun < 1) {
782  nrun = 1;
783  }
784  if (nrun > nleft) {
785  nrun = nleft;
786  }
787  if (nrun > 0) {
788  app->setSimulationChunk(ntot,nrun,npar,nchunk);
789  }
790  nleft -= nrun;
791  ntot += nrun;
792  writeControlFile();
793  double f,df;
794  if (accu > 0 && getCombinedResult(f,df)) {
795  if (df < 100 && df < accu) {
796  char c = '%';
797  egsWarning("\n\n*** After combining the results of all parallel "
798  "jobs the requested\n uncertainty of %g%c was reached: %g%c\n"
799  " => terminating simulation.\n\n",accu,c,df,c);
800  return 0;
801  }
802  }
803  return nrun;
804 }
805 
819 void rco_sleep(const int &mscnds) {
820 #ifdef WIN32
821  Sleep(mscnds);
822 #else
823  usleep(mscnds * 1000);
824 #endif
825 }
826 
828  cpu_time = timer.time();
829  egsInformation("\n\nFinished simulation\n\n");
830  egsInformation("%-40s%.2f (sec.) %.4f(hours)\n",
831  "Total cpu time for this run:",cpu_time,cpu_time/3600);
832  //egsInformation("Total cpu time for this run: %g seconds (%g hours)\n\n",
833  // cpu_time, cpu_time/3600);
834  if (previous_cpu_time > 0)
835  egsInformation("%-40s%.2f (sec.) %.4f (hours)\n",
836  "CPU time including previous runs:",cpu_time+previous_cpu_time,
837  (cpu_time+previous_cpu_time)/3600);
838  egsInformation("%-40s%-14g\n","Histories per hour:",3600.*ndone/
839  (cpu_time+previous_cpu_time));
840  egsInformation("%-40s%-14lld\n","Number of random numbers used:",
841  app->randomNumbersUsed());
842  double ch_steps, all_steps;
843  app->getElectronSteps(ch_steps,all_steps);
844  egsInformation("%-40s%-14g\n","Number of electron CH steps:",
845  ch_steps);
846  //egsInformation("%-40s%14g\n","Number of all electron steps:",
847  // all_steps);
848  egsInformation("%-40s","Number of all electron steps:");
849  egsInformation("%-14g\n",all_steps);
850 
851  int n_par = app->getNparallel(),
852  i_par = app->getIparallel(),
853  i_first = app->getFirstParallel();
854  /* If parallel run and last job, trigger the app combineResults method */
855  return (n_par && i_par == i_first + n_par - 1) ? 1 : 0;
856 }
857 
860  if (err < 0) {
861  return err;
862  }
863  /* Check and wait for all jobs to finish */
864  if (watcher_job) {
865  int interval = 0, njobs_done = 0, njobs_done_old= 0;
866  while (interval < check_intervals) {
867  rco_sleep(milliseconds);
868  if (check_egsdat) {
869  njobs_done = app->howManyJobsDone();
870  //egsInformation("\n-> Finished %d jobs...\n",njobs_done);
871  if (njobs_done == npar - 1) {
872  watcher_job=false;//don't enter this after all jobs done!
873  break;
874  }
875  // Only combine if new jobs finished
876  if (njobs_done_old < njobs_done) {
877  egsInformation("=> Combining %d jobs ...\n",njobs_done);
878  app->combinePartialResults();
879  }
880  njobs_done_old = njobs_done;
881  }
882  interval++;
883  }
884  return 1;
885  }
886  /*I am not a watcher job, do not combine results yet!*/
887  return 0;
888 }
889 
890 int EGS_JCFControl::finishSimulation() {
892  if (err < 0) {
893  return err;
894  }
895  if (removed_jcf) {
896  return 0;
897  }
898  if (!readControlFile()) {
899  return -2;
900  }
901  njob--;
902  writeControlFile();
903  p->closeControlFile();
904  if (njob > 0 || removed_jcf) {
905  return 0;
906  }
907  string cfile = egsJoinPath(app->getAppDir(),app->getFinalOutputFile());
908  cfile += ".lock";
909 #ifdef WIN32
910  int res = _unlink(cfile.c_str());
911 #else
912  int res = unlink(cfile.c_str());
913 #endif
914  if (res) egsWarning("EGS_JCFControl::finishSimulation: failed to remove "
915  " the job control file %s\n",cfile.c_str());
916  removed_jcf = true;
917  return 1;
918 }
919 
920 EGS_JCFControl::~EGS_JCFControl() {
921  delete p;
922 }
923 
924 bool EGS_JCFControl::closeControlFile() {
925  return p->closeControlFile();
926 }
927 
928 bool EGS_JCFControl::lockControlFile() {
929  return p->lockControlFile();
930 }
931 
932 bool EGS_JCFControl::unlockControlFile() {
933  return p->unlockControlFile();
934 }
935 
936 bool EGS_JCFControl::rewindControlFile() {
937  return p->rewindControlFile();
938 }
939 
940 typedef EGS_RunControl *(*EGS_RunControlCreationFunction)(EGS_Application *);
941 
942 EGS_RunControl *EGS_RunControl::getRunControlObject(EGS_Application *a) {
943  if (!a) {
944  egsWarning("EGS_RunControl::getRunControlObject(): "
945  "null application?\n");
946  return 0;
947  }
948  EGS_Input *inp = a->getInput();
949  EGS_Input *irc = 0;
950  if (inp) {
951  irc = inp->getInputItem("run control");
952  }
953  /* If no input file, defaults to simple RCO for single runs and
954  to JCF RCO for parallel runs.
955  */
956  if (!irc) {
957  /*
958  egsWarning("EGS_RunControl::getRunControlObject(): "
959  "the application does not have any input\n");
960  return 0;
961  */
962  if (a->getNparallel() > 0) {
963  return new EGS_JCFControl(a);
964  }
965  else {
966  return new EGS_RunControl(a);
967  }
968  }
969  /*
970  EGS_Input *irc = inp->getInputItem("run control");
971  if( !irc ) {
972  egsWarning("EGS_RunControl::getRunControlObject(): "
973  "the application input has no 'run control' item\n");
974  return 0;
975  }
976  */
977  string libname;
978  int err = irc->getInput("library",libname);
979  EGS_RunControl *result;
980  if (!err) {
981  EGS_Library *lib = 0;
982  for (unsigned int j=0; j<rc_libs.size(); j++) {
983  if (libname == rc_libs[j]->libraryName()) {
984  lib = rc_libs[j];
985  break;
986  }
987  }
988  if (!lib) {
989  string dsodir = egsJoinPath("egs++","dso");
990  dsodir = egsJoinPath(dsodir,CONFIG_NAME);
991  dsodir = egsJoinPath(a->getHenHouse(),dsodir);
992  lib = new EGS_Library(libname.c_str(),dsodir.c_str());
993  lib->load();
994  if (!lib->isLoaded()) {
995  egsWarning("EGS_RunControl::getRunControlObject: failed to"
996  " load the library %s from %s\n",libname.c_str(),
997  dsodir.c_str());
998  delete irc;
999  return 0;
1000  }
1001  rc_libs.push_back(lib);
1002  }
1003  EGS_RunControlCreationFunction create =
1004  (EGS_RunControlCreationFunction) lib->resolve("createRunControl");
1005  if (!create) {
1006  egsWarning("EGS_RunControl::getRunControlObject: failed to"
1007  " resolve the run control creation function of library %s\n",
1008  libname.c_str());
1009  result = 0;
1010  }
1011  else {
1012  result = create(a);
1013  }
1014  }
1015  else {
1016  if (a->getNparallel() > 0) {
1017  vector<string> allowed_types;
1018  allowed_types.push_back("simple");
1019  allowed_types.push_back("uniform");
1020  allowed_types.push_back("balanced");
1021  int rco_t = irc->getInput("rco type",allowed_types,2);
1022  switch (rco_t) {
1023  case 0:
1024  result = new EGS_RunControl(a);
1025  break;
1026  case 1:
1027  result = new EGS_UniformRunControl(a);
1028  break;
1029  case 2:
1030  result = new EGS_JCFControl(a);
1031  break;
1032  default:
1033  result = new EGS_JCFControl(a);
1034  }
1035  }
1036  else {
1037  result = new EGS_RunControl(a);
1038  }
1039  }
1040  delete irc;
1041  return result;
1042 }
Base class for advanced EGSnrc C++ applications.
virtual EGS_I64 randomNumbersUsed() const
Returns the number of random numbers used.
virtual void describeSimulation()
Describe the simulation.
virtual int outputData()
Output intermediate results.
int getNparallel() const
Returns the number of parallel jobs executing.
EGS_Input * getInput()
Returns a pointer to the EGS_Input object containing the user input to the application found in the i...
virtual void getElectronSteps(double &ch_steps, double &all_steps) const
Get the number of electron steps taken.
virtual void getCurrentResult(double &sum, double &sum2, double &norm, double &count)
Report the current result.
int getFirstParallel() const
Returns the first job number in a parallel run.
int howManyJobsDone()
Counts how many *.egsdat files in app folder.
virtual void setSimulationChunk(EGS_I64 nstart, EGS_I64 nrun, int npar, int nchunk)
Set the simulation chunk.
const string & getFinalOutputFile() const
Returns the base name of the final output file(s)
const string & getAppDir() const
Returns the absolute path to the user code directory.
virtual int combineResults()
Combine results from parallel runs.
const string & getHenHouse() const
Returns the HEN_HOUSE directory.
int getIparallel() const
Returns the job number in a parallel run.
virtual int combinePartialResults()
Combine intermediate results from parallel runs.
virtual int readData()
Read intermediate results.
A class for storing information in a tree-like structure of key-value pairs. This class is used throu...
Definition: egs_input.h:182
EGS_Input * takeInputItem(const string &key, bool self=true)
Get the property named key.
Definition: egs_input.cpp:229
int getInput(const string &key, vector< string > &values) const
Assign values to an array of strings from an input identified by key.
Definition: egs_input.cpp:341
A 'job control file' (JCF) RCO.
A class for dynamically loading shared libraries.
Definition: egs_library.h:52
bool isLoaded() const
Returns true if the library is loaded, false otherwise.
bool load()
Loads the library.
void * resolve(const char *func)
Returns the address of the exported symbol func.
A simple run control object for advanced EGSnrc C++ applications.
RCOType rco_type
RCO type to use.
@ balanced
parallel jobs with balanced load via JCF
@ uniform
parallel jobs with same numbe of histories
@ simple
single job or multiple independent jobs
EGS_RunControl(EGS_Application *app)
Creates an RCO for the application app.
virtual int startSimulation()
Starts the simulation.
virtual bool startBatch(int, EGS_I64)
Start a new batch.
virtual ~EGS_RunControl()
Destructor.
virtual bool finishBatch()
Finish a batch.
virtual int finishSimulation()
Finish the simulation.
EGS_Float time()
Returns the CPU time in seconds since start() was called.
Definition: egs_timer.cpp:106
void start()
Starts the time measurement.
Definition: egs_timer.cpp:102
A job control object for homogeneous computing environments (HCE).
int finishSimulation()
Uses 'watcher' jobs to determine if the simulation has finished.
EGS_Application class header file.
Global egspp functions header file.
EGS_Input class header file.
EGS_Library class header file.
void rco_sleep(const int &mscnds)
Suspend execution for a given time (in ms)
EGS_RunControl and EGS_JCFControl class header file.
bool EGS_EXPORT egsStoreI64(ostream &data, EGS_I64 n)
Writes the 64 bit integer n to the output stream data and returns true on success,...
EGS_InfoFunction EGS_EXPORT egsInformation
Always use this function for reporting the progress of a simulation and any other type of information...
EGS_InfoFunction EGS_EXPORT egsFatal
Always use this function for reporting fatal errors.
bool EGS_EXPORT egsGetI64(istream &data, EGS_I64 &n)
Reads a 64 bit integer from the stream data and assigns it to n. Returns true on success,...
string egsJoinPath(const string &first, const string &second)
Join two path variables (or a path and a file name) using the platform specific directory separator a...
EGS_InfoFunction EGS_EXPORT egsWarning
Always use this function for reporting warnings.