source: trunk/GSASIImath.py @ 2327

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

dist & angles now work for seq results table; esds are fake

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