source: trunk/GSASIImath.py @ 2329

Last change on this file since 2329 was 2329, checked in by vondreele, 7 years ago

sequential table distance & angle now OK, esds for distance OK, non yet for angle

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