source: trunk/GSASIIconstrGUI.py @ 3396

Last change on this file since 3396 was 3396, checked in by vondreele, 4 years ago

set refList=False for a couple of calls to GetHistogramPhaseData? where reflection list shouldn't be rebuilt
fix OnDrawDist? to report Sym Ops correctly
Move atom labels over 4 spaces to be clear of atom ball

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