source: trunk/GSASIImath.py @ 1987

Last change on this file since 1987 was 1987, checked in by vondreele, 7 years ago

set up wave derivatives

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