source: trunk/GSASIImath.py @ 1978

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

fix to update reflections in HKLplot
remove ZigZag? from incommensurate list
add pylab to G2math if in debug mode
revisions to modulations anticipating Crenel & Sawtooth
fixes to SS structure factor calcs. Some success.

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