source: trunk/GSASIIconstrGUI.py @ 3136

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

make GSAS-II python 3.6 compliant & preserve python 2.7 use;changes:
do from future import division, print_function for all GSAS-II py sources
all menu items revised to be py 2.7/3.6 compliant
all wx.OPEN --> wx.FD_OPEN in file dialogs
all integer divides (typically for image pixel math) made explicit with ; ambiguous ones made floats as appropriate
all print "stuff" --> print (stuff)
all print >> pFile,'stuff' --> pFile.writeCIFtemplate('stuff')
all read file opens made explicit 'r' or 'rb'
all cPickle imports made for py2.7 or 3.6 as cPickle or _pickle; test for '2' platform.version_tuple[0] for py 2.7
define cPickleload to select load(fp) or load(fp,encoding='latin-1') for loading gpx files; provides cross compatibility between py 2.7/3.6 gpx files
make dict.keys() as explicit list(dict.keys()) as needed (NB: possible source of remaining py3.6 bugs)
make zip(a,b) as explicit list(zip(a,b)) as needed (NB: possible source of remaining py3.6 bugs)
select unichr/chr according test for '2' platform.version_tuple[0] for py 2.7 (G2pwdGUI * G2plot) for special characters
select wg.EVT_GRID_CELL_CHANGE (classic) or wg.EVT_GRID_CELL_CHANGED (phoenix) in grid Bind
maxint --> maxsize; used in random number stuff
raise Exception,"stuff" --> raise Exception("stuff")
wx 'classic' sizer.DeleteWindows?() or 'phoenix' sizer.Clear(True)
wx 'classic' SetToolTipString?(text) or 'phoenix' SetToolTip?(wx.ToolTip?(text)); define SetToolTipString?(self,text) to handle the choice in plots
status.SetFields? --> status.SetStatusText?
'classic' AddSimpleTool? or 'phoenix' self.AddTool? for plot toolbar; Bind different as well
define GetItemPydata? as it doesn't exist in wx 'phoenix'
allow python versions 2.7 & 3.6 to run GSAS-II
Bind override commented out - no logging capability (NB: remove all logging code?)
all import ContentsValidator? open filename & test if valid then close; filepointer removed from Reader
binary importers (mostly images) test for 'byte' type & convert as needed to satisfy py 3.6 str/byte rules

  • Property svn:eol-style set to native
  • Property svn:keywords set to Date Author Revision URL Id
File size: 94.3 KB
Line 
1# -*- coding: utf-8 -*-
2#GSASIIconstrGUI - constraint GUI routines
3########### SVN repository information ###################
4# $Date: 2017-10-23 16:39:16 +0000 (Mon, 23 Oct 2017) $
5# $Author: vondreele $
6# $Revision: 3136 $
7# $URL: trunk/GSASIIconstrGUI.py $
8# $Id: GSASIIconstrGUI.py 3136 2017-10-23 16:39:16Z 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: 3136 $")
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    constraints = G2frame.GPXtree.GetItemPyData(item)
1218#    GSASIIpath.IPyBreak()
1219    parmDict = {}
1220    varyList = []
1221    for ia,code in enumerate(atCodes):
1222        atom = nAtoms[ia]
1223        siteSym = G2spc.SytSym(atom[cx:cx+3],nSGData)[0]
1224        CSX = G2spc.GetCSxinel(siteSym)
1225        item = code.split('+')[0]
1226        iat,opr = item.split(':')
1227        Nop = abs(int(opr))%100-1
1228        if '-' in opr:
1229            Nop *= -1
1230        Opr = oldPhase['General']['SGData']['SGOps'][abs(Nop)][0]
1231        if Nop < 0:         #inversion
1232            Opr *= -1
1233        XOpr = np.inner(Opr,Trans.T)
1234        names = ['dAx','dAy','dAz']
1235        for ix,name in enumerate(names):
1236            IndpCon = [1.0,G2obj.G2VarObj('%d::%s:%s'%(opId,name,iat))]
1237            DepCons = []
1238            for iop,opval in enumerate(XOpr[ix]):
1239                if opval and CSX[0][ix]:    #-opval from defn of dAx, etc.
1240                    DepCons.append([-opval,G2obj.G2VarObj('%d::%s:%d'%(npId,names[iop],ia))])
1241            if len(DepCons) == 1:
1242                constraints['Phase'].append([IndpCon,DepCons[0],None,None,'e'])
1243            elif len(DepCons) > 1:
1244                for Dep in DepCons:
1245                    Dep[0] *= -1
1246                constraints['Phase'].append([IndpCon]+DepCons+[0.0,None,'c'])
1247        for name in ['Afrac','AUiso']:
1248            IndpCon = [1.0,G2obj.G2VarObj('%d::%s:%s'%(opId,name,iat))]
1249            DepCons = [1.0,G2obj.G2VarObj('%d::%s:%d'%(npId,name,ia))]
1250            constraints['Phase'].append([IndpCon,DepCons,None,None,'e'])
1251        #how do I do Uij's for most Trans?
1252    Anames = [['A0','A3','A4'],['A3','A1','A5'],['A4','A5','A2']]
1253    As = ['A0','A1','A2','A3','A4','A5']
1254    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]]
1255    Axes = ['a','b','c']
1256    Holds = []
1257    for iA,Aid in enumerate(Aids):
1258        parm = SetUniqAj(opId,iA,Aid[2],oSGData['SGLaue'])
1259        parmDict[parm] = oAcof[iA]
1260        varyList.append(parm)
1261        IndpCon = [1.0,G2obj.G2VarObj(parm)]
1262        DepCons = []
1263        for iat in range(3):
1264            if nSGData['SGLaue'] in ['-1','2/m']:       #set holds
1265                if (abs(nAcof[iA]) < 1.e-8) and (abs(Trans[Aid[0],Aid[1]]) < 1.e-8):
1266                    if Axes[iat] != oSGData['SGUniq'] and oSGData['SGLaue'] != nSGData['SGLaue']:
1267                        HoldObj = G2obj.G2VarObj('%d::%s'%(npId,Aid[2]))
1268                        if not HoldObj in Holds: 
1269                            constraints['Phase'].append([[0.0,HoldObj],None,None,'h'])
1270                            Holds.append(HoldObj)
1271                            continue
1272#            print iA,Aid,iat,invTrans[iat][Aid[0]],invTrans[Aid[1]][iat],Anames[Aid[0]][Aid[1]],parm
1273            if abs(invTrans[iat,Aid[1]]) > 1.e-8 and abs(nAcof[iA]) > 1.e-8:
1274                parm = SetUniqAj(npId,iA,Anames[Aid[0]][Aid[1]],nSGData['SGLaue'])
1275                parmDict[parm] = nAcof[As.index(Aid[2])]
1276                if not parm in varyList:
1277                    varyList.append(parm)
1278                DepCons.append([Trans[Aid[0],Aid[0]]*Trans[Aid[1],Aid[1]],G2obj.G2VarObj(parm)])
1279        if len(DepCons) == 1:
1280            constraints['Phase'].append([IndpCon,DepCons[0],None,None,'e'])
1281        elif len(DepCons) > 1:       
1282            for Dep in DepCons:
1283                Dep[0] *= -1
1284            constraints['Phase'].append([IndpCon]+DepCons+[0.0,None,'c'])
1285#    constDict,fixedList,ignored = G2stIO.ProcessConstraints(constraints['Phase'])
1286#    groups,parmlist = G2mv.GroupConstraints(constDict)
1287#    G2mv.GenerateConstraints(groups,parmlist,varyList,constDict,fixedList,parmDict)
1288#    print 'old',parmDict
1289#    G2mv.Dict2Map(parmDict,varyList)
1290#    print 'new',parmDict
1291    for hId,hist in enumerate(UseList):    #HAP - seems OK
1292        ohapkey = '%d:%d:'%(opId,hId)
1293        nhapkey = '%d:%d:'%(npId,hId)
1294        IndpCon = [1.0,G2obj.G2VarObj(ohapkey+'Scale')]
1295        DepCons = [detTrans,G2obj.G2VarObj(nhapkey+'Scale')]
1296        constraints['HAP'].append([IndpCon,DepCons,None,None,'e'])
1297        for name in ['Size;i','Mustrain;i']:
1298            IndpCon = [1.0,G2obj.G2VarObj(ohapkey+name)]
1299            DepCons = [1.0,G2obj.G2VarObj(nhapkey+name)]
1300            constraints['HAP'].append([IndpCon,DepCons,None,None,'e'])
1301       
1302################################################################################
1303#### Rigid bodies
1304################################################################################
1305
1306def UpdateRigidBodies(G2frame,data):
1307    '''Called when Rigid bodies tree item is selected.
1308    Displays the rigid bodies in the data window
1309    '''
1310    if not data.get('RBIds') or not data:
1311        data.update({'Vector':{'AtInfo':{}},'Residue':{'AtInfo':{}},
1312            'RBIds':{'Vector':[],'Residue':[]}})       #empty/bad dict - fill it
1313           
1314    global resList
1315    Indx = {}
1316    resList = []
1317    plotDefaults = {'oldxy':[0.,0.],'Quaternion':[0.,0.,0.,1.],'cameraPos':30.,'viewDir':[0,0,1],}
1318
1319    G2frame.rbBook = G2G.GSNoteBook(parent=G2frame.dataWindow)
1320    G2frame.dataWindow.GetSizer().Add(G2frame.rbBook,1,wx.ALL|wx.EXPAND)
1321    VectorRB = wx.ScrolledWindow(G2frame.rbBook)
1322    VectorRBDisplay = wx.Panel(VectorRB)
1323    G2frame.rbBook.AddPage(VectorRB,'Vector rigid bodies')
1324    ResidueRB = wx.ScrolledWindow(G2frame.rbBook)
1325    ResidueRBDisplay = wx.Panel(ResidueRB)
1326    G2frame.rbBook.AddPage(ResidueRB,'Residue rigid bodies')
1327   
1328    def OnPageChanged(event):
1329        global resList
1330        resList = []
1331        if event:       #page change event!
1332            page = event.GetSelection()
1333        else:
1334            page = G2frame.rbBook.GetSelection()
1335        #G2frame.rbBook.SetSize(G2frame.dataWindow.GetClientSize())    #TODO -almost right
1336        G2frame.rbBook.ChangeSelection(page)
1337        text = G2frame.rbBook.GetPageText(page)
1338        if text == 'Vector rigid bodies':
1339            G2gd.SetDataMenuBar(G2frame,G2frame.dataWindow.VectorBodyMenu)
1340            G2frame.Bind(wx.EVT_MENU, AddVectorRB, id=G2G.wxID_VECTORBODYADD)
1341            G2frame.Page = [page,'vrb']
1342            UpdateVectorRB()
1343        elif text == 'Residue rigid bodies':
1344            G2gd.SetDataMenuBar(G2frame,G2frame.dataWindow.RigidBodyMenu)
1345            G2frame.Bind(wx.EVT_MENU, AddResidueRB, id=G2G.wxID_RIGIDBODYADD)
1346            G2frame.Bind(wx.EVT_MENU, OnImportRigidBody, id=G2G.wxID_RIGIDBODYIMPORT)
1347            G2frame.Bind(wx.EVT_MENU, OnDefineTorsSeq, id=G2G.wxID_RESIDUETORSSEQ) #enable only if residue RBs exist?
1348            G2frame.Page = [page,'rrb']
1349            UpdateResidueRB()
1350           
1351    def getMacroFile(macName):
1352        defDir = os.path.join(os.path.split(__file__)[0],'GSASIImacros')
1353        dlg = wx.FileDialog(G2frame,message='Choose '+macName+' rigid body macro file',
1354            defaultDir=defDir,defaultFile="",wildcard="GSAS-II macro file (*.mac)|*.mac",
1355            style=wx.FD_OPEN | wx.CHANGE_DIR)
1356        try:
1357            if dlg.ShowModal() == wx.ID_OK:
1358                macfile = dlg.GetPath()
1359                macro = open(macfile,'Ur')
1360                head = macro.readline()
1361                if macName not in head:
1362                    print (head)
1363                    print ('**** ERROR - wrong restraint macro file selected, try again ****')
1364                    macro = []
1365            else: # cancel was pressed
1366                macro = []
1367        finally:
1368            dlg.Destroy()
1369        return macro        #advanced past 1st line
1370       
1371    def getTextFile():
1372        dlg = wx.FileDialog(G2frame,'Choose rigid body text file', '.', '',
1373            "GSAS-II text file (*.txt)|*.txt|XYZ file (*.xyz)|*.xyz|"
1374            "Sybyl mol2 file (*.mol2)|*.mol2|PDB file (*.pdb;*.ent)|*.pdb;*.ent",
1375            wx.FD_OPEN | wx.CHANGE_DIR)
1376        try:
1377            if dlg.ShowModal() == wx.ID_OK:
1378                txtfile = dlg.GetPath()
1379                ext = os.path.splitext(txtfile)[1]
1380                text = open(txtfile,'Ur')
1381            else: # cancel was pressed
1382                ext = ''
1383                text = []
1384        finally:
1385            dlg.Destroy()
1386        if 'ent' in ext:
1387            ext = '.pdb'
1388        return text,ext.lower()
1389       
1390    def OnImportRigidBody(event):
1391        page = G2frame.rbBook.GetSelection()
1392        if 'Vector' in G2frame.rbBook.GetPageText(page):
1393            pass
1394        elif 'Residue' in G2frame.rbBook.GetPageText(page):
1395            ImportResidueRB()
1396           
1397    def AddVectorRB(event):
1398        AtInfo = data['Vector']['AtInfo']
1399        dlg = G2G.MultiIntegerDialog(G2frame,'New Rigid Body',['No. atoms','No. translations'],[1,1])
1400        if dlg.ShowModal() == wx.ID_OK:
1401            nAtoms,nTrans = dlg.GetValues()
1402            rbId = ran.randint(0,sys.maxsize)
1403            vecMag = [1.0 for i in range(nTrans)]
1404            vecRef = [False for i in range(nTrans)]
1405            vecVal = [np.zeros((nAtoms,3)) for j in range(nTrans)]
1406            rbTypes = ['C' for i in range(nAtoms)]
1407            Info = G2elem.GetAtomInfo('C')
1408            AtInfo['C'] = [Info['Drad'],Info['Color']]
1409            data['Vector'][rbId] = {'RBname':'UNKRB','VectMag':vecMag,'rbXYZ':np.zeros((nAtoms,3)),
1410                'rbRef':[0,1,2,False],'VectRef':vecRef,'rbTypes':rbTypes,'rbVect':vecVal,'useCount':0}
1411            data['RBIds']['Vector'].append(rbId)
1412        dlg.Destroy()
1413        UpdateVectorRB()
1414       
1415    def AddResidueRB(event):
1416        AtInfo = data['Residue']['AtInfo']
1417        macro = getMacroFile('rigid body')
1418        if not macro:
1419            return
1420        macStr = macro.readline()
1421        while macStr:
1422            items = macStr.split()
1423            if 'I' == items[0]:
1424                rbId = ran.randint(0,sys.maxsize)
1425                rbName = items[1]
1426                rbTypes = []
1427                rbXYZ = []
1428                rbSeq = []
1429                atNames = []
1430                nAtms,nSeq,nOrig,mRef,nRef = [int(items[i]) for i in [2,3,4,5,6]]
1431                for iAtm in range(nAtms):
1432                    macStr = macro.readline().split()
1433                    atName = macStr[0]
1434                    atType = macStr[1]
1435                    atNames.append(atName)
1436                    rbXYZ.append([float(macStr[i]) for i in [2,3,4]])
1437                    rbTypes.append(atType)
1438                    if atType not in AtInfo:
1439                        Info = G2elem.GetAtomInfo(atType)
1440                        AtInfo[atType] = [Info['Drad'],Info['Color']]
1441                rbXYZ = np.array(rbXYZ)-np.array(rbXYZ[nOrig-1])
1442                for iSeq in range(nSeq):
1443                    macStr = macro.readline().split()
1444                    mSeq = int(macStr[0])
1445                    for jSeq in range(mSeq):
1446                        macStr = macro.readline().split()
1447                        iBeg = int(macStr[0])-1
1448                        iFin = int(macStr[1])-1
1449                        angle = 0.0
1450                        nMove = int(macStr[2])
1451                        iMove = [int(macStr[i])-1 for i in range(3,nMove+3)]
1452                        rbSeq.append([iBeg,iFin,angle,iMove])
1453                data['Residue'][rbId] = {'RBname':rbName,'rbXYZ':rbXYZ,'rbTypes':rbTypes,
1454                    'atNames':atNames,'rbRef':[nOrig-1,mRef-1,nRef-1,True],'rbSeq':rbSeq,
1455                    'SelSeq':[0,0],'useCount':0}
1456                data['RBIds']['Residue'].append(rbId)
1457                print ('Rigid body '+rbName+' added')
1458            macStr = macro.readline()
1459        macro.close()
1460        UpdateResidueRB()
1461       
1462    def ImportResidueRB():
1463        AtInfo = data['Residue']['AtInfo']
1464        text,ext = getTextFile()
1465        if not text:
1466            return
1467        rbId = ran.randint(0,sys.maxsize)
1468        rbTypes = []
1469        rbXYZ = []
1470        atNames = []
1471        txtStr = text.readline()
1472        if 'xyz' in ext:
1473            txtStr = text.readline()
1474            txtStr = text.readline()
1475        elif 'mol2' in ext:
1476            while 'ATOM' not in txtStr:
1477                txtStr = text.readline()
1478            txtStr = text.readline()
1479        elif 'pdb' in ext:
1480            while 'ATOM' not in txtStr[:6] and 'HETATM' not in txtStr[:6]:
1481                txtStr = text.readline()
1482                #print txtStr
1483        items = txtStr.split()
1484        while len(items):
1485            if 'txt' in ext:
1486                atName = items[0]
1487                atType = items[1]
1488                rbXYZ.append([float(items[i]) for i in [2,3,4]])
1489            elif 'xyz' in ext:
1490                atType = items[0]
1491                rbXYZ.append([float(items[i]) for i in [1,2,3]])
1492                atName = atType+str(len(rbXYZ))
1493            elif 'mol2' in ext:
1494                atType = items[1]
1495                atName = items[1]+items[0]
1496                rbXYZ.append([float(items[i]) for i in [2,3,4]])
1497            elif 'pdb' in ext:
1498                atType = items[-1]
1499                atName = items[2]
1500                xyz = txtStr[30:55].split()                   
1501                rbXYZ.append([float(x) for x in xyz])
1502            atNames.append(atName)
1503            rbTypes.append(atType)
1504            if atType not in AtInfo:
1505                Info = G2elem.GetAtomInfo(atType)
1506                AtInfo[atType] = [Info['Drad'],Info['Color']]
1507            txtStr = text.readline()
1508            if 'mol2' in ext and 'BOND' in txtStr:
1509                break
1510            if 'pdb' in ext and ('ATOM' not in txtStr[:6] and 'HETATM' not in txtStr[:6]):
1511                break
1512            items = txtStr.split()
1513        if len(atNames) < 3:
1514            G2G.G2MessageBox(G2frame,'Not enough atoms in rigid body; must be 3 or more')
1515        else:
1516            rbXYZ = np.array(rbXYZ)-np.array(rbXYZ[0])
1517            Xxyz = rbXYZ[1]
1518            X = Xxyz/np.sqrt(np.sum(Xxyz**2))
1519            Yxyz = rbXYZ[2]
1520            Y = Yxyz/np.sqrt(np.sum(Yxyz**2))
1521            Mat = G2mth.getRBTransMat(X,Y)
1522            rbXYZ = np.inner(Mat,rbXYZ).T
1523            data['Residue'][rbId] = {'RBname':'UNKRB','rbXYZ':rbXYZ,'rbTypes':rbTypes,
1524                'atNames':atNames,'rbRef':[0,1,2,False],'rbSeq':[],'SelSeq':[0,0],'useCount':0}
1525            data['RBIds']['Residue'].append(rbId)
1526            print ('Rigid body UNKRB added')
1527        text.close()
1528        UpdateResidueRB()
1529       
1530    def FindNeighbors(Orig,XYZ,atTypes,atNames,AtInfo):
1531        Radii = []
1532        for Atype in atTypes:
1533            Radii.append(AtInfo[Atype][0])
1534        Radii = np.array(Radii)
1535        Neigh = []
1536        Dx = XYZ-XYZ[Orig]
1537        dist = np.sqrt(np.sum(Dx**2,axis=1))
1538        sumR = Radii[Orig]+Radii
1539        IndB = ma.nonzero(ma.masked_greater(dist-0.85*sumR,0.))
1540        for j in IndB[0]:
1541            if j != Orig and atTypes[j] != 'H':
1542                Neigh.append(atNames[j])
1543        return Neigh
1544       
1545    def FindAllNeighbors(XYZ,atTypes,atNames,AtInfo):
1546        NeighDict = {}
1547        for iat,xyz in enumerate(atNames):
1548            NeighDict[atNames[iat]] = FindNeighbors(iat,XYZ,atTypes,atNames,AtInfo)
1549        return NeighDict
1550       
1551    def FindRiding(Orig,Pivot,NeighDict):
1552        riding = [Orig,Pivot]
1553        iAdd = 1
1554        new = True
1555        while new:
1556            newAtms = NeighDict[riding[iAdd]]
1557            for At in newAtms:
1558                new = False
1559                if At not in riding:
1560                    riding.append(At)
1561                    new = True
1562            iAdd += 1
1563            if iAdd < len(riding):
1564                new = True
1565        return riding[2:]
1566                       
1567    def OnDefineTorsSeq(event):
1568        rbKeys = list(data['Residue'].keys())
1569        rbKeys.remove('AtInfo')
1570        rbNames = [data['Residue'][k]['RBname'] for k in rbKeys]
1571        rbIds = dict(zip(rbNames,rbKeys))
1572        rbNames.sort()
1573        rbId = 0
1574        if len(rbNames) == 0:
1575            print ('There are no rigid bodies defined')
1576            G2frame.ErrorDialog('No rigid bodies','There are no rigid bodies defined',
1577                                parent=G2frame)
1578            return
1579        elif len(rbNames) > 1:
1580            dlg = wx.SingleChoiceDialog(G2frame,'Select rigid body for torsion sequence','Torsion sequence',rbNames)
1581            if dlg.ShowModal() == wx.ID_OK:
1582                sel = dlg.GetSelection()
1583                rbId = rbIds[rbNames[sel]]
1584                rbData = data['Residue'][rbId]
1585            dlg.Destroy()
1586        else:
1587            rbId = rbIds[rbNames[0]]
1588            rbData = data['Residue'][rbId]
1589        if not len(rbData):
1590            return
1591        atNames = rbData['atNames']
1592        AtInfo = data['Residue']['AtInfo']
1593        atTypes = rbData['rbTypes']
1594        XYZ = rbData['rbXYZ']
1595        neighDict = FindAllNeighbors(XYZ,atTypes,atNames,AtInfo)
1596        TargList = []           
1597        dlg = wx.SingleChoiceDialog(G2frame,'Select origin atom for torsion sequence','Origin atom',rbData['atNames'])
1598        if dlg.ShowModal() == wx.ID_OK:
1599            Orig = dlg.GetSelection()
1600            TargList = neighDict[atNames[Orig]]
1601        dlg.Destroy()
1602        if not len(TargList):
1603            return
1604        dlg = wx.SingleChoiceDialog(G2frame,'Select pivot atom for torsion sequence','Pivot atom',TargList)
1605        if dlg.ShowModal() == wx.ID_OK:
1606            Piv = atNames.index(TargList[dlg.GetSelection()])
1607            riding = FindRiding(atNames[Orig],atNames[Piv],neighDict)
1608            Riding = []
1609            for atm in riding:
1610                Riding.append(atNames.index(atm))
1611            rbData['rbSeq'].append([Orig,Piv,0.0,Riding])           
1612        dlg.Destroy()
1613        UpdateResidueRB()
1614
1615    def UpdateVectorRB(Scroll=0):
1616        AtInfo = data['Vector']['AtInfo']
1617        refChoice = {}
1618        if 'DELETED' in str(G2frame.GetStatusBar()):   #seems to be no other way to do this (wx bug)
1619            if GSASIIpath.GetConfigValue('debug'):
1620                print ('wx error: Rigid Body/Status not cleanly deleted after Refine')
1621            return
1622        SetStatusLine(' You may use e.g. "c60" or "s60" for a vector entry')
1623        def rbNameSizer(rbId,rbData):
1624
1625            def OnRBName(event):
1626                event.Skip()
1627                Obj = event.GetEventObject()
1628                rbData['RBname'] = Obj.GetValue()
1629               
1630            def OnDelRB(event):
1631                Obj = event.GetEventObject()
1632                rbId = Indx[Obj.GetId()]
1633                if rbId in data['Vector']:
1634                    del data['Vector'][rbId]
1635                    data['RBIds']['Vector'].remove(rbId)
1636                    rbData['useCount'] -= 1
1637                wx.CallAfter(UpdateVectorRB)
1638               
1639            def OnPlotRB(event):
1640                Obj = event.GetEventObject()
1641                Obj.SetValue(False)
1642                G2plt.PlotRigidBody(G2frame,'Vector',AtInfo,rbData,plotDefaults)
1643           
1644            nameSizer = wx.BoxSizer(wx.HORIZONTAL)
1645            nameSizer.Add(wx.StaticText(VectorRBDisplay,-1,'Rigid body name: '),
1646                0,wx.ALIGN_CENTER_VERTICAL)
1647            RBname = wx.TextCtrl(VectorRBDisplay,-1,rbData['RBname'])
1648            Indx[RBname.GetId()] = rbId
1649            RBname.Bind(wx.EVT_TEXT_ENTER,OnRBName)
1650            RBname.Bind(wx.EVT_KILL_FOCUS,OnRBName)
1651            nameSizer.Add(RBname,0,wx.ALIGN_CENTER_VERTICAL)
1652            nameSizer.Add((5,0),)
1653            plotRB = wx.CheckBox(VectorRBDisplay,-1,'Plot?')
1654            Indx[plotRB.GetId()] = rbId
1655            plotRB.Bind(wx.EVT_CHECKBOX,OnPlotRB)
1656            nameSizer.Add(plotRB,0,wx.ALIGN_CENTER_VERTICAL)
1657            nameSizer.Add((5,0),)
1658            if not rbData['useCount']:
1659                delRB = wx.CheckBox(VectorRBDisplay,-1,'Delete?')
1660                Indx[delRB.GetId()] = rbId
1661                delRB.Bind(wx.EVT_CHECKBOX,OnDelRB)
1662                nameSizer.Add(delRB,0,wx.ALIGN_CENTER_VERTICAL)
1663            return nameSizer
1664           
1665        def rbRefAtmSizer(rbId,rbData):
1666           
1667            def OnRefSel(event):
1668                Obj = event.GetEventObject()
1669                iref = Indx[Obj.GetId()]
1670                sel = Obj.GetValue()
1671                rbData['rbRef'][iref] = atNames.index(sel)
1672                FillRefChoice(rbId,rbData)
1673           
1674            refAtmSizer = wx.BoxSizer(wx.HORIZONTAL)
1675            atNames = [name+str(i) for i,name in enumerate(rbData['rbTypes'])]
1676            rbRef = rbData.get('rbRef',[0,1,2,False])
1677            rbData['rbRef'] = rbRef
1678            if rbData['useCount']:
1679                refAtmSizer.Add(wx.StaticText(VectorRBDisplay,-1,
1680                    'Orientation reference atoms A-B-C: %s, %s, %s'%(atNames[rbRef[0]], \
1681                     atNames[rbRef[1]],atNames[rbRef[2]])),0)
1682            else:
1683                refAtmSizer.Add(wx.StaticText(VectorRBDisplay,-1,
1684                    'Orientation reference atoms A-B-C: '),0,wx.ALIGN_CENTER_VERTICAL)
1685                for i in range(3):
1686                    choices = [atNames[j] for j in refChoice[rbId][i]]
1687                    refSel = wx.ComboBox(VectorRBDisplay,-1,value='',
1688                        choices=choices,style=wx.CB_READONLY|wx.CB_DROPDOWN)
1689                    refSel.SetValue(atNames[rbRef[i]])
1690                    refSel.Bind(wx.EVT_COMBOBOX, OnRefSel)
1691                    Indx[refSel.GetId()] = i
1692                    refAtmSizer.Add(refSel,0,wx.ALIGN_CENTER_VERTICAL)
1693            return refAtmSizer
1694                       
1695        def rbVectMag(rbId,imag,rbData):
1696           
1697            def OnRBVectorMag(event):
1698                event.Skip()
1699                Obj = event.GetEventObject()
1700                rbId,imag = Indx[Obj.GetId()]
1701                try:
1702                    val = float(Obj.GetValue())
1703                    if val <= 0.:
1704                        raise ValueError
1705                    rbData['VectMag'][imag] = val
1706                except ValueError:
1707                    pass
1708                Obj.SetValue('%8.4f'%(val))
1709                wx.CallAfter(UpdateVectorRB,VectorRB.GetScrollPos(wx.VERTICAL))
1710                G2plt.PlotRigidBody(G2frame,'Vector',AtInfo,data['Vector'][rbId],plotDefaults)
1711               
1712            def OnRBVectorRef(event):
1713                Obj = event.GetEventObject()
1714                rbId,imag = Indx[Obj.GetId()]
1715                rbData['VectRef'][imag] = Obj.GetValue()
1716                       
1717            magSizer = wx.wx.BoxSizer(wx.HORIZONTAL)
1718            magSizer.Add(wx.StaticText(VectorRBDisplay,-1,'Translation magnitude: '),
1719                0,wx.ALIGN_CENTER_VERTICAL)
1720            magValue = wx.TextCtrl(VectorRBDisplay,-1,'%8.4f'%(rbData['VectMag'][imag]))
1721            Indx[magValue.GetId()] = [rbId,imag]
1722            magValue.Bind(wx.EVT_TEXT_ENTER,OnRBVectorMag)
1723            magValue.Bind(wx.EVT_KILL_FOCUS,OnRBVectorMag)
1724            magSizer.Add(magValue,0,wx.ALIGN_CENTER_VERTICAL)
1725            magSizer.Add((5,0),)
1726            magref = wx.CheckBox(VectorRBDisplay,-1,label=' Refine?') 
1727            magref.SetValue(rbData['VectRef'][imag])
1728            magref.Bind(wx.EVT_CHECKBOX,OnRBVectorRef)
1729            Indx[magref.GetId()] = [rbId,imag]
1730            magSizer.Add(magref,0,wx.ALIGN_CENTER_VERTICAL)
1731            return magSizer
1732           
1733        def rbVectors(rbId,imag,mag,XYZ,rbData):
1734
1735            def TypeSelect(event):
1736                AtInfo = data['Vector']['AtInfo']
1737                r,c = event.GetRow(),event.GetCol()
1738                if vecGrid.GetColLabelValue(c) == 'Type':
1739                    PE = G2elemGUI.PickElement(G2frame,oneOnly=True)
1740                    if PE.ShowModal() == wx.ID_OK:
1741                        if PE.Elem != 'None':
1742                            El = PE.Elem.strip().lower().capitalize()
1743                            if El not in AtInfo:
1744                                Info = G2elem.GetAtomInfo(El)
1745                                AtInfo[El] = [Info['Drad'],Info['Color']]
1746                            rbData['rbTypes'][r] = El
1747                            vecGrid.SetCellValue(r,c,El)
1748                    PE.Destroy()
1749                wx.CallAfter(UpdateVectorRB,VectorRB.GetScrollPos(wx.VERTICAL))
1750
1751            def ChangeCell(event):
1752                r,c =  event.GetRow(),event.GetCol()
1753                if r >= 0 and (0 <= c < 3):
1754                    try:
1755                        val = float(vecGrid.GetCellValue(r,c))
1756                        rbData['rbVect'][imag][r][c] = val
1757                    except ValueError:
1758                        pass
1759                G2plt.PlotRigidBody(G2frame,'Vector',AtInfo,data['Vector'][rbId],plotDefaults)
1760                wx.CallAfter(UpdateVectorRB,VectorRB.GetScrollPos(wx.VERTICAL))
1761
1762            vecSizer = wx.BoxSizer()
1763            Types = 3*[wg.GRID_VALUE_FLOAT+':10,5',]+[wg.GRID_VALUE_STRING,]+3*[wg.GRID_VALUE_FLOAT+':10,5',]
1764            colLabels = ['Vector x','Vector y','Vector z','Type','Cart x','Cart y','Cart z']
1765            table = []
1766            rowLabels = []
1767            for ivec,xyz in enumerate(rbData['rbVect'][imag]):
1768                table.append(list(xyz)+[rbData['rbTypes'][ivec],]+list(XYZ[ivec]))
1769                rowLabels.append(str(ivec))
1770            vecTable = G2G.Table(table,rowLabels=rowLabels,colLabels=colLabels,types=Types)
1771            vecGrid = G2G.GSGrid(VectorRBDisplay)
1772            vecGrid.SetTable(vecTable, True)
1773            if 'phoenix' in wx.version():
1774                vecGrid.Bind(wg.EVT_GRID_CELL_CHANGED, ChangeCell)
1775            else:
1776                vecGrid.Bind(wg.EVT_GRID_CELL_CHANGE, ChangeCell)
1777            if not imag:
1778                vecGrid.Bind(wg.EVT_GRID_CELL_LEFT_DCLICK, TypeSelect)
1779            attr = wx.grid.GridCellAttr()
1780            attr.SetEditor(G2G.GridFractionEditor(vecGrid))
1781            for c in range(3):
1782                vecGrid.SetColAttr(c, attr)
1783            for row in range(vecTable.GetNumberRows()):
1784                if imag:
1785                    vecGrid.SetCellStyle(row,3,VERY_LIGHT_GREY,True)                   
1786                for col in [4,5,6]:
1787                    vecGrid.SetCellStyle(row,col,VERY_LIGHT_GREY,True)
1788#            vecGrid.SetScrollRate(0,0)
1789            vecGrid.AutoSizeColumns(False)
1790            vecSizer.Add(vecGrid)
1791            return vecSizer
1792       
1793        def FillRefChoice(rbId,rbData):
1794            choiceIds = [i for i in range(len(rbData['rbTypes']))]
1795           
1796            rbRef = rbData.get('rbRef',[-1,-1,-1,False])
1797            for i in range(3):
1798                choiceIds.remove(rbRef[i])
1799            refChoice[rbId] = [choiceIds[:],choiceIds[:],choiceIds[:]]
1800            for i in range(3):
1801                refChoice[rbId][i].append(rbRef[i])
1802                refChoice[rbId][i].sort()     
1803           
1804        if VectorRB.GetSizer(): VectorRB.GetSizer().Clear(True)
1805        VectorRBSizer = wx.BoxSizer(wx.VERTICAL)
1806        for rbId in data['RBIds']['Vector']:
1807            if rbId != 'AtInfo':
1808                rbData = data['Vector'][rbId]
1809                FillRefChoice(rbId,rbData)
1810                VectorRBSizer.Add(rbNameSizer(rbId,rbData),0)
1811                VectorRBSizer.Add(rbRefAtmSizer(rbId,rbData),0)
1812                XYZ = np.array([[0.,0.,0.] for Ty in rbData['rbTypes']])
1813                for imag,mag in enumerate(rbData['VectMag']):
1814                    XYZ += mag*rbData['rbVect'][imag]
1815                    VectorRBSizer.Add(rbVectMag(rbId,imag,rbData),0)
1816                    VectorRBSizer.Add(rbVectors(rbId,imag,mag,XYZ,rbData),0)
1817                VectorRBSizer.Add((5,5),0)
1818                data['Vector'][rbId]['rbXYZ'] = XYZ       
1819        VectorRBSizer.Layout()   
1820        VectorRBDisplay.SetSizer(VectorRBSizer,True)
1821        Size = VectorRBSizer.GetMinSize()
1822        Size[0] += 40
1823        Size[1] = max(Size[1],450) + 20
1824        VectorRBDisplay.SetSize(Size)
1825        VectorRB.SetScrollbars(10,10,Size[0]/10-4,Size[1]/10-1)
1826        VectorRB.Scroll(0,Scroll)
1827       
1828    def UpdateResidueRB():
1829        AtInfo = data['Residue']['AtInfo']
1830        refChoice = {}
1831        RefObjs = []
1832
1833        def rbNameSizer(rbId,rbData):
1834
1835            def OnRBName(event):
1836                Obj = event.GetEventObject()
1837                rbData['RBname'] = Obj.GetValue()
1838               
1839            def OnDelRB(event):
1840                Obj = event.GetEventObject()
1841                rbId = Indx[Obj.GetId()]
1842                if rbId in data['Residue']: 
1843                    del data['Residue'][rbId]
1844                    data['RBIds']['Residue'].remove(rbId)
1845                wx.CallAfter(UpdateResidueRB)
1846               
1847            def OnStripH(event):
1848                Obj = event.GetEventObject()
1849                rbId = Indx[Obj.GetId()]
1850                if rbId in data['Residue']:
1851                    newNames = []
1852                    newTypes = []
1853                    newXYZ = []
1854                    for i,atype in enumerate(rbData['rbTypes']):
1855                        if atype != 'H':
1856                            newNames.append(rbData['atNames'][i])
1857                            newTypes.append(rbData['rbTypes'][i])
1858                            newXYZ.append(rbData['rbXYZ'][i])
1859                    rbData['atNames'] = newNames
1860                    rbData['rbTypes'] = newTypes
1861                    rbData['rbXYZ'] = newXYZ
1862                G2plt.PlotRigidBody(G2frame,'Residue',AtInfo,rbData,plotDefaults)
1863                wx.CallAfter(UpdateResidueRB)
1864                   
1865            def OnPlotRB(event):
1866                Obj = event.GetEventObject()
1867                Obj.SetValue(False)
1868                G2plt.PlotRigidBody(G2frame,'Residue',AtInfo,rbData,plotDefaults)
1869           
1870            nameSizer = wx.BoxSizer(wx.HORIZONTAL)
1871            nameSizer.Add(wx.StaticText(ResidueRBDisplay,-1,'Residue name: '),
1872                0,wx.ALIGN_CENTER_VERTICAL)
1873            RBname = wx.TextCtrl(ResidueRBDisplay,-1,rbData['RBname'])
1874            Indx[RBname.GetId()] = rbId
1875            RBname.Bind(wx.EVT_TEXT_ENTER,OnRBName)
1876            RBname.Bind(wx.EVT_KILL_FOCUS,OnRBName)
1877            nameSizer.Add(RBname,0,wx.ALIGN_CENTER_VERTICAL)
1878            nameSizer.Add((5,0),)
1879            plotRB = wx.CheckBox(ResidueRBDisplay,-1,'Plot?')
1880            Indx[plotRB.GetId()] = rbId
1881            plotRB.Bind(wx.EVT_CHECKBOX,OnPlotRB)
1882            nameSizer.Add(plotRB,0,wx.ALIGN_CENTER_VERTICAL)
1883            nameSizer.Add((5,0),)
1884            if not rbData['useCount']:
1885                delRB = wx.CheckBox(ResidueRBDisplay,-1,'Delete?')
1886                Indx[delRB.GetId()] = rbId
1887                delRB.Bind(wx.EVT_CHECKBOX,OnDelRB)
1888                nameSizer.Add(delRB,0,wx.ALIGN_CENTER_VERTICAL)
1889                if 'H'  in rbData['rbTypes']:
1890                    stripH = wx.CheckBox(ResidueRBDisplay,-1,'Strip H-atoms?')
1891                    Indx[stripH.GetId()] = rbId
1892                    stripH.Bind(wx.EVT_CHECKBOX,OnStripH)
1893                    nameSizer.Add(stripH,0,wx.ALIGN_CENTER_VERTICAL)
1894            return nameSizer
1895           
1896        def rbResidues(rbId,rbData):
1897           
1898            def TypeSelect(event):
1899                AtInfo = data['Residue']['AtInfo']
1900                r,c = event.GetRow(),event.GetCol()
1901                if vecGrid.GetColLabelValue(c) == 'Type':
1902                    PE = G2elemGUI.PickElement(G2frame,oneOnly=True)
1903                    if PE.ShowModal() == wx.ID_OK:
1904                        if PE.Elem != 'None':
1905                            El = PE.Elem.strip().lower().capitalize()
1906                            if El not in AtInfo:
1907                                Info = G2elem.GetAtomInfo(El)
1908                                AtInfo[El] = [Info['Drad']['Color']]
1909                            rbData['rbTypes'][r] = El
1910                            vecGrid.SetCellValue(r,c,El)
1911                    PE.Destroy()
1912
1913            def ChangeCell(event):
1914                r,c =  event.GetRow(),event.GetCol()
1915                if r >= 0 and (0 <= c < 3):
1916                    try:
1917                        val = float(vecGrid.GetCellValue(r,c))
1918                        rbData['rbXYZ'][r][c] = val
1919                    except ValueError:
1920                        pass
1921                       
1922            def RowSelect(event):
1923                r,c =  event.GetRow(),event.GetCol()
1924                if c < 0:                   #only row clicks
1925                    for vecgrid in resList:
1926                        vecgrid.ClearSelection()
1927                    vecGrid.SelectRow(r,True)
1928
1929            def OnRefSel(event):
1930               
1931                Obj = event.GetEventObject()
1932                iref,res,jref = Indx[Obj.GetId()]
1933                sel = Obj.GetValue()
1934                ind = atNames.index(sel)
1935                if rbData['rbTypes'][ind] == 'H':
1936                    G2G.G2MessageBox(G2frame,'You should not select an H-atom for rigid body orientation')
1937                rbData['rbRef'][iref] = ind
1938                FillRefChoice(rbId,rbData)
1939                for i,ref in enumerate(RefObjs[jref]):
1940                    ref.SetItems([atNames[j] for j in refChoice[rbId][i]])
1941                    ref.SetValue(atNames[rbData['rbRef'][i]])                   
1942                rbXYZ = rbData['rbXYZ']
1943                if not iref:     #origin change
1944                    rbXYZ -= rbXYZ[ind]
1945                #TODO - transform all atom XYZ by axis choices
1946                Xxyz = rbXYZ[rbData['rbRef'][1]]
1947                X = Xxyz/np.sqrt(np.sum(Xxyz**2))
1948                Yxyz = rbXYZ[rbData['rbRef'][2]]
1949                Y = Yxyz/np.sqrt(np.sum(Yxyz**2))
1950                Mat = G2mth.getRBTransMat(X,Y)
1951                rbXYZ = np.inner(Mat,rbXYZ).T
1952                rbData['rbXYZ'] = rbXYZ
1953                res.ClearSelection()
1954                resTable = res.GetTable()
1955                for r in range(res.GetNumberRows()):
1956                    row = resTable.GetRowValues(r)
1957                    row[2:4] = rbXYZ[r]
1958                    resTable.SetRowValues(r,row)
1959                res.ForceRefresh()
1960                G2plt.PlotRigidBody(G2frame,'Residue',AtInfo,rbData,plotDefaults)
1961               
1962            Types = 2*[wg.GRID_VALUE_STRING,]+3*[wg.GRID_VALUE_FLOAT+':10,5',]
1963            colLabels = ['Name','Type','Cart x','Cart y','Cart z']
1964            table = []
1965            rowLabels = []
1966            for ivec,xyz in enumerate(rbData['rbXYZ']):
1967                table.append([rbData['atNames'][ivec],]+[rbData['rbTypes'][ivec],]+list(xyz))
1968                rowLabels.append(str(ivec))
1969            vecTable = G2G.Table(table,rowLabels=rowLabels,colLabels=colLabels,types=Types)
1970            vecGrid = G2G.GSGrid(ResidueRBDisplay)
1971            Indx[vecGrid.GetId()] = rbId
1972            resList.append(vecGrid)
1973            vecGrid.SetTable(vecTable, True)
1974            if 'phoenix' in wx.version():
1975                vecGrid.Bind(wg.EVT_GRID_CELL_CHANGED, ChangeCell)
1976            else:
1977                vecGrid.Bind(wg.EVT_GRID_CELL_CHANGE, ChangeCell)
1978            vecGrid.Bind(wg.EVT_GRID_CELL_LEFT_DCLICK, TypeSelect)
1979            vecGrid.Bind(wg.EVT_GRID_LABEL_LEFT_CLICK, RowSelect)
1980            attr = wx.grid.GridCellAttr()
1981            attr.SetEditor(G2G.GridFractionEditor(vecGrid))
1982            for c in range(3):
1983                vecGrid.SetColAttr(c, attr)
1984            for row in range(vecTable.GetNumberRows()):
1985                for col in range(5):
1986                    vecGrid.SetCellStyle(row,col,VERY_LIGHT_GREY,True)
1987#            vecGrid.SetScrollRate(0,0)
1988            vecGrid.AutoSizeColumns(False)
1989            vecSizer = wx.BoxSizer()
1990            vecSizer.Add(vecGrid)
1991           
1992            refAtmSizer = wx.BoxSizer(wx.HORIZONTAL)
1993            atNames = rbData['atNames']
1994            rbRef = rbData['rbRef']
1995            if rbData['rbRef'][3] or rbData['useCount']:
1996                refAtmSizer.Add(wx.StaticText(ResidueRBDisplay,-1,
1997                    'Orientation reference non-H atoms A-B-C: %s, %s, %s'%(atNames[rbRef[0]], \
1998                     atNames[rbRef[1]],atNames[rbRef[2]])),0)
1999            else:
2000                refAtmSizer.Add(wx.StaticText(ResidueRBDisplay,-1,
2001                    'Orientation reference non-H atoms A-B-C: '),0,wx.ALIGN_CENTER_VERTICAL)
2002                refObj = [0,0,0]
2003                for i in range(3):
2004                    choices = [atNames[j] for j in refChoice[rbId][i]]
2005                    refSel = wx.ComboBox(ResidueRBDisplay,-1,value='',
2006                        choices=choices,style=wx.CB_READONLY|wx.CB_DROPDOWN)
2007                    refSel.SetValue(atNames[rbRef[i]])
2008                    refSel.Bind(wx.EVT_COMBOBOX, OnRefSel)
2009                    Indx[refSel.GetId()] = [i,vecGrid,len(RefObjs)]
2010                    refObj[i] = refSel
2011                    refAtmSizer.Add(refSel,0,wx.ALIGN_CENTER_VERTICAL)
2012                RefObjs.append(refObj)
2013           
2014            mainSizer = wx.BoxSizer(wx.VERTICAL)
2015            mainSizer.Add(refAtmSizer)
2016            mainSizer.Add(vecSizer)
2017            return mainSizer
2018           
2019        def SeqSizer(angSlide,rbId,iSeq,Seq,atNames):
2020           
2021            def ChangeAngle(event):
2022                event.Skip()
2023                Obj = event.GetEventObject()
2024                rbId,Seq = Indx[Obj.GetId()][:2]
2025                val = Seq[2]
2026                try:
2027                    val = float(Obj.GetValue())
2028                    Seq[2] = val
2029                except ValueError:
2030                    pass
2031                Obj.SetValue('%8.2f'%(val))
2032                G2plt.PlotRigidBody(G2frame,'Residue',AtInfo,data['Residue'][rbId],plotDefaults)
2033               
2034            def OnRadBtn(event):
2035                Obj = event.GetEventObject()
2036                Seq,iSeq,angId = Indx[Obj.GetId()]
2037                data['Residue'][rbId]['SelSeq'] = [iSeq,angId]
2038                angSlide.SetValue(int(100*Seq[2]))
2039               
2040            def OnDelBtn(event):
2041                Obj = event.GetEventObject()
2042                rbId,Seq = Indx[Obj.GetId()]
2043                data['Residue'][rbId]['rbSeq'].remove(Seq)       
2044                wx.CallAfter(UpdateResidueRB)
2045           
2046            seqSizer = wx.FlexGridSizer(0,5,2,2)
2047            seqSizer.AddGrowableCol(3,0)
2048            iBeg,iFin,angle,iMove = Seq
2049            ang = wx.TextCtrl(ResidueRBDisplay,-1,'%8.2f'%(angle),size=(50,20))
2050            if not iSeq:
2051                radBt = wx.RadioButton(ResidueRBDisplay,-1,'',style=wx.RB_GROUP)
2052                data['Residue'][rbId]['SelSeq'] = [iSeq,ang.GetId()]
2053            else:
2054                radBt = wx.RadioButton(ResidueRBDisplay,-1,'')
2055            radBt.Bind(wx.EVT_RADIOBUTTON,OnRadBtn)                   
2056            seqSizer.Add(radBt)
2057            delBt = wx.RadioButton(ResidueRBDisplay,-1,'')
2058            delBt.Bind(wx.EVT_RADIOBUTTON,OnDelBtn)
2059            seqSizer.Add(delBt)
2060            bond = wx.TextCtrl(ResidueRBDisplay,-1,'%s %s'%(atNames[iBeg],atNames[iFin]),size=(50,20))
2061            seqSizer.Add(bond,0,wx.ALIGN_CENTER_VERTICAL)
2062            Indx[radBt.GetId()] = [Seq,iSeq,ang.GetId()]
2063            Indx[delBt.GetId()] = [rbId,Seq]
2064            Indx[ang.GetId()] = [rbId,Seq,ang]
2065            ang.Bind(wx.EVT_TEXT_ENTER,ChangeAngle)
2066            ang.Bind(wx.EVT_KILL_FOCUS,ChangeAngle)
2067            seqSizer.Add(ang,0,wx.ALIGN_CENTER_VERTICAL)
2068            atms = ''
2069            for i in iMove:   
2070                atms += ' %s,'%(atNames[i])
2071            moves = wx.TextCtrl(ResidueRBDisplay,-1,atms[:-1],size=(200,20))
2072            seqSizer.Add(moves,1,wx.ALIGN_CENTER_VERTICAL|wx.EXPAND|wx.RIGHT)
2073            return seqSizer
2074           
2075        def SlideSizer():
2076           
2077            def OnSlider(event):
2078                Obj = event.GetEventObject()
2079                rbData = Indx[Obj.GetId()]
2080                iSeq,angId = rbData['SelSeq']
2081                val = float(Obj.GetValue())/100.
2082                rbData['rbSeq'][iSeq][2] = val
2083                Indx[angId][2].SetValue('%8.2f'%(val))
2084                G2plt.PlotRigidBody(G2frame,'Residue',AtInfo,rbData,plotDefaults)
2085           
2086            slideSizer = wx.BoxSizer(wx.HORIZONTAL)
2087            slideSizer.Add(wx.StaticText(ResidueRBDisplay,-1,'Selected torsion angle:'),0)
2088            iSeq,angId = rbData['SelSeq']
2089            angSlide = wx.Slider(ResidueRBDisplay,-1,
2090                int(100*rbData['rbSeq'][iSeq][2]),0,36000,size=(200,20),
2091                style=wx.SL_HORIZONTAL)
2092            angSlide.Bind(wx.EVT_SLIDER, OnSlider)
2093            Indx[angSlide.GetId()] = rbData
2094            slideSizer.Add(angSlide,0)           
2095            return slideSizer,angSlide
2096           
2097        def FillRefChoice(rbId,rbData):
2098            choiceIds = [i for i in range(len(rbData['atNames']))]
2099            for seq in rbData['rbSeq']:
2100                for i in seq[3]:
2101                    try:
2102                        choiceIds.remove(i)
2103                    except ValueError:
2104                        pass
2105            rbRef = rbData['rbRef']
2106            for i in range(3):
2107                try:
2108                    choiceIds.remove(rbRef[i])
2109                except ValueError:
2110                    pass
2111            refChoice[rbId] = [choiceIds[:],choiceIds[:],choiceIds[:]]
2112            for i in range(3):
2113                refChoice[rbId][i].append(rbRef[i])
2114                refChoice[rbId][i].sort()     
2115           
2116        ResidueRBDisplay.DestroyChildren()
2117        ResidueRBSizer = wx.BoxSizer(wx.VERTICAL)
2118        for rbId in data['RBIds']['Residue']:
2119            rbData = data['Residue'][rbId]
2120            if len(rbData['rbXYZ']) < 3:    #patch - skip around bad RBs with too few atoms
2121                continue
2122            FillRefChoice(rbId,rbData)
2123            ResidueRBSizer.Add(rbNameSizer(rbId,rbData),0)
2124            ResidueRBSizer.Add(rbResidues(rbId,rbData),0)
2125            ResidueRBSizer.Add((5,5),0)
2126            if rbData['rbSeq']:
2127                slideSizer,angSlide = SlideSizer()
2128            if len(rbData['rbSeq']):
2129                ResidueRBSizer.Add(wx.StaticText(ResidueRBDisplay,-1,
2130                    'Sel  Del  Bond             Angle      Riding atoms'),
2131                    0,wx.ALIGN_CENTER_VERTICAL)                       
2132            for iSeq,Seq in enumerate(rbData['rbSeq']):
2133                ResidueRBSizer.Add(SeqSizer(angSlide,rbId,iSeq,Seq,rbData['atNames']))
2134            if rbData['rbSeq']:
2135                ResidueRBSizer.Add(slideSizer,)
2136            ResidueRBSizer.Add(wx.StaticText(ResidueRBDisplay,-1,70*'-'))
2137
2138        ResidueRBSizer.Add((5,25),)
2139        ResidueRBSizer.Layout()   
2140        ResidueRBDisplay.SetSizer(ResidueRBSizer,True)
2141        Size = ResidueRBSizer.GetMinSize()
2142        Size[0] += 40
2143        Size[1] = max(Size[1],450) + 20
2144        ResidueRBDisplay.SetSize(Size)
2145        ResidueRB.SetScrollbars(10,10,Size[0]/10-4,Size[1]/10-1)
2146       
2147    def SetStatusLine(text):
2148        G2frame.GetStatusBar().SetStatusText(text,1)                                     
2149
2150    G2gd.SetDataMenuBar(G2frame,G2frame.dataWindow.RigidBodyMenu)
2151    G2frame.SetTitle('Rigid bodies')
2152    SetStatusLine('')
2153    UpdateVectorRB()
2154    G2frame.rbBook.Bind(wx.aui.EVT_AUINOTEBOOK_PAGE_CHANGED, OnPageChanged)
2155    wx.CallAfter(OnPageChanged,None)
Note: See TracBrowser for help on using the repository browser.