source: trunk/exports/G2cif.py @ 1067

Last change on this file since 1067 was 1067, checked in by toby, 10 years ago

CIF updates

  • Property svn:eol-style set to native
File size: 95.1 KB
Line 
1#!/usr/bin/env python
2# -*- coding: utf-8 -*-
3#G2cif
4########### SVN repository information ###################
5# $Date: 2013-07-22 20:57:37 -0500 (Mon, 22 Jul 2013) $
6# $Author: toby $
7# $Revision: 1006 $
8# $URL: https://subversion.xray.aps.anl.gov/pyGSAS/trunk/exports/G2cif.py $
9# $Id: G2cif.py 1006 2013-07-23 01:57:37Z toby $
10########### SVN repository information ###################
11'''Code to export a GSAS-II project as a CIF
12The heavy lifting is done in method export
13'''
14
15# TODO: set def names for phase/hist save & load, bond pub flags,...
16
17import datetime as dt
18import os.path
19import sys
20import numpy as np
21import cPickle
22import copy
23import wx
24import wx.lib.scrolledpanel as wxscroll
25import wx.lib.resizewidget as rw
26import GSASIIpath
27GSASIIpath.SetVersionNumber("$Revision: 1006 $")
28import GSASIIIO as G2IO
29#reload(G2IO)
30import GSASIIgrid as G2gd
31reload(G2gd)
32import GSASIIstrIO as G2stIO
33#reload(G2stIO)
34#import GSASIImapvars as G2mv
35import GSASIImath as G2mth
36#reload(G2mth)
37import GSASIIlattice as G2lat
38import GSASIIspc as G2spc
39#reload(G2spc)
40import GSASIIphsGUI as G2pg
41#reload(G2pg)
42import GSASIIstrMain as G2stMn
43#reload(G2stMn)
44
45DEBUG = False    #True to skip printing of reflection/powder profile lists
46
47CIFdic = None
48
49def getCallerDocString(): # for development
50    "Return the calling function's doc string"
51    import inspect as ins
52    for item in ins.stack()[1][0].f_code.co_consts:
53        if type(item) is str:
54            return item
55    else:
56        return '?'
57
58#===============================================================================
59# misc CIF utilities
60#===============================================================================
61def PickleCIFdict(fil):
62    '''Loads a CIF dictionary, cherry picks out the items needed
63    by local code and sticks them into a python dict and writes
64    that dict out as a cPickle file for later reuse.
65    If the write fails a warning message is printed,
66    but no exception occurs.
67
68    :param str fil: file name of CIF dictionary, will usually end
69      in .dic
70    :returns: the dict with the definitions 
71    '''
72    import CifFile as cif # PyCifRW from James Hester
73    cifdic = {}
74    dictobj = cif.CifDic(fil)
75    if DEBUG: print('loaded '+str(fil))
76    for item in dictobj.keys():
77        cifdic[item] = {}
78        for j in (
79            '_definition','_type',
80            '_enumeration',
81            '_enumeration_detail',
82            '_enumeration_range'):
83            if dictobj[item].get(j):
84                cifdic[item][j] = dictobj[item][j]
85    try:
86        fil = os.path.splitext(fil)[0]+'.cpickle'
87        fp = open(fil,'w')
88        cPickle.dump(cifdic,fp)
89        fp.close()
90        if DEBUG: print('wrote '+str(fil))
91    except:
92        print ('Unable to write '+str(fil))
93    return cifdic
94
95def LoadCIFdic():
96    '''Create a composite core+powder CIF lookup dict containing
97    information about all items in the CIF dictionaries, loading
98    pickled files if possible. The routine looks for files
99    named cif_core.cpickle and cif_pd.cpickle in every
100    directory in the path and if they are not found, files
101    cif_core.dic and/or cif_pd.dic are read.
102
103    :returns: the dict with the definitions 
104    '''
105    cifdic = {}
106    for ftyp in "cif_core","cif_pd":
107        for loc in sys.path:
108            fil = os.path.join(loc,ftyp+".cpickle")
109            if not os.path.exists(fil): continue
110            fp = open(fil,'r')
111            try:
112                cifdic.update(cPickle.load(fp))
113                if DEBUG: print('reloaded '+str(fil))
114                break
115            finally:
116                fp.close()
117        else:
118            for loc in sys.path:
119                fil = os.path.join(loc,ftyp+".dic")
120                if not os.path.exists(fil): continue
121                #try:
122                if True:
123                    cifdic.update(PickleCIFdict(fil))
124                    break
125                #except:
126                #    pass
127            else:
128                print('Could not load '+ftyp+' dictionary')
129    return cifdic
130
131class CIFdefHelp(wx.Button):
132    '''Create a help button that displays help information on
133    the current data item
134
135    :param parent: the panel which will be the parent of the button
136    :param str msg: the help text to be displayed
137    :param wx.Dialog helpwin: Frame for CIF editing dialog
138    :param wx.TextCtrl helptxt: TextCtrl where help text is placed
139    '''
140    def __init__(self,parent,msg,helpwin,helptxt):
141        wx.Button.__init__(self,parent,wx.ID_HELP)
142        self.Bind(wx.EVT_BUTTON,self._onPress)
143        self.msg=msg
144        self.parent = parent
145        #self.helpwin = self.parent.helpwin
146        self.helpwin = helpwin
147        self.helptxt = helptxt
148    def _onPress(self,event):
149        'Respond to a button press by displaying the requested text'
150        try:
151            #helptxt = self.helptxt
152            ow,oh = self.helptxt.GetSize()
153            self.helptxt.SetLabel(self.msg)
154            w,h = self.helptxt.GetSize()
155            if h > oh:
156                self.helpwin.GetSizer().Fit(self.helpwin)
157        except: # error posting help, ignore
158            return
159
160def CIF2dict(cf):
161    '''copy the contents of a CIF out from a PyCifRW block object
162    into a dict
163
164    :returns: cifblk, loopstructure where cifblk is a dict with
165      CIF items and loopstructure is a list of lists that defines
166      which items are in which loops.
167    '''
168    blk = cf.keys()[0] # assume templates are a single CIF block, use the 1st
169    loopstructure = cf[blk].loopnames()[:] # copy over the list of loop contents
170    dblk = {}
171    for item in cf[blk].keys(): # make a copy of all the items in the block
172        dblk[item] = cf[blk][item]
173    return dblk,loopstructure
174
175def dict2CIF(dblk,loopstructure,blockname='Template'):
176    '''Create a PyCifRW CIF object containing a single CIF
177    block object from a dict and loop structure list.
178
179    :param dblk: a dict containing values for each CIF item
180    :param list loopstructure: a list of lists containing the contents of
181      each loop, as an example::
182
183         [ ["_a","_b"], ["_c"], ["_d_1","_d_2","_d_3"]]
184
185      this describes a CIF with this type of structure::
186
187        loop_ _a _b <a1> <b1> <a2> ...
188        loop_ _c <c1> <c2>...
189        loop _d_1 _d_2 _d_3 ...
190
191      Note that the values for each looped CIF item, such as _a,
192      are contained in a list, for example as cifblk["_a"]
193
194    :param str blockname: an optional name for the CIF block.
195      Defaults to 'Template'
196
197    :returns: the newly created PyCifRW CIF object
198    '''
199
200    import CifFile as cif # PyCifRW from James Hester
201    # compile a 'list' of items in loops
202    loopnames = set()
203    for i in loopstructure:
204        loopnames |= set(i)
205    # create a new block
206    newblk = cif.CifBlock()
207    # add the looped items
208    for keys in loopstructure:
209        vals = []
210        for key in keys:
211            vals.append(dblk[key])
212        newblk.AddCifItem(([keys],[vals]))
213    # add the non-looped items
214    for item in dblk:
215        if item in loopnames: continue
216        newblk[item] = dblk[item]
217    # create a CIF and add the block
218    newcf = cif.CifFile()
219    newcf[blockname] = newblk   
220    return newcf
221
222
223class EditCIFtemplate(wx.Dialog):
224    '''Create a dialog for editing a CIF template
225   
226    :param wx.Frame parent: parent frame or None
227    :param cifblk: dict or PyCifRW block containing values for each CIF item
228    :param list loopstructure: a list of lists containing the contents of
229      each loop, as an example::
230
231         [ ["_a","_b"], ["_c"], ["_d_1","_d_2","_d_3"]]
232
233      this describes a CIF with this type of structure::
234
235        loop_ _a _b <a1> <b1> <a2> ...
236        loop_ _c <c1> <c2>...
237        loop _d_1 _d_2 _d_3 ...
238
239      Note that the values for each looped CIF item, such as _a,
240      are contained in a list, for example as cifblk["_a"]
241    '''
242    def __init__(self,parent,cifblk,loopstructure):
243        OKbuttons = []
244        self.cifblk = cifblk
245        self.loopstructure = loopstructure
246        self.newfile = None
247        global CIFdic  # once this is loaded, keep it around
248        if CIFdic is None:
249            CIFdic = LoadCIFdic()
250        wx.Dialog.__init__(self,parent,style=wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER)
251
252        # define widgets that will be needed during panel creation
253        self.helptxt = wx.StaticText(self,wx.ID_ANY,"")
254        savebtn = wx.Button(self, wx.ID_CLOSE, "Save as template")
255        OKbuttons.append(savebtn)
256        savebtn.Bind(wx.EVT_BUTTON,self._onClose)
257        OKbtn = wx.Button(self, wx.ID_OK, "Use")
258        OKbtn.SetDefault()
259        OKbuttons.append(OKbtn)
260
261        self.SetTitle('Edit items in CIF template')
262        vbox = wx.BoxSizer(wx.VERTICAL)
263        cpnl = EditCIFpanel(self,cifblk,loopstructure,CIFdic,OKbuttons,size=(300,300))
264        vbox.Add(cpnl, 1, wx.ALIGN_LEFT|wx.ALL|wx.EXPAND, 0)
265        G2gd.HorizontalLine(vbox,self)
266        vbox.Add(self.helptxt, 0, wx.EXPAND|wx.ALL, 5)
267        G2gd.HorizontalLine(vbox,self)
268        btnsizer = wx.BoxSizer(wx.HORIZONTAL)
269        btn = wx.Button(self, wx.ID_CANCEL)
270        btnsizer.Add(btn,0,wx.ALIGN_CENTER|wx.ALL)
271        btnsizer.Add(savebtn,0,wx.ALIGN_CENTER|wx.ALL)
272        btnsizer.Add(OKbtn,0,wx.ALIGN_CENTER|wx.ALL)
273        vbox.Add(btnsizer, 0, wx.ALIGN_CENTER|wx.ALL, 5)
274        self.SetSizer(vbox)
275        vbox.Fit(self)
276    def Post(self):
277        '''Display the dialog
278       
279        :returns: True, unless Cancel has been pressed.
280        '''
281        return (self.ShowModal() == wx.ID_OK)
282    def _onClose(self,event):
283        'Save CIF entries in a template file'
284        dlg = wx.FileDialog(
285            self, message="Save as CIF template",
286            defaultDir=os.getcwd(),
287            defaultFile="",
288            wildcard="CIF (*.cif)|*.cif",
289            style=wx.SAVE | wx.CHANGE_DIR
290            )
291        val = (dlg.ShowModal() == wx.ID_OK)
292        fil = dlg.GetPath()
293        dlg.Destroy()
294        if val: # ignore a Cancel button
295            fil = os.path.splitext(fil)[0]+'.cif' # force extension
296            fp = open(fil,'w')
297            newcf = dict2CIF(self.cifblk,self.loopstructure)
298            fp.write(newcf.WriteOut())
299            fp.close()
300            self.newfile = fil
301            self.EndModal(wx.ID_OK)
302
303class EditCIFpanel(wxscroll.ScrolledPanel):
304    '''Creates a scrolled panel for editing CIF template items
305
306    :param wx.Frame parent: parent frame where panel will be placed
307    :param cifblk: dict or PyCifRW block containing values for each CIF item
308    :param list loopstructure: a list of lists containing the contents of
309      each loop, as an example::
310
311         [ ["_a","_b"], ["_c"], ["_d_1","_d_2","_d_3"]]
312
313      this describes a CIF with this type of structure::
314
315        loop_ _a _b <a1> <b1> <a2> ...
316        loop_ _c <c1> <c2>...
317        loop _d_1 _d_2 _d_3 ...
318
319      Note that the values for each looped CIF item, such as _a,
320      are contained in a list, for example as cifblk["_a"]
321
322    :param dict cifdic: optional CIF dictionary definitions
323    :param (other): optional keyword parameters for wx.ScrolledPanel
324    '''
325    def __init__(self, parent, cifblk, loopstructure, cifdic={}, OKbuttons=[], **kw):
326        self.parent = parent
327        wxscroll.ScrolledPanel.__init__(self, parent, wx.ID_ANY, **kw)
328        self.vbox = None
329        self.AddDict = None
330        self.cifdic = cifdic
331        self.cifblk = cifblk
332        self.loops = loopstructure
333        self.parent = parent
334        self.LayoutCalled = False
335        self.parentOKbuttons = OKbuttons
336        self.ValidatedControlsList = []
337        self._fill()
338    def _fill(self):
339        'Fill the scrolled panel with widgets for each CIF item'
340        wx.BeginBusyCursor()
341        self.AddDict = {}
342        self.ValidatedControlsList = []
343        # delete any only contents
344        if self.vbox:
345            self.vbox.DeleteWindows()
346            self.vbox = None
347            self.Update()
348        vbox = wx.BoxSizer(wx.VERTICAL)
349        self.vbox = vbox
350        # compile a 'list' of items in loops
351        loopnames = set()
352        for i in self.loops:
353            loopnames |= set(i)
354        # post the looped CIF items
355        for lnum,lp in enumerate(self.loops):
356            hbox = wx.BoxSizer(wx.HORIZONTAL)
357            hbox.Add(wx.StaticText(self,wx.ID_ANY,'Loop '+str(lnum+1)))
358            vbox.Add(hbox)
359            but = wx.Button(self,wx.ID_ANY,"Add row")
360            self.AddDict[but]=lnum
361           
362            hbox.Add(but)           
363            but.Bind(wx.EVT_BUTTON,self.OnAddRow)
364            fbox = wx.GridBagSizer(0, 0)
365            vbox.Add(fbox)
366            rows = 0
367            for i,item in enumerate(lp):
368                txt = wx.StaticText(self,wx.ID_ANY,item+"  ")
369                fbox.Add(txt,(0,i+1))
370                if self.cifdic.get(item):
371                    df = self.cifdic[item].get('_definition')
372                    if df:
373                        txt.SetToolTipString(G2IO.trim(df))
374                        but = CIFdefHelp(self,
375                                         "Definition for "+item+":\n\n"+G2IO.trim(df),
376                                         self.parent,
377                                         self.parent.helptxt)
378                        fbox.Add(but,(1,i+1),flag=wx.ALIGN_CENTER)
379                for j,val in enumerate(self.cifblk[item]):
380                    ent = self.CIFEntryWidget(self.cifblk[item],j,item)
381                    fbox.Add(ent,(j+2,i+1),flag=wx.EXPAND|wx.ALL)
382                rows = max(rows,len(self.cifblk[item]))
383            for i in range(rows):
384                txt = wx.StaticText(self,wx.ID_ANY,str(i+1))
385                fbox.Add(txt,(i+2,0))
386            line = wx.StaticLine(self,wx.ID_ANY, size=(-1,3), style=wx.LI_HORIZONTAL)
387            vbox.Add(line, 0, wx.EXPAND|wx.ALIGN_CENTER|wx.ALL, 10)
388               
389        # post the non-looped CIF items
390        for item in sorted(self.cifblk.keys()):
391            if item not in loopnames:
392                hbox = wx.BoxSizer(wx.HORIZONTAL)
393                vbox.Add(hbox)
394                txt = wx.StaticText(self,wx.ID_ANY,item)
395                hbox.Add(txt)
396                if self.cifdic.get(item):
397                    df = self.cifdic[item].get('_definition')
398                    if df:
399                        txt.SetToolTipString(G2IO.trim(df))
400                        but = CIFdefHelp(self,
401                                         "Definition for "+item+":\n\n"+G2IO.trim(df),
402                                         self.parent,
403                                         self.parent.helptxt)
404                        hbox.Add(but,0,wx.ALL,2)
405                ent = self.CIFEntryWidget(self.cifblk,item,item)
406                hbox.Add(ent)
407        self.SetSizer(vbox)
408        #vbox.Fit(self.parent)
409        self.SetAutoLayout(1)
410        self.SetupScrolling()
411        self.Bind(rw.EVT_RW_LAYOUT_NEEDED, self.OnLayoutNeeded)
412        self.Layout()
413        wx.EndBusyCursor()
414    def OnLayoutNeeded(self,event):
415        '''Called when an update of the panel layout is needed. Calls
416        self.DoLayout after the current operations are complete using
417        CallAfter. This is called only once, according to flag
418        self.LayoutCalled, which is cleared in self.DoLayout.
419        '''
420        if self.LayoutCalled: return # call already queued
421        wx.CallAfter(self.DoLayout) # queue a call
422        self.LayoutCalled = True
423    def DoLayout(self):
424        '''Update the Layout and scroll bars for the Panel. Clears
425        self.LayoutCalled so that next change to panel can
426        request a new update
427        '''
428        wx.BeginBusyCursor()
429        self.Layout()
430        self.SetupScrolling()
431        wx.EndBusyCursor()
432        self.LayoutCalled = False
433    def OnAddRow(self,event):
434        'add a row to a loop'
435        lnum = self.AddDict.get(event.GetEventObject())
436        if lnum is None: return
437        for item in self.loops[lnum]:
438            self.cifblk[item].append('?')
439        self._fill()
440
441    def ControlOKButton(self,setvalue):
442        '''Enable or Disable the OK button(s) for the dialog. Note that this is
443        passed into the ValidatedTxtCtrl for use by validators.
444
445        :param bool setvalue: if True, all entries in the dialog are
446          checked for validity. The first invalid control triggers
447          disabling of buttons.
448          If False then the OK button(s) are disabled with no checking
449          of the invalid flag for each control.
450        '''
451        if setvalue: # turn button on, do only if all controls show as valid
452            for ctrl in self.ValidatedControlsList:
453                if ctrl.invalid:
454                    for btn in self.parentOKbuttons:
455                        btn.Disable()
456                    return
457            else:
458                for btn in self.parentOKbuttons:
459                    btn.Enable()
460        else:
461            for btn in self.parentOKbuttons:
462                btn.Disable()
463       
464    def CIFEntryWidget(self,dct,item,dataname):
465        '''Create an entry widget for a CIF item. Use a validated entry for numb values
466        where int is required when limits are integers and floats otherwise.
467        At present this does not allow entry of the special CIF values of "." and "?" for
468        numerical values and highlights them as invalid.
469        Use a selection widget when there are specific enumerated values for a string.       
470        '''
471        if self.cifdic.get(dataname):
472            if self.cifdic[dataname].get('_enumeration'):
473                values = ['?']+self.cifdic[dataname]['_enumeration']
474                choices = ['undefined']
475                for i in self.cifdic[dataname].get('_enumeration_detail',values):
476                    choices.append(G2IO.trim(i))
477                ent = G2gd.EnumSelector(self, dct, item, choices, values, size=(200, -1))
478                return ent
479            if self.cifdic[dataname].get('_type') == 'numb':
480                mn = None
481                mx = None
482                hint = int
483                if self.cifdic[dataname].get('_enumeration_range'):
484                    rng = self.cifdic[dataname]['_enumeration_range'].split(':')
485                    if '.' in rng[0] or '.' in rng[1]: hint = float
486                    if rng[0]: mn = hint(rng[0])
487                    if rng[1]: mx = hint(rng[1])
488                    ent = G2gd.ValidatedTxtCtrl(
489                        self,dct,item,typeHint=hint,min=mn,max=mx,
490                        CIFinput=True,
491                        OKcontrol=self.ControlOKButton)
492                    self.ValidatedControlsList.append(ent)
493                    return ent
494        rw1 = rw.ResizeWidget(self)
495        #print item
496        #print dct[item]
497        ent = G2gd.ValidatedTxtCtrl(
498            rw1,dct,item,size=(100, 20),
499            style=wx.TE_MULTILINE|wx.TE_PROCESS_ENTER,
500            CIFinput=True,
501            OKcontrol=self.ControlOKButton)
502        self.ValidatedControlsList.append(ent)
503        return rw1
504
505class CIFtemplateSelect(wx.BoxSizer):
506    '''Create a set of buttons to show, select and edit a CIF template
507
508    :param str tmplate: one of 'publ', 'phase', or 'instrument' to determine
509      the type of template
510    '''
511    def __init__(self,frame,panel,tmplate,G2dict, repaint, title):
512        wx.BoxSizer.__init__(self,wx.VERTICAL)
513        self.cifdefs = frame
514        self.dict = G2dict
515        self.repaint = repaint
516        self.fil = 'template_'+tmplate+'.cif'
517        self.CIF = G2dict.get("CIF_template")
518        txt = wx.StaticText(panel,wx.ID_ANY,title)
519        self.Add(txt,0,wx.ALIGN_CENTER)
520        # change font on title
521        txtfnt = txt.GetFont()
522        txtfnt.SetWeight(wx.BOLD)
523        txtfnt.SetPointSize(2+txtfnt.GetPointSize())
524        txt.SetFont(txtfnt)
525        self.Add((-1,3))
526
527        if not self.CIF: # empty or None
528            for pth in sys.path:
529                if os.path.exists(os.path.join(pth,self.fil)):
530                    self.CIF = os.path.join(pth,self.fil)
531                    CIFtxt = "Template: "+self.fil
532                    break
533            else:
534                print CIF+' not found in path!'
535                self.CIF = None
536                CIFtxt = "none! (No template found)"
537        elif type(self.CIF) is not list and type(self.CIF) is not tuple:
538            if not os.path.exists(self.CIF):
539                print("Error: template file has disappeared:"+self.CIF)
540                self.CIF = None
541                CIFtxt = "none! (file not found)"
542            else:
543                if len(self.CIF) < 40:
544                    CIFtxt = "File: "+self.CIF
545                else:
546                    CIFtxt = "File: ..."+self.CIF[-40:]
547        else:
548            CIFtxt = "Template is customized"
549        # show template source
550        self.Add(wx.StaticText(panel,wx.ID_ANY,CIFtxt))
551        # show str, button to select file; button to edit (if CIF defined)
552        but = wx.Button(panel,wx.ID_ANY,"Select Template File")
553        but.Bind(wx.EVT_BUTTON,self.onGetTemplateFile)
554        hbox =  wx.BoxSizer(wx.HORIZONTAL)
555        hbox.Add(but,0,0,2)
556        but = wx.Button(panel,wx.ID_ANY,"Edit Template")
557        but.Bind(wx.EVT_BUTTON,self.onEditTemplateContents)
558        hbox.Add(but,0,0,2)
559        #self.Add(hbox,0,wx.ALIGN_CENTER)
560        self.Add(hbox)
561    def onGetTemplateFile(self,event):
562        dlg = wx.FileDialog(
563            self.cifdefs, message="Save as CIF template",
564            defaultDir=os.getcwd(),
565            defaultFile="",
566            wildcard="CIF (*.cif)|*.cif",
567            style=wx.OPEN | wx.CHANGE_DIR
568            )
569        if dlg.ShowModal() == wx.ID_OK:
570            self.dict["CIF_template"] = dlg.GetPath()
571            dlg.Destroy()           
572            self.repaint() #EditCIFDefaults()
573        else:
574            dlg.Destroy()
575
576    def onEditTemplateContents(self,event):
577        import CifFile as cif # PyCifRW from James Hester
578        if type(self.CIF) is list or  type(self.CIF) is tuple:
579            dblk,loopstructure = copy.deepcopy(self.CIF) # don't modify original
580        else:
581            dblk,loopstructure = CIF2dict(cif.ReadCif(self.CIF))
582        dlg = EditCIFtemplate(self.cifdefs,dblk,loopstructure)
583        val = dlg.Post()
584        if val:
585            if dlg.newfile: # results saved in file
586                self.dict["CIF_template"] = dlg.newfile
587                print 'saved'
588            else:
589                self.dict["CIF_template"] = [dlg.cifblk,dlg.loopstructure]
590                print 'edited'
591            self.repaint() #EditCIFDefaults() # note that this does a dlg.Destroy()
592        else:
593            print 'cancelled'
594            dlg.Destroy()       
595
596#===============================================================================
597# end of misc CIF utilities
598#===============================================================================
599
600class ExportCIF(G2IO.ExportBaseclass):
601    def __init__(self,G2frame):
602        super(self.__class__,self).__init__( # fancy way to say <parentclass>.__init__
603            G2frame=G2frame,
604            formatName = 'full CIF',
605            extension='.cif',
606            longFormatName = 'Export project as CIF'
607            )
608        self.author = ''
609
610    def export(self,mode='full'):
611        '''Export a CIF
612
613        :param str mode: "full" (default) to create a complete CIF of project,
614          "simple" for a simple CIF with only coordinates
615        '''
616   
617# ===== define functions for export method =======================================
618        def openCIF(filnam):
619            if DEBUG:
620                self.fp = sys.stdout
621            else:
622                self.fp = open(filnam,'w')
623
624        def closeCIF():
625            if not DEBUG:
626                self.fp.close()
627           
628        def WriteCIFitem(name,value=''):
629            if value:
630                if "\n" in value or len(value)> 70:
631                    if name.strip(): self.fp.write(name+'\n')
632                    self.fp.write('; '+value+'\n')
633                    self.fp.write('; '+'\n')
634                elif " " in value:
635                    if len(name)+len(value) > 65:
636                        self.fp.write(name + '\n   ' + '"' + str(value) + '"'+'\n')
637                    else:
638                        self.fp.write(name + '  ' + '"' + str(value) + '"'+'\n')
639                else:
640                    if len(name)+len(value) > 65:
641                        self.fp.write(name+'\n   ' + value+'\n')
642                    else:
643                        self.fp.write(name+'  ' + value+'\n')
644            else:
645                self.fp.write(name+'\n')
646
647        def WriteAudit():
648            WriteCIFitem('_audit_creation_method',
649                         'created in GSAS-II')
650            WriteCIFitem('_audit_creation_date',self.CIFdate)
651            if self.author:
652                WriteCIFitem('_audit_author_name',self.author)
653            WriteCIFitem('_audit_update_record',
654                         self.CIFdate+'  Initial software-generated CIF')
655
656        def WriteOverall():
657            '''Write out overall refinement information.
658
659            More could be done here, but this is a good start.
660            '''
661            WriteCIFitem('_pd_proc_info_datetime', self.CIFdate)
662            WriteCIFitem('_pd_calc_method', 'Rietveld Refinement')
663            #WriteCIFitem('_refine_ls_shift/su_max',DAT1)
664            #WriteCIFitem('_refine_ls_shift/su_mean',DAT2)
665            #WriteCIFitem('_refine_diff_density_max',rhomax)    #these need to be defined for each phase!
666            #WriteCIFitem('_refine_diff_density_min',rhomin)
667            WriteCIFitem('_computing_structure_refinement','GSAS-II (Toby & Von Dreele, J. Appl. Cryst. 46, 544-549, 2013)')
668            try:
669                vars = str(len(self.OverallParms['Covariance']['varyList']))
670            except:
671                vars = '?'
672            WriteCIFitem('_refine_ls_number_parameters',vars)
673            try:
674                GOF = G2mth.ValEsd(self.OverallParms['Covariance']['Rvals']['GOF'],-0.009)
675            except:
676                GOF = '?'
677            WriteCIFitem('_refine_ls_goodness_of_fit_all',GOF)
678
679            # get restraint info
680            # restraintDict = self.OverallParms.get('Restraints',{})
681            # for i in  self.OverallParms['Constraints']:
682            #     print i
683            #     for j in self.OverallParms['Constraints'][i]:
684            #         print j
685            #WriteCIFitem('_refine_ls_number_restraints',TEXT)
686            # other things to consider reporting
687            # _refine_ls_number_reflns
688            # _refine_ls_goodness_of_fit_obs
689            # _refine_ls_wR_factor_obs
690            # _refine_ls_restrained_S_all
691            # _refine_ls_restrained_S_obs
692
693            # include an overall profile r-factor, if there is more than one powder histogram
694            R = '%.5f'%(self.OverallParms['Covariance']['Rvals']['Rwp']/100.)
695            WriteCIFitem('\n# OVERALL WEIGHTED R-FACTOR')
696            WriteCIFitem('_refine_ls_wR_factor_obs',R)
697                # _refine_ls_R_factor_all
698                # _refine_ls_R_factor_obs               
699            WriteCIFitem('_refine_ls_matrix_type','full')
700            #WriteCIFitem('_refine_ls_matrix_type','userblocks')
701
702        def WritePubTemplate():
703            '''TODO: insert the publication template ``template_publ.cif`` or some modified
704            version for this project. Store this in the GPX file?
705            '''
706            print getCallerDocString()
707
708        def WritePhaseTemplate():
709            '''TODO: insert the phase template ``template_phase.cif`` or some modified
710            version for this project
711            '''
712            print getCallerDocString()
713
714        def WritePowderTemplate():
715            '''TODO: insert the phase template ``template_instrument.cif`` or some modified
716            version for this project
717            '''
718            print getCallerDocString()
719
720        def WriteSnglXtalTemplate():
721            '''TODO: insert the single-crystal histogram template
722            for this project
723            '''
724            print getCallerDocString()
725
726        def FormatSH(phasenam):
727            'Format a full spherical harmonics texture description as a string'
728            phasedict = self.Phases[phasenam] # pointer to current phase info           
729            pfx = str(phasedict['pId'])+'::'
730            s = ""
731            textureData = phasedict['General']['SH Texture']   
732            if textureData.get('Order'):
733                s += "Spherical Harmonics correction. Order = "+str(textureData['Order'])
734                s += " Model: " + str(textureData['Model']) + "\n    Orientation angles: "
735                for name in ['omega','chi','phi']:
736                    aname = pfx+'SH '+name
737                    s += name + " = "
738                    sig = self.sigDict.get(aname,-0.09)
739                    s += G2mth.ValEsd(self.parmDict[aname],sig)
740                    s += "; "
741                s += "\n"
742                s1 = "    Coefficients:  "
743                for name in textureData['SH Coeff'][1]:
744                    aname = pfx+name
745                    if len(s1) > 60:
746                        s += s1 + "\n"
747                        s1 = "    "
748                    s1 += aname + ' = '
749                    sig = self.sigDict.get(aname,-0.0009)
750                    s1 += G2mth.ValEsd(self.parmDict[aname],sig)
751                    s1 += "; "
752                s += s1
753            return s
754
755        def FormatHAPpo(phasenam):
756            '''return the March-Dollase/SH correction for every
757            histogram in the current phase formatted into a
758            character string
759            '''
760            phasedict = self.Phases[phasenam] # pointer to current phase info           
761            s = ''
762            for histogram in sorted(phasedict['Histograms']):
763                if histogram.startswith("HKLF"): continue # powder only
764                Histogram = self.Histograms.get(histogram)
765                if not Histogram: continue
766                hapData = phasedict['Histograms'][histogram]
767                if hapData['Pref.Ori.'][0] == 'MD':
768                    aname = str(phasedict['pId'])+':'+str(Histogram['hId'])+':MD'
769                    if self.parmDict.get(aname,1.0) != 1.0: continue
770                    sig = self.sigDict.get(aname,-0.009)
771                    if s != "": s += '\n'
772                    s += 'March-Dollase correction'
773                    if len(self.powderDict) > 1:
774                        s += ', histogram '+str(Histogram['hId']+1)
775                    s += ' coef. = ' + G2mth.ValEsd(self.parmDict[aname],sig)
776                    s += ' axis = ' + str(hapData['Pref.Ori.'][3])
777                else: # must be SH
778                    if s != "": s += '\n'
779                    s += 'Simple spherical harmonic correction'
780                    if len(self.powderDict) > 1:
781                        s += ', histogram '+str(Histogram['hId']+1)
782                    s += ' Order = '+str(hapData['Pref.Ori.'][4])+'\n'
783                    s1 = "    Coefficients:  "
784                    for item in hapData['Pref.Ori.'][5]:
785                        aname = str(phasedict['pId'])+':'+str(Histogram['hId'])+':'+item
786                        if len(s1) > 60:
787                            s += s1 + "\n"
788                            s1 = "    "
789                        s1 += aname + ' = '
790                        sig = self.sigDict.get(aname,-0.0009)
791                        s1 += G2mth.ValEsd(self.parmDict[aname],sig)
792                        s1 += "; "
793                    s += s1
794            return s
795        def FormatBackground(bkg,hId):
796            '''Display the Background information as a descriptive text string.
797           
798            TODO: this needs to be expanded to show the diffuse peak and
799            Debye term information as well. (Bob)
800
801            :returns: the text description (str)
802            '''
803            hfx = ':'+str(hId)+':'
804            fxn, bkgdict = bkg
805            terms = fxn[2]
806            txt = 'Background function: "'+fxn[0]+'" function with '+str(terms)+' terms:\n'
807            l = "    "
808            for i,v in enumerate(fxn[3:]):
809                name = '%sBack:%d'%(hfx,i)
810                sig = self.sigDict.get(name,-0.009)
811                if len(l) > 60:
812                    txt += l + '\n'
813                    l = '    '
814                l += G2mth.ValEsd(v,sig)+', '
815            txt += l
816            if bkgdict['nDebye']:
817                txt += '\n  Background Debye function parameters: A, R, U:'
818                names = ['A:','R:','U:']
819                for i in range(bkgdict['nDebye']):
820                    txt += '\n    '
821                    for j in range(3):
822                        name = hfx+'Debye'+names[j]+str(i)
823                        sig = self.sigDict.get(name,-0.009)
824                        txt += G2mth.ValEsd(bkgdict['debyeTerms'][i][2*j],sig)+', '
825            if bkgdict['nPeaks']:
826                txt += '\n  Background peak parameters: pos, int, sig, gam:'
827                names = ['pos:','int:','sig:','gam:']
828                for i in range(bkgdict['nPeaks']):
829                    txt += '\n    '
830                    for j in range(4):
831                        name = hfx+'BkPk'+names[j]+str(i)
832                        sig = self.sigDict.get(name,-0.009)
833                        txt += G2mth.ValEsd(bkgdict['peaksList'][i][2*j],sig)+', '
834            return txt
835
836        def FormatInstProfile(instparmdict,hId):
837            '''Format the instrumental profile parameters with a
838            string description. Will only be called on PWDR histograms
839            '''
840            s = ''
841            inst = instparmdict[0]
842            hfx = ':'+str(hId)+':'
843            if 'C' in inst['Type'][0]:
844                s = 'Finger-Cox-Jephcoat function parameters U, V, W, X, Y, SH/L:\n'
845                s += '  peak variance(Gauss) = Utan(Th)^2^+Vtan(Th)+W:\n'
846                s += '  peak HW(Lorentz) = X/cos(Th)+Ytan(Th); SH/L = S/L+H/L\n'
847                s += '  U, V, W in (centideg)^2^, X & Y in centideg\n    '
848                for item in ['U','V','W','X','Y','SH/L']:
849                    name = hfx+item
850                    sig = self.sigDict.get(name,-0.009)
851                    s += G2mth.ValEsd(inst[item][1],sig)+', '                   
852            elif 'T' in inst['Type'][0]:    #to be tested after TOF Rietveld done
853                s = 'Von Dreele-Jorgenson-Windsor function parameters\n'+ \
854                    '   alpha, beta-0, beta-1, beta-q, sig-0, sig-1, sig-q, X, Y:\n    '
855                for item in ['alpha','bet-0','bet-1','bet-q','sig-0','sig-1','sig-q','X','Y']:
856                    name = hfx+item
857                    sig = self.sigDict.get(name,-0.009)
858                    s += G2mth.ValEsd(inst[item][1],sig)+', '
859            return s
860
861        def FormatPhaseProfile(phasenam):
862            '''Format the phase-related profile parameters (size/strain)
863            with a string description.
864            return an empty string or None if there are no
865            powder histograms for this phase.
866            '''
867            s = ''
868            phasedict = self.Phases[phasenam] # pointer to current phase info
869            SGData = phasedict['General'] ['SGData']         
870            for histogram in sorted(phasedict['Histograms']):
871                if histogram.startswith("HKLF"): continue # powder only
872                Histogram = self.Histograms.get(histogram)
873                if not Histogram: continue
874                hapData = phasedict['Histograms'][histogram]
875                pId = phasedict['pId']
876                hId = Histogram['hId']
877                phfx = '%d:%d:'%(pId,hId)
878                size = hapData['Size']
879                mustrain = hapData['Mustrain']
880                hstrain = hapData['HStrain']
881                s = '  Crystallite size model "%s" for %s (microns)\n  '%(size[0],phasenam)
882                names = ['Size;i','Size;mx']
883                if 'uniax' in size[0]:
884                    names = ['Size;i','Size;a','Size;mx']
885                    s += 'anisotropic axis is %s\n  '%(str(size[3]))
886                    s += 'parameters: equatorial size, axial size, G/L mix\n    '
887                    for i,item in enumerate(names):
888                        name = phfx+item
889                        sig = self.sigDict.get(name,-0.009)
890                        s += G2mth.ValEsd(size[1][i],sig)+', '
891                elif 'ellip' in size[0]:
892                    s += 'parameters: S11, S22, S33, S12, S13, S23, G/L mix\n    '
893                    for i in range(6):
894                        name = phfx+'Size:'+str(i)
895                        sig = self.sigDict.get(name,-0.009)
896                        s += G2mth.ValEsd(size[4][i],sig)+', '
897                    sig = self.sigDict.get(phfx+'Size;mx',-0.009)
898                    s += G2mth.ValEsd(size[1][2],sig)+', '                                           
899                else:       #isotropic
900                    s += 'parameters: Size, G/L mix\n    '
901                    i = 0
902                    for item in names:
903                        name = phfx+item
904                        sig = self.sigDict.get(name,-0.009)
905                        s += G2mth.ValEsd(size[1][i],sig)+', '
906                        i = 2    #skip the aniso value               
907                s += '\n  Mustrain model "%s" for %s (10^6^)\n  '%(mustrain[0],phasenam)
908                names = ['Mustrain;i','Mustrain;mx']
909                if 'uniax' in mustrain[0]:
910                    names = ['Mustrain;i','Mustrain;a','Mustrain;mx']
911                    s += 'anisotropic axis is %s\n  '%(str(size[3]))
912                    s += 'parameters: equatorial mustrain, axial mustrain, G/L mix\n    '
913                    for i,item in enumerate(names):
914                        name = phfx+item
915                        sig = self.sigDict.get(name,-0.009)
916                        s += G2mth.ValEsd(mustrain[1][i],sig)+', '
917                elif 'general' in mustrain[0]:
918                    names = 'parameters: '
919                    for i,name in enumerate(G2spc.MustrainNames(SGData)):
920                        names += name+', '
921                        if i == 9:
922                            names += '\n  '
923                    names += 'G/L mix\n    '
924                    s += names
925                    txt = ''
926                    for i in range(len(mustrain[4])):
927                        name = phfx+'Mustrain:'+str(i)
928                        sig = self.sigDict.get(name,-0.009)
929                        if len(txt) > 60:
930                            s += txt+'\n    '
931                            txt = ''
932                        txt += G2mth.ValEsd(mustrain[4][i],sig)+', '
933                    s += txt                                           
934                    sig = self.sigDict.get(phfx+'Mustrain;mx',-0.009)
935                    s += G2mth.ValEsd(mustrain[1][2],sig)+', '
936                   
937                else:       #isotropic
938                    s += '  parameters: Mustrain, G/L mix\n    '
939                    i = 0
940                    for item in names:
941                        name = phfx+item
942                        sig = self.sigDict.get(name,-0.009)
943                        s += G2mth.ValEsd(mustrain[1][i],sig)+', '
944                        i = 2    #skip the aniso value               
945                s += '\n  Macrostrain for %s\n'%(phasenam)
946                txt = '  parameters: '
947                names = G2spc.HStrainNames(SGData)
948                for name in names:
949                    txt += name+', '
950                s += txt+'\n    '
951                for i in range(len(names)):
952                    name = phfx+name[i]
953                    sig = self.sigDict.get(name,-0.009)
954                    s += G2mth.ValEsd(hstrain[0][i],sig)+', '
955            return s
956       
957        def FmtAtomType(sym):
958            'Reformat a GSAS-II atom type symbol to match CIF rules'
959            sym = sym.replace('_','') # underscores are not allowed: no isotope designation?
960            # in CIF, oxidation state sign symbols come after, not before
961            if '+' in sym:
962                sym = sym.replace('+','') + '+'
963            elif '-' in sym:
964                sym = sym.replace('-','') + '-'
965            return sym
966           
967        def PutInCol(val,wid):
968            '''Pad a value to >=wid+1 columns by adding spaces at the end. Always
969            adds at least one space
970            '''
971            val = str(val).replace(' ','')
972            if not val: val = '?'
973            fmt = '{:' + str(wid) + '} '
974            return fmt.format(val)
975
976        def MakeUniqueLabel(lbl,labellist):
977            'Make sure that every atom label is unique'
978            lbl = lbl.strip()
979            if not lbl: # deal with a blank label
980                lbl = 'A_1'
981            if lbl not in labellist:
982                labellist.append(lbl)
983                return lbl
984            i = 1
985            prefix = lbl
986            if '_' in lbl:
987                prefix = lbl[:lbl.rfind('_')]
988                suffix = lbl[lbl.rfind('_')+1:]
989                try:
990                    i = int(suffix)+1
991                except:
992                    pass
993            while prefix+'_'+str(i) in labellist:
994                i += 1
995            else:
996                lbl = prefix+'_'+str(i)
997                labellist.append(lbl)
998
999        def WriteAtomsNuclear(phasenam):
1000            'Write atom positions to CIF'
1001            phasedict = self.Phases[phasenam] # pointer to current phase info
1002            General = phasedict['General']
1003            cx,ct,cs,cia = General['AtomPtrs']
1004            Atoms = phasedict['Atoms']
1005            cfrac = cx+3
1006            fpfx = str(phasedict['pId'])+'::Afrac:'       
1007            for i,at in enumerate(Atoms):
1008                fval = self.parmDict.get(fpfx+str(i),at[cfrac])
1009                if fval != 0.0:
1010                    break
1011            else:
1012                WriteCIFitem('\n# PHASE HAS NO ATOMS!')
1013                return
1014               
1015            WriteCIFitem('\n# ATOMIC COORDINATES AND DISPLACEMENT PARAMETERS')
1016            WriteCIFitem('loop_ '+
1017                         '\n\t_atom_site_label'+
1018                         '\n\t_atom_site_type_symbol'+
1019                         '\n\t_atom_site_fract_x'+
1020                         '\n\t_atom_site_fract_y'+
1021                         '\n\t_atom_site_fract_z'+
1022                         '\n\t_atom_site_occupancy'+
1023                         '\n\t_atom_site_adp_type'+
1024                         '\n\t_atom_site_U_iso_or_equiv'+
1025                         '\n\t_atom_site_symmetry_multiplicity')
1026
1027            varnames = {cx:'Ax',cx+1:'Ay',cx+2:'Az',cx+3:'Afrac',
1028                        cia+1:'AUiso',cia+2:'AU11',cia+3:'AU22',cia+4:'AU33',
1029                        cia+5:'AU12',cia+6:'AU13',cia+7:'AU23'}
1030            self.labellist = []
1031           
1032            pfx = str(phasedict['pId'])+'::'
1033            # loop over all atoms
1034            naniso = 0
1035            for i,at in enumerate(Atoms):
1036                s = PutInCol(MakeUniqueLabel(at[ct-1],self.labellist),6) # label
1037                fval = self.parmDict.get(fpfx+str(i),at[cfrac])
1038                if fval == 0.0: continue # ignore any atoms that have a occupancy set to 0 (exact)
1039                s += PutInCol(FmtAtomType(at[ct]),4) # type
1040                if at[cia] == 'I':
1041                    adp = 'Uiso '
1042                else:
1043                    adp = 'Uani '
1044                    naniso += 1
1045                    # compute Uequiv crudely
1046                    # correct: Defined as "1/3 trace of diagonalized U matrix".
1047                    # SEE cell2GS & Uij2Ueqv to GSASIIlattice. Former is needed to make the GS matrix used by the latter.
1048                    t = 0.0
1049                    for j in (2,3,4):
1050                        var = pfx+varnames[cia+j]+":"+str(i)
1051                        t += self.parmDict.get(var,at[cia+j])
1052                for j in (cx,cx+1,cx+2,cx+3,cia,cia+1):
1053                    if j in (cx,cx+1,cx+2):
1054                        dig = 11
1055                        sigdig = -0.00009
1056                    else:
1057                        dig = 10
1058                        sigdig = -0.009
1059                    if j == cia:
1060                        s += adp
1061                    else:
1062                        var = pfx+varnames[j]+":"+str(i)
1063                        dvar = pfx+"d"+varnames[j]+":"+str(i)
1064                        if dvar not in self.sigDict:
1065                            dvar = var
1066                        if j == cia+1 and adp == 'Uani ':
1067                            val = t/3.
1068                            sig = sigdig
1069                        else:
1070                            #print var,(var in self.parmDict),(var in self.sigDict)
1071                            val = self.parmDict.get(var,at[j])
1072                            sig = self.sigDict.get(dvar,sigdig)
1073                        s += PutInCol(G2mth.ValEsd(val,sig),dig)
1074                s += PutInCol(at[cs+1],3)
1075                WriteCIFitem(s)
1076            if naniso == 0: return
1077            # now loop over aniso atoms
1078            WriteCIFitem('\nloop_' + '\n\t_atom_site_aniso_label' + 
1079                         '\n\t_atom_site_aniso_U_11' + '\n\t_atom_site_aniso_U_12' +
1080                         '\n\t_atom_site_aniso_U_13' + '\n\t_atom_site_aniso_U_22' +
1081                         '\n\t_atom_site_aniso_U_23' + '\n\t_atom_site_aniso_U_33')
1082            for i,at in enumerate(Atoms):
1083                fval = self.parmDict.get(fpfx+str(i),at[cfrac])
1084                if fval == 0.0: continue # ignore any atoms that have a occupancy set to 0 (exact)
1085                if at[cia] == 'I': continue
1086                s = PutInCol(self.labellist[i],6) # label
1087                for j in (2,3,4,5,6,7):
1088                    sigdig = -0.0009
1089                    var = pfx+varnames[cia+j]+":"+str(i)
1090                    val = self.parmDict.get(var,at[cia+j])
1091                    sig = self.sigDict.get(var,sigdig)
1092                    s += PutInCol(G2mth.ValEsd(val,sig),11)
1093                WriteCIFitem(s)
1094
1095        def HillSortElements(elmlist):
1096            '''Sort elements in "Hill" order: C, H, others, (where others
1097            are alphabetical).
1098
1099            :params list elmlist: a list of element strings
1100
1101            :returns: a sorted list of element strings
1102            '''
1103            newlist = []
1104            oldlist = elmlist[:]
1105            for elm in ('C','H'):
1106                if elm in elmlist:
1107                    newlist.append(elm)
1108                    oldlist.pop(oldlist.index(elm))
1109            return newlist+sorted(oldlist)
1110
1111        def WriteComposition(phasenam):
1112            '''determine the composition for the unit cell, crudely determine Z and
1113            then compute the composition in formula units
1114            '''
1115            phasedict = self.Phases[phasenam] # pointer to current phase info
1116            General = phasedict['General']
1117            Z = General.get('cellZ',0.0)
1118            cx,ct,cs,cia = General['AtomPtrs']
1119            Atoms = phasedict['Atoms']
1120            fpfx = str(phasedict['pId'])+'::Afrac:'       
1121            cfrac = cx+3
1122            cmult = cs+1
1123            compDict = {} # combines H,D & T
1124            sitemultlist = []
1125            massDict = dict(zip(General['AtomTypes'],General['AtomMass']))
1126            cellmass = 0
1127            for i,at in enumerate(Atoms):
1128                atype = at[ct].strip()
1129                if atype.find('-') != -1: atype = atype.split('-')[0]
1130                if atype.find('+') != -1: atype = atype.split('+')[0]
1131                atype = atype[0].upper()+atype[1:2].lower() # force case conversion
1132                if atype == "D" or atype == "D": atype = "H"
1133                fvar = fpfx+str(i)
1134                fval = self.parmDict.get(fvar,at[cfrac])
1135                mult = at[cmult]
1136                if not massDict.get(at[ct]):
1137                    print 'No mass found for atom type '+at[ct]
1138                    print 'Will not compute cell contents for phase '+phasenam
1139                    return
1140                cellmass += massDict[at[ct]]*mult*fval
1141                compDict[atype] = compDict.get(atype,0.0) + mult*fval
1142                if fval == 1: sitemultlist.append(mult)
1143            if len(compDict.keys()) == 0: return # no elements!
1144            if Z < 1: # Z has not been computed or set by user
1145                Z = 1
1146                for i in range(2,min(sitemultlist)+1):
1147                    for m in sitemultlist:
1148                        if m % i != 0:
1149                            break
1150                        else:
1151                            Z = i
1152                General['cellZ'] = Z # save it
1153
1154            # when scattering factors are included in the CIF, this needs to be
1155            # added to the loop here but only in the one-block case.
1156            # For multiblock CIFs, scattering factors go in the histogram
1157            # blocks  (for all atoms in all appropriate phases) - an example?:
1158#loop_
1159#    _atom_type_symbol
1160#    _atom_type_description
1161#    _atom_type_scat_dispersion_real
1162#    _atom_type_scat_dispersion_imag
1163#    _atom_type_scat_source
1164#    'C' 'C' 0.0033 0.0016
1165#                         'International Tables Vol C Tables 4.2.6.8 and 6.1.1.4'
1166#    'H' 'H' 0.0000 0.0000
1167#                         'International Tables Vol C Tables 4.2.6.8 and 6.1.1.4'
1168#    'P' 'P' 0.1023 0.0942
1169#                         'International Tables Vol C Tables 4.2.6.8 and 6.1.1.4'
1170#    'Cl' 'Cl' 0.1484 0.1585
1171#                         'International Tables Vol C Tables 4.2.6.8 and 6.1.1.4'
1172#    'Cu' 'Cu' 0.3201 1.2651
1173#                         'International Tables Vol C Tables 4.2.6.8 and 6.1.1.4'
1174
1175            #if oneblock: # add scattering factors for current phase here
1176            WriteCIFitem('\nloop_  _atom_type_symbol _atom_type_number_in_cell')
1177            formula = ''
1178            reload(G2mth)
1179            for elem in HillSortElements(compDict.keys()):
1180                WriteCIFitem('  ' + PutInCol(elem,4) +
1181                             G2mth.ValEsd(compDict[elem],-0.009,True))
1182                if formula: formula += " "
1183                formula += elem
1184                if compDict[elem] == Z: continue
1185                formula += G2mth.ValEsd(compDict[elem]/Z,-0.009,True)
1186            WriteCIFitem( '\n# Note that Z affects _cell_formula_sum and _weight')
1187            WriteCIFitem( '_cell_formula_units_Z',str(Z))
1188            WriteCIFitem( '_chemical_formula_sum',formula)
1189            WriteCIFitem( '_chemical_formula_weight',
1190                          G2mth.ValEsd(cellmass/Z,-0.09,True))
1191
1192        def WriteDistances(phasenam,SymOpList,offsetList,symOpList,G2oprList):
1193            '''Report bond distances and angles for the CIF
1194
1195            Note that _geom_*_symmetry_* fields are values of form
1196            n_klm where n is the symmetry operation in SymOpList (counted
1197            starting with 1) and (k-5, l-5, m-5) are translations to add
1198            to (x,y,z). See
1199            http://www.iucr.org/__data/iucr/cifdic_html/1/cif_core.dic/Igeom_angle_site_symmetry_.html
1200
1201            TODO: need a method to select publication flags for distances/angles
1202            '''
1203            phasedict = self.Phases[phasenam] # pointer to current phase info           
1204            Atoms = phasedict['Atoms']
1205            generalData = phasedict['General']
1206            cx,ct,cs,cia = phasedict['General']['AtomPtrs']
1207            cn = ct-1
1208            fpfx = str(phasedict['pId'])+'::Afrac:'       
1209            cfrac = cx+3
1210            DisAglData = {}
1211            DisAglCtls = {}
1212            # create a list of atoms, but skip atoms with zero occupancy
1213            xyz = []
1214            fpfx = str(phasedict['pId'])+'::Afrac:'       
1215            for i,atom in enumerate(Atoms):
1216                if self.parmDict.get(fpfx+str(i),atom[cfrac]) == 0.0: continue
1217                xyz.append([i,]+atom[cn:cn+2]+atom[cx:cx+3])
1218            if 'DisAglCtls' in generalData:
1219                DisAglCtls = generalData['DisAglCtls']
1220            else:
1221                dlg = G2gd.DisAglDialog(self.G2frame,DisAglCtls,generalData)
1222                if dlg.ShowModal() == wx.ID_OK:
1223                    DisAglCtls = dlg.GetData()
1224                    generalData['DisAglCtls'] = DisAglCtls
1225                else:
1226                    dlg.Destroy()
1227                    return
1228                dlg.Destroy()
1229            DisAglData['OrigAtoms'] = xyz
1230            DisAglData['TargAtoms'] = xyz
1231            SymOpList,offsetList,symOpList,G2oprList = G2spc.AllOps(
1232                generalData['SGData'])
1233
1234            xpandSGdata = generalData['SGData'].copy()
1235            xpandSGdata.update({'SGOps':symOpList,
1236                                'SGInv':False,
1237                                'SGLatt':'P',
1238                                'SGCen':np.array([[0, 0, 0]]),})
1239            DisAglData['SGData'] = xpandSGdata
1240
1241            DisAglData['Cell'] = generalData['Cell'][1:] #+ volume
1242            if 'pId' in phasedict:
1243                DisAglData['pId'] = phasedict['pId']
1244                DisAglData['covData'] = self.OverallParms['Covariance']
1245            try:
1246                AtomLabels,DistArray,AngArray = G2stMn.RetDistAngle(DisAglCtls,DisAglData)
1247            except KeyError:        # inside DistAngle for missing atom types in DisAglCtls
1248                print '**** ERROR - try again but do "Reset" to fill in missing atom types ****'
1249                   
1250            # loop over interatomic distances for this phase
1251            WriteCIFitem('\n# MOLECULAR GEOMETRY')
1252            WriteCIFitem('loop_' + 
1253                         '\n\t_geom_bond_atom_site_label_1' +
1254                         '\n\t_geom_bond_atom_site_label_2' + 
1255                         '\n\t_geom_bond_distance' + 
1256                         '\n\t_geom_bond_site_symmetry_1' + 
1257                         '\n\t_geom_bond_site_symmetry_2' + 
1258                         '\n\t_geom_bond_publ_flag')
1259
1260            for i in sorted(AtomLabels.keys()):
1261                Dist = DistArray[i]
1262                for D in Dist:
1263                    line = '  '+PutInCol(AtomLabels[i],6)+PutInCol(AtomLabels[D[0]],6)
1264                    sig = D[4]
1265                    if sig == 0: sig = -0.00009
1266                    line += PutInCol(G2mth.ValEsd(D[3],sig,True),10)
1267                    line += "  1_555 "
1268                    line += " {:3d}_".format(D[2])
1269                    for d in D[1]:
1270                        line += "{:1d}".format(d+5)
1271                    line += " yes"
1272                    WriteCIFitem(line)
1273
1274            # loop over interatomic angles for this phase
1275            WriteCIFitem('\nloop_' + 
1276                         '\n\t_geom_angle_atom_site_label_1' + 
1277                         '\n\t_geom_angle_atom_site_label_2' + 
1278                         '\n\t_geom_angle_atom_site_label_3' + 
1279                         '\n\t_geom_angle' + 
1280                         '\n\t_geom_angle_site_symmetry_1' +
1281                         '\n\t_geom_angle_site_symmetry_2' + 
1282                         '\n\t_geom_angle_site_symmetry_3' + 
1283                         '\n\t_geom_angle_publ_flag')
1284
1285            for i in sorted(AtomLabels.keys()):
1286                Dist = DistArray[i]
1287                for k,j,tup in AngArray[i]:
1288                    Dj = Dist[j]
1289                    Dk = Dist[k]
1290                    line = '  '+PutInCol(AtomLabels[Dj[0]],6)+PutInCol(AtomLabels[i],6)+PutInCol(AtomLabels[Dk[0]],6)
1291                    sig = tup[1]
1292                    if sig == 0: sig = -0.009
1293                    line += PutInCol(G2mth.ValEsd(tup[0],sig,True),10)
1294                    line += " {:3d}_".format(Dj[2])
1295                    for d in Dj[1]:
1296                        line += "{:1d}".format(d+5)
1297                    line += "  1_555 "
1298                    line += " {:3d}_".format(Dk[2])
1299                    for d in Dk[1]:
1300                        line += "{:1d}".format(d+5)
1301                    line += " yes"
1302                    WriteCIFitem(line)
1303
1304        def WritePhaseInfo(phasenam):
1305            WriteCIFitem('\n# phase info for '+str(phasenam) + ' follows')
1306            phasedict = self.Phases[phasenam] # pointer to current phase info           
1307            WriteCIFitem('_pd_phase_name', phasenam)
1308            pfx = str(phasedict['pId'])+'::'
1309            A,sigA = G2stIO.cellFill(pfx,phasedict['General']['SGData'],self.parmDict,self.sigDict)
1310            cellSig = G2stIO.getCellEsd(pfx,
1311                                       phasedict['General']['SGData'],A,
1312                                       self.OverallParms['Covariance'])  # returns 7 vals, includes sigVol
1313            cellList = G2lat.A2cell(A) + (G2lat.calc_V(A),)
1314            defsigL = 3*[-0.00001] + 3*[-0.001] + [-0.01] # significance to use when no sigma
1315            names = ['length_a','length_b','length_c',
1316                     'angle_alpha','angle_beta ','angle_gamma',
1317                     'volume']
1318            prevsig = 0
1319            for lbl,defsig,val,sig in zip(names,defsigL,cellList,cellSig):
1320                if sig:
1321                    txt = G2mth.ValEsd(val,sig)
1322                    prevsig = -sig # use this as the significance for next value
1323                else:
1324                    txt = G2mth.ValEsd(val,min(defsig,prevsig),True)
1325                WriteCIFitem('_cell_'+lbl,txt)
1326                   
1327            WriteCIFitem('_symmetry_cell_setting',
1328                         phasedict['General']['SGData']['SGSys'])
1329
1330            spacegroup = phasedict['General']['SGData']['SpGrp'].strip()
1331            # regularize capitalization and remove trailing H/R
1332            spacegroup = spacegroup[0].upper() + spacegroup[1:].lower().rstrip('rh ')
1333            WriteCIFitem('_symmetry_space_group_name_H-M',spacegroup)
1334
1335            # generate symmetry operations including centering and center of symmetry
1336            SymOpList,offsetList,symOpList,G2oprList = G2spc.AllOps(
1337                phasedict['General']['SGData'])
1338            WriteCIFitem('loop_\n    _space_group_symop_id\n    _space_group_symop_operation_xyz')
1339            for i,op in enumerate(SymOpList,start=1):
1340                WriteCIFitem('   {:3d}  {:}'.format(i,op.lower()))
1341
1342            # loop over histogram(s) used in this phase
1343            if not oneblock and not self.quickmode:
1344                # report pointers to the histograms used in this phase
1345                histlist = []
1346                for hist in self.Phases[phasenam]['Histograms']:
1347                    if self.Phases[phasenam]['Histograms'][hist]['Use']:
1348                        if phasebyhistDict.get(hist):
1349                            phasebyhistDict[hist].append(phasenam)
1350                        else:
1351                            phasebyhistDict[hist] = [phasenam,]
1352                        blockid = datablockidDict.get(hist)
1353                        if not blockid:
1354                            print "Internal error: no block for data. Phase "+str(
1355                                phasenam)+" histogram "+str(hist)
1356                            histlist = []
1357                            break
1358                        histlist.append(blockid)
1359
1360                if len(histlist) == 0:
1361                    WriteCIFitem('# Note: phase has no associated data')
1362
1363            # report atom params
1364            if phasedict['General']['Type'] == 'nuclear':        #this needs macromolecular variant, etc!
1365                WriteAtomsNuclear(phasenam)
1366            else:
1367                raise Exception,"no export for mm coordinates implemented"
1368            # report cell contents
1369            WriteComposition(phasenam)
1370            if not self.quickmode:      # report distances and angles
1371                WriteDistances(phasenam,SymOpList,offsetList,symOpList,G2oprList)
1372               
1373        def Yfmt(ndec,val):
1374            'Format intensity values'
1375            out = ("{:."+str(ndec)+"f}").format(val)
1376            out = out.rstrip('0')  # strip zeros to right of decimal
1377            return out.rstrip('.')  # and decimal place when not needed
1378           
1379        def WriteReflStat(refcount,hklmin,hklmax,dmin,dmax,nRefSets=1):
1380            WriteCIFitem('_reflns_number_total', str(refcount))
1381            if hklmin is not None and nRefSets == 1: # hkl range has no meaning with multiple phases
1382                WriteCIFitem('_reflns_limit_h_min', str(int(hklmin[0])))
1383                WriteCIFitem('_reflns_limit_h_max', str(int(hklmax[0])))
1384                WriteCIFitem('_reflns_limit_k_min', str(int(hklmin[1])))
1385                WriteCIFitem('_reflns_limit_k_max', str(int(hklmax[1])))
1386                WriteCIFitem('_reflns_limit_l_min', str(int(hklmin[2])))
1387                WriteCIFitem('_reflns_limit_l_max', str(int(hklmax[2])))
1388            if hklmin is not None:
1389                WriteCIFitem('_reflns_d_resolution_low  ', G2mth.ValEsd(dmax,-0.009))
1390                WriteCIFitem('_reflns_d_resolution_high ', G2mth.ValEsd(dmin,-0.009))
1391
1392        def WritePowderData(histlbl):
1393            histblk = self.Histograms[histlbl]
1394            inst = histblk['Instrument Parameters'][0]
1395            hId = histblk['hId']
1396            pfx = ':' + str(hId) + ':'
1397           
1398            if 'Lam1' in inst:
1399                ratio = self.parmDict.get('I(L2)/I(L1)',inst['I(L2)/I(L1)'][1])
1400                sratio = self.sigDict.get('I(L2)/I(L1)',-0.0009)
1401                lam1 = self.parmDict.get('Lam1',inst['Lam1'][1])
1402                slam1 = self.sigDict.get('Lam1',-0.00009)
1403                lam2 = self.parmDict.get('Lam2',inst['Lam2'][1])
1404                slam2 = self.sigDict.get('Lam2',-0.00009)
1405                # always assume Ka1 & Ka2 if two wavelengths are present
1406                WriteCIFitem('_diffrn_radiation_type','K\\a~1,2~')
1407                WriteCIFitem('loop_' + 
1408                             '\n\t_diffrn_radiation_wavelength' +
1409                             '\n\t_diffrn_radiation_wavelength_wt' + 
1410                             '\n\t_diffrn_radiation_wavelength_id')
1411                WriteCIFitem('  ' + PutInCol(G2mth.ValEsd(lam1,slam1),15)+
1412                             PutInCol('1.0',15) + 
1413                             PutInCol('1',5))
1414                WriteCIFitem('  ' + PutInCol(G2mth.ValEsd(lam2,slam2),15)+
1415                             PutInCol(G2mth.ValEsd(ratio,sratio),15)+
1416                             PutInCol('2',5))               
1417            else:
1418                lam1 = self.parmDict.get('Lam',inst['Lam'][1])
1419                slam1 = self.sigDict.get('Lam',-0.00009)
1420                WriteCIFitem('_diffrn_radiation_wavelength',G2mth.ValEsd(lam1,slam1))
1421
1422            if not oneblock:
1423                if not phasebyhistDict.get(histlbl):
1424                    WriteCIFitem('\n# No phases associated with this data set')
1425                else:
1426                    WriteCIFitem('\n# PHASE TABLE')
1427                    WriteCIFitem('loop_' +
1428                                 '\n\t_pd_phase_id' + 
1429                                 '\n\t_pd_phase_block_id' + 
1430                                 '\n\t_pd_phase_mass_%')
1431                    wtFrSum = 0.
1432                    for phasenam in phasebyhistDict.get(histlbl):
1433                        hapData = self.Phases[phasenam]['Histograms'][histlbl]
1434                        General = self.Phases[phasenam]['General']
1435                        wtFrSum += hapData['Scale'][0]*General['Mass']
1436
1437                    for phasenam in phasebyhistDict.get(histlbl):
1438                        hapData = self.Phases[phasenam]['Histograms'][histlbl]
1439                        General = self.Phases[phasenam]['General']
1440                        wtFr = hapData['Scale'][0]*General['Mass']/wtFrSum
1441                        pfx = str(self.Phases[phasenam]['pId'])+':'+str(hId)+':'
1442                        if pfx+'Scale' in self.sigDict:
1443                            sig = self.sigDict[pfx+'Scale']*wtFr/hapData['Scale'][0]
1444                        else:
1445                            sig = -0.0001
1446                        WriteCIFitem(
1447                            '  '+
1448                            str(self.Phases[phasenam]['pId']) +
1449                            '  '+datablockidDict[phasenam]+
1450                            '  '+G2mth.ValEsd(wtFr,sig)
1451                            )
1452                    WriteCIFitem('loop_' +
1453                                 '\n\t_gsas_proc_phase_R_F_factor' +
1454                                 '\n\t_gsas_proc_phase_R_Fsqd_factor' +
1455                                 '\n\t_gsas_proc_phase_id' +
1456                                 '\n\t_gsas_proc_phase_block_id')
1457                    for phasenam in phasebyhistDict.get(histlbl):
1458                        pfx = str(self.Phases[phasenam]['pId'])+':'+str(hId)+':'
1459                        WriteCIFitem(
1460                            '  '+
1461                            '  '+G2mth.ValEsd(histblk[pfx+'Rf']/100.,-.00009) +
1462                            '  '+G2mth.ValEsd(histblk[pfx+'Rf^2']/100.,-.00009)+
1463                            '  '+str(self.Phases[phasenam]['pId'])+
1464                            '  '+datablockidDict[phasenam]
1465                            )
1466            else:
1467                # single phase in this histogram
1468                pfx = '0:'+str(hId)+':'
1469                WriteCIFitem('_refine_ls_R_F_factor      ','%.5f'%(histblk[pfx+'Rf']/100.))
1470                WriteCIFitem('_refine_ls_R_Fsqd_factor   ','%.5f'%(histblk[pfx+'Rf^2']/100.))
1471               
1472            WriteCIFitem('_pd_proc_ls_prof_R_factor   ','%.5f'%(histblk['R']/100.))
1473            WriteCIFitem('_pd_proc_ls_prof_wR_factor  ','%.5f'%(histblk['wR']/100.))
1474            WriteCIFitem('_gsas_proc_ls_prof_R_B_factor ','%.5f'%(histblk['Rb']/100.))
1475            WriteCIFitem('_gsas_proc_ls_prof_wR_B_factor','%.5f'%(histblk['wRb']/100.))
1476            WriteCIFitem('_pd_proc_ls_prof_wR_expected','%.5f'%(histblk['wRmin']/100.))
1477
1478            if histblk['Instrument Parameters'][0]['Type'][1][1] == 'X':
1479                WriteCIFitem('_diffrn_radiation_probe','x-ray')
1480                pola = histblk['Instrument Parameters'][0].get('Polariz.')
1481                if pola:
1482                    pfx = ':' + str(hId) + ':'
1483                    sig = self.sigDict.get(pfx+'Polariz.',-0.0009)
1484                    txt = G2mth.ValEsd(pola[1],sig)
1485                    WriteCIFitem('_diffrn_radiation_polarisn_ratio',txt)
1486            elif histblk['Instrument Parameters'][0]['Type'][1][1] == 'N':
1487                WriteCIFitem('_diffrn_radiation_probe','neutron')
1488            # TOF (note that this may not be defined)
1489            #if histblk['Instrument Parameters'][0]['Type'][1][2] == 'T':
1490            #    WriteCIFitem('_pd_meas_2theta_fixed',text)
1491           
1492
1493            # TODO: this will need help from Bob
1494            #if not oneblock:
1495            #WriteCIFitem('\n# SCATTERING FACTOR INFO')
1496            #WriteCIFitem('loop_  _atom_type_symbol')
1497            #if histblk['Instrument Parameters'][0]['Type'][1][1] == 'X':
1498            #    WriteCIFitem('      _atom_type_scat_dispersion_real')
1499            #    WriteCIFitem('      _atom_type_scat_dispersion_imag')
1500            #    for lbl in ('a1','a2','a3', 'a4', 'b1', 'b2', 'b3', 'b4', 'c'):
1501            #        WriteCIFitem('      _atom_type_scat_Cromer_Mann_'+lbl)
1502            #elif histblk['Instrument Parameters'][0]['Type'][1][1] == 'N':
1503            #    WriteCIFitem('      _atom_type_scat_length_neutron')
1504            #WriteCIFitem('      _atom_type_scat_source')
1505
1506            WriteCIFitem('_pd_proc_ls_background_function',FormatBackground(histblk['Background'],histblk['hId']))
1507
1508            # TODO: this will need help from Bob
1509            #WriteCIFitem('_exptl_absorpt_process_details','?')
1510            #WriteCIFitem('_exptl_absorpt_correction_T_min','?')
1511            #WriteCIFitem('_exptl_absorpt_correction_T_max','?')
1512            #C extinction
1513            #WRITE(IUCIF,'(A)') '# Extinction correction'
1514            #CALL WRVAL(IUCIF,'_gsas_exptl_extinct_corr_T_min',TEXT(1:10))
1515            #CALL WRVAL(IUCIF,'_gsas_exptl_extinct_corr_T_max',TEXT(11:20))
1516
1517            if not oneblock:                 # instrumental profile terms go here
1518                WriteCIFitem('_pd_proc_ls_profile_function', 
1519                    FormatInstProfile(histblk["Instrument Parameters"],histblk['hId']))
1520
1521            #refprx = '_refln.' # mm
1522            refprx = '_refln_' # normal
1523            WriteCIFitem('\n# STRUCTURE FACTOR TABLE')           
1524            # compute maximum intensity reflection
1525            Imax = 0
1526            for phasenam in histblk['Reflection Lists']:
1527                scale = self.Phases[phasenam]['Histograms'][histlbl]['Scale'][0]
1528                Icorr = np.array([refl[13] for refl in histblk['Reflection Lists'][phasenam]])[0]
1529                FO2 = np.array([refl[8] for refl in histblk['Reflection Lists'][phasenam]])
1530                I100 = scale*FO2*Icorr
1531                Imax = max(Imax,max(I100))
1532
1533            WriteCIFitem('loop_')
1534            if len(histblk['Reflection Lists'].keys()) > 1:
1535                WriteCIFitem('\t_pd_refln_phase_id')
1536            WriteCIFitem('\t' + refprx + 'index_h' + 
1537                         '\n\t' + refprx + 'index_k' + 
1538                         '\n\t' + refprx + 'index_l' + 
1539                         '\n\t' + refprx + 'F_squared_meas' + 
1540                         '\n\t' + refprx + 'F_squared_calc' + 
1541                         '\n\t' + refprx + 'phase_calc' + 
1542                         '\n\t_pd_refln_d_spacing')
1543            if Imax > 0:
1544                WriteCIFitem('\t_gsas_i100_meas')
1545
1546            refcount = 0
1547            hklmin = None
1548            hklmax = None
1549            dmax = None
1550            dmin = None
1551            for phasenam in histblk['Reflection Lists']:
1552                scale = self.Phases[phasenam]['Histograms'][histlbl]['Scale'][0]
1553                phaseid = self.Phases[phasenam]['pId']
1554                refcount += len(histblk['Reflection Lists'][phasenam])
1555                for ref in histblk['Reflection Lists'][phasenam]:
1556                    if DEBUG:
1557                        print 'DEBUG: skip reflection list'
1558                        break
1559                    if hklmin is None:
1560                        hklmin = ref[0:3]
1561                        hklmax = ref[0:3]
1562                        dmax = dmin = ref[4]
1563                    if len(histblk['Reflection Lists'].keys()) > 1:
1564                        s = PutInCol(phaseid,2)
1565                    else:
1566                        s = ""
1567                    for i,hkl in enumerate(ref[0:3]):
1568                        hklmax[i] = max(hkl,hklmax[i])
1569                        hklmin[i] = min(hkl,hklmin[i])
1570                        s += PutInCol(int(hkl),4)
1571                    for I in ref[8:10]:
1572                        s += PutInCol(G2mth.ValEsd(I,-0.0009),10)
1573                    s += PutInCol(G2mth.ValEsd(ref[10],-0.9),7)
1574                    dmax = max(dmax,ref[4])
1575                    dmin = min(dmin,ref[4])
1576                    s += PutInCol(G2mth.ValEsd(ref[4],-0.009),8)
1577                    if Imax > 0:
1578                        I100 = 100.*scale*ref[8]*ref[13]/Imax
1579                        s += PutInCol(G2mth.ValEsd(I100,-0.09),6)
1580                    WriteCIFitem("  "+s)
1581
1582            WriteReflStat(refcount,hklmin,hklmax,dmin,dmax,len(histblk['Reflection Lists']))
1583            WriteCIFitem('\n# POWDER DATA TABLE')
1584            # is data fixed step? If the step varies by <0.01% treat as fixed step
1585            steps = histblk['Data'][0][1:] - histblk['Data'][0][:-1]
1586            if abs(max(steps)-min(steps)) > abs(max(steps))/10000.:
1587                fixedstep = False
1588            else:
1589                fixedstep = True
1590
1591            if fixedstep: # and not TOF
1592                WriteCIFitem('_pd_meas_2theta_range_min', G2mth.ValEsd(histblk['Data'][0][0],-0.00009))
1593                WriteCIFitem('_pd_meas_2theta_range_max', G2mth.ValEsd(histblk['Data'][0][-1],-0.00009))
1594                WriteCIFitem('_pd_meas_2theta_range_inc', G2mth.ValEsd(steps.sum()/len(steps),-0.00009))
1595                # zero correct, if defined
1596                zero = None
1597                zerolst = histblk['Instrument Parameters'][0].get('Zero')
1598                if zerolst: zero = zerolst[1]
1599                zero = self.parmDict.get('Zero',zero)
1600                if zero:
1601                    WriteCIFitem('_pd_proc_2theta_range_min', G2mth.ValEsd(histblk['Data'][0][0]-zero,-0.00009))
1602                    WriteCIFitem('_pd_proc_2theta_range_max', G2mth.ValEsd(histblk['Data'][0][-1]-zero,-0.00009))
1603                    WriteCIFitem('_pd_proc_2theta_range_inc', G2mth.ValEsd(steps.sum()/len(steps),-0.00009))
1604               
1605            if zero:
1606                WriteCIFitem('_pd_proc_number_of_points', str(len(histblk['Data'][0])))
1607            else:
1608                WriteCIFitem('_pd_meas_number_of_points', str(len(histblk['Data'][0])))
1609            WriteCIFitem('\nloop_')
1610            #            WriteCIFitem('\t_pd_proc_d_spacing') # need easy way to get this
1611            if not fixedstep:
1612                if zero:
1613                    WriteCIFitem('\t_pd_proc_2theta_corrected')
1614                else:
1615                    WriteCIFitem('\t_pd_meas_2theta_scan')
1616            # at least for now, always report weights.
1617            #if countsdata:
1618            #    WriteCIFitem('\t_pd_meas_counts_total')
1619            #else:
1620            WriteCIFitem('\t_pd_meas_intensity_total')
1621            WriteCIFitem('\t_pd_calc_intensity_total')
1622            WriteCIFitem('\t_pd_proc_intensity_bkg_calc')
1623            WriteCIFitem('\t_pd_proc_ls_weight')
1624            maxY = max(histblk['Data'][1].max(),histblk['Data'][3].max())
1625            if maxY < 0: maxY *= -10 # this should never happen, but...
1626            ndec = max(0,10-int(np.log10(maxY))-1) # 10 sig figs should be enough
1627            maxSU = histblk['Data'][2].max()
1628            if maxSU < 0: maxSU *= -1 # this should never happen, but...
1629            ndecSU = max(0,8-int(np.log10(maxSU))-1) # 8 sig figs should be enough
1630            lowlim,highlim = histblk['Limits'][1]
1631
1632            if DEBUG:
1633                print 'DEBUG: skip profile list'
1634            else:   
1635                for x,yobs,yw,ycalc,ybkg in zip(histblk['Data'][0],
1636                                                histblk['Data'][1],
1637                                                histblk['Data'][2],
1638                                                histblk['Data'][3],
1639                                                histblk['Data'][4]):
1640                    if lowlim <= x <= highlim:
1641                        pass
1642                    else:
1643                        yw = 0.0 # show the point is not in use
1644   
1645                    if fixedstep:
1646                        s = ""
1647                    else:
1648                        s = PutInCol(G2mth.ValEsd(x-zero,-0.00009),10)
1649                    s += PutInCol(Yfmt(ndec,yobs),12)
1650                    s += PutInCol(Yfmt(ndec,ycalc),12)
1651                    s += PutInCol(Yfmt(ndec,ybkg),11)
1652                    s += PutInCol(Yfmt(ndecSU,yw),9)
1653                    WriteCIFitem("  "+s)
1654
1655        def WriteSingleXtalData(histlbl):
1656            histblk = self.Histograms[histlbl]
1657            #refprx = '_refln.' # mm
1658            refprx = '_refln_' # normal
1659
1660            WriteCIFitem('\n# STRUCTURE FACTOR TABLE')           
1661            WriteCIFitem('loop_' + 
1662                         '\n\t' + refprx + 'index_h' + 
1663                         '\n\t' + refprx + 'index_k' + 
1664                         '\n\t' + refprx + 'index_l' +
1665                         '\n\t' + refprx + 'F_squared_meas' + 
1666                         '\n\t' + refprx + 'F_squared_sigma' + 
1667                         '\n\t' + refprx + 'F_squared_calc' + 
1668                         '\n\t' + refprx + 'phase_calc'
1669                         )
1670
1671            hklmin = None
1672            hklmax = None
1673            dmax = None
1674            dmin = None
1675            refcount = len(histblk['Data'])
1676            for ref in histblk['Data']:
1677                s = "  "
1678                if hklmin is None:
1679                    hklmin = ref[0:3]
1680                    hklmax = ref[0:3]
1681                    dmax = dmin = ref[4]
1682                for i,hkl in enumerate(ref[0:3]):
1683                    hklmax[i] = max(hkl,hklmax[i])
1684                    hklmin[i] = min(hkl,hklmin[i])
1685                    s += PutInCol(int(hkl),4)
1686                sig = ref[6] * ref[8] / ref[5]
1687                s += PutInCol(G2mth.ValEsd(ref[8],-abs(sig/10)),12)
1688                s += PutInCol(G2mth.ValEsd(sig,-abs(sig)/10.),10)
1689                s += PutInCol(G2mth.ValEsd(ref[9],-abs(sig/10)),12)
1690                s += PutInCol(G2mth.ValEsd(ref[10],-0.9),7)
1691                dmax = max(dmax,ref[4])
1692                dmin = min(dmin,ref[4])
1693                WriteCIFitem(s)
1694            WriteReflStat(refcount,hklmin,hklmax,dmin,dmax)
1695            hId = histblk['hId']
1696            pfx = '0:'+str(hId)+':'
1697            WriteCIFitem('_reflns_wR_factor_obs    ','%.4f'%(histblk['wR']/100.))
1698            WriteCIFitem('_reflns_R_F_factor_obs   ','%.4f'%(histblk[pfx+'Rf']/100.))
1699            WriteCIFitem('_reflns_R_Fsqd_factor_obs','%.4f'%(histblk[pfx+'Rf^2']/100.))
1700        def EditAuthor(event=None):
1701            'Edit the CIF author name'
1702            dlg = G2gd.SingleStringDialog(self.G2frame,
1703                                          'Get CIF Author',
1704                                          'Provide CIF Author name (Last, First)',
1705                                          value=self.author)
1706            if not dlg.Show():
1707                dlg.Destroy()
1708                return False  # cancel was pressed
1709            self.author = dlg.GetValue()
1710            dlg.Destroy()
1711            try:
1712                self.OverallParms['Controls']["Author"] = self.author # save for future
1713            except KeyError:
1714                pass
1715            return True
1716        def EditInstNames(event=None):
1717            'Provide a dialog for editing instrument names'
1718            dictlist = []
1719            keylist = []
1720            lbllist = []
1721            for hist in self.Histograms:
1722                if hist.startswith("PWDR"): 
1723                    key2 = "Sample Parameters"
1724                    d = self.Histograms[hist][key2]
1725                elif hist.startswith("HKLF"): 
1726                    key2 = "Instrument Parameters"
1727                    d = self.Histograms[hist][key2][0]
1728                   
1729                lbllist.append(hist)
1730                dictlist.append(d)
1731                keylist.append('InstrName')
1732                instrname = d.get('InstrName')
1733                if instrname is None:
1734                    d['InstrName'] = ''
1735            return G2gd.CallScrolledMultiEditor(
1736                self.G2frame,dictlist,keylist,
1737                prelbl=range(1,len(dictlist)+1),
1738                postlbl=lbllist,
1739                title='Instrument names',
1740                header="Edit instrument names. Note that a non-blank\nname is required for all histograms",
1741                CopyButton=True)
1742           
1743        def EditRanges(event):
1744            but = event.GetEventObject()
1745            phasedict = but.phasedict
1746            dlg = G2gd.DisAglDialog(self.G2frame,{},phasedict['General'])
1747            if dlg.ShowModal() == wx.ID_OK:
1748                phasedict['General']['DisAglCtls'] = dlg.GetData()
1749            dlg.Destroy()
1750           
1751        def EditCIFDefaults():
1752            'Fills the CIF Defaults window'
1753            import wx.lib.scrolledpanel as wxscroll
1754            self.cifdefs.DestroyChildren()
1755            self.cifdefs.SetTitle('Edit CIF settings')
1756            vbox = wx.BoxSizer(wx.VERTICAL)
1757            cpnl = wxscroll.ScrolledPanel(self.cifdefs,size=(300,300))
1758            cbox = wx.BoxSizer(wx.VERTICAL)
1759            but = wx.Button(cpnl, wx.ID_ANY,'Edit CIF Author')
1760            but.Bind(wx.EVT_BUTTON,EditAuthor)
1761            cbox.Add(but,0,wx.ALIGN_CENTER,3)
1762            but = wx.Button(cpnl, wx.ID_ANY,'Edit Instrument Name(s)')
1763            but.Bind(wx.EVT_BUTTON,EditInstNames)
1764            cbox.Add(but,0,wx.ALIGN_CENTER,3)
1765            G2gd.HorizontalLine(cbox,cpnl)         
1766            cbox.Add(
1767                CIFtemplateSelect(self.cifdefs,
1768                                  cpnl,'publ',self.OverallParms['Controls'],
1769                                  EditCIFDefaults,
1770                                  "Publication (overall) template",
1771                                  ),
1772                0,wx.EXPAND|wx.ALIGN_LEFT|wx.ALL)
1773            for phasenam in sorted(self.Phases.keys()):
1774                G2gd.HorizontalLine(cbox,cpnl)         
1775                title = 'Phase '+phasenam
1776                phasedict = self.Phases[phasenam] # pointer to current phase info           
1777                cbox.Add(
1778                    CIFtemplateSelect(self.cifdefs,
1779                                      cpnl,'phase',phasedict['General'],
1780                                      EditCIFDefaults,
1781                                      title),
1782                    0,wx.EXPAND|wx.ALIGN_LEFT|wx.ALL)
1783                cpnl.SetSizer(cbox)
1784                but = wx.Button(cpnl, wx.ID_ANY,'Edit distance/angle ranges')
1785                #cbox.Add(but,0,wx.ALIGN_CENTER,3)
1786                cbox.Add((-1,2))
1787                cbox.Add(but,0,wx.ALIGN_LEFT,0)
1788                but.phasedict = self.Phases[phasenam]  # set a pointer to current phase info     
1789                but.Bind(wx.EVT_BUTTON,EditRanges)     # phase bond/angle ranges
1790            for i in sorted(self.powderDict.keys()):
1791                G2gd.HorizontalLine(cbox,cpnl)         
1792                hist = self.powderDict[i]
1793                histblk = self.Histograms[hist]
1794                title = 'Powder dataset '+hist[5:]
1795                cbox.Add(
1796                    CIFtemplateSelect(self.cifdefs,
1797                                      cpnl,'powder',histblk["Sample Parameters"],
1798                                      EditCIFDefaults,
1799                                      title),
1800                    0,wx.EXPAND|wx.ALIGN_LEFT|wx.ALL)
1801                cpnl.SetSizer(cbox)
1802            for i in sorted(self.xtalDict.keys()):
1803                G2gd.HorizontalLine(cbox,cpnl)         
1804                hist = self.xtalDict[i]
1805                histblk = self.Histograms[hist]
1806                title = 'Single Xtal dataset '+hist[5:]
1807                cbox.Add(
1808                    CIFtemplateSelect(self.cifdefs,
1809                                      cpnl,'single',histblk["Instrument Parameters"][0],
1810                                      EditCIFDefaults,
1811                                      title),
1812                    0,wx.EXPAND|wx.ALIGN_LEFT|wx.ALL)
1813                cpnl.SetSizer(cbox)
1814
1815            cpnl.SetAutoLayout(1)
1816            cpnl.SetupScrolling()
1817            #cpnl.Bind(rw.EVT_RW_LAYOUT_NEEDED, self.OnLayoutNeeded) # needed if sizes change
1818            cpnl.Layout()
1819
1820            vbox.Add(cpnl, 1, wx.ALIGN_LEFT|wx.ALL|wx.EXPAND, 0)
1821            btnsizer = wx.StdDialogButtonSizer()
1822            btn = wx.Button(self.cifdefs, wx.ID_OK, "Create CIF")
1823            btn.SetDefault()
1824            btnsizer.AddButton(btn)
1825            btn = wx.Button(self.cifdefs, wx.ID_CANCEL)
1826            btnsizer.AddButton(btn)
1827            btnsizer.Realize()
1828            vbox.Add(btnsizer, 0, wx.ALIGN_CENTER|wx.ALL, 5)
1829            self.cifdefs.SetSizer(vbox)
1830            vbox.Fit(self.cifdefs)
1831            self.cifdefs.Layout()
1832
1833# ===== end of functions for export method =======================================
1834#=================================================================================
1835
1836        # the export process starts here
1837        # load all of the tree into a set of dicts
1838        self.loadTree()
1839        # create a dict with refined values and their uncertainties
1840        self.loadParmDict()
1841
1842        # Someday: get restraint & constraint info
1843        #restraintDict = self.OverallParms.get('Restraints',{})
1844        #for i in  self.OverallParms['Constraints']:
1845        #    print i
1846        #    for j in self.OverallParms['Constraints'][i]:
1847        #        print j
1848
1849        self.CIFdate = dt.datetime.strftime(dt.datetime.now(),"%Y-%m-%dT%H:%M")
1850        # index powder and single crystal histograms
1851        self.powderDict = {}
1852        self.xtalDict = {}
1853        for hist in self.Histograms:
1854            i = self.Histograms[hist]['hId']
1855            if hist.startswith("PWDR"): 
1856                self.powderDict[i] = hist
1857            elif hist.startswith("HKLF"): 
1858                self.xtalDict[i] = hist
1859        # is there anything to export?
1860        if len(self.Phases) == len(self.powderDict) == len(self.xtalDict) == 0:
1861           self.G2frame.ErrorDialog(
1862               'Empty project',
1863               'Project does not contain interconnected data & phase(s)')
1864           return
1865        # get the project file name
1866        self.CIFname = os.path.splitext(
1867            os.path.split(self.G2frame.GSASprojectfile)[1]
1868            )[0]
1869        self.CIFname = self.CIFname.replace(' ','')
1870        if not self.CIFname: # none defined & needed, save as GPX to get one
1871            self.G2frame.OnFileSaveas(None)
1872            if not self.G2frame.GSASprojectfile: return
1873            self.CIFname = os.path.splitext(
1874                os.path.split(self.G2frame.GSASprojectfile)[1]
1875                )[0]
1876            self.CIFname = self.CIFname.replace(' ','')
1877        # test for quick CIF mode or no data
1878        self.quickmode = False
1879        phasenam = phasenum = None # include all phases
1880        if mode != "full" or len(self.powderDict) + len(self.xtalDict) == 0:
1881            self.quickmode = True
1882            oneblock = True
1883            if len(self.Phases) == 0:
1884                self.G2frame.ErrorDialog(
1885                    'No phase present',
1886                    'Cannot create a coordinates CIF with no phases')
1887                return
1888            elif len(self.Phases) > 1: # quick mode: choose one phase
1889                choices = sorted(self.Phases.keys())
1890                phasenum = G2gd.ItemSelector(choices,self.G2frame)
1891                if phasenum is None: return
1892                phasenam = choices[phasenum]
1893        # will this require a multiblock CIF?
1894        elif len(self.Phases) > 1:
1895            oneblock = False
1896        elif len(self.powderDict) + len(self.xtalDict) > 1:
1897            oneblock = False
1898        else: # one phase, one dataset, Full CIF
1899            oneblock = True
1900
1901        # make sure needed infomation is present
1902        # get CIF author name -- required for full CIFs
1903        try:
1904            self.author = self.OverallParms['Controls'].get("Author",'').strip()
1905        except KeyError:
1906            pass
1907        while not (self.author or self.quickmode):
1908            if not EditAuthor(): return
1909        self.shortauthorname = self.author.replace(',','').replace(' ','')[:20]
1910
1911        # check there is an instrument name for every histogram
1912        if not self.quickmode:
1913            invalid = 0
1914            key3 = 'InstrName'
1915            for hist in self.Histograms:
1916                if hist.startswith("PWDR"): 
1917                    key2 = "Sample Parameters"
1918                    d = self.Histograms[hist][key2]
1919                elif hist.startswith("HKLF"): 
1920                    key2 = "Instrument Parameters"
1921                    d = self.Histograms[hist][key2][0]                   
1922                instrname = d.get(key3)
1923                if instrname is None:
1924                    d[key3] = ''
1925                    invalid += 1
1926                elif instrname.strip() == '':
1927                    invalid += 1
1928            if invalid:
1929                msg = ""
1930                if invalid > 3: msg = (
1931                    "\n\nNote: it may be faster to set the name for\n"
1932                    "one histogram for each instrument and use the\n"
1933                    "File/Copy option to duplicate the name"
1934                    )
1935                if not EditInstNames(): return
1936        # check for a distance-angle range search range for each phase
1937        if not self.quickmode:
1938            for phasenam in sorted(self.Phases.keys()):
1939                #i = self.Phases[phasenam]['pId']
1940                phasedict = self.Phases[phasenam] # pointer to current phase info           
1941                if 'DisAglCtls' not in phasedict['General']:
1942                    dlg = G2gd.DisAglDialog(self.G2frame,{},phasedict['General'])
1943                    if dlg.ShowModal() == wx.ID_OK:
1944                        phasedict['General']['DisAglCtls'] = dlg.GetData()
1945                    else:
1946                        dlg.Destroy()
1947                        return
1948                    dlg.Destroy()
1949
1950        if oneblock and not self.quickmode:
1951            # select a dataset to use (there should only be one set in one block,
1952            # but take whatever comes 1st)
1953            for hist in self.Histograms:
1954                histblk = self.Histograms[hist]
1955                if hist.startswith("PWDR"): 
1956                    instnam = histblk["Sample Parameters"]['InstrName']
1957                    break # ignore all but 1st data histogram
1958                elif hist.startswith("HKLF"): 
1959                    instnam = histblk["Instrument Parameters"][0]['InstrName']
1960                    break # ignore all but 1st data histogram
1961        if self.quickmode:
1962            fil = self.askSaveFile()
1963        else:
1964            fil = self.defSaveFile()
1965        if not fil: return
1966        if not self.quickmode: # give the user a chance to edit all defaults
1967            self.cifdefs = wx.Dialog(
1968                self.G2frame,style=wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER)
1969            EditCIFDefaults()
1970            val = self.cifdefs.ShowModal()
1971            self.cifdefs.Destroy()
1972            if val != wx.ID_OK:
1973                return
1974        #======================================================================
1975        # Start writing the CIF - single block
1976        #======================================================================
1977        openCIF(fil)
1978        if oneblock:
1979            WriteCIFitem('data_'+self.CIFname)
1980            if phasenam is None: # if not already selected, select the first phase (should be one)
1981                phasenam = self.Phases.keys()[0]
1982            #print 'phasenam',phasenam
1983            phaseblk = self.Phases[phasenam] # pointer to current phase info
1984            if not self.quickmode:
1985                instnam = instnam.replace(' ','')
1986                WriteCIFitem('_pd_block_id',
1987                             str(self.CIFdate) + "|" + str(self.CIFname) + "|" +
1988                             str(self.shortauthorname) + "|" + instnam)
1989                WriteAudit()
1990                WritePubTemplate()
1991                WriteOverall()
1992                WritePhaseTemplate()
1993            # report the phase info
1994            WritePhaseInfo(phasenam)
1995            if hist.startswith("PWDR") and not self.quickmode:
1996                # preferred orientation
1997                SH = FormatSH(phasenam)
1998                MD = FormatHAPpo(phasenam)
1999                if SH and MD:
2000                    WriteCIFitem('_pd_proc_ls_pref_orient_corr', SH + '\n' + MD)
2001                elif SH or MD:
2002                    WriteCIFitem('_pd_proc_ls_pref_orient_corr', SH + MD)
2003                else:
2004                    WriteCIFitem('_pd_proc_ls_pref_orient_corr', 'none')
2005                    # report profile, since one-block: include both histogram and phase info
2006                WriteCIFitem('_pd_proc_ls_profile_function',
2007                    FormatInstProfile(histblk["Instrument Parameters"],histblk['hId'])
2008                    +'\n'+FormatPhaseProfile(phasenam))
2009                WritePowderTemplate()
2010                WritePowderData(hist)
2011            elif hist.startswith("HKLF") and not self.quickmode:
2012                WriteSnglXtalTemplate()
2013                WriteSingleXtalData(hist)
2014        else:
2015        #======================================================================
2016        # Start writing the CIF - multiblock
2017        #======================================================================
2018            # publication info
2019            WriteCIFitem('\ndata_'+self.CIFname+'_publ')
2020            WriteAudit()
2021            WriteCIFitem('_pd_block_id',
2022                         str(self.CIFdate) + "|" + str(self.CIFname) + "|" +
2023                         str(self.shortauthorname) + "|Overall")
2024            WritePubTemplate()
2025            # overall info
2026            WriteCIFitem('data_'+str(self.CIFname)+'_overall')
2027            WriteOverall()
2028            #============================================================
2029            WriteCIFitem('# POINTERS TO PHASE AND HISTOGRAM BLOCKS')
2030            datablockidDict = {} # save block names here -- N.B. check for conflicts between phase & hist names (unlikely!)
2031            # loop over phase blocks
2032            if len(self.Phases) > 1:
2033                loopprefix = ''
2034                WriteCIFitem('loop_   _pd_phase_block_id')
2035            else:
2036                loopprefix = '_pd_phase_block_id'
2037           
2038            for phasenam in sorted(self.Phases.keys()):
2039                i = self.Phases[phasenam]['pId']
2040                datablockidDict[phasenam] = (str(self.CIFdate) + "|" + str(self.CIFname) + "|" +
2041                             'phase_'+ str(i) + '|' + str(self.shortauthorname))
2042                WriteCIFitem(loopprefix,datablockidDict[phasenam])
2043            # loop over data blocks
2044            if len(self.powderDict) + len(self.xtalDict) > 1:
2045                loopprefix = ''
2046                WriteCIFitem('loop_   _pd_block_diffractogram_id')
2047            else:
2048                loopprefix = '_pd_block_diffractogram_id'
2049            for i in sorted(self.powderDict.keys()):
2050                hist = self.powderDict[i]
2051                histblk = self.Histograms[hist]
2052                instnam = histblk["Sample Parameters"]['InstrName']
2053                instnam = instnam.replace(' ','')
2054                i = histblk['hId']
2055                datablockidDict[hist] = (str(self.CIFdate) + "|" + str(self.CIFname) + "|" +
2056                                         str(self.shortauthorname) + "|" +
2057                                         instnam + "_hist_"+str(i))
2058                WriteCIFitem(loopprefix,datablockidDict[hist])
2059            for i in sorted(self.xtalDict.keys()):
2060                hist = self.xtalDict[i]
2061                histblk = self.Histograms[hist]
2062                instnam = histblk["Instrument Parameters"][0]['InstrName']
2063                instnam = instnam.replace(' ','')
2064                i = histblk['hId']
2065                datablockidDict[hist] = (str(self.CIFdate) + "|" + str(self.CIFname) + "|" +
2066                                         str(self.shortauthorname) + "|" +
2067                                         instnam + "_hist_"+str(i))
2068                WriteCIFitem(loopprefix,datablockidDict[hist])
2069            #============================================================
2070            # loop over phases, exporting them
2071            phasebyhistDict = {} # create a cross-reference to phases by histogram
2072            for j,phasenam in enumerate(sorted(self.Phases.keys())):
2073                i = self.Phases[phasenam]['pId']
2074                WriteCIFitem('\ndata_'+self.CIFname+"_phase_"+str(i))
2075                print "debug, processing ",phasenam
2076                WriteCIFitem('# Information for phase '+str(i))
2077                WriteCIFitem('_pd_block_id',datablockidDict[phasenam])
2078                # report the phase
2079                WritePhaseTemplate()
2080                WritePhaseInfo(phasenam)
2081                # preferred orientation
2082                SH = FormatSH(phasenam)
2083                MD = FormatHAPpo(phasenam)
2084                if SH and MD:
2085                    WriteCIFitem('_pd_proc_ls_pref_orient_corr', SH + '\n' + MD)
2086                elif SH or MD:
2087                    WriteCIFitem('_pd_proc_ls_pref_orient_corr', SH + MD)
2088                else:
2089                    WriteCIFitem('_pd_proc_ls_pref_orient_corr', 'none')
2090                # report sample profile terms
2091                PP = FormatPhaseProfile(phasenam)
2092                if PP:
2093                    WriteCIFitem('_pd_proc_ls_profile_function',PP)
2094                   
2095            #============================================================
2096            # loop over histograms, exporting them
2097            for i in sorted(self.powderDict.keys()):
2098                hist = self.powderDict[i]
2099                histblk = self.Histograms[hist]
2100                if hist.startswith("PWDR"): 
2101                    WriteCIFitem('\ndata_'+self.CIFname+"_pwd_"+str(i))
2102                    #instnam = histblk["Sample Parameters"]['InstrName']
2103                    # report instrumental profile terms
2104                    WriteCIFitem('_pd_proc_ls_profile_function',
2105                        FormatInstProfile(histblk["Instrument Parameters"],histblk['hId']))
2106                    WriteCIFitem('# Information for histogram '+str(i)+': '+hist)
2107                    WriteCIFitem('_pd_block_id',datablockidDict[hist])
2108                    WritePowderTemplate()
2109                    WritePowderData(hist)
2110            for i in sorted(self.xtalDict.keys()):
2111                hist = self.xtalDict[i]
2112                histblk = self.Histograms[hist]
2113                if hist.startswith("HKLF"): 
2114                    WriteCIFitem('\ndata_'+self.CIFname+"_sx_"+str(i))
2115                    #instnam = histblk["Instrument Parameters"][0]['InstrName']
2116                    WriteCIFitem('# Information for histogram '+str(i)+': '+hist)
2117                    WriteCIFitem('_pd_block_id',datablockidDict[hist])
2118                    WriteSnglXtalTemplate()
2119                    WriteSingleXtalData(hist)
2120
2121        WriteCIFitem('#--' + 15*'eof--' + '#')
2122        closeCIF()
Note: See TracBrowser for help on using the repository browser.