Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1import re
2from numpy import zeros, genfromtxt, array, triu_indices, ndarray
3from argparse import HelpFormatter, ArgumentParser
4from sys import exit, argv
5from os import makedirs, listdir
7from mango.debug import debug
8from mango.io import read_inputfile, read_data, restartfile_read, get_restartdata
9from mango.constants import _variables, c, keys
10from mango.managers import start_server
13# def _parsingav(value):
14# """Set the averaging of a data set to true or false."""
15# return (value == "av")
18def col_check(check):
19 """Check columns are available."""
20 diffx = set(check["x"][:, 0]).difference(c.columns_flat)
21 diffy = set(check["y"][:, 0]).difference(c.columns_flat)
22 diff = set(list(diffx) + list(diffy)).difference(set(['+', '-', '*', '/']))
23 if diff != set([]):
24 c.Error("F Invalid column{} {}, unable to plot graph".format(
25 "s" if len(list(diff)) > 1 else "", diff))
28class SmartFormatter(HelpFormatter):
29 """
30 Smart help formatter.
32 Allows for splitting the help
33 over multiple lines by starting help with 'R|'
34 """
36 def _split_lines(self, text, width):
37 if text.startswith('R|'):
38 return text[2:].splitlines()
39 return HelpFormatter._split_lines(self, text, width)
41 def _format_action_invocation(self, action):
42 if not action.option_strings: 42 ↛ 43line 42 didn't jump to line 43, because the condition on line 42 was never true
43 metavar, = self._metavar_formatter(action, action.dest)(1)
44 return metavar
46 else:
47 parts = []
49 # if the Optional doesn't take a value, format is:
50 # -s, --long
51 if action.nargs == 0:
52 parts.extend(action.option_strings)
54 # if the Optional takes a value, format is:
55 # -s ARGS, --long ARGS
56 else:
57 default = action.dest.upper()
58 args_string = self._format_args(action, default)
60 # parts.append('%s %s' % (option_string, args_string)) ### this is change
61 ls = args_string.split(" ", 1)
62 if len(ls) == 2:
63 args_string = ls[1][:-1] if ls[0].startswith('[') else ls[1]
65 parts.extend(action.option_strings)
66 parts[-1] += ' %s' % args_string
68 return ', '.join(parts)
71def list_add_arguments(self, k):
72 """
73 Parse arguments from dictionary.
75 Parameters
76 ----------
77 k: OrderedDict
78 Dictionary of keywords and defaults
80 """
81 for i, j in k.items():
82 args = j[1][0]
83 kw = j[1][1]
84 args = tuple(args) if isinstance(args, list) else [args]
85 try:
86 if 'type' not in kw and 'action' not in kw:
87 kw['type'] = j[0][1]
88 if 'help' in kw: 88 ↛ 90line 88 didn't jump to line 90, because the condition on line 88 was never false
89 kw['help'] = kw['help'].format(j[0][2].rsplit("/", 1)[-1] if j[0][0] == 'logs' else j[0][2])
90 kw['default'] = None
91 self.add_argument(*args, **kw)
93 except TypeError:
94 self.add_argument(*args)
97def notebook_help(k=keys.words):
98 """Print Help in Jupyter Notebook."""
99 from IPython.display import HTML, display
100 st = '<table><tr><th>VariableName</th><th>Help</th></tr>'
101 it = '<tr><td>{}</td><td>{}</td></tr>'
102 end = '</table>'
103 s = _help(k, st, it, _mlines_nb, end)
104 display(HTML(s))
105 exit(0)
108def _mlines_nb(s, it, i, j, fmt):
109 s += it.format(i, j[1][1]['help'].format(fmt)[2:])
110 return s
113def interactive_help(k=keys.words):
114 """Print commandline help."""
115 st = 'VariableName' + 10 * ' ' + 'Help\n' + '-' * 30 + '\n'
116 it = '{:<22s}{}\n\n'
117 end = ''
118 s = _help(k, st, it, _mlines_ih, end)
119 print(s)
120 exit(0)
123def _mlines_ih(s, it, i, j, fmt):
124 if len(j[1][1]['help'].split('\n')) > 1:
125 string = j[1][1]['help'].split('\n')
126 string[0] = string[0][2:]
127 string[-1] = string[-1].format(fmt)
128 newl = it.format(i, string[0])[:-1]
129 for line in string[1:]:
130 newl += it.format('', line)[:-1]
131 s += newl + '\n'
132 else:
133 s += it.format(i, j[1][1]['help'].format(fmt)[2:])
134 return s
137def _help(k, st, it, func, end):
138 """
139 Format help.
141 Parameters
142 ----------
143 k: dict
144 dictionary of keywords
145 st: str
146 Header of help
147 it: str
148 table cells
149 func: function
150 entry formatter
151 end: str
152 final line of table
154 Returns
155 -------
156 s: str
157 formatted help string
159 """
160 s = st
161 for i, j in k.items():
162 if j[0][0] == 'logs':
163 fmt = j[0][2].rsplit("/", 1)[-1]
164 else:
165 fmt = j[0][2]
166 s = func(s, it, i, j, fmt)
167 # if len(j[1][1]['help'].split('\n')) > 1:
168 # string = j[1][1]['help'].split('\n')
169 # string[0] = string[0][2:]
170 # string[-1] = string[-1].format(fmt)
171 # newl = it.format(i, string[0])[:-1]
172 # for line in string[1:]:
173 # newl += it.format('', line)[:-1]
174 # s += newl + '\n'
175 # else:
176 # s += it.format(i, j[1][1]['help'].format(fmt)[2:])
177 s += end
178 return s
181def gethelp(opts, parser):
182 """Help output selector."""
183 if opts == "-h" or '-h' in argv:
184 c._banner()
185 if c.using_IP: 185 ↛ 186line 185 didn't jump to line 186, because the condition on line 185 was never true
186 notebook_help(keys.words)
187 elif c.interactive: 187 ↛ 188line 187 didn't jump to line 188, because the condition on line 187 was never true
188 interactive_help(keys.words)
189 else:
190 parser.parse_args(['-h'])
191 elif opts == '-ifh' or '-ifh' in argv:
192 c._banner()
193 interactive_help(keys.words)
196def parse_in(k=keys.words):
197 """
198 Argparse function.
200 Parameters
201 ----------
202 k: Ordereddict
203 dictionary of available commands
205 Returns
206 -------
207 parser: instance
208 argparse parser instance
210 """
211 ArgumentParser.list_add_arguments = list_add_arguments
212 parser = ArgumentParser(prog='mango',
213 description="Mango: magnetic nanoparticle simulator CLI Help",
214 formatter_class=SmartFormatter)
216 parser.list_add_arguments(k)
217 return parser
220def array_vars(parsing, variables):
221 """Set up arrays for variables."""
222 for key in variables:
223 z_arr = zeros(parsing.no_molecules)
224 if isinstance(parsing.__dict__[key], str): 224 ↛ 225line 224 didn't jump to line 225, because the condition on line 224 was never true
225 parsing.__dict__[key] = genfromtxt(parsing.__dict__[key], dtype=float)
226 elif isinstance(parsing.__dict__[key], float):
227 parsing.__dict__[key] += z_arr
228 parsing.defaults += ["{}_1".format(key)]
229 elif len(parsing.__dict__[key]) == parsing.no_molecules:
230 parsing.__dict__[key] = array(parsing.__dict__[key], dtype=float)
231 elif len(parsing.__dict__[key]) == 1:
232 parsing.__dict__[key] = float(parsing.__dict__[key][0]) + z_arr
233 parsing.defaults += ["{}_1".format(key)]
234 else:
235 c.Error("F {} array must be the same length as No. MNPs".format(key))
237 return parsing
240def setdefaults(parsing, default_val):
241 """
242 Set default values for variables that weren't in input.
244 Parameters
245 ----------
246 parsing: instance
247 ArgumentParser instance
248 default_val: dict
249 Default Values
251 Returns
252 -------
253 parsing: instance
254 ArgumentParser instance
256 """
257 parsing.defaults = []
258 for key, value in default_val.items():
259 if key not in parsing.__dict__ or parsing.__dict__[key] is None:
260 parsing.__dict__[key] = default_val[key]
261 parsing.defaults += [key]
262 return parsing
265@debug(['args'])
266def parse(argparse, opts={}):
267 """
268 Parse user input.
270 Parameters
271 ----------
272 argparse: bool
273 Is argparse used?
274 opts: dict
275 dict of arguments if argparse isn't used
277 Returns
278 -------
279 parsing: instance
280 instance of variables
282 """
283 parser = parse_in(keys.words)
285 gethelp(opts, parser)
287 if argparse:
288 parsing = parser.parse_args()
289 parsing = read_inputfile(parsing.inputfile, parsing.restart) if parsing.inputfile else parsing
290 else:
291 parsing = read_inputfile(opts)
293 if parsing.restart: 293 ↛ 294line 293 didn't jump to line 294, because the condition on line 293 was never true
294 return restart_in(parsing)
296 if ('no_molecules' in parsing.__dict__ and 'boxsize' not in parsing.__dict__): 296 ↛ 297line 296 didn't jump to line 297, because the condition on line 296 was never true
297 rad = 1.1 * parsing.radius if 'radius' in parsing.__dict__ else keys.defaults["radius"]
298 parsing.boxsize = parsing.no_molecules * rad
300 parsing = setdefaults(parsing, keys.defaults)
301 # parsing.average = {av: False for av in c.averages}
302 parsing.files = {file: False for file in c.files}
304 if parsing.cfile: 304 ↛ 305line 304 didn't jump to line 305, because the condition on line 304 was never true
305 if ['lf'] == parsing.cfile:
306 c.Error("F No file type specified")
307 parsing.lastframe = ('lf' in parsing.cfile)
308 getfiles = set(parsing.cfile) & set(c.files)
309 for i in getfiles:
310 parsing.files[i] = True
311 # parsing.average[c.averages[c.files.index(i)]] = _parsingav(parsing.cfile[parsing.cfile.index(i) - 1])
312 else:
313 parsing.lastframe = None
315 if parsing.column:
316 parsing.column, parsing.files = column_manip(parsing.column, parsing.files)
318 return splitvars(sanity(fixparam(array_vars(parsing, ['radius', 'dens']))))
321def fixparam(p):
322 """Modify parameters for units and types."""
323 set_const(p.no_molecules)
324 p.Mdens *= 1e-6 # [1e6 emu/g]
325 p.sigma, p.limit = calcsigmalim(p.radius, p.no_molecules)
326 p.epsilon = c.Ar_KB * (p.sigma / c.Ar_sigma)**3 if p.epsilon is None else p.epsilon
327 p.ms = p.Mdens * p.dens # [1e6 emu/g] * [g/mol]
328 p.nu *= 1e-12 # time [1e-12 s] -> [1e12 Hz]
329 p.extra_iter = None
330 p.RandNoState = {}
331 p.nmax = int(p.nmax)
332 p.savechunk = int(p.savechunk)
333 p.run = int(p.run)
334 p.time = 0.0
335 p.field = (not p.suscep and p.H_0 != 0.0)
336 p.stats = list(range(p.stats))
337 return p
340def calcsigmalim(radius, np):
341 """Calculate sigma and cutoff limit."""
342 dists = radius[c.tri[0]] + radius[c.tri[1]]
343 return dists / (2**(1 / 6)), 1 / dists
346def splitvars(p):
347 """
348 Split parameters into variables and flags.
350 Parameters
351 ----------
352 p: instance
353 argparse instance
355 Returns
356 -------
357 var, flg: instance
358 variable and flag instances
360 """
361 # Store variables and flags
362 var_list, flags_list = keys.flgorvar()
364 def store(lis, dic):
365 nd = {}
366 for l in lis:
367 nd[l] = dic[l]
368 return nd
370 # Class of variables and flags
371 var = _variables(**store(var_list, p.__dict__))
372 flg = _variables(**store(flags_list, p.__dict__))
374 # Some Variables need changing based on input
375 flg.pp = not flg.run == 0
377 if flg.suscep == []: 377 ↛ 378line 377 didn't jump to line 378, because the condition on line 377 was never true
378 flg.suscep = True
380 if var.temp == 0: 380 ↛ 381line 380 didn't jump to line 381, because the condition on line 380 was never true
381 flg.neel = flg.suscep = False
383 return var, flg
386def sanity(p):
387 """Sanity checks."""
388 if p.dens.any() <= 0.0: 388 ↛ 389line 388 didn't jump to line 389, because the condition on line 388 was never true
389 c.Error("F The density of all particles must be greater than zero")
391 if p.temp < 0: 391 ↛ 392line 391 didn't jump to line 392, because the condition on line 391 was never true
392 c.Error("F Temperature must be positive")
394 if p.eta < 0: 394 ↛ 395line 394 didn't jump to line 395, because the condition on line 394 was never true
395 c.Error("F Viscosity must be positive")
397 if isinstance(p.boxsize, ndarray) and (p.boxsize.size != 1 or p.boxsize.size != 3): 397 ↛ 398line 397 didn't jump to line 398, because the condition on line 397 was never true
398 c.Error("F Boxsize must be 1 dimensional or 3 dimensional")
400 return p
403def filenaming(var, flg, run=1):
404 """
405 File naming.
407 Avoid overwriting files etc.
409 Parameters
410 ----------
411 var, flg: instance
412 class variable and flag instances
414 """
415 if flg.pp or flg.restart: 415 ↛ 416line 415 didn't jump to line 416, because the condition on line 415 was never true
416 var.directory = var.logs if var.logs.endswith("/") else var.logs + "/"
417 else:
418 var.directory = "{}{}n{:g}_{:g}K_{}{:g}nm/".format(
419 var.logs, "" if var.logs.endswith("/") else "/",
420 var.nmax, var.temp,
421 '' if flg.suscep else '{:g}gauss_{:g}kHz_'.format(var.H_0 * 1e-6, var.nu * 1e-15),
422 var.radius[0] * 10)
423 # Make Log directory as needed
424 makedirs(var.directory, exist_ok=True)
426 # avoid overwriting other log files
427 try:
428 files = listdir(var.directory)
429 except FileNotFoundError:
430 c.Error("F Log directory does not exist")
432 # Get run number for filename
433 if not flg.pp and not flg.restart: 433 ↛ 438line 433 didn't jump to line 438, because the condition on line 433 was never false
434 while any(file.startswith('{}Run{:g}'.format("S_" if flg.suscep else "", 434 ↛ 436line 434 didn't jump to line 436, because the condition on line 434 was never true
435 run)) for file in files):
436 run += 1
438 flg.run = flg.run if flg.pp or flg.restart else run
440 check_name = '{}Run{:g}_mol-'.format("S_" if flg.suscep else "", flg.run)
442 if flg.pp and not flg.ufile: 442 ↛ 443line 442 didn't jump to line 443, because the condition on line 442 was never true
443 from mango.pp.util import interactive_getfiles
444 flg, check_name = interactive_getfiles(flg, check_name, files)
446 # Add final name and save location to variables
447 var.name = "{}{}{:g}.".format(var.directory, check_name, var.no_molecules)
450def verbosity(var, flg):
451 """
452 Verbosity Changing.
454 Parameters
455 ----------
456 var, flg: instance
457 class variable and flag instances
459 """
460 c.header(flg.nout >= 2, flg.nout >= 4)
462 verb = (flg.nout >= 2)
464 flg.prog = (flg.nout >= 3 and c.tinfo['otty'])
466 c.Error("EV {} {} {} {}".format(verb, var.directory, flg.run, 1 if flg.pp else 0))
468 # progress bar switches
469 if flg.prog:
470 c.progress = start_server("Prog")
471 if not flg.pp: # (flg.column is not False or flg.suscep or flg.files['xyz']):
472 c.progress(f"setup {var.stats}")
475def set_const(no_molecules):
476 c.tri = triu_indices(n=no_molecules, k=1)
477 c.set_accuracy(c.EPS2)
480def restart_in(parsing):
481 """
482 Get data from run to be restarted.
484 Parameters
485 ----------
486 parsing: instance
487 argparse parsing instance
489 Returns
490 -------
491 variables: instance
492 class instance of all variables to be used
494 """
495 filenames, save_type, directory, total = restartfile_read(parsing.restart)
497 restart, restartflags, written = get_restartdata(read_data(filenames[0], save_type), filenames)
499 restart.extra_iter = {f'stat{k}': total - writ - 1 for k, writ in written.items()}
501 restart.time = 0.0
502 restart.stats = list(written.keys())
503 if parsing.walltime is not None:
504 restart.walltime = parsing.walltime
506 for i in ['logs', 'directory']:
507 restart.__dict__[i] = directory
509 restartflags.opt = False
510 restartflags.restart = parsing.restart
512 set_const(restart.no_molecules)
514 return restart, restartflags
517def column_manip(column_var, files):
518 """
519 Create a list of plots to be made.
521 TODO cleanup
523 Parameters
524 ----------
525 column_var: list
526 list parsed from user input
527 eg. ["time kinetic[5,:,3] + avtrans_pot / - avmag_pot[:100]", "time kinetic"]
528 files: dict
529 files to create
531 Returns
532 -------
533 plot_store: dict
534 plots to create
535 files: dict
536 files to create
538 """
539 def search(key):
540 """
541 Search for averaging of data.
543 Parameters
544 ----------
545 key: str
546 key to find
548 Returns
549 -------
550 key: str
551 found key
552 inlist: bool
553 is key data to be averaged?
555 """
556 inlist = False
557 if key == "time":
558 return [key, True]
560 for enum, i in enumerate(c.columns.keys()):
561 if key in c.columns[i] or key[2:] in c.columns[i]:
562 files[i] = True
563 inlist = True
564 # if not average_var[c.averages[enum]] or key[:2] == 'av':
565 # average_var[c.averages[enum]] = _parsingav(key[:2])
566 # key = key[2:] if average_var[c.averages[enum]] else key
568 return [key, inlist]
570 def rearray(j):
571 """
572 Rearrayify array splitting.
574 Parameters
575 ----------
576 j: str
577 array indexing like string eg 5,:
579 Returns
580 -------
581 j: str
582 proper array index eg [5,:]
584 """
585 for ind in range(len(j) // 2):
586 j[(ind * 2) + 1] = "[" + j[(ind * 2) + 1] + "]"
587 return j
589 def shapedlist(j, cols_list_arr):
590 """Shape array so its square also marker for 'missing' array."""
591 arr_j = array(j + [''] if len(j) % 2 == 1 else j).reshape(-1, 2)
592 # split elements at operations
593 for no, k in enumerate(arr_j[:, 0]):
594 cols_list_arr += [search(cl) for cl in filter(None, re.split('([-+/*])', k))]
595 if arr_j[no, 1] != '':
596 cols_list_arr[-1][1] = arr_j[no, 1]
598 cols_list_arr = []
599 cols_list = []
600 plot_no = 1
601 plot_store = {}
603 for i, j in enumerate(column_var):
604 # Plotting multiple graphs with multiple lines, split to new graph
605 # if j == ",":
606 # plot_store["plot_{}".format(plot_no)] = {"x": array(cols_list), "y": array(cols_list_arr)}
607 # col_check(plot_store["plot_{}".format(plot_no)])
608 # cols_list_arr = []
609 # cols_list = []
610 # plot_no += 1
611 # continue
612 j = re.split(r'[\[\]]', j.replace(" ", ""))
613 if len(j) > 1:
614 shapedlist(rearray(j), cols_list) if i == 0 else shapedlist(rearray(j), cols_list_arr)
615 else:
616 for k in j:
617 colum = re.split('([-+/*])', k)
618 for cl in colum:
619 if i == 0:
620 cols_list += [search(cl)]
621 else:
622 cols_list_arr += [search(cl)]
624 # if j != ",":
625 plot_store["plot_{}".format(plot_no)] = {"x": array(cols_list), "y": array(cols_list_arr)}
626 col_check(plot_store["plot_{}".format(plot_no)])
628 return plot_store, files
631if __name__ == "__main__":
632 pass