source: trunk/GSASIImath.py @ 3867

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

modulated mag moments now ok; not sf calc

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