source: trunk/GSASIIconstrGUI.py @ 3438

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

g2strIO
fix magnetic hkl extinctions in reflection generation
remove Uniq & Phi - never used
change all config 'debug' out put to have 'DBG' in message - many places
fix phoenix decode problem for Comments
Display transformation math on Transform dialog box & labels on matrix & vectors
use a general exception in getSelection (G2plot line 7141) to avoid crashes

  • Property svn:eol-style set to native
  • Property svn:keywords set to Date Author Revision URL Id
File size: 95.8 KB
Line 
1# -*- coding: utf-8 -*-
2#GSASIIconstrGUI - constraint GUI routines
3########### SVN repository information ###################
4# $Date: 2018-06-18 16:35:30 +0000 (Mon, 18 Jun 2018) $
5# $Author: vondreele $
6# $Revision: 3438 $
7# $URL: trunk/GSASIIconstrGUI.py $
8# $Id: GSASIIconstrGUI.py 3438 2018-06-18 16:35:30Z 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: 3438 $")
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(FindEquivVarb(FrstVarb,[i for i in varList if i not in omitVars]))
746            newcons = SelectVarbs(page,VarObj,moreVarb,title2+FrstVarb,constType)
747            if len(newcons) > 0:
748                if CheckAddedConstraint(newcons):
749                    data[constrDictEnt] += newcons
750        dlg.Destroy()
751        wx.CallAfter(OnPageChanged,None)
752                       
753    def FindNeighbors(phase,FrstName,AtNames):
754        General = phase['General']
755        cx,ct,cs,cia = General['AtomPtrs']
756        Atoms = phase['Atoms']
757        atNames = [atom[ct-1] for atom in Atoms]
758        Cell = General['Cell'][1:7]
759        Amat,Bmat = G2lat.cell2AB(Cell)
760        atTypes = General['AtomTypes']
761        Radii = np.array(General['BondRadii'])
762        AtInfo = dict(zip(atTypes,Radii)) #or General['BondRadii']
763        Orig = atNames.index(FrstName.split()[1])
764        OType = Atoms[Orig][ct]
765        XYZ = G2mth.getAtomXYZ(Atoms,cx)       
766        Neigh = []
767        Dx = np.inner(Amat,XYZ-XYZ[Orig]).T
768        dist = np.sqrt(np.sum(Dx**2,axis=1))
769        sumR = AtInfo[OType]+0.5    #H-atoms only!
770        IndB = ma.nonzero(ma.masked_greater(dist-0.85*sumR,0.))
771        for j in IndB[0]:
772            if j != Orig:
773                Neigh.append(AtNames[j])
774        return Neigh
775       
776    def GetAddAtomVars(page,title1,title2,varList,constrDictEnt,constType):
777        '''Get the atom variables to be added for OnAddAtomEquiv. Then create and
778        check the constraints. Riding for H atoms only.
779        '''
780        Atoms = {G2obj.VarDescr(i)[0]:[] for i in varList if 'Atom' in G2obj.VarDescr(i)[0]}
781        for item in varList:
782            atName = G2obj.VarDescr(item)[0]
783            if atName in Atoms:
784                Atoms[atName].append(item)
785        AtNames = list(Atoms.keys())
786        AtNames.sort()
787        dlg = G2G.G2SingleChoiceDialog(G2frame,'Select 1st atom:',
788            title1,AtNames,monoFont=True,size=(625,400))
789        dlg.CenterOnParent()
790        FrstAtom = ''
791        if dlg.ShowModal() == wx.ID_OK:
792            sel = dlg.GetSelection()
793            FrstAtom = AtNames[sel]
794            if 'riding' in constType:
795                phaseName = (FrstAtom.split(' in ')[1]).strip()
796                phase = Phases[phaseName]
797                AtNames = FindNeighbors(phase,FrstAtom,AtNames)
798            else:
799                AtNames.remove(FrstAtom)
800        dlg.Destroy()
801        if FrstAtom == '':
802            print ('no atom selected')
803            return
804        dlg = G2G.G2MultiChoiceDialog(
805            G2frame,title2+FrstAtom,
806            'Constrain '+str(FrstAtom)+' with...',AtNames,
807            toggle=False,size=(625,400),monoFont=True)
808        if dlg.ShowModal() == wx.ID_OK:
809            Selections = dlg.GetSelections()[:]
810        else:
811            print ('no target atom selected')
812            dlg.Destroy()
813            return
814        dlg.Destroy()
815        for name in Atoms[FrstAtom]:
816            newcons = []
817            constr = []
818            if 'riding' in constType:
819                if 'AUiso' in name:
820                    constr = [[1.0,G2obj.G2VarObj(name)]]
821                elif 'AU11' in name:
822                    pass
823                elif 'AU' not in name:
824                    constr = [[1.0,G2obj.G2VarObj(name)]]
825            else:
826                constr = [[1.0,G2obj.G2VarObj(name)]]
827            pref = ':'+name.rsplit(':',1)[0].split(':',1)[1]    #get stuff between phase id & atom id
828            for sel in Selections:
829                name2 = Atoms[AtNames[sel]][0]
830                pid = name2.split(':',1)[0]                     #get phase id for 2nd atom
831                id = name2.rsplit(':',1)[-1]                    #get atom no. for 2nd atom
832                if 'riding' in constType:
833                    pref = pid+pref
834                    if 'AUiso' in pref:
835                        parts = pref.split('AUiso')
836                        constr += [[1.2,G2obj.G2VarObj('%s:%s'%(parts[0]+'AUiso',id))]]
837                    elif 'AU' not in pref:
838                        constr += [[1.0,G2obj.G2VarObj('%s:%s'%(pref,id))]]
839                else:
840                    constr += [[1.0,G2obj.G2VarObj('%s:%s'%(pid+pref,id))]]
841            if not constr:
842                continue
843            if 'frac' in pref and 'riding' not in constType:
844                newcons = [constr+[1.0,None,'c']]
845            else:
846                newcons = [constr+[None,None,'e']]
847            if len(newcons) > 0:
848                if CheckAddedConstraint(newcons):
849                    data[constrDictEnt] += newcons
850        wx.CallAfter(OnPageChanged,None)
851                       
852    def MakeConstraintsSizer(name,pageDisplay):
853        '''Creates a sizer displaying all of the constraints entered of
854        the specified type.
855
856        :param str name: the type of constraints to be displayed ('HAP',
857          'Hist', 'Phase', 'Global', 'Sym-Generated')
858        :param wx.Panel pageDisplay: parent panel for sizer
859        :returns: wx.Sizer created by method
860        '''
861        if name == 'Sym-Generated':         #show symmetry generated constraints
862            Sizer1 =  wx.BoxSizer(wx.VERTICAL)
863            Sizer1.Add(wx.StaticText(pageDisplay,wx.ID_ANY,
864                                    'Equivalences generated based on cell/space group input'))
865            Sizer1.Add((-1,5))
866            Sizer = wx.FlexGridSizer(0,2,0,0)
867            Sizer1.Add(Sizer)
868            for sym in G2mv.GetSymEquiv():
869                Sizer.Add(wx.StaticText(pageDisplay,wx.ID_ANY,'EQUIV'),
870                           0,wx.ALIGN_CENTER_VERTICAL|wx.ALIGN_CENTER|wx.RIGHT|wx.LEFT,3)
871                Sizer.Add(wx.StaticText(pageDisplay,wx.ID_ANY,sym))
872                Sizer.Add((-1,-1))
873                Sizer.Add((-1,2))
874            return Sizer1
875        constSizer = wx.FlexGridSizer(0,6,0,0)
876        maxlen = 50 # characters before wrapping a constraint
877        for Id,item in enumerate(data[name]):
878            refineflag = False
879            helptext = ""
880            eqString = ['',]
881            problemItem = False
882            for term in item[:-3]:
883                if str(term[1]) in G2mv.problemVars:
884                    problemItem = True
885            if item[-1] == 'h': # Hold on variable
886                constSizer.Add((-1,-1),0)              # blank space for edit button
887                typeString = 'FIXED'
888                var = str(item[0][1])
889                varMean = G2obj.fmtVarDescr(var)
890                eqString[-1] =  var +'   '
891                helptext = "Prevents variable:\n"+ var + " ("+ varMean + ")\nfrom being changed"
892            elif isinstance(item[-1],str): # not true on original-style (2011?) constraints
893                constEdit = wx.Button(pageDisplay,wx.ID_ANY,'Edit',style=wx.BU_EXACTFIT)
894                constEdit.Bind(wx.EVT_BUTTON,OnConstEdit)
895                constSizer.Add(constEdit,0,wx.LEFT|wx.RIGHT|wx.ALIGN_CENTER_VERTICAL|wx.ALIGN_CENTER,1)            # edit button
896                Indx[constEdit.GetId()] = [Id,name]
897                if item[-1] == 'f':
898                    helptext = "A new variable"
899                    if item[-3]:
900                        helptext += " named "+str(item[-3])
901                    helptext += " is created from a linear combination of the following variables:\n"
902                    for term in item[:-3]:
903                        var = str(term[1])
904                        if len(eqString[-1]) > maxlen:
905                            eqString.append(' ')
906                        m = term[0]
907                        if eqString[-1] != '':
908                            if m >= 0:
909                                eqString[-1] += ' + '
910                            else:
911                                eqString[-1] += ' - '
912                                m = abs(m)
913                        eqString[-1] += '%.3f*%s '%(m,var)
914                        varMean = G2obj.fmtVarDescr(var)
915                        helptext += "\n" + var + " ("+ varMean + ")"
916                    if '_Explain' in data:
917                        if data['_Explain'].get(item[-3]):
918                            helptext += '\n\n'
919                            helptext += data['_Explain'][item[-3]]
920                    # typeString = 'NEWVAR'
921                    # if item[-3]:
922                    #     eqString[-1] += ' = '+item[-3]
923                    # else:
924                    #     eqString[-1] += ' = New Variable'
925                    if item[-3]:
926                        typeString = item[-3] + ' = '
927                    else:
928                        typeString = 'New Variable = '
929                    #print 'refine',item[-2]
930                    refineflag = True
931                elif item[-1] == 'c':
932                    helptext = "The following variables constrained to equal a constant:"
933                    for term in item[:-3]:
934                        var = str(term[1])
935                        if len(eqString[-1]) > maxlen:
936                            eqString.append(' ')
937                        if eqString[-1] != '':
938                            if term[0] > 0:
939                                eqString[-1] += ' + '
940                            else:
941                                eqString[-1] += ' - '
942                        eqString[-1] += '%.3f*%s '%(abs(term[0]),var)
943                        varMean = G2obj.fmtVarDescr(var)
944                        helptext += "\n" + var + " ("+ varMean + ")"
945                    typeString = 'CONST'
946                    eqString[-1] += ' = '+str(item[-3])
947                elif item[-1] == 'e':
948                    helptext = "The following variables are set to be equivalent, noting multipliers:"
949                    for term in item[:-3]:
950                        var = str(term[1])
951                        if term[0] == 0: term[0] = 1.0
952                        if len(eqString[-1]) > maxlen:
953                            eqString.append(' ')
954                        if eqString[-1] == '':
955                            eqString[-1] += var+' '
956                            first = term[0]
957                        else:
958                            eqString[-1] += ' = %.3f*%s '%(first/term[0],var)
959                        varMean = G2obj.fmtVarDescr(var)
960                        helptext += "\n" + var + " ("+ varMean + ")"
961                    typeString = 'EQUIV'
962                else:
963                    print ('Unexpected constraint'+item)
964               
965            else:
966                print ('Removing old-style constraints')
967                data[name] = []
968                return constSizer
969            constDel = wx.Button(pageDisplay,wx.ID_ANY,'Delete',style=wx.BU_EXACTFIT)
970            constDel.Bind(wx.EVT_BUTTON,OnConstDel)
971            Indx[constDel.GetId()] = [Id,name]
972            constSizer.Add(constDel,0,wx.LEFT|wx.RIGHT|wx.ALIGN_CENTER_VERTICAL|wx.ALIGN_CENTER,1)             # delete button
973            if helptext:
974                ch = G2G.HelpButton(pageDisplay,helptext)
975                constSizer.Add(ch,0,wx.LEFT|wx.RIGHT|wx.ALIGN_CENTER_VERTICAL|wx.ALIGN_CENTER,1)
976            else:
977                constSizer.Add((-1,-1))
978            if refineflag:
979                ch = G2G.G2CheckBox(pageDisplay,'',item,-2)
980                constSizer.Add(ch,0,wx.LEFT|wx.RIGHT|wx.ALIGN_CENTER_VERTICAL|wx.ALIGN_CENTER,1)
981            else:
982                constSizer.Add((-1,-1))               
983            constSizer.Add(wx.StaticText(pageDisplay,wx.ID_ANY,typeString),
984                           0,wx.ALIGN_CENTER_VERTICAL|wx.ALIGN_CENTER|wx.RIGHT|wx.LEFT,3)
985            if problemItem: eqString[-1] += ' -- Conflict: see console'
986            if len(eqString) > 1:
987                Eq = wx.BoxSizer(wx.VERTICAL)
988                for s in eqString:
989                    line = wx.StaticText(pageDisplay,wx.ID_ANY,s)
990                    if problemItem: line.SetBackgroundColour(wx.YELLOW)
991                    Eq.Add(line,0,wx.ALIGN_CENTER_VERTICAL)
992                Eq.Add((-1,4))
993            else:
994                Eq = wx.StaticText(pageDisplay,wx.ID_ANY,eqString[0])
995                if problemItem: Eq.SetBackgroundColour(wx.YELLOW)
996            constSizer.Add(Eq,1,wx.ALIGN_CENTER_VERTICAL)
997        return constSizer
998               
999    def OnConstDel(event):
1000        'Delete a constraint'
1001        Obj = event.GetEventObject()
1002        Id,name = Indx[Obj.GetId()]
1003        del(data[name][Id])
1004        wx.CallAfter(OnPageChanged,None)
1005       
1006    def OnConstEdit(event):
1007        '''Called to edit an individual contraint in response to a
1008        click on its Edit button
1009        '''
1010        Obj = event.GetEventObject()
1011        Id,name = Indx[Obj.GetId()]
1012        if data[name][Id][-1] == 'f':
1013            items = data[name][Id][:-3]
1014            constType = 'New Variable'
1015            if data[name][Id][-3]:
1016                varname = data[name][Id][-3]
1017            else:
1018                varname = ""
1019            lbl = 'Enter value for each term in constraint; sum = new variable'
1020            dlg = ConstraintDialog(G2frame,constType,lbl,items,
1021                                   varname=varname,varyflag=data[name][Id][-2])
1022        elif data[name][Id][-1] == 'c':
1023            items = data[name][Id][:-3]+[
1024                [data[name][Id][-3],'fixed value =']]
1025            constType = 'Constraint'
1026            lbl = 'Edit value for each term in constant constraint sum'
1027            dlg = ConstraintDialog(G2frame,constType,lbl,items)
1028        elif data[name][Id][-1] == 'e':
1029            items = data[name][Id][:-3]
1030            constType = 'Equivalence'
1031            lbl = 'The following terms are set to be equal:'
1032            dlg = ConstraintDialog(G2frame,constType,lbl,items,'/')
1033        else:
1034            return
1035        try:
1036            prev = data[name][Id][:]
1037            if dlg.ShowModal() == wx.ID_OK:
1038                result = dlg.GetData()
1039                for i in range(len(data[name][Id][:-3])):
1040                    if type(data[name][Id][i]) is tuple: # fix non-mutable construct
1041                        data[name][Id][i] = list(data[name][Id][i])
1042                    data[name][Id][i][0] = result[i][0]
1043                if data[name][Id][-1] == 'c':
1044                    data[name][Id][-3] = str(result[-1][0])
1045                elif data[name][Id][-1] == 'f':
1046                    # process the variable name to put in global form (::var)
1047                    varname = str(dlg.newvar[0]).strip().replace(' ','_')
1048                    if varname.startswith('::'):
1049                        varname = varname[2:]
1050                    varname = varname.replace(':',';')
1051                    if varname:
1052                        data[name][Id][-3] = varname
1053                    else:
1054                        data[name][Id][-3] = ''
1055                    data[name][Id][-2] = dlg.newvar[1]
1056                if not CheckChangedConstraint():
1057                    data[name][Id] = prev
1058        except:
1059            import traceback
1060            print (traceback.format_exc())
1061        finally:
1062            dlg.Destroy()
1063        wx.CallAfter(OnPageChanged,None)
1064   
1065    def UpdateConstraintPanel(panel,typ):
1066        '''Update the contents of the selected Constraint
1067        notebook tab. Called in :func:`OnPageChanged`
1068        '''
1069        if panel.GetSizer(): panel.GetSizer().Clear(True)
1070        Siz = wx.BoxSizer(wx.VERTICAL)
1071        Siz.Add((5,5),0)
1072        Siz.Add(MakeConstraintsSizer(typ,panel),1,wx.EXPAND)
1073        panel.SetSizer(Siz,True)
1074        Size = Siz.GetMinSize()
1075        Size[0] += 40
1076        Size[1] = max(Size[1],450) + 20
1077        panel.SetSize(Size)
1078        panel.SetScrollbars(10,10,Size[0]/10-4,Size[1]/10-1)
1079        panel.Show()
1080
1081    def OnPageChanged(event):
1082        '''Called when a tab is pressed or when a "select tab" menu button is
1083        used (see RaisePage), or to refresh the current tab contents (event=None)
1084        '''
1085        if event:       #page change event!
1086            page = event.GetSelection()
1087        else: # called directly, get current page
1088            page = G2frame.constr.GetSelection()
1089        G2frame.constr.ChangeSelection(page)
1090        text = G2frame.constr.GetPageText(page)
1091        G2frame.dataWindow.ConstraintEdit.Enable(G2G.wxID_EQUIVALANCEATOMS,False)
1092#        G2frame.dataWindow.ConstraintEdit.Enable(G2G.wxID_ADDRIDING,False)
1093        if text == 'Histogram/Phase':
1094            enableEditCons = [False]+4*[True]
1095            G2frame.Page = [page,'hap']
1096            UpdateConstraintPanel(HAPConstr,'HAP')
1097        elif text == 'Histogram':
1098            enableEditCons = [False]+4*[True]
1099            G2frame.Page = [page,'hst']
1100            UpdateConstraintPanel(HistConstr,'Hist')
1101        elif text == 'Phase':
1102            enableEditCons = 5*[True]
1103            G2frame.Page = [page,'phs']
1104            G2frame.dataWindow.ConstraintEdit.Enable(G2G.wxID_EQUIVALANCEATOMS,True)
1105#            G2frame.dataWindow.ConstraintEdit.Enable(G2G.wxID_ADDRIDING,True)
1106            if 'DELETED' in str(PhaseConstr):   #seems to be no other way to do this (wx bug)
1107                if GSASIIpath.GetConfigValue('debug'):
1108                    print ('DBG_wx error: PhaseConstr not cleanly deleted after Refine')
1109                return
1110            UpdateConstraintPanel(PhaseConstr,'Phase')
1111        elif text == 'Global':
1112            enableEditCons = [False]+4*[True]
1113            G2frame.Page = [page,'glb']
1114            UpdateConstraintPanel(GlobalConstr,'Global')
1115        else:
1116            enableEditCons = 5*[False]
1117            G2frame.Page = [page,'sym']
1118            UpdateConstraintPanel(SymConstr,'Sym-Generated')
1119        # remove menu items when not allowed
1120        for obj,flag in zip(G2frame.dataWindow.ConstraintEdit.GetMenuItems(),enableEditCons): 
1121            obj.Enable(flag)
1122        G2frame.dataWindow.SetDataSize()
1123
1124    def RaisePage(event):
1125        'Respond to a "select tab" menu button'
1126        try:
1127            i = (G2G.wxID_CONSPHASE,
1128                 G2G.wxID_CONSHAP,
1129                 G2G.wxID_CONSHIST,
1130                 G2G.wxID_CONSGLOBAL,
1131                 G2G.wxID_CONSSYM,
1132                ).index(event.GetId())
1133            G2frame.constr.SetSelection(i)
1134            wx.CallAfter(OnPageChanged,None)
1135        except ValueError:
1136            print('Unexpected event in RaisePage')
1137
1138    def SetStatusLine(text):
1139        G2frame.GetStatusBar().SetStatusText(text,1)                                     
1140       
1141    G2gd.SetDataMenuBar(G2frame,G2frame.dataWindow.ConstraintMenu)
1142    SetStatusLine('')
1143   
1144    G2frame.Bind(wx.EVT_MENU, OnAddConstraint, id=G2G.wxID_CONSTRAINTADD)
1145    G2frame.Bind(wx.EVT_MENU, OnAddFunction, id=G2G.wxID_FUNCTADD)
1146    G2frame.Bind(wx.EVT_MENU, OnAddEquivalence, id=G2G.wxID_EQUIVADD)
1147    G2frame.Bind(wx.EVT_MENU, OnAddHold, id=G2G.wxID_HOLDADD)
1148    G2frame.Bind(wx.EVT_MENU, OnAddAtomEquiv, id=G2G.wxID_EQUIVALANCEATOMS)
1149#    G2frame.Bind(wx.EVT_MENU, OnAddRiding, id=G2G.wxID_ADDRIDING)
1150    # tab commands
1151    for id in (G2G.wxID_CONSPHASE,
1152               G2G.wxID_CONSHAP,
1153               G2G.wxID_CONSHIST,
1154               G2G.wxID_CONSGLOBAL,
1155               G2G.wxID_CONSSYM,
1156               ):
1157        G2frame.Bind(wx.EVT_MENU, RaisePage,id=id)
1158
1159    #G2frame.constr = G2G.GSNoteBook(parent=G2frame.dataWindow,size=G2frame.dataWindow.GetClientSize())
1160    G2frame.constr = G2G.GSNoteBook(parent=G2frame.dataWindow)
1161    G2frame.dataWindow.GetSizer().Add(G2frame.constr,1,wx.ALL|wx.EXPAND)
1162    # note that order of pages is hard-coded in RaisePage
1163    PhaseConstr = wx.ScrolledWindow(G2frame.constr)
1164    G2frame.constr.AddPage(PhaseConstr,'Phase')
1165    HAPConstr = wx.ScrolledWindow(G2frame.constr)
1166    G2frame.constr.AddPage(HAPConstr,'Histogram/Phase')
1167    HistConstr = wx.ScrolledWindow(G2frame.constr)
1168    G2frame.constr.AddPage(HistConstr,'Histogram')
1169    GlobalConstr = wx.ScrolledWindow(G2frame.constr)
1170    G2frame.constr.AddPage(GlobalConstr,'Global')
1171    SymConstr = wx.ScrolledWindow(G2frame.constr)
1172    G2frame.constr.AddPage(SymConstr,'Sym-Generated')
1173    wx.CallAfter(OnPageChanged,None)
1174    G2frame.constr.Bind(wx.aui.EVT_AUINOTEBOOK_PAGE_CHANGED, OnPageChanged)
1175    # validate all the constrants -- should not see any errors here normally
1176    allcons = []
1177    for key in data:
1178        if key.startswith('_'): continue
1179        allcons += data[key]
1180    if not len(allcons): return
1181    errmsg,warnmsg = CheckConstraints(allcons)
1182    if errmsg:
1183        G2frame.ErrorDialog('Constraint Error',
1184                            'Error in constraints:\n'+errmsg+'\nCheck console output for more information',
1185                            parent=G2frame)
1186        print (errmsg)
1187        print (G2mv.VarRemapShow([],True))
1188    elif warnmsg:
1189        print ('Unexpected contraint warning:\n'+warnmsg)
1190
1191################################################################################
1192#### Make nuclear/magnetic phase transition constraints - called by OnTransform in G2phsGUI
1193################################################################################       
1194       
1195def TransConstraints(G2frame,oldPhase,newPhase,Trans,Vec,atCodes):
1196    '''Add constraints for new magnetic phase created via transformation of old
1197    nuclear one
1198    NB: A = [G11,G22,G33,2*G12,2*G13,2*G23]
1199    '''
1200   
1201    def SetUniqAj(pId,Aname,SGLaue):
1202        if SGLaue in ['4/m','4/mmm'] and iA in [0,1]:
1203            parm = '%d::%s'%(pId,'A0')
1204        elif SGLaue in ['m3','m3m'] and iA in [0,1,2]:
1205            parm = '%d::%s'%(pId,'A0')
1206        elif SGLaue in ['6/m','6/mmm','3m1', '31m', '3'] and iA in [0,1,3]:
1207            parm = '%d::%s'%(pId,'A0')
1208        elif SGLaue in ['3R', '3mR']:
1209            if ia in [0,1,2]:
1210                parm = '%d::%s'%(pId,'A0')
1211            else:
1212                parm = '%d::%s'%(pId,'A3')
1213        else:
1214            parm = '%d::%s'%(pId,Aname)
1215        return parm
1216       
1217    Histograms,Phases = G2frame.GetUsedHistogramsAndPhasesfromTree()
1218    UseList = newPhase['Histograms']
1219    detTrans = np.abs(nl.det(Trans))
1220    invTrans = nl.inv(Trans)
1221#    print 'invTrans',invTrans
1222    nAcof = G2lat.cell2A(newPhase['General']['Cell'][1:7])
1223   
1224    opId = oldPhase['pId']
1225    npId = newPhase['pId']
1226    cx,ct,cs,cia = newPhase['General']['AtomPtrs']
1227    nAtoms = newPhase['Atoms']
1228    oSGData = oldPhase['General']['SGData']
1229    nSGData = newPhase['General']['SGData']
1230    oAcof = G2lat.cell2A(oldPhase['General']['Cell'][1:7])
1231    nAcof = G2lat.cell2A(newPhase['General']['Cell'][1:7])
1232    item = G2gd.GetGPXtreeItemId(G2frame,G2frame.root,'Constraints')
1233    if not item:
1234        return
1235    constraints = G2frame.GPXtree.GetItemPyData(item)
1236    parmDict = {}
1237    varyList = []
1238    xnames = ['dAx','dAy','dAz']
1239    unames = [['AU11','AU12','AU13'],['AU12','AU22','AU23'],['AU13','AU23','AU33']]
1240#    Us = ['U11','U22','U33','U12','U13','U23']
1241    Uids = [[0,0,'AU11'],[1,1,'AU22'],[2,2,'AU33'],[0,1,'AU12'],[0,2,'AU13'],[1,2,'AU23']]
1242    for ia,code in enumerate(atCodes):
1243        atom = nAtoms[ia]
1244        siteSym = G2spc.SytSym(atom[cx:cx+3],nSGData)[0]
1245        CSX = G2spc.GetCSxinel(siteSym)
1246        CSU = G2spc.GetCSuinel(siteSym)
1247        item = code.split('+')[0]
1248        iat,opr = item.split(':')
1249        Nop = abs(int(opr))%100-1
1250        if '-' in opr:
1251            Nop *= -1
1252        Opr = oldPhase['General']['SGData']['SGOps'][abs(Nop)][0]
1253        if Nop < 0:         #inversion
1254            Opr *= -1
1255        XOpr = np.inner(Opr,invTrans)
1256#        for ix,name in enumerate(xnames):
1257        for ix in list(set(CSX[0])):
1258            if not ix:
1259                continue
1260            name = xnames[ix-1]
1261            IndpCon = [1.0,G2obj.G2VarObj('%d::%s:%d'%(npId,name,ia))]
1262            DepCons = []
1263            for iop,opval in enumerate(XOpr[ix-1]):
1264                if opval:
1265                    DepCons.append([opval,G2obj.G2VarObj('%d::%s:%s'%(opId,xnames[iop],iat))])
1266            if len(DepCons) == 1:
1267                constraints['Phase'].append([IndpCon,DepCons[0],None,None,'e'])
1268            elif len(DepCons) > 1:
1269                for Dep in DepCons:
1270                    Dep[0] *= -1
1271                constraints['Phase'].append([IndpCon]+DepCons+[0.0,None,'c'])
1272        for name in ['Afrac','AUiso']:
1273            IndpCon = [1.0,G2obj.G2VarObj('%d::%s:%d'%(npId,name,ia))]
1274            DepCons = [1.0,G2obj.G2VarObj('%d::%s:%s'%(opId,name,iat))]
1275            constraints['Phase'].append([IndpCon,DepCons,None,None,'e'])
1276#        for iu,Uid in enumerate(Uids):
1277        for iu in list(set(CSU[0])):
1278            if not iu:
1279                continue
1280            Uid = Uids[iu-1]
1281            IndpCon = [1.0,G2obj.G2VarObj('%d::%s:%s'%(npId,Uid[2],ia))]
1282            DepCons = []
1283            for iau in range(3):
1284                for ibu in range(3):
1285                    if abs(Trans[Uid[0],iau]) > 1.e-4 and abs(Trans[Uid[1],ibu]) > 1.e-4:
1286                        parm = '%d::%s:%s'%(opId,unames[ibu][iau],iat)
1287                        if not parm in varyList:
1288                            varyList.append(parm)
1289                        DepCons.append([Trans[ibu,iau]/detTrans,G2obj.G2VarObj(parm)])
1290            if len(DepCons) == 1:
1291                constraints['Phase'].append([IndpCon,DepCons[0],None,None,'e'])
1292            elif len(DepCons) > 1:       
1293                for Dep in DepCons:
1294                    Dep[0] *= -1
1295                constraints['Phase'].append([IndpCon]+DepCons+[0.0,None,'c'])
1296           
1297        #how do I do Uij's for most Trans?
1298    Anames = [['A0','A3','A4'],['A3','A1','A5'],['A4','A5','A2']]
1299    As = ['A0','A1','A2','A3','A4','A5']
1300    Aids = [[0,0,'A0'],[1,1,'A1'],[2,2,'A2'],[0,1,'A3'],[0,2,'A4'],[1,2,'A5']]
1301    Axes = ['a','b','c']
1302    Holds = []
1303    for iA,Aid in enumerate(Aids):
1304        parm = SetUniqAj(npId,Aid[2],nSGData['SGLaue'])
1305        parmDict[parm] = nAcof[iA]
1306        varyList.append(parm)
1307        IndpCon = [1.0,G2obj.G2VarObj(parm)]
1308        DepCons = []
1309        for iat in range(3):
1310            if nSGData['SGLaue'] in ['-1','2/m']:       #set holds
1311                if (abs(nAcof[iA]) < 1.e-8) and (abs(Trans[Aid[0],Aid[1]]) < 1.e-8):
1312                    if Axes[iat] != nSGData['SGUniq'] and nSGData['SGLaue'] != oSGData['SGLaue']:
1313                        HoldObj = G2obj.G2VarObj('%d::%s'%(npId,Aid[2]))
1314                        if not HoldObj in Holds: 
1315                            constraints['Phase'].append([[0.0,HoldObj],None,None,'h'])
1316                            Holds.append(HoldObj)
1317                            continue
1318            for ibt in range(3):
1319                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:
1320                    parm = SetUniqAj(opId,Anames[ibt][iat],nSGData['SGLaue'])
1321                    parmDict[parm] = oAcof[As.index(Anames[ibt][iat])]
1322                    if not parm in varyList:
1323                        varyList.append(parm)
1324                    DepCons.append([Trans[ibt,iat],G2obj.G2VarObj(parm)])
1325        if len(DepCons) == 1:
1326            constraints['Phase'].append([IndpCon,DepCons[0],None,None,'e'])
1327        elif len(DepCons) > 1:       
1328            for Dep in DepCons:
1329                Dep[0] *= -1
1330            constraints['Phase'].append([IndpCon]+DepCons+[0.0,None,'c'])
1331#    constDict,fixedList,ignored = G2stIO.ProcessConstraints(constraints['Phase'])
1332#    groups,parmlist = G2mv.GroupConstraints(constDict)
1333#    G2mv.GenerateConstraints(groups,parmlist,varyList,constDict,fixedList,parmDict)
1334#    print 'old',parmDict
1335#    G2mv.Dict2Map(parmDict,varyList)
1336#    print 'new',parmDict
1337    for hId,hist in enumerate(UseList):    #HAP - seems OK
1338        ohapkey = '%d:%d:'%(opId,hId)
1339        nhapkey = '%d:%d:'%(npId,hId)
1340        IndpCon = [1.0,G2obj.G2VarObj(ohapkey+'Scale')]
1341        DepCons = [detTrans,G2obj.G2VarObj(nhapkey+'Scale')]
1342        constraints['HAP'].append([IndpCon,DepCons,None,None,'e'])
1343        for name in ['Size;i','Mustrain;i']:
1344            IndpCon = [1.0,G2obj.G2VarObj(ohapkey+name)]
1345            DepCons = [1.0,G2obj.G2VarObj(nhapkey+name)]
1346            constraints['HAP'].append([IndpCon,DepCons,None,None,'e'])
1347       
1348################################################################################
1349#### Rigid bodies
1350################################################################################
1351
1352def UpdateRigidBodies(G2frame,data):
1353    '''Called when Rigid bodies tree item is selected.
1354    Displays the rigid bodies in the data window
1355    '''
1356    if not data.get('RBIds') or not data:
1357        data.update({'Vector':{'AtInfo':{}},'Residue':{'AtInfo':{}},
1358            'RBIds':{'Vector':[],'Residue':[]}})       #empty/bad dict - fill it
1359           
1360    global resList,rbId
1361    Indx = {}
1362    resList = []
1363    plotDefaults = {'oldxy':[0.,0.],'Quaternion':[0.,0.,0.,1.],'cameraPos':30.,'viewDir':[0,0,1],}
1364
1365    G2frame.rbBook = G2G.GSNoteBook(parent=G2frame.dataWindow)
1366    G2frame.dataWindow.GetSizer().Add(G2frame.rbBook,1,wx.ALL|wx.EXPAND)
1367    VectorRB = wx.ScrolledWindow(G2frame.rbBook)
1368    VectorRBDisplay = wx.Panel(VectorRB)
1369    G2frame.rbBook.AddPage(VectorRB,'Vector rigid bodies')
1370    ResidueRB = wx.ScrolledWindow(G2frame.rbBook)
1371    ResidueRBDisplay = wx.Panel(ResidueRB)
1372    G2frame.rbBook.AddPage(ResidueRB,'Residue rigid bodies')
1373   
1374    def OnPageChanged(event):
1375        global resList
1376        resList = []
1377        if event:       #page change event!
1378            page = event.GetSelection()
1379        else:
1380            page = G2frame.rbBook.GetSelection()
1381        G2frame.rbBook.ChangeSelection(page)
1382        text = G2frame.rbBook.GetPageText(page)
1383        if text == 'Vector rigid bodies':
1384            G2gd.SetDataMenuBar(G2frame,G2frame.dataWindow.VectorBodyMenu)
1385            G2frame.Bind(wx.EVT_MENU, AddVectorRB, id=G2G.wxID_VECTORBODYADD)
1386            G2frame.Page = [page,'vrb']
1387            UpdateVectorRB()
1388        elif text == 'Residue rigid bodies':
1389            G2gd.SetDataMenuBar(G2frame,G2frame.dataWindow.RigidBodyMenu)
1390            G2frame.Bind(wx.EVT_MENU, AddResidueRB, id=G2G.wxID_RIGIDBODYADD)
1391            G2frame.Bind(wx.EVT_MENU, OnImportRigidBody, id=G2G.wxID_RIGIDBODYIMPORT)
1392            G2frame.Bind(wx.EVT_MENU, OnDefineTorsSeq, id=G2G.wxID_RESIDUETORSSEQ) #enable only if residue RBs exist?
1393            G2frame.Page = [page,'rrb']
1394            UpdateResidueRB()
1395           
1396    def getMacroFile(macName):
1397        defDir = os.path.join(os.path.split(__file__)[0],'GSASIImacros')
1398        dlg = wx.FileDialog(G2frame,message='Choose '+macName+' rigid body macro file',
1399            defaultDir=defDir,defaultFile="",wildcard="GSAS-II macro file (*.mac)|*.mac",
1400            style=wx.FD_OPEN | wx.CHANGE_DIR)
1401        try:
1402            if dlg.ShowModal() == wx.ID_OK:
1403                macfile = dlg.GetPath()
1404                macro = open(macfile,'Ur')
1405                head = macro.readline()
1406                if macName not in head:
1407                    print (head)
1408                    print ('**** ERROR - wrong restraint macro file selected, try again ****')
1409                    macro = []
1410            else: # cancel was pressed
1411                macro = []
1412        finally:
1413            dlg.Destroy()
1414        return macro        #advanced past 1st line
1415       
1416    def getTextFile():
1417        dlg = wx.FileDialog(G2frame,'Choose rigid body text file', '.', '',
1418            "GSAS-II text file (*.txt)|*.txt|XYZ file (*.xyz)|*.xyz|"
1419            "Sybyl mol2 file (*.mol2)|*.mol2|PDB file (*.pdb;*.ent)|*.pdb;*.ent",
1420            wx.FD_OPEN | wx.CHANGE_DIR)
1421        try:
1422            if dlg.ShowModal() == wx.ID_OK:
1423                txtfile = dlg.GetPath()
1424                ext = os.path.splitext(txtfile)[1]
1425                text = open(txtfile,'Ur')
1426            else: # cancel was pressed
1427                ext = ''
1428                text = []
1429        finally:
1430            dlg.Destroy()
1431        if 'ent' in ext:
1432            ext = '.pdb'
1433        return text,ext.lower()
1434       
1435    def OnImportRigidBody(event):
1436        page = G2frame.rbBook.GetSelection()
1437        if 'Vector' in G2frame.rbBook.GetPageText(page):
1438            pass
1439        elif 'Residue' in G2frame.rbBook.GetPageText(page):
1440            ImportResidueRB()
1441           
1442    def AddVectorRB(event):
1443        AtInfo = data['Vector']['AtInfo']
1444        dlg = G2G.MultiIntegerDialog(G2frame,'New Rigid Body',['No. atoms','No. translations'],[1,1])
1445        if dlg.ShowModal() == wx.ID_OK:
1446            nAtoms,nTrans = dlg.GetValues()
1447            rbId = ran.randint(0,sys.maxsize)
1448            vecMag = [1.0 for i in range(nTrans)]
1449            vecRef = [False for i in range(nTrans)]
1450            vecVal = [np.zeros((nAtoms,3)) for j in range(nTrans)]
1451            rbTypes = ['C' for i in range(nAtoms)]
1452            Info = G2elem.GetAtomInfo('C')
1453            AtInfo['C'] = [Info['Drad'],Info['Color']]
1454            data['Vector'][rbId] = {'RBname':'UNKRB','VectMag':vecMag,'rbXYZ':np.zeros((nAtoms,3)),
1455                'rbRef':[0,1,2,False],'VectRef':vecRef,'rbTypes':rbTypes,'rbVect':vecVal,'useCount':0}
1456            data['RBIds']['Vector'].append(rbId)
1457        dlg.Destroy()
1458        UpdateVectorRB()
1459       
1460    def AddResidueRB(event):
1461        AtInfo = data['Residue']['AtInfo']
1462        macro = getMacroFile('rigid body')
1463        if not macro:
1464            return
1465        macStr = macro.readline()
1466        while macStr:
1467            items = macStr.split()
1468            if 'I' == items[0]:
1469                rbId = ran.randint(0,sys.maxsize)
1470                rbName = items[1]
1471                rbTypes = []
1472                rbXYZ = []
1473                rbSeq = []
1474                atNames = []
1475                nAtms,nSeq,nOrig,mRef,nRef = [int(items[i]) for i in [2,3,4,5,6]]
1476                for iAtm in range(nAtms):
1477                    macStr = macro.readline().split()
1478                    atName = macStr[0]
1479                    atType = macStr[1]
1480                    atNames.append(atName)
1481                    rbXYZ.append([float(macStr[i]) for i in [2,3,4]])
1482                    rbTypes.append(atType)
1483                    if atType not in AtInfo:
1484                        Info = G2elem.GetAtomInfo(atType)
1485                        AtInfo[atType] = [Info['Drad'],Info['Color']]
1486                rbXYZ = np.array(rbXYZ)-np.array(rbXYZ[nOrig-1])
1487                for iSeq in range(nSeq):
1488                    macStr = macro.readline().split()
1489                    mSeq = int(macStr[0])
1490                    for jSeq in range(mSeq):
1491                        macStr = macro.readline().split()
1492                        iBeg = int(macStr[0])-1
1493                        iFin = int(macStr[1])-1
1494                        angle = 0.0
1495                        nMove = int(macStr[2])
1496                        iMove = [int(macStr[i])-1 for i in range(3,nMove+3)]
1497                        rbSeq.append([iBeg,iFin,angle,iMove])
1498                data['Residue'][rbId] = {'RBname':rbName,'rbXYZ':rbXYZ,'rbTypes':rbTypes,
1499                    'atNames':atNames,'rbRef':[nOrig-1,mRef-1,nRef-1,True],'rbSeq':rbSeq,
1500                    'SelSeq':[0,0],'useCount':0}
1501                data['RBIds']['Residue'].append(rbId)
1502                print ('Rigid body '+rbName+' added')
1503            macStr = macro.readline()
1504        macro.close()
1505        UpdateResidueRB()
1506       
1507    def ImportResidueRB():
1508        AtInfo = data['Residue']['AtInfo']
1509        text,ext = getTextFile()
1510        if not text:
1511            return
1512        rbId = ran.randint(0,sys.maxsize)
1513        rbTypes = []
1514        rbXYZ = []
1515        atNames = []
1516        txtStr = text.readline()
1517        if 'xyz' in ext:
1518            txtStr = text.readline()
1519            txtStr = text.readline()
1520        elif 'mol2' in ext:
1521            while 'ATOM' not in txtStr:
1522                txtStr = text.readline()
1523            txtStr = text.readline()
1524        elif 'pdb' in ext:
1525            while 'ATOM' not in txtStr[:6] and 'HETATM' not in txtStr[:6]:
1526                txtStr = text.readline()
1527                #print txtStr
1528        items = txtStr.split()
1529        while len(items):
1530            if 'txt' in ext:
1531                atName = items[0]
1532                atType = items[1]
1533                rbXYZ.append([float(items[i]) for i in [2,3,4]])
1534            elif 'xyz' in ext:
1535                atType = items[0]
1536                rbXYZ.append([float(items[i]) for i in [1,2,3]])
1537                atName = atType+str(len(rbXYZ))
1538            elif 'mol2' in ext:
1539                atType = items[1]
1540                atName = items[1]+items[0]
1541                rbXYZ.append([float(items[i]) for i in [2,3,4]])
1542            elif 'pdb' in ext:
1543                atType = items[-1]
1544                atName = items[2]
1545                xyz = txtStr[30:55].split()                   
1546                rbXYZ.append([float(x) for x in xyz])
1547            atNames.append(atName)
1548            rbTypes.append(atType)
1549            if atType not in AtInfo:
1550                Info = G2elem.GetAtomInfo(atType)
1551                AtInfo[atType] = [Info['Drad'],Info['Color']]
1552            txtStr = text.readline()
1553            if 'mol2' in ext and 'BOND' in txtStr:
1554                break
1555            if 'pdb' in ext and ('ATOM' not in txtStr[:6] and 'HETATM' not in txtStr[:6]):
1556                break
1557            items = txtStr.split()
1558        if len(atNames) < 3:
1559            G2G.G2MessageBox(G2frame,'Not enough atoms in rigid body; must be 3 or more')
1560        else:
1561            rbXYZ = np.array(rbXYZ)-np.array(rbXYZ[0])
1562            Xxyz = rbXYZ[1]
1563            X = Xxyz/np.sqrt(np.sum(Xxyz**2))
1564            Yxyz = rbXYZ[2]
1565            Y = Yxyz/np.sqrt(np.sum(Yxyz**2))
1566            Mat = G2mth.getRBTransMat(X,Y)
1567            rbXYZ = np.inner(Mat,rbXYZ).T
1568            data['Residue'][rbId] = {'RBname':'UNKRB','rbXYZ':rbXYZ,'rbTypes':rbTypes,
1569                'atNames':atNames,'rbRef':[0,1,2,False],'rbSeq':[],'SelSeq':[0,0],'useCount':0}
1570            data['RBIds']['Residue'].append(rbId)
1571            print ('Rigid body UNKRB added')
1572        text.close()
1573        UpdateResidueRB(rbId)
1574       
1575    def FindNeighbors(Orig,XYZ,atTypes,atNames,AtInfo):
1576        Radii = []
1577        for Atype in atTypes:
1578            Radii.append(AtInfo[Atype][0])
1579        Radii = np.array(Radii)
1580        Neigh = []
1581        Dx = XYZ-XYZ[Orig]
1582        dist = np.sqrt(np.sum(Dx**2,axis=1))
1583        sumR = Radii[Orig]+Radii
1584        IndB = ma.nonzero(ma.masked_greater(dist-0.85*sumR,0.))
1585        for j in IndB[0]:
1586            if j != Orig and atTypes[j] != 'H':
1587                Neigh.append(atNames[j])
1588        return Neigh
1589       
1590    def FindAllNeighbors(XYZ,atTypes,atNames,AtInfo):
1591        NeighDict = {}
1592        for iat,xyz in enumerate(atNames):
1593            NeighDict[atNames[iat]] = FindNeighbors(iat,XYZ,atTypes,atNames,AtInfo)
1594        return NeighDict
1595       
1596    def FindRiding(Orig,Pivot,NeighDict):
1597        riding = [Orig,Pivot]
1598        iAdd = 1
1599        new = True
1600        while new:
1601            newAtms = NeighDict[riding[iAdd]]
1602            for At in newAtms:
1603                new = False
1604                if At not in riding:
1605                    riding.append(At)
1606                    new = True
1607            iAdd += 1
1608            if iAdd < len(riding):
1609                new = True
1610        return riding[2:]
1611                       
1612    def OnDefineTorsSeq(event):
1613        global rbId
1614        rbData = data['Residue'][rbId]
1615        if not len(rbData):
1616            return
1617        atNames = rbData['atNames']
1618        AtInfo = data['Residue']['AtInfo']
1619        atTypes = rbData['rbTypes']
1620        XYZ = rbData['rbXYZ']
1621        neighDict = FindAllNeighbors(XYZ,atTypes,atNames,AtInfo)
1622        TargList = []           
1623        dlg = wx.SingleChoiceDialog(G2frame,'Select origin atom for torsion sequence','Origin atom',rbData['atNames'])
1624        if dlg.ShowModal() == wx.ID_OK:
1625            Orig = dlg.GetSelection()
1626            TargList = neighDict[atNames[Orig]]
1627        dlg.Destroy()
1628        if not len(TargList):
1629            return
1630        dlg = wx.SingleChoiceDialog(G2frame,'Select pivot atom for torsion sequence','Pivot atom',TargList)
1631        if dlg.ShowModal() == wx.ID_OK:
1632            Piv = atNames.index(TargList[dlg.GetSelection()])
1633            riding = FindRiding(atNames[Orig],atNames[Piv],neighDict)
1634            Riding = []
1635            for atm in riding:
1636                Riding.append(atNames.index(atm))
1637            rbData['rbSeq'].append([Orig,Piv,0.0,Riding])           
1638        dlg.Destroy()
1639        UpdateResidueRB(rbId)
1640
1641    def UpdateVectorRB(Scroll=0):
1642        AtInfo = data['Vector']['AtInfo']
1643        refChoice = {}
1644        if 'DELETED' in str(G2frame.GetStatusBar()):   #seems to be no other way to do this (wx bug)
1645            if GSASIIpath.GetConfigValue('debug'):
1646                print ('DBG_wx error: Rigid Body/Status not cleanly deleted after Refine')
1647            return
1648        SetStatusLine(' You may use e.g. "c60" or "s60" for a vector entry')
1649        def rbNameSizer(rbId,rbData):
1650
1651            def OnRBName(event):
1652                event.Skip()
1653                Obj = event.GetEventObject()
1654                rbData['RBname'] = Obj.GetValue()
1655               
1656            def OnDelRB(event):
1657                Obj = event.GetEventObject()
1658                rbId = Indx[Obj.GetId()]
1659                if rbId in data['Vector']:
1660                    del data['Vector'][rbId]
1661                    data['RBIds']['Vector'].remove(rbId)
1662                    rbData['useCount'] -= 1
1663                wx.CallAfter(UpdateVectorRB)
1664               
1665            def OnPlotRB(event):
1666                Obj = event.GetEventObject()
1667                Obj.SetValue(False)
1668                G2plt.PlotRigidBody(G2frame,'Vector',AtInfo,rbData,plotDefaults)
1669           
1670            nameSizer = wx.BoxSizer(wx.HORIZONTAL)
1671            nameSizer.Add(wx.StaticText(VectorRBDisplay,-1,'Rigid body name: '),
1672                0,wx.ALIGN_CENTER_VERTICAL)
1673            RBname = wx.TextCtrl(VectorRBDisplay,-1,rbData['RBname'])
1674            Indx[RBname.GetId()] = rbId
1675            RBname.Bind(wx.EVT_TEXT_ENTER,OnRBName)
1676            RBname.Bind(wx.EVT_KILL_FOCUS,OnRBName)
1677            nameSizer.Add(RBname,0,wx.ALIGN_CENTER_VERTICAL)
1678            nameSizer.Add((5,0),)
1679            plotRB = wx.CheckBox(VectorRBDisplay,-1,'Plot?')
1680            Indx[plotRB.GetId()] = rbId
1681            plotRB.Bind(wx.EVT_CHECKBOX,OnPlotRB)
1682            nameSizer.Add(plotRB,0,wx.ALIGN_CENTER_VERTICAL)
1683            nameSizer.Add((5,0),)
1684            if not rbData['useCount']:
1685                delRB = wx.CheckBox(VectorRBDisplay,-1,'Delete?')
1686                Indx[delRB.GetId()] = rbId
1687                delRB.Bind(wx.EVT_CHECKBOX,OnDelRB)
1688                nameSizer.Add(delRB,0,wx.ALIGN_CENTER_VERTICAL)
1689            return nameSizer
1690           
1691        def rbRefAtmSizer(rbId,rbData):
1692           
1693            def OnRefSel(event):
1694                Obj = event.GetEventObject()
1695                iref = Indx[Obj.GetId()]
1696                sel = Obj.GetValue()
1697                rbData['rbRef'][iref] = atNames.index(sel)
1698                FillRefChoice(rbId,rbData)
1699           
1700            refAtmSizer = wx.BoxSizer(wx.HORIZONTAL)
1701            atNames = [name+str(i) for i,name in enumerate(rbData['rbTypes'])]
1702            rbRef = rbData.get('rbRef',[0,1,2,False])
1703            rbData['rbRef'] = rbRef
1704            if rbData['useCount']:
1705                refAtmSizer.Add(wx.StaticText(VectorRBDisplay,-1,
1706                    'Orientation reference atoms A-B-C: %s, %s, %s'%(atNames[rbRef[0]], \
1707                     atNames[rbRef[1]],atNames[rbRef[2]])),0)
1708            else:
1709                refAtmSizer.Add(wx.StaticText(VectorRBDisplay,-1,
1710                    'Orientation reference atoms A-B-C: '),0,wx.ALIGN_CENTER_VERTICAL)
1711                for i in range(3):
1712                    choices = [atNames[j] for j in refChoice[rbId][i]]
1713                    refSel = wx.ComboBox(VectorRBDisplay,-1,value='',
1714                        choices=choices,style=wx.CB_READONLY|wx.CB_DROPDOWN)
1715                    refSel.SetValue(atNames[rbRef[i]])
1716                    refSel.Bind(wx.EVT_COMBOBOX, OnRefSel)
1717                    Indx[refSel.GetId()] = i
1718                    refAtmSizer.Add(refSel,0,wx.ALIGN_CENTER_VERTICAL)
1719            return refAtmSizer
1720                       
1721        def rbVectMag(rbId,imag,rbData):
1722           
1723            def OnRBVectorMag(event):
1724                event.Skip()
1725                Obj = event.GetEventObject()
1726                rbId,imag = Indx[Obj.GetId()]
1727                try:
1728                    val = float(Obj.GetValue())
1729                    if val <= 0.:
1730                        raise ValueError
1731                    rbData['VectMag'][imag] = val
1732                except ValueError:
1733                    pass
1734                Obj.SetValue('%8.4f'%(val))
1735                wx.CallAfter(UpdateVectorRB,VectorRB.GetScrollPos(wx.VERTICAL))
1736                G2plt.PlotRigidBody(G2frame,'Vector',AtInfo,data['Vector'][rbId],plotDefaults)
1737               
1738            def OnRBVectorRef(event):
1739                Obj = event.GetEventObject()
1740                rbId,imag = Indx[Obj.GetId()]
1741                rbData['VectRef'][imag] = Obj.GetValue()
1742                       
1743            magSizer = wx.wx.BoxSizer(wx.HORIZONTAL)
1744            magSizer.Add(wx.StaticText(VectorRBDisplay,-1,'Translation magnitude: '),
1745                0,wx.ALIGN_CENTER_VERTICAL)
1746            magValue = wx.TextCtrl(VectorRBDisplay,-1,'%8.4f'%(rbData['VectMag'][imag]))
1747            Indx[magValue.GetId()] = [rbId,imag]
1748            magValue.Bind(wx.EVT_TEXT_ENTER,OnRBVectorMag)
1749            magValue.Bind(wx.EVT_KILL_FOCUS,OnRBVectorMag)
1750            magSizer.Add(magValue,0,wx.ALIGN_CENTER_VERTICAL)
1751            magSizer.Add((5,0),)
1752            magref = wx.CheckBox(VectorRBDisplay,-1,label=' Refine?') 
1753            magref.SetValue(rbData['VectRef'][imag])
1754            magref.Bind(wx.EVT_CHECKBOX,OnRBVectorRef)
1755            Indx[magref.GetId()] = [rbId,imag]
1756            magSizer.Add(magref,0,wx.ALIGN_CENTER_VERTICAL)
1757            return magSizer
1758           
1759        def rbVectors(rbId,imag,mag,XYZ,rbData):
1760
1761            def TypeSelect(event):
1762                AtInfo = data['Vector']['AtInfo']
1763                r,c = event.GetRow(),event.GetCol()
1764                if vecGrid.GetColLabelValue(c) == 'Type':
1765                    PE = G2elemGUI.PickElement(G2frame,oneOnly=True)
1766                    if PE.ShowModal() == wx.ID_OK:
1767                        if PE.Elem != 'None':
1768                            El = PE.Elem.strip().lower().capitalize()
1769                            if El not in AtInfo:
1770                                Info = G2elem.GetAtomInfo(El)
1771                                AtInfo[El] = [Info['Drad'],Info['Color']]
1772                            rbData['rbTypes'][r] = El
1773                            vecGrid.SetCellValue(r,c,El)
1774                    PE.Destroy()
1775                wx.CallAfter(UpdateVectorRB,VectorRB.GetScrollPos(wx.VERTICAL))
1776
1777            def ChangeCell(event):
1778                r,c =  event.GetRow(),event.GetCol()
1779                if r >= 0 and (0 <= c < 3):
1780                    try:
1781                        val = float(vecGrid.GetCellValue(r,c))
1782                        rbData['rbVect'][imag][r][c] = val
1783                    except ValueError:
1784                        pass
1785                G2plt.PlotRigidBody(G2frame,'Vector',AtInfo,data['Vector'][rbId],plotDefaults)
1786                wx.CallAfter(UpdateVectorRB,VectorRB.GetScrollPos(wx.VERTICAL))
1787
1788            vecSizer = wx.BoxSizer()
1789            Types = 3*[wg.GRID_VALUE_FLOAT+':10,5',]+[wg.GRID_VALUE_STRING,]+3*[wg.GRID_VALUE_FLOAT+':10,5',]
1790            colLabels = ['Vector x','Vector y','Vector z','Type','Cart x','Cart y','Cart z']
1791            table = []
1792            rowLabels = []
1793            for ivec,xyz in enumerate(rbData['rbVect'][imag]):
1794                table.append(list(xyz)+[rbData['rbTypes'][ivec],]+list(XYZ[ivec]))
1795                rowLabels.append(str(ivec))
1796            vecTable = G2G.Table(table,rowLabels=rowLabels,colLabels=colLabels,types=Types)
1797            vecGrid = G2G.GSGrid(VectorRBDisplay)
1798            vecGrid.SetTable(vecTable, True)
1799            if 'phoenix' in wx.version():
1800                vecGrid.Bind(wg.EVT_GRID_CELL_CHANGED, ChangeCell)
1801            else:
1802                vecGrid.Bind(wg.EVT_GRID_CELL_CHANGE, ChangeCell)
1803            if not imag:
1804                vecGrid.Bind(wg.EVT_GRID_CELL_LEFT_DCLICK, TypeSelect)
1805            attr = wx.grid.GridCellAttr()
1806            attr.SetEditor(G2G.GridFractionEditor(vecGrid))
1807            for c in range(3):
1808                vecGrid.SetColAttr(c, attr)
1809            for row in range(vecTable.GetNumberRows()):
1810                if imag:
1811                    vecGrid.SetCellStyle(row,3,VERY_LIGHT_GREY,True)                   
1812                for col in [4,5,6]:
1813                    vecGrid.SetCellStyle(row,col,VERY_LIGHT_GREY,True)
1814#            vecGrid.SetScrollRate(0,0)
1815            vecGrid.AutoSizeColumns(False)
1816            vecSizer.Add(vecGrid)
1817            return vecSizer
1818       
1819        def FillRefChoice(rbId,rbData):
1820            choiceIds = [i for i in range(len(rbData['rbTypes']))]
1821           
1822            rbRef = rbData.get('rbRef',[-1,-1,-1,False])
1823            for i in range(3):
1824                choiceIds.remove(rbRef[i])
1825            refChoice[rbId] = [choiceIds[:],choiceIds[:],choiceIds[:]]
1826            for i in range(3):
1827                refChoice[rbId][i].append(rbRef[i])
1828                refChoice[rbId][i].sort()     
1829           
1830        if VectorRB.GetSizer(): VectorRB.GetSizer().Clear(True)
1831        VectorRBSizer = wx.BoxSizer(wx.VERTICAL)
1832        for rbId in data['RBIds']['Vector']:
1833            if rbId != 'AtInfo':
1834                rbData = data['Vector'][rbId]
1835                FillRefChoice(rbId,rbData)
1836                VectorRBSizer.Add(rbNameSizer(rbId,rbData),0)
1837                VectorRBSizer.Add(rbRefAtmSizer(rbId,rbData),0)
1838                XYZ = np.array([[0.,0.,0.] for Ty in rbData['rbTypes']])
1839                for imag,mag in enumerate(rbData['VectMag']):
1840                    XYZ += mag*rbData['rbVect'][imag]
1841                    VectorRBSizer.Add(rbVectMag(rbId,imag,rbData),0)
1842                    VectorRBSizer.Add(rbVectors(rbId,imag,mag,XYZ,rbData),0)
1843                VectorRBSizer.Add((5,5),0)
1844                data['Vector'][rbId]['rbXYZ'] = XYZ       
1845        VectorRBSizer.Layout()   
1846        VectorRBDisplay.SetSizer(VectorRBSizer,True)
1847        Size = VectorRBSizer.GetMinSize()
1848        Size[0] += 40
1849        Size[1] = max(Size[1],450) + 20
1850        VectorRBDisplay.SetSize(Size)
1851        VectorRB.SetScrollbars(10,10,Size[0]/10-4,Size[1]/10-1)
1852        VectorRB.Scroll(0,Scroll)
1853       
1854    def UpdateResidueRB(rbId=0):
1855        AtInfo = data['Residue']['AtInfo']
1856        refChoice = {}
1857        RefObjs = []
1858
1859        def rbNameSizer(rbId,rbData):
1860
1861            def OnRBName(event):
1862                Obj = event.GetEventObject()
1863                rbData['RBname'] = Obj.GetValue()
1864                wx.CallAfter(UpdateResidueRB,rbId)
1865               
1866            def OnDelRB(event):
1867                Obj = event.GetEventObject()
1868                rbId = Indx[Obj.GetId()]
1869                if rbId in data['Residue']: 
1870                    del data['Residue'][rbId]
1871                    data['RBIds']['Residue'].remove(rbId)
1872                wx.CallAfter(UpdateResidueRB)
1873               
1874            def OnStripH(event):
1875                Obj = event.GetEventObject()
1876                rbId = Indx[Obj.GetId()]
1877                if rbId in data['Residue']:
1878                    newNames = []
1879                    newTypes = []
1880                    newXYZ = []
1881                    for i,atype in enumerate(rbData['rbTypes']):
1882                        if atype != 'H':
1883                            newNames.append(rbData['atNames'][i])
1884                            newTypes.append(rbData['rbTypes'][i])
1885                            newXYZ.append(rbData['rbXYZ'][i])
1886                    rbData['atNames'] = newNames
1887                    rbData['rbTypes'] = newTypes
1888                    rbData['rbXYZ'] = newXYZ
1889                G2plt.PlotRigidBody(G2frame,'Residue',AtInfo,rbData,plotDefaults)
1890                wx.CallAfter(UpdateResidueRB,rbId)
1891                   
1892            def OnPlotRB(event):
1893                Obj = event.GetEventObject()
1894                Obj.SetValue(False)
1895                G2plt.PlotRigidBody(G2frame,'Residue',AtInfo,rbData,plotDefaults)
1896           
1897            nameSizer = wx.BoxSizer(wx.HORIZONTAL)
1898            nameSizer.Add(wx.StaticText(ResidueRBDisplay,-1,'Residue name: '),
1899                0,wx.ALIGN_CENTER_VERTICAL)
1900            RBname = wx.TextCtrl(ResidueRBDisplay,-1,rbData['RBname'])
1901            Indx[RBname.GetId()] = rbId
1902            RBname.Bind(wx.EVT_TEXT_ENTER,OnRBName)
1903            RBname.Bind(wx.EVT_KILL_FOCUS,OnRBName)
1904            nameSizer.Add(RBname,0,wx.ALIGN_CENTER_VERTICAL)
1905            nameSizer.Add((5,0),)
1906            plotRB = wx.CheckBox(ResidueRBDisplay,-1,'Plot?')
1907            Indx[plotRB.GetId()] = rbId
1908            plotRB.Bind(wx.EVT_CHECKBOX,OnPlotRB)
1909            nameSizer.Add(plotRB,0,wx.ALIGN_CENTER_VERTICAL)
1910            nameSizer.Add((5,0),)
1911            if not rbData['useCount']:
1912                delRB = wx.CheckBox(ResidueRBDisplay,-1,'Delete?')
1913                Indx[delRB.GetId()] = rbId
1914                delRB.Bind(wx.EVT_CHECKBOX,OnDelRB)
1915                nameSizer.Add(delRB,0,wx.ALIGN_CENTER_VERTICAL)
1916                if 'H'  in rbData['rbTypes']:
1917                    stripH = wx.CheckBox(ResidueRBDisplay,-1,'Strip H-atoms?')
1918                    Indx[stripH.GetId()] = rbId
1919                    stripH.Bind(wx.EVT_CHECKBOX,OnStripH)
1920                    nameSizer.Add(stripH,0,wx.ALIGN_CENTER_VERTICAL)
1921            return nameSizer
1922           
1923        def rbResidues(rbId,rbData):
1924           
1925            def TypeSelect(event):
1926                AtInfo = data['Residue']['AtInfo']
1927                r,c = event.GetRow(),event.GetCol()
1928                if vecGrid.GetColLabelValue(c) == 'Type':
1929                    PE = G2elemGUI.PickElement(G2frame,oneOnly=True)
1930                    if PE.ShowModal() == wx.ID_OK:
1931                        if PE.Elem != 'None':
1932                            El = PE.Elem.strip().lower().capitalize()
1933                            if El not in AtInfo:
1934                                Info = G2elem.GetAtomInfo(El)
1935                                AtInfo[El] = [Info['Drad']['Color']]
1936                            rbData['rbTypes'][r] = El
1937                            vecGrid.SetCellValue(r,c,El)
1938                    PE.Destroy()
1939
1940            def ChangeCell(event):
1941                r,c =  event.GetRow(),event.GetCol()
1942                if r >= 0 and (0 <= c < 3):
1943                    try:
1944                        val = float(vecGrid.GetCellValue(r,c))
1945                        rbData['rbXYZ'][r][c] = val
1946                    except ValueError:
1947                        pass
1948                       
1949            def RowSelect(event):
1950                r,c =  event.GetRow(),event.GetCol()
1951                if c < 0:                   #only row clicks
1952                    for vecgrid in resList:
1953                        vecgrid.ClearSelection()
1954                    vecGrid.SelectRow(r,True)
1955
1956            def OnRefSel(event):
1957               
1958                Obj = event.GetEventObject()
1959                iref,res,jref = Indx[Obj.GetId()]
1960                sel = Obj.GetValue()
1961                ind = atNames.index(sel)
1962                if rbData['rbTypes'][ind] == 'H':
1963                    G2G.G2MessageBox(G2frame,'You should not select an H-atom for rigid body orientation')
1964                rbData['rbRef'][iref] = ind
1965                FillRefChoice(rbId,rbData)
1966                for i,ref in enumerate(RefObjs[jref]):
1967                    ref.SetItems([atNames[j] for j in refChoice[rbId][i]])
1968                    ref.SetValue(atNames[rbData['rbRef'][i]])                   
1969                rbXYZ = rbData['rbXYZ']
1970                if not iref:     #origin change
1971                    rbXYZ -= rbXYZ[ind]
1972                #TODO - transform all atom XYZ by axis choices
1973                Xxyz = rbXYZ[rbData['rbRef'][1]]
1974                X = Xxyz/np.sqrt(np.sum(Xxyz**2))
1975                Yxyz = rbXYZ[rbData['rbRef'][2]]
1976                Y = Yxyz/np.sqrt(np.sum(Yxyz**2))
1977                Mat = G2mth.getRBTransMat(X,Y)
1978                rbXYZ = np.inner(Mat,rbXYZ).T
1979                rbData['rbXYZ'] = rbXYZ
1980                res.ClearSelection()
1981                resTable = res.GetTable()
1982                for r in range(res.GetNumberRows()):
1983                    row = resTable.GetRowValues(r)
1984                    row[2:4] = rbXYZ[r]
1985                    resTable.SetRowValues(r,row)
1986                res.ForceRefresh()
1987                G2plt.PlotRigidBody(G2frame,'Residue',AtInfo,rbData,plotDefaults)
1988               
1989            Types = 2*[wg.GRID_VALUE_STRING,]+3*[wg.GRID_VALUE_FLOAT+':10,5',]
1990            colLabels = ['Name','Type','Cart x','Cart y','Cart z']
1991            table = []
1992            rowLabels = []
1993            for ivec,xyz in enumerate(rbData['rbXYZ']):
1994                table.append([rbData['atNames'][ivec],]+[rbData['rbTypes'][ivec],]+list(xyz))
1995                rowLabels.append(str(ivec))
1996            vecTable = G2G.Table(table,rowLabels=rowLabels,colLabels=colLabels,types=Types)
1997            vecGrid = G2G.GSGrid(ResidueRBDisplay)
1998            Indx[vecGrid.GetId()] = rbId
1999            resList.append(vecGrid)
2000            vecGrid.SetTable(vecTable, True)
2001            if 'phoenix' in wx.version():
2002                vecGrid.Bind(wg.EVT_GRID_CELL_CHANGED, ChangeCell)
2003            else:
2004                vecGrid.Bind(wg.EVT_GRID_CELL_CHANGE, ChangeCell)
2005            vecGrid.Bind(wg.EVT_GRID_CELL_LEFT_DCLICK, TypeSelect)
2006            vecGrid.Bind(wg.EVT_GRID_LABEL_LEFT_CLICK, RowSelect)
2007            attr = wx.grid.GridCellAttr()
2008            attr.SetEditor(G2G.GridFractionEditor(vecGrid))
2009            for c in range(3):
2010                vecGrid.SetColAttr(c, attr)
2011            for row in range(vecTable.GetNumberRows()):
2012                for col in range(5):
2013                    vecGrid.SetCellStyle(row,col,VERY_LIGHT_GREY,True)
2014            vecGrid.AutoSizeColumns(False)
2015            vecSizer = wx.BoxSizer()
2016            vecSizer.Add(vecGrid)
2017           
2018            refAtmSizer = wx.BoxSizer(wx.HORIZONTAL)
2019            atNames = rbData['atNames']
2020            rbRef = rbData['rbRef']
2021            if rbData['rbRef'][3] or rbData['useCount']:
2022                refAtmSizer.Add(wx.StaticText(ResidueRBDisplay,-1,
2023                    'Orientation reference non-H atoms A-B-C: %s, %s, %s'%(atNames[rbRef[0]], \
2024                     atNames[rbRef[1]],atNames[rbRef[2]])),0)
2025            else:
2026                refAtmSizer.Add(wx.StaticText(ResidueRBDisplay,-1,
2027                    'Orientation reference non-H atoms A-B-C: '),0,wx.ALIGN_CENTER_VERTICAL)
2028                refObj = [0,0,0]
2029                for i in range(3):
2030                    choices = [atNames[j] for j in refChoice[rbId][i]]
2031                    refSel = wx.ComboBox(ResidueRBDisplay,-1,value='',
2032                        choices=choices,style=wx.CB_READONLY|wx.CB_DROPDOWN)
2033                    refSel.SetValue(atNames[rbRef[i]])
2034                    refSel.Bind(wx.EVT_COMBOBOX, OnRefSel)
2035                    Indx[refSel.GetId()] = [i,vecGrid,len(RefObjs)]
2036                    refObj[i] = refSel
2037                    refAtmSizer.Add(refSel,0,wx.ALIGN_CENTER_VERTICAL)
2038                RefObjs.append(refObj)
2039           
2040            mainSizer = wx.BoxSizer(wx.VERTICAL)
2041            mainSizer.Add(refAtmSizer)
2042            mainSizer.Add(vecSizer)
2043            return mainSizer
2044           
2045        def SeqSizer(angSlide,rbId,iSeq,Seq,atNames):
2046           
2047            def ChangeAngle(event):
2048                event.Skip()
2049                Obj = event.GetEventObject()
2050                rbId,Seq = Indx[Obj.GetId()][:2]
2051                val = Seq[2]
2052                try:
2053                    val = float(Obj.GetValue())
2054                    Seq[2] = val
2055                except ValueError:
2056                    pass
2057                Obj.SetValue('%8.2f'%(val))
2058                G2plt.PlotRigidBody(G2frame,'Residue',AtInfo,data['Residue'][rbId],plotDefaults)
2059               
2060            def OnRadBtn(event):
2061                Obj = event.GetEventObject()
2062                Seq,iSeq,angId = Indx[Obj.GetId()]
2063                data['Residue'][rbId]['SelSeq'] = [iSeq,angId]
2064                angSlide.SetValue(int(100*Seq[2]))
2065               
2066            def OnDelBtn(event):
2067                Obj = event.GetEventObject()
2068                rbId,Seq = Indx[Obj.GetId()]
2069                data['Residue'][rbId]['rbSeq'].remove(Seq)       
2070                wx.CallAfter(UpdateResidueRB,rbId)
2071           
2072            seqSizer = wx.FlexGridSizer(0,5,2,2)
2073            seqSizer.AddGrowableCol(3,0)
2074            iBeg,iFin,angle,iMove = Seq
2075            ang = wx.TextCtrl(ResidueRBDisplay,-1,'%8.2f'%(angle),size=(50,20))
2076            if not iSeq:
2077                radBt = wx.RadioButton(ResidueRBDisplay,-1,'',style=wx.RB_GROUP)
2078                data['Residue'][rbId]['SelSeq'] = [iSeq,ang.GetId()]
2079            else:
2080                radBt = wx.RadioButton(ResidueRBDisplay,-1,'')
2081            radBt.Bind(wx.EVT_RADIOBUTTON,OnRadBtn)                   
2082            seqSizer.Add(radBt)
2083            delBt = wx.RadioButton(ResidueRBDisplay,-1,'')
2084            delBt.Bind(wx.EVT_RADIOBUTTON,OnDelBtn)
2085            seqSizer.Add(delBt)
2086            bond = wx.TextCtrl(ResidueRBDisplay,-1,'%s %s'%(atNames[iBeg],atNames[iFin]),size=(50,20))
2087            seqSizer.Add(bond,0,wx.ALIGN_CENTER_VERTICAL)
2088            Indx[radBt.GetId()] = [Seq,iSeq,ang.GetId()]
2089            Indx[delBt.GetId()] = [rbId,Seq]
2090            Indx[ang.GetId()] = [rbId,Seq,ang]
2091            ang.Bind(wx.EVT_TEXT_ENTER,ChangeAngle)
2092            ang.Bind(wx.EVT_KILL_FOCUS,ChangeAngle)
2093            seqSizer.Add(ang,0,wx.ALIGN_CENTER_VERTICAL)
2094            atms = ''
2095            for i in iMove:   
2096                atms += ' %s,'%(atNames[i])
2097            moves = wx.TextCtrl(ResidueRBDisplay,-1,atms[:-1],size=(200,20))
2098            seqSizer.Add(moves,1,wx.ALIGN_CENTER_VERTICAL|wx.EXPAND|wx.RIGHT)
2099            return seqSizer
2100           
2101        def SlideSizer():
2102           
2103            def OnSlider(event):
2104                Obj = event.GetEventObject()
2105                rbData = Indx[Obj.GetId()]
2106                iSeq,angId = rbData['SelSeq']
2107                val = float(Obj.GetValue())/100.
2108                rbData['rbSeq'][iSeq][2] = val
2109                Indx[angId][2].SetValue('%8.2f'%(val))
2110                G2plt.PlotRigidBody(G2frame,'Residue',AtInfo,rbData,plotDefaults)
2111           
2112            slideSizer = wx.BoxSizer(wx.HORIZONTAL)
2113            slideSizer.Add(wx.StaticText(ResidueRBDisplay,-1,'Selected torsion angle:'),0)
2114            iSeq,angId = rbData['SelSeq']
2115            angSlide = wx.Slider(ResidueRBDisplay,-1,
2116                int(100*rbData['rbSeq'][iSeq][2]),0,36000,size=(200,20),
2117                style=wx.SL_HORIZONTAL)
2118            angSlide.Bind(wx.EVT_SLIDER, OnSlider)
2119            Indx[angSlide.GetId()] = rbData
2120            slideSizer.Add(angSlide,0)           
2121            return slideSizer,angSlide
2122           
2123        def FillRefChoice(rbId,rbData):
2124            choiceIds = [i for i in range(len(rbData['atNames']))]
2125            for seq in rbData['rbSeq']:
2126                for i in seq[3]:
2127                    try:
2128                        choiceIds.remove(i)
2129                    except ValueError:
2130                        pass
2131            rbRef = rbData['rbRef']
2132            for i in range(3):
2133                try:
2134                    choiceIds.remove(rbRef[i])
2135                except ValueError:
2136                    pass
2137            refChoice[rbId] = [choiceIds[:],choiceIds[:],choiceIds[:]]
2138            for i in range(3):
2139                refChoice[rbId][i].append(rbRef[i])
2140                refChoice[rbId][i].sort()
2141               
2142        def OnSelect(event):
2143            rbname = rbchoice[select.GetSelection()]
2144            rbId = RBnames[rbname]
2145            wx.CallLater(100,UpdateResidueRB,rbId)
2146           
2147        if ResidueRBDisplay.GetSizer(): ResidueRBDisplay.GetSizer().Clear(True)
2148        RBnames = {}
2149        for rbid in data['RBIds']['Residue']:
2150            RBnames.update({data['Residue'][rbid]['RBname']:rbid,})
2151        rbchoice = RBnames.keys()
2152        ResidueRBSizer = wx.BoxSizer(wx.VERTICAL)
2153        if len(RBnames) > 1:
2154            selSizer = wx.BoxSizer(wx.HORIZONTAL)
2155            selSizer.Add(wx.StaticText(ResidueRBDisplay,label=' Select residue to view:'),0)
2156            rbchoice.sort()
2157            select = wx.ComboBox(ResidueRBDisplay,choices=rbchoice)
2158            select.Bind(wx.EVT_COMBOBOX,OnSelect)
2159            selSizer.Add(select,0)
2160            ResidueRBSizer.Add(selSizer,0)
2161        if not rbId:
2162            rbId = RBnames[rbchoice[0]]
2163        rbData = data['Residue'][rbId]
2164        FillRefChoice(rbId,rbData)
2165        ResidueRBSizer.Add(rbNameSizer(rbId,rbData),0)
2166        ResidueRBSizer.Add(rbResidues(rbId,rbData),0)
2167        ResidueRBSizer.Add((5,5),0)
2168        if rbData['rbSeq']:
2169            slideSizer,angSlide = SlideSizer()
2170        if len(rbData['rbSeq']):
2171            ResidueRBSizer.Add(wx.StaticText(ResidueRBDisplay,-1,
2172                'Sel  Del  Bond             Angle      Riding atoms'),
2173                0,wx.ALIGN_CENTER_VERTICAL)                       
2174        for iSeq,Seq in enumerate(rbData['rbSeq']):
2175            ResidueRBSizer.Add(SeqSizer(angSlide,rbId,iSeq,Seq,rbData['atNames']))
2176        if rbData['rbSeq']:
2177            ResidueRBSizer.Add(slideSizer,)
2178
2179        ResidueRBSizer.Add((5,25),)
2180        ResidueRBSizer.Layout()   
2181        ResidueRBDisplay.SetSizer(ResidueRBSizer,True)
2182        ResidueRBDisplay.SetAutoLayout(True)
2183        Size = ResidueRBSizer.GetMinSize()
2184        ResidueRBDisplay.SetSize(Size)
2185        ResidueRBDisplay.Show()
2186       
2187    def SetStatusLine(text):
2188        G2frame.GetStatusBar().SetStatusText(text,1)                                     
2189
2190    G2gd.SetDataMenuBar(G2frame,G2frame.dataWindow.RigidBodyMenu)
2191    SetStatusLine('')
2192    UpdateVectorRB()
2193    G2frame.rbBook.Bind(wx.aui.EVT_AUINOTEBOOK_PAGE_CHANGED, OnPageChanged)
2194    wx.CallAfter(OnPageChanged,None)
Note: See TracBrowser for help on using the repository browser.