Coverage for larch/wxlib/xrfdisplay_fitpeaks.py: 8%

1096 statements  

« prev     ^ index     » next       coverage.py v7.6.0, created at 2024-10-16 21:04 +0000

1#!/usr/bin/env python 

2""" 

3fitting GUI for XRF display 

4""" 

5import sys 

6import time 

7import copy 

8from functools import partial 

9from threading import Thread 

10from pathlib import Path 

11import json 

12import numpy as np 

13import wx 

14import wx.lib.agw.pycollapsiblepane as CP 

15import wx.lib.scrolledpanel as scrolled 

16import wx.dataview as dv 

17DVSTYLE = dv.DV_SINGLE|dv.DV_VERT_RULES|dv.DV_ROW_LINES 

18 

19from lmfit import Parameter, Minimizer 

20 

21from . import (SimpleText, FloatCtrl, FloatSpin, Choice, Font, pack, 

22 Button, Check, HLine, GridPanel, RowPanel, CEN, LEFT, 

23 RIGHT, FileSave, GUIColors, FRAMESTYLE, BitmapButton, 

24 SetTip, GridPanel, Popup, FloatSpinWithPin, get_icon, 

25 fix_filename, flatnotebook, PeriodicTablePanel, 

26 FONTSIZE, FONTSIZE_FW) 

27 

28from xraydb import (material_mu, xray_edge, materials, add_material, 

29 atomic_number, atomic_symbol, xray_line) 

30# from .notebooks import flatnotebook 

31# from .periodictable import PeriodicTablePanel 

32from .parameter import ParameterPanel 

33 

34from larch import Group 

35 

36from ..math import peak_indices 

37from ..xrf import xrf_background, MCA, FanoFactors 

38from ..utils import json_dump, json_load, gformat 

39from ..utils.jsonutils import encode4js, decode4js 

40from ..utils.physical_constants import E_MASS 

41from ..site_config import user_larchdir 

42from .xrfdisplay_utils import (XRFGROUP, mcaname, XRFRESULTS_GROUP, 

43 MAKE_XRFRESULTS_GROUP) 

44 

45def read_filterdata(flist, _larch): 

46 """ read filters data""" 

47 materials = _larch.symtable.get_symbol('_xray._materials') 

48 out = {} 

49 out['None'] = ('', 0) 

50 for name in flist: 

51 if name in materials: 

52 out[name] = materials[name] 

53 return out 

54 

55def VarChoice(p, default=0, size=(90, -1)): 

56 if default in (False, 'False', 'Fix', 'No'): 

57 default = 0 

58 else: 

59 default = 1 

60 return Choice(p, choices=['Fix', 'Vary'], 

61 size=size, default=default) 

62 

63NFILTERS = 4 

64MIN_CORREL = 0.10 

65 

66tooltips = {'ptable': 'Select Elements to include in model', 

67 'cen': 'centroid of peak', 

68 'step': 'size of step extending to low energy side of peak, fraction of peak height', 

69 'gamma': 'gamma (lorentzian-like weight) of Voigt function', 

70 'tail': 'intensity of tail function at low energy side of peak', 

71 'beta': 'width of tail function at low energy side of peak', 

72 'sigma': 'scale sigma from Energy/Noise by this amount', 

73 } 

74 

75CompositionUnits = ('ng/mm^2', 'wt %', 'ppm') 

76 

77Detector_Materials = ['Si', 'Ge'] 

78EFano_Text = 'Peak Widths: sigma = sqrt(E_Fano * Energy + Noise**2) ' 

79Geom_Text = 'Angles in degrees: 90=normal to surface, 0=grazing surface' 

80Energy_Text = 'All energies in keV' 

81 

82 

83FitTols = ['1.e-2', '3.e-3', '1.e-3', '3.e-4', '1.e-4', '3.e-5', '1.e-5', 

84 '3.e-6', '1.e-6', '3.e-7', '1.e-7'] 

85FitSteps = ['1.e-2', '3.e-3', '1.e-3', '3.e-4', '1.e-4', '3.e-5', '1.e-5', 

86 '3.e-6', '1.e-6', '3.e-7', '1.e-7'] 

87 

88FitMaxNFevs = ['200', '500', '1000', '1500', '2000', '3000', '5000', '10000'] 

89 

90xrfmod_setup = """### XRF Model: {mca_label:s} @ {datetime:s} 

91# mca data group for fit: 

92{XRFGROUP}.workmca = {mcagroup} 

93 

94# setup XRF Model: 

95_xrfmodel = xrf_model(xray_energy={en_xray:.2f}, count_time={count_time:.5f}, 

96 energy_min={en_min:.2f}, energy_max={en_max:.2f}) 

97 

98_xrfmodel.set_detector(thickness={det_thk:.5f}, material='{det_mat:s}', 

99 cal_offset={cal_offset:.5f}, cal_slope={cal_slope:.5f}, 

100 vary_cal_offset={cal_vary!r}, vary_cal_slope={cal_vary!r}, 

101 peak_step={peak_step:.5f}, vary_peak_step={peak_step_vary:s}, 

102 peak_tail={peak_tail:.5f}, vary_peak_tail={peak_tail_vary:s}, 

103 peak_beta={peak_beta:.5f}, vary_peak_beta={peak_beta_vary:s}, 

104 peak_gamma={peak_gamma:.5f}, vary_peak_gamma={peak_gamma_vary:s}, 

105 noise={det_noise:.5f}, vary_noise={det_noise_vary:s})""" 

106 

107xrfmod_scattpeak = """ 

108# add scatter peak 

109_xrfmodel.add_scatter_peak(name='{peakname:s}', center={_cen:.2f}, 

110 amplitude=1e5, step={_step:.5f}, tail={_tail:.5f}, beta={_beta:.5f}, 

111 sigmax={_sigma:.5f}, vary_center={vcen:s}, vary_step={vstep:s}, 

112 vary_tail={vtail:s}, vary_beta={vbeta:s}, vary_sigmax={vsigma:s})""" 

113 

114xrfmod_fitscript = """ 

115# run XRF fit, save results 

116_xrffitresult = _xrfmodel.fit_spectrum({XRFGROUP}.workmca, energy_min={emin:.2f}, energy_max={emax:.2f}, 

117 fit_toler={fit_toler:.6g}, fit_step={fit_step:.6g}, max_nfev={max_nfev:d}) 

118_xrfresults.insert(0, _xrffitresult) 

119######## 

120""" 

121 

122xrfmod_filter = "_xrfmodel.add_filter('{name:s}', {thick:.5f}, vary_thickness={vary:s})" 

123xrfmod_matrix = "_xrfmodel.set_matrix('{name:s}', {thick:.5f}, density={density:.5f})" 

124xrfmod_pileup = "_xrfmodel.add_pileup(scale={scale:.3f}, vary={vary:s})" 

125xrfmod_escape = "_xrfmodel.add_escape(scale={scale:.3f}, vary={vary:s})" 

126 

127xrfmod_savejs = "_xrfresults[{nfit:d}].save('{filename:s}')" 

128 

129xrfmod_elems = """ 

130# add elements 

131for atsym in {elemlist:s}: 

132 _xrfmodel.add_element(atsym) 

133#endfor 

134del atsym""" 

135 

136Filter_Lengths = ['microns', 'mm', 'cm'] 

137Filter_Materials = ['None', 'air', 'nitrogen', 'helium', 'kapton', 

138 'beryllium', 'aluminum', 'mylar', 'pmma'] 

139 

140 

141DEF_CONFIG = { "mca_name": "", "escape_use": True, "escape_amp": 0.25, 

142 "pileup_use": True, "pileup_amp": 0.2, "escape_amp_vary": 

143 True, "pileup_amp_vary": True, "cal_slope": 0.01, 

144 "cal_offset": 0, "cal_vary": True, "det_mat": "Si", "det_thk": 

145 0.4, "det_noise_vary": True, 

146 "en_xray": 30, "en_min": 2.5, "en_max": 30, 

147 "flux_in": 1e9, "det_noise": 0.035, "angle_in": 45.0, 

148 "angle_out": 45.0, "det_dist": 50.0, "det_area": 50.0, 

149 "elements": [16, 17, 18, 20, 22, 24, 25, 26, 28, 29, 30], 

150 "filter1_mat": "beryllium", "filter1_thk": 0.025, "filter1_var": False, 

151 "filter2_mat": "air", "filter2_thk": 50.0, "filter2_var": False, 

152 "filter3_mat": "kapton", "filter3_thk": 0.0, "filter3_var": False, 

153 "filter4_mat": "aluminum", "filter4_thk": 0.0, "filter4_var": False, 

154 "matrix_mat": "", "matrix_thk": 0.0, "matrix_den": 1.0, 

155 "peak_step": 0.025, "peak_gamma": 0.05, "peak_tail": 0.1, 

156 "peak_beta": 0.5, "peak_step_vary": False, "peak_tail_vary": False, 

157 "peak_gamma_vary": False, "peak_beta_vary": False, 

158 "elastic_use": True, "elastic_cen_vary": False, "elastic_step_vary": False, 

159 "elastic_beta_vary": False, "elastic_tail_vary": False, 

160 "elastic_sigma_vary": False, "elastic_cen": 30, 

161 "elastic_step": 0.025, "elastic_tail": 0.1, 

162 "elastic_beta": 0.5, "elastic_sigma": 1.0, 

163 "compton1_use": True, "compton1_cen_vary": True, 

164 "compton1_step_vary": True, "compton1_beta_vary": False, 

165 "compton1_tail_vary": False, "compton1_sigma_vary": False, 

166 "compton1_cen": 12.1875, "compton1_step": 0.025, "compton1_tail": 0.25, 

167 "compton1_beta": 2.0, "compton1_sigma": 1.5, 

168 "compton2_use": True, "compton2_cen_vary": True, "compton2_step_vary": False, 

169 "compton2_beta_vary": False, "compton2_tail_vary": False, 

170 "compton2_sigma_vary": False, "compton2_cen": 11.875, 

171 "compton2_step": 0.025, "compton2_tail": 0.25, "compton2_beta": 2.5, 

172 "compton2_sigma": 2.0, "count_time": 120.0, 

173 } 

174 

175class FitSpectraFrame(wx.Frame): 

176 """Frame for Spectral Analysis""" 

177 

178 def __init__(self, parent, size=(750, 850)): 

179 self.parent = parent 

180 self._larch = parent.larch 

181 symtable = self._larch.symtable 

182 # fetch current spectra from parent 

183 if not symtable.has_group(XRFRESULTS_GROUP): 

184 self._larch.eval(MAKE_XRFRESULTS_GROUP) 

185 

186 self.xrfresults = symtable.get_symbol(XRFRESULTS_GROUP) 

187 xrfgroup = symtable.get_group(XRFGROUP) 

188 mcagroup = getattr(xrfgroup, '_mca') 

189 self.mca = getattr(xrfgroup, mcagroup) 

190 self.mcagroup = f'{XRFGROUP}.{mcagroup}' 

191 

192 self.config = DEF_CONFIG 

193 

194 opts = getattr(xrfgroup, 'fitconfig', None) 

195 if opts is None: 

196 xrf_conffile = Path(user_larchdir, 'xrf_fitconfig.json') 

197 if xrf_conffile.exists(): 

198 opts = json_load(xrf_conffile.as_posix()) 

199 if opts is not None: 

200 self.config.update(opts) 

201 

202 efactor = 1.0 if max(self.mca.energy) < 250. else 1000.0 

203 

204 if self.mca.incident_energy is None: 

205 self.mca.incident_energy = 20.0 

206 if self.mca.incident_energy > 250: 

207 self.mca.incident_energy /= 1000.0 

208 

209 self.nfit = 0 

210 self.colors = GUIColors() 

211 wx.Frame.__init__(self, parent, -1, 'Fit XRF Spectra', 

212 size=size, style=wx.DEFAULT_FRAME_STYLE) 

213 

214 mainfont = wx.Font(FONTSIZE_FW-1, wx.SWISS, wx.NORMAL, wx.NORMAL) 

215 self.font_fixedwidth = wx.Font(FONTSIZE_FW, wx.MODERN, wx.NORMAL, 

216 wx.NORMAL) 

217 self.SetFont(mainfont) 

218 

219 self.wids = {} 

220 self.owids = {} 

221 

222 pan = GridPanel(self) 

223 self.mca_label = self.mca.label 

224 self.wids['mca_name'] = SimpleText(pan, self.mca_label, size=(550, -1), style=LEFT) 

225 self.wids['btn_calc'] = Button(pan, 'Calculate Model', size=(150, -1), 

226 action=self.onShowModel) 

227 self.wids['btn_fit'] = Button(pan, 'Fit Model', size=(150, -1), 

228 action=self.onFitModel) 

229 self.wids['fit_message'] = SimpleText(pan, ' ', size=(300, -1), style=LEFT) 

230 

231 pan.AddText(" XRF Spectrum: ", colour='#880000') 

232 pan.Add(self.wids['mca_name'], dcol=3) 

233 pan.Add(self.wids['btn_calc'], newrow=True) 

234 pan.Add(self.wids['btn_fit']) 

235 pan.Add(self.wids['fit_message']) 

236 self.panels = {} 

237 self.panels['Beam & Detector'] = self.beamdet_page 

238 self.panels['Filters & Matrix'] = self.materials_page 

239 self.panels['Elements & Peaks'] = self.elempeaks_page 

240 self.panels['Fit Results'] = self.fitresult_page 

241 self.panels['Composition'] = self.composition_page 

242 

243 self.nb = flatnotebook(pan, self.panels, on_change=self.onNBChanged) 

244 pan.Add((5, 5), newrow=True) 

245 pan.Add(self.nb, dcol=5, drow=10, newrow=True) 

246 pan.pack() 

247 

248 self.Show() 

249 self.Raise() 

250 

251 def onNBChanged(self, event=None): 

252 pagelabel = self.nb._pages.GetPageText(event.GetSelection()).strip() 

253 if pagelabel.startswith('Composition'): 

254 self.UpdateCompositionPage() 

255 

256 def elempeaks_page(self, **kws): 

257 "elements and peaks parameters" 

258 mca = self.parent.mca 

259 wids = self.wids 

260 p = GridPanel(self) 

261 self.selected_elems = [] 

262 ptable_fontsize = 9 

263 

264 ptpanel = wx.Panel(p) 

265 self.ptable = PeriodicTablePanel(ptpanel, multi_select=True, 

266 fontsize=ptable_fontsize, 

267 size=(360, 180), 

268 tooltip_msg=tooltips['ptable'], 

269 onselect=self.onElemSelect) 

270 ptsizer = wx.BoxSizer(wx.HORIZONTAL) 

271 ptsizer.Add((50,50), 1, wx.ALIGN_LEFT|wx.GROW, 2) 

272 ptsizer.Add(self.ptable, 0, wx.ALIGN_CENTER|wx.ALL, 2) 

273 ptsizer.Add((5, 5), 1, wx.ALIGN_LEFT|wx.GROW, 2) 

274 pack(ptpanel, ptsizer) 

275 

276 cnf = self.config 

277 for name, xmax, xinc in (('step', 1, 0.005), 

278 ('gamma', 10, 0.01), 

279 ('beta', 10, 0.01), 

280 ('tail', 1.0, 0.05)): 

281 fname = 'peak_%s' % name 

282 vname = 'peak_%s_vary' % name 

283 wids[fname] = FloatSpin(p, value=cnf[fname], digits=3, 

284 min_val=0, max_val=xmax, 

285 increment=xinc, tooltip=tooltips[name]) 

286 wids[vname] = VarChoice(p, default=cnf[vname]) 

287 

288 btn_from_peaks = Button(p, 'Guess Peaks', size=(175, -1), 

289 action=self.onElems_GuessPeaks) 

290 # tooltip='Guess elements from peak locations') 

291 btn_from_rois = Button(p, 'Use ROIS as Peaks', size=(175, -1), 

292 action=self.onElems_FromROIS) 

293 btn_clear_elems = Button(p, 'Clear All Peaks', size=(175, -1), 

294 action=self.onElems_Clear) 

295 wx.CallAfter(self.onElems_GuessPeaks) 

296 

297 p.AddText('Elements to model:', colour='#880000', dcol=2) 

298 p.Add(ptpanel, dcol=5, drow=5, pad=5, 

299 style=wx.ALIGN_RIGHT|wx.ALL|wx.GROW, 

300 newrow=True) 

301 irow = p.irow 

302 

303 p.Add(btn_from_peaks, icol=5, dcol=2, irow=irow) 

304 p.Add(btn_from_rois, icol=5, dcol=2, irow=irow+1) 

305 p.Add(btn_clear_elems, icol=5, dcol=2, irow=irow+2) 

306 p.irow += 5 

307 

308 p.Add((2, 2), newrow=True) 

309 p.AddText(' Step: ') 

310 p.Add(wids['peak_step']) 

311 p.Add(wids['peak_step_vary']) 

312 

313 p.AddText(' Gamma : ') 

314 p.Add(wids['peak_gamma']) 

315 p.Add(wids['peak_gamma_vary']) 

316 

317 p.Add((2, 2), newrow=True) 

318 p.AddText(' Beta: ') 

319 p.Add(wids['peak_beta']) 

320 p.Add(wids['peak_beta_vary']) 

321 

322 p.AddText(' Tail: ', size=(120, -1)) 

323 p.Add(wids['peak_tail']) 

324 p.Add(wids['peak_tail_vary']) 

325 p.Add((2, 2), newrow=True) 

326 p.Add(HLine(p, size=(650, 3)), dcol=8) 

327 p.Add((2, 2), newrow=True) 

328 

329 opts = dict(size=(100, -1), min_val=0, digits=4, increment=0.010) 

330 for name, escale in (('elastic', 0), ('compton1', 1), ('compton2', 2)): 

331 en = self.mca.incident_energy 

332 for i in range(escale): 

333 en = en * (1 - 1/(1 + (E_MASS*0.001)/en)) # Compton shift at 90 deg 

334 

335 wids[f'{name:s}_use'] = Check(p, label='Include', default=cnf[f'{name:s}_use']) 

336 p.Add((2, 2), newrow=True) 

337 p.AddText(" %s Peak:" % name.title(), colour='#880000') 

338 p.Add(wids[f'{name:s}_use'], dcol=2) 

339 

340 for att, title, xmax, xinc, newrow in (('cen', ' Energy (kev): ', 9e12, 0.01, True), 

341 ('step', ' Step: ', 1, 0.005, False), 

342 ('sigma', ' Sigma Scale:', 10, 0.05, True), 

343 ('beta', ' Beta: ', 10, 0.01, False), 

344 ('tail', ' Tail: ', 10, 0.01, True)): 

345 label = f'{name:s}_{att:s}' 

346 val = en if att == 'cen' else cnf[label] 

347 wids[f'{label:s}_vary'] = VarChoice(p, default=cnf[f'{label:s}_vary']) 

348 

349 wids[label] = FloatSpin(p, value=val, digits=3, min_val=0, 

350 max_val=xmax, increment=xinc, 

351 tooltip=tooltips[att]) 

352 

353 p.AddText(title, size=(120, -1)) 

354 p.Add(wids[label]) 

355 p.Add(wids[f'{label:s}_vary']) 

356 if newrow: 

357 p.Add((2, 2), newrow=True) 

358 

359 p.Add(HLine(p, size=(650, 3)), dcol=7) 

360 

361 p.pack() 

362 return p 

363 

364 def beamdet_page(self, **kws): 

365 "beam / detector settings" 

366 mca = self.mca 

367 en_min = 2.0 

368 en_max = self.mca.incident_energy 

369 

370 cal_offset = getattr(mca, 'offset', 0) 

371 cal_slope = getattr(mca, 'slope', 0.010) 

372 det_noise = getattr(mca, 'det_noise', 0.035) 

373 escape_amp = getattr(mca, 'escape_amp', 0.25) 

374 

375 if not hasattr(self.mca, 'pileup_scale'): 

376 self.mca.predict_pileup() 

377 pileup_amp = max(0.001, getattr(mca, 'pileup_scale', 0.25)) 

378 

379 wids = self.wids 

380 pdet = GridPanel(self, itemstyle=LEFT) 

381 

382 def addLine(pan): 

383 pan.Add(HLine(pan, size=(650, 3)), dcol=6, newrow=True) 

384 

385 wids['escape_use'] = Check(pdet, label='Include Escape in Fit', 

386 default=True, action=self.onUsePileupEscape) 

387 wids['escape_amp'] = FloatSpin(pdet, value=escape_amp, 

388 min_val=0, max_val=100, digits=3, 

389 increment=0.01, size=(125, -1)) 

390 

391 wids['pileup_use'] = Check(pdet, label='Include Pileup in Fit', 

392 default=True, 

393 action=self.onUsePileupEscape) 

394 wids['pileup_amp'] = FloatSpin(pdet, value=pileup_amp, 

395 min_val=0, max_val=100, digits=3, 

396 increment=0.01, size=(125, -1)) 

397 

398 wids['escape_amp_vary'] = VarChoice(pdet, default=True) 

399 wids['pileup_amp_vary'] = VarChoice(pdet, default=(pileup_amp>0.002)) 

400 

401 

402 wids['cal_slope'] = FloatSpin(pdet, value=cal_slope, 

403 min_val=0, max_val=100, 

404 digits=4, increment=0.01, size=(125, -1)) 

405 wids['cal_offset'] = FloatSpin(pdet, value=cal_offset, 

406 min_val=-500, max_val=500, 

407 digits=4, increment=0.01, size=(125, -1)) 

408 

409 wids['cal_vary'] = Check(pdet, label='Vary Calibration in Fit', default=True) 

410 

411 wids['det_mat'] = Choice(pdet, choices=Detector_Materials, 

412 size=(125, -1), default=0, 

413 action=self.onDetMaterial) 

414 

415 wids['det_thk'] = FloatSpin(pdet, value=0.400, size=(125, -1), 

416 increment=0.010, min_val=0, max_val=10, 

417 digits=4) 

418 

419 wids['det_noise_vary'] = VarChoice(pdet, default=1) 

420 

421 opts = dict(size=(125, -1), min_val=0, max_val=500, digits=3, 

422 increment=0.10) 

423 wids['en_xray'] = FloatSpin(pdet, value=self.mca.incident_energy, 

424 action=self.onSetXrayEnergy, **opts) 

425 wids['en_min'] = FloatSpin(pdet, value=en_min, **opts) 

426 wids['en_max'] = FloatSpin(pdet, value=en_max, **opts) 

427 wids['flux_in'] = FloatCtrl(pdet, value=5.e10, gformat=True, 

428 minval=0, size=(125, -1)) 

429 

430 opts.update({'increment': 0.005}) 

431 wids['det_noise'] = FloatSpin(pdet, value=det_noise, **opts) 

432 wids['det_efano'] = SimpleText(pdet, size=(250, -1), 

433 label='E_Fano= %.4e' % FanoFactors['Si']) 

434 

435 opts.update(digits=1, max_val=90, min_val=0, increment=1) 

436 wids['angle_in'] = FloatSpin(pdet, value=45, **opts) 

437 wids['angle_out'] = FloatSpin(pdet, value=45, **opts) 

438 

439 opts.update(digits=1, max_val=5e9, min_val=0, increment=1) 

440 wids['det_dist'] = FloatSpin(pdet, value=50, **opts) 

441 wids['det_area'] = FloatSpin(pdet, value=50, **opts) 

442 

443 for notyet in ('angle_in', 'angle_out', 'det_dist', 'det_area', 

444 'flux_in'): 

445 wids[notyet].Disable() 

446 

447 wids['fit_toler'] = Choice(pdet, choices=FitTols, size=(125, -1)) 

448 wids['fit_toler'].SetStringSelection('1.e-4') 

449 wids['fit_step'] = Choice(pdet, choices=FitSteps, size=(125, -1)) 

450 wids['fit_step'].SetStringSelection('1.e-4') 

451 wids['fit_maxnfev'] = Choice(pdet, choices=FitMaxNFevs, size=(125, -1)) 

452 wids['fit_maxnfev'].SetStringSelection('1000') 

453 

454 pdet.AddText(' Beam Energy, Fit Range :', colour='#880000', dcol=2) 

455 pdet.AddText(' X-ray Energy (keV): ', newrow=True) 

456 pdet.Add(wids['en_xray']) 

457 pdet.AddText('Incident Flux (Hz): ', newrow=False) 

458 pdet.Add(wids['flux_in']) 

459 pdet.AddText(' Fit Energy Min (keV): ', newrow=True) 

460 pdet.Add(wids['en_min']) 

461 pdet.AddText('Fit Energy Max (keV): ') 

462 pdet.Add(wids['en_max']) 

463 pdet.AddText(' Fit Step Size: ', newrow=True) 

464 pdet.Add(wids['fit_step']) 

465 pdet.AddText('Fit Tolerance: ') 

466 pdet.Add(wids['fit_toler']) 

467 pdet.AddText(' Fit Max Evaluations: ', newrow=True) 

468 pdet.Add(wids['fit_maxnfev']) 

469 

470 

471 addLine(pdet) 

472 pdet.AddText(' Energy Calibration :', colour='#880000', dcol=1, newrow=True) 

473 pdet.Add(wids['cal_vary'], dcol=2) 

474 pdet.AddText(' Offset (keV): ', newrow=True) 

475 pdet.Add(wids['cal_offset']) 

476 pdet.AddText('Slope (keV/bin) : ') 

477 pdet.Add(wids['cal_slope']) 

478 

479 addLine(pdet) 

480 pdet.AddText(' Detector Material:', colour='#880000', dcol=1, newrow=True) 

481 pdet.AddText(EFano_Text, dcol=3) 

482 pdet.AddText(' Material: ', newrow=True) 

483 pdet.Add(wids['det_mat']) 

484 pdet.Add(wids['det_efano'], dcol=2) 

485 pdet.AddText(' Thickness (mm): ', newrow=True) 

486 pdet.Add(wids['det_thk']) 

487 pdet.AddText(' Noise (keV): ', newrow=True) 

488 pdet.Add(wids['det_noise']) 

489 pdet.Add(wids['det_noise_vary'], dcol=2) 

490 

491 

492 addLine(pdet) 

493 pdet.AddText(' Escape && Pileup:', colour='#880000', dcol=2, newrow=True) 

494 pdet.AddText(' Escape Scale:', newrow=True) 

495 pdet.Add(wids['escape_amp']) 

496 pdet.Add(wids['escape_amp_vary']) 

497 pdet.Add(wids['escape_use'], dcol=3) 

498 

499 pdet.AddText(' Pileup Scale:', newrow=True) 

500 pdet.Add(wids['pileup_amp']) 

501 pdet.Add(wids['pileup_amp_vary']) 

502 pdet.Add(wids['pileup_use'], dcol=3) 

503 

504 addLine(pdet) 

505 pdet.AddText(' Geometry:', colour='#880000', dcol=1, newrow=True) 

506 pdet.AddText(Geom_Text, dcol=3) 

507 pdet.AddText(' Incident Angle (deg):', newrow=True) 

508 pdet.Add(wids['angle_in']) 

509 pdet.AddText(' Exit Angle (deg):', newrow=False) 

510 pdet.Add(wids['angle_out']) 

511 pdet.AddText(' Detector Distance (mm): ', newrow=True) 

512 pdet.Add(wids['det_dist']) 

513 pdet.AddText(' Detector Area (mm^2): ', newrow=False) 

514 pdet.Add(wids['det_area']) 

515 

516 

517 addLine(pdet) 

518 pdet.pack() 

519 return pdet 

520 

521 def materials_page(self, **kws): 

522 "filters and matrix settings" 

523 wids = self.wids 

524 pan = GridPanel(self, itemstyle=LEFT) 

525 

526 pan.AddText(' Filters :', colour='#880000', dcol=2) # , newrow=True) 

527 pan.AddManyText((' Filter #', 'Material', 'Thickness (mm)', 

528 'Vary Thickness'), style=LEFT, newrow=True) 

529 opts = dict(size=(125, -1), min_val=0, digits=5, increment=0.005) 

530 

531 for i in range(NFILTERS): 

532 t = 'filter%d' % (i+1) 

533 wids['%s_mat'%t] = Choice(pan, choices=Filter_Materials, default=0, 

534 size=(150, -1), 

535 action=partial(self.onFilterMaterial, index=i+1)) 

536 wids['%s_thk'%t] = FloatSpin(pan, value=0.0, **opts) 

537 wids['%s_var'%t] = VarChoice(pan, default=0) 

538 if i == 0: # first selection 

539 wids['%s_mat'%t].SetStringSelection('beryllium') 

540 wids['%s_thk'%t].SetValue(0.0250) 

541 elif i == 1: # second selection 

542 wids['%s_mat'%t].SetStringSelection('air') 

543 wids['%s_thk'%t].SetValue(50.00) 

544 elif i == 2: # third selection 

545 wids['%s_mat'%t].SetStringSelection('kapton') 

546 wids['%s_thk'%t].SetValue(0.00) 

547 elif i == 3: # third selection 

548 wids['%s_mat'%t].SetStringSelection('aluminum') 

549 wids['%s_thk'%t].SetValue(0.00) 

550 

551 pan.AddText(' %i' % (i+1), newrow=True) 

552 pan.Add(wids['%s_mat' % t]) 

553 pan.Add(wids['%s_thk' % t]) 

554 pan.Add(wids['%s_var' % t]) 

555 

556 pan.Add(HLine(pan, size=(650, 3)), dcol=6, newrow=True) 

557 

558 pan.AddText(' Matrix:', colour='#880000', newrow=True) 

559 pan.AddText(' NOTE: thin film limit only', dcol=3) 

560 

561 wids['matrix_mat'] = wx.TextCtrl(pan, value='', size=(275, -1)) 

562 wids['matrix_thk'] = FloatSpin(pan, value=0.0, **opts) 

563 wids['matrix_den'] = FloatSpin(pan, value=1.0, **opts) 

564 wids['matrix_btn'] = Button(pan, 'Use Material', size=(175, -1), 

565 action=self.onUseCurrentMaterialAsFilter) 

566 wids['matrix_btn'].Disable() 

567 pan.AddText(' Material/Formula:', dcol=1, newrow=True) 

568 pan.Add(wids['matrix_mat'], dcol=2) 

569 pan.Add(wids['matrix_btn'], dcol=3) 

570 pan.AddText(' Thickness (mm):', newrow=True) 

571 pan.Add(wids['matrix_thk']) 

572 pan.AddText(' Density (gr/cm^3):', newrow=False) 

573 pan.Add(wids['matrix_den']) 

574 

575 pan.Add(HLine(pan, size=(650, 3)), dcol=6, newrow=True) 

576 

577 # Materials 

578 pan.AddText(' Known Materials:', colour='#880000', dcol=4, newrow=True) 

579 

580 mview = self.owids['materials'] = dv.DataViewListCtrl(pan, style=DVSTYLE) 

581 mview.Bind(dv.EVT_DATAVIEW_SELECTION_CHANGED, self.onSelectMaterial) 

582 self.selected_material = '' 

583 

584 mview.AppendTextColumn('Name', width=150) 

585 mview.AppendTextColumn('Formula', width=325) 

586 mview.AppendTextColumn('density', width=90) 

587 mview.AppendToggleColumn('Filter?', width=75) 

588 for col in range(4): 

589 this = mview.Columns[col] 

590 align = wx.ALIGN_LEFT 

591 this.Sortable = True 

592 this.Alignment = this.Renderer.Alignment = align 

593 

594 mview.SetMinSize((725, 170)) 

595 mview.DeleteAllItems() 

596 self.materials_data = {} 

597 for name, data in materials._read_materials_db().items(): 

598 # print("DATA " , name, data) 

599 formula, density = data.formula, data.density 

600 self.materials_data[name] = (formula, density) 

601 mview.AppendItem((name, formula, "%9.6f"%density, 

602 name in Filter_Materials)) 

603 pan.Add(mview, dcol=5, newrow=True) 

604 

605 pan.AddText(' Add Material:', colour='#880000', newrow=True) 

606 pan.Add(Button(pan, 'Add', size=(175, -1), 

607 action=self.onAddMaterial)) 

608 pan.Add((10, 10)) 

609 bx = Button(pan, 'Update Filter List', size=(175, -1), 

610 action=self.onUpdateFilterList) 

611 pan.Add(bx) 

612 

613 self.owids['newmat_name'] = wx.TextCtrl(pan, value='', size=(175, -1)) 

614 self.owids['newmat_dens'] = FloatSpin(pan, value=1.0, **opts) 

615 self.owids['newmat_form'] = wx.TextCtrl(pan, value='', size=(400, -1)) 

616 

617 

618 for notyet in ('matrix_mat', 'matrix_thk', 'matrix_den', 

619 'matrix_btn'): 

620 wids[notyet].Disable() 

621 

622 pan.AddText(' Name:', newrow=True) 

623 pan.Add(self.owids['newmat_name']) 

624 pan.AddText(' Density (gr/cm^3):', newrow=False) 

625 pan.Add(self.owids['newmat_dens']) 

626 pan.AddText(' Formula:', newrow=True) 

627 pan.Add(self.owids['newmat_form'], dcol=3) 

628 pan.pack() 

629 return pan 

630 

631 def fitresult_page(self, **kws): 

632 sizer = wx.GridBagSizer(3, 3) 

633 panel = scrolled.ScrolledPanel(self) 

634 # title row 

635 wids = self.owids 

636 title = SimpleText(panel, 'Fit Results', font=Font(FONTSIZE+1), 

637 colour=self.colors.title, style=LEFT) 

638 

639 wids['data_title'] = SimpleText(panel, '< > ', font=Font(FONTSIZE+1), 

640 colour=self.colors.title, style=LEFT) 

641 

642 wids['fitlabel_lab'] = SimpleText(panel, 'Fit Label:') 

643 wids['fitlabel_txt'] = wx.TextCtrl(panel, -1, ' ', size=(150, -1)) 

644 wids['fitlabel_btn'] = Button(panel, 'Set Label', size=(150, -1), 

645 action=self.onChangeFitLabel) 

646 

647 opts = dict(default=False, size=(175, -1), action=self.onPlot) 

648 wids['plot_comps'] = Check(panel, label='Show Components?', **opts) 

649 self.plot_choice = Button(panel, 'Plot', 

650 size=(150, -1), action=self.onPlot) 

651 

652 self.save_result = Button(panel, 'Save Model', 

653 size=(150, -1), action=self.onSaveFitResult) 

654 SetTip(self.save_result, 'save model and result to be loaded later') 

655 

656 self.export_fit = Button(panel, 'Export Fit', 

657 size=(150, -1), action=self.onExportFitResult) 

658 SetTip(self.export_fit, 'save arrays and results to text file') 

659 

660 irow = 0 

661 sizer.Add(title, (irow, 0), (1, 1), LEFT) 

662 sizer.Add(wids['data_title'], (irow, 1), (1, 3), LEFT) 

663 

664 irow += 1 

665 sizer.Add(self.save_result, (irow, 0), (1, 1), LEFT) 

666 sizer.Add(self.export_fit, (irow, 1), (1, 1), LEFT) 

667 sizer.Add(self.plot_choice, (irow, 2), (1, 1), LEFT) 

668 sizer.Add(wids['plot_comps'], (irow, 3), (1, 1), LEFT) 

669 

670 irow += 1 

671 sizer.Add(wids['fitlabel_lab'], (irow, 0), (1, 1), LEFT) 

672 sizer.Add(wids['fitlabel_txt'], (irow, 1), (1, 1), LEFT) 

673 sizer.Add(wids['fitlabel_btn'], (irow, 2), (1, 2), LEFT) 

674 

675 

676 irow += 1 

677 sizer.Add(HLine(panel, size=(650, 3)), (irow, 0), (1, 5), LEFT) 

678 

679 irow += 1 

680 title = SimpleText(panel, '[[Fit Statistics]]', font=Font(FONTSIZE+1), 

681 colour=self.colors.title, style=LEFT) 

682 sizer.Add(title, (irow, 0), (1, 4), LEFT) 

683 

684 sview = wids['stats'] = dv.DataViewListCtrl(panel, style=DVSTYLE) 

685 sview.SetFont(self.font_fixedwidth) 

686 sview.Bind(dv.EVT_DATAVIEW_SELECTION_CHANGED, self.onSelectFit) 

687 sview.AppendTextColumn('Fit Label', width=120) 

688 sview.AppendTextColumn('N_vary', width=80) 

689 sview.AppendTextColumn('N_eval', width=80) 

690 sview.AppendTextColumn('\u03c7\u00B2', width=130) 

691 sview.AppendTextColumn('\u03c7\u00B2_reduced', width=130) 

692 sview.AppendTextColumn('Akaike Info', width=130) 

693 

694 for col in range(sview.ColumnCount): 

695 this = sview.Columns[col] 

696 isort, align = True, wx.ALIGN_RIGHT 

697 if col == 0: 

698 align = wx.ALIGN_LEFT 

699 this.Sortable = isort 

700 this.Alignment = this.Renderer.Alignment = align 

701 sview.SetMinSize((725, 150)) 

702 

703 irow += 1 

704 sizer.Add(sview, (irow, 0), (1, 5), LEFT) 

705 

706 irow += 1 

707 sizer.Add(HLine(panel, size=(650, 3)), (irow, 0), (1, 5), LEFT) 

708 

709 irow += 1 

710 title = SimpleText(panel, '[[Variables]]', font=Font(FONTSIZE+1), 

711 colour=self.colors.title, style=LEFT) 

712 sizer.Add(title, (irow, 0), (1, 1), LEFT) 

713 

714 pview = wids['params'] = dv.DataViewListCtrl(panel, style=DVSTYLE) 

715 pview.SetFont(self.font_fixedwidth) 

716 wids['paramsdata'] = [] 

717 pview.AppendTextColumn('Parameter', width=150) 

718 pview.AppendTextColumn('Refined Value', width=130) 

719 pview.AppendTextColumn('Standard Error', width=130) 

720 pview.AppendTextColumn('% Uncertainty', width=130) 

721 pview.AppendTextColumn('Initial Value', width=130) 

722 

723 for col in range(4): 

724 this = pview.Columns[col] 

725 align = wx.ALIGN_LEFT 

726 if col > 0: 

727 align = wx.ALIGN_RIGHT 

728 this.Sortable = False 

729 this.Alignment = this.Renderer.Alignment = align 

730 

731 pview.SetMinSize((725, 200)) 

732 pview.Bind(dv.EVT_DATAVIEW_SELECTION_CHANGED, self.onSelectParameter) 

733 

734 irow += 1 

735 sizer.Add(pview, (irow, 0), (1, 5), LEFT) 

736 

737 irow += 1 

738 sizer.Add(HLine(panel, size=(650, 3)), (irow, 0), (1, 5), LEFT) 

739 

740 irow += 1 

741 title = SimpleText(panel, '[[Correlations]]', font=Font(FONTSIZE+1), 

742 colour=self.colors.title, style=LEFT) 

743 

744 wids['all_correl'] = Button(panel, 'Show All', 

745 size=(100, -1), action=self.onAllCorrel) 

746 

747 wids['min_correl'] = FloatSpin(panel, value=MIN_CORREL, 

748 min_val=0, size=(100, -1), 

749 digits=3, increment=0.1) 

750 

751 ctitle = SimpleText(panel, 'minimum correlation: ') 

752 sizer.Add(title, (irow, 0), (1, 1), LEFT) 

753 sizer.Add(ctitle, (irow, 1), (1, 1), LEFT) 

754 sizer.Add(wids['min_correl'], (irow, 2), (1, 1), LEFT) 

755 sizer.Add(wids['all_correl'], (irow, 3), (1, 1), LEFT) 

756 

757 cview = wids['correl'] = dv.DataViewListCtrl(panel, style=DVSTYLE) 

758 cview.SetFont(self.font_fixedwidth) 

759 cview.AppendTextColumn('Parameter 1', width=150) 

760 cview.AppendTextColumn('Parameter 2', width=150) 

761 cview.AppendTextColumn('Correlation', width=150) 

762 

763 for col in (0, 1, 2): 

764 this = cview.Columns[col] 

765 this.Sortable = False 

766 align = wx.ALIGN_LEFT 

767 if col == 2: 

768 align = wx.ALIGN_RIGHT 

769 this.Alignment = this.Renderer.Alignment = align 

770 cview.SetMinSize((725, 125)) 

771 

772 irow += 1 

773 sizer.Add(cview, (irow, 0), (1, 5), LEFT) 

774 pack(panel, sizer) 

775 panel.SetMinSize((725, 750)) 

776 panel.SetupScrolling() 

777 return panel 

778 

779 def composition_page(self, **kws): 

780 sizer = wx.GridBagSizer(3, 3) 

781 panel = scrolled.ScrolledPanel(self) 

782 wids = self.owids 

783 title = SimpleText(panel, 'Composition Results', font=Font(FONTSIZE+1), 

784 colour=self.colors.title, style=LEFT) 

785 wids['data_title2'] = SimpleText(panel, '< > ', font=Font(FONTSIZE+1), 

786 colour=self.colors.title, style=LEFT) 

787 

788 cview = wids['composition'] = dv.DataViewListCtrl(panel, style=DVSTYLE) 

789 cview.SetFont(self.font_fixedwidth) 

790 cview.AppendTextColumn(' Z ', width=50) 

791 cview.AppendTextColumn(' Element ', width=100) 

792 cview.AppendTextColumn(' Amplitude', width=170) 

793 cview.AppendTextColumn(' Concentration', width=170) 

794 cview.AppendTextColumn(' Uncertainty', width=180) 

795 

796 for col in range(5): 

797 this = cview.Columns[col] 

798 align = wx.ALIGN_RIGHT 

799 if col == 1: 

800 align = wx.ALIGN_LEFT 

801 this.Sortable = True 

802 this.Alignment = this.Renderer.Alignment = align 

803 

804 cview.SetMinSize((725, 500)) 

805 wids['comp_fitlabel'] = Choice(panel, choices=[''], size=(175, -1), 

806 action=self.onCompSelectFit) 

807 

808 self.compscale_lock = 0.0 

809 wids['comp_elemchoice'] = Choice(panel, choices=[''], size=(125, -1)) 

810 # action=self.onCompSetElemAbundance) 

811 wids['comp_elemscale'] = FloatSpin(panel, value=1.0, digits=5, min_val=0, 

812 increment=0.01, 

813 action=self.onCompSetElemAbundance) 

814 wids['comp_units'] = Choice(panel, choices=CompositionUnits, size=(125, -1)) 

815 wids['comp_scale'] = FloatCtrl(panel, value=0, size=(200, -1), precision=5, 

816 minval=0, action=self.onCompSetScale) 

817 

818 wids['comp_save'] = Button(panel, 'Save This Concentration Data', 

819 size=(200, -1), action=self.onCompSave) 

820 

821 irow = 0 

822 sizer.Add(title, (irow, 0), (1, 2), LEFT) 

823 sizer.Add(wids['data_title2'], (irow, 2), (1, 5), LEFT) 

824 irow += 1 

825 sizer.Add(SimpleText(panel, 'Fit Label:'), (irow, 0), (1, 1), LEFT) 

826 sizer.Add(wids['comp_fitlabel'], (irow, 1), (1, 5), LEFT) 

827 

828 irow += 1 

829 sizer.Add(SimpleText(panel, 'Scale Element:'), (irow, 0), (1, 1), LEFT) 

830 sizer.Add(wids['comp_elemchoice'], (irow, 1), (1, 1), LEFT) 

831 sizer.Add(SimpleText(panel, ' to:'), (irow, 2), (1, 1), LEFT) 

832 sizer.Add(wids['comp_elemscale'], (irow, 3), (1, 1), LEFT) 

833 sizer.Add(wids['comp_units'], (irow, 4), (1, 1), LEFT) 

834 

835 irow += 1 

836 sizer.Add(SimpleText(panel, 'Scaling Factor:'), (irow, 0), (1, 1), LEFT) 

837 sizer.Add(wids['comp_scale'], (irow, 1), (1, 3), LEFT) 

838 

839 irow += 1 

840 sizer.Add(wids['composition'], (irow, 0), (3, 6), LEFT) 

841 

842 irow += 3 

843 sizer.Add(wids['comp_save'], (irow, 0), (1, 3), LEFT) 

844 

845 pack(panel, sizer) 

846 panel.SetMinSize((725, 750)) 

847 panel.SetupScrolling() 

848 return panel 

849 

850 def onCompSetScale(self, event=None, value=None): 

851 if len(self.xrfresults) < 1 or (time.time() - self.compscale_lock) < 0.25: 

852 return 

853 self.compscale_lock = time.time() 

854 owids = self.owids 

855 result = self.get_fitresult(nfit=owids['comp_fitlabel'].GetSelection()) 

856 cur_elem = owids['comp_elemchoice'].GetStringSelection() 

857 conc_vals = {} 

858 for elem in result.comps.keys(): 

859 parname = 'amp_%s' % elem.lower() 

860 if parname in result.params: 

861 par = result.params[parname] 

862 conc_vals[elem] = [par.value, par.stderr] 

863 

864 try: 

865 scale = self.owids['comp_scale'].GetValue() 

866 except: 

867 return 

868 

869 owids['comp_elemscale'].SetValue(conc_vals[cur_elem][0]*scale) 

870 owids['composition'].DeleteAllItems() 

871 result.concentration_results = conc_vals 

872 result.concentration_scale = scale 

873 

874 for elem, dat in conc_vals.items(): 

875 zat = "%d" % atomic_number(elem) 

876 val, serr = dat 

877 rval = "%15.4f" % val 

878 sval = "%15.4f" % (val*scale) 

879 uval = "%15.4f" % (serr*scale) 

880 try: 

881 uval = uval + ' ({:.2%})'.format(abs(serr/val)) 

882 except ZeroDivisionError: 

883 pass 

884 owids['composition'].AppendItem((zat, elem, rval, sval, uval)) 

885 

886 def onCompSetElemAbundance(self, event=None, value=None): 

887 if len(self.xrfresults) < 1 or (time.time() - self.compscale_lock) < 0.25: 

888 return 

889 self.compscale_lock = time.time() 

890 owids = self.owids 

891 result = self.get_fitresult(nfit=owids['comp_fitlabel'].GetSelection()) 

892 cur_elem = owids['comp_elemchoice'].GetStringSelection() 

893 conc_vals = {} 

894 for elem in result.comps.keys(): 

895 parname = 'amp_%s' % elem.lower() 

896 if parname in result.params: 

897 par = result.params[parname] 

898 conc_vals[elem] = [par.value, par.stderr] 

899 

900 result.concentration_results = conc_vals 

901 elem_value = owids['comp_elemscale'].GetValue() 

902 

903 scale = elem_value/conc_vals[cur_elem][0] 

904 result.concentration_scale = scale 

905 owids['comp_scale'].SetValue(scale) 

906 owids['composition'].DeleteAllItems() 

907 for elem, dat in conc_vals.items(): 

908 zat = "%d" % atomic_number(elem) 

909 val, serr = dat 

910 rval = "%15.4f" % val 

911 sval = "%15.4f" % (val*scale) 

912 uval = "%15.4f" % (serr*scale) 

913 try: 

914 uval = uval + ' ({:.2%})'.format(abs(serr/val)) 

915 except ZeroDivisionError: 

916 pass 

917 owids['composition'].AppendItem((zat, elem, rval, sval, uval)) 

918 

919 

920 def onCompSave(self, event=None): 

921 result = self.get_fitresult(nfit=self.owids['comp_fitlabel'].GetSelection()) 

922 scale = result.concentration_scale 

923 deffile = self.mca.label + '_' + result.label 

924 deffile = fix_filename(deffile.replace('.', '_')) + '_xrf.csv' 

925 wcards = "CSV (*.csv)|*.csv|All files (*.*)|*.*" 

926 sfile = FileSave(self, 'Save Concentration Results', 

927 default_file=deffile, 

928 wildcard=wcards) 

929 if sfile is not None: 

930 buff = ["# results for MCA labeled: %s" % self.mca.label, 

931 "# fit label: %s" % result.label, 

932 "# concentration units: %s" % self.owids['comp_units'].GetStringSelection(), 

933 "# count time: %s" % result.count_time, 

934 "# scale: %s" % result.concentration_scale, 

935 "# Fit Report:" ] 

936 for l in result.fit_report.split('\n'): 

937 buff.append("# %s" % l) 

938 buff.append("###########") 

939 buff.append("#Element Concentration Uncertainty Raw_Amplitude") 

940 for elem, dat in result.concentration_results.items(): 

941 eout = (elem + ' '*4)[:4] 

942 val, serr = dat 

943 rval = "%15.4f" % val 

944 sval = "%15.4f" % (val*scale) 

945 uval = "%15.4f" % (serr*scale) 

946 buff.append(" ".join([eout, sval, uval, rval])) 

947 buff.append('') 

948 with open(sfile, 'w', encoding=sys.getdefaultencoding()) as fh: 

949 fh.write('\n'.join(buff)) 

950 

951 def onCompSelectFit(self, event=None): 

952 result = self.get_fitresult(nfit=self.owids['comp_fitlabel'].GetSelection()) 

953 cur_elem = self.owids['comp_elemchoice'].GetStringSelection() 

954 self.owids['comp_elemchoice'].Clear() 

955 elems = [el['symbol'] for el in result.elements] 

956 self.owids['comp_elemchoice'].SetChoices(elems) 

957 if len(cur_elem) > 0: 

958 self.owids['comp_elemchoice'].SetStringSelection(cur_elem) 

959 else: 

960 self.owids['comp_elemchoice'].SetSelection(0) 

961 self.onCompSetElemAbundance() 

962 

963 def UpdateCompositionPage(self, event=None): 

964 self.xrfresults = self._larch.symtable.get_symbol(XRFRESULTS_GROUP) 

965 if len(self.xrfresults) > 0: 

966 result = self.get_fitresult() 

967 fitlab = self.owids['comp_fitlabel'] 

968 fitlab.Clear() 

969 fitlab.SetChoices([a.label for a in self.xrfresults]) 

970 fitlab.SetStringSelection(result.label) 

971 self.onCompSelectFit() 

972 

973 def onElems_Clear(self, event=None): 

974 self.ptable.on_clear_all() 

975 

976 def onElems_GuessPeaks(self, event=None): 

977 mca = self.mca 

978 _indices = peak_indices(mca.counts*1.0, min_dist=5, threshold=0.025) 

979 peak_energies = mca.energy[_indices] 

980 

981 elrange = range(10, 92) 

982 atsyms = [atomic_symbol(i) for i in elrange] 

983 kalphas = [0.001*xray_line(i, 'Ka').energy for i in elrange] 

984 kbetas = [0.001*xray_line(i, 'Kb').energy for i in elrange] 

985 self.ptable.on_clear_all() 

986 elems = [] 

987 for iz, en in enumerate(peak_energies): 

988 for i, ex in enumerate(kalphas): 

989 if abs(en - ex) < 0.025: 

990 elems.append(atsyms[i]) 

991 peak_energies[iz] = -ex 

992 

993 for iz, en in enumerate(peak_energies): 

994 if en > 0: 

995 for i, ex in enumerate(kbetas): 

996 if abs(en - ex) < 0.025: 

997 if atsyms[i] not in elems: 

998 elems.append(atsyms[i]) 

999 peak_energies[iz] = -ex 

1000 

1001 en = self.wids['en_xray'].GetValue() 

1002 emin = self.wids['en_min'].GetValue() 

1003 for elem in elems: 

1004 kedge = 0.001*xray_edge(elem, 'K').energy 

1005 l3edge = 0.001*xray_edge(elem, 'L3').energy 

1006 l2edge = 0.001*xray_edge(elem, 'L3').energy 

1007 if ((kedge < en and kedge > emin) or 

1008 (l3edge < en and l3edge > emin) or 

1009 (l2edge < en and l2edge > emin)): 

1010 if elem not in self.ptable.selected: 

1011 self.ptable.onclick(label=elem) 

1012 

1013 def onElems_FromROIS(self, event=None): 

1014 for roi in self.mca.rois: 

1015 words = roi.name.split() 

1016 elem = words[0].title() 

1017 if (elem in self.ptable.syms and 

1018 elem not in self.ptable.selected): 

1019 self.ptable.onclick(label=elem) 

1020 self.onSetXrayEnergy() 

1021 

1022 def onSetXrayEnergy(self, event=None): 

1023 en = self.wids['en_xray'].GetValue() 

1024 self.wids['en_max'].SetValue(en) 

1025 self.wids['elastic_cen'].SetValue(en) 

1026 self.wids['compton1_cen'].SetValue(en*0.975) 

1027 self.wids['compton2_cen'].SetValue(en*0.950) 

1028 emin = self.wids['en_min'].GetValue() * 1.25 

1029 

1030 self.ptable.on_clear_all() 

1031 for roi in self.mca.rois: 

1032 words = roi.name.split() 

1033 elem = words[0].title() 

1034 kedge = l3edge = l2edge = 0.0 

1035 try: 

1036 kedge = 0.001*xray_edge(elem, 'K').energy 

1037 l3edge = 0.001*xray_edge(elem, 'L3').energy 

1038 l2edge = 0.001*xray_edge(elem, 'L3').energy 

1039 except: 

1040 pass 

1041 if ((kedge < en and kedge > emin) or 

1042 (l3edge < en and l3edge > emin) or 

1043 (l2edge < en and l2edge > emin)): 

1044 if elem not in self.ptable.selected: 

1045 self.ptable.onclick(label=elem) 

1046 

1047 def onDetMaterial(self, event=None): 

1048 dmat = self.wids['det_mat'].GetStringSelection() 

1049 if dmat not in FanoFactors: 

1050 dmat = 'Si' 

1051 self.wids['det_efano'].SetLabel('E_Fano= %.4e' % FanoFactors[dmat]) 

1052 

1053 def onFilterMaterial(self, evt=None, index=1): 

1054 name = evt.GetString() 

1055 den = self.materials_data.get(name, (None, 1.0))[1] 

1056 t = 'filter%d' % (index) 

1057 thick = self.wids['%s_thk'%t] 

1058 if den < 0.1 and thick.GetValue() < 0.1: 

1059 thick.SetValue(10.0) 

1060 thick.SetIncrement(0.5) 

1061 elif den > 0.1 and thick.GetValue() < 1.e-5: 

1062 thick.SetValue(0.0250) 

1063 thick.SetIncrement(0.005) 

1064 

1065 def onUseCurrentMaterialAsFilter(self, evt=None): 

1066 name = self.selected_material 

1067 density = self.materials_data.get(name, (None, 1.0))[1] 

1068 self.wids['matrix_den'].SetValue(density) 

1069 self.wids['matrix_mat'].SetValue(name) 

1070 

1071 def onSelectMaterial(self, evt=None): 

1072 if self.owids['materials'] is None: 

1073 return 

1074 item = self.owids['materials'].GetSelectedRow() 

1075 name = None 

1076 if item > -1: 

1077 name = list(self.materials_data.keys())[item] 

1078 self.selected_material = name 

1079 

1080 self.wids['matrix_btn'].Enable(name is not None) 

1081 if name is not None: 

1082 self.wids['matrix_btn'].SetLabel('Use %s' % name) 

1083 

1084 def onUpdateFilterList(self, evt=None): 

1085 flist = ['None'] 

1086 for i in range(len(self.materials_data)): 

1087 if self.owids['materials'].GetToggleValue(i, 3): # is filter 

1088 flist.append(self.owids['materials'].GetTextValue(i, 0)) 

1089 

1090 for i in range(NFILTERS): 

1091 t = 'filter%d' % (i+1) 

1092 choice = self.wids['%s_mat'%t] 

1093 cur = choice.GetStringSelection() 

1094 choice.Clear() 

1095 choice.SetChoices(flist) 

1096 if cur in flist: 

1097 choice.SetStringSelection(cur) 

1098 else: 

1099 choice.SetSelection(0) 

1100 

1101 def onAddMaterial(self, evt=None): 

1102 name = self.owids['newmat_name'].GetValue() 

1103 formula = self.owids['newmat_form'].GetValue() 

1104 density = self.owids['newmat_dens'].GetValue() 

1105 add = len(name) > 0 and len(formula)>0 

1106 if add and name in self.materials_data: 

1107 add = (Popup(self, 

1108 "Overwrite definition of '%s'?" % name, 

1109 'Re-define material?', 

1110 style=wx.OK|wx.CANCEL)==wx.ID_OK) 

1111 if add: 

1112 irow = list(self.materials_data.keys()).index(name) 

1113 self.owids['materials'].DeleteItem(irow) 

1114 if add: 

1115 add_material(name, formula, density) 

1116 self.materials_data[name] = (formula, density) 

1117 self.selected_material = name 

1118 self.owids['materials'].AppendItem((name, formula, 

1119 "%9.6f"%density, 

1120 False)) 

1121 

1122 def onElemSelect(self, event=None, elem=None): 

1123 self.ptable.tsym.SetLabel('') 

1124 self.ptable.title.SetLabel('%d elements selected' % 

1125 len(self.ptable.selected)) 

1126 

1127 def onUsePileupEscape(self, event=None): 

1128 puse = self.wids['pileup_use'].IsChecked() 

1129 self.wids['pileup_amp'].Enable(puse) 

1130 self.wids['pileup_amp_vary'].Enable(puse) 

1131 

1132 puse = self.wids['escape_use'].IsChecked() 

1133 self.wids['escape_amp'].Enable(puse) 

1134 self.wids['escape_amp_vary'].Enable(puse) 

1135 

1136 

1137 def onUsePeak(self, event=None, name=None, value=None): 

1138 if value is None and event is not None: 

1139 value = event.IsChecked() 

1140 if name is None: 

1141 return 

1142 for a in ('cen', 'step', 'tail', 'sigma', 'beta'): 

1143 self.wids['%s_%s'%(name, a)].Enable(value) 

1144 varwid = self.wids.get('%s_%s_vary'%(name, a), None) 

1145 if varwid is not None: 

1146 varwid.Enable(value) 

1147 

1148 def build_model(self, match_amplitudes=True): 

1149 """build xrf_model from form settings""" 

1150 vars = {'Vary':'True', 'Fix': 'False', 'True':True, 'False': False} 

1151 opts = {} 

1152 for key, wid in self.wids.items(): 

1153 val = None 

1154 if hasattr(wid, 'GetValue'): 

1155 val = wid.GetValue() 

1156 elif hasattr(wid, 'IsChecked'): 

1157 val = wid.IsChecked() 

1158 elif isinstance(wid, Choice): 

1159 val = wid.GetStringSelection() 

1160 elif hasattr(wid, 'GetStringSelection'): 

1161 val = wid.GetStringSelection() 

1162 elif hasattr(wid, 'GetLabel'): 

1163 val = wid.GetLabel() 

1164 if isinstance(val, str) and val.title() in vars: 

1165 val = vars[val.title()] 

1166 opts[key] = val 

1167 opts['count_time'] = getattr(self.mca, 'real_time', 1.0) 

1168 if opts['count_time'] is None: 

1169 opts['count_time'] = 1.0 

1170 opts['datetime'] = time.ctime() 

1171 opts['mca_label'] = self.mca_label 

1172 opts['mcagroup'] = self.mcagroup 

1173 opts['XRFGROUP'] = XRFGROUP 

1174 script = [xrfmod_setup.format(**opts)] 

1175 

1176 for peakname in ('Elastic', 'Compton1', 'Compton2'): 

1177 t = peakname.lower() 

1178 if opts['%s_use'% t]: 

1179 d = {'peakname': t} 

1180 d['_cen'] = opts['%s_cen'%t] 

1181 d['vcen'] = opts['%s_cen_vary'%t] 

1182 d['_step'] = opts['%s_step'%t] 

1183 d['vstep'] = opts['%s_step_vary'%t] 

1184 d['_tail'] = opts['%s_tail'%t] 

1185 d['vtail'] = opts['%s_tail_vary'%t] 

1186 d['_beta'] = opts['%s_beta'%t] 

1187 d['vbeta'] = opts['%s_beta_vary'%t] 

1188 d['_sigma'] = opts['%s_sigma'%t] 

1189 d['vsigma'] = opts['%s_sigma_vary'%t] 

1190 script.append(xrfmod_scattpeak.format(**d)) 

1191 

1192 for i in range(NFILTERS): 

1193 t = 'filter%d' % (i+1) 

1194 f_mat = opts['%s_mat'%t] 

1195 if f_mat not in (None, 'None') and int(1e6*opts['%s_thk'%t]) > 1: 

1196 script.append(xrfmod_filter.format(name=f_mat, 

1197 thick=opts['%s_thk'%t], 

1198 vary=opts['%s_var'%t])) 

1199 

1200 m_mat = opts['matrix_mat'].strip() 

1201 if len(m_mat) > 0 and int(1e6*opts['matrix_thk']) > 1: 

1202 script.append(xrfmod_matrix.format(name=m_mat, 

1203 thick=opts['matrix_thk'], 

1204 density=opts['matrix_den'])) 

1205 

1206 if opts['pileup_use'] in ('True', True): 

1207 script.append(xrfmod_pileup.format(scale=opts['pileup_amp'], 

1208 vary=opts['pileup_amp_vary'])) 

1209 

1210 if opts['escape_use'] in ('True', True): 

1211 script.append(xrfmod_escape.format(scale=opts['escape_amp'], 

1212 vary=opts['escape_amp_vary'])) 

1213 

1214 # sort elements selected on Periodic Table by Z 

1215 elemz = [] 

1216 for elem in self.ptable.selected: 

1217 elemz.append( 1 + self.ptable.syms.index(elem)) 

1218 elemz.sort() 

1219 opts['elements'] = elemz 

1220 

1221 xrfgroup = self._larch.symtable.get_group(XRFGROUP) 

1222 setattr(xrfgroup, 'fitconfig', opts) 

1223 json_dump(opts, Path(user_larchdir, 'xrf_fitconfig.json')) 

1224 

1225 

1226 syms = ["'%s'" % self.ptable.syms[iz-1] for iz in elemz] 

1227 syms = '[%s]' % (', '.join(syms)) 

1228 script.append(xrfmod_elems.format(elemlist=syms)) 

1229 

1230 script.append("# set initial estimate of xrf intensity") 

1231 script.append("{XRFGROUP}.workmca.xrf_init = _xrfmodel.calc_spectrum({XRFGROUP}.workmca.energy)") 

1232 script = '\n'.join(script) 

1233 self.model_script = script.format(group=self.mcagroup, XRFGROUP=XRFGROUP) 

1234 

1235 self._larch.eval(self.model_script) 

1236 

1237 cmds = [] 

1238 self._larch.symtable.get_symbol('_xrfmodel') 

1239 self.xrfmod = self._larch.symtable.get_symbol('_xrfmodel') 

1240 floor = 1.e-12*max(self.mca.counts) 

1241 

1242 if match_amplitudes: 

1243 total = 0.0 * self.mca.counts 

1244 for name, parr in self.xrfmod.comps.items(): 

1245 nam = name.lower() 

1246 try: 

1247 imax = np.where(parr > 0.99*parr.max())[0][0] 

1248 except: # probably means all counts are zero 

1249 imax = int(len(parr)/2.0) 

1250 scale = 2.0*self.mca.counts[imax] / (parr[imax]+1.00) 

1251 ampname = 'amp_%s' % nam 

1252 if nam in ('elastic', 'compton1', 'compton2', 'compton', 

1253 'background', 'pileup', 'escape'): 

1254 ampname = f'{nam}_amp' 

1255 if nam in ('background', 'pileup', 'escape'): 

1256 scale = 1.0 

1257 if nam in ('compton2',): 

1258 scale *= 0.5 

1259 

1260 paramval = self.xrfmod.params[ampname].value 

1261 s = f"_xrfmodel.params['{ampname}'].value = {paramval*scale:.5f}" 

1262 cmds.append(s) 

1263 parr *= scale 

1264 parr[np.where(parr<floor)] = floor 

1265 total += parr 

1266 self.xrfmod.current_model = total 

1267 script = '\n'.join(cmds) 

1268 self._larch.eval(script) 

1269 self.model_script = f"{self.model_script}\n{script}" 

1270 

1271 s = f"{XRFGROUP}.workmca.xrf_init = _xrfmodel.calc_spectrum({XRFGROUP}.workmca.energy)" 

1272 self._larch.eval(s) 

1273 

1274 def plot_model(self, model_spectrum=None, init=False, with_comps=False, 

1275 label=None): 

1276 conf = self.parent.conf 

1277 

1278 plotkws = {'linewidth': 2.5, 'delay_draw': True, 'grid': False, 

1279 'ylog_scale': self.parent.ylog_scale, 'show_legend': False, 

1280 'fullbox': False} 

1281 

1282 ppanel = self.parent.panel 

1283 ppanel.conf.reset_trace_properties() 

1284 self.parent.plot(self.mca.energy, self.mca.counts, mca=self.mca, 

1285 xlabel='E (keV)', xmin=0, with_rois=False, **plotkws) 

1286 

1287 if model_spectrum is None: 

1288 model_spectrum = self.xrfmod.current_model if init else self.xrfmod.best_fit 

1289 if label is None: 

1290 label = 'model' if init else 'best fit' 

1291 

1292 self.parent.oplot(self.mca.energy, model_spectrum, 

1293 label=label, color=conf.fit_color, **plotkws) 

1294 

1295 comp_traces = [] 

1296 plotkws.update({'fill': True, 'alpha':0.35, 'show_legend': True}) 

1297 for label, arr in self.xrfmod.comps.items(): 

1298 ppanel.oplot(self.mca.energy, arr, label=label, **plotkws) 

1299 comp_traces.append(ppanel.conf.ntrace - 1) 

1300 

1301 

1302 yscale = {False:'linear', True:'log'}[self.parent.ylog_scale] 

1303 ppanel.set_logscale(yscale=yscale) 

1304 ppanel.set_viewlimits() 

1305 ppanel.conf.auto_margins = False 

1306 ppanel.conf.set_margins(0.1, 0.02, 0.20, 0.1) 

1307 ppanel.conf.set_legend_location('upper right', False) 

1308 ppanel.conf.show_legend_frame = True 

1309 ppanel.conf.draw_legend(show=True, delay_draw=False) 

1310 

1311 if not with_comps: 

1312 for obj, data in ppanel.conf.legend_map.items(): 

1313 line, trace, legline, legtext = data 

1314 if trace in comp_traces: 

1315 legline.set_alpha(0.50) 

1316 legtext.set_alpha(0.50) 

1317 line.set_visible(False) 

1318 ppanel.conf.fills[trace].set_visible(False) 

1319 ppanel.draw() 

1320 

1321 def onShowModel(self, event=None): 

1322 self.build_model() 

1323 self.plot_model(init=True, with_comps=False) 

1324 self.wids['fit_message'].SetLabel("Initial Model Built") 

1325 

1326 def onFitIteration(self, iter=0, pars=None): 

1327 if iter % 10 == 0: 

1328 nvar = len([p for p in pars.values() if p.vary]) 

1329 print(f"XRF Fit iteration {iter}, {nvar} variables") 

1330 # pass 

1331 

1332 

1333 def onFitModel(self, event=None): 

1334 self.build_model() 

1335 xrfmod = self._larch.symtable.get_symbol('_xrfmodel') 

1336 xrfmod.iter_callback = self.onFitIteration 

1337 

1338 fit_tol = float(self.wids['fit_toler'].GetStringSelection()) 

1339 fit_step = float(self.wids['fit_step'].GetStringSelection()) 

1340 max_nfev = int(self.wids['fit_maxnfev'].GetStringSelection()) 

1341 emin = float(self.wids['en_min'].GetValue()) 

1342 emax = float(self.wids['en_max'].GetValue()) 

1343 

1344 fit_script = xrfmod_fitscript.format(group=self.mcagroup, 

1345 XRFGROUP=XRFGROUP, 

1346 emin=emin, emax=emax, 

1347 fit_toler=fit_tol, 

1348 fit_step=fit_step, 

1349 max_nfev=max_nfev) 

1350 # print("-- > ", fit_script) 

1351 

1352 self._larch.eval(fit_script) 

1353 dgroup = self._larch.symtable.get_group(self.mcagroup) 

1354 self.xrfresults = self._larch.symtable.get_symbol(XRFRESULTS_GROUP) 

1355 

1356 try: 

1357 xrfresult = self.xrfresults[0] 

1358 self.wids['fit_message'].SetLabel("Fit Complete") 

1359 except: 

1360 self.wids['fit_message'].SetLabel("fit failed, cannot get result") 

1361 return 

1362 

1363 xrfresult.script = "%s\n%s" % (self.model_script, fit_script) 

1364 xrfresult.label = "fit %d" % (len(self.xrfresults)) 

1365 self.plot_model(init=True, with_comps=False) 

1366 for i in range(len(self.nb.pagelist)): 

1367 if self.nb.GetPageText(i).strip().startswith('Fit R'): 

1368 self.nb.SetSelection(i) 

1369 time.sleep(0.002) 

1370 self.show_results() 

1371 

1372 def onClose(self, event=None): 

1373 self.Destroy() 

1374 

1375 def onSaveFitResult(self, event=None): 

1376 result = self.get_fitresult() 

1377 deffile = self.mca.label + '_' + result.label 

1378 deffile = fix_filename(deffile.replace('.', '_')) + '.xrfmodel' 

1379 ModelWcards = "XRF Models(*.xrfmodel)|*.xrfmodel|All files (*.*)|*.*" 

1380 sfile = FileSave(self, 'Save XRF Model', default_file=deffile, 

1381 wildcard=ModelWcards) 

1382 if sfile is not None: 

1383 self._larch.eval(xrfmod_savejs.format(group=self.mcagroup, 

1384 nfit=self.nfit, 

1385 filename=sfile)) 

1386 

1387 def onExportFitResult(self, event=None): 

1388 result = self.get_fitresult() 

1389 deffile = self.mca.label + '_' + result.label 

1390 deffile = fix_filename(deffile.replace('.', '_')) + '_xrf.txt' 

1391 wcards = 'All files (*.*)|*.*' 

1392 outfile = FileSave(self, 'Export Fit Result', default_file=deffile) 

1393 if outfile is not None: 

1394 buff = ['# XRF Fit %s: %s' % (self.mca.label, result.label), 

1395 '## Fit Script:'] 

1396 for a in result.script.split('\n'): 

1397 buff.append('# %s' % a) 

1398 buff.append('## Fit Report:') 

1399 for a in result.fit_report.split('\n'): 

1400 buff.append('# %s' % a) 

1401 

1402 buff.append('#') 

1403 buff.append('########################################') 

1404 

1405 labels = ['energy', 'counts', 'best_fit', 

1406 'best_energy', 'fit_window', 

1407 'fit_weight', 'attenuation'] 

1408 labels.extend(list(result.comps.keys())) 

1409 

1410 buff.append('# %s' % (' '.join(labels))) 

1411 

1412 npts = len(self.mca.energy) 

1413 for i in range(npts): 

1414 dline = [gformat(self.mca.energy[i]), 

1415 gformat(self.mca.counts[i]), 

1416 gformat(result.best_fit[i]), 

1417 gformat(result.best_en[i]), 

1418 gformat(result.fit_window[i]), 

1419 gformat(result.fit_weight[i]), 

1420 gformat(result.atten[i])] 

1421 for c in result.comps.values(): 

1422 dline.append(gformat(c[i])) 

1423 buff.append(' '.join(dline)) 

1424 buff.append('\n') 

1425 with open(outfile, 'w', encoding=sys.getdefaultencoding()) as fh: 

1426 fh.write('\n'.join(buff)) 

1427 

1428 def get_fitresult(self, nfit=None): 

1429 if nfit is None: 

1430 nfit = self.nfit 

1431 

1432 self.xrfresults = self._larch.symtable.get_symbol(XRFRESULTS_GROUP) 

1433 self.nfit = max(0, nfit) 

1434 self.nfit = min(self.nfit, len(self.xrfresults)-1) 

1435 return self.xrfresults[self.nfit] 

1436 

1437 def onChangeFitLabel(self, event=None): 

1438 label = self.owids['fitlabel_txt'].GetValue() 

1439 result = self.get_fitresult() 

1440 result.label = label 

1441 self.show_results() 

1442 

1443 def onPlot(self, event=None): 

1444 result = self.get_fitresult() 

1445 xrfmod = self._larch.symtable.get_symbol('_xrfmodel') 

1446 with_comps = self.owids['plot_comps'].IsChecked() 

1447 spect = xrfmod.calc_spectrum(self.mca.energy, 

1448 params=result.params) 

1449 self.plot_model(model_spectrum=spect, with_comps=with_comps, 

1450 label=result.label) 

1451 

1452 def onSelectFit(self, evt=None): 

1453 if self.owids['stats'] is None: 

1454 return 

1455 item = self.owids['stats'].GetSelectedRow() 

1456 if item > -1: 

1457 self.show_fitresult(nfit=item) 

1458 

1459 def onSelectParameter(self, evt=None): 

1460 if self.owids['params'] is None: 

1461 return 

1462 if not self.owids['params'].HasSelection(): 

1463 return 

1464 item = self.owids['params'].GetSelectedRow() 

1465 pname = self.owids['paramsdata'][item] 

1466 

1467 cormin= self.owids['min_correl'].GetValue() 

1468 self.owids['correl'].DeleteAllItems() 

1469 

1470 result = self.get_fitresult() 

1471 this = result.params[pname] 

1472 if this.correl is not None: 

1473 sort_correl = sorted(this.correl.items(), key=lambda it: abs(it[1])) 

1474 for name, corval in reversed(sort_correl): 

1475 if abs(corval) > cormin: 

1476 self.owids['correl'].AppendItem((pname, name, "% .4f" % corval)) 

1477 

1478 def onAllCorrel(self, evt=None): 

1479 result = self.get_fitresult() 

1480 params = result.params 

1481 parnames = list(params.keys()) 

1482 

1483 cormin= self.owids['min_correl'].GetValue() 

1484 correls = {} 

1485 for i, name in enumerate(parnames): 

1486 par = params[name] 

1487 if not par.vary: 

1488 continue 

1489 if hasattr(par, 'correl') and par.correl is not None: 

1490 for name2 in parnames[i+1:]: 

1491 if (name != name2 and name2 in par.correl and 

1492 abs(par.correl[name2]) > cormin): 

1493 correls["%s$$%s" % (name, name2)] = par.correl[name2] 

1494 

1495 sort_correl = sorted(correls.items(), key=lambda it: abs(it[1])) 

1496 sort_correl.reverse() 

1497 

1498 self.owids['correl'].DeleteAllItems() 

1499 

1500 for namepair, corval in sort_correl: 

1501 name1, name2 = namepair.split('$$') 

1502 self.owids['correl'].AppendItem((name1, name2, "% .4f" % corval)) 

1503 

1504 def show_results(self): 

1505 cur = self.get_fitresult() 

1506 self.owids['stats'].DeleteAllItems() 

1507 for i, res in enumerate(self.xrfresults): 

1508 args = [res.label] 

1509 for attr in ('nvarys', 'nfev', 'chisqr', 'redchi', 'aic'): 

1510 val = getattr(res, attr) 

1511 if isinstance(val, int): 

1512 val = '%d' % val 

1513 else: 

1514 val = gformat(val, 11) 

1515 args.append(val) 

1516 self.owids['stats'].AppendItem(tuple(args)) 

1517 self.owids['data_title'].SetLabel("%s: %.3f sec" % (self.mca.label, cur.count_time)) 

1518 self.owids['data_title2'].SetLabel("%s: %.3f sec" % (self.mca.label, cur.count_time)) 

1519 self.owids['fitlabel_txt'].SetValue(cur.label) 

1520 self.show_fitresult(nfit=self.nfit) 

1521 

1522 def show_fitresult(self, nfit=0, mca=None): 

1523 if mca is not None: 

1524 self.mca = mca 

1525 result = self.get_fitresult(nfit=nfit) 

1526 

1527 self.owids['data_title'].SetLabel("%s: %.3f sec" % (self.mca.label, result.count_time)) 

1528 self.owids['data_title2'].SetLabel("%s: %.3f sec" % (self.mca.label, result.count_time)) 

1529 self.result = result 

1530 self.owids['fitlabel_txt'].SetValue(result.label) 

1531 self.owids['params'].DeleteAllItems() 

1532 self.owids['paramsdata'] = [] 

1533 for param in reversed(result.params.values()): 

1534 pname = param.name 

1535 try: 

1536 val = gformat(param.value, 10) 

1537 except (TypeError, ValueError): 

1538 val = ' ??? ' 

1539 serr, perr = ' N/A ', ' N/A ' 

1540 if param.stderr is not None: 

1541 serr = gformat(param.stderr, 10) 

1542 try: 

1543 perr = '{:.3f}'.format(100.0*abs(param.stderr/param.value)) 

1544 except ZeroDivisionError: 

1545 perr = '?' 

1546 extra = ' ' 

1547 if param.expr is not None: 

1548 extra = ' = %s ' % param.expr 

1549 elif not param.vary: 

1550 extra = ' (fixed)' 

1551 elif param.init_value is not None: 

1552 extra = gformat(param.init_value, 10) 

1553 

1554 self.owids['params'].AppendItem((pname, val, serr, perr, extra)) 

1555 self.owids['paramsdata'].append(pname) 

1556 self.Refresh()