source: trunk/GSASIImath.py @ 1930

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

allow exclude atoms from H-atom position calcs.
hydrogen add complete(?) - needs testing
new hydrogen update implemented

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