source: trunk/GSASIIstrIO.py @ 4861

Last change on this file since 4861 was 4861, checked in by toby, 3 years ago

fix Bob's RB constraint fix

  • Property svn:eol-style set to native
  • Property svn:keywords set to Date Author Revision URL Id
File size: 174.1 KB
Line 
1# -*- coding: utf-8 -*-
2########### SVN repository information ###################
3# $Date: 2021-03-21 01:20:23 +0000 (Sun, 21 Mar 2021) $
4# $Author: toby $
5# $Revision: 4861 $
6# $URL: trunk/GSASIIstrIO.py $
7# $Id: GSASIIstrIO.py 4861 2021-03-21 01:20:23Z toby $
8########### SVN repository information ###################
9'''
10*GSASIIstrIO: structure I/O routines*
11-------------------------------------
12
13Contains routines for reading from GPX files and printing to the .LST file.
14Used for refinements and in G2scriptable.
15
16Should not contain any wxpython references as this should be able to be used
17in non-GUI settings.
18
19'''
20from __future__ import division, print_function
21import platform
22import os
23import os.path as ospath
24import time
25import math
26import copy
27if '2' in platform.python_version_tuple()[0]:
28    import cPickle
29else:
30    import pickle as cPickle
31import numpy as np
32import numpy.ma as ma
33import GSASIIpath
34GSASIIpath.SetVersionNumber("$Revision: 4861 $")
35import GSASIIElem as G2el
36import GSASIIlattice as G2lat
37import GSASIIspc as G2spc
38import GSASIIobj as G2obj
39import GSASIImapvars as G2mv
40import GSASIImath as G2mth
41import GSASIIfiles as G2fil
42import GSASIIpy3 as G2py3
43
44sind = lambda x: np.sin(x*np.pi/180.)
45cosd = lambda x: np.cos(x*np.pi/180.)
46tand = lambda x: np.tan(x*np.pi/180.)
47asind = lambda x: 180.*np.arcsin(x)/np.pi
48acosd = lambda x: 180.*np.arccos(x)/np.pi
49atan2d = lambda y,x: 180.*np.arctan2(y,x)/np.pi
50   
51ateln2 = 8.0*math.log(2.0)
52
53#===============================================================================
54# Support for GPX file reading
55#===============================================================================
56def cPickleLoad(fp):
57    if '2' in platform.python_version_tuple()[0]:
58        return cPickle.load(fp)
59    else:
60       return cPickle.load(fp,encoding='latin-1')
61
62gpxIndex = {}; gpxNamelist = []; gpxSize = -1
63'''Global variables used in :func:`IndexGPX` to see if file has changed
64(gpxSize) and to index where to find each 1st-level tree item in the file.
65'''
66
67def GetFullGPX(GPXfile):
68    ''' Returns complete contents of GSASII gpx file.
69    Used in :func:`GSASIIscriptable.LoadDictFromProjFile`.
70
71    :param str GPXfile: full .gpx file name
72    :returns: Project,nameList, where
73
74      * Project (dict) is a representation of gpx file following the GSAS-II
75        tree structure for each item: key = tree name (e.g. 'Controls',
76        'Restraints', etc.), data is dict
77      * nameList (list) has names of main tree entries & subentries used to reconstruct project file
78    '''
79    return IndexGPX(GPXfile,read=True)
80
81def IndexGPX(GPXfile,read=False):
82    '''Create an index to a GPX file, optionally the file into memory.
83    The byte size of the GPX file is saved. If this routine is called
84    again, and if this size does not change, indexing is not repeated
85    since it is assumed the file has not changed (this can be overriden
86    by setting read=True).
87
88    :param str GPXfile: full .gpx file name
89    :returns: Project,nameList if read=, where
90
91      * Project (dict) is a representation of gpx file following the GSAS-II
92        tree structure for each item: key = tree name (e.g. 'Controls',
93        'Restraints', etc.), data is dict
94      * nameList (list) has names of main tree entries & subentries used to reconstruct project file
95    '''
96    global gpxSize
97    if gpxSize == os.path.getsize(GPXfile) and not read:
98        return
99    global gpxIndex
100    gpxIndex = {}
101    global gpxNamelist
102    gpxNamelist = []
103    if GSASIIpath.GetConfigValue('debug'): print("DBG: Indexing GPX file")
104    gpxSize = os.path.getsize(GPXfile)
105    fp = open(GPXfile,'rb')
106    Project = {}
107    try:
108        while True:
109            pos = fp.tell()
110            data = cPickleLoad(fp)
111            datum = data[0]
112            gpxIndex[datum[0]] = pos
113            if read: Project[datum[0]] = {'data':datum[1]}
114            gpxNamelist.append([datum[0],])
115            for datus in data[1:]:
116                if read: Project[datum[0]][datus[0]] = datus[1]
117                gpxNamelist[-1].append(datus[0])
118        # print('project load successful')
119    except EOFError:
120        pass
121    except Exception as msg:
122        G2fil.G2Print('Read Error:',msg)
123        raise Exception("Error reading file "+str(GPXfile)+". This is not a GSAS-II .gpx file")
124    finally:
125        fp.close()
126    if read: return Project,gpxNamelist   
127   
128def GetControls(GPXfile):
129    ''' Returns dictionary of control items found in GSASII gpx file
130
131    :param str GPXfile: full .gpx file name
132    :return: dictionary of control items
133    '''
134    Controls = copy.deepcopy(G2obj.DefaultControls)
135    IndexGPX(GPXfile)
136    pos = gpxIndex.get('Controls')
137    if pos is None:
138        G2fil.G2Print('Warning: Controls not found in gpx file {}'.format(GPXfile))
139        return Controls
140    fp = open(GPXfile,'rb')
141    fp.seek(pos)
142    datum = cPickleLoad(fp)[0]
143    fp.close()
144    Controls.update(datum[1])
145    return Controls
146
147def GetConstraints(GPXfile):
148    '''Read the constraints from the GPX file and interpret them
149
150    called in :func:`ReadCheckConstraints`, :func:`GSASIIstrMain.Refine`
151    and :func:`GSASIIstrMain.SeqRefine`.
152    '''
153    IndexGPX(GPXfile)
154    fl = open(GPXfile,'rb')
155    pos = gpxIndex.get('Constraints')
156    if pos is None:
157        raise Exception("No constraints in GPX file")
158    fl.seek(pos)
159    datum = cPickleLoad(fl)[0]
160    fl.close()
161    constList = []
162    for item in datum[1]:
163        if item.startswith('_'): continue
164        constList += datum[1][item]
165    constDict,fixedList,ignored = ProcessConstraints(constList)
166    if ignored:
167        G2fil.G2Print ('Warning: {} Constraints were rejected. Was a constrained phase, histogram or atom deleted?'.format(ignored))
168    return constDict,fixedList
169   
170def ProcessConstraints(constList):
171    """Interpret the constraints in the constList input into a dictionary, etc.
172    All :class:`GSASIIobj.G2VarObj` objects are mapped to the appropriate
173    phase/hist/atoms based on the object internals (random Ids). If this can't be
174    done (if a phase has been deleted, etc.), the variable is ignored.
175    If the constraint cannot be used due to too many dropped variables,
176    it is counted as ignored.
177    NB: this processing does not include symmetry imposed constraints
178   
179    :param list constList: a list of lists where each item in the outer list
180      specifies a constraint of some form, as described in the :mod:`GSASIIobj`
181      :ref:`Constraint definition <Constraint_definitions_table>`.
182
183    :returns:  a tuple of (constDict,fixedList,ignored) where:
184     
185      * constDict (list of dicts) contains the constraint relationships
186      * fixedList (list) contains the fixed values for each type
187        of constraint.
188      * ignored (int) counts the number of invalid constraint items
189        (should always be zero!)
190    """
191    constDict = []
192    fixedList = []
193    ignored = 0
194    namedVarList = []
195    for item in constList:
196        if item[-1] == 'h':
197            # process a hold
198            fixedList.append('0')
199            var = str(item[0][1])
200            if '?' not in var:
201                constDict.append({var:0.0})
202            else:
203                ignored += 1
204        elif item[-1] == 'f':
205            # process a new variable
206            fixedList.append(None)
207            D = {}
208            varyFlag = item[-2]
209            varname = item[-3]
210            for term in item[:-3]:
211                var = str(term[1])
212                if '?' not in var:
213                    D[var] = term[0]
214            if len(D) > 1:
215                # add extra dict terms for input variable name and vary flag
216                if varname is not None:
217                    varname = str(varname) # in case this is a G2VarObj
218                    if varname.startswith(':'):
219                        D['_name'] = varname
220                    else:
221                        D['_name'] = '::nv-' + varname
222                    D['_name'] = G2obj.MakeUniqueLabel(D['_name'],namedVarList)
223                D['_vary'] = varyFlag == True # force to bool
224                constDict.append(D)
225            else:
226                ignored += 1
227            #constFlag[-1] = ['Vary']
228        elif item[-1] == 'c': 
229            # process a contraint relationship
230            D = {}
231            for term in item[:-3]:
232                var = str(term[1])
233                if '?' not in var:
234                    D[var] = term[0]
235            if len(D) >= 1:
236                fixedList.append(str(item[-3]))
237                constDict.append(D)
238            else:
239                ignored += 1
240        elif item[-1] == 'e':
241            # process an equivalence
242            firstmult = None
243            eqlist = []
244            for term in item[:-3]:
245                if term[0] == 0: term[0] = 1.0
246                var = str(term[1])
247                if '?' in var: continue
248                if firstmult is None:
249                    firstmult = term[0]
250                    firstvar = var
251                else:
252                    eqlist.append([var,firstmult/term[0]])
253            if len(eqlist) > 0:
254                G2mv.StoreEquivalence(firstvar,eqlist,False)
255            else:
256                ignored += 1
257        else:
258            ignored += 1
259    return constDict,fixedList,ignored
260
261def ReadCheckConstraints(GPXfile, seqHist=None):
262    '''Load constraints and related info and return any error or warning messages
263    This is done from the GPX file rather than the tree.
264
265    :param dict seqHist: defines a specific histogram to be loaded for a sequential
266       refinement, if None (default) all are loaded.
267    '''
268    # init constraints
269    G2mv.InitVars()   
270    # get variables
271    Histograms,Phases = GetUsedHistogramsAndPhases(GPXfile)
272    if not Phases:
273        return 'Error: No phases or no histograms in phases!',''
274    if not Histograms:
275        return 'Error: no diffraction data',''
276    constrDict,fixedList = GetConstraints(GPXfile) # load user constraints before internally generated ones
277    if seqHist: Histograms = {seqHist:Histograms[seqHist]}  # sequential fit: only need one histogram
278    rigidbodyDict = GetRigidBodies(GPXfile)
279    rbIds = rigidbodyDict.get('RBIds',{'Vector':[],'Residue':[]})
280    rbVary,rbDict = GetRigidBodyModels(rigidbodyDict,Print=False)
281    Natoms,atomIndx,phaseVary,phaseDict,pawleyLookup,FFtables,BLtables,MFtables,maxSSwave = \
282        GetPhaseData(Phases,RestraintDict=None,rbIds=rbIds,Print=False) # generates atom symmetry constraints
283    hapVary,hapDict,controlDict = GetHistogramPhaseData(Phases,Histograms,Print=False,resetRefList=False)
284    histVary,histDict,controlDict = GetHistogramData(Histograms,Print=False)
285    varyList = rbVary+phaseVary+hapVary+histVary
286    msg = G2mv.EvaluateMultipliers(constrDict,phaseDict,hapDict,histDict)
287    if msg:
288        return 'Unable to interpret multiplier(s): '+msg,''
289    errmsg, warnmsg = G2mv.CheckConstraints(varyList,constrDict,fixedList)
290    if errmsg:
291        # print some diagnostic info on the constraints
292        G2fil.G2Print('Error in constraints:\n'+errmsg+
293              '\nRefinement not possible due to conflict in constraints, see below:\n')
294        G2fil.G2Print(G2mv.VarRemapShow(varyList,True),mode='error')
295    return errmsg, warnmsg
296   
297def makeTwinFrConstr(Phases,Histograms,hapVary):
298    TwConstr = []
299    TwFixed = []
300    for Phase in Phases:
301        pId = Phases[Phase]['pId']
302        for Histogram in Phases[Phase]['Histograms']:
303            try:
304                hId = Histograms[Histogram]['hId']
305                phfx = '%d:%d:'%(pId,hId)
306                if phfx+'TwinFr:0' in hapVary:
307                    TwFixed.append('1.0')     #constraint value
308                    nTwin = len(Phases[Phase]['Histograms'][Histogram]['Twins'])
309                    TwConstr.append({phfx+'TwinFr:'+str(i):'1.0' for i in range(nTwin)})
310            except KeyError:    #unused histograms?
311                pass
312    return TwConstr,TwFixed   
313   
314def GetRestraints(GPXfile):
315    '''Read the restraints from the GPX file.
316    Throws an exception if not found in the .GPX file
317    '''
318    IndexGPX(GPXfile)
319    fl = open(GPXfile,'rb')
320    pos = gpxIndex.get('Restraints')
321    if pos is None:
322        raise Exception("No Restraints in GPX file")
323    fl.seek(pos)
324    datum = cPickleLoad(fl)[0]
325    fl.close()
326    return datum[1]
327   
328def GetRigidBodies(GPXfile):
329    '''Read the rigid body models from the GPX file
330    '''
331    IndexGPX(GPXfile)
332    fl = open(GPXfile,'rb')
333    pos = gpxIndex.get('Rigid bodies')
334    if pos is None:
335        raise Exception("No Rigid bodies in GPX file")
336    fl.seek(pos)
337    datum = cPickleLoad(fl)[0]
338    fl.close()
339    return datum[1]
340       
341def GetFprime(controlDict,Histograms):
342    'Needs a doc string'
343    FFtables = controlDict['FFtables']
344    if not FFtables:
345        return
346    histoList = list(Histograms.keys())
347    histoList.sort()
348    for histogram in histoList:
349        if histogram[:4] in ['PWDR','HKLF']:
350            Histogram = Histograms[histogram]
351            hId = Histogram['hId']
352            hfx = ':%d:'%(hId)
353            if 'X' in controlDict[hfx+'histType']:
354                keV = controlDict[hfx+'keV']
355                for El in FFtables:
356                    Orbs = G2el.GetXsectionCoeff(El.split('+')[0].split('-')[0])
357                    FP,FPP,Mu = G2el.FPcalc(Orbs, keV)
358                    FFtables[El][hfx+'FP'] = FP
359                    FFtables[El][hfx+'FPP'] = FPP
360                   
361def PrintFprime(FFtables,pfx,pFile):
362    pFile.write('\n Resonant form factors:(ref: D.T. Cromer & D.A. Liberman (1981), Acta Cryst. A37, 267-268.)\n')
363    Elstr = ' Element:'
364    FPstr = " f'     :"
365    FPPstr = ' f"     :'
366    for El in FFtables:
367        Elstr += ' %8s'%(El)
368        FPstr += ' %8.3f'%(FFtables[El][pfx+'FP'])
369        FPPstr += ' %8.3f'%(FFtables[El][pfx+'FPP'])
370    pFile.write(Elstr+'\n')
371    pFile.write(FPstr+'\n')
372    pFile.write(FPPstr+'\n')
373   
374def PrintBlength(BLtables,wave,pFile):
375    pFile.write('\n Resonant neutron scattering lengths:\n')
376    Elstr = ' Element:'
377    FPstr = " b'     :"
378    FPPstr = ' b"     :'
379    for El in BLtables:
380        BP,BPP = G2el.BlenResCW([El,],BLtables,wave)
381        Elstr += ' %8s'%(El)
382        FPstr += ' %8.3f'%(BP)
383        FPPstr += ' %8.3f'%(BPP)
384    pFile.write(Elstr+'\n')
385    pFile.write(FPstr+'\n')
386    pFile.write(FPPstr+'\n')
387
388def PrintISOmodes(pFile,Phases,parmDict,sigDict):
389    '''Prints the values for the ISODISTORT modes into the project's
390    .lst file after a refinement.
391    '''
392    for phase in Phases:
393        if 'ISODISTORT' not in Phases[phase]: continue
394        data = Phases[phase]
395        ISO = data['ISODISTORT']
396        if 'G2VarList' in ISO:
397            deltaList = []
398            for gv,Ilbl in zip(ISO['G2VarList'],ISO['IsoVarList']):
399                dvar = gv.varname()
400                var = dvar.replace('::dA','::A')
401                albl = Ilbl[:Ilbl.rfind('_')]
402                v = Ilbl[Ilbl.rfind('_')+1:]
403                pval = ISO['ParentStructure'][albl][['dx','dy','dz'].index(v)]
404                if var in parmDict:
405                    cval = parmDict[var]
406                else:
407                    print('PrintISOmodes Error: Atom not found',"No value found for parameter "+str(var))
408                    return
409                deltaList.append(cval-pval)
410            modeVals = np.inner(ISO['Var2ModeMatrix'],deltaList)
411           
412        if 'G2OccVarList' in ISO:
413            deltaOccList = []
414            for gv,Ilbl in zip(ISO['G2OccVarList'],ISO['OccVarList']):
415                var = gv.varname()
416                albl = Ilbl[:Ilbl.rfind('_')]
417                pval = ISO['BaseOcc'][albl]
418                if var in parmDict:
419                    cval = parmDict[var]
420                else:
421                    print('PrintISOmodes Error: Atom not found',"No value found for parameter "+str(var))
422                    return
423                deltaOccList.append(cval-pval)
424            modeOccVals = np.inner(ISO['Var2OccMatrix'],deltaOccList)
425           
426        if 'G2VarList' in ISO:
427            pFile.write('\n ISODISTORT Displacive Modes for phase {}\n'.format(data['General'].get('Name','')))
428            l = str(max([len(i) for i in ISO['IsoModeList']])+3)
429            fmt = '  {:'+l+'}{}'
430            for var,val,norm,G2mode in zip(
431                    ISO['IsoModeList'],modeVals,ISO['NormList'],ISO['G2ModeList'] ):
432                try:
433                    value = G2py3.FormatSigFigs(val/norm)
434                    if str(G2mode) in sigDict:
435                        value = G2mth.ValEsd(val/norm,sigDict[str(G2mode)]/norm)
436                except TypeError:
437                    value = '?'
438                pFile.write(fmt.format(var,value)+'\n')
439               
440        if 'G2OccVarList' in ISO:
441            pFile.write('\n ISODISTORT Occupancy Modes for phase {}\n'.format(data['General'].get('Name','')))
442            l = str(max([len(i) for i in ISO['OccModeList']])+3)
443            fmt = '  {:'+l+'}{}'
444            for var,val,norm,G2mode in zip(
445                    ISO['OccModeList'],modeOccVals,ISO['OccNormList'],ISO['G2OccModeList'] ):
446                try:
447                    value = G2py3.FormatSigFigs(val/norm)
448                    if str(G2mode) in sigDict:
449                        value = G2mth.ValEsd(val/norm,sigDict[str(G2mode)]/norm)
450                except TypeError:
451                    value = '?'
452                pFile.write(fmt.format(var,value)+'\n')
453   
454def GetPhaseNames(GPXfile):
455    ''' Returns a list of phase names found under 'Phases' in GSASII gpx file
456
457    :param str GPXfile: full .gpx file name
458    :return: list of phase names
459    '''
460    IndexGPX(GPXfile)
461    fl = open(GPXfile,'rb')
462    pos = gpxIndex.get('Phases')
463    if pos is None:
464        raise Exception("No Phases in GPX file")
465    fl.seek(pos)
466    data = cPickleLoad(fl)
467    fl.close()
468    return [datus[0] for datus in data[1:]]
469
470def GetAllPhaseData(GPXfile,PhaseName):
471    ''' Returns the entire dictionary for PhaseName from GSASII gpx file
472
473    :param str GPXfile: full .gpx file name
474    :param str PhaseName: phase name
475    :return: phase dictionary or None if PhaseName is not present
476    '''       
477    IndexGPX(GPXfile)
478    fl = open(GPXfile,'rb')
479    pos = gpxIndex.get('Phases')
480    if pos is None:
481        raise Exception("No Phases in GPX file")
482    fl.seek(pos)
483    data = cPickleLoad(fl)
484    fl.close()
485
486    for datus in data[1:]:
487        if datus[0] == PhaseName:
488            return datus[1]
489   
490def GetHistograms(GPXfile,hNames):
491    """ Returns a dictionary of histograms found in GSASII gpx file
492
493    :param str GPXfile: full .gpx file name
494    :param str hNames: list of histogram names
495    :return: dictionary of histograms (types = PWDR & HKLF)
496
497    """
498    IndexGPX(GPXfile)
499    fl = open(GPXfile,'rb')
500    Histograms = {}
501    for hist in hNames:
502        pos = gpxIndex.get(hist)
503        if pos is None:
504            raise Exception("Histogram {} not found in GPX file".format(hist))
505        fl.seek(pos)
506        data = cPickleLoad(fl)
507        datum = data[0]
508        if 'PWDR' in hist[:4]:
509            PWDRdata = {}
510            PWDRdata.update(datum[1][0])        #weight factor
511            PWDRdata['Data'] = ma.array(ma.getdata(datum[1][1]))          #masked powder data arrays/clear previous masks
512            PWDRdata[data[2][0]] = data[2][1]       #Limits & excluded regions (if any)
513            PWDRdata[data[3][0]] = data[3][1]       #Background
514            PWDRdata[data[4][0]] = data[4][1]       #Instrument parameters
515            PWDRdata[data[5][0]] = data[5][1]       #Sample parameters
516            try:
517               PWDRdata[data[9][0]] = data[9][1]       #Reflection lists might be missing
518            except IndexError:
519                PWDRdata['Reflection Lists'] = {}
520            PWDRdata['Residuals'] = {}
521            Histograms[hist] = PWDRdata
522        elif 'HKLF' in hist[:4]:
523            HKLFdata = {}
524            HKLFdata.update(datum[1][0])        #weight factor
525#patch
526            if 'list' in str(type(datum[1][1])):
527            #if isinstance(datum[1][1],list):
528                RefData = {'RefList':[],'FF':{}}
529                for ref in datum[1][1]:
530                    RefData['RefList'].append(ref[:11]+[ref[13],])
531                RefData['RefList'] = np.array(RefData['RefList'])
532                datum[1][1] = RefData
533#end patch
534            datum[1][1]['FF'] = {}
535            HKLFdata['Data'] = datum[1][1]
536            HKLFdata[data[1][0]] = data[1][1]       #Instrument parameters
537            HKLFdata['Reflection Lists'] = None
538            HKLFdata['Residuals'] = {}
539            Histograms[hist] = HKLFdata           
540    fl.close()
541    return Histograms
542   
543def GetHistogramNames(GPXfile,hTypes):
544    """ Returns a list of histogram names found in a GSAS-II .gpx file that
545    match specifed histogram types. Names are returned in the order they
546    appear in the file.
547
548    :param str GPXfile: full .gpx file name
549    :param str hTypes: list of histogram types
550    :return: list of histogram names (types = PWDR & HKLF)
551
552    """
553    IndexGPX(GPXfile)
554    return [n[0] for n in gpxNamelist if n[0][:4] in hTypes]
555   
556def GetUsedHistogramsAndPhases(GPXfile):
557    ''' Returns all histograms that are found in any phase
558    and any phase that uses a histogram. This also
559    assigns numbers to used phases and histograms by the
560    order they appear in the file.
561
562    :param str GPXfile: full .gpx file name
563    :returns: (Histograms,Phases)
564
565     * Histograms = dictionary of histograms as {name:data,...}
566     * Phases = dictionary of phases that use histograms
567
568    '''
569    phaseNames = GetPhaseNames(GPXfile)
570    histoList = GetHistogramNames(GPXfile,['PWDR','HKLF'])
571    allHistograms = GetHistograms(GPXfile,histoList)
572    phaseData = {}
573    for name in phaseNames: 
574        phaseData[name] =  GetAllPhaseData(GPXfile,name)
575    Histograms = {}
576    Phases = {}
577    for phase in phaseData:
578        Phase = phaseData[phase]
579        if Phase['General']['Type'] == 'faulted': continue      #don't use faulted phases!
580        if Phase['Histograms']:
581            for hist in Phase['Histograms']:
582                if 'Use' not in Phase['Histograms'][hist]:      #patch
583                    Phase['Histograms'][hist]['Use'] = True
584                if Phase['Histograms'][hist]['Use'] and phase not in Phases:
585                    pId = phaseNames.index(phase)
586                    Phase['pId'] = pId
587                    Phases[phase] = Phase
588                if hist not in Histograms and Phase['Histograms'][hist]['Use']:
589                    try:
590                        Histograms[hist] = allHistograms[hist]
591                        hId = histoList.index(hist)
592                        Histograms[hist]['hId'] = hId
593                    except KeyError: # would happen if a referenced histogram were
594                        # renamed or deleted
595                        G2fil.G2Print('Warning: For phase "'+phase+
596                              '" unresolved reference to histogram "'+hist+'"')
597    # load the fix background info into the histograms
598    for hist in Histograms:
599        if 'Background' not in Histograms[hist]: continue
600        fixedBkg = Histograms[hist]['Background'][1].get('background PWDR')
601        if fixedBkg:
602            if not fixedBkg[0]: continue
603            # patch: add refinement flag, if needed
604            if len(fixedBkg) == 2: fixedBkg += [False]
605            h = Histograms[hist]['Background'][1]
606            try:
607                Limits = Histograms[hist]['Limits'][1]
608                x = Histograms[hist]['Data'][0]
609                xB = np.searchsorted(x,Limits[0])
610                xF = np.searchsorted(x,Limits[1])+1
611                h['fixback'] = allHistograms[fixedBkg[0]]['Data'][1][xB:xF]
612            except KeyError: # would happen if a referenced histogram were renamed or deleted
613                G2fil.G2Print('Warning: For hist "{}" unresolved background reference to hist "{}"'
614                          .format(hist,fixedBkg[0]))
615    G2obj.IndexAllIds(Histograms=Histograms,Phases=Phases)
616    return Histograms,Phases
617   
618def getBackupName(GPXfile,makeBack):
619    '''
620    Get the name for the backup .gpx file name
621   
622    :param str GPXfile: full .gpx file name
623    :param bool makeBack: if True the name of a new file is returned, if
624      False the name of the last file that exists is returned
625    :returns: the name of a backup file
626   
627    '''
628    GPXpath,GPXname = ospath.split(GPXfile)
629    if GPXpath == '': GPXpath = '.'
630    Name = ospath.splitext(GPXname)[0]
631    files = os.listdir(GPXpath)
632    last = 0
633    for name in files:
634        name = name.split('.')
635        if len(name) == 3 and name[0] == Name and 'bak' in name[1]:
636            if makeBack:
637                last = max(last,int(name[1].strip('bak'))+1)
638            else:
639                last = max(last,int(name[1].strip('bak')))
640    GPXback = ospath.join(GPXpath,ospath.splitext(GPXname)[0]+'.bak'+str(last)+'.gpx')
641    return GPXback   
642       
643def GPXBackup(GPXfile,makeBack=True):
644    '''
645    makes a backup of the specified .gpx file
646   
647    :param str GPXfile: full .gpx file name
648    :param bool makeBack: if True (default), the backup is written to
649      a new file; if False, the last backup is overwritten
650    :returns: the name of the backup file that was written
651    '''
652    import distutils.file_util as dfu
653    GPXback = getBackupName(GPXfile,makeBack)
654    tries = 0
655    while True:
656        try:
657            dfu.copy_file(GPXfile,GPXback)
658            break
659        except:
660            tries += 1
661            if tries > 10:
662                return GPXfile  #failed!
663            time.sleep(1)           #just wait a second!         
664    return GPXback
665
666def SetUsedHistogramsAndPhases(GPXfile,Histograms,Phases,RigidBodies,CovData,parmFrozenList,makeBack=True):
667    ''' Updates gpxfile from all histograms that are found in any phase
668    and any phase that used a histogram. Also updates rigid body definitions.
669    This is used for non-sequential fits, but not for sequential fitting.
670
671    :param str GPXfile: full .gpx file name
672    :param dict Histograms: dictionary of histograms as {name:data,...}
673    :param dict Phases: dictionary of phases that use histograms
674    :param dict RigidBodies: dictionary of rigid bodies
675    :param dict CovData: dictionary of refined variables, varyList, & covariance matrix
676    :param list parmFrozenList: list of parameters (as str) that are frozen
677      due to limits; converted to :class:`GSASIIobj.G2VarObj` objects.
678    :param bool makeBack: True if new backup of .gpx file is to be made; else
679      use the last one made
680    '''
681                       
682    import distutils.file_util as dfu
683    GPXback = GPXBackup(GPXfile,makeBack)
684    G2fil.G2Print ('Read from file:'+GPXback)
685    G2fil.G2Print ('Save to file  :'+GPXfile)
686    infile = open(GPXback,'rb')
687    outfile = open(GPXfile,'wb')
688    while True:
689        try:
690            data = cPickleLoad(infile)
691        except EOFError:
692            break
693        datum = data[0]
694#        print 'read: ',datum[0]
695        if datum[0] == 'Phases':
696            for iphase in range(len(data)):
697                if data[iphase][0] in Phases:
698                    phaseName = data[iphase][0]
699                    data[iphase][1].update(Phases[phaseName])
700        elif datum[0] == 'Covariance':
701            data[0][1] = CovData
702        elif datum[0] == 'Rigid bodies':
703            data[0][1] = RigidBodies
704        elif datum[0] == 'Controls':
705            Controls = data[0][1]
706            if 'parmFrozen' not in Controls:
707                Controls['parmFrozen'] = {}
708            Controls['parmFrozen']['FrozenList'] = [
709                    i if type(i) is G2obj.G2VarObj
710                    else G2obj.G2VarObj(i)
711                    for i in parmFrozenList]
712        try:
713            histogram = Histograms[datum[0]]
714#            print 'found ',datum[0]
715            data[0][1][1] = histogram['Data']
716            data[0][1][0].update(histogram['Residuals'])
717            for datus in data[1:]:
718#                print '    read: ',datus[0]
719                if datus[0] in ['Instrument Parameters','Sample Parameters','Reflection Lists']:
720                    datus[1] = histogram[datus[0]]
721                if datus[0] == 'Background': # remove fixed background from file
722                    d1 = {key:histogram['Background'][1][key]
723                              for key in histogram['Background'][1]
724                              if not key.startswith('_fixed')}               
725                    datus[1] = copy.deepcopy(histogram['Background'])
726                    datus[1][1] = d1
727        except KeyError:
728            pass
729        try:                       
730            cPickle.dump(data,outfile,1)
731        except AttributeError:
732            G2fil.G2Print ('ERROR - bad data in least squares result')
733            infile.close()
734            outfile.close()
735            dfu.copy_file(GPXback,GPXfile)
736            G2fil.G2Print ('GPX file save failed - old version retained',mode='error')
737            return
738       
739    infile.close()
740    outfile.close()
741           
742    G2fil.G2Print ('GPX file save successful')
743   
744def GetSeqResult(GPXfile):
745    '''
746    Returns the sequential results table information from a GPX file.
747    Called at the beginning of :meth:`GSASIIstrMain.SeqRefine`
748   
749    :param str GPXfile: full .gpx file name
750    :returns: a dict containing the sequential results table
751    '''
752    IndexGPX(GPXfile)
753    pos = gpxIndex.get('Sequential results')
754    if pos is None:
755        return {}
756    fl = open(GPXfile,'rb')
757    fl.seek(pos)
758    datum = cPickleLoad(fl)[0]
759    fl.close()
760    return datum[1]
761   
762def SetupSeqSavePhases(GPXfile):
763    '''Initialize the files used to save intermediate results from
764    sequential fits.
765    '''
766    IndexGPX(GPXfile)
767    # load initial Phase results from GPX
768    fl = open(GPXfile,'rb')
769    pos = gpxIndex.get('Phases')
770    if pos is None:
771        raise Exception("No Phases in GPX file")
772    fl.seek(pos)
773    data = cPickleLoad(fl)
774    fl.close()
775    # create GPX-like file to store latest Phase info; init with start vals
776    GPXphase = os.path.splitext(GPXfile)[0]+'.seqPhase'
777    fp = open(GPXphase,'wb')
778    cPickle.dump(data,fp,1)
779    fp.close()
780    # create empty file for histogram info
781    GPXhist = os.path.splitext(GPXfile)[0]+'.seqHist'
782    fp = open(GPXhist,'wb')
783    fp.close()
784
785def SaveUpdatedHistogramsAndPhases(GPXfile,Histograms,Phases,RigidBodies,CovData,parmFrozen):
786    '''
787    Save phase and histogram information into "pseudo-gpx" files. The phase
788    information is overwritten each time this is called, but histogram information is
789    appended after each sequential step.
790
791    :param str GPXfile: full .gpx file name
792    :param dict Histograms: dictionary of histograms as {name:data,...}
793    :param dict Phases: dictionary of phases that use histograms
794    :param dict RigidBodies: dictionary of rigid bodies
795    :param dict CovData: dictionary of refined variables, varyList, & covariance matrix
796    :param dict parmFrozen: dict with frozen parameters for all phases
797      and histograms (specified as str values)
798    '''
799                       
800    import distutils.file_util as dfu
801   
802    GPXphase = os.path.splitext(GPXfile)[0]+'.seqPhase'
803    fp = open(GPXphase,'rb')
804    data = cPickleLoad(fp) # first block in file should be Phases
805    if data[0][0] != 'Phases':
806        raise Exception('Unexpected block in {} file. How did this happen?'
807                            .format(GPXphase))
808    fp.close()
809    # update previous phase info
810    for datum in data[1:]: 
811        if datum[0] in Phases:
812            datum[1].update(Phases[datum[0]])
813    # save latest Phase/refinement info
814    fp = open(GPXphase,'wb')
815    cPickle.dump(data,fp,1)
816    cPickle.dump([['Covariance',CovData]],fp,1)
817    cPickle.dump([['Rigid bodies',RigidBodies]],fp,1)
818    cPickle.dump([['parmFrozen',parmFrozen]],fp,1)
819    fp.close()
820    # create an entry that looks like a PWDR tree item
821    for key in Histograms:
822        if key.startswith('PWDR '):
823            break
824    else:
825        raise Exception('No PWDR entry in Histogram dict!')
826    histname = key
827    hist = copy.deepcopy(Histograms[key])
828    xfer_dict = {'Index Peak List': [[], []],
829                   'Comments': [],
830                   'Unit Cells List': [],
831                   'Peak List': {'peaks': [], 'sigDict': {}},
832                   }
833    histData = hist['Data']
834    del hist['Data']
835    for key in ('Limits','Background','Instrument Parameters',
836                    'Sample Parameters','Reflection Lists'):
837        xfer_dict[key] = hist[key]
838        if key == 'Background':  # remove fixed background from file
839            xfer_dict['Background'][1] = {k:hist['Background'][1][k]
840                      for k in hist['Background'][1]
841                      if not k.startswith('_fixed')}               
842        del hist[key]
843    # xform into a gpx-type entry
844    data = []
845    data.append([histname,[hist,histData,histname]])       
846    for key in ['Comments','Limits','Background','Instrument Parameters',
847             'Sample Parameters','Peak List','Index Peak List',
848             'Unit Cells List','Reflection Lists']:
849        data.append([key,xfer_dict[key]])
850    # append histogram to histogram info
851    GPXhist = os.path.splitext(GPXfile)[0]+'.seqHist'
852    fp = open(GPXhist,'ab')
853    cPickle.dump(data,fp,1)
854    fp.close()
855    return
856   
857def SetSeqResult(GPXfile,Histograms,SeqResult):
858    '''
859    Places the sequential results information into a GPX file
860    after a refinement has been completed.
861    Called at the end of :meth:`GSASIIstrMain.SeqRefine`
862
863    :param str GPXfile: full .gpx file name
864    '''
865    GPXback = GPXBackup(GPXfile)
866    G2fil.G2Print ('Read from file:'+GPXback)
867    G2fil.G2Print ('Save to file  :'+GPXfile)
868    GPXphase = os.path.splitext(GPXfile)[0]+'.seqPhase'
869    fp = open(GPXphase,'rb')
870    data = cPickleLoad(fp) # first block in file should be Phases
871    if data[0][0] != 'Phases':
872        raise Exception('Unexpected block in {} file. How did this happen?'.format(GPXphase))
873    Phases = {}
874    for name,vals in data[1:]:
875        Phases[name] = vals       
876    name,CovData = cPickleLoad(fp)[0] # 2nd block in file should be Covariance
877    name,RigidBodies = cPickleLoad(fp)[0] # 3rd block in file should be Rigid Bodies
878    name,parmFrozenDict = cPickleLoad(fp)[0] # 4th block in file should be frozen parameters
879    fp.close()
880    GPXhist = os.path.splitext(GPXfile)[0]+'.seqHist'
881    hist = open(GPXhist,'rb')
882    # build an index to the GPXhist file
883    histIndex = {}
884    while True:
885        loc = hist.tell()
886        try:
887            datum = cPickleLoad(hist)[0]
888        except EOFError:
889            break
890        histIndex[datum[0]] = loc
891
892    infile = open(GPXback,'rb')
893    outfile = open(GPXfile,'wb')
894    while True:
895        try:
896            data = cPickleLoad(infile)
897        except EOFError:
898            break
899        datum = data[0]
900        if datum[0] == 'Sequential results':
901            data[0][1] = SeqResult
902        elif datum[0] == 'Phases':
903            for pdata in data[1:]:
904                if pdata[0] in Phases:
905                    pdata[1].update(Phases[pdata[0]])
906        elif datum[0] == 'Covariance':
907            data[0][1] = CovData
908        elif datum[0] == 'Rigid bodies':
909            data[0][1] = RigidBodies
910        elif datum[0] == 'Controls': # reset the Copy Next flag after a sequential fit
911            Controls = data[0][1]
912            Controls['Copy2Next'] = False
913            for key in parmFrozenDict:
914                Controls['parmFrozen'][key] = [
915                    i if type(i) is G2obj.G2VarObj
916                    else G2obj.G2VarObj(i)
917                    for i in parmFrozenDict[key]]
918        elif datum[0] in histIndex:
919            hist.seek(histIndex[datum[0]])
920            hdata = cPickleLoad(hist)
921            if data[0][0] != hdata[0][0]:
922                G2fil.G2Print('Error! Updating {} with {}'.format(data[0][0],hdata[0][0]))
923            data[0] = hdata[0]
924            xferItems = ['Background','Instrument Parameters','Sample Parameters','Reflection Lists']
925            hItems = {name:j+1 for j,(name,val) in enumerate(hdata[1:]) if name in xferItems}
926            for j,(name,val) in enumerate(data[1:]):
927                if name not in xferItems: continue
928                data[j+1][1] = hdata[hItems[name]][1]
929        cPickle.dump(data,outfile,1)
930    hist.close()
931    infile.close()
932    outfile.close()
933    # clean up tmp files
934    try:
935        os.remove(GPXphase)
936    except:
937        G2fil.G2Print('Warning: unable to delete {}'.format(GPXphase))
938    try:
939        os.remove(GPXhist)
940    except:
941        G2fil.G2Print('Warning: unable to delete {}'.format(GPXhist))
942    G2fil.G2Print ('GPX file merge completed')
943
944#==============================================================================
945# Refinement routines
946#==============================================================================
947def ShowBanner(pFile=None):
948    'Print authorship, copyright and citation notice'
949    pFile.write(80*'*'+'\n')
950    pFile.write('   General Structure Analysis System-II Crystal Structure Refinement\n')
951    pFile.write('              by Robert B. Von Dreele & Brian H. Toby\n')
952    pFile.write('                Argonne National Laboratory(C), 2010\n')
953    pFile.write(' This product includes software developed by the UChicago Argonne, LLC,\n')
954    pFile.write('            as Operator of Argonne National Laboratory.\n')
955    pFile.write('                          Please cite:\n')
956    pFile.write('   B.H. Toby & R.B. Von Dreele, J. Appl. Cryst. 46, 544-549 (2013)\n')
957
958    pFile.write(80*'*'+'\n')
959
960def ShowControls(Controls,pFile=None,SeqRef=False,preFrozenCount=0):
961    'Print controls information'
962    pFile.write(' Least squares controls:\n')
963    pFile.write(' Refinement type: %s\n'%Controls['deriv type'])
964    if 'Hessian' in Controls['deriv type']:
965        pFile.write(' Maximum number of cycles: %d\n'%Controls['max cyc'])
966    else:
967        pFile.write(' Minimum delta-M/M for convergence: %.2g\n'%(Controls['min dM/M']))
968    pFile.write(' Regularize hydrogens (if any): %s\n'%Controls.get('HatomFix',False))
969    pFile.write(' Initial shift factor: %.3f\n'%(Controls['shift factor']))
970    if SeqRef:
971        pFile.write(' Sequential refinement controls:\n')
972        pFile.write(' Copy of histogram results to next: %s\n'%(Controls['Copy2Next']))
973        pFile.write(' Process histograms in reverse order: %s\n'%(Controls['Reverse Seq']))
974    if preFrozenCount:
975        pFile.write('\n Starting refinement with {} Frozen variables\n\n'.format(preFrozenCount))
976   
977def GetPawleyConstr(SGLaue,PawleyRef,im,pawleyVary):
978    'needs a doc string'
979#    if SGLaue in ['-1','2/m','mmm']:
980#        return                      #no Pawley symmetry required constraints
981    eqvDict = {}
982    for i,varyI in enumerate(pawleyVary):
983        eqvDict[varyI] = []
984        refI = int(varyI.split(':')[-1])
985        ih,ik,il = PawleyRef[refI][:3]
986        dspI = PawleyRef[refI][4+im]
987        for varyJ in pawleyVary[i+1:]:
988            refJ = int(varyJ.split(':')[-1])
989            jh,jk,jl = PawleyRef[refJ][:3]
990            dspJ = PawleyRef[refJ][4+im]
991            if SGLaue in ['4/m','4/mmm']:
992                isum = ih**2+ik**2
993                jsum = jh**2+jk**2
994                if abs(il) == abs(jl) and isum == jsum:
995                    eqvDict[varyI].append(varyJ) 
996            elif SGLaue in ['3R','3mR']:
997                isum = ih**2+ik**2+il**2
998                jsum = jh**2+jk**2+jl**2
999                isum2 = ih*ik+ih*il+ik*il
1000                jsum2 = jh*jk+jh*jl+jk*jl
1001                if isum == jsum and isum2 == jsum2:
1002                    eqvDict[varyI].append(varyJ) 
1003            elif SGLaue in ['3','3m1','31m','6/m','6/mmm']:
1004                isum = ih**2+ik**2+ih*ik
1005                jsum = jh**2+jk**2+jh*jk
1006                if abs(il) == abs(jl) and isum == jsum:
1007                    eqvDict[varyI].append(varyJ) 
1008            elif SGLaue in ['m3','m3m']:
1009                isum = ih**2+ik**2+il**2
1010                jsum = jh**2+jk**2+jl**2
1011                if isum == jsum:
1012                    eqvDict[varyI].append(varyJ)
1013            elif abs(dspI-dspJ)/dspI < 1.e-4:
1014                eqvDict[varyI].append(varyJ) 
1015    for item in pawleyVary:
1016        if eqvDict[item]:
1017            for item2 in pawleyVary:
1018                if item2 in eqvDict[item]:
1019                    eqvDict[item2] = []
1020            G2mv.StoreEquivalence(item,eqvDict[item])
1021                   
1022def cellVary(pfx,SGData): 
1023    '''Creates equivalences for a phase based on the Laue class.
1024    Returns a list of A tensor terms that are non-zero.
1025    '''
1026    if SGData['SGLaue'] in ['-1',]:
1027        return [pfx+'A0',pfx+'A1',pfx+'A2',pfx+'A3',pfx+'A4',pfx+'A5']
1028    elif SGData['SGLaue'] in ['2/m',]:
1029        if SGData['SGUniq'] == 'a':
1030            return [pfx+'A0',pfx+'A1',pfx+'A2',pfx+'A5']
1031        elif SGData['SGUniq'] == 'b':
1032            return [pfx+'A0',pfx+'A1',pfx+'A2',pfx+'A4']
1033        else:
1034            return [pfx+'A0',pfx+'A1',pfx+'A2',pfx+'A3']
1035    elif SGData['SGLaue'] in ['mmm',]:
1036        return [pfx+'A0',pfx+'A1',pfx+'A2']
1037    elif SGData['SGLaue'] in ['4/m','4/mmm']:
1038        G2mv.StoreEquivalence(pfx+'A0',(pfx+'A1',))
1039        return [pfx+'A0',pfx+'A1',pfx+'A2']
1040    elif SGData['SGLaue'] in ['6/m','6/mmm','3m1', '31m', '3']:
1041        G2mv.StoreEquivalence(pfx+'A0',(pfx+'A1',pfx+'A3',))
1042        return [pfx+'A0',pfx+'A1',pfx+'A2',pfx+'A3']
1043    elif SGData['SGLaue'] in ['3R', '3mR']:
1044        G2mv.StoreEquivalence(pfx+'A0',(pfx+'A1',pfx+'A2',))
1045        G2mv.StoreEquivalence(pfx+'A3',(pfx+'A4',pfx+'A5',))
1046        return [pfx+'A0',pfx+'A1',pfx+'A2',pfx+'A3',pfx+'A4',pfx+'A5']                       
1047    elif SGData['SGLaue'] in ['m3m','m3']:
1048        G2mv.StoreEquivalence(pfx+'A0',(pfx+'A1',pfx+'A2',))
1049        return [pfx+'A0',pfx+'A1',pfx+'A2']
1050       
1051def modVary(pfx,SSGData):
1052    vary = []
1053    for i,item in enumerate(SSGData['modSymb']):
1054        if item in ['a','b','g']:
1055            vary.append(pfx+'mV%d'%(i))
1056    return vary
1057       
1058################################################################################
1059##### Rigid Body Models and not General.get('doPawley')
1060################################################################################
1061       
1062def GetRigidBodyModels(rigidbodyDict,Print=True,pFile=None):
1063    'Get Rigid body info from tree entry and print it to .LST file'
1064   
1065    def PrintResRBModel(RBModel):
1066        pFile.write('Residue RB name: %s no.atoms: %d, No. times used: %d\n'%
1067            (RBModel['RBname'],len(RBModel['rbTypes']),RBModel['useCount']))
1068        for i in WriteResRBModel(RBModel):
1069            pFile.write(i)
1070       
1071    def PrintVecRBModel(RBModel):
1072        pFile.write('Vector RB name: %s no.atoms: %d No. times used: %d\n'%
1073            (RBModel['RBname'],len(RBModel['rbTypes']),RBModel['useCount']))
1074        for i in WriteVecRBModel(RBModel):
1075            pFile.write(i)
1076        pFile.write('Orientation defined by: atom %s -> atom %s & atom %s -> atom %s\n'%
1077            (RBModel['rbRef'][0],RBModel['rbRef'][1],RBModel['rbRef'][0],RBModel['rbRef'][2]))
1078           
1079    if Print and pFile is None: raise Exception("specify pFile or Print=False")
1080    rbVary = []
1081    rbDict = {}
1082    rbIds = rigidbodyDict.get('RBIds',{'Vector':[],'Residue':[]})
1083    if len(rbIds['Vector']):
1084        for irb,item in enumerate(rbIds['Vector']):
1085            if rigidbodyDict['Vector'][item]['useCount']:
1086                RBmags = rigidbodyDict['Vector'][item]['VectMag']
1087                RBrefs = rigidbodyDict['Vector'][item]['VectRef']
1088                for i,[mag,ref] in enumerate(zip(RBmags,RBrefs)):
1089                    pid = '::RBV;'+str(i)+':'+str(irb)
1090                    rbDict[pid] = mag
1091                    if ref:
1092                        rbVary.append(pid)
1093                if Print:
1094                    pFile.write('\nVector rigid body model:\n')
1095                    PrintVecRBModel(rigidbodyDict['Vector'][item])
1096    if len(rbIds['Residue']):
1097        for item in rbIds['Residue']:
1098            if rigidbodyDict['Residue'][item]['useCount']:
1099                if Print:
1100                    pFile.write('\nResidue rigid body model:\n')
1101                    PrintResRBModel(rigidbodyDict['Residue'][item])
1102    return rbVary,rbDict
1103   
1104def SetRigidBodyModels(parmDict,sigDict,rigidbodyDict,pFile=None):
1105    'needs a doc string'
1106   
1107    def PrintRBVectandSig(VectRB,VectSig):
1108        pFile.write('\n Rigid body vector magnitudes for %s:\n'%VectRB['RBname'])
1109        namstr = '  names :'
1110        valstr = '  values:'
1111        sigstr = '  esds  :'
1112        for i,[val,sig] in enumerate(zip(VectRB['VectMag'],VectSig)):
1113            namstr += '%12s'%('Vect '+str(i))
1114            valstr += '%12.4f'%(val)
1115            if sig:
1116                sigstr += '%12.4f'%(sig)
1117            else:
1118                sigstr += 12*' '
1119        pFile.write(namstr+'\n')
1120        pFile.write(valstr+'\n')
1121        pFile.write(sigstr+'\n')       
1122       
1123    RBIds = rigidbodyDict.get('RBIds',{'Vector':[],'Residue':[]})  #these are lists of rbIds
1124    if not RBIds['Vector']:
1125        return
1126    for irb,item in enumerate(RBIds['Vector']):
1127        if rigidbodyDict['Vector'][item]['useCount']:
1128            VectSig = []
1129            RBmags = rigidbodyDict['Vector'][item]['VectMag']
1130            for i,mag in enumerate(RBmags):
1131                name = '::RBV;'+str(i)+':'+str(irb)
1132                if name in sigDict:
1133                    VectSig.append(sigDict[name])
1134            PrintRBVectandSig(rigidbodyDict['Vector'][item],VectSig)   
1135       
1136################################################################################
1137##### Phase data
1138################################################################################                   
1139def GetPhaseData(PhaseData,RestraintDict={},rbIds={},Print=True,pFile=None,
1140                 seqRef=False,symHold=None):
1141    '''Setup the phase information for a structural refinement, used for
1142    regular and sequential refinements, optionally printing information
1143    to the .lst file (if Print is True)
1144    '''
1145           
1146    def PrintFFtable(FFtable):
1147        pFile.write('\n X-ray scattering factors:\n')
1148        pFile.write('   Symbol     fa                                      fb                                      fc\n')
1149        pFile.write(99*'-'+'\n')
1150        for Ename in FFtable:
1151            ffdata = FFtable[Ename]
1152            fa = ffdata['fa']
1153            fb = ffdata['fb']
1154            pFile.write(' %8s %9.5f %9.5f %9.5f %9.5f %9.5f %9.5f %9.5f %9.5f %9.5f\n'%
1155                (Ename.ljust(8),fa[0],fa[1],fa[2],fa[3],fb[0],fb[1],fb[2],fb[3],ffdata['fc']))
1156               
1157    def PrintMFtable(MFtable):
1158        pFile.write('\n <j0> Magnetic scattering factors:\n')
1159        pFile.write('   Symbol     mfa                                    mfb                                     mfc\n')
1160        pFile.write(99*'-'+'\n')
1161        for Ename in MFtable:
1162            mfdata = MFtable[Ename]
1163            fa = mfdata['mfa']
1164            fb = mfdata['mfb']
1165            pFile.write(' %8s %9.5f %9.5f %9.5f %9.5f %9.5f %9.5f %9.5f %9.5f %9.5f\n'%
1166                (Ename.ljust(8),fa[0],fa[1],fa[2],fa[3],fb[0],fb[1],fb[2],fb[3],mfdata['mfc']))
1167        pFile.write('\n <j2> Magnetic scattering factors:\n')
1168        pFile.write('   Symbol     nfa                                    nfb                                     nfc\n')
1169        pFile.write(99*'-'+'\n')
1170        for Ename in MFtable:
1171            mfdata = MFtable[Ename]
1172            fa = mfdata['nfa']
1173            fb = mfdata['nfb']
1174            pFile.write(' %8s %9.5f %9.5f %9.5f %9.5f %9.5f %9.5f %9.5f %9.5f %9.5f\n'%
1175                (Ename.ljust(8),fa[0],fa[1],fa[2],fa[3],fb[0],fb[1],fb[2],fb[3],mfdata['nfc']))
1176               
1177    def PrintBLtable(BLtable):
1178        pFile.write('\n Neutron scattering factors:\n')
1179        pFile.write('   Symbol   isotope       mass       b       resonant terms\n')
1180        pFile.write(99*'-'+'\n')
1181        for Ename in BLtable:
1182            bldata = BLtable[Ename]
1183            isotope = bldata[0]
1184            mass = bldata[1]['Mass']
1185            if 'BW-LS' in bldata[1]:
1186                bres = bldata[1]['BW-LS']
1187                blen = 0
1188            else:
1189                blen = bldata[1]['SL'][0]
1190                bres = []
1191            line = ' %8s%11s %10.3f %8.3f'%(Ename.ljust(8),isotope.center(11),mass,blen)
1192            for item in bres:
1193                line += '%10.5g'%(item)
1194            pFile.write(line+'\n')
1195           
1196    def PrintRBObjects(resRBData,vecRBData):
1197               
1198        def PrintRBThermals():
1199            tlstr = ['11','22','33','12','13','23']
1200            sstr = ['12','13','21','23','31','32','AA','BB']
1201            TLS = RB['ThermalMotion'][1]
1202            TLSvar = RB['ThermalMotion'][2]
1203            if 'T' in RB['ThermalMotion'][0]:
1204                pFile.write('TLS data\n')
1205                text = ''
1206                for i in range(6):
1207                    text += 'T'+tlstr[i]+' %8.4f %s '%(TLS[i],str(TLSvar[i])[0])
1208                pFile.write(text+'\n')
1209                if 'L' in RB['ThermalMotion'][0]: 
1210                    text = ''
1211                    for i in range(6,12):
1212                        text += 'L'+tlstr[i-6]+' %8.2f %s '%(TLS[i],str(TLSvar[i])[0])
1213                    pFile.write(text+'\n')
1214                if 'S' in RB['ThermalMotion'][0]:
1215                    text = ''
1216                    for i in range(12,20):
1217                        text += 'S'+sstr[i-12]+' %8.3f %s '%(TLS[i],str(TLSvar[i])[0])
1218                    pFile.write(text+'\n')
1219            if 'U' in RB['ThermalMotion'][0]:
1220                pFile.write('Uiso data\n')
1221                text = 'Uiso'+' %10.3f %s'%(TLS[0],str(TLSvar[0])[0])           
1222                pFile.write(text+'\n')
1223           
1224        if len(resRBData):
1225            for RB in resRBData:
1226                Oxyz = RB['Orig'][0]
1227                Qrijk = RB['Orient'][0]
1228                Angle = 2.0*acosd(Qrijk[0])
1229                pFile.write('\nRBObject %s at %10.4f %10.4f %10.4f Refine? %s\n'%
1230                    (RB['RBname'],Oxyz[0],Oxyz[1],Oxyz[2],RB['Orig'][1]))
1231                pFile.write('Orientation angle,vector: %10.3f %10.4f %10.4f %10.4f Refine? %s\n'%
1232                    (Angle,Qrijk[1],Qrijk[2],Qrijk[3],RB['Orient'][1]))
1233                pFile.write('Atom site frac: %10.3f Refine? %s\n'%(RB['AtomFrac'][0],RB['AtomFrac'][1]))
1234                Torsions = RB['Torsions']
1235                if len(Torsions):
1236                    text = 'Torsions: '
1237                    for torsion in Torsions:
1238                        text += '%10.4f Refine? %s'%(torsion[0],torsion[1])
1239                    pFile.write(text+'\n')
1240                PrintRBThermals()
1241        if len(vecRBData):
1242            for RB in vecRBData:
1243                Oxyz = RB['Orig'][0]
1244                Qrijk = RB['Orient'][0]
1245                Angle = 2.0*acosd(Qrijk[0])
1246                pFile.write('\nRBObject %s at %10.4f %10.4f %10.4f Refine? %s\n'%
1247                    (RB['RBname'],Oxyz[0],Oxyz[1],Oxyz[2],RB['Orig'][1]))
1248                pFile.write('Orientation angle,vector: %10.3f %10.4f %10.4f %10.4f Refine? %s\n'%
1249                    (Angle,Qrijk[1],Qrijk[2],Qrijk[3],RB['Orient'][1]))
1250                pFile.write('Atom site frac: %10.3f Refine? %s\n'%(RB['AtomFrac'][0],RB['AtomFrac'][1]))
1251                PrintRBThermals()
1252               
1253    def PrintAtoms(General,Atoms):
1254        cx,ct,cs,cia = General['AtomPtrs']
1255        pFile.write('\n Atoms:\n')
1256        line = '   name    type  refine?   x         y         z    '+ \
1257            '  frac site sym  mult I/A   Uiso     U11     U22     U33     U12     U13     U23'
1258        if General['Type'] == 'macromolecular':
1259            line = ' res no residue chain'+line
1260        pFile.write(line+'\n')
1261        if General['Type'] in ['nuclear','magnetic','faulted',]:
1262            pFile.write(135*'-'+'\n')
1263            for i,at in enumerate(Atoms):
1264                line = '%7s'%(at[ct-1])+'%7s'%(at[ct])+'%7s'%(at[ct+1])+'%10.5f'%(at[cx])+'%10.5f'%(at[cx+1])+ \
1265                    '%10.5f'%(at[cx+2])+'%8.3f'%(at[cx+3])+'%7s'%(at[cs])+'%5d'%(at[cs+1])+'%5s'%(at[cia])
1266                if at[cia] == 'I':
1267                    line += '%8.5f'%(at[cia+1])+48*' '
1268                else:
1269                    line += 8*' '
1270                    for j in range(6):
1271                        line += '%8.5f'%(at[cia+2+j])
1272                pFile.write(line+'\n')
1273        elif General['Type'] == 'macromolecular':
1274            pFile.write(135*'-'+'\n')           
1275            for i,at in enumerate(Atoms):
1276                line = '%7s'%(at[0])+'%7s'%(at[1])+'%7s'%(at[2])+'%7s'%(at[ct-1])+'%7s'%(at[ct])+'%7s'%(at[ct+1])+'%10.5f'%(at[cx])+'%10.5f'%(at[cx+1])+ \
1277                    '%10.5f'%(at[cx+2])+'%8.3f'%(at[cx+3])+'%7s'%(at[cs])+'%5d'%(at[cs+1])+'%5s'%(at[cia])
1278                if at[cia] == 'I':
1279                    line += '%8.4f'%(at[cia+1])+48*' '
1280                else:
1281                    line += 8*' '
1282                    for j in range(6):
1283                        line += '%8.4f'%(at[cia+2+j])
1284                pFile.write(line+'\n')
1285               
1286    def PrintMoments(General,Atoms):
1287        cx,ct,cs,cia = General['AtomPtrs']
1288        cmx = cx+4
1289        AtInfo = dict(zip(General['AtomTypes'],General['Lande g']))
1290        pFile.write('\n Magnetic moments:\n')
1291        line = '   name    type  refine?  Mx        My        Mz    '
1292        pFile.write(line+'\n')
1293        pFile.write(135*'-'+'\n')
1294        for i,at in enumerate(Atoms):
1295            if AtInfo[at[ct]]:
1296                line = '%7s'%(at[ct-1])+'%7s'%(at[ct])+'%7s'%(at[ct+1])+'%10.5f'%(at[cmx])+'%10.5f'%(at[cmx+1])+ \
1297                    '%10.5f'%(at[cmx+2])
1298                pFile.write(line+'\n')
1299       
1300               
1301    def PrintWaves(General,Atoms):
1302        cx,ct,cs,cia = General['AtomPtrs']
1303        pFile.write('\n Modulation waves\n')
1304        names = {'Sfrac':['Fsin','Fcos','Fzero','Fwid'],'Spos':['Xsin','Ysin','Zsin','Xcos','Ycos','Zcos','Tmin','Tmax','Xmax','Ymax','Zmax'],
1305            'Sadp':['U11sin','U22sin','U33sin','U12sin','U13sin','U23sin','U11cos','U22cos',
1306            'U33cos','U12cos','U13cos','U23cos'],'Smag':['MXsin','MYsin','MZsin','MXcos','MYcos','MZcos']}
1307        pFile.write(135*'-'+'\n')
1308        for i,at in enumerate(Atoms):
1309            AtomSS = at[-1]['SS1']
1310            for Stype in ['Sfrac','Spos','Sadp','Smag']:
1311                Waves = AtomSS[Stype]
1312                if len(Waves):
1313                    pFile.write(' atom: %s, site sym: %s, type: %s wave type: %s:\n'%
1314                        (at[ct-1],at[cs],Stype,Waves[0]))
1315                else:
1316                    continue
1317                for iw,wave in enumerate(Waves[1:]):                   
1318                    line = ''
1319                    if Waves[0] in ['Block','ZigZag'] and Stype == 'Spos' and not iw:
1320                        for item in names[Stype][6:]:
1321                            line += '%8s '%(item)                       
1322                    else:
1323                        if Stype == 'Spos':
1324                            for item in names[Stype][:6]:
1325                                line += '%8s '%(item)
1326                        else:
1327                            for item in names[Stype]:
1328                                line += '%8s '%(item)
1329                    pFile.write(line+'\n')
1330                    line = ''
1331                    for item in wave[0]:
1332                        line += '%8.4f '%(item)
1333                    line += ' Refine? '+str(wave[1])
1334                    pFile.write(line+'\n')
1335       
1336    def PrintTexture(textureData):
1337        topstr = '\n Spherical harmonics texture: Order:' + \
1338            str(textureData['Order'])
1339        if textureData['Order']:
1340            pFile.write('%s Refine? %s\n'%(topstr,textureData['SH Coeff'][0]))
1341        else:
1342            pFile.write(topstr+'\n')
1343            return
1344        names = ['omega','chi','phi']
1345        line = '\n'
1346        for name in names:
1347            line += ' SH '+name+':'+'%12.4f'%(textureData['Sample '+name][1])+' Refine? '+str(textureData['Sample '+name][0])
1348        pFile.write(line+'\n')
1349        pFile.write('\n Texture coefficients:\n')
1350        SHcoeff = textureData['SH Coeff'][1]
1351        SHkeys = list(SHcoeff.keys())
1352        nCoeff = len(SHcoeff)
1353        nBlock = nCoeff//10+1
1354        iBeg = 0
1355        iFin = min(iBeg+10,nCoeff)
1356        for block in range(nBlock):
1357            ptlbls = ' names :'
1358            ptstr =  ' values:'
1359            for item in SHkeys[iBeg:iFin]:
1360                ptlbls += '%12s'%(item)
1361                ptstr += '%12.4f'%(SHcoeff[item]) 
1362            pFile.write(ptlbls+'\n')
1363            pFile.write(ptstr+'\n')
1364            iBeg += 10
1365            iFin = min(iBeg+10,nCoeff)
1366       
1367    def MakeRBParms(rbKey,phaseVary,phaseDict):
1368        #### patch 2/24/21 BHT: new param, AtomFrac in RB
1369        if 'AtomFrac' not in RB: raise Exception('out of date RB: edit in RB Models')
1370        # end patch
1371        rbid = str(rbids.index(RB['RBId']))
1372        pfxRB = pfx+'RB'+rbKey+'P'
1373        pstr = ['x','y','z']
1374        ostr = ['a','i','j','k']
1375        Sytsym = G2spc.SytSym(RB['Orig'][0],SGData)[0]
1376        xId,xCoef = G2spc.GetCSxinel(Sytsym) # gen origin site sym
1377        equivs = {1:[],2:[],3:[]}
1378        for i in range(3):
1379            name = pfxRB+pstr[i]+':'+str(iRB)+':'+rbid
1380            phaseDict[name] = RB['Orig'][0][i]
1381            if RB['Orig'][1]:
1382                if xId[i] > 0:                               
1383                    phaseVary += [name,]
1384                    equivs[xId[i]].append([name,xCoef[i]])
1385                elif symHold is not None: #variable is held due to symmetry
1386                    symHold.append(name)
1387        for k in equivs:
1388            equiv = equivs[k]
1389            if len(equiv) > 1:
1390                name = equiv[0][0]
1391                coef = equiv[0][1]
1392                for eqv in equiv[1:]:
1393                    eqv[1] /= coef
1394                    G2mv.StoreEquivalence(name,(eqv,))
1395        pfxRB = pfx+'RB'+rbKey+'O'
1396        A,V = G2mth.Q2AV(RB['Orig'][0])
1397        fixAxis = [0, np.abs(V).argmax()+1]
1398        for i in range(4):
1399            name = pfxRB+ostr[i]+':'+str(iRB)+':'+rbid
1400            phaseDict[name] = RB['Orient'][0][i]
1401            if RB['Orient'][1] == 'AV' and i:
1402                phaseVary += [name,]
1403            elif RB['Orient'][1] == 'A' and not i:
1404                phaseVary += [name,]
1405            elif RB['Orient'][1] == 'V' and i not in fixAxis:
1406                phaseVary += [name,]
1407        name = pfx+'RB'+rbKey+'f:'+str(iRB)+':'+rbid
1408        phaseDict[name] = RB['AtomFrac'][0]
1409        if RB['AtomFrac'][1]:
1410            phaseVary += [name,]
1411               
1412    def MakeRBThermals(rbKey,phaseVary,phaseDict):
1413        rbid = str(rbids.index(RB['RBId']))
1414        tlstr = ['11','22','33','12','13','23']
1415        sstr = ['12','13','21','23','31','32','AA','BB']
1416        if 'T' in RB['ThermalMotion'][0]:
1417            pfxRB = pfx+'RB'+rbKey+'T'
1418            for i in range(6):
1419                name = pfxRB+tlstr[i]+':'+str(iRB)+':'+rbid
1420                phaseDict[name] = RB['ThermalMotion'][1][i]
1421                if RB['ThermalMotion'][2][i]:
1422                    phaseVary += [name,]
1423        if 'L' in RB['ThermalMotion'][0]:
1424            pfxRB = pfx+'RB'+rbKey+'L'
1425            for i in range(6):
1426                name = pfxRB+tlstr[i]+':'+str(iRB)+':'+rbid
1427                phaseDict[name] = RB['ThermalMotion'][1][i+6]
1428                if RB['ThermalMotion'][2][i+6]:
1429                    phaseVary += [name,]
1430        if 'S' in RB['ThermalMotion'][0]:
1431            pfxRB = pfx+'RB'+rbKey+'S'
1432            for i in range(8):
1433                name = pfxRB+sstr[i]+':'+str(iRB)+':'+rbid
1434                phaseDict[name] = RB['ThermalMotion'][1][i+12]
1435                if RB['ThermalMotion'][2][i+12]:
1436                    phaseVary += [name,]
1437        if 'U' in RB['ThermalMotion'][0]:
1438            name = pfx+'RB'+rbKey+'U:'+str(iRB)+':'+rbid
1439            phaseDict[name] = RB['ThermalMotion'][1][0]
1440            if RB['ThermalMotion'][2][0]:
1441                phaseVary += [name,]
1442               
1443    def MakeRBTorsions(rbKey,phaseVary,phaseDict):
1444        rbid = str(rbids.index(RB['RBId']))
1445        pfxRB = pfx+'RB'+rbKey+'Tr;'
1446        for i,torsion in enumerate(RB['Torsions']):
1447            name = pfxRB+str(i)+':'+str(iRB)+':'+rbid
1448            phaseDict[name] = torsion[0]
1449            if torsion[1]:
1450                phaseVary += [name,]
1451                   
1452    if Print and pFile is None: raise Exception("specify pFile or Print=False")
1453    if Print:
1454        pFile.write('\n Phases:\n')
1455    phaseVary = []
1456    phaseDict = {}
1457    pawleyLookup = {}
1458    FFtables = {}                   #scattering factors - xrays
1459    MFtables = {}                   #Mag. form factors
1460    BLtables = {}                   # neutrons
1461    Natoms = {}
1462    maxSSwave = {}
1463    shModels = ['cylindrical','none','shear - 2/m','rolling - mmm']
1464    SamSym = dict(zip(shModels,['0','-1','2/m','mmm']))
1465    atomIndx = {}
1466    for name in PhaseData:
1467        General = PhaseData[name]['General']
1468        pId = PhaseData[name]['pId']
1469        pfx = str(pId)+'::'
1470        FFtable = G2el.GetFFtable(General['AtomTypes'])
1471        BLtable = G2el.GetBLtable(General)
1472        FFtables.update(FFtable)
1473        BLtables.update(BLtable)
1474        phaseDict[pfx+'isMag'] = False
1475        SGData = General['SGData']
1476        SGtext,SGtable = G2spc.SGPrint(SGData)
1477        if General['Type'] == 'magnetic':
1478            MFtable = G2el.GetMFtable(General['AtomTypes'],General['Lande g'])
1479            MFtables.update(MFtable)
1480            phaseDict[pfx+'isMag'] = True
1481            SpnFlp = SGData['SpnFlp']
1482        Atoms = PhaseData[name]['Atoms']
1483        if Atoms and not General.get('doPawley'):
1484            cx,ct,cs,cia = General['AtomPtrs']
1485            AtLookup = G2mth.FillAtomLookUp(Atoms,cia+8)
1486        PawleyRef = PhaseData[name].get('Pawley ref',[])
1487        cell = General['Cell']
1488        A = G2lat.cell2A(cell[1:7])
1489        phaseDict.update({pfx+'A0':A[0],pfx+'A1':A[1],pfx+'A2':A[2],
1490            pfx+'A3':A[3],pfx+'A4':A[4],pfx+'A5':A[5],pfx+'Vol':G2lat.calc_V(A)})
1491        if cell[0]:
1492            phaseVary += cellVary(pfx,SGData)       #also fills in symmetry required constraints
1493        SSGtext = []    #no superstructure
1494        im = 0
1495        if General.get('Modulated',False):
1496            im = 1      #refl offset
1497            Vec,vRef,maxH = General['SuperVec']
1498            phaseDict.update({pfx+'mV0':Vec[0],pfx+'mV1':Vec[1],pfx+'mV2':Vec[2]})
1499            SSGData = General['SSGData']
1500            SSGtext,SSGtable = G2spc.SSGPrint(SGData,SSGData)
1501            if vRef:
1502                phaseVary += modVary(pfx,SSGData)       
1503        resRBData = PhaseData[name]['RBModels'].get('Residue',[])
1504        if resRBData:
1505            rbids = rbIds['Residue']    #NB: used in the MakeRB routines
1506            for iRB,RB in enumerate(resRBData):
1507                MakeRBParms('R',phaseVary,phaseDict)
1508                MakeRBThermals('R',phaseVary,phaseDict)
1509                MakeRBTorsions('R',phaseVary,phaseDict)
1510       
1511        vecRBData = PhaseData[name]['RBModels'].get('Vector',[])
1512        if vecRBData:
1513            rbids = rbIds['Vector']    #NB: used in the MakeRB routines
1514            for iRB,RB in enumerate(vecRBData):
1515                MakeRBParms('V',phaseVary,phaseDict)
1516                MakeRBThermals('V',phaseVary,phaseDict)
1517                   
1518        Natoms[pfx] = 0
1519        maxSSwave[pfx] = {'Sfrac':0,'Spos':0,'Sadp':0,'Smag':0}
1520        if Atoms and not General.get('doPawley'):
1521            cx,ct,cs,cia = General['AtomPtrs']
1522            Natoms[pfx] = len(Atoms)
1523            for i,at in enumerate(Atoms):
1524                atomIndx[at[cia+8]] = [pfx,i]      #lookup table for restraints
1525                phaseDict.update({pfx+'Atype:'+str(i):at[ct],pfx+'Afrac:'+str(i):at[cx+3],pfx+'Amul:'+str(i):at[cs+1],
1526                    pfx+'Ax:'+str(i):at[cx],pfx+'Ay:'+str(i):at[cx+1],pfx+'Az:'+str(i):at[cx+2],
1527                    pfx+'dAx:'+str(i):0.,pfx+'dAy:'+str(i):0.,pfx+'dAz:'+str(i):0.,         #refined shifts for x,y,z
1528                    pfx+'AI/A:'+str(i):at[cia],})
1529                if at[cia] == 'I':
1530                    phaseDict[pfx+'AUiso:'+str(i)] = at[cia+1]
1531                else:
1532                    phaseDict.update({pfx+'AU11:'+str(i):at[cia+2],pfx+'AU22:'+str(i):at[cia+3],pfx+'AU33:'+str(i):at[cia+4],
1533                        pfx+'AU12:'+str(i):at[cia+5],pfx+'AU13:'+str(i):at[cia+6],pfx+'AU23:'+str(i):at[cia+7]})
1534                if General['Type'] == 'magnetic':
1535                    phaseDict.update({pfx+'AMx:'+str(i):at[cx+4],pfx+'AMy:'+str(i):at[cx+5],pfx+'AMz:'+str(i):at[cx+6]})
1536                if 'F' in at[ct+1]:
1537                    phaseVary.append(pfx+'Afrac:'+str(i))
1538                if 'X' in at[ct+1]:
1539                    try:    #patch for sytsym name changes
1540                        xId,xCoef = G2spc.GetCSxinel(at[cs])
1541                    except KeyError:
1542                        Sytsym = G2spc.SytSym(at[cx:cx+3],SGData)[0]
1543                        at[cs] = Sytsym
1544                        xId,xCoef = G2spc.GetCSxinel(at[cs])
1545                    names = [pfx+'dAx:'+str(i),pfx+'dAy:'+str(i),pfx+'dAz:'+str(i)]
1546                    equivs = {1:[],2:[],3:[]}
1547                    for j in range(3):
1548                        if xId[j] > 0:                               
1549                            phaseVary.append(names[j])
1550                            equivs[xId[j]].append([names[j],xCoef[j]])
1551                        elif symHold is not None: #variable is held due to symmetry
1552                            symHold.append(names[j])
1553                    for equiv in equivs:
1554                        if len(equivs[equiv]) > 1:
1555                            name = equivs[equiv][0][0]
1556                            coef = equivs[equiv][0][1]
1557                            for eqv in equivs[equiv][1:]:
1558                                eqv[1] /= coef
1559                                G2mv.StoreEquivalence(name,(eqv,))
1560                if 'U' in at[ct+1]:
1561                    if at[cia] == 'I':
1562                        phaseVary.append(pfx+'AUiso:'+str(i))
1563                    else:
1564                        try:    #patch for sytsym name changes
1565                            uId,uCoef = G2spc.GetCSuinel(at[cs])[:2]
1566                        except KeyError:
1567                            Sytsym = G2spc.SytSym(at[cx:cx+3],SGData)[0]
1568                            at[cs] = Sytsym
1569                            uId,uCoef = G2spc.GetCSuinel(at[cs])[:2]
1570                        names = [pfx+'AU11:'+str(i),pfx+'AU22:'+str(i),pfx+'AU33:'+str(i),
1571                            pfx+'AU12:'+str(i),pfx+'AU13:'+str(i),pfx+'AU23:'+str(i)]
1572                        equivs = {1:[],2:[],3:[],4:[],5:[],6:[]}
1573                        for j in range(6):
1574                            if uId[j] > 0:                               
1575                                phaseVary.append(names[j])
1576                                equivs[uId[j]].append([names[j],uCoef[j]])
1577                        for equiv in equivs:
1578                            if len(equivs[equiv]) > 1:
1579                                name = equivs[equiv][0][0]
1580                                coef = equivs[equiv][0][1]
1581                                for eqv in equivs[equiv][1:]:
1582                                    eqv[1] /= coef
1583                                    G2mv.StoreEquivalence(name,(eqv,))
1584                if 'M' in at[ct+1]:
1585                    SytSym,Mul,Nop,dupDir = G2spc.SytSym(at[cx:cx+3],SGData)
1586                    mId,mCoef = G2spc.GetCSpqinel(SpnFlp,dupDir)
1587                    names = [pfx+'AMx:'+str(i),pfx+'AMy:'+str(i),pfx+'AMz:'+str(i)]
1588                    equivs = {1:[],2:[],3:[]}
1589                    for j in range(3):
1590                        if mId[j] > 0:
1591                            phaseVary.append(names[j])
1592                            equivs[mId[j]].append([names[j],mCoef[j]])
1593                    for equiv in equivs:
1594                        if len(equivs[equiv]) > 1:
1595                            name = equivs[equiv][0][0]
1596                            coef = equivs[equiv][0][1]
1597                            for eqv in equivs[equiv][1:]:
1598                                eqv[1] /= coef
1599                                G2mv.StoreEquivalence(name,(eqv,))
1600                if General.get('Modulated',False):
1601                    AtomSS = at[-1]['SS1']
1602                    for Stype in ['Sfrac','Spos','Sadp','Smag']:
1603                        Waves = AtomSS[Stype]
1604                        if len(Waves):
1605                            waveType = Waves[0]
1606                        else:
1607                            continue
1608                        phaseDict[pfx+Stype[1].upper()+'waveType:'+str(i)] = waveType
1609                        nx = 0
1610                        for iw,wave in enumerate(Waves[1:]):
1611                            if not iw:
1612                                if waveType in ['ZigZag','Block']:
1613                                    nx = 1
1614                                CSI = G2spc.GetSSfxuinel(waveType,Stype,1,at[cx:cx+3],SGData,SSGData)
1615                            else:
1616                                CSI = G2spc.GetSSfxuinel('Fourier',Stype,iw+1-nx,at[cx:cx+3],SGData,SSGData)
1617                            uId,uCoef = CSI[0]
1618                            stiw = str(i)+':'+str(iw)
1619                            if Stype == 'Spos':
1620                                if waveType in ['ZigZag','Block',] and not iw:
1621                                    names = [pfx+'Tmin:'+stiw,pfx+'Tmax:'+stiw,pfx+'Xmax:'+stiw,pfx+'Ymax:'+stiw,pfx+'Zmax:'+stiw]
1622                                    equivs = {1:[],2:[], 3:[],4:[],5:[]}
1623                                else:
1624                                    names = [pfx+'Xsin:'+stiw,pfx+'Ysin:'+stiw,pfx+'Zsin:'+stiw,
1625                                        pfx+'Xcos:'+stiw,pfx+'Ycos:'+stiw,pfx+'Zcos:'+stiw]
1626                                    equivs = {1:[],2:[],3:[], 4:[],5:[],6:[]}
1627                            elif Stype == 'Sadp':
1628                                names = [pfx+'U11sin:'+stiw,pfx+'U22sin:'+stiw,pfx+'U33sin:'+stiw,
1629                                    pfx+'U12sin:'+stiw,pfx+'U13sin:'+stiw,pfx+'U23sin:'+stiw,
1630                                    pfx+'U11cos:'+stiw,pfx+'U22cos:'+stiw,pfx+'U33cos:'+stiw,
1631                                    pfx+'U12cos:'+stiw,pfx+'U13cos:'+stiw,pfx+'U23cos:'+stiw]
1632                                equivs = {1:[],2:[],3:[],4:[],5:[],6:[], 7:[],8:[],9:[],10:[],11:[],12:[]}
1633                            elif Stype == 'Sfrac':
1634                                equivs = {1:[],2:[]}
1635                                if 'Crenel' in waveType and not iw:
1636                                    names = [pfx+'Fzero:'+stiw,pfx+'Fwid:'+stiw]
1637                                else:
1638                                    names = [pfx+'Fsin:'+stiw,pfx+'Fcos:'+stiw]
1639                            elif Stype == 'Smag':
1640                                equivs = {1:[],2:[],3:[], 4:[],5:[],6:[]}
1641                                names = [pfx+'MXsin:'+stiw,pfx+'MYsin:'+stiw,pfx+'MZsin:'+stiw,
1642                                    pfx+'MXcos:'+stiw,pfx+'MYcos:'+stiw,pfx+'MZcos:'+stiw]
1643                            phaseDict.update(dict(zip(names,wave[0])))
1644                            if wave[1]: #what do we do here for multiple terms in modulation constraints?
1645                                for j in range(len(equivs)):
1646                                    if uId[j][0] > 0:                               
1647                                        phaseVary.append(names[j])
1648                                        equivs[uId[j][0]].append([names[j],uCoef[j][0]])
1649                                for equiv in equivs:
1650                                    if len(equivs[equiv]) > 1:
1651                                        name = equivs[equiv][0][0]
1652                                        coef = equivs[equiv][0][1]
1653                                        for eqv in equivs[equiv][1:]:
1654                                            eqv[1] /= coef
1655                                            G2mv.StoreEquivalence(name,(eqv,))
1656                            maxSSwave[pfx][Stype] = max(maxSSwave[pfx][Stype],iw+1)
1657            textureData = General['SH Texture']
1658            if textureData['Order'] and not seqRef:
1659                phaseDict[pfx+'SHorder'] = textureData['Order']
1660                phaseDict[pfx+'SHmodel'] = SamSym[textureData['Model']]
1661                for item in ['omega','chi','phi']:
1662                    phaseDict[pfx+'SH '+item] = textureData['Sample '+item][1]
1663                    if textureData['Sample '+item][0]:
1664                        phaseVary.append(pfx+'SH '+item)
1665                for item in textureData['SH Coeff'][1]:
1666                    phaseDict[pfx+item] = textureData['SH Coeff'][1][item]
1667                    if textureData['SH Coeff'][0]:
1668                        phaseVary.append(pfx+item)
1669               
1670            if Print:
1671                pFile.write('\n Phase name: %s\n'%General['Name'])
1672                pFile.write(135*'='+'\n')
1673                PrintFFtable(FFtable)
1674                PrintBLtable(BLtable)
1675                if General['Type'] == 'magnetic':
1676                    PrintMFtable(MFtable)
1677                pFile.write('\n')
1678                #how do we print magnetic symmetry table? TBD
1679                if len(SSGtext):    #if superstructure
1680                    for line in SSGtext: pFile.write(line+'\n')
1681                    if len(SSGtable):
1682                        for item in SSGtable:
1683                            line = ' %s '%(item)
1684                            pFile.write(line+'\n') 
1685                    else:
1686                        pFile.write(' ( 1)    %s\n'%(SSGtable[0]))
1687                else:
1688                    for line in SGtext: pFile.write(line+'\n')
1689                    if len(SGtable):
1690                        for item in SGtable:
1691                            line = ' %s '%(item)
1692                            pFile.write(line+'\n') 
1693                    else:
1694                        pFile.write(' ( 1)    %s\n'%(SGtable[0]))
1695                PrintRBObjects(resRBData,vecRBData)
1696                PrintAtoms(General,Atoms)
1697                if General['Type'] == 'magnetic':
1698                    PrintMoments(General,Atoms)
1699                if General.get('Modulated',False):
1700                    PrintWaves(General,Atoms)
1701                pFile.write('\n Unit cell: a = %.5f b = %.5f c = %.5f alpha = %.3f beta = %.3f gamma = %.3f volume = %.3f Refine? %s\n'%
1702                    (cell[1],cell[2],cell[3],cell[4],cell[5],cell[6],cell[7],cell[0]))
1703                if len(SSGtext):    #if superstructure
1704                    pFile.write('\n Modulation vector: mV0 = %.4f mV1 = %.4f mV2 = %.4f max mod. index = %d Refine? %s\n'%
1705                        (Vec[0],Vec[1],Vec[2],maxH,vRef))
1706                if not seqRef:
1707                    PrintTexture(textureData)
1708                if name in RestraintDict:
1709                    PrintRestraints(cell[1:7],SGData,General['AtomPtrs'],Atoms,AtLookup,
1710                        textureData,RestraintDict[name],pFile)
1711                   
1712        elif PawleyRef:
1713            if Print:
1714                pFile.write('\n Phase name: %s\n'%General['Name'])
1715                pFile.write(135*'='+'\n')
1716                pFile.write('\n')
1717                if len(SSGtext):    #if superstructure
1718                    for line in SSGtext: pFile.write(line+'\n')
1719                    if len(SSGtable):
1720                        for item in SSGtable:
1721                            line = ' %s '%(item)
1722                            pFile.write(line+'\n') 
1723                    else:
1724                        pFile.write(' ( 1)    %s\n'%SSGtable[0])
1725                else:
1726                    for line in SGtext: pFile.write(line+'\n')
1727                    if len(SGtable):
1728                        for item in SGtable:
1729                            line = ' %s '%(item)
1730                            pFile.write(line+'\n')
1731                    else:
1732                        pFile.write(' ( 1)    %s\n'%(SGtable[0]))
1733                pFile.write('\n Unit cell: a = %.5f b = %.5f c = %.5f alpha = %.3f beta = %.3f gamma = %.3f volume = %.3f Refine? %s\n'%
1734                    (cell[1],cell[2],cell[3],cell[4],cell[5],cell[6],cell[7],cell[0]))
1735                if len(SSGtext):    #if superstructure
1736                    pFile.write('\n Modulation vector: mV0 = %.4f mV1 = %.4f mV2 = %.4f max mod. index = %d Refine? %s\n'%
1737                        (Vec[0],Vec[1],Vec[2],maxH,vRef))
1738            pawleyVary = []
1739            for i,refl in enumerate(PawleyRef):
1740                phaseDict[pfx+'PWLref:'+str(i)] = refl[6+im]
1741                if im:
1742                    pawleyLookup[pfx+'%d,%d,%d,%d'%(refl[0],refl[1],refl[2],refl[3])] = i
1743                else:
1744                    pawleyLookup[pfx+'%d,%d,%d'%(refl[0],refl[1],refl[2])] = i
1745                if refl[5+im]:
1746                    pawleyVary.append(pfx+'PWLref:'+str(i))
1747            GetPawleyConstr(SGData['SGLaue'],PawleyRef,im,pawleyVary)      #does G2mv.StoreEquivalence
1748            phaseVary += pawleyVary
1749               
1750    return Natoms,atomIndx,phaseVary,phaseDict,pawleyLookup,FFtables,BLtables,MFtables,maxSSwave
1751   
1752def cellFill(pfx,SGData,parmDict,sigDict): 
1753    '''Returns the filled-out reciprocal cell (A) terms and their uncertainties
1754    from the parameter and sig dictionaries.
1755
1756    :param str pfx: parameter prefix ("n::", where n is a phase number)
1757    :param dict SGdata: a symmetry object
1758    :param dict parmDict: a dictionary of parameters
1759    :param dict sigDict:  a dictionary of uncertainties on parameters
1760
1761    :returns: A,sigA where each is a list of six terms with the A terms
1762    '''
1763    if SGData['SGLaue'] in ['-1',]:
1764        A = [parmDict[pfx+'A0'],parmDict[pfx+'A1'],parmDict[pfx+'A2'],
1765            parmDict[pfx+'A3'],parmDict[pfx+'A4'],parmDict[pfx+'A5']]
1766    elif SGData['SGLaue'] in ['2/m',]:
1767        if SGData['SGUniq'] == 'a':
1768            A = [parmDict[pfx+'A0'],parmDict[pfx+'A1'],parmDict[pfx+'A2'],
1769                0,0,parmDict[pfx+'A5']]
1770        elif SGData['SGUniq'] == 'b':
1771            A = [parmDict[pfx+'A0'],parmDict[pfx+'A1'],parmDict[pfx+'A2'],
1772                0,parmDict[pfx+'A4'],0]
1773        else:
1774            A = [parmDict[pfx+'A0'],parmDict[pfx+'A1'],parmDict[pfx+'A2'],
1775                parmDict[pfx+'A3'],0,0]
1776    elif SGData['SGLaue'] in ['mmm',]:
1777        A = [parmDict[pfx+'A0'],parmDict[pfx+'A1'],parmDict[pfx+'A2'],0,0,0]
1778    elif SGData['SGLaue'] in ['4/m','4/mmm']:
1779        A = [parmDict[pfx+'A0'],parmDict[pfx+'A0'],parmDict[pfx+'A2'],0,0,0]
1780    elif SGData['SGLaue'] in ['6/m','6/mmm','3m1', '31m', '3']:
1781        A = [parmDict[pfx+'A0'],parmDict[pfx+'A0'],parmDict[pfx+'A2'],
1782            parmDict[pfx+'A0'],0,0]
1783    elif SGData['SGLaue'] in ['3R', '3mR']:
1784        A = [parmDict[pfx+'A0'],parmDict[pfx+'A0'],parmDict[pfx+'A0'],
1785            parmDict[pfx+'A3'],parmDict[pfx+'A3'],parmDict[pfx+'A3']]
1786    elif SGData['SGLaue'] in ['m3m','m3']:
1787        A = [parmDict[pfx+'A0'],parmDict[pfx+'A0'],parmDict[pfx+'A0'],0,0,0]
1788
1789    try:
1790        if SGData['SGLaue'] in ['-1',]:
1791            sigA = [sigDict[pfx+'A0'],sigDict[pfx+'A1'],sigDict[pfx+'A2'],
1792                sigDict[pfx+'A3'],sigDict[pfx+'A4'],sigDict[pfx+'A5']]
1793        elif SGData['SGLaue'] in ['2/m',]:
1794            if SGData['SGUniq'] == 'a':
1795                sigA = [sigDict[pfx+'A0'],sigDict[pfx+'A1'],sigDict[pfx+'A2'],
1796                    0,0,sigDict[pfx+'A5']]
1797            elif SGData['SGUniq'] == 'b':
1798                sigA = [sigDict[pfx+'A0'],sigDict[pfx+'A1'],sigDict[pfx+'A2'],
1799                    0,sigDict[pfx+'A4'],0]
1800            else:
1801                sigA = [sigDict[pfx+'A0'],sigDict[pfx+'A1'],sigDict[pfx+'A2'],
1802                    sigDict[pfx+'A3'],0,0]
1803        elif SGData['SGLaue'] in ['mmm',]:
1804            sigA = [sigDict[pfx+'A0'],sigDict[pfx+'A1'],sigDict[pfx+'A2'],0,0,0]
1805        elif SGData['SGLaue'] in ['4/m','4/mmm']:
1806            sigA = [sigDict[pfx+'A0'],0,sigDict[pfx+'A2'],0,0,0]
1807        elif SGData['SGLaue'] in ['6/m','6/mmm','3m1', '31m', '3']:
1808            sigA = [sigDict[pfx+'A0'],0,sigDict[pfx+'A2'],0,0,0]
1809        elif SGData['SGLaue'] in ['3R', '3mR']:
1810            sigA = [sigDict[pfx+'A0'],0,0,sigDict[pfx+'A3'],0,0]
1811        elif SGData['SGLaue'] in ['m3m','m3']:
1812            sigA = [sigDict[pfx+'A0'],0,0,0,0,0]
1813    except KeyError:
1814        sigA = [0,0,0,0,0,0]
1815    return A,sigA
1816       
1817def PrintRestraints(cell,SGData,AtPtrs,Atoms,AtLookup,textureData,phaseRest,pFile):
1818    'needs a doc string'
1819    if phaseRest:
1820        Amat = G2lat.cell2AB(cell)[0]
1821        cx,ct,cs = AtPtrs[:3]
1822        names = [['Bond','Bonds'],['Angle','Angles'],['Plane','Planes'],
1823            ['Chiral','Volumes'],['Torsion','Torsions'],['Rama','Ramas'],
1824            ['ChemComp','Sites'],['Texture','HKLs']]
1825        for name,rest in names:
1826            itemRest = phaseRest[name]
1827            if rest in itemRest and itemRest[rest] and itemRest['Use']:
1828                pFile.write('\n  %s restraint weight factor %10.3f Use: %s\n'%(name,itemRest['wtFactor'],str(itemRest['Use'])))
1829                if name in ['Bond','Angle','Plane','Chiral']:
1830                    pFile.write('     calc       obs      sig   delt/sig  atoms(symOp): \n')
1831                    for indx,ops,obs,esd in itemRest[rest]:
1832                        try:
1833                            AtNames = G2mth.GetAtomItemsById(Atoms,AtLookup,indx,ct-1)
1834                            AtName = ''
1835                            for i,Aname in enumerate(AtNames):
1836                                AtName += Aname
1837                                if ops[i] == '1':
1838                                    AtName += '-'
1839                                else:
1840                                    AtName += '+('+ops[i]+')-'
1841                            XYZ = np.array(G2mth.GetAtomItemsById(Atoms,AtLookup,indx,cx,3))
1842                            XYZ = G2mth.getSyXYZ(XYZ,ops,SGData)
1843                            if name == 'Bond':
1844                                calc = G2mth.getRestDist(XYZ,Amat)
1845                            elif name == 'Angle':
1846                                calc = G2mth.getRestAngle(XYZ,Amat)
1847                            elif name == 'Plane':
1848                                calc = G2mth.getRestPlane(XYZ,Amat)
1849                            elif name == 'Chiral':
1850                                calc = G2mth.getRestChiral(XYZ,Amat)
1851                            pFile.write(' %9.3f %9.3f %8.3f %8.3f   %s\n'%(calc,obs,esd,(obs-calc)/esd,AtName[:-1]))
1852                        except KeyError:
1853                            del itemRest[rest]
1854                elif name in ['Torsion','Rama']:
1855                    pFile.write('  atoms(symOp)  calc  obs  sig  delt/sig  torsions: \n')
1856                    coeffDict = itemRest['Coeff']
1857                    for indx,ops,cofName,esd in itemRest[rest]:
1858                        AtNames = G2mth.GetAtomItemsById(Atoms,AtLookup,indx,ct-1)
1859                        AtName = ''
1860                        for i,Aname in enumerate(AtNames):
1861                            AtName += Aname+'+('+ops[i]+')-'
1862                        XYZ = np.array(G2mth.GetAtomItemsById(Atoms,AtLookup,indx,cx,3))
1863                        XYZ = G2mth.getSyXYZ(XYZ,ops,SGData)
1864                        if name == 'Torsion':
1865                            tor = G2mth.getRestTorsion(XYZ,Amat)
1866                            restr,calc = G2mth.calcTorsionEnergy(tor,coeffDict[cofName])
1867                            pFile.write(' %8.3f %8.3f %.3f %8.3f %8.3f %s\n'%(calc,obs,esd,(obs-calc)/esd,tor,AtName[:-1]))
1868                        else:
1869                            phi,psi = G2mth.getRestRama(XYZ,Amat)
1870                            restr,calc = G2mth.calcRamaEnergy(phi,psi,coeffDict[cofName])                               
1871                            pFile.write(' %8.3f %8.3f %8.3f %8.3f %8.3f %8.3f %s\n'%(calc,obs,esd,(obs-calc)/esd,phi,psi,AtName[:-1]))
1872                elif name == 'ChemComp':
1873                    pFile.write('     atoms   mul*frac  factor     prod\n')
1874                    for indx,factors,obs,esd in itemRest[rest]:
1875                        try:
1876                            atoms = G2mth.GetAtomItemsById(Atoms,AtLookup,indx,ct-1)
1877                            mul = np.array(G2mth.GetAtomItemsById(Atoms,AtLookup,indx,cs+1))
1878                            frac = np.array(G2mth.GetAtomItemsById(Atoms,AtLookup,indx,cs-1))
1879                            mulfrac = mul*frac
1880                            calcs = mul*frac*factors
1881                            for iatm,[atom,mf,fr,clc] in enumerate(zip(atoms,mulfrac,factors,calcs)):
1882                                pFile.write(' %10s %8.3f %8.3f %8.3f\n'%(atom,mf,fr,clc))
1883                            pFile.write(' Sum:                   calc: %8.3f obs: %8.3f esd: %8.3f\n'%(np.sum(calcs),obs,esd))
1884                        except KeyError:
1885                            del itemRest[rest]
1886                elif name == 'Texture' and textureData['Order']:
1887                    Start = False
1888                    SHCoef = textureData['SH Coeff'][1]
1889                    shModels = ['cylindrical','none','shear - 2/m','rolling - mmm']
1890                    SamSym = dict(zip(shModels,['0','-1','2/m','mmm']))
1891                    pFile.write ('    HKL  grid  neg esd  sum neg  num neg use unit?  unit esd \n')
1892                    for hkl,grid,esd1,ifesd2,esd2 in itemRest[rest]:
1893                        phi,beta = G2lat.CrsAng(np.array(hkl),cell,SGData)
1894                        ODFln = G2lat.Flnh(Start,SHCoef,phi,beta,SGData)
1895                        R,P,Z = G2mth.getRestPolefig(ODFln,SamSym[textureData['Model']],grid)
1896                        Z = ma.masked_greater(Z,0.0)
1897                        num = ma.count(Z)
1898                        sum = 0
1899                        if num:
1900                            sum = np.sum(Z)
1901                        pFile.write ('   %d %d %d  %d %8.3f %8.3f %8d   %s    %8.3f\n'%(hkl[0],hkl[1],hkl[2],grid,esd1,sum,num,str(ifesd2),esd2))
1902       
1903def getCellEsd(pfx,SGData,A,covData):
1904    'needs a doc string'
1905    rVsq = G2lat.calc_rVsq(A)
1906    G,g = G2lat.A2Gmat(A)       #get recip. & real metric tensors
1907    RMnames = [pfx+'A0',pfx+'A1',pfx+'A2',pfx+'A3',pfx+'A4',pfx+'A5']
1908    varyList = covData['varyList']
1909    covMatrix = covData['covMatrix']
1910    if len(covMatrix):
1911        vcov = G2mth.getVCov(RMnames,varyList,covMatrix)
1912        if SGData['SGLaue'] in ['3', '3m1', '31m', '6/m', '6/mmm']:
1913            vcov[1,1] = vcov[3,3] = vcov[0,1] = vcov[1,0] = vcov[0,0]
1914            vcov[1,3] = vcov[3,1] = vcov[0,3] = vcov[3,0] = vcov[0,0]
1915            vcov[1,2] = vcov[2,1] = vcov[2,3] = vcov[3,2] = vcov[0,2]
1916        elif SGData['SGLaue'] in ['m3','m3m']:
1917            vcov[0:3,0:3] = vcov[0,0]
1918        elif SGData['SGLaue'] in ['4/m', '4/mmm']:
1919            vcov[0:2,0:2] = vcov[0,0]
1920            vcov[1,2] = vcov[2,1] = vcov[0,2]
1921        elif SGData['SGLaue'] in ['3R','3mR']:
1922            vcov[0:3,0:3] = vcov[0,0]
1923    #        vcov[4,4] = vcov[5,5] = vcov[3,3]
1924            vcov[3:6,3:6] = vcov[3,3]
1925            vcov[0:3,3:6] = vcov[0,3]
1926            vcov[3:6,0:3] = vcov[3,0]
1927    else:
1928        vcov = np.eye(6)
1929    delt = 1.e-9
1930    drVdA = np.zeros(6)
1931    for i in range(6):
1932        A[i] += delt
1933        drVdA[i] = G2lat.calc_rVsq(A)
1934        A[i] -= 2*delt
1935        drVdA[i] -= G2lat.calc_rVsq(A)
1936        A[i] += delt
1937    drVdA /= 2.*delt   
1938    srcvlsq = np.inner(drVdA,np.inner(drVdA,vcov))
1939    Vol = 1/np.sqrt(rVsq)
1940    sigVol = Vol**3*np.sqrt(srcvlsq)/2.         #ok - checks with GSAS
1941   
1942    dcdA = np.zeros((6,6))
1943    for i in range(6):
1944        pdcdA =np.zeros(6)
1945        A[i] += delt
1946        pdcdA += G2lat.A2cell(A)
1947        A[i] -= 2*delt
1948        pdcdA -= G2lat.A2cell(A)
1949        A[i] += delt
1950        dcdA[i] = pdcdA/(2.*delt)
1951   
1952    sigMat = np.inner(dcdA,np.inner(dcdA,vcov))
1953    var = np.diag(sigMat)
1954    CS = np.where(var>0.,np.sqrt(var),0.)
1955    if SGData['SGLaue'] in ['3', '3m1', '31m', '6/m', '6/mmm','m3','m3m','4/m','4/mmm']:
1956        CS[3:6] = 0.0
1957    return [CS[0],CS[1],CS[2],CS[5],CS[4],CS[3],sigVol]
1958   
1959def SetPhaseData(parmDict,sigDict,Phases,RBIds,covData,RestraintDict=None,pFile=None):
1960    '''Called after a refinement to transfer parameters from the parameter dict to
1961    the phase(s) information read from a GPX file. Also prints values to the .lst file
1962    '''
1963   
1964    def PrintAtomsAndSig(General,Atoms,atomsSig):
1965        pFile.write('\n Atoms:\n')
1966        line = '   name      x         y         z      frac   Uiso     U11     U22     U33     U12     U13     U23'
1967        if General['Type'] == 'macromolecular':
1968            line = ' res no residue chain '+line
1969        cx,ct,cs,cia = General['AtomPtrs']
1970        pFile.write(line+'\n')
1971        pFile.write(135*'-'+'\n')
1972        fmt = {0:'%7s',ct:'%7s',cx:'%10.5f',cx+1:'%10.5f',cx+2:'%10.5f',cx+3:'%8.3f',cia+1:'%8.5f',
1973            cia+2:'%8.5f',cia+3:'%8.5f',cia+4:'%8.5f',cia+5:'%8.5f',cia+6:'%8.5f',cia+7:'%8.5f'}
1974        noFXsig = {cx:[10*' ','%10s'],cx+1:[10*' ','%10s'],cx+2:[10*' ','%10s'],cx+3:[8*' ','%8s']}
1975        for atyp in General['AtomTypes']:       #zero composition
1976            General['NoAtoms'][atyp] = 0.
1977        for i,at in enumerate(Atoms):
1978            General['NoAtoms'][at[ct]] += at[cx+3]*float(at[cx+5])     #new composition
1979            if General['Type'] == 'macromolecular':
1980                name = ' %s %s %s %s:'%(at[0],at[1],at[2],at[3])
1981                valstr = ' values:          '
1982                sigstr = ' sig   :          '
1983            else:
1984                name = fmt[0]%(at[ct-1])+fmt[1]%(at[ct])+':'
1985                valstr = ' values:'
1986                sigstr = ' sig   :'
1987            for ind in range(cx,cx+4):
1988                sigind = str(i)+':'+str(ind)
1989                valstr += fmt[ind]%(at[ind])                   
1990                if sigind in atomsSig:
1991                    sigstr += fmt[ind]%(atomsSig[sigind])
1992                else:
1993                    sigstr += noFXsig[ind][1]%(noFXsig[ind][0])
1994            if at[cia] == 'I':
1995                valstr += fmt[cia+1]%(at[cia+1])
1996                if '%d:%d'%(i,cia+1) in atomsSig:
1997                    sigstr += fmt[cia+1]%(atomsSig['%d:%d'%(i,cia+1)])
1998                else:
1999                    sigstr += 8*' '
2000            else:
2001                valstr += 8*' '
2002                sigstr += 8*' '
2003                for ind in range(cia+2,cia+8):
2004                    sigind = str(i)+':'+str(ind)
2005                    valstr += fmt[ind]%(at[ind])
2006                    if sigind in atomsSig:                       
2007                        sigstr += fmt[ind]%(atomsSig[sigind])
2008                    else:
2009                        sigstr += 8*' '
2010            pFile.write(name+'\n')
2011            pFile.write(valstr+'\n')
2012            pFile.write(sigstr+'\n')
2013           
2014    def PrintMomentsAndSig(General,Atoms,atomsSig):
2015        cell = General['Cell'][1:7]
2016        G = G2lat.fillgmat(cell)
2017        ast = np.sqrt(np.diag(G))
2018        GS = G/np.outer(ast,ast)
2019        pFile.write('\n Magnetic Moments:\n')    #add magnitude & angle, etc.? TBD
2020        line = '   name       Mx        My        Mz       |Mag|'
2021        cx,ct,cs,cia = General['AtomPtrs']
2022        cmx = cx+4
2023        AtInfo = dict(zip(General['AtomTypes'],General['Lande g']))
2024        pFile.write(line+'\n')
2025        pFile.write(135*'-'+'\n')
2026        fmt = {0:'%7s',ct:'%7s',cmx:'%10.3f',cmx+1:'%10.3f',cmx+2:'%10.3f'}
2027        noFXsig = {cmx:[10*' ','%10s'],cmx+1:[10*' ','%10s'],cmx+2:[10*' ','%10s']}
2028        for i,at in enumerate(Atoms):
2029            if AtInfo[at[ct]]:
2030                name = fmt[0]%(at[ct-1])+fmt[1]%(at[ct])+':'
2031                valstr = ' values:'
2032                sigstr = ' sig   :'
2033                for ind in range(cmx,cmx+3):
2034                    sigind = str(i)+':'+str(ind)
2035                    valstr += fmt[ind]%(at[ind])                   
2036                    if sigind in atomsSig:
2037                        sigstr += fmt[ind]%(atomsSig[sigind])
2038                    else:
2039                        sigstr += noFXsig[ind][1]%(noFXsig[ind][0])
2040                mag = np.array(at[cmx:cmx+3])
2041                Mag = np.sqrt(np.inner(mag,np.inner(mag,GS)))
2042                valstr += '%10.3f'%Mag
2043                sigstr += 10*' '
2044                pFile.write(name+'\n')
2045                pFile.write(valstr+'\n')
2046                pFile.write(sigstr+'\n')
2047           
2048    def PrintWavesAndSig(General,Atoms,wavesSig):
2049        cx,ct,cs,cia = General['AtomPtrs']
2050        pFile.write('\n Modulation waves\n')
2051        names = {'Sfrac':['Fsin','Fcos','Fzero','Fwid'],'Spos':['Xsin','Ysin','Zsin','Xcos','Ycos','Zcos','Tmin','Tmax','Xmax','Ymax','Zmax'],
2052            'Sadp':['U11sin','U22sin','U33sin','U12sin','U13sin','U23sin','U11cos','U22cos',
2053            'U33cos','U12cos','U13cos','U23cos'],'Smag':['MXsin','MYsin','MZsin','MXcos','MYcos','MZcos']}
2054        pFile.write(135*'-'+'\n')
2055        for i,at in enumerate(Atoms):
2056            AtomSS = at[-1]['SS1']
2057            for Stype in ['Sfrac','Spos','Sadp','Smag']:
2058                Waves = AtomSS[Stype]
2059                if len(Waves) > 1:
2060                    waveType = Waves[0]
2061                else:
2062                    continue
2063                if len(Waves):
2064                    pFile.write(' atom: %s, site sym: %s, type: %s wave type: %s:\n'%
2065                        (at[ct-1],at[cs],Stype,waveType))
2066                    for iw,wave in enumerate(Waves[1:]):
2067                        stiw = ':'+str(i)+':'+str(iw)
2068                        namstr = '  names :'
2069                        valstr = '  values:'
2070                        sigstr = '  esds  :'
2071                        if Stype == 'Spos':
2072                            nt = 6
2073                            ot = 0
2074                            if waveType in ['ZigZag','Block',] and not iw:
2075                                nt = 5
2076                                ot = 6
2077                            for j in range(nt):
2078                                name = names['Spos'][j+ot]
2079                                namstr += '%12s'%(name)
2080                                valstr += '%12.4f'%(wave[0][j])
2081                                if name+stiw in wavesSig:
2082                                    sigstr += '%12.4f'%(wavesSig[name+stiw])
2083                                else:
2084                                    sigstr += 12*' '
2085                        elif Stype == 'Sfrac':
2086                            ot = 0
2087                            if 'Crenel' in waveType and not iw:
2088                                ot = 2
2089                            for j in range(2):
2090                                name = names['Sfrac'][j+ot]
2091                                namstr += '%12s'%(names['Sfrac'][j+ot])
2092                                valstr += '%12.4f'%(wave[0][j])
2093                                if name+stiw in wavesSig:
2094                                    sigstr += '%12.4f'%(wavesSig[name+stiw])
2095                                else:
2096                                    sigstr += 12*' '
2097                        elif Stype == 'Sadp':
2098                            for j in range(12):
2099                                name = names['Sadp'][j]
2100                                namstr += '%10s'%(names['Sadp'][j])
2101                                valstr += '%10.6f'%(wave[0][j])
2102                                if name+stiw in wavesSig:
2103                                    sigstr += '%10.6f'%(wavesSig[name+stiw])
2104                                else:
2105                                    sigstr += 10*' '
2106                        elif Stype == 'Smag':
2107                            for j in range(6):
2108                                name = names['Smag'][j]
2109                                namstr += '%12s'%(names['Smag'][j])
2110                                valstr += '%12.4f'%(wave[0][j])
2111                                if name+stiw in wavesSig:
2112                                    sigstr += '%12.4f'%(wavesSig[name+stiw])
2113                                else:
2114                                    sigstr += 12*' '
2115                               
2116                    pFile.write(namstr+'\n')
2117                    pFile.write(valstr+'\n')
2118                    pFile.write(sigstr+'\n')
2119       
2120               
2121    def PrintRBObjPOAndSig(rbfx,rbsx):
2122        for i in WriteRBObjPOAndSig(pfx,rbfx,rbsx,parmDict,sigDict):
2123            pFile.write(i+'\n')
2124       
2125    def PrintRBObjTLSAndSig(rbfx,rbsx,TLS):
2126        for i in WriteRBObjTLSAndSig(pfx,rbfx,rbsx,TLS,parmDict,sigDict):
2127            pFile.write(i)
2128       
2129    def PrintRBObjTorAndSig(rbsx):
2130        nTors = len(RBObj['Torsions'])
2131        if nTors:
2132            for i in WriteRBObjTorAndSig(pfx,rbsx,parmDict,sigDict,nTors):
2133                pFile.write(i)
2134               
2135    def PrintSHtextureAndSig(textureData,SHtextureSig):
2136        Tindx = 1.0
2137        Tvar = 0.0
2138        pFile.write('\n Spherical harmonics texture: Order: %d\n'%textureData['Order'])
2139        names = ['omega','chi','phi']
2140        namstr = '  names :'
2141        ptstr =  '  values:'
2142        sigstr = '  esds  :'
2143        for name in names:
2144            namstr += '%12s'%(name)
2145            ptstr += '%12.3f'%(textureData['Sample '+name][1])
2146            if 'Sample '+name in SHtextureSig:
2147                sigstr += '%12.3f'%(SHtextureSig['Sample '+name])
2148            else:
2149                sigstr += 12*' '
2150        pFile.write(namstr+'\n')
2151        pFile.write(ptstr+'\n')
2152        pFile.write(sigstr+'\n')
2153        pFile.write('\n Texture coefficients:\n')
2154        SHcoeff = textureData['SH Coeff'][1]
2155        SHkeys = list(SHcoeff.keys())
2156        nCoeff = len(SHcoeff)
2157        nBlock = nCoeff//10+1
2158        iBeg = 0
2159        iFin = min(iBeg+10,nCoeff)
2160        for block in range(nBlock):
2161            namstr = '  names :'
2162            ptstr =  '  values:'
2163            sigstr = '  esds  :'
2164            for name in SHkeys[iBeg:iFin]:
2165                namstr += '%12s'%(name)
2166                ptstr += '%12.3f'%(SHcoeff[name])
2167                l = 2.0*eval(name.strip('C'))[0]+1
2168                Tindx += SHcoeff[name]**2/l
2169                if name in SHtextureSig:
2170                    Tvar += (2.*SHcoeff[name]*SHtextureSig[name]/l)**2
2171                    sigstr += '%12.3f'%(SHtextureSig[name])
2172                else:
2173                    sigstr += 12*' '
2174            pFile.write(namstr+'\n')
2175            pFile.write(ptstr+'\n')
2176            pFile.write(sigstr+'\n')
2177            iBeg += 10
2178            iFin = min(iBeg+10,nCoeff)
2179        pFile.write(' Texture index J = %.3f(%d)'%(Tindx,int(1000*np.sqrt(Tvar))))
2180           
2181    ##########################################################################
2182    # SetPhaseData starts here
2183    if pFile: pFile.write('\n Phases:\n')
2184    for phase in Phases:
2185        if pFile: pFile.write(' Result for phase: %s\n'%phase)
2186        if pFile: pFile.write(135*'='+'\n')
2187        Phase = Phases[phase]
2188        General = Phase['General']
2189        SGData = General['SGData']
2190        Atoms = Phase['Atoms']
2191        AtLookup = []
2192        if Atoms and not General.get('doPawley'):
2193            cx,ct,cs,cia = General['AtomPtrs']
2194            AtLookup = G2mth.FillAtomLookUp(Atoms,cia+8)
2195        cell = General['Cell']
2196        pId = Phase['pId']
2197        pfx = str(pId)+'::'
2198        if cell[0]:
2199            A,sigA = cellFill(pfx,SGData,parmDict,sigDict)
2200            cellSig = getCellEsd(pfx,SGData,A,covData)  #includes sigVol
2201            if pFile: pFile.write(' Reciprocal metric tensor: \n')
2202            ptfmt = "%15.9f"
2203            names = ['A11','A22','A33','A12','A13','A23']
2204            namstr = '  names :'
2205            ptstr =  '  values:'
2206            sigstr = '  esds  :'
2207            for name,a,siga in zip(names,A,sigA):
2208                namstr += '%15s'%(name)
2209                ptstr += ptfmt%(a)
2210                if siga:
2211                    sigstr += ptfmt%(siga)
2212                else:
2213                    sigstr += 15*' '
2214            if pFile: pFile.write(namstr+'\n')
2215            if pFile: pFile.write(ptstr+'\n')
2216            if pFile: pFile.write(sigstr+'\n')
2217            cell[1:7] = G2lat.A2cell(A)
2218            cell[7] = G2lat.calc_V(A)
2219            if pFile: pFile.write(' New unit cell:\n')
2220            ptfmt = ["%12.6f","%12.6f","%12.6f","%12.4f","%12.4f","%12.4f","%12.3f"]
2221            names = ['a','b','c','alpha','beta','gamma','Volume']
2222            namstr = '  names :'
2223            ptstr =  '  values:'
2224            sigstr = '  esds  :'
2225            for name,fmt,a,siga in zip(names,ptfmt,cell[1:8],cellSig):
2226                namstr += '%12s'%(name)
2227                ptstr += fmt%(a)
2228                if siga:
2229                    sigstr += fmt%(siga)
2230                else:
2231                    sigstr += 12*' '
2232            if pFile: pFile.write(namstr+'\n')
2233            if pFile: pFile.write(ptstr+'\n')
2234            if pFile: pFile.write(sigstr+'\n')
2235        ik = 6  #for Pawley stuff below
2236        if General.get('Modulated',False):
2237            ik = 7
2238            Vec,vRef,maxH = General['SuperVec']
2239            if vRef:
2240                if pFile: pFile.write(' New modulation vector:\n')
2241                namstr = '  names :'
2242                ptstr =  '  values:'
2243                sigstr = '  esds  :'
2244                for iv,var in enumerate(['mV0','mV1','mV2']):
2245                    namstr += '%12s'%(pfx+var)
2246                    ptstr += '%12.6f'%(parmDict[pfx+var])
2247                    if pfx+var in sigDict:
2248                        Vec[iv] = parmDict[pfx+var]
2249                        sigstr += '%12.6f'%(sigDict[pfx+var])
2250                    else:
2251                        sigstr += 12*' '
2252                if pFile: pFile.write(namstr+'\n')
2253                if pFile: pFile.write(ptstr+'\n')
2254                if pFile: pFile.write(sigstr+'\n')
2255           
2256        General['Mass'] = 0.
2257        if Phase['General'].get('doPawley'):
2258            pawleyRef = Phase['Pawley ref']
2259            for i,refl in enumerate(pawleyRef):
2260                key = pfx+'PWLref:'+str(i)
2261                refl[ik] = parmDict[key]
2262                if key in sigDict:
2263                    refl[ik+1] = sigDict[key]
2264                else:
2265                    refl[ik+1] = 0
2266        else:
2267            VRBIds = RBIds['Vector']
2268            RRBIds = RBIds['Residue']
2269            RBModels = Phase['RBModels']
2270            if pFile:
2271                for irb,RBObj in enumerate(RBModels.get('Vector',[])):
2272                    jrb = VRBIds.index(RBObj['RBId'])
2273                    rbsx = str(irb)+':'+str(jrb)
2274                    pFile.write(' Vector rigid body parameters:\n')
2275                    PrintRBObjPOAndSig('RBV',rbsx)
2276                    PrintRBObjTLSAndSig('RBV',rbsx,RBObj['ThermalMotion'][0])
2277                for irb,RBObj in enumerate(RBModels.get('Residue',[])):
2278                    jrb = RRBIds.index(RBObj['RBId'])
2279                    rbsx = str(irb)+':'+str(jrb)
2280                    pFile.write(' Residue rigid body parameters:\n')
2281                    PrintRBObjPOAndSig('RBR',rbsx)
2282                    PrintRBObjTLSAndSig('RBR',rbsx,RBObj['ThermalMotion'][0])
2283                    PrintRBObjTorAndSig(rbsx)
2284            atomsSig = {}
2285            wavesSig = {}
2286            cx,ct,cs,cia = General['AtomPtrs']
2287            for i,at in enumerate(Atoms):
2288                names = {cx:pfx+'Ax:'+str(i),cx+1:pfx+'Ay:'+str(i),cx+2:pfx+'Az:'+str(i),cx+3:pfx+'Afrac:'+str(i),
2289                    cia+1:pfx+'AUiso:'+str(i),cia+2:pfx+'AU11:'+str(i),cia+3:pfx+'AU22:'+str(i),cia+4:pfx+'AU33:'+str(i),
2290                    cia+5:pfx+'AU12:'+str(i),cia+6:pfx+'AU13:'+str(i),cia+7:pfx+'AU23:'+str(i),
2291                    cx+4:pfx+'AMx:'+str(i),cx+5:pfx+'AMy:'+str(i),cx+6:pfx+'AMz:'+str(i)}
2292                for ind in range(cx,cx+4):
2293                    at[ind] = parmDict[names[ind]]
2294                    if ind in range(cx,cx+3):
2295                        name = names[ind].replace('A','dA')
2296                    else:
2297                        name = names[ind]
2298                    if name in sigDict:
2299                        atomsSig[str(i)+':'+str(ind)] = sigDict[name]
2300                if at[cia] == 'I':
2301                    at[cia+1] = parmDict[names[cia+1]]
2302                    if names[cia+1] in sigDict:
2303                        atomsSig['%d:%d'%(i,cia+1)] = sigDict[names[cia+1]]
2304                else:
2305                    for ind in range(cia+2,cia+8):
2306                        at[ind] = parmDict[names[ind]]
2307                        if names[ind] in sigDict:
2308                            atomsSig[str(i)+':'+str(ind)] = sigDict[names[ind]]
2309                if General['Type'] == 'magnetic':
2310                    for ind in range(cx+4,cx+7):
2311                        at[ind] = parmDict[names[ind]]
2312                        if names[ind] in sigDict:
2313                            atomsSig[str(i)+':'+str(ind)] = sigDict[names[ind]]
2314                ind = General['AtomTypes'].index(at[ct])
2315                General['Mass'] += General['AtomMass'][ind]*at[cx+3]*at[cx+5]
2316                if General.get('Modulated',False):
2317                    AtomSS = at[-1]['SS1']
2318                    for Stype in ['Sfrac','Spos','Sadp','Smag']:
2319                        Waves = AtomSS[Stype]
2320                        if len(Waves):
2321                            waveType = Waves[0]
2322                        else:
2323                            continue
2324                        for iw,wave in enumerate(Waves[1:]):
2325                            stiw = str(i)+':'+str(iw)
2326                            if Stype == 'Spos':
2327                                if waveType in ['ZigZag','Block',] and not iw:
2328                                    names = ['Tmin:'+stiw,'Tmax:'+stiw,'Xmax:'+stiw,'Ymax:'+stiw,'Zmax:'+stiw]
2329                                else:
2330                                    names = ['Xsin:'+stiw,'Ysin:'+stiw,'Zsin:'+stiw,
2331                                        'Xcos:'+stiw,'Ycos:'+stiw,'Zcos:'+stiw]
2332                            elif Stype == 'Sadp':
2333                                names = ['U11sin:'+stiw,'U22sin:'+stiw,'U33sin:'+stiw,
2334                                    'U12sin:'+stiw,'U13sin:'+stiw,'U23sin:'+stiw,
2335                                    'U11cos:'+stiw,'U22cos:'+stiw,'U33cos:'+stiw,
2336                                    'U12cos:'+stiw,'U13cos:'+stiw,'U23cos:'+stiw]
2337                            elif Stype == 'Sfrac':
2338                                if 'Crenel' in waveType and not iw:
2339                                    names = ['Fzero:'+stiw,'Fwid:'+stiw]
2340                                else:
2341                                    names = ['Fsin:'+stiw,'Fcos:'+stiw]
2342                            elif Stype == 'Smag':
2343                                names = ['MXsin:'+stiw,'MYsin:'+stiw,'MZsin:'+stiw,
2344                                    'MXcos:'+stiw,'MYcos:'+stiw,'MZcos:'+stiw]
2345                            for iname,name in enumerate(names):
2346                                AtomSS[Stype][iw+1][0][iname] = parmDict[pfx+name]
2347                                if pfx+name in sigDict:
2348                                    wavesSig[name] = sigDict[pfx+name]
2349
2350            if pFile: PrintAtomsAndSig(General,Atoms,atomsSig)
2351            if pFile and General['Type'] == 'magnetic':
2352                PrintMomentsAndSig(General,Atoms,atomsSig)
2353            if pFile and General.get('Modulated',False):
2354                PrintWavesAndSig(General,Atoms,wavesSig)
2355               
2356            density = G2mth.getDensity(General)[0]
2357            if pFile: pFile.write('\n Density: {:.4f} g/cm**3\n'.format(density))
2358           
2359       
2360        textureData = General['SH Texture']   
2361        if textureData['Order']:
2362            SHtextureSig = {}
2363            for name in ['omega','chi','phi']:
2364                aname = pfx+'SH '+name
2365                textureData['Sample '+name][1] = parmDict[aname]
2366                if aname in sigDict:
2367                    SHtextureSig['Sample '+name] = sigDict[aname]
2368            for name in textureData['SH Coeff'][1]:
2369                aname = pfx+name
2370                textureData['SH Coeff'][1][name] = parmDict[aname]
2371                if aname in sigDict:
2372                    SHtextureSig[name] = sigDict[aname]
2373            PrintSHtextureAndSig(textureData,SHtextureSig)
2374        if phase in RestraintDict and not Phase['General'].get('doPawley'):
2375            PrintRestraints(cell[1:7],SGData,General['AtomPtrs'],Atoms,AtLookup,
2376                textureData,RestraintDict[phase],pFile)
2377                   
2378################################################################################
2379##### Histogram & Phase data
2380################################################################################       
2381                   
2382def GetHistogramPhaseData(Phases,Histograms,Print=True,pFile=None,resetRefList=True):
2383    '''Loads the HAP histogram/phase information into dicts
2384
2385    :param dict Phases: phase information
2386    :param dict Histograms: Histogram information
2387    :param bool Print: prints information as it is read
2388    :param file pFile: file object to print to (the default, None causes printing to the console)
2389    :param bool resetRefList: Should the contents of the Reflection List be initialized
2390      on loading. The default, True, initializes the Reflection List as it is loaded.
2391
2392    :returns: (hapVary,hapDict,controlDict)
2393      * hapVary: list of refined variables
2394      * hapDict: dict with refined variables and their values
2395      * controlDict: dict with fixed parameters
2396    '''
2397   
2398    def PrintSize(hapData):
2399        if hapData[0] in ['isotropic','uniaxial']:
2400            line = '\n Size model    : %9s'%(hapData[0])
2401            line += ' equatorial:'+'%12.3f'%(hapData[1][0])+' Refine? '+str(hapData[2][0])
2402            if hapData[0] == 'uniaxial':
2403                line += ' axial:'+'%12.3f'%(hapData[1][1])+' Refine? '+str(hapData[2][1])
2404            line += '\n\t LG mixing coeff.: %12.4f'%(hapData[1][2])+' Refine? '+str(hapData[2][2])
2405            pFile.write(line+'\n')
2406        else:
2407            pFile.write('\n Size model    : %s\n\t LG mixing coeff.:%12.4f Refine? %s\n'%
2408                (hapData[0],hapData[1][2],hapData[2][2]))
2409            Snames = ['S11','S22','S33','S12','S13','S23']
2410            ptlbls = ' names :'
2411            ptstr =  ' values:'
2412            varstr = ' refine:'
2413            for i,name in enumerate(Snames):
2414                ptlbls += '%12s' % (name)
2415                ptstr += '%12.3f' % (hapData[4][i])
2416                varstr += '%12s' % (str(hapData[5][i]))
2417            pFile.write(ptlbls+'\n')
2418            pFile.write(ptstr+'\n')
2419            pFile.write(varstr+'\n')
2420       
2421    def PrintMuStrain(hapData,SGData):
2422        if hapData[0] in ['isotropic','uniaxial']:
2423            line = '\n Mustrain model: %9s'%(hapData[0])
2424            line += ' equatorial:'+'%12.1f'%(hapData[1][0])+' Refine? '+str(hapData[2][0])
2425            if hapData[0] == 'uniaxial':
2426                line += ' axial:'+'%12.1f'%(hapData[1][1])+' Refine? '+str(hapData[2][1])
2427            line +='\n\t LG mixing coeff.:%12.4f'%(hapData[1][2])+' Refine? '+str(hapData[2][2])
2428            pFile.write(line+'\n')
2429        else:
2430            pFile.write('\n Mustrain model: %s\n\t LG mixing coeff.:%12.4f Refine? %s\n'%
2431                (hapData[0],hapData[1][2],hapData[2][2]))
2432            Snames = G2spc.MustrainNames(SGData)
2433            ptlbls = ' names :'
2434            ptstr =  ' values:'
2435            varstr = ' refine:'
2436            for i,name in enumerate(Snames):
2437                ptlbls += '%12s' % (name)
2438                ptstr += '%12.1f' % (hapData[4][i])
2439                varstr += '%12s' % (str(hapData[5][i]))
2440            pFile.write(ptlbls+'\n')
2441            pFile.write(ptstr+'\n')
2442            pFile.write(varstr+'\n')
2443
2444    def PrintHStrain(hapData,SGData):
2445        pFile.write('\n Hydrostatic/elastic strain:\n')
2446        Hsnames = G2spc.HStrainNames(SGData)
2447        ptlbls = ' names :'
2448        ptstr =  ' values:'
2449        varstr = ' refine:'
2450        for i,name in enumerate(Hsnames):
2451            ptlbls += '%12s' % (name)
2452            ptstr += '%12.4g' % (hapData[0][i])
2453            varstr += '%12s' % (str(hapData[1][i]))
2454        pFile.write(ptlbls+'\n')
2455        pFile.write(ptstr+'\n')
2456        pFile.write(varstr+'\n')
2457
2458    def PrintSHPO(hapData):
2459        pFile.write('\n Spherical harmonics preferred orientation: Order: %d Refine? %s\n'%(hapData[4],hapData[2]))
2460        ptlbls = ' names :'
2461        ptstr =  ' values:'
2462        for item in hapData[5]:
2463            ptlbls += '%12s'%(item)
2464            ptstr += '%12.3f'%(hapData[5][item]) 
2465        pFile.write(ptlbls+'\n')
2466        pFile.write(ptstr+'\n')
2467   
2468    def PrintBabinet(hapData):
2469        pFile.write('\n Babinet form factor modification:\n')
2470        ptlbls = ' names :'
2471        ptstr =  ' values:'
2472        varstr = ' refine:'
2473        for name in ['BabA','BabU']:
2474            ptlbls += '%12s' % (name)
2475            ptstr += '%12.6f' % (hapData[name][0])
2476            varstr += '%12s' % (str(hapData[name][1]))
2477        pFile.write(ptlbls+'\n')
2478        pFile.write(ptstr+'\n')
2479        pFile.write(varstr+'\n')
2480       
2481    hapDict = {}
2482    hapVary = []
2483    controlDict = {}
2484   
2485    for phase in Phases:
2486        HistoPhase = Phases[phase]['Histograms']
2487        SGData = Phases[phase]['General']['SGData']
2488        cell = Phases[phase]['General']['Cell'][1:7]
2489        A = G2lat.cell2A(cell)
2490        if Phases[phase]['General'].get('Modulated',False):
2491            SSGData = Phases[phase]['General']['SSGData']
2492            Vec,x,maxH = Phases[phase]['General']['SuperVec']
2493        pId = Phases[phase]['pId']
2494        for histogram in Histograms:
2495            if histogram not in HistoPhase and phase in Histograms[histogram]['Reflection Lists']:
2496                #remove previously created reflection list if histogram is removed from phase
2497                #print("removing ",phase,"from",histogram)
2498                del Histograms[histogram]['Reflection Lists'][phase]
2499        histoList = list(HistoPhase.keys())
2500        histoList.sort()
2501        for histogram in histoList:
2502            try:
2503                Histogram = Histograms[histogram]
2504            except KeyError:                       
2505                #skip if histogram not included e.g. in a sequential refinement
2506                continue
2507            if not HistoPhase[histogram]['Use']:        #remove previously created  & now unused phase reflection list
2508                if phase in Histograms[histogram]['Reflection Lists']:
2509                    del Histograms[histogram]['Reflection Lists'][phase]
2510                continue
2511            hapData = HistoPhase[histogram]
2512            hId = Histogram['hId']
2513            if 'PWDR' in histogram:
2514                limits = Histogram['Limits'][1]
2515                inst = Histogram['Instrument Parameters'][0]    #TODO - grab table here if present
2516                if 'C' in inst['Type'][1]:
2517                    try:
2518                        wave = inst['Lam'][1]
2519                    except KeyError:
2520                        wave = inst['Lam1'][1]
2521                    dmin = wave/(2.0*sind(limits[1]/2.0))
2522                elif 'T' in inst['Type'][0]:
2523                    dmin = limits[0]/inst['difC'][1]
2524                else:
2525                    wave = inst['Lam'][1]
2526                    dmin = wave/(2.0*sind(limits[1]/2.0))
2527                pfx = str(pId)+':'+str(hId)+':'
2528                if Phases[phase]['General']['doPawley']:
2529                    hapDict[pfx+'LeBail'] = False           #Pawley supercedes LeBail
2530                    hapDict[pfx+'newLeBail'] = True
2531                    Tmin = G2lat.Dsp2pos(inst,dmin)
2532                    if 'T' in inst['Type'][1]:
2533                        limits[0] = max(limits[0],Tmin)
2534                    else:
2535                        limits[1] = min(limits[1],Tmin)
2536                else:
2537                    hapDict[pfx+'LeBail'] = hapData.get('LeBail',False)
2538                    hapDict[pfx+'newLeBail'] = hapData.get('newLeBail',True)
2539                if Phases[phase]['General']['Type'] == 'magnetic':
2540                    dmin = max(dmin,Phases[phase]['General'].get('MagDmin',0.))
2541                for item in ['Scale','Extinction']:
2542                    hapDict[pfx+item] = hapData[item][0]
2543                    if hapData[item][1] and not hapDict[pfx+'LeBail']:
2544                        hapVary.append(pfx+item)
2545                names = G2spc.HStrainNames(SGData)
2546                HSvals = []
2547                for i,name in enumerate(names):
2548                    hapDict[pfx+name] = hapData['HStrain'][0][i]
2549                    HSvals.append(hapDict[pfx+name])
2550                    if hapData['HStrain'][1][i]:
2551                        hapVary.append(pfx+name)
2552                if 'Layer Disp' in hapData:
2553                    hapDict[pfx+'LayerDisp'] = hapData['Layer Disp'][0]
2554                    if hapData['Layer Disp'][1]:
2555                        hapVary.append(pfx+'LayerDisp')
2556                else:
2557                    hapDict[pfx+'LayerDisp'] = 0.0
2558                controlDict[pfx+'poType'] = hapData['Pref.Ori.'][0]
2559                if hapData['Pref.Ori.'][0] == 'MD':
2560                    hapDict[pfx+'MD'] = hapData['Pref.Ori.'][1]
2561                    controlDict[pfx+'MDAxis'] = hapData['Pref.Ori.'][3]
2562                    if hapData['Pref.Ori.'][2] and not hapDict[pfx+'LeBail']:
2563                        hapVary.append(pfx+'MD')
2564                else:                           #'SH' spherical harmonics
2565                    controlDict[pfx+'SHord'] = hapData['Pref.Ori.'][4]
2566                    controlDict[pfx+'SHncof'] = len(hapData['Pref.Ori.'][5])
2567                    controlDict[pfx+'SHnames'] = G2lat.GenSHCoeff(SGData['SGLaue'],'0',controlDict[pfx+'SHord'],False)
2568                    controlDict[pfx+'SHhkl'] = []
2569                    try: #patch for old Pref.Ori. items
2570                        controlDict[pfx+'SHtoler'] = 0.1
2571                        if hapData['Pref.Ori.'][6][0] != '':
2572                            controlDict[pfx+'SHhkl'] = [eval(a.replace(' ',',')) for a in hapData['Pref.Ori.'][6]]
2573                        controlDict[pfx+'SHtoler'] = hapData['Pref.Ori.'][7]
2574                    except IndexError:
2575                        pass
2576                    for item in hapData['Pref.Ori.'][5]:
2577                        hapDict[pfx+item] = hapData['Pref.Ori.'][5][item]
2578                        if hapData['Pref.Ori.'][2] and not hapDict[pfx+'LeBail']:
2579                            hapVary.append(pfx+item)
2580                for item in ['Mustrain','Size']:
2581                    controlDict[pfx+item+'Type'] = hapData[item][0]
2582                    hapDict[pfx+item+';mx'] = hapData[item][1][2]
2583                    if hapData[item][2][2]:
2584                        hapVary.append(pfx+item+';mx')
2585                    if hapData[item][0] in ['isotropic','uniaxial']:
2586                        hapDict[pfx+item+';i'] = hapData[item][1][0]
2587                        if hapData[item][2][0]:
2588                            hapVary.append(pfx+item+';i')
2589                        if hapData[item][0] == 'uniaxial':
2590                            controlDict[pfx+item+'Axis'] = hapData[item][3]
2591                            hapDict[pfx+item+';a'] = hapData[item][1][1]
2592                            if hapData[item][2][1]:
2593                                hapVary.append(pfx+item+';a')
2594                    else:       #generalized for mustrain or ellipsoidal for size
2595                        Nterms = len(hapData[item][4])
2596                        if item == 'Mustrain':
2597                            names = G2spc.MustrainNames(SGData)
2598                            pwrs = []
2599                            for name in names:
2600                                h,k,l = name[1:]
2601                                pwrs.append([int(h),int(k),int(l)])
2602                            controlDict[pfx+'MuPwrs'] = pwrs
2603                        for i in range(Nterms):
2604                            sfx = ';'+str(i)
2605                            hapDict[pfx+item+sfx] = hapData[item][4][i]
2606                            if hapData[item][5][i]:
2607                                hapVary.append(pfx+item+sfx)
2608                if Phases[phase]['General']['Type'] != 'magnetic':
2609                    for bab in ['BabA','BabU']:
2610                        hapDict[pfx+bab] = hapData['Babinet'][bab][0]
2611                        if hapData['Babinet'][bab][1] and not hapDict[pfx+'LeBail']:
2612                            hapVary.append(pfx+bab)
2613                               
2614                if Print: 
2615                    pFile.write('\n Phase: %s in histogram: %s\n'%(phase,histogram))
2616                    pFile.write(135*'='+'\n')
2617                    if hapDict[pfx+'LeBail']:
2618                        pFile.write(' Perform LeBail extraction\n')                     
2619                    else:
2620                        pFile.write(' Phase fraction  : %10.4g Refine? %s\n'%(hapData['Scale'][0],hapData['Scale'][1]))
2621                        pFile.write(' Extinction coeff: %10.4f Refine? %s\n'%(hapData['Extinction'][0],hapData['Extinction'][1]))
2622                        if hapData['Pref.Ori.'][0] == 'MD':
2623                            Ax = hapData['Pref.Ori.'][3]
2624                            pFile.write(' March-Dollase PO: %10.4f Refine? %s Axis: %d %d %d\n'%
2625                                (hapData['Pref.Ori.'][1],hapData['Pref.Ori.'][2],Ax[0],Ax[1],Ax[2]))
2626                        else: #'SH' for spherical harmonics
2627                            PrintSHPO(hapData['Pref.Ori.'])
2628                            pFile.write(' Penalty hkl list: %s tolerance: %.2f\n'%(controlDict[pfx+'SHhkl'],controlDict[pfx+'SHtoler']))
2629                    PrintSize(hapData['Size'])
2630                    PrintMuStrain(hapData['Mustrain'],SGData)
2631                    PrintHStrain(hapData['HStrain'],SGData)
2632                    if 'Layer Disp' in hapData:
2633                        pFile.write(' Layer Displacement: %10.3f Refine? %s\n'%(hapData['Layer Disp'][0],hapData['Layer Disp'][1]))
2634                    if Phases[phase]['General']['Type'] != 'magnetic':
2635                        if hapData['Babinet']['BabA'][0]:
2636                            PrintBabinet(hapData['Babinet'])
2637                if phase in Histogram['Reflection Lists'] and 'RefList' not in Histogram['Reflection Lists'][phase] and hapData.get('LeBail',False):
2638                    hapData['newLeBail'] = True
2639                if resetRefList and (not hapDict[pfx+'LeBail'] or (hapData.get('LeBail',False) and hapData['newLeBail'])):
2640                    if hapData.get('LeBail',True):         #stop regeneating reflections for LeBail
2641                        hapData['newLeBail'] = False
2642                    refList = []
2643#                    Uniq = []
2644#                    Phi = []
2645                    useExt = 'magnetic' in Phases[phase]['General']['Type'] and 'N' in inst['Type'][0]
2646                    if Phases[phase]['General'].get('Modulated',False):
2647                        ifSuper = True
2648                        HKLd = np.array(G2lat.GenSSHLaue(dmin,SGData,SSGData,Vec,maxH,A))
2649                        HKLd = G2mth.sortArray(HKLd,4,reverse=True)
2650                        for h,k,l,m,d in HKLd:
2651                            ext,mul,uniq,phi = G2spc.GenHKLf([h,k,l],SGData)
2652                            mul *= 2      # for powder overlap of Friedel pairs
2653                            if m or not ext or useExt:
2654                                if 'C' in inst['Type'][0]:
2655                                    pos = G2lat.Dsp2pos(inst,d)
2656                                    if limits[0] < pos < limits[1]:
2657                                        refList.append([h,k,l,m,mul,d, pos,0.0,0.0,0.0,1., 0.0,0.0,1.0,1.0,1.0])
2658                                        #... sig,gam,fotsq,fctsq, phase,icorr,prfo,abs,ext
2659#                                        Uniq.append(uniq)
2660#                                        Phi.append(phi)
2661                                elif 'T' in inst['Type'][0]:
2662                                    pos = G2lat.Dsp2pos(inst,d)
2663                                    if limits[0] < pos < limits[1]:
2664                                        wave = inst['difC'][1]*d/(252.816*inst['fltPath'][0])
2665                                        refList.append([h,k,l,m,mul,d, pos,0.0,0.0,0.0,1., 0.0,0.0,0.0,0.0,wave, 1.0,1.0,1.0])
2666                                        # ... sig,gam,fotsq,fctsq, phase,icorr,alp,bet,wave, prfo,abs,ext
2667                                        #TODO - if tabulated put alp & bet in here
2668#                                        Uniq.append(uniq)
2669#                                        Phi.append(phi)
2670                                elif 'B' in inst['Type'][0]:
2671                                    pos = G2lat.Dsp2pos(inst,d)
2672                                    if limits[0] < pos < limits[1]:
2673                                        refList.append([h,k,l,m,mul,d, pos,0.0,0.0,0.0,1., 0.0,0.0,0.0,0.0, 1.0,1.0,1.0])
2674                                        # ... sig,gam,fotsq,fctsq, phase,icorr,alp,bet, prfo,abs,ext
2675                    else:
2676                        ifSuper = False
2677                        HKLd = np.array(G2lat.GenHLaue(dmin,SGData,A))
2678                        HKLd = G2mth.sortArray(HKLd,3,reverse=True)
2679                        for h,k,l,d in HKLd:
2680                            ext,mul,uniq,phi = G2spc.GenHKLf([h,k,l],SGData)
2681                            if ext and 'N' in inst['Type'][0] and  'MagSpGrp' in SGData:
2682                                ext = G2spc.checkMagextc([h,k,l],SGData)
2683                            mul *= 2      # for powder overlap of Friedel pairs
2684                            if ext and not useExt:
2685                                continue
2686                            if 'C' in inst['Type'][0]:
2687                                pos = G2lat.Dsp2pos(inst,d)
2688                                if limits[0] < pos < limits[1]:
2689                                    refList.append([h,k,l,mul,d, pos,0.0,0.0,0.0,1., 0.0,0.0,1.0,1.0,1.0])
2690                                    #... sig,gam,fotsq,fctsq, phase,icorr,prfo,abs,ext
2691#                                    Uniq.append(uniq)
2692#                                    Phi.append(phi)
2693                            elif 'T' in inst['Type'][0]:
2694                                pos = G2lat.Dsp2pos(inst,d)
2695                                if limits[0] < pos < limits[1]:
2696                                    wave = inst['difC'][1]*d/(252.816*inst['fltPath'][0])
2697                                    refList.append([h,k,l,mul,d, pos,0.0,0.0,0.0,1., 0.0,0.0,0.0,0.0,wave, 1.0,1.0,1.0])
2698                                    # ... sig,gam,fotsq,fctsq, phase,icorr,alp,bet,wave, prfo,abs,ext
2699#                                    Uniq.append(uniq)
2700#                                    Phi.append(phi)
2701                            elif 'B' in inst['Type'][0]:
2702                                pos = G2lat.Dsp2pos(inst,d)
2703                                if limits[0] < pos < limits[1]:
2704                                    refList.append([h,k,l,mul,d, pos,0.0,0.0,0.0,1., 0.0,0.0,0.0,0.0, 1.0,1.0,1.0])
2705                                    # ... sig,gam,fotsq,fctsq, phase,icorr,alp,bet, prfo,abs,ext
2706                    Histogram['Reflection Lists'][phase] = {'RefList':np.array(refList),'FF':{},'Type':inst['Type'][0],'Super':ifSuper}
2707            elif 'HKLF' in histogram:
2708                inst = Histogram['Instrument Parameters'][0]
2709                hId = Histogram['hId']
2710                hfx = ':%d:'%(hId)
2711                for item in inst:
2712                    if type(inst) is not list and item != 'Type': continue # skip over non-refined items (such as InstName)
2713                    hapDict[hfx+item] = inst[item][1]
2714                pfx = str(pId)+':'+str(hId)+':'
2715                hapDict[pfx+'Scale'] = hapData['Scale'][0]
2716                if hapData['Scale'][1]:
2717                    hapVary.append(pfx+'Scale')
2718                               
2719                extApprox,extType,extParms = hapData['Extinction']
2720                controlDict[pfx+'EType'] = extType
2721                controlDict[pfx+'EApprox'] = extApprox
2722                if 'C' in inst['Type'][0]:
2723                    controlDict[pfx+'Tbar'] = extParms['Tbar']
2724                    controlDict[pfx+'Cos2TM'] = extParms['Cos2TM']
2725                if 'Primary' in extType:
2726                    Ekey = ['Ep',]
2727                elif 'I & II' in extType:
2728                    Ekey = ['Eg','Es']
2729                elif 'Secondary Type II' == extType:
2730                    Ekey = ['Es',]
2731                elif 'Secondary Type I' == extType:
2732                    Ekey = ['Eg',]
2733                else:   #'None'
2734                    Ekey = []
2735                for eKey in Ekey:
2736                    hapDict[pfx+eKey] = extParms[eKey][0]
2737                    if extParms[eKey][1]:
2738                        hapVary.append(pfx+eKey)
2739                for bab in ['BabA','BabU']:
2740                    hapDict[pfx+bab] = hapData['Babinet'][bab][0]
2741                    if hapData['Babinet'][bab][1]:
2742                        hapVary.append(pfx+bab)
2743                Twins = hapData.get('Twins',[[np.array([[1,0,0],[0,1,0],[0,0,1]]),[1.0,False,0]],])
2744                if len(Twins) == 1:
2745                    hapDict[pfx+'Flack'] = hapData.get('Flack',[0.,False])[0]
2746                    if hapData.get('Flack',[0,False])[1]:
2747                        hapVary.append(pfx+'Flack')
2748                sumTwFr = 0.
2749                controlDict[pfx+'TwinLaw'] = []
2750                controlDict[pfx+'TwinInv'] = []
2751                NTL = 0           
2752                for it,twin in enumerate(Twins):
2753                    if 'bool' in str(type(twin[0])):
2754                        controlDict[pfx+'TwinInv'].append(twin[0])
2755                        controlDict[pfx+'TwinLaw'].append(np.zeros((3,3)))
2756                    else:
2757                        NTL += 1
2758                        controlDict[pfx+'TwinInv'].append(False)
2759                        controlDict[pfx+'TwinLaw'].append(twin[0])
2760                    if it:
2761                        hapDict[pfx+'TwinFr:'+str(it)] = twin[1]
2762                        sumTwFr += twin[1]
2763                    else:
2764                        hapDict[pfx+'TwinFr:0'] = twin[1][0]
2765                        controlDict[pfx+'TwinNMN'] = twin[1][1]
2766                    if Twins[0][1][1]:
2767                        hapVary.append(pfx+'TwinFr:'+str(it))
2768                controlDict[pfx+'NTL'] = NTL
2769                #need to make constraint on TwinFr
2770                controlDict[pfx+'TwinLaw'] = np.array(controlDict[pfx+'TwinLaw'])
2771                if len(Twins) > 1:    #force sum to unity
2772                    hapDict[pfx+'TwinFr:0'] = 1.-sumTwFr
2773                if Print: 
2774                    pFile.write('\n Phase: %s in histogram: %s\n'%(phase,histogram))
2775                    pFile.write(135*'='+'\n')
2776                    pFile.write(' Scale factor     : %10.4g Refine? %s\n'%(hapData['Scale'][0],hapData['Scale'][1]))
2777                    if extType != 'None':
2778                        pFile.write(' Extinction  Type: %15s approx: %10s\n'%(extType,extApprox))
2779                        text = ' Parameters       :'
2780                        for eKey in Ekey:
2781                            text += ' %4s : %10.3e Refine? '%(eKey,extParms[eKey][0])+str(extParms[eKey][1])
2782                        pFile.write(text+'\n')
2783                    if hapData['Babinet']['BabA'][0]:
2784                        PrintBabinet(hapData['Babinet'])
2785                    if not SGData['SGInv'] and len(Twins) == 1:
2786                        pFile.write(' Flack parameter: %10.3f Refine? %s\n'%(hapData['Flack'][0],hapData['Flack'][1]))
2787                    if len(Twins) > 1:
2788                        for it,twin in enumerate(Twins):
2789                            if 'bool' in str(type(twin[0])):
2790                                pFile.write(' Nonmerohedral twin fr.: %5.3f Inv? %s Refine? %s\n'%
2791                                    (hapDict[pfx+'TwinFr:'+str(it)],str(controlDict[pfx+'TwinInv'][it]),str(Twins[0][1][1])))
2792                            else:
2793                                pFile.write(' Twin law: %s Twin fr.: %5.3f Refine? %s\n'%
2794                                    (str(twin[0]).replace('\n',','),hapDict[pfx+'TwinFr:'+str(it)],str(Twins[0][1][1])))
2795                       
2796                Histogram['Reflection Lists'] = phase       
2797               
2798    return hapVary,hapDict,controlDict
2799   
2800def SetHistogramPhaseData(parmDict,sigDict,Phases,Histograms,calcControls,Print=True,pFile=None):
2801    'needs a doc string'
2802   
2803    def PrintSizeAndSig(hapData,sizeSig):
2804        line = '\n Size model:     %9s'%(hapData[0])
2805        refine = False
2806        if hapData[0] in ['isotropic','uniaxial']:
2807            line += ' equatorial:%12.5f'%(hapData[1][0])
2808            if sizeSig[0][0]:
2809                line += ', sig:%8.4f'%(sizeSig[0][0])
2810                refine = True
2811            if hapData[0] == 'uniaxial':
2812                line += ' axial:%12.4f'%(hapData[1][1])
2813                if sizeSig[0][1]:
2814                    refine = True
2815                    line += ', sig:%8.4f'%(sizeSig[0][1])
2816            line += ' LG mix coeff.:%12.4f'%(hapData[1][2])
2817            if sizeSig[0][2]:
2818                refine = True
2819                line += ', sig:%8.4f'%(sizeSig[0][2])
2820            if refine:
2821                pFile.write(line+'\n')
2822        else:
2823            line += ' LG mix coeff.:%12.4f'%(hapData[1][2])
2824            if sizeSig[0][2]:
2825                refine = True
2826                line += ', sig:%8.4f'%(sizeSig[0][2])
2827            Snames = ['S11','S22','S33','S12','S13','S23']
2828            ptlbls = ' name  :'
2829            ptstr =  ' value :'
2830            sigstr = ' sig   :'
2831            for i,name in enumerate(Snames):
2832                ptlbls += '%12s' % (name)
2833                ptstr += '%12.6f' % (hapData[4][i])
2834                if sizeSig[1][i]:
2835                    refine = True
2836                    sigstr += '%12.6f' % (sizeSig[1][i])
2837                else:
2838                    sigstr += 12*' '
2839            if refine:
2840                pFile.write(line+'\n')
2841                pFile.write(ptlbls+'\n')
2842                pFile.write(ptstr+'\n')
2843                pFile.write(sigstr+'\n')
2844       
2845    def PrintMuStrainAndSig(hapData,mustrainSig,SGData):
2846        line = '\n Mustrain model: %9s\n'%(hapData[0])
2847        refine = False
2848        if hapData[0] in ['isotropic','uniaxial']:
2849            line += ' equatorial:%12.1f'%(hapData[1][0])
2850            if mustrainSig[0][0]:
2851                line += ', sig:%8.1f'%(mustrainSig[0][0])
2852                refine = True
2853            if hapData[0] == 'uniaxial':
2854                line += ' axial:%12.1f'%(hapData[1][1])
2855                if mustrainSig[0][1]:
2856                     line += ', sig:%8.1f'%(mustrainSig[0][1])
2857            line += ' LG mix coeff.:%12.4f'%(hapData[1][2])
2858            if mustrainSig[0][2]:
2859                refine = True
2860                line += ', sig:%8.3f'%(mustrainSig[0][2])
2861            if refine:
2862                pFile.write(line+'\n')
2863        else:
2864            line += ' LG mix coeff.:%12.4f'%(hapData[1][2])
2865            if mustrainSig[0][2]:
2866                refine = True
2867                line += ', sig:%8.3f'%(mustrainSig[0][2])
2868            Snames = G2spc.MustrainNames(SGData)
2869            ptlbls = ' name  :'
2870            ptstr =  ' value :'
2871            sigstr = ' sig   :'
2872            for i,name in enumerate(Snames):
2873                ptlbls += '%12s' % (name)
2874                ptstr += '%12.1f' % (hapData[4][i])
2875                if mustrainSig[1][i]:
2876                    refine = True
2877                    sigstr += '%12.1f' % (mustrainSig[1][i])
2878                else:
2879                    sigstr += 12*' '
2880            if refine:
2881                pFile.write(line+'\n')
2882                pFile.write(ptlbls+'\n')
2883                pFile.write(ptstr+'\n')
2884                pFile.write(sigstr+'\n')
2885           
2886    def PrintHStrainAndSig(hapData,strainSig,SGData):
2887        Hsnames = G2spc.HStrainNames(SGData)
2888        ptlbls = ' name  :'
2889        ptstr =  ' value :'
2890        sigstr = ' sig   :'
2891        refine = False
2892        for i,name in enumerate(Hsnames):
2893            ptlbls += '%12s' % (name)
2894            ptstr += '%12.4g' % (hapData[0][i])
2895            if name in strainSig:
2896                refine = True
2897                sigstr += '%12.4g' % (strainSig[name])
2898            else:
2899                sigstr += 12*' '
2900        if refine:
2901            pFile.write('\n Hydrostatic/elastic strain:\n')
2902            pFile.write(ptlbls+'\n')
2903            pFile.write(ptstr+'\n')
2904            pFile.write(sigstr+'\n')
2905       
2906    def PrintSHPOAndSig(pfx,hapData,POsig):
2907        Tindx = 1.0
2908        Tvar = 0.0
2909        pFile.write('\n Spherical harmonics preferred orientation: Order: %d\n'%hapData[4])
2910        ptlbls = ' names :'
2911        ptstr =  ' values:'
2912        sigstr = ' sig   :'
2913        for item in hapData[5]:
2914            ptlbls += '%12s'%(item)
2915            ptstr += '%12.3f'%(hapData[5][item])
2916            l = 2.0*eval(item.strip('C'))[0]+1
2917            Tindx += hapData[5][item]**2/l
2918            if pfx+item in POsig:
2919                Tvar += (2.*hapData[5][item]*POsig[pfx+item]/l)**2
2920                sigstr += '%12.3f'%(POsig[pfx+item])
2921            else:
2922                sigstr += 12*' ' 
2923        pFile.write(ptlbls+'\n')
2924        pFile.write(ptstr+'\n')
2925        pFile.write(sigstr+'\n')
2926        pFile.write('\n Texture index J = %.3f(%d)\n'%(Tindx,int(1000*np.sqrt(Tvar))))
2927       
2928    def PrintExtAndSig(pfx,hapData,ScalExtSig):
2929        pFile.write('\n Single crystal extinction: Type: %s Approx: %s\n'%(hapData[0],hapData[1]))
2930        text = ''
2931        for item in hapData[2]:
2932            if pfx+item in ScalExtSig:
2933                text += '       %s: '%(item)
2934                text += '%12.2e'%(hapData[2][item][0])
2935                if pfx+item in ScalExtSig:
2936                    text += ' sig: %12.2e'%(ScalExtSig[pfx+item])
2937        pFile.write(text+'\n')   
2938       
2939    def PrintBabinetAndSig(pfx,hapData,BabSig):
2940        pFile.write('\n Babinet form factor modification:\n')
2941        ptlbls = ' names :'
2942        ptstr =  ' values:'
2943        sigstr = ' sig   :'
2944        for item in hapData:
2945            ptlbls += '%12s'%(item)
2946            ptstr += '%12.3f'%(hapData[item][0])
2947            if pfx+item in BabSig:
2948                sigstr += '%12.3f'%(BabSig[pfx+item])
2949            else:
2950                sigstr += 12*' ' 
2951        pFile.write(ptlbls+'\n')
2952        pFile.write(ptstr+'\n')
2953        pFile.write(sigstr+'\n')
2954       
2955    def PrintTwinsAndSig(pfx,twinData,TwinSig):
2956        pFile.write('\n Twin Law fractions :\n')
2957        ptlbls = ' names :'
2958        ptstr =  ' values:'
2959        sigstr = ' sig   :'
2960        for it,item in enumerate(twinData):
2961            ptlbls += '     twin #%d'%(it)
2962            if it:
2963                ptstr += '%12.3f'%(item[1])
2964            else:
2965                ptstr += '%12.3f'%(item[1][0])
2966            if pfx+'TwinFr:'+str(it) in TwinSig:
2967                sigstr += '%12.3f'%(TwinSig[pfx+'TwinFr:'+str(it)])
2968            else:
2969                sigstr += 12*' ' 
2970        pFile.write(ptlbls+'\n')
2971        pFile.write(ptstr+'\n')
2972        pFile.write(sigstr+'\n')
2973       
2974   
2975    PhFrExtPOSig = {}
2976    SizeMuStrSig = {}
2977    ScalExtSig = {}
2978    BabSig = {}
2979    TwinFrSig = {}
2980    wtFrSum = {}
2981    for phase in Phases:
2982        HistoPhase = Phases[phase]['Histograms']
2983        General = Phases[phase]['General']
2984        SGData = General['SGData']
2985        pId = Phases[phase]['pId']
2986        histoList = list(HistoPhase.keys())
2987        histoList.sort()
2988        for histogram in histoList:
2989            try:
2990                Histogram = Histograms[histogram]
2991            except KeyError:                       
2992                #skip if histogram not included e.g. in a sequential refinement
2993                continue
2994            if not Phases[phase]['Histograms'][histogram]['Use']:
2995                #skip if phase absent from this histogram
2996                continue
2997            hapData = HistoPhase[histogram]
2998            hId = Histogram['hId']
2999            pfx = str(pId)+':'+str(hId)+':'
3000            if hId not in wtFrSum:
3001                wtFrSum[hId] = 0.
3002            if 'PWDR' in histogram:
3003                for item in ['Scale','Extinction']:
3004                    hapData[item][0] = parmDict[pfx+item]
3005                    if pfx+item in sigDict and not parmDict[pfx+'LeBail']:
3006                        PhFrExtPOSig.update({pfx+item:sigDict[pfx+item],})
3007                wtFrSum[hId] += hapData['Scale'][0]*General['Mass']
3008                if hapData['Pref.Ori.'][0] == 'MD':
3009                    hapData['Pref.Ori.'][1] = parmDict[pfx+'MD']
3010                    if pfx+'MD' in sigDict and not parmDict[pfx+'LeBail']:
3011                        PhFrExtPOSig.update({pfx+'MD':sigDict[pfx+'MD'],})
3012                else:                           #'SH' spherical harmonics
3013                    for item in hapData['Pref.Ori.'][5]:
3014                        hapData['Pref.Ori.'][5][item] = parmDict[pfx+item]
3015                        if pfx+item in sigDict and not parmDict[pfx+'LeBail']:
3016                            PhFrExtPOSig.update({pfx+item:sigDict[pfx+item],})
3017                SizeMuStrSig.update({pfx+'Mustrain':[[0,0,0],[0 for i in range(len(hapData['Mustrain'][4]))]],
3018                    pfx+'Size':[[0,0,0],[0 for i in range(len(hapData['Size'][4]))]],
3019                    pfx+'HStrain':{}})                 
3020                for item in ['Mustrain','Size']:
3021                    hapData[item][1][2] = parmDict[pfx+item+';mx']
3022#                    hapData[item][1][2] = min(1.,max(0.,hapData[item][1][2]))
3023                    if pfx+item+';mx' in sigDict:
3024                        SizeMuStrSig[pfx+item][0][2] = sigDict[pfx+item+';mx']
3025                    if hapData[item][0] in ['isotropic','uniaxial']:                   
3026                        hapData[item][1][0] = parmDict[pfx+item+';i']
3027                        if item == 'Size':
3028                            hapData[item][1][0] = min(10.,max(0.001,hapData[item][1][0]))
3029                        if pfx+item+';i' in sigDict: 
3030                            SizeMuStrSig[pfx+item][0][0] = sigDict[pfx+item+';i']
3031                        if hapData[item][0] == 'uniaxial':
3032                            hapData[item][1][1] = parmDict[pfx+item+';a']
3033                            if item == 'Size':
3034                                hapData[item][1][1] = min(10.,max(0.001,hapData[item][1][1]))                       
3035                            if pfx+item+';a' in sigDict:
3036                                SizeMuStrSig[pfx+item][0][1] = sigDict[pfx+item+';a']
3037                    else:       #generalized for mustrain or ellipsoidal for size
3038                        Nterms = len(hapData[item][4])
3039                        for i in range(Nterms):
3040                            sfx = ';'+str(i)
3041                            hapData[item][4][i] = parmDict[pfx+item+sfx]
3042                            if pfx+item+sfx in sigDict:
3043                                SizeMuStrSig[pfx+item][1][i] = sigDict[pfx+item+sfx]
3044                names = G2spc.HStrainNames(SGData)
3045                for i,name in enumerate(names):
3046                    hapData['HStrain'][0][i] = parmDict[pfx+name]
3047                    if pfx+name in sigDict:
3048                        SizeMuStrSig[pfx+'HStrain'][name] = sigDict[pfx+name]
3049                if 'Layer Disp' in hapData:
3050                    hapData['Layer Disp'][0] = parmDict[pfx+'LayerDisp']
3051                    if pfx+'LayerDisp' in sigDict:
3052                        SizeMuStrSig[pfx+'LayerDisp'] = sigDict[pfx+'LayerDisp']
3053                if Phases[phase]['General']['Type'] != 'magnetic':
3054                    for name in ['BabA','BabU']:
3055                        hapData['Babinet'][name][0] = parmDict[pfx+name]
3056                        if pfx+name in sigDict and not parmDict[pfx+'LeBail']:
3057                            BabSig[pfx+name] = sigDict[pfx+name]               
3058               
3059            elif 'HKLF' in histogram:
3060                for item in ['Scale','Flack']:
3061                    if parmDict.get(pfx+item):
3062                        hapData[item][0] = parmDict[pfx+item]
3063                        if pfx+item in sigDict:
3064                            ScalExtSig[pfx+item] = sigDict[pfx+item]
3065                for item in ['Ep','Eg','Es']:
3066                    if parmDict.get(pfx+item):
3067                        hapData['Extinction'][2][item][0] = parmDict[pfx+item]
3068                        if pfx+item in sigDict:
3069                            ScalExtSig[pfx+item] = sigDict[pfx+item]
3070                for item in ['BabA','BabU']:
3071                    hapData['Babinet'][item][0] = parmDict[pfx+item]
3072                    if pfx+item in sigDict:
3073                        BabSig[pfx+item] = sigDict[pfx+item]
3074                if 'Twins' in hapData:
3075                    it = 1
3076                    sumTwFr = 0.
3077                    while True:
3078                        try:
3079                            hapData['Twins'][it][1] = parmDict[pfx+'TwinFr:'+str(it)]
3080                            if pfx+'TwinFr:'+str(it) in sigDict:
3081                                TwinFrSig[pfx+'TwinFr:'+str(it)] = sigDict[pfx+'TwinFr:'+str(it)]
3082                            if it:
3083                                sumTwFr += hapData['Twins'][it][1]
3084                            it += 1
3085                        except KeyError:
3086                            break
3087                    hapData['Twins'][0][1][0] = 1.-sumTwFr
3088
3089    if Print:
3090        for phase in Phases:
3091            HistoPhase = Phases[phase]['Histograms']
3092            General = Phases[phase]['General']
3093            SGData = General['SGData']
3094            pId = Phases[phase]['pId']
3095            histoList = list(HistoPhase.keys())
3096            histoList.sort()
3097            for histogram in histoList:
3098                try:
3099                    Histogram = Histograms[histogram]
3100                except KeyError:                       
3101                    #skip if histogram not included e.g. in a sequential refinement
3102                    continue
3103                hapData = HistoPhase[histogram]
3104                hId = Histogram['hId']
3105                Histogram['Residuals'][str(pId)+'::Name'] = phase
3106                pfx = str(pId)+':'+str(hId)+':'
3107                hfx = ':%s:'%(hId)
3108                if pfx+'Nref' not in Histogram['Residuals']:    #skip not used phase in histogram
3109                    continue
3110                pFile.write('\n Phase: %s in histogram: %s\n'%(phase,histogram))
3111                pFile.write(135*'='+'\n')
3112                Inst = Histogram['Instrument Parameters'][0]
3113                if 'PWDR' in histogram:
3114                    pFile.write(' Final refinement RF, RF^2 = %.2f%%, %.2f%% on %d reflections\n'%
3115                        (Histogram['Residuals'][pfx+'Rf'],Histogram['Residuals'][pfx+'Rf^2'],Histogram['Residuals'][pfx+'Nref']))
3116                    pFile.write(' Durbin-Watson statistic = %.3f\n'%(Histogram['Residuals']['Durbin-Watson']))
3117                    pFile.write(' Bragg intensity sum = %.3g\n'%(Histogram['Residuals'][pfx+'sumInt']))
3118                   
3119                    if parmDict[pfx+'LeBail']:
3120                        pFile.write(' Performed LeBail extraction for phase %s in histogram %s\n'%(phase,histogram))
3121                    else:
3122                        # if calcControls != None:    #skipped in seqRefine
3123                        #     if 'X'in Inst['Type'][0]:
3124                        #         PrintFprime(calcControls['FFtables'],hfx,pFile)
3125                        #     elif 'NC' in Inst['Type'][0]:
3126                        #         PrintBlength(calcControls['BLtables'],Inst['Lam'][1],pFile)
3127                        if pfx+'Scale' in PhFrExtPOSig:
3128                            wtFr = hapData['Scale'][0]*General['Mass']/wtFrSum[hId]
3129                            sigwtFr = PhFrExtPOSig[pfx+'Scale']*wtFr/hapData['Scale'][0]
3130                            pFile.write(' Phase fraction  : %10.5g, sig %10.5g Weight fraction  : %8.5f, sig %10.5f\n'%
3131                                (hapData['Scale'][0],PhFrExtPOSig[pfx+'Scale'],wtFr,sigwtFr))
3132                        if pfx+'Extinction' in PhFrExtPOSig:
3133                            pFile.write(' Extinction coeff: %10.4f, sig %10.4f\n'%(hapData['Extinction'][0],PhFrExtPOSig[pfx+'Extinction']))
3134                        if hapData['Pref.Ori.'][0] == 'MD':
3135                            if pfx+'MD' in PhFrExtPOSig:
3136                                pFile.write(' March-Dollase PO: %10.4f, sig %10.4f\n'%(hapData['Pref.Ori.'][1],PhFrExtPOSig[pfx+'MD']))
3137                        else:
3138                            PrintSHPOAndSig(pfx,hapData['Pref.Ori.'],PhFrExtPOSig)
3139                    PrintSizeAndSig(hapData['Size'],SizeMuStrSig[pfx+'Size'])
3140                    PrintMuStrainAndSig(hapData['Mustrain'],SizeMuStrSig[pfx+'Mustrain'],SGData)
3141                    PrintHStrainAndSig(hapData['HStrain'],SizeMuStrSig[pfx+'HStrain'],SGData)
3142                    if pfx+'LayerDisp' in SizeMuStrSig:
3143                        pFile.write(' Layer displacement : %10.3f, sig %10.3f\n'%(hapData['Layer Disp'][0],SizeMuStrSig[pfx+'LayerDisp']))           
3144                    if Phases[phase]['General']['Type'] != 'magnetic' and not parmDict[pfx+'LeBail']:
3145                        if len(BabSig):
3146                            PrintBabinetAndSig(pfx,hapData['Babinet'],BabSig)
3147                   
3148                elif 'HKLF' in histogram:
3149                    pFile.write(' Final refinement RF, RF^2 = %.2f%%, %.2f%% on %d reflections (%d user rejected, %d sp.gp.extinct)\n'%
3150                        (Histogram['Residuals'][pfx+'Rf'],Histogram['Residuals'][pfx+'Rf^2'],Histogram['Residuals'][pfx+'Nref'],
3151                        Histogram['Residuals'][pfx+'Nrej'],Histogram['Residuals'][pfx+'Next']))
3152                    if calcControls != None:    #skipped in seqRefine
3153                        if 'X'in Inst['Type'][0]:
3154                            PrintFprime(calcControls['FFtables'],hfx,pFile)
3155                        elif 'NC' in Inst['Type'][0]:
3156                            PrintBlength(calcControls['BLtables'],Inst['Lam'][1],pFile)
3157                    pFile.write(' HKLF histogram weight factor = %.3f\n'%(Histogram['wtFactor']))
3158                    if pfx+'Scale' in ScalExtSig:
3159                        pFile.write(' Scale factor : %10.4g, sig %10.4g\n'%(hapData['Scale'][0],ScalExtSig[pfx+'Scale']))
3160                    if hapData['Extinction'][0] != 'None':
3161                        PrintExtAndSig(pfx,hapData['Extinction'],ScalExtSig)
3162                    if len(BabSig):
3163                        PrintBabinetAndSig(pfx,hapData['Babinet'],BabSig)
3164                    if pfx+'Flack' in ScalExtSig:
3165                        pFile.write(' Flack parameter : %10.3f, sig %10.3f\n'%(hapData['Flack'][0],ScalExtSig[pfx+'Flack']))
3166                    if len(TwinFrSig):
3167                        PrintTwinsAndSig(pfx,hapData['Twins'],TwinFrSig)
3168
3169################################################################################
3170##### Histogram data
3171################################################################################       
3172                   
3173def GetHistogramData(Histograms,Print=True,pFile=None):
3174    'needs a doc string'
3175   
3176    def GetBackgroundParms(hId,Background):
3177        Back = Background[0]
3178        DebyePeaks = Background[1]
3179        bakType,bakFlag = Back[:2]
3180        backVals = Back[3:]
3181        backNames = [':'+str(hId)+':Back;'+str(i) for i in range(len(backVals))]
3182        backDict = dict(zip(backNames,backVals))
3183        backVary = []
3184        if bakFlag:
3185            backVary = backNames
3186        backDict[':'+str(hId)+':nDebye'] = DebyePeaks['nDebye']
3187        backDict[':'+str(hId)+':nPeaks'] = DebyePeaks['nPeaks']
3188        debyeDict = {}
3189        debyeList = []
3190        for i in range(DebyePeaks['nDebye']):
3191            debyeNames = [':'+str(hId)+':DebyeA;'+str(i),':'+str(hId)+':DebyeR;'+str(i),':'+str(hId)+':DebyeU;'+str(i)]
3192            debyeDict.update(dict(zip(debyeNames,DebyePeaks['debyeTerms'][i][::2])))
3193            debyeList += zip(debyeNames,DebyePeaks['debyeTerms'][i][1::2])
3194        debyeVary = []
3195        for item in debyeList:
3196            if item[1]:
3197                debyeVary.append(item[0])
3198        backDict.update(debyeDict)
3199        backVary += debyeVary
3200        peakDict = {}
3201        peakList = []
3202        for i in range(DebyePeaks['nPeaks']):
3203            peakNames = [':'+str(hId)+':BkPkpos;'+str(i),':'+str(hId)+ \
3204                ':BkPkint;'+str(i),':'+str(hId)+':BkPksig;'+str(i),':'+str(hId)+':BkPkgam;'+str(i)]
3205            peakDict.update(dict(zip(peakNames,DebyePeaks['peaksList'][i][::2])))
3206            peakList += zip(peakNames,DebyePeaks['peaksList'][i][1::2])
3207        peakVary = []
3208        for item in peakList:
3209            if item[1]:
3210                peakVary.append(item[0])
3211        backDict.update(peakDict)
3212        backVary += peakVary
3213        if 'background PWDR' in Background[1]:
3214            backDict[':'+str(hId)+':Back File'] = Background[1]['background PWDR'][0]
3215            backDict[':'+str(hId)+':BF mult'] = Background[1]['background PWDR'][1]
3216            try:
3217                if Background[1]['background PWDR'][0] and Background[1]['background PWDR'][2]:
3218                    backVary.append(':'+str(hId)+':BF mult')
3219            except IndexError:  # old version without refine flag
3220                pass
3221        return bakType,backDict,backVary           
3222       
3223    def GetInstParms(hId,Inst):
3224        #patch
3225        if 'Z' not in Inst:
3226            Inst['Z'] = [0.0,0.0,False]
3227        dataType = Inst['Type'][0]
3228        instDict = {}
3229        insVary = []
3230        pfx = ':'+str(hId)+':'
3231        insKeys = list(Inst.keys())
3232        insKeys.sort()
3233        for item in insKeys:
3234            if item in 'Azimuth':
3235                continue
3236            insName = pfx+item
3237            instDict[insName] = Inst[item][1]
3238            if len(Inst[item]) > 2 and Inst[item][2]:
3239                insVary.append(insName)
3240        if 'C' in dataType:
3241            instDict[pfx+'SH/L'] = max(instDict[pfx+'SH/L'],0.0005)
3242        elif 'T' in dataType:   #trap zero alp, bet coeff.
3243            if not instDict[pfx+'alpha']:
3244                instDict[pfx+'alpha'] = 1.0
3245            if not instDict[pfx+'beta-0'] and not instDict[pfx+'beta-1']:
3246                instDict[pfx+'beta-1'] = 1.0
3247        elif 'B' in dataType:   #trap zero alp, bet coeff.
3248            if not instDict[pfx+'alpha-0'] and not instDict[pfx+'alpha-1']:
3249                instDict[pfx+'alpha-1'] = 1.0
3250            if not instDict[pfx+'beta-0'] and not instDict[pfx+'beta-1']:
3251                instDict[pfx+'beta-1'] = 1.0
3252        return dataType,instDict,insVary
3253       
3254    def GetSampleParms(hId,Sample):
3255        sampVary = []
3256        hfx = ':'+str(hId)+':'       
3257        sampDict = {hfx+'Gonio. radius':Sample['Gonio. radius'],hfx+'Omega':Sample['Omega'],
3258            hfx+'Chi':Sample['Chi'],hfx+'Phi':Sample['Phi'],hfx+'Azimuth':Sample['Azimuth']}
3259        for key in ('Temperature','Pressure','FreePrm1','FreePrm2','FreePrm3'):
3260            if key in Sample:
3261                sampDict[hfx+key] = Sample[key]
3262        Type = Sample['Type']
3263        if 'Bragg' in Type:             #Bragg-Brentano
3264            for item in ['Scale','Shift','Transparency','SurfRoughA','SurfRoughB']:
3265                sampDict[hfx+item] = Sample[item][0]
3266                if Sample[item][1]:
3267                    sampVary.append(hfx+item)
3268        elif 'Debye' in Type:        #Debye-Scherrer
3269            for item in ['Scale','Absorption','DisplaceX','DisplaceY']:
3270                sampDict[hfx+item] = Sample[item][0]
3271                if Sample[item][1]:
3272                    sampVary.append(hfx+item)
3273        return Type,sampDict,sampVary
3274       
3275    def PrintBackground(Background):
3276        Back = Background[0]
3277        DebyePeaks = Background[1]
3278        pFile.write('\n Background function: %s Refine? %s\n'%(Back[0],Back[1]))
3279        line = ' Coefficients: '
3280        for i,back in enumerate(Back[3:]):
3281            line += '%10.3f'%(back)
3282            if i and not i%10:
3283                line += '\n'+15*' '
3284        pFile.write(line+'\n')
3285        if DebyePeaks['nDebye']:
3286            pFile.write('\n Debye diffuse scattering coefficients\n')
3287            parms = ['DebyeA','DebyeR','DebyeU']
3288            line = ' names :  '
3289            for parm in parms:
3290                line += '%8s refine?'%(parm)
3291            pFile.write(line+'\n')
3292            for j,term in enumerate(DebyePeaks['debyeTerms']):
3293                line = ' term'+'%2d'%(j)+':'
3294                for i in range(3):
3295                    line += '%10.3f %5s'%(term[2*i],bool(term[2*i+1]))                   
3296                pFile.write(line+'\n')
3297        if DebyePeaks['nPeaks']:
3298            pFile.write('\n Single peak coefficients\n')
3299            parms =    ['BkPkpos','BkPkint','BkPksig','BkPkgam']
3300            line = ' names :  '
3301            for parm in parms:
3302                line += '%8s refine?'%(parm)
3303            pFile.write(line+'\n')
3304            for j,term in enumerate(DebyePeaks['peaksList']):
3305                line = ' peak'+'%2d'%(j)+':'
3306                for i in range(4):
3307                    line += '%12.3f %5s'%(term[2*i],bool(term[2*i+1]))                   
3308                pFile.write(line+'\n')
3309        if 'background PWDR' in DebyePeaks:
3310            try:
3311                pFile.write(' Fixed background file: %s; mult: %.3f  Refine? %s\n'%(DebyePeaks['background PWDR'][0],
3312                    DebyePeaks['background PWDR'][1],DebyePeaks['background PWDR'][2]))
3313            except IndexError:  #old version without refine flag
3314                pass
3315       
3316    def PrintInstParms(Inst):
3317        pFile.write('\n Instrument Parameters:\n')
3318        insKeys = list(Inst.keys())
3319        insKeys.sort()
3320        iBeg = 0
3321        Ok = True
3322        while Ok:
3323            ptlbls = ' name  :'
3324            ptstr =  ' value :'
3325            varstr = ' refine:'
3326            iFin = min(iBeg+9,len(insKeys))
3327            for item in insKeys[iBeg:iFin]:
3328                if item not in ['Type','Source']:
3329                    ptlbls += '%12s' % (item)
3330                    ptstr += '%12.6f' % (Inst[item][1])
3331                    if item in ['Lam1','Lam2','Azimuth','fltPath','2-theta',]:
3332                        varstr += 12*' '
3333                    else:
3334                        varstr += '%12s' % (str(bool(Inst[item][2])))
3335            pFile.write(ptlbls+'\n')
3336            pFile.write(ptstr+'\n')
3337            pFile.write(varstr+'\n')
3338            iBeg = iFin
3339            if iBeg == len(insKeys):
3340                Ok = False
3341            else:
3342                pFile.write('\n')
3343       
3344    def PrintSampleParms(Sample):
3345        pFile.write('\n Sample Parameters:\n')
3346        pFile.write(' Goniometer omega = %.2f, chi = %.2f, phi = %.2f\n'%
3347            (Sample['Omega'],Sample['Chi'],Sample['Phi']))
3348        ptlbls = ' name  :'
3349        ptstr =  ' value :'
3350        varstr = ' refine:'
3351        if 'Bragg' in Sample['Type']:
3352            for item in ['Scale','Shift','Transparency','SurfRoughA','SurfRoughB']:
3353                ptlbls += '%14s'%(item)
3354                ptstr += '%14.4f'%(Sample[item][0])
3355                varstr += '%14s'%(str(bool(Sample[item][1])))
3356           
3357        elif 'Debye' in Type:        #Debye-Scherrer
3358            for item in ['Scale','Absorption','DisplaceX','DisplaceY']:
3359                ptlbls += '%14s'%(item)
3360                ptstr += '%14.4f'%(Sample[item][0])
3361                varstr += '%14s'%(str(bool(Sample[item][1])))
3362
3363        pFile.write(ptlbls+'\n')
3364        pFile.write(ptstr+'\n')
3365        pFile.write(varstr+'\n')
3366       
3367    histDict = {}
3368    histVary = []
3369    controlDict = {}
3370    histoList = list(Histograms.keys())
3371    histoList.sort()
3372    for histogram in histoList:
3373        if 'PWDR' in histogram:
3374            Histogram = Histograms[histogram]
3375            hId = Histogram['hId']
3376            pfx = ':'+str(hId)+':'
3377            controlDict[pfx+'wtFactor'] = Histogram['wtFactor']
3378            controlDict[pfx+'Limits'] = Histogram['Limits'][1]
3379            controlDict[pfx+'Exclude'] = Histogram['Limits'][2:]
3380            for excl in controlDict[pfx+'Exclude']:
3381                Histogram['Data'][0] = ma.masked_inside(Histogram['Data'][0],excl[0],excl[1])
3382            if controlDict[pfx+'Exclude']:
3383                ma.mask_rows(Histogram['Data'])
3384            Background = Histogram['Background']
3385            Type,bakDict,bakVary = GetBackgroundParms(hId,Background)
3386            controlDict[pfx+'bakType'] = Type
3387            histDict.update(bakDict)
3388            histVary += bakVary
3389           
3390            Inst = Histogram['Instrument Parameters']        #TODO ? ignores tabulated alp,bet & delt for TOF
3391            if 'T' in Type and len(Inst[1]):    #patch -  back-to-back exponential contribution to TOF line shape is removed
3392                G2fil.G2Print ('Warning: tabulated profile coefficients are ignored')
3393            Type,instDict,insVary = GetInstParms(hId,Inst[0])
3394            controlDict[pfx+'histType'] = Type
3395            if 'XC' in Type or 'XB' in Type:
3396                if pfx+'Lam1' in instDict:
3397                    controlDict[pfx+'keV'] = 12.397639/instDict[pfx+'Lam1']
3398                else:
3399                    controlDict[pfx+'keV'] = 12.397639/instDict[pfx+'Lam']           
3400            histDict.update(instDict)
3401            histVary += insVary
3402           
3403            Sample = Histogram['Sample Parameters']
3404            Type,sampDict,sampVary = GetSampleParms(hId,Sample)
3405            controlDict[pfx+'instType'] = Type
3406            histDict.update(sampDict)
3407            histVary += sampVary
3408           
3409   
3410            if Print: 
3411                pFile.write('\n Histogram: %s histogram Id: %d\n'%(histogram,hId))
3412                pFile.write(135*'='+'\n')
3413                Units = {'C':' deg','T':' msec','B':' deg'}
3414                units = Units[controlDict[pfx+'histType'][2]]
3415                Limits = controlDict[pfx+'Limits']
3416                pFile.write(' Instrument type: %s\n'%Sample['Type'])
3417                pFile.write(' Histogram limits: %8.2f%s to %8.2f%s\n'%(Limits[0],units,Limits[1],units))
3418                if len(controlDict[pfx+'Exclude']):
3419                    excls = controlDict[pfx+'Exclude']
3420                    for excl in excls:
3421                        pFile.write(' Excluded region:  %8.2f%s to %8.2f%s\n'%(excl[0],units,excl[1],units)) 
3422                PrintSampleParms(Sample)
3423                PrintInstParms(Inst[0])
3424                PrintBackground(Background)
3425        elif 'HKLF' in histogram:
3426            Histogram = Histograms[histogram]
3427            hId = Histogram['hId']
3428            pfx = ':'+str(hId)+':'
3429            controlDict[pfx+'wtFactor'] = Histogram['wtFactor']
3430            Inst = Histogram['Instrument Parameters'][0]
3431            controlDict[pfx+'histType'] = Inst['Type'][0]
3432            if 'X' in Inst['Type'][0]:
3433                histDict[pfx+'Lam'] = Inst['Lam'][1]
3434                controlDict[pfx+'keV'] = 12.397639/histDict[pfx+'Lam']
3435            elif 'NC' in Inst['Type'][0] or 'NB' in Inst['Type'][0]:                   
3436                histDict[pfx+'Lam'] = Inst['Lam'][1]
3437    return histVary,histDict,controlDict
3438   
3439def SetHistogramData(parmDict,sigDict,Histograms,calcControls,Print=True,pFile=None,seq=False):
3440    'Shows histogram data after a refinement'
3441   
3442    def SetBackgroundParms(pfx,Background,parmDict,sigDict):
3443        Back = Background[0]
3444        DebyePeaks = Background[1]
3445        lenBack = len(Back[3:])
3446        backSig = [0 for i in range(lenBack+3*DebyePeaks['nDebye']+4*DebyePeaks['nPeaks'])]
3447        for i in range(lenBack):
3448            Back[3+i] = parmDict[pfx+'Back;'+str(i)]
3449            if pfx+'Back;'+str(i) in sigDict:
3450                backSig[i] = sigDict[pfx+'Back;'+str(i)]
3451        if DebyePeaks['nDebye']:
3452            for i in range(DebyePeaks['nDebye']):
3453                names = [pfx+'DebyeA;'+str(i),pfx+'DebyeR;'+str(i),pfx+'DebyeU;'+str(i)]
3454                for j,name in enumerate(names):
3455                    DebyePeaks['debyeTerms'][i][2*j] = parmDict[name]
3456                    if name in sigDict:
3457                        backSig[lenBack+3*i+j] = sigDict[name]           
3458        if DebyePeaks['nPeaks']:
3459            for i in range(DebyePeaks['nPeaks']):
3460                names = [pfx+'BkPkpos;'+str(i),pfx+'BkPkint;'+str(i),
3461                    pfx+'BkPksig;'+str(i),pfx+'BkPkgam;'+str(i)]
3462                for j,name in enumerate(names):
3463                    DebyePeaks['peaksList'][i][2*j] = parmDict[name]
3464                    if name in sigDict:
3465                        backSig[lenBack+3*DebyePeaks['nDebye']+4*i+j] = sigDict[name]
3466        if pfx+'BF mult' in sigDict:
3467            DebyePeaks['background PWDR'][1] = parmDict[pfx+'BF mult']
3468            backSig.append(sigDict[pfx+'BF mult'])
3469               
3470        return backSig
3471       
3472    def SetInstParms(pfx,Inst,parmDict,sigDict):
3473        instSig = {}
3474        insKeys = list(Inst.keys())
3475        insKeys.sort()
3476        for item in insKeys:
3477            insName = pfx+item
3478            Inst[item][1] = parmDict[insName]
3479            if insName in sigDict:
3480                instSig[item] = sigDict[insName]
3481            else:
3482                instSig[item] = 0
3483        return instSig
3484       
3485    def SetSampleParms(pfx,Sample,parmDict,sigDict):
3486        if 'Bragg' in Sample['Type']:             #Bragg-Brentano
3487            sampSig = [0 for i in range(5)]
3488            for i,item in enumerate(['Scale','Shift','Transparency','SurfRoughA','SurfRoughB']):
3489                Sample[item][0] = parmDict[pfx+item]
3490                if pfx+item in sigDict:
3491                    sampSig[i] = sigDict[pfx+item]
3492        elif 'Debye' in Sample['Type']:        #Debye-Scherrer
3493            sampSig = [0 for i in range(4)]
3494            for i,item in enumerate(['Scale','Absorption','DisplaceX','DisplaceY']):
3495                Sample[item][0] = parmDict[pfx+item]
3496                if pfx+item in sigDict:
3497                    sampSig[i] = sigDict[pfx+item]
3498        return sampSig
3499       
3500    def PrintBackgroundSig(Background,backSig):
3501        Back = Background[0]
3502        DebyePeaks = Background[1]
3503        valstr = ' value : '
3504        sigstr = ' sig   : '
3505        refine = False
3506        for i,back in enumerate(Back[3:]):
3507            valstr += '%10.4g'%(back)
3508            if Back[1]:
3509                refine = True
3510                sigstr += '%10.4g'%(backSig[i])
3511            else:
3512                sigstr += 10*' '
3513        if refine:
3514            pFile.write('\n Background function: %s\n'%Back[0])
3515            pFile.write(valstr+'\n')
3516            pFile.write(sigstr+'\n')
3517        if DebyePeaks['nDebye']:
3518            ifAny = False
3519            ptfmt = "%12.3f"
3520            names =  ' names :'
3521            ptstr =  ' values:'
3522            sigstr = ' esds  :'
3523            for item in sigDict:
3524                if 'Debye' in item:
3525                    ifAny = True
3526                    names += '%12s'%(item)
3527                    ptstr += ptfmt%(parmDict[item])
3528                    sigstr += ptfmt%(sigDict[item])
3529            if ifAny:
3530                pFile.write('\n Debye diffuse scattering coefficients\n')
3531                pFile.write(names+'\n')
3532                pFile.write(ptstr+'\n')
3533                pFile.write(sigstr+'\n')
3534        if DebyePeaks['nPeaks']:
3535            pFile.write('\n Single peak coefficients:\n')
3536            parms =    ['BkPkpos','BkPkint','BkPksig','BkPkgam']
3537            line = ' peak no. '
3538            for parm in parms:
3539                line += '%14s%12s'%(parm.center(14),'esd'.center(12))
3540            pFile.write(line+'\n')
3541            for ip in range(DebyePeaks['nPeaks']):
3542                ptstr = ' %4d '%(ip)
3543                for parm in parms:
3544                    fmt = '%14.3f'
3545                    efmt = '%12.3f'
3546                    if parm == 'BkPkpos':
3547                        fmt = '%14.4f'
3548                        efmt = '%12.4f'
3549                    name = pfx+parm+';%d'%(ip)
3550                    ptstr += fmt%(parmDict[name])
3551                    if name in sigDict:
3552                        ptstr += efmt%(sigDict[name])
3553                    else:
3554                        ptstr += 12*' '
3555                pFile.write(ptstr+'\n')
3556        if 'background PWDR' in DebyePeaks and DebyePeaks['background PWDR'][2]:
3557            pFile.write(' Fixed background scale: %.3f(%d)\n'%(DebyePeaks['background PWDR'][1],int(1000*backSig[-1])))
3558        sumBk = np.array(Histogram['sumBk'])
3559        pFile.write(' Background sums: empirical %.3g, Debye %.3g, peaks %.3g, Total %.3g\n'%
3560            (sumBk[0],sumBk[1],sumBk[2],np.sum(sumBk)))
3561       
3562    def PrintInstParmsSig(Inst,instSig):
3563        refine = False
3564        insKeys = list(instSig.keys())
3565        insKeys.sort()
3566        iBeg = 0
3567        Ok = True
3568        while Ok:
3569            ptlbls = ' names :'
3570            ptstr =  ' value :'
3571            sigstr = ' sig   :'
3572            iFin = min(iBeg+9,len(insKeys))
3573            for name in insKeys[iBeg:iFin]:
3574                if name not in  ['Type','Lam1','Lam2','Azimuth','Source','fltPath']:
3575                    ptlbls += '%12s' % (name)
3576                    ptstr += '%12.6f' % (Inst[name][1])
3577                    if instSig[name]:
3578                        refine = True
3579                        sigstr += '%12.6f' % (instSig[name])
3580                    else:
3581                        sigstr += 12*' '
3582            if refine:
3583                pFile.write('\n Instrument Parameters:\n')
3584                pFile.write(ptlbls+'\n')
3585                pFile.write(ptstr+'\n')
3586                pFile.write(sigstr+'\n')
3587            iBeg = iFin
3588            if iBeg == len(insKeys):
3589                Ok = False
3590       
3591    def PrintSampleParmsSig(Sample,sampleSig):
3592        ptlbls = ' names :'
3593        ptstr =  ' values:'
3594        sigstr = ' sig   :'
3595        refine = False
3596        if 'Bragg' in Sample['Type']:
3597            for i,item in enumerate(['Scale','Shift','Transparency','SurfRoughA','SurfRoughB']):
3598                ptlbls += '%14s'%(item)
3599                ptstr += '%14.4f'%(Sample[item][0])
3600                if sampleSig[i]:
3601                    refine = True
3602                    sigstr += '%14.4f'%(sampleSig[i])
3603                else:
3604                    sigstr += 14*' '
3605           
3606        elif 'Debye' in Sample['Type']:        #Debye-Scherrer
3607            for i,item in enumerate(['Scale','Absorption','DisplaceX','DisplaceY']):
3608                ptlbls += '%14s'%(item)
3609                ptstr += '%14.4f'%(Sample[item][0])
3610                if sampleSig[i]:
3611                    refine = True
3612                    sigstr += '%14.4f'%(sampleSig[i])
3613                else:
3614                    sigstr += 14*' '
3615
3616        if refine:
3617            pFile.write('\n Sample Parameters:\n')
3618            pFile.write(ptlbls+'\n')
3619            pFile.write(ptstr+'\n')
3620            pFile.write(sigstr+'\n')
3621       
3622    histoList = list(Histograms.keys())
3623    histoList.sort()
3624    for histogram in histoList:
3625        if 'PWDR' in histogram:
3626            Histogram = Histograms[histogram]
3627            hId = Histogram['hId']
3628            pfx = ':'+str(hId)+':'
3629            Background = Histogram['Background']
3630            backSig = SetBackgroundParms(pfx,Background,parmDict,sigDict)
3631           
3632            Inst = Histogram['Instrument Parameters'][0]
3633            instSig = SetInstParms(pfx,Inst,parmDict,sigDict)
3634       
3635            Sample = Histogram['Sample Parameters']
3636            sampSig = SetSampleParms(pfx,Sample,parmDict,sigDict)
3637
3638            if Print and not seq:
3639                pFile.write('\n Histogram: %s histogram Id: %d\n'%(histogram,hId))
3640                pFile.write(135*'='+'\n')
3641            if Print:
3642                pFile.write(' PWDR histogram weight factor = '+'%.3f\n'%(Histogram['wtFactor']))
3643                pFile.write(' Final refinement wR = %.2f%% on %d observations in this histogram\n'%
3644                (Histogram['Residuals']['wR'],Histogram['Residuals']['Nobs']))
3645                pFile.write(' Other residuals: R = %.2f%%, R-bkg = %.2f%%, wR-bkg = %.2f%% wRmin = %.2f%%\n'%
3646                (Histogram['Residuals']['R'],Histogram['Residuals']['Rb'],Histogram['Residuals']['wR'],Histogram['Residuals']['wRmin']))
3647                pFile.write(' Instrument type: %s\n'%Sample['Type'])
3648                if calcControls != None:    #skipped in seqRefine
3649                    if 'X' in Inst['Type'][0]:
3650                        PrintFprime(calcControls['FFtables'],pfx,pFile)
3651                    elif 'NC' in Inst['Type'][0]:
3652                        PrintBlength(calcControls['BLtables'],Inst['Lam'][1],pFile)
3653                PrintSampleParmsSig(Sample,sampSig)
3654                PrintInstParmsSig(Inst,instSig)
3655                PrintBackgroundSig(Background,backSig)
3656               
3657def WriteRBObjPOAndSig(pfx,rbfx,rbsx,parmDict,sigDict):
3658    '''Cribbed version of PrintRBObjPOAndSig but returns lists of strings.
3659    Moved so it can be used in ExportCIF
3660    '''
3661    namstr = '  names :'
3662    valstr = '  values:'
3663    sigstr = '  esds  :'
3664    for i,px in enumerate(['Px:','Py:','Pz:']):
3665        name = pfx+rbfx+px+rbsx
3666        namstr += '%12s'%('Pos '+px[1])
3667        valstr += '%12.5f'%(parmDict[name])
3668        if name in sigDict:
3669            sigstr += '%12.5f'%(sigDict[name])
3670        else:
3671            sigstr += 12*' '
3672    for i,po in enumerate(['Oa:','Oi:','Oj:','Ok:']):
3673        name = pfx+rbfx+po+rbsx
3674        namstr += '%12s'%('Ori '+po[1])
3675        valstr += '%12.5f'%(parmDict[name])
3676        if name in sigDict:
3677            sigstr += '%12.5f'%(sigDict[name])
3678        else:
3679            sigstr += 12*' '
3680    name = pfx+rbfx+'f:'+rbsx
3681    namstr += '%12s'%('Frac')
3682    valstr += '%12.5f'%(parmDict[name])
3683    if name in sigDict:
3684        sigstr += '%12.5f'%(sigDict[name])
3685    else:
3686        sigstr += 12*' '
3687    return (namstr,valstr,sigstr)
3688
3689def WriteRBObjTLSAndSig(pfx,rbfx,rbsx,TLS,parmDict,sigDict):
3690    '''Cribbed version of PrintRBObjTLSAndSig but returns lists of strings.
3691    Moved so it can be used in ExportCIF
3692    '''
3693    out = []
3694    namstr = '  names :'
3695    valstr = '  values:'
3696    sigstr = '  esds  :'
3697    if 'N' not in TLS:
3698        out.append(' Thermal motion:\n')
3699    if 'T' in TLS:
3700        for i,pt in enumerate(['T11:','T22:','T33:','T12:','T13:','T23:']):
3701            name = pfx+rbfx+pt+rbsx
3702            namstr += '%12s'%(pt[:3])
3703            valstr += '%12.5f'%(parmDict[name])
3704            if name in sigDict:
3705                sigstr += '%12.5f'%(sigDict[name])
3706            else:
3707                sigstr += 12*' '
3708        out.append(namstr+'\n')
3709        out.append(valstr+'\n')
3710        out.append(sigstr+'\n')
3711    if 'L' in TLS:
3712        namstr = '  names :'
3713        valstr = '  values:'
3714        sigstr = '  esds  :'
3715        for i,pt in enumerate(['L11:','L22:','L33:','L12:','L13:','L23:']):
3716            name = pfx+rbfx+pt+rbsx
3717            namstr += '%12s'%(pt[:3])
3718            valstr += '%12.3f'%(parmDict[name])
3719            if name in sigDict:
3720                sigstr += '%12.3f'%(sigDict[name])
3721            else:
3722                sigstr += 12*' '
3723        out.append(namstr+'\n')
3724        out.append(valstr+'\n')
3725        out.append(sigstr+'\n')
3726    if 'S' in TLS:
3727        namstr = '  names :'
3728        valstr = '  values:'
3729        sigstr = '  esds  :'
3730        for i,pt in enumerate(['S12:','S13:','S21:','S23:','S31:','S32:','SAA:','SBB:']):
3731            name = pfx+rbfx+pt+rbsx
3732            namstr += '%12s'%(pt[:3])
3733            valstr += '%12.4f'%(parmDict[name])
3734            if name in sigDict:
3735                sigstr += '%12.4f'%(sigDict[name])
3736            else:
3737                sigstr += 12*' '
3738        out.append(namstr+'\n')
3739        out.append(valstr+'\n')
3740        out.append(sigstr+'\n')
3741    if 'U' in TLS:
3742        name = pfx+rbfx+'U:'+rbsx
3743        namstr = '  names :'+'%12s'%('Uiso')
3744        valstr = '  values:'+'%12.5f'%(parmDict[name])
3745        if name in sigDict:
3746            sigstr = '  esds  :'+'%12.5f'%(sigDict[name])
3747        else:
3748            sigstr = '  esds  :'+12*' '
3749        out.append(namstr+'\n')
3750        out.append(valstr+'\n')
3751        out.append(sigstr+'\n')
3752    return out
3753
3754def WriteRBObjTorAndSig(pfx,rbsx,parmDict,sigDict,nTors):
3755    '''Cribbed version of PrintRBObjTorAndSig but returns lists of strings.
3756    Moved so it can be used in ExportCIF
3757    '''
3758    out = []
3759    namstr = '  names :'
3760    valstr = '  values:'
3761    sigstr = '  esds  :'
3762    out.append(' Torsions:\n')
3763    for it in range(nTors):
3764        name = pfx+'RBRTr;'+str(it)+':'+rbsx
3765        namstr += '%12s'%('Tor'+str(it))
3766        valstr += '%12.4f'%(parmDict[name])
3767        if name in sigDict:
3768            sigstr += '%12.4f'%(sigDict[name])
3769    out.append(namstr+'\n')
3770    out.append(valstr+'\n')
3771    out.append(sigstr+'\n')
3772    return out
3773
3774def WriteResRBModel(RBModel):
3775    '''Write description of a residue rigid body. Code shifted from
3776    PrintResRBModel to make usable from G2export_CIF
3777    '''
3778    out = []
3779    atNames = RBModel['atNames']
3780    rbRef = RBModel['rbRef']
3781    rbSeq = RBModel['rbSeq']
3782    out.append('    At name       x          y          z\n')
3783    for name,xyz in zip(atNames,RBModel['rbXYZ']):
3784        out.append(%8s %10.4f %10.4f %10.4f\n'%(name,xyz[0],xyz[1],xyz[2]))
3785    out.append('  Orientation defined by: %s -> %s & %s -> %s\n'%
3786        (atNames[rbRef[0]],atNames[rbRef[1]],atNames[rbRef[0]],atNames[rbRef[2]]))
3787    if rbSeq:
3788        for i,rbseq in enumerate(rbSeq):
3789#                nameLst = [atNames[i] for i in rbseq[3]]
3790            out.append('  Torsion sequence %d Bond: %s - %s riding: %s\n'%
3791                    (i,atNames[rbseq[0]],atNames[rbseq[1]],str([atNames[i] for i in rbseq[3]])))
3792    return out
3793
3794def WriteVecRBModel(RBModel,sigDict={},irb=None):
3795    '''Write description of a vector rigid body. Code shifted from
3796    PrintVecRBModel to make usable from G2export_CIF
3797    '''
3798    out = []
3799    rbRef = RBModel['rbRef']
3800    atTypes = RBModel['rbTypes']
3801    for i in range(len(RBModel['VectMag'])):
3802        if sigDict and irb is not None:
3803            name = '::RBV;'+str(i)+':'+str(irb)
3804            s = G2mth.ValEsd(RBModel['VectMag'][i],sigDict.get(name,-.0001))
3805            out.append('Vector no.: %d Magnitude: %s\n'%(i,s))
3806        else:
3807            out.append('Vector no.: %d Magnitude: %8.4f Refine? %s\n'%
3808                           (i,RBModel['VectMag'][i],RBModel['VectRef'][i]))
3809        out.append('  No. Type     vx         vy         vz\n')
3810        for j,[name,xyz] in enumerate(zip(atTypes,RBModel['rbVect'][i])):
3811            out.append(%d   %2s %10.4f %10.4f %10.4f\n'%(j,name,xyz[0],xyz[1],xyz[2]))
3812    out.append('  No. Type      x          y          z\n')
3813    for i,[name,xyz] in enumerate(zip(atTypes,RBModel['rbXYZ'])):
3814        out.append(%d   %2s %10.4f %10.4f %10.4f\n'%(i,name,xyz[0],xyz[1],xyz[2]))
3815    return out
Note: See TracBrowser for help on using the repository browser.