source: trunk/GSASIIpwd.py @ 4519

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

G2fpaGUI - minor cleanup
G2lattice - test on 'T' instead of 'C' so that 'B' PWDR types properly handled
G2math - add getPinkalpha, getPinkbeta % deriv routines for pink beam peak shapes

modify setPeakparams to use them

G2plot - test on 'T' instead of 'C' so that 'B' PWDR types properly handled
G2pwd - add pink beam peak shape function - same as TOF peak shape; scale sig & gam to be in centidegrees

  • add wtFactor to FitPeaks? fix bad PWDR weights for peak fitting so reduced chi2 is more appropriate

G2pwdGUI - test on 'T' instead of 'C' so that 'B' PWDR types properly handled

  • add wtFactor to DoPeakFit? to fix bad PWDR weights for peak fitting so reduced chi2 is more appropriate

G2strMath - latest in incommensurate mag str factors - no real improvement

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