1 | # -*- coding: utf-8 -*- |
---|
2 | ########### SVN repository information ################### |
---|
3 | # $Date: 2023-09-23 01:54:05 +0000 (Sat, 23 Sep 2023) $ |
---|
4 | # $Author: toby $ |
---|
5 | # $Revision: 5661 $ |
---|
6 | # $URL: trunk/GSASIIstrMain.py $ |
---|
7 | # $Id: GSASIIstrMain.py 5661 2023-09-23 01:54:05Z toby $ |
---|
8 | ########### SVN repository information ################### |
---|
9 | ''' |
---|
10 | :mod:`GSASIIstrMain` routines, used for refinement are found below. |
---|
11 | ''' |
---|
12 | from __future__ import division, print_function |
---|
13 | import platform |
---|
14 | import sys |
---|
15 | import os.path as ospath |
---|
16 | import time |
---|
17 | import math |
---|
18 | import copy |
---|
19 | if '2' in platform.python_version_tuple()[0]: |
---|
20 | import cPickle |
---|
21 | else: |
---|
22 | import pickle as cPickle |
---|
23 | import numpy as np |
---|
24 | import numpy.linalg as nl |
---|
25 | import scipy.optimize as so |
---|
26 | import GSASIIpath |
---|
27 | GSASIIpath.SetBinaryPath() |
---|
28 | GSASIIpath.SetVersionNumber("$Revision: 5661 $") |
---|
29 | import GSASIIlattice as G2lat |
---|
30 | import GSASIIspc as G2spc |
---|
31 | import GSASIImapvars as G2mv |
---|
32 | import GSASIImath as G2mth |
---|
33 | import GSASIIstrIO as G2stIO |
---|
34 | import GSASIIstrMath as G2stMth |
---|
35 | import GSASIIobj as G2obj |
---|
36 | import GSASIIfiles as G2fil |
---|
37 | import GSASIIElem as G2elem |
---|
38 | import GSASIIscriptable as G2sc |
---|
39 | import atmdata |
---|
40 | |
---|
41 | sind = lambda x: np.sin(x*np.pi/180.) |
---|
42 | cosd = lambda x: np.cos(x*np.pi/180.) |
---|
43 | tand = lambda x: np.tan(x*np.pi/180.) |
---|
44 | asind = lambda x: 180.*np.arcsin(x)/np.pi |
---|
45 | acosd = lambda x: 180.*np.arccos(x)/np.pi |
---|
46 | atan2d = lambda y,x: 180.*np.arctan2(y,x)/np.pi |
---|
47 | |
---|
48 | ateln2 = 8.0*math.log(2.0) |
---|
49 | DEBUG = True |
---|
50 | #PhFrExtPOSig = None |
---|
51 | |
---|
52 | def ReportProblems(result,Rvals,varyList): |
---|
53 | '''Create a message based results from the refinement |
---|
54 | ''' |
---|
55 | #report on SVD 0's and highly correlated variables |
---|
56 | msg = '' |
---|
57 | # process singular variables; all vars go to console, first 10 to |
---|
58 | # dialog window |
---|
59 | psing = result[2].get('psing',[]) |
---|
60 | if len(psing): |
---|
61 | if msg: msg += '\n' |
---|
62 | m = 'Error: {} Parameter(s) dropped:'.format(len(psing)) |
---|
63 | msg += m |
---|
64 | G2fil.G2Print(m, mode='warn') |
---|
65 | m = '' |
---|
66 | for i,val in enumerate(psing): |
---|
67 | if i == 0: |
---|
68 | msg += '\n{}'.format(varyList[val]) |
---|
69 | m = ' {}'.format(varyList[val]) |
---|
70 | else: |
---|
71 | if len(m) > 70: |
---|
72 | G2fil.G2Print(m, mode='warn') |
---|
73 | m = ' ' |
---|
74 | else: |
---|
75 | m += ', ' |
---|
76 | m += '{}'.format(varyList[val]) |
---|
77 | if i == 10: |
---|
78 | msg += ', {}... see console for full list'.format(varyList[val]) |
---|
79 | elif i > 10: |
---|
80 | pass |
---|
81 | else: |
---|
82 | msg += ', {}'.format(varyList[val]) |
---|
83 | if m: G2fil.G2Print(m, mode='warn') |
---|
84 | SVD0 = result[2].get('SVD0',0) |
---|
85 | if SVD0 == 1: |
---|
86 | msg += 'Warning: Soft (SVD) singularity in the Hessian' |
---|
87 | elif SVD0 > 0: |
---|
88 | msg += 'Warning: {} soft (SVD) Hessian singularities'.format(SVD0) |
---|
89 | SVDsing = result[2].get('SVDsing',[]) |
---|
90 | if len(SVDsing): |
---|
91 | if msg: msg += '\n' |
---|
92 | m = 'SVD problem(s) likely from:' |
---|
93 | msg += m |
---|
94 | G2fil.G2Print(m, mode='warn') |
---|
95 | m = '' |
---|
96 | for i,val in enumerate(SVDsing): |
---|
97 | if i == 0: |
---|
98 | msg += '\n{}'.format(varyList[val]) |
---|
99 | m = ' {}'.format(varyList[val]) |
---|
100 | else: |
---|
101 | if len(m) > 70: |
---|
102 | G2fil.G2Print(m, mode='warn') |
---|
103 | m = ' ' |
---|
104 | else: |
---|
105 | m += ', ' |
---|
106 | m += '{}'.format(varyList[val]) |
---|
107 | if i == 10: |
---|
108 | msg += ', {}... see console for full list'.format(varyList[val]) |
---|
109 | elif i > 10: |
---|
110 | pass |
---|
111 | else: |
---|
112 | msg += ', {}'.format(varyList[val]) |
---|
113 | if m: G2fil.G2Print(m, mode='warn') |
---|
114 | #report on highly correlated variables |
---|
115 | Hcorr = result[2].get('Hcorr',[]) |
---|
116 | if len(Hcorr) > 0: |
---|
117 | if msg: msg += '\n' |
---|
118 | m = 'Note highly correlated parameters:' |
---|
119 | G2fil.G2Print(m, mode='warn') |
---|
120 | msg += m |
---|
121 | elif SVD0 > 0: |
---|
122 | if msg: msg += '\n' |
---|
123 | m = 'Check covariance matrix for parameter correlation' |
---|
124 | G2fil.G2Print(m, mode='warn') |
---|
125 | msg += m |
---|
126 | for i,(v1,v2,corr) in enumerate(Hcorr): |
---|
127 | if corr > .95: |
---|
128 | stars = '**' |
---|
129 | else: |
---|
130 | stars = ' ' |
---|
131 | m = ' {} {} and {} (@{:.2f}%)'.format( |
---|
132 | stars,varyList[v1],varyList[v2],100.*corr) |
---|
133 | G2fil.G2Print(m, mode='warn') |
---|
134 | if i == 5: |
---|
135 | msg += '\n' + m |
---|
136 | msg += '\n ... check console for more' |
---|
137 | elif i < 5: |
---|
138 | msg += '\n' + m |
---|
139 | if msg: |
---|
140 | if 'msg' not in Rvals: Rvals['msg'] = '' |
---|
141 | Rvals['msg'] += msg |
---|
142 | |
---|
143 | def IgnoredLatticePrms(Phases): |
---|
144 | ignore = [] |
---|
145 | copydict = {} |
---|
146 | for p in Phases: |
---|
147 | pfx = str(Phases[p]['pId']) + '::' |
---|
148 | laue = Phases[p]['General']['SGData']['SGLaue'] |
---|
149 | axis = Phases[p]['General']['SGData']['SGUniq'] |
---|
150 | if laue in ['-1',]: |
---|
151 | pass |
---|
152 | elif laue in ['2/m',]: |
---|
153 | if axis == 'a': |
---|
154 | ignore += [pfx+'A4',pfx+'A5'] |
---|
155 | elif axis == 'b': |
---|
156 | ignore += [pfx+'A3',pfx+'A5'] |
---|
157 | else: |
---|
158 | ignore += [pfx+'A3',pfx+'A4'] |
---|
159 | elif laue in ['mmm',]: |
---|
160 | ignore += [pfx+'A3',pfx+'A4',pfx+'A5'] |
---|
161 | elif laue in ['4/m','4/mmm']: |
---|
162 | ignore += [pfx+'A1',pfx+'A3',pfx+'A4',pfx+'A5'] |
---|
163 | copydict[pfx+'A0':[pfx+'A1']] |
---|
164 | elif laue in ['6/m','6/mmm','3m1', '31m', '3']: |
---|
165 | ignore += [pfx+'A1',pfx+'A3',pfx+'A4',pfx+'A5'] |
---|
166 | copydict[pfx+'A0'] = [pfx+'A1',pfx+'A3'] |
---|
167 | elif laue in ['3R', '3mR']: |
---|
168 | ignore += [pfx+'A1',pfx+'A2',pfx+'A4',pfx+'A5'] |
---|
169 | copydict[pfx+'A0'] = [pfx+'A1',pfx+'A2'] |
---|
170 | copydict[pfx+'A3'] = [pfx+'A4',pfx+'A5'] |
---|
171 | elif laue in ['m3m','m3']: |
---|
172 | ignore += [pfx+'A1',pfx+'A2',pfx+'A3',pfx+'A4',pfx+'A5'] |
---|
173 | copydict[pfx+'A0'] = [pfx+'A1',pfx+'A2'] |
---|
174 | return ignore,copydict |
---|
175 | |
---|
176 | def AllPrmDerivs(Controls,Histograms,Phases,restraintDict,rigidbodyDict, |
---|
177 | parmDict,varyList,calcControls,pawleyLookup,symHold, |
---|
178 | dlg=None): |
---|
179 | '''Computes the derivative of the fitting function (total Chi**2) with |
---|
180 | respect to every parameter in the parameter dictionary (parmDict) |
---|
181 | by applying shift below the parameter value as well as above. |
---|
182 | |
---|
183 | :returns: a dict with the derivatives keyed by variable number. |
---|
184 | Derivatives are a list with three values: evaluated over |
---|
185 | v-d to v; v-d to v+d; v to v+d where v is the current value for the |
---|
186 | variable and d is a small delta value chosen for that variable type. |
---|
187 | ''' |
---|
188 | import re |
---|
189 | rms = lambda y: np.sqrt(np.mean(y**2)) |
---|
190 | G2mv.Map2Dict(parmDict,varyList) |
---|
191 | begin = time.time() |
---|
192 | seqList = Controls.get('Seq Data',[]) |
---|
193 | hId = '*' |
---|
194 | if seqList: |
---|
195 | hId = str(Histograms[seqList[0]]['hId']) |
---|
196 | # values = np.array(G2stMth.Dict2Values(parmDict, varyList)) |
---|
197 | # if np.any(np.isnan(values)): |
---|
198 | # raise G2obj.G2Exception('ERROR - nan found in LS parameters - use Calculate/View LS parms to locate') |
---|
199 | latIgnoreLst,latCopyDict = IgnoredLatticePrms(Phases) |
---|
200 | HistoPhases=[Histograms,Phases,restraintDict,rigidbodyDict] |
---|
201 | origDiffs = G2stMth.errRefine([],HistoPhases,parmDict,[],calcControls,pawleyLookup,None) |
---|
202 | chiStart = rms(origDiffs) |
---|
203 | origParms = copy.deepcopy(parmDict) |
---|
204 | #print('after 1st calc',time.time()-begin) |
---|
205 | derivCalcs = {} |
---|
206 | if dlg: dlg.SetRange(len(origParms)) |
---|
207 | for i,prm in enumerate(origParms): |
---|
208 | if dlg: |
---|
209 | if not dlg.Update(i)[0]: |
---|
210 | return None |
---|
211 | parmDict = copy.deepcopy(origParms) |
---|
212 | p,h,nam = prm.split(':')[:3] |
---|
213 | if hId != '*' and h != '' and h != hId: continue |
---|
214 | if (type(parmDict[prm]) is bool or type(parmDict[prm]) is str or |
---|
215 | type(parmDict[prm]) is int): continue |
---|
216 | if type(parmDict[prm]) is not float and type(parmDict[prm] |
---|
217 | ) is not np.float64: |
---|
218 | print('*** unexpected type for ',prm,parmDict[prm],type(parmDict[prm])) |
---|
219 | continue |
---|
220 | if prm in latIgnoreLst: continue # remove unvaried lattice params |
---|
221 | if re.match(r'\d:\d:D[012][012]',prm): continue # don't need Dij terms |
---|
222 | if nam in ['Vol','Gonio. radius']: continue |
---|
223 | if nam.startswith('dA') and nam[2] in ['x','y','z']: continue |
---|
224 | delta = max(abs(parmDict[prm])*0.0001,1e-6) |
---|
225 | if nam in ['Shift','DisplaceX','DisplaceY',]: |
---|
226 | delta = 0.1 |
---|
227 | elif nam.startswith('AUiso'): |
---|
228 | delta = 1e-5 |
---|
229 | if nam[0] == 'A' and nam[1] in ['x','y','z']: |
---|
230 | dprm = prm.replace('::A','::dA') |
---|
231 | if dprm in symHold: continue # held by symmetry |
---|
232 | delta = 1e-6 |
---|
233 | else: |
---|
234 | dprm = prm |
---|
235 | #print('***',prm,type(parmDict[prm])) |
---|
236 | #origVal = parmDict[dprm] |
---|
237 | parmDict[dprm] -= delta |
---|
238 | G2mv.Dict2Map(parmDict) |
---|
239 | if dprm in latCopyDict: # apply contraints on lattice parameters |
---|
240 | for i in latCopyDict: |
---|
241 | parmDict[i] = parmDict[dprm] |
---|
242 | #for i in parmDict: |
---|
243 | # if origParms[i] != parmDict[i]: print('changed',i,origParms[i],parmDict[i]) |
---|
244 | chiLow = rms(G2stMth.errRefine([],HistoPhases,parmDict,[],calcControls,pawleyLookup,None)) |
---|
245 | parmDict[dprm] += 2*delta |
---|
246 | G2mv.Dict2Map(parmDict) |
---|
247 | if dprm in latCopyDict: # apply contraints on lattice parameters |
---|
248 | for i in latCopyDict: |
---|
249 | parmDict[i] = parmDict[dprm] |
---|
250 | #for i in parmDict: |
---|
251 | # if origParms[i] != parmDict[i]: print('changed',i,origParms[i],parmDict[i]) |
---|
252 | chiHigh = rms(G2stMth.errRefine([],HistoPhases,parmDict,[],calcControls,pawleyLookup,None)) |
---|
253 | #print('===>',prm,parmDict[dprm],delta) |
---|
254 | #print(chiLow,chiStart,chiHigh) |
---|
255 | #print((chiLow-chiStart)/delta,0.5*(chiLow-chiHigh)/delta,(chiStart-chiHigh)/delta) |
---|
256 | derivCalcs[prm] = ((chiLow-chiStart)/delta,0.5*(chiLow-chiHigh)/delta,(chiStart-chiHigh)/delta) |
---|
257 | print('derivative computation time',time.time()-begin) |
---|
258 | return derivCalcs |
---|
259 | |
---|
260 | def RefineCore(Controls,Histograms,Phases,restraintDict,rigidbodyDict,parmDict,varyList, |
---|
261 | calcControls,pawleyLookup,ifSeq,printFile,dlg,refPlotUpdate=None): |
---|
262 | '''Core optimization routines, shared between SeqRefine and Refine |
---|
263 | |
---|
264 | :returns: 5-tuple of ifOk (bool), Rvals (dict), result, covMatrix, sig |
---|
265 | ''' |
---|
266 | #patch (added Oct 2020) convert variable names for parm limits to G2VarObj |
---|
267 | G2sc.patchControls(Controls) |
---|
268 | # end patch |
---|
269 | # print 'current',varyList |
---|
270 | # for item in parmDict: print item,parmDict[item] ######### show dict just before refinement |
---|
271 | ifPrint = True |
---|
272 | if ifSeq: |
---|
273 | ifPrint = False |
---|
274 | Rvals = {} |
---|
275 | chisq0 = None |
---|
276 | Lastshft = None |
---|
277 | while True: |
---|
278 | G2mv.Map2Dict(parmDict,varyList) |
---|
279 | begin = time.time() |
---|
280 | values = np.array(G2stMth.Dict2Values(parmDict, varyList)) |
---|
281 | if np.any(np.isnan(values)): |
---|
282 | raise G2obj.G2Exception('ERROR - nan found in LS parameters - use Calculate/View LS parms to locate') |
---|
283 | # test code to compute GOF and save for external repeat |
---|
284 | #args = ([Histograms,Phases,restraintDict,rigidbodyDict],parmDict,varyList,calcControls,pawleyLookup,dlg) |
---|
285 | #print '*** before fit chi**2',np.sum(G2stMth.errRefine(values,*args)**2) |
---|
286 | #fl = open('beforeFit.cpickle','wb') |
---|
287 | #cPickle.dump(values,fl,1) |
---|
288 | #cPickle.dump(args[:-1],fl,1) |
---|
289 | #fl.close() |
---|
290 | Ftol = Controls['min dM/M'] |
---|
291 | Xtol = Controls['SVDtol'] |
---|
292 | Factor = Controls['shift factor'] |
---|
293 | if 'Jacobian' in Controls['deriv type']: |
---|
294 | maxCyc = Controls.get('max cyc',1) |
---|
295 | result = so.leastsq(G2stMth.errRefine,values,Dfun=G2stMth.dervRefine,full_output=True, |
---|
296 | ftol=Ftol,col_deriv=True,factor=Factor, |
---|
297 | args=([Histograms,Phases,restraintDict,rigidbodyDict],parmDict,varyList,calcControls,pawleyLookup,dlg)) |
---|
298 | ncyc = int(result[2]['nfev']/2) |
---|
299 | result[2]['num cyc'] = ncyc |
---|
300 | if refPlotUpdate is not None: refPlotUpdate(Histograms) # update plot after completion |
---|
301 | elif 'analytic Hessian' in Controls['deriv type']: |
---|
302 | Lamda = Controls.get('Marquardt',-3) |
---|
303 | maxCyc = Controls['max cyc'] |
---|
304 | result = G2mth.HessianLSQ(G2stMth.errRefine,values,Hess=G2stMth.HessRefine,ftol=Ftol,xtol=Xtol,maxcyc=maxCyc,Print=ifPrint,lamda=Lamda, |
---|
305 | args=([Histograms,Phases,restraintDict,rigidbodyDict],parmDict,varyList,calcControls,pawleyLookup,dlg), |
---|
306 | refPlotUpdate=refPlotUpdate) |
---|
307 | ncyc = result[2]['num cyc']+1 |
---|
308 | Rvals['lamMax'] = result[2]['lamMax'] |
---|
309 | if 'Ouch#4' in result[2]: |
---|
310 | Rvals['Aborted'] = True |
---|
311 | if 'msg' in result[2]: |
---|
312 | Rvals['msg'] = result[2]['msg'] |
---|
313 | Controls['Marquardt'] = -3 #reset to default |
---|
314 | if 'chisq0' in result[2] and chisq0 is None: |
---|
315 | chisq0 = result[2]['chisq0'] |
---|
316 | elif 'Hessian SVD' in Controls['deriv type']: |
---|
317 | maxCyc = Controls['max cyc'] |
---|
318 | result = G2mth.HessianSVD(G2stMth.errRefine,values,Hess=G2stMth.HessRefine,ftol=Ftol,xtol=Xtol,maxcyc=maxCyc,Print=ifPrint, |
---|
319 | args=([Histograms,Phases,restraintDict,rigidbodyDict],parmDict,varyList,calcControls,pawleyLookup,dlg), |
---|
320 | refPlotUpdate=refPlotUpdate) |
---|
321 | if result[1] is None: |
---|
322 | IfOK = False |
---|
323 | covMatrix = [] |
---|
324 | sig = len(varyList)*[None,] |
---|
325 | break |
---|
326 | ncyc = result[2]['num cyc']+1 |
---|
327 | if 'chisq0' in result[2] and chisq0 is None: |
---|
328 | chisq0 = result[2]['chisq0'] |
---|
329 | else: #'numeric' |
---|
330 | maxCyc = Controls.get('max cyc',1) |
---|
331 | result = so.leastsq(G2stMth.errRefine,values,full_output=True,ftol=Ftol,epsfcn=1.e-8,factor=Factor, |
---|
332 | args=([Histograms,Phases,restraintDict,rigidbodyDict],parmDict,varyList,calcControls,pawleyLookup,dlg)) |
---|
333 | ncyc = 1 |
---|
334 | result[2]['num cyc'] = ncyc |
---|
335 | if len(varyList): |
---|
336 | ncyc = int(result[2]['nfev']/len(varyList)) |
---|
337 | if refPlotUpdate is not None: refPlotUpdate(Histograms) # update plot |
---|
338 | #table = dict(zip(varyList,zip(values,result[0],(result[0]-values)))) |
---|
339 | #for item in table: print(item,table[item]) #useful debug - are things shifting? |
---|
340 | runtime = time.time()-begin |
---|
341 | Rvals['SVD0'] = result[2].get('SVD0',0) |
---|
342 | Rvals['converged'] = result[2].get('Converged') |
---|
343 | Rvals['DelChi2'] = result[2].get('DelChi2',-1.) |
---|
344 | Rvals['chisq'] = np.sum(result[2]['fvec']**2) |
---|
345 | G2stMth.Values2Dict(parmDict, varyList, result[0]) |
---|
346 | G2mv.Dict2Map(parmDict) |
---|
347 | Rvals['Nobs'] = Histograms['Nobs'] |
---|
348 | Rvals['Nvars'] = len(varyList) |
---|
349 | Rvals['RestraintSum'] = Histograms.get('RestraintSum',0.) |
---|
350 | Rvals['RestraintTerms'] = Histograms.get('RestraintTerms',0) |
---|
351 | Rvals['Rwp'] = np.sqrt(Rvals['chisq']/Histograms['sumwYo'])*100. #to % |
---|
352 | Rvals['GOF'] = np.sqrt(Rvals['chisq']/(Histograms['Nobs']+Rvals['RestraintTerms']-len(varyList))) |
---|
353 | printFile.write(' Number of function calls: %d No. of observations: %d No. of parameters: %d User rejected: %d Sp. gp. extinct: %d\n'% \ |
---|
354 | (result[2]['nfev'],Histograms['Nobs'],len(varyList),Histograms['Nrej'],Histograms['Next'])) |
---|
355 | if ncyc: |
---|
356 | printFile.write(' Refinement time = %8.3fs, %8.3fs/cycle, for %d cycles\n'%(runtime,runtime/ncyc,ncyc)) |
---|
357 | printFile.write(' wR = %7.2f%%, chi**2 = %12.6g, GOF = %6.2f\n'%(Rvals['Rwp'],Rvals['chisq'],Rvals['GOF'])) |
---|
358 | sig = len(varyList)*[None,] |
---|
359 | if 'None' in str(type(result[1])) and ifSeq: #this bails out of a sequential refinement on singular matrix |
---|
360 | IfOK = False |
---|
361 | covMatrix = [] |
---|
362 | G2fil.G2Print ('Warning: **** Refinement failed - singular matrix ****') |
---|
363 | if 'Hessian' in Controls['deriv type']: |
---|
364 | num = len(varyList)-1 |
---|
365 | # BHT -- I am not sure if this works correctly: |
---|
366 | for i,val in enumerate(np.flipud(result[2]['psing'])): |
---|
367 | if val: |
---|
368 | G2fil.G2Print('Bad parameter: '+varyList[num-i],mode='warn') |
---|
369 | else: |
---|
370 | Ipvt = result[2]['ipvt'] |
---|
371 | for i,ipvt in enumerate(Ipvt): |
---|
372 | if not np.sum(result[2]['fjac'],axis=1)[i]: |
---|
373 | G2fil.G2Print('Bad parameter: '+varyList[ipvt-1],mode='warn') |
---|
374 | break |
---|
375 | IfOK = True |
---|
376 | if not len(varyList) or not maxCyc: |
---|
377 | covMatrix = [] |
---|
378 | break |
---|
379 | try: |
---|
380 | covMatrix = result[1]*Rvals['GOF']**2 |
---|
381 | sig = np.sqrt(np.diag(covMatrix)) |
---|
382 | Lastshft = result[0]-values #NOT last shift since values is starting set before current refinement |
---|
383 | #table = dict(zip(varyList,zip(values,result[0],Lastshft,Lastshft/sig))) |
---|
384 | #for item in table: print(item,table[item]) #useful debug |
---|
385 | Rvals['Max shft/sig'] = np.max(np.nan_to_num(Lastshft/sig)) |
---|
386 | if np.any(np.isnan(sig)) or not sig.shape: |
---|
387 | G2fil.G2Print ('*** Least squares aborted - some invalid esds possible ***',mode='error') |
---|
388 | else: |
---|
389 | print('Maximum shift/esd = {:.3f} for all cycles'.format(Rvals['Max shft/sig'])) |
---|
390 | # report on refinement issues. Result in Rvals['msg'] |
---|
391 | ReportProblems(result,Rvals,varyList) |
---|
392 | break #refinement succeeded - finish up! |
---|
393 | except TypeError: |
---|
394 | # if we get here, no result[1] (covar matrix) was returned or other calc error: refinement failed |
---|
395 | IfOK = False |
---|
396 | if 'Hessian' in Controls['deriv type']: |
---|
397 | SVD0 = result[2].get('SVD0') |
---|
398 | if SVD0 == -1: |
---|
399 | G2fil.G2Print ('**** Refinement failed - singular matrix ****',mode='error') |
---|
400 | elif SVD0 == -2: |
---|
401 | G2fil.G2Print ('**** Refinement failed - other problem ****',mode='error') |
---|
402 | elif SVD0 > 0: |
---|
403 | G2fil.G2Print ('**** Refinement failed with {} SVD singularities ****'.format(SVD0),mode='error') |
---|
404 | else: |
---|
405 | G2fil.G2Print ('**** Refinement failed ****',mode='error') |
---|
406 | if result[1] is None: |
---|
407 | IfOK = False |
---|
408 | covMatrix = [] |
---|
409 | sig = len(varyList)*[None,] |
---|
410 | # report on highly correlated variables |
---|
411 | ReportProblems(result,Rvals,varyList) |
---|
412 | # process singular variables |
---|
413 | if dlg: break # refining interactively |
---|
414 | else: |
---|
415 | G2fil.G2Print ('**** Refinement failed - singular matrix ****',mode='error') |
---|
416 | Ipvt = result[2]['ipvt'] |
---|
417 | for i,ipvt in enumerate(Ipvt): |
---|
418 | if not np.sum(result[2]['fjac'],axis=1)[i]: |
---|
419 | G2fil.G2Print ('Removing parameter: '+varyList[ipvt-1]) |
---|
420 | del(varyList[ipvt-1]) |
---|
421 | break |
---|
422 | if IfOK: |
---|
423 | if CheckLeBail(Phases): # only needed for LeBail extraction |
---|
424 | G2stMth.errRefine([],[Histograms,Phases,restraintDict,rigidbodyDict], |
---|
425 | parmDict,[],calcControls,pawleyLookup,dlg) |
---|
426 | G2stMth.GetFobsSq(Histograms,Phases,parmDict,calcControls) |
---|
427 | if chisq0 is not None: |
---|
428 | Rvals['GOF0'] = np.sqrt(chisq0/(Histograms['Nobs']-len(varyList))) |
---|
429 | return IfOK,Rvals,result,covMatrix,sig,Lastshft |
---|
430 | |
---|
431 | def Refine(GPXfile,dlg=None,makeBack=True,refPlotUpdate=None,newLeBail=False,allDerivs=False): |
---|
432 | '''Global refinement -- refines to minimize against all histograms. |
---|
433 | This can be called in one of three ways, from :meth:`GSASIIdataGUI.GSASII.OnRefine` in an |
---|
434 | interactive refinement, where dlg will be a wx.ProgressDialog, or non-interactively from |
---|
435 | :meth:`GSASIIscriptable.G2Project.refine` or from :func:`do_refine`, where dlg will be None. |
---|
436 | ''' |
---|
437 | import GSASIImpsubs as G2mp |
---|
438 | G2mp.InitMP() |
---|
439 | import pytexture as ptx |
---|
440 | ptx.pyqlmninit() #initialize fortran arrays for spherical harmonics |
---|
441 | |
---|
442 | printFile = open(ospath.splitext(GPXfile)[0]+'.lst','w') |
---|
443 | G2stIO.ShowBanner(printFile) |
---|
444 | varyList = [] |
---|
445 | parmDict = {} |
---|
446 | G2mv.InitVars() |
---|
447 | Controls = G2stIO.GetControls(GPXfile) |
---|
448 | Controls['newLeBail'] = newLeBail |
---|
449 | G2stIO.ShowControls(Controls,printFile) |
---|
450 | calcControls = {} |
---|
451 | calcControls.update(Controls) |
---|
452 | constrDict,fixedList = G2stIO.ReadConstraints(GPXfile) |
---|
453 | restraintDict = G2stIO.GetRestraints(GPXfile) |
---|
454 | Histograms,Phases = G2stIO.GetUsedHistogramsAndPhases(GPXfile) |
---|
455 | if not Phases: |
---|
456 | G2fil.G2Print (' *** ERROR - you have no phases to refine! ***') |
---|
457 | G2fil.G2Print (' *** Refine aborted ***') |
---|
458 | return False,{'msg':'No phases'} |
---|
459 | if not Histograms: |
---|
460 | G2fil.G2Print (' *** ERROR - you have no data to refine with! ***') |
---|
461 | G2fil.G2Print (' *** Refine aborted ***') |
---|
462 | return False,{'msg':'No data'} |
---|
463 | rigidbodyDict = G2stIO.GetRigidBodies(GPXfile) |
---|
464 | rbIds = rigidbodyDict.get('RBIds',{'Vector':[],'Residue':[]}) |
---|
465 | rbVary,rbDict = G2stIO.GetRigidBodyModels(rigidbodyDict,pFile=printFile) |
---|
466 | symHold = None |
---|
467 | if allDerivs: #============= develop partial derivative map |
---|
468 | symHold = [] |
---|
469 | (Natoms,atomIndx,phaseVary,phaseDict,pawleyLookup,FFtables,EFtables,BLtables,MFtables, |
---|
470 | maxSSwave) = G2stIO.GetPhaseData(Phases,restraintDict,rbIds,pFile=printFile,symHold=symHold) |
---|
471 | calcControls['atomIndx'] = atomIndx |
---|
472 | calcControls['Natoms'] = Natoms |
---|
473 | calcControls['FFtables'] = FFtables |
---|
474 | calcControls['EFtables'] = EFtables |
---|
475 | calcControls['BLtables'] = BLtables |
---|
476 | calcControls['MFtables'] = MFtables |
---|
477 | calcControls['maxSSwave'] = maxSSwave |
---|
478 | hapVary,hapDict,controlDict = G2stIO.GetHistogramPhaseData(Phases,Histograms,Controls=calcControls,pFile=printFile) |
---|
479 | TwConstr,TwFixed = G2stIO.makeTwinFrConstr(Phases,Histograms,hapVary) |
---|
480 | constrDict += TwConstr |
---|
481 | fixedList += TwFixed |
---|
482 | calcControls.update(controlDict) |
---|
483 | histVary,histDict,controlDict = G2stIO.GetHistogramData(Histograms,pFile=printFile) |
---|
484 | calcControls.update(controlDict) |
---|
485 | varyList = rbVary+phaseVary+hapVary+histVary |
---|
486 | parmDict.update(rbDict) |
---|
487 | parmDict.update(phaseDict) |
---|
488 | parmDict.update(hapDict) |
---|
489 | parmDict.update(histDict) |
---|
490 | G2stIO.GetFprime(calcControls,Histograms) |
---|
491 | # do constraint processing |
---|
492 | varyListStart = tuple(varyList) # save the original varyList before dependent vars are removed |
---|
493 | msg = G2mv.EvaluateMultipliers(constrDict,parmDict) |
---|
494 | if allDerivs: #============= develop partial derivative map |
---|
495 | varyListStart = varyList |
---|
496 | varyList = None |
---|
497 | if msg: |
---|
498 | return False,{'msg':'Unable to interpret multiplier(s): '+msg} |
---|
499 | try: |
---|
500 | errmsg,warnmsg,groups,parmlist = G2mv.GenerateConstraints(varyList,constrDict,fixedList,parmDict) |
---|
501 | G2mv.normParms(parmDict) |
---|
502 | G2mv.Map2Dict(parmDict,varyList) # changes varyList |
---|
503 | except G2mv.ConstraintException: |
---|
504 | G2fil.G2Print (' *** ERROR - your constraints are internally inconsistent ***') |
---|
505 | return False,{'msg':' Constraint error'} |
---|
506 | |
---|
507 | # remove frozen vars from refinement |
---|
508 | if 'parmFrozen' not in Controls: |
---|
509 | Controls['parmFrozen'] = {} |
---|
510 | if 'FrozenList' not in Controls['parmFrozen']: |
---|
511 | Controls['parmFrozen']['FrozenList'] = [] |
---|
512 | if varyList is not None: |
---|
513 | parmFrozenList = Controls['parmFrozen']['FrozenList'] |
---|
514 | frozenList = [i for i in varyList if i in parmFrozenList] |
---|
515 | if len(frozenList) != 0: |
---|
516 | varyList = [i for i in varyList if i not in parmFrozenList] |
---|
517 | G2fil.G2Print( |
---|
518 | 'Frozen refined variables (due to exceeding limits)\n\t:{}' |
---|
519 | .format(frozenList)) |
---|
520 | |
---|
521 | ifSeq = False |
---|
522 | printFile.write('\n Refinement results:\n') |
---|
523 | printFile.write(135*'-'+'\n') |
---|
524 | Rvals = {} |
---|
525 | G2mv.Dict2Map(parmDict) # impose constraints initially |
---|
526 | if allDerivs: #============= develop partial derivative map |
---|
527 | derivDict = AllPrmDerivs(Controls, Histograms, Phases, restraintDict, |
---|
528 | rigidbodyDict, parmDict, varyList, calcControls, |
---|
529 | pawleyLookup,symHold,dlg) |
---|
530 | return derivDict,varyListStart |
---|
531 | |
---|
532 | try: |
---|
533 | covData = {} |
---|
534 | IfOK,Rvals,result,covMatrix,sig,Lastshft = RefineCore(Controls,Histograms,Phases,restraintDict, |
---|
535 | rigidbodyDict,parmDict,varyList,calcControls,pawleyLookup,ifSeq,printFile,dlg, |
---|
536 | refPlotUpdate=refPlotUpdate) |
---|
537 | if IfOK: |
---|
538 | if len(covMatrix): #empty for zero cycle refinement |
---|
539 | sigDict = dict(zip(varyList,sig)) |
---|
540 | newCellDict = G2stMth.GetNewCellParms(parmDict,varyList) |
---|
541 | newAtomDict = G2stMth.ApplyXYZshifts(parmDict,varyList) |
---|
542 | covData = {'variables':result[0],'varyList':varyList,'sig':sig,'Rvals':Rvals, |
---|
543 | 'varyListStart':varyListStart,'Lastshft':Lastshft, |
---|
544 | 'covMatrix':covMatrix,'title':GPXfile,'newAtomDict':newAtomDict, |
---|
545 | 'newCellDict':newCellDict,'freshCOV':True} |
---|
546 | # add indirectly computed uncertainties into the esd dict |
---|
547 | sigDict.update(G2mv.ComputeDepESD(covMatrix,varyList)) |
---|
548 | G2stIO.PrintIndependentVars(parmDict,varyList,sigDict,pFile=printFile) |
---|
549 | G2stMth.ApplyRBModels(parmDict,Phases,rigidbodyDict,True) |
---|
550 | G2stIO.SetRigidBodyModels(parmDict,sigDict,rigidbodyDict,printFile) |
---|
551 | G2stIO.SetPhaseData(parmDict,sigDict,Phases,rbIds,covData,restraintDict,printFile) |
---|
552 | G2stIO.SetISOmodes(parmDict,sigDict,Phases,printFile) |
---|
553 | G2stIO.SetHistogramPhaseData(parmDict,sigDict,Phases,Histograms,calcControls, |
---|
554 | pFile=printFile,covMatrix=covMatrix,varyList=varyList) |
---|
555 | G2stIO.SetHistogramData(parmDict,sigDict,Histograms,calcControls,pFile=printFile) |
---|
556 | # check for variables outside their allowed range, reset and freeze them |
---|
557 | frozen = dropOOBvars(varyList,parmDict,sigDict,Controls,parmFrozenList) |
---|
558 | # covData['depSig'] = G2stIO.PhFrExtPOSig # created in G2stIO.SetHistogramData, no longer used? |
---|
559 | covData['depSigDict'] = {i:(parmDict[i],sigDict[i]) for i in parmDict if i in sigDict} |
---|
560 | if len(frozen): |
---|
561 | if 'msg' in Rvals: |
---|
562 | Rvals['msg'] += '\n' |
---|
563 | else: |
---|
564 | Rvals['msg'] = '' |
---|
565 | msg = ('Warning: {} variable(s) refined outside limits and were frozen ({} total frozen)' |
---|
566 | .format(len(frozen),len(parmFrozenList)) |
---|
567 | ) |
---|
568 | G2fil.G2Print(msg) |
---|
569 | Rvals['msg'] += msg |
---|
570 | elif len(parmFrozenList): |
---|
571 | if 'msg' in Rvals: |
---|
572 | Rvals['msg'] += '\n' |
---|
573 | else: |
---|
574 | Rvals['msg'] = '' |
---|
575 | msg = ('Note: a total of {} variable(s) are frozen due to refining outside limits' |
---|
576 | .format(len(parmFrozenList)) |
---|
577 | ) |
---|
578 | G2fil.G2Print('Note: ',msg) |
---|
579 | Rvals['msg'] += msg |
---|
580 | G2stIO.SetUsedHistogramsAndPhases(GPXfile,Histograms,Phases,rigidbodyDict,covData,parmFrozenList,makeBack) |
---|
581 | printFile.close() |
---|
582 | G2fil.G2Print (' Refinement results are in file: '+ospath.splitext(GPXfile)[0]+'.lst') |
---|
583 | G2fil.G2Print (' ***** Refinement successful *****') |
---|
584 | else: |
---|
585 | G2fil.G2Print ('****ERROR - Refinement failed',mode='error') |
---|
586 | if 'msg' in Rvals: |
---|
587 | G2fil.G2Print ('Note refinement problem:',mode='warn') |
---|
588 | G2fil.G2Print (Rvals['msg'],mode='warn') |
---|
589 | raise G2obj.G2Exception('**** ERROR: Refinement failed ****') |
---|
590 | except G2obj.G2RefineCancel as Msg: |
---|
591 | printFile.close() |
---|
592 | G2fil.G2Print (' ***** Refinement stopped *****') |
---|
593 | if not hasattr(Msg,'msg'): Msg.msg = str(Msg) |
---|
594 | if 'msg' in Rvals: |
---|
595 | Rvals['msg'] += '\n' |
---|
596 | Rvals['msg'] += Msg.msg |
---|
597 | if not dlg: |
---|
598 | G2fil.G2Print ('Note refinement problem:',mode='warn') |
---|
599 | G2fil.G2Print (Rvals['msg'],mode='warn') |
---|
600 | else: |
---|
601 | Rvals['msg'] = Msg.msg |
---|
602 | return False,Rvals |
---|
603 | # except G2obj.G2Exception as Msg: # cell metric error, others? |
---|
604 | except Exception as Msg: # cell metric error, others? |
---|
605 | if GSASIIpath.GetConfigValue('debug'): |
---|
606 | import traceback |
---|
607 | print(traceback.format_exc()) |
---|
608 | if not hasattr(Msg,'msg'): Msg.msg = str(Msg) |
---|
609 | printFile.close() |
---|
610 | G2fil.G2Print (' ***** Refinement error *****') |
---|
611 | if 'msg' in Rvals: |
---|
612 | Rvals['msg'] += '\n\n' |
---|
613 | Rvals['msg'] += Msg.msg |
---|
614 | if not dlg: |
---|
615 | G2fil.G2Print ('Note refinement problem:',mode='warn') |
---|
616 | G2fil.G2Print (Rvals['msg'],mode='warn') |
---|
617 | else: |
---|
618 | Rvals['msg'] = Msg.msg |
---|
619 | return False,Rvals |
---|
620 | |
---|
621 | #for testing purposes, create a file for testderiv |
---|
622 | if GSASIIpath.GetConfigValue('debug'): # and IfOK: |
---|
623 | #needs: values,HistoPhases,parmDict,varylist,calcControls,pawleyLookup |
---|
624 | fl = open(ospath.splitext(GPXfile)[0]+'.testDeriv','wb') |
---|
625 | cPickle.dump(result[0],fl,1) |
---|
626 | cPickle.dump([Histograms,Phases,restraintDict,rigidbodyDict],fl,1) |
---|
627 | cPickle.dump([constrDict,fixedList,G2mv.GetDependentVars()],fl,1) |
---|
628 | cPickle.dump(parmDict,fl,1) |
---|
629 | cPickle.dump(varyList,fl,1) |
---|
630 | cPickle.dump(calcControls,fl,1) |
---|
631 | cPickle.dump(pawleyLookup,fl,1) |
---|
632 | fl.close() |
---|
633 | if dlg: |
---|
634 | return True,Rvals |
---|
635 | elif 'msg' in Rvals: |
---|
636 | G2fil.G2Print ('Reported from refinement:',mode='warn') |
---|
637 | G2fil.G2Print (Rvals['msg'],mode='warn') |
---|
638 | |
---|
639 | def CheckLeBail(Phases): |
---|
640 | '''Check if there is a LeBail extraction in any histogram |
---|
641 | |
---|
642 | :returns: True if there is at least one LeBail flag turned on, False otherwise |
---|
643 | ''' |
---|
644 | for key in Phases: |
---|
645 | phase = Phases[key] |
---|
646 | for h in phase['Histograms']: |
---|
647 | #phase['Histograms'][h] |
---|
648 | if not phase['Histograms'][h]['Use']: continue |
---|
649 | try: |
---|
650 | if phase['Histograms'][h]['LeBail']: |
---|
651 | return True |
---|
652 | except KeyError: #HKLF & old gpx files |
---|
653 | pass |
---|
654 | return False |
---|
655 | |
---|
656 | def DoLeBail(GPXfile,dlg=None,cycles=10,refPlotUpdate=None,seqList=None): |
---|
657 | '''Fit LeBail intensities without changes to any other refined parameters. |
---|
658 | This is a stripped-down version of :func:`Refine` that does not perform |
---|
659 | any refinement cycles |
---|
660 | |
---|
661 | :param str GPXfile: G2 .gpx file name |
---|
662 | :param wx.ProgressDialog dlg: optional progress window to update. |
---|
663 | Default is None, which means no calls are made to this. |
---|
664 | :param int cycles: Number of LeBail cycles to perform |
---|
665 | :param function refPlotUpdate: Optional routine used to plot results. |
---|
666 | Default is None, which means no calls are made to this. |
---|
667 | :param list seqList: List of histograms to be processed. Default |
---|
668 | is None which means that all used histograms in .gpx file are processed. |
---|
669 | ''' |
---|
670 | import GSASIImpsubs as G2mp |
---|
671 | G2mp.InitMP() |
---|
672 | import pytexture as ptx |
---|
673 | ptx.pyqlmninit() #initialize fortran arrays for spherical harmonics |
---|
674 | |
---|
675 | #varyList = [] |
---|
676 | parmDict = {} |
---|
677 | Controls = G2stIO.GetControls(GPXfile) |
---|
678 | calcControls = {} |
---|
679 | calcControls.update(Controls) |
---|
680 | constrDict,fixedList = G2stIO.ReadConstraints(GPXfile) |
---|
681 | restraintDict = {} |
---|
682 | Histograms_All,Phases = G2stIO.GetUsedHistogramsAndPhases(GPXfile) |
---|
683 | if seqList: |
---|
684 | Histograms = {i:Histograms_All[i] for i in seqList} |
---|
685 | else: |
---|
686 | Histograms = Histograms_All |
---|
687 | if not Phases: |
---|
688 | G2fil.G2Print (' *** ERROR - you have no phases to refine! ***') |
---|
689 | return False,{'msg':'No phases'} |
---|
690 | if not Histograms: |
---|
691 | G2fil.G2Print (' *** ERROR - you have no data to refine with! ***') |
---|
692 | return False,{'msg':'No data'} |
---|
693 | if not CheckLeBail(Phases): |
---|
694 | msg = 'Warning: There are no histograms with LeBail extraction enabled' |
---|
695 | G2fil.G2Print ('*** '+msg+' ***') |
---|
696 | return False,{'msg': msg} |
---|
697 | rigidbodyDict = G2stIO.GetRigidBodies(GPXfile) |
---|
698 | rbIds = rigidbodyDict.get('RBIds',{'Vector':[],'Residue':[]}) |
---|
699 | rbVary,rbDict = G2stIO.GetRigidBodyModels(rigidbodyDict,Print=False) |
---|
700 | (Natoms,atomIndx,phaseVary,phaseDict,pawleyLookup,FFtables,EFtables,BLtables,MFtables, |
---|
701 | maxSSwave) = G2stIO.GetPhaseData(Phases,restraintDict,rbIds,Print=False) |
---|
702 | calcControls['atomIndx'] = atomIndx |
---|
703 | calcControls['Natoms'] = Natoms |
---|
704 | calcControls['FFtables'] = FFtables |
---|
705 | calcControls['EFtables'] = EFtables |
---|
706 | calcControls['BLtables'] = BLtables |
---|
707 | calcControls['MFtables'] = MFtables |
---|
708 | calcControls['maxSSwave'] = maxSSwave |
---|
709 | hapVary,hapDict,controlDict = G2stIO.GetHistogramPhaseData(Phases,Histograms,Controls=calcControls,Print=False) |
---|
710 | calcControls.update(controlDict) |
---|
711 | histVary,histDict,controlDict = G2stIO.GetHistogramData(Histograms,Print=False) |
---|
712 | calcControls.update(controlDict) |
---|
713 | parmDict.update(rbDict) |
---|
714 | parmDict.update(phaseDict) |
---|
715 | parmDict.update(hapDict) |
---|
716 | parmDict.update(histDict) |
---|
717 | G2stIO.GetFprime(calcControls,Histograms) |
---|
718 | try: |
---|
719 | for i in range(cycles): |
---|
720 | M = G2stMth.errRefine([],[Histograms,Phases,restraintDict,rigidbodyDict],parmDict,[],calcControls,pawleyLookup,dlg) |
---|
721 | G2stMth.GetFobsSq(Histograms,Phases,parmDict,calcControls) |
---|
722 | if refPlotUpdate is not None: refPlotUpdate(Histograms,i) |
---|
723 | Rvals = {} |
---|
724 | Rvals['chisq'] = np.sum(M**2) |
---|
725 | Rvals['Nobs'] = Histograms['Nobs'] |
---|
726 | Rvals['Rwp'] = np.sqrt(Rvals['chisq']/Histograms['sumwYo'])*100. #to % |
---|
727 | Rvals['GOF'] = np.sqrt(Rvals['chisq']/(Histograms['Nobs'])) # no variables |
---|
728 | |
---|
729 | covData = {'variables':0,'varyList':[],'sig':[],'Rvals':Rvals,'varyListStart':[], |
---|
730 | 'covMatrix':None,'title':GPXfile,'freshCOV':True} #np.zeros([0,0])? |
---|
731 | # ?? 'newAtomDict':newAtomDict,'newCellDict':newCellDict, |
---|
732 | |
---|
733 | G2stIO.SetUsedHistogramsAndPhases(GPXfile,Histograms,Phases,rigidbodyDict,covData,[],True) |
---|
734 | G2fil.G2Print (' ***** LeBail fit completed *****') |
---|
735 | return True,Rvals |
---|
736 | except Exception as Msg: |
---|
737 | G2fil.G2Print (' ***** LeBail fit error *****') |
---|
738 | if not hasattr(Msg,'msg'): Msg.msg = str(Msg) |
---|
739 | if GSASIIpath.GetConfigValue('debug'): |
---|
740 | import traceback |
---|
741 | print(traceback.format_exc()) |
---|
742 | return False,{'msg':Msg.msg} |
---|
743 | |
---|
744 | def phaseCheck(phaseVary,Phases,histogram): |
---|
745 | ''' |
---|
746 | Removes unused parameters from phase varylist if phase not in histogram |
---|
747 | for seq refinement removes vars in "Fix FXU" and "FixedSeqVars" here |
---|
748 | ''' |
---|
749 | NewVary = [] |
---|
750 | for phase in Phases: |
---|
751 | if histogram not in Phases[phase]['Histograms']: continue |
---|
752 | if Phases[phase]['Histograms'][histogram]['Use']: |
---|
753 | pId = Phases[phase]['pId'] |
---|
754 | newVary = [item for item in phaseVary if item.split(':')[0] == str(pId)] |
---|
755 | FixVals = Phases[phase]['Histograms'][histogram].get('Fix FXU',' ') |
---|
756 | if 'F' in FixVals: |
---|
757 | newVary = [item for item in newVary if not 'Afrac' in item] |
---|
758 | if 'X' in FixVals: |
---|
759 | newVary = [item for item in newVary if not 'dA' in item] |
---|
760 | if 'U' in FixVals: |
---|
761 | newVary = [item for item in newVary if not 'AU' in item] |
---|
762 | if 'M' in FixVals: |
---|
763 | newVary = [item for item in newVary if not 'AM' in item] |
---|
764 | removeVars = Phases[phase]['Histograms'][histogram].get('FixedSeqVars',[]) |
---|
765 | newVary = [item for item in newVary if item not in removeVars] |
---|
766 | NewVary += newVary |
---|
767 | return NewVary |
---|
768 | |
---|
769 | def SeqRefine(GPXfile,dlg,refPlotUpdate=None): |
---|
770 | '''Perform a sequential refinement -- cycles through all selected histgrams, |
---|
771 | one at a time |
---|
772 | ''' |
---|
773 | import GSASIImpsubs as G2mp |
---|
774 | G2mp.InitMP() |
---|
775 | import pytexture as ptx |
---|
776 | ptx.pyqlmninit() #initialize fortran arrays for spherical harmonics |
---|
777 | msgs = {} |
---|
778 | printFile = open(ospath.splitext(GPXfile)[0]+'.lst','w') |
---|
779 | G2fil.G2Print ('Starting Sequential Refinement') |
---|
780 | G2stIO.ShowBanner(printFile) |
---|
781 | Controls = G2stIO.GetControls(GPXfile) |
---|
782 | preFrozenCount = 0 |
---|
783 | for h in Controls['parmFrozen']: |
---|
784 | if h == 'FrozenList': |
---|
785 | continue |
---|
786 | preFrozenCount += len(Controls['parmFrozen'][h]) |
---|
787 | G2stIO.ShowControls(Controls,printFile,SeqRef=True,preFrozenCount=preFrozenCount) |
---|
788 | restraintDict = G2stIO.GetRestraints(GPXfile) |
---|
789 | Histograms,Phases = G2stIO.GetUsedHistogramsAndPhases(GPXfile) |
---|
790 | if not Phases: |
---|
791 | G2fil.G2Print (' *** ERROR - you have no phases to refine! ***') |
---|
792 | G2fil.G2Print (' *** Refine aborted ***') |
---|
793 | return False,'No phases' |
---|
794 | if not Histograms: |
---|
795 | G2fil.G2Print (' *** ERROR - you have no data to refine with! ***') |
---|
796 | G2fil.G2Print (' *** Refine aborted ***') |
---|
797 | return False,'No data' |
---|
798 | rigidbodyDict = G2stIO.GetRigidBodies(GPXfile) |
---|
799 | rbIds = rigidbodyDict.get('RBIds',{'Vector':[],'Residue':[]}) |
---|
800 | rbVary,rbDict = G2stIO.GetRigidBodyModels(rigidbodyDict,pFile=printFile) |
---|
801 | G2mv.InitVars() |
---|
802 | (Natoms,atomIndx,phaseVary,phaseDict,pawleyLookup,FFtables,EFtables,BLtables,MFtables,maxSSwave) = \ |
---|
803 | G2stIO.GetPhaseData(Phases,restraintDict,rbIds,Print=False,pFile=printFile,seqHistName='All') |
---|
804 | for item in phaseVary: |
---|
805 | if '::A0' in item: |
---|
806 | G2fil.G2Print ('**** WARNING - lattice parameters should not be refined in a sequential refinement ****') |
---|
807 | G2fil.G2Print ('**** instead use the Dij parameters for each powder histogram ****') |
---|
808 | return False,'Lattice parameter refinement error - see console message' |
---|
809 | if '::C(' in item: |
---|
810 | G2fil.G2Print ('**** WARNING - phase texture parameters should not be refined in a sequential refinement ****') |
---|
811 | G2fil.G2Print ('**** instead use the C(L,N) parameters for each powder histogram ****') |
---|
812 | return False,'Phase texture refinement error - see console message' |
---|
813 | if 'Seq Data' in Controls: |
---|
814 | histNames = Controls['Seq Data'] |
---|
815 | else: # patch from before Controls['Seq Data'] was implemented? |
---|
816 | histNames = G2stIO.GetHistogramNames(GPXfile,['PWDR',]) |
---|
817 | if Controls.get('Reverse Seq'): |
---|
818 | histNames.reverse() |
---|
819 | SeqResult = G2stIO.GetSeqResult(GPXfile) |
---|
820 | # SeqResult = {'SeqPseudoVars':{},'SeqParFitEqList':[]} |
---|
821 | Histo = {} |
---|
822 | NewparmDict = {} |
---|
823 | G2stIO.SetupSeqSavePhases(GPXfile) |
---|
824 | msgs['steepestNum'] = 0 |
---|
825 | msgs['maxshift/sigma'] = [] |
---|
826 | lasthist = '' |
---|
827 | for ihst,histogram in enumerate(histNames): |
---|
828 | if GSASIIpath.GetConfigValue('Show_timing'): t1 = time.time() |
---|
829 | G2fil.G2Print('\nRefining with '+str(histogram)) |
---|
830 | G2mv.InitVars() |
---|
831 | (Natoms,atomIndx,phaseVary,phaseDict,pawleyLookup,FFtables,EFtables,BLtables,MFtables,maxSSwave) = \ |
---|
832 | G2stIO.GetPhaseData(Phases,restraintDict,rbIds,Print=False,pFile=printFile,seqHistName=histogram) |
---|
833 | ifPrint = False |
---|
834 | if dlg: |
---|
835 | dlg.SetTitle('Residual for histogram '+str(ihst)) |
---|
836 | calcControls = {} |
---|
837 | calcControls['atomIndx'] = atomIndx |
---|
838 | calcControls['Natoms'] = Natoms |
---|
839 | calcControls['FFtables'] = FFtables |
---|
840 | calcControls['EFtables'] = EFtables |
---|
841 | calcControls['BLtables'] = BLtables |
---|
842 | calcControls['MFtables'] = MFtables |
---|
843 | calcControls['maxSSwave'] = maxSSwave |
---|
844 | if histogram not in Histograms: |
---|
845 | G2fil.G2Print("Error: not found!") |
---|
846 | raise G2obj.G2Exception("refining with invalid histogram {}".format(histogram)) |
---|
847 | hId = Histograms[histogram]['hId'] |
---|
848 | redphaseVary = phaseCheck(phaseVary,Phases,histogram) |
---|
849 | Histo = {histogram:Histograms[histogram],} |
---|
850 | hapVary,hapDict,controlDict = G2stIO.GetHistogramPhaseData(Phases,Histo,Controls=calcControls,Print=False) |
---|
851 | calcControls.update(controlDict) |
---|
852 | histVary,histDict,controlDict = G2stIO.GetHistogramData(Histo,False) |
---|
853 | calcControls.update(controlDict) |
---|
854 | varyList = rbVary+redphaseVary+hapVary+histVary |
---|
855 | # if not ihst: |
---|
856 | # save the initial vary list, but without histogram numbers on parameters |
---|
857 | saveVaryList = varyList[:] |
---|
858 | for i,item in enumerate(saveVaryList): |
---|
859 | items = item.split(':') |
---|
860 | if items[1]: |
---|
861 | items[1] = '' |
---|
862 | item = ':'.join(items) |
---|
863 | saveVaryList[i] = item |
---|
864 | if not ihst: |
---|
865 | SeqResult['varyList'] = saveVaryList |
---|
866 | else: |
---|
867 | SeqResult['varyList'] = list(set(SeqResult['varyList']+saveVaryList)) |
---|
868 | parmDict = {} |
---|
869 | parmDict.update(rbDict) |
---|
870 | parmDict.update(phaseDict) |
---|
871 | parmDict.update(hapDict) |
---|
872 | parmDict.update(histDict) |
---|
873 | if Controls['Copy2Next']: # update with parms from last histogram |
---|
874 | #parmDict.update(NewparmDict) # don't use in case extra entries would cause a problem |
---|
875 | for parm in NewparmDict: |
---|
876 | if parm in parmDict: |
---|
877 | parmDict[parm] = NewparmDict[parm] |
---|
878 | for phase in Phases: |
---|
879 | if Phases[phase]['Histograms'][histogram].get('LeBail',False) and lasthist: |
---|
880 | oldFsqs = Histograms[lasthist]['Reflection Lists'][phase]['RefList'].T[8:10] #assume no superlattice! |
---|
881 | newRefs = Histograms[histogram]['Reflection Lists'][phase]['RefList'] |
---|
882 | if len(newRefs) == len(oldFsqs.T): |
---|
883 | newRefs.T[8:10] = copy.copy(oldFsqs) |
---|
884 | # for i,ref in enumerate(newRefs): |
---|
885 | # ref[8:10] = oldFsqs.T[i] |
---|
886 | else: |
---|
887 | print('ERROR - mismatch in reflection list length bewteen %s and %s; no copy done'%(lasthist,histogram)) |
---|
888 | ####TBD: if LeBail copy reflections here? |
---|
889 | elif histogram in SeqResult: # update phase from last seq ref |
---|
890 | NewparmDict = SeqResult[histogram].get('parmDict',{}) |
---|
891 | for parm in NewparmDict: |
---|
892 | if '::' in parm and parm in parmDict: |
---|
893 | parmDict[parm] = NewparmDict[parm] |
---|
894 | |
---|
895 | G2stIO.GetFprime(calcControls,Histo) |
---|
896 | # do constraint processing (again, if called from GSASIIdataGUI.GSASII.OnSeqRefine) |
---|
897 | constrDict,fixedList = G2stIO.ReadConstraints(GPXfile,seqHist=hId) |
---|
898 | varyListStart = tuple(varyList) # save the original varyList before dependent vars are removed |
---|
899 | |
---|
900 | msg = G2mv.EvaluateMultipliers(constrDict,phaseDict,hapDict,histDict) |
---|
901 | if msg: |
---|
902 | return False,'Unable to interpret multiplier(s): '+msg |
---|
903 | |
---|
904 | try: |
---|
905 | errmsg,warnmsg,groups,parmlist = G2mv.GenerateConstraints(varyList,constrDict,fixedList,parmDict, |
---|
906 | seqHistNum=hId,raiseException=True) |
---|
907 | constraintInfo = (groups,parmlist,constrDict,fixedList,ihst) |
---|
908 | G2mv.normParms(parmDict) |
---|
909 | G2mv.Map2Dict(parmDict,varyList) # changes varyList |
---|
910 | except G2mv.ConstraintException: |
---|
911 | G2fil.G2Print (' *** ERROR - your constraints are internally inconsistent for histogram {}***'.format(hId)) |
---|
912 | return False,' Constraint error' |
---|
913 | if not ihst: |
---|
914 | # first histogram to refine against |
---|
915 | firstVaryList = [] |
---|
916 | for item in varyList: |
---|
917 | items = item.split(':') |
---|
918 | if items[1]: |
---|
919 | items[1] = '' |
---|
920 | item = ':'.join(items) |
---|
921 | firstVaryList.append(item) |
---|
922 | newVaryList = firstVaryList |
---|
923 | else: |
---|
924 | newVaryList = [] |
---|
925 | for item in varyList: |
---|
926 | items = item.split(':') |
---|
927 | if items[1]: |
---|
928 | items[1] = '' |
---|
929 | item = ':'.join(items) |
---|
930 | newVaryList.append(item) |
---|
931 | if newVaryList != firstVaryList and Controls['Copy2Next']: |
---|
932 | # variable lists are expected to match between sequential refinements when Copy2Next is on |
---|
933 | #print '**** ERROR - variable list for this histogram does not match previous' |
---|
934 | #print ' Copy of variables is not possible' |
---|
935 | #print '\ncurrent histogram',histogram,'has',len(newVaryList),'variables' |
---|
936 | combined = list(set(firstVaryList+newVaryList)) |
---|
937 | c = [var for var in combined if var not in newVaryList] |
---|
938 | p = [var for var in combined if var not in firstVaryList] |
---|
939 | G2fil.G2Print('*** Variables change ***') |
---|
940 | for typ,vars in [('Removed',c),('Added',p)]: |
---|
941 | line = ' '+typ+': ' |
---|
942 | if vars: |
---|
943 | for var in vars: |
---|
944 | if len(line) > 70: |
---|
945 | G2fil.G2Print(line) |
---|
946 | line = ' ' |
---|
947 | line += var + ', ' |
---|
948 | else: |
---|
949 | line += 'none, ' |
---|
950 | G2fil.G2Print(line[:-2]) |
---|
951 | firstVaryList = newVaryList |
---|
952 | |
---|
953 | ifSeq = True |
---|
954 | printFile.write('\n Refinement results for histogram id {}: {}\n' |
---|
955 | .format(hId,histogram)) |
---|
956 | printFile.write(135*'-'+'\n') |
---|
957 | lasthist = histogram |
---|
958 | # remove frozen vars |
---|
959 | if 'parmFrozen' not in Controls: |
---|
960 | Controls['parmFrozen'] = {} |
---|
961 | if histogram not in Controls['parmFrozen']: |
---|
962 | Controls['parmFrozen'][histogram] = [] |
---|
963 | parmFrozenList = Controls['parmFrozen'][histogram] |
---|
964 | frozenList = [i for i in varyList if i in parmFrozenList] |
---|
965 | if len(frozenList) != 0: |
---|
966 | varyList = [i for i in varyList if i not in parmFrozenList] |
---|
967 | s = '' |
---|
968 | for a in frozenList: |
---|
969 | if s: |
---|
970 | s+= ', ' |
---|
971 | s += a |
---|
972 | printFile.write( |
---|
973 | ' The following refined variables have previously been frozen due to exceeding limits:\n\t{}\n' |
---|
974 | .format(s)) |
---|
975 | G2mv.Dict2Map(parmDict) # impose constraints initially |
---|
976 | try: |
---|
977 | IfOK,Rvals,result,covMatrix,sig,Lastshft = RefineCore(Controls,Histo,Phases,restraintDict, |
---|
978 | rigidbodyDict,parmDict,varyList,calcControls,pawleyLookup,ifSeq,printFile,dlg, |
---|
979 | refPlotUpdate=refPlotUpdate) |
---|
980 | try: |
---|
981 | shft = '%.4f'% Rvals['Max shft/sig'] |
---|
982 | except: |
---|
983 | shft = '?' |
---|
984 | G2fil.G2Print (' wR = %7.2f%%, chi**2 = %12.6g, reduced chi**2 = %6.2f, last delta chi = %.4f, last shft/sig = %s'%( |
---|
985 | Rvals['Rwp'],Rvals['chisq'],Rvals['GOF']**2,Rvals['DelChi2'],shft)) |
---|
986 | if Rvals.get('lamMax',0) >= 10.: |
---|
987 | msgs['steepestNum'] += 1 |
---|
988 | if Rvals.get('Max shft/sig'): |
---|
989 | msgs['maxshift/sigma'].append(Rvals['Max shft/sig']) |
---|
990 | # add the uncertainties into the esd dictionary (sigDict) |
---|
991 | if not IfOK: |
---|
992 | G2fil.G2Print('***** Sequential refinement failed at histogram '+histogram,mode='warn') |
---|
993 | break |
---|
994 | sigDict = dict(zip(varyList,sig)) |
---|
995 | # add indirectly computed uncertainties into the esd dict |
---|
996 | sigDict.update(G2mv.ComputeDepESD(covMatrix,varyList)) |
---|
997 | |
---|
998 | newCellDict = copy.deepcopy(G2stMth.GetNewCellParms(parmDict,varyList)) |
---|
999 | newAtomDict = copy.deepcopy(G2stMth.ApplyXYZshifts(parmDict,varyList)) |
---|
1000 | SeqResult[histogram] = { |
---|
1001 | 'variables':result[0],'varyList':varyList,'sig':sig,'Rvals':Rvals, |
---|
1002 | 'varyListStart':varyListStart,'Lastshft':Lastshft, |
---|
1003 | 'covMatrix':covMatrix,'title':histogram,'newAtomDict':newAtomDict, |
---|
1004 | 'newCellDict':newCellDict,'depParmDict':{}, |
---|
1005 | 'constraintInfo':constraintInfo, |
---|
1006 | 'parmDict':parmDict, |
---|
1007 | } |
---|
1008 | G2stMth.ApplyRBModels(parmDict,Phases,rigidbodyDict,True) |
---|
1009 | # G2stIO.SetRigidBodyModels(parmDict,sigDict,rigidbodyDict,printFile) # TODO: why is this not called? Do rigid body prms get updated? |
---|
1010 | G2stIO.SetISOmodes(parmDict,sigDict,Phases,None) |
---|
1011 | G2stIO.SetHistogramPhaseData(parmDict,sigDict,Phases,Histo,None,ifPrint, |
---|
1012 | pFile=printFile,covMatrix=covMatrix,varyList=varyList) |
---|
1013 | G2stIO.SetHistogramData(parmDict,sigDict,Histo,None,ifPrint,printFile,seq=True) |
---|
1014 | # check for variables outside their allowed range, reset and freeze them |
---|
1015 | frozen = dropOOBvars(varyList,parmDict,sigDict,Controls,parmFrozenList) |
---|
1016 | msg = None |
---|
1017 | if len(frozen) > 0: |
---|
1018 | msg = ('Hist {}: {} variables were outside limits and were frozen (now {} frozen total)' |
---|
1019 | .format(ihst,len(frozen),len(parmFrozenList))) |
---|
1020 | G2fil.G2Print(msg) |
---|
1021 | msg = (' {} variables were outside limits and were frozen (now {} frozen total)' |
---|
1022 | .format(len(frozen),len(parmFrozenList))) |
---|
1023 | for p in frozen: |
---|
1024 | if p not in varyList: |
---|
1025 | print('Frozen Warning: {} not in varyList. This should not happen!'.format(p)) |
---|
1026 | continue |
---|
1027 | i = varyList.index(p) |
---|
1028 | result[0][i] = parmDict[p] |
---|
1029 | sig[i] = -0.1 |
---|
1030 | # a dict with values & esds for dependent (constrained) parameters - avoid extraneous holds |
---|
1031 | SeqResult[histogram]['depParmDict'] = {i:(parmDict[i],sigDict[i]) for i in sigDict if i not in varyList} |
---|
1032 | |
---|
1033 | |
---|
1034 | G2stIO.SaveUpdatedHistogramsAndPhases(GPXfile,Histo,Phases, |
---|
1035 | rigidbodyDict,SeqResult[histogram],Controls['parmFrozen']) |
---|
1036 | if msg: |
---|
1037 | printFile.write(msg+'\n') |
---|
1038 | NewparmDict = {} |
---|
1039 | # make dict of varied parameters in current histogram, renamed to |
---|
1040 | # next histogram, for use in next refinement. |
---|
1041 | if Controls['Copy2Next'] and ihst < len(histNames)-1: |
---|
1042 | hId = Histo[histogram]['hId'] # current histogram |
---|
1043 | nexthId = Histograms[histNames[ihst+1]]['hId'] |
---|
1044 | for parm in set(list(varyList)+list(varyListStart)): |
---|
1045 | items = parm.split(':') |
---|
1046 | if len(items) < 3: |
---|
1047 | continue |
---|
1048 | if str(hId) in items[1]: |
---|
1049 | items[1] = str(nexthId) |
---|
1050 | newparm = ':'.join(items) |
---|
1051 | NewparmDict[newparm] = parmDict[parm] |
---|
1052 | else: |
---|
1053 | if items[2].startswith('dA'): parm = parm.replace(':dA',':A') |
---|
1054 | NewparmDict[parm] = parmDict[parm] |
---|
1055 | |
---|
1056 | except G2obj.G2RefineCancel as Msg: |
---|
1057 | if not hasattr(Msg,'msg'): Msg.msg = str(Msg) |
---|
1058 | printFile.close() |
---|
1059 | G2fil.G2Print (' ***** Refinement stopped *****') |
---|
1060 | return False,Msg.msg |
---|
1061 | except G2obj.G2Exception as Msg: # cell metric error, others? |
---|
1062 | if not hasattr(Msg,'msg'): Msg.msg = str(Msg) |
---|
1063 | printFile.close() |
---|
1064 | G2fil.G2Print (' ***** Refinement error *****') |
---|
1065 | return False,Msg.msg |
---|
1066 | if GSASIIpath.GetConfigValue('Show_timing'): |
---|
1067 | t2 = time.time() |
---|
1068 | G2fil.G2Print("Fit step time {:.2f} sec.".format(t2-t1)) |
---|
1069 | t1 = t2 |
---|
1070 | SeqResult['histNames'] = [itm for itm in G2stIO.GetHistogramNames(GPXfile,['PWDR',]) if itm in SeqResult.keys()] |
---|
1071 | try: |
---|
1072 | G2stIO.SetSeqResult(GPXfile,Histograms,SeqResult) |
---|
1073 | except Exception as msg: |
---|
1074 | print('Error reading Sequential results\n',str(msg)) |
---|
1075 | if GSASIIpath.GetConfigValue('debug'): |
---|
1076 | import traceback |
---|
1077 | print(traceback.format_exc()) |
---|
1078 | postFrozenCount = 0 |
---|
1079 | for h in Controls['parmFrozen']: |
---|
1080 | if h == 'FrozenList': continue |
---|
1081 | postFrozenCount += len(Controls['parmFrozen'][h]) |
---|
1082 | if postFrozenCount: |
---|
1083 | msgs['Frozen'] = 'Ending refinement with {} Frozen variables ({} added now)\n'.format(postFrozenCount,postFrozenCount-preFrozenCount) |
---|
1084 | printFile.write('\n'+msgs['Frozen']) |
---|
1085 | printFile.close() |
---|
1086 | G2fil.G2Print (' Sequential refinement results are in file: '+ospath.splitext(GPXfile)[0]+'.lst') |
---|
1087 | G2fil.G2Print (' ***** Sequential refinement successful *****') |
---|
1088 | return True,msgs |
---|
1089 | |
---|
1090 | def dropOOBvars(varyList,parmDict,sigDict,Controls,parmFrozenList): |
---|
1091 | '''Find variables in the parameters dict that are outside the ranges |
---|
1092 | (in parmMinDict and parmMaxDict) and set them to the limits values. |
---|
1093 | Add any such variables into the list of frozen variable |
---|
1094 | (parmFrozenList). Returns a list of newly frozen variables, if any. |
---|
1095 | ''' |
---|
1096 | parmMinDict = Controls.get('parmMinDict',{}) |
---|
1097 | parmMaxDict = Controls.get('parmMaxDict',{}) |
---|
1098 | freeze = [] |
---|
1099 | if parmMinDict or parmMaxDict: |
---|
1100 | for name in varyList: |
---|
1101 | if name not in parmDict: continue |
---|
1102 | n,val = G2obj.prmLookup(name,parmMinDict) |
---|
1103 | if n is not None: |
---|
1104 | if parmDict[name] < parmMinDict[n]: |
---|
1105 | parmDict[name] = parmMinDict[n] |
---|
1106 | sigDict[name] = 0.0 |
---|
1107 | freeze.append(name) |
---|
1108 | continue |
---|
1109 | n,val = G2obj.prmLookup(name,parmMaxDict) |
---|
1110 | if n is not None: |
---|
1111 | if parmDict[name] > parmMaxDict[n]: |
---|
1112 | parmDict[name] = parmMaxDict[n] |
---|
1113 | sigDict[name] = 0.0 |
---|
1114 | freeze.append(name) |
---|
1115 | continue |
---|
1116 | for v in freeze: |
---|
1117 | if v not in parmFrozenList: |
---|
1118 | parmFrozenList.append(v) |
---|
1119 | return freeze |
---|
1120 | |
---|
1121 | def RetDistAngle(DisAglCtls,DisAglData,dlg=None): |
---|
1122 | '''Compute and return distances and angles |
---|
1123 | |
---|
1124 | :param dict DisAglCtls: contains distance/angle radii usually defined using |
---|
1125 | :func:`GSASIIctrlGUI.DisAglDialog` |
---|
1126 | :param dict DisAglData: contains phase data: |
---|
1127 | Items 'OrigAtoms' and 'TargAtoms' contain the atoms to be used |
---|
1128 | for distance/angle origins and atoms to be used as targets. |
---|
1129 | Item 'SGData' has the space group information (see :ref:`Space Group object<SGData_table>`) |
---|
1130 | |
---|
1131 | :returns: AtomLabels,DistArray,AngArray where: |
---|
1132 | |
---|
1133 | **AtomLabels** is a dict of atom labels, keys are the atom number |
---|
1134 | |
---|
1135 | **DistArray** is a dict keyed by the origin atom number where the value is a list |
---|
1136 | of distance entries. The value for each distance is a list containing: |
---|
1137 | |
---|
1138 | 0) the target atom number (int); |
---|
1139 | 1) the unit cell offsets added to x,y & z (tuple of int values) |
---|
1140 | 2) the symmetry operator number (which may be modified to indicate centering and center of symmetry) |
---|
1141 | 3) an interatomic distance in A (float) |
---|
1142 | 4) an uncertainty on the distance in A or 0.0 (float) |
---|
1143 | |
---|
1144 | **AngArray** is a dict keyed by the origin (central) atom number where |
---|
1145 | the value is a list of |
---|
1146 | angle entries. The value for each angle entry consists of three values: |
---|
1147 | |
---|
1148 | 0) a distance item reference for one neighbor (int) |
---|
1149 | 1) a distance item reference for a second neighbor (int) |
---|
1150 | 2) a angle, uncertainty pair; the s.u. may be zero (tuple of two floats) |
---|
1151 | |
---|
1152 | The AngArray distance reference items refer directly to the index of the items in the |
---|
1153 | DistArray item for the list of distances for the central atom. |
---|
1154 | ''' |
---|
1155 | import numpy.ma as ma |
---|
1156 | |
---|
1157 | SGData = DisAglData['SGData'] |
---|
1158 | Cell = DisAglData['Cell'] |
---|
1159 | Amat,Bmat = G2lat.cell2AB(Cell[:6]) |
---|
1160 | covData = {} |
---|
1161 | if len(DisAglData.get('covData',{})): |
---|
1162 | covData = DisAglData['covData'] |
---|
1163 | covMatrix = covData['covMatrix'] |
---|
1164 | varyList = covData['varyList'] |
---|
1165 | pfx = str(DisAglData['pId'])+'::' |
---|
1166 | |
---|
1167 | Factor = DisAglCtls['Factors'] |
---|
1168 | Radii = dict(zip(DisAglCtls['AtomTypes'],zip(DisAglCtls['BondRadii'],DisAglCtls['AngleRadii']))) |
---|
1169 | indices = (-2,-1,0,1,2) |
---|
1170 | Units = np.array([[h,k,l] for h in indices for k in indices for l in indices]) |
---|
1171 | origAtoms = DisAglData['OrigAtoms'] |
---|
1172 | targAtoms = DisAglData['TargAtoms'] |
---|
1173 | AtomLabels = {} |
---|
1174 | for Oatom in origAtoms: |
---|
1175 | AtomLabels[Oatom[0]] = Oatom[1] |
---|
1176 | for Oatom in targAtoms: |
---|
1177 | AtomLabels[Oatom[0]] = Oatom[1] |
---|
1178 | DistArray = {} |
---|
1179 | AngArray = {} |
---|
1180 | for iO,Oatom in enumerate(origAtoms): |
---|
1181 | DistArray[Oatom[0]] = [] |
---|
1182 | AngArray[Oatom[0]] = [] |
---|
1183 | OxyzNames = '' |
---|
1184 | IndBlist = [] |
---|
1185 | Dist = [] |
---|
1186 | Vect = [] |
---|
1187 | VectA = [] |
---|
1188 | angles = [] |
---|
1189 | for Tatom in targAtoms: |
---|
1190 | Xvcov = [] |
---|
1191 | TxyzNames = '' |
---|
1192 | if len(DisAglData.get('covData',{})): |
---|
1193 | OxyzNames = [pfx+'dAx:%d'%(Oatom[0]),pfx+'dAy:%d'%(Oatom[0]),pfx+'dAz:%d'%(Oatom[0])] |
---|
1194 | TxyzNames = [pfx+'dAx:%d'%(Tatom[0]),pfx+'dAy:%d'%(Tatom[0]),pfx+'dAz:%d'%(Tatom[0])] |
---|
1195 | Xvcov = G2mth.getVCov(OxyzNames+TxyzNames,varyList,covMatrix) |
---|
1196 | BsumR = (Radii[Oatom[2]][0]+Radii[Tatom[2]][0])*Factor[0] |
---|
1197 | AsumR = (Radii[Oatom[2]][1]+Radii[Tatom[2]][1])*Factor[1] |
---|
1198 | for [Txyz,Top,Tunit,Spn] in G2spc.GenAtom(Tatom[3:6],SGData,False,Move=False): |
---|
1199 | Dx = (Txyz-np.array(Oatom[3:6]))+Units |
---|
1200 | dx = np.inner(Amat,Dx) |
---|
1201 | dist = ma.masked_less(np.sqrt(np.sum(dx**2,axis=0)),0.5) |
---|
1202 | IndB = ma.nonzero(ma.masked_greater(dist-BsumR,0.)) |
---|
1203 | if np.any(IndB): |
---|
1204 | for indb in IndB: |
---|
1205 | for i in range(len(indb)): |
---|
1206 | if str(dx.T[indb][i]) not in IndBlist: |
---|
1207 | IndBlist.append(str(dx.T[indb][i])) |
---|
1208 | unit = Units[indb][i] |
---|
1209 | tunit = (unit[0]+Tunit[0],unit[1]+Tunit[1],unit[2]+Tunit[2]) |
---|
1210 | sig = 0.0 |
---|
1211 | if len(Xvcov): |
---|
1212 | pdpx = G2mth.getDistDerv(Oatom[3:6],Tatom[3:6],Amat,unit,Top,SGData) |
---|
1213 | sig = np.sqrt(np.inner(pdpx,np.inner(pdpx,Xvcov))) |
---|
1214 | Dist.append([Oatom[0],Tatom[0],tunit,Top,ma.getdata(dist[indb])[i],sig]) |
---|
1215 | if (Dist[-1][-2]-AsumR) <= 0.: |
---|
1216 | Vect.append(dx.T[indb][i]/Dist[-1][-2]) |
---|
1217 | VectA.append([OxyzNames,np.array(Oatom[3:6]),TxyzNames,np.array(Tatom[3:6]),unit,Top]) |
---|
1218 | else: |
---|
1219 | Vect.append([0.,0.,0.]) |
---|
1220 | VectA.append([]) |
---|
1221 | if dlg is not None: |
---|
1222 | dlg.Update(iO,newmsg='Atoms done=%d'%(iO)) |
---|
1223 | for D in Dist: |
---|
1224 | DistArray[Oatom[0]].append(D[1:]) |
---|
1225 | Vect = np.array(Vect) |
---|
1226 | angles = np.zeros((len(Vect),len(Vect))) |
---|
1227 | angsig = np.zeros((len(Vect),len(Vect))) |
---|
1228 | for i,veca in enumerate(Vect): |
---|
1229 | if np.any(veca): |
---|
1230 | for j,vecb in enumerate(Vect): |
---|
1231 | if np.any(vecb): |
---|
1232 | angles[i][j],angsig[i][j] = G2mth.getAngSig(VectA[i],VectA[j],Amat,SGData,covData) |
---|
1233 | if i <= j: continue |
---|
1234 | AngArray[Oatom[0]].append((i,j, |
---|
1235 | G2mth.getAngSig(VectA[i],VectA[j],Amat,SGData,covData))) |
---|
1236 | return AtomLabels,DistArray,AngArray |
---|
1237 | |
---|
1238 | def PrintDistAngle(DisAglCtls,DisAglData,out=sys.stdout): |
---|
1239 | '''Print distances and angles |
---|
1240 | |
---|
1241 | :param dict DisAglCtls: contains distance/angle radii usually defined using |
---|
1242 | :func:`GSASIIctrlGUI.DisAglDialog` |
---|
1243 | :param dict DisAglData: contains phase data: |
---|
1244 | Items 'OrigAtoms' and 'TargAtoms' contain the atoms to be used |
---|
1245 | for distance/angle origins and atoms to be used as targets. |
---|
1246 | Item 'SGData' has the space group information (see :ref:`Space Group object<SGData_table>`) |
---|
1247 | :param file out: file object for output. Defaults to sys.stdout. |
---|
1248 | ''' |
---|
1249 | def MyPrint(s): |
---|
1250 | out.write(s+'\n') |
---|
1251 | # print(s,file=out) # use in Python 3 |
---|
1252 | |
---|
1253 | def ShowBanner(name): |
---|
1254 | MyPrint(80*'*') |
---|
1255 | MyPrint(' Interatomic Distances and Angles for phase '+name) |
---|
1256 | MyPrint((80*'*')+'\n') |
---|
1257 | |
---|
1258 | ShowBanner(DisAglCtls['Name']) |
---|
1259 | SGData = DisAglData['SGData'] |
---|
1260 | SGtext,SGtable = G2spc.SGPrint(SGData) |
---|
1261 | for line in SGtext: MyPrint(line) |
---|
1262 | if len(SGtable) > 1: |
---|
1263 | for i,item in enumerate(SGtable[::2]): |
---|
1264 | if 2*i+1 == len(SGtable): |
---|
1265 | line = ' %s'%(item.ljust(30)) |
---|
1266 | else: |
---|
1267 | line = ' %s %s'%(item.ljust(30),SGtable[2*i+1].ljust(30)) |
---|
1268 | MyPrint(line) |
---|
1269 | else: |
---|
1270 | MyPrint(' ( 1) %s'%(SGtable[0])) #triclinic case |
---|
1271 | Cell = DisAglData['Cell'] |
---|
1272 | |
---|
1273 | Amat,Bmat = G2lat.cell2AB(Cell[:6]) |
---|
1274 | covData = {} |
---|
1275 | if len(DisAglData.get('covData',{})): |
---|
1276 | covData = DisAglData['covData'] |
---|
1277 | pfx = str(DisAglData['pId'])+'::' |
---|
1278 | A = G2lat.cell2A(Cell[:6]) |
---|
1279 | cellSig = G2stIO.getCellEsd(pfx,SGData,A,covData) |
---|
1280 | names = [' a = ',' b = ',' c = ',' alpha = ',' beta = ',' gamma = ',' Volume = '] |
---|
1281 | valEsd = [G2mth.ValEsd(Cell[i],cellSig[i],True) for i in range(7)] |
---|
1282 | line = '\n Unit cell:' |
---|
1283 | for name,vals in zip(names,valEsd): |
---|
1284 | line += name+vals |
---|
1285 | MyPrint(line) |
---|
1286 | else: |
---|
1287 | MyPrint('\n Unit cell: a = '+('%.5f'%Cell[0])+' b = '+('%.5f'%Cell[1])+' c = '+('%.5f'%Cell[2])+ |
---|
1288 | ' alpha = '+('%.3f'%Cell[3])+' beta = '+('%.3f'%Cell[4])+' gamma = '+ |
---|
1289 | ('%.3f'%Cell[5])+' Volume = '+('%.3f'%Cell[6])) |
---|
1290 | |
---|
1291 | AtomLabels,DistArray,AngArray = RetDistAngle(DisAglCtls,DisAglData) |
---|
1292 | origAtoms = DisAglData['OrigAtoms'] |
---|
1293 | for Oatom in origAtoms: |
---|
1294 | i = Oatom[0] |
---|
1295 | Dist = DistArray[i] |
---|
1296 | nDist = len(Dist) |
---|
1297 | angles = np.zeros((nDist,nDist)) |
---|
1298 | angsig = np.zeros((nDist,nDist)) |
---|
1299 | for k,j,tup in AngArray[i]: |
---|
1300 | angles[k][j],angsig[k][j] = angles[j][k],angsig[j][k] = tup |
---|
1301 | line = '' |
---|
1302 | for i,x in enumerate(Oatom[3:6]): |
---|
1303 | line += ('%12.5f'%x).rstrip('0') |
---|
1304 | MyPrint('\n Distances & angles for '+Oatom[1]+' at '+line.rstrip()) |
---|
1305 | MyPrint(80*'*') |
---|
1306 | line = '' |
---|
1307 | for dist in Dist[:-1]: |
---|
1308 | line += '%12s'%(AtomLabels[dist[0]].center(12)) |
---|
1309 | MyPrint(' To cell +(sym. op.) dist. '+line.rstrip()) |
---|
1310 | BVS = {} |
---|
1311 | BVdat = {} |
---|
1312 | Otyp = G2elem.FixValence(Oatom[2]).split('+')[0].split('-')[0] |
---|
1313 | BVox = [BV for BV in atmdata.BVSoxid[Otyp] if '+' in BV] |
---|
1314 | if len(BVox): |
---|
1315 | BVS = {BV:0.0 for BV in BVox} |
---|
1316 | BVdat = {BV:dict(zip(['O','F','Cl'],atmdata.BVScoeff[BV])) for BV in BVox} |
---|
1317 | pvline = 'Bond Valence sums for: ' |
---|
1318 | for i,dist in enumerate(Dist): |
---|
1319 | line = '' |
---|
1320 | for j,angle in enumerate(angles[i][0:i]): |
---|
1321 | sig = angsig[i][j] |
---|
1322 | if angle: |
---|
1323 | if sig: |
---|
1324 | line += '%12s'%(G2mth.ValEsd(angle,sig,True).center(12)) |
---|
1325 | else: |
---|
1326 | val = '%.3f'%(angle) |
---|
1327 | line += '%12s'%(val.center(12)) |
---|
1328 | else: |
---|
1329 | line += 12*' ' |
---|
1330 | if dist[4]: #sig exists! |
---|
1331 | val = G2mth.ValEsd(dist[3],dist[4]) |
---|
1332 | else: |
---|
1333 | val = '%8.4f'%(dist[3]) |
---|
1334 | if len(BVox): |
---|
1335 | Tatm = G2elem.FixValence(DisAglData['TargAtoms'][dist[0]][2]).split('-')[0] |
---|
1336 | if Tatm in ['O','F','Cl']: |
---|
1337 | for BV in BVox: |
---|
1338 | BVS[BV] += np.exp((BVdat[BV][Tatm]-dist[3])/0.37) |
---|
1339 | tunit = '[%2d%2d%2d]'% dist[1] |
---|
1340 | MyPrint((' %8s%10s+(%4d) %12s'%(AtomLabels[dist[0]].ljust(8),tunit.ljust(10),dist[2],val.center(12)))+line.rstrip()) |
---|
1341 | if len(BVox): |
---|
1342 | MyPrint(80*'*') |
---|
1343 | for BV in BVox: |
---|
1344 | pvline += ' %s: %.2f '%(BV,BVS[BV]) |
---|
1345 | MyPrint(pvline) |
---|
1346 | |
---|
1347 | def DisAglTor(DATData): |
---|
1348 | 'Needs a doc string' |
---|
1349 | SGData = DATData['SGData'] |
---|
1350 | Cell = DATData['Cell'] |
---|
1351 | |
---|
1352 | Amat,Bmat = G2lat.cell2AB(Cell[:6]) |
---|
1353 | covData = {} |
---|
1354 | pfx = '' |
---|
1355 | if 'covData' in DATData: |
---|
1356 | covData = DATData['covData'] |
---|
1357 | pfx = str(DATData['pId'])+'::' |
---|
1358 | Datoms = [] |
---|
1359 | Oatoms = [] |
---|
1360 | for i,atom in enumerate(DATData['Datoms']): |
---|
1361 | symop = atom[-1].split('+') |
---|
1362 | if len(symop) == 1: |
---|
1363 | symop.append('0,0,0') |
---|
1364 | symop[0] = int(symop[0]) |
---|
1365 | symop[1] = eval(symop[1]) |
---|
1366 | atom.append(symop) |
---|
1367 | Datoms.append(atom) |
---|
1368 | oatom = DATData['Oatoms'][i] |
---|
1369 | names = ['','',''] |
---|
1370 | if pfx: |
---|
1371 | names = [pfx+'dAx:'+str(oatom[0]),pfx+'dAy:'+str(oatom[0]),pfx+'dAz:'+str(oatom[0])] |
---|
1372 | oatom += [names,] |
---|
1373 | Oatoms.append(oatom) |
---|
1374 | atmSeq = [atom[1]+'('+atom[-2]+')' for atom in Datoms] |
---|
1375 | if DATData['Natoms'] == 4: #torsion |
---|
1376 | Tors,sig = G2mth.GetDATSig(Oatoms,Datoms,Amat,SGData,covData) |
---|
1377 | G2fil.G2Print (' Torsion angle for %s atom sequence: %s = %s'%(DATData['Name'],str(atmSeq).replace("'","")[1:-1],G2mth.ValEsd(Tors,sig))) |
---|
1378 | G2fil.G2Print (' NB: Atom sequence determined by selection order') |
---|
1379 | return # done with torsion |
---|
1380 | elif DATData['Natoms'] == 3: #angle |
---|
1381 | Ang,sig = G2mth.GetDATSig(Oatoms,Datoms,Amat,SGData,covData) |
---|
1382 | G2fil.G2Print (' Angle in %s for atom sequence: %s = %s'%(DATData['Name'],str(atmSeq).replace("'","")[1:-1],G2mth.ValEsd(Ang,sig))) |
---|
1383 | G2fil.G2Print (' NB: Atom sequence determined by selection order') |
---|
1384 | else: #2 atoms - distance |
---|
1385 | Dist,sig = G2mth.GetDATSig(Oatoms,Datoms,Amat,SGData,covData) |
---|
1386 | G2fil.G2Print (' Distance in %s for atom sequence: %s = %s'%(DATData['Name'],str(atmSeq).replace("'","")[1:-1],G2mth.ValEsd(Dist,sig))) |
---|
1387 | |
---|
1388 | def BestPlane(PlaneData): |
---|
1389 | 'Needs a doc string' |
---|
1390 | |
---|
1391 | def ShowBanner(name): |
---|
1392 | G2fil.G2Print (80*'*') |
---|
1393 | G2fil.G2Print (' Best plane result for phase '+name) |
---|
1394 | G2fil.G2Print (80*'*','\n') |
---|
1395 | |
---|
1396 | ShowBanner(PlaneData['Name']) |
---|
1397 | |
---|
1398 | Cell = PlaneData['Cell'] |
---|
1399 | Amat,Bmat = G2lat.cell2AB(Cell[:6]) |
---|
1400 | Atoms = PlaneData['Atoms'] |
---|
1401 | sumXYZ = np.zeros(3) |
---|
1402 | XYZ = [] |
---|
1403 | Natoms = len(Atoms) |
---|
1404 | for atom in Atoms: |
---|
1405 | xyz = np.array(atom[3:6]) |
---|
1406 | XYZ.append(xyz) |
---|
1407 | sumXYZ += xyz |
---|
1408 | sumXYZ /= Natoms |
---|
1409 | XYZ = np.array(XYZ)-sumXYZ |
---|
1410 | XYZ = np.inner(Amat,XYZ).T |
---|
1411 | Zmat = np.zeros((3,3)) |
---|
1412 | for i,xyz in enumerate(XYZ): |
---|
1413 | Zmat += np.outer(xyz.T,xyz) |
---|
1414 | G2fil.G2Print (' Selected atoms centered at %10.5f %10.5f %10.5f'%(sumXYZ[0],sumXYZ[1],sumXYZ[2])) |
---|
1415 | Evec,Emat = nl.eig(Zmat) |
---|
1416 | Evec = np.sqrt(Evec)/(Natoms-3) |
---|
1417 | Order = np.argsort(Evec) |
---|
1418 | XYZ = np.inner(XYZ,Emat.T).T |
---|
1419 | XYZ = np.array([XYZ[Order[2]],XYZ[Order[1]],XYZ[Order[0]]]).T |
---|
1420 | G2fil.G2Print (' Atoms in Cartesian best plane coordinates:') |
---|
1421 | G2fil.G2Print (' Name X Y Z') |
---|
1422 | for i,xyz in enumerate(XYZ): |
---|
1423 | G2fil.G2Print (' %6s%10.3f%10.3f%10.3f'%(Atoms[i][1].ljust(6),xyz[0],xyz[1],xyz[2])) |
---|
1424 | G2fil.G2Print ('\n Best plane RMS X =%8.3f, Y =%8.3f, Z =%8.3f'%(Evec[Order[2]],Evec[Order[1]],Evec[Order[0]])) |
---|
1425 | |
---|
1426 | def do_refine(*args): |
---|
1427 | 'Called to run a refinement when this module is executed ' |
---|
1428 | starttime = time.time() |
---|
1429 | #arg = sys.argv |
---|
1430 | if len(args) >= 1: |
---|
1431 | files = args |
---|
1432 | elif len(sys.argv) > 1: |
---|
1433 | files = sys.argv[1:] |
---|
1434 | else: |
---|
1435 | G2fil.G2Print ('ERROR GSASIIstrMain.do_refine error - missing filename') |
---|
1436 | G2fil.G2Print ('Use "python GSASIIstrMain.py f1.gpx [f2.gpx f3.gpx...]" to run') |
---|
1437 | G2fil.G2Print ('or call GSASIIstrMain.do_refine directly') |
---|
1438 | sys.exit() |
---|
1439 | for GPXfile in files: |
---|
1440 | if not ospath.exists(GPXfile): |
---|
1441 | G2fil.G2Print ('ERROR - '+GPXfile+" doesn't exist! Skipping.") |
---|
1442 | continue |
---|
1443 | # TODO: test below |
---|
1444 | # figure out if this is a sequential refinement and call SeqRefine(GPXfile,None) |
---|
1445 | #Controls = G2stIO.GetControls(GPXfile) |
---|
1446 | #if Controls.get('Seq Data',[]): |
---|
1447 | Refine(GPXfile,None) |
---|
1448 | #else: |
---|
1449 | # SeqRefine(GPXfile,None) |
---|
1450 | G2fil.G2Print("Done with {}.\nExecution time {:.2f} sec.".format(GPXfile,time.time()-starttime)) |
---|
1451 | |
---|
1452 | if __name__ == '__main__': |
---|
1453 | GSASIIpath.InvokeDebugOpts() |
---|
1454 | do_refine() |
---|