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 numpy import (all as nall, array, array_str, reshape, where, einsum, concatenate, 

2 ndarray, block, arange, savetxt, atleast_1d, atleast_2d, genfromtxt) 

3from re import search 

4from sys import stdout, argv 

5from os import get_terminal_size, path, getcwd, fstat 

6from shutil import copyfile 

7from datetime import datetime 

8from asyncio import new_event_loop 

9from contextlib import suppress 

10 

11from mango.constants import c, _variables, keys, rmemptyfile, boolconvert 

12from mango.debug import debug 

13from mango.managers import serverwrapper 

14 

15import mango.imports as imports 

16 

17 

18@debug(['io']) 

19def _input(func): 

20 """ 

21 User input that wipes itself after input. 

22 

23 (For error printing) 

24 """ 

25 out = input(func) 

26 stdout.write("\x1b[A\x1b[A") 

27 a = get_terminal_size() 

28 stdout.write(" " * a.columns) 

29 return out 

30 

31 

32def separater(response): 

33 response = atleast_2d(response) 

34 

35 cols = response.shape[-1] 

36 

37 if cols == 3: 

38 response = (response, None, None) 

39 elif cols == 6: 

40 response = (response[..., :3], response[..., 3:6], None) 

41 elif cols >= 9: 41 ↛ 44line 41 didn't jump to line 44, because the condition on line 41 was never false

42 response = (response[..., :3], response[..., 3:6], response[..., 6:9]) 

43 

44 return response 

45 

46 

47class Data(): 

48 append = set(["position", "magnetisation", "momentum", "iter_time", "magnetic_field", 

49 "forces", "neel_relaxation"]) 

50 

51 

52class write_data(Data): 

53 """Writing all calculated data to file.""" 

54 

55 @debug(["write"]) 

56 def __init__(self, var, flg): 

57 self.dump, self.save_type = imports.write(flg.save_type) 

58 name = var.name 

59 self.directory = name[:name.rfind("/") + 1] 

60 self.restartfile = "{}{}{}".format(self.directory, name[name.rfind("Run"):name.rfind("_")], "restart") 

61 self.total = var.skip_iters + 1 

62 self.nmax = self.total * var.no_molecules 

63 

64 self.restarted = flg.restart 

65 self.restart() 

66 

67 def setup(self, name): 

68 self.loop = new_event_loop() 

69 self.addtorestart(name) 

70 return self.save_type 

71 

72 def write(self, *args, **kw): 

73 self.loop.run_until_complete(self._write(*args, **kw)) 

74 

75 @debug(['io']) 

76 async def _write(self, moldata_dict): 

77 """Write data to specific file type.""" 

78 if self.save_type == "hdf5": 

79 self.dump(moldata_dict['name'], moldata_dict, 

80 compression=('blosc', 9), datalength=self.nmax, append=self.append) 

81 elif self.save_type == "pkl": 81 ↛ 85line 81 didn't jump to line 85, because the condition on line 81 was never false

82 with open(moldata_dict['name'], "ab") as w: 

83 self.dump(moldata_dict, w, protocol=-1) 

84 else: 

85 no_mol = moldata_dict["position"].shape[1] 

86 txt_data(moldata_dict, no_mol) 

87 

88 def restart(self): 

89 if path.isfile(self.restartfile): 89 ↛ 90line 89 didn't jump to line 90, because the condition on line 89 was never true

90 copyfile(self.restartfile, self.restartfile + "R" + datetime.now().isoformat(timespec='minutes')) 

91 with open(self.restartfile, 'w') as rf: 

92 rf.write(f"save_type {self.save_type}\ndirectory {self.directory}\ntotal_tosave {self.total}\n") 

93 

94 def addtorestart(self, fname): 

95 with open(self.restartfile, "a") as rf: 

96 rf.write(f"{fname.rsplit('/')[-1]}\n") 

97 

98 

99class read_data(Data): 

100 """ 

101 Reads data in for post processing. 

102 

103 Improvements 

104 * Only read in data required rather than all data 

105 - Works for hdf5 files 

106 """ 

107 

108 def __init__(self, fname, rtype=None, xyzpp=False): 

109 if rtype is None: 109 ↛ 110line 109 didn't jump to line 110, because the condition on line 109 was never true

110 fname, rtype = fname.rsplit('.', 1) 

111 self.xyzpp = xyzpp 

112 self.load, self.rtype = imports.read(rtype) 

113 self._newname(fname, xyzpp) 

114 

115 def _readtype(self): 

116 

117 if self.rtype == "hdf5": 

118 self._rd = self._hdf5read 

119 elif self.rtype == "pkl": 

120 self._rd = self._pickleread 

121 elif self.xyzpp: 121 ↛ 122line 121 didn't jump to line 122, because the condition on line 121 was never true

122 self._rd = self.xyzppread 

123 self.xvsloc = "{}.var".format(self.fname.split('.')[0]) 

124 elif self.rtype == 'xyz': 

125 self._rd = self.xyzread 

126 else: 

127 self._rd = self.txtread 

128 

129 def _hdf5read(self, obj, *args): 

130 return self.load(self.fname, obj, *args) 

131 

132 def _pickleread(self, obj, *args): 

133 if obj is None: 133 ↛ 134line 133 didn't jump to line 134, because the condition on line 133 was never true

134 c.Error('F pkl files require key name') 

135 lspl = [x for x in filter(None, obj.split('/'))] 

136 data = [] 

137 with open(self.fname, 'rb') as self.v: 

138 l_obj = lspl[0] if len(lspl) > 1 else obj 

139 while self.v.tell() < fstat(self.v.fileno()).st_size: 

140 data.append(self.load(self.v)[l_obj]) 

141 

142 if len(lspl) > 1: 

143 for n, reads in enumerate(data): 

144 for i in lspl[1:]: 

145 data[n] = data[n][i] 

146 if l_obj in self.append: 146 ↛ 147line 146 didn't jump to line 147, because the condition on line 146 was never true

147 data = concatenate(data, axis=0) 

148 return data[-1] if l_obj in ['vars', 'flags'] else data 

149 

150 def _newname(self, fname, xyzpp): 

151 self.xyzpp = xyzpp 

152 self.fname = fname if fname.endswith(self.rtype) else "{}.{}".format(fname, self.rtype) 

153 self._readtype() 

154 

155 @debug(["io"]) 

156 def read(self, obj=None, fname=None, restart=False, lengthcheck=False, keylist=False, chunk=None, xyzpp=False): 

157 """ 

158 Read in pickle and hdf5 outputs. 

159 

160 Parameters 

161 ---------- 

162 obj: string 

163 object to be read in 

164 fname: string 

165 file to be read in 

166 restart: bool 

167 Is read in to restart run 

168 lengthcheck: bool 

169 Get lenth of position data and last iteration 

170 

171 """ 

172 if fname is None: 

173 fname = self.fname 

174 if self.fname != fname: 

175 self._newname(fname, xyzpp) 

176 

177 if restart: 

178 if self.rtype in ["xyz", "txt"]: 178 ↛ 179line 178 didn't jump to line 179, because the condition on line 178 was never true

179 c.Error("F Restarting is not implemented for this filetype") 

180 return self.getrestart(*self.lengthcheck()) 

181 elif lengthcheck: 181 ↛ 182line 181 didn't jump to line 182, because the condition on line 181 was never true

182 return self.lengthcheck() 

183 else: 

184 return self._rd(obj, chunk, keylist) 

185 

186 def getrestart(self, position, last_iter): 

187 """Get restart data.""" 

188 restart = _variables(**self._rd('vars')) 

189 restart.pos = position[last_iter, :, :] 

190 restart.mag = self._rd("magnetisation")[last_iter, :, :] 

191 restart.mom = self._rd("momentum")[last_iter, :, :] 

192 

193 restartflags = _variables(**self._rd('flags')) 

194 return restart, restartflags, last_iter 

195 

196 def lengthcheck(self): 

197 """ 

198 Check which row number is the last iteration. 

199 

200 Redundency check to make sure the last used row is the last row 

201 """ 

202 position = self._rd('position') 

203 l_it = nall(position == 0.0, axis=(-2, -1)) 

204 

205 if not nall(l_it): 

206 # l_it == False produces correct answer 

207 # l_it is False does not 

208 last_iter = where(l_it == False)[0][-1] 

209 else: 

210 last_iter = 0 

211 

212 return position, last_iter 

213 

214 def xyzppread(self, obj=None, *args): 

215 """ 

216 Post process from xyz file. 

217 

218 Requires associated <filename>.var file with variables needed for post processing 

219 also needs to include the variable columns with the xyz column variable names. 

220 

221 e.g. 

222 mass 1.111 

223 radius 1.23e-4 

224 columns position,velocity,force 

225 

226 Only internal variable names currently supported 

227 """ 

228 if 'xvars' not in self.__dict__.keys(): 

229 self.xvars = self._xyzvars() 

230 

231 if obj == 'vars': 

232 return self.xvars 

233 elif 'xyz' not in self.__dict__.keys(): 

234 _, self.xyz = self.xyzread() 

235 self.xyz = separater(self.xyz) 

236 

237 try: 

238 col = self.xvars['columns'].index(obj) 

239 return self.xyz[col] 

240 except ValueError: 

241 if "velocity" in self.xvars['columns']: 

242 col = self.xvars['columns'].index("velocity") 

243 return self.xyz[col] * self.xvars['mass'] 

244 

245 def xyzread(self, *args): 

246 """Read all data from (extended) xyz file.""" 

247 with open(self.fname, "rb") as loc: 

248 for lines in loc: 248 ↛ 252line 248 didn't jump to line 252, because the loop on line 248 didn't complete

249 if lines[:-1] not in [b"", b"#"]: 249 ↛ 248line 249 didn't jump to line 248, because the condition on line 249 was never false

250 break 

251 

252 response = atleast_2d(genfromtxt(self._skip_lines(self.fname, lines), comments="#", dtype=str)) 

253 

254 molecules = response[:, 0] 

255 

256 lastaxis = response.shape[-1] - 1 

257 

258 lines = int(lines.decode("utf-8")) 

259 

260 if (response.shape[0] % lines != 0 or lastaxis % 3 != 0): 

261 c.Error("F Input array shape not consistant with number of molecules specified") 

262 

263 response = response[:, 1:].astype(float).reshape((-1, lines, lastaxis), order='F') 

264 

265 return molecules, response 

266 

267 @staticmethod 

268 def _skip_lines(fname, lines): 

269 with open(fname, "rb") as file: 

270 skip = -1 

271 for lno, line in enumerate(file): 

272 if line.startswith(lines): 

273 skip = lno + 1 

274 yield b"#" + line if line.startswith((b"i", lines)) or lno == skip else line 

275 

276 def _xyzvars(self, vnames={}): 

277 with open(self.xvsloc) as xyzvars: 

278 for line in xyzvars: 

279 if "=" in line: 

280 line = [x.strip() for x in line.split("=")] 

281 val = line[1].split(' ')[0] 

282 if "." in line[1].split(' ')[0]: 

283 val = float(val) 

284 else: 

285 with suppress(ValueError): 

286 val = int(val) 

287 vnames[line[0].split(" ")[-1]] = val 

288 

289 vnames['columns'] = self._col_splitter(vnames['columns']) 

290 for i in ['dens', 'vol']: 

291 vnames[i] = atleast_1d(vnames[i]) 

292 

293 return vnames 

294 

295 @staticmethod 

296 def _col_splitter(columns): 

297 if "," in columns: 

298 return columns.split(',') 

299 else: 

300 return columns.split(' ') 

301 

302 def txtread(self, obj, *args): 

303 """ 

304 Read in text file output. 

305 

306 * Not restartable 

307 

308 Parameters 

309 ---------- 

310 obj: string 

311 type of data to be read in, Variables "vars", Data "data" 

312 

313 """ 

314 if obj == "vars": 

315 self.variables = {} 

316 

317 with open(self.fname, "r") as fp: 

318 for line in fp: 

319 if search(r'[#]', line) and not search(',', line): 

320 values = array(line.strip().split()[:]) 

321 self.variables[values[1]] = float(values[3]) 

322 elif search(',', line): 

323 break 

324 # FIX from here some variables are not saved 

325 return self.variables 

326 

327 elif obj == "data": 

328 # FIX data shape and return values 

329 data = [] 

330 # read file 

331 with open(self.fname, "r") as fp: 

332 data_temp = array([line.strip().split() for line in fp if not search(r'[#]', line)]) 

333 mol_sv = set(data_temp[:, 0]) 

334 data = array(data_temp[:, 1:], dtype=float) 

335 nmax = int(self.variables["nmax"]) 

336 mol = int(self.variables["no_mol"]) 

337 stats = int(self.variables["stats"]) 

338 iterations = data[:nmax, :2] 

339 neelr = {'mol_{}'.format(i): reshape(data[:, 2], (nmax, mol), order='F')[:, i] for i in range(mol)} 

340 magnetisation = reshape(data[:, 3:6], (stats, nmax, mol, 3), order='F') 

341 position = reshape(data[:, 9:12], (stats, nmax, mol, 3), order='F') 

342 forces = reshape(data[:, 12:15], (stats, nmax, mol, 3), order='F') 

343 momentum = reshape(data[:, 15:18], (stats, nmax, mol, 3), order='F') 

344 return {"molecule names": mol_sv, 

345 "iter_time": iterations, 

346 "neel_relaxation": neelr, 

347 "magnetisation": magnetisation, 

348 "position": position, 

349 "forces": forces, 

350 "momentum": momentum} 

351 

352 

353def read_inputfile(filename, restart=None, kwds=keys.words): 

354 """Input File Reading.""" 

355 if type(filename) is str: 

356 try: 

357 with open(filename, "r") as f: 

358 opts = {k: v.strip() 

359 for k, v in [line.strip('\n').replace("\t", " ").split("[")[0].split(' ', 1) 

360 for line in f if line[0] not in ['#', '\n']]} 

361 except FileNotFoundError: 

362 c.Error("F Input File not found") 

363 else: 

364 opts = filename 

365 

366 args = {} 

367 for key, value in opts.items(): 

368 value = value.split('#')[0] if isinstance(value, str) else value 

369 try: 

370 if kwds[key][0][1] == int: 

371 args[kwds[key][0][0]] = kwds[key][0][1](float(value)) 

372 elif kwds[key][0][1] == bool: 

373 args[kwds[key][0][0]] = boolconvert(value) if not isinstance(value, bool) else value 

374 elif kwds[key][0][1] == list and not isinstance(value, list): 

375 args[kwds[key][0][0]] = value if isinstance(value, bool) else boolconvert(value) if value.lower() in ["true", "false"] else value.split() 

376 else: 

377 args[kwds[key][0][0]] = kwds[key][0][1](value) 

378 except KeyError: 378 ↛ 380line 378 didn't jump to line 380

379 c.Error("F Input parameter '{}' doesn't exist".format(key)) 

380 except ValueError: 

381 c.Error("F Input parameter '{}' (value: '{}') can't be read in".format(key, value)) 

382 

383 args = _variables(**args) 

384 args.restart = restart or (args.restart if 'restart' in opts else None) 

385 args.inputfile = filename 

386 return args 

387 

388 

389@serverwrapper("Error") 

390def inputregen(file=None, outfile=None, explanation=False): 

391 """ 

392 Generate an input file based of an existing results file. 

393 

394 Parameters 

395 ---------- 

396 file (or argv[1]): str 

397 Data file location 

398 outfile (or argv[2]): str 

399 Input file name (optional, defaults to 'Input') 

400 

401 """ 

402 if '-h' in argv: 402 ↛ 403line 402 didn't jump to line 403, because the condition on line 402 was never true

403 print(inputregen.__doc__) 

404 return 

405 c.Error("{}{}".format("W This command is considered very very alpha\n", 

406 "use for reference not as an input file")) 

407 

408 arglen = len(argv) 

409 

410 if arglen >= 2 and file is None: 

411 filename = argv[1] 

412 else: 

413 filename = file 

414 

415 if arglen == 3 and outfile is None: 

416 outfname = argv[2] 

417 elif outfile is not None: 

418 outfname = outfile 

419 else: 

420 outfname = "Input" 

421 

422 if filename in [None, False]: 

423 c.Error("M Filename not provided, generating default inputfile") 

424 arglist = [[i, j[0][2]] for i, j in keys.words.items()] 

425 else: 

426 

427 filein = read_data(*filename.rsplit(".", 1)) 

428 flags = filein.read("flags") 

429 var = filein.read("vars") 

430 arglist = [[k, j] for i, j in var.items() for k, l in keys.words.items() if i in l[0]] 

431 arglist += [[k, j] for i, j in flags.items() if i != 'run' for k, l in keys.words.items() if i in l[0]] 

432 

433 file = [] 

434 for i, j in arglist: 

435 if str(j)[0] == '[': 

436 j = str(j)[1:-1] 

437 file.append("{:21s} {}\n".format(i, j)) 

438 

439 if explanation: 439 ↛ 440line 439 didn't jump to line 440, because the condition on line 439 was never true

440 splitters = {0: '# Input files\n', 

441 3: '\n# Properties\n', 

442 10: '\n# Calculation Options\n', 

443 25: '\n# Postprocessing\n', 

444 36: '\n# Run Style Changes\n', 

445 43: '\n# Néel Relaxation Properties\n'} 

446 

447 with open(outfname, "w") as outf: 

448 for no, line in enumerate(file): 

449 try: 

450 exp = keys.explanation[no][2:].split('\n') 

451 exp2 = exp[1].split(':')[-1].split('[')[-1][:-1] 

452 exp = '{} {}'.format(exp[0].split('\n')[0].split('(')[0], '[' + exp2 if exp2[-1] == ']' else '') 

453 except IndexError: 

454 exp = keys.explanation[no][2:] 

455 if '/' in line: 

456 line = line.split('/') 

457 line = '{}./{}'.format(line[0], line[-1]) 

458 if no in splitters.keys(): 

459 outf.write(splitters[no]) 

460 outf.write("{:33s} # {}\n".format(line[:-1], exp)) 

461 else: 

462 with open(outfname, "w") as outf: 

463 for line in file: 

464 outf.write(line) 

465 

466 rmemptyfile(outfname) 

467 

468 

469@serverwrapper("Error") 

470def getvar(): 

471 """ 

472 Get variables from existing run. 

473 

474 Run with 

475 

476 mango_vars -f [Files...] -v [VARS....] 

477 """ 

478 from argparse import ArgumentParser 

479 parser = ArgumentParser(prog='MangoVars', 

480 description="MangoVars reader") 

481 parser.add_argument('-f', action='append', nargs='*', type=str, help='Files') 

482 parser.add_argument('-v', action='append', nargs='*', type=str, help='Variables') 

483 p = parser.parse_args() 

484 

485 p.f = [item for sl in p.f for item in sl] 

486 p.v = [item for sl in p.v for item in sl] 

487 

488 for f in p.f: 

489 print(f) 

490 name, save_type = f.rsplit('.', 1) 

491 

492 reader = read_data(name, save_type) 

493 variables = reader.read("vars") 

494 for i in p.v: 

495 if i in variables: 

496 print(i, variables[i]) 

497 else: 

498 # Get variable names 

499 c.Error('{}{}'.format('>W Only internal variable names currently supported\n', 

500 'Can\'t find {}'.format(i))) 

501 

502 

503def restart_gen(file=None, reps=None, outfile=None): 

504 """ 

505 Restart generator. 

506 

507 Run with 

508 

509 mango_restartregen [file] [reps] [outfile <optional>] 

510 """ 

511 if '-h' in argv: 511 ↛ 512line 511 didn't jump to line 512, because the condition on line 511 was never true

512 print(restart_gen.__doc__) 

513 return 

514 lenarg = len(argv) 

515 file = argv[1].rsplit(".", maxsplit=2) if file is None else file.rsplit(".", maxsplit=2) 

516 reps = int(argv[2] if reps is None else reps) 

517 outfile = (outfile if outfile is not None else 

518 "Run{}restart".format(file[0].split("Run")[1].split("_")[0]) if lenarg != 4 else argv[3]) 

519 

520 save_type = file[-1] 

521 f_name = file[0] 

522 directory_F = f_name.rsplit("/", 1) 

523 directory = getcwd() if len(directory_F[0]) == 1 else "/" if len(directory_F[0]) < 1 else directory_F[0] 

524 name = [] 

525 

526 for i in range(reps): 

527 name.append("{}.{:g}.{}".format(directory_F[1], i + 1, save_type)) 

528 

529 total_tosave = read_data(name[0], save_type).read(restart=True)[0].skip_iters + 1 

530 

531 with open(outfile, "w") as rgen: 

532 rgen.write("save_type {}\n".format(save_type)) 

533 rgen.write("directory {}\n".format(directory)) 

534 rgen.write("total_tosave {}\n".format(total_tosave)) 

535 for i in name: 

536 rgen.write("{} \n".format(i)) 

537 

538 rmemptyfile(outfile) 

539 

540 

541def restartfile_read(r_file): 

542 """Read restart file.""" 

543 with open(r_file) as rf: 

544 filename = [] 

545 # collectLI = [] 

546 for line in rf: 

547 line_data = line.split() 

548 if line_data == []: 

549 pass 

550 elif line_data[0] == "save_type": 

551 save_type = line_data[1] 

552 elif line_data[0] == 'directory': 

553 directory = line_data[1] 

554 elif line_data[0] == 'total_tosave': 

555 total = int(line_data[1]) 

556 else: 

557 filename.append(line_data[0]) 

558 

559 if not path.isdir(directory): 559 ↛ 563line 559 didn't jump to line 563, because the condition on line 559 was never false

560 rfile_loc = r_file.rsplit('/', 1)[0] 

561 directory = rfile_loc + "/" if rfile_loc != '' else "./" 

562 

563 return [directory + file for file in filename], save_type, directory, total 

564 

565 

566def get_restartdata(restart_data, filenames): 

567 """Collect all restart data from all files.""" 

568 collectmag = {} 

569 collectmom = {} 

570 collectpos = {} 

571 collectiters = {} 

572 RandNoState = {} 

573 

574 for name in filenames: 

575 stat = int(name.rsplit(".", 2)[-2]) - 1 

576 restart, restartflags, last_iter = restart_data.read(fname=name, restart=True) 

577 RandNoState = {**RandNoState, **restart.RandNoState} 

578 collectmag[stat] = array(restart.mag) 

579 collectmom[stat] = array(restart.mom) 

580 collectpos[stat] = array(restart.pos) 

581 collectiters[stat] = last_iter 

582 

583 restart.RandNoState = RandNoState 

584 restart.mag = collectmag 

585 restart.mom = collectmom 

586 restart.pos = collectpos 

587 

588 return restart, restartflags, collectiters 

589 

590 

591def xyz_write(xyz_mol_data, timescale, directory, run, flg, mmag, boxsize): 

592 """ 

593 Write an extended xyz file. 

594 

595 :: 

596 

597 [n, number of atoms] 

598 [timestep] [current time] [flags...] 

599 mol[0] [position x y z] [momentum x y z] [magnetisation x y z] [forces x y z] 

600 . 

601 . 

602 mol[n -1]... 

603 

604 Parameters 

605 ---------- 

606 xyz_mol_data: np.array 

607 array of data in the form [pos(xyz) mom(xyz) mag(xyz) force(xyz)] for each molecule 

608 timescale: np.array 

609 timescale list 

610 directory: string 

611 save directory 

612 run: int 

613 run number 

614 flg: class 

615 class of flags 

616 flags used - pbc, mag_sw, align 

617 mmag: float 

618 Magnetic saturation * volume 

619 boxsize: float 

620 periodic boxsize 

621 

622 """ 

623 xyz_dshape = xyz_mol_data.shape 

624 

625 filename = "{}{}Run{}{}{}.m.xyz".format(directory, "S_" if flg.suscep else "", 

626 str(run), "ALIGN" if flg.align else "", 

627 'LF' if flg.lastframe else "") 

628 

629 no_mol = xyz_dshape[-2] 

630 

631 if not path.isfile(filename) or run.endswith('nofin'): 

632 with open(filename, "wb") as xyz_w: 

633 # Put force and velocity flags if needed 

634 if flg.pbc: 

635 axis = boxsize 

636 else: 

637 axis = False 

638 

639 commentline = bytes(" momentum={} mag={} force={} boxsize={} \n".format(True, flg.mag_sw, True, axis), 'utf-8') 

640 

641 xyz_string = 'MNP {}'.format(12 * ' % 15.7e') 

642 xyz_mol_data[..., 6:9] = einsum("ijk, j -> ijk", xyz_mol_data[..., 6:9], 1 / mmag) 

643 

644 for i in [xyz_dshape[0] - 1] if flg.lastframe else range(xyz_dshape[0]): 

645 xyz_w.write(bytes("{} \ni={} time={:e}[ps]".format(no_mol, i, timescale[i]), 'utf-8')) 

646 xyz_w.write(commentline) 

647 savetxt(xyz_w, xyz_mol_data[i, :, :], fmt=xyz_string) 

648 else: 

649 c.Error(">M Complete XYZ file exists, skipping") 

650 

651 rmemptyfile(filename) 

652 

653 

654def file_write(data_array, f, timescale, directory, run, flg, mmag, boxsize): 

655 """ 

656 Write a file of energy changes or momenta changes. 

657 

658 Parameters 

659 ---------- 

660 data_array: np.array 

661 shape of (x, y, 15) for momenta or (x, y, 4) for energy 

662 f: string 

663 Magnetisation "M" or Energy "E" 

664 run: int 

665 run number 

666 

667 """ 

668 def _write(f): 

669 filename = "{}{}Run{}Conservation_{}{}.txt".format(directory, "S_" if flg.suscep else "", run, f, 

670 'LF' if flg.lastframe else "") 

671 

672 if not path.isfile(filename) or run.endswith('nofin'): 

673 itern = arange(0, timescale['F'].shape[0]) 

674 it_ti = block([[itern], [timescale['F']]]) 

675 data = block([it_ti.T, data_array]) 

676 savetxt(filename, data[-1][None, :] if flg.lastframe else data, fmt=format_string, header=titlestring) 

677 else: 

678 c.Error(">M Complete {} file exists, skipping".format("Momenta" if f == "M" else "Energy")) 

679 

680 if f == "E": 

681 titlestring = "itern. time kinetic_e pot_e1 pot_e2 total_e" 

682 format_string = "%.0d" + " % 15.7e" * 5 

683 _write("E") 

684 

685 if f == "M": 

686 titlestring = "itern. time total_m(x,y,z) mag_m(x,y,z) angular_m(x,y,z) CoM(x,y,z) CoM_vel(x,y,z)" 

687 format_string = "%.0d" + " % 15.7e" * 16 

688 _write("M") 

689 

690 if f == 'X': 

691 xyz_write(data_array, timescale['X'], directory, run, flg, mmag, boxsize) 

692 

693 

694def txt_data(moldata_dict, no_mol): 

695 """ 

696 Write output as textfile. 

697 

698 Specify "txt" 

699 

700 Parameters 

701 ---------- 

702 moldata_dict: dict 

703 dictionary of data to be written 

704 no_mol: int 

705 Number of Moelcules 

706 

707 """ 

708 newfile = (not path.isfile(moldata_dict['name'])) 

709 

710 stringfile = '' 

711 if newfile: 

712 stringfile += "# {:8s} = {:12} []\n".format("no_mol", no_mol + 1) 

713 stringfile += "# {:8s} = {:12.5g} [s]\n".format('t0', moldata_dict['vars']['t0']) 

714 stringfile += "# {:8s} = {:12.5g} [s]\n".format('dt', moldata_dict['vars']['dt']) 

715 stringfile += "# {:8s} = {:12} []\n".format('nmax', moldata_dict['vars']['nmax']) 

716 stringfile += "# {:8s} = {:12} []\n".format('skip', moldata_dict['vars']['skip']) 

717 stringfile += "# {:8s} = {:12.5g} []\n".format('stats', moldata_dict['vars']['stats']) 

718 stringfile += "\n" 

719 stringfile += "# {:8s} = {:12.5g} [K]\n".format('temp', moldata_dict['vars']['temp']) 

720 

721 str_g = no_mol * "{:12.5g} " 

722 str_e = no_mol * "{:12.5e} " 

723 str_f = no_mol * "{:12.5f} " 

724 

725 stringfile += "# {:8s} = {} [cm]\n".format('radius', str_g.format(*moldata_dict['vars']['radius'])) 

726 stringfile += "# {:8s} = {} [g/cm^3]\n".format('dens', str_g.format(*moldata_dict['vars']['dens'])) 

727 stringfile += "# {:8s} = {} [emu/cm^3]\n".format('ms', str_g.format(*moldata_dict['vars']['ms'])) 

728 stringfile += "# {:8s} = {:12.5g} [oe]\n".format('H_0', moldata_dict['vars']['H_0']) 

729 stringfile += "# {:8s} = {:12.5g} [kHz]\n".format('nu', moldata_dict['vars']['nu']) 

730 stringfile += "# {:8s} = {:12.5g} [g/cm/s]\n".format('eta', moldata_dict['vars']['eta']) 

731 stringfile += "# {:8s} = {} []\n".format('chi0', str_g.format(*moldata_dict['vars']['chi0'])) 

732 stringfile += "# {:8s} = {} [s]\n".format('tauB', str_g.format(*moldata_dict['vars']['tauB'])) 

733 stringfile += "\n" 

734 stringfile += "# {:8s} = {} [cm^3]\n".format('volume', str_e.format(*moldata_dict['vars']['vol'])) 

735 stringfile += "# {:8s} = {} []\n".format('alpha', str_f.format(*moldata_dict['vars']['alpha'])) 

736 stringfile += "# {:8s} = {} [1/oe/s]\n".format('geff', str_f.format(*moldata_dict['vars']['geff'])) 

737 stringfile += "# {:8s} = {} [oe]\n".format('c2', str_f.format(*moldata_dict['vars']['c2'])) 

738 stringfile += "\n" 

739 

740 with suppress(KeyError): 

741 stringfile += "# {:8s} = {:12.5g} [s]\n".format('tauN', moldata_dict['vars']['tauN']) 

742 stringfile += "# {:8s} = {:12.2e} [erg/cm^3]\n".format('keff', moldata_dict['vars']['keff']) 

743 stringfile += "# {:8s} = {:12.2e} [Hz]\n".format('nu_0', moldata_dict['vars']['nu_0']) 

744 

745 stringfile += "\n" 

746 stringfile += "# {:8s} = {:12.5f} []\n".format('theta', *moldata_dict['vars']['theta']) 

747 stringfile += "# {:8s} = {:12.5f} []\n".format('phi', *moldata_dict['vars']['phi']) 

748 stringfile += "# {:8s} = {:12.5f} []\n".format('boxsize', moldata_dict['vars']['boxsize']) 

749 stringfile += "\n" 

750 stringfile += "# mol, iteration, time, neel, magnetisation (3 comp.), position, forces, momentum\n" 

751 

752 for a, j in enumerate(array(moldata_dict['iter_time'])): 

753 for i in range(no_mol): 

754 mol = "mol_{}".format(i) 

755 k = int(moldata_dict['neel_relaxation'][mol][a]) if 'tauN' in moldata_dict['vars'].keys() else 0 

756 L = array_str(moldata_dict['magnetisation'][a, i])[1:-1] 

757 n = array_str(moldata_dict['position'][a, i])[1:-1] 

758 o = array_str(moldata_dict['forces'][a, i])[1:-1] 

759 p = array_str(moldata_dict['momentum'][a, i])[1:-1] 

760 

761 stringfile += "{} {} {} {} {} {} {}\n".format(mol, str(j)[1:-1], k, L, n, o, p) 

762 stringfile += '\n' 

763 

764 with open(moldata_dict['name'], "a") as w: 

765 w.write(stringfile) 

766 

767 rmemptyfile(moldata_dict['name'])