1 | # -*- coding: utf-8 -*- |
---|
2 | #GSASIIconstrGUI - constraint GUI routines |
---|
3 | ########### SVN repository information ################### |
---|
4 | # $Date: 2021-11-04 13:09:15 +0000 (Thu, 04 Nov 2021) $ |
---|
5 | # $Author: vondreele $ |
---|
6 | # $Revision: 5065 $ |
---|
7 | # $URL: trunk/GSASIIconstrGUI.py $ |
---|
8 | # $Id: GSASIIconstrGUI.py 5065 2021-11-04 13:09:15Z vondreele $ |
---|
9 | ########### SVN repository information ################### |
---|
10 | ''' |
---|
11 | *GSASIIconstrGUI: Constraint GUI routines* |
---|
12 | ------------------------------------------ |
---|
13 | |
---|
14 | Used to define constraints and rigid bodies. |
---|
15 | |
---|
16 | ''' |
---|
17 | from __future__ import division, print_function |
---|
18 | import platform |
---|
19 | import sys |
---|
20 | import copy |
---|
21 | import os.path |
---|
22 | import wx |
---|
23 | import wx.grid as wg |
---|
24 | import wx.lib.scrolledpanel as wxscroll |
---|
25 | import wx.lib.gridmovers as gridmovers |
---|
26 | import random as ran |
---|
27 | import numpy as np |
---|
28 | import numpy.ma as ma |
---|
29 | import numpy.linalg as nl |
---|
30 | import GSASIIpath |
---|
31 | GSASIIpath.SetVersionNumber("$Revision: 5065 $") |
---|
32 | import GSASIIElem as G2elem |
---|
33 | import GSASIIElemGUI as G2elemGUI |
---|
34 | import GSASIIstrIO as G2stIO |
---|
35 | import GSASIImapvars as G2mv |
---|
36 | import GSASIImath as G2mth |
---|
37 | import GSASIIlattice as G2lat |
---|
38 | import GSASIIdataGUI as G2gd |
---|
39 | import GSASIIctrlGUI as G2G |
---|
40 | import GSASIIfiles as G2fl |
---|
41 | import GSASIIplot as G2plt |
---|
42 | import GSASIIobj as G2obj |
---|
43 | import GSASIIspc as G2spc |
---|
44 | import GSASIIpy3 as G2py3 |
---|
45 | import GSASIIphsGUI as G2phG |
---|
46 | import GSASIIIO as G2IO |
---|
47 | import GSASIIscriptable as G2sc |
---|
48 | VERY_LIGHT_GREY = wx.Colour(235,235,235) |
---|
49 | WACV = wx.ALIGN_CENTER_VERTICAL |
---|
50 | |
---|
51 | class G2BoolEditor(wg.GridCellBoolEditor): |
---|
52 | '''Substitute for wx.grid.GridCellBoolEditor except toggles |
---|
53 | grid items immediately when opened, updates grid & table contents after every |
---|
54 | item change |
---|
55 | ''' |
---|
56 | def __init__(self): |
---|
57 | self.saveVals = None |
---|
58 | wx.grid.GridCellBoolEditor.__init__(self) |
---|
59 | |
---|
60 | |
---|
61 | def Create(self, parent, id, evtHandler): |
---|
62 | '''Create the editing control (wx.CheckBox) when cell is opened |
---|
63 | for edit |
---|
64 | ''' |
---|
65 | self._tc = wx.CheckBox(parent, -1, "") |
---|
66 | self._tc.Bind(wx.EVT_CHECKBOX, self.onCheckSet) |
---|
67 | self.SetControl(self._tc) |
---|
68 | if evtHandler: |
---|
69 | self._tc.PushEventHandler(evtHandler) |
---|
70 | |
---|
71 | def onCheckSet(self, event): |
---|
72 | '''Callback used when checkbox is toggled. |
---|
73 | Makes change to table immediately (creating event) |
---|
74 | ''' |
---|
75 | if self.saveVals: |
---|
76 | self.ApplyEdit(*self.saveVals) |
---|
77 | |
---|
78 | |
---|
79 | def SetSize(self, rect): |
---|
80 | '''Set position/size the edit control within the cell's rectangle. |
---|
81 | ''' |
---|
82 | # self._tc.SetDimensions(rect.x, rect.y, rect.width+2, rect.height+2, # older |
---|
83 | self._tc.SetSize(rect.x, rect.y, rect.width+2, rect.height+2, |
---|
84 | wx.SIZE_ALLOW_MINUS_ONE) |
---|
85 | |
---|
86 | def BeginEdit(self, row, col, grid): |
---|
87 | '''Prepares the edit control by loading the initial |
---|
88 | value from the table (toggles it since you would not |
---|
89 | click on it if you were not planning to change it), |
---|
90 | buts saves the original, pre-change value. |
---|
91 | Makes change to table immediately. |
---|
92 | Saves the info needed to make updates in self.saveVals. |
---|
93 | Sets the focus. |
---|
94 | ''' |
---|
95 | if grid.GetTable().GetValue(row,col) not in [True,False]: |
---|
96 | return |
---|
97 | self.startValue = int(grid.GetTable().GetValue(row, col)) |
---|
98 | self.saveVals = row, col, grid |
---|
99 | # invert state and set in editor |
---|
100 | if self.startValue: |
---|
101 | grid.GetTable().SetValue(row, col, 0) |
---|
102 | self._tc.SetValue(0) |
---|
103 | else: |
---|
104 | grid.GetTable().SetValue(row, col, 1) |
---|
105 | self._tc.SetValue(1) |
---|
106 | self._tc.SetFocus() |
---|
107 | self.ApplyEdit(*self.saveVals) |
---|
108 | |
---|
109 | def EndEdit(self, row, col, grid, oldVal=None): |
---|
110 | '''End editing the cell. This is supposed to |
---|
111 | return None if the value has not changed, but I am not |
---|
112 | sure that actually works. |
---|
113 | ''' |
---|
114 | val = int(self._tc.GetValue()) |
---|
115 | if val != oldVal: #self.startValue:? |
---|
116 | return val |
---|
117 | else: |
---|
118 | return None |
---|
119 | |
---|
120 | def ApplyEdit(self, row, col, grid): |
---|
121 | '''Save the value into the table, and create event. |
---|
122 | Called after EndEdit(), BeginEdit and onCheckSet. |
---|
123 | ''' |
---|
124 | val = int(self._tc.GetValue()) |
---|
125 | grid.GetTable().SetValue(row, col, val) # update the table |
---|
126 | |
---|
127 | def Reset(self): |
---|
128 | '''Reset the value in the control back to its starting value. |
---|
129 | ''' |
---|
130 | self._tc.SetValue(self.startValue) |
---|
131 | |
---|
132 | def StartingClick(self): |
---|
133 | '''This seems to be needed for BeginEdit to work properly''' |
---|
134 | pass |
---|
135 | |
---|
136 | def Destroy(self): |
---|
137 | "final cleanup" |
---|
138 | super(G2BoolEditor, self).Destroy() |
---|
139 | |
---|
140 | def Clone(self): |
---|
141 | 'required' |
---|
142 | return G2BoolEditor() |
---|
143 | |
---|
144 | class DragableRBGrid(wg.Grid): |
---|
145 | '''Simple grid implentation for display of rigid body positions. |
---|
146 | |
---|
147 | :param parent: frame or panel where grid will be placed |
---|
148 | :param dict rb: dict with atom labels, types and positions |
---|
149 | :param function onChange: a callback used every time a value in |
---|
150 | rb is changed. |
---|
151 | ''' |
---|
152 | def __init__(self, parent, rb, onChange=None): |
---|
153 | #wg.Grid.__init__(self, parent, wx.ID_ANY,size=(-1,200)) |
---|
154 | wg.Grid.__init__(self, parent, wx.ID_ANY) |
---|
155 | self.SetTable(RBDataTable(rb,onChange), True) |
---|
156 | # Enable Row moving |
---|
157 | gridmovers.GridRowMover(self) |
---|
158 | self.Bind(gridmovers.EVT_GRID_ROW_MOVE, self.OnRowMove, self) |
---|
159 | self.SetColSize(0, 60) |
---|
160 | self.SetColSize(1, 40) |
---|
161 | self.SetColSize(2, 35) |
---|
162 | for r in range(len(rb['RBlbls'])): |
---|
163 | self.SetReadOnly(r,0,isReadOnly=True) |
---|
164 | self.SetCellEditor(r, 1, G2BoolEditor()) |
---|
165 | self.SetCellRenderer(r, 1, wg.GridCellBoolRenderer()) |
---|
166 | self.SetReadOnly(r,2,isReadOnly=True) |
---|
167 | self.SetCellEditor(r,3, wg.GridCellFloatEditor()) |
---|
168 | self.SetCellEditor(r,4, wg.GridCellFloatEditor()) |
---|
169 | self.SetCellEditor(r,6, wg.GridCellFloatEditor()) |
---|
170 | |
---|
171 | def OnRowMove(self,evt): |
---|
172 | 'called when a row move needs to take place' |
---|
173 | frm = evt.GetMoveRow() # Row being moved |
---|
174 | to = evt.GetBeforeRow() # Before which row to insert |
---|
175 | self.GetTable().MoveRow(frm,to) |
---|
176 | |
---|
177 | def completeEdits(self): |
---|
178 | 'complete any outstanding edits' |
---|
179 | if self.IsCellEditControlEnabled(): # complete any grid edits in progress |
---|
180 | #if GSASIIpath.GetConfigValue('debug'): print ('Completing grid edit') |
---|
181 | self.SaveEditControlValue() |
---|
182 | self.HideCellEditControl() |
---|
183 | self.DisableCellEditControl() |
---|
184 | |
---|
185 | class RBDataTable(wg.GridTableBase): |
---|
186 | '''A Table to support :class:`DragableRBGrid` |
---|
187 | ''' |
---|
188 | def __init__(self,rb,onChange): |
---|
189 | wg.GridTableBase.__init__(self) |
---|
190 | self.colLabels = ['Label','Select','Type','x','y','z'] |
---|
191 | self.coords = rb['RBcoords'] |
---|
192 | self.labels = rb['RBlbls'] |
---|
193 | self.types = rb['RBtypes'] |
---|
194 | self.index = rb['RBindex'] |
---|
195 | self.select = rb['RBselection'] |
---|
196 | self.onChange = onChange |
---|
197 | |
---|
198 | # required methods |
---|
199 | def GetNumberRows(self): |
---|
200 | return len(self.labels) |
---|
201 | def GetNumberCols(self): |
---|
202 | return len(self.colLabels) |
---|
203 | def IsEmptyCell(self, row, col): |
---|
204 | return False |
---|
205 | def GetValue(self, row, col): |
---|
206 | row = self.index[row] |
---|
207 | if col == 0: |
---|
208 | return self.labels[row] |
---|
209 | elif col == 1: |
---|
210 | if self.select[row]: |
---|
211 | return '1' |
---|
212 | else: |
---|
213 | return '' |
---|
214 | elif col == 2: |
---|
215 | return self.types[row] |
---|
216 | else: |
---|
217 | return '{:.5f}'.format(self.coords[row][col-3]) |
---|
218 | def SetValue(self, row, col, value): |
---|
219 | row = self.index[row] |
---|
220 | try: |
---|
221 | if col == 0: |
---|
222 | self.labels[row] = value |
---|
223 | elif col == 1: |
---|
224 | self.select[row] = bool(value) |
---|
225 | elif col == 2: |
---|
226 | self.types[row] = value |
---|
227 | else: |
---|
228 | self.coords[row][col-3] = float(value) |
---|
229 | except: |
---|
230 | pass |
---|
231 | if self.onChange: |
---|
232 | self.onChange() |
---|
233 | # Display column & row labels |
---|
234 | def GetColLabelValue(self, col): |
---|
235 | return self.colLabels[col] |
---|
236 | def GetRowLabelValue(self,row): |
---|
237 | return str(row) |
---|
238 | |
---|
239 | # Implement "row movement" by updating the pointer array |
---|
240 | def MoveRow(self,frm,to): |
---|
241 | grid = self.GetView() |
---|
242 | if grid: |
---|
243 | move = self.index[frm] |
---|
244 | del self.index[frm] |
---|
245 | if frm > to: |
---|
246 | self.index.insert(to,move) |
---|
247 | else: |
---|
248 | self.index.insert(to-1,move) |
---|
249 | |
---|
250 | # Notify the grid |
---|
251 | grid.BeginBatch() |
---|
252 | msg = wg.GridTableMessage( |
---|
253 | self, wg.GRIDTABLE_NOTIFY_ROWS_DELETED, frm, 1 |
---|
254 | ) |
---|
255 | grid.ProcessTableMessage(msg) |
---|
256 | msg = wg.GridTableMessage( |
---|
257 | self, wg.GRIDTABLE_NOTIFY_ROWS_INSERTED, to, 1 |
---|
258 | ) |
---|
259 | grid.ProcessTableMessage(msg) |
---|
260 | grid.EndBatch() |
---|
261 | if self.onChange: |
---|
262 | self.onChange() |
---|
263 | |
---|
264 | # def MakeDrawAtom(data,atom): |
---|
265 | # 'Convert atom to format needed to draw it' |
---|
266 | # generalData = data['General'] |
---|
267 | # deftype = G2obj.validateAtomDrawType( |
---|
268 | # GSASIIpath.GetConfigValue('DrawAtoms_default'),generalData) |
---|
269 | # if generalData['Type'] in ['nuclear','faulted',]: |
---|
270 | # atomInfo = [atom[:2]+atom[3:6]+['1']+[deftype]+ |
---|
271 | # ['']+[[255,255,255]]+atom[9:]+[[],[]]][0] |
---|
272 | # ct,cs = [1,8] #type & color |
---|
273 | # atNum = generalData['AtomTypes'].index(atom[ct]) |
---|
274 | # atomInfo[cs] = list(generalData['Color'][atNum]) |
---|
275 | # return atomInfo |
---|
276 | |
---|
277 | class ConstraintDialog(wx.Dialog): |
---|
278 | '''Window to edit Constraint values |
---|
279 | ''' |
---|
280 | def __init__(self,parent,title,text,data,separator='*',varname="",varyflag=False): |
---|
281 | wx.Dialog.__init__(self,parent,-1,'Edit '+title, |
---|
282 | pos=wx.DefaultPosition,style=wx.DEFAULT_DIALOG_STYLE) |
---|
283 | self.data = data[:] |
---|
284 | self.newvar = [varname,varyflag] |
---|
285 | panel = wx.Panel(self) |
---|
286 | mainSizer = wx.BoxSizer(wx.VERTICAL) |
---|
287 | topLabl = wx.StaticText(panel,-1,text) |
---|
288 | mainSizer.Add((10,10),1) |
---|
289 | mainSizer.Add(topLabl,0,wx.LEFT,10) |
---|
290 | mainSizer.Add((10,10),1) |
---|
291 | dataGridSizer = wx.FlexGridSizer(cols=3,hgap=2,vgap=2) |
---|
292 | self.OkBtn = wx.Button(panel,wx.ID_OK) |
---|
293 | for id in range(len(self.data)): |
---|
294 | lbl1 = lbl = str(self.data[id][1]) |
---|
295 | if lbl[-1] != '=': lbl1 = lbl + ' ' + separator + ' ' |
---|
296 | name = wx.StaticText(panel,wx.ID_ANY,lbl1,style=wx.ALIGN_RIGHT) |
---|
297 | scale = G2G.ValidatedTxtCtrl(panel,self.data[id],0,OKcontrol=self.DisableOK) |
---|
298 | dataGridSizer.Add(name,0,wx.LEFT|wx.RIGHT|WACV,5) |
---|
299 | dataGridSizer.Add(scale,0,wx.RIGHT,3) |
---|
300 | if ':' in lbl: |
---|
301 | dataGridSizer.Add( |
---|
302 | wx.StaticText(panel,-1,G2obj.fmtVarDescr(lbl)), |
---|
303 | 0,wx.RIGHT|WACV,3) |
---|
304 | else: |
---|
305 | dataGridSizer.Add((-1,-1)) |
---|
306 | if title == 'New Variable': |
---|
307 | name = wx.StaticText(panel,wx.ID_ANY,"New variable's\nname (optional)", |
---|
308 | style=wx.ALIGN_CENTER) |
---|
309 | scale = G2G.ValidatedTxtCtrl(panel,self.newvar,0,notBlank=False) |
---|
310 | dataGridSizer.Add(name,0,wx.LEFT|wx.RIGHT|WACV,5) |
---|
311 | dataGridSizer.Add(scale,0,wx.RIGHT|WACV,3) |
---|
312 | self.refine = wx.CheckBox(panel,label='Refine?') |
---|
313 | self.refine.SetValue(self.newvar[1]==True) |
---|
314 | self.refine.Bind(wx.EVT_CHECKBOX, self.OnCheckBox) |
---|
315 | dataGridSizer.Add(self.refine,0,wx.RIGHT|WACV,3) |
---|
316 | |
---|
317 | mainSizer.Add(dataGridSizer,0,wx.EXPAND) |
---|
318 | self.OkBtn.Bind(wx.EVT_BUTTON, self.OnOk) |
---|
319 | self.OkBtn.SetDefault() |
---|
320 | cancelBtn = wx.Button(panel,wx.ID_CANCEL) |
---|
321 | cancelBtn.Bind(wx.EVT_BUTTON, self.OnCancel) |
---|
322 | btnSizer = wx.BoxSizer(wx.HORIZONTAL) |
---|
323 | btnSizer.Add((20,20),1) |
---|
324 | btnSizer.Add(self.OkBtn) |
---|
325 | btnSizer.Add((20,20),1) |
---|
326 | btnSizer.Add(cancelBtn) |
---|
327 | btnSizer.Add((20,20),1) |
---|
328 | |
---|
329 | mainSizer.Add(btnSizer,0,wx.EXPAND, 10) |
---|
330 | panel.SetSizer(mainSizer) |
---|
331 | panel.Fit() |
---|
332 | self.Fit() |
---|
333 | self.CenterOnParent() |
---|
334 | |
---|
335 | def DisableOK(self,setting): |
---|
336 | for id in range(len(self.data)): # coefficient cannot be zero |
---|
337 | try: |
---|
338 | if abs(self.data[id][0]) < 1.e-20: |
---|
339 | setting = False |
---|
340 | break |
---|
341 | except: |
---|
342 | pass |
---|
343 | if setting: |
---|
344 | self.OkBtn.Enable() |
---|
345 | else: |
---|
346 | self.OkBtn.Disable() |
---|
347 | |
---|
348 | def OnCheckBox(self,event): |
---|
349 | self.newvar[1] = self.refine.GetValue() |
---|
350 | |
---|
351 | def OnOk(self,event): |
---|
352 | parent = self.GetParent() |
---|
353 | parent.Raise() |
---|
354 | self.EndModal(wx.ID_OK) |
---|
355 | |
---|
356 | def OnCancel(self,event): |
---|
357 | parent = self.GetParent() |
---|
358 | parent.Raise() |
---|
359 | self.EndModal(wx.ID_CANCEL) |
---|
360 | |
---|
361 | def GetData(self): |
---|
362 | return self.data |
---|
363 | |
---|
364 | ##### Constraints ################################################################################ |
---|
365 | def CheckConstraints(G2frame,Phases,Histograms,data,newcons=[],reqVaryList=None,seqhst=None,seqmode='use-all'): |
---|
366 | '''Load constraints & check them for errors. |
---|
367 | |
---|
368 | N.B. Equivalences based on symmetry (etc.) |
---|
369 | are generated by running :func:`GSASIIstrIO.GetPhaseData`. |
---|
370 | |
---|
371 | When reqVaryList is included (see WarnConstraintLimit) then |
---|
372 | parameters with limits are checked against constraints and a |
---|
373 | warning is shown. |
---|
374 | ''' |
---|
375 | G2mv.InitVars() |
---|
376 | #Find all constraints |
---|
377 | constrDict = [] |
---|
378 | for key in data: |
---|
379 | if key.startswith('_'): continue |
---|
380 | constrDict += data[key] |
---|
381 | if newcons: |
---|
382 | constrDict = constrDict + newcons |
---|
383 | constrDict, fixedList, ignored = G2mv.ProcessConstraints(constrDict, seqhst=seqhst, seqmode=seqmode) |
---|
384 | parmDict = {} |
---|
385 | # generate symmetry constraints to check for conflicts |
---|
386 | rigidbodyDict = G2frame.GPXtree.GetItemPyData( |
---|
387 | G2gd.GetGPXtreeItemId(G2frame, G2frame.root, 'Rigid bodies')) |
---|
388 | rbIds = rigidbodyDict.get('RBIds', {'Vector': [], 'Residue': []}) |
---|
389 | rbVary, rbDict = G2stIO.GetRigidBodyModels(rigidbodyDict, Print=False) |
---|
390 | parmDict.update(rbDict) |
---|
391 | (Natoms, atomIndx, phaseVary, phaseDict, pawleyLookup, FFtables, |
---|
392 | BLtables, MFtables, maxSSwave) = G2stIO.GetPhaseData( |
---|
393 | Phases, RestraintDict=None, rbIds=rbIds, Print=False) # generates atom symmetry constraints |
---|
394 | parmDict.update(phaseDict) |
---|
395 | # get Hist and HAP info |
---|
396 | hapVary, hapDict, controlDict = G2stIO.GetHistogramPhaseData( |
---|
397 | Phases, Histograms, Print=False, resetRefList=False) |
---|
398 | parmDict.update(hapDict) |
---|
399 | histVary, histDict, controlDict = G2stIO.GetHistogramData(Histograms, Print=False) |
---|
400 | parmDict.update(histDict) |
---|
401 | |
---|
402 | # TODO: twining info needed? |
---|
403 | #TwConstr,TwFixed = G2stIO.makeTwinFrConstr(Phases,Histograms,hapVary) |
---|
404 | #constrDict += TwConstr |
---|
405 | #fixedList += TwFixed |
---|
406 | varyList = rbVary+phaseVary+hapVary+histVary |
---|
407 | |
---|
408 | msg = G2mv.EvaluateMultipliers(constrDict,parmDict) |
---|
409 | if msg: |
---|
410 | return 'Unable to interpret multiplier(s): '+msg,'' |
---|
411 | if reqVaryList: |
---|
412 | varyList = reqVaryList[:] |
---|
413 | errmsg,warnmsg,groups,parmlist = G2mv.GenerateConstraints(varyList,constrDict,fixedList,parmDict) # changes varyList |
---|
414 | |
---|
415 | impossible = [] |
---|
416 | if reqVaryList: |
---|
417 | Controls = G2frame.GPXtree.GetItemPyData(G2gd.GetGPXtreeItemId(G2frame,G2frame.root, 'Controls')) |
---|
418 | for key in ('parmMinDict','parmMaxDict','parmFrozen'): |
---|
419 | if key not in Controls: Controls[key] = {} |
---|
420 | G2mv.Map2Dict(parmDict,varyList) # changes varyList |
---|
421 | # check for limits on dependent vars |
---|
422 | consVars = [i for i in reqVaryList if i not in varyList] |
---|
423 | impossible = set( |
---|
424 | [str(i) for i in Controls['parmMinDict'] if i in consVars] + |
---|
425 | [str(i) for i in Controls['parmMaxDict'] if i in consVars]) |
---|
426 | if impossible: |
---|
427 | msg = '' |
---|
428 | for i in sorted(impossible): |
---|
429 | if msg: msg += ', ' |
---|
430 | msg += i |
---|
431 | msg = ' &'.join(msg.rsplit(',',1)) |
---|
432 | msg = ('Note: limits on variable(s) '+msg+ |
---|
433 | ' will be ignored because they are constrained.') |
---|
434 | G2G.G2MessageBox(G2frame,msg,'Limits ignored for constrained vars') |
---|
435 | else: |
---|
436 | G2mv.Map2Dict(parmDict,varyList) # changes varyList |
---|
437 | return errmsg,warnmsg |
---|
438 | |
---|
439 | def UpdateConstraints(G2frame, data, selectTab=None, Clear=False): |
---|
440 | '''Called when Constraints tree item is selected. |
---|
441 | Displays the constraints in the data window |
---|
442 | ''' |
---|
443 | |
---|
444 | def FindEquivVarb(name,nameList): |
---|
445 | 'Creates a list of variables appropriate to constrain with name' |
---|
446 | outList = [] |
---|
447 | #phlist = [] |
---|
448 | items = name.split(':') |
---|
449 | namelist = [items[2],] |
---|
450 | if 'dA' in name: |
---|
451 | namelist = ['dAx','dAy','dAz'] |
---|
452 | elif 'AU' in name: |
---|
453 | namelist = ['AUiso','AU11','AU22','AU33','AU12','AU13','AU23'] |
---|
454 | elif 'AM' in name: |
---|
455 | namelist = ['AMx','AMy','AMz'] |
---|
456 | elif items[-1] in ['A0','A1','A2','A3','A4','A5']: |
---|
457 | namelist = ['A0','A1','A2','A3','A4','A5'] |
---|
458 | elif items[-1] in ['D11','D22','D33','D12','D13','D23']: |
---|
459 | namelist = ['D11','D22','D33','D12','D13','D23'] |
---|
460 | elif 'Tm' in name: |
---|
461 | namelist = ['Tmin','Tmax'] |
---|
462 | elif 'MX' in name or 'MY' in name or 'MZ' in name: |
---|
463 | namelist = ['MXcos','MYcos','MZcos','MXsin','MYsin','MZsin'] |
---|
464 | elif 'mV' in name: |
---|
465 | namelist = ['mV0','mV1','mV2'] |
---|
466 | elif 'Debye' in name or 'BkPk' in name: #special cases for Background fxns |
---|
467 | dbname = name.split(';')[0].split(':')[2] |
---|
468 | return [item for item in nameList if dbname in item] |
---|
469 | elif 'RB' in name: |
---|
470 | rbfx = 'RB'+items[2][2] |
---|
471 | if 'T' in name and 'Tr' not in name: |
---|
472 | namelist = [rbfx+'T11',rbfx+'T22',rbfx+'T33',rbfx+'T12',rbfx+'T13',rbfx+'T23'] |
---|
473 | if 'L' in name: |
---|
474 | namelist = [rbfx+'L11',rbfx+'L22',rbfx+'L33',rbfx+'L12',rbfx+'L13',rbfx+'L23'] |
---|
475 | if 'S' in name: |
---|
476 | namelist = [rbfx+'S12',rbfx+'S13',rbfx+'S21',rbfx+'S23',rbfx+'S31',rbfx+'S32',rbfx+'SAA',rbfx+'SBB'] |
---|
477 | if 'U' in name: |
---|
478 | namelist = [rbfx+'U',] |
---|
479 | |
---|
480 | for item in nameList: |
---|
481 | keys = item.split(':') |
---|
482 | #if keys[0] not in phlist: |
---|
483 | # phlist.append(keys[0]) |
---|
484 | if items[1] == '*' and keys[2] in namelist: # wildcard -- select only sequential options |
---|
485 | keys[1] = '*' |
---|
486 | mitem = ':'.join(keys) |
---|
487 | if mitem == name: continue |
---|
488 | if mitem not in outList: outList.append(mitem) |
---|
489 | elif keys[2] in namelist and item != name: |
---|
490 | outList.append(item) |
---|
491 | return outList |
---|
492 | |
---|
493 | def SelectVarbs(page,FrstVarb,varList,legend,constType): |
---|
494 | '''Select variables used in constraints after one variable has |
---|
495 | been selected. This routine determines the appropriate variables to be |
---|
496 | used based on the one that has been selected and asks for more to be added. |
---|
497 | |
---|
498 | It then creates the constraint and adds it to the constraints list. |
---|
499 | |
---|
500 | Called from OnAddEquivalence, OnAddFunction & OnAddConstraint (all but |
---|
501 | OnAddHold) |
---|
502 | |
---|
503 | :param list page: defines calling page -- type of variables to be used |
---|
504 | :parm GSASIIobj.G2VarObj FrstVarb: reference to first selected variable |
---|
505 | :param list varList: list of other appropriate variables to select from |
---|
506 | :param str legend: header for selection dialog |
---|
507 | :param str constType: type of constraint to be generated |
---|
508 | :returns: a constraint, as defined in |
---|
509 | :ref:`GSASIIobj <Constraint_definitions_table>` |
---|
510 | ''' |
---|
511 | choices = [[i]+list(G2obj.VarDescr(i)) for i in varList] |
---|
512 | meaning = G2obj.getDescr(FrstVarb.name) |
---|
513 | if not meaning: |
---|
514 | meaning = "(no definition found!)" |
---|
515 | l = str(FrstVarb).split(':') |
---|
516 | # make lists of phases & histograms to iterate over |
---|
517 | phaselist = [l[0]] |
---|
518 | if l[0]: |
---|
519 | phaselbl = ['phase #'+l[0]] |
---|
520 | if len(Phases) > 1: |
---|
521 | phaselist += ['all'] |
---|
522 | phaselbl += ['all phases'] |
---|
523 | else: |
---|
524 | phaselbl = [''] |
---|
525 | histlist = [l[1]] |
---|
526 | if l[1] == '*': |
---|
527 | pass |
---|
528 | elif l[1]: |
---|
529 | histlbl = ['histogram #'+l[1]] |
---|
530 | if len(Histograms) > 1: |
---|
531 | histlist += ['all'] |
---|
532 | histlbl += ['all histograms'] |
---|
533 | typ = Histograms[G2obj.LookupHistName(l[1])[0]]['Instrument Parameters'][0]['Type'][1] |
---|
534 | i = 0 |
---|
535 | for hist in Histograms: |
---|
536 | if Histograms[hist]['Instrument Parameters'][0]['Type'][1] == typ: i += 1 |
---|
537 | if i > 1: |
---|
538 | histlist += ['all='+typ] |
---|
539 | histlbl += ['all '+typ+' histograms'] |
---|
540 | else: |
---|
541 | histlbl = [''] |
---|
542 | # make a list of equivalent parameter names |
---|
543 | nameList = [FrstVarb.name] |
---|
544 | for var in varList: |
---|
545 | nam = var.split(":")[2] |
---|
546 | if nam not in nameList: nameList += [nam] |
---|
547 | # add "wild-card" names to the list of variables |
---|
548 | if l[1] == '*': |
---|
549 | pass |
---|
550 | elif page[1] == 'phs': |
---|
551 | if 'RB' in FrstVarb.name: |
---|
552 | pass |
---|
553 | elif FrstVarb.atom is None: |
---|
554 | for nam in nameList: |
---|
555 | for ph,plbl in zip(phaselist,phaselbl): |
---|
556 | if plbl: plbl = 'For ' + plbl |
---|
557 | var = ph+"::"+nam |
---|
558 | if var == str(FrstVarb) or var in varList: continue |
---|
559 | varList += [var] |
---|
560 | choices.append([var,plbl,meaning]) |
---|
561 | else: |
---|
562 | for nam in nameList: |
---|
563 | for ph,plbl in zip(phaselist,phaselbl): |
---|
564 | if plbl: plbl = ' in ' + plbl |
---|
565 | for atype in ['']+TypeList: |
---|
566 | if atype: |
---|
567 | albl = "For "+atype+" atoms" |
---|
568 | akey = "all="+atype |
---|
569 | else: |
---|
570 | albl = "For all atoms" |
---|
571 | akey = "all" |
---|
572 | var = ph+"::"+nam+":"+akey |
---|
573 | if var == str(FrstVarb) or var in varList: continue |
---|
574 | varList += [var] |
---|
575 | choices.append([var,albl+plbl,meaning]) |
---|
576 | elif page[1] == 'hap': |
---|
577 | if FrstVarb.name == "Scale": |
---|
578 | meaning = "Phase fraction" |
---|
579 | for nam in nameList: |
---|
580 | for ph,plbl in zip(phaselist,phaselbl): |
---|
581 | if plbl: plbl = 'For ' + plbl |
---|
582 | for hst,hlbl in zip(histlist,histlbl): |
---|
583 | if hlbl: |
---|
584 | if plbl: |
---|
585 | hlbl = ' in ' + hlbl |
---|
586 | else: |
---|
587 | hlbl = 'For ' + hlbl |
---|
588 | var = ph+":"+hst+":"+nam |
---|
589 | if var == str(FrstVarb) or var in varList: continue |
---|
590 | varList += [var] |
---|
591 | choices.append([var,plbl+hlbl,meaning]) |
---|
592 | elif page[1] == 'hst': |
---|
593 | if FrstVarb.name == "Scale": |
---|
594 | meaning = "Scale factor" |
---|
595 | for nam in nameList: |
---|
596 | for hst,hlbl in zip(histlist,histlbl): |
---|
597 | if hlbl: |
---|
598 | hlbl = 'For ' + hlbl |
---|
599 | var = ":"+hst+":"+nam |
---|
600 | if var == str(FrstVarb) or var in varList: continue |
---|
601 | varList += [var] |
---|
602 | choices.append([var,hlbl,meaning]) |
---|
603 | elif page[1] == 'glb' or page[1] == 'sym': |
---|
604 | pass |
---|
605 | else: |
---|
606 | raise Exception('Unknown constraint page '+ page[1]) |
---|
607 | if len(choices): |
---|
608 | l1 = l2 = 1 |
---|
609 | for i1,i2,i3 in choices: |
---|
610 | l1 = max(l1,len(i1)) |
---|
611 | l2 = max(l2,len(i2)) |
---|
612 | fmt = "{:"+str(l1)+"s} {:"+str(l2)+"s} {:s}" |
---|
613 | atchoices = [fmt.format(*i1) for i1 in choices] # reformat list as str with columns |
---|
614 | dlg = G2G.G2MultiChoiceDialog( |
---|
615 | G2frame,legend, |
---|
616 | 'Constrain '+str(FrstVarb)+' with...',atchoices, |
---|
617 | toggle=False,size=(625,400),monoFont=True) |
---|
618 | dlg.CenterOnParent() |
---|
619 | res = dlg.ShowModal() |
---|
620 | Selections = dlg.GetSelections()[:] |
---|
621 | dlg.Destroy() |
---|
622 | if res != wx.ID_OK: return [] |
---|
623 | if len(Selections) == 0: |
---|
624 | dlg = wx.MessageDialog( |
---|
625 | G2frame, |
---|
626 | 'No variables were selected to include with '+str(FrstVarb), |
---|
627 | 'No variables') |
---|
628 | dlg.CenterOnParent() |
---|
629 | dlg.ShowModal() |
---|
630 | dlg.Destroy() |
---|
631 | return [] |
---|
632 | else: |
---|
633 | dlg = wx.MessageDialog( |
---|
634 | G2frame, |
---|
635 | 'There are no appropriate variables to include with '+str(FrstVarb), |
---|
636 | 'No variables') |
---|
637 | dlg.CenterOnParent() |
---|
638 | dlg.ShowModal() |
---|
639 | dlg.Destroy() |
---|
640 | return [] |
---|
641 | # now process the variables provided by the user |
---|
642 | varbs = [str(FrstVarb),] # list of selected variables |
---|
643 | for sel in Selections: |
---|
644 | var = varList[sel] |
---|
645 | # phase(s) included |
---|
646 | l = var.split(':') |
---|
647 | if l[0] == "all": |
---|
648 | phlist = [str(Phases[phase]['pId']) for phase in Phases] |
---|
649 | else: |
---|
650 | phlist = [l[0]] |
---|
651 | # histogram(s) included |
---|
652 | if l[1] == "all": |
---|
653 | hstlist = [str(Histograms[hist]['hId']) for hist in Histograms] |
---|
654 | elif '=' in l[1]: |
---|
655 | htyp = l[1].split('=')[1] |
---|
656 | hstlist = [str(Histograms[hist]['hId']) for hist in Histograms if |
---|
657 | Histograms[hist]['Instrument Parameters'][0]['Type'][1] == htyp] |
---|
658 | else: |
---|
659 | hstlist = [l[1]] |
---|
660 | if len(l) == 3: |
---|
661 | for ph in phlist: |
---|
662 | for hst in hstlist: |
---|
663 | var = ph + ":" + hst + ":" + l[2] |
---|
664 | if var in varbs: continue |
---|
665 | varbs.append(var) |
---|
666 | else: # constraints with atoms or rigid bodies |
---|
667 | if len(l) == 5: # rigid body parameter |
---|
668 | var = ':'.join(l) |
---|
669 | if var in varbs: continue |
---|
670 | varbs.append(var) |
---|
671 | elif l[3] == "all": |
---|
672 | for ph in phlist: |
---|
673 | key = G2obj.LookupPhaseName(ph)[0] |
---|
674 | for hst in hstlist: # should be blank |
---|
675 | for iatm,at in enumerate(Phases[key]['Atoms']): |
---|
676 | var = ph + ":" + hst + ":" + l[2] + ":" + str(iatm) |
---|
677 | if var in varbs: continue |
---|
678 | varbs.append(var) |
---|
679 | elif '=' in l[3]: |
---|
680 | for ph in phlist: |
---|
681 | key = G2obj.LookupPhaseName(ph)[0] |
---|
682 | cx,ct,cs,cia = Phases[key]['General']['AtomPtrs'] |
---|
683 | for hst in hstlist: # should be blank |
---|
684 | atyp = l[3].split('=')[1] |
---|
685 | for iatm,at in enumerate(Phases[key]['Atoms']): |
---|
686 | if at[ct] != atyp: continue |
---|
687 | var = ph + ":" + hst + ":" + l[2] + ":" + str(iatm) |
---|
688 | if var in varbs: continue |
---|
689 | varbs.append(var) |
---|
690 | else: |
---|
691 | for ph in phlist: |
---|
692 | key = G2obj.LookupPhaseName(ph)[0] |
---|
693 | for hst in hstlist: # should be blank |
---|
694 | var = ph + ":" + hst + ":" + l[2] + ":" + l[3] |
---|
695 | if var in varbs: continue |
---|
696 | varbs.append(var) |
---|
697 | if len(varbs) >= 1 or 'constraint' in constType: |
---|
698 | constr = [[1.0,FrstVarb]] |
---|
699 | for item in varbs[1:]: |
---|
700 | constr += [[1.0,G2obj.G2VarObj(item)]] |
---|
701 | if 'equivalence' in constType: |
---|
702 | return [constr+[None,None,'e']] |
---|
703 | elif 'function' in constType: |
---|
704 | return [constr+[None,False,'f']] |
---|
705 | elif 'constraint' in constType: |
---|
706 | return [constr+[1.0,None,'c']] |
---|
707 | else: |
---|
708 | raise Exception('Unknown constraint type: '+str(constType)) |
---|
709 | else: |
---|
710 | dlg = wx.MessageDialog( |
---|
711 | G2frame, |
---|
712 | 'There are no selected variables to include with '+str(FrstVarb), |
---|
713 | 'No variables') |
---|
714 | dlg.CenterOnParent() |
---|
715 | dlg.ShowModal() |
---|
716 | dlg.Destroy() |
---|
717 | return [] |
---|
718 | |
---|
719 | def CheckAddedConstraint(newcons): |
---|
720 | '''Check a new constraint that has just been input. |
---|
721 | If there is an error display a message and discard the last entry |
---|
722 | |
---|
723 | Since the varylist is not available, no warning messages |
---|
724 | should be generated here |
---|
725 | |
---|
726 | :returns: True if constraint should be added |
---|
727 | ''' |
---|
728 | |
---|
729 | errmsg,warnmsg = CheckConstraints(G2frame,Phases,Histograms,data,newcons,seqhst=seqhistnum,seqmode=seqmode) |
---|
730 | if errmsg: |
---|
731 | G2frame.ErrorDialog('Constraint Error', |
---|
732 | 'Error with newly added constraint:\n'+errmsg+ |
---|
733 | '\nIgnoring newly added constraint',parent=G2frame) |
---|
734 | # reset error status |
---|
735 | errmsg,warnmsg = CheckConstraints(G2frame,Phases,Histograms,data,seqhst=seqhistnum,seqmode=seqmode) |
---|
736 | if errmsg: |
---|
737 | print (errmsg) |
---|
738 | print (G2mv.VarRemapShow([],True)) |
---|
739 | return False |
---|
740 | elif warnmsg: |
---|
741 | print ('Unexpected contraint warning:\n'+warnmsg) |
---|
742 | return True |
---|
743 | |
---|
744 | def WarnConstraintLimit(): |
---|
745 | '''Check if constraints reference variables with limits. |
---|
746 | Displays a warning message, but does nothing |
---|
747 | ''' |
---|
748 | parmDict,reqVaryList = G2frame.MakeLSParmDict() |
---|
749 | try: |
---|
750 | errmsg,warnmsg = CheckConstraints(G2frame,Phases,Histograms,data,[],reqVaryList,seqhst=seqhistnum,seqmode=seqmode) |
---|
751 | except Exception as msg: |
---|
752 | return 'CheckConstraints error retrieving parameter\nError='+str(msg),'' |
---|
753 | return errmsg,warnmsg |
---|
754 | |
---|
755 | def CheckChangedConstraint(): |
---|
756 | '''Check all constraints after an edit has been made. |
---|
757 | If there is an error display a message and reject the change. |
---|
758 | |
---|
759 | Since the varylist is not available, no warning messages |
---|
760 | should be generated. |
---|
761 | |
---|
762 | :returns: True if the edit should be retained |
---|
763 | ''' |
---|
764 | errmsg,warnmsg = CheckConstraints(G2frame,Phases,Histograms,data,seqhst=seqhistnum,seqmode=seqmode) |
---|
765 | if errmsg: |
---|
766 | G2frame.ErrorDialog('Constraint Error', |
---|
767 | 'Error after editing constraint:\n'+errmsg+ |
---|
768 | '\nDiscarding last constraint edit',parent=G2frame) |
---|
769 | # reset error status |
---|
770 | errmsg,warnmsg = CheckConstraints(G2frame,Phases,Histograms,data,seqhst=seqhistnum,seqmode=seqmode) |
---|
771 | if errmsg: |
---|
772 | print (errmsg) |
---|
773 | print (G2mv.VarRemapShow([],True)) |
---|
774 | return False |
---|
775 | elif warnmsg: |
---|
776 | print ('Unexpected contraint warning:\n'+warnmsg) |
---|
777 | return True |
---|
778 | |
---|
779 | def PageSelection(page): |
---|
780 | 'Decode page reference' |
---|
781 | if page[1] == "phs": |
---|
782 | vartype = "phase" |
---|
783 | varList = G2obj.removeNonRefined(phaseList) # remove any non-refinable prms from list |
---|
784 | constrDictEnt = 'Phase' |
---|
785 | elif page[1] == "hap": |
---|
786 | vartype = "Histogram*Phase" |
---|
787 | varList = G2obj.removeNonRefined(hapList) # remove any non-refinable prms from list |
---|
788 | constrDictEnt = 'HAP' |
---|
789 | elif page[1] == "hst": |
---|
790 | vartype = "Histogram" |
---|
791 | varList = G2obj.removeNonRefined(histList) # remove any non-refinable prms from list |
---|
792 | constrDictEnt = 'Hist' |
---|
793 | elif page[1] == "glb": |
---|
794 | vartype = "Global" |
---|
795 | varList = G2obj.removeNonRefined(globalList) # remove any non-refinable prms from list |
---|
796 | |
---|
797 | constrDictEnt = 'Global' |
---|
798 | elif page[1] == "sym": |
---|
799 | return None,None,None |
---|
800 | else: |
---|
801 | raise Exception('Should not happen!') |
---|
802 | return vartype,varList,constrDictEnt |
---|
803 | |
---|
804 | def OnAddHold(event): |
---|
805 | '''Create a new Hold constraint |
---|
806 | |
---|
807 | Hold constraints allows the user to select one variable (the list of available |
---|
808 | variables depends on which tab is currently active). |
---|
809 | ''' |
---|
810 | page = G2frame.Page |
---|
811 | vartype,varList,constrDictEnt = PageSelection(page) |
---|
812 | if vartype is None: return |
---|
813 | varList = G2obj.SortVariables(varList) |
---|
814 | title1 = "Hold "+vartype+" variable" |
---|
815 | if not varList: |
---|
816 | G2frame.ErrorDialog('No variables','There are no variables of type '+vartype, |
---|
817 | parent=G2frame) |
---|
818 | return |
---|
819 | l2 = l1 = 1 |
---|
820 | for i in varList: |
---|
821 | l1 = max(l1,len(i)) |
---|
822 | loc,desc = G2obj.VarDescr(i) |
---|
823 | l2 = max(l2,len(loc)) |
---|
824 | fmt = "{:"+str(l1)+"s} {:"+str(l2)+"s} {:s}" |
---|
825 | varListlbl = [fmt.format(i,*G2obj.VarDescr(i)) for i in varList] |
---|
826 | #varListlbl = ["("+i+") "+G2obj.fmtVarDescr(i) for i in varList] |
---|
827 | legend = "Select variables to hold (Will not be varied, even if vary flag is set)" |
---|
828 | dlg = G2G.G2MultiChoiceDialog(G2frame, |
---|
829 | legend,title1,varListlbl,toggle=False,size=(625,400),monoFont=True) |
---|
830 | dlg.CenterOnParent() |
---|
831 | if dlg.ShowModal() == wx.ID_OK: |
---|
832 | for sel in dlg.GetSelections(): |
---|
833 | Varb = varList[sel] |
---|
834 | VarObj = G2obj.G2VarObj(Varb) |
---|
835 | newcons = [[[0.0,VarObj],None,None,'h']] |
---|
836 | if CheckAddedConstraint(newcons): |
---|
837 | data[constrDictEnt] += newcons |
---|
838 | dlg.Destroy() |
---|
839 | wx.CallAfter(OnPageChanged,None) |
---|
840 | |
---|
841 | def OnAddEquivalence(event): |
---|
842 | '''add an Equivalence constraint''' |
---|
843 | page = G2frame.Page |
---|
844 | vartype,varList,constrDictEnt = PageSelection(page) |
---|
845 | if vartype is None: return |
---|
846 | title1 = "Create equivalence constraint between "+vartype+" variables" |
---|
847 | title2 = "Select additional "+vartype+" variable(s) to be equivalent with " |
---|
848 | if not varList: |
---|
849 | G2frame.ErrorDialog('No variables','There are no variables of type '+vartype, |
---|
850 | parent=G2frame) |
---|
851 | return |
---|
852 | # legend = "Select variables to make equivalent (only one of the variables will be varied when all are set to be varied)" |
---|
853 | GetAddVars(page,title1,title2,varList,constrDictEnt,'equivalence') |
---|
854 | |
---|
855 | def OnAddAtomEquiv(event): |
---|
856 | ''' Add equivalences between all parameters on atoms ''' |
---|
857 | page = G2frame.Page |
---|
858 | vartype,varList,constrDictEnt = PageSelection(page) |
---|
859 | if vartype is None: return |
---|
860 | title1 = "Setup equivalent atom variables" |
---|
861 | title2 = "Select additional atoms(s) to be equivalent with " |
---|
862 | if not varList: |
---|
863 | G2frame.ErrorDialog('No variables','There are no variables of type '+vartype, |
---|
864 | parent=G2frame) |
---|
865 | return |
---|
866 | # legend = "Select atoms to make equivalent (only one of the atom variables will be varied when all are set to be varied)" |
---|
867 | GetAddAtomVars(page,title1,title2,varList,constrDictEnt,'equivalence') |
---|
868 | |
---|
869 | def OnAddRiding(event): |
---|
870 | ''' Add riding equivalences between all parameters on atoms - not currently used''' |
---|
871 | page = G2frame.Page |
---|
872 | vartype,varList,constrDictEnt = PageSelection(page) |
---|
873 | if vartype is None: return |
---|
874 | title1 = "Setup riding atoms " |
---|
875 | title2 = "Select additional atoms(s) to ride on " |
---|
876 | if not varList: |
---|
877 | G2frame.ErrorDialog('No variables','There are no variables of type '+vartype, |
---|
878 | parent=G2frame) |
---|
879 | return |
---|
880 | # legend = "Select atoms to ride (only one of the atom variables will be varied when all are set to be varied)" |
---|
881 | GetAddAtomVars(page,title1,title2,varList,constrDictEnt,'riding') |
---|
882 | |
---|
883 | def OnAddFunction(event): |
---|
884 | '''add a Function (new variable) constraint''' |
---|
885 | page = G2frame.Page |
---|
886 | vartype,varList,constrDictEnt = PageSelection(page) |
---|
887 | if vartype is None: return |
---|
888 | title1 = "Setup new variable based on "+vartype+" variables" |
---|
889 | title2 = "Include additional "+vartype+" variable(s) to be included with " |
---|
890 | if not varList: |
---|
891 | G2frame.ErrorDialog('No variables','There are no variables of type '+vartype, |
---|
892 | parent=G2frame) |
---|
893 | return |
---|
894 | # legend = "Select variables to include in a new variable (the new variable will be varied when all included variables are varied)" |
---|
895 | GetAddVars(page,title1,title2,varList,constrDictEnt,'function') |
---|
896 | |
---|
897 | def OnAddConstraint(event): |
---|
898 | '''add a constraint equation to the constraints list''' |
---|
899 | page = G2frame.Page |
---|
900 | vartype,varList,constrDictEnt = PageSelection(page) |
---|
901 | if vartype is None: return |
---|
902 | title1 = "Creating constraint on "+vartype+" variables" |
---|
903 | title2 = "Select additional "+vartype+" variable(s) to include in constraint with " |
---|
904 | if not varList: |
---|
905 | G2frame.ErrorDialog('No variables','There are no variables of type '+vartype, |
---|
906 | parent=G2frame) |
---|
907 | return |
---|
908 | # legend = "Select variables to include in a constraint equation (the values will be constrainted to equal a specified constant)" |
---|
909 | GetAddVars(page,title1,title2,varList,constrDictEnt,'constraint') |
---|
910 | |
---|
911 | def GetAddVars(page,title1,title2,varList,constrDictEnt,constType): |
---|
912 | '''Get the variables to be added for OnAddEquivalence, OnAddFunction, |
---|
913 | and OnAddConstraint. Then create and check the constraint. |
---|
914 | ''' |
---|
915 | #varListlbl = ["("+i+") "+G2obj.fmtVarDescr(i) for i in varList] |
---|
916 | if constType == 'equivalence': |
---|
917 | omitVars = G2mv.GetDependentVars() |
---|
918 | else: |
---|
919 | omitVars = [] |
---|
920 | varList = G2obj.SortVariables([i for i in varList if i not in omitVars]) |
---|
921 | l2 = l1 = 1 |
---|
922 | for i in varList: |
---|
923 | l1 = max(l1,len(i)) |
---|
924 | loc,desc = G2obj.VarDescr(i) |
---|
925 | l2 = max(l2,len(loc)) |
---|
926 | fmt = "{:"+str(l1)+"s} {:"+str(l2)+"s} {:s}" |
---|
927 | varListlbl = [fmt.format(i,*G2obj.VarDescr(i)) for i in varList] |
---|
928 | dlg = G2G.G2SingleChoiceDialog(G2frame,'Select 1st variable:', |
---|
929 | title1,varListlbl,monoFont=True,size=(625,400)) |
---|
930 | dlg.CenterOnParent() |
---|
931 | if dlg.ShowModal() == wx.ID_OK: |
---|
932 | if constType == 'equivalence': |
---|
933 | omitVars = G2mv.GetDependentVars() + G2mv.GetIndependentVars() |
---|
934 | sel = dlg.GetSelection() |
---|
935 | FrstVarb = varList[sel] |
---|
936 | VarObj = G2obj.G2VarObj(FrstVarb) |
---|
937 | moreVarb = G2obj.SortVariables(FindEquivVarb(FrstVarb,[i for i in varList if i not in omitVars])) |
---|
938 | newcons = SelectVarbs(page,VarObj,moreVarb,title2+FrstVarb,constType) |
---|
939 | if len(newcons) > 0: |
---|
940 | if CheckAddedConstraint(newcons): |
---|
941 | data[constrDictEnt] += newcons |
---|
942 | dlg.Destroy() |
---|
943 | WarnConstraintLimit() |
---|
944 | wx.CallAfter(OnPageChanged,None) |
---|
945 | |
---|
946 | def FindNeighbors(phase,FrstName,AtNames): |
---|
947 | General = phase['General'] |
---|
948 | cx,ct,cs,cia = General['AtomPtrs'] |
---|
949 | Atoms = phase['Atoms'] |
---|
950 | atNames = [atom[ct-1] for atom in Atoms] |
---|
951 | Cell = General['Cell'][1:7] |
---|
952 | Amat,Bmat = G2lat.cell2AB(Cell) |
---|
953 | atTypes = General['AtomTypes'] |
---|
954 | Radii = np.array(General['BondRadii']) |
---|
955 | AtInfo = dict(zip(atTypes,Radii)) #or General['BondRadii'] |
---|
956 | Orig = atNames.index(FrstName.split()[1]) |
---|
957 | OType = Atoms[Orig][ct] |
---|
958 | XYZ = G2mth.getAtomXYZ(Atoms,cx) |
---|
959 | Neigh = [] |
---|
960 | Dx = np.inner(Amat,XYZ-XYZ[Orig]).T |
---|
961 | dist = np.sqrt(np.sum(Dx**2,axis=1)) |
---|
962 | sumR = AtInfo[OType]+0.5 #H-atoms only! |
---|
963 | IndB = ma.nonzero(ma.masked_greater(dist-0.85*sumR,0.)) |
---|
964 | for j in IndB[0]: |
---|
965 | if j != Orig: |
---|
966 | Neigh.append(AtNames[j]) |
---|
967 | return Neigh |
---|
968 | |
---|
969 | def GetAddAtomVars(page,title1,title2,varList,constrDictEnt,constType): |
---|
970 | '''Get the atom variables to be added for OnAddAtomEquiv. Then create and |
---|
971 | check the constraints. Riding for H atoms only. |
---|
972 | ''' |
---|
973 | Atoms = {G2obj.VarDescr(i)[0]:[] for i in varList if 'Atom' in G2obj.VarDescr(i)[0]} |
---|
974 | for item in varList: |
---|
975 | atName = G2obj.VarDescr(item)[0] |
---|
976 | if atName in Atoms: |
---|
977 | Atoms[atName].append(item) |
---|
978 | AtNames = list(Atoms.keys()) |
---|
979 | AtNames.sort() |
---|
980 | dlg = G2G.G2SingleChoiceDialog(G2frame,'Select 1st atom:', |
---|
981 | title1,AtNames,monoFont=True,size=(625,400)) |
---|
982 | dlg.CenterOnParent() |
---|
983 | FrstAtom = '' |
---|
984 | if dlg.ShowModal() == wx.ID_OK: |
---|
985 | sel = dlg.GetSelection() |
---|
986 | FrstAtom = AtNames[sel] |
---|
987 | if 'riding' in constType: |
---|
988 | phaseName = (FrstAtom.split(' in ')[1]).strip() |
---|
989 | phase = Phases[phaseName] |
---|
990 | AtNames = FindNeighbors(phase,FrstAtom,AtNames) |
---|
991 | else: |
---|
992 | AtNames.remove(FrstAtom) |
---|
993 | dlg.Destroy() |
---|
994 | if FrstAtom == '': |
---|
995 | print ('no atom selected') |
---|
996 | return |
---|
997 | dlg = G2G.G2MultiChoiceDialog( |
---|
998 | G2frame,title2+FrstAtom, |
---|
999 | 'Constrain '+str(FrstAtom)+' with...',AtNames, |
---|
1000 | toggle=False,size=(625,400),monoFont=True) |
---|
1001 | if dlg.ShowModal() == wx.ID_OK: |
---|
1002 | Selections = dlg.GetSelections()[:] |
---|
1003 | else: |
---|
1004 | print ('no target atom selected') |
---|
1005 | dlg.Destroy() |
---|
1006 | return |
---|
1007 | dlg.Destroy() |
---|
1008 | for name in Atoms[FrstAtom]: |
---|
1009 | newcons = [] |
---|
1010 | constr = [] |
---|
1011 | if 'riding' in constType: |
---|
1012 | if 'AUiso' in name: |
---|
1013 | constr = [[1.0,G2obj.G2VarObj(name)]] |
---|
1014 | elif 'AU11' in name: |
---|
1015 | pass |
---|
1016 | elif 'AU' not in name: |
---|
1017 | constr = [[1.0,G2obj.G2VarObj(name)]] |
---|
1018 | else: |
---|
1019 | constr = [[1.0,G2obj.G2VarObj(name)]] |
---|
1020 | pref = ':'+name.rsplit(':',1)[0].split(':',1)[1] #get stuff between phase id & atom id |
---|
1021 | for sel in Selections: |
---|
1022 | name2 = Atoms[AtNames[sel]][0] |
---|
1023 | pid = name2.split(':',1)[0] #get phase id for 2nd atom |
---|
1024 | id = name2.rsplit(':',1)[-1] #get atom no. for 2nd atom |
---|
1025 | if 'riding' in constType: |
---|
1026 | pref = pid+pref |
---|
1027 | if 'AUiso' in pref: |
---|
1028 | parts = pref.split('AUiso') |
---|
1029 | constr += [[1.2,G2obj.G2VarObj('%s:%s'%(parts[0]+'AUiso',id))]] |
---|
1030 | elif 'AU' not in pref: |
---|
1031 | constr += [[1.0,G2obj.G2VarObj('%s:%s'%(pref,id))]] |
---|
1032 | else: |
---|
1033 | constr += [[1.0,G2obj.G2VarObj('%s:%s'%(pid+pref,id))]] |
---|
1034 | if not constr: |
---|
1035 | continue |
---|
1036 | if 'frac' in pref and 'riding' not in constType: |
---|
1037 | newcons = [constr+[1.0,None,'c']] |
---|
1038 | else: |
---|
1039 | newcons = [constr+[None,None,'e']] |
---|
1040 | if len(newcons) > 0: |
---|
1041 | if CheckAddedConstraint(newcons): |
---|
1042 | data[constrDictEnt] += newcons |
---|
1043 | WarnConstraintLimit() |
---|
1044 | wx.CallAfter(OnPageChanged,None) |
---|
1045 | |
---|
1046 | def MakeConstraintsSizer(name,panel): |
---|
1047 | '''Creates a sizer displaying all of the constraints entered of |
---|
1048 | the specified type. |
---|
1049 | |
---|
1050 | :param str name: the type of constraints to be displayed ('HAP', |
---|
1051 | 'Hist', 'Phase', 'Global', 'Sym-Generated') |
---|
1052 | :param wx.Panel panel: parent panel for sizer |
---|
1053 | :returns: wx.Sizer created by method |
---|
1054 | ''' |
---|
1055 | if name == 'Sym-Generated': #show symmetry generated constraints |
---|
1056 | Sizer1 = wx.BoxSizer(wx.VERTICAL) |
---|
1057 | if symHolds: |
---|
1058 | Sizer1.Add(wx.StaticText(panel,wx.ID_ANY, |
---|
1059 | 'Position variables fixed by space group symmetry')) |
---|
1060 | Sizer1.Add((-1,5)) |
---|
1061 | Sizer = wx.FlexGridSizer(0,3,0,0) |
---|
1062 | Sizer1.Add(Sizer) |
---|
1063 | for var in symHolds: |
---|
1064 | varMean = G2obj.fmtVarDescr(var) |
---|
1065 | helptext = "Prevents variable: "+ var + " ("+ varMean + ")\nfrom being changed" |
---|
1066 | ch = G2G.HelpButton(panel,helptext) |
---|
1067 | Sizer.Add(ch,0,wx.LEFT|wx.RIGHT|WACV|wx.ALIGN_CENTER,1) |
---|
1068 | Sizer.Add(wx.StaticText(panel,wx.ID_ANY,'FIXED'), |
---|
1069 | 0,WACV|wx.ALIGN_CENTER|wx.RIGHT|wx.LEFT,2) |
---|
1070 | Sizer.Add(wx.StaticText(panel,wx.ID_ANY,var), |
---|
1071 | 0,WACV|wx.ALIGN_CENTER|wx.RIGHT|wx.LEFT) |
---|
1072 | else: |
---|
1073 | Sizer1.Add(wx.StaticText(panel,wx.ID_ANY, |
---|
1074 | 'No holds generated')) |
---|
1075 | Sizer1.Add((-1,10)) |
---|
1076 | symGen,SymErr,SymHelp = G2mv.GetSymEquiv(seqmode,seqhistnum) |
---|
1077 | if len(symGen) == 0: |
---|
1078 | Sizer1.Add(wx.StaticText(panel,wx.ID_ANY, |
---|
1079 | 'No equivalences generated')) |
---|
1080 | return Sizer1 |
---|
1081 | Sizer1.Add(wx.StaticText(panel,wx.ID_ANY, |
---|
1082 | 'Equivalences generated based on cell/space group input')) |
---|
1083 | Sizer1.Add((-1,5)) |
---|
1084 | Sizer = wx.FlexGridSizer(0,5,0,0) |
---|
1085 | Sizer1.Add(Sizer) |
---|
1086 | helptext = '' |
---|
1087 | for sym,(warnmsg,note),helptext in zip(symGen,SymErr,SymHelp): |
---|
1088 | if warnmsg: |
---|
1089 | if helptext: helptext += '\n\n' |
---|
1090 | helptext += warnmsg |
---|
1091 | if helptext: |
---|
1092 | ch = G2G.HelpButton(panel,helptext) |
---|
1093 | Sizer.Add(ch,0,wx.LEFT|wx.RIGHT|WACV|wx.ALIGN_CENTER,1) |
---|
1094 | else: |
---|
1095 | Sizer.Add((-1,-1)) |
---|
1096 | Sizer.Add(wx.StaticText(panel,wx.ID_ANY,'EQUIV'), |
---|
1097 | 0,WACV|wx.ALIGN_CENTER|wx.RIGHT|wx.LEFT,2) |
---|
1098 | Sizer.Add(wx.StaticText(panel,wx.ID_ANY,sym), |
---|
1099 | 0,WACV|wx.ALIGN_LEFT|wx.RIGHT|wx.LEFT,2) |
---|
1100 | if note: |
---|
1101 | Sizer.Add(wx.StaticText(panel,wx.ID_ANY,note), |
---|
1102 | 0,WACV|wx.ALIGN_LEFT|wx.RIGHT|wx.LEFT,2) |
---|
1103 | else: |
---|
1104 | Sizer.Add((-1,-1)) |
---|
1105 | Sizer.Add((-1,-1)) |
---|
1106 | return Sizer1 |
---|
1107 | constSizer = wx.FlexGridSizer(0,8,0,0) |
---|
1108 | maxlen = 50 # characters before wrapping a constraint |
---|
1109 | for Id,item in enumerate(data[name]): |
---|
1110 | refineflag = False |
---|
1111 | helptext = "" |
---|
1112 | eqString = ['',] |
---|
1113 | problemItem, warnmsg, note = G2mv.getConstrError(item,seqmode,seqhistnum) |
---|
1114 | #badVar = False |
---|
1115 | #for term in item[:-3]: |
---|
1116 | # if str(term[1]) in G2mv.problemVars: |
---|
1117 | # problemItem = True |
---|
1118 | if item[-1] == 'h': # Hold on variable |
---|
1119 | constSizer.Add((-1,-1),0) # blank space for edit button |
---|
1120 | typeString = 'FIXED' |
---|
1121 | #var = str(item[0][1]) |
---|
1122 | var,explain,note,warnmsg = item[0][1].fmtVarByMode(seqmode,note,warnmsg) |
---|
1123 | #if '?' in var: badVar = True |
---|
1124 | varMean = G2obj.fmtVarDescr(var) |
---|
1125 | eqString[-1] = var +' ' |
---|
1126 | helptext = "Prevents variable:\n"+ var + " ("+ varMean + ")\nfrom being changed" |
---|
1127 | elif item[-1] == 'f' or item[-1] == 'e' or item[-1] == 'c': # not true on original-style (2011?) constraints |
---|
1128 | constEdit = wx.Button(panel,wx.ID_ANY,'Edit',style=wx.BU_EXACTFIT) |
---|
1129 | constEdit.Bind(wx.EVT_BUTTON,OnConstEdit) |
---|
1130 | constSizer.Add(constEdit,0,wx.LEFT|wx.RIGHT|WACV|wx.ALIGN_CENTER,1) # edit button |
---|
1131 | Indx[constEdit.GetId()] = [Id,name] |
---|
1132 | if item[-1] == 'f': |
---|
1133 | helptext = "A new variable" |
---|
1134 | if item[-3]: |
---|
1135 | helptext += " named "+str(item[-3]) |
---|
1136 | helptext += " is created from a linear combination of the following variables:\n" |
---|
1137 | for term in item[:-3]: |
---|
1138 | #var = str(term[1]) |
---|
1139 | var,explain,note,warnmsg = term[1].fmtVarByMode(seqmode,note,warnmsg) |
---|
1140 | #if '?' in var: badVar = True |
---|
1141 | if len(eqString[-1]) > maxlen: |
---|
1142 | eqString.append(' ') |
---|
1143 | m = term[0] |
---|
1144 | if eqString[-1] != '': |
---|
1145 | if m >= 0: |
---|
1146 | eqString[-1] += ' + ' |
---|
1147 | else: |
---|
1148 | eqString[-1] += ' - ' |
---|
1149 | m = abs(m) |
---|
1150 | if m == 1: |
---|
1151 | eqString[-1] += '{:} '.format(var) |
---|
1152 | else: |
---|
1153 | eqString[-1] += '{:g}*{:} '.format(m,var) |
---|
1154 | varMean = G2obj.fmtVarDescr(var) |
---|
1155 | helptext += '\n {:3g} * {:} '.format(m,var) + " ("+ varMean + ")" |
---|
1156 | # Add ISODISTORT help items |
---|
1157 | if '_Explain' in data: |
---|
1158 | # this ignores the phase number. TODO: refactor that in? |
---|
1159 | hlptxt = None |
---|
1160 | try: |
---|
1161 | hlptxt = data['_Explain'].get(item[-3]) |
---|
1162 | except TypeError: |
---|
1163 | # note that phase RanId is item[-3].phase |
---|
1164 | hlptxt = data['_Explain'].get(str(item[-3].phase)+item[-3].name) |
---|
1165 | if hlptxt: |
---|
1166 | if helptext: helptext += '\n\nNote warning:\n' |
---|
1167 | helptext += hlptxt |
---|
1168 | if item[-3]: |
---|
1169 | typeString = '(NEWVAR) ' + str(item[-3]) + ' = ' |
---|
1170 | else: |
---|
1171 | typeString = 'New Variable = ' |
---|
1172 | #print 'refine',item[-2] |
---|
1173 | refineflag = True |
---|
1174 | elif item[-1] == 'c': |
---|
1175 | helptext = "The following variables are constrained to equal a constant:" |
---|
1176 | for term in item[:-3]: |
---|
1177 | #var = str(term[1]) |
---|
1178 | var,explain,note,warnmsg = term[1].fmtVarByMode(seqmode,note,warnmsg) |
---|
1179 | #if '?' in var: badVar = True |
---|
1180 | if len(eqString[-1]) > maxlen: |
---|
1181 | eqString.append(' ') |
---|
1182 | m = term[0] |
---|
1183 | if eqString[-1] != '': |
---|
1184 | if term[0] > 0: |
---|
1185 | eqString[-1] += ' + ' |
---|
1186 | else: |
---|
1187 | eqString[-1] += ' - ' |
---|
1188 | m = -term[0] |
---|
1189 | if m == 1: |
---|
1190 | eqString[-1] += '{:} '.format(var) |
---|
1191 | else: |
---|
1192 | eqString[-1] += '{:g}*{:} '.format(m,var) |
---|
1193 | varMean = G2obj.fmtVarDescr(var) |
---|
1194 | helptext += '\n {:3g} * {:} '.format(m,var) + " ("+ varMean + ")" |
---|
1195 | helptext += explain |
---|
1196 | typeString = 'CONST' |
---|
1197 | eqString[-1] += ' = '+str(item[-3]) |
---|
1198 | elif item[-1] == 'e' and len(item[:-3]) == 2: |
---|
1199 | if item[0][0] == 0: item[0][0] = 1.0 |
---|
1200 | if item[1][0] == 0: item[1][0] = 1.0 |
---|
1201 | #var = str(item[0][1]) |
---|
1202 | var,explain,note,warnmsg = item[0][1].fmtVarByMode(seqmode,note,warnmsg) |
---|
1203 | #if '?' in var: badVar = True |
---|
1204 | helptext = 'Variable {:} '.format(var) + " ("+ G2obj.fmtVarDescr(var) + ")" |
---|
1205 | helptext += "\n\nis equivalent to " |
---|
1206 | m = item[0][0]/item[1][0] |
---|
1207 | #var1 = str(item[1][1]) |
---|
1208 | var1,explain,note,warnmsg = item[1][1].fmtVarByMode(seqmode,note,warnmsg) |
---|
1209 | helptext += '\n {:3g} * {:} '.format(m,var1) + " ("+ G2obj.fmtVarDescr(var1) + ")" |
---|
1210 | eqString[-1] += '{:} = {:}'.format(var1,var) |
---|
1211 | if m != 1: |
---|
1212 | eqString[-1] += ' / ' + str(m) |
---|
1213 | typeString = 'EQUIV' |
---|
1214 | elif item[-1] == 'e': |
---|
1215 | helptext = "The following variable:" |
---|
1216 | normval = item[0][0] |
---|
1217 | indepterm = item[0][1] |
---|
1218 | for i,term in enumerate(item[:-3]): |
---|
1219 | #var = str(term[1]) |
---|
1220 | var,explain,note,warnmsg = term[1].fmtVarByMode(seqmode,note,warnmsg) |
---|
1221 | #if '?' in var: badVar = True |
---|
1222 | if term[0] == 0: term[0] = 1.0 |
---|
1223 | if len(eqString[-1]) > maxlen: |
---|
1224 | eqString.append(' ') |
---|
1225 | varMean = G2obj.fmtVarDescr(var) |
---|
1226 | if i == 0: # move independent variable to end, as requested by Bob |
---|
1227 | helptext += '\n{:} '.format(var) + " ("+ varMean + ")" |
---|
1228 | helptext += "\n\nis equivalent to the following, noting multipliers:" |
---|
1229 | continue |
---|
1230 | elif eqString[-1] != '': |
---|
1231 | eqString[-1] += ' = ' |
---|
1232 | #m = normval/term[0] |
---|
1233 | m = term[0]/normval |
---|
1234 | if m == 1: |
---|
1235 | eqString[-1] += '{:}'.format(var) |
---|
1236 | else: |
---|
1237 | eqString[-1] += '{:g}*{:} '.format(m,var) |
---|
1238 | helptext += '\n {:3g} * {:} '.format(m,var) + " ("+ varMean + ")" |
---|
1239 | eqString[-1] += ' = {:} '.format(indepterm) |
---|
1240 | typeString = 'EQUIV' |
---|
1241 | else: |
---|
1242 | print ('Unexpected constraint'+item) |
---|
1243 | |
---|
1244 | else: |
---|
1245 | print ('Removing old-style constraints') |
---|
1246 | data[name] = [] |
---|
1247 | return constSizer |
---|
1248 | if warnmsg: |
---|
1249 | if helptext: helptext += '\n\nNote warning:\n' |
---|
1250 | helptext += warnmsg |
---|
1251 | if helptext: |
---|
1252 | ch = G2G.HelpButton(panel,helptext) |
---|
1253 | constSizer.Add(ch,0,wx.LEFT|wx.RIGHT|WACV|wx.ALIGN_CENTER,1) |
---|
1254 | else: |
---|
1255 | constSizer.Add((-1,-1)) |
---|
1256 | constDel = wx.CheckBox(panel,label='sel ') |
---|
1257 | constSizer.Add(constDel,0,wx.LEFT|wx.RIGHT|WACV|wx.ALIGN_CENTER,1) # delete selection |
---|
1258 | panel.delBtn.checkboxList.append([constDel,Id,name]) |
---|
1259 | if refineflag: |
---|
1260 | ch = G2G.G2CheckBox(panel,'vary ',item,-2) |
---|
1261 | constSizer.Add(ch,0,wx.LEFT|wx.RIGHT|WACV|wx.ALIGN_CENTER,1) |
---|
1262 | else: |
---|
1263 | constSizer.Add((-1,-1)) |
---|
1264 | constSizer.Add(wx.StaticText(panel,wx.ID_ANY,typeString), |
---|
1265 | 0,WACV|wx.ALIGN_CENTER|wx.RIGHT|wx.LEFT,3) |
---|
1266 | #if badVar: eqString[-1] += ' -- Error: variable removed' |
---|
1267 | #if note: eqString[-1] += ' (' + note + ')' |
---|
1268 | if len(eqString) > 1: |
---|
1269 | Eq = wx.BoxSizer(wx.VERTICAL) |
---|
1270 | for s in eqString: |
---|
1271 | line = wx.StaticText(panel,wx.ID_ANY,s) |
---|
1272 | if problemItem: line.SetBackgroundColour(wx.YELLOW) |
---|
1273 | Eq.Add(line,0) |
---|
1274 | Eq.Add((-1,4)) |
---|
1275 | else: |
---|
1276 | Eq = wx.StaticText(panel,wx.ID_ANY,eqString[0]) |
---|
1277 | if problemItem: Eq.SetBackgroundColour(wx.YELLOW) |
---|
1278 | constSizer.Add(Eq,1,WACV) |
---|
1279 | constSizer.Add((3,3)) |
---|
1280 | if note: |
---|
1281 | Eq = wx.StaticText(panel,wx.ID_ANY,note) |
---|
1282 | if problemItem: Eq.SetBackgroundColour(wx.YELLOW) |
---|
1283 | else: |
---|
1284 | Eq = (-1,-1) |
---|
1285 | constSizer.Add(Eq,1,WACV,3) |
---|
1286 | if panel.delBtn.checkboxList: |
---|
1287 | panel.delBtn.Enable(True) |
---|
1288 | else: |
---|
1289 | panel.delBtn.Enable(False) |
---|
1290 | return constSizer |
---|
1291 | |
---|
1292 | def OnConstDel(event): |
---|
1293 | 'Delete selected constraints' |
---|
1294 | sel = G2frame.constr.GetSelection() |
---|
1295 | delList = [] |
---|
1296 | for obj,Id,name in event.GetEventObject().checkboxList: |
---|
1297 | if obj.GetValue(): del data[name][Id] |
---|
1298 | wx.CallAfter(UpdateConstraints,G2frame,data,sel,True) |
---|
1299 | |
---|
1300 | def OnConstEdit(event): |
---|
1301 | '''Called to edit an individual contraint in response to a |
---|
1302 | click on its Edit button |
---|
1303 | ''' |
---|
1304 | Obj = event.GetEventObject() |
---|
1305 | sel = G2frame.constr.GetSelection() |
---|
1306 | Id,name = Indx[Obj.GetId()] |
---|
1307 | if data[name][Id][-1] == 'f': |
---|
1308 | items = data[name][Id][:-3] |
---|
1309 | constType = 'New Variable' |
---|
1310 | if data[name][Id][-3]: |
---|
1311 | varname = str(data[name][Id][-3]) |
---|
1312 | else: |
---|
1313 | varname = "" |
---|
1314 | lbl = 'Enter value for each term in constraint; sum = new variable' |
---|
1315 | dlg = ConstraintDialog(G2frame,constType,lbl,items, |
---|
1316 | varname=varname,varyflag=data[name][Id][-2]) |
---|
1317 | elif data[name][Id][-1] == 'c': |
---|
1318 | items = data[name][Id][:-3]+[ |
---|
1319 | [str(data[name][Id][-3]),'fixed value =']] |
---|
1320 | constType = 'Constraint' |
---|
1321 | lbl = 'Edit value for each term in constant constraint sum' |
---|
1322 | dlg = ConstraintDialog(G2frame,constType,lbl,items) |
---|
1323 | elif data[name][Id][-1] == 'e': |
---|
1324 | items = data[name][Id][:-3] |
---|
1325 | constType = 'Equivalence' |
---|
1326 | lbl = 'The following terms are set to be equal:' |
---|
1327 | dlg = ConstraintDialog(G2frame,constType,lbl,items,'*') |
---|
1328 | else: |
---|
1329 | return |
---|
1330 | try: |
---|
1331 | prev = copy.deepcopy(data[name][Id]) |
---|
1332 | if dlg.ShowModal() == wx.ID_OK: |
---|
1333 | result = dlg.GetData() |
---|
1334 | for i in range(len(data[name][Id][:-3])): |
---|
1335 | if type(data[name][Id][i]) is tuple: # fix non-mutable construct |
---|
1336 | data[name][Id][i] = list(data[name][Id][i]) |
---|
1337 | data[name][Id][i][0] = result[i][0] |
---|
1338 | if data[name][Id][-1] == 'c': |
---|
1339 | data[name][Id][-3] = str(result[-1][0]) |
---|
1340 | elif data[name][Id][-1] == 'f': |
---|
1341 | data[name][Id][-2] = dlg.newvar[1] |
---|
1342 | if dlg.newvar[0]: |
---|
1343 | # process the variable name to put in global form (::var) |
---|
1344 | varname = str(dlg.newvar[0]).strip().replace(' ','_') |
---|
1345 | if varname.startswith('::'): |
---|
1346 | varname = varname[2:] |
---|
1347 | varname = varname.replace(':',';') |
---|
1348 | if varname: |
---|
1349 | data[name][Id][-3] = varname |
---|
1350 | else: |
---|
1351 | data[name][Id][-3] = '' |
---|
1352 | if not CheckChangedConstraint(): |
---|
1353 | data[name][Id] = prev |
---|
1354 | else: |
---|
1355 | data[name][Id] = prev |
---|
1356 | except: |
---|
1357 | import traceback |
---|
1358 | print (traceback.format_exc()) |
---|
1359 | finally: |
---|
1360 | dlg.Destroy() |
---|
1361 | # wx.CallAfter(OnPageChanged,None) |
---|
1362 | G2frame.dataWindow.ClearData() |
---|
1363 | wx.CallAfter(UpdateConstraints,G2frame,data,sel,True) |
---|
1364 | |
---|
1365 | def UpdateConstraintPanel(panel,typ): |
---|
1366 | '''Update the contents of the selected Constraint |
---|
1367 | notebook tab. Called in :func:`OnPageChanged` |
---|
1368 | ''' |
---|
1369 | if panel.GetSizer(): panel.GetSizer().Clear(True) |
---|
1370 | Siz = wx.BoxSizer(wx.VERTICAL) |
---|
1371 | Siz.Add((5,5),0) |
---|
1372 | if typ != 'Sym-Generated': |
---|
1373 | butSizer = wx.BoxSizer(wx.HORIZONTAL) |
---|
1374 | btn = wx.Button(panel, wx.ID_ANY, 'Show Errors') |
---|
1375 | btn.Bind(wx.EVT_BUTTON,lambda event: G2G.ShowScrolledInfo(panel,errmsg,header='Error info')) |
---|
1376 | butSizer.Add(btn,0,wx.ALIGN_CENTER_VERTICAL) |
---|
1377 | btn.Enable(len(errmsg) > 0) |
---|
1378 | btn = wx.Button(panel, wx.ID_ANY, 'Show Warnings') |
---|
1379 | butSizer.Add(btn,0,wx.ALIGN_CENTER_VERTICAL) |
---|
1380 | btn.Bind(wx.EVT_BUTTON,lambda event: G2G.ShowScrolledInfo(panel,warnmsg)) |
---|
1381 | btn.Enable(len(warnmsg) > 0) |
---|
1382 | btn = wx.Button(panel, wx.ID_ANY, 'Show generated constraints') |
---|
1383 | butSizer.Add(btn,0,wx.ALIGN_CENTER_VERTICAL) |
---|
1384 | btn.Bind(wx.EVT_BUTTON,lambda event: |
---|
1385 | G2G.ShowScrolledInfo(panel, |
---|
1386 | 'Constraints after processing\n\n'+G2mv.VarRemapShow(), |
---|
1387 | header='Generated constraints')) |
---|
1388 | panel.delBtn = wx.Button(panel, wx.ID_ANY, 'Delete selected') |
---|
1389 | butSizer.Add(panel.delBtn,0,wx.ALIGN_CENTER_VERTICAL) |
---|
1390 | panel.delBtn.Bind(wx.EVT_BUTTON,OnConstDel) |
---|
1391 | panel.delBtn.checkboxList = [] |
---|
1392 | if G2frame.testSeqRefineMode(): |
---|
1393 | Siz.Add(butSizer) |
---|
1394 | butSizer = wx.BoxSizer(wx.HORIZONTAL) |
---|
1395 | butSizer.Add(wx.StaticText(panel,wx.ID_ANY,' Sequential Ref. Settings. Wildcard use: '),0,WACV) |
---|
1396 | btn = G2G.EnumSelector(panel, data, '_seqmode', |
---|
1397 | ['Set hist # to *', 'Ignore unless hist=*', 'Use as supplied'], |
---|
1398 | ['auto-wildcard', 'wildcards-only', 'use-all'], |
---|
1399 | lambda x: wx.CallAfter(UpdateConstraints, G2frame, data, G2frame.constr.GetSelection(), True)) |
---|
1400 | butSizer.Add(btn,0,wx.ALIGN_CENTER_VERTICAL) |
---|
1401 | butSizer.Add(G2G.HelpButton(panel,helpIndex='Constraints-SeqRef')) |
---|
1402 | butSizer.Add(wx.StaticText(panel,wx.ID_ANY,' Selected histogram: '),0,WACV) |
---|
1403 | btn = G2G.EnumSelector(panel, data, '_seqhist', |
---|
1404 | list(seqHistList),list(range(len(seqHistList))), |
---|
1405 | lambda x: wx.CallAfter(UpdateConstraints, G2frame, data, G2frame.constr.GetSelection(), True)) |
---|
1406 | butSizer.Add(btn,0,wx.ALIGN_CENTER_VERTICAL) |
---|
1407 | Siz.Add(butSizer) |
---|
1408 | G2G.HorizontalLine(Siz,panel) |
---|
1409 | # Siz.Add((5,5),0) |
---|
1410 | Siz.Add(MakeConstraintsSizer(typ,panel),1,wx.EXPAND) |
---|
1411 | panel.SetSizer(Siz,True) |
---|
1412 | Size = Siz.GetMinSize() |
---|
1413 | Size[0] += 40 |
---|
1414 | Size[1] = max(Size[1],450) + 20 |
---|
1415 | panel.SetSize(Size) |
---|
1416 | panel.SetScrollbars(10,10,Size[0]/10-4,Size[1]/10-1) |
---|
1417 | panel.Show() |
---|
1418 | |
---|
1419 | def OnPageChanged(event,selectTab=None): |
---|
1420 | '''Called when a tab is pressed or when a "select tab" menu button is |
---|
1421 | used (see RaisePage), or to refresh the current tab contents (event=None) |
---|
1422 | ''' |
---|
1423 | if event: #page change event! |
---|
1424 | page = event.GetSelection() |
---|
1425 | elif selectTab: #reload previous |
---|
1426 | page = selectTab |
---|
1427 | else: # called directly, get current page |
---|
1428 | try: |
---|
1429 | page = G2frame.constr.GetSelection() |
---|
1430 | except: |
---|
1431 | if GSASIIpath.GetConfigValue('debug'): print('DBG_gpx open error:C++ Run time error - skipped') |
---|
1432 | return |
---|
1433 | G2frame.constr.ChangeSelection(page) |
---|
1434 | text = G2frame.constr.GetPageText(page) |
---|
1435 | G2frame.dataWindow.ConstraintEdit.Enable(G2G.wxID_EQUIVALANCEATOMS,False) |
---|
1436 | # G2frame.dataWindow.ConstraintEdit.Enable(G2G.wxID_ADDRIDING,False) |
---|
1437 | if text == 'Histogram/Phase': |
---|
1438 | enableEditCons = [False]+4*[True] |
---|
1439 | G2frame.Page = [page,'hap'] |
---|
1440 | UpdateConstraintPanel(HAPConstr,'HAP') |
---|
1441 | elif text == 'Histogram': |
---|
1442 | enableEditCons = [False]+4*[True] |
---|
1443 | G2frame.Page = [page,'hst'] |
---|
1444 | UpdateConstraintPanel(HistConstr,'Hist') |
---|
1445 | elif text == 'Phase': |
---|
1446 | enableEditCons = 5*[True] |
---|
1447 | G2frame.Page = [page,'phs'] |
---|
1448 | G2frame.dataWindow.ConstraintEdit.Enable(G2G.wxID_EQUIVALANCEATOMS,True) |
---|
1449 | # G2frame.dataWindow.ConstraintEdit.Enable(G2G.wxID_ADDRIDING,True) |
---|
1450 | if 'DELETED' in str(PhaseConstr): #seems to be no other way to do this (wx bug) |
---|
1451 | if GSASIIpath.GetConfigValue('debug'): |
---|
1452 | print ('DBG_wx error: PhaseConstr not cleanly deleted after Refine') |
---|
1453 | return |
---|
1454 | UpdateConstraintPanel(PhaseConstr,'Phase') |
---|
1455 | elif text == 'Global': |
---|
1456 | enableEditCons = [False]+4*[True] |
---|
1457 | G2frame.Page = [page,'glb'] |
---|
1458 | UpdateConstraintPanel(GlobalConstr,'Global') |
---|
1459 | else: |
---|
1460 | enableEditCons = 5*[False] |
---|
1461 | G2frame.Page = [page,'sym'] |
---|
1462 | UpdateConstraintPanel(SymConstr,'Sym-Generated') |
---|
1463 | # remove menu items when not allowed |
---|
1464 | for obj,flag in zip(G2frame.dataWindow.ConstraintEdit.GetMenuItems(),enableEditCons): |
---|
1465 | obj.Enable(flag) |
---|
1466 | G2frame.dataWindow.SetDataSize() |
---|
1467 | |
---|
1468 | def RaisePage(event): |
---|
1469 | 'Respond to a "select tab" menu button' |
---|
1470 | try: |
---|
1471 | i = (G2G.wxID_CONSPHASE, |
---|
1472 | G2G.wxID_CONSHAP, |
---|
1473 | G2G.wxID_CONSHIST, |
---|
1474 | G2G.wxID_CONSGLOBAL, |
---|
1475 | G2G.wxID_CONSSYM, |
---|
1476 | ).index(event.GetId()) |
---|
1477 | G2frame.constr.SetSelection(i) |
---|
1478 | wx.CallAfter(OnPageChanged,None) |
---|
1479 | except ValueError: |
---|
1480 | print('Unexpected event in RaisePage') |
---|
1481 | |
---|
1482 | def SetStatusLine(text): |
---|
1483 | G2frame.GetStatusBar().SetStatusText(text,1) |
---|
1484 | |
---|
1485 | def OnShowISODISTORT(event): |
---|
1486 | ShowIsoDistortCalc(G2frame) |
---|
1487 | |
---|
1488 | #### UpdateConstraints execution starts here ############################## |
---|
1489 | if Clear: |
---|
1490 | G2frame.dataWindow.ClearData() |
---|
1491 | if not data: # usually created in CheckNotebook |
---|
1492 | data.update({'Hist':[],'HAP':[],'Phase':[],'Global':[], |
---|
1493 | '_seqmode':'auto-wildcard', '_seqhist':0}) #empty dict - fill it |
---|
1494 | if 'Global' not in data: #patch |
---|
1495 | data['Global'] = [] |
---|
1496 | seqHistList = G2frame.testSeqRefineMode() |
---|
1497 | # patch: added ~version 5030 -- new mode for wild-card use in seq refs |
---|
1498 | # note that default for older sequential fits is 'wildcards-only' but in new GPX is 'auto-wildcard' |
---|
1499 | if seqHistList: |
---|
1500 | data['_seqmode'] = data.get('_seqmode','wildcards-only') |
---|
1501 | else: |
---|
1502 | data['_seqmode'] = data.get('_seqmode','auto-wildcard') |
---|
1503 | data['_seqhist'] = data.get('_seqhist',0) |
---|
1504 | # end patch |
---|
1505 | |
---|
1506 | # DEBUG code #===================================== |
---|
1507 | #import GSASIIconstrGUI |
---|
1508 | #reload(GSASIIconstrGUI) |
---|
1509 | #reload(G2obj) |
---|
1510 | #reload(G2stIO) |
---|
1511 | #import GSASIIstrMain |
---|
1512 | #reload(GSASIIstrMain) |
---|
1513 | #reload(G2mv) |
---|
1514 | #reload(G2gd) |
---|
1515 | #=================================================== |
---|
1516 | seqmode = 'use-all' |
---|
1517 | seqhistnum = None |
---|
1518 | if seqHistList: # Selections used with sequential refinements |
---|
1519 | seqmode = data.get('_seqmode','wildcards-only') |
---|
1520 | seqhistnum = min(data.get('_seqhist',0),len(seqHistList)-1) |
---|
1521 | Histograms,Phases = G2frame.GetUsedHistogramsAndPhasesfromTree() |
---|
1522 | G2frame.dataWindow.ConstraintEdit.Enable(G2G.wxID_SHOWISO,True) |
---|
1523 | #removed this check as it prevents examination of ISODISTORT constraints without data |
---|
1524 | # if not len(Phases) or not len(Histograms): |
---|
1525 | # dlg = wx.MessageDialog(G2frame,'You need both phases and histograms to see Constraints', |
---|
1526 | # 'No phases or histograms') |
---|
1527 | # dlg.CenterOnParent() |
---|
1528 | # dlg.ShowModal() |
---|
1529 | # dlg.Destroy() |
---|
1530 | # return |
---|
1531 | G2obj.IndexAllIds(Histograms,Phases) |
---|
1532 | # for p in Phases: |
---|
1533 | # if 'ISODISTORT' in Phases[p] and 'G2VarList' in Phases[p]['ISODISTORT']: |
---|
1534 | # G2frame.dataWindow.ConstraintEdit.Enable(G2G.wxID_SHOWISO,True) |
---|
1535 | # break |
---|
1536 | ###### patch: convert old-style (str) variables in constraints to G2VarObj objects ##### |
---|
1537 | for key,value in data.items(): |
---|
1538 | if key.startswith('_'): continue |
---|
1539 | j = 0 |
---|
1540 | for cons in value: |
---|
1541 | #print cons # DEBUG |
---|
1542 | for i in range(len(cons[:-3])): |
---|
1543 | if type(cons[i][1]) is str: |
---|
1544 | cons[i][1] = G2obj.G2VarObj(cons[i][1]) |
---|
1545 | j += 1 |
---|
1546 | if j: |
---|
1547 | print (str(key) + ': '+str(j)+' variable(s) as strings converted to objects') |
---|
1548 | ##### end patch ############################################################################# |
---|
1549 | rigidbodyDict = G2frame.GPXtree.GetItemPyData( |
---|
1550 | G2gd.GetGPXtreeItemId(G2frame,G2frame.root,'Rigid bodies')) |
---|
1551 | rbIds = rigidbodyDict.get('RBIds',{'Vector':[],'Residue':[]}) |
---|
1552 | rbVary,rbDict = G2stIO.GetRigidBodyModels(rigidbodyDict,Print=False) |
---|
1553 | badPhaseParms = ['Ax','Ay','Az','Amul','AI/A','Atype','SHorder','AwaveType','FwaveType','PwaveType','MwaveType','Vol','isMag',] |
---|
1554 | globalList = list(rbDict.keys()) |
---|
1555 | globalList.sort() |
---|
1556 | try: |
---|
1557 | AtomDict = dict([Phases[phase]['pId'],Phases[phase]['Atoms']] for phase in Phases) |
---|
1558 | except KeyError: |
---|
1559 | G2frame.ErrorDialog('Constraint Error','Constraints cannot be set until a cycle of least squares'+ |
---|
1560 | ' has been run.\nWe suggest you refine a scale factor.') |
---|
1561 | return |
---|
1562 | |
---|
1563 | # create a list of the phase variables |
---|
1564 | symHolds = [] |
---|
1565 | (Natoms,atomIndx,phaseVary,phaseDict,pawleyLookup,FFtable,BLtable, |
---|
1566 | MFtable,maxSSwave) = G2stIO.GetPhaseData( |
---|
1567 | Phases,rbIds=rbIds,Print=False,symHold=symHolds) |
---|
1568 | phaseList = [] |
---|
1569 | for item in phaseDict: |
---|
1570 | if item.split(':')[2] not in badPhaseParms: |
---|
1571 | phaseList.append(item) |
---|
1572 | phaseList.sort() |
---|
1573 | phaseAtNames = {} |
---|
1574 | phaseAtTypes = {} |
---|
1575 | TypeList = [] |
---|
1576 | for item in phaseList: |
---|
1577 | Split = item.split(':') |
---|
1578 | if Split[2][:2] in ['AU','Af','dA','AM']: |
---|
1579 | Id = int(Split[0]) |
---|
1580 | phaseAtNames[item] = AtomDict[Id][int(Split[3])][0] |
---|
1581 | phaseAtTypes[item] = AtomDict[Id][int(Split[3])][1] |
---|
1582 | if phaseAtTypes[item] not in TypeList: |
---|
1583 | TypeList.append(phaseAtTypes[item]) |
---|
1584 | else: |
---|
1585 | phaseAtNames[item] = '' |
---|
1586 | phaseAtTypes[item] = '' |
---|
1587 | |
---|
1588 | # create a list of the hist*phase variables |
---|
1589 | if seqHistList: # for sequential refinement, only process selected histgram in list |
---|
1590 | histDict = {seqHistList[seqhistnum]:Histograms[seqHistList[seqhistnum]]} |
---|
1591 | else: |
---|
1592 | histDict = Histograms |
---|
1593 | hapVary,hapDict,controlDict = G2stIO.GetHistogramPhaseData(Phases,histDict,Print=False,resetRefList=False) |
---|
1594 | hapList = sorted([i for i in hapDict.keys() if i.split(':')[2] not in ('Type',)]) |
---|
1595 | # derive list of variables |
---|
1596 | if seqHistList: # convert histogram # to wildcard |
---|
1597 | wildList = [] # list of variables with "*" for histogram number |
---|
1598 | for i in hapList: |
---|
1599 | s = i.split(':') |
---|
1600 | if s[1] == "": continue |
---|
1601 | s[1] = '*' |
---|
1602 | sj = ':'.join(s) |
---|
1603 | if sj not in wildList: wildList.append(sj) |
---|
1604 | if seqmode == 'use-all': |
---|
1605 | hapList += wildList |
---|
1606 | else: |
---|
1607 | hapList = wildList |
---|
1608 | histVary,histDict,controlDict = G2stIO.GetHistogramData(histDict,Print=False) |
---|
1609 | histList = list(histDict.keys()) |
---|
1610 | histList.sort() |
---|
1611 | if seqHistList: # convert histogram # to wildcard |
---|
1612 | wildList = [] # list of variables with "*" for histogram number |
---|
1613 | for i in histList: |
---|
1614 | s = i.split(':') |
---|
1615 | if s[1] == "": continue |
---|
1616 | s[1] = '*' |
---|
1617 | sj = ':'.join(s) |
---|
1618 | if sj not in wildList: wildList.append(sj) |
---|
1619 | if seqmode == 'use-all': |
---|
1620 | histList += wildList |
---|
1621 | else: |
---|
1622 | histList = wildList |
---|
1623 | |
---|
1624 | Indx = {} |
---|
1625 | G2frame.Page = [0,'phs'] |
---|
1626 | |
---|
1627 | G2gd.SetDataMenuBar(G2frame,G2frame.dataWindow.ConstraintMenu) |
---|
1628 | SetStatusLine('') |
---|
1629 | |
---|
1630 | G2frame.Bind(wx.EVT_MENU, OnAddConstraint, id=G2G.wxID_CONSTRAINTADD) |
---|
1631 | G2frame.Bind(wx.EVT_MENU, OnAddFunction, id=G2G.wxID_FUNCTADD) |
---|
1632 | G2frame.Bind(wx.EVT_MENU, OnAddEquivalence, id=G2G.wxID_EQUIVADD) |
---|
1633 | G2frame.Bind(wx.EVT_MENU, OnAddHold, id=G2G.wxID_HOLDADD) |
---|
1634 | G2frame.Bind(wx.EVT_MENU, OnAddAtomEquiv, id=G2G.wxID_EQUIVALANCEATOMS) |
---|
1635 | # G2frame.Bind(wx.EVT_MENU, OnAddRiding, id=G2G.wxID_ADDRIDING) |
---|
1636 | G2frame.Bind(wx.EVT_MENU, OnShowISODISTORT, id=G2G.wxID_SHOWISO) |
---|
1637 | # tab commands |
---|
1638 | for id in (G2G.wxID_CONSPHASE, |
---|
1639 | G2G.wxID_CONSHAP, |
---|
1640 | G2G.wxID_CONSHIST, |
---|
1641 | G2G.wxID_CONSGLOBAL, |
---|
1642 | G2G.wxID_CONSSYM, |
---|
1643 | ): |
---|
1644 | G2frame.Bind(wx.EVT_MENU, RaisePage,id=id) |
---|
1645 | |
---|
1646 | #G2frame.constr = G2G.GSNoteBook(parent=G2frame.dataWindow,size=G2frame.dataWindow.GetClientSize()) |
---|
1647 | G2frame.constr = G2G.GSNoteBook(parent=G2frame.dataWindow) |
---|
1648 | G2frame.dataWindow.GetSizer().Add(G2frame.constr,1,wx.ALL|wx.EXPAND) |
---|
1649 | # note that order of pages is hard-coded in RaisePage |
---|
1650 | PhaseConstr = wx.ScrolledWindow(G2frame.constr) |
---|
1651 | G2frame.constr.AddPage(PhaseConstr,'Phase') |
---|
1652 | HAPConstr = wx.ScrolledWindow(G2frame.constr) |
---|
1653 | G2frame.constr.AddPage(HAPConstr,'Histogram/Phase') |
---|
1654 | HistConstr = wx.ScrolledWindow(G2frame.constr) |
---|
1655 | G2frame.constr.AddPage(HistConstr,'Histogram') |
---|
1656 | GlobalConstr = wx.ScrolledWindow(G2frame.constr) |
---|
1657 | G2frame.constr.AddPage(GlobalConstr,'Global') |
---|
1658 | SymConstr = wx.ScrolledWindow(G2frame.constr) |
---|
1659 | G2frame.constr.AddPage(SymConstr,'Sym-Generated') |
---|
1660 | wx.CallAfter(OnPageChanged,None,selectTab) |
---|
1661 | G2frame.constr.Bind(wx.aui.EVT_AUINOTEBOOK_PAGE_CHANGED, OnPageChanged) |
---|
1662 | errmsg,warnmsg = WarnConstraintLimit() # check limits & constraints |
---|
1663 | if errmsg: |
---|
1664 | G2frame.ErrorDialog('Constraint Error', |
---|
1665 | 'Error in constraints.\nCheck console output for more information'+ |
---|
1666 | ' or press "Show Errors" & "Show Warnings" buttons', |
---|
1667 | parent=G2frame) |
---|
1668 | if seqhistnum is None: |
---|
1669 | print ('\nError message(s):\n',errmsg) |
---|
1670 | else: |
---|
1671 | print ('\nError message(s) for histogram #{}:\n{}'.format(seqhistnum,errmsg)) |
---|
1672 | if warnmsg: print ('\nAlso note these constraint warning(s):\n'+warnmsg) |
---|
1673 | #elif GSASIIpath.GetConfigValue('debug'): |
---|
1674 | # print ('Generated constraints\n',G2mv.VarRemapShow()) |
---|
1675 | |
---|
1676 | ###### check scale & phase fractions, create constraint if needed ############# |
---|
1677 | def CheckAllScalePhaseFractions(G2frame): |
---|
1678 | '''Check if scale factor and all phase fractions are refined without a constraint |
---|
1679 | for all used histograms, if so, offer the user a chance to create a constraint |
---|
1680 | on the sum of phase fractions |
---|
1681 | ''' |
---|
1682 | histograms, phases = G2frame.GetUsedHistogramsAndPhasesfromTree() |
---|
1683 | cId = G2gd.GetGPXtreeItemId(G2frame,G2frame.root,'Constraints') |
---|
1684 | Constraints = G2frame.GPXtree.GetItemPyData(cId) |
---|
1685 | |
---|
1686 | problems = [] |
---|
1687 | for h,hist in enumerate(histograms): |
---|
1688 | if CheckScalePhaseFractions(G2frame,hist,histograms,phases,Constraints): |
---|
1689 | problems.append((h,hist)) |
---|
1690 | if len(problems) == 0: return |
---|
1691 | msg = 'You are refining the scale factor and all phase fractions for histogram(s) #' |
---|
1692 | for i,(h,hist) in enumerate(problems): |
---|
1693 | if i: msg += ', ' |
---|
1694 | msg += str(h) |
---|
1695 | msg += '. This is not recommended as it will produce an unstable refinement. Do you want to create constrain(s) on the sum of phase fractions to address this? (Press "No" to continue without)' |
---|
1696 | dlg = wx.MessageDialog(G2frame,msg,'Warning: Constraint Needed',wx.YES|wx.NO) |
---|
1697 | ans = dlg.ShowModal() |
---|
1698 | dlg.Destroy() |
---|
1699 | if ans == wx.ID_YES: |
---|
1700 | for h,hist in problems: |
---|
1701 | constr = [] |
---|
1702 | for p in phases: |
---|
1703 | if hist not in phases[p]['Histograms']: continue |
---|
1704 | if not phases[p]['Histograms'][hist]['Use']: continue |
---|
1705 | constr += [[1.0,G2obj.G2VarObj(':'.join( |
---|
1706 | (str(phases[p]['pId']), |
---|
1707 | str(histograms[hist]['hId']), |
---|
1708 | 'Scale') |
---|
1709 | ))]] |
---|
1710 | Constraints['HAP'].append(constr+[1.0,None,'c']) |
---|
1711 | wx.CallAfter(G2frame.GPXtree.SelectItem,cId) # should call SelectDataTreeItem |
---|
1712 | UpdateConstraints(G2frame,Constraints,1,True) # repaint with HAP tab |
---|
1713 | return True |
---|
1714 | return False |
---|
1715 | |
---|
1716 | def CheckScalePhaseFractions(G2frame,hist,histograms,phases,Constraints): |
---|
1717 | '''Check if scale factor and all phase fractions are refined without a constraint |
---|
1718 | for histogram hist, if so, offer the user a chance to create a constraint |
---|
1719 | on the sum of phase fractions |
---|
1720 | ''' |
---|
1721 | if G2frame.testSeqRefineMode(): # more work needed due to seqmode |
---|
1722 | return False |
---|
1723 | # histStr = '*' |
---|
1724 | else: |
---|
1725 | histStr = str(histograms[hist]['hId']) |
---|
1726 | # Is this powder? |
---|
1727 | if not hist.startswith('PWDR '): return False |
---|
1728 | # do this only if the scale factor is varied |
---|
1729 | if not histograms[hist]['Sample Parameters']['Scale'][1]: return False |
---|
1730 | # are all phase fractions varied in all used histograms? |
---|
1731 | phaseCount = 0 |
---|
1732 | for p in phases: |
---|
1733 | if hist not in phases[p]['Histograms']: continue |
---|
1734 | if phases[p]['Histograms'][hist]['Use'] and not phases[p]['Histograms'][hist]['Scale'][1]: |
---|
1735 | return False |
---|
1736 | else: |
---|
1737 | phaseCount += 1 |
---|
1738 | |
---|
1739 | # all phase fractions and scale factor varied, now scan for a constraint |
---|
1740 | for c in Constraints.get('HAP',[]): |
---|
1741 | if c[-1] != 'c': continue |
---|
1742 | if not c[-3]: continue |
---|
1743 | if len(c[:-3]) != phaseCount: continue |
---|
1744 | # got a constraint equation with right number of terms, is it on phase fractions for |
---|
1745 | # the correct histogram? |
---|
1746 | if all([(i[1].name == 'Scale' and i[1].varname().split(':')[1] == histStr) for i in c[:-3]]): |
---|
1747 | # got a constraint, this is OK |
---|
1748 | return False |
---|
1749 | return True |
---|
1750 | |
---|
1751 | #### Make nuclear/magnetic phase transition constraints - called by OnTransform in G2phsGUI ########## |
---|
1752 | def TransConstraints(G2frame,oldPhase,newPhase,Trans,Vec,atCodes): |
---|
1753 | '''Add constraints for new magnetic phase created via transformation of old |
---|
1754 | nuclear one |
---|
1755 | NB: A = [G11,G22,G33,2*G12,2*G13,2*G23] |
---|
1756 | ''' |
---|
1757 | |
---|
1758 | def SetUniqAj(pId,iA,SGData): |
---|
1759 | SGLaue = SGData['SGLaue'] |
---|
1760 | SGUniq = SGData['SGUniq'] |
---|
1761 | if SGLaue in ['m3','m3m']: |
---|
1762 | if iA in [0,1,2]: |
---|
1763 | parm = '%d::%s'%(pId,'A0') |
---|
1764 | else: |
---|
1765 | parm = None |
---|
1766 | elif SGLaue in ['4/m','4/mmm']: |
---|
1767 | if iA in [0,1]: |
---|
1768 | parm = '%d::%s'%(pId,'A0') |
---|
1769 | elif iA == 2: |
---|
1770 | parm = '%d::%s'%(pId,'A2') |
---|
1771 | else: |
---|
1772 | parm = None |
---|
1773 | elif SGLaue in ['6/m','6/mmm','3m1', '31m', '3']: |
---|
1774 | if iA in [0,1,3]: |
---|
1775 | parm = '%d::%s'%(pId,'A0') |
---|
1776 | elif iA == 2: |
---|
1777 | parm = '%d::%s'%(pId,'A2') |
---|
1778 | else: |
---|
1779 | parm = None |
---|
1780 | elif SGLaue in ['3R', '3mR']: |
---|
1781 | if ia in [0,1,2]: |
---|
1782 | parm = '%d::%s'%(pId,'A0') |
---|
1783 | else: |
---|
1784 | parm = '%d::%s'%(pId,'A3') |
---|
1785 | elif SGLaue in ['mmm',]: |
---|
1786 | if iA in [0,1,2]: |
---|
1787 | parm = '%d::A%s'%(pId,iA) |
---|
1788 | else: |
---|
1789 | parm = None |
---|
1790 | elif SGLaue == '2/m': |
---|
1791 | if iA in [0,1,2]: |
---|
1792 | parm = '%d::A%s'%(pId,iA) |
---|
1793 | elif iA == 3 and SGUniq == 'c': |
---|
1794 | parm = '%d::A%s'%(pId,iA) |
---|
1795 | elif iA == 4 and SGUniq == 'b': |
---|
1796 | parm = '%d::A%s'%(pId,iA) |
---|
1797 | elif iA == 5 and SGUniq == 'a': |
---|
1798 | parm = '%d::A%s'%(pId,iA) |
---|
1799 | else: |
---|
1800 | parm = None |
---|
1801 | else: |
---|
1802 | parm = '%d::A%s'%(pId,iA) |
---|
1803 | return parm |
---|
1804 | |
---|
1805 | Histograms,Phases = G2frame.GetUsedHistogramsAndPhasesfromTree() |
---|
1806 | UseList = newPhase['Histograms'] |
---|
1807 | detTrans = np.abs(nl.det(Trans)) |
---|
1808 | opId = oldPhase['pId'] |
---|
1809 | npId = newPhase['pId'] |
---|
1810 | cx,ct,cs,cia = newPhase['General']['AtomPtrs'] |
---|
1811 | nAtoms = newPhase['Atoms'] |
---|
1812 | nSGData = newPhase['General']['SGData'] |
---|
1813 | #oAcof = G2lat.cell2A(oldPhase['General']['Cell'][1:7]) |
---|
1814 | #nAcof = G2lat.cell2A(newPhase['General']['Cell'][1:7]) |
---|
1815 | item = G2gd.GetGPXtreeItemId(G2frame,G2frame.root,'Constraints') |
---|
1816 | if not item: |
---|
1817 | print('Error: no constraints in Data Tree') |
---|
1818 | return |
---|
1819 | constraints = G2frame.GPXtree.GetItemPyData(item) |
---|
1820 | xnames = ['dAx','dAy','dAz'] |
---|
1821 | # constraints on matching atom params between phases |
---|
1822 | for ia,code in enumerate(atCodes): |
---|
1823 | atom = nAtoms[ia] |
---|
1824 | if not ia and atom[cia] == 'A': |
---|
1825 | wx.MessageDialog(G2frame, |
---|
1826 | 'Anisotropic thermal motion constraints are not developed at the present time', |
---|
1827 | 'Anisotropic thermal constraint?',style=wx.ICON_INFORMATION).ShowModal() |
---|
1828 | siteSym = G2spc.SytSym(atom[cx:cx+3],nSGData)[0] |
---|
1829 | CSX = G2spc.GetCSxinel(siteSym) |
---|
1830 | # CSU = G2spc.GetCSuinel(siteSym) |
---|
1831 | item = code.split('+')[0] |
---|
1832 | iat,opr = item.split(':') |
---|
1833 | Nop = abs(int(opr))%100-1 |
---|
1834 | if '-' in opr: |
---|
1835 | Nop *= -1 |
---|
1836 | Opr = oldPhase['General']['SGData']['SGOps'][abs(Nop)][0] |
---|
1837 | if Nop < 0: #inversion |
---|
1838 | Opr *= -1 |
---|
1839 | XOpr = np.inner(Opr,Trans) |
---|
1840 | invOpr = nl.inv(XOpr) |
---|
1841 | for i,ix in enumerate(list(CSX[0])): |
---|
1842 | if not ix: |
---|
1843 | continue |
---|
1844 | name = xnames[i] |
---|
1845 | IndpCon = [1.0,G2obj.G2VarObj('%d::%s:%d'%(npId,name,ia))] |
---|
1846 | DepCons = [] |
---|
1847 | for iop,opval in enumerate(invOpr[i]): |
---|
1848 | if abs(opval) > 1e-6: |
---|
1849 | DepCons.append([opval,G2obj.G2VarObj('%d::%s:%s'%(opId,xnames[iop],iat))]) |
---|
1850 | if len(DepCons) == 1: |
---|
1851 | constraints['Phase'].append([DepCons[0],IndpCon,None,None,'e']) |
---|
1852 | elif len(DepCons) > 1: |
---|
1853 | IndpCon[0] = -1. |
---|
1854 | constraints['Phase'].append([IndpCon]+DepCons+[0.0,None,'c']) |
---|
1855 | for name in ['Afrac','AUiso']: |
---|
1856 | IndpCon = [1.0,G2obj.G2VarObj('%d::%s:%d'%(npId,name,ia))] |
---|
1857 | DepCons = [1.0,G2obj.G2VarObj('%d::%s:%s'%(opId,name,iat))] |
---|
1858 | constraints['Phase'].append([DepCons,IndpCon,None,None,'e']) |
---|
1859 | |
---|
1860 | # unfinished Anisotropic constraint generation |
---|
1861 | # Uids = [[0,0,'AU11'],[1,1,'AU22'],[2,2,'AU33'],[0,1,'AU12'],[0,2,'AU13'],[1,2,'AU23']] |
---|
1862 | # DepConsDict = dict(zip(Us,[[],[],[],[],[],[]])) |
---|
1863 | # for iu,Uid in enumerate(Uids): |
---|
1864 | # UMT = np.zeros((3,3)) |
---|
1865 | # UMT[Uid[0],Uid[1]] = 1 |
---|
1866 | # nUMT = G2lat.prodMGMT(UMT,invTrans) |
---|
1867 | # nUT = G2lat.UijtoU6(nUMT) |
---|
1868 | # for iu,nU in enumerate(nUT): |
---|
1869 | # if abs(nU) > 1.e-8: |
---|
1870 | # parm = '%d::%s;%s'%(opId,Us[iu],iat) |
---|
1871 | # DepConsDict[Uid[2]].append([abs(nU%1.),G2obj.G2VarObj(parm)]) |
---|
1872 | # nUcof = atom[iu:iu+6] |
---|
1873 | # conStrings = [] |
---|
1874 | # for iU,Usi in enumerate(Us): |
---|
1875 | # parm = '%d::%s;%d'%(npId,Usi,ia) |
---|
1876 | # parmDict[parm] = nUcof[iU] |
---|
1877 | # varyList.append(parm) |
---|
1878 | # IndpCon = [1.0,G2obj.G2VarObj(parm)] |
---|
1879 | # conStr = str([IndpCon,DepConsDict[Usi]]) |
---|
1880 | # if conStr in conStrings: |
---|
1881 | # continue |
---|
1882 | # conStrings.append(conStr) |
---|
1883 | # if len(DepConsDict[Usi]) == 1: |
---|
1884 | # if DepConsDict[Usi][0]: |
---|
1885 | # constraints['Phase'].append([IndpCon,DepConsDict[Usi][0],None,None,'e']) |
---|
1886 | # elif len(DepConsDict[Usi]) > 1: |
---|
1887 | # for Dep in DepConsDict[Usi]: |
---|
1888 | # Dep[0] *= -1 |
---|
1889 | # constraints['Phase'].append([IndpCon]+DepConsDict[Usi]+[0.0,None,'c']) |
---|
1890 | |
---|
1891 | #how do I do Uij's for most Trans? |
---|
1892 | |
---|
1893 | # constraints on lattice parameters between phases |
---|
1894 | Aold = G2lat.cell2A(oldPhase['General']['Cell'][1:7]) |
---|
1895 | if True: # debug |
---|
1896 | constraints['Phase'] += G2lat.GenCellConstraints(Trans,opId,npId,Aold, |
---|
1897 | oldPhase['General']['SGData'],nSGData,True) |
---|
1898 | print('old A*',G2lat.cell2A(oldPhase['General']['Cell'][1:7])) |
---|
1899 | print('new A*',G2lat.cell2A(newPhase['General']['Cell'][1:7])) |
---|
1900 | print('old cell',oldPhase['General']['Cell'][1:7]) |
---|
1901 | print('new cell',newPhase['General']['Cell'][1:7]) |
---|
1902 | else: |
---|
1903 | constraints['Phase'] += G2lat.GenCellConstraints(Trans,opId,npId,Aold, |
---|
1904 | oldPhase['General']['SGData'],nSGData,True) |
---|
1905 | # constraints on HAP Scale, etc. |
---|
1906 | for hId,hist in enumerate(UseList): #HAP - seems OK |
---|
1907 | ohapkey = '%d:%d:'%(opId,hId) |
---|
1908 | nhapkey = '%d:%d:'%(npId,hId) |
---|
1909 | IndpCon = [1.0,G2obj.G2VarObj(ohapkey+'Scale')] |
---|
1910 | DepCons = [detTrans,G2obj.G2VarObj(nhapkey+'Scale')] |
---|
1911 | constraints['HAP'].append([DepCons,IndpCon,None,None,'e']) |
---|
1912 | for name in ['Size;i','Mustrain;i']: |
---|
1913 | IndpCon = [1.0,G2obj.G2VarObj(ohapkey+name)] |
---|
1914 | DepCons = [1.0,G2obj.G2VarObj(nhapkey+name)] |
---|
1915 | constraints['HAP'].append([IndpCon,DepCons,None,None,'e']) |
---|
1916 | |
---|
1917 | #### Rigid bodies ############################################################# |
---|
1918 | resRBsel = None |
---|
1919 | def UpdateRigidBodies(G2frame,data): |
---|
1920 | '''Called when Rigid bodies tree item is selected. |
---|
1921 | Displays the rigid bodies in the data window |
---|
1922 | ''' |
---|
1923 | def OnPageChanged(event): |
---|
1924 | global resList |
---|
1925 | resList = [] |
---|
1926 | if event: #page change event! |
---|
1927 | page = event.GetSelection() |
---|
1928 | else: |
---|
1929 | try: |
---|
1930 | page = G2frame.rbBook.GetSelection() |
---|
1931 | except: |
---|
1932 | if GSASIIpath.GetConfigValue('debug'): print('DBG_gpx open error:C++ Run time error - skipped') |
---|
1933 | return |
---|
1934 | G2frame.rbBook.ChangeSelection(page) |
---|
1935 | text = G2frame.rbBook.GetPageText(page) |
---|
1936 | if text == 'Vector rigid bodies': |
---|
1937 | G2gd.SetDataMenuBar(G2frame,G2frame.dataWindow.VectorBodyMenu) |
---|
1938 | G2frame.Bind(wx.EVT_MENU, AddVectorRB, id=G2G.wxID_VECTORBODYADD) |
---|
1939 | G2frame.Bind(wx.EVT_MENU, ExtractPhaseRB, id=G2G.wxID_VECTORBODYIMP) |
---|
1940 | G2frame.Bind(wx.EVT_MENU, AddVectTrans, id=G2G.wxID_VECTORBODYEXTD) |
---|
1941 | G2frame.Bind(wx.EVT_MENU, SaveVectorRB, id=G2G.wxID_VECTORBODYSAV) |
---|
1942 | G2frame.Bind(wx.EVT_MENU, ReadVectorRB, id=G2G.wxID_VECTORBODYRD) |
---|
1943 | G2frame.Page = [page,'vrb'] |
---|
1944 | UpdateVectorRB() |
---|
1945 | elif text == 'Residue rigid bodies': |
---|
1946 | G2gd.SetDataMenuBar(G2frame,G2frame.dataWindow.RigidBodyMenu) |
---|
1947 | G2frame.Bind(wx.EVT_MENU, AddResidueRB, id=G2G.wxID_RIGIDBODYADD) |
---|
1948 | G2frame.Bind(wx.EVT_MENU, ExtractPhaseRB, id=G2G.wxID_RIGIDBODYIMP) |
---|
1949 | G2frame.Bind(wx.EVT_MENU, OnImportRigidBody, id=G2G.wxID_RIGIDBODYIMPORT) |
---|
1950 | G2frame.Bind(wx.EVT_MENU, OnSaveRigidBody, id=G2G.wxID_RIGIDBODYSAVE) |
---|
1951 | G2frame.Bind(wx.EVT_MENU, OnDefineTorsSeq, id=G2G.wxID_RESIDUETORSSEQ) #enable only if residue RBs exist? |
---|
1952 | G2frame.Bind(wx.EVT_MENU, DumpVectorRB, id=G2G.wxID_RESBODYSAV) |
---|
1953 | G2frame.Bind(wx.EVT_MENU, LoadVectorRB, id=G2G.wxID_RESBODYRD) |
---|
1954 | G2frame.Page = [page,'rrb'] |
---|
1955 | UpdateResidueRB() |
---|
1956 | else: |
---|
1957 | G2gd.SetDataMenuBar(G2frame) |
---|
1958 | #G2frame.Page = [page,'rrb'] |
---|
1959 | |
---|
1960 | def getMacroFile(macName): |
---|
1961 | defDir = os.path.join(os.path.split(__file__)[0],'GSASIImacros') |
---|
1962 | dlg = wx.FileDialog(G2frame,message='Choose '+macName+' rigid body macro file', |
---|
1963 | defaultDir=defDir,defaultFile="",wildcard="GSAS-II macro file (*.mac)|*.mac", |
---|
1964 | style=wx.FD_OPEN | wx.FD_CHANGE_DIR) |
---|
1965 | try: |
---|
1966 | if dlg.ShowModal() == wx.ID_OK: |
---|
1967 | macfile = dlg.GetPath() |
---|
1968 | macro = open(macfile,'r') |
---|
1969 | head = macro.readline() |
---|
1970 | if macName not in head: |
---|
1971 | print (head) |
---|
1972 | print ('**** ERROR - wrong restraint macro file selected, try again ****') |
---|
1973 | macro = [] |
---|
1974 | else: # cancel was pressed |
---|
1975 | macro = [] |
---|
1976 | finally: |
---|
1977 | dlg.Destroy() |
---|
1978 | return macro #advanced past 1st line |
---|
1979 | |
---|
1980 | def getTextFile(): |
---|
1981 | dlg = wx.FileDialog(G2frame,'Choose rigid body text file', G2frame.LastGPXdir, '', |
---|
1982 | "GSAS-II text file (*.txt)|*.txt|XYZ file (*.xyz)|*.xyz|" |
---|
1983 | "Sybyl mol2 file (*.mol2)|*.mol2|PDB file (*.pdb;*.ent)|*.pdb;*.ent", |
---|
1984 | wx.FD_OPEN | wx.FD_CHANGE_DIR) |
---|
1985 | try: |
---|
1986 | if dlg.ShowModal() == wx.ID_OK: |
---|
1987 | txtfile = dlg.GetPath() |
---|
1988 | ext = os.path.splitext(txtfile)[1] |
---|
1989 | text = open(txtfile,'r') |
---|
1990 | else: # cancel was pressed |
---|
1991 | ext = '' |
---|
1992 | text = [] |
---|
1993 | finally: |
---|
1994 | dlg.Destroy() |
---|
1995 | if 'ent' in ext: |
---|
1996 | ext = '.pdb' |
---|
1997 | return text,ext.lower() |
---|
1998 | |
---|
1999 | def OnImportRigidBody(event): |
---|
2000 | page = G2frame.rbBook.GetSelection() |
---|
2001 | if 'Vector' in G2frame.rbBook.GetPageText(page): |
---|
2002 | pass |
---|
2003 | elif 'Residue' in G2frame.rbBook.GetPageText(page): |
---|
2004 | ImportResidueRB() |
---|
2005 | |
---|
2006 | def OnSaveRigidBody(event): |
---|
2007 | page = G2frame.rbBook.GetSelection() |
---|
2008 | if 'Vector' in G2frame.rbBook.GetPageText(page): |
---|
2009 | pass |
---|
2010 | elif 'Residue' in G2frame.rbBook.GetPageText(page): |
---|
2011 | SaveResidueRB() |
---|
2012 | |
---|
2013 | def DumpVectorRB(event): |
---|
2014 | global resRBsel |
---|
2015 | if resRBsel not in data['Residue']: |
---|
2016 | return |
---|
2017 | rbData = data['Residue'][resRBsel] |
---|
2018 | pth = G2G.GetExportPath(G2frame) |
---|
2019 | dlg = wx.FileDialog(G2frame, 'Choose file to save residue rigid body', |
---|
2020 | pth, '', 'RRB files (*.resbody)|*.resbody', |
---|
2021 | wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT) |
---|
2022 | try: |
---|
2023 | if dlg.ShowModal() == wx.ID_OK: |
---|
2024 | filename = dlg.GetPath() |
---|
2025 | filename = os.path.splitext(filename)[0]+'.resbody' # set extension |
---|
2026 | fp = open(filename,'w') |
---|
2027 | fp.write('Name: '+rbData['RBname']+'\n') |
---|
2028 | fp.write('atNames: ') |
---|
2029 | for i in rbData['atNames']: |
---|
2030 | fp.write(str(i)+" ") |
---|
2031 | fp.write('\n') |
---|
2032 | for item in rbData['rbSeq']: |
---|
2033 | fp.write('rbSeq: ') |
---|
2034 | fp.write('{:d} {:d} {:.1f}: '.format(*item[:3])) |
---|
2035 | for num in item[3]: |
---|
2036 | fp.write('{:d} '.format(num)) |
---|
2037 | fp.write('\n') |
---|
2038 | for i,sym in enumerate(rbData['rbTypes']): |
---|
2039 | fp.write("{:3s}".format(sym)) |
---|
2040 | fp.write('{:8.5f}{:9.5f}{:9.5f} ' |
---|
2041 | .format(*rbData['rbXYZ'][i])) |
---|
2042 | fp.write('\n') |
---|
2043 | fp.close() |
---|
2044 | print ('Vector rigid body saved to: '+filename) |
---|
2045 | |
---|
2046 | finally: |
---|
2047 | dlg.Destroy() |
---|
2048 | |
---|
2049 | def LoadVectorRB(event): |
---|
2050 | AtInfo = data['Residue']['AtInfo'] |
---|
2051 | pth = G2G.GetExportPath(G2frame) |
---|
2052 | dlg = wx.FileDialog(G2frame, 'Choose file to read vector rigid body', |
---|
2053 | pth, '', 'RRB files (*.resbody)|*.resbody', |
---|
2054 | wx.FD_OPEN) |
---|
2055 | try: |
---|
2056 | if dlg.ShowModal() == wx.ID_OK: |
---|
2057 | filename = dlg.GetPath() |
---|
2058 | filename = os.path.splitext(filename)[0]+'.resbody' # set extension |
---|
2059 | fp = open(filename,'r') |
---|
2060 | l = fp.readline().strip() |
---|
2061 | if 'Name' not in l: |
---|
2062 | fp.close() |
---|
2063 | G2frame.ErrorDialog('Read Error', |
---|
2064 | 'File '+filename+' does not start with Name\nFirst line =' |
---|
2065 | +l+'\ninvalid file',parent=G2frame) |
---|
2066 | return |
---|
2067 | name = l.split(':')[1].strip() |
---|
2068 | line = fp.readline().strip().split(':')[1].split() |
---|
2069 | atNames = [i for i in line] |
---|
2070 | types = [] |
---|
2071 | coords = [] |
---|
2072 | l = fp.readline().strip() |
---|
2073 | rbSeq = [] |
---|
2074 | while 'rbSeq' in l: |
---|
2075 | tag,vals,lst = l.split(':') |
---|
2076 | seq = [] |
---|
2077 | for t,v in zip((int,int,float),vals.split()): |
---|
2078 | seq.append(t(v)) |
---|
2079 | seq.append([]) |
---|
2080 | for num in lst.split(): |
---|
2081 | seq[-1].append(int(num)) |
---|
2082 | rbSeq.append(seq) |
---|
2083 | l = fp.readline().strip() |
---|
2084 | while l: |
---|
2085 | nums = l.strip().split() |
---|
2086 | types.append(nums.pop(0)) |
---|
2087 | t = types[-1] |
---|
2088 | if t not in AtInfo: |
---|
2089 | Info = G2elem.GetAtomInfo(t) |
---|
2090 | AtInfo[t] = [Info['Drad'],Info['Color']] |
---|
2091 | coords.append([float(nums.pop(0)) for j in range(3)]) |
---|
2092 | l = fp.readline().strip() |
---|
2093 | fp.close() |
---|
2094 | else: |
---|
2095 | return |
---|
2096 | finally: |
---|
2097 | dlg.Destroy() |
---|
2098 | coords = np.array(coords) |
---|
2099 | rbid = ran.randint(0,sys.maxsize) |
---|
2100 | namelist = [data['Residue'][key]['RBname'] for key in data['Residue'] |
---|
2101 | if 'RBname' in data['Residue'][key]] |
---|
2102 | name = G2obj.MakeUniqueLabel(name,namelist) |
---|
2103 | data['Residue'][rbid] = {'RBname':name, |
---|
2104 | 'rbXYZ': coords, |
---|
2105 | 'rbRef':[0,1,2,False], |
---|
2106 | 'rbTypes':types, 'atNames':atNames, |
---|
2107 | 'useCount':0, |
---|
2108 | 'rbSeq':rbSeq, 'SelSeq':[0,0],} |
---|
2109 | data['RBIds']['Residue'].append(rbid) |
---|
2110 | UpdateResidueRB() |
---|
2111 | |
---|
2112 | def AddVectorRB(event): |
---|
2113 | 'Create a new vector rigid body' |
---|
2114 | AtInfo = data['Vector']['AtInfo'] |
---|
2115 | dlg = G2G.MultiIntegerDialog(G2frame,'New Rigid Body',['No. atoms','No. translations'],[3,1]) |
---|
2116 | if dlg.ShowModal() == wx.ID_OK: |
---|
2117 | nAtoms,nTrans = dlg.GetValues() |
---|
2118 | if nAtoms < 3: |
---|
2119 | dlg.Destroy() |
---|
2120 | G2G.G2MessageBox(G2frame,'A vector rigid body must have 3 or more atoms') |
---|
2121 | return |
---|
2122 | rbid = ran.randint(0,sys.maxsize) |
---|
2123 | vecMag = [1.0 for i in range(nTrans)] |
---|
2124 | vecRef = [False for i in range(nTrans)] |
---|
2125 | vecVal = [np.zeros((nAtoms,3)) for j in range(nTrans)] |
---|
2126 | rbTypes = ['C' for i in range(nAtoms)] |
---|
2127 | Info = G2elem.GetAtomInfo('C') |
---|
2128 | AtInfo['C'] = [Info['Drad'],Info['Color']] |
---|
2129 | name = 'UNKRB' |
---|
2130 | namelist = [data['Vector'][key]['RBname'] for key in data['Vector'] |
---|
2131 | if 'RBname' in data['Vector'][key]] |
---|
2132 | name = G2obj.MakeUniqueLabel(name,namelist) |
---|
2133 | data['Vector'][rbid] = {'RBname':name,'VectMag':vecMag,'rbXYZ':np.zeros((nAtoms,3)), |
---|
2134 | 'rbRef':[0,1,2,False],'VectRef':vecRef,'rbTypes':rbTypes,'rbVect':vecVal,'useCount':0} |
---|
2135 | data['RBIds']['Vector'].append(rbid) |
---|
2136 | dlg.Destroy() |
---|
2137 | UpdateVectorRB() |
---|
2138 | |
---|
2139 | def ExtractPhaseRB(event): |
---|
2140 | 'Extract a rigid body from a file with a phase' |
---|
2141 | def SetupDrawing(atmData): |
---|
2142 | '''Add the dicts needed for G2plt.PlotStructure to work to the |
---|
2143 | reader .Phase object |
---|
2144 | ''' |
---|
2145 | generalData = atmData['General'] |
---|
2146 | generalData['BondRadii'] = [] |
---|
2147 | |
---|
2148 | G2phG.SetDrawingDefaults(atmData['Drawing']) |
---|
2149 | atmData['Drawing'].update( |
---|
2150 | {'oldxy':[0.,0.],'Quaternion':[0.,0.,0.,1.],'cameraPos':150., |
---|
2151 | 'viewDir':[0,0,1],'atomPtrs': [2, 1, 6, 17], |
---|
2152 | }) |
---|
2153 | atmData['Drawing']['showRigidBodies'] = False |
---|
2154 | generalData['Map'] = {'MapType':False, 'rho':[]} |
---|
2155 | generalData['AtomTypes'] = [] |
---|
2156 | generalData['BondRadii'] = [] |
---|
2157 | generalData['AngleRadii'] = [] |
---|
2158 | generalData['vdWRadii'] = [] |
---|
2159 | generalData['Color'] = [] |
---|
2160 | generalData['Isotopes'] = {} |
---|
2161 | generalData['Isotope'] = {} |
---|
2162 | cx,ct,cs,cia = generalData['AtomPtrs'] |
---|
2163 | generalData['Mydir'] = G2frame.dirname |
---|
2164 | for iat,atom in enumerate(atmData['Atoms']): |
---|
2165 | atom[ct] = atom[ct].lower().capitalize() #force elem symbol to standard form |
---|
2166 | if atom[ct] not in generalData['AtomTypes'] and atom[ct] != 'UNK': |
---|
2167 | Info = G2elem.GetAtomInfo(atom[ct]) |
---|
2168 | if not Info: |
---|
2169 | atom[ct] = 'UNK' |
---|
2170 | continue |
---|
2171 | atom[ct] = Info['Symbol'] # N.B. symbol might be changed by GetAtomInfo |
---|
2172 | generalData['AtomTypes'].append(atom[ct]) |
---|
2173 | generalData['Z'] = Info['Z'] |
---|
2174 | generalData['Isotopes'][atom[ct]] = Info['Isotopes'] |
---|
2175 | generalData['BondRadii'].append(Info['Drad']) |
---|
2176 | generalData['AngleRadii'].append(Info['Arad']) |
---|
2177 | generalData['vdWRadii'].append(Info['Vdrad']) |
---|
2178 | if atom[ct] in generalData['Isotope']: |
---|
2179 | if generalData['Isotope'][atom[ct]] not in generalData['Isotopes'][atom[ct]]: |
---|
2180 | isotope = list(generalData['Isotopes'][atom[ct]].keys())[-1] |
---|
2181 | generalData['Isotope'][atom[ct]] = isotope |
---|
2182 | else: |
---|
2183 | generalData['Isotope'][atom[ct]] = 'Nat. Abund.' |
---|
2184 | if 'Nat. Abund.' not in generalData['Isotopes'][atom[ct]]: |
---|
2185 | isotope = list(generalData['Isotopes'][atom[ct]].keys())[-1] |
---|
2186 | generalData['Isotope'][atom[ct]] = isotope |
---|
2187 | generalData['Color'].append(Info['Color']) |
---|
2188 | # if generalData['Type'] == 'magnetic': |
---|
2189 | # if len(landeg) < len(generalData['AtomTypes']): |
---|
2190 | # landeg.append(2.0) |
---|
2191 | atmData['Drawing']['Atoms'] = [] |
---|
2192 | for atom in atmData['Atoms']: |
---|
2193 | atmData['Drawing']['Atoms'].append(G2mth.MakeDrawAtom(atmData,atom)) |
---|
2194 | |
---|
2195 | def onCancel(event,page=0): |
---|
2196 | 'complete or bail out from RB define, cleaning up' |
---|
2197 | G2frame.rbBook.DeletePage(G2frame.rbBook.FindPage(pagename)) |
---|
2198 | G2frame.rbBook.SetSelection(page) |
---|
2199 | |
---|
2200 | def Page1(): |
---|
2201 | '''Show the GUI for first stage of the rigid body with all atoms in |
---|
2202 | phase in crystal coordinates. Select the atoms to go onto the |
---|
2203 | next stage |
---|
2204 | ''' |
---|
2205 | def ShowSelection(selections): |
---|
2206 | 'respond to change in atom selections' |
---|
2207 | ct,cs = [1,8] |
---|
2208 | generalData = rd.Phase['General'] |
---|
2209 | for i,atom in enumerate(rd.Phase['Drawing']['Atoms']): |
---|
2210 | if i in selections: |
---|
2211 | factor = 1 |
---|
2212 | else: |
---|
2213 | factor = 2.5 |
---|
2214 | atNum = generalData['AtomTypes'].index(atom[ct]) |
---|
2215 | atom[cs] = list(np.array(generalData['Color'][atNum])//factor) |
---|
2216 | draw(*drawArgs) |
---|
2217 | def onPage1OK(event): |
---|
2218 | '1st section has been completed, move onto next' |
---|
2219 | G2frame.G2plotNB.Delete(rd.Phase['General']['Name']) |
---|
2220 | GetCoords(atmsel) |
---|
2221 | Page2() |
---|
2222 | |
---|
2223 | if 'macromolecular' == rd.Phase['General']['Type']: |
---|
2224 | # for PDB imports, lets see if a quick reformat of atoms list will work |
---|
2225 | rd.Phase['Atoms'] = [a[3:] for a in rd.Phase['Atoms']] |
---|
2226 | rd.Phase['General']['AtomPtrs'] = [i-3 for i in rd.Phase['General']['AtomPtrs']] |
---|
2227 | rd.Phase['General']['Type'] = 'nuclear' |
---|
2228 | SetupDrawing(rd.Phase) # add information to reader object to allow plotting |
---|
2229 | atomlist = [atom[0] for atom in rd.Phase['Atoms']] |
---|
2230 | atmsel = list(range(len(rd.Phase['Atoms']))) |
---|
2231 | # broken -- # why no bonds? |
---|
2232 | #for atm in rd.Phase['Drawing']['Atoms']: |
---|
2233 | # atm[6] = 'balls & sticks' |
---|
2234 | |
---|
2235 | draw,drawArgs = G2plt.PlotStructure(G2frame,rd.Phase,True) |
---|
2236 | ShowSelection(atmsel) |
---|
2237 | |
---|
2238 | if G2frame.rbBook.FindPage(pagename) is not None: |
---|
2239 | G2frame.rbBook.DeletePage(G2frame.rbBook.FindPage(pagename)) |
---|
2240 | |
---|
2241 | RBImp = wx.ScrolledWindow(G2frame.rbBook) |
---|
2242 | RBImpPnl = wx.Panel(RBImp) |
---|
2243 | G2frame.rbBook.AddPage(RBImp,pagename) |
---|
2244 | G2frame.rbBook.SetSelection(G2frame.rbBook.FindPage(pagename)) |
---|
2245 | |
---|
2246 | HelpInfo = ''' |
---|
2247 | This window shows all the atoms that were read from the |
---|
2248 | selected phase file. Select the atoms that will be used in the |
---|
2249 | rigid body processing (this may include atoms needed to |
---|
2250 | define an axis or origin that will not be included in the |
---|
2251 | eventual rigid body.) Note that in the plot window, |
---|
2252 | unselected atoms appear much darker than selected atoms. |
---|
2253 | ''' |
---|
2254 | mainSizer = G2G.G2MultiChoiceWindow(RBImpPnl, |
---|
2255 | 'Select atoms to import', |
---|
2256 | atomlist,atmsel,OnChange=ShowSelection, |
---|
2257 | helpText=HelpInfo) |
---|
2258 | |
---|
2259 | # OK/Cancel buttons |
---|
2260 | btnsizer = wx.StdDialogButtonSizer() |
---|
2261 | OKbtn = wx.Button(RBImpPnl, wx.ID_OK, 'Continue') |
---|
2262 | OKbtn.SetDefault() |
---|
2263 | btnsizer.AddButton(OKbtn) |
---|
2264 | OKbtn.Bind(wx.EVT_BUTTON,onPage1OK) |
---|
2265 | btn = wx.Button(RBImpPnl, wx.ID_CANCEL) |
---|
2266 | btn.Bind(wx.EVT_BUTTON,onCancel) |
---|
2267 | btnsizer.AddButton(btn) |
---|
2268 | btnsizer.Realize() |
---|
2269 | mainSizer.Add(btnsizer,0,wx.ALIGN_CENTER,50) |
---|
2270 | |
---|
2271 | RBImpPnl.SetSizer(mainSizer,True) |
---|
2272 | |
---|
2273 | mainSizer.Layout() |
---|
2274 | Size = mainSizer.GetMinSize() |
---|
2275 | Size[0] += 40 |
---|
2276 | Size[1] = max(Size[1],G2frame.GetSize()[1]-200) + 20 |
---|
2277 | RBImpPnl.SetSize(Size) |
---|
2278 | RBImp.SetScrollbars(10,10,Size[0]/10-4,Size[1]/10-1) |
---|
2279 | RBImp.Scroll(0,0) |
---|
2280 | |
---|
2281 | def Page2(): |
---|
2282 | '''Show the GUI for the second stage, where selected atoms are |
---|
2283 | now in Cartesian space, manipulate the axes and export selected |
---|
2284 | atoms to a vector or residue rigid body. |
---|
2285 | ''' |
---|
2286 | def UpdateDraw(event=None): |
---|
2287 | 'Called when info changes in grid, replots' |
---|
2288 | UpdateVectorBody(rbData) |
---|
2289 | DrawCallback() |
---|
2290 | |
---|
2291 | def onSetAll(event): |
---|
2292 | 'Set all atoms as selected' |
---|
2293 | grid.completeEdits() |
---|
2294 | for i in range(len(rd.Phase['RBselection'])): |
---|
2295 | rd.Phase['RBselection'][i] = 1 # table needs 0/1 for T/F |
---|
2296 | grid.ForceRefresh() |
---|
2297 | UpdateDraw() |
---|
2298 | |
---|
2299 | def onToggle(event): |
---|
2300 | 'Toggles selection state for all atoms' |
---|
2301 | grid.completeEdits() |
---|
2302 | for i in range(len(rd.Phase['RBselection'])): |
---|
2303 | rd.Phase['RBselection'][i] = int(not rd.Phase['RBselection'][i]) |
---|
2304 | grid.ForceRefresh() |
---|
2305 | UpdateDraw() |
---|
2306 | |
---|
2307 | def onSetOrigin(event): |
---|
2308 | 'Resets origin to midpoint between all selected atoms' |
---|
2309 | grid.completeEdits() |
---|
2310 | center = np.array([0.,0.,0.]) |
---|
2311 | count = 0 |
---|
2312 | for i in range(len(rd.Phase['RBselection'])): |
---|
2313 | if rd.Phase['RBselection'][i]: |
---|
2314 | count += 1 |
---|
2315 | center += rd.Phase['RBcoords'][i] |
---|
2316 | if count: |
---|
2317 | rd.Phase['RBcoords'] -= center/count |
---|
2318 | grid.ForceRefresh() |
---|
2319 | UpdateDraw() |
---|
2320 | |
---|
2321 | def onSetX(event): |
---|
2322 | grid.completeEdits() |
---|
2323 | center = np.array([0.,0.,0.]) |
---|
2324 | count = 0 |
---|
2325 | for i in range(len(rd.Phase['RBselection'])): |
---|
2326 | if rd.Phase['RBselection'][i]: |
---|
2327 | count += 1 |
---|
2328 | center += rd.Phase['RBcoords'][i] |
---|
2329 | if not count: |
---|
2330 | G2G.G2MessageBox(G2frame,'No atoms selected', |
---|
2331 | 'Selection required') |
---|
2332 | return |
---|
2333 | XYZP = center/count |
---|
2334 | if np.sqrt(sum(XYZP**2)) < 0.1: |
---|
2335 | G2G.G2MessageBox(G2frame, |
---|
2336 | 'The selected atom(s) are too close to the origin', |
---|
2337 | 'near origin') |
---|
2338 | return |
---|
2339 | if bntOpts['direction'] == 'y': |
---|
2340 | YP = XYZP / np.sqrt(np.sum(XYZP**2)) |
---|
2341 | ZP = np.cross((1,0,0),YP) |
---|
2342 | if sum(ZP*ZP) < .1: # pathological condition: Y' along X |
---|
2343 | ZP = np.cross((0,0,1),YP) |
---|
2344 | XP = np.cross(YP,ZP) |
---|
2345 | elif bntOpts['direction'] == 'z': |
---|
2346 | ZP = XYZP / np.sqrt(np.sum(XYZP**2)) |
---|
2347 | XP = np.cross((0,1,0),ZP) |
---|
2348 | if sum(XP*XP) < .1: # pathological condition: X' along Y |
---|
2349 | XP = np.cross((0,0,1),ZP) |
---|
2350 | YP = np.cross(ZP,XP) |
---|
2351 | else: |
---|
2352 | XP = XYZP / np.sqrt(np.sum(XYZP**2)) |
---|
2353 | YP = np.cross((0,0,1),XP) |
---|
2354 | if sum(YP*YP) < .1: # pathological condition: X' along Z |
---|
2355 | YP = np.cross((0,1,0),XP) |
---|
2356 | ZP = np.cross(XP,YP) |
---|
2357 | trans = np.array((XP,YP,ZP)) |
---|
2358 | # update atoms in place |
---|
2359 | rd.Phase['RBcoords'][:] = np.inner(trans,rd.Phase['RBcoords']).T |
---|
2360 | grid.ForceRefresh() |
---|
2361 | UpdateDraw() |
---|
2362 | |
---|
2363 | def onSetPlane(event): |
---|
2364 | '''Finds eigen vector/matrix for best "ellipsoid" about atoms; |
---|
2365 | rotate atoms so that smallest axis is along choice. |
---|
2366 | ''' |
---|
2367 | grid.completeEdits() |
---|
2368 | selList = [i==1 for i in rd.Phase['RBselection']] |
---|
2369 | XYZ = rd.Phase['RBcoords'][selList] |
---|
2370 | Natoms = len(XYZ) |
---|
2371 | if Natoms < 3: |
---|
2372 | G2G.G2MessageBox(G2frame,'A plane requires three or more atoms','Need more atoms') |
---|
2373 | return |
---|
2374 | Zmat = np.zeros((3,3)) |
---|
2375 | for xyz in XYZ: |
---|
2376 | Zmat += np.outer(xyz.T,xyz) |
---|
2377 | Evec,Emat = nl.eig(Zmat) |
---|
2378 | Order = np.argsort(np.nan_to_num(Evec)) #short-long order |
---|
2379 | if bntOpts['plane'] == 'xy': #short along z |
---|
2380 | trans = np.array([Emat[Order[2]],Emat[Order[1]],Emat[Order[0]]]) |
---|
2381 | elif bntOpts['plane'] == 'yz': #short along x |
---|
2382 | trans = np.array([Emat[Order[0]],Emat[Order[2]],Emat[Order[1]]]) |
---|
2383 | elif bntOpts['plane'] == 'xz': #short along y |
---|
2384 | trans = np.array([Emat[Order[1]],Emat[Order[0]],Emat[Order[2]]]) |
---|
2385 | else: |
---|
2386 | print('unexpected plane',bntOpts['plane']) |
---|
2387 | return |
---|
2388 | # update atoms in place |
---|
2389 | rd.Phase['RBcoords'][:] = np.inner(trans,rd.Phase['RBcoords']).T |
---|
2390 | grid.ForceRefresh() |
---|
2391 | UpdateDraw() |
---|
2392 | |
---|
2393 | def onWriteXYZ(event): |
---|
2394 | '''Writes selected atoms in a .xyz file for use in Avogadro, etc. |
---|
2395 | ''' |
---|
2396 | grid.completeEdits() |
---|
2397 | center = np.array([0.,0.,0.]) |
---|
2398 | count = 0 |
---|
2399 | for i in range(len(rd.Phase['RBselection'])): |
---|
2400 | if rd.Phase['RBselection'][i]: |
---|
2401 | count += 1 |
---|
2402 | center += rd.Phase['RBcoords'][i] |
---|
2403 | if count: |
---|
2404 | center /= count |
---|
2405 | else: |
---|
2406 | print('nothing selected') |
---|
2407 | return |
---|
2408 | obj = G2IO.ExportBaseclass(G2frame,'XYZ','.xyz') |
---|
2409 | #obj.InitExport(None) |
---|
2410 | if obj.ExportSelect(): # set export parameters; ask for file name |
---|
2411 | return |
---|
2412 | obj.OpenFile() |
---|
2413 | obj.Write(str(count)) |
---|
2414 | obj.Write('') |
---|
2415 | for i in range(len(rd.Phase['RBselection'])): |
---|
2416 | if rd.Phase['RBselection'][i]: |
---|
2417 | line = ' ' + rd.Phase['RBtypes'][i] |
---|
2418 | for xyz in rd.Phase['RBcoords'][i]: |
---|
2419 | line += ' ' + str(xyz) |
---|
2420 | obj.Write(line) |
---|
2421 | obj.CloseFile() |
---|
2422 | #GSASIIpath.IPyBreak() |
---|
2423 | |
---|
2424 | def onAddVector(event): |
---|
2425 | '''Adds selected atoms as a new vector rigid body. |
---|
2426 | Closes out the importer tab when done. |
---|
2427 | ''' |
---|
2428 | grid.completeEdits() |
---|
2429 | name = os.path.splitext(os.path.split(filename)[1])[0] |
---|
2430 | namelist = [data['Vector'][key]['RBname'] for key in |
---|
2431 | data['Vector'] if 'RBname' in data['Vector'][key]] |
---|
2432 | name = G2obj.MakeUniqueLabel(name,namelist) |
---|
2433 | rb = MakeVectorBody(name) |
---|
2434 | UpdateVectorBody(rb,True) |
---|
2435 | if len(rb['rbTypes']) < 3: return # must have at least 3 atoms |
---|
2436 | rbid = ran.randint(0,sys.maxsize) |
---|
2437 | data['Vector'][rbid] = rb |
---|
2438 | data['RBIds']['Vector'].append(rbid) |
---|
2439 | for t in rb['rbTypes']: |
---|
2440 | if t in data['Vector']['AtInfo']: continue |
---|
2441 | Info = G2elem.GetAtomInfo(t) |
---|
2442 | data['Vector']['AtInfo'][t] = [Info['Drad'],Info['Color']] |
---|
2443 | G2frame.G2plotNB.Delete('Rigid body') |
---|
2444 | onCancel(event,0) |
---|
2445 | |
---|
2446 | def onAddResidue(event): |
---|
2447 | '''Adds selected atoms as a new residue rigid body. |
---|
2448 | Closes out the importer tab when done. |
---|
2449 | ''' |
---|
2450 | grid.completeEdits() |
---|
2451 | name = os.path.split(filename)[1] |
---|
2452 | rbXYZ = [] |
---|
2453 | rbTypes = [] |
---|
2454 | atNames = [] |
---|
2455 | for i in rd.Phase['RBindex']: |
---|
2456 | if rd.Phase['RBselection'][i]: |
---|
2457 | rbXYZ.append(rd.Phase['RBcoords'][i]) |
---|
2458 | rbTypes.append(rd.Phase['RBtypes'][i]) |
---|
2459 | atNames.append(rd.Phase['RBlbls'][i]) |
---|
2460 | if len(rbTypes) < 3: return # must have at least 3 atoms |
---|
2461 | rbXYZ = np.array(rbXYZ) |
---|
2462 | rbid = ran.randint(0,sys.maxsize) |
---|
2463 | namelist = [data['Residue'][key]['RBname'] for key in |
---|
2464 | data['Residue'] if 'RBname' in data['Residue'][key]] |
---|
2465 | name = G2obj.MakeUniqueLabel(name,namelist) |
---|
2466 | data['Residue'][rbid] = {'RBname':name,'rbXYZ':rbXYZ, |
---|
2467 | 'rbTypes':rbTypes,'atNames':atNames,'rbRef':[0,1,2,False], |
---|
2468 | 'rbSeq':[],'SelSeq':[0,0],'useCount':0} |
---|
2469 | data['RBIds']['Residue'].append(rbid) |
---|
2470 | for t in rbTypes: |
---|
2471 | if t in data['Residue']['AtInfo']: continue |
---|
2472 | Info = G2elem.GetAtomInfo(t) |
---|
2473 | data['Residue']['AtInfo'][t] = [Info['Drad'],Info['Color']] |
---|
2474 | |
---|
2475 | print ('Rigid body added') |
---|
2476 | G2frame.G2plotNB.Delete('Rigid body') |
---|
2477 | onCancel(event,1) |
---|
2478 | |
---|
2479 | if G2frame.rbBook.FindPage(pagename) is not None: |
---|
2480 | G2frame.rbBook.DeletePage(G2frame.rbBook.FindPage(pagename)) |
---|
2481 | RBImp = wx.ScrolledWindow(G2frame.rbBook) |
---|
2482 | RBImpPnl = wx.Panel(RBImp) |
---|
2483 | G2frame.rbBook.AddPage(RBImp,pagename) |
---|
2484 | G2frame.rbBook.SetSelection(G2frame.rbBook.FindPage(pagename)) |
---|
2485 | AtInfo = {} |
---|
2486 | for t in rd.Phase['RBtypes']: |
---|
2487 | if t in AtInfo: continue |
---|
2488 | Info = G2elem.GetAtomInfo(t) |
---|
2489 | AtInfo[t] = [Info['Drad'],Info['Color']] |
---|
2490 | plotDefaults = {'oldxy':[0.,0.],'Quaternion':[0.,0.,0.,1.],'cameraPos':30.,'viewDir':[0,0,1],} |
---|
2491 | |
---|
2492 | rd.Phase['RBindex'] = list(range(len(rd.Phase['RBtypes']))) |
---|
2493 | rd.Phase['RBselection'] = len(rd.Phase['RBtypes']) * [1] |
---|
2494 | name = 'UNKRB' |
---|
2495 | namelist = [data['Vector'][key]['RBname'] for key in |
---|
2496 | data['Vector'] if 'RBname' in data['Vector'][key]] |
---|
2497 | name = G2obj.MakeUniqueLabel(name,namelist) |
---|
2498 | rbData = MakeVectorBody() |
---|
2499 | DrawCallback = G2plt.PlotRigidBody(G2frame,'Vector',AtInfo,rbData,plotDefaults) |
---|
2500 | |
---|
2501 | mainSizer = wx.BoxSizer(wx.HORIZONTAL) |
---|
2502 | btnSizer = wx.BoxSizer(wx.VERTICAL) |
---|
2503 | helpText = ''' |
---|
2504 | In this window, if wanted, |
---|
2505 | one can select one or more atoms and use them |
---|
2506 | to define an origin, a specified axis or place the selected atoms into |
---|
2507 | a selected plane. (Different sets of atoms can be used for each |
---|
2508 | operation.) |
---|
2509 | %%Once that is done, atoms can be selected and can be exported in a |
---|
2510 | "XYZ" file for use in a program such as Avogadro or can be used to |
---|
2511 | create a Vector or Residue rigid body. |
---|
2512 | ''' |
---|
2513 | btnSizer.Add(G2G.HelpButton(RBImpPnl,helpText,wrap=400), |
---|
2514 | 0,wx.ALIGN_RIGHT) |
---|
2515 | btnSizer.Add(wx.StaticText(RBImpPnl,wx.ID_ANY,'Reorder atoms by dragging'),0,wx.ALL) |
---|
2516 | btnSizer.Add((-1,15)) |
---|
2517 | btn = wx.Button(RBImpPnl, wx.ID_ANY, 'Set All') |
---|
2518 | btn.Bind(wx.EVT_BUTTON,onSetAll) |
---|
2519 | btnSizer.Add(btn,0,wx.ALIGN_CENTER) |
---|
2520 | btn = wx.Button(RBImpPnl, wx.ID_ANY, 'Toggle') |
---|
2521 | btn.Bind(wx.EVT_BUTTON,onToggle) |
---|
2522 | btnSizer.Add(btn,0,wx.ALIGN_CENTER) |
---|
2523 | btnSizer.Add((-1,15)) |
---|
2524 | btnSizer.Add(wx.StaticText(RBImpPnl,wx.ID_ANY,'Reorient using selected\natoms...'),0,wx.ALL) |
---|
2525 | btnSizer.Add((-1,5)) |
---|
2526 | btn = wx.Button(RBImpPnl, wx.ID_ANY, 'Set origin') |
---|
2527 | btn.Bind(wx.EVT_BUTTON,onSetOrigin) |
---|
2528 | btnSizer.Add(btn,0,wx.ALIGN_CENTER) |
---|
2529 | |
---|
2530 | bntOpts = {'plane':'xy','direction':'x'} |
---|
2531 | inSizer = wx.BoxSizer(wx.HORIZONTAL) |
---|
2532 | btn = wx.Button(RBImpPnl, wx.ID_ANY, 'Place in plane') |
---|
2533 | btn.Bind(wx.EVT_BUTTON,onSetPlane) |
---|
2534 | inSizer.Add(btn) |
---|
2535 | inSizer.Add(G2G.G2ChoiceButton(RBImpPnl,('xy','yz','xz'),None,None,bntOpts,'plane')) |
---|
2536 | btnSizer.Add(inSizer,0,wx.ALIGN_CENTER) |
---|
2537 | |
---|
2538 | inSizer = wx.BoxSizer(wx.HORIZONTAL) |
---|
2539 | btn = wx.Button(RBImpPnl, wx.ID_ANY, 'Define as') |
---|
2540 | btn.Bind(wx.EVT_BUTTON,onSetX) |
---|
2541 | inSizer.Add(btn) |
---|
2542 | inSizer.Add(G2G.G2ChoiceButton(RBImpPnl,('x','y','z'),None,None,bntOpts,'direction')) |
---|
2543 | btnSizer.Add(inSizer,0,wx.ALIGN_CENTER) |
---|
2544 | |
---|
2545 | btnSizer.Add((-1,15)) |
---|
2546 | btnSizer.Add(wx.StaticText(RBImpPnl,wx.ID_ANY,'Use selected atoms to\ncreate...'),0,wx.ALL) |
---|
2547 | btnSizer.Add((-1,5)) |
---|
2548 | btn = wx.Button(RBImpPnl, wx.ID_ANY, 'export as xyz') |
---|
2549 | btn.Bind(wx.EVT_BUTTON,onWriteXYZ) |
---|
2550 | btnSizer.Add(btn,0,wx.ALIGN_CENTER) |
---|
2551 | btnSizer.Add((-1,10)) |
---|
2552 | btn = wx.Button(RBImpPnl, wx.ID_ANY, 'a Vector Body') |
---|
2553 | btn.Bind(wx.EVT_BUTTON,onAddVector) |
---|
2554 | btnSizer.Add(btn,0,wx.ALIGN_CENTER) |
---|
2555 | btn = wx.Button(RBImpPnl, wx.ID_ANY, 'a Residue Body') |
---|
2556 | btn.Bind(wx.EVT_BUTTON,onAddResidue) |
---|
2557 | btnSizer.Add(btn,0,wx.ALIGN_CENTER) |
---|
2558 | btn = wx.Button(RBImpPnl, wx.ID_CANCEL) |
---|
2559 | btn.Bind(wx.EVT_BUTTON,onCancel) |
---|
2560 | btnSizer.Add((-1,10)) |
---|
2561 | btnSizer.Add(btn,0,wx.ALIGN_CENTER) |
---|
2562 | |
---|
2563 | mainSizer.Add(btnSizer) |
---|
2564 | mainSizer.Add((5,5)) |
---|
2565 | grid = DragableRBGrid(RBImpPnl,rd.Phase,UpdateDraw) |
---|
2566 | mainSizer.Add(grid) |
---|
2567 | RBImpPnl.SetSizer(mainSizer,True) |
---|
2568 | mainSizer.Layout() |
---|
2569 | Size = mainSizer.GetMinSize() |
---|
2570 | Size[0] += 40 |
---|
2571 | Size[1] = max(Size[1],G2frame.GetSize()[1]-200) + 20 |
---|
2572 | RBImpPnl.SetSize(Size) |
---|
2573 | RBImp.SetScrollbars(10,10,Size[0]/10-4,Size[1]/10-1) |
---|
2574 | RBImp.Scroll(0,0) |
---|
2575 | |
---|
2576 | def GetCoords(atmsel): |
---|
2577 | '''Create orthogonal coordinates for selected atoms. |
---|
2578 | Place the origin at the center of the body |
---|
2579 | ''' |
---|
2580 | atms = rd.Phase['Atoms'] |
---|
2581 | cell = rd.Phase['General']['Cell'][1:7] |
---|
2582 | Amat,Bmat = G2lat.cell2AB(cell) |
---|
2583 | rd.Phase['RBcoords'] = np.array([np.inner(Amat,atms[i][3:6]) for i in atmsel]) |
---|
2584 | rd.Phase['RBcoords'] -= rd.Phase['RBcoords'].mean(axis=0) # origin to middle |
---|
2585 | rd.Phase['RBtypes'] = [atms[i][1] for i in atmsel] |
---|
2586 | rd.Phase['RBlbls'] = [atms[i][0] for i in atmsel] |
---|
2587 | |
---|
2588 | def UpdateVectorBody(rb,useSelection=False): |
---|
2589 | '''Put the atoms in order to pass for plotting or for storage as |
---|
2590 | a vector rigid body. |
---|
2591 | |
---|
2592 | :param dict rb: rigid body contents created in :func:`MakeVectorBody` |
---|
2593 | :param bool useSelection: True if the rd.Phase['RBselection'] |
---|
2594 | values will be used to select which atoms are included in the |
---|
2595 | rigid body. If False (default) they are included in rb |
---|
2596 | and are used for plotting. |
---|
2597 | ''' |
---|
2598 | coordlist = [] |
---|
2599 | typeslist = [] |
---|
2600 | sellist = [] |
---|
2601 | for i in rd.Phase['RBindex']: |
---|
2602 | use = True |
---|
2603 | if useSelection and not rd.Phase['RBselection'][i]: use = False |
---|
2604 | if use: |
---|
2605 | coordlist.append(rd.Phase['RBcoords'][i]) |
---|
2606 | typeslist.append(rd.Phase['RBtypes'][i]) |
---|
2607 | sellist.append(rd.Phase['RBselection'][i]) |
---|
2608 | coordlist = np.array(coordlist) |
---|
2609 | rb['rbXYZ'] = coordlist |
---|
2610 | rb['rbVect'] = [coordlist] |
---|
2611 | rb['rbTypes'] = typeslist |
---|
2612 | if not useSelection: |
---|
2613 | rb['Selection'] = sellist |
---|
2614 | elif 'Selection' in rb: |
---|
2615 | del rb['Selection'] |
---|
2616 | |
---|
2617 | def MakeVectorBody(name=''): |
---|
2618 | '''Make the basic vector rigid body dict (w/o coordinates) used for |
---|
2619 | export and for plotting |
---|
2620 | ''' |
---|
2621 | vecMag = [1.0] |
---|
2622 | vecRef = [False] |
---|
2623 | rb = {'RBname':name,'VectMag':vecMag, |
---|
2624 | 'rbRef':[0,1,2,False],'VectRef':vecRef, |
---|
2625 | 'useCount':0} |
---|
2626 | UpdateVectorBody(rb) |
---|
2627 | return rb |
---|
2628 | |
---|
2629 | # too lazy to figure out why wx crashes |
---|
2630 | if wx.__version__.split('.')[0] != '4': |
---|
2631 | wx.MessageBox('Sorry, wxPython 4.x is required to run this command', |
---|
2632 | caption='Update Python', |
---|
2633 | style=wx.ICON_EXCLAMATION) |
---|
2634 | return |
---|
2635 | if platform.python_version()[:1] == '2': |
---|
2636 | wx.MessageBox('Sorry, Python >=3.x is required to run this command', |
---|
2637 | caption='Update Python', |
---|
2638 | style=wx.ICON_EXCLAMATION) |
---|
2639 | return |
---|
2640 | |
---|
2641 | # get importer type and a phase file of that type |
---|
2642 | G2sc.LoadG2fil() |
---|
2643 | choices = [rd.formatName for rd in G2sc.Readers['Phase']] |
---|
2644 | dlg = G2G.G2SingleChoiceDialog(G2frame,'Select the format of the file', |
---|
2645 | 'select format',choices) |
---|
2646 | dlg.CenterOnParent() |
---|
2647 | try: |
---|
2648 | if dlg.ShowModal() == wx.ID_OK: |
---|
2649 | col = dlg.GetSelection() |
---|
2650 | else: |
---|
2651 | col = None |
---|
2652 | return |
---|
2653 | finally: |
---|
2654 | dlg.Destroy() |
---|
2655 | reader = G2sc.Readers['Phase'][col] |
---|
2656 | |
---|
2657 | choices = reader.formatName + " file (" |
---|
2658 | w = "" |
---|
2659 | for extn in reader.extensionlist: |
---|
2660 | if w != "": w += ";" |
---|
2661 | w += "*" + extn |
---|
2662 | choices += w + ")|" + w |
---|
2663 | #choices += "|zip archive (.zip)|*.zip" |
---|
2664 | if not reader.strictExtension: |
---|
2665 | choices += "|any file (*.*)|*.*" |
---|
2666 | typ = '( type '+reader.formatName+')' |
---|
2667 | filelist = G2G.GetImportFile(G2frame, |
---|
2668 | message="Choose phase input file"+typ, |
---|
2669 | defaultFile="",wildcard=choices,style=wx.FD_OPEN) |
---|
2670 | if len(filelist) != 1: return |
---|
2671 | |
---|
2672 | # read in the phase file |
---|
2673 | filename = filelist[0] |
---|
2674 | rd = reader |
---|
2675 | with open(filename, 'r'): |
---|
2676 | rd.ReInitialize() |
---|
2677 | rd.errors = "" |
---|
2678 | if not rd.ContentsValidator(filename): # Report error |
---|
2679 | G2fl.G2Print("Warning: File {} has a validation error".format(filename)) |
---|
2680 | return |
---|
2681 | if len(rd.selections) > 1: |
---|
2682 | print("File {} has {} phases. This is unexpected." |
---|
2683 | .format(filename,len(rd.selections))) |
---|
2684 | return |
---|
2685 | |
---|
2686 | rd.objname = os.path.basename(filename) |
---|
2687 | try: |
---|
2688 | rd.Reader(filename) |
---|
2689 | except Exception as msg: |
---|
2690 | G2fl.G2Print("Warning: read of file {} failed\n{}".format( |
---|
2691 | filename,rd.errors)) |
---|
2692 | if GSASIIpath.GetConfigValue('debug'): |
---|
2693 | print(msg) |
---|
2694 | import traceback |
---|
2695 | print (traceback.format_exc()) |
---|
2696 | GSASIIpath.IPyBreak() |
---|
2697 | return |
---|
2698 | |
---|
2699 | pagename = 'Rigid body importer' |
---|
2700 | Page1() |
---|
2701 | return |
---|
2702 | |
---|
2703 | def AddVectTrans(event): |
---|
2704 | 'Add a translation to an existing vector rigid body' |
---|
2705 | choices = [] |
---|
2706 | rbIdlist = [] |
---|
2707 | for rbid in data['RBIds']['Vector']: |
---|
2708 | if rbid != 'AtInfo': |
---|
2709 | rbIdlist.append(rbid) |
---|
2710 | choices.append(data['Vector'][rbid]['RBname']) |
---|
2711 | if len(choices) == 0: |
---|
2712 | G2G.G2MessageBox(G2frame,'No Vector Rigid Bodies found', |
---|
2713 | 'No VR Bodies') |
---|
2714 | return |
---|
2715 | elif len(choices) == 1: |
---|
2716 | rbid = rbIdlist[0] |
---|
2717 | else: |
---|
2718 | dlg = G2G.G2SingleChoiceDialog(G2frame,'Select the rigid body to save', |
---|
2719 | 'select format',choices) |
---|
2720 | try: |
---|
2721 | if dlg.ShowModal() == wx.ID_OK: |
---|
2722 | rbid = rbIdlist[dlg.GetSelection()] |
---|
2723 | else: |
---|
2724 | return |
---|
2725 | finally: |
---|
2726 | dlg.Destroy() |
---|
2727 | data['Vector'][rbid]['VectMag'] += [1.0] |
---|
2728 | data['Vector'][rbid]['VectRef'] += [False] |
---|
2729 | nAtoms = len(data['Vector'][rbid]['rbXYZ']) |
---|
2730 | data['Vector'][rbid]['rbVect'] += [np.zeros((nAtoms,3))] |
---|
2731 | UpdateVectorRB() |
---|
2732 | |
---|
2733 | def SaveVectorRB(event): |
---|
2734 | choices = [] |
---|
2735 | rbIdlist = [] |
---|
2736 | for rbid in data['RBIds']['Vector']: |
---|
2737 | if rbid != 'AtInfo': |
---|
2738 | rbIdlist.append(rbid) |
---|
2739 | choices.append(data['Vector'][rbid]['RBname']) |
---|
2740 | if len(choices) == 0: |
---|
2741 | G2G.G2MessageBox(G2frame,'No Vector Rigid Bodies found', |
---|
2742 | 'No VR Bodies') |
---|
2743 | return |
---|
2744 | elif len(choices) == 1: |
---|
2745 | rbid = rbIdlist[0] |
---|
2746 | else: |
---|
2747 | dlg = G2G.G2SingleChoiceDialog(G2frame,'Select the rigid body to save', |
---|
2748 | 'select format',choices) |
---|
2749 | try: |
---|
2750 | if dlg.ShowModal() == wx.ID_OK: |
---|
2751 | rbid = rbIdlist[dlg.GetSelection()] |
---|
2752 | else: |
---|
2753 | return |
---|
2754 | finally: |
---|
2755 | dlg.Destroy() |
---|
2756 | |
---|
2757 | pth = G2G.GetExportPath(G2frame) |
---|
2758 | dlg = wx.FileDialog(G2frame, 'Choose file to save vector rigid body', |
---|
2759 | pth, '', 'VRB files (*.vecbody)|*.vecbody', |
---|
2760 | wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT) |
---|
2761 | try: |
---|
2762 | if dlg.ShowModal() == wx.ID_OK: |
---|
2763 | filename = dlg.GetPath() |
---|
2764 | filename = os.path.splitext(filename)[0]+'.vecbody' # set extension |
---|
2765 | fp = open(filename,'w') |
---|
2766 | fp.write('Name: '+data['Vector'][rbid]['RBname']+'\n') |
---|
2767 | fp.write('Trans: ') |
---|
2768 | for i in data['Vector'][rbid]['VectMag']: |
---|
2769 | fp.write(str(i)+" ") |
---|
2770 | fp.write('\n') |
---|
2771 | ntrans = len(data['Vector'][rbid]['VectMag']) |
---|
2772 | for i,sym in enumerate(data['Vector'][rbid]['rbTypes']): |
---|
2773 | fp.write("{:3s}".format(sym)) |
---|
2774 | for j in range(ntrans): |
---|
2775 | fp.write('{:8.5f}{:9.5f}{:9.5f} ' |
---|
2776 | .format(*data['Vector'][rbid]['rbVect'][j][i])) |
---|
2777 | fp.write('\n') |
---|
2778 | fp.close() |
---|
2779 | print ('Vector rigid body saved to: '+filename) |
---|
2780 | finally: |
---|
2781 | dlg.Destroy() |
---|
2782 | |
---|
2783 | def ReadVectorRB(event): |
---|
2784 | AtInfo = data['Vector']['AtInfo'] |
---|
2785 | pth = G2G.GetExportPath(G2frame) |
---|
2786 | dlg = wx.FileDialog(G2frame, 'Choose file to read vector rigid body', |
---|
2787 | pth, '', 'VRB files (*.vecbody)|*.vecbody', |
---|
2788 | wx.FD_OPEN) |
---|
2789 | try: |
---|
2790 | if dlg.ShowModal() == wx.ID_OK: |
---|
2791 | filename = dlg.GetPath() |
---|
2792 | filename = os.path.splitext(filename)[0]+'.vecbody' # set extension |
---|
2793 | fp = open(filename,'r') |
---|
2794 | l = fp.readline().strip() |
---|
2795 | if 'Name' not in l: |
---|
2796 | fp.close() |
---|
2797 | G2frame.ErrorDialog('Read Error', |
---|
2798 | 'File '+filename+' does not start with Name\nFirst line =' |
---|
2799 | +l+'\ninvalid file',parent=G2frame) |
---|
2800 | return |
---|
2801 | name = l.split(':')[1].strip() |
---|
2802 | trans = fp.readline().strip().split(':')[1].split() |
---|
2803 | vecMag = [float(i) for i in trans] |
---|
2804 | ntrans = len(trans) |
---|
2805 | vecs = [[] for i in range(ntrans)] |
---|
2806 | types = [] |
---|
2807 | l = fp.readline().strip() |
---|
2808 | while l: |
---|
2809 | nums = l.strip().split() |
---|
2810 | types.append(nums.pop(0)) |
---|
2811 | t = types[-1] |
---|
2812 | if t not in AtInfo: |
---|
2813 | Info = G2elem.GetAtomInfo(t) |
---|
2814 | AtInfo[t] = [Info['Drad'],Info['Color']] |
---|
2815 | for i in range(ntrans): |
---|
2816 | vecs[i].append([float(nums.pop(0)) for j in range(3)]) |
---|
2817 | l = fp.readline().strip() |
---|
2818 | fp.close() |
---|
2819 | else: |
---|
2820 | return |
---|
2821 | finally: |
---|
2822 | dlg.Destroy() |
---|
2823 | natoms = len(types) |
---|
2824 | vecs = [np.array(vecs[i]) for i in range(ntrans)] |
---|
2825 | rbid = ran.randint(0,sys.maxsize) |
---|
2826 | namelist = [data['Vector'][key]['RBname'] for key in data['Vector'] |
---|
2827 | if 'RBname' in data['Vector'][key]] |
---|
2828 | name = G2obj.MakeUniqueLabel(name,namelist) |
---|
2829 | data['Vector'][rbid] = {'RBname':name,'VectMag':vecMag, |
---|
2830 | 'rbXYZ':np.zeros((natoms,3)), |
---|
2831 | 'rbRef':[0,1,2,False],'VectRef':ntrans*[False], |
---|
2832 | 'rbTypes':types, |
---|
2833 | 'rbVect':vecs,'useCount':0} |
---|
2834 | data['RBIds']['Vector'].append(rbid) |
---|
2835 | UpdateVectorRB() |
---|
2836 | |
---|
2837 | def AddResidueRB(event): |
---|
2838 | global resRBsel |
---|
2839 | AtInfo = data['Residue']['AtInfo'] |
---|
2840 | macro = getMacroFile('rigid body') |
---|
2841 | if not macro: |
---|
2842 | return |
---|
2843 | macStr = macro.readline() |
---|
2844 | while macStr: |
---|
2845 | items = macStr.split() |
---|
2846 | if 'I' == items[0]: |
---|
2847 | resRBsel = ran.randint(0,sys.maxsize) |
---|
2848 | rbName = items[1] |
---|
2849 | rbTypes = [] |
---|
2850 | rbXYZ = [] |
---|
2851 | rbSeq = [] |
---|
2852 | atNames = [] |
---|
2853 | nAtms,nSeq,nOrig,mRef,nRef = [int(items[i]) for i in [2,3,4,5,6]] |
---|
2854 | for iAtm in range(nAtms): |
---|
2855 | macStr = macro.readline().split() |
---|
2856 | atName = macStr[0] |
---|
2857 | atType = macStr[1] |
---|
2858 | atNames.append(atName) |
---|
2859 | rbXYZ.append([float(macStr[i]) for i in [2,3,4]]) |
---|
2860 | rbTypes.append(atType) |
---|
2861 | if atType not in AtInfo: |
---|
2862 | Info = G2elem.GetAtomInfo(atType) |
---|
2863 | AtInfo[atType] = [Info['Drad'],Info['Color']] |
---|
2864 | rbXYZ = np.array(rbXYZ)-np.array(rbXYZ[nOrig-1]) |
---|
2865 | for iSeq in range(nSeq): |
---|
2866 | macStr = macro.readline().split() |
---|
2867 | mSeq = int(macStr[0]) |
---|
2868 | for jSeq in range(mSeq): |
---|
2869 | macStr = macro.readline().split() |
---|
2870 | iBeg = int(macStr[0])-1 |
---|
2871 | iFin = int(macStr[1])-1 |
---|
2872 | angle = 0.0 |
---|
2873 | nMove = int(macStr[2]) |
---|
2874 | iMove = [int(macStr[i])-1 for i in range(3,nMove+3)] |
---|
2875 | rbSeq.append([iBeg,iFin,angle,iMove]) |
---|
2876 | namelist = [data['Residue'][key]['RBname'] for key in |
---|
2877 | data['Residue'] if 'RBname' in data['Residue'][key]] |
---|
2878 | rbName = G2obj.MakeUniqueLabel(rbName,namelist) |
---|
2879 | data['Residue'][resRBsel] = {'RBname':rbName,'rbXYZ':rbXYZ,'rbTypes':rbTypes, |
---|
2880 | 'atNames':atNames,'rbRef':[nOrig-1,mRef-1,nRef-1,True],'rbSeq':rbSeq, |
---|
2881 | 'SelSeq':[0,0],'useCount':0,'molCent':None} |
---|
2882 | data['RBIds']['Residue'].append(resRBsel) |
---|
2883 | print ('Rigid body '+rbName+' added') |
---|
2884 | macStr = macro.readline() |
---|
2885 | macro.close() |
---|
2886 | UpdateResidueRB() |
---|
2887 | |
---|
2888 | def ImportResidueRB(): |
---|
2889 | global resRBsel |
---|
2890 | AtInfo = data['Residue']['AtInfo'] |
---|
2891 | text,ext = getTextFile() |
---|
2892 | if not text: |
---|
2893 | return |
---|
2894 | resRBsel = ran.randint(0,sys.maxsize) |
---|
2895 | rbTypes = [] |
---|
2896 | rbXYZ = [] |
---|
2897 | atNames = [] |
---|
2898 | txtStr = text.readline() |
---|
2899 | if 'xyz' in ext: |
---|
2900 | txtStr = text.readline() |
---|
2901 | txtStr = text.readline() |
---|
2902 | elif 'mol2' in ext: |
---|
2903 | while 'ATOM' not in txtStr: |
---|
2904 | txtStr = text.readline() |
---|
2905 | txtStr = text.readline() |
---|
2906 | elif 'pdb' in ext: |
---|
2907 | while 'ATOM' not in txtStr[:6] and 'HETATM' not in txtStr[:6]: |
---|
2908 | txtStr = text.readline() |
---|
2909 | items = txtStr.split() |
---|
2910 | nat = 1 |
---|
2911 | while len(items): |
---|
2912 | if 'txt' in ext: |
---|
2913 | atName = items[0] |
---|
2914 | atType = items[1] |
---|
2915 | rbXYZ.append([float(items[i]) for i in [2,3,4]]) |
---|
2916 | elif 'xyz' in ext: |
---|
2917 | atType = items[0] |
---|
2918 | rbXYZ.append([float(items[i]) for i in [1,2,3]]) |
---|
2919 | atName = '%s%d'%(atType,nat) |
---|
2920 | elif 'mol2' in ext: |
---|
2921 | atType = items[1] |
---|
2922 | atName = items[1]+items[0] |
---|
2923 | rbXYZ.append([float(items[i]) for i in [2,3,4]]) |
---|
2924 | elif 'pdb' in ext: |
---|
2925 | atType = items[-1] |
---|
2926 | if not items[2][-1].isnumeric(): |
---|
2927 | atName = '%s%d'%(items[2],nat) |
---|
2928 | else: |
---|
2929 | atName = '5s'%items[2] |
---|
2930 | xyz = txtStr[30:55].split() |
---|
2931 | rbXYZ.append([float(x) for x in xyz]) |
---|
2932 | atNames.append(atName) |
---|
2933 | rbTypes.append(atType) |
---|
2934 | if atType not in AtInfo: |
---|
2935 | Info = G2elem.GetAtomInfo(atType) |
---|
2936 | AtInfo[atType] = [Info['Drad'],Info['Color']] |
---|
2937 | txtStr = text.readline() |
---|
2938 | if 'mol2' in ext and 'BOND' in txtStr: |
---|
2939 | break |
---|
2940 | if 'pdb' in ext and ('ATOM' not in txtStr[:6] and 'HETATM' not in txtStr[:6]): |
---|
2941 | break |
---|
2942 | items = txtStr.split() |
---|
2943 | nat += 1 |
---|
2944 | if len(atNames) < 3: |
---|
2945 | G2G.G2MessageBox(G2frame,'Not enough atoms in rigid body; must be 3 or more') |
---|
2946 | else: |
---|
2947 | rbXYZ = np.array(rbXYZ)-np.array(rbXYZ[0]) |
---|
2948 | Xxyz = rbXYZ[1] |
---|
2949 | X = Xxyz/np.sqrt(np.sum(Xxyz**2)) |
---|
2950 | Yxyz = rbXYZ[2] |
---|
2951 | Y = Yxyz/np.sqrt(np.sum(Yxyz**2)) |
---|
2952 | Mat = G2mth.getRBTransMat(X,Y) |
---|
2953 | rbXYZ = np.inner(Mat,rbXYZ).T |
---|
2954 | name = 'UNKRB' |
---|
2955 | namelist = [data['Residue'][key]['RBname'] for key in data['Residue'] |
---|
2956 | if 'RBname' in data['Residue'][key]] |
---|
2957 | name = G2obj.MakeUniqueLabel(name,namelist) |
---|
2958 | data['Residue'][resRBsel] = {'RBname':name,'rbXYZ':rbXYZ,'rbTypes':rbTypes, |
---|
2959 | 'atNames':atNames,'rbRef':[0,1,2,False],'rbSeq':[],'SelSeq':[0,0],'useCount':0,'molCent':False} |
---|
2960 | data['RBIds']['Residue'].append(resRBsel) |
---|
2961 | print ('Rigid body UNKRB added') |
---|
2962 | text.close() |
---|
2963 | UpdateResidueRB() |
---|
2964 | |
---|
2965 | def SaveResidueRB(): |
---|
2966 | global resRBsel |
---|
2967 | pth = G2G.GetExportPath(G2frame) |
---|
2968 | dlg = wx.FileDialog(G2frame, 'Choose PDB file for Atom XYZ', pth, '', |
---|
2969 | 'PDB files (*.pdb)|*.pdb',wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT) |
---|
2970 | try: |
---|
2971 | if dlg.ShowModal() == wx.ID_OK: |
---|
2972 | filename = dlg.GetPath() |
---|
2973 | filename = os.path.splitext(filename)[0]+'.pdb' # make extension .pdb |
---|
2974 | File = open(filename,'w') |
---|
2975 | rbData = data['Residue'][resRBsel] |
---|
2976 | for iat,xyz in enumerate(rbData['rbXYZ']): |
---|
2977 | File.write('ATOM %6d %-4s%3s 1 %8.3f%8.3f%8.3f 1.00 0.00 %2s\n'%( |
---|
2978 | iat,rbData['atNames'][iat],rbData['RBname'][:3],xyz[0],xyz[1],xyz[2],rbData['rbTypes'][iat])) |
---|
2979 | File.close() |
---|
2980 | print ('Atom XYZ saved to: '+filename) |
---|
2981 | finally: |
---|
2982 | dlg.Destroy() |
---|
2983 | |
---|
2984 | |
---|
2985 | def FindNeighbors(Orig,XYZ,atTypes,atNames,AtInfo): |
---|
2986 | Radii = [] |
---|
2987 | for Atype in atTypes: |
---|
2988 | Radii.append(AtInfo[Atype][0]) |
---|
2989 | Radii = np.array(Radii) |
---|
2990 | Neigh = [] |
---|
2991 | Dx = XYZ-XYZ[Orig] |
---|
2992 | dist = np.sqrt(np.sum(Dx**2,axis=1)) |
---|
2993 | sumR = Radii[Orig]+Radii |
---|
2994 | IndB = ma.nonzero(ma.masked_greater(dist-0.85*sumR,0.)) |
---|
2995 | for j in IndB[0]: |
---|
2996 | if j != Orig and atTypes[j] != 'H': |
---|
2997 | Neigh.append(atNames[j]) |
---|
2998 | return Neigh |
---|
2999 | |
---|
3000 | def FindAllNeighbors(XYZ,atTypes,atNames,AtInfo): |
---|
3001 | NeighDict = {} |
---|
3002 | for iat,xyz in enumerate(atNames): |
---|
3003 | NeighDict[atNames[iat]] = FindNeighbors(iat,XYZ,atTypes,atNames,AtInfo) |
---|
3004 | return NeighDict |
---|
3005 | |
---|
3006 | def FindRiding(Orig,Pivot,NeighDict): |
---|
3007 | riding = [Orig,Pivot] |
---|
3008 | iAdd = 1 |
---|
3009 | new = True |
---|
3010 | while new: |
---|
3011 | newAtms = NeighDict[riding[iAdd]] |
---|
3012 | for At in newAtms: |
---|
3013 | new = False |
---|
3014 | if At not in riding: |
---|
3015 | riding.append(At) |
---|
3016 | new = True |
---|
3017 | iAdd += 1 |
---|
3018 | if iAdd < len(riding): |
---|
3019 | new = True |
---|
3020 | return riding[2:] |
---|
3021 | |
---|
3022 | def OnDefineTorsSeq(event): |
---|
3023 | global resRBsel |
---|
3024 | rbData = data['Residue'][resRBsel] |
---|
3025 | if not len(rbData): |
---|
3026 | return |
---|
3027 | atNames = rbData['atNames'] |
---|
3028 | AtInfo = data['Residue']['AtInfo'] |
---|
3029 | atTypes = rbData['rbTypes'] |
---|
3030 | XYZ = rbData['rbXYZ'] |
---|
3031 | neighDict = FindAllNeighbors(XYZ,atTypes,atNames,AtInfo) |
---|
3032 | TargList = [] |
---|
3033 | dlg = wx.SingleChoiceDialog(G2frame,'Select origin atom for torsion sequence','Origin atom',rbData['atNames']) |
---|
3034 | if dlg.ShowModal() == wx.ID_OK: |
---|
3035 | Orig = dlg.GetSelection() |
---|
3036 | TargList = neighDict[atNames[Orig]] |
---|
3037 | dlg.Destroy() |
---|
3038 | if not len(TargList): |
---|
3039 | return |
---|
3040 | dlg = wx.SingleChoiceDialog(G2frame,'Select pivot atom for torsion sequence','Pivot atom',TargList) |
---|
3041 | if dlg.ShowModal() == wx.ID_OK: |
---|
3042 | Piv = atNames.index(TargList[dlg.GetSelection()]) |
---|
3043 | riding = FindRiding(atNames[Orig],atNames[Piv],neighDict) |
---|
3044 | Riding = [] |
---|
3045 | for atm in riding: |
---|
3046 | Riding.append(atNames.index(atm)) |
---|
3047 | rbData['rbSeq'].append([Orig,Piv,0.0,Riding]) |
---|
3048 | dlg.Destroy() |
---|
3049 | UpdateResidueRB() |
---|
3050 | |
---|
3051 | def UpdateVectorRB(Scroll=0): |
---|
3052 | '''Display & edit a selected Vector RB |
---|
3053 | ''' |
---|
3054 | global resRBsel |
---|
3055 | def rbNameSizer(rbid,rbData): |
---|
3056 | |
---|
3057 | def OnRBName(event): |
---|
3058 | event.Skip() |
---|
3059 | Obj = event.GetEventObject() |
---|
3060 | name = Obj.GetValue() |
---|
3061 | if name == rbData['RBname']: return # no change |
---|
3062 | namelist = [data['Vector'][key]['RBname'] for key in |
---|
3063 | data['Vector'] if 'RBname' in data['Vector'][key]] |
---|
3064 | name = G2obj.MakeUniqueLabel(name,namelist) |
---|
3065 | rbData['RBname'] = name |
---|
3066 | wx.CallAfter(UpdateVectorRB) |
---|
3067 | |
---|
3068 | def OnDelRB(event): |
---|
3069 | Obj = event.GetEventObject() |
---|
3070 | rbid = Indx[Obj.GetId()] |
---|
3071 | if rbid in data['Vector']: |
---|
3072 | del data['Vector'][rbid] |
---|
3073 | data['RBIds']['Vector'].remove(rbid) |
---|
3074 | rbData['useCount'] -= 1 |
---|
3075 | wx.CallAfter(UpdateVectorRB) |
---|
3076 | |
---|
3077 | def OnPlotRB(event): |
---|
3078 | G2plt.PlotRigidBody(G2frame,'Vector',AtInfo,rbData,plotDefaults) |
---|
3079 | |
---|
3080 | # start of rbNameSizer |
---|
3081 | nameSizer = wx.BoxSizer(wx.HORIZONTAL) |
---|
3082 | nameSizer.Add(wx.StaticText(VectorRBDisplay,-1,'Rigid body name: '),0,WACV) |
---|
3083 | RBname = wx.TextCtrl(VectorRBDisplay,-1,rbData['RBname'], |
---|
3084 | style=wx.TE_PROCESS_ENTER) |
---|
3085 | RBname.Bind(wx.EVT_LEAVE_WINDOW, OnRBName) |
---|
3086 | RBname.Bind(wx.EVT_TEXT_ENTER,OnRBName) |
---|
3087 | RBname.Bind(wx.EVT_KILL_FOCUS,OnRBName) |
---|
3088 | nameSizer.Add(RBname,0,WACV) |
---|
3089 | nameSizer.Add((5,0),) |
---|
3090 | plotRB = wx.Button(VectorRBDisplay,wx.ID_ANY,'Plot',style=wx.BU_EXACTFIT) |
---|
3091 | plotRB.Bind(wx.EVT_BUTTON, OnPlotRB) |
---|
3092 | Indx[plotRB.GetId()] = rbid |
---|
3093 | nameSizer.Add(plotRB,0,WACV) |
---|
3094 | nameSizer.Add((5,0),) |
---|
3095 | if not rbData['useCount']: |
---|
3096 | delRB = wx.Button(VectorRBDisplay,wx.ID_ANY,"Delete",style=wx.BU_EXACTFIT) |
---|
3097 | delRB.Bind(wx.EVT_BUTTON, OnDelRB) |
---|
3098 | Indx[delRB.GetId()] = rbid |
---|
3099 | nameSizer.Add(delRB,0,WACV) |
---|
3100 | return nameSizer |
---|
3101 | |
---|
3102 | def rbRefAtmSizer(rbid,rbData): |
---|
3103 | |
---|
3104 | def OnRefSel(event): |
---|
3105 | Obj = event.GetEventObject() |
---|
3106 | iref = Indx[Obj.GetId()] |
---|
3107 | sel = Obj.GetValue() |
---|
3108 | rbData['rbRef'][iref] = atNames.index(sel) |
---|
3109 | FillRefChoice(rbid,rbData) |
---|
3110 | |
---|
3111 | refAtmSizer = wx.BoxSizer(wx.HORIZONTAL) |
---|
3112 | atNames = [name+str(i) for i,name in enumerate(rbData['rbTypes'])] |
---|
3113 | rbRef = rbData.get('rbRef',[0,1,2,False]) |
---|
3114 | rbData['rbRef'] = rbRef |
---|
3115 | if rbData['useCount']: |
---|
3116 | refAtmSizer.Add(wx.StaticText(VectorRBDisplay,-1, |
---|
3117 | 'Orientation reference atoms A-B-C: %s, %s, %s'%(atNames[rbRef[0]], \ |
---|
3118 | atNames[rbRef[1]],atNames[rbRef[2]])),0) |
---|
3119 | else: |
---|
3120 | refAtmSizer.Add(wx.StaticText(VectorRBDisplay,-1, |
---|
3121 | 'Orientation reference atoms A-B-C: '),0,WACV) |
---|
3122 | for i in range(3): |
---|
3123 | choices = [atNames[j] for j in refChoice[rbid][i]] |
---|
3124 | refSel = wx.ComboBox(VectorRBDisplay,-1,value='', |
---|
3125 | choices=choices,style=wx.CB_READONLY|wx.CB_DROPDOWN) |
---|
3126 | refSel.SetValue(atNames[rbRef[i]]) |
---|
3127 | refSel.Bind(wx.EVT_COMBOBOX, OnRefSel) |
---|
3128 | Indx[refSel.GetId()] = i |
---|
3129 | refAtmSizer.Add(refSel,0,WACV) |
---|
3130 | refHelpInfo = ''' |
---|
3131 | * The "Orientation Reference" control defines the Cartesian |
---|
3132 | axes for rigid bodies with the three atoms, A, B and C. |
---|
3133 | The vector from B to A defines the x-axis and the y axis is placed |
---|
3134 | in the plane defined by B to A and C to A. A,B,C must not be collinear. |
---|
3135 | ''' |
---|
3136 | hlp = G2G.HelpButton(VectorRBDisplay,refHelpInfo,wrap=400) |
---|
3137 | refAtmSizer.Add(hlp,0,wx.LEFT|wx.RIGHT|wx.ALIGN_CENTER_VERTICAL,2) |
---|
3138 | return refAtmSizer |
---|
3139 | |
---|
3140 | def rbVectMag(rbid,imag,rbData): |
---|
3141 | |
---|
3142 | def OnRBVectorMag(event): |
---|
3143 | event.Skip() |
---|
3144 | Obj = event.GetEventObject() |
---|
3145 | rbid,imag = Indx[Obj.GetId()] |
---|
3146 | try: |
---|
3147 | val = float(Obj.GetValue()) |
---|
3148 | if val <= 0.: |
---|
3149 | raise ValueError |
---|
3150 | rbData['VectMag'][imag] = val |
---|
3151 | except ValueError: |
---|
3152 | pass |
---|
3153 | Obj.SetValue('%8.4f'%(val)) |
---|
3154 | wx.CallAfter(UpdateVectorRB,VectorRB.GetScrollPos(wx.VERTICAL)) |
---|
3155 | G2plt.PlotRigidBody(G2frame,'Vector',AtInfo,data['Vector'][rbid],plotDefaults) |
---|
3156 | |
---|
3157 | def OnRBVectorRef(event): |
---|
3158 | Obj = event.GetEventObject() |
---|
3159 | rbid,imag = Indx[Obj.GetId()] |
---|
3160 | rbData['VectRef'][imag] = Obj.GetValue() |
---|
3161 | |
---|
3162 | magSizer = wx.BoxSizer(wx.HORIZONTAL) |
---|
3163 | magSizer.Add(wx.StaticText(VectorRBDisplay,-1,'Translation magnitude: '),0,WACV) |
---|
3164 | magValue = wx.TextCtrl(VectorRBDisplay,-1,'%8.4f'%(rbData['VectMag'][imag])) |
---|
3165 | Indx[magValue.GetId()] = [rbid,imag] |
---|
3166 | magValue.Bind(wx.EVT_TEXT_ENTER,OnRBVectorMag) |
---|
3167 | magValue.Bind(wx.EVT_KILL_FOCUS,OnRBVectorMag) |
---|
3168 | magSizer.Add(magValue,0,WACV) |
---|
3169 | magSizer.Add((5,0),) |
---|
3170 | magref = wx.CheckBox(VectorRBDisplay,label=' Refine?') |
---|
3171 | magref.SetValue(rbData['VectRef'][imag]) |
---|
3172 | magref.Bind(wx.EVT_CHECKBOX,OnRBVectorRef) |
---|
3173 | Indx[magref.GetId()] = [rbid,imag] |
---|
3174 | magSizer.Add(magref,0,WACV) |
---|
3175 | return magSizer |
---|
3176 | |
---|
3177 | def rbVectors(rbid,imag,mag,XYZ,rbData): |
---|
3178 | |
---|
3179 | def TypeSelect(event): |
---|
3180 | AtInfo = data['Vector']['AtInfo'] |
---|
3181 | r,c = event.GetRow(),event.GetCol() |
---|
3182 | if vecGrid.GetColLabelValue(c) == 'Type': |
---|
3183 | PE = G2elemGUI.PickElement(G2frame,oneOnly=True) |
---|
3184 | if PE.ShowModal() == wx.ID_OK: |
---|
3185 | if PE.Elem != 'None': |
---|
3186 | El = PE.Elem.strip().lower().capitalize() |
---|
3187 | if El not in AtInfo: |
---|
3188 | Info = G2elem.GetAtomInfo(El) |
---|
3189 | AtInfo[El] = [Info['Drad'],Info['Color']] |
---|
3190 | rbData['rbTypes'][r] = El |
---|
3191 | vecGrid.SetCellValue(r,c,El) |
---|
3192 | PE.Destroy() |
---|
3193 | wx.CallAfter(UpdateVectorRB,VectorRB.GetScrollPos(wx.VERTICAL)) |
---|
3194 | |
---|
3195 | def ChangeCell(event): |
---|
3196 | r,c = event.GetRow(),event.GetCol() |
---|
3197 | if r >= 0 and (0 <= c < 3): |
---|
3198 | try: |
---|
3199 | val = float(vecGrid.GetCellValue(r,c)) |
---|
3200 | rbData['rbVect'][imag][r][c] = val |
---|
3201 | except ValueError: |
---|
3202 | pass |
---|
3203 | G2plt.PlotRigidBody(G2frame,'Vector',AtInfo,data['Vector'][rbid],plotDefaults) |
---|
3204 | wx.CallAfter(UpdateVectorRB,VectorRB.GetScrollPos(wx.VERTICAL)) |
---|
3205 | |
---|
3206 | vecSizer = wx.BoxSizer() |
---|
3207 | Types = 3*[wg.GRID_VALUE_FLOAT+':10,5',]+[wg.GRID_VALUE_STRING,]+3*[wg.GRID_VALUE_FLOAT+':10,5',] |
---|
3208 | colLabels = ['Vector x','Vector y','Vector z','Type','Cart x','Cart y','Cart z'] |
---|
3209 | table = [] |
---|
3210 | rowLabels = [] |
---|
3211 | atNames = [] |
---|
3212 | for ivec,xyz in enumerate(rbData['rbVect'][imag]): |
---|
3213 | table.append(list(xyz)+[rbData['rbTypes'][ivec],]+list(XYZ[ivec])) |
---|
3214 | rowLabels.append(str(ivec)) |
---|
3215 | atNames.append(rbData['rbTypes'][ivec]+str(ivec)) |
---|
3216 | rbData['atNames'] = atNames |
---|
3217 | vecTable = G2G.Table(table,rowLabels=rowLabels,colLabels=colLabels,types=Types) |
---|
3218 | vecGrid = G2G.GSGrid(VectorRBDisplay) |
---|
3219 | vecGrid.SetTable(vecTable, True) |
---|
3220 | if 'phoenix' in wx.version(): |
---|
3221 | vecGrid.Bind(wg.EVT_GRID_CELL_CHANGED, ChangeCell) |
---|
3222 | else: |
---|
3223 | vecGrid.Bind(wg.EVT_GRID_CELL_CHANGE, ChangeCell) |
---|
3224 | if not imag: |
---|
3225 | vecGrid.Bind(wg.EVT_GRID_CELL_LEFT_DCLICK, TypeSelect) |
---|
3226 | attr = wx.grid.GridCellAttr() |
---|
3227 | attr.IncRef() |
---|
3228 | attr.SetEditor(G2G.GridFractionEditor(vecGrid)) |
---|
3229 | for c in range(3): |
---|
3230 | attr.IncRef() |
---|
3231 | vecGrid.SetColAttr(c, attr) |
---|
3232 | for row in range(vecTable.GetNumberRows()): |
---|
3233 | if imag: |
---|
3234 | vecGrid.SetCellStyle(row,3,VERY_LIGHT_GREY,True) |
---|
3235 | for col in [4,5,6]: |
---|
3236 | vecGrid.SetCellStyle(row,col,VERY_LIGHT_GREY,True) |
---|
3237 | # vecGrid.SetScrollRate(0,0) |
---|
3238 | vecGrid.AutoSizeColumns(False) |
---|
3239 | vecSizer.Add(vecGrid) |
---|
3240 | return vecSizer |
---|
3241 | |
---|
3242 | def FillRefChoice(rbid,rbData): |
---|
3243 | choiceIds = [i for i in range(len(rbData['rbTypes']))] |
---|
3244 | |
---|
3245 | rbRef = rbData.get('rbRef',[-1,-1,-1,False]) |
---|
3246 | for i in range(3): |
---|
3247 | if rbRef[i] in choiceIds: choiceIds.remove(rbRef[i]) |
---|
3248 | refChoice[rbid] = [choiceIds[:],choiceIds[:],choiceIds[:]] |
---|
3249 | for i in range(3): |
---|
3250 | refChoice[rbid][i].append(rbRef[i]) |
---|
3251 | refChoice[rbid][i].sort() |
---|
3252 | |
---|
3253 | def OnRBSelect(event): |
---|
3254 | global resRBsel |
---|
3255 | sel = rbSelect.GetSelection() |
---|
3256 | if sel == 0: return # 1st entry is blank |
---|
3257 | rbname = rbchoice[sel-1] |
---|
3258 | resRBsel = RBnames[rbname] |
---|
3259 | wx.CallLater(100,UpdateVectorRB) |
---|
3260 | |
---|
3261 | # beginning of UpdateVectorRB |
---|
3262 | AtInfo = data['Vector']['AtInfo'] |
---|
3263 | refChoice = {} |
---|
3264 | #RefObjs = [] |
---|
3265 | |
---|
3266 | GS = VectorRBDisplay.GetSizer() |
---|
3267 | if GS: |
---|
3268 | try: #get around a c++ error in wx 4.0; doing is again seems to be OK |
---|
3269 | GS.Clear(True) |
---|
3270 | except: |
---|
3271 | GS.Clear(True) |
---|
3272 | |
---|
3273 | RBnames = {} |
---|
3274 | for rbid in data['RBIds']['Vector']: |
---|
3275 | RBnames.update({data['Vector'][rbid]['RBname']:rbid,}) |
---|
3276 | if not RBnames: |
---|
3277 | return |
---|
3278 | rbchoice = list(RBnames.keys()) |
---|
3279 | rbchoice.sort() |
---|
3280 | if GS: |
---|
3281 | VectorRBSizer = GS |
---|
3282 | else: |
---|
3283 | VectorRBSizer = wx.BoxSizer(wx.VERTICAL) |
---|
3284 | if resRBsel not in data['RBIds']['Vector']: |
---|
3285 | resRBsel = RBnames[rbchoice[0]] |
---|
3286 | if len(RBnames) > 1: |
---|
3287 | selSizer = wx.BoxSizer(wx.HORIZONTAL) |
---|
3288 | selSizer.Add(wx.StaticText(VectorRBDisplay,label=' Select rigid body to view:'),0) |
---|
3289 | rbSelect = wx.ComboBox(VectorRBDisplay,choices=['']+rbchoice) |
---|
3290 | name = data['Vector'][resRBsel]['RBname'] |
---|
3291 | rbSelect.SetSelection(1+rbchoice.index(name)) |
---|
3292 | rbSelect.Bind(wx.EVT_COMBOBOX,OnRBSelect) |
---|
3293 | selSizer.Add(rbSelect,0) |
---|
3294 | VectorRBSizer.Add(selSizer,0) |
---|
3295 | rbData = data['Vector'][resRBsel] |
---|
3296 | if 'DELETED' in str(G2frame.GetStatusBar()): #seems to be no other way to do this (wx bug) |
---|
3297 | if GSASIIpath.GetConfigValue('debug'): |
---|
3298 | print ('DBG_wx error: Rigid Body/Status not cleanly deleted after Refine') |
---|
3299 | return |
---|
3300 | SetStatusLine(' You may use e.g. "c60" or "s60" for a vector entry') |
---|
3301 | FillRefChoice(resRBsel,rbData) |
---|
3302 | VectorRBSizer.Add(rbNameSizer(resRBsel,rbData),0) |
---|
3303 | VectorRBSizer.Add(rbRefAtmSizer(resRBsel,rbData),0) |
---|
3304 | XYZ = np.array([[0.,0.,0.] for Ty in rbData['rbTypes']]) |
---|
3305 | for imag,mag in enumerate(rbData['VectMag']): |
---|
3306 | XYZ += mag*rbData['rbVect'][imag] |
---|
3307 | VectorRBSizer.Add(rbVectMag(rbid,imag,rbData),0) |
---|
3308 | VectorRBSizer.Add(rbVectors(rbid,imag,mag,XYZ,rbData),0) |
---|
3309 | VectorRBSizer.Add((5,5),0) |
---|
3310 | data['Vector'][rbid]['rbXYZ'] = XYZ |
---|
3311 | |
---|
3312 | VectorRBSizer.Add((5,25),) |
---|
3313 | VectorRBSizer.Layout() |
---|
3314 | VectorRBDisplay.SetSizer(VectorRBSizer,True) |
---|
3315 | VectorRBDisplay.SetAutoLayout(True) |
---|
3316 | Size = VectorRBSizer.GetMinSize() |
---|
3317 | VectorRBDisplay.SetSize(Size) |
---|
3318 | |
---|
3319 | Size[0] += 40 |
---|
3320 | Size[1] = max(Size[1],450) + 20 |
---|
3321 | VectorRB.SetSize(Size) |
---|
3322 | VectorRB.SetScrollbars(10,10,Size[0]/10-4,Size[1]/10-1) |
---|
3323 | G2frame.dataWindow.SendSizeEvent() |
---|
3324 | |
---|
3325 | VectorRBDisplay.Show() |
---|
3326 | |
---|
3327 | |
---|
3328 | def UpdateResidueRB(): |
---|
3329 | '''Draw the contents of the Residue Rigid Body tab for Rigid Bodies tree entry |
---|
3330 | ''' |
---|
3331 | global resRBsel |
---|
3332 | def rbNameSizer(rbid,rbData): |
---|
3333 | |
---|
3334 | def OnRBName(event): |
---|
3335 | event.Skip() |
---|
3336 | Obj = event.GetEventObject() |
---|
3337 | name = Obj.GetValue() |
---|
3338 | if name == rbData['RBname']: return # no change |
---|
3339 | namelist = [data['Residue'][key]['RBname'] for key in |
---|
3340 | data['Residue'] if 'RBname' in data['Residue'][key]] |
---|
3341 | name = G2obj.MakeUniqueLabel(name,namelist) |
---|
3342 | rbData['RBname'] = name |
---|
3343 | wx.CallAfter(UpdateResidueRB) |
---|
3344 | |
---|
3345 | def OnDelRB(event): |
---|
3346 | Obj = event.GetEventObject() |
---|
3347 | rbid = Indx[Obj.GetId()] |
---|
3348 | if rbid in data['Residue']: |
---|
3349 | del data['Residue'][rbid] |
---|
3350 | data['RBIds']['Residue'].remove(rbid) |
---|
3351 | wx.CallAfter(UpdateResidueRB) |
---|
3352 | |
---|
3353 | def OnStripH(event): |
---|
3354 | Obj = event.GetEventObject() |
---|
3355 | rbid = Indx[Obj.GetId()] |
---|
3356 | if rbid in data['Residue']: |
---|
3357 | newNames = [] |
---|
3358 | newTypes = [] |
---|
3359 | newXYZ = [] |
---|
3360 | for i,atype in enumerate(rbData['rbTypes']): |
---|
3361 | if atype != 'H': |
---|
3362 | newNames.append(rbData['atNames'][i]) |
---|
3363 | newTypes.append(rbData['rbTypes'][i]) |
---|
3364 | newXYZ.append(rbData['rbXYZ'][i]) |
---|
3365 | rbData['atNames'] = newNames |
---|
3366 | rbData['rbTypes'] = newTypes |
---|
3367 | rbData['rbXYZ'] = newXYZ |
---|
3368 | G2plt.PlotRigidBody(G2frame,'Residue',AtInfo,rbData,plotDefaults) |
---|
3369 | wx.CallAfter(UpdateResidueRB) |
---|
3370 | |
---|
3371 | def OnPlotRB(event): |
---|
3372 | G2plt.PlotRigidBody(G2frame,'Residue',AtInfo,rbData,plotDefaults) |
---|
3373 | |
---|
3374 | # start of rbNameSizer |
---|
3375 | nameSizer = wx.BoxSizer(wx.HORIZONTAL) |
---|
3376 | nameSizer.Add(wx.StaticText(ResidueRBDisplay,-1,'Residue name: '),0,WACV) |
---|
3377 | RBname = wx.TextCtrl(ResidueRBDisplay,-1,rbData['RBname'], |
---|
3378 | style=wx.TE_PROCESS_ENTER) |
---|
3379 | RBname.Bind(wx.EVT_LEAVE_WINDOW, OnRBName) |
---|
3380 | RBname.Bind(wx.EVT_TEXT_ENTER,OnRBName) |
---|
3381 | RBname.Bind(wx.EVT_KILL_FOCUS,OnRBName) |
---|
3382 | nameSizer.Add(RBname,0,WACV) |
---|
3383 | nameSizer.Add((5,0),) |
---|
3384 | plotRB = wx.Button(ResidueRBDisplay,wx.ID_ANY,'Plot',style=wx.BU_EXACTFIT) |
---|
3385 | plotRB.Bind(wx.EVT_BUTTON, OnPlotRB) |
---|
3386 | Indx[plotRB.GetId()] = rbid |
---|
3387 | nameSizer.Add(plotRB,0,WACV) |
---|
3388 | nameSizer.Add((5,0),) |
---|
3389 | if not rbData['useCount']: |
---|
3390 | delRB = wx.Button(ResidueRBDisplay,wx.ID_ANY,"Delete",style=wx.BU_EXACTFIT) |
---|
3391 | delRB.Bind(wx.EVT_BUTTON, OnDelRB) |
---|
3392 | Indx[delRB.GetId()] = rbid |
---|
3393 | nameSizer.Add(delRB,0,WACV) |
---|
3394 | if 'H' in rbData['rbTypes']: |
---|
3395 | stripH = wx.Button(ResidueRBDisplay,wx.ID_ANY,"Strip H-atoms",style=wx.BU_EXACTFIT) |
---|
3396 | stripH.Bind(wx.EVT_BUTTON, OnStripH) |
---|
3397 | Indx[stripH.GetId()] = rbid |
---|
3398 | nameSizer.Add(stripH,0,WACV) |
---|
3399 | nameSizer.Add(wx.StaticText(ResidueRBDisplay,-1,' body type #'+ |
---|
3400 | str(data['RBIds']['Residue'].index(rbid))),0,WACV) |
---|
3401 | return nameSizer |
---|
3402 | |
---|
3403 | def rbResidues(rbid,rbData): |
---|
3404 | |
---|
3405 | def TypeSelect(event): |
---|
3406 | AtInfo = data['Residue']['AtInfo'] |
---|
3407 | r,c = event.GetRow(),event.GetCol() |
---|
3408 | if resGrid.GetColLabelValue(c) == 'Type': |
---|
3409 | PE = G2elemGUI.PickElement(G2frame,oneOnly=True) |
---|
3410 | if PE.ShowModal() == wx.ID_OK: |
---|
3411 | if PE.Elem != 'None': |
---|
3412 | El = PE.Elem.strip().lower().capitalize() |
---|
3413 | if El not in AtInfo: |
---|
3414 | Info = G2elem.GetAtomInfo(El) |
---|
3415 | AtInfo[El] = [Info['Drad'],Info['Color']] |
---|
3416 | rbData['rbTypes'][r] = El |
---|
3417 | resGrid.SetCellValue(r,c,El) |
---|
3418 | PE.Destroy() |
---|
3419 | |
---|
3420 | def ChangeCell(event): |
---|
3421 | r,c = event.GetRow(),event.GetCol() |
---|
3422 | if c == 0: |
---|
3423 | rbData['atNames'][r] = resGrid.GetCellValue(r,c) |
---|
3424 | if r >= 0 and (2 <= c <= 4): |
---|
3425 | try: |
---|
3426 | val = float(resGrid.GetCellValue(r,c)) |
---|
3427 | rbData['rbXYZ'][r][c-2] = val |
---|
3428 | except ValueError: |
---|
3429 | pass |
---|
3430 | G2plt.PlotRigidBody(G2frame,'Residue',AtInfo,rbData,plotDefaults) |
---|
3431 | |
---|
3432 | def OnRefSel(event): |
---|
3433 | Obj = event.GetEventObject() |
---|
3434 | iref,res,jref = Indx[Obj.GetId()] |
---|
3435 | sel = Obj.GetValue() |
---|
3436 | ind = atNames.index(sel) |
---|
3437 | if rbData['rbTypes'][ind] == 'H': |
---|
3438 | G2G.G2MessageBox(G2frame,'You should not select an H-atom for rigid body orientation') |
---|
3439 | rbData['rbRef'][iref] = ind |
---|
3440 | FillRefChoice(rbid,rbData) |
---|
3441 | for i,ref in enumerate(RefObjs[jref]): |
---|
3442 | ref.SetItems([atNames[j] for j in refChoice[rbid][i]]) |
---|
3443 | ref.SetValue(atNames[rbData['rbRef'][i]]) |
---|
3444 | rbXYZ = rbData['rbXYZ'] |
---|
3445 | if not iref: #origin change |
---|
3446 | rbXYZ -= rbXYZ[ind] |
---|
3447 | Xxyz = rbXYZ[rbData['rbRef'][1]] |
---|
3448 | X = Xxyz/np.sqrt(np.sum(Xxyz**2)) |
---|
3449 | Yxyz = rbXYZ[rbData['rbRef'][2]] |
---|
3450 | Y = Yxyz/np.sqrt(np.sum(Yxyz**2)) |
---|
3451 | Mat = G2mth.getRBTransMat(X,Y) |
---|
3452 | rbXYZ = np.inner(Mat,rbXYZ).T |
---|
3453 | rbData['rbXYZ'] = rbXYZ |
---|
3454 | rbData['molCent'] = False |
---|
3455 | res.ClearSelection() |
---|
3456 | resTable = res.GetTable() |
---|
3457 | for r in range(res.GetNumberRows()): |
---|
3458 | row = resTable.GetRowValues(r) |
---|
3459 | row[2:4] = rbXYZ[r] |
---|
3460 | resTable.SetRowValues(r,row) |
---|
3461 | res.ForceRefresh() |
---|
3462 | G2plt.PlotRigidBody(G2frame,'Residue',AtInfo,rbData,plotDefaults) |
---|
3463 | |
---|
3464 | def OnMolCent(event): |
---|
3465 | rbData['molCent'] = not rbData['molCent'] |
---|
3466 | if rbData['molCent']: |
---|
3467 | Obj = event.GetEventObject() |
---|
3468 | res = Indx[Obj.GetId()] |
---|
3469 | rbXYZ = rbData['rbXYZ'] |
---|
3470 | rbCent = np.array([np.sum(rbXYZ[:,0]),np.sum(rbXYZ[:,1]),np.sum(rbXYZ[:,2])])/rbXYZ.shape[0] |
---|
3471 | rbXYZ -= rbCent |
---|
3472 | rbData['rbXYZ'] = rbXYZ |
---|
3473 | res.ClearSelection() |
---|
3474 | resTable = res.GetTable() |
---|
3475 | for r in range(res.GetNumberRows()): |
---|
3476 | row = resTable.GetRowValues(r) |
---|
3477 | row[2:4] = rbXYZ[r] |
---|
3478 | resTable.SetRowValues(r,row) |
---|
3479 | res.ForceRefresh() |
---|
3480 | G2plt.PlotRigidBody(G2frame,'Residue',AtInfo,rbData,plotDefaults) |
---|
3481 | |
---|
3482 | def OnCycleXYZ(event): |
---|
3483 | Obj = event.GetEventObject() |
---|
3484 | res = Indx[Obj.GetId()] |
---|
3485 | rbXYZ = rbData['rbXYZ'] |
---|
3486 | resTable = res.GetTable() |
---|
3487 | for r in range(res.GetNumberRows()): |
---|
3488 | rbXYZ[r] = np.roll(rbXYZ[r],1) |
---|
3489 | row = resTable.GetRowValues(r) |
---|
3490 | row[2:4] = rbXYZ[r] |
---|
3491 | resTable.SetRowValues(r,row) |
---|
3492 | res.ForceRefresh() |
---|
3493 | G2plt.PlotRigidBody(G2frame,'Residue',AtInfo,rbData,plotDefaults) |
---|
3494 | |
---|
3495 | Types = 2*[wg.GRID_VALUE_STRING,]+3*[wg.GRID_VALUE_FLOAT+':10,5',] |
---|
3496 | colLabels = ['Name','Type','Cart x','Cart y','Cart z'] |
---|
3497 | table = [] |
---|
3498 | rowLabels = [] |
---|
3499 | for ivec,xyz in enumerate(rbData['rbXYZ']): |
---|
3500 | table.append([rbData['atNames'][ivec],]+[rbData['rbTypes'][ivec],]+list(xyz)) |
---|
3501 | rowLabels.append(str(ivec)) |
---|
3502 | vecTable = G2G.Table(table,rowLabels=rowLabels,colLabels=colLabels,types=Types) |
---|
3503 | resGrid = G2G.GSGrid(ResidueRBDisplay) |
---|
3504 | Indx[resGrid.GetId()] = rbid |
---|
3505 | resList.append(resGrid) |
---|
3506 | resGrid.SetTable(vecTable, True) |
---|
3507 | if 'phoenix' in wx.version(): |
---|
3508 | resGrid.Bind(wg.EVT_GRID_CELL_CHANGED, ChangeCell) |
---|
3509 | else: |
---|
3510 | resGrid.Bind(wg.EVT_GRID_CELL_CHANGE, ChangeCell) |
---|
3511 | resGrid.Bind(wg.EVT_GRID_CELL_LEFT_DCLICK, TypeSelect) |
---|
3512 | for c in range(2,5): |
---|
3513 | attr = wx.grid.GridCellAttr() |
---|
3514 | attr.IncRef() |
---|
3515 | attr.SetEditor(G2G.GridFractionEditor(resGrid)) |
---|
3516 | resGrid.SetColAttr(c, attr) |
---|
3517 | for row in range(vecTable.GetNumberRows()): |
---|
3518 | resGrid.SetReadOnly(row,1,True) |
---|
3519 | resGrid.AutoSizeColumns(False) |
---|
3520 | vecSizer = wx.BoxSizer() |
---|
3521 | vecSizer.Add(resGrid) |
---|
3522 | |
---|
3523 | refAtmSizer = wx.BoxSizer(wx.HORIZONTAL) |
---|
3524 | atNames = rbData['atNames'] |
---|
3525 | rbRef = rbData['rbRef'] |
---|
3526 | if rbData['rbRef'][3] or rbData['useCount']: |
---|
3527 | refAtmSizer.Add(wx.StaticText(ResidueRBDisplay,-1, |
---|
3528 | 'Orientation reference non-H atoms A-B-C: %s, %s, %s'%(atNames[rbRef[0]], \ |
---|
3529 | atNames[rbRef[1]],atNames[rbRef[2]])),0) |
---|
3530 | else: |
---|
3531 | refAtmSizer.Add(wx.StaticText(ResidueRBDisplay,-1, |
---|
3532 | 'Orientation reference non-H atoms A-B-C: '),0,WACV) |
---|
3533 | refObj = [0,0,0] |
---|
3534 | for i in range(3): |
---|
3535 | choices = [atNames[j] for j in refChoice[rbid][i]] |
---|
3536 | refSel = wx.ComboBox(ResidueRBDisplay,-1,value='', |
---|
3537 | choices=choices,style=wx.CB_READONLY|wx.CB_DROPDOWN) |
---|
3538 | refSel.SetValue(atNames[rbRef[i]]) |
---|
3539 | refSel.Bind(wx.EVT_COMBOBOX, OnRefSel) |
---|
3540 | Indx[refSel.GetId()] = [i,resGrid,len(RefObjs)] |
---|
3541 | refObj[i] = refSel |
---|
3542 | refAtmSizer.Add(refSel,0,WACV) |
---|
3543 | RefObjs.append(refObj) |
---|
3544 | cycleXYZ = wx.Button(ResidueRBDisplay,label=' Cycle XYZ?') |
---|
3545 | cycleXYZ.Bind(wx.EVT_BUTTON,OnCycleXYZ) |
---|
3546 | Indx[cycleXYZ.GetId()] = resGrid |
---|
3547 | refAtmSizer.Add(cycleXYZ,0,WACV) |
---|
3548 | if 'molCent' not in rbData: rbData['molCent'] = False #patch |
---|
3549 | molcent = wx.Button(ResidueRBDisplay,label=' Center RB?') |
---|
3550 | molcent.Bind(wx.EVT_BUTTON,OnMolCent) |
---|
3551 | Indx[molcent.GetId()] = resGrid |
---|
3552 | refAtmSizer.Add(molcent,0,WACV) |
---|
3553 | refHelpInfo = ''' |
---|
3554 | * The "Orientation Reference" control defines the Cartesian |
---|
3555 | axes for rigid bodies with the three atoms, A, B and C. |
---|
3556 | The vector from B to A defines the x-axis and the y axis is placed |
---|
3557 | in the plane defined by B to A and C to A. A,B,C must not be collinear. |
---|
3558 | |
---|
3559 | %%* The origin is at A unless the "Center RB?" button is pressed. |
---|
3560 | |
---|
3561 | %%* The 'Cycle XYZ' button will permute the rigid body XYZ coordinates so |
---|
3562 | XYZ --> ZXY. Repeat if needed. |
---|
3563 | |
---|
3564 | %%* The "Center RB?" button will shift the origin of the |
---|
3565 | rigid body to be the midpoint of all atoms in the body (not mass weighted). |
---|
3566 | ''' |
---|
3567 | hlp = G2G.HelpButton(ResidueRBDisplay,refHelpInfo,wrap=400) |
---|
3568 | refAtmSizer.Add(hlp,0,wx.LEFT|wx.RIGHT|wx.ALIGN_CENTER_VERTICAL,2) |
---|
3569 | |
---|
3570 | mainSizer = wx.BoxSizer(wx.VERTICAL) |
---|
3571 | mainSizer.Add(refAtmSizer) |
---|
3572 | mainSizer.Add(vecSizer) |
---|
3573 | return mainSizer |
---|
3574 | |
---|
3575 | def Add2SeqSizer(seqSizer,angSlide,rbid,iSeq,Seq,atNames): |
---|
3576 | |
---|
3577 | def ChangeAngle(event): |
---|
3578 | event.Skip() |
---|
3579 | Obj = event.GetEventObject() |
---|
3580 | rbid,Seq = Indx[Obj.GetId()][:2] |
---|
3581 | val = Seq[2] |
---|
3582 | try: |
---|
3583 | val = float(Obj.GetValue()) |
---|
3584 | Seq[2] = val |
---|
3585 | except ValueError: |
---|
3586 | pass |
---|
3587 | Obj.SetValue('%8.2f'%(val)) |
---|
3588 | G2plt.PlotRigidBody(G2frame,'Residue',AtInfo,data['Residue'][rbid],plotDefaults) |
---|
3589 | |
---|
3590 | def OnRadBtn(event): |
---|
3591 | Obj = event.GetEventObject() |
---|
3592 | Seq,iSeq,angId = Indx[Obj.GetId()] |
---|
3593 | data['Residue'][rbid]['SelSeq'] = [iSeq,angId] |
---|
3594 | angSlide.SetValue(int(100*Seq[2])) |
---|
3595 | |
---|
3596 | def OnDelBtn(event): |
---|
3597 | Obj = event.GetEventObject() |
---|
3598 | rbid,Seq = Indx[Obj.GetId()] |
---|
3599 | data['Residue'][rbid]['rbSeq'].remove(Seq) |
---|
3600 | wx.CallAfter(UpdateResidueRB) |
---|
3601 | |
---|
3602 | iBeg,iFin,angle,iMove = Seq |
---|
3603 | ang = wx.TextCtrl(ResidueRBDisplay,wx.ID_ANY, |
---|
3604 | '%8.2f'%(angle),size=(70,-1),style=wx.TE_PROCESS_ENTER) |
---|
3605 | if not iSeq: |
---|
3606 | radBt = wx.RadioButton(ResidueRBDisplay,wx.ID_ANY, |
---|
3607 | '',style=wx.RB_GROUP) |
---|
3608 | data['Residue'][rbid]['SelSeq'] = [iSeq,ang.GetId()] |
---|
3609 | radBt.SetValue(True) |
---|
3610 | else: |
---|
3611 | radBt = wx.RadioButton(ResidueRBDisplay,wx.ID_ANY,'') |
---|
3612 | radBt.Bind(wx.EVT_RADIOBUTTON,OnRadBtn) |
---|
3613 | seqSizer.Add(radBt) |
---|
3614 | delBt = wx.Button(ResidueRBDisplay,wx.ID_ANY,'Del', |
---|
3615 | style=wx.BU_EXACTFIT) |
---|
3616 | delBt.Bind(wx.EVT_BUTTON,OnDelBtn) |
---|
3617 | seqSizer.Add(delBt) |
---|
3618 | bond = wx.StaticText(ResidueRBDisplay,wx.ID_ANY, |
---|
3619 | '%s %s'%(atNames[iBeg],atNames[iFin]),size=(50,20)) |
---|
3620 | seqSizer.Add(bond,0,WACV) |
---|
3621 | Indx[radBt.GetId()] = [Seq,iSeq,ang.GetId()] |
---|
3622 | Indx[delBt.GetId()] = [rbid,Seq] |
---|
3623 | Indx[ang.GetId()] = [rbid,Seq,ang] |
---|
3624 | ang.Bind(wx.EVT_TEXT_ENTER,ChangeAngle) |
---|
3625 | ang.Bind(wx.EVT_KILL_FOCUS,ChangeAngle) |
---|
3626 | seqSizer.Add(ang,0,WACV) |
---|
3627 | atms = '' |
---|
3628 | for i in iMove: |
---|
3629 | atms += ' %s,'%(atNames[i]) |
---|
3630 | moves = wx.StaticText(ResidueRBDisplay,wx.ID_ANY, |
---|
3631 | atms[:-1],size=(200,20)) |
---|
3632 | seqSizer.Add(moves,1,wx.EXPAND|wx.RIGHT) |
---|
3633 | return seqSizer |
---|
3634 | |
---|
3635 | def SlideSizer(): |
---|
3636 | |
---|
3637 | def OnSlider(event): |
---|
3638 | Obj = event.GetEventObject() |
---|
3639 | rbData = Indx[Obj.GetId()] |
---|
3640 | iSeq,angId = rbData['SelSeq'] |
---|
3641 | val = float(Obj.GetValue())/100. |
---|
3642 | rbData['rbSeq'][iSeq][2] = val |
---|
3643 | Indx[angId][2].SetValue('%8.2f'%(val)) |
---|
3644 | G2plt.PlotRigidBody(G2frame,'Residue',AtInfo,rbData,plotDefaults) |
---|
3645 | |
---|
3646 | slideSizer = wx.BoxSizer(wx.HORIZONTAL) |
---|
3647 | slideSizer.Add(wx.StaticText(ResidueRBDisplay,-1,'Selected torsion angle:'),0) |
---|
3648 | iSeq,angId = rbData['SelSeq'] |
---|
3649 | angSlide = wx.Slider(ResidueRBDisplay,-1, |
---|
3650 | int(100*rbData['rbSeq'][iSeq][2]),0,36000,size=(200,20), |
---|
3651 | style=wx.SL_HORIZONTAL) |
---|
3652 | angSlide.Bind(wx.EVT_SLIDER, OnSlider) |
---|
3653 | Indx[angSlide.GetId()] = rbData |
---|
3654 | slideSizer.Add(angSlide,0) |
---|
3655 | return slideSizer,angSlide |
---|
3656 | |
---|
3657 | def FillRefChoice(rbid,rbData): |
---|
3658 | choiceIds = [i for i in range(len(rbData['atNames']))] |
---|
3659 | for seq in rbData['rbSeq']: |
---|
3660 | for i in seq[3]: |
---|
3661 | try: |
---|
3662 | choiceIds.remove(i) |
---|
3663 | except ValueError: |
---|
3664 | pass |
---|
3665 | rbRef = rbData['rbRef'] |
---|
3666 | for i in range(3): |
---|
3667 | try: |
---|
3668 | choiceIds.remove(rbRef[i]) |
---|
3669 | except ValueError: |
---|
3670 | pass |
---|
3671 | refChoice[rbid] = [choiceIds[:],choiceIds[:],choiceIds[:]] |
---|
3672 | for i in range(3): |
---|
3673 | refChoice[rbid][i].append(rbRef[i]) |
---|
3674 | refChoice[rbid][i].sort() |
---|
3675 | |
---|
3676 | def OnRBSelect(event): |
---|
3677 | global resRBsel |
---|
3678 | sel = rbSelect.GetSelection() |
---|
3679 | if sel == 0: return # 1st entry is blank |
---|
3680 | rbname = rbchoice[sel-1] |
---|
3681 | resRBsel = RBnames[rbname] |
---|
3682 | G2plt.PlotRigidBody(G2frame,'Residue',AtInfo,rbData,plotDefaults) |
---|
3683 | wx.CallLater(100,UpdateResidueRB) |
---|
3684 | |
---|
3685 | #----- beginning of UpdateResidueRB ----------------------------------------------- |
---|
3686 | AtInfo = data['Residue']['AtInfo'] |
---|
3687 | refChoice = {} |
---|
3688 | RefObjs = [] |
---|
3689 | |
---|
3690 | GS = ResidueRBDisplay.GetSizer() |
---|
3691 | if GS: |
---|
3692 | try: #get around a c++ error in wx 4.0; doing is again seems to be OK |
---|
3693 | GS.Clear(True) |
---|
3694 | except: |
---|
3695 | GS.Clear(True) |
---|
3696 | |
---|
3697 | RBnames = {} |
---|
3698 | for rbid in data['RBIds']['Residue']: |
---|
3699 | RBnames.update({data['Residue'][rbid]['RBname']:rbid,}) |
---|
3700 | if not RBnames: |
---|
3701 | return |
---|
3702 | rbchoice = list(RBnames.keys()) |
---|
3703 | rbchoice.sort() |
---|
3704 | if GS: |
---|
3705 | ResidueRBSizer = GS |
---|
3706 | else: |
---|
3707 | ResidueRBSizer = wx.BoxSizer(wx.VERTICAL) |
---|
3708 | if resRBsel not in data['RBIds']['Residue']: |
---|
3709 | resRBsel = RBnames[rbchoice[0]] |
---|
3710 | if len(RBnames) > 1: |
---|
3711 | selSizer = wx.BoxSizer(wx.HORIZONTAL) |
---|
3712 | selSizer.Add(wx.StaticText(ResidueRBDisplay, |
---|
3713 | label=' Select residue to view:'),0) |
---|
3714 | rbSelect = wx.ComboBox(ResidueRBDisplay,choices=['']+rbchoice) |
---|
3715 | name = data['Residue'][resRBsel]['RBname'] |
---|
3716 | rbSelect.SetSelection(1+rbchoice.index(name)) |
---|
3717 | rbSelect.Bind(wx.EVT_COMBOBOX,OnRBSelect) |
---|
3718 | selSizer.Add(rbSelect,0) |
---|
3719 | ResidueRBSizer.Add(selSizer,0) |
---|
3720 | rbData = data['Residue'][resRBsel] |
---|
3721 | FillRefChoice(resRBsel,rbData) |
---|
3722 | ResidueRBSizer.Add(rbNameSizer(resRBsel,rbData),0) |
---|
3723 | ResidueRBSizer.Add(rbResidues(resRBsel,rbData),0) |
---|
3724 | if len(rbData['rbSeq']): |
---|
3725 | ResidueRBSizer.Add((-1,15),0) |
---|
3726 | slideSizer,angSlide = SlideSizer() |
---|
3727 | seqSizer = wx.FlexGridSizer(0,5,4,8) |
---|
3728 | for lbl in 'Sel','','Bond','Angle','Riding atoms': |
---|
3729 | seqSizer.Add(wx.StaticText(ResidueRBDisplay,wx.ID_ANY,lbl)) |
---|
3730 | ResidueRBSizer.Add(seqSizer) |
---|
3731 | # for iSeq,Seq in enumerate(rbData['rbSeq']): |
---|
3732 | # ResidueRBSizer.Add(SeqSizer(angSlide,resRBsel,iSeq,Seq,rbData['atNames'])) |
---|
3733 | for iSeq,Seq in enumerate(rbData['rbSeq']): |
---|
3734 | Add2SeqSizer(seqSizer,angSlide,resRBsel,iSeq,Seq,rbData['atNames']) |
---|
3735 | ResidueRBSizer.Add(slideSizer,) |
---|
3736 | |
---|
3737 | ResidueRBSizer.Add((5,25),) |
---|
3738 | ResidueRBSizer.Layout() |
---|
3739 | ResidueRBDisplay.SetSizer(ResidueRBSizer,True) |
---|
3740 | ResidueRBDisplay.SetAutoLayout(True) |
---|
3741 | Size = ResidueRBSizer.GetMinSize() |
---|
3742 | ResidueRBDisplay.SetSize(Size) |
---|
3743 | |
---|
3744 | Size[0] += 40 |
---|
3745 | Size[1] = max(Size[1],450) + 20 |
---|
3746 | ResidueRB.SetSize(Size) |
---|
3747 | ResidueRB.SetScrollbars(10,10,Size[0]/10-4,Size[1]/10-1) |
---|
3748 | G2frame.dataWindow.SendSizeEvent() |
---|
3749 | |
---|
3750 | ResidueRBDisplay.Show() |
---|
3751 | |
---|
3752 | def SetStatusLine(text): |
---|
3753 | G2frame.GetStatusBar().SetStatusText(text,1) |
---|
3754 | |
---|
3755 | #### UpdateRigidBodies starts here ========= |
---|
3756 | global resList,resRBsel |
---|
3757 | if not data.get('RBIds') or not data: |
---|
3758 | data.update({'Vector':{'AtInfo':{}},'Residue':{'AtInfo':{}}, |
---|
3759 | 'RBIds':{'Vector':[],'Residue':[]}}) #empty/bad dict - fill it |
---|
3760 | Indx = {} |
---|
3761 | resList = [] |
---|
3762 | plotDefaults = {'oldxy':[0.,0.],'Quaternion':[0.,0.,0.,1.],'cameraPos':30.,'viewDir':[0,0,1],} |
---|
3763 | G2frame.rbBook = G2G.GSNoteBook(parent=G2frame.dataWindow) |
---|
3764 | G2frame.dataWindow.GetSizer().Add(G2frame.rbBook,1,wx.ALL|wx.EXPAND) |
---|
3765 | VectorRB = wx.ScrolledWindow(G2frame.rbBook) |
---|
3766 | VectorRBDisplay = wx.Panel(VectorRB) |
---|
3767 | G2frame.rbBook.AddPage(VectorRB,'Vector rigid bodies') |
---|
3768 | ResidueRB = wx.ScrolledWindow(G2frame.rbBook) |
---|
3769 | ResidueRBDisplay = wx.Panel(ResidueRB) |
---|
3770 | G2frame.rbBook.AddPage(ResidueRB,'Residue rigid bodies') |
---|
3771 | # vector RBs are not too common, so select Residue as the default when one is present |
---|
3772 | if len(data['RBIds']['Residue']) > 0 and len(data['RBIds']['Vector']) == 0: |
---|
3773 | G2frame.rbBook.ChangeSelection(1) |
---|
3774 | OnPageChanged(None) |
---|
3775 | G2gd.SetDataMenuBar(G2frame,G2frame.dataWindow.RigidBodyMenu) |
---|
3776 | SetStatusLine('') |
---|
3777 | UpdateVectorRB() |
---|
3778 | G2frame.rbBook.Bind(wx.aui.EVT_AUINOTEBOOK_PAGE_CHANGED, OnPageChanged) |
---|
3779 | wx.CallAfter(OnPageChanged,None) |
---|
3780 | |
---|
3781 | def ShowIsoDistortCalc(G2frame,phase=None): |
---|
3782 | '''Compute the ISODISTORT mode values from the current coordinates. |
---|
3783 | Called in response to the (Phase/Atoms tab) AtomCompute or |
---|
3784 | Constraints/Edit Constr. "Show ISODISTORT modes" menu item, which |
---|
3785 | should be enabled only when Phase['ISODISTORT'] is defined. |
---|
3786 | ''' |
---|
3787 | def _onClose(event): |
---|
3788 | dlg.EndModal(wx.ID_CANCEL) |
---|
3789 | def fmtHelp(item,fullname): |
---|
3790 | helptext = "A new variable" |
---|
3791 | if item[-3]: |
---|
3792 | helptext += " named "+str(item[-3]) |
---|
3793 | helptext += " is a linear combination of the following parameters:\n" |
---|
3794 | first = True |
---|
3795 | for term in item[:-3]: |
---|
3796 | line = '' |
---|
3797 | var = str(term[1]) |
---|
3798 | m = term[0] |
---|
3799 | if first: |
---|
3800 | first = False |
---|
3801 | line += ' = ' |
---|
3802 | else: |
---|
3803 | if m >= 0: |
---|
3804 | line += ' + ' |
---|
3805 | else: |
---|
3806 | line += ' - ' |
---|
3807 | m = abs(m) |
---|
3808 | line += '%.3f*%s '%(m,var) |
---|
3809 | varMean = G2obj.fmtVarDescr(var) |
---|
3810 | helptext += "\n" + line + " ("+ varMean + ")" |
---|
3811 | helptext += '\n\nISODISTORT full name: '+str(fullname) |
---|
3812 | return helptext |
---|
3813 | |
---|
3814 | Phases = G2frame.GetPhaseData() |
---|
3815 | isophases = [p for p in Phases if 'G2VarList' in Phases[p]['ISODISTORT']] |
---|
3816 | if not isophases: |
---|
3817 | G2G.G2MessageBox(G2frame,'no ISODISTORT mode data for any phase') |
---|
3818 | return |
---|
3819 | if phase and phase not in isophases: |
---|
3820 | G2G.G2MessageBox(G2frame,'no ISODISTORT mode data for this phase') |
---|
3821 | return |
---|
3822 | elif not phase and len(isophases) == 1: |
---|
3823 | phase = isophases[0] |
---|
3824 | elif not phase: |
---|
3825 | dlg = wx.SingleChoiceDialog(G2frame,'Select phase from ISODISTORT phases', |
---|
3826 | 'Select Phase',isophases) |
---|
3827 | if dlg.ShowModal() == wx.ID_OK: |
---|
3828 | sel = dlg.GetSelection() |
---|
3829 | phase = isophases[sel] |
---|
3830 | else: |
---|
3831 | return |
---|
3832 | |
---|
3833 | covdata = G2frame.GPXtree.GetItemPyData( |
---|
3834 | G2gd.GetGPXtreeItemId(G2frame,G2frame.root,'Covariance')) |
---|
3835 | # make a lookup table for named NewVar Phase constraints |
---|
3836 | sub = G2gd.GetGPXtreeItemId(G2frame,G2frame.root,'Constraints') |
---|
3837 | Constraints = G2frame.GPXtree.GetItemPyData(sub) |
---|
3838 | constrDict = {} |
---|
3839 | for c in Constraints['Phase']: |
---|
3840 | if c[-1] != 'f' or not c[-3]: continue |
---|
3841 | constrDict[str(c[-3])] = c |
---|
3842 | |
---|
3843 | parmDict,varyList = G2frame.MakeLSParmDict() |
---|
3844 | |
---|
3845 | dlg = wx.Dialog(G2frame,wx.ID_ANY,'ISODISTORT mode values',#size=(630,400), |
---|
3846 | style=wx.DEFAULT_DIALOG_STYLE|wx.RESIZE_BORDER) |
---|
3847 | mainSizer = wx.BoxSizer(wx.VERTICAL) |
---|
3848 | data = Phases[phase] |
---|
3849 | ISO = data['ISODISTORT'] |
---|
3850 | mainSizer.Add(wx.StaticText(dlg,wx.ID_ANY, |
---|
3851 | 'ISODISTORT mode computation for coordinates in phase '+str(data['General'].get('Name')))) |
---|
3852 | aSizer = wx.BoxSizer(wx.HORIZONTAL) |
---|
3853 | panel1 = wxscroll.ScrolledPanel( |
---|
3854 | dlg, wx.ID_ANY,#size=(100,200), |
---|
3855 | style = wx.TAB_TRAVERSAL|wx.SUNKEN_BORDER) |
---|
3856 | subSizer1 = wx.FlexGridSizer(cols=2,hgap=5,vgap=2) |
---|
3857 | panel2 = wxscroll.ScrolledPanel( |
---|
3858 | dlg, wx.ID_ANY,#size=(100,200), |
---|
3859 | style = wx.TAB_TRAVERSAL|wx.SUNKEN_BORDER) |
---|
3860 | subSizer2 = wx.FlexGridSizer(cols=3,hgap=5,vgap=2) |
---|
3861 | subSizer1.Add(wx.StaticText(panel1,wx.ID_ANY,'Parameter name ')) |
---|
3862 | subSizer1.Add(wx.StaticText(panel1,wx.ID_ANY,' value'),0,wx.ALIGN_RIGHT) |
---|
3863 | subSizer2.Add((-1,-1)) |
---|
3864 | subSizer2.Add(wx.StaticText(panel2,wx.ID_ANY,'Mode name ')) |
---|
3865 | subSizer2.Add(wx.StaticText(panel2,wx.ID_ANY,' value'),0,wx.ALIGN_RIGHT) |
---|
3866 | # ISODISTORT displacive modes |
---|
3867 | if 'G2VarList' in ISO: |
---|
3868 | deltaList = [] |
---|
3869 | for gv,Ilbl in zip(ISO['G2VarList'],ISO['IsoVarList']): |
---|
3870 | dvar = gv.varname() |
---|
3871 | var = dvar.replace('::dA','::A') |
---|
3872 | albl = Ilbl[:Ilbl.rfind('_')] |
---|
3873 | v = Ilbl[Ilbl.rfind('_')+1:] |
---|
3874 | pval = ISO['ParentStructure'][albl][['dx','dy','dz'].index(v)] |
---|
3875 | if var in parmDict: |
---|
3876 | cval = parmDict[var][0] |
---|
3877 | else: |
---|
3878 | cval = 0.0 |
---|
3879 | deltaList.append(cval-pval) |
---|
3880 | modeVals = np.inner(ISO['Var2ModeMatrix'],deltaList) |
---|
3881 | for lbl,xyz,var,val,norm,G2mode in zip( |
---|
3882 | ISO['IsoVarList'],deltaList, |
---|
3883 | ISO['IsoModeList'],modeVals,ISO['NormList'],ISO['G2ModeList'] ): |
---|
3884 | if str(G2mode) in constrDict: |
---|
3885 | ch = G2G.HelpButton(panel2,fmtHelp(constrDict[str(G2mode)],var)) |
---|
3886 | subSizer2.Add(ch,0,wx.LEFT|wx.RIGHT|WACV|wx.ALIGN_CENTER,1) |
---|
3887 | else: |
---|
3888 | subSizer2.Add((-1,-1)) |
---|
3889 | subSizer1.Add(wx.StaticText(panel1,wx.ID_ANY,str(lbl))) |
---|
3890 | try: |
---|
3891 | value = G2py3.FormatSigFigs(xyz) |
---|
3892 | except TypeError: |
---|
3893 | value = str(xyz) |
---|
3894 | subSizer1.Add(wx.StaticText(panel1,wx.ID_ANY,value),0,wx.ALIGN_RIGHT) |
---|
3895 | subSizer2.Add(wx.StaticText(panel2,wx.ID_ANY,str(var))) |
---|
3896 | try: |
---|
3897 | value = G2py3.FormatSigFigs(val/norm) |
---|
3898 | if 'varyList' in covdata: |
---|
3899 | if str(G2mode) in covdata['varyList']: |
---|
3900 | sig = covdata['sig'][covdata['varyList'].index(str(G2mode))] |
---|
3901 | value = G2mth.ValEsd(val/norm,sig/norm) |
---|
3902 | except TypeError: |
---|
3903 | value = '?' |
---|
3904 | subSizer2.Add(wx.StaticText(panel2,wx.ID_ANY,value),0,wx.ALIGN_RIGHT) |
---|
3905 | # ISODISTORT occupancy modes |
---|
3906 | if 'G2OccVarList' in ISO: |
---|
3907 | deltaList = [] |
---|
3908 | for gv,Ilbl in zip(ISO['G2OccVarList'],ISO['OccVarList']): |
---|
3909 | var = gv.varname() |
---|
3910 | albl = Ilbl[:Ilbl.rfind('_')] |
---|
3911 | pval = ISO['BaseOcc'][albl] |
---|
3912 | if var in parmDict: |
---|
3913 | cval = parmDict[var][0] |
---|
3914 | else: |
---|
3915 | dlg.EndModal(wx.ID_CANCEL) |
---|
3916 | G2frame.ErrorDialog('Atom not found',"No value found for parameter "+str(var)) |
---|
3917 | return |
---|
3918 | deltaList.append(cval-pval) |
---|
3919 | modeVals = np.inner(ISO['Var2OccMatrix'],deltaList) |
---|
3920 | for lbl,delocc,var,val,norm,G2mode in zip( |
---|
3921 | ISO['OccVarList'],deltaList, |
---|
3922 | ISO['OccModeList'],modeVals,ISO['OccNormList'],ISO['G2OccModeList']): |
---|
3923 | if str(G2mode) in constrDict: |
---|
3924 | ch = G2G.HelpButton(panel2,fmtHelp(constrDict[str(G2mode)],var)) |
---|
3925 | subSizer2.Add(ch,0,wx.LEFT|wx.RIGHT|WACV|wx.ALIGN_CENTER,1) |
---|
3926 | else: |
---|
3927 | subSizer2.Add((-1,-1)) |
---|
3928 | subSizer1.Add(wx.StaticText(panel1,wx.ID_ANY,str(lbl))) |
---|
3929 | try: |
---|
3930 | value = G2py3.FormatSigFigs(delocc) |
---|
3931 | except TypeError: |
---|
3932 | value = str(delocc) |
---|
3933 | subSizer1.Add(wx.StaticText(panel1,wx.ID_ANY,value),0,wx.ALIGN_RIGHT) |
---|
3934 | #subSizer.Add((10,-1)) |
---|
3935 | subSizer2.Add(wx.StaticText(panel2,wx.ID_ANY,str(var))) |
---|
3936 | try: |
---|
3937 | value = G2py3.FormatSigFigs(val/norm) |
---|
3938 | if 'varyList' in covdata: |
---|
3939 | if str(G2mode) in covdata['varyList']: |
---|
3940 | sig = covdata['sig'][covdata['varyList'].index(str(G2mode))] |
---|
3941 | value = G2mth.ValEsd(val/norm,sig/norm) |
---|
3942 | except TypeError: |
---|
3943 | value = '?' |
---|
3944 | subSizer2.Add(wx.StaticText(panel2,wx.ID_ANY,value),0,wx.ALIGN_RIGHT) |
---|
3945 | |
---|
3946 | # finish up ScrolledPanel |
---|
3947 | panel1.SetSizer(subSizer1) |
---|
3948 | panel2.SetSizer(subSizer2) |
---|
3949 | panel1.SetAutoLayout(1) |
---|
3950 | panel1.SetupScrolling() |
---|
3951 | panel2.SetAutoLayout(1) |
---|
3952 | panel2.SetupScrolling() |
---|
3953 | # Allow window to be enlarged but not made smaller |
---|
3954 | dlg.SetSizer(mainSizer) |
---|
3955 | w1,l1 = subSizer1.GetSize() |
---|
3956 | w2,l2 = subSizer2.GetSize() |
---|
3957 | panel1.SetMinSize((w1+10,200)) |
---|
3958 | panel2.SetMinSize((w2+20,200)) |
---|
3959 | aSizer.Add(panel1,1, wx.ALL|wx.EXPAND,1) |
---|
3960 | aSizer.Add(panel2,2, wx.ALL|wx.EXPAND,1) |
---|
3961 | mainSizer.Add(aSizer,1, wx.ALL|wx.EXPAND,1) |
---|
3962 | |
---|
3963 | # make OK button |
---|
3964 | btnsizer = wx.BoxSizer(wx.HORIZONTAL) |
---|
3965 | btn = wx.Button(dlg, wx.ID_CLOSE) |
---|
3966 | btn.Bind(wx.EVT_BUTTON,_onClose) |
---|
3967 | btnsizer.Add(btn) |
---|
3968 | mainSizer.Add(btnsizer, 0, wx.ALIGN_CENTER|wx.ALL, 5) |
---|
3969 | |
---|
3970 | mainSizer.Fit(dlg) |
---|
3971 | dlg.SetMinSize(dlg.GetSize()) |
---|
3972 | dlg.ShowModal() |
---|
3973 | dlg.Destroy() |
---|