source: trunk/GSASIImath.py @ 4542

Last change on this file since 4542 was 4542, checked in by vondreele, 3 years ago

revert PeaksUniqure? back to version <#4473 so it properly does sorted map peaks

  • Property svn:eol-style set to native
  • Property svn:keywords set to Date Author Revision URL Id
File size: 196.2 KB
Line 
1# -*- coding: utf-8 -*-
2#GSASIImath - major mathematics routines
3########### SVN repository information ###################
4# $Date: 2020-08-14 22:24:27 +0000 (Fri, 14 Aug 2020) $
5# $Author: vondreele $
6# $Revision: 4542 $
7# $URL: trunk/GSASIImath.py $
8# $Id: GSASIImath.py 4542 2020-08-14 22:24:27Z vondreele $
9########### SVN repository information ###################
10'''
11*GSASIImath: computation module*
12================================
13
14Routines for least-squares minimization and other stuff
15
16'''
17from __future__ import division, print_function
18import random as rn
19import numpy as np
20import numpy.linalg as nl
21import numpy.ma as ma
22import time
23import math
24import copy
25import GSASIIpath
26GSASIIpath.SetVersionNumber("$Revision: 4542 $")
27import GSASIIElem as G2el
28import GSASIIlattice as G2lat
29import GSASIIspc as G2spc
30import GSASIIpwd as G2pwd
31import GSASIIfiles as G2fil
32import numpy.fft as fft
33import scipy.optimize as so
34try:
35    import pypowder as pwd
36except ImportError:
37    print ('pypowder is not available - profile calcs. not allowed')
38
39sind = lambda x: np.sin(x*np.pi/180.)
40cosd = lambda x: np.cos(x*np.pi/180.)
41tand = lambda x: np.tan(x*np.pi/180.)
42asind = lambda x: 180.*np.arcsin(x)/np.pi
43acosd = lambda x: 180.*np.arccos(x)/np.pi
44atand = lambda x: 180.*np.arctan(x)/np.pi
45atan2d = lambda y,x: 180.*np.arctan2(y,x)/np.pi
46try:  # fails on doc build
47    twopi = 2.0*np.pi
48    twopisq = 2.0*np.pi**2
49    _double_min = np.finfo(float).min
50    _double_max = np.finfo(float).max
51except TypeError:
52    pass
53nxs = np.newaxis
54   
55################################################################################
56##### Hessian least-squares Levenberg-Marquardt routine
57################################################################################
58
59def pinv(a, rcond=1e-15 ):
60    """
61    Compute the (Moore-Penrose) pseudo-inverse of a matrix.
62    Modified from numpy.linalg.pinv; assumes a is Hessian & returns no. zeros found
63    Calculate the generalized inverse of a matrix using its
64    singular-value decomposition (SVD) and including all
65    *large* singular values.
66
67    :param array a: (M, M) array_like - here assumed to be LS Hessian
68      Matrix to be pseudo-inverted.
69    :param float rcond: Cutoff for small singular values.
70      Singular values smaller (in modulus) than
71      `rcond` * largest_singular_value (again, in modulus)
72      are set to zero.
73
74    :returns: B : (M, M) ndarray
75      The pseudo-inverse of `a`
76
77    Raises: LinAlgError
78      If the SVD computation does not converge.
79
80    Notes:
81      The pseudo-inverse of a matrix A, denoted :math:`A^+`, is
82      defined as: "the matrix that 'solves' [the least-squares problem]
83      :math:`Ax = b`," i.e., if :math:`\\bar{x}` is said solution, then
84      :math:`A^+` is that matrix such that :math:`\\bar{x} = A^+b`.
85
86    It can be shown that if :math:`Q_1 \\Sigma Q_2^T = A` is the singular
87    value decomposition of A, then
88    :math:`A^+ = Q_2 \\Sigma^+ Q_1^T`, where :math:`Q_{1,2}` are
89    orthogonal matrices, :math:`\\Sigma` is a diagonal matrix consisting
90    of A's so-called singular values, (followed, typically, by
91    zeros), and then :math:`\\Sigma^+` is simply the diagonal matrix
92    consisting of the reciprocals of A's singular values
93    (again, followed by zeros). [1]
94
95    References:
96    .. [1] G. Strang, *Linear Algebra and Its Applications*, 2nd Ed., Orlando, FL, Academic Press, Inc., 1980, pp. 139-142.
97
98    """
99    u, s, vt = nl.svd(a)
100    cutoff = rcond*np.maximum.reduce(s)
101    s = np.where(s>cutoff,1./s,0.)
102    nzero = s.shape[0]-np.count_nonzero(s)
103#    res = np.dot(np.transpose(vt), np.multiply(s[:, np.newaxis], np.transpose(u)))
104    res = np.dot(vt.T,s[:,nxs]*u.T)
105    return res,nzero
106
107def HessianLSQ(func,x0,Hess,args=(),ftol=1.49012e-8,xtol=1.e-6, maxcyc=0,lamda=-3,Print=False,refPlotUpdate=None):
108    """
109    Minimize the sum of squares of a function (:math:`f`) evaluated on a series of
110    values (y): :math:`\sum_{y=0}^{N_{obs}} f(y,{args})`   
111    where :math:`x = arg min(\sum_{y=0}^{N_{obs}} (func(y)^2,axis=0))`
112
113    :param function func: callable method or function
114        should take at least one (possibly length N vector) argument and
115        returns M floating point numbers.
116    :param np.ndarray x0: The starting estimate for the minimization of length N
117    :param function Hess: callable method or function
118        A required function or method to compute the weighted vector and Hessian for func.
119        It must be a symmetric NxN array
120    :param tuple args: Any extra arguments to func are placed in this tuple.
121    :param float ftol: Relative error desired in the sum of squares.
122    :param float xtol: Relative tolerance of zeros in the SVD solution in nl.pinv.
123    :param int maxcyc: The maximum number of cycles of refinement to execute, if -1 refine
124        until other limits are met (ftol, xtol)
125    :param int lamda: initial Marquardt lambda=10**lamda
126    :param bool Print: True for printing results (residuals & times) by cycle
127
128    :returns: (x,cov_x,infodict) where
129
130      * x : ndarray
131        The solution (or the result of the last iteration for an unsuccessful
132        call).
133      * cov_x : ndarray
134        Uses the fjac and ipvt optional outputs to construct an
135        estimate of the jacobian around the solution.  ``None`` if a
136        singular matrix encountered (indicates very flat curvature in
137        some direction).  This matrix must be multiplied by the
138        residual standard deviation to get the covariance of the
139        parameter estimates -- see curve_fit.
140      * infodict : dict
141        a dictionary of optional outputs with the keys:
142
143         * 'fvec' : the function evaluated at the output
144         * 'num cyc':
145         * 'nfev':
146         * 'lamMax':
147         * 'psing':
148         * 'SVD0':
149           
150    """
151               
152    ifConverged = False
153    deltaChi2 = -10.
154    x0 = np.array(x0, ndmin=1)      #might be redundant?
155    n = len(x0)
156    if type(args) != type(()):
157        args = (args,)
158       
159    icycle = 0
160    One = np.ones((n,n))
161    lam = 10.**lamda
162    lamMax = lam
163    nfev = 0
164    if Print:
165        G2fil.G2Print(' Hessian Levenberg-Marquardt SVD refinement on %d variables:'%(n))
166    Lam = np.zeros((n,n))
167    Xvec = np.zeros(len(x0))
168    while icycle < maxcyc:
169        time0 = time.time()
170        M = func(x0,*args)
171        Nobs = len(M)
172        nfev += 1
173        chisq0 = np.sum(M**2)
174        Yvec,Amat = Hess(x0,*args)
175        Adiag = np.sqrt(np.diag(Amat))
176        psing = np.where(np.abs(Adiag) < 1.e-14,True,False)
177        if np.any(psing):                #hard singularity in matrix
178            return [x0,None,{'num cyc':icycle,'fvec':M,'nfev':nfev,'lamMax':lamMax,'psing':psing,'SVD0':-1}]
179        Anorm = np.outer(Adiag,Adiag)
180        Yvec /= Adiag
181        Amat /= Anorm
182        if Print:
183            G2fil.G2Print('initial chi^2 %.5g on %d obs.'%(chisq0,Nobs))
184        chitol = ftol
185        while True:
186            Lam = np.eye(Amat.shape[0])*lam
187            Amatlam = Amat*(One+Lam)
188            try:
189                Ainv,Nzeros = pinv(Amatlam,xtol)    #do Moore-Penrose inversion (via SVD)
190            except nl.LinAlgError:
191                psing = list(np.where(np.diag(nl.qr(Amatlam)[1]) < 1.e-14)[0])
192                G2fil.G2Print('ouch #1 bad SVD inversion; change parameterization for ',psing, mode='error')
193                return [x0,None,{'num cyc':icycle,'fvec':M,'nfev':nfev,'lamMax':lamMax,'psing':psing,'SVD0':-1}]
194            Xvec = np.inner(Ainv,Yvec)      #solve
195            Xvec /= Adiag
196            M2 = func(x0+Xvec,*args)
197            nfev += 1
198            chisq1 = np.sum(M2**2)
199            if chisq1 > chisq0*(1.+chitol):     #TODO put Alan Coehlo's criteria for lambda here?
200                lam *= 10.
201                if Print:
202                    G2fil.G2Print('new chi^2 %.5g on %d obs., %d SVD zeros ; matrix modification needed; lambda now %.1e'  \
203                           %(chisq1,Nobs,Nzeros,lam))
204            else:
205                x0 += Xvec
206                lam /= 10.
207                break
208            if lam > 10.:
209                G2fil.G2Print('ouch #3 chisq1 %g.4 stuck > chisq0 %g.4'%(chisq1,chisq0), mode='warn')
210                break
211            chitol *= 2
212        lamMax = max(lamMax,lam)
213        deltaChi2 = (chisq0-chisq1)/chisq0
214        if Print:
215            G2fil.G2Print(' Cycle: %d, Time: %.2fs, Chi**2: %.5g for %d obs., Lambda: %.3g,  Delta: %.3g'%(
216                icycle,time.time()-time0,chisq1,Nobs,lamMax,deltaChi2))
217        Histograms = args[0][0]
218        if refPlotUpdate is not None: refPlotUpdate(Histograms,icycle)   # update plot
219        if deltaChi2 < ftol:
220            ifConverged = True
221            if Print: G2fil.G2Print("converged")
222            break
223        icycle += 1
224    M = func(x0,*args)
225    nfev += 1
226    Yvec,Amat = Hess(x0,*args)
227    Adiag = np.sqrt(np.diag(Amat))
228    Anorm = np.outer(Adiag,Adiag)
229    Lam = np.eye(Amat.shape[0])*lam
230    Amatlam = Amat/Anorm       
231    try:
232        Bmat,Nzero = pinv(Amatlam,xtol)    #Moore-Penrose inversion (via SVD) & count of zeros
233        if Print: G2fil.G2Print('Found %d SVD zeros'%(Nzero), mode='warn')
234        Bmat = Bmat/Anorm
235        return [x0,Bmat,{'num cyc':icycle,'fvec':M,'nfev':nfev,'lamMax':lamMax,'psing':[],
236            'SVD0':Nzero,'Converged': ifConverged, 'DelChi2':deltaChi2,'Xvec':Xvec}]
237    except nl.LinAlgError:
238        G2fil.G2Print('ouch #2 linear algebra error in making v-cov matrix', mode='error')
239        psing = []
240        if maxcyc:
241            psing = list(np.where(np.diag(nl.qr(Amat)[1]) < 1.e-14)[0])
242        return [x0,None,{'num cyc':icycle,'fvec':M,'nfev':nfev,'lamMax':lamMax,'psing':psing,'SVD0':-1,'Xvec':None}]         
243           
244def HessianSVD(func,x0,Hess,args=(),ftol=1.49012e-8,xtol=1.e-6, maxcyc=0,lamda=-3,Print=False,refPlotUpdate=None):
245   
246    """
247    Minimize the sum of squares of a function (:math:`f`) evaluated on a series of
248    values (y): :math:`\sum_{y=0}^{N_{obs}} f(y,{args})`   
249    where :math:`x = arg min(\sum_{y=0}^{N_{obs}} (func(y)^2,axis=0))`
250
251    :param function func: callable method or function
252        should take at least one (possibly length N vector) argument and
253        returns M floating point numbers.
254    :param np.ndarray x0: The starting estimate for the minimization of length N
255    :param function Hess: callable method or function
256        A required function or method to compute the weighted vector and Hessian for func.
257        It must be a symmetric NxN array
258    :param tuple args: Any extra arguments to func are placed in this tuple.
259    :param float ftol: Relative error desired in the sum of squares.
260    :param float xtol: Relative tolerance of zeros in the SVD solution in nl.pinv.
261    :param int maxcyc: The maximum number of cycles of refinement to execute, if -1 refine
262        until other limits are met (ftol, xtol)
263    :param bool Print: True for printing results (residuals & times) by cycle
264
265    :returns: (x,cov_x,infodict) where
266
267      * x : ndarray
268        The solution (or the result of the last iteration for an unsuccessful
269        call).
270      * cov_x : ndarray
271        Uses the fjac and ipvt optional outputs to construct an
272        estimate of the jacobian around the solution.  ``None`` if a
273        singular matrix encountered (indicates very flat curvature in
274        some direction).  This matrix must be multiplied by the
275        residual standard deviation to get the covariance of the
276        parameter estimates -- see curve_fit.
277      * infodict : dict
278        a dictionary of optional outputs with the keys:
279
280         * 'fvec' : the function evaluated at the output
281         * 'num cyc':
282         * 'nfev':
283         * 'lamMax':0.
284         * 'psing':
285         * 'SVD0':
286           
287    """
288               
289    ifConverged = False
290    deltaChi2 = -10.
291    x0 = np.array(x0, ndmin=1)      #might be redundant?
292    n = len(x0)
293    if type(args) != type(()):
294        args = (args,)
295       
296    icycle = 0
297    nfev = 0
298    if Print:
299        G2fil.G2Print(' Hessian SVD refinement on %d variables:'%(n))
300    while icycle < maxcyc:
301        time0 = time.time()
302        M = func(x0,*args)
303        nfev += 1
304        chisq0 = np.sum(M**2)
305        Yvec,Amat = Hess(x0,*args)
306        Adiag = np.sqrt(np.diag(Amat))
307        psing = np.where(np.abs(Adiag) < 1.e-14,True,False)
308        if np.any(psing):                #hard singularity in matrix
309            return [x0,None,{'num cyc':icycle,'fvec':M,'nfev':nfev,'lamMax':0.,'psing':psing,'SVD0':-1}]
310        Anorm = np.outer(Adiag,Adiag)
311        Yvec /= Adiag
312        Amat /= Anorm
313        if Print:
314            G2fil.G2Print('initial chi^2 %.5g'%(chisq0))
315        try:
316            Ainv,Nzeros = pinv(Amat,xtol)    #do Moore-Penrose inversion (via SVD)
317        except nl.LinAlgError:
318            G2fil.G2Print('ouch #1 bad SVD inversion; change parameterization', mode='warn')
319            psing = list(np.where(np.diag(nl.qr(Amat)[1]) < 1.e-14)[0])
320            return [x0,None,{'num cyc':icycle,'fvec':M,'nfev':nfev,'lamMax':0.,'psing':psing,'SVD0':-1}]
321        Xvec = np.inner(Ainv,Yvec)      #solve
322        Xvec /= Adiag
323        M2 = func(x0+Xvec,*args)
324        nfev += 1
325        chisq1 = np.sum(M2**2)
326        deltaChi2 = (chisq0-chisq1)/chisq0
327        if Print:
328            G2fil.G2Print(' Cycle: %d, Time: %.2fs, Chi**2: %.5g, Delta: %.3g'%(
329                icycle,time.time()-time0,chisq1,deltaChi2))
330        Histograms = args[0][0]
331        if refPlotUpdate is not None: refPlotUpdate(Histograms,icycle)   # update plot
332        if deltaChi2 < ftol:
333            ifConverged = True
334            if Print: G2fil.G2Print("converged")
335            break
336        icycle += 1
337    M = func(x0,*args)
338    nfev += 1
339    Yvec,Amat = Hess(x0,*args)
340    Adiag = np.sqrt(np.diag(Amat))
341    Anorm = np.outer(Adiag,Adiag)
342    Amat = Amat/Anorm       
343    try:
344        Bmat,Nzero = pinv(Amat,xtol)    #Moore-Penrose inversion (via SVD) & count of zeros
345        G2fil.G2Print('Found %d SVD zeros'%(Nzero), mode='warn')
346#        Bmat = nl.inv(Amatlam); Nzeros = 0
347        Bmat = Bmat/Anorm
348        return [x0,Bmat,{'num cyc':icycle,'fvec':M,'nfev':nfev,'lamMax':0.,'psing':[],
349            'SVD0':Nzero,'Converged': ifConverged, 'DelChi2':deltaChi2}]
350    except nl.LinAlgError:
351        G2fil.G2Print('ouch #2 linear algebra error in making v-cov matrix', mode='error')
352        psing = []
353        if maxcyc:
354            psing = list(np.where(np.diag(nl.qr(Amat)[1]) < 1.e-14)[0])
355        return [x0,None,{'num cyc':icycle,'fvec':M,'nfev':nfev,'lamMax':0.,'psing':psing,'SVD0':-1}]         
356           
357def getVCov(varyNames,varyList,covMatrix):
358    '''obtain variance-covariance terms for a set of variables. NB: the varyList
359    and covMatrix were saved by the last least squares refinement so they must match.
360   
361    :param list varyNames: variable names to find v-cov matric for
362    :param list varyList: full list of all variables in v-cov matrix
363    :param nparray covMatrix: full variance-covariance matrix from the last
364     least squares refinement
365   
366    :returns: nparray vcov: variance-covariance matrix for the variables given
367     in varyNames
368   
369    '''
370    vcov = np.zeros((len(varyNames),len(varyNames)))
371    for i1,name1 in enumerate(varyNames):
372        for i2,name2 in enumerate(varyNames):
373            try:
374                vcov[i1][i2] = covMatrix[varyList.index(name1)][varyList.index(name2)]
375            except ValueError:
376                vcov[i1][i2] = 0.0
377#                if i1 == i2:
378#                    vcov[i1][i2] = 1e-20
379#                else:
380#                    vcov[i1][i2] = 0.0
381    return vcov
382   
383################################################################################
384##### Atom manipulations
385################################################################################
386
387def FindMolecule(ind,generalData,atomData):                    #uses numpy & masks - very fast even for proteins!
388
389    def getNeighbors(atom,radius):
390        Dx = UAtoms-np.array(atom[cx:cx+3])
391        dist = ma.masked_less(np.sqrt(np.sum(np.inner(Amat,Dx)**2,axis=0)),0.5) #gets rid of disorder "bonds" < 0.5A
392        sumR = Radii+radius
393        return set(ma.nonzero(ma.masked_greater(dist-factor*sumR,0.))[0])                #get indices of bonded atoms
394
395    import numpy.ma as ma
396    indices = (-1,0,1)
397    Units = np.array([[h,k,l] for h in indices for k in indices for l in indices],dtype='f')
398    cx,ct,cs,ci = generalData['AtomPtrs']
399    DisAglCtls = generalData['DisAglCtls']
400    SGData = generalData['SGData']
401    Amat,Bmat = G2lat.cell2AB(generalData['Cell'][1:7])
402    radii = DisAglCtls['BondRadii']
403    atomTypes = DisAglCtls['AtomTypes']
404    factor = DisAglCtls['Factors'][0]
405    unit = np.zeros(3)
406    try:
407        indH = atomTypes.index('H')
408        radii[indH] = 0.5
409    except:
410        pass           
411    nAtom = len(atomData)
412    Indx = list(range(nAtom))
413    UAtoms = []
414    Radii = []
415    for atom in atomData:
416        UAtoms.append(np.array(atom[cx:cx+3]))
417        Radii.append(radii[atomTypes.index(atom[ct])])
418    UAtoms = np.array(UAtoms)
419    Radii = np.array(Radii)
420    for nOp,Op in enumerate(SGData['SGOps'][1:]):
421        UAtoms = np.concatenate((UAtoms,(np.inner(Op[0],UAtoms[:nAtom]).T+Op[1])))
422        Radii = np.concatenate((Radii,Radii[:nAtom]))
423        Indx += Indx[:nAtom]
424    for icen,cen in enumerate(SGData['SGCen'][1:]):
425        UAtoms = np.concatenate((UAtoms,(UAtoms+cen)))
426        Radii = np.concatenate((Radii,Radii))
427        Indx += Indx[:nAtom]
428    if SGData['SGInv']:
429        UAtoms = np.concatenate((UAtoms,-UAtoms))
430        Radii = np.concatenate((Radii,Radii))
431        Indx += Indx
432    UAtoms %= 1.
433    mAtoms = len(UAtoms)
434    for unit in Units:
435        if np.any(unit):    #skip origin cell
436            UAtoms = np.concatenate((UAtoms,UAtoms[:mAtoms]+unit))
437            Radii = np.concatenate((Radii,Radii[:mAtoms]))
438            Indx += Indx[:mAtoms]
439    UAtoms = np.array(UAtoms)
440    Radii = np.array(Radii)
441    newAtoms = [atomData[ind],]
442    atomData[ind] = None
443    radius = Radii[ind]
444    IndB = getNeighbors(newAtoms[-1],radius)
445    while True:
446        if not len(IndB):
447            break
448        indb = IndB.pop()
449        if atomData[Indx[indb]] == None:
450            continue
451        while True:
452            try:
453                jndb = IndB.index(indb)
454                IndB.remove(jndb)
455            except:
456                break
457        newAtom = copy.copy(atomData[Indx[indb]])
458        newAtom[cx:cx+3] = UAtoms[indb]     #NB: thermal Uij, etc. not transformed!
459        newAtoms.append(newAtom)
460        atomData[Indx[indb]] = None
461        IndB = set(list(IndB)+list(getNeighbors(newAtoms[-1],radius)))
462        if len(IndB) > nAtom:
463            return 'Assemble molecule cannot be used on extended structures'
464    for atom in atomData:
465        if atom != None:
466            newAtoms.append(atom)
467    return newAtoms
468       
469def FindAtomIndexByIDs(atomData,loc,IDs,Draw=True):
470    '''finds the set of atom array indices for a list of atom IDs. Will search
471    either the Atom table or the drawAtom table.
472   
473    :param list atomData: Atom or drawAtom table containting coordinates, etc.
474    :param int loc: location of atom id in atomData record
475    :param list IDs: atom IDs to be found
476    :param bool Draw: True if drawAtom table to be searched; False if Atom table
477      is searched
478   
479    :returns: list indx: atom (or drawAtom) indices
480   
481    '''
482    indx = []
483    for i,atom in enumerate(atomData):
484        if Draw and atom[loc] in IDs:
485            indx.append(i)
486        elif atom[loc] in IDs:
487            indx.append(i)
488    return indx
489
490def FillAtomLookUp(atomData,indx):
491    '''create a dictionary of atom indexes with atom IDs as keys
492   
493    :param list atomData: Atom table to be used
494    :param int  indx: pointer to position of atom id in atom record (typically cia+8)
495   
496    :returns: dict atomLookUp: dictionary of atom indexes with atom IDs as keys
497   
498    '''
499    return {atom[indx]:iatm for iatm,atom in enumerate(atomData)}
500
501def GetAtomsById(atomData,atomLookUp,IdList):
502    '''gets a list of atoms from Atom table that match a set of atom IDs
503   
504    :param list atomData: Atom table to be used
505    :param dict atomLookUp: dictionary of atom indexes with atom IDs as keys
506    :param list IdList: atom IDs to be found
507   
508    :returns: list atoms: list of atoms found
509   
510    '''
511    atoms = []
512    for Id in IdList:
513        atoms.append(atomData[atomLookUp[Id]])
514    return atoms
515   
516def GetAtomItemsById(atomData,atomLookUp,IdList,itemLoc,numItems=1):
517    '''gets atom parameters for atoms using atom IDs
518   
519    :param list atomData: Atom table to be used
520    :param dict atomLookUp: dictionary of atom indexes with atom IDs as keys
521    :param list IdList: atom IDs to be found
522    :param int itemLoc: pointer to desired 1st item in an atom table entry
523    :param int numItems: number of items to be retrieved
524   
525    :returns: type name: description
526   
527    '''
528    Items = []
529    if not isinstance(IdList,list):
530        IdList = [IdList,]
531    for Id in IdList:
532        if numItems == 1:
533            Items.append(atomData[atomLookUp[Id]][itemLoc])
534        else:
535            Items.append(atomData[atomLookUp[Id]][itemLoc:itemLoc+numItems])
536    return Items
537   
538def GetAtomCoordsByID(pId,parmDict,AtLookup,indx):
539    '''default doc string
540   
541    :param type name: description
542   
543    :returns: type name: description
544   
545    '''
546    pfx = [str(pId)+'::A'+i+':' for i in ['x','y','z']]
547    dpfx = [str(pId)+'::dA'+i+':' for i in ['x','y','z']]
548    XYZ = []
549    for ind in indx:
550        names = [pfx[i]+str(AtLookup[ind]) for i in range(3)]
551        dnames = [dpfx[i]+str(AtLookup[ind]) for i in range(3)]
552        XYZ.append([parmDict[name]+parmDict[dname] for name,dname in zip(names,dnames)])
553    return XYZ
554   
555def GetAtomFracByID(pId,parmDict,AtLookup,indx):
556    '''default doc string
557   
558    :param type name: description
559   
560    :returns: type name: description
561   
562    '''
563    pfx = str(pId)+'::Afrac:'
564    Frac = []
565    for ind in indx:
566        name = pfx+str(AtLookup[ind])
567        Frac.append(parmDict[name])
568    return Frac
569   
570#    for Atom in Atoms:
571#        XYZ = Atom[cx:cx+3]
572#        if 'A' in Atom[cia]:
573#            U6 = Atom[cia+2:cia+8]
574   
575
576def ApplySeqData(data,seqData):
577    '''Applies result from seq. refinement to drawing atom positions & Uijs
578    '''
579    generalData = data['General']
580    SGData = generalData['SGData']
581    cx,ct,cs,cia = generalData['AtomPtrs']
582    drawingData = data['Drawing']
583    dcx,dct,dcs,dci = drawingData['atomPtrs']
584    atoms = data['Atoms']
585    drawAtoms = drawingData['Atoms']
586    pId = data['pId']
587    pfx = '%d::'%(pId)
588    parmDict = seqData['parmDict']
589    for ia,atom in enumerate(atoms):
590        dxyz = np.array([parmDict[pfx+'dAx:'+str(ia)],parmDict[pfx+'dAy:'+str(ia)],parmDict[pfx+'dAz:'+str(ia)]])
591        if atom[cia] == 'A':
592            atuij = np.array([parmDict[pfx+'AU11:'+str(ia)],parmDict[pfx+'AU22:'+str(ia)],parmDict[pfx+'AU33:'+str(ia)],
593                parmDict[pfx+'AU12:'+str(ia)],parmDict[pfx+'AU13:'+str(ia)],parmDict[pfx+'AU23:'+str(ia)]])
594        else:
595            atuiso = parmDict[pfx+'AUiso:'+str(ia)]
596        atxyz = G2spc.MoveToUnitCell(np.array(atom[cx:cx+3])+dxyz)[0]
597        indx = FindAtomIndexByIDs(drawAtoms,dci,[atom[cia+8],],True)
598        for ind in indx:
599            drawatom = drawAtoms[ind]
600            opr = drawatom[dcs-1]
601            #how do I handle Sfrac? - fade the atoms?
602            if atom[cia] == 'A':                   
603                X,U = G2spc.ApplyStringOps(opr,SGData,atxyz,atuij)
604                drawatom[dcx:dcx+3] = X
605                drawatom[dci-6:dci] = U
606            else:
607                X = G2spc.ApplyStringOps(opr,SGData,atxyz)
608                drawatom[dcx:dcx+3] = X
609                drawatom[dci-7] = atuiso
610    return drawAtoms
611   
612def FindNeighbors(phase,FrstName,AtNames,notName=''):
613    General = phase['General']
614    cx,ct,cs,cia = General['AtomPtrs']
615    Atoms = phase['Atoms']
616    atNames = [atom[ct-1] for atom in Atoms]
617    Cell = General['Cell'][1:7]
618    Amat,Bmat = G2lat.cell2AB(Cell)
619    atTypes = General['AtomTypes']
620    Radii = np.array(General['BondRadii'])
621    try:
622        DisAglCtls = General['DisAglCtls']   
623        radiusFactor = DisAglCtls['Factors'][0]
624    except:
625        radiusFactor = 0.85
626    AtInfo = dict(zip(atTypes,Radii)) #or General['BondRadii']
627    Orig = atNames.index(FrstName)
628    OId = Atoms[Orig][cia+8]
629    OType = Atoms[Orig][ct]
630    XYZ = getAtomXYZ(Atoms,cx)       
631    Neigh = []
632    Ids = []
633    Dx = np.inner(Amat,XYZ-XYZ[Orig]).T
634    dist = np.sqrt(np.sum(Dx**2,axis=1))
635    sumR = np.array([AtInfo[OType]+AtInfo[atom[ct]] for atom in Atoms])
636    IndB = ma.nonzero(ma.masked_greater(dist-radiusFactor*sumR,0.))
637    for j in IndB[0]:
638        if j != Orig:
639            if AtNames[j] not in notName:
640                Neigh.append([AtNames[j],dist[j],True])
641                Ids.append(Atoms[j][cia+8])
642    return Neigh,[OId,Ids]
643
644def FindOctahedron(results):
645    Octahedron = np.array([[1.,0,0],[0,1.,0],[0,0,1.],[-1.,0,0],[0,-1.,0],[0,0,-1.]])
646    Polygon = np.array([result[3] for result in results])
647    Dists = np.array([np.sqrt(np.sum(axis**2)) for axis in Polygon])
648    bond = np.mean(Dists)
649    std = np.std(Dists)
650    Norms = Polygon/Dists[:,nxs]
651    Tilts = acosd(np.dot(Norms,Octahedron[0]))
652    iAng = np.argmin(Tilts)
653    Qavec = np.cross(Norms[iAng],Octahedron[0])
654    QA = AVdeg2Q(Tilts[iAng],Qavec)
655    newNorms = prodQVQ(QA,Norms)
656    Rots = acosd(np.dot(newNorms,Octahedron[1]))
657    jAng = np.argmin(Rots)
658    Qbvec = np.cross(Norms[jAng],Octahedron[1])
659    QB = AVdeg2Q(Rots[jAng],Qbvec)
660    QQ = prodQQ(QA,QB)   
661    newNorms = prodQVQ(QQ,Norms)
662    dispVecs = np.array([norm[:,nxs]-Octahedron.T for norm in newNorms])
663    disp = np.sqrt(np.sum(dispVecs**2,axis=1))
664    dispids = np.argmin(disp,axis=1)
665    vecDisp = np.array([dispVecs[i,:,dispid] for i,dispid in enumerate(dispids)])
666    Disps = np.array([disp[i,dispid] for i,dispid in enumerate(dispids)])
667    meanDisp = np.mean(Disps)
668    stdDisp = np.std(Disps)
669    A,V = Q2AVdeg(QQ)
670    return bond,std,meanDisp,stdDisp,A,V,vecDisp
671   
672def FindTetrahedron(results):
673    Tetrahedron = np.array([[1.,1,1],[1,-1,-1],[-1,1,-1],[-1,-1,1]])/np.sqrt(3.)
674    Polygon = np.array([result[3] for result in results])
675    Dists = np.array([np.sqrt(np.sum(axis**2)) for axis in Polygon])
676    bond = np.mean(Dists)
677    std = np.std(Dists)
678    Norms = Polygon/Dists[:,nxs]
679    Tilts = acosd(np.dot(Norms,Tetrahedron[0]))
680    iAng = np.argmin(Tilts)
681    Qavec = np.cross(Norms[iAng],Tetrahedron[0])
682    QA = AVdeg2Q(Tilts[iAng],Qavec)
683    newNorms = prodQVQ(QA,Norms)
684    Rots = acosd(np.dot(newNorms,Tetrahedron[1]))
685    jAng = np.argmin(Rots)
686    Qbvec = np.cross(Norms[jAng],Tetrahedron[1])
687    QB = AVdeg2Q(Rots[jAng],Qbvec)
688    QQ = prodQQ(QA,QB)   
689    newNorms = prodQVQ(QQ,Norms)
690    dispVecs = np.array([norm[:,nxs]-Tetrahedron.T for norm in newNorms])
691    disp = np.sqrt(np.sum(dispVecs**2,axis=1))
692    dispids = np.argmin(disp,axis=1)
693    vecDisp = np.array([dispVecs[i,:,dispid] for i,dispid in enumerate(dispids)])
694    Disps = np.array([disp[i,dispid] for i,dispid in enumerate(dispids)])
695    meanDisp = np.mean(Disps)
696    stdDisp = np.std(Disps)
697    A,V = Q2AVdeg(QQ)
698    return bond,std,meanDisp,stdDisp,A,V,vecDisp
699   
700def FindAllNeighbors(phase,FrstName,AtNames,notName='',Orig=None,Short=False):
701    General = phase['General']
702    cx,ct,cs,cia = General['AtomPtrs']
703    Atoms = phase['Atoms']
704    atNames = [atom[ct-1] for atom in Atoms]
705    atTypes = [atom[ct] for atom in Atoms]
706    Cell = General['Cell'][1:7]
707    Amat,Bmat = G2lat.cell2AB(Cell)
708    SGData = General['SGData']
709    indices = (-1,0,1)
710    Units = np.array([[h,k,l] for h in indices for k in indices for l in indices])
711    AtTypes = General['AtomTypes']
712    Radii = np.array(General['BondRadii'])
713    try:
714        DisAglCtls = General['DisAglCtls']   
715        radiusFactor = DisAglCtls['Factors'][0]
716    except:
717        radiusFactor = 0.85
718    AtInfo = dict(zip(AtTypes,Radii)) #or General['BondRadii']
719    if Orig is None:
720        Orig = atNames.index(FrstName)
721    OId = Atoms[Orig][cia+8]
722    OType = Atoms[Orig][ct]
723    XYZ = getAtomXYZ(Atoms,cx)       
724    Oxyz = XYZ[Orig]
725    Neigh = []
726    Ids = []
727    sumR = np.array([AtInfo[OType]+AtInfo[atom[ct]] for atom in Atoms])
728    sumR = np.reshape(np.tile(sumR,27),(27,-1))
729    results = []
730    for xyz in XYZ:
731        results.append(G2spc.GenAtom(xyz,SGData,False,Move=False))
732    for iA,result in enumerate(results):
733        if iA != Orig:               
734            for [Txyz,Top,Tunit,Spn] in result:
735                Dx = np.array([Txyz-Oxyz+unit for unit in Units])
736                dx = np.inner(Dx,Amat)
737                dist = np.sqrt(np.sum(dx**2,axis=1))
738                IndB = ma.nonzero(ma.masked_greater(dist-radiusFactor*sumR[:,iA],0.))
739                for iU in IndB[0]:
740                    if AtNames[iA%len(AtNames)] != notName:
741                        unit = Units[iU]
742                        if np.any(unit):
743                            Topstr = ' +(%4d)[%2d,%2d,%2d]'%(Top,unit[0],unit[1],unit[2])
744                        else:
745                            Topstr = ' +(%4d)'%(Top)
746                        if Short:
747                            Neigh.append([AtNames[iA%len(AtNames)],dist[iU],True])
748                        else:
749                            Neigh.append([AtNames[iA]+Topstr,atTypes[iA],dist[iU],dx[iU]])
750                        Ids.append(Atoms[iA][cia+8])
751    return Neigh,[OId,Ids]
752   
753def calcBond(A,Ax,Bx,MTCU):
754    cell = G2lat.A2cell(A)
755    Amat,Bmat = G2lat.cell2AB(cell)
756    M,T,C,U = MTCU
757    Btx = np.inner(M,Bx)+T+C+U
758    Dx = Btx-Ax
759    dist = np.sqrt(np.inner(Amat,Dx))
760    return dist
761   
762def AddHydrogens(AtLookUp,General,Atoms,AddHydId):
763   
764    def getTransMat(RXYZ,OXYZ,TXYZ,Amat):
765        Vec = np.inner(Amat,np.array([OXYZ-TXYZ[0],RXYZ-TXYZ[0]])).T           
766        Vec /= np.sqrt(np.sum(Vec**2,axis=1))[:,nxs]
767        Mat2 = np.cross(Vec[0],Vec[1])      #UxV
768        Mat2 /= np.sqrt(np.sum(Mat2**2))
769        Mat3 = np.cross(Mat2,Vec[0])        #(UxV)xU
770        return nl.inv(np.array([Vec[0],Mat2,Mat3]))       
771   
772    cx,ct,cs,cia = General['AtomPtrs']
773    Cell = General['Cell'][1:7]
774    Amat,Bmat = G2lat.cell2AB(Cell)
775    nBonds = AddHydId[-1]+len(AddHydId[1])
776    Oatom = GetAtomsById(Atoms,AtLookUp,[AddHydId[0],])[0]
777    OXYZ = np.array(Oatom[cx:cx+3])
778    if 'I' in Oatom[cia]:
779        Uiso = Oatom[cia+1]
780    else:
781        Uiso = (Oatom[cia+2]+Oatom[cia+3]+Oatom[cia+4])/3.0       #simple average
782    Uiso = max(Uiso,0.005)                      #set floor!
783    Tatoms = GetAtomsById(Atoms,AtLookUp,AddHydId[1])
784    TXYZ = np.array([tatom[cx:cx+3] for tatom in Tatoms]) #3 x xyz
785    if nBonds == 4:
786        if AddHydId[-1] == 1:
787            Vec = TXYZ-OXYZ
788            Len = np.sqrt(np.sum(np.inner(Amat,Vec).T**2,axis=0))
789            Vec = np.sum(Vec/Len,axis=0)
790            Len = np.sqrt(np.sum(Vec**2))
791            Hpos = OXYZ-0.98*np.inner(Bmat,Vec).T/Len
792            HU = 1.1*Uiso
793            return [Hpos,],[HU,]
794        elif AddHydId[-1] == 2:
795            Vec = np.inner(Amat,TXYZ-OXYZ).T
796            Vec[0] += Vec[1]            #U - along bisector
797            Vec /= np.sqrt(np.sum(Vec**2,axis=1))[:,nxs]
798            Mat2 = np.cross(Vec[0],Vec[1])      #UxV
799            Mat2 /= np.sqrt(np.sum(Mat2**2))
800            Mat3 = np.cross(Mat2,Vec[0])        #(UxV)xU
801            iMat = nl.inv(np.array([Vec[0],Mat2,Mat3]))
802            Hpos = np.array([[-0.97*cosd(54.75),0.97*sind(54.75),0.],
803                [-0.97*cosd(54.75),-0.97*sind(54.75),0.]])
804            HU = 1.2*Uiso*np.ones(2)
805            Hpos = np.inner(Bmat,np.inner(iMat,Hpos).T).T+OXYZ
806            return Hpos,HU
807        else:
808            Ratom = GetAtomsById(Atoms,AtLookUp,[AddHydId[2],])[0]
809            RXYZ = np.array(Ratom[cx:cx+3])
810            iMat = getTransMat(RXYZ,OXYZ,TXYZ,Amat)
811            a = 0.96*cosd(70.5)
812            b = 0.96*sind(70.5)
813            Hpos = np.array([[a,0.,-b],[a,-b*cosd(30.),0.5*b],[a,b*cosd(30.),0.5*b]])
814            Hpos = np.inner(Bmat,np.inner(iMat,Hpos).T).T+OXYZ
815            HU = 1.5*Uiso*np.ones(3)
816            return Hpos,HU         
817    elif nBonds == 3:
818        if AddHydId[-1] == 1:
819            Vec = np.sum(TXYZ-OXYZ,axis=0)               
820            Len = np.sqrt(np.sum(np.inner(Amat,Vec).T**2))
821            Vec = -0.93*Vec/Len
822            Hpos = OXYZ+Vec
823            HU = 1.1*Uiso
824            return [Hpos,],[HU,]
825        elif AddHydId[-1] == 2:
826            Ratom = GetAtomsById(Atoms,AtLookUp,[AddHydId[2],])[0]
827            RXYZ = np.array(Ratom[cx:cx+3])
828            iMat = getTransMat(RXYZ,OXYZ,TXYZ,Amat)
829            a = 0.93*cosd(60.)
830            b = 0.93*sind(60.)
831            Hpos = [[a,b,0],[a,-b,0]]
832            Hpos = np.inner(Bmat,np.inner(iMat,Hpos).T).T+OXYZ
833            HU = 1.2*Uiso*np.ones(2)
834            return Hpos,HU
835    else:   #2 bonds
836        if 'C' in Oatom[ct]:
837            Vec = TXYZ[0]-OXYZ
838            Len = np.sqrt(np.sum(np.inner(Amat,Vec).T**2))
839            Vec = -0.93*Vec/Len
840            Hpos = OXYZ+Vec
841            HU = 1.1*Uiso
842            return [Hpos,],[HU,]
843        elif 'O' in Oatom[ct]:
844            mapData = General['Map']
845            Ratom = GetAtomsById(Atoms,AtLookUp,[AddHydId[2],])[0]
846            RXYZ = np.array(Ratom[cx:cx+3])
847            iMat = getTransMat(RXYZ,OXYZ,TXYZ,Amat)
848            a = 0.82*cosd(70.5)
849            b = 0.82*sind(70.5)
850            azm = np.arange(0.,360.,5.)
851            Hpos = np.array([[a,b*cosd(x),b*sind(x)] for x in azm])
852            Hpos = np.inner(Bmat,np.inner(iMat,Hpos).T).T+OXYZ
853            Rhos = np.array([getRho(pos,mapData) for pos in Hpos])
854            imax = np.argmax(Rhos)
855            HU = 1.5*Uiso
856            return [Hpos[imax],],[HU,]
857    return [],[]
858       
859#def AtomUij2TLS(atomData,atPtrs,Amat,Bmat,rbObj):   #unfinished & not used
860#    '''default doc string
861#   
862#    :param type name: description
863#   
864#    :returns: type name: description
865#   
866#    '''
867#    for atom in atomData:
868#        XYZ = np.inner(Amat,atom[cx:cx+3])
869#        if atom[cia] == 'A':
870#            UIJ = atom[cia+2:cia+8]
871               
872def TLS2Uij(xyz,g,Amat,rbObj):    #not used anywhere, but could be?
873    '''default doc string
874   
875    :param type name: description
876   
877    :returns: type name: description
878   
879    '''
880    TLStype,TLS = rbObj['ThermalMotion'][:2]
881    Tmat = np.zeros((3,3))
882    Lmat = np.zeros((3,3))
883    Smat = np.zeros((3,3))
884    gvec = np.sqrt(np.array([g[0][0]**2,g[1][1]**2,g[2][2]**2,
885        g[0][0]*g[1][1],g[0][0]*g[2][2],g[1][1]*g[2][2]]))
886    if 'T' in TLStype:
887        Tmat = G2lat.U6toUij(TLS[:6])
888    if 'L' in TLStype:
889        Lmat = G2lat.U6toUij(TLS[6:12])
890    if 'S' in TLStype:
891        Smat = np.array([[TLS[18],TLS[12],TLS[13]],[TLS[14],TLS[19],TLS[15]],[TLS[16],TLS[17],0] ])
892    XYZ = np.inner(Amat,xyz)
893    Axyz = np.array([[ 0,XYZ[2],-XYZ[1]], [-XYZ[2],0,XYZ[0]], [XYZ[1],-XYZ[0],0]] )
894    Umat = Tmat+np.inner(Axyz,Smat)+np.inner(Smat.T,Axyz.T)+np.inner(np.inner(Axyz,Lmat),Axyz.T)
895    beta = np.inner(np.inner(g,Umat),g)
896    return G2lat.UijtoU6(beta)*gvec   
897       
898def AtomTLS2UIJ(atomData,atPtrs,Amat,rbObj):    #not used anywhere, but could be?
899    '''default doc string
900   
901    :param type name: description
902   
903    :returns: type name: description
904   
905    '''
906    cx,ct,cs,cia = atPtrs
907    TLStype,TLS = rbObj['ThermalMotion'][:2]
908    Tmat = np.zeros((3,3))
909    Lmat = np.zeros((3,3))
910    Smat = np.zeros((3,3))
911    G,g = G2lat.A2Gmat(Amat)
912    gvec = 1./np.sqrt(np.array([g[0][0],g[1][1],g[2][2],g[0][1],g[0][2],g[1][2]]))
913    if 'T' in TLStype:
914        Tmat = G2lat.U6toUij(TLS[:6])
915    if 'L' in TLStype:
916        Lmat = G2lat.U6toUij(TLS[6:12])
917    if 'S' in TLStype:
918        Smat = np.array([ [TLS[18],TLS[12],TLS[13]], [TLS[14],TLS[19],TLS[15]], [TLS[16],TLS[17],0] ])
919    for atom in atomData:
920        XYZ = np.inner(Amat,atom[cx:cx+3])
921        Axyz = np.array([ 0,XYZ[2],-XYZ[1], -XYZ[2],0,XYZ[0], XYZ[1],-XYZ[0],0],ndmin=2 )
922        if 'U' in TLStype:
923            atom[cia+1] = TLS[0]
924            atom[cia] = 'I'
925        else:
926            atom[cia] = 'A'
927            Umat = Tmat+np.inner(Axyz,Smat)+np.inner(Smat.T,Axyz.T)+np.inner(np.inner(Axyz,Lmat),Axyz.T)
928            beta = np.inner(np.inner(g,Umat),g)
929            atom[cia+2:cia+8] = G2spc.U2Uij(beta/gvec)
930
931def GetXYZDist(xyz,XYZ,Amat):
932    '''gets distance from position xyz to all XYZ, xyz & XYZ are np.array
933        and are in crystal coordinates; Amat is crystal to Cart matrix
934   
935    :param type name: description
936   
937    :returns: type name: description
938   
939    '''
940    return np.sqrt(np.sum(np.inner(Amat,XYZ-xyz)**2,axis=0))
941
942def getAtomXYZ(atoms,cx):
943    '''Create an array of fractional coordinates from the atoms list
944   
945    :param list atoms: atoms object as found in tree
946    :param int cx: offset to where coordinates are found
947   
948    :returns: np.array with shape (n,3)   
949    '''
950    XYZ = []
951    for atom in atoms:
952        XYZ.append(atom[cx:cx+3])
953    return np.array(XYZ)
954
955def getRBTransMat(X,Y):
956    '''Get transformation for Cartesian axes given 2 vectors
957    X will  be parallel to new X-axis; X cross Y will be new Z-axis &
958    (X cross Y) cross Y will be new Y-axis
959    Useful for rigid body axes definintion
960   
961    :param array X: normalized vector
962    :param array Y: normalized vector
963   
964    :returns: array M: transformation matrix
965   
966    use as XYZ' = np.inner(M,XYZ) where XYZ are Cartesian
967   
968    '''
969    Mat2 = np.cross(X,Y)      #UxV-->Z
970    Mat2 /= np.sqrt(np.sum(Mat2**2))
971    Mat3 = np.cross(Mat2,X)        #(UxV)xU-->Y
972    Mat3 /= np.sqrt(np.sum(Mat3**2))
973    return np.array([X,Mat3,Mat2])       
974               
975def RotateRBXYZ(Bmat,Cart,oriQ):
976    '''rotate & transform cartesian coordinates to crystallographic ones
977    no translation applied. To be used for numerical derivatives
978   
979    :param type name: description
980   
981    :returns: type name: description
982   
983    '''
984    ''' returns crystal coordinates for atoms described by RBObj
985    '''
986    XYZ = np.zeros_like(Cart)
987    for i,xyz in enumerate(Cart):
988        XYZ[i] = np.inner(Bmat,prodQVQ(oriQ,xyz))
989    return XYZ
990
991def UpdateRBXYZ(Bmat,RBObj,RBData,RBType):
992    '''returns crystal coordinates for atoms described by RBObj
993   
994    :param np.array Bmat: see :func:`GSASIIlattice.cell2AB`
995    :param dict rbObj: rigid body selection/orientation information
996    :param dict RBData: rigid body tree data structure
997    :param str RBType: rigid body type, 'Vector' or 'Residue'
998
999    :returns: coordinates for rigid body as XYZ,Cart where XYZ is
1000       the location in crystal coordinates and Cart is in cartesian
1001    '''
1002    RBRes = RBData[RBType][RBObj['RBId']]
1003    if RBType == 'Vector':
1004        vecs = RBRes['rbVect']
1005        mags = RBRes['VectMag']
1006        Cart = np.zeros_like(vecs[0])
1007        for vec,mag in zip(vecs,mags):
1008            Cart += vec*mag
1009    elif RBType == 'Residue':
1010        Cart = np.array(RBRes['rbXYZ'])
1011        for tor,seq in zip(RBObj['Torsions'],RBRes['rbSeq']):
1012            QuatA = AVdeg2Q(tor[0],Cart[seq[0]]-Cart[seq[1]])
1013            Cart[seq[3]] = prodQVQ(QuatA,(Cart[seq[3]]-Cart[seq[1]]))+Cart[seq[1]]
1014    XYZ = np.zeros_like(Cart)
1015    for i,xyz in enumerate(Cart):
1016        XYZ[i] = np.inner(Bmat,prodQVQ(RBObj['Orient'][0],xyz))+RBObj['Orig'][0]
1017    return XYZ,Cart
1018
1019def UpdateMCSAxyz(Bmat,MCSA):
1020    '''default doc string
1021   
1022    :param type name: description
1023   
1024    :returns: type name: description
1025   
1026    '''
1027    xyz = []
1028    atTypes = []
1029    iatm = 0
1030    for model in MCSA['Models'][1:]:        #skip the MD model
1031        if model['Type'] == 'Atom':
1032            xyz.append(model['Pos'][0])
1033            atTypes.append(model['atType'])
1034            iatm += 1
1035        else:
1036            RBRes = MCSA['rbData'][model['Type']][model['RBId']]
1037            Pos = np.array(model['Pos'][0])
1038            Ori = np.array(model['Ori'][0])
1039            Qori = AVdeg2Q(Ori[0],Ori[1:])
1040            if model['Type'] == 'Vector':
1041                vecs = RBRes['rbVect']
1042                mags = RBRes['VectMag']
1043                Cart = np.zeros_like(vecs[0])
1044                for vec,mag in zip(vecs,mags):
1045                    Cart += vec*mag
1046            elif model['Type'] == 'Residue':
1047                Cart = np.array(RBRes['rbXYZ'])
1048                for itor,seq in enumerate(RBRes['rbSeq']):
1049                    QuatA = AVdeg2Q(model['Tor'][0][itor],Cart[seq[0]]-Cart[seq[1]])
1050                    Cart[seq[3]] = prodQVQ(QuatA,(Cart[seq[3]]-Cart[seq[1]]))+Cart[seq[1]]
1051            if model['MolCent'][1]:
1052                Cart -= model['MolCent'][0]
1053            for i,x in enumerate(Cart):
1054                xyz.append(np.inner(Bmat,prodQVQ(Qori,x))+Pos)
1055                atType = RBRes['rbTypes'][i]
1056                atTypes.append(atType)
1057                iatm += 1
1058    return np.array(xyz),atTypes
1059   
1060def SetMolCent(model,RBData):
1061    '''default doc string
1062   
1063    :param type name: description
1064   
1065    :returns: type name: description
1066   
1067    '''
1068    rideList = []
1069    RBRes = RBData[model['Type']][model['RBId']]
1070    if model['Type'] == 'Vector':
1071        vecs = RBRes['rbVect']
1072        mags = RBRes['VectMag']
1073        Cart = np.zeros_like(vecs[0])
1074        for vec,mag in zip(vecs,mags):
1075            Cart += vec*mag
1076    elif model['Type'] == 'Residue':
1077        Cart = np.array(RBRes['rbXYZ'])
1078        for seq in RBRes['rbSeq']:
1079            rideList += seq[3]
1080    centList = set(range(len(Cart)))-set(rideList)
1081    cent = np.zeros(3)
1082    for i in centList:
1083        cent += Cart[i]
1084    model['MolCent'][0] = cent/len(centList)   
1085   
1086def UpdateRBUIJ(Bmat,Cart,RBObj):
1087    '''default doc string
1088   
1089    :param type name: description
1090   
1091    :returns: type name: description
1092   
1093    '''
1094    ''' returns atom I/A, Uiso or UIJ for atoms at XYZ as described by RBObj
1095    '''
1096    TLStype,TLS = RBObj['ThermalMotion'][:2]
1097    T = np.zeros(6)
1098    L = np.zeros(6)
1099    S = np.zeros(8)
1100    if 'T' in TLStype:
1101        T = TLS[:6]
1102    if 'L' in TLStype:
1103        L = np.array(TLS[6:12])*(np.pi/180.)**2
1104    if 'S' in TLStype:
1105        S = np.array(TLS[12:])*(np.pi/180.)
1106    g = nl.inv(np.inner(Bmat,Bmat))
1107    gvec = np.sqrt(np.array([g[0][0]**2,g[1][1]**2,g[2][2]**2,
1108        g[0][0]*g[1][1],g[0][0]*g[2][2],g[1][1]*g[2][2]]))
1109    Uout = []
1110    Q = RBObj['Orient'][0]
1111    for X in Cart:
1112        X = prodQVQ(Q,X)
1113        if 'U' in TLStype:
1114            Uout.append(['I',TLS[0],0,0,0,0,0,0])
1115        elif not 'N' in TLStype:
1116            U = [0,0,0,0,0,0]
1117            U[0] = T[0]+L[1]*X[2]**2+L[2]*X[1]**2-2.0*L[5]*X[1]*X[2]+2.0*(S[2]*X[2]-S[4]*X[1])
1118            U[1] = T[1]+L[0]*X[2]**2+L[2]*X[0]**2-2.0*L[4]*X[0]*X[2]+2.0*(S[5]*X[0]-S[0]*X[2])
1119            U[2] = T[2]+L[1]*X[0]**2+L[0]*X[1]**2-2.0*L[3]*X[1]*X[0]+2.0*(S[1]*X[1]-S[3]*X[0])
1120            U[3] = T[3]+L[4]*X[1]*X[2]+L[5]*X[0]*X[2]-L[3]*X[2]**2-L[2]*X[0]*X[1]+  \
1121                S[4]*X[0]-S[5]*X[1]-(S[6]+S[7])*X[2]
1122            U[4] = T[4]+L[3]*X[1]*X[2]+L[5]*X[0]*X[1]-L[4]*X[1]**2-L[1]*X[0]*X[2]+  \
1123                S[3]*X[2]-S[2]*X[0]+S[6]*X[1]
1124            U[5] = T[5]+L[3]*X[0]*X[2]+L[4]*X[0]*X[1]-L[5]*X[0]**2-L[0]*X[2]*X[1]+  \
1125                S[0]*X[1]-S[1]*X[2]+S[7]*X[0]
1126            Umat = G2lat.U6toUij(U)
1127            beta = np.inner(np.inner(Bmat.T,Umat),Bmat)
1128            Uout.append(['A',0.0,]+list(G2lat.UijtoU6(beta)*gvec))
1129        else:
1130            Uout.append(['N',])
1131    return Uout
1132   
1133def GetSHCoeff(pId,parmDict,SHkeys):
1134    '''default doc string
1135   
1136    :param type name: description
1137   
1138    :returns: type name: description
1139   
1140    '''
1141    SHCoeff = {}
1142    for shkey in SHkeys:
1143        shname = str(pId)+'::'+shkey
1144        SHCoeff[shkey] = parmDict[shname]
1145    return SHCoeff
1146       
1147def getMass(generalData):
1148    '''Computes mass of unit cell contents
1149   
1150    :param dict generalData: The General dictionary in Phase
1151   
1152    :returns: float mass: Crystal unit cell mass in AMU.
1153   
1154    '''
1155    mass = 0.
1156    for i,elem in enumerate(generalData['AtomTypes']):
1157        mass += generalData['NoAtoms'][elem]*generalData['AtomMass'][i]
1158    return max(mass,1.0)   
1159
1160def getDensity(generalData):
1161    '''calculate crystal structure density
1162   
1163    :param dict generalData: The General dictionary in Phase
1164   
1165    :returns: float density: crystal density in gm/cm^3
1166   
1167    '''
1168    mass = getMass(generalData)
1169    Volume = generalData['Cell'][7]
1170    density = mass/(0.6022137*Volume)
1171    return density,Volume/mass
1172   
1173def getWave(Parms):
1174    '''returns wavelength from Instrument parameters dictionary
1175   
1176    :param dict Parms: Instrument parameters;
1177        must contain:
1178        Lam: single wavelength
1179        or
1180        Lam1: Ka1 radiation wavelength
1181   
1182    :returns: float wave: wavelength
1183   
1184    '''
1185    try:
1186        return Parms['Lam'][1]
1187    except KeyError:
1188        return Parms['Lam1'][1]
1189       
1190def getMeanWave(Parms):
1191    '''returns mean wavelength from Instrument parameters dictionary
1192   
1193    :param dict Parms: Instrument parameters;
1194        must contain:
1195        Lam: single wavelength
1196        or
1197        Lam1,Lam2: Ka1,Ka2 radiation wavelength
1198        I(L2)/I(L1): Ka2/Ka1 ratio
1199   
1200    :returns: float wave: mean wavelength
1201   
1202    '''
1203    try:
1204        return Parms['Lam'][1]
1205    except KeyError:
1206        meanLam = (Parms['Lam1'][1]+Parms['I(L2)/I(L1)'][1]*Parms['Lam2'][1])/   \
1207            (1.+Parms['I(L2)/I(L1)'][1])
1208        return meanLam
1209   
1210       
1211def El2Mass(Elements):
1212    '''compute molecular weight from Elements
1213   
1214    :param dict Elements: elements in molecular formula;
1215        each must contain
1216        Num: number of atoms in formula
1217        Mass: at. wt.
1218   
1219    :returns: float mass: molecular weight.
1220   
1221    '''
1222    mass = 0
1223    for El in Elements:
1224        mass += Elements[El]['Num']*Elements[El]['Mass']
1225    return mass
1226       
1227def Den2Vol(Elements,density):
1228    '''converts density to molecular volume
1229   
1230    :param dict Elements: elements in molecular formula;
1231        each must contain
1232        Num: number of atoms in formula
1233        Mass: at. wt.
1234    :param float density: material density in gm/cm^3
1235   
1236    :returns: float volume: molecular volume in A^3
1237   
1238    '''
1239    return El2Mass(Elements)/(density*0.6022137)
1240   
1241def Vol2Den(Elements,volume):
1242    '''converts volume to density
1243   
1244    :param dict Elements: elements in molecular formula;
1245        each must contain
1246        Num: number of atoms in formula
1247        Mass: at. wt.
1248    :param float volume: molecular volume in A^3
1249   
1250    :returns: float density: material density in gm/cm^3
1251   
1252    '''
1253    return El2Mass(Elements)/(volume*0.6022137)
1254   
1255def El2EstVol(Elements):
1256    '''Estimate volume from molecular formula; assumes atom volume = 10A^3
1257   
1258    :param dict Elements: elements in molecular formula;
1259        each must contain
1260        Num: number of atoms in formula
1261   
1262    :returns: float volume: estimate of molecular volume in A^3
1263   
1264    '''
1265    vol = 0
1266    for El in Elements:
1267        vol += 10.*Elements[El]['Num']
1268    return vol
1269   
1270def XScattDen(Elements,vol,wave=0.):
1271    '''Estimate X-ray scattering density from molecular formula & volume;
1272    ignores valence, but includes anomalous effects
1273   
1274    :param dict Elements: elements in molecular formula;
1275        each element must contain
1276        Num: number of atoms in formula
1277        Z: atomic number
1278    :param float vol: molecular volume in A^3
1279    :param float wave: optional wavelength in A
1280   
1281    :returns: float rho: scattering density in 10^10cm^-2;
1282        if wave > 0 the includes f' contribution
1283    :returns: float mu: if wave>0 absorption coeff in cm^-1 ; otherwise 0
1284    :returns: float fpp: if wave>0 f" in 10^10cm^-2; otherwise 0
1285   
1286    '''
1287    rho = 0
1288    mu = 0
1289    fpp = 0
1290    if wave: 
1291        Xanom = XAnomAbs(Elements,wave)
1292    for El in Elements:
1293        f0 = Elements[El]['Z']
1294        if wave:
1295            f0 += Xanom[El][0]
1296            fpp += Xanom[El][1]*Elements[El]['Num']
1297            mu += Xanom[El][2]*Elements[El]['Num']
1298        rho += Elements[El]['Num']*f0
1299    return 28.179*rho/vol,0.1*mu/vol,28.179*fpp/vol
1300   
1301def NCScattDen(Elements,vol,wave=0.):
1302    '''Estimate neutron scattering density from molecular formula & volume;
1303    ignores valence, but includes anomalous effects
1304   
1305    :param dict Elements: elements in molecular formula;
1306        each element must contain
1307        Num: number of atoms in formula
1308        Z: atomic number
1309    :param float vol: molecular volume in A^3
1310    :param float wave: optional wavelength in A
1311   
1312    :returns: float rho: scattering density in 10^10cm^-2;
1313        if wave > 0 the includes f' contribution
1314    :returns: float mu: if wave>0 absorption coeff in cm^-1 ; otherwise 0
1315    :returns: float fpp: if wave>0 f" in 10^10cm^-2; otherwise 0
1316   
1317    '''
1318    rho = 0
1319    mu = 0
1320    bpp = 0
1321    for El in Elements:
1322        isotope = Elements[El]['Isotope']
1323        b0 = Elements[El]['Isotopes'][isotope]['SL'][0]
1324        mu += Elements[El]['Isotopes'][isotope].get('SA',0.)*Elements[El]['Num']
1325        if wave and 'BW-LS' in Elements[El]['Isotopes'][isotope]:
1326            Re,Im,E0,gam,A,E1,B,E2 = Elements[El]['Isotopes'][isotope]['BW-LS'][1:]
1327            Emev = 81.80703/wave**2
1328            T0 = Emev-E0
1329            T1 = Emev-E1
1330            T2 = Emev-E2
1331            D0 = T0**2+gam**2
1332            D1 = T1**2+gam**2
1333            D2 = T2**2+gam**2
1334            b0 += Re*(T0/D0+A*T1/D1+B*T2/D2)
1335            bpp += Im*(1/D0+A/D1+B/D2)
1336        else:
1337            bpp += Elements[El]['Isotopes'][isotope]['SL'][1]
1338        rho += Elements[El]['Num']*b0
1339    if wave: mu *= wave
1340    return 100.*rho/vol,mu/vol,100.*bpp/vol
1341   
1342def wavekE(wavekE):
1343    '''Convert wavelength to energy & vise versa
1344   
1345    :param float waveKe:wavelength in A or energy in kE
1346   
1347    :returns float waveKe:the other one
1348   
1349    '''
1350    return 12.397639/wavekE
1351       
1352def XAnomAbs(Elements,wave):
1353    kE = wavekE(wave)
1354    Xanom = {}
1355    for El in Elements:
1356        Orbs = G2el.GetXsectionCoeff(El)
1357        Xanom[El] = G2el.FPcalc(Orbs, kE)
1358    return Xanom        #f',f", mu
1359   
1360################################################################################
1361#### Modulation math
1362################################################################################
1363
1364def makeWaves(waveTypes,FSSdata,XSSdata,USSdata,MSSdata,Mast):
1365    '''
1366    waveTypes: array nAtoms: 'Fourier','ZigZag' or 'Block'
1367    FSSdata: array 2 x atoms x waves    (sin,cos terms)
1368    XSSdata: array 2x3 x atoms X waves (sin,cos terms)
1369    USSdata: array 2x6 x atoms X waves (sin,cos terms)
1370    MSSdata: array 2x3 x atoms X waves (sin,cos terms)
1371   
1372    Mast: array orthogonalization matrix for Uij
1373    '''
1374    ngl = 36                    #selected for integer steps for 1/6,1/4,1/3...
1375    glTau,glWt = pwd.pygauleg(0.,1.,ngl)         #get Gauss-Legendre intervals & weights
1376    mglTau = np.arange(0.,1.,1./ngl)
1377    Ax = np.array(XSSdata[:3]).T   #atoms x waves x sin pos mods
1378    Bx = np.array(XSSdata[3:]).T   #...cos pos mods
1379    Af = np.array(FSSdata[0]).T    #sin frac mods x waves x atoms
1380    Bf = np.array(FSSdata[1]).T    #cos frac mods...
1381    Au = Mast*np.array(G2lat.U6toUij(USSdata[:6])).T   #atoms x waves x sin Uij mods as betaij
1382    Bu = Mast*np.array(G2lat.U6toUij(USSdata[6:])).T   #...cos Uij mods as betaij
1383    Am = np.array(MSSdata[:3]).T   #atoms x waves x sin pos mods
1384    Bm = np.array(MSSdata[3:]).T   #...cos pos mods
1385    nWaves = [Af.shape[1],Ax.shape[1],Au.shape[1],Am.shape[1]] 
1386    if nWaves[0]:
1387        tauF = np.arange(1.,nWaves[0]+1)[:,nxs]*glTau  #Fwaves x ngl
1388        FmodA = Af[:,:,nxs]*np.sin(twopi*tauF[nxs,:,:])   #atoms X Fwaves X ngl
1389        FmodB = Bf[:,:,nxs]*np.cos(twopi*tauF[nxs,:,:])
1390        Fmod = np.sum(1.0+FmodA+FmodB,axis=1)             #atoms X ngl; sum waves
1391    else:
1392        Fmod = 1.0           
1393    XmodZ = np.zeros((Ax.shape[0],Ax.shape[1],3,ngl))
1394    XmodA = np.zeros((Ax.shape[0],Ax.shape[1],3,ngl))
1395    XmodB = np.zeros((Ax.shape[0],Ax.shape[1],3,ngl))
1396    for iatm in range(Ax.shape[0]):
1397        nx = 0
1398        if 'ZigZag' in waveTypes[iatm]:
1399            nx = 1
1400            Tmm = Ax[iatm][0][:2]                       
1401            XYZmax = np.array([Ax[iatm][0][2],Bx[iatm][0][0],Bx[iatm][0][1]])
1402            XmodZ[iatm][0] += posZigZag(glTau,Tmm,XYZmax).T
1403        elif 'Block' in waveTypes[iatm]:
1404            nx = 1
1405            Tmm = Ax[iatm][0][:2]                       
1406            XYZmax = np.array([Ax[iatm][0][2],Bx[iatm][0][0],Bx[iatm][0][1]])
1407            XmodZ[iatm][0] += posBlock(glTau,Tmm,XYZmax).T
1408        tauX = np.arange(1.,nWaves[1]+1-nx)[:,nxs]*glTau  #Xwaves x ngl
1409        if nx:   
1410            XmodA[iatm][:-nx] = Ax[iatm,nx:,:,nxs]*np.sin(twopi*tauX[nxs,:,nxs,:]) #atoms X waves X 3 X ngl
1411            XmodB[iatm][:-nx] = Bx[iatm,nx:,:,nxs]*np.cos(twopi*tauX[nxs,:,nxs,:]) #ditto
1412        else:
1413            XmodA[iatm] = Ax[iatm,:,:,nxs]*np.sin(twopi*tauX[nxs,:,nxs,:]) #atoms X waves X 3 X ngl
1414            XmodB[iatm] = Bx[iatm,:,:,nxs]*np.cos(twopi*tauX[nxs,:,nxs,:]) #ditto
1415    Xmod = np.sum(XmodA+XmodB+XmodZ,axis=1)                #atoms X 3 X ngl; sum waves
1416    Xmod = np.swapaxes(Xmod,1,2)
1417    if nWaves[2]:
1418        tauU = np.arange(1.,nWaves[2]+1)[:,nxs]*glTau     #Uwaves x ngl
1419        UmodA = Au[:,:,:,:,nxs]*np.sin(twopi*tauU[nxs,:,nxs,nxs,:]) #atoms x waves x 3x3 x ngl
1420        UmodB = Bu[:,:,:,:,nxs]*np.cos(twopi*tauU[nxs,:,nxs,nxs,:]) #ditto
1421        Umod = np.swapaxes(np.sum(UmodA+UmodB,axis=1),1,3)      #atoms x 3x3 x ngl; sum waves
1422    else:
1423        Umod = 1.0
1424    if nWaves[3]:
1425        tauM = np.arange(1.,nWaves[3]+1-nx)[:,nxs]*mglTau  #Mwaves x ngl
1426        MmodA = Am[:,:,:,nxs]*np.sin(twopi*tauM[nxs,:,nxs,:]) #atoms X waves X 3 X ngl
1427        MmodB = Bm[:,:,:,nxs]*np.cos(twopi*tauM[nxs,:,nxs,:]) #ditto
1428        Mmod = np.sum(MmodA+MmodB,axis=1)
1429        Mmod = np.swapaxes(Mmod,1,2)    #Mxyz,Ntau,Natm
1430    else:
1431        Mmod = 1.0
1432    return ngl,nWaves,Fmod,Xmod,Umod,Mmod,glTau,glWt
1433
1434def MagMod(glTau,XYZ,modQ,MSSdata,SGData,SSGData):
1435    '''
1436    this needs to make magnetic moment modulations & magnitudes as
1437    fxn of gTau points; NB: this allows only 1 mag. wave fxn.
1438    '''
1439    Am = np.array(MSSdata[3:]).T[:,0,:]   #atoms x cos pos mods; only 1 wave used
1440    Bm = np.array(MSSdata[:3]).T[:,0,:]   #...sin pos mods
1441    SGMT = np.array([ops[0] for ops in SGData['SGOps']])        #not .T!!
1442    Sinv = np.array([nl.inv(ops[0]) for ops in SSGData['SSGOps']])
1443    SGT = np.array([ops[1] for ops in SSGData['SSGOps']])
1444    if SGData['SGInv']:
1445        SGMT = np.vstack((SGMT,-SGMT))
1446        Sinv = np.vstack((Sinv,-Sinv))
1447        SGT = np.vstack((SGT,-SGT))
1448    SGMT = np.vstack([SGMT for cen in SGData['SGCen']])
1449    Sinv = np.vstack([Sinv for cen in SGData['SGCen']])
1450    SGT = np.vstack([SGT+cen for cen in SSGData['SSGCen']])%1.
1451    if SGData['SGGray']:
1452        SGMT = np.vstack((SGMT,SGMT))
1453        Sinv = np.vstack((Sinv,Sinv))
1454        SGT = np.vstack((SGT,SGT+np.array([0.,0.,0.,.5])))%1.
1455    mst = Sinv[:,3,:3]
1456    epsinv = Sinv[:,3,3]
1457    phi = np.inner(XYZ,modQ).T
1458    TA = np.sum(mst[nxs,:,:]*(XYZ-SGT[:,:3][nxs,:,:]),axis=-1).T
1459    phase =  TA[nxs,:,:] + epsinv[nxs,:,nxs]*(glTau[:,nxs,nxs]-SGT[:,3][nxs,:,nxs]+phi[nxs,:,:])
1460    psin = np.sin(twopi*phase)
1461    pcos = np.cos(twopi*phase)
1462    MmodA = Am[nxs,nxs,:,:]*pcos[:,:,:,nxs]    #cos term
1463    MmodB = Bm[nxs,nxs,:,:]*psin[:,:,:,nxs]    #sin term
1464    MmodA = np.sum(SGMT[nxs,:,nxs,:,:]*MmodA[:,:,:,nxs,:],axis=-1)*SGData['SpnFlp'][nxs,:,nxs,nxs]
1465    MmodB = np.sum(SGMT[nxs,:,nxs,:,:]*MmodB[:,:,:,nxs,:],axis=-1)*SGData['SpnFlp'][nxs,:,nxs,nxs]
1466    return MmodA,MmodB    #Ntau,Nops,Natm,Mxyz; cos & sin parts; sum matches drawn atom moments
1467       
1468def Modulation(H,HP,nWaves,Fmod,Xmod,Umod,glTau,glWt):
1469    '''
1470    H: array nRefBlk x ops X hklt
1471    HP: array nRefBlk x ops X hklt proj to hkl
1472    nWaves: list number of waves for frac, pos, uij & mag
1473    Fmod: array 2 x atoms x waves    (sin,cos terms)
1474    Xmod: array atoms X 3 X ngl
1475    Umod: array atoms x 3x3 x ngl
1476    glTau,glWt: arrays Gauss-Lorentzian pos & wts
1477    '''
1478   
1479    if nWaves[2]:       #uij (adp) waves
1480        if len(HP.shape) > 2:
1481            HbH = np.exp(-np.sum(HP[:,:,nxs,nxs,:]*np.inner(HP,Umod),axis=-1)) # refBlk x ops x atoms x ngl add Overhauser corr.?
1482        else:
1483            HbH = np.exp(-np.sum(HP[:,nxs,nxs,:]*np.inner(HP,Umod),axis=-1)) # refBlk x ops x atoms x ngl add Overhauser corr.?
1484    else:
1485        HbH = 1.0
1486    HdotX = np.inner(HP,Xmod)                   #refBlk x ops x atoms X ngl
1487    if len(H.shape) > 2:
1488        D = H[:,:,3:]*glTau[nxs,nxs,:]              #m*e*tau; refBlk x ops X ngl
1489        HdotXD = twopi*(HdotX+D[:,:,nxs,:])
1490    else:
1491        D = H[:,3:]*glTau[nxs,:]              #m*e*tau; refBlk x ops X ngl
1492        HdotXD = twopi*(HdotX+D[:,nxs,:])
1493    cosHA = np.sum(Fmod*HbH*np.cos(HdotXD)*glWt,axis=-1)       #real part; refBlk X ops x atoms; sum for G-L integration
1494    sinHA = np.sum(Fmod*HbH*np.sin(HdotXD)*glWt,axis=-1)       #imag part; ditto
1495    return np.array([cosHA,sinHA])             # 2 x refBlk x SGops x atoms
1496   
1497def ModulationTw(H,HP,nWaves,Fmod,Xmod,Umod,glTau,glWt):
1498    '''
1499    H: array nRefBlk x tw x ops X hklt
1500    HP: array nRefBlk x tw x ops X hklt proj to hkl
1501    Fmod: array 2 x atoms x waves    (sin,cos terms)
1502    Xmod: array atoms X ngl X 3
1503    Umod: array atoms x ngl x 3x3
1504    glTau,glWt: arrays Gauss-Lorentzian pos & wts
1505    '''
1506   
1507    if nWaves[2]:
1508        if len(HP.shape) > 3:   #Blocks of reflections
1509            HbH = np.exp(-np.sum(HP[:,:,nxs,nxs,:]*np.inner(HP,Umod),axis=-1)) # refBlk x ops x atoms x ngl add Overhauser corr.?
1510        else:   #single reflections
1511            HbH = np.exp(-np.sum(HP[:,nxs,nxs,:]*np.inner(HP,Umod),axis=-1)) # refBlk x ops x atoms x ngl add Overhauser corr.?
1512    else:
1513        HbH = 1.0
1514    HdotX = np.inner(HP,Xmod)                   #refBlk x tw x ops x atoms X ngl
1515    if len(H.shape) > 3:
1516        D = glTau*H[:,:,:,3:,nxs]              #m*e*tau; refBlk x tw x ops X ngl
1517        HdotXD = twopi*(HdotX+D[:,:,:,nxs,:])
1518    else:
1519        D = H*glTau[nxs,:]              #m*e*tau; refBlk x ops X ngl
1520        HdotXD = twopi*(HdotX+D[:,nxs,:])
1521    cosHA = np.sum(Fmod*HbH*np.cos(HdotXD)*glWt,axis=-1)       #real part; refBlk X ops x atoms; sum for G-L integration
1522    sinHA = np.sum(Fmod*HbH*np.sin(HdotXD)*glWt,axis=-1)       #imag part; ditto
1523    return np.array([cosHA,sinHA])             # 2 x refBlk x SGops x atoms
1524   
1525def makeWavesDerv(ngl,waveTypes,FSSdata,XSSdata,USSdata,Mast):
1526    '''
1527    Only for Fourier waves for fraction, position & adp (probably not used for magnetism)
1528    FSSdata: array 2 x atoms x waves    (sin,cos terms)
1529    XSSdata: array 2x3 x atoms X waves (sin,cos terms)
1530    USSdata: array 2x6 x atoms X waves (sin,cos terms)
1531    Mast: array orthogonalization matrix for Uij
1532    '''
1533    glTau,glWt = pwd.pygauleg(0.,1.,ngl)         #get Gauss-Legendre intervals & weights
1534    waveShapes = [FSSdata.T.shape,XSSdata.T.shape,USSdata.T.shape]
1535    Af = np.array(FSSdata[0]).T    #sin frac mods x waves x atoms
1536    Bf = np.array(FSSdata[1]).T    #cos frac mods...
1537    Ax = np.array(XSSdata[:3]).T   #...cos pos mods x waves x atoms
1538    Bx = np.array(XSSdata[3:]).T   #...cos pos mods
1539    Au = Mast*np.array(G2lat.U6toUij(USSdata[:6])).T   #atoms x waves x sin Uij mods
1540    Bu = Mast*np.array(G2lat.U6toUij(USSdata[6:])).T   #...cos Uij mods
1541    nWaves = [Af.shape[1],Ax.shape[1],Au.shape[1]] 
1542    StauX = np.zeros((Ax.shape[0],Ax.shape[1],3,ngl))    #atoms x waves x 3 x ngl
1543    CtauX = np.zeros((Ax.shape[0],Ax.shape[1],3,ngl))
1544    ZtauXt = np.zeros((Ax.shape[0],2,3,ngl))               #atoms x Tminmax x 3 x ngl
1545    ZtauXx = np.zeros((Ax.shape[0],3,ngl))               #atoms x XYZmax x ngl
1546    for iatm in range(Ax.shape[0]):
1547        nx = 0
1548        if 'ZigZag' in waveTypes[iatm]:
1549            nx = 1
1550        elif 'Block' in waveTypes[iatm]:
1551            nx = 1
1552        tauX = np.arange(1.,nWaves[1]+1-nx)[:,nxs]*glTau  #Xwaves x ngl
1553        if nx:   
1554            StauX[iatm][nx:] = np.ones_like(Ax)[iatm,nx:,:,nxs]*np.sin(twopi*tauX)[nxs,:,nxs,:]   #atoms X waves X 3(xyz) X ngl
1555            CtauX[iatm][nx:] = np.ones_like(Bx)[iatm,nx:,:,nxs]*np.cos(twopi*tauX)[nxs,:,nxs,:]   #ditto
1556        else:
1557            StauX[iatm] = np.ones_like(Ax)[iatm,:,:,nxs]*np.sin(twopi*tauX)[nxs,:,nxs,:]   #atoms X waves X 3(xyz) X ngl
1558            CtauX[iatm] = np.ones_like(Bx)[iatm,:,:,nxs]*np.cos(twopi*tauX)[nxs,:,nxs,:]   #ditto
1559    if nWaves[0]:
1560        tauF = np.arange(1.,nWaves[0]+1)[:,nxs]*glTau  #Fwaves x ngl
1561        StauF = np.ones_like(Af)[:,:,nxs]*np.sin(twopi*tauF)[nxs,:,:] #also dFmod/dAf
1562        CtauF = np.ones_like(Bf)[:,:,nxs]*np.cos(twopi*tauF)[nxs,:,:] #also dFmod/dBf
1563    else:
1564        StauF = 1.0
1565        CtauF = 1.0
1566    if nWaves[2]:
1567        tauU = np.arange(1.,nWaves[2]+1)[:,nxs]*glTau     #Uwaves x ngl
1568        StauU = np.ones_like(Au)[:,:,:,:,nxs]*np.sin(twopi*tauU)[nxs,:,nxs,nxs,:]   #also dUmodA/dAu
1569        CtauU = np.ones_like(Bu)[:,:,:,:,nxs]*np.cos(twopi*tauU)[nxs,:,nxs,nxs,:]   #also dUmodB/dBu
1570        UmodA = Au[:,:,:,:,nxs]*StauU #atoms x waves x 3x3 x ngl
1571        UmodB = Bu[:,:,:,:,nxs]*CtauU #ditto
1572#derivs need to be ops x atoms x waves x 6uij; ops x atoms x waves x ngl x 6uij before sum
1573        StauU = np.rollaxis(np.rollaxis(np.swapaxes(StauU,2,4),-1),-1)
1574        CtauU = np.rollaxis(np.rollaxis(np.swapaxes(CtauU,2,4),-1),-1)
1575    else:
1576        StauU = 1.0
1577        CtauU = 1.0
1578        UmodA = 0.
1579        UmodB = 0.
1580    return waveShapes,[StauF,CtauF],[StauX,CtauX,ZtauXt,ZtauXx],[StauU,CtauU],UmodA+UmodB
1581   
1582def ModulationDerv(H,HP,Hij,nWaves,waveShapes,Fmod,Xmod,UmodAB,SCtauF,SCtauX,SCtauU,glTau,glWt):
1583    '''
1584    Compute Fourier modulation derivatives
1585    H: array ops X hklt proj to hkl
1586    HP: array ops X hklt proj to hkl
1587    Hij: array 2pi^2[a*^2h^2 b*^2k^2 c*^2l^2 a*b*hk a*c*hl b*c*kl] of projected hklm to hkl space
1588    '''
1589   
1590    Mf = [H.shape[0],]+list(waveShapes[0])    #=[ops,atoms,waves,2] (sin+cos frac mods)
1591    dGdMfC = np.zeros(Mf)
1592    dGdMfS = np.zeros(Mf)
1593    Mx = [H.shape[0],]+list(waveShapes[1])   #=[ops,atoms,waves,6] (sin+cos pos mods)
1594    dGdMxC = np.zeros(Mx)
1595    dGdMxS = np.zeros(Mx)
1596    Mu = [H.shape[0],]+list(waveShapes[2])    #=[ops,atoms,waves,12] (sin+cos Uij mods)
1597    dGdMuC = np.zeros(Mu)
1598    dGdMuS = np.zeros(Mu)
1599   
1600    D = twopi*H[:,3][:,nxs]*glTau[nxs,:]              #m*e*tau; ops X ngl
1601    HdotX = twopi*np.inner(HP,Xmod)        #ops x atoms X ngl
1602    HdotXD = HdotX+D[:,nxs,:]
1603    if nWaves[2]:
1604        Umod = np.swapaxes((UmodAB),2,4)      #atoms x waves x ngl x 3x3 (symmetric so I can do this!)
1605        HuH = np.sum(HP[:,nxs,nxs,nxs]*np.inner(HP,Umod),axis=-1)    #ops x atoms x waves x ngl
1606        HuH = np.sum(HP[:,nxs,nxs,nxs]*np.inner(HP,Umod),axis=-1)    #ops x atoms x waves x ngl
1607        HbH = np.exp(-np.sum(HuH,axis=-2)) # ops x atoms x ngl; sum waves - OK vs Modulation version
1608#        part1 = -np.exp(-HuH)*Fmod[nxs,:,nxs,:]    #ops x atoms x waves x ngl
1609        part1 = -np.exp(-HuH)*Fmod    #ops x atoms x waves x ngl
1610        dUdAu = Hij[:,nxs,nxs,nxs,:]*np.rollaxis(G2lat.UijtoU6(SCtauU[0]),0,4)[nxs,:,:,:,:]    #ops x atoms x waves x ngl x 6sinUij
1611        dUdBu = Hij[:,nxs,nxs,nxs,:]*np.rollaxis(G2lat.UijtoU6(SCtauU[1]),0,4)[nxs,:,:,:,:]    #ops x atoms x waves x ngl x 6cosUij
1612        dGdMuCa = np.sum(part1[:,:,:,:,nxs]*dUdAu*np.cos(HdotXD)[:,:,nxs,:,nxs]*glWt[nxs,nxs,nxs,:,nxs],axis=-2)   #ops x atoms x waves x 6uij; G-L sum
1613        dGdMuCb = np.sum(part1[:,:,:,:,nxs]*dUdBu*np.cos(HdotXD)[:,:,nxs,:,nxs]*glWt[nxs,nxs,nxs,:,nxs],axis=-2)
1614        dGdMuC = np.concatenate((dGdMuCa,dGdMuCb),axis=-1)   #ops x atoms x waves x 12uij
1615        dGdMuSa = np.sum(part1[:,:,:,:,nxs]*dUdAu*np.sin(HdotXD)[:,:,nxs,:,nxs]*glWt[nxs,nxs,nxs,:,nxs],axis=-2)   #ops x atoms x waves x 6uij; G-L sum
1616        dGdMuSb = np.sum(part1[:,:,:,:,nxs]*dUdBu*np.sin(HdotXD)[:,:,nxs,:,nxs]*glWt[nxs,nxs,nxs,:,nxs],axis=-2)
1617        dGdMuS = np.concatenate((dGdMuSa,dGdMuSb),axis=-1)   #ops x atoms x waves x 12uij
1618    else:
1619        HbH = np.ones_like(HdotX)
1620    dHdXA = twopi*HP[:,nxs,nxs,nxs,:]*np.swapaxes(SCtauX[0],-1,-2)[nxs,:,:,:,:]    #ops x atoms x sine waves x ngl x xyz
1621    dHdXB = twopi*HP[:,nxs,nxs,nxs,:]*np.swapaxes(SCtauX[1],-1,-2)[nxs,:,:,:,:]    #ditto - cos waves
1622# ops x atoms x waves x 2xyz - real part - good
1623#    dGdMxCa = -np.sum((Fmod[nxs,:,:]*HbH)[:,:,nxs,:,nxs]*(dHdXA*np.sin(HdotXD)[:,:,nxs,:,nxs])*glWt[nxs,nxs,nxs,:,nxs],axis=-2)
1624#    dGdMxCb = -np.sum((Fmod[nxs,:,:]*HbH)[:,:,nxs,:,nxs]*(dHdXB*np.sin(HdotXD)[:,:,nxs,:,nxs])*glWt[nxs,nxs,nxs,:,nxs],axis=-2)
1625    dGdMxCa = -np.sum((Fmod*HbH)[:,:,nxs,:,nxs]*(dHdXA*np.sin(HdotXD)[:,:,nxs,:,nxs])*glWt[nxs,nxs,nxs,:,nxs],axis=-2)
1626    dGdMxCb = -np.sum((Fmod*HbH)[:,:,nxs,:,nxs]*(dHdXB*np.sin(HdotXD)[:,:,nxs,:,nxs])*glWt[nxs,nxs,nxs,:,nxs],axis=-2)
1627    dGdMxC = np.concatenate((dGdMxCa,dGdMxCb),axis=-1)
1628# ops x atoms x waves x 2xyz - imag part - good
1629#    dGdMxSa = np.sum((Fmod[nxs,:,:]*HbH)[:,:,nxs,:,nxs]*(dHdXA*np.cos(HdotXD)[:,:,nxs,:,nxs])*glWt[nxs,nxs,nxs,:,nxs],axis=-2)   
1630#    dGdMxSb = np.sum((Fmod[nxs,:,:]*HbH)[:,:,nxs,:,nxs]*(dHdXB*np.cos(HdotXD)[:,:,nxs,:,nxs])*glWt[nxs,nxs,nxs,:,nxs],axis=-2)   
1631    dGdMxSa = np.sum((Fmod*HbH)[:,:,nxs,:,nxs]*(dHdXA*np.cos(HdotXD)[:,:,nxs,:,nxs])*glWt[nxs,nxs,nxs,:,nxs],axis=-2)   
1632    dGdMxSb = np.sum((Fmod*HbH)[:,:,nxs,:,nxs]*(dHdXB*np.cos(HdotXD)[:,:,nxs,:,nxs])*glWt[nxs,nxs,nxs,:,nxs],axis=-2)   
1633    dGdMxS = np.concatenate((dGdMxSa,dGdMxSb),axis=-1)
1634    return [dGdMfC,dGdMfS],[dGdMxC,dGdMxS],[dGdMuC,dGdMuS]
1635       
1636def posFourier(tau,psin,pcos):
1637    A = np.array([ps[:,nxs]*np.sin(2*np.pi*(i+1)*tau) for i,ps in enumerate(psin)])
1638    B = np.array([pc[:,nxs]*np.cos(2*np.pi*(i+1)*tau) for i,pc in enumerate(pcos)])
1639    return np.sum(A,axis=0)+np.sum(B,axis=0)
1640   
1641def posZigZag(T,Tmm,Xmax):
1642    DT = Tmm[1]-Tmm[0]
1643    Su = 2.*Xmax/DT
1644    Sd = 2.*Xmax/(1.-DT)
1645    A = np.array([np.where(  0.< (t-Tmm[0])%1. <= DT, -Xmax+Su*((t-Tmm[0])%1.), Xmax-Sd*((t-Tmm[1])%1.)) for t in T])
1646    return A
1647   
1648#def posZigZagDerv(T,Tmm,Xmax):
1649#    DT = Tmm[1]-Tmm[0]
1650#    Su = 2.*Xmax/DT
1651#    Sd = 2.*Xmax/(1.-DT)
1652#    dAdT = np.zeros((2,3,len(T)))
1653#    dAdT[0] = np.array([np.where(Tmm[0] < t <= Tmm[1],Su*(t-Tmm[0]-1)/DT,-Sd*(t-Tmm[1])/(1.-DT)) for t in T]).T
1654#    dAdT[1] = np.array([np.where(Tmm[0] < t <= Tmm[1],-Su*(t-Tmm[0])/DT,Sd*(t-Tmm[1])/(1.-DT)) for t in T]).T
1655#    dAdX = np.ones(3)[:,nxs]*np.array([np.where(Tmm[0] < t%1. <= Tmm[1],-1.+2.*(t-Tmm[0])/DT,1.-2.*(t-Tmm[1])%1./DT) for t in T])
1656#    return dAdT,dAdX
1657
1658def posBlock(T,Tmm,Xmax):
1659    A = np.array([np.where(Tmm[0] < t%1. <= Tmm[1],-Xmax,Xmax) for t in T])
1660    return A
1661   
1662#def posBlockDerv(T,Tmm,Xmax):
1663#    dAdT = np.zeros((2,3,len(T)))
1664#    ind = np.searchsorted(T,Tmm)
1665#    dAdT[0,:,ind[0]] = -Xmax/len(T)
1666#    dAdT[1,:,ind[1]] = Xmax/len(T)
1667#    dAdX = np.ones(3)[:,nxs]*np.array([np.where(Tmm[0] < t <= Tmm[1],-1.,1.) for t in T])  #OK
1668#    return dAdT,dAdX
1669   
1670def fracCrenel(tau,Toff,Twid):
1671    Tau = (tau-Toff)%1.
1672    A = np.where(Tau<Twid,1.,0.)
1673    return A
1674   
1675def fracFourier(tau,fsin,fcos):
1676    if len(fsin) == 1:
1677        A = np.array([fsin[0]*np.sin(2.*np.pi*tau)])
1678        B = np.array([fcos[0]*np.cos(2.*np.pi*tau)])
1679    else:
1680        A = np.array([fs[:,nxs]*np.sin(2.*np.pi*(i+1)*tau) for i,fs in enumerate(fsin)])
1681        B = np.array([fc[:,nxs]*np.cos(2.*np.pi*(i+1)*tau) for i,fc in enumerate(fcos)])
1682    return np.sum(A,axis=0)+np.sum(B,axis=0)
1683
1684def ApplyModulation(data,tau):
1685    '''Applies modulation to drawing atom positions & Uijs for given tau
1686    '''
1687    generalData = data['General']
1688    cell = generalData['Cell'][1:7]
1689    G,g = G2lat.cell2Gmat(cell)
1690    SGData = generalData['SGData']
1691    SSGData = generalData['SSGData']
1692    cx,ct,cs,cia = generalData['AtomPtrs']
1693    drawingData = data['Drawing']
1694    modul = generalData['SuperVec'][0]
1695    dcx,dct,dcs,dci = drawingData['atomPtrs']
1696    atoms = data['Atoms']
1697    drawAtoms = drawingData['Atoms']
1698    Fade = np.ones(len(drawAtoms))
1699    for atom in atoms:
1700        atxyz = np.array(atom[cx:cx+3])
1701        atuij = np.array(atom[cia+2:cia+8])
1702        Sfrac = atom[-1]['SS1']['Sfrac']
1703        Spos = atom[-1]['SS1']['Spos']
1704        Sadp = atom[-1]['SS1']['Sadp']
1705        if generalData['Type'] == 'magnetic':
1706            Smag = atom[-1]['SS1']['Smag']
1707            atmom = np.array(atom[cx+4:cx+7])
1708        indx = FindAtomIndexByIDs(drawAtoms,dci,[atom[cia+8],],True)
1709        for ind in indx:
1710            drawatom = drawAtoms[ind]
1711            opr = drawatom[dcs-1]
1712            sop,ssop,icent,cent,unit = G2spc.OpsfromStringOps(opr,SGData,SSGData)
1713            drxyz = (np.inner(sop[0],atxyz)+sop[1]+cent)*icent+np.array(unit)
1714            tauT = G2spc.getTauT(tau,sop,ssop,drxyz,modul)[-1]
1715            tauT *= icent       #invert wave on -1
1716#            print(tau,tauT,opr,G2spc.MT2text(sop).replace(' ',''),G2spc.SSMT2text(ssop).replace(' ',''))
1717            wave = np.zeros(3)
1718            uwave = np.zeros(6)
1719            mom = np.zeros(3)
1720            if len(Sfrac):
1721                scof = []
1722                ccof = []
1723                waveType = Sfrac[0]
1724                for i,sfrac in enumerate(Sfrac[1:]):
1725                    if not i and 'Crenel' in waveType:
1726                        Fade[ind] += fracCrenel(tauT,sfrac[0][0],sfrac[0][1])
1727                    else:
1728                        scof.append(sfrac[0][0])
1729                        ccof.append(sfrac[0][1])
1730                    if len(scof):
1731                        Fade[ind] += np.sum(fracFourier(tauT,scof,ccof))                           
1732            if len(Spos):
1733                scof = []
1734                ccof = []
1735                waveType = Spos[0]
1736                for i,spos in enumerate(Spos[1:]):
1737                    if waveType in ['ZigZag','Block'] and not i:
1738                        Tminmax = spos[0][:2]
1739                        XYZmax = np.array(spos[0][2:5])
1740                        if waveType == 'Block':
1741                            wave = np.array(posBlock([tauT,],Tminmax,XYZmax))[0]
1742                        elif waveType == 'ZigZag':
1743                            wave = np.array(posZigZag([tauT,],Tminmax,XYZmax))[0]
1744                    else:
1745                        scof.append(spos[0][:3])
1746                        ccof.append(spos[0][3:])
1747                if len(scof):
1748                    wave += np.sum(posFourier(tauT,np.array(scof),np.array(ccof)),axis=1)
1749            if generalData['Type'] == 'magnetic' and len(Smag):
1750                scof = []
1751                ccof = []
1752                waveType = Smag[0]
1753                for i,spos in enumerate(Smag[1:]):
1754                    scof.append(spos[0][:3])
1755                    ccof.append(spos[0][3:])
1756                if len(scof):
1757                    mom += np.sum(posFourier(tauT,np.array(scof),np.array(ccof)),axis=1)
1758            if len(Sadp):
1759                scof = []
1760                ccof = []
1761                waveType = Sadp[0]
1762                for i,sadp in enumerate(Sadp[1:]):
1763                    scof.append(sadp[0][:6])
1764                    ccof.append(sadp[0][6:])
1765                ures = posFourier(tauT,np.array(scof),np.array(ccof))
1766                if np.any(ures):
1767                    uwave += np.sum(ures,axis=1)
1768            if atom[cia] == 'A':                   
1769                X,U = G2spc.ApplyStringOps(opr,SGData,atxyz+wave,atuij+uwave)
1770                drawatom[dcx:dcx+3] = X
1771                drawatom[dci-6:dci] = U
1772            else:
1773                X = G2spc.ApplyStringOps(opr,SGData,atxyz+wave)
1774                drawatom[dcx:dcx+3] = X
1775            if generalData['Type'] == 'magnetic':
1776                M = G2spc.ApplyStringOpsMom(opr,SGData,SSGData,atmom+mom)
1777                drawatom[dcx+3:dcx+6] = M
1778    return drawAtoms,Fade
1779   
1780# gauleg.py Gauss Legendre numerical quadrature, x and w computation
1781# integrate from a to b using n evaluations of the function f(x) 
1782# usage: from gauleg import gaulegf         
1783#        x,w = gaulegf( a, b, n)                               
1784#        area = 0.0                                           
1785#        for i in range(1,n+1):          #  yes, 1..n                   
1786#          area += w[i]*f(x[i])                                   
1787
1788def gaulegf(a, b, n):
1789    x = range(n+1) # x[0] unused
1790    w = range(n+1) # w[0] unused
1791    eps = 3.0E-14
1792    m = (n+1)/2
1793    xm = 0.5*(b+a)
1794    xl = 0.5*(b-a)
1795    for i in range(1,m+1):
1796        z = math.cos(3.141592654*(i-0.25)/(n+0.5))
1797        while True:
1798            p1 = 1.0
1799            p2 = 0.0
1800            for j in range(1,n+1):
1801                p3 = p2
1802                p2 = p1
1803                p1 = ((2.0*j-1.0)*z*p2-(j-1.0)*p3)/j
1804       
1805            pp = n*(z*p1-p2)/(z*z-1.0)
1806            z1 = z
1807            z = z1 - p1/pp
1808            if abs(z-z1) <= eps:
1809                break
1810
1811        x[i] = xm - xl*z
1812        x[n+1-i] = xm + xl*z
1813        w[i] = 2.0*xl/((1.0-z*z)*pp*pp)
1814        w[n+1-i] = w[i]
1815    return np.array(x), np.array(w)
1816# end gaulegf
1817   
1818   
1819def BessJn(nmax,x):
1820    ''' compute Bessel function J(n,x) from scipy routine & recurrance relation
1821    returns sequence of J(n,x) for n in range [-nmax...0...nmax]
1822   
1823    :param  integer nmax: maximul order for Jn(x)
1824    :param  float x: argument for Jn(x)
1825   
1826    :returns numpy array: [J(-nmax,x)...J(0,x)...J(nmax,x)]
1827   
1828    '''
1829    import scipy.special as sp
1830    bessJn = np.zeros(2*nmax+1)
1831    bessJn[nmax] = sp.j0(x)
1832    bessJn[nmax+1] = sp.j1(x)
1833    bessJn[nmax-1] = -bessJn[nmax+1]
1834    for i in range(2,nmax+1):
1835        bessJn[i+nmax] = 2*(i-1)*bessJn[nmax+i-1]/x-bessJn[nmax+i-2]
1836        bessJn[nmax-i] = bessJn[i+nmax]*(-1)**i
1837    return bessJn
1838   
1839def BessIn(nmax,x):
1840    ''' compute modified Bessel function I(n,x) from scipy routines & recurrance relation
1841    returns sequence of I(n,x) for n in range [-nmax...0...nmax]
1842   
1843    :param  integer nmax: maximul order for In(x)
1844    :param  float x: argument for In(x)
1845   
1846    :returns numpy array: [I(-nmax,x)...I(0,x)...I(nmax,x)]
1847   
1848    '''
1849    import scipy.special as sp
1850    bessIn = np.zeros(2*nmax+1)
1851    bessIn[nmax] = sp.i0(x)
1852    bessIn[nmax+1] = sp.i1(x)
1853    bessIn[nmax-1] = bessIn[nmax+1]
1854    for i in range(2,nmax+1):
1855        bessIn[i+nmax] = bessIn[nmax+i-2]-2*(i-1)*bessIn[nmax+i-1]/x
1856        bessIn[nmax-i] = bessIn[i+nmax]
1857    return bessIn
1858       
1859   
1860################################################################################
1861##### distance, angle, planes, torsion stuff
1862################################################################################
1863
1864def CalcDist(distance_dict, distance_atoms, parmDict):
1865    if not len(parmDict):
1866        return 0.
1867    pId = distance_dict['pId']
1868    A = [parmDict['%s::A%d'%(pId,i)] for i in range(6)]
1869    Amat = G2lat.cell2AB(G2lat.A2cell(A))[0]
1870    Oxyz = [parmDict['%s::A%s:%d'%(pId,x,distance_atoms[0])] for x in ['x','y','z']]
1871    Txyz = [parmDict['%s::A%s:%d'%(pId,x,distance_atoms[1])] for x in ['x','y','z']]
1872    inv = 1
1873    symNo = distance_dict['symNo']
1874    if symNo < 0:
1875        inv = -1
1876        symNo *= -1
1877    cen = symNo//100
1878    op = symNo%100-1
1879    M,T = distance_dict['SGData']['SGOps'][op]
1880    D = T*inv+distance_dict['SGData']['SGCen'][cen]
1881    D += distance_dict['cellNo']
1882    Txyz = np.inner(M*inv,Txyz)+D
1883    dist = np.sqrt(np.sum(np.inner(Amat,(Txyz-Oxyz))**2))
1884#    GSASIIpath.IPyBreak()
1885    return dist   
1886   
1887def CalcDistDeriv(distance_dict, distance_atoms, parmDict):
1888    if not len(parmDict):
1889        return None
1890    pId = distance_dict['pId']
1891    A = [parmDict['%s::A%d'%(pId,i)] for i in range(6)]
1892    Amat = G2lat.cell2AB(G2lat.A2cell(A))[0]
1893    Oxyz = [parmDict['%s::A%s:%d'%(pId,x,distance_atoms[0])] for x in ['x','y','z']]
1894    Txyz = [parmDict['%s::A%s:%d'%(pId,x,distance_atoms[1])] for x in ['x','y','z']]
1895    symNo = distance_dict['symNo']
1896    Tunit = distance_dict['cellNo']
1897    SGData = distance_dict['SGData']   
1898    deriv = getDistDerv(Oxyz,Txyz,Amat,Tunit,symNo,SGData)
1899    return deriv
1900   
1901def CalcAngle(angle_dict, angle_atoms, parmDict):
1902    if not len(parmDict):
1903        return 0.
1904    pId = angle_dict['pId']
1905    A = [parmDict['%s::A%d'%(pId,i)] for i in range(6)]
1906    Amat = G2lat.cell2AB(G2lat.A2cell(A))[0]
1907    Oxyz = [parmDict['%s::A%s:%d'%(pId,x,angle_atoms[0])] for x in ['x','y','z']]
1908    Axyz = [parmDict['%s::A%s:%d'%(pId,x,angle_atoms[1][0])] for x in ['x','y','z']]
1909    Bxyz = [parmDict['%s::A%s:%d'%(pId,x,angle_atoms[1][1])] for x in ['x','y','z']]
1910    ABxyz = [Axyz,Bxyz]
1911    symNo = angle_dict['symNo']
1912    vec = np.zeros((2,3))
1913    for i in range(2):
1914        inv = 1
1915        if symNo[i] < 0:
1916            inv = -1
1917        cen = inv*symNo[i]//100
1918        op = inv*symNo[i]%100-1
1919        M,T = angle_dict['SGData']['SGOps'][op]
1920        D = T*inv+angle_dict['SGData']['SGCen'][cen]
1921        D += angle_dict['cellNo'][i]
1922        ABxyz[i] = np.inner(M*inv,ABxyz[i])+D
1923        vec[i] = np.inner(Amat,(ABxyz[i]-Oxyz))
1924        dist = np.sqrt(np.sum(vec[i]**2))
1925        if not dist:
1926            return 0.
1927        vec[i] /= dist
1928    angle = acosd(np.sum(vec[0]*vec[1]))
1929    return angle
1930
1931def CalcAngleDeriv(angle_dict, angle_atoms, parmDict):
1932    if not len(parmDict):
1933        return None
1934    pId = angle_dict['pId']
1935    A = [parmDict['%s::A%d'%(pId,i)] for i in range(6)]
1936    Amat = G2lat.cell2AB(G2lat.A2cell(A))[0]
1937    Oxyz = [parmDict['%s::A%s:%d'%(pId,x,angle_atoms[0])] for x in ['x','y','z']]
1938    Axyz = [parmDict['%s::A%s:%d'%(pId,x,angle_atoms[1][0])] for x in ['x','y','z']]
1939    Bxyz = [parmDict['%s::A%s:%d'%(pId,x,angle_atoms[1][1])] for x in ['x','y','z']]
1940    symNo = angle_dict['symNo']
1941    Tunit = angle_dict['cellNo']
1942    SGData = angle_dict['SGData']   
1943    deriv = getAngleDerv(Oxyz,Axyz,Bxyz,Amat,Tunit,symNo,SGData)
1944    return deriv
1945
1946def getSyXYZ(XYZ,ops,SGData):
1947    '''default doc
1948   
1949    :param type name: description
1950   
1951    :returns: type name: description
1952   
1953    '''
1954    XYZout = np.zeros_like(XYZ)
1955    for i,[xyz,op] in enumerate(zip(XYZ,ops)):
1956        if op == '1':
1957            XYZout[i] = xyz
1958        else:
1959            oprs = op.split('+')
1960            unit = [0,0,0]
1961            if len(oprs)>1:
1962                unit = np.array(list(eval(oprs[1])))
1963            syop =int(oprs[0])
1964            inv = syop//abs(syop)
1965            syop *= inv
1966            cent = syop//100
1967            syop %= 100
1968            syop -= 1
1969            M,T = SGData['SGOps'][syop]
1970            XYZout[i] = (np.inner(M,xyz)+T)*inv+SGData['SGCen'][cent]+unit
1971    return XYZout
1972   
1973def getRestDist(XYZ,Amat):
1974    '''default doc string
1975   
1976    :param type name: description
1977   
1978    :returns: type name: description
1979   
1980    '''
1981    return np.sqrt(np.sum(np.inner(Amat,(XYZ[1]-XYZ[0]))**2))
1982   
1983def getRestDeriv(Func,XYZ,Amat,ops,SGData):
1984    '''default doc string
1985   
1986    :param type name: description
1987   
1988    :returns: type name: description
1989   
1990    '''
1991    deriv = np.zeros((len(XYZ),3))
1992    dx = 0.00001
1993    for j,xyz in enumerate(XYZ):
1994        for i,x in enumerate(np.array([[dx,0,0],[0,dx,0],[0,0,dx]])):
1995            XYZ[j] -= x
1996            d1 = Func(getSyXYZ(XYZ,ops,SGData),Amat)
1997            XYZ[j] += 2*x
1998            d2 = Func(getSyXYZ(XYZ,ops,SGData),Amat)
1999            XYZ[j] -= x
2000            deriv[j][i] = (d1-d2)/(2*dx)
2001    return deriv.flatten()
2002
2003def getRestAngle(XYZ,Amat):
2004    '''default doc string
2005   
2006    :param type name: description
2007   
2008    :returns: type name: description
2009   
2010    '''
2011   
2012    def calcVec(Ox,Tx,Amat):
2013        return np.inner(Amat,(Tx-Ox))
2014
2015    VecA = calcVec(XYZ[1],XYZ[0],Amat)
2016    VecA /= np.sqrt(np.sum(VecA**2))
2017    VecB = calcVec(XYZ[1],XYZ[2],Amat)
2018    VecB /= np.sqrt(np.sum(VecB**2))
2019    edge = VecB-VecA
2020    edge = np.sum(edge**2)
2021    angle = (2.-edge)/2.
2022    angle = max(angle,-1.)
2023    return acosd(angle)
2024   
2025def getRestPlane(XYZ,Amat):
2026    '''default doc string
2027   
2028    :param type name: description
2029   
2030    :returns: type name: description
2031   
2032    '''
2033    sumXYZ = np.zeros(3)
2034    for xyz in XYZ:
2035        sumXYZ += xyz
2036    sumXYZ /= len(XYZ)
2037    XYZ = np.array(XYZ)-sumXYZ
2038    XYZ = np.inner(Amat,XYZ).T
2039    Zmat = np.zeros((3,3))
2040    for i,xyz in enumerate(XYZ):
2041        Zmat += np.outer(xyz.T,xyz)
2042    Evec,Emat = nl.eig(Zmat)
2043    Evec = np.sqrt(Evec)/(len(XYZ)-3)
2044    Order = np.argsort(Evec)
2045    return Evec[Order[0]]
2046   
2047def getRestChiral(XYZ,Amat):   
2048    '''default doc string
2049   
2050    :param type name: description
2051   
2052    :returns: type name: description
2053   
2054    '''
2055    VecA = np.empty((3,3))   
2056    VecA[0] = np.inner(XYZ[1]-XYZ[0],Amat)
2057    VecA[1] = np.inner(XYZ[2]-XYZ[0],Amat)
2058    VecA[2] = np.inner(XYZ[3]-XYZ[0],Amat)
2059    return nl.det(VecA)
2060   
2061def getRestTorsion(XYZ,Amat):
2062    '''default doc string
2063   
2064    :param type name: description
2065   
2066    :returns: type name: description
2067   
2068    '''
2069    VecA = np.empty((3,3))
2070    VecA[0] = np.inner(XYZ[1]-XYZ[0],Amat)
2071    VecA[1] = np.inner(XYZ[2]-XYZ[1],Amat)
2072    VecA[2] = np.inner(XYZ[3]-XYZ[2],Amat)
2073    D = nl.det(VecA)
2074    Mag = np.sqrt(np.sum(VecA*VecA,axis=1))
2075    P12 = np.sum(VecA[0]*VecA[1])/(Mag[0]*Mag[1])
2076    P13 = np.sum(VecA[0]*VecA[2])/(Mag[0]*Mag[2])
2077    P23 = np.sum(VecA[1]*VecA[2])/(Mag[1]*Mag[2])
2078    Ang = 1.0
2079    if abs(P12) < 1.0 and abs(P23) < 1.0:
2080        Ang = (P12*P23-P13)/(np.sqrt(1.-P12**2)*np.sqrt(1.-P23**2))
2081    TOR = (acosd(Ang)*D/abs(D)+720.)%360.
2082    return TOR
2083   
2084def calcTorsionEnergy(TOR,Coeff=[]):
2085    '''default doc string
2086   
2087    :param type name: description
2088   
2089    :returns: type name: description
2090   
2091    '''
2092    sum = 0.
2093    if len(Coeff):
2094        cof = np.reshape(Coeff,(3,3)).T
2095        delt = TOR-cof[1]
2096        delt = np.where(delt<-180.,delt+360.,delt)
2097        delt = np.where(delt>180.,delt-360.,delt)
2098        term = -cof[2]*delt**2
2099        val = cof[0]*np.exp(term/1000.0)
2100        pMax = cof[0][np.argmin(val)]
2101        Eval = np.sum(val)
2102        sum = Eval-pMax
2103    return sum,Eval
2104
2105def getTorsionDeriv(XYZ,Amat,Coeff):
2106    '''default doc string
2107   
2108    :param type name: description
2109   
2110    :returns: type name: description
2111   
2112    '''
2113    deriv = np.zeros((len(XYZ),3))
2114    dx = 0.00001
2115    for j,xyz in enumerate(XYZ):
2116        for i,x in enumerate(np.array([[dx,0,0],[0,dx,0],[0,0,dx]])):
2117            XYZ[j] -= x
2118            tor = getRestTorsion(XYZ,Amat)
2119            p1,d1 = calcTorsionEnergy(tor,Coeff)
2120            XYZ[j] += 2*x
2121            tor = getRestTorsion(XYZ,Amat)
2122            p2,d2 = calcTorsionEnergy(tor,Coeff)           
2123            XYZ[j] -= x
2124            deriv[j][i] = (p2-p1)/(2*dx)
2125    return deriv.flatten()
2126
2127def getRestRama(XYZ,Amat):
2128    '''Computes a pair of torsion angles in a 5 atom string
2129   
2130    :param nparray XYZ: crystallographic coordinates of 5 atoms
2131    :param nparray Amat: crystal to cartesian transformation matrix
2132   
2133    :returns: list (phi,psi) two torsion angles in degrees
2134   
2135    '''
2136    phi = getRestTorsion(XYZ[:5],Amat)
2137    psi = getRestTorsion(XYZ[1:],Amat)
2138    return phi,psi
2139   
2140def calcRamaEnergy(phi,psi,Coeff=[]):
2141    '''Computes pseudo potential energy from a pair of torsion angles and a
2142    numerical description of the potential energy surface. Used to create
2143    penalty function in LS refinement:     
2144    :math:`Eval(\\phi,\\psi) = C[0]*exp(-V/1000)`
2145
2146    where :math:`V = -C[3] * (\\phi-C[1])^2 - C[4]*(\\psi-C[2])^2 - 2*(\\phi-C[1])*(\\psi-C[2])`
2147   
2148    :param float phi: first torsion angle (:math:`\\phi`)
2149    :param float psi: second torsion angle (:math:`\\psi`)
2150    :param list Coeff: pseudo potential coefficients
2151   
2152    :returns: list (sum,Eval): pseudo-potential difference from minimum & value;
2153      sum is used for penalty function.
2154   
2155    '''
2156    sum = 0.
2157    Eval = 0.
2158    if len(Coeff):
2159        cof = Coeff.T
2160        dPhi = phi-cof[1]
2161        dPhi = np.where(dPhi<-180.,dPhi+360.,dPhi)
2162        dPhi = np.where(dPhi>180.,dPhi-360.,dPhi)
2163        dPsi = psi-cof[2]
2164        dPsi = np.where(dPsi<-180.,dPsi+360.,dPsi)
2165        dPsi = np.where(dPsi>180.,dPsi-360.,dPsi)
2166        val = -cof[3]*dPhi**2-cof[4]*dPsi**2-2.0*cof[5]*dPhi*dPsi
2167        val = cof[0]*np.exp(val/1000.)
2168        pMax = cof[0][np.argmin(val)]
2169        Eval = np.sum(val)
2170        sum = Eval-pMax
2171    return sum,Eval
2172
2173def getRamaDeriv(XYZ,Amat,Coeff):
2174    '''Computes numerical derivatives of torsion angle pair pseudo potential
2175    with respect of crystallographic atom coordinates of the 5 atom sequence
2176   
2177    :param nparray XYZ: crystallographic coordinates of 5 atoms
2178    :param nparray Amat: crystal to cartesian transformation matrix
2179    :param list Coeff: pseudo potential coefficients
2180   
2181    :returns: list (deriv) derivatives of pseudopotential with respect to 5 atom
2182     crystallographic xyz coordinates.
2183   
2184    '''
2185    deriv = np.zeros((len(XYZ),3))
2186    dx = 0.00001
2187    for j,xyz in enumerate(XYZ):
2188        for i,x in enumerate(np.array([[dx,0,0],[0,dx,0],[0,0,dx]])):
2189            XYZ[j] -= x
2190            phi,psi = getRestRama(XYZ,Amat)
2191            p1,d1 = calcRamaEnergy(phi,psi,Coeff)
2192            XYZ[j] += 2*x
2193            phi,psi = getRestRama(XYZ,Amat)
2194            p2,d2 = calcRamaEnergy(phi,psi,Coeff)
2195            XYZ[j] -= x
2196            deriv[j][i] = (p2-p1)/(2*dx)
2197    return deriv.flatten()
2198
2199def getRestPolefig(ODFln,SamSym,Grid):
2200    '''default doc string
2201   
2202    :param type name: description
2203   
2204    :returns: type name: description
2205   
2206    '''
2207    X,Y = np.meshgrid(np.linspace(1.,-1.,Grid),np.linspace(-1.,1.,Grid))
2208    R,P = np.sqrt(X**2+Y**2).flatten(),atan2d(Y,X).flatten()
2209    R = np.where(R <= 1.,2.*atand(R),0.0)
2210    Z = np.zeros_like(R)
2211    Z = G2lat.polfcal(ODFln,SamSym,R,P)
2212    Z = np.reshape(Z,(Grid,Grid))
2213    return np.reshape(R,(Grid,Grid)),np.reshape(P,(Grid,Grid)),Z
2214
2215def getRestPolefigDerv(HKL,Grid,SHCoeff):
2216    '''default doc string
2217   
2218    :param type name: description
2219   
2220    :returns: type name: description
2221   
2222    '''
2223    pass
2224       
2225def getDistDerv(Oxyz,Txyz,Amat,Tunit,Top,SGData):
2226    '''default doc string
2227   
2228    :param type name: description
2229   
2230    :returns: type name: description
2231   
2232    '''
2233    def calcDist(Ox,Tx,U,inv,C,M,T,Amat):
2234        TxT = inv*(np.inner(M,Tx)+T)+C+U
2235        return np.sqrt(np.sum(np.inner(Amat,(TxT-Ox))**2))
2236       
2237    inv = Top/abs(Top)
2238    cent = abs(Top)//100
2239    op = abs(Top)%100-1
2240    M,T = SGData['SGOps'][op]
2241    C = SGData['SGCen'][cent]
2242    dx = .00001
2243    deriv = np.zeros(6)
2244    for i in [0,1,2]:
2245        Oxyz[i] -= dx
2246        d0 = calcDist(Oxyz,Txyz,Tunit,inv,C,M,T,Amat)
2247        Oxyz[i] += 2*dx
2248        deriv[i] = (calcDist(Oxyz,Txyz,Tunit,inv,C,M,T,Amat)-d0)/(2.*dx)
2249        Oxyz[i] -= dx
2250        Txyz[i] -= dx
2251        d0 = calcDist(Oxyz,Txyz,Tunit,inv,C,M,T,Amat)
2252        Txyz[i] += 2*dx
2253        deriv[i+3] = (calcDist(Oxyz,Txyz,Tunit,inv,C,M,T,Amat)-d0)/(2.*dx)
2254        Txyz[i] -= dx
2255    return deriv
2256   
2257def getAngleDerv(Oxyz,Axyz,Bxyz,Amat,Tunit,symNo,SGData):
2258   
2259    def calcAngle(Oxyz,ABxyz,Amat,Tunit,symNo,SGData):
2260        vec = np.zeros((2,3))
2261        for i in range(2):
2262            inv = 1
2263            if symNo[i] < 0:
2264                inv = -1
2265            cen = inv*symNo[i]//100
2266            op = inv*symNo[i]%100-1
2267            M,T = SGData['SGOps'][op]
2268            D = T*inv+SGData['SGCen'][cen]
2269            D += Tunit[i]
2270            ABxyz[i] = np.inner(M*inv,ABxyz[i])+D
2271            vec[i] = np.inner(Amat,(ABxyz[i]-Oxyz))
2272            dist = np.sqrt(np.sum(vec[i]**2))
2273            if not dist:
2274                return 0.
2275            vec[i] /= dist
2276        angle = acosd(np.sum(vec[0]*vec[1]))
2277    #    GSASIIpath.IPyBreak()
2278        return angle
2279       
2280    dx = .00001
2281    deriv = np.zeros(9)
2282    for i in [0,1,2]:
2283        Oxyz[i] -= dx
2284        a0 = calcAngle(Oxyz,[Axyz,Bxyz],Amat,Tunit,symNo,SGData)
2285        Oxyz[i] += 2*dx
2286        deriv[i] = (calcAngle(Oxyz,[Axyz,Bxyz],Amat,Tunit,symNo,SGData)-a0)/(2.*dx)
2287        Oxyz[i] -= dx
2288        Axyz[i] -= dx
2289        a0 = calcAngle(Oxyz,[Axyz,Bxyz],Amat,Tunit,symNo,SGData)
2290        Axyz[i] += 2*dx
2291        deriv[i+3] = (calcAngle(Oxyz,[Axyz,Bxyz],Amat,Tunit,symNo,SGData)-a0)/(2.*dx)
2292        Axyz[i] -= dx
2293        Bxyz[i] -= dx
2294        a0 = calcAngle(Oxyz,[Axyz,Bxyz],Amat,Tunit,symNo,SGData)
2295        Bxyz[i] += 2*dx
2296        deriv[i+6] = (calcAngle(Oxyz,[Axyz,Bxyz],Amat,Tunit,symNo,SGData)-a0)/(2.*dx)
2297        Bxyz[i] -= dx
2298    return deriv
2299   
2300def getAngSig(VA,VB,Amat,SGData,covData={}):
2301    '''default doc string
2302   
2303    :param type name: description
2304   
2305    :returns: type name: description
2306   
2307    '''
2308    def calcVec(Ox,Tx,U,inv,C,M,T,Amat):
2309        TxT = inv*(np.inner(M,Tx)+T)+C+U
2310        return np.inner(Amat,(TxT-Ox))
2311       
2312    def calcAngle(Ox,TxA,TxB,unitA,unitB,invA,CA,MA,TA,invB,CB,MB,TB,Amat):
2313        VecA = calcVec(Ox,TxA,unitA,invA,CA,MA,TA,Amat)
2314        VecA /= np.sqrt(np.sum(VecA**2))
2315        VecB = calcVec(Ox,TxB,unitB,invB,CB,MB,TB,Amat)
2316        VecB /= np.sqrt(np.sum(VecB**2))
2317        edge = VecB-VecA
2318        edge = np.sum(edge**2)
2319        angle = (2.-edge)/2.
2320        angle = max(angle,-1.)
2321        return acosd(angle)
2322       
2323    OxAN,OxA,TxAN,TxA,unitA,TopA = VA
2324    OxBN,OxB,TxBN,TxB,unitB,TopB = VB
2325    invA = invB = 1
2326    invA = TopA//abs(TopA)
2327    invB = TopB//abs(TopB)
2328    centA = abs(TopA)//100
2329    centB = abs(TopB)//100
2330    opA = abs(TopA)%100-1
2331    opB = abs(TopB)%100-1
2332    MA,TA = SGData['SGOps'][opA]
2333    MB,TB = SGData['SGOps'][opB]
2334    CA = SGData['SGCen'][centA]
2335    CB = SGData['SGCen'][centB]
2336    if 'covMatrix' in covData:
2337        covMatrix = covData['covMatrix']
2338        varyList = covData['varyList']
2339        AngVcov = getVCov(OxAN+TxAN+TxBN,varyList,covMatrix)
2340        dx = .00001
2341        dadx = np.zeros(9)
2342        Ang = calcAngle(OxA,TxA,TxB,unitA,unitB,invA,CA,MA,TA,invB,CB,MB,TB,Amat)
2343        for i in [0,1,2]:
2344            OxA[i] -= dx
2345            a0 = calcAngle(OxA,TxA,TxB,unitA,unitB,invA,CA,MA,TA,invB,CB,MB,TB,Amat)
2346            OxA[i] += 2*dx
2347            dadx[i] = (calcAngle(OxA,TxA,TxB,unitA,unitB,invA,CA,MA,TA,invB,CB,MB,TB,Amat)-a0)/(2*dx)
2348            OxA[i] -= dx
2349           
2350            TxA[i] -= dx
2351            a0 = calcAngle(OxA,TxA,TxB,unitA,unitB,invA,CA,MA,TA,invB,CB,MB,TB,Amat)
2352            TxA[i] += 2*dx
2353            dadx[i+3] = (calcAngle(OxA,TxA,TxB,unitA,unitB,invA,CA,MA,TA,invB,CB,MB,TB,Amat)-a0)/(2*dx)
2354            TxA[i] -= dx
2355           
2356            TxB[i] -= dx
2357            a0 = calcAngle(OxA,TxA,TxB,unitA,unitB,invA,CA,MA,TA,invB,CB,MB,TB,Amat)
2358            TxB[i] += 2*dx
2359            dadx[i+6] = (calcAngle(OxA,TxA,TxB,unitA,unitB,invA,CA,MA,TA,invB,CB,MB,TB,Amat)-a0)/(2*dx)
2360            TxB[i] -= dx
2361           
2362        sigAng = np.sqrt(np.inner(dadx,np.inner(AngVcov,dadx)))
2363        if sigAng < 0.01:
2364            sigAng = 0.0
2365        return Ang,sigAng
2366    else:
2367        return calcAngle(OxA,TxA,TxB,unitA,unitB,invA,CA,MA,TA,invB,CB,MB,TB,Amat),0.0
2368
2369def GetDistSig(Oatoms,Atoms,Amat,SGData,covData={}):
2370    '''default doc string
2371   
2372    :param type name: description
2373   
2374    :returns: type name: description
2375   
2376    '''
2377    def calcDist(Atoms,SyOps,Amat):
2378        XYZ = []
2379        for i,atom in enumerate(Atoms):
2380            Inv,M,T,C,U = SyOps[i]
2381            XYZ.append(np.array(atom[1:4]))
2382            XYZ[-1] = Inv*(np.inner(M,np.array(XYZ[-1]))+T)+C+U
2383            XYZ[-1] = np.inner(Amat,XYZ[-1]).T
2384        V1 = XYZ[1]-XYZ[0]
2385        return np.sqrt(np.sum(V1**2))
2386       
2387    SyOps = []
2388    names = []
2389    for i,atom in enumerate(Oatoms):
2390        names += atom[-1]
2391        Op,unit = Atoms[i][-1]
2392        inv = Op//abs(Op)
2393        m,t = SGData['SGOps'][abs(Op)%100-1]
2394        c = SGData['SGCen'][abs(Op)//100]
2395        SyOps.append([inv,m,t,c,unit])
2396    Dist = calcDist(Oatoms,SyOps,Amat)
2397   
2398    sig = -0.001
2399    if 'covMatrix' in covData:
2400        dx = .00001
2401        dadx = np.zeros(6)
2402        for i in range(6):
2403            ia = i//3
2404            ix = i%3
2405            Oatoms[ia][ix+1] += dx
2406            a0 = calcDist(Oatoms,SyOps,Amat)
2407            Oatoms[ia][ix+1] -= 2*dx
2408            dadx[i] = (calcDist(Oatoms,SyOps,Amat)-a0)/(2.*dx)
2409        covMatrix = covData['covMatrix']
2410        varyList = covData['varyList']
2411        DistVcov = getVCov(names,varyList,covMatrix)
2412        sig = np.sqrt(np.inner(dadx,np.inner(DistVcov,dadx)))
2413        if sig < 0.001:
2414            sig = -0.001
2415   
2416    return Dist,sig
2417
2418def GetAngleSig(Oatoms,Atoms,Amat,SGData,covData={}):
2419    '''default doc string
2420   
2421    :param type name: description
2422   
2423    :returns: type name: description
2424   
2425    '''
2426
2427    def calcAngle(Atoms,SyOps,Amat):
2428        XYZ = []
2429        for i,atom in enumerate(Atoms):
2430            Inv,M,T,C,U = SyOps[i]
2431            XYZ.append(np.array(atom[1:4]))
2432            XYZ[-1] = Inv*(np.inner(M,np.array(XYZ[-1]))+T)+C+U
2433            XYZ[-1] = np.inner(Amat,XYZ[-1]).T
2434        V1 = XYZ[1]-XYZ[0]
2435        V1 /= np.sqrt(np.sum(V1**2))
2436        V2 = XYZ[1]-XYZ[2]
2437        V2 /= np.sqrt(np.sum(V2**2))
2438        V3 = V2-V1
2439        cang = min(1.,max((2.-np.sum(V3**2))/2.,-1.))
2440        return acosd(cang)
2441
2442    SyOps = []
2443    names = []
2444    for i,atom in enumerate(Oatoms):
2445        names += atom[-1]
2446        Op,unit = Atoms[i][-1]
2447        inv = Op//abs(Op)
2448        m,t = SGData['SGOps'][abs(Op)%100-1]
2449        c = SGData['SGCen'][abs(Op)//100]
2450        SyOps.append([inv,m,t,c,unit])
2451    Angle = calcAngle(Oatoms,SyOps,Amat)
2452   
2453    sig = -0.01
2454    if 'covMatrix' in covData:
2455        dx = .00001
2456        dadx = np.zeros(9)
2457        for i in range(9):
2458            ia = i//3
2459            ix = i%3
2460            Oatoms[ia][ix+1] += dx
2461            a0 = calcAngle(Oatoms,SyOps,Amat)
2462            Oatoms[ia][ix+1] -= 2*dx
2463            dadx[i] = (calcAngle(Oatoms,SyOps,Amat)-a0)/(2.*dx)
2464        covMatrix = covData['covMatrix']
2465        varyList = covData['varyList']
2466        AngVcov = getVCov(names,varyList,covMatrix)
2467        sig = np.sqrt(np.inner(dadx,np.inner(AngVcov,dadx)))
2468        if sig < 0.01:
2469            sig = -0.01
2470   
2471    return Angle,sig
2472
2473def GetTorsionSig(Oatoms,Atoms,Amat,SGData,covData={}):
2474    '''default doc string
2475   
2476    :param type name: description
2477   
2478    :returns: type name: description
2479   
2480    '''
2481
2482    def calcTorsion(Atoms,SyOps,Amat):
2483       
2484        XYZ = []
2485        for i,atom in enumerate(Atoms):
2486            Inv,M,T,C,U = SyOps[i]
2487            XYZ.append(np.array(atom[1:4]))
2488            XYZ[-1] = Inv*(np.inner(M,np.array(XYZ[-1]))+T)+C+U
2489            XYZ[-1] = np.inner(Amat,XYZ[-1]).T
2490        V1 = XYZ[1]-XYZ[0]
2491        V2 = XYZ[2]-XYZ[1]
2492        V3 = XYZ[3]-XYZ[2]
2493        V1 /= np.sqrt(np.sum(V1**2))
2494        V2 /= np.sqrt(np.sum(V2**2))
2495        V3 /= np.sqrt(np.sum(V3**2))
2496        M = np.array([V1,V2,V3])
2497        D = nl.det(M)
2498        P12 = np.dot(V1,V2)
2499        P13 = np.dot(V1,V3)
2500        P23 = np.dot(V2,V3)
2501        Tors = acosd((P12*P23-P13)/(np.sqrt(1.-P12**2)*np.sqrt(1.-P23**2)))*D/abs(D)
2502        return Tors
2503           
2504    SyOps = []
2505    names = []
2506    for i,atom in enumerate(Oatoms):
2507        names += atom[-1]
2508        Op,unit = Atoms[i][-1]
2509        inv = Op//abs(Op)
2510        m,t = SGData['SGOps'][abs(Op)%100-1]
2511        c = SGData['SGCen'][abs(Op)//100]
2512        SyOps.append([inv,m,t,c,unit])
2513    Tors = calcTorsion(Oatoms,SyOps,Amat)
2514   
2515    sig = -0.01
2516    if 'covMatrix' in covData:
2517        dx = .00001
2518        dadx = np.zeros(12)
2519        for i in range(12):
2520            ia = i//3
2521            ix = i%3
2522            Oatoms[ia][ix+1] -= dx
2523            a0 = calcTorsion(Oatoms,SyOps,Amat)
2524            Oatoms[ia][ix+1] += 2*dx
2525            dadx[i] = (calcTorsion(Oatoms,SyOps,Amat)-a0)/(2.*dx)
2526            Oatoms[ia][ix+1] -= dx           
2527        covMatrix = covData['covMatrix']
2528        varyList = covData['varyList']
2529        TorVcov = getVCov(names,varyList,covMatrix)
2530        sig = np.sqrt(np.inner(dadx,np.inner(TorVcov,dadx)))
2531        if sig < 0.01:
2532            sig = -0.01
2533   
2534    return Tors,sig
2535       
2536def GetDATSig(Oatoms,Atoms,Amat,SGData,covData={}):
2537    '''default doc string
2538   
2539    :param type name: description
2540   
2541    :returns: type name: description
2542   
2543    '''
2544
2545    def calcDist(Atoms,SyOps,Amat):
2546        XYZ = []
2547        for i,atom in enumerate(Atoms):
2548            Inv,M,T,C,U = SyOps[i]
2549            XYZ.append(np.array(atom[1:4]))
2550            XYZ[-1] = Inv*(np.inner(M,np.array(XYZ[-1]))+T)+C+U
2551            XYZ[-1] = np.inner(Amat,XYZ[-1]).T
2552        V1 = XYZ[1]-XYZ[0]
2553        return np.sqrt(np.sum(V1**2))
2554       
2555    def calcAngle(Atoms,SyOps,Amat):
2556        XYZ = []
2557        for i,atom in enumerate(Atoms):
2558            Inv,M,T,C,U = SyOps[i]
2559            XYZ.append(np.array(atom[1:4]))
2560            XYZ[-1] = Inv*(np.inner(M,np.array(XYZ[-1]))+T)+C+U
2561            XYZ[-1] = np.inner(Amat,XYZ[-1]).T
2562        V1 = XYZ[1]-XYZ[0]
2563        V1 /= np.sqrt(np.sum(V1**2))
2564        V2 = XYZ[1]-XYZ[2]
2565        V2 /= np.sqrt(np.sum(V2**2))
2566        V3 = V2-V1
2567        cang = min(1.,max((2.-np.sum(V3**2))/2.,-1.))
2568        return acosd(cang)
2569
2570    def calcTorsion(Atoms,SyOps,Amat):
2571       
2572        XYZ = []
2573        for i,atom in enumerate(Atoms):
2574            Inv,M,T,C,U = SyOps[i]
2575            XYZ.append(np.array(atom[1:4]))
2576            XYZ[-1] = Inv*(np.inner(M,np.array(XYZ[-1]))+T)+C+U
2577            XYZ[-1] = np.inner(Amat,XYZ[-1]).T
2578        V1 = XYZ[1]-XYZ[0]
2579        V2 = XYZ[2]-XYZ[1]
2580        V3 = XYZ[3]-XYZ[2]
2581        V1 /= np.sqrt(np.sum(V1**2))
2582        V2 /= np.sqrt(np.sum(V2**2))
2583        V3 /= np.sqrt(np.sum(V3**2))
2584        M = np.array([V1,V2,V3])
2585        D = nl.det(M)
2586        P12 = np.dot(V1,V2)
2587        P13 = np.dot(V1,V3)
2588        P23 = np.dot(V2,V3)
2589        Tors = acosd((P12*P23-P13)/(np.sqrt(1.-P12**2)*np.sqrt(1.-P23**2)))*D/abs(D)
2590        return Tors
2591           
2592    SyOps = []
2593    names = []
2594    for i,atom in enumerate(Oatoms):
2595        names += atom[-1]
2596        Op,unit = Atoms[i][-1]
2597        inv = Op//abs(Op)
2598        m,t = SGData['SGOps'][abs(Op)%100-1]
2599        c = SGData['SGCen'][abs(Op)//100]
2600        SyOps.append([inv,m,t,c,unit])
2601    M = len(Oatoms)
2602    if M == 2:
2603        Val = calcDist(Oatoms,SyOps,Amat)
2604    elif M == 3:
2605        Val = calcAngle(Oatoms,SyOps,Amat)
2606    else:
2607        Val = calcTorsion(Oatoms,SyOps,Amat)
2608   
2609    sigVals = [-0.001,-0.01,-0.01]
2610    sig = sigVals[M-3]
2611    if 'covMatrix' in covData:
2612        dx = .00001
2613        N = M*3
2614        dadx = np.zeros(N)
2615        for i in range(N):
2616            ia = i//3
2617            ix = i%3
2618            Oatoms[ia][ix+1] += dx
2619            if M == 2:
2620                a0 = calcDist(Oatoms,SyOps,Amat)
2621            elif M == 3:
2622                a0 = calcAngle(Oatoms,SyOps,Amat)
2623            else:
2624                a0 = calcTorsion(Oatoms,SyOps,Amat)
2625            Oatoms[ia][ix+1] -= 2*dx
2626            if M == 2:
2627                dadx[i] = (calcDist(Oatoms,SyOps,Amat)-a0)/(2.*dx)               
2628            elif M == 3:
2629                dadx[i] = (calcAngle(Oatoms,SyOps,Amat)-a0)/(2.*dx)               
2630            else:
2631                dadx[i] = (calcTorsion(Oatoms,SyOps,Amat)-a0)/(2.*dx)
2632        covMatrix = covData['covMatrix']
2633        varyList = covData['varyList']
2634        Vcov = getVCov(names,varyList,covMatrix)
2635        sig = np.sqrt(np.inner(dadx,np.inner(Vcov,dadx)))
2636        if sig < sigVals[M-3]:
2637            sig = sigVals[M-3]
2638   
2639    return Val,sig
2640       
2641def ValEsd(value,esd=0,nTZ=False):
2642    '''Format a floating point number with a given level of precision or
2643    with in crystallographic format with a "esd", as value(esd). If esd is
2644    negative the number is formatted with the level of significant figures
2645    appropriate if abs(esd) were the esd, but the esd is not included.
2646    if the esd is zero, approximately 6 significant figures are printed.
2647    nTZ=True causes "extra" zeros to be removed after the decimal place.
2648    for example:
2649
2650      * "1.235(3)" for value=1.2346 & esd=0.003
2651      * "1.235(3)e4" for value=12346. & esd=30
2652      * "1.235(3)e6" for value=0.12346e7 & esd=3000
2653      * "1.235" for value=1.2346 & esd=-0.003
2654      * "1.240" for value=1.2395 & esd=-0.003
2655      * "1.24" for value=1.2395 & esd=-0.003 with nTZ=True
2656      * "1.23460" for value=1.2346 & esd=0.0
2657
2658    :param float value: number to be formatted
2659    :param float esd: uncertainty or if esd < 0, specifies level of
2660      precision to be shown e.g. esd=-0.01 gives 2 places beyond decimal
2661    :param bool nTZ: True to remove trailing zeros (default is False)
2662    :returns: value(esd) or value as a string
2663
2664    '''
2665    # Note: this routine is Python 3 compatible -- I think
2666    cutoff = 3.16228    #=(sqrt(10); same as old GSAS   was 1.95
2667    if math.isnan(value): # invalid value, bail out
2668        return '?'
2669    if math.isnan(esd): # invalid esd, treat as zero
2670        esd = 0
2671        esdoff = 5
2672#    if esd < 1.e-5:
2673#        esd = 0
2674#        esdoff = 5
2675    elif esd != 0:
2676        # transform the esd to a one or two digit integer
2677        l = math.log10(abs(esd)) % 1.
2678        if l < math.log10(cutoff): l+= 1.       
2679        intesd = int(round(10**l)) # esd as integer
2680        # determine the number of digits offset for the esd
2681        esdoff = int(round(math.log10(intesd*1./abs(esd))))
2682    else:
2683        esdoff = 5
2684    valoff = 0
2685    if abs(value) < abs(esdoff): # value is effectively zero
2686        pass
2687    elif esdoff < 0 or abs(value) > 1.0e6 or abs(value) < 1.0e-4: # use scientific notation
2688        # where the digit offset is to the left of the decimal place or where too many
2689        # digits are needed
2690        if abs(value) > 1:
2691            valoff = int(math.log10(abs(value)))
2692        elif abs(value) > 0:
2693            valoff = int(math.log10(abs(value))-0.9999999)
2694        else:
2695            valoff = 0
2696    if esd != 0:
2697        if valoff+esdoff < 0:
2698            valoff = esdoff = 0
2699        out = ("{:."+str(valoff+esdoff)+"f}").format(value/10**valoff) # format the value
2700    elif valoff != 0: # esd = 0; exponential notation ==> esdoff decimal places
2701        out = ("{:."+str(esdoff)+"f}").format(value/10**valoff) # format the value
2702    else: # esd = 0; non-exponential notation ==> esdoff+1 significant digits
2703        if abs(value) > 0:           
2704            extra = -math.log10(abs(value))
2705        else:
2706            extra = 0
2707        if extra > 0: extra += 1
2708        out = ("{:."+str(max(0,esdoff+int(extra)))+"f}").format(value) # format the value
2709    if esd > 0:
2710        out += ("({:d})").format(intesd)  # add the esd
2711    elif nTZ and '.' in out:
2712        out = out.rstrip('0')  # strip zeros to right of decimal
2713        out = out.rstrip('.')  # and decimal place when not needed
2714    if valoff != 0:
2715        out += ("e{:d}").format(valoff) # add an exponent, when needed
2716    return out
2717   
2718###############################################################################
2719##### Protein validation - "ERRATV2" analysis
2720###############################################################################
2721
2722def validProtein(Phase,old):
2723   
2724    def sumintact(intact):
2725        return {'CC':intact['CC'],'NN':intact['NN'],'OO':intact['OO'],
2726        'CN':(intact['CN']+intact['NC']),'CO':(intact['CO']+intact['OC']),
2727        'NO':(intact['NO']+intact['ON'])}
2728       
2729    resNames = ['ALA','ARG','ASN','ASP','CYS','GLN','GLU','GLY','HIS','ILE',
2730        'LEU','LYS','MET','PHE','PRO','SER','THR','TRP','TYR','VAL','MSE']
2731# data from errat.f
2732    b1_old = np.array([ 
2733        [1154.343,  600.213, 1051.018, 1132.885,  960.738],
2734        [600.213, 1286.818, 1282.042,  957.156,  612.789],
2735        [1051.018, 1282.042, 3519.471,  991.974, 1226.491],
2736        [1132.885,  957.156,  991.974, 1798.672,  820.355],
2737        [960.738,  612.789, 1226.491,  820.355, 2428.966]
2738        ])
2739    avg_old = np.array([ 0.225, 0.281, 0.071, 0.237, 0.044])    #Table 1 3.5A Obsd. Fr. p 1513
2740# data taken from erratv2.ccp
2741    b1 = np.array([
2742          [5040.279078850848200,        3408.805141583649400,   4152.904423767300600,   4236.200004171890200,   5054.781210204625500], 
2743          [3408.805141583648900,        8491.906094010220800,   5958.881777877950300,   1521.387352718486200,   4304.078200827221700], 
2744          [4152.904423767301500,        5958.881777877952100,   7637.167089335050100,   6620.715738223072500,   5287.691183798410700], 
2745          [4236.200004171890200,        1521.387352718486200,   6620.715738223072500,   18368.343774298410000,  4050.797811118806700], 
2746          [5054.781210204625500,        4304.078200827220800,   5287.691183798409800,   4050.797811118806700,   6666.856740479164700]])
2747    avg = np.array([0.192765509919262, 0.195575208778518, 0.275322406824210, 0.059102357035642, 0.233154192767480])
2748    General = Phase['General']
2749    Amat,Bmat = G2lat.cell2AB(General['Cell'][1:7])
2750    cx,ct,cs,cia = General['AtomPtrs']
2751    Atoms = Phase['Atoms']
2752    cartAtoms = []
2753    xyzmin = 999.*np.ones(3)
2754    xyzmax = -999.*np.ones(3)
2755    #select residue atoms,S,Se --> O make cartesian
2756    for atom in Atoms:
2757        if atom[1] in resNames:
2758            cartAtoms.append(atom[:cx+3])
2759            if atom[4].strip() in ['S','Se']:
2760                if not old:
2761                    continue        #S,Se skipped for erratv2?
2762                cartAtoms[-1][3] = 'Os'
2763                cartAtoms[-1][4] = 'O'
2764            cartAtoms[-1][cx:cx+3] = np.inner(Amat,cartAtoms[-1][cx:cx+3])
2765            cartAtoms[-1].append(atom[cia+8])
2766    XYZ = np.array([atom[cx:cx+3] for atom in cartAtoms])
2767    xyzmin = np.array([np.min(XYZ.T[i]) for i in [0,1,2]])
2768    xyzmax = np.array([np.max(XYZ.T[i]) for i in [0,1,2]])
2769    nbox = list(np.array(np.ceil((xyzmax-xyzmin)/4.),dtype=int))+[15,]
2770    Boxes = np.zeros(nbox,dtype=int)
2771    iBox = np.array([np.trunc((XYZ.T[i]-xyzmin[i])/4.) for i in [0,1,2]],dtype=int).T
2772    for ib,box in enumerate(iBox):  #put in a try for too many atoms in box (IndexError)?
2773        try:
2774            Boxes[box[0],box[1],box[2],0] += 1
2775            Boxes[box[0],box[1],box[2],Boxes[box[0],box[1],box[2],0]] = ib
2776        except IndexError:
2777            G2fil.G2Print('Error: too many atoms in box' )
2778            continue
2779    #Box content checks with errat.f $ erratv2.cpp ibox1 arrays
2780    indices = (-1,0,1)
2781    Units = np.array([[h,k,l] for h in indices for k in indices for l in indices]) 
2782    dsmax = 3.75**2
2783    if old:
2784        dsmax = 3.5**2
2785    chains = []
2786    resIntAct = []
2787    chainIntAct = []
2788    res = []
2789    resNames = []
2790    resIDs = {}
2791    resname = []
2792    resID = {}
2793    newChain = True
2794    intact = {'CC':0,'CN':0,'CO':0,'NN':0,'NO':0,'OO':0,'NC':0,'OC':0,'ON':0}
2795    for ia,atom in enumerate(cartAtoms):
2796        jntact = {'CC':0,'CN':0,'CO':0,'NN':0,'NO':0,'OO':0,'NC':0,'OC':0,'ON':0}
2797        if atom[2] not in chains:   #get chain id & save residue sequence from last chain
2798            chains.append(atom[2])
2799            if len(resIntAct):
2800                resIntAct.append(sumintact(intact))
2801                chainIntAct.append(resIntAct)
2802                resNames += resname
2803                resIDs.update(resID)
2804                res = []
2805                resname = []
2806                resID = {}
2807                resIntAct = []
2808                intact = {'CC':0,'CN':0,'CO':0,'NN':0,'NO':0,'OO':0,'NC':0,'OC':0,'ON':0}
2809                newChain = True
2810        if atom[0] not in res:  #new residue, get residue no.
2811            if res and int(res[-1]) != int(atom[0])-1:  #a gap in chain - not new chain
2812                intact = {'CC':0,'CN':0,'CO':0,'NN':0,'NO':0,'OO':0,'NC':0,'OC':0,'ON':0}
2813                ires = int(res[-1])
2814                for i in range(int(atom[0])-ires-1):
2815                    res.append(str(ires+i+1))
2816                    resname.append('')
2817                    resIntAct.append(sumintact(intact))
2818            res.append(atom[0])
2819            name = '%s-%s%s'%(atom[2],atom[0],atom[1])
2820            resname.append(name)
2821            resID[name] = atom[-1]
2822            if not newChain:
2823                resIntAct.append(sumintact(intact))
2824            intact = {'CC':0,'CN':0,'CO':0,'NN':0,'NO':0,'OO':0,'NC':0,'OC':0,'ON':0}
2825            newChain = False
2826        ibox = iBox[ia]         #box location of atom
2827        tgts = []
2828        for unit in Units:      #assemble list of all possible target atoms
2829            jbox = ibox+unit
2830            if np.all(jbox>=0) and np.all((jbox-nbox[:3])<0):               
2831                tgts += list(Boxes[jbox[0],jbox[1],jbox[2]])
2832        tgts = list(set(tgts))
2833        tgts = [tgt for tgt in tgts if atom[:3] != cartAtoms[tgt][:3]]    #exclude same residue
2834        tgts = [tgt for tgt in tgts if np.sum((XYZ[ia]-XYZ[tgt])**2) < dsmax]
2835        ires = int(atom[0])
2836        if old:
2837            if atom[3].strip() == 'C':
2838                tgts = [tgt for tgt in tgts if not (cartAtoms[tgt][3].strip() == 'N' and int(cartAtoms[tgt][0]) in [ires-1,ires+1])]
2839            elif atom[3].strip() == 'N':
2840                tgts = [tgt for tgt in tgts if not (cartAtoms[tgt][3].strip() in ['C','CA'] and int(cartAtoms[tgt][0]) in [ires-1,ires+1])]
2841            elif atom[3].strip() == 'CA':
2842                tgts = [tgt for tgt in tgts if not (cartAtoms[tgt][3].strip() == 'N' and int(cartAtoms[tgt][0]) in [ires-1,ires+1])]
2843        else:
2844            tgts = [tgt for tgt in tgts if not int(cartAtoms[tgt][0]) in [ires+1,ires+2,ires+3,ires+4,ires+5,ires+6,ires+7,ires+8]]
2845            if atom[3].strip() == 'C':
2846                tgts = [tgt for tgt in tgts if not (cartAtoms[tgt][3].strip() == 'N' and int(cartAtoms[tgt][0]) == ires+1)]
2847            elif atom[3].strip() == 'N':
2848                tgts = [tgt for tgt in tgts if not (cartAtoms[tgt][3].strip() == 'C' and int(cartAtoms[tgt][0]) == ires-1)]
2849        for tgt in tgts:
2850            dsqt = np.sqrt(np.sum((XYZ[ia]-XYZ[tgt])**2))
2851            mult = 1.0
2852            if dsqt > 3.25 and not old:
2853                mult = 2.*(3.75-dsqt)
2854            intype = atom[4].strip()+cartAtoms[tgt][4].strip()
2855            if 'S' not in intype:
2856                intact[intype] += mult
2857                jntact[intype] += mult
2858#        print ia,atom[0]+atom[1]+atom[3],tgts,jntact['CC'],jntact['CN']+jntact['NC'],jntact['CO']+jntact['OC'],jntact['NN'],jntact['NO']+jntact['ON']
2859    resNames += resname
2860    resIDs.update(resID)
2861    resIntAct.append(sumintact(intact))
2862    chainIntAct.append(resIntAct)
2863    chainProb = []
2864    for ich,chn in enumerate(chains):
2865        IntAct = chainIntAct[ich]
2866        nRes = len(IntAct)
2867        Probs = [0.,0.,0.,0.]   #skip 1st 4 residues in chain
2868        for i in range(4,nRes-4):
2869            if resNames[i]:
2870                mtrx = np.zeros(5)
2871                summ = 0.
2872                for j in range(i-4,i+5):
2873                    summ += np.sum(np.array(list(IntAct[j].values())))
2874                    if old:
2875                        mtrx[0] += IntAct[j]['CC']
2876                        mtrx[1] += IntAct[j]['CO']
2877                        mtrx[2] += IntAct[j]['NN']
2878                        mtrx[3] += IntAct[j]['NO']
2879                        mtrx[4] += IntAct[j]['OO']
2880                    else:
2881                        mtrx[0] += IntAct[j]['CC']
2882                        mtrx[1] += IntAct[j]['CN']
2883                        mtrx[2] += IntAct[j]['CO']
2884                        mtrx[3] += IntAct[j]['NN']
2885                        mtrx[4] += IntAct[j]['NO']
2886                mtrx /= summ
2887    #            print i+1,mtrx*summ
2888                if old:
2889                    mtrx -= avg_old
2890                    prob = np.inner(np.inner(mtrx,b1_old),mtrx)
2891                else:
2892                    mtrx -= avg
2893                    prob = np.inner(np.inner(mtrx,b1),mtrx)
2894            else:       #skip the gaps
2895                prob = 0.0
2896            Probs.append(prob)
2897        Probs += 4*[0.,]        #skip last 4 residues in chain
2898        chainProb += Probs
2899    return resNames,chainProb,resIDs
2900   
2901################################################################################
2902##### Texture fitting stuff
2903################################################################################
2904
2905def FitTexture(General,Gangls,refData,keyList,pgbar):
2906    import pytexture as ptx
2907    ptx.pyqlmninit()            #initialize fortran arrays for spherical harmonics
2908   
2909    def printSpHarm(textureData,SHtextureSig):
2910        Tindx = 1.0
2911        Tvar = 0.0
2912        print ('\n Spherical harmonics texture: Order:' + str(textureData['Order']))
2913        names = ['omega','chi','phi']
2914        namstr = '  names :'
2915        ptstr =  '  values:'
2916        sigstr = '  esds  :'
2917        for name in names:
2918            namstr += '%12s'%('Sample '+name)
2919            ptstr += '%12.3f'%(textureData['Sample '+name][1])
2920            if 'Sample '+name in SHtextureSig:
2921                sigstr += '%12.3f'%(SHtextureSig['Sample '+name])
2922            else:
2923                sigstr += 12*' '
2924        print (namstr)
2925        print (ptstr)
2926        print (sigstr)
2927        print ('\n Texture coefficients:')
2928        SHcoeff = textureData['SH Coeff'][1]
2929        SHkeys = list(SHcoeff.keys())
2930        nCoeff = len(SHcoeff)
2931        nBlock = nCoeff//10+1
2932        iBeg = 0
2933        iFin = min(iBeg+10,nCoeff)
2934        for block in range(nBlock):
2935            namstr = '  names :'
2936            ptstr =  '  values:'
2937            sigstr = '  esds  :'
2938            for name in SHkeys[iBeg:iFin]:
2939                if 'C' in name:
2940                    l = 2.0*eval(name.strip('C'))[0]+1
2941                    Tindx += SHcoeff[name]**2/l
2942                    namstr += '%12s'%(name)
2943                    ptstr += '%12.3f'%(SHcoeff[name])
2944                    if name in SHtextureSig:
2945                        Tvar += (2.*SHcoeff[name]*SHtextureSig[name]/l)**2
2946                        sigstr += '%12.3f'%(SHtextureSig[name])
2947                    else:
2948                        sigstr += 12*' '
2949            print (namstr)
2950            print (ptstr)
2951            print (sigstr)
2952            iBeg += 10
2953            iFin = min(iBeg+10,nCoeff)
2954        print(' Texture index J = %.3f(%d)'%(Tindx,int(1000*np.sqrt(Tvar))))
2955           
2956    def Dict2Values(parmdict, varylist):
2957        '''Use before call to leastsq to setup list of values for the parameters
2958        in parmdict, as selected by key in varylist'''
2959        return [parmdict[key] for key in varylist] 
2960       
2961    def Values2Dict(parmdict, varylist, values):
2962        ''' Use after call to leastsq to update the parameter dictionary with
2963        values corresponding to keys in varylist'''
2964        parmdict.update(list(zip(varylist,values)))
2965       
2966    def errSpHarm(values,SGData,cell,Gangls,shModel,refData,parmDict,varyList,pgbar):
2967        parmDict.update(list(zip(varyList,values)))
2968        Mat = np.empty(0)
2969        sumObs = 0
2970        Sangls = [parmDict['Sample '+'omega'],parmDict['Sample '+'chi'],parmDict['Sample '+'phi']]
2971        for hist in Gangls.keys():
2972            Refs = refData[hist]
2973            Refs[:,5] = np.where(Refs[:,5]>0.,Refs[:,5],0.)
2974            wt = 1./np.sqrt(np.fmax(Refs[:,4],.25))
2975#            wt = 1./np.max(Refs[:,4],.25)
2976            sumObs += np.sum(wt*Refs[:,5])
2977            Refs[:,6] = 1.
2978            H = Refs[:,:3]
2979            phi,beta = G2lat.CrsAng(H,cell,SGData)
2980            psi,gam,x,x = G2lat.SamAng(Refs[:,3]/2.,Gangls[hist],Sangls,False) #assume not Bragg-Brentano!
2981            for item in parmDict:
2982                if 'C' in item:
2983                    L,M,N = eval(item.strip('C'))
2984                    Kcl = G2lat.GetKcl(L,N,SGData['SGLaue'],phi,beta)
2985                    Ksl,x,x = G2lat.GetKsl(L,M,shModel,psi,gam)
2986                    Lnorm = G2lat.Lnorm(L)
2987                    Refs[:,6] += parmDict[item]*Lnorm*Kcl*Ksl
2988            mat = wt*(Refs[:,5]-Refs[:,6])
2989            Mat = np.concatenate((Mat,mat))
2990        sumD = np.sum(np.abs(Mat))
2991        R = min(100.,100.*sumD/sumObs)
2992        pgbar.Raise()
2993        pgbar.Update(R,newmsg='Residual = %5.2f'%(R))
2994        print (' Residual: %.3f%%'%(R))
2995        return Mat
2996       
2997    def dervSpHarm(values,SGData,cell,Gangls,shModel,refData,parmDict,varyList,pgbar):
2998        Mat = np.empty(0)
2999        Sangls = [parmDict['Sample omega'],parmDict['Sample chi'],parmDict['Sample phi']]
3000        for hist in Gangls.keys():
3001            mat = np.zeros((len(varyList),len(refData[hist])))
3002            Refs = refData[hist]
3003            H = Refs[:,:3]
3004            wt = 1./np.sqrt(np.fmax(Refs[:,4],.25))
3005#            wt = 1./np.max(Refs[:,4],.25)
3006            phi,beta = G2lat.CrsAng(H,cell,SGData)
3007            psi,gam,dPdA,dGdA = G2lat.SamAng(Refs[:,3]/2.,Gangls[hist],Sangls,False) #assume not Bragg-Brentano!
3008            for j,item in enumerate(varyList):
3009                if 'C' in item:
3010                    L,M,N = eval(item.strip('C'))
3011                    Kcl = G2lat.GetKcl(L,N,SGData['SGLaue'],phi,beta)
3012                    Ksl,dKdp,dKdg = G2lat.GetKsl(L,M,shModel,psi,gam)
3013                    Lnorm = G2lat.Lnorm(L)
3014                    mat[j] = -wt*Lnorm*Kcl*Ksl
3015                    for k,itema in enumerate(['Sample omega','Sample chi','Sample phi']):
3016                        try:
3017                            l = varyList.index(itema)
3018                            mat[l] -= parmDict[item]*wt*Lnorm*Kcl*(dKdp*dPdA[k]+dKdg*dGdA[k])
3019                        except ValueError:
3020                            pass
3021            if len(Mat):
3022                Mat = np.concatenate((Mat,mat.T))
3023            else:
3024                Mat = mat.T
3025        print ('deriv')
3026        return Mat
3027
3028    print (' Fit texture for '+General['Name'])
3029    SGData = General['SGData']
3030    cell = General['Cell'][1:7]
3031    Texture = General['SH Texture']
3032    if not Texture['Order']:
3033        return 'No spherical harmonics coefficients'
3034    varyList = []
3035    parmDict = copy.copy(Texture['SH Coeff'][1])
3036    for item in ['Sample omega','Sample chi','Sample phi']:
3037        parmDict[item] = Texture[item][1]
3038        if Texture[item][0]:
3039            varyList.append(item)
3040    if Texture['SH Coeff'][0]:
3041        varyList += list(Texture['SH Coeff'][1].keys())
3042    while True:
3043        begin = time.time()
3044        values =  np.array(Dict2Values(parmDict, varyList))
3045        result = so.leastsq(errSpHarm,values,Dfun=dervSpHarm,full_output=True,ftol=1.e-6,
3046            args=(SGData,cell,Gangls,Texture['Model'],refData,parmDict,varyList,pgbar))
3047        ncyc = int(result[2]['nfev']//2)
3048        if ncyc:
3049            runtime = time.time()-begin   
3050            chisq = np.sum(result[2]['fvec']**2)
3051            Values2Dict(parmDict, varyList, result[0])
3052            GOF = chisq/(len(result[2]['fvec'])-len(varyList))       #reduced chi^2
3053            G2fil.G2Print ('Number of function calls: %d Number of observations: %d Number of parameters: %d'%(result[2]['nfev'],len(result[2]['fvec']),len(varyList)))
3054            G2fil.G2Print ('refinement time = %8.3fs, %8.3fs/cycle'%(runtime,runtime/ncyc))
3055            try:
3056                sig = np.sqrt(np.diag(result[1])*GOF)
3057                if np.any(np.isnan(sig)):
3058                    G2fil.G2Print ('*** Least squares aborted - some invalid esds possible ***', mode='error')
3059                break                   #refinement succeeded - finish up!
3060            except ValueError:          #result[1] is None on singular matrix
3061                G2fil.G2Print ('**** Refinement failed - singular matrix ****', mode='error')
3062                return None
3063        else:
3064            break
3065   
3066    if ncyc:
3067        for parm in parmDict:
3068            if 'C' in parm:
3069                Texture['SH Coeff'][1][parm] = parmDict[parm]
3070            else:
3071                Texture[parm][1] = parmDict[parm] 
3072        sigDict = dict(zip(varyList,sig))
3073        printSpHarm(Texture,sigDict)
3074       
3075    return None
3076   
3077################################################################################
3078##### Fourier & charge flip stuff
3079################################################################################
3080
3081def adjHKLmax(SGData,Hmax):
3082    '''default doc string
3083   
3084    :param type name: description
3085   
3086    :returns: type name: description
3087   
3088    '''
3089    if SGData['SGLaue'] in ['3','3m1','31m','6/m','6/mmm']:
3090        Hmax[0] = int(math.ceil(Hmax[0]/6.))*6
3091        Hmax[1] = int(math.ceil(Hmax[1]/6.))*6
3092        Hmax[2] = int(math.ceil(Hmax[2]/4.))*4
3093    else:
3094        Hmax[0] = int(math.ceil(Hmax[0]/4.))*4
3095        Hmax[1] = int(math.ceil(Hmax[1]/4.))*4
3096        Hmax[2] = int(math.ceil(Hmax[2]/4.))*4
3097
3098def OmitMap(data,reflDict,pgbar=None):
3099    '''default doc string
3100   
3101    :param type name: description
3102   
3103    :returns: type name: description
3104   
3105    '''
3106    generalData = data['General']
3107    if not generalData['Map']['MapType']:
3108        G2fil.G2Print ('**** ERROR - Fourier map not defined')
3109        return
3110    mapData = generalData['Map']
3111    dmin = mapData['GridStep']*2.
3112    SGData = generalData['SGData']
3113    SGMT = np.array([ops[0].T for ops in SGData['SGOps']])
3114    SGT = np.array([ops[1] for ops in SGData['SGOps']])
3115    cell = generalData['Cell'][1:8]       
3116    A = G2lat.cell2A(cell[:6])
3117    Hmax = np.asarray(G2lat.getHKLmax(dmin,SGData,A),dtype='i')+1
3118    adjHKLmax(SGData,Hmax)
3119    Fhkl = np.zeros(shape=2*Hmax,dtype='c16')
3120    time0 = time.time()
3121    for iref,ref in enumerate(reflDict['RefList']):
3122        if ref[4] >= dmin:
3123            Fosq,Fcsq,ph = ref[8:11]
3124            Uniq = np.inner(ref[:3],SGMT)
3125            Phi = np.inner(ref[:3],SGT)
3126            for i,hkl in enumerate(Uniq):        #uses uniq
3127                hkl = np.asarray(hkl,dtype='i')
3128                dp = 360.*Phi[i]                #and phi
3129                a = cosd(ph+dp)
3130                b = sind(ph+dp)
3131                phasep = complex(a,b)
3132                phasem = complex(a,-b)
3133                if '2Fo-Fc' in mapData['MapType']:
3134                    F = 2.*np.sqrt(Fosq)-np.sqrt(Fcsq)
3135                else:
3136                    F = np.sqrt(Fosq)
3137                h,k,l = hkl+Hmax
3138                Fhkl[h,k,l] = F*phasep
3139                h,k,l = -hkl+Hmax
3140                Fhkl[h,k,l] = F*phasem
3141    rho0 = fft.fftn(fft.fftshift(Fhkl))/cell[6]
3142    M = np.mgrid[0:4,0:4,0:4]
3143    blkIds = np.array(list(zip(M[0].flatten(),M[1].flatten(),M[2].flatten())))
3144    iBeg = blkIds*rho0.shape//4
3145    iFin = (blkIds+1)*rho0.shape//4
3146    rho_omit = np.zeros_like(rho0)
3147    nBlk = 0
3148    for iB,iF in zip(iBeg,iFin):
3149        rho1 = np.copy(rho0)
3150        rho1[iB[0]:iF[0],iB[1]:iF[1],iB[2]:iF[2]] = 0.
3151        Fnew = fft.ifftshift(fft.ifftn(rho1))
3152        Fnew = np.where(Fnew,Fnew,1.0)           #avoid divide by zero
3153        phase = Fnew/np.absolute(Fnew)
3154        OFhkl = np.absolute(Fhkl)*phase
3155        rho1 = np.real(fft.fftn(fft.fftshift(OFhkl)))*(1.+0j)
3156        rho_omit[iB[0]:iF[0],iB[1]:iF[1],iB[2]:iF[2]] = np.copy(rho1[iB[0]:iF[0],iB[1]:iF[1],iB[2]:iF[2]])
3157        nBlk += 1
3158        pgbar.Raise()
3159        pgbar.Update(nBlk)
3160    mapData['rho'] = np.real(rho_omit)/cell[6]
3161    mapData['rhoMax'] = max(np.max(mapData['rho']),-np.min(mapData['rho']))
3162    mapData['minmax'] = [np.max(mapData['rho']),np.min(mapData['rho'])]
3163    G2fil.G2Print ('Omit map time: %.4f no. elements: %d dimensions: %s'%(time.time()-time0,Fhkl.size,str(Fhkl.shape)))
3164    return mapData
3165   
3166def FourierMap(data,reflDict):
3167    '''default doc string
3168   
3169    :param type name: description
3170   
3171    :returns: type name: description
3172   
3173    '''
3174    generalData = data['General']
3175    mapData = generalData['Map']
3176    dmin = mapData['GridStep']*2.
3177    SGData = generalData['SGData']
3178    SGMT = np.array([ops[0].T for ops in SGData['SGOps']])
3179    SGT = np.array([ops[1] for ops in SGData['SGOps']])
3180    cell = generalData['Cell'][1:8]       
3181    A = G2lat.cell2A(cell[:6])
3182    Hmax = np.asarray(G2lat.getHKLmax(dmin,SGData,A),dtype='i')+1
3183    adjHKLmax(SGData,Hmax)
3184    Fhkl = np.zeros(shape=2*Hmax,dtype='c16')
3185#    Fhkl[0,0,0] = generalData['F000X']
3186    time0 = time.time()
3187    for iref,ref in enumerate(reflDict['RefList']):
3188        if ref[4] > dmin:
3189            Fosq,Fcsq,ph = ref[8:11]
3190            Uniq = np.inner(ref[:3],SGMT)
3191            Phi = np.inner(ref[:3],SGT)
3192            for i,hkl in enumerate(Uniq):        #uses uniq
3193                hkl = np.asarray(hkl,dtype='i')
3194                dp = 360.*Phi[i]                #and phi
3195                a = cosd(ph+dp)
3196                b = sind(ph+dp)
3197                phasep = complex(a,b)
3198                phasem = complex(a,-b)
3199                if 'Fobs' in mapData['MapType']:
3200                    F = np.where(Fosq>0.,np.sqrt(Fosq),0.)
3201                    h,k,l = hkl+Hmax
3202                    Fhkl[h,k,l] = F*phasep
3203                    h,k,l = -hkl+Hmax
3204                    Fhkl[h,k,l] = F*phasem
3205                elif 'Fcalc' in mapData['MapType']:
3206                    F = np.sqrt(Fcsq)
3207                    h,k,l = hkl+Hmax
3208                    Fhkl[h,k,l] = F*phasep
3209                    h,k,l = -hkl+Hmax
3210                    Fhkl[h,k,l] = F*phasem
3211                elif 'delt-F' in mapData['MapType']:
3212                    dF = np.where(Fosq>0.,np.sqrt(Fosq),0.)-np.sqrt(Fcsq)
3213                    h,k,l = hkl+Hmax
3214                    Fhkl[h,k,l] = dF*phasep
3215                    h,k,l = -hkl+Hmax
3216                    Fhkl[h,k,l] = dF*phasem
3217                elif '2*Fo-Fc' in mapData['MapType']:
3218                    F = 2.*np.where(Fosq>0.,np.sqrt(Fosq),0.)-np.sqrt(Fcsq)
3219                    h,k,l = hkl+Hmax
3220                    Fhkl[h,k,l] = F*phasep
3221                    h,k,l = -hkl+Hmax
3222                    Fhkl[h,k,l] = F*phasem
3223                elif 'Patterson' in mapData['MapType']:
3224                    h,k,l = hkl+Hmax
3225                    Fhkl[h,k,l] = complex(Fosq,0.)
3226                    h,k,l = -hkl+Hmax
3227                    Fhkl[h,k,l] = complex(Fosq,0.)
3228    rho = fft.fftn(fft.fftshift(Fhkl))/cell[6]
3229    G2fil.G2Print ('Fourier map time: %.4f no. elements: %d dimensions: %s'%(time.time()-time0,Fhkl.size,str(Fhkl.shape)))
3230    mapData['Type'] = reflDict['Type']
3231    mapData['rho'] = np.real(rho)
3232    mapData['rhoMax'] = max(np.max(mapData['rho']),-np.min(mapData['rho']))
3233    mapData['minmax'] = [np.max(mapData['rho']),np.min(mapData['rho'])]
3234   
3235def Fourier4DMap(data,reflDict):
3236    '''default doc string
3237   
3238    :param type name: description
3239   
3240    :returns: type name: description
3241   
3242    '''
3243    generalData = data['General']
3244    map4DData = generalData['4DmapData']
3245    mapData = generalData['Map']
3246    dmin = mapData['GridStep']*2.
3247    SGData = generalData['SGData']
3248    SSGData = generalData['SSGData']
3249    SSGMT = np.array([ops[0].T for ops in SSGData['SSGOps']])
3250    SSGT = np.array([ops[1] for ops in SSGData['SSGOps']])
3251    cell = generalData['Cell'][1:8]       
3252    A = G2lat.cell2A(cell[:6])
3253    maxM = 4
3254    Hmax = G2lat.getHKLmax(dmin,SGData,A)+[maxM,]
3255    adjHKLmax(SGData,Hmax)
3256    Hmax = np.asarray(Hmax,dtype='i')+1
3257    Fhkl = np.zeros(shape=2*Hmax,dtype='c16')
3258    time0 = time.time()
3259    for iref,ref in enumerate(reflDict['RefList']):
3260        if ref[5] > dmin:
3261            Fosq,Fcsq,ph = ref[9:12]
3262            Fosq = np.where(Fosq>0.,Fosq,0.)    #can't use Fo^2 < 0
3263            Uniq = np.inner(ref[:4],SSGMT)
3264            Phi = np.inner(ref[:4],SSGT)
3265            for i,hkl in enumerate(Uniq):        #uses uniq
3266                hkl = np.asarray(hkl,dtype='i')
3267                dp = 360.*Phi[i]                #and phi
3268                a = cosd(ph+dp)
3269                b = sind(ph+dp)
3270                phasep = complex(a,b)
3271                phasem = complex(a,-b)
3272                if 'Fobs' in mapData['MapType']:
3273                    F = np.sqrt(Fosq)
3274                    h,k,l,m = hkl+Hmax
3275                    Fhkl[h,k,l,m] = F*phasep
3276                    h,k,l,m = -hkl+Hmax
3277                    Fhkl[h,k,l,m] = F*phasem
3278                elif 'Fcalc' in mapData['MapType']:
3279                    F = np.sqrt(Fcsq)
3280                    h,k,l,m = hkl+Hmax
3281                    Fhkl[h,k,l,m] = F*phasep
3282                    h,k,l,m = -hkl+Hmax
3283                    Fhkl[h,k,l,m] = F*phasem                   
3284                elif 'delt-F' in mapData['MapType']:
3285                    dF = np.sqrt(Fosq)-np.sqrt(Fcsq)
3286                    h,k,l,m = hkl+Hmax
3287                    Fhkl[h,k,l,m] = dF*phasep
3288                    h,k,l,m = -hkl+Hmax
3289                    Fhkl[h,k,l,m] = dF*phasem
3290    SSrho = fft.fftn(fft.fftshift(Fhkl))/cell[6]          #4D map
3291    rho = fft.fftn(fft.fftshift(Fhkl[:,:,:,maxM+1]))/cell[6]    #3D map
3292    map4DData['rho'] = np.real(SSrho)
3293    map4DData['rhoMax'] = max(np.max(map4DData['rho']),-np.min(map4DData['rho']))
3294    map4DData['minmax'] = [np.max(map4DData['rho']),np.min(map4DData['rho'])]
3295    map4DData['Type'] = reflDict['Type']
3296    mapData['Type'] = reflDict['Type']
3297    mapData['rho'] = np.real(rho)
3298    mapData['rhoMax'] = max(np.max(mapData['rho']),-np.min(mapData['rho']))
3299    mapData['minmax'] = [np.max(mapData['rho']),np.min(mapData['rho'])]
3300    G2fil.G2Print ('Fourier map time: %.4f no. elements: %d dimensions: %s'%(time.time()-time0,Fhkl.size,str(Fhkl.shape)))
3301
3302# map printing for testing purposes
3303def printRho(SGLaue,rho,rhoMax):                         
3304    '''default doc string
3305   
3306    :param type name: description
3307   
3308    :returns: type name: description
3309   
3310    '''
3311    dim = len(rho.shape)
3312    if dim == 2:
3313        ix,jy = rho.shape
3314        for j in range(jy):
3315            line = ''
3316            if SGLaue in ['3','3m1','31m','6/m','6/mmm']:
3317                line += (jy-j)*'  '
3318            for i in range(ix):
3319                r = int(100*rho[i,j]/rhoMax)
3320                line += '%4d'%(r)
3321            print (line+'\n')
3322    else:
3323        ix,jy,kz = rho.shape
3324        for k in range(kz):
3325            print ('k = %d'%k)
3326            for j in range(jy):
3327                line = ''
3328                if SGLaue in ['3','3m1','31m','6/m','6/mmm']:
3329                    line += (jy-j)*'  '
3330                for i in range(ix):
3331                    r = int(100*rho[i,j,k]/rhoMax)
3332                    line += '%4d'%(r)
3333                print (line+'\n')
3334## keep this
3335               
3336def findOffset(SGData,A,Fhkl):   
3337    '''default doc string
3338   
3339    :param type name: description
3340   
3341    :returns: type name: description
3342   
3343    '''
3344    if SGData['SpGrp'] == 'P 1':
3345        return [0,0,0]   
3346    hklShape = Fhkl.shape
3347    hklHalf = np.array(hklShape)/2
3348    sortHKL = np.argsort(Fhkl.flatten())
3349    Fdict = {}
3350    for hkl in sortHKL:
3351        HKL = np.unravel_index(hkl,hklShape)
3352        F = Fhkl[HKL[0]][HKL[1]][HKL[2]]
3353        if F == 0.:
3354            break
3355        Fdict['%.6f'%(np.absolute(F))] = hkl
3356    Flist = np.flipud(np.sort(list(Fdict.keys())))
3357    F = str(1.e6)
3358    i = 0
3359    DH = []
3360    Dphi = []
3361    Hmax = 2*np.asarray(G2lat.getHKLmax(3.5,SGData,A),dtype='i')
3362    for F in Flist:
3363        hkl = np.unravel_index(Fdict[F],hklShape)
3364        if np.any(np.abs(hkl-hklHalf)-Hmax > 0):
3365            continue
3366        iabsnt,mulp,Uniq,Phi = G2spc.GenHKLf(list(hkl-hklHalf),SGData)
3367        Uniq = np.array(Uniq,dtype='i')
3368        Phi = np.array(Phi)
3369        Uniq = np.concatenate((Uniq,-Uniq))+hklHalf         # put in Friedel pairs & make as index to Farray
3370        Phi = np.concatenate((Phi,-Phi))                      # and their phase shifts
3371        Fh0 = Fhkl[hkl[0],hkl[1],hkl[2]]
3372        ang0 = np.angle(Fh0,deg=True)/360.
3373        for H,phi in list(zip(Uniq,Phi))[1:]:
3374            ang = (np.angle(Fhkl[int(H[0]),int(H[1]),int(H[2])],deg=True)/360.-phi)
3375            dH = H-hkl
3376            dang = ang-ang0
3377            DH.append(dH)
3378            Dphi.append((dang+.5) % 1.0)
3379        if i > 20 or len(DH) > 30:
3380            break
3381        i += 1
3382    DH = np.array(DH)
3383    G2fil.G2Print (' map offset no.of terms: %d from %d reflections'%(len(DH),len(Flist)))
3384    Dphi = np.array(Dphi)
3385    steps = np.array(hklShape)
3386    X,Y,Z = np.mgrid[0:1:1./steps[0],0:1:1./steps[1],0:1:1./steps[2]]
3387    XYZ = np.array(list(zip(X.flatten(),Y.flatten(),Z.flatten())))
3388    Dang = (np.dot(XYZ,DH.T)+.5)%1.-Dphi
3389    Mmap = np.reshape(np.sum((Dang)**2,axis=1),newshape=steps)/len(DH)
3390    hist,bins = np.histogram(Mmap,bins=1000)
3391#    for i,item in enumerate(hist[:10]):
3392#        print item,bins[i]
3393    chisq = np.min(Mmap)
3394    DX = -np.array(np.unravel_index(np.argmin(Mmap),Mmap.shape))
3395    G2fil.G2Print (' map offset chi**2: %.3f, map offset: %d %d %d'%(chisq,DX[0],DX[1],DX[2]))
3396#    print (np.dot(DX,DH.T)+.5)%1.-Dphi
3397    return DX
3398   
3399def ChargeFlip(data,reflDict,pgbar):
3400    '''default doc string
3401   
3402    :param type name: description
3403   
3404    :returns: type name: description
3405   
3406    '''
3407    generalData = data['General']
3408    mapData = generalData['Map']
3409    flipData = generalData['Flip']
3410    FFtable = {}
3411    if 'None' not in flipData['Norm element']:
3412        normElem = flipData['Norm element'].upper()
3413        FFs = G2el.GetFormFactorCoeff(normElem.split('+')[0].split('-')[0])
3414        for ff in FFs:
3415            if ff['Symbol'] == normElem:
3416                FFtable.update(ff)
3417    dmin = flipData['GridStep']*2.
3418    SGData = generalData['SGData']
3419    SGMT = np.array([ops[0].T for ops in SGData['SGOps']])
3420    SGT = np.array([ops[1] for ops in SGData['SGOps']])
3421    cell = generalData['Cell'][1:8]       
3422    A = G2lat.cell2A(cell[:6])
3423    Vol = cell[6]
3424    im = 0
3425    if generalData['Modulated'] == True:
3426        im = 1
3427    Hmax = np.asarray(G2lat.getHKLmax(dmin,SGData,A),dtype='i')+1
3428    adjHKLmax(SGData,Hmax)
3429    Ehkl = np.zeros(shape=2*Hmax,dtype='c16')       #2X64bits per complex no.
3430    time0 = time.time()
3431    for iref,ref in enumerate(reflDict['RefList']):
3432        dsp = ref[4+im]
3433        if im and ref[3]:   #skip super lattice reflections - result is 3D projection
3434            continue
3435        if dsp > dmin:
3436            ff = 0.1*Vol    #est. no. atoms for ~10A**3/atom
3437            if FFtable:
3438                SQ = 0.25/dsp**2
3439                ff *= G2el.ScatFac(FFtable,SQ)[0]
3440            if ref[8+im] > 0.:         #use only +ve Fobs**2
3441                E = np.sqrt(ref[8+im])/ff
3442            else:
3443                E = 0.
3444            ph = ref[10]
3445            ph = rn.uniform(0.,360.)
3446            Uniq = np.inner(ref[:3],SGMT)
3447            Phi = np.inner(ref[:3],SGT)
3448            for i,hkl in enumerate(Uniq):        #uses uniq
3449                hkl = np.asarray(hkl,dtype='i')
3450                dp = 360.*Phi[i]                #and phi
3451                a = cosd(ph+dp)
3452                b = sind(ph+dp)
3453                phasep = complex(a,b)
3454                phasem = complex(a,-b)
3455                h,k,l = hkl+Hmax
3456                Ehkl[h,k,l] = E*phasep
3457                h,k,l = -hkl+Hmax
3458                Ehkl[h,k,l] = E*phasem
3459#    Ehkl[Hmax] = 0.00001           #this to preserve F[0,0,0]
3460    testHKL = np.array(flipData['testHKL'])+Hmax
3461    CEhkl = copy.copy(Ehkl)
3462    MEhkl = ma.array(Ehkl,mask=(Ehkl==0.0))
3463    Emask = ma.getmask(MEhkl)
3464    sumE = np.sum(ma.array(np.absolute(CEhkl),mask=Emask))
3465    Ncyc = 0
3466    old = np.seterr(all='raise')
3467    twophases = []
3468    while True:       
3469        CErho = np.real(fft.fftn(fft.fftshift(CEhkl)))*(1.+0j)
3470        CEsig = np.std(CErho)
3471        CFrho = np.where(np.real(CErho) >= flipData['k-factor']*CEsig,CErho,-CErho)
3472        CFrho = np.where(np.real(CErho) <= flipData['k-Max']*CEsig,CFrho,-CFrho)      #solves U atom problem!
3473        CFhkl = fft.ifftshift(fft.ifftn(CFrho))
3474        CFhkl = np.where(CFhkl,CFhkl,1.0)           #avoid divide by zero
3475        phase = CFhkl/np.absolute(CFhkl)
3476        twophases.append([np.angle(phase[h,k,l]) for h,k,l in testHKL])
3477        CEhkl = np.absolute(Ehkl)*phase
3478        Ncyc += 1
3479        sumCF = np.sum(ma.array(np.absolute(CFhkl),mask=Emask))
3480        DEhkl = np.absolute(np.absolute(Ehkl)/sumE-np.absolute(CFhkl)/sumCF)
3481        Rcf = min(100.,np.sum(ma.array(DEhkl,mask=Emask)*100.))
3482        if Rcf < 5.:
3483            break
3484        GoOn = pgbar.Update(Rcf,newmsg='%s%8.3f%s\n%s %d'%('Residual Rcf =',Rcf,'%','No.cycles = ',Ncyc))[0]
3485        if not GoOn or Ncyc > 10000:
3486            break
3487    np.seterr(**old)
3488    G2fil.G2Print (' Charge flip time: %.4f'%(time.time()-time0),'no. elements: %d'%(Ehkl.size))
3489    CErho = np.real(fft.fftn(fft.fftshift(CEhkl)))/10.  #? to get on same scale as e-map
3490    G2fil.G2Print (' No.cycles = %d Residual Rcf =%8.3f%s Map size: %s'%(Ncyc,Rcf,'%',str(CErho.shape)))
3491    roll = findOffset(SGData,A,CEhkl)               #CEhkl needs to be just the observed set, not the full set!
3492       
3493    mapData['Rcf'] = Rcf
3494    mapData['rho'] = np.roll(np.roll(np.roll(CErho,roll[0],axis=0),roll[1],axis=1),roll[2],axis=2)
3495    mapData['rhoMax'] = max(np.max(mapData['rho']),-np.min(mapData['rho']))
3496    mapData['minmax'] = [np.max(mapData['rho']),np.min(mapData['rho'])]
3497    mapData['Type'] = reflDict['Type']
3498    return mapData,twophases
3499   
3500def findSSOffset(SGData,SSGData,A,Fhklm):   
3501    '''default doc string
3502   
3503    :param type name: description
3504   
3505    :returns: type name: description
3506   
3507    '''
3508    if SGData['SpGrp'] == 'P 1':
3509        return [0,0,0,0]   
3510    hklmShape = Fhklm.shape
3511    hklmHalf = np.array(hklmShape)/2
3512    sortHKLM = np.argsort(Fhklm.flatten())
3513    Fdict = {}
3514    for hklm in sortHKLM:
3515        HKLM = np.unravel_index(hklm,hklmShape)
3516        F = Fhklm[HKLM[0]][HKLM[1]][HKLM[2]][HKLM[3]]
3517        if F == 0.:
3518            break
3519        Fdict['%.6f'%(np.absolute(F))] = hklm
3520    Flist = np.flipud(np.sort(list(Fdict.keys())))
3521    F = str(1.e6)
3522    i = 0
3523    DH = []
3524    Dphi = []
3525    SSGMT = np.array([ops[0].T for ops in SSGData['SSGOps']])
3526    SSGT = np.array([ops[1] for ops in SSGData['SSGOps']])
3527    Hmax = 2*np.asarray(G2lat.getHKLmax(3.5,SGData,A),dtype='i')
3528    for F in Flist:
3529        hklm = np.unravel_index(Fdict[F],hklmShape)
3530        if np.any(np.abs(hklm-hklmHalf)[:3]-Hmax > 0):
3531            continue
3532        Uniq = np.inner(hklm-hklmHalf,SSGMT)
3533        Phi = np.inner(hklm-hklmHalf,SSGT)
3534        Uniq = np.concatenate((Uniq,-Uniq))+hklmHalf         # put in Friedel pairs & make as index to Farray
3535        Phi = np.concatenate((Phi,-Phi))                      # and their phase shifts
3536        Fh0 = Fhklm[hklm[0],hklm[1],hklm[2],hklm[3]]
3537        ang0 = np.angle(Fh0,deg=True)/360.
3538        for H,phi in list(zip(Uniq,Phi))[1:]:
3539            H = np.array(H,dtype=int)
3540            ang = (np.angle(Fhklm[H[0],H[1],H[2],H[3]],deg=True)/360.-phi)
3541            dH = H-hklm
3542            dang = ang-ang0
3543            DH.append(dH)
3544            Dphi.append((dang+.5) % 1.0)
3545        if i > 20 or len(DH) > 30:
3546            break
3547        i += 1
3548    DH = np.array(DH)
3549    G2fil.G2Print (' map offset no.of terms: %d from %d reflections'%(len(DH),len(Flist)))
3550    Dphi = np.array(Dphi)
3551    steps = np.array(hklmShape)
3552    X,Y,Z,T = np.mgrid[0:1:1./steps[0],0:1:1./steps[1],0:1:1./steps[2],0:1:1./steps[3]]
3553    XYZT = np.array(list(zip(X.flatten(),Y.flatten(),Z.flatten(),T.flatten())))
3554    Dang = (np.dot(XYZT,DH.T)+.5)%1.-Dphi
3555    Mmap = np.reshape(np.sum((Dang)**2,axis=1),newshape=steps)/len(DH)
3556    hist,bins = np.histogram(Mmap,bins=1000)
3557#    for i,item in enumerate(hist[:10]):
3558#        print item,bins[i]
3559    chisq = np.min(Mmap)
3560    DX = -np.array(np.unravel_index(np.argmin(Mmap),Mmap.shape))
3561    G2fil.G2Print (' map offset chi**2: %.3f, map offset: %d %d %d %d'%(chisq,DX[0],DX[1],DX[2],DX[3]))
3562#    print (np.dot(DX,DH.T)+.5)%1.-Dphi
3563    return DX
3564   
3565def SSChargeFlip(data,reflDict,pgbar):
3566    '''default doc string
3567   
3568    :param type name: description
3569   
3570    :returns: type name: description
3571   
3572    '''
3573    generalData = data['General']
3574    mapData = generalData['Map']
3575    map4DData = {}
3576    flipData = generalData['Flip']
3577    FFtable = {}
3578    if 'None' not in flipData['Norm element']:
3579        normElem = flipData['Norm element'].upper()
3580        FFs = G2el.GetFormFactorCoeff(normElem.split('+')[0].split('-')[0])
3581        for ff in FFs:
3582            if ff['Symbol'] == normElem:
3583                FFtable.update(ff)
3584    dmin = flipData['GridStep']*2.
3585    SGData = generalData['SGData']
3586    SSGData = generalData['SSGData']
3587    SSGMT = np.array([ops[0].T for ops in SSGData['SSGOps']])
3588    SSGT = np.array([ops[1] for ops in SSGData['SSGOps']])
3589    cell = generalData['Cell'][1:8]       
3590    A = G2lat.cell2A(cell[:6])
3591    Vol = cell[6]
3592    maxM = 4
3593    Hmax = np.asarray(G2lat.getHKLmax(dmin,SGData,A)+[maxM,],dtype='i')+1
3594    adjHKLmax(SGData,Hmax)
3595    Ehkl = np.zeros(shape=2*Hmax,dtype='c16')       #2X64bits per complex no.
3596    time0 = time.time()
3597    for iref,ref in enumerate(reflDict['RefList']):
3598        dsp = ref[5]
3599        if dsp > dmin:
3600            ff = 0.1*Vol    #est. no. atoms for ~10A**3/atom
3601            if FFtable:
3602                SQ = 0.25/dsp**2
3603                ff *= G2el.ScatFac(FFtable,SQ)[0]
3604            if ref[9] > 0.:         #use only +ve Fobs**2
3605                E = np.sqrt(ref[9])/ff
3606            else:
3607                E = 0.
3608            ph = ref[11]
3609            ph = rn.uniform(0.,360.)
3610            Uniq = np.inner(ref[:4],SSGMT)
3611            Phi = np.inner(ref[:4],SSGT)
3612            for i,hklm in enumerate(Uniq):        #uses uniq
3613                hklm = np.asarray(hklm,dtype='i')
3614                dp = 360.*Phi[i]                #and phi
3615                a = cosd(ph+dp)
3616                b = sind(ph+dp)
3617                phasep = complex(a,b)
3618                phasem = complex(a,-b)
3619                h,k,l,m = hklm+Hmax
3620                Ehkl[h,k,l,m] = E*phasep
3621                h,k,l,m = -hklm+Hmax       #Friedel pair refl.
3622                Ehkl[h,k,l,m] = E*phasem
3623#    Ehkl[Hmax] = 0.00001           #this to preserve F[0,0,0]
3624    CEhkl = copy.copy(Ehkl)
3625    MEhkl = ma.array(Ehkl,mask=(Ehkl==0.0))
3626    Emask = ma.getmask(MEhkl)
3627    sumE = np.sum(ma.array(np.absolute(CEhkl),mask=Emask))
3628    Ncyc = 0
3629    old = np.seterr(all='raise')
3630    while True:       
3631        CErho = np.real(fft.fftn(fft.fftshift(CEhkl)))*(1.+0j)
3632        CEsig = np.std(CErho)
3633        CFrho = np.where(np.real(CErho) >= flipData['k-factor']*CEsig,CErho,-CErho)
3634        CFrho = np.where(np.real(CErho) <= flipData['k-Max']*CEsig,CFrho,-CFrho)      #solves U atom problem!
3635        CFhkl = fft.ifftshift(fft.ifftn(CFrho))
3636        CFhkl = np.where(CFhkl,CFhkl,1.0)           #avoid divide by zero
3637        phase = CFhkl/np.absolute(CFhkl)
3638        CEhkl = np.absolute(Ehkl)*phase
3639        Ncyc += 1
3640        sumCF = np.sum(ma.array(np.absolute(CFhkl),mask=Emask))
3641        DEhkl = np.absolute(np.absolute(Ehkl)/sumE-np.absolute(CFhkl)/sumCF)
3642        Rcf = min(100.,np.sum(ma.array(DEhkl,mask=Emask)*100.))
3643        if Rcf < 5.:
3644            break
3645        GoOn = pgbar.Update(Rcf,newmsg='%s%8.3f%s\n%s %d'%('Residual Rcf =',Rcf,'%','No.cycles = ',Ncyc))[0]
3646        if not GoOn or Ncyc > 10000:
3647            break
3648    np.seterr(**old)
3649    G2fil.G2Print (' Charge flip time: %.4f no. elements: %d'%(time.time()-time0,Ehkl.size))
3650    CErho = np.real(fft.fftn(fft.fftshift(CEhkl[:,:,:,maxM+1])))/10.    #? to get on same scale as e-map
3651    SSrho = np.real(fft.fftn(fft.fftshift(CEhkl)))/10.                  #? ditto
3652    G2fil.G2Print (' No.cycles = %d Residual Rcf =%8.3f%s Map size: %s'%(Ncyc,Rcf,'%',str(CErho.shape)))
3653    roll = findSSOffset(SGData,SSGData,A,CEhkl)               #CEhkl needs to be just the observed set, not the full set!
3654
3655    mapData['Rcf'] = Rcf
3656    mapData['rho'] = np.roll(np.roll(np.roll(CErho,roll[0],axis=0),roll[1],axis=1),roll[2],axis=2)
3657    mapData['rhoMax'] = max(np.max(mapData['rho']),-np.min(mapData['rho']))
3658    mapData['minmax'] = [np.max(mapData['rho']),np.min(mapData['rho'])]
3659    mapData['Type'] = reflDict['Type']
3660
3661    map4DData['Rcf'] = Rcf
3662    map4DData['rho'] = np.real(np.roll(np.roll(np.roll(np.roll(SSrho,roll[0],axis=0),roll[1],axis=1),roll[2],axis=2),roll[3],axis=3))
3663    map4DData['rhoMax'] = max(np.max(map4DData['rho']),-np.min(map4DData['rho']))
3664    map4DData['minmax'] = [np.max(map4DData['rho']),np.min(map4DData['rho'])]
3665    map4DData['Type'] = reflDict['Type']
3666    return mapData,map4DData
3667   
3668def getRho(xyz,mapData):
3669    ''' get scattering density at a point by 8-point interpolation
3670    param xyz: coordinate to be probed
3671    param: mapData: dict of map data
3672   
3673    :returns: density at xyz
3674    '''
3675    rollMap = lambda rho,roll: np.roll(np.roll(np.roll(rho,roll[0],axis=0),roll[1],axis=1),roll[2],axis=2)
3676    if not len(mapData):
3677        return 0.0
3678    rho = copy.copy(mapData['rho'])     #don't mess up original
3679    if not len(rho):
3680        return 0.0
3681    mapShape = np.array(rho.shape)
3682    mapStep = 1./mapShape
3683    X = np.array(xyz)%1.    #get into unit cell
3684    I = np.array(X*mapShape,dtype='int')
3685    D = X-I*mapStep         #position inside map cell
3686    D12 = D[0]*D[1]
3687    D13 = D[0]*D[2]
3688    D23 = D[1]*D[2]
3689    D123 = np.prod(D)
3690    Rho = rollMap(rho,-I)    #shifts map so point is in corner
3691    R = Rho[0,0,0]*(1.-np.sum(D))+Rho[1,0,0]*D[0]+Rho[0,1,0]*D[1]+Rho[0,0,1]*D[2]+  \
3692        Rho[1,1,1]*D123+Rho[0,1,1]*(D23-D123)+Rho[1,0,1]*(D13-D123)+Rho[1,1,0]*(D12-D123)+  \
3693        Rho[0,0,0]*(D12+D13+D23-D123)-Rho[0,0,1]*(D13+D23-D123)-    \
3694        Rho[0,1,0]*(D23+D12-D123)-Rho[1,0,0]*(D13+D12-D123)   
3695    return R
3696       
3697def getRhos(XYZ,rho):
3698    ''' get scattering density at an array of point by 8-point interpolation
3699    this is faster than gerRho which is only used for single points. However, getRhos is
3700    replaced by scipy.ndimage.interpolation.map_coordinates which does a better job &  is just as fast.
3701    Thus, getRhos is unused in GSAS-II at this time.
3702    param xyz:  array coordinates to be probed Nx3
3703    param: rho: array copy of map (NB: don't use original!)
3704   
3705    :returns: density at xyz
3706    '''
3707    def getBoxes(rho,I):
3708        Rhos = np.zeros((2,2,2))
3709        Mx,My,Mz = rho.shape
3710        Ix,Iy,Iz = I
3711        Rhos = np.array([[[rho[Ix%Mx,Iy%My,Iz%Mz],rho[Ix%Mx,Iy%My,(Iz+1)%Mz]],
3712                          [rho[Ix%Mx,(Iy+1)%My,Iz%Mz],rho[Ix%Mx,(Iy+1)%My,(Iz+1)%Mz]]],
3713                        [[rho[(Ix+1)%Mx,Iy%My,Iz%Mz],rho[(Ix+1)%Mx,Iy%My,(Iz+1)%Mz]],
3714                          [rho[(Ix+1)%Mx,(Iy+1)%My,Iz%Mz],rho[(Ix+1)%Mx,(Iy+1)%My,(Iz+1)%Mz]]]])
3715        return Rhos
3716       
3717    Blk = 400     #400 doesn't seem to matter
3718    nBlk = len(XYZ)//Blk        #select Blk so this is an exact divide
3719    mapShape = np.array(rho.shape)
3720    mapStep = 1./mapShape
3721    X = XYZ%1.    #get into unit cell
3722    iBeg = 0
3723    R = np.zeros(len(XYZ))
3724#actually a lot faster!
3725    for iblk in range(nBlk):
3726        iFin = iBeg+Blk
3727        Xs = X[iBeg:iFin]
3728        I = np.array(np.rint(Xs*mapShape),dtype='int')
3729        Rhos = np.array([getBoxes(rho,i) for i in I])
3730        Ds = Xs-I*mapStep
3731        RIJs = Rhos[:,0,:2,:2]*(1.-Ds[:,0][:,nxs,nxs])
3732        RIs = RIJs[:,0]*(1.-Ds[:,1][:,nxs])+RIJs[:,1]*Ds[:,1][:,nxs]
3733        R[iBeg:iFin] = RIs[:,0]*(1.-Ds[:,2])+RIs[:,1]*Ds[:,2]
3734        iBeg += Blk
3735    return R
3736       
3737def SearchMap(generalData,drawingData,Neg=False):
3738    '''Does a search of a density map for peaks meeting the criterion of peak
3739    height is greater than mapData['cutOff']/100 of mapData['rhoMax'] where
3740    mapData is data['General']['mapData']; the map is also in mapData.
3741
3742    :param generalData: the phase data structure; includes the map
3743    :param drawingData: the drawing data structure
3744    :param Neg:  if True then search for negative peaks (i.e. H-atoms & neutron data)
3745
3746    :returns: (peaks,mags,dzeros) where
3747
3748        * peaks : ndarray
3749          x,y,z positions of the peaks found in the map
3750        * mags : ndarray
3751          the magnitudes of the peaks
3752        * dzeros : ndarray
3753          the distance of the peaks from  the unit cell origin
3754        * dcent : ndarray
3755          the distance of the peaks from  the unit cell center
3756
3757    '''       
3758    rollMap = lambda rho,roll: np.roll(np.roll(np.roll(rho,roll[0],axis=0),roll[1],axis=1),roll[2],axis=2)
3759   
3760    norm = 1./(np.sqrt(3.)*np.sqrt(2.*np.pi)**3)
3761   
3762    def fixSpecialPos(xyz,SGData,Amat):
3763        equivs = G2spc.GenAtom(xyz,SGData,Move=True)
3764        X = []
3765        xyzs = [equiv[0] for equiv in equivs]
3766        for x in xyzs:
3767            if np.sqrt(np.sum(np.inner(Amat,xyz-x)**2,axis=0))<0.5:
3768                X.append(x)
3769        if len(X) > 1:
3770            return np.average(X,axis=0)
3771        else:
3772            return xyz
3773       
3774    def rhoCalc(parms,rX,rY,rZ,res,SGLaue):
3775        Mag,x0,y0,z0,sig = parms
3776        z = -((x0-rX)**2+(y0-rY)**2+(z0-rZ)**2)/(2.*sig**2)
3777#        return norm*Mag*np.exp(z)/(sig*res**3)     #not slower but some faults in LS
3778        return norm*Mag*(1.+z+z**2/2.)/(sig*res**3)
3779       
3780    def peakFunc(parms,rX,rY,rZ,rho,res,SGLaue):
3781        Mag,x0,y0,z0,sig = parms
3782        M = rho-rhoCalc(parms,rX,rY,rZ,res,SGLaue)
3783        return M
3784       
3785    def peakHess(parms,rX,rY,rZ,rho,res,SGLaue):
3786        Mag,x0,y0,z0,sig = parms
3787        dMdv = np.zeros(([5,]+list(rX.shape)))
3788        delt = .01
3789        for i in range(5):
3790            parms[i] -= delt
3791            rhoCm = rhoCalc(parms,rX,rY,rZ,res,SGLaue)
3792            parms[i] += 2.*delt
3793            rhoCp = rhoCalc(parms,rX,rY,rZ,res,SGLaue)
3794            parms[i] -= delt
3795            dMdv[i] = (rhoCp-rhoCm)/(2.*delt)
3796        rhoC = rhoCalc(parms,rX,rY,rZ,res,SGLaue)
3797        Vec = np.sum(np.sum(np.sum(dMdv*(rho-rhoC),axis=3),axis=2),axis=1)
3798        dMdv = np.reshape(dMdv,(5,rX.size))
3799        Hess = np.inner(dMdv,dMdv)
3800       
3801        return Vec,Hess
3802       
3803    SGData = generalData['SGData']
3804    Amat,Bmat = G2lat.cell2AB(generalData['Cell'][1:7])
3805    peaks = []
3806    mags = []
3807    dzeros = []
3808    dcent = []
3809    try:
3810        mapData = generalData['Map']
3811        contLevel = mapData['cutOff']*mapData['rhoMax']/100.
3812        if Neg:
3813            rho = -copy.copy(mapData['rho'])     #flip +/-
3814        else:
3815            rho = copy.copy(mapData['rho'])     #don't mess up original
3816        mapHalf = np.array(rho.shape)/2
3817        res = mapData['GridStep']*2.
3818        incre = np.array(rho.shape,dtype=np.float)
3819        step = max(1.0,1./res)+1
3820        steps = np.array((3*[step,]),dtype='int32')
3821    except KeyError:
3822        G2fil.G2Print ('**** ERROR - Fourier map not defined')
3823        return peaks,mags
3824    rhoMask = ma.array(rho,mask=(rho<contLevel))
3825    indices = (-1,0,1)
3826    rolls = np.array([[h,k,l] for h in indices for k in indices for l in indices])
3827    for roll in rolls:
3828        if np.any(roll):
3829            rhoMask = ma.array(rhoMask,mask=(rhoMask-rollMap(rho,roll)<=0.))
3830    indx = np.transpose(rhoMask.nonzero())
3831    peaks = indx/incre
3832    mags = rhoMask[rhoMask.nonzero()]
3833    for i,[ind,peak,mag] in enumerate(zip(indx,peaks,mags)):
3834        rho = rollMap(rho,ind)
3835        rMM = mapHalf-steps
3836        rMP = mapHalf+steps+1
3837        rhoPeak = rho[int(rMM[0]):int(rMP[0]),int(rMM[1]):int(rMP[1]),int(rMM[2]):int(rMP[2])]
3838        peakInt = np.sum(rhoPeak)*res**3
3839        rX,rY,rZ = np.mgrid[int(rMM[0]):int(rMP[0]),int(rMM[1]):int(rMP[1]),int(rMM[2]):int(rMP[2])]
3840        x0 = [peakInt,mapHalf[0],mapHalf[1],mapHalf[2],2.0]          #magnitude, position & width(sig)
3841        result = HessianLSQ(peakFunc,x0,Hess=peakHess,
3842            args=(rX,rY,rZ,rhoPeak,res,SGData['SGLaue']),ftol=.01,maxcyc=10)
3843        x1 = result[0]
3844        if not np.any(x1 < 0):
3845            peak = (np.array(x1[1:4])-ind)/incre
3846        peak = fixSpecialPos(peak,SGData,Amat)
3847        rho = rollMap(rho,-ind)
3848    cent = np.ones(3)*.5     
3849    dzeros = np.sqrt(np.sum(np.inner(Amat,peaks)**2,axis=0))
3850    dcent = np.sqrt(np.sum(np.inner(Amat,peaks-cent)**2,axis=0))
3851    if Neg:     #want negative magnitudes for negative peaks
3852        return np.array(peaks),-np.array([mags,]).T,np.array([dzeros,]).T,np.array([dcent,]).T
3853    else:
3854        return np.array(peaks),np.array([mags,]).T,np.array([dzeros,]).T,np.array([dcent,]).T
3855   
3856def sortArray(data,pos,reverse=False):
3857    '''data is a list of items
3858    sort by pos in list; reverse if True
3859    '''
3860    T = []
3861    for i,M in enumerate(data):
3862        try:
3863            T.append((M[pos],i))
3864        except IndexError:
3865            return data
3866    D = dict(zip(T,data))
3867    T.sort()
3868    if reverse:
3869        T.reverse()
3870    X = []
3871    for key in T:
3872        X.append(D[key])
3873    return X
3874
3875def PeaksEquiv(data,Ind):
3876    '''Find the equivalent map peaks for those selected. Works on the
3877    contents of data['Map Peaks'].
3878
3879    :param data: the phase data structure
3880    :param list Ind: list of selected peak indices
3881    :returns: augmented list of peaks including those related by symmetry to the
3882      ones in Ind
3883
3884    '''       
3885    def Duplicate(xyz,peaks,Amat):
3886        if True in [np.allclose(np.inner(Amat,xyz),np.inner(Amat,peak),atol=0.5) for peak in peaks]:
3887            return True
3888        return False
3889                           
3890    generalData = data['General']
3891    Amat,Bmat = G2lat.cell2AB(generalData['Cell'][1:7])
3892    SGData = generalData['SGData']
3893    mapPeaks = data['Map Peaks']
3894    XYZ = np.array([xyz[1:4] for xyz in mapPeaks])
3895    Indx = {}
3896    for ind in Ind:
3897        xyz = np.array(mapPeaks[ind][1:4])
3898        xyzs = np.array([equiv[0] for equiv in G2spc.GenAtom(xyz,SGData,Move=True)])
3899        for jnd,xyz in enumerate(XYZ):       
3900            Indx[jnd] = Duplicate(xyz,xyzs,Amat)
3901    Ind = []
3902    for ind in Indx:
3903        if Indx[ind]:
3904            Ind.append(ind)
3905    return Ind
3906               
3907def PeaksUnique(data,Ind):
3908    '''Finds the symmetry unique set of peaks from those selected. Selects
3909    the one closest to the center of the unit cell.
3910    Works on the contents of data['Map Peaks']. Called from OnPeaksUnique in
3911    GSASIIphsGUI.py,
3912
3913    :param data: the phase data structure
3914    :param list Ind: list of selected peak indices
3915
3916    :returns: the list of symmetry unique peaks from among those given in Ind
3917    '''       
3918#    XYZE = np.array([[equiv[0] for equiv in G2spc.GenAtom(xyz[1:4],SGData,Move=True)] for xyz in mapPeaks]) #keep this!!
3919
3920    def noDuplicate(xyz,peaks,Amat):
3921        if True in [np.allclose(np.inner(Amat,xyz),np.inner(Amat,peak),atol=0.5) for peak in peaks]:
3922            return False
3923        return True
3924                           
3925    generalData = data['General']
3926    Amat,Bmat = G2lat.cell2AB(generalData['Cell'][1:7])
3927    SGData = generalData['SGData']
3928    mapPeaks = data['Map Peaks']
3929   
3930   
3931    Indx = {}
3932    XYZ = {}
3933    for ind in Ind:
3934        XYZ[ind] = np.array(mapPeaks[ind][1:4])
3935        Indx[ind] = True
3936    for ind in Ind:
3937        if Indx[ind]:
3938            xyz = XYZ[ind]
3939            for jnd in Ind:
3940                if ind != jnd and Indx[jnd]:                       
3941                    Equiv = G2spc.GenAtom(XYZ[jnd],SGData,Move=True)
3942                    xyzs = np.array([equiv[0] for equiv in Equiv])
3943                    Indx[jnd] = noDuplicate(xyz,xyzs,Amat)
3944    Ind = []
3945    for ind in Indx:
3946        if Indx[ind]:
3947            Ind.append(ind)
3948    return Ind
3949   
3950   
3951   
3952   
3953   
3954   
3955    # XYZ = {ind:np.array(mapPeaks[ind][1:4]) for ind in Ind}
3956    # Indx = [True for ind in Ind]
3957    # Unique = []
3958    # # scan through peaks, finding all peaks equivalent to peak ind
3959    # for ind in Ind:
3960    #     if Indx[ind]:
3961    #         xyz = XYZ[ind]
3962    #         dups = []
3963    #         for jnd in Ind:
3964    #             # only consider peaks we have not looked at before
3965    #             # and were not already found to be equivalent
3966    #             if jnd > ind and Indx[jnd]:                       
3967    #                 Equiv = G2spc.GenAtom(XYZ[jnd],SGData,Move=True)
3968    #                 xyzs = np.array([equiv[0] for equiv in Equiv])
3969    #                 if not noDuplicate(xyz,xyzs,Amat):
3970    #                     Indx[jnd] = False
3971    #                     dups.append(jnd)
3972    #                 Indx[jnd] = noDuplicate(xyz,xyzs,Amat)
3973    # #        # select the unique peak closest to cell center
3974    # #         cntr = mapPeaks[ind][-1]
3975    # #         icntr = ind
3976    # #         for jnd in dups:
3977    # #             if mapPeaks[jnd][-1] < cntr:
3978    # #                 cntr = mapPeaks[jnd][-1]
3979    # #                 icntr = jnd
3980    # #         Unique.append(icntr)
3981    # # return Unique
3982    # Ind = []
3983    # for ind in Indx:
3984    #     if Indx[ind]:
3985    #         Ind.append(ind)
3986    # return Ind
3987################################################################################
3988##### Dysnomia setup & return stuff
3989################################################################################
3990   
3991
3992   
3993################################################################################
3994##### single peak fitting profile fxn stuff
3995################################################################################
3996
3997def getCWsig(ins,pos):
3998    '''get CW peak profile sigma^2
3999   
4000    :param dict ins: instrument parameters with at least 'U', 'V', & 'W'
4001      as values only
4002    :param float pos: 2-theta of peak
4003    :returns: float getCWsig: peak sigma^2
4004   
4005    '''
4006    tp = tand(pos/2.0)
4007    return ins['U']*tp**2+ins['V']*tp+ins['W']
4008   
4009def getCWsigDeriv(pos):
4010    '''get derivatives of CW peak profile sigma^2 wrt U,V, & W
4011   
4012    :param float pos: 2-theta of peak
4013   
4014    :returns: list getCWsigDeriv: d(sig^2)/dU, d(sig)/dV & d(sig)/dW
4015   
4016    '''
4017    tp = tand(pos/2.0)
4018    return tp**2,tp,1.0
4019   
4020def getCWgam(ins,pos):
4021    '''get CW peak profile gamma
4022   
4023    :param dict ins: instrument parameters with at least 'X', 'Y' & 'Z'
4024      as values only
4025    :param float pos: 2-theta of peak
4026    :returns: float getCWgam: peak gamma
4027   
4028    '''
4029    return ins['X']/cosd(pos/2.0)+ins['Y']*tand(pos/2.0)+ins.get('Z',0.0)
4030   
4031def getCWgamDeriv(pos):
4032    '''get derivatives of CW peak profile gamma wrt X, Y & Z
4033   
4034    :param float pos: 2-theta of peak
4035   
4036    :returns: list getCWgamDeriv: d(gam)/dX & d(gam)/dY
4037   
4038    '''
4039    return 1./cosd(pos/2.0),tand(pos/2.0),1.0
4040   
4041def getTOFsig(ins,dsp):
4042    '''get TOF peak profile sigma^2
4043   
4044    :param dict ins: instrument parameters with at least 'sig-0', 'sig-1' & 'sig-q'
4045      as values only
4046    :param float dsp: d-spacing of peak
4047   
4048    :returns: float getTOFsig: peak sigma^2
4049   
4050    '''
4051    return ins['sig-0']+ins['sig-1']*dsp**2+ins['sig-2']*dsp**4+ins['sig-q']*dsp
4052   
4053def getTOFsigDeriv(dsp):
4054    '''get derivatives of TOF peak profile sigma^2 wrt sig-0, sig-1, & sig-q
4055   
4056    :param float dsp: d-spacing of peak
4057   
4058    :returns: list getTOFsigDeriv: d(sig0/d(sig-0), d(sig)/d(sig-1) & d(sig)/d(sig-q)
4059   
4060    '''
4061    return 1.0,dsp**2,dsp**4,dsp
4062   
4063def getTOFgamma(ins,dsp):
4064    '''get TOF peak profile gamma
4065   
4066    :param dict ins: instrument parameters with at least 'X', 'Y' & 'Z'
4067      as values only
4068    :param float dsp: d-spacing of peak
4069   
4070    :returns: float getTOFgamma: peak gamma
4071   
4072    '''
4073    return ins['Z']+ins['X']*dsp+ins['Y']*dsp**2
4074   
4075def getTOFgammaDeriv(dsp):
4076    '''get derivatives of TOF peak profile gamma wrt X, Y & Z
4077   
4078    :param float dsp: d-spacing of peak
4079   
4080    :returns: list getTOFgammaDeriv: d(gam)/dX & d(gam)/dY
4081   
4082    '''
4083    return dsp,dsp**2,1.0
4084   
4085def getTOFbeta(ins,dsp):
4086    '''get TOF peak profile beta
4087   
4088    :param dict ins: instrument parameters with at least 'beat-0', 'beta-1' & 'beta-q'
4089      as values only
4090    :param float dsp: d-spacing of peak
4091   
4092    :returns: float getTOFbeta: peak beat
4093   
4094    '''
4095    return ins['beta-0']+ins['beta-1']/dsp**4+ins['beta-q']/dsp**2
4096   
4097def getTOFbetaDeriv(dsp):
4098    '''get derivatives of TOF peak profile beta wrt beta-0, beta-1, & beat-q
4099   
4100    :param float dsp: d-spacing of peak
4101   
4102    :returns: list getTOFbetaDeriv: d(beta)/d(beat-0), d(beta)/d(beta-1) & d(beta)/d(beta-q)
4103   
4104    '''
4105    return 1.0,1./dsp**4,1./dsp**2
4106   
4107def getTOFalpha(ins,dsp):
4108    '''get TOF peak profile alpha
4109   
4110    :param dict ins: instrument parameters with at least 'alpha'
4111      as values only
4112    :param float dsp: d-spacing of peak
4113   
4114    :returns: flaot getTOFalpha: peak alpha
4115   
4116    '''
4117    return ins['alpha']/dsp
4118   
4119def getTOFalphaDeriv(dsp):
4120    '''get derivatives of TOF peak profile beta wrt alpha
4121   
4122    :param float dsp: d-spacing of peak
4123   
4124    :returns: float getTOFalphaDeriv: d(alp)/d(alpha)
4125   
4126    '''
4127    return 1./dsp
4128   
4129def getPinkalpha(ins,tth):
4130    '''get TOF peak profile alpha
4131   
4132    :param dict ins: instrument parameters with at least 'alpha'
4133      as values only
4134    :param float tth: 2-theta of peak
4135   
4136    :returns: flaot getPinkalpha: peak alpha
4137   
4138    '''
4139    return ins['alpha-0']+ ins['alpha-1']*tand(tth/2.)
4140   
4141def getPinkalphaDeriv(tth):
4142    '''get derivatives of TOF peak profile beta wrt alpha
4143   
4144    :param float dsp: d-spacing of peak
4145   
4146    :returns: float getTOFalphaDeriv: d(alp)/d(alpha-0), d(alp)/d(alpha-1)
4147   
4148    '''
4149    return 1.0,tand(tth/2.)
4150   
4151def getPinkbeta(ins,tth):
4152    '''get TOF peak profile beta
4153   
4154    :param dict ins: instrument parameters with at least 'beat-0' & 'beta-1'
4155      as values only
4156    :param float tth: 2-theta of peak
4157   
4158    :returns: float getaPinkbeta: peak beta
4159   
4160    '''
4161    return ins['beta-0']+ins['beta-1']*tand(tth/2.)
4162   
4163def getPinkbetaDeriv(tth):
4164    '''get derivatives of TOF peak profile beta wrt beta-0 & beta-1
4165   
4166    :param float dsp: d-spacing of peak
4167   
4168    :returns: list getTOFbetaDeriv: d(beta)/d(beta-0) & d(beta)/d(beta-1)
4169   
4170    '''
4171    return 1.0,tand(tth/2.)
4172   
4173def setPeakparms(Parms,Parms2,pos,mag,ifQ=False,useFit=False):
4174    '''set starting peak parameters for single peak fits from plot selection or auto selection
4175   
4176    :param dict Parms: instrument parameters dictionary
4177    :param dict Parms2: table lookup for TOF profile coefficients
4178    :param float pos: peak position in 2-theta, TOF or Q (ifQ=True)
4179    :param float mag: peak top magnitude from pick
4180    :param bool ifQ: True if pos in Q
4181    :param bool useFit: True if use fitted CW Parms values (not defaults)
4182   
4183    :returns: list XY: peak list entry:
4184        for CW: [pos,0,mag,1,sig,0,gam,0]
4185        for TOF: [pos,0,mag,1,alp,0,bet,0,sig,0,gam,0]
4186        for Pink: [pos,0,mag,1,alp,0,bet,0,sig,0,gam,0]
4187        NB: mag refinement set by default, all others off
4188   
4189    '''
4190    ind = 0
4191    if useFit:
4192        ind = 1
4193    ins = {}
4194    if 'T' in Parms['Type'][0]:
4195        if ifQ:
4196            dsp = 2.*np.pi/pos
4197            pos = Parms['difC']*dsp
4198        else:
4199            dsp = pos/Parms['difC'][1]
4200        if 'Pdabc' in Parms2:
4201            for x in ['sig-0','sig-1','sig-2','sig-q','X','Y','Z']:
4202                ins[x] = Parms.get(x,[0.0,0.0])[ind]
4203            Pdabc = Parms2['Pdabc'].T
4204            alp = np.interp(dsp,Pdabc[0],Pdabc[1])
4205            bet = np.interp(dsp,Pdabc[0],Pdabc[2])
4206        else:
4207            for x in ['alpha','beta-0','beta-1','beta-q','sig-0','sig-1','sig-2','sig-q','X','Y','Z']:
4208                ins[x] = Parms.get(x,[0.0,0.0])[ind]
4209            alp = getTOFalpha(ins,dsp)
4210            bet = getTOFbeta(ins,dsp)
4211        sig = getTOFsig(ins,dsp)
4212        gam = getTOFgamma(ins,dsp)
4213        XY = [pos,0,mag,1,alp,0,bet,0,sig,0,gam,0]
4214    elif 'C' in Parms['Type'][0]:                            #CW data - TOF later in an else
4215        for x in ['U','V','W','X','Y','Z']:
4216            ins[x] = Parms.get(x,[0.0,0.0])[ind]
4217        if ifQ:                              #qplot - convert back to 2-theta
4218            pos = 2.0*asind(pos*getWave(Parms)/(4*math.pi))
4219        sig = getCWsig(ins,pos)
4220        gam = getCWgam(ins,pos)           
4221        XY = [pos,0, mag,1, sig,0, gam,0]       #default refine intensity 1st
4222    elif 'B' in Parms['Type'][0]:
4223        for x in ['U','V','W','X','Y','Z','alpha-0','alpha-1','beta-0','beta-1']:
4224            ins[x] = Parms.get(x,[0.0,0.0])[ind]
4225        if ifQ:                              #qplot - convert back to 2-theta
4226            pos = 2.0*asind(pos*getWave(Parms)/(4*math.pi))
4227        alp = getPinkalpha(ins,pos)
4228        bet = getPinkbeta(ins,pos)
4229        sig = getCWsig(ins,pos)
4230        gam = getCWgam(ins,pos)           
4231        XY = [pos,0,mag,1,alp,0,bet,0,sig,0,gam,0]       #default refine intensity 1st
4232    return XY
4233   
4234################################################################################
4235##### MC/SA stuff
4236################################################################################
4237
4238#scipy/optimize/anneal.py code modified by R. Von Dreele 2013
4239# Original Author: Travis Oliphant 2002
4240# Bug-fixes in 2006 by Tim Leslie
4241
4242
4243import numpy
4244from numpy import asarray, exp, squeeze, sign, \
4245     all, shape, array, where
4246from numpy import random
4247
4248#__all__ = ['anneal']
4249
4250class base_schedule(object):
4251    def __init__(self):
4252        self.dwell = 20
4253        self.lower = 0.
4254        self.upper = 1.
4255        self.Ninit = 50
4256        self.accepted = 0
4257        self.tests = 0
4258        self.feval = 0
4259        self.k = 0
4260        self.T = None
4261
4262    def init(self, **options):
4263        self.__dict__.update(options)
4264        self.lower = asarray(self.lower)
4265        self.lower = where(self.lower == numpy.NINF, -_double_max, self.lower)
4266        self.upper = asarray(self.upper)
4267        self.upper = where(self.upper == numpy.PINF, _double_max, self.upper)
4268        self.k = 0
4269        self.accepted = 0
4270        self.feval = 0
4271        self.tests = 0
4272
4273    def getstart_temp(self, best_state):
4274        """ Find a matching starting temperature and starting parameters vector
4275        i.e. find x0 such that func(x0) = T0.
4276
4277        :param best_state: _state
4278            A _state object to store the function value and x0 found.
4279
4280        :returns: x0 : array
4281            The starting parameters vector.
4282        """
4283
4284        assert(not self.dims is None)
4285        lrange = self.lower
4286        urange = self.upper
4287        fmax = _double_min
4288        fmin = _double_max
4289        for _ in range(self.Ninit):
4290            x0 = random.uniform(size=self.dims)*(urange-lrange) + lrange
4291            fval = self.func(x0, *self.args)
4292            self.feval += 1
4293            if fval > fmax:
4294                fmax = fval
4295            if fval < fmin:
4296                fmin = fval
4297                best_state.cost = fval
4298                best_state.x = array(x0)
4299
4300        self.T0 = (fmax-fmin)*1.5
4301        return best_state.x
4302       
4303    def set_range(self,x0,frac):
4304        delrange = frac*(self.upper-self.lower)
4305        self.upper = x0+delrange
4306        self.lower = x0-delrange
4307
4308    def accept_test(self, dE):
4309        T = self.T
4310        self.tests += 1
4311        if dE < 0:
4312            self.accepted += 1
4313            return 1
4314        p = exp(-dE*1.0/T)
4315        if (p > random.uniform(0.0, 1.0)):
4316            self.accepted += 1
4317            return 1
4318        return 0
4319
4320    def update_guess(self, x0):
4321        return np.squeeze(np.random.uniform(0.,1.,size=self.dims))*(self.upper-self.lower)+self.lower
4322
4323    def update_temp(self, x0):
4324        pass
4325
4326class fast_sa(base_schedule):
4327    def init(self, **options):
4328        self.__dict__.update(options)
4329
4330    def update_guess(self, x0):
4331        x0 = asarray(x0)
4332        u = squeeze(random.uniform(0.0, 1.0, size=self.dims))
4333        T = self.T
4334        xc = (sign(u-0.5)*T*((1+1.0/T)**abs(2*u-1)-1.0)+1.0)/2.0
4335        xnew = xc*(self.upper - self.lower)+self.lower
4336        return xnew
4337
4338    def update_temp(self):
4339        self.T = self.T0*exp(-self.c * self.k**(self.quench))
4340        self.k += 1
4341        return
4342
4343class log_sa(base_schedule):        #OK
4344
4345    def init(self,**options):
4346        self.__dict__.update(options)
4347       
4348    def update_guess(self,x0):     #same as default #TODO - is this a reasonable update procedure?
4349        u = squeeze(random.uniform(0.0, 1.0, size=self.dims))
4350        T = self.T
4351        xc = (sign(u-0.5)*T*((1+1.0/T)**abs(2*u-1)-1.0)+1.0)/2.0
4352        xnew = xc*(self.upper - self.lower)+self.lower
4353        return xnew
4354       
4355    def update_temp(self):
4356        self.k += 1
4357        self.T = self.T0*self.slope**self.k
4358       
4359class _state(object):
4360    def __init__(self):
4361        self.x = None
4362        self.cost = None
4363
4364def makeTsched(data):
4365    if data['Algorithm'] == 'fast':
4366        sched = fast_sa()
4367        sched.quench = data['fast parms'][0]
4368        sched.c = data['fast parms'][1]
4369    elif data['Algorithm'] == 'log':
4370        sched = log_sa()
4371        sched.slope = data['log slope']
4372    sched.T0 = data['Annealing'][0]
4373    if not sched.T0:
4374        sched.T0 = 50.
4375    Tf = data['Annealing'][1]
4376    if not Tf:
4377        Tf = 0.001
4378    Tsched = [sched.T0,]
4379    while Tsched[-1] > Tf:
4380        sched.update_temp()
4381        Tsched.append(sched.T)
4382    return Tsched[1:]
4383   
4384def anneal(func, x0, args=(), schedule='fast', 
4385           T0=None, Tf=1e-12, maxeval=None, maxaccept=None, maxiter=400,
4386           feps=1e-6, quench=1.0, c=1.0,
4387           lower=-100, upper=100, dwell=50, slope=0.9,ranStart=False,
4388           ranRange=0.10,autoRan=False,dlg=None):
4389    """Minimize a function using simulated annealing.
4390
4391    Schedule is a schedule class implementing the annealing schedule.
4392    Available ones are 'fast', 'cauchy', 'boltzmann'
4393
4394    :param callable func: f(x, \*args)
4395        Function to be optimized.
4396    :param ndarray x0:
4397        Initial guess.
4398    :param tuple args:
4399        Extra parameters to `func`.
4400    :param base_schedule schedule:
4401        Annealing schedule to use (a class).
4402    :param float T0:
4403        Initial Temperature (estimated as 1.2 times the largest
4404        cost-function deviation over random points in the range).
4405    :param float Tf:
4406        Final goal temperature.
4407    :param int maxeval:
4408        Maximum function evaluations.
4409    :param int maxaccept:
4410        Maximum changes to accept.
4411    :param int maxiter:
4412        Maximum cooling iterations.
4413    :param float feps:
4414        Stopping relative error tolerance for the function value in
4415        last four coolings.
4416    :param float quench,c:
4417        Parameters to alter fast_sa schedule.
4418    :param float/ndarray lower,upper:
4419        Lower and upper bounds on `x`.
4420    :param int dwell:
4421        The number of times to search the space at each temperature.
4422    :param float slope:
4423        Parameter for log schedule
4424    :param bool ranStart=False:
4425        True for set 10% of ranges about x
4426
4427    :returns: (xmin, Jmin, T, feval, iters, accept, retval) where
4428
4429     * xmin (ndarray): Point giving smallest value found.
4430     * Jmin (float): Minimum value of function found.
4431     * T (float): Final temperature.
4432     * feval (int): Number of function evaluations.
4433     * iters (int): Number of cooling iterations.
4434     * accept (int): Number of tests accepted.
4435     * retval (int): Flag indicating stopping condition:
4436
4437              *  0: Points no longer changing
4438              *  1: Cooled to final temperature
4439              *  2: Maximum function evaluations
4440              *  3: Maximum cooling iterations reached
4441              *  4: Maximum accepted query locations reached
4442              *  5: Final point not the minimum amongst encountered points
4443
4444    *Notes*:
4445    Simulated annealing is a random algorithm which uses no derivative
4446    information from the function being optimized. In practice it has
4447    been more useful in discrete optimization than continuous
4448    optimization, as there are usually better algorithms for continuous
4449    optimization problems.
4450
4451    Some experimentation by trying the difference temperature
4452    schedules and altering their parameters is likely required to
4453    obtain good performance.
4454
4455    The randomness in the algorithm comes from random sampling in numpy.
4456    To obtain the same results you can call numpy.random.seed with the
4457    same seed immediately before calling scipy.optimize.anneal.
4458
4459    We give a brief description of how the three temperature schedules
4460    generate new points and vary their temperature. Temperatures are
4461    only updated with iterations in the outer loop. The inner loop is
4462    over range(dwell), and new points are generated for
4463    every iteration in the inner loop. (Though whether the proposed
4464    new points are accepted is probabilistic.)
4465
4466    For readability, let d denote the dimension of the inputs to func.
4467    Also, let x_old denote the previous state, and k denote the
4468    iteration number of the outer loop. All other variables not
4469    defined below are input variables to scipy.optimize.anneal itself.
4470
4471    In the 'fast' schedule the updates are ::
4472
4473        u ~ Uniform(0, 1, size=d)
4474        y = sgn(u - 0.5) * T * ((1+ 1/T)**abs(2u-1) -1.0)
4475        xc = y * (upper - lower)
4476        x_new = x_old + xc
4477
4478        T_new = T0 * exp(-c * k**quench)
4479
4480    """
4481   
4482    ''' Scipy license:
4483        Copyright (c) 2001, 2002 Enthought, Inc.
4484    All rights reserved.
4485   
4486    Copyright (c) 2003-2016 SciPy Developers.
4487    All rights reserved.
4488   
4489    Redistribution and use in source and binary forms, with or without
4490    modification, are permitted provided that the following conditions are met:
4491   
4492      a. Redistributions of source code must retain the above copyright notice,
4493         this list of conditions and the following disclaimer.
4494      b. Redistributions in binary form must reproduce the above copyright
4495         notice, this list of conditions and the following disclaimer in the
4496         documentation and/or other materials provided with the distribution.
4497      c. Neither the name of Enthought nor the names of the SciPy Developers
4498         may be used to endorse or promote products derived from this software
4499         without specific prior written permission.
4500    '''
4501    x0 = asarray(x0)
4502    lower = asarray(lower)
4503    upper = asarray(upper)
4504
4505    schedule = eval(schedule+'_sa()')
4506    #   initialize the schedule
4507    schedule.init(dims=shape(x0),func=func,args=args,T0=T0,lower=lower, upper=upper,
4508        c=c, quench=quench, dwell=dwell, slope=slope)
4509
4510    current_state, last_state, best_state = _state(), _state(), _state()
4511    if ranStart:
4512        schedule.set_range(x0,ranRange)
4513    if T0 is None:
4514        x0 = schedule.getstart_temp(best_state)
4515    else:
4516        x0 = random.uniform(size=len(x0))*(upper-lower) + lower
4517        best_state.x = None
4518        best_state.cost = numpy.Inf
4519
4520    last_state.x = asarray(x0).copy()
4521    fval = func(x0,*args)
4522    schedule.feval += 1
4523    last_state.cost = fval
4524    if last_state.cost < best_state.cost:
4525        best_state.cost = fval
4526        best_state.x = asarray(x0).copy()
4527    schedule.T = schedule.T0
4528    fqueue = [100, 300, 500, 700]
4529    iters = 1
4530    keepGoing = True
4531    bestn = 0
4532    while keepGoing:
4533        retval = 0
4534        for n in range(dwell):
4535            current_state.x = schedule.update_guess(last_state.x)
4536            current_state.cost = func(current_state.x,*args)
4537            schedule.feval += 1
4538
4539            dE = current_state.cost - last_state.cost
4540            if schedule.accept_test(dE):
4541                last_state.x = current_state.x.copy()
4542                last_state.cost = current_state.cost
4543                if last_state.cost < best_state.cost:
4544                    best_state.x = last_state.x.copy()
4545                    best_state.cost = last_state.cost
4546                    bestn = n
4547                    if best_state.cost < 1.0 and autoRan:
4548                        schedule.set_range(x0,best_state.cost/2.)                       
4549        if dlg:
4550            GoOn = dlg.Update(min(100.,best_state.cost*100),
4551                newmsg='%s%8.5f, %s%d\n%s%8.4f%s'%('Temperature =',schedule.T, \
4552                    'Best trial:',bestn,  \
4553                    'MC/SA Residual:',best_state.cost*100,'%', \
4554                    ))[0]
4555            if not GoOn:
4556                best_state.x = last_state.x.copy()
4557                best_state.cost = last_state.cost
4558                retval = 5
4559        schedule.update_temp()
4560        iters += 1
4561        # Stopping conditions
4562        # 0) last saved values of f from each cooling step
4563        #     are all very similar (effectively cooled)
4564        # 1) Tf is set and we are below it
4565        # 2) maxeval is set and we are past it
4566        # 3) maxiter is set and we are past it
4567        # 4) maxaccept is set and we are past it
4568        # 5) user canceled run via progress bar
4569
4570        fqueue.append(squeeze(last_state.cost))