- Timestamp:
- 01/26/12 21:06:59 (4 months ago)
- Location:
- branches/mcxtrace-1.0/src/mcrun2
- Files:
-
- 1 added
- 2 modified
-
main.py (modified) (8 diffs)
-
mcstas.py (modified) (7 diffs)
-
optimisation.py (added)
Legend:
- Unmodified
- Added
- Removed
-
branches/mcxtrace-1.0/src/mcrun2/main.py
r3273 r3274 5 5 from os.path import isfile, isdir, abspath, dirname 6 6 from optparse import OptionParser, OptionGroup, OptionValueError 7 from decimal import Decimal, InvalidOperation 7 8 8 9 from mcstas import McStas 10 from optimisation import Scanner, LinearInterval 9 11 10 12 LOG = logging.getLogger('mcstas') … … 43 45 help='set number of scan points') 44 46 47 add('-L', '--list', 48 action='store_true', 49 help='use a fixed list of points for linear scanning') 50 45 51 # Multiprocessing 46 52 add('--mpi', … … 71 77 72 78 add('--optimise-file', 73 metavar='FILE', 79 metavar='FILE', default='mcstas.dat', 74 80 help='store optimisation results in FILE ' 75 '(defaults to: "mc optim_####.dat")')81 '(defaults to: "mcstas.dat")') 76 82 77 83 # Misc options … … 96 102 opt = OptionGroup(parser, 'Instrument options') 97 103 add = opt.add_option 98 99 104 100 105 # Misc options … … 120 125 # Data options 121 126 dir_exists = lambda path: isdir(abspath(path)) 127 122 128 def check_file(exist=True): 123 129 ''' Validate the path to a file ''' … … 126 132 else: 127 133 def is_valid(path): 128 ''' Ensure thatpath to file exists and filename is provided '''134 ''' Ensure path to file exists and filename is provided ''' 129 135 if not dir_exists(dirname(path)): 130 136 return False … … 174 180 175 181 182 def is_decimal(string): 183 ''' Check if string is parsable as decimal/float ''' 184 try: 185 Decimal(string) 186 return True 187 except InvalidOperation: 188 return False 189 190 191 def get_parameters(options): 192 ''' Get fixed and scan/optimise parameters ''' 193 fixed_params = {} 194 intervals = {} 195 for param in options.params: 196 if '=' in param: 197 key, value = param.split('=', 1) 198 interval = value.split(',') 199 # When just one point is present, fix as constant 200 if len(interval) == 1: 201 fixed_params[key] = Decimal(value) 202 else: 203 LOG.debug('interval: %s', interval) 204 intervals[key] = interval 205 else: 206 LOG.warning('Ignoring invalid parameter: "%s"', param) 207 return (fixed_params, intervals) 208 209 176 210 def main(): 177 211 ''' Main routine ''' 178 212 179 213 # Setup logging 180 formatter = logging.Formatter('%(created) s, %(levelname)8s: %(message)s')214 formatter = logging.Formatter('%(created).2f, %(levelname)8s: %(message)s') 181 215 182 216 handler = logging.StreamHandler() … … 211 245 # Run McStas 212 246 mcstas = McStas(options.instr) 213 214 # Set parameters215 for param in options.params:216 if '=' in param:217 key, value = param.split('=', 1)218 mcstas.setParameter(key, value)219 else:220 LOG.warning('Ignoring invalid parameter: "%s"', param)221 222 247 mcstas.prepare(options) 223 mcstas.run() 248 instrs = mcstas.get_info().get_instrument() 249 LOG.debug('info: %s', instrs) 250 251 (fixed_params, intervals) = get_parameters(options) 252 253 # Set fixed parameters 254 for key, value in fixed_params.items(): 255 mcstas.set_parameter(key, value) 256 257 # Check for linear scanning 258 linear_interval = None 259 260 # Can't both do list and interval scanning 261 if options.list and options.numpoints: 262 raise OptionValueError('--numpoints cannot be used with --list') 263 264 elif options.list: 265 if len(intervals) == 0: 266 raise OptionValueError( 267 '--list was chosen but no lists was presented.') 268 points = len(intervals.values()[0]) 269 if not(all(map(lambda i: len(i) == points, intervals.values()))): 270 raise OptionValueError( 271 'All variables much have an equal amount of points.') 272 linear_interval = LinearInterval.from_list( 273 points, intervals) 274 275 elif options.numpoints is not None: 276 if options.numpoints < 2: 277 raise OptionValueError( 278 ('Cannot scan variable(s) %s using only one data point. ' 279 'Please use -N to specify the number of points.') % \ 280 ', '.join(intervals.keys())) 281 # Check that input is valid decimals 282 if not all(map(lambda i: 283 len(i) == 2 and 284 all(map(is_decimal, i)), intervals.values())): 285 raise OptionValueError('Could not parse intervals.') 286 287 linear_interval = LinearInterval.from_range( 288 options.numpoints, intervals) 289 290 # Parameters for linear scanning present 291 if linear_interval: 292 scanner = Scanner(mcstas, intervals) 293 scanner.set_points(linear_interval) 294 scanner.run() 295 else: 296 mcstas.run() 224 297 225 298 -
branches/mcxtrace-1.0/src/mcrun2/mcstas.py
r3273 r3274 1 1 2 import atexit 3 import logging 4 import os 5 import re 2 6 import tempfile 3 import atexit 4 import os 5 import logging 7 import yaml 6 8 7 9 from os.path import isfile, dirname, basename, splitext 8 10 from subprocess import Popen, PIPE 11 from decimal import Decimal 9 12 10 13 … … 31 34 ' '.join(self.args)) 32 35 36 33 37 class Process: 34 38 ''' An external process ''' … … 48 52 49 53 # Run executable 50 LOG.debug('CMD: %s %s', self.executable, args)54 LOG.debug('CMD: %s %s', self.executable, ' '.join(args)) 51 55 fid = Popen([self.executable] + args, 52 56 stdout=pipe, … … 81 85 atexit.register(self.cleanup) 82 86 83 84 def setParameter(self, key, value): 87 def set_parameter(self, key, value): 85 88 ''' Set the value of an experiment parameter ''' 86 89 self.params[key] = value 87 88 90 89 91 def prepare(self, options): … … 114 116 # Compiler optimisation 115 117 args = ['-o', self.binpath] + cflags + [self.cpath] 116 117 118 Process(options.cc).run(args) 118 119 119 120 120 def run(self, pipe=False): … … 140 140 141 141 # Add parameters last 142 args += [ '%s=%s' % (key, value) for key, value in self.params.items() ] 142 args += ['%s=%s' % (key, value) 143 for key, value in self.params.items()] 143 144 144 145 # Run McStas 145 146 if not mpi: 146 147 LOG.info('Running: %s', self.binpath) 147 Process(self.binpath).run(args, pipe=pipe)148 return Process(self.binpath).run(args, pipe=pipe) 148 149 else: 149 150 LOG.info('Running via MPI: %s', self.binpath) 150 151 mpi_args = ['-np', str(options.mpi), self.binpath] 151 152 mpi_args += args 152 Process(options.mpirun).run(mpi_args, pipe=pipe) 153 153 return Process(options.mpirun).run(mpi_args, pipe=pipe) 154 155 def get_info(self): 156 return McStasInfo(Process(self.binpath).run(['--info'], pipe=True)) 154 157 155 158 def cleanup(self): … … 163 166 os.rmdir(self.dir) 164 167 168 169 class Detector(object): 170 ''' A detector ''' 171 def __init__(self, name, intensity, error, count, path): 172 self.name = name 173 self.intensity = Decimal(intensity) 174 self.error = Decimal(error) 175 self.count = Decimal(count) 176 self.path = path 177 178 179 class McStasInfo: 180 ''' Parsing McStas experiment information (--info) ''' 181 182 PARAMETERS_RE = re.compile(r'^\s*Parameters:(.*)', flags=re.MULTILINE) 183 SEPERATOR_RE = re.compile(r'^([^:]+):\s*') 184 QUOTE_RE = re.compile(r'^(\s*[^:]+):\s*([^\[\s].*)$', flags=re.MULTILINE) 185 GROUP_RE = re.compile(r'begin ([^\s]+)(.+)end \1', flags=re.DOTALL) 186 PARAM_RE = re.compile(r'^\s*Param:\s+"', flags=re.MULTILINE) 187 188 def __init__(self, data): 189 self.data = data 190 self.info = self._parse_info() 191 192 def _parse_info(self): 193 """ 194 Parse the raw McStas info output 195 The output resembles YAML but not quite. 196 It's converted to YAML by: 197 0. Ensuring a space after 'key:' -> 'key: ' 198 1. Adding qoutes 'key: value' -> 'key: "value"' 199 2. Changing 'begin foobar\n ...\n end foobar' -> 'foobar:\n' 200 3. Add unique suffix number to each param: 201 Param: lambda=0.7 202 Param: DM=1.8 203 --> 204 Param0: lambda=0.7 205 Param1: DM=1.8 206 4. Split up 'Parameters' to form a list 207 """ 208 209 def escape(line): 210 ''' Escape \ and " ''' 211 return line.replace('\\', '\\\\').replace('"', r'\"') 212 213 def quote(match): 214 ''' Quote a value ''' 215 return '%s: "%s"' % (match.group(1), escape(match.group(2))) 216 217 def param_number(match): 218 ''' Assign unique number to each param ''' 219 param_number.prev_param_number += 1 220 return match.group(0).replace('Param', 221 'Param%i' % param_number.prev_param_number) 222 # start count at 0 (previous is -1) 223 setattr(param_number, 'prev_param_number', -1) 224 225 def parameters_to_list(match): 226 old_str = match.group(1) 227 if old_str.strip(): 228 new_str = ' [%s]' % ','.join(match.group(1).split()) 229 return match.group(0).replace(old_str, new_str) 230 return match.group(0).strip() + ' []' 231 232 yaml_str = self.data 233 yaml_str = self.PARAMETERS_RE.sub(parameters_to_list, yaml_str) 234 yaml_str = self.SEPERATOR_RE.sub(r'\1: ', yaml_str) 235 yaml_str = self.QUOTE_RE.sub(quote, yaml_str) 236 yaml_str = self.GROUP_RE.sub(r'\1:\2', yaml_str) 237 yaml_str = self.PARAM_RE.sub(param_number, yaml_str) 238 239 return yaml.load(yaml_str) 240 241 def get(self, key): 242 return self.info[key] 243 244 def get_simulation(self): 245 return self.get('simulation') 246 247 def get_instrument(self): 248 return self.get('instrument') 249 250 251 class McStasResult: 252 ''' Parsing of McStas experiment output ''' 253 254 DETECTOR_RE = r'Detector: ([^\s]+)_I=([^ ]+) \1_ERR=([^\s]+) \1_N=([^ ]+) "(\1[^"]+)"' 255 256 def __init__(self, data): 257 self.data = data 258 self.detectors = None 259 260 def get_detectors(self): 261 ''' Extract detector information ''' 262 if self.detectors is not None: 263 return self.detectors 264 res = re.findall(self.DETECTOR_RE, self.data) 265 266 return [Detector(name, intensity, error, count, path) 267 for name, intensity, error, count, path in res]
