Skip to content

Commit 34cb5ac

Browse files
committed
add logging and plotting by refinemenrt of selected parameters as per #288
1 parent c04cd58 commit 34cb5ac

4 files changed

Lines changed: 204 additions & 49 deletions

File tree

GSASII/GSASIIctrlGUI.py

Lines changed: 89 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1292,11 +1292,18 @@ def popupSelector(self,event):
12921292
for i in self.choiceDict: self.choiceDict[i] = False
12931293
self.choiceDict[self.choices[new]] = True
12941294
self.selected[new] = True
1295-
elif self.choices[0] == "all":
1296-
# special handling when 1st item is all: turn on everything
1297-
# when all is selected.
1298-
if new == 0:
1299-
self.selected[:] = [False] + (len(self.selected)-1)*[True]
1295+
# elif self.choices[0] == "all":
1296+
# # special handling when 1st item is all: turn on everything
1297+
# # when all is selected.
1298+
# if new == 0:
1299+
# self.selected[:] = [False] + (len(self.selected)-1)*[True]
1300+
# if self.choices[-1] == "(clear)":
1301+
# self.selected[-1] = [False]
1302+
# elif self.choices[-1] == "(clear)":
1303+
# # special handling when last item is all: clear off everything
1304+
# # when that is selected.
1305+
# if new == len:
1306+
# self.selected[:] = (len(self.selected))*[False]
13001307

13011308
menu.Destroy()
13021309
if self.OnChange: wx.CallAfter(self.OnChange,**self.kw)
@@ -5009,6 +5016,9 @@ def _onClose(self,event):
50095016
self.EndModal(wx.ID_CANCEL)
50105017

50115018
class VirtualVarBox(wx.ListCtrl):
5019+
'''This is used to construct the parameter list display for viewing
5020+
least squares parameters
5021+
'''
50125022
def __init__(self, parent):
50135023
self.parmWin = parent
50145024
#patch (added Oct 2020) convert variable names for parm limits to G2VarObj
@@ -5020,8 +5030,8 @@ def __init__(self, parent):
50205030
)
50215031

50225032
for i,(lbl,wid) in enumerate(zip(
5023-
('#', "Parameter", "Ref", "Value", "Min", "Max", "Explanation"),
5024-
(40 , 125 , 30 , 100 , 75 , 75 , 700),)):
5033+
('#', "Parameter", "Ref", 'Log', "Value", "Min", "Max", "Explanation"),
5034+
(40 , 125 , 30 , 30 , 75 , 75 , 75 , 700),)):
50255035
self.InsertColumn(i, lbl)
50265036
self.SetColumnWidth(i, wid)
50275037

@@ -5034,6 +5044,7 @@ def __init__(self, parent):
50345044
self.attr1.SetBackgroundColour((255,255,150))
50355045

50365046
self.Bind(wx.EVT_LIST_ITEM_SELECTED, self.OnRowSelected)
5047+
self.Bind(wx.EVT_LIST_ITEM_RIGHT_CLICK, self.OnRowRightClick)
50375048

50385049
def SetContents(self,parent):
50395050
self.varList = []
@@ -5055,6 +5066,30 @@ def SetContents(self,parent):
50555066
#oldlen = self.GetItemCount()
50565067
self.SetItemCount(len(self.varList))
50575068

5069+
def OnRowRightClick(self, event, row=None):
5070+
'''This adds or removes a variable from the list of logged
5071+
paremeters.
5072+
5073+
When a row is right-clicked this seems to be called twice, once
5074+
event.GetIndex() of -1, then OnRowSelected is called and then this
5075+
is called again, but where event.GetIndex() is the row that was
5076+
selected.
5077+
'''
5078+
def RightClickClear():
5079+
self.RightClick = False
5080+
row = event.GetIndex()
5081+
self.RightClick = True # inhibit use of OnRowSelected
5082+
if row < 0: return
5083+
var = self.varList[row]
5084+
self.parmWin.Controls['LoggedVars'] = self.parmWin.Controls.get('LoggedVars',[])
5085+
if var in self.parmWin.Controls['LoggedVars']:
5086+
self.parmWin.Controls['LoggedVars'].remove(var)
5087+
else:
5088+
self.parmWin.Controls['LoggedVars'].append(var)
5089+
if GSASIIpath.GetConfigValue('debug'): print(self.parmWin.Controls['LoggedVars'])
5090+
self.parmWin.SendSizeEvent() # redraws the window
5091+
wx.CallAfter(RightClickClear)
5092+
50585093
def OnRowSelected(self, event, row=None):
50595094
'Creates an edit window when a parameter is selected'
50605095
def ResetFrozen(event):
@@ -5140,6 +5175,10 @@ def SetWildAfter(d,name,wname,mode):
51405175
self.OnRowSelected(None, row)
51415176

51425177
# start of OnRowSelected
5178+
if getattr(self,'RightClick',False): # ignore calls generated by right-click
5179+
self.RightClick = False
5180+
return
5181+
self.RightClick = False
51435182
if event is not None:
51445183
row = event.Index
51455184
elif row is None:
@@ -5219,18 +5258,18 @@ def SetWildAfter(d,name,wname,mode):
52195258
subSizer.Add(wx.StaticText(dlg,wx.ID_ANY,'Minimum limit'),0,wx.CENTER)
52205259
subSizer.Add(ValidatedTxtCtrl(dlg,self.parmWin.Controls['parmMinDict'],n,nDig=(10,2,'g')),0,WACV)
52215260
delMbtn = wx.Button(dlg, wx.ID_ANY,'Delete',style=wx.BU_EXACTFIT)
5222-
subSizer.Add((5,-1),0,WACV)
5261+
subSizer.Add((5,-1))
52235262
subSizer.Add(delMbtn,0,WACV)
52245263
delMbtn.Bind(wx.EVT_BUTTON, delM)
52255264
if name.split(':')[1]: # is this using a histogram?
5226-
subSizer.Add((5,-1),0,WACV)
5265+
subSizer.Add((5,-1))
52275266
wild = wx.CheckBox(dlg,wx.ID_ANY,label='Match all histograms ')
52285267
wild.SetValue(str(n).split(':')[1] == '*')
52295268
wild.Bind(wx.EVT_CHECKBOX,SetWild)
52305269
wild.hist = True
52315270
subSizer.Add(wild,0,WACV)
52325271
elif len(name.split(':')) > 3:
5233-
subSizer.Add((5,-1),0,WACV)
5272+
subSizer.Add((5,-1))
52345273
wild = wx.CheckBox(dlg,wx.ID_ANY,label='Match all atoms ')
52355274
wild.SetValue(str(n).split(':')[3] == '*')
52365275
wild.Bind(wx.EVT_CHECKBOX,SetWild)
@@ -5249,27 +5288,49 @@ def SetWildAfter(d,name,wname,mode):
52495288
subSizer.Add(wx.StaticText(dlg,wx.ID_ANY,'Maximum limit'),0,wx.CENTER)
52505289
subSizer.Add(ValidatedTxtCtrl(dlg,self.parmWin.Controls['parmMaxDict'],n,nDig=(10,2,'g')),0,WACV)
52515290
delMbtn = wx.Button(dlg, wx.ID_ANY,'Delete',style=wx.BU_EXACTFIT)
5252-
subSizer.Add((5,-1),0,WACV)
5291+
subSizer.Add((5,-1))
52535292
subSizer.Add(delMbtn,0,WACV)
52545293
delMbtn.Bind(wx.EVT_BUTTON, delM)
52555294
delMbtn.max = True
52565295
if name.split(':')[1]: # is this using a histogram?
5257-
subSizer.Add((5,-1),0,WACV)
5296+
subSizer.Add((5,-1))
52585297
wild = wx.CheckBox(dlg,wx.ID_ANY,label='Match all histograms ')
52595298
wild.SetValue(str(n).split(':')[1] == '*')
52605299
wild.Bind(wx.EVT_CHECKBOX,SetWild)
52615300
wild.max = True
52625301
wild.hist = True
52635302
subSizer.Add(wild,0,WACV)
52645303
elif len(name.split(':')) > 3:
5265-
subSizer.Add((5,-1),0,WACV)
5304+
subSizer.Add((5,-1))
52665305
wild = wx.CheckBox(dlg,wx.ID_ANY,label='Match all atoms ')
52675306
wild.SetValue(str(n).split(':')[3] == '*')
52685307
wild.Bind(wx.EVT_CHECKBOX,SetWild)
52695308
wild.max = True
52705309
subSizer.Add(wild,0,WACV)
52715310
mainSizer.Add(subSizer,0)
52725311

5312+
self.parmWin.Controls['LoggedVars'] = self.parmWin.Controls.get('LoggedVars',[])
5313+
def LogLblButton(button):
5314+
if name in self.parmWin.Controls['LoggedVars']:
5315+
lbl = 'Remove from Logging'
5316+
else:
5317+
lbl = 'Log var'
5318+
button.SetLabel(lbl)
5319+
def LogAddRemove(event):
5320+
if name in self.parmWin.Controls['LoggedVars']:
5321+
self.parmWin.Controls['LoggedVars'].remove(name)
5322+
else:
5323+
self.parmWin.Controls['LoggedVars'].append(name)
5324+
LogLblButton(event.GetEventObject())
5325+
dlg.Layout()
5326+
self.parmWin.SendSizeEvent()
5327+
5328+
mainSizer.Add((-1,10))
5329+
addbtn = wx.Button(dlg, wx.ID_ANY,'')
5330+
LogLblButton(addbtn)
5331+
addbtn.Bind(wx.EVT_BUTTON, LogAddRemove)
5332+
mainSizer.Add(addbtn,0)
5333+
52735334
btnsizer = wx.StdDialogButtonSizer()
52745335
OKbtn = wx.Button(dlg, wx.ID_OK)
52755336
OKbtn.SetDefault()
@@ -5310,6 +5371,18 @@ def OnGetItemText(self, item, col):
53105371
return "C"
53115372
return ""
53125373
elif col == 3:
5374+
logged = self.parmWin.Controls.get('LoggedVars',[])
5375+
if GSASIIpath.GetConfigValue('LogAllVars') and not logged:
5376+
if name in self.parmWin.varyList:
5377+
if self.parmWin.varyList.index(name) < 50:
5378+
return 'A'
5379+
else:
5380+
return ' '
5381+
return ' '
5382+
elif name in logged:
5383+
return 'Y'
5384+
return 'N'
5385+
elif col == 4:
53135386
if atmParNam: name = atmParNam
53145387
try:
53155388
value = G2fil.FormatSigFigs(self.parmWin.parmDict[name])
@@ -5318,8 +5391,8 @@ def OnGetItemText(self, item, col):
53185391
except TypeError:
53195392
value = str(self.parmWin.parmDict[name])+' -?' # unexpected
53205393
return value
5321-
elif col == 4 or col == 5: # min/max value
5322-
if col == 4: # min
5394+
elif col == 5 or col == 6: # min/max value
5395+
if col == 5: # min
53235396
d = self.parmWin.Controls['parmMinDict']
53245397
else:
53255398
d = self.parmWin.Controls['parmMaxDict']
@@ -5329,7 +5402,7 @@ def OnGetItemText(self, item, col):
53295402
return G2fil.FormatSigFigs(val,8)
53305403
except TypeError:
53315404
return "?"
5332-
elif col == 6:
5405+
elif col == 7:
53335406
v = G2obj.getVarDescr(name)
53345407
if v is not None and v[-1] is not None:
53355408
txt = G2obj.fmtVarDescr(name)

GSASII/GSASIIdataGUI.py

Lines changed: 99 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -5559,6 +5559,12 @@ def OnRefine(self,event):
55595559
if dlg2.ShowModal() == wx.ID_OK:
55605560
self.reloadFromGPX(rtext,Rvals)
55615561
G2IO.LogCellChanges(self)
5562+
if Rvals.get('LoggedVals'):
5563+
txt = ''
5564+
for p,v in Rvals['LoggedVals'].items():
5565+
if txt: txt += ', '
5566+
txt += (f'{p} : {v:.7g}')
5567+
self.AddToNotebook(txt,'VALS',False)
55625568
if refPlotUpdate:
55635569
refPlotUpdate({},restore=True)
55645570
refPlotUpdate = None
@@ -7507,7 +7513,6 @@ def readFromFile(reader):
75077513
return f'from {reader.formatName} file'
75087514

75097515
#### Notebook Tree Item editor ##############################################
7510-
NBinfo = {}
75117516
def UpdateNotebook(G2frame,data):
75127517
'''Called when the data tree notebook entry is selected. Allows for
75137518
editing of the text in that tree entry
@@ -7532,42 +7537,82 @@ def OnSaveNotebook(event):
75327537
filename = os.path.join(G2frame.dirname,filename)
75337538
with open(filename,'w') as fp:
75347539
fp.write(textBox.GetText())
7535-
print(f'Notebook contents written into {filename}')
7540+
G2G.G2MessageBox(G2frame,
7541+
f'Displayed notebook contents written into {filename}',
7542+
'file written')
7543+
75367544
def onPlotNotebook():
7537-
'Locate R values from the Notebook and plot them'
7538-
NBinfo['plotLbl']
7539-
if NBinfo['plotLbl']['GOF']:
7540-
target = 'GOF'
7541-
elif NBinfo['plotLbl']['Rw']:
7542-
target = 'Rw'
7545+
'Locate values from the Notebook and plot them'
7546+
if NBplotLbl['GOF'] or NBplotLbl['Rw']:
7547+
if NBplotLbl['GOF']:
7548+
target = 'GOF'
7549+
elif NBplotLbl['Rw']:
7550+
target = 'Rw'
7551+
vals = []
7552+
for i,l in enumerate(data):
7553+
ls = l.split()
7554+
if len(ls) < 1: # empty line
7555+
continue
7556+
elif '[' not in ls[0] or '[REF]' in ls[0]:
7557+
if target not in l: continue
7558+
try:
7559+
vals.append(float(l.split(target)[1].split(',')[0].replace('=','').replace('%','')))
7560+
except:
7561+
continue
7562+
X = np.arange(len(vals))
7563+
Y = np.array(vals)
75437564
else:
7544-
G2frame.G2plotNB.Delete('fit results')
7545-
return
7546-
vals = []
7547-
for i,l in enumerate(data):
7548-
ls = l.split()
7549-
if len(ls) < 1: # empty line
7550-
continue
7551-
elif '[' not in ls[0] or '[REF]' in ls[0]:
7552-
if target not in l: continue
7565+
# find what we are plotting if not Rw or GOF
7566+
for i,j in NBplotLbl.items():
7567+
if j:
7568+
target = i
7569+
break
7570+
else:
7571+
target = 'none'
7572+
if target == 'none':
7573+
G2frame.G2plotNB.Delete('fit results')
7574+
return
7575+
c = 0
7576+
vals = []
7577+
pos = []
7578+
for l in data:
7579+
if '[REF]' in l: c += 1
7580+
if '[VALS]' not in l: continue
7581+
v = {i.split(' : ')[0].strip() : i.split(' : ')[1]
7582+
for i in l[6:].split(',')}
7583+
if target not in v: continue
75537584
try:
7554-
vals.append(float(l.split(target)[1].split(',')[0].replace('=','').replace('%','')))
7585+
vals.append(float(v[target]))
7586+
pos.append(c)
75557587
except:
7556-
continue
7557-
Y = np.array(vals)
7558-
XY = [np.arange(len(Y)),Y]
7559-
G2plt.PlotXY(G2frame,[XY,],Title='fit results',labelX='seq',labelY=target,lines=True)
7588+
pass
7589+
X = np.array(pos)
7590+
Y = np.array(vals)
7591+
if len(Y) == 0:
7592+
G2G.G2MessageBox(G2frame,'There are no values to plot','No points')
7593+
return
7594+
elif len(Y) == 1:
7595+
G2G.G2MessageBox(G2frame,'There is only one value to plot','One point')
7596+
return
7597+
XY = [X,Y]
7598+
G2plt.PlotXY(G2frame,[XY,],Title='fit results',newPlot=True,
7599+
labelX='seq',labelY=target,lines=True)
75607600
##### === UpdateNotebook starts here
7561-
filterLbls = ['all',
7601+
filterLbls = ['all', # must be 1st
75627602
'Timestamps','Refinement results','Variables',
75637603
'Comments','Charge flip','Fourier','Peak fit',
75647604
'Constraints','Restraints','Rigid Bodies',
7565-
'Cell params','GSAS-II version #']
7566-
filterPrefix = ['',
7605+
'Cell params','GSAS-II version #','Logged vars',
7606+
'(clear)' # must be last
7607+
]
7608+
filterPrefix = ['', # should match filterLbls
75677609
'TS', 'REF','VARS',
75687610
'CM', 'CF', 'FM', 'PF',
75697611
'CNSTR','RSTR','RB',
7570-
'CEL','VER']
7612+
'CEL','VER','VALS',
7613+
' ']
7614+
G2frame.G2plotNB.Delete('fit results') # clear any old plot since we
7615+
# start out with None selected
75717616
cId = GetGPXtreeItemId(G2frame,G2frame.root, 'Controls')
75727617
if cId:
75737618
controls = G2frame.GPXtree.GetItemPyData(cId)
@@ -7598,17 +7643,38 @@ def onPlotNotebook():
75987643
botSizer.Add(G2G.EnumSelector(parent,controls['Notebook'],
75997644
'order',['oldest-1st','newest-1st'],[False,True],
76007645
OnChange=onUpdateWindow))
7601-
controls['Notebook']['filterSel'] = controls['Notebook'].get(
7602-
'filterSel',[False]+(len(filterLbls)-1)*[True])
7603-
NBinfo['plotLbl'] = NBinfo.get('plotLbl',{'none':True,'Rw':False,'GOF':False})
7604-
for i in range(len(filterPrefix)-len(controls['Notebook']['filterSel'])):
7605-
controls['Notebook']['filterSel'] += [True] # pad list if needed
7646+
# initize filter settings on 1st use
7647+
controls['Notebook']['filterSel'] = controls['Notebook'].get('filterSel',[])
7648+
# initialize if list has changed or this is new
7649+
if len(controls['Notebook']['filterSel']) != len(filterLbls):
7650+
controls['Notebook']['filterSel'] = [False]+(len(filterLbls)-1)*[True]
7651+
controls['Notebook']['filterSel'][filterPrefix.index('VER')] = False
7652+
controls['Notebook']['filterSel'][filterPrefix.index('VALS')] = False
7653+
controls['Notebook']['filterSel'][filterPrefix.index(' ')] = False
7654+
# set all if all selected
7655+
if controls['Notebook']['filterSel'][0]:
7656+
controls['Notebook']['filterSel'] = [True for i in controls['Notebook']['filterSel']]
7657+
controls['Notebook']['filterSel'][0] = False
7658+
controls['Notebook']['filterSel'][-1] = False
7659+
# reset all if (clear) selected
7660+
elif controls['Notebook']['filterSel'][-1]:
7661+
controls['Notebook']['filterSel'] = [False for i in controls['Notebook']['filterSel']]
7662+
# find recorded parameters
7663+
v = []
7664+
for l in data:
7665+
if '[VALS]' not in l: continue
7666+
v += [i.split(' : ')[0].strip() for i in l[6:].split(',') if i.strip()]
7667+
plotable = ['Rw','GOF']
7668+
plotable += sorted(list(set(v)))
7669+
NBplotLbl = {'none':True}
7670+
NBplotLbl.update(dict.fromkeys(plotable,False))
76067671
botSizer.Add((20,-1))
76077672
fBtn = G2G.popupSelectorButton(parent,'Set filters',
7608-
filterLbls,controls['Notebook']['filterSel'],
7673+
filterLbls,
7674+
controls['Notebook']['filterSel'],
76097675
OnChange=onUpdateWindow)
76107676
botSizer.Add(fBtn,0,WACV)
7611-
fBtn = G2G.popupSelectorButton(parent,'Plot',choiceDict=NBinfo['plotLbl'],
7677+
fBtn = G2G.popupSelectorButton(parent,'Plot',choiceDict=NBplotLbl,
76127678
OnChange=onPlotNotebook)
76137679
botSizer.Add((20,-1))
76147680
botSizer.Add(fBtn,0,WACV)

GSASII/GSASIIstrMain.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -641,6 +641,15 @@ def Refine(GPXfile,dlg=None,makeBack=True,refPlotUpdate=None,newLeBail=False,all
641641

642642
# document the refinement further: RB, constraints, restraints, what's varied
643643
Rvals['varyList'] = 'Varied: ' + ', '.join(varyList)
644+
newValDict = {}
645+
if Controls.get('LoggedVars'): # parameter logging
646+
newValDict = {i:float(parmDict[i]) for i in
647+
sorted(Controls['LoggedVars'])
648+
if i in parmDict}
649+
elif GSASIIpath.GetConfigValue('LogAllVars'):
650+
newValDict = {i:float(parmDict[i]) for c,i in enumerate(varyList)
651+
if c<50 and i in parmDict}
652+
if newValDict: Rvals['LoggedVals'] = newValDict
644653
s = G2mv.VarRemapSumm()
645654
if s: Rvals['contrSumm'] = f'Constraints: {s}'
646655
Rvals['restrSumm'] = G2stIO.SummRestraints(restraintDict)

0 commit comments

Comments
 (0)