source: trunk/GSASIImath.py @ 2165

Last change on this file since 2165 was 2154, checked in by vondreele, 9 years ago

remove range restriction on LGmix - inhibited doing refinements
do wx.CallAfter? for size/strain plots in G2ddataGUI
Add TransformDialog?, prodMGMT, TransformCell?, TransformU6, TransformXYZ to G2lattice
A start on TransformAtoms? in G2math
fill in OnTransform?

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