Coverage for /Users/Newville/Codes/xraylarch/larch/wxlib/cif_browser.py: 11%

559 statements  

« prev     ^ index     » next       coverage.py v7.3.2, created at 2023-11-09 10:08 -0600

1#!/usr/bin/env python 

2""" 

3Browse CIF Files, maybe run Feff 

4""" 

5 

6import os 

7import sys 

8import time 

9import copy 

10# from threading import Thread 

11import numpy as np 

12np.seterr(all='ignore') 

13 

14from functools import partial 

15import wx 

16import wx.lib.scrolledpanel as scrolled 

17import wx.lib.agw.flatnotebook as fnb 

18from wx.adv import AboutBox, AboutDialogInfo 

19from matplotlib.ticker import FuncFormatter 

20 

21from wxmplot import PlotPanel 

22from xraydb.chemparser import chemparse 

23from xraydb import atomic_number 

24 

25import larch 

26from larch import Group 

27from larch.xafs import feff8l, feff6l 

28from larch.xrd.cif2feff import cif_sites 

29from larch.utils import read_textfile, mkdir 

30from larch.utils.paths import unixpath 

31from larch.utils.strutils import fix_filename, unique_name, strict_ascii 

32from larch.site_config import user_larchdir 

33 

34from larch.wxlib import (LarchFrame, FloatSpin, EditableListBox, 

35 FloatCtrl, SetTip, get_icon, SimpleText, pack, 

36 Button, Popup, HLine, FileSave, FileOpen, Choice, 

37 Check, MenuItem, CEN, LEFT, FRAMESTYLE, 

38 Font, FONTSIZE, flatnotebook, LarchUpdaterDialog, 

39 PeriodicTablePanel, FeffResultsPanel, LarchWxApp, 

40 ExceptionPopup, set_color) 

41 

42from larch.xrd import CifStructure, get_amcsd, find_cifs, get_cif, parse_cif_file 

43 

44LEFT = wx.ALIGN_LEFT 

45CEN |= wx.ALL 

46FNB_STYLE = fnb.FNB_NO_X_BUTTON|fnb.FNB_SMART_TABS 

47FNB_STYLE |= fnb.FNB_NO_NAV_BUTTONS|fnb.FNB_NODRAG 

48 

49MAINSIZE = (1000, 650) 

50 

51class CIFFrame(wx.Frame): 

52 _about = """Larch Crystallographic Information File Browser 

53 Data from American Mineralogist Crystal Structure Database 

54 

55 Matt Newville <newville @ cars.uchicago.edu> 

56 """ 

57 

58 def __init__(self, parent=None, _larch=None, with_feff=False, 

59 with_fdmnes=False, usecif_callback=None, path_importer=None, 

60 filename=None, **kws): 

61 

62 wx.Frame.__init__(self, parent, -1, size=MAINSIZE, style=FRAMESTYLE) 

63 

64 title = "Larch American Mineralogist CIF Browser" 

65 self.with_feff = with_feff 

66 self.with_fdmnes = with_fdmnes 

67 self.usecif_callback = usecif_callback 

68 self.larch = _larch 

69 if _larch is None: 

70 self.larch = larch.Interpreter() 

71 self.larch.eval("# started CIF browser\n") 

72 

73 self.path_importer = path_importer 

74 self.cifdb = get_amcsd() 

75 self.all_minerals = self.cifdb.all_minerals() 

76 self.subframes = {} 

77 self.has_xrd1d = False 

78 self.xrd1d_thread = None 

79 self.current_cif = None 

80 self.SetTitle(title) 

81 self.SetSize(MAINSIZE) 

82 self.SetFont(Font(FONTSIZE)) 

83 

84 self.createMainPanel() 

85 self.createMenus() 

86 

87 if with_feff: 

88 self.larch.eval("if not hasattr('_sys', '_feffruns'): _sys._feffruns = {}") 

89 self.feff_folder = unixpath(os.path.join(user_larchdir, 'feff')) 

90 mkdir(self.feff_folder) 

91 self.feffruns_list = [] 

92 for fname in os.listdir(self.feff_folder): 

93 full = os.path.join(self.feff_folder, fname) 

94 if os.path.isdir(full): 

95 self.feffruns_list.append(fname) 

96 

97 self.statusbar = self.CreateStatusBar(2, style=wx.STB_DEFAULT_STYLE) 

98 self.statusbar.SetStatusWidths([-3, -1]) 

99 statusbar_fields = [" ", ""] 

100 for i in range(len(statusbar_fields)): 

101 self.statusbar.SetStatusText(statusbar_fields[i], i) 

102 self.Show() 

103 

104 def createMainPanel(self): 

105 display0 = wx.Display(0) 

106 client_area = display0.ClientArea 

107 xmin, ymin, xmax, ymax = client_area 

108 xpos = int((xmax-xmin)*0.07) + xmin 

109 ypos = int((ymax-ymin)*0.09) + ymin 

110 self.SetPosition((xpos, ypos)) 

111 

112 splitter = wx.SplitterWindow(self, style=wx.SP_LIVE_UPDATE) 

113 splitter.SetMinimumPaneSize(250) 

114 

115 leftpanel = wx.Panel(splitter) 

116 self.ciflist = EditableListBox(leftpanel, 

117 self.onShowCIF, size=(300,-1)) 

118 set_color(self.ciflist, 'list_fg', bg='list_bg') 

119 self.cif_selections = {} 

120 

121 sizer = wx.BoxSizer(wx.VERTICAL) 

122 sizer.Add(self.ciflist, 1, LEFT|wx.GROW|wx.ALL, 1) 

123 pack(leftpanel, sizer) 

124 

125 # right hand side 

126 rightpanel = scrolled.ScrolledPanel(splitter) 

127 panel = wx.Panel(rightpanel) 

128 sizer = wx.GridBagSizer(2, 2) 

129 

130 self.title = SimpleText(panel, 'Search American Mineralogical CIF Database:', 

131 size=(700, -1), style=LEFT) 

132 self.title.SetFont(Font(FONTSIZE+2)) 

133 wids = self.wids = {} 

134 

135 

136 minlab = SimpleText(panel, ' Mineral Name: ') 

137 minhint= SimpleText(panel, ' example: hem* ') 

138 wids['mineral'] = wx.TextCtrl(panel, value='', size=(250, -1), 

139 style=wx.TE_PROCESS_ENTER) 

140 wids['mineral'].Bind(wx.EVT_TEXT_ENTER, self.onSearch) 

141 

142 authlab = SimpleText(panel, ' Author Name: ') 

143 wids['author'] = wx.TextCtrl(panel, value='', size=(250, -1), 

144 style=wx.TE_PROCESS_ENTER) 

145 wids['author'].Bind(wx.EVT_TEXT_ENTER, self.onSearch) 

146 

147 journlab = SimpleText(panel, ' Journal Name: ') 

148 wids['journal'] = wx.TextCtrl(panel, value='', size=(250, -1), 

149 style=wx.TE_PROCESS_ENTER) 

150 wids['journal'].Bind(wx.EVT_TEXT_ENTER, self.onSearch) 

151 

152 elemlab = SimpleText(panel, ' Include Elements: ') 

153 elemhint= SimpleText(panel, ' example: O, Fe, Si ') 

154 

155 wids['contains_elements'] = wx.TextCtrl(panel, value='', size=(250, -1), 

156 style=wx.TE_PROCESS_ENTER) 

157 wids['contains_elements'].Bind(wx.EVT_TEXT_ENTER, self.onSearch) 

158 

159 exelemlab = SimpleText(panel, ' Exclude Elements: ') 

160 wids['excludes_elements'] = wx.TextCtrl(panel, value='', size=(250, -1), 

161 style=wx.TE_PROCESS_ENTER) 

162 wids['excludes_elements'].Bind(wx.EVT_TEXT_ENTER, self.onSearch) 

163 

164 wids['excludes_elements'].Enable() 

165 wids['strict_contains'] = Check(panel, default=False, 

166 label='Include only the elements listed', 

167 action=self.onStrict) 

168 

169 wids['full_occupancy'] = Check(panel, default=False, 

170 label='Only Structures with Full Occupancy') 

171 

172 wids['search'] = Button(panel, 'Search for CIFs', action=self.onSearch) 

173 

174 

175 

176 ir = 0 

177 sizer.Add(self.title, (0, 0), (1, 6), LEFT, 2) 

178 

179 ir += 1 

180 sizer.Add(HLine(panel, size=(650, 2)), (ir, 0), (1, 6), LEFT, 3) 

181 

182 ir += 1 

183 sizer.Add(minlab, (ir, 0), (1, 1), LEFT, 3) 

184 sizer.Add(wids['mineral'], (ir, 1), (1, 3), LEFT, 3) 

185 sizer.Add(minhint, (ir, 4), (1, 2), LEFT, 3) 

186 ir += 1 

187 sizer.Add(authlab, (ir, 0), (1, 1), LEFT, 3) 

188 sizer.Add(wids['author'], (ir, 1), (1, 3), LEFT, 3) 

189 

190 ir += 1 

191 sizer.Add(journlab, (ir, 0), (1, 1), LEFT, 3) 

192 sizer.Add(wids['journal'], (ir, 1), (1, 3), LEFT, 3) 

193 

194 ir += 1 

195 sizer.Add(elemlab, (ir, 0), (1, 1), LEFT, 3) 

196 sizer.Add(wids['contains_elements'], (ir, 1), (1, 3), LEFT, 3) 

197 sizer.Add(elemhint, (ir, 4), (1, 3), LEFT, 2) 

198 

199 ir += 1 

200 sizer.Add(exelemlab, (ir, 0), (1, 1), LEFT, 3) 

201 sizer.Add(wids['excludes_elements'], (ir, 1), (1, 3), LEFT, 3) 

202 

203 ir += 1 

204 sizer.Add(wids['search'], (ir, 0), (1, 1), LEFT, 3) 

205 sizer.Add(wids['strict_contains'], (ir, 1), (1, 4), LEFT, 3) 

206 

207 ir += 1 

208 sizer.Add(wids['full_occupancy'], (ir, 1), (1, 4), LEFT, 3) 

209 

210 # 

211 if self.with_feff: 

212 wids['feff_runfolder'] = wx.TextCtrl(panel, value='calc1', size=(250, -1)) 

213 wids['feff_runbutton'] = Button(panel, ' Run Feff ', action=self.onRunFeff) 

214 wids['feff_runbutton'].Disable() 

215 wids['feff_without_h'] = Check(panel, default=True, label='Remove H atoms', 

216 action=self.onGetFeff) 

217 

218 

219 wids['feff_central_atom'] = Choice(panel, choices=['<empty>'], size=(80, -1), 

220 action=self.onFeffCentralAtom) 

221 wids['feff_edge'] = Choice(panel, choices=['K', 'L3', 'L2', 'L1', 

222 'M5', 'M4'], 

223 size=(80, -1), 

224 action=self.onGetFeff) 

225 

226 wids['feffvers'] = Choice(panel, choices=['6', '8'], default=1, 

227 size=(80, -1), 

228 action=self.onGetFeff) 

229 wids['feff_site'] = Choice(panel, choices=['1', '2', '3', '4'], 

230 size=(80, -1), 

231 action=self.onGetFeff) 

232 wids['feff_cluster_size'] = FloatSpin(panel, value=7.0, digits=2, 

233 increment=0.1, max_val=10, 

234 action=self.onGetFeff) 

235 wids['feff_central_atom'].Disable() 

236 wids['feff_edge'].Disable() 

237 wids['feff_cluster_size'].Disable() 

238 

239 ir += 1 

240 sizer.Add(HLine(panel, size=(650, 2)), (ir, 0), (1, 6), LEFT, 3) 

241 

242 ir += 1 

243 

244 sizer.Add(SimpleText(panel, ' Absorbing Atom: '), (ir, 0), (1, 1), LEFT, 3) 

245 sizer.Add(wids['feff_central_atom'], (ir, 1), (1, 1), LEFT, 3) 

246 sizer.Add(SimpleText(panel, ' Crystal Site: '), (ir, 2), (1, 1), LEFT, 3) 

247 sizer.Add(wids['feff_site'], (ir, 3), (1, 1), LEFT, 3) 

248 sizer.Add(SimpleText(panel, ' Edge: '), (ir, 4), (1, 1), LEFT, 3) 

249 sizer.Add(wids['feff_edge'], (ir, 5), (1, 1), LEFT, 3) 

250 

251 ir += 1 

252 sizer.Add(SimpleText(panel, ' Cluster Size (\u212B): '), (ir, 0), (1, 1), LEFT, 3) 

253 sizer.Add(wids['feff_cluster_size'], (ir, 1), (1, 1), LEFT, 3) 

254 sizer.Add(SimpleText(panel, ' Feff Version:'), (ir, 2), (1, 1), LEFT, 3) 

255 sizer.Add(wids['feffvers'], (ir, 3), (1, 1), LEFT, 3) 

256 sizer.Add(wids['feff_without_h'], (ir, 4), (1, 2), LEFT, 3) 

257 

258 ir += 1 

259 sizer.Add(SimpleText(panel, ' Feff Folder: '), (ir, 0), (1, 1), LEFT, 3) 

260 sizer.Add(wids['feff_runfolder'], (ir, 1), (1, 4), LEFT, 3) 

261 sizer.Add(wids['feff_runbutton'], (ir, 5), (1, 1), LEFT, 3) 

262 

263 if self.usecif_callback is not None: 

264 wids['cif_use_button'] = Button(panel, ' Use This CIF', action=self.onUseCIF) 

265 wids['cif_use_button'].Disable() 

266 

267 ir += 1 

268 sizer.Add(wids['cif_use_button'], (ir, 5), (1, 1), LEFT, 3) 

269 

270 

271 ir += 1 

272 sizer.Add(HLine(panel, size=(650, 2)), (ir, 0), (1, 6), LEFT, 3) 

273 

274 pack(panel, sizer) 

275 

276 self.nb = flatnotebook(rightpanel, {}, on_change=self.onNBChanged) 

277 

278 

279 def _swallow_plot_messages(s, panel=0): 

280 pass 

281 

282 self.plotpanel = PlotPanel(rightpanel, messenger=_swallow_plot_messages) 

283 try: 

284 plotopts = self.larch.symtable._sys.wx.plotopts 

285 self.plotpanel.conf.set_theme(plotopts['theme']) 

286 self.plotpanel.conf.enable_grid(plotopts['show_grid']) 

287 except: 

288 pass 

289 

290 self.plotpanel.SetMinSize((250, 250)) 

291 self.plotpanel.SetMaxSize((675, 400)) 

292 self.plotpanel.onPanelExposed = self.showXRD1D 

293 

294 cif_panel = wx.Panel(rightpanel) 

295 wids['cif_text'] = wx.TextCtrl(cif_panel, value='<CIF TEXT>', 

296 style=wx.TE_MULTILINE|wx.TE_READONLY, 

297 size=(700, 450)) 

298 wids['cif_text'].SetFont(Font(FONTSIZE+1)) 

299 cif_sizer = wx.BoxSizer(wx.VERTICAL) 

300 cif_sizer.Add(wids['cif_text'], 0, LEFT, 1) 

301 pack(cif_panel, cif_sizer) 

302 

303 

304 self.nbpages = [] 

305 for label, page in (('CIF Text', cif_panel), 

306 ('1-D XRD Pattern', self.plotpanel), 

307 ): 

308 self.nb.AddPage(page, label, True) 

309 self.nbpages.append((label, page)) 

310 

311 if self.with_feff: 

312 self.feffresults = FeffResultsPanel(rightpanel, 

313 path_importer=self.path_importer, 

314 _larch=self.larch) 

315 

316 feffinp_panel = wx.Panel(rightpanel) 

317 wids['feff_text'] = wx.TextCtrl(feffinp_panel, 

318 value='<Feff Input Text>', 

319 style=wx.TE_MULTILINE, 

320 size=(700, 450)) 

321 wids['feff_text'].CanCopy() 

322 

323 feffinp_panel.onPanelExposed = self.onGetFeff 

324 wids['feff_text'].SetFont(Font(FONTSIZE+1)) 

325 feff_sizer = wx.BoxSizer(wx.VERTICAL) 

326 feff_sizer.Add(wids['feff_text'], 0, LEFT, 1) 

327 pack(feffinp_panel, feff_sizer) 

328 

329 feffout_panel = wx.Panel(rightpanel) 

330 wids['feffout_text'] = wx.TextCtrl(feffout_panel, 

331 value='<Feff Output>', 

332 style=wx.TE_MULTILINE, 

333 size=(700, 450)) 

334 wids['feffout_text'].CanCopy() 

335 wids['feffout_text'].SetFont(Font(FONTSIZE+1)) 

336 feffout_sizer = wx.BoxSizer(wx.VERTICAL) 

337 feffout_sizer.Add(wids['feffout_text'], 0, LEFT, 1) 

338 pack(feffout_panel, feffout_sizer) 

339 

340 for label, page in (('Feff Input Text', feffinp_panel), 

341 ('Feff Output Text', feffout_panel), 

342 ('Feff Results', self.feffresults), 

343 ): 

344 self.nb.AddPage(page, label, True) 

345 self.nbpages.append((label, page)) 

346 self.nb.SetSelection(0) 

347 

348 r_sizer = wx.BoxSizer(wx.VERTICAL) 

349 r_sizer.Add(panel, 0, LEFT|wx.GROW|wx.ALL) 

350 r_sizer.Add(self.nb, 1, LEFT|wx.GROW, 2) 

351 pack(rightpanel, r_sizer) 

352 rightpanel.SetupScrolling() 

353 splitter.SplitVertically(leftpanel, rightpanel, 1) 

354 

355 def get_nbpage(self, name): 

356 "get nb page by name" 

357 name = name.lower() 

358 for i, dat in enumerate(self.nbpages): 

359 label, page = dat 

360 if name in label.lower(): 

361 return i, page 

362 return (0, self.nbpages[0][1]) 

363 

364 def onStrict(self, event=None): 

365 strict = self.wids['strict_contains'].IsChecked() 

366 self.wids['excludes_elements'].Enable(not strict) 

367 

368 def onSearch(self, event=None): 

369 mineral_name = self.wids['mineral'].GetValue().strip() 

370 if len(mineral_name) < 1: 

371 mineral_name = None 

372 author_name = self.wids['author'].GetValue().strip() 

373 if len(author_name) < 1: 

374 author_name = None 

375 journal_name = self.wids['journal'].GetValue().strip() 

376 if len(journal_name) < 1: 

377 journal_name = None 

378 contains_elements = self.wids['contains_elements'].GetValue().strip() 

379 if len(contains_elements) < 1: 

380 contains_elements = None 

381 else: 

382 contains_elements = [a.strip().title() for a in contains_elements.split(',')] 

383 excludes_elements = self.wids['excludes_elements'].GetValue().strip() 

384 if len(excludes_elements) < 1: 

385 excludes_elements = None 

386 else: 

387 excludes_elements = [a.strip().title() for a in excludes_elements.split(',')] 

388 strict_contains = self.wids['strict_contains'].IsChecked() 

389 full_occupancy = self.wids['full_occupancy'].IsChecked() 

390 all_cifs = find_cifs(mineral_name=mineral_name, 

391 journal_name=journal_name, 

392 author_name=author_name, 

393 contains_elements=contains_elements, 

394 excludes_elements=excludes_elements, 

395 strict_contains=strict_contains, 

396 full_occupancy=full_occupancy) 

397 if len(all_cifs) == 0: 

398 all_cifs = find_cifs(mineral_name=mineral_name + '*', 

399 journal_name=journal_name, 

400 author_name=author_name, 

401 contains_elements=contains_elements, 

402 excludes_elements=excludes_elements, 

403 strict_contains=strict_contains, 

404 full_occupancy=full_occupancy) 

405 self.cif_selections = {} 

406 self.ciflist.Clear() 

407 for cif in all_cifs: 

408 try: 

409 label = cif.formula.replace(' ', '') 

410 mineral = cif.get_mineralname() 

411 year = cif.publication.year 

412 journal= cif.publication.journalname 

413 label = f'{label}: {mineral}, {year} {journal}' 

414 except: 

415 label = None 

416 if label is not None: 

417 self.cif_selections[label] = cif.ams_id 

418 self.ciflist.Append(label) 

419 

420 def onShowCIF(self, event=None, cif_id=None): 

421 if cif_id is not None: 

422 cif = get_cif(cif_id) 

423 self.cif_label = '%d' % cif_id 

424 elif event is not None: 

425 self.cif_label = event.GetString() 

426 cif = get_cif(self.cif_selections[self.cif_label]) 

427 self.current_cif = cif 

428 self.has_xrd1d = False 

429 self.wids['cif_text'].SetValue(cif.ciftext) 

430 

431 if self.with_feff: 

432 elems = chemparse(cif.formula.replace(' ', '')) 

433 self.wids['feff_central_atom'].Enable() 

434 self.wids['feff_edge'].Enable() 

435 self.wids['feff_cluster_size'].Enable() 

436 

437 self.wids['feff_central_atom'].Clear() 

438 self.wids['feff_central_atom'].AppendItems(list(elems.keys())) 

439 self.wids['feff_central_atom'].Select(0) 

440 

441 el0 = list(elems.keys())[0] 

442 edge_val = 'K' if atomic_number(el0) < 60 else 'L3' 

443 self.wids['feff_edge'].SetStringSelection(edge_val) 

444 

445 sites = cif_sites(cif.ciftext, absorber=el0) 

446 try: 

447 sites = ['%d' % (i+1) for i in range(len(sites))] 

448 except: 

449 title = "Could not make sense of atomic sites" 

450 message = [f"Elements: {list(elems.keys())}", 

451 f"Sites: {sites}"] 

452 ExceptionPopup(self, title, message) 

453 

454 self.wids['feff_site'].Clear() 

455 self.wids['feff_site'].AppendItems(sites) 

456 self.wids['feff_site'].Select(0) 

457 

458 if self.usecif_callback is not None: 

459 self.wids['cif_use_button'].Enable() 

460 

461 i, p = self.get_nbpage('CIF Text') 

462 self.nb.SetSelection(i) 

463 

464 def onUseCIF(self, event=None): 

465 if self.usecif_callback is not None: 

466 self.usecif_callback(cif=self.current_cif) 

467 

468 

469 def onFeffCentralAtom(self, event=None): 

470 cif = self.current_cif 

471 if cif is None: 

472 return 

473 catom = event.GetString() 

474 try: 

475 sites = cif_sites(cif.ciftext, absorber=catom) 

476 sites = ['%d' % (i+1) for i in range(len(sites))] 

477 self.wids['feff_site'].Clear() 

478 self.wids['feff_site'].AppendItems(sites) 

479 self.wids['feff_site'].Select(0) 

480 except: 

481 self.write_message(f"could not get sites for central atom '{catom}'") 

482 title = f"Could not get sites for central atom '{catom}'" 

483 message = [] 

484 ExceptionPopup(self, title, message) 

485 

486 edge_val = 'K' if atomic_number(catom) < 60 else 'L3' 

487 self.wids['feff_edge'].SetStringSelection(edge_val) 

488 self.onGetFeff() 

489 

490 def onGetFeff(self, event=None): 

491 cif = self.current_cif 

492 if cif is None or not self.with_feff: 

493 return 

494 

495 edge = self.wids['feff_edge'].GetStringSelection() 

496 version8 = '8' == self.wids['feffvers'].GetStringSelection() 

497 catom = self.wids['feff_central_atom'].GetStringSelection() 

498 asite = int(self.wids['feff_site'].GetStringSelection()) 

499 csize = self.wids['feff_cluster_size'].GetValue() 

500 with_h = not self.wids['feff_without_h'].IsChecked() 

501 mineral = cif.get_mineralname() 

502 folder = f'{catom:s}{asite:d}_{edge:s}_{mineral}_cif{cif.ams_id:d}' 

503 folder = unique_name(fix_filename(folder), self.feffruns_list) 

504 

505 fefftext = cif.get_feffinp(catom, edge=edge, cluster_size=csize, 

506 absorber_site=asite, version8=version8, 

507 with_h=with_h) 

508 

509 self.wids['feff_runfolder'].SetValue(folder) 

510 self.wids['feff_text'].SetValue(fefftext) 

511 self.wids['feff_runbutton'].Enable() 

512 i, p = self.get_nbpage('Feff Input') 

513 self.nb.SetSelection(i) 

514 

515 def onRunFeff(self, event=None): 

516 fefftext = self.wids['feff_text'].GetValue() 

517 if len(fefftext) < 100 or 'ATOMS' not in fefftext or not self.with_feff: 

518 return 

519 

520 ciftext = self.wids['cif_text'].GetValue() 

521 cif = self.current_cif 

522 cif_fname = None 

523 if cif is not None and len(ciftext) > 100: 

524 mineral = cif.get_mineralname() 

525 cif_fname = f'{mineral}_cif{cif.ams_id:d}.cif' 

526 

527 # cc = self.current_cif 

528 # edge = self.wids['feff_edge'].GetStringSelection() 

529 # catom = self.wids['feff_central_atom'].GetStringSelection() 

530 # asite = int(self.wids['feff_site'].GetStringSelection()) 

531 # mineral = cc.get_mineralname() 

532 # folder = f'{catom:s}{asite:d}_{edge:s}_{mineral}_cif{cc.ams_id:d}' 

533 # folder = unixpath(os.path.join(self.feff_folder, folder)) 

534 version8 = '8' == self.wids['feffvers'].GetStringSelection() 

535 

536 fname = self.wids['feff_runfolder'].GetValue() 

537 fname = unique_name(fix_filename(fname), self.feffruns_list) 

538 self.feffruns_list.append(fname) 

539 self.folder = folder = unixpath(os.path.join(self.feff_folder, fname)) 

540 mkdir(self.folder) 

541 ix, p = self.get_nbpage('Feff Output') 

542 self.nb.SetSelection(ix) 

543 

544 out = self.wids['feffout_text'] 

545 out.Clear() 

546 out.SetInsertionPoint(0) 

547 out.WriteText(f'########\n###\n# Run Feff in folder: {folder:s}\n') 

548 out.SetInsertionPoint(out.GetLastPosition()) 

549 out.WriteText('###\n########\n') 

550 out.SetInsertionPoint(out.GetLastPosition()) 

551 

552 fname = unixpath(os.path.join(folder, 'feff.inp')) 

553 with open(fname, 'w', encoding=sys.getdefaultencoding()) as fh: 

554 fh.write(strict_ascii(fefftext)) 

555 

556 if cif_fname is not None: 

557 cname = unixpath(os.path.join(folder, fix_filename(cif_fname))) 

558 with open(cname, 'w', encoding=sys.getdefaultencoding()) as fh: 

559 fh.write(strict_ascii(ciftext)) 

560 wx.CallAfter(self.run_feff, folder, version8=version8) 

561 

562 def run_feff(self, folder=None, version8=True): 

563 print("RUN FEFF ", folder) 

564 _, dname = os.path.split(folder) 

565 prog, cmd = feff8l, 'feff8l' 

566 if not version8: 

567 prog, cmd = feff6l, 'feff6l' 

568 command = f"{cmd:s}(folder='{folder:s}')" 

569 self.larch.eval(f"## running Feff as:\n# {command:s}\n##\n") 

570 

571 prog(folder=folder, message_writer=self.feff_output) 

572 self.larch.eval("## gathering results:\n") 

573 self.larch.eval(f"_sys._feffruns['{dname:s}'] = get_feff_pathinfo('{folder:s}')") 

574 this_feffrun = self.larch.symtable._sys._feffruns[f'{dname:s}'] 

575 self.feffresults.set_feffresult(this_feffrun) 

576 ix, p = self.get_nbpage('Feff Results') 

577 self.nb.SetSelection(ix) 

578 

579 # clean up unused, intermediate Feff files 

580 for fname in os.listdir(folder): 

581 if (fname.endswith('.json') or fname.endswith('.pad') or 

582 fname.endswith('.bin') or fname.startswith('log') or 

583 fname in ('chi.dat', 'xmu.dat', 'misc.dat')): 

584 os.unlink(unixpath(os.path.join(folder, fname))) 

585 

586 def feff_output(self, text): 

587 out = self.wids['feffout_text'] 

588 ix, p = self.get_nbpage('Feff Output') 

589 self.nb.SetSelection(ix) 

590 pos0 = out.GetLastPosition() 

591 if not text.endswith('\n'): 

592 text = '%s\n' % text 

593 out.WriteText(text) 

594 out.SetInsertionPoint(out.GetLastPosition()) 

595 out.Update() 

596 out.Refresh() 

597 

598 def onExportFeff(self, event=None): 

599 if self.current_cif is None: 

600 return 

601 fefftext = self.wids['feff_text'].GetValue() 

602 if len(fefftext) < 20: 

603 return 

604 cc = self.current_cif 

605 minname = cc.get_mineralname() 

606 fname = f'{minname}_cif{cc.ams_id:d}_feff.inp' 

607 wildcard = 'Feff Inut files (*.inp)|*.inp|All files (*.*)|*.*' 

608 path = FileSave(self, message='Save Feff File', 

609 wildcard=wildcard, 

610 default_file=fname) 

611 if path is not None: 

612 with open(path, 'w', encoding=sys.getdefaultencoding()) as fh: 

613 fh.write(fefftext) 

614 self.write_message("Wrote Feff file %s" % path, 0) 

615 

616 

617 def onExportCIF(self, event=None): 

618 if self.current_cif is None: 

619 return 

620 cc = self.current_cif 

621 minname = cc.get_mineralname() 

622 fname = f'{minname}_cif{cc.ams_id:d}.cif' 

623 wildcard = 'CIF files (*.cif)|*.cif|All files (*.*)|*.*' 

624 path = FileSave(self, message='Save CIF File', 

625 wildcard=wildcard, 

626 default_file=fname) 

627 if path is not None: 

628 with open(path, 'w', encoding=sys.getdefaultencoding()) as fh: 

629 fh.write(cc.ciftext) 

630 self.write_message("Wrote CIF file %s" % path, 0) 

631 

632 def onImportCIF(self, event=None): 

633 wildcard = 'CIF files (*.cif)|*.cif|All files (*.*)|*.*' 

634 path = FileOpen(self, message='Open CIF File', 

635 wildcard=wildcard, default_file='My.cif') 

636 

637 if path is not None: 

638 try: 

639 cif_data = parse_cif_file(path) 

640 except: 

641 title = f"Cannot parse CIF file '{path}'" 

642 message = [f"Error reading CIF File: {path}"] 

643 ExceptionPopup(self, title, message) 

644 return 

645 

646 try: 

647 cif_id = self.cifdb.add_ciffile(path) 

648 except: 

649 title = f"Cannot add CIF from '{path}' to CIF database" 

650 message = [f"Error adding CIF File to database: {path}"] 

651 ExceptionPopup(self, title, message) 

652 return 

653 

654 try: 

655 self.onShowCIF(cif_id=cif_id) 

656 except: 

657 title = f"Cannot show CIF from '{path}'" 

658 message = [f"Error displaying CIF File: {path}"] 

659 ExceptionPopup(self, title, message) 

660 

661 def onImportFeff(self, event=None): 

662 if not self.with_feff: 

663 return 

664 wildcard = 'Feff input files (*.inp)|*.inp|All files (*.*)|*.*' 

665 path = FileOpen(self, message='Open Feff Input File', 

666 wildcard=wildcard, default_file='feff.inp') 

667 if path is not None: 

668 fefftext = None 

669 _, fname = os.path.split(path) 

670 fname = fname.replace('.inp', '_run') 

671 fname = unique_name(fix_filename(fname), self.feffruns_list) 

672 fefftext = read_textfile(path) 

673 if fefftext is not None: 

674 self.wids['feff_text'].SetValue(fefftext) 

675 self.wids['feff_runfolder'].SetValue(fname) 

676 self.wids['feff_runbutton'].Enable() 

677 i, p = self.get_nbpage('Feff Input') 

678 self.nb.SetSelection(i) 

679 

680 def onFeffFolder(self, eventa=None): 

681 "prompt for Feff Folder" 

682 dlg = wx.DirDialog(self, 'Select Main Folder for Feff Calculations', 

683 style=wx.DD_DEFAULT_STYLE|wx.DD_CHANGE_DIR) 

684 

685 dlg.SetPath(self.feff_folder) 

686 if dlg.ShowModal() == wx.ID_CANCEL: 

687 return None 

688 self.feff_folder = os.path.abspath(dlg.GetPath()) 

689 mkdir(self.feff_folder) 

690 

691 def onNBChanged(self, event=None): 

692 callback = getattr(self.nb.GetCurrentPage(), 'onPanelExposed', None) 

693 if callable(callback): 

694 callback() 

695 

696 def showXRD1D(self, event=None): 

697 if self.has_xrd1d or self.current_cif is None: 

698 return 

699 

700 def display_xrd1d(): 

701 t0 = time.time() 

702 sfact = self.current_cif.get_structure_factors() 

703 try: 

704 self.cifdb.set_hkls(self.current_cif.ams_id, sfact.hkls) 

705 except: 

706 pass 

707 

708 max_ = sfact.intensity.max() 

709 mask = np.where(sfact.intensity>max_/10.0)[0] 

710 qval = sfact.q[mask] 

711 ival = sfact.intensity[mask] 

712 ival = ival/(1.0*ival.max()) 

713 

714 def qd_formatter(q, pos): 

715 qval = float(q) 

716 dval = '\n[%.2f]' % (2*np.pi/max(qval, 1.e-6)) 

717 return r"%.2f%s" % (qval, dval) 

718 

719 qd_label = r'$Q\rm\,(\AA^{-1}) \,\> [d \rm\,(\AA)]$' 

720 title = self.cif_label + '\n' + '(cif %d)' % (self.current_cif.ams_id) 

721 ppan = self.plotpanel 

722 ppan.plot(qval, ival, linewidth=0, marker='o', markersize=2, 

723 xlabel=qd_label, ylabel='Relative Intensity', 

724 title=title, titlefontsize=8, delay_draw=True) 

725 

726 ppan.axes.bar(qval, ival, 0.1, color='blue') 

727 ppan.axes.xaxis.set_major_formatter(FuncFormatter(qd_formatter)) 

728 ppan.canvas.draw() 

729 self.has_xrd1d = True 

730 

731 display_xrd1d() 

732# self.xrd1d_thread = Thread(target=display_xrd1d) 

733# self.xrd1d_thread.start() 

734# time.sleep(0.25) 

735# self.xrd1d_thread.join() 

736 

737 

738 def onSelAll(self, event=None): 

739 self.controller.filelist.select_all() 

740 

741 def onSelNone(self, event=None): 

742 self.controller.filelist.select_none() 

743 

744 def write_message(self, msg, panel=0): 

745 """write a message to the Status Bar""" 

746 self.statusbar.SetStatusText(msg, panel) 

747 

748 def createMenus(self): 

749 # ppnl = self.plotpanel 

750 self.menubar = wx.MenuBar() 

751 fmenu = wx.Menu() 

752 group_menu = wx.Menu() 

753 data_menu = wx.Menu() 

754 ppeak_menu = wx.Menu() 

755 m = {} 

756 

757 MenuItem(self, fmenu, "&Open CIF File\tCtrl+O", 

758 "Open CIF File", self.onImportCIF) 

759 

760 MenuItem(self, fmenu, "&Save CIF File\tCtrl+S", 

761 "Save CIF File", self.onExportCIF) 

762 

763 MenuItem(self, fmenu, "Open Feff Input File", 

764 "Open Feff input File", self.onImportFeff) 

765 

766 MenuItem(self, fmenu, "Save &Feff Inp File\tCtrl+F", 

767 "Save Feff6 File", self.onExportFeff) 

768 

769 fmenu.AppendSeparator() 

770 MenuItem(self, fmenu, "Select Main Feff Folder", 

771 "Select Main Folder for running Feff", 

772 self.onFeffFolder) 

773 fmenu.AppendSeparator() 

774 MenuItem(self, fmenu, "Quit", "Exit", self.onClose) 

775 

776 self.menubar.Append(fmenu, "&File") 

777 

778 self.SetMenuBar(self.menubar) 

779 self.Bind(wx.EVT_CLOSE, self.onClose) 

780 

781 def onClose(self, event=None): 

782 self.Destroy() 

783 

784 

785class CIFViewer(LarchWxApp): 

786 def __init__(self, filename=None, version_info=None, **kws): 

787 self.filename = filename 

788 LarchWxApp.__init__(self, version_info=version_info, **kws) 

789 

790 def createApp(self): 

791 frame = CIFFrame(filename=self.filename, 

792 version_info=self.version_info) 

793 self.SetTopWindow(frame) 

794 return True 

795 

796def cif_viewer(**kws): 

797 CIFViewer(**kws) 

798 

799if __name__ == '__main__': 

800 CIFViewer().MainLoop()