Coverage for larch/wxlib/xas_importer.py: 8%
750 statements
« prev ^ index » next coverage.py v7.6.0, created at 2024-10-16 21:04 +0000
« prev ^ index » next coverage.py v7.6.0, created at 2024-10-16 21:04 +0000
1import re
2import numpy as np
3from functools import partial
4from pathlib import Path
5import wx
6import wx.lib.scrolledpanel as scrolled
7import wx.lib.agw.flatnotebook as fnb
8from wxmplot import PlotPanel
10from wxutils import (SimpleText, FloatCtrl, GUIColors, Button, Choice,
11 FileCheckList, pack, Popup, Check, MenuItem, CEN, RIGHT, LEFT,
12 FRAMESTYLE, HLine, Font)
14import larch
15from larch import Group
16from larch.utils.strutils import fix_varname
17from larch.xafs.xafsutils import guess_energy_units
18from larch.io.xas_data_source import open_xas_source
19from larch.utils.physical_constants import PLANCK_HC, DEG2RAD, PI
21CEN |= wx.ALL
22FNB_STYLE = fnb.FNB_NO_X_BUTTON | fnb.FNB_SMART_TABS
23FNB_STYLE |= fnb.FNB_NO_NAV_BUTTONS | fnb.FNB_NODRAG
25XPRE_OPS = ("", "log(", "-log(")
26YPRE_OPS = ("", "log(", "-log(")
27ARR_OPS = ("+", "-", "*", "/")
29YERR_OPS = ("Constant", "Sqrt(Y)", "Array")
30CONV_OPS = ("Lorenztian", "Gaussian")
32XDATATYPES = ("xydata", "xas")
33ENUNITS_TYPES = ("eV", "keV", "degrees", "not energy")
36class AddColumnsFrame(wx.Frame):
37 """Add Column Labels for a larch grouop"""
39 def __init__(self, parent, group, on_ok=None):
40 self.parent = parent
41 self.group = group
42 self.on_ok = on_ok
43 super().__init__(
44 None,
45 -1,
46 "Add Selected Columns",
47 style=wx.DEFAULT_FRAME_STYLE | wx.TAB_TRAVERSAL,
48 )
50 self.SetFont(Font(10))
51 sizer = wx.GridBagSizer(2, 2)
52 panel = scrolled.ScrolledPanel(self)
54 self.SetMinSize((550, 550))
56 self.wids = {}
58 lab_aname = SimpleText(panel, label=" Save Array Name:")
59 lab_range = SimpleText(panel, label=" Use column index:")
60 lab_regex = SimpleText(panel, label=" Use column label:")
62 wids = self.wids = {}
64 wids["arrayname"] = wx.TextCtrl(panel, value="sum", size=(175, -1))
65 wids["tc_nums"] = wx.TextCtrl(panel, value="1,3-10", size=(175, -1))
66 wids["tc_regex"] = wx.TextCtrl(panel, value="*fe*", size=(175, -1))
68 savebtn = Button(panel, "Save", action=self.onOK)
69 plotbtn = Button(panel, "Plot Sum", action=self.onPlot)
70 sel_nums = Button(panel, "Select by Index", action=self.onSelColumns)
71 sel_re = Button(panel, "Select by Pattern", action=self.onSelRegex)
73 sizer.Add(lab_aname, (0, 0), (1, 2), LEFT, 3)
74 sizer.Add(wids["arrayname"], (0, 2), (1, 1), LEFT, 3)
76 sizer.Add(plotbtn, (0, 3), (1, 1), LEFT, 3)
77 sizer.Add(savebtn, (0, 4), (1, 1), LEFT, 3)
79 sizer.Add(lab_range, (1, 0), (1, 2), LEFT, 3)
80 sizer.Add(wids["tc_nums"], (1, 2), (1, 1), LEFT, 3)
81 sizer.Add(sel_nums, (1, 3), (1, 2), LEFT, 3)
83 sizer.Add(lab_regex, (2, 0), (1, 2), LEFT, 3)
84 sizer.Add(wids["tc_regex"], (2, 2), (1, 1), LEFT, 3)
85 sizer.Add(sel_re, (2, 3), (1, 2), LEFT, 3)
87 sizer.Add(HLine(panel, size=(550, 2)), (3, 0), (1, 5), LEFT, 3)
88 ir = 4
90 cind = SimpleText(panel, label=" Index ")
91 csel = SimpleText(panel, label=" Select ")
92 cname = SimpleText(panel, label=" Array Name ")
94 sizer.Add(cind, (ir, 0), (1, 1), LEFT, 3)
95 sizer.Add(csel, (ir, 1), (1, 1), LEFT, 3)
96 sizer.Add(cname, (ir, 2), (1, 3), LEFT, 3)
98 for i, name in enumerate(group.array_labels):
99 ir += 1
100 cind = SimpleText(panel, label=" %i " % (i + 1))
101 cname = SimpleText(panel, label=" %s " % name)
102 csel = Check(panel, label="", default=False)
104 self.wids["col_%d" % i] = csel
106 sizer.Add(cind, (ir, 0), (1, 1), LEFT, 3)
107 sizer.Add(csel, (ir, 1), (1, 1), LEFT, 3)
108 sizer.Add(cname, (ir, 2), (1, 3), LEFT, 3)
110 pack(panel, sizer)
111 panel.SetupScrolling()
113 mainsizer = wx.BoxSizer(wx.VERTICAL)
114 mainsizer.Add(panel, 1, wx.GROW | wx.ALL, 1)
116 pack(self, mainsizer)
117 self.Show()
118 self.SetSize(self.GetBestSize())
119 self.Raise()
121 def make_sum(self):
122 sel = []
123 for name, wid in self.wids.items():
124 if name.startswith("col_") and wid.IsChecked():
125 sel.append(int(name[4:]))
126 self.selected_columns = np.array(sel)
127 narr, npts = self.group.raw.data.shape
128 yplot = np.zeros(npts, dtype=np.float)
129 for i in sel:
130 yplot += self.group.raw.data[i, :]
131 return yplot
133 def get_label(self):
134 label_in = self.wids["arrayname"].GetValue()
135 label = fix_varname(label_in)
136 if label in self.group.array_labels:
137 count = 1
138 while label in self.group.array_labels and count < 1000:
139 label = "%s_%d" % (label, count)
140 count += 1
141 if label != label_in:
142 self.wids["arrayname"].SetValue(label)
143 return label
145 def onOK(self, event=None):
146 yplot = self.make_sum()
147 npts = len(yplot)
148 label = self.get_label()
149 self.group.array_labels.append(label)
150 new = np.append(self.group.raw.data, yplot.reshape(1, npts), axis=0)
151 self.group.raw.data = new
152 self.on_ok(label, self.selected_columns)
154 def onPlot(self, event=None):
155 yplot = self.make_sum()
156 xplot = self.group.xplot
157 label = self.get_label()
158 label = "%s (not saved)" % label
159 popts = dict(
160 marker="o",
161 markersize=4,
162 linewidth=1.5,
163 ylabel=label,
164 label=label,
165 xlabel=self.group.plot_xlabel,
166 )
167 self.parent.plotpanel.plot(xplot, yplot, **popts)
169 def onSelColumns(self, event=None):
170 pattern = self.wids["tc_nums"].GetValue().split(",")
171 sel = []
172 for part in pattern:
173 if "-" in part:
174 start, stop = part.split("-")
175 try:
176 istart = int(start)
177 except ValueError:
178 istart = 1
179 try:
180 istop = int(stop)
181 except ValueError:
182 istop = len(self.group.array_labels) + 1
184 sel.extend(range(istart - 1, istop))
185 else:
186 try:
187 sel.append(int(part) - 1)
188 except Exception:
189 pass
191 for name, wid in self.wids.items():
192 if name.startswith("col_"):
193 wid.SetValue(int(name[4:]) in sel)
195 def onSelRegex(self, event=None):
196 pattern = self.wids["tc_regex"].GetValue().replace("*", ".*")
197 pattern = pattern.replace("..*", ".*")
198 sel = []
199 for i, name in enumerate(self.group.array_labels):
200 sel = re.search(pattern, name, flags=re.IGNORECASE) is not None
201 self.wids["col_%d" % i].SetValue(sel)
204class EditColumnFrame(wx.Frame):
205 """Edit Column Labels for a larch grouop"""
207 def __init__(self, parent, group, on_ok=None):
208 self.parent = parent
209 self.group = group
210 self.on_ok = on_ok
211 super().__init__(
212 None,
213 -1,
214 "Edit Array Names",
215 style=wx.DEFAULT_FRAME_STYLE | wx.TAB_TRAVERSAL,
216 )
218 self.SetFont(Font(10))
219 sizer = wx.GridBagSizer(2, 2)
220 panel = scrolled.ScrolledPanel(self)
222 self.SetMinSize((675, 450))
224 self.wids = {}
225 ir = 0
226 sizer.Add(
227 Button(panel, "Apply Changes", size=(200, -1), action=self.onOK),
228 (0, 1),
229 (1, 2),
230 LEFT,
231 3,
232 )
233 sizer.Add(
234 Button(panel, "Use Column Number", size=(200, -1), action=self.onColNumber),
235 (0, 3),
236 (1, 2),
237 LEFT,
238 3,
239 )
240 sizer.Add(HLine(panel, size=(550, 2)), (1, 1), (1, 5), LEFT, 3)
242 cind = SimpleText(panel, label="Column")
243 cold = SimpleText(panel, label="Current Name")
244 cnew = SimpleText(panel, label="Enter New Name")
245 cret = SimpleText(panel, label=" Result ", size=(150, -1))
246 cinfo = SimpleText(panel, label=" Data Range")
247 cplot = SimpleText(panel, label=" Plot")
249 ir = 2
250 sizer.Add(cind, (ir, 0), (1, 1), LEFT, 3)
251 sizer.Add(cold, (ir, 1), (1, 1), LEFT, 3)
252 sizer.Add(cnew, (ir, 2), (1, 1), LEFT, 3)
253 sizer.Add(cret, (ir, 3), (1, 1), LEFT, 3)
254 sizer.Add(cinfo, (ir, 4), (1, 1), LEFT, 3)
255 sizer.Add(cplot, (ir, 5), (1, 1), LEFT, 3)
257 for i, name in enumerate(group.array_labels):
258 ir += 1
259 cind = SimpleText(panel, label=" %i " % (i + 1))
260 cold = SimpleText(panel, label=" %s " % name)
261 cret = SimpleText(panel, label=fix_varname(name), size=(150, -1))
263 cnew = wx.TextCtrl(
264 panel, value=name, size=(150, -1), style=wx.TE_PROCESS_ENTER
265 )
267 cnew.Bind(wx.EVT_TEXT_ENTER, partial(self.update, index=i))
268 cnew.Bind(wx.EVT_KILL_FOCUS, partial(self.update, index=i))
270 arr = group.data[i, :]
271 info_str = " [ %8g : %8g ] " % (arr.min(), arr.max())
272 cinfo = SimpleText(panel, label=info_str)
273 cplot = Button(panel, "Plot", action=partial(self.onPlot, index=i))
275 self.wids["%d" % i] = cnew
276 self.wids["ret_%d" % i] = cret
278 sizer.Add(cind, (ir, 0), (1, 1), LEFT, 3)
279 sizer.Add(cold, (ir, 1), (1, 1), LEFT, 3)
280 sizer.Add(cnew, (ir, 2), (1, 1), LEFT, 3)
281 sizer.Add(cret, (ir, 3), (1, 1), LEFT, 3)
282 sizer.Add(cinfo, (ir, 4), (1, 1), LEFT, 3)
283 sizer.Add(cplot, (ir, 5), (1, 1), LEFT, 3)
285 pack(panel, sizer)
286 panel.SetupScrolling()
288 mainsizer = wx.BoxSizer(wx.VERTICAL)
289 mainsizer.Add(panel, 1, wx.GROW | wx.ALL, 1)
291 pack(self, mainsizer)
292 self.Show()
293 self.Raise()
295 def onPlot(self, event=None, index=None):
296 if index is not None:
297 x = self.parent.workgroup.index
298 y = self.parent.workgroup.data[index, :]
299 label = self.wids["ret_%i" % index].GetLabel()
300 popts = dict(
301 marker="o",
302 markersize=4,
303 linewidth=1.5,
304 ylabel=label,
305 xlabel="data point",
306 label=label,
307 )
308 self.parent.plotpanel.plot(x, y, **popts)
310 def onColNumber(self, evt=None, index=-1):
311 for name, wid in self.wids.items():
312 val = name
313 if name.startswith("ret_"):
314 val = name[4:]
315 setter = wid.SetLabel
316 else:
317 setter = wid.SetValue
318 setter("col_%d" % (int(val) + 1))
320 def update(self, evt=None, index=-1):
321 newval = fix_varname(self.wids["%d" % index].GetValue())
322 self.wids["ret_%i" % index].SetLabel(newval)
324 def onOK(self, evt=None):
325 group = self.group
326 array_labels = []
327 for i in range(len(self.group.array_labels)):
328 newname = self.wids["ret_%i" % i].GetLabel()
329 array_labels.append(newname)
331 if callable(self.on_ok):
332 self.on_ok(array_labels)
333 self.Destroy()
336class XasImporter(wx.Frame):
337 """Column Data File, select columns"""
339 def __init__(
340 self,
341 parent,
342 filename=None,
343 data_source=None,
344 last_array_sel=None,
345 _larch=None,
346 read_ok_cb=None,
347 ):
348 if data_source is None:
349 try:
350 data_source = open_xas_source(filename)
351 except Exception as e:
352 title = "Not a valid data source: %s" % filename
353 message = "Data source error %s: %s" % (filename, e)
354 r = Popup(parent, message, title)
355 return None
356 self.data_source = data_source
358 self.parent = parent
359 self.path = filename
360 self.extra_sums = {}
361 self._larch = _larch
362 self.scans = self.data_source.get_sorted_scan_names()
363 self.curscan = None
365 self.subframes = {}
366 self.workgroup = Group()
367 for attr in ("path", "filename", "datatype", "array_labels", "data"):
368 setattr(self.workgroup, attr, None)
370 self.workgroup.datatype = "xas"
372 self.read_ok_cb = read_ok_cb
374 self.array_sel = dict(
375 xarr=None,
376 yarr1=None,
377 yarr2=None,
378 yop="/",
379 ypop="",
380 monod=3.1355316,
381 en_units="eV",
382 yerror=YERR_OPS[0],
383 yerr_val=1,
384 yerr_arr=None,
385 )
387 if last_array_sel is not None:
388 self.array_sel.update(last_array_sel)
390 super().__init__(None, -1, f"Build Arrays for {filename:s}", style=FRAMESTYLE)
392 self.SetMinSize((750, 550))
393 self.SetSize((850, 650))
394 self.colors = GUIColors()
396 x0, y0 = parent.GetPosition()
397 self.SetPosition((x0 + 60, y0 + 60))
398 self.SetFont(Font(10))
400 splitter = wx.SplitterWindow(self, style=wx.SP_LIVE_UPDATE)
401 splitter.SetMinimumPaneSize(200)
403 leftpanel = wx.Panel(splitter)
404 ltop = wx.Panel(leftpanel)
406 sel_none = Button(ltop, "Select None", size=(100, 30), action=self.onSelNone)
407 sel_all = Button(ltop, "Select All", size=(100, 30), action=self.onSelAll)
408 sel_imp = Button(
409 ltop, "Import Selected Scans", size=(200, -1), action=self.onOK
410 )
412 self.scanlist = FileCheckList(leftpanel, select_action=self.onScanSelect)
413 self.scanlist.AppendItems(self.scans)
415 tsizer = wx.GridBagSizer(2, 2)
416 tsizer.Add(sel_all, (0, 0), (1, 1), LEFT, 0)
417 tsizer.Add(sel_none, (0, 1), (1, 1), LEFT, 0)
418 tsizer.Add(sel_imp, (1, 0), (1, 2), LEFT, 0)
419 pack(ltop, tsizer)
421 sizer = wx.BoxSizer(wx.VERTICAL)
422 sizer.Add(ltop, 0, LEFT | wx.GROW, 1)
423 sizer.Add(self.scanlist, 1, LEFT | wx.GROW | wx.ALL, 1)
424 pack(leftpanel, sizer)
426 rightpanel = wx.Panel(splitter)
427 rtop = wx.Panel(rightpanel)
429 self.wid_scantitle = SimpleText(
430 rtop, "<no scan selected>", font=Font(11), style=LEFT
431 )
432 self.wid_scantime = SimpleText(
433 rtop, "<no scan selected>", font=Font(11), style=LEFT
434 )
435 self.title = SimpleText(
436 rtop,
437 "<no scan selected>",
438 font=Font(11),
439 colour=self.colors.title,
440 style=LEFT,
441 )
443 yarr_labels = self.yarr_labels = ["1.0", "0.0", ""]
444 xarr_labels = self.xarr_labels = ["_index"]
446 self.xarr = Choice(
447 rtop, choices=xarr_labels, action=self.onXSelect, size=(150, -1)
448 )
449 self.yarr1 = Choice(
450 rtop, choices=yarr_labels, action=self.onUpdate, size=(150, -1)
451 )
452 self.yarr2 = Choice(
453 rtop, choices=yarr_labels, action=self.onUpdate, size=(150, -1)
454 )
455 self.yerr_arr = Choice(
456 rtop, choices=yarr_labels, action=self.onUpdate, size=(150, -1)
457 )
458 self.yerr_arr.Disable()
460 self.datatype = Choice(
461 rtop, choices=XDATATYPES, action=self.onUpdate, size=(150, -1)
462 )
463 self.datatype.SetStringSelection(self.workgroup.datatype)
465 self.en_units = Choice(
466 rtop, choices=ENUNITS_TYPES, action=self.onEnUnitsSelect, size=(150, -1)
467 )
469 self.ypop = Choice(rtop, choices=YPRE_OPS, action=self.onUpdate, size=(150, -1))
470 self.yop = Choice(rtop, choices=ARR_OPS, action=self.onUpdate, size=(50, -1))
471 self.yerr_op = Choice(
472 rtop, choices=YERR_OPS, action=self.onYerrChoice, size=(150, -1)
473 )
475 self.yerr_val = FloatCtrl(rtop, value=1, precision=4, size=(90, -1))
476 self.monod_val = FloatCtrl(rtop, value=3.1355316, precision=7, size=(90, -1))
478 xlab = SimpleText(rtop, " X array: ")
479 ylab = SimpleText(rtop, " Y array: ")
480 units_lab = SimpleText(rtop, " Units: ")
481 yerr_lab = SimpleText(rtop, " Yerror: ")
482 dtype_lab = SimpleText(rtop, " Data Type: ")
483 monod_lab = SimpleText(rtop, " Mono D spacing (Ang): ")
484 yerrval_lab = SimpleText(rtop, " Value:")
486 self.ysuf = SimpleText(rtop, "")
487 self.message = SimpleText(
488 rtop, "", font=Font(11), colour=self.colors.title, style=LEFT
489 )
491 self.ypop.SetStringSelection(self.array_sel["ypop"])
492 self.yop.SetStringSelection(self.array_sel["yop"])
493 self.monod_val.SetValue(self.array_sel["monod"])
494 self.monod_val.SetAction(self.onUpdate)
496 self.monod_val.Enable(self.array_sel["en_units"].startswith("deg"))
497 self.en_units.SetStringSelection(self.array_sel["en_units"])
498 self.yerr_op.SetStringSelection(self.array_sel["yerror"])
499 self.yerr_val.SetValue(self.array_sel["yerr_val"])
500 if "(" in self.array_sel["ypop"]:
501 self.ysuf.SetLabel(")")
503 sizer = wx.GridBagSizer(2, 2)
504 ir = 0
505 sizer.Add(self.title, (ir, 0), (1, 7), LEFT, 5)
507 ir += 1
508 sizer.Add(self.wid_scantitle, (ir, 0), (1, 3), LEFT, 0)
509 sizer.Add(self.wid_scantime, (ir, 3), (1, 2), LEFT, 0)
511 ir += 1
512 sizer.Add(xlab, (ir, 0), (1, 1), LEFT, 0)
513 sizer.Add(self.xarr, (ir, 1), (1, 1), LEFT, 0)
514 sizer.Add(units_lab, (ir, 2), (1, 2), RIGHT, 0)
515 sizer.Add(self.en_units, (ir, 4), (1, 2), LEFT, 0)
517 ir += 1
518 sizer.Add(dtype_lab, (ir, 0), (1, 1), LEFT, 0)
519 sizer.Add(self.datatype, (ir, 1), (1, 1), LEFT, 0)
520 sizer.Add(monod_lab, (ir, 2), (1, 2), RIGHT, 0)
521 sizer.Add(self.monod_val, (ir, 4), (1, 1), LEFT, 0)
523 ir += 1
524 sizer.Add(ylab, (ir, 0), (1, 1), LEFT, 0)
525 sizer.Add(self.ypop, (ir, 1), (1, 1), LEFT, 0)
526 sizer.Add(self.yarr1, (ir, 2), (1, 1), LEFT, 0)
527 sizer.Add(self.yop, (ir, 3), (1, 1), RIGHT, 0)
528 sizer.Add(self.yarr2, (ir, 4), (1, 1), LEFT, 0)
529 sizer.Add(self.ysuf, (ir, 5), (1, 1), LEFT, 0)
531 ir += 1
532 sizer.Add(yerr_lab, (ir, 0), (1, 1), LEFT, 0)
533 sizer.Add(self.yerr_op, (ir, 1), (1, 1), LEFT, 0)
534 sizer.Add(self.yerr_arr, (ir, 2), (1, 1), LEFT, 0)
535 sizer.Add(yerrval_lab, (ir, 3), (1, 1), RIGHT, 0)
536 sizer.Add(self.yerr_val, (ir, 4), (1, 2), LEFT, 0)
538 ir += 1
539 sizer.Add(self.message, (ir, 0), (1, 4), LEFT, 0)
540 pack(rtop, sizer)
542 self.nb = fnb.FlatNotebook(rightpanel, -1, agwStyle=FNB_STYLE)
543 self.nb.SetTabAreaColour(wx.Colour(248, 248, 240))
544 self.nb.SetActiveTabColour(wx.Colour(254, 254, 195))
545 self.nb.SetNonActiveTabTextColour(wx.Colour(40, 40, 180))
546 self.nb.SetActiveTabTextColour(wx.Colour(80, 0, 0))
548 self.plotpanel = PlotPanel(rightpanel, messenger=self.plot_messages)
549 try:
550 plotopts = self._larch.symtable._sys.wx.plotopts
551 self.plotpanel.conf.set_theme(plotopts["theme"])
552 self.plotpanel.conf.enable_grid(plotopts["show_grid"])
553 except Exception:
554 pass
556 self.plotpanel.SetMinSize((300, 250))
558 shead = wx.Panel(rightpanel)
559 self.scaninfo = wx.TextCtrl(
560 shead, style=wx.TE_MULTILINE | wx.TE_READONLY, size=(400, 250)
561 )
562 self.scaninfo.SetValue("<no scan selected>")
563 self.scaninfo.SetFont(Font(10))
564 textsizer = wx.BoxSizer(wx.VERTICAL)
565 textsizer.Add(self.scaninfo, 1, LEFT | wx.GROW, 1)
566 pack(shead, textsizer)
568 fhead = wx.Panel(rightpanel)
569 self.fileinfo = wx.TextCtrl(
570 fhead, style=wx.TE_MULTILINE | wx.TE_READONLY, size=(400, 250)
571 )
572 self.fileinfo.SetValue("")
573 self.fileinfo.SetFont(Font(10))
574 textsizer = wx.BoxSizer(wx.VERTICAL)
575 textsizer.Add(self.fileinfo, 1, LEFT | wx.GROW, 1)
576 pack(fhead, textsizer)
578 self.nb.AddPage(fhead, " File Info ", True)
579 self.nb.AddPage(shead, " Scan Info ", True)
580 self.nb.AddPage(self.plotpanel, " Plot of Selected Arrays ", True)
582 sizer = wx.BoxSizer(wx.VERTICAL)
583 sizer.Add(rtop, 0, LEFT | wx.GROW, 1)
584 sizer.Add(self.nb, 1, LEFT | wx.GROW | wx.ALL, 1)
585 pack(rightpanel, sizer)
587 splitter.SplitVertically(leftpanel, rightpanel, 1)
588 self.statusbar = self.CreateStatusBar(2, 0)
589 self.statusbar.SetStatusWidths([-1, -1])
590 statusbar_fields = [filename, ""]
591 for i in range(len(statusbar_fields)):
592 self.statusbar.SetStatusText(statusbar_fields[i], i)
594 csize = self.GetSize()
595 bsize = self.GetBestSize()
596 if bsize[0] > csize[0]:
597 csize[0] = bsize[0]
598 if bsize[1] > csize[1]:
599 csize[1] = bsize[1]
600 self.SetSize(csize)
601 self.Show()
602 self.Raise()
603 self.defaultScanSelect()
605 def set_energy_units(self):
606 ix = self.xarr.GetSelection()
607 xname = self.xarr.GetStringSelection()
608 rdata = self.curscan.data
609 try:
610 ncol, npts = rdata.shape
611 except Exception:
612 self.statusbar.SetStatusText(
613 f"Warning: Could not read data for scan '{self.curscan.name:s}'"
614 )
615 ncol, npts = 0, 0
617 workgroup = self.workgroup
618 if xname.startswith("_index") or ix >= ncol:
619 workgroup.xplot = 1.0 * np.arange(npts)
620 else:
621 workgroup.xplot = 1.0 * rdata[ix, :]
622 eguess = guess_energy_units(workgroup.xplot)
623 if eguess.startswith("eV"):
624 self.en_units.SetStringSelection("eV")
625 elif eguess.startswith("keV"):
626 self.en_units.SetStringSelection("keV")
628 def defaultScanSelect(self):
629 try:
630 name = self.scans[-1]
631 self.curscan = self.data_source.get_scan(name)
632 except Exception:
633 self.onUpdate()
634 else:
635 self.updateScanSelect()
637 def onScanSelect(self, event=None):
638 try:
639 scan_desc = event.GetString()
640 name = [s.strip() for s in scan_desc.split(" | ")][0]
641 self.curscan = self.data_source.get_scan(name)
642 slist = list(self.scanlist.GetCheckedStrings())
643 if scan_desc not in slist:
644 slist.append(scan_desc)
645 self.scanlist.SetCheckedStrings(slist)
646 except Exception:
647 pass
648 else:
649 self.updateScanSelect()
651 def updateScanSelect(self):
652 if self.curscan is None:
653 return
655 self.wid_scantitle.SetLabel(" %s" % self.curscan.description)
656 self.wid_scantime.SetLabel(self.curscan.start_time)
657 self.title.SetLabel(" %s, scan %s" % (self.path, self.curscan.name))
659 arr_labels = [l.lower() for l in self.curscan.labels]
660 yarr_labels = self.yarr_labels = arr_labels + ["1.0", "0.0", ""]
661 xarr_labels = self.xarr_labels = arr_labels + ["_index"]
663 xsel = self.xarr.GetStringSelection()
664 self.xarr.Clear()
665 self.xarr.AppendItems(xarr_labels)
666 if xsel in xarr_labels:
667 self.xarr.SetStringSelection(xsel)
668 else:
669 self.xarr.SetSelection(0)
671 y1sel = self.yarr1.GetStringSelection()
672 self.yarr1.Clear()
673 self.yarr1.AppendItems(yarr_labels)
674 if y1sel in yarr_labels:
675 self.yarr1.SetStringSelection(y1sel)
676 else:
677 self.yarr1.SetSelection(1)
679 y2sel = self.yarr2.GetStringSelection()
680 self.yarr2.Clear()
681 self.yarr2.AppendItems(yarr_labels)
682 if y2sel in yarr_labels:
683 self.yarr2.SetStringSelection(y2sel)
685 xsel = self.xarr.GetStringSelection()
686 self.workgroup.datatype = "xas"
687 self.datatype.SetStringSelection(self.workgroup.datatype)
689 self.scaninfo.SetValue(self.curscan.info)
690 self.set_energy_units()
691 self.onUpdate()
693 def show_subframe(self, name, frameclass, **opts):
694 shown = False
695 if name in self.subframes:
696 try:
697 self.subframes[name].Raise()
698 shown = True
699 except Exception:
700 pass
701 if not shown:
702 self.subframes[name] = frameclass(self, **opts)
703 self.subframes[name].Show()
704 self.subframes[name].Raise()
706 def onAddColumns(self, event=None):
707 self.show_subframe(
708 "addcol", AddColumnsFrame, group=self.workgroup, on_ok=self.add_columns
709 )
711 def add_columns(self, label, selection):
712 new_labels = self.workgroup.array_labels
713 self.set_array_labels(new_labels)
714 self.yarr1.SetStringSelection(new_labels[-1])
715 self.extra_sums[label] = selection
716 self.onUpdate()
718 def onEditNames(self, evt=None):
719 self.show_subframe(
720 "editcol",
721 EditColumnFrame,
722 group=self.workgroup,
723 on_ok=self.set_array_labels,
724 )
726 def set_array_labels(self, arr_labels):
727 self.workgroup.array_labels = arr_labels
728 yarr_labels = self.yarr_labels = arr_labels + ["1.0", "0.0", ""]
729 xarr_labels = self.xarr_labels = arr_labels + ["_index"]
731 def update(wid, choices):
732 curstr = wid.GetStringSelection()
733 curind = wid.GetSelection()
734 wid.SetChoices(choices)
735 if curstr in choices:
736 wid.SetStringSelection(curstr)
737 else:
738 wid.SetSelection(curind)
740 update(self.xarr, xarr_labels)
741 update(self.yarr1, yarr_labels)
742 update(self.yarr2, yarr_labels)
743 update(self.yerr_arr, yarr_labels)
744 self.onUpdate()
746 def onSelAll(self, event=None):
747 self.scanlist.SetCheckedStrings(self.scans)
749 def onSelNone(self, event=None):
750 self.scanlist.SetCheckedStrings([])
752 def onOK(self, event=None):
753 """build arrays according to selection"""
754 scanlist = []
755 for s in self.scanlist.GetCheckedStrings():
756 words = [s.strip() for s in s.split("|")]
757 scanlist.append(words[0])
758 if len(scanlist) == 0:
759 cancel = Popup(
760 self,
761 """No scans selected.
762 Cancel import from this project?""",
763 "Cancel Import?",
764 style=wx.YES_NO,
765 )
766 if wx.ID_YES == cancel:
767 self.Destroy()
768 else:
769 return
771 en_units = self.en_units.GetStringSelection()
772 dspace = float(self.monod_val.GetValue())
773 xarr = self.xarr.GetStringSelection()
774 yarr1 = self.yarr1.GetStringSelection()
775 yarr2 = self.yarr2.GetStringSelection()
776 ypop = self.ypop.GetStringSelection()
777 yop = self.yop.GetStringSelection()
778 yerr_op = self.yerr_op.GetStringSelection()
779 yerr_arr = self.yerr_arr.GetStringSelection()
780 yerr_idx = self.yerr_arr.GetSelection()
781 yerr_val = self.yerr_val.GetValue()
782 yerr_expr = "1"
783 if yerr_op.startswith("const"):
784 yerr_expr = "%f" % yerr_val
785 elif yerr_op.lower().startswith("array"):
786 yerr_expr = "%%s.data[%i, :]" % yerr_idx
787 elif yerr_op.startswith("sqrt"):
788 yerr_expr = "sqrt(%s.yplot)"
789 self.expressions["yerr"] = yerr_expr
791 # generate script to pass back to calling program:
792 # read_cmd = "_data_source.get_scan('{scan}')"
793 buff = [
794 "{group} = {_data_source}.get_scan('{scan}')",
795 "{group}.path = '{path}'",
796 "{group}.is_frozen = False",
797 ]
799 for label, selection in self.extra_sums.items():
800 buff.append("{group}.array_labels.append('%s')" % label)
801 buff.append("_tmparr = {group}.data[%s, :].sum(axis=0)" % repr(selection))
802 buff.append("_tmpn = len(_tmparr)")
803 buff.append(
804 "{group}.data = append({group}.data, _tmparr.reshape(1, _tmpn), axis=0)"
805 )
806 buff.append("del _tmparr, _tmpn")
808 for attr in ("datatype", "plot_xlabel", "plot_ylabel"):
809 val = getattr(self.workgroup, attr)
810 buff.append("{group}.%s = '%s'" % (attr, val))
812 expr = self.expressions["xplot"].replace("%s", "{group:s}")
813 if en_units.startswith("deg"):
814 buff.append(f"mono_dspace = {dspace:.9f}")
815 buff.append(
816 f"{ group} .xplot = PLANCK_HC/(2*mono_dspace*sin(DEG2RAD*({expr:s})))"
817 )
818 elif en_units.startswith("keV"):
819 buff.append(f"{ group} .xplot = 1000.0*{expr:s}")
820 else:
821 buff.append(f"{ group} .xplot = {expr:s}")
823 for aname in ("yplot", "yerr"):
824 expr = self.expressions[aname].replace("%s", "{group:s}")
825 buff.append("{group}.%s = %s" % (aname, expr))
827 if getattr(self.workgroup, "datatype", "xydata") == "xas":
828 buff.append("{group}.energy = {group}.xplot")
829 buff.append("{group}.mu = {group}.yplot")
830 buff.append("sort_xafs({group}, overwrite=True, fix_repeats=True)")
831 else:
832 buff.append("{group}.scale = 1./(ptp({group}.yplot))+1.e-16)")
833 script = "\n".join(buff)
835 self.array_sel["xarr"] = xarr
836 self.array_sel["yarr1"] = yarr1
837 self.array_sel["yarr2"] = yarr2
838 self.array_sel["yop"] = yop
839 self.array_sel["ypop"] = ypop
840 self.array_sel["yerror"] = yerr_op
841 self.array_sel["yerr_val"] = yerr_val
842 self.array_sel["yerr_arr"] = yerr_arr
843 self.array_sel["monod"] = dspace
844 self.array_sel["en_units"] = en_units
846 if self.read_ok_cb is not None:
847 self.read_ok_cb(script, self.path, scanlist, array_sel=self.array_sel)
849 for f in self.subframes.values():
850 try:
851 f.Destroy()
852 except Exception:
853 pass
854 self.Destroy()
856 def onCancel(self, event=None):
857 self.workgroup.import_ok = False
858 for f in self.subframes.values():
859 try:
860 f.Destroy()
861 except Exception:
862 pass
863 self.Destroy()
865 def onYerrChoice(self, evt=None):
866 yerr_choice = evt.GetString()
867 self.yerr_arr.Disable()
868 self.yerr_val.Disable()
869 if "const" in yerr_choice.lower():
870 self.yerr_val.Enable()
871 elif "array" in yerr_choice.lower():
872 self.yerr_arr.Enable()
873 self.onUpdate()
875 def onXSelect(self, evt=None):
876 ix = self.xarr.GetSelection()
877 xname = self.xarr.GetStringSelection()
879 workgroup = self.workgroup
880 rdata = self.curscan.data
881 ncol, npts = rdata.shape
882 if xname.startswith("_index") or ix >= ncol:
883 workgroup.xplot = 1.0 * np.arange(npts)
884 else:
885 workgroup.xplot = 1.0 * rdata[ix, :]
887 self.monod_val.Disable()
888 if self.datatype.GetStringSelection().strip().lower() == "xydata":
889 self.en_units.SetSelection(4)
890 else:
891 eguess = guess_energy_units(workgroup.xplot)
892 if eguess.startswith("keV"):
893 self.en_units.SetSelection(1)
894 elif eguess.startswith("deg"):
895 self.en_units.SetSelection(2)
896 self.monod_val.Enable()
897 else:
898 self.en_units.SetSelection(0)
900 self.onUpdate()
902 def onEnUnitsSelect(self, evt=None):
903 self.monod_val.Enable(self.en_units.GetStringSelection().startswith("deg"))
904 self.onUpdate()
906 def onUpdate(self, value=None, evt=None):
907 """column selections changed calc xplot and yplot"""
908 # dtcorr = self.dtcorr.IsChecked()
909 workgroup = self.workgroup
910 rdata = self.curscan.data
912 dtcorr = False
914 ix = self.xarr.GetSelection()
915 xname = self.xarr.GetStringSelection()
916 yname1 = self.yarr1.GetStringSelection().strip()
917 yname2 = self.yarr2.GetStringSelection().strip()
918 iy1 = self.yarr1.GetSelection()
919 iy2 = self.yarr2.GetSelection()
920 yop = self.yop.GetStringSelection().strip()
922 exprs = dict(xplot=None, yplot=None, yerr=None)
924 ncol, npts = rdata.shape
925 workgroup.index = 1.0 * np.arange(npts)
926 if xname.startswith("_index") or ix >= ncol:
927 workgroup.xplot = 1.0 * np.arange(npts)
928 xname = "_index"
929 exprs["xplot"] = "arange(%i)" % npts
930 else:
931 workgroup.xplot = 1.0 * rdata[ix, :]
932 exprs["xplot"] = "%%s.data[%i, : ]" % ix
934 xlabel = xname
935 en_units = self.en_units.GetStringSelection()
936 if en_units.startswith("deg"):
937 dspace = float(self.monod_val.GetValue())
938 workgroup.xplot = PLANCK_HC / (2 * dspace * np.sin(DEG2RAD * workgroup.xplot))
939 xlabel = xname + " (eV)"
940 elif en_units.startswith("keV"):
941 workgroup.xplot *= 1000.0
942 xlabel = xname + " (eV)"
944 workgroup.datatype = self.datatype.GetStringSelection().strip().lower()
946 def pre_op(opwid, arr):
947 opstr = opwid.GetStringSelection().strip()
948 suf = ""
949 if opstr in ("-log(", "log("):
950 suf = ")"
951 if opstr == "log(":
952 arr = np.log(arr)
953 elif opstr == "-log(":
954 arr = -np.log(arr)
955 arr[np.where(np.isnan(arr))] = 0
956 return suf, opstr, arr
958 ylabel = yname1
959 if len(yname2) == 0:
960 yname2 = "1.0"
961 else:
962 ylabel = "%s%s%s" % (ylabel, yop, yname2)
964 if yname1 == "0.0":
965 yarr1 = np.zeros(npts) * 1.0
966 yexpr1 = "zeros(%i)" % npts
967 elif len(yname1) == 0 or yname1 == "1.0" or iy1 >= ncol:
968 yarr1 = np.ones(npts) * 1.0
969 yexpr1 = "ones(%i)" % npts
970 else:
971 yarr1 = rdata[iy1, :]
972 yexpr1 = "%%s.data[%i, : ]" % iy1
974 if yname2 == "0.0":
975 yarr2 = np.zeros(npts) * 1.0
976 yexpr2 = "0.0"
977 elif len(yname2) == 0 or yname2 == "1.0" or iy2 >= ncol:
978 yarr2 = np.ones(npts) * 1.0
979 yexpr2 = "1.0"
980 else:
981 yarr2 = rdata[iy2, :]
982 yexpr2 = "%%s.data[%i, : ]" % iy2
984 workgroup.yplot = yarr1
986 exprs["yplot"] = yexpr1
987 if yop in ("+", "-", "*", "/"):
988 exprs["yplot"] = "%s %s %s" % (yexpr1, yop, yexpr2)
989 if yop == "+":
990 workgroup.yplot = yarr1.__add__(yarr2)
991 elif yop == "-":
992 workgroup.yplot = yarr1.__sub__(yarr2)
993 elif yop == "*":
994 workgroup.yplot = yarr1.__mul__(yarr2)
995 elif yop == "/":
996 workgroup.yplot = yarr1.__truediv__(yarr2)
998 ysuf, ypop, workgroup.yplot = pre_op(self.ypop, workgroup.yplot)
999 exprs["yplot"] = "%s%s%s" % (ypop, exprs["yplot"], ysuf)
1001 yerr_op = self.yerr_op.GetStringSelection().lower()
1002 exprs["yerr"] = "1"
1003 if yerr_op.startswith("const"):
1004 yerr = self.yerr_val.GetValue()
1005 exprs["yerr"] = "%f" % yerr
1006 elif yerr_op.startswith("array"):
1007 iyerr = self.yerr_arr.GetSelection()
1008 yerr = rdata[iyerr, :]
1009 exprs["yerr"] = "%%s.data[%i, :]" % iyerr
1010 elif yerr_op.startswith("sqrt"):
1011 yerr = np.sqrt(workgroup.yplot)
1012 exprs["yerr"] = "sqrt(%s.yplot)"
1014 self.expressions = exprs
1015 self.array_sel = {
1016 "xarr": xname,
1017 "ypop": ypop,
1018 "yop": yop,
1019 "yarr1": yname1,
1020 "yarr2": yname2,
1021 }
1022 try:
1023 npts = min(len(workgroup.xplot), len(workgroup.yplot))
1024 except AttributeError:
1025 return
1026 except ValueError:
1027 return
1029 en = workgroup.xplot
1030 if (workgroup.datatype == "xas") and (
1031 (
1032 len(en) > 1000
1033 or any(np.diff(en) < 0)
1034 or ((max(en) - min(en)) > 350 and (np.diff(en[:100]).mean() < 1.0))
1035 )
1036 ):
1037 self.statusbar.SetStatusText("Warning: XAS data may need to be rebinned!")
1039 workgroup.filename = self.path
1040 workgroup.npts = npts
1041 workgroup.plot_xlabel = xlabel
1042 workgroup.plot_ylabel = ylabel
1043 workgroup.xplot = np.array(workgroup.xplot[:npts])
1044 workgroup.yplot = np.array(workgroup.yplot[:npts])
1045 workgroup.y = workgroup.yplot
1046 workgroup.yerr = yerr
1047 if isinstance(yerr, np.ndarray):
1048 workgroup.yerr = np.array(yerr[:npts])
1050 if workgroup.datatype == "xas":
1051 workgroup.energy = workgroup.xplot
1052 workgroup.mu = workgroup.yplot
1053 elif workgroup.datatype == 'xydata':
1054 workgroup.x = workgroup.xplot[:]
1055 workgroup.y = workgroup.yplot[:]
1056 workgroup.xshift = 0.0
1057 workgroup.scale = np.ptp(workgroup.y+1.e-15)
1059 fname = Path(workgroup.filename).name
1060 popts = dict(
1061 marker="o",
1062 markersize=4,
1063 linewidth=1.5,
1064 title=fname,
1065 ylabel=ylabel,
1066 xlabel=xlabel,
1067 label="%s: %s" % (fname, workgroup.plot_ylabel),
1068 )
1069 try:
1070 self.plotpanel.plot(workgroup.xplot, workgroup.yplot, **popts)
1071 except Exception:
1072 pass
1074 def plot_messages(self, msg, panel=1):
1075 self.statusbar.SetStatusText(msg, panel)