source: trunk/GSASIImath.py @ 1988

Last change on this file since 1988 was 1988, checked in by vondreele, 8 years ago

fix atom id problem in AddRestraints?
refactor Modulation & ModulationDerv? to remove expModInt
fix AtomRefine?
fix Uij derivative in SS problems

  • Property svn:eol-style set to native
  • Property svn:keywords set to Date Author Revision URL Id
File size: 147.8 KB
Line 
1# -*- coding: utf-8 -*-
2#GSASIImath - major mathematics routines
3########### SVN repository information ###################
4# $Date: 2015-10-06 19:53:02 +0000 (Tue, 06 Oct 2015) $
5# $Author: vondreele $
6# $Revision: 1988 $
7# $URL: trunk/GSASIImath.py $
8# $Id: GSASIImath.py 1988 2015-10-06 19:53:02Z 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: 1988 $")
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,H,FSSdata,XSSdata,USSdata,Mast):
929    '''
930    H: array ops X hklt
931    Ax: array atoms X waves X xyz
932    Bx: array atoms X waves X xyz
933    '''
934   
935    nxs = np.newaxis
936    glTau,glWt = pwd.pygauleg(0.,1.,32)         #get Gauss-Legendre intervals & weights
937    Ax = np.array(XSSdata[:3]).T   #atoms x waves x sin pos mods
938    Bx = np.array(XSSdata[3:]).T   #...cos pos mods
939    Af = np.array(FSSdata[0]).T    #sin frac mods x waves x atoms
940    Bf = np.array(FSSdata[1]).T    #cos frac mods...
941    Au = Mast*np.array(G2lat.U6toUij(USSdata[:6])).T   #atoms x waves x sin Uij mods
942    Bu = Mast*np.array(G2lat.U6toUij(USSdata[6:])).T   #...cos Uij mods
943   
944    if 'Fourier' in waveTypes:
945        nf = 0
946        nx = 0
947        XmodZ = np.zeros((Ax.shape[0],Ax.shape[1],3,32))
948        FmodZ = np.zeros((Af.shape[0],Af.shape[1],32))
949        if 'Crenel' in waveTypes:
950            nC = np.where('Crenel' in waveTypes)
951            nf = 1
952            #FmodZ = 0   replace
953    else:
954        nx = 1
955        if 'Sawtooth' in waveTypes:
956            nS = np.where('Sawtooth' in waveTypes)
957            #XmodZ = 0   replace
958    if Af.shape[1]:
959        tauF = np.arange(1.,Af.shape[1]+1-nf)[:,nxs]*glTau  #Fwaves x 32
960        FmodA = Af[:,nf:,nxs]*np.sin(twopi*tauF[nxs,:,:])   #atoms X Fwaves X 32
961        FmodB = Bf[:,nf:,nxs]*np.cos(twopi*tauF[nxs,:,:])
962        Fmod = np.sum(FmodA+FmodB+FmodC,axis=1)             #atoms X 32; sum waves
963    else:
964        Fmod = 1.0           
965    if Ax.shape[1]:
966        tauX = np.arange(1.,Ax.shape[1]+1-nx)[:,nxs]*glTau  #Xwaves x 32
967        XmodA = Ax[:,nx:,:,nxs]*np.sin(twopi*tauX[nxs,:,nxs,:]) #atoms X waves X pos X 32
968        XmodB = Bx[:,nx:,:,nxs]*np.cos(twopi*tauX[nxs,:,nxs,:]) #ditto
969        Xmod = np.sum(XmodA+XmodB+XmodZ,axis=1)                #atoms X pos X 32; sum waves
970    if Au.shape[1]:
971        tauU = np.arange(1.,Au.shape[1]+1)[:,nxs]*glTau     #Uwaves x 32
972        UmodA = Au[:,:,:,:,nxs]*np.sin(twopi*tauU[nxs,:,nxs,nxs,:]) #atoms x waves x 3x3 x 32
973        UmodB = Bu[:,:,:,:,nxs]*np.cos(twopi*tauU[nxs,:,nxs,nxs,:]) #ditto
974        Umod = np.swapaxes(np.sum(UmodA+UmodB,axis=1),1,3)      #atoms x 3x3 x 32; sum waves
975        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.?
976    else:
977        HbH = 1.0
978    D = H[:,:,3][:,:,nxs]*glTau[nxs,nxs,:]              #m*tau;refBlk x ops X 32
979    HdotX = np.inner(H[:,:,:3],np.swapaxes(Xmod,1,2))+D[:,:,nxs,:]     #refBlk X ops x atoms X 32
980    sinHA = np.sum(Fmod*HbH*np.sin(twopi*HdotX)*glWt,axis=-1)       #refBlk X ops x atoms; sum for G-L integration
981    cosHA = np.sum(Fmod*HbH*np.cos(twopi*HdotX)*glWt,axis=-1)       #ditto
982#        GSASIIpath.IPyBreak()                 
983
984    return np.array([cosHA,sinHA])             # 2 x refBlk x SGops x atoms
985   
986def ModulationDerv(waveTypes,H,FSSdata,XSSdata,USSdata,Mast):
987    '''
988    H: array ops X hklt
989    Ax: array atoms X waves X xyz
990    Bx: array atoms X waves X xyz
991    '''
992   
993    nxs = np.newaxis
994    glTau,glWt = pwd.pygauleg(0.,1.,32)         #get Gauss-Legendre intervals & weights
995    Ax = np.array(XSSdata[:3]).T   #atoms x waves x sin pos mods
996    dGdAx = np.zeros_like(Ax)
997    Bx = np.array(XSSdata[3:]).T   #...cos pos mods
998    dGdBx = np.zeros_like(Bx)
999    Af = np.array(FSSdata[0]).T    #sin frac mods x waves x atoms
1000    dGdAf = np.zeros_like(Af)
1001    Bf = np.array(FSSdata[1]).T    #cos frac mods...
1002    dGdBf = np.zeros_like(Bf)
1003    Au = Mast*np.array(G2lat.U6toUij(USSdata[:6])).T   #atoms x waves x sin Uij mods
1004    dGdAu = np.zeros_like(Au)
1005    Bu = Mast*np.array(G2lat.U6toUij(USSdata[6:])).T   #...cos Uij mods
1006    dGdBu = np.zeros_like(Bu)
1007   
1008    if 'Fourier' in waveTypes:
1009        nf = 0
1010        nx = 0
1011        XmodZ = np.zeros((Ax.shape[0],Ax.shape[1],3,32))
1012        FmodZ = np.zeros((Af.shape[0],Af.shape[1],32))
1013        if 'Crenel' in waveTypes:
1014            nC = np.where('Crenel' in waveTypes)
1015            nf = 1
1016            #FmodZ = 0   replace
1017    else:
1018        nx = 1
1019        if 'Sawtooth' in waveTypes:
1020            nS = np.where('Sawtooth' in waveTypes)
1021            #XmodZ = 0   replace
1022    if Af.shape[1]:
1023        tauF = np.arange(1.,Af.shape[1]+1-nf)[:,nxs]*glTau  #Fwaves x 32
1024        StauF = np.ones_like(Af)[:,nf:,nxs]*np.sin(twopi*tauF)[nxs,:,:] #also dFmodA/dAf
1025        CtauF = np.ones_like(Bf)[:,nf:,nxs]*np.cos(twopi*tauF)[nxs,:,:] #also dFmodB/dBf
1026        FmodA = Af[:,nf:,nxs]*StauF   #atoms X Fwaves X 32
1027        FmodB = Bf[:,nf:,nxs]*CtauF
1028        Fmod = np.sum(FmodA+FmodB+FmodC,axis=1)             #atoms X 32; sum waves
1029    else:
1030        Fmod = 1.0           
1031    if Ax.shape[1]:
1032        tauX = np.arange(1.,Ax.shape[1]+1-nx)[:,nxs]*glTau  #Xwaves x 32
1033        StauX = np.ones_like(Ax)[:,nx:,:,nxs]*np.sin(twopi*tauX)[nxs,:,nxs,:]   #also dXmodA/dAx
1034        CtauX = np.ones_like(Bx)[:,nx:,:,nxs]*np.cos(twopi*tauX)[nxs,:,nxs,:]   #also dXmodB/dBx
1035        XmodA = Ax[:,nx:,:,nxs]*StauX #atoms X waves X pos X 32
1036        XmodB = Bx[:,nx:,:,nxs]*CtauX #ditto
1037        Xmod = np.sum(XmodA+XmodB+XmodZ,axis=1)                #atoms X pos X 32; sum waves
1038    if Au.shape[1]:
1039        tauU = np.arange(1.,Au.shape[1]+1)[:,nxs]*glTau     #Uwaves x 32
1040        StauU = np.ones_like(Au)[:,:,:,:,nxs]*np.sin(twopi*tauU)[nxs,:,nxs,nxs,:]   #also dUmodA/dAu
1041        CtauU = np.ones_like(Bu)[:,:,:,:,nxs]*np.cos(twopi*tauU)[nxs,:,nxs,nxs,:]   #also dUmodB/dBu
1042        UmodA = Au[:,:,:,:,nxs]*StauU #atoms x waves x 3x3 x 32
1043        UmodB = Bu[:,:,:,:,nxs]*CtauU #ditto
1044        Umod = np.swapaxes(np.sum(UmodA+UmodB,axis=1),1,3)      #atoms x 3x3 x 32; sum waves
1045        HbH = np.exp(-np.sum(H[:,nxs,nxs,:3]*np.inner(H[:,:3],Umod),axis=-1)) # ops x atoms x 32 add Overhauser corr.?
1046    else:
1047        HbH = 1.0
1048    D = H[:,3][:,nxs]*glTau[nxs,:]              #m*tau; ops X 32
1049    HdotX = np.inner(H[:,:3],np.swapaxes(Xmod,1,2))+D[:,nxs,:]     #ops x atoms X 32
1050    sinHA = np.sum(Fmod*HbH*np.sin(twopi*HdotX)*glWt,axis=-1)       #ops x atoms; sum for G-L integration
1051    cosHA = np.sum(Fmod*HbH*np.cos(twopi*HdotX)*glWt,axis=-1)       #ditto
1052#   GSASIIpath.IPyBreak()                 
1053    return np.array([cosHA,sinHA]),dGdAf,dGdBf,dGdAx,dGdBx,dGdAu,dGdBu      #ops X atoms
1054   
1055def posFourier(tau,psin,pcos,smul):
1056    A = np.array([ps[:,np.newaxis]*np.sin(2*np.pi*(i+1)*tau) for i,ps in enumerate(psin)])*smul
1057    B = np.array([pc[:,np.newaxis]*np.cos(2*np.pi*(i+1)*tau) for i,pc in enumerate(pcos)])
1058    return np.sum(A,axis=0)+np.sum(B,axis=0)
1059   
1060def posSawtooth(tau,Toff,slopes):
1061    Tau = (tau-Toff)%1.
1062    A = slopes[:,np.newaxis]*Tau
1063    return A
1064
1065def posZigZag(tau,Toff,slopes):
1066    Tau = (tau-Toff)%1.
1067    A = np.where(Tau <= 0.5,slopes[:,np.newaxis]*Tau,slopes[:,np.newaxis]*(1.-Tau))
1068    return A
1069   
1070def fracCrenel(tau,Toff,Twid):
1071    Tau = (tau-Toff)%1.
1072    A = np.where(Tau<Twid,1.,0.)
1073    return A
1074   
1075def fracFourier(tau,fsin,fcos):
1076    A = np.array([fs[:,np.newaxis]*np.sin(2.*np.pi*(i+1)*tau) for i,fs in enumerate(fsin)])
1077    B = np.array([fc[:,np.newaxis]*np.cos(2.*np.pi*(i+1)*tau) for i,fc in enumerate(fcos)])
1078    return np.sum(A,axis=0)+np.sum(B,axis=0)
1079   
1080def ApplyModulation(data,tau):
1081    '''Applies modulation to drawing atom positions & Uijs for given tau
1082    '''
1083    generalData = data['General']
1084    SGData = generalData['SGData']
1085    SSGData = generalData['SSGData']
1086    cx,ct,cs,cia = generalData['AtomPtrs']
1087    drawingData = data['Drawing']
1088    dcx,dct,dcs,dci = drawingData['atomPtrs']
1089    atoms = data['Atoms']
1090    drawAtoms = drawingData['Atoms']
1091    Fade = np.zeros(len(drawAtoms))
1092    for atom in atoms:   
1093        atxyz = G2spc.MoveToUnitCell(np.array(atom[cx:cx+3]))[0]
1094        atuij = np.array(atom[cia+2:cia+8])
1095        waveType = atom[-1]['SS1']['waveType']
1096        Sfrac = atom[-1]['SS1']['Sfrac']
1097        Spos = atom[-1]['SS1']['Spos']
1098        Sadp = atom[-1]['SS1']['Sadp']
1099        indx = FindAtomIndexByIDs(drawAtoms,dci,[atom[cia+8],],True)
1100        for ind in indx:
1101            drawatom = drawAtoms[ind]
1102            opr = drawatom[dcs-1]
1103            sop,ssop,icent = G2spc.OpsfromStringOps(opr,SGData,SSGData)
1104            sdet,ssdet,dtau,dT,tauT = G2spc.getTauT(tau,sop,ssop,atxyz)
1105            smul = sdet         # n-fold vs m operator on wave
1106            tauT *= icent       #invert wave on -1
1107            wave = np.zeros(3)
1108            uwave = np.zeros(6)
1109            #how do I handle Sfrac? - fade the atoms?
1110            if len(Sfrac):
1111                scof = []
1112                ccof = []
1113                for i,sfrac in enumerate(Sfrac):
1114                    if not i and 'Crenel' in waveType:
1115                        Fade[ind] += fracCrenel(tauT,sfrac[0][0],sfrac[0][1])
1116                    else:
1117                        scof.append(sfrac[0][0])
1118                        ccof.append(sfrac[0][1])
1119                    if len(scof):
1120                        Fade[ind] += np.sum(fracFourier(tauT,scof,ccof))                           
1121            if len(Spos):
1122                scof = []
1123                ccof = []
1124                for i,spos in enumerate(Spos):
1125                    if waveType in ['Sawtooth','ZigZag'] and not i:
1126                        Toff = spos[0][0]
1127                        slopes = np.array(spos[0][1:])
1128                        if waveType == 'Sawtooth':
1129                            wave = posSawtooth(tauT,Toff,slopes)
1130                        elif waveType == 'ZigZag':
1131                            wave = posZigZag(tauT,Toff,slopes)
1132                    else:
1133                        scof.append(spos[0][:3])
1134                        ccof.append(spos[0][3:])
1135                wave += np.sum(posFourier(tauT,np.array(scof),np.array(ccof),smul),axis=1)
1136            if len(Sadp):
1137                scof = []
1138                ccof = []
1139                for i,sadp in enumerate(Sadp):
1140                    scof.append(sadp[0][:6])
1141                    ccof.append(sadp[0][6:])
1142                uwave += np.sum(posFourier(tauT,np.array(scof),np.array(ccof),smul),axis=1)
1143            if atom[cia] == 'A':                   
1144                X,U = G2spc.ApplyStringOps(opr,SGData,atxyz+wave,atuij+uwave)
1145                drawatom[dcx:dcx+3] = X
1146                drawatom[dci-6:dci] = U
1147            else:
1148                X = G2spc.ApplyStringOps(opr,SGData,atxyz+wave)
1149                drawatom[dcx:dcx+3] = X
1150    return drawAtoms,Fade
1151   
1152# gauleg.py Gauss Legendre numerical quadrature, x and w computation
1153# integrate from a to b using n evaluations of the function f(x) 
1154# usage: from gauleg import gaulegf         
1155#        x,w = gaulegf( a, b, n)                               
1156#        area = 0.0                                           
1157#        for i in range(1,n+1):          #  yes, 1..n                   
1158#          area += w[i]*f(x[i])                                   
1159
1160import math
1161def gaulegf(a, b, n):
1162  x = range(n+1) # x[0] unused
1163  w = range(n+1) # w[0] unused
1164  eps = 3.0E-14
1165  m = (n+1)/2
1166  xm = 0.5*(b+a)
1167  xl = 0.5*(b-a)
1168  for i in range(1,m+1):
1169    z = math.cos(3.141592654*(i-0.25)/(n+0.5))
1170    while True:
1171      p1 = 1.0
1172      p2 = 0.0
1173      for j in range(1,n+1):
1174        p3 = p2
1175        p2 = p1
1176        p1 = ((2.0*j-1.0)*z*p2-(j-1.0)*p3)/j
1177
1178      pp = n*(z*p1-p2)/(z*z-1.0)
1179      z1 = z
1180      z = z1 - p1/pp
1181      if abs(z-z1) <= eps:
1182            break
1183
1184    x[i] = xm - xl*z
1185    x[n+1-i] = xm + xl*z
1186    w[i] = 2.0*xl/((1.0-z*z)*pp*pp)
1187    w[n+1-i] = w[i]
1188  return np.array(x), np.array(w)
1189# end gaulegf
1190   
1191   
1192def BessJn(nmax,x):
1193    ''' compute Bessel function J(n,x) from scipy routine & recurrance relation
1194    returns sequence of J(n,x) for n in range [-nmax...0...nmax]
1195   
1196    :param  integer nmax: maximul order for Jn(x)
1197    :param  float x: argument for Jn(x)
1198   
1199    :returns numpy array: [J(-nmax,x)...J(0,x)...J(nmax,x)]
1200   
1201    '''
1202    import scipy.special as sp
1203    bessJn = np.zeros(2*nmax+1)
1204    bessJn[nmax] = sp.j0(x)
1205    bessJn[nmax+1] = sp.j1(x)
1206    bessJn[nmax-1] = -bessJn[nmax+1]
1207    for i in range(2,nmax+1):
1208        bessJn[i+nmax] = 2*(i-1)*bessJn[nmax+i-1]/x-bessJn[nmax+i-2]
1209        bessJn[nmax-i] = bessJn[i+nmax]*(-1)**i
1210    return bessJn
1211   
1212def BessIn(nmax,x):
1213    ''' compute modified Bessel function I(n,x) from scipy routines & recurrance relation
1214    returns sequence of I(n,x) for n in range [-nmax...0...nmax]
1215   
1216    :param  integer nmax: maximul order for In(x)
1217    :param  float x: argument for In(x)
1218   
1219    :returns numpy array: [I(-nmax,x)...I(0,x)...I(nmax,x)]
1220   
1221    '''
1222    import scipy.special as sp
1223    bessIn = np.zeros(2*nmax+1)
1224    bessIn[nmax] = sp.i0(x)
1225    bessIn[nmax+1] = sp.i1(x)
1226    bessIn[nmax-1] = bessIn[nmax+1]
1227    for i in range(2,nmax+1):
1228        bessIn[i+nmax] = bessIn[nmax+i-2]-2*(i-1)*bessIn[nmax+i-1]/x
1229        bessIn[nmax-i] = bessIn[i+nmax]
1230    return bessIn
1231       
1232   
1233################################################################################
1234##### distance, angle, planes, torsion stuff
1235################################################################################
1236
1237def getSyXYZ(XYZ,ops,SGData):
1238    '''default doc string
1239   
1240    :param type name: description
1241   
1242    :returns: type name: description
1243   
1244    '''
1245    XYZout = np.zeros_like(XYZ)
1246    for i,[xyz,op] in enumerate(zip(XYZ,ops)):
1247        if op == '1':
1248            XYZout[i] = xyz
1249        else:
1250            oprs = op.split('+')
1251            unit = [0,0,0]
1252            if len(oprs)>1:
1253                unit = np.array(list(eval(oprs[1])))
1254            syop =int(oprs[0])
1255            inv = syop/abs(syop)
1256            syop *= inv
1257            cent = syop/100
1258            syop %= 100
1259            syop -= 1
1260            M,T = SGData['SGOps'][syop]
1261            XYZout[i] = (np.inner(M,xyz)+T)*inv+SGData['SGCen'][cent]+unit
1262    return XYZout
1263   
1264def getRestDist(XYZ,Amat):
1265    '''default doc string
1266   
1267    :param type name: description
1268   
1269    :returns: type name: description
1270   
1271    '''
1272    return np.sqrt(np.sum(np.inner(Amat,(XYZ[1]-XYZ[0]))**2))
1273   
1274def getRestDeriv(Func,XYZ,Amat,ops,SGData):
1275    '''default doc string
1276   
1277    :param type name: description
1278   
1279    :returns: type name: description
1280   
1281    '''
1282    deriv = np.zeros((len(XYZ),3))
1283    dx = 0.00001
1284    for j,xyz in enumerate(XYZ):
1285        for i,x in enumerate(np.array([[dx,0,0],[0,dx,0],[0,0,dx]])):
1286            XYZ[j] -= x
1287            d1 = Func(getSyXYZ(XYZ,ops,SGData),Amat)
1288            XYZ[j] += 2*x
1289            d2 = Func(getSyXYZ(XYZ,ops,SGData),Amat)
1290            XYZ[j] -= x
1291            deriv[j][i] = (d1-d2)/(2*dx)
1292    return deriv.flatten()
1293
1294def getRestAngle(XYZ,Amat):
1295    '''default doc string
1296   
1297    :param type name: description
1298   
1299    :returns: type name: description
1300   
1301    '''
1302   
1303    def calcVec(Ox,Tx,Amat):
1304        return np.inner(Amat,(Tx-Ox))
1305
1306    VecA = calcVec(XYZ[1],XYZ[0],Amat)
1307    VecA /= np.sqrt(np.sum(VecA**2))
1308    VecB = calcVec(XYZ[1],XYZ[2],Amat)
1309    VecB /= np.sqrt(np.sum(VecB**2))
1310    edge = VecB-VecA
1311    edge = np.sum(edge**2)
1312    angle = (2.-edge)/2.
1313    angle = max(angle,-1.)
1314    return acosd(angle)
1315   
1316def getRestPlane(XYZ,Amat):
1317    '''default doc string
1318   
1319    :param type name: description
1320   
1321    :returns: type name: description
1322   
1323    '''
1324    sumXYZ = np.zeros(3)
1325    for xyz in XYZ:
1326        sumXYZ += xyz
1327    sumXYZ /= len(XYZ)
1328    XYZ = np.array(XYZ)-sumXYZ
1329    XYZ = np.inner(Amat,XYZ).T
1330    Zmat = np.zeros((3,3))
1331    for i,xyz in enumerate(XYZ):
1332        Zmat += np.outer(xyz.T,xyz)
1333    Evec,Emat = nl.eig(Zmat)
1334    Evec = np.sqrt(Evec)/(len(XYZ)-3)
1335    Order = np.argsort(Evec)
1336    return Evec[Order[0]]
1337   
1338def getRestChiral(XYZ,Amat):   
1339    '''default doc string
1340   
1341    :param type name: description
1342   
1343    :returns: type name: description
1344   
1345    '''
1346    VecA = np.empty((3,3))   
1347    VecA[0] = np.inner(XYZ[1]-XYZ[0],Amat)
1348    VecA[1] = np.inner(XYZ[2]-XYZ[0],Amat)
1349    VecA[2] = np.inner(XYZ[3]-XYZ[0],Amat)
1350    return nl.det(VecA)
1351   
1352def getRestTorsion(XYZ,Amat):
1353    '''default doc string
1354   
1355    :param type name: description
1356   
1357    :returns: type name: description
1358   
1359    '''
1360    VecA = np.empty((3,3))
1361    VecA[0] = np.inner(XYZ[1]-XYZ[0],Amat)
1362    VecA[1] = np.inner(XYZ[2]-XYZ[1],Amat)
1363    VecA[2] = np.inner(XYZ[3]-XYZ[2],Amat)
1364    D = nl.det(VecA)
1365    Mag = np.sqrt(np.sum(VecA*VecA,axis=1))
1366    P12 = np.sum(VecA[0]*VecA[1])/(Mag[0]*Mag[1])
1367    P13 = np.sum(VecA[0]*VecA[2])/(Mag[0]*Mag[2])
1368    P23 = np.sum(VecA[1]*VecA[2])/(Mag[1]*Mag[2])
1369    Ang = 1.0
1370    if abs(P12) < 1.0 and abs(P23) < 1.0:
1371        Ang = (P12*P23-P13)/(np.sqrt(1.-P12**2)*np.sqrt(1.-P23**2))
1372    TOR = (acosd(Ang)*D/abs(D)+720.)%360.
1373    return TOR
1374   
1375def calcTorsionEnergy(TOR,Coeff=[]):
1376    '''default doc string
1377   
1378    :param type name: description
1379   
1380    :returns: type name: description
1381   
1382    '''
1383    sum = 0.
1384    if len(Coeff):
1385        cof = np.reshape(Coeff,(3,3)).T
1386        delt = TOR-cof[1]
1387        delt = np.where(delt<-180.,delt+360.,delt)
1388        delt = np.where(delt>180.,delt-360.,delt)
1389        term = -cof[2]*delt**2
1390        val = cof[0]*np.exp(term/1000.0)
1391        pMax = cof[0][np.argmin(val)]
1392        Eval = np.sum(val)
1393        sum = Eval-pMax
1394    return sum,Eval
1395
1396def getTorsionDeriv(XYZ,Amat,Coeff):
1397    '''default doc string
1398   
1399    :param type name: description
1400   
1401    :returns: type name: description
1402   
1403    '''
1404    deriv = np.zeros((len(XYZ),3))
1405    dx = 0.00001
1406    for j,xyz in enumerate(XYZ):
1407        for i,x in enumerate(np.array([[dx,0,0],[0,dx,0],[0,0,dx]])):
1408            XYZ[j] -= x
1409            tor = getRestTorsion(XYZ,Amat)
1410            p1,d1 = calcTorsionEnergy(tor,Coeff)
1411            XYZ[j] += 2*x
1412            tor = getRestTorsion(XYZ,Amat)
1413            p2,d2 = calcTorsionEnergy(tor,Coeff)           
1414            XYZ[j] -= x
1415            deriv[j][i] = (p2-p1)/(2*dx)
1416    return deriv.flatten()
1417
1418def getRestRama(XYZ,Amat):
1419    '''Computes a pair of torsion angles in a 5 atom string
1420   
1421    :param nparray XYZ: crystallographic coordinates of 5 atoms
1422    :param nparray Amat: crystal to cartesian transformation matrix
1423   
1424    :returns: list (phi,psi) two torsion angles in degrees
1425   
1426    '''
1427    phi = getRestTorsion(XYZ[:5],Amat)
1428    psi = getRestTorsion(XYZ[1:],Amat)
1429    return phi,psi
1430   
1431def calcRamaEnergy(phi,psi,Coeff=[]):
1432    '''Computes pseudo potential energy from a pair of torsion angles and a
1433    numerical description of the potential energy surface. Used to create
1434    penalty function in LS refinement:     
1435    :math:`Eval(\\phi,\\psi) = C[0]*exp(-V/1000)`
1436
1437    where :math:`V = -C[3] * (\\phi-C[1])^2 - C[4]*(\\psi-C[2])^2 - 2*(\\phi-C[1])*(\\psi-C[2])`
1438   
1439    :param float phi: first torsion angle (:math:`\\phi`)
1440    :param float psi: second torsion angle (:math:`\\psi`)
1441    :param list Coeff: pseudo potential coefficients
1442   
1443    :returns: list (sum,Eval): pseudo-potential difference from minimum & value;
1444      sum is used for penalty function.
1445   
1446    '''
1447    sum = 0.
1448    Eval = 0.
1449    if len(Coeff):
1450        cof = Coeff.T
1451        dPhi = phi-cof[1]
1452        dPhi = np.where(dPhi<-180.,dPhi+360.,dPhi)
1453        dPhi = np.where(dPhi>180.,dPhi-360.,dPhi)
1454        dPsi = psi-cof[2]
1455        dPsi = np.where(dPsi<-180.,dPsi+360.,dPsi)
1456        dPsi = np.where(dPsi>180.,dPsi-360.,dPsi)
1457        val = -cof[3]*dPhi**2-cof[4]*dPsi**2-2.0*cof[5]*dPhi*dPsi
1458        val = cof[0]*np.exp(val/1000.)
1459        pMax = cof[0][np.argmin(val)]
1460        Eval = np.sum(val)
1461        sum = Eval-pMax
1462    return sum,Eval
1463
1464def getRamaDeriv(XYZ,Amat,Coeff):
1465    '''Computes numerical derivatives of torsion angle pair pseudo potential
1466    with respect of crystallographic atom coordinates of the 5 atom sequence
1467   
1468    :param nparray XYZ: crystallographic coordinates of 5 atoms
1469    :param nparray Amat: crystal to cartesian transformation matrix
1470    :param list Coeff: pseudo potential coefficients
1471   
1472    :returns: list (deriv) derivatives of pseudopotential with respect to 5 atom
1473     crystallographic xyz coordinates.
1474   
1475    '''
1476    deriv = np.zeros((len(XYZ),3))
1477    dx = 0.00001
1478    for j,xyz in enumerate(XYZ):
1479        for i,x in enumerate(np.array([[dx,0,0],[0,dx,0],[0,0,dx]])):
1480            XYZ[j] -= x
1481            phi,psi = getRestRama(XYZ,Amat)
1482            p1,d1 = calcRamaEnergy(phi,psi,Coeff)
1483            XYZ[j] += 2*x
1484            phi,psi = getRestRama(XYZ,Amat)
1485            p2,d2 = calcRamaEnergy(phi,psi,Coeff)
1486            XYZ[j] -= x
1487            deriv[j][i] = (p2-p1)/(2*dx)
1488    return deriv.flatten()
1489
1490def getRestPolefig(ODFln,SamSym,Grid):
1491    '''default doc string
1492   
1493    :param type name: description
1494   
1495    :returns: type name: description
1496   
1497    '''
1498    X,Y = np.meshgrid(np.linspace(1.,-1.,Grid),np.linspace(-1.,1.,Grid))
1499    R,P = np.sqrt(X**2+Y**2).flatten(),atan2d(Y,X).flatten()
1500    R = np.where(R <= 1.,2.*atand(R),0.0)
1501    Z = np.zeros_like(R)
1502    Z = G2lat.polfcal(ODFln,SamSym,R,P)
1503    Z = np.reshape(Z,(Grid,Grid))
1504    return np.reshape(R,(Grid,Grid)),np.reshape(P,(Grid,Grid)),Z
1505
1506def getRestPolefigDerv(HKL,Grid,SHCoeff):
1507    '''default doc string
1508   
1509    :param type name: description
1510   
1511    :returns: type name: description
1512   
1513    '''
1514    pass
1515       
1516def getDistDerv(Oxyz,Txyz,Amat,Tunit,Top,SGData):
1517    '''default doc string
1518   
1519    :param type name: description
1520   
1521    :returns: type name: description
1522   
1523    '''
1524    def calcDist(Ox,Tx,U,inv,C,M,T,Amat):
1525        TxT = inv*(np.inner(M,Tx)+T)+C+U
1526        return np.sqrt(np.sum(np.inner(Amat,(TxT-Ox))**2))
1527       
1528    inv = Top/abs(Top)
1529    cent = abs(Top)/100
1530    op = abs(Top)%100-1
1531    M,T = SGData['SGOps'][op]
1532    C = SGData['SGCen'][cent]
1533    dx = .00001
1534    deriv = np.zeros(6)
1535    for i in [0,1,2]:
1536        Oxyz[i] -= dx
1537        d0 = calcDist(Oxyz,Txyz,Tunit,inv,C,M,T,Amat)
1538        Oxyz[i] += 2*dx
1539        deriv[i] = (calcDist(Oxyz,Txyz,Tunit,inv,C,M,T,Amat)-d0)/(2.*dx)
1540        Oxyz[i] -= dx
1541        Txyz[i] -= dx
1542        d0 = calcDist(Oxyz,Txyz,Tunit,inv,C,M,T,Amat)
1543        Txyz[i] += 2*dx
1544        deriv[i+3] = (calcDist(Oxyz,Txyz,Tunit,inv,C,M,T,Amat)-d0)/(2.*dx)
1545        Txyz[i] -= dx
1546    return deriv
1547   
1548def getAngSig(VA,VB,Amat,SGData,covData={}):
1549    '''default doc string
1550   
1551    :param type name: description
1552   
1553    :returns: type name: description
1554   
1555    '''
1556    def calcVec(Ox,Tx,U,inv,C,M,T,Amat):
1557        TxT = inv*(np.inner(M,Tx)+T)+C+U
1558        return np.inner(Amat,(TxT-Ox))
1559       
1560    def calcAngle(Ox,TxA,TxB,unitA,unitB,invA,CA,MA,TA,invB,CB,MB,TB,Amat):
1561        VecA = calcVec(Ox,TxA,unitA,invA,CA,MA,TA,Amat)
1562        VecA /= np.sqrt(np.sum(VecA**2))
1563        VecB = calcVec(Ox,TxB,unitB,invB,CB,MB,TB,Amat)
1564        VecB /= np.sqrt(np.sum(VecB**2))
1565        edge = VecB-VecA
1566        edge = np.sum(edge**2)
1567        angle = (2.-edge)/2.
1568        angle = max(angle,-1.)
1569        return acosd(angle)
1570       
1571    OxAN,OxA,TxAN,TxA,unitA,TopA = VA
1572    OxBN,OxB,TxBN,TxB,unitB,TopB = VB
1573    invA = invB = 1
1574    invA = TopA/abs(TopA)
1575    invB = TopB/abs(TopB)
1576    centA = abs(TopA)/100
1577    centB = abs(TopB)/100
1578    opA = abs(TopA)%100-1
1579    opB = abs(TopB)%100-1
1580    MA,TA = SGData['SGOps'][opA]
1581    MB,TB = SGData['SGOps'][opB]
1582    CA = SGData['SGCen'][centA]
1583    CB = SGData['SGCen'][centB]
1584    if 'covMatrix' in covData:
1585        covMatrix = covData['covMatrix']
1586        varyList = covData['varyList']
1587        AngVcov = getVCov(OxAN+TxAN+TxBN,varyList,covMatrix)
1588        dx = .00001
1589        dadx = np.zeros(9)
1590        Ang = calcAngle(OxA,TxA,TxB,unitA,unitB,invA,CA,MA,TA,invB,CB,MB,TB,Amat)
1591        for i in [0,1,2]:
1592            OxA[i] -= dx
1593            a0 = calcAngle(OxA,TxA,TxB,unitA,unitB,invA,CA,MA,TA,invB,CB,MB,TB,Amat)
1594            OxA[i] += 2*dx
1595            dadx[i] = (calcAngle(OxA,TxA,TxB,unitA,unitB,invA,CA,MA,TA,invB,CB,MB,TB,Amat)-a0)/(2*dx)
1596            OxA[i] -= dx
1597           
1598            TxA[i] -= dx
1599            a0 = calcAngle(OxA,TxA,TxB,unitA,unitB,invA,CA,MA,TA,invB,CB,MB,TB,Amat)
1600            TxA[i] += 2*dx
1601            dadx[i+3] = (calcAngle(OxA,TxA,TxB,unitA,unitB,invA,CA,MA,TA,invB,CB,MB,TB,Amat)-a0)/(2*dx)
1602            TxA[i] -= dx
1603           
1604            TxB[i] -= dx
1605            a0 = calcAngle(OxA,TxA,TxB,unitA,unitB,invA,CA,MA,TA,invB,CB,MB,TB,Amat)
1606            TxB[i] += 2*dx
1607            dadx[i+6] = (calcAngle(OxA,TxA,TxB,unitA,unitB,invA,CA,MA,TA,invB,CB,MB,TB,Amat)-a0)/(2*dx)
1608            TxB[i] -= dx
1609           
1610        sigAng = np.sqrt(np.inner(dadx,np.inner(AngVcov,dadx)))
1611        if sigAng < 0.01:
1612            sigAng = 0.0
1613        return Ang,sigAng
1614    else:
1615        return calcAngle(OxA,TxA,TxB,unitA,unitB,invA,CA,MA,TA,invB,CB,MB,TB,Amat),0.0
1616
1617def GetDistSig(Oatoms,Atoms,Amat,SGData,covData={}):
1618    '''default doc string
1619   
1620    :param type name: description
1621   
1622    :returns: type name: description
1623   
1624    '''
1625    def calcDist(Atoms,SyOps,Amat):
1626        XYZ = []
1627        for i,atom in enumerate(Atoms):
1628            Inv,M,T,C,U = SyOps[i]
1629            XYZ.append(np.array(atom[1:4]))
1630            XYZ[-1] = Inv*(np.inner(M,np.array(XYZ[-1]))+T)+C+U
1631            XYZ[-1] = np.inner(Amat,XYZ[-1]).T
1632        V1 = XYZ[1]-XYZ[0]
1633        return np.sqrt(np.sum(V1**2))
1634       
1635    Inv = []
1636    SyOps = []
1637    names = []
1638    for i,atom in enumerate(Oatoms):
1639        names += atom[-1]
1640        Op,unit = Atoms[i][-1]
1641        inv = Op/abs(Op)
1642        m,t = SGData['SGOps'][abs(Op)%100-1]
1643        c = SGData['SGCen'][abs(Op)/100]
1644        SyOps.append([inv,m,t,c,unit])
1645    Dist = calcDist(Oatoms,SyOps,Amat)
1646   
1647    sig = -0.001
1648    if 'covMatrix' in covData:
1649        parmNames = []
1650        dx = .00001
1651        dadx = np.zeros(6)
1652        for i in range(6):
1653            ia = i/3
1654            ix = i%3
1655            Oatoms[ia][ix+1] += dx
1656            a0 = calcDist(Oatoms,SyOps,Amat)
1657            Oatoms[ia][ix+1] -= 2*dx
1658            dadx[i] = (calcDist(Oatoms,SyOps,Amat)-a0)/(2.*dx)
1659        covMatrix = covData['covMatrix']
1660        varyList = covData['varyList']
1661        DistVcov = getVCov(names,varyList,covMatrix)
1662        sig = np.sqrt(np.inner(dadx,np.inner(DistVcov,dadx)))
1663        if sig < 0.001:
1664            sig = -0.001
1665   
1666    return Dist,sig
1667
1668def GetAngleSig(Oatoms,Atoms,Amat,SGData,covData={}):
1669    '''default doc string
1670   
1671    :param type name: description
1672   
1673    :returns: type name: description
1674   
1675    '''
1676
1677    def calcAngle(Atoms,SyOps,Amat):
1678        XYZ = []
1679        for i,atom in enumerate(Atoms):
1680            Inv,M,T,C,U = SyOps[i]
1681            XYZ.append(np.array(atom[1:4]))
1682            XYZ[-1] = Inv*(np.inner(M,np.array(XYZ[-1]))+T)+C+U
1683            XYZ[-1] = np.inner(Amat,XYZ[-1]).T
1684        V1 = XYZ[1]-XYZ[0]
1685        V1 /= np.sqrt(np.sum(V1**2))
1686        V2 = XYZ[1]-XYZ[2]
1687        V2 /= np.sqrt(np.sum(V2**2))
1688        V3 = V2-V1
1689        cang = min(1.,max((2.-np.sum(V3**2))/2.,-1.))
1690        return acosd(cang)
1691
1692    Inv = []
1693    SyOps = []
1694    names = []
1695    for i,atom in enumerate(Oatoms):
1696        names += atom[-1]
1697        Op,unit = Atoms[i][-1]
1698        inv = Op/abs(Op)
1699        m,t = SGData['SGOps'][abs(Op)%100-1]
1700        c = SGData['SGCen'][abs(Op)/100]
1701        SyOps.append([inv,m,t,c,unit])
1702    Angle = calcAngle(Oatoms,SyOps,Amat)
1703   
1704    sig = -0.01
1705    if 'covMatrix' in covData:
1706        parmNames = []
1707        dx = .00001
1708        dadx = np.zeros(9)
1709        for i in range(9):
1710            ia = i/3
1711            ix = i%3
1712            Oatoms[ia][ix+1] += dx
1713            a0 = calcAngle(Oatoms,SyOps,Amat)
1714            Oatoms[ia][ix+1] -= 2*dx
1715            dadx[i] = (calcAngle(Oatoms,SyOps,Amat)-a0)/(2.*dx)
1716        covMatrix = covData['covMatrix']
1717        varyList = covData['varyList']
1718        AngVcov = getVCov(names,varyList,covMatrix)
1719        sig = np.sqrt(np.inner(dadx,np.inner(AngVcov,dadx)))
1720        if sig < 0.01:
1721            sig = -0.01
1722   
1723    return Angle,sig
1724
1725def GetTorsionSig(Oatoms,Atoms,Amat,SGData,covData={}):
1726    '''default doc string
1727   
1728    :param type name: description
1729   
1730    :returns: type name: description
1731   
1732    '''
1733
1734    def calcTorsion(Atoms,SyOps,Amat):
1735       
1736        XYZ = []
1737        for i,atom in enumerate(Atoms):
1738            Inv,M,T,C,U = SyOps[i]
1739            XYZ.append(np.array(atom[1:4]))
1740            XYZ[-1] = Inv*(np.inner(M,np.array(XYZ[-1]))+T)+C+U
1741            XYZ[-1] = np.inner(Amat,XYZ[-1]).T
1742        V1 = XYZ[1]-XYZ[0]
1743        V2 = XYZ[2]-XYZ[1]
1744        V3 = XYZ[3]-XYZ[2]
1745        V1 /= np.sqrt(np.sum(V1**2))
1746        V2 /= np.sqrt(np.sum(V2**2))
1747        V3 /= np.sqrt(np.sum(V3**2))
1748        M = np.array([V1,V2,V3])
1749        D = nl.det(M)
1750        Ang = 1.0
1751        P12 = np.dot(V1,V2)
1752        P13 = np.dot(V1,V3)
1753        P23 = np.dot(V2,V3)
1754        Tors = acosd((P12*P23-P13)/(np.sqrt(1.-P12**2)*np.sqrt(1.-P23**2)))*D/abs(D)
1755        return Tors
1756           
1757    Inv = []
1758    SyOps = []
1759    names = []
1760    for i,atom in enumerate(Oatoms):
1761        names += atom[-1]
1762        Op,unit = Atoms[i][-1]
1763        inv = Op/abs(Op)
1764        m,t = SGData['SGOps'][abs(Op)%100-1]
1765        c = SGData['SGCen'][abs(Op)/100]
1766        SyOps.append([inv,m,t,c,unit])
1767    Tors = calcTorsion(Oatoms,SyOps,Amat)
1768   
1769    sig = -0.01
1770    if 'covMatrix' in covData:
1771        parmNames = []
1772        dx = .00001
1773        dadx = np.zeros(12)
1774        for i in range(12):
1775            ia = i/3
1776            ix = i%3
1777            Oatoms[ia][ix+1] -= dx
1778            a0 = calcTorsion(Oatoms,SyOps,Amat)
1779            Oatoms[ia][ix+1] += 2*dx
1780            dadx[i] = (calcTorsion(Oatoms,SyOps,Amat)-a0)/(2.*dx)
1781            Oatoms[ia][ix+1] -= dx           
1782        covMatrix = covData['covMatrix']
1783        varyList = covData['varyList']
1784        TorVcov = getVCov(names,varyList,covMatrix)
1785        sig = np.sqrt(np.inner(dadx,np.inner(TorVcov,dadx)))
1786        if sig < 0.01:
1787            sig = -0.01
1788   
1789    return Tors,sig
1790       
1791def GetDATSig(Oatoms,Atoms,Amat,SGData,covData={}):
1792    '''default doc string
1793   
1794    :param type name: description
1795   
1796    :returns: type name: description
1797   
1798    '''
1799
1800    def calcDist(Atoms,SyOps,Amat):
1801        XYZ = []
1802        for i,atom in enumerate(Atoms):
1803            Inv,M,T,C,U = SyOps[i]
1804            XYZ.append(np.array(atom[1:4]))
1805            XYZ[-1] = Inv*(np.inner(M,np.array(XYZ[-1]))+T)+C+U
1806            XYZ[-1] = np.inner(Amat,XYZ[-1]).T
1807        V1 = XYZ[1]-XYZ[0]
1808        return np.sqrt(np.sum(V1**2))
1809       
1810    def calcAngle(Atoms,SyOps,Amat):
1811        XYZ = []
1812        for i,atom in enumerate(Atoms):
1813            Inv,M,T,C,U = SyOps[i]
1814            XYZ.append(np.array(atom[1:4]))
1815            XYZ[-1] = Inv*(np.inner(M,np.array(XYZ[-1]))+T)+C+U
1816            XYZ[-1] = np.inner(Amat,XYZ[-1]).T
1817        V1 = XYZ[1]-XYZ[0]
1818        V1 /= np.sqrt(np.sum(V1**2))
1819        V2 = XYZ[1]-XYZ[2]
1820        V2 /= np.sqrt(np.sum(V2**2))
1821        V3 = V2-V1
1822        cang = min(1.,max((2.-np.sum(V3**2))/2.,-1.))
1823        return acosd(cang)
1824
1825    def calcTorsion(Atoms,SyOps,Amat):
1826       
1827        XYZ = []
1828        for i,atom in enumerate(Atoms):
1829            Inv,M,T,C,U = SyOps[i]
1830            XYZ.append(np.array(atom[1:4]))
1831            XYZ[-1] = Inv*(np.inner(M,np.array(XYZ[-1]))+T)+C+U
1832            XYZ[-1] = np.inner(Amat,XYZ[-1]).T
1833        V1 = XYZ[1]-XYZ[0]
1834        V2 = XYZ[2]-XYZ[1]
1835        V3 = XYZ[3]-XYZ[2]
1836        V1 /= np.sqrt(np.sum(V1**2))
1837        V2 /= np.sqrt(np.sum(V2**2))
1838        V3 /= np.sqrt(np.sum(V3**2))
1839        M = np.array([V1,V2,V3])
1840        D = nl.det(M)
1841        Ang = 1.0
1842        P12 = np.dot(V1,V2)
1843        P13 = np.dot(V1,V3)
1844        P23 = np.dot(V2,V3)
1845        Tors = acosd((P12*P23-P13)/(np.sqrt(1.-P12**2)*np.sqrt(1.-P23**2)))*D/abs(D)
1846        return Tors
1847           
1848    Inv = []
1849    SyOps = []
1850    names = []
1851    for i,atom in enumerate(Oatoms):
1852        names += atom[-1]
1853        Op,unit = Atoms[i][-1]
1854        inv = Op/abs(Op)
1855        m,t = SGData['SGOps'][abs(Op)%100-1]
1856        c = SGData['SGCen'][abs(Op)/100]
1857        SyOps.append([inv,m,t,c,unit])
1858    M = len(Oatoms)
1859    if M == 2:
1860        Val = calcDist(Oatoms,SyOps,Amat)
1861    elif M == 3:
1862        Val = calcAngle(Oatoms,SyOps,Amat)
1863    else:
1864        Val = calcTorsion(Oatoms,SyOps,Amat)
1865   
1866    sigVals = [-0.001,-0.01,-0.01]
1867    sig = sigVals[M-3]
1868    if 'covMatrix' in covData:
1869        parmNames = []
1870        dx = .00001
1871        N = M*3
1872        dadx = np.zeros(N)
1873        for i in range(N):
1874            ia = i/3
1875            ix = i%3
1876            Oatoms[ia][ix+1] += dx
1877            if M == 2:
1878                a0 = calcDist(Oatoms,SyOps,Amat)
1879            elif M == 3:
1880                a0 = calcAngle(Oatoms,SyOps,Amat)
1881            else:
1882                a0 = calcTorsion(Oatoms,SyOps,Amat)
1883            Oatoms[ia][ix+1] -= 2*dx
1884            if M == 2:
1885                dadx[i] = (calcDist(Oatoms,SyOps,Amat)-a0)/(2.*dx)               
1886            elif M == 3:
1887                dadx[i] = (calcAngle(Oatoms,SyOps,Amat)-a0)/(2.*dx)               
1888            else:
1889                dadx[i] = (calcTorsion(Oatoms,SyOps,Amat)-a0)/(2.*dx)
1890        covMatrix = covData['covMatrix']
1891        varyList = covData['varyList']
1892        Vcov = getVCov(names,varyList,covMatrix)
1893        sig = np.sqrt(np.inner(dadx,np.inner(Vcov,dadx)))
1894        if sig < sigVals[M-3]:
1895            sig = sigVals[M-3]
1896   
1897    return Val,sig
1898       
1899def ValEsd(value,esd=0,nTZ=False):
1900    '''Format a floating point number with a given level of precision or
1901    with in crystallographic format with a "esd", as value(esd). If esd is
1902    negative the number is formatted with the level of significant figures
1903    appropriate if abs(esd) were the esd, but the esd is not included.
1904    if the esd is zero, approximately 6 significant figures are printed.
1905    nTZ=True causes "extra" zeros to be removed after the decimal place.
1906    for example:
1907
1908      * "1.235(3)" for value=1.2346 & esd=0.003
1909      * "1.235(3)e4" for value=12346. & esd=30
1910      * "1.235(3)e6" for value=0.12346e7 & esd=3000
1911      * "1.235" for value=1.2346 & esd=-0.003
1912      * "1.240" for value=1.2395 & esd=-0.003
1913      * "1.24" for value=1.2395 & esd=-0.003 with nTZ=True
1914      * "1.23460" for value=1.2346 & esd=0.0
1915
1916    :param float value: number to be formatted
1917    :param float esd: uncertainty or if esd < 0, specifies level of
1918      precision to be shown e.g. esd=-0.01 gives 2 places beyond decimal
1919    :param bool nTZ: True to remove trailing zeros (default is False)
1920    :returns: value(esd) or value as a string
1921
1922    '''
1923    # Note: this routine is Python 3 compatible -- I think
1924    cutoff = 3.16228    #=(sqrt(10); same as old GSAS   was 1.95
1925    if math.isnan(value): # invalid value, bail out
1926        return '?'
1927    if math.isnan(esd): # invalid esd, treat as zero
1928        esd = 0
1929        esdoff = 5
1930    elif esd != 0:
1931        # transform the esd to a one or two digit integer
1932        l = math.log10(abs(esd)) % 1.
1933        if l < math.log10(cutoff): l+= 1.       
1934        intesd = int(round(10**l)) # esd as integer
1935        # determine the number of digits offset for the esd
1936        esdoff = int(round(math.log10(intesd*1./abs(esd))))
1937    else:
1938        esdoff = 5
1939    valoff = 0
1940    if abs(value) < abs(esdoff): # value is effectively zero
1941        pass
1942    elif esdoff < 0 or abs(value) > 1.0e6 or abs(value) < 1.0e-4: # use scientific notation
1943        # where the digit offset is to the left of the decimal place or where too many
1944        # digits are needed
1945        if abs(value) > 1:
1946            valoff = int(math.log10(abs(value)))
1947        elif abs(value) > 0:
1948            valoff = int(math.log10(abs(value))-0.9999999)
1949        else:
1950            valoff = 0
1951    if esd != 0:
1952        if valoff+esdoff < 0:
1953            valoff = esdoff = 0
1954        out = ("{:."+str(valoff+esdoff)+"f}").format(value/10**valoff) # format the value
1955    elif valoff != 0: # esd = 0; exponential notation ==> esdoff decimal places
1956        out = ("{:."+str(esdoff)+"f}").format(value/10**valoff) # format the value
1957    else: # esd = 0; non-exponential notation ==> esdoff+1 significant digits
1958        if abs(value) > 0:           
1959            extra = -math.log10(abs(value))
1960        else:
1961            extra = 0
1962        if extra > 0: extra += 1
1963        out = ("{:."+str(max(0,esdoff+int(extra)))+"f}").format(value) # format the value
1964    if esd > 0:
1965        out += ("({:d})").format(intesd)  # add the esd
1966    elif nTZ and '.' in out:
1967        out = out.rstrip('0')  # strip zeros to right of decimal
1968        out = out.rstrip('.')  # and decimal place when not needed
1969    if valoff != 0:
1970        out += ("e{:d}").format(valoff) # add an exponent, when needed
1971    return out
1972   
1973################################################################################
1974##### Texture fitting stuff
1975################################################################################
1976
1977def FitTexture(General,Gangls,refData,keyList,pgbar):
1978    import pytexture as ptx
1979    ptx.pyqlmninit()            #initialize fortran arrays for spherical harmonics
1980   
1981    def printSpHarm(textureData,SHtextureSig):
1982        print '\n Spherical harmonics texture: Order:' + str(textureData['Order'])
1983        names = ['omega','chi','phi']
1984        namstr = '  names :'
1985        ptstr =  '  values:'
1986        sigstr = '  esds  :'
1987        for name in names:
1988            namstr += '%12s'%('Sample '+name)
1989            ptstr += '%12.3f'%(textureData['Sample '+name][1])
1990            if 'Sample '+name in SHtextureSig:
1991                sigstr += '%12.3f'%(SHtextureSig['Sample '+name])
1992            else:
1993                sigstr += 12*' '
1994        print namstr
1995        print ptstr
1996        print sigstr
1997        print '\n Texture coefficients:'
1998        SHcoeff = textureData['SH Coeff'][1]
1999        SHkeys = SHcoeff.keys()
2000        nCoeff = len(SHcoeff)
2001        nBlock = nCoeff/10+1
2002        iBeg = 0
2003        iFin = min(iBeg+10,nCoeff)
2004        for block in range(nBlock):
2005            namstr = '  names :'
2006            ptstr =  '  values:'
2007            sigstr = '  esds  :'
2008            for name in SHkeys[iBeg:iFin]:
2009                if 'C' in name:
2010                    namstr += '%12s'%(name)
2011                    ptstr += '%12.3f'%(SHcoeff[name])
2012                    if name in SHtextureSig:
2013                        sigstr += '%12.3f'%(SHtextureSig[name])
2014                    else:
2015                        sigstr += 12*' '
2016            print namstr
2017            print ptstr
2018            print sigstr
2019            iBeg += 10
2020            iFin = min(iBeg+10,nCoeff)
2021           
2022    def Dict2Values(parmdict, varylist):
2023        '''Use before call to leastsq to setup list of values for the parameters
2024        in parmdict, as selected by key in varylist'''
2025        return [parmdict[key] for key in varylist] 
2026       
2027    def Values2Dict(parmdict, varylist, values):
2028        ''' Use after call to leastsq to update the parameter dictionary with
2029        values corresponding to keys in varylist'''
2030        parmdict.update(zip(varylist,values))
2031       
2032    def errSpHarm(values,SGData,cell,Gangls,shModel,refData,parmDict,varyList,pgbar):
2033        parmDict.update(zip(varyList,values))
2034        Mat = np.empty(0)
2035        sumObs = 0
2036        Sangls = [parmDict['Sample '+'omega'],parmDict['Sample '+'chi'],parmDict['Sample '+'phi']]
2037        for hist in Gangls.keys():
2038            Refs = refData[hist]
2039            Refs[:,5] = np.where(Refs[:,5]>0.,Refs[:,5],0.)
2040            wt = 1./np.sqrt(np.max(Refs[:,4],.25))
2041#            wt = 1./np.max(Refs[:,4],.25)
2042            sumObs += np.sum(wt*Refs[:,5])
2043            Refs[:,6] = 1.
2044            H = Refs[:,:3]
2045            phi,beta = G2lat.CrsAng(H,cell,SGData)
2046            psi,gam,x,x = G2lat.SamAng(Refs[:,3]/2.,Gangls[hist],Sangls,False) #assume not Bragg-Brentano!
2047            for item in parmDict:
2048                if 'C' in item:
2049                    L,M,N = eval(item.strip('C'))
2050                    Kcl = G2lat.GetKcl(L,N,SGData['SGLaue'],phi,beta)
2051                    Ksl,x,x = G2lat.GetKsl(L,M,shModel,psi,gam)
2052                    Lnorm = G2lat.Lnorm(L)
2053                    Refs[:,6] += parmDict[item]*Lnorm*Kcl*Ksl
2054            mat = wt*(Refs[:,5]-Refs[:,6])
2055            Mat = np.concatenate((Mat,mat))
2056        sumD = np.sum(np.abs(Mat))
2057        R = min(100.,100.*sumD/sumObs)
2058        pgbar.Update(R,newmsg='Residual = %5.2f'%(R))
2059        print ' Residual: %.3f%%'%(R)
2060        return Mat
2061       
2062    def dervSpHarm(values,SGData,cell,Gangls,shModel,refData,parmDict,varyList,pgbar):
2063        Mat = np.empty(0)
2064        Sangls = [parmDict['Sample omega'],parmDict['Sample chi'],parmDict['Sample phi']]
2065        for hist in Gangls.keys():
2066            mat = np.zeros((len(varyList),len(refData[hist])))
2067            Refs = refData[hist]
2068            H = Refs[:,:3]
2069            wt = 1./np.sqrt(np.max(Refs[:,4],.25))
2070#            wt = 1./np.max(Refs[:,4],.25)
2071            phi,beta = G2lat.CrsAng(H,cell,SGData)
2072            psi,gam,dPdA,dGdA = G2lat.SamAng(Refs[:,3]/2.,Gangls[hist],Sangls,False) #assume not Bragg-Brentano!
2073            for j,item in enumerate(varyList):
2074                if 'C' in item:
2075                    L,M,N = eval(item.strip('C'))
2076                    Kcl = G2lat.GetKcl(L,N,SGData['SGLaue'],phi,beta)
2077                    Ksl,dKdp,dKdg = G2lat.GetKsl(L,M,shModel,psi,gam)
2078                    Lnorm = G2lat.Lnorm(L)
2079                    mat[j] = -wt*Lnorm*Kcl*Ksl
2080                    for k,itema in enumerate(['Sample omega','Sample chi','Sample phi']):
2081                        try:
2082                            l = varyList.index(itema)
2083                            mat[l] -= parmDict[item]*wt*Lnorm*Kcl*(dKdp*dPdA[k]+dKdg*dGdA[k])
2084                        except ValueError:
2085                            pass
2086            if len(Mat):
2087                Mat = np.concatenate((Mat,mat.T))
2088            else:
2089                Mat = mat.T
2090        print 'deriv'
2091        return Mat
2092
2093    print ' Fit texture for '+General['Name']
2094    SGData = General['SGData']
2095    cell = General['Cell'][1:7]
2096    Texture = General['SH Texture']
2097    if not Texture['Order']:
2098        return 'No spherical harmonics coefficients'
2099    varyList = []
2100    parmDict = copy.copy(Texture['SH Coeff'][1])
2101    for item in ['Sample omega','Sample chi','Sample phi']:
2102        parmDict[item] = Texture[item][1]
2103        if Texture[item][0]:
2104            varyList.append(item)
2105    if Texture['SH Coeff'][0]:
2106        varyList += Texture['SH Coeff'][1].keys()
2107    while True:
2108        begin = time.time()
2109        values =  np.array(Dict2Values(parmDict, varyList))
2110        result = so.leastsq(errSpHarm,values,Dfun=dervSpHarm,full_output=True,ftol=1.e-6,
2111            args=(SGData,cell,Gangls,Texture['Model'],refData,parmDict,varyList,pgbar))
2112        ncyc = int(result[2]['nfev']/2)
2113        if ncyc:
2114            runtime = time.time()-begin   
2115            chisq = np.sum(result[2]['fvec']**2)
2116            Values2Dict(parmDict, varyList, result[0])
2117            GOF = chisq/(len(result[2]['fvec'])-len(varyList))       #reduced chi^2
2118            print 'Number of function calls:',result[2]['nfev'],' Number of observations: ',len(result[2]['fvec']),' Number of parameters: ',len(varyList)
2119            print 'refinement time = %8.3fs, %8.3fs/cycle'%(runtime,runtime/ncyc)
2120            try:
2121                sig = np.sqrt(np.diag(result[1])*GOF)
2122                if np.any(np.isnan(sig)):
2123                    print '*** Least squares aborted - some invalid esds possible ***'
2124                break                   #refinement succeeded - finish up!
2125            except ValueError:          #result[1] is None on singular matrix
2126                print '**** Refinement failed - singular matrix ****'
2127                return None
2128        else:
2129            break
2130   
2131    if ncyc:
2132        for parm in parmDict:
2133            if 'C' in parm:
2134                Texture['SH Coeff'][1][parm] = parmDict[parm]
2135            else:
2136                Texture[parm][1] = parmDict[parm] 
2137        sigDict = dict(zip(varyList,sig))
2138        printSpHarm(Texture,sigDict)
2139       
2140    return None
2141   
2142################################################################################
2143##### Fourier & charge flip stuff
2144################################################################################
2145
2146def adjHKLmax(SGData,Hmax):
2147    '''default doc string
2148   
2149    :param type name: description
2150   
2151    :returns: type name: description
2152   
2153    '''
2154    if SGData['SGLaue'] in ['3','3m1','31m','6/m','6/mmm']:
2155        Hmax[0] = int(math.ceil(Hmax[0]/6.))*6
2156        Hmax[1] = int(math.ceil(Hmax[1]/6.))*6
2157        Hmax[2] = int(math.ceil(Hmax[2]/4.))*4
2158    else:
2159        Hmax[0] = int(math.ceil(Hmax[0]/4.))*4
2160        Hmax[1] = int(math.ceil(Hmax[1]/4.))*4
2161        Hmax[2] = int(math.ceil(Hmax[2]/4.))*4
2162
2163def OmitMap(data,reflDict,pgbar=None):
2164    '''default doc string
2165   
2166    :param type name: description
2167   
2168    :returns: type name: description
2169   
2170    '''
2171    generalData = data['General']
2172    if not generalData['Map']['MapType']:
2173        print '**** ERROR - Fourier map not defined'
2174        return
2175    mapData = generalData['Map']
2176    dmin = mapData['Resolution']
2177    SGData = generalData['SGData']
2178    SGMT = np.array([ops[0].T for ops in SGData['SGOps']])
2179    SGT = np.array([ops[1] for ops in SGData['SGOps']])
2180    cell = generalData['Cell'][1:8]       
2181    A = G2lat.cell2A(cell[:6])
2182    Hmax = np.asarray(G2lat.getHKLmax(dmin,SGData,A),dtype='i')+1
2183    adjHKLmax(SGData,Hmax)
2184    Fhkl = np.zeros(shape=2*Hmax,dtype='c16')
2185    time0 = time.time()
2186    for iref,ref in enumerate(reflDict['RefList']):
2187        if ref[4] >= dmin:
2188            Fosq,Fcsq,ph = ref[8:11]
2189            Uniq = np.inner(ref[:3],SGMT)
2190            Phi = np.inner(ref[:3],SGT)
2191            for i,hkl in enumerate(Uniq):        #uses uniq
2192                hkl = np.asarray(hkl,dtype='i')
2193                dp = 360.*Phi[i]                #and phi
2194                a = cosd(ph+dp)
2195                b = sind(ph+dp)
2196                phasep = complex(a,b)
2197                phasem = complex(a,-b)
2198                Fo = np.sqrt(Fosq)
2199                if '2Fo-Fc' in mapData['MapType']:
2200                    F = 2.*np.sqrt(Fosq)-np.sqrt(Fcsq)
2201                else:
2202                    F = np.sqrt(Fosq)
2203                h,k,l = hkl+Hmax
2204                Fhkl[h,k,l] = F*phasep
2205                h,k,l = -hkl+Hmax
2206                Fhkl[h,k,l] = F*phasem
2207    rho0 = fft.fftn(fft.fftshift(Fhkl))/cell[6]
2208    M = np.mgrid[0:4,0:4,0:4]
2209    blkIds = np.array(zip(M[0].flatten(),M[1].flatten(),M[2].flatten()))
2210    iBeg = blkIds*rho0.shape/4
2211    iFin = (blkIds+1)*rho0.shape/4
2212    rho_omit = np.zeros_like(rho0)
2213    nBlk = 0
2214    for iB,iF in zip(iBeg,iFin):
2215        rho1 = np.copy(rho0)
2216        rho1[iB[0]:iF[0],iB[1]:iF[1],iB[2]:iF[2]] = 0.
2217        Fnew = fft.ifftshift(fft.ifftn(rho1))
2218        Fnew = np.where(Fnew,Fnew,1.0)           #avoid divide by zero
2219        phase = Fnew/np.absolute(Fnew)
2220        OFhkl = np.absolute(Fhkl)*phase
2221        rho1 = np.real(fft.fftn(fft.fftshift(OFhkl)))*(1.+0j)
2222        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]])
2223        nBlk += 1
2224        pgbar.Update(nBlk)
2225    mapData['rho'] = np.real(rho_omit)/cell[6]
2226    mapData['rhoMax'] = max(np.max(mapData['rho']),-np.min(mapData['rho']))
2227    mapData['minmax'] = [np.max(mapData['rho']),np.min(mapData['rho'])]
2228    print 'Omit map time: %.4f'%(time.time()-time0),'no. elements: %d'%(Fhkl.size)
2229    return mapData
2230   
2231def Fourier4DMap(data,reflDict):
2232    '''default doc string
2233   
2234    :param type name: description
2235   
2236    :returns: type name: description
2237   
2238    '''
2239    generalData = data['General']
2240    map4DData = generalData['4DmapData']
2241    mapData = generalData['Map']
2242    dmin = mapData['Resolution']
2243    SGData = generalData['SGData']
2244    SSGData = generalData['SSGData']
2245    SSGMT = np.array([ops[0].T for ops in SSGData['SSGOps']])
2246    SSGT = np.array([ops[1] for ops in SSGData['SSGOps']])
2247    cell = generalData['Cell'][1:8]       
2248    A = G2lat.cell2A(cell[:6])
2249    maxM = generalData['SuperVec'][2]
2250    Hmax = G2lat.getHKLmax(dmin,SGData,A)+[maxM,]
2251    adjHKLmax(SGData,Hmax)
2252    Hmax = np.asarray(Hmax,dtype='i')+1
2253    Fhkl = np.zeros(shape=2*Hmax,dtype='c16')
2254    time0 = time.time()
2255    for iref,ref in enumerate(reflDict['RefList']):
2256        if ref[5] > dmin:
2257            Fosq,Fcsq,ph = ref[9:12]
2258            Fosq = np.where(Fosq>0.,Fosq,0.)    #can't use Fo^2 < 0
2259            Uniq = np.inner(ref[:4],SSGMT)
2260            Phi = np.inner(ref[:4],SSGT)
2261            for i,hkl in enumerate(Uniq):        #uses uniq
2262                hkl = np.asarray(hkl,dtype='i')
2263                dp = 360.*Phi[i]                #and phi
2264                a = cosd(ph+dp)
2265                b = sind(ph+dp)
2266                phasep = complex(a,b)
2267                phasem = complex(a,-b)
2268                if 'Fobs' in mapData['MapType']:
2269                    F = np.sqrt(Fosq)
2270                    h,k,l,m = hkl+Hmax
2271                    Fhkl[h,k,l,m] = F*phasep
2272                    h,k,l,m = -hkl+Hmax
2273                    Fhkl[h,k,l,m] = F*phasem
2274                elif 'Fcalc' in mapData['MapType']:
2275                    F = np.sqrt(Fcsq)
2276                    h,k,l,m = hkl+Hmax
2277                    Fhkl[h,k,l,m] = F*phasep
2278                    h,k,l,m = -hkl+Hmax
2279                    Fhkl[h,k,l,m] = F*phasem                   
2280                elif 'delt-F' in mapData['MapType']:
2281                    dF = np.sqrt(Fosq)-np.sqrt(Fcsq)
2282                    h,k,l,m = hkl+Hmax
2283                    Fhkl[h,k,l,m] = dF*phasep
2284                    h,k,l,m = -hkl+Hmax
2285                    Fhkl[h,k,l,m] = dF*phasem
2286    SSrho = fft.fftn(fft.fftshift(Fhkl))/cell[6]                #4D map
2287    rho = fft.fftn(fft.fftshift(Fhkl[:,:,:,maxM+1]))/cell[6]    #3D map
2288    map4DData['rho'] = np.real(SSrho)
2289    map4DData['rhoMax'] = max(np.max(map4DData['rho']),-np.min(map4DData['rho']))
2290    map4DData['minmax'] = [np.max(map4DData['rho']),np.min(map4DData['rho'])]
2291    map4DData['Type'] = reflDict['Type']
2292    mapData['Type'] = reflDict['Type']
2293    mapData['rho'] = np.real(rho)
2294    mapData['rhoMax'] = max(np.max(mapData['rho']),-np.min(mapData['rho']))
2295    mapData['minmax'] = [np.max(mapData['rho']),np.min(mapData['rho'])]
2296    print 'Fourier map time: %.4f'%(time.time()-time0),'no. elements: %d'%(Fhkl.size)
2297
2298def FourierMap(data,reflDict):
2299    '''default doc string
2300   
2301    :param type name: description
2302   
2303    :returns: type name: description
2304   
2305    '''
2306    generalData = data['General']
2307    mapData = generalData['Map']
2308    dmin = mapData['Resolution']
2309    SGData = generalData['SGData']
2310    SGMT = np.array([ops[0].T for ops in SGData['SGOps']])
2311    SGT = np.array([ops[1] for ops in SGData['SGOps']])
2312    cell = generalData['Cell'][1:8]       
2313    A = G2lat.cell2A(cell[:6])
2314    Hmax = np.asarray(G2lat.getHKLmax(dmin,SGData,A),dtype='i')+1
2315    adjHKLmax(SGData,Hmax)
2316    Fhkl = np.zeros(shape=2*Hmax,dtype='c16')
2317#    Fhkl[0,0,0] = generalData['F000X']
2318    time0 = time.time()
2319    for iref,ref in enumerate(reflDict['RefList']):
2320        if ref[4] > dmin:
2321            Fosq,Fcsq,ph = ref[8:11]
2322            Uniq = np.inner(ref[:3],SGMT)
2323            Phi = np.inner(ref[:3],SGT)
2324            for i,hkl in enumerate(Uniq):        #uses uniq
2325                hkl = np.asarray(hkl,dtype='i')
2326                dp = 360.*Phi[i]                #and phi
2327                a = cosd(ph+dp)
2328                b = sind(ph+dp)
2329                phasep = complex(a,b)
2330                phasem = complex(a,-b)
2331                if 'Fobs' in mapData['MapType']:
2332                    F = np.where(Fosq>0.,np.sqrt(Fosq),0.)
2333                    h,k,l = hkl+Hmax
2334                    Fhkl[h,k,l] = F*phasep
2335                    h,k,l = -hkl+Hmax
2336                    Fhkl[h,k,l] = F*phasem
2337                elif 'Fcalc' in mapData['MapType']:
2338                    F = np.sqrt(Fcsq)
2339                    h,k,l = hkl+Hmax
2340                    Fhkl[h,k,l] = F*phasep
2341                    h,k,l = -hkl+Hmax
2342                    Fhkl[h,k,l] = F*phasem
2343                elif 'delt-F' in mapData['MapType']:
2344                    dF = np.where(Fosq>0.,np.sqrt(Fosq),0.)-np.sqrt(Fcsq)
2345                    h,k,l = hkl+Hmax
2346                    Fhkl[h,k,l] = dF*phasep
2347                    h,k,l = -hkl+Hmax
2348                    Fhkl[h,k,l] = dF*phasem
2349                elif '2*Fo-Fc' in mapData['MapType']:
2350                    F = 2.*np.where(Fosq>0.,np.sqrt(Fosq),0.)-np.sqrt(Fcsq)
2351                    h,k,l = hkl+Hmax
2352                    Fhkl[h,k,l] = F*phasep
2353                    h,k,l = -hkl+Hmax
2354                    Fhkl[h,k,l] = F*phasem
2355                elif 'Patterson' in mapData['MapType']:
2356                    h,k,l = hkl+Hmax
2357                    Fhkl[h,k,l] = complex(Fosq,0.)
2358                    h,k,l = -hkl+Hmax
2359                    Fhkl[h,k,l] = complex(Fosq,0.)
2360    rho = fft.fftn(fft.fftshift(Fhkl))/cell[6]
2361    print 'Fourier map time: %.4f'%(time.time()-time0),'no. elements: %d'%(Fhkl.size)
2362    mapData['Type'] = reflDict['Type']
2363    mapData['rho'] = np.real(rho)
2364    mapData['rhoMax'] = max(np.max(mapData['rho']),-np.min(mapData['rho']))
2365    mapData['minmax'] = [np.max(mapData['rho']),np.min(mapData['rho'])]
2366   
2367# map printing for testing purposes
2368def printRho(SGLaue,rho,rhoMax):                         
2369    '''default doc string
2370   
2371    :param type name: description
2372   
2373    :returns: type name: description
2374   
2375    '''
2376    dim = len(rho.shape)
2377    if dim == 2:
2378        ix,jy = rho.shape
2379        for j in range(jy):
2380            line = ''
2381            if SGLaue in ['3','3m1','31m','6/m','6/mmm']:
2382                line += (jy-j)*'  '
2383            for i in range(ix):
2384                r = int(100*rho[i,j]/rhoMax)
2385                line += '%4d'%(r)
2386            print line+'\n'
2387    else:
2388        ix,jy,kz = rho.shape
2389        for k in range(kz):
2390            print 'k = ',k
2391            for j in range(jy):
2392                line = ''
2393                if SGLaue in ['3','3m1','31m','6/m','6/mmm']:
2394                    line += (jy-j)*'  '
2395                for i in range(ix):
2396                    r = int(100*rho[i,j,k]/rhoMax)
2397                    line += '%4d'%(r)
2398                print line+'\n'
2399## keep this
2400               
2401def findOffset(SGData,A,Fhkl):   
2402    '''default doc string
2403   
2404    :param type name: description
2405   
2406    :returns: type name: description
2407   
2408    '''
2409    if SGData['SpGrp'] == 'P 1':
2410        return [0,0,0]   
2411    hklShape = Fhkl.shape
2412    hklHalf = np.array(hklShape)/2
2413    sortHKL = np.argsort(Fhkl.flatten())
2414    Fdict = {}
2415    for hkl in sortHKL:
2416        HKL = np.unravel_index(hkl,hklShape)
2417        F = Fhkl[HKL[0]][HKL[1]][HKL[2]]
2418        if F == 0.:
2419            break
2420        Fdict['%.6f'%(np.absolute(F))] = hkl
2421    Flist = np.flipud(np.sort(Fdict.keys()))
2422    F = str(1.e6)
2423    i = 0
2424    DH = []
2425    Dphi = []
2426    SGMT = np.array([ops[0].T for ops in SGData['SGOps']])
2427    SGT = np.array([ops[1] for ops in SGData['SGOps']])
2428    Hmax = 2*np.asarray(G2lat.getHKLmax(3.5,SGData,A),dtype='i')
2429    for F in Flist:
2430        hkl = np.unravel_index(Fdict[F],hklShape)
2431        if np.any(np.abs(hkl-hklHalf)-Hmax > 0):
2432            continue
2433        iabsnt,mulp,Uniq,Phi = G2spc.GenHKLf(list(hkl-hklHalf),SGData)
2434        Uniq = np.array(Uniq,dtype='i')
2435        Phi = np.array(Phi)
2436        Uniq = np.concatenate((Uniq,-Uniq))+hklHalf         # put in Friedel pairs & make as index to Farray
2437        Phi = np.concatenate((Phi,-Phi))                      # and their phase shifts
2438        Fh0 = Fhkl[hkl[0],hkl[1],hkl[2]]
2439        ang0 = np.angle(Fh0,deg=True)/360.
2440        for H,phi in zip(Uniq,Phi)[1:]:
2441            ang = (np.angle(Fhkl[H[0],H[1],H[2]],deg=True)/360.-phi)
2442            dH = H-hkl
2443            dang = ang-ang0
2444            DH.append(dH)
2445            Dphi.append((dang+.5) % 1.0)
2446        if i > 20 or len(DH) > 30:
2447            break
2448        i += 1
2449    DH = np.array(DH)
2450    print ' map offset no.of terms: %d from %d reflections'%(len(DH),len(Flist))
2451    Dphi = np.array(Dphi)
2452    steps = np.array(hklShape)
2453    X,Y,Z = np.mgrid[0:1:1./steps[0],0:1:1./steps[1],0:1:1./steps[2]]
2454    XYZ = np.array(zip(X.flatten(),Y.flatten(),Z.flatten()))
2455    Dang = (np.dot(XYZ,DH.T)+.5)%1.-Dphi
2456    Mmap = np.reshape(np.sum((Dang)**2,axis=1),newshape=steps)/len(DH)
2457    hist,bins = np.histogram(Mmap,bins=1000)
2458#    for i,item in enumerate(hist[:10]):
2459#        print item,bins[i]
2460    chisq = np.min(Mmap)
2461    DX = -np.array(np.unravel_index(np.argmin(Mmap),Mmap.shape))
2462    print ' map offset chi**2: %.3f, map offset: %d %d %d'%(chisq,DX[0],DX[1],DX[2])
2463#    print (np.dot(DX,DH.T)+.5)%1.-Dphi
2464    return DX
2465   
2466def ChargeFlip(data,reflDict,pgbar):
2467    '''default doc string
2468   
2469    :param type name: description
2470   
2471    :returns: type name: description
2472   
2473    '''
2474    generalData = data['General']
2475    mapData = generalData['Map']
2476    flipData = generalData['Flip']
2477    FFtable = {}
2478    if 'None' not in flipData['Norm element']:
2479        normElem = flipData['Norm element'].upper()
2480        FFs = G2el.GetFormFactorCoeff(normElem.split('+')[0].split('-')[0])
2481        for ff in FFs:
2482            if ff['Symbol'] == normElem:
2483                FFtable.update(ff)
2484    dmin = flipData['Resolution']
2485    SGData = generalData['SGData']
2486    SGMT = np.array([ops[0].T for ops in SGData['SGOps']])
2487    SGT = np.array([ops[1] for ops in SGData['SGOps']])
2488    cell = generalData['Cell'][1:8]       
2489    A = G2lat.cell2A(cell[:6])
2490    Vol = cell[6]
2491    im = 0
2492    if generalData['Type'] in ['modulated','magnetic',]:
2493        im = 1
2494    Hmax = np.asarray(G2lat.getHKLmax(dmin,SGData,A),dtype='i')+1
2495    adjHKLmax(SGData,Hmax)
2496    Ehkl = np.zeros(shape=2*Hmax,dtype='c16')       #2X64bits per complex no.
2497    time0 = time.time()
2498    for iref,ref in enumerate(reflDict['RefList']):
2499        dsp = ref[4+im]
2500        if im and ref[3]:   #skip super lattice reflections - result is 3D projection
2501            continue
2502        if dsp > dmin:
2503            ff = 0.1*Vol    #est. no. atoms for ~10A**3/atom
2504            if FFtable:
2505                SQ = 0.25/dsp**2
2506                ff *= G2el.ScatFac(FFtable,SQ)[0]
2507            if ref[8+im] > 0.:         #use only +ve Fobs**2
2508                E = np.sqrt(ref[8+im])/ff
2509            else:
2510                E = 0.
2511            ph = ref[10]
2512            ph = rn.uniform(0.,360.)
2513            Uniq = np.inner(ref[:3],SGMT)
2514            Phi = np.inner(ref[:3],SGT)
2515            for i,hkl in enumerate(Uniq):        #uses uniq
2516                hkl = np.asarray(hkl,dtype='i')
2517                dp = 360.*Phi[i]                #and phi
2518                a = cosd(ph+dp)
2519                b = sind(ph+dp)
2520                phasep = complex(a,b)
2521                phasem = complex(a,-b)
2522                h,k,l = hkl+Hmax
2523                Ehkl[h,k,l] = E*phasep
2524                h,k,l = -hkl+Hmax       #Friedel pair refl.
2525                Ehkl[h,k,l] = E*phasem
2526#    Ehkl[Hmax] = 0.00001           #this to preserve F[0,0,0]
2527    CEhkl = copy.copy(Ehkl)
2528    MEhkl = ma.array(Ehkl,mask=(Ehkl==0.0))
2529    Emask = ma.getmask(MEhkl)
2530    sumE = np.sum(ma.array(np.absolute(CEhkl),mask=Emask))
2531    Ncyc = 0
2532    old = np.seterr(all='raise')
2533    while True:       
2534        CErho = np.real(fft.fftn(fft.fftshift(CEhkl)))*(1.+0j)
2535        CEsig = np.std(CErho)
2536        CFrho = np.where(np.real(CErho) >= flipData['k-factor']*CEsig,CErho,-CErho)
2537        CFrho = np.where(np.real(CErho) <= flipData['k-Max']*CEsig,CFrho,-CFrho)      #solves U atom problem!
2538        CFhkl = fft.ifftshift(fft.ifftn(CFrho))
2539        CFhkl = np.where(CFhkl,CFhkl,1.0)           #avoid divide by zero
2540        phase = CFhkl/np.absolute(CFhkl)
2541        CEhkl = np.absolute(Ehkl)*phase
2542        Ncyc += 1
2543        sumCF = np.sum(ma.array(np.absolute(CFhkl),mask=Emask))
2544        DEhkl = np.absolute(np.absolute(Ehkl)/sumE-np.absolute(CFhkl)/sumCF)
2545        Rcf = min(100.,np.sum(ma.array(DEhkl,mask=Emask)*100.))
2546        if Rcf < 5.:
2547            break
2548        GoOn = pgbar.Update(Rcf,newmsg='%s%8.3f%s\n%s %d'%('Residual Rcf =',Rcf,'%','No.cycles = ',Ncyc))[0]
2549        if not GoOn or Ncyc > 10000:
2550            break
2551    np.seterr(**old)
2552    print ' Charge flip time: %.4f'%(time.time()-time0),'no. elements: %d'%(Ehkl.size)
2553    CErho = np.real(fft.fftn(fft.fftshift(CEhkl)))
2554    print ' No.cycles = ',Ncyc,'Residual Rcf =%8.3f%s'%(Rcf,'%')+' Map size:',CErho.shape
2555    roll = findOffset(SGData,A,CEhkl)               #CEhkl needs to be just the observed set, not the full set!
2556       
2557    mapData['Rcf'] = Rcf
2558    mapData['rho'] = np.roll(np.roll(np.roll(CErho,roll[0],axis=0),roll[1],axis=1),roll[2],axis=2)
2559    mapData['rhoMax'] = max(np.max(mapData['rho']),-np.min(mapData['rho']))
2560    mapData['minmax'] = [np.max(mapData['rho']),np.min(mapData['rho'])]
2561    mapData['Type'] = reflDict['Type']
2562    return mapData
2563   
2564def findSSOffset(SGData,SSGData,A,Fhklm):   
2565    '''default doc string
2566   
2567    :param type name: description
2568   
2569    :returns: type name: description
2570   
2571    '''
2572    if SGData['SpGrp'] == 'P 1':
2573        return [0,0,0,0]   
2574    hklmShape = Fhklm.shape
2575    hklmHalf = np.array(hklmShape)/2
2576    sortHKLM = np.argsort(Fhklm.flatten())
2577    Fdict = {}
2578    for hklm in sortHKLM:
2579        HKLM = np.unravel_index(hklm,hklmShape)
2580        F = Fhklm[HKLM[0]][HKLM[1]][HKLM[2]][HKLM[3]]
2581        if F == 0.:
2582            break
2583        Fdict['%.6f'%(np.absolute(F))] = hklm
2584    Flist = np.flipud(np.sort(Fdict.keys()))
2585    F = str(1.e6)
2586    i = 0
2587    DH = []
2588    Dphi = []
2589    SSGMT = np.array([ops[0].T for ops in SSGData['SSGOps']])
2590    SSGT = np.array([ops[1] for ops in SSGData['SSGOps']])
2591    Hmax = 2*np.asarray(G2lat.getHKLmax(3.5,SGData,A),dtype='i')
2592    for F in Flist:
2593        hklm = np.unravel_index(Fdict[F],hklmShape)
2594        if np.any(np.abs(hklm-hklmHalf)[:3]-Hmax > 0):
2595            continue
2596        Uniq = np.inner(hklm-hklmHalf,SSGMT)
2597        Phi = np.inner(hklm-hklmHalf,SSGT)
2598        Uniq = np.concatenate((Uniq,-Uniq))+hklmHalf         # put in Friedel pairs & make as index to Farray
2599        Phi = np.concatenate((Phi,-Phi))                      # and their phase shifts
2600        Fh0 = Fhklm[hklm[0],hklm[1],hklm[2],hklm[3]]
2601        ang0 = np.angle(Fh0,deg=True)/360.
2602        for H,phi in zip(Uniq,Phi)[1:]:
2603            ang = (np.angle(Fhklm[H[0],H[1],H[2],H[3]],deg=True)/360.-phi)
2604            dH = H-hklm
2605            dang = ang-ang0
2606            DH.append(dH)
2607            Dphi.append((dang+.5) % 1.0)
2608        if i > 20 or len(DH) > 30:
2609            break
2610        i += 1
2611    DH = np.array(DH)
2612    print ' map offset no.of terms: %d from %d reflections'%(len(DH),len(Flist))
2613    Dphi = np.array(Dphi)
2614    steps = np.array(hklmShape)
2615    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]]
2616    XYZT = np.array(zip(X.flatten(),Y.flatten(),Z.flatten(),T.flatten()))
2617    Dang = (np.dot(XYZT,DH.T)+.5)%1.-Dphi
2618    Mmap = np.reshape(np.sum((Dang)**2,axis=1),newshape=steps)/len(DH)
2619    hist,bins = np.histogram(Mmap,bins=1000)
2620#    for i,item in enumerate(hist[:10]):
2621#        print item,bins[i]
2622    chisq = np.min(Mmap)
2623    DX = -np.array(np.unravel_index(np.argmin(Mmap),Mmap.shape))
2624    print ' map offset chi**2: %.3f, map offset: %d %d %d %d'%(chisq,DX[0],DX[1],DX[2],DX[3])
2625#    print (np.dot(DX,DH.T)+.5)%1.-Dphi
2626    return DX
2627   
2628def SSChargeFlip(data,reflDict,pgbar):
2629    '''default doc string
2630   
2631    :param type name: description
2632   
2633    :returns: type name: description
2634   
2635    '''
2636    generalData = data['General']
2637    mapData = generalData['Map']
2638    map4DData = {}
2639    flipData = generalData['Flip']
2640    FFtable = {}
2641    if 'None' not in flipData['Norm element']:
2642        normElem = flipData['Norm element'].upper()
2643        FFs = G2el.GetFormFactorCoeff(normElem.split('+')[0].split('-')[0])
2644        for ff in FFs:
2645            if ff['Symbol'] == normElem:
2646                FFtable.update(ff)
2647    dmin = flipData['Resolution']
2648    SGData = generalData['SGData']
2649    SSGData = generalData['SSGData']
2650    SGMT = np.array([ops[0].T for ops in SGData['SGOps']])
2651    SGT = np.array([ops[1] for ops in SGData['SGOps']])
2652    SSGMT = np.array([ops[0].T for ops in SSGData['SSGOps']])
2653    SSGT = np.array([ops[1] for ops in SSGData['SSGOps']])
2654    cell = generalData['Cell'][1:8]       
2655    A = G2lat.cell2A(cell[:6])
2656    Vol = cell[6]
2657    maxM = generalData['SuperVec'][2]
2658    Hmax = np.asarray(G2lat.getHKLmax(dmin,SGData,A)+[maxM,],dtype='i')+1
2659    adjHKLmax(SGData,Hmax)
2660    Ehkl = np.zeros(shape=2*Hmax,dtype='c16')       #2X64bits per complex no.
2661    time0 = time.time()
2662    for iref,ref in enumerate(reflDict['RefList']):
2663        dsp = ref[5]
2664        if dsp > dmin:
2665            ff = 0.1*Vol    #est. no. atoms for ~10A**3/atom
2666            if FFtable:
2667                SQ = 0.25/dsp**2
2668                ff *= G2el.ScatFac(FFtable,SQ)[0]
2669            if ref[9] > 0.:         #use only +ve Fobs**2
2670                E = np.sqrt(ref[9])/ff
2671            else:
2672                E = 0.
2673            ph = ref[11]
2674            ph = rn.uniform(0.,360.)
2675            Uniq = np.inner(ref[:4],SSGMT)
2676            Phi = np.inner(ref[:4],SSGT)
2677            for i,hklm in enumerate(Uniq):        #uses uniq
2678                hklm = np.asarray(hklm,dtype='i')
2679                dp = 360.*Phi[i]                #and phi
2680                a = cosd(ph+dp)
2681                b = sind(ph+dp)
2682                phasep = complex(a,b)
2683                phasem = complex(a,-b)
2684                h,k,l,m = hklm+Hmax
2685                Ehkl[h,k,l,m] = E*phasep
2686                h,k,l,m = -hklm+Hmax       #Friedel pair refl.
2687                Ehkl[h,k,l,m] = E*phasem
2688#    Ehkl[Hmax] = 0.00001           #this to preserve F[0,0,0]
2689    CEhkl = copy.copy(Ehkl)
2690    MEhkl = ma.array(Ehkl,mask=(Ehkl==0.0))
2691    Emask = ma.getmask(MEhkl)
2692    sumE = np.sum(ma.array(np.absolute(CEhkl),mask=Emask))
2693    Ncyc = 0
2694    old = np.seterr(all='raise')
2695    while True:       
2696        CErho = np.real(fft.fftn(fft.fftshift(CEhkl)))*(1.+0j)
2697        CEsig = np.std(CErho)
2698        CFrho = np.where(np.real(CErho) >= flipData['k-factor']*CEsig,CErho,-CErho)
2699        CFrho = np.where(np.real(CErho) <= flipData['k-Max']*CEsig,CFrho,-CFrho)      #solves U atom problem!
2700        CFhkl = fft.ifftshift(fft.ifftn(CFrho))
2701        CFhkl = np.where(CFhkl,CFhkl,1.0)           #avoid divide by zero
2702        phase = CFhkl/np.absolute(CFhkl)
2703        CEhkl = np.absolute(Ehkl)*phase
2704        Ncyc += 1
2705        sumCF = np.sum(ma.array(np.absolute(CFhkl),mask=Emask))
2706        DEhkl = np.absolute(np.absolute(Ehkl)/sumE-np.absolute(CFhkl)/sumCF)
2707        Rcf = min(100.,np.sum(ma.array(DEhkl,mask=Emask)*100.))
2708        if Rcf < 5.:
2709            break
2710        GoOn = pgbar.Update(Rcf,newmsg='%s%8.3f%s\n%s %d'%('Residual Rcf =',Rcf,'%','No.cycles = ',Ncyc))[0]
2711        if not GoOn or Ncyc > 10000:
2712            break
2713    np.seterr(**old)
2714    print ' Charge flip time: %.4f'%(time.time()-time0),'no. elements: %d'%(Ehkl.size)
2715    CErho = np.real(fft.fftn(fft.fftshift(CEhkl[:,:,:,maxM+1])))
2716    SSrho = np.real(fft.fftn(fft.fftshift(CEhkl)))
2717    print ' No.cycles = ',Ncyc,'Residual Rcf =%8.3f%s'%(Rcf,'%')+' Map size:',CErho.shape
2718    roll = findSSOffset(SGData,SSGData,A,CEhkl)               #CEhkl needs to be just the observed set, not the full set!
2719
2720    mapData['Rcf'] = Rcf
2721    mapData['rho'] = np.roll(np.roll(np.roll(CErho,roll[0],axis=0),roll[1],axis=1),roll[2],axis=2)
2722    mapData['rhoMax'] = max(np.max(mapData['rho']),-np.min(mapData['rho']))
2723    mapData['minmax'] = [np.max(mapData['rho']),np.min(mapData['rho'])]
2724    mapData['Type'] = reflDict['Type']
2725
2726    map4DData['Rcf'] = Rcf
2727    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))
2728    map4DData['rhoMax'] = max(np.max(map4DData['rho']),-np.min(map4DData['rho']))
2729    map4DData['minmax'] = [np.max(map4DData['rho']),np.min(map4DData['rho'])]
2730    map4DData['Type'] = reflDict['Type']
2731    return mapData,map4DData
2732   
2733def getRho(xyz,mapData):
2734    ''' get scattering density at a point by 8-point interpolation
2735    param xyz: coordinate to be probed
2736    param: mapData: dict of map data
2737   
2738    :returns: density at xyz
2739    '''
2740    rollMap = lambda rho,roll: np.roll(np.roll(np.roll(rho,roll[0],axis=0),roll[1],axis=1),roll[2],axis=2)
2741    if not len(mapData):
2742        return 0.0
2743    rho = copy.copy(mapData['rho'])     #don't mess up original
2744    if not len(rho):
2745        return 0.0
2746    mapShape = np.array(rho.shape)
2747    mapStep = 1./mapShape
2748    X = np.array(xyz)%1.    #get into unit cell
2749    I = np.array(X*mapShape,dtype='int')
2750    D = X-I*mapStep         #position inside map cell
2751    D12 = D[0]*D[1]
2752    D13 = D[0]*D[2]
2753    D23 = D[1]*D[2]
2754    D123 = np.prod(D)
2755    Rho = rollMap(rho,-I)    #shifts map so point is in corner
2756    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]+  \
2757        Rho[1,1,1]*D123+Rho[0,1,1]*(D23-D123)+Rho[1,0,1]*(D13-D123)+Rho[1,1,0]*(D12-D123)+  \
2758        Rho[0,0,0]*(D12+D13+D23-D123)-Rho[0,0,1]*(D13+D23-D123)-    \
2759        Rho[0,1,0]*(D23+D12-D123)-Rho[1,0,0]*(D13+D12-D123)   
2760    return R
2761       
2762def SearchMap(generalData,drawingData,Neg=False):
2763    '''Does a search of a density map for peaks meeting the criterion of peak
2764    height is greater than mapData['cutOff']/100 of mapData['rhoMax'] where
2765    mapData is data['General']['mapData']; the map is also in mapData.
2766
2767    :param generalData: the phase data structure; includes the map
2768    :param drawingData: the drawing data structure
2769    :param Neg:  if True then search for negative peaks (i.e. H-atoms & neutron data)
2770
2771    :returns: (peaks,mags,dzeros) where
2772
2773        * peaks : ndarray
2774          x,y,z positions of the peaks found in the map
2775        * mags : ndarray
2776          the magnitudes of the peaks
2777        * dzeros : ndarray
2778          the distance of the peaks from  the unit cell origin
2779        * dcent : ndarray
2780          the distance of the peaks from  the unit cell center
2781
2782    '''       
2783    rollMap = lambda rho,roll: np.roll(np.roll(np.roll(rho,roll[0],axis=0),roll[1],axis=1),roll[2],axis=2)
2784   
2785    norm = 1./(np.sqrt(3.)*np.sqrt(2.*np.pi)**3)
2786   
2787#    def noDuplicate(xyz,peaks,Amat):
2788#        XYZ = np.inner(Amat,xyz)
2789#        if True in [np.allclose(XYZ,np.inner(Amat,peak),atol=0.5) for peak in peaks]:
2790#            print ' Peak',xyz,' <0.5A from another peak'
2791#            return False
2792#        return True
2793#                           
2794    def fixSpecialPos(xyz,SGData,Amat):
2795        equivs = G2spc.GenAtom(xyz,SGData,Move=True)
2796        X = []
2797        xyzs = [equiv[0] for equiv in equivs]
2798        for x in xyzs:
2799            if np.sqrt(np.sum(np.inner(Amat,xyz-x)**2,axis=0))<0.5:
2800                X.append(x)
2801        if len(X) > 1:
2802            return np.average(X,axis=0)
2803        else:
2804            return xyz
2805       
2806    def rhoCalc(parms,rX,rY,rZ,res,SGLaue):
2807        Mag,x0,y0,z0,sig = parms
2808        z = -((x0-rX)**2+(y0-rY)**2+(z0-rZ)**2)/(2.*sig**2)
2809#        return norm*Mag*np.exp(z)/(sig*res**3)     #not slower but some faults in LS
2810        return norm*Mag*(1.+z+z**2/2.)/(sig*res**3)
2811       
2812    def peakFunc(parms,rX,rY,rZ,rho,res,SGLaue):
2813        Mag,x0,y0,z0,sig = parms
2814        M = rho-rhoCalc(parms,rX,rY,rZ,res,SGLaue)
2815        return M
2816       
2817    def peakHess(parms,rX,rY,rZ,rho,res,SGLaue):
2818        Mag,x0,y0,z0,sig = parms
2819        dMdv = np.zeros(([5,]+list(rX.shape)))
2820        delt = .01
2821        for i in range(5):
2822            parms[i] -= delt
2823            rhoCm = rhoCalc(parms,rX,rY,rZ,res,SGLaue)
2824            parms[i] += 2.*delt
2825            rhoCp = rhoCalc(parms,rX,rY,rZ,res,SGLaue)
2826            parms[i] -= delt
2827            dMdv[i] = (rhoCp-rhoCm)/(2.*delt)
2828        rhoC = rhoCalc(parms,rX,rY,rZ,res,SGLaue)
2829        Vec = np.sum(np.sum(np.sum(dMdv*(rho-rhoC),axis=3),axis=2),axis=1)
2830        dMdv = np.reshape(dMdv,(5,rX.size))
2831        Hess = np.inner(dMdv,dMdv)
2832       
2833        return Vec,Hess
2834       
2835    phaseName = generalData['Name']
2836    SGData = generalData['SGData']
2837    Amat,Bmat = G2lat.cell2AB(generalData['Cell'][1:7])
2838    peaks = []
2839    mags = []
2840    dzeros = []
2841    dcent = []
2842    try:
2843        mapData = generalData['Map']
2844        contLevel = mapData['cutOff']*mapData['rhoMax']/100.
2845        if Neg:
2846            rho = -copy.copy(mapData['rho'])     #flip +/-
2847        else:
2848            rho = copy.copy(mapData['rho'])     #don't mess up original
2849        mapHalf = np.array(rho.shape)/2
2850        res = mapData['Resolution']
2851        incre = np.array(rho.shape,dtype=np.float)
2852        step = max(1.0,1./res)+1
2853        steps = np.array(3*[step,])
2854    except KeyError:
2855        print '**** ERROR - Fourier map not defined'
2856        return peaks,mags
2857    rhoMask = ma.array(rho,mask=(rho<contLevel))
2858    indices = (-1,0,1)
2859    rolls = np.array([[h,k,l] for h in indices for k in indices for l in indices])
2860    for roll in rolls:
2861        if np.any(roll):
2862            rhoMask = ma.array(rhoMask,mask=(rhoMask-rollMap(rho,roll)<=0.))
2863    indx = np.transpose(rhoMask.nonzero())
2864    peaks = indx/incre
2865    mags = rhoMask[rhoMask.nonzero()]
2866    for i,[ind,peak,mag] in enumerate(zip(indx,peaks,mags)):
2867        rho = rollMap(rho,ind)
2868        rMM = mapHalf-steps
2869        rMP = mapHalf+steps+1
2870        rhoPeak = rho[rMM[0]:rMP[0],rMM[1]:rMP[1],rMM[2]:rMP[2]]
2871        peakInt = np.sum(rhoPeak)*res**3
2872        rX,rY,rZ = np.mgrid[rMM[0]:rMP[0],rMM[1]:rMP[1],rMM[2]:rMP[2]]
2873        x0 = [peakInt,mapHalf[0],mapHalf[1],mapHalf[2],2.0]          #magnitude, position & width(sig)
2874        result = HessianLSQ(peakFunc,x0,Hess=peakHess,
2875            args=(rX,rY,rZ,rhoPeak,res,SGData['SGLaue']),ftol=.01,maxcyc=10)
2876        x1 = result[0]
2877        if not np.any(x1 < 0):
2878            mag = x1[0]
2879            peak = (np.array(x1[1:4])-ind)/incre
2880        peak = fixSpecialPos(peak,SGData,Amat)
2881        rho = rollMap(rho,-ind)
2882    cent = np.ones(3)*.5     
2883    dzeros = np.sqrt(np.sum(np.inner(Amat,peaks)**2,axis=0))
2884    dcent = np.sqrt(np.sum(np.inner(Amat,peaks-cent)**2,axis=0))
2885    if Neg:     #want negative magnitudes for negative peaks
2886        return np.array(peaks),-np.array([mags,]).T,np.array([dzeros,]).T,np.array([dcent,]).T
2887    else:
2888        return np.array(peaks),np.array([mags,]).T,np.array([dzeros,]).T,np.array([dcent,]).T
2889   
2890def sortArray(data,pos,reverse=False):
2891    '''data is a list of items
2892    sort by pos in list; reverse if True
2893    '''
2894    T = []
2895    for i,M in enumerate(data):
2896        try:
2897            T.append((M[pos],i))
2898        except IndexError:
2899            return data
2900    D = dict(zip(T,data))
2901    T.sort()
2902    if reverse:
2903        T.reverse()
2904    X = []
2905    for key in T:
2906        X.append(D[key])
2907    return X
2908
2909def PeaksEquiv(data,Ind):
2910    '''Find the equivalent map peaks for those selected. Works on the
2911    contents of data['Map Peaks'].
2912
2913    :param data: the phase data structure
2914    :param list Ind: list of selected peak indices
2915    :returns: augmented list of peaks including those related by symmetry to the
2916      ones in Ind
2917
2918    '''       
2919    def Duplicate(xyz,peaks,Amat):
2920        if True in [np.allclose(np.inner(Amat,xyz),np.inner(Amat,peak),atol=0.5) for peak in peaks]:
2921            return True
2922        return False
2923                           
2924    generalData = data['General']
2925    cell = generalData['Cell'][1:7]
2926    Amat,Bmat = G2lat.cell2AB(generalData['Cell'][1:7])
2927    A = G2lat.cell2A(cell)
2928    SGData = generalData['SGData']
2929    mapPeaks = data['Map Peaks']
2930    XYZ = np.array([xyz[1:4] for xyz in mapPeaks])
2931    Indx = {}
2932    for ind in Ind:
2933        xyz = np.array(mapPeaks[ind][1:4])
2934        xyzs = np.array([equiv[0] for equiv in G2spc.GenAtom(xyz,SGData,Move=True)])
2935        for jnd,xyz in enumerate(XYZ):       
2936            Indx[jnd] = Duplicate(xyz,xyzs,Amat)
2937    Ind = []
2938    for ind in Indx:
2939        if Indx[ind]:
2940            Ind.append(ind)
2941    return Ind
2942               
2943def PeaksUnique(data,Ind):
2944    '''Finds the symmetry unique set of peaks from those selected. Works on the
2945    contents of data['Map Peaks'].
2946
2947    :param data: the phase data structure
2948    :param list Ind: list of selected peak indices
2949    :returns: the list of symmetry unique peaks from among those given in Ind
2950
2951    '''       
2952#    XYZE = np.array([[equiv[0] for equiv in G2spc.GenAtom(xyz[1:4],SGData,Move=True)] for xyz in mapPeaks]) #keep this!!
2953
2954    def noDuplicate(xyz,peaks,Amat):
2955        if True in [np.allclose(np.inner(Amat,xyz),np.inner(Amat,peak),atol=0.5) for peak in peaks]:
2956            return False
2957        return True
2958                           
2959    generalData = data['General']
2960    cell = generalData['Cell'][1:7]
2961    Amat,Bmat = G2lat.cell2AB(generalData['Cell'][1:7])
2962    A = G2lat.cell2A(cell)
2963    SGData = generalData['SGData']
2964    mapPeaks = data['Map Peaks']
2965    Indx = {}
2966    XYZ = {}
2967    for ind in Ind:
2968        XYZ[ind] = np.array(mapPeaks[ind][1:4])
2969        Indx[ind] = True
2970    for ind in Ind:
2971        if Indx[ind]:
2972            xyz = XYZ[ind]
2973            for jnd in Ind:
2974                if ind != jnd and Indx[jnd]:                       
2975                    Equiv = G2spc.GenAtom(XYZ[jnd],SGData,Move=True)
2976                    xyzs = np.array([equiv[0] for equiv in Equiv])
2977                    Indx[jnd] = noDuplicate(xyz,xyzs,Amat)
2978    Ind = []
2979    for ind in Indx:
2980        if Indx[ind]:
2981            Ind.append(ind)
2982    return Ind
2983   
2984################################################################################
2985##### single peak fitting profile fxn stuff
2986################################################################################
2987
2988def getCWsig(ins,pos):
2989    '''get CW peak profile sigma
2990   
2991    :param dict ins: instrument parameters with at least 'U', 'V', & 'W'
2992      as values only
2993    :param float pos: 2-theta of peak
2994    :returns: float getCWsig: peak sigma
2995   
2996    '''
2997    tp = tand(pos/2.0)
2998    return ins['U']*tp**2+ins['V']*tp+ins['W']
2999   
3000def getCWsigDeriv(pos):
3001    '''get derivatives of CW peak profile sigma wrt U,V, & W
3002   
3003    :param float pos: 2-theta of peak
3004   
3005    :returns: list getCWsigDeriv: d(sig)/dU, d(sig)/dV & d(sig)/dW
3006   
3007    '''
3008    tp = tand(pos/2.0)
3009    return tp**2,tp,1.0
3010   
3011def getCWgam(ins,pos):
3012    '''get CW peak profile gamma
3013   
3014    :param dict ins: instrument parameters with at least 'X' & 'Y'
3015      as values only
3016    :param float pos: 2-theta of peak
3017    :returns: float getCWgam: peak gamma
3018   
3019    '''
3020    return ins['X']/cosd(pos/2.0)+ins['Y']*tand(pos/2.0)
3021   
3022def getCWgamDeriv(pos):
3023    '''get derivatives of CW peak profile gamma wrt X & Y
3024   
3025    :param float pos: 2-theta of peak
3026   
3027    :returns: list getCWgamDeriv: d(gam)/dX & d(gam)/dY
3028   
3029    '''
3030    return 1./cosd(pos/2.0),tand(pos/2.0)
3031   
3032def getTOFsig(ins,dsp):
3033    '''get TOF peak profile sigma
3034   
3035    :param dict ins: instrument parameters with at least 'sig-0', 'sig-1' & 'sig-q'
3036      as values only
3037    :param float dsp: d-spacing of peak
3038   
3039    :returns: float getTOFsig: peak sigma
3040   
3041    '''
3042    return ins['sig-0']+ins['sig-1']*dsp**2+ins['sig-2']*dsp**4+ins['sig-q']/dsp**2
3043   
3044def getTOFsigDeriv(dsp):
3045    '''get derivatives of TOF peak profile gamma wrt sig-0, sig-1, & sig-q
3046   
3047    :param float dsp: d-spacing of peak
3048   
3049    :returns: list getTOFsigDeriv: d(sig0/d(sig-0), d(sig)/d(sig-1) & d(sig)/d(sig-q)
3050   
3051    '''
3052    return 1.0,dsp**2,dsp**4,1./dsp**2
3053   
3054def getTOFgamma(ins,dsp):
3055    '''get TOF peak profile gamma
3056   
3057    :param dict ins: instrument parameters with at least 'X' & 'Y'
3058      as values only
3059    :param float dsp: d-spacing of peak
3060   
3061    :returns: float getTOFgamma: peak gamma
3062   
3063    '''
3064    return ins['X']*dsp+ins['Y']*dsp**2
3065   
3066def getTOFgammaDeriv(dsp):
3067    '''get derivatives of TOF peak profile gamma wrt X & Y
3068   
3069    :param float dsp: d-spacing of peak
3070   
3071    :returns: list getTOFgammaDeriv: d(gam)/dX & d(gam)/dY
3072   
3073    '''
3074    return dsp,dsp**2
3075   
3076def getTOFbeta(ins,dsp):
3077    '''get TOF peak profile beta
3078   
3079    :param dict ins: instrument parameters with at least 'beat-0', 'beta-1' & 'beta-q'
3080      as values only
3081    :param float dsp: d-spacing of peak
3082   
3083    :returns: float getTOFbeta: peak beat
3084   
3085    '''
3086    return ins['beta-0']+ins['beta-1']/dsp**4+ins['beta-q']/dsp**2
3087   
3088def getTOFbetaDeriv(dsp):
3089    '''get derivatives of TOF peak profile beta wrt beta-0, beta-1, & beat-q
3090   
3091    :param float dsp: d-spacing of peak
3092   
3093    :returns: list getTOFbetaDeriv: d(beta)/d(beat-0), d(beta)/d(beta-1) & d(beta)/d(beta-q)
3094   
3095    '''
3096    return 1.0,1./dsp**4,1./dsp**2
3097   
3098def getTOFalpha(ins,dsp):
3099    '''get TOF peak profile alpha
3100   
3101    :param dict ins: instrument parameters with at least 'alpha'
3102      as values only
3103    :param float dsp: d-spacing of peak
3104   
3105    :returns: flaot getTOFalpha: peak alpha
3106   
3107    '''
3108    return ins['alpha']/dsp
3109   
3110def getTOFalphaDeriv(dsp):
3111    '''get derivatives of TOF peak profile beta wrt alpha
3112   
3113    :param float dsp: d-spacing of peak
3114   
3115    :returns: float getTOFalphaDeriv: d(alp)/d(alpha)
3116   
3117    '''
3118    return 1./dsp
3119   
3120def setPeakparms(Parms,Parms2,pos,mag,ifQ=False,useFit=False):
3121    '''set starting peak parameters for single peak fits from plot selection or auto selection
3122   
3123    :param dict Parms: instrument parameters dictionary
3124    :param dict Parms2: table lookup for TOF profile coefficients
3125    :param float pos: peak position in 2-theta, TOF or Q (ifQ=True)
3126    :param float mag: peak top magnitude from pick
3127    :param bool ifQ: True if pos in Q
3128    :param bool useFit: True if use fitted CW Parms values (not defaults)
3129   
3130    :returns: list XY: peak list entry:
3131        for CW: [pos,0,mag,1,sig,0,gam,0]
3132        for TOF: [pos,0,mag,1,alp,0,bet,0,sig,0,gam,0]
3133        NB: mag refinement set by default, all others off
3134   
3135    '''
3136    ind = 0
3137    if useFit:
3138        ind = 1
3139    ins = {}
3140    if 'C' in Parms['Type'][0]:                            #CW data - TOF later in an elif
3141        for x in ['U','V','W','X','Y']:
3142            ins[x] = Parms[x][ind]
3143        if ifQ:                              #qplot - convert back to 2-theta
3144            pos = 2.0*asind(pos*wave/(4*math.pi))
3145        sig = getCWsig(ins,pos)
3146        gam = getCWgam(ins,pos)           
3147        XY = [pos,0, mag,1, sig,0, gam,0]       #default refine intensity 1st
3148    else:
3149        if ifQ:
3150            dsp = 2.*np.pi/pos
3151            pos = Parms['difC']*dsp
3152        else:
3153            dsp = pos/Parms['difC'][1]
3154        if 'Pdabc' in Parms2:
3155            for x in ['sig-0','sig-1','sig-2','sig-q','X','Y']:
3156                ins[x] = Parms[x][ind]
3157            Pdabc = Parms2['Pdabc'].T
3158            alp = np.interp(dsp,Pdabc[0],Pdabc[1])
3159            bet = np.interp(dsp,Pdabc[0],Pdabc[2])
3160        else:
3161            for x in ['alpha','beta-0','beta-1','beta-q','sig-0','sig-1','sig-2','sig-q','X','Y']:
3162                ins[x] = Parms[x][ind]
3163            alp = getTOFalpha(ins,dsp)
3164            bet = getTOFbeta(ins,dsp)
3165        sig = getTOFsig(ins,dsp)
3166        gam = getTOFgamma(ins,dsp)
3167        XY = [pos,0,mag,1,alp,0,bet,0,sig,0,gam,0]
3168    return XY
3169   
3170################################################################################
3171##### MC/SA stuff
3172################################################################################
3173
3174#scipy/optimize/anneal.py code modified by R. Von Dreele 2013
3175# Original Author: Travis Oliphant 2002
3176# Bug-fixes in 2006 by Tim Leslie
3177
3178
3179import numpy
3180from numpy import asarray, tan, exp, ones, squeeze, sign, \
3181     all, log, sqrt, pi, shape, array, minimum, where
3182from numpy import random
3183
3184#__all__ = ['anneal']
3185
3186_double_min = numpy.finfo(float).min
3187_double_max = numpy.finfo(float).max
3188class base_schedule(object):
3189    def __init__(self):
3190        self.dwell = 20
3191        self.learn_rate = 0.5
3192        self.lower = -10
3193        self.upper = 10
3194        self.Ninit = 50
3195        self.accepted = 0
3196        self.tests = 0
3197        self.feval = 0
3198        self.k = 0
3199        self.T = None
3200
3201    def init(self, **options):
3202        self.__dict__.update(options)
3203        self.lower = asarray(self.lower)
3204        self.lower = where(self.lower == numpy.NINF, -_double_max, self.lower)
3205        self.upper = asarray(self.upper)
3206        self.upper = where(self.upper == numpy.PINF, _double_max, self.upper)
3207        self.k = 0
3208        self.accepted = 0
3209        self.feval = 0
3210        self.tests = 0
3211
3212    def getstart_temp(self, best_state):
3213        """ Find a matching starting temperature and starting parameters vector
3214        i.e. find x0 such that func(x0) = T0.
3215
3216        Parameters
3217        ----------
3218        best_state : _state
3219            A _state object to store the function value and x0 found.
3220
3221        returns
3222        -------
3223        x0 : array
3224            The starting parameters vector.
3225        """
3226
3227        assert(not self.dims is None)
3228        lrange = self.lower
3229        urange = self.upper
3230        fmax = _double_min
3231        fmin = _double_max
3232        for _ in range(self.Ninit):
3233            x0 = random.uniform(size=self.dims)*(urange-lrange) + lrange
3234            fval = self.func(x0, *self.args)
3235            self.feval += 1
3236            if fval > fmax:
3237                fmax = fval
3238            if fval < fmin:
3239                fmin = fval
3240                best_state.cost = fval
3241                best_state.x = array(x0)
3242
3243        self.T0 = (fmax-fmin)*1.5
3244        return best_state.x
3245       
3246    def set_range(self,x0,frac):
3247        delrange = frac*(self.upper-self.lower)
3248        self.upper = x0+delrange
3249        self.lower = x0-delrange
3250
3251    def accept_test(self, dE):
3252        T = self.T
3253        self.tests += 1
3254        if dE < 0:
3255            self.accepted += 1
3256            return 1
3257        p = exp(-dE*1.0/self.boltzmann/T)
3258        if (p > random.uniform(0.0, 1.0)):
3259            self.accepted += 1
3260            return 1
3261        return 0
3262
3263    def update_guess(self, x0):
3264        return np.squeeze(np.random.uniform(0.,1.,size=self.dims))*(self.upper-self.lower)+self.lower
3265
3266    def update_temp(self, x0):
3267        pass
3268
3269
3270#  A schedule due to Lester Ingber modified to use bounds - OK
3271class fast_sa(base_schedule):
3272    def init(self, **options):
3273        self.__dict__.update(options)
3274        if self.m is None:
3275            self.m = 1.0
3276        if self.n is None:
3277            self.n = 1.0
3278        self.c = self.m * exp(-self.n * self.quench)
3279
3280    def update_guess(self, x0):
3281        x0 = asarray(x0)
3282        u = squeeze(random.uniform(0.0, 1.0, size=self.dims))
3283        T = self.T
3284        xc = (sign(u-0.5)*T*((1+1.0/T)**abs(2*u-1)-1.0)+1.0)/2.0
3285        xnew = xc*(self.upper - self.lower)+self.lower
3286        return xnew
3287#        y = sign(u-0.5)*T*((1+1.0/T)**abs(2*u-1)-1.0)
3288#        xc = y*(self.upper - self.lower)
3289#        xnew = x0 + xc
3290#        return xnew
3291
3292    def update_temp(self):
3293        self.T = self.T0*exp(-self.c * self.k**(self.quench))
3294        self.k += 1
3295        return
3296
3297class cauchy_sa(base_schedule):     #modified to use bounds - not good
3298    def update_guess(self, x0):
3299        x0 = asarray(x0)
3300        numbers = squeeze(random.uniform(-pi/4, pi/4, size=self.dims))
3301        xc = (1.+(self.learn_rate * self.T * tan(numbers))%1.)
3302        xnew = xc*(self.upper - self.lower)+self.lower
3303        return xnew
3304#        numbers = squeeze(random.uniform(-pi/2, pi/2, size=self.dims))
3305#        xc = self.learn_rate * self.T * tan(numbers)
3306#        xnew = x0 + xc
3307#        return xnew
3308
3309    def update_temp(self):
3310        self.T = self.T0/(1+self.k)
3311        self.k += 1
3312        return
3313
3314class boltzmann_sa(base_schedule):
3315#    def update_guess(self, x0):
3316#        std = minimum(sqrt(self.T)*ones(self.dims), (self.upper-self.lower)/3.0/self.learn_rate)
3317#        x0 = asarray(x0)
3318#        xc = squeeze(random.normal(0, 1.0, size=self.dims))
3319#
3320#        xnew = x0 + xc*std*self.learn_rate
3321#        return xnew
3322
3323    def update_temp(self):
3324        self.k += 1
3325        self.T = self.T0 / log(self.k+1.0)
3326        return
3327
3328class log_sa(base_schedule):        #OK
3329
3330    def init(self,**options):
3331        self.__dict__.update(options)
3332       
3333    def update_guess(self,x0):     #same as default
3334        return np.squeeze(np.random.uniform(0.,1.,size=self.dims))*(self.upper-self.lower)+self.lower
3335       
3336    def update_temp(self):
3337        self.k += 1
3338        self.T = self.T0*self.slope**self.k
3339       
3340class _state(object):
3341    def __init__(self):
3342        self.x = None
3343        self.cost = None
3344
3345# TODO:
3346#     allow for general annealing temperature profile
3347#     in that case use update given by alpha and omega and
3348#     variation of all previous updates and temperature?
3349
3350# Simulated annealing
3351
3352def anneal(func, x0, args=(), schedule='fast', full_output=0,
3353           T0=None, Tf=1e-12, maxeval=None, maxaccept=None, maxiter=400,
3354           boltzmann=1.0, learn_rate=0.5, feps=1e-6, quench=1.0, m=1.0, n=1.0,
3355           lower=-100, upper=100, dwell=50, slope=0.9,ranStart=False,
3356           ranRange=0.10,autoRan=False,dlg=None):
3357    """Minimize a function using simulated annealing.
3358
3359    Schedule is a schedule class implementing the annealing schedule.
3360    Available ones are 'fast', 'cauchy', 'boltzmann'
3361
3362    :param callable func: f(x, \*args)
3363        Function to be optimized.
3364    :param ndarray x0:
3365        Initial guess.
3366    :param tuple args:
3367        Extra parameters to `func`.
3368    :param base_schedule schedule:
3369        Annealing schedule to use (a class).
3370    :param bool full_output:
3371        Whether to return optional outputs.
3372    :param float T0:
3373        Initial Temperature (estimated as 1.2 times the largest
3374        cost-function deviation over random points in the range).
3375    :param float Tf:
3376        Final goal temperature.
3377    :param int maxeval:
3378        Maximum function evaluations.
3379    :param int maxaccept:
3380        Maximum changes to accept.
3381    :param int maxiter:
3382        Maximum cooling iterations.
3383    :param float learn_rate:
3384        Scale constant for adjusting guesses.
3385    :param float boltzmann:
3386        Boltzmann constant in acceptance test
3387        (increase for less stringent test at each temperature).
3388    :param float feps:
3389        Stopping relative error tolerance for the function value in
3390        last four coolings.
3391    :param float quench,m,n:
3392        Parameters to alter fast_sa schedule.
3393    :param float/ndarray lower,upper:
3394        Lower and upper bounds on `x`.
3395    :param int dwell:
3396        The number of times to search the space at each temperature.
3397    :param float slope:
3398        Parameter for log schedule
3399    :param bool ranStart=False:
3400        True for set 10% of ranges about x
3401
3402    :returns: (xmin, Jmin, T, feval, iters, accept, retval) where
3403
3404     * xmin (ndarray): Point giving smallest value found.
3405     * Jmin (float): Minimum value of function found.
3406     * T (float): Final temperature.
3407     * feval (int): Number of function evaluations.
3408     * iters (int): Number of cooling iterations.
3409     * accept (int): Number of tests accepted.
3410     * retval (int): Flag indicating stopping condition:
3411
3412              *  0: Points no longer changing
3413              *  1: Cooled to final temperature
3414              *  2: Maximum function evaluations
3415              *  3: Maximum cooling iterations reached
3416              *  4: Maximum accepted query locations reached
3417              *  5: Final point not the minimum amongst encountered points
3418
3419    *Notes*:
3420    Simulated annealing is a random algorithm which uses no derivative
3421    information from the function being optimized. In practice it has
3422    been more useful in discrete optimization than continuous
3423    optimization, as there are usually better algorithms for continuous
3424    optimization problems.
3425
3426    Some experimentation by trying the difference temperature
3427    schedules and altering their parameters is likely required to
3428    obtain good performance.
3429
3430    The randomness in the algorithm comes from random sampling in numpy.
3431    To obtain the same results you can call numpy.random.seed with the
3432    same seed immediately before calling scipy.optimize.anneal.
3433
3434    We give a brief description of how the three temperature schedules
3435    generate new points and vary their temperature. Temperatures are
3436    only updated with iterations in the outer loop. The inner loop is
3437    over xrange(dwell), and new points are generated for
3438    every iteration in the inner loop. (Though whether the proposed
3439    new points are accepted is probabilistic.)
3440
3441    For readability, let d denote the dimension of the inputs to func.
3442    Also, let x_old denote the previous state, and k denote the
3443    iteration number of the outer loop. All other variables not
3444    defined below are input variables to scipy.optimize.anneal itself.
3445
3446    In the 'fast' schedule the updates are ::
3447
3448        u ~ Uniform(0, 1, size=d)
3449        y = sgn(u - 0.5) * T * ((1+ 1/T)**abs(2u-1) -1.0)
3450        xc = y * (upper - lower)
3451        x_new = x_old + xc
3452
3453        c = n * exp(-n * quench)
3454        T_new = T0 * exp(-c * k**quench)
3455
3456
3457    In the 'cauchy' schedule the updates are ::
3458
3459        u ~ Uniform(-pi/2, pi/2, size=d)
3460        xc = learn_rate * T * tan(u)
3461        x_new = x_old + xc
3462
3463        T_new = T0 / (1+k)
3464
3465    In the 'boltzmann' schedule the updates are ::
3466
3467        std = minimum( sqrt(T) * ones(d), (upper-lower) / (3*learn_rate) )
3468        y ~ Normal(0, std, size=d)
3469        x_new = x_old + learn_rate * y
3470
3471        T_new = T0 / log(1+k)
3472
3473    """
3474    x0 = asarray(x0)
3475    lower = asarray(lower)
3476    upper = asarray(upper)
3477
3478    schedule = eval(schedule+'_sa()')
3479    #   initialize the schedule
3480    schedule.init(dims=shape(x0),func=func,args=args,boltzmann=boltzmann,T0=T0,
3481                  learn_rate=learn_rate, lower=lower, upper=upper,
3482                  m=m, n=n, quench=quench, dwell=dwell, slope=slope)
3483
3484    current_state, last_state, best_state = _state(), _state(), _state()
3485    if ranStart:
3486        schedule.set_range(x0,ranRange)
3487    if T0 is None:
3488        x0 = schedule.getstart_temp(best_state)
3489    else:
3490        x0 = random.uniform(size=len(x0))*(upper-lower) + lower
3491        best_state.x = None
3492        best_state.cost = numpy.Inf
3493
3494    last_state.x = asarray(x0).copy()
3495    fval = func(x0,*args)
3496    schedule.feval += 1
3497    last_state.cost = fval
3498    if last_state.cost < best_state.cost:
3499        best_state.cost = fval
3500        best_state.x = asarray(x0).copy()
3501    schedule.T = schedule.T0
3502    fqueue = [100, 300, 500, 700]
3503    iters = 1
3504    keepGoing = True
3505    bestn = 0
3506    while keepGoing:
3507        retval = 0
3508        for n in xrange(dwell):
3509            current_state.x = schedule.update_guess(last_state.x)
3510            current_state.cost = func(current_state.x,*args)
3511            schedule.feval += 1
3512
3513            dE = current_state.cost - last_state.cost
3514            if schedule.accept_test(dE):
3515                last_state.x = current_state.x.copy()
3516                last_state.cost = current_state.cost
3517                if last_state.cost < best_state.cost:
3518                    best_state.x = last_state.x.copy()
3519                    best_state.cost = last_state.cost
3520                    bestn = n
3521                    if best_state.cost < 1.0 and autoRan:
3522                        schedule.set_range(x0,best_state.cost/2.)                       
3523        if dlg:
3524            GoOn = dlg.Update(min(100.,best_state.cost*100),
3525                newmsg='%s%8.5f, %s%d\n%s%8.4f%s'%('Temperature =',schedule.T, \
3526                    'Best trial:',bestn,  \
3527                    'MC/SA Residual:',best_state.cost*100,'%', \
3528                    ))[0]
3529            if not GoOn:
3530                best_state.x = last_state.x.copy()
3531                best_state.cost = last_state.cost
3532                retval = 5
3533        schedule.update_temp()
3534        iters += 1
3535        # Stopping conditions
3536        # 0) last saved values of f from each cooling step
3537        #     are all very similar (effectively cooled)
3538        # 1) Tf is set and we are below it
3539        # 2) maxeval is set and we are past it
3540        # 3) maxiter is set and we are past it
3541        # 4) maxaccept is set and we are past it
3542        # 5) user canceled run via progress bar
3543
3544        fqueue.append(squeeze(last_state.cost))
3545        fqueue.pop(0)
3546        af = asarray(fqueue)*1.0
3547        if retval == 5:
3548            print ' User terminated run; incomplete MC/SA'
3549            keepGoing = False
3550            break
3551        if all(abs((af-af[0])/af[0]) < feps):
3552            retval = 0
3553            if abs(af[-1]-best_state.cost) > feps*10:
3554                retval = 5
3555#                print "Warning: Cooled to %f at %s but this is not" \
3556#                      % (squeeze(last_state.cost), str(squeeze(last_state.x))) \
3557#                      + " the smallest point found."
3558            break
3559        if (Tf is not None) and (schedule.T < Tf):
3560            retval = 1
3561            break
3562        if (maxeval is not None) and (schedule.feval > maxeval):
3563            retval = 2
3564            break
3565        if (iters > maxiter):
3566            print "Warning: Maximum number of iterations exceeded."
3567            retval = 3
3568            break
3569        if (maxaccept is not None) and (schedule.accepted > maxaccept):
3570            retval = 4
3571            break
3572
3573    if full_output:
3574        return best_state.x, best_state.cost, schedule.T, \
3575               schedule.feval, iters, schedule.accepted, retval
3576    else:
3577        return best_state.x, retval
3578
3579def worker(iCyc,data,RBdata,reflType,reflData,covData,out_q,nprocess=-1):
3580    outlist = []
3581    random.seed(int(time.time())%100000+nprocess)   #make sure each process has a different random start
3582    for n in range(iCyc):
3583        result = mcsaSearch(data,RBdata,reflType,reflData,covData,None)
3584        outlist.append(result[0])
3585        print ' MC/SA residual: %.3f%% structure factor time: %.3f'%(100*result[0][2],result[1])
3586    out_q.put(outlist)
3587
3588def MPmcsaSearch(nCyc,data,RBdata,reflType,reflData,covData):
3589    import multiprocessing as mp
3590   
3591    nprocs = mp.cpu_count()
3592    out_q = mp.Queue()
3593    procs = []
3594    iCyc = np.zeros(nprocs)
3595    for i in range(nCyc):
3596        iCyc[i%nprocs] += 1
3597    for i in range(nprocs):
3598        p = mp.Process(target=worker,args=(int(iCyc[i]),data,RBdata,reflType,reflData,covData,out_q,i))
3599        procs.append(p)
3600        p.start()
3601    resultlist = []
3602    for i in range(nprocs):
3603        resultlist += out_q.get()
3604    for p in procs:
3605        p.join()
3606    return resultlist
3607
3608def mcsaSearch(data,RBdata,reflType,reflData,covData,pgbar):
3609    '''default doc string
3610   
3611    :param type name: description
3612   
3613    :returns: type name: description
3614   
3615    '''
3616   
3617    global tsum
3618    tsum = 0.
3619   
3620    def getMDparms(item,pfx,parmDict,varyList):
3621        parmDict[pfx+'MDaxis'] = item['axis']
3622        parmDict[pfx+'MDval'] = item['Coef'][0]
3623        if item['Coef'][1]:
3624            varyList += [pfx+'MDval',]
3625            limits = item['Coef'][2]
3626            lower.append(limits[0])
3627            upper.append(limits[1])
3628                       
3629    def getAtomparms(item,pfx,aTypes,SGData,parmDict,varyList):
3630        parmDict[pfx+'Atype'] = item['atType']
3631        aTypes |= set([item['atType'],]) 
3632        pstr = ['Ax','Ay','Az']
3633        XYZ = [0,0,0]
3634        for i in range(3):
3635            name = pfx+pstr[i]
3636            parmDict[name] = item['Pos'][0][i]
3637            XYZ[i] = parmDict[name]
3638            if item['Pos'][1][i]:
3639                varyList += [name,]
3640                limits = item['Pos'][2][i]
3641                lower.append(limits[0])
3642                upper.append(limits[1])
3643        parmDict[pfx+'Amul'] = len(G2spc.GenAtom(XYZ,SGData))
3644           
3645    def getRBparms(item,mfx,aTypes,RBdata,SGData,atNo,parmDict,varyList):
3646        parmDict[mfx+'MolCent'] = item['MolCent']
3647        parmDict[mfx+'RBId'] = item['RBId']
3648        pstr = ['Px','Py','Pz']
3649        ostr = ['Qa','Qi','Qj','Qk']    #angle,vector not quaternion
3650        for i in range(3):
3651            name = mfx+pstr[i]
3652            parmDict[name] = item['Pos'][0][i]
3653            if item['Pos'][1][i]:
3654                varyList += [name,]
3655                limits = item['Pos'][2][i]
3656                lower.append(limits[0])
3657                upper.append(limits[1])
3658        AV = item['Ori'][0]
3659        A = AV[0]
3660        V = AV[1:]
3661        for i in range(4):
3662            name = mfx+ostr[i]
3663            if i:
3664                parmDict[name] = V[i-1]
3665            else:
3666                parmDict[name] = A
3667            if item['Ovar'] == 'AV':
3668                varyList += [name,]
3669                limits = item['Ori'][2][i]
3670                lower.append(limits[0])
3671                upper.append(limits[1])
3672            elif item['Ovar'] == 'A' and not i:
3673                varyList += [name,]
3674                limits = item['Ori'][2][i]
3675                lower.append(limits[0])
3676                upper.append(limits[1])
3677        if 'Tor' in item:      #'Tor' not there for 'Vector' RBs
3678            for i in range(len(item['Tor'][0])):
3679                name = mfx+'Tor'+str(i)
3680                parmDict[name] = item['Tor'][0][i]
3681                if item['Tor'][1][i]:
3682                    varyList += [name,]
3683                    limits = item['Tor'][2][i]
3684                    lower.append(limits[0])
3685                    upper.append(limits[1])
3686        atypes = RBdata[item['Type']][item['RBId']]['rbTypes']
3687        aTypes |= set(atypes)
3688        atNo += len(atypes)
3689        return atNo
3690               
3691    def GetAtomM(Xdata,SGData):
3692        Mdata = []
3693        for xyz in Xdata:
3694            Mdata.append(float(len(G2spc.GenAtom(xyz,SGData))))
3695        return np.array(Mdata)
3696       
3697    def GetAtomT(RBdata,parmDict):
3698        'Needs a doc string'
3699        atNo = parmDict['atNo']
3700        nfixAt = parmDict['nfixAt']
3701        Tdata = atNo*[' ',]
3702        for iatm in range(nfixAt):
3703            parm = ':'+str(iatm)+':Atype'
3704            if parm in parmDict:
3705                Tdata[iatm] = aTypes.index(parmDict[parm])
3706        iatm = nfixAt
3707        for iObj in range(parmDict['nObj']):
3708            pfx = str(iObj)+':'
3709            if parmDict[pfx+'Type'] in ['Vector','Residue']:
3710                if parmDict[pfx+'Type'] == 'Vector':
3711                    RBRes = RBdata['Vector'][parmDict[pfx+'RBId']]
3712                    nAtm = len(RBRes['rbVect'][0])
3713                else:       #Residue
3714                    RBRes = RBdata['Residue'][parmDict[pfx+'RBId']]
3715                    nAtm = len(RBRes['rbXYZ'])
3716                for i in range(nAtm):
3717                    Tdata[iatm] = aTypes.index(RBRes['rbTypes'][i])
3718                    iatm += 1
3719            elif parmDict[pfx+'Type'] == 'Atom':
3720                atNo = parmDict[pfx+'atNo']
3721                parm = pfx+'Atype'              #remove extra ':'
3722                if parm in parmDict:
3723                    Tdata[atNo] = aTypes.index(parmDict[parm])
3724                iatm += 1
3725            else:
3726                continue        #skips March Dollase
3727        return Tdata
3728       
3729    def GetAtomX(RBdata,parmDict):
3730        'Needs a doc string'
3731        Bmat = parmDict['Bmat']
3732        atNo = parmDict['atNo']
3733        nfixAt = parmDict['nfixAt']
3734        Xdata = np.zeros((3,atNo))
3735        keys = {':Ax':Xdata[0],':Ay':Xdata[1],':Az':Xdata[2]}
3736        for iatm in range(nfixAt):
3737            for key in keys:
3738                parm = ':'+str(iatm)+key
3739                if parm in parmDict:
3740                    keys[key][iatm] = parmDict[parm]
3741        iatm = nfixAt
3742        for iObj in range(parmDict['nObj']):
3743            pfx = str(iObj)+':'
3744            if parmDict[pfx+'Type'] in ['Vector','Residue']:
3745                if parmDict[pfx+'Type'] == 'Vector':
3746                    RBRes = RBdata['Vector'][parmDict[pfx+'RBId']]
3747                    vecs = RBRes['rbVect']
3748                    mags = RBRes['VectMag']
3749                    Cart = np.zeros_like(vecs[0])
3750                    for vec,mag in zip(vecs,mags):
3751                        Cart += vec*mag
3752                elif parmDict[pfx+'Type'] == 'Residue':
3753                    RBRes = RBdata['Residue'][parmDict[pfx+'RBId']]
3754                    Cart = np.array(RBRes['rbXYZ'])
3755                    for itor,seq in enumerate(RBRes['rbSeq']):
3756                        QuatA = AVdeg2Q(parmDict[pfx+'Tor'+str(itor)],Cart[seq[0]]-Cart[seq[1]])
3757                        Cart[seq[3]] = prodQVQ(QuatA,Cart[seq[3]]-Cart[seq[1]])+Cart[seq[1]]
3758                if parmDict[pfx+'MolCent'][1]:
3759                    Cart -= parmDict[pfx+'MolCent'][0]
3760                Qori = AVdeg2Q(parmDict[pfx+'Qa'],[parmDict[pfx+'Qi'],parmDict[pfx+'Qj'],parmDict[pfx+'Qk']])
3761                Pos = np.array([parmDict[pfx+'Px'],parmDict[pfx+'Py'],parmDict[pfx+'Pz']])
3762                Xdata.T[iatm:iatm+len(Cart)] = np.inner(Bmat,prodQVQ(Qori,Cart)).T+Pos
3763                iatm += len(Cart)
3764            elif parmDict[pfx+'Type'] == 'Atom':
3765                atNo = parmDict[pfx+'atNo']
3766                for key in keys:
3767                    parm = pfx+key[1:]              #remove extra ':'
3768                    if parm in parmDict:
3769                        keys[key][atNo] = parmDict[parm]
3770                iatm += 1
3771            else:
3772                continue        #skips March Dollase
3773        return Xdata.T
3774       
3775    def getAllTX(Tdata,Mdata,Xdata,SGM,SGT):
3776        allX = np.inner(Xdata,SGM)+SGT
3777        allT = np.repeat(Tdata,allX.shape[1])
3778        allM = np.repeat(Mdata,allX.shape[1])
3779        allX = np.reshape(allX,(-1,3))
3780        return allT,allM,allX
3781
3782    def getAllX(Xdata,SGM,SGT):
3783        allX = np.inner(Xdata,SGM)+SGT
3784        allX = np.reshape(allX,(-1,3))
3785        return allX
3786       
3787    def normQuaternions(RBdata,parmDict,varyList,values):
3788        for iObj in range(parmDict['nObj']):
3789            pfx = str(iObj)+':'
3790            if parmDict[pfx+'Type'] in ['Vector','Residue']:
3791                Qori = AVdeg2Q(parmDict[pfx+'Qa'],[parmDict[pfx+'Qi'],parmDict[pfx+'Qj'],parmDict[pfx+'Qk']])
3792                A,V = Q2AVdeg(Qori)
3793                for i,name in enumerate(['Qa','Qi','Qj','Qk']):
3794                    if i:
3795                        parmDict[pfx+name] = V[i-1]
3796                    else:
3797                        parmDict[pfx+name] = A
3798       
3799    def mcsaCalc(values,refList,rcov,cosTable,ifInv,allFF,RBdata,varyList,parmDict):
3800        ''' Compute structure factors for all h,k,l for phase
3801        input:
3802            refList: [ref] where each ref = h,k,l,m,d,...
3803            rcov:   array[nref,nref] covariance terms between Fo^2 values
3804            ifInv:  bool True if centrosymmetric
3805            allFF: array[nref,natoms] each value is mult*FF(H)/max(mult)
3806            RBdata: [dict] rigid body dictionary
3807            varyList: [list] names of varied parameters in MC/SA (not used here)           
3808            ParmDict: [dict] problem parameters
3809        puts result F^2 in each ref[5] in refList
3810        returns:
3811            delt-F*rcov*delt-F/sum(Fo^2)^2
3812        '''       
3813        global tsum
3814        t0 = time.time()
3815        parmDict.update(dict(zip(varyList,values)))             #update parameter tables
3816        Xdata = GetAtomX(RBdata,parmDict)                       #get new atom coords from RB
3817        allX = getAllX(Xdata,SGM,SGT)                           #fill unit cell - dups. OK
3818        MDval = parmDict['0:MDval']                             #get March-Dollase coeff
3819        HX2pi = 2.*np.pi*np.inner(allX,refList[:3].T)           #form 2piHX for every H,X pair
3820        Aterm = refList[3]*np.sum(allFF*np.cos(HX2pi),axis=0)**2    #compute real part for all H
3821        refList[5] = Aterm
3822        if not ifInv:
3823            refList[5] += refList[3]*np.sum(allFF*np.sin(HX2pi),axis=0)**2    #imaginary part for all H
3824        if len(cosTable):        #apply MD correction
3825            refList[5] *= np.sum(np.sqrt((MDval/(cosTable*(MDval**3-1.)+1.))**3),axis=1)/cosTable.shape[1]
3826        sumFcsq = np.sum(refList[5])
3827        scale = parmDict['sumFosq']/sumFcsq
3828        refList[5] *= scale
3829        refList[6] = refList[4]-refList[5]
3830        M = np.inner(refList[6],np.inner(rcov,refList[6]))
3831        tsum += (time.time()-t0)
3832        return M/np.sum(refList[4]**2)
3833
3834    sq8ln2 = np.sqrt(8*np.log(2))
3835    sq2pi = np.sqrt(2*np.pi)
3836    sq4pi = np.sqrt(4*np.pi)
3837    generalData = data['General']
3838    Amat,Bmat = G2lat.cell2AB(generalData['Cell'][1:7])
3839    Gmat,gmat = G2lat.cell2Gmat(generalData['Cell'][1:7])
3840    SGData = generalData['SGData']
3841    SGM = np.array([SGData['SGOps'][i][0] for i in range(len(SGData['SGOps']))])
3842    SGMT = np.array([SGData['SGOps'][i][0].T for i in range(len(SGData['SGOps']))])
3843    SGT = np.array([SGData['SGOps'][i][1] for i in range(len(SGData['SGOps']))])
3844    fixAtoms = data['Atoms']                       #if any
3845    cx,ct,cs = generalData['AtomPtrs'][:3]
3846    aTypes = set([])
3847    parmDict = {'Bmat':Bmat,'Gmat':Gmat}
3848    varyList = []
3849    atNo = 0
3850    for atm in fixAtoms:
3851        pfx = ':'+str(atNo)+':'
3852        parmDict[pfx+'Atype'] = atm[ct]
3853        aTypes |= set([atm[ct],])
3854        pstr = ['Ax','Ay','Az']
3855        parmDict[pfx+'Amul'] = atm[cs+1]
3856        for i in range(3):
3857            name = pfx+pstr[i]
3858            parmDict[name] = atm[cx+i]
3859        atNo += 1
3860    parmDict['nfixAt'] = len(fixAtoms)       
3861    MCSA = generalData['MCSA controls']
3862    reflName = MCSA['Data source']
3863    phaseName = generalData['Name']
3864    MCSAObjs = data['MCSA']['Models']               #list of MCSA models
3865    upper = []
3866    lower = []
3867    MDvec = np.zeros(3)
3868    for i,item in enumerate(MCSAObjs):
3869        mfx = str(i)+':'
3870        parmDict[mfx+'Type'] = item['Type']
3871        if item['Type'] == 'MD':
3872            getMDparms(item,mfx,parmDict,varyList)
3873            MDvec = np.array(item['axis'])
3874        elif item['Type'] == 'Atom':
3875            getAtomparms(item,mfx,aTypes,SGData,parmDict,varyList)
3876            parmDict[mfx+'atNo'] = atNo
3877            atNo += 1
3878        elif item['Type'] in ['Residue','Vector']:
3879            atNo = getRBparms(item,mfx,aTypes,RBdata,SGData,atNo,parmDict,varyList)
3880    parmDict['atNo'] = atNo                 #total no. of atoms
3881    parmDict['nObj'] = len(MCSAObjs)
3882    aTypes = list(aTypes)
3883    Tdata = GetAtomT(RBdata,parmDict)
3884    Xdata = GetAtomX(RBdata,parmDict)
3885    Mdata = GetAtomM(Xdata,SGData)
3886    allT,allM = getAllTX(Tdata,Mdata,Xdata,SGM,SGT)[:2]
3887    FFtables = G2el.GetFFtable(aTypes)
3888    refs = []
3889    allFF = []
3890    cosTable = []
3891    sumFosq = 0
3892    if 'PWDR' in reflName:
3893        for ref in reflData:
3894            h,k,l,m,d,pos,sig,gam,f = ref[:9]
3895            if d >= MCSA['dmin']:
3896                sig = G2pwd.getgamFW(sig,gam)/sq8ln2        #--> sig from FWHM
3897                SQ = 0.25/d**2
3898                allFF.append(allM*[G2el.getFFvalues(FFtables,SQ,True)[i] for i in allT]/np.max(allM))
3899                refs.append([h,k,l,m,f*m,pos,sig])
3900                sumFosq += f*m
3901                Heqv = np.inner(np.array([h,k,l]),SGMT)
3902                cosTable.append(G2lat.CosAngle(Heqv,MDvec,Gmat))
3903        nRef = len(refs)
3904        cosTable = np.array(cosTable)**2
3905        rcov = np.zeros((nRef,nRef))
3906        for iref,refI in enumerate(refs):
3907            rcov[iref][iref] = 1./(sq4pi*refI[6])
3908            for jref,refJ in enumerate(refs[:iref]):
3909                t1 = refI[6]**2+refJ[6]**2
3910                t2 = (refJ[5]-refI[5])**2/(2.*t1)
3911                if t2 > 10.:
3912                    rcov[iref][jref] = 0.
3913                else:
3914                    rcov[iref][jref] = 1./(sq2pi*np.sqrt(t1)*np.exp(t2))
3915        rcov += (rcov.T-np.diagflat(np.diagonal(rcov)))
3916        Rdiag = np.sqrt(np.diag(rcov))
3917        Rnorm = np.outer(Rdiag,Rdiag)
3918        rcov /= Rnorm
3919    elif 'Pawley' in reflName:  #need a bail out if Pawley cov matrix doesn't exist.
3920        vNames = []
3921        pfx = str(data['pId'])+'::PWLref:'
3922        for iref,refI in enumerate(reflData):           #Pawley reflection set
3923            h,k,l,m,d,v,f,s = refI
3924            if d >= MCSA['dmin'] and v:       #skip unrefined ones
3925                vNames.append(pfx+str(iref))
3926                SQ = 0.25/d**2
3927                allFF.append(allM*[G2el.getFFvalues(FFtables,SQ,True)[i] for i in allT]/np.max(allM))
3928                refs.append([h,k,l,m,f*m,iref,0.])
3929                sumFosq += f*m
3930                Heqv = np.inner(np.array([h,k,l]),SGMT)
3931                cosTable.append(G2lat.CosAngle(Heqv,MDvec,Gmat))
3932        cosTable = np.array(cosTable)**2
3933        nRef = len(refs)
3934#        if generalData['doPawley'] and (covData['freshCOV'] or  MCSA['newDmin']):
3935        if covData['freshCOV'] or  MCSA['newDmin']:
3936            vList = covData['varyList']
3937            covMatrix = covData['covMatrix']
3938            rcov = getVCov(vNames,vList,covMatrix)
3939            rcov += (rcov.T-np.diagflat(np.diagonal(rcov)))
3940            Rdiag = np.sqrt(np.diag(rcov))
3941            Rdiag = np.where(Rdiag,Rdiag,1.0)
3942            Rnorm = np.outer(Rdiag,Rdiag)
3943            rcov /= Rnorm
3944            MCSA['rcov'] = rcov
3945            covData['freshCOV'] = False
3946            MCSA['newDmin'] = False
3947        else:
3948            rcov = MCSA['rcov']
3949    elif 'HKLF' in reflName:
3950        for ref in reflData:
3951            [h,k,l,m,d],f = ref[:5],ref[6]
3952            if d >= MCSA['dmin']:
3953                SQ = 0.25/d**2
3954                allFF.append(allM*[G2el.getFFvalues(FFtables,SQ,True)[i] for i in allT]/np.max(allM))
3955                refs.append([h,k,l,m,f*m,0.,0.])
3956                sumFosq += f*m
3957        nRef = len(refs)
3958        rcov = np.identity(len(refs))
3959    allFF = np.array(allFF).T
3960    refs = np.array(refs).T
3961    print ' Minimum d-spacing used: %.2f No. reflections used: %d'%(MCSA['dmin'],nRef)
3962    print ' Number of parameters varied: %d'%(len(varyList))
3963    parmDict['sumFosq'] = sumFosq
3964    x0 = [parmDict[val] for val in varyList]
3965    ifInv = SGData['SGInv']
3966    # consider replacing anneal with scipy.optimize.basinhopping
3967    results = anneal(mcsaCalc,x0,args=(refs,rcov,cosTable,ifInv,allFF,RBdata,varyList,parmDict),
3968        schedule=MCSA['Algorithm'], full_output=True,
3969        T0=MCSA['Annealing'][0], Tf=MCSA['Annealing'][1],dwell=MCSA['Annealing'][2],
3970        boltzmann=MCSA['boltzmann'], learn_rate=0.5, 
3971        quench=MCSA['fast parms'][0], m=MCSA['fast parms'][1], n=MCSA['fast parms'][2],
3972        lower=lower, upper=upper, slope=MCSA['log slope'],ranStart=MCSA.get('ranStart',False),
3973        ranRange=MCSA.get('ranRange',0.10),autoRan=MCSA.get('autoRan',False),dlg=pgbar)
3974    M = mcsaCalc(results[0],refs,rcov,cosTable,ifInv,allFF,RBdata,varyList,parmDict)
3975#    for ref in refs.T:
3976#        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])
3977#    print np.sqrt((np.sum(refs[6]**2)/np.sum(refs[4]**2)))
3978    Result = [False,False,results[1],results[2],]+list(results[0])
3979    Result.append(varyList)
3980    return Result,tsum
3981
3982       
3983################################################################################
3984##### Quaternion stuff
3985################################################################################
3986
3987def prodQQ(QA,QB):
3988    ''' Grassman quaternion product
3989        QA,QB quaternions; q=r+ai+bj+ck
3990    '''
3991    D = np.zeros(4)
3992    D[0] = QA[0]*QB[0]-QA[1]*QB[1]-QA[2]*QB[2]-QA[3]*QB[3]
3993    D[1] = QA[0]*QB[1]+QA[1]*QB[0]+QA[2]*QB[3]-QA[3]*QB[2]
3994    D[2] = QA[0]*QB[2]-QA[1]*QB[3]+QA[2]*QB[0]+QA[3]*QB[1]
3995    D[3] = QA[0]*QB[3]+QA[1]*QB[2]-QA[2]*QB[1]+QA[3]*QB[0]
3996   
3997#    D[0] = QA[0]*QB[0]-np.dot(QA[1:],QB[1:])
3998#    D[1:] = QA[0]*QB[1:]+QB[0]*QA[1:]+np.cross(QA[1:],QB[1:])
3999   
4000    return D
4001   
4002def normQ(QA):
4003    ''' get length of quaternion & normalize it
4004        q=r+ai+bj+ck
4005    '''
4006    n = np.sqrt(np.sum(np.array(QA)**2))
4007    return QA/n
4008   
4009def invQ(Q):
4010    '''
4011        get inverse of quaternion
4012        q=r+ai+bj+ck; q* = r-ai-bj-ck
4013    '''
4014    return Q*np.array([1,-1,-1,-1])
4015   
4016def prodQVQ(Q,V):
4017    """
4018    compute the quaternion vector rotation qvq-1 = v'
4019    q=r+ai+bj+ck
4020    """
4021    T2 = Q[0]*Q[1]
4022    T3 = Q[0]*Q[2]
4023    T4 = Q[0]*Q[3]
4024    T5 = -Q[1]*Q[1]
4025    T6 = Q[1]*Q[2]
4026    T7 = Q[1]*Q[3]
4027    T8 = -Q[2]*Q[2]
4028    T9 = Q[2]*Q[3]
4029    T10 = -Q[3]*Q[3]
4030    M = np.array([[T8+T10,T6-T4,T3+T7],[T4+T6,T5+T10,T9-T2],[T7-T3,T2+T9,T5+T8]])
4031    VP = 2.*np.inner(V,M)
4032    return VP+V
4033   
4034def Q2Mat(Q):
4035    ''' make rotation matrix from quaternion
4036        q=r+ai+bj+ck
4037    '''
4038    QN = normQ(Q)
4039    aa = QN[0]**2
4040    ab = QN[0]*QN[1]
4041    ac = QN[0]*QN[2]
4042    ad = QN[0]*QN[3]
4043    bb = QN[1]**2
4044    bc = QN[1]*QN[2]
4045    bd = QN[1]*QN[3]
4046    cc = QN[2]**2
4047    cd = QN[2]*QN[3]
4048    dd = QN[3]**2
4049    M = [[aa+bb-cc-dd, 2.*(bc-ad),  2.*(ac+bd)],
4050        [2*(ad+bc),   aa-bb+cc-dd,  2.*(cd-ab)],
4051        [2*(bd-ac),    2.*(ab+cd), aa-bb-cc+dd]]
4052    return np.array(M)
4053   
4054def AV2Q(A,V):
4055    ''' convert angle (radians) & vector to quaternion
4056        q=r+ai+bj+ck
4057    '''
4058    Q = np.zeros(4)
4059    d = nl.norm(np.array(V))
4060    if d:
4061        V /= d
4062        if not A:       #==0.
4063            A = 2.*np.pi
4064        p = A/2.
4065        Q[0] = np.cos(p)
4066        Q[1:4] = V*np.sin(p)
4067    else:
4068        Q[3] = 1.
4069    return Q
4070   
4071def AVdeg2Q(A,V):
4072    ''' convert angle (degrees) & vector to quaternion
4073        q=r+ai+bj+ck
4074    '''
4075    Q = np.zeros(4)
4076    d = nl.norm(np.array(V))
4077    if not A:       #== 0.!
4078        A = 360.
4079    if d:
4080        V /= d
4081        p = A/2.
4082        Q[0] = cosd(p)
4083        Q[1:4] = V*sind(p)
4084    else:
4085        Q[3] = 1.
4086    return Q
4087   
4088def Q2AVdeg(Q):
4089    ''' convert quaternion to angle (degrees 0-360) & normalized vector
4090        q=r+ai+bj+ck
4091    '''
4092    A = 2.*acosd(Q[0])
4093    V = np.array(Q[1:])
4094    V /= sind(A/2.)
4095    return A,V
4096   
4097def Q2AV(Q):
4098    ''' convert quaternion to angle (radians 0-2pi) & normalized vector
4099        q=r+ai+bj+ck
4100    '''
4101    A = 2.*np.arccos(Q[0])
4102    V = np.array(Q[1:])
4103    V /= np.sin(A/2.)
4104    return A,V
4105   
4106def randomQ(r0,r1,r2,r3):
4107    ''' create random quaternion from 4 random numbers in range (-1,1)
4108    '''
4109    sum = 0
4110    Q = np.array(4)
4111    Q[0] = r0
4112    sum += Q[0]**2
4113    Q[1] = np.sqrt(1.-sum)*r1
4114    sum += Q[1]**2
4115    Q[2] = np.sqrt(1.-sum)*r2
4116    sum += Q[2]**2
4117    Q[3] = np.sqrt(1.-sum)*np.where(r3<0.,-1.,1.)
4118    return Q
4119   
4120def randomAVdeg(r0,r1,r2,r3):
4121    ''' create random angle (deg),vector from 4 random number in range (-1,1)
4122    '''
4123    return Q2AVdeg(randomQ(r0,r1,r2,r3))
4124   
4125def makeQuat(A,B,C):
4126    ''' Make quaternion from rotation of A vector to B vector about C axis
4127
4128        :param np.array A,B,C: Cartesian 3-vectors
4129        :returns: quaternion & rotation angle in radians q=r+ai+bj+ck
4130    '''
4131
4132    V1 = np.cross(A,C)
4133    V2 = np.cross(B,C)
4134    if nl.norm(V1)*nl.norm(V2):
4135        V1 /= nl.norm(V1)
4136        V2 /= nl.norm(V2)
4137        V3 = np.cross(V1,V2)
4138    else:
4139        V3 = np.zeros(3)
4140    Q = np.array([0.,0.,0.,1.])
4141    D = 0.
4142    if nl.norm(V3):
4143        V3 /= nl.norm(V3)
4144        D1 = min(1.0,max(-1.0,np.vdot(V1,V2)))
4145        D = np.arccos(D1)/2.0
4146        V1 = C-V3
4147        V2 = C+V3
4148        DM = nl.norm(V1)
4149        DP = nl.norm(V2)
4150        S = np.sin(D)
4151        Q[0] = np.cos(D)
4152        Q[1:] = V3*S
4153        D *= 2.
4154        if DM > DP:
4155            D *= -1.
4156    return Q,D
4157   
4158def annealtests():
4159    from numpy import cos
4160    # minimum expected at ~-0.195
4161    func = lambda x: cos(14.5*x-0.3) + (x+0.2)*x
4162    print anneal(func,1.0,full_output=1,upper=3.0,lower=-3.0,feps=1e-4,maxiter=2000,schedule='cauchy')
4163    print anneal(func,1.0,full_output=1,upper=3.0,lower=-3.0,feps=1e-4,maxiter=2000,schedule='fast')
4164    print anneal(func,1.0,full_output=1,upper=3.0,lower=-3.0,feps=1e-4,maxiter=2000,schedule='boltzmann')
4165
4166    # minimum expected at ~[-0.195, -0.1]
4167    func = lambda x: cos(14.5*x[0]-0.3) + (x[1]+0.2)*x[1] + (x[0]+0.2)*x[0]
4168    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')
4169    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')
4170    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')
4171
4172
4173if __name__ == '__main__':
4174    annealtests()
Note: See TracBrowser for help on using the repository browser.