source: trunk/GSASIImath.py @ 2038

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

add betaij2Uij to G2lattice
revisions to SS structure factor calcs. Use hklt proj to hkl is several places
fxn works for thiourea derivs OK except X,Y,Zcos modulations; no Uijsin/cos derivatives yet
adj scaling of 4D charge flip maps
convert betaij vals from Jana2K files to Uij
start on SS read phase from cif
added a hklt F import (might vanish)

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