source: trunk/GSASIImath.py @ 1956

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

fix (again) fill unit cell, dynamic SS drawing & GenAtom? issues - now all OK
fixes to get SS structure factors to compute & twins in SS data issues

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