- Timestamp:
- Jan 30, 2014 11:45:40 PM (11 years ago)
- Location:
- trunk
- Files:
-
- 3 edited
Legend:
- Unmodified
- Added
- Removed
-
TabularUnified trunk/GSASIIconstrGUI.py ¶
r1160 r1209 310 310 :ref:`GSASIIobj <Constraint_definitions_table>` 311 311 ''' 312 atchoice = ["("+i+") "+G2obj.fmtVarDescr(i) for i in varList]312 choices = [[i]+list(G2obj.VarDescr(i)) for i in varList] 313 313 meaning = G2obj.getDescr(FrstVarb.name) 314 314 if not meaning: … … 355 355 if var == str(FrstVarb) or var in varList: continue 356 356 varList += [var] 357 atchoice += ['('+var+') '+plbl+": "+meaning]357 choices.append([var,plbl,meaning]) 358 358 else: 359 359 for nam in nameList: … … 370 370 if var == str(FrstVarb) or var in varList: continue 371 371 varList += [var] 372 atchoice += ['('+var+') '+albl+plbl+": "+meaning]372 choices.append([var,albl+plbl,meaning]) 373 373 elif page[1] == 'hap': 374 374 for nam in nameList: … … 384 384 if var == str(FrstVarb) or var in varList: continue 385 385 varList += [var] 386 atchoice += ['('+var+') '+plbl+hlbl+": "+meaning]386 choices.append([var,plbl+hlbl,meaning]) 387 387 elif page[1] == 'hst': 388 388 for nam in nameList: … … 393 393 if var == str(FrstVarb) or var in varList: continue 394 394 varList += [var] 395 atchoice += ['('+var+') '+hlbl+": "+meaning]395 choices.append([var,hlbl,meaning]) 396 396 elif page[1] == 'glb': 397 397 pass 398 398 else: 399 399 raise Exception, 'Unknown constraint page '+ page[1] 400 if len(atchoice): 401 dlg = wx.MultiChoiceDialog( 400 if len(choices): 401 l1 = l2 = 1 402 for i1,i2,i3 in choices: 403 l1 = max(l1,len(i1)) 404 l2 = max(l2,len(i2)) 405 fmt = "{:"+str(l1)+"s} {:"+str(l2)+"s} {:s}" 406 atchoice = [fmt.format(*i) for i in choices] 407 dlg = G2gd.G2MultiChoiceDialog( 402 408 G2frame.dataFrame,legend, 403 'Constrain '+str(FrstVarb)+' with...',atchoice )404 dlg.SetSize((625,250))409 'Constrain '+str(FrstVarb)+' with...',atchoice, 410 toggle=False,size=(625,400),monoFont=True) 405 411 dlg.CenterOnParent() 406 412 res = dlg.ShowModal() … … 591 597 parent=G2frame.dataFrame) 592 598 return 593 varListlbl = ["("+i+") "+G2obj.fmtVarDescr(i) for i in varList] 599 l2 = l1 = 1 600 for i in varList: 601 l1 = max(l1,len(i)) 602 loc,desc = G2obj.VarDescr(i) 603 l2 = max(l2,len(loc)) 604 fmt = "{:"+str(l1)+"s} {:"+str(l2)+"s} {:s}" 605 varListlbl = [fmt.format(i,*G2obj.VarDescr(i)) for i in varList] 606 #varListlbl = ["("+i+") "+G2obj.fmtVarDescr(i) for i in varList] 594 607 legend = "Select variables to hold (Will not be varied, even if vary flag is set)" 595 608 dlg = G2gd.G2MultiChoiceDialog( 596 609 G2frame.dataFrame, 597 legend,title1,varListlbl,toggle=False,size=(625, 250))610 legend,title1,varListlbl,toggle=False,size=(625,400),monoFont=True) 598 611 dlg.CenterOnParent() 599 612 if dlg.ShowModal() == wx.ID_OK: … … 617 630 parent=G2frame.dataFrame) 618 631 return 619 varListlbl = ["("+i+") "+G2obj.fmtVarDescr(i) for i in varList]620 632 legend = "Select variables to make equivalent (only one of the variables will be varied when all are set to be varied)" 621 GetAddVars(page,title1,title2,varList, varListlbl,constrDictEnt,'equivalence')633 GetAddVars(page,title1,title2,varList,constrDictEnt,'equivalence') 622 634 623 635 def OnAddFunction(event): … … 631 643 parent=G2frame.dataFrame) 632 644 return 633 varListlbl = ["("+i+") "+G2obj.fmtVarDescr(i) for i in varList]634 645 legend = "Select variables to include in a new variable (the new variable will be varied when all included variables are varied)" 635 GetAddVars(page,title1,title2,varList, varListlbl,constrDictEnt,'function')646 GetAddVars(page,title1,title2,varList,constrDictEnt,'function') 636 647 637 648 def OnAddConstraint(event): … … 645 656 parent=G2frame.dataFrame) 646 657 return 647 varListlbl = ["("+i+") "+G2obj.fmtVarDescr(i) for i in varList]648 658 legend = "Select variables to include in a constraint equation (the values will be constrainted to equal a specified constant)" 649 GetAddVars(page,title1,title2,varList, varListlbl,constrDictEnt,'constraint')650 651 def GetAddVars(page,title1,title2,varList, varListlbl,constrDictEnt,constType):659 GetAddVars(page,title1,title2,varList,constrDictEnt,'constraint') 660 661 def GetAddVars(page,title1,title2,varList,constrDictEnt,constType): 652 662 '''Get the variables to be added for OnAddEquivalence, OnAddFunction, 653 663 and OnAddConstraint. Then create and check the constraint. 654 664 ''' 655 dlg = wx.SingleChoiceDialog(G2frame.dataFrame,'Select 1st variable:',title1,varListlbl) 656 dlg.SetSize((625,250)) 665 #varListlbl = ["("+i+") "+G2obj.fmtVarDescr(i) for i in varList] 666 l2 = l1 = 1 667 for i in varList: 668 l1 = max(l1,len(i)) 669 loc,desc = G2obj.VarDescr(i) 670 l2 = max(l2,len(loc)) 671 fmt = "{:"+str(l1)+"s} {:"+str(l2)+"s} {:s}" 672 varListlbl = [fmt.format(i,*G2obj.VarDescr(i)) for i in varList] 673 dlg = G2gd.G2SingleChoiceDialog(G2frame.dataFrame,'Select 1st variable:', 674 title1,varListlbl, 675 monoFont=True,size=(625,400)) 657 676 dlg.CenterOnParent() 658 677 if dlg.ShowModal() == wx.ID_OK: -
TabularUnified trunk/GSASIIgrid.py ¶
r1208 r1209 211 211 * tc: (*wx.TextCtrl*) the TextCtrl name 212 212 213 The number of keyword arguments may be increased in the future , ifneeds arise,213 The number of keyword arguments may be increased in the future should needs arise, 214 214 so it is best to code these functions with a \*\*kwargs argument so they will 215 215 continue to run without errors … … 301 301 raise Exception,("ValidatedTxtCtrl error: Unknown element ("+str(key)+ 302 302 ") type: "+str(type(val))) 303 # When the mouse is moved away or the widget loses focus 303 # When the mouse is moved away or the widget loses focus, 304 304 # display the last saved value, if an expression 305 305 self.Bind(wx.EVT_LEAVE_WINDOW, self._onLeaveWindow) … … 332 332 wx.TextCtrl.SetValue(self,str(G2py3.FormatValue(val,self.nDig))) 333 333 else: 334 wx.TextCtrl.SetValue(self,str(G2py3.FormatSigFigs(val)) )334 wx.TextCtrl.SetValue(self,str(G2py3.FormatSigFigs(val)).rstrip('0')) 335 335 else: 336 336 wx.TextCtrl.SetValue(self,str(val)) … … 1374 1374 1375 1375 ################################################################################ 1376 1376 1377 class G2MultiChoiceDialog(wx.Dialog): 1377 1378 '''A dialog similar to MultiChoiceDialog except that buttons are … … 1384 1385 :param bool toggle: If True (default) the toggle and select all buttons 1385 1386 are displayed 1386 1387 :param bool monoFont: If False (default), use a variable-spaced font; 1388 if True use a equally-spaced font. 1389 :param bool filterBox: If True (default) an input widget is placed on 1390 the window and only entries matching the entered text are shown. 1387 1391 :param kw: optional keyword parameters for the wx.Dialog may 1388 1392 be included such as size [which defaults to `(320,310)`] and … … 1392 1396 :returns: the name of the created dialog 1393 1397 ''' 1394 def __init__(self,parent, title, header, ChoiceList, toggle=True, **kw): 1395 # process keyword parameters, notably Style 1398 def __init__(self,parent, title, header, ChoiceList, toggle=True, 1399 monoFont=False, filterBox=True, **kw): 1400 # process keyword parameters, notably style 1396 1401 options = {'size':(320,310), # default Frame keywords 1397 1402 'style':wx.DEFAULT_DIALOG_STYLE|wx.RESIZE_BORDER|wx.CENTRE| wx.OK | wx.CANCEL, 1398 1403 } 1399 1404 options.update(kw) 1405 self.ChoiceList = ChoiceList 1406 self.filterlist = range(len(self.ChoiceList)) 1400 1407 if options['style'] & wx.OK: 1401 1408 useOK = True … … 1412 1419 # fill the dialog 1413 1420 Sizer = wx.BoxSizer(wx.VERTICAL) 1414 Sizer.Add(wx.StaticText(self,wx.ID_ANY,title),0,wx.ALL,12) 1421 topSizer = wx.BoxSizer(wx.HORIZONTAL) 1422 topSizer.Add(wx.StaticText(self,wx.ID_ANY,title),1,wx.ALL|wx.EXPAND,0) 1423 if filterBox: 1424 self.timer = wx.Timer() 1425 self.timer.Bind(wx.EVT_TIMER,self.Filter) 1426 topSizer.Add(wx.StaticText(self,wx.ID_ANY,'Filter: '),0,wx.ALL,1) 1427 self.txt = wx.TextCtrl(self, wx.ID_ANY, size=(80,-1)) 1428 self.txt.Bind(wx.EVT_CHAR,self.onChar) 1429 topSizer.Add(self.txt,0,wx.ALL,0) 1430 Sizer.Add(topSizer,0,wx.ALL|wx.EXPAND,8) 1415 1431 self.clb = wx.CheckListBox(self, wx.ID_ANY, (30,30), wx.DefaultSize, ChoiceList) 1432 if monoFont: 1433 font1 = wx.Font(self.clb.GetFont().GetPointSize()-1, 1434 wx.MODERN, wx.NORMAL, wx.NORMAL, False) 1435 self.clb.SetFont(font1) 1416 1436 self.numchoices = len(ChoiceList) 1417 1437 Sizer.Add(self.clb,1,wx.LEFT|wx.RIGHT|wx.EXPAND,10) … … 1445 1465 def GetSelections(self): 1446 1466 'Returns a list of the indices for the selected choices' 1447 return [ ifor i in range(self.numchoices) if self.clb.IsChecked(i)]1467 return [self.filterlist[i] for i in range(self.numchoices) if self.clb.IsChecked(i)] 1448 1468 def _SetAll(self,event): 1449 1469 'Set all choices on' … … 1453 1473 for i in range(self.numchoices): 1454 1474 self.clb.Check(i,not self.clb.IsChecked(i)) 1475 def onChar(self,event): 1476 if self.timer.IsRunning(): 1477 self.timer.Stop() 1478 self.timer.Start(1000,oneShot=True) 1479 event.Skip() 1480 def Filter(self,event): 1481 txt = self.txt.GetValue() 1482 self.clb.Clear() 1483 self.Update() 1484 self.filterlist = [] 1485 if txt: 1486 txt = txt.lower() 1487 ChoiceList = [] 1488 for i,item in enumerate(self.ChoiceList): 1489 if item.lower().find(txt) != -1: 1490 ChoiceList.append(item) 1491 self.filterlist.append(i) 1492 else: 1493 self.filterlist = range(len(self.ChoiceList)) 1494 ChoiceList = self.ChoiceList 1495 self.clb.AppendItems(ChoiceList) 1496 1497 ################################################################################ 1498 1499 class G2SingleChoiceDialog(wx.Dialog): 1500 '''A dialog similar to wx.SingleChoiceDialog except that a filter can be 1501 added. 1502 1503 :param wx.Frame ParentFrame: reference to parent frame 1504 :param str title: heading above list of choices 1505 :param str header: Title to place on window frame 1506 :param list ChoiceList: a list of choices where one will be selected 1507 :param bool monoFont: If False (default), use a variable-spaced font; 1508 if True use a equally-spaced font. 1509 :param bool filterBox: If True (default) an input widget is placed on 1510 the window and only entries matching the entered text are shown. 1511 :param kw: optional keyword parameters for the wx.Dialog may 1512 be included such as size [which defaults to `(320,310)`] and 1513 style (which defaults to ``wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER | wx.CENTRE | wx.OK | wx.CANCEL``); 1514 note that ``wx.OK`` and ``wx.CANCEL`` controls 1515 the presence of the eponymous buttons in the dialog. 1516 :returns: the name of the created dialog 1517 ''' 1518 def __init__(self,parent, title, header, ChoiceList, 1519 monoFont=False, filterBox=True, **kw): 1520 # process keyword parameters, notably style 1521 options = {'size':(320,310), # default Frame keywords 1522 'style':wx.DEFAULT_DIALOG_STYLE|wx.RESIZE_BORDER|wx.CENTRE| wx.OK | wx.CANCEL, 1523 } 1524 options.update(kw) 1525 self.ChoiceList = ChoiceList 1526 self.filterlist = range(len(self.ChoiceList)) 1527 if options['style'] & wx.OK: 1528 useOK = True 1529 options['style'] ^= wx.OK 1530 else: 1531 useOK = False 1532 if options['style'] & wx.CANCEL: 1533 useCANCEL = True 1534 options['style'] ^= wx.CANCEL 1535 else: 1536 useCANCEL = False 1537 # create the dialog frame 1538 wx.Dialog.__init__(self,parent,wx.ID_ANY,header,**options) 1539 # fill the dialog 1540 Sizer = wx.BoxSizer(wx.VERTICAL) 1541 topSizer = wx.BoxSizer(wx.HORIZONTAL) 1542 topSizer.Add(wx.StaticText(self,wx.ID_ANY,title),1,wx.ALL|wx.EXPAND,0) 1543 if filterBox: 1544 self.timer = wx.Timer() 1545 self.timer.Bind(wx.EVT_TIMER,self.Filter) 1546 topSizer.Add(wx.StaticText(self,wx.ID_ANY,'Filter: '),0,wx.ALL,1) 1547 self.txt = wx.TextCtrl(self, wx.ID_ANY, size=(80,-1)) 1548 self.txt.Bind(wx.EVT_CHAR,self.onChar) 1549 topSizer.Add(self.txt,0,wx.ALL,0) 1550 Sizer.Add(topSizer,0,wx.ALL|wx.EXPAND,8) 1551 self.clb = wx.ListBox(self, wx.ID_ANY, (30,30), wx.DefaultSize, ChoiceList) 1552 self.clb.Bind(wx.EVT_LEFT_DCLICK,self.onDoubleClick) 1553 if monoFont: 1554 font1 = wx.Font(self.clb.GetFont().GetPointSize()-1, 1555 wx.MODERN, wx.NORMAL, wx.NORMAL, False) 1556 self.clb.SetFont(font1) 1557 self.numchoices = len(ChoiceList) 1558 Sizer.Add(self.clb,1,wx.LEFT|wx.RIGHT|wx.EXPAND,10) 1559 Sizer.Add((-1,10)) 1560 # OK/Cancel buttons 1561 btnsizer = wx.StdDialogButtonSizer() 1562 if useOK: 1563 OKbtn = wx.Button(self, wx.ID_OK) 1564 OKbtn.SetDefault() 1565 btnsizer.AddButton(OKbtn) 1566 if useCANCEL: 1567 btn = wx.Button(self, wx.ID_CANCEL) 1568 btnsizer.AddButton(btn) 1569 btnsizer.Realize() 1570 Sizer.Add((-1,5)) 1571 Sizer.Add(btnsizer,0,wx.ALIGN_RIGHT,50) 1572 Sizer.Add((-1,20)) 1573 # OK done, let's get outa here 1574 self.SetSizer(Sizer) 1575 def GetSelection(self): 1576 'Returns the index of the selected choice' 1577 i = self.clb.GetSelection() 1578 if i < 0 or i >= len(self.filterlist): 1579 return wx.NOT_FOUND 1580 return self.filterlist[i] 1581 def onChar(self,event): 1582 if self.timer.IsRunning(): 1583 self.timer.Stop() 1584 self.timer.Start(1000,oneShot=True) 1585 event.Skip() 1586 def Filter(self,event): 1587 txt = self.txt.GetValue() 1588 self.clb.Clear() 1589 self.Update() 1590 self.filterlist = [] 1591 if txt: 1592 txt = txt.lower() 1593 ChoiceList = [] 1594 for i,item in enumerate(self.ChoiceList): 1595 if item.lower().find(txt) != -1: 1596 ChoiceList.append(item) 1597 self.filterlist.append(i) 1598 else: 1599 self.filterlist = range(len(self.ChoiceList)) 1600 ChoiceList = self.ChoiceList 1601 self.clb.AppendItems(ChoiceList) 1602 def onDoubleClick(self,event): 1603 self.EndModal(wx.ID_OK) 1604 1605 ################################################################################ 1455 1606 1456 1607 def ItemSelector(ChoiceList, ParentFrame=None, -
TabularUnified trunk/GSASIIobj.py ¶
r1202 r1209 836 836 837 837 ''' 838 import re 839 import imp 838 840 import random as ran 839 841 import sys 840 842 import GSASIIpath 841 843 import GSASIImath as G2mth 844 import numpy as np 842 845 843 846 GSASIIpath.SetVersionNumber("$Revision$") … … 1098 1101 '''Return a string with a more complete description for a GSAS-II variable 1099 1102 1100 TODO: This will not handle rigid body parameters yet 1101 1102 :param str name: A full G2 variable name with 2 or 3 1103 colons (<p>:<h>:name[:<a>]) 1103 :param str name: A full G2 variable name with 2 or 3 or 4 1104 colons (<p>:<h>:name[:<a>] or <p>::RBname:<?>:<?>]) 1104 1105 1105 1106 :returns: a string with the description 1107 ''' 1108 s,l = VarDescr(varname) 1109 return s+": "+l 1110 1111 def VarDescr(varname): 1112 '''Return two strings with a more complete description for a GSAS-II variable 1113 1114 :param str name: A full G2 variable name with 2 or 3 or 4 1115 colons (<p>:<h>:name[:<a>] or <p>::RBname:<?>:<?>]) 1116 1117 :returns: (loc,meaning) where loc describes what item the variable is mapped 1118 (phase, histogram, etc.) and meaning describes what the variable does. 1106 1119 ''' 1107 1120 … … 1110 1123 return "invalid variable name ("+str(varname)+")!" 1111 1124 1112 if not l[ 4]:1113 l[ 4] = "(variable needs a definition!)"1125 if not l[-1]: 1126 l[-1] = "(variable needs a definition!)" 1114 1127 1115 1128 s = "" … … 1123 1136 else: 1124 1137 hlbl = 'Hist='+hlbl 1125 s = "Ph="+str(lbl)+" * "+str(hlbl)+": " 1138 s = "Ph="+str(lbl)+" * "+str(hlbl) 1139 elif l[4] is not None: # rigid body parameter 1140 lbl = ShortPhaseNames.get(l[0],'phase?') 1141 s = "Res #"+str(l[3])+" body #"+str(l[4])+" in "+str(lbl) 1126 1142 elif l[3] is not None: # atom parameter, 1127 1143 lbl = ShortPhaseNames.get(l[0],'phase?') … … 1130 1146 except KeyError: 1131 1147 albl = 'Atom?' 1132 s = "Atom "+str(albl)+" in "+str(lbl) +": "1148 s = "Atom "+str(albl)+" in "+str(lbl) 1133 1149 elif l[0] is not None: 1134 1150 lbl = ShortPhaseNames.get(l[0],'phase?') 1135 s = "Phase "+str(lbl) +": "1151 s = "Phase "+str(lbl) 1136 1152 elif l[1] is not None: 1137 1153 hlbl = ShortHistNames.get(l[1],'? #'+str(l[1])) … … 1142 1158 else: 1143 1159 hlbl = 'Hist='+hlbl 1144 s = str(hlbl) +": "1160 s = str(hlbl) 1145 1161 if not s: 1146 s = 'Global: ' 1147 s += l[4] 1148 return s 1162 s = 'Global' 1163 return s,l[-1] 1149 1164 1150 1165 def getVarDescr(varname): 1151 1166 '''Return a short description for a GSAS-II variable 1152 1167 1153 :param str name: A full G2 variable name with 2 or 3 1154 colons (<p>:<h>:name[:<a >])1168 :param str name: A full G2 variable name with 2 or 3 or 4 1169 colons (<p>:<h>:name[:<a1>][:<a2>]) 1155 1170 1156 :returns: a five element list as [`p`,`h`,`name`,`a`,`description`],1157 where `p`, `h`, `a ` are str values or `None`, for the phase number,1171 :returns: a six element list as [`p`,`h`,`name`,`a1`,`a2`,`description`], 1172 where `p`, `h`, `a1`, `a2` are str values or `None`, for the phase number, 1158 1173 the histogram number and the atom number; `name` will always be 1159 1174 an str; and `description` is str or `None`. … … 1163 1178 l = varname.split(':') 1164 1179 if len(l) == 3: 1180 l += [None,None] 1181 elif len(l) == 4: 1165 1182 l += [None] 1166 if len(l) != 4:1183 elif len(l) != 5: 1167 1184 return None 1168 for i in (0,1,3 ):1185 for i in (0,1,3,4): 1169 1186 if l[i] == "": 1170 1187 l[i] = None … … 1193 1210 1194 1211 ''' 1195 import re1196 1212 if reVarDesc: return # already done 1197 1213 for key,value in { … … 1225 1241 'nDebye' : 'Debye model background corr. terms', 1226 1242 'nPeaks' : 'Fixed peak background corr. terms', 1243 'RBV.*' : 'Vector rigid body parameter', 1244 'RBR.*' : 'Residue rigid body parameter', 1245 'RBRO([aijk])' : 'Residue rigid body orientation parameter', 1246 'RBRP([xyz])' : 'Residue rigid body position parameter', 1247 'RBRTr;.*' : 'Residue rigid body torsion parameter', 1248 'RBR([TLS])([123AB][123AB])' : 'Residue rigid body group disp. param.', 1227 1249 # Global vars (::<var>) 1228 1250 }.items(): … … 1244 1266 return m.expand(reVarDesc[key]) 1245 1267 return None 1268 1269 def GenWildCard(varlist): 1270 '''Generate wildcard versions of G2 variables. These introduce '*' 1271 for a phase, histogram or atom number (but only for one of these 1272 fields) but only when there is more than one matching variable in the 1273 input variable list. So if the input is this:: 1274 1275 varlist = ['0::AUiso:0', '0::AUiso:1', '1::AUiso:0'] 1276 1277 then the output will be this:: 1278 1279 wildList = ['*::AUiso:0', '0::AUiso:*'] 1280 1281 :param list varlist: an input list of GSAS-II variable names 1282 (such as 0::AUiso:0) 1283 1284 :returns: wildList, the generated list of wild card variable names. 1285 ''' 1286 wild = [] 1287 for i in (0,1,3): 1288 currentL = varlist[:] 1289 while currentL: 1290 item1 = currentL.pop(0) 1291 i1splt = item1.split(':') 1292 if i >= len(i1splt): continue 1293 if i1splt[i]: 1294 nextL = [] 1295 i1splt[i] = '[0-9]+' 1296 rexp = re.compile(':'.join(i1splt)) 1297 matchlist = [item1] 1298 for nxtitem in currentL: 1299 if rexp.match(nxtitem): 1300 matchlist += [nxtitem] 1301 else: 1302 nextL.append(nxtitem) 1303 if len(matchlist) > 1: 1304 i1splt[i] = '*' 1305 wild.append(':'.join(i1splt)) 1306 currentL = nextL 1307 return wild 1308 1309 def LookupWildCard(varname,varlist): 1310 '''returns a list of variable names from list varname 1311 that match wildcard name in varname 1312 1313 :param str varname: a G2 variable name containing a wildcard 1314 (such as \*::var) 1315 :param list varlist: the list of all variable names used in 1316 the current project 1317 :returns: a list of matching GSAS-II variables (may be empty) 1318 ''' 1319 rexp = re.compile(varname.replace('*','[0-9]+')) 1320 return sorted([var for var in varlist if rexp.match(var)]) 1321 1246 1322 1247 1323 def _lookup(dic,key): … … 1262 1338 Note that :func:`LoadID` should be used to (re)load the current Ids 1263 1339 before creating or later using the G2VarObj object. 1340 1341 TODO: This does not handle rigid body variables at present 1264 1342 1265 1343 A :class:`G2VarObj` object can be created with a single parameter: … … 1406 1484 print 'atomDict', self.IDdict['atoms'] 1407 1485 1486 #========================================================================== 1487 # shortcut routines 1488 exp = np.exp 1489 sind = lambda x: np.sin(x*np.pi/180.) 1490 tand = lambda x: np.tan(x*np.pi/180.) 1491 cosd = lambda x: np.cos(x*np.pi/180.) 1492 sind = sin = s = lambda x: np.sin(x*np.pi/180.) 1493 cosd = cos = c = lambda x: np.cos(x*np.pi/180.) 1494 tand = tan = t = lambda x: np.tan(x*np.pi/180.) 1495 sqrt = sq = lambda x: np.sqrt(x) 1496 pi = lambda: np.pi 1497 class ExpressionObj(object): 1498 '''Defines an object with a user-defined expression, to be used for 1499 secondary fits or restraints. Object is created null, but is changed 1500 using :meth:`LoadExpression`. This contains only the minimum 1501 information that needs to be stored to save and load the expression 1502 and how it is mapped to GSAS-II variables. 1503 ''' 1504 def __init__(self): 1505 self.expression = '' 1506 'The expression as a text string' 1507 self.assgnVars = {} 1508 '''A dict where keys are label names in the expression mapping to a GSAS-II 1509 variable. The value is a list with a G2 variable name and derivative step size. 1510 Note that the G2 variable name may contain a wild-card and correspond to 1511 multiple values. 1512 ''' 1513 self.freeVars = {} 1514 '''A dict where keys are label names in the expression mapping to a free 1515 parameter. The value is a list with: 1516 1517 * a name assigned to the parameter 1518 * a value for to the parameter 1519 * a derivative step size and 1520 * a flag to determine if the variable is refined. 1521 ''' 1522 1523 self.lastError = ('','') 1524 '''Shows last encountered error in processing expression 1525 (list of 1-3 str values)''' 1526 1527 def LoadExpression(self,expr,exprVarLst,varSelect,varName,varValue,varStep,varRefflag): 1528 '''Load the expression and associated settings into the object. Raises 1529 an exception if the expression is not parsed, if not all functions 1530 are defined or if not all needed parameter labels in the expression 1531 are defined. 1532 1533 This will not test if the variable referenced in these definitions 1534 are actually in the parameter dictionary. This is checked when the 1535 computation for the expression is done in :meth:`SetupCalc`. 1536 1537 :param str expr: the expression 1538 :param list exprVarLst: parameter labels found in the expression 1539 :param dict varSelect: this will be 0 for Free parameters 1540 and non-zero for expression labels linked to G2 variables. 1541 :param dict varName: Defines a name (str) associated with each free parameter 1542 :param dict varValue: Defines a value (float) associated with each free parameter 1543 :param dict varStep: Defines a derivative step size (float) for each 1544 parameter labels found in the expression 1545 :param dict varRefflag: Defines a refinement flag (bool) 1546 associated with each free parameter 1547 ''' 1548 self.expression = expr 1549 self.compiledExpr = None 1550 self.freeVars = {} 1551 self.assgnVars = {} 1552 for v in exprVarLst: 1553 if varSelect[v] == 0: 1554 self.freeVars[v] = [ 1555 varName.get(v), 1556 varValue.get(v), 1557 varStep.get(v), 1558 varRefflag.get(v), 1559 ] 1560 else: 1561 self.assgnVars[v] = [ 1562 varName[v], 1563 varStep.get(v), 1564 ] 1565 self.CheckVars() 1566 1567 def EditExpression(self,exprVarLst,varSelect,varName,varValue,varStep,varRefflag): 1568 '''Load the expression and associated settings from the object into 1569 arrays used for editing. 1570 1571 :param list exprVarLst: parameter labels found in the expression 1572 :param dict varSelect: this will be 0 for Free parameters 1573 and non-zero for expression labels linked to G2 variables. 1574 :param dict varName: Defines a name (str) associated with each free parameter 1575 :param dict varValue: Defines a value (float) associated with each free parameter 1576 :param dict varStep: Defines a derivative step size (float) for each 1577 parameter labels found in the expression 1578 :param dict varRefflag: Defines a refinement flag (bool) 1579 associated with each free parameter 1580 1581 :returns: the expression as a str 1582 ''' 1583 for v in self.freeVars: 1584 varSelect[v] = 0 1585 varName[v] = self.freeVars[v][0] 1586 varValue[v] = self.freeVars[v][1] 1587 varStep[v] = self.freeVars[v][2] 1588 varRefflag[v] = self.freeVars[v][3] 1589 for v in self.assgnVars: 1590 varSelect[v] = 1 1591 varName[v] = self.assgnVars[v][0] 1592 varStep[v] = self.assgnVars[v][1] 1593 return self.expression 1594 1595 def GetVaried(self): 1596 'Returns the names of the free parameters that will be refined' 1597 return ["::"+self.freeVars[v][0] for v in self.freeVars if self.freeVars[v][3]] 1598 1599 def CheckVars(self): 1600 '''Check that the expression can be parsed, all functions are 1601 defined and that input loaded into the object is internally 1602 consistent. If not an Exception is raised. 1603 1604 :returns: a dict with references to packages needed to 1605 find functions referenced in the expression. 1606 ''' 1607 ret = self.ParseExpression(self.expression) 1608 if not ret: 1609 raise Exception("Expression parse error") 1610 exprLblList,fxnpkgdict = ret 1611 # check each var used in expression is defined 1612 defined = self.assgnVars.keys() + self.freeVars.keys() 1613 notfound = [] 1614 for var in exprLblList: 1615 if var not in defined: 1616 notfound.append(var) 1617 if notfound: 1618 msg = 'Not all variables defined' 1619 msg1 = 'The following variables were not defined: ' 1620 msg2 = '' 1621 for var in notfound: 1622 if msg: msg += ', ' 1623 msg += var 1624 self.lastError = (msg1,' '+msg2) 1625 raise Exception(msg) 1626 return fxnpkgdict 1627 1628 def ParseExpression(self,expr): 1629 '''Parse an expression and return a dict of called functions and 1630 the variables used in the expression. Returns None in case an error 1631 is encountered. If packages are referenced in functions, they are loaded 1632 and the functions are looked up into the modules global 1633 workspace. 1634 1635 Note that no changes are made to the object other than 1636 saving an error message, so that this can be used for testing prior 1637 to the save. 1638 1639 :returns: a list of variables used variables 1640 ''' 1641 self.lastError = ('','') 1642 import ast 1643 def FindFunction(f): 1644 '''Find the object corresponding to function f 1645 :param str f: a function name such as 'numpy.exp' 1646 :returns: (pkgdict,pkgobj) where pkgdict contains a dict 1647 that defines the package location(s) and where pkgobj 1648 defines the object associated with the function. 1649 If the function is not found, pkgobj is None. 1650 ''' 1651 df = f.split('.') 1652 pkgdict = {} 1653 # no listed package, try in current namespace 1654 if len(df) == 1: 1655 try: 1656 fxnobj = eval(f) 1657 return pkgdict,fxnobj 1658 except (AttributeError, NameError): 1659 return None 1660 else: 1661 try: 1662 fxnobj = eval(f) 1663 pkgdict[df[0]] = eval(df[0]) 1664 return pkgdict,fxnobj 1665 except (AttributeError, NameError): 1666 pass 1667 # includes a package, lets try to load the packages 1668 pkgname = '' 1669 path = sys.path 1670 for pkg in f.split('.')[:-1]: # if needed, descend down the tree 1671 if pkgname: 1672 pkgname += '.' + pkg 1673 else: 1674 pkgname = pkg 1675 fp = None 1676 try: 1677 fp, fppath,desc = imp.find_module(pkg,path) 1678 pkgobj = imp.load_module(pkg,fp,fppath,desc) 1679 pkgdict[pkgname] = pkgobj 1680 path = [fppath] 1681 except Exception as msg: 1682 print('load of '+pkgname+' failed with error='+str(msg)) 1683 return {},None 1684 finally: 1685 if fp: fp.close() 1686 try: 1687 #print 'before',pkgdict.keys() 1688 fxnobj = eval(f,globals(),pkgdict) 1689 #print 'after 1',pkgdict.keys() 1690 #fxnobj = eval(f,pkgdict) 1691 #print 'after 2',pkgdict.keys() 1692 return pkgdict,fxnobj 1693 except: 1694 continue 1695 return None # not found 1696 def ASTtransverse(node,fxn=False): 1697 '''Transverse a AST-parsed expresson, compiling a list of variables 1698 referenced in the expression. This routine is used recursively. 1699 1700 :returns: varlist,fxnlist where 1701 varlist is a list of referenced variable names and 1702 fxnlist is a list of used functions 1703 ''' 1704 varlist = [] 1705 fxnlist = [] 1706 if isinstance(node, list): 1707 for b in node: 1708 v,f = ASTtransverse(b,fxn) 1709 varlist += v 1710 fxnlist += f 1711 elif isinstance(node, ast.AST): 1712 for a, b in ast.iter_fields(node): 1713 if isinstance(b, ast.AST): 1714 if a == 'func': 1715 fxnlist += ['.'.join(ASTtransverse(b,True)[0])] 1716 continue 1717 v,f = ASTtransverse(b,fxn) 1718 varlist += v 1719 fxnlist += f 1720 elif isinstance(b, list): 1721 v,f = ASTtransverse(b,fxn) 1722 varlist += v 1723 fxnlist += f 1724 elif node.__class__.__name__ == "Name": 1725 varlist += [b] 1726 elif fxn and node.__class__.__name__ == "Attribute": 1727 varlist += [b] 1728 return varlist,fxnlist 1729 try: 1730 exprast = ast.parse(expr) 1731 except SyntaxError as err: 1732 s = '' 1733 for i in traceback.format_exc().splitlines()[-3:-1]: 1734 if s: s += "\n" 1735 s += str(i) 1736 self.lastError = ("Error parsing expression:",s) 1737 return 1738 # find the variables & functions 1739 v,f = ASTtransverse(exprast) 1740 varlist = sorted(list(set(v))) 1741 fxnlist = list(set(f)) 1742 pkgdict = {} 1743 # check the functions are defined 1744 for fxn in fxnlist: 1745 fxndict,fxnobj = FindFunction(fxn) 1746 if not fxnobj: 1747 self.lastError = ("Error: Invalid function",fxn, 1748 "is not defined") 1749 return 1750 if not hasattr(fxnobj,'__call__'): 1751 self.lastError = ("Error: Not a function.",fxn, 1752 "cannot be called as a function") 1753 return 1754 pkgdict.update(fxndict) 1755 return varlist,pkgdict 1756 1757 #========================================================================== 1758 class ExpressionCalcObj(object): 1759 '''An object used to evaluate an expression from a :class:`ExpressionObj` 1760 object. 1761 1762 :param ExpressionObj exprObj: a :class:`~ExpressionObj` expression object with 1763 an expression string and mappings for the parameter labels in that object. 1764 ''' 1765 def __init__(self,exprObj): 1766 self.eObj = exprObj 1767 'The expression and mappings; a :class:`ExpressionObj` object' 1768 self.compiledExpr = None 1769 'The expression as compiled byte-code' 1770 self.exprDict = {} 1771 '''dict that defines values for labels used in expression and packages 1772 referenced by functions 1773 ''' 1774 self.derivStep = {} 1775 '''Contains step sizes for derivatives for variables used in the expression; 1776 if a variable is not included in this the derivatives will be computed as zero 1777 ''' 1778 self.lblLookup = {} 1779 '''Lookup table that specifies the expression label name that is 1780 tied to a particular GSAS-II parameters in the parmDict. 1781 ''' 1782 self.fxnpkgdict = {} 1783 '''a dict with references to packages needed to 1784 find functions referenced in the expression. 1785 ''' 1786 self.varLookup = {} 1787 '''Lookup table that specifies the GSAS-II variable(s) 1788 indexed by the expression label name. (Used for only for diagnostics 1789 not evaluation of expression.) 1790 ''' 1791 1792 def SetupCalc(self,parmDict): 1793 '''Do all preparations to use the expression for computation. 1794 Adds the free parameter values to the parameter dict (parmDict). 1795 ''' 1796 self.fxnpkgdict = self.eObj.CheckVars() 1797 # all is OK, compile the expression 1798 self.compiledExpr = compile(self.eObj.expression,'','eval') 1799 1800 # look at first value in parmDict to determine its type 1801 parmsInList = True 1802 for key in parmDict: 1803 val = parmDict[key] 1804 if isinstance(val, basestring): 1805 parmsInList = False 1806 break 1807 try: # check if values are in lists 1808 val = parmDict[key][0] 1809 except TypeError: 1810 parmsInList = False 1811 break 1812 1813 # set up the dicts needed to speed computations 1814 self.exprDict = {} 1815 self.derivStep = {} 1816 self.lblLookup = {} 1817 self.varLookup = {} 1818 for v in self.eObj.freeVars: 1819 varname = self.eObj.freeVars[v][0] 1820 varname = "::" + varname.lstrip(':').replace(' ','_').replace(':',';') 1821 self.lblLookup[varname] = v 1822 self.varLookup[v] = varname 1823 if parmsInList: 1824 parmDict[varname] = [self.eObj.freeVars[v][1],self.eObj.freeVars[v][3]] 1825 else: 1826 parmDict[varname] = self.eObj.freeVars[v][1] 1827 self.exprDict[v] = self.eObj.freeVars[v][1] 1828 if self.eObj.freeVars[v][3]: 1829 self.derivStep[varname] = self.eObj.freeVars[v][2] 1830 for v in self.eObj.assgnVars: 1831 step = self.eObj.assgnVars[v][1] 1832 varname = self.eObj.assgnVars[v][0] 1833 if '*' in varname: 1834 varlist = LookupWildCard(varname,parmDict.keys()) 1835 if len(varlist) == 0: 1836 raise Exception,"No variables match "+str(v) 1837 for var in varlist: 1838 self.lblLookup[var] = v 1839 self.derivStep[var] = np.array( 1840 [step if var1 == var else 0 for var1 in varlist] 1841 ) 1842 if parmsInList: 1843 self.exprDict[v] = np.array([parmDict[var][0] for var in varlist]) 1844 else: 1845 self.exprDict[v] = np.array([parmDict[var] for var in varlist]) 1846 self.varLookup[v] = [var for var in varlist] 1847 elif varname in parmDict: 1848 self.lblLookup[varname] = v 1849 self.varLookup[v] = varname 1850 if parmsInList: 1851 self.exprDict[v] = parmDict[varname][0] 1852 else: 1853 self.exprDict[v] = parmDict[varname] 1854 self.derivStep[varname] = step 1855 else: 1856 raise Exception,"No value for variable "+str(v) 1857 self.exprDict.update(self.fxnpkgdict) 1858 1859 def EvalExpression(self): 1860 '''Evaluate an expression. Note that the expression 1861 and mapping are taken from the :class:`ExpressionObj` expression object 1862 and the parameter values were specified in :meth:`SetupCalc`. 1863 :returns: a single value for the expression. If parameter 1864 values are arrays (for example, from wild-carded variable names), 1865 the sum of the resulting expression is return. 1866 1867 For example, if the expression is ``'A*B'``, 1868 where A is 2.0 and B maps to ``'1::Afrac:*'``, which evaluates to:: 1869 1870 [0.5, 1, 0.5] 1871 1872 then the result will be ``4.0``. 1873 ''' 1874 if self.compiledExpr is None: 1875 raise Exception,"EvalExpression called before SetupCalc" 1876 val = eval(self.compiledExpr,globals(),self.exprDict) 1877 if not np.isscalar(val): 1878 val = np.sum(val) 1879 return val 1880 1881 def EvalDeriv(self,varname): 1882 '''Evaluate the expression derivative with respect to a 1883 GSAS-II variable name. 1884 1885 :param str varname: a G2 variable name (will not have a wild-card) 1886 :returns: the derivative 1887 ''' 1888 if varname not in self.derivStep: return 0.0 1889 if varname not in self.lblLookup: return 0.0 1890 step = self.derivStep[varname] 1891 lbl = self.lblLookup[varname] # what label does this map to in expression? 1892 origval = self.exprDict[lbl] 1893 1894 self.exprDict[lbl] = origval + step 1895 val1 = eval(self.compiledExpr,globals(),self.exprDict) 1896 1897 self.exprDict[lbl] = origval - step 1898 val2 = eval(self.compiledExpr,globals(),self.exprDict) 1899 1900 self.exprDict[lbl] = origval # reset back to central value 1901 1902 val = (val1 - val2) / (2.*np.max(step)) 1903 if not np.isscalar(val): 1904 val = np.sum(val) 1905 return val 1906 1907 if __name__ == "__main__": 1908 # test equation evaluation 1909 def showEQ(calcobj): 1910 print 50*'=' 1911 print calcobj.eObj.expression,'=',calcobj.EvalExpression() 1912 for v in sorted(calcobj.varLookup): 1913 print " ",v,'=',calcobj.exprDict[v],'=',calcobj.varLookup[v] 1914 print ' Derivatives' 1915 for v in calcobj.derivStep.keys(): 1916 print ' d(Expr)/d('+v+') =',calcobj.EvalDeriv(v) 1917 1918 obj = ExpressionObj() 1919 1920 obj.expression = "A*np.exp(B)" 1921 obj.assgnVars = {'B': ['0::Afrac:1', 0.0001]} 1922 obj.freeVars = {'A': [u'A', 0.5, 0.0001, True]} 1923 #obj.CheckVars() 1924 parmDict2 = {'0::Afrac:0':[0.0,True], '0::Afrac:1': [1.0,False]} 1925 calcobj = ExpressionCalcObj(obj) 1926 calcobj.SetupCalc(parmDict2) 1927 showEQ(calcobj) 1928 1929 obj.expression = "A*np.exp(B)" 1930 obj.assgnVars = {'B': ['0::Afrac:*', 0.0001]} 1931 obj.freeVars = {'A': [u'Free Prm A', 0.5, 0.0001, True]} 1932 #obj.CheckVars() 1933 parmDict1 = {'0::Afrac:0':1.0, '0::Afrac:1': 1.0} 1934 calcobj = ExpressionCalcObj(obj) 1935 calcobj.SetupCalc(parmDict1) 1936 showEQ(calcobj) 1937 1938 calcobj.SetupCalc(parmDict2) 1939 showEQ(calcobj)
Note: See TracChangeset
for help on using the changeset viewer.