Hide keyboard shortcuts

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 

9 

10from _version import version 

11from mango.debug import debug, verb 

12 

13 

14class const(): 

15 """ 

16 | Constants and version number. 

17 

18 | Internal Units 

19 

20 | 

21 | Main 

22 

23 | 

24 

25 * time: 1e-12 [s] 

26 * length: 1e-6 [cm] 

27 * mass: 1e-18 [g] 

28 

29 | Derived 

30 

31 | 

32 

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] 

38 

39 | 

40 

41 """ 

42 @debug(['const']) 

43 def __init__(self): 

44 """Get constants and check environment.""" 

45 self.version = version 

46 

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 

57 

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() 

66 

67 self.reseed() 

68 

69 self.posfuncs = ['vv_trans', 'vv_mag', 'ff_trans', 'ff_mag', 'hh_mag', 'force_vars', 'energy_vars'] 

70 

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)"}) 

85 

86 c2 = [v2 for v in self.units.values() if type(v) == dict for v2 in v.keys()] 

87 c2 += ['time'] 

88 

89 self.columns_flat = set(c2) 

90 self.columns = {"xyz": c2[:4], 

91 "energy": c2[4:8], 

92 "momenta": c2[8:13]} 

93 

94 self.files = list(self.columns.keys()) 

95 

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 

100 

101 def jump(self, jumps): 

102 self.rgen = self.rgen.jumped(jumps) 

103 self.random = self._Gen(self.rgen) 

104 

105 def set_accuracy(self, EPS_val=None): 

106 self.EPS_val = EPS_val if EPS_val else self.EPS2 

107 

108 def Error(self, msg): 

109 """ 

110 Simple error printer. 

111 

112 Use managers.serverwrapper('Error') decorator for fully fledged error management 

113 """ 

114 print(msg) 

115 if msg.startswith('F'): 

116 exit(1) 

117 

118 def Prog(self, msg): 

119 """Blank function, replaced on the fly with progressbar if required.""" 

120 pass 

121 

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) 

126 

127 def clear(self): 

128 """Clear screen and move cursor.""" 

129 print(self.t.clear + self.t.move_y(0), end='', flush=True) 

130 

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") 

139 

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("") 

157 

158 

159def MTrnd(seed=12345): 

160 """Random number generator.""" 

161 from numpy.random import Generator, MT19937 

162 rgen = MT19937(seed) 

163 return Generator, rgen 

164 

165 

166def GT(): 

167 """ 

168 Get terminal information. 

169 

170 Returns 

171 ------- 

172 Terminal: instance 

173 Blessings terminal class instance 

174 Terminal info: dict 

175 Terminal info 

176 

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 

186 

187 @contextmanager 

188 def location(*args): 

189 yield 

190 

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() 

200 

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 

205 

206 

207def LCT(): 

208 """ 

209 Construct Levi-Civita Tensor. 

210 

211 Returns 

212 ------- 

213 eijk: array 

214 Levi-Civita tensor 

215 

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 

221 

222 

223def UIP(): 

224 """ 

225 Test for Jupyter Notebooks. 

226 

227 Returns 

228 ------- 

229 using_IP: bool 

230 Are we using Jupyter notebooks? 

231 

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 

238 

239 

240def MPL(): 

241 """ 

242 Test matplotlib is functioning. 

243 

244 Returns 

245 ------- 

246 mpl: bool 

247 Can we use matplotlib? 

248 

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 

254 

255 

256def EMPI(): 

257 """ 

258 Test whether MPI entry point os used. 

259 

260 Returns 

261 ------- 

262 MPI.COMM_WORLD: comm 

263 All processors communicator 

264 MPI: module 

265 MPI module from mpi4py or class 

266 

267 """ 

268 def mpifuns(): 

269 class CW(): 

270 def Get_size(): 

271 return 0 

272 

273 def Get_rank(): 

274 return 0 

275 

276 class MPI(): 

277 COMM_WORLD = CW 

278 return MPI 

279 

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() 

287 

288 return MPI.COMM_WORLD, MPI 

289 

290 

291def CPU(): 

292 """ 

293 Get number of cores available. 

294 

295 Returns 

296 ------- 

297 processors: int 

298 number of processors 

299 

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() 

318 

319 return int(processors) 

320 

321 

322def UCODE(t): 

323 """ 

324 Set up unicode strings. 

325 

326 Returns 

327 ------- 

328 ustr: class 

329 Unicode chars store 

330 uc: bool 

331 Is environment unicode? 

332 

333 """ 

334 unicode = run(['locale', 'charmap'], stdout=PIPE, stderr=PIPE) 

335 uc = (unicode.stdout.decode()[:-1] == 'UTF-8') 

336 

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" 

351 

352 return ustr, uc 

353 

354 

355def INTTERM(): 

356 """ 

357 Check for interactive terminal. 

358 

359 Returns 

360 ------- 

361 interactive: bool 

362 Is environment interactive? 

363 

364 """ 

365 try: 

366 from sys import ps1 

367 interactive = True 

368 except ImportError: 

369 interactive = False 

370 return interactive 

371 

372 

373def PROF(): 

374 """ 

375 Test for code profiling. 

376 

377 Returns 

378 ------- 

379 profile: bool 

380 Is profile in argv? 

381 

382 """ 

383 try: 

384 pind = argv.index("--profile") 

385 del argv[pind] 

386 profile = True 

387 except ValueError: 

388 profile = False 

389 return profile 

390 

391 

392def nestedDict(): 

393 """ 

394 Nested Dictionaries. 

395 

396 In principle this creates an infinitely deep nested dictionary 

397 

398 eg. dict_name["key"]["key"]["key"]= value 

399 

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) 

404 

405 

406class getkeywords(): 

407 """Get keywords and organise them.""" 

408 

409 def __init__(self): 

410 """Get defaults and keywords.""" 

411 self.words = self._getkeywords() 

412 self.defaults = self._getdefaults() 

413 self.explanation = self._gethelp() 

414 

415 def _gethelp(self): 

416 """ 

417 Get default vaules for all arguments. 

418 

419 Returns 

420 ------- 

421 dict 

422 default values 

423 

424 """ 

425 explanation = {} 

426 for no, i in enumerate(self.words.values()): 

427 explanation[no] = i[1][1]['help'] 

428 

429 return explanation 

430 

431 def _getdefaults(self): 

432 """ 

433 Get default vaules for all arguments. 

434 

435 Returns 

436 ------- 

437 dict 

438 default values 

439 

440 """ 

441 defs = {} 

442 for i in self.words.values(): 

443 defs[i[0][0]] = i[0][2] 

444 

445 return defs 

446 

447 def flgorvar(self): 

448 """ 

449 Sort arguments into list of flags or variables. 

450 

451 Returns 

452 ------- 

453 var_list: list 

454 list of vars 

455 flg_list: list 

456 list of flags 

457 

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'] 

462 

463 def renamed(lis, key): 

464 n = 4 if len(key) == 5 else 0 

465 lis += [key[n]] 

466 

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]) 

472 

473 return var_list, flag_list 

474 

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)) 

483 

484 @debug(["keys"]) 

485 def _getkeywords(self): 

486 """ 

487 Definition of all keywords. 

488 

489 Includes argparse calls, default name, types, values and container 

490 

491 Returns 

492 ------- 

493 OrderedDict 

494 All current keywords 

495 

496 """ 

497 return OrderedDict( 

498 # Input files 

499 {"InputFile": (("inputfile", str, None, "var"), 

500 ('-I', 

501 dict(dest='inputfile', help="R|Input file location"))), 

502 

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])'))), 

531 

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])'))), 

580 

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)')))), 

621 

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)'))), 

645 

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 }) 

660 

661 

662class _variables(): 

663 """ 

664 Dynamic creation of variables. 

665 

666 Initial input is a dictionary but further values can be added 

667 as with any class by: 

668 

669 class_instance.variable_name = value 

670 """ 

671 

672 def __init__(self, **argd): 

673 self.__dict__.update(argd) 

674 

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) 

686 

687 

688def switch(switch_name): 

689 """ 

690 Decorate to switch off a function. 

691 

692 Improvements- TODO 

693 

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 

710 

711 

712def rmemptyfile(filename): 

713 """ 

714 Remove files of Zero size. 

715 

716 Parameters 

717 ---------- 

718 filename: str 

719 Full filename 

720 

721 """ 

722 with suppress(FileNotFoundError): 

723 if stat(filename).st_size == 0: 

724 remove(filename) 

725 

726 

727def boolconvert(boolean): 

728 """ 

729 Convert strings to bool correctly. 

730 

731 Parameters 

732 ---------- 

733 boolean: str 

734 

735 Returns 

736 ------- 

737 bool 

738 

739 """ 

740 return boolean.lower() in ("true", "t", "1") 

741 

742 

743def asciistring(length): 

744 """ 

745 Get a list of characters of desired length. 

746 

747 Current max is 26 characters 

748 

749 Parameters 

750 ---------- 

751 length: int 

752 number of characters to return 

753 

754 Returns 

755 ------- 

756 str of length specified 

757 

758 """ 

759 return [chr(i) for i in range(ord('a'), ord('z') + 1)][:length] 

760 

761 

762class bin_count(): 

763 """ 

764 Fast ufunc.at. 

765 

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 

769 

770 pass flattened arrays in eg arr.ravel() 

771 """ 

772 __slots__ = ['leng', 'shape'] 

773 

774 def __init__(self, **argd): 

775 """ 

776 Intialise. 

777 

778 dictionary needs leng and shape 

779 """ 

780 for k, v in argd.items(): 

781 setattr(self, k, v) 

782 

783 def addat(self, to, at, fro): 

784 """Add at.""" 

785 to += bincount(at, fro, minlength=self.leng) 

786 

787 def subtractat(self, to, at, fro): 

788 """Subtract at.""" 

789 to -= bincount(at, fro, minlength=self.leng) 

790 

791 def reshape(self, to): 

792 """Reshape to given shape.""" 

793 return to.reshape(self.shape) 

794 

795 

796c = const() 

797keys = getkeywords() 

798keys.words = keys.words if 'helpout.py' in argv else {'keywords'} 

799 

800if __name__ == "__main__": 

801 pass