source: trunk/GSASIImath.py @ 1925

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

create AddHatomDialog? & work on OnHydAtomAdd?
allow reading of .cor images made at APS 1ID

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