33from datetime import datetime
44from decimal import Decimal
55from os .path import join
6+ from multiprocessing import Pool
7+ import copy
68
79try :
810 from scipy .optimize import minimize
@@ -218,9 +220,39 @@ def from_range(N, intervals):
218220class InvalidInterval (McRunException ):
219221 pass
220222
223+ def _simulate_point (args ):
224+ i , point , intervals , mcstas_config , mcstas_dir = args
225+
226+ from shutil import copyfile
227+ from os .path import join
228+
229+ # Make a new instance of McStas and configure it
230+ mcstas = copy .deepcopy (mcstas_config ) # You need to define a way to deepcopy or clone your mcstas object
231+ par_values = []
232+
233+ # Ensure we get a mccode.sim pr. thread subdir (e.g. for monitoring seed value
234+ mcstas .simfile = join (mcstas_dir , 'mccode.sim' )
235+
236+ # Shift thread seed to avoid duplicate simulations / biasing
237+ mcstas .options .seed = (i * 1024 )+ mcstas .options .seed
238+
239+ for key in intervals :
240+ mcstas .set_parameter (key , point [key ])
241+ par_values .append (point [key ])
242+
243+ current_dir = f'{ mcstas_dir } /{ i } '
244+ mcstas .run (pipe = False , extra_opts = {'dir' : current_dir })
245+ detectors = mcsimdetectors (current_dir )
246+
247+ result = {
248+ 'index' : i ,
249+ 'params' : par_values ,
250+ 'detectors' : detectors
251+ }
252+ return result
221253
222254class Scanner :
223- """ Perform a series of simulation steps along a given set of points """
255+ """ Perform a series of simulation steps along a given set of points"""
224256 def __init__ (self , mcstas , intervals ):
225257 self .mcstas = mcstas
226258 self .intervals = intervals
@@ -244,6 +276,7 @@ def run(self):
244276 mcstas_dir = self .mcstas .options .dir
245277 if mcstas_dir == '' :
246278 mcstas_dir = '.'
279+
247280
248281 with open (self .outfile , 'w' ) as outfile :
249282 for i , point in enumerate (self .points ):
@@ -278,6 +311,72 @@ def run(self):
278311 outfile .flush ()
279312
280313
314+ class Scanner_split :
315+ """ Perform a series of simulation steps along a given set of points,
316+ Where each simulation is controlled by its own thread. """
317+ def __init__ (self , mcstas , intervals , nb_cpu ):
318+ self .mcstas = mcstas
319+ self .intervals = intervals
320+ self .points = None
321+ self .nb_cpu = nb_cpu
322+ self .outfile = mcstas .options .optimise_file
323+ self .simfile = join (mcstas .options .dir , 'mccode.sim' )
324+
325+ def set_points (self , points ):
326+ self .points = points
327+
328+ def set_outfile (self , path ):
329+ self .outfile = path
330+
331+ def run (self ):
332+ LOG .info ('Running Scanner split, result file is "%s"' % self .outfile )
333+
334+ if len (self .intervals ) == 0 :
335+ raise InvalidInterval ('No interval range specified' )
336+
337+ mcstas_dir = self .mcstas .options .dir or '.'
338+
339+ if self .mcstas .options .seed is None :
340+ dt = datetime .now ()
341+ LOG .info ('No incoming seed from cmdline, setting to current Unix epoch (%d)!' % dt .timestamp ())
342+ self .mcstas .options .seed = dt .timestamp ()
343+
344+ # Prepare data to pass into processes
345+ args_list = [
346+ (i , point , self .intervals , self .mcstas , mcstas_dir )
347+ for i , point in enumerate (self .points )
348+ ]
349+
350+ with Pool (processes = self .nb_cpu ) as pool :
351+ results = pool .map (_simulate_point , args_list )
352+
353+ # Sort results to preserve order
354+ results .sort (key = lambda r : r ['index' ])
355+
356+ with open (self .outfile , 'w' ) as outfile :
357+ wrote_headers = False
358+ for result in results :
359+ if result ['detectors' ] is None :
360+ continue
361+
362+ if not wrote_headers :
363+ names = [d .name for d in result ['detectors' ]]
364+ outfile .write (build_header (self .mcstas .options , self .intervals .keys (), self .intervals , names ))
365+ with open (self .simfile , 'w' ) as simfile :
366+ simfile .write (build_mccodesim_header (
367+ self .mcstas .options ,
368+ self .intervals ,
369+ names ,
370+ version = self .mcstas .version
371+ ))
372+ wrote_headers = True
373+
374+ values = ['%s %s' % (d .intensity , d .error ) for d in result ['detectors' ]]
375+ line = '%s %s\n ' % (' ' .join (map (str , result ['params' ])), ' ' .join (values ))
376+ outfile .write (line )
377+ outfile .flush ()
378+
379+
281380class Optimizer :
282381 """ Optimize monitors by varying the parameters within interval """
283382
0 commit comments