source: trunk/GSASIIconstrGUI.py @ 3317

Last change on this file since 3317 was 3317, checked in by vondreele, 5 years ago

magnetic site symmetry restrictions on moments & magnetic site symmetry symbols

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