source: trunk/GSASIImath.py @ 2318

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

Add atom-atom distance to seq results table - needs debugging

  • Property svn:eol-style set to native
  • Property svn:keywords set to Date Author Revision URL Id
File size: 164.1 KB
Line 
1# -*- coding: utf-8 -*-
2#GSASIImath - major mathematics routines
3########### SVN repository information ###################
4# $Date: 2016-06-14 02:11:36 +0000 (Tue, 14 Jun 2016) $
5# $Author: vondreele $
6# $Revision: 2318 $
7# $URL: trunk/GSASIImath.py $
8# $Id: GSASIImath.py 2318 2016-06-14 02:11:36Z 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: 2318 $")
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    M *= inv
1525    T *= inv
1526    D = T+distance_dict['SGData']['SGCen'][cen]
1527    D += distance_dict['cellNo']
1528    Txyz = np.inner(M,Txyz)+D
1529    dist = np.sqrt(np.sum(np.inner(Amat,(Txyz-Oxyz))**2))
1530#    GSASIIpath.IPyBreak()
1531    return dist   
1532   
1533def getSyXYZ(XYZ,ops,SGData):
1534    '''default doc string
1535   
1536    :param type name: description
1537   
1538    :returns: type name: description
1539   
1540    '''
1541    XYZout = np.zeros_like(XYZ)
1542    for i,[xyz,op] in enumerate(zip(XYZ,ops)):
1543        if op == '1':
1544            XYZout[i] = xyz
1545        else:
1546            oprs = op.split('+')
1547            unit = [0,0,0]
1548            if len(oprs)>1:
1549                unit = np.array(list(eval(oprs[1])))
1550            syop =int(oprs[0])
1551            inv = syop/abs(syop)
1552            syop *= inv
1553            cent = syop/100
1554            syop %= 100
1555            syop -= 1
1556            M,T = SGData['SGOps'][syop]
1557            XYZout[i] = (np.inner(M,xyz)+T)*inv+SGData['SGCen'][cent]+unit
1558    return XYZout
1559   
1560def getRestDist(XYZ,Amat):
1561    '''default doc string
1562   
1563    :param type name: description
1564   
1565    :returns: type name: description
1566   
1567    '''
1568    return np.sqrt(np.sum(np.inner(Amat,(XYZ[1]-XYZ[0]))**2))
1569   
1570def getRestDeriv(Func,XYZ,Amat,ops,SGData):
1571    '''default doc string
1572   
1573    :param type name: description
1574   
1575    :returns: type name: description
1576   
1577    '''
1578    deriv = np.zeros((len(XYZ),3))
1579    dx = 0.00001
1580    for j,xyz in enumerate(XYZ):
1581        for i,x in enumerate(np.array([[dx,0,0],[0,dx,0],[0,0,dx]])):
1582            XYZ[j] -= x
1583            d1 = Func(getSyXYZ(XYZ,ops,SGData),Amat)
1584            XYZ[j] += 2*x
1585            d2 = Func(getSyXYZ(XYZ,ops,SGData),Amat)
1586            XYZ[j] -= x
1587            deriv[j][i] = (d1-d2)/(2*dx)
1588    return deriv.flatten()
1589
1590def getRestAngle(XYZ,Amat):
1591    '''default doc string
1592   
1593    :param type name: description
1594   
1595    :returns: type name: description
1596   
1597    '''
1598   
1599    def calcVec(Ox,Tx,Amat):
1600        return np.inner(Amat,(Tx-Ox))
1601
1602    VecA = calcVec(XYZ[1],XYZ[0],Amat)
1603    VecA /= np.sqrt(np.sum(VecA**2))
1604    VecB = calcVec(XYZ[1],XYZ[2],Amat)
1605    VecB /= np.sqrt(np.sum(VecB**2))
1606    edge = VecB-VecA
1607    edge = np.sum(edge**2)
1608    angle = (2.-edge)/2.
1609    angle = max(angle,-1.)
1610    return acosd(angle)
1611   
1612def getRestPlane(XYZ,Amat):
1613    '''default doc string
1614   
1615    :param type name: description
1616   
1617    :returns: type name: description
1618   
1619    '''
1620    sumXYZ = np.zeros(3)
1621    for xyz in XYZ:
1622        sumXYZ += xyz
1623    sumXYZ /= len(XYZ)
1624    XYZ = np.array(XYZ)-sumXYZ
1625    XYZ = np.inner(Amat,XYZ).T
1626    Zmat = np.zeros((3,3))
1627    for i,xyz in enumerate(XYZ):
1628        Zmat += np.outer(xyz.T,xyz)
1629    Evec,Emat = nl.eig(Zmat)
1630    Evec = np.sqrt(Evec)/(len(XYZ)-3)
1631    Order = np.argsort(Evec)
1632    return Evec[Order[0]]
1633   
1634def getRestChiral(XYZ,Amat):   
1635    '''default doc string
1636   
1637    :param type name: description
1638   
1639    :returns: type name: description
1640   
1641    '''
1642    VecA = np.empty((3,3))   
1643    VecA[0] = np.inner(XYZ[1]-XYZ[0],Amat)
1644    VecA[1] = np.inner(XYZ[2]-XYZ[0],Amat)
1645    VecA[2] = np.inner(XYZ[3]-XYZ[0],Amat)
1646    return nl.det(VecA)
1647   
1648def getRestTorsion(XYZ,Amat):
1649    '''default doc string
1650   
1651    :param type name: description
1652   
1653    :returns: type name: description
1654   
1655    '''
1656    VecA = np.empty((3,3))
1657    VecA[0] = np.inner(XYZ[1]-XYZ[0],Amat)
1658    VecA[1] = np.inner(XYZ[2]-XYZ[1],Amat)
1659    VecA[2] = np.inner(XYZ[3]-XYZ[2],Amat)
1660    D = nl.det(VecA)
1661    Mag = np.sqrt(np.sum(VecA*VecA,axis=1))
1662    P12 = np.sum(VecA[0]*VecA[1])/(Mag[0]*Mag[1])
1663    P13 = np.sum(VecA[0]*VecA[2])/(Mag[0]*Mag[2])
1664    P23 = np.sum(VecA[1]*VecA[2])/(Mag[1]*Mag[2])
1665    Ang = 1.0
1666    if abs(P12) < 1.0 and abs(P23) < 1.0:
1667        Ang = (P12*P23-P13)/(np.sqrt(1.-P12**2)*np.sqrt(1.-P23**2))
1668    TOR = (acosd(Ang)*D/abs(D)+720.)%360.
1669    return TOR
1670   
1671def calcTorsionEnergy(TOR,Coeff=[]):
1672    '''default doc string
1673   
1674    :param type name: description
1675   
1676    :returns: type name: description
1677   
1678    '''
1679    sum = 0.
1680    if len(Coeff):
1681        cof = np.reshape(Coeff,(3,3)).T
1682        delt = TOR-cof[1]
1683        delt = np.where(delt<-180.,delt+360.,delt)
1684        delt = np.where(delt>180.,delt-360.,delt)
1685        term = -cof[2]*delt**2
1686        val = cof[0]*np.exp(term/1000.0)
1687        pMax = cof[0][np.argmin(val)]
1688        Eval = np.sum(val)
1689        sum = Eval-pMax
1690    return sum,Eval
1691
1692def getTorsionDeriv(XYZ,Amat,Coeff):
1693    '''default doc string
1694   
1695    :param type name: description
1696   
1697    :returns: type name: description
1698   
1699    '''
1700    deriv = np.zeros((len(XYZ),3))
1701    dx = 0.00001
1702    for j,xyz in enumerate(XYZ):
1703        for i,x in enumerate(np.array([[dx,0,0],[0,dx,0],[0,0,dx]])):
1704            XYZ[j] -= x
1705            tor = getRestTorsion(XYZ,Amat)
1706            p1,d1 = calcTorsionEnergy(tor,Coeff)
1707            XYZ[j] += 2*x
1708            tor = getRestTorsion(XYZ,Amat)
1709            p2,d2 = calcTorsionEnergy(tor,Coeff)           
1710            XYZ[j] -= x
1711            deriv[j][i] = (p2-p1)/(2*dx)
1712    return deriv.flatten()
1713
1714def getRestRama(XYZ,Amat):
1715    '''Computes a pair of torsion angles in a 5 atom string
1716   
1717    :param nparray XYZ: crystallographic coordinates of 5 atoms
1718    :param nparray Amat: crystal to cartesian transformation matrix
1719   
1720    :returns: list (phi,psi) two torsion angles in degrees
1721   
1722    '''
1723    phi = getRestTorsion(XYZ[:5],Amat)
1724    psi = getRestTorsion(XYZ[1:],Amat)
1725    return phi,psi
1726   
1727def calcRamaEnergy(phi,psi,Coeff=[]):
1728    '''Computes pseudo potential energy from a pair of torsion angles and a
1729    numerical description of the potential energy surface. Used to create
1730    penalty function in LS refinement:     
1731    :math:`Eval(\\phi,\\psi) = C[0]*exp(-V/1000)`
1732
1733    where :math:`V = -C[3] * (\\phi-C[1])^2 - C[4]*(\\psi-C[2])^2 - 2*(\\phi-C[1])*(\\psi-C[2])`
1734   
1735    :param float phi: first torsion angle (:math:`\\phi`)
1736    :param float psi: second torsion angle (:math:`\\psi`)
1737    :param list Coeff: pseudo potential coefficients
1738   
1739    :returns: list (sum,Eval): pseudo-potential difference from minimum & value;
1740      sum is used for penalty function.
1741   
1742    '''
1743    sum = 0.
1744    Eval = 0.
1745    if len(Coeff):
1746        cof = Coeff.T
1747        dPhi = phi-cof[1]
1748        dPhi = np.where(dPhi<-180.,dPhi+360.,dPhi)
1749        dPhi = np.where(dPhi>180.,dPhi-360.,dPhi)
1750        dPsi = psi-cof[2]
1751        dPsi = np.where(dPsi<-180.,dPsi+360.,dPsi)
1752        dPsi = np.where(dPsi>180.,dPsi-360.,dPsi)
1753        val = -cof[3]*dPhi**2-cof[4]*dPsi**2-2.0*cof[5]*dPhi*dPsi
1754        val = cof[0]*np.exp(val/1000.)
1755        pMax = cof[0][np.argmin(val)]
1756        Eval = np.sum(val)
1757        sum = Eval-pMax
1758    return sum,Eval
1759
1760def getRamaDeriv(XYZ,Amat,Coeff):
1761    '''Computes numerical derivatives of torsion angle pair pseudo potential
1762    with respect of crystallographic atom coordinates of the 5 atom sequence
1763   
1764    :param nparray XYZ: crystallographic coordinates of 5 atoms
1765    :param nparray Amat: crystal to cartesian transformation matrix
1766    :param list Coeff: pseudo potential coefficients
1767   
1768    :returns: list (deriv) derivatives of pseudopotential with respect to 5 atom
1769     crystallographic xyz coordinates.
1770   
1771    '''
1772    deriv = np.zeros((len(XYZ),3))
1773    dx = 0.00001
1774    for j,xyz in enumerate(XYZ):
1775        for i,x in enumerate(np.array([[dx,0,0],[0,dx,0],[0,0,dx]])):
1776            XYZ[j] -= x
1777            phi,psi = getRestRama(XYZ,Amat)
1778            p1,d1 = calcRamaEnergy(phi,psi,Coeff)
1779            XYZ[j] += 2*x
1780            phi,psi = getRestRama(XYZ,Amat)
1781            p2,d2 = calcRamaEnergy(phi,psi,Coeff)
1782            XYZ[j] -= x
1783            deriv[j][i] = (p2-p1)/(2*dx)
1784    return deriv.flatten()
1785
1786def getRestPolefig(ODFln,SamSym,Grid):
1787    '''default doc string
1788   
1789    :param type name: description
1790   
1791    :returns: type name: description
1792   
1793    '''
1794    X,Y = np.meshgrid(np.linspace(1.,-1.,Grid),np.linspace(-1.,1.,Grid))
1795    R,P = np.sqrt(X**2+Y**2).flatten(),atan2d(Y,X).flatten()
1796    R = np.where(R <= 1.,2.*atand(R),0.0)
1797    Z = np.zeros_like(R)
1798    Z = G2lat.polfcal(ODFln,SamSym,R,P)
1799    Z = np.reshape(Z,(Grid,Grid))
1800    return np.reshape(R,(Grid,Grid)),np.reshape(P,(Grid,Grid)),Z
1801
1802def getRestPolefigDerv(HKL,Grid,SHCoeff):
1803    '''default doc string
1804   
1805    :param type name: description
1806   
1807    :returns: type name: description
1808   
1809    '''
1810    pass
1811       
1812def getDistDerv(Oxyz,Txyz,Amat,Tunit,Top,SGData):
1813    '''default doc string
1814   
1815    :param type name: description
1816   
1817    :returns: type name: description
1818   
1819    '''
1820    def calcDist(Ox,Tx,U,inv,C,M,T,Amat):
1821        TxT = inv*(np.inner(M,Tx)+T)+C+U
1822        return np.sqrt(np.sum(np.inner(Amat,(TxT-Ox))**2))
1823       
1824    inv = Top/abs(Top)
1825    cent = abs(Top)/100
1826    op = abs(Top)%100-1
1827    M,T = SGData['SGOps'][op]
1828    C = SGData['SGCen'][cent]
1829    dx = .00001
1830    deriv = np.zeros(6)
1831    for i in [0,1,2]:
1832        Oxyz[i] -= dx
1833        d0 = calcDist(Oxyz,Txyz,Tunit,inv,C,M,T,Amat)
1834        Oxyz[i] += 2*dx
1835        deriv[i] = (calcDist(Oxyz,Txyz,Tunit,inv,C,M,T,Amat)-d0)/(2.*dx)
1836        Oxyz[i] -= dx
1837        Txyz[i] -= dx
1838        d0 = calcDist(Oxyz,Txyz,Tunit,inv,C,M,T,Amat)
1839        Txyz[i] += 2*dx
1840        deriv[i+3] = (calcDist(Oxyz,Txyz,Tunit,inv,C,M,T,Amat)-d0)/(2.*dx)
1841        Txyz[i] -= dx
1842    return deriv
1843   
1844def getAngSig(VA,VB,Amat,SGData,covData={}):
1845    '''default doc string
1846   
1847    :param type name: description
1848   
1849    :returns: type name: description
1850   
1851    '''
1852    def calcVec(Ox,Tx,U,inv,C,M,T,Amat):
1853        TxT = inv*(np.inner(M,Tx)+T)+C+U
1854        return np.inner(Amat,(TxT-Ox))
1855       
1856    def calcAngle(Ox,TxA,TxB,unitA,unitB,invA,CA,MA,TA,invB,CB,MB,TB,Amat):
1857        VecA = calcVec(Ox,TxA,unitA,invA,CA,MA,TA,Amat)
1858        VecA /= np.sqrt(np.sum(VecA**2))
1859        VecB = calcVec(Ox,TxB,unitB,invB,CB,MB,TB,Amat)
1860        VecB /= np.sqrt(np.sum(VecB**2))
1861        edge = VecB-VecA
1862        edge = np.sum(edge**2)
1863        angle = (2.-edge)/2.
1864        angle = max(angle,-1.)
1865        return acosd(angle)
1866       
1867    OxAN,OxA,TxAN,TxA,unitA,TopA = VA
1868    OxBN,OxB,TxBN,TxB,unitB,TopB = VB
1869    invA = invB = 1
1870    invA = TopA/abs(TopA)
1871    invB = TopB/abs(TopB)
1872    centA = abs(TopA)/100
1873    centB = abs(TopB)/100
1874    opA = abs(TopA)%100-1
1875    opB = abs(TopB)%100-1
1876    MA,TA = SGData['SGOps'][opA]
1877    MB,TB = SGData['SGOps'][opB]
1878    CA = SGData['SGCen'][centA]
1879    CB = SGData['SGCen'][centB]
1880    if 'covMatrix' in covData:
1881        covMatrix = covData['covMatrix']
1882        varyList = covData['varyList']
1883        AngVcov = getVCov(OxAN+TxAN+TxBN,varyList,covMatrix)
1884        dx = .00001
1885        dadx = np.zeros(9)
1886        Ang = calcAngle(OxA,TxA,TxB,unitA,unitB,invA,CA,MA,TA,invB,CB,MB,TB,Amat)
1887        for i in [0,1,2]:
1888            OxA[i] -= dx
1889            a0 = calcAngle(OxA,TxA,TxB,unitA,unitB,invA,CA,MA,TA,invB,CB,MB,TB,Amat)
1890            OxA[i] += 2*dx
1891            dadx[i] = (calcAngle(OxA,TxA,TxB,unitA,unitB,invA,CA,MA,TA,invB,CB,MB,TB,Amat)-a0)/(2*dx)
1892            OxA[i] -= dx
1893           
1894            TxA[i] -= dx
1895            a0 = calcAngle(OxA,TxA,TxB,unitA,unitB,invA,CA,MA,TA,invB,CB,MB,TB,Amat)
1896            TxA[i] += 2*dx
1897            dadx[i+3] = (calcAngle(OxA,TxA,TxB,unitA,unitB,invA,CA,MA,TA,invB,CB,MB,TB,Amat)-a0)/(2*dx)
1898            TxA[i] -= dx
1899           
1900            TxB[i] -= dx
1901            a0 = calcAngle(OxA,TxA,TxB,unitA,unitB,invA,CA,MA,TA,invB,CB,MB,TB,Amat)
1902            TxB[i] += 2*dx
1903            dadx[i+6] = (calcAngle(OxA,TxA,TxB,unitA,unitB,invA,CA,MA,TA,invB,CB,MB,TB,Amat)-a0)/(2*dx)
1904            TxB[i] -= dx
1905           
1906        sigAng = np.sqrt(np.inner(dadx,np.inner(AngVcov,dadx)))
1907        if sigAng < 0.01:
1908            sigAng = 0.0
1909        return Ang,sigAng
1910    else:
1911        return calcAngle(OxA,TxA,TxB,unitA,unitB,invA,CA,MA,TA,invB,CB,MB,TB,Amat),0.0
1912
1913def GetDistSig(Oatoms,Atoms,Amat,SGData,covData={}):
1914    '''default doc string
1915   
1916    :param type name: description
1917   
1918    :returns: type name: description
1919   
1920    '''
1921    def calcDist(Atoms,SyOps,Amat):
1922        XYZ = []
1923        for i,atom in enumerate(Atoms):
1924            Inv,M,T,C,U = SyOps[i]
1925            XYZ.append(np.array(atom[1:4]))
1926            XYZ[-1] = Inv*(np.inner(M,np.array(XYZ[-1]))+T)+C+U
1927            XYZ[-1] = np.inner(Amat,XYZ[-1]).T
1928        V1 = XYZ[1]-XYZ[0]
1929        return np.sqrt(np.sum(V1**2))
1930       
1931    Inv = []
1932    SyOps = []
1933    names = []
1934    for i,atom in enumerate(Oatoms):
1935        names += atom[-1]
1936        Op,unit = Atoms[i][-1]
1937        inv = Op/abs(Op)
1938        m,t = SGData['SGOps'][abs(Op)%100-1]
1939        c = SGData['SGCen'][abs(Op)/100]
1940        SyOps.append([inv,m,t,c,unit])
1941    Dist = calcDist(Oatoms,SyOps,Amat)
1942   
1943    sig = -0.001
1944    if 'covMatrix' in covData:
1945        parmNames = []
1946        dx = .00001
1947        dadx = np.zeros(6)
1948        for i in range(6):
1949            ia = i/3
1950            ix = i%3
1951            Oatoms[ia][ix+1] += dx
1952            a0 = calcDist(Oatoms,SyOps,Amat)
1953            Oatoms[ia][ix+1] -= 2*dx
1954            dadx[i] = (calcDist(Oatoms,SyOps,Amat)-a0)/(2.*dx)
1955        covMatrix = covData['covMatrix']
1956        varyList = covData['varyList']
1957        DistVcov = getVCov(names,varyList,covMatrix)
1958        sig = np.sqrt(np.inner(dadx,np.inner(DistVcov,dadx)))
1959        if sig < 0.001:
1960            sig = -0.001
1961   
1962    return Dist,sig
1963
1964def GetAngleSig(Oatoms,Atoms,Amat,SGData,covData={}):
1965    '''default doc string
1966   
1967    :param type name: description
1968   
1969    :returns: type name: description
1970   
1971    '''
1972
1973    def calcAngle(Atoms,SyOps,Amat):
1974        XYZ = []
1975        for i,atom in enumerate(Atoms):
1976            Inv,M,T,C,U = SyOps[i]
1977            XYZ.append(np.array(atom[1:4]))
1978            XYZ[-1] = Inv*(np.inner(M,np.array(XYZ[-1]))+T)+C+U
1979            XYZ[-1] = np.inner(Amat,XYZ[-1]).T
1980        V1 = XYZ[1]-XYZ[0]
1981        V1 /= np.sqrt(np.sum(V1**2))
1982        V2 = XYZ[1]-XYZ[2]
1983        V2 /= np.sqrt(np.sum(V2**2))
1984        V3 = V2-V1
1985        cang = min(1.,max((2.-np.sum(V3**2))/2.,-1.))
1986        return acosd(cang)
1987
1988    Inv = []
1989    SyOps = []
1990    names = []
1991    for i,atom in enumerate(Oatoms):
1992        names += atom[-1]
1993        Op,unit = Atoms[i][-1]
1994        inv = Op/abs(Op)
1995        m,t = SGData['SGOps'][abs(Op)%100-1]
1996        c = SGData['SGCen'][abs(Op)/100]
1997        SyOps.append([inv,m,t,c,unit])
1998    Angle = calcAngle(Oatoms,SyOps,Amat)
1999   
2000    sig = -0.01
2001    if 'covMatrix' in covData:
2002        parmNames = []
2003        dx = .00001
2004        dadx = np.zeros(9)
2005        for i in range(9):
2006            ia = i/3
2007            ix = i%3
2008            Oatoms[ia][ix+1] += dx
2009            a0 = calcAngle(Oatoms,SyOps,Amat)
2010            Oatoms[ia][ix+1] -= 2*dx
2011            dadx[i] = (calcAngle(Oatoms,SyOps,Amat)-a0)/(2.*dx)
2012        covMatrix = covData['covMatrix']
2013        varyList = covData['varyList']
2014        AngVcov = getVCov(names,varyList,covMatrix)
2015        sig = np.sqrt(np.inner(dadx,np.inner(AngVcov,dadx)))
2016        if sig < 0.01:
2017            sig = -0.01
2018   
2019    return Angle,sig
2020
2021def GetTorsionSig(Oatoms,Atoms,Amat,SGData,covData={}):
2022    '''default doc string
2023   
2024    :param type name: description
2025   
2026    :returns: type name: description
2027   
2028    '''
2029
2030    def calcTorsion(Atoms,SyOps,Amat):
2031       
2032        XYZ = []
2033        for i,atom in enumerate(Atoms):
2034            Inv,M,T,C,U = SyOps[i]
2035            XYZ.append(np.array(atom[1:4]))
2036            XYZ[-1] = Inv*(np.inner(M,np.array(XYZ[-1]))+T)+C+U
2037            XYZ[-1] = np.inner(Amat,XYZ[-1]).T
2038        V1 = XYZ[1]-XYZ[0]
2039        V2 = XYZ[2]-XYZ[1]
2040        V3 = XYZ[3]-XYZ[2]
2041        V1 /= np.sqrt(np.sum(V1**2))
2042        V2 /= np.sqrt(np.sum(V2**2))
2043        V3 /= np.sqrt(np.sum(V3**2))
2044        M = np.array([V1,V2,V3])
2045        D = nl.det(M)
2046        Ang = 1.0
2047        P12 = np.dot(V1,V2)
2048        P13 = np.dot(V1,V3)
2049        P23 = np.dot(V2,V3)
2050        Tors = acosd((P12*P23-P13)/(np.sqrt(1.-P12**2)*np.sqrt(1.-P23**2)))*D/abs(D)
2051        return Tors
2052           
2053    Inv = []
2054    SyOps = []
2055    names = []
2056    for i,atom in enumerate(Oatoms):
2057        names += atom[-1]
2058        Op,unit = Atoms[i][-1]
2059        inv = Op/abs(Op)
2060        m,t = SGData['SGOps'][abs(Op)%100-1]
2061        c = SGData['SGCen'][abs(Op)/100]
2062        SyOps.append([inv,m,t,c,unit])
2063    Tors = calcTorsion(Oatoms,SyOps,Amat)
2064   
2065    sig = -0.01
2066    if 'covMatrix' in covData:
2067        parmNames = []
2068        dx = .00001
2069        dadx = np.zeros(12)
2070        for i in range(12):
2071            ia = i/3
2072            ix = i%3
2073            Oatoms[ia][ix+1] -= dx
2074            a0 = calcTorsion(Oatoms,SyOps,Amat)
2075            Oatoms[ia][ix+1] += 2*dx
2076            dadx[i] = (calcTorsion(Oatoms,SyOps,Amat)-a0)/(2.*dx)
2077            Oatoms[ia][ix+1] -= dx           
2078        covMatrix = covData['covMatrix']
2079        varyList = covData['varyList']
2080        TorVcov = getVCov(names,varyList,covMatrix)
2081        sig = np.sqrt(np.inner(dadx,np.inner(TorVcov,dadx)))
2082        if sig < 0.01:
2083            sig = -0.01
2084   
2085    return Tors,sig
2086       
2087def GetDATSig(Oatoms,Atoms,Amat,SGData,covData={}):
2088    '''default doc string
2089   
2090    :param type name: description
2091   
2092    :returns: type name: description
2093   
2094    '''
2095
2096    def calcDist(Atoms,SyOps,Amat):
2097        XYZ = []
2098        for i,atom in enumerate(Atoms):
2099            Inv,M,T,C,U = SyOps[i]
2100            XYZ.append(np.array(atom[1:4]))
2101            XYZ[-1] = Inv*(np.inner(M,np.array(XYZ[-1]))+T)+C+U
2102            XYZ[-1] = np.inner(Amat,XYZ[-1]).T
2103        V1 = XYZ[1]-XYZ[0]
2104        return np.sqrt(np.sum(V1**2))
2105       
2106    def calcAngle(Atoms,SyOps,Amat):
2107        XYZ = []
2108        for i,atom in enumerate(Atoms):
2109            Inv,M,T,C,U = SyOps[i]
2110            XYZ.append(np.array(atom[1:4]))
2111            XYZ[-1] = Inv*(np.inner(M,np.array(XYZ[-1]))+T)+C+U
2112            XYZ[-1] = np.inner(Amat,XYZ[-1]).T
2113        V1 = XYZ[1]-XYZ[0]
2114        V1 /= np.sqrt(np.sum(V1**2))
2115        V2 = XYZ[1]-XYZ[2]
2116        V2 /= np.sqrt(np.sum(V2**2))
2117        V3 = V2-V1
2118        cang = min(1.,max((2.-np.sum(V3**2))/2.,-1.))
2119        return acosd(cang)
2120
2121    def calcTorsion(Atoms,SyOps,Amat):
2122       
2123        XYZ = []
2124        for i,atom in enumerate(Atoms):
2125            Inv,M,T,C,U = SyOps[i]
2126            XYZ.append(np.array(atom[1:4]))
2127            XYZ[-1] = Inv*(np.inner(M,np.array(XYZ[-1]))+T)+C+U
2128            XYZ[-1] = np.inner(Amat,XYZ[-1]).T
2129        V1 = XYZ[1]-XYZ[0]
2130        V2 = XYZ[2]-XYZ[1]
2131        V3 = XYZ[3]-XYZ[2]
2132        V1 /= np.sqrt(np.sum(V1**2))
2133        V2 /= np.sqrt(np.sum(V2**2))
2134        V3 /= np.sqrt(np.sum(V3**2))
2135        M = np.array([V1,V2,V3])
2136        D = nl.det(M)
2137        Ang = 1.0
2138        P12 = np.dot(V1,V2)
2139        P13 = np.dot(V1,V3)
2140        P23 = np.dot(V2,V3)
2141        Tors = acosd((P12*P23-P13)/(np.sqrt(1.-P12**2)*np.sqrt(1.-P23**2)))*D/abs(D)
2142        return Tors
2143           
2144    Inv = []
2145    SyOps = []
2146    names = []
2147    for i,atom in enumerate(Oatoms):
2148        names += atom[-1]
2149        Op,unit = Atoms[i][-1]
2150        inv = Op/abs(Op)
2151        m,t = SGData['SGOps'][abs(Op)%100-1]
2152        c = SGData['SGCen'][abs(Op)/100]
2153        SyOps.append([inv,m,t,c,unit])
2154    M = len(Oatoms)
2155    if M == 2:
2156        Val = calcDist(Oatoms,SyOps,Amat)
2157    elif M == 3:
2158        Val = calcAngle(Oatoms,SyOps,Amat)
2159    else:
2160        Val = calcTorsion(Oatoms,SyOps,Amat)
2161   
2162    sigVals = [-0.001,-0.01,-0.01]
2163    sig = sigVals[M-3]
2164    if 'covMatrix' in covData:
2165        parmNames = []
2166        dx = .00001
2167        N = M*3
2168        dadx = np.zeros(N)
2169        for i in range(N):
2170            ia = i/3
2171            ix = i%3
2172            Oatoms[ia][ix+1] += dx
2173            if M == 2:
2174                a0 = calcDist(Oatoms,SyOps,Amat)
2175            elif M == 3:
2176                a0 = calcAngle(Oatoms,SyOps,Amat)
2177            else:
2178                a0 = calcTorsion(Oatoms,SyOps,Amat)
2179            Oatoms[ia][ix+1] -= 2*dx
2180            if M == 2:
2181                dadx[i] = (calcDist(Oatoms,SyOps,Amat)-a0)/(2.*dx)               
2182            elif M == 3:
2183                dadx[i] = (calcAngle(Oatoms,SyOps,Amat)-a0)/(2.*dx)               
2184            else:
2185                dadx[i] = (calcTorsion(Oatoms,SyOps,Amat)-a0)/(2.*dx)
2186        covMatrix = covData['covMatrix']
2187        varyList = covData['varyList']
2188        Vcov = getVCov(names,varyList,covMatrix)
2189        sig = np.sqrt(np.inner(dadx,np.inner(Vcov,dadx)))
2190        if sig < sigVals[M-3]:
2191            sig = sigVals[M-3]
2192   
2193    return Val,sig
2194       
2195def ValEsd(value,esd=0,nTZ=False):
2196    '''Format a floating point number with a given level of precision or
2197    with in crystallographic format with a "esd", as value(esd). If esd is
2198    negative the number is formatted with the level of significant figures
2199    appropriate if abs(esd) were the esd, but the esd is not included.
2200    if the esd is zero, approximately 6 significant figures are printed.
2201    nTZ=True causes "extra" zeros to be removed after the decimal place.
2202    for example:
2203
2204      * "1.235(3)" for value=1.2346 & esd=0.003
2205      * "1.235(3)e4" for value=12346. & esd=30
2206      * "1.235(3)e6" for value=0.12346e7 & esd=3000
2207      * "1.235" for value=1.2346 & esd=-0.003
2208      * "1.240" for value=1.2395 & esd=-0.003
2209      * "1.24" for value=1.2395 & esd=-0.003 with nTZ=True
2210      * "1.23460" for value=1.2346 & esd=0.0
2211
2212    :param float value: number to be formatted
2213    :param float esd: uncertainty or if esd < 0, specifies level of
2214      precision to be shown e.g. esd=-0.01 gives 2 places beyond decimal
2215    :param bool nTZ: True to remove trailing zeros (default is False)
2216    :returns: value(esd) or value as a string
2217
2218    '''
2219    # Note: this routine is Python 3 compatible -- I think
2220    cutoff = 3.16228    #=(sqrt(10); same as old GSAS   was 1.95
2221    if math.isnan(value): # invalid value, bail out
2222        return '?'
2223    if math.isnan(esd): # invalid esd, treat as zero
2224        esd = 0
2225        esdoff = 5
2226    elif esd != 0:
2227        # transform the esd to a one or two digit integer
2228        l = math.log10(abs(esd)) % 1.
2229        if l < math.log10(cutoff): l+= 1.       
2230        intesd = int(round(10**l)) # esd as integer
2231        # determine the number of digits offset for the esd
2232        esdoff = int(round(math.log10(intesd*1./abs(esd))))
2233    else:
2234        esdoff = 5
2235    valoff = 0
2236    if abs(value) < abs(esdoff): # value is effectively zero
2237        pass
2238    elif esdoff < 0 or abs(value) > 1.0e6 or abs(value) < 1.0e-4: # use scientific notation
2239        # where the digit offset is to the left of the decimal place or where too many
2240        # digits are needed
2241        if abs(value) > 1:
2242            valoff = int(math.log10(abs(value)))
2243        elif abs(value) > 0:
2244            valoff = int(math.log10(abs(value))-0.9999999)
2245        else:
2246            valoff = 0
2247    if esd != 0:
2248        if valoff+esdoff < 0:
2249            valoff = esdoff = 0
2250        out = ("{:."+str(valoff+esdoff)+"f}").format(value/10**valoff) # format the value
2251    elif valoff != 0: # esd = 0; exponential notation ==> esdoff decimal places
2252        out = ("{:."+str(esdoff)+"f}").format(value/10**valoff) # format the value
2253    else: # esd = 0; non-exponential notation ==> esdoff+1 significant digits
2254        if abs(value) > 0:           
2255            extra = -math.log10(abs(value))
2256        else:
2257            extra = 0
2258        if extra > 0: extra += 1
2259        out = ("{:."+str(max(0,esdoff+int(extra)))+"f}").format(value) # format the value
2260    if esd > 0:
2261        out += ("({:d})").format(intesd)  # add the esd
2262    elif nTZ and '.' in out:
2263        out = out.rstrip('0')  # strip zeros to right of decimal
2264        out = out.rstrip('.')  # and decimal place when not needed
2265    if valoff != 0:
2266        out += ("e{:d}").format(valoff) # add an exponent, when needed
2267    return out
2268   
2269################################################################################
2270##### Texture fitting stuff
2271################################################################################
2272
2273def FitTexture(General,Gangls,refData,keyList,pgbar):
2274    import pytexture as ptx
2275    ptx.pyqlmninit()            #initialize fortran arrays for spherical harmonics
2276   
2277    def printSpHarm(textureData,SHtextureSig):
2278        print '\n Spherical harmonics texture: Order:' + str(textureData['Order'])
2279        names = ['omega','chi','phi']
2280        namstr = '  names :'
2281        ptstr =  '  values:'
2282        sigstr = '  esds  :'
2283        for name in names:
2284            namstr += '%12s'%('Sample '+name)
2285            ptstr += '%12.3f'%(textureData['Sample '+name][1])
2286            if 'Sample '+name in SHtextureSig:
2287                sigstr += '%12.3f'%(SHtextureSig['Sample '+name])
2288            else:
2289                sigstr += 12*' '
2290        print namstr
2291        print ptstr
2292        print sigstr
2293        print '\n Texture coefficients:'
2294        SHcoeff = textureData['SH Coeff'][1]
2295        SHkeys = SHcoeff.keys()
2296        nCoeff = len(SHcoeff)
2297        nBlock = nCoeff/10+1
2298        iBeg = 0
2299        iFin = min(iBeg+10,nCoeff)
2300        for block in range(nBlock):
2301            namstr = '  names :'
2302            ptstr =  '  values:'
2303            sigstr = '  esds  :'
2304            for name in SHkeys[iBeg:iFin]:
2305                if 'C' in name:
2306                    namstr += '%12s'%(name)
2307                    ptstr += '%12.3f'%(SHcoeff[name])
2308                    if name in SHtextureSig:
2309                        sigstr += '%12.3f'%(SHtextureSig[name])
2310                    else:
2311                        sigstr += 12*' '
2312            print namstr
2313            print ptstr
2314            print sigstr
2315            iBeg += 10
2316            iFin = min(iBeg+10,nCoeff)
2317           
2318    def Dict2Values(parmdict, varylist):
2319        '''Use before call to leastsq to setup list of values for the parameters
2320        in parmdict, as selected by key in varylist'''
2321        return [parmdict[key] for key in varylist] 
2322       
2323    def Values2Dict(parmdict, varylist, values):
2324        ''' Use after call to leastsq to update the parameter dictionary with
2325        values corresponding to keys in varylist'''
2326        parmdict.update(zip(varylist,values))
2327       
2328    def errSpHarm(values,SGData,cell,Gangls,shModel,refData,parmDict,varyList,pgbar):
2329        parmDict.update(zip(varyList,values))
2330        Mat = np.empty(0)
2331        sumObs = 0
2332        Sangls = [parmDict['Sample '+'omega'],parmDict['Sample '+'chi'],parmDict['Sample '+'phi']]
2333        for hist in Gangls.keys():
2334            Refs = refData[hist]
2335            Refs[:,5] = np.where(Refs[:,5]>0.,Refs[:,5],0.)
2336            wt = 1./np.sqrt(np.max(Refs[:,4],.25))
2337#            wt = 1./np.max(Refs[:,4],.25)
2338            sumObs += np.sum(wt*Refs[:,5])
2339            Refs[:,6] = 1.
2340            H = Refs[:,:3]
2341            phi,beta = G2lat.CrsAng(H,cell,SGData)
2342            psi,gam,x,x = G2lat.SamAng(Refs[:,3]/2.,Gangls[hist],Sangls,False) #assume not Bragg-Brentano!
2343            for item in parmDict:
2344                if 'C' in item:
2345                    L,M,N = eval(item.strip('C'))
2346                    Kcl = G2lat.GetKcl(L,N,SGData['SGLaue'],phi,beta)
2347                    Ksl,x,x = G2lat.GetKsl(L,M,shModel,psi,gam)
2348                    Lnorm = G2lat.Lnorm(L)
2349                    Refs[:,6] += parmDict[item]*Lnorm*Kcl*Ksl
2350            mat = wt*(Refs[:,5]-Refs[:,6])
2351            Mat = np.concatenate((Mat,mat))
2352        sumD = np.sum(np.abs(Mat))
2353        R = min(100.,100.*sumD/sumObs)
2354        pgbar.Update(R,newmsg='Residual = %5.2f'%(R))
2355        print ' Residual: %.3f%%'%(R)
2356        return Mat
2357       
2358    def dervSpHarm(values,SGData,cell,Gangls,shModel,refData,parmDict,varyList,pgbar):
2359        Mat = np.empty(0)
2360        Sangls = [parmDict['Sample omega'],parmDict['Sample chi'],parmDict['Sample phi']]
2361        for hist in Gangls.keys():
2362            mat = np.zeros((len(varyList),len(refData[hist])))
2363            Refs = refData[hist]
2364            H = Refs[:,:3]
2365            wt = 1./np.sqrt(np.max(Refs[:,4],.25))
2366#            wt = 1./np.max(Refs[:,4],.25)
2367            phi,beta = G2lat.CrsAng(H,cell,SGData)
2368            psi,gam,dPdA,dGdA = G2lat.SamAng(Refs[:,3]/2.,Gangls[hist],Sangls,False) #assume not Bragg-Brentano!
2369            for j,item in enumerate(varyList):
2370                if 'C' in item:
2371                    L,M,N = eval(item.strip('C'))
2372                    Kcl = G2lat.GetKcl(L,N,SGData['SGLaue'],phi,beta)
2373                    Ksl,dKdp,dKdg = G2lat.GetKsl(L,M,shModel,psi,gam)
2374                    Lnorm = G2lat.Lnorm(L)
2375                    mat[j] = -wt*Lnorm*Kcl*Ksl
2376                    for k,itema in enumerate(['Sample omega','Sample chi','Sample phi']):
2377                        try:
2378                            l = varyList.index(itema)
2379                            mat[l] -= parmDict[item]*wt*Lnorm*Kcl*(dKdp*dPdA[k]+dKdg*dGdA[k])
2380                        except ValueError:
2381                            pass
2382            if len(Mat):
2383                Mat = np.concatenate((Mat,mat.T))
2384            else:
2385                Mat = mat.T
2386        print 'deriv'
2387        return Mat
2388
2389    print ' Fit texture for '+General['Name']
2390    SGData = General['SGData']
2391    cell = General['Cell'][1:7]
2392    Texture = General['SH Texture']
2393    if not Texture['Order']:
2394        return 'No spherical harmonics coefficients'
2395    varyList = []
2396    parmDict = copy.copy(Texture['SH Coeff'][1])
2397    for item in ['Sample omega','Sample chi','Sample phi']:
2398        parmDict[item] = Texture[item][1]
2399        if Texture[item][0]:
2400            varyList.append(item)
2401    if Texture['SH Coeff'][0]:
2402        varyList += Texture['SH Coeff'][1].keys()
2403    while True:
2404        begin = time.time()
2405        values =  np.array(Dict2Values(parmDict, varyList))
2406        result = so.leastsq(errSpHarm,values,Dfun=dervSpHarm,full_output=True,ftol=1.e-6,
2407            args=(SGData,cell,Gangls,Texture['Model'],refData,parmDict,varyList,pgbar))
2408        ncyc = int(result[2]['nfev']/2)
2409        if ncyc:
2410            runtime = time.time()-begin   
2411            chisq = np.sum(result[2]['fvec']**2)
2412            Values2Dict(parmDict, varyList, result[0])
2413            GOF = chisq/(len(result[2]['fvec'])-len(varyList))       #reduced chi^2
2414            print 'Number of function calls:',result[2]['nfev'],' Number of observations: ',len(result[2]['fvec']),' Number of parameters: ',len(varyList)
2415            print 'refinement time = %8.3fs, %8.3fs/cycle'%(runtime,runtime/ncyc)
2416            try:
2417                sig = np.sqrt(np.diag(result[1])*GOF)
2418                if np.any(np.isnan(sig)):
2419                    print '*** Least squares aborted - some invalid esds possible ***'
2420                break                   #refinement succeeded - finish up!
2421            except ValueError:          #result[1] is None on singular matrix
2422                print '**** Refinement failed - singular matrix ****'
2423                return None
2424        else:
2425            break
2426   
2427    if ncyc:
2428        for parm in parmDict:
2429            if 'C' in parm:
2430                Texture['SH Coeff'][1][parm] = parmDict[parm]
2431            else:
2432                Texture[parm][1] = parmDict[parm] 
2433        sigDict = dict(zip(varyList,sig))
2434        printSpHarm(Texture,sigDict)
2435       
2436    return None
2437   
2438################################################################################
2439##### Fourier & charge flip stuff
2440################################################################################
2441
2442def adjHKLmax(SGData,Hmax):
2443    '''default doc string
2444   
2445    :param type name: description
2446   
2447    :returns: type name: description
2448   
2449    '''
2450    if SGData['SGLaue'] in ['3','3m1','31m','6/m','6/mmm']:
2451        Hmax[0] = int(math.ceil(Hmax[0]/6.))*6
2452        Hmax[1] = int(math.ceil(Hmax[1]/6.))*6
2453        Hmax[2] = int(math.ceil(Hmax[2]/4.))*4
2454    else:
2455        Hmax[0] = int(math.ceil(Hmax[0]/4.))*4
2456        Hmax[1] = int(math.ceil(Hmax[1]/4.))*4
2457        Hmax[2] = int(math.ceil(Hmax[2]/4.))*4
2458
2459def OmitMap(data,reflDict,pgbar=None):
2460    '''default doc string
2461   
2462    :param type name: description
2463   
2464    :returns: type name: description
2465   
2466    '''
2467    generalData = data['General']
2468    if not generalData['Map']['MapType']:
2469        print '**** ERROR - Fourier map not defined'
2470        return
2471    mapData = generalData['Map']
2472    dmin = mapData['Resolution']
2473    SGData = generalData['SGData']
2474    SGMT = np.array([ops[0].T for ops in SGData['SGOps']])
2475    SGT = np.array([ops[1] for ops in SGData['SGOps']])
2476    cell = generalData['Cell'][1:8]       
2477    A = G2lat.cell2A(cell[:6])
2478    Hmax = np.asarray(G2lat.getHKLmax(dmin,SGData,A),dtype='i')+1
2479    adjHKLmax(SGData,Hmax)
2480    Fhkl = np.zeros(shape=2*Hmax,dtype='c16')
2481    time0 = time.time()
2482    for iref,ref in enumerate(reflDict['RefList']):
2483        if ref[4] >= dmin:
2484            Fosq,Fcsq,ph = ref[8:11]
2485            Uniq = np.inner(ref[:3],SGMT)
2486            Phi = np.inner(ref[:3],SGT)
2487            for i,hkl in enumerate(Uniq):        #uses uniq
2488                hkl = np.asarray(hkl,dtype='i')
2489                dp = 360.*Phi[i]                #and phi
2490                a = cosd(ph+dp)
2491                b = sind(ph+dp)
2492                phasep = complex(a,b)
2493                phasem = complex(a,-b)
2494                Fo = np.sqrt(Fosq)
2495                if '2Fo-Fc' in mapData['MapType']:
2496                    F = 2.*np.sqrt(Fosq)-np.sqrt(Fcsq)
2497                else:
2498                    F = np.sqrt(Fosq)
2499                h,k,l = hkl+Hmax
2500                Fhkl[h,k,l] = F*phasep
2501                h,k,l = -hkl+Hmax
2502                Fhkl[h,k,l] = F*phasem
2503    rho0 = fft.fftn(fft.fftshift(Fhkl))/cell[6]
2504    M = np.mgrid[0:4,0:4,0:4]
2505    blkIds = np.array(zip(M[0].flatten(),M[1].flatten(),M[2].flatten()))
2506    iBeg = blkIds*rho0.shape/4
2507    iFin = (blkIds+1)*rho0.shape/4
2508    rho_omit = np.zeros_like(rho0)
2509    nBlk = 0
2510    for iB,iF in zip(iBeg,iFin):
2511        rho1 = np.copy(rho0)
2512        rho1[iB[0]:iF[0],iB[1]:iF[1],iB[2]:iF[2]] = 0.
2513        Fnew = fft.ifftshift(fft.ifftn(rho1))
2514        Fnew = np.where(Fnew,Fnew,1.0)           #avoid divide by zero
2515        phase = Fnew/np.absolute(Fnew)
2516        OFhkl = np.absolute(Fhkl)*phase
2517        rho1 = np.real(fft.fftn(fft.fftshift(OFhkl)))*(1.+0j)
2518        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]])
2519        nBlk += 1
2520        pgbar.Update(nBlk)
2521    mapData['rho'] = np.real(rho_omit)/cell[6]
2522    mapData['rhoMax'] = max(np.max(mapData['rho']),-np.min(mapData['rho']))
2523    mapData['minmax'] = [np.max(mapData['rho']),np.min(mapData['rho'])]
2524    print 'Omit map time: %.4f'%(time.time()-time0),'no. elements: %d'%(Fhkl.size)
2525    return mapData
2526   
2527def FourierMap(data,reflDict):
2528    '''default doc string
2529   
2530    :param type name: description
2531   
2532    :returns: type name: description
2533   
2534    '''
2535    generalData = data['General']
2536    mapData = generalData['Map']
2537    dmin = mapData['Resolution']
2538    SGData = generalData['SGData']
2539    SGMT = np.array([ops[0].T for ops in SGData['SGOps']])
2540    SGT = np.array([ops[1] for ops in SGData['SGOps']])
2541    cell = generalData['Cell'][1:8]       
2542    A = G2lat.cell2A(cell[:6])
2543    Hmax = np.asarray(G2lat.getHKLmax(dmin,SGData,A),dtype='i')+1
2544    adjHKLmax(SGData,Hmax)
2545    Fhkl = np.zeros(shape=2*Hmax,dtype='c16')
2546#    Fhkl[0,0,0] = generalData['F000X']
2547    time0 = time.time()
2548    for iref,ref in enumerate(reflDict['RefList']):
2549        if ref[4] > dmin:
2550            Fosq,Fcsq,ph = ref[8:11]
2551            Uniq = np.inner(ref[:3],SGMT)
2552            Phi = np.inner(ref[:3],SGT)
2553            for i,hkl in enumerate(Uniq):        #uses uniq
2554                hkl = np.asarray(hkl,dtype='i')
2555                dp = 360.*Phi[i]                #and phi
2556                a = cosd(ph+dp)
2557                b = sind(ph+dp)
2558                phasep = complex(a,b)
2559                phasem = complex(a,-b)
2560                if 'Fobs' in mapData['MapType']:
2561                    F = np.where(Fosq>0.,np.sqrt(Fosq),0.)
2562                    h,k,l = hkl+Hmax
2563                    Fhkl[h,k,l] = F*phasep
2564                    h,k,l = -hkl+Hmax
2565                    Fhkl[h,k,l] = F*phasem
2566                elif 'Fcalc' in mapData['MapType']:
2567                    F = np.sqrt(Fcsq)
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                elif 'delt-F' in mapData['MapType']:
2573                    dF = np.where(Fosq>0.,np.sqrt(Fosq),0.)-np.sqrt(Fcsq)
2574                    h,k,l = hkl+Hmax
2575                    Fhkl[h,k,l] = dF*phasep
2576                    h,k,l = -hkl+Hmax
2577                    Fhkl[h,k,l] = dF*phasem
2578                elif '2*Fo-Fc' in mapData['MapType']:
2579                    F = 2.*np.where(Fosq>0.,np.sqrt(Fosq),0.)-np.sqrt(Fcsq)
2580                    h,k,l = hkl+Hmax
2581                    Fhkl[h,k,l] = F*phasep
2582                    h,k,l = -hkl+Hmax
2583                    Fhkl[h,k,l] = F*phasem
2584                elif 'Patterson' in mapData['MapType']:
2585                    h,k,l = hkl+Hmax
2586                    Fhkl[h,k,l] = complex(Fosq,0.)
2587                    h,k,l = -hkl+Hmax
2588                    Fhkl[h,k,l] = complex(Fosq,0.)
2589    rho = fft.fftn(fft.fftshift(Fhkl))/cell[6]
2590    print 'Fourier map time: %.4f'%(time.time()-time0),'no. elements: %d'%(Fhkl.size)
2591    mapData['Type'] = reflDict['Type']
2592    mapData['rho'] = np.real(rho)
2593    mapData['rhoMax'] = max(np.max(mapData['rho']),-np.min(mapData['rho']))
2594    mapData['minmax'] = [np.max(mapData['rho']),np.min(mapData['rho'])]
2595   
2596def Fourier4DMap(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    map4DData = generalData['4DmapData']
2606    mapData = generalData['Map']
2607    dmin = mapData['Resolution']
2608    SGData = generalData['SGData']
2609    SSGData = generalData['SSGData']
2610    SSGMT = np.array([ops[0].T for ops in SSGData['SSGOps']])
2611    SSGT = np.array([ops[1] for ops in SSGData['SSGOps']])
2612    cell = generalData['Cell'][1:8]       
2613    A = G2lat.cell2A(cell[:6])
2614    maxM = 4
2615    Hmax = G2lat.getHKLmax(dmin,SGData,A)+[maxM,]
2616    adjHKLmax(SGData,Hmax)
2617    Hmax = np.asarray(Hmax,dtype='i')+1
2618    Fhkl = np.zeros(shape=2*Hmax,dtype='c16')
2619    time0 = time.time()
2620    for iref,ref in enumerate(reflDict['RefList']):
2621        if ref[5] > dmin:
2622            Fosq,Fcsq,ph = ref[9:12]
2623            Fosq = np.where(Fosq>0.,Fosq,0.)    #can't use Fo^2 < 0
2624            Uniq = np.inner(ref[:4],SSGMT)
2625            Phi = np.inner(ref[:4],SSGT)
2626            for i,hkl in enumerate(Uniq):        #uses uniq
2627                hkl = np.asarray(hkl,dtype='i')
2628                dp = 360.*Phi[i]                #and phi
2629                a = cosd(ph+dp)
2630                b = sind(ph+dp)
2631                phasep = complex(a,b)
2632                phasem = complex(a,-b)
2633                if 'Fobs' in mapData['MapType']:
2634                    F = np.sqrt(Fosq)
2635                    h,k,l,m = hkl+Hmax
2636                    Fhkl[h,k,l,m] = F*phasep
2637                    h,k,l,m = -hkl+Hmax
2638                    Fhkl[h,k,l,m] = F*phasem
2639                elif 'Fcalc' in mapData['MapType']:
2640                    F = np.sqrt(Fcsq)
2641                    h,k,l,m = hkl+Hmax
2642                    Fhkl[h,k,l,m] = F*phasep
2643                    h,k,l,m = -hkl+Hmax
2644                    Fhkl[h,k,l,m] = F*phasem                   
2645                elif 'delt-F' in mapData['MapType']:
2646                    dF = np.sqrt(Fosq)-np.sqrt(Fcsq)
2647                    h,k,l,m = hkl+Hmax
2648                    Fhkl[h,k,l,m] = dF*phasep
2649                    h,k,l,m = -hkl+Hmax
2650                    Fhkl[h,k,l,m] = dF*phasem
2651    SSrho = fft.fftn(fft.fftshift(Fhkl))/cell[6]          #4D map
2652    rho = fft.fftn(fft.fftshift(Fhkl[:,:,:,maxM+1]))/cell[6]    #3D map
2653    map4DData['rho'] = np.real(SSrho)
2654    map4DData['rhoMax'] = max(np.max(map4DData['rho']),-np.min(map4DData['rho']))
2655    map4DData['minmax'] = [np.max(map4DData['rho']),np.min(map4DData['rho'])]
2656    map4DData['Type'] = reflDict['Type']
2657    mapData['Type'] = reflDict['Type']
2658    mapData['rho'] = np.real(rho)
2659    mapData['rhoMax'] = max(np.max(mapData['rho']),-np.min(mapData['rho']))
2660    mapData['minmax'] = [np.max(mapData['rho']),np.min(mapData['rho'])]
2661    print 'Fourier map time: %.4f'%(time.time()-time0),'no. elements: %d'%(Fhkl.size)
2662
2663# map printing for testing purposes
2664def printRho(SGLaue,rho,rhoMax):                         
2665    '''default doc string
2666   
2667    :param type name: description
2668   
2669    :returns: type name: description
2670   
2671    '''
2672    dim = len(rho.shape)
2673    if dim == 2:
2674        ix,jy = rho.shape
2675        for j in range(jy):
2676            line = ''
2677            if SGLaue in ['3','3m1','31m','6/m','6/mmm']:
2678                line += (jy-j)*'  '
2679            for i in range(ix):
2680                r = int(100*rho[i,j]/rhoMax)
2681                line += '%4d'%(r)
2682            print line+'\n'
2683    else:
2684        ix,jy,kz = rho.shape
2685        for k in range(kz):
2686            print 'k = ',k
2687            for j in range(jy):
2688                line = ''
2689                if SGLaue in ['3','3m1','31m','6/m','6/mmm']:
2690                    line += (jy-j)*'  '
2691                for i in range(ix):
2692                    r = int(100*rho[i,j,k]/rhoMax)
2693                    line += '%4d'%(r)
2694                print line+'\n'
2695## keep this
2696               
2697def findOffset(SGData,A,Fhkl):   
2698    '''default doc string
2699   
2700    :param type name: description
2701   
2702    :returns: type name: description
2703   
2704    '''
2705    if SGData['SpGrp'] == 'P 1':
2706        return [0,0,0]   
2707    hklShape = Fhkl.shape
2708    hklHalf = np.array(hklShape)/2
2709    sortHKL = np.argsort(Fhkl.flatten())
2710    Fdict = {}
2711    for hkl in sortHKL:
2712        HKL = np.unravel_index(hkl,hklShape)
2713        F = Fhkl[HKL[0]][HKL[1]][HKL[2]]
2714        if F == 0.:
2715            break
2716        Fdict['%.6f'%(np.absolute(F))] = hkl
2717    Flist = np.flipud(np.sort(Fdict.keys()))
2718    F = str(1.e6)
2719    i = 0
2720    DH = []
2721    Dphi = []
2722    SGMT = np.array([ops[0].T for ops in SGData['SGOps']])
2723    SGT = np.array([ops[1] for ops in SGData['SGOps']])
2724    Hmax = 2*np.asarray(G2lat.getHKLmax(3.5,SGData,A),dtype='i')
2725    for F in Flist:
2726        hkl = np.unravel_index(Fdict[F],hklShape)
2727        if np.any(np.abs(hkl-hklHalf)-Hmax > 0):
2728            continue
2729        iabsnt,mulp,Uniq,Phi = G2spc.GenHKLf(list(hkl-hklHalf),SGData)
2730        Uniq = np.array(Uniq,dtype='i')
2731        Phi = np.array(Phi)
2732        Uniq = np.concatenate((Uniq,-Uniq))+hklHalf         # put in Friedel pairs & make as index to Farray
2733        Phi = np.concatenate((Phi,-Phi))                      # and their phase shifts
2734        Fh0 = Fhkl[hkl[0],hkl[1],hkl[2]]
2735        ang0 = np.angle(Fh0,deg=True)/360.
2736        for H,phi in zip(Uniq,Phi)[1:]:
2737            ang = (np.angle(Fhkl[H[0],H[1],H[2]],deg=True)/360.-phi)
2738            dH = H-hkl
2739            dang = ang-ang0
2740            DH.append(dH)
2741            Dphi.append((dang+.5) % 1.0)
2742        if i > 20 or len(DH) > 30:
2743            break
2744        i += 1
2745    DH = np.array(DH)
2746    print ' map offset no.of terms: %d from %d reflections'%(len(DH),len(Flist))
2747    Dphi = np.array(Dphi)
2748    steps = np.array(hklShape)
2749    X,Y,Z = np.mgrid[0:1:1./steps[0],0:1:1./steps[1],0:1:1./steps[2]]
2750    XYZ = np.array(zip(X.flatten(),Y.flatten(),Z.flatten()))
2751    Dang = (np.dot(XYZ,DH.T)+.5)%1.-Dphi
2752    Mmap = np.reshape(np.sum((Dang)**2,axis=1),newshape=steps)/len(DH)
2753    hist,bins = np.histogram(Mmap,bins=1000)
2754#    for i,item in enumerate(hist[:10]):
2755#        print item,bins[i]
2756    chisq = np.min(Mmap)
2757    DX = -np.array(np.unravel_index(np.argmin(Mmap),Mmap.shape))
2758    print ' map offset chi**2: %.3f, map offset: %d %d %d'%(chisq,DX[0],DX[1],DX[2])
2759#    print (np.dot(DX,DH.T)+.5)%1.-Dphi
2760    return DX
2761   
2762def ChargeFlip(data,reflDict,pgbar):
2763    '''default doc string
2764   
2765    :param type name: description
2766   
2767    :returns: type name: description
2768   
2769    '''
2770    generalData = data['General']
2771    mapData = generalData['Map']
2772    flipData = generalData['Flip']
2773    FFtable = {}
2774    if 'None' not in flipData['Norm element']:
2775        normElem = flipData['Norm element'].upper()
2776        FFs = G2el.GetFormFactorCoeff(normElem.split('+')[0].split('-')[0])
2777        for ff in FFs:
2778            if ff['Symbol'] == normElem:
2779                FFtable.update(ff)
2780    dmin = flipData['Resolution']
2781    SGData = generalData['SGData']
2782    SGMT = np.array([ops[0].T for ops in SGData['SGOps']])
2783    SGT = np.array([ops[1] for ops in SGData['SGOps']])
2784    cell = generalData['Cell'][1:8]       
2785    A = G2lat.cell2A(cell[:6])
2786    Vol = cell[6]
2787    im = 0
2788    if generalData['Type'] in ['modulated','magnetic',]:
2789        im = 1
2790    Hmax = np.asarray(G2lat.getHKLmax(dmin,SGData,A),dtype='i')+1
2791    adjHKLmax(SGData,Hmax)
2792    Ehkl = np.zeros(shape=2*Hmax,dtype='c16')       #2X64bits per complex no.
2793    time0 = time.time()
2794    for iref,ref in enumerate(reflDict['RefList']):
2795        dsp = ref[4+im]
2796        if im and ref[3]:   #skip super lattice reflections - result is 3D projection
2797            continue
2798        if dsp > dmin:
2799            ff = 0.1*Vol    #est. no. atoms for ~10A**3/atom
2800            if FFtable:
2801                SQ = 0.25/dsp**2
2802                ff *= G2el.ScatFac(FFtable,SQ)[0]
2803            if ref[8+im] > 0.:         #use only +ve Fobs**2
2804                E = np.sqrt(ref[8+im])/ff
2805            else:
2806                E = 0.
2807            ph = ref[10]
2808            ph = rn.uniform(0.,360.)
2809            Uniq = np.inner(ref[:3],SGMT)
2810            Phi = np.inner(ref[:3],SGT)
2811            for i,hkl in enumerate(Uniq):        #uses uniq
2812                hkl = np.asarray(hkl,dtype='i')
2813                dp = 360.*Phi[i]                #and phi
2814                a = cosd(ph+dp)
2815                b = sind(ph+dp)
2816                phasep = complex(a,b)
2817                phasem = complex(a,-b)
2818                h,k,l = hkl+Hmax
2819                Ehkl[h,k,l] = E*phasep
2820                h,k,l = -hkl+Hmax       #Friedel pair refl.
2821                Ehkl[h,k,l] = E*phasem
2822#    Ehkl[Hmax] = 0.00001           #this to preserve F[0,0,0]
2823    testHKL = np.array(flipData['testHKL'])+Hmax
2824    CEhkl = copy.copy(Ehkl)
2825    MEhkl = ma.array(Ehkl,mask=(Ehkl==0.0))
2826    Emask = ma.getmask(MEhkl)
2827    sumE = np.sum(ma.array(np.absolute(CEhkl),mask=Emask))
2828    Ncyc = 0
2829    old = np.seterr(all='raise')
2830    twophases = []
2831    while True:       
2832        CErho = np.real(fft.fftn(fft.fftshift(CEhkl)))*(1.+0j)
2833        CEsig = np.std(CErho)
2834        CFrho = np.where(np.real(CErho) >= flipData['k-factor']*CEsig,CErho,-CErho)
2835        CFrho = np.where(np.real(CErho) <= flipData['k-Max']*CEsig,CFrho,-CFrho)      #solves U atom problem!
2836        CFhkl = fft.ifftshift(fft.ifftn(CFrho))
2837        CFhkl = np.where(CFhkl,CFhkl,1.0)           #avoid divide by zero
2838        phase = CFhkl/np.absolute(CFhkl)
2839        twophases.append([np.angle(phase[h,k,l]) for h,k,l in testHKL])
2840        CEhkl = np.absolute(Ehkl)*phase
2841        Ncyc += 1
2842        sumCF = np.sum(ma.array(np.absolute(CFhkl),mask=Emask))
2843        DEhkl = np.absolute(np.absolute(Ehkl)/sumE-np.absolute(CFhkl)/sumCF)
2844        Rcf = min(100.,np.sum(ma.array(DEhkl,mask=Emask)*100.))
2845        if Rcf < 5.:
2846            break
2847        GoOn = pgbar.Update(Rcf,newmsg='%s%8.3f%s\n%s %d'%('Residual Rcf =',Rcf,'%','No.cycles = ',Ncyc))[0]
2848        if not GoOn or Ncyc > 10000:
2849            break
2850    np.seterr(**old)
2851    print ' Charge flip time: %.4f'%(time.time()-time0),'no. elements: %d'%(Ehkl.size)
2852    CErho = np.real(fft.fftn(fft.fftshift(CEhkl)))/10.  #? to get on same scale as e-map
2853    print ' No.cycles = ',Ncyc,'Residual Rcf =%8.3f%s'%(Rcf,'%')+' Map size:',CErho.shape
2854    roll = findOffset(SGData,A,CEhkl)               #CEhkl needs to be just the observed set, not the full set!
2855       
2856    mapData['Rcf'] = Rcf
2857    mapData['rho'] = np.roll(np.roll(np.roll(CErho,roll[0],axis=0),roll[1],axis=1),roll[2],axis=2)
2858    mapData['rhoMax'] = max(np.max(mapData['rho']),-np.min(mapData['rho']))
2859    mapData['minmax'] = [np.max(mapData['rho']),np.min(mapData['rho'])]
2860    mapData['Type'] = reflDict['Type']
2861    return mapData,twophases
2862   
2863def findSSOffset(SGData,SSGData,A,Fhklm):   
2864    '''default doc string
2865   
2866    :param type name: description
2867   
2868    :returns: type name: description
2869   
2870    '''
2871    if SGData['SpGrp'] == 'P 1':
2872        return [0,0,0,0]   
2873    hklmShape = Fhklm.shape
2874    hklmHalf = np.array(hklmShape)/2
2875    sortHKLM = np.argsort(Fhklm.flatten())
2876    Fdict = {}
2877    for hklm in sortHKLM:
2878        HKLM = np.unravel_index(hklm,hklmShape)
2879        F = Fhklm[HKLM[0]][HKLM[1]][HKLM[2]][HKLM[3]]
2880        if F == 0.:
2881            break
2882        Fdict['%.6f'%(np.absolute(F))] = hklm
2883    Flist = np.flipud(np.sort(Fdict.keys()))
2884    F = str(1.e6)
2885    i = 0
2886    DH = []
2887    Dphi = []
2888    SSGMT = np.array([ops[0].T for ops in SSGData['SSGOps']])
2889    SSGT = np.array([ops[1] for ops in SSGData['SSGOps']])
2890    Hmax = 2*np.asarray(G2lat.getHKLmax(3.5,SGData,A),dtype='i')
2891    for F in Flist:
2892        hklm = np.unravel_index(Fdict[F],hklmShape)
2893        if np.any(np.abs(hklm-hklmHalf)[:3]-Hmax > 0):
2894            continue
2895        Uniq = np.inner(hklm-hklmHalf,SSGMT)
2896        Phi = np.inner(hklm-hklmHalf,SSGT)
2897        Uniq = np.concatenate((Uniq,-Uniq))+hklmHalf         # put in Friedel pairs & make as index to Farray
2898        Phi = np.concatenate((Phi,-Phi))                      # and their phase shifts
2899        Fh0 = Fhklm[hklm[0],hklm[1],hklm[2],hklm[3]]
2900        ang0 = np.angle(Fh0,deg=True)/360.
2901        for H,phi in zip(Uniq,Phi)[1:]:
2902            ang = (np.angle(Fhklm[H[0],H[1],H[2],H[3]],deg=True)/360.-phi)
2903            dH = H-hklm
2904            dang = ang-ang0
2905            DH.append(dH)
2906            Dphi.append((dang+.5) % 1.0)
2907        if i > 20 or len(DH) > 30:
2908            break
2909        i += 1
2910    DH = np.array(DH)
2911    print ' map offset no.of terms: %d from %d reflections'%(len(DH),len(Flist))
2912    Dphi = np.array(Dphi)
2913    steps = np.array(hklmShape)
2914    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]]
2915    XYZT = np.array(zip(X.flatten(),Y.flatten(),Z.flatten(),T.flatten()))
2916    Dang = (np.dot(XYZT,DH.T)+.5)%1.-Dphi
2917    Mmap = np.reshape(np.sum((Dang)**2,axis=1),newshape=steps)/len(DH)
2918    hist,bins = np.histogram(Mmap,bins=1000)
2919#    for i,item in enumerate(hist[:10]):
2920#        print item,bins[i]
2921    chisq = np.min(Mmap)
2922    DX = -np.array(np.unravel_index(np.argmin(Mmap),Mmap.shape))
2923    print ' map offset chi**2: %.3f, map offset: %d %d %d %d'%(chisq,DX[0],DX[1],DX[2],DX[3])
2924#    print (np.dot(DX,DH.T)+.5)%1.-Dphi
2925    return DX
2926   
2927def SSChargeFlip(data,reflDict,pgbar):
2928    '''default doc string
2929   
2930    :param type name: description
2931   
2932    :returns: type name: description
2933   
2934    '''
2935    generalData = data['General']
2936    mapData = generalData['Map']
2937    map4DData = {}
2938    flipData = generalData['Flip']
2939    FFtable = {}
2940    if 'None' not in flipData['Norm element']:
2941        normElem = flipData['Norm element'].upper()
2942        FFs = G2el.GetFormFactorCoeff(normElem.split('+')[0].split('-')[0])
2943        for ff in FFs:
2944            if ff['Symbol'] == normElem:
2945                FFtable.update(ff)
2946    dmin = flipData['Resolution']
2947    SGData = generalData['SGData']
2948    SSGData = generalData['SSGData']
2949    SGMT = np.array([ops[0].T for ops in SGData['SGOps']])
2950    SGT = np.array([ops[1] for ops in SGData['SGOps']])
2951    SSGMT = np.array([ops[0].T for ops in SSGData['SSGOps']])
2952    SSGT = np.array([ops[1] for ops in SSGData['SSGOps']])
2953    cell = generalData['Cell'][1:8]       
2954    A = G2lat.cell2A(cell[:6])
2955    Vol = cell[6]
2956    maxM = 4
2957    Hmax = np.asarray(G2lat.getHKLmax(dmin,SGData,A)+[maxM,],dtype='i')+1
2958    adjHKLmax(SGData,Hmax)
2959    Ehkl = np.zeros(shape=2*Hmax,dtype='c16')       #2X64bits per complex no.
2960    time0 = time.time()
2961    for iref,ref in enumerate(reflDict['RefList']):
2962        dsp = ref[5]
2963        if dsp > dmin:
2964            ff = 0.1*Vol    #est. no. atoms for ~10A**3/atom
2965            if FFtable:
2966                SQ = 0.25/dsp**2
2967                ff *= G2el.ScatFac(FFtable,SQ)[0]
2968            if ref[9] > 0.:         #use only +ve Fobs**2
2969                E = np.sqrt(ref[9])/ff
2970            else:
2971                E = 0.
2972            ph = ref[11]
2973            ph = rn.uniform(0.,360.)
2974            Uniq = np.inner(ref[:4],SSGMT)
2975            Phi = np.inner(ref[:4],SSGT)
2976            for i,hklm in enumerate(Uniq):        #uses uniq
2977                hklm = np.asarray(hklm,dtype='i')
2978                dp = 360.*Phi[i]                #and phi
2979                a = cosd(ph+dp)
2980                b = sind(ph+dp)
2981                phasep = complex(a,b)
2982                phasem = complex(a,-b)
2983                h,k,l,m = hklm+Hmax
2984                Ehkl[h,k,l,m] = E*phasep
2985                h,k,l,m = -hklm+Hmax       #Friedel pair refl.
2986                Ehkl[h,k,l,m] = E*phasem
2987#    Ehkl[Hmax] = 0.00001           #this to preserve F[0,0,0]
2988    CEhkl = copy.copy(Ehkl)
2989    MEhkl = ma.array(Ehkl,mask=(Ehkl==0.0))
2990    Emask = ma.getmask(MEhkl)
2991    sumE = np.sum(ma.array(np.absolute(CEhkl),mask=Emask))
2992    Ncyc = 0
2993    old = np.seterr(all='raise')
2994    while True:       
2995        CErho = np.real(fft.fftn(fft.fftshift(CEhkl)))*(1.+0j)
2996        CEsig = np.std(CErho)
2997        CFrho = np.where(np.real(CErho) >= flipData['k-factor']*CEsig,CErho,-CErho)
2998        CFrho = np.where(np.real(CErho) <= flipData['k-Max']*CEsig,CFrho,-CFrho)      #solves U atom problem!
2999        CFhkl = fft.ifftshift(fft.ifftn(CFrho))
3000        CFhkl = np.where(CFhkl,CFhkl,1.0)           #avoid divide by zero
3001        phase = CFhkl/np.absolute(CFhkl)
3002        CEhkl = np.absolute(Ehkl)*phase
3003        Ncyc += 1
3004        sumCF = np.sum(ma.array(np.absolute(CFhkl),mask=Emask))
3005        DEhkl = np.absolute(np.absolute(Ehkl)/sumE-np.absolute(CFhkl)/sumCF)
3006        Rcf = min(100.,np.sum(ma.array(DEhkl,mask=Emask)*100.))
3007        if Rcf < 5.:
3008            break
3009        GoOn = pgbar.Update(Rcf,newmsg='%s%8.3f%s\n%s %d'%('Residual Rcf =',Rcf,'%','No.cycles = ',Ncyc))[0]
3010        if not GoOn or Ncyc > 10000:
3011            break
3012    np.seterr(**old)
3013    print ' Charge flip time: %.4f'%(time.time()-time0),'no. elements: %d'%(Ehkl.size)
3014    CErho = np.real(fft.fftn(fft.fftshift(CEhkl[:,:,:,maxM+1])))/10.    #? to get on same scale as e-map
3015    SSrho = np.real(fft.fftn(fft.fftshift(CEhkl)))/10.                  #? ditto
3016    print ' No.cycles = ',Ncyc,'Residual Rcf =%8.3f%s'%(Rcf,'%')+' Map size:',CErho.shape
3017    roll = findSSOffset(SGData,SSGData,A,CEhkl)               #CEhkl needs to be just the observed set, not the full set!
3018
3019    mapData['Rcf'] = Rcf
3020    mapData['rho'] = np.roll(np.roll(np.roll(CErho,roll[0],axis=0),roll[1],axis=1),roll[2],axis=2)
3021    mapData['rhoMax'] = max(np.max(mapData['rho']),-np.min(mapData['rho']))
3022    mapData['minmax'] = [np.max(mapData['rho']),np.min(mapData['rho'])]
3023    mapData['Type'] = reflDict['Type']
3024
3025    map4DData['Rcf'] = Rcf
3026    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))
3027    map4DData['rhoMax'] = max(np.max(map4DData['rho']),-np.min(map4DData['rho']))
3028    map4DData['minmax'] = [np.max(map4DData['rho']),np.min(map4DData['rho'])]
3029    map4DData['Type'] = reflDict['Type']
3030    return mapData,map4DData
3031   
3032def getRho(xyz,mapData):
3033    ''' get scattering density at a point by 8-point interpolation
3034    param xyz: coordinate to be probed
3035    param: mapData: dict of map data
3036   
3037    :returns: density at xyz
3038    '''
3039    rollMap = lambda rho,roll: np.roll(np.roll(np.roll(rho,roll[0],axis=0),roll[1],axis=1),roll[2],axis=2)
3040    if not len(mapData):
3041        return 0.0
3042    rho = copy.copy(mapData['rho'])     #don't mess up original
3043    if not len(rho):
3044        return 0.0
3045    mapShape = np.array(rho.shape)
3046    mapStep = 1./mapShape
3047    X = np.array(xyz)%1.    #get into unit cell
3048    I = np.array(X*mapShape,dtype='int')
3049    D = X-I*mapStep         #position inside map cell
3050    D12 = D[0]*D[1]
3051    D13 = D[0]*D[2]
3052    D23 = D[1]*D[2]
3053    D123 = np.prod(D)
3054    Rho = rollMap(rho,-I)    #shifts map so point is in corner
3055    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]+  \
3056        Rho[1,1,1]*D123+Rho[0,1,1]*(D23-D123)+Rho[1,0,1]*(D13-D123)+Rho[1,1,0]*(D12-D123)+  \
3057        Rho[0,0,0]*(D12+D13+D23-D123)-Rho[0,0,1]*(D13+D23-D123)-    \
3058        Rho[0,1,0]*(D23+D12-D123)-Rho[1,0,0]*(D13+D12-D123)   
3059    return R
3060       
3061def SearchMap(generalData,drawingData,Neg=False):
3062    '''Does a search of a density map for peaks meeting the criterion of peak
3063    height is greater than mapData['cutOff']/100 of mapData['rhoMax'] where
3064    mapData is data['General']['mapData']; the map is also in mapData.
3065
3066    :param generalData: the phase data structure; includes the map
3067    :param drawingData: the drawing data structure
3068    :param Neg:  if True then search for negative peaks (i.e. H-atoms & neutron data)
3069
3070    :returns: (peaks,mags,dzeros) where
3071
3072        * peaks : ndarray
3073          x,y,z positions of the peaks found in the map
3074        * mags : ndarray
3075          the magnitudes of the peaks
3076        * dzeros : ndarray
3077          the distance of the peaks from  the unit cell origin
3078        * dcent : ndarray
3079          the distance of the peaks from  the unit cell center
3080
3081    '''       
3082    rollMap = lambda rho,roll: np.roll(np.roll(np.roll(rho,roll[0],axis=0),roll[1],axis=1),roll[2],axis=2)
3083   
3084    norm = 1./(np.sqrt(3.)*np.sqrt(2.*np.pi)**3)
3085   
3086#    def noDuplicate(xyz,peaks,Amat):
3087#        XYZ = np.inner(Amat,xyz)
3088#        if True in [np.allclose(XYZ,np.inner(Amat,peak),atol=0.5) for peak in peaks]:
3089#            print ' Peak',xyz,' <0.5A from another peak'
3090#            return False
3091#        return True
3092#                           
3093    def fixSpecialPos(xyz,SGData,Amat):
3094        equivs = G2spc.GenAtom(xyz,SGData,Move=True)
3095        X = []
3096        xyzs = [equiv[0] for equiv in equivs]
3097        for x in xyzs:
3098            if np.sqrt(np.sum(np.inner(Amat,xyz-x)**2,axis=0))<0.5:
3099                X.append(x)
3100        if len(X) > 1:
3101            return np.average(X,axis=0)
3102        else:
3103            return xyz
3104       
3105    def rhoCalc(parms,rX,rY,rZ,res,SGLaue):
3106        Mag,x0,y0,z0,sig = parms
3107        z = -((x0-rX)**2+(y0-rY)**2+(z0-rZ)**2)/(2.*sig**2)
3108#        return norm*Mag*np.exp(z)/(sig*res**3)     #not slower but some faults in LS
3109        return norm*Mag*(1.+z+z**2/2.)/(sig*res**3)
3110       
3111    def peakFunc(parms,rX,rY,rZ,rho,res,SGLaue):
3112        Mag,x0,y0,z0,sig = parms
3113        M = rho-rhoCalc(parms,rX,rY,rZ,res,SGLaue)
3114        return M
3115       
3116    def peakHess(parms,rX,rY,rZ,rho,res,SGLaue):
3117        Mag,x0,y0,z0,sig = parms
3118        dMdv = np.zeros(([5,]+list(rX.shape)))
3119        delt = .01
3120        for i in range(5):
3121            parms[i] -= delt
3122            rhoCm = rhoCalc(parms,rX,rY,rZ,res,SGLaue)
3123            parms[i] += 2.*delt
3124            rhoCp = rhoCalc(parms,rX,rY,rZ,res,SGLaue)
3125            parms[i] -= delt
3126            dMdv[i] = (rhoCp-rhoCm)/(2.*delt)
3127        rhoC = rhoCalc(parms,rX,rY,rZ,res,SGLaue)
3128        Vec = np.sum(np.sum(np.sum(dMdv*(rho-rhoC),axis=3),axis=2),axis=1)
3129        dMdv = np.reshape(dMdv,(5,rX.size))
3130        Hess = np.inner(dMdv,dMdv)
3131       
3132        return Vec,Hess
3133       
3134    phaseName = generalData['Name']
3135    SGData = generalData['SGData']
3136    Amat,Bmat = G2lat.cell2AB(generalData['Cell'][1:7])
3137    peaks = []
3138    mags = []
3139    dzeros = []
3140    dcent = []
3141    try:
3142        mapData = generalData['Map']
3143        contLevel = mapData['cutOff']*mapData['rhoMax']/100.
3144        if Neg:
3145            rho = -copy.copy(mapData['rho'])     #flip +/-
3146        else:
3147            rho = copy.copy(mapData['rho'])     #don't mess up original
3148        mapHalf = np.array(rho.shape)/2
3149        res = mapData['Resolution']
3150        incre = np.array(rho.shape,dtype=np.float)
3151        step = max(1.0,1./res)+1
3152        steps = np.array((3*[step,]),dtype='int32')
3153    except KeyError:
3154        print '**** ERROR - Fourier map not defined'
3155        return peaks,mags
3156    rhoMask = ma.array(rho,mask=(rho<contLevel))
3157    indices = (-1,0,1)
3158    rolls = np.array([[h,k,l] for h in indices for k in indices for l in indices])
3159    for roll in rolls:
3160        if np.any(roll):
3161            rhoMask = ma.array(rhoMask,mask=(rhoMask-rollMap(rho,roll)<=0.))
3162    indx = np.transpose(rhoMask.nonzero())
3163    peaks = indx/incre
3164    mags = rhoMask[rhoMask.nonzero()]
3165    for i,[ind,peak,mag] in enumerate(zip(indx,peaks,mags)):
3166        rho = rollMap(rho,ind)
3167        rMM = mapHalf-steps
3168        rMP = mapHalf+steps+1
3169        rhoPeak = rho[rMM[0]:rMP[0],rMM[1]:rMP[1],rMM[2]:rMP[2]]
3170        peakInt = np.sum(rhoPeak)*res**3
3171        rX,rY,rZ = np.mgrid[rMM[0]:rMP[0],rMM[1]:rMP[1],rMM[2]:rMP[2]]
3172        x0 = [peakInt,mapHalf[0],mapHalf[1],mapHalf[2],2.0]          #magnitude, position & width(sig)
3173        result = HessianLSQ(peakFunc,x0,Hess=peakHess,
3174            args=(rX,rY,rZ,rhoPeak,res,SGData['SGLaue']),ftol=.01,maxcyc=10)
3175        x1 = result[0]
3176        if not np.any(x1 < 0):
3177            mag = x1[0]
3178            peak = (np.array(x1[1:4])-ind)/incre
3179        peak = fixSpecialPos(peak,SGData,Amat)
3180        rho = rollMap(rho,-ind)
3181    cent = np.ones(3)*.5     
3182    dzeros = np.sqrt(np.sum(np.inner(Amat,peaks)**2,axis=0))
3183    dcent = np.sqrt(np.sum(np.inner(Amat,peaks-cent)**2,axis=0))
3184    if Neg:     #want negative magnitudes for negative peaks
3185        return np.array(peaks),-np.array([mags,]).T,np.array([dzeros,]).T,np.array([dcent,]).T
3186    else:
3187        return np.array(peaks),np.array([mags,]).T,np.array([dzeros,]).T,np.array([dcent,]).T
3188   
3189def sortArray(data,pos,reverse=False):
3190    '''data is a list of items
3191    sort by pos in list; reverse if True
3192    '''
3193    T = []
3194    for i,M in enumerate(data):
3195        try:
3196            T.append((M[pos],i))
3197        except IndexError:
3198            return data
3199    D = dict(zip(T,data))
3200    T.sort()
3201    if reverse:
3202        T.reverse()
3203    X = []
3204    for key in T:
3205        X.append(D[key])
3206    return X
3207
3208def PeaksEquiv(data,Ind):
3209    '''Find the equivalent map peaks for those selected. Works on the
3210    contents of data['Map Peaks'].
3211
3212    :param data: the phase data structure
3213    :param list Ind: list of selected peak indices
3214    :returns: augmented list of peaks including those related by symmetry to the
3215      ones in Ind
3216
3217    '''       
3218    def Duplicate(xyz,peaks,Amat):
3219        if True in [np.allclose(np.inner(Amat,xyz),np.inner(Amat,peak),atol=0.5) for peak in peaks]:
3220            return True
3221        return False
3222                           
3223    generalData = data['General']
3224    cell = generalData['Cell'][1:7]
3225    Amat,Bmat = G2lat.cell2AB(generalData['Cell'][1:7])
3226    A = G2lat.cell2A(cell)
3227    SGData = generalData['SGData']
3228    mapPeaks = data['Map Peaks']
3229    XYZ = np.array([xyz[1:4] for xyz in mapPeaks])
3230    Indx = {}
3231    for ind in Ind:
3232        xyz = np.array(mapPeaks[ind][1:4])
3233        xyzs = np.array([equiv[0] for equiv in G2spc.GenAtom(xyz,SGData,Move=True)])
3234        for jnd,xyz in enumerate(XYZ):       
3235            Indx[jnd] = Duplicate(xyz,xyzs,Amat)
3236    Ind = []
3237    for ind in Indx:
3238        if Indx[ind]:
3239            Ind.append(ind)
3240    return Ind
3241               
3242def PeaksUnique(data,Ind):
3243    '''Finds the symmetry unique set of peaks from those selected. Works on the
3244    contents of data['Map Peaks'].
3245
3246    :param data: the phase data structure
3247    :param list Ind: list of selected peak indices
3248    :returns: the list of symmetry unique peaks from among those given in Ind
3249
3250    '''       
3251#    XYZE = np.array([[equiv[0] for equiv in G2spc.GenAtom(xyz[1:4],SGData,Move=True)] for xyz in mapPeaks]) #keep this!!
3252
3253    def noDuplicate(xyz,peaks,Amat):
3254        if True in [np.allclose(np.inner(Amat,xyz),np.inner(Amat,peak),atol=0.5) for peak in peaks]:
3255            return False
3256        return True
3257                           
3258    generalData = data['General']
3259    cell = generalData['Cell'][1:7]
3260    Amat,Bmat = G2lat.cell2AB(generalData['Cell'][1:7])
3261    A = G2lat.cell2A(cell)
3262    SGData = generalData['SGData']
3263    mapPeaks = data['Map Peaks']
3264    Indx = {}
3265    XYZ = {}
3266    for ind in Ind:
3267        XYZ[ind] = np.array(mapPeaks[ind][1:4])
3268        Indx[ind] = True
3269    for ind in Ind:
3270        if Indx[ind]:
3271            xyz = XYZ[ind]
3272            for jnd in Ind:
3273                if ind != jnd and Indx[jnd]:                       
3274                    Equiv = G2spc.GenAtom(XYZ[jnd],SGData,Move=True)
3275                    xyzs = np.array([equiv[0] for equiv in Equiv])
3276                    Indx[jnd] = noDuplicate(xyz,xyzs,Amat)
3277    Ind = []
3278    for ind in Indx:
3279        if Indx[ind]:
3280            Ind.append(ind)
3281    return Ind
3282   
3283################################################################################
3284##### single peak fitting profile fxn stuff
3285################################################################################
3286
3287def getCWsig(ins,pos):
3288    '''get CW peak profile sigma^2
3289   
3290    :param dict ins: instrument parameters with at least 'U', 'V', & 'W'
3291      as values only
3292    :param float pos: 2-theta of peak
3293    :returns: float getCWsig: peak sigma^2
3294   
3295    '''
3296    tp = tand(pos/2.0)
3297    return ins['U']*tp**2+ins['V']*tp+ins['W']
3298   
3299def getCWsigDeriv(pos):
3300    '''get derivatives of CW peak profile sigma^2 wrt U,V, & W
3301   
3302    :param float pos: 2-theta of peak
3303   
3304    :returns: list getCWsigDeriv: d(sig^2)/dU, d(sig)/dV & d(sig)/dW
3305   
3306    '''
3307    tp = tand(pos/2.0)
3308    return tp**2,tp,1.0
3309   
3310def getCWgam(ins,pos):
3311    '''get CW peak profile gamma
3312   
3313    :param dict ins: instrument parameters with at least 'X' & 'Y'
3314      as values only
3315    :param float pos: 2-theta of peak
3316    :returns: float getCWgam: peak gamma
3317   
3318    '''
3319    return ins['X']/cosd(pos/2.0)+ins['Y']*tand(pos/2.0)
3320   
3321def getCWgamDeriv(pos):
3322    '''get derivatives of CW peak profile gamma wrt X & Y
3323   
3324    :param float pos: 2-theta of peak
3325   
3326    :returns: list getCWgamDeriv: d(gam)/dX & d(gam)/dY
3327   
3328    '''
3329    return 1./cosd(pos/2.0),tand(pos/2.0)
3330   
3331def getTOFsig(ins,dsp):
3332    '''get TOF peak profile sigma^2
3333   
3334    :param dict ins: instrument parameters with at least 'sig-0', 'sig-1' & 'sig-q'
3335      as values only
3336    :param float dsp: d-spacing of peak
3337   
3338    :returns: float getTOFsig: peak sigma^2
3339   
3340    '''
3341    return ins['sig-0']+ins['sig-1']*dsp**2+ins['sig-2']*dsp**4+ins['sig-q']/dsp**2
3342   
3343def getTOFsigDeriv(dsp):
3344    '''get derivatives of TOF peak profile sigma^2 wrt sig-0, sig-1, & sig-q
3345   
3346    :param float dsp: d-spacing of peak
3347   
3348    :returns: list getTOFsigDeriv: d(sig0/d(sig-0), d(sig)/d(sig-1) & d(sig)/d(sig-q)
3349   
3350    '''
3351    return 1.0,dsp**2,dsp**4,1./dsp**2
3352   
3353def getTOFgamma(ins,dsp):
3354    '''get TOF peak profile gamma
3355   
3356    :param dict ins: instrument parameters with at least 'X' & 'Y'
3357      as values only
3358    :param float dsp: d-spacing of peak
3359   
3360    :returns: float getTOFgamma: peak gamma
3361   
3362    '''
3363    return ins['X']*dsp+ins['Y']*dsp**2
3364   
3365def getTOFgammaDeriv(dsp):
3366    '''get derivatives of TOF peak profile gamma wrt X & Y
3367   
3368    :param float dsp: d-spacing of peak
3369   
3370    :returns: list getTOFgammaDeriv: d(gam)/dX & d(gam)/dY
3371   
3372    '''
3373    return dsp,dsp**2
3374   
3375def getTOFbeta(ins,dsp):
3376    '''get TOF peak profile beta
3377   
3378    :param dict ins: instrument parameters with at least 'beat-0', 'beta-1' & 'beta-q'
3379      as values only
3380    :param float dsp: d-spacing of peak
3381   
3382    :returns: float getTOFbeta: peak beat
3383   
3384    '''
3385    return ins['beta-0']+ins['beta-1']/dsp**4+ins['beta-q']/dsp**2
3386   
3387def getTOFbetaDeriv(dsp):
3388    '''get derivatives of TOF peak profile beta wrt beta-0, beta-1, & beat-q
3389   
3390    :param float dsp: d-spacing of peak
3391   
3392    :returns: list getTOFbetaDeriv: d(beta)/d(beat-0), d(beta)/d(beta-1) & d(beta)/d(beta-q)
3393   
3394    '''
3395    return 1.0,1./dsp**4,1./dsp**2
3396   
3397def getTOFalpha(ins,dsp):
3398    '''get TOF peak profile alpha
3399   
3400    :param dict ins: instrument parameters with at least 'alpha'
3401      as values only
3402    :param float dsp: d-spacing of peak
3403   
3404    :returns: flaot getTOFalpha: peak alpha
3405   
3406    '''
3407    return ins['alpha']/dsp
3408   
3409def getTOFalphaDeriv(dsp):
3410    '''get derivatives of TOF peak profile beta wrt alpha
3411   
3412    :param float dsp: d-spacing of peak
3413   
3414    :returns: float getTOFalphaDeriv: d(alp)/d(alpha)
3415   
3416    '''
3417    return 1./dsp
3418   
3419def setPeakparms(Parms,Parms2,pos,mag,ifQ=False,useFit=False):
3420    '''set starting peak parameters for single peak fits from plot selection or auto selection
3421   
3422    :param dict Parms: instrument parameters dictionary
3423    :param dict Parms2: table lookup for TOF profile coefficients
3424    :param float pos: peak position in 2-theta, TOF or Q (ifQ=True)
3425    :param float mag: peak top magnitude from pick
3426    :param bool ifQ: True if pos in Q
3427    :param bool useFit: True if use fitted CW Parms values (not defaults)
3428   
3429    :returns: list XY: peak list entry:
3430        for CW: [pos,0,mag,1,sig,0,gam,0]
3431        for TOF: [pos,0,mag,1,alp,0,bet,0,sig,0,gam,0]
3432        NB: mag refinement set by default, all others off
3433   
3434    '''
3435    ind = 0
3436    if useFit:
3437        ind = 1
3438    ins = {}
3439    if 'C' in Parms['Type'][0]:                            #CW data - TOF later in an elif
3440        for x in ['U','V','W','X','Y']:
3441            ins[x] = Parms[x][ind]
3442        if ifQ:                              #qplot - convert back to 2-theta
3443            pos = 2.0*asind(pos*wave/(4*math.pi))
3444        sig = getCWsig(ins,pos)
3445        gam = getCWgam(ins,pos)           
3446        XY = [pos,0, mag,1, sig,0, gam,0]       #default refine intensity 1st
3447    else:
3448        if ifQ:
3449            dsp = 2.*np.pi/pos
3450            pos = Parms['difC']*dsp
3451        else:
3452            dsp = pos/Parms['difC'][1]
3453        if 'Pdabc' in Parms2:
3454            for x in ['sig-0','sig-1','sig-2','sig-q','X','Y']:
3455                ins[x] = Parms[x][ind]
3456            Pdabc = Parms2['Pdabc'].T
3457            alp = np.interp(dsp,Pdabc[0],Pdabc[1])
3458            bet = np.interp(dsp,Pdabc[0],Pdabc[2])
3459        else:
3460            for x in ['alpha','beta-0','beta-1','beta-q','sig-0','sig-1','sig-2','sig-q','X','Y']:
3461                ins[x] = Parms[x][ind]
3462            alp = getTOFalpha(ins,dsp)
3463            bet = getTOFbeta(ins,dsp)
3464        sig = getTOFsig(ins,dsp)
3465        gam = getTOFgamma(ins,dsp)
3466        XY = [pos,0,mag,1,alp,0,bet,0,sig,0,gam,0]
3467    return XY
3468   
3469################################################################################
3470##### MC/SA stuff
3471################################################################################
3472
3473#scipy/optimize/anneal.py code modified by R. Von Dreele 2013
3474# Original Author: Travis Oliphant 2002
3475# Bug-fixes in 2006 by Tim Leslie
3476
3477
3478import numpy
3479from numpy import asarray, tan, exp, ones, squeeze, sign, \
3480     all, log, sqrt, pi, shape, array, minimum, where
3481from numpy import random
3482
3483#__all__ = ['anneal']
3484
3485_double_min = numpy.finfo(float).min
3486_double_max = numpy.finfo(float).max
3487class base_schedule(object):
3488    def __init__(self):
3489        self.dwell = 20
3490        self.learn_rate = 0.5
3491        self.lower = -10
3492        self.upper = 10
3493        self.Ninit = 50
3494        self.accepted = 0
3495        self.tests = 0
3496        self.feval = 0
3497        self.k = 0
3498        self.T = None
3499
3500    def init(self, **options):
3501        self.__dict__.update(options)
3502        self.lower = asarray(self.lower)
3503        self.lower = where(self.lower == numpy.NINF, -_double_max, self.lower)
3504        self.upper = asarray(self.upper)
3505        self.upper = where(self.upper == numpy.PINF, _double_max, self.upper)
3506        self.k = 0
3507        self.accepted = 0
3508        self.feval = 0
3509        self.tests = 0
3510
3511    def getstart_temp(self, best_state):
3512        """ Find a matching starting temperature and starting parameters vector
3513        i.e. find x0 such that func(x0) = T0.
3514
3515        Parameters
3516        ----------
3517        best_state : _state
3518            A _state object to store the function value and x0 found.
3519
3520        returns
3521        -------
3522        x0 : array
3523            The starting parameters vector.
3524        """
3525
3526        assert(not self.dims is None)
3527        lrange = self.lower
3528        urange = self.upper
3529        fmax = _double_min
3530        fmin = _double_max
3531        for _ in range(self.Ninit):
3532            x0 = random.uniform(size=self.dims)*(urange-lrange) + lrange
3533            fval = self.func(x0, *self.args)
3534            self.feval += 1
3535            if fval > fmax:
3536                fmax = fval
3537            if fval < fmin:
3538                fmin = fval
3539                best_state.cost = fval
3540                best_state.x = array(x0)
3541
3542        self.T0 = (fmax-fmin)*1.5
3543        return best_state.x
3544       
3545    def set_range(self,x0,frac):
3546        delrange = frac*(self.upper-self.lower)
3547        self.upper = x0+delrange
3548        self.lower = x0-delrange
3549
3550    def accept_test(self, dE):
3551        T = self.T
3552        self.tests += 1
3553        if dE < 0:
3554            self.accepted += 1
3555            return 1
3556        p = exp(-dE*1.0/self.boltzmann/T)
3557        if (p > random.uniform(0.0, 1.0)):
3558            self.accepted += 1
3559            return 1
3560        return 0
3561
3562    def update_guess(self, x0):
3563        return np.squeeze(np.random.uniform(0.,1.,size=self.dims))*(self.upper-self.lower)+self.lower
3564
3565    def update_temp(self, x0):
3566        pass
3567
3568
3569#  A schedule due to Lester Ingber modified to use bounds - OK
3570class fast_sa(base_schedule):
3571    def init(self, **options):
3572        self.__dict__.update(options)
3573        if self.m is None:
3574            self.m = 1.0
3575        if self.n is None:
3576            self.n = 1.0
3577        self.c = self.m * exp(-self.n * self.quench)
3578
3579    def update_guess(self, x0):
3580        x0 = asarray(x0)
3581        u = squeeze(random.uniform(0.0, 1.0, size=self.dims))
3582        T = self.T
3583        xc = (sign(u-0.5)*T*((1+1.0/T)**abs(2*u-1)-1.0)+1.0)/2.0
3584        xnew = xc*(self.upper - self.lower)+self.lower
3585        return xnew
3586#        y = sign(u-0.5)*T*((1+1.0/T)**abs(2*u-1)-1.0)
3587#        xc = y*(self.upper - self.lower)
3588#        xnew = x0 + xc
3589#        return xnew
3590
3591    def update_temp(self):
3592        self.T = self.T0*exp(-self.c * self.k**(self.quench))
3593        self.k += 1
3594        return
3595
3596class cauchy_sa(base_schedule):     #modified to use bounds - not good
3597    def update_guess(self, x0):
3598        x0 = asarray(x0)
3599        numbers = squeeze(random.uniform(-pi/4, pi/4, size=self.dims))
3600        xc = (1.+(self.learn_rate * self.T * tan(numbers))%1.)
3601        xnew = xc*(self.upper - self.lower)+self.lower
3602        return xnew
3603#        numbers = squeeze(random.uniform(-pi/2, pi/2, size=self.dims))
3604#        xc = self.learn_rate * self.T * tan(numbers)
3605#        xnew = x0 + xc
3606#        return xnew
3607
3608    def update_temp(self):
3609        self.T = self.T0/(1+self.k)
3610        self.k += 1
3611        return
3612
3613class boltzmann_sa(base_schedule):
3614#    def update_guess(self, x0):
3615#        std = minimum(sqrt(self.T)*ones(self.dims), (self.upper-self.lower)/3.0/self.learn_rate)
3616#        x0 = asarray(x0)
3617#        xc = squeeze(random.normal(0, 1.0, size=self.dims))
3618#
3619#        xnew = x0 + xc*std*self.learn_rate
3620#        return xnew
3621
3622    def update_temp(self):
3623        self.k += 1
3624        self.T = self.T0 / log(self.k+1.0)
3625        return
3626
3627class log_sa(base_schedule):        #OK
3628
3629    def init(self,**options):
3630        self.__dict__.update(options)
3631       
3632    def update_guess(self,x0):     #same as default
3633        return np.squeeze(np.random.uniform(0.,1.,size=self.dims))*(self.upper-self.lower)+self.lower
3634       
3635    def update_temp(self):
3636        self.k += 1
3637        self.T = self.T0*self.slope**self.k
3638       
3639class _state(object):
3640    def __init__(self):
3641        self.x = None
3642        self.cost = None
3643
3644# TODO:
3645#     allow for general annealing temperature profile
3646#     in that case use update given by alpha and omega and
3647#     variation of all previous updates and temperature?
3648
3649# Simulated annealing
3650
3651def anneal(func, x0, args=(), schedule='fast', full_output=0,
3652           T0=None, Tf=1e-12, maxeval=None, maxaccept=None, maxiter=400,
3653           boltzmann=1.0, learn_rate=0.5, feps=1e-6, quench=1.0, m=1.0, n=1.0,
3654           lower=-100, upper=100, dwell=50, slope=0.9,ranStart=False,
3655           ranRange=0.10,autoRan=False,dlg=None):
3656    """Minimize a function using simulated annealing.
3657
3658    Schedule is a schedule class implementing the annealing schedule.
3659    Available ones are 'fast', 'cauchy', 'boltzmann'
3660
3661    :param callable func: f(x, \*args)
3662        Function to be optimized.
3663    :param ndarray x0:
3664        Initial guess.
3665    :param tuple args:
3666        Extra parameters to `func`.
3667    :param base_schedule schedule:
3668        Annealing schedule to use (a class).
3669    :param bool full_output:
3670        Whether to return optional outputs.
3671    :param float T0:
3672        Initial Temperature (estimated as 1.2 times the largest
3673        cost-function deviation over random points in the range).
3674    :param float Tf:
3675        Final goal temperature.
3676    :param int maxeval:
3677        Maximum function evaluations.
3678    :param int maxaccept:
3679        Maximum changes to accept.
3680    :param int maxiter:
3681        Maximum cooling iterations.
3682    :param float learn_rate:
3683        Scale constant for adjusting guesses.
3684    :param float boltzmann:
3685        Boltzmann constant in acceptance test
3686        (increase for less stringent test at each temperature).
3687    :param float feps:
3688        Stopping relative error tolerance for the function value in
3689        last four coolings.
3690    :param float quench,m,n:
3691        Parameters to alter fast_sa schedule.
3692    :param float/ndarray lower,upper:
3693        Lower and upper bounds on `x`.
3694    :param int dwell:
3695        The number of times to search the space at each temperature.
3696    :param float slope:
3697        Parameter for log schedule
3698    :param bool ranStart=False:
3699        True for set 10% of ranges about x
3700
3701    :returns: (xmin, Jmin, T, feval, iters, accept, retval) where
3702
3703     * xmin (ndarray): Point giving smallest value found.
3704     * Jmin (float): Minimum value of function found.
3705     * T (float): Final temperature.
3706     * feval (int): Number of function evaluations.
3707     * iters (int): Number of cooling iterations.
3708     * accept (int): Number of tests accepted.
3709     * retval (int): Flag indicating stopping condition:
3710
3711              *  0: Points no longer changing
3712              *  1: Cooled to final temperature
3713              *  2: Maximum function evaluations
3714              *  3: Maximum cooling iterations reached
3715              *  4: Maximum accepted query locations reached
3716              *  5: Final point not the minimum amongst encountered points
3717
3718    *Notes*:
3719    Simulated annealing is a random algorithm which uses no derivative
3720    information from the function being optimized. In practice it has
3721    been more useful in discrete optimization than continuous
3722    optimization, as there are usually better algorithms for continuous
3723    optimization problems.
3724
3725    Some experimentation by trying the difference temperature
3726    schedules and altering their parameters is likely required to
3727    obtain good performance.
3728
3729    The randomness in the algorithm comes from random sampling in numpy.
3730    To obtain the same results you can call numpy.random.seed with the
3731    same seed immediately before calling scipy.optimize.anneal.
3732
3733    We give a brief description of how the three temperature schedules
3734    generate new points and vary their temperature. Temperatures are
3735    only updated with iterations in the outer loop. The inner loop is
3736    over xrange(dwell), and new points are generated for
3737    every iteration in the inner loop. (Though whether the proposed
3738    new points are accepted is probabilistic.)
3739
3740    For readability, let d denote the dimension of the inputs to func.
3741    Also, let x_old denote the previous state, and k denote the
3742    iteration number of the outer loop. All other variables not
3743    defined below are input variables to scipy.optimize.anneal itself.
3744
3745    In the 'fast' schedule the updates are ::
3746
3747        u ~ Uniform(0, 1, size=d)
3748        y = sgn(u - 0.5) * T * ((1+ 1/T)**abs(2u-1) -1.0)
3749        xc = y * (upper - lower)
3750        x_new = x_old + xc
3751
3752        c = n * exp(-n * quench)
3753        T_new = T0 * exp(-c * k**quench)
3754
3755
3756    In the 'cauchy' schedule the updates are ::
3757
3758        u ~ Uniform(-pi/2, pi/2, size=d)
3759        xc = learn_rate * T * tan(u)
3760        x_new = x_old + xc
3761
3762        T_new = T0 / (1+k)
3763
3764    In the 'boltzmann' schedule the updates are ::
3765
3766        std = minimum( sqrt(T) * ones(d), (upper-lower) / (3*learn_rate) )
3767        y ~ Normal(0, std, size=d)
3768        x_new = x_old + learn_rate * y
3769
3770        T_new = T0 / log(1+k)
3771
3772    """
3773    x0 = asarray(x0)
3774    lower = asarray(lower)
3775    upper = asarray(upper)
3776
3777    schedule = eval(schedule+'_sa()')
3778    #   initialize the schedule
3779    schedule.init(dims=shape(x0),func=func,args=args,boltzmann=boltzmann,T0=T0,
3780                  learn_rate=learn_rate, lower=lower, upper=upper,
3781                  m=m, n=n, quench=quench, dwell=dwell, slope=slope)
3782
3783    current_state, last_state, best_state = _state(), _state(), _state()
3784    if ranStart:
3785        schedule.set_range(x0,ranRange)
3786    if T0 is None:
3787        x0 = schedule.getstart_temp(best_state)
3788    else:
3789        x0 = random.uniform(size=len(x0))*(upper-lower) + lower
3790        best_state.x = None
3791        best_state.cost = numpy.Inf
3792
3793    last_state.x = asarray(x0).copy()
3794    fval = func(x0,*args)
3795    schedule.feval += 1
3796    last_state.cost = fval
3797    if last_state.cost < best_state.cost:
3798        best_state.cost = fval
3799        best_state.x = asarray(x0).copy()
3800    schedule.T = schedule.T0
3801    fqueue = [100, 300, 500, 700]
3802    iters = 1
3803    keepGoing = True
3804    bestn = 0
3805    while keepGoing:
3806        retval = 0
3807        for n in xrange(dwell):
3808            current_state.x = schedule.update_guess(last_state.x)
3809            current_state.cost = func(current_state.x,*args)
3810            schedule.feval += 1
3811
3812            dE = current_state.cost - last_state.cost
3813            if schedule.accept_test(dE):
3814                last_state.x = current_state.x.copy()
3815                last_state.cost = current_state.cost
3816                if last_state.cost < best_state.cost:
3817                    best_state.x = last_state.x.copy()
3818                    best_state.cost = last_state.cost
3819                    bestn = n
3820                    if best_state.cost < 1.0 and autoRan:
3821                        schedule.set_range(x0,best_state.cost/2.)                       
3822        if dlg:
3823            GoOn = dlg.Update(min(100.,best_state.cost*100),
3824                newmsg='%s%8.5f, %s%d\n%s%8.4f%s'%('Temperature =',schedule.T, \
3825                    'Best trial:',bestn,  \
3826                    'MC/SA Residual:',best_state.cost*100,'%', \
3827                    ))[0]
3828            if not GoOn:
3829                best_state.x = last_state.x.copy()
3830                best_state.cost = last_state.cost
3831                retval = 5
3832        schedule.update_temp()
3833        iters += 1
3834        # Stopping conditions
3835        # 0) last saved values of f from each cooling step
3836        #     are all very similar (effectively cooled)
3837        # 1) Tf is set and we are below it
3838        # 2) maxeval is set and we are past it
3839        # 3) maxiter is set and we are past it
3840        # 4) maxaccept is set and we are past it
3841        # 5) user canceled run via progress bar
3842
3843        fqueue.append(squeeze(last_state.cost))
3844        fqueue.pop(0)
3845        af = asarray(fqueue)*1.0
3846        if retval == 5:
3847            print ' User terminated run; incomplete MC/SA'
3848            keepGoing = False
3849            break
3850        if all(abs((af-af[0])/af[0]) < feps):
3851            retval = 0
3852            if abs(af[-1]-best_state.cost) > feps*10:
3853                retval = 5
3854#                print "Warning: Cooled to %f at %s but this is not" \
3855#                      % (squeeze(last_state.cost), str(squeeze(last_state.x))) \
3856#                      + " the smallest point found."
3857            break
3858        if (Tf is not None) and (schedule.T < Tf):
3859            retval = 1
3860            break
3861        if (maxeval is not None) and (schedule.feval > maxeval):
3862            retval = 2
3863            break
3864        if (iters > maxiter):
3865            print "Warning: Maximum number of iterations exceeded."
3866            retval = 3
3867            break
3868        if (maxaccept is not None) and (schedule.accepted > maxaccept):
3869            retval = 4
3870            break
3871
3872    if full_output:
3873        return best_state.x, best_state.cost, schedule.T, \
3874               schedule.feval, iters, schedule.accepted, retval
3875    else:
3876        return best_state.x, retval
3877
3878def worker(iCyc,data,RBdata,reflType,reflData,covData,out_q,nprocess=-1):
3879    outlist = []
3880    random.seed(int(time.time())%100000+nprocess)   #make sure each process has a different random start
3881    for n in range(iCyc):
3882        result = mcsaSearch(data,RBdata,reflType,reflData,covData,None)
3883        outlist.append(result[0])
3884        print ' MC/SA residual: %.3f%% structure factor time: %.3f'%(100*result[0][2],result[1])
3885    out_q.put(outlist)
3886
3887def MPmcsaSearch(nCyc,data,RBdata,reflType,reflData,covData):
3888    import multiprocessing as mp
3889   
3890    nprocs = mp.cpu_count()
3891    out_q = mp.Queue()
3892    procs = []
3893    iCyc = np.zeros(nprocs)
3894    for i in range(nCyc):
3895        iCyc[i%nprocs] += 1
3896    for i in range(nprocs):
3897        p = mp.Process(target=worker,args=(int(iCyc[i]),data,RBdata,reflType,reflData,covData,out_q,i))
3898        procs.append(p)
3899        p.start()
3900    resultlist = []
3901    for i in range(nprocs):
3902        resultlist += out_q.get()
3903    for p in procs:
3904        p.join()
3905    return resultlist
3906
3907def mcsaSearch(data,RBdata,reflType,reflData,covData,pgbar):
3908    '''default doc string
3909   
3910    :param type name: description
3911   
3912    :returns: type name: description
3913    '''
3914   
3915    global tsum
3916    tsum = 0.
3917   
3918    def getMDparms(item,pfx,parmDict,varyList):
3919        parmDict[pfx+'MDaxis'] = item['axis']
3920        parmDict[pfx+'MDval'] = item['Coef'][0]
3921        if item['Coef'][1]:
3922            varyList += [pfx+'MDval',]
3923            limits = item['Coef'][2]
3924            lower.append(limits[0])
3925            upper.append(limits[1])
3926                       
3927    def getAtomparms(item,pfx,aTypes,SGData,parmDict,varyList):
3928        parmDict[pfx+'Atype'] = item['atType']
3929        aTypes |= set([item['atType'],]) 
3930        pstr = ['Ax','Ay','Az']
3931        XYZ = [0,0,0]
3932        for i in range(3):
3933            name = pfx+pstr[i]
3934            parmDict[name] = item['Pos'][0][i]
3935            XYZ[i] = parmDict[name]
3936            if item['Pos'][1][i]:
3937                varyList += [name,]
3938                limits = item['Pos'][2][i]
3939                lower.append(limits[0])
3940                upper.append(limits[1])
3941        parmDict[pfx+'Amul'] = len(G2spc.GenAtom(XYZ,SGData))
3942           
3943    def getRBparms(item,mfx,aTypes,RBdata,SGData,atNo,parmDict,varyList):
3944        parmDict[mfx+'MolCent'] = item['MolCent']
3945        parmDict[mfx+'RBId'] = item['RBId']
3946        pstr = ['Px','Py','Pz']
3947        ostr = ['Qa','Qi','Qj','Qk']    #angle,vector not quaternion
3948        for i in range(3):
3949            name = mfx+pstr[i]
3950            parmDict[name] = item['Pos'][0][i]
3951            if item['Pos'][1][i]:
3952                varyList += [name,]
3953                limits = item['Pos'][2][i]
3954                lower.append(limits[0])
3955                upper.append(limits[1])
3956        AV = item['Ori'][0]
3957        A = AV[0]
3958        V = AV[1:]
3959        for i in range(4):
3960            name = mfx+ostr[i]
3961            if i:
3962                parmDict[name] = V[i-1]
3963            else:
3964                parmDict[name] = A
3965            if item['Ovar'] == 'AV':
3966                varyList += [name,]
3967                limits = item['Ori'][2][i]
3968                lower.append(limits[0])
3969                upper.append(limits[1])
3970            elif item['Ovar'] == 'A' and not i:
3971                varyList += [name,]
3972                limits = item['Ori'][2][i]
3973                lower.append(limits[0])
3974                upper.append(limits[1])
3975        if 'Tor' in item:      #'Tor' not there for 'Vector' RBs
3976            for i in range(len(item['Tor'][0])):
3977                name = mfx+'Tor'+str(i)
3978                parmDict[name] = item['Tor'][0][i]
3979                if item['Tor'][1][i]:
3980                    varyList += [name,]
3981                    limits = item['Tor'][2][i]
3982                    lower.append(limits[0])
3983                    upper.append(limits[1])
3984        atypes = RBdata[item['Type']][item['RBId']]['rbTypes']
3985        aTypes |= set(atypes)
3986        atNo += len(atypes)
3987        return atNo
3988               
3989    def GetAtomM(Xdata,SGData):
3990        Mdata = []
3991        for xyz in Xdata:
3992            Mdata.append(float(len(G2spc.GenAtom(xyz,SGData))))
3993        return np.array(Mdata)
3994       
3995    def GetAtomT(RBdata,parmDict):
3996        'Needs a doc string'
3997        atNo = parmDict['atNo']
3998        nfixAt = parmDict['nfixAt']
3999        Tdata = atNo*[' ',]
4000        for iatm in range(nfixAt):
4001            parm = ':'+str(iatm)+':Atype'
4002            if parm in parmDict:
4003                Tdata[iatm] = aTypes.index(parmDict[parm])
4004        iatm = nfixAt
4005        for iObj in range(parmDict['nObj']):
4006            pfx = str(iObj)+':'
4007            if parmDict[pfx+'Type'] in ['Vector','Residue']:
4008                if parmDict[pfx+'Type'] == 'Vector':
4009                    RBRes = RBdata['Vector'][parmDict[pfx+'RBId']]
4010                    nAtm = len(RBRes['rbVect'][0])
4011                else:       #Residue
4012                    RBRes = RBdata['Residue'][parmDict[pfx+'RBId']]
4013                    nAtm = len(RBRes['rbXYZ'])
4014                for i in range(nAtm):
4015                    Tdata[iatm] = aTypes.index(RBRes['rbTypes'][i])
4016                    iatm += 1
4017            elif parmDict[pfx+'Type'] == 'Atom':
4018                atNo = parmDict[pfx+'atNo']
4019                parm = pfx+'Atype'              #remove extra ':'
4020                if parm in parmDict:
4021                    Tdata[atNo] = aTypes.index(parmDict[parm])
4022                iatm += 1
4023            else:
4024                continue        #skips March Dollase
4025        return Tdata
4026       
4027    def GetAtomX(RBdata,parmDict):
4028        'Needs a doc string'
4029        Bmat = parmDict['Bmat']
4030        atNo = parmDict['atNo']
4031        nfixAt = parmDict['nfixAt']
4032        Xdata = np.zeros((3,atNo))
4033        keys = {':Ax':Xdata[0],':Ay':Xdata[1],':Az':Xdata[2]}
4034        for iatm in range(nfixAt):
4035            for key in keys:
4036                parm = ':'+str(iatm)+key
4037                if parm in parmDict:
4038                    keys[key][iatm] = parmDict[parm]
4039        iatm = nfixAt
4040        for iObj in range(parmDict['nObj']):
4041            pfx = str(iObj)+':'
4042            if parmDict[pfx+'Type'] in ['Vector','Residue']:
4043                if parmDict[pfx+'Type'] == 'Vector':
4044                    RBRes = RBdata['Vector'][parmDict[pfx+'RBId']]
4045                    vecs = RBRes['rbVect']
4046                    mags = RBRes['VectMag']
4047                    Cart = np.zeros_like(vecs[0])
4048                    for vec,mag in zip(vecs,mags):
4049                        Cart += vec*mag
4050                elif parmDict[pfx+'Type'] == 'Residue':
4051                    RBRes = RBdata['Residue'][parmDict[pfx+'RBId']]
4052                    Cart = np.array(RBRes['rbXYZ'])
4053                    for itor,seq in enumerate(RBRes['rbSeq']):
4054                        QuatA = AVdeg2Q(parmDict[pfx+'Tor'+str(itor)],Cart[seq[0]]-Cart[seq[1]])
4055                        Cart[seq[3]] = prodQVQ(QuatA,Cart[seq[3]]-Cart[seq[1]])+Cart[seq[1]]
4056                if parmDict[pfx+'MolCent'][1]:
4057                    Cart -= parmDict[pfx+'MolCent'][0]
4058                Qori = AVdeg2Q(parmDict[pfx+'Qa'],[parmDict[pfx+'Qi'],parmDict[pfx+'Qj'],parmDict[pfx+'Qk']])
4059                Pos = np.array([parmDict[pfx+'Px'],parmDict[pfx+'Py'],parmDict[pfx+'Pz']])
4060                Xdata.T[iatm:iatm+len(Cart)] = np.inner(Bmat,prodQVQ(Qori,Cart)).T+Pos
4061                iatm += len(Cart)
4062            elif parmDict[pfx+'Type'] == 'Atom':
4063                atNo = parmDict[pfx+'atNo']
4064                for key in keys:
4065                    parm = pfx+key[1:]              #remove extra ':'
4066                    if parm in parmDict:
4067                        keys[key][atNo] = parmDict[parm]
4068                iatm += 1
4069            else:
4070                continue        #skips March Dollase
4071        return Xdata.T
4072       
4073    def getAllTX(Tdata,Mdata,Xdata,SGM,SGT):
4074        allX = np.inner(Xdata,SGM)+SGT
4075        allT = np.repeat(Tdata,allX.shape[1])
4076        allM = np.repeat(Mdata,allX.shape[1])
4077        allX = np.reshape(allX,(-1,3))
4078        return allT,allM,allX
4079
4080    def getAllX(Xdata,SGM,SGT):
4081        allX = np.inner(Xdata,SGM)+SGT
4082        allX = np.reshape(allX,(-1,3))
4083        return allX
4084       
4085    def normQuaternions(RBdata,parmDict,varyList,values):
4086        for iObj in range(parmDict['nObj']):
4087            pfx = str(iObj)+':'
4088            if parmDict[pfx+'Type'] in ['Vector','Residue']:
4089                Qori = AVdeg2Q(parmDict[pfx+'Qa'],[parmDict[pfx+'Qi'],parmDict[pfx+'Qj'],parmDict[pfx+'Qk']])
4090                A,V = Q2AVdeg(Qori)
4091                for i,name in enumerate(['Qa','Qi','Qj','Qk']):
4092                    if i:
4093                        parmDict[pfx+name] = V[i-1]
4094                    else:
4095                        parmDict[pfx+name] = A
4096       
4097    def mcsaCalc(values,refList,rcov,cosTable,ifInv,allFF,RBdata,varyList,parmDict):
4098        ''' Compute structure factors for all h,k,l for phase
4099        input:
4100            refList: [ref] where each ref = h,k,l,m,d,...
4101            rcov:   array[nref,nref] covariance terms between Fo^2 values
4102            ifInv:  bool True if centrosymmetric
4103            allFF: array[nref,natoms] each value is mult*FF(H)/max(mult)
4104            RBdata: [dict] rigid body dictionary
4105            varyList: [list] names of varied parameters in MC/SA (not used here)           
4106            ParmDict: [dict] problem parameters
4107        puts result F^2 in each ref[5] in refList
4108        returns:
4109            delt-F*rcov*delt-F/sum(Fo^2)^2
4110        '''       
4111        global tsum
4112        t0 = time.time()
4113        parmDict.update(dict(zip(varyList,values)))             #update parameter tables
4114        Xdata = GetAtomX(RBdata,parmDict)                       #get new atom coords from RB
4115        allX = getAllX(Xdata,SGM,SGT)                           #fill unit cell - dups. OK
4116        MDval = parmDict['0:MDval']                             #get March-Dollase coeff
4117        HX2pi = 2.*np.pi*np.inner(allX,refList[:3].T)           #form 2piHX for every H,X pair
4118        Aterm = refList[3]*np.sum(allFF*np.cos(HX2pi),axis=0)**2    #compute real part for all H
4119        refList[5] = Aterm
4120        if not ifInv:
4121            refList[5] += refList[3]*np.sum(allFF*np.sin(HX2pi),axis=0)**2    #imaginary part for all H
4122        if len(cosTable):        #apply MD correction
4123            refList[5] *= np.sum(np.sqrt((MDval/(cosTable*(MDval**3-1.)+1.))**3),axis=1)/cosTable.shape[1]
4124        sumFcsq = np.sum(refList[5])
4125        scale = parmDict['sumFosq']/sumFcsq
4126        refList[5] *= scale
4127        refList[6] = refList[4]-refList[5]
4128        M = np.inner(refList[6],np.inner(rcov,refList[6]))
4129        tsum += (time.time()-t0)
4130        return M/np.sum(refList[4]**2)
4131
4132    sq8ln2 = np.sqrt(8*np.log(2))
4133    sq2pi = np.sqrt(2*np.pi)
4134    sq4pi = np.sqrt(4*np.pi)
4135    generalData = data['General']
4136    Amat,Bmat = G2lat.cell2AB(generalData['Cell'][1:7])
4137    Gmat,gmat = G2lat.cell2Gmat(generalData['Cell'][1:7])
4138    SGData = generalData['SGData']
4139    SGM = np.array([SGData['SGOps'][i][0] for i in range(len(SGData['SGOps']))])
4140    SGMT = np.array([SGData['SGOps'][i][0].T for i in range(len(SGData['SGOps']))])
4141    SGT = np.array([SGData['SGOps'][i][1] for i in range(len(SGData['SGOps']))])
4142    fixAtoms = data['Atoms']                       #if any
4143    cx,ct,cs = generalData['AtomPtrs'][:3]
4144    aTypes = set([])
4145    parmDict = {'Bmat':Bmat,'Gmat':Gmat}
4146    varyList = []
4147    atNo = 0
4148    for atm in fixAtoms:
4149        pfx = ':'+str(atNo)+':'
4150        parmDict[pfx+'Atype'] = atm[ct]
4151        aTypes |= set([atm[ct],])
4152        pstr = ['Ax','Ay','Az']
4153        parmDict[pfx+'Amul'] = atm[cs+1]
4154        for i in range(3):
4155            name = pfx+pstr[i]
4156            parmDict[name] = atm[cx+i]
4157        atNo += 1
4158    parmDict['nfixAt'] = len(fixAtoms)       
4159    MCSA = generalData['MCSA controls']
4160    reflName = MCSA['Data source']
4161    phaseName = generalData['Name']
4162    MCSAObjs = data['MCSA']['Models']               #list of MCSA models
4163    upper = []
4164    lower = []
4165    MDvec = np.zeros(3)
4166    for i,item in enumerate(MCSAObjs):
4167        mfx = str(i)+':'
4168        parmDict[mfx+'Type'] = item['Type']
4169        if item['Type'] == 'MD':
4170            getMDparms(item,mfx,parmDict,varyList)
4171            MDvec = np.array(item['axis'])
4172        elif item['Type'] == 'Atom':
4173            getAtomparms(item,mfx,aTypes,SGData,parmDict,varyList)
4174            parmDict[mfx+'atNo'] = atNo
4175            atNo += 1
4176        elif item['Type'] in ['Residue','Vector']:
4177            atNo = getRBparms(item,mfx,aTypes,RBdata,SGData,atNo,parmDict,varyList)
4178    parmDict['atNo'] = atNo                 #total no. of atoms
4179    parmDict['nObj'] = len(MCSAObjs)
4180    aTypes = list(aTypes)
4181    Tdata = GetAtomT(RBdata,parmDict)
4182    Xdata = GetAtomX(RBdata,parmDict)
4183    Mdata = GetAtomM(Xdata,SGData)
4184    allT,allM = getAllTX(Tdata,Mdata,Xdata,SGM,SGT)[:2]
4185    FFtables = G2el.GetFFtable(aTypes)
4186    refs = []
4187    allFF = []
4188    cosTable = []
4189    sumFosq = 0
4190    if 'PWDR' in reflName:
4191        for ref in reflData:
4192            h,k,l,m,d,pos,sig,gam,f = ref[:9]
4193            if d >= MCSA['dmin']:
4194                sig = np.sqrt(sig)      #var -> sig in centideg
4195                sig = G2pwd.getgamFW(gam,sig)/sq8ln2        #FWHM -> sig in centideg
4196                SQ = 0.25/d**2
4197                allFF.append(allM*[G2el.getFFvalues(FFtables,SQ,True)[i] for i in allT]/np.max(allM))
4198                refs.append([h,k,l,m,f*m,pos,sig])
4199                sumFosq += f*m
4200                Heqv = np.inner(np.array([h,k,l]),SGMT)
4201                cosTable.append(G2lat.CosAngle(Heqv,MDvec,Gmat))
4202        nRef = len(refs)
4203        cosTable = np.array(cosTable)**2
4204        rcov = np.zeros((nRef,nRef))
4205        for iref,refI in enumerate(refs):
4206            rcov[iref][iref] = 1./(sq4pi*refI[6])
4207            for jref,refJ in enumerate(refs[:iref]):
4208                t1 = refI[6]**2+refJ[6]**2
4209                t2 = (refJ[5]-refI[5])**2/(2.*t1)
4210                if t2 > 10.:
4211                    rcov[iref][jref] = 0.
4212                else:
4213                    rcov[iref][jref] = 1./(sq2pi*np.sqrt(t1)*np.exp(t2))
4214        rcov += (rcov.T-np.diagflat(np.diagonal(rcov)))
4215        Rdiag = np.sqrt(np.diag(rcov))
4216        Rnorm = np.outer(Rdiag,Rdiag)
4217        rcov /= Rnorm
4218    elif 'Pawley' in reflName:  #need a bail out if Pawley cov matrix doesn't exist.
4219        vNames = []
4220        pfx = str(data['pId'])+'::PWLref:'
4221        for iref,refI in enumerate(reflData):           #Pawley reflection set
4222            h,k,l,m,d,v,f,s = refI
4223            if d >= MCSA['dmin'] and v:       #skip unrefined ones
4224                vNames.append(pfx+str(iref))
4225                SQ = 0.25/d**2
4226                allFF.append(allM*[G2el.getFFvalues(FFtables,SQ,True)[i] for i in allT]/np.max(allM))
4227                refs.append([h,k,l,m,f*m,iref,0.])
4228                sumFosq += f*m
4229                Heqv = np.inner(np.array([h,k,l]),SGMT)
4230                cosTable.append(G2lat.CosAngle(Heqv,MDvec,Gmat))
4231        cosTable = np.array(cosTable)**2
4232        nRef = len(refs)
4233#        if generalData['doPawley'] and (covData['freshCOV'] or  MCSA['newDmin']):
4234        if covData['freshCOV'] or  MCSA['newDmin']:
4235            vList = covData['varyList']
4236            covMatrix = covData['covMatrix']
4237            rcov = getVCov(vNames,vList,covMatrix)
4238            rcov += (rcov.T-np.diagflat(np.diagonal(rcov)))
4239            Rdiag = np.sqrt(np.diag(rcov))
4240            Rdiag = np.where(Rdiag,Rdiag,1.0)
4241            Rnorm = np.outer(Rdiag,Rdiag)
4242            rcov /= Rnorm
4243            MCSA['rcov'] = rcov
4244            covData['freshCOV'] = False
4245            MCSA['newDmin'] = False
4246        else:
4247            rcov = MCSA['rcov']
4248    elif 'HKLF' in reflName:
4249        for ref in reflData:
4250            [h,k,l,m,d],f = ref[:5],ref[6]
4251            if d >= MCSA['dmin']:
4252                SQ = 0.25/d**2
4253                allFF.append(allM*[G2el.getFFvalues(FFtables,SQ,True)[i] for i in allT]/np.max(allM))
4254                refs.append([h,k,l,m,f*m,0.,0.])
4255                sumFosq += f*m
4256        nRef = len(refs)
4257        rcov = np.identity(len(refs))
4258    allFF = np.array(allFF).T
4259    refs = np.array(refs).T
4260    print ' Minimum d-spacing used: %.2f No. reflections used: %d'%(MCSA['dmin'],nRef)
4261    print ' Number of parameters varied: %d'%(len(varyList))
4262    parmDict['sumFosq'] = sumFosq
4263    x0 = [parmDict[val] for val in varyList]
4264    ifInv = SGData['SGInv']
4265    # consider replacing anneal with scipy.optimize.basinhopping
4266    results = anneal(mcsaCalc,x0,args=(refs,rcov,cosTable,ifInv,allFF,RBdata,varyList,parmDict),
4267        schedule=MCSA['Algorithm'], full_output=True,
4268        T0=MCSA['Annealing'][0], Tf=MCSA['Annealing'][1],dwell=MCSA['Annealing'][2],
4269        boltzmann=MCSA['boltzmann'], learn_rate=0.5, 
4270        quench=MCSA['fast parms'][0], m=MCSA['fast parms'][1], n=MCSA['fast parms'][2],
4271        lower=lower, upper=upper, slope=MCSA['log slope'],ranStart=MCSA.get('ranStart',False),
4272        ranRange=MCSA.get('ranRange',0.10),autoRan=MCSA.get('autoRan',False),dlg=pgbar)
4273    M = mcsaCalc(results[0],refs,rcov,cosTable,ifInv,allFF,RBdata,varyList,parmDict)
4274#    for ref in refs.T:
4275#        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])
4276#    print np.sqrt((np.sum(refs[6]**2)/np.sum(refs[4]**2)))
4277    Result = [False,False,results[1],results[2],]+list(results[0])
4278    Result.append(varyList)
4279    return Result,tsum
4280
4281       
4282################################################################################
4283##### Quaternion stuff
4284################################################################################
4285
4286def prodQQ(QA,QB):
4287    ''' Grassman quaternion product
4288        QA,QB quaternions; q=r+ai+bj+ck
4289    '''
4290    D = np.zeros(4)
4291    D[0] = QA[0]*QB[0]-QA[1]*QB[1]-QA[2]*QB[2]-QA[3]*QB[3]
4292    D[1] = QA[0]*QB[1]+QA[1]*QB[0]+QA[2]*QB[3]-QA[3]*QB[2]
4293    D[2] = QA[0]*QB[2]-QA[1]*QB[3]+QA[2]*QB[0]+QA[3]*QB[1]
4294    D[3] = QA[0]*QB[3]+QA[1]*QB[2]-QA[2]*QB[1]+QA[3]*QB[0]
4295   
4296#    D[0] = QA[0]*QB[0]-np.dot(QA[1:],QB[1:])
4297#    D[1:] = QA[0]*QB[1:]+QB[0]*QA[1:]+np.cross(QA[1:],QB[1:])
4298   
4299    return D
4300   
4301def normQ(QA):
4302    ''' get length of quaternion & normalize it
4303        q=r+ai+bj+ck
4304    '''
4305    n = np.sqrt(np.sum(np.array(QA)**2))
4306    return QA/n
4307   
4308def invQ(Q):
4309    '''
4310        get inverse of quaternion
4311        q=r+ai+bj+ck; q* = r-ai-bj-ck
4312    '''
4313    return Q*np.array([1,-1,-1,-1])
4314   
4315def prodQVQ(Q,V):
4316    """
4317    compute the quaternion vector rotation qvq-1 = v'
4318    q=r+ai+bj+ck
4319    """
4320    T2 = Q[0]*Q[1]
4321    T3 = Q[0]*Q[2]
4322    T4 = Q[0]*Q[3]
4323    T5 = -Q[1]*Q[1]
4324    T6 = Q[1]*Q[2]
4325    T7 = Q[1]*Q[3]
4326    T8 = -Q[2]*Q[2]
4327    T9 = Q[2]*Q[3]
4328    T10 = -Q[3]*Q[3]
4329    M = np.array([[T8+T10,T6-T4,T3+T7],[T4+T6,T5+T10,T9-T2],[T7-T3,T2+T9,T5+T8]])
4330    VP = 2.*np.inner(V,M)
4331    return VP+V
4332   
4333def Q2Mat(Q):
4334    ''' make rotation matrix from quaternion
4335        q=r+ai+bj+ck
4336    '''
4337    QN = normQ(Q)
4338    aa = QN[0]**2
4339    ab = QN[0]*QN[1]
4340    ac = QN[0]*QN[2]
4341    ad = QN[0]*QN[3]
4342    bb = QN[1]**2
4343    bc = QN[1]*QN[2]
4344    bd = QN[1]*QN[3]
4345    cc = QN[2]**2
4346    cd = QN[2]*QN[3]
4347    dd = QN[3]**2
4348    M = [[aa+bb-cc-dd, 2.*(bc-ad),  2.*(ac+bd)],
4349        [2*(ad+bc),   aa-bb+cc-dd,  2.*(cd-ab)],
4350        [2*(bd-ac),    2.*(ab+cd), aa-bb-cc+dd]]
4351    return np.array(M)
4352   
4353def AV2Q(A,V):
4354    ''' convert angle (radians) & vector to quaternion
4355        q=r+ai+bj+ck
4356    '''
4357    Q = np.zeros(4)
4358    d = nl.norm(np.array(V))
4359    if d:
4360        V /= d
4361        if not A:       #==0.
4362            A = 2.*np.pi
4363        p = A/2.
4364        Q[0] = np.cos(p)
4365        Q[1:4] = V*np.sin(p)
4366    else:
4367        Q[3] = 1.
4368    return Q
4369   
4370def AVdeg2Q(A,V):
4371    ''' convert angle (degrees) & vector to quaternion
4372        q=r+ai+bj+ck
4373    '''
4374    Q = np.zeros(4)
4375    d = nl.norm(np.array(V))
4376    if not A:       #== 0.!
4377        A = 360.
4378    if d:
4379        V /= d
4380        p = A/2.
4381        Q[0] = cosd(p)
4382        Q[1:4] = V*sind(p)
4383    else:
4384        Q[3] = 1.
4385    return Q
4386   
4387def Q2AVdeg(Q):
4388    ''' convert quaternion to angle (degrees 0-360) & normalized vector
4389        q=r+ai+bj+ck
4390    '''
4391    A = 2.*acosd(Q[0])
4392    V = np.array(Q[1:])
4393    V /= sind(A/2.)
4394    return A,V
4395   
4396def Q2AV(Q):
4397    ''' convert quaternion to angle (radians 0-2pi) & normalized vector
4398        q=r+ai+bj+ck
4399    '''
4400    A = 2.*np.arccos(Q[0])
4401    V = np.array(Q[1:])
4402    V /= np.sin(A/2.)
4403    return A,V
4404   
4405def randomQ(r0,r1,r2,r3):
4406    ''' create random quaternion from 4 random numbers in range (-1,1)
4407    '''
4408    sum = 0
4409    Q = np.array(4)
4410    Q[0] = r0
4411    sum += Q[0]**2
4412    Q[1] = np.sqrt(1.-sum)*r1
4413    sum += Q[1]**2
4414    Q[2] = np.sqrt(1.-sum)*r2
4415    sum += Q[2]**2
4416    Q[3] = np.sqrt(1.-sum)*np.where(r3<0.,-1.,1.)
4417    return Q
4418   
4419def randomAVdeg(r0,r1,r2,r3):
4420    ''' create random angle (deg),vector from 4 random number in range (-1,1)
4421    '''
4422    return Q2AVdeg(randomQ(r0,r1,r2,r3))
4423   
4424def makeQuat(A,B,C):
4425    ''' Make quaternion from rotation of A vector to B vector about C axis
4426
4427        :param np.array A,B,C: Cartesian 3-vectors
4428        :returns: quaternion & rotation angle in radians q=r+ai+bj+ck
4429    '''
4430
4431    V1 = np.cross(A,C)
4432    V2 = np.cross(B,C)
4433    if nl.norm(V1)*nl.norm(V2):
4434        V1 /= nl.norm(V1)
4435        V2 /= nl.norm(V2)
4436        V3 = np.cross(V1,V2)
4437    else:
4438        V3 = np.zeros(3)
4439    Q = np.array([0.,0.,0.,1.])
4440    D = 0.
4441    if nl.norm(V3):
4442        V3 /= nl.norm(V3)
4443        D1 = min(1.0,max(-1.0,np.vdot(V1,V2)))
4444        D = np.arccos(D1)/2.0
4445        V1 = C-V3
4446        V2 = C+V3
4447        DM = nl.norm(V1)
4448        DP = nl.norm(V2)
4449        S = np.sin(D)
4450        Q[0] = np.cos(D)
4451        Q[1:] = V3*S
4452        D *= 2.
4453        if DM > DP:
4454            D *= -1.
4455    return Q,D
4456   
4457def annealtests():
4458    from numpy import cos
4459    # minimum expected at ~-0.195
4460    func = lambda x: cos(14.5*x-0.3) + (x+0.2)*x
4461    print anneal(func,1.0,full_output=1,upper=3.0,lower=-3.0,feps=1e-4,maxiter=2000,schedule='cauchy')
4462    print anneal(func,1.0,full_output=1,upper=3.0,lower=-3.0,feps=1e-4,maxiter=2000,schedule='fast')
4463    print anneal(func,1.0,full_output=1,upper=3.0,lower=-3.0,feps=1e-4,maxiter=2000,schedule='boltzmann')
4464
4465    # minimum expected at ~[-0.195, -0.1]
4466    func = lambda x: cos(14.5*x[0]-0.3) + (x[1]+0.2)*x[1] + (x[0]+0.2)*x[0]
4467    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')
4468    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')
4469    print anneal(func,[1.0, 1.0],full_output=1,upper=[3.0, 3.0],lower=[-3.0, -3.0],feps=1e-4,maxiter=2000,schedule='boltzmann')
4470
4471
4472if __name__ == '__main__':
4473    annealtests()
Note: See TracBrowser for help on using the repository browser.