source: trunk/GSASIImath.py @ 2853

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

remove a 'exposed' test print statement
add a print of SVD zeros found for each change in lambda in LS
change orientation for rigid body display to inv(quaternion)

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