source: trunk/GSASIIconstrGUI.py @ 3056

Last change on this file since 3056 was 3056, checked in by toby, 5 years ago

expand error reporting with constraints (no fixes yet to prevent them)

  • Property svn:eol-style set to native
  • Property svn:keywords set to Date Author Revision URL Id
File size: 92.2 KB
Line 
1# -*- coding: utf-8 -*-
2#GSASIIconstrGUI - constraint GUI routines
3########### SVN repository information ###################
4# $Date: 2017-09-08 19:17:29 +0000 (Fri, 08 Sep 2017) $
5# $Author: toby $
6# $Revision: 3056 $
7# $URL: trunk/GSASIIconstrGUI.py $
8# $Id: GSASIIconstrGUI.py 3056 2017-09-08 19:17:29Z toby $
9########### SVN repository information ###################
10'''
11*GSASIIconstrGUI: Constraint GUI routines*
12------------------------------------------
13
14Used to define constraints and rigid bodies.
15
16'''
17import sys
18import wx
19import wx.grid as wg
20import random as ran
21import numpy as np
22import numpy.ma as ma
23import numpy.linalg as nl
24import os.path
25import GSASIIpath
26GSASIIpath.SetVersionNumber("$Revision: 3056 $")
27import GSASIIElem as G2elem
28import GSASIIElemGUI as G2elemGUI
29import GSASIIstrIO as G2stIO
30import GSASIImapvars as G2mv
31import GSASIImath as G2mth
32import GSASIIlattice as G2lat
33import GSASIIdataGUI as G2gd
34import GSASIIctrlGUI as G2G
35import GSASIIplot as G2plt
36import GSASIIobj as G2obj
37import GSASIIspc as G2spc
38VERY_LIGHT_GREY = wx.Colour(235,235,235)
39
40class ConstraintDialog(wx.Dialog):
41    '''Window to edit Constraint values
42    '''
43    def __init__(self,parent,title,text,data,separator='*',varname="",varyflag=False):
44        wx.Dialog.__init__(self,parent,-1,'Edit '+title, 
45            pos=wx.DefaultPosition,style=wx.DEFAULT_DIALOG_STYLE)
46        self.data = data[:]
47        self.newvar = [varname,varyflag]
48        panel = wx.Panel(self)
49        mainSizer = wx.BoxSizer(wx.VERTICAL)
50        topLabl = wx.StaticText(panel,-1,text)
51        mainSizer.Add((10,10),1)
52        mainSizer.Add(topLabl,0,wx.ALIGN_CENTER_VERTICAL|wx.LEFT,10)
53        mainSizer.Add((10,10),1)
54        dataGridSizer = wx.FlexGridSizer(cols=3,hgap=2,vgap=2)
55        self.OkBtn = wx.Button(panel,wx.ID_OK)
56        for id in range(len(self.data)):
57            lbl1 = lbl = str(self.data[id][1])
58            if lbl[-1] != '=': lbl1 = lbl + ' ' + separator + ' '
59            name = wx.StaticText(panel,wx.ID_ANY,lbl1,
60                                 style=wx.ALIGN_RIGHT)
61            scale = G2G.ValidatedTxtCtrl(panel,self.data[id],0,
62                                          typeHint=float,
63                                          OKcontrol=self.DisableOK)
64            dataGridSizer.Add(name,0,wx.LEFT|wx.RIGHT|wx.ALIGN_CENTER_VERTICAL,5)
65            dataGridSizer.Add(scale,0,wx.RIGHT,3)
66            if ':' in lbl:
67                dataGridSizer.Add(
68                    wx.StaticText(panel,-1,G2obj.fmtVarDescr(lbl)),
69                    0,wx.RIGHT|wx.ALIGN_CENTER_VERTICAL,3)
70            else:
71                dataGridSizer.Add((-1,-1))
72        if title == 'New Variable':
73            name = wx.StaticText(panel,wx.ID_ANY,"New variable's\nname (optional)",
74                                 style=wx.ALIGN_CENTER)
75            scale = G2G.ValidatedTxtCtrl(panel,self.newvar,0,
76                                          typeHint=str,notBlank=False)
77            dataGridSizer.Add(name,0,wx.LEFT|wx.RIGHT|wx.ALIGN_CENTER_VERTICAL,5)
78            dataGridSizer.Add(scale,0,wx.RIGHT|wx.ALIGN_CENTER_VERTICAL,3)
79            self.refine = wx.CheckBox(panel,label='Refine?')
80            self.refine.SetValue(self.newvar[1]==True)
81            self.refine.Bind(wx.EVT_CHECKBOX, self.OnCheckBox)
82            dataGridSizer.Add(self.refine,0,wx.RIGHT|wx.ALIGN_CENTER_VERTICAL,3)
83           
84        mainSizer.Add(dataGridSizer,0,wx.EXPAND)
85        self.OkBtn.Bind(wx.EVT_BUTTON, self.OnOk)
86        self.OkBtn.SetDefault()
87        cancelBtn = wx.Button(panel,wx.ID_CANCEL)
88        cancelBtn.Bind(wx.EVT_BUTTON, self.OnCancel)
89        btnSizer = wx.BoxSizer(wx.HORIZONTAL)
90        btnSizer.Add((20,20),1)
91        btnSizer.Add(self.OkBtn)
92        btnSizer.Add((20,20),1)
93        btnSizer.Add(cancelBtn)
94        btnSizer.Add((20,20),1)
95
96        mainSizer.Add(btnSizer,0,wx.EXPAND|wx.BOTTOM|wx.TOP, 10)
97        panel.SetSizer(mainSizer)
98        panel.Fit()
99        self.Fit()
100        self.CenterOnParent()
101       
102    def DisableOK(self,setting):
103        if setting:
104            self.OkBtn.Enable()
105        else:
106            self.OkBtn.Disable()
107
108    def OnCheckBox(self,event):
109        self.newvar[1] = self.refine.GetValue()
110
111    def OnOk(self,event):
112        parent = self.GetParent()
113        parent.Raise()
114        self.EndModal(wx.ID_OK)             
115
116    def OnCancel(self,event):
117        parent = self.GetParent()
118        parent.Raise()
119        self.EndModal(wx.ID_CANCEL)             
120
121    def GetData(self):
122        return self.data
123       
124################################################################################
125#####  Constraints
126################################################################################           
127       
128def UpdateConstraints(G2frame,data):
129    '''Called when Constraints tree item is selected.
130    Displays the constraints in the data window
131    '''
132    if not data:
133        data.update({'Hist':[],'HAP':[],'Phase':[],'Global':[]})       #empty dict - fill it
134    if 'Global' not in data:                                            #patch
135        data['Global'] = []
136    # DEBUG code ########################################
137    #import GSASIIconstrGUI
138    #reload(GSASIIconstrGUI)
139    #reload(G2obj)
140    #reload(G2stIO)
141    #import GSASIIstrMain
142    #reload(GSASIIstrMain)   
143    #reload(G2mv)
144    #reload(G2gd)
145    ###################################################
146    Histograms,Phases = G2frame.GetUsedHistogramsAndPhasesfromTree()
147    G2obj.IndexAllIds(Histograms,Phases)
148    ##################################################################################
149    # patch: convert old-style (str) variables in constraints to G2VarObj objects
150    for key,value in data.items():
151        if key.startswith('_'): continue
152        j = 0
153        for cons in value:
154            #print cons             # DEBUG
155            for i in range(len(cons[:-3])):
156                if type(cons[i][1]) is str:
157                    cons[i][1] = G2obj.G2VarObj(cons[i][1])
158                    j += 1
159        if j:
160            print str(key) + ': '+str(j)+' variable(s) as strings converted to objects'
161    ##################################################################################
162    rigidbodyDict = G2frame.GPXtree.GetItemPyData(
163        G2gd.GetGPXtreeItemId(G2frame,G2frame.root,'Rigid bodies'))
164    rbIds = rigidbodyDict.get('RBIds',{'Vector':[],'Residue':[]})
165    rbVary,rbDict = G2stIO.GetRigidBodyModels(rigidbodyDict,Print=False)
166    badPhaseParms = ['Ax','Ay','Az','Amul','AI/A','Atype','SHorder','mV0','mV1','mV2','waveType','Vol','isMag',]
167    globalList = rbDict.keys()
168    globalList.sort()
169    try:
170        AtomDict = dict([Phases[phase]['pId'],Phases[phase]['Atoms']] for phase in Phases)
171    except KeyError:
172        G2frame.ErrorDialog('Constraint Error','Constraints cannot be set until a cycle of least squares'+
173                            'has been run.\nWe suggest you refine a scale factor.')
174        return
175
176    # create a list of the phase variables
177    Natoms,atomIndx,phaseVary,phaseDict,pawleyLookup,FFtable,BLtable,MFtable,maxSSwave = G2stIO.GetPhaseData(Phases,rbIds=rbIds,Print=False)
178    phaseList = []
179    for item in phaseDict:
180        if item.split(':')[2] not in badPhaseParms:
181            phaseList.append(item)
182    phaseList.sort()
183    phaseAtNames = {}
184    phaseAtTypes = {}
185    TypeList = []
186    for item in phaseList:
187        Split = item.split(':')
188        if Split[2][:2] in ['AU','Af','dA','AM']:
189            Id = int(Split[0])
190            phaseAtNames[item] = AtomDict[Id][int(Split[3])][0]
191            phaseAtTypes[item] = AtomDict[Id][int(Split[3])][1]
192            if phaseAtTypes[item] not in TypeList:
193                TypeList.append(phaseAtTypes[item])
194        else:
195            phaseAtNames[item] = ''
196            phaseAtTypes[item] = ''
197             
198    # create a list of the hist*phase variables; include wildcards here
199    hapVary,hapDict,controlDict = G2stIO.GetHistogramPhaseData(Phases,Histograms,Print=False)
200    hapList = [i for i in hapDict.keys() if i.split(':')[2] not in ('Type',)]
201    hapList.sort()
202    wildList = [] # list of variables with "*" for histogram number
203    for i in hapList:
204        s = i.split(':')
205        if s[1] == "": continue
206        s[1] = '*'
207        sj = ':'.join(s)
208        if sj not in wildList: wildList.append(sj)
209    #wildList.sort() # unneeded
210    hapList += wildList
211    histVary,histDict,controlDict = G2stIO.GetHistogramData(Histograms,Print=False)
212    histList = []
213    for item in histDict:
214        if item.split(':')[2] not in ['Omega','Type','Chi','Phi',
215                                      'Azimuth','Gonio. radius',
216                                      'Lam1','Lam2','Back','Temperature','Pressure',
217                                      'FreePrm1','FreePrm2','FreePrm3',
218                                      ]:
219            histList.append(item)
220    histList.sort()
221    wildList = []
222    # for i in histList: # any reason to have this for hist constraints?
223    #     s = i.split(':')
224    #     if s[1] == "": continue
225    #     s[1] = '*'
226    #     sj = ':'.join(s)
227    #     if sj not in wildList: wildList.append(sj)
228    # histList += wildList
229    Indx = {}
230    G2frame.Page = [0,'phs']
231       
232    def FindEquivVarb(name,nameList):
233        'Creates a list of variables appropriate to constrain with name'
234        outList = []
235        #phlist = []
236        items = name.split(':')
237        namelist = [items[2],]
238        if 'dA' in name:
239            namelist = ['dAx','dAy','dAz']
240        elif 'AU' in name:
241            namelist = ['AUiso','AU11','AU22','AU33','AU12','AU13','AU23']
242        elif 'AM' in name:
243            namelist = ['AMx','AMy','AMz']
244        elif items[-1] in ['A0','A1','A2','A3','A4','A5']:
245            namelist = ['A0','A1','A2','A3','A4','A5']
246        elif items[-1] in ['D11','D22','D33','D12','D13','D23']:
247            namelist = ['D11','D22','D33','D12','D13','D23']
248        elif 'Tm' in name:
249            namelist = ['Tmin','Tmax']
250        elif 'RB' in name:
251            rbfx = 'RB'+items[2][2]
252            if 'T' in name and 'Tr' not in name:
253                namelist = [rbfx+'T11',rbfx+'T22',rbfx+'T33',rbfx+'T12',rbfx+'T13',rbfx+'T23']
254            if 'L' in name:
255                namelist = [rbfx+'L11',rbfx+'L22',rbfx+'L33',rbfx+'L12',rbfx+'L13',rbfx+'L23']
256            if 'S' in name:
257                namelist = [rbfx+'S12',rbfx+'S13',rbfx+'S21',rbfx+'S23',rbfx+'S31',rbfx+'S32',rbfx+'SAA',rbfx+'SBB']
258            if 'U' in name:
259                namelist = [rbfx+'U',]
260
261        for item in nameList:
262            keys = item.split(':')
263            #if keys[0] not in phlist:
264            #    phlist.append(keys[0])
265            if items[1] == '*' and keys[2] in namelist: # wildcard -- select only sequential options
266                keys[1] = '*'
267                mitem = ':'.join(keys)
268                if mitem == name: continue
269                if mitem not in outList: outList.append(mitem)
270            elif keys[2] in namelist and item != name:
271                outList.append(item)
272        return outList
273       
274    def SelectVarbs(page,FrstVarb,varList,legend,constType):
275        '''Select variables used in constraints after one variable has
276        been selected. This routine determines the appropriate variables to be
277        used based on the one that has been selected and asks for more to be added.
278
279        It then creates the constraint and adds it to the constraints list.
280       
281        Called from OnAddEquivalence, OnAddFunction & OnAddConstraint (all but
282        OnAddHold)
283
284        :param list page: defines calling page -- type of variables to be used
285        :parm GSASIIobj.G2VarObj FrstVarb: reference to first selected variable
286        :param list varList: list of other appropriate variables to select from
287        :param str legend: header for selection dialog
288        :param str constType: type of constraint to be generated
289        :returns: a constraint, as defined in
290          :ref:`GSASIIobj <Constraint_definitions_table>`
291        '''
292        choices = [[i]+list(G2obj.VarDescr(i)) for i in varList]
293        meaning = G2obj.getDescr(FrstVarb.name)
294        if not meaning:
295            meaning = "(no definition found!)"
296        l = str(FrstVarb).split(':')
297        # make lists of phases & histograms to iterate over
298        phaselist = [l[0]]
299        if l[0]:
300            phaselbl = ['phase #'+l[0]]
301            if len(Phases) > 1:
302                phaselist += ['all'] 
303                phaselbl += ['all phases']
304        else:
305            phaselbl = ['']
306        histlist = [l[1]]
307        if l[1] == '*':
308            pass
309        elif l[1]:
310            histlbl = ['histogram #'+l[1]]
311            if len(Histograms) > 1:
312                histlist += ['all']
313                histlbl += ['all histograms']
314                typ = Histograms[G2obj.LookupHistName(l[1])[0]]['Instrument Parameters'][0]['Type'][1]
315                i = 0
316                for hist in Histograms:
317                    if Histograms[hist]['Instrument Parameters'][0]['Type'][1] == typ: i += 1
318                if i > 1:
319                    histlist += ['all='+typ]
320                    histlbl += ['all '+typ+' histograms']
321        else:
322            histlbl = ['']
323        # make a list of equivalent parameter names
324        nameList = [FrstVarb.name]
325        for var in varList:
326            nam = var.split(":")[2]
327            if nam not in nameList: nameList += [nam]
328        # add "wild-card" names to the list of variables
329        if l[1] == '*':
330            pass
331        elif page[1] == 'phs':
332            if 'RB' in FrstVarb.name:
333                pass
334            elif FrstVarb.atom is None:
335                for nam in nameList:
336                    for ph,plbl in zip(phaselist,phaselbl):
337                        if plbl: plbl = 'For ' + plbl
338                        var = ph+"::"+nam
339                        if var == str(FrstVarb) or var in varList: continue
340                        varList += [var]
341                        choices.append([var,plbl,meaning])
342            else:
343                for nam in nameList:
344                    for ph,plbl in zip(phaselist,phaselbl):
345                        if plbl: plbl = ' in ' + plbl
346                        for atype in ['']+TypeList:
347                            if atype:
348                                albl = "For "+atype+" atoms"
349                                akey = "all="+atype                       
350                            else:
351                                albl = "For all atoms"
352                                akey = "all"
353                            var = ph+"::"+nam+":"+akey
354                            if var == str(FrstVarb) or var in varList: continue
355                            varList += [var]
356                            choices.append([var,albl+plbl,meaning])
357        elif page[1] == 'hap':
358            if FrstVarb.name == "Scale":
359                meaning = "Phase fraction"
360            for nam in nameList:
361                for ph,plbl in zip(phaselist,phaselbl):
362                    if plbl: plbl = 'For ' + plbl
363                    for hst,hlbl in zip(histlist,histlbl):
364                        if hlbl:
365                            if plbl:
366                                hlbl = ' in ' + hlbl
367                            else:
368                                hlbl = 'For ' + hlbl                               
369                            var = ph+":"+hst+":"+nam
370                            if var == str(FrstVarb) or var in varList: continue
371                            varList += [var]
372                            choices.append([var,plbl+hlbl,meaning])
373        elif page[1] == 'hst':
374            if FrstVarb.name == "Scale":
375                meaning = "Scale factor"
376            for nam in nameList:
377                for hst,hlbl in zip(histlist,histlbl):
378                    if hlbl:
379                        hlbl = 'For ' + hlbl                               
380                        var = ":"+hst+":"+nam
381                        if var == str(FrstVarb) or var in varList: continue
382                        varList += [var]
383                        choices.append([var,hlbl,meaning])
384        elif page[1] == 'glb':
385            pass
386        else:
387            raise Exception, 'Unknown constraint page '+ page[1]                   
388        if len(choices):
389            l1 = l2 = 1
390            for i1,i2,i3 in choices:
391                l1 = max(l1,len(i1))
392                l2 = max(l2,len(i2))
393            fmt = "{:"+str(l1)+"s} {:"+str(l2)+"s} {:s}"
394            atchoice = [fmt.format(*i1) for i1 in choices]
395            dlg = G2G.G2MultiChoiceDialog(
396                G2frame,legend,
397                'Constrain '+str(FrstVarb)+' with...',atchoice,
398                toggle=False,size=(625,400),monoFont=True)
399            dlg.CenterOnParent()
400            res = dlg.ShowModal()
401            Selections = dlg.GetSelections()[:]
402            dlg.Destroy()
403            if res != wx.ID_OK: return []
404            if len(Selections) == 0:
405                dlg = wx.MessageDialog(
406                    G2frame,
407                    'No variables were selected to include with '+str(FrstVarb),
408                    'No variables')
409                dlg.CenterOnParent()
410                dlg.ShowModal()
411                dlg.Destroy()
412                return []
413        else:
414            dlg = wx.MessageDialog(
415                G2frame,
416                'There are no appropriate variables to include with '+str(FrstVarb),
417                'No variables')
418            dlg.CenterOnParent()
419            dlg.ShowModal()
420            dlg.Destroy()
421            return []
422        # now process the variables provided by the user
423        varbs = [str(FrstVarb),] # list of selected variables
424        for sel in Selections:
425            var = varList[sel]
426            # phase(s) included
427            l = var.split(':')
428            if l[0] == "all":
429                phlist = [str(Phases[phase]['pId']) for phase in Phases]
430            else:
431                phlist = [l[0]]
432            # histogram(s) included
433            if l[1] == "all":
434                hstlist = [str(Histograms[hist]['hId']) for hist in Histograms]
435            elif '=' in l[1]:
436                htyp = l[1].split('=')[1]
437                hstlist = [str(Histograms[hist]['hId']) for hist in Histograms if 
438                           Histograms[hist]['Instrument Parameters'][0]['Type'][1] == htyp]
439            else:
440                hstlist = [l[1]]
441            if len(l) == 3:
442                for ph in phlist:
443                    for hst in hstlist:
444                        var = ph + ":" + hst + ":" + l[2]
445                        if var in varbs: continue
446                        varbs.append(var)
447            else: # constraints with atoms or rigid bodies
448                if len(l) == 5: # rigid body parameter
449                    var = ':'.join(l)
450                    if var in varbs: continue
451                    varbs.append(var)
452                elif l[3] == "all":
453                    for ph in phlist:
454                        key = G2obj.LookupPhaseName(l[0])[0]
455                        for hst in hstlist: # should be blank
456                            for iatm,at in enumerate(Phases[key]['Atoms']):
457                                var = ph + ":" + hst + ":" + l[2] + ":" + str(iatm)
458                                if var in varbs: continue
459                                varbs.append(var)
460                elif '=' in l[3]:
461                    for ph in phlist:
462                        key = G2obj.LookupPhaseName(l[0])[0]
463                        cx,ct,cs,cia = Phases[key]['General']['AtomPtrs']
464                        for hst in hstlist: # should be blank
465                            atyp = l[3].split('=')[1]
466                            for iatm,at in enumerate(Phases[key]['Atoms']):
467                                if at[ct] != atyp: continue
468                                var = ph + ":" + hst + ":" + l[2] + ":" + str(iatm)
469                                if var in varbs: continue
470                                varbs.append(var)
471                else:
472                    for ph in phlist:
473                        key = G2obj.LookupPhaseName(l[0])[0]
474                        for hst in hstlist: # should be blank
475                            var = ph + ":" + hst + ":" + l[2] + ":" + l[3]
476                            if var in varbs: continue
477                            varbs.append(var)
478        if len(varbs) >= 1 or 'constraint' in constType:
479            constr = [[1.0,FrstVarb]]
480            for item in varbs[1:]:
481                constr += [[1.0,G2obj.G2VarObj(item)]]
482            if 'equivalence' in constType:
483                return [constr+[None,None,'e']]
484            elif 'function' in constType:
485                return [constr+[None,False,'f']]
486            elif 'constraint' in constType:
487                return [constr+[1.0,None,'c']]
488            else:
489                raise Exception,'Unknown constraint type: '+str(constType)
490        else:
491            dlg = wx.MessageDialog(
492                G2frame,
493                'There are no selected variables to include with '+str(FrstVarb),
494                'No variables')
495            dlg.CenterOnParent()
496            dlg.ShowModal()
497            dlg.Destroy()
498        return []
499
500    def CheckAddedConstraint(newcons):
501        '''Check a new constraint that has just been input.
502        If there is an error display a message and give the user a
503        choice to keep or discard the last entry (why keep? -- they
504        may want to delete something else or edit multipliers).
505        Since the varylist is not available, no warning messages
506        should be generated.
507
508        :returns: True if constraint should be added
509        '''
510        allcons = []
511        for key in data:
512            if key.startswith('_'): continue
513            allcons += data[key]
514        allcons += newcons
515        if not len(allcons): return True
516        G2mv.InitVars()   
517        constDictList,fixedList,ignored = G2stIO.ProcessConstraints(allcons)
518        errmsg, warnmsg = G2mv.CheckConstraints('',constDictList,fixedList)
519        if errmsg:
520            res = G2frame.ErrorDialog('Constraint Error',
521                'Error with newly added constraint:\n'+errmsg+
522                '\n\nDiscard newly added constraint?',parent=G2frame,
523                wtype=wx.YES_NO)
524            return res != wx.ID_YES
525        elif warnmsg:
526            print 'Unexpected contraint warning:\n',warnmsg
527        return True
528
529    def CheckChangedConstraint():
530        '''Check all constraints after an edit has been made.
531        If there is an error display a message and give the user a
532        choice to keep or discard the last edit.
533        Since the varylist is not available, no warning messages
534        should be generated.
535       
536        :returns: True if the edit should be retained
537        '''
538        allcons = []
539        for key in data:
540            if key.startswith('_'): continue
541            allcons += data[key]
542        if not len(allcons): return True
543        G2mv.InitVars()   
544        constDictList,fixedList,ignored = G2stIO.ProcessConstraints(allcons)
545        errmsg, warnmsg = G2mv.CheckConstraints('',constDictList,fixedList)
546        if errmsg:
547            res = G2frame.ErrorDialog('Constraint Error',
548                'Error after editing constraint:\n'+errmsg+
549                '\n\nDiscard last constraint edit?',parent=G2frame,
550                wtype=wx.YES_NO)
551            return res != wx.ID_YES
552        elif warnmsg:
553            print 'Unexpected contraint warning:\n',warnmsg
554        return True
555             
556    def PageSelection(page):
557        'Decode page reference'
558        if page[1] == "phs":
559            vartype = "phase"
560            varList = phaseList
561            constrDictEnt = 'Phase'
562        elif page[1] == "hap":
563            vartype = "Histogram*Phase"
564            varList = hapList
565            constrDictEnt = 'HAP'
566        elif page[1] == "hst":
567            vartype = "Histogram"
568            varList = histList
569            constrDictEnt = 'Hist'
570        elif page[1] == "glb":
571            vartype = "Global"
572            varList = globalList
573            constrDictEnt = 'Global'
574        else:
575            raise Exception,'Should not happen!'
576        return vartype,varList,constrDictEnt
577
578    def OnAddHold(event):
579        '''Create a new Hold constraint
580
581        Hold constraints allows the user to select one variable (the list of available
582        variables depends on which tab is currently active).
583        '''
584        page = G2frame.Page
585        vartype,varList,constrDictEnt = PageSelection(page)
586        title1 = "Hold "+vartype+" variable"
587        if not varList:
588            G2frame.ErrorDialog('No variables','There are no variables of type '+vartype,
589                parent=G2frame)
590            return
591        l2 = l1 = 1
592        for i in varList:
593            l1 = max(l1,len(i))
594            loc,desc = G2obj.VarDescr(i)
595            l2 = max(l2,len(loc))
596        fmt = "{:"+str(l1)+"s} {:"+str(l2)+"s} {:s}"
597        varListlbl = [fmt.format(i,*G2obj.VarDescr(i)) for i in varList]
598        #varListlbl = ["("+i+") "+G2obj.fmtVarDescr(i) for i in varList]
599        legend = "Select variables to hold (Will not be varied, even if vary flag is set)"
600        dlg = G2G.G2MultiChoiceDialog(G2frame,
601            legend,title1,varListlbl,toggle=False,size=(625,400),monoFont=True)
602        dlg.CenterOnParent()
603        if dlg.ShowModal() == wx.ID_OK:
604            for sel in dlg.GetSelections():
605                Varb = varList[sel]
606                VarObj = G2obj.G2VarObj(Varb)
607                newcons = [[[0.0,VarObj],None,None,'h']]
608                if CheckAddedConstraint(newcons):
609                    data[constrDictEnt] += newcons
610        dlg.Destroy()
611        wx.CallAfter(OnPageChanged,None)
612       
613    def OnAddEquivalence(event):
614        '''add an Equivalence constraint'''
615        page = G2frame.Page
616        vartype,varList,constrDictEnt = PageSelection(page)
617        title1 = "Setup equivalent "+vartype+" variables"
618        title2 = "Select additional "+vartype+" variable(s) to be equivalent with "
619        if not varList:
620            G2frame.ErrorDialog('No variables','There are no variables of type '+vartype,
621                parent=G2frame)
622            return
623#        legend = "Select variables to make equivalent (only one of the variables will be varied when all are set to be varied)"
624        GetAddVars(page,title1,title2,varList,constrDictEnt,'equivalence')
625       
626    def OnAddAtomEquiv(event):
627        ''' Add equivalences between all parameters on atoms '''
628        page = G2frame.Page
629        vartype,varList,constrDictEnt = PageSelection(page)
630        title1 = "Setup equivalent atom variables"
631        title2 = "Select additional atoms(s) to be equivalent with "
632        if not varList:
633            G2frame.ErrorDialog('No variables','There are no variables of type '+vartype,
634                parent=G2frame)
635            return
636#        legend = "Select atoms to make equivalent (only one of the atom variables will be varied when all are set to be varied)"
637        GetAddAtomVars(page,title1,title2,varList,constrDictEnt,'equivalence')
638       
639    def OnAddRiding(event):
640        ''' Add riding equivalences between all parameters on atoms '''
641        page = G2frame.Page
642        vartype,varList,constrDictEnt = PageSelection(page)
643        title1 = "Setup riding atoms "
644        title2 = "Select additional atoms(s) to ride on "
645        if not varList:
646            G2frame.ErrorDialog('No variables','There are no variables of type '+vartype,
647                parent=G2frame)
648            return
649#        legend = "Select atoms to ride (only one of the atom variables will be varied when all are set to be varied)"
650        GetAddAtomVars(page,title1,title2,varList,constrDictEnt,'riding')
651   
652    def OnAddFunction(event):
653        '''add a Function (new variable) constraint'''
654        page = G2frame.Page
655        vartype,varList,constrDictEnt = PageSelection(page)
656        title1 = "Setup new variable based on "+vartype+" variables"
657        title2 = "Include additional "+vartype+" variable(s) to be included with "
658        if not varList:
659            G2frame.ErrorDialog('No variables','There are no variables of type '+vartype,
660                parent=G2frame)
661            return
662#        legend = "Select variables to include in a new variable (the new variable will be varied when all included variables are varied)"
663        GetAddVars(page,title1,title2,varList,constrDictEnt,'function')
664                       
665    def OnAddConstraint(event):
666        '''add a constraint equation to the constraints list'''
667        page = G2frame.Page
668        vartype,varList,constrDictEnt = PageSelection(page)
669        title1 = "Setup constraint on "+vartype+" variables"
670        title2 = "Select additional "+vartype+" variable(s) to include in constraint with "
671        if not varList:
672            G2frame.ErrorDialog('No variables','There are no variables of type '+vartype,
673                parent=G2frame)
674            return
675#        legend = "Select variables to include in a constraint equation (the values will be constrainted to equal a specified constant)"
676        GetAddVars(page,title1,title2,varList,constrDictEnt,'constraint')
677
678    def GetAddVars(page,title1,title2,varList,constrDictEnt,constType):
679        '''Get the variables to be added for OnAddEquivalence, OnAddFunction,
680        and OnAddConstraint. Then create and check the constraint.
681        '''
682        #varListlbl = ["("+i+") "+G2obj.fmtVarDescr(i) for i in varList]
683        l2 = l1 = 1
684        for i in varList:
685            l1 = max(l1,len(i))
686            loc,desc = G2obj.VarDescr(i)
687            l2 = max(l2,len(loc))
688        fmt = "{:"+str(l1)+"s} {:"+str(l2)+"s} {:s}"
689        varListlbl = [fmt.format(i,*G2obj.VarDescr(i)) for i in varList]       
690        dlg = G2G.G2SingleChoiceDialog(G2frame,'Select 1st variable:',
691            title1,varListlbl,monoFont=True,size=(625,400))
692        dlg.CenterOnParent()
693        if dlg.ShowModal() == wx.ID_OK:
694            sel = dlg.GetSelection()
695            FrstVarb = varList[sel]
696            VarObj = G2obj.G2VarObj(FrstVarb)
697            moreVarb = FindEquivVarb(FrstVarb,varList)
698            newcons = SelectVarbs(page,VarObj,moreVarb,title2+FrstVarb,constType)
699            if len(newcons) > 0:
700                if CheckAddedConstraint(newcons):
701                    data[constrDictEnt] += newcons
702        dlg.Destroy()
703        wx.CallAfter(OnPageChanged,None)
704                       
705    def FindNeighbors(phase,FrstName,AtNames):
706        General = phase['General']
707        cx,ct,cs,cia = General['AtomPtrs']
708        Atoms = phase['Atoms']
709        atNames = [atom[ct-1] for atom in Atoms]
710        Cell = General['Cell'][1:7]
711        Amat,Bmat = G2lat.cell2AB(Cell)
712        atTypes = General['AtomTypes']
713        Radii = np.array(General['BondRadii'])
714        AtInfo = dict(zip(atTypes,Radii)) #or General['BondRadii']
715        Orig = atNames.index(FrstName.split()[1])
716        OType = Atoms[Orig][ct]
717        XYZ = G2mth.getAtomXYZ(Atoms,cx)       
718        Neigh = []
719        Dx = np.inner(Amat,XYZ-XYZ[Orig]).T
720        dist = np.sqrt(np.sum(Dx**2,axis=1))
721        sumR = AtInfo[OType]+0.5    #H-atoms only!
722        IndB = ma.nonzero(ma.masked_greater(dist-0.85*sumR,0.))
723        for j in IndB[0]:
724            if j != Orig:
725                Neigh.append(AtNames[j])
726        return Neigh
727       
728    def GetAddAtomVars(page,title1,title2,varList,constrDictEnt,constType):
729        '''Get the atom variables to be added for OnAddAtomEquiv. Then create and
730        check the constraints. Riding for H atoms only.
731        '''
732        Atoms = {G2obj.VarDescr(i)[0]:[] for i in varList if 'Atom' in G2obj.VarDescr(i)[0]}
733        for item in varList:
734            atName = G2obj.VarDescr(item)[0]
735            if atName in Atoms:
736                Atoms[atName].append(item)
737        AtNames = Atoms.keys()
738        AtNames.sort()
739        dlg = G2G.G2SingleChoiceDialog(G2frame,'Select 1st atom:',
740            title1,AtNames,monoFont=True,size=(625,400))
741        dlg.CenterOnParent()
742        FrstAtom = ''
743        if dlg.ShowModal() == wx.ID_OK:
744            sel = dlg.GetSelection()
745            FrstAtom = AtNames[sel]
746            if 'riding' in constType:
747                phaseName = (FrstAtom.split(' in ')[1]).strip()
748                phase = Phases[phaseName]
749                AtNames = FindNeighbors(phase,FrstAtom,AtNames)
750            else:
751                AtNames.remove(FrstAtom)
752        dlg.Destroy()
753        if FrstAtom == '':
754            print 'no atom selected'
755            return
756        dlg = G2G.G2MultiChoiceDialog(
757            G2frame,title2+FrstAtom,
758            'Constrain '+str(FrstAtom)+' with...',AtNames,
759            toggle=False,size=(625,400),monoFont=True)
760        if dlg.ShowModal() == wx.ID_OK:
761            Selections = dlg.GetSelections()[:]
762        else:
763            print 'no target atom selected'
764            dlg.Destroy()
765            return
766        dlg.Destroy()
767        for name in Atoms[FrstAtom]:
768            newcons = []
769            constr = []
770            if 'riding' in constType:
771                if 'AUiso' in name:
772                    constr = [[1.0,G2obj.G2VarObj(name)]]
773                elif 'AU11' in name:
774                    pass
775                elif 'AU' not in name:
776                    constr = [[1.0,G2obj.G2VarObj(name)]]
777            else:
778                constr = [[1.0,G2obj.G2VarObj(name)]]
779            pref = ':'+name.rsplit(':',1)[0].split(':',1)[1]    #get stuff between phase id & atom id
780            for sel in Selections:
781                name2 = Atoms[AtNames[sel]][0]
782                pid = name2.split(':',1)[0]                     #get phase id for 2nd atom
783                id = name2.rsplit(':',1)[-1]                    #get atom no. for 2nd atom
784                if 'riding' in constType:
785                    pref = pid+pref
786                    if 'AUiso' in pref:
787                        parts = pref.split('AUiso')
788                        constr += [[1.2,G2obj.G2VarObj('%s:%s'%(parts[0]+'AUiso',id))]]
789                    elif 'AU' not in pref:
790                        constr += [[1.0,G2obj.G2VarObj('%s:%s'%(pref,id))]]
791                else:
792                    constr += [[1.0,G2obj.G2VarObj('%s:%s'%(pid+pref,id))]]
793            if not constr:
794                continue
795            if 'frac' in pref and 'riding' not in constType:
796                newcons = [constr+[1.0,None,'c']]
797            else:
798                newcons = [constr+[None,None,'e']]
799            if len(newcons) > 0:
800                if CheckAddedConstraint(newcons):
801                    data[constrDictEnt] += newcons
802        wx.CallAfter(OnPageChanged,None)
803                       
804    def MakeConstraintsSizer(name,pageDisplay):
805        '''Creates a sizer displaying all of the constraints entered of
806        the specified type.
807
808        :param str name: the type of constraints to be displayed ('HAP',
809          'Hist', 'Phase', 'Global', 'Sym-Generated')
810        :param wx.Panel pageDisplay: parent panel for sizer
811        :returns: wx.Sizer created by method
812        '''
813        if name == 'Sym-Generated':         #show symmetry generated constraints
814            Sizer1 =  wx.BoxSizer(wx.VERTICAL)
815            Sizer1.Add(wx.StaticText(pageDisplay,wx.ID_ANY,
816                                    'Equivalences generated based on cell/space group input'))
817            Sizer1.Add((-1,5))
818            Sizer = wx.FlexGridSizer(0,2,0,0)
819            Sizer1.Add(Sizer)
820            for sym in G2mv.GetSymEquiv():
821                Sizer.Add(wx.StaticText(pageDisplay,wx.ID_ANY,'EQUIV'),
822                           0,wx.ALIGN_CENTER_VERTICAL|wx.ALIGN_CENTER|wx.RIGHT|wx.LEFT,3)
823                Sizer.Add(wx.StaticText(pageDisplay,wx.ID_ANY,sym))
824                Sizer.Add((-1,-1))
825                Sizer.Add((-1,2))
826            return Sizer1
827        constSizer = wx.FlexGridSizer(0,6,0,0)
828        maxlen = 50 # characters before wrapping a constraint
829        for Id,item in enumerate(data[name]):
830            refineflag = False
831            helptext = ""
832            eqString = ['',]
833            problemItem = False
834            for term in item[:-3]:
835                if str(term[1]) in G2mv.problemVars:
836                    problemItem = True
837            if item[-1] == 'h': # Hold on variable
838                constSizer.Add((-1,-1),0)              # blank space for edit button
839                typeString = 'FIXED'
840                var = str(item[0][1])
841                varMean = G2obj.fmtVarDescr(var)
842                eqString[-1] =  var +'   '
843                helptext = "Prevents variable:\n"+ var + " ("+ varMean + ")\nfrom being changed"
844            elif isinstance(item[-1],str): # not true on original-style (2011?) constraints
845                constEdit = wx.Button(pageDisplay,wx.ID_ANY,'Edit',style=wx.BU_EXACTFIT)
846                constEdit.Bind(wx.EVT_BUTTON,OnConstEdit)
847                constSizer.Add(constEdit,0,wx.LEFT|wx.RIGHT|wx.ALIGN_CENTER_VERTICAL|wx.ALIGN_CENTER,1)            # edit button
848                Indx[constEdit.GetId()] = [Id,name]
849                if item[-1] == 'f':
850                    helptext = "A new variable"
851                    if item[-3]:
852                        helptext += " named "+str(item[-3])
853                    helptext += " is created from a linear combination of the following variables:\n"
854                    for term in item[:-3]:
855                        var = str(term[1])
856                        if len(eqString[-1]) > maxlen:
857                            eqString.append(' ')
858                        m = term[0]
859                        if eqString[-1] != '':
860                            if m >= 0:
861                                eqString[-1] += ' + '
862                            else:
863                                eqString[-1] += ' - '
864                                m = abs(m)
865                        eqString[-1] += '%.3f*%s '%(m,var)
866                        varMean = G2obj.fmtVarDescr(var)
867                        helptext += "\n" + var + " ("+ varMean + ")"
868                    if '_Explain' in data:
869                        if data['_Explain'].get(item[-3]):
870                            helptext += '\n\n'
871                            helptext += data['_Explain'][item[-3]]
872                    # typeString = 'NEWVAR'
873                    # if item[-3]:
874                    #     eqString[-1] += ' = '+item[-3]
875                    # else:
876                    #     eqString[-1] += ' = New Variable'
877                    if item[-3]:
878                        typeString = item[-3] + ' = '
879                    else:
880                        typeString = 'New Variable = '
881                    #print 'refine',item[-2]
882                    refineflag = True
883                elif item[-1] == 'c':
884                    helptext = "The following variables constrained to equal a constant:"
885                    for term in item[:-3]:
886                        var = str(term[1])
887                        if len(eqString[-1]) > maxlen:
888                            eqString.append(' ')
889                        if eqString[-1] != '':
890                            if term[0] > 0:
891                                eqString[-1] += ' + '
892                            else:
893                                eqString[-1] += ' - '
894                        eqString[-1] += '%.3f*%s '%(abs(term[0]),var)
895                        varMean = G2obj.fmtVarDescr(var)
896                        helptext += "\n" + var + " ("+ varMean + ")"
897                    typeString = 'CONST'
898                    eqString[-1] += ' = '+str(item[-3])
899                elif item[-1] == 'e':
900                    helptext = "The following variables are set to be equivalent, noting multipliers:"
901                    for term in item[:-3]:
902                        var = str(term[1])
903                        if term[0] == 0: term[0] = 1.0
904                        if len(eqString[-1]) > maxlen:
905                            eqString.append(' ')
906                        if eqString[-1] == '':
907                            eqString[-1] += var+' '
908                            first = term[0]
909                        else:
910                            eqString[-1] += ' = %.3f*%s '%(first/term[0],var)
911                        varMean = G2obj.fmtVarDescr(var)
912                        helptext += "\n" + var + " ("+ varMean + ")"
913                    typeString = 'EQUIV'
914                else:
915                    print 'Unexpected constraint',item
916               
917            else:
918                print 'Removing old-style constraints'
919                data[name] = []
920                return constSizer
921            constDel = wx.Button(pageDisplay,wx.ID_ANY,'Delete',style=wx.BU_EXACTFIT)
922            constDel.Bind(wx.EVT_BUTTON,OnConstDel)
923            Indx[constDel.GetId()] = [Id,name]
924            constSizer.Add(constDel,0,wx.LEFT|wx.RIGHT|wx.ALIGN_CENTER_VERTICAL|wx.ALIGN_CENTER,1)             # delete button
925            if helptext:
926                ch = G2G.HelpButton(pageDisplay,helptext)
927                constSizer.Add(ch,0,wx.LEFT|wx.RIGHT|wx.ALIGN_CENTER_VERTICAL|wx.ALIGN_CENTER,1)
928            else:
929                constSizer.Add((-1,-1))
930            if refineflag:
931                ch = G2G.G2CheckBox(pageDisplay,'',item,-2)
932                constSizer.Add(ch,0,wx.LEFT|wx.RIGHT|wx.ALIGN_CENTER_VERTICAL|wx.ALIGN_CENTER,1)
933            else:
934                constSizer.Add((-1,-1))               
935            constSizer.Add(wx.StaticText(pageDisplay,wx.ID_ANY,typeString),
936                           0,wx.ALIGN_CENTER_VERTICAL|wx.ALIGN_CENTER|wx.RIGHT|wx.LEFT,3)
937            if problemItem: eqString[-1] += ' -- Conflict: see console'
938            if len(eqString) > 1:
939                Eq = wx.BoxSizer(wx.VERTICAL)
940                for s in eqString:
941                    line = wx.StaticText(pageDisplay,wx.ID_ANY,s)
942                    if problemItem: line.SetBackgroundColour(wx.YELLOW)
943                    Eq.Add(line,0,wx.ALIGN_CENTER_VERTICAL)
944                Eq.Add((-1,4))
945            else:
946                Eq = wx.StaticText(pageDisplay,wx.ID_ANY,eqString[0])
947                if problemItem: Eq.SetBackgroundColour(wx.YELLOW)
948            constSizer.Add(Eq,1,wx.ALIGN_CENTER_VERTICAL)
949        return constSizer
950               
951    def OnConstDel(event):
952        'Delete a constraint'
953        Obj = event.GetEventObject()
954        Id,name = Indx[Obj.GetId()]
955        del(data[name][Id])
956        wx.CallAfter(OnPageChanged,None)
957       
958    def OnConstEdit(event):
959        '''Called to edit an individual contraint in response to a
960        click on its Edit button
961        '''
962        Obj = event.GetEventObject()
963        Id,name = Indx[Obj.GetId()]
964        if data[name][Id][-1] == 'f':
965            items = data[name][Id][:-3]
966            constType = 'New Variable'
967            if data[name][Id][-3]:
968                varname = data[name][Id][-3]
969            else:
970                varname = ""
971            lbl = 'Enter value for each term in constraint; sum = new variable'
972            dlg = ConstraintDialog(G2frame,constType,lbl,items,
973                                   varname=varname,varyflag=data[name][Id][-2])
974        elif data[name][Id][-1] == 'c':
975            items = data[name][Id][:-3]+[
976                [data[name][Id][-3],'fixed value =']]
977            constType = 'Constraint'
978            lbl = 'Edit value for each term in constant constraint sum'
979            dlg = ConstraintDialog(G2frame,constType,lbl,items)
980        elif data[name][Id][-1] == 'e':
981            items = data[name][Id][:-3]
982            constType = 'Equivalence'
983            lbl = 'The following terms are set to be equal:'
984            dlg = ConstraintDialog(G2frame,constType,lbl,items,'/')
985        else:
986            return
987        try:
988            prev = data[name][Id][:]
989            if dlg.ShowModal() == wx.ID_OK:
990                result = dlg.GetData()
991                for i in range(len(data[name][Id][:-3])):
992                    if type(data[name][Id][i]) is tuple: # fix non-mutable construct
993                        data[name][Id][i] = list(data[name][Id][i])
994                    data[name][Id][i][0] = result[i][0]
995                if data[name][Id][-1] == 'c':
996                    data[name][Id][-3] = str(result[-1][0])
997                elif data[name][Id][-1] == 'f':
998                    # process the variable name to put in global form (::var)
999                    varname = str(dlg.newvar[0]).strip().replace(' ','_')
1000                    if varname.startswith('::'):
1001                        varname = varname[2:]
1002                    varname = varname.replace(':',';')
1003                    if varname:
1004                        data[name][Id][-3] = varname
1005                    else:
1006                        data[name][Id][-3] = ''
1007                    data[name][Id][-2] = dlg.newvar[1]
1008                if not CheckChangedConstraint():
1009                    data[name][Id] = prev
1010        except:
1011            import traceback
1012            print traceback.format_exc()
1013        finally:
1014            dlg.Destroy()
1015        wx.CallAfter(OnPageChanged,None)
1016   
1017    def UpdateConstraintPanel(panel,typ):
1018        '''Update the contents of the selected Constraint
1019        notebook tab. Called in :func:`OnPageChanged`
1020        '''
1021        if panel.GetSizer(): panel.GetSizer().Clear(True)
1022        Siz = wx.BoxSizer(wx.VERTICAL)
1023        Siz.Add((5,5),0)
1024        Siz.Add(MakeConstraintsSizer(typ,panel),1,wx.EXPAND)
1025        panel.SetSizer(Siz,True)
1026        Size = Siz.GetMinSize()
1027        Size[0] += 40
1028        Size[1] = max(Size[1],450) + 20
1029        panel.SetSize(Size)
1030        panel.SetScrollbars(10,10,Size[0]/10-4,Size[1]/10-1)
1031        panel.Show()
1032
1033    def OnPageChanged(event):
1034        '''Called when a tab is pressed or when a "select tab" menu button is
1035        used (see RaisePage), or to refresh the current tab contents (event=None)
1036        '''
1037        if event:       #page change event!
1038            page = event.GetSelection()
1039        else: # called directly, get current page
1040            page = G2frame.constr.GetSelection()
1041        #G2frame.constr.SetSize(G2frame.dataWindow.GetClientSize())    #TODO -almost right
1042        G2frame.constr.ChangeSelection(page)
1043        text = G2frame.constr.GetPageText(page)
1044        G2frame.dataWindow.ConstraintEdit.Enable(G2G.wxID_EQUIVALANCEATOMS,False)
1045#        G2frame.dataWindow.ConstraintEdit.Enable(G2G.wxID_ADDRIDING,False)
1046        if text == 'Histogram/Phase':
1047            G2frame.Page = [page,'hap']
1048            UpdateConstraintPanel(HAPConstr,'HAP')
1049        elif text == 'Histogram':
1050            G2frame.Page = [page,'hst']
1051            UpdateConstraintPanel(HistConstr,'Hist')
1052        elif text == 'Phase':
1053            G2frame.Page = [page,'phs']
1054            G2frame.dataWindow.ConstraintEdit.Enable(G2G.wxID_EQUIVALANCEATOMS,True)
1055#            G2frame.dataWindow.ConstraintEdit.Enable(G2G.wxID_ADDRIDING,True)
1056            if 'DELETED' in str(PhaseConstr):   #seems to be no other way to do this (wx bug)
1057                if GSASIIpath.GetConfigValue('debug'):
1058                    print 'wx error: PhaseConstr not cleanly deleted after Refine'
1059                return
1060            UpdateConstraintPanel(PhaseConstr,'Phase')
1061        elif text == 'Global':
1062            G2frame.Page = [page,'glb']
1063            UpdateConstraintPanel(GlobalConstr,'Global')
1064        else:
1065            G2frame.Page = [page,'sym']
1066            UpdateConstraintPanel(SymConstr,'Sym-Generated')           
1067        G2frame.dataWindow.SetDataSize()
1068
1069    def RaisePage(event):
1070        'Respond to a "select tab" menu button'
1071        try:
1072            i = (G2G.wxID_CONSPHASE,
1073                 G2G.wxID_CONSHAP,
1074                 G2G.wxID_CONSHIST,
1075                 G2G.wxID_CONSGLOBAL).index(event.GetId())
1076            G2frame.constr.SetSelection(i)
1077            wx.CallAfter(OnPageChanged,None)
1078        except ValueError:
1079            print('Unexpected event in RaisePage')
1080
1081    def SetStatusLine(text):
1082        G2frame.GetStatusBar().SetStatusText(text,1)                                     
1083       
1084    G2gd.SetDataMenuBar(G2frame,G2frame.dataWindow.ConstraintMenu)
1085    #G2frame.SetLabel(G2frame.GetLabel().split('||')[0]+' || '+'Constraints')
1086    G2frame.SetTitle('Constraints')
1087    SetStatusLine('')
1088   
1089    G2frame.Bind(wx.EVT_MENU, OnAddConstraint, id=G2G.wxID_CONSTRAINTADD)
1090    G2frame.Bind(wx.EVT_MENU, OnAddFunction, id=G2G.wxID_FUNCTADD)
1091    G2frame.Bind(wx.EVT_MENU, OnAddEquivalence, id=G2G.wxID_EQUIVADD)
1092    G2frame.Bind(wx.EVT_MENU, OnAddHold, id=G2G.wxID_HOLDADD)
1093    G2frame.Bind(wx.EVT_MENU, OnAddAtomEquiv, id=G2G.wxID_EQUIVALANCEATOMS)
1094#    G2frame.Bind(wx.EVT_MENU, OnAddRiding, id=G2G.wxID_ADDRIDING)
1095    # tab commands
1096    for id in (G2G.wxID_CONSPHASE,
1097               G2G.wxID_CONSHAP,
1098               G2G.wxID_CONSHIST,
1099               G2G.wxID_CONSGLOBAL):
1100        G2frame.Bind(wx.EVT_MENU, RaisePage,id=id)
1101
1102    #G2frame.constr = G2G.GSNoteBook(parent=G2frame.dataWindow,size=G2frame.dataWindow.GetClientSize())
1103    G2frame.constr = G2G.GSNoteBook(parent=G2frame.dataWindow)
1104    G2frame.dataWindow.GetSizer().Add(G2frame.constr,1,wx.ALL|wx.EXPAND)
1105    # note that order of pages is hard-coded in RaisePage
1106    PhaseConstr = wx.ScrolledWindow(G2frame.constr)
1107    G2frame.constr.AddPage(PhaseConstr,'Phase')
1108    HAPConstr = wx.ScrolledWindow(G2frame.constr)
1109    G2frame.constr.AddPage(HAPConstr,'Histogram/Phase')
1110    HistConstr = wx.ScrolledWindow(G2frame.constr)
1111    G2frame.constr.AddPage(HistConstr,'Histogram')
1112    GlobalConstr = wx.ScrolledWindow(G2frame.constr)
1113    G2frame.constr.AddPage(GlobalConstr,'Global')
1114    SymConstr = wx.ScrolledWindow(G2frame.constr)
1115    G2frame.constr.AddPage(SymConstr,'Sym-Generated')
1116    wx.CallAfter(OnPageChanged,None)
1117    G2frame.constr.Bind(wx.aui.EVT_AUINOTEBOOK_PAGE_CHANGED, OnPageChanged)
1118    # validate all the constrants -- should not see any errors here normally
1119    allcons = []
1120    for key in data:
1121        if key.startswith('_'): continue
1122        allcons += data[key]
1123    if not len(allcons): return
1124    #reload(G2mv) # TODO: for testing
1125    G2mv.InitVars()   
1126    constDictList,fixedList,ignored = G2stIO.ProcessConstraints(allcons)
1127    # generate symmetry constraints to check for conflicts
1128    rigidbodyDict = G2frame.GPXtree.GetItemPyData(   
1129            G2gd.GetGPXtreeItemId(G2frame,G2frame.root,'Rigid bodies'))
1130    rbIds = rigidbodyDict.get('RBIds',{'Vector':[],'Residue':[]})
1131    rbVary,rbDict = G2stIO.GetRigidBodyModels(rigidbodyDict,Print=False)
1132    Natoms,atomIndx,phaseVary,phaseDict,pawleyLookup,FFtables,BLtables,MFtables,maxSSwave = G2stIO.GetPhaseData(
1133        Phases,RestraintDict=None,rbIds=rbIds,Print=False) # generates atom symmetry constraints
1134    errmsg, warnmsg = G2mv.CheckConstraints('',constDictList,fixedList)
1135    if errmsg:
1136        G2frame.ErrorDialog('Constraint Error',
1137                            'Error in constraints:\n'+errmsg+'\nCheck console output for more information',
1138                            parent=G2frame)
1139        print errmsg
1140        print G2mv.VarRemapShow([],True)
1141    elif warnmsg:
1142        print 'Unexpected contraint warning:\n',warnmsg
1143
1144################################################################################
1145#### Make nuclear-magnetic phase constraints - called by OnTransform in G2phsGUI
1146################################################################################       
1147       
1148def MagConstraints(G2frame,oldPhase,newPhase,Trans,Vec,atCodes):
1149    '''Add constraints for new magnetic phase created via transformation of old
1150    nuclear one
1151    NB: A = [G11,G22,G33,2*G12,2*G13,2*G23]
1152    '''
1153   
1154    def SetUniqAj(pId,iA,Aname,SGLaue):
1155        if SGLaue in ['4/m','4/mmm'] and iA in [0,1]:
1156            parm = '%d::%s'%(pId,'A0')
1157        elif SGLaue in ['m3','m3m'] and iA in [0,1,2]:
1158            parm = '%d::%s'%(pId,'A0')
1159        elif SGLaue in ['6/m','6/mmm','3m1', '31m', '3'] and iA in [0,1,3]:
1160            parm = '%d::%s'%(pId,'A0')
1161        elif SGLaue in ['3R', '3mR']:
1162            if ia in [0,1,2]:
1163                parm = '%d::%s'%(pId,'A0')
1164            else:
1165                parm = '%d::%s'%(pId,'A3')
1166        else:
1167            parm = '%d::%s'%(pId,Aname)
1168        return parm
1169       
1170    Histograms,Phases = G2frame.GetUsedHistogramsAndPhasesfromTree()
1171    UseList = newPhase['Histograms']
1172    detTrans = np.abs(nl.det(Trans))
1173    invTrans = nl.inv(Trans)
1174#    print 'invTrans',invTrans
1175    nAcof = G2lat.cell2A(newPhase['General']['Cell'][1:7])
1176   
1177    opId = oldPhase['pId']
1178    npId = newPhase['pId']
1179    cx,ct,cs,cia = newPhase['General']['AtomPtrs']
1180    nAtoms = newPhase['Atoms']
1181    oSGData = oldPhase['General']['SGData']
1182    nSGData = newPhase['General']['SGData']
1183    oAcof = G2lat.cell2A(oldPhase['General']['Cell'][1:7])
1184    nAcof = G2lat.cell2A(newPhase['General']['Cell'][1:7])
1185    item = G2gd.GetGPXtreeItemId(G2frame,G2frame.root,'Constraints') 
1186    constraints = G2frame.GPXtree.GetItemPyData(item)
1187#    GSASIIpath.IPyBreak()
1188    parmDict = {}
1189    varyList = []
1190    for ia,code in enumerate(atCodes):
1191        atom = nAtoms[ia]
1192        siteSym = G2spc.SytSym(atom[cx:cx+3],nSGData)[0]
1193        CSX = G2spc.GetCSxinel(siteSym)
1194        item = code.split('+')[0]
1195        iat,opr = item.split(':')
1196        Nop = abs(int(opr))%100-1
1197        if '-' in opr:
1198            Nop *= -1
1199        Opr = oldPhase['General']['SGData']['SGOps'][abs(Nop)][0]
1200        if Nop < 0:         #inversion
1201            Opr *= -1
1202        XOpr = np.inner(Opr,Trans.T)
1203        names = ['dAx','dAy','dAz']
1204        for ix,name in enumerate(names):
1205            IndpCon = [1.0,G2obj.G2VarObj('%d::%s:%s'%(opId,name,iat))]
1206            DepCons = []
1207            for iop,opval in enumerate(XOpr[ix]):
1208                if opval and CSX[0][ix]:    #-opval from defn of dAx, etc.
1209                    DepCons.append([-opval,G2obj.G2VarObj('%d::%s:%d'%(npId,names[iop],ia))])
1210            if len(DepCons) == 1:
1211                constraints['Phase'].append([IndpCon,DepCons[0],None,None,'e'])
1212            elif len(DepCons) > 1:
1213                for Dep in DepCons:
1214                    Dep[0] *= -1
1215                constraints['Phase'].append([IndpCon]+DepCons+[0.0,None,'c'])
1216        for name in ['Afrac','AUiso']:
1217            IndpCon = [1.0,G2obj.G2VarObj('%d::%s:%s'%(opId,name,iat))]
1218            DepCons = [1.0,G2obj.G2VarObj('%d::%s:%d'%(npId,name,ia))]
1219            constraints['Phase'].append([IndpCon,DepCons,None,None,'e'])
1220        #how do I do Uij's for most Trans?
1221    Anames = [['A0','A3','A4'],['A3','A1','A5'],['A4','A5','A2']]
1222    As = ['A0','A1','A2','A3','A4','A5']
1223    Aids = [[0,0,'A0',-1],[1,1,'A1',-1],[2,2,'A2',-1],[0,1,'A3',2],[0,2,'A4',1],[1,2,'A5',0]]
1224    Axes = ['a','b','c']
1225    Holds = []
1226    for iA,Aid in enumerate(Aids):
1227        parm = SetUniqAj(opId,iA,Aid[2],oSGData['SGLaue'])
1228        parmDict[parm] = oAcof[iA]
1229        varyList.append(parm)
1230        IndpCon = [1.0,G2obj.G2VarObj(parm)]
1231        DepCons = []
1232        for iat in range(3):
1233            if nSGData['SGLaue'] in ['-1','2/m']:       #set holds
1234                if (abs(nAcof[iA]) < 1.e-8) and (abs(Trans[Aid[0],Aid[1]]) < 1.e-8):
1235                    if Axes[iat] != oSGData['SGUniq'] and oSGData['SGLaue'] != nSGData['SGLaue']:
1236                        HoldObj = G2obj.G2VarObj('%d::%s'%(npId,Aid[2]))
1237                        if not HoldObj in Holds: 
1238                            constraints['Phase'].append([[0.0,HoldObj],None,None,'h'])
1239                            Holds.append(HoldObj)
1240                            continue
1241#            print iA,Aid,iat,invTrans[iat][Aid[0]],invTrans[Aid[1]][iat],Anames[Aid[0]][Aid[1]],parm
1242            if abs(invTrans[iat,Aid[1]]) > 1.e-8 and abs(nAcof[iA]) > 1.e-8:
1243                parm = SetUniqAj(npId,iA,Anames[Aid[0]][Aid[1]],nSGData['SGLaue'])
1244                parmDict[parm] = nAcof[As.index(Aid[2])]
1245                if not parm in varyList:
1246                    varyList.append(parm)
1247                DepCons.append([Trans[Aid[0],Aid[0]]*Trans[Aid[1],Aid[1]],G2obj.G2VarObj(parm)])
1248        if len(DepCons) == 1:
1249            constraints['Phase'].append([IndpCon,DepCons[0],None,None,'e'])
1250        elif len(DepCons) > 1:       
1251            for Dep in DepCons:
1252                Dep[0] *= -1
1253            constraints['Phase'].append([IndpCon]+DepCons+[0.0,None,'c'])
1254#    constDict,fixedList,ignored = G2stIO.ProcessConstraints(constraints['Phase'])
1255#    groups,parmlist = G2mv.GroupConstraints(constDict)
1256#    G2mv.GenerateConstraints(groups,parmlist,varyList,constDict,fixedList,parmDict)
1257#    print 'old',parmDict
1258#    G2mv.Dict2Map(parmDict,varyList)
1259#    print 'new',parmDict
1260    for hId,hist in enumerate(UseList):    #HAP - seems OK
1261        ohapkey = '%d:%d:'%(opId,hId)
1262        nhapkey = '%d:%d:'%(npId,hId)
1263        IndpCon = [1.0,G2obj.G2VarObj(ohapkey+'Scale')]
1264        DepCons = [detTrans,G2obj.G2VarObj(nhapkey+'Scale')]
1265        constraints['HAP'].append([IndpCon,DepCons,None,None,'e'])
1266        for name in ['Size;i','Mustrain;i']:
1267            IndpCon = [1.0,G2obj.G2VarObj(ohapkey+name)]
1268            DepCons = [1.0,G2obj.G2VarObj(nhapkey+name)]
1269            constraints['HAP'].append([IndpCon,DepCons,None,None,'e'])
1270       
1271################################################################################
1272#### Rigid bodies
1273################################################################################
1274
1275def UpdateRigidBodies(G2frame,data):
1276    '''Called when Rigid bodies tree item is selected.
1277    Displays the rigid bodies in the data window
1278    '''
1279    if not data.get('RBIds') or not data:
1280        data.update({'Vector':{'AtInfo':{}},'Residue':{'AtInfo':{}},
1281            'RBIds':{'Vector':[],'Residue':[]}})       #empty/bad dict - fill it
1282           
1283    global resList
1284    Indx = {}
1285    resList = []
1286    plotDefaults = {'oldxy':[0.,0.],'Quaternion':[0.,0.,0.,1.],'cameraPos':30.,'viewDir':[0,0,1],}
1287
1288    G2frame.rbBook = G2G.GSNoteBook(parent=G2frame.dataWindow)
1289    G2frame.dataWindow.GetSizer().Add(G2frame.rbBook,1,wx.ALL|wx.EXPAND)
1290    VectorRB = wx.ScrolledWindow(G2frame.rbBook)
1291    VectorRBDisplay = wx.Panel(VectorRB)
1292    G2frame.rbBook.AddPage(VectorRB,'Vector rigid bodies')
1293    ResidueRB = wx.ScrolledWindow(G2frame.rbBook)
1294    ResidueRBDisplay = wx.Panel(ResidueRB)
1295    G2frame.rbBook.AddPage(ResidueRB,'Residue rigid bodies')
1296   
1297    def OnPageChanged(event):
1298        global resList
1299        resList = []
1300        if event:       #page change event!
1301            page = event.GetSelection()
1302        else:
1303            page = G2frame.rbBook.GetSelection()
1304        #G2frame.rbBook.SetSize(G2frame.dataWindow.GetClientSize())    #TODO -almost right
1305        G2frame.rbBook.ChangeSelection(page)
1306        text = G2frame.rbBook.GetPageText(page)
1307        if text == 'Vector rigid bodies':
1308            G2gd.SetDataMenuBar(G2frame,G2frame.dataWindow.VectorBodyMenu)
1309            G2frame.Bind(wx.EVT_MENU, AddVectorRB, id=G2G.wxID_VECTORBODYADD)
1310            G2frame.Page = [page,'vrb']
1311            UpdateVectorRB()
1312        elif text == 'Residue rigid bodies':
1313            G2gd.SetDataMenuBar(G2frame,G2frame.dataWindow.RigidBodyMenu)
1314            G2frame.Bind(wx.EVT_MENU, AddResidueRB, id=G2G.wxID_RIGIDBODYADD)
1315            G2frame.Bind(wx.EVT_MENU, OnImportRigidBody, id=G2G.wxID_RIGIDBODYIMPORT)
1316            G2frame.Bind(wx.EVT_MENU, OnDefineTorsSeq, id=G2G.wxID_RESIDUETORSSEQ) #enable only if residue RBs exist?
1317            G2frame.Page = [page,'rrb']
1318            UpdateResidueRB()
1319           
1320    def getMacroFile(macName):
1321        defDir = os.path.join(os.path.split(__file__)[0],'GSASIImacros')
1322        dlg = wx.FileDialog(G2frame,message='Choose '+macName+' rigid body macro file',
1323            defaultDir=defDir,defaultFile="",wildcard="GSAS-II macro file (*.mac)|*.mac",
1324            style=wx.OPEN | wx.CHANGE_DIR)
1325        try:
1326            if dlg.ShowModal() == wx.ID_OK:
1327                macfile = dlg.GetPath()
1328                macro = open(macfile,'Ur')
1329                head = macro.readline()
1330                if macName not in head:
1331                    print head
1332                    print '**** ERROR - wrong restraint macro file selected, try again ****'
1333                    macro = []
1334            else: # cancel was pressed
1335                macro = []
1336        finally:
1337            dlg.Destroy()
1338        return macro        #advanced past 1st line
1339       
1340    def getTextFile():
1341        dlg = wx.FileDialog(G2frame,'Choose rigid body text file', '.', '',
1342            "GSAS-II text file (*.txt)|*.txt|XYZ file (*.xyz)|*.xyz|"
1343            "Sybyl mol2 file (*.mol2)|*.mol2|PDB file (*.pdb;*.ent)|*.pdb;*.ent",
1344            wx.OPEN | wx.CHANGE_DIR)
1345        try:
1346            if dlg.ShowModal() == wx.ID_OK:
1347                txtfile = dlg.GetPath()
1348                ext = os.path.splitext(txtfile)[1]
1349                text = open(txtfile,'Ur')
1350            else: # cancel was pressed
1351                ext = ''
1352                text = []
1353        finally:
1354            dlg.Destroy()
1355        if 'ent' in ext:
1356            ext = '.pdb'
1357        return text,ext.lower()
1358       
1359    def OnImportRigidBody(event):
1360        page = G2frame.rbBook.GetSelection()
1361        if 'Vector' in G2frame.rbBook.GetPageText(page):
1362            pass
1363        elif 'Residue' in G2frame.rbBook.GetPageText(page):
1364            ImportResidueRB()
1365           
1366    def AddVectorRB(event):
1367        AtInfo = data['Vector']['AtInfo']
1368        dlg = G2G.MultiIntegerDialog(G2frame,'New Rigid Body',['No. atoms','No. translations'],[1,1])
1369        if dlg.ShowModal() == wx.ID_OK:
1370            nAtoms,nTrans = dlg.GetValues()
1371            rbId = ran.randint(0,sys.maxint)
1372            vecMag = [1.0 for i in range(nTrans)]
1373            vecRef = [False for i in range(nTrans)]
1374            vecVal = [np.zeros((nAtoms,3)) for j in range(nTrans)]
1375            rbTypes = ['C' for i in range(nAtoms)]
1376            Info = G2elem.GetAtomInfo('C')
1377            AtInfo['C'] = [Info['Drad'],Info['Color']]
1378            data['Vector'][rbId] = {'RBname':'UNKRB','VectMag':vecMag,'rbXYZ':np.zeros((nAtoms,3)),
1379                'rbRef':[0,1,2,False],'VectRef':vecRef,'rbTypes':rbTypes,'rbVect':vecVal,'useCount':0}
1380            data['RBIds']['Vector'].append(rbId)
1381        dlg.Destroy()
1382        UpdateVectorRB()
1383       
1384    def AddResidueRB(event):
1385        AtInfo = data['Residue']['AtInfo']
1386        macro = getMacroFile('rigid body')
1387        if not macro:
1388            return
1389        macStr = macro.readline()
1390        while macStr:
1391            items = macStr.split()
1392            if 'I' == items[0]:
1393                rbId = ran.randint(0,sys.maxint)
1394                rbName = items[1]
1395                rbTypes = []
1396                rbXYZ = []
1397                rbSeq = []
1398                atNames = []
1399                nAtms,nSeq,nOrig,mRef,nRef = [int(items[i]) for i in [2,3,4,5,6]]
1400                for iAtm in range(nAtms):
1401                    macStr = macro.readline().split()
1402                    atName = macStr[0]
1403                    atType = macStr[1]
1404                    atNames.append(atName)
1405                    rbXYZ.append([float(macStr[i]) for i in [2,3,4]])
1406                    rbTypes.append(atType)
1407                    if atType not in AtInfo:
1408                        Info = G2elem.GetAtomInfo(atType)
1409                        AtInfo[atType] = [Info['Drad'],Info['Color']]
1410                rbXYZ = np.array(rbXYZ)-np.array(rbXYZ[nOrig-1])
1411                for iSeq in range(nSeq):
1412                    macStr = macro.readline().split()
1413                    mSeq = int(macStr[0])
1414                    for jSeq in range(mSeq):
1415                        macStr = macro.readline().split()
1416                        iBeg = int(macStr[0])-1
1417                        iFin = int(macStr[1])-1
1418                        angle = 0.0
1419                        nMove = int(macStr[2])
1420                        iMove = [int(macStr[i])-1 for i in range(3,nMove+3)]
1421                        rbSeq.append([iBeg,iFin,angle,iMove])
1422                data['Residue'][rbId] = {'RBname':rbName,'rbXYZ':rbXYZ,'rbTypes':rbTypes,
1423                    'atNames':atNames,'rbRef':[nOrig-1,mRef-1,nRef-1,True],'rbSeq':rbSeq,
1424                    'SelSeq':[0,0],'useCount':0}
1425                data['RBIds']['Residue'].append(rbId)
1426                print 'Rigid body '+rbName+' added'
1427            macStr = macro.readline()
1428        macro.close()
1429        UpdateResidueRB()
1430       
1431    def ImportResidueRB():
1432        AtInfo = data['Residue']['AtInfo']
1433        text,ext = getTextFile()
1434        if not text:
1435            return
1436        rbId = ran.randint(0,sys.maxint)
1437        rbTypes = []
1438        rbXYZ = []
1439        atNames = []
1440        txtStr = text.readline()
1441        if 'xyz' in ext:
1442            txtStr = text.readline()
1443            txtStr = text.readline()
1444        elif 'mol2' in ext:
1445            while 'ATOM' not in txtStr:
1446                txtStr = text.readline()
1447            txtStr = text.readline()
1448        elif 'pdb' in ext:
1449            while 'ATOM' not in txtStr[:6] and 'HETATM' not in txtStr[:6]:
1450                txtStr = text.readline()
1451                #print txtStr
1452        items = txtStr.split()
1453        while len(items):
1454            if 'txt' in ext:
1455                atName = items[0]
1456                atType = items[1]
1457                rbXYZ.append([float(items[i]) for i in [2,3,4]])
1458            elif 'xyz' in ext:
1459                atType = items[0]
1460                rbXYZ.append([float(items[i]) for i in [1,2,3]])
1461                atName = atType+str(len(rbXYZ))
1462            elif 'mol2' in ext:
1463                atType = items[1]
1464                atName = items[1]+items[0]
1465                rbXYZ.append([float(items[i]) for i in [2,3,4]])
1466            elif 'pdb' in ext:
1467                atType = items[-1]
1468                atName = items[2]
1469                xyz = txtStr[30:55].split()                   
1470                rbXYZ.append([float(x) for x in xyz])
1471            atNames.append(atName)
1472            rbTypes.append(atType)
1473            if atType not in AtInfo:
1474                Info = G2elem.GetAtomInfo(atType)
1475                AtInfo[atType] = [Info['Drad'],Info['Color']]
1476            txtStr = text.readline()
1477            if 'mol2' in ext and 'BOND' in txtStr:
1478                break
1479            if 'pdb' in ext and ('ATOM' not in txtStr[:6] and 'HETATM' not in txtStr[:6]):
1480                break
1481            items = txtStr.split()
1482        if len(atNames) < 3:
1483            G2G.G2MessageBox(G2frame,'Not enough atoms in rigid body; must be 3 or more')
1484        else:
1485            rbXYZ = np.array(rbXYZ)-np.array(rbXYZ[0])
1486            data['Residue'][rbId] = {'RBname':'UNKRB','rbXYZ':rbXYZ,'rbTypes':rbTypes,
1487                'atNames':atNames,'rbRef':[0,1,2,False],'rbSeq':[],'SelSeq':[0,0],'useCount':0}
1488            data['RBIds']['Residue'].append(rbId)
1489            print 'Rigid body UNKRB added'
1490        text.close()
1491        UpdateResidueRB()
1492       
1493    def FindNeighbors(Orig,XYZ,atTypes,atNames,AtInfo):
1494        Radii = []
1495        for Atype in atTypes:
1496            Radii.append(AtInfo[Atype][0])
1497        Radii = np.array(Radii)
1498        Neigh = []
1499        Dx = XYZ-XYZ[Orig]
1500        dist = np.sqrt(np.sum(Dx**2,axis=1))
1501        sumR = Radii[Orig]+Radii
1502        IndB = ma.nonzero(ma.masked_greater(dist-0.85*sumR,0.))
1503        for j in IndB[0]:
1504            if j != Orig and atTypes[j] != 'H':
1505                Neigh.append(atNames[j])
1506        return Neigh
1507       
1508    def FindAllNeighbors(XYZ,atTypes,atNames,AtInfo):
1509        NeighDict = {}
1510        for iat,xyz in enumerate(atNames):
1511            NeighDict[atNames[iat]] = FindNeighbors(iat,XYZ,atTypes,atNames,AtInfo)
1512        return NeighDict
1513       
1514    def FindRiding(Orig,Pivot,NeighDict):
1515        riding = [Orig,Pivot]
1516        iAdd = 1
1517        new = True
1518        while new:
1519            newAtms = NeighDict[riding[iAdd]]
1520            for At in newAtms:
1521                new = False
1522                if At not in riding:
1523                    riding.append(At)
1524                    new = True
1525            iAdd += 1
1526            if iAdd < len(riding):
1527                new = True
1528        return riding[2:]
1529                       
1530    def OnDefineTorsSeq(event):
1531        rbKeys = data['Residue'].keys()
1532        rbKeys.remove('AtInfo')
1533        rbNames = [data['Residue'][k]['RBname'] for k in rbKeys]
1534        rbIds = dict(zip(rbNames,rbKeys))
1535        rbNames.sort()
1536        rbId = 0
1537        if len(rbNames) == 0:
1538            print 'There are no rigid bodies defined'
1539            G2frame.ErrorDialog('No rigid bodies','There are no rigid bodies defined',
1540                                parent=G2frame)
1541            return
1542        elif len(rbNames) > 1:
1543            dlg = wx.SingleChoiceDialog(G2frame,'Select rigid body for torsion sequence','Torsion sequence',rbNames)
1544            if dlg.ShowModal() == wx.ID_OK:
1545                sel = dlg.GetSelection()
1546                rbId = rbIds[rbNames[sel]]
1547                rbData = data['Residue'][rbId]
1548            dlg.Destroy()
1549        else:
1550            rbId = rbIds[rbNames[0]]
1551            rbData = data['Residue'][rbId]
1552        if not len(rbData):
1553            return
1554        atNames = rbData['atNames']
1555        AtInfo = data['Residue']['AtInfo']
1556        atTypes = rbData['rbTypes']
1557        XYZ = rbData['rbXYZ']
1558        neighDict = FindAllNeighbors(XYZ,atTypes,atNames,AtInfo)
1559        TargList = []           
1560        dlg = wx.SingleChoiceDialog(G2frame,'Select origin atom for torsion sequence','Origin atom',rbData['atNames'])
1561        if dlg.ShowModal() == wx.ID_OK:
1562            Orig = dlg.GetSelection()
1563            TargList = neighDict[atNames[Orig]]
1564        dlg.Destroy()
1565        if not len(TargList):
1566            return
1567        dlg = wx.SingleChoiceDialog(G2frame,'Select pivot atom for torsion sequence','Pivot atom',TargList)
1568        if dlg.ShowModal() == wx.ID_OK:
1569            Piv = atNames.index(TargList[dlg.GetSelection()])
1570            riding = FindRiding(atNames[Orig],atNames[Piv],neighDict)
1571            Riding = []
1572            for atm in riding:
1573                Riding.append(atNames.index(atm))
1574            rbData['rbSeq'].append([Orig,Piv,0.0,Riding])           
1575        dlg.Destroy()
1576        UpdateResidueRB()
1577
1578    def UpdateVectorRB(Scroll=0):
1579        AtInfo = data['Vector']['AtInfo']
1580        refChoice = {}
1581        if 'DELETED' in str(G2frame.GetStatusBar()):   #seems to be no other way to do this (wx bug)
1582            if GSASIIpath.GetConfigValue('debug'):
1583                print 'wx error: Rigid Body/Status not cleanly deleted after Refine'
1584            return
1585        SetStatusLine(' You may use e.g. "c60" or "s60" for a vector entry')
1586        def rbNameSizer(rbId,rbData):
1587
1588            def OnRBName(event):
1589                event.Skip()
1590                Obj = event.GetEventObject()
1591                rbData['RBname'] = Obj.GetValue()
1592               
1593            def OnDelRB(event):
1594                Obj = event.GetEventObject()
1595                rbId = Indx[Obj.GetId()]
1596                if rbId in data['Vector']:
1597                    del data['Vector'][rbId]
1598                    data['RBIds']['Vector'].remove(rbId)
1599                    rbData['useCount'] -= 1
1600                wx.CallAfter(UpdateVectorRB)
1601               
1602            def OnPlotRB(event):
1603                Obj = event.GetEventObject()
1604                Obj.SetValue(False)
1605                G2plt.PlotRigidBody(G2frame,'Vector',AtInfo,rbData,plotDefaults)
1606           
1607            nameSizer = wx.BoxSizer(wx.HORIZONTAL)
1608            nameSizer.Add(wx.StaticText(VectorRBDisplay,-1,'Rigid body name: '),
1609                0,wx.ALIGN_CENTER_VERTICAL)
1610            RBname = wx.TextCtrl(VectorRBDisplay,-1,rbData['RBname'])
1611            Indx[RBname.GetId()] = rbId
1612            RBname.Bind(wx.EVT_TEXT_ENTER,OnRBName)
1613            RBname.Bind(wx.EVT_KILL_FOCUS,OnRBName)
1614            nameSizer.Add(RBname,0,wx.ALIGN_CENTER_VERTICAL)
1615            nameSizer.Add((5,0),)
1616            plotRB = wx.CheckBox(VectorRBDisplay,-1,'Plot?')
1617            Indx[plotRB.GetId()] = rbId
1618            plotRB.Bind(wx.EVT_CHECKBOX,OnPlotRB)
1619            nameSizer.Add(plotRB,0,wx.ALIGN_CENTER_VERTICAL)
1620            nameSizer.Add((5,0),)
1621            if not rbData['useCount']:
1622                delRB = wx.CheckBox(VectorRBDisplay,-1,'Delete?')
1623                Indx[delRB.GetId()] = rbId
1624                delRB.Bind(wx.EVT_CHECKBOX,OnDelRB)
1625                nameSizer.Add(delRB,0,wx.ALIGN_CENTER_VERTICAL)
1626            return nameSizer
1627           
1628        def rbRefAtmSizer(rbId,rbData):
1629           
1630            def OnRefSel(event):
1631                Obj = event.GetEventObject()
1632                iref = Indx[Obj.GetId()]
1633                sel = Obj.GetValue()
1634                rbData['rbRef'][iref] = atNames.index(sel)
1635                FillRefChoice(rbId,rbData)
1636           
1637            refAtmSizer = wx.BoxSizer(wx.HORIZONTAL)
1638            atNames = [name+str(i) for i,name in enumerate(rbData['rbTypes'])]
1639            rbRef = rbData.get('rbRef',[0,1,2,False])
1640            rbData['rbRef'] = rbRef
1641            if rbData['useCount']:
1642                refAtmSizer.Add(wx.StaticText(VectorRBDisplay,-1,
1643                    'Orientation reference atoms A-B-C: %s, %s, %s'%(atNames[rbRef[0]], \
1644                     atNames[rbRef[1]],atNames[rbRef[2]])),0)
1645            else:
1646                refAtmSizer.Add(wx.StaticText(VectorRBDisplay,-1,
1647                    'Orientation reference atoms A-B-C: '),0,wx.ALIGN_CENTER_VERTICAL)
1648                for i in range(3):
1649                    choices = [atNames[j] for j in refChoice[rbId][i]]
1650                    refSel = wx.ComboBox(VectorRBDisplay,-1,value='',
1651                        choices=choices,style=wx.CB_READONLY|wx.CB_DROPDOWN)
1652                    refSel.SetValue(atNames[rbRef[i]])
1653                    refSel.Bind(wx.EVT_COMBOBOX, OnRefSel)
1654                    Indx[refSel.GetId()] = i
1655                    refAtmSizer.Add(refSel,0,wx.ALIGN_CENTER_VERTICAL)
1656            return refAtmSizer
1657                       
1658        def rbVectMag(rbId,imag,rbData):
1659           
1660            def OnRBVectorMag(event):
1661                event.Skip()
1662                Obj = event.GetEventObject()
1663                rbId,imag = Indx[Obj.GetId()]
1664                try:
1665                    val = float(Obj.GetValue())
1666                    if val <= 0.:
1667                        raise ValueError
1668                    rbData['VectMag'][imag] = val
1669                except ValueError:
1670                    pass
1671                Obj.SetValue('%8.4f'%(val))
1672                wx.CallAfter(UpdateVectorRB,VectorRB.GetScrollPos(wx.VERTICAL))
1673                G2plt.PlotRigidBody(G2frame,'Vector',AtInfo,data['Vector'][rbId],plotDefaults)
1674               
1675            def OnRBVectorRef(event):
1676                Obj = event.GetEventObject()
1677                rbId,imag = Indx[Obj.GetId()]
1678                rbData['VectRef'][imag] = Obj.GetValue()
1679                       
1680            magSizer = wx.wx.BoxSizer(wx.HORIZONTAL)
1681            magSizer.Add(wx.StaticText(VectorRBDisplay,-1,'Translation magnitude: '),
1682                0,wx.ALIGN_CENTER_VERTICAL)
1683            magValue = wx.TextCtrl(VectorRBDisplay,-1,'%8.4f'%(rbData['VectMag'][imag]))
1684            Indx[magValue.GetId()] = [rbId,imag]
1685            magValue.Bind(wx.EVT_TEXT_ENTER,OnRBVectorMag)
1686            magValue.Bind(wx.EVT_KILL_FOCUS,OnRBVectorMag)
1687            magSizer.Add(magValue,0,wx.ALIGN_CENTER_VERTICAL)
1688            magSizer.Add((5,0),)
1689            magref = wx.CheckBox(VectorRBDisplay,-1,label=' Refine?') 
1690            magref.SetValue(rbData['VectRef'][imag])
1691            magref.Bind(wx.EVT_CHECKBOX,OnRBVectorRef)
1692            Indx[magref.GetId()] = [rbId,imag]
1693            magSizer.Add(magref,0,wx.ALIGN_CENTER_VERTICAL)
1694            return magSizer
1695           
1696        def rbVectors(rbId,imag,mag,XYZ,rbData):
1697
1698            def TypeSelect(event):
1699                AtInfo = data['Vector']['AtInfo']
1700                r,c = event.GetRow(),event.GetCol()
1701                if vecGrid.GetColLabelValue(c) == 'Type':
1702                    PE = G2elemGUI.PickElement(G2frame,oneOnly=True)
1703                    if PE.ShowModal() == wx.ID_OK:
1704                        if PE.Elem != 'None':
1705                            El = PE.Elem.strip().lower().capitalize()
1706                            if El not in AtInfo:
1707                                Info = G2elem.GetAtomInfo(El)
1708                                AtInfo[El] = [Info['Drad'],Info['Color']]
1709                            rbData['rbTypes'][r] = El
1710                            vecGrid.SetCellValue(r,c,El)
1711                    PE.Destroy()
1712                wx.CallAfter(UpdateVectorRB,VectorRB.GetScrollPos(wx.VERTICAL))
1713
1714            def ChangeCell(event):
1715                r,c =  event.GetRow(),event.GetCol()
1716                if r >= 0 and (0 <= c < 3):
1717                    try:
1718                        val = float(vecGrid.GetCellValue(r,c))
1719                        rbData['rbVect'][imag][r][c] = val
1720                    except ValueError:
1721                        pass
1722                G2plt.PlotRigidBody(G2frame,'Vector',AtInfo,data['Vector'][rbId],plotDefaults)
1723                wx.CallAfter(UpdateVectorRB,VectorRB.GetScrollPos(wx.VERTICAL))
1724
1725            vecSizer = wx.BoxSizer()
1726            Types = 3*[wg.GRID_VALUE_FLOAT+':10,5',]+[wg.GRID_VALUE_STRING,]+3*[wg.GRID_VALUE_FLOAT+':10,5',]
1727            colLabels = ['Vector x','Vector y','Vector z','Type','Cart x','Cart y','Cart z']
1728            table = []
1729            rowLabels = []
1730            for ivec,xyz in enumerate(rbData['rbVect'][imag]):
1731                table.append(list(xyz)+[rbData['rbTypes'][ivec],]+list(XYZ[ivec]))
1732                rowLabels.append(str(ivec))
1733            vecTable = G2G.Table(table,rowLabels=rowLabels,colLabels=colLabels,types=Types)
1734            vecGrid = G2G.GSGrid(VectorRBDisplay)
1735            vecGrid.SetTable(vecTable, True)
1736            vecGrid.Bind(wg.EVT_GRID_CELL_CHANGE, ChangeCell)
1737            if not imag:
1738                vecGrid.Bind(wg.EVT_GRID_CELL_LEFT_DCLICK, TypeSelect)
1739            attr = wx.grid.GridCellAttr()
1740            attr.SetEditor(G2G.GridFractionEditor(vecGrid))
1741            for c in range(3):
1742                vecGrid.SetColAttr(c, attr)
1743            for row in range(vecTable.GetNumberRows()):
1744                if imag:
1745                    vecGrid.SetCellStyle(row,3,VERY_LIGHT_GREY,True)                   
1746                for col in [4,5,6]:
1747                    vecGrid.SetCellStyle(row,col,VERY_LIGHT_GREY,True)
1748#            vecGrid.SetScrollRate(0,0)
1749            vecGrid.AutoSizeColumns(False)
1750            vecSizer.Add(vecGrid)
1751            return vecSizer
1752       
1753        def FillRefChoice(rbId,rbData):
1754            choiceIds = [i for i in range(len(rbData['rbTypes']))]
1755           
1756            rbRef = rbData.get('rbRef',[-1,-1,-1,False])
1757            for i in range(3):
1758                choiceIds.remove(rbRef[i])
1759            refChoice[rbId] = [choiceIds[:],choiceIds[:],choiceIds[:]]
1760            for i in range(3):
1761                refChoice[rbId][i].append(rbRef[i])
1762                refChoice[rbId][i].sort()     
1763           
1764        if VectorRB.GetSizer(): VectorRB.GetSizer().Clear(True)
1765        VectorRBSizer = wx.BoxSizer(wx.VERTICAL)
1766        for rbId in data['RBIds']['Vector']:
1767            if rbId != 'AtInfo':
1768                rbData = data['Vector'][rbId]
1769                FillRefChoice(rbId,rbData)
1770                VectorRBSizer.Add(rbNameSizer(rbId,rbData),0)
1771                VectorRBSizer.Add(rbRefAtmSizer(rbId,rbData),0)
1772                XYZ = np.array([[0.,0.,0.] for Ty in rbData['rbTypes']])
1773                for imag,mag in enumerate(rbData['VectMag']):
1774                    XYZ += mag*rbData['rbVect'][imag]
1775                    VectorRBSizer.Add(rbVectMag(rbId,imag,rbData),0)
1776                    VectorRBSizer.Add(rbVectors(rbId,imag,mag,XYZ,rbData),0)
1777                VectorRBSizer.Add((5,5),0)
1778                data['Vector'][rbId]['rbXYZ'] = XYZ       
1779        VectorRBSizer.Layout()   
1780        VectorRBDisplay.SetSizer(VectorRBSizer,True)
1781        Size = VectorRBSizer.GetMinSize()
1782        Size[0] += 40
1783        Size[1] = max(Size[1],450) + 20
1784        VectorRBDisplay.SetSize(Size)
1785        VectorRB.SetScrollbars(10,10,Size[0]/10-4,Size[1]/10-1)
1786        VectorRB.Scroll(0,Scroll)
1787       
1788    def UpdateResidueRB():
1789        AtInfo = data['Residue']['AtInfo']
1790        refChoice = {}
1791        RefObjs = []
1792
1793        def rbNameSizer(rbId,rbData):
1794
1795            def OnRBName(event):
1796                Obj = event.GetEventObject()
1797                rbData['RBname'] = Obj.GetValue()
1798               
1799            def OnDelRB(event):
1800                Obj = event.GetEventObject()
1801                rbId = Indx[Obj.GetId()]
1802                if rbId in data['Residue']: 
1803                    del data['Residue'][rbId]
1804                    data['RBIds']['Residue'].remove(rbId)
1805                wx.CallAfter(UpdateResidueRB)
1806               
1807            def OnStripH(event):
1808                Obj = event.GetEventObject()
1809                rbId = Indx[Obj.GetId()]
1810                if rbId in data['Residue']:
1811                    newNames = []
1812                    newTypes = []
1813                    newXYZ = []
1814                    for i,atype in enumerate(rbData['rbTypes']):
1815                        if atype != 'H':
1816                            newNames.append(rbData['atNames'][i])
1817                            newTypes.append(rbData['rbTypes'][i])
1818                            newXYZ.append(rbData['rbXYZ'][i])
1819                    rbData['atNames'] = newNames
1820                    rbData['rbTypes'] = newTypes
1821                    rbData['rbXYZ'] = newXYZ
1822                wx.CallAfter(UpdateResidueRB)
1823                   
1824            def OnPlotRB(event):
1825                Obj = event.GetEventObject()
1826                Obj.SetValue(False)
1827                G2plt.PlotRigidBody(G2frame,'Residue',AtInfo,rbData,plotDefaults)
1828           
1829            nameSizer = wx.BoxSizer(wx.HORIZONTAL)
1830            nameSizer.Add(wx.StaticText(ResidueRBDisplay,-1,'Residue name: '),
1831                0,wx.ALIGN_CENTER_VERTICAL)
1832            RBname = wx.TextCtrl(ResidueRBDisplay,-1,rbData['RBname'])
1833            Indx[RBname.GetId()] = rbId
1834            RBname.Bind(wx.EVT_TEXT_ENTER,OnRBName)
1835            RBname.Bind(wx.EVT_KILL_FOCUS,OnRBName)
1836            nameSizer.Add(RBname,0,wx.ALIGN_CENTER_VERTICAL)
1837            nameSizer.Add((5,0),)
1838            plotRB = wx.CheckBox(ResidueRBDisplay,-1,'Plot?')
1839            Indx[plotRB.GetId()] = rbId
1840            plotRB.Bind(wx.EVT_CHECKBOX,OnPlotRB)
1841            nameSizer.Add(plotRB,0,wx.ALIGN_CENTER_VERTICAL)
1842            nameSizer.Add((5,0),)
1843            if not rbData['useCount']:
1844                delRB = wx.CheckBox(ResidueRBDisplay,-1,'Delete?')
1845                Indx[delRB.GetId()] = rbId
1846                delRB.Bind(wx.EVT_CHECKBOX,OnDelRB)
1847                nameSizer.Add(delRB,0,wx.ALIGN_CENTER_VERTICAL)
1848                if 'H'  in rbData['rbTypes']:
1849                    stripH = wx.CheckBox(ResidueRBDisplay,-1,'Strip H-atoms?')
1850                    Indx[stripH.GetId()] = rbId
1851                    stripH.Bind(wx.EVT_CHECKBOX,OnStripH)
1852                    nameSizer.Add(stripH,0,wx.ALIGN_CENTER_VERTICAL)
1853            return nameSizer
1854           
1855        def rbResidues(rbId,rbData):
1856           
1857            def TypeSelect(event):
1858                AtInfo = data['Residue']['AtInfo']
1859                r,c = event.GetRow(),event.GetCol()
1860                if vecGrid.GetColLabelValue(c) == 'Type':
1861                    PE = G2elemGUI.PickElement(G2frame,oneOnly=True)
1862                    if PE.ShowModal() == wx.ID_OK:
1863                        if PE.Elem != 'None':
1864                            El = PE.Elem.strip().lower().capitalize()
1865                            if El not in AtInfo:
1866                                Info = G2elem.GetAtomInfo(El)
1867                                AtInfo[El] = [Info['Drad']['Color']]
1868                            rbData['rbTypes'][r] = El
1869                            vecGrid.SetCellValue(r,c,El)
1870                    PE.Destroy()
1871
1872            def ChangeCell(event):
1873                r,c =  event.GetRow(),event.GetCol()
1874                if r >= 0 and (0 <= c < 3):
1875                    try:
1876                        val = float(vecGrid.GetCellValue(r,c))
1877                        rbData['rbXYZ'][r][c] = val
1878                    except ValueError:
1879                        pass
1880                       
1881            def RowSelect(event):
1882                r,c =  event.GetRow(),event.GetCol()
1883                if c < 0:                   #only row clicks
1884                    for vecgrid in resList:
1885                        vecgrid.ClearSelection()
1886                    vecGrid.SelectRow(r,True)
1887
1888            def OnRefSel(event):
1889                Obj = event.GetEventObject()
1890                iref,res,jref = Indx[Obj.GetId()]
1891                sel = Obj.GetValue()
1892                ind = atNames.index(sel)
1893                if rbData['rbTypes'][ind] == 'H':
1894                    G2G.G2MessageBox(G2frame,'You should not select an H-atom for rigid body orientation')
1895                rbData['rbRef'][iref] = ind
1896                FillRefChoice(rbId,rbData)
1897                for i,ref in enumerate(RefObjs[jref]):
1898                    ref.SetItems([atNames[j] for j in refChoice[rbId][i]])
1899                    ref.SetValue(atNames[rbData['rbRef'][i]])
1900                if not iref:     #origin change
1901                    rbXYZ = rbData['rbXYZ']
1902                    rbXYZ -= rbXYZ[ind]
1903                    res.ClearSelection()
1904                    resTable = res.GetTable()
1905                    for r in range(res.GetNumberRows()):
1906                        row = resTable.GetRowValues(r)
1907                        row[2:4] = rbXYZ[r]
1908                        resTable.SetRowValues(r,row)
1909                    res.ForceRefresh()
1910                    G2plt.PlotRigidBody(G2frame,'Residue',AtInfo,rbData,plotDefaults)
1911               
1912            Types = 2*[wg.GRID_VALUE_STRING,]+3*[wg.GRID_VALUE_FLOAT+':10,5',]
1913            colLabels = ['Name','Type','Cart x','Cart y','Cart z']
1914            table = []
1915            rowLabels = []
1916            for ivec,xyz in enumerate(rbData['rbXYZ']):
1917                table.append([rbData['atNames'][ivec],]+[rbData['rbTypes'][ivec],]+list(xyz))
1918                rowLabels.append(str(ivec))
1919            vecTable = G2G.Table(table,rowLabels=rowLabels,colLabels=colLabels,types=Types)
1920            vecGrid = G2G.GSGrid(ResidueRBDisplay)
1921            Indx[vecGrid.GetId()] = rbId
1922            resList.append(vecGrid)
1923            vecGrid.SetTable(vecTable, True)
1924            vecGrid.Bind(wg.EVT_GRID_CELL_CHANGE, ChangeCell)
1925            vecGrid.Bind(wg.EVT_GRID_CELL_LEFT_DCLICK, TypeSelect)
1926            vecGrid.Bind(wg.EVT_GRID_LABEL_LEFT_CLICK, RowSelect)
1927            attr = wx.grid.GridCellAttr()
1928            attr.SetEditor(G2G.GridFractionEditor(vecGrid))
1929            for c in range(3):
1930                vecGrid.SetColAttr(c, attr)
1931            for row in range(vecTable.GetNumberRows()):
1932                for col in range(5):
1933                    vecGrid.SetCellStyle(row,col,VERY_LIGHT_GREY,True)
1934#            vecGrid.SetScrollRate(0,0)
1935            vecGrid.AutoSizeColumns(False)
1936            vecSizer = wx.BoxSizer()
1937            vecSizer.Add(vecGrid)
1938           
1939            refAtmSizer = wx.BoxSizer(wx.HORIZONTAL)
1940            atNames = rbData['atNames']
1941            rbRef = rbData['rbRef']
1942            if rbData['rbRef'][3] or rbData['useCount']:
1943                refAtmSizer.Add(wx.StaticText(ResidueRBDisplay,-1,
1944                    'Orientation reference non-H atoms A-B-C: %s, %s, %s'%(atNames[rbRef[0]], \
1945                     atNames[rbRef[1]],atNames[rbRef[2]])),0)
1946            else:
1947                refAtmSizer.Add(wx.StaticText(ResidueRBDisplay,-1,
1948                    'Orientation reference non-H atoms A-B-C: '),0,wx.ALIGN_CENTER_VERTICAL)
1949                refObj = [0,0,0]
1950                for i in range(3):
1951                    choices = [atNames[j] for j in refChoice[rbId][i]]
1952                    refSel = wx.ComboBox(ResidueRBDisplay,-1,value='',
1953                        choices=choices,style=wx.CB_READONLY|wx.CB_DROPDOWN)
1954                    refSel.SetValue(atNames[rbRef[i]])
1955                    refSel.Bind(wx.EVT_COMBOBOX, OnRefSel)
1956                    Indx[refSel.GetId()] = [i,vecGrid,len(RefObjs)]
1957                    refObj[i] = refSel
1958                    refAtmSizer.Add(refSel,0,wx.ALIGN_CENTER_VERTICAL)
1959                RefObjs.append(refObj)
1960           
1961            mainSizer = wx.BoxSizer(wx.VERTICAL)
1962            mainSizer.Add(refAtmSizer)
1963            mainSizer.Add(vecSizer)
1964            return mainSizer
1965           
1966        def SeqSizer(angSlide,rbId,iSeq,Seq,atNames):
1967           
1968            def ChangeAngle(event):
1969                event.Skip()
1970                Obj = event.GetEventObject()
1971                rbId,Seq = Indx[Obj.GetId()][:2]
1972                val = Seq[2]
1973                try:
1974                    val = float(Obj.GetValue())
1975                    Seq[2] = val
1976                except ValueError:
1977                    pass
1978                Obj.SetValue('%8.2f'%(val))
1979                G2plt.PlotRigidBody(G2frame,'Residue',AtInfo,data['Residue'][rbId],plotDefaults)
1980               
1981            def OnRadBtn(event):
1982                Obj = event.GetEventObject()
1983                Seq,iSeq,angId = Indx[Obj.GetId()]
1984                data['Residue'][rbId]['SelSeq'] = [iSeq,angId]
1985                angSlide.SetValue(int(100*Seq[2]))
1986               
1987            def OnDelBtn(event):
1988                Obj = event.GetEventObject()
1989                rbId,Seq = Indx[Obj.GetId()]
1990                data['Residue'][rbId]['rbSeq'].remove(Seq)       
1991                wx.CallAfter(UpdateResidueRB)
1992           
1993            seqSizer = wx.FlexGridSizer(0,5,2,2)
1994            seqSizer.AddGrowableCol(3,0)
1995            iBeg,iFin,angle,iMove = Seq
1996            ang = wx.TextCtrl(ResidueRBDisplay,-1,'%8.2f'%(angle),size=(50,20))
1997            if not iSeq:
1998                radBt = wx.RadioButton(ResidueRBDisplay,-1,'',style=wx.RB_GROUP)
1999                data['Residue'][rbId]['SelSeq'] = [iSeq,ang.GetId()]
2000            else:
2001                radBt = wx.RadioButton(ResidueRBDisplay,-1,'')
2002            radBt.Bind(wx.EVT_RADIOBUTTON,OnRadBtn)                   
2003            seqSizer.Add(radBt)
2004            delBt = wx.RadioButton(ResidueRBDisplay,-1,'')
2005            delBt.Bind(wx.EVT_RADIOBUTTON,OnDelBtn)
2006            seqSizer.Add(delBt)
2007            bond = wx.TextCtrl(ResidueRBDisplay,-1,'%s %s'%(atNames[iBeg],atNames[iFin]),size=(50,20))
2008            seqSizer.Add(bond,0,wx.ALIGN_CENTER_VERTICAL)
2009            Indx[radBt.GetId()] = [Seq,iSeq,ang.GetId()]
2010            Indx[delBt.GetId()] = [rbId,Seq]
2011            Indx[ang.GetId()] = [rbId,Seq,ang]
2012            ang.Bind(wx.EVT_TEXT_ENTER,ChangeAngle)
2013            ang.Bind(wx.EVT_KILL_FOCUS,ChangeAngle)
2014            seqSizer.Add(ang,0,wx.ALIGN_CENTER_VERTICAL)
2015            atms = ''
2016            for i in iMove:   
2017                atms += ' %s,'%(atNames[i])
2018            moves = wx.TextCtrl(ResidueRBDisplay,-1,atms[:-1],size=(200,20))
2019            seqSizer.Add(moves,1,wx.ALIGN_CENTER_VERTICAL|wx.EXPAND|wx.RIGHT)
2020            return seqSizer
2021           
2022        def SlideSizer():
2023           
2024            def OnSlider(event):
2025                Obj = event.GetEventObject()
2026                rbData = Indx[Obj.GetId()]
2027                iSeq,angId = rbData['SelSeq']
2028                val = float(Obj.GetValue())/100.
2029                rbData['rbSeq'][iSeq][2] = val
2030                Indx[angId][2].SetValue('%8.2f'%(val))
2031                G2plt.PlotRigidBody(G2frame,'Residue',AtInfo,rbData,plotDefaults)
2032           
2033            slideSizer = wx.BoxSizer(wx.HORIZONTAL)
2034            slideSizer.Add(wx.StaticText(ResidueRBDisplay,-1,'Selected torsion angle:'),0)
2035            iSeq,angId = rbData['SelSeq']
2036            angSlide = wx.Slider(ResidueRBDisplay,-1,
2037                int(100*rbData['rbSeq'][iSeq][2]),0,36000,size=(200,20),
2038                style=wx.SL_HORIZONTAL)
2039            angSlide.Bind(wx.EVT_SLIDER, OnSlider)
2040            Indx[angSlide.GetId()] = rbData
2041            slideSizer.Add(angSlide,0)           
2042            return slideSizer,angSlide
2043           
2044        def FillRefChoice(rbId,rbData):
2045            choiceIds = [i for i in range(len(rbData['atNames']))]
2046            for seq in rbData['rbSeq']:
2047                for i in seq[3]:
2048                    try:
2049                        choiceIds.remove(i)
2050                    except ValueError:
2051                        pass
2052            rbRef = rbData['rbRef']
2053            for i in range(3):
2054                try:
2055                    choiceIds.remove(rbRef[i])
2056                except ValueError:
2057                    pass
2058            refChoice[rbId] = [choiceIds[:],choiceIds[:],choiceIds[:]]
2059            for i in range(3):
2060                refChoice[rbId][i].append(rbRef[i])
2061                refChoice[rbId][i].sort()     
2062           
2063        ResidueRBDisplay.DestroyChildren()
2064        ResidueRBSizer = wx.BoxSizer(wx.VERTICAL)
2065        for rbId in data['RBIds']['Residue']:
2066            rbData = data['Residue'][rbId]
2067            if len(rbData['rbXYZ']) < 3:    #patch - skip around bad RBs with too few atoms
2068                continue
2069            FillRefChoice(rbId,rbData)
2070            ResidueRBSizer.Add(rbNameSizer(rbId,rbData),0)
2071            ResidueRBSizer.Add(rbResidues(rbId,rbData),0)
2072            ResidueRBSizer.Add((5,5),0)
2073            if rbData['rbSeq']:
2074                slideSizer,angSlide = SlideSizer()
2075            if len(rbData['rbSeq']):
2076                ResidueRBSizer.Add(wx.StaticText(ResidueRBDisplay,-1,
2077                    'Sel  Del  Bond             Angle      Riding atoms'),
2078                    0,wx.ALIGN_CENTER_VERTICAL)                       
2079            for iSeq,Seq in enumerate(rbData['rbSeq']):
2080                ResidueRBSizer.Add(SeqSizer(angSlide,rbId,iSeq,Seq,rbData['atNames']))
2081            if rbData['rbSeq']:
2082                ResidueRBSizer.Add(slideSizer,)
2083            ResidueRBSizer.Add(wx.StaticText(ResidueRBDisplay,-1,70*'-'))
2084
2085        ResidueRBSizer.Add((5,25),)
2086        ResidueRBSizer.Layout()   
2087        ResidueRBDisplay.SetSizer(ResidueRBSizer,True)
2088        Size = ResidueRBSizer.GetMinSize()
2089        Size[0] += 40
2090        Size[1] = max(Size[1],450) + 20
2091        ResidueRBDisplay.SetSize(Size)
2092        ResidueRB.SetScrollbars(10,10,Size[0]/10-4,Size[1]/10-1)
2093       
2094    def SetStatusLine(text):
2095        G2frame.GetStatusBar().SetStatusText(text,1)                                     
2096
2097    G2gd.SetDataMenuBar(G2frame,G2frame.dataWindow.RigidBodyMenu)
2098    G2frame.SetTitle('Rigid bodies')
2099    SetStatusLine('')
2100    UpdateVectorRB()
2101    G2frame.rbBook.Bind(wx.aui.EVT_AUINOTEBOOK_PAGE_CHANGED, OnPageChanged)
2102    wx.CallAfter(OnPageChanged,None)
Note: See TracBrowser for help on using the repository browser.