source: trunk/GSASIImath.py @ 1984

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

some SS derv work

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