source: trunk/GSASIIconstrGUI.py @ 3057

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

more work on constraints: prevent use of perviously constrained variables in new equivalences; include sym. generated constraints in checks; do not allow error-generating constraints to be added

  • Property svn:eol-style set to native
  • Property svn:keywords set to Date Author Revision URL Id
File size: 93.1 KB
Line 
1# -*- coding: utf-8 -*-
2#GSASIIconstrGUI - constraint GUI routines
3########### SVN repository information ###################
4# $Date: 2017-09-08 22:56:57 +0000 (Fri, 08 Sep 2017) $
5# $Author: toby $
6# $Revision: 3057 $
7# $URL: trunk/GSASIIconstrGUI.py $
8# $Id: GSASIIconstrGUI.py 3057 2017-09-08 22:56:57Z 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: 3057 $")
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' or page[1] == 'sym':
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 CheckConstraints(constraintSet):
501        '''Check for errors in a set of constraints. Constraints based on symmetry (etc.)
502        are generated by running :func:`GSASIIstrIO.GetPhaseData`.
503        '''
504        G2mv.InitVars()   
505        constDictList,fixedList,ignored = G2stIO.ProcessConstraints(constraintSet)
506        # generate symmetry constraints to check for conflicts
507        rigidbodyDict = G2frame.GPXtree.GetItemPyData(   
508            G2gd.GetGPXtreeItemId(G2frame,G2frame.root,'Rigid bodies'))
509        rbIds = rigidbodyDict.get('RBIds',{'Vector':[],'Residue':[]})
510        rbVary,rbDict = G2stIO.GetRigidBodyModels(rigidbodyDict,Print=False)
511        Natoms,atomIndx,phaseVary,phaseDict,pawleyLookup,FFtables,BLtables,MFtables,maxSSwave = G2stIO.GetPhaseData(
512            Phases,RestraintDict=None,rbIds=rbIds,Print=False) # generates atom symmetry constraints
513        return G2mv.CheckConstraints('',constDictList,fixedList)
514
515    def CheckAddedConstraint(newcons):
516        '''Check a new constraint that has just been input.
517        If there is an error display a message and discard the last entry
518
519        Since the varylist is not available, no warning messages
520        should be generated here
521
522        :returns: True if constraint should be added
523        '''
524        allcons1 = []
525        for key in data:
526            if key.startswith('_'): continue
527            allcons1 += data[key]
528        allcons = allcons1[:]
529        allcons += newcons
530        if not len(allcons): return True
531        errmsg,warnmsg = CheckConstraints(allcons)
532        if errmsg:
533            res = G2frame.ErrorDialog('Constraint Error',
534                'Error with newly added constraint:\n'+errmsg+
535                '\nIgnoring newly added constraint',parent=G2frame)
536            # reset error status
537            errmsg,warnmsg = CheckConstraints(allcons1)
538            if errmsg:
539                print errmsg
540                print G2mv.VarRemapShow([],True)
541            return False
542        elif warnmsg:
543            print 'Unexpected contraint warning:\n',warnmsg
544        return True
545
546    def CheckChangedConstraint():
547        '''Check all constraints after an edit has been made.
548        If there is an error display a message and reject the change.
549
550        Since the varylist is not available, no warning messages
551        should be generated.
552       
553        :returns: True if the edit should be retained
554        '''
555        allcons = []
556        for key in data:
557            if key.startswith('_'): continue
558            allcons += data[key]
559        if not len(allcons): return True
560        errmsg,warnmsg = CheckConstraints(allcons)
561        if errmsg:
562            res = G2frame.ErrorDialog('Constraint Error',
563                'Error after editing constraint:\n'+errmsg+
564                '\nDiscarding last constraint edit',parent=G2frame)
565            # reset error status
566            errmsg,warnmsg = CheckConstraints(allcons1)
567            if errmsg:
568                print errmsg
569                print G2mv.VarRemapShow([],True)
570            return False
571        elif warnmsg:
572            print 'Unexpected contraint warning:\n',warnmsg
573        return True
574             
575    def PageSelection(page):
576        'Decode page reference'
577        if page[1] == "phs":
578            vartype = "phase"
579            varList = phaseList
580            constrDictEnt = 'Phase'
581        elif page[1] == "hap":
582            vartype = "Histogram*Phase"
583            varList = hapList
584            constrDictEnt = 'HAP'
585        elif page[1] == "hst":
586            vartype = "Histogram"
587            varList = histList
588            constrDictEnt = 'Hist'
589        elif page[1] == "glb":
590            vartype = "Global"
591            varList = globalList
592            constrDictEnt = 'Global'
593        elif page[1] == "sym":
594            return None,None,None
595        else:
596            raise Exception,'Should not happen!'
597        return vartype,varList,constrDictEnt
598
599    def OnAddHold(event):
600        '''Create a new Hold constraint
601
602        Hold constraints allows the user to select one variable (the list of available
603        variables depends on which tab is currently active).
604        '''
605        page = G2frame.Page
606        vartype,varList,constrDictEnt = PageSelection(page)
607        if vartype is None: return
608        title1 = "Hold "+vartype+" variable"
609        if not varList:
610            G2frame.ErrorDialog('No variables','There are no variables of type '+vartype,
611                parent=G2frame)
612            return
613        l2 = l1 = 1
614        for i in varList:
615            l1 = max(l1,len(i))
616            loc,desc = G2obj.VarDescr(i)
617            l2 = max(l2,len(loc))
618        fmt = "{:"+str(l1)+"s} {:"+str(l2)+"s} {:s}"
619        varListlbl = [fmt.format(i,*G2obj.VarDescr(i)) for i in varList]
620        #varListlbl = ["("+i+") "+G2obj.fmtVarDescr(i) for i in varList]
621        legend = "Select variables to hold (Will not be varied, even if vary flag is set)"
622        dlg = G2G.G2MultiChoiceDialog(G2frame,
623            legend,title1,varListlbl,toggle=False,size=(625,400),monoFont=True)
624        dlg.CenterOnParent()
625        if dlg.ShowModal() == wx.ID_OK:
626            for sel in dlg.GetSelections():
627                Varb = varList[sel]
628                VarObj = G2obj.G2VarObj(Varb)
629                newcons = [[[0.0,VarObj],None,None,'h']]
630                if CheckAddedConstraint(newcons):
631                    data[constrDictEnt] += newcons
632        dlg.Destroy()
633        wx.CallAfter(OnPageChanged,None)
634       
635    def OnAddEquivalence(event):
636        '''add an Equivalence constraint'''
637        page = G2frame.Page
638        vartype,varList,constrDictEnt = PageSelection(page)
639        if vartype is None: return
640        title1 = "Setup equivalent "+vartype+" variables"
641        title2 = "Select additional "+vartype+" variable(s) to be equivalent with "
642        if not varList:
643            G2frame.ErrorDialog('No variables','There are no variables of type '+vartype,
644                parent=G2frame)
645            return
646#        legend = "Select variables to make equivalent (only one of the variables will be varied when all are set to be varied)"
647        GetAddVars(page,title1,title2,varList,constrDictEnt,'equivalence')
648       
649    def OnAddAtomEquiv(event):
650        ''' Add equivalences between all parameters on atoms '''
651        page = G2frame.Page
652        vartype,varList,constrDictEnt = PageSelection(page)
653        if vartype is None: return
654        title1 = "Setup equivalent atom variables"
655        title2 = "Select additional atoms(s) to be equivalent with "
656        if not varList:
657            G2frame.ErrorDialog('No variables','There are no variables of type '+vartype,
658                parent=G2frame)
659            return
660#        legend = "Select atoms to make equivalent (only one of the atom variables will be varied when all are set to be varied)"
661        GetAddAtomVars(page,title1,title2,varList,constrDictEnt,'equivalence')
662       
663    def OnAddRiding(event):
664        ''' Add riding equivalences between all parameters on atoms '''
665        page = G2frame.Page
666        vartype,varList,constrDictEnt = PageSelection(page)
667        if vartype is None: return
668        title1 = "Setup riding atoms "
669        title2 = "Select additional atoms(s) to ride on "
670        if not varList:
671            G2frame.ErrorDialog('No variables','There are no variables of type '+vartype,
672                parent=G2frame)
673            return
674#        legend = "Select atoms to ride (only one of the atom variables will be varied when all are set to be varied)"
675        GetAddAtomVars(page,title1,title2,varList,constrDictEnt,'riding')
676   
677    def OnAddFunction(event):
678        '''add a Function (new variable) constraint'''
679        page = G2frame.Page
680        vartype,varList,constrDictEnt = PageSelection(page)
681        if vartype is None: return
682        title1 = "Setup new variable based on "+vartype+" variables"
683        title2 = "Include additional "+vartype+" variable(s) to be included with "
684        if not varList:
685            G2frame.ErrorDialog('No variables','There are no variables of type '+vartype,
686                parent=G2frame)
687            return
688#        legend = "Select variables to include in a new variable (the new variable will be varied when all included variables are varied)"
689        GetAddVars(page,title1,title2,varList,constrDictEnt,'function')
690                       
691    def OnAddConstraint(event):
692        '''add a constraint equation to the constraints list'''
693        page = G2frame.Page
694        vartype,varList,constrDictEnt = PageSelection(page)
695        if vartype is None: return
696        title1 = "Setup constraint on "+vartype+" variables"
697        title2 = "Select additional "+vartype+" variable(s) to include in constraint with "
698        if not varList:
699            G2frame.ErrorDialog('No variables','There are no variables of type '+vartype,
700                parent=G2frame)
701            return
702#        legend = "Select variables to include in a constraint equation (the values will be constrainted to equal a specified constant)"
703        GetAddVars(page,title1,title2,varList,constrDictEnt,'constraint')
704
705    def GetAddVars(page,title1,title2,varList,constrDictEnt,constType):
706        '''Get the variables to be added for OnAddEquivalence, OnAddFunction,
707        and OnAddConstraint. Then create and check the constraint.
708        '''
709        #varListlbl = ["("+i+") "+G2obj.fmtVarDescr(i) for i in varList]
710        l2 = l1 = 1
711        for i in varList:
712            l1 = max(l1,len(i))
713            loc,desc = G2obj.VarDescr(i)
714            l2 = max(l2,len(loc))
715        fmt = "{:"+str(l1)+"s} {:"+str(l2)+"s} {:s}"
716        if constType == 'equivalence':
717            omitVars = G2mv.GetDependentVars()
718        else:
719            omitVars = []
720        varList = [i for i in varList if i not in omitVars]
721        varListlbl = [fmt.format(i,*G2obj.VarDescr(i)) for i in varList]
722        dlg = G2G.G2SingleChoiceDialog(G2frame,'Select 1st variable:',
723            title1,varListlbl,monoFont=True,size=(625,400))
724        dlg.CenterOnParent()
725        if dlg.ShowModal() == wx.ID_OK:
726            if constType == 'equivalence':
727                omitVars = G2mv.GetDependentVars() + G2mv.GetIndependentVars()
728            sel = dlg.GetSelection()
729            FrstVarb = varList[sel]
730            VarObj = G2obj.G2VarObj(FrstVarb)
731            moreVarb = FindEquivVarb(FrstVarb,[i for i in varList if i not in omitVars])
732            newcons = SelectVarbs(page,VarObj,moreVarb,title2+FrstVarb,constType)
733            if len(newcons) > 0:
734                if CheckAddedConstraint(newcons):
735                    data[constrDictEnt] += newcons
736        dlg.Destroy()
737        wx.CallAfter(OnPageChanged,None)
738                       
739    def FindNeighbors(phase,FrstName,AtNames):
740        General = phase['General']
741        cx,ct,cs,cia = General['AtomPtrs']
742        Atoms = phase['Atoms']
743        atNames = [atom[ct-1] for atom in Atoms]
744        Cell = General['Cell'][1:7]
745        Amat,Bmat = G2lat.cell2AB(Cell)
746        atTypes = General['AtomTypes']
747        Radii = np.array(General['BondRadii'])
748        AtInfo = dict(zip(atTypes,Radii)) #or General['BondRadii']
749        Orig = atNames.index(FrstName.split()[1])
750        OType = Atoms[Orig][ct]
751        XYZ = G2mth.getAtomXYZ(Atoms,cx)       
752        Neigh = []
753        Dx = np.inner(Amat,XYZ-XYZ[Orig]).T
754        dist = np.sqrt(np.sum(Dx**2,axis=1))
755        sumR = AtInfo[OType]+0.5    #H-atoms only!
756        IndB = ma.nonzero(ma.masked_greater(dist-0.85*sumR,0.))
757        for j in IndB[0]:
758            if j != Orig:
759                Neigh.append(AtNames[j])
760        return Neigh
761       
762    def GetAddAtomVars(page,title1,title2,varList,constrDictEnt,constType):
763        '''Get the atom variables to be added for OnAddAtomEquiv. Then create and
764        check the constraints. Riding for H atoms only.
765        '''
766        Atoms = {G2obj.VarDescr(i)[0]:[] for i in varList if 'Atom' in G2obj.VarDescr(i)[0]}
767        for item in varList:
768            atName = G2obj.VarDescr(item)[0]
769            if atName in Atoms:
770                Atoms[atName].append(item)
771        AtNames = Atoms.keys()
772        AtNames.sort()
773        dlg = G2G.G2SingleChoiceDialog(G2frame,'Select 1st atom:',
774            title1,AtNames,monoFont=True,size=(625,400))
775        dlg.CenterOnParent()
776        FrstAtom = ''
777        if dlg.ShowModal() == wx.ID_OK:
778            sel = dlg.GetSelection()
779            FrstAtom = AtNames[sel]
780            if 'riding' in constType:
781                phaseName = (FrstAtom.split(' in ')[1]).strip()
782                phase = Phases[phaseName]
783                AtNames = FindNeighbors(phase,FrstAtom,AtNames)
784            else:
785                AtNames.remove(FrstAtom)
786        dlg.Destroy()
787        if FrstAtom == '':
788            print 'no atom selected'
789            return
790        dlg = G2G.G2MultiChoiceDialog(
791            G2frame,title2+FrstAtom,
792            'Constrain '+str(FrstAtom)+' with...',AtNames,
793            toggle=False,size=(625,400),monoFont=True)
794        if dlg.ShowModal() == wx.ID_OK:
795            Selections = dlg.GetSelections()[:]
796        else:
797            print 'no target atom selected'
798            dlg.Destroy()
799            return
800        dlg.Destroy()
801        for name in Atoms[FrstAtom]:
802            newcons = []
803            constr = []
804            if 'riding' in constType:
805                if 'AUiso' in name:
806                    constr = [[1.0,G2obj.G2VarObj(name)]]
807                elif 'AU11' in name:
808                    pass
809                elif 'AU' not in name:
810                    constr = [[1.0,G2obj.G2VarObj(name)]]
811            else:
812                constr = [[1.0,G2obj.G2VarObj(name)]]
813            pref = ':'+name.rsplit(':',1)[0].split(':',1)[1]    #get stuff between phase id & atom id
814            for sel in Selections:
815                name2 = Atoms[AtNames[sel]][0]
816                pid = name2.split(':',1)[0]                     #get phase id for 2nd atom
817                id = name2.rsplit(':',1)[-1]                    #get atom no. for 2nd atom
818                if 'riding' in constType:
819                    pref = pid+pref
820                    if 'AUiso' in pref:
821                        parts = pref.split('AUiso')
822                        constr += [[1.2,G2obj.G2VarObj('%s:%s'%(parts[0]+'AUiso',id))]]
823                    elif 'AU' not in pref:
824                        constr += [[1.0,G2obj.G2VarObj('%s:%s'%(pref,id))]]
825                else:
826                    constr += [[1.0,G2obj.G2VarObj('%s:%s'%(pid+pref,id))]]
827            if not constr:
828                continue
829            if 'frac' in pref and 'riding' not in constType:
830                newcons = [constr+[1.0,None,'c']]
831            else:
832                newcons = [constr+[None,None,'e']]
833            if len(newcons) > 0:
834                if CheckAddedConstraint(newcons):
835                    data[constrDictEnt] += newcons
836        wx.CallAfter(OnPageChanged,None)
837                       
838    def MakeConstraintsSizer(name,pageDisplay):
839        '''Creates a sizer displaying all of the constraints entered of
840        the specified type.
841
842        :param str name: the type of constraints to be displayed ('HAP',
843          'Hist', 'Phase', 'Global', 'Sym-Generated')
844        :param wx.Panel pageDisplay: parent panel for sizer
845        :returns: wx.Sizer created by method
846        '''
847        if name == 'Sym-Generated':         #show symmetry generated constraints
848            Sizer1 =  wx.BoxSizer(wx.VERTICAL)
849            Sizer1.Add(wx.StaticText(pageDisplay,wx.ID_ANY,
850                                    'Equivalences generated based on cell/space group input'))
851            Sizer1.Add((-1,5))
852            Sizer = wx.FlexGridSizer(0,2,0,0)
853            Sizer1.Add(Sizer)
854            for sym in G2mv.GetSymEquiv():
855                Sizer.Add(wx.StaticText(pageDisplay,wx.ID_ANY,'EQUIV'),
856                           0,wx.ALIGN_CENTER_VERTICAL|wx.ALIGN_CENTER|wx.RIGHT|wx.LEFT,3)
857                Sizer.Add(wx.StaticText(pageDisplay,wx.ID_ANY,sym))
858                Sizer.Add((-1,-1))
859                Sizer.Add((-1,2))
860            return Sizer1
861        constSizer = wx.FlexGridSizer(0,6,0,0)
862        maxlen = 50 # characters before wrapping a constraint
863        for Id,item in enumerate(data[name]):
864            refineflag = False
865            helptext = ""
866            eqString = ['',]
867            problemItem = False
868            for term in item[:-3]:
869                if str(term[1]) in G2mv.problemVars:
870                    problemItem = True
871            if item[-1] == 'h': # Hold on variable
872                constSizer.Add((-1,-1),0)              # blank space for edit button
873                typeString = 'FIXED'
874                var = str(item[0][1])
875                varMean = G2obj.fmtVarDescr(var)
876                eqString[-1] =  var +'   '
877                helptext = "Prevents variable:\n"+ var + " ("+ varMean + ")\nfrom being changed"
878            elif isinstance(item[-1],str): # not true on original-style (2011?) constraints
879                constEdit = wx.Button(pageDisplay,wx.ID_ANY,'Edit',style=wx.BU_EXACTFIT)
880                constEdit.Bind(wx.EVT_BUTTON,OnConstEdit)
881                constSizer.Add(constEdit,0,wx.LEFT|wx.RIGHT|wx.ALIGN_CENTER_VERTICAL|wx.ALIGN_CENTER,1)            # edit button
882                Indx[constEdit.GetId()] = [Id,name]
883                if item[-1] == 'f':
884                    helptext = "A new variable"
885                    if item[-3]:
886                        helptext += " named "+str(item[-3])
887                    helptext += " is created from a linear combination of the following variables:\n"
888                    for term in item[:-3]:
889                        var = str(term[1])
890                        if len(eqString[-1]) > maxlen:
891                            eqString.append(' ')
892                        m = term[0]
893                        if eqString[-1] != '':
894                            if m >= 0:
895                                eqString[-1] += ' + '
896                            else:
897                                eqString[-1] += ' - '
898                                m = abs(m)
899                        eqString[-1] += '%.3f*%s '%(m,var)
900                        varMean = G2obj.fmtVarDescr(var)
901                        helptext += "\n" + var + " ("+ varMean + ")"
902                    if '_Explain' in data:
903                        if data['_Explain'].get(item[-3]):
904                            helptext += '\n\n'
905                            helptext += data['_Explain'][item[-3]]
906                    # typeString = 'NEWVAR'
907                    # if item[-3]:
908                    #     eqString[-1] += ' = '+item[-3]
909                    # else:
910                    #     eqString[-1] += ' = New Variable'
911                    if item[-3]:
912                        typeString = item[-3] + ' = '
913                    else:
914                        typeString = 'New Variable = '
915                    #print 'refine',item[-2]
916                    refineflag = True
917                elif item[-1] == 'c':
918                    helptext = "The following variables constrained to equal a constant:"
919                    for term in item[:-3]:
920                        var = str(term[1])
921                        if len(eqString[-1]) > maxlen:
922                            eqString.append(' ')
923                        if eqString[-1] != '':
924                            if term[0] > 0:
925                                eqString[-1] += ' + '
926                            else:
927                                eqString[-1] += ' - '
928                        eqString[-1] += '%.3f*%s '%(abs(term[0]),var)
929                        varMean = G2obj.fmtVarDescr(var)
930                        helptext += "\n" + var + " ("+ varMean + ")"
931                    typeString = 'CONST'
932                    eqString[-1] += ' = '+str(item[-3])
933                elif item[-1] == 'e':
934                    helptext = "The following variables are set to be equivalent, noting multipliers:"
935                    for term in item[:-3]:
936                        var = str(term[1])
937                        if term[0] == 0: term[0] = 1.0
938                        if len(eqString[-1]) > maxlen:
939                            eqString.append(' ')
940                        if eqString[-1] == '':
941                            eqString[-1] += var+' '
942                            first = term[0]
943                        else:
944                            eqString[-1] += ' = %.3f*%s '%(first/term[0],var)
945                        varMean = G2obj.fmtVarDescr(var)
946                        helptext += "\n" + var + " ("+ varMean + ")"
947                    typeString = 'EQUIV'
948                else:
949                    print 'Unexpected constraint',item
950               
951            else:
952                print 'Removing old-style constraints'
953                data[name] = []
954                return constSizer
955            constDel = wx.Button(pageDisplay,wx.ID_ANY,'Delete',style=wx.BU_EXACTFIT)
956            constDel.Bind(wx.EVT_BUTTON,OnConstDel)
957            Indx[constDel.GetId()] = [Id,name]
958            constSizer.Add(constDel,0,wx.LEFT|wx.RIGHT|wx.ALIGN_CENTER_VERTICAL|wx.ALIGN_CENTER,1)             # delete button
959            if helptext:
960                ch = G2G.HelpButton(pageDisplay,helptext)
961                constSizer.Add(ch,0,wx.LEFT|wx.RIGHT|wx.ALIGN_CENTER_VERTICAL|wx.ALIGN_CENTER,1)
962            else:
963                constSizer.Add((-1,-1))
964            if refineflag:
965                ch = G2G.G2CheckBox(pageDisplay,'',item,-2)
966                constSizer.Add(ch,0,wx.LEFT|wx.RIGHT|wx.ALIGN_CENTER_VERTICAL|wx.ALIGN_CENTER,1)
967            else:
968                constSizer.Add((-1,-1))               
969            constSizer.Add(wx.StaticText(pageDisplay,wx.ID_ANY,typeString),
970                           0,wx.ALIGN_CENTER_VERTICAL|wx.ALIGN_CENTER|wx.RIGHT|wx.LEFT,3)
971            if problemItem: eqString[-1] += ' -- Conflict: see console'
972            if len(eqString) > 1:
973                Eq = wx.BoxSizer(wx.VERTICAL)
974                for s in eqString:
975                    line = wx.StaticText(pageDisplay,wx.ID_ANY,s)
976                    if problemItem: line.SetBackgroundColour(wx.YELLOW)
977                    Eq.Add(line,0,wx.ALIGN_CENTER_VERTICAL)
978                Eq.Add((-1,4))
979            else:
980                Eq = wx.StaticText(pageDisplay,wx.ID_ANY,eqString[0])
981                if problemItem: Eq.SetBackgroundColour(wx.YELLOW)
982            constSizer.Add(Eq,1,wx.ALIGN_CENTER_VERTICAL)
983        return constSizer
984               
985    def OnConstDel(event):
986        'Delete a constraint'
987        Obj = event.GetEventObject()
988        Id,name = Indx[Obj.GetId()]
989        del(data[name][Id])
990        wx.CallAfter(OnPageChanged,None)
991       
992    def OnConstEdit(event):
993        '''Called to edit an individual contraint in response to a
994        click on its Edit button
995        '''
996        Obj = event.GetEventObject()
997        Id,name = Indx[Obj.GetId()]
998        if data[name][Id][-1] == 'f':
999            items = data[name][Id][:-3]
1000            constType = 'New Variable'
1001            if data[name][Id][-3]:
1002                varname = data[name][Id][-3]
1003            else:
1004                varname = ""
1005            lbl = 'Enter value for each term in constraint; sum = new variable'
1006            dlg = ConstraintDialog(G2frame,constType,lbl,items,
1007                                   varname=varname,varyflag=data[name][Id][-2])
1008        elif data[name][Id][-1] == 'c':
1009            items = data[name][Id][:-3]+[
1010                [data[name][Id][-3],'fixed value =']]
1011            constType = 'Constraint'
1012            lbl = 'Edit value for each term in constant constraint sum'
1013            dlg = ConstraintDialog(G2frame,constType,lbl,items)
1014        elif data[name][Id][-1] == 'e':
1015            items = data[name][Id][:-3]
1016            constType = 'Equivalence'
1017            lbl = 'The following terms are set to be equal:'
1018            dlg = ConstraintDialog(G2frame,constType,lbl,items,'/')
1019        else:
1020            return
1021        try:
1022            prev = data[name][Id][:]
1023            if dlg.ShowModal() == wx.ID_OK:
1024                result = dlg.GetData()
1025                for i in range(len(data[name][Id][:-3])):
1026                    if type(data[name][Id][i]) is tuple: # fix non-mutable construct
1027                        data[name][Id][i] = list(data[name][Id][i])
1028                    data[name][Id][i][0] = result[i][0]
1029                if data[name][Id][-1] == 'c':
1030                    data[name][Id][-3] = str(result[-1][0])
1031                elif data[name][Id][-1] == 'f':
1032                    # process the variable name to put in global form (::var)
1033                    varname = str(dlg.newvar[0]).strip().replace(' ','_')
1034                    if varname.startswith('::'):
1035                        varname = varname[2:]
1036                    varname = varname.replace(':',';')
1037                    if varname:
1038                        data[name][Id][-3] = varname
1039                    else:
1040                        data[name][Id][-3] = ''
1041                    data[name][Id][-2] = dlg.newvar[1]
1042                if not CheckChangedConstraint():
1043                    data[name][Id] = prev
1044        except:
1045            import traceback
1046            print traceback.format_exc()
1047        finally:
1048            dlg.Destroy()
1049        wx.CallAfter(OnPageChanged,None)
1050   
1051    def UpdateConstraintPanel(panel,typ):
1052        '''Update the contents of the selected Constraint
1053        notebook tab. Called in :func:`OnPageChanged`
1054        '''
1055        if panel.GetSizer(): panel.GetSizer().Clear(True)
1056        Siz = wx.BoxSizer(wx.VERTICAL)
1057        Siz.Add((5,5),0)
1058        Siz.Add(MakeConstraintsSizer(typ,panel),1,wx.EXPAND)
1059        panel.SetSizer(Siz,True)
1060        Size = Siz.GetMinSize()
1061        Size[0] += 40
1062        Size[1] = max(Size[1],450) + 20
1063        panel.SetSize(Size)
1064        panel.SetScrollbars(10,10,Size[0]/10-4,Size[1]/10-1)
1065        panel.Show()
1066
1067    def OnPageChanged(event):
1068        '''Called when a tab is pressed or when a "select tab" menu button is
1069        used (see RaisePage), or to refresh the current tab contents (event=None)
1070        '''
1071        if event:       #page change event!
1072            page = event.GetSelection()
1073        else: # called directly, get current page
1074            page = G2frame.constr.GetSelection()
1075        #G2frame.constr.SetSize(G2frame.dataWindow.GetClientSize())    #TODO -almost right
1076        G2frame.constr.ChangeSelection(page)
1077        text = G2frame.constr.GetPageText(page)
1078        G2frame.dataWindow.ConstraintEdit.Enable(G2G.wxID_EQUIVALANCEATOMS,False)
1079#        G2frame.dataWindow.ConstraintEdit.Enable(G2G.wxID_ADDRIDING,False)
1080        if text == 'Histogram/Phase':
1081            G2frame.Page = [page,'hap']
1082            UpdateConstraintPanel(HAPConstr,'HAP')
1083        elif text == 'Histogram':
1084            G2frame.Page = [page,'hst']
1085            UpdateConstraintPanel(HistConstr,'Hist')
1086        elif text == 'Phase':
1087            G2frame.Page = [page,'phs']
1088            G2frame.dataWindow.ConstraintEdit.Enable(G2G.wxID_EQUIVALANCEATOMS,True)
1089#            G2frame.dataWindow.ConstraintEdit.Enable(G2G.wxID_ADDRIDING,True)
1090            if 'DELETED' in str(PhaseConstr):   #seems to be no other way to do this (wx bug)
1091                if GSASIIpath.GetConfigValue('debug'):
1092                    print 'wx error: PhaseConstr not cleanly deleted after Refine'
1093                return
1094            UpdateConstraintPanel(PhaseConstr,'Phase')
1095        elif text == 'Global':
1096            G2frame.Page = [page,'glb']
1097            UpdateConstraintPanel(GlobalConstr,'Global')
1098        else:
1099            G2frame.Page = [page,'sym']
1100            UpdateConstraintPanel(SymConstr,'Sym-Generated')           
1101        G2frame.dataWindow.SetDataSize()
1102
1103    def RaisePage(event):
1104        'Respond to a "select tab" menu button'
1105        try:
1106            i = (G2G.wxID_CONSPHASE,
1107                 G2G.wxID_CONSHAP,
1108                 G2G.wxID_CONSHIST,
1109                 G2G.wxID_CONSGLOBAL,
1110                 G2G.wxID_CONSSYM,
1111                ).index(event.GetId())
1112            G2frame.constr.SetSelection(i)
1113            wx.CallAfter(OnPageChanged,None)
1114        except ValueError:
1115            print('Unexpected event in RaisePage')
1116
1117    def SetStatusLine(text):
1118        G2frame.GetStatusBar().SetStatusText(text,1)                                     
1119       
1120    G2gd.SetDataMenuBar(G2frame,G2frame.dataWindow.ConstraintMenu)
1121    #G2frame.SetLabel(G2frame.GetLabel().split('||')[0]+' || '+'Constraints')
1122    G2frame.SetTitle('Constraints')
1123    SetStatusLine('')
1124   
1125    G2frame.Bind(wx.EVT_MENU, OnAddConstraint, id=G2G.wxID_CONSTRAINTADD)
1126    G2frame.Bind(wx.EVT_MENU, OnAddFunction, id=G2G.wxID_FUNCTADD)
1127    G2frame.Bind(wx.EVT_MENU, OnAddEquivalence, id=G2G.wxID_EQUIVADD)
1128    G2frame.Bind(wx.EVT_MENU, OnAddHold, id=G2G.wxID_HOLDADD)
1129    G2frame.Bind(wx.EVT_MENU, OnAddAtomEquiv, id=G2G.wxID_EQUIVALANCEATOMS)
1130#    G2frame.Bind(wx.EVT_MENU, OnAddRiding, id=G2G.wxID_ADDRIDING)
1131    # tab commands
1132    for id in (G2G.wxID_CONSPHASE,
1133               G2G.wxID_CONSHAP,
1134               G2G.wxID_CONSHIST,
1135               G2G.wxID_CONSGLOBAL,
1136               G2G.wxID_CONSSYM,
1137               ):
1138        G2frame.Bind(wx.EVT_MENU, RaisePage,id=id)
1139
1140    #G2frame.constr = G2G.GSNoteBook(parent=G2frame.dataWindow,size=G2frame.dataWindow.GetClientSize())
1141    G2frame.constr = G2G.GSNoteBook(parent=G2frame.dataWindow)
1142    G2frame.dataWindow.GetSizer().Add(G2frame.constr,1,wx.ALL|wx.EXPAND)
1143    # note that order of pages is hard-coded in RaisePage
1144    PhaseConstr = wx.ScrolledWindow(G2frame.constr)
1145    G2frame.constr.AddPage(PhaseConstr,'Phase')
1146    HAPConstr = wx.ScrolledWindow(G2frame.constr)
1147    G2frame.constr.AddPage(HAPConstr,'Histogram/Phase')
1148    HistConstr = wx.ScrolledWindow(G2frame.constr)
1149    G2frame.constr.AddPage(HistConstr,'Histogram')
1150    GlobalConstr = wx.ScrolledWindow(G2frame.constr)
1151    G2frame.constr.AddPage(GlobalConstr,'Global')
1152    SymConstr = wx.ScrolledWindow(G2frame.constr)
1153    G2frame.constr.AddPage(SymConstr,'Sym-Generated')
1154    wx.CallAfter(OnPageChanged,None)
1155    G2frame.constr.Bind(wx.aui.EVT_AUINOTEBOOK_PAGE_CHANGED, OnPageChanged)
1156    # validate all the constrants -- should not see any errors here normally
1157    allcons = []
1158    for key in data:
1159        if key.startswith('_'): continue
1160        allcons += data[key]
1161    if not len(allcons): return
1162    errmsg,warnmsg = CheckConstraints(allcons)
1163    if errmsg:
1164        G2frame.ErrorDialog('Constraint Error',
1165                            'Error in constraints:\n'+errmsg+'\nCheck console output for more information',
1166                            parent=G2frame)
1167        print errmsg
1168        print G2mv.VarRemapShow([],True)
1169    elif warnmsg:
1170        print 'Unexpected contraint warning:\n',warnmsg
1171
1172################################################################################
1173#### Make nuclear-magnetic phase constraints - called by OnTransform in G2phsGUI
1174################################################################################       
1175       
1176def MagConstraints(G2frame,oldPhase,newPhase,Trans,Vec,atCodes):
1177    '''Add constraints for new magnetic phase created via transformation of old
1178    nuclear one
1179    NB: A = [G11,G22,G33,2*G12,2*G13,2*G23]
1180    '''
1181   
1182    def SetUniqAj(pId,iA,Aname,SGLaue):
1183        if SGLaue in ['4/m','4/mmm'] and iA in [0,1]:
1184            parm = '%d::%s'%(pId,'A0')
1185        elif SGLaue in ['m3','m3m'] and iA in [0,1,2]:
1186            parm = '%d::%s'%(pId,'A0')
1187        elif SGLaue in ['6/m','6/mmm','3m1', '31m', '3'] and iA in [0,1,3]:
1188            parm = '%d::%s'%(pId,'A0')
1189        elif SGLaue in ['3R', '3mR']:
1190            if ia in [0,1,2]:
1191                parm = '%d::%s'%(pId,'A0')
1192            else:
1193                parm = '%d::%s'%(pId,'A3')
1194        else:
1195            parm = '%d::%s'%(pId,Aname)
1196        return parm
1197       
1198    Histograms,Phases = G2frame.GetUsedHistogramsAndPhasesfromTree()
1199    UseList = newPhase['Histograms']
1200    detTrans = np.abs(nl.det(Trans))
1201    invTrans = nl.inv(Trans)
1202#    print 'invTrans',invTrans
1203    nAcof = G2lat.cell2A(newPhase['General']['Cell'][1:7])
1204   
1205    opId = oldPhase['pId']
1206    npId = newPhase['pId']
1207    cx,ct,cs,cia = newPhase['General']['AtomPtrs']
1208    nAtoms = newPhase['Atoms']
1209    oSGData = oldPhase['General']['SGData']
1210    nSGData = newPhase['General']['SGData']
1211    oAcof = G2lat.cell2A(oldPhase['General']['Cell'][1:7])
1212    nAcof = G2lat.cell2A(newPhase['General']['Cell'][1:7])
1213    item = G2gd.GetGPXtreeItemId(G2frame,G2frame.root,'Constraints') 
1214    constraints = G2frame.GPXtree.GetItemPyData(item)
1215#    GSASIIpath.IPyBreak()
1216    parmDict = {}
1217    varyList = []
1218    for ia,code in enumerate(atCodes):
1219        atom = nAtoms[ia]
1220        siteSym = G2spc.SytSym(atom[cx:cx+3],nSGData)[0]
1221        CSX = G2spc.GetCSxinel(siteSym)
1222        item = code.split('+')[0]
1223        iat,opr = item.split(':')
1224        Nop = abs(int(opr))%100-1
1225        if '-' in opr:
1226            Nop *= -1
1227        Opr = oldPhase['General']['SGData']['SGOps'][abs(Nop)][0]
1228        if Nop < 0:         #inversion
1229            Opr *= -1
1230        XOpr = np.inner(Opr,Trans.T)
1231        names = ['dAx','dAy','dAz']
1232        for ix,name in enumerate(names):
1233            IndpCon = [1.0,G2obj.G2VarObj('%d::%s:%s'%(opId,name,iat))]
1234            DepCons = []
1235            for iop,opval in enumerate(XOpr[ix]):
1236                if opval and CSX[0][ix]:    #-opval from defn of dAx, etc.
1237                    DepCons.append([-opval,G2obj.G2VarObj('%d::%s:%d'%(npId,names[iop],ia))])
1238            if len(DepCons) == 1:
1239                constraints['Phase'].append([IndpCon,DepCons[0],None,None,'e'])
1240            elif len(DepCons) > 1:
1241                for Dep in DepCons:
1242                    Dep[0] *= -1
1243                constraints['Phase'].append([IndpCon]+DepCons+[0.0,None,'c'])
1244        for name in ['Afrac','AUiso']:
1245            IndpCon = [1.0,G2obj.G2VarObj('%d::%s:%s'%(opId,name,iat))]
1246            DepCons = [1.0,G2obj.G2VarObj('%d::%s:%d'%(npId,name,ia))]
1247            constraints['Phase'].append([IndpCon,DepCons,None,None,'e'])
1248        #how do I do Uij's for most Trans?
1249    Anames = [['A0','A3','A4'],['A3','A1','A5'],['A4','A5','A2']]
1250    As = ['A0','A1','A2','A3','A4','A5']
1251    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]]
1252    Axes = ['a','b','c']
1253    Holds = []
1254    for iA,Aid in enumerate(Aids):
1255        parm = SetUniqAj(opId,iA,Aid[2],oSGData['SGLaue'])
1256        parmDict[parm] = oAcof[iA]
1257        varyList.append(parm)
1258        IndpCon = [1.0,G2obj.G2VarObj(parm)]
1259        DepCons = []
1260        for iat in range(3):
1261            if nSGData['SGLaue'] in ['-1','2/m']:       #set holds
1262                if (abs(nAcof[iA]) < 1.e-8) and (abs(Trans[Aid[0],Aid[1]]) < 1.e-8):
1263                    if Axes[iat] != oSGData['SGUniq'] and oSGData['SGLaue'] != nSGData['SGLaue']:
1264                        HoldObj = G2obj.G2VarObj('%d::%s'%(npId,Aid[2]))
1265                        if not HoldObj in Holds: 
1266                            constraints['Phase'].append([[0.0,HoldObj],None,None,'h'])
1267                            Holds.append(HoldObj)
1268                            continue
1269#            print iA,Aid,iat,invTrans[iat][Aid[0]],invTrans[Aid[1]][iat],Anames[Aid[0]][Aid[1]],parm
1270            if abs(invTrans[iat,Aid[1]]) > 1.e-8 and abs(nAcof[iA]) > 1.e-8:
1271                parm = SetUniqAj(npId,iA,Anames[Aid[0]][Aid[1]],nSGData['SGLaue'])
1272                parmDict[parm] = nAcof[As.index(Aid[2])]
1273                if not parm in varyList:
1274                    varyList.append(parm)
1275                DepCons.append([Trans[Aid[0],Aid[0]]*Trans[Aid[1],Aid[1]],G2obj.G2VarObj(parm)])
1276        if len(DepCons) == 1:
1277            constraints['Phase'].append([IndpCon,DepCons[0],None,None,'e'])
1278        elif len(DepCons) > 1:       
1279            for Dep in DepCons:
1280                Dep[0] *= -1
1281            constraints['Phase'].append([IndpCon]+DepCons+[0.0,None,'c'])
1282#    constDict,fixedList,ignored = G2stIO.ProcessConstraints(constraints['Phase'])
1283#    groups,parmlist = G2mv.GroupConstraints(constDict)
1284#    G2mv.GenerateConstraints(groups,parmlist,varyList,constDict,fixedList,parmDict)
1285#    print 'old',parmDict
1286#    G2mv.Dict2Map(parmDict,varyList)
1287#    print 'new',parmDict
1288    for hId,hist in enumerate(UseList):    #HAP - seems OK
1289        ohapkey = '%d:%d:'%(opId,hId)
1290        nhapkey = '%d:%d:'%(npId,hId)
1291        IndpCon = [1.0,G2obj.G2VarObj(ohapkey+'Scale')]
1292        DepCons = [detTrans,G2obj.G2VarObj(nhapkey+'Scale')]
1293        constraints['HAP'].append([IndpCon,DepCons,None,None,'e'])
1294        for name in ['Size;i','Mustrain;i']:
1295            IndpCon = [1.0,G2obj.G2VarObj(ohapkey+name)]
1296            DepCons = [1.0,G2obj.G2VarObj(nhapkey+name)]
1297            constraints['HAP'].append([IndpCon,DepCons,None,None,'e'])
1298       
1299################################################################################
1300#### Rigid bodies
1301################################################################################
1302
1303def UpdateRigidBodies(G2frame,data):
1304    '''Called when Rigid bodies tree item is selected.
1305    Displays the rigid bodies in the data window
1306    '''
1307    if not data.get('RBIds') or not data:
1308        data.update({'Vector':{'AtInfo':{}},'Residue':{'AtInfo':{}},
1309            'RBIds':{'Vector':[],'Residue':[]}})       #empty/bad dict - fill it
1310           
1311    global resList
1312    Indx = {}
1313    resList = []
1314    plotDefaults = {'oldxy':[0.,0.],'Quaternion':[0.,0.,0.,1.],'cameraPos':30.,'viewDir':[0,0,1],}
1315
1316    G2frame.rbBook = G2G.GSNoteBook(parent=G2frame.dataWindow)
1317    G2frame.dataWindow.GetSizer().Add(G2frame.rbBook,1,wx.ALL|wx.EXPAND)
1318    VectorRB = wx.ScrolledWindow(G2frame.rbBook)
1319    VectorRBDisplay = wx.Panel(VectorRB)
1320    G2frame.rbBook.AddPage(VectorRB,'Vector rigid bodies')
1321    ResidueRB = wx.ScrolledWindow(G2frame.rbBook)
1322    ResidueRBDisplay = wx.Panel(ResidueRB)
1323    G2frame.rbBook.AddPage(ResidueRB,'Residue rigid bodies')
1324   
1325    def OnPageChanged(event):
1326        global resList
1327        resList = []
1328        if event:       #page change event!
1329            page = event.GetSelection()
1330        else:
1331            page = G2frame.rbBook.GetSelection()
1332        #G2frame.rbBook.SetSize(G2frame.dataWindow.GetClientSize())    #TODO -almost right
1333        G2frame.rbBook.ChangeSelection(page)
1334        text = G2frame.rbBook.GetPageText(page)
1335        if text == 'Vector rigid bodies':
1336            G2gd.SetDataMenuBar(G2frame,G2frame.dataWindow.VectorBodyMenu)
1337            G2frame.Bind(wx.EVT_MENU, AddVectorRB, id=G2G.wxID_VECTORBODYADD)
1338            G2frame.Page = [page,'vrb']
1339            UpdateVectorRB()
1340        elif text == 'Residue rigid bodies':
1341            G2gd.SetDataMenuBar(G2frame,G2frame.dataWindow.RigidBodyMenu)
1342            G2frame.Bind(wx.EVT_MENU, AddResidueRB, id=G2G.wxID_RIGIDBODYADD)
1343            G2frame.Bind(wx.EVT_MENU, OnImportRigidBody, id=G2G.wxID_RIGIDBODYIMPORT)
1344            G2frame.Bind(wx.EVT_MENU, OnDefineTorsSeq, id=G2G.wxID_RESIDUETORSSEQ) #enable only if residue RBs exist?
1345            G2frame.Page = [page,'rrb']
1346            UpdateResidueRB()
1347           
1348    def getMacroFile(macName):
1349        defDir = os.path.join(os.path.split(__file__)[0],'GSASIImacros')
1350        dlg = wx.FileDialog(G2frame,message='Choose '+macName+' rigid body macro file',
1351            defaultDir=defDir,defaultFile="",wildcard="GSAS-II macro file (*.mac)|*.mac",
1352            style=wx.OPEN | wx.CHANGE_DIR)
1353        try:
1354            if dlg.ShowModal() == wx.ID_OK:
1355                macfile = dlg.GetPath()
1356                macro = open(macfile,'Ur')
1357                head = macro.readline()
1358                if macName not in head:
1359                    print head
1360                    print '**** ERROR - wrong restraint macro file selected, try again ****'
1361                    macro = []
1362            else: # cancel was pressed
1363                macro = []
1364        finally:
1365            dlg.Destroy()
1366        return macro        #advanced past 1st line
1367       
1368    def getTextFile():
1369        dlg = wx.FileDialog(G2frame,'Choose rigid body text file', '.', '',
1370            "GSAS-II text file (*.txt)|*.txt|XYZ file (*.xyz)|*.xyz|"
1371            "Sybyl mol2 file (*.mol2)|*.mol2|PDB file (*.pdb;*.ent)|*.pdb;*.ent",
1372            wx.OPEN | wx.CHANGE_DIR)
1373        try:
1374            if dlg.ShowModal() == wx.ID_OK:
1375                txtfile = dlg.GetPath()
1376                ext = os.path.splitext(txtfile)[1]
1377                text = open(txtfile,'Ur')
1378            else: # cancel was pressed
1379                ext = ''
1380                text = []
1381        finally:
1382            dlg.Destroy()
1383        if 'ent' in ext:
1384            ext = '.pdb'
1385        return text,ext.lower()
1386       
1387    def OnImportRigidBody(event):
1388        page = G2frame.rbBook.GetSelection()
1389        if 'Vector' in G2frame.rbBook.GetPageText(page):
1390            pass
1391        elif 'Residue' in G2frame.rbBook.GetPageText(page):
1392            ImportResidueRB()
1393           
1394    def AddVectorRB(event):
1395        AtInfo = data['Vector']['AtInfo']
1396        dlg = G2G.MultiIntegerDialog(G2frame,'New Rigid Body',['No. atoms','No. translations'],[1,1])
1397        if dlg.ShowModal() == wx.ID_OK:
1398            nAtoms,nTrans = dlg.GetValues()
1399            rbId = ran.randint(0,sys.maxint)
1400            vecMag = [1.0 for i in range(nTrans)]
1401            vecRef = [False for i in range(nTrans)]
1402            vecVal = [np.zeros((nAtoms,3)) for j in range(nTrans)]
1403            rbTypes = ['C' for i in range(nAtoms)]
1404            Info = G2elem.GetAtomInfo('C')
1405            AtInfo['C'] = [Info['Drad'],Info['Color']]
1406            data['Vector'][rbId] = {'RBname':'UNKRB','VectMag':vecMag,'rbXYZ':np.zeros((nAtoms,3)),
1407                'rbRef':[0,1,2,False],'VectRef':vecRef,'rbTypes':rbTypes,'rbVect':vecVal,'useCount':0}
1408            data['RBIds']['Vector'].append(rbId)
1409        dlg.Destroy()
1410        UpdateVectorRB()
1411       
1412    def AddResidueRB(event):
1413        AtInfo = data['Residue']['AtInfo']
1414        macro = getMacroFile('rigid body')
1415        if not macro:
1416            return
1417        macStr = macro.readline()
1418        while macStr:
1419            items = macStr.split()
1420            if 'I' == items[0]:
1421                rbId = ran.randint(0,sys.maxint)
1422                rbName = items[1]
1423                rbTypes = []
1424                rbXYZ = []
1425                rbSeq = []
1426                atNames = []
1427                nAtms,nSeq,nOrig,mRef,nRef = [int(items[i]) for i in [2,3,4,5,6]]
1428                for iAtm in range(nAtms):
1429                    macStr = macro.readline().split()
1430                    atName = macStr[0]
1431                    atType = macStr[1]
1432                    atNames.append(atName)
1433                    rbXYZ.append([float(macStr[i]) for i in [2,3,4]])
1434                    rbTypes.append(atType)
1435                    if atType not in AtInfo:
1436                        Info = G2elem.GetAtomInfo(atType)
1437                        AtInfo[atType] = [Info['Drad'],Info['Color']]
1438                rbXYZ = np.array(rbXYZ)-np.array(rbXYZ[nOrig-1])
1439                for iSeq in range(nSeq):
1440                    macStr = macro.readline().split()
1441                    mSeq = int(macStr[0])
1442                    for jSeq in range(mSeq):
1443                        macStr = macro.readline().split()
1444                        iBeg = int(macStr[0])-1
1445                        iFin = int(macStr[1])-1
1446                        angle = 0.0
1447                        nMove = int(macStr[2])
1448                        iMove = [int(macStr[i])-1 for i in range(3,nMove+3)]
1449                        rbSeq.append([iBeg,iFin,angle,iMove])
1450                data['Residue'][rbId] = {'RBname':rbName,'rbXYZ':rbXYZ,'rbTypes':rbTypes,
1451                    'atNames':atNames,'rbRef':[nOrig-1,mRef-1,nRef-1,True],'rbSeq':rbSeq,
1452                    'SelSeq':[0,0],'useCount':0}
1453                data['RBIds']['Residue'].append(rbId)
1454                print 'Rigid body '+rbName+' added'
1455            macStr = macro.readline()
1456        macro.close()
1457        UpdateResidueRB()
1458       
1459    def ImportResidueRB():
1460        AtInfo = data['Residue']['AtInfo']
1461        text,ext = getTextFile()
1462        if not text:
1463            return
1464        rbId = ran.randint(0,sys.maxint)
1465        rbTypes = []
1466        rbXYZ = []
1467        atNames = []
1468        txtStr = text.readline()
1469        if 'xyz' in ext:
1470            txtStr = text.readline()
1471            txtStr = text.readline()
1472        elif 'mol2' in ext:
1473            while 'ATOM' not in txtStr:
1474                txtStr = text.readline()
1475            txtStr = text.readline()
1476        elif 'pdb' in ext:
1477            while 'ATOM' not in txtStr[:6] and 'HETATM' not in txtStr[:6]:
1478                txtStr = text.readline()
1479                #print txtStr
1480        items = txtStr.split()
1481        while len(items):
1482            if 'txt' in ext:
1483                atName = items[0]
1484                atType = items[1]
1485                rbXYZ.append([float(items[i]) for i in [2,3,4]])
1486            elif 'xyz' in ext:
1487                atType = items[0]
1488                rbXYZ.append([float(items[i]) for i in [1,2,3]])
1489                atName = atType+str(len(rbXYZ))
1490            elif 'mol2' in ext:
1491                atType = items[1]
1492                atName = items[1]+items[0]
1493                rbXYZ.append([float(items[i]) for i in [2,3,4]])
1494            elif 'pdb' in ext:
1495                atType = items[-1]
1496                atName = items[2]
1497                xyz = txtStr[30:55].split()                   
1498                rbXYZ.append([float(x) for x in xyz])
1499            atNames.append(atName)
1500            rbTypes.append(atType)
1501            if atType not in AtInfo:
1502                Info = G2elem.GetAtomInfo(atType)
1503                AtInfo[atType] = [Info['Drad'],Info['Color']]
1504            txtStr = text.readline()
1505            if 'mol2' in ext and 'BOND' in txtStr:
1506                break
1507            if 'pdb' in ext and ('ATOM' not in txtStr[:6] and 'HETATM' not in txtStr[:6]):
1508                break
1509            items = txtStr.split()
1510        if len(atNames) < 3:
1511            G2G.G2MessageBox(G2frame,'Not enough atoms in rigid body; must be 3 or more')
1512        else:
1513            rbXYZ = np.array(rbXYZ)-np.array(rbXYZ[0])
1514            data['Residue'][rbId] = {'RBname':'UNKRB','rbXYZ':rbXYZ,'rbTypes':rbTypes,
1515                'atNames':atNames,'rbRef':[0,1,2,False],'rbSeq':[],'SelSeq':[0,0],'useCount':0}
1516            data['RBIds']['Residue'].append(rbId)
1517            print 'Rigid body UNKRB added'
1518        text.close()
1519        UpdateResidueRB()
1520       
1521    def FindNeighbors(Orig,XYZ,atTypes,atNames,AtInfo):
1522        Radii = []
1523        for Atype in atTypes:
1524            Radii.append(AtInfo[Atype][0])
1525        Radii = np.array(Radii)
1526        Neigh = []
1527        Dx = XYZ-XYZ[Orig]
1528        dist = np.sqrt(np.sum(Dx**2,axis=1))
1529        sumR = Radii[Orig]+Radii
1530        IndB = ma.nonzero(ma.masked_greater(dist-0.85*sumR,0.))
1531        for j in IndB[0]:
1532            if j != Orig and atTypes[j] != 'H':
1533                Neigh.append(atNames[j])
1534        return Neigh
1535       
1536    def FindAllNeighbors(XYZ,atTypes,atNames,AtInfo):
1537        NeighDict = {}
1538        for iat,xyz in enumerate(atNames):
1539            NeighDict[atNames[iat]] = FindNeighbors(iat,XYZ,atTypes,atNames,AtInfo)
1540        return NeighDict
1541       
1542    def FindRiding(Orig,Pivot,NeighDict):
1543        riding = [Orig,Pivot]
1544        iAdd = 1
1545        new = True
1546        while new:
1547            newAtms = NeighDict[riding[iAdd]]
1548            for At in newAtms:
1549                new = False
1550                if At not in riding:
1551                    riding.append(At)
1552                    new = True
1553            iAdd += 1
1554            if iAdd < len(riding):
1555                new = True
1556        return riding[2:]
1557                       
1558    def OnDefineTorsSeq(event):
1559        rbKeys = data['Residue'].keys()
1560        rbKeys.remove('AtInfo')
1561        rbNames = [data['Residue'][k]['RBname'] for k in rbKeys]
1562        rbIds = dict(zip(rbNames,rbKeys))
1563        rbNames.sort()
1564        rbId = 0
1565        if len(rbNames) == 0:
1566            print 'There are no rigid bodies defined'
1567            G2frame.ErrorDialog('No rigid bodies','There are no rigid bodies defined',
1568                                parent=G2frame)
1569            return
1570        elif len(rbNames) > 1:
1571            dlg = wx.SingleChoiceDialog(G2frame,'Select rigid body for torsion sequence','Torsion sequence',rbNames)
1572            if dlg.ShowModal() == wx.ID_OK:
1573                sel = dlg.GetSelection()
1574                rbId = rbIds[rbNames[sel]]
1575                rbData = data['Residue'][rbId]
1576            dlg.Destroy()
1577        else:
1578            rbId = rbIds[rbNames[0]]
1579            rbData = data['Residue'][rbId]
1580        if not len(rbData):
1581            return
1582        atNames = rbData['atNames']
1583        AtInfo = data['Residue']['AtInfo']
1584        atTypes = rbData['rbTypes']
1585        XYZ = rbData['rbXYZ']
1586        neighDict = FindAllNeighbors(XYZ,atTypes,atNames,AtInfo)
1587        TargList = []           
1588        dlg = wx.SingleChoiceDialog(G2frame,'Select origin atom for torsion sequence','Origin atom',rbData['atNames'])
1589        if dlg.ShowModal() == wx.ID_OK:
1590            Orig = dlg.GetSelection()
1591            TargList = neighDict[atNames[Orig]]
1592        dlg.Destroy()
1593        if not len(TargList):
1594            return
1595        dlg = wx.SingleChoiceDialog(G2frame,'Select pivot atom for torsion sequence','Pivot atom',TargList)
1596        if dlg.ShowModal() == wx.ID_OK:
1597            Piv = atNames.index(TargList[dlg.GetSelection()])
1598            riding = FindRiding(atNames[Orig],atNames[Piv],neighDict)
1599            Riding = []
1600            for atm in riding:
1601                Riding.append(atNames.index(atm))
1602            rbData['rbSeq'].append([Orig,Piv,0.0,Riding])           
1603        dlg.Destroy()
1604        UpdateResidueRB()
1605
1606    def UpdateVectorRB(Scroll=0):
1607        AtInfo = data['Vector']['AtInfo']
1608        refChoice = {}
1609        if 'DELETED' in str(G2frame.GetStatusBar()):   #seems to be no other way to do this (wx bug)
1610            if GSASIIpath.GetConfigValue('debug'):
1611                print 'wx error: Rigid Body/Status not cleanly deleted after Refine'
1612            return
1613        SetStatusLine(' You may use e.g. "c60" or "s60" for a vector entry')
1614        def rbNameSizer(rbId,rbData):
1615
1616            def OnRBName(event):
1617                event.Skip()
1618                Obj = event.GetEventObject()
1619                rbData['RBname'] = Obj.GetValue()
1620               
1621            def OnDelRB(event):
1622                Obj = event.GetEventObject()
1623                rbId = Indx[Obj.GetId()]
1624                if rbId in data['Vector']:
1625                    del data['Vector'][rbId]
1626                    data['RBIds']['Vector'].remove(rbId)
1627                    rbData['useCount'] -= 1
1628                wx.CallAfter(UpdateVectorRB)
1629               
1630            def OnPlotRB(event):
1631                Obj = event.GetEventObject()
1632                Obj.SetValue(False)
1633                G2plt.PlotRigidBody(G2frame,'Vector',AtInfo,rbData,plotDefaults)
1634           
1635            nameSizer = wx.BoxSizer(wx.HORIZONTAL)
1636            nameSizer.Add(wx.StaticText(VectorRBDisplay,-1,'Rigid body name: '),
1637                0,wx.ALIGN_CENTER_VERTICAL)
1638            RBname = wx.TextCtrl(VectorRBDisplay,-1,rbData['RBname'])
1639            Indx[RBname.GetId()] = rbId
1640            RBname.Bind(wx.EVT_TEXT_ENTER,OnRBName)
1641            RBname.Bind(wx.EVT_KILL_FOCUS,OnRBName)
1642            nameSizer.Add(RBname,0,wx.ALIGN_CENTER_VERTICAL)
1643            nameSizer.Add((5,0),)
1644            plotRB = wx.CheckBox(VectorRBDisplay,-1,'Plot?')
1645            Indx[plotRB.GetId()] = rbId
1646            plotRB.Bind(wx.EVT_CHECKBOX,OnPlotRB)
1647            nameSizer.Add(plotRB,0,wx.ALIGN_CENTER_VERTICAL)
1648            nameSizer.Add((5,0),)
1649            if not rbData['useCount']:
1650                delRB = wx.CheckBox(VectorRBDisplay,-1,'Delete?')
1651                Indx[delRB.GetId()] = rbId
1652                delRB.Bind(wx.EVT_CHECKBOX,OnDelRB)
1653                nameSizer.Add(delRB,0,wx.ALIGN_CENTER_VERTICAL)
1654            return nameSizer
1655           
1656        def rbRefAtmSizer(rbId,rbData):
1657           
1658            def OnRefSel(event):
1659                Obj = event.GetEventObject()
1660                iref = Indx[Obj.GetId()]
1661                sel = Obj.GetValue()
1662                rbData['rbRef'][iref] = atNames.index(sel)
1663                FillRefChoice(rbId,rbData)
1664           
1665            refAtmSizer = wx.BoxSizer(wx.HORIZONTAL)
1666            atNames = [name+str(i) for i,name in enumerate(rbData['rbTypes'])]
1667            rbRef = rbData.get('rbRef',[0,1,2,False])
1668            rbData['rbRef'] = rbRef
1669            if rbData['useCount']:
1670                refAtmSizer.Add(wx.StaticText(VectorRBDisplay,-1,
1671                    'Orientation reference atoms A-B-C: %s, %s, %s'%(atNames[rbRef[0]], \
1672                     atNames[rbRef[1]],atNames[rbRef[2]])),0)
1673            else:
1674                refAtmSizer.Add(wx.StaticText(VectorRBDisplay,-1,
1675                    'Orientation reference atoms A-B-C: '),0,wx.ALIGN_CENTER_VERTICAL)
1676                for i in range(3):
1677                    choices = [atNames[j] for j in refChoice[rbId][i]]
1678                    refSel = wx.ComboBox(VectorRBDisplay,-1,value='',
1679                        choices=choices,style=wx.CB_READONLY|wx.CB_DROPDOWN)
1680                    refSel.SetValue(atNames[rbRef[i]])
1681                    refSel.Bind(wx.EVT_COMBOBOX, OnRefSel)
1682                    Indx[refSel.GetId()] = i
1683                    refAtmSizer.Add(refSel,0,wx.ALIGN_CENTER_VERTICAL)
1684            return refAtmSizer
1685                       
1686        def rbVectMag(rbId,imag,rbData):
1687           
1688            def OnRBVectorMag(event):
1689                event.Skip()
1690                Obj = event.GetEventObject()
1691                rbId,imag = Indx[Obj.GetId()]
1692                try:
1693                    val = float(Obj.GetValue())
1694                    if val <= 0.:
1695                        raise ValueError
1696                    rbData['VectMag'][imag] = val
1697                except ValueError:
1698                    pass
1699                Obj.SetValue('%8.4f'%(val))
1700                wx.CallAfter(UpdateVectorRB,VectorRB.GetScrollPos(wx.VERTICAL))
1701                G2plt.PlotRigidBody(G2frame,'Vector',AtInfo,data['Vector'][rbId],plotDefaults)
1702               
1703            def OnRBVectorRef(event):
1704                Obj = event.GetEventObject()
1705                rbId,imag = Indx[Obj.GetId()]
1706                rbData['VectRef'][imag] = Obj.GetValue()
1707                       
1708            magSizer = wx.wx.BoxSizer(wx.HORIZONTAL)
1709            magSizer.Add(wx.StaticText(VectorRBDisplay,-1,'Translation magnitude: '),
1710                0,wx.ALIGN_CENTER_VERTICAL)
1711            magValue = wx.TextCtrl(VectorRBDisplay,-1,'%8.4f'%(rbData['VectMag'][imag]))
1712            Indx[magValue.GetId()] = [rbId,imag]
1713            magValue.Bind(wx.EVT_TEXT_ENTER,OnRBVectorMag)
1714            magValue.Bind(wx.EVT_KILL_FOCUS,OnRBVectorMag)
1715            magSizer.Add(magValue,0,wx.ALIGN_CENTER_VERTICAL)
1716            magSizer.Add((5,0),)
1717            magref = wx.CheckBox(VectorRBDisplay,-1,label=' Refine?') 
1718            magref.SetValue(rbData['VectRef'][imag])
1719            magref.Bind(wx.EVT_CHECKBOX,OnRBVectorRef)
1720            Indx[magref.GetId()] = [rbId,imag]
1721            magSizer.Add(magref,0,wx.ALIGN_CENTER_VERTICAL)
1722            return magSizer
1723           
1724        def rbVectors(rbId,imag,mag,XYZ,rbData):
1725
1726            def TypeSelect(event):
1727                AtInfo = data['Vector']['AtInfo']
1728                r,c = event.GetRow(),event.GetCol()
1729                if vecGrid.GetColLabelValue(c) == 'Type':
1730                    PE = G2elemGUI.PickElement(G2frame,oneOnly=True)
1731                    if PE.ShowModal() == wx.ID_OK:
1732                        if PE.Elem != 'None':
1733                            El = PE.Elem.strip().lower().capitalize()
1734                            if El not in AtInfo:
1735                                Info = G2elem.GetAtomInfo(El)
1736                                AtInfo[El] = [Info['Drad'],Info['Color']]
1737                            rbData['rbTypes'][r] = El
1738                            vecGrid.SetCellValue(r,c,El)
1739                    PE.Destroy()
1740                wx.CallAfter(UpdateVectorRB,VectorRB.GetScrollPos(wx.VERTICAL))
1741
1742            def ChangeCell(event):
1743                r,c =  event.GetRow(),event.GetCol()
1744                if r >= 0 and (0 <= c < 3):
1745                    try:
1746                        val = float(vecGrid.GetCellValue(r,c))
1747                        rbData['rbVect'][imag][r][c] = val
1748                    except ValueError:
1749                        pass
1750                G2plt.PlotRigidBody(G2frame,'Vector',AtInfo,data['Vector'][rbId],plotDefaults)
1751                wx.CallAfter(UpdateVectorRB,VectorRB.GetScrollPos(wx.VERTICAL))
1752
1753            vecSizer = wx.BoxSizer()
1754            Types = 3*[wg.GRID_VALUE_FLOAT+':10,5',]+[wg.GRID_VALUE_STRING,]+3*[wg.GRID_VALUE_FLOAT+':10,5',]
1755            colLabels = ['Vector x','Vector y','Vector z','Type','Cart x','Cart y','Cart z']
1756            table = []
1757            rowLabels = []
1758            for ivec,xyz in enumerate(rbData['rbVect'][imag]):
1759                table.append(list(xyz)+[rbData['rbTypes'][ivec],]+list(XYZ[ivec]))
1760                rowLabels.append(str(ivec))
1761            vecTable = G2G.Table(table,rowLabels=rowLabels,colLabels=colLabels,types=Types)
1762            vecGrid = G2G.GSGrid(VectorRBDisplay)
1763            vecGrid.SetTable(vecTable, True)
1764            vecGrid.Bind(wg.EVT_GRID_CELL_CHANGE, ChangeCell)
1765            if not imag:
1766                vecGrid.Bind(wg.EVT_GRID_CELL_LEFT_DCLICK, TypeSelect)
1767            attr = wx.grid.GridCellAttr()
1768            attr.SetEditor(G2G.GridFractionEditor(vecGrid))
1769            for c in range(3):
1770                vecGrid.SetColAttr(c, attr)
1771            for row in range(vecTable.GetNumberRows()):
1772                if imag:
1773                    vecGrid.SetCellStyle(row,3,VERY_LIGHT_GREY,True)                   
1774                for col in [4,5,6]:
1775                    vecGrid.SetCellStyle(row,col,VERY_LIGHT_GREY,True)
1776#            vecGrid.SetScrollRate(0,0)
1777            vecGrid.AutoSizeColumns(False)
1778            vecSizer.Add(vecGrid)
1779            return vecSizer
1780       
1781        def FillRefChoice(rbId,rbData):
1782            choiceIds = [i for i in range(len(rbData['rbTypes']))]
1783           
1784            rbRef = rbData.get('rbRef',[-1,-1,-1,False])
1785            for i in range(3):
1786                choiceIds.remove(rbRef[i])
1787            refChoice[rbId] = [choiceIds[:],choiceIds[:],choiceIds[:]]
1788            for i in range(3):
1789                refChoice[rbId][i].append(rbRef[i])
1790                refChoice[rbId][i].sort()     
1791           
1792        if VectorRB.GetSizer(): VectorRB.GetSizer().Clear(True)
1793        VectorRBSizer = wx.BoxSizer(wx.VERTICAL)
1794        for rbId in data['RBIds']['Vector']:
1795            if rbId != 'AtInfo':
1796                rbData = data['Vector'][rbId]
1797                FillRefChoice(rbId,rbData)
1798                VectorRBSizer.Add(rbNameSizer(rbId,rbData),0)
1799                VectorRBSizer.Add(rbRefAtmSizer(rbId,rbData),0)
1800                XYZ = np.array([[0.,0.,0.] for Ty in rbData['rbTypes']])
1801                for imag,mag in enumerate(rbData['VectMag']):
1802                    XYZ += mag*rbData['rbVect'][imag]
1803                    VectorRBSizer.Add(rbVectMag(rbId,imag,rbData),0)
1804                    VectorRBSizer.Add(rbVectors(rbId,imag,mag,XYZ,rbData),0)
1805                VectorRBSizer.Add((5,5),0)
1806                data['Vector'][rbId]['rbXYZ'] = XYZ       
1807        VectorRBSizer.Layout()   
1808        VectorRBDisplay.SetSizer(VectorRBSizer,True)
1809        Size = VectorRBSizer.GetMinSize()
1810        Size[0] += 40
1811        Size[1] = max(Size[1],450) + 20
1812        VectorRBDisplay.SetSize(Size)
1813        VectorRB.SetScrollbars(10,10,Size[0]/10-4,Size[1]/10-1)
1814        VectorRB.Scroll(0,Scroll)
1815       
1816    def UpdateResidueRB():
1817        AtInfo = data['Residue']['AtInfo']
1818        refChoice = {}
1819        RefObjs = []
1820
1821        def rbNameSizer(rbId,rbData):
1822
1823            def OnRBName(event):
1824                Obj = event.GetEventObject()
1825                rbData['RBname'] = Obj.GetValue()
1826               
1827            def OnDelRB(event):
1828                Obj = event.GetEventObject()
1829                rbId = Indx[Obj.GetId()]
1830                if rbId in data['Residue']: 
1831                    del data['Residue'][rbId]
1832                    data['RBIds']['Residue'].remove(rbId)
1833                wx.CallAfter(UpdateResidueRB)
1834               
1835            def OnStripH(event):
1836                Obj = event.GetEventObject()
1837                rbId = Indx[Obj.GetId()]
1838                if rbId in data['Residue']:
1839                    newNames = []
1840                    newTypes = []
1841                    newXYZ = []
1842                    for i,atype in enumerate(rbData['rbTypes']):
1843                        if atype != 'H':
1844                            newNames.append(rbData['atNames'][i])
1845                            newTypes.append(rbData['rbTypes'][i])
1846                            newXYZ.append(rbData['rbXYZ'][i])
1847                    rbData['atNames'] = newNames
1848                    rbData['rbTypes'] = newTypes
1849                    rbData['rbXYZ'] = newXYZ
1850                wx.CallAfter(UpdateResidueRB)
1851                   
1852            def OnPlotRB(event):
1853                Obj = event.GetEventObject()
1854                Obj.SetValue(False)
1855                G2plt.PlotRigidBody(G2frame,'Residue',AtInfo,rbData,plotDefaults)
1856           
1857            nameSizer = wx.BoxSizer(wx.HORIZONTAL)
1858            nameSizer.Add(wx.StaticText(ResidueRBDisplay,-1,'Residue name: '),
1859                0,wx.ALIGN_CENTER_VERTICAL)
1860            RBname = wx.TextCtrl(ResidueRBDisplay,-1,rbData['RBname'])
1861            Indx[RBname.GetId()] = rbId
1862            RBname.Bind(wx.EVT_TEXT_ENTER,OnRBName)
1863            RBname.Bind(wx.EVT_KILL_FOCUS,OnRBName)
1864            nameSizer.Add(RBname,0,wx.ALIGN_CENTER_VERTICAL)
1865            nameSizer.Add((5,0),)
1866            plotRB = wx.CheckBox(ResidueRBDisplay,-1,'Plot?')
1867            Indx[plotRB.GetId()] = rbId
1868            plotRB.Bind(wx.EVT_CHECKBOX,OnPlotRB)
1869            nameSizer.Add(plotRB,0,wx.ALIGN_CENTER_VERTICAL)
1870            nameSizer.Add((5,0),)
1871            if not rbData['useCount']:
1872                delRB = wx.CheckBox(ResidueRBDisplay,-1,'Delete?')
1873                Indx[delRB.GetId()] = rbId
1874                delRB.Bind(wx.EVT_CHECKBOX,OnDelRB)
1875                nameSizer.Add(delRB,0,wx.ALIGN_CENTER_VERTICAL)
1876                if 'H'  in rbData['rbTypes']:
1877                    stripH = wx.CheckBox(ResidueRBDisplay,-1,'Strip H-atoms?')
1878                    Indx[stripH.GetId()] = rbId
1879                    stripH.Bind(wx.EVT_CHECKBOX,OnStripH)
1880                    nameSizer.Add(stripH,0,wx.ALIGN_CENTER_VERTICAL)
1881            return nameSizer
1882           
1883        def rbResidues(rbId,rbData):
1884           
1885            def TypeSelect(event):
1886                AtInfo = data['Residue']['AtInfo']
1887                r,c = event.GetRow(),event.GetCol()
1888                if vecGrid.GetColLabelValue(c) == 'Type':
1889                    PE = G2elemGUI.PickElement(G2frame,oneOnly=True)
1890                    if PE.ShowModal() == wx.ID_OK:
1891                        if PE.Elem != 'None':
1892                            El = PE.Elem.strip().lower().capitalize()
1893                            if El not in AtInfo:
1894                                Info = G2elem.GetAtomInfo(El)
1895                                AtInfo[El] = [Info['Drad']['Color']]
1896                            rbData['rbTypes'][r] = El
1897                            vecGrid.SetCellValue(r,c,El)
1898                    PE.Destroy()
1899
1900            def ChangeCell(event):
1901                r,c =  event.GetRow(),event.GetCol()
1902                if r >= 0 and (0 <= c < 3):
1903                    try:
1904                        val = float(vecGrid.GetCellValue(r,c))
1905                        rbData['rbXYZ'][r][c] = val
1906                    except ValueError:
1907                        pass
1908                       
1909            def RowSelect(event):
1910                r,c =  event.GetRow(),event.GetCol()
1911                if c < 0:                   #only row clicks
1912                    for vecgrid in resList:
1913                        vecgrid.ClearSelection()
1914                    vecGrid.SelectRow(r,True)
1915
1916            def OnRefSel(event):
1917                Obj = event.GetEventObject()
1918                iref,res,jref = Indx[Obj.GetId()]
1919                sel = Obj.GetValue()
1920                ind = atNames.index(sel)
1921                if rbData['rbTypes'][ind] == 'H':
1922                    G2G.G2MessageBox(G2frame,'You should not select an H-atom for rigid body orientation')
1923                rbData['rbRef'][iref] = ind
1924                FillRefChoice(rbId,rbData)
1925                for i,ref in enumerate(RefObjs[jref]):
1926                    ref.SetItems([atNames[j] for j in refChoice[rbId][i]])
1927                    ref.SetValue(atNames[rbData['rbRef'][i]])
1928                if not iref:     #origin change
1929                    rbXYZ = rbData['rbXYZ']
1930                    rbXYZ -= rbXYZ[ind]
1931                    res.ClearSelection()
1932                    resTable = res.GetTable()
1933                    for r in range(res.GetNumberRows()):
1934                        row = resTable.GetRowValues(r)
1935                        row[2:4] = rbXYZ[r]
1936                        resTable.SetRowValues(r,row)
1937                    res.ForceRefresh()
1938                    G2plt.PlotRigidBody(G2frame,'Residue',AtInfo,rbData,plotDefaults)
1939               
1940            Types = 2*[wg.GRID_VALUE_STRING,]+3*[wg.GRID_VALUE_FLOAT+':10,5',]
1941            colLabels = ['Name','Type','Cart x','Cart y','Cart z']
1942            table = []
1943            rowLabels = []
1944            for ivec,xyz in enumerate(rbData['rbXYZ']):
1945                table.append([rbData['atNames'][ivec],]+[rbData['rbTypes'][ivec],]+list(xyz))
1946                rowLabels.append(str(ivec))
1947            vecTable = G2G.Table(table,rowLabels=rowLabels,colLabels=colLabels,types=Types)
1948            vecGrid = G2G.GSGrid(ResidueRBDisplay)
1949            Indx[vecGrid.GetId()] = rbId
1950            resList.append(vecGrid)
1951            vecGrid.SetTable(vecTable, True)
1952            vecGrid.Bind(wg.EVT_GRID_CELL_CHANGE, ChangeCell)
1953            vecGrid.Bind(wg.EVT_GRID_CELL_LEFT_DCLICK, TypeSelect)
1954            vecGrid.Bind(wg.EVT_GRID_LABEL_LEFT_CLICK, RowSelect)
1955            attr = wx.grid.GridCellAttr()
1956            attr.SetEditor(G2G.GridFractionEditor(vecGrid))
1957            for c in range(3):
1958                vecGrid.SetColAttr(c, attr)
1959            for row in range(vecTable.GetNumberRows()):
1960                for col in range(5):
1961                    vecGrid.SetCellStyle(row,col,VERY_LIGHT_GREY,True)
1962#            vecGrid.SetScrollRate(0,0)
1963            vecGrid.AutoSizeColumns(False)
1964            vecSizer = wx.BoxSizer()
1965            vecSizer.Add(vecGrid)
1966           
1967            refAtmSizer = wx.BoxSizer(wx.HORIZONTAL)
1968            atNames = rbData['atNames']
1969            rbRef = rbData['rbRef']
1970            if rbData['rbRef'][3] or rbData['useCount']:
1971                refAtmSizer.Add(wx.StaticText(ResidueRBDisplay,-1,
1972                    'Orientation reference non-H atoms A-B-C: %s, %s, %s'%(atNames[rbRef[0]], \
1973                     atNames[rbRef[1]],atNames[rbRef[2]])),0)
1974            else:
1975                refAtmSizer.Add(wx.StaticText(ResidueRBDisplay,-1,
1976                    'Orientation reference non-H atoms A-B-C: '),0,wx.ALIGN_CENTER_VERTICAL)
1977                refObj = [0,0,0]
1978                for i in range(3):
1979                    choices = [atNames[j] for j in refChoice[rbId][i]]
1980                    refSel = wx.ComboBox(ResidueRBDisplay,-1,value='',
1981                        choices=choices,style=wx.CB_READONLY|wx.CB_DROPDOWN)
1982                    refSel.SetValue(atNames[rbRef[i]])
1983                    refSel.Bind(wx.EVT_COMBOBOX, OnRefSel)
1984                    Indx[refSel.GetId()] = [i,vecGrid,len(RefObjs)]
1985                    refObj[i] = refSel
1986                    refAtmSizer.Add(refSel,0,wx.ALIGN_CENTER_VERTICAL)
1987                RefObjs.append(refObj)
1988           
1989            mainSizer = wx.BoxSizer(wx.VERTICAL)
1990            mainSizer.Add(refAtmSizer)
1991            mainSizer.Add(vecSizer)
1992            return mainSizer
1993           
1994        def SeqSizer(angSlide,rbId,iSeq,Seq,atNames):
1995           
1996            def ChangeAngle(event):
1997                event.Skip()
1998                Obj = event.GetEventObject()
1999                rbId,Seq = Indx[Obj.GetId()][:2]
2000                val = Seq[2]
2001                try:
2002                    val = float(Obj.GetValue())
2003                    Seq[2] = val
2004                except ValueError:
2005                    pass
2006                Obj.SetValue('%8.2f'%(val))
2007                G2plt.PlotRigidBody(G2frame,'Residue',AtInfo,data['Residue'][rbId],plotDefaults)
2008               
2009            def OnRadBtn(event):
2010                Obj = event.GetEventObject()
2011                Seq,iSeq,angId = Indx[Obj.GetId()]
2012                data['Residue'][rbId]['SelSeq'] = [iSeq,angId]
2013                angSlide.SetValue(int(100*Seq[2]))
2014               
2015            def OnDelBtn(event):
2016                Obj = event.GetEventObject()
2017                rbId,Seq = Indx[Obj.GetId()]
2018                data['Residue'][rbId]['rbSeq'].remove(Seq)       
2019                wx.CallAfter(UpdateResidueRB)
2020           
2021            seqSizer = wx.FlexGridSizer(0,5,2,2)
2022            seqSizer.AddGrowableCol(3,0)
2023            iBeg,iFin,angle,iMove = Seq
2024            ang = wx.TextCtrl(ResidueRBDisplay,-1,'%8.2f'%(angle),size=(50,20))
2025            if not iSeq:
2026                radBt = wx.RadioButton(ResidueRBDisplay,-1,'',style=wx.RB_GROUP)
2027                data['Residue'][rbId]['SelSeq'] = [iSeq,ang.GetId()]
2028            else:
2029                radBt = wx.RadioButton(ResidueRBDisplay,-1,'')
2030            radBt.Bind(wx.EVT_RADIOBUTTON,OnRadBtn)                   
2031            seqSizer.Add(radBt)
2032            delBt = wx.RadioButton(ResidueRBDisplay,-1,'')
2033            delBt.Bind(wx.EVT_RADIOBUTTON,OnDelBtn)
2034            seqSizer.Add(delBt)
2035            bond = wx.TextCtrl(ResidueRBDisplay,-1,'%s %s'%(atNames[iBeg],atNames[iFin]),size=(50,20))
2036            seqSizer.Add(bond,0,wx.ALIGN_CENTER_VERTICAL)
2037            Indx[radBt.GetId()] = [Seq,iSeq,ang.GetId()]
2038            Indx[delBt.GetId()] = [rbId,Seq]
2039            Indx[ang.GetId()] = [rbId,Seq,ang]
2040            ang.Bind(wx.EVT_TEXT_ENTER,ChangeAngle)
2041            ang.Bind(wx.EVT_KILL_FOCUS,ChangeAngle)
2042            seqSizer.Add(ang,0,wx.ALIGN_CENTER_VERTICAL)
2043            atms = ''
2044            for i in iMove:   
2045                atms += ' %s,'%(atNames[i])
2046            moves = wx.TextCtrl(ResidueRBDisplay,-1,atms[:-1],size=(200,20))
2047            seqSizer.Add(moves,1,wx.ALIGN_CENTER_VERTICAL|wx.EXPAND|wx.RIGHT)
2048            return seqSizer
2049           
2050        def SlideSizer():
2051           
2052            def OnSlider(event):
2053                Obj = event.GetEventObject()
2054                rbData = Indx[Obj.GetId()]
2055                iSeq,angId = rbData['SelSeq']
2056                val = float(Obj.GetValue())/100.
2057                rbData['rbSeq'][iSeq][2] = val
2058                Indx[angId][2].SetValue('%8.2f'%(val))
2059                G2plt.PlotRigidBody(G2frame,'Residue',AtInfo,rbData,plotDefaults)
2060           
2061            slideSizer = wx.BoxSizer(wx.HORIZONTAL)
2062            slideSizer.Add(wx.StaticText(ResidueRBDisplay,-1,'Selected torsion angle:'),0)
2063            iSeq,angId = rbData['SelSeq']
2064            angSlide = wx.Slider(ResidueRBDisplay,-1,
2065                int(100*rbData['rbSeq'][iSeq][2]),0,36000,size=(200,20),
2066                style=wx.SL_HORIZONTAL)
2067            angSlide.Bind(wx.EVT_SLIDER, OnSlider)
2068            Indx[angSlide.GetId()] = rbData
2069            slideSizer.Add(angSlide,0)           
2070            return slideSizer,angSlide
2071           
2072        def FillRefChoice(rbId,rbData):
2073            choiceIds = [i for i in range(len(rbData['atNames']))]
2074            for seq in rbData['rbSeq']:
2075                for i in seq[3]:
2076                    try:
2077                        choiceIds.remove(i)
2078                    except ValueError:
2079                        pass
2080            rbRef = rbData['rbRef']
2081            for i in range(3):
2082                try:
2083                    choiceIds.remove(rbRef[i])
2084                except ValueError:
2085                    pass
2086            refChoice[rbId] = [choiceIds[:],choiceIds[:],choiceIds[:]]
2087            for i in range(3):
2088                refChoice[rbId][i].append(rbRef[i])
2089                refChoice[rbId][i].sort()     
2090           
2091        ResidueRBDisplay.DestroyChildren()
2092        ResidueRBSizer = wx.BoxSizer(wx.VERTICAL)
2093        for rbId in data['RBIds']['Residue']:
2094            rbData = data['Residue'][rbId]
2095            if len(rbData['rbXYZ']) < 3:    #patch - skip around bad RBs with too few atoms
2096                continue
2097            FillRefChoice(rbId,rbData)
2098            ResidueRBSizer.Add(rbNameSizer(rbId,rbData),0)
2099            ResidueRBSizer.Add(rbResidues(rbId,rbData),0)
2100            ResidueRBSizer.Add((5,5),0)
2101            if rbData['rbSeq']:
2102                slideSizer,angSlide = SlideSizer()
2103            if len(rbData['rbSeq']):
2104                ResidueRBSizer.Add(wx.StaticText(ResidueRBDisplay,-1,
2105                    'Sel  Del  Bond             Angle      Riding atoms'),
2106                    0,wx.ALIGN_CENTER_VERTICAL)                       
2107            for iSeq,Seq in enumerate(rbData['rbSeq']):
2108                ResidueRBSizer.Add(SeqSizer(angSlide,rbId,iSeq,Seq,rbData['atNames']))
2109            if rbData['rbSeq']:
2110                ResidueRBSizer.Add(slideSizer,)
2111            ResidueRBSizer.Add(wx.StaticText(ResidueRBDisplay,-1,70*'-'))
2112
2113        ResidueRBSizer.Add((5,25),)
2114        ResidueRBSizer.Layout()   
2115        ResidueRBDisplay.SetSizer(ResidueRBSizer,True)
2116        Size = ResidueRBSizer.GetMinSize()
2117        Size[0] += 40
2118        Size[1] = max(Size[1],450) + 20
2119        ResidueRBDisplay.SetSize(Size)
2120        ResidueRB.SetScrollbars(10,10,Size[0]/10-4,Size[1]/10-1)
2121       
2122    def SetStatusLine(text):
2123        G2frame.GetStatusBar().SetStatusText(text,1)                                     
2124
2125    G2gd.SetDataMenuBar(G2frame,G2frame.dataWindow.RigidBodyMenu)
2126    G2frame.SetTitle('Rigid bodies')
2127    SetStatusLine('')
2128    UpdateVectorRB()
2129    G2frame.rbBook.Bind(wx.aui.EVT_AUINOTEBOOK_PAGE_CHANGED, OnPageChanged)
2130    wx.CallAfter(OnPageChanged,None)
Note: See TracBrowser for help on using the repository browser.