source: trunk/GSASIIconstrGUI.py @ 3406

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

fix Transform to create correct(?) constraints between lattices, atom positions & anisotropic thermal parameters.
Checked for axes permutations in orthorhombic.

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