Commit 9b24f41f0ffc461c5a5136969d6f08a6af38b3ba

Authored by Jürgen Knödlseder
1 parent d3d48015

Add mputils.process() function and reduce code duplication

cscripts/cslightcrv.py
... ... @@ -647,20 +647,13 @@ class cslightcrv(ctools.csobservation):
647 647 # Write header
648 648 self._log_header1(gammalib.TERSE, 'Generate lightcurve')
649 649  
650   - # If using multiprocessing
  650 + # If more than a single thread is requested then use multiprocessing
651 651 if self._nthreads > 1:
652 652  
653   - # Create pool of workers
654   - from multiprocessing import Pool
655   - pool = Pool(processes = self._nthreads)
656   -
657   - # Run time bin analysis in parallel with map
  653 + # Compute time bins
658 654 args = [(self, i) for i in range(self._tbins.size())]
659   - poolresults = pool.map(_multiprocessing_func_wrapper, args)
660   -
661   - # Close pool and join
662   - pool.close()
663   - pool.join()
  655 + poolresults = mputils.process(self._nthreads,
  656 + _multiprocessing_func_wrapper, args)
664 657  
665 658 # Construct results
666 659 results = []
... ...
cscripts/csphagen.py
... ... @@ -578,17 +578,10 @@ class csphagen(ctools.csobservation):
578 578 # If there is more than one observation and we use multiprocessing
579 579 if self._nthreads > 1 and self.obs().size() > 1:
580 580  
581   - # Create pool of workers
582   - from multiprocessing import Pool
583   - pool = Pool(processes = self._nthreads)
584   -
585   - # Run time bin analysis in parallel with map
  581 + # Compute observations
586 582 args = [(self, i) for i in range(self.obs().size())]
587   - poolresults = pool.map(_multiprocessing_func_wrapper, args)
588   -
589   - # Close pool and join
590   - pool.close()
591   - pool.join()
  583 + poolresults = mputils.process(self._nthreads,
  584 + _multiprocessing_func_wrapper, args)
592 585  
593 586 # Construct results
594 587 for i in range(self.obs().size()):
... ...
cscripts/csphasecrv.py
... ... @@ -493,20 +493,13 @@ class csphasecrv(ctools.csobservation):
493 493 # Write header
494 494 self._log_header1(gammalib.TERSE, 'Generate phase curve')
495 495  
496   - # If using multiprocessing
  496 + # If more than a single thread is requested then use multiprocessing
497 497 if self._nthreads > 1:
498 498  
499   - # Create pool of workers
500   - from multiprocessing import Pool
501   - pool = Pool(processes = self._nthreads)
502   -
503   - # Run time bin analysis in parallel with map
  499 + # Compute phase bins
504 500 args = [(self, phbin) for phbin in self._phbins]
505   - poolresults = pool.map(_multiprocessing_func_wrapper, args)
506   -
507   - # Close pool and join
508   - pool.close()
509   - pool.join()
  501 + poolresults = mputils.process(self._nthreads,
  502 + _multiprocessing_func_wrapper, args)
510 503  
511 504 # Construct results
512 505 for i in range(len(self._phbins)):
... ...
cscripts/cspull.py
... ... @@ -377,40 +377,34 @@ class cspull(ctools.csobservation):
377 377 # Write header
378 378 self._log_header1(gammalib.TERSE, 'Generate pull distribution')
379 379  
380   - # If using multiprocessing
381   - if self._nthreads > 1:
382   -
383   - # Create pool of workers
384   - from multiprocessing import Pool
385   - pool = Pool(processes = self._nthreads)
386   -
387   - # Run time bin analysis in parallel with map
388   - args = [(self, seed + self['seed'].integer())
389   - for seed in range(self['ntrials'].integer())]
390   - poolresults = pool.map(_multiprocessing_func_wrapper, args)
  380 + # Get number of trials
  381 + ntrials = self['ntrials'].integer()
391 382  
392   - # Close pool and join
393   - pool.close()
394   - pool.join()
  383 + # Get seed value
  384 + seed = self['seed'].integer()
395 385  
396   - # Write results
397   - for seed in range(self['ntrials'].integer()):
398   - result = poolresults[seed][0]
399   - ioutils.write_csv_row(self['outfile'].filename().url(), seed,
400   - result['colnames'], result['values'])
401   - self._log_string(gammalib.TERSE, poolresults[seed][1]['log'], False)
  386 + # If more than a single thread is requested then use multiprocessing
  387 + if self._nthreads > 1:
  388 + args = [(self, i + seed) for i in range(ntrials)]
  389 + poolresults = mputils.process(self._nthreads,
  390 + _multiprocessing_func_wrapper, args)
402 391  
403   - # Otherwise, loop over trials
404   - else:
  392 + # Continue with regular processing
  393 + for i in range(ntrials):
405 394  
406   - for seed in range(self['ntrials'].integer()):
  395 + # If multiprocessing was used then recover results and put them
  396 + # into the log file
  397 + if self._nthreads > 1:
  398 + result = poolresults[i][0]
  399 + self._log_string(gammalib.TERSE, poolresults[i][1]['log'], False)
407 400  
408   - # Make a trial and add initial seed
409   - result = self._trial(seed + self['seed'].integer())
  401 + # ... otherwise make a trial now
  402 + else:
  403 + result = self._trial(i + seed)
410 404  
411   - # Write out trial result
412   - ioutils.write_csv_row(self['outfile'].filename().url(), seed,
413   - result['colnames'], result['values'])
  405 + # Write out trial result
  406 + ioutils.write_csv_row(self['outfile'].filename().url(), i,
  407 + result['colnames'], result['values'])
414 408  
415 409 # Return
416 410 return
... ...
cscripts/cssens.py
... ... @@ -715,17 +715,10 @@ class cssens(ctools.csobservation):
715 715 # If using multiprocessing
716 716 if self._nthreads > 1 and self._ebounds.size() > 1:
717 717  
718   - # Create pool of workers
719   - from multiprocessing import Pool
720   - pool = Pool(processes = self._nthreads)
721   -
722   - # Run energy bin analysis in parallel with map
  718 + # Compute phase bins
723 719 args = [(self, ieng) for ieng in range(self._ebounds.size())]
724   - poolresults = pool.map(_multiprocessing_func_wrapper, args)
725   -
726   - # Close pool and join
727   - pool.close()
728   - pool.join()
  720 + poolresults = mputils.process(self._nthreads,
  721 + _multiprocessing_func_wrapper, args)
729 722  
730 723 # Construct results
731 724 for ieng in range(self._ebounds.size()):
... ...
cscripts/csspec.py
... ... @@ -771,20 +771,13 @@ class csspec(ctools.csobservation):
771 771 # Initialise results
772 772 results = []
773 773  
774   - # If using multiprocessing
  774 + # If more than a single thread is requested then use multiprocessing
775 775 if self._nthreads > 1:
776 776  
777   - # Create pool of workers
778   - from multiprocessing import Pool
779   - pool = Pool(processes = self._nthreads)
780   -
781   - # Run energy bin analysis in parallel with map
  777 + # Compute energy bins
782 778 args = [(self, i) for i in range(self._ebounds.size())]
783   - poolresults = pool.map(_multiprocessing_func_wrapper, args)
784   -
785   - # Close pool and join
786   - pool.close()
787   - pool.join()
  779 + poolresults = mputils.process(self._nthreads,
  780 + _multiprocessing_func_wrapper, args)
788 781  
789 782 # Construct results
790 783 for i in range(self._ebounds.size()):
... ...
cscripts/cstsdist.py
... ... @@ -329,38 +329,30 @@ class cstsdist(ctools.csobservation):
329 329 # Write header
330 330 self._log_header1(gammalib.TERSE, 'Generate TS distribution')
331 331  
332   - # If using multiprocessing
333   - if self._nthreads > 1:
334   -
335   - # Create pool of workers
336   - from multiprocessing import Pool
337   - pool = Pool(processes = self._nthreads)
338   -
339   - # Run time bin analysis in parallel with map
340   - args = [(self, seed) for seed in range(self['ntrials'].integer())]
341   - poolresults = pool.map(_multiprocessing_func_wrapper, args)
  332 + # Get number of trials
  333 + ntrials = self['ntrials'].integer()
342 334  
343   - # Close pool and join
344   - pool.close()
345   - pool.join()
346   -
347   - # Write results
348   - for seed in range(self['ntrials'].integer()):
349   - result = poolresults[seed][0]
350   - ioutils.write_csv_row(self['outfile'].filename().url(), seed,
351   - result['colnames'], result['values'])
352   - self._log_string(gammalib.TERSE, poolresults[seed][1]['log'], False)
  335 + # If more than a single thread is requested then use multiprocessing
  336 + if self._nthreads > 1:
  337 + args = [(self, i) for i in range(ntrials)]
  338 + poolresults = mputils.process(self._nthreads,
  339 + _multiprocessing_func_wrapper, args)
353 340  
354   - # Otherwise, loop over trials
355   - else:
  341 + # Continue with regular processing
  342 + for i in range(ntrials):
356 343  
357   - for seed in range(self['ntrials'].integer()):
  344 + # If multiprocessing was used then recover results and put them
  345 + # into the log file
  346 + if self._nthreads > 1:
  347 + result = poolresults[i][0]
  348 + self._log_string(gammalib.TERSE, poolresults[i][1]['log'], False)
358 349  
359   - # Make a trial
360   - result = self._trial(seed)
  350 + # ... otherwise make a trial now
  351 + else:
  352 + result = self._trial(i)
361 353  
362   - # Write out trial result
363   - ioutils.write_csv_row(self['outfile'].filename().url(), seed,
  354 + # Write out trial result
  355 + ioutils.write_csv_row(self['outfile'].filename().url(), i,
364 356 result['colnames'], result['values'])
365 357  
366 358 # Return
... ...
cscripts/mputils.py
... ... @@ -20,12 +20,12 @@
20 20 import gammalib
21 21  
22 22  
23   -# ==================== #
24   -# Number of processes #
25   -# ==================== #
  23 +# ====================== #
  24 +# Get number of threads #
  25 +# ====================== #
26 26 def nthreads(cls):
27 27 """
28   - Determines the number of parallel processes to use.
  28 + Determines the number of parallel processes to use
29 29  
30 30 The number is based on the user parameter "nthreads" and the availability
31 31 of the multiprocessing module.
... ... @@ -38,7 +38,7 @@ def nthreads(cls):
38 38 Returns
39 39 -------
40 40 nthreads : int
41   - Number of parallel processes
  41 + Number of threads
42 42 """
43 43 # Log multiprocessing configuration
44 44 cls._log_header1(gammalib.NORMAL, 'Multiprocessing')
... ... @@ -69,3 +69,41 @@ def nthreads(cls):
69 69  
70 70 # Return number of processes to be used
71 71 return nthreads
  72 +
  73 +
  74 +# ============================ #
  75 +# Execute function in parallel #
  76 +# ============================ #
  77 +def process(nthreads, function, args):
  78 + """
  79 + Execute function in parallel
  80 +
  81 + Parameters
  82 + ----------
  83 + nthreads : int
  84 + Number of parallel threads
  85 + function : Python function
  86 + Function to be executed in parallel
  87 + args : list of tuples
  88 + Function arguments for each evaluation
  89 +
  90 + Returns
  91 + -------
  92 + results : list of dicts
  93 + List of function evaluation results
  94 + """
  95 + # Import Pool module from multiprocessing module
  96 + from multiprocessing import Pool
  97 +
  98 + # Create a pool of processes
  99 + pool = Pool(processes=nthreads)
  100 +
  101 + # Run function in parallel
  102 + results = pool.map(function, args)
  103 +
  104 + # Close pool and join
  105 + pool.close()
  106 + pool.join()
  107 +
  108 + # Return results
  109 + return results
... ...
test/test_ctbutterfly.py
... ... @@ -123,6 +123,12 @@ class Test(test):
123 123 # Check result file
124 124 self._check_result_file('ctbutterfly_py1.dat')
125 125  
  126 + # Check CSV result
  127 + self.test_value(butterfly.butterfly().nrows(), 100,
  128 + 'Check for 100 rows in CSV file')
  129 + self.test_value(butterfly.butterfly().ncols(), 4,
  130 + 'Check for 4 columns in CSV file')
  131 +
126 132 # Set-up ctbutterfly
127 133 butterfly = ctools.ctbutterfly()
128 134 butterfly['inobs'] = self._events
... ...