source: trunk/GSASIImath.py @ 4456

Last change on this file since 4456 was 4431, checked in by toby, 5 years ago

new code for locating RBs; redo GUI for RB extractor

  • Property svn:eol-style set to native
  • Property svn:keywords set to Date Author Revision URL Id
File size: 194.5 KB
Line 
1# -*- coding: utf-8 -*-
2#GSASIImath - major mathematics routines
3########### SVN repository information ###################
4# $Date: 2020-05-24 22:19:15 +0000 (Sun, 24 May 2020) $
5# $Author: vondreele $
6# $Revision: 4431 $
7# $URL: trunk/GSASIImath.py $
8# $Id: GSASIImath.py 4431 2020-05-24 22:19:15Z 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: 4431 $")
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    DisAglCtls = General['DisAglCtls']   
622    radiusFactor = DisAglCtls['Factors'][0]
623    AtInfo = dict(zip(atTypes,Radii)) #or General['BondRadii']
624    Orig = atNames.index(FrstName)
625    OId = Atoms[Orig][cia+8]
626    OType = Atoms[Orig][ct]
627    XYZ = getAtomXYZ(Atoms,cx)       
628    Neigh = []
629    Ids = []
630    Dx = np.inner(Amat,XYZ-XYZ[Orig]).T
631    dist = np.sqrt(np.sum(Dx**2,axis=1))
632    sumR = np.array([AtInfo[OType]+AtInfo[atom[ct]] for atom in Atoms])
633    IndB = ma.nonzero(ma.masked_greater(dist-radiusFactor*sumR,0.))
634    for j in IndB[0]:
635        if j != Orig:
636            if AtNames[j] not in notName:
637                Neigh.append([AtNames[j],dist[j],True])
638                Ids.append(Atoms[j][cia+8])
639    return Neigh,[OId,Ids]
640
641def FindOctahedron(results):
642    Octahedron = np.array([[1.,0,0],[0,1.,0],[0,0,1.],[-1.,0,0],[0,-1.,0],[0,0,-1.]])
643    Polygon = np.array([result[3] for result in results])
644    Dists = np.array([np.sqrt(np.sum(axis**2)) for axis in Polygon])
645    bond = np.mean(Dists)
646    std = np.std(Dists)
647    Norms = Polygon/Dists[:,nxs]
648    Tilts = acosd(np.dot(Norms,Octahedron[0]))
649    iAng = np.argmin(Tilts)
650    Qavec = np.cross(Norms[iAng],Octahedron[0])
651    QA = AVdeg2Q(Tilts[iAng],Qavec)
652    newNorms = prodQVQ(QA,Norms)
653    Rots = acosd(np.dot(newNorms,Octahedron[1]))
654    jAng = np.argmin(Rots)
655    Qbvec = np.cross(Norms[jAng],Octahedron[1])
656    QB = AVdeg2Q(Rots[jAng],Qbvec)
657    QQ = prodQQ(QA,QB)   
658    newNorms = prodQVQ(QQ,Norms)
659    dispVecs = np.array([norm[:,nxs]-Octahedron.T for norm in newNorms])
660    disp = np.sqrt(np.sum(dispVecs**2,axis=1))
661    dispids = np.argmin(disp,axis=1)
662    vecDisp = np.array([dispVecs[i,:,dispid] for i,dispid in enumerate(dispids)])
663    Disps = np.array([disp[i,dispid] for i,dispid in enumerate(dispids)])
664    meanDisp = np.mean(Disps)
665    stdDisp = np.std(Disps)
666    A,V = Q2AVdeg(QQ)
667    return bond,std,meanDisp,stdDisp,A,V,vecDisp
668   
669def FindTetrahedron(results):
670    Tetrahedron = np.array([[1.,1,1],[1,-1,-1],[-1,1,-1],[-1,-1,1]])/np.sqrt(3.)
671    Polygon = np.array([result[3] for result in results])
672    Dists = np.array([np.sqrt(np.sum(axis**2)) for axis in Polygon])
673    bond = np.mean(Dists)
674    std = np.std(Dists)
675    Norms = Polygon/Dists[:,nxs]
676    Tilts = acosd(np.dot(Norms,Tetrahedron[0]))
677    iAng = np.argmin(Tilts)
678    Qavec = np.cross(Norms[iAng],Tetrahedron[0])
679    QA = AVdeg2Q(Tilts[iAng],Qavec)
680    newNorms = prodQVQ(QA,Norms)
681    Rots = acosd(np.dot(newNorms,Tetrahedron[1]))
682    jAng = np.argmin(Rots)
683    Qbvec = np.cross(Norms[jAng],Tetrahedron[1])
684    QB = AVdeg2Q(Rots[jAng],Qbvec)
685    QQ = prodQQ(QA,QB)   
686    newNorms = prodQVQ(QQ,Norms)
687    dispVecs = np.array([norm[:,nxs]-Tetrahedron.T for norm in newNorms])
688    disp = np.sqrt(np.sum(dispVecs**2,axis=1))
689    dispids = np.argmin(disp,axis=1)
690    vecDisp = np.array([dispVecs[i,:,dispid] for i,dispid in enumerate(dispids)])
691    Disps = np.array([disp[i,dispid] for i,dispid in enumerate(dispids)])
692    meanDisp = np.mean(Disps)
693    stdDisp = np.std(Disps)
694    A,V = Q2AVdeg(QQ)
695    return bond,std,meanDisp,stdDisp,A,V,vecDisp
696   
697def FindAllNeighbors(phase,FrstName,AtNames,notName='',Orig=None,Short=False):
698    General = phase['General']
699    cx,ct,cs,cia = General['AtomPtrs']
700    Atoms = phase['Atoms']
701    atNames = [atom[ct-1] for atom in Atoms]
702    atTypes = [atom[ct] for atom in Atoms]
703    Cell = General['Cell'][1:7]
704    Amat,Bmat = G2lat.cell2AB(Cell)
705    SGData = General['SGData']
706    indices = (-1,0,1)
707    Units = np.array([[h,k,l] for h in indices for k in indices for l in indices])
708    AtTypes = General['AtomTypes']
709    Radii = np.array(General['BondRadii'])
710    DisAglCtls = General['DisAglCtls']   
711    radiusFactor = DisAglCtls['Factors'][0]
712    AtInfo = dict(zip(AtTypes,Radii)) #or General['BondRadii']
713    if Orig is None:
714        Orig = atNames.index(FrstName)
715    OId = Atoms[Orig][cia+8]
716    OType = Atoms[Orig][ct]
717    XYZ = getAtomXYZ(Atoms,cx)       
718    Oxyz = XYZ[Orig]
719    Neigh = []
720    Ids = []
721    sumR = np.array([AtInfo[OType]+AtInfo[atom[ct]] for atom in Atoms])
722    sumR = np.reshape(np.tile(sumR,27),(27,-1))
723    results = []
724    for xyz in XYZ:
725        results.append(G2spc.GenAtom(xyz,SGData,False,Move=False))
726    for iA,result in enumerate(results):
727        if iA != Orig:               
728            for [Txyz,Top,Tunit,Spn] in result:
729                Dx = np.array([Txyz-Oxyz+unit for unit in Units])
730                dx = np.inner(Dx,Amat)
731                dist = np.sqrt(np.sum(dx**2,axis=1))
732                IndB = ma.nonzero(ma.masked_greater(dist-radiusFactor*sumR[:,iA],0.))
733                for iU in IndB[0]:
734                    if AtNames[iA%len(AtNames)] != notName:
735                        unit = Units[iU]
736                        if np.any(unit):
737                            Topstr = ' +(%4d)[%2d,%2d,%2d]'%(Top,unit[0],unit[1],unit[2])
738                        else:
739                            Topstr = ' +(%4d)'%(Top)
740                        if Short:
741                            Neigh.append([AtNames[iA%len(AtNames)],dist[iU],True])
742                        else:
743                            Neigh.append([AtNames[iA]+Topstr,atTypes[iA],dist[iU],dx[iU]])
744                        Ids.append(Atoms[iA][cia+8])
745    return Neigh,[OId,Ids]
746   
747def calcBond(A,Ax,Bx,MTCU):
748    cell = G2lat.A2cell(A)
749    Amat,Bmat = G2lat.cell2AB(cell)
750    M,T,C,U = MTCU
751    Btx = np.inner(M,Bx)+T+C+U
752    Dx = Btx-Ax
753    dist = np.sqrt(np.inner(Amat,Dx))
754    return dist
755   
756def AddHydrogens(AtLookUp,General,Atoms,AddHydId):
757   
758    def getTransMat(RXYZ,OXYZ,TXYZ,Amat):
759        Vec = np.inner(Amat,np.array([OXYZ-TXYZ[0],RXYZ-TXYZ[0]])).T           
760        Vec /= np.sqrt(np.sum(Vec**2,axis=1))[:,nxs]
761        Mat2 = np.cross(Vec[0],Vec[1])      #UxV
762        Mat2 /= np.sqrt(np.sum(Mat2**2))
763        Mat3 = np.cross(Mat2,Vec[0])        #(UxV)xU
764        return nl.inv(np.array([Vec[0],Mat2,Mat3]))       
765   
766    cx,ct,cs,cia = General['AtomPtrs']
767    Cell = General['Cell'][1:7]
768    Amat,Bmat = G2lat.cell2AB(Cell)
769    nBonds = AddHydId[-1]+len(AddHydId[1])
770    Oatom = GetAtomsById(Atoms,AtLookUp,[AddHydId[0],])[0]
771    OXYZ = np.array(Oatom[cx:cx+3])
772    if 'I' in Oatom[cia]:
773        Uiso = Oatom[cia+1]
774    else:
775        Uiso = (Oatom[cia+2]+Oatom[cia+3]+Oatom[cia+4])/3.0       #simple average
776    Uiso = max(Uiso,0.005)                      #set floor!
777    Tatoms = GetAtomsById(Atoms,AtLookUp,AddHydId[1])
778    TXYZ = np.array([tatom[cx:cx+3] for tatom in Tatoms]) #3 x xyz
779    if nBonds == 4:
780        if AddHydId[-1] == 1:
781            Vec = TXYZ-OXYZ
782            Len = np.sqrt(np.sum(np.inner(Amat,Vec).T**2,axis=0))
783            Vec = np.sum(Vec/Len,axis=0)
784            Len = np.sqrt(np.sum(Vec**2))
785            Hpos = OXYZ-0.98*np.inner(Bmat,Vec).T/Len
786            HU = 1.1*Uiso
787            return [Hpos,],[HU,]
788        elif AddHydId[-1] == 2:
789            Vec = np.inner(Amat,TXYZ-OXYZ).T
790            Vec[0] += Vec[1]            #U - along bisector
791            Vec /= np.sqrt(np.sum(Vec**2,axis=1))[:,nxs]
792            Mat2 = np.cross(Vec[0],Vec[1])      #UxV
793            Mat2 /= np.sqrt(np.sum(Mat2**2))
794            Mat3 = np.cross(Mat2,Vec[0])        #(UxV)xU
795            iMat = nl.inv(np.array([Vec[0],Mat2,Mat3]))
796            Hpos = np.array([[-0.97*cosd(54.75),0.97*sind(54.75),0.],
797                [-0.97*cosd(54.75),-0.97*sind(54.75),0.]])
798            HU = 1.2*Uiso*np.ones(2)
799            Hpos = np.inner(Bmat,np.inner(iMat,Hpos).T).T+OXYZ
800            return Hpos,HU
801        else:
802            Ratom = GetAtomsById(Atoms,AtLookUp,[AddHydId[2],])[0]
803            RXYZ = np.array(Ratom[cx:cx+3])
804            iMat = getTransMat(RXYZ,OXYZ,TXYZ,Amat)
805            a = 0.96*cosd(70.5)
806            b = 0.96*sind(70.5)
807            Hpos = np.array([[a,0.,-b],[a,-b*cosd(30.),0.5*b],[a,b*cosd(30.),0.5*b]])
808            Hpos = np.inner(Bmat,np.inner(iMat,Hpos).T).T+OXYZ
809            HU = 1.5*Uiso*np.ones(3)
810            return Hpos,HU         
811    elif nBonds == 3:
812        if AddHydId[-1] == 1:
813            Vec = np.sum(TXYZ-OXYZ,axis=0)               
814            Len = np.sqrt(np.sum(np.inner(Amat,Vec).T**2))
815            Vec = -0.93*Vec/Len
816            Hpos = OXYZ+Vec
817            HU = 1.1*Uiso
818            return [Hpos,],[HU,]
819        elif AddHydId[-1] == 2:
820            Ratom = GetAtomsById(Atoms,AtLookUp,[AddHydId[2],])[0]
821            RXYZ = np.array(Ratom[cx:cx+3])
822            iMat = getTransMat(RXYZ,OXYZ,TXYZ,Amat)
823            a = 0.93*cosd(60.)
824            b = 0.93*sind(60.)
825            Hpos = [[a,b,0],[a,-b,0]]
826            Hpos = np.inner(Bmat,np.inner(iMat,Hpos).T).T+OXYZ
827            HU = 1.2*Uiso*np.ones(2)
828            return Hpos,HU
829    else:   #2 bonds
830        if 'C' in Oatom[ct]:
831            Vec = TXYZ[0]-OXYZ
832            Len = np.sqrt(np.sum(np.inner(Amat,Vec).T**2))
833            Vec = -0.93*Vec/Len
834            Hpos = OXYZ+Vec
835            HU = 1.1*Uiso
836            return [Hpos,],[HU,]
837        elif 'O' in Oatom[ct]:
838            mapData = General['Map']
839            Ratom = GetAtomsById(Atoms,AtLookUp,[AddHydId[2],])[0]
840            RXYZ = np.array(Ratom[cx:cx+3])
841            iMat = getTransMat(RXYZ,OXYZ,TXYZ,Amat)
842            a = 0.82*cosd(70.5)
843            b = 0.82*sind(70.5)
844            azm = np.arange(0.,360.,5.)
845            Hpos = np.array([[a,b*cosd(x),b*sind(x)] for x in azm])
846            Hpos = np.inner(Bmat,np.inner(iMat,Hpos).T).T+OXYZ
847            Rhos = np.array([getRho(pos,mapData) for pos in Hpos])
848            imax = np.argmax(Rhos)
849            HU = 1.5*Uiso
850            return [Hpos[imax],],[HU,]
851    return [],[]
852       
853#def AtomUij2TLS(atomData,atPtrs,Amat,Bmat,rbObj):   #unfinished & not used
854#    '''default doc string
855#   
856#    :param type name: description
857#   
858#    :returns: type name: description
859#   
860#    '''
861#    for atom in atomData:
862#        XYZ = np.inner(Amat,atom[cx:cx+3])
863#        if atom[cia] == 'A':
864#            UIJ = atom[cia+2:cia+8]
865               
866def TLS2Uij(xyz,g,Amat,rbObj):    #not used anywhere, but could be?
867    '''default doc string
868   
869    :param type name: description
870   
871    :returns: type name: description
872   
873    '''
874    TLStype,TLS = rbObj['ThermalMotion'][:2]
875    Tmat = np.zeros((3,3))
876    Lmat = np.zeros((3,3))
877    Smat = np.zeros((3,3))
878    gvec = np.sqrt(np.array([g[0][0]**2,g[1][1]**2,g[2][2]**2,
879        g[0][0]*g[1][1],g[0][0]*g[2][2],g[1][1]*g[2][2]]))
880    if 'T' in TLStype:
881        Tmat = G2lat.U6toUij(TLS[:6])
882    if 'L' in TLStype:
883        Lmat = G2lat.U6toUij(TLS[6:12])
884    if 'S' in TLStype:
885        Smat = np.array([[TLS[18],TLS[12],TLS[13]],[TLS[14],TLS[19],TLS[15]],[TLS[16],TLS[17],0] ])
886    XYZ = np.inner(Amat,xyz)
887    Axyz = np.array([[ 0,XYZ[2],-XYZ[1]], [-XYZ[2],0,XYZ[0]], [XYZ[1],-XYZ[0],0]] )
888    Umat = Tmat+np.inner(Axyz,Smat)+np.inner(Smat.T,Axyz.T)+np.inner(np.inner(Axyz,Lmat),Axyz.T)
889    beta = np.inner(np.inner(g,Umat),g)
890    return G2lat.UijtoU6(beta)*gvec   
891       
892def AtomTLS2UIJ(atomData,atPtrs,Amat,rbObj):    #not used anywhere, but could be?
893    '''default doc string
894   
895    :param type name: description
896   
897    :returns: type name: description
898   
899    '''
900    cx,ct,cs,cia = atPtrs
901    TLStype,TLS = rbObj['ThermalMotion'][:2]
902    Tmat = np.zeros((3,3))
903    Lmat = np.zeros((3,3))
904    Smat = np.zeros((3,3))
905    G,g = G2lat.A2Gmat(Amat)
906    gvec = 1./np.sqrt(np.array([g[0][0],g[1][1],g[2][2],g[0][1],g[0][2],g[1][2]]))
907    if 'T' in TLStype:
908        Tmat = G2lat.U6toUij(TLS[:6])
909    if 'L' in TLStype:
910        Lmat = G2lat.U6toUij(TLS[6:12])
911    if 'S' in TLStype:
912        Smat = np.array([ [TLS[18],TLS[12],TLS[13]], [TLS[14],TLS[19],TLS[15]], [TLS[16],TLS[17],0] ])
913    for atom in atomData:
914        XYZ = np.inner(Amat,atom[cx:cx+3])
915        Axyz = np.array([ 0,XYZ[2],-XYZ[1], -XYZ[2],0,XYZ[0], XYZ[1],-XYZ[0],0],ndmin=2 )
916        if 'U' in TLStype:
917            atom[cia+1] = TLS[0]
918            atom[cia] = 'I'
919        else:
920            atom[cia] = 'A'
921            Umat = Tmat+np.inner(Axyz,Smat)+np.inner(Smat.T,Axyz.T)+np.inner(np.inner(Axyz,Lmat),Axyz.T)
922            beta = np.inner(np.inner(g,Umat),g)
923            atom[cia+2:cia+8] = G2spc.U2Uij(beta/gvec)
924
925def GetXYZDist(xyz,XYZ,Amat):
926    '''gets distance from position xyz to all XYZ, xyz & XYZ are np.array
927        and are in crystal coordinates; Amat is crystal to Cart matrix
928   
929    :param type name: description
930   
931    :returns: type name: description
932   
933    '''
934    return np.sqrt(np.sum(np.inner(Amat,XYZ-xyz)**2,axis=0))
935
936def getAtomXYZ(atoms,cx):
937    '''Create an array of fractional coordinates from the atoms list
938   
939    :param list atoms: atoms object as found in tree
940    :param int cx: offset to where coordinates are found
941   
942    :returns: np.array with shape (n,3)   
943    '''
944    XYZ = []
945    for atom in atoms:
946        XYZ.append(atom[cx:cx+3])
947    return np.array(XYZ)
948
949def getRBTransMat(X,Y):
950    '''Get transformation for Cartesian axes given 2 vectors
951    X will  be parallel to new X-axis; X cross Y will be new Z-axis &
952    (X cross Y) cross Y will be new Y-axis
953    Useful for rigid body axes definintion
954   
955    :param array X: normalized vector
956    :param array Y: normalized vector
957   
958    :returns: array M: transformation matrix
959   
960    use as XYZ' = np.inner(M,XYZ) where XYZ are Cartesian
961   
962    '''
963    Mat2 = np.cross(X,Y)      #UxV-->Z
964    Mat2 /= np.sqrt(np.sum(Mat2**2))
965    Mat3 = np.cross(Mat2,X)        #(UxV)xU-->Y
966    Mat3 /= np.sqrt(np.sum(Mat3**2))
967    return np.array([X,Mat3,Mat2])       
968               
969def RotateRBXYZ(Bmat,Cart,oriQ):
970    '''rotate & transform cartesian coordinates to crystallographic ones
971    no translation applied. To be used for numerical derivatives
972   
973    :param type name: description
974   
975    :returns: type name: description
976   
977    '''
978    ''' returns crystal coordinates for atoms described by RBObj
979    '''
980    XYZ = np.zeros_like(Cart)
981    for i,xyz in enumerate(Cart):
982        XYZ[i] = np.inner(Bmat,prodQVQ(oriQ,xyz))
983    return XYZ
984
985def UpdateRBXYZ(Bmat,RBObj,RBData,RBType):
986    '''returns crystal coordinates for atoms described by RBObj
987   
988    :param np.array Bmat: see :func:`GSASIIlattice.cell2AB`
989    :param dict rbObj: rigid body selection/orientation information
990    :param dict RBData: rigid body tree data structure
991    :param str RBType: rigid body type, 'Vector' or 'Residue'
992
993    :returns: coordinates for rigid body as XYZ,Cart where XYZ is
994       the location in crystal coordinates and Cart is in cartesian
995    '''
996    RBRes = RBData[RBType][RBObj['RBId']]
997    if RBType == 'Vector':
998        vecs = RBRes['rbVect']
999        mags = RBRes['VectMag']
1000        Cart = np.zeros_like(vecs[0])
1001        for vec,mag in zip(vecs,mags):
1002            Cart += vec*mag
1003    elif RBType == 'Residue':
1004        Cart = np.array(RBRes['rbXYZ'])
1005        for tor,seq in zip(RBObj['Torsions'],RBRes['rbSeq']):
1006            QuatA = AVdeg2Q(tor[0],Cart[seq[0]]-Cart[seq[1]])
1007            Cart[seq[3]] = prodQVQ(QuatA,(Cart[seq[3]]-Cart[seq[1]]))+Cart[seq[1]]
1008    XYZ = np.zeros_like(Cart)
1009    for i,xyz in enumerate(Cart):
1010        XYZ[i] = np.inner(Bmat,prodQVQ(RBObj['Orient'][0],xyz))+RBObj['Orig'][0]
1011    return XYZ,Cart
1012
1013def UpdateMCSAxyz(Bmat,MCSA):
1014    '''default doc string
1015   
1016    :param type name: description
1017   
1018    :returns: type name: description
1019   
1020    '''
1021    xyz = []
1022    atTypes = []
1023    iatm = 0
1024    for model in MCSA['Models'][1:]:        #skip the MD model
1025        if model['Type'] == 'Atom':
1026            xyz.append(model['Pos'][0])
1027            atTypes.append(model['atType'])
1028            iatm += 1
1029        else:
1030            RBRes = MCSA['rbData'][model['Type']][model['RBId']]
1031            Pos = np.array(model['Pos'][0])
1032            Ori = np.array(model['Ori'][0])
1033            Qori = AVdeg2Q(Ori[0],Ori[1:])
1034            if model['Type'] == 'Vector':
1035                vecs = RBRes['rbVect']
1036                mags = RBRes['VectMag']
1037                Cart = np.zeros_like(vecs[0])
1038                for vec,mag in zip(vecs,mags):
1039                    Cart += vec*mag
1040            elif model['Type'] == 'Residue':
1041                Cart = np.array(RBRes['rbXYZ'])
1042                for itor,seq in enumerate(RBRes['rbSeq']):
1043                    QuatA = AVdeg2Q(model['Tor'][0][itor],Cart[seq[0]]-Cart[seq[1]])
1044                    Cart[seq[3]] = prodQVQ(QuatA,(Cart[seq[3]]-Cart[seq[1]]))+Cart[seq[1]]
1045            if model['MolCent'][1]:
1046                Cart -= model['MolCent'][0]
1047            for i,x in enumerate(Cart):
1048                xyz.append(np.inner(Bmat,prodQVQ(Qori,x))+Pos)
1049                atType = RBRes['rbTypes'][i]
1050                atTypes.append(atType)
1051                iatm += 1
1052    return np.array(xyz),atTypes
1053   
1054def SetMolCent(model,RBData):
1055    '''default doc string
1056   
1057    :param type name: description
1058   
1059    :returns: type name: description
1060   
1061    '''
1062    rideList = []
1063    RBRes = RBData[model['Type']][model['RBId']]
1064    if model['Type'] == 'Vector':
1065        vecs = RBRes['rbVect']
1066        mags = RBRes['VectMag']
1067        Cart = np.zeros_like(vecs[0])
1068        for vec,mag in zip(vecs,mags):
1069            Cart += vec*mag
1070    elif model['Type'] == 'Residue':
1071        Cart = np.array(RBRes['rbXYZ'])
1072        for seq in RBRes['rbSeq']:
1073            rideList += seq[3]
1074    centList = set(range(len(Cart)))-set(rideList)
1075    cent = np.zeros(3)
1076    for i in centList:
1077        cent += Cart[i]
1078    model['MolCent'][0] = cent/len(centList)   
1079   
1080def UpdateRBUIJ(Bmat,Cart,RBObj):
1081    '''default doc string
1082   
1083    :param type name: description
1084   
1085    :returns: type name: description
1086   
1087    '''
1088    ''' returns atom I/A, Uiso or UIJ for atoms at XYZ as described by RBObj
1089    '''
1090    TLStype,TLS = RBObj['ThermalMotion'][:2]
1091    T = np.zeros(6)
1092    L = np.zeros(6)
1093    S = np.zeros(8)
1094    if 'T' in TLStype:
1095        T = TLS[:6]
1096    if 'L' in TLStype:
1097        L = np.array(TLS[6:12])*(np.pi/180.)**2
1098    if 'S' in TLStype:
1099        S = np.array(TLS[12:])*(np.pi/180.)
1100    g = nl.inv(np.inner(Bmat,Bmat))
1101    gvec = np.sqrt(np.array([g[0][0]**2,g[1][1]**2,g[2][2]**2,
1102        g[0][0]*g[1][1],g[0][0]*g[2][2],g[1][1]*g[2][2]]))
1103    Uout = []
1104    Q = RBObj['Orient'][0]
1105    for X in Cart:
1106        X = prodQVQ(Q,X)
1107        if 'U' in TLStype:
1108            Uout.append(['I',TLS[0],0,0,0,0,0,0])
1109        elif not 'N' in TLStype:
1110            U = [0,0,0,0,0,0]
1111            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])
1112            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])
1113            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])
1114            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]+  \
1115                S[4]*X[0]-S[5]*X[1]-(S[6]+S[7])*X[2]
1116            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]+  \
1117                S[3]*X[2]-S[2]*X[0]+S[6]*X[1]
1118            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]+  \
1119                S[0]*X[1]-S[1]*X[2]+S[7]*X[0]
1120            Umat = G2lat.U6toUij(U)
1121            beta = np.inner(np.inner(Bmat.T,Umat),Bmat)
1122            Uout.append(['A',0.0,]+list(G2lat.UijtoU6(beta)*gvec))
1123        else:
1124            Uout.append(['N',])
1125    return Uout
1126   
1127def GetSHCoeff(pId,parmDict,SHkeys):
1128    '''default doc string
1129   
1130    :param type name: description
1131   
1132    :returns: type name: description
1133   
1134    '''
1135    SHCoeff = {}
1136    for shkey in SHkeys:
1137        shname = str(pId)+'::'+shkey
1138        SHCoeff[shkey] = parmDict[shname]
1139    return SHCoeff
1140       
1141def getMass(generalData):
1142    '''Computes mass of unit cell contents
1143   
1144    :param dict generalData: The General dictionary in Phase
1145   
1146    :returns: float mass: Crystal unit cell mass in AMU.
1147   
1148    '''
1149    mass = 0.
1150    for i,elem in enumerate(generalData['AtomTypes']):
1151        mass += generalData['NoAtoms'][elem]*generalData['AtomMass'][i]
1152    return max(mass,1.0)   
1153
1154def getDensity(generalData):
1155    '''calculate crystal structure density
1156   
1157    :param dict generalData: The General dictionary in Phase
1158   
1159    :returns: float density: crystal density in gm/cm^3
1160   
1161    '''
1162    mass = getMass(generalData)
1163    Volume = generalData['Cell'][7]
1164    density = mass/(0.6022137*Volume)
1165    return density,Volume/mass
1166   
1167def getWave(Parms):
1168    '''returns wavelength from Instrument parameters dictionary
1169   
1170    :param dict Parms: Instrument parameters;
1171        must contain:
1172        Lam: single wavelength
1173        or
1174        Lam1: Ka1 radiation wavelength
1175   
1176    :returns: float wave: wavelength
1177   
1178    '''
1179    try:
1180        return Parms['Lam'][1]
1181    except KeyError:
1182        return Parms['Lam1'][1]
1183       
1184def getMeanWave(Parms):
1185    '''returns mean wavelength from Instrument parameters dictionary
1186   
1187    :param dict Parms: Instrument parameters;
1188        must contain:
1189        Lam: single wavelength
1190        or
1191        Lam1,Lam2: Ka1,Ka2 radiation wavelength
1192        I(L2)/I(L1): Ka2/Ka1 ratio
1193   
1194    :returns: float wave: mean wavelength
1195   
1196    '''
1197    try:
1198        return Parms['Lam'][1]
1199    except KeyError:
1200        meanLam = (Parms['Lam1'][1]+Parms['I(L2)/I(L1)'][1]*Parms['Lam2'][1])/   \
1201            (1.+Parms['I(L2)/I(L1)'][1])
1202        return meanLam
1203   
1204       
1205def El2Mass(Elements):
1206    '''compute molecular weight from Elements
1207   
1208    :param dict Elements: elements in molecular formula;
1209        each must contain
1210        Num: number of atoms in formula
1211        Mass: at. wt.
1212   
1213    :returns: float mass: molecular weight.
1214   
1215    '''
1216    mass = 0
1217    for El in Elements:
1218        mass += Elements[El]['Num']*Elements[El]['Mass']
1219    return mass
1220       
1221def Den2Vol(Elements,density):
1222    '''converts density to molecular volume
1223   
1224    :param dict Elements: elements in molecular formula;
1225        each must contain
1226        Num: number of atoms in formula
1227        Mass: at. wt.
1228    :param float density: material density in gm/cm^3
1229   
1230    :returns: float volume: molecular volume in A^3
1231   
1232    '''
1233    return El2Mass(Elements)/(density*0.6022137)
1234   
1235def Vol2Den(Elements,volume):
1236    '''converts volume to density
1237   
1238    :param dict Elements: elements in molecular formula;
1239        each must contain
1240        Num: number of atoms in formula
1241        Mass: at. wt.
1242    :param float volume: molecular volume in A^3
1243   
1244    :returns: float density: material density in gm/cm^3
1245   
1246    '''
1247    return El2Mass(Elements)/(volume*0.6022137)
1248   
1249def El2EstVol(Elements):
1250    '''Estimate volume from molecular formula; assumes atom volume = 10A^3
1251   
1252    :param dict Elements: elements in molecular formula;
1253        each must contain
1254        Num: number of atoms in formula
1255   
1256    :returns: float volume: estimate of molecular volume in A^3
1257   
1258    '''
1259    vol = 0
1260    for El in Elements:
1261        vol += 10.*Elements[El]['Num']
1262    return vol
1263   
1264def XScattDen(Elements,vol,wave=0.):
1265    '''Estimate X-ray scattering density from molecular formula & volume;
1266    ignores valence, but includes anomalous effects
1267   
1268    :param dict Elements: elements in molecular formula;
1269        each element must contain
1270        Num: number of atoms in formula
1271        Z: atomic number
1272    :param float vol: molecular volume in A^3
1273    :param float wave: optional wavelength in A
1274   
1275    :returns: float rho: scattering density in 10^10cm^-2;
1276        if wave > 0 the includes f' contribution
1277    :returns: float mu: if wave>0 absorption coeff in cm^-1 ; otherwise 0
1278    :returns: float fpp: if wave>0 f" in 10^10cm^-2; otherwise 0
1279   
1280    '''
1281    rho = 0
1282    mu = 0
1283    fpp = 0
1284    if wave: 
1285        Xanom = XAnomAbs(Elements,wave)
1286    for El in Elements:
1287        f0 = Elements[El]['Z']
1288        if wave:
1289            f0 += Xanom[El][0]
1290            fpp += Xanom[El][1]*Elements[El]['Num']
1291            mu += Xanom[El][2]*Elements[El]['Num']
1292        rho += Elements[El]['Num']*f0
1293    return 28.179*rho/vol,0.1*mu/vol,28.179*fpp/vol
1294   
1295def NCScattDen(Elements,vol,wave=0.):
1296    '''Estimate neutron scattering density from molecular formula & volume;
1297    ignores valence, but includes anomalous effects
1298   
1299    :param dict Elements: elements in molecular formula;
1300        each element must contain
1301        Num: number of atoms in formula
1302        Z: atomic number
1303    :param float vol: molecular volume in A^3
1304    :param float wave: optional wavelength in A
1305   
1306    :returns: float rho: scattering density in 10^10cm^-2;
1307        if wave > 0 the includes f' contribution
1308    :returns: float mu: if wave>0 absorption coeff in cm^-1 ; otherwise 0
1309    :returns: float fpp: if wave>0 f" in 10^10cm^-2; otherwise 0
1310   
1311    '''
1312    rho = 0
1313    mu = 0
1314    bpp = 0
1315    for El in Elements:
1316        isotope = Elements[El]['Isotope']
1317        b0 = Elements[El]['Isotopes'][isotope]['SL'][0]
1318        mu += Elements[El]['Isotopes'][isotope].get('SA',0.)*Elements[El]['Num']
1319        if wave and 'BW-LS' in Elements[El]['Isotopes'][isotope]:
1320            Re,Im,E0,gam,A,E1,B,E2 = Elements[El]['Isotopes'][isotope]['BW-LS'][1:]
1321            Emev = 81.80703/wave**2
1322            T0 = Emev-E0
1323            T1 = Emev-E1
1324            T2 = Emev-E2
1325            D0 = T0**2+gam**2
1326            D1 = T1**2+gam**2
1327            D2 = T2**2+gam**2
1328            b0 += Re*(T0/D0+A*T1/D1+B*T2/D2)
1329            bpp += Im*(1/D0+A/D1+B/D2)
1330        else:
1331            bpp += Elements[El]['Isotopes'][isotope]['SL'][1]
1332        rho += Elements[El]['Num']*b0
1333    if wave: mu *= wave
1334    return 100.*rho/vol,mu/vol,100.*bpp/vol
1335   
1336def wavekE(wavekE):
1337    '''Convert wavelength to energy & vise versa
1338   
1339    :param float waveKe:wavelength in A or energy in kE
1340   
1341    :returns float waveKe:the other one
1342   
1343    '''
1344    return 12.397639/wavekE
1345       
1346def XAnomAbs(Elements,wave):
1347    kE = wavekE(wave)
1348    Xanom = {}
1349    for El in Elements:
1350        Orbs = G2el.GetXsectionCoeff(El)
1351        Xanom[El] = G2el.FPcalc(Orbs, kE)
1352    return Xanom        #f',f", mu
1353   
1354################################################################################
1355#### Modulation math
1356################################################################################
1357
1358def makeWaves(waveTypes,FSSdata,XSSdata,USSdata,MSSdata,Mast):
1359    '''
1360    waveTypes: array nAtoms: 'Fourier','ZigZag' or 'Block'
1361    FSSdata: array 2 x atoms x waves    (sin,cos terms)
1362    XSSdata: array 2x3 x atoms X waves (sin,cos terms)
1363    USSdata: array 2x6 x atoms X waves (sin,cos terms)
1364    MSSdata: array 2x3 x atoms X waves (sin,cos terms)
1365   
1366    Mast: array orthogonalization matrix for Uij
1367    '''
1368    ngl = 36                    #selected for integer steps for 1/6,1/4,1/3...
1369    glTau,glWt = pwd.pygauleg(0.,1.,ngl)         #get Gauss-Legendre intervals & weights
1370    mglTau = np.arange(0.,1.,1./ngl)
1371    Ax = np.array(XSSdata[:3]).T   #atoms x waves x sin pos mods
1372    Bx = np.array(XSSdata[3:]).T   #...cos pos mods
1373    Af = np.array(FSSdata[0]).T    #sin frac mods x waves x atoms
1374    Bf = np.array(FSSdata[1]).T    #cos frac mods...
1375    Au = Mast*np.array(G2lat.U6toUij(USSdata[:6])).T   #atoms x waves x sin Uij mods as betaij
1376    Bu = Mast*np.array(G2lat.U6toUij(USSdata[6:])).T   #...cos Uij mods as betaij
1377    Am = np.array(MSSdata[:3]).T   #atoms x waves x sin pos mods
1378    Bm = np.array(MSSdata[3:]).T   #...cos pos mods
1379    nWaves = [Af.shape[1],Ax.shape[1],Au.shape[1],Am.shape[1]] 
1380    if nWaves[0]:
1381        tauF = np.arange(1.,nWaves[0]+1)[:,nxs]*glTau  #Fwaves x ngl
1382        FmodA = Af[:,:,nxs]*np.sin(twopi*tauF[nxs,:,:])   #atoms X Fwaves X ngl
1383        FmodB = Bf[:,:,nxs]*np.cos(twopi*tauF[nxs,:,:])
1384        Fmod = np.sum(1.0+FmodA+FmodB,axis=1)             #atoms X ngl; sum waves
1385    else:
1386        Fmod = 1.0           
1387    XmodZ = np.zeros((Ax.shape[0],Ax.shape[1],3,ngl))
1388    XmodA = np.zeros((Ax.shape[0],Ax.shape[1],3,ngl))
1389    XmodB = np.zeros((Ax.shape[0],Ax.shape[1],3,ngl))
1390    for iatm in range(Ax.shape[0]):
1391        nx = 0
1392        if 'ZigZag' in waveTypes[iatm]:
1393            nx = 1
1394            Tmm = Ax[iatm][0][:2]                       
1395            XYZmax = np.array([Ax[iatm][0][2],Bx[iatm][0][0],Bx[iatm][0][1]])
1396            XmodZ[iatm][0] += posZigZag(glTau,Tmm,XYZmax).T
1397        elif 'Block' in waveTypes[iatm]:
1398            nx = 1
1399            Tmm = Ax[iatm][0][:2]                       
1400            XYZmax = np.array([Ax[iatm][0][2],Bx[iatm][0][0],Bx[iatm][0][1]])
1401            XmodZ[iatm][0] += posBlock(glTau,Tmm,XYZmax).T
1402        tauX = np.arange(1.,nWaves[1]+1-nx)[:,nxs]*glTau  #Xwaves x ngl
1403        if nx:   
1404            XmodA[iatm][:-nx] = Ax[iatm,nx:,:,nxs]*np.sin(twopi*tauX[nxs,:,nxs,:]) #atoms X waves X 3 X ngl
1405            XmodB[iatm][:-nx] = Bx[iatm,nx:,:,nxs]*np.cos(twopi*tauX[nxs,:,nxs,:]) #ditto
1406        else:
1407            XmodA[iatm] = Ax[iatm,:,:,nxs]*np.sin(twopi*tauX[nxs,:,nxs,:]) #atoms X waves X 3 X ngl
1408            XmodB[iatm] = Bx[iatm,:,:,nxs]*np.cos(twopi*tauX[nxs,:,nxs,:]) #ditto
1409    Xmod = np.sum(XmodA+XmodB+XmodZ,axis=1)                #atoms X 3 X ngl; sum waves
1410    Xmod = np.swapaxes(Xmod,1,2)
1411    if nWaves[2]:
1412        tauU = np.arange(1.,nWaves[2]+1)[:,nxs]*glTau     #Uwaves x ngl
1413        UmodA = Au[:,:,:,:,nxs]*np.sin(twopi*tauU[nxs,:,nxs,nxs,:]) #atoms x waves x 3x3 x ngl
1414        UmodB = Bu[:,:,:,:,nxs]*np.cos(twopi*tauU[nxs,:,nxs,nxs,:]) #ditto
1415        Umod = np.swapaxes(np.sum(UmodA+UmodB,axis=1),1,3)      #atoms x 3x3 x ngl; sum waves
1416    else:
1417        Umod = 1.0
1418    if nWaves[3]:
1419        tauM = np.arange(1.,nWaves[3]+1-nx)[:,nxs]*mglTau  #Mwaves x ngl
1420        MmodA = Am[:,:,:,nxs]*np.sin(twopi*tauM[nxs,:,nxs,:]) #atoms X waves X 3 X ngl
1421        MmodB = Bm[:,:,:,nxs]*np.cos(twopi*tauM[nxs,:,nxs,:]) #ditto
1422        Mmod = np.sum(MmodA+MmodB,axis=1)
1423        Mmod = np.swapaxes(Mmod,1,2)    #Mxyz,Ntau,Natm
1424    else:
1425        Mmod = 1.0
1426    return ngl,nWaves,Fmod,Xmod,Umod,Mmod,glTau,glWt
1427
1428def MagMod(glTau,XYZ,modQ,MSSdata,SGData,SSGData):
1429    '''
1430    this needs to make magnetic moment modulations & magnitudes as
1431    fxn of gTau points
1432    '''
1433    Am = np.array(MSSdata[3:]).T   #atoms x waves x cos pos mods
1434    Bm = np.array(MSSdata[:3]).#...sin pos mods
1435    nWaves = Am.shape[1]
1436    SGMT = np.array([ops[0] for ops in SGData['SGOps']])        #not .T!!
1437    Sinv = np.array([nl.inv(ops[0]) for ops in SSGData['SSGOps']])
1438    SGT = np.array([ops[1] for ops in SSGData['SSGOps']])
1439    if SGData['SGInv']:
1440        SGMT = np.vstack((SGMT,-SGMT))
1441        Sinv =np.vstack((Sinv,-Sinv))
1442        SGT = np.vstack((SGT,-SGT))
1443    SGMT = np.vstack([SGMT for cen in SGData['SGCen']])
1444    Sinv = np.vstack([Sinv for cen in SGData['SGCen']])
1445    SGT = np.vstack([SGT+cen for cen in SSGData['SSGCen']])%1.
1446    if SGData['SGGray']:
1447        SGMT = np.vstack((SGMT,SGMT))
1448        Sinv =np.vstack((Sinv,Sinv))
1449        SGT = np.vstack((SGT,SGT+.5))%1.
1450    mst = Sinv[:,3,:3]
1451    epsinv = Sinv[:,3,3]
1452    phi = np.inner(XYZ,modQ).T
1453    TA = np.sum(mst[nxs,:,:]*(XYZ-SGT[:,:3][nxs,:,:]),axis=-1).T
1454    tauT =  TA[nxs,:,:] + epsinv[nxs,:,nxs]*(glTau[:,nxs,nxs]-SGT[:,3][nxs,:,nxs]+phi[nxs,:,:])
1455    modind = np.arange(nWaves)+1.
1456    phase = (modind[:,nxs,nxs]*tauT)     #Nops,Natm,Nwave
1457    psin = np.sin(twopi*phase)
1458    pcos = np.cos(twopi*phase)
1459    MmodA = np.sum(Am[nxs,nxs,:,:,:]*pcos[:,:,:,nxs,nxs],axis=3)/2.    #cos term
1460    MmodB = np.sum(Bm[nxs,nxs,:,:,:]*psin[:,:,:,nxs,nxs],axis=3)/2.    #sin term
1461    MmodA = np.sum(SGMT[nxs,:,nxs,:,:]*MmodA[:,:,:,nxs,:],axis=-1)*SGData['MagMom'][nxs,:,nxs,nxs]
1462    MmodB = np.sum(SGMT[nxs,:,nxs,:,:]*MmodB[:,:,:,nxs,:],axis=-1)*SGData['MagMom'][nxs,:,nxs,nxs]
1463    return MmodA,MmodB    #Ntau,Nops,Natm,,Mxyz; cos & sin parts; sum matches drawn atom moments
1464       
1465def Modulation(H,HP,nWaves,Fmod,Xmod,Umod,glTau,glWt):
1466    '''
1467    H: array nRefBlk x ops X hklt
1468    HP: array nRefBlk x ops X hklt proj to hkl
1469    nWaves: list number of waves for frac, pos, uij & mag
1470    Fmod: array 2 x atoms x waves    (sin,cos terms)
1471    Xmod: array atoms X 3 X ngl
1472    Umod: array atoms x 3x3 x ngl
1473    glTau,glWt: arrays Gauss-Lorentzian pos & wts
1474    '''
1475   
1476    if nWaves[2]:       #uij (adp) waves
1477        if len(HP.shape) > 2:
1478            HbH = np.exp(-np.sum(HP[:,:,nxs,nxs,:]*np.inner(HP,Umod),axis=-1)) # refBlk x ops x atoms x ngl add Overhauser corr.?
1479        else:
1480            HbH = np.exp(-np.sum(HP[:,nxs,nxs,:]*np.inner(HP,Umod),axis=-1)) # refBlk x ops x atoms x ngl add Overhauser corr.?
1481    else:
1482        HbH = 1.0
1483    HdotX = np.inner(HP,Xmod)                   #refBlk x ops x atoms X ngl
1484    if len(H.shape) > 2:
1485        D = H[:,:,3:]*glTau[nxs,nxs,:]              #m*e*tau; refBlk x ops X ngl
1486        HdotXD = twopi*(HdotX+D[:,:,nxs,:])
1487    else:
1488        D = H[:,3:]*glTau[nxs,:]              #m*e*tau; refBlk x ops X ngl
1489        HdotXD = twopi*(HdotX+D[:,nxs,:])
1490    cosHA = np.sum(Fmod*HbH*np.cos(HdotXD)*glWt,axis=-1)       #real part; refBlk X ops x atoms; sum for G-L integration
1491    sinHA = np.sum(Fmod*HbH*np.sin(HdotXD)*glWt,axis=-1)       #imag part; ditto
1492    return np.array([cosHA,sinHA])             # 2 x refBlk x SGops x atoms
1493   
1494#def MagModulation(H,HP,nWaves,Fmod,Xmod,Umod,Mmod,glTau,glWt):
1495#    '''
1496#    H: array nRefBlk x ops X hklt
1497#    HP: array nRefBlk x ops X hklt proj to hkl
1498#    nWaves: list number of waves for frac, pos, uij & mag
1499#    Fmod: array 2 x atoms x waves    (sin,cos terms)
1500#    Xmod: array atoms X 3 X ngl
1501#    Umod: array atoms x 3x3 x ngl
1502#    Mmod: array atoms x 3x3 x ngl
1503#    glTau,glWt: arrays Gauss-Lorentzian pos & wts
1504#    '''
1505#   
1506#    if nWaves[2]:       #uij (adp) waves
1507#        if len(HP.shape) > 2:
1508#            HbH = np.exp(-np.sum(HP[:,:,nxs,nxs,:]*np.inner(HP,Umod),axis=-1)) # refBlk x ops x atoms x ngl add Overhauser corr.?
1509#        else:
1510#            HbH = np.exp(-np.sum(HP[:,nxs,nxs,:]*np.inner(HP,Umod),axis=-1)) # refBlk x ops x atoms x ngl add Overhauser corr.?
1511#    else:
1512#        HbH = 1.0
1513#    HdotX = np.inner(HP,Xmod)                   #refBlk x ops x atoms X ngl
1514#    if len(H.shape) > 2:
1515#        D = H[:,:,3:]*glTau[nxs,nxs,:]              #m*e*tau; refBlk x ops X ngl
1516#        HdotXD = twopi*(HdotX+D[:,:,nxs,:])
1517#    else:
1518#        D = H[:,3:]*glTau[nxs,:]              #m*e*tau; refBlk x ops X ngl
1519#        HdotXD = twopi*(HdotX+D[:,nxs,:])
1520#    M = np.swapaxes(Mmod,1,2)
1521#    cosHA = np.sum(M[nxs,nxs,:,:,:]*(Fmod*HbH*np.cos(HdotXD)[:,:,:,nxs,:]*glWt),axis=-1)       #real part; refBlk X ops x atoms; sum for G-L integration
1522#    sinHA = np.sum(M[nxs,nxs,:,:,:]*(Fmod*HbH*np.sin(HdotXD)[:,:,:,nxs,:]*glWt),axis=-1)       #imag part; ditto
1523#    return np.array([cosHA,sinHA])             # 2 x refBlk x SGops x atoms
1524#
1525def ModulationTw(H,HP,nWaves,Fmod,Xmod,Umod,glTau,glWt):
1526    '''
1527    H: array nRefBlk x tw x ops X hklt
1528    HP: array nRefBlk x tw x ops X hklt proj to hkl
1529    Fmod: array 2 x atoms x waves    (sin,cos terms)
1530    Xmod: array atoms X ngl X 3
1531    Umod: array atoms x ngl x 3x3
1532    glTau,glWt: arrays Gauss-Lorentzian pos & wts
1533    '''
1534   
1535    if nWaves[2]:
1536        if len(HP.shape) > 3:   #Blocks of reflections
1537            HbH = np.exp(-np.sum(HP[:,:,nxs,nxs,:]*np.inner(HP,Umod),axis=-1)) # refBlk x ops x atoms x ngl add Overhauser corr.?
1538        else:   #single reflections
1539            HbH = np.exp(-np.sum(HP[:,nxs,nxs,:]*np.inner(HP,Umod),axis=-1)) # refBlk x ops x atoms x ngl add Overhauser corr.?
1540    else:
1541        HbH = 1.0
1542    HdotX = np.inner(HP,Xmod)                   #refBlk x tw x ops x atoms X ngl
1543    if len(H.shape) > 3:
1544        D = glTau*H[:,:,:,3:,nxs]              #m*e*tau; refBlk x tw x ops X ngl
1545        HdotXD = twopi*(HdotX+D[:,:,:,nxs,:])
1546    else:
1547        D = H*glTau[nxs,:]              #m*e*tau; refBlk x ops X ngl
1548        HdotXD = twopi*(HdotX+D[:,nxs,:])
1549    cosHA = np.sum(Fmod*HbH*np.cos(HdotXD)*glWt,axis=-1)       #real part; refBlk X ops x atoms; sum for G-L integration
1550    sinHA = np.sum(Fmod*HbH*np.sin(HdotXD)*glWt,axis=-1)       #imag part; ditto
1551    return np.array([cosHA,sinHA])             # 2 x refBlk x SGops x atoms
1552   
1553def makeWavesDerv(ngl,waveTypes,FSSdata,XSSdata,USSdata,Mast):
1554    '''
1555    Only for Fourier waves for fraction, position & adp (probably not used for magnetism)
1556    FSSdata: array 2 x atoms x waves    (sin,cos terms)
1557    XSSdata: array 2x3 x atoms X waves (sin,cos terms)
1558    USSdata: array 2x6 x atoms X waves (sin,cos terms)
1559    Mast: array orthogonalization matrix for Uij
1560    '''
1561    glTau,glWt = pwd.pygauleg(0.,1.,ngl)         #get Gauss-Legendre intervals & weights
1562    waveShapes = [FSSdata.T.shape,XSSdata.T.shape,USSdata.T.shape]
1563    Af = np.array(FSSdata[0]).T    #sin frac mods x waves x atoms
1564    Bf = np.array(FSSdata[1]).T    #cos frac mods...
1565    Ax = np.array(XSSdata[:3]).T   #...cos pos mods x waves x atoms
1566    Bx = np.array(XSSdata[3:]).T   #...cos pos mods
1567    Au = Mast*np.array(G2lat.U6toUij(USSdata[:6])).T   #atoms x waves x sin Uij mods
1568    Bu = Mast*np.array(G2lat.U6toUij(USSdata[6:])).T   #...cos Uij mods
1569    nWaves = [Af.shape[1],Ax.shape[1],Au.shape[1]] 
1570    StauX = np.zeros((Ax.shape[0],Ax.shape[1],3,ngl))    #atoms x waves x 3 x ngl
1571    CtauX = np.zeros((Ax.shape[0],Ax.shape[1],3,ngl))
1572    ZtauXt = np.zeros((Ax.shape[0],2,3,ngl))               #atoms x Tminmax x 3 x ngl
1573    ZtauXx = np.zeros((Ax.shape[0],3,ngl))               #atoms x XYZmax x ngl
1574    for iatm in range(Ax.shape[0]):
1575        nx = 0
1576        if 'ZigZag' in waveTypes[iatm]:
1577            nx = 1
1578        elif 'Block' in waveTypes[iatm]:
1579            nx = 1
1580        tauX = np.arange(1.,nWaves[1]+1-nx)[:,nxs]*glTau  #Xwaves x ngl
1581        if nx:   
1582            StauX[iatm][nx:] = np.ones_like(Ax)[iatm,nx:,:,nxs]*np.sin(twopi*tauX)[nxs,:,nxs,:]   #atoms X waves X 3(xyz) X ngl
1583            CtauX[iatm][nx:] = np.ones_like(Bx)[iatm,nx:,:,nxs]*np.cos(twopi*tauX)[nxs,:,nxs,:]   #ditto
1584        else:
1585            StauX[iatm] = np.ones_like(Ax)[iatm,:,:,nxs]*np.sin(twopi*tauX)[nxs,:,nxs,:]   #atoms X waves X 3(xyz) X ngl
1586            CtauX[iatm] = np.ones_like(Bx)[iatm,:,:,nxs]*np.cos(twopi*tauX)[nxs,:,nxs,:]   #ditto
1587    if nWaves[0]:
1588        tauF = np.arange(1.,nWaves[0]+1)[:,nxs]*glTau  #Fwaves x ngl
1589        StauF = np.ones_like(Af)[:,:,nxs]*np.sin(twopi*tauF)[nxs,:,:] #also dFmod/dAf
1590        CtauF = np.ones_like(Bf)[:,:,nxs]*np.cos(twopi*tauF)[nxs,:,:] #also dFmod/dBf
1591    else:
1592        StauF = 1.0
1593        CtauF = 1.0
1594    if nWaves[2]:
1595        tauU = np.arange(1.,nWaves[2]+1)[:,nxs]*glTau     #Uwaves x ngl
1596        StauU = np.ones_like(Au)[:,:,:,:,nxs]*np.sin(twopi*tauU)[nxs,:,nxs,nxs,:]   #also dUmodA/dAu
1597        CtauU = np.ones_like(Bu)[:,:,:,:,nxs]*np.cos(twopi*tauU)[nxs,:,nxs,nxs,:]   #also dUmodB/dBu
1598        UmodA = Au[:,:,:,:,nxs]*StauU #atoms x waves x 3x3 x ngl
1599        UmodB = Bu[:,:,:,:,nxs]*CtauU #ditto
1600#derivs need to be ops x atoms x waves x 6uij; ops x atoms x waves x ngl x 6uij before sum
1601        StauU = np.rollaxis(np.rollaxis(np.swapaxes(StauU,2,4),-1),-1)
1602        CtauU = np.rollaxis(np.rollaxis(np.swapaxes(CtauU,2,4),-1),-1)
1603    else:
1604        StauU = 1.0
1605        CtauU = 1.0
1606        UmodA = 0.
1607        UmodB = 0.
1608    return waveShapes,[StauF,CtauF],[StauX,CtauX,ZtauXt,ZtauXx],[StauU,CtauU],UmodA+UmodB
1609   
1610def ModulationDerv(H,HP,Hij,nWaves,waveShapes,Fmod,Xmod,UmodAB,SCtauF,SCtauX,SCtauU,glTau,glWt):
1611    '''
1612    Compute Fourier modulation derivatives
1613    H: array ops X hklt proj to hkl
1614    HP: array ops X hklt proj to hkl
1615    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
1616    '''
1617   
1618    Mf = [H.shape[0],]+list(waveShapes[0])    #=[ops,atoms,waves,2] (sin+cos frac mods)
1619    dGdMfC = np.zeros(Mf)
1620    dGdMfS = np.zeros(Mf)
1621    Mx = [H.shape[0],]+list(waveShapes[1])   #=[ops,atoms,waves,6] (sin+cos pos mods)
1622    dGdMxC = np.zeros(Mx)
1623    dGdMxS = np.zeros(Mx)
1624    Mu = [H.shape[0],]+list(waveShapes[2])    #=[ops,atoms,waves,12] (sin+cos Uij mods)
1625    dGdMuC = np.zeros(Mu)
1626    dGdMuS = np.zeros(Mu)
1627   
1628    D = twopi*H[:,3][:,nxs]*glTau[nxs,:]              #m*e*tau; ops X ngl
1629    HdotX = twopi*np.inner(HP,Xmod)        #ops x atoms X ngl
1630    HdotXD = HdotX+D[:,nxs,:]
1631    if nWaves[2]:
1632        Umod = np.swapaxes((UmodAB),2,4)      #atoms x waves x ngl x 3x3 (symmetric so I can do this!)
1633        HuH = np.sum(HP[:,nxs,nxs,nxs]*np.inner(HP,Umod),axis=-1)    #ops x atoms x waves x ngl
1634        HuH = np.sum(HP[:,nxs,nxs,nxs]*np.inner(HP,Umod),axis=-1)    #ops x atoms x waves x ngl
1635        HbH = np.exp(-np.sum(HuH,axis=-2)) # ops x atoms x ngl; sum waves - OK vs Modulation version
1636#        part1 = -np.exp(-HuH)*Fmod[nxs,:,nxs,:]    #ops x atoms x waves x ngl
1637        part1 = -np.exp(-HuH)*Fmod    #ops x atoms x waves x ngl
1638        dUdAu = Hij[:,nxs,nxs,nxs,:]*np.rollaxis(G2lat.UijtoU6(SCtauU[0]),0,4)[nxs,:,:,:,:]    #ops x atoms x waves x ngl x 6sinUij
1639        dUdBu = Hij[:,nxs,nxs,nxs,:]*np.rollaxis(G2lat.UijtoU6(SCtauU[1]),0,4)[nxs,:,:,:,:]    #ops x atoms x waves x ngl x 6cosUij
1640        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
1641        dGdMuCb = np.sum(part1[:,:,:,:,nxs]*dUdBu*np.cos(HdotXD)[:,:,nxs,:,nxs]*glWt[nxs,nxs,nxs,:,nxs],axis=-2)
1642        dGdMuC = np.concatenate((dGdMuCa,dGdMuCb),axis=-1)   #ops x atoms x waves x 12uij
1643        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
1644        dGdMuSb = np.sum(part1[:,:,:,:,nxs]*dUdBu*np.sin(HdotXD)[:,:,nxs,:,nxs]*glWt[nxs,nxs,nxs,:,nxs],axis=-2)
1645        dGdMuS = np.concatenate((dGdMuSa,dGdMuSb),axis=-1)   #ops x atoms x waves x 12uij
1646    else:
1647        HbH = np.ones_like(HdotX)
1648    dHdXA = twopi*HP[:,nxs,nxs,nxs,:]*np.swapaxes(SCtauX[0],-1,-2)[nxs,:,:,:,:]    #ops x atoms x sine waves x ngl x xyz
1649    dHdXB = twopi*HP[:,nxs,nxs,nxs,:]*np.swapaxes(SCtauX[1],-1,-2)[nxs,:,:,:,:]    #ditto - cos waves
1650# ops x atoms x waves x 2xyz - real part - good
1651#    dGdMxCa = -np.sum((Fmod[nxs,:,:]*HbH)[:,:,nxs,:,nxs]*(dHdXA*np.sin(HdotXD)[:,:,nxs,:,nxs])*glWt[nxs,nxs,nxs,:,nxs],axis=-2)
1652#    dGdMxCb = -np.sum((Fmod[nxs,:,:]*HbH)[:,:,nxs,:,nxs]*(dHdXB*np.sin(HdotXD)[:,:,nxs,:,nxs])*glWt[nxs,nxs,nxs,:,nxs],axis=-2)
1653    dGdMxCa = -np.sum((Fmod*HbH)[:,:,nxs,:,nxs]*(dHdXA*np.sin(HdotXD)[:,:,nxs,:,nxs])*glWt[nxs,nxs,nxs,:,nxs],axis=-2)
1654    dGdMxCb = -np.sum((Fmod*HbH)[:,:,nxs,:,nxs]*(dHdXB*np.sin(HdotXD)[:,:,nxs,:,nxs])*glWt[nxs,nxs,nxs,:,nxs],axis=-2)
1655    dGdMxC = np.concatenate((dGdMxCa,dGdMxCb),axis=-1)
1656# ops x atoms x waves x 2xyz - imag part - good
1657#    dGdMxSa = np.sum((Fmod[nxs,:,:]*HbH)[:,:,nxs,:,nxs]*(dHdXA*np.cos(HdotXD)[:,:,nxs,:,nxs])*glWt[nxs,nxs,nxs,:,nxs],axis=-2)   
1658#    dGdMxSb = np.sum((Fmod[nxs,:,:]*HbH)[:,:,nxs,:,nxs]*(dHdXB*np.cos(HdotXD)[:,:,nxs,:,nxs])*glWt[nxs,nxs,nxs,:,nxs],axis=-2)   
1659    dGdMxSa = np.sum((Fmod*HbH)[:,:,nxs,:,nxs]*(dHdXA*np.cos(HdotXD)[:,:,nxs,:,nxs])*glWt[nxs,nxs,nxs,:,nxs],axis=-2)   
1660    dGdMxSb = np.sum((Fmod*HbH)[:,:,nxs,:,nxs]*(dHdXB*np.cos(HdotXD)[:,:,nxs,:,nxs])*glWt[nxs,nxs,nxs,:,nxs],axis=-2)   
1661    dGdMxS = np.concatenate((dGdMxSa,dGdMxSb),axis=-1)
1662    return [dGdMfC,dGdMfS],[dGdMxC,dGdMxS],[dGdMuC,dGdMuS]
1663       
1664def posFourier(tau,psin,pcos):
1665    A = np.array([ps[:,nxs]*np.sin(2*np.pi*(i+1)*tau) for i,ps in enumerate(psin)])
1666    B = np.array([pc[:,nxs]*np.cos(2*np.pi*(i+1)*tau) for i,pc in enumerate(pcos)])
1667    return np.sum(A,axis=0)+np.sum(B,axis=0)
1668   
1669def posZigZag(T,Tmm,Xmax):
1670    DT = Tmm[1]-Tmm[0]
1671    Su = 2.*Xmax/DT
1672    Sd = 2.*Xmax/(1.-DT)
1673    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])
1674    return A
1675   
1676#def posZigZagDerv(T,Tmm,Xmax):
1677#    DT = Tmm[1]-Tmm[0]
1678#    Su = 2.*Xmax/DT
1679#    Sd = 2.*Xmax/(1.-DT)
1680#    dAdT = np.zeros((2,3,len(T)))
1681#    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
1682#    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
1683#    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])
1684#    return dAdT,dAdX
1685
1686def posBlock(T,Tmm,Xmax):
1687    A = np.array([np.where(Tmm[0] < t%1. <= Tmm[1],-Xmax,Xmax) for t in T])
1688    return A
1689   
1690#def posBlockDerv(T,Tmm,Xmax):
1691#    dAdT = np.zeros((2,3,len(T)))
1692#    ind = np.searchsorted(T,Tmm)
1693#    dAdT[0,:,ind[0]] = -Xmax/len(T)
1694#    dAdT[1,:,ind[1]] = Xmax/len(T)
1695#    dAdX = np.ones(3)[:,nxs]*np.array([np.where(Tmm[0] < t <= Tmm[1],-1.,1.) for t in T])  #OK
1696#    return dAdT,dAdX
1697   
1698def fracCrenel(tau,Toff,Twid):
1699    Tau = (tau-Toff)%1.
1700    A = np.where(Tau<Twid,1.,0.)
1701    return A
1702   
1703def fracFourier(tau,fsin,fcos):
1704    if len(fsin) == 1:
1705        A = np.array([fsin[0]*np.sin(2.*np.pi*tau)])
1706        B = np.array([fcos[0]*np.cos(2.*np.pi*tau)])
1707    else:
1708        A = np.array([fs[:,nxs]*np.sin(2.*np.pi*(i+1)*tau) for i,fs in enumerate(fsin)])
1709        B = np.array([fc[:,nxs]*np.cos(2.*np.pi*(i+1)*tau) for i,fc in enumerate(fcos)])
1710    return np.sum(A,axis=0)+np.sum(B,axis=0)
1711
1712def ApplyModulation(data,tau):
1713    '''Applies modulation to drawing atom positions & Uijs for given tau
1714    '''
1715    generalData = data['General']
1716    cell = generalData['Cell'][1:7]
1717    G,g = G2lat.cell2Gmat(cell)
1718    SGData = generalData['SGData']
1719    SSGData = generalData['SSGData']
1720    cx,ct,cs,cia = generalData['AtomPtrs']
1721    drawingData = data['Drawing']
1722    modul = generalData['SuperVec'][0]
1723    dcx,dct,dcs,dci = drawingData['atomPtrs']
1724    atoms = data['Atoms']
1725    drawAtoms = drawingData['Atoms']
1726    Fade = np.ones(len(drawAtoms))
1727    for atom in atoms:
1728        atxyz = np.array(atom[cx:cx+3])
1729        atuij = np.array(atom[cia+2:cia+8])
1730        Sfrac = atom[-1]['SS1']['Sfrac']
1731        Spos = atom[-1]['SS1']['Spos']
1732        Sadp = atom[-1]['SS1']['Sadp']
1733        if generalData['Type'] == 'magnetic':
1734            Smag = atom[-1]['SS1']['Smag']
1735            atmom = np.array(atom[cx+4:cx+7])
1736        indx = FindAtomIndexByIDs(drawAtoms,dci,[atom[cia+8],],True)
1737        for ind in indx:
1738            drawatom = drawAtoms[ind]
1739            opr = drawatom[dcs-1]
1740            sop,ssop,icent,cent,unit = G2spc.OpsfromStringOps(opr,SGData,SSGData)
1741            drxyz = (np.inner(sop[0],atxyz)+sop[1]+cent)*icent+np.array(unit)
1742            tauT = G2spc.getTauT(tau,sop,ssop,drxyz,modul)[-1]
1743            tauT *= icent       #invert wave on -1
1744#            print(tau,tauT,opr,G2spc.MT2text(sop).replace(' ',''),G2spc.SSMT2text(ssop).replace(' ',''))
1745            wave = np.zeros(3)
1746            uwave = np.zeros(6)
1747            mom = np.zeros(3)
1748            if len(Sfrac):
1749                scof = []
1750                ccof = []
1751                waveType = Sfrac[0]
1752                for i,sfrac in enumerate(Sfrac[1:]):
1753                    if not i and 'Crenel' in waveType:
1754                        Fade[ind] += fracCrenel(tauT,sfrac[0][0],sfrac[0][1])
1755                    else:
1756                        scof.append(sfrac[0][0])
1757                        ccof.append(sfrac[0][1])
1758                    if len(scof):
1759                        Fade[ind] += np.sum(fracFourier(tauT,scof,ccof))                           
1760            if len(Spos):
1761                scof = []
1762                ccof = []
1763                waveType = Spos[0]
1764                for i,spos in enumerate(Spos[1:]):
1765                    if waveType in ['ZigZag','Block'] and not i:
1766                        Tminmax = spos[0][:2]
1767                        XYZmax = np.array(spos[0][2:5])
1768                        if waveType == 'Block':
1769                            wave = np.array(posBlock([tauT,],Tminmax,XYZmax))[0]
1770                        elif waveType == 'ZigZag':
1771                            wave = np.array(posZigZag([tauT,],Tminmax,XYZmax))[0]
1772                    else:
1773                        scof.append(spos[0][:3])
1774                        ccof.append(spos[0][3:])
1775                if len(scof):
1776                    wave += np.sum(posFourier(tauT,np.array(scof),np.array(ccof)),axis=1)
1777            if generalData['Type'] == 'magnetic' and len(Smag):
1778                scof = []
1779                ccof = []
1780                waveType = Smag[0]
1781                for i,spos in enumerate(Smag[1:]):
1782                    scof.append(spos[0][:3])
1783                    ccof.append(spos[0][3:])
1784                if len(scof):
1785                    mom += np.sum(posFourier(tauT,np.array(scof),np.array(ccof)),axis=1)
1786            if len(Sadp):
1787                scof = []
1788                ccof = []
1789                waveType = Sadp[0]
1790                for i,sadp in enumerate(Sadp[1:]):
1791                    scof.append(sadp[0][:6])
1792                    ccof.append(sadp[0][6:])
1793                ures = posFourier(tauT,np.array(scof),np.array(ccof))
1794                if np.any(ures):
1795                    uwave += np.sum(ures,axis=1)
1796            if atom[cia] == 'A':                   
1797                X,U = G2spc.ApplyStringOps(opr,SGData,atxyz+wave,atuij+uwave)
1798                drawatom[dcx:dcx+3] = X
1799                drawatom[dci-6:dci] = U
1800            else:
1801                X = G2spc.ApplyStringOps(opr,SGData,atxyz+wave)
1802                drawatom[dcx:dcx+3] = X
1803            if generalData['Type'] == 'magnetic':
1804                M = G2spc.ApplyStringOpsMom(opr,SGData,atmom+mom)
1805                drawatom[dcx+3:dcx+6] = M
1806    return drawAtoms,Fade
1807   
1808# gauleg.py Gauss Legendre numerical quadrature, x and w computation
1809# integrate from a to b using n evaluations of the function f(x) 
1810# usage: from gauleg import gaulegf         
1811#        x,w = gaulegf( a, b, n)                               
1812#        area = 0.0                                           
1813#        for i in range(1,n+1):          #  yes, 1..n                   
1814#          area += w[i]*f(x[i])                                   
1815
1816def gaulegf(a, b, n):
1817    x = range(n+1) # x[0] unused
1818    w = range(n+1) # w[0] unused
1819    eps = 3.0E-14
1820    m = (n+1)/2
1821    xm = 0.5*(b+a)
1822    xl = 0.5*(b-a)
1823    for i in range(1,m+1):
1824        z = math.cos(3.141592654*(i-0.25)/(n+0.5))
1825        while True:
1826            p1 = 1.0
1827            p2 = 0.0
1828            for j in range(1,n+1):
1829                p3 = p2
1830                p2 = p1
1831                p1 = ((2.0*j-1.0)*z*p2-(j-1.0)*p3)/j
1832       
1833            pp = n*(z*p1-p2)/(z*z-1.0)
1834            z1 = z
1835            z = z1 - p1/pp
1836            if abs(z-z1) <= eps:
1837                break
1838
1839        x[i] = xm - xl*z
1840        x[n+1-i] = xm + xl*z
1841        w[i] = 2.0*xl/((1.0-z*z)*pp*pp)
1842        w[n+1-i] = w[i]
1843    return np.array(x), np.array(w)
1844# end gaulegf
1845   
1846   
1847def BessJn(nmax,x):
1848    ''' compute Bessel function J(n,x) from scipy routine & recurrance relation
1849    returns sequence of J(n,x) for n in range [-nmax...0...nmax]
1850   
1851    :param  integer nmax: maximul order for Jn(x)
1852    :param  float x: argument for Jn(x)
1853   
1854    :returns numpy array: [J(-nmax,x)...J(0,x)...J(nmax,x)]
1855   
1856    '''
1857    import scipy.special as sp
1858    bessJn = np.zeros(2*nmax+1)
1859    bessJn[nmax] = sp.j0(x)
1860    bessJn[nmax+1] = sp.j1(x)
1861    bessJn[nmax-1] = -bessJn[nmax+1]
1862    for i in range(2,nmax+1):
1863        bessJn[i+nmax] = 2*(i-1)*bessJn[nmax+i-1]/x-bessJn[nmax+i-2]
1864        bessJn[nmax-i] = bessJn[i+nmax]*(-1)**i
1865    return bessJn
1866   
1867def BessIn(nmax,x):
1868    ''' compute modified Bessel function I(n,x) from scipy routines & recurrance relation
1869    returns sequence of I(n,x) for n in range [-nmax...0...nmax]
1870   
1871    :param  integer nmax: maximul order for In(x)
1872    :param  float x: argument for In(x)
1873   
1874    :returns numpy array: [I(-nmax,x)...I(0,x)...I(nmax,x)]
1875   
1876    '''
1877    import scipy.special as sp
1878    bessIn = np.zeros(2*nmax+1)
1879    bessIn[nmax] = sp.i0(x)
1880    bessIn[nmax+1] = sp.i1(x)
1881    bessIn[nmax-1] = bessIn[nmax+1]
1882    for i in range(2,nmax+1):
1883        bessIn[i+nmax] = bessIn[nmax+i-2]-2*(i-1)*bessIn[nmax+i-1]/x
1884        bessIn[nmax-i] = bessIn[i+nmax]
1885    return bessIn
1886       
1887   
1888################################################################################
1889##### distance, angle, planes, torsion stuff
1890################################################################################
1891
1892def CalcDist(distance_dict, distance_atoms, parmDict):
1893    if not len(parmDict):
1894        return 0.
1895    pId = distance_dict['pId']
1896    A = [parmDict['%s::A%d'%(pId,i)] for i in range(6)]
1897    Amat = G2lat.cell2AB(G2lat.A2cell(A))[0]
1898    Oxyz = [parmDict['%s::A%s:%d'%(pId,x,distance_atoms[0])] for x in ['x','y','z']]
1899    Txyz = [parmDict['%s::A%s:%d'%(pId,x,distance_atoms[1])] for x in ['x','y','z']]
1900    inv = 1
1901    symNo = distance_dict['symNo']
1902    if symNo < 0:
1903        inv = -1
1904        symNo *= -1
1905    cen = symNo//100
1906    op = symNo%100-1
1907    M,T = distance_dict['SGData']['SGOps'][op]
1908    D = T*inv+distance_dict['SGData']['SGCen'][cen]
1909    D += distance_dict['cellNo']
1910    Txyz = np.inner(M*inv,Txyz)+D
1911    dist = np.sqrt(np.sum(np.inner(Amat,(Txyz-Oxyz))**2))
1912#    GSASIIpath.IPyBreak()
1913    return dist   
1914   
1915def CalcDistDeriv(distance_dict, distance_atoms, parmDict):
1916    if not len(parmDict):
1917        return None
1918    pId = distance_dict['pId']
1919    A = [parmDict['%s::A%d'%(pId,i)] for i in range(6)]
1920    Amat = G2lat.cell2AB(G2lat.A2cell(A))[0]
1921    Oxyz = [parmDict['%s::A%s:%d'%(pId,x,distance_atoms[0])] for x in ['x','y','z']]
1922    Txyz = [parmDict['%s::A%s:%d'%(pId,x,distance_atoms[1])] for x in ['x','y','z']]
1923    symNo = distance_dict['symNo']
1924    Tunit = distance_dict['cellNo']
1925    SGData = distance_dict['SGData']   
1926    deriv = getDistDerv(Oxyz,Txyz,Amat,Tunit,symNo,SGData)
1927    return deriv
1928   
1929def CalcAngle(angle_dict, angle_atoms, parmDict):
1930    if not len(parmDict):
1931        return 0.
1932    pId = angle_dict['pId']
1933    A = [parmDict['%s::A%d'%(pId,i)] for i in range(6)]
1934    Amat = G2lat.cell2AB(G2lat.A2cell(A))[0]
1935    Oxyz = [parmDict['%s::A%s:%d'%(pId,x,angle_atoms[0])] for x in ['x','y','z']]
1936    Axyz = [parmDict['%s::A%s:%d'%(pId,x,angle_atoms[1][0])] for x in ['x','y','z']]
1937    Bxyz = [parmDict['%s::A%s:%d'%(pId,x,angle_atoms[1][1])] for x in ['x','y','z']]
1938    ABxyz = [Axyz,Bxyz]
1939    symNo = angle_dict['symNo']
1940    vec = np.zeros((2,3))
1941    for i in range(2):
1942        inv = 1
1943        if symNo[i] < 0:
1944            inv = -1
1945        cen = inv*symNo[i]//100
1946        op = inv*symNo[i]%100-1
1947        M,T = angle_dict['SGData']['SGOps'][op]
1948        D = T*inv+angle_dict['SGData']['SGCen'][cen]
1949        D += angle_dict['cellNo'][i]
1950        ABxyz[i] = np.inner(M*inv,ABxyz[i])+D
1951        vec[i] = np.inner(Amat,(ABxyz[i]-Oxyz))
1952        dist = np.sqrt(np.sum(vec[i]**2))
1953        if not dist:
1954            return 0.
1955        vec[i] /= dist
1956    angle = acosd(np.sum(vec[0]*vec[1]))
1957    return angle
1958
1959def CalcAngleDeriv(angle_dict, angle_atoms, parmDict):
1960    if not len(parmDict):
1961        return None
1962    pId = angle_dict['pId']
1963    A = [parmDict['%s::A%d'%(pId,i)] for i in range(6)]
1964    Amat = G2lat.cell2AB(G2lat.A2cell(A))[0]
1965    Oxyz = [parmDict['%s::A%s:%d'%(pId,x,angle_atoms[0])] for x in ['x','y','z']]
1966    Axyz = [parmDict['%s::A%s:%d'%(pId,x,angle_atoms[1][0])] for x in ['x','y','z']]
1967    Bxyz = [parmDict['%s::A%s:%d'%(pId,x,angle_atoms[1][1])] for x in ['x','y','z']]
1968    symNo = angle_dict['symNo']
1969    Tunit = angle_dict['cellNo']
1970    SGData = angle_dict['SGData']   
1971    deriv = getAngleDerv(Oxyz,Axyz,Bxyz,Amat,Tunit,symNo,SGData)
1972    return deriv
1973
1974def getSyXYZ(XYZ,ops,SGData):
1975    '''default doc
1976   
1977    :param type name: description
1978   
1979    :returns: type name: description
1980   
1981    '''
1982    XYZout = np.zeros_like(XYZ)
1983    for i,[xyz,op] in enumerate(zip(XYZ,ops)):
1984        if op == '1':
1985            XYZout[i] = xyz
1986        else:
1987            oprs = op.split('+')
1988            unit = [0,0,0]
1989            if len(oprs)>1:
1990                unit = np.array(list(eval(oprs[1])))
1991            syop =int(oprs[0])
1992            inv = syop//abs(syop)
1993            syop *= inv
1994            cent = syop//100
1995            syop %= 100
1996            syop -= 1
1997            M,T = SGData['SGOps'][syop]
1998            XYZout[i] = (np.inner(M,xyz)+T)*inv+SGData['SGCen'][cent]+unit
1999    return XYZout
2000   
2001def getRestDist(XYZ,Amat):
2002    '''default doc string
2003   
2004    :param type name: description
2005   
2006    :returns: type name: description
2007   
2008    '''
2009    return np.sqrt(np.sum(np.inner(Amat,(XYZ[1]-XYZ[0]))**2))
2010   
2011def getRestDeriv(Func,XYZ,Amat,ops,SGData):
2012    '''default doc string
2013   
2014    :param type name: description
2015   
2016    :returns: type name: description
2017   
2018    '''
2019    deriv = np.zeros((len(XYZ),3))
2020    dx = 0.00001
2021    for j,xyz in enumerate(XYZ):
2022        for i,x in enumerate(np.array([[dx,0,0],[0,dx,0],[0,0,dx]])):
2023            XYZ[j] -= x
2024            d1 = Func(getSyXYZ(XYZ,ops,SGData),Amat)
2025            XYZ[j] += 2*x
2026            d2 = Func(getSyXYZ(XYZ,ops,SGData),Amat)
2027            XYZ[j] -= x
2028            deriv[j][i] = (d1-d2)/(2*dx)
2029    return deriv.flatten()
2030
2031def getRestAngle(XYZ,Amat):
2032    '''default doc string
2033   
2034    :param type name: description
2035   
2036    :returns: type name: description
2037   
2038    '''
2039   
2040    def calcVec(Ox,Tx,Amat):
2041        return np.inner(Amat,(Tx-Ox))
2042
2043    VecA = calcVec(XYZ[1],XYZ[0],Amat)
2044    VecA /= np.sqrt(np.sum(VecA**2))
2045    VecB = calcVec(XYZ[1],XYZ[2],Amat)
2046    VecB /= np.sqrt(np.sum(VecB**2))
2047    edge = VecB-VecA
2048    edge = np.sum(edge**2)
2049    angle = (2.-edge)/2.
2050    angle = max(angle,-1.)
2051    return acosd(angle)
2052   
2053def getRestPlane(XYZ,Amat):
2054    '''default doc string
2055   
2056    :param type name: description
2057   
2058    :returns: type name: description
2059   
2060    '''
2061    sumXYZ = np.zeros(3)
2062    for xyz in XYZ:
2063        sumXYZ += xyz
2064    sumXYZ /= len(XYZ)
2065    XYZ = np.array(XYZ)-sumXYZ
2066    XYZ = np.inner(Amat,XYZ).T
2067    Zmat = np.zeros((3,3))
2068    for i,xyz in enumerate(XYZ):
2069        Zmat += np.outer(xyz.T,xyz)
2070    Evec,Emat = nl.eig(Zmat)
2071    Evec = np.sqrt(Evec)/(len(XYZ)-3)
2072    Order = np.argsort(Evec)
2073    return Evec[Order[0]]
2074   
2075def getRestChiral(XYZ,Amat):   
2076    '''default doc string
2077   
2078    :param type name: description
2079   
2080    :returns: type name: description
2081   
2082    '''
2083    VecA = np.empty((3,3))   
2084    VecA[0] = np.inner(XYZ[1]-XYZ[0],Amat)
2085    VecA[1] = np.inner(XYZ[2]-XYZ[0],Amat)
2086    VecA[2] = np.inner(XYZ[3]-XYZ[0],Amat)
2087    return nl.det(VecA)
2088   
2089def getRestTorsion(XYZ,Amat):
2090    '''default doc string
2091   
2092    :param type name: description
2093   
2094    :returns: type name: description
2095   
2096    '''
2097    VecA = np.empty((3,3))
2098    VecA[0] = np.inner(XYZ[1]-XYZ[0],Amat)
2099    VecA[1] = np.inner(XYZ[2]-XYZ[1],Amat)
2100    VecA[2] = np.inner(XYZ[3]-XYZ[2],Amat)
2101    D = nl.det(VecA)
2102    Mag = np.sqrt(np.sum(VecA*VecA,axis=1))
2103    P12 = np.sum(VecA[0]*VecA[1])/(Mag[0]*Mag[1])
2104    P13 = np.sum(VecA[0]*VecA[2])/(Mag[0]*Mag[2])
2105    P23 = np.sum(VecA[1]*VecA[2])/(Mag[1]*Mag[2])
2106    Ang = 1.0
2107    if abs(P12) < 1.0 and abs(P23) < 1.0:
2108        Ang = (P12*P23-P13)/(np.sqrt(1.-P12**2)*np.sqrt(1.-P23**2))
2109    TOR = (acosd(Ang)*D/abs(D)+720.)%360.
2110    return TOR
2111   
2112def calcTorsionEnergy(TOR,Coeff=[]):
2113    '''default doc string
2114   
2115    :param type name: description
2116   
2117    :returns: type name: description
2118   
2119    '''
2120    sum = 0.
2121    if len(Coeff):
2122        cof = np.reshape(Coeff,(3,3)).T
2123        delt = TOR-cof[1]
2124        delt = np.where(delt<-180.,delt+360.,delt)
2125        delt = np.where(delt>180.,delt-360.,delt)
2126        term = -cof[2]*delt**2
2127        val = cof[0]*np.exp(term/1000.0)
2128        pMax = cof[0][np.argmin(val)]
2129        Eval = np.sum(val)
2130        sum = Eval-pMax
2131    return sum,Eval
2132
2133def getTorsionDeriv(XYZ,Amat,Coeff):
2134    '''default doc string
2135   
2136    :param type name: description
2137   
2138    :returns: type name: description
2139   
2140    '''
2141    deriv = np.zeros((len(XYZ),3))
2142    dx = 0.00001
2143    for j,xyz in enumerate(XYZ):
2144        for i,x in enumerate(np.array([[dx,0,0],[0,dx,0],[0,0,dx]])):
2145            XYZ[j] -= x
2146            tor = getRestTorsion(XYZ,Amat)
2147            p1,d1 = calcTorsionEnergy(tor,Coeff)
2148            XYZ[j] += 2*x
2149            tor = getRestTorsion(XYZ,Amat)
2150            p2,d2 = calcTorsionEnergy(tor,Coeff)           
2151            XYZ[j] -= x
2152            deriv[j][i] = (p2-p1)/(2*dx)
2153    return deriv.flatten()
2154
2155def getRestRama(XYZ,Amat):
2156    '''Computes a pair of torsion angles in a 5 atom string
2157   
2158    :param nparray XYZ: crystallographic coordinates of 5 atoms
2159    :param nparray Amat: crystal to cartesian transformation matrix
2160   
2161    :returns: list (phi,psi) two torsion angles in degrees
2162   
2163    '''
2164    phi = getRestTorsion(XYZ[:5],Amat)
2165    psi = getRestTorsion(XYZ[1:],Amat)
2166    return phi,psi
2167   
2168def calcRamaEnergy(phi,psi,Coeff=[]):
2169    '''Computes pseudo potential energy from a pair of torsion angles and a
2170    numerical description of the potential energy surface. Used to create
2171    penalty function in LS refinement:     
2172    :math:`Eval(\\phi,\\psi) = C[0]*exp(-V/1000)`
2173
2174    where :math:`V = -C[3] * (\\phi-C[1])^2 - C[4]*(\\psi-C[2])^2 - 2*(\\phi-C[1])*(\\psi-C[2])`
2175   
2176    :param float phi: first torsion angle (:math:`\\phi`)
2177    :param float psi: second torsion angle (:math:`\\psi`)
2178    :param list Coeff: pseudo potential coefficients
2179   
2180    :returns: list (sum,Eval): pseudo-potential difference from minimum & value;
2181      sum is used for penalty function.
2182   
2183    '''
2184    sum = 0.
2185    Eval = 0.
2186    if len(Coeff):
2187        cof = Coeff.T
2188        dPhi = phi-cof[1]
2189        dPhi = np.where(dPhi<-180.,dPhi+360.,dPhi)
2190        dPhi = np.where(dPhi>180.,dPhi-360.,dPhi)
2191        dPsi = psi-cof[2]
2192        dPsi = np.where(dPsi<-180.,dPsi+360.,dPsi)
2193        dPsi = np.where(dPsi>180.,dPsi-360.,dPsi)
2194        val = -cof[3]*dPhi**2-cof[4]*dPsi**2-2.0*cof[5]*dPhi*dPsi
2195        val = cof[0]*np.exp(val/1000.)
2196        pMax = cof[0][np.argmin(val)]
2197        Eval = np.sum(val)
2198        sum = Eval-pMax
2199    return sum,Eval
2200
2201def getRamaDeriv(XYZ,Amat,Coeff):
2202    '''Computes numerical derivatives of torsion angle pair pseudo potential
2203    with respect of crystallographic atom coordinates of the 5 atom sequence
2204   
2205    :param nparray XYZ: crystallographic coordinates of 5 atoms
2206    :param nparray Amat: crystal to cartesian transformation matrix
2207    :param list Coeff: pseudo potential coefficients
2208   
2209    :returns: list (deriv) derivatives of pseudopotential with respect to 5 atom
2210     crystallographic xyz coordinates.
2211   
2212    '''
2213    deriv = np.zeros((len(XYZ),3))
2214    dx = 0.00001
2215    for j,xyz in enumerate(XYZ):
2216        for i,x in enumerate(np.array([[dx,0,0],[0,dx,0],[0,0,dx]])):
2217            XYZ[j] -= x
2218            phi,psi = getRestRama(XYZ,Amat)
2219            p1,d1 = calcRamaEnergy(phi,psi,Coeff)
2220            XYZ[j] += 2*x
2221            phi,psi = getRestRama(XYZ,Amat)
2222            p2,d2 = calcRamaEnergy(phi,psi,Coeff)
2223            XYZ[j] -= x
2224            deriv[j][i] = (p2-p1)/(2*dx)
2225    return deriv.flatten()
2226
2227def getRestPolefig(ODFln,SamSym,Grid):
2228    '''default doc string
2229   
2230    :param type name: description
2231   
2232    :returns: type name: description
2233   
2234    '''
2235    X,Y = np.meshgrid(np.linspace(1.,-1.,Grid),np.linspace(-1.,1.,Grid))
2236    R,P = np.sqrt(X**2+Y**2).flatten(),atan2d(Y,X).flatten()
2237    R = np.where(R <= 1.,2.*atand(R),0.0)
2238    Z = np.zeros_like(R)
2239    Z = G2lat.polfcal(ODFln,SamSym,R,P)
2240    Z = np.reshape(Z,(Grid,Grid))
2241    return np.reshape(R,(Grid,Grid)),np.reshape(P,(Grid,Grid)),Z
2242
2243def getRestPolefigDerv(HKL,Grid,SHCoeff):
2244    '''default doc string
2245   
2246    :param type name: description
2247   
2248    :returns: type name: description
2249   
2250    '''
2251    pass
2252       
2253def getDistDerv(Oxyz,Txyz,Amat,Tunit,Top,SGData):
2254    '''default doc string
2255   
2256    :param type name: description
2257   
2258    :returns: type name: description
2259   
2260    '''
2261    def calcDist(Ox,Tx,U,inv,C,M,T,Amat):
2262        TxT = inv*(np.inner(M,Tx)+T)+C+U
2263        return np.sqrt(np.sum(np.inner(Amat,(TxT-Ox))**2))
2264       
2265    inv = Top/abs(Top)
2266    cent = abs(Top)//100
2267    op = abs(Top)%100-1
2268    M,T = SGData['SGOps'][op]
2269    C = SGData['SGCen'][cent]
2270    dx = .00001
2271    deriv = np.zeros(6)
2272    for i in [0,1,2]:
2273        Oxyz[i] -= dx
2274        d0 = calcDist(Oxyz,Txyz,Tunit,inv,C,M,T,Amat)
2275        Oxyz[i] += 2*dx
2276        deriv[i] = (calcDist(Oxyz,Txyz,Tunit,inv,C,M,T,Amat)-d0)/(2.*dx)
2277        Oxyz[i] -= dx
2278        Txyz[i] -= dx
2279        d0 = calcDist(Oxyz,Txyz,Tunit,inv,C,M,T,Amat)
2280        Txyz[i] += 2*dx
2281        deriv[i+3] = (calcDist(Oxyz,Txyz,Tunit,inv,C,M,T,Amat)-d0)/(2.*dx)
2282        Txyz[i] -= dx
2283    return deriv
2284   
2285def getAngleDerv(Oxyz,Axyz,Bxyz,Amat,Tunit,symNo,SGData):
2286   
2287    def calcAngle(Oxyz,ABxyz,Amat,Tunit,symNo,SGData):
2288        vec = np.zeros((2,3))
2289        for i in range(2):
2290            inv = 1
2291            if symNo[i] < 0:
2292                inv = -1
2293            cen = inv*symNo[i]//100
2294            op = inv*symNo[i]%100-1
2295            M,T = SGData['SGOps'][op]
2296            D = T*inv+SGData['SGCen'][cen]
2297            D += Tunit[i]
2298            ABxyz[i] = np.inner(M*inv,ABxyz[i])+D
2299            vec[i] = np.inner(Amat,(ABxyz[i]-Oxyz))
2300            dist = np.sqrt(np.sum(vec[i]**2))
2301            if not dist:
2302                return 0.
2303            vec[i] /= dist
2304        angle = acosd(np.sum(vec[0]*vec[1]))
2305    #    GSASIIpath.IPyBreak()
2306        return angle
2307       
2308    dx = .00001
2309    deriv = np.zeros(9)
2310    for i in [0,1,2]:
2311        Oxyz[i] -= dx
2312        a0 = calcAngle(Oxyz,[Axyz,Bxyz],Amat,Tunit,symNo,SGData)
2313        Oxyz[i] += 2*dx
2314        deriv[i] = (calcAngle(Oxyz,[Axyz,Bxyz],Amat,Tunit,symNo,SGData)-a0)/(2.*dx)
2315        Oxyz[i] -= dx
2316        Axyz[i] -= dx
2317        a0 = calcAngle(Oxyz,[Axyz,Bxyz],Amat,Tunit,symNo,SGData)
2318        Axyz[i] += 2*dx
2319        deriv[i+3] = (calcAngle(Oxyz,[Axyz,Bxyz],Amat,Tunit,symNo,SGData)-a0)/(2.*dx)
2320        Axyz[i] -= dx
2321        Bxyz[i] -= dx
2322        a0 = calcAngle(Oxyz,[Axyz,Bxyz],Amat,Tunit,symNo,SGData)
2323        Bxyz[i] += 2*dx
2324        deriv[i+6] = (calcAngle(Oxyz,[Axyz,Bxyz],Amat,Tunit,symNo,SGData)-a0)/(2.*dx)
2325        Bxyz[i] -= dx
2326    return deriv
2327   
2328def getAngSig(VA,VB,Amat,SGData,covData={}):
2329    '''default doc string
2330   
2331    :param type name: description
2332   
2333    :returns: type name: description
2334   
2335    '''
2336    def calcVec(Ox,Tx,U,inv,C,M,T,Amat):
2337        TxT = inv*(np.inner(M,Tx)+T)+C+U
2338        return np.inner(Amat,(TxT-Ox))
2339       
2340    def calcAngle(Ox,TxA,TxB,unitA,unitB,invA,CA,MA,TA,invB,CB,MB,TB,Amat):
2341        VecA = calcVec(Ox,TxA,unitA,invA,CA,MA,TA,Amat)
2342        VecA /= np.sqrt(np.sum(VecA**2))
2343        VecB = calcVec(Ox,TxB,unitB,invB,CB,MB,TB,Amat)
2344        VecB /= np.sqrt(np.sum(VecB**2))
2345        edge = VecB-VecA
2346        edge = np.sum(edge**2)
2347        angle = (2.-edge)/2.
2348        angle = max(angle,-1.)
2349        return acosd(angle)
2350       
2351    OxAN,OxA,TxAN,TxA,unitA,TopA = VA
2352    OxBN,OxB,TxBN,TxB,unitB,TopB = VB
2353    invA = invB = 1
2354    invA = TopA//abs(TopA)
2355    invB = TopB//abs(TopB)
2356    centA = abs(TopA)//100
2357    centB = abs(TopB)//100
2358    opA = abs(TopA)%100-1
2359    opB = abs(TopB)%100-1
2360    MA,TA = SGData['SGOps'][opA]
2361    MB,TB = SGData['SGOps'][opB]
2362    CA = SGData['SGCen'][centA]
2363    CB = SGData['SGCen'][centB]
2364    if 'covMatrix' in covData:
2365        covMatrix = covData['covMatrix']
2366        varyList = covData['varyList']
2367        AngVcov = getVCov(OxAN+TxAN+TxBN,varyList,covMatrix)
2368        dx = .00001
2369        dadx = np.zeros(9)
2370        Ang = calcAngle(OxA,TxA,TxB,unitA,unitB,invA,CA,MA,TA,invB,CB,MB,TB,Amat)
2371        for i in [0,1,2]:
2372            OxA[i] -= dx
2373            a0 = calcAngle(OxA,TxA,TxB,unitA,unitB,invA,CA,MA,TA,invB,CB,MB,TB,Amat)
2374            OxA[i] += 2*dx
2375            dadx[i] = (calcAngle(OxA,TxA,TxB,unitA,unitB,invA,CA,MA,TA,invB,CB,MB,TB,Amat)-a0)/(2*dx)
2376            OxA[i] -= dx
2377           
2378            TxA[i] -= dx
2379            a0 = calcAngle(OxA,TxA,TxB,unitA,unitB,invA,CA,MA,TA,invB,CB,MB,TB,Amat)
2380            TxA[i] += 2*dx
2381            dadx[i+3] = (calcAngle(OxA,TxA,TxB,unitA,unitB,invA,CA,MA,TA,invB,CB,MB,TB,Amat)-a0)/(2*dx)
2382            TxA[i] -= dx
2383           
2384            TxB[i] -= dx
2385            a0 = calcAngle(OxA,TxA,TxB,unitA,unitB,invA,CA,MA,TA,invB,CB,MB,TB,Amat)
2386            TxB[i] += 2*dx
2387            dadx[i+6] = (calcAngle(OxA,TxA,TxB,unitA,unitB,invA,CA,MA,TA,invB,CB,MB,TB,Amat)-a0)/(2*dx)
2388            TxB[i] -= dx
2389           
2390        sigAng = np.sqrt(np.inner(dadx,np.inner(AngVcov,dadx)))
2391        if sigAng < 0.01:
2392            sigAng = 0.0
2393        return Ang,sigAng
2394    else:
2395        return calcAngle(OxA,TxA,TxB,unitA,unitB,invA,CA,MA,TA,invB,CB,MB,TB,Amat),0.0
2396
2397def GetDistSig(Oatoms,Atoms,Amat,SGData,covData={}):
2398    '''default doc string
2399   
2400    :param type name: description
2401   
2402    :returns: type name: description
2403   
2404    '''
2405    def calcDist(Atoms,SyOps,Amat):
2406        XYZ = []
2407        for i,atom in enumerate(Atoms):
2408            Inv,M,T,C,U = SyOps[i]
2409            XYZ.append(np.array(atom[1:4]))
2410            XYZ[-1] = Inv*(np.inner(M,np.array(XYZ[-1]))+T)+C+U
2411            XYZ[-1] = np.inner(Amat,XYZ[-1]).T
2412        V1 = XYZ[1]-XYZ[0]
2413        return np.sqrt(np.sum(V1**2))
2414       
2415    SyOps = []
2416    names = []
2417    for i,atom in enumerate(Oatoms):
2418        names += atom[-1]
2419        Op,unit = Atoms[i][-1]
2420        inv = Op//abs(Op)
2421        m,t = SGData['SGOps'][abs(Op)%100-1]
2422        c = SGData['SGCen'][abs(Op)//100]
2423        SyOps.append([inv,m,t,c,unit])
2424    Dist = calcDist(Oatoms,SyOps,Amat)
2425   
2426    sig = -0.001
2427    if 'covMatrix' in covData:
2428        dx = .00001
2429        dadx = np.zeros(6)
2430        for i in range(6):
2431            ia = i//3
2432            ix = i%3
2433            Oatoms[ia][ix+1] += dx
2434            a0 = calcDist(Oatoms,SyOps,Amat)
2435            Oatoms[ia][ix+1] -= 2*dx
2436            dadx[i] = (calcDist(Oatoms,SyOps,Amat)-a0)/(2.*dx)
2437        covMatrix = covData['covMatrix']
2438        varyList = covData['varyList']
2439        DistVcov = getVCov(names,varyList,covMatrix)
2440        sig = np.sqrt(np.inner(dadx,np.inner(DistVcov,dadx)))
2441        if sig < 0.001:
2442            sig = -0.001
2443   
2444    return Dist,sig
2445
2446def GetAngleSig(Oatoms,Atoms,Amat,SGData,covData={}):
2447    '''default doc string
2448   
2449    :param type name: description
2450   
2451    :returns: type name: description
2452   
2453    '''
2454
2455    def calcAngle(Atoms,SyOps,Amat):
2456        XYZ = []
2457        for i,atom in enumerate(Atoms):
2458            Inv,M,T,C,U = SyOps[i]
2459            XYZ.append(np.array(atom[1:4]))
2460            XYZ[-1] = Inv*(np.inner(M,np.array(XYZ[-1]))+T)+C+U
2461            XYZ[-1] = np.inner(Amat,XYZ[-1]).T
2462        V1 = XYZ[1]-XYZ[0]
2463        V1 /= np.sqrt(np.sum(V1**2))
2464        V2 = XYZ[1]-XYZ[2]
2465        V2 /= np.sqrt(np.sum(V2**2))
2466        V3 = V2-V1
2467        cang = min(1.,max((2.-np.sum(V3**2))/2.,-1.))
2468        return acosd(cang)
2469
2470    SyOps = []
2471    names = []
2472    for i,atom in enumerate(Oatoms):
2473        names += atom[-1]
2474        Op,unit = Atoms[i][-1]
2475        inv = Op//abs(Op)
2476        m,t = SGData['SGOps'][abs(Op)%100-1]
2477        c = SGData['SGCen'][abs(Op)//100]
2478        SyOps.append([inv,m,t,c,unit])
2479    Angle = calcAngle(Oatoms,SyOps,Amat)
2480   
2481    sig = -0.01
2482    if 'covMatrix' in covData:
2483        dx = .00001
2484        dadx = np.zeros(9)
2485        for i in range(9):
2486            ia = i//3
2487            ix = i%3
2488            Oatoms[ia][ix+1] += dx
2489            a0 = calcAngle(Oatoms,SyOps,Amat)
2490            Oatoms[ia][ix+1] -= 2*dx
2491            dadx[i] = (calcAngle(Oatoms,SyOps,Amat)-a0)/(2.*dx)
2492        covMatrix = covData['covMatrix']
2493        varyList = covData['varyList']
2494        AngVcov = getVCov(names,varyList,covMatrix)
2495        sig = np.sqrt(np.inner(dadx,np.inner(AngVcov,dadx)))
2496        if sig < 0.01:
2497            sig = -0.01
2498   
2499    return Angle,sig
2500
2501def GetTorsionSig(Oatoms,Atoms,Amat,SGData,covData={}):
2502    '''default doc string
2503   
2504    :param type name: description
2505   
2506    :returns: type name: description
2507   
2508    '''
2509
2510    def calcTorsion(Atoms,SyOps,Amat):
2511       
2512        XYZ = []
2513        for i,atom in enumerate(Atoms):
2514            Inv,M,T,C,U = SyOps[i]
2515            XYZ.append(np.array(atom[1:4]))
2516            XYZ[-1] = Inv*(np.inner(M,np.array(XYZ[-1]))+T)+C+U
2517            XYZ[-1] = np.inner(Amat,XYZ[-1]).T
2518        V1 = XYZ[1]-XYZ[0]
2519        V2 = XYZ[2]-XYZ[1]
2520        V3 = XYZ[3]-XYZ[2]
2521        V1 /= np.sqrt(np.sum(V1**2))
2522        V2 /= np.sqrt(np.sum(V2**2))
2523        V3 /= np.sqrt(np.sum(V3**2))
2524        M = np.array([V1,V2,V3])
2525        D = nl.det(M)
2526        P12 = np.dot(V1,V2)
2527        P13 = np.dot(V1,V3)
2528        P23 = np.dot(V2,V3)
2529        Tors = acosd((P12*P23-P13)/(np.sqrt(1.-P12**2)*np.sqrt(1.-P23**2)))*D/abs(D)
2530        return Tors
2531           
2532    SyOps = []
2533    names = []
2534    for i,atom in enumerate(Oatoms):
2535        names += atom[-1]
2536        Op,unit = Atoms[i][-1]
2537        inv = Op//abs(Op)
2538        m,t = SGData['SGOps'][abs(Op)%100-1]
2539        c = SGData['SGCen'][abs(Op)//100]
2540        SyOps.append([inv,m,t,c,unit])
2541    Tors = calcTorsion(Oatoms,SyOps,Amat)
2542   
2543    sig = -0.01
2544    if 'covMatrix' in covData:
2545        dx = .00001
2546        dadx = np.zeros(12)
2547        for i in range(12):
2548            ia = i//3
2549            ix = i%3
2550            Oatoms[ia][ix+1] -= dx
2551            a0 = calcTorsion(Oatoms,SyOps,Amat)
2552            Oatoms[ia][ix+1] += 2*dx
2553            dadx[i] = (calcTorsion(Oatoms,SyOps,Amat)-a0)/(2.*dx)
2554            Oatoms[ia][ix+1] -= dx           
2555        covMatrix = covData['covMatrix']
2556        varyList = covData['varyList']
2557        TorVcov = getVCov(names,varyList,covMatrix)
2558        sig = np.sqrt(np.inner(dadx,np.inner(TorVcov,dadx)))
2559        if sig < 0.01:
2560            sig = -0.01
2561   
2562    return Tors,sig
2563       
2564def GetDATSig(Oatoms,Atoms,Amat,SGData,covData={}):
2565    '''default doc string
2566   
2567    :param type name: description
2568   
2569    :returns: type name: description
2570   
2571    '''
2572
2573    def calcDist(Atoms,SyOps,Amat):
2574        XYZ = []
2575        for i,atom in enumerate(Atoms):
2576            Inv,M,T,C,U = SyOps[i]
2577            XYZ.append(np.array(atom[1:4]))
2578            XYZ[-1] = Inv*(np.inner(M,np.array(XYZ[-1]))+T)+C+U
2579            XYZ[-1] = np.inner(Amat,XYZ[-1]).T
2580        V1 = XYZ[1]-XYZ[0]
2581        return np.sqrt(np.sum(V1**2))
2582       
2583    def calcAngle(Atoms,SyOps,Amat):
2584        XYZ = []
2585        for i,atom in enumerate(Atoms):
2586            Inv,M,T,C,U = SyOps[i]
2587            XYZ.append(np.array(atom[1:4]))
2588            XYZ[-1] = Inv*(np.inner(M,np.array(XYZ[-1]))+T)+C+U
2589            XYZ[-1] = np.inner(Amat,XYZ[-1]).T
2590        V1 = XYZ[1]-XYZ[0]
2591        V1 /= np.sqrt(np.sum(V1**2))
2592        V2 = XYZ[1]-XYZ[2]
2593        V2 /= np.sqrt(np.sum(V2**2))
2594        V3 = V2-V1
2595        cang = min(1.,max((2.-np.sum(V3**2))/2.,-1.))
2596        return acosd(cang)
2597
2598    def calcTorsion(Atoms,SyOps,Amat):
2599       
2600        XYZ = []
2601        for i,atom in enumerate(Atoms):
2602            Inv,M,T,C,U = SyOps[i]
2603            XYZ.append(np.array(atom[1:4]))
2604            XYZ[-1] = Inv*(np.inner(M,np.array(XYZ[-1]))+T)+C+U
2605            XYZ[-1] = np.inner(Amat,XYZ[-1]).T
2606        V1 = XYZ[1]-XYZ[0]
2607        V2 = XYZ[2]-XYZ[1]
2608        V3 = XYZ[3]-XYZ[2]
2609        V1 /= np.sqrt(np.sum(V1**2))
2610        V2 /= np.sqrt(np.sum(V2**2))
2611        V3 /= np.sqrt(np.sum(V3**2))
2612        M = np.array([V1,V2,V3])
2613        D = nl.det(M)
2614        P12 = np.dot(V1,V2)
2615        P13 = np.dot(V1,V3)
2616        P23 = np.dot(V2,V3)
2617        Tors = acosd((P12*P23-P13)/(np.sqrt(1.-P12**2)*np.sqrt(1.-P23**2)))*D/abs(D)
2618        return Tors
2619           
2620    SyOps = []
2621    names = []
2622    for i,atom in enumerate(Oatoms):
2623        names += atom[-1]
2624        Op,unit = Atoms[i][-1]
2625        inv = Op//abs(Op)
2626        m,t = SGData['SGOps'][abs(Op)%100-1]
2627        c = SGData['SGCen'][abs(Op)//100]
2628        SyOps.append([inv,m,t,c,unit])
2629    M = len(Oatoms)
2630    if M == 2:
2631        Val = calcDist(Oatoms,SyOps,Amat)
2632    elif M == 3:
2633        Val = calcAngle(Oatoms,SyOps,Amat)
2634    else:
2635        Val = calcTorsion(Oatoms,SyOps,Amat)
2636   
2637    sigVals = [-0.001,-0.01,-0.01]
2638    sig = sigVals[M-3]
2639    if 'covMatrix' in covData:
2640        dx = .00001
2641        N = M*3
2642        dadx = np.zeros(N)
2643        for i in range(N):
2644            ia = i//3
2645            ix = i%3
2646            Oatoms[ia][ix+1] += dx
2647            if M == 2:
2648                a0 = calcDist(Oatoms,SyOps,Amat)
2649            elif M == 3:
2650                a0 = calcAngle(Oatoms,SyOps,Amat)
2651            else:
2652                a0 = calcTorsion(Oatoms,SyOps,Amat)
2653            Oatoms[ia][ix+1] -= 2*dx
2654            if M == 2:
2655                dadx[i] = (calcDist(Oatoms,SyOps,Amat)-a0)/(2.*dx)               
2656            elif M == 3:
2657                dadx[i] = (calcAngle(Oatoms,SyOps,Amat)-a0)/(2.*dx)               
2658            else:
2659                dadx[i] = (calcTorsion(Oatoms,SyOps,Amat)-a0)/(2.*dx)
2660        covMatrix = covData['covMatrix']
2661        varyList = covData['varyList']
2662        Vcov = getVCov(names,varyList,covMatrix)
2663        sig = np.sqrt(np.inner(dadx,np.inner(Vcov,dadx)))
2664        if sig < sigVals[M-3]:
2665            sig = sigVals[M-3]
2666   
2667    return Val,sig
2668       
2669def ValEsd(value,esd=0,nTZ=False):
2670    '''Format a floating point number with a given level of precision or
2671    with in crystallographic format with a "esd", as value(esd). If esd is
2672    negative the number is formatted with the level of significant figures
2673    appropriate if abs(esd) were the esd, but the esd is not included.
2674    if the esd is zero, approximately 6 significant figures are printed.
2675    nTZ=True causes "extra" zeros to be removed after the decimal place.
2676    for example:
2677
2678      * "1.235(3)" for value=1.2346 & esd=0.003
2679      * "1.235(3)e4" for value=12346. & esd=30
2680      * "1.235(3)e6" for value=0.12346e7 & esd=3000
2681      * "1.235" for value=1.2346 & esd=-0.003
2682      * "1.240" for value=1.2395 & esd=-0.003
2683      * "1.24" for value=1.2395 & esd=-0.003 with nTZ=True
2684      * "1.23460" for value=1.2346 & esd=0.0
2685
2686    :param float value: number to be formatted
2687    :param float esd: uncertainty or if esd < 0, specifies level of
2688      precision to be shown e.g. esd=-0.01 gives 2 places beyond decimal
2689    :param bool nTZ: True to remove trailing zeros (default is False)
2690    :returns: value(esd) or value as a string
2691
2692    '''
2693    # Note: this routine is Python 3 compatible -- I think
2694    cutoff = 3.16228    #=(sqrt(10); same as old GSAS   was 1.95
2695    if math.isnan(value): # invalid value, bail out
2696        return '?'
2697    if math.isnan(esd): # invalid esd, treat as zero
2698        esd = 0
2699        esdoff = 5
2700#    if esd < 1.e-5:
2701#        esd = 0
2702#        esdoff = 5
2703    elif esd != 0:
2704        # transform the esd to a one or two digit integer
2705        l = math.log10(abs(esd)) % 1.
2706        if l < math.log10(cutoff): l+= 1.       
2707        intesd = int(round(10**l)) # esd as integer
2708        # determine the number of digits offset for the esd
2709        esdoff = int(round(math.log10(intesd*1./abs(esd))))
2710    else:
2711        esdoff = 5
2712    valoff = 0
2713    if abs(value) < abs(esdoff): # value is effectively zero
2714        pass
2715    elif esdoff < 0 or abs(value) > 1.0e6 or abs(value) < 1.0e-4: # use scientific notation
2716        # where the digit offset is to the left of the decimal place or where too many
2717        # digits are needed
2718        if abs(value) > 1:
2719            valoff = int(math.log10(abs(value)))
2720        elif abs(value) > 0:
2721            valoff = int(math.log10(abs(value))-0.9999999)
2722        else:
2723            valoff = 0
2724    if esd != 0:
2725        if valoff+esdoff < 0:
2726            valoff = esdoff = 0
2727        out = ("{:."+str(valoff+esdoff)+"f}").format(value/10**valoff) # format the value
2728    elif valoff != 0: # esd = 0; exponential notation ==> esdoff decimal places
2729        out = ("{:."+str(esdoff)+"f}").format(value/10**valoff) # format the value
2730    else: # esd = 0; non-exponential notation ==> esdoff+1 significant digits
2731        if abs(value) > 0:           
2732            extra = -math.log10(abs(value))
2733        else:
2734            extra = 0
2735        if extra > 0: extra += 1
2736        out = ("{:."+str(max(0,esdoff+int(extra)))+"f}").format(value) # format the value
2737    if esd > 0:
2738        out += ("({:d})").format(intesd)  # add the esd
2739    elif nTZ and '.' in out:
2740        out = out.rstrip('0')  # strip zeros to right of decimal
2741        out = out.rstrip('.')  # and decimal place when not needed
2742    if valoff != 0:
2743        out += ("e{:d}").format(valoff) # add an exponent, when needed
2744    return out
2745   
2746###############################################################################
2747##### Protein validation - "ERRATV2" analysis
2748###############################################################################
2749
2750def validProtein(Phase,old):
2751   
2752    def sumintact(intact):
2753        return {'CC':intact['CC'],'NN':intact['NN'],'OO':intact['OO'],
2754        'CN':(intact['CN']+intact['NC']),'CO':(intact['CO']+intact['OC']),
2755        'NO':(intact['NO']+intact['ON'])}
2756       
2757    resNames = ['ALA','ARG','ASN','ASP','CYS','GLN','GLU','GLY','HIS','ILE',
2758        'LEU','LYS','MET','PHE','PRO','SER','THR','TRP','TYR','VAL','MSE']
2759# data from errat.f
2760    b1_old = np.array([ 
2761        [1154.343,  600.213, 1051.018, 1132.885,  960.738],
2762        [600.213, 1286.818, 1282.042,  957.156,  612.789],
2763        [1051.018, 1282.042, 3519.471,  991.974, 1226.491],
2764        [1132.885,  957.156,  991.974, 1798.672,  820.355],
2765        [960.738,  612.789, 1226.491,  820.355, 2428.966]
2766        ])
2767    avg_old = np.array([ 0.225, 0.281, 0.071, 0.237, 0.044])    #Table 1 3.5A Obsd. Fr. p 1513
2768# data taken from erratv2.ccp
2769    b1 = np.array([
2770          [5040.279078850848200,        3408.805141583649400,   4152.904423767300600,   4236.200004171890200,   5054.781210204625500], 
2771          [3408.805141583648900,        8491.906094010220800,   5958.881777877950300,   1521.387352718486200,   4304.078200827221700], 
2772          [4152.904423767301500,        5958.881777877952100,   7637.167089335050100,   6620.715738223072500,   5287.691183798410700], 
2773          [4236.200004171890200,        1521.387352718486200,   6620.715738223072500,   18368.343774298410000,  4050.797811118806700], 
2774          [5054.781210204625500,        4304.078200827220800,   5287.691183798409800,   4050.797811118806700,   6666.856740479164700]])
2775    avg = np.array([0.192765509919262, 0.195575208778518, 0.275322406824210, 0.059102357035642, 0.233154192767480])
2776    General = Phase['General']
2777    Amat,Bmat = G2lat.cell2AB(General['Cell'][1:7])
2778    cx,ct,cs,cia = General['AtomPtrs']
2779    Atoms = Phase['Atoms']
2780    cartAtoms = []
2781    xyzmin = 999.*np.ones(3)
2782    xyzmax = -999.*np.ones(3)
2783    #select residue atoms,S,Se --> O make cartesian
2784    for atom in Atoms:
2785        if atom[1] in resNames:
2786            cartAtoms.append(atom[:cx+3])
2787            if atom[4].strip() in ['S','Se']:
2788                if not old:
2789                    continue        #S,Se skipped for erratv2?
2790                cartAtoms[-1][3] = 'Os'
2791                cartAtoms[-1][4] = 'O'
2792            cartAtoms[-1][cx:cx+3] = np.inner(Amat,cartAtoms[-1][cx:cx+3])
2793            cartAtoms[-1].append(atom[cia+8])
2794    XYZ = np.array([atom[cx:cx+3] for atom in cartAtoms])
2795    xyzmin = np.array([np.min(XYZ.T[i]) for i in [0,1,2]])
2796    xyzmax = np.array([np.max(XYZ.T[i]) for i in [0,1,2]])
2797    nbox = list(np.array(np.ceil((xyzmax-xyzmin)/4.),dtype=int))+[15,]
2798    Boxes = np.zeros(nbox,dtype=int)
2799    iBox = np.array([np.trunc((XYZ.T[i]-xyzmin[i])/4.) for i in [0,1,2]],dtype=int).T
2800    for ib,box in enumerate(iBox):  #put in a try for too many atoms in box (IndexError)?
2801        try:
2802            Boxes[box[0],box[1],box[2],0] += 1
2803            Boxes[box[0],box[1],box[2],Boxes[box[0],box[1],box[2],0]] = ib
2804        except IndexError:
2805            G2fil.G2Print('Error: too many atoms in box' )
2806            continue
2807    #Box content checks with errat.f $ erratv2.cpp ibox1 arrays
2808    indices = (-1,0,1)
2809    Units = np.array([[h,k,l] for h in indices for k in indices for l in indices]) 
2810    dsmax = 3.75**2
2811    if old:
2812        dsmax = 3.5**2
2813    chains = []
2814    resIntAct = []
2815    chainIntAct = []
2816    res = []
2817    resNames = []
2818    resIDs = {}
2819    resname = []
2820    resID = {}
2821    newChain = True
2822    intact = {'CC':0,'CN':0,'CO':0,'NN':0,'NO':0,'OO':0,'NC':0,'OC':0,'ON':0}
2823    for ia,atom in enumerate(cartAtoms):
2824        jntact = {'CC':0,'CN':0,'CO':0,'NN':0,'NO':0,'OO':0,'NC':0,'OC':0,'ON':0}
2825        if atom[2] not in chains:   #get chain id & save residue sequence from last chain
2826            chains.append(atom[2])
2827            if len(resIntAct):
2828                resIntAct.append(sumintact(intact))
2829                chainIntAct.append(resIntAct)
2830                resNames += resname
2831                resIDs.update(resID)
2832                res = []
2833                resname = []
2834                resID = {}
2835                resIntAct = []
2836                intact = {'CC':0,'CN':0,'CO':0,'NN':0,'NO':0,'OO':0,'NC':0,'OC':0,'ON':0}
2837                newChain = True
2838        if atom[0] not in res:  #new residue, get residue no.
2839            if res and int(res[-1]) != int(atom[0])-1:  #a gap in chain - not new chain
2840                intact = {'CC':0,'CN':0,'CO':0,'NN':0,'NO':0,'OO':0,'NC':0,'OC':0,'ON':0}
2841                ires = int(res[-1])
2842                for i in range(int(atom[0])-ires-1):
2843                    res.append(str(ires+i+1))
2844                    resname.append('')
2845                    resIntAct.append(sumintact(intact))
2846            res.append(atom[0])
2847            name = '%s-%s%s'%(atom[2],atom[0],atom[1])
2848            resname.append(name)
2849            resID[name] = atom[-1]
2850            if not newChain:
2851                resIntAct.append(sumintact(intact))
2852            intact = {'CC':0,'CN':0,'CO':0,'NN':0,'NO':0,'OO':0,'NC':0,'OC':0,'ON':0}
2853            newChain = False
2854        ibox = iBox[ia]         #box location of atom
2855        tgts = []
2856        for unit in Units:      #assemble list of all possible target atoms
2857            jbox = ibox+unit
2858            if np.all(jbox>=0) and np.all((jbox-nbox[:3])<0):               
2859                tgts += list(Boxes[jbox[0],jbox[1],jbox[2]])
2860        tgts = list(set(tgts))
2861        tgts = [tgt for tgt in tgts if atom[:3] != cartAtoms[tgt][:3]]    #exclude same residue
2862        tgts = [tgt for tgt in tgts if np.sum((XYZ[ia]-XYZ[tgt])**2) < dsmax]
2863        ires = int(atom[0])
2864        if old:
2865            if atom[3].strip() == 'C':
2866                tgts = [tgt for tgt in tgts if not (cartAtoms[tgt][3].strip() == 'N' and int(cartAtoms[tgt][0]) in [ires-1,ires+1])]
2867            elif atom[3].strip() == 'N':
2868                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])]
2869            elif atom[3].strip() == 'CA':
2870                tgts = [tgt for tgt in tgts if not (cartAtoms[tgt][3].strip() == 'N' and int(cartAtoms[tgt][0]) in [ires-1,ires+1])]
2871        else:
2872            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]]
2873            if atom[3].strip() == 'C':
2874                tgts = [tgt for tgt in tgts if not (cartAtoms[tgt][3].strip() == 'N' and int(cartAtoms[tgt][0]) == ires+1)]
2875            elif atom[3].strip() == 'N':
2876                tgts = [tgt for tgt in tgts if not (cartAtoms[tgt][3].strip() == 'C' and int(cartAtoms[tgt][0]) == ires-1)]
2877        for tgt in tgts:
2878            dsqt = np.sqrt(np.sum((XYZ[ia]-XYZ[tgt])**2))
2879            mult = 1.0
2880            if dsqt > 3.25 and not old:
2881                mult = 2.*(3.75-dsqt)
2882            intype = atom[4].strip()+cartAtoms[tgt][4].strip()
2883            if 'S' not in intype:
2884                intact[intype] += mult
2885                jntact[intype] += mult
2886#        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']
2887    resNames += resname
2888    resIDs.update(resID)
2889    resIntAct.append(sumintact(intact))
2890    chainIntAct.append(resIntAct)
2891    chainProb = []
2892    for ich,chn in enumerate(chains):
2893        IntAct = chainIntAct[ich]
2894        nRes = len(IntAct)
2895        Probs = [0.,0.,0.,0.]   #skip 1st 4 residues in chain
2896        for i in range(4,nRes-4):
2897            if resNames[i]:
2898                mtrx = np.zeros(5)
2899                summ = 0.
2900                for j in range(i-4,i+5):
2901                    summ += np.sum(np.array(list(IntAct[j].values())))
2902                    if old:
2903                        mtrx[0] += IntAct[j]['CC']
2904                        mtrx[1] += IntAct[j]['CO']
2905                        mtrx[2] += IntAct[j]['NN']
2906                        mtrx[3] += IntAct[j]['NO']
2907                        mtrx[4] += IntAct[j]['OO']
2908                    else:
2909                        mtrx[0] += IntAct[j]['CC']
2910                        mtrx[1] += IntAct[j]['CN']
2911                        mtrx[2] += IntAct[j]['CO']
2912                        mtrx[3] += IntAct[j]['NN']
2913                        mtrx[4] += IntAct[j]['NO']
2914                mtrx /= summ
2915    #            print i+1,mtrx*summ
2916                if old:
2917                    mtrx -= avg_old
2918                    prob = np.inner(np.inner(mtrx,b1_old),mtrx)
2919                else:
2920                    mtrx -= avg
2921                    prob = np.inner(np.inner(mtrx,b1),mtrx)
2922            else:       #skip the gaps
2923                prob = 0.0
2924            Probs.append(prob)
2925        Probs += 4*[0.,]        #skip last 4 residues in chain
2926        chainProb += Probs
2927    return resNames,chainProb,resIDs
2928   
2929################################################################################
2930##### Texture fitting stuff
2931################################################################################
2932
2933def FitTexture(General,Gangls,refData,keyList,pgbar):
2934    import pytexture as ptx
2935    ptx.pyqlmninit()            #initialize fortran arrays for spherical harmonics
2936   
2937    def printSpHarm(textureData,SHtextureSig):
2938        Tindx = 1.0
2939        Tvar = 0.0
2940        print ('\n Spherical harmonics texture: Order:' + str(textureData['Order']))
2941        names = ['omega','chi','phi']
2942        namstr = '  names :'
2943        ptstr =  '  values:'
2944        sigstr = '  esds  :'
2945        for name in names:
2946            namstr += '%12s'%('Sample '+name)
2947            ptstr += '%12.3f'%(textureData['Sample '+name][1])
2948            if 'Sample '+name in SHtextureSig:
2949                sigstr += '%12.3f'%(SHtextureSig['Sample '+name])
2950            else:
2951                sigstr += 12*' '
2952        print (namstr)
2953        print (ptstr)
2954        print (sigstr)
2955        print ('\n Texture coefficients:')
2956        SHcoeff = textureData['SH Coeff'][1]
2957        SHkeys = list(SHcoeff.keys())
2958        nCoeff = len(SHcoeff)
2959        nBlock = nCoeff//10+1
2960        iBeg = 0
2961        iFin = min(iBeg+10,nCoeff)
2962        for block in range(nBlock):
2963            namstr = '  names :'
2964            ptstr =  '  values:'
2965            sigstr = '  esds  :'
2966            for name in SHkeys[iBeg:iFin]:
2967                if 'C' in name:
2968                    l = 2.0*eval(name.strip('C'))[0]+1
2969                    Tindx += SHcoeff[name]**2/l
2970                    namstr += '%12s'%(name)
2971                    ptstr += '%12.3f'%(SHcoeff[name])
2972                    if name in SHtextureSig:
2973                        Tvar += (2.*SHcoeff[name]*SHtextureSig[name]/l)**2
2974                        sigstr += '%12.3f'%(SHtextureSig[name])
2975                    else:
2976                        sigstr += 12*' '
2977            print (namstr)
2978            print (ptstr)
2979            print (sigstr)
2980            iBeg += 10
2981            iFin = min(iBeg+10,nCoeff)
2982        print(' Texture index J = %.3f(%d)'%(Tindx,int(1000*np.sqrt(Tvar))))
2983           
2984    def Dict2Values(parmdict, varylist):
2985        '''Use before call to leastsq to setup list of values for the parameters
2986        in parmdict, as selected by key in varylist'''
2987        return [parmdict[key] for key in varylist] 
2988       
2989    def Values2Dict(parmdict, varylist, values):
2990        ''' Use after call to leastsq to update the parameter dictionary with
2991        values corresponding to keys in varylist'''
2992        parmdict.update(list(zip(varylist,values)))
2993       
2994    def errSpHarm(values,SGData,cell,Gangls,shModel,refData,parmDict,varyList,pgbar):
2995        parmDict.update(list(zip(varyList,values)))
2996        Mat = np.empty(0)
2997        sumObs = 0
2998        Sangls = [parmDict['Sample '+'omega'],parmDict['Sample '+'chi'],parmDict['Sample '+'phi']]
2999        for hist in Gangls.keys():
3000            Refs = refData[hist]
3001            Refs[:,5] = np.where(Refs[:,5]>0.,Refs[:,5],0.)
3002            wt = 1./np.sqrt(np.fmax(Refs[:,4],.25))
3003#            wt = 1./np.max(Refs[:,4],.25)
3004            sumObs += np.sum(wt*Refs[:,5])
3005            Refs[:,6] = 1.
3006            H = Refs[:,:3]
3007            phi,beta = G2lat.CrsAng(H,cell,SGData)
3008            psi,gam,x,x = G2lat.SamAng(Refs[:,3]/2.,Gangls[hist],Sangls,False) #assume not Bragg-Brentano!
3009            for item in parmDict:
3010                if 'C' in item:
3011                    L,M,N = eval(item.strip('C'))
3012                    Kcl = G2lat.GetKcl(L,N,SGData['SGLaue'],phi,beta)
3013                    Ksl,x,x = G2lat.GetKsl(L,M,shModel,psi,gam)
3014                    Lnorm = G2lat.Lnorm(L)
3015                    Refs[:,6] += parmDict[item]*Lnorm*Kcl*Ksl
3016            mat = wt*(Refs[:,5]-Refs[:,6])
3017            Mat = np.concatenate((Mat,mat))
3018        sumD = np.sum(np.abs(Mat))
3019        R = min(100.,100.*sumD/sumObs)
3020        pgbar.Raise()
3021        pgbar.Update(R,newmsg='Residual = %5.2f'%(R))
3022        print (' Residual: %.3f%%'%(R))
3023        return Mat
3024       
3025    def dervSpHarm(values,SGData,cell,Gangls,shModel,refData,parmDict,varyList,pgbar):
3026        Mat = np.empty(0)
3027        Sangls = [parmDict['Sample omega'],parmDict['Sample chi'],parmDict['Sample phi']]
3028        for hist in Gangls.keys():
3029            mat = np.zeros((len(varyList),len(refData[hist])))
3030            Refs = refData[hist]
3031            H = Refs[:,:3]
3032            wt = 1./np.sqrt(np.fmax(Refs[:,4],.25))
3033#            wt = 1./np.max(Refs[:,4],.25)
3034            phi,beta = G2lat.CrsAng(H,cell,SGData)
3035            psi,gam,dPdA,dGdA = G2lat.SamAng(Refs[:,3]/2.,Gangls[hist],Sangls,False) #assume not Bragg-Brentano!
3036            for j,item in enumerate(varyList):
3037                if 'C' in item:
3038                    L,M,N = eval(item.strip('C'))
3039                    Kcl = G2lat.GetKcl(L,N,SGData['SGLaue'],phi,beta)
3040                    Ksl,dKdp,dKdg = G2lat.GetKsl(L,M,shModel,psi,gam)
3041                    Lnorm = G2lat.Lnorm(L)
3042                    mat[j] = -wt*Lnorm*Kcl*Ksl
3043                    for k,itema in enumerate(['Sample omega','Sample chi','Sample phi']):
3044                        try:
3045                            l = varyList.index(itema)
3046                            mat[l] -= parmDict[item]*wt*Lnorm*Kcl*(dKdp*dPdA[k]+dKdg*dGdA[k])
3047                        except ValueError:
3048                            pass
3049            if len(Mat):
3050                Mat = np.concatenate((Mat,mat.T))
3051            else:
3052                Mat = mat.T
3053        print ('deriv')
3054        return Mat
3055
3056    print (' Fit texture for '+General['Name'])
3057    SGData = General['SGData']
3058    cell = General['Cell'][1:7]
3059    Texture = General['SH Texture']
3060    if not Texture['Order']:
3061        return 'No spherical harmonics coefficients'
3062    varyList = []
3063    parmDict = copy.copy(Texture['SH Coeff'][1])
3064    for item in ['Sample omega','Sample chi','Sample phi']:
3065        parmDict[item] = Texture[item][1]
3066        if Texture[item][0]:
3067            varyList.append(item)
3068    if Texture['SH Coeff'][0]:
3069        varyList += list(Texture['SH Coeff'][1].keys())
3070    while True:
3071        begin = time.time()
3072        values =  np.array(Dict2Values(parmDict, varyList))
3073        result = so.leastsq(errSpHarm,values,Dfun=dervSpHarm,full_output=True,ftol=1.e-6,
3074            args=(SGData,cell,Gangls,Texture['Model'],refData,parmDict,varyList,pgbar))
3075        ncyc = int(result[2]['nfev']//2)
3076        if ncyc:
3077            runtime = time.time()-begin   
3078            chisq = np.sum(result[2]['fvec']**2)
3079            Values2Dict(parmDict, varyList, result[0])
3080            GOF = chisq/(len(result[2]['fvec'])-len(varyList))       #reduced chi^2
3081            G2fil.G2Print ('Number of function calls: %d Number of observations: %d Number of parameters: %d'%(result[2]['nfev'],len(result[2]['fvec']),len(varyList)))
3082            G2fil.G2Print ('refinement time = %8.3fs, %8.3fs/cycle'%(runtime,runtime/ncyc))
3083            try:
3084                sig = np.sqrt(np.diag(result[1])*GOF)
3085                if np.any(np.isnan(sig)):
3086                    G2fil.G2Print ('*** Least squares aborted - some invalid esds possible ***', mode='error')
3087                break                   #refinement succeeded - finish up!
3088            except ValueError:          #result[1] is None on singular matrix
3089                G2fil.G2Print ('**** Refinement failed - singular matrix ****', mode='error')
3090                return None
3091        else:
3092            break
3093   
3094    if ncyc:
3095        for parm in parmDict:
3096            if 'C' in parm:
3097                Texture['SH Coeff'][1][parm] = parmDict[parm]
3098            else:
3099                Texture[parm][1] = parmDict[parm] 
3100        sigDict = dict(zip(varyList,sig))
3101        printSpHarm(Texture,sigDict)
3102       
3103    return None
3104   
3105################################################################################
3106##### Fourier & charge flip stuff
3107################################################################################
3108
3109def adjHKLmax(SGData,Hmax):
3110    '''default doc string
3111   
3112    :param type name: description
3113   
3114    :returns: type name: description
3115   
3116    '''
3117    if SGData['SGLaue'] in ['3','3m1','31m','6/m','6/mmm']:
3118        Hmax[0] = int(math.ceil(Hmax[0]/6.))*6
3119        Hmax[1] = int(math.ceil(Hmax[1]/6.))*6
3120        Hmax[2] = int(math.ceil(Hmax[2]/4.))*4
3121    else:
3122        Hmax[0] = int(math.ceil(Hmax[0]/4.))*4
3123        Hmax[1] = int(math.ceil(Hmax[1]/4.))*4
3124        Hmax[2] = int(math.ceil(Hmax[2]/4.))*4
3125
3126def OmitMap(data,reflDict,pgbar=None):
3127    '''default doc string
3128   
3129    :param type name: description
3130   
3131    :returns: type name: description
3132   
3133    '''
3134    generalData = data['General']
3135    if not generalData['Map']['MapType']:
3136        G2fil.G2Print ('**** ERROR - Fourier map not defined')
3137        return
3138    mapData = generalData['Map']
3139    dmin = mapData['GridStep']*2.
3140    SGData = generalData['SGData']
3141    SGMT = np.array([ops[0].T for ops in SGData['SGOps']])
3142    SGT = np.array([ops[1] for ops in SGData['SGOps']])
3143    cell = generalData['Cell'][1:8]       
3144    A = G2lat.cell2A(cell[:6])
3145    Hmax = np.asarray(G2lat.getHKLmax(dmin,SGData,A),dtype='i')+1
3146    adjHKLmax(SGData,Hmax)
3147    Fhkl = np.zeros(shape=2*Hmax,dtype='c16')
3148    time0 = time.time()
3149    for iref,ref in enumerate(reflDict['RefList']):
3150        if ref[4] >= dmin:
3151            Fosq,Fcsq,ph = ref[8:11]
3152            Uniq = np.inner(ref[:3],SGMT)
3153            Phi = np.inner(ref[:3],SGT)
3154            for i,hkl in enumerate(Uniq):        #uses uniq
3155                hkl = np.asarray(hkl,dtype='i')
3156                dp = 360.*Phi[i]                #and phi
3157                a = cosd(ph+dp)
3158                b = sind(ph+dp)
3159                phasep = complex(a,b)
3160                phasem = complex(a,-b)
3161                if '2Fo-Fc' in mapData['MapType']:
3162                    F = 2.*np.sqrt(Fosq)-np.sqrt(Fcsq)
3163                else:
3164                    F = np.sqrt(Fosq)
3165                h,k,l = hkl+Hmax
3166                Fhkl[h,k,l] = F*phasep
3167                h,k,l = -hkl+Hmax
3168                Fhkl[h,k,l] = F*phasem
3169    rho0 = fft.fftn(fft.fftshift(Fhkl))/cell[6]
3170    M = np.mgrid[0:4,0:4,0:4]
3171    blkIds = np.array(list(zip(M[0].flatten(),M[1].flatten(),M[2].flatten())))
3172    iBeg = blkIds*rho0.shape//4
3173    iFin = (blkIds+1)*rho0.shape//4
3174    rho_omit = np.zeros_like(rho0)
3175    nBlk = 0
3176    for iB,iF in zip(iBeg,iFin):
3177        rho1 = np.copy(rho0)
3178        rho1[iB[0]:iF[0],iB[1]:iF[1],iB[2]:iF[2]] = 0.
3179        Fnew = fft.ifftshift(fft.ifftn(rho1))
3180        Fnew = np.where(Fnew,Fnew,1.0)           #avoid divide by zero
3181        phase = Fnew/np.absolute(Fnew)
3182        OFhkl = np.absolute(Fhkl)*phase
3183        rho1 = np.real(fft.fftn(fft.fftshift(OFhkl)))*(1.+0j)
3184        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]])
3185        nBlk += 1
3186        pgbar.Raise()
3187        pgbar.Update(nBlk)
3188    mapData['rho'] = np.real(rho_omit)/cell[6]
3189    mapData['rhoMax'] = max(np.max(mapData['rho']),-np.min(mapData['rho']))
3190    mapData['minmax'] = [np.max(mapData['rho']),np.min(mapData['rho'])]
3191    G2fil.G2Print ('Omit map time: %.4f no. elements: %d dimensions: %s'%(time.time()-time0,Fhkl.size,str(Fhkl.shape)))
3192    return mapData
3193   
3194def FourierMap(data,reflDict):
3195    '''default doc string
3196   
3197    :param type name: description
3198   
3199    :returns: type name: description
3200   
3201    '''
3202    generalData = data['General']
3203    mapData = generalData['Map']
3204    dmin = mapData['GridStep']*2.
3205    SGData = generalData['SGData']
3206    SGMT = np.array([ops[0].T for ops in SGData['SGOps']])
3207    SGT = np.array([ops[1] for ops in SGData['SGOps']])
3208    cell = generalData['Cell'][1:8]       
3209    A = G2lat.cell2A(cell[:6])
3210    Hmax = np.asarray(G2lat.getHKLmax(dmin,SGData,A),dtype='i')+1
3211    adjHKLmax(SGData,Hmax)
3212    Fhkl = np.zeros(shape=2*Hmax,dtype='c16')
3213#    Fhkl[0,0,0] = generalData['F000X']
3214    time0 = time.time()
3215    for iref,ref in enumerate(reflDict['RefList']):
3216        if ref[4] > dmin:
3217            Fosq,Fcsq,ph = ref[8:11]
3218            Uniq = np.inner(ref[:3],SGMT)
3219            Phi = np.inner(ref[:3],SGT)
3220            for i,hkl in enumerate(Uniq):        #uses uniq
3221                hkl = np.asarray(hkl,dtype='i')
3222                dp = 360.*Phi[i]                #and phi
3223                a = cosd(ph+dp)
3224                b = sind(ph+dp)
3225                phasep = complex(a,b)
3226                phasem = complex(a,-b)
3227                if 'Fobs' in mapData['MapType']:
3228                    F = np.where(Fosq>0.,np.sqrt(Fosq),0.)
3229                    h,k,l = hkl+Hmax
3230                    Fhkl[h,k,l] = F*phasep
3231                    h,k,l = -hkl+Hmax
3232                    Fhkl[h,k,l] = F*phasem
3233                elif 'Fcalc' in mapData['MapType']:
3234                    F = np.sqrt(Fcsq)
3235                    h,k,l = hkl+Hmax
3236                    Fhkl[h,k,l] = F*phasep
3237                    h,k,l = -hkl+Hmax
3238                    Fhkl[h,k,l] = F*phasem
3239                elif 'delt-F' in mapData['MapType']:
3240                    dF = np.where(Fosq>0.,np.sqrt(Fosq),0.)-np.sqrt(Fcsq)
3241                    h,k,l = hkl+Hmax
3242                    Fhkl[h,k,l] = dF*phasep
3243                    h,k,l = -hkl+Hmax
3244                    Fhkl[h,k,l] = dF*phasem
3245                elif '2*Fo-Fc' in mapData['MapType']:
3246                    F = 2.*np.where(Fosq>0.,np.sqrt(Fosq),0.)-np.sqrt(Fcsq)
3247                    h,k,l = hkl+Hmax
3248                    Fhkl[h,k,l] = F*phasep
3249                    h,k,l = -hkl+Hmax
3250                    Fhkl[h,k,l] = F*phasem
3251                elif 'Patterson' in mapData['MapType']:
3252                    h,k,l = hkl+Hmax
3253                    Fhkl[h,k,l] = complex(Fosq,0.)
3254                    h,k,l = -hkl+Hmax
3255                    Fhkl[h,k,l] = complex(Fosq,0.)
3256    rho = fft.fftn(fft.fftshift(Fhkl))/cell[6]
3257    G2fil.G2Print ('Fourier map time: %.4f no. elements: %d dimensions: %s'%(time.time()-time0,Fhkl.size,str(Fhkl.shape)))
3258    mapData['Type'] = reflDict['Type']
3259    mapData['rho'] = np.real(rho)
3260    mapData['rhoMax'] = max(np.max(mapData['rho']),-np.min(mapData['rho']))
3261    mapData['minmax'] = [np.max(mapData['rho']),np.min(mapData['rho'])]
3262   
3263def Fourier4DMap(data,reflDict):
3264    '''default doc string
3265   
3266    :param type name: description
3267   
3268    :returns: type name: description
3269   
3270    '''
3271    generalData = data['General']
3272    map4DData = generalData['4DmapData']
3273    mapData = generalData['Map']
3274    dmin = mapData['GridStep']*2.
3275    SGData = generalData['SGData']
3276    SSGData = generalData['SSGData']
3277    SSGMT = np.array([ops[0].T for ops in SSGData['SSGOps']])
3278    SSGT = np.array([ops[1] for ops in SSGData['SSGOps']])
3279    cell = generalData['Cell'][1:8]       
3280    A = G2lat.cell2A(cell[:6])
3281    maxM = 4
3282    Hmax = G2lat.getHKLmax(dmin,SGData,A)+[maxM,]
3283    adjHKLmax(SGData,Hmax)
3284    Hmax = np.asarray(Hmax,dtype='i')+1
3285    Fhkl = np.zeros(shape=2*Hmax,dtype='c16')
3286    time0 = time.time()
3287    for iref,ref in enumerate(reflDict['RefList']):
3288        if ref[5] > dmin:
3289            Fosq,Fcsq,ph = ref[9:12]
3290            Fosq = np.where(Fosq>0.,Fosq,0.)    #can't use Fo^2 < 0
3291            Uniq = np.inner(ref[:4],SSGMT)
3292            Phi = np.inner(ref[:4],SSGT)
3293            for i,hkl in enumerate(Uniq):        #uses uniq
3294                hkl = np.asarray(hkl,dtype='i')
3295                dp = 360.*Phi[i]                #and phi
3296                a = cosd(ph+dp)
3297                b = sind(ph+dp)
3298                phasep = complex(a,b)
3299                phasem = complex(a,-b)
3300                if 'Fobs' in mapData['MapType']:
3301                    F = np.sqrt(Fosq)
3302                    h,k,l,m = hkl+Hmax
3303                    Fhkl[h,k,l,m] = F*phasep
3304                    h,k,l,m = -hkl+Hmax
3305                    Fhkl[h,k,l,m] = F*phasem
3306                elif 'Fcalc' in mapData['MapType']:
3307                    F = np.sqrt(Fcsq)
3308                    h,k,l,m = hkl+Hmax
3309                    Fhkl[h,k,l,m] = F*phasep
3310                    h,k,l,m = -hkl+Hmax
3311                    Fhkl[h,k,l,m] = F*phasem                   
3312                elif 'delt-F' in mapData['MapType']:
3313                    dF = np.sqrt(Fosq)-np.sqrt(Fcsq)
3314                    h,k,l,m = hkl+Hmax
3315                    Fhkl[h,k,l,m] = dF*phasep
3316                    h,k,l,m = -hkl+Hmax
3317                    Fhkl[h,k,l,m] = dF*phasem
3318    SSrho = fft.fftn(fft.fftshift(Fhkl))/cell[6]          #4D map
3319    rho = fft.fftn(fft.fftshift(Fhkl[:,:,:,maxM+1]))/cell[6]    #3D map
3320    map4DData['rho'] = np.real(SSrho)
3321    map4DData['rhoMax'] = max(np.max(map4DData['rho']),-np.min(map4DData['rho']))
3322    map4DData['minmax'] = [np.max(map4DData['rho']),np.min(map4DData['rho'])]
3323    map4DData['Type'] = reflDict['Type']
3324    mapData['Type'] = reflDict['Type']
3325    mapData['rho'] = np.real(rho)
3326    mapData['rhoMax'] = max(np.max(mapData['rho']),-np.min(mapData['rho']))
3327    mapData['minmax'] = [np.max(mapData['rho']),np.min(mapData['rho'])]
3328    G2fil.G2Print ('Fourier map time: %.4f no. elements: %d dimensions: %s'%(time.time()-time0,Fhkl.size,str(Fhkl.shape)))
3329
3330# map printing for testing purposes
3331def printRho(SGLaue,rho,rhoMax):                         
3332    '''default doc string
3333   
3334    :param type name: description
3335   
3336    :returns: type name: description
3337   
3338    '''
3339    dim = len(rho.shape)
3340    if dim == 2:
3341        ix,jy = rho.shape
3342        for j in range(jy):
3343            line = ''
3344            if SGLaue in ['3','3m1','31m','6/m','6/mmm']:
3345                line += (jy-j)*'  '
3346            for i in range(ix):
3347                r = int(100*rho[i,j]/rhoMax)
3348                line += '%4d'%(r)
3349            print (line+'\n')
3350    else:
3351        ix,jy,kz = rho.shape
3352        for k in range(kz):
3353            print ('k = %d'%k)
3354            for j in range(jy):
3355                line = ''
3356                if SGLaue in ['3','3m1','31m','6/m','6/mmm']:
3357                    line += (jy-j)*'  '
3358                for i in range(ix):
3359                    r = int(100*rho[i,j,k]/rhoMax)
3360                    line += '%4d'%(r)
3361                print (line+'\n')
3362## keep this
3363               
3364def findOffset(SGData,A,Fhkl):   
3365    '''default doc string
3366   
3367    :param type name: description
3368   
3369    :returns: type name: description
3370   
3371    '''
3372    if SGData['SpGrp'] == 'P 1':
3373        return [0,0,0]   
3374    hklShape = Fhkl.shape
3375    hklHalf = np.array(hklShape)/2
3376    sortHKL = np.argsort(Fhkl.flatten())
3377    Fdict = {}
3378    for hkl in sortHKL:
3379        HKL = np.unravel_index(hkl,hklShape)
3380        F = Fhkl[HKL[0]][HKL[1]][HKL[2]]
3381        if F == 0.:
3382            break
3383        Fdict['%.6f'%(np.absolute(F))] = hkl
3384    Flist = np.flipud(np.sort(list(Fdict.keys())))
3385    F = str(1.e6)
3386    i = 0
3387    DH = []
3388    Dphi = []
3389    Hmax = 2*np.asarray(G2lat.getHKLmax(3.5,SGData,A),dtype='i')
3390    for F in Flist:
3391        hkl = np.unravel_index(Fdict[F],hklShape)
3392        if np.any(np.abs(hkl-hklHalf)-Hmax > 0):
3393            continue
3394        iabsnt,mulp,Uniq,Phi = G2spc.GenHKLf(list(hkl-hklHalf),SGData)
3395        Uniq = np.array(Uniq,dtype='i')
3396        Phi = np.array(Phi)
3397        Uniq = np.concatenate((Uniq,-Uniq))+hklHalf         # put in Friedel pairs & make as index to Farray
3398        Phi = np.concatenate((Phi,-Phi))                      # and their phase shifts
3399        Fh0 = Fhkl[hkl[0],hkl[1],hkl[2]]
3400        ang0 = np.angle(Fh0,deg=True)/360.
3401        for H,phi in list(zip(Uniq,Phi))[1:]:
3402            ang = (np.angle(Fhkl[int(H[0]),int(H[1]),int(H[2])],deg=True)/360.-phi)
3403            dH = H-hkl
3404            dang = ang-ang0
3405            DH.append(dH)
3406            Dphi.append((dang+.5) % 1.0)
3407        if i > 20 or len(DH) > 30:
3408            break
3409        i += 1
3410    DH = np.array(DH)
3411    G2fil.G2Print (' map offset no.of terms: %d from %d reflections'%(len(DH),len(Flist)))
3412    Dphi = np.array(Dphi)
3413    steps = np.array(hklShape)
3414    X,Y,Z = np.mgrid[0:1:1./steps[0],0:1:1./steps[1],0:1:1./steps[2]]
3415    XYZ = np.array(list(zip(X.flatten(),Y.flatten(),Z.flatten())))
3416    Dang = (np.dot(XYZ,DH.T)+.5)%1.-Dphi
3417    Mmap = np.reshape(np.sum((Dang)**2,axis=1),newshape=steps)/len(DH)
3418    hist,bins = np.histogram(Mmap,bins=1000)
3419#    for i,item in enumerate(hist[:10]):
3420#        print item,bins[i]
3421    chisq = np.min(Mmap)
3422    DX = -np.array(np.unravel_index(np.argmin(Mmap),Mmap.shape))
3423    G2fil.G2Print (' map offset chi**2: %.3f, map offset: %d %d %d'%(chisq,DX[0],DX[1],DX[2]))
3424#    print (np.dot(DX,DH.T)+.5)%1.-Dphi
3425    return DX
3426   
3427def ChargeFlip(data,reflDict,pgbar):
3428    '''default doc string
3429   
3430    :param type name: description
3431   
3432    :returns: type name: description
3433   
3434    '''
3435    generalData = data['General']
3436    mapData = generalData['Map']
3437    flipData = generalData['Flip']
3438    FFtable = {}
3439    if 'None' not in flipData['Norm element']:
3440        normElem = flipData['Norm element'].upper()
3441        FFs = G2el.GetFormFactorCoeff(normElem.split('+')[0].split('-')[0])
3442        for ff in FFs:
3443            if ff['Symbol'] == normElem:
3444                FFtable.update(ff)
3445    dmin = flipData['GridStep']*2.
3446    SGData = generalData['SGData']
3447    SGMT = np.array([ops[0].T for ops in SGData['SGOps']])
3448    SGT = np.array([ops[1] for ops in SGData['SGOps']])
3449    cell = generalData['Cell'][1:8]       
3450    A = G2lat.cell2A(cell[:6])
3451    Vol = cell[6]
3452    im = 0
3453    if generalData['Modulated'] == True:
3454        im = 1
3455    Hmax = np.asarray(G2lat.getHKLmax(dmin,SGData,A),dtype='i')+1
3456    adjHKLmax(SGData,Hmax)
3457    Ehkl = np.zeros(shape=2*Hmax,dtype='c16')       #2X64bits per complex no.
3458    time0 = time.time()
3459    for iref,ref in enumerate(reflDict['RefList']):
3460        dsp = ref[4+im]
3461        if im and ref[3]:   #skip super lattice reflections - result is 3D projection
3462            continue
3463        if dsp > dmin:
3464            ff = 0.1*Vol    #est. no. atoms for ~10A**3/atom
3465            if FFtable:
3466                SQ = 0.25/dsp**2
3467                ff *= G2el.ScatFac(FFtable,SQ)[0]
3468            if ref[8+im] > 0.:         #use only +ve Fobs**2
3469                E = np.sqrt(ref[8+im])/ff
3470            else:
3471                E = 0.
3472            ph = ref[10]
3473            ph = rn.uniform(0.,360.)
3474            Uniq = np.inner(ref[:3],SGMT)
3475            Phi = np.inner(ref[:3],SGT)
3476            for i,hkl in enumerate(Uniq):        #uses uniq
3477                hkl = np.asarray(hkl,dtype='i')
3478                dp = 360.*Phi[i]                #and phi
3479                a = cosd(ph+dp)
3480                b = sind(ph+dp)
3481                phasep = complex(a,b)
3482                phasem = complex(a,-b)
3483                h,k,l = hkl+Hmax
3484                Ehkl[h,k,l] = E*phasep
3485                h,k,l = -hkl+Hmax
3486                Ehkl[h,k,l] = E*phasem
3487#    Ehkl[Hmax] = 0.00001           #this to preserve F[0,0,0]
3488    testHKL = np.array(flipData['testHKL'])+Hmax
3489    CEhkl = copy.copy(Ehkl)
3490    MEhkl = ma.array(Ehkl,mask=(Ehkl==0.0))
3491    Emask = ma.getmask(MEhkl)
3492    sumE = np.sum(ma.array(np.absolute(CEhkl),mask=Emask))
3493    Ncyc = 0
3494    old = np.seterr(all='raise')
3495    twophases = []
3496    while True:       
3497        CErho = np.real(fft.fftn(fft.fftshift(CEhkl)))*(1.+0j)
3498        CEsig = np.std(CErho)
3499        CFrho = np.where(np.real(CErho) >= flipData['k-factor']*CEsig,CErho,-CErho)
3500        CFrho = np.where(np.real(CErho) <= flipData['k-Max']*CEsig,CFrho,-CFrho)      #solves U atom problem!
3501        CFhkl = fft.ifftshift(fft.ifftn(CFrho))
3502        CFhkl = np.where(CFhkl,CFhkl,1.0)           #avoid divide by zero
3503        phase = CFhkl/np.absolute(CFhkl)
3504        twophases.append([np.angle(phase[h,k,l]) for h,k,l in testHKL])
3505        CEhkl = np.absolute(Ehkl)*phase
3506        Ncyc += 1
3507        sumCF = np.sum(ma.array(np.absolute(CFhkl),mask=Emask))
3508        DEhkl = np.absolute(np.absolute(Ehkl)/sumE-np.absolute(CFhkl)/sumCF)
3509        Rcf = min(100.,np.sum(ma.array(DEhkl,mask=Emask)*100.))
3510        if Rcf < 5.:
3511            break
3512        GoOn = pgbar.Update(Rcf,newmsg='%s%8.3f%s\n%s %d'%('Residual Rcf =',Rcf,'%','No.cycles = ',Ncyc))[0]
3513        if not GoOn or Ncyc > 10000:
3514            break
3515    np.seterr(**old)
3516    G2fil.G2Print (' Charge flip time: %.4f'%(time.time()-time0),'no. elements: %d'%(Ehkl.size))
3517    CErho = np.real(fft.fftn(fft.fftshift(CEhkl)))/10.  #? to get on same scale as e-map
3518    G2fil.G2Print (' No.cycles = %d Residual Rcf =%8.3f%s Map size: %s'%(Ncyc,Rcf,'%',str(CErho.shape)))
3519    roll = findOffset(SGData,A,CEhkl)               #CEhkl needs to be just the observed set, not the full set!
3520       
3521    mapData['Rcf'] = Rcf
3522    mapData['rho'] = np.roll(np.roll(np.roll(CErho,roll[0],axis=0),roll[1],axis=1),roll[2],axis=2)
3523    mapData['rhoMax'] = max(np.max(mapData['rho']),-np.min(mapData['rho']))
3524    mapData['minmax'] = [np.max(mapData['rho']),np.min(mapData['rho'])]
3525    mapData['Type'] = reflDict['Type']
3526    return mapData,twophases
3527   
3528def findSSOffset(SGData,SSGData,A,Fhklm):   
3529    '''default doc string
3530   
3531    :param type name: description
3532   
3533    :returns: type name: description
3534   
3535    '''
3536    if SGData['SpGrp'] == 'P 1':
3537        return [0,0,0,0]   
3538    hklmShape = Fhklm.shape
3539    hklmHalf = np.array(hklmShape)/2
3540    sortHKLM = np.argsort(Fhklm.flatten())
3541    Fdict = {}
3542    for hklm in sortHKLM:
3543        HKLM = np.unravel_index(hklm,hklmShape)
3544        F = Fhklm[HKLM[0]][HKLM[1]][HKLM[2]][HKLM[3]]
3545        if F == 0.:
3546            break
3547        Fdict['%.6f'%(np.absolute(F))] = hklm
3548    Flist = np.flipud(np.sort(list(Fdict.keys())))
3549    F = str(1.e6)
3550    i = 0
3551    DH = []
3552    Dphi = []
3553    SSGMT = np.array([ops[0].T for ops in SSGData['SSGOps']])
3554    SSGT = np.array([ops[1] for ops in SSGData['SSGOps']])
3555    Hmax = 2*np.asarray(G2lat.getHKLmax(3.5,SGData,A),dtype='i')
3556    for F in Flist:
3557        hklm = np.unravel_index(Fdict[F],hklmShape)
3558        if np.any(np.abs(hklm-hklmHalf)[:3]-Hmax > 0):
3559            continue
3560        Uniq = np.inner(hklm-hklmHalf,SSGMT)
3561        Phi = np.inner(hklm-hklmHalf,SSGT)
3562        Uniq = np.concatenate((Uniq,-Uniq))+hklmHalf         # put in Friedel pairs & make as index to Farray
3563        Phi = np.concatenate((Phi,-Phi))                      # and their phase shifts
3564        Fh0 = Fhklm[hklm[0],hklm[1],hklm[2],hklm[3]]
3565        ang0 = np.angle(Fh0,deg=True)/360.
3566        for H,phi in list(zip(Uniq,Phi))[1:]:
3567            H = np.array(H,dtype=int)
3568            ang = (np.angle(Fhklm[H[0],H[1],H[2],H[3]],deg=True)/360.-phi)
3569            dH = H-hklm
3570            dang = ang-ang0
3571            DH.append(dH)
3572            Dphi.append((dang+.5) % 1.0)
3573        if i > 20 or len(DH) > 30:
3574            break
3575        i += 1
3576    DH = np.array(DH)
3577    G2fil.G2Print (' map offset no.of terms: %d from %d reflections'%(len(DH),len(Flist)))
3578    Dphi = np.array(Dphi)
3579    steps = np.array(hklmShape)
3580    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]]
3581    XYZT = np.array(list(zip(X.flatten(),Y.flatten(),Z.flatten(),T.flatten())))
3582    Dang = (np.dot(XYZT,DH.T)+.5)%1.-Dphi
3583    Mmap = np.reshape(np.sum((Dang)**2,axis=1),newshape=steps)/len(DH)
3584    hist,bins = np.histogram(Mmap,bins=1000)
3585#    for i,item in enumerate(hist[:10]):
3586#        print item,bins[i]
3587    chisq = np.min(Mmap)
3588    DX = -np.array(np.unravel_index(np.argmin(Mmap),Mmap.shape))
3589    G2fil.G2Print (' map offset chi**2: %.3f, map offset: %d %d %d %d'%(chisq,DX[0],DX[1],DX[2],DX[3]))
3590#    print (np.dot(DX,DH.T)+.5)%1.-Dphi
3591    return DX
3592   
3593def SSChargeFlip(data,reflDict,pgbar):
3594    '''default doc string
3595   
3596    :param type name: description
3597   
3598    :returns: type name: description
3599   
3600    '''
3601    generalData = data['General']
3602    mapData = generalData['Map']
3603    map4DData = {}
3604    flipData = generalData['Flip']
3605    FFtable = {}
3606    if 'None' not in flipData['Norm element']:
3607        normElem = flipData['Norm element'].upper()
3608        FFs = G2el.GetFormFactorCoeff(normElem.split('+')[0].split('-')[0])
3609        for ff in FFs:
3610            if ff['Symbol'] == normElem:
3611                FFtable.update(ff)
3612    dmin = flipData['GridStep']*2.
3613    SGData = generalData['SGData']
3614    SSGData = generalData['SSGData']
3615    SSGMT = np.array([ops[0].T for ops in SSGData['SSGOps']])
3616    SSGT = np.array([ops[1] for ops in SSGData['SSGOps']])
3617    cell = generalData['Cell'][1:8]       
3618    A = G2lat.cell2A(cell[:6])
3619    Vol = cell[6]
3620    maxM = 4
3621    Hmax = np.asarray(G2lat.getHKLmax(dmin,SGData,A)+[maxM,],dtype='i')+1
3622    adjHKLmax(SGData,Hmax)
3623    Ehkl = np.zeros(shape=2*Hmax,dtype='c16')       #2X64bits per complex no.
3624    time0 = time.time()
3625    for iref,ref in enumerate(reflDict['RefList']):
3626        dsp = ref[5]
3627        if dsp > dmin:
3628            ff = 0.1*Vol    #est. no. atoms for ~10A**3/atom
3629            if FFtable:
3630                SQ = 0.25/dsp**2
3631                ff *= G2el.ScatFac(FFtable,SQ)[0]
3632            if ref[9] > 0.:         #use only +ve Fobs**2
3633                E = np.sqrt(ref[9])/ff
3634            else:
3635                E = 0.
3636            ph = ref[11]
3637            ph = rn.uniform(0.,360.)
3638            Uniq = np.inner(ref[:4],SSGMT)
3639            Phi = np.inner(ref[:4],SSGT)
3640            for i,hklm in enumerate(Uniq):        #uses uniq
3641                hklm = np.asarray(hklm,dtype='i')
3642                dp = 360.*Phi[i]                #and phi
3643                a = cosd(ph+dp)
3644                b = sind(ph+dp)
3645                phasep = complex(a,b)
3646                phasem = complex(a,-b)
3647                h,k,l,m = hklm+Hmax
3648                Ehkl[h,k,l,m] = E*phasep
3649                h,k,l,m = -hklm+Hmax       #Friedel pair refl.
3650                Ehkl[h,k,l,m] = E*phasem
3651#    Ehkl[Hmax] = 0.00001           #this to preserve F[0,0,0]
3652    CEhkl = copy.copy(Ehkl)
3653    MEhkl = ma.array(Ehkl,mask=(Ehkl==0.0))
3654    Emask = ma.getmask(MEhkl)
3655    sumE = np.sum(ma.array(np.absolute(CEhkl),mask=Emask))
3656    Ncyc = 0
3657    old = np.seterr(all='raise')
3658    while True:       
3659        CErho = np.real(fft.fftn(fft.fftshift(CEhkl)))*(1.+0j)
3660        CEsig = np.std(CErho)
3661        CFrho = np.where(np.real(CErho) >= flipData['k-factor']*CEsig,CErho,-CErho)
3662        CFrho = np.where(np.real(CErho) <= flipData['k-Max']*CEsig,CFrho,-CFrho)      #solves U atom problem!
3663        CFhkl = fft.ifftshift(fft.ifftn(CFrho))
3664        CFhkl = np.where(CFhkl,CFhkl,1.0)           #avoid divide by zero
3665        phase = CFhkl/np.absolute(CFhkl)
3666        CEhkl = np.absolute(Ehkl)*phase
3667        Ncyc += 1
3668        sumCF = np.sum(ma.array(np.absolute(CFhkl),mask=Emask))
3669        DEhkl = np.absolute(np.absolute(Ehkl)/sumE-np.absolute(CFhkl)/sumCF)
3670        Rcf = min(100.,np.sum(ma.array(DEhkl,mask=Emask)*100.))
3671        if Rcf < 5.:
3672            break
3673        GoOn = pgbar.Update(Rcf,newmsg='%s%8.3f%s\n%s %d'%('Residual Rcf =',Rcf,'%','No.cycles = ',Ncyc))[0]
3674        if not GoOn or Ncyc > 10000:
3675            break
3676    np.seterr(**old)
3677    G2fil.G2Print (' Charge flip time: %.4f no. elements: %d'%(time.time()-time0,Ehkl.size))
3678    CErho = np.real(fft.fftn(fft.fftshift(CEhkl[:,:,:,maxM+1])))/10.    #? to get on same scale as e-map
3679    SSrho = np.real(fft.fftn(fft.fftshift(CEhkl)))/10.                  #? ditto
3680    G2fil.G2Print (' No.cycles = %d Residual Rcf =%8.3f%s Map size: %s'%(Ncyc,Rcf,'%',str(CErho.shape)))
3681    roll = findSSOffset(SGData,SSGData,A,CEhkl)               #CEhkl needs to be just the observed set, not the full set!
3682
3683    mapData['Rcf'] = Rcf
3684    mapData['rho'] = np.roll(np.roll(np.roll(CErho,roll[0],axis=0),roll[1],axis=1),roll[2],axis=2)
3685    mapData['rhoMax'] = max(np.max(mapData['rho']),-np.min(mapData['rho']))
3686    mapData['minmax'] = [np.max(mapData['rho']),np.min(mapData['rho'])]
3687    mapData['Type'] = reflDict['Type']
3688
3689    map4DData['Rcf'] = Rcf
3690    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))
3691    map4DData['rhoMax'] = max(np.max(map4DData['rho']),-np.min(map4DData['rho']))
3692    map4DData['minmax'] = [np.max(map4DData['rho']),np.min(map4DData['rho'])]
3693    map4DData['Type'] = reflDict['Type']
3694    return mapData,map4DData
3695   
3696def getRho(xyz,mapData):
3697    ''' get scattering density at a point by 8-point interpolation
3698    param xyz: coordinate to be probed
3699    param: mapData: dict of map data
3700   
3701    :returns: density at xyz
3702    '''
3703    rollMap = lambda rho,roll: np.roll(np.roll(np.roll(rho,roll[0],axis=0),roll[1],axis=1),roll[2],axis=2)
3704    if not len(mapData):
3705        return 0.0
3706    rho = copy.copy(mapData['rho'])     #don't mess up original
3707    if not len(rho):
3708        return 0.0
3709    mapShape = np.array(rho.shape)
3710    mapStep = 1./mapShape
3711    X = np.array(xyz)%1.    #get into unit cell
3712    I = np.array(X*mapShape,dtype='int')
3713    D = X-I*mapStep         #position inside map cell
3714    D12 = D[0]*D[1]
3715    D13 = D[0]*D[2]
3716    D23 = D[1]*D[2]
3717    D123 = np.prod(D)
3718    Rho = rollMap(rho,-I)    #shifts map so point is in corner
3719    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]+  \
3720        Rho[1,1,1]*D123+Rho[0,1,1]*(D23-D123)+Rho[1,0,1]*(D13-D123)+Rho[1,1,0]*(D12-D123)+  \
3721        Rho[0,0,0]*(D12+D13+D23-D123)-Rho[0,0,1]*(D13+D23-D123)-    \
3722        Rho[0,1,0]*(D23+D12-D123)-Rho[1,0,0]*(D13+D12-D123)   
3723    return R
3724       
3725def getRhos(XYZ,rho):
3726    ''' get scattering density at an array of point by 8-point interpolation
3727    this is faster than gerRho which is only used for single points. However, getRhos is
3728    replaced by scipy.ndimage.interpolation.map_coordinates which does a better job &  is just as fast.
3729    Thus, getRhos is unused in GSAS-II at this time.
3730    param xyz:  array coordinates to be probed Nx3
3731    param: rho: array copy of map (NB: don't use original!)
3732   
3733    :returns: density at xyz
3734    '''
3735    def getBoxes(rho,I):
3736        Rhos = np.zeros((2,2,2))
3737        Mx,My,Mz = rho.shape
3738        Ix,Iy,Iz = I
3739        Rhos = np.array([[[rho[Ix%Mx,Iy%My,Iz%Mz],rho[Ix%Mx,Iy%My,(Iz+1)%Mz]],
3740                          [rho[Ix%Mx,(Iy+1)%My,Iz%Mz],rho[Ix%Mx,(Iy+1)%My,(Iz+1)%Mz]]],
3741                        [[rho[(Ix+1)%Mx,Iy%My,Iz%Mz],rho[(Ix+1)%Mx,Iy%My,(Iz+1)%Mz]],
3742                          [rho[(Ix+1)%Mx,(Iy+1)%My,Iz%Mz],rho[(Ix+1)%Mx,(Iy+1)%My,(Iz+1)%Mz]]]])
3743        return Rhos
3744       
3745    Blk = 400     #400 doesn't seem to matter
3746    nBlk = len(XYZ)//Blk        #select Blk so this is an exact divide
3747    mapShape = np.array(rho.shape)
3748    mapStep = 1./mapShape
3749    X = XYZ%1.    #get into unit cell
3750    iBeg = 0
3751    R = np.zeros(len(XYZ))
3752#actually a lot faster!
3753    for iblk in range(nBlk):
3754        iFin = iBeg+Blk
3755        Xs = X[iBeg:iFin]
3756        I = np.array(np.rint(Xs*mapShape),dtype='int')
3757        Rhos = np.array([getBoxes(rho,i) for i in I])
3758        Ds = Xs-I*mapStep
3759        RIJs = Rhos[:,0,:2,:2]*(1.-Ds[:,0][:,nxs,nxs])
3760        RIs = RIJs[:,0]*(1.-Ds[:,1][:,nxs])+RIJs[:,1]*Ds[:,1][:,nxs]
3761        R[iBeg:iFin] = RIs[:,0]*(1.-Ds[:,2])+RIs[:,1]*Ds[:,2]
3762        iBeg += Blk
3763    return R
3764       
3765def SearchMap(generalData,drawingData,Neg=False):
3766    '''Does a search of a density map for peaks meeting the criterion of peak
3767    height is greater than mapData['cutOff']/100 of mapData['rhoMax'] where
3768    mapData is data['General']['mapData']; the map is also in mapData.
3769
3770    :param generalData: the phase data structure; includes the map
3771    :param drawingData: the drawing data structure
3772    :param Neg:  if True then search for negative peaks (i.e. H-atoms & neutron data)
3773
3774    :returns: (peaks,mags,dzeros) where
3775
3776        * peaks : ndarray
3777          x,y,z positions of the peaks found in the map
3778        * mags : ndarray
3779          the magnitudes of the peaks
3780        * dzeros : ndarray
3781          the distance of the peaks from  the unit cell origin
3782        * dcent : ndarray
3783          the distance of the peaks from  the unit cell center
3784
3785    '''       
3786    rollMap = lambda rho,roll: np.roll(np.roll(np.roll(rho,roll[0],axis=0),roll[1],axis=1),roll[2],axis=2)
3787   
3788    norm = 1./(np.sqrt(3.)*np.sqrt(2.*np.pi)**3)
3789   
3790    def fixSpecialPos(xyz,SGData,Amat):
3791        equivs = G2spc.GenAtom(xyz,SGData,Move=True)
3792        X = []
3793        xyzs = [equiv[0] for equiv in equivs]
3794        for x in xyzs:
3795            if np.sqrt(np.sum(np.inner(Amat,xyz-x)**2,axis=0))<0.5:
3796                X.append(x)
3797        if len(X) > 1:
3798            return np.average(X,axis=0)
3799        else:
3800            return xyz
3801       
3802    def rhoCalc(parms,rX,rY,rZ,res,SGLaue):
3803        Mag,x0,y0,z0,sig = parms
3804        z = -((x0-rX)**2+(y0-rY)**2+(z0-rZ)**2)/(2.*sig**2)
3805#        return norm*Mag*np.exp(z)/(sig*res**3)     #not slower but some faults in LS
3806        return norm*Mag*(1.+z+z**2/2.)/(sig*res**3)
3807       
3808    def peakFunc(parms,rX,rY,rZ,rho,res,SGLaue):
3809        Mag,x0,y0,z0,sig = parms
3810        M = rho-rhoCalc(parms,rX,rY,rZ,res,SGLaue)
3811        return M
3812       
3813    def peakHess(parms,rX,rY,rZ,rho,res,SGLaue):
3814        Mag,x0,y0,z0,sig = parms
3815        dMdv = np.zeros(([5,]+list(rX.shape)))
3816        delt = .01
3817        for i in range(5):
3818            parms[i] -= delt
3819            rhoCm = rhoCalc(parms,rX,rY,rZ,res,SGLaue)
3820            parms[i] += 2.*delt
3821            rhoCp = rhoCalc(parms,rX,rY,rZ,res,SGLaue)
3822            parms[i] -= delt
3823            dMdv[i] = (rhoCp-rhoCm)/(2.*delt)
3824        rhoC = rhoCalc(parms,rX,rY,rZ,res,SGLaue)
3825        Vec = np.sum(np.sum(np.sum(dMdv*(rho-rhoC),axis=3),axis=2),axis=1)
3826        dMdv = np.reshape(dMdv,(5,rX.size))
3827        Hess = np.inner(dMdv,dMdv)
3828       
3829        return Vec,Hess
3830       
3831    SGData = generalData['SGData']
3832    Amat,Bmat = G2lat.cell2AB(generalData['Cell'][1:7])
3833    peaks = []
3834    mags = []
3835    dzeros = []
3836    dcent = []
3837    try:
3838        mapData = generalData['Map']
3839        contLevel = mapData['cutOff']*mapData['rhoMax']/100.
3840        if Neg:
3841            rho = -copy.copy(mapData['rho'])     #flip +/-
3842        else:
3843            rho = copy.copy(mapData['rho'])     #don't mess up original
3844        mapHalf = np.array(rho.shape)/2
3845        res = mapData['GridStep']*2.
3846        incre = np.array(rho.shape,dtype=np.float)
3847        step = max(1.0,1./res)+1
3848        steps = np.array((3*[step,]),dtype='int32')
3849    except KeyError:
3850        G2fil.G2Print ('**** ERROR - Fourier map not defined')
3851        return peaks,mags
3852    rhoMask = ma.array(rho,mask=(rho<contLevel))
3853    indices = (-1,0,1)
3854    rolls = np.array([[h,k,l] for h in indices for k in indices for l in indices])
3855    for roll in rolls:
3856        if np.any(roll):
3857            rhoMask = ma.array(rhoMask,mask=(rhoMask-rollMap(rho,roll)<=0.))
3858    indx = np.transpose(rhoMask.nonzero())
3859    peaks = indx/incre
3860    mags = rhoMask[rhoMask.nonzero()]
3861    for i,[ind,peak,mag] in enumerate(zip(indx,peaks,mags)):
3862        rho = rollMap(rho,ind)
3863        rMM = mapHalf-steps
3864        rMP = mapHalf+steps+1
3865        rhoPeak = rho[int(rMM[0]):int(rMP[0]),int(rMM[1]):int(rMP[1]),int(rMM[2]):int(rMP[2])]
3866        peakInt = np.sum(rhoPeak)*res**3
3867        rX,rY,rZ = np.mgrid[int(rMM[0]):int(rMP[0]),int(rMM[1]):int(rMP[1]),int(rMM[2]):int(rMP[2])]
3868        x0 = [peakInt,mapHalf[0],mapHalf[1],mapHalf[2],2.0]          #magnitude, position & width(sig)
3869        result = HessianLSQ(peakFunc,x0,Hess=peakHess,
3870            args=(rX,rY,rZ,rhoPeak,res,SGData['SGLaue']),ftol=.01,maxcyc=10)
3871        x1 = result[0]
3872        if not np.any(x1 < 0):
3873            peak = (np.array(x1[1:4])-ind)/incre
3874        peak = fixSpecialPos(peak,SGData,Amat)
3875        rho = rollMap(rho,-ind)
3876    cent = np.ones(3)*.5     
3877    dzeros = np.sqrt(np.sum(np.inner(Amat,peaks)**2,axis=0))
3878    dcent = np.sqrt(np.sum(np.inner(Amat,peaks-cent)**2,axis=0))
3879    if Neg:     #want negative magnitudes for negative peaks
3880        return np.array(peaks),-np.array([mags,]).T,np.array([dzeros,]).T,np.array([dcent,]).T
3881    else:
3882        return np.array(peaks),np.array([mags,]).T,np.array([dzeros,]).T,np.array([dcent,]).T
3883   
3884def sortArray(data,pos,reverse=False):
3885    '''data is a list of items
3886    sort by pos in list; reverse if True
3887    '''
3888    T = []
3889    for i,M in enumerate(data):
3890        try:
3891            T.append((M[pos],i))
3892        except IndexError:
3893            return data
3894    D = dict(zip(T,data))
3895    T.sort()
3896    if reverse:
3897        T.reverse()
3898    X = []
3899    for key in T:
3900        X.append(D[key])
3901    return X
3902
3903def PeaksEquiv(data,Ind):
3904    '''Find the equivalent map peaks for those selected. Works on the
3905    contents of data['Map Peaks'].
3906
3907    :param data: the phase data structure
3908    :param list Ind: list of selected peak indices
3909    :returns: augmented list of peaks including those related by symmetry to the
3910      ones in Ind
3911
3912    '''       
3913    def Duplicate(xyz,peaks,Amat):
3914        if True in [np.allclose(np.inner(Amat,xyz),np.inner(Amat,peak),atol=0.5) for peak in peaks]:
3915            return True
3916        return False
3917                           
3918    generalData = data['General']
3919    Amat,Bmat = G2lat.cell2AB(generalData['Cell'][1:7])
3920    SGData = generalData['SGData']
3921    mapPeaks = data['Map Peaks']
3922    XYZ = np.array([xyz[1:4] for xyz in mapPeaks])
3923    Indx = {}
3924    for ind in Ind:
3925        xyz = np.array(mapPeaks[ind][1:4])
3926        xyzs = np.array([equiv[0] for equiv in G2spc.GenAtom(xyz,SGData,Move=True)])
3927        for jnd,xyz in enumerate(XYZ):       
3928            Indx[jnd] = Duplicate(xyz,xyzs,Amat)
3929    Ind = []
3930    for ind in Indx:
3931        if Indx[ind]:
3932            Ind.append(ind)
3933    return Ind
3934               
3935def PeaksUnique(data,Ind):
3936    '''Finds the symmetry unique set of peaks from those selected. Works on the
3937    contents of data['Map Peaks'].
3938
3939    :param data: the phase data structure
3940    :param list Ind: list of selected peak indices
3941    :returns: the list of symmetry unique peaks from among those given in Ind
3942
3943    '''       
3944#    XYZE = np.array([[equiv[0] for equiv in G2spc.GenAtom(xyz[1:4],SGData,Move=True)] for xyz in mapPeaks]) #keep this!!
3945
3946    def noDuplicate(xyz,peaks,Amat):
3947        if True in [np.allclose(np.inner(Amat,xyz),np.inner(Amat,peak),atol=0.5) for peak in peaks]:
3948            return False
3949        return True
3950                           
3951    generalData = data['General']
3952    Amat,Bmat = G2lat.cell2AB(generalData['Cell'][1:7])
3953    SGData = generalData['SGData']
3954    mapPeaks = data['Map Peaks']
3955    Indx = {}
3956    XYZ = {}
3957    for ind in Ind:
3958        XYZ[ind] = np.array(mapPeaks[ind][1:4])
3959        Indx[ind] = True
3960    for ind in Ind:
3961        if Indx[ind]:
3962            xyz = XYZ[ind]
3963            for jnd in Ind:
3964                if ind != jnd and Indx[jnd]:                       
3965                    Equiv = G2spc.GenAtom(XYZ[jnd],SGData,Move=True)
3966                    xyzs = np.array([equiv[0] for equiv in Equiv])
3967                    Indx[jnd] = noDuplicate(xyz,xyzs,Amat)
3968    Ind = []
3969    for ind in Indx:
3970        if Indx[ind]:
3971            Ind.append(ind)
3972    return Ind
3973
3974################################################################################
3975##### Dysnomia setup & return stuff
3976################################################################################
3977   
3978
3979   
3980################################################################################
3981##### single peak fitting profile fxn stuff
3982################################################################################
3983
3984def getCWsig(ins,pos):
3985    '''get CW peak profile sigma^2
3986   
3987    :param dict ins: instrument parameters with at least 'U', 'V', & 'W'
3988      as values only
3989    :param float pos: 2-theta of peak
3990    :returns: float getCWsig: peak sigma^2
3991   
3992    '''
3993    tp = tand(pos/2.0)
3994    return ins['U']*tp**2+ins['V']*tp+ins['W']
3995   
3996def getCWsigDeriv(pos):
3997    '''get derivatives of CW peak profile sigma^2 wrt U,V, & W
3998   
3999    :param float pos: 2-theta of peak
4000   
4001    :returns: list getCWsigDeriv: d(sig^2)/dU, d(sig)/dV & d(sig)/dW
4002   
4003    '''
4004    tp = tand(pos/2.0)
4005    return tp**2,tp,1.0
4006   
4007def getCWgam(ins,pos):
4008    '''get CW peak profile gamma
4009   
4010    :param dict ins: instrument parameters with at least 'X', 'Y' & 'Z'
4011      as values only
4012    :param float pos: 2-theta of peak
4013    :returns: float getCWgam: peak gamma
4014   
4015    '''
4016    return ins['X']/cosd(pos/2.0)+ins['Y']*tand(pos/2.0)+ins.get('Z',0.0)
4017   
4018def getCWgamDeriv(pos):
4019    '''get derivatives of CW peak profile gamma wrt X, Y & Z
4020   
4021    :param float pos: 2-theta of peak
4022   
4023    :returns: list getCWgamDeriv: d(gam)/dX & d(gam)/dY
4024   
4025    '''
4026    return 1./cosd(pos/2.0),tand(pos/2.0),1.0
4027   
4028def getTOFsig(ins,dsp):
4029    '''get TOF peak profile sigma^2
4030   
4031    :param dict ins: instrument parameters with at least 'sig-0', 'sig-1' & 'sig-q'
4032      as values only
4033    :param float dsp: d-spacing of peak
4034   
4035    :returns: float getTOFsig: peak sigma^2
4036   
4037    '''
4038    return ins['sig-0']+ins['sig-1']*dsp**2+ins['sig-2']*dsp**4+ins['sig-q']*dsp
4039   
4040def getTOFsigDeriv(dsp):
4041    '''get derivatives of TOF peak profile sigma^2 wrt sig-0, sig-1, & sig-q
4042   
4043    :param float dsp: d-spacing of peak
4044   
4045    :returns: list getTOFsigDeriv: d(sig0/d(sig-0), d(sig)/d(sig-1) & d(sig)/d(sig-q)
4046   
4047    '''
4048    return 1.0,dsp**2,dsp**4,dsp
4049   
4050def getTOFgamma(ins,dsp):
4051    '''get TOF peak profile gamma
4052   
4053    :param dict ins: instrument parameters with at least 'X', 'Y' & 'Z'
4054      as values only
4055    :param float dsp: d-spacing of peak
4056   
4057    :returns: float getTOFgamma: peak gamma
4058   
4059    '''
4060    return ins['Z']+ins['X']*dsp+ins['Y']*dsp**2
4061   
4062def getTOFgammaDeriv(dsp):
4063    '''get derivatives of TOF peak profile gamma wrt X, Y & Z
4064   
4065    :param float dsp: d-spacing of peak
4066   
4067    :returns: list getTOFgammaDeriv: d(gam)/dX & d(gam)/dY
4068   
4069    '''
4070    return dsp,dsp**2,1.0
4071   
4072def getTOFbeta(ins,dsp):
4073    '''get TOF peak profile beta
4074   
4075    :param dict ins: instrument parameters with at least 'beat-0', 'beta-1' & 'beta-q'
4076      as values only
4077    :param float dsp: d-spacing of peak
4078   
4079    :returns: float getTOFbeta: peak beat
4080   
4081    '''
4082    return ins['beta-0']+ins['beta-1']/dsp**4+ins['beta-q']/dsp**2
4083   
4084def getTOFbetaDeriv(dsp):
4085    '''get derivatives of TOF peak profile beta wrt beta-0, beta-1, & beat-q
4086   
4087    :param float dsp: d-spacing of peak
4088   
4089    :returns: list getTOFbetaDeriv: d(beta)/d(beat-0), d(beta)/d(beta-1) & d(beta)/d(beta-q)
4090   
4091    '''
4092    return 1.0,1./dsp**4,1./dsp**2
4093   
4094def getTOFalpha(ins,dsp):
4095    '''get TOF peak profile alpha
4096   
4097    :param dict ins: instrument parameters with at least 'alpha'
4098      as values only
4099    :param float dsp: d-spacing of peak
4100   
4101    :returns: flaot getTOFalpha: peak alpha
4102   
4103    '''
4104    return ins['alpha']/dsp
4105   
4106def getTOFalphaDeriv(dsp):
4107    '''get derivatives of TOF peak profile beta wrt alpha
4108   
4109    :param float dsp: d-spacing of peak
4110   
4111    :returns: float getTOFalphaDeriv: d(alp)/d(alpha)
4112   
4113    '''
4114    return 1./dsp
4115   
4116def setPeakparms(Parms,Parms2,pos,mag,ifQ=False,useFit=False):
4117    '''set starting peak parameters for single peak fits from plot selection or auto selection
4118   
4119    :param dict Parms: instrument parameters dictionary
4120    :param dict Parms2: table lookup for TOF profile coefficients
4121    :param float pos: peak position in 2-theta, TOF or Q (ifQ=True)
4122    :param float mag: peak top magnitude from pick
4123    :param bool ifQ: True if pos in Q
4124    :param bool useFit: True if use fitted CW Parms values (not defaults)
4125   
4126    :returns: list XY: peak list entry:
4127        for CW: [pos,0,mag,1,sig,0,gam,0]
4128        for TOF: [pos,0,mag,1,alp,0,bet,0,sig,0,gam,0]
4129        NB: mag refinement set by default, all others off
4130   
4131    '''
4132    ind = 0
4133    if useFit:
4134        ind = 1
4135    ins = {}
4136    if 'C' in Parms['Type'][0]:                            #CW data - TOF later in an else
4137        for x in ['U','V','W','X','Y','Z']:
4138            ins[x] = Parms.get(x,[0.0,0.0])[ind]
4139        if ifQ:                              #qplot - convert back to 2-theta
4140            pos = 2.0*asind(pos*getWave(Parms)/(4*math.pi))
4141        sig = getCWsig(ins,pos)
4142        gam = getCWgam(ins,pos)           
4143        XY = [pos,0, mag,1, sig,0, gam,0]       #default refine intensity 1st
4144    else:
4145        if ifQ:
4146            dsp = 2.*np.pi/pos
4147            pos = Parms['difC']*dsp
4148        else:
4149            dsp = pos/Parms['difC'][1]
4150        if 'Pdabc' in Parms2:
4151            for x in ['sig-0','sig-1','sig-2','sig-q','X','Y','Z']:
4152                ins[x] = Parms.get(x,[0.0,0.0])[ind]
4153            Pdabc = Parms2['Pdabc'].T
4154            alp = np.interp(dsp,Pdabc[0],Pdabc[1])
4155            bet = np.interp(dsp,Pdabc[0],Pdabc[2])
4156        else:
4157            for x in ['alpha','beta-0','beta-1','beta-q','sig-0','sig-1','sig-2','sig-q','X','Y','Z']:
4158                ins[x] = Parms.get(x,[0.0,0.0])[ind]
4159            alp = getTOFalpha(ins,dsp)
4160            bet = getTOFbeta(ins,dsp)
4161        sig = getTOFsig(ins,dsp)
4162        gam = getTOFgamma(ins,dsp)
4163        XY = [pos,0,mag,1,alp,0,bet,0,sig,0,gam,0]
4164    return XY
4165   
4166################################################################################
4167##### MC/SA stuff
4168################################################################################
4169
4170#scipy/optimize/anneal.py code modified by R. Von Dreele 2013
4171# Original Author: Travis Oliphant 2002
4172# Bug-fixes in 2006 by Tim Leslie
4173
4174
4175import numpy
4176from numpy import asarray, exp, squeeze, sign, \
4177     all, shape, array, where
4178from numpy import random
4179
4180#__all__ = ['anneal']
4181
4182class base_schedule(object):
4183    def __init__(self):
4184        self.dwell = 20
4185        self.lower = 0.
4186        self.upper = 1.
4187        self.Ninit = 50
4188        self.accepted = 0
4189        self.tests = 0
4190        self.feval = 0
4191        self.k = 0
4192        self.T = None
4193
4194    def init(self, **options):
4195        self.__dict__.update(options)
4196        self.lower = asarray(self.lower)
4197        self.lower = where(self.lower == numpy.NINF, -_double_max, self.lower)
4198        self.upper = asarray(self.upper)
4199        self.upper = where(self.upper == numpy.PINF, _double_max, self.upper)
4200        self.k = 0
4201        self.accepted = 0
4202        self.feval = 0
4203        self.tests = 0
4204
4205    def getstart_temp(self, best_state):
4206        """ Find a matching starting temperature and starting parameters vector
4207        i.e. find x0 such that func(x0) = T0.
4208
4209        :param best_state: _state
4210            A _state object to store the function value and x0 found.
4211
4212        :returns: x0 : array
4213            The starting parameters vector.
4214        """
4215
4216        assert(not self.dims is None)
4217        lrange = self.lower
4218        urange = self.upper
4219        fmax = _double_min
4220        fmin = _double_max
4221        for _ in range(self.Ninit):
4222            x0 = random.uniform(size=self.dims)*(urange-lrange) + lrange
4223            fval = self.func(x0, *self.args)
4224            self.feval += 1
4225            if fval > fmax:
4226                fmax = fval
4227            if fval < fmin:
4228                fmin = fval
4229                best_state.cost = fval
4230                best_state.x = array(x0)
4231
4232        self.T0 = (fmax-fmin)*1.5
4233        return best_state.x
4234       
4235    def set_range(self,x0,frac):
4236        delrange = frac*(self.upper-self.lower)
4237        self.upper = x0+delrange
4238        self.lower = x0-delrange
4239
4240    def accept_test(self, dE):
4241        T = self.T
4242        self.tests += 1
4243        if dE < 0:
4244            self.accepted += 1
4245            return 1
4246        p = exp(-dE*1.0/T)
4247        if (p > random.uniform(0.0, 1.0)):
4248            self.accepted += 1
4249            return 1
4250        return 0
4251
4252    def update_guess(self, x0):
4253        return np.squeeze(np.random.uniform(0.,1.,size=self.dims))*(self.upper-self.lower)+self.lower
4254
4255    def update_temp(self, x0):
4256        pass
4257
4258class fast_sa(base_schedule):
4259    def init(self, **options):
4260        self.__dict__.update(options)
4261
4262    def update_guess(self, x0):
4263        x0 = asarray(x0)
4264        u = squeeze(random.uniform(0.0, 1.0, size=self.dims))
4265        T = self.T
4266        xc = (sign(u-0.5)*T*((1+1.0/T)**abs(2*u-1)-1.0)+1.0)/2.0
4267        xnew = xc*(self.upper - self.lower)+self.lower
4268        return xnew
4269
4270    def update_temp(self):
4271        self.T = self.T0*exp(-self.c * self.k**(self.quench))
4272        self.k += 1
4273        return
4274
4275class log_sa(base_schedule):        #OK
4276
4277    def init(self,**options):
4278        self.__dict__.update(options)
4279       
4280    def update_guess(self,x0):     #same as default #TODO - is this a reasonable update procedure?
4281        u = squeeze(random.uniform(0.0, 1.0, size=self.dims))
4282        T = self.T
4283        xc = (sign(u-0.5)*T*((1+1.0/T)**abs(2*u-1)-1.0)+1.0)/2.0
4284        xnew = xc*(self.upper - self.lower)+self.lower
4285        return xnew
4286       
4287    def update_temp(self):
4288        self.k += 1
4289        self.T = self.T0*self.slope**self.k
4290       
4291class _state(object):
4292    def __init__(self):
4293        self.x = None
4294        self.cost = None
4295
4296def makeTsched(data):
4297    if data['Algorithm'] == 'fast':
4298        sched = fast_sa()
4299        sched.quench = data['fast parms'][0]
4300        sched.c = data['fast parms'][1]
4301    elif data['Algorithm'] == 'log':
4302        sched = log_sa()
4303        sched.slope = data['log slope']
4304    sched.T0 = data['Annealing'][0]
4305    if not sched.T0:
4306        sched.T0 = 50.
4307    Tf = data['Annealing'][1]
4308    if not Tf:
4309        Tf = 0.001
4310    Tsched = [sched.T0,]
4311    while Tsched[-1] > Tf:
4312        sched.update_temp()
4313        Tsched.append(sched.T)
4314    return Tsched[1:]
4315   
4316def anneal(func, x0, args=(), schedule='fast', 
4317           T0=None, Tf=1e-12, maxeval=None, maxaccept=None, maxiter=400,
4318           feps=1e-6, quench=1.0, c=1.0,
4319           lower=-100, upper=100, dwell=50, slope=0.9,ranStart=False,
4320           ranRange=0.10,autoRan=False,dlg=None):
4321    """Minimize a function using simulated annealing.
4322
4323    Schedule is a schedule class implementing the annealing schedule.
4324    Available ones are 'fast', 'cauchy', 'boltzmann'
4325
4326    :param callable func: f(x, \*args)
4327        Function to be optimized.
4328    :param ndarray x0:
4329        Initial guess.
4330    :param tuple args:
4331        Extra parameters to `func`.
4332    :param base_schedule schedule:
4333        Annealing schedule to use (a class).
4334    :param float T0:
4335        Initial Temperature (estimated as 1.2 times the largest
4336        cost-function deviation over random points in the range).
4337    :param float Tf:
4338        Final goal temperature.
4339    :param int maxeval:
4340        Maximum function evaluations.
4341    :param int maxaccept:
4342        Maximum changes to accept.
4343    :param int maxiter:
4344        Maximum cooling iterations.
4345    :param float feps:
4346        Stopping relative error tolerance for the function value in
4347        last four coolings.
4348    :param float quench,c:
4349        Parameters to alter fast_sa schedule.
4350    :param float/ndarray lower,upper:
4351        Lower and upper bounds on `x`.
4352    :param int dwell:
4353        The number of times to search the space at each temperature.
4354    :param float slope:
4355        Parameter for log schedule
4356    :param bool ranStart=False:
4357        True for set 10% of ranges about x
4358
4359    :returns: (xmin, Jmin, T, feval, iters, accept, retval) where
4360
4361     * xmin (ndarray): Point giving smallest value found.
4362     * Jmin (float): Minimum value of function found.
4363     * T (float): Final temperature.
4364     * feval (int): Number of function evaluations.
4365     * iters (int): Number of cooling iterations.
4366     * accept (int): Number of tests accepted.
4367     * retval (int): Flag indicating stopping condition:
4368
4369              *  0: Points no longer changing
4370              *  1: Cooled to final temperature
4371              *  2: Maximum function evaluations
4372              *  3: Maximum cooling iterations reached
4373              *  4: Maximum accepted query locations reached
4374              *  5: Final point not the minimum amongst encountered points
4375
4376    *Notes*:
4377    Simulated annealing is a random algorithm which uses no derivative
4378    information from the function being optimized. In practice it has
4379    been more useful in discrete optimization than continuous
4380    optimization, as there are usually better algorithms for continuous
4381    optimization problems.
4382
4383    Some experimentation by trying the difference temperature
4384    schedules and altering their parameters is likely required to
4385    obtain good performance.
4386
4387    The randomness in the algorithm comes from random sampling in numpy.
4388    To obtain the same results you can call numpy.random.seed with the
4389    same seed immediately before calling scipy.optimize.anneal.
4390
4391    We give a brief description of how the three temperature schedules
4392    generate new points and vary their temperature. Temperatures are
4393    only updated with iterations in the outer loop. The inner loop is
4394    over range(dwell), and new points are generated for
4395    every iteration in the inner loop. (Though whether the proposed
4396    new points are accepted is probabilistic.)
4397
4398    For readability, let d denote the dimension of the inputs to func.
4399    Also, let x_old denote the previous state, and k denote the
4400    iteration number of the outer loop. All other variables not
4401    defined below are input variables to scipy.optimize.anneal itself.
4402
4403    In the 'fast' schedule the updates are ::
4404
4405        u ~ Uniform(0, 1, size=d)
4406        y = sgn(u - 0.5) * T * ((1+ 1/T)**abs(2u-1) -1.0)
4407        xc = y * (upper - lower)
4408        x_new = x_old + xc
4409
4410        T_new = T0 * exp(-c * k**quench)
4411
4412    """
4413   
4414    ''' Scipy license:
4415        Copyright (c) 2001, 2002 Enthought, Inc.
4416    All rights reserved.
4417   
4418    Copyright (c) 2003-2016 SciPy Developers.
4419    All rights reserved.
4420   
4421    Redistribution and use in source and binary forms, with or without
4422    modification, are permitted provided that the following conditions are met:
4423   
4424      a. Redistributions of source code must retain the above copyright notice,
4425         this list of conditions and the following disclaimer.
4426      b. Redistributions in binary form must reproduce the above copyright
4427         notice, this list of conditions and the following disclaimer in the
4428         documentation and/or other materials provided with the distribution.
4429      c. Neither the name of Enthought nor the names of the SciPy Developers
4430         may be used to endorse or promote products derived from this software
4431         without specific prior written permission.
4432    '''
4433    x0 = asarray(x0)
4434    lower = asarray(lower)
4435    upper = asarray(upper)
4436
4437    schedule = eval(schedule+'_sa()')
4438    #   initialize the schedule
4439    schedule.init(dims=shape(x0),func=func,args=args,T0=T0,lower=lower, upper=upper,
4440        c=c, quench=quench, dwell=dwell, slope=slope)
4441
4442    current_state, last_state, best_state = _state(), _state(), _state()
4443    if ranStart:
4444        schedule.set_range(x0,ranRange)
4445    if T0 is None:
4446        x0 = schedule.getstart_temp(best_state)
4447    else:
4448        x0 = random.uniform(size=len(x0))*(upper-lower) + lower
4449        best_state.x = None
4450        best_state.cost = numpy.Inf
4451
4452    last_state.x = asarray(x0).copy()
4453    fval = func(x0,*args)
4454    schedule.feval += 1
4455    last_state.cost = fval
4456    if last_state.cost < best_state.cost:
4457        best_state.cost = fval
4458        best_state.x = asarray(x0).copy()
4459    schedule.T = schedule.T0
4460    fqueue = [100, 300, 500, 700]
4461    iters = 1
4462    keepGoing = True
4463    bestn = 0
4464    while keepGoing:
4465        retval = 0
4466        for n in range(dwell):
4467            current_state.x = schedule.update_guess(last_state.x)
4468            current_state.cost = func(current_state.x,*args)
4469            schedule.feval += 1
4470
4471            dE = current_state.cost - last_state.cost
4472            if schedule.accept_test(dE):
4473                last_state.x = current_state.x.copy()
4474                last_state.cost = current_state.cost
4475                if last_state.cost < best_state.cost:
4476                    best_state.x = last_state.x.copy()
4477                    best_state.cost = last_state.cost
4478                    bestn = n
4479                    if best_state.cost < 1.0 and autoRan:
4480                        schedule.set_range(x0,best_state.cost/2.)                       
4481        if dlg:
4482            GoOn = dlg.Update(min(100.,best_state.cost*100),
4483                newmsg='%s%8.5f, %s%d\n%s%8.4f%s'%('Temperature =',schedule.T, \
4484                    'Best trial:',bestn,  \
4485                    'MC/SA Residual:',best_state.cost*100,'%', \
4486                    ))[0]
4487            if not GoOn:
4488                best_state.x = last_state.x.copy()
4489                best_state.cost = last_state.cost
4490                retval = 5
4491        schedule.update_temp()
4492        iters += 1
4493        # Stopping conditions
4494        # 0) last saved values of f from each cooling step
4495        #     are all very similar (effectively cooled)
4496        # 1) Tf is set and we are below it
4497        # 2) maxeval is set and we are past it
4498        # 3) maxiter is set and we are past it
4499        # 4) maxaccept is set and we are past it
4500        # 5) user canceled run via progress bar
4501
4502        fqueue.append(squeeze(last_state.cost))
4503        fqueue.pop(0)
4504        af = asarray(fqueue)*1.0
4505        if retval == 5:
4506            G2fil.G2Print ('Error: User terminated run; incomplete MC/SA')
4507            keepGoing = False
4508            break
4509        if all(abs((af-af[0])/af[0]) < feps):
4510            retval = 0
4511            if abs(af[-1]-best_state.cost) > feps*10:
4512                retval = 5
4513                G2fil.G2Print (" Warning: Cooled to %.4f > selected Tmin %.4f in %d steps"%(squeeze(last_state.cost),Tf,iters-1))
4514            break
4515        if (Tf is not None) and (schedule.T < Tf):
4516#            print ' Minimum T reached in %d steps'%(iters-1)
4517            retval = 1
4518            break
4519        if (maxeval is not None) and (schedule.feval > maxeval):
4520            retval = 2
4521            break
4522        if (iters > maxiter):
4523            G2fil.G2Print  (" Warning: Maximum number of iterations exceeded.")
4524            retval = 3
4525            break
4526        if (maxaccept is not None) and (schedule.accepted > maxaccept):
4527            retval = 4
4528            break
4529
4530    return best_state.x, best_state.cost, schedule.T, \
4531           schedule.feval, iters, schedule.accepted, retval
4532
4533def worker(iCyc,data,RBdata,reflType,reflData,covData,out_q,out_t,out_n,nprocess=-1):
4534    outlist = []
4535    timelist = []
4536    nsflist = []
4537    random.seed(int(time.time())%100000+nprocess)   #make sure each process has a different random start
4538    for n in range(iCyc):
4539        result = mcsaSearch(data,RBdata,reflType,reflData,covData,None,False)         #mcsa result,time,rcov
4540        outlist.append(result[0])
4541        timelist.append(result[1])
4542        nsflist.append(result[2])
4543        G2fil.G2Print (' MC/SA final fit: %.3f%% structure factor time: %.3f'%(100*result[0][2],result[1]))
4544    out_q.put(outlist)
4545    out_t.put(timelist)
4546    out_n.put(nsflist)
4547
4548def MPmcsaSearch(nCyc,data,RBdata,reflType,reflData,covData,nprocs):
4549    import multiprocessing as mp
4550   
4551    out_q = mp.Queue()
4552    out_t = mp.Queue()
4553    out_n = mp.Queue()
4554    procs = []
4555    totsftime = 0.
4556    totnsf = 0
4557    iCyc = np.zeros(nprocs)
4558    for i in range(nCyc):
4559        iCyc[i%nprocs] += 1
4560    for i in range(nprocs):
4561        p = mp.Process(target=worker,args=(int(iCyc[i]),data,RBdata,reflType,reflData,covData,out_q,out_t,out_n,i))
4562        procs.append(p)
4563        p.start()
4564    resultlist = []
4565    for i in range(nprocs):
4566        resultlist += out_q.get()
4567        totsftime += np.sum(out_t.get())
4568        totnsf += np.sum(out_n.get())
4569    for p in procs:
4570        p.join()
4571    return resultlist,totsftime,totnsf
4572
4573def mcsaSearch(data,RBdata,reflType,reflData,covData,pgbar,start=True):
4574    '''default doc string
4575   
4576    :param type name: description
4577   
4578    :returns: type name: description
4579    '''
4580   
4581    class RandomDisplacementBounds(object):
4582        """random displacement with bounds"""
4583        def __init__(self, xmin, xmax, stepsize=0.5):
4584            self.xmin = xmin
4585            self.xmax = xmax
4586            self.stepsize = stepsize
4587   
4588        def __call__(self, x):
4589            """take a random step but ensure the new position is within the bounds"""
4590            while True:
4591                # this could be done in a much more clever way, but it will work for example purposes
4592                steps = self.xmax-self.xmin
4593                xnew = x + np.random.uniform(-self.stepsize*steps, self.stepsize*steps, np.shape(x))
4594                if np.all(xnew < self.xmax) and np.all(xnew > self.xmin):
4595                    break
4596            return xnew
4597   
4598    global tsum,nsum
4599    tsum = 0.
4600    nsum = 0
4601   
4602    def getMDparms(item,pfx,parmDict,varyList):
4603        parmDict[pfx+'MDaxis'] = item['axis']
4604        parmDict[pfx+'MDval'] = item['Coef'][0]
4605        if item['Coef'][1]:
4606            varyList += [pfx+'MDval',]
4607            limits = item['Coef'][2]
4608            lower.append(limits[0])
4609            upper.append(limits[1])
4610                       
4611    def getAtomparms(item,pfx,aTypes,SGData,parmDict,varyList):
4612        parmDict[pfx+'Atype'] = item['atType']
4613        aTypes |= set([item['atType'],]) 
4614        pstr = ['Ax','Ay','Az']
4615        XYZ = [0,0,0]
4616        for i in range(3):
4617            name = pfx+pstr[i]
4618            parmDict[name] = item['Pos'][0][i]
4619            XYZ[i] = parmDict[name]
4620            if item['Pos'][1][i]:
4621                varyList += [name,]
4622                limits = item['Pos'][2][i]
4623                lower.append(limits[0])
4624                upper.append(limits[1])
4625        parmDict[pfx+'Amul'] = len(list(G2spc.GenAtom(XYZ,SGData)))
4626           
4627    def getRBparms(item,mfx,aTypes,RBdata,SGData,atNo,parmDict,varyList):
4628        parmDict[mfx+'MolCent'] = item['MolCent']
4629        parmDict[mfx+'RBId'] = item['RBId']
4630        pstr = ['Px','Py','Pz']
4631        ostr = ['Qa','Qi','Qj','Qk']    #angle,vector not quaternion
4632        for i in range(3):
4633            name = mfx+pstr[i]
4634            parmDict[name] = item['Pos'][0][i]
4635            if item['Pos'][1][i]:
4636                varyList += [name,]
4637                limits = item['Pos'][2][i]
4638                lower.append(limits[0])
4639                upper.append(limits[1])
4640        AV = item['Ori'][0]
4641        A = AV[0]
4642        V = AV[1:]
4643        for i in range(4):
4644            name = mfx+ostr[i]
4645            if i:
4646                parmDict[name] = V[i-1]
4647            else:
4648                parmDict[name] = A
4649            if item['Ovar'] == 'AV':
4650                varyList += [name,]
4651                limits = item['Ori'][2][i]
4652                lower.append(limits[0])
4653                upper.append(limits[1])
4654            elif item['Ovar'] == 'A' and not i:
4655                varyList += [name,]
4656                limits = item['Ori'][2][i]
4657                lower.append(limits[0])
4658                upper.append(limits[1])
4659        if 'Tor' in item:      #'Tor' not there for 'Vector' RBs
4660            for i in range(len(item['Tor'][0])):
4661                name = mfx+'Tor'+str(i)
4662                parmDict[name] = item['Tor'][0][i]
4663                if item['Tor'][1][i]:
4664                    varyList += [name,]
4665                    limits = item['Tor'][2][i]
4666                    lower.append(limits[0])
4667                    upper.append(limits[1])
4668        atypes = RBdata[item['Type']][item['RBId']]['rbTypes']
4669        aTypes |= set(atypes)
4670        atNo += len(atypes)
4671        return atNo
4672               
4673    def GetAtomM(Xdata,SGData):
4674        Mdata = []
4675        for xyz in Xdata:
4676            Mdata.append(float(len(list(G2spc.GenAtom(xyz,SGData)))))
4677        return np.array(Mdata)
4678       
4679    def GetAtomT(RBdata,parmDict):
4680        'Needs a doc string'
4681        atNo = parmDict['atNo']
4682        nfixAt = parmDict['nfixAt']
4683        Tdata = atNo*[' ',]
4684        for iatm in range(nfixAt):
4685            parm = ':'+str(iatm)+':Atype'
4686            if parm in parmDict:
4687                Tdata[iatm] = aTypes.index(parmDict[parm])
4688        iatm = nfixAt
4689        for iObj in range(parmDict['nObj']):
4690            pfx = str(iObj)+':'
4691            if parmDict[pfx+'Type'] in ['Vector','Residue']:
4692                if parmDict[pfx+'Type'] == 'Vector':
4693                    RBRes = RBdata['Vector'][parmDict[pfx+'RBId']]
4694                    nAtm = len(RBRes['rbVect'][0])
4695                else:       #Residue
4696                    RBRes = RBdata['Residue'][parmDict[pfx+'RBId']]
4697                    nAtm = len(RBRes['rbXYZ'])
4698                for i in range(nAtm):
4699                    Tdata[iatm] = aTypes.index(RBRes['rbTypes'][i])
4700                    iatm += 1
4701            elif parmDict[pfx+'Type'] == 'Atom':
4702                atNo = parmDict[pfx+'atNo']
4703                parm = pfx+'Atype'              #remove extra ':'
4704                if parm in parmDict:
4705                    Tdata[atNo] = aTypes.index(parmDict[parm])
4706                iatm += 1
4707            else:
4708                continue        #skips March Dollase
4709        return Tdata
4710       
4711    def GetAtomX(RBdata,parmDict):
4712        'Needs a doc string'
4713        Bmat = parmDict['Bmat']
4714        atNo = parmDict['atNo']
4715        nfixAt = parmDict['nfixAt']
4716        Xdata = np.zeros((3,atNo))
4717        keys = {':Ax':Xdata[0],':Ay':Xdata[1],':Az':Xdata[2]}
4718        for iatm in range(nfixAt):
4719            for key in keys:
4720                parm = ':'+str(iatm)+key
4721                if parm in parmDict:
4722                    keys[key][iatm] = parmDict[parm]
4723        iatm = nfixAt
4724        for iObj in range(parmDict['nObj']):
4725            pfx = str(iObj)+':'
4726            if parmDict[pfx+'Type'] in ['Vector','Residue']:
4727                if parmDict[pfx+'Type'] == 'Vector':
4728                    RBRes = RBdata['Vector'][parmDict[pfx+'RBId']]
4729                    vecs = RBRes['rbVect']
4730                    mags = RBRes['VectMag']
4731                    Cart = np.zeros_like(vecs[0])
4732                    for vec,mag in zip(vecs,mags):
4733                        Cart += vec*mag
4734                elif parmDict[pfx+'Type'] == 'Residue':
4735                    RBRes = RBdata['Residue'][parmDict[pfx+'RBId']]
4736                    Cart = np.array(RBRes['rbXYZ'])
4737                    for itor,seq in enumerate(RBRes['rbSeq']):
4738                        QuatA = AVdeg2Q(parmDict[pfx+'Tor'+str(itor)],Cart[seq[0]]-Cart[seq[1]])
4739                        Cart[seq[3]] = prodQVQ(QuatA,Cart[seq[3]]-Cart[seq[1]])+Cart[seq[1]]
4740                if parmDict[pfx+'MolCent'][1]:
4741                    Cart -= parmDict[pfx+'MolCent'][0]
4742                Qori = AVdeg2Q(parmDict[pfx+'Qa'],[parmDict[pfx+'Qi'],parmDict[pfx+'Qj'],parmDict[pfx+'Qk']])
4743                Pos = np.array([parmDict[pfx+'Px'],parmDict[pfx+'Py'],parmDict[pfx+'Pz']])
4744                Xdata.T[iatm:iatm+len(Cart)] = np.inner(Bmat,prodQVQ(Qori,Cart)).T+Pos
4745                iatm += len(Cart)
4746            elif parmDict[pfx+'Type'] == 'Atom':
4747                atNo = parmDict[pfx+'atNo']
4748                for key in keys:
4749                    parm = pfx+key[1:]              #remove extra ':'
4750                    if parm in parmDict:
4751                        keys[key][atNo] = parmDict[parm]
4752                iatm += 1
4753            else:
4754                continue        #skips March Dollase
4755        return Xdata.T
4756       
4757    def getAllTX(Tdata,Mdata,Xdata,SGM,SGT):
4758        allX = np.inner(Xdata,SGM)+SGT
4759        allT = np.repeat(Tdata,allX.shape[1])
4760        allM = np.repeat(Mdata,allX.shape[1])
4761        allX = np.reshape(allX,(-1,3))
4762        return allT,allM,allX
4763
4764    def getAllX(Xdata,SGM,SGT):
4765        allX = np.inner(Xdata,SGM)+SGT
4766        allX = np.reshape(allX,(-1,3))
4767        return allX
4768       
4769    def normQuaternions(RBdata,parmDict,varyList,values):
4770        for iObj in range(parmDict['nObj']):
4771            pfx = str(iObj)+':'
4772            if parmDict[pfx+'Type'] in ['Vector','Residue']:
4773                Qori = AVdeg2Q(parmDict[pfx+'Qa'],[parmDict[pfx+'Qi'],parmDict[pfx+'Qj'],parmDict[pfx+'Qk']])
4774                A,V = Q2AVdeg(Qori)
4775                for i,name in enumerate(['Qa','Qi','Qj','Qk']):
4776                    if i:
4777                        parmDict[pfx+name] = V[i-1]
4778                    else:
4779                        parmDict[pfx+name] = A
4780       
4781    def mcsaCalc(values,refList,rcov,cosTable,ifInv,allFF,RBdata,varyList,parmDict):
4782        ''' Compute structure factors for all h,k,l for phase
4783        input:
4784            refList: [ref] where each ref = h,k,l,m,d,...
4785            rcov:   array[nref,nref] covariance terms between Fo^2 values
4786            ifInv:  bool True if centrosymmetric
4787            allFF: array[nref,natoms] each value is mult*FF(H)/max(mult)
4788            RBdata: [dict] rigid body dictionary
4789            varyList: [list] names of varied parameters in MC/SA (not used here)           
4790            ParmDict: [dict] problem parameters
4791        puts result F^2 in each ref[5] in refList
4792        returns:
4793            delt-F*rcov*delt-F/sum(Fo^2)
4794        '''   
4795           
4796        global tsum,nsum
4797        t0 = time.time()
4798        parmDict.update(dict(zip(varyList,values)))             #update parameter tables
4799        Xdata = GetAtomX(RBdata,parmDict)                       #get new atom coords from RB
4800        allX = getAllX(Xdata,SGM,SGT)                           #fill unit cell - dups. OK
4801        MDval = parmDict['0:MDval']                             #get March-Dollase coeff
4802        HX2pi = 2.*np.pi*np.inner(allX,refList[:3].T)           #form 2piHX for every H,X pair
4803        Aterm = refList[3]*np.sum(allFF*np.cos(HX2pi),axis=0)**2    #compute real part for all H
4804        refList[5] = Aterm
4805        if not ifInv:
4806            refList[5] += refList[3]*np.sum(allFF*np.sin(HX2pi),axis=0)**2    #imaginary part for all H
4807        if len(cosTable):        #apply MD correction
4808            refList[5] *= np.sum(np.sqrt((MDval/(cosTable*(MDval**3-1.)+1.))**3),axis=1)/cosTable.shape[1]
4809        sumFcsq = np.sum(refList[5])
4810        scale = parmDict['sumFosq']/sumFcsq
4811        refList[5] *= scale
4812        refList[6] = refList[4]-refList[5]
4813        M = np.inner(refList[6],np.inner(rcov,refList[6]))
4814        tsum += (time.time()-t0)
4815        nsum += 1
4816        return np.sqrt(M/np.sum(refList[4]**2))
4817   
4818    def MCSAcallback(x, f,accept):
4819        return not pgbar.Update(min(100.,f*100),
4820            newmsg='%s%8.4f%s'%('MC/SA Residual:',f*100,'%'))[0]
4821
4822    sq2pi = np.sqrt(2*np.pi)
4823    sq4pi = np.sqrt(4*np.pi)
4824    generalData = data['General']
4825    Amat,Bmat = G2lat.cell2AB(generalData['Cell'][1:7])
4826    Gmat,gmat = G2lat.cell2Gmat(generalData['Cell'][1:7])
4827    SGData = generalData['SGData']
4828    SGM = np.array([SGData['SGOps'][i][0] for i in range(len(SGData['SGOps']))])
4829    SGMT = np.array([SGData['SGOps'][i][0].T for i in range(len(SGData['SGOps']))])
4830    SGT = np.array([SGData['SGOps'][i][1] for i in range(len(SGData['SGOps']))])
4831    fixAtoms = data['Atoms']                       #if any
4832    cx,ct,cs = generalData['AtomPtrs'][:3]
4833    aTypes = set([])
4834    parmDict = {'Bmat':Bmat,'Gmat':Gmat}
4835    varyList = []
4836    atNo = 0
4837    for atm in fixAtoms:
4838        pfx = ':'+str(atNo)+':'
4839        parmDict[pfx+'Atype'] = atm[ct]
4840        aTypes |= set([atm[ct],])
4841        pstr = ['Ax','Ay','Az']
4842        parmDict[pfx+'Amul'] = atm[cs+1]
4843        for i in range(3):
4844            name = pfx+pstr[i]
4845            parmDict[name] = atm[cx+i]
4846        atNo += 1
4847    parmDict['nfixAt'] = len(fixAtoms)       
4848    MCSA = generalData['MCSA controls']
4849    reflName = MCSA['Data source']
4850    MCSAObjs = data['MCSA']['Models']               #list of MCSA models
4851    upper = []
4852    lower = []
4853    MDvec = np.zeros(3)
4854    for i,item in enumerate(MCSAObjs):
4855        mfx = str(i)+':'
4856        parmDict[mfx+'Type'] = item['Type']
4857        if item['Type'] == 'MD':
4858            getMDparms(item,mfx,parmDict,varyList)
4859            MDvec = np.array(item['axis'])
4860        elif item['Type'] == 'Atom':
4861            getAtomparms(item,mfx,aTypes,SGData,parmDict,varyList)
4862            parmDict[mfx+'atNo'] = atNo
4863            atNo += 1
4864        elif item['Type'] in ['Residue','Vector']:
4865            atNo = getRBparms(item,mfx,aTypes,RBdata,SGData,atNo,parmDict,varyList)
4866    parmDict['atNo'] = atNo                 #total no. of atoms
4867    parmDict['nObj'] = len(MCSAObjs)
4868    aTypes = list(aTypes)
4869    Tdata = GetAtomT(RBdata,parmDict)
4870    Xdata = GetAtomX(RBdata,parmDict)
4871    Mdata = GetAtomM(Xdata,SGData)
4872    allT,allM = getAllTX(Tdata,Mdata,Xdata,SGM,SGT)[:2]
4873    FFtables = G2el.GetFFtable(aTypes)
4874    refs = []
4875    allFF = []
4876    cosTable = []
4877    sumFosq = 0
4878    if 'PWDR' in reflName:
4879        for ref in reflData:
4880            h,k,l,m,d,pos,sig,gam,f = ref[:9]
4881            if d >= MCSA['dmin']:
4882                sig = np.sqrt(sig)      #var -> sig in centideg
4883                sig = .01*G2pwd.getgamFW(gam,sig)        #sig,gam -> FWHM in deg
4884                SQ = 0.25/d**2
4885                allFF.append(allM*[G2el.getFFvalues(FFtables,SQ,True)[i] for i in allT]/np.max(allM))
4886                refs.append([h,k,l,m,f*m,pos,sig])
4887                sumFosq += f*m
4888                Heqv = np.inner(np.array([h,k,l]),SGMT)
4889                cosTable.append(G2lat.CosAngle(Heqv,MDvec,Gmat))
4890        nRef = len(refs)
4891        cosTable = np.array(cosTable)**2
4892        rcov = np.zeros((nRef,nRef))
4893        for iref,refI in enumerate(refs):
4894            rcov[iref][iref] = 1./(sq4pi*refI[6])
4895            for jref,refJ in enumerate(refs[:iref]):
4896                t1 = refI[6]**2+refJ[6]**2
4897                t2 = (refJ[5]-refI[5])**2/(2.*t1)
4898                if t2 > 10.:
4899                    rcov[iref][jref] = 0.
4900                else:
4901                    rcov[iref][jref] = 1./(sq2pi*np.sqrt(t1)*np.exp(t2))
4902        rcov += (rcov.T-np.diagflat(np.diagonal(rcov)))
4903        Rdiag = np.sqrt(np.diag(rcov))
4904        Rnorm = np.outer(Rdiag,Rdiag)
4905        rcov /= Rnorm
4906    elif 'Pawley' in reflName:  #need a bail out if Pawley cov matrix doesn't exist.
4907        vNames = []
4908        pfx = str(data['pId'])+'::PWLref:'
4909        for iref,refI in enumerate(reflData):           #Pawley reflection set
4910            h,k,l,m,d,v,f,s = refI
4911            if d >= MCSA['dmin'] and v:       #skip unrefined ones
4912                vNames.append(pfx+str(iref))
4913                SQ = 0.25/d**2
4914                allFF.append(allM*[G2el.getFFvalues(FFtables,SQ,True)[i] for i in allT]/np.max(allM))
4915                refs.append([h,k,l,m,f*m,iref,0.])
4916                sumFosq += f*m
4917                Heqv = np.inner(np.array([h,k,l]),SGMT)
4918                cosTable.append(G2lat.CosAngle(Heqv,MDvec,Gmat))
4919        cosTable = np.array(cosTable)**2
4920        nRef = len(refs)
4921#        if generalData['doPawley'] and (covData['freshCOV'] or  MCSA['newDmin']):
4922        if covData['freshCOV'] or  MCSA['newDmin']:
4923            vList = covData['varyList']
4924            covMatrix = covData['covMatrix']
4925            rcov = getVCov(vNames,vList,covMatrix)
4926            rcov += (rcov.T-np.diagflat(np.diagonal(rcov)))
4927            Rdiag = np.sqrt(np.diag(rcov))
4928            Rdiag = np.where(Rdiag,Rdiag,1.0)
4929            Rnorm = np.outer(Rdiag,Rdiag)
4930            rcov /= Rnorm
4931            MCSA['rcov'] = rcov
4932            covData['freshCOV'] = False
4933            MCSA['newDmin'] = False
4934        else:
4935            rcov = MCSA['rcov']
4936    elif 'HKLF' in reflName:
4937        for ref in reflData:
4938            [h,k,l,m,d],f = ref[:5],ref[6]
4939            if d >= MCSA['dmin']:
4940                SQ = 0.25/d**2
4941                allFF.append(allM*[G2el.getFFvalues(FFtables,SQ,True)[i] for i in allT]/np.max(allM))
4942                refs.append([h,k,l,m,f*m,0.,0.])
4943                sumFosq += f*m
4944        nRef = len(refs)
4945        rcov = np.identity(len(refs))
4946    allFF = np.array(allFF).T
4947    refs = np.array(refs).T
4948    if start:
4949        G2fil.G2Print (' Minimum d-spacing used: %.2f No. reflections used: %d'%(MCSA['dmin'],nRef))
4950        G2fil.G2Print (' Number of parameters varied: %d'%(len(varyList)))
4951        start = False
4952    parmDict['sumFosq'] = sumFosq
4953    x0 = [parmDict[val] for val in varyList]
4954    ifInv = SGData['SGInv']
4955    bounds = np.array(list(zip(lower,upper)))
4956    if MCSA['Algorithm'] == 'Basin Hopping':
4957#        import basinhopping as bs
4958        take_step = RandomDisplacementBounds(np.array(lower), np.array(upper))
4959        results = so.basinhopping(mcsaCalc,x0,take_step=take_step,disp=True,T=MCSA['Annealing'][0],
4960                interval=MCSA['Annealing'][2]/10,niter=MCSA['Annealing'][2],minimizer_kwargs={'method':'L-BFGS-B','bounds':bounds,
4961                'args':(refs,rcov,cosTable,ifInv,allFF,RBdata,varyList,parmDict)},callback=MCSAcallback)
4962    else:
4963        T0 = MCSA['Annealing'][0]
4964        if not T0:
4965            T0 = None
4966        results = anneal(mcsaCalc,x0,args=(refs,rcov,cosTable,ifInv,allFF,RBdata,varyList,parmDict),
4967            schedule=MCSA['Algorithm'], dwell=MCSA['Annealing'][2],maxiter=10000,
4968            T0=T0, Tf=MCSA['Annealing'][1],
4969            quench=MCSA['fast parms'][0], c=MCSA['fast parms'][1], 
4970            lower=lower, upper=upper, slope=MCSA['log slope'],ranStart=MCSA.get('ranStart',False),
4971            ranRange=MCSA.get('ranRange',10.)/100.,autoRan=MCSA.get('autoRan',False),dlg=pgbar)
4972        G2fil.G2Print (' Acceptance rate: %.2f%% MCSA residual: %.2f%%'%(100.*results[5]/results[3],100.*results[1]))
4973        results = so.minimize(mcsaCalc,results[0],method='L-BFGS-B',args=(refs,rcov,cosTable,ifInv,allFF,RBdata,varyList,parmDict),
4974            bounds=bounds,)
4975    mcsaCalc(results['x'],refs,rcov,cosTable,ifInv,allFF,RBdata,varyList,parmDict)
4976    Result = [False,False,results['fun'],0.0,]+list(results['x'])
4977    Result.append(varyList)
4978    return Result,tsum,nsum,rcov
4979
4980       
4981################################################################################
4982##### Quaternion stuff
4983################################################################################
4984
4985def prodQQ(QA,QB):
4986    ''' Grassman quaternion product
4987        QA,QB quaternions; q=r+ai+bj+ck
4988    '''
4989    D = np.zeros(4)
4990    D[0] = QA[0]*QB[0]-QA[1]*QB[1]-QA[2]*QB[2]-QA[3]*QB[3]
4991    D[1] = QA[0]*QB[1]+QA[1]*QB[0]+QA[2]*QB[3]-QA[3]*QB[2]
4992    D[2] = QA[0]*QB[2]-QA[1]*QB[3]+QA[2]*QB[0]+QA[3]*QB[1]
4993    D[3] = QA[0]*QB[3]+QA[1]*QB[2]-QA[2]*QB[1]+QA[3]*QB[0]
4994   
4995#    D[0] = QA[0]*QB[0]-np.dot(QA[1:],QB[1:])
4996#    D[1:] = QA[0]*QB[1:]+QB[0]*QA[1:]+np.cross(QA[1:],QB[1:])
4997   
4998    return D
4999   
5000def normQ(QA):
5001    ''' get length of quaternion & normalize it
5002        q=r+ai+bj+ck
5003    '''
5004    n = np.sqrt(np.sum(np.array(QA)**2))
5005    return QA/n
5006   
5007def invQ(Q):
5008    '''
5009        get inverse of quaternion
5010        q=r+ai+bj+ck; q* = r-ai-bj-ck
5011    '''
5012    return Q*np.array([1,-1,-1,-1])
5013   
5014def prodQVQ(Q,V):
5015    """
5016    compute the quaternion vector rotation qvq-1 = v'
5017    q=r+ai+bj+ck
5018    """
5019    T2 = Q[0]*Q[1]
5020    T3 = Q[0]*Q[2]
5021    T4 = Q[0]*Q[3]
5022    T5 = -Q[1]*Q[1]
5023    T6 = Q[1]*Q[2]
5024    T7 = Q[1]*Q[3]
5025    T8 = -Q[2]*Q[2]
5026    T9 = Q[2]*Q[3]
5027    T10 = -Q[3]*Q[3]
5028    M = np.array([[T8+T10,T6-T4,T3+T7],[T4+T6,T5+T10,T9-T2],[T7-T3,T2+T9,T5+T8]])
5029    VP = 2.*np.inner(V,M)
5030    return VP+V
5031   
5032def Q2Mat(Q):
5033    ''' make rotation matrix from quaternion
5034        q=r+ai+bj+ck
5035    '''
5036    QN = normQ(Q)
5037    aa = QN[0]**2
5038    ab = QN[0]*QN[1]
5039    ac = QN[0]*QN[2]
5040    ad = QN[0]*QN[3]
5041    bb = QN[1]**2
5042    bc = QN[1]*QN[2]
5043    bd = QN[1]*QN[3]
5044    cc = QN[2]**2
5045    cd = QN[2]*QN[3]
5046    dd = QN[3]**2
5047    M = [[aa+bb-cc-dd, 2.*(bc-ad),  2.*(ac+bd)],
5048        [2*(ad+bc),   aa-bb+cc-dd,  2.*(cd-ab)],
5049        [2*(bd-ac),    2.*(ab+cd), aa-bb-cc+dd]]
5050    return np.array(M)
5051   
5052def AV2Q(A,V):
5053    ''' convert angle (radians) & vector to quaternion
5054        q=r+ai+bj+ck
5055    '''
5056    Q = np.zeros(4)
5057    d = nl.norm(np.array(V))
5058    if d:
5059        V = V/d
5060        if not A:       #==0.
5061            A = 2.*np.pi
5062        p = A/2.
5063        Q[0] = np.cos(p)
5064        Q[1:4] = V*np.sin(p)
5065    else:
5066        Q[3] = 1.
5067    return Q
5068   
5069def AVdeg2Q(A,V):
5070    ''' convert angle (degrees) & vector to quaternion
5071        q=r+ai+bj+ck
5072    '''
5073    Q = np.zeros(4)
5074    d = nl.norm(np.array(V))
5075    if not A:       #== 0.!
5076        A = 360.
5077    if d:
5078        V = V/d
5079        p = A/2.
5080        Q[0] = cosd(p)
5081        Q[1:4] = V*sind(p)
5082    else:
5083        Q[3] = 1.
5084    return Q
5085   
5086def Q2AVdeg(Q):
5087    ''' convert quaternion to angle (degrees 0-360) & normalized vector
5088        q=r+ai+bj+ck
5089    '''
5090    A = 2.*acosd(Q[0])
5091    V = np.array(Q[1:])
5092    V = V/sind(A/2.)
5093    return A,V
5094   
5095def Q2AV(Q):
5096    ''' convert quaternion to angle (radians 0-2pi) & normalized vector
5097        q=r+ai+bj+ck
5098    '''
5099    A = 2.*np.arccos(Q[0])
5100    V = np.array(Q[1:])
5101    V = V/np.sin(A/2.)
5102    return A,V
5103   
5104def randomQ(r0,r1,r2,r3):
5105    ''' create random quaternion from 4 random numbers in range (-1,1)
5106    '''
5107    sum = 0
5108    Q = np.array(4)
5109    Q[0] = r0
5110    sum += Q[0]**2
5111    Q[1] = np.sqrt(1.-sum)*r1
5112    sum += Q[1]**2
5113    Q[2] = np.sqrt(1.-sum)*r2
5114    sum += Q[2]**2
5115    Q[3] = np.sqrt(1.-sum)*np.where(r3<0.,-1.,1.)
5116    return Q
5117   
5118def randomAVdeg(r0,r1,r2,r3):
5119    ''' create random angle (deg),vector from 4 random number in range (-1,1)
5120    '''
5121    return Q2AVdeg(randomQ(r0,r1,r2,r3))
5122   
5123def makeQuat(A,B,C):
5124    ''' Make quaternion from rotation of A vector to B vector about C axis
5125
5126        :param np.array A,B,C: Cartesian 3-vectors
5127        :returns: quaternion & rotation angle in radians q=r+ai+bj+ck
5128    '''
5129
5130    V1 = np.cross(A,C)
5131    V2 = np.cross(B,C)
5132    if nl.norm(V1)*nl.norm(V2):
5133        V1 = V1/nl.norm(V1)
5134        V2 = V2/nl.norm(V2)
5135        V3 = np.cross(V1,V2)
5136    else:
5137        V3 = np.zeros(3)
5138    Q = np.array([0.,0.,0.,1.])
5139    D = 0.
5140    if nl.norm(V3):
5141        V3 = V3/nl.norm(V3)
5142        D1 = min(1.0,max(-1.0,np.vdot(V1,V2)))
5143        D = np.arccos(D1)/2.0
5144        V1 = C-V3
5145        V2 = C+V3
5146        DM = nl.norm(V1)
5147        DP = nl.norm(V2)
5148        S = np.sin(D)
5149        Q[0] = np.cos(D)
5150        Q[1:] = V3*S
5151        D *= 2.
5152        if DM > DP:
5153            D *= -1.
5154    return Q,D
5155   
5156def annealtests():
5157    from numpy import cos
5158    # minimum expected at ~-0.195
5159    func = lambda x: cos(14.5*x-0.3) + (x+0.2)*x
5160    print (anneal(func,1.0,full_output=1,upper=3.0,lower=-3.0,feps=1e-4,maxiter=2000,schedule='cauchy'))
5161    print (anneal(func,1.0,full_output=1,upper=3.0,lower=-3.0,feps=1e-4,maxiter=2000,schedule='fast'))
5162    print (anneal(func,1.0,full_output=1,upper=3.0,lower=-3.0,feps=1e-4,maxiter=2000,schedule='boltzmann'))
5163
5164    # minimum expected at ~[-0.195, -0.1]
5165    func = lambda x: cos(14.5*x[0]-0.3) + (x[1]+0.2)*x[1] + (x[0]+0.2)*x[0]
5166    print (anneal(func,[1.0, 1.0],full_output=1,upper=[3.0, 3.0],lower=[-3.0, -3.0],feps=1e-4,maxiter=2000,schedule='cauchy'))
5167    print (anneal(func,[1.0, 1.0],full_output=1,upper=[3.0, 3.0],lower=[-3.0, -3.0],feps=1e-4,maxiter=2000,schedule='fast'))
5168    print (anneal(func,[1.0, 1.0],full_output=1,upper=[3.0, 3.0],lower=[-3.0, -3.0],feps=1e-4,maxiter=2000,schedule='boltzmann'))
5169
5170if __name__ == '__main__':
5171    annealtests()
Note: See TracBrowser for help on using the repository browser.