source: trunk/GSASIImath.py @ 4073

Last change on this file since 4073 was 4073, checked in by vondreele, 4 years ago

minor typos & allow import PDB to read no unit cell dummy PDBs from SHAPES/dammin, etc.

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