source: trunk/GSASIImath.py @ 2308

Last change on this file since 2308 was 2301, checked in by vondreele, 9 years ago

add FindAllNeighbors? to G2math for finding all symm equiv neighbors
add bond & angle pseudovariables for seq results - not finished
fix float problems for outChannels & LRazimuth - should be int

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