source: trunk/GSASIImath.py @ 4057

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

tweaks of the mag. str. fctr. code

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