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
1from scipy.constants import physical_constants
2from numpy import finfo, float32, float64, zeros, bincount, ndarray, unique
3from collections import defaultdict, OrderedDict
4from contextlib import contextmanager, suppress
5from sys import stdout, stderr, executable, argv, exit
6from os import getcwd, name as osname, devnull, stat, remove
7from subprocess import run, PIPE
8from functools import wraps
10from _version import version
11from mango.debug import debug, verb
14class const():
15 """
16 | Constants and version number.
18 | Internal Units
20 |
21 | Main
23 |
25 * time: 1e-12 [s]
26 * length: 1e-6 [cm]
27 * mass: 1e-18 [g]
29 | Derived
31 |
33 * energy unit: 1e-6 [erg]
34 * temperature unit: 1e0 [K]
35 * magnetic induction: 1e6 [gauss]
36 * magnetisation: 1e-12 [emu]
37 * force: 1 [g/cm.s^2]
39 |
41 """
42 @debug(['const'])
43 def __init__(self):
44 """Get constants and check environment."""
45 self.version = version
47 self.KB = physical_constants["Boltzmann constant"][0] * 1.0e13 # energy units /K
48 self.GBARE = -physical_constants["electron gyromag. ratio"][0] * 1.0e-10 # unit: 1/(gauss *ps)
49 self.EPS = finfo(float32).eps
50 self.EPS2 = finfo(float64).eps
51 self.Ar_KB = 119.8 * self.KB
52 self.Ar_sigma = 0.03405
53 self.opsys = osname
54 self.eijk = LCT()
55 self.set_accuracy()
56 self.verb = verb
58 self.t, self.tinfo = GT()
59 self.profile = PROF()
60 self.havedisplay = MPL()
61 self.using_IP = UIP()
62 self.interactive = INTTERM()
63 self.ustr, self.unicode = UCODE(self.t)
64 self.processors = CPU()
65 self.comm, self.MPI = EMPI()
67 self.reseed()
69 self.posfuncs = ['vv_trans', 'vv_mag', 'ff_trans', 'ff_mag', 'hh_mag', 'force_vars', 'energy_vars']
71 self.units = OrderedDict({"xyz": {"position": '(nm)',
72 "magnetisation": '(p.emu)',
73 "forces": r'($\mu$N)',
74 "momentum": r'($\mu$g.nm/s)'},
75 "energy": {"Etotal": r'($\mu$ erg)',
76 "mag_pot": r'($\mu$ erg)',
77 "trans_pot": r'($\mu$ erg)',
78 "kinetic": r'($\mu$ erg)'},
79 "momenta": {"Mtotal": r'($\mu$g.nm/s)',
80 "total_mag": r'($\mu$g.nm/s)',
81 "total_angular": r'($\mu$g.nm/s)',
82 "CoM": '(nm)',
83 "CoM_vel": '(nm/ps)'},
84 "time": "(ps)"})
86 c2 = [v2 for v in self.units.values() if type(v) == dict for v2 in v.keys()]
87 c2 += ['time']
89 self.columns_flat = set(c2)
90 self.columns = {"xyz": c2[:4],
91 "energy": c2[4:8],
92 "momenta": c2[8:13]}
94 self.files = list(self.columns.keys())
96 def reseed(self, seed=12345):
97 self._Gen, rng = MTrnd(seed)
98 self.random = self._Gen(rng)
99 self.rgen = self.random.bit_generator
101 def jump(self, jumps):
102 self.rgen = self.rgen.jumped(jumps)
103 self.random = self._Gen(self.rgen)
105 def set_accuracy(self, EPS_val=None):
106 self.EPS_val = EPS_val if EPS_val else self.EPS2
108 def Error(self, msg):
109 """
110 Simple error printer.
112 Use managers.serverwrapper('Error') decorator for fully fledged error management
113 """
114 print(msg)
115 if msg.startswith('F'):
116 exit(1)
118 def Prog(self, msg):
119 """Blank function, replaced on the fly with progressbar if required."""
120 pass
122 def echo(self, msg, x, y):
123 """Print at specified cursor location."""
124 with self.t.location(x, y):
125 print(msg, end='', flush=True)
127 def clear(self):
128 """Clear screen and move cursor."""
129 print(self.t.clear + self.t.move_y(0), end='', flush=True)
131 def _banner(self):
132 print(r"___ ___ ___ _ _ _____ _____ ")
133 print(r"| \/ | / _ \ | \ | || __ \| _ |")
134 print(r"| . . |/ /_\ \| \| || | \/| | | |")
135 print(r"| |\/| || _ || . ` || | __ | | | |")
136 print(r"| | | || | | || |\ || |_\ \\ \_/ /")
137 print(r"\_| |_/\_| |_/\_| \_/ \____/ \___/ ")
138 print(f"Version: {self.version}", end="\n\n")
140 def header(self, constants, debug_pr):
141 """Output file header."""
142 if self.comm.Get_rank() == 0: 142 ↛ 144line 142 didn't jump to line 144, because the condition on line 142 was never false
143 self._banner()
144 if constants or hasattr(self, "DBG"):
145 print("Constants\n---------")
146 print("KB {: 15.7e} {}erg/K".format(self.KB, self.ustr.mu))
147 print("GBARE {: 15.7e} 1/(gauss*ps)".format(self.GBARE))
148 print("EPS2 {: 15.7e} (float64 precision)".format(self.EPS2))
149 print("EPS {: 15.7e} (float32 precision)".format(self.EPS))
150 if debug_pr or hasattr(self, "DBG"):
151 print("\nRunning on {}".format(self.opsys))
152 print("stdout {}".format("to screen" if self.tinfo['otty'] is True else "redirected"))
153 print("stderr {}".format("to screen" if self.tinfo['etty'] is True else "redirected"))
154 print("Display {}available graphs can{} be viewed onscreen".format(
155 "" if self.havedisplay else "not ", "" if self.havedisplay else "'t"))
156 print("")
159def MTrnd(seed=12345):
160 """Random number generator."""
161 from numpy.random import Generator, MT19937
162 rgen = MT19937(seed)
163 return Generator, rgen
166def GT():
167 """
168 Get terminal information.
170 Returns
171 -------
172 Terminal: instance
173 Blessings terminal class instance
174 Terminal info: dict
175 Terminal info
177 """
178 class Term():
179 width = None
180 height = None
181 does_styling = False
182 clear = ''
183 subscript = ''
184 no_subscript = ''
185 is_a_tty = False
187 @contextmanager
188 def location(*args):
189 yield
191 def move_y(*args):
192 return ''
193 try:
194 from blessings import Terminal, curses
195 t = Terminal()
196 except (ImportError, curses.error if 'Terminal' in locals() else ModuleNotFoundError) as e:
197 if not stdout.isatty(): 197 ↛ 199line 197 didn't jump to line 199, because the condition on line 197 was never false
198 verb.print(f"Blessings: {e}", file=stderr)
199 t = Term()
201 width = t.width if t.is_a_tty else 0
202 tinfo = {'c0': [0, width // 2], 'c1': [width // 2, width],
203 'otty': stdout.isatty(), 'etty': stderr.isatty()}
204 return t, tinfo
207def LCT():
208 """
209 Construct Levi-Civita Tensor.
211 Returns
212 -------
213 eijk: array
214 Levi-Civita tensor
216 """
217 eijk = zeros((3, 3, 3))
218 eijk[0, 1, 2] = eijk[1, 2, 0] = eijk[2, 0, 1] = 1
219 eijk[0, 2, 1] = eijk[2, 1, 0] = eijk[1, 0, 2] = -1
220 return eijk
223def UIP():
224 """
225 Test for Jupyter Notebooks.
227 Returns
228 -------
229 using_IP: bool
230 Are we using Jupyter notebooks?
232 """
233 try:
234 using_IP = str(get_ipython()).split()[0].split('.')[-1] == 'ZMQInteractiveShell'
235 except NameError:
236 using_IP = False
237 return using_IP
240def MPL():
241 """
242 Test matplotlib is functioning.
244 Returns
245 -------
246 mpl: bool
247 Can we use matplotlib?
249 """
250 with open(devnull, "a") as nf:
251 exitval = run([executable, "-c", "import matplotlib.pyplot as plt; plt.figure()"],
252 stderr=nf)
253 return exitval.returncode == 0
256def EMPI():
257 """
258 Test whether MPI entry point os used.
260 Returns
261 -------
262 MPI.COMM_WORLD: comm
263 All processors communicator
264 MPI: module
265 MPI module from mpi4py or class
267 """
268 def mpifuns():
269 class CW():
270 def Get_size():
271 return 0
273 def Get_rank():
274 return 0
276 class MPI():
277 COMM_WORLD = CW
278 return MPI
280 if argv[0].split('/')[-1].endswith("mpi"): 280 ↛ 281line 280 didn't jump to line 281, because the condition on line 280 was never true
281 try:
282 from mpi4py import MPI
283 except ImportError:
284 MPI = mpifuns()
285 else:
286 MPI = mpifuns()
288 return MPI.COMM_WORLD, MPI
291def CPU():
292 """
293 Get number of cores available.
295 Returns
296 -------
297 processors: int
298 number of processors
300 """
301 try:
302 pind = argv.index("--cores")
303 processors = argv[pind + 1]
304 del argv[pind + 1]
305 del argv[pind]
306 except ValueError:
307 try:
308 from os import sched_getaffinity
309 processors = len(sched_getaffinity(0))
310 except ImportError:
311 try:
312 from numexpr import detect_number_of_cores
313 processors = detect_number_of_cores()
314 except ImportError:
315 # I tried
316 from multiprocessing import cpu_count
317 processors = cpu_count()
319 return int(processors)
322def UCODE(t):
323 """
324 Set up unicode strings.
326 Returns
327 -------
328 ustr: class
329 Unicode chars store
330 uc: bool
331 Is environment unicode?
333 """
334 unicode = run(['locale', 'charmap'], stdout=PIPE, stderr=PIPE)
335 uc = (unicode.stdout.decode()[:-1] == 'UTF-8')
337 ustr = _variables()
338 if uc: 338 ↛ 345line 338 didn't jump to line 345, because the condition on line 338 was never false
339 ustr.mu = "\u03BC"
340 ustr.chi = "\u03C7"
341 ustr.tau = "\u03C4"
342 ustr.taub = ustr.tau + t.subscript + 'b' + t.no_subscript
343 ustr.taun = ustr.tau + t.subscript + 'n' + t.no_subscript
344 else:
345 verb.print("Character encoding: ", unicode.stdout)
346 ustr.mu = "mu"
347 ustr.chi = "chi"
348 ustr.tau = "tau"
349 ustr.taub = "tau_b"
350 ustr.taun = "tau_n"
352 return ustr, uc
355def INTTERM():
356 """
357 Check for interactive terminal.
359 Returns
360 -------
361 interactive: bool
362 Is environment interactive?
364 """
365 try:
366 from sys import ps1
367 interactive = True
368 except ImportError:
369 interactive = False
370 return interactive
373def PROF():
374 """
375 Test for code profiling.
377 Returns
378 -------
379 profile: bool
380 Is profile in argv?
382 """
383 try:
384 pind = argv.index("--profile")
385 del argv[pind]
386 profile = True
387 except ValueError:
388 profile = False
389 return profile
392def nestedDict():
393 """
394 Nested Dictionaries.
396 In principle this creates an infinitely deep nested dictionary
398 eg. dict_name["key"]["key"]["key"]= value
400 each key can have sub keys and because defaultdict creates
401 the key if it doesn't exist this is all that is needed
402 """
403 return defaultdict(nestedDict)
406class getkeywords():
407 """Get keywords and organise them."""
409 def __init__(self):
410 """Get defaults and keywords."""
411 self.words = self._getkeywords()
412 self.defaults = self._getdefaults()
413 self.explanation = self._gethelp()
415 def _gethelp(self):
416 """
417 Get default vaules for all arguments.
419 Returns
420 -------
421 dict
422 default values
424 """
425 explanation = {}
426 for no, i in enumerate(self.words.values()):
427 explanation[no] = i[1][1]['help']
429 return explanation
431 def _getdefaults(self):
432 """
433 Get default vaules for all arguments.
435 Returns
436 -------
437 dict
438 default values
440 """
441 defs = {}
442 for i in self.words.values():
443 defs[i[0][0]] = i[0][2]
445 return defs
447 def flgorvar(self):
448 """
449 Sort arguments into list of flags or variables.
451 Returns
452 -------
453 var_list: list
454 list of vars
455 flg_list: list
456 list of flags
458 """
459 var_list = ['extra_iter', 'RandNoState', 'defaults',
460 'time', 'ms', 'sigma', 'limit'] # 1e-12 [s], 1e6[emu/cm^3], 1e-6 [cm],1/1.e-6 cm (inverted)
461 flag_list = ['files', 'field', 'lastframe']
463 def renamed(lis, key):
464 n = 4 if len(key) == 5 else 0
465 lis += [key[n]]
467 for i in self.words.values():
468 if i[0][3] == "var":
469 renamed(var_list, i[0])
470 elif i[0][3] == "flg": 470 ↛ 467line 470 didn't jump to line 467, because the condition on line 470 was never false
471 renamed(flag_list, i[0])
473 return var_list, flag_list
475 @staticmethod
476 def _range(val, min=0, max=1):
477 from argparse import ArgumentTypeError
478 value = float(val)
479 if min <= value <= max:
480 return value
481 else:
482 raise ArgumentTypeError('value not in range {}-{}'.format(min, max))
484 @debug(["keys"])
485 def _getkeywords(self):
486 """
487 Definition of all keywords.
489 Includes argparse calls, default name, types, values and container
491 Returns
492 -------
493 OrderedDict
494 All current keywords
496 """
497 return OrderedDict(
498 # Input files
499 {"InputFile": (("inputfile", str, None, "var"),
500 ('-I',
501 dict(dest='inputfile', help="R|Input file location"))),
503 "RestartFile": (("restart", str, None, "flg"),
504 ('-R',
505 dict(dest="restart", help="R|Restart file location"))),
506 "PositionFile": (("location", str, None, "var"),
507 ('-P',
508 dict(dest="location", help="R|Positions file location"))),
509 # Properties
510 "ExternalFieldFreq": (("nu", float, 1e-6, "var"), # [Hz] 267.0e3 was katherine
511 ('-nu',
512 dict(help='R|External Field Frequency\n(default: {:3.2e}[Hz])'))),
513 "ExternalFieldStrength": (("H_0", float, 167.0, "var"), # 1e-6 gauss 167.0 * 1e6 was katherine
514 (['-f', '--field'],
515 dict(dest="H_0", help='R|External Field Strength\n(default: {}[1e-6 Gauss])'))),
516 "MediumViscosity": (("eta", float, 1.002e-2, "var"), # [g/(cm *s)]
517 (['-e', '--eta'],
518 dict(help='R|Solvent Viscosity\n(default: {} [g/(cm *s)])'))),
519 "MagneticDensity": (("Mdens", float, 85, "var"), # [emu/g]
520 ('-Mdens',
521 dict(help='R|Magnetic Density of particles\n(default {} [emu/g])'))),
522 "ParticleDensity": (("dens", float, 6.99, "var"), # [g /cm^3]
523 (['-dn', '--dens'],
524 dict(nargs='+', help='R|Density of each particle\n(default: {}[g /cm^3])'))),
525 "ParticleRadius": (("radius", float, 5.0, "var"), # [1e-6cm]
526 (['-rad', '--radius'],
527 dict(nargs='+', help='R|Radius of each particle\n(default: {}[1e-6cm])'))),
528 "Temperature": (("temp", float, 298.0, "var"), # [K]
529 (['-T', '--temp'],
530 dict(help='R|Temperature\n(default: {}[K])'))),
532 # Calculation Options
533 "BackgroundNoise": (("noise", bool, True, "flg"),
534 ('--no-noise', dict(action="store_false", dest='noise',
535 help='R|Background Noise switch\n(default: {})'))),
536 "Boxsize": (("boxsize", float, 50, "var"), # [1e-6cm]
537 (['-bx', '--boxsize'],
538 dict(nargs='+', help='R|Periodic boundary box size\n(default: max of (1.1 * Number of Molecules * Radius) or {}[1e-6cm])'))),
539 "Epsilon": (("epsilon", float, None, "var"), # e.u.
540 ('--epsilon',
541 dict(help='R|Set depth of Potential Well\n(default: Scales wrt Ar (isothermal compresibility))'))),
542 "Iterations": (("nmax", float, 4.0e2, "var"),
543 (['-n', '--nmax'],
544 dict(help='R|Number of Iterations\n(default: {})'))),
545 "Labview": (("labview", bool, True, "flg"),
546 ('--no-labview',
547 dict(action="store_false",
548 help='R|Remove initial cluster momentum\n(default: {})'))),
549 "MagnetisationSW": (("mag_sw", bool, True, "flg"),
550 ('--no-mag',
551 dict(action="store_false", dest='mag_sw', help='R|Magnetisation calculation switch\n(default: {})'))),
552 "NumberParticles": (("no_molecules", int, 5, "var"),
553 (['-np', '--no_particles'],
554 dict(dest='no_molecules', help='R|Number of Particles\n(default: {})'))),
555 "Optimisation": (("opt", bool, False, "flg"),
556 ('--opt',
557 dict(action="store_true", dest='opt', help='R|Auto-minimise energy of the system\n(default: {})'))),
558 "PeriodicBoundaries": (("pbc", bool, True, "flg"),
559 ('--no-pbc',
560 dict(action="store_false", dest='pbc', help='R|Periodic Boundary Conditions\n(default: {})'))),
561 "Potential": (('potential', str, False, "var"),
562 ('--potential',
563 dict(help='R|Change potential used'))),
564 "Repetitions": (("stats", int, 10, "var"),
565 (['-st', '--stats'],
566 dict(help='R|Number of statistical repetitions\n(default: {})'))),
567 "SkipSave": (("skip", int, 1, "var"),
568 (['-sk', '--skip'],
569 dict(help='R|Save every # iterations\n(default: {})'))),
570 "SusceptibilityCalc": (("suscep", list, False, "flg"),
571 ('--suscep',
572 dict(nargs='*', type=str, choices=['mag', 'vel', 'angular', 'inertia', 'rotation', ''],
573 help='R|Zero field Calculation\n(default: {})'))),
574 "ThermalOptim": (('op_thermal', bool, False, "flg"),
575 ('--op_thermal',
576 dict(action='store_true', help='R|Add thermal noise to the momentum after the optimisation\n(default: {})'))),
577 "Timestep": (("dt", float, 20, "var"), # [ps]
578 ('-dt',
579 dict(help='R|Timestep\n(default: {}[ps])'))),
581 # Postprocessing Options
582 "Align": (('align', bool, False, "flg"),
583 ('--align',
584 dict(action="store_true", help='R|Align with Kabsch algorithm\n(default: {})'))),
585 "Blocking": (('block', list, [5, 10, 20, 50, 100], "var"),
586 ('--bl',
587 dict(nargs='+', type=int, dest='block',
588 help='R|Block averaging bins\n(default: {})'))),
589 "CreateFile": (("cfile", list, False, "flg"),
590 ('--cfile',
591 dict(nargs='+', type=str, choices=['xyz', 'energy', 'momenta', 'lf'],
592 help='R|Create output files\n(default: {})'))),
593 "Equilibrate": (('eq', float, 0.1, "flg"),
594 ('-eq',
595 dict(type=self._range, dest='eq',
596 help='R|Strip fraction of trajectory for equilibration\n(default: {})'))),
597 "PlotColumns": (("column", list, False, "flg"),
598 ('--column',
599 dict(nargs='+', type=str, help='R|Plot simple datasets'))),
600 "Run": (("run", int, False, "flg"),
601 ('--run',
602 dict(help='R|Select run number\n(default: {})'))),
603 "KineticTemp": (('kinetic', bool, False, "flg"),
604 ('--kinetic',
605 dict(action="store_true", help='R|Calculate kinetic temperature\n(default: {})'))),
606 "LengthCheck": (('lengthcheck', bool, False, "flg"),
607 ('--lengthcheck',
608 dict(action="store_true", help='R|Calculate average particle separation\n(default: {})'))),
609 "ShowGraphs": (("showg", bool, False, "flg"),
610 ('--showg',
611 dict(action="store_true", help='R|Show graphs during run\n(default: {})'))),
612 "SaveGraphs": (("saveg", bool, False, "flg"),
613 ('--saveg',
614 dict(action="store_true", help='{}{}'.format('R|Save graphs\n',
615 'WARNING: slow for large datasets\n(default: {})')))),
616 "UseFile": (("ufile", list, False, "flg"),
617 ('--ufile',
618 dict(type=str, help='{}'.format('R|Post process from a specific file\n',
619 'Possible file types: [xyz, hdf5, pkl, txt]\n',
620 'for xyz see help(mango.io.read_data.xyzppread)')))),
622 # Run style changes
623 "LogDirectory": (("logs", str, "{}/Mango_{}_Logs".format(getcwd(), c.version), "var"),
624 ('--logs',
625 dict(help='R|Log save directory\n(default:./{})'))),
626 "Parallel": (("parallel", bool, True, "flg"),
627 ('--no-parallel',
628 dict(action="store_false", dest='parallel', help='R|Parallel Computation\n(default: {})'))),
629 "SaveChunk": (("savechunk", float, 1e4, "var"),
630 (['-sv', '--savechunk'],
631 dict(help='{}{}'.format('R|Save to file every # iterations\n',
632 '(default: Smallest of {:3.0e} and Nmax)')))),
633 "SaveFiletype": (("save_type", str, 'hdf5', "flg"),
634 ('--save_type',
635 dict(choices=['hdf5', 'pkl', 'txt'], help='R|Save filetype (hdf5, pkl, txt)\n(default: {})'))),
636 "Scfiter": (("scfiter", int, 20, "var"),
637 ('--scfiter',
638 dict(help='R|Magnetic self consistancy loop before warning\n(default: {})'))),
639 "Walltime": (("walltime", int, 1e7, "var"),
640 ('--walltime',
641 dict(help='R|Run time\n(default: {:3.0e}[s])'))),
642 "Verbosity": (("nout", int, 2, "flg"),
643 (['-v', '--verbosity'],
644 dict(dest='nout', action='count', help='R|Verbosity level\n(default: {}, Max implemented: 5)'))),
646 # Data for Neel Relaxations from: Journal of Magnetism and Magnetic Materials 321 (2009) 3126-3131
647 "NeelRelaxations": (("neel", bool, False, "flg"),
648 ('--neel',
649 dict(action="store_true", help='R|Enable Neel relaxations\n(default: {})'))),
650 "NeelAnisotropicFactor": (("keff", float, 2.25e-8, "var"), # Unit [1.e12 erg/cm^3]
651 (['-k', '--keff'],
652 dict(help='R|Set anisotropic factor\n(default: {:3.2e}[erg*cm3])'))),
653 "NeelTau0": (("tauN_0", float, 1.7, "var"), # Unit [1.e-12 s]
654 ('--tauN_0',
655 dict(help='R|Zero value for {}\n'.format(c.ustr.taun) + '(default: {:3.2e}[1.e-12 s])'))),
656 "NeelTemp0": (("temp0", float, 262.0, "var"), # Unit: ([K]
657 ('--temp0',
658 dict(help='R|Neel zero temperature\n(default: {:3.2e}[K])'))),
659 })
662class _variables():
663 """
664 Dynamic creation of variables.
666 Initial input is a dictionary but further values can be added
667 as with any class by:
669 class_instance.variable_name = value
670 """
672 def __init__(self, **argd):
673 self.__dict__.update(argd)
675 def print_contents(self):
676 for i, j in self.__dict__.items():
677 if isinstance(j, ndarray):
678 pr_j, index, counts = unique(j, return_index=True, return_counts=True)
679 if index.size == 1:
680 pr_j = "{} x {}".format(pr_j[0], counts[0])
681 else:
682 pr_j = pr_j, index, counts
683 else:
684 pr_j = j
685 print(i, pr_j)
688def switch(switch_name):
689 """
690 Decorate to switch off a function.
692 Improvements- TODO
694 * Still return expected return values but with no changes to variables or new variables set to 0 or none
695 * only call if switched off
696 """
697 def sw_decorate(func_to_decorate):
698 @wraps(func_to_decorate)
699 def wrapper(*args, **kw):
700 switch = getattr(getattr(args[0], "flg"), switch_name)
701 if switch:
702 result = func_to_decorate(*args, **kw)
703 else:
704 def nfunc(*args, **kw):
705 pass
706 result = nfunc()
707 return result
708 return wrapper
709 return sw_decorate
712def rmemptyfile(filename):
713 """
714 Remove files of Zero size.
716 Parameters
717 ----------
718 filename: str
719 Full filename
721 """
722 with suppress(FileNotFoundError):
723 if stat(filename).st_size == 0:
724 remove(filename)
727def boolconvert(boolean):
728 """
729 Convert strings to bool correctly.
731 Parameters
732 ----------
733 boolean: str
735 Returns
736 -------
737 bool
739 """
740 return boolean.lower() in ("true", "t", "1")
743def asciistring(length):
744 """
745 Get a list of characters of desired length.
747 Current max is 26 characters
749 Parameters
750 ----------
751 length: int
752 number of characters to return
754 Returns
755 -------
756 str of length specified
758 """
759 return [chr(i) for i in range(ord('a'), ord('z') + 1)][:length]
762class bin_count():
763 """
764 Fast ufunc.at.
766 bincount OR ufunc.at
767 but ufunc.at is slower: https://github.com/numpy/numpy/issues/5922
768 Much faster even with the manipulation needed for bincount
770 pass flattened arrays in eg arr.ravel()
771 """
772 __slots__ = ['leng', 'shape']
774 def __init__(self, **argd):
775 """
776 Intialise.
778 dictionary needs leng and shape
779 """
780 for k, v in argd.items():
781 setattr(self, k, v)
783 def addat(self, to, at, fro):
784 """Add at."""
785 to += bincount(at, fro, minlength=self.leng)
787 def subtractat(self, to, at, fro):
788 """Subtract at."""
789 to -= bincount(at, fro, minlength=self.leng)
791 def reshape(self, to):
792 """Reshape to given shape."""
793 return to.reshape(self.shape)
796c = const()
797keys = getkeywords()
798keys.words = keys.words if 'helpout.py' in argv else {'keywords'}
800if __name__ == "__main__":
801 pass