source: trunk/GSASIIpwd.py @ 4536

Last change on this file since 4536 was 4536, checked in by vondreele, 3 years ago

supply missing default pink derivatives
fix "Bravais lattice" issues in Unit Cells List

  • Property svn:eol-style set to native
  • Property svn:keywords set to Date Author Revision URL Id
File size: 169.2 KB
Line 
1#/usr/bin/env python
2# -*- coding: utf-8 -*-
3'''
4*GSASII powder calculation module*
5==================================
6
7'''
8########### SVN repository information ###################
9# $Date: 2020-08-02 13:16:06 +0000 (Sun, 02 Aug 2020) $
10# $Author: vondreele $
11# $Revision: 4536 $
12# $URL: trunk/GSASIIpwd.py $
13# $Id: GSASIIpwd.py 4536 2020-08-02 13:16:06Z vondreele $
14########### SVN repository information ###################
15from __future__ import division, print_function
16import sys
17import math
18import time
19import os
20import os.path
21import subprocess as subp
22import datetime as dt
23import copy
24
25import numpy as np
26import numpy.linalg as nl
27import numpy.ma as ma
28import random as rand
29import numpy.fft as fft
30import scipy.interpolate as si
31import scipy.stats as st
32import scipy.optimize as so
33import scipy.special as sp
34
35import GSASIIpath
36filversion = "$Revision: 4536 $"
37GSASIIpath.SetVersionNumber("$Revision: 4536 $")
38import GSASIIlattice as G2lat
39import GSASIIspc as G2spc
40import GSASIIElem as G2elem
41import GSASIImath as G2mth
42try:
43    import pypowder as pyd
44except ImportError:
45    print ('pypowder is not available - profile calcs. not allowed')
46try:
47    import pydiffax as pyx
48except ImportError:
49    print ('pydiffax is not available for this platform')
50import GSASIIfiles as G2fil
51
52   
53# trig functions in degrees
54tand = lambda x: math.tan(x*math.pi/180.)
55atand = lambda x: 180.*math.atan(x)/math.pi
56atan2d = lambda y,x: 180.*math.atan2(y,x)/math.pi
57cosd = lambda x: math.cos(x*math.pi/180.)
58acosd = lambda x: 180.*math.acos(x)/math.pi
59rdsq2d = lambda x,p: round(1.0/math.sqrt(x),p)
60#numpy versions
61npsind = lambda x: np.sin(x*np.pi/180.)
62npasind = lambda x: 180.*np.arcsin(x)/math.pi
63npcosd = lambda x: np.cos(x*math.pi/180.)
64npacosd = lambda x: 180.*np.arccos(x)/math.pi
65nptand = lambda x: np.tan(x*math.pi/180.)
66npatand = lambda x: 180.*np.arctan(x)/np.pi
67npatan2d = lambda y,x: 180.*np.arctan2(y,x)/np.pi
68npT2stl = lambda tth, wave: 2.0*npsind(tth/2.0)/wave    #=d*
69npT2q = lambda tth,wave: 2.0*np.pi*npT2stl(tth,wave)    #=2pi*d*
70ateln2 = 8.0*math.log(2.0)
71sateln2 = np.sqrt(ateln2)
72nxs = np.newaxis
73
74################################################################################
75#### Powder utilities
76################################################################################
77
78def PhaseWtSum(G2frame,histo):
79    '''
80    Calculate sum of phase mass*phase fraction for PWDR data (exclude magnetic phases)
81   
82    :param G2frame: GSASII main frame structure
83    :param str histo: histogram name
84    :returns: sum(scale*mass) for phases in histo
85    '''
86    Histograms,Phases = G2frame.GetUsedHistogramsAndPhasesfromTree()
87    wtSum = 0.0
88    for phase in Phases:
89        if Phases[phase]['General']['Type'] != 'magnetic':
90            if histo in Phases[phase]['Histograms']:
91                if not Phases[phase]['Histograms'][histo]['Use']: continue
92                mass = Phases[phase]['General']['Mass']
93                phFr = Phases[phase]['Histograms'][histo]['Scale'][0]
94                wtSum += mass*phFr
95    return wtSum
96   
97################################################################################
98#### GSASII pwdr & pdf calculation routines
99################################################################################
100       
101def Transmission(Geometry,Abs,Diam):
102    '''
103    Calculate sample transmission
104
105    :param str Geometry: one of 'Cylinder','Bragg-Brentano','Tilting flat plate in transmission','Fixed flat plate'
106    :param float Abs: absorption coeff in cm-1
107    :param float Diam: sample thickness/diameter in mm
108    '''
109    if 'Cylinder' in Geometry:      #Lobanov & Alte da Veiga for 2-theta = 0; beam fully illuminates sample
110        MuR = Abs*Diam/20.0
111        if MuR <= 3.0:
112            T0 = 16/(3.*math.pi)
113            T1 = -0.045780
114            T2 = -0.02489
115            T3 = 0.003045
116            T = -T0*MuR-T1*MuR**2-T2*MuR**3-T3*MuR**4
117            if T < -20.:
118                return 2.06e-9
119            else:
120                return math.exp(T)
121        else:
122            T1 = 1.433902
123            T2 = 0.013869+0.337894
124            T3 = 1.933433+1.163198
125            T4 = 0.044365-0.04259
126            T = (T1-T4)/(1.0+T2*(MuR-3.0))**T3+T4
127            return T/100.
128    elif 'plate' in Geometry:
129        MuR = Abs*Diam/10.
130        return math.exp(-MuR)
131    elif 'Bragg' in Geometry:
132        return 0.0
133       
134def SurfaceRough(SRA,SRB,Tth):
135    ''' Suortti (J. Appl. Cryst, 5,325-331, 1972) surface roughness correction
136    :param float SRA: Suortti surface roughness parameter
137    :param float SRB: Suortti surface roughness parameter
138    :param float Tth: 2-theta(deg) - can be numpy array
139   
140    '''
141    sth = npsind(Tth/2.)
142    T1 = np.exp(-SRB/sth)
143    T2 = SRA+(1.-SRA)*np.exp(-SRB)
144    return (SRA+(1.-SRA)*T1)/T2
145   
146def SurfaceRoughDerv(SRA,SRB,Tth):
147    ''' Suortti surface roughness correction derivatives
148    :param float SRA: Suortti surface roughness parameter (dimensionless)
149    :param float SRB: Suortti surface roughness parameter (dimensionless)
150    :param float Tth: 2-theta(deg) - can be numpy array
151    :return list: [dydSRA,dydSRB] derivatives to be used for intensity derivative
152    '''
153    sth = npsind(Tth/2.)
154    T1 = np.exp(-SRB/sth)
155    T2 = SRA+(1.-SRA)*np.exp(-SRB)
156    Trans = (SRA+(1.-SRA)*T1)/T2
157    dydSRA = ((1.-T1)*T2-(1.-np.exp(-SRB))*Trans)/T2**2
158    dydSRB = ((SRA-1.)*T1*T2/sth-Trans*(SRA-T2))/T2**2
159    return [dydSRA,dydSRB]
160
161def Absorb(Geometry,MuR,Tth,Phi=0,Psi=0):
162    '''Calculate sample absorption
163    :param str Geometry: one of 'Cylinder','Bragg-Brentano','Tilting Flat Plate in transmission','Fixed flat plate'
164    :param float MuR: absorption coeff * sample thickness/2 or radius
165    :param Tth: 2-theta scattering angle - can be numpy array
166    :param float Phi: flat plate tilt angle - future
167    :param float Psi: flat plate tilt axis - future
168    '''
169   
170    def muRunder3(MuR,Sth2):
171        T0 = 16.0/(3.*np.pi)
172        T1 = (25.99978-0.01911*Sth2**0.25)*np.exp(-0.024551*Sth2)+ \
173            0.109561*np.sqrt(Sth2)-26.04556
174        T2 = -0.02489-0.39499*Sth2+1.219077*Sth2**1.5- \
175            1.31268*Sth2**2+0.871081*Sth2**2.5-0.2327*Sth2**3
176        T3 = 0.003045+0.018167*Sth2-0.03305*Sth2**2
177        Trns = -T0*MuR-T1*MuR**2-T2*MuR**3-T3*MuR**4
178        return np.exp(Trns)
179   
180    def muRover3(MuR,Sth2):
181        T1 = 1.433902+11.07504*Sth2-8.77629*Sth2*Sth2+ \
182            10.02088*Sth2**3-3.36778*Sth2**4
183        T2 = (0.013869-0.01249*Sth2)*np.exp(3.27094*Sth2)+ \
184            (0.337894+13.77317*Sth2)/(1.0+11.53544*Sth2)**1.555039
185        T3 = 1.933433/(1.0+23.12967*Sth2)**1.686715- \
186            0.13576*np.sqrt(Sth2)+1.163198
187        T4 = 0.044365-0.04259/(1.0+0.41051*Sth2)**148.4202
188        Trns = (T1-T4)/(1.0+T2*(MuR-3.0))**T3+T4
189        return Trns/100.
190       
191    Sth2 = npsind(Tth/2.0)**2
192    if 'Cylinder' in Geometry:      #Lobanov & Alte da Veiga for 2-theta = 0; beam fully illuminates sample
193        if 'array' in str(type(MuR)):
194            MuRSTh2 = np.vstack((MuR,Sth2))
195            AbsCr = np.where(MuRSTh2[0]<=3.0,muRunder3(MuRSTh2[0],MuRSTh2[1]),muRover3(MuRSTh2[0],MuRSTh2[1]))
196            return AbsCr
197        else:
198            if MuR <= 3.0:
199                return muRunder3(MuR,Sth2)
200            else:
201                return muRover3(MuR,Sth2)
202    elif 'Bragg' in Geometry:
203        return 1.0
204    elif 'Fixed' in Geometry: #assumes sample plane is perpendicular to incident beam
205        # and only defined for 2theta < 90
206        MuT = 2.*MuR
207        T1 = np.exp(-MuT)
208        T2 = np.exp(-MuT/npcosd(Tth))
209        Tb = MuT-MuT/npcosd(Tth)
210        return (T2-T1)/Tb
211    elif 'Tilting' in Geometry: #assumes symmetric tilt so sample plane is parallel to diffraction vector
212        MuT = 2.*MuR
213        cth = npcosd(Tth/2.0)
214        return np.exp(-MuT/cth)/cth
215       
216def AbsorbDerv(Geometry,MuR,Tth,Phi=0,Psi=0):
217    'needs a doc string'
218    dA = 0.001
219    AbsP = Absorb(Geometry,MuR+dA,Tth,Phi,Psi)
220    if MuR:
221        AbsM = Absorb(Geometry,MuR-dA,Tth,Phi,Psi)
222        return (AbsP-AbsM)/(2.0*dA)
223    else:
224        return (AbsP-1.)/dA
225       
226def Polarization(Pola,Tth,Azm=0.0):
227    """   Calculate angle dependent x-ray polarization correction (not scaled correctly!)
228
229    :param Pola: polarization coefficient e.g 1.0 fully polarized, 0.5 unpolarized
230    :param Azm: azimuthal angle e.g. 0.0 in plane of polarization
231    :param Tth: 2-theta scattering angle - can be numpy array
232      which (if either) of these is "right"?
233    :return: (pola, dpdPola)
234      * pola = ((1-Pola)*npcosd(Azm)**2+Pola*npsind(Azm)**2)*npcosd(Tth)**2+ \
235        (1-Pola)*npsind(Azm)**2+Pola*npcosd(Azm)**2
236      * dpdPola: derivative needed for least squares
237
238    """
239    cazm = npcosd(Azm)
240    sazm = npsind(Azm)
241    pola = ((1.0-Pola)*cazm**2+Pola*npsind(Azm)**2)*npcosd(Tth)**2+   \
242        (1.0-Pola)*sazm**2+Pola*cazm**2
243    dpdPola = -npsind(Tth)**2*(sazm**2-cazm**2)
244    return pola,dpdPola
245   
246def Oblique(ObCoeff,Tth):
247    'currently assumes detector is normal to beam'
248    if ObCoeff:
249        return (1.-ObCoeff)/(1.0-np.exp(np.log(ObCoeff)/npcosd(Tth)))
250    else:
251        return 1.0
252               
253def Ruland(RulCoff,wave,Q,Compton):
254    'needs a doc string'
255    C = 2.9978e8
256    D = 1.5e-3
257    hmc = 0.024262734687
258    sinth2 = (Q*wave/(4.0*np.pi))**2
259    dlam = (wave**2)*Compton*Q/C
260    dlam_c = 2.0*hmc*sinth2-D*wave**2
261    return 1.0/((1.0+dlam/RulCoff)*(1.0+(np.pi*dlam_c/(dlam+RulCoff))**2))
262   
263def LorchWeight(Q):
264    'needs a doc string'
265    return np.sin(np.pi*(Q[-1]-Q)/(2.0*Q[-1]))
266           
267def GetAsfMean(ElList,Sthl2):
268    '''Calculate various scattering factor terms for PDF calcs
269
270    :param dict ElList: element dictionary contains scattering factor coefficients, etc.
271    :param np.array Sthl2: numpy array of sin theta/lambda squared values
272    :returns: mean(f^2), mean(f)^2, mean(compton)
273    '''
274    sumNoAtoms = 0.0
275    FF = np.zeros_like(Sthl2)
276    FF2 = np.zeros_like(Sthl2)
277    CF = np.zeros_like(Sthl2)
278    for El in ElList:
279        sumNoAtoms += ElList[El]['FormulaNo']
280    for El in ElList:
281        el = ElList[El]
282        ff2 = (G2elem.ScatFac(el,Sthl2)+el['fp'])**2+el['fpp']**2
283        cf = G2elem.ComptonFac(el,Sthl2)
284        FF += np.sqrt(ff2)*el['FormulaNo']/sumNoAtoms
285        FF2 += ff2*el['FormulaNo']/sumNoAtoms
286        CF += cf*el['FormulaNo']/sumNoAtoms
287    return FF2,FF**2,CF
288   
289def GetNumDensity(ElList,Vol):
290    'needs a doc string'
291    sumNoAtoms = 0.0
292    for El in ElList:
293        sumNoAtoms += ElList[El]['FormulaNo']
294    return sumNoAtoms/Vol
295           
296def CalcPDF(data,inst,limits,xydata):
297    '''Computes I(Q), S(Q) & G(r) from Sample, Bkg, etc. diffraction patterns loaded into
298    dict xydata; results are placed in xydata.
299    Calculation parameters are found in dicts data and inst and list limits.
300    The return value is at present an empty list.
301    '''
302    auxPlot = []
303    if 'T' in inst['Type'][0]:
304        Ibeg = 0
305        Ifin = len(xydata['Sample'][1][0])
306    else:
307        Ibeg = np.searchsorted(xydata['Sample'][1][0],limits[0])
308        Ifin = np.searchsorted(xydata['Sample'][1][0],limits[1])+1
309    #subtract backgrounds - if any & use PWDR limits
310    IofQ = copy.deepcopy(xydata['Sample'])
311    IofQ[1] = np.array(IofQ[1])[:,Ibeg:Ifin]
312    if data['Sample Bkg.']['Name']:
313        IofQ[1][1] += xydata['Sample Bkg.'][1][1][Ibeg:Ifin]*data['Sample Bkg.']['Mult']
314    if data['Container']['Name']:
315        xycontainer = xydata['Container'][1][1]*data['Container']['Mult']
316        if data['Container Bkg.']['Name']:
317            xycontainer += xydata['Container Bkg.'][1][1][Ibeg:Ifin]*data['Container Bkg.']['Mult']
318        IofQ[1][1] += xycontainer[Ibeg:Ifin]
319    data['IofQmin'] = IofQ[1][1][-1]
320    IofQ[1][1] -= data.get('Flat Bkg',0.)
321    #get element data & absorption coeff.
322    ElList = data['ElList']
323    Tth = IofQ[1][0]    #2-theta or TOF!
324    if 'X' in inst['Type'][0]:
325        Abs = G2lat.CellAbsorption(ElList,data['Form Vol'])
326        #Apply angle dependent corrections
327        MuR = Abs*data['Diam']/20.0
328        IofQ[1][1] /= Absorb(data['Geometry'],MuR,Tth)
329        IofQ[1][1] /= Polarization(inst['Polariz.'][1],Tth,Azm=inst['Azimuth'][1])[0]
330        if data['DetType'] == 'Image plate':
331            IofQ[1][1] *= Oblique(data['ObliqCoeff'],Tth)
332    elif 'T' in inst['Type'][0]:    #neutron TOF normalized data - needs wavelength dependent absorption
333        wave = 2.*G2lat.TOF2dsp(inst,IofQ[1][0])*npsind(inst['2-theta'][1]/2.)
334        Els = ElList.keys()
335        Isotope = {El:'Nat. abund.' for El in Els}
336        GD = {'AtomTypes':ElList,'Isotope':Isotope}
337        BLtables = G2elem.GetBLtable(GD)
338        FP,FPP = G2elem.BlenResTOF(Els,BLtables,wave)
339        Abs = np.zeros(len(wave))
340        for iel,El in enumerate(Els):
341            BL = BLtables[El][1]
342            SA = BL['SA']*wave/1.798197+4.0*np.pi*FPP[iel]**2 #+BL['SL'][1]?
343            SA *= ElList[El]['FormulaNo']/data['Form Vol']
344            Abs += SA
345        MuR = Abs*data['Diam']/2.
346        IofQ[1][1] /= Absorb(data['Geometry'],MuR,inst['2-theta'][1]*np.ones(len(wave)))       
347    XY = IofQ[1]   
348    #convert to Q
349#    nQpoints = len(XY[0])     #points for Q interpolation
350    nQpoints = 5000
351    if 'C' in inst['Type'][0]:
352        wave = G2mth.getWave(inst)
353        minQ = npT2q(Tth[0],wave)
354        maxQ = npT2q(Tth[-1],wave)   
355        Qpoints = np.linspace(0.,maxQ,nQpoints,endpoint=True)
356        dq = Qpoints[1]-Qpoints[0]
357        XY[0] = npT2q(XY[0],wave)
358        Qdata = si.griddata(XY[0],XY[1],Qpoints,method='linear',fill_value=XY[1][0])    #interpolate I(Q)
359    elif 'T' in inst['Type'][0]:
360        difC = inst['difC'][1]
361        minQ = 2.*np.pi*difC/Tth[-1]
362        maxQ = 2.*np.pi*difC/Tth[0]
363        Qpoints = np.linspace(0.,maxQ,nQpoints,endpoint=True)
364        dq = Qpoints[1]-Qpoints[0]
365        XY[0] = 2.*np.pi*difC/XY[0]
366        Qdata = si.griddata(XY[0],XY[1],Qpoints,method='linear',fill_value=XY[1][-1])    #interpolate I(Q)
367    Qdata -= np.min(Qdata)*data['BackRatio']
368   
369    qLimits = data['QScaleLim']
370    maxQ = np.searchsorted(Qpoints,min(Qpoints[-1],qLimits[1]))+1
371    minQ = np.searchsorted(Qpoints,min(qLimits[0],0.90*Qpoints[-1]))
372    qLimits = [Qpoints[minQ],Qpoints[maxQ-1]]
373    newdata = []
374    if len(IofQ) < 3:
375        xydata['IofQ'] = [IofQ[0],[Qpoints,Qdata],'']
376    else:
377        xydata['IofQ'] = [IofQ[0],[Qpoints,Qdata],IofQ[2]]
378    for item in xydata['IofQ'][1]:
379        newdata.append(item[:maxQ])
380    xydata['IofQ'][1] = newdata
381   
382    xydata['SofQ'] = copy.deepcopy(xydata['IofQ'])
383    if 'XC' in inst['Type'][0]:
384        FFSq,SqFF,CF = GetAsfMean(ElList,(xydata['SofQ'][1][0]/(4.0*np.pi))**2)  #these are <f^2>,<f>^2,Cf
385    else: #TOF
386        CF = np.zeros(len(xydata['SofQ'][1][0]))
387        FFSq = np.ones(len(xydata['SofQ'][1][0]))
388        SqFF = np.ones(len(xydata['SofQ'][1][0]))
389    Q = xydata['SofQ'][1][0]
390#    auxPlot.append([Q,np.copy(CF),'CF-unCorr'])
391    if 'XC' in inst['Type'][0]:
392        ruland = Ruland(data['Ruland'],wave,Q,CF)
393#        auxPlot.append([Q,ruland,'Ruland'])     
394        CF *= ruland
395#    auxPlot.append([Q,CF,'CF-Corr'])
396    scale = np.sum((FFSq+CF)[minQ:maxQ])/np.sum(xydata['SofQ'][1][1][minQ:maxQ])
397    xydata['SofQ'][1][1] *= scale
398    if 'XC' in inst['Type'][0]:
399        xydata['SofQ'][1][1] -= CF
400    xydata['SofQ'][1][1] = xydata['SofQ'][1][1]/SqFF
401    scale = len(xydata['SofQ'][1][1][minQ:maxQ])/np.sum(xydata['SofQ'][1][1][minQ:maxQ])
402    xydata['SofQ'][1][1] *= scale
403    xydata['FofQ'] = copy.deepcopy(xydata['SofQ'])
404    xydata['FofQ'][1][1] = xydata['FofQ'][1][0]*(xydata['SofQ'][1][1]-1.0)
405    if data['Lorch']:
406        xydata['FofQ'][1][1] *= LorchWeight(Q)   
407    xydata['GofR'] = copy.deepcopy(xydata['FofQ'])
408    xydata['gofr'] = copy.deepcopy(xydata['FofQ'])
409    nR = len(xydata['GofR'][1][1])
410    Rmax = GSASIIpath.GetConfigValue('PDF_Rmax',100.)
411    mul = int(round(2.*np.pi*nR/(Rmax*qLimits[1])))
412#    mul = int(round(2.*np.pi*nR/(data.get('Rmax',100.)*qLimits[1])))
413    R = 2.*np.pi*np.linspace(0,nR,nR,endpoint=True)/(mul*qLimits[1])
414    xydata['GofR'][1][0] = R
415    xydata['gofr'][1][0] = R
416    GR = -dq*np.imag(fft.fft(xydata['FofQ'][1][1],mul*nR)[:nR])
417    xydata['GofR'][1][1] = GR
418    gr = GR/(np.pi*R)
419    xydata['gofr'][1][1] = gr
420    numbDen = 0.
421    if 'ElList' in data:
422        numbDen = GetNumDensity(data['ElList'],data['Form Vol'])
423    if data.get('noRing',True):
424        Rmin = data['Rmin']
425        xydata['gofr'][1][1] = np.where(R<Rmin,-4.*numbDen,xydata['gofr'][1][1])
426        xydata['GofR'][1][1] = np.where(R<Rmin,-4.*R*np.pi*numbDen,xydata['GofR'][1][1])
427    return auxPlot
428   
429def PDFPeakFit(peaks,data):
430    rs2pi = 1./np.sqrt(2*np.pi)
431   
432    def MakeParms(peaks):
433        varyList = []
434        parmDict = {'slope':peaks['Background'][1][1]}
435        if peaks['Background'][2]:
436            varyList.append('slope')
437        for i,peak in enumerate(peaks['Peaks']):
438            parmDict['PDFpos;'+str(i)] = peak[0]
439            parmDict['PDFmag;'+str(i)] = peak[1]
440            parmDict['PDFsig;'+str(i)] = peak[2]
441            if 'P' in peak[3]:
442                varyList.append('PDFpos;'+str(i))
443            if 'M' in peak[3]:
444                varyList.append('PDFmag;'+str(i))
445            if 'S' in peak[3]:
446                varyList.append('PDFsig;'+str(i))
447        return parmDict,varyList
448       
449    def SetParms(peaks,parmDict,varyList):
450        if 'slope' in varyList:
451            peaks['Background'][1][1] = parmDict['slope']
452        for i,peak in enumerate(peaks['Peaks']):
453            if 'PDFpos;'+str(i) in varyList:
454                peak[0] = parmDict['PDFpos;'+str(i)]
455            if 'PDFmag;'+str(i) in varyList:
456                peak[1] = parmDict['PDFmag;'+str(i)]
457            if 'PDFsig;'+str(i) in varyList:
458                peak[2] = parmDict['PDFsig;'+str(i)]
459       
460   
461    def CalcPDFpeaks(parmdict,Xdata):
462        Z = parmDict['slope']*Xdata
463        ipeak = 0
464        while True:
465            try:
466                pos = parmdict['PDFpos;'+str(ipeak)]
467                mag = parmdict['PDFmag;'+str(ipeak)]
468                wid = parmdict['PDFsig;'+str(ipeak)]
469                wid2 = 2.*wid**2
470                Z += mag*rs2pi*np.exp(-(Xdata-pos)**2/wid2)/wid
471                ipeak += 1
472            except KeyError:        #no more peaks to process
473                return Z
474               
475    def errPDFProfile(values,xdata,ydata,parmdict,varylist):       
476        parmdict.update(zip(varylist,values))
477        M = CalcPDFpeaks(parmdict,xdata)-ydata
478        return M
479           
480    newpeaks = copy.copy(peaks)
481    iBeg = np.searchsorted(data[1][0],newpeaks['Limits'][0])
482    iFin = np.searchsorted(data[1][0],newpeaks['Limits'][1])+1
483    X = data[1][0][iBeg:iFin]
484    Y = data[1][1][iBeg:iFin]
485    parmDict,varyList = MakeParms(peaks)
486    if not len(varyList):
487        G2fil.G2Print (' Nothing varied')
488        return newpeaks,None,None,None,None,None
489   
490    Rvals = {}
491    values =  np.array(Dict2Values(parmDict, varyList))
492    result = so.leastsq(errPDFProfile,values,full_output=True,ftol=0.0001,
493           args=(X,Y,parmDict,varyList))
494    chisq = np.sum(result[2]['fvec']**2)
495    Values2Dict(parmDict, varyList, result[0])
496    SetParms(peaks,parmDict,varyList)
497    Rvals['Rwp'] = np.sqrt(chisq/np.sum(Y**2))*100.      #to %
498    chisq = np.sum(result[2]['fvec']**2)/(len(X)-len(values))   #reduced chi^2 = M/(Nobs-Nvar)
499    sigList = list(np.sqrt(chisq*np.diag(result[1])))   
500    Z = CalcPDFpeaks(parmDict,X)
501    newpeaks['calc'] = [X,Z]
502    return newpeaks,result[0],varyList,sigList,parmDict,Rvals   
503   
504def MakeRDF(RDFcontrols,background,inst,pwddata):
505    import scipy.signal as signal
506    auxPlot = []
507    if 'C' in inst['Type'][0]:
508        Tth = pwddata[0]
509        wave = G2mth.getWave(inst)
510        minQ = npT2q(Tth[0],wave)
511        maxQ = npT2q(Tth[-1],wave)
512        powQ = npT2q(Tth,wave) 
513    elif 'T' in inst['Type'][0]:
514        TOF = pwddata[0]
515        difC = inst['difC'][1]
516        minQ = 2.*np.pi*difC/TOF[-1]
517        maxQ = 2.*np.pi*difC/TOF[0]
518        powQ = 2.*np.pi*difC/TOF
519    piDQ = np.pi/(maxQ-minQ)
520    Qpoints = np.linspace(minQ,maxQ,len(pwddata[0]),endpoint=True)
521    if RDFcontrols['UseObsCalc'] == 'obs-calc':
522        Qdata = si.griddata(powQ,pwddata[1]-pwddata[3],Qpoints,method=RDFcontrols['Smooth'],fill_value=0.)
523    elif RDFcontrols['UseObsCalc'] == 'obs-back':
524        Qdata = si.griddata(powQ,pwddata[1]-pwddata[4],Qpoints,method=RDFcontrols['Smooth'],fill_value=pwddata[1][0])
525    elif RDFcontrols['UseObsCalc'] == 'calc-back':
526        Qdata = si.griddata(powQ,pwddata[3]-pwddata[4],Qpoints,method=RDFcontrols['Smooth'],fill_value=pwddata[1][0])
527    Qdata *= np.sin((Qpoints-minQ)*piDQ)/piDQ
528    Qdata *= 0.5*np.sqrt(Qpoints)       #Qbin normalization
529#    GSASIIpath.IPyBreak()
530    dq = Qpoints[1]-Qpoints[0]
531    nR = len(Qdata)
532    R = 0.5*np.pi*np.linspace(0,nR,nR)/(4.*maxQ)
533    iFin = np.searchsorted(R,RDFcontrols['maxR'])+1
534    bBut,aBut = signal.butter(4,0.01)
535    Qsmooth = signal.filtfilt(bBut,aBut,Qdata)
536#    auxPlot.append([Qpoints,Qdata,'interpolate:'+RDFcontrols['Smooth']])
537#    auxPlot.append([Qpoints,Qsmooth,'interpolate:'+RDFcontrols['Smooth']])
538    DofR = dq*np.imag(fft.fft(Qsmooth,16*nR)[:nR])
539#    DofR = dq*np.imag(ft.fft(Qsmooth,16*nR)[:nR])
540    auxPlot.append([R[:iFin],DofR[:iFin],'D(R) for '+RDFcontrols['UseObsCalc']])   
541    return auxPlot
542
543# PDF optimization =============================================================
544def OptimizePDF(data,xydata,limits,inst,showFit=True,maxCycles=5):
545    import scipy.optimize as opt
546    numbDen = GetNumDensity(data['ElList'],data['Form Vol'])
547    Min,Init,Done = SetupPDFEval(data,xydata,limits,inst,numbDen)
548    xstart = Init()
549    bakMul = data['Sample Bkg.']['Mult']
550    if showFit:
551        rms = Min(xstart)
552        G2fil.G2Print('  Optimizing corrections to improve G(r) at low r')
553        if data['Sample Bkg.'].get('Refine',False):
554#            data['Flat Bkg'] = 0.
555            G2fil.G2Print('  start: Ruland={:.3f}, Sample Bkg mult={:.3f} (RMS:{:.4f})'.format(
556                data['Ruland'],data['Sample Bkg.']['Mult'],rms))
557        else:
558            G2fil.G2Print('  start: Flat Bkg={:.1f}, BackRatio={:.3f}, Ruland={:.3f} (RMS:{:.4f})'.format(
559                data['Flat Bkg'],data['BackRatio'],data['Ruland'],rms))
560    if data['Sample Bkg.'].get('Refine',False):
561        res = opt.minimize(Min,xstart,bounds=([0.01,1],[1.2*bakMul,0.8*bakMul]),
562                    method='L-BFGS-B',options={'maxiter':maxCycles},tol=0.001)
563    else:
564        res = opt.minimize(Min,xstart,bounds=([0,None],[0,1],[0.01,1]),
565                    method='L-BFGS-B',options={'maxiter':maxCycles},tol=0.001)
566    Done(res['x'])
567    if showFit:
568        if res['success']:
569            msg = 'Converged'
570        else:
571            msg = 'Not Converged'
572        if data['Sample Bkg.'].get('Refine',False):
573            G2fil.G2Print('  end:   Ruland={:.3f}, Sample Bkg mult={:.3f} (RMS:{:.4f}) *** {} ***\n'.format(
574                data['Ruland'],data['Sample Bkg.']['Mult'],res['fun'],msg))
575        else:
576            G2fil.G2Print('  end:   Flat Bkg={:.1f}, BackRatio={:.3f}, Ruland={:.3f}) *** {} ***\n'.format(
577                data['Flat Bkg'],data['BackRatio'],data['Ruland'],res['fun'],msg))
578    return res
579
580def SetupPDFEval(data,xydata,limits,inst,numbDen):
581    Data = copy.deepcopy(data)
582    BkgMax = 1.
583    def EvalLowPDF(arg):
584        '''Objective routine -- evaluates the RMS deviations in G(r)
585        from -4(pi)*#density*r for for r<Rmin
586        arguments are ['Flat Bkg','BackRatio','Ruland'] scaled so that
587        the min & max values are between 0 and 1.
588        '''
589        if Data['Sample Bkg.'].get('Refine',False):
590            R,S = arg
591            Data['Sample Bkg.']['Mult'] = S
592        else:
593            F,B,R = arg
594            Data['Flat Bkg'] = F*BkgMax
595            Data['BackRatio'] = B
596        Data['Ruland'] = R/10.
597        CalcPDF(Data,inst,limits,xydata)
598        # test low r computation
599        g = xydata['GofR'][1][1]
600        r = xydata['GofR'][1][0]
601        g0 = g[r < Data['Rmin']] + 4*np.pi*r[r < Data['Rmin']]*numbDen
602        M = sum(g0**2)/len(g0)
603        return M
604    def GetCurrentVals():
605        '''Get the current ['Flat Bkg','BackRatio','Ruland'] with scaling
606        '''
607        if data['Sample Bkg.'].get('Refine',False):
608                return [max(10*data['Ruland'],.05),data['Sample']['Mult']]
609        try:
610            F = data['Flat Bkg']/BkgMax
611        except:
612            F = 0
613        return [F,data['BackRatio'],max(10*data['Ruland'],.05)]
614    def SetFinalVals(arg):
615        '''Set the 'Flat Bkg', 'BackRatio' & 'Ruland' values from the
616        scaled, refined values and plot corrected region of G(r)
617        '''
618        if data['Sample Bkg.'].get('Refine',False):
619            R,S = arg
620            data['Sample Bkg.']['Mult'] = S
621        else:
622            F,B,R = arg
623            data['Flat Bkg'] = F*BkgMax
624            data['BackRatio'] = B
625        data['Ruland'] = R/10.
626        CalcPDF(data,inst,limits,xydata)
627    EvalLowPDF(GetCurrentVals())
628    BkgMax = max(xydata['IofQ'][1][1])/50.
629    return EvalLowPDF,GetCurrentVals,SetFinalVals
630
631################################################################################       
632#### GSASII peak fitting routines: Finger, Cox & Jephcoat model       
633################################################################################
634
635def factorize(num):
636    ''' Provide prime number factors for integer num
637    :returns: dictionary of prime factors (keys) & power for each (data)
638    '''
639    factors = {}
640    orig = num
641
642    # we take advantage of the fact that (i +1)**2 = i**2 + 2*i +1
643    i, sqi = 2, 4
644    while sqi <= num:
645        while not num%i:
646            num /= i
647            factors[i] = factors.get(i, 0) + 1
648
649        sqi += 2*i + 1
650        i += 1
651
652    if num != 1 and num != orig:
653        factors[num] = factors.get(num, 0) + 1
654
655    if factors:
656        return factors
657    else:
658        return {num:1}          #a prime number!
659           
660def makeFFTsizeList(nmin=1,nmax=1023,thresh=15):
661    ''' Provide list of optimal data sizes for FFT calculations
662
663    :param int nmin: minimum data size >= 1
664    :param int nmax: maximum data size > nmin
665    :param int thresh: maximum prime factor allowed
666    :Returns: list of data sizes where the maximum prime factor is < thresh
667    ''' 
668    plist = []
669    nmin = max(1,nmin)
670    nmax = max(nmin+1,nmax)
671    for p in range(nmin,nmax):
672        if max(list(factorize(p).keys())) < thresh:
673            plist.append(p)
674    return plist
675
676np.seterr(divide='ignore')
677
678# Normal distribution
679
680# loc = mu, scale = std
681_norm_pdf_C = 1./math.sqrt(2*math.pi)
682class norm_gen(st.rv_continuous):
683    'needs a doc string'
684     
685    def pdf(self,x,*args,**kwds):
686        loc,scale=kwds['loc'],kwds['scale']
687        x = (x-loc)/scale
688        return np.exp(-x**2/2.0) * _norm_pdf_C / scale
689       
690norm = norm_gen(name='norm',longname='A normal',extradoc="""
691
692Normal distribution
693
694The location (loc) keyword specifies the mean.
695The scale (scale) keyword specifies the standard deviation.
696
697normal.pdf(x) = exp(-x**2/2)/sqrt(2*pi)
698""")
699
700## Cauchy
701
702# median = loc
703
704class cauchy_gen(st.rv_continuous):
705    'needs a doc string'
706
707    def pdf(self,x,*args,**kwds):
708        loc,scale=kwds['loc'],kwds['scale']
709        x = (x-loc)/scale
710        return 1.0/np.pi/(1.0+x*x) / scale
711       
712cauchy = cauchy_gen(name='cauchy',longname='Cauchy',extradoc="""
713
714Cauchy distribution
715
716cauchy.pdf(x) = 1/(pi*(1+x**2))
717
718This is the t distribution with one degree of freedom.
719""")
720   
721   
722#GSASII peak fitting routine: Finger, Cox & Jephcoat model       
723
724
725class fcjde_gen(st.rv_continuous):
726    """
727    Finger-Cox-Jephcoat D(2phi,2th) function for S/L = H/L
728    Ref: J. Appl. Cryst. (1994) 27, 892-900.
729
730    :param x: array -1 to 1
731    :param t: 2-theta position of peak
732    :param s: sum(S/L,H/L); S: sample height, H: detector opening,
733      L: sample to detector opening distance
734    :param dx: 2-theta step size in deg
735
736    :returns: for fcj.pdf
737
738     * T = x*dx+t
739     * s = S/L+H/L
740     * if x < 0::
741
742        fcj.pdf = [1/sqrt({cos(T)**2/cos(t)**2}-1) - 1/s]/|cos(T)|
743
744     * if x >= 0: fcj.pdf = 0   
745    """
746    def _pdf(self,x,t,s,dx):
747        T = dx*x+t
748        ax2 = abs(npcosd(T))
749        ax = ax2**2
750        bx = npcosd(t)**2
751        bx = np.where(ax>bx,bx,ax)
752        fx = np.where(ax>bx,(np.sqrt(bx/(ax-bx))-1./s)/ax2,0.0)
753        fx = np.where(fx > 0.,fx,0.0)
754        return fx
755             
756    def pdf(self,x,*args,**kwds):
757        loc=kwds['loc']
758        return self._pdf(x-loc,*args)
759       
760fcjde = fcjde_gen(name='fcjde',shapes='t,s,dx')
761               
762def getWidthsCW(pos,sig,gam,shl):
763    '''Compute the peak widths used for computing the range of a peak
764    for constant wavelength data. On low-angle side, 50 FWHM are used,
765    on high-angle side 75 are used, low angle side extended for axial divergence
766    (for peaks above 90 deg, these are reversed.)
767    '''
768    widths = [np.sqrt(sig)/100.,gam/100.]
769    fwhm = 2.355*widths[0]+widths[1]
770    fmin = 50.*(fwhm+shl*abs(npcosd(pos)))
771    fmax = 75.0*fwhm
772    if pos > 90:
773        fmin,fmax = [fmax,fmin]         
774    return widths,fmin,fmax
775   
776def getWidthsTOF(pos,alp,bet,sig,gam):
777    '''Compute the peak widths used for computing the range of a peak
778    for constant wavelength data. 50 FWHM are used on both sides each
779    extended by exponential coeff.
780    '''
781    widths = [np.sqrt(sig),gam]
782    fwhm = 2.355*widths[0]+2.*widths[1]
783    fmin = 50.*fwhm*(1.+1./alp)   
784    fmax = 50.*fwhm*(1.+1./bet)
785    return widths,fmin,fmax
786   
787def getFWHM(pos,Inst):
788    '''Compute total FWHM from Thompson, Cox & Hastings (1987) , J. Appl. Cryst. 20, 79-83
789    via getgamFW(g,s).
790   
791    :param pos: float peak position in deg 2-theta or tof in musec
792    :param Inst: dict instrument parameters
793   
794    :returns float: total FWHM of pseudoVoigt in deg or musec
795    ''' 
796   
797    sig = lambda Th,U,V,W: np.sqrt(max(0.001,U*tand(Th)**2+V*tand(Th)+W))
798    sigTOF = lambda dsp,S0,S1,S2,Sq: np.sqrt(S0+S1*dsp**2+S2*dsp**4+Sq*dsp)
799    gam = lambda Th,X,Y,Z: Z+X/cosd(Th)+Y*tand(Th)
800    gamTOF = lambda dsp,X,Y,Z: Z+X*dsp+Y*dsp**2
801    alpTOF = lambda dsp,alp: alp/dsp
802    betTOF = lambda dsp,bet0,bet1,betq: bet0+bet1/dsp**4+betq/dsp**2
803    alpPink = lambda pos,alp0,alp1: alp0+alp1*tand(pos/2.)
804    betPink = lambda pos,bet0,bet1: bet0+bet1*tand(pos/2.)
805    if 'T' in Inst['Type'][0]:
806        dsp = pos/Inst['difC'][1]
807        alp = alpTOF(dsp,Inst['alpha'][1])
808        bet = betTOF(dsp,Inst['beta-0'][1],Inst['beta-1'][1],Inst['beta-q'][1])
809        s = sigTOF(dsp,Inst['sig-0'][1],Inst['sig-1'][1],Inst['sig-2'][1],Inst['sig-q'][1])
810        g = gamTOF(dsp,Inst['X'][1],Inst['Y'][1],Inst['Z'][1])
811        return getgamFW(g,s)+np.log(2.0)*(alp+bet)/(alp*bet)
812    elif 'C' in Inst['Type'][0]:
813        s = sig(pos/2.,Inst['U'][1],Inst['V'][1],Inst['W'][1])
814        g = gam(pos/2.,Inst['X'][1],Inst['Y'][1],Inst['Z'][1])
815        return getgamFW(g,s)/100.  #returns FWHM in deg
816    else:   #'B'
817        alp = alpPink(pos,Inst['alpha-0'][1],Inst['alpha-1'][1])
818        bet = betPink(pos,Inst['beta-0'][1],Inst['beta-1'][1])
819        s = sig(pos/2.,Inst['U'][1],Inst['V'][1],Inst['W'][1])
820        g = gam(pos/2.,Inst['X'][1],Inst['Y'][1],Inst['Z'][1])
821        return getgamFW(g,s)/100.+np.log(2.0)*(alp+bet)/(alp*bet)  #returns FWHM in deg
822   
823def getgamFW(g,s):
824    '''Compute total FWHM from Thompson, Cox & Hastings (1987), J. Appl. Cryst. 20, 79-83
825    lambda fxn needs FWHM for both Gaussian & Lorentzian components
826   
827    :param g: float Lorentzian gamma = FWHM(L)
828    :param s: float Gaussian sig
829   
830    :returns float: total FWHM of pseudoVoigt
831    ''' 
832    gamFW = lambda s,g: np.exp(np.log(s**5+2.69269*s**4*g+2.42843*s**3*g**2+4.47163*s**2*g**3+0.07842*s*g**4+g**5)/5.)
833    return gamFW(2.35482*s,g)   #sqrt(8ln2)*sig = FWHM(G)
834               
835def getFCJVoigt(pos,intens,sig,gam,shl,xdata):   
836    '''Compute the Finger-Cox-Jepcoat modified Voigt function for a
837    CW powder peak by direct convolution. This version is not used.
838    '''
839    DX = xdata[1]-xdata[0]
840    widths,fmin,fmax = getWidthsCW(pos,sig,gam,shl)
841    x = np.linspace(pos-fmin,pos+fmin,256)
842    dx = x[1]-x[0]
843    Norm = norm.pdf(x,loc=pos,scale=widths[0])
844    Cauchy = cauchy.pdf(x,loc=pos,scale=widths[1])
845    arg = [pos,shl/57.2958,dx,]
846    FCJ = fcjde.pdf(x,*arg,loc=pos)
847    if len(np.nonzero(FCJ)[0])>5:
848        z = np.column_stack([Norm,Cauchy,FCJ]).T
849        Z = fft.fft(z)
850        Df = fft.ifft(Z.prod(axis=0)).real
851    else:
852        z = np.column_stack([Norm,Cauchy]).T
853        Z = fft.fft(z)
854        Df = fft.fftshift(fft.ifft(Z.prod(axis=0))).real
855    Df /= np.sum(Df)
856    Df = si.interp1d(x,Df,bounds_error=False,fill_value=0.0)
857    return intens*Df(xdata)*DX/dx
858
859def getBackground(pfx,parmDict,bakType,dataType,xdata,fixedBkg={}):
860    '''Computes the background from vars pulled from gpx file or tree.
861    '''
862    if 'T' in dataType:
863        q = 2.*np.pi*parmDict[pfx+'difC']/xdata
864    else:
865        wave = parmDict.get(pfx+'Lam',parmDict.get(pfx+'Lam1',1.0))
866        q = npT2q(xdata,wave)
867    yb = np.zeros_like(xdata)
868    nBak = 0
869    cw = np.diff(xdata)
870    cw = np.append(cw,cw[-1])
871    sumBk = [0.,0.,0]
872    while True:
873        key = pfx+'Back;'+str(nBak)
874        if key in parmDict:
875            nBak += 1
876        else:
877            break
878#empirical functions
879    if bakType in ['chebyschev','cosine','chebyschev-1']:
880        dt = xdata[-1]-xdata[0]   
881        for iBak in range(nBak):
882            key = pfx+'Back;'+str(iBak)
883            if bakType == 'chebyschev':
884                ybi = parmDict[key]*(-1.+2.*(xdata-xdata[0])/dt)**iBak
885            elif bakType == 'chebyschev-1':
886                xpos = -1.+2.*(xdata-xdata[0])/dt
887                ybi = parmDict[key]*np.cos(iBak*np.arccos(xpos))
888            elif bakType == 'cosine':
889                ybi = parmDict[key]*npcosd(180.*xdata*iBak/xdata[-1])
890            yb += ybi
891        sumBk[0] = np.sum(yb)
892    elif bakType in ['Q^2 power series','Q^-2 power series']:
893        QT = 1.
894        yb += np.ones_like(yb)*parmDict[pfx+'Back;0']
895        for iBak in range(nBak-1):
896            key = pfx+'Back;'+str(iBak+1)
897            if '-2' in bakType:
898                QT *= (iBak+1)*q**-2
899            else:
900                QT *= q**2/(iBak+1)
901            yb += QT*parmDict[key]
902        sumBk[0] = np.sum(yb)
903    elif bakType in ['lin interpolate','inv interpolate','log interpolate',]:
904        if nBak == 1:
905            yb = np.ones_like(xdata)*parmDict[pfx+'Back;0']
906        elif nBak == 2:
907            dX = xdata[-1]-xdata[0]
908            T2 = (xdata-xdata[0])/dX
909            T1 = 1.0-T2
910            yb = parmDict[pfx+'Back;0']*T1+parmDict[pfx+'Back;1']*T2
911        else:
912            xnomask = ma.getdata(xdata)
913            xmin,xmax = xnomask[0],xnomask[-1]
914            if bakType == 'lin interpolate':
915                bakPos = np.linspace(xmin,xmax,nBak,True)
916            elif bakType == 'inv interpolate':
917                bakPos = 1./np.linspace(1./xmax,1./xmin,nBak,True)
918            elif bakType == 'log interpolate':
919                bakPos = np.exp(np.linspace(np.log(xmin),np.log(xmax),nBak,True))
920            bakPos[0] = xmin
921            bakPos[-1] = xmax
922            bakVals = np.zeros(nBak)
923            for i in range(nBak):
924                bakVals[i] = parmDict[pfx+'Back;'+str(i)]
925            bakInt = si.interp1d(bakPos,bakVals,'linear')
926            yb = bakInt(ma.getdata(xdata))
927        sumBk[0] = np.sum(yb)
928#Debye function       
929    if pfx+'difC' in parmDict:
930        ff = 1.
931    else:       
932        try:
933            wave = parmDict[pfx+'Lam']
934        except KeyError:
935            wave = parmDict[pfx+'Lam1']
936        SQ = (q/(4.*np.pi))**2
937        FF = G2elem.GetFormFactorCoeff('Si')[0]
938        ff = np.array(G2elem.ScatFac(FF,SQ)[0])**2
939    iD = 0       
940    while True:
941        try:
942            dbA = parmDict[pfx+'DebyeA;'+str(iD)]
943            dbR = parmDict[pfx+'DebyeR;'+str(iD)]
944            dbU = parmDict[pfx+'DebyeU;'+str(iD)]
945            ybi = ff*dbA*np.sin(q*dbR)*np.exp(-dbU*q**2)/(q*dbR)
946            yb += ybi
947            sumBk[1] += np.sum(ybi)
948            iD += 1       
949        except KeyError:
950            break
951#peaks
952    iD = 0
953    while True:
954        try:
955            pkP = parmDict[pfx+'BkPkpos;'+str(iD)]
956            pkI = max(parmDict[pfx+'BkPkint;'+str(iD)],0.1)
957            pkS = max(parmDict[pfx+'BkPksig;'+str(iD)],1.)
958            pkG = max(parmDict[pfx+'BkPkgam;'+str(iD)],0.1)
959            if 'C' in dataType:
960                Wd,fmin,fmax = getWidthsCW(pkP,pkS,pkG,.002)
961            else: #'T'OF
962                Wd,fmin,fmax = getWidthsTOF(pkP,1.,1.,pkS,pkG)
963            iBeg = np.searchsorted(xdata,pkP-fmin)
964            iFin = np.searchsorted(xdata,pkP+fmax)
965            lenX = len(xdata)
966            if not iBeg:
967                iFin = np.searchsorted(xdata,pkP+fmax)
968            elif iBeg == lenX:
969                iFin = iBeg
970            else:
971                iFin = np.searchsorted(xdata,pkP+fmax)
972            if 'C' in dataType:
973                ybi = pkI*getFCJVoigt3(pkP,pkS,pkG,0.002,xdata[iBeg:iFin])
974                yb[iBeg:iFin] += ybi
975            elif 'T' in dataType:
976                ybi = pkI*getEpsVoigt(pkP,1.,1.,pkS,pkG,xdata[iBeg:iFin])
977                yb[iBeg:iFin] += ybi
978            elif 'B' in dataType:
979                ybi = pkI*getEpsVoigt(pkP,1.,1.,pkS/100.,pkG/1.e4,xdata[iBeg:iFin])
980                yb[iBeg:iFin] += ybi
981            sumBk[2] += np.sum(ybi)
982            iD += 1       
983        except KeyError:
984            break
985        except ValueError:
986            G2fil.G2Print ('**** WARNING - backround peak '+str(iD)+' sigma is negative; fix & try again ****')
987            break
988    # fixed background from file
989    if len(fixedBkg) >= 3:
990        mult = fixedBkg.get('_fixedMult',0.0)
991        if len(fixedBkg.get('_fixedValues',[])) != len(yb):
992            G2fil.G2Print('Lengths of backgrounds do not agree: yb={}, fixed={}'.format(
993                len(yb),len(fixedBkg.get('_fixedValues',[]))))
994        elif mult: 
995            yb -= mult*fixedBkg.get('_fixedValues',[]) # N.B. mult is negative
996            sumBk[0] = sum(yb)
997    return yb,sumBk
998   
999def getBackgroundDerv(hfx,parmDict,bakType,dataType,xdata):
1000    'needs a doc string'
1001    if 'T' in dataType:
1002        q = 2.*np.pi*parmDict[hfx+'difC']/xdata
1003    else:
1004        wave = parmDict.get(hfx+'Lam',parmDict.get(hfx+'Lam1',1.0))
1005        q = 2.*np.pi*npsind(xdata/2.)/wave
1006    nBak = 0
1007    while True:
1008        key = hfx+'Back;'+str(nBak)
1009        if key in parmDict:
1010            nBak += 1
1011        else:
1012            break
1013    dydb = np.zeros(shape=(nBak,len(xdata)))
1014    dyddb = np.zeros(shape=(3*parmDict[hfx+'nDebye'],len(xdata)))
1015    dydpk = np.zeros(shape=(4*parmDict[hfx+'nPeaks'],len(xdata)))
1016    cw = np.diff(xdata)
1017    cw = np.append(cw,cw[-1])
1018
1019    if bakType in ['chebyschev','cosine','chebyschev-1']:
1020        dt = xdata[-1]-xdata[0]   
1021        for iBak in range(nBak):   
1022            if bakType == 'chebyschev':
1023                dydb[iBak] = (-1.+2.*(xdata-xdata[0])/dt)**iBak
1024            elif bakType == 'chebyschev-1':
1025                xpos = -1.+2.*(xdata-xdata[0])/dt
1026                dydb[iBak] = np.cos(iBak*np.arccos(xpos))
1027            elif bakType == 'cosine':
1028                dydb[iBak] = npcosd(180.*xdata*iBak/xdata[-1])
1029    elif bakType in ['Q^2 power series','Q^-2 power series']:
1030        QT = 1.
1031        dydb[0] = np.ones_like(xdata)
1032        for iBak in range(nBak-1):
1033            if '-2' in bakType:
1034                QT *= (iBak+1)*q**-2
1035            else:
1036                QT *= q**2/(iBak+1)
1037            dydb[iBak+1] = QT
1038    elif bakType in ['lin interpolate','inv interpolate','log interpolate',]:
1039        if nBak == 1:
1040            dydb[0] = np.ones_like(xdata)
1041        elif nBak == 2:
1042            dX = xdata[-1]-xdata[0]
1043            T2 = (xdata-xdata[0])/dX
1044            T1 = 1.0-T2
1045            dydb = [T1,T2]
1046        else:
1047            xnomask = ma.getdata(xdata)
1048            xmin,xmax = xnomask[0],xnomask[-1]
1049            if bakType == 'lin interpolate':
1050                bakPos = np.linspace(xmin,xmax,nBak,True)
1051            elif bakType == 'inv interpolate':
1052                bakPos = 1./np.linspace(1./xmax,1./xmin,nBak,True)
1053            elif bakType == 'log interpolate':
1054                bakPos = np.exp(np.linspace(np.log(xmin),np.log(xmax),nBak,True))
1055            bakPos[0] = xmin
1056            bakPos[-1] = xmax
1057            for i,pos in enumerate(bakPos):
1058                if i == 0:
1059                    dydb[0] = np.where(xdata<bakPos[1],(bakPos[1]-xdata)/(bakPos[1]-bakPos[0]),0.)
1060                elif i == len(bakPos)-1:
1061                    dydb[i] = np.where(xdata>bakPos[-2],(bakPos[-1]-xdata)/(bakPos[-1]-bakPos[-2]),0.)
1062                else:
1063                    dydb[i] = np.where(xdata>bakPos[i],
1064                        np.where(xdata<bakPos[i+1],(bakPos[i+1]-xdata)/(bakPos[i+1]-bakPos[i]),0.),
1065                        np.where(xdata>bakPos[i-1],(xdata-bakPos[i-1])/(bakPos[i]-bakPos[i-1]),0.))
1066    if hfx+'difC' in parmDict:
1067        ff = 1.
1068    else:
1069        wave = parmDict.get(hfx+'Lam',parmDict.get(hfx+'Lam1',1.0))
1070        q = npT2q(xdata,wave)
1071        SQ = (q/(4*np.pi))**2
1072        FF = G2elem.GetFormFactorCoeff('Si')[0]
1073        ff = np.array(G2elem.ScatFac(FF,SQ)[0])*np.pi**2    #needs pi^2~10. for cw data (why?)
1074    iD = 0       
1075    while True:
1076        try:
1077            if hfx+'difC' in parmDict:
1078                q = 2*np.pi*parmDict[hfx+'difC']/xdata
1079            dbA = parmDict[hfx+'DebyeA;'+str(iD)]
1080            dbR = parmDict[hfx+'DebyeR;'+str(iD)]
1081            dbU = parmDict[hfx+'DebyeU;'+str(iD)]
1082            sqr = np.sin(q*dbR)/(q*dbR)
1083            cqr = np.cos(q*dbR)
1084            temp = np.exp(-dbU*q**2)
1085            dyddb[3*iD] = ff*sqr*temp
1086            dyddb[3*iD+1] = ff*dbA*temp*(cqr-sqr)/(dbR)
1087            dyddb[3*iD+2] = -ff*dbA*sqr*temp*q**2
1088            iD += 1
1089        except KeyError:
1090            break
1091    iD = 0
1092    while True:
1093        try:
1094            pkP = parmDict[hfx+'BkPkpos;'+str(iD)]
1095            pkI = max(parmDict[hfx+'BkPkint;'+str(iD)],0.1)
1096            pkS = max(parmDict[hfx+'BkPksig;'+str(iD)],1.0)
1097            pkG = max(parmDict[hfx+'BkPkgam;'+str(iD)],0.1)
1098            if 'C' in dataType:
1099                Wd,fmin,fmax = getWidthsCW(pkP,pkS,pkG,.002)
1100            else: #'T' or 'B'
1101                Wd,fmin,fmax = getWidthsTOF(pkP,1.,1.,pkS,pkG)
1102            iBeg = np.searchsorted(xdata,pkP-fmin)
1103            iFin = np.searchsorted(xdata,pkP+fmax)
1104            lenX = len(xdata)
1105            if not iBeg:
1106                iFin = np.searchsorted(xdata,pkP+fmax)
1107            elif iBeg == lenX:
1108                iFin = iBeg
1109            else:
1110                iFin = np.searchsorted(xdata,pkP+fmax)
1111            if 'C' in dataType:
1112                Df,dFdp,dFds,dFdg,x = getdFCJVoigt3(pkP,pkS,pkG,.002,xdata[iBeg:iFin])
1113                dydpk[4*iD][iBeg:iFin] += 100.*cw[iBeg:iFin]*pkI*dFdp
1114                dydpk[4*iD+1][iBeg:iFin] += 100.*cw[iBeg:iFin]*Df
1115                dydpk[4*iD+2][iBeg:iFin] += 100.*cw[iBeg:iFin]*pkI*dFds
1116                dydpk[4*iD+3][iBeg:iFin] += 100.*cw[iBeg:iFin]*pkI*dFdg
1117            else:   #'T'OF
1118                Df,dFdp,x,x,dFds,dFdg = getdEpsVoigt(pkP,1.,1.,pkS,pkG,xdata[iBeg:iFin])
1119                dydpk[4*iD][iBeg:iFin] += pkI*dFdp
1120                dydpk[4*iD+1][iBeg:iFin] += Df
1121                dydpk[4*iD+2][iBeg:iFin] += pkI*dFds
1122                dydpk[4*iD+3][iBeg:iFin] += pkI*dFdg
1123            iD += 1       
1124        except KeyError:
1125            break
1126        except ValueError:
1127            G2fil.G2Print ('**** WARNING - backround peak '+str(iD)+' sigma is negative; fix & try again ****')
1128            break       
1129    return dydb,dyddb,dydpk
1130
1131#use old fortran routine
1132def getFCJVoigt3(pos,sig,gam,shl,xdata):
1133    '''Compute the Finger-Cox-Jepcoat modified Pseudo-Voigt function for a
1134    CW powder peak in external Fortran routine
1135    '''
1136    Df = pyd.pypsvfcj(len(xdata),xdata-pos,pos,sig,gam,shl)
1137#    Df = pyd.pypsvfcjo(len(xdata),xdata-pos,pos,sig,gam,shl)
1138    Df /= np.sum(Df)
1139    return Df
1140
1141def getdFCJVoigt3(pos,sig,gam,shl,xdata):
1142    '''Compute analytic derivatives the Finger-Cox-Jepcoat modified Pseudo-Voigt
1143    function for a CW powder peak
1144    '''
1145    Df,dFdp,dFds,dFdg,dFdsh = pyd.pydpsvfcj(len(xdata),xdata-pos,pos,sig,gam,shl)
1146#    Df,dFdp,dFds,dFdg,dFdsh = pyd.pydpsvfcjo(len(xdata),xdata-pos,pos,sig,gam,shl)
1147    return Df,dFdp,dFds,dFdg,dFdsh
1148
1149def getPsVoigt(pos,sig,gam,xdata):
1150    'needs a doc string'
1151   
1152    Df = pyd.pypsvoigt(len(xdata),xdata-pos,sig,gam)
1153    Df /= np.sum(Df)
1154    return Df
1155
1156def getdPsVoigt(pos,sig,gam,xdata):
1157    'needs a doc string'
1158   
1159    Df,dFdp,dFds,dFdg = pyd.pydpsvoigt(len(xdata),xdata-pos,sig,gam)
1160    return Df,dFdp,dFds,dFdg
1161
1162def getEpsVoigt(pos,alp,bet,sig,gam,xdata):
1163    'needs a doc string'
1164    Df = pyd.pyepsvoigt(len(xdata),xdata-pos,alp,bet,sig,gam)
1165    Df /= np.sum(Df)
1166    return Df 
1167   
1168def getdEpsVoigt(pos,alp,bet,sig,gam,xdata):
1169    'needs a doc string'
1170    Df,dFdp,dFda,dFdb,dFds,dFdg = pyd.pydepsvoigt(len(xdata),xdata-pos,alp,bet,sig,gam)
1171    return Df,dFdp,dFda,dFdb,dFds,dFdg   
1172
1173def ellipseSize(H,Sij,GB):
1174    'Implements r=1/sqrt(sum((1/S)*(q.v)^2) per note from Alexander Brady'
1175    HX = np.inner(H.T,GB)
1176    lenHX = np.sqrt(np.sum(HX**2))
1177    Esize,Rsize = nl.eigh(G2lat.U6toUij(Sij))           
1178    R = np.inner(HX/lenHX,Rsize)**2*Esize         #want column length for hkl in crystal
1179    lenR = 1./np.sqrt(np.sum(R))
1180    return lenR
1181
1182def ellipseSizeDerv(H,Sij,GB):
1183    'needs a doc string'
1184    lenR = ellipseSize(H,Sij,GB)
1185    delt = 0.001
1186    dRdS = np.zeros(6)
1187    for i in range(6):
1188        Sij[i] -= delt
1189        lenM = ellipseSize(H,Sij,GB)
1190        Sij[i] += 2.*delt
1191        lenP = ellipseSize(H,Sij,GB)
1192        Sij[i] -= delt
1193        dRdS[i] = (lenP-lenM)/(2.*delt)
1194    return lenR,dRdS
1195
1196def getMustrain(HKL,G,SGData,muStrData):
1197    if muStrData[0] == 'isotropic':
1198        return np.ones(HKL.shape[1])*muStrData[1][0]
1199    elif muStrData[0] == 'uniaxial':
1200        H = np.array(HKL)
1201        P = np.array(muStrData[3])
1202        cosP,sinP = np.array([G2lat.CosSinAngle(h,P,G) for h in H.T]).T
1203        Si = muStrData[1][0]
1204        Sa = muStrData[1][1]
1205        return Si*Sa/(np.sqrt((Si*cosP)**2+(Sa*sinP)**2))
1206    else:       #generalized - P.W. Stephens model
1207        H = np.array(HKL)
1208        rdsq = np.array([G2lat.calc_rDsq2(h,G) for h in H.T])
1209        Strms = np.array(G2spc.MustrainCoeff(H,SGData))
1210        Sum = np.sum(np.array(muStrData[4])[:,nxs]*Strms,axis=0)
1211        return np.sqrt(Sum)/rdsq
1212   
1213def getCrSize(HKL,G,GB,sizeData):
1214    if sizeData[0] == 'isotropic':
1215        return np.ones(HKL.shape[1])*sizeData[1][0]
1216    elif sizeData[0] == 'uniaxial':
1217        H = np.array(HKL)
1218        P = np.array(sizeData[3])
1219        cosP,sinP = np.array([G2lat.CosSinAngle(h,P,G) for h in H.T]).T
1220        Si = sizeData[1][0]
1221        Sa = sizeData[1][1]
1222        return Si*Sa/(np.sqrt((Si*cosP)**2+(Sa*sinP)**2))
1223    else:
1224        Sij =[sizeData[4][i] for i in range(6)]
1225        H = np.array(HKL)
1226        return 1./np.array([ellipseSize(h,Sij,GB) for h in H.T])**2
1227
1228def getHKLpeak(dmin,SGData,A,Inst=None,nodup=False):
1229    '''
1230    Generates allowed by symmetry reflections with d >= dmin
1231    NB: GenHKLf & checkMagextc return True for extinct reflections
1232
1233    :param dmin:  minimum d-spacing
1234    :param SGData: space group data obtained from SpcGroup
1235    :param A: lattice parameter terms A1-A6
1236    :param Inst: instrument parameter info
1237    :returns: HKLs: np.array hkl, etc for allowed reflections
1238
1239    '''
1240    HKL = G2lat.GenHLaue(dmin,SGData,A)       
1241    HKLs = []
1242    ds = []
1243    for h,k,l,d in HKL:
1244        ext = G2spc.GenHKLf([h,k,l],SGData)[0]
1245        if ext and 'MagSpGrp' in SGData:
1246            ext = G2spc.checkMagextc([h,k,l],SGData)
1247        if not ext:
1248            if nodup and int(10000*d) in ds:
1249                continue
1250            ds.append(int(10000*d))
1251            if Inst == None:
1252                HKLs.append([h,k,l,d,0,-1])
1253            else:
1254                HKLs.append([h,k,l,d,G2lat.Dsp2pos(Inst,d),-1])
1255    return np.array(HKLs)
1256
1257def getHKLMpeak(dmin,Inst,SGData,SSGData,Vec,maxH,A):
1258    'needs a doc string'
1259    HKLs = []
1260    vec = np.array(Vec)
1261    vstar = np.sqrt(G2lat.calc_rDsq(vec,A))     #find extra needed for -n SS reflections
1262    dvec = 1./(maxH*vstar+1./dmin)
1263    HKL = G2lat.GenHLaue(dvec,SGData,A)       
1264    SSdH = [vec*h for h in range(-maxH,maxH+1)]
1265    SSdH = dict(zip(range(-maxH,maxH+1),SSdH))
1266    ifMag = False
1267    if 'MagSpGrp' in SGData:
1268        ifMag = True
1269    for h,k,l,d in HKL:
1270        ext = G2spc.GenHKLf([h,k,l],SGData)[0]
1271        if not ext and d >= dmin:
1272            HKLs.append([h,k,l,0,d,G2lat.Dsp2pos(Inst,d),-1])
1273        for dH in SSdH:
1274            if dH:
1275                DH = SSdH[dH]
1276                H = [h+DH[0],k+DH[1],l+DH[2]]
1277                d = float(1/np.sqrt(G2lat.calc_rDsq(H,A)))
1278                if d >= dmin:
1279                    HKLM = np.array([h,k,l,dH])
1280                    if G2spc.checkSSextc(HKLM,SSGData) or ifMag:
1281                        HKLs.append([h,k,l,dH,d,G2lat.Dsp2pos(Inst,d),-1])   
1282    return G2lat.sortHKLd(HKLs,True,True,True)
1283
1284def getPeakProfile(dataType,parmDict,xdata,varyList,bakType):
1285    'Computes the profile for a powder pattern'
1286   
1287    yb = getBackground('',parmDict,bakType,dataType,xdata)[0]
1288    yc = np.zeros_like(yb)
1289    cw = np.diff(xdata)
1290    cw = np.append(cw,cw[-1])
1291    if 'C' in dataType:
1292        shl = max(parmDict['SH/L'],0.002)
1293        Ka2 = False
1294        if 'Lam1' in parmDict.keys():
1295            Ka2 = True
1296            lamRatio = 360*(parmDict['Lam2']-parmDict['Lam1'])/(np.pi*parmDict['Lam1'])
1297            kRatio = parmDict['I(L2)/I(L1)']
1298        iPeak = 0
1299        while True:
1300            try:
1301                pos = parmDict['pos'+str(iPeak)]
1302                tth = (pos-parmDict['Zero'])
1303                intens = parmDict['int'+str(iPeak)]
1304                sigName = 'sig'+str(iPeak)
1305                if sigName in varyList:
1306                    sig = parmDict[sigName]
1307                else:
1308                    sig = G2mth.getCWsig(parmDict,tth)
1309                sig = max(sig,0.001)          #avoid neg sigma^2
1310                gamName = 'gam'+str(iPeak)
1311                if gamName in varyList:
1312                    gam = parmDict[gamName]
1313                else:
1314                    gam = G2mth.getCWgam(parmDict,tth)
1315                gam = max(gam,0.001)             #avoid neg gamma
1316                Wd,fmin,fmax = getWidthsCW(pos,sig,gam,shl)
1317                iBeg = np.searchsorted(xdata,pos-fmin)
1318                iFin = np.searchsorted(xdata,pos+fmin)
1319                if not iBeg+iFin:       #peak below low limit
1320                    iPeak += 1
1321                    continue
1322                elif not iBeg-iFin:     #peak above high limit
1323                    return yb+yc
1324                yc[iBeg:iFin] += intens*getFCJVoigt3(pos,sig,gam,shl,xdata[iBeg:iFin])
1325                if Ka2:
1326                    pos2 = pos+lamRatio*tand(pos/2.0)       # + 360/pi * Dlam/lam * tan(th)
1327                    iBeg = np.searchsorted(xdata,pos2-fmin)
1328                    iFin = np.searchsorted(xdata,pos2+fmin)
1329                    if iBeg-iFin:
1330                        yc[iBeg:iFin] += intens*kRatio*getFCJVoigt3(pos2,sig,gam,shl,xdata[iBeg:iFin])
1331                iPeak += 1
1332            except KeyError:        #no more peaks to process
1333                return yb+yc
1334    elif 'B' in dataType:
1335        iPeak = 0
1336        dsp = 1.0 #for now - fix later
1337        while True:
1338            try:
1339                pos = parmDict['pos'+str(iPeak)]
1340                tth = (pos-parmDict['Zero'])
1341                intens = parmDict['int'+str(iPeak)]
1342                alpName = 'alp'+str(iPeak)
1343                if alpName in varyList:
1344                    alp = parmDict[alpName]
1345                else:
1346                    alp = G2mth.getPinkalpha(parmDict,tth)
1347                alp = max(0.1,alp)
1348                betName = 'bet'+str(iPeak)
1349                if betName in varyList:
1350                    bet = parmDict[betName]
1351                else:
1352                    bet = G2mth.getPinkbeta(parmDict,tth)
1353                bet = max(0.0001,bet)
1354                sigName = 'sig'+str(iPeak)
1355                if sigName in varyList:
1356                    sig = parmDict[sigName]
1357                else:
1358                    sig = G2mth.getCWsig(parmDict,tth)
1359                sig = max(sig,0.001)          #avoid neg sigma^2
1360                gamName = 'gam'+str(iPeak)
1361                if gamName in varyList:
1362                    gam = parmDict[gamName]
1363                else:
1364                    gam = G2mth.getCWgam(parmDict,tth)
1365                gam = max(gam,0.001)             #avoid neg gamma
1366                Wd,fmin,fmax = getWidthsTOF(pos,alp,bet,sig,gam)
1367                iBeg = np.searchsorted(xdata,pos-fmin)
1368                iFin = np.searchsorted(xdata,pos+fmin)
1369                if not iBeg+iFin:       #peak below low limit
1370                    iPeak += 1
1371                    continue
1372                elif not iBeg-iFin:     #peak above high limit
1373                    return yb+yc
1374                yc[iBeg:iFin] += intens*getEpsVoigt(pos,alp,bet,sig/1.e4,gam/100.,xdata[iBeg:iFin])
1375                iPeak += 1
1376            except KeyError:        #no more peaks to process
1377                return yb+yc       
1378    else:
1379        Pdabc = parmDict['Pdabc']
1380        difC = parmDict['difC']
1381        iPeak = 0
1382        while True:
1383            try:
1384                pos = parmDict['pos'+str(iPeak)]               
1385                tof = pos-parmDict['Zero']
1386                dsp = tof/difC
1387                intens = parmDict['int'+str(iPeak)]
1388                alpName = 'alp'+str(iPeak)
1389                if alpName in varyList:
1390                    alp = parmDict[alpName]
1391                else:
1392                    if len(Pdabc):
1393                        alp = np.interp(dsp,Pdabc[0],Pdabc[1])
1394                    else:
1395                        alp = G2mth.getTOFalpha(parmDict,dsp)
1396                alp = max(0.1,alp)
1397                betName = 'bet'+str(iPeak)
1398                if betName in varyList:
1399                    bet = parmDict[betName]
1400                else:
1401                    if len(Pdabc):
1402                        bet = np.interp(dsp,Pdabc[0],Pdabc[2])
1403                    else:
1404                        bet = G2mth.getTOFbeta(parmDict,dsp)
1405                bet = max(0.0001,bet)
1406                sigName = 'sig'+str(iPeak)
1407                if sigName in varyList:
1408                    sig = parmDict[sigName]
1409                else:
1410                    sig = G2mth.getTOFsig(parmDict,dsp)
1411                gamName = 'gam'+str(iPeak)
1412                if gamName in varyList:
1413                    gam = parmDict[gamName]
1414                else:
1415                    gam = G2mth.getTOFgamma(parmDict,dsp)
1416                gam = max(gam,0.001)             #avoid neg gamma
1417                Wd,fmin,fmax = getWidthsTOF(pos,alp,bet,sig,gam)
1418                iBeg = np.searchsorted(xdata,pos-fmin)
1419                iFin = np.searchsorted(xdata,pos+fmax)
1420                lenX = len(xdata)
1421                if not iBeg:
1422                    iFin = np.searchsorted(xdata,pos+fmax)
1423                elif iBeg == lenX:
1424                    iFin = iBeg
1425                else:
1426                    iFin = np.searchsorted(xdata,pos+fmax)
1427                if not iBeg+iFin:       #peak below low limit
1428                    iPeak += 1
1429                    continue
1430                elif not iBeg-iFin:     #peak above high limit
1431                    return yb+yc
1432                yc[iBeg:iFin] += intens*getEpsVoigt(pos,alp,bet,sig,gam,xdata[iBeg:iFin])
1433                iPeak += 1
1434            except KeyError:        #no more peaks to process
1435                return yb+yc
1436           
1437def getPeakProfileDerv(dataType,parmDict,xdata,varyList,bakType):
1438    'needs a doc string'
1439# needs to return np.array([dMdx1,dMdx2,...]) in same order as varylist = backVary,insVary,peakVary order
1440    dMdv = np.zeros(shape=(len(varyList),len(xdata)))
1441    dMdb,dMddb,dMdpk = getBackgroundDerv('',parmDict,bakType,dataType,xdata)
1442    if 'Back;0' in varyList:            #background derivs are in front if present
1443        dMdv[0:len(dMdb)] = dMdb
1444    names = ['DebyeA','DebyeR','DebyeU']
1445    for name in varyList:
1446        if 'Debye' in name:
1447            parm,Id = name.split(';')
1448            ip = names.index(parm)
1449            dMdv[varyList.index(name)] = dMddb[3*int(Id)+ip]
1450    names = ['BkPkpos','BkPkint','BkPksig','BkPkgam']
1451    for name in varyList:
1452        if 'BkPk' in name:
1453            parm,Id = name.split(';')
1454            ip = names.index(parm)
1455            dMdv[varyList.index(name)] = dMdpk[4*int(Id)+ip]
1456    cw = np.diff(xdata)
1457    cw = np.append(cw,cw[-1])
1458    if 'C' in dataType:
1459        shl = max(parmDict['SH/L'],0.002)
1460        Ka2 = False
1461        if 'Lam1' in parmDict.keys():
1462            Ka2 = True
1463            lamRatio = 360*(parmDict['Lam2']-parmDict['Lam1'])/(np.pi*parmDict['Lam1'])
1464            kRatio = parmDict['I(L2)/I(L1)']
1465        iPeak = 0
1466        while True:
1467            try:
1468                pos = parmDict['pos'+str(iPeak)]
1469                tth = (pos-parmDict['Zero'])
1470                intens = parmDict['int'+str(iPeak)]
1471                sigName = 'sig'+str(iPeak)
1472                if sigName in varyList:
1473                    sig = parmDict[sigName]
1474                    dsdU = dsdV = dsdW = 0
1475                else:
1476                    sig = G2mth.getCWsig(parmDict,tth)
1477                    dsdU,dsdV,dsdW = G2mth.getCWsigDeriv(tth)
1478                sig = max(sig,0.001)          #avoid neg sigma
1479                gamName = 'gam'+str(iPeak)
1480                if gamName in varyList:
1481                    gam = parmDict[gamName]
1482                    dgdX = dgdY = dgdZ = 0
1483                else:
1484                    gam = G2mth.getCWgam(parmDict,tth)
1485                    dgdX,dgdY,dgdZ = G2mth.getCWgamDeriv(tth)
1486                gam = max(gam,0.001)             #avoid neg gamma
1487                Wd,fmin,fmax = getWidthsCW(pos,sig,gam,shl)
1488                iBeg = np.searchsorted(xdata,pos-fmin)
1489                iFin = np.searchsorted(xdata,pos+fmin)
1490                if not iBeg+iFin:       #peak below low limit
1491                    iPeak += 1
1492                    continue
1493                elif not iBeg-iFin:     #peak above high limit
1494                    break
1495                dMdpk = np.zeros(shape=(6,len(xdata)))
1496                dMdipk = getdFCJVoigt3(pos,sig,gam,shl,xdata[iBeg:iFin])
1497                for i in range(1,5):
1498                    dMdpk[i][iBeg:iFin] += 100.*cw[iBeg:iFin]*intens*dMdipk[i]
1499                dMdpk[0][iBeg:iFin] += 100.*cw[iBeg:iFin]*dMdipk[0]
1500                dervDict = {'int':dMdpk[0],'pos':dMdpk[1],'sig':dMdpk[2],'gam':dMdpk[3],'shl':dMdpk[4]}
1501                if Ka2:
1502                    pos2 = pos+lamRatio*tand(pos/2.0)       # + 360/pi * Dlam/lam * tan(th)
1503                    iBeg = np.searchsorted(xdata,pos2-fmin)
1504                    iFin = np.searchsorted(xdata,pos2+fmin)
1505                    if iBeg-iFin:
1506                        dMdipk2 = getdFCJVoigt3(pos2,sig,gam,shl,xdata[iBeg:iFin])
1507                        for i in range(1,5):
1508                            dMdpk[i][iBeg:iFin] += 100.*cw[iBeg:iFin]*intens*kRatio*dMdipk2[i]
1509                        dMdpk[0][iBeg:iFin] += 100.*cw[iBeg:iFin]*kRatio*dMdipk2[0]
1510                        dMdpk[5][iBeg:iFin] += 100.*cw[iBeg:iFin]*dMdipk2[0]
1511                        dervDict = {'int':dMdpk[0],'pos':dMdpk[1],'sig':dMdpk[2],'gam':dMdpk[3],'shl':dMdpk[4],'L1/L2':dMdpk[5]*intens}
1512                for parmName in ['pos','int','sig','gam']:
1513                    try:
1514                        idx = varyList.index(parmName+str(iPeak))
1515                        dMdv[idx] = dervDict[parmName]
1516                    except ValueError:
1517                        pass
1518                if 'U' in varyList:
1519                    dMdv[varyList.index('U')] += dsdU*dervDict['sig']
1520                if 'V' in varyList:
1521                    dMdv[varyList.index('V')] += dsdV*dervDict['sig']
1522                if 'W' in varyList:
1523                    dMdv[varyList.index('W')] += dsdW*dervDict['sig']
1524                if 'X' in varyList:
1525                    dMdv[varyList.index('X')] += dgdX*dervDict['gam']
1526                if 'Y' in varyList:
1527                    dMdv[varyList.index('Y')] += dgdY*dervDict['gam']
1528                if 'Z' in varyList:
1529                    dMdv[varyList.index('Z')] += dgdZ*dervDict['gam']
1530                if 'SH/L' in varyList:
1531                    dMdv[varyList.index('SH/L')] += dervDict['shl']         #problem here
1532                if 'I(L2)/I(L1)' in varyList:
1533                    dMdv[varyList.index('I(L2)/I(L1)')] += dervDict['L1/L2']
1534                iPeak += 1
1535            except KeyError:        #no more peaks to process
1536                break
1537    elif 'B' in dataType:
1538        iPeak = 0
1539        while True:
1540            try:
1541                pos = parmDict['pos'+str(iPeak)]
1542                tth = (pos-parmDict['Zero'])
1543                intens = parmDict['int'+str(iPeak)]
1544                alpName = 'alp'+str(iPeak)
1545                if alpName in varyList:
1546                    alp = parmDict[alpName]
1547                    dada0 = dada1 = 0.0
1548                else:
1549                    alp = G2mth.getPinkalpha(parmDict,tth)
1550                    dada0,dada1 = G2mth.getPinkalphaDeriv(tth)
1551                alp = max(0.0001,alp)
1552                betName = 'bet'+str(iPeak)
1553                if betName in varyList:
1554                    bet = parmDict[betName]
1555                    dbdb0 = dbdb1 = 0.0
1556                else:
1557                    bet = G2mth.getPinkbeta(parmDict,tth)
1558                    dbdb0,dbdb1 = G2mth.getPinkbetaDeriv(tth)
1559                bet = max(0.0001,bet)
1560                sigName = 'sig'+str(iPeak)
1561                if sigName in varyList:
1562                    sig = parmDict[sigName]
1563                    dsdU = dsdV = dsdW = 0
1564                else:
1565                    sig = G2mth.getCWsig(parmDict,tth)
1566                    dsdU,dsdV,dsdW = G2mth.getCWsigDeriv(tth)
1567                sig = max(sig,0.001)          #avoid neg sigma
1568                gamName = 'gam'+str(iPeak)
1569                if gamName in varyList:
1570                    gam = parmDict[gamName]
1571                    dgdX = dgdY = dgdZ = 0
1572                else:
1573                    gam = G2mth.getCWgam(parmDict,tth)
1574                    dgdX,dgdY,dgdZ = G2mth.getCWgamDeriv(tth)
1575                gam = max(gam,0.001)             #avoid neg gamma
1576                Wd,fmin,fmax = getWidthsTOF(pos,alp,bet,sig/1.e4,gam/100.)
1577                iBeg = np.searchsorted(xdata,pos-fmin)
1578                iFin = np.searchsorted(xdata,pos+fmin)
1579                if not iBeg+iFin:       #peak below low limit
1580                    iPeak += 1
1581                    continue
1582                elif not iBeg-iFin:     #peak above high limit
1583                    break
1584                dMdpk = np.zeros(shape=(7,len(xdata)))
1585                dMdipk = getdEpsVoigt(pos,alp,bet,sig/1.e4,gam/100.,xdata[iBeg:iFin])
1586                for i in range(1,6):
1587                    dMdpk[i][iBeg:iFin] += cw[iBeg:iFin]*intens*dMdipk[i]
1588                dMdpk[0][iBeg:iFin] += cw[iBeg:iFin]*dMdipk[0]
1589                dervDict = {'int':dMdpk[0],'pos':dMdpk[1],'alp':dMdpk[2],'bet':dMdpk[3],'sig':dMdpk[4]/1.e4,'gam':dMdpk[5]/100.}
1590                for parmName in ['pos','int','alp','bet','sig','gam']:
1591                    try:
1592                        idx = varyList.index(parmName+str(iPeak))
1593                        dMdv[idx] = dervDict[parmName]
1594                    except ValueError:
1595                        pass
1596                if 'U' in varyList:
1597                    dMdv[varyList.index('U')] += dsdU*dervDict['sig']
1598                if 'V' in varyList:
1599                    dMdv[varyList.index('V')] += dsdV*dervDict['sig']
1600                if 'W' in varyList:
1601                    dMdv[varyList.index('W')] += dsdW*dervDict['sig']
1602                if 'X' in varyList:
1603                    dMdv[varyList.index('X')] += dgdX*dervDict['gam']
1604                if 'Y' in varyList:
1605                    dMdv[varyList.index('Y')] += dgdY*dervDict['gam']
1606                if 'Z' in varyList:
1607                    dMdv[varyList.index('Z')] += dgdZ*dervDict['gam']
1608                if 'alpha-0' in varyList:
1609                    dMdv[varyList.index('alpha-0')] += dada0*dervDict['alp']
1610                if 'alpha-1' in varyList:
1611                    dMdv[varyList.index('alpha-1')] += dada1*dervDict['alp']
1612                if 'beta-0' in varyList:
1613                    dMdv[varyList.index('beta-0')] += dbdb0*dervDict['bet']
1614                if 'beta-1' in varyList:
1615                    dMdv[varyList.index('beta-1')] += dbdb1*dervDict['bet']
1616                iPeak += 1
1617            except KeyError:        #no more peaks to process
1618                break       
1619    else:
1620        Pdabc = parmDict['Pdabc']
1621        difC = parmDict['difC']
1622        iPeak = 0
1623        while True:
1624            try:
1625                pos = parmDict['pos'+str(iPeak)]               
1626                tof = pos-parmDict['Zero']
1627                dsp = tof/difC
1628                intens = parmDict['int'+str(iPeak)]
1629                alpName = 'alp'+str(iPeak)
1630                if alpName in varyList:
1631                    alp = parmDict[alpName]
1632                else:
1633                    if len(Pdabc):
1634                        alp = np.interp(dsp,Pdabc[0],Pdabc[1])
1635                        dada0 = 0
1636                    else:
1637                        alp = G2mth.getTOFalpha(parmDict,dsp)
1638                        dada0 = G2mth.getTOFalphaDeriv(dsp)
1639                betName = 'bet'+str(iPeak)
1640                if betName in varyList:
1641                    bet = parmDict[betName]
1642                else:
1643                    if len(Pdabc):
1644                        bet = np.interp(dsp,Pdabc[0],Pdabc[2])
1645                        dbdb0 = dbdb1 = dbdb2 = 0
1646                    else:
1647                        bet = G2mth.getTOFbeta(parmDict,dsp)
1648                        dbdb0,dbdb1,dbdb2 = G2mth.getTOFbetaDeriv(dsp)
1649                sigName = 'sig'+str(iPeak)
1650                if sigName in varyList:
1651                    sig = parmDict[sigName]
1652                    dsds0 = dsds1 = dsds2 = dsds3 = 0
1653                else:
1654                    sig = G2mth.getTOFsig(parmDict,dsp)
1655                    dsds0,dsds1,dsds2,dsds3 = G2mth.getTOFsigDeriv(dsp)
1656                gamName = 'gam'+str(iPeak)
1657                if gamName in varyList:
1658                    gam = parmDict[gamName]
1659                    dsdX = dsdY = dsdZ = 0
1660                else:
1661                    gam = G2mth.getTOFgamma(parmDict,dsp)
1662                    dsdX,dsdY,dsdZ = G2mth.getTOFgammaDeriv(dsp)
1663                gam = max(gam,0.001)             #avoid neg gamma
1664                Wd,fmin,fmax = getWidthsTOF(pos,alp,bet,sig,gam)
1665                iBeg = np.searchsorted(xdata,pos-fmin)
1666                lenX = len(xdata)
1667                if not iBeg:
1668                    iFin = np.searchsorted(xdata,pos+fmax)
1669                elif iBeg == lenX:
1670                    iFin = iBeg
1671                else:
1672                    iFin = np.searchsorted(xdata,pos+fmax)
1673                if not iBeg+iFin:       #peak below low limit
1674                    iPeak += 1
1675                    continue
1676                elif not iBeg-iFin:     #peak above high limit
1677                    break
1678                dMdpk = np.zeros(shape=(7,len(xdata)))
1679                dMdipk = getdEpsVoigt(pos,alp,bet,sig,gam,xdata[iBeg:iFin])
1680                for i in range(1,6):
1681                    dMdpk[i][iBeg:iFin] += intens*cw[iBeg:iFin]*dMdipk[i]
1682                dMdpk[0][iBeg:iFin] += cw[iBeg:iFin]*dMdipk[0]
1683                dervDict = {'int':dMdpk[0],'pos':dMdpk[1],'alp':dMdpk[2],'bet':dMdpk[3],'sig':dMdpk[4],'gam':dMdpk[5]}
1684                for parmName in ['pos','int','alp','bet','sig','gam']:
1685                    try:
1686                        idx = varyList.index(parmName+str(iPeak))
1687                        dMdv[idx] = dervDict[parmName]
1688                    except ValueError:
1689                        pass
1690                if 'alpha' in varyList:
1691                    dMdv[varyList.index('alpha')] += dada0*dervDict['alp']
1692                if 'beta-0' in varyList:
1693                    dMdv[varyList.index('beta-0')] += dbdb0*dervDict['bet']
1694                if 'beta-1' in varyList:
1695                    dMdv[varyList.index('beta-1')] += dbdb1*dervDict['bet']
1696                if 'beta-q' in varyList:
1697                    dMdv[varyList.index('beta-q')] += dbdb2*dervDict['bet']
1698                if 'sig-0' in varyList:
1699                    dMdv[varyList.index('sig-0')] += dsds0*dervDict['sig']
1700                if 'sig-1' in varyList:
1701                    dMdv[varyList.index('sig-1')] += dsds1*dervDict['sig']
1702                if 'sig-2' in varyList:
1703                    dMdv[varyList.index('sig-2')] += dsds2*dervDict['sig']
1704                if 'sig-q' in varyList:
1705                    dMdv[varyList.index('sig-q')] += dsds3*dervDict['sig']
1706                if 'X' in varyList:
1707                    dMdv[varyList.index('X')] += dsdX*dervDict['gam']
1708                if 'Y' in varyList:
1709                    dMdv[varyList.index('Y')] += dsdY*dervDict['gam']
1710                if 'Z' in varyList:
1711                    dMdv[varyList.index('Z')] += dsdZ*dervDict['gam']
1712                iPeak += 1
1713            except KeyError:        #no more peaks to process
1714                break
1715    return dMdv
1716       
1717def Dict2Values(parmdict, varylist):
1718    '''Use before call to leastsq to setup list of values for the parameters
1719    in parmdict, as selected by key in varylist'''
1720    return [parmdict[key] for key in varylist] 
1721   
1722def Values2Dict(parmdict, varylist, values):
1723    ''' Use after call to leastsq to update the parameter dictionary with
1724    values corresponding to keys in varylist'''
1725    parmdict.update(zip(varylist,values))
1726   
1727def SetBackgroundParms(Background):
1728    'Loads background parameters into dicts/lists to create varylist & parmdict'
1729    if len(Background) == 1:            # fix up old backgrounds
1730        Background.append({'nDebye':0,'debyeTerms':[]})
1731    bakType,bakFlag = Background[0][:2]
1732    backVals = Background[0][3:]
1733    backNames = ['Back;'+str(i) for i in range(len(backVals))]
1734    Debye = Background[1]           #also has background peaks stuff
1735    backDict = dict(zip(backNames,backVals))
1736    backVary = []
1737    if bakFlag:
1738        backVary = backNames
1739
1740    backDict['nDebye'] = Debye['nDebye']
1741    debyeDict = {}
1742    debyeList = []
1743    for i in range(Debye['nDebye']):
1744        debyeNames = ['DebyeA;'+str(i),'DebyeR;'+str(i),'DebyeU;'+str(i)]
1745        debyeDict.update(dict(zip(debyeNames,Debye['debyeTerms'][i][::2])))
1746        debyeList += zip(debyeNames,Debye['debyeTerms'][i][1::2])
1747    debyeVary = []
1748    for item in debyeList:
1749        if item[1]:
1750            debyeVary.append(item[0])
1751    backDict.update(debyeDict)
1752    backVary += debyeVary
1753
1754    backDict['nPeaks'] = Debye['nPeaks']
1755    peaksDict = {}
1756    peaksList = []
1757    for i in range(Debye['nPeaks']):
1758        peaksNames = ['BkPkpos;'+str(i),'BkPkint;'+str(i),'BkPksig;'+str(i),'BkPkgam;'+str(i)]
1759        peaksDict.update(dict(zip(peaksNames,Debye['peaksList'][i][::2])))
1760        peaksList += zip(peaksNames,Debye['peaksList'][i][1::2])
1761    peaksVary = []
1762    for item in peaksList:
1763        if item[1]:
1764            peaksVary.append(item[0])
1765    backDict.update(peaksDict)
1766    backVary += peaksVary
1767    return bakType,backDict,backVary
1768   
1769def DoCalibInst(IndexPeaks,Inst):
1770   
1771    def SetInstParms():
1772        dataType = Inst['Type'][0]
1773        insVary = []
1774        insNames = []
1775        insVals = []
1776        for parm in Inst:
1777            insNames.append(parm)
1778            insVals.append(Inst[parm][1])
1779            if parm in ['Lam','difC','difA','difB','Zero',]:
1780                if Inst[parm][2]:
1781                    insVary.append(parm)
1782        instDict = dict(zip(insNames,insVals))
1783        return dataType,instDict,insVary
1784       
1785    def GetInstParms(parmDict,Inst,varyList):
1786        for name in Inst:
1787            Inst[name][1] = parmDict[name]
1788       
1789    def InstPrint(Inst,sigDict):
1790        print ('Instrument Parameters:')
1791        if 'C' in Inst['Type'][0] or 'B' in Inst['Type'][0]:
1792            ptfmt = "%12.6f"
1793        else:
1794            ptfmt = "%12.3f"
1795        ptlbls = 'names :'
1796        ptstr =  'values:'
1797        sigstr = 'esds  :'
1798        for parm in Inst:
1799            if parm in  ['Lam','difC','difA','difB','Zero',]:
1800                ptlbls += "%s" % (parm.center(12))
1801                ptstr += ptfmt % (Inst[parm][1])
1802                if parm in sigDict:
1803                    sigstr += ptfmt % (sigDict[parm])
1804                else:
1805                    sigstr += 12*' '
1806        print (ptlbls)
1807        print (ptstr)
1808        print (sigstr)
1809       
1810    def errPeakPos(values,peakDsp,peakPos,peakWt,dataType,parmDict,varyList):
1811        parmDict.update(zip(varyList,values))
1812        return np.sqrt(peakWt)*(G2lat.getPeakPos(dataType,parmDict,peakDsp)-peakPos)
1813
1814    peakPos = []
1815    peakDsp = []
1816    peakWt = []
1817    for peak,sig in zip(IndexPeaks[0],IndexPeaks[1]):
1818        if peak[2] and peak[3] and sig > 0.:
1819            peakPos.append(peak[0])
1820            peakDsp.append(peak[-1])    #d-calc
1821#            peakWt.append(peak[-1]**2/sig**2)   #weight by d**2
1822            peakWt.append(1./(sig*peak[-1]))   #
1823    peakPos = np.array(peakPos)
1824    peakDsp = np.array(peakDsp)
1825    peakWt = np.array(peakWt)
1826    dataType,insDict,insVary = SetInstParms()
1827    parmDict = {}
1828    parmDict.update(insDict)
1829    varyList = insVary
1830    if not len(varyList):
1831        G2fil.G2Print ('**** ERROR - nothing to refine! ****')
1832        return False
1833    while True:
1834        begin = time.time()
1835        values =  np.array(Dict2Values(parmDict, varyList))
1836        result = so.leastsq(errPeakPos,values,full_output=True,ftol=0.000001,
1837            args=(peakDsp,peakPos,peakWt,dataType,parmDict,varyList))
1838        ncyc = int(result[2]['nfev']/2)
1839        runtime = time.time()-begin   
1840        chisq = np.sum(result[2]['fvec']**2)
1841        Values2Dict(parmDict, varyList, result[0])
1842        GOF = chisq/(len(peakPos)-len(varyList))       #reduced chi^2
1843        G2fil.G2Print ('Number of function calls: %d Number of observations: %d Number of parameters: %d'%(result[2]['nfev'],len(peakPos),len(varyList)))
1844        G2fil.G2Print ('calib time = %8.3fs, %8.3fs/cycle'%(runtime,runtime/ncyc))
1845        G2fil.G2Print ('chi**2 = %12.6g, reduced chi**2 = %6.2f'%(chisq,GOF))
1846        try:
1847            sig = np.sqrt(np.diag(result[1])*GOF)
1848            if np.any(np.isnan(sig)):
1849                G2fil.G2Print ('*** Least squares aborted - some invalid esds possible ***')
1850            break                   #refinement succeeded - finish up!
1851        except ValueError:          #result[1] is None on singular matrix
1852            G2fil.G2Print ('**** Refinement failed - singular matrix ****')
1853       
1854    sigDict = dict(zip(varyList,sig))
1855    GetInstParms(parmDict,Inst,varyList)
1856    InstPrint(Inst,sigDict)
1857    return True
1858           
1859def DoPeakFit(FitPgm,Peaks,Background,Limits,Inst,Inst2,data,fixback=None,prevVaryList=[],oneCycle=False,controls=None,wtFactor=1.0,dlg=None):
1860    '''Called to perform a peak fit, refining the selected items in the peak
1861    table as well as selected items in the background.
1862
1863    :param str FitPgm: type of fit to perform. At present this is ignored.
1864    :param list Peaks: a list of peaks. Each peak entry is a list with 8 values:
1865      four values followed by a refine flag where the values are: position, intensity,
1866      sigma (Gaussian width) and gamma (Lorentzian width). From the Histogram/"Peak List"
1867      tree entry, dict item "peaks"
1868    :param list Background: describes the background. List with two items.
1869      Item 0 specifies a background model and coefficients. Item 1 is a dict.
1870      From the Histogram/Background tree entry.
1871    :param list Limits: min and max x-value to use
1872    :param dict Inst: Instrument parameters
1873    :param dict Inst2: more Instrument parameters
1874    :param numpy.array data: a 5xn array. data[0] is the x-values,
1875      data[1] is the y-values, data[2] are weight values, data[3], [4] and [5] are
1876      calc, background and difference intensities, respectively.
1877    :param array fixback: fixed background values
1878    :param list prevVaryList: Used in sequential refinements to override the
1879      variable list. Defaults as an empty list.
1880    :param bool oneCycle: True if only one cycle of fitting should be performed
1881    :param dict controls: a dict specifying two values, Ftol = controls['min dM/M']
1882      and derivType = controls['deriv type']. If None default values are used.
1883    :param float wtFactor. weight multiplier; = 1.0 by default
1884    :param wx.Dialog dlg: A dialog box that is updated with progress from the fit.
1885      Defaults to None, which means no updates are done.
1886    '''
1887    def GetBackgroundParms(parmList,Background):
1888        iBak = 0
1889        while True:
1890            try:
1891                bakName = 'Back;'+str(iBak)
1892                Background[0][iBak+3] = parmList[bakName]
1893                iBak += 1
1894            except KeyError:
1895                break
1896        iDb = 0
1897        while True:
1898            names = ['DebyeA;','DebyeR;','DebyeU;']
1899            try:
1900                for i,name in enumerate(names):
1901                    val = parmList[name+str(iDb)]
1902                    Background[1]['debyeTerms'][iDb][2*i] = val
1903                iDb += 1
1904            except KeyError:
1905                break
1906        iDb = 0
1907        while True:
1908            names = ['BkPkpos;','BkPkint;','BkPksig;','BkPkgam;']
1909            try:
1910                for i,name in enumerate(names):
1911                    val = parmList[name+str(iDb)]
1912                    Background[1]['peaksList'][iDb][2*i] = val
1913                iDb += 1
1914            except KeyError:
1915                break
1916               
1917    def BackgroundPrint(Background,sigDict):
1918        print ('Background coefficients for '+Background[0][0]+' function')
1919        ptfmt = "%12.5f"
1920        ptstr =  'value: '
1921        sigstr = 'esd  : '
1922        for i,back in enumerate(Background[0][3:]):
1923            ptstr += ptfmt % (back)
1924            if Background[0][1]:
1925                prm = 'Back;'+str(i)
1926                if prm in sigDict:
1927                    sigstr += ptfmt % (sigDict[prm])
1928                else:
1929                    sigstr += " "*12
1930            if len(ptstr) > 75:
1931                print (ptstr)
1932                if Background[0][1]: print (sigstr)
1933                ptstr =  'value: '
1934                sigstr = 'esd  : '
1935        if len(ptstr) > 8:
1936            print (ptstr)
1937            if Background[0][1]: print (sigstr)
1938
1939        if Background[1]['nDebye']:
1940            parms = ['DebyeA;','DebyeR;','DebyeU;']
1941            print ('Debye diffuse scattering coefficients')
1942            ptfmt = "%12.5f"
1943            print (' term       DebyeA       esd        DebyeR       esd        DebyeU        esd')
1944            for term in range(Background[1]['nDebye']):
1945                line = ' term %d'%(term)
1946                for ip,name in enumerate(parms):
1947                    line += ptfmt%(Background[1]['debyeTerms'][term][2*ip])
1948                    if name+str(term) in sigDict:
1949                        line += ptfmt%(sigDict[name+str(term)])
1950                    else:
1951                        line += " "*12
1952                print (line)
1953        if Background[1]['nPeaks']:
1954            print ('Coefficients for Background Peaks')
1955            ptfmt = "%15.3f"
1956            for j,pl in enumerate(Background[1]['peaksList']):
1957                names =  'peak %3d:'%(j+1)
1958                ptstr =  'values  :'
1959                sigstr = 'esds    :'
1960                for i,lbl in enumerate(['BkPkpos','BkPkint','BkPksig','BkPkgam']):
1961                    val = pl[2*i]
1962                    prm = lbl+";"+str(j)
1963                    names += '%15s'%(prm)
1964                    ptstr += ptfmt%(val)
1965                    if prm in sigDict:
1966                        sigstr += ptfmt%(sigDict[prm])
1967                    else:
1968                        sigstr += " "*15
1969                print (names)
1970                print (ptstr)
1971                print (sigstr)
1972                           
1973    def SetInstParms(Inst):
1974        dataType = Inst['Type'][0]
1975        insVary = []
1976        insNames = []
1977        insVals = []
1978        for parm in Inst:
1979            insNames.append(parm)
1980            insVals.append(Inst[parm][1])
1981            if parm in ['U','V','W','X','Y','Z','SH/L','I(L2)/I(L1)','alpha',
1982                'beta-0','beta-1','beta-q','sig-0','sig-1','sig-2','sig-q','alpha-0','alpha-1'] and Inst[parm][2]:
1983                    insVary.append(parm)
1984        instDict = dict(zip(insNames,insVals))
1985#        instDict['X'] = max(instDict['X'],0.01)
1986#        instDict['Y'] = max(instDict['Y'],0.01)
1987        if 'SH/L' in instDict:
1988            instDict['SH/L'] = max(instDict['SH/L'],0.002)
1989        return dataType,instDict,insVary
1990       
1991    def GetInstParms(parmDict,Inst,varyList,Peaks):
1992        for name in Inst:
1993            Inst[name][1] = parmDict[name]
1994        iPeak = 0
1995        while True:
1996            try:
1997                sigName = 'sig'+str(iPeak)
1998                pos = parmDict['pos'+str(iPeak)]
1999                if sigName not in varyList:
2000                    if 'T' in Inst['Type'][0]:
2001                        dsp = G2lat.Pos2dsp(Inst,pos)
2002                        parmDict[sigName] = G2mth.getTOFsig(parmDict,dsp)
2003                    else:
2004                        parmDict[sigName] = G2mth.getCWsig(parmDict,pos)
2005                gamName = 'gam'+str(iPeak)
2006                if gamName not in varyList:
2007                    if 'T' in Inst['Type'][0]:
2008                        dsp = G2lat.Pos2dsp(Inst,pos)
2009                        parmDict[gamName] = G2mth.getTOFgamma(parmDict,dsp)
2010                    else:
2011                        parmDict[gamName] = G2mth.getCWgam(parmDict,pos)
2012                iPeak += 1
2013            except KeyError:
2014                break
2015       
2016    def InstPrint(Inst,sigDict):
2017        print ('Instrument Parameters:')
2018        ptfmt = "%12.6f"
2019        ptlbls = 'names :'
2020        ptstr =  'values:'
2021        sigstr = 'esds  :'
2022        for parm in Inst:
2023            if parm in  ['U','V','W','X','Y','Z','SH/L','I(L2)/I(L1)','alpha',
2024                'beta-0','beta-1','beta-q','sig-0','sig-1','sig-2','sig-q','alpha-0','alpha-1']:
2025                ptlbls += "%s" % (parm.center(12))
2026                ptstr += ptfmt % (Inst[parm][1])
2027                if parm in sigDict:
2028                    sigstr += ptfmt % (sigDict[parm])
2029                else:
2030                    sigstr += 12*' '
2031        print (ptlbls)
2032        print (ptstr)
2033        print (sigstr)
2034
2035    def SetPeaksParms(dataType,Peaks):
2036        peakNames = []
2037        peakVary = []
2038        peakVals = []
2039        if 'C' in dataType:
2040            names = ['pos','int','sig','gam']
2041        else:   #'T' and 'B'
2042            names = ['pos','int','alp','bet','sig','gam']
2043        for i,peak in enumerate(Peaks):
2044            for j,name in enumerate(names):
2045                peakVals.append(peak[2*j])
2046                parName = name+str(i)
2047                peakNames.append(parName)
2048                if peak[2*j+1]:
2049                    peakVary.append(parName)
2050        return dict(zip(peakNames,peakVals)),peakVary
2051               
2052    def GetPeaksParms(Inst,parmDict,Peaks,varyList):
2053        if 'C' in Inst['Type'][0]:
2054            names = ['pos','int','sig','gam']
2055        else:   #'T' & 'B'
2056            names = ['pos','int','alp','bet','sig','gam']
2057        for i,peak in enumerate(Peaks):
2058            pos = parmDict['pos'+str(i)]
2059            if 'difC' in Inst:
2060                dsp = pos/Inst['difC'][1]
2061            for j in range(len(names)):
2062                parName = names[j]+str(i)
2063                if parName in varyList:
2064                    peak[2*j] = parmDict[parName]
2065                elif 'alp' in parName:
2066                    if 'T' in Inst['Type'][0]:
2067                        peak[2*j] = G2mth.getTOFalpha(parmDict,dsp)
2068                    else: #'B'
2069                        peak[2*j] = G2mth.getPinkalpha(parmDict,pos)
2070                elif 'bet' in parName:
2071                    if 'T' in Inst['Type'][0]:
2072                        peak[2*j] = G2mth.getTOFbeta(parmDict,dsp)
2073                    else:   #'B'
2074                        peak[2*j] = G2mth.getPinkbeta(parmDict,pos)
2075                elif 'sig' in parName:
2076                    if 'T' in Inst['Type'][0]:
2077                        peak[2*j] = G2mth.getTOFsig(parmDict,dsp)
2078                    else:   #'C' & 'B'
2079                        peak[2*j] = G2mth.getCWsig(parmDict,pos)
2080                elif 'gam' in parName:
2081                    if 'T' in Inst['Type'][0]:
2082                        peak[2*j] = G2mth.getTOFgamma(parmDict,dsp)
2083                    else:   #'C' & 'B'
2084                        peak[2*j] = G2mth.getCWgam(parmDict,pos)
2085                       
2086    def PeaksPrint(dataType,parmDict,sigDict,varyList,ptsperFW):
2087        print ('Peak coefficients:')
2088        if 'C' in dataType:
2089            names = ['pos','int','sig','gam']
2090        else:   #'T' & 'B'
2091            names = ['pos','int','alp','bet','sig','gam']           
2092        head = 13*' '
2093        for name in names:
2094            if name in ['alp','bet']:
2095                head += name.center(8)+'esd'.center(8)
2096            else:
2097                head += name.center(10)+'esd'.center(10)
2098        head += 'bins'.center(8)
2099        print (head)
2100        if 'C' in dataType:
2101            ptfmt = {'pos':"%10.5f",'int':"%10.1f",'sig':"%10.3f",'gam':"%10.3f"}
2102        elif 'T' in dataType:
2103            ptfmt = {'pos':"%10.2f",'int':"%10.4f",'alp':"%8.3f",'bet':"%8.5f",'sig':"%10.3f",'gam':"%10.3f"}
2104        else: #'B'
2105            ptfmt = {'pos':"%10.5f",'int':"%10.1f",'alp':"%8.2f",'bet':"%8.4f",'sig':"%10.3f",'gam':"%10.3f"}
2106        for i,peak in enumerate(Peaks):
2107            ptstr =  ':'
2108            for j in range(len(names)):
2109                name = names[j]
2110                parName = name+str(i)
2111                ptstr += ptfmt[name] % (parmDict[parName])
2112                if parName in varyList:
2113                    ptstr += ptfmt[name] % (sigDict[parName])
2114                else:
2115                    if name in ['alp','bet']:
2116                        ptstr += 8*' '
2117                    else:
2118                        ptstr += 10*' '
2119            ptstr += '%9.2f'%(ptsperFW[i])
2120            print ('%s'%(('Peak'+str(i+1)).center(8)),ptstr)
2121               
2122    def devPeakProfile(values,xdata,ydata, weights,dataType,parmdict,varylist,bakType,dlg):
2123        parmdict.update(zip(varylist,values))
2124        return np.sqrt(weights)*getPeakProfileDerv(dataType,parmdict,xdata,varylist,bakType)
2125           
2126    def errPeakProfile(values,xdata,ydata,weights,dataType,parmdict,varylist,bakType,dlg):       
2127        parmdict.update(zip(varylist,values))
2128        M = np.sqrt(weights)*(getPeakProfile(dataType,parmdict,xdata,varylist,bakType)-ydata)
2129        Rwp = min(100.,np.sqrt(np.sum(M**2)/np.sum(weights*ydata**2))*100.)
2130        if dlg:
2131            dlg.Raise()
2132            GoOn = dlg.Update(Rwp,newmsg='%s%8.3f%s'%('Peak fit Rwp =',Rwp,'%'))[0]
2133            if not GoOn:
2134                return -M           #abort!!
2135        return M
2136       
2137    if controls:
2138        Ftol = controls['min dM/M']
2139    else:
2140        Ftol = 0.0001
2141    if oneCycle:
2142        Ftol = 1.0
2143    x,y,w,yc,yb,yd = data   #these are numpy arrays - remove masks!
2144    if fixback is None:
2145        fixback = np.zeros_like(y)
2146    yc *= 0.                            #set calcd ones to zero
2147    yb *= 0.
2148    yd *= 0.
2149    cw = x[1:]-x[:-1]
2150    xBeg = np.searchsorted(x,Limits[0])
2151    xFin = np.searchsorted(x,Limits[1])+1
2152    bakType,bakDict,bakVary = SetBackgroundParms(Background)
2153    dataType,insDict,insVary = SetInstParms(Inst)
2154    peakDict,peakVary = SetPeaksParms(Inst['Type'][0],Peaks)
2155    parmDict = {}
2156    parmDict.update(bakDict)
2157    parmDict.update(insDict)
2158    parmDict.update(peakDict)
2159    parmDict['Pdabc'] = []      #dummy Pdabc
2160    parmDict.update(Inst2)      #put in real one if there
2161    if prevVaryList:
2162        varyList = prevVaryList[:]
2163    else:
2164        varyList = bakVary+insVary+peakVary
2165    fullvaryList = varyList[:]
2166    while True:
2167        begin = time.time()
2168        values =  np.array(Dict2Values(parmDict, varyList))
2169        Rvals = {}
2170        badVary = []
2171        result = so.leastsq(errPeakProfile,values,Dfun=devPeakProfile,full_output=True,ftol=Ftol,col_deriv=True,
2172               args=(x[xBeg:xFin],(y+fixback)[xBeg:xFin],wtFactor*w[xBeg:xFin],dataType,parmDict,varyList,bakType,dlg))
2173        ncyc = int(result[2]['nfev']/2)
2174        runtime = time.time()-begin   
2175        chisq = np.sum(result[2]['fvec']**2)
2176        Values2Dict(parmDict, varyList, result[0])
2177        Rvals['Rwp'] = np.sqrt(chisq/np.sum(wtFactor*w[xBeg:xFin]*(y+fixback)[xBeg:xFin]**2))*100.      #to %
2178        Rvals['GOF'] = chisq/(xFin-xBeg-len(varyList))       #reduced chi^2
2179        G2fil.G2Print ('Number of function calls: %d Number of observations: %d Number of parameters: %d'%(result[2]['nfev'],xFin-xBeg,len(varyList)))
2180        if ncyc:
2181            G2fil.G2Print ('fitpeak time = %8.3fs, %8.3fs/cycle'%(runtime,runtime/ncyc))
2182        G2fil.G2Print ('Rwp = %7.2f%%, chi**2 = %12.6g, reduced chi**2 = %6.2f'%(Rvals['Rwp'],chisq,Rvals['GOF']))
2183        sig = [0]*len(varyList)
2184        if len(varyList) == 0: break  # if nothing was refined
2185        try:
2186            sig = np.sqrt(np.diag(result[1])*Rvals['GOF'])
2187            if np.any(np.isnan(sig)):
2188                G2fil.G2Print ('*** Least squares aborted - some invalid esds possible ***')
2189            break                   #refinement succeeded - finish up!
2190        except ValueError:          #result[1] is None on singular matrix
2191            G2fil.G2Print ('**** Refinement failed - singular matrix ****')
2192            Ipvt = result[2]['ipvt']
2193            for i,ipvt in enumerate(Ipvt):
2194                if not np.sum(result[2]['fjac'],axis=1)[i]:
2195                    G2fil.G2Print ('Removing parameter: '+varyList[ipvt-1])
2196                    badVary.append(varyList[ipvt-1])
2197                    del(varyList[ipvt-1])
2198                    break
2199            else: # nothing removed
2200                break
2201    if dlg: dlg.Destroy()
2202    sigDict = dict(zip(varyList,sig))
2203    yb[xBeg:xFin] = getBackground('',parmDict,bakType,dataType,x[xBeg:xFin])[0]-fixback[xBeg:xFin]
2204    yc[xBeg:xFin] = getPeakProfile(dataType,parmDict,x[xBeg:xFin],varyList,bakType)-fixback[xBeg:xFin]
2205    yd[xBeg:xFin] = y[xBeg:xFin]-yc[xBeg:xFin]
2206    GetBackgroundParms(parmDict,Background)
2207    if bakVary: BackgroundPrint(Background,sigDict)
2208    GetInstParms(parmDict,Inst,varyList,Peaks)
2209    if insVary: InstPrint(Inst,sigDict)
2210    GetPeaksParms(Inst,parmDict,Peaks,varyList)
2211    binsperFWHM = []
2212    for peak in Peaks:
2213        FWHM = getFWHM(peak[0],Inst)
2214        try:
2215            binsperFWHM.append(FWHM/cw[x.searchsorted(peak[0])])
2216        except IndexError:
2217            binsperFWHM.append(0.)
2218    if peakVary: PeaksPrint(dataType,parmDict,sigDict,varyList,binsperFWHM)
2219    if len(binsperFWHM):
2220        if min(binsperFWHM) < 1.:
2221            G2fil.G2Print ('*** Warning: calculated peak widths are too narrow to refine profile coefficients ***')
2222            if 'T' in Inst['Type'][0]:
2223                G2fil.G2Print (' Manually increase sig-0, 1, or 2 in Instrument Parameters')
2224            else:
2225                G2fil.G2Print (' Manually increase W in Instrument Parameters')
2226        elif min(binsperFWHM) < 4.:
2227            G2fil.G2Print ('*** Warning: data binning yields too few data points across peak FWHM for reliable Rietveld refinement ***')
2228            G2fil.G2Print ('*** recommended is 6-10; you have %.2f ***'%(min(binsperFWHM)))
2229    return sigDict,result,sig,Rvals,varyList,parmDict,fullvaryList,badVary
2230   
2231def calcIncident(Iparm,xdata):
2232    'needs a doc string'
2233
2234    def IfunAdv(Iparm,xdata):
2235        Itype = Iparm['Itype']
2236        Icoef = Iparm['Icoeff']
2237        DYI = np.ones((12,xdata.shape[0]))
2238        YI = np.ones_like(xdata)*Icoef[0]
2239       
2240        x = xdata/1000.                 #expressions are in ms
2241        if Itype == 'Exponential':
2242            for i in [1,3,5,7,9]:
2243                Eterm = np.exp(-Icoef[i+1]*x**((i+1)/2))
2244                YI += Icoef[i]*Eterm
2245                DYI[i] *= Eterm
2246                DYI[i+1] *= -Icoef[i]*Eterm*x**((i+1)/2)           
2247        elif 'Maxwell'in Itype:
2248            Eterm = np.exp(-Icoef[2]/x**2)
2249            DYI[1] = Eterm/x**5
2250            DYI[2] = -Icoef[1]*DYI[1]/x**2
2251            YI += (Icoef[1]*Eterm/x**5)
2252            if 'Exponential' in Itype:
2253                for i in range(3,11,2):
2254                    Eterm = np.exp(-Icoef[i+1]*x**((i+1)/2))
2255                    YI += Icoef[i]*Eterm
2256                    DYI[i] *= Eterm
2257                    DYI[i+1] *= -Icoef[i]*Eterm*x**((i+1)/2)
2258            else:   #Chebyschev
2259                T = (2./x)-1.
2260                Ccof = np.ones((12,xdata.shape[0]))
2261                Ccof[1] = T
2262                for i in range(2,12):
2263                    Ccof[i] = 2*T*Ccof[i-1]-Ccof[i-2]
2264                for i in range(1,10):
2265                    YI += Ccof[i]*Icoef[i+2]
2266                    DYI[i+2] =Ccof[i]
2267        return YI,DYI
2268       
2269    Iesd = np.array(Iparm['Iesd'])
2270    Icovar = Iparm['Icovar']
2271    YI,DYI = IfunAdv(Iparm,xdata)
2272    YI = np.where(YI>0,YI,1.)
2273    WYI = np.zeros_like(xdata)
2274    vcov = np.zeros((12,12))
2275    k = 0
2276    for i in range(12):
2277        for j in range(i,12):
2278            vcov[i][j] = Icovar[k]*Iesd[i]*Iesd[j]
2279            vcov[j][i] = Icovar[k]*Iesd[i]*Iesd[j]
2280            k += 1
2281    M = np.inner(vcov,DYI.T)
2282    WYI = np.sum(M*DYI,axis=0)
2283    WYI = np.where(WYI>0.,WYI,0.)
2284    return YI,WYI
2285
2286################################################################################
2287#### RMCutilities
2288################################################################################
2289   
2290def MakeInst(PWDdata,Name,Size,Mustrain,useSamBrd):
2291    inst = PWDdata['Instrument Parameters'][0]
2292    Xsb = 0.
2293    Ysb = 0.
2294    if 'T' in inst['Type'][1]:
2295        difC = inst['difC'][1]
2296        if useSamBrd[0]:
2297            if 'ellipsoidal' not in Size[0]:    #take the isotropic term only
2298                Xsb = 1.e-4*difC/Size[1][0]
2299        if useSamBrd[1]:
2300            if 'generalized' not in Mustrain[0]:    #take the isotropic term only
2301                Ysb = 1.e-6*difC*Mustrain[1][0]
2302        prms = ['Bank',
2303                'difC','difA','Zero','2-theta',
2304                'alpha','beta-0','beta-1',
2305                'sig-0','sig-1','sig-2',
2306                'Z','X','Y']
2307        fname = Name+'.inst'
2308        fl = open(fname,'w')
2309        fl.write('1\n')
2310        fl.write('%d\n'%int(inst[prms[0]][1]))
2311        fl.write('%19.11f%19.11f%19.11f%19.11f\n'%(inst[prms[1]][1],inst[prms[2]][1],inst[prms[3]][1],inst[prms[4]][1]))
2312        fl.write('%12.6e%14.6e%14.6e\n'%(inst[prms[5]][1],inst[prms[6]][1],inst[prms[7]][1]))
2313        fl.write('%12.6e%14.6e%14.6e\n'%(inst[prms[8]][1],inst[prms[9]][1],inst[prms[10]][1]))   
2314        fl.write('%12.6e%14.6e%14.6e%14.6e%14.6e\n'%(inst[prms[11]][1],inst[prms[12]][1]+Ysb,inst[prms[13]][1]+Xsb,0.0,0.0))
2315        fl.close()
2316    else:
2317        if useSamBrd[0]:
2318            wave = G2mth.getWave(inst)
2319            if 'ellipsoidal' not in Size[0]:    #take the isotropic term only
2320                Xsb = 1.8*wave/(np.pi*Size[1][0])
2321        if useSamBrd[1]:
2322            if 'generalized' not in Mustrain[0]:    #take the isotropic term only
2323                Ysb = 0.0180*Mustrain[1][0]/np.pi
2324        prms = ['Bank',
2325                'Lam','Zero','Polariz.',
2326                'U','V','W',
2327                'X','Y']
2328        fname = Name+'.inst'
2329        fl = open(fname,'w')
2330        fl.write('1\n')
2331        fl.write('%d\n'%int(inst[prms[0]][1]))
2332        fl.write('%10.5f%10.5f%10.4f%10d\n'%(inst[prms[1]][1],-100.*inst[prms[2]][1],inst[prms[3]][1],0))
2333        fl.write('%10.3f%10.3f%10.3f\n'%(inst[prms[4]][1],inst[prms[5]][1],inst[prms[6]][1]))
2334        fl.write('%10.3f%10.3f%10.3f\n'%(inst[prms[7]][1]+Xsb,inst[prms[8]][1]+Ysb,0.0))   
2335        fl.write('%10.3f%10.3f%10.3f\n'%(0.0,0.0,0.0))
2336        fl.close()
2337    return fname
2338   
2339def MakeBack(PWDdata,Name):
2340    Back = PWDdata['Background'][0]
2341    inst = PWDdata['Instrument Parameters'][0]
2342    if 'chebyschev-1' != Back[0]:
2343        return None
2344    Nback = Back[2]
2345    BackVals = Back[3:]
2346    fname = Name+'.back'
2347    fl = open(fname,'w')
2348    fl.write('%10d\n'%Nback)
2349    for val in BackVals:
2350        if 'T' in inst['Type'][1]:
2351            fl.write('%12.6g\n'%(float(val)))
2352        else:
2353            fl.write('%12.6g\n'%val)
2354    fl.close()
2355    return fname
2356
2357def findDup(Atoms):
2358    Dup = []
2359    Fracs = []
2360    for iat1,at1 in enumerate(Atoms):
2361        if any([at1[0] in dup for dup in Dup]):
2362            continue
2363        else:
2364            Dup.append([at1[0],])
2365            Fracs.append([at1[6],])
2366        for iat2,at2 in enumerate(Atoms[(iat1+1):]):
2367            if np.sum((np.array(at1[3:6])-np.array(at2[3:6]))**2) < 0.00001:
2368                Dup[-1] += [at2[0],]
2369                Fracs[-1]+= [at2[6],]
2370    return Dup,Fracs
2371
2372def MakeRMC6f(PWDdata,Name,Phase,RMCPdict):   
2373   
2374    Meta = RMCPdict['metadata']
2375    Atseq = RMCPdict['atSeq']
2376    Supercell =  RMCPdict['SuperCell']
2377    generalData = Phase['General']
2378    Dups,Fracs = findDup(Phase['Atoms'])
2379    Sfracs = [np.cumsum(fracs) for fracs in Fracs]
2380    ifSfracs = any([np.any(sfracs-1.) for sfracs in Sfracs])
2381    Sample = PWDdata['Sample Parameters']
2382    Meta['temperature'] = Sample['Temperature']
2383    Meta['pressure'] = Sample['Pressure']
2384    Cell = generalData['Cell'][1:7]
2385    Trans = np.eye(3)*np.array(Supercell)
2386    newPhase = copy.deepcopy(Phase)
2387    newPhase['General']['SGData'] = G2spc.SpcGroup('P 1')[1]
2388    newPhase['General']['Cell'][1:] = G2lat.TransformCell(Cell,Trans)
2389    GB = G2lat.cell2Gmat( newPhase['General']['Cell'][1:7])[0]
2390    RMCPdict['Rmax'] = np.min(np.sqrt(np.array([1./G2lat.calc_rDsq2(H,GB) for H in [[1,0,0],[0,1,0],[0,0,1]]])))/2.
2391    newPhase,Atcodes = G2lat.TransformPhase(Phase,newPhase,Trans,np.zeros(3),np.zeros(3),ifMag=False,Force=True)
2392    Natm = np.core.defchararray.count(np.array(Atcodes),'+')    #no. atoms in original unit cell
2393    Natm = np.count_nonzero(Natm-1)
2394    Atoms = newPhase['Atoms']
2395    reset = False
2396   
2397    if ifSfracs:
2398        Natm = np.core.defchararray.count(np.array(Atcodes),'+')    #no. atoms in original unit cell
2399        Natm = np.count_nonzero(Natm-1)
2400        Satoms = []
2401        for i in range(len(Atoms)//Natm):
2402            ind = i*Natm
2403            Satoms.append(G2mth.sortArray(G2mth.sortArray(G2mth.sortArray(Atoms[ind:ind+Natm],5),4),3))
2404        Natoms = []
2405        for satoms in Satoms:
2406            for idup,dup in enumerate(Dups):
2407                ldup = len(dup)
2408                natm = len(satoms)
2409                i = 0
2410                while i < natm:
2411                    if satoms[i][0] in dup:
2412                        atoms = satoms[i:i+ldup]
2413                        try:
2414                            atom = atoms[np.searchsorted(Sfracs[idup],rand.random())]
2415                            Natoms.append(atom)
2416                        except IndexError:      #what about vacancies?
2417                            if 'Va' not in Atseq:
2418                                reset = True
2419                                Atseq.append('Va')
2420                                RMCPdict['aTypes']['Va'] = 0.0
2421                            atom = atoms[0]
2422                            atom[1] = 'Va'
2423                            Natoms.append(atom)
2424                        i += ldup
2425                    else:
2426                       i += 1
2427    else:
2428        Natoms = Atoms
2429   
2430    NAtype = np.zeros(len(Atseq))
2431    for atom in Natoms:
2432        NAtype[Atseq.index(atom[1])] += 1
2433    NAstr = ['%6d'%i for i in NAtype]
2434    Cell = newPhase['General']['Cell'][1:7]
2435    if os.path.exists(Name+'.his6f'):
2436        os.remove(Name+'.his6f')
2437    if os.path.exists(Name+'.neigh'):
2438        os.remove(Name+'.neigh')
2439    fname = Name+'.rmc6f'
2440    fl = open(fname,'w')
2441    fl.write('(Version 6f format configuration file)\n')
2442    for item in Meta:
2443        fl.write('%-20s%s\n'%('Metadata '+item+':',Meta[item]))
2444    fl.write('Atom types present:                 %s\n'%'    '.join(Atseq))
2445    fl.write('Number of each atom type:       %s\n'%''.join(NAstr))
2446    fl.write('Number of atoms:                %d\n'%len(Natoms))
2447    fl.write('%-35s%4d%4d%4d\n'%('Supercell dimensions:',Supercell[0],Supercell[1],Supercell[2]))
2448    fl.write('Cell (Ang/deg): %12.6f%12.6f%12.6f%12.6f%12.6f%12.6f\n'%(
2449            Cell[0],Cell[1],Cell[2],Cell[3],Cell[4],Cell[5]))
2450    A,B = G2lat.cell2AB(Cell,True)
2451    fl.write('Lattice vectors (Ang):\n')   
2452    for i in [0,1,2]:
2453        fl.write('%12.6f%12.6f%12.6f\n'%(A[i,0],A[i,1],A[i,2]))
2454    fl.write('Atoms (fractional coordinates):\n')
2455    nat = 0
2456    for atm in Atseq:
2457        for iat,atom in enumerate(Natoms):
2458            if atom[1] == atm:
2459                nat += 1
2460                atcode = Atcodes[iat].split(':')
2461                cell = [0,0,0]
2462                if '+' in atcode[1]:
2463                    cell = eval(atcode[1].split('+')[1])
2464                fl.write('%6d%4s  [%s]%19.15f%19.15f%19.15f%6d%4d%4d%4d\n'%(       
2465                        nat,atom[1].strip(),atcode[0],atom[3],atom[4],atom[5],(iat)%Natm+1,cell[0],cell[1],cell[2]))
2466    fl.close()
2467    return fname,reset
2468
2469def MakeBragg(PWDdata,Name,Phase):
2470    generalData = Phase['General']
2471    Vol = generalData['Cell'][7]
2472    Data = PWDdata['Data']
2473    Inst = PWDdata['Instrument Parameters'][0]
2474    Bank = int(Inst['Bank'][1])
2475    Sample = PWDdata['Sample Parameters']
2476    Scale = Sample['Scale'][0]
2477    if 'X' in Inst['Type'][1]:
2478        Scale *= 2.
2479    Limits = PWDdata['Limits'][1]
2480    Ibeg = np.searchsorted(Data[0],Limits[0])
2481    Ifin = np.searchsorted(Data[0],Limits[1])+1
2482    fname = Name+'.bragg'
2483    fl = open(fname,'w')
2484    fl.write('%12d%6d%15.7f%15.4f\n'%(Ifin-Ibeg-2,Bank,Scale,Vol))
2485    if 'T' in Inst['Type'][0]:
2486        fl.write('%12s%12s\n'%('   TOF,ms','  I(obs)'))
2487        for i in range(Ibeg,Ifin-1):
2488            fl.write('%12.8f%12.6f\n'%(Data[0][i]/1000.,Data[1][i]))
2489    else:
2490        fl.write('%12s%12s\n'%('   2-theta, deg','  I(obs)'))
2491        for i in range(Ibeg,Ifin-1):
2492            fl.write('%11.6f%15.2f\n'%(Data[0][i],Data[1][i]))       
2493    fl.close()
2494    return fname
2495
2496def MakeRMCPdat(PWDdata,Name,Phase,RMCPdict):
2497    Meta = RMCPdict['metadata']
2498    Times = RMCPdict['runTimes']
2499    Atseq = RMCPdict['atSeq']
2500    Atypes = RMCPdict['aTypes']
2501    atPairs = RMCPdict['Pairs']
2502    Files = RMCPdict['files']
2503    BraggWt = RMCPdict['histogram'][1]
2504    inst = PWDdata['Instrument Parameters'][0]
2505    try:
2506        refList = PWDdata['Reflection Lists'][Name]['RefList']
2507    except KeyError:
2508        return 'Error - missing reflection list; you must do Refine first'
2509    dMin = refList[-1][4]
2510    gsasType = 'xray2'
2511    if 'T' in inst['Type'][1]:
2512        gsasType = 'gsas3'
2513    elif 'X' in inst['Type'][1]:
2514        XFF = G2elem.GetFFtable(Atseq)
2515        Xfl = open(Name+'.xray','w')
2516        for atm in Atseq:
2517            fa = XFF[atm]['fa']
2518            fb = XFF[atm]['fb']
2519            fc = XFF[atm]['fc']
2520            Xfl.write('%2s  %8.4f%8.4f%8.4f%8.4f%8.4f%8.4f%8.4f%8.4f%8.4f\n'%(
2521                    atm.upper(),fa[0],fb[0],fa[1],fb[1],fa[2],fb[2],fa[3],fb[3],fc))
2522        Xfl.close()
2523    lenA = len(Atseq)
2524    Pairs = []
2525    for pair in [[' %s-%s'%(Atseq[i],Atseq[j]) for j in range(i,lenA)] for i in range(lenA)]:
2526        Pairs += pair
2527    pairMin = [atPairs[pair]for pair in Pairs if pair in atPairs]
2528    maxMoves = [Atypes[atm] for atm in Atseq if atm in Atypes]
2529    fname = Name+'.dat'
2530    fl = open(fname,'w')
2531    fl.write(' %% Hand edit the following as needed\n')
2532    fl.write('TITLE :: '+Name+'\n')
2533    fl.write('MATERIAL :: '+Meta['material']+'\n')
2534    fl.write('PHASE :: '+Meta['phase']+'\n')
2535    fl.write('TEMPERATURE :: '+str(Meta['temperature'])+'\n')
2536    fl.write('INVESTIGATOR :: '+Meta['owner']+'\n')
2537    minHD = ' '.join(['%6.3f'%dist[0] for dist in pairMin])
2538    minD = ' '.join(['%6.3f'%dist[1] for dist in pairMin])
2539    maxD = ' '.join(['%6.3f'%dist[2] for dist in pairMin])
2540    fl.write('MINIMUM_DISTANCES ::   %s  Angstrom\n'%minHD)
2541    maxMv = ' '.join(['%6.3f'%mov for mov in maxMoves])
2542    fl.write('MAXIMUM_MOVES ::   %s Angstrom\n'%maxMv)
2543    fl.write('R_SPACING ::  0.0200 Angstrom\n')
2544    fl.write('PRINT_PERIOD :: 100\n')
2545    fl.write('TIME_LIMIT ::     %.2f MINUTES\n'%Times[0])
2546    fl.write('SAVE_PERIOD ::    %.2f MINUTES\n'%Times[1])
2547    fl.write('\n')
2548    fl.write('ATOMS :: '+' '.join(Atseq)+'\n')
2549    fl.write('\n')
2550    fl.write('FLAGS ::\n')
2551    fl.write('  > NO_MOVEOUT\n')
2552    fl.write('  > NO_SAVE_CONFIGURATIONS\n')
2553    fl.write('  > NO_RESOLUTION_CONVOLUTION\n')
2554    fl.write('\n')
2555    fl.write('INPUT_CONFIGURATION_FORMAT ::  rmc6f\n')
2556    fl.write('SAVE_CONFIGURATION_FORMAT  ::  rmc6f\n')
2557    fl.write('IGNORE_HISTORY_FILE ::\n')
2558    fl.write('\n')
2559    fl.write('DISTANCE_WINDOW ::\n')
2560    fl.write('  > MNDIST :: %s\n'%minD)
2561    fl.write('  > MXDIST :: %s\n'%maxD)
2562    if len(RMCPdict['Potentials']['Stretch']) or len(RMCPdict['Potentials']['Stretch']):
2563        fl.write('\n')
2564        fl.write('POTENTIALS ::\n')
2565        fl.write('  > TEMPERATURE :: %.1f K\n'%RMCPdict['Potentials']['Pot. Temp.'])
2566        fl.write('  > PLOT :: pixels=400, colour=red, zangle=90, zrotation=45 deg\n')
2567        if len(RMCPdict['Potentials']['Stretch']):
2568            fl.write('  > STRETCH_SEARCH :: %.1f%%\n'%RMCPdict['Potentials']['Stretch search'])
2569            for bond in RMCPdict['Potentials']['Stretch']:
2570                fl.write('  > STRETCH :: %s %s %.2f eV %.2f Ang\n'%(bond[0],bond[1],bond[3],bond[2]))       
2571        if len(RMCPdict['Potentials']['Angles']):
2572            fl.write('  > ANGLE_SEARCH :: %.1f%%\n'%RMCPdict['Potentials']['Angle search'])
2573            for angle in RMCPdict['Potentials']['Angles']:
2574                fl.write('  > ANGLE :: %s %s %s %.2f eV %.2f deg %.2f %.2f Ang\n'%
2575                    (angle[1],angle[0],angle[2],angle[6],angle[3],angle[4],angle[5]))
2576    if RMCPdict['useBVS']:
2577        fl.write('BVS ::\n')
2578        fl.write('  > ATOM :: '+' '.join(Atseq)+'\n')
2579        fl.write('  > WEIGHTS :: %s\n'%' '.join(['%6.3f'%RMCPdict['BVS'][bvs][2] for bvs in RMCPdict['BVS']]))
2580        oxid = []
2581        for val in RMCPdict['Oxid']:
2582            if len(val) == 3:
2583                oxid.append(val[0][1:])
2584            else:
2585                oxid.append(val[0][2:])
2586        fl.write('  > OXID :: %s\n'%' '.join(oxid))
2587        fl.write('  > RIJ :: %s\n'%' '.join(['%6.3f'%RMCPdict['BVS'][bvs][0] for bvs in RMCPdict['BVS']]))
2588        fl.write('  > BVAL :: %s\n'%' '.join(['%6.3f'%RMCPdict['BVS'][bvs][1] for bvs in RMCPdict['BVS']]))
2589        fl.write('  > CUTOFF :: %s\n'%' '.join(['%6.3f'%RMCPdict['BVS'][bvs][2] for bvs in RMCPdict['BVS']]))       
2590        fl.write('  > SAVE :: 100000\n')
2591        fl.write('  > UPDATE :: 100000\n')
2592        if len(RMCPdict['Swap']):
2593            fl.write('\n')
2594            fl.write('SWAP_MULTI ::\n')
2595            for swap in RMCPdict['Swap']:
2596                try:
2597                    at1 = Atseq.index(swap[0])
2598                    at2 = Atseq.index(swap[1])
2599                except ValueError:
2600                    break
2601                fl.write('  > SWAP_ATOMS :: %d %d %.2f\n'%(at1,at2,swap[2]))
2602       
2603    if len(RMCPdict['FxCN']):
2604        fl.write('FIXED_COORDINATION_CONSTRAINTS ::  %d\n'%len(RMCPdict['FxCN']))       
2605        for ifx,fxcn in enumerate(RMCPdict['FxCN']):
2606            try:
2607                at1 = Atseq.index(fxcn[0])
2608                at2 = Atseq.index(fxcn[1])
2609            except ValueError:
2610                break
2611            fl.write('  > CSTR%d ::   %d %d %.2f %.2f %.2f %.2f %.6f\n'%(ifx+1,at1+1,at2+1,fxcn[2],fxcn[3],fxcn[4],fxcn[5],fxcn[6]))
2612    if len(RMCPdict['AveCN']):
2613        fl.write('AVERAGE_COORDINATION_CONSTRAINTS ::  %d\n'%len(RMCPdict['AveCN']))
2614        for iav,avcn in enumerate(RMCPdict['AveCN']):
2615            try:
2616                at1 = Atseq.index(avcn[0])
2617                at2 = Atseq.index(avcn[1])
2618            except ValueError:
2619                break
2620            fl.write('  > CAVSTR%d ::   %d %d %.2f %.2f %.2f %.6f\n'%(iav+1,at1+1,at2+1,avcn[2],avcn[3],avcn[4],avcn[5]))
2621    for File in Files:
2622        if Files[File][0] and Files[File][0] != 'Select':
2623            if 'Xray' in File and 'F(Q)' in File:
2624                fqdata = open(Files[File][0],'r')
2625                lines = int(fqdata.readline()[:-1])
2626            fl.write('\n')
2627            fl.write('%s ::\n'%File.split(';')[0].upper().replace(' ','_'))
2628            fl.write('  > FILENAME :: %s\n'%Files[File][0])
2629            fl.write('  > DATA_TYPE :: %s\n'%Files[File][2])
2630            fl.write('  > FIT_TYPE :: %s\n'%Files[File][2])
2631            if 'Xray' not in File:
2632                fl.write('  > START_POINT :: 1\n')
2633                fl.write('  > END_POINT :: 3000\n')
2634                fl.write('  > WEIGHT :: %.4f\n'%Files[File][1])
2635            fl.write('  > CONSTANT_OFFSET 0.000\n')
2636            fl.write('  > NO_FITTED_OFFSET\n')
2637            if RMCPdict['FitScale']:
2638                fl.write('  > FITTED_SCALE\n')
2639            else:
2640                fl.write('  > NO_FITTED_SCALE\n')
2641            if Files[File][3] !='RMC':
2642                fl.write('  > %s\n'%Files[File][3])
2643            if 'reciprocal' in File:
2644                fl.write('  > CONVOLVE ::\n')
2645                if 'Xray' in File:
2646                    fl.write('  > RECIPROCAL_SPACE_FIT :: 1 %d 1\n'%lines)
2647                    fl.write('  > RECIPROCAL_SPACE_PARAMETERS :: 1 %d %.4f\n'%(lines,Files[File][1]))
2648                    fl.write('  > REAL_SPACE_FIT :: 1 %d 1\n'%(3*lines//2))
2649                    fl.write('  > REAL_SPACE_PARAMETERS :: 1 %d %.4f\n'%(3*lines//2,1./Files[File][1]))
2650    fl.write('\n')
2651    fl.write('BRAGG ::\n')
2652    fl.write('  > BRAGG_SHAPE :: %s\n'%gsasType)
2653    fl.write('  > RECALCUATE\n')
2654    fl.write('  > DMIN :: %.2f\n'%(dMin-0.02))
2655    fl.write('  > WEIGHT :: %10.3f\n'%BraggWt)
2656    fl.write('\n')
2657    fl.write('END  ::\n')
2658    fl.close()
2659    return fname
2660
2661# def FindBonds(Phase,RMCPdict):
2662#     generalData = Phase['General']
2663#     cx,ct,cs,cia = generalData['AtomPtrs']
2664#     atomData = Phase['Atoms']
2665#     Res = 'RMC'
2666#     if 'macro' in generalData['Type']:
2667#         Res = atomData[0][ct-3]
2668#     AtDict = {atom[ct-1]:atom[ct] for atom in atomData}
2669#     Pairs = RMCPdict['Pairs']   #dict!
2670#     BondList = []
2671#     notNames = []
2672#     for FrstName in AtDict:
2673#         nbrs = G2mth.FindAllNeighbors(Phase,FrstName,list(AtDict.keys()),notName=notNames,Short=True)[0]
2674#         Atyp1 = AtDict[FrstName]
2675#         if 'Va' in Atyp1:
2676#             continue
2677#         for nbr in nbrs:
2678#             Atyp2 = AtDict[nbr[0]]
2679#             if 'Va' in Atyp2:
2680#                 continue
2681#             try:
2682#                 bndData = Pairs[' %s-%s'%(Atyp1,Atyp2)][1:]
2683#             except KeyError:
2684#                 bndData = Pairs[' %s-%s'%(Atyp2,Atyp1)][1:]
2685#             if any(bndData):
2686#                 if bndData[0] <= nbr[1] <= bndData[1]:
2687#                     bondStr = str((FrstName,nbr[0])+tuple(bndData))+',\n'
2688#                     revbondStr = str((nbr[0],FrstName)+tuple(bndData))+',\n'
2689#                     if bondStr not in BondList and revbondStr not in BondList:
2690#                         BondList.append(bondStr)
2691#         notNames.append(FrstName)
2692#     return Res,BondList
2693
2694# def FindAngles(Phase,RMCPdict):
2695#     generalData = Phase['General']
2696#     Cell = generalData['Cell'][1:7]
2697#     Amat = G2lat.cell2AB(Cell)[0]
2698#     cx,ct,cs,cia = generalData['AtomPtrs']
2699#     atomData = Phase['Atoms']
2700#     AtLookup = G2mth.FillAtomLookUp(atomData,cia+8)
2701#     AtDict = {atom[ct-1]:atom[ct] for atom in atomData}
2702#     Angles = RMCPdict['Angles']
2703#     AngDict = {'%s-%s-%s'%(angle[0],angle[1],angle[2]):angle[3:] for angle in Angles}
2704#     AngleList = []
2705#     for MidName in AtDict:
2706#         nbrs,nbrIds = G2mth.FindAllNeighbors(Phase,MidName,list(AtDict.keys()),Short=True)
2707#         if len(nbrs) < 2: #need 2 neighbors to make an angle
2708#             continue
2709#         Atyp2 = AtDict[MidName]
2710#         for i,nbr1 in enumerate(nbrs):
2711#             Atyp1 = AtDict[nbr1[0]]
2712#             for j,nbr3 in enumerate(nbrs[i+1:]):
2713#                 Atyp3 = AtDict[nbr3[0]]
2714#                 IdList = [nbrIds[1][i],nbrIds[0],nbrIds[1][i+j+1]]
2715#                 try:
2716#                     angData = AngDict['%s-%s-%s'%(Atyp1,Atyp2,Atyp3)]
2717#                 except KeyError:
2718#                     try:
2719#                         angData = AngDict['%s-%s-%s'%(Atyp3,Atyp2,Atyp1)]
2720#                     except KeyError:
2721#                         continue
2722#                 XYZ = np.array(G2mth.GetAtomItemsById(atomData,AtLookup,IdList,cx,numItems=3))
2723#                 calAngle = G2mth.getRestAngle(XYZ,Amat)
2724#                 if angData[0] <= calAngle <= angData[1]:
2725#                     angStr = str((MidName,nbr1[0],nbr3[0])+tuple(angData))+',\n'
2726#                     revangStr = str((MidName,nbr3[0],nbr1[0])+tuple(angData))+',\n'
2727#                     if angStr not in AngleList and revangStr not in AngleList:
2728#                         AngleList.append(angStr)
2729#     return AngleList
2730
2731# def GetSqConvolution(XY,d):
2732
2733#     n = XY.shape[1]
2734#     snew = np.zeros(n)
2735#     dq = np.zeros(n)
2736#     sold = XY[1]
2737#     q = XY[0]
2738#     dq[1:] = np.diff(q)
2739#     dq[0] = dq[1]
2740   
2741#     for j in range(n):
2742#         for i in range(n):
2743#             b = abs(q[i]-q[j])
2744#             t = q[i]+q[j]
2745#             if j == i:
2746#                 snew[j] += q[i]*sold[i]*(d-np.sin(t*d)/t)*dq[i]
2747#             else:
2748#                 snew[j] += q[i]*sold[i]*(np.sin(b*d)/b-np.sin(t*d)/t)*dq[i]
2749#         snew[j] /= np.pi*q[j]
2750   
2751#     snew[0] = snew[1]
2752#     return snew
2753
2754# def GetMaxSphere(pdbName):
2755#     try:
2756#         pFil = open(pdbName,'r')
2757#     except FileNotFoundError:
2758#         return None
2759#     while True:
2760#         line = pFil.readline()
2761#         if 'Boundary' in line:
2762#             line = line.split()[3:]
2763#             G = np.array([float(item) for item in line])
2764#             G = np.reshape(G,(3,3))**2
2765#             G = nl.inv(G)
2766#             pFil.close()
2767#             break
2768#     dspaces = [0.5/np.sqrt(G2lat.calc_rDsq2(H,G)) for H in np.eye(3)]
2769#     return min(dspaces)
2770   
2771def MakefullrmcRun(pName,Phase,RMCPdict):
2772    BondList = {}
2773    for k in RMCPdict['Pairs']:
2774        if RMCPdict['Pairs'][k][1]+RMCPdict['Pairs'][k][2]>0:
2775            BondList[k] = (RMCPdict['Pairs'][k][1],RMCPdict['Pairs'][k][2])
2776    AngleList = []
2777    for angle in RMCPdict['Angles']:
2778        if angle[3] == angle[4] or angle[5] >= angle[6] or angle[6] <= 0:
2779            continue
2780        for i in (0,1,2):
2781            angle[i] = angle[i].strip()
2782        AngleList.append(angle)
2783    rmin = RMCPdict['min Contact']
2784    cell = Phase['General']['Cell'][1:7]
2785    SymOpList = G2spc.AllOps(Phase['General']['SGData'])[0]
2786    cx,ct,cs,cia = Phase['General']['AtomPtrs']
2787    atomsList = []
2788    for atom in Phase['Atoms']:
2789        el = ''.join([i for i in atom[ct] if i.isalpha()])
2790        atomsList.append([el] + atom[cx:cx+4])
2791    rname = pName+'-fullrmc.py'
2792    restart = '%s_restart.pdb'%pName
2793    Files = RMCPdict['files']
2794    rundata = ''
2795    rundata += '#### fullrmc %s file; edit by hand if you so choose #####\n'%rname
2796    rundata += '# created in '+__file__+" v"+filversion.split()[1]
2797    rundata += dt.datetime.strftime(dt.datetime.now()," at %Y-%m-%dT%H:%M\n")
2798    rundata += '''
2799# fullrmc imports (all that are potentially useful)
2800import os,glob
2801import time
2802#import matplotlib.pyplot as plt
2803import numpy as np
2804from fullrmc.Core import Collection
2805from fullrmc.Engine import Engine
2806import fullrmc.Constraints.PairDistributionConstraints as fPDF
2807from fullrmc.Constraints.StructureFactorConstraints import ReducedStructureFactorConstraint, StructureFactorConstraint
2808from fullrmc.Constraints.DistanceConstraints import DistanceConstraint
2809from fullrmc.Constraints.BondConstraints import BondConstraint
2810from fullrmc.Constraints.AngleConstraints import BondsAngleConstraint
2811from fullrmc.Constraints.DihedralAngleConstraints import DihedralAngleConstraint
2812from fullrmc.Generators.Swaps import SwapPositionsGenerator
2813
2814### When True, erases an existing enging to provide a fresh start
2815FRESH_START = {}
2816time0 = time.time()
2817'''.format(RMCPdict['ReStart'][0])
2818   
2819    rundata += '# setup structure\n'
2820    rundata += 'cell = ' + str(cell) + '\n'
2821    rundata += "SymOpList = "+str([i.lower() for i in SymOpList]) + '\n'
2822    rundata += 'atomList = ' + str(atomsList).replace('],','],\n  ') + '\n'
2823    rundata += 'supercell = ' + str(RMCPdict['SuperCell']) + '\n'
2824
2825    rundata += '\n# initialize engine\n'
2826    rundata += 'engineFileName = "%s.rmc"\n'%pName
2827
2828    rundata += '''\n# check Engine exists if so (and not FRESH_START) load it
2829# otherwise build it
2830ENGINE = Engine(path=None)
2831if not ENGINE.is_engine(engineFileName) or FRESH_START:
2832    ## create structure
2833    ENGINE = Engine(path=engineFileName, freshStart=True)
2834    ENGINE.build_crystal_set_pdb(symOps     = SymOpList,
2835                                 atoms      = atomList,
2836                                 unitcellBC = cell,
2837                                 supercell  = supercell)
2838    rmax = min( [ENGINE.boundaryConditions.get_a(), ENGINE.boundaryConditions.get_b(), ENGINE.boundaryConditions.get_c()] ) /2.
2839'''   
2840    import atmdata
2841    rundata += '# conversion factors (may be needed)\n'
2842    rundata += '    sumCiBi2 = 0.\n'
2843    for elem in Phase['General']['AtomTypes']:
2844        rundata += '    Ci = ENGINE.numberOfAtomsPerElement["{}"]/len(ENGINE.allElements)\n'.format(elem)
2845        rundata += '    sumCiBi2 += (Ci*{})**2\n'.format(atmdata.AtmBlens[elem+'_']['SL'][0])
2846    rundata += '    rho0 = len(ENGINE.allNames)/ENGINE.volume\n'
2847    # settings that require a new Engine
2848    for File in Files:
2849        filDat = RMCPdict['files'][File]
2850        if not os.path.exists(filDat[0]): continue
2851        sfwt = 'neutronCohb'
2852        if 'Xray' in File:
2853            sfwt = 'atomicNumber'
2854        if 'G(r)' in File:
2855            rundata += '    GR = np.loadtxt("%s").T\n'%filDat[0]
2856            if filDat[3] == 0:
2857                rundata += '''    # read and xform G(r) as defined in RMCProfile
2858    # see eq. 44 in Keen, J. Appl. Cryst. (2001) 34 172-177\n'''
2859                rundata += '    GR[1] *= 4 * np.pi * GR[0] * rho0 / sumCiBi2\n'
2860                rundata += '    GofR = fPDF.PairDistributionConstraint(experimentalData=GR.T, weighting="%s")\n'%sfwt
2861            elif filDat[3] == 1:
2862                rundata += '    # This is G(r) as defined in PDFFIT\n'
2863                rundata += '    GofR = fPDF.PairDistributionConstraint(experimentalData=GR.T, weighting="%s")\n'%sfwt
2864            elif filDat[3] == 2:
2865                rundata += '    # This is g(r)\n'
2866                rundata += '    GofR = fPDF.PairCorrelationConstraint(experimentalData=GR.T, weighting="%s")\n'%sfwt
2867            else:
2868                raise ValueError('Invalid G(r) type: '+str(filDat[3]))
2869            rundata += '    ENGINE.add_constraints([GofR])\n'
2870            rundata += '    GofR.set_limits((None, rmax))\n'
2871        elif '(Q)' in File:
2872            rundata += '    SOQ = np.loadtxt("%s").T\n'%filDat[0]
2873            if filDat[3] == 0:
2874                rundata += '    # Read & xform F(Q) as defined in RMCProfile to S(Q)-1\n'
2875                rundata += '    SOQ[1] *= 1 / sumCiBi2\n'
2876            elif filDat[3] == 1:
2877                rundata += '    # This is S(Q) as defined in PDFFIT\n'
2878                rundata += '    SOQ[1] -= 1\n'
2879            if filDat[4]:
2880                rundata += '    SOQ[1] = Collection.sinc_convolution(q=SOQ[0],sq=SOQ[1],rmax=rmax)\n'
2881            rundata += '    SofQ = ReducedStructureFactorConstraint(experimentalData=SOQ.T, weighting="%s")\n'%sfwt
2882            rundata += '    ENGINE.add_constraints([SofQ])\n'
2883        else:
2884            print('What is this?')
2885    rundata += '    ENGINE.add_constraints(DistanceConstraint(defaultLowerDistance={}))\n'.format(RMCPdict['min Contact'])
2886    if BondList:
2887        rundata += '''    B_CONSTRAINT   = BondConstraint()
2888    ENGINE.add_constraints(B_CONSTRAINT)
2889    B_CONSTRAINT.create_supercell_bonds(bondsDefinition=[
2890'''
2891        for pair in BondList:
2892            e1,e2 = pair.split('-')
2893            rundata += '            ("element","{}","{}",{},{}),\n'.format(
2894                                        e1.strip(),e2.strip(),*BondList[pair])
2895        rundata += '             ])\n'
2896    if AngleList:
2897        rundata += '''    A_CONSTRAINT   = BondsAngleConstraint()
2898    ENGINE.add_constraints(A_CONSTRAINT)
2899    A_CONSTRAINT.create_supercell_angles(anglesDefinition=[
2900'''
2901        for item in AngleList:
2902            rundata += ('            '+
2903               '("element","{1}","{0}","{2}",{5},{6},{5},{6},{3},{4}),\n'.format(*item))
2904        rundata += '             ])\n'
2905    rundata += '    for f in glob.glob("{}_*.log"): os.remove(f)\n'.format(pName)
2906    rundata += '''
2907    ENGINE.save()
2908else:
2909    ENGINE = ENGINE.load(path=engineFileName)
2910'''
2911    rundata += 'ENGINE.set_log_file("{}")\n'.format(pName)
2912    if RMCPdict['Swaps']:
2913        rundata += '\n#set up for site swaps\n'
2914        rundata += 'aN = ENGINE.allNames\n'
2915        rundata += 'SwapGen = {}\n'
2916        for swap in RMCPdict['Swaps']:
2917            rundata += 'SwapA = [[idx] for idx in range(len(aN)) if aN[idx]=="%s"]\n'%swap[0]
2918            rundata += 'SwapB = [[idx] for idx in range(len(aN)) if aN[idx]=="%s"]\n'%swap[1]
2919            rundata += 'SwapGen["%s-%s"] = [SwapPositionsGenerator(swapList=SwapA),SwapPositionsGenerator(swapList=SwapB),%.2f]\n'%(swap[0],swap[1],swap[2])
2920        rundata += '    for swaps in SwapGen:\n'
2921        rundata += '        AB = swaps.split("-")\n'
2922        rundata += '        ENGINE.set_groups_as_atoms()\n'
2923        rundata += '        for g in ENGINE.groups:\n'
2924        rundata += '            if aN[g.indexes[0]]==AB[0]:\n'
2925        rundata += '                g.set_move_generator(SwapGen[swaps][0])\n'
2926        rundata += '            elif aN[g.indexes[0]]==AB[1]:\n'
2927        rundata += '                g.set_move_generator(SwapGen[swaps][1])\n'
2928        rundata += '            sProb = SwapGen[swaps][2]\n'
2929    rundata += '\n# set weights -- do this now so values can be changed without a restart\n'
2930    rundata += 'wtDict = {}\n'
2931    for File in Files:
2932        filDat = RMCPdict['files'][File]
2933        if not os.path.exists(filDat[0]): continue
2934        if 'Xray' in File:
2935            sfwt = 'atomicNumber'
2936        else:
2937            sfwt = 'neutronCohb'
2938        if 'G(r)' in File:
2939            typ = 'Pair'
2940        elif '(Q)' in File:
2941            typ = 'Struct'
2942        rundata += 'wtDict["{}-{}"] = {}\n'.format(typ,sfwt,filDat[1])
2943    rundata += 'for c in ENGINE.constraints:  # loop over predefined constraints\n'
2944    rundata += '    if type(c) is fPDF.PairDistributionConstraint:\n'
2945    rundata += '        c.set_variance_squared(1./wtDict["Pair-"+c.weighting])\n'
2946    rundata += '        c.set_limits((None,rmax))\n'
2947    if RMCPdict['FitScale']:
2948        rundata += '        c.set_adjust_scale_factor((10, 0.01, 100.))\n'
2949    rundata += '    elif type(c) is ReducedStructureFactorConstraint:\n'
2950    rundata += '        c.set_variance_squared(1./wtDict["Struct-"+c.weighting])\n'
2951    if RMCPdict['FitScale']:
2952        rundata += '        c.set_adjust_scale_factor((10, 0.01, 100.))\n'
2953    # torsions difficult to implement, must be internal to cell & named with
2954    # fullrmc atom names
2955    # if len(RMCPdict['Torsions']):         # Torsions currently commented out in GUI
2956    #     rundata += 'for c in ENGINE.constraints:  # look for Dihedral Angle Constraints\n'
2957    #     rundata += '    if type(c) is DihedralAngleConstraint:\n'
2958    #     rundata += '        c.set_variance_squared(%f)\n'%RMCPdict['Torsion Weight']
2959    #     rundata += '        c.create_angles_by_definition(anglesDefinition={"%s":[\n'%Res
2960    #     for torsion in RMCPdict['Torsions']:
2961    #         rundata += '    %s\n'%str(tuple(torsion))
2962    #     rundata += '        ]})\n'           
2963    rundata += '\n# setup runs for fullrmc\n'
2964
2965    rundata += 'steps = 10000\n'
2966    rundata += 'for _ in range({}):\n'.format(RMCPdict['Cycles'])
2967    rundata += '    ENGINE.set_groups_as_atoms()\n'
2968    rundata += '    expected = ENGINE.generated+steps\n'
2969   
2970    rundata += '    ENGINE.run(restartPdb="%s",numberOfSteps=steps, saveFrequency=1000)\n'%restart
2971    rundata += '    if ENGINE.generated != expected: break # run was stopped\n'
2972    rundata += 'print("ENGINE run time %.2f s"%(time.time()-time0))\n'
2973    rfile = open(rname,'w')
2974    rfile.writelines(rundata)
2975    rfile.close()
2976    return rname
2977   
2978def GetRMCBonds(general,RMCPdict,Atoms,bondList):
2979    bondDist = []
2980    Cell = general['Cell'][1:7]
2981    Supercell =  RMCPdict['SuperCell']
2982    Trans = np.eye(3)*np.array(Supercell)
2983    Cell = G2lat.TransformCell(Cell,Trans)[:6]
2984    Amat,Bmat = G2lat.cell2AB(Cell)
2985    indices = (-1,0,1)
2986    Units = np.array([[h,k,l] for h in indices for k in indices for l in indices])
2987    for bonds in bondList:
2988        Oxyz = np.array(Atoms[bonds[0]][1:])
2989        Txyz = np.array([Atoms[tgt-1][1:] for tgt in bonds[1]])       
2990        Dx = np.array([Txyz-Oxyz+unit for unit in Units])
2991        Dx = np.sqrt(np.sum(np.inner(Dx,Amat)**2,axis=2))
2992        for dx in Dx.T:
2993            bondDist.append(np.min(dx))
2994    return np.array(bondDist)
2995   
2996def GetRMCAngles(general,RMCPdict,Atoms,angleList):
2997    bondAngles = []
2998    Cell = general['Cell'][1:7]
2999    Supercell =  RMCPdict['SuperCell']
3000    Trans = np.eye(3)*np.array(Supercell)
3001    Cell = G2lat.TransformCell(Cell,Trans)[:6]
3002    Amat,Bmat = G2lat.cell2AB(Cell)
3003    indices = (-1,0,1)
3004    Units = np.array([[h,k,l] for h in indices for k in indices for l in indices])
3005    for angle in angleList:
3006        Oxyz = np.array(Atoms[angle[0]][1:])
3007        TAxyz = np.array([Atoms[tgt-1][1:] for tgt in angle[1].T[0]])       
3008        TBxyz = np.array([Atoms[tgt-1][1:] for tgt in angle[1].T[1]])       
3009        DAxV = np.inner(np.array([TAxyz-Oxyz+unit for unit in Units]),Amat)
3010        DAx = np.sqrt(np.sum(DAxV**2,axis=2))
3011        DBxV = np.inner(np.array([TBxyz-Oxyz+unit for unit in Units]),Amat)
3012        DBx = np.sqrt(np.sum(DBxV**2,axis=2))
3013        iDAx = np.argmin(DAx,axis=0)
3014        iDBx = np.argmin(DBx,axis=0)
3015        for i,[iA,iB] in enumerate(zip(iDAx,iDBx)):
3016            DAv = DAxV[iA,i]/DAx[iA,i]
3017            DBv = DBxV[iB,i]/DBx[iB,i]
3018            bondAngles.append(npacosd(np.sum(DAv*DBv)))
3019    return np.array(bondAngles)
3020   
3021################################################################################
3022#### Reflectometry calculations
3023################################################################################
3024
3025def REFDRefine(Profile,ProfDict,Inst,Limits,Substances,data):
3026    G2fil.G2Print ('fit REFD data by '+data['Minimizer']+' using %.2f%% data resolution'%(data['Resolution'][0]))
3027   
3028    class RandomDisplacementBounds(object):
3029        """random displacement with bounds"""
3030        def __init__(self, xmin, xmax, stepsize=0.5):
3031            self.xmin = xmin
3032            self.xmax = xmax
3033            self.stepsize = stepsize
3034   
3035        def __call__(self, x):
3036            """take a random step but ensure the new position is within the bounds"""
3037            while True:
3038                # this could be done in a much more clever way, but it will work for example purposes
3039                steps = self.xmax-self.xmin
3040                xnew = x + np.random.uniform(-self.stepsize*steps, self.stepsize*steps, np.shape(x))
3041                if np.all(xnew < self.xmax) and np.all(xnew > self.xmin):
3042                    break
3043            return xnew
3044   
3045    def GetModelParms():
3046        parmDict = {}
3047        varyList = []
3048        values = []
3049        bounds = []
3050        parmDict['dQ type'] = data['dQ type']
3051        parmDict['Res'] = data['Resolution'][0]/(100.*sateln2)     #% FWHM-->decimal sig
3052        for parm in ['Scale','FltBack']:
3053            parmDict[parm] = data[parm][0]
3054            if data[parm][1]:
3055                varyList.append(parm)
3056                values.append(data[parm][0])
3057                bounds.append(Bounds[parm])
3058        parmDict['Layer Seq'] = np.array(['0',]+data['Layer Seq'].split()+[str(len(data['Layers'])-1),],dtype=int)
3059        parmDict['nLayers'] = len(parmDict['Layer Seq'])
3060        for ilay,layer in enumerate(data['Layers']):
3061            name = layer['Name']
3062            cid = str(ilay)+';'
3063            parmDict[cid+'Name'] = name
3064            for parm in ['Thick','Rough','DenMul','Mag SLD','iDenMul']:
3065                parmDict[cid+parm] = layer.get(parm,[0.,False])[0]
3066                if layer.get(parm,[0.,False])[1]:
3067                    varyList.append(cid+parm)
3068                    value = layer[parm][0]
3069                    values.append(value)
3070                    if value:
3071                        bound = [value*Bfac,value/Bfac]
3072                    else:
3073                        bound = [0.,10.]
3074                    bounds.append(bound)
3075            if name not in ['vacuum','unit scatter']:
3076                parmDict[cid+'rho'] = Substances[name]['Scatt density']
3077                parmDict[cid+'irho'] = Substances[name].get('XImag density',0.)
3078        return parmDict,varyList,values,bounds
3079   
3080    def SetModelParms():
3081        line = ' Refined parameters: Histogram scale: %.4g'%(parmDict['Scale'])
3082        if 'Scale' in varyList:
3083            data['Scale'][0] = parmDict['Scale']
3084            line += ' esd: %.4g'%(sigDict['Scale'])                                                             
3085        G2fil.G2Print (line)
3086        line = ' Flat background: %15.4g'%(parmDict['FltBack'])
3087        if 'FltBack' in varyList:
3088            data['FltBack'][0] = parmDict['FltBack']
3089            line += ' esd: %15.3g'%(sigDict['FltBack'])
3090        G2fil.G2Print (line)
3091        for ilay,layer in enumerate(data['Layers']):
3092            name = layer['Name']
3093            G2fil.G2Print (' Parameters for layer: %d %s'%(ilay,name))
3094            cid = str(ilay)+';'
3095            line = ' '
3096            line2 = ' Scattering density: Real %.5g'%(Substances[name]['Scatt density']*parmDict[cid+'DenMul'])
3097            line2 += ' Imag %.5g'%(Substances[name].get('XImag density',0.)*parmDict[cid+'DenMul'])
3098            for parm in ['Thick','Rough','DenMul','Mag SLD','iDenMul']:
3099                if parm in layer:
3100                    if parm == 'Rough':
3101                        layer[parm][0] = abs(parmDict[cid+parm])    #make positive
3102                    else:
3103                        layer[parm][0] = parmDict[cid+parm]
3104                    line += ' %s: %.3f'%(parm,layer[parm][0])
3105                    if cid+parm in varyList:
3106                        line += ' esd: %.3g'%(sigDict[cid+parm])
3107            G2fil.G2Print (line)
3108            G2fil.G2Print (line2)
3109   
3110    def calcREFD(values,Q,Io,wt,Qsig,parmDict,varyList):
3111        parmDict.update(zip(varyList,values))
3112        M = np.sqrt(wt)*(getREFD(Q,Qsig,parmDict)-Io)
3113        return M
3114   
3115    def sumREFD(values,Q,Io,wt,Qsig,parmDict,varyList):
3116        parmDict.update(zip(varyList,values))
3117        M = np.sqrt(wt)*(getREFD(Q,Qsig,parmDict)-Io)
3118        return np.sum(M**2)
3119   
3120    def getREFD(Q,Qsig,parmDict):
3121        Ic = np.ones_like(Q)*parmDict['FltBack']
3122        Scale = parmDict['Scale']
3123        Nlayers = parmDict['nLayers']
3124        Res = parmDict['Res']
3125        depth = np.zeros(Nlayers)
3126        rho = np.zeros(Nlayers)
3127        irho = np.zeros(Nlayers)
3128        sigma = np.zeros(Nlayers)
3129        for ilay,lay in enumerate(parmDict['Layer Seq']):
3130            cid = str(lay)+';'
3131            depth[ilay] = parmDict[cid+'Thick']
3132            sigma[ilay] = parmDict[cid+'Rough']
3133            if parmDict[cid+'Name'] == u'unit scatter':
3134                rho[ilay] = parmDict[cid+'DenMul']
3135                irho[ilay] = parmDict[cid+'iDenMul']
3136            elif 'vacuum' != parmDict[cid+'Name']:
3137                rho[ilay] = parmDict[cid+'rho']*parmDict[cid+'DenMul']
3138                irho[ilay] = parmDict[cid+'irho']*parmDict[cid+'DenMul']
3139            if cid+'Mag SLD' in parmDict:
3140                rho[ilay] += parmDict[cid+'Mag SLD']
3141        if parmDict['dQ type'] == 'None':
3142            AB = abeles(0.5*Q,depth,rho,irho,sigma[1:])     #Q --> k, offset roughness for abeles
3143        elif 'const' in parmDict['dQ type']:
3144            AB = SmearAbeles(0.5*Q,Q*Res,depth,rho,irho,sigma[1:])
3145        else:       #dQ/Q in data
3146            AB = SmearAbeles(0.5*Q,Qsig,depth,rho,irho,sigma[1:])
3147        Ic += AB*Scale
3148        return Ic
3149       
3150    def estimateT0(takestep):
3151        Mmax = -1.e-10
3152        Mmin = 1.e10
3153        for i in range(100):
3154            x0 = takestep(values)
3155            M = sumREFD(x0,Q[Ibeg:Ifin],Io[Ibeg:Ifin],wtFactor*wt[Ibeg:Ifin],Qsig[Ibeg:Ifin],parmDict,varyList)
3156            Mmin = min(M,Mmin)
3157            MMax = max(M,Mmax)
3158        return 1.5*(MMax-Mmin)
3159
3160    Q,Io,wt,Ic,Ib,Qsig = Profile[:6]
3161    if data.get('2% weight'):
3162        wt = 1./(0.02*Io)**2
3163    Qmin = Limits[1][0]
3164    Qmax = Limits[1][1]
3165    wtFactor = ProfDict['wtFactor']
3166    Bfac = data['Toler']
3167    Ibeg = np.searchsorted(Q,Qmin)
3168    Ifin = np.searchsorted(Q,Qmax)+1    #include last point
3169    Ic[:] = 0
3170    Bounds = {'Scale':[data['Scale'][0]*Bfac,data['Scale'][0]/Bfac],'FltBack':[0.,1.e-6],
3171              'DenMul':[0.,1.],'Thick':[1.,500.],'Rough':[0.,10.],'Mag SLD':[-10.,10.],'iDenMul':[-1.,1.]}
3172    parmDict,varyList,values,bounds = GetModelParms()
3173    Msg = 'Failed to converge'
3174    if varyList:
3175        if data['Minimizer'] == 'LMLS': 
3176            result = so.leastsq(calcREFD,values,full_output=True,epsfcn=1.e-8,ftol=1.e-6,
3177                args=(Q[Ibeg:Ifin],Io[Ibeg:Ifin],wtFactor*wt[Ibeg:Ifin],Qsig[Ibeg:Ifin],parmDict,varyList))
3178            parmDict.update(zip(varyList,result[0]))
3179            chisq = np.sum(result[2]['fvec']**2)
3180            ncalc = result[2]['nfev']
3181            covM = result[1]
3182            newVals = result[0]
3183        elif data['Minimizer'] == 'Basin Hopping':
3184            xyrng = np.array(bounds).T
3185            take_step = RandomDisplacementBounds(xyrng[0], xyrng[1])
3186            T0 = estimateT0(take_step)
3187            G2fil.G2Print (' Estimated temperature: %.3g'%(T0))
3188            result = so.basinhopping(sumREFD,values,take_step=take_step,disp=True,T=T0,stepsize=Bfac,
3189                interval=20,niter=200,minimizer_kwargs={'method':'L-BFGS-B','bounds':bounds,
3190                'args':(Q[Ibeg:Ifin],Io[Ibeg:Ifin],wtFactor*wt[Ibeg:Ifin],Qsig[Ibeg:Ifin],parmDict,varyList)})
3191            chisq = result.fun
3192            ncalc = result.nfev
3193            newVals = result.x
3194            covM = []
3195        elif data['Minimizer'] == 'MC/SA Anneal':
3196            xyrng = np.array(bounds).T
3197            result = G2mth.anneal(sumREFD, values, 
3198                args=(Q[Ibeg:Ifin],Io[Ibeg:Ifin],wtFactor*wt[Ibeg:Ifin],Qsig[Ibeg:Ifin],parmDict,varyList),
3199                schedule='log', full_output=True,maxeval=None, maxaccept=None, maxiter=10,dwell=1000,
3200                boltzmann=10.0, feps=1e-6,lower=xyrng[0], upper=xyrng[1], slope=0.9,ranStart=True,
3201                ranRange=0.20,autoRan=False,dlg=None)
3202            newVals = result[0]
3203            parmDict.update(zip(varyList,newVals))
3204            chisq = result[1]
3205            ncalc = result[3]
3206            covM = []
3207            G2fil.G2Print (' MC/SA final temperature: %.4g'%(result[2]))
3208        elif data['Minimizer'] == 'L-BFGS-B':
3209            result = so.minimize(sumREFD,values,method='L-BFGS-B',bounds=bounds,   #ftol=Ftol,
3210                args=(Q[Ibeg:Ifin],Io[Ibeg:Ifin],wtFactor*wt[Ibeg:Ifin],Qsig[Ibeg:Ifin],parmDict,varyList))
3211            parmDict.update(zip(varyList,result['x']))
3212            chisq = result.fun
3213            ncalc = result.nfev
3214            newVals = result.x
3215            covM = []
3216    else:   #nothing varied
3217        M = calcREFD(values,Q[Ibeg:Ifin],Io[Ibeg:Ifin],wtFactor*wt[Ibeg:Ifin],Qsig[Ibeg:Ifin],parmDict,varyList)
3218        chisq = np.sum(M**2)
3219        ncalc = 0
3220        covM = []
3221        sig = []
3222        sigDict = {}
3223        result = []
3224    Rvals = {}
3225    Rvals['Rwp'] = np.sqrt(chisq/np.sum(wt[Ibeg:Ifin]*Io[Ibeg:Ifin]**2))*100.      #to %
3226    Rvals['GOF'] = chisq/(Ifin-Ibeg-len(varyList))       #reduced chi^2
3227    Ic[Ibeg:Ifin] = getREFD(Q[Ibeg:Ifin],Qsig[Ibeg:Ifin],parmDict)
3228    Ib[Ibeg:Ifin] = parmDict['FltBack']
3229    try:
3230        if not len(varyList):
3231            Msg += ' - nothing refined'
3232            raise ValueError
3233        Nans = np.isnan(newVals)
3234        if np.any(Nans):
3235            name = varyList[Nans.nonzero(True)[0]]
3236            Msg += ' Nan result for '+name+'!'
3237            raise ValueError
3238        Negs = np.less_equal(newVals,0.)
3239        if np.any(Negs):
3240            indx = Negs.nonzero()
3241            name = varyList[indx[0][0]]
3242            if name != 'FltBack' and name.split(';')[1] in ['Thick',]:
3243                Msg += ' negative coefficient for '+name+'!'
3244                raise ValueError
3245        if len(covM):
3246            sig = np.sqrt(np.diag(covM)*Rvals['GOF'])
3247            covMatrix = covM*Rvals['GOF']
3248        else:
3249            sig = np.zeros(len(varyList))
3250            covMatrix = []
3251        sigDict = dict(zip(varyList,sig))
3252        G2fil.G2Print (' Results of reflectometry data modelling fit:')
3253        G2fil.G2Print ('Number of function calls: %d Number of observations: %d Number of parameters: %d'%(ncalc,Ifin-Ibeg,len(varyList)))
3254        G2fil.G2Print ('Rwp = %7.2f%%, chi**2 = %12.6g, reduced chi**2 = %6.2f'%(Rvals['Rwp'],chisq,Rvals['GOF']))
3255        SetModelParms()
3256        return True,result,varyList,sig,Rvals,covMatrix,parmDict,''
3257    except (ValueError,TypeError):      #when bad LS refinement; covM missing or with nans
3258        G2fil.G2Print (Msg)
3259        return False,0,0,0,0,0,0,Msg
3260       
3261def makeSLDprofile(data,Substances):
3262   
3263    sq2 = np.sqrt(2.)
3264    laySeq = ['0',]+data['Layer Seq'].split()+[str(len(data['Layers'])-1),]
3265    Nlayers = len(laySeq)
3266    laySeq = np.array(laySeq,dtype=int)
3267    interfaces = np.zeros(Nlayers)
3268    rho = np.zeros(Nlayers)
3269    sigma = np.zeros(Nlayers)
3270    name = data['Layers'][0]['Name']
3271    thick = 0.
3272    for ilay,lay in enumerate(laySeq):
3273        layer = data['Layers'][lay]
3274        name = layer['Name']
3275        if 'Thick' in layer:
3276            thick += layer['Thick'][0]
3277            interfaces[ilay] = layer['Thick'][0]+interfaces[ilay-1]
3278        if 'Rough' in layer:
3279            sigma[ilay] = max(0.001,layer['Rough'][0])
3280        if name != 'vacuum':
3281            if name == 'unit scatter':
3282                rho[ilay] = np.sqrt(layer['DenMul'][0]**2+layer['iDenMul'][0]**2)
3283            else:
3284                rrho = Substances[name]['Scatt density']
3285                irho = Substances[name]['XImag density']
3286                rho[ilay] = np.sqrt(rrho**2+irho**2)*layer['DenMul'][0]
3287        if 'Mag SLD' in layer:
3288            rho[ilay] += layer['Mag SLD'][0]
3289    name = data['Layers'][-1]['Name']
3290    x = np.linspace(-0.15*thick,1.15*thick,1000,endpoint=True)
3291    xr = np.flipud(x)
3292    interfaces[-1] = x[-1]
3293    y = np.ones_like(x)*rho[0]
3294    iBeg = 0
3295    for ilayer in range(Nlayers-1):
3296        delt = rho[ilayer+1]-rho[ilayer]
3297        iPos = np.searchsorted(x,interfaces[ilayer])
3298        y[iBeg:] += (delt/2.)*sp.erfc((interfaces[ilayer]-x[iBeg:])/(sq2*sigma[ilayer+1]))
3299        iBeg = iPos
3300    return x,xr,y   
3301
3302def REFDModelFxn(Profile,Inst,Limits,Substances,data):
3303   
3304    Q,Io,wt,Ic,Ib,Qsig = Profile[:6]
3305    Qmin = Limits[1][0]
3306    Qmax = Limits[1][1]
3307    iBeg = np.searchsorted(Q,Qmin)
3308    iFin = np.searchsorted(Q,Qmax)+1    #include last point
3309    Ib[:] = data['FltBack'][0]
3310    Ic[:] = 0
3311    Scale = data['Scale'][0]
3312    if data['Layer Seq'] == []:
3313        return
3314    laySeq = ['0',]+data['Layer Seq'].split()+[str(len(data['Layers'])-1),]
3315    Nlayers = len(laySeq)
3316    depth = np.zeros(Nlayers)
3317    rho = np.zeros(Nlayers)
3318    irho = np.zeros(Nlayers)
3319    sigma = np.zeros(Nlayers)
3320    for ilay,lay in enumerate(np.array(laySeq,dtype=int)):
3321        layer = data['Layers'][lay]
3322        name = layer['Name']
3323        if 'Thick' in layer:    #skips first & last layers
3324            depth[ilay] = layer['Thick'][0]
3325        if 'Rough' in layer:    #skips first layer
3326            sigma[ilay] = layer['Rough'][0]
3327        if 'unit scatter' == name:
3328            rho[ilay] = layer['DenMul'][0]
3329            irho[ilay] = layer['iDenMul'][0]
3330        else:
3331            rho[ilay] = Substances[name]['Scatt density']*layer['DenMul'][0]
3332            irho[ilay] = Substances[name].get('XImag density',0.)*layer['DenMul'][0]
3333        if 'Mag SLD' in layer:
3334            rho[ilay] += layer['Mag SLD'][0]
3335    if data['dQ type'] == 'None':
3336        AB = abeles(0.5*Q[iBeg:iFin],depth,rho,irho,sigma[1:])     #Q --> k, offset roughness for abeles
3337    elif 'const' in data['dQ type']:
3338        res = data['Resolution'][0]/(100.*sateln2)
3339        AB = SmearAbeles(0.5*Q[iBeg:iFin],res*Q[iBeg:iFin],depth,rho,irho,sigma[1:])
3340    else:       #dQ/Q in data
3341        AB = SmearAbeles(0.5*Q[iBeg:iFin],Qsig[iBeg:iFin],depth,rho,irho,sigma[1:])
3342    Ic[iBeg:iFin] = AB*Scale+Ib[iBeg:iFin]
3343
3344def abeles(kz, depth, rho, irho=0, sigma=0):
3345    """
3346    Optical matrix form of the reflectivity calculation.
3347    O.S. Heavens, Optical Properties of Thin Solid Films
3348   
3349    Reflectometry as a function of kz for a set of slabs.
3350
3351    :param kz: float[n] (1/Ang). Scattering vector, :math:`2\\pi\\sin(\\theta)/\\lambda`.
3352        This is :math:`\\tfrac12 Q_z`.       
3353    :param depth:  float[m] (Ang).
3354        thickness of each layer.  The thickness of the incident medium
3355        and substrate are ignored.
3356    :param rho:  float[n,k] (1e-6/Ang^2)
3357        Real scattering length density for each layer for each kz
3358    :param irho:  float[n,k] (1e-6/Ang^2)
3359        Imaginary scattering length density for each layer for each kz
3360        Note: absorption cross section mu = 2 irho/lambda for neutrons
3361    :param sigma: float[m-1] (Ang)
3362        interfacial roughness.  This is the roughness between a layer
3363        and the previous layer. The sigma array should have m-1 entries.
3364
3365    Slabs are ordered with the surface SLD at index 0 and substrate at
3366    index -1, or reversed if kz < 0.
3367    """
3368    def calc(kz, depth, rho, irho, sigma):
3369        if len(kz) == 0: return kz
3370   
3371        # Complex index of refraction is relative to the incident medium.
3372        # We can get the same effect using kz_rel^2 = kz^2 + 4*pi*rho_o
3373        # in place of kz^2, and ignoring rho_o
3374        kz_sq = kz**2 + 4e-6*np.pi*rho[:,0]
3375        k = kz
3376   
3377        # According to Heavens, the initial matrix should be [ 1 F; F 1],
3378        # which we do by setting B=I and M0 to [1 F; F 1].  An extra matrix
3379        # multiply versus some coding convenience.
3380        B11 = 1
3381        B22 = 1
3382        B21 = 0
3383        B12 = 0
3384        for i in range(0, len(depth)-1):
3385            k_next = np.sqrt(kz_sq - 4e-6*np.pi*(rho[:,i+1] + 1j*irho[:,i+1]))
3386            F = (k - k_next) / (k + k_next)
3387            F *= np.exp(-2*k*k_next*sigma[i]**2)
3388            #print "==== layer",i
3389            #print "kz:", kz
3390            #print "k:", k
3391            #print "k_next:",k_next
3392            #print "F:",F
3393            #print "rho:",rho[:,i+1]
3394            #print "irho:",irho[:,i+1]
3395            #print "d:",depth[i],"sigma:",sigma[i]
3396            M11 = np.exp(1j*k*depth[i]) if i>0 else 1
3397            M22 = np.exp(-1j*k*depth[i]) if i>0 else 1
3398            M21 = F*M11
3399            M12 = F*M22
3400            C1 = B11*M11 + B21*M12
3401            C2 = B11*M21 + B21*M22
3402            B11 = C1
3403            B21 = C2
3404            C1 = B12*M11 + B22*M12
3405            C2 = B12*M21 + B22*M22
3406            B12 = C1
3407            B22 = C2
3408            k = k_next
3409   
3410        r = B12/B11
3411        return np.absolute(r)**2
3412
3413    if np.isscalar(kz): kz = np.asarray([kz], 'd')
3414
3415    m = len(depth)
3416
3417    # Make everything into arrays
3418    depth = np.asarray(depth,'d')
3419    rho = np.asarray(rho,'d')
3420    irho = irho*np.ones_like(rho) if np.isscalar(irho) else np.asarray(irho,'d')
3421    sigma = sigma*np.ones(m-1,'d') if np.isscalar(sigma) else np.asarray(sigma,'d')
3422
3423    # Repeat rho,irho columns as needed
3424    if len(rho.shape) == 1:
3425        rho = rho[None,:]
3426        irho = irho[None,:]
3427
3428    return calc(kz, depth, rho, irho, sigma)
3429   
3430def SmearAbeles(kz,dq, depth, rho, irho=0, sigma=0):
3431    y = abeles(kz, depth, rho, irho, sigma)
3432    s = dq/2.
3433    y += 0.1354*(abeles(kz+2*s, depth, rho, irho, sigma)+abeles(kz-2*s, depth, rho, irho, sigma))
3434    y += 0.24935*(abeles(kz-5*s/3., depth, rho, irho, sigma)+abeles(kz+5*s/3., depth, rho, irho, sigma)) 
3435    y += 0.4111*(abeles(kz-4*s/3., depth, rho, irho, sigma)+abeles(kz+4*s/3., depth, rho, irho, sigma)) 
3436    y += 0.60653*(abeles(kz-s, depth, rho, irho, sigma) +abeles(kz+s, depth, rho, irho, sigma))
3437    y += 0.80074*(abeles(kz-2*s/3., depth, rho, irho, sigma)+abeles(kz-2*s/3., depth, rho, irho, sigma))
3438    y += 0.94596*(abeles(kz-s/3., depth, rho, irho, sigma)+abeles(kz-s/3., depth, rho, irho, sigma))
3439    y *= 0.137023
3440    return y
3441       
3442def makeRefdFFT(Limits,Profile):
3443    G2fil.G2Print ('make fft')
3444    Q,Io = Profile[:2]
3445    Qmin = Limits[1][0]
3446    Qmax = Limits[1][1]
3447    iBeg = np.searchsorted(Q,Qmin)
3448    iFin = np.searchsorted(Q,Qmax)+1    #include last point
3449    Qf = np.linspace(0.,Q[iFin-1],5000)
3450    QI = si.interp1d(Q[iBeg:iFin],Io[iBeg:iFin],bounds_error=False,fill_value=0.0)
3451    If = QI(Qf)*Qf**4
3452    R = np.linspace(0.,5000.,5000)
3453    F = fft.rfft(If)
3454    return R,F
3455
3456   
3457################################################################################
3458#### Stacking fault simulation codes
3459################################################################################
3460
3461def GetStackParms(Layers):
3462   
3463    Parms = []
3464#cell parms
3465    if Layers['Laue'] in ['-3','-3m','4/m','4/mmm','6/m','6/mmm']:
3466        Parms.append('cellA')
3467        Parms.append('cellC')
3468    else:
3469        Parms.append('cellA')
3470        Parms.append('cellB')
3471        Parms.append('cellC')
3472        if Layers['Laue'] != 'mmm':
3473            Parms.append('cellG')
3474#Transition parms
3475    for iY in range(len(Layers['Layers'])):
3476        for iX in range(len(Layers['Layers'])):
3477            Parms.append('TransP;%d;%d'%(iY,iX))
3478            Parms.append('TransX;%d;%d'%(iY,iX))
3479            Parms.append('TransY;%d;%d'%(iY,iX))
3480            Parms.append('TransZ;%d;%d'%(iY,iX))
3481    return Parms
3482
3483def StackSim(Layers,ctrls,scale=0.,background={},limits=[],inst={},profile=[]):
3484    '''Simulate powder or selected area diffraction pattern from stacking faults using DIFFaX
3485   
3486    :param dict Layers: dict with following items
3487
3488      ::
3489
3490       {'Laue':'-1','Cell':[False,1.,1.,1.,90.,90.,90,1.],
3491       'Width':[[10.,10.],[False,False]],'Toler':0.01,'AtInfo':{},
3492       'Layers':[],'Stacking':[],'Transitions':[]}
3493       
3494    :param str ctrls: controls string to be written on DIFFaX controls.dif file
3495    :param float scale: scale factor
3496    :param dict background: background parameters
3497    :param list limits: min/max 2-theta to be calculated
3498    :param dict inst: instrument parameters dictionary
3499    :param list profile: powder pattern data
3500   
3501    Note that parameters all updated in place   
3502    '''
3503    import atmdata
3504    path = sys.path
3505    for name in path:
3506        if 'bin' in name:
3507            DIFFaX = name+'/DIFFaX.exe'
3508            G2fil.G2Print (' Execute '+DIFFaX)
3509            break
3510    # make form factor file that DIFFaX wants - atom types are GSASII style
3511    sf = open('data.sfc','w')
3512    sf.write('GSASII special form factor file for DIFFaX\n\n')
3513    atTypes = list(Layers['AtInfo'].keys())
3514    if 'H' not in atTypes:
3515        atTypes.insert(0,'H')
3516    for atType in atTypes:
3517        if atType == 'H': 
3518            blen = -.3741
3519        else:
3520            blen = Layers['AtInfo'][atType]['Isotopes']['Nat. Abund.']['SL'][0]
3521        Adat = atmdata.XrayFF[atType]
3522        text = '%4s'%(atType.ljust(4))
3523        for i in range(4):
3524            text += '%11.6f%11.6f'%(Adat['fa'][i],Adat['fb'][i])
3525        text += '%11.6f%11.6f'%(Adat['fc'],blen)
3526        text += '%3d\n'%(Adat['Z'])
3527        sf.write(text)
3528    sf.close()
3529    #make DIFFaX control.dif file - future use GUI to set some of these flags
3530    cf = open('control.dif','w')
3531    if ctrls == '0\n0\n3\n' or ctrls == '0\n1\n3\n': 
3532        x0 = profile[0]
3533        iBeg = np.searchsorted(x0,limits[0])
3534        iFin = np.searchsorted(x0,limits[1])+1
3535        if iFin-iBeg > 20000:
3536            iFin = iBeg+20000
3537        Dx = (x0[iFin]-x0[iBeg])/(iFin-iBeg)
3538        cf.write('GSASII-DIFFaX.dat\n'+ctrls)
3539        cf.write('%.6f %.6f %.6f\n1\n1\nend\n'%(x0[iBeg],x0[iFin],Dx))
3540    else:
3541        cf.write('GSASII-DIFFaX.dat\n'+ctrls)
3542        inst = {'Type':['XSC','XSC',]}
3543    cf.close()
3544    #make DIFFaX data file
3545    df = open('GSASII-DIFFaX.dat','w')
3546    df.write('INSTRUMENTAL\n')
3547    if 'X' in inst['Type'][0]:
3548        df.write('X-RAY\n')
3549    elif 'N' in inst['Type'][0]:
3550        df.write('NEUTRON\n')
3551    if ctrls == '0\n0\n3\n' or ctrls == '0\n1\n3\n': 
3552        df.write('%.4f\n'%(G2mth.getMeanWave(inst)))
3553        U = ateln2*inst['U'][1]/10000.
3554        V = ateln2*inst['V'][1]/10000.
3555        W = ateln2*inst['W'][1]/10000.
3556        HWHM = U*nptand(x0[iBeg:iFin]/2.)**2+V*nptand(x0[iBeg:iFin]/2.)+W
3557        HW = np.sqrt(np.mean(HWHM))
3558    #    df.write('PSEUDO-VOIGT 0.015 -0.0036 0.009 0.605 TRIM\n')
3559        if 'Mean' in Layers['selInst']:
3560            df.write('GAUSSIAN %.6f TRIM\n'%(HW))     #fast option - might not really matter
3561        elif 'Gaussian' in Layers['selInst']:
3562            df.write('GAUSSIAN %.6f %.6f %.6f TRIM\n'%(U,V,W))    #slow - make a GUI option?
3563        else:
3564            df.write('None\n')
3565    else:
3566        df.write('0.10\nNone\n')
3567    df.write('STRUCTURAL\n')
3568    a,b,c = Layers['Cell'][1:4]
3569    gam = Layers['Cell'][6]
3570    df.write('%.4f %.4f %.4f %.3f\n'%(a,b,c,gam))
3571    laue = Layers['Laue']
3572    if laue == '2/m(ab)':
3573        laue = '2/m(1)'
3574    elif laue == '2/m(c)':
3575        laue = '2/m(2)'
3576    if 'unknown' in Layers['Laue']:
3577        df.write('%s %.3f\n'%(laue,Layers['Toler']))
3578    else:   
3579        df.write('%s\n'%(laue))
3580    df.write('%d\n'%(len(Layers['Layers'])))
3581    if Layers['Width'][0][0] < 1. or Layers['Width'][0][1] < 1.:
3582        df.write('%.1f %.1f\n'%(Layers['Width'][0][0]*10000.,Layers['Width'][0][0]*10000.))    #mum to A
3583    layerNames = []
3584    for layer in Layers['Layers']:
3585        layerNames.append(layer['Name'])
3586    for il,layer in enumerate(Layers['Layers']):
3587        if layer['SameAs']:
3588            df.write('LAYER %d = %d\n'%(il+1,layerNames.index(layer['SameAs'])+1))
3589            continue
3590        df.write('LAYER %d\n'%(il+1))
3591        if '-1' in layer['Symm']:
3592            df.write('CENTROSYMMETRIC\n')
3593        else:
3594            df.write('NONE\n')
3595        for ia,atom in enumerate(layer['Atoms']):
3596            [name,atype,x,y,z,frac,Uiso] = atom
3597            if '-1' in layer['Symm'] and [x,y,z] == [0.,0.,0.]:
3598                frac /= 2.
3599            df.write('%4s %3d %.5f %.5f %.5f %.4f %.2f\n'%(atype.ljust(6),ia,x,y,z,78.9568*Uiso,frac))
3600    df.write('STACKING\n')
3601    df.write('%s\n'%(Layers['Stacking'][0]))
3602    if 'recursive' in Layers['Stacking'][0]:
3603        df.write('%s\n'%Layers['Stacking'][1])
3604    else:
3605        if 'list' in Layers['Stacking'][1]:
3606            Slen = len(Layers['Stacking'][2])
3607            iB = 0
3608            iF = 0
3609            while True:
3610                iF += 68
3611                if iF >= Slen:
3612                    break
3613                iF = min(iF,Slen)
3614                df.write('%s\n'%(Layers['Stacking'][2][iB:iF]))
3615                iB = iF
3616        else:
3617            df.write('%s\n'%Layers['Stacking'][1])   
3618    df.write('TRANSITIONS\n')
3619    for iY in range(len(Layers['Layers'])):
3620        sumPx = 0.
3621        for iX in range(len(Layers['Layers'])):
3622            p,dx,dy,dz = Layers['Transitions'][iY][iX][:4]
3623            p = round(p,3)
3624            df.write('%.3f %.5f %.5f %.5f\n'%(p,dx,dy,dz))
3625            sumPx += p
3626        if sumPx != 1.0:    #this has to be picky since DIFFaX is.
3627            G2fil.G2Print ('ERROR - Layer probabilities sum to %.3f DIFFaX will insist it = 1.0'%sumPx)
3628            df.close()
3629            os.remove('data.sfc')
3630            os.remove('control.dif')
3631            os.remove('GSASII-DIFFaX.dat')
3632            return       
3633    df.close()   
3634    time0 = time.time()
3635    try:
3636        subp.call(DIFFaX)
3637    except OSError:
3638        G2fil.G2Print('DIFFax.exe is not available for this platform',mode='warn')
3639    G2fil.G2Print (' DIFFaX time = %.2fs'%(time.time()-time0))
3640    if os.path.exists('GSASII-DIFFaX.spc'):
3641        Xpat = np.loadtxt('GSASII-DIFFaX.spc').T
3642        iFin = iBeg+Xpat.shape[1]
3643        bakType,backDict,backVary = SetBackgroundParms(background)
3644        backDict['Lam1'] = G2mth.getWave(inst)
3645        profile[4][iBeg:iFin] = getBackground('',backDict,bakType,inst['Type'][0],profile[0][iBeg:iFin])[0]   
3646        profile[3][iBeg:iFin] = Xpat[-1]*scale+profile[4][iBeg:iFin]
3647        if not np.any(profile[1]):                   #fill dummy data x,y,w,yc,yb,yd
3648            rv = st.poisson(profile[3][iBeg:iFin])
3649            profile[1][iBeg:iFin] = rv.rvs()
3650            Z = np.ones_like(profile[3][iBeg:iFin])
3651            Z[1::2] *= -1
3652            profile[1][iBeg:iFin] = profile[3][iBeg:iFin]+np.abs(profile[1][iBeg:iFin]-profile[3][iBeg:iFin])*Z
3653            profile[2][iBeg:iFin] = np.where(profile[1][iBeg:iFin]>0.,1./profile[1][iBeg:iFin],1.0)
3654        profile[5][iBeg:iFin] = profile[1][iBeg:iFin]-profile[3][iBeg:iFin]
3655    #cleanup files..
3656        os.remove('GSASII-DIFFaX.spc')
3657    elif os.path.exists('GSASII-DIFFaX.sadp'):
3658        Sadp = np.fromfile('GSASII-DIFFaX.sadp','>u2')
3659        Sadp = np.reshape(Sadp,(256,-1))
3660        Layers['Sadp']['Img'] = Sadp
3661        os.remove('GSASII-DIFFaX.sadp')
3662    os.remove('data.sfc')
3663    os.remove('control.dif')
3664    os.remove('GSASII-DIFFaX.dat')
3665   
3666def SetPWDRscan(inst,limits,profile):
3667   
3668    wave = G2mth.getMeanWave(inst)
3669    x0 = profile[0]
3670    iBeg = np.searchsorted(x0,limits[0])
3671    iFin = np.searchsorted(x0,limits[1])
3672    if iFin-iBeg > 20000:
3673        iFin = iBeg+20000
3674    Dx = (x0[iFin]-x0[iBeg])/(iFin-iBeg)
3675    pyx.pygetinst(wave,x0[iBeg],x0[iFin],Dx)
3676    return iFin-iBeg
3677       
3678def SetStackingSF(Layers,debug):
3679# Load scattering factors into DIFFaX arrays
3680    import atmdata
3681    atTypes = Layers['AtInfo'].keys()
3682    aTypes = []
3683    for atype in atTypes:
3684        aTypes.append('%4s'%(atype.ljust(4)))
3685    SFdat = []
3686    for atType in atTypes:
3687        Adat = atmdata.XrayFF[atType]
3688        SF = np.zeros(9)
3689        SF[:8:2] = Adat['fa']
3690        SF[1:8:2] = Adat['fb']
3691        SF[8] = Adat['fc']
3692        SFdat.append(SF)
3693    SFdat = np.array(SFdat)
3694    pyx.pyloadscf(len(atTypes),aTypes,SFdat.T,debug)
3695   
3696def SetStackingClay(Layers,Type):
3697# Controls
3698    rand.seed()
3699    ranSeed = rand.randint(1,2**16-1)
3700    try:   
3701        laueId = ['-1','2/m(ab)','2/m(c)','mmm','-3','-3m','4/m','4/mmm',
3702            '6/m','6/mmm'].index(Layers['Laue'])+1
3703    except ValueError:  #for 'unknown'
3704        laueId = -1
3705    if 'SADP' in Type:
3706        planeId = ['h0l','0kl','hhl','h-hl'].index(Layers['Sadp']['Plane'])+1
3707        lmax = int(Layers['Sadp']['Lmax'])
3708    else:
3709        planeId = 0
3710        lmax = 0
3711# Sequences
3712    StkType = ['recursive','explicit'].index(Layers['Stacking'][0])
3713    try:
3714        StkParm = ['infinite','random','list'].index(Layers['Stacking'][1])
3715    except ValueError:
3716        StkParm = -1
3717    if StkParm == 2:    #list
3718        StkSeq = [int(val) for val in Layers['Stacking'][2].split()]
3719        Nstk = len(StkSeq)
3720    else:
3721        Nstk = 1
3722        StkSeq = [0,]
3723    if StkParm == -1:
3724        StkParm = int(Layers['Stacking'][1])
3725    Wdth = Layers['Width'][0]
3726    mult = 1
3727    controls = [laueId,planeId,lmax,mult,StkType,StkParm,ranSeed]
3728    LaueSym = Layers['Laue'].ljust(12)
3729    pyx.pygetclay(controls,LaueSym,Wdth,Nstk,StkSeq)
3730    return laueId,controls
3731   
3732def SetCellAtoms(Layers):
3733    Cell = Layers['Cell'][1:4]+Layers['Cell'][6:7]
3734# atoms in layers
3735    atTypes = list(Layers['AtInfo'].keys())
3736    AtomXOU = []
3737    AtomTp = []
3738    LayerSymm = []
3739    LayerNum = []
3740    layerNames = []
3741    Natm = 0
3742    Nuniq = 0
3743    for layer in Layers['Layers']:
3744        layerNames.append(layer['Name'])
3745    for il,layer in enumerate(Layers['Layers']):
3746        if layer['SameAs']:
3747            LayerNum.append(layerNames.index(layer['SameAs'])+1)
3748            continue
3749        else:
3750            LayerNum.append(il+1)
3751            Nuniq += 1
3752        if '-1' in layer['Symm']:
3753            LayerSymm.append(1)
3754        else:
3755            LayerSymm.append(0)
3756        for ia,atom in enumerate(layer['Atoms']):
3757            [name,atype,x,y,z,frac,Uiso] = atom
3758            Natm += 1
3759            AtomTp.append('%4s'%(atype.ljust(4)))
3760            Ta = atTypes.index(atype)+1
3761            AtomXOU.append([float(Nuniq),float(ia+1),float(Ta),x,y,z,frac,Uiso*78.9568])
3762    AtomXOU = np.array(AtomXOU)
3763    Nlayers = len(layerNames)
3764    pyx.pycellayer(Cell,Natm,AtomTp,AtomXOU.T,Nuniq,LayerSymm,Nlayers,LayerNum)
3765    return Nlayers
3766   
3767def SetStackingTrans(Layers,Nlayers):
3768# Transitions
3769    TransX = []
3770    TransP = []
3771    for Ytrans in Layers['Transitions']:
3772        TransP.append([trans[0] for trans in Ytrans])   #get just the numbers
3773        TransX.append([trans[1:4] for trans in Ytrans])   #get just the numbers
3774    TransP = np.array(TransP,dtype='float').T
3775    TransX = np.array(TransX,dtype='float')
3776#    GSASIIpath.IPyBreak()
3777    pyx.pygettrans(Nlayers,TransP,TransX)
3778   
3779def CalcStackingPWDR(Layers,scale,background,limits,inst,profile,debug):
3780# Scattering factors
3781    SetStackingSF(Layers,debug)
3782# Controls & sequences
3783    laueId,controls = SetStackingClay(Layers,'PWDR')
3784# cell & atoms
3785    Nlayers = SetCellAtoms(Layers)
3786    Volume = Layers['Cell'][7]   
3787# Transitions
3788    SetStackingTrans(Layers,Nlayers)
3789# PWDR scan
3790    Nsteps = SetPWDRscan(inst,limits,profile)
3791# result as Spec
3792    x0 = profile[0]
3793    profile[3] = np.zeros(len(profile[0]))
3794    profile[4] = np.zeros(len(profile[0]))
3795    profile[5] = np.zeros(len(profile[0]))
3796    iBeg = np.searchsorted(x0,limits[0])
3797    iFin = np.searchsorted(x0,limits[1])+1
3798    if iFin-iBeg > 20000:
3799        iFin = iBeg+20000
3800    Nspec = 20001       
3801    spec = np.zeros(Nspec,dtype='double')   
3802    time0 = time.time()
3803    pyx.pygetspc(controls,Nspec,spec)
3804    G2fil.G2Print (' GETSPC time = %.2fs'%(time.time()-time0))
3805    time0 = time.time()
3806    U = ateln2*inst['U'][1]/10000.
3807    V = ateln2*inst['V'][1]/10000.
3808    W = ateln2*inst['W'][1]/10000.
3809    HWHM = U*nptand(x0[iBeg:iFin]/2.)**2+V*nptand(x0[iBeg:iFin]/2.)+W
3810    HW = np.sqrt(np.mean(HWHM))
3811    BrdSpec = np.zeros(Nsteps)
3812    if 'Mean' in Layers['selInst']:
3813        pyx.pyprofile(U,V,W,HW,1,Nsteps,BrdSpec)
3814    elif 'Gaussian' in Layers['selInst']:
3815        pyx.pyprofile(U,V,W,HW,4,Nsteps,BrdSpec)
3816    else:
3817        BrdSpec = spec[:Nsteps]
3818    BrdSpec /= Volume
3819    iFin = iBeg+Nsteps
3820    bakType,backDict,backVary = SetBackgroundParms(background)
3821    backDict['Lam1'] = G2mth.getWave(inst)
3822    profile[4][iBeg:iFin] = getBackground('',backDict,bakType,inst['Type'][0],profile[0][iBeg:iFin])[0]   
3823    profile[3][iBeg:iFin] = BrdSpec*scale+profile[4][iBeg:iFin]
3824    if not np.any(profile[1]):                   #fill dummy data x,y,w,yc,yb,yd
3825        try:
3826            rv = st.poisson(profile[3][iBeg:iFin])
3827            profile[1][iBeg:iFin] = rv.rvs()
3828        except ValueError:
3829            profile[1][iBeg:iFin] = profile[3][iBeg:iFin]
3830        Z = np.ones_like(profile[3][iBeg:iFin])
3831        Z[1::2] *= -1
3832        profile[1][iBeg:iFin] = profile[3][iBeg:iFin]+np.abs(profile[1][iBeg:iFin]-profile[3][iBeg:iFin])*Z
3833        profile[2][iBeg:iFin] = np.where(profile[1][iBeg:iFin]>0.,1./profile[1][iBeg:iFin],1.0)
3834    profile[5][iBeg:iFin] = profile[1][iBeg:iFin]-profile[3][iBeg:iFin]
3835    G2fil.G2Print (' Broadening time = %.2fs'%(time.time()-time0))
3836   
3837def CalcStackingSADP(Layers,debug):
3838   
3839# Scattering factors
3840    SetStackingSF(Layers,debug)
3841# Controls & sequences
3842    laueId,controls = SetStackingClay(Layers,'SADP')
3843# cell & atoms
3844    Nlayers = SetCellAtoms(Layers)   
3845# Transitions
3846    SetStackingTrans(Layers,Nlayers)
3847# result as Sadp
3848    Nspec = 20001       
3849    spec = np.zeros(Nspec,dtype='double')   
3850    time0 = time.time()
3851    hkLim,Incr,Nblk = pyx.pygetsadp(controls,Nspec,spec)
3852    Sapd = np.zeros((256,256))
3853    iB = 0
3854    for i in range(hkLim):
3855        iF = iB+Nblk
3856        p1 = 127+int(i*Incr)
3857        p2 = 128-int(i*Incr)
3858        if Nblk == 128:
3859            if i:
3860                Sapd[128:,p1] = spec[iB:iF]
3861                Sapd[:128,p1] = spec[iF:iB:-1]
3862            Sapd[128:,p2] = spec[iB:iF]
3863            Sapd[:128,p2] = spec[iF:iB:-1]
3864        else:
3865            if i:
3866                Sapd[:,p1] = spec[iB:iF]
3867            Sapd[:,p2] = spec[iB:iF]
3868        iB += Nblk
3869    Layers['Sadp']['Img'] = Sapd
3870    G2fil.G2Print (' GETSAD time = %.2fs'%(time.time()-time0))
3871   
3872###############################################################################
3873#### Maximum Entropy Method - Dysnomia
3874###############################################################################
3875   
3876def makePRFfile(data,MEMtype):
3877    ''' makes Dysnomia .prf control file from Dysnomia GUI controls
3878   
3879    :param dict data: GSAS-II phase data
3880    :param int MEMtype: 1 for neutron data with negative scattering lengths
3881                        0 otherwise
3882    :returns str: name of Dysnomia control file
3883    '''
3884
3885    generalData = data['General']
3886    pName = generalData['Name'].replace(' ','_')
3887    DysData = data['Dysnomia']
3888    prfName = pName+'.prf'
3889    prf = open(prfName,'w')
3890    prf.write('$PREFERENCES\n')
3891    prf.write(pName+'.mem\n') #or .fos?
3892    prf.write(pName+'.out\n')
3893    prf.write(pName+'.pgrid\n')
3894    prf.write(pName+'.fba\n')
3895    prf.write(pName+'_eps.raw\n')
3896    prf.write('%d\n'%MEMtype)
3897    if DysData['DenStart'] == 'uniform':
3898        prf.write('0\n')
3899    else:
3900        prf.write('1\n')
3901    if DysData['Optimize'] == 'ZSPA':
3902        prf.write('0\n')
3903    else:
3904        prf.write('1\n')
3905    prf.write('1\n')
3906    if DysData['Lagrange'][0] == 'user':
3907        prf.write('0\n')
3908    else:
3909        prf.write('1\n')
3910    prf.write('%.4f %d\n'%(DysData['Lagrange'][1],DysData['wt pwr']))
3911    prf.write('%.3f\n'%DysData['Lagrange'][2])
3912    prf.write('%.2f\n'%DysData['E_factor'])
3913    prf.write('1\n')
3914    prf.write('0\n')
3915    prf.write('%d\n'%DysData['Ncyc'])
3916    prf.write('1\n')
3917    prf.write('1 0 0 0 0 0 0 0\n')
3918    if DysData['prior'] == 'uniform':
3919        prf.write('0\n')
3920    else:
3921        prf.write('1\n')
3922    prf.close()
3923    return prfName
3924
3925def makeMEMfile(data,reflData,MEMtype,DYSNOMIA):
3926    ''' make Dysnomia .mem file of reflection data, etc.
3927
3928    :param dict data: GSAS-II phase data
3929    :param list reflData: GSAS-II reflection data
3930    :param int MEMtype: 1 for neutron data with negative scattering lengths
3931                        0 otherwise
3932    :param str DYSNOMIA: path to dysnomia.exe
3933    '''
3934   
3935    DysData = data['Dysnomia']
3936    generalData = data['General']
3937    cell = generalData['Cell'][1:7]
3938    A = G2lat.cell2A(cell)
3939    SGData = generalData['SGData']
3940    pName = generalData['Name'].replace(' ','_')
3941    memName = pName+'.mem'
3942    Map = generalData['Map']
3943    Type = Map['Type']
3944    UseList = Map['RefList']
3945    mem = open(memName,'w')
3946    mem.write('%s\n'%(generalData['Name']+' from '+UseList[0]))
3947    a,b,c,alp,bet,gam = cell
3948    mem.write('%10.5f%10.5f%10.5f%10.5f%10.5f%10.5f\n'%(a,b,c,alp,bet,gam))
3949    mem.write('      0.0000000      0.0000000     -1    0    0    0     P\n')   #dummy PO stuff
3950    SGSym = generalData['SGData']['SpGrp']
3951    try:
3952        SGId = G2spc.spgbyNum.index(SGSym)
3953    except ValueError:
3954        return False
3955    org = 1
3956    if SGSym in G2spc.spg2origins:
3957        org = 2
3958    mapsize = Map['rho'].shape
3959    sumZ = 0.
3960    sumpos = 0.
3961    sumneg = 0.
3962    mem.write('%5d%5d%5d%5d%5d\n'%(SGId,org,mapsize[0],mapsize[1],mapsize[2]))
3963    for atm in generalData['NoAtoms']:
3964        Nat = generalData['NoAtoms'][atm]
3965        AtInfo = G2elem.GetAtomInfo(atm)
3966        sumZ += Nat*AtInfo['Z']
3967        isotope = generalData['Isotope'][atm]
3968        blen = generalData['Isotopes'][atm][isotope]['SL'][0]
3969        if blen < 0.:
3970            sumneg += blen*Nat
3971        else:
3972            sumpos += blen*Nat
3973    if 'X' in Type:
3974        mem.write('%10.2f  0.001\n'%sumZ)
3975    elif 'N' in Type and MEMtype:
3976        mem.write('%10.3f%10.3f 0.001\n'%(sumpos,sumneg))
3977    else:
3978        mem.write('%10.3f 0.001\n'%sumpos)
3979       
3980    dmin = DysData['MEMdmin']
3981    TOFlam = 2.0*dmin*npsind(80.0)
3982    refSet = G2lat.GenHLaue(dmin,SGData,A)      #list of h,k,l,d
3983    refDict = {'%d %d %d'%(ref[0],ref[1],ref[2]):ref for ref in refSet}
3984       
3985    refs = []
3986    prevpos = 0.
3987    for ref in reflData:
3988        if ref[3] < 0:
3989            continue
3990        if 'T' in Type:
3991            h,k,l,mult,dsp,pos,sig,gam,Fobs,Fcalc,phase,x,x,x,x,prfo = ref[:16]
3992            s = np.sqrt(max(sig,0.0001))   #var -> sig in deg
3993            FWHM = getgamFW(gam,s)
3994            if dsp < dmin:
3995                continue
3996            theta = npasind(TOFlam/(2.*dsp))
3997            FWHM *= nptand(theta)/pos
3998            pos = 2.*theta
3999        else:
4000            h,k,l,mult,dsp,pos,sig,gam,Fobs,Fcalc,phase,x,prfo = ref[:13]
4001            g = gam/100.    #centideg -> deg
4002            s = np.sqrt(max(sig,0.0001))/100.   #var -> sig in deg
4003            FWHM = getgamFW(g,s)
4004        delt = pos-prevpos
4005        refs.append([h,k,l,mult,pos,FWHM,Fobs,phase,delt])
4006        prevpos = pos
4007           
4008    ovlp = DysData['overlap']
4009    refs1 = []
4010    refs2 = []
4011    nref2 = 0
4012    iref = 0
4013    Nref = len(refs)
4014    start = False
4015    while iref < Nref-1:
4016        if refs[iref+1][-1] < ovlp*refs[iref][5]:
4017            if refs[iref][-1] > ovlp*refs[iref][5]:
4018                refs2.append([])
4019                start = True
4020            if nref2 == len(refs2):
4021                refs2.append([])
4022            refs2[nref2].append(refs[iref])
4023        else:
4024            if start:
4025                refs2[nref2].append(refs[iref])
4026                start = False
4027                nref2 += 1
4028            else:
4029                refs1.append(refs[iref])
4030        iref += 1
4031    if start:
4032        refs2[nref2].append(refs[iref])
4033    else:
4034        refs1.append(refs[iref])
4035   
4036    mem.write('%5d\n'%len(refs1))
4037    for ref in refs1:
4038        h,k,l = ref[:3]
4039        hkl = '%d %d %d'%(h,k,l)
4040        if hkl in refDict:
4041            del refDict[hkl]
4042        Fobs = np.sqrt(ref[6])
4043        mem.write('%5d%5d%5d%10.3f%10.3f%10.3f\n'%(h,k,l,Fobs*npcosd(ref[7]),Fobs*npsind(ref[7]),max(0.01*Fobs,0.1)))
4044    while True and nref2:
4045        if not len(refs2[-1]):
4046            del refs2[-1]
4047        else:
4048            break
4049    mem.write('%5d\n'%len(refs2))
4050    for iref2,ref2 in enumerate(refs2):
4051        mem.write('#%5d\n'%iref2)
4052        mem.write('%5d\n'%len(ref2))
4053        Gsum = 0.
4054        Msum = 0
4055        for ref in ref2:
4056            Gsum += ref[6]*ref[3]
4057            Msum += ref[3]
4058        G = np.sqrt(Gsum/Msum)
4059        h,k,l = ref2[0][:3]
4060        hkl = '%d %d %d'%(h,k,l)
4061        if hkl in refDict:
4062            del refDict[hkl]
4063        mem.write('%5d%5d%5d%10.3f%10.3f%5d\n'%(h,k,l,G,max(0.01*G,0.1),ref2[0][3]))
4064        for ref in ref2[1:]:
4065            h,k,l,m = ref[:4]
4066            mem.write('%5d%5d%5d%5d\n'%(h,k,l,m))
4067            hkl = '%d %d %d'%(h,k,l)
4068            if hkl in refDict:
4069                del refDict[hkl]
4070    if len(refDict):
4071        mem.write('%d\n'%len(refDict))
4072        for hkl in list(refDict.keys()):
4073            h,k,l = refDict[hkl][:3]
4074            mem.write('%5d%5d%5d\n'%(h,k,l))
4075    else:
4076        mem.write('0\n')
4077    mem.close()
4078    return True
4079
4080def MEMupdateReflData(prfName,data,reflData):
4081    ''' Update reflection data with new Fosq, phase result from Dysnomia
4082
4083    :param str prfName: phase.mem file name
4084    :param list reflData: GSAS-II reflection data
4085    '''
4086   
4087    generalData = data['General']
4088    Map = generalData['Map']
4089    Type = Map['Type']
4090    cell = generalData['Cell'][1:7]
4091    A = G2lat.cell2A(cell)
4092    reflDict = {}
4093    newRefs = []
4094    for iref,ref in enumerate(reflData):
4095        if ref[3] > 0:
4096            newRefs.append(ref)
4097            reflDict[hash('%5d%5d%5d'%(ref[0],ref[1],ref[2]))] = iref
4098    fbaName = os.path.splitext(prfName)[0]+'.fba'
4099    if os.path.isfile(fbaName):
4100        fba = open(fbaName,'r')
4101    else:
4102        return False
4103    fba.readline()
4104    Nref = int(fba.readline()[:-1])
4105    fbalines = fba.readlines()
4106    for line in fbalines[:Nref]:
4107        info = line.split()
4108        h = int(info[0])
4109        k = int(info[1])
4110        l = int(info[2])
4111        FoR = float(info[3])
4112        FoI = float(info[4])
4113        Fosq = FoR**2+FoI**2
4114        phase = npatan2d(FoI,FoR)
4115        try:
4116            refId = reflDict[hash('%5d%5d%5d'%(h,k,l))]
4117        except KeyError:    #added reflections at end skipped
4118            d = float(1/np.sqrt(G2lat.calc_rDsq([h,k,l],A)))
4119            if 'T' in Type:
4120                newRefs.append([h,k,l,-1,d,0.,0.01,1.0,Fosq,Fosq,phase,1.0,1.0,1.0,1.0,1.0,1.0,1.0])
4121            else:
4122                newRefs.append([h,k,l,-1,d,0.,0.01,1.0,Fosq,Fosq,phase,1.0,1.0,1.0,1.0])
4123            continue
4124        newRefs[refId][8] = Fosq
4125        newRefs[refId][10] = phase
4126    newRefs = np.array(newRefs)
4127    return True,newRefs
4128   
4129#### testing data
4130NeedTestData = True
4131def TestData():
4132    'needs a doc string'
4133#    global NeedTestData
4134    global bakType
4135    bakType = 'chebyschev'
4136    global xdata
4137    xdata = np.linspace(4.0,40.0,36000)
4138    global parmDict0
4139    parmDict0 = {
4140        'pos0':5.6964,'int0':8835.8,'sig0':1.0,'gam0':1.0,
4141        'pos1':11.4074,'int1':3922.3,'sig1':1.0,'gam1':1.0,
4142        'pos2':20.6426,'int2':1573.7,'sig2':1.0,'gam2':1.0,
4143        'pos3':26.9568,'int3':925.1,'sig3':1.0,'gam3':1.0,
4144        'U':1.163,'V':-0.605,'W':0.093,'X':0.0,'Y':2.183,'Z':0.0,'SH/L':0.002,
4145        'Back0':5.384,'Back1':-0.015,'Back2':.004,
4146        }
4147    global parmDict1
4148    parmDict1 = {
4149        'pos0':13.4924,'int0':48697.6,'sig0':1.0,'gam0':1.0,
4150        'pos1':23.4360,'int1':43685.5,'sig1':1.0,'gam1':1.0,
4151        'pos2':27.1152,'int2':123712.6,'sig2':1.0,'gam2':1.0,
4152        'pos3':33.7196,'int3':65349.4,'sig3':1.0,'gam3':1.0,
4153        'pos4':36.1119,'int4':115829.8,'sig4':1.0,'gam4':1.0,
4154        'pos5':39.0122,'int5':6916.9,'sig5':1.0,'gam5':1.0,
4155        'U':22.75,'V':-17.596,'W':10.594,'X':1.577,'Y':5.778,'Z':0.0,'SH/L':0.002,
4156        'Back0':36.897,'Back1':-0.508,'Back2':.006,
4157        'Lam1':1.540500,'Lam2':1.544300,'I(L2)/I(L1)':0.5,
4158        }
4159    global parmDict2
4160    parmDict2 = {
4161        'pos0':5.7,'int0':1000.0,'sig0':0.5,'gam0':0.5,
4162        'U':2.,'V':-2.,'W':5.,'X':0.5,'Y':0.5,'Z':0.0,'SH/L':0.02,
4163        'Back0':5.,'Back1':-0.02,'Back2':.004,
4164#        'Lam1':1.540500,'Lam2':1.544300,'I(L2)/I(L1)':0.5,
4165        }
4166    global varyList
4167    varyList = []
4168
4169def test0():
4170    if NeedTestData: TestData()
4171    gplot = plotter.add('FCJ-Voigt, 11BM').gca()
4172    gplot.plot(xdata,getBackground('',parmDict0,bakType,'PXC',xdata)[0])   
4173    gplot.plot(xdata,getPeakProfile(parmDict0,xdata,varyList,bakType))
4174    fplot = plotter.add('FCJ-Voigt, Ka1+2').gca()
4175    fplot.plot(xdata,getBackground('',parmDict1,bakType,'PXC',xdata)[0])   
4176    fplot.plot(xdata,getPeakProfile(parmDict1,xdata,varyList,bakType))
4177   
4178def test1():
4179    if NeedTestData: TestData()
4180    time0 = time.time()
4181    for i in range(100):
4182        getPeakProfile(parmDict1,xdata,varyList,bakType)
4183    G2fil.G2Print ('100+6*Ka1-2 peaks=1200 peaks %.2f'%time.time()-time0)
4184   
4185def test2(name,delt):
4186    if NeedTestData: TestData()
4187    varyList = [name,]
4188    xdata = np.linspace(5.6,5.8,400)
4189    hplot = plotter.add('derivatives test for '+name).gca()
4190    hplot.plot(xdata,getPeakProfileDerv(parmDict2,xdata,varyList,bakType)[0])
4191    y0 = getPeakProfile(parmDict2,xdata,varyList,bakType)
4192    parmDict2[name] += delt
4193    y1 = getPeakProfile(parmDict2,xdata,varyList,bakType)
4194    hplot.plot(xdata,(y1-y0)/delt,'r+')
4195   
4196def test3(name,delt):
4197    if NeedTestData: TestData()
4198    names = ['pos','sig','gam','shl']
4199    idx = names.index(name)
4200    myDict = {'pos':parmDict2['pos0'],'sig':parmDict2['sig0'],'gam':parmDict2['gam0'],'shl':parmDict2['SH/L']}
4201    xdata = np.linspace(5.6,5.8,800)
4202    dx = xdata[1]-xdata[0]
4203    hplot = plotter.add('derivatives test for '+name).gca()
4204    hplot.plot(xdata,100.*dx*getdFCJVoigt3(myDict['pos'],myDict['sig'],myDict['gam'],myDict['shl'],xdata)[idx+1])
4205    y0 = getFCJVoigt3(myDict['pos'],myDict['sig'],myDict['gam'],myDict['shl'],xdata)
4206    myDict[name] += delt
4207    y1 = getFCJVoigt3(myDict['pos'],myDict['sig'],myDict['gam'],myDict['shl'],xdata)
4208    hplot.plot(xdata,(y1-y0)/delt,'r+')
4209
4210if __name__ == '__main__':
4211    import GSASIItestplot as plot
4212    global plotter
4213    plotter = plot.PlotNotebook()
4214#    test0()
4215#    for name in ['int0','pos0','sig0','gam0','U','V','W','X','Y','Z','SH/L','I(L2)/I(L1)']:
4216    for name,shft in [['int0',0.1],['pos0',0.0001],['sig0',0.01],['gam0',0.00001],
4217        ['U',0.1],['V',0.01],['W',0.01],['X',0.0001],['Y',0.0001],['Z',0.0001],['SH/L',0.00005]]:
4218        test2(name,shft)
4219    for name,shft in [['pos',0.0001],['sig',0.01],['gam',0.0001],['shl',0.00005]]:
4220        test3(name,shft)
4221    G2fil.G2Print ("OK")
4222    plotter.StartEventLoop()
Note: See TracBrowser for help on using the repository browser.