source: trunk/GSASIImath.py @ 2854

Last change on this file since 2854 was 2854, checked in by vondreele, 6 years ago

implement Hessian SVD refinement option (no Levenberg-Marquardt). Works for final refinements but not good for initial refinements (far from minimum). Warning in Controls status bar

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