source: trunk/exports/G2export_CIF.py @ 5232

Last change on this file since 5232 was 5232, checked in by toby, 15 months ago

correct weight fraction calc and replace uses with lookup

  • Property svn:eol-style set to native
  • Property svn:keywords set to Date Author Revision URL Id
File size: 248.3 KB
Line 
1#!/usr/bin/env python
2# -*- coding: utf-8 -*-
3########### SVN repository information ###################
4# $Date: 2022-03-17 00:21:28 +0000 (Thu, 17 Mar 2022) $
5# $Author: toby $
6# $Revision: 5232 $
7# $URL: trunk/exports/G2export_CIF.py $
8# $Id: G2export_CIF.py 5232 2022-03-17 00:21:28Z toby $
9########### SVN repository information ###################
10'''
11*Module G2export_CIF: CIF Exports*
12------------------------------------------------------
13
14This implements a complex exporter :class:`ExportCIF` that can implement an
15entire project in a complete CIF intended for submission as a
16publication. In addition, there are three subclasses of :class:`ExportCIF`:
17:class:`ExportProjectCIF`,
18:class:`ExportPhaseCIF` and :class:`ExportDataCIF` where extra parameters
19for the _Exporter() determine if a project, single phase or data set are written.
20'''
21
22from __future__ import division, print_function
23import platform
24import datetime as dt
25import os.path
26import sys
27import numpy as np
28if '2' in platform.python_version_tuple()[0]:
29    import cPickle as pickle
30else:
31    import pickle
32import copy
33import re
34try:
35    import wx
36    import wx.lib.scrolledpanel as wxscroll
37    import wx.lib.resizewidget as rw
38except ImportError:
39    # Avoid wx dependency for CLI
40    class Placeholder(object):
41        def __init__(self):
42            self.BoxSizer = object
43            self.Button = object
44            self.Dialog = object
45            self.ScrolledPanel = object
46    wx = Placeholder()
47    wxscroll = Placeholder()
48import GSASIIpath
49GSASIIpath.SetVersionNumber("$Revision: 5232 $")
50import GSASIIIO as G2IO
51try:
52    import GSASIIctrlGUI as G2G
53except ImportError:
54    pass
55import GSASIIobj as G2obj
56import GSASIImath as G2mth
57import GSASIIspc as G2spc
58import GSASIIlattice as G2lat
59import GSASIIstrMain as G2stMn
60import GSASIIstrIO as G2stIO       
61import GSASIImapvars as G2mv
62import GSASIIElem as G2el
63import GSASIIpy3 as G2py3
64
65DEBUG = False    #True to skip printing of reflection/powder profile lists
66
67CIFdic = None
68
69cellNames = ['length_a','length_b','length_c',
70             'angle_alpha','angle_beta ','angle_gamma',
71             'volume']
72def striphist(var,insChar=''):
73    'strip a histogram number from a var name'
74    sv = var.split(':')
75    if len(sv) <= 1: return var
76    if sv[1]:
77        sv[1] = insChar
78    return ':'.join(sv)
79
80def getCellwStrain(phasedict,seqData,pId,histname):
81    'Get cell parameters and their errors for a sequential fit'
82    #newCellDict = {}
83    #if name in seqData and 'newCellDict' in seqData[histname]:
84    #    newCellDict.update(seqData[histname]['newCellDict'])
85   
86    pfx = str(pId)+'::' # prefix for A values from phase
87    Albls = [pfx+'A'+str(i) for i in range(6)]
88    Avals = G2lat.cell2A(phasedict['General']['Cell'][1:7])
89    #AiLookup = {}
90    DijLookup = {}
91    zeroDict = dict(zip(Avals,6*[0.,]))
92    for i,v in enumerate(('D11','D22','D33','D12','D13','D23')):
93        if pfx+v in seqData[histname]['newCellDict']:
94            Avals[i] = seqData[histname]['newCellDict'][pfx+v][1]
95            #AiLookup[seqData[histname]['newCellDict'][pfx+v][0]] = pfx+v
96            DijLookup[pfx+v] = seqData[histname]['newCellDict'][pfx+v][0]
97    covData = {  # relabeled with p:h:Dij as p::Ai
98        'varyList': [DijLookup.get(striphist(v),v) for v in seqData[histname]['varyList']], 
99        'covMatrix': seqData[histname]['covMatrix']}
100    # apply symmetry
101    cellDict = dict(zip(Albls,Avals))
102    try:    # convert to direct cell
103        A,zeros = G2stIO.cellFill(pfx,phasedict['General']['SGData'],cellDict,zeroDict)
104        cell = list(G2lat.A2cell(A)) + [G2lat.calc_V(A)]
105        cE = G2stIO.getCellEsd(pfx,phasedict['General']['SGData'],A,covData,unique=True)
106    except:
107        cell = 7*[None]
108        cE = 7*[None]
109    return cell,cE
110
111def mkSeqResTable(mode,seqHistList,seqData,Phases,Histograms,Controls):
112    '''Setup sequential results table (based on code from
113    GSASIIseqGUI.UpdateSeqResults)
114
115    TODO: This should be merged with the table build code in
116    GSASIIseqGUI.UpdateSeqResults and moved to somewhere non-GUI
117    like GSASIIstrIO to create a single routine that can be used
118    in both places, but this means returning some
119    of the code that has been removed from there
120    '''
121
122    newAtomDict = seqData[seqHistList[0]].get('newAtomDict',{}) # dict with atom positions; relative & absolute
123    atomLookup = {newAtomDict[item][0]:item for item in newAtomDict if item in seqData['varyList']}
124    phaseLookup = {Phases[phase]['pId']:phase for phase in Phases}
125
126    # make dict of varied cell parameters equivalents
127    ESDlookup = {} # provides the Dij term for each Ak term (where terms are refined)
128    Dlookup = {} # provides the Ak term for each Dij term (where terms are refined)
129    newCellDict = {}
130    for name in seqHistList:
131        if name in seqData and 'newCellDict' in seqData[name]:
132            newCellDict.update(seqData[name]['newCellDict'])
133    cellAlist = []
134    for item in newCellDict:
135        cellAlist.append(newCellDict[item][0])
136        if item in seqData.get('varyList',[]):
137            ESDlookup[newCellDict[item][0]] = item
138            Dlookup[item] = newCellDict[item][0]
139    # add coordinate equivalents to lookup table
140    for parm in atomLookup:
141        Dlookup[atomLookup[parm]] = parm
142        ESDlookup[parm] = atomLookup[parm]
143
144    # get unit cell & symmetry for all phases & initial stuff for later use
145    RecpCellTerms = {}
146    SGdata = {}
147    uniqCellIndx = {}
148    #initialCell = {}
149    RcellLbls = {}
150    zeroDict = {}
151    for phase in Phases:
152        pId = Phases[phase]['pId']
153        pfx = str(pId)+'::' # prefix for A values from phase
154        RcellLbls[pId] = [pfx+'A'+str(i) for i in range(6)]
155        RecpCellTerms[pId] = G2lat.cell2A(Phases[phase]['General']['Cell'][1:7])
156        zeroDict[pId] = dict(zip(RcellLbls[pId],6*[0.,]))
157        SGdata[pId] = Phases[phase]['General']['SGData']
158        laue = SGdata[pId]['SGLaue']
159        if laue == '2/m':
160            laue += SGdata[pId]['SGUniq']
161        for symlist,celllist in G2lat.UniqueCellByLaue:
162            if laue in symlist:
163                uniqCellIndx[pId] = celllist
164                break
165        else: # should not happen
166            uniqCellIndx[pId] = list(range(6))
167           
168    # scan for locations where the variables change
169    VaryListChanges = [] # histograms where there is a change
170    combinedVaryList = []
171    firstValueDict = {}
172    vallookup = {}
173    posdict = {}
174    prevVaryList = []
175    foundNames = []
176    missing = 0
177    for i,name in enumerate(seqHistList):
178        if name not in seqData:
179            if missing < 5:
180                print(" Warning: "+name+" not found")
181            elif missing == 5:
182                print (' Warning: more are missing')
183            missing += 1
184            continue
185        foundNames.append(name)
186        maxPWL = 5
187        for var,val,sig in zip(seqData[name]['varyList'],seqData[name]['variables'],seqData[name]['sig']):
188            svar = striphist(var,'*') # wild-carded
189            if 'PWL' in svar:
190                if int(svar.split(':')[-1]) > maxPWL:
191                    continue
192            if svar not in combinedVaryList:
193                # add variables to list as they appear
194                combinedVaryList.append(svar)
195                firstValueDict[svar] = (val,sig)
196        if prevVaryList != seqData[name]['varyList']: # this refinement has a different refinement list from previous
197            prevVaryList = seqData[name]['varyList']
198            vallookup[name] = dict(zip(seqData[name]['varyList'],seqData[name]['variables']))
199            posdict[name] = {}
200            for var in seqData[name]['varyList']:
201                svar = striphist(var,'*')
202                if 'PWL' in svar:
203                    if int(svar.split(':')[-1]) > maxPWL:
204                        continue
205                posdict[name][combinedVaryList.index(svar)] = svar
206            VaryListChanges.append(name)
207    if missing:
208        print (' Warning: Total of %d data sets missing from sequential results'%(missing))
209
210    #### --- start building table
211    histNames = foundNames
212#            sampleParms = GetSampleParms()
213    nRows = len(histNames)
214    tblValues = [list(range(nRows))]   # table of values arranged by columns
215    tblSigs =   [None]                 # a list of sigma values, or None if not defined
216    tblLabels = ['Number']             # a label for the column
217    tblTypes = ['int']
218    # start with Rwp values
219    tblValues += [[seqData[name]['Rvals']['Rwp'] for name in histNames]]
220    tblSigs += [None]
221    tblLabels += ['Rwp']
222    tblTypes += ['10,3']
223    # add changing sample parameters to table
224    sampleParmDict = {'Temperature':[],'Pressure':[],'Time':[],
225                 'FreePrm1':[],'FreePrm2':[],'FreePrm3':[],'Omega':[],
226                 'Chi':[],'Phi':[],'Azimuth':[],}
227    for key in sampleParmDict:
228        for h in histNames:
229            var = ":" + str(Histograms[h]['hId']) + ":" + key
230            sampleParmDict[key].append(seqData[h]['parmDict'].get(var))
231        if not np.all(np.array(sampleParmDict[key]) == sampleParmDict[key][0]):
232            tblValues += [sampleParmDict[key]]
233            tblSigs.append(None)
234            if 'FreePrm' in key and key in Controls:
235                tblLabels.append(Controls[item])
236            else:
237                tblLabels.append(key)
238            tblTypes += ['float']
239
240    # add unique cell parameters 
241    if len(newCellDict):
242        for pId in sorted(RecpCellTerms):
243            pfx = str(pId)+'::' # prefix for A values from phase
244            cells = []
245            cellESDs = []
246            Albls = [pfx+'A'+str(i) for i in range(6)]
247            for name in histNames:
248                #if name not in Histograms: continue
249                hId = Histograms[name]['hId']
250                phfx = '%d:%d:'%(pId,hId)
251                esdLookUp = {}
252                dLookup = {}
253                for item in seqData[name]['newCellDict']:
254                    if phfx+item.split('::')[1] in seqData[name]['varyList']:
255                        esdLookUp[newCellDict[item][0]] = item
256                        dLookup[item] = newCellDict[item][0]
257                covData = {'varyList': [dLookup.get(striphist(v),v) for v in seqData[name]['varyList']],
258                    'covMatrix': seqData[name]['covMatrix']}
259                A = RecpCellTerms[pId][:] # make copy of starting A values
260                # update with refined values
261                for i,j in enumerate(('D11','D22','D33','D12','D13','D23')):
262                    var = str(pId)+'::A'+str(i)
263                    Dvar = str(pId)+':'+str(hId)+':'+j
264                    # apply Dij value if non-zero
265                    if Dvar in seqData[name]['parmDict']:
266                        parmDict = seqData[name]['parmDict']
267                        if parmDict[Dvar] != 0.0:
268                            A[i] += parmDict[Dvar]
269                    # override with fit result if is Dij varied
270                    if var in cellAlist:
271                        try:
272                            A[i] = seqData[name]['newCellDict'][esdLookUp[var]][1] # get refined value
273                        except KeyError:
274                            pass
275                # apply symmetry
276                cellDict = dict(zip(Albls,A))
277                try:    # convert to direct cell
278                    A,zeros = G2stIO.cellFill(pfx,SGdata[pId],cellDict,zeroDict[pId])
279                    c = G2lat.A2cell(A)
280                    vol = G2lat.calc_V(A)
281                    cE = G2stIO.getCellEsd(pfx,SGdata[pId],A,covData,unique=True)
282                except:
283                    c = 6*[None]
284                    cE = 6*[None]
285                    vol = None
286                # add only unique values to table
287                if name in Phases[phaseLookup[pId]]['Histograms']:
288                    cells += [[c[i] for i in uniqCellIndx[pId]]+[vol]]
289                    cellESDs += [[cE[i] for i in uniqCellIndx[pId]]+[cE[-1]]]
290                else:
291                    cells += [[None for i in uniqCellIndx[pId]]+[None]]
292                    cellESDs += [[None for i in uniqCellIndx[pId]]+[None]]
293            p = phaseLookup[pId]
294            tblLabels += ['{}, {}'.format(G2lat.cellAlbl[i],p) for i in uniqCellIndx[pId]]
295            tblTypes += ['10,5' if i <3 else '10,3' for i in uniqCellIndx[pId]]
296            tblLabels.append('{}, {}'.format('Volume',p))
297            tblTypes += ['10,3']
298            tblValues += zip(*cells)
299            tblSigs += zip(*cellESDs)
300
301    # sort out the variables in their selected order
302    varcols = 0
303    varlbls = []
304    for d in posdict.values():
305        varcols = max(varcols,max(d.keys())+1)
306    # get labels for each column
307    for i in range(varcols):
308        lbl = ''
309        for h in VaryListChanges:
310            if posdict[h].get(i):
311                if posdict[h].get(i) in lbl: continue
312                if lbl != "": lbl += '/'
313                lbl += posdict[h].get(i)
314        varlbls.append(lbl)
315    vals = []
316    esds = []
317    varsellist = None        # will be a list of variable names in the order they are selected to appear
318    # tabulate values for each hist, leaving None for blank columns
319    for name in histNames:
320        if name in posdict:
321            varsellist = [posdict[name].get(i) for i in range(varcols)]
322            # translate variable names to how they will be used in the headings
323            vs = [striphist(v,'*') for v in seqData[name]['varyList']]
324            # determine the index for each column (or None) in the seqData[]['variables'] and ['sig'] lists
325            sellist = [vs.index(v) if v is not None else None for v in varsellist]
326            #sellist = [i if striphist(v,'*') in varsellist else None for i,v in enumerate(seqData[name]['varyList'])]
327        if not varsellist: raise Exception()
328        vals.append([seqData[name]['variables'][s] if s is not None else None for s in sellist])
329        esds.append([seqData[name]['sig'][s] if s is not None else None for s in sellist])
330    tblValues += zip(*vals)
331    tblSigs += zip(*esds)
332    tblLabels += varlbls
333    tblTypes += ['float' for i in varlbls]
334   
335    # tabulate constrained variables, removing histogram numbers if needed
336    # from parameter label
337    depValDict = {}
338    depSigDict = {}
339    for name in histNames:
340        for var in seqData[name].get('depParmDict',{}):
341            val,sig = seqData[name]['depParmDict'][var]
342            svar = striphist(var,'*')
343            if svar not in depValDict:
344               depValDict[svar] = [val]
345               depSigDict[svar] = [sig]
346            else:
347               depValDict[svar].append(val)
348               depSigDict[svar].append(sig)
349
350    # add the dependent constrained variables to the table
351    for var in sorted(depValDict):
352        if len(depValDict[var]) != len(histNames): continue
353        tblLabels.append(var)
354        tblTypes.append('10,5')
355        tblSigs += [depSigDict[var]]
356        tblValues += [depValDict[var]]
357
358    # add refined atom parameters to table
359    for parm in sorted(atomLookup):
360        tblLabels.append(parm)
361        tblTypes.append('10,5')
362        tblValues += [[seqData[name]['newAtomDict'][atomLookup[parm]][1] for name in histNames]]
363        if atomLookup[parm] in seqData[histNames[0]]['varyList']:
364            col = seqData[histNames[0]]['varyList'].index(atomLookup[parm])
365            tblSigs += [[seqData[name]['sig'][col] for name in histNames]]
366        else:
367            tblSigs += [None]
368
369    # compute and add weight fractions to table if varied
370    for phase in Phases:
371        pId = Phases[phase]['pId']
372        var = str(pId)+':*:Scale'
373        if var not in combinedVaryList+list(depValDict.keys()): continue   
374        wtFrList = []
375        sigwtFrList = []
376        for i,name in enumerate(histNames):
377            skip = False
378            if name not in Phases[phase]['Histograms']:
379                skip = True
380            elif not Phases[phase]['Histograms'][name]['Use']:
381                skip = True
382            hId = Histograms[name]['hId']
383            var = str(pId)+':'+str(hId)+':WgtFrac'
384            if var not in seqData[name]['depParmDict']: skip = True
385            if skip:
386                wtFrList.append(None)
387                sigwtFrList.append(0.0)
388                continue
389            wtFr,sig = seqData[name]['depParmDict'][var]
390            wtFrList.append(wtFr)
391            sigwtFrList.append(sig)
392        p = phaseLookup[Phases[phase]['pId']]
393        tblLabels.append(p + ' Wgt Frac')
394        tblTypes.append('10,4')
395        tblValues += [wtFrList]
396        tblSigs += [sigwtFrList]
397    return tblLabels,tblValues,tblSigs,tblTypes
398
399
400# Refactored over here to allow access by GSASIIscriptable.py
401def WriteCIFitem(fp, name, value=''):
402    '''Helper function for writing CIF output.'''
403    # Ignores unicode issues
404    if value:
405        if "\n" in value or (len(value) > 70 and ' ' in value.strip()):
406            if name.strip():
407                fp.write(name+'\n')
408            fp.write(';\n'+value+'\n')
409            fp.write(';'+'\n')
410        elif " " in value:
411            if len(name)+len(value) > 65:
412                fp.write(name + '\n   ' + '"' + str(value) + '"'+'\n')
413            else:
414                fp.write(name + '  ' + '"' + str(value) + '"'+'\n')
415        else:
416            if len(name)+len(value) > 65:
417                fp.write(name+'\n   ' + value+'\n')
418            else:
419                fp.write(name+'  ' + value+'\n')
420    else:
421        fp.write(name+'\n')
422
423def RBheader(fp):
424    WriteCIFitem(fp,'\n# RIGID BODY DETAILS')
425    WriteCIFitem(fp,'loop_\n    _restr_rigid_body_class.class_id\n    _restr_rigid_body_class.details')
426
427# Refactored over here to allow access by GSASIIscriptable.py
428def WriteAtomsNuclear(fp, phasedict, phasenam, parmDict, sigDict, labellist,
429                          RBparms={}):
430    'Write atom positions to CIF'
431    # phasedict = self.Phases[phasenam] # pointer to current phase info
432    General = phasedict['General']
433    cx,ct,cs,cia = General['AtomPtrs']
434    GS = G2lat.cell2GS(General['Cell'][1:7])
435    Amat = G2lat.cell2AB(General['Cell'][1:7])[0]
436    Atoms = phasedict['Atoms']
437    cfrac = cx+3
438    fpfx = str(phasedict['pId'])+'::Afrac:'
439    for i,at in enumerate(Atoms):
440        fval = parmDict.get(fpfx+str(i),at[cfrac])
441        if fval != 0.0:
442            break
443    else:
444        WriteCIFitem(fp, '\n# PHASE HAS NO ATOMS!')
445        return
446
447    WriteCIFitem(fp, '\n# ATOMIC COORDINATES AND DISPLACEMENT PARAMETERS')
448    WriteCIFitem(fp, 'loop_ '+
449                 '\n   _atom_site_label'+
450                 '\n   _atom_site_type_symbol'+
451                 '\n   _atom_site_fract_x'+
452                 '\n   _atom_site_fract_y'+
453                 '\n   _atom_site_fract_z'+
454                 '\n   _atom_site_occupancy'+
455                 '\n   _atom_site_adp_type'+
456                 '\n   _atom_site_U_iso_or_equiv'+
457                 '\n   _atom_site_site_symmetry_multiplicity')
458
459    varnames = {cx:'Ax',cx+1:'Ay',cx+2:'Az',cx+3:'Afrac',
460                cia+1:'AUiso',cia+2:'AU11',cia+3:'AU22',cia+4:'AU33',
461                cia+5:'AU12',cia+6:'AU13',cia+7:'AU23'}
462    # Empty the labellist
463    while labellist:
464        labellist.pop()
465
466    pfx = str(phasedict['pId'])+'::'
467    # loop over all atoms
468    naniso = 0
469    for i,at in enumerate(Atoms):
470        if phasedict['General']['Type'] == 'macromolecular':
471            label = '%s_%s_%s_%s'%(at[ct-1],at[ct-3],at[ct-4],at[ct-2])
472            s = PutInCol(MakeUniqueLabel(label,labellist),15) # label
473        else:
474            s = PutInCol(MakeUniqueLabel(at[ct-1],labellist),6) # label
475        fval = parmDict.get(fpfx+str(i),at[cfrac])
476        if fval == 0.0: continue # ignore any atoms that have a occupancy set to 0 (exact)
477        s += PutInCol(FmtAtomType(at[ct]),4) # type
478        if at[cia] == 'I':
479            adp = 'Uiso '
480        else:
481            adp = 'Uani '
482            naniso += 1
483            t = G2lat.Uij2Ueqv(at[cia+2:cia+8],GS,Amat)[0]
484            for j in (2,3,4):
485                var = pfx+varnames[cia+j]+":"+str(i)
486        for j in (cx,cx+1,cx+2,cx+3,cia,cia+1):
487            if j in (cx,cx+1,cx+2):
488                dig = 11
489                sigdig = -0.00009
490            else:
491                dig = 10
492                sigdig = -0.0009
493            if j == cia:
494                s += adp
495            else:
496                var = pfx+varnames[j]+":"+str(i)
497                dvar = pfx+"d"+varnames[j]+":"+str(i)
498                if dvar not in sigDict:
499                    dvar = var
500                if j == cia+1 and adp == 'Uani ':
501                    sig = sigdig
502                    val = t
503                else:
504                    #print var,(var in parmDict),(var in sigDict)
505                    val = parmDict.get(var,at[j])
506                    sig = sigDict.get(dvar,sigdig)
507                    #if dvar in G2mv.GetDependentVars(): # do not include an esd for dependent vars
508                    #    sig = -abs(sig)
509                s += PutInCol(G2mth.ValEsd(val,sig),dig)
510        s += PutInCol(at[cs+1],3)
511        WriteCIFitem(fp, s)
512    if naniso != 0: 
513        # now loop over aniso atoms
514        WriteCIFitem(fp, '\nloop_' + '\n   _atom_site_aniso_label' +
515                     '\n   _atom_site_aniso_U_11' + '\n   _atom_site_aniso_U_22' +
516                     '\n   _atom_site_aniso_U_33' + '\n   _atom_site_aniso_U_12' +
517                     '\n   _atom_site_aniso_U_13' + '\n   _atom_site_aniso_U_23')
518        for i,at in enumerate(Atoms):
519            fval = parmDict.get(fpfx+str(i),at[cfrac])
520            if fval == 0.0: continue # ignore any atoms that have a occupancy set to 0 (exact)
521            if at[cia] == 'I': continue
522            s = PutInCol(labellist[i],6) # label
523            for j in (2,3,4,5,6,7):
524                sigdig = -0.0009
525                var = pfx+varnames[cia+j]+":"+str(i)
526                val = parmDict.get(var,at[cia+j])
527                sig = sigDict.get(var,sigdig)
528                s += PutInCol(G2mth.ValEsd(val,sig),11)
529            WriteCIFitem(fp, s)
530    # save information about rigid bodies
531    header = False
532    num = 0
533    rbAtoms = []
534    for irb,RBObj in enumerate(phasedict['RBModels'].get('Residue',[])):
535        if not header:
536            header = True
537            RBheader(fp)
538        jrb = RBparms['RBIds']['Residue'].index(RBObj['RBId'])
539        rbsx = str(irb)+':'+str(jrb)
540        num += 1
541        WriteCIFitem(fp,'',str(num))
542        RBModel = RBparms['Residue'][RBObj['RBId']]
543        SGData = phasedict['General']['SGData']
544        Sytsym,Mult = G2spc.SytSym(RBObj['Orig'][0],SGData)[:2]
545        s = '''GSAS-II residue rigid body "{}" with {} atoms
546  Site symmetry @ origin: {}, multiplicity: {}
547'''.format(RBObj['RBname'],len(RBModel['rbTypes']),Sytsym,Mult)
548        for i in G2stIO.WriteResRBModel(RBModel):
549            s += i
550        s += '\n Location:\n'
551        for i in G2stIO.WriteRBObjPOAndSig(pfx,'RBR',rbsx,parmDict,sigDict):
552            s += i+'\n'
553        for i in G2stIO.WriteRBObjTLSAndSig(pfx,'RBR',rbsx,
554                        RBObj['ThermalMotion'][0],parmDict,sigDict):
555            s += i
556        nTors = len(RBObj['Torsions'])
557        if nTors:
558            for i in G2stIO.WriteRBObjTorAndSig(pfx,rbsx,parmDict,sigDict,
559                        nTors):
560                s += i
561        WriteCIFitem(fp,'',s.rstrip())
562       
563        pId = phasedict['pId']
564        for i in RBObj['Ids']:
565            lbl = G2obj.LookupAtomLabel(pId,G2obj.LookupAtomId(pId,i))[0]
566            rbAtoms.append('{:7s} 1_555 {:3d} ?'.format(lbl,num))
567        #GSASIIpath.IPyBreak()
568
569    for irb,RBObj in enumerate(phasedict['RBModels'].get('Vector',[])):
570        if not header:
571            header = True
572            RBheader(fp)
573        jrb = RBparms['RBIds']['Vector'].index(RBObj['RBId'])
574        rbsx = str(irb)+':'+str(jrb)
575        num += 1
576        WriteCIFitem(fp,'',str(num))
577        RBModel = RBparms['Vector'][RBObj['RBId']]
578        SGData = phasedict['General']['SGData']
579        Sytsym,Mult = G2spc.SytSym(RBObj['Orig'][0],SGData)[:2]
580        s = '''GSAS-II vector rigid body "{}" with {} atoms
581  Site symmetry @ origin: {}, multiplicity: {}
582'''.format(RBObj['RBname'],len(RBModel['rbTypes']),Sytsym,Mult)
583        for i in G2stIO.WriteVecRBModel(RBModel,sigDict,irb):
584            s += i
585        s += '\n Location:\n'
586        for i in G2stIO.WriteRBObjPOAndSig(pfx,'RBV',rbsx,parmDict,sigDict):
587            s += i+'\n'
588        for i in G2stIO.WriteRBObjTLSAndSig(pfx,'RBV',rbsx,
589                        RBObj['ThermalMotion'][0],parmDict,sigDict):
590            s += i
591        WriteCIFitem(fp,'',s.rstrip())
592       
593        pId = phasedict['pId']
594        for i in RBObj['Ids']:
595            lbl = G2obj.LookupAtomLabel(pId,G2obj.LookupAtomId(pId,i))[0]
596            rbAtoms.append('{:7s} 1_555 {:3d} ?'.format(lbl,num))
597
598    if rbAtoms:
599        WriteCIFitem(fp,'loop_\n    _restr_rigid_body.id'+
600            '\n    _restr_rigid_body.atom_site_label\n    _restr_rigid_body.site_symmetry'+
601            '\n    _restr_rigid_body.class_id\n    _restr_rigid_body.details')
602        for i,l in enumerate(rbAtoms):
603            WriteCIFitem(fp,'   {:5d} {}'.format(i+1,l))
604           
605def WriteAtomsMagnetic(fp, phasedict, phasenam, parmDict, sigDict, labellist):
606    'Write atom positions to CIF'
607    # phasedict = self.Phases[phasenam] # pointer to current phase info
608    General = phasedict['General']
609    cx,ct,cs,cia = General['AtomPtrs']
610    Atoms = phasedict['Atoms']
611    cfrac = cx+3
612    fpfx = str(phasedict['pId'])+'::Afrac:'
613    for i,at in enumerate(Atoms):
614        fval = parmDict.get(fpfx+str(i),at[cfrac])
615        if fval != 0.0:
616            break
617    else:
618        WriteCIFitem(fp, '\n# PHASE HAS NO ATOMS!')
619        return
620
621    WriteCIFitem(fp, '\n# ATOMIC COORDINATES AND DISPLACEMENT PARAMETERS')
622    WriteCIFitem(fp, 'loop_ '+
623                 '\n   _atom_site_label'+
624                 '\n   _atom_site_type_symbol'+
625                 '\n   _atom_site_fract_x'+
626                 '\n   _atom_site_fract_y'+
627                 '\n   _atom_site_fract_z'+
628                 '\n   _atom_site_occupancy'+
629                 '\n   _atom_site_adp_type'+
630                 '\n   _atom_site_U_iso_or_equiv'+
631                 '\n   _atom_site_site_symmetry_multiplicity')
632
633    varnames = {cx:'Ax',cx+1:'Ay',cx+2:'Az',cx+3:'Afrac',
634                cx+4:'AMx',cx+5:'AMy',cx+6:'AMz',
635                cia+1:'AUiso',cia+2:'AU11',cia+3:'AU22',cia+4:'AU33',
636                cia+5:'AU12',cia+6:'AU13',cia+7:'AU23'}
637    # Empty the labellist
638    while labellist:
639        labellist.pop()
640
641    pfx = str(phasedict['pId'])+'::'
642    # loop over all atoms
643    naniso = 0
644    for i,at in enumerate(Atoms):
645        if phasedict['General']['Type'] == 'macromolecular':
646            label = '%s_%s_%s_%s'%(at[ct-1],at[ct-3],at[ct-4],at[ct-2])
647            s = PutInCol(MakeUniqueLabel(label,labellist),15) # label
648        else:
649            s = PutInCol(MakeUniqueLabel(at[ct-1],labellist),6) # label
650        fval = parmDict.get(fpfx+str(i),at[cfrac])
651        if fval == 0.0: continue # ignore any atoms that have a occupancy set to 0 (exact)
652        s += PutInCol(FmtAtomType(at[ct]),4) # type
653        if at[cia] == 'I':
654            adp = 'Uiso '
655        else:
656            adp = 'Uani '
657            naniso += 1
658            # compute Uequiv crudely
659            # correct: Defined as "1/3 trace of diagonalized U matrix".
660            # SEE cell2GS & Uij2Ueqv to GSASIIlattice. Former is needed to make the GS matrix used by the latter.
661            t = 0.0
662            for j in (2,3,4):
663                var = pfx+varnames[cia+j]+":"+str(i)
664                t += parmDict.get(var,at[cia+j])
665        for j in (cx,cx+1,cx+2,cx+3,cia,cia+1):
666            if j in (cx,cx+1,cx+2):
667                dig = 11
668                sigdig = -0.00009
669            else:
670                dig = 10
671                sigdig = -0.009
672            if j == cia:
673                s += adp
674            else:
675                var = pfx+varnames[j]+":"+str(i)
676                dvar = pfx+"d"+varnames[j]+":"+str(i)
677                if dvar not in sigDict:
678                    dvar = var
679                if j == cia+1 and adp == 'Uani ':
680                    val = t/3.
681                    sig = sigdig
682                else:
683                    #print var,(var in parmDict),(var in sigDict)
684                    val = parmDict.get(var,at[j])
685                    sig = sigDict.get(dvar,sigdig)
686                    #if dvar in G2mv.GetDependentVars(): # do not include an esd for dependent vars
687                    #    sig = -abs(sig)
688                s += PutInCol(G2mth.ValEsd(val,sig),dig)
689        s += PutInCol(at[cs+1],3)
690        WriteCIFitem(fp, s)
691    if naniso: 
692        # now loop over aniso atoms
693        WriteCIFitem(fp, '\nloop_' + '\n   _atom_site_aniso_label' +
694                     '\n   _atom_site_aniso_U_11' + '\n   _atom_site_aniso_U_22' +
695                     '\n   _atom_site_aniso_U_33' + '\n   _atom_site_aniso_U_12' +
696                     '\n   _atom_site_aniso_U_13' + '\n   _atom_site_aniso_U_23')
697        for i,at in enumerate(Atoms):
698            fval = parmDict.get(fpfx+str(i),at[cfrac])
699            if fval == 0.0: continue # ignore any atoms that have a occupancy set to 0 (exact)
700            if at[cia] == 'I': continue
701            s = PutInCol(labellist[i],6) # label
702            for j in (2,3,4,5,6,7):
703                sigdig = -0.0009
704                var = pfx+varnames[cia+j]+":"+str(i)
705                val = parmDict.get(var,at[cia+j])
706                sig = sigDict.get(var,sigdig)
707                s += PutInCol(G2mth.ValEsd(val,sig),11)
708            WriteCIFitem(fp, s)
709    # now loop over mag atoms (e.g. all of them)
710    WriteCIFitem(fp, '\nloop_' + '\n   _atom_site_moment.label' +
711                 '\n   _atom_site_moment.crystalaxis_x' +
712                 '\n   _atom_site_moment.crystalaxis_y' +
713                 '\n   _atom_site_moment.crystalaxis_z')
714    for i,at in enumerate(Atoms):
715        fval = parmDict.get(fpfx+str(i),at[cfrac])
716        if fval == 0.0: continue # ignore any atoms that have a occupancy set to 0 (exact)
717        s = PutInCol(labellist[i],6) # label
718        for j in (cx+4,cx+5,cx+6):
719            sigdig = -0.0009
720            var = pfx+varnames[j]+":"+str(i)
721            val = parmDict.get(var,at[j])
722            sig = sigDict.get(var,sigdig)
723            s += PutInCol(G2mth.ValEsd(val,sig),11)
724        WriteCIFitem(fp, s)
725
726def WriteAtomsMM(fp, phasedict, phasenam, parmDict, sigDict,
727                          RBparms={}):
728    'Write atom positions to CIF using mmCIF items'
729    AA3letter = ['ALA','ARG','ASN','ASP','CYS','GLN','GLU','GLY','HIS','ILE',
730                'LEU','LYS','MET','PHE','PRO','SER','THR','TRP','TYR','VAL','MSE']
731    # phasedict = self.Phases[phasenam] # pointer to current phase info
732    General = phasedict['General']
733    cx,ct,cs,cia = General['AtomPtrs']
734    #GS = G2lat.cell2GS(General['Cell'][1:7])
735    Amat = G2lat.cell2AB(General['Cell'][1:7])[0]
736    Atoms = phasedict['Atoms']
737    #cfrac = cx+3
738    #fpfx = str(phasedict['pId'])+'::Afrac:'
739    if len(Atoms) == 0:
740        WriteCIFitem(fp, '\n# PHASE HAS NO ATOMS!')
741        return
742
743    WriteCIFitem(fp, '\n# ATOMIC COORDINATES AND DISPLACEMENT PARAMETERS')
744    WriteCIFitem(fp, 'loop_ '+
745                 '\n   _atom_site.group_PDB'+
746                 '\n   _atom_site.id'+
747                 '\n   _atom_site.type_symbol'+
748                 '\n   _atom_site.label_atom_id'+
749                 '\n   _atom_site.auth_atom_id'+
750                 '\n   _atom_site.label_alt_id'+
751                 '\n   _atom_site.label_comp_id'+
752                 '\n   _atom_site.auth_comp_id'+
753                 '\n   _atom_site.label_asym_id'+
754                 '\n   _atom_site.auth_asym_id'+
755                 '\n   _atom_site.label_entity_id'+
756                 '\n   _atom_site.label_seq_id'+
757                 '\n   _atom_site.auth_seq_id'+
758                 '\n   _atom_site.pdbx_PDB_ins_code'+
759                 '\n   _atom_site.pdbx_formal_charge'+
760                 '\n   _atom_site.pdbx_PDB_model_num'
761                 '\n   _atom_site.fract_x'+
762                 '\n   _atom_site.fract_y'+
763                 '\n   _atom_site.fract_z'+
764                 '\n   _atom_site.occupancy'+
765                 '\n   _atom_site.B_iso_or_equiv'+
766                 '\n   _atom_site.Cartn_x'+
767                 '\n   _atom_site.Cartn_y'+
768                 '\n   _atom_site.Cartn_z'
769                 )
770
771# _atom_site.Cartn_x_esd
772# _atom_site.Cartn_y_esd
773# _atom_site.Cartn_z_esd
774# _atom_site.occupancy_esd
775#
776
777    varnames = {cx:'Ax',cx+1:'Ay',cx+2:'Az',cx+3:'Afrac',
778                cia+1:'AUiso',cia+2:'AU11',cia+3:'AU22',cia+4:'AU33',
779                cia+5:'AU12',cia+6:'AU13',cia+7:'AU23'}
780
781    pfx = str(phasedict['pId'])+'::'
782    num = 0
783    # uniquely index the side chains
784    entity_id = {}
785    for i,at in enumerate(Atoms): 
786        if at[ct-2] not in entity_id:
787            num += 1
788            entity_id[at[ct-2]] = num
789
790    # loop over all atoms
791#    naniso = 0
792    for i,at in enumerate(Atoms):
793        if at[ct-3] in AA3letter:
794            s = 'ATOM   '
795        else:
796            s = 'HETATM '
797        s += PutInCol(str(i+1),5) # atom number
798        s += PutInCol(FmtAtomType(at[ct]),4) # type
799        s += PutInCol(at[ct-1],4) # _atom_id
800        s += PutInCol(at[ct-1],4) # _atom_id
801        s += PutInCol('.',2) # alt_id
802        s += PutInCol(at[ct-3],4) # comp_id
803        s += PutInCol(at[ct-3],4) # comp_id
804        s += PutInCol(at[ct-2],3) # _asym_id
805        s += PutInCol(at[ct-2],3) # _asym_id
806        s += PutInCol(str(entity_id[at[ct-2]]),3) # entity_id
807        s += PutInCol(at[ct-4],2) # _seq_id
808        s += PutInCol(at[ct-4],2) # _seq_id
809        s += PutInCol('?',2) # pdbx_PDB_ins_code
810        s += PutInCol('?',2) # pdbx_formal_charge
811        s += PutInCol('1',2) # pdbx_PDB_model_num
812           
813#        fval = parmDict.get(fpfx+str(i),at[cfrac])
814#        if fval == 0.0: continue # ignore any atoms that have a occupancy set to 0 (exact)
815#        if at[cia] == 'I':
816#            adp = 'Uiso '
817#        else:
818#            adp = 'Uani '
819#            naniso += 1
820#            t = G2lat.Uij2Ueqv(at[cia+2:cia+8],GS,Amat)[0]
821#            for j in (2,3,4):
822#                var = pfx+varnames[cia+j]+":"+str(i)
823        for j in (cx,cx+1,cx+2,cx+3,cia+1):
824            if j in (cx,cx+1,cx+2):
825                dig = 11
826                sigdig = -0.00009
827            else:
828                dig = 5
829                sigdig = -0.009
830            var = pfx+varnames[j]+":"+str(i)
831            dvar = pfx+"d"+varnames[j]+":"+str(i)
832            if dvar not in sigDict:
833                dvar = var
834            #print var,(var in parmDict),(var in sigDict)
835            val = parmDict.get(var,at[j])
836            sig = sigDict.get(dvar,sigdig)
837            if j == cia+1:  # convert U to B
838                val *= 8*np.pi**2
839                sig *= 8*np.pi**2
840            #if dvar in G2mv.GetDependentVars(): # do not include an esd for dependent vars
841            #    sig = -abs(sig)
842            s += PutInCol(G2mth.ValEsd(val,sig),dig)
843        # Cartesian coordinates
844        for xyz in np.inner(Amat,at[cx:cx+3]):
845            s += PutInCol(G2mth.ValEsd(xyz,-0.009),8)
846        WriteCIFitem(fp, s)
847    # save information about rigid bodies
848#     header = False
849#     num = 0
850#     rbAtoms = []
851#     for irb,RBObj in enumerate(phasedict['RBModels'].get('Residue',[])):
852#         if not header:
853#             header = True
854#             RBheader(fp)
855#         jrb = RBparms['RBIds']['Residue'].index(RBObj['RBId'])
856#         rbsx = str(irb)+':'+str(jrb)
857#         num += 1
858#         WriteCIFitem(fp,'',str(num))
859#         RBModel = RBparms['Residue'][RBObj['RBId']]
860#         SGData = phasedict['General']['SGData']
861#         Sytsym,Mult = G2spc.SytSym(RBObj['Orig'][0],SGData)[:2]
862#         s = '''GSAS-II residue rigid body "{}" with {} atoms
863#   Site symmetry @ origin: {}, multiplicity: {}
864# '''.format(RBObj['RBname'],len(RBModel['rbTypes']),Sytsym,Mult)
865#         for i in G2stIO.WriteResRBModel(RBModel):
866#             s += i
867#         s += '\n Location:\n'
868#         for i in G2stIO.WriteRBObjPOAndSig(pfx,'RBR',rbsx,parmDict,sigDict):
869#             s += i+'\n'
870#         for i in G2stIO.WriteRBObjTLSAndSig(pfx,'RBR',rbsx,
871#                         RBObj['ThermalMotion'][0],parmDict,sigDict):
872#             s += i
873#         nTors = len(RBObj['Torsions'])
874#         if nTors:
875#             for i in G2stIO.WriteRBObjTorAndSig(pfx,rbsx,parmDict,sigDict,
876#                         nTors):
877#                 s += i
878#         WriteCIFitem(fp,'',s.rstrip())
879       
880#         pId = phasedict['pId']
881#         for i in RBObj['Ids']:
882#             lbl = G2obj.LookupAtomLabel(pId,G2obj.LookupAtomId(pId,i))[0]
883#             rbAtoms.append('{:7s} 1_555 {:3d} ?'.format(lbl,num))
884#         #GSASIIpath.IPyBreak()
885
886#     for irb,RBObj in enumerate(phasedict['RBModels'].get('Vector',[])):
887#         if not header:
888#             header = True
889#             RBheader(fp)
890#         jrb = RBparms['RBIds']['Vector'].index(RBObj['RBId'])
891#         rbsx = str(irb)+':'+str(jrb)
892#         num += 1
893#         WriteCIFitem(fp,'',str(num))
894#         RBModel = RBparms['Vector'][RBObj['RBId']]
895#         SGData = phasedict['General']['SGData']
896#         Sytsym,Mult = G2spc.SytSym(RBObj['Orig'][0],SGData)[:2]
897#         s = '''GSAS-II vector rigid body "{}" with {} atoms
898#   Site symmetry @ origin: {}, multiplicity: {}
899# '''.format(RBObj['RBname'],len(RBModel['rbTypes']),Sytsym,Mult)
900#         for i in G2stIO.WriteVecRBModel(RBModel,sigDict,irb):
901#             s += i
902#         s += '\n Location:\n'
903#         for i in G2stIO.WriteRBObjPOAndSig(pfx,'RBV',rbsx,parmDict,sigDict):
904#             s += i+'\n'
905#         for i in G2stIO.WriteRBObjTLSAndSig(pfx,'RBV',rbsx,
906#                         RBObj['ThermalMotion'][0],parmDict,sigDict):
907#             s += i
908#         WriteCIFitem(fp,'',s.rstrip())
909       
910#         pId = phasedict['pId']
911#         for i in RBObj['Ids']:
912#             lbl = G2obj.LookupAtomLabel(pId,G2obj.LookupAtomId(pId,i))[0]
913#             rbAtoms.append('{:7s} 1_555 {:3d} ?'.format(lbl,num))
914
915#     if rbAtoms:
916#         WriteCIFitem(fp,'loop_\n    _restr_rigid_body.id'+
917#             '\n    _restr_rigid_body.atom_site_label\n    _restr_rigid_body.site_symmetry'+
918#             '\n    _restr_rigid_body.class_id\n    _restr_rigid_body.details')
919#         for i,l in enumerate(rbAtoms):
920#             WriteCIFitem(fp,'   {:5d} {}'.format(i+1,l))
921
922# Refactored over here to allow access by GSASIIscriptable.py
923def WriteSeqAtomsNuclear(fp, cell, phasedict, phasenam, hist, seqData, RBparms):
924    'Write atom positions to CIF'
925    General = phasedict['General']
926    cx,ct,cs,cia = General['AtomPtrs']
927    GS = G2lat.cell2GS(cell[:6])
928    Amat = G2lat.cell2AB(cell[:6])[0]
929
930    # phasedict = self.Phases[phasenam] # pointer to current phase info
931    parmDict = seqData[hist]['parmDict']
932    sigDict = dict(zip(seqData[hist]['varyList'],seqData[hist]['sig']))
933    Atoms = phasedict['Atoms']
934    cfrac = cx+3
935    fpfx = str(phasedict['pId'])+'::Afrac:'
936    for i,at in enumerate(Atoms):
937        fval = parmDict.get(fpfx+str(i),at[cfrac])
938        if fval != 0.0:
939            break
940    else:
941        WriteCIFitem(fp, '\n# PHASE HAS NO ATOMS!')
942        return
943
944    WriteCIFitem(fp, '\n# ATOMIC COORDINATES AND DISPLACEMENT PARAMETERS')
945    WriteCIFitem(fp, 'loop_ '+
946                 '\n   _atom_site_label'+
947                 '\n   _atom_site_type_symbol'+
948                 '\n   _atom_site_fract_x'+
949                 '\n   _atom_site_fract_y'+
950                 '\n   _atom_site_fract_z'+
951                 '\n   _atom_site_occupancy'+
952                 '\n   _atom_site_adp_type'+
953                 '\n   _atom_site_U_iso_or_equiv'+
954                 '\n   _atom_site_site_symmetry_multiplicity')
955
956    varnames = {cx:'Ax',cx+1:'Ay',cx+2:'Az',cx+3:'Afrac',
957                cia+1:'AUiso',cia+2:'AU11',cia+3:'AU22',cia+4:'AU33',
958                cia+5:'AU12',cia+6:'AU13',cia+7:'AU23'}
959
960    labellist = []  # used to make atom labels unique as required in CIF
961    pfx = str(phasedict['pId'])+'::'
962    # loop over all atoms
963    naniso = 0
964
965    for i,at in enumerate(Atoms):
966        if phasedict['General']['Type'] == 'macromolecular':
967            label = '%s_%s_%s_%s'%(at[ct-1],at[ct-3],at[ct-4],at[ct-2])
968            s = PutInCol(MakeUniqueLabel(label,labellist),15) # label
969        else:
970            s = PutInCol(MakeUniqueLabel(at[ct-1],labellist),6) # label
971        fval = parmDict.get(fpfx+str(i),at[cfrac])
972        if fval == 0.0: continue # ignore any atoms that have a occupancy set to 0 (exact)
973        s += PutInCol(FmtAtomType(at[ct]),4) # type
974        if at[cia] == 'I':
975            adp = 'Uiso '
976        else:
977            adp = 'Uani '
978            naniso += 1
979            t = G2lat.Uij2Ueqv(at[cia+2:cia+8],GS,Amat)[0]
980            for j in (2,3,4):
981                var = pfx+varnames[cia+j]+":"+str(i)
982        for j in (cx,cx+1,cx+2,cx+3,cia,cia+1):
983            if j in (cx,cx+1,cx+2):
984                dig = 11
985                sigdig = -0.00009
986            else:
987                dig = 10
988                sigdig = -0.0009
989            if j == cia:
990                s += adp
991            else:
992                var = pfx+varnames[j]+":"+str(i)
993                dvar = pfx+"d"+varnames[j]+":"+str(i)
994                if dvar not in sigDict:
995                    dvar = var
996                if j == cia+1 and adp == 'Uani ':
997                    sig = sigdig
998                    val = t
999                else:
1000                    #print var,(var in parmDict),(var in sigDict)
1001                    val = parmDict.get(var,at[j])
1002                    sig = sigDict.get(dvar,sigdig)
1003                    #if dvar in G2mv.GetDependentVars(): # do not include an esd for dependent vars
1004                    #    sig = -abs(sig)
1005                s += PutInCol(G2mth.ValEsd(val,sig),dig)
1006        s += PutInCol(at[cs+1],3)
1007        WriteCIFitem(fp, s)
1008    if naniso != 0: 
1009        # now loop over aniso atoms
1010        WriteCIFitem(fp, '\nloop_' + '\n   _atom_site_aniso_label' +
1011                     '\n   _atom_site_aniso_U_11' + '\n   _atom_site_aniso_U_22' +
1012                     '\n   _atom_site_aniso_U_33' + '\n   _atom_site_aniso_U_12' +
1013                     '\n   _atom_site_aniso_U_13' + '\n   _atom_site_aniso_U_23')
1014        for i,at in enumerate(Atoms):
1015            fval = parmDict.get(fpfx+str(i),at[cfrac])
1016            if fval == 0.0: continue # ignore any atoms that have a occupancy set to 0 (exact)
1017            if at[cia] == 'I': continue
1018            s = PutInCol(labellist[i],6) # label
1019            for j in (2,3,4,5,6,7):
1020                sigdig = -0.0009
1021                var = pfx+varnames[cia+j]+":"+str(i)
1022                val = parmDict.get(var,at[cia+j])
1023                sig = sigDict.get(var,sigdig)
1024                s += PutInCol(G2mth.ValEsd(val,sig),11)
1025            WriteCIFitem(fp, s)
1026    # save information about rigid bodies
1027    header = False
1028    num = 0
1029    rbAtoms = []
1030    for irb,RBObj in enumerate(phasedict['RBModels'].get('Residue',[])):
1031        if not header:
1032            header = True
1033            RBheader(fp)
1034        jrb = RBparms['RBIds']['Residue'].index(RBObj['RBId'])
1035        rbsx = str(irb)+':'+str(jrb)
1036        num += 1
1037        WriteCIFitem(fp,'',str(num))
1038        RBModel = RBparms['Residue'][RBObj['RBId']]
1039        SGData = phasedict['General']['SGData']
1040        Sytsym,Mult = G2spc.SytSym(RBObj['Orig'][0],SGData)[:2]
1041        s = '''GSAS-II residue rigid body "{}" with {} atoms
1042  Site symmetry @ origin: {}, multiplicity: {}
1043'''.format(RBObj['RBname'],len(RBModel['rbTypes']),Sytsym,Mult)
1044        for i in G2stIO.WriteResRBModel(RBModel):
1045            s += i
1046        s += '\n Location:\n'
1047        for i in G2stIO.WriteRBObjPOAndSig(pfx,'RBR',rbsx,parmDict,sigDict):
1048            s += i+'\n'
1049        for i in G2stIO.WriteRBObjTLSAndSig(pfx,'RBR',rbsx,
1050                        RBObj['ThermalMotion'][0],parmDict,sigDict):
1051            s += i
1052        nTors = len(RBObj['Torsions'])
1053        if nTors:
1054            for i in G2stIO.WriteRBObjTorAndSig(pfx,rbsx,parmDict,sigDict,
1055                        nTors):
1056                s += i
1057        WriteCIFitem(fp,'',s.rstrip())
1058       
1059        pId = phasedict['pId']
1060        for i in RBObj['Ids']:
1061            lbl = G2obj.LookupAtomLabel(pId,G2obj.LookupAtomId(pId,i))[0]
1062            rbAtoms.append('{:7s} 1_555 {:3d} ?'.format(lbl,num))
1063        #GSASIIpath.IPyBreak()
1064
1065    for irb,RBObj in enumerate(phasedict['RBModels'].get('Vector',[])):
1066        if not header:
1067            header = True
1068            RBheader(fp)
1069        jrb = RBparms['RBIds']['Vector'].index(RBObj['RBId'])
1070        rbsx = str(irb)+':'+str(jrb)
1071        num += 1
1072        WriteCIFitem(fp,'',str(num))
1073        RBModel = RBparms['Vector'][RBObj['RBId']]
1074        SGData = phasedict['General']['SGData']
1075        Sytsym,Mult = G2spc.SytSym(RBObj['Orig'][0],SGData)[:2]
1076        s = '''GSAS-II vector rigid body "{}" with {} atoms
1077  Site symmetry @ origin: {}, multiplicity: {}
1078'''.format(RBObj['RBname'],len(RBModel['rbTypes']),Sytsym,Mult)
1079        for i in G2stIO.WriteVecRBModel(RBModel,sigDict,irb):
1080            s += i
1081        s += '\n Location:\n'
1082        for i in G2stIO.WriteRBObjPOAndSig(pfx,'RBV',rbsx,parmDict,sigDict):
1083            s += i+'\n'
1084        for i in G2stIO.WriteRBObjTLSAndSig(pfx,'RBV',rbsx,
1085                        RBObj['ThermalMotion'][0],parmDict,sigDict):
1086            s += i
1087        WriteCIFitem(fp,'',s.rstrip())
1088       
1089        pId = phasedict['pId']
1090        for i in RBObj['Ids']:
1091            lbl = G2obj.LookupAtomLabel(pId,G2obj.LookupAtomId(pId,i))[0]
1092            rbAtoms.append('{:7s} 1_555 {:3d} ?'.format(lbl,num))
1093
1094    if rbAtoms:
1095        WriteCIFitem(fp,'loop_\n    _restr_rigid_body.id'+
1096            '\n    _restr_rigid_body.atom_site_label\n    _restr_rigid_body.site_symmetry'+
1097            '\n    _restr_rigid_body.class_id\n    _restr_rigid_body.details')
1098        for i,l in enumerate(rbAtoms):
1099            WriteCIFitem(fp,'   {:5d} {}'.format(i+1,l))
1100           
1101# Refactored over here to allow access by GSASIIscriptable.py
1102def MakeUniqueLabel(lbl, labellist):
1103    lbl = lbl.strip()
1104    if not lbl: # deal with a blank label
1105        lbl = 'A_1'
1106    if lbl not in labellist:
1107        labellist.append(lbl)
1108        return lbl
1109    i = 1
1110    prefix = lbl
1111    if '_' in lbl:
1112        prefix = lbl[:lbl.rfind('_')]
1113        suffix = lbl[lbl.rfind('_')+1:]
1114        try:
1115            i = int(suffix)+1
1116        except:
1117            pass
1118    while prefix+'_'+str(i) in labellist:
1119        i += 1
1120    else:
1121        lbl = prefix+'_'+str(i)
1122        labellist.append(lbl)
1123
1124
1125# Refactored over here to allow access by GSASIIscriptable.py
1126def HillSortElements(elmlist):
1127    '''Sort elements in "Hill" order: C, H, others, (where others
1128    are alphabetical).
1129
1130    :params list elmlist: a list of element strings
1131
1132    :returns: a sorted list of element strings
1133    '''
1134    newlist = []
1135    oldlist = elmlist[:]
1136    for elm in ('C','H'):
1137        if elm in elmlist:
1138            newlist.append(elm)
1139            oldlist.pop(oldlist.index(elm))
1140    return newlist+sorted(oldlist)
1141
1142
1143def FmtAtomType(sym):
1144    'Reformat a GSAS-II atom type symbol to match CIF rules'
1145    sym = sym.replace('_','') # underscores are not allowed: no isotope designation?
1146    # in CIF, oxidation state sign symbols come after, not before
1147    if '+' in sym:
1148        sym = sym.replace('+','') + '+'
1149    elif '-' in sym:
1150        sym = sym.replace('-','') + '-'
1151    return sym
1152
1153
1154def PutInCol(val, wid):
1155    val = str(val).replace(' ', '')
1156    if not val: val = '?'
1157    fmt = '{:' + str(wid) + '} '
1158    try:
1159        return fmt.format(val)
1160    except TypeError:
1161        return fmt.format('.')
1162
1163
1164# Refactored over here to allow access by GSASIIscriptable.py
1165def WriteComposition(fp, phasedict, phasenam, parmDict, quickmode=True, keV=None):
1166    '''determine the composition for the unit cell, crudely determine Z and
1167    then compute the composition in formula units.
1168
1169    If quickmode is False, then scattering factors are added to the element loop.
1170
1171    If keV is specified, then resonant scattering factors are also computed and included.
1172    '''
1173    General = phasedict['General']
1174    Z = General.get('cellZ',0.0)
1175    cx,ct,cs,cia = General['AtomPtrs']
1176    Atoms = phasedict['Atoms']
1177    fpfx = str(phasedict['pId'])+'::Afrac:'
1178    cfrac = cx+3
1179    cmult = cs+1
1180    compDict = {} # combines H,D & T
1181    sitemultlist = []
1182    massDict = dict(zip(General['AtomTypes'],General['AtomMass']))
1183    cellmass = 0
1184    elmLookup = {}
1185    for i,at in enumerate(Atoms):
1186        atype = at[ct].strip()
1187        if atype.find('-') != -1: atype = atype.split('-')[0]
1188        if atype.find('+') != -1: atype = atype.split('+')[0]
1189        atype = atype[0].upper()+atype[1:2].lower() # force case conversion
1190        if atype == "D" or atype == "D": atype = "H"
1191        fvar = fpfx+str(i)
1192        fval = parmDict.get(fvar,at[cfrac])
1193        mult = at[cmult]
1194        if not massDict.get(at[ct]):
1195            print('Error: No mass found for atom type '+at[ct])
1196            print('Will not compute cell contents for phase '+phasenam)
1197            return
1198        cellmass += massDict[at[ct]]*mult*fval
1199        compDict[atype] = compDict.get(atype,0.0) + mult*fval
1200        elmLookup[atype] = at[ct].strip()
1201        if fval == 1: sitemultlist.append(mult)
1202    if len(compDict.keys()) == 0: return # no elements!
1203    if Z < 1: # Z has not been computed or set by user
1204        Z = 1
1205        if not sitemultlist:
1206            General['cellZ'] = 1
1207            return
1208        for i in range(2,min(sitemultlist)+1):
1209            for m in sitemultlist:
1210                if m % i != 0:
1211                    break
1212                else:
1213                    Z = i
1214        General['cellZ'] = Z # save it
1215
1216    if not quickmode:
1217        FFtable = G2el.GetFFtable(General['AtomTypes'])
1218        BLtable = G2el.GetBLtable(General)
1219
1220    WriteCIFitem(fp, '\nloop_  _atom_type_symbol _atom_type_number_in_cell')
1221    s = '       '
1222    if not quickmode:
1223        for j in ('a1','a2','a3','a4','b1','b2','b3','b4','c',2,1):
1224            if len(s) > 80:
1225                WriteCIFitem(fp, s)
1226                s = '       '
1227            if j==1:
1228                s += ' _atom_type_scat_source'
1229            elif j==2:
1230                s += ' _atom_type_scat_length_neutron'
1231            else:
1232                s += ' _atom_type_scat_Cromer_Mann_'
1233                s += j
1234        if keV:
1235            WriteCIFitem(fp, s)
1236            s = '        _atom_type_scat_dispersion_real _atom_type_scat_dispersion_imag _atom_type_scat_dispersion_source'
1237        WriteCIFitem(fp, s)
1238
1239           
1240    formula = ''
1241    for elem in HillSortElements(list(compDict.keys())):
1242        s = '  '
1243        elmsym = elmLookup[elem]
1244        # CIF does not allow underscore in element symbol (https://www.iucr.org/__data/iucr/cifdic_html/1/cif_core.dic/Iatom_type_symbol.html)
1245        if elmsym.endswith("_"):
1246             s += PutInCol(elmsym.replace('_',''))
1247        elif '_' in elmsym:
1248             s += PutInCol(elmsym.replace('_','~'))
1249        else:
1250            s += PutInCol(elmsym,7)
1251        s += PutInCol(G2mth.ValEsd(compDict[elem],-0.009,True),5)
1252        if not quickmode:
1253            for i in 'fa','fb','fc':
1254                if i != 'fc':
1255                    for j in range(4):
1256                        if elmsym in FFtable:
1257                            val = G2mth.ValEsd(FFtable[elmsym][i][j],-0.0009,True)
1258                        else:
1259                            val = '?'
1260                        s += ' '
1261                        s += PutInCol(val,9)
1262                else:
1263                    if elmsym in FFtable:
1264                        val = G2mth.ValEsd(FFtable[elmsym][i],-0.0009,True)
1265                    else:
1266                        val = '?'
1267                    s += ' '
1268                    s += PutInCol(val,9)
1269            if elmsym in BLtable:
1270                bldata = BLtable[elmsym]
1271                #isotope = bldata[0]
1272                #mass = bldata[1]['Mass']
1273                if 'BW-LS' in bldata[1]:
1274                    val = 0
1275                else:
1276                    val = G2mth.ValEsd(bldata[1]['SL'][0],-0.0009,True)
1277            else:
1278                val = '?'
1279            s += ' '
1280            s += PutInCol(val,9)
1281            WriteCIFitem(fp,s.rstrip())
1282            WriteCIFitem(fp,'      https://subversion.xray.aps.anl.gov/pyGSAS/trunk/atmdata.py')
1283            if keV:
1284                Orbs = G2el.GetXsectionCoeff(elem.split('+')[0].split('-')[0])
1285                FP,FPP,Mu = G2el.FPcalc(Orbs, keV)
1286                WriteCIFitem(fp,{:8.3f}{:8.3f}   https://subversion.xray.aps.anl.gov/pyGSAS/trunk/atmdata.py'.format(FP,FPP))
1287        else:
1288            WriteCIFitem(fp,s.rstrip())
1289        if formula: formula += " "
1290        formula += elem
1291        if compDict[elem] == Z: continue
1292        formula += G2mth.ValEsd(compDict[elem]/Z,-0.009,True)
1293    WriteCIFitem(fp,  '\n# Note that Z affects _cell_formula_sum and _weight')
1294    WriteCIFitem(fp,  '_cell_formula_units_Z',str(Z))
1295    WriteCIFitem(fp,  '_chemical_formula_sum',formula)
1296    WriteCIFitem(fp,  '_chemical_formula_weight',
1297                  G2mth.ValEsd(cellmass/Z,-0.09,True))
1298
1299def WriteCompositionMM(fp, phasedict, phasenam, parmDict, quickmode=True, keV=None):
1300    '''determine the composition for the unit cell, crudely determine Z and
1301    then compute the composition in formula units.
1302
1303    If quickmode is False, then scattering factors are added to the element loop.
1304
1305    If keV is specified, then resonant scattering factors are also computed and included.
1306    '''
1307    General = phasedict['General']
1308    Z = General.get('cellZ',0.0)
1309    cx,ct,cs,cia = General['AtomPtrs']
1310    Atoms = phasedict['Atoms']
1311    fpfx = str(phasedict['pId'])+'::Afrac:'
1312    cfrac = cx+3
1313    cmult = cs+1
1314    compDict = {} # combines H,D & T
1315    sitemultlist = []
1316    massDict = dict(zip(General['AtomTypes'],General['AtomMass']))
1317    cellmass = 0
1318    elmLookup = {}
1319    for i,at in enumerate(Atoms):
1320        atype = at[ct].strip()
1321        if atype.find('-') != -1: atype = atype.split('-')[0]
1322        if atype.find('+') != -1: atype = atype.split('+')[0]
1323        atype = atype[0].upper()+atype[1:2].lower() # force case conversion
1324        if atype == "D" or atype == "D": atype = "H"
1325        fvar = fpfx+str(i)
1326        fval = parmDict.get(fvar,at[cfrac])
1327        mult = at[cmult]
1328        if not massDict.get(at[ct]):
1329            print('Error: No mass found for atom type '+at[ct])
1330            print('Will not compute cell contents for phase '+phasenam)
1331            return
1332        cellmass += massDict[at[ct]]*mult*fval
1333        compDict[atype] = compDict.get(atype,0.0) + mult*fval
1334        elmLookup[atype] = at[ct].strip()
1335        if fval == 1: sitemultlist.append(mult)
1336    if len(compDict.keys()) == 0: return # no elements!
1337    if Z < 1: # Z has not been computed or set by user
1338        Z = 1
1339        if not sitemultlist:
1340            General['cellZ'] = 1
1341            return
1342        for i in range(2,min(sitemultlist)+1):
1343            for m in sitemultlist:
1344                if m % i != 0:
1345                    break
1346                else:
1347                    Z = i
1348        General['cellZ'] = Z # save it
1349
1350    if not quickmode:
1351        FFtable = G2el.GetFFtable(General['AtomTypes'])
1352        BLtable = G2el.GetBLtable(General)
1353
1354    WriteCIFitem(fp, '\nloop_  _atom_type.symbol _atom_type.number_in_cell')
1355    s = '       '
1356    if not quickmode:
1357        for j in ('a1','a2','a3','a4','b1','b2','b3','b4','c',2,1):
1358            if len(s) > 80:
1359                WriteCIFitem(fp, s)
1360                s = '       '
1361            if j==1:
1362                s += ' _atom_type.scat_source'
1363            elif j==2:
1364                s += ' _atom_type.scat_length_neutron'
1365            else:
1366                s += ' _atom_type.scat_Cromer_Mann_'
1367                s += j
1368        if keV:
1369            WriteCIFitem(fp, s)
1370            s = '        _atom_type.scat_dispersion_real _atom_type.scat_dispersion_imag _atom_type_scat_dispersion_source'
1371        WriteCIFitem(fp, s)
1372
1373           
1374    formula = ''
1375    for elem in HillSortElements(list(compDict.keys())):
1376        s = '  '
1377        elmsym = elmLookup[elem]
1378        # CIF does not allow underscore in element symbol (https://www.iucr.org/__data/iucr/cifdic_html/1/cif_core.dic/Iatom_type_symbol.html)
1379        if elmsym.endswith("_"):
1380             s += PutInCol(elmsym.replace('_',''))
1381        elif '_' in elmsym:
1382             s += PutInCol(elmsym.replace('_','~'))
1383        else:
1384            s += PutInCol(elmsym,7)
1385        s += PutInCol(G2mth.ValEsd(compDict[elem],-0.009,True),5)
1386        if not quickmode:
1387            for i in 'fa','fb','fc':
1388                if i != 'fc':
1389                    for j in range(4):
1390                        if elmsym in FFtable:
1391                            val = G2mth.ValEsd(FFtable[elmsym][i][j],-0.0009,True)
1392                        else:
1393                            val = '?'
1394                        s += ' '
1395                        s += PutInCol(val,9)
1396                else:
1397                    if elmsym in FFtable:
1398                        val = G2mth.ValEsd(FFtable[elmsym][i],-0.0009,True)
1399                    else:
1400                        val = '?'
1401                    s += ' '
1402                    s += PutInCol(val,9)
1403            if elmsym in BLtable:
1404                bldata = BLtable[elmsym]
1405                #isotope = bldata[0]
1406                #mass = bldata[1]['Mass']
1407                if 'BW-LS' in bldata[1]:
1408                    val = 0
1409                else:
1410                    val = G2mth.ValEsd(bldata[1]['SL'][0],-0.0009,True)
1411            else:
1412                val = '?'
1413            s += ' '
1414            s += PutInCol(val,9)
1415            WriteCIFitem(fp,s.rstrip())
1416            WriteCIFitem(fp,'      https://subversion.xray.aps.anl.gov/pyGSAS/trunk/atmdata.py')
1417            if keV:
1418                Orbs = G2el.GetXsectionCoeff(elem.split('+')[0].split('-')[0])
1419                FP,FPP,Mu = G2el.FPcalc(Orbs, keV)
1420                WriteCIFitem(fp,{:8.3f}{:8.3f}   https://subversion.xray.aps.anl.gov/pyGSAS/trunk/atmdata.py'.format(FP,FPP))
1421        else:
1422            WriteCIFitem(fp,s.rstrip())
1423        if formula: formula += " "
1424        formula += elem
1425        if compDict[elem] == Z: continue
1426        formula += G2mth.ValEsd(compDict[elem]/Z,-0.009,True)
1427    WriteCIFitem(fp,  '\n# Note that Z affects _cell_formula.sum and .weight')
1428    WriteCIFitem(fp,  '_cell.formula_units_Z',str(Z))
1429    WriteCIFitem(fp,  '_chemical_formula.sum',formula)
1430    WriteCIFitem(fp,  '_chemical_formula.weight',
1431                  G2mth.ValEsd(cellmass/Z,-0.09,True))
1432
1433class ExportCIF(G2IO.ExportBaseclass):
1434    '''Base class for CIF exports
1435    '''
1436    def __init__(self,G2frame,formatName,extension,longFormatName=None,):
1437        G2IO.ExportBaseclass.__init__(self,G2frame,formatName,extension,longFormatName=None)
1438        self.exporttype = []
1439        self.author = ''
1440        self.CIFname = ''
1441
1442    def ValidateAscii(self,checklist):
1443        '''Validate items as ASCII'''
1444        msg = ''
1445        for lbl,val in checklist:
1446            if not all(ord(c) < 128 for c in val):
1447                if msg: msg += '\n'
1448                msg += lbl + " contains unicode characters: " + val
1449        if msg:
1450            G2G.G2MessageBox(self.G2frame,
1451                             'Error: CIFs can contain only ASCII characters. Please change item(s) below:\n\n'+msg,
1452                             'Unicode not valid for CIF')
1453            return True
1454
1455    def _CellSelectNeeded(self,phasenam):
1456        '''Determines if selection is needed for a T value in a multiblock CIF
1457
1458        :returns: True if the choice of T is ambiguous and a human should
1459          be asked.
1460        '''
1461        phasedict = self.Phases[phasenam] # pointer to current phase info
1462        Tlist = {}  # histname & T values used for cell w/o Hstrain
1463        DijTlist = {} # hId & T values used for cell w/Hstrain
1464        # scan over histograms used in this phase to determine the best
1465        # data collection T value
1466        for h in phasedict['Histograms']:
1467            if not phasedict['Histograms'][h]['Use']: continue
1468            if 'Flack' in phasedict['Histograms'][h].keys():       #single crystal data
1469                return False
1470            T = self.Histograms[h]['Sample Parameters']['Temperature']
1471            if np.any(abs(np.array(phasedict['Histograms'][h]['HStrain'][0])) > 1e-8):
1472                DijTlist[h] = T
1473            else:
1474                Tlist[h] = T
1475        if len(Tlist) > 0:
1476            T = sum(Tlist.values())/len(Tlist)
1477            if max(Tlist.values()) - T > 1:
1478                return True # temperatures span more than 1 degree, user needs to pick one
1479            return False
1480        elif len(DijTlist) == 1:
1481            return False
1482        elif len(DijTlist) > 1:
1483                # each histogram has different cell lengths, user needs to pick one
1484            return True
1485       
1486    def _CellSelectHist(self,phasenam):
1487        '''Select T value for a phase in a multiblock CIF
1488
1489        :returns: T,h_ranId where T is a temperature (float) or '?' and
1490          h_ranId is the random Id (ranId) for a histogram in the
1491          current phase. This is stored in OverallParms['Controls']['CellHistSelection']
1492        '''
1493        phasedict = self.Phases[phasenam] # pointer to current phase info
1494        Tlist = {}  # histname & T values used for cell w/o Hstrain
1495        DijTlist = {} # hId & T values used for cell w/Hstrain
1496        # scan over histograms used in this phase to determine the best
1497        # data collection T value
1498        for h in phasedict['Histograms']:
1499            if not phasedict['Histograms'][h]['Use']: continue
1500            if 'Flack' in phasedict['Histograms'][h].keys():       #single crystal data
1501                return (300,None)
1502            T = self.Histograms[h]['Sample Parameters']['Temperature']
1503            if np.any(abs(np.array(phasedict['Histograms'][h]['HStrain'][0])) > 1e-8):
1504                DijTlist[h] = T
1505            else:
1506                Tlist[h] = T
1507        if len(Tlist) > 0:
1508            T = sum(Tlist.values())/len(Tlist)
1509            if max(Tlist.values()) - T > 1:
1510                # temperatures span more than 1 degree, user needs to pick one
1511                choices = ["{} (unweighted average)".format(T)]
1512                Ti = [T]
1513                for h in Tlist:
1514                    choices += ["{} (hist {})".format(Tlist[h],h)]
1515                    Ti += [Tlist[h]]                           
1516                msg = 'The cell parameters for phase {} are from\nhistograms with different temperatures.\n\nSelect a T value below'.format(phasenam)
1517                dlg = wx.SingleChoiceDialog(self.G2frame,msg,'Select T',choices)
1518                if dlg.ShowModal() == wx.ID_OK:
1519                    T = Ti[dlg.GetSelection()]
1520                else:
1521                    T = '?'
1522                dlg.Destroy()
1523            return (T,None)
1524        elif len(DijTlist) == 1:
1525            h = list(DijTlist.keys())[0]
1526            h_ranId = self.Histograms[h]['ranId']
1527            return (DijTlist[h],h_ranId)
1528        elif len(DijTlist) > 1:
1529            # each histogram has different cell lengths, user needs to pick one
1530            choices = []
1531            hi = []
1532            for h in DijTlist:
1533                choices += ["{} (hist {})".format(DijTlist[h],h)]
1534                hi += [h]
1535            msg = 'There are {} sets of cell parameters for phase {}\n due to refined Hstrain values.\n\nSelect the histogram to use with the phase form list below'.format(len(DijTlist),phasenam)
1536            dlg = wx.SingleChoiceDialog(self.G2frame,msg,'Select cell',choices)
1537            if dlg.ShowModal() == wx.ID_OK:
1538                h = hi[dlg.GetSelection()] 
1539                h_ranId = self.Histograms[h]['ranId']
1540                T = DijTlist[h]
1541            else:
1542                T = '?'
1543                h_ranId = None
1544            dlg.Destroy()
1545            return (T,h_ranId)
1546        else:
1547            print('Unexpected option in _CellSelectHist for',phasenam)
1548            return ('?',None)
1549
1550    def ShowHstrainCells(self,phasenam,datablockidDict):
1551        '''Displays the unit cell parameters for phases where Dij values create
1552        mutiple sets of lattice parameters. At present there is no way defined for this in
1553        CIF, so local data names are used.
1554        '''
1555        phasedict = self.Phases[phasenam] # pointer to current phase info
1556        Tlist = {}  # histname & T values used for cell w/o Hstrain
1557        DijTlist = {} # hId & T values used for cell w/Hstrain
1558        # scan over histograms used in this phase
1559        for h in phasedict['Histograms']:
1560            if not phasedict['Histograms'][h]['Use']: continue
1561            if np.any(abs(np.array(phasedict['Histograms'][h]['HStrain'][0])) > 1e-8):
1562                DijTlist[h] = self.Histograms[h]['Sample Parameters']['Temperature']
1563            else:
1564                Tlist[h] = self.Histograms[h]['Sample Parameters']['Temperature']
1565        if len(DijTlist) == 0: return
1566        if len(Tlist) + len(DijTlist) < 2: return
1567        SGData = phasedict['General']['SGData']
1568        for i in range(len(G2py3.cellGUIlist)):
1569            if SGData['SGLaue'] in G2py3.cellGUIlist[i][0]:
1570                terms = G2py3.cellGUIlist[i][5] + [6]
1571                break
1572        else:
1573            print('ShowHstrainCells error: Laue class not found',SGData['SGLaue'])
1574            terms = list(range(7))
1575       
1576        WriteCIFitem(self.fp, '\n# cell parameters generated by hydrostatic strain')
1577        WriteCIFitem(self.fp, 'loop_')
1578        WriteCIFitem(self.fp, '\t _gsas_measurement_temperature')
1579        for i in terms:
1580            WriteCIFitem(self.fp, '\t _gsas_cell_'+cellNames[i])
1581        WriteCIFitem(self.fp, '\t _gsas_cell_histogram_blockid')
1582        for h,T in Tlist.items():
1583            pId = phasedict['pId']
1584            hId = self.Histograms[h]['hId']
1585            cellList,cellSig = G2stIO.getCellSU(pId,hId,
1586                                        phasedict['General']['SGData'],
1587                                        self.parmDict,
1588                                        self.OverallParms['Covariance'])
1589            line = '  ' + PutInCol(G2mth.ValEsd(T,-1.),6)
1590            for i in terms:
1591                line += PutInCol(G2mth.ValEsd(cellList[i],cellSig[i]),12)
1592            line += ' ' + datablockidDict[h]
1593            WriteCIFitem(self.fp, line)
1594        for h,T in DijTlist.items():
1595            pId = phasedict['pId']
1596            hId = self.Histograms[h]['hId']
1597            cellList,cellSig = G2stIO.getCellSU(pId,hId,
1598                                        phasedict['General']['SGData'],
1599                                        self.parmDict,
1600                                        self.OverallParms['Covariance'])
1601            line = '  ' + PutInCol(G2mth.ValEsd(T,-1.),6)
1602            for i in terms:
1603                line += PutInCol(G2mth.ValEsd(cellList[i],cellSig[i]),12)
1604            line += ' ' + datablockidDict[h]
1605            WriteCIFitem(self.fp, line)       
1606
1607    def _Exporter(self,event=None,phaseOnly=None,histOnly=None):
1608        '''Basic code to export a CIF. Export can be full or simple, as set by
1609        phaseOnly and histOnly which skips distances & angles, etc.
1610
1611        :param bool phaseOnly: used to export only one phase
1612        :param bool histOnly: used to export only one histogram
1613        '''
1614
1615#***** define functions for export method =======================================
1616        def WriteAudit():
1617            'Write the CIF audit values. Perhaps should be in a single element loop.'
1618            WriteCIFitem(self.fp, '_audit_creation_method',
1619                         'created in GSAS-II')
1620            WriteCIFitem(self.fp, '_audit_creation_date',self.CIFdate)
1621            if self.author:
1622                WriteCIFitem(self.fp, '_audit_author_name',self.author)
1623            WriteCIFitem(self.fp, '_audit_update_record',
1624                         self.CIFdate+'  Initial software-generated CIF')
1625
1626        def WriteOverall(mode=None):
1627            '''Write out overall refinement information.
1628
1629            More could be done here, but this is a good start.
1630            '''
1631            if self.ifPWDR:
1632                WriteCIFitem(self.fp, '_pd_proc_info_datetime', self.CIFdate)
1633                WriteCIFitem(self.fp, '_pd_calc_method', 'Rietveld Refinement')
1634               
1635            #WriteCIFitem(self.fp, '_refine_ls_shift/su_mean',DAT2)
1636            WriteCIFitem(self.fp, '_computing_structure_refinement','GSAS-II (Toby & Von Dreele, J. Appl. Cryst. 46, 544-549, 2013)')
1637            if self.ifHKLF:
1638                controls = self.OverallParms['Controls']
1639                try:
1640                    if controls['F**2']:
1641                        thresh = 'F**2>%.1fu(F**2)'%(controls['UsrReject']['minF/sig'])
1642                    else:
1643                        thresh = 'F>%.1fu(F)'%(controls['UsrReject']['minF/sig'])
1644                    WriteCIFitem(self.fp, '_reflns_threshold_expression', thresh)
1645                except KeyError:
1646                    pass
1647            WriteCIFitem(self.fp, '_refine_ls_matrix_type','full')
1648
1649            if mode == 'seq': return
1650            try:
1651                vars = str(len(self.OverallParms['Covariance']['varyList']))
1652            except:
1653                vars = '?'
1654            WriteCIFitem(self.fp, '_refine_ls_number_parameters',vars)
1655            try:
1656                GOF = G2mth.ValEsd(self.OverallParms['Covariance']['Rvals']['GOF'],-0.009)
1657            except:
1658                GOF = '?'
1659            WriteCIFitem(self.fp, '_refine_ls_goodness_of_fit_all',GOF)
1660            DAT1 = self.OverallParms['Covariance']['Rvals'].get('Max shft/sig',0.0)
1661            if DAT1:
1662                WriteCIFitem(self.fp, '_refine_ls_shift/su_max','%.4f'%DAT1)
1663
1664            # get restraint info
1665            # restraintDict = self.OverallParms.get('Restraints',{})
1666            # for i in  self.OverallParms['Constraints']:
1667            #     print i
1668            #     for j in self.OverallParms['Constraints'][i]:
1669            #         print j
1670            #WriteCIFitem(self.fp, '_refine_ls_number_restraints',TEXT)
1671            # other things to consider reporting
1672            # _refine_ls_number_reflns
1673            # _refine_ls_goodness_of_fit_obs
1674            # _refine_ls_wR_factor_obs
1675            # _refine_ls_restrained_S_all
1676            # _refine_ls_restrained_S_obs
1677
1678            # include an overall profile r-factor, if there is more than one powder histogram
1679            R = '%.5f'%(self.OverallParms['Covariance']['Rvals']['Rwp']/100.)
1680            WriteCIFitem(self.fp, '\n# OVERALL WEIGHTED R-FACTOR')
1681            WriteCIFitem(self.fp, '_refine_ls_wR_factor_obs',R)
1682                # _refine_ls_R_factor_all
1683                # _refine_ls_R_factor_obs
1684            #WriteCIFitem(self.fp, '_refine_ls_matrix_type','userblocks')
1685
1686        def WriteOverallMM(mode=None):
1687            '''Write out overall refinement information.
1688
1689            More could be done here, but this is a good start.
1690            '''
1691            if self.ifPWDR:
1692                WriteCIFitem(self.fp, '_pd_proc_info_datetime', self.CIFdate)
1693                WriteCIFitem(self.fp, '_pd_calc_method', 'Rietveld Refinement')
1694               
1695            #WriteCIFitem(self.fp, '_refine.ls_shift_over_su_mean',DAT2)
1696            WriteCIFitem(self.fp, '_computing_structure_refinement','GSAS-II (Toby & Von Dreele, J. Appl. Cryst. 46, 544-549, 2013)')
1697            if self.ifHKLF:
1698                controls = self.OverallParms['Controls']
1699                try:
1700                    if controls['F**2']:
1701                        thresh = 'F**2>%.1fu(F**2)'%(controls['UsrReject']['minF/sig'])
1702                    else:
1703                        thresh = 'F>%.1fu(F)'%(controls['UsrReject']['minF/sig'])
1704                    WriteCIFitem(self.fp, '_reflns.threshold_expression', thresh)
1705                except KeyError:
1706                    pass
1707            WriteCIFitem(self.fp, '_refine.ls_matrix_type','full')
1708
1709            if mode == 'seq': return
1710            try:
1711                vars = str(len(self.OverallParms['Covariance']['varyList']))
1712            except:
1713                vars = '?'
1714            WriteCIFitem(self.fp, '_refine.ls_number_parameters',vars)
1715            try:
1716                GOF = G2mth.ValEsd(self.OverallParms['Covariance']['Rvals']['GOF'],-0.009)
1717            except:
1718                GOF = '?'
1719            WriteCIFitem(self.fp, '_refine.ls_goodness_of_fit_all',GOF)
1720            DAT1 = self.OverallParms['Covariance']['Rvals'].get('Max shft/sig',0.0)
1721            if DAT1:
1722                WriteCIFitem(self.fp, '_refine.ls_shift_over_su_max','%.4f'%DAT1)
1723
1724            # get restraint info
1725            # restraintDict = self.OverallParms.get('Restraints',{})
1726            # for i in  self.OverallParms['Constraints']:
1727            #     print i
1728            #     for j in self.OverallParms['Constraints'][i]:
1729            #         print j
1730            #WriteCIFitem(self.fp, '_refine_ls_number_restraints',TEXT)
1731            # other things to consider reporting
1732            # _refine_ls_number_reflns
1733            # _refine_ls_goodness_of_fit_obs
1734            # _refine_ls_wR_factor_obs
1735            # _refine_ls_restrained_S_all
1736            # _refine_ls_restrained_S_obs
1737
1738            # include an overall profile r-factor, if there is more than one powder histogram
1739            R = '%.5f'%(self.OverallParms['Covariance']['Rvals']['Rwp']/100.)
1740            WriteCIFitem(self.fp, '\n# OVERALL WEIGHTED R-FACTOR')
1741            WriteCIFitem(self.fp, '_refine.ls_wR_factor_obs',R)
1742
1743        def writeCIFtemplate(G2dict,tmplate,defaultname='',
1744                                 cifKey="CIF_template"):
1745            '''Write out the selected or edited CIF template
1746            An unedited CIF template file is copied, comments intact; an edited
1747            CIF template is written out from PyCifRW which of course strips comments.
1748            In all cases the initial data_ header is stripped (there should only be one!)
1749            '''
1750            CIFobj = G2dict.get(cifKey)
1751            if defaultname:
1752                defaultname = G2obj.StripUnicode(defaultname)
1753                defaultname = re.sub(r'[^a-zA-Z0-9_-]','',defaultname)
1754                defaultname = tmplate + "_" + defaultname + ".cif"
1755            else:
1756                defaultname = ''
1757            templateDefName = 'template_'+tmplate+'.cif'
1758            if not CIFobj: # copying a template
1759                lbl = 'Standard version'
1760                for pth in [os.getcwd()]+sys.path:
1761                    fil = os.path.join(pth,defaultname)
1762                    if os.path.exists(fil) and defaultname: break
1763                else:
1764                    for pth in sys.path:
1765                        fil = os.path.join(pth,templateDefName)
1766                        if os.path.exists(fil): break
1767                    else:
1768                        print(CIFobj+' not found in path!')
1769                        return
1770                fp = open(fil,'r')
1771                txt = fp.read()
1772                fp.close()
1773            elif type(CIFobj) is not list and type(CIFobj) is not tuple:
1774                lbl = 'Saved version'
1775                if not os.path.exists(CIFobj):
1776                    print("Error: requested template file has disappeared: "+CIFobj)
1777                    return
1778                fp = open(CIFobj,'r')
1779                txt = fp.read()
1780                fp.close()
1781            else:
1782                lbl = 'Project-specific version'
1783                txt = dict2CIF(CIFobj[0],CIFobj[1]).WriteOut()
1784            # remove the PyCifRW header, if present
1785            #if txt.find('PyCifRW') > -1 and txt.find('data_') > -1:
1786            pre = txt.index("data_")
1787            restofline = txt.index("\n",pre)
1788            name = txt[pre+5:restofline]
1789            txt = "\n# {} of {} template follows{}".format(
1790                lbl, name, txt[restofline:])
1791            #txt = txt.replace('data_','#')
1792            WriteCIFitem(self.fp, txt)
1793
1794        def FormatSH(phasenam):
1795            'Format a full spherical harmonics texture description as a string'
1796            phasedict = self.Phases[phasenam] # pointer to current phase info
1797            pfx = str(phasedict['pId'])+'::'
1798            s = ""
1799            textureData = phasedict['General']['SH Texture']
1800            if textureData.get('Order'):
1801                s += "Spherical Harmonics correction. Order = "+str(textureData['Order'])
1802                s += " Model: " + str(textureData['Model']) + "\n    Orientation angles: "
1803                for name in ['omega','chi','phi']:
1804                    aname = pfx+'SH '+name
1805                    s += name + " = "
1806                    sig = self.sigDict.get(aname,-0.09)
1807                    s += G2mth.ValEsd(self.parmDict[aname],sig)
1808                    s += "; "
1809                s += "\n"
1810                s1 = "    Coefficients:  "
1811                for name in textureData['SH Coeff'][1]:
1812                    aname = pfx+name
1813                    if len(s1) > 60:
1814                        s += s1 + "\n"
1815                        s1 = "    "
1816                    s1 += aname + ' = '
1817                    sig = self.sigDict.get(aname,-0.0009)
1818                    s1 += G2mth.ValEsd(self.parmDict[aname],sig)
1819                    s1 += "; "
1820                s += s1
1821            return s
1822
1823        def FormatHAPpo(phasenam):
1824            '''return the March-Dollase/SH correction for every
1825            histogram in the current phase formatted into a
1826            character string
1827            '''
1828            phasedict = self.Phases[phasenam] # pointer to current phase info
1829            s = ''
1830            for histogram in sorted(phasedict['Histograms']):
1831                if histogram.startswith("HKLF"): continue # powder only
1832                if not self.Phases[phasenam]['Histograms'][histogram]['Use']: continue
1833                Histogram = self.Histograms.get(histogram)
1834                if not Histogram: continue
1835                hapData = phasedict['Histograms'][histogram]
1836                if hapData['Pref.Ori.'][0] == 'MD':
1837                    aname = str(phasedict['pId'])+':'+str(Histogram['hId'])+':MD'
1838                    if self.parmDict.get(aname,1.0) != 1.0: continue
1839                    sig = self.sigDict.get(aname,-0.009)
1840                    if s != "": s += '\n'
1841                    s += 'March-Dollase correction'
1842                    if len(self.powderDict) > 1:
1843                        s += ', histogram '+str(Histogram['hId']+1)
1844                    s += ' coef. = ' + G2mth.ValEsd(self.parmDict[aname],sig)
1845                    s += ' axis = ' + str(hapData['Pref.Ori.'][3])
1846                else: # must be SH
1847                    if s != "": s += '\n'
1848                    s += 'Simple spherical harmonic correction'
1849                    if len(self.powderDict) > 1:
1850                        s += ', histogram '+str(Histogram['hId']+1)
1851                    s += ' Order = '+str(hapData['Pref.Ori.'][4])+'\n'
1852                    s1 = "    Coefficients:  "
1853                    for item in hapData['Pref.Ori.'][5]:
1854                        aname = str(phasedict['pId'])+':'+str(Histogram['hId'])+':'+item
1855                        if len(s1) > 60:
1856                            s += s1 + "\n"
1857                            s1 = "    "
1858                        s1 += aname + ' = '
1859                        sig = self.sigDict.get(aname,-0.0009)
1860                        s1 += G2mth.ValEsd(self.parmDict[aname],sig)
1861                        s1 += "; "
1862                    s += s1
1863            return s
1864
1865        def FormatBackground(bkg,hId):
1866            '''Display the Background information as a descriptive text string.
1867
1868            TODO: this needs to be expanded to show the diffuse peak and
1869            Debye term information as well. (Bob)
1870
1871            :returns: the text description (str)
1872            '''
1873            hfx = ':'+str(hId)+':'
1874            fxn, bkgdict = bkg
1875            terms = fxn[2]
1876            txt = 'Background function: "'+fxn[0]+'" function with '+str(terms)+' terms:\n'
1877            l = "    "
1878            for i,v in enumerate(fxn[3:]):
1879                name = '%sBack;%d'%(hfx,i)
1880                sig = self.sigDict.get(name,-0.009)
1881                if len(l) > 60:
1882                    txt += l + '\n'
1883                    l = '    '
1884                l += G2mth.ValEsd(v,sig)+', '
1885            txt += l
1886            if bkgdict['nDebye']:
1887                txt += '\n  Background Debye function parameters: A, R, U:'
1888                names = ['A;','R;','U;']
1889                for i in range(bkgdict['nDebye']):
1890                    txt += '\n    '
1891                    for j in range(3):
1892                        name = hfx+'Debye'+names[j]+str(i)
1893                        sig = self.sigDict.get(name,-0.009)
1894                        txt += G2mth.ValEsd(bkgdict['debyeTerms'][i][2*j],sig)+', '
1895            if bkgdict['nPeaks']:
1896                txt += '\n  Background peak parameters: pos, int, sig, gam:'
1897                names = ['pos;','int;','sig;','gam;']
1898                for i in range(bkgdict['nPeaks']):
1899                    txt += '\n    '
1900                    for j in range(4):
1901                        name = hfx+'BkPk'+names[j]+str(i)
1902                        sig = self.sigDict.get(name,-0.009)
1903                        txt += G2mth.ValEsd(bkgdict['peaksList'][i][2*j],sig)+', '
1904            return txt
1905
1906        def FormatInstProfile(instparmdict,hId):
1907            '''Format the instrumental profile parameters with a
1908            string description. Will only be called on PWDR histograms
1909            '''
1910            s = ''
1911            inst = instparmdict[0]
1912            hfx = ':'+str(hId)+':'
1913            if 'C' in inst['Type'][0]:
1914                s = 'Finger-Cox-Jephcoat function parameters U, V, W, X, Y, SH/L:\n'
1915                s += '  peak variance(Gauss) = Utan(Th)^2^+Vtan(Th)+W:\n'
1916                s += '  peak HW(Lorentz) = X/cos(Th)+Ytan(Th); SH/L = S/L+H/L\n'
1917                s += '  U, V, W in (centideg)^2^, X & Y in centideg\n    '
1918                for item in ['U','V','W','X','Y','SH/L']:
1919                    name = hfx+item
1920                    sig = self.sigDict.get(name,-0.009)
1921                    s += G2mth.ValEsd(inst[item][1],sig)+', '
1922            elif 'T' in inst['Type'][0]:    #to be tested after TOF Rietveld done
1923                s = 'Von Dreele-Jorgenson-Windsor function parameters\n'+ \
1924                    '   alpha, beta-0, beta-1, beta-q, sig-0, sig-1, sig-2, sig-q, X, Y:\n    '
1925                for item in ['alpha','beta-0','beta-1','beta-q','sig-0','sig-1','sig-2','sig-q','X','Y']:
1926                    name = hfx+item
1927                    sig = self.sigDict.get(name,-0.009)
1928                    s += G2mth.ValEsd(inst[item][1],sig)+', '
1929            return s
1930
1931        def FormatPhaseProfile(phasenam,hist=''):
1932            '''Format the phase-related profile parameters (size/strain)
1933            with a string description.
1934            return an empty string or None if there are no
1935            powder histograms for this phase.
1936            '''
1937            s = ''
1938            phasedict = self.Phases[phasenam] # pointer to current phase info
1939            if hist:
1940                parmDict = self.seqData[hist]['parmDict']
1941                sigDict = dict(zip(self.seqData[hist]['varyList'],self.seqData[hist]['sig']))
1942            else:
1943                parmDict = self.parmDict
1944                sigDict = self.sigDict
1945           
1946            SGData = phasedict['General'] ['SGData']
1947            for histogram in sorted(phasedict['Histograms']):
1948                if hist is not None and hist != histogram: continue
1949                if histogram.startswith("HKLF"): continue # powder only
1950                Histogram = self.Histograms.get(histogram)
1951                if not Histogram: continue
1952                hapData = phasedict['Histograms'][histogram]
1953                pId = phasedict['pId']
1954                hId = Histogram['hId']
1955                phfx = '%d:%d:'%(pId,hId)
1956                size = hapData['Size']
1957                mustrain = hapData['Mustrain']
1958                hstrain = hapData['HStrain']
1959                if s: s += '\n'
1960                if len(self.powderDict) > 1: # if one histogram, no ambiguity
1961                    s += '  Parameters for histogram #{:} {:} & phase {:}\n'.format(
1962                        str(hId),str(histogram),phasenam)
1963                s += '  Crystallite size in microns with "%s" model:\n  '%(size[0])
1964                names = ['Size;i','Size;mx']
1965                if 'uniax' in size[0]:
1966                    names = ['Size;i','Size;a','Size;mx']
1967                    s += 'anisotropic axis is %s\n  '%(str(size[3]))
1968                    s += 'parameters: equatorial size, axial size, G/L mix\n    '
1969                    for i,item in enumerate(names):
1970                        name = phfx+item
1971                        val = parmDict.get(name,size[1][i])
1972                        sig = sigDict.get(name,-0.009)
1973                        s += G2mth.ValEsd(val,sig)+', '
1974                elif 'ellip' in size[0]:
1975                    s += 'parameters: S11, S22, S33, S12, S13, S23, G/L mix\n    '
1976                    for i in range(6):
1977                        name = phfx+'Size:'+str(i)
1978                        val = parmDict.get(name,size[4][i])
1979                        sig = sigDict.get(name,-0.009)
1980                        s += G2mth.ValEsd(val,sig)+', '
1981                    sig = sigDict.get(phfx+'Size;mx',-0.009)
1982                    s += G2mth.ValEsd(size[1][2],sig)+', '
1983                else:       #isotropic
1984                    s += 'parameters: Size, G/L mix\n    '
1985                    i = 0
1986                    for item in names:
1987                        name = phfx+item
1988                        val = parmDict.get(name,size[1][i])
1989                        sig = sigDict.get(name,-0.009)
1990                        s += G2mth.ValEsd(val,sig)+', '
1991                        i = 2    #skip the aniso value
1992                s += '\n  Microstrain, "%s" model (10^6^ * delta Q/Q)\n  '%(mustrain[0])
1993                names = ['Mustrain;i','Mustrain;mx']
1994                if 'uniax' in mustrain[0]:
1995                    names = ['Mustrain;i','Mustrain;a','Mustrain;mx']
1996                    s += 'anisotropic axis is %s\n  '%(str(size[3]))
1997                    s += 'parameters: equatorial mustrain, axial mustrain, G/L mix\n    '
1998                    for i,item in enumerate(names):
1999                        name = phfx+item
2000                        val = parmDict.get(name,mustrain[1][i])
2001                        sig = sigDict.get(name,-0.009)
2002                        s += G2mth.ValEsd(val,sig)+', '
2003                elif 'general' in mustrain[0]:
2004                    names = 'parameters: '
2005                    for i,name in enumerate(G2spc.MustrainNames(SGData)):
2006                        names += name+', '
2007                        if i == 9:
2008                            names += '\n  '
2009                    names += 'G/L mix\n    '
2010                    s += names
2011                    txt = ''
2012                    for i in range(len(mustrain[4])):
2013                        name = phfx+'Mustrain:'+str(i)
2014                        val = parmDict.get(name,mustrain[4][i])
2015                        sig = sigDict.get(name,-0.009)
2016                        if len(txt) > 60:
2017                            s += txt+'\n    '
2018                            txt = ''
2019                        txt += G2mth.ValEsd(val,sig)+', '
2020                    s += txt
2021                    name = phfx+'Mustrain;mx'
2022                    val = parmDict.get(name,mustrain[1][2])
2023                    sig = sigDict.get(name,-0.009)
2024                    s += G2mth.ValEsd(val,sig)+', '
2025
2026                else:       #isotropic
2027                    s += '  parameters: Mustrain, G/L mix\n    '
2028                    i = 0
2029                    for item in names:
2030                        name = phfx+item
2031                        val = parmDict.get(name,mustrain[1][i])
2032                        sig = sigDict.get(name,-0.009)
2033                        s += G2mth.ValEsd(val,sig)+', '
2034                        i = 2    #skip the aniso value
2035                s1 = \n  Macrostrain parameters: '
2036                names = G2spc.HStrainNames(SGData)
2037                for name in names:
2038                    s1 += name+', '
2039                s1 += '\n    '
2040                macrostrain = False
2041                for i in range(len(names)):
2042                    name = phfx+names[i]
2043                    val = parmDict.get(name,hstrain[0][i])
2044                    sig = sigDict.get(name,-0.000009)
2045                    s1 += G2mth.ValEsd(val,sig)+', '
2046                    if hstrain[0][i]: macrostrain = True
2047                if macrostrain:
2048                    s += s1 + '\n'
2049                    # show revised lattice parameters here someday
2050                else:
2051                    s += '\n'
2052            return s
2053
2054        def MakeUniqueLabel(lbl,labellist):
2055            'Make sure that every atom label is unique'
2056            lbl = lbl.strip()
2057            if not lbl: # deal with a blank label
2058                lbl = 'A_1'
2059            if lbl not in labellist:
2060                labellist.append(lbl)
2061                return lbl
2062            i = 1
2063            prefix = lbl
2064            if '_' in lbl:
2065                prefix = lbl[:lbl.rfind('_')]
2066                suffix = lbl[lbl.rfind('_')+1:]
2067                try:
2068                    i = int(suffix)+1
2069                except:
2070                    pass
2071            while prefix+'_'+str(i) in labellist:
2072                i += 1
2073            else:
2074                lbl = prefix+'_'+str(i)
2075                labellist.append(lbl)
2076
2077        def WriteDistances(phasenam):
2078            '''Report bond distances and angles for the CIF
2079
2080            Note that _geom_*_symmetry_* fields are values of form
2081            n_klm where n is the symmetry operation in SymOpList (counted
2082            starting with 1) and (k-5, l-5, m-5) are translations to add
2083            to (x,y,z). See
2084            http://www.iucr.org/__data/iucr/cifdic_html/1/cif_core.dic/Igeom_angle_site_symmetry_.html
2085
2086            TODO: need a method to select publication flags for distances/angles
2087            '''
2088            phasedict = self.Phases[phasenam] # pointer to current phase info
2089            Atoms = phasedict['Atoms']
2090            generalData = phasedict['General']
2091            # create a dict for storing Pub flag for bonds/angles, if needed
2092            if phasedict['General'].get("DisAglHideFlag") is None:
2093                phasedict['General']["DisAglHideFlag"] = {}
2094            DisAngSel = phasedict['General']["DisAglHideFlag"]
2095            cx,ct,cs,cia = phasedict['General']['AtomPtrs']
2096            cn = ct-1
2097            fpfx = str(phasedict['pId'])+'::Afrac:'
2098            cfrac = cx+3
2099            DisAglData = {}
2100            # create a list of atoms, but skip atoms with zero occupancy
2101            xyz = []
2102            fpfx = str(phasedict['pId'])+'::Afrac:'
2103            for i,atom in enumerate(Atoms):
2104                if self.parmDict.get(fpfx+str(i),atom[cfrac]) == 0.0: continue
2105                xyz.append([i,]+atom[cn:cn+2]+atom[cx:cx+3])
2106            if 'DisAglCtls' not in generalData:
2107                # should not happen, since DisAglDialog should be called
2108                # for all phases before getting here
2109                dlg = G2G.DisAglDialog(
2110                    self.G2frame,
2111                    {},
2112                    generalData)
2113                if dlg.ShowModal() == wx.ID_OK:
2114                    generalData['DisAglCtls'] = dlg.GetData()
2115                else:
2116                    dlg.Destroy()
2117                    return
2118                dlg.Destroy()
2119            DisAglData['OrigAtoms'] = xyz
2120            DisAglData['TargAtoms'] = xyz
2121            SymOpList,offsetList,symOpList,G2oprList,G2opcodes = G2spc.AllOps(
2122                generalData['SGData'])
2123
2124#            xpandSGdata = generalData['SGData'].copy()
2125#            xpandSGdata.update({'SGOps':symOpList,
2126#                                'SGInv':False,
2127#                                'SGLatt':'P',
2128#                                'SGCen':np.array([[0, 0, 0]]),})
2129#            DisAglData['SGData'] = xpandSGdata
2130            DisAglData['SGData'] = generalData['SGData'].copy()
2131
2132            DisAglData['Cell'] = generalData['Cell'][1:] #+ volume
2133            if 'pId' in phasedict:
2134                DisAglData['pId'] = phasedict['pId']
2135                DisAglData['covData'] = self.OverallParms['Covariance']
2136            try:
2137                AtomLabels,DistArray,AngArray = G2stMn.RetDistAngle(
2138                    generalData['DisAglCtls'],
2139                    DisAglData)
2140            except KeyError:        # inside DistAngle for missing atom types in DisAglCtls
2141                print(u'**** ERROR computing distances & angles for phase {} ****\nresetting to default values'.format(phasenam))
2142                data = generalData['DisAglCtls'] = {}
2143                data['Name'] = generalData['Name']
2144                data['Factors'] = [0.85,0.85]
2145                data['AtomTypes'] = generalData['AtomTypes']
2146                data['BondRadii'] = generalData['BondRadii'][:]
2147                data['AngleRadii'] = generalData['AngleRadii'][:]
2148                try:
2149                    AtomLabels,DistArray,AngArray = G2stMn.RetDistAngle(
2150                        generalData['DisAglCtls'],
2151                        DisAglData)
2152                except:
2153                    print('Reset failed. To fix this, use the Reset button in the "edit distance/angle menu" for this phase')
2154                    return
2155
2156            # loop over interatomic distances for this phase
2157            WriteCIFitem(self.fp, '\n# MOLECULAR GEOMETRY')
2158            First = True
2159            for i in sorted(AtomLabels.keys()):
2160                Dist = DistArray[i]
2161                for D in Dist:
2162                    line = '  '+PutInCol(AtomLabels[i],6)+PutInCol(AtomLabels[D[0]],6)
2163                    sig = D[4]
2164                    if sig == 0: sig = -0.00009
2165                    line += PutInCol(G2mth.ValEsd(D[3],sig,True),10)
2166                    line += "  1_555 "
2167                    symopNum = G2opcodes.index(D[2])
2168                    line += " {:3d}_".format(symopNum+1)
2169                    for d,o in zip(D[1],offsetList[symopNum]):
2170                        line += "{:1d}".format(d-o+5)
2171                    if DisAngSel.get((i,tuple(D[0:3]))):
2172                        line += " no"
2173                    else:
2174                        line += " yes"
2175                    if First:
2176                        First = False
2177                        WriteCIFitem(self.fp, 'loop_' +
2178                         '\n   _geom_bond_atom_site_label_1' +
2179                         '\n   _geom_bond_atom_site_label_2' +
2180                         '\n   _geom_bond_distance' +
2181                         '\n   _geom_bond_site_symmetry_1' +
2182                         '\n   _geom_bond_site_symmetry_2' +
2183                         '\n   _geom_bond_publ_flag')
2184                    WriteCIFitem(self.fp, line)
2185
2186            # loop over interatomic angles for this phase
2187            First = True
2188            for i in sorted(AtomLabels.keys()):
2189                Dist = DistArray[i]
2190                for k,j,tup in AngArray[i]:
2191                    Dj = Dist[j]
2192                    Dk = Dist[k]
2193                    line = '  '+PutInCol(AtomLabels[Dj[0]],6)+PutInCol(AtomLabels[i],6)+PutInCol(AtomLabels[Dk[0]],6)
2194                    sig = tup[1]
2195                    if sig == 0: sig = -0.009
2196                    line += PutInCol(G2mth.ValEsd(tup[0],sig,True),10)
2197                    line += " {:3d}_".format(G2opcodes.index(Dj[2])+1)
2198                    for d in Dj[1]:
2199                        line += "{:1d}".format(d+5)
2200                    line += "  1_555 "
2201                    line += " {:3d}_".format(G2opcodes.index(Dk[2])+1)
2202                    for d in Dk[1]:
2203                        line += "{:1d}".format(d+5)
2204                    key = (tuple(Dk[0:3]),i,tuple(Dj[0:3]))
2205                    if DisAngSel.get(key):
2206                        line += " no"
2207                    else:
2208                        line += " yes"
2209                    if First:
2210                        First = False
2211                        WriteCIFitem(self.fp, '\nloop_' +
2212                         '\n   _geom_angle_atom_site_label_1' +
2213                         '\n   _geom_angle_atom_site_label_2' +
2214                         '\n   _geom_angle_atom_site_label_3' +
2215                         '\n   _geom_angle' +
2216                         '\n   _geom_angle_site_symmetry_1' +
2217                         '\n   _geom_angle_site_symmetry_2' +
2218                         '\n   _geom_angle_site_symmetry_3' +
2219                         '\n   _geom_angle_publ_flag')
2220                    WriteCIFitem(self.fp, line)
2221
2222
2223        def WriteSeqDistances(phasenam,histname,phasedict,cellList,seqData):
2224            '''Report bond distances and angles for the CIF from a Sequential fit
2225
2226            Note that _geom_*_symmetry_* fields are values of form
2227            n_klm where n is the symmetry operation in SymOpList (counted
2228            starting with 1) and (k-5, l-5, m-5) are translations to add
2229            to (x,y,z). See
2230            http://www.iucr.org/__data/iucr/cifdic_html/1/cif_core.dic/Igeom_angle_site_symmetry_.html
2231
2232            TODO: this is based on WriteDistances and could likely be merged with that
2233            without too much work. Note also that G2stMn.RetDistAngle is pretty slow for
2234            sequential fits, since it is called so many times.
2235            '''
2236            Atoms = phasedict['Atoms']
2237            generalData = phasedict['General']
2238            parmDict = seqData[histname]['parmDict']
2239#            sigDict = dict(zip(seqData[hist]['varyList'],seqData[hist]['sig']))
2240            # create a dict for storing Pub flag for bonds/angles, if needed
2241            if phasedict['General'].get("DisAglHideFlag") is None:
2242                phasedict['General']["DisAglHideFlag"] = {}
2243            DisAngSel = phasedict['General']["DisAglHideFlag"]
2244            cx,ct,cs,cia = phasedict['General']['AtomPtrs']
2245            cn = ct-1
2246#            fpfx = str(phasedict['pId'])+'::Afrac:'
2247            cfrac = cx+3
2248            DisAglData = {}
2249            # create a list of atoms, but skip atoms with zero occupancy
2250            xyz = []
2251            fpfx = str(phasedict['pId'])+'::Afrac:'
2252            for i,atom in enumerate(Atoms):
2253                if parmDict.get(fpfx+str(i),atom[cfrac]) == 0.0: continue
2254                thisatom = [i] + atom[cn:cn+2]
2255                for j,lab in enumerate(['x','y','z']):
2256                    xyzkey = str(phasedict['pId'])+'::A'+ lab + ':' +str(i)
2257                    thisatom.append(parmDict.get(xyzkey,atom[cx+j]))
2258                xyz.append(thisatom)
2259            DisAglData['OrigAtoms'] = xyz
2260            DisAglData['TargAtoms'] = xyz
2261            SymOpList,offsetList,symOpList,G2oprList,G2opcodes = G2spc.AllOps(
2262                generalData['SGData'])
2263
2264#            xpandSGdata = generalData['SGData'].copy()
2265#            xpandSGdata.update({'SGOps':symOpList,
2266#                                'SGInv':False,
2267#                                'SGLatt':'P',
2268#                                'SGCen':np.array([[0, 0, 0]]),})
2269#            DisAglData['SGData'] = xpandSGdata
2270            DisAglData['SGData'] = generalData['SGData'].copy()
2271
2272            DisAglData['Cell'] = cellList  #+ volume
2273            if 'pId' in phasedict:
2274                DisAglData['pId'] = phasedict['pId']
2275                DisAglData['covData'] = seqData[histname]
2276                # self.OverallParms['Covariance']
2277            try:
2278                AtomLabels,DistArray,AngArray = G2stMn.RetDistAngle(
2279                    generalData['DisAglCtls'],
2280                    DisAglData)
2281            except KeyError:        # inside DistAngle for missing atom types in DisAglCtls
2282                print(u'**** ERROR computing distances & angles for phase {} ****\nresetting to default values'.format(phasenam))
2283                data = generalData['DisAglCtls'] = {}
2284                data['Name'] = generalData['Name']
2285                data['Factors'] = [0.85,0.85]
2286                data['AtomTypes'] = generalData['AtomTypes']
2287                data['BondRadii'] = generalData['BondRadii'][:]
2288                data['AngleRadii'] = generalData['AngleRadii'][:]
2289                try:
2290                    AtomLabels,DistArray,AngArray = G2stMn.RetDistAngle(
2291                        generalData['DisAglCtls'],
2292                        DisAglData)
2293                except:
2294                    print('Reset failed. To fix this, use the Reset button in the "edit distance/angle menu" for this phase')
2295                    return
2296
2297            # loop over interatomic distances for this phase
2298            WriteCIFitem(self.fp, '\n# MOLECULAR GEOMETRY')
2299            First = True
2300            for i in sorted(AtomLabels.keys()):
2301                Dist = DistArray[i]
2302                for D in Dist:
2303                    line = '  '+PutInCol(AtomLabels[i],6)+PutInCol(AtomLabels[D[0]],6)
2304                    sig = D[4]
2305                    if sig == 0: sig = -0.00009
2306                    line += PutInCol(G2mth.ValEsd(D[3],sig,True),10)
2307                    line += "  1_555 "
2308                    symopNum = G2opcodes.index(D[2])
2309                    line += " {:3d}_".format(symopNum+1)
2310                    for d,o in zip(D[1],offsetList[symopNum]):
2311                        line += "{:1d}".format(d-o+5)
2312                    if DisAngSel.get((i,tuple(D[0:3]))):
2313                        line += " no"
2314                    else:
2315                        line += " yes"
2316                    if First:
2317                        First = False
2318                        WriteCIFitem(self.fp, 'loop_' +
2319                         '\n   _geom_bond_atom_site_label_1' +
2320                         '\n   _geom_bond_atom_site_label_2' +
2321                         '\n   _geom_bond_distance' +
2322                         '\n   _geom_bond_site_symmetry_1' +
2323                         '\n   _geom_bond_site_symmetry_2' +
2324                         '\n   _geom_bond_publ_flag')
2325                    WriteCIFitem(self.fp, line)
2326
2327            # loop over interatomic angles for this phase
2328            First = True
2329            for i in sorted(AtomLabels.keys()):
2330                Dist = DistArray[i]
2331                for k,j,tup in AngArray[i]:
2332                    Dj = Dist[j]
2333                    Dk = Dist[k]
2334                    line = '  '+PutInCol(AtomLabels[Dj[0]],6)+PutInCol(AtomLabels[i],6)+PutInCol(AtomLabels[Dk[0]],6)
2335                    sig = tup[1]
2336                    if sig == 0: sig = -0.009
2337                    line += PutInCol(G2mth.ValEsd(tup[0],sig,True),10)
2338                    line += " {:3d}_".format(G2opcodes.index(Dj[2])+1)
2339                    for d in Dj[1]:
2340                        line += "{:1d}".format(d+5)
2341                    line += "  1_555 "
2342                    line += " {:3d}_".format(G2opcodes.index(Dk[2])+1)
2343                    for d in Dk[1]:
2344                        line += "{:1d}".format(d+5)
2345                    key = (tuple(Dk[0:3]),i,tuple(Dj[0:3]))
2346                    if DisAngSel.get(key):
2347                        line += " no"
2348                    else:
2349                        line += " yes"
2350                    if First:
2351                        First = False
2352                        WriteCIFitem(self.fp, '\nloop_' +
2353                         '\n   _geom_angle_atom_site_label_1' +
2354                         '\n   _geom_angle_atom_site_label_2' +
2355                         '\n   _geom_angle_atom_site_label_3' +
2356                         '\n   _geom_angle' +
2357                         '\n   _geom_angle_site_symmetry_1' +
2358                         '\n   _geom_angle_site_symmetry_2' +
2359                         '\n   _geom_angle_site_symmetry_3' +
2360                         '\n   _geom_angle_publ_flag')
2361                    WriteCIFitem(self.fp, line)
2362
2363        def WriteSeqOverallPhaseInfo(phasenam,histblk):
2364            'Write out the phase information for the selected phase for the overall block in a sequential fit'
2365            WriteCIFitem(self.fp, '# overall phase info for '+str(phasenam) + ' follows')
2366            phasedict = self.Phases[phasenam] # pointer to current phase info
2367            WriteCIFitem(self.fp, '_pd_phase_name', phasenam)
2368
2369            WriteCIFitem(self.fp, '_symmetry_cell_setting',
2370                         phasedict['General']['SGData']['SGSys'])
2371
2372              # moved to WriteSeqPhaseVals()
2373#             if phasedict['General']['Type'] in ['nuclear','macromolecular']:
2374#                 spacegroup = phasedict['General']['SGData']['SpGrp'].strip()
2375#                 # regularize capitalization and remove trailing H/R
2376#                 spacegroup = spacegroup[0].upper() + spacegroup[1:].lower().rstrip('rh ')
2377#                 WriteCIFitem(self.fp, '_symmetry_space_group_name_H-M',spacegroup)
2378   
2379#                 # generate symmetry operations including centering and center of symmetry
2380#                 SymOpList,offsetList,symOpList,G2oprList,G2opcodes = G2spc.AllOps(
2381#                     phasedict['General']['SGData'])
2382#                 WriteCIFitem(self.fp, 'loop_\n    _space_group_symop_id\n    _space_group_symop_operation_xyz')
2383#                 for i,op in enumerate(SymOpList,start=1):
2384#                     WriteCIFitem(self.fp, '   {:3d}  {:}'.format(i,op.lower()))
2385#             elif phasedict['General']['Type'] == 'magnetic':
2386#                 parentSpGrp = phasedict['General']['SGData']['SpGrp'].strip()
2387#                 parentSpGrp = parentSpGrp[0].upper() + parentSpGrp[1:].lower().rstrip('rh ')
2388#                 WriteCIFitem(self.fp, '_parent_space_group.name_H-M_alt',parentSpGrp)
2389# #                [Trans,Uvec,Vvec] = phasedict['General']['SGData']['fromParent']         #save these
2390#                 spacegroup = phasedict['General']['SGData']['MagSpGrp'].strip()
2391#                 spacegroup = spacegroup[0].upper() + spacegroup[1:].lower().rstrip('rh ')
2392#                 WriteCIFitem(self.fp, '_space_group_magn.name_BNS',spacegroup)
2393#                 WriteCIFitem(self.fp, '_space_group.magn_point_group',phasedict['General']['SGData']['MagPtGp'])
2394
2395#                 # generate symmetry operations including centering and center of symmetry
2396#                 SymOpList,offsetList,symOpList,G2oprList,G2opcodes = G2spc.AllOps(
2397#                     phasedict['General']['SGData'])
2398#                 SpnFlp = phasedict['General']['SGData']['SpnFlp']
2399#                 WriteCIFitem(self.fp, 'loop_\n    _space_group_symop_magn_operation.id\n    _space_group_symop_magn_operation.xyz')
2400#                 for i,op in enumerate(SymOpList,start=1):
2401#                     if SpnFlp[i-1] >0:
2402#                         opr = op.lower()+',+1'
2403#                     else:
2404#                         opr = op.lower()+',-1'
2405#                     WriteCIFitem(self.fp, '   {:3d}  {:}'.format(i,opr))
2406                   
2407            lam = None
2408            if 'X' in histblk['Instrument Parameters'][0]['Type'][0]:
2409                for k in ('Lam','Lam1'):
2410                    if k in histblk['Instrument Parameters'][0]:
2411                        lam = histblk['Instrument Parameters'][0][k][0]
2412                        break
2413            keV = None
2414            if lam: keV = 12.397639/lam
2415            # report cell contents                       
2416            WriteComposition(self.fp, self.Phases[phasenam], phasenam, self.parmDict, False, keV)
2417       
2418        def WriteSeqPhaseVals(phasenam,phasedict,pId,histname):
2419            'Write out the phase information for the selected phase'
2420            WriteCIFitem(self.fp, '_pd_phase_name', phasenam)
2421            cellList,cellSig = getCellwStrain(phasedict,self.seqData,pId,histname)
2422            T = self.Histograms[histname]['Sample Parameters']['Temperature']
2423            try:
2424                T = G2mth.ValEsd(T,-1.0)
2425            except:
2426                pass
2427            WriteCIFitem(self.fp, '_symmetry_cell_setting',
2428                         phasedict['General']['SGData']['SGSys'])
2429
2430            # generate symmetry operations including centering and center of symmetry
2431            # note that this would be better in WriteSeqOverallPhaseInfo() so there could
2432            # be only one copy per phase
2433            if phasedict['General']['Type'] in ['nuclear','macromolecular']:
2434                spacegroup = phasedict['General']['SGData']['SpGrp'].strip()
2435                # regularize capitalization and remove trailing H/R
2436                spacegroup = spacegroup[0].upper() + spacegroup[1:].lower().rstrip('rh ')
2437                WriteCIFitem(self.fp, '_symmetry_space_group_name_H-M',spacegroup)
2438   
2439                # generate symmetry operations including centering and center of symmetry
2440                SymOpList,offsetList,symOpList,G2oprList,G2opcodes = G2spc.AllOps(
2441                    phasedict['General']['SGData'])
2442                WriteCIFitem(self.fp, 'loop_\n    _space_group_symop_id\n    _space_group_symop_operation_xyz')
2443                for i,op in enumerate(SymOpList,start=1):
2444                    WriteCIFitem(self.fp, '   {:3d}  {:}'.format(i,op.lower()))
2445            elif phasedict['General']['Type'] == 'magnetic':
2446                parentSpGrp = phasedict['General']['SGData']['SpGrp'].strip()
2447                parentSpGrp = parentSpGrp[0].upper() + parentSpGrp[1:].lower().rstrip('rh ')
2448                WriteCIFitem(self.fp, '_parent_space_group.name_H-M_alt',parentSpGrp)
2449#                [Trans,Uvec,Vvec] = phasedict['General']['SGData']['fromParent']         #save these
2450                spacegroup = phasedict['General']['SGData']['MagSpGrp'].strip()
2451                spacegroup = spacegroup[0].upper() + spacegroup[1:].lower().rstrip('rh ')
2452                WriteCIFitem(self.fp, '_space_group_magn.name_BNS',spacegroup)
2453                WriteCIFitem(self.fp, '_space_group.magn_point_group',phasedict['General']['SGData']['MagPtGp'])
2454
2455                # generate symmetry operations including centering and center of symmetry
2456                SymOpList,offsetList,symOpList,G2oprList,G2opcodes = G2spc.AllOps(
2457                    phasedict['General']['SGData'])
2458                SpnFlp = phasedict['General']['SGData']['SpnFlp']
2459                WriteCIFitem(self.fp, 'loop_\n    _space_group_symop_magn_operation.id\n    _space_group_symop_magn_operation.xyz')
2460                for i,op in enumerate(SymOpList,start=1):
2461                    if SpnFlp[i-1] >0:
2462                        opr = op.lower()+',+1'
2463                    else:
2464                        opr = op.lower()+',-1'
2465                    WriteCIFitem(self.fp, '   {:3d}  {:}'.format(i,opr))
2466
2467            WriteCIFitem(self.fp,"_cell_measurement_temperature",T)
2468            defsigL = 3*[-0.00001] + 3*[-0.001] + [-0.01] # significance to use when no sigma
2469            prevsig = 0
2470            for lbl,defsig,val,sig in zip(cellNames,defsigL,cellList,cellSig):
2471                if sig:
2472                    txt = G2mth.ValEsd(val,sig)
2473                    prevsig = -sig # use this as the significance for next value
2474                else:
2475                    txt = G2mth.ValEsd(val,min(defsig,prevsig),True)
2476                WriteCIFitem(self.fp, '_cell_'+lbl,txt)
2477
2478            mass = G2mth.getMass(phasedict['General'])
2479            Volume = cellList[6]
2480            density = mass/(0.6022137*Volume)
2481            WriteCIFitem(self.fp, '_exptl_crystal_density_diffrn',
2482                    G2mth.ValEsd(density,-0.001))
2483
2484            # report atom params
2485            if phasedict['General']['Type'] in ['nuclear','macromolecular']:        #this needs macromolecular variant, etc!
2486                WriteSeqAtomsNuclear(self.fp, cellList, phasedict, phasenam, histname, 
2487                                         self.seqData, self.OverallParms['Rigid bodies'])
2488            else:
2489                print("Warning: no export for sequential "+str(phasedict['General']['Type'])+" coordinates implemented")
2490#                raise Exception("no export for "+str(phasedict['General']['Type'])+" coordinates implemented")
2491
2492            if phasedict['General']['Type'] == 'nuclear':
2493                WriteSeqDistances(phasenam,histname,phasedict,cellList,self.seqData)
2494
2495            # N.B. map info probably not possible w/sequential
2496#            if 'Map' in phasedict['General'] and 'minmax' in phasedict['General']['Map']:
2497#                WriteCIFitem(self.fp, '\n# Difference density results')
2498#                MinMax = phasedict['General']['Map']['minmax']
2499#                WriteCIFitem(self.fp, '_refine_diff_density_max',G2mth.ValEsd(MinMax[0],-0.009))
2500#                WriteCIFitem(self.fp, '_refine_diff_density_min',G2mth.ValEsd(MinMax[1],-0.009))
2501               
2502        def WritePhaseInfo(phasenam,quick=True,oneblock=True):
2503            'Write out the phase information for the selected phase'
2504            WriteCIFitem(self.fp, '\n# phase info for '+str(phasenam) + ' follows')
2505            phasedict = self.Phases[phasenam] # pointer to current phase info
2506            WriteCIFitem(self.fp, '_pd_phase_name', phasenam)
2507            cellList,cellSig = self.GetCell(phasenam,unique=True)
2508            if quick:  # leave temperature as unknown
2509                WriteCIFitem(self.fp,"_cell_measurement_temperature","?")
2510            elif oneblock:
2511                pass # temperature should be written when the histogram saved later
2512            else: # get T set in _SelectPhaseT_CellSelectHist and possibly get new cell params
2513                T,hRanId = self.CellHistSelection.get(phasedict['ranId'],
2514                                                          ('?',None))
2515                try:
2516                    T = G2mth.ValEsd(T,-1.0)
2517                except:
2518                    pass
2519                WriteCIFitem(self.fp,"_cell_measurement_temperature",T)
2520                for h in self.Histograms:
2521                    if self.Histograms[h]['ranId'] == hRanId:
2522                        pId = phasedict['pId']
2523                        hId = self.Histograms[h]['hId']
2524                        cellList,cellSig = G2stIO.getCellSU(pId,hId,
2525                                        phasedict['General']['SGData'],
2526                                        self.parmDict,
2527                                        self.OverallParms['Covariance'])
2528                        break
2529                else:
2530                    T = '?'
2531
2532            defsigL = 3*[-0.00001] + 3*[-0.001] + [-0.01] # significance to use when no sigma
2533            prevsig = 0
2534            for lbl,defsig,val,sig in zip(cellNames,defsigL,cellList,cellSig):
2535                if sig:
2536                    txt = G2mth.ValEsd(val,sig)
2537                    prevsig = -sig # use this as the significance for next value
2538                else:
2539                    txt = G2mth.ValEsd(val,min(defsig,prevsig),True)
2540                WriteCIFitem(self.fp, '_cell_'+lbl,txt)
2541               
2542            density = G2mth.getDensity(phasedict['General'])[0]
2543            WriteCIFitem(self.fp, '_exptl_crystal_density_diffrn',
2544                    G2mth.ValEsd(density,-0.001))                   
2545
2546            WriteCIFitem(self.fp, '_symmetry_cell_setting',
2547                         phasedict['General']['SGData']['SGSys'])
2548
2549            if phasedict['General']['Type'] in ['nuclear','macromolecular']:
2550                spacegroup = phasedict['General']['SGData']['SpGrp'].strip()
2551                # regularize capitalization and remove trailing H/R
2552                spacegroup = spacegroup[0].upper() + spacegroup[1:].lower().rstrip('rh ')
2553                WriteCIFitem(self.fp, '_symmetry_space_group_name_H-M',spacegroup)
2554   
2555                # generate symmetry operations including centering and center of symmetry
2556                SymOpList,offsetList,symOpList,G2oprList,G2opcodes = G2spc.AllOps(
2557                    phasedict['General']['SGData'])
2558                WriteCIFitem(self.fp, 'loop_\n    _space_group_symop_id\n    _space_group_symop_operation_xyz')
2559                for i,op in enumerate(SymOpList,start=1):
2560                    WriteCIFitem(self.fp, '   {:3d}  {:}'.format(i,op.lower()))
2561            elif phasedict['General']['Type'] == 'magnetic':
2562                parentSpGrp = phasedict['General']['SGData']['SpGrp'].strip()
2563                parentSpGrp = parentSpGrp[0].upper() + parentSpGrp[1:].lower().rstrip('rh ')
2564                WriteCIFitem(self.fp, '_parent_space_group.name_H-M_alt',parentSpGrp)
2565#                [Trans,Uvec,Vvec] = phasedict['General']['SGData']['fromParent']         #save these
2566                spacegroup = phasedict['General']['SGData']['MagSpGrp'].strip()
2567                spacegroup = spacegroup[0].upper() + spacegroup[1:].lower().rstrip('rh ')
2568                WriteCIFitem(self.fp, '_space_group_magn.name_BNS',spacegroup)
2569                WriteCIFitem(self.fp, '_space_group.magn_point_group',phasedict['General']['SGData']['MagPtGp'])
2570
2571                # generate symmetry operations including centering and center of symmetry
2572                SymOpList,offsetList,symOpList,G2oprList,G2opcodes = G2spc.AllOps(
2573                    phasedict['General']['SGData'])
2574                SpnFlp = phasedict['General']['SGData']['SpnFlp']
2575                WriteCIFitem(self.fp, 'loop_\n    _space_group_symop_magn_operation.id\n    _space_group_symop_magn_operation.xyz')
2576                for i,op in enumerate(SymOpList,start=1):
2577                    if SpnFlp[i-1] >0:
2578                        opr = op.lower()+',+1'
2579                    else:
2580                        opr = op.lower()+',-1'
2581                    WriteCIFitem(self.fp, '   {:3d}  {:}'.format(i,opr))
2582
2583            # loop over histogram(s) used in this phase
2584            if not oneblock and not self.quickmode:
2585                # report pointers to the histograms used in this phase
2586                histlist = []
2587                for hist in self.Phases[phasenam]['Histograms']:
2588                    # if self.Phases[phasenam]['Histograms'][hist]['Use']:
2589                    #     if phasebyhistDict.get(hist):
2590                    #         phasebyhistDict[hist].append(phasenam)
2591                    #     else:
2592                    #         phasebyhistDict[hist] = [phasenam,]
2593                        blockid = datablockidDict.get(hist)
2594                        if not blockid:
2595                            print("Internal error: no block for data. Phase "+str(
2596                                phasenam)+" histogram "+str(hist))
2597                            histlist = []
2598                            break
2599                        histlist.append(blockid)
2600
2601                if len(histlist) == 0:
2602                    WriteCIFitem(self.fp, '# Note: phase has no associated data')
2603
2604            # report atom params
2605            if phasedict['General']['Type'] in ['nuclear','macromolecular']:        #this needs macromolecular variant, etc!
2606                try:
2607                    self.labellist
2608                except AttributeError:
2609                    self.labellist = []
2610                WriteAtomsNuclear(self.fp, self.Phases[phasenam], phasenam,
2611                                  self.parmDict, self.sigDict, self.labellist,
2612                                      self.OverallParms['Rigid bodies'])
2613            else:
2614                try:
2615                    self.labellist
2616                except AttributeError:
2617                    self.labellist = []
2618                WriteAtomsMagnetic(self.fp, self.Phases[phasenam], phasenam,
2619                                  self.parmDict, self.sigDict, self.labellist)
2620#                raise Exception("no export for "+str(phasedict['General']['Type'])+" coordinates implemented")
2621            keV = None             
2622            if oneblock: # get xray wavelength
2623                lamlist = []
2624                for hist in self.Histograms:
2625                    if 'X' not in self.Histograms[hist]['Instrument Parameters'][0]['Type'][0]:
2626                        continue
2627                    for k in ('Lam','Lam1'):
2628                        if k in self.Histograms[hist]['Instrument Parameters'][0]:
2629                            lamlist.append(self.Histograms[hist]['Instrument Parameters'][0][k][0])
2630                            break
2631                if len(lamlist) == 1:
2632                    keV = 12.397639/lamlist[0]
2633
2634            # report cell contents                       
2635            WriteComposition(self.fp, self.Phases[phasenam], phasenam, self.parmDict, self.quickmode, keV)
2636            if not self.quickmode and phasedict['General']['Type'] == 'nuclear':      # report distances and angles
2637                WriteDistances(phasenam)
2638            if 'Map' in phasedict['General'] and 'minmax' in phasedict['General']['Map']:
2639                WriteCIFitem(self.fp, '\n# Difference density results')
2640                MinMax = phasedict['General']['Map']['minmax']
2641                WriteCIFitem(self.fp, '_refine_diff_density_max',G2mth.ValEsd(MinMax[0],-0.009))
2642                WriteCIFitem(self.fp, '_refine_diff_density_min',G2mth.ValEsd(MinMax[1],-0.009))
2643
2644        def WritePhaseInfoMM(phasenam,quick=True,oneblock=True):
2645            'Write out the phase information for the selected phase for a macromolecular phase'
2646            WriteCIFitem(self.fp, '\n# phase info for '+str(phasenam) + ' follows')
2647            phasedict = self.Phases[phasenam] # pointer to current phase info
2648            WriteCIFitem(self.fp, '_cell.entry_id', phasenam)
2649            cellList,cellSig = self.GetCell(phasenam,unique=True)
2650            if oneblock:
2651                pass # temperature should be written when the histogram saved later
2652            else: # get T set in _SelectPhaseT_CellSelectHist and possibly get new cell params
2653                T,hRanId = self.CellHistSelection.get(phasedict['ranId'],
2654                                                          ('?',None))
2655                try:
2656                    T = G2mth.ValEsd(T,-1.0)
2657                except:
2658                    pass
2659                WriteCIFitem(self.fp,"_cell_measurement.temp",T)
2660                for h in self.Histograms:
2661                    if self.Histograms[h]['ranId'] == hRanId:
2662                        pId = phasedict['pId']
2663                        hId = self.Histograms[h]['hId']
2664                        cellList,cellSig = G2stIO.getCellSU(pId,hId,
2665                                        phasedict['General']['SGData'],
2666                                        self.parmDict,
2667                                        self.OverallParms['Covariance'])
2668                        break
2669                else:
2670                    T = '?'
2671
2672            defsigL = 3*[-0.00001] + 3*[-0.001] + [-0.01] # significance to use when no sigma
2673            prevsig = 0
2674            for lbl,defsig,val,sig in zip(cellNames,defsigL,cellList,cellSig):
2675                if sig:
2676                    txt = G2mth.ValEsd(val,sig)
2677                    prevsig = -sig # use this as the significance for next value
2678                else:
2679                    txt = G2mth.ValEsd(val,min(defsig,prevsig),True)
2680                WriteCIFitem(self.fp, '_cell.'+lbl,txt)
2681               
2682            density = G2mth.getDensity(phasedict['General'])[0]
2683            WriteCIFitem(self.fp, '_exptl_crystal.density_diffrn',
2684                    G2mth.ValEsd(density,-0.001))                   
2685
2686            WriteCIFitem(self.fp, '_symmetry.cell_setting',
2687                         phasedict['General']['SGData']['SGSys'])
2688
2689            spacegroup = phasedict['General']['SGData']['SpGrp'].strip()
2690            # regularize capitalization and remove trailing H/R
2691            spacegroup = spacegroup[0].upper() + spacegroup[1:].lower().rstrip('rh ')
2692            WriteCIFitem(self.fp, '_symmetry.space_group_name_H-M',spacegroup)
2693
2694            # generate symmetry operations including centering and center of symmetry
2695            SymOpList,offsetList,symOpList,G2oprList,G2opcodes = G2spc.AllOps(
2696                phasedict['General']['SGData'])
2697            WriteCIFitem(self.fp, 'loop_\n    _space_group.symop_id\n    _space_group.symop_operation_xyz')
2698            for i,op in enumerate(SymOpList,start=1):
2699                WriteCIFitem(self.fp, '   {:3d}  {:}'.format(i,op.lower()))
2700
2701            # loop over histogram(s) used in this phase
2702            if not oneblock and not self.quickmode:
2703                # report pointers to the histograms used in this phase
2704                histlist = []
2705                for hist in self.Phases[phasenam]['Histograms']:
2706                    # if self.Phases[phasenam]['Histograms'][hist]['Use']:
2707                    #     if phasebyhistDict.get(hist):
2708                    #         phasebyhistDict[hist].append(phasenam)
2709                    #     else:
2710                    #         phasebyhistDict[hist] = [phasenam,]
2711                        blockid = datablockidDict.get(hist)
2712                        if not blockid:
2713                            print("Internal error: no block for data. Phase "+str(
2714                                phasenam)+" histogram "+str(hist))
2715                            histlist = []
2716                            break
2717                        histlist.append(blockid)
2718
2719                if len(histlist) == 0:
2720                    WriteCIFitem(self.fp, '# Note: phase has no associated data')
2721
2722            # report atom params
2723            try:
2724                self.labellist
2725            except AttributeError:
2726                self.labellist = []
2727            WriteAtomsMM(self.fp, self.Phases[phasenam], phasenam,
2728                              self.parmDict, self.sigDict, 
2729                                  self.OverallParms['Rigid bodies'])
2730
2731            keV = None             
2732            if oneblock: # get xray wavelength
2733                lamlist = []
2734                for hist in self.Histograms:
2735                    if 'X' not in self.Histograms[hist]['Instrument Parameters'][0]['Type'][0]:
2736                        continue
2737                    for k in ('Lam','Lam1'):
2738                        if k in self.Histograms[hist]['Instrument Parameters'][0]:
2739                            lamlist.append(self.Histograms[hist]['Instrument Parameters'][0][k][0])
2740                            break
2741                if len(lamlist) == 1:
2742                    keV = 12.397639/lamlist[0]
2743
2744            # report cell contents                       
2745            WriteCompositionMM(self.fp, self.Phases[phasenam], phasenam, self.parmDict, self.quickmode, keV)
2746            #if not self.quickmode and phasedict['General']['Type'] == 'nuclear':      # report distances and angles
2747            #    WriteDistances(phasenam)
2748            if 'Map' in phasedict['General'] and 'minmax' in phasedict['General']['Map']:
2749                WriteCIFitem(self.fp, '\n# Difference density results')
2750                MinMax = phasedict['General']['Map']['minmax']
2751                WriteCIFitem(self.fp, '_refine.diff_density_max',G2mth.ValEsd(MinMax[0],-0.009))
2752                WriteCIFitem(self.fp, '_refine.diff_density_min',G2mth.ValEsd(MinMax[1],-0.009))
2753               
2754        def Yfmt(ndec,val):
2755            'Format intensity values'
2756            try:
2757                out = ("{:."+str(ndec)+"f}").format(val)
2758                out = out.rstrip('0')  # strip zeros to right of decimal
2759                return out.rstrip('.')  # and decimal place when not needed
2760            except TypeError:
2761                print(val)
2762                return '.'
2763           
2764        def WriteReflStat(refcount,hklmin,hklmax,dmin,dmax,nRefSets=1):
2765            'Write reflection statistics'
2766            WriteCIFitem(self.fp, '_reflns_number_total', str(refcount))
2767            if hklmin is not None and nRefSets == 1: # hkl range has no meaning with multiple phases
2768                WriteCIFitem(self.fp, '_reflns_limit_h_min', str(int(hklmin[0])))
2769                WriteCIFitem(self.fp, '_reflns_limit_h_max', str(int(hklmax[0])))
2770                WriteCIFitem(self.fp, '_reflns_limit_k_min', str(int(hklmin[1])))
2771                WriteCIFitem(self.fp, '_reflns_limit_k_max', str(int(hklmax[1])))
2772                WriteCIFitem(self.fp, '_reflns_limit_l_min', str(int(hklmin[2])))
2773                WriteCIFitem(self.fp, '_reflns_limit_l_max', str(int(hklmax[2])))
2774            if hklmin is not None:
2775                WriteCIFitem(self.fp, '_reflns_d_resolution_low  ', G2mth.ValEsd(dmax,-0.009))
2776                WriteCIFitem(self.fp, '_reflns_d_resolution_high ', G2mth.ValEsd(dmin,-0.009))
2777
2778        def WritePowderData(histlbl,seq=False):
2779            'Write out the selected powder diffraction histogram info'
2780            histblk = self.Histograms[histlbl]
2781            inst = histblk['Instrument Parameters'][0]
2782            if seq:
2783                resdblk = histblk['Residuals']
2784            else:
2785                resdblk = histblk
2786            hId = histblk['hId']
2787            pfx = ':' + str(hId) + ':'
2788
2789            if 'Lam1' in inst:
2790                ratio = self.parmDict.get('I(L2)/I(L1)',inst['I(L2)/I(L1)'][1])
2791                sratio = self.sigDict.get('I(L2)/I(L1)',-0.0009)
2792                lam1 = self.parmDict.get('Lam1',inst['Lam1'][1])
2793                slam1 = self.sigDict.get('Lam1',-0.00009)
2794                lam2 = self.parmDict.get('Lam2',inst['Lam2'][1])
2795                slam2 = self.sigDict.get('Lam2',-0.00009)
2796                # always assume Ka1 & Ka2 if two wavelengths are present
2797                WriteCIFitem(self.fp, '_diffrn_radiation_type','K\\a~1,2~')
2798                WriteCIFitem(self.fp, 'loop_' +
2799                             '\n   _diffrn_radiation_wavelength' +
2800                             '\n   _diffrn_radiation_wavelength_wt' +
2801                             '\n   _diffrn_radiation_wavelength_id')
2802                WriteCIFitem(self.fp, '  ' + PutInCol(G2mth.ValEsd(lam1,slam1),15)+
2803                             PutInCol('1.0',15) +
2804                             PutInCol('1',5))
2805                WriteCIFitem(self.fp, '  ' + PutInCol(G2mth.ValEsd(lam2,slam2),15)+
2806                             PutInCol(G2mth.ValEsd(ratio,sratio),15)+
2807                             PutInCol('2',5))
2808            elif 'Lam' in inst:
2809                lam1 = self.parmDict.get('Lam',inst['Lam'][1])
2810                slam1 = self.sigDict.get('Lam',-0.00009)
2811                WriteCIFitem(self.fp, '_diffrn_radiation_wavelength',G2mth.ValEsd(lam1,slam1))
2812
2813            if not oneblock:
2814                if not phasebyhistDict.get(histlbl) and not seq:
2815                    WriteCIFitem(self.fp, '\n# No phases associated with this data set')
2816                elif len(self.Phases) == 1:
2817                    pId = self.Phases[list(self.Phases.keys())[0]]['pId']
2818                    pfx = str(pId)+':'+str(hId)+':'
2819                    WriteCIFitem(self.fp, '_refine_ls_R_F_factor      ','%.5f'%(resdblk[pfx+'Rf']/100.))
2820                    WriteCIFitem(self.fp, '_refine_ls_R_Fsqd_factor   ','%.5f'%(resdblk[pfx+'Rf^2']/100.))
2821                else:
2822                    WriteCIFitem(self.fp, '\n# PHASE TABLE')
2823                    WriteCIFitem(self.fp, 'loop_' +
2824                                 '\n   _pd_phase_id' +
2825                                 '\n   _pd_phase_block_id' +
2826                                 '\n   _pd_phase_mass_%')
2827                    hId = self.Histograms[histlbl]['hId']
2828                    for phasenam in phasebyhistDict.get(histlbl):
2829                        pId = self.Phases[phasenam]['pId']
2830                        var = str(pId)+':'+str(hId)+':WgtFrac'
2831                        if self.seqData is None and 'depSigDict' in self.OverallParms['Covariance']:
2832                            depDict = self.OverallParms['Covariance']['depSigDict']
2833                        elif self.seqData is not None and 'depParmDict' in self.seqData[histlbl]:
2834                            depDict = self.seqData[histlbl]['depParmDict']
2835                        else:
2836                            depDict = {}
2837                        if var in depDict:
2838                            wtFr,sig = depDict[var]
2839                            wgtstr = G2mth.ValEsd(wtFr,sig)
2840                        else:                               
2841                            wgtstr = '?'
2842                        WriteCIFitem(self.fp,
2843                            '  '+
2844                            str(self.Phases[phasenam]['pId']) +
2845                            '  '+datablockidDict[phasenam]+
2846                            '  '+wgtstr
2847                            )
2848                    WriteCIFitem(self.fp, 'loop_' +
2849                                 '\n   _gsas_proc_phase_R_F_factor' +
2850                                 '\n   _gsas_proc_phase_R_Fsqd_factor' +
2851                                 '\n   _gsas_proc_phase_id' +
2852                                 '\n   _gsas_proc_phase_block_id')
2853                    for phasenam in phasebyhistDict.get(histlbl):
2854                        pfx = str(self.Phases[phasenam]['pId'])+':'+str(hId)+':'
2855                        WriteCIFitem(self.fp,
2856                            '  '+
2857                            '  '+G2mth.ValEsd(resdblk[pfx+'Rf']/100.,-.00009) +
2858                            '  '+G2mth.ValEsd(resdblk[pfx+'Rf^2']/100.,-.00009)+
2859                            '  '+str(self.Phases[phasenam]['pId'])+
2860                            '  '+datablockidDict[phasenam]
2861                            )
2862            elif len(self.Phases) == 1:
2863                # single phase in this histogram
2864                # get the phase number here
2865                pId = self.Phases[list(self.Phases.keys())[0]]['pId']
2866                pfx = str(pId)+':'+str(hId)+':'
2867                WriteCIFitem(self.fp, '_refine_ls_R_F_factor      ','%.5f'%(resdblk[pfx+'Rf']/100.))
2868                WriteCIFitem(self.fp, '_refine_ls_R_Fsqd_factor   ','%.5f'%(resdblk[pfx+'Rf^2']/100.))
2869               
2870            try:
2871                WriteCIFitem(self.fp, '_pd_proc_ls_prof_R_factor   ','%.5f'%(resdblk['R']/100.))
2872                WriteCIFitem(self.fp, '_pd_proc_ls_prof_wR_factor  ','%.5f'%(resdblk['wR']/100.))
2873                WriteCIFitem(self.fp, '_gsas_proc_ls_prof_R_B_factor ','%.5f'%(resdblk['Rb']/100.))
2874                WriteCIFitem(self.fp, '_gsas_proc_ls_prof_wR_B_factor','%.5f'%(resdblk['wRb']/100.))
2875                WriteCIFitem(self.fp, '_pd_proc_ls_prof_wR_expected','%.5f'%(resdblk['wRmin']/100.))
2876                if not oneblock: # written in WriteOverall, don't repeat in a one-block CIF
2877                    WriteCIFitem(self.fp, '_refine_ls_goodness_of_fit_all','%.2f'%(resdblk['wR']/resdblk['wRmin']))
2878            except KeyError:
2879                pass
2880
2881            if histblk['Instrument Parameters'][0]['Type'][1][1] == 'X':
2882                WriteCIFitem(self.fp, '_diffrn_radiation_probe','x-ray')
2883                pola = histblk['Instrument Parameters'][0].get('Polariz.')
2884                if pola:
2885                    pfx = ':' + str(hId) + ':'
2886                    sig = self.sigDict.get(pfx+'Polariz.',-0.0009)
2887                    txt = G2mth.ValEsd(pola[1],sig)
2888                    WriteCIFitem(self.fp, '_diffrn_radiation_polarisn_ratio',txt)
2889            elif histblk['Instrument Parameters'][0]['Type'][1][1] == 'N':
2890                WriteCIFitem(self.fp, '_diffrn_radiation_probe','neutron')
2891            if 'T' in inst['Type'][0]:
2892                txt = G2mth.ValEsd(inst['2-theta'][0],-0.009)
2893                WriteCIFitem(self.fp, '_pd_meas_2theta_fixed',txt)
2894
2895            WriteCIFitem(self.fp, '_pd_proc_ls_background_function',FormatBackground(histblk['Background'],histblk['hId']))
2896
2897            # TODO: this will need help from Bob
2898            #WriteCIFitem(self.fp, '_exptl_absorpt_process_details','?')
2899            #WriteCIFitem(self.fp, '_exptl_absorpt_correction_T_min','?')
2900            #WriteCIFitem(self.fp, '_exptl_absorpt_correction_T_max','?')
2901            #C extinction
2902            #WRITE(IUCIF,'(A)') '# Extinction correction'
2903            #CALL WRVAL(IUCIF,'_gsas_exptl_extinct_corr_T_min',TEXT(1:10))
2904            #CALL WRVAL(IUCIF,'_gsas_exptl_extinct_corr_T_max',TEXT(11:20))
2905
2906            # code removed because it is causing duplication in histogram block 1/26/19 BHT
2907            #if not oneblock:                 # instrumental profile terms go here
2908            #    WriteCIFitem(self.fp, '_pd_proc_ls_profile_function',
2909            #        FormatInstProfile(histblk["Instrument Parameters"],histblk['hId']))
2910
2911            #refprx = '_refln.' # mm
2912            refprx = '_refln_' # normal
2913            # data collection parameters for the powder dataset
2914
2915            temperature = histblk['Sample Parameters'].get('Temperature') # G2 uses K
2916            if not temperature:
2917                T = '?'
2918            else:
2919                T = G2mth.ValEsd(temperature,-0.009,True) # CIF uses K
2920            WriteCIFitem(self.fp, '_diffrn_ambient_temperature',T)
2921
2922            pressure = histblk['Sample Parameters'].get('Pressure') #G2 uses mega-Pascal
2923            if not pressure:
2924                P = '?'
2925            else:
2926                P = G2mth.ValEsd(pressure*1000,-0.09,True) # CIF uses kilopascal (G2 Mpa)
2927            WriteCIFitem(self.fp, '_diffrn_ambient_pressure',P)
2928
2929            WriteCIFitem(self.fp, '\n# STRUCTURE FACTOR TABLE')
2930            # compute maximum intensity reflection
2931            Imax = 0
2932            phaselist = []
2933            for phasenam in histblk['Reflection Lists']:
2934                try:
2935                    scale = self.Phases[phasenam]['Histograms'][histlbl]['Scale'][0]
2936                except KeyError: # reflection table from removed phase?
2937                    continue
2938                phaselist.append(phasenam)
2939                refList = np.asarray(histblk['Reflection Lists'][phasenam]['RefList'])
2940                I100 = scale*refList.T[8]*refList.T[11]
2941                #Icorr = np.array([refl[13] for refl in histblk['Reflection Lists'][phasenam]])[0]
2942                #FO2 = np.array([refl[8] for refl in histblk['Reflection Lists'][phasenam]])
2943                #I100 = scale*FO2*Icorr
2944                Imax = max(Imax,max(I100))
2945
2946            WriteCIFitem(self.fp, 'loop_')
2947            if len(phaselist) > 1:
2948                WriteCIFitem(self.fp, '   _pd_refln_phase_id')
2949            WriteCIFitem(self.fp, '   ' + refprx + 'index_h' +
2950                         '\n   ' + refprx + 'index_k' +
2951                         '\n   ' + refprx + 'index_l' +
2952                         '\n   ' + refprx + 'F_squared_meas' +
2953                         '\n   ' + refprx + 'F_squared_calc' +
2954                         '\n   ' + refprx + 'phase_calc' +
2955                         '\n   _refln_d_spacing')
2956            if Imax > 0:
2957                WriteCIFitem(self.fp, '   _gsas_i100_meas')
2958
2959            refcount = 0
2960            hklmin = None
2961            hklmax = None
2962            dmax = None
2963            dmin = None
2964            for phasenam in phaselist:
2965                scale = self.Phases[phasenam]['Histograms'][histlbl]['Scale'][0]
2966                phaseid = self.Phases[phasenam]['pId']
2967                refcount += len(histblk['Reflection Lists'][phasenam]['RefList'])
2968                refList = np.asarray(histblk['Reflection Lists'][phasenam]['RefList'])
2969                I100 = scale*refList.T[8]*refList.T[11]
2970                for j,ref in enumerate(histblk['Reflection Lists'][phasenam]['RefList']):
2971                    if DEBUG:
2972                        print('DEBUG: skipping reflection list')
2973                        break
2974                    if hklmin is None:
2975                        hklmin = copy.copy(ref[0:3])
2976                        hklmax = copy.copy(ref[0:3])
2977                    if dmin is None:
2978                         dmax = dmin = ref[4]
2979                    if len(phaselist) > 1:
2980                        s = PutInCol(phaseid,2)
2981                    else:
2982                        s = ""
2983                    for i,hkl in enumerate(ref[0:3]):
2984                        hklmax[i] = max(hkl,hklmax[i])
2985                        hklmin[i] = min(hkl,hklmin[i])
2986                        s += PutInCol(int(hkl),4)
2987                    for I in ref[8:10]:
2988                        s += PutInCol(G2mth.ValEsd(I,-0.0009),10)
2989                    s += PutInCol(G2mth.ValEsd(ref[10],-0.9),7)
2990                    dmax = max(dmax,ref[4])
2991                    dmin = min(dmin,ref[4])
2992                    s += PutInCol(G2mth.ValEsd(ref[4],-0.00009),8)
2993                    if Imax > 0:
2994                        s += PutInCol(G2mth.ValEsd(100.*I100[j]/Imax,-0.09),6)
2995                    WriteCIFitem(self.fp, "  "+s)
2996
2997            WriteReflStat(refcount,hklmin,hklmax,dmin,dmax,len(phaselist))
2998            WriteCIFitem(self.fp, '\n# POWDER DATA TABLE')
2999            # is data fixed step? If the step varies by <0.01% treat as fixed step
3000            steps = abs(histblk['Data'][0][1:] - histblk['Data'][0][:-1])
3001            if (max(steps)-min(steps)) > np.mean(steps)/10000.:
3002                fixedstep = False
3003            else:
3004                fixedstep = True
3005
3006            zero = None
3007            if fixedstep and 'T' not in inst['Type'][0]: # and not TOF
3008                WriteCIFitem(self.fp, '_pd_meas_2theta_range_min', G2mth.ValEsd(histblk['Data'][0][0],-0.00009))
3009                WriteCIFitem(self.fp, '_pd_meas_2theta_range_max', G2mth.ValEsd(histblk['Data'][0][-1],-0.00009))
3010                WriteCIFitem(self.fp, '_pd_meas_2theta_range_inc', G2mth.ValEsd(np.mean(steps),-0.00009))
3011                # zero correct, if defined
3012                zerolst = histblk['Instrument Parameters'][0].get('Zero')
3013                if zerolst: zero = zerolst[1]
3014                zero = self.parmDict.get('Zero',zero)
3015                if zero:
3016                    WriteCIFitem(self.fp, '_pd_proc_2theta_range_min', G2mth.ValEsd(histblk['Data'][0][0]-zero,-0.00009))
3017                    WriteCIFitem(self.fp, '_pd_proc_2theta_range_max', G2mth.ValEsd(histblk['Data'][0][-1]-zero,-0.00009))
3018                    WriteCIFitem(self.fp, '_pd_proc_2theta_range_inc', G2mth.ValEsd(steps.sum()/len(steps),-0.00009))
3019
3020            if zero:
3021                WriteCIFitem(self.fp, '_pd_proc_number_of_points', str(len(histblk['Data'][0])))
3022            else:
3023                WriteCIFitem(self.fp, '_pd_meas_number_of_points', str(len(histblk['Data'][0])))
3024            WriteCIFitem(self.fp, '\nloop_')
3025            #            WriteCIFitem(self.fp, '   _pd_proc_d_spacing') # need easy way to get this
3026            if not fixedstep:
3027                if zero:
3028                    WriteCIFitem(self.fp, '   _pd_proc_2theta_corrected')
3029                elif 'T' in inst['Type'][0]: # and not TOF
3030                    WriteCIFitem(self.fp, '   _pd_meas_time_of_flight')
3031                else:
3032                    WriteCIFitem(self.fp, '   _pd_meas_2theta_scan')
3033            # at least for now, always report weights.
3034            #if countsdata:
3035            #    WriteCIFitem(self.fp, '   _pd_meas_counts_total')
3036            #else:
3037            WriteCIFitem(self.fp, '   _pd_meas_intensity_total')
3038            WriteCIFitem(self.fp, '   _pd_calc_intensity_total')
3039            WriteCIFitem(self.fp, '   _pd_proc_intensity_bkg_calc')
3040            WriteCIFitem(self.fp, '   _pd_proc_ls_weight')
3041            maxY = max(histblk['Data'][1].max(),histblk['Data'][3].max())
3042            if maxY < 0: maxY *= -10 # this should never happen, but...
3043            ndec = max(0,10-int(np.log10(maxY))-1) # 10 sig figs should be enough
3044            maxSU = histblk['Data'][2].max()
3045            if maxSU < 0: maxSU *= -1 # this should never happen, but...
3046            ndecSU = max(0,8-int(np.log10(maxSU))-1) # 8 sig figs should be enough
3047            lowlim,highlim = histblk['Limits'][1]
3048
3049            if DEBUG:
3050                print('DEBUG: skipping profile list')
3051            else:
3052                for x,yobs,yw,ycalc,ybkg in zip(histblk['Data'][0].data,        #get the data from these masked arrays
3053                                                histblk['Data'][1].data,
3054                                                histblk['Data'][2].data,
3055                                                histblk['Data'][3].data,
3056                                                histblk['Data'][4].data):
3057                    if lowlim <= x <= highlim:
3058                        pass
3059                    else:
3060                        yw = 0.0 # show the point is not in use
3061
3062                    if fixedstep:
3063                        s = ""
3064                    elif zero:
3065                        s = PutInCol(G2mth.ValEsd(x-zero,-0.00009),10)
3066                    else:
3067                        s = PutInCol(G2mth.ValEsd(x,-0.00009),10)
3068                    s += PutInCol(Yfmt(ndec,yobs),12)
3069                    s += PutInCol(Yfmt(ndec,ycalc),12)
3070                    s += PutInCol(Yfmt(ndec,ybkg),11)
3071                    s += PutInCol(Yfmt(ndecSU,yw),9)
3072                    WriteCIFitem(self.fp, "  "+s)
3073                   
3074        def WritePowderDataMM(histlbl,seq=False):
3075            'Write out the selected powder diffraction histogram info'
3076            histblk = self.Histograms[histlbl]
3077            inst = histblk['Instrument Parameters'][0]
3078            hId = histblk['hId']
3079            pfx = ':' + str(hId) + ':'
3080
3081            WriteCIFitem(self.fp, '_diffrn.id',str(hId))
3082            WriteCIFitem(self.fp, '_diffrn.crystal_id',str(hId))
3083           
3084            if 'Lam1' in inst:
3085                ratio = self.parmDict.get('I(L2)/I(L1)',inst['I(L2)/I(L1)'][1])
3086                sratio = self.sigDict.get('I(L2)/I(L1)',-0.0009)
3087                lam1 = self.parmDict.get('Lam1',inst['Lam1'][1])
3088                slam1 = self.sigDict.get('Lam1',-0.00009)
3089                lam2 = self.parmDict.get('Lam2',inst['Lam2'][1])
3090                slam2 = self.sigDict.get('Lam2',-0.00009)
3091                # always assume Ka1 & Ka2 if two wavelengths are present
3092                WriteCIFitem(self.fp, '_diffrn_radiation.type','K\\a~1,2~')
3093                WriteCIFitem(self.fp, 'loop_' +
3094                             '\n   _diffrn_radiation_wavelength.wavelength' +
3095                             '\n   _diffrn_radiation_wavelength.wt' +
3096                             '\n   _diffrn_radiation_wavelength.id')
3097                WriteCIFitem(self.fp, '  ' + PutInCol(G2mth.ValEsd(lam1,slam1),15)+
3098                             PutInCol('1.0',15) +
3099                             PutInCol('1',5))
3100                WriteCIFitem(self.fp, '  ' + PutInCol(G2mth.ValEsd(lam2,slam2),15)+
3101                             PutInCol(G2mth.ValEsd(ratio,sratio),15)+
3102                             PutInCol('2',5))
3103            elif 'Lam' in inst:
3104                WriteCIFitem(self.fp, '_diffrn_radiation.diffrn_id',str(hId))
3105                WriteCIFitem(self.fp, '_diffrn_radiation.wavelength_id','1')
3106                WriteCIFitem(self.fp, '_diffrn_radiation_wavelength.id','1') 
3107                lam1 = self.parmDict.get('Lam',inst['Lam'][1])
3108                slam1 = self.sigDict.get('Lam',-0.00009)
3109                WriteCIFitem(self.fp, '_diffrn_radiation_wavelength.wavelength',G2mth.ValEsd(lam1,slam1))
3110
3111            if not oneblock:
3112                if seq:
3113                    pass
3114                elif not phasebyhistDict.get(histlbl):
3115                    WriteCIFitem(self.fp, '\n# No phases associated with this data set')
3116                else:
3117                    WriteCIFitem(self.fp, '\n# PHASE TABLE')
3118                    WriteCIFitem(self.fp, 'loop_' +
3119                                 '\n   _pd_phase_id' +
3120                                 '\n   _pd_phase_block_id' +
3121                                 '\n   _pd_phase_mass_%')
3122                    hId = self.Histograms[histlbl]['hId']
3123                    for phasenam in phasebyhistDict.get(histlbl):
3124                        pId = self.Phases[phasenam]['pId']
3125                        var = str(pId)+':'+str(hId)+':WgtFrac'
3126                        if self.seqData is None and 'depSigDict' in self.OverallParms['Covariance']:
3127                            depDict = self.OverallParms['Covariance']['depSigDict']
3128                        elif self.seqData is not None and 'depSigDict' in self.seqData[histlbl]:
3129                            depDict = self.seqData[histlbl]['depParmDict']
3130                        else:
3131                            depDict = {}
3132                        if var in depDict:
3133                            wtFr,sig = depDict[var]
3134                            wgtstr = G2mth.ValEsd(wtFr,sig)
3135                        else:                               
3136                            wgtstr = '?'
3137                        WriteCIFitem(self.fp,
3138                            '  '+
3139                            str(self.Phases[phasenam]['pId']) +
3140                            '  '+datablockidDict[phasenam]+
3141                            '  '+wgtstr
3142                            )
3143                    WriteCIFitem(self.fp, 'loop_' +
3144                                 '\n   _gsas_proc_phase_R_F_factor' +
3145                                 '\n   _gsas_proc_phase_R_Fsqd_factor' +
3146                                 '\n   _gsas_proc_phase_id' +
3147                                 '\n   _gsas_proc_phase_block_id')
3148                    for phasenam in phasebyhistDict.get(histlbl):
3149                        pfx = str(self.Phases[phasenam]['pId'])+':'+str(hId)+':'
3150                        WriteCIFitem(self.fp,
3151                            '  '+
3152                            '  '+G2mth.ValEsd(histblk[pfx+'Rf']/100.,-.00009) +
3153                            '  '+G2mth.ValEsd(histblk[pfx+'Rf^2']/100.,-.00009)+
3154                            '  '+str(self.Phases[phasenam]['pId'])+
3155                            '  '+datablockidDict[phasenam]
3156                            )
3157            elif len(self.Phases) == 1:
3158                # single phase in this histogram
3159                # get the phase number here
3160                pId = self.Phases[list(self.Phases.keys())[0]]['pId']
3161                pfx = str(pId)+':'+str(hId)+':'
3162                WriteCIFitem(self.fp, '_refine.ls_R_factor_all    ','%.5f'%(histblk[pfx+'Rf']/100.))
3163                WriteCIFitem(self.fp, '_refine_ls.R_Fsqd_factor   ','%.5f'%(histblk[pfx+'Rf^2']/100.))
3164               
3165            try:
3166                WriteCIFitem(self.fp, '_pd_proc_ls_prof_R_factor   ','%.5f'%(histblk['R']/100.))
3167                WriteCIFitem(self.fp, '_pd_proc_ls_prof_wR_factor  ','%.5f'%(histblk['wR']/100.))
3168                WriteCIFitem(self.fp, '_gsas_proc_ls_prof_R_B_factor ','%.5f'%(histblk['Rb']/100.))
3169                WriteCIFitem(self.fp, '_gsas_proc_ls_prof_wR_B_factor','%.5f'%(histblk['wRb']/100.))
3170                WriteCIFitem(self.fp, '_pd_proc_ls_prof_wR_expected','%.5f'%(histblk['wRmin']/100.))
3171            except KeyError:
3172                pass
3173
3174            if histblk['Instrument Parameters'][0]['Type'][1][1] == 'X':
3175                WriteCIFitem(self.fp, '_diffrn_radiation.probe','x-ray')
3176                pola = histblk['Instrument Parameters'][0].get('Polariz.')
3177                if pola:
3178                    pfx = ':' + str(hId) + ':'
3179                    sig = self.sigDict.get(pfx+'Polariz.',-0.0009)
3180                    txt = G2mth.ValEsd(pola[1],sig)
3181                    WriteCIFitem(self.fp, '_diffrn_radiation.polarisn_ratio',txt)
3182            elif histblk['Instrument Parameters'][0]['Type'][1][1] == 'N':
3183                WriteCIFitem(self.fp, '_diffrn_radiation.probe','neutron')
3184            if 'T' in inst['Type'][0]:
3185                txt = G2mth.ValEsd(inst['2-theta'][0],-0.009)
3186                WriteCIFitem(self.fp, '_pd_meas_2theta_fixed',txt)
3187
3188            WriteCIFitem(self.fp, '_pd_proc_ls_background_function',FormatBackground(histblk['Background'],histblk['hId']))
3189
3190            # TODO: this will need help from Bob
3191            #WriteCIFitem(self.fp, '_exptl_absorpt_process_details','?')
3192            #WriteCIFitem(self.fp, '_exptl_absorpt_correction_T_min','?')
3193            #WriteCIFitem(self.fp, '_exptl_absorpt_correction_T_max','?')
3194            #C extinction
3195            #WRITE(IUCIF,'(A)') '# Extinction correction'
3196            #CALL WRVAL(IUCIF,'_gsas_exptl_extinct_corr_T_min',TEXT(1:10))
3197            #CALL WRVAL(IUCIF,'_gsas_exptl_extinct_corr_T_max',TEXT(11:20))
3198
3199            # code removed because it is causing duplication in histogram block 1/26/19 BHT
3200            #if not oneblock:                 # instrumental profile terms go here
3201            #    WriteCIFitem(self.fp, '_pd_proc_ls_profile_function',
3202            #        FormatInstProfile(histblk["Instrument Parameters"],histblk['hId']))
3203
3204            # data collection parameters for the powder dataset
3205
3206            temperature = histblk['Sample Parameters'].get('Temperature') # G2 uses K
3207            if not temperature:
3208                T = '?'
3209            else:
3210                T = G2mth.ValEsd(temperature,-0.009,True) # CIF uses K
3211            WriteCIFitem(self.fp, '_diffrn_ambient.temp',T)
3212
3213            pressure = histblk['Sample Parameters'].get('Pressure') #G2 uses mega-Pascal
3214            if not pressure:
3215                P = '?'
3216            else:
3217                P = G2mth.ValEsd(pressure*1000,-0.09,True) # CIF uses kilopascal (G2 Mpa)
3218            WriteCIFitem(self.fp, '_diffrn_ambient.pressure',P)
3219
3220            WriteCIFitem(self.fp, '\n# STRUCTURE FACTOR TABLE')
3221            # compute maximum intensity reflection
3222            Imax = 0
3223            phaselist = []
3224            for phasenam in histblk['Reflection Lists']:
3225                try:
3226                    scale = self.Phases[phasenam]['Histograms'][histlbl]['Scale'][0]
3227                except KeyError: # reflection table from removed phase?
3228                    continue
3229                phaselist.append(phasenam)
3230                refList = np.asarray(histblk['Reflection Lists'][phasenam]['RefList'])
3231                I100 = scale*refList.T[8]*refList.T[11]
3232                #Icorr = np.array([refl[13] for refl in histblk['Reflection Lists'][phasenam]])[0]
3233                #FO2 = np.array([refl[8] for refl in histblk['Reflection Lists'][phasenam]])
3234                #I100 = scale*FO2*Icorr
3235#                Imax = max(Imax,max(I100))
3236
3237            WriteCIFitem(self.fp, 'loop_')
3238            #refprx = '_refln.' # mm
3239            if len(phaselist) > 1:
3240                WriteCIFitem(self.fp, '   _pd_refln_phase_id')
3241            WriteCIFitem(self.fp, '   _refln.index_h' +
3242                         '\n   _refln.index_k' +
3243                         '\n   _refln.index_l' +
3244                         '\n   _refln.F_squared_meas' +
3245                         '\n   _refln.F_squared_calc' +
3246                         '\n   _refln.phase_calc' +
3247                         '\n   _refln.d_spacing' +
3248                         '\n   _refln.status' +
3249                         '\n   _refln.crystal_id' +
3250                         '\n   _refln.wavelength_id' + 
3251                         '\n   _refln.scale_group_code' + 
3252                         '\n   _refln.F_squared_sigma')
3253           
3254#            if Imax > 0:
3255#                WriteCIFitem(self.fp, '   _gsas_i100_meas')
3256
3257            refcount = 0
3258            hklmin = None
3259            hklmax = None
3260            dmax = None
3261            dmin = None
3262            for phasenam in phaselist:
3263                scale = self.Phases[phasenam]['Histograms'][histlbl]['Scale'][0]
3264                phaseid = self.Phases[phasenam]['pId']
3265                refcount += len(histblk['Reflection Lists'][phasenam]['RefList'])
3266                refList = np.asarray(histblk['Reflection Lists'][phasenam]['RefList'])
3267                I100 = scale*refList.T[8]*refList.T[11]
3268                for j,ref in enumerate(histblk['Reflection Lists'][phasenam]['RefList']):
3269                    if DEBUG:
3270                        print('DEBUG: skipping reflection list')
3271                        break
3272                    if hklmin is None:
3273                        hklmin = copy.copy(ref[0:3])
3274                        hklmax = copy.copy(ref[0:3])
3275                    if dmin is None:
3276                         dmax = dmin = ref[4]
3277                    if len(phaselist) > 1:
3278                        s = PutInCol(phaseid,2)
3279                    else:
3280                        s = ""
3281                    for i,hkl in enumerate(ref[0:3]):
3282                        hklmax[i] = max(hkl,hklmax[i])
3283                        hklmin[i] = min(hkl,hklmin[i])
3284                        s += PutInCol(int(hkl),4)
3285                    for I in ref[8:10]:
3286                        s += PutInCol(G2mth.ValEsd(I,-0.0009),14)
3287                    s += PutInCol(G2mth.ValEsd(ref[10],-0.9),7)
3288                    dmax = max(dmax,ref[4])
3289                    dmin = min(dmin,ref[4])
3290                    s += PutInCol(G2mth.ValEsd(ref[4],-0.00009),8)
3291#                    if Imax > 0:
3292#                        s += PutInCol(G2mth.ValEsd(100.*I100[j]/Imax,-0.09),6)
3293                    s += PutInCol('o',2)
3294                    s += PutInCol('1',2)
3295                    s += PutInCol('1',2)
3296                    s += PutInCol('1',2)
3297                    s += PutInCol('.',2)
3298                    WriteCIFitem(self.fp, "  "+s)
3299
3300            # Write reflection statistics
3301            WriteCIFitem(self.fp, '_diffrn_reflns.number', str(refcount))
3302            if hklmin is not None and len(phaselist) == 1: # hkl range has no meaning with multiple phases
3303                WriteCIFitem(self.fp, '_diffrn_reflns.limit_h_min', str(int(hklmin[0])))
3304                WriteCIFitem(self.fp, '_diffrn_reflns.limit_h_max', str(int(hklmax[0])))
3305                WriteCIFitem(self.fp, '_diffrn_reflns.limit_k_min', str(int(hklmin[1])))
3306                WriteCIFitem(self.fp, '_diffrn_reflns.limit_k_max', str(int(hklmax[1])))
3307                WriteCIFitem(self.fp, '_diffrn_reflns.limit_l_min', str(int(hklmin[2])))
3308                WriteCIFitem(self.fp, '_diffrn_reflns.limit_l_max', str(int(hklmax[2])))
3309            if hklmin is not None:
3310                WriteCIFitem(self.fp, '_reflns.d_resolution_low  ', G2mth.ValEsd(dmax,-0.009))
3311                WriteCIFitem(self.fp, '_reflns.d_resolution_high ', G2mth.ValEsd(dmin,-0.009))
3312               
3313            WriteCIFitem(self.fp, '\n# POWDER DATA TABLE')
3314
3315            # is data fixed step? If the step varies by <0.01% treat as fixed step
3316            fixedstep = False
3317            zero = None
3318            WriteCIFitem(self.fp, '_refine.pdbx_pd_meas_number_of_points', str(len(histblk['Data'][0])))
3319            WriteCIFitem(self.fp, '\nloop_')
3320            #            WriteCIFitem(self.fp, '   _pd_proc_d_spacing') # need easy way to get this
3321            if 'T' in inst['Type'][0]: # and not TOF
3322                WriteCIFitem(self.fp, '   _pd_meas_time_of_flight')
3323            else:
3324                WriteCIFitem(self.fp, '   _pdbx_powder_data.pd_meas_2theta_scan')
3325            # at least for now, always report weights.
3326            #if countsdata:
3327            #    WriteCIFitem(self.fp, '   _pd_meas_counts_total')
3328            #else:
3329            WriteCIFitem(self.fp, '   _pdbx_powder_data.pd_meas_intensity_total')
3330            WriteCIFitem(self.fp, '   _pdbx_powder_data.pd_calc_intensity_total')
3331            WriteCIFitem(self.fp, '   _pdbx_powder_data.pd_proc_intensity_bkg_calc')
3332            WriteCIFitem(self.fp, '   _pdbx_powder_data.pd_proc_ls_weight')
3333            maxY = max(histblk['Data'][1].max(),histblk['Data'][3].max())
3334            if maxY < 0: maxY *= -10 # this should never happen, but...
3335            ndec = max(0,10-int(np.log10(maxY))-1) # 10 sig figs should be enough
3336            maxSU = histblk['Data'][2].max()
3337            if maxSU < 0: maxSU *= -1 # this should never happen, but...
3338            ndecSU = max(0,8-int(np.log10(maxSU))-1) # 8 sig figs should be enough
3339            lowlim,highlim = histblk['Limits'][1]
3340
3341            if DEBUG:
3342                print('DEBUG: skipping profile list')
3343            else:
3344                for x,yobs,yw,ycalc,ybkg in zip(histblk['Data'][0].data,        #get the data from these masked arrays
3345                                                histblk['Data'][1].data,
3346                                                histblk['Data'][2].data,
3347                                                histblk['Data'][3].data,
3348                                                histblk['Data'][4].data):
3349                    if lowlim <= x <= highlim:
3350                        pass
3351                    else:
3352                        yw = 0.0 # show the point is not in use
3353
3354                    if fixedstep:
3355                        s = ""
3356                    elif zero:
3357                        s = PutInCol(G2mth.ValEsd(x-zero,-0.00009),10)
3358                    else:
3359                        s = PutInCol(G2mth.ValEsd(x,-0.00009),10)
3360                    s += PutInCol(Yfmt(ndec,yobs),12)
3361                    s += PutInCol(Yfmt(ndec,ycalc),12)
3362                    s += PutInCol(Yfmt(ndec,ybkg),11)
3363                    s += PutInCol(Yfmt(ndecSU,yw),9)
3364                    WriteCIFitem(self.fp, "  "+s)
3365
3366        def WriteSingleXtalData(histlbl):
3367            'Write out the selected single crystal histogram info'
3368            histblk = self.Histograms[histlbl]
3369
3370            #refprx = '_refln.' # mm
3371            refprx = '_refln_' # normal
3372
3373            WriteCIFitem(self.fp, '\n# STRUCTURE FACTOR TABLE')
3374            WriteCIFitem(self.fp, 'loop_' +
3375                         '\n   ' + refprx + 'index_h' +
3376                         '\n   ' + refprx + 'index_k' +
3377                         '\n   ' + refprx + 'index_l' +
3378                         '\n   ' + refprx + 'F_squared_meas' +
3379                         '\n   ' + refprx + 'F_squared_sigma' +
3380                         '\n   ' + refprx + 'F_squared_calc' +
3381                         '\n   ' + refprx + 'phase_calc'
3382                         )
3383
3384            hklmin = None
3385            hklmax = None
3386            dmax = None
3387            dmin = None
3388            refcount = len(histblk['Data']['RefList'])
3389            for ref in histblk['Data']['RefList']:
3390                if ref[3] <= 0:      #skip user rejected reflections (mul <= 0)
3391                    continue
3392                s = "  "
3393                if hklmin is None:
3394                    hklmin = copy.copy(ref[0:3])
3395                    hklmax = copy.copy(ref[0:3])
3396                    dmax = dmin = ref[4]
3397                for i,hkl in enumerate(ref[0:3]):
3398                    hklmax[i] = max(hkl,hklmax[i])
3399                    hklmin[i] = min(hkl,hklmin[i])
3400                    s += PutInCol(int(hkl),4)
3401                if ref[5] == 0.0:
3402                    s += PutInCol(G2mth.ValEsd(ref[8],0),12)
3403                    s += PutInCol('.',10)
3404                    s += PutInCol(G2mth.ValEsd(ref[9],0),12)
3405                else:
3406                    sig = ref[6] * ref[8] / ref[5]
3407                    s += PutInCol(G2mth.ValEsd(ref[8],-abs(sig/10)),12)
3408                    s += PutInCol(G2mth.ValEsd(sig,-abs(sig)/10.),10)
3409                    s += PutInCol(G2mth.ValEsd(ref[9],-abs(sig/10)),12)
3410                s += PutInCol(G2mth.ValEsd(ref[10],-0.9),7)
3411                dmax = max(dmax,ref[4])
3412                dmin = min(dmin,ref[4])
3413                WriteCIFitem(self.fp, s)
3414            if not self.quickmode: # statistics only in a full CIF
3415                WriteReflStat(refcount,hklmin,hklmax,dmin,dmax)
3416                hId = histblk['hId']
3417                hfx = '0:'+str(hId)+':'
3418                phfx = '%d:%d:'%(0,hId)
3419                extType,extModel,extParms = self.Phases[phasenam]['Histograms'][histlbl]['Extinction']
3420                if extModel != 'None':
3421                    WriteCIFitem(self.fp, '# Extinction scaled by 1.e5')
3422                    WriteCIFitem(self.fp, '_refine_ls_extinction_method','Becker-Coppens %s %s'%(extModel,extType))
3423                    sig = -1.e-3
3424                    if extModel == 'Primary':
3425                        parm = extParms['Ep'][0]*1.e5
3426                        if extParms['Ep'][1]:
3427                            sig = self.sigDict[phfx+'Ep']*1.e5
3428                        text = G2mth.ValEsd(parm,sig)
3429                    elif extModel == 'Secondary Type I':
3430                        parm = extParms['Eg'][0]*1.e5
3431                        if extParms['Eg'][1]:
3432                            sig = self.sigDict[phfx+'Eg']*1.e5
3433                        text = G2mth.ValEsd(parm,sig)
3434                    elif extModel == 'Secondary Type II':
3435                        parm = extParms['Es'][0]*1.e5
3436                        if extParms['Es'][1]:
3437                            sig = self.sigDict[phfx+'Es']*1.e5
3438                        text = G2mth.ValEsd(parm,sig)
3439                    elif extModel == 'Secondary Type I & II':
3440                        parm = extParms['Eg'][0]*1.e5
3441                        if extParms['Es'][1]:
3442                            sig = self.sigDict[phfx+'Es']*1.e5
3443                        text = G2mth.ValEsd(parm,sig)
3444                        sig = -1.0e-3
3445                        parm = extParms['Es'][0]*1.e5
3446                        if extParms['Es'][1]:
3447                            sig = self.sigDict[phfx+'Es']*1.e5
3448                        text += G2mth.ValEsd(parm,sig)
3449                    WriteCIFitem(self.fp, '_refine_ls_extinction_coef',text)
3450                    WriteCIFitem(self.fp, '_refine_ls_extinction_expression','Becker & Coppens (1974). Acta Cryst. A30, 129-147')
3451
3452                WriteCIFitem(self.fp, '_refine_ls_wR_factor_gt    ','%.4f'%(histblk['wR']/100.))
3453                WriteCIFitem(self.fp, '_refine_ls_R_factor_gt     ','%.4f'%(histblk[hfx+'Rf']/100.))
3454                WriteCIFitem(self.fp, '_refine_ls_R_Fsqd_factor   ','%.4f'%(histblk[hfx+'Rf^2']/100.))
3455        def EditAuthor(event=None):
3456            'dialog to edit the CIF author info'
3457            'Edit the CIF author name'
3458            dlg = G2G.SingleStringDialog(self.G2frame,
3459                                          'Get CIF Author',
3460                                          'Provide CIF Author name (Last, First)',
3461                                          value=self.author)
3462            if not dlg.Show():
3463                dlg.Destroy()
3464                return False  # cancel was pressed
3465            self.author = dlg.GetValue()
3466            self.shortauthorname = self.author.replace(',','').replace(' ','')[:20]
3467            dlg.Destroy()
3468            try:
3469                self.OverallParms['Controls']["Author"] = self.author # save for future
3470            except KeyError:
3471                pass
3472            return True
3473
3474        def EditInstNames(event=None):
3475            'Provide a dialog for editing instrument names; for sequential fit, only need one name'
3476            dictlist = []
3477            keylist = []
3478            lbllist = []
3479            for hist in sorted(self.Histograms):
3480                if hist.startswith("PWDR"):
3481                    key2 = "Sample Parameters"
3482                    d = self.Histograms[hist][key2]
3483                elif hist.startswith("HKLF"):
3484                    key2 = "Instrument Parameters"
3485                    d = self.Histograms[hist][key2][0]
3486
3487                lbllist.append(hist)
3488                dictlist.append(d)
3489                keylist.append('InstrName')
3490                instrname = d.get('InstrName')
3491                if instrname is None:
3492                    d['InstrName'] = ''
3493                if hist.startswith("PWDR") and seqmode: break               
3494            return G2G.CallScrolledMultiEditor(
3495                self.G2frame,dictlist,keylist,
3496                prelbl=range(1,len(dictlist)+1),
3497                postlbl=lbllist,
3498                title='Instrument names',
3499                header="Edit instrument names. Note that a non-blank\nname is required for all histograms",
3500                CopyButton=True,ASCIIonly=True)
3501
3502        def EditRanges(event):
3503            '''Edit the bond distance/angle search range; phase is determined from
3504            a pointer placed in the button object (.phasedict) that references the
3505            phase dictionary
3506            '''
3507            but = event.GetEventObject()
3508            phasedict = but.phasedict
3509            dlg = G2G.DisAglDialog(
3510                self.G2frame,
3511                phasedict['General']['DisAglCtls'], # edited
3512                phasedict['General'], # defaults
3513                )
3514            if dlg.ShowModal() == wx.ID_OK:
3515                phasedict['General']['DisAglCtls'] = dlg.GetData()
3516            dlg.Destroy()
3517           
3518        def SetCellT(event):
3519            '''Set the temperature value by selection of a histogram
3520            '''
3521            but = event.GetEventObject()
3522            phasenam = but.phase
3523            rId =  self.Phases[phasenam]['ranId']
3524            self.CellHistSelection[rId] = self._CellSelectHist(phasenam)
3525           
3526        def EditCIFDefaults():
3527            '''Fills the CIF Defaults window with controls for editing various CIF export
3528            parameters (mostly related to templates).
3529            '''
3530            if len(self.cifdefs.GetChildren()) > 0:
3531                saveSize = self.cifdefs.GetSize()
3532                self.cifdefs.DestroyChildren()
3533            else:
3534                saveSize = None
3535            self.cifdefs.SetTitle('Edit CIF settings')
3536            vbox = wx.BoxSizer(wx.VERTICAL)
3537            vbox.Add(wx.StaticText(self.cifdefs, wx.ID_ANY,'Creating file '+self.filename))
3538            but = wx.Button(self.cifdefs, wx.ID_ANY,'Edit CIF Author')
3539            but.Bind(wx.EVT_BUTTON,EditAuthor)
3540            vbox.Add(but,0,wx.ALIGN_CENTER,3)
3541            but = wx.Button(self.cifdefs, wx.ID_ANY,'Edit Instrument Name(s)')
3542            but.Bind(wx.EVT_BUTTON,EditInstNames)
3543            vbox.Add(but,0,wx.ALIGN_CENTER,3)
3544            cpnl = wxscroll.ScrolledPanel(self.cifdefs,size=(300,300))
3545            cbox = wx.BoxSizer(wx.VERTICAL)
3546            G2G.HorizontalLine(cbox,cpnl)
3547            cbox.Add(
3548                CIFtemplateSelect(self.cifdefs,
3549                                  cpnl,'publ',self.OverallParms['Controls'],
3550                                  EditCIFDefaults,
3551                                  "Publication (overall) template",
3552                                  ),
3553                0,wx.EXPAND|wx.ALIGN_LEFT|wx.ALL)
3554            for phasenam in sorted(self.Phases.keys()):
3555                G2G.HorizontalLine(cbox,cpnl)
3556                title = 'Phase '+phasenam
3557                phasedict = self.Phases[phasenam] # pointer to current phase info
3558                cbox.Add(
3559                    CIFtemplateSelect(self.cifdefs,
3560                                      cpnl,'phase',phasedict['General'],
3561                                      EditCIFDefaults,
3562                                      title,
3563                                      phasenam),
3564                    0,wx.EXPAND|wx.ALIGN_LEFT|wx.ALL)
3565                cpnl.SetSizer(cbox)
3566                if phasedict['General']['Type'] == 'nuclear':
3567                    but = wx.Button(cpnl, wx.ID_ANY,'Edit distance/angle ranges')
3568                    cbox.Add(but,0,wx.ALIGN_LEFT,0)
3569                    cbox.Add((-1,2))
3570                    but.phasedict = self.Phases[phasenam]  # set a pointer to current phase info
3571                    but.Bind(wx.EVT_BUTTON,EditRanges)     # phase bond/angle ranges
3572                    but = wx.Button(cpnl, wx.ID_ANY,'Set distance/angle publication flags')
3573                    but.phase = phasenam  # set a pointer to current phase info
3574                    but.Bind(wx.EVT_BUTTON,SelectDisAglFlags)     # phase bond/angle ranges
3575                    cbox.Add(but,0,wx.ALIGN_LEFT,0)
3576                if self._CellSelectNeeded(phasenam):
3577                    but = wx.Button(cpnl, wx.ID_ANY,'Select cell temperature')
3578                    cbox.Add(but,0,wx.ALIGN_LEFT,0)
3579                    cbox.Add((-1,2))
3580                    but.phase = phasenam  # set a pointer to current phase info
3581                    but.Bind(wx.EVT_BUTTON,SetCellT)
3582                cbox.Add((-1,2))
3583            for i in sorted(self.powderDict.keys()):
3584                G2G.HorizontalLine(cbox,cpnl)
3585                if seqmode:
3586                    hist = self.powderDict[i]
3587                    histblk = self.Histograms[hist]
3588                    title = 'All Powder datasets'
3589                    cbox.Add(
3590                        CIFtemplateSelect(self.cifdefs,
3591                                      cpnl,'powder',self.OverallParms['Controls'],
3592                                      EditCIFDefaults,
3593                                      title,
3594                                      histblk["Sample Parameters"]['InstrName'],
3595                                      cifKey="seqCIF_template"),
3596                        0,wx.EXPAND|wx.ALIGN_LEFT|wx.ALL)
3597                    break
3598                hist = self.powderDict[i]
3599                histblk = self.Histograms[hist]
3600                title = 'Powder dataset '+hist[5:]
3601                cbox.Add(
3602                    CIFtemplateSelect(self.cifdefs,
3603                                      cpnl,'powder',histblk["Sample Parameters"],
3604                                      EditCIFDefaults,
3605                                      title,
3606                                      histblk["Sample Parameters"]['InstrName']),
3607                    0,wx.EXPAND|wx.ALIGN_LEFT|wx.ALL)
3608            for i in sorted(self.xtalDict.keys()):
3609                G2G.HorizontalLine(cbox,cpnl)
3610                hist = self.xtalDict[i]
3611                histblk = self.Histograms[hist]
3612                title = 'Single Xtal dataset '+hist[5:]
3613                cbox.Add(
3614                    CIFtemplateSelect(self.cifdefs,
3615                                      cpnl,'single',histblk["Instrument Parameters"][0],
3616                                      EditCIFDefaults,
3617                                      title,
3618                                      histblk["Instrument Parameters"][0]['InstrName']),
3619                    0,wx.EXPAND|wx.ALIGN_LEFT|wx.ALL)
3620            cpnl.SetSizer(cbox)
3621            cpnl.SetAutoLayout(1)
3622            cpnl.SetupScrolling()
3623            #cpnl.Bind(rw.EVT_RW_LAYOUT_NEEDED, self.OnLayoutNeeded) # needed if sizes change
3624            cpnl.Layout()
3625
3626            vbox.Add(cpnl, 1, wx.ALIGN_LEFT|wx.ALL|wx.EXPAND, 0)
3627            btnsizer = wx.StdDialogButtonSizer()
3628            btn = wx.Button(self.cifdefs, wx.ID_OK, "Create CIF")
3629            btn.SetDefault()
3630            btnsizer.AddButton(btn)
3631            btn = wx.Button(self.cifdefs, wx.ID_CANCEL)
3632            btnsizer.AddButton(btn)
3633            btnsizer.Realize()
3634            vbox.Add(btnsizer, 0, wx.ALIGN_CENTER|wx.ALL, 5)
3635            self.cifdefs.SetSizer(vbox)
3636            if not saveSize:
3637                vbox.Fit(self.cifdefs)
3638            self.cifdefs.Layout()
3639
3640        def OnToggleButton(event):
3641            'Respond to press of ToggleButton in SelectDisAglFlags'
3642            but = event.GetEventObject()
3643            if but.GetValue():
3644                but.DisAglSel[but.key] = True
3645            else:
3646                try:
3647                    del but.DisAglSel[but.key]
3648                except KeyError:
3649                    pass
3650        def keepTrue(event):
3651            event.GetEventObject().SetValue(True)
3652        def keepFalse(event):
3653            event.GetEventObject().SetValue(False)
3654
3655        def SelectDisAglFlags(event):
3656            'Select Distance/Angle use flags for the selected phase'
3657            phasenam = event.GetEventObject().phase
3658            phasedict = self.Phases[phasenam]
3659            SymOpList,offsetList,symOpList,G2oprList,G2opcodes = G2spc.AllOps(phasedict['General']['SGData'])
3660            generalData = phasedict['General']
3661            # create a dict for storing Pub flag for bonds/angles, if needed
3662            if phasedict['General'].get("DisAglHideFlag") is None:
3663                phasedict['General']["DisAglHideFlag"] = {}
3664            DisAngSel = phasedict['General']["DisAglHideFlag"]
3665
3666            cx,ct,cs,cia = phasedict['General']['AtomPtrs']
3667            cn = ct-1
3668            cfrac = cx+3
3669            DisAglData = {}
3670            # create a list of atoms, but skip atoms with zero occupancy
3671            xyz = []
3672            fpfx = str(phasedict['pId'])+'::Afrac:'
3673            for i,atom in enumerate(phasedict['Atoms']):
3674                if self.parmDict.get(fpfx+str(i),atom[cfrac]) == 0.0: continue
3675                xyz.append([i,]+atom[cn:cn+2]+atom[cx:cx+3])
3676            if 'DisAglCtls' not in generalData:
3677                # should not be used, since DisAglDialog should be called
3678                # for all phases before getting here
3679                dlg = G2G.DisAglDialog(
3680                    self.cifdefs,
3681                    {},
3682                    generalData)
3683                if dlg.ShowModal() == wx.ID_OK:
3684                    generalData['DisAglCtls'] = dlg.GetData()
3685                else:
3686                    dlg.Destroy()
3687                    return
3688                dlg.Destroy()
3689            dlg = wx.Dialog(
3690                self.G2frame,
3691                style=wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER)
3692            vbox = wx.BoxSizer(wx.VERTICAL)
3693            txt = wx.StaticText(dlg,wx.ID_ANY,'Searching distances for phase '+phasenam
3694                                +'\nPlease wait...')
3695            vbox.Add(txt,0,wx.ALL|wx.EXPAND)
3696            dlg.SetSizer(vbox)
3697            dlg.CenterOnParent()
3698            dlg.Show() # post "please wait"
3699            wx.BeginBusyCursor() # and change cursor
3700
3701            DisAglData['OrigAtoms'] = xyz
3702            DisAglData['TargAtoms'] = xyz
3703            SymOpList,offsetList,symOpList,G2oprList,G2opcodes = G2spc.AllOps(
3704                generalData['SGData'])
3705
3706#            xpandSGdata = generalData['SGData'].copy()
3707#            xpandSGdata.update({'SGOps':symOpList,
3708#                                'SGInv':False,
3709#                                'SGLatt':'P',
3710#                                'SGCen':np.array([[0, 0, 0]]),})
3711#            DisAglData['SGData'] = xpandSGdata
3712            DisAglData['SGData'] = generalData['SGData'].copy()
3713
3714            DisAglData['Cell'] = generalData['Cell'][1:] #+ volume
3715            if 'pId' in phasedict:
3716                DisAglData['pId'] = phasedict['pId']
3717                DisAglData['covData'] = self.OverallParms['Covariance']
3718            try:
3719                AtomLabels,DistArray,AngArray = G2stMn.RetDistAngle(
3720                    generalData['DisAglCtls'],
3721                    DisAglData)
3722            except KeyError:        # inside DistAngle for missing atom types in DisAglCtls
3723                print('**** ERROR - try again but do "Reset" to fill in missing atom types ****')
3724            wx.EndBusyCursor()
3725            txt.SetLabel('Set publication flags for distances and angles in\nphase '+phasenam)
3726            vbox.Add((5,5))
3727            vbox.Add(wx.StaticText(dlg,wx.ID_ANY,
3728                                   'The default is to flag all distances and angles as to be'+
3729                                   '\npublished. Change this by pressing appropriate buttons.'),
3730                     0,wx.ALL|wx.EXPAND)
3731            hbox = wx.BoxSizer(wx.HORIZONTAL)
3732            vbox.Add(hbox)
3733            hbox.Add(wx.StaticText(dlg,wx.ID_ANY,'Button appearance: '))
3734            but = wx.ToggleButton(dlg,wx.ID_ANY,'Publish')
3735            but.Bind(wx.EVT_TOGGLEBUTTON,keepFalse)
3736            hbox.Add(but)
3737            but = wx.ToggleButton(dlg,wx.ID_ANY,"Don't publish")
3738            but.Bind(wx.EVT_TOGGLEBUTTON,keepTrue)
3739            hbox.Add(but)
3740            but.SetValue(True)
3741            G2G.HorizontalLine(vbox,dlg)
3742
3743            cpnl = wxscroll.ScrolledPanel(dlg,size=(400,300))
3744            cbox = wx.BoxSizer(wx.VERTICAL)
3745            for c in sorted(DistArray):
3746                karr = []
3747                UsedCols = {}
3748                cbox.Add(wx.StaticText(cpnl,wx.ID_ANY,
3749                                   'distances to/angles around atom '+AtomLabels[c]))
3750                #dbox = wx.GridBagSizer(hgap=5)
3751                dbox = wx.GridBagSizer()
3752                for i,D in enumerate(DistArray[c]):
3753                    karr.append(tuple(D[0:3]))
3754                    val = "{:.2f}".format(D[3])
3755                    sym = " [{:d} {:d} {:d}]".format(*D[1]) + " #{:d}".format(D[2])
3756                    dbox.Add(wx.StaticText(cpnl,wx.ID_ANY,AtomLabels[D[0]]),
3757                             (i+1,0)
3758                             )
3759                    dbox.Add(wx.StaticText(cpnl,wx.ID_ANY,sym),
3760                             (i+1,1)
3761                             )
3762                    but = wx.ToggleButton(cpnl,wx.ID_ANY,val)
3763                    but.key = (c,karr[-1])
3764                    but.DisAglSel = DisAngSel
3765                    if DisAngSel.get(but.key): but.SetValue(True)
3766                    but.Bind(wx.EVT_TOGGLEBUTTON,OnToggleButton)
3767                    dbox.Add(but,(i+1,2),border=1)
3768                for i,D in enumerate(AngArray[c]):
3769                    val = "{:.1f}".format(D[2][0])
3770                    but = wx.ToggleButton(cpnl,wx.ID_ANY,val)
3771                    but.key = (karr[D[0]],c,karr[D[1]])
3772                    but.DisAglSel = DisAngSel
3773                    if DisAngSel.get(but.key): but.SetValue(True)
3774                    but.Bind(wx.EVT_TOGGLEBUTTON,OnToggleButton)
3775                    dbox.Add(but,(D[0]+1,D[1]+3),border=1)
3776                    UsedCols[D[1]+3] = True
3777                for i,D in enumerate(DistArray[c][:-1]): # label columns that are used
3778                    if UsedCols.get(i+3):
3779                        dbox.Add(wx.StaticText(cpnl,wx.ID_ANY,AtomLabels[D[0]]),
3780                                 (0,i+3),
3781                                 flag=wx.ALIGN_CENTER
3782                                 )
3783                dbox.Add(wx.StaticText(cpnl,wx.ID_ANY,'distance'),
3784                                 (0,2),
3785                                 flag=wx.ALIGN_CENTER
3786                                 )
3787                cbox.Add(dbox)
3788                G2G.HorizontalLine(cbox,cpnl)
3789            cpnl.SetSizer(cbox)
3790            cpnl.SetAutoLayout(1)
3791            cpnl.SetupScrolling()
3792            #cpnl.Bind(rw.EVT_RW_LAYOUT_NEEDED, self.OnLayoutNeeded) # needed if sizes change
3793            cpnl.Layout()
3794
3795            vbox.Add(cpnl, 1, wx.ALIGN_LEFT|wx.ALL|wx.EXPAND, 0)
3796
3797            btnsizer = wx.StdDialogButtonSizer()
3798            btn = wx.Button(dlg, wx.ID_OK, "Done")
3799            btn.SetDefault()
3800            btnsizer.AddButton(btn)
3801            btnsizer.Realize()
3802            vbox.Add(btnsizer, 0, wx.ALIGN_CENTER|wx.ALL, 5)
3803            dlg.SetSizer(vbox)
3804            vbox.Fit(dlg)
3805            dlg.Layout()
3806
3807            dlg.CenterOnParent()
3808            dlg.ShowModal()
3809
3810#==============================================================================
3811####  _Exporter code starts here         ======================================
3812#==============================================================================
3813        # make sure required information is present
3814        self.CIFdate = dt.datetime.strftime(dt.datetime.now(),"%Y-%m-%dT%H:%M")
3815        if not self.CIFname: # Get a name for the CIF. If not defined, use the GPX name (save, if that is needed).
3816            if not self.G2frame.GSASprojectfile:
3817                self.G2frame.OnFileSaveas(None)
3818            if not self.G2frame.GSASprojectfile: return
3819            self.CIFname = os.path.splitext(
3820                os.path.split(self.G2frame.GSASprojectfile)[1]
3821                )[0]
3822            self.CIFname = self.CIFname.replace(' ','')
3823        # replace non-ASCII characters in CIFname with dots
3824        s = ''
3825        for c in self.CIFname:
3826            if ord(c) < 128:
3827                s += c
3828            else:
3829                s += '.'
3830        self.CIFname = s
3831        phasebyhistDict = {} # a cross-reference to phases by histogram -- used in sequential fits
3832        for phasenam in self.Phases:
3833            for hist in self.Phases[phasenam]['Histograms']:
3834                if self.Phases[phasenam]['Histograms'][hist]['Use']:
3835                    if phasebyhistDict.get(hist):
3836                        phasebyhistDict[hist].append(phasenam)
3837                    else:
3838                        phasebyhistDict[hist] = [phasenam,]
3839        #=================================================================
3840        # write quick CIFs
3841        #=================================================================
3842        if phaseOnly: #====Phase only CIF ================================
3843            print('Writing CIF output to file '+self.filename)
3844            oneblock = True
3845            self.quickmode = True
3846            self.Write(' ')
3847            self.Write(70*'#')
3848            WriteCIFitem(self.fp, 'data_'+phaseOnly.replace(' ','_'))
3849            WriteCIFitem(self.fp, '_gsas_GSASII_version',
3850                             str(GSASIIpath.GetVersionNumber()))
3851            #phaseblk = self.Phases[phaseOnly] # pointer to current phase info
3852            # report the phase info
3853            if self.Phases[phaseOnly]['General']['Type'] == 'macromolecular':
3854                WritePhaseInfoMM(phaseOnly)
3855            else:
3856                WritePhaseInfo(phaseOnly)
3857            return
3858        elif histOnly: #====Histogram only CIF ================================
3859            print('Writing CIF output to file '+self.filename)
3860            MM = False
3861            for p in self.Phases:
3862                if self.Phases[p]['General']['Type'] == 'macromolecular':
3863                    MM = True
3864                    break
3865            hist = histOnly
3866            #histname = histOnly.replace(' ','')
3867            oneblock = True
3868            self.quickmode = True
3869            self.ifHKLF = False
3870            self.ifPWDR = True
3871            self.Write(' ')
3872            self.Write(70*'#')
3873            #phasenam = self.Phases.keys()[0]
3874            WriteCIFitem(self.fp, 'data_'+self.CIFname)
3875            WriteCIFitem(self.fp, '_gsas_GSASII_version',
3876                             str(GSASIIpath.GetVersionNumber()))
3877            if hist.startswith("PWDR") and MM:
3878                WritePowderDataMM(hist)
3879            elif hist.startswith("PWDR"):
3880                WritePowderData(hist)
3881            elif hist.startswith("HKLF"):
3882                WriteSingleXtalData(hist)
3883            return
3884        #===============================================================================
3885        # setup for sequential fits here
3886        #===============================================================================
3887        seqmode = False
3888        seqHistList = []
3889        if self.G2frame.testSeqRefineMode():
3890            if self.seqData is None:
3891                raise Exception('Use Export/Sequential project for sequential refinements')
3892            if len(self.Phases) > 1:
3893                phaseWithHist = False  # multiple phases per histogram
3894            else:
3895                phaseWithHist = True   # include the phase in the same block as the histogram
3896            seqmode = True
3897            seqHistList = [h for h in self.seqData['histNames'] if h in self.seqData]
3898            if 'Use' in self.seqData and len(seqHistList) == len(self.seqData.get('Use',[])):
3899                seqHistList = [h for i,h in enumerate(seqHistList) if self.seqData['Use'][i]]
3900               
3901        #===============================================================================
3902        ### full CIF export starts here (Sequential too)
3903        #===============================================================================
3904        # load saved CIF author name
3905        self.author = self.OverallParms['Controls'].get("Author",'?').strip()
3906        # initialize dict for Selection of Hist for unit cell reporting
3907        self.OverallParms['Controls']['CellHistSelection'] = self.OverallParms[
3908            'Controls'].get('CellHistSelection',{})
3909        self.CellHistSelection = self.OverallParms['Controls']['CellHistSelection']
3910        if self.OverallParms['Controls']['max cyc'] > 1:
3911            dlg = wx.MessageDialog(
3912                self.G2frame,
3913                'GSAS-II reports the maximum shift to s.u. ratio over all cycles in the last refinement,'+
3914                ' while CIF expects it over only the last cycle. \n\n'+
3915                'Do you want to set the maximum cycles to 1 and repeat the last refinement'+
3916                ' so these will be the same before creating CIF? (Use No to continue)',
3917                'Max(shift/esd) in question',wx.YES|wx.NO)
3918            ret = dlg.ShowModal()
3919            dlg.CenterOnParent()
3920            dlg.Destroy()
3921            if ret == wx.ID_YES:
3922                self.OverallParms['Controls']['max cyc'] = 1
3923                self.G2frame.OnRefine(None)
3924                self.InitExport(event)  # restart export using updated project
3925                self.loadTree()
3926               
3927        # create a dict with refined values and their uncertainties
3928        self.loadParmDict()
3929        # is there anything to export?
3930        if len(self.Phases) == len(self.powderDict) == len(self.xtalDict) == 0:
3931           self.G2frame.ErrorDialog(
3932               'Empty project',
3933               'Project does not contain any data or phases. Are they interconnected?')
3934           return
3935        if self.ExportSelect('ask'): return
3936        if not self.filename:
3937            print('No name supplied')
3938            return
3939        self.OpenFile(delayOpen=True)
3940       
3941        #if self.ExportSelect('default'): return
3942        # Someday: get restraint & constraint info
3943        #restraintDict = self.OverallParms.get('Restraints',{})
3944        #for i in  self.OverallParms['Constraints']:
3945        #    print i
3946        #    for j in self.OverallParms['Constraints'][i]:
3947        #        print j
3948
3949        self.quickmode = False # full CIF
3950        phasenam = None # include all phases
3951        # Will this require a multiblock CIF?
3952        if len(self.Phases) > 1:
3953            oneblock = False
3954        elif len(self.powderDict) + len(self.xtalDict) > 1:
3955            oneblock = False
3956        else: # one phase, one dataset, Full CIF
3957            oneblock = True
3958
3959        # check there is an instrument name for every histogram
3960        self.ifPWDR = False
3961        self.ifHKLF = False
3962        invalid = 0
3963        key3 = 'InstrName'
3964        for hist in self.Histograms:
3965            if hist.startswith("PWDR"):
3966                self.ifPWDR = True
3967                key2 = "Sample Parameters"
3968                d = self.Histograms[hist][key2]
3969            elif hist.startswith("HKLF"):
3970                self.ifHKLF = True
3971                key2 = "Instrument Parameters"
3972                d = self.Histograms[hist][key2][0]
3973            instrname = d.get(key3)
3974            if instrname is None:
3975                d[key3] = ''
3976                invalid += 1
3977            elif instrname.strip() == '':
3978                invalid += 1
3979            if hist.startswith("PWDR") and seqmode: break
3980        if invalid:
3981            #msg = ""
3982            #if invalid > 3: msg = (
3983            #    "\n\nNote: it may be faster to set the name for\n"
3984            #    "one histogram for each instrument and use the\n"
3985            #    "File/Copy option to duplicate the name"
3986            #    )
3987            if not EditInstNames(): return
3988
3989        # check for a distance-angle range search range for each phase
3990        for phasenam in sorted(self.Phases.keys()):
3991            #i = self.Phases[phasenam]['pId']
3992            phasedict = self.Phases[phasenam] # pointer to current phase info
3993            if 'DisAglCtls' not in phasedict['General']:
3994                dlg = G2G.DisAglDialog(
3995                    self.G2frame,
3996                    {},
3997                    phasedict['General'])
3998                if dlg.ShowModal() == wx.ID_OK:
3999                    phasedict['General']['DisAglCtls'] = dlg.GetData()
4000                else:
4001                    dlg.Destroy()
4002                    return
4003                dlg.Destroy()
4004                   
4005        # check if temperature values & pressure are defaulted
4006        default = 0
4007        for hist in self.Histograms:
4008            if hist.startswith("PWDR"):
4009                key2 = "Sample Parameters"
4010                T = self.Histograms[hist][key2].get('Temperature')
4011                if not T:
4012                    default += 1
4013                elif T == 300:
4014                    default += 1
4015                P = self.Histograms[hist][key2].get('Pressure')
4016                if not P:
4017                    default += 1
4018                elif P == 1:
4019                    default += 1
4020        if default > 0:
4021            dlg = wx.MessageDialog(
4022                self.G2frame,
4023                'Temperature/Pressure values appear to be defaulted for some powder histograms (See Sample Parameters for each PWDR tree entry). Do you want to use those values?',
4024                'Check T and P values',
4025                wx.OK|wx.CANCEL)
4026            ret = dlg.ShowModal()
4027            dlg.CenterOnParent()
4028            dlg.Destroy()
4029            if ret != wx.ID_OK: return
4030        if oneblock:
4031            # select a dataset to use (there should only be one set in one block,
4032            # but take whatever comes 1st)
4033            for hist in self.Histograms:
4034                histblk = self.Histograms[hist]
4035                if hist.startswith("PWDR"):
4036                    instnam = histblk["Sample Parameters"]['InstrName']
4037                    break # ignore all but 1st data histogram
4038                elif hist.startswith("HKLF"):
4039                    instnam = histblk["Instrument Parameters"][0]['InstrName']
4040                    break # ignore all but 1st data histogram
4041        # give the user a window to edit CIF contents
4042        if not self.author:
4043            self.author = self.OverallParms['Controls'].get("Author",'?').strip()
4044        if not self.author:
4045            if not EditAuthor(): return
4046        self.ValidateAscii([('Author name',self.author),]) # check for ASCII strings where needed, warn on problems
4047        self.shortauthorname = self.author.replace(',','').replace(' ','')[:20]
4048        self.cifdefs = wx.Dialog(
4049            self.G2frame,
4050            style=wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER)
4051        self.cifdefs.G2frame = self.G2frame
4052        self.cifdefs.CenterOnParent()
4053        EditCIFDefaults()
4054        if self.cifdefs.ShowModal() != wx.ID_OK:
4055            self.cifdefs.Destroy()
4056            return
4057        while self.ValidateAscii([('Author name',self.author),
4058                                  ]): # validate a few things as ASCII
4059            if self.cifdefs.ShowModal() != wx.ID_OK:
4060                self.cifdefs.Destroy()
4061                return
4062        self.cifdefs.Destroy()
4063        MM = False
4064        for p in self.Phases:
4065            if self.Phases[p]['General']['Type'] == 'macromolecular':
4066                MM = True
4067                break
4068        #======================================================================
4069        # export different types of CIFs below
4070        #======================================================================
4071        print('Writing CIF output to file '+self.filename+"...")
4072        self.openDelayed()
4073        if self.currentExportType == 'single' or self.currentExportType == 'powder':
4074            #======================================================================
4075            #### Data only CIF (powder/xtal) ======================================
4076            #======================================================================
4077            hist = self.histnam[0]
4078            self.CIFname = hist[5:40].replace(' ','')
4079            WriteCIFitem(self.fp, 'data_'+self.CIFname)
4080            WriteCIFitem(self.fp, '_gsas_GSASII_version',
4081                             str(GSASIIpath.GetVersionNumber()))
4082            if hist.startswith("PWDR") and MM:
4083                WritePowderDataMM(hist)
4084            elif hist.startswith("PWDR"):
4085                WritePowderData(hist)
4086            elif hist.startswith("HKLF"):
4087                WriteSingleXtalData(hist)
4088            else:
4089                print ("should not happen")
4090        elif oneblock:
4091            #======================================================================
4092            #### Full (data & phase) single block CIF =============================
4093            #======================================================================
4094            WriteCIFitem(self.fp, 'data_'+self.CIFname)
4095            WriteCIFitem(self.fp, '_gsas_GSASII_version',
4096                             str(GSASIIpath.GetVersionNumber()))
4097            if phasenam is None: # if not already selected, select the first phase (should be one)
4098                phasenam = self.Phases.keys()[0]
4099            #print 'phasenam',phasenam
4100            #phaseblk = self.Phases[phasenam] # pointer to current phase info
4101            instnam = instnam.replace(' ','')
4102            WriteCIFitem(self.fp, '_pd_block_id',
4103                         str(self.CIFdate) + "|" + str(self.CIFname) + "|" +
4104                         str(self.shortauthorname) + "|" + instnam)
4105            WriteAudit()
4106            writeCIFtemplate(self.OverallParms['Controls'],'publ') # overall (publication) template
4107            if MM:
4108                WriteOverallMM()
4109            else:
4110                WriteOverall()
4111            writeCIFtemplate(self.Phases[phasenam]['General'],'phase',phasenam) # write phase template
4112            # report the phase info
4113            if self.Phases[phasenam]['General']['Type'] == 'macromolecular':
4114                WritePhaseInfoMM(phasenam,False)
4115            else:
4116                WritePhaseInfo(phasenam,False)
4117            if hist.startswith("PWDR"):  # this is invoked for single-block CIFs
4118                # preferred orientation
4119                SH = FormatSH(phasenam)
4120                MD = FormatHAPpo(phasenam)
4121                if SH and MD:
4122                    WriteCIFitem(self.fp, '_pd_proc_ls_pref_orient_corr', SH + '\n' + MD)
4123                elif SH or MD:
4124                    WriteCIFitem(self.fp, '_pd_proc_ls_pref_orient_corr', SH + MD)
4125                else:
4126                    WriteCIFitem(self.fp, '_pd_proc_ls_pref_orient_corr', 'none')
4127                # report profile, since one-block: include both histogram and phase info (N.B. there is only 1 of each)
4128                WriteCIFitem(self.fp, '_pd_proc_ls_profile_function',
4129                    FormatInstProfile(histblk["Instrument Parameters"],histblk['hId'])
4130                    +'\n'+FormatPhaseProfile(phasenam))
4131
4132                histblk = self.Histograms[hist]["Sample Parameters"]
4133                writeCIFtemplate(histblk,'powder',histblk['InstrName']) # write powder template
4134                if hist.startswith("PWDR") and MM:
4135                    WritePowderDataMM(hist)
4136                else:
4137                    WritePowderData(hist)
4138            elif hist.startswith("HKLF"):
4139                histprm = self.Histograms[hist]["Instrument Parameters"][0]
4140                writeCIFtemplate(histprm,'single',histprm['InstrName']) # single crystal template
4141                WriteSingleXtalData(hist)
4142        elif seqHistList:
4143            #======================================================================
4144            #### sequential fit export (multiblock)
4145            #======================================================================
4146            for phasenam in sorted(self.Phases.keys()):
4147                rId = phasedict['ranId']
4148                if rId in self.CellHistSelection: continue
4149                self.CellHistSelection[rId] = self._CellSelectHist(phasenam)
4150            nsteps = 1 + len(self.Phases) + len(seqHistList)
4151            try:
4152                dlg = wx.ProgressDialog('CIF progress','starting',nsteps,parent=self.G2frame)
4153                dlg.CenterOnParent()
4154
4155                # publication info block
4156                step = 1
4157                dlg.Update(step,"Exporting overall section")
4158                WriteCIFitem(self.fp, '\ndata_'+self.CIFname+'_publ')
4159                WriteCIFitem(self.fp, '_gsas_GSASII_version',
4160                                str(GSASIIpath.GetVersionNumber()))
4161                WriteAudit()
4162                WriteCIFitem(self.fp, '_pd_block_id',
4163                             str(self.CIFdate) + "|" + str(self.CIFname) + "|" +
4164                             str(self.shortauthorname) + "|Overall")
4165                writeCIFtemplate(self.OverallParms['Controls'],'publ') #insert the publication template
4166                # ``template_publ.cif`` or a modified version
4167               
4168                # overall info block
4169                WriteCIFitem(self.fp, 'data_'+str(self.CIFname)+'_overall')
4170                WriteOverall('seq')
4171                hist = seqHistList[0]
4172                instnam = self.Histograms[hist]["Sample Parameters"]['InstrName']
4173                writeCIFtemplate(self.OverallParms['Controls'],'powder',instnam,
4174                                     cifKey="seqCIF_template") # powder template for all histograms
4175                instnam = instnam.replace(' ','')
4176                #============================================================
4177                if phaseWithHist:
4178                    WriteCIFitem(self.fp, '# POINTERS TO HISTOGRAM BLOCKS (Phase in histogram block)')
4179                else:
4180                    WriteCIFitem(self.fp, '# POINTERS TO HISTOGRAM BLOCKS (Phases pointer in histogram block)') 
4181                datablockidDict = {} # save block names here
4182                # loop over data blocks
4183                WriteCIFitem(self.fp, 'loop_   _pd_block_diffractogram_id')
4184                for hist in seqHistList:
4185                    j = self.Histograms[hist]['hId']
4186                    datablockidDict[hist] = (str(self.CIFdate) + "|" + str(self.CIFname) + "|" +
4187                                             str(self.shortauthorname) + "|" +
4188                                             instnam + "_hist_"+str(j))
4189                    WriteCIFitem(self.fp, '  '+datablockidDict[hist])
4190                # for i in sorted(self.xtalDict.keys()):
4191                #     hist = self.xtalDict[i]
4192                #     histblk = self.Histograms[hist]
4193                #     instnam = histblk["Instrument Parameters"][0]['InstrName']
4194                #     instnam = instnam.replace(' ','')
4195                #     i = histblk['hId']
4196                #     datablockidDict[hist] = (str(self.CIFdate) + "|" + str(self.CIFname) + "|" +
4197                #                              str(self.shortauthorname) + "|" +
4198                #                              instnam + "_hist_"+str(i))
4199                #     WriteCIFitem(self.fp, loopprefix,datablockidDict[hist])
4200                # setup and show sequential results table
4201                tblLabels,tblValues,tblSigs,tblTypes = mkSeqResTable('cif',seqHistList,self.seqData,
4202                                                    self.Phases,self.Histograms,self.Controls)
4203                WriteCIFitem(self.fp, '\n# Sequential results table') # (in case anyone can make sense of it)
4204                WriteCIFitem(self.fp, 'loop_   _gsas_seq_results_col_num _gsas_seq_results_col_label')
4205                for i,lbl in enumerate(tblLabels):
4206                    s = PutInCol(str(i),5)
4207                    if ' ' in lbl:
4208                        s += '"' + lbl + '"'
4209                    else:
4210                        s += lbl
4211                    WriteCIFitem(self.fp,"  "+s)
4212                s = 'loop_ '
4213                linelength = 120
4214                for i in range(len(tblLabels)):
4215                    if len(s) > linelength:
4216                        WriteCIFitem(self.fp,s)
4217                        s = '  '
4218                    s += " _gsas_seq_results_val" + str(i)
4219                WriteCIFitem(self.fp,s)
4220               
4221                for r in range(len(tblValues[0])):
4222                    s = ''
4223                    for c in range(len(tblLabels)):
4224                        if len(s) > linelength:
4225                            WriteCIFitem(self.fp,s)
4226                            s = '  '
4227                        sig = None
4228                        if tblSigs[c] is not None:
4229                            sig = tblSigs[c][r]
4230                           
4231                        if tblValues[c][r] is None:
4232                            if tblTypes[c] == 'int':
4233                                wid = 5
4234                            elif tblTypes[c] == 'str':
4235                                wid = 10
4236                            else:
4237                                wid = 12                               
4238                            s += PutInCol('.',wid)
4239                        elif sig is None and ',' in tblTypes[c]:
4240                            s += PutInCol(
4241                                ('{{:{}.{}f}}'.format(*tblTypes[c].split(','))).format(tblValues[c][r]),12)
4242                        elif tblTypes[c] == 'int':
4243                            s += PutInCol(str(tblValues[c][r]),5)
4244                        elif tblTypes[c] == 'str':
4245                            s += PutInCol(str(tblValues[c][r]),10)
4246                        elif sig is None and ',' in tblTypes[c]:
4247                            s += PutInCol(
4248                                ('{{:{}.{}f}}'.format(*tblTypes[c].split(','))).format(tblValues[c][r]),12)
4249                        elif sig is None and tblTypes[c] == 'float':
4250                            s += PutInCol('{:.6g}'.format(tblValues[c][r]),12)
4251                        elif sig:
4252                            s += PutInCol(G2mth.ValEsd(tblValues[c][r],sig),12)
4253                        else:
4254                            s += PutInCol(str(tblValues[c][r]),15)
4255                    WriteCIFitem(self.fp,s+'\n')
4256
4257                # sample template info: a block for each phase in project
4258                histblk = self.Histograms[seqHistList[0]]
4259                if phaseWithHist:    # include sample info in overall block
4260                    step += 1
4261                    dlg.Update(step,"Exporting phase")
4262                    phasenam = list(self.Phases.keys())[0]
4263                    writeCIFtemplate(self.Phases[phasenam]['General'],'phase',phasenam) # write phase template
4264                    WriteSeqOverallPhaseInfo(phasenam,histblk)
4265                else:
4266                    for j,phasenam in enumerate(sorted(self.Phases.keys())):
4267                        pId = self.Phases[phasenam]['pId']
4268                        step += 1
4269                        dlg.Update(step,"Exporting phase {}".format(pId))
4270                        WriteCIFitem(self.fp, '\n#'+78*'=')
4271                        WriteCIFitem(self.fp, 'data_'+self.CIFname+"_overall_phase"+str(j)+'\n')
4272                        writeCIFtemplate(self.Phases[phasenam]['General'],'phase',phasenam) # write phase template
4273                        WriteSeqOverallPhaseInfo(phasenam,histblk)
4274
4275                # create a block for each histogram, include phase in block for one-phase refinements
4276                # or separate blocks for each phase & histogram if more than one phase
4277                for i,hist in enumerate(seqHistList):
4278                    print('processing hist #',i,'hId=',self.Histograms[hist]['hId'],hist)
4279                    hId = self.Histograms[hist]['hId']
4280                    step += 1
4281                    dlg.Update(step,"Exporting "+hist.strip())
4282                    histblk = self.Histograms[hist]
4283                    WriteCIFitem(self.fp, '# Information for histogram '+str(i)+': '+hist)
4284                    WriteCIFitem(self.fp, '\ndata_'+self.CIFname+"_pwd_"+str(i))
4285                    WriteCIFitem(self.fp, '_pd_block_id',datablockidDict[hist])
4286                    if not phaseWithHist:
4287                        WriteCIFitem(self.fp, '\n# POINTERS TO PHASE BLOCKS')
4288                        phaseBlockName = {}
4289                        WriteCIFitem(self.fp, 'loop_ _pd_phase_id _pd_phase_block_id _pd_phase_mass_%')
4290                        for j,phasenam in enumerate(sorted(self.Phases.keys())):
4291                            pId = self.Phases[phasenam]['pId']
4292                            if hist not in self.Phases[phasenam]['Histograms']: continue
4293                            if not self.Phases[phasenam]['Histograms'][hist]['Use']: continue
4294                            if ' ' in phasenam:
4295                                s = PutInCol('"'+phasenam+'"',20)
4296                            else:
4297                                s = PutInCol(phasenam,20)
4298                            phaseBlockName[pId] = datablockidDict[hist]+'_p'+str(j+1)
4299                            var = str(pId)+':'+str(hId)+':WgtFrac'
4300                            if var in self.seqData[hist].get('depParmDict',{}):
4301                                wtFr,sig = self.seqData[hist]['depParmDict'][var]
4302                                wgtstr = G2mth.ValEsd(wtFr,sig)
4303                            else:                               
4304                                wgtstr = '?'
4305                            WriteCIFitem(self.fp, "  "+ s + " " + phaseBlockName[pId] + "  " + wgtstr)
4306                            datablockidDict[phasenam] = phaseBlockName[pId]
4307                        PP = FormatInstProfile(histblk["Instrument Parameters"],histblk['hId'])
4308                        PP += '\n'
4309                        WriteCIFitem(self.fp, '_pd_proc_ls_profile_function',PP)
4310               
4311                    WritePowderData(hist,seq=True) # write background, data & reflections, some instrument & sample terms
4312                    writeCIFtemplate(self.OverallParms['Controls'],'powder',
4313                                         self.Histograms[hist]["Sample Parameters"]['InstrName'],
4314                                         cifKey="seqCIF_template") # powder template for all histograms
4315                    WriteCIFitem(self.fp, '\n# PHASE INFO FOR HISTOGRAM '+hist)
4316                    # loop over phases, add a block header if there is more than one phase
4317                    for j,phasenam in enumerate(sorted(self.Phases.keys())):
4318                        pId = self.Phases[phasenam]['pId']
4319                        if hist not in self.Phases[phasenam]['Histograms']: continue
4320                        if not self.Phases[phasenam]['Histograms'][hist]['Use']: continue
4321                        WriteCIFitem(self.fp, '\n# phase info for '+str(phasenam) + ' follows')
4322                        if not phaseWithHist:
4323                            WriteCIFitem(self.fp, 'data_'+self.CIFname+"_hist"+str(i)+"_phase"+str(j))
4324                            WriteCIFitem(self.fp, '_pd_block_id',phaseBlockName[pId])
4325                            WriteCIFitem(self.fp, '')
4326
4327                        WriteSeqPhaseVals(phasenam,self.Phases[phasenam],pId,hist)
4328
4329                        # preferred orientation & profile terms
4330                        if self.ifPWDR:
4331                            #SH = FormatSH(phasenam)     # TODO: needs to use seqData
4332                            #MD = FormatHAPpo(phasenam)  # TODO: switch to seqData
4333                            #if SH and MD:
4334                            #    WriteCIFitem(self.fp, '_pd_proc_ls_pref_orient_corr', SH + '\n' + MD)
4335                            #elif SH or MD:
4336                            #    WriteCIFitem(self.fp, '_pd_proc_ls_pref_orient_corr', SH + MD)
4337                            #else:
4338                            #    WriteCIFitem(self.fp, '_pd_proc_ls_pref_orient_corr', 'none')
4339                            # report sample profile terms for all histograms with current phase
4340                            if phaseWithHist:
4341                                PP = FormatInstProfile(histblk["Instrument Parameters"],histblk['hId'])
4342                                PP += '\n'
4343                            else:
4344                                PP = ''
4345                            PP += FormatPhaseProfile(phasenam,hist)
4346                            WriteCIFitem(self.fp, '\n_pd_proc_ls_profile_function',PP)
4347            finally:
4348                dlg.Destroy()
4349        else:
4350            #======================================================================
4351            #### multiblock: multiple phases and/or histograms export
4352            #======================================================================
4353            oneblock = False
4354            for phasenam in sorted(self.Phases.keys()):
4355                rId = phasedict['ranId']
4356                if rId in self.CellHistSelection: continue
4357                self.CellHistSelection[rId] = self._CellSelectHist(phasenam)
4358            nsteps = 1 + len(self.Phases) + len(self.powderDict) + len(self.xtalDict)
4359            try:
4360                dlg = wx.ProgressDialog('CIF progress','starting',nsteps,parent=self.G2frame)
4361                dlg.CenterOnParent()
4362
4363                # publication info
4364                step = 1
4365                dlg.Update(step,"Exporting overall section")
4366                WriteCIFitem(self.fp, '\ndata_'+self.CIFname+'_publ')
4367                WriteCIFitem(self.fp, '_gsas_GSASII_version',
4368                                str(GSASIIpath.GetVersionNumber()))
4369                WriteAudit()
4370                WriteCIFitem(self.fp, '_pd_block_id',
4371                             str(self.CIFdate) + "|" + str(self.CIFname) + "|" +
4372                             str(self.shortauthorname) + "|PubInfo")
4373                writeCIFtemplate(self.OverallParms['Controls'],'publ') #insert the publication template
4374                # ``template_publ.cif`` or a modified version
4375               
4376                # overall info -- it is not strictly necessary to separate this from the previous
4377                # publication block, but I think this makes sense
4378               
4379                WriteCIFitem(self.fp, 'data_'+str(self.CIFname)+'_overall')
4380                WriteCIFitem(self.fp, '_pd_block_id',
4381                             str(self.CIFdate) + "|" + str(self.CIFname) + "|" +
4382                             str(self.shortauthorname) + "|Overall")
4383                if MM:
4384                    WriteOverallMM()
4385                else:
4386                    WriteOverall()
4387                #============================================================
4388                WriteCIFitem(self.fp, '# POINTERS TO PHASE AND/OR HISTOGRAM BLOCKS')
4389                datablockidDict = {} # save block names here -- N.B. check for conflicts between phase & hist names (unlikely!)
4390                # loop over phase blocks
4391                if len(self.Phases) > 1:
4392                    loopprefix = ''
4393                    WriteCIFitem(self.fp, 'loop_   _pd_phase_block_id')
4394                    for phasenam in sorted(self.Phases.keys()):
4395                        i = self.Phases[phasenam]['pId']
4396                        datablockidDict[phasenam] = (str(self.CIFdate) + "|" + str(self.CIFname) + "|" +
4397                                                         'phase_'+ str(i) + '|' + str(self.shortauthorname))
4398                        WriteCIFitem(self.fp, loopprefix,datablockidDict[phasenam])
4399                else:    # phase in overall block
4400                    for phasenam in sorted(self.Phases.keys()): break
4401                    datablockidDict[phasenam] = (str(self.CIFdate) + "|" + str(self.CIFname) + "|" +
4402                                                     str(self.shortauthorname) + "|Overall")
4403                # loop over data blocks
4404                if len(self.powderDict) + len(self.xtalDict) > 1:
4405                    loopprefix = ''
4406                    WriteCIFitem(self.fp, 'loop_   _pd_block_diffractogram_id')
4407                else:
4408                    loopprefix = '_pd_block_diffractogram_id'
4409                for i in sorted(self.powderDict.keys()):
4410                    hist = self.powderDict[i]
4411                    histblk = self.Histograms[hist]
4412                    instnam = histblk["Sample Parameters"]['InstrName']
4413                    instnam = instnam.replace(' ','')
4414                    j = histblk['hId']
4415                    datablockidDict[hist] = (str(self.CIFdate) + "|" + str(self.CIFname) + "|" +
4416                                             str(self.shortauthorname) + "|" +