source: trunk/GSASIIconstrGUI.py @ 3397

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

fix Constraints problem when no histograms (or phases)
display new BNS lattice after change in TransformDialog?
Make magnetic now a Button
fix SyteSym? & mult after space group/BNS change - new UpdateSytSym? routine in G2spc

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