source: trunk/GSASIIconstrGUI.py @ 3061

Last change on this file since 3061 was 3061, checked in by toby, 4 years ago

sort variable names

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