source: trunk/GSASIIIO.py @ 2757

Last change on this file since 2757 was 2754, checked in by vondreele, 8 years ago

fix "off-by-one" problem in powder profiles; calc missed last point - now fixed.
add import of reflectometry data - same as small angle data
implement plotting of reflectometry data & all the scale options
set up the Model page for reflectometry (Layers is missing)

  • Property svn:eol-style set to native
  • Property svn:keywords set to Date Author Revision URL Id
File size: 117.5 KB
Line 
1# -*- coding: utf-8 -*-
2########### SVN repository information ###################
3# $Date: 2017-03-14 15:19:59 +0000 (Tue, 14 Mar 2017) $
4# $Author: vondreele $
5# $Revision: 2754 $
6# $URL: trunk/GSASIIIO.py $
7# $Id: GSASIIIO.py 2754 2017-03-14 15:19:59Z vondreele $
8########### SVN repository information ###################
9'''
10*GSASIIIO: Misc I/O routines*
11=============================
12
13Module with miscellaneous routines for input and output. Many
14are GUI routines to interact with user.
15
16Includes support for image reading.
17
18Also includes base classes for data import routines.
19
20'''
21"""GSASIIIO: functions for IO of data
22   Copyright: 2008, Robert B. Von Dreele (Argonne National Laboratory)
23"""
24import wx
25import math
26import numpy as np
27import copy
28import cPickle
29import sys
30import re
31import random as ran
32import GSASIIpath
33GSASIIpath.SetVersionNumber("$Revision: 2754 $")
34import GSASIIgrid as G2gd
35import GSASIIspc as G2spc
36import GSASIIobj as G2obj
37import GSASIIlattice as G2lat
38import GSASIImath as G2mth
39import GSASIIpwdGUI as G2pdG
40import GSASIIimgGUI as G2imG
41import GSASIIimage as G2img
42import GSASIIElem as G2el
43import GSASIIstrIO as G2stIO
44import GSASIImapvars as G2mv
45import GSASIIctrls as G2G
46import os
47import os.path as ospath
48
49DEBUG = False       #=True for various prints
50TRANSP = False      #=true to transpose images for testing
51if GSASIIpath.GetConfigValue('Transpose'): TRANSP = True
52npsind = lambda x: np.sin(x*np.pi/180.)
53
54def sfloat(S):
55    'Convert a string to float. An empty field or a unconvertable value is treated as zero'
56    if S.strip():
57        try:
58            return float(S)
59        except ValueError:
60            pass
61    return 0.0
62
63def sint(S):
64    'Convert a string to int. An empty field is treated as zero'
65    if S.strip():
66        return int(S)
67    else:
68        return 0
69
70def trim(val):
71    '''Simplify a string containing leading and trailing spaces
72    as well as newlines, tabs, repeated spaces etc. into a shorter and
73    more simple string, by replacing all ranges of whitespace
74    characters with a single space.
75
76    :param str val: the string to be simplified
77
78    :returns: the (usually) shortened version of the string
79    '''
80    return re.sub('\s+', ' ', val).strip()
81
82def makeInstDict(names,data,codes):
83    inst = dict(zip(names,zip(data,data,codes)))
84    for item in inst:
85        inst[item] = list(inst[item])
86    return inst
87
88def FileDlgFixExt(dlg,file):
89    'this is needed to fix a problem in linux wx.FileDialog'
90    ext = dlg.GetWildcard().split('|')[2*dlg.GetFilterIndex()+1].strip('*')
91    if ext not in file:
92        file += ext
93    return file
94       
95def GetPowderPeaks(fileName):
96    'Read powder peaks from a file'
97    sind = lambda x: math.sin(x*math.pi/180.)
98    asind = lambda x: 180.*math.asin(x)/math.pi
99    wave = 1.54052
100    File = open(fileName,'Ur')
101    Comments = []
102    peaks = []
103    S = File.readline()
104    while S:
105        if S[:1] == '#':
106            Comments.append(S[:-1])
107        else:
108            item = S.split()
109            if len(item) == 1:
110                peaks.append([float(item[0]),1.0])
111            elif len(item) > 1:
112                peaks.append([float(item[0]),float(item[0])])
113        S = File.readline()
114    File.close()
115    if Comments:
116       print 'Comments on file:'
117       for Comment in Comments: 
118            print Comment
119            if 'wavelength' in Comment:
120                wave = float(Comment.split('=')[1])
121    Peaks = []
122    if peaks[0][0] > peaks[-1][0]:          # d-spacings - assume CuKa
123        for peak in peaks:
124            dsp = peak[0]
125            sth = wave/(2.0*dsp)
126            if sth < 1.0:
127                tth = 2.0*asind(sth)
128            else:
129                break
130            Peaks.append([tth,peak[1],True,False,0,0,0,dsp,0.0])
131    else:                                   #2-thetas - assume Cuka (for now)
132        for peak in peaks:
133            tth = peak[0]
134            dsp = wave/(2.0*sind(tth/2.0))
135            Peaks.append([tth,peak[1],True,False,0,0,0,dsp,0.0])
136    limits = [1000.,0.]
137    for peak in Peaks:
138        limits[0] = min(limits[0],peak[0])
139        limits[1] = max(limits[1],peak[0])
140    limits[0] = max(1.,(int(limits[0]-1.)/5)*5.)
141    limits[1] = min(170.,(int(limits[1]+1.)/5)*5.)
142    return Comments,Peaks,limits,wave
143
144def GetCheckImageFile(G2frame,treeId):
145    '''Try to locate an image file if the project and image have been moved
146    together. If the image file cannot be found, request the location from
147    the user.
148
149    :param wx.Frame G2frame: main GSAS-II Frame and data object
150    :param wx.Id treeId: Id for the main tree item for the image
151    :returns: Npix,imagefile,imagetag with (Npix) number of pixels,
152       imagefile, if it exists, or the name of a file that does exist or False if the user presses Cancel
153       and (imagetag) an optional image number
154
155    '''
156    Npix,Imagefile,imagetag = G2frame.PatternTree.GetImageLoc(treeId)
157    if isinstance(Imagefile,list):
158        imagefile,imagetag = Imagefile
159    else:
160        imagefile = Imagefile
161    if not os.path.exists(imagefile):
162        print 'Image file '+imagefile+' not found'
163        fil = imagefile.replace('\\','/') # windows?!
164        # see if we can find a file with same name or in a similarly named sub-dir
165        pth,fil = os.path.split(fil)
166        prevpth = None
167        while pth and pth != prevpth:
168            prevpth = pth
169            if os.path.exists(os.path.join(G2frame.dirname,fil)):
170                print 'found image file '+os.path.join(G2frame.dirname,fil)
171                imagefile = os.path.join(G2frame.dirname,fil)
172                G2frame.PatternTree.UpdateImageLoc(treeId,imagefile)
173                return Npix,imagefile,imagetag
174            pth,enddir = os.path.split(pth)
175            fil = os.path.join(enddir,fil)
176        # not found as a subdirectory, drop common parts of path for last saved & image file names
177        #    if image was .../A/B/C/imgs/ima.ge
178        #      & GPX was  .../A/B/C/refs/fil.gpx but is now .../NEW/TEST/TEST1
179        #    will look for .../NEW/TEST/TEST1/imgs/ima.ge, .../NEW/TEST/imgs/ima.ge, .../NEW/imgs/ima.ge and so on
180        Controls = G2frame.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(G2frame,G2frame.root, 'Controls'))
181        gpxPath = Controls.get('LastSavedAs','').replace('\\','/').split('/') # blank in older .GPX files
182        imgPath = imagefile.replace('\\','/').split('/')
183        for p1,p2 in zip(gpxPath,imgPath):
184            if p1 == p2:
185                gpxPath.pop(0),imgPath.pop(0)
186            else:
187                break
188        fil = os.path.join(*imgPath) # file with non-common prefix elements
189        prevpth = None
190        pth = os.path.abspath(G2frame.dirname)
191        while pth and pth != prevpth:
192            prevpth = pth
193            if os.path.exists(os.path.join(pth,fil)):
194                print 'found image file '+os.path.join(pth,fil)
195                imagefile = os.path.join(pth,fil)
196                G2frame.PatternTree.UpdateImageLoc(treeId,imagefile)
197                return Npix,imagefile,imagetag
198            pth,enddir = os.path.split(pth)
199        #GSASIIpath.IPyBreak()
200
201    if not os.path.exists(imagefile):
202        prevnam = os.path.split(imagefile)[1]
203        prevext = os.path.splitext(imagefile)[1]
204        wildcard = 'Image format (*'+prevext+')|*'+prevext
205        dlg = wx.FileDialog(G2frame, 'Previous image file ('+prevnam+') not found; open here', '.', prevnam,
206                            wildcard,wx.OPEN)
207        try:
208            dlg.SetFilename(''+ospath.split(imagefile)[1])
209            if dlg.ShowModal() == wx.ID_OK:
210                imagefile = dlg.GetPath()
211                G2frame.PatternTree.UpdateImageLoc(treeId,imagefile)
212            else:
213                imagefile = False
214        finally:
215            dlg.Destroy()
216    return Npix,imagefile,imagetag
217
218def EditImageParms(parent,Data,Comments,Image,filename):
219    dlg = wx.Dialog(parent, wx.ID_ANY, 'Edit image parameters',
220                    style=wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER)
221    def onClose(event):
222        dlg.EndModal(wx.ID_OK)
223    mainsizer = wx.BoxSizer(wx.VERTICAL)
224    h,w = Image.shape[:2]
225    mainsizer.Add(wx.StaticText(dlg,wx.ID_ANY,
226                                'File '+str(filename)+'\nImage size: '+str(h)+' x '+str(w)),
227                  0,wx.ALIGN_LEFT|wx.ALL, 2)
228   
229    vsizer = wx.BoxSizer(wx.HORIZONTAL)
230    vsizer.Add(wx.StaticText(dlg,wx.ID_ANY,u'Wavelength (\xC5) '),
231               0,wx.ALIGN_LEFT|wx.ALL, 2)
232    wdgt = G2G.ValidatedTxtCtrl(dlg,Data,'wavelength')
233    vsizer.Add(wdgt)
234    mainsizer.Add(vsizer,0,wx.ALIGN_LEFT|wx.ALL, 2)
235
236    vsizer = wx.BoxSizer(wx.HORIZONTAL)
237    vsizer.Add(wx.StaticText(dlg,wx.ID_ANY,u'Pixel size (\xb5m). Width '),
238               0,wx.ALIGN_LEFT|wx.ALL, 2)
239    wdgt = G2G.ValidatedTxtCtrl(dlg,Data['pixelSize'],0,
240                                 size=(50,-1))
241    vsizer.Add(wdgt)
242    vsizer.Add(wx.StaticText(dlg,wx.ID_ANY,u'  Height '),
243               wx.ALIGN_LEFT|wx.ALL, 2)
244    wdgt = G2G.ValidatedTxtCtrl(dlg,Data['pixelSize'],1,
245                                 size=(50,-1))
246    vsizer.Add(wdgt)
247    mainsizer.Add(vsizer,0,wx.ALIGN_LEFT|wx.ALL, 2)
248
249    vsizer = wx.BoxSizer(wx.HORIZONTAL)
250    vsizer.Add(wx.StaticText(dlg,wx.ID_ANY,u'Sample to detector (mm) '),
251               0,wx.ALIGN_LEFT|wx.ALL, 2)
252    wdgt = G2G.ValidatedTxtCtrl(dlg,Data,'distance')
253    vsizer.Add(wdgt)
254    mainsizer.Add(vsizer,0,wx.ALIGN_LEFT|wx.ALL, 2)
255
256    vsizer = wx.BoxSizer(wx.HORIZONTAL)
257    vsizer.Add(wx.StaticText(dlg,wx.ID_ANY,u'Beam center (pixels). X = '),
258               0,wx.ALIGN_LEFT|wx.ALL, 2)
259    wdgt = G2G.ValidatedTxtCtrl(dlg,Data['center'],0,
260                                 size=(75,-1))
261    vsizer.Add(wdgt)
262    vsizer.Add(wx.StaticText(dlg,wx.ID_ANY,u'  Y = '),
263               wx.ALIGN_LEFT|wx.ALL, 2)
264    wdgt = G2G.ValidatedTxtCtrl(dlg,Data['center'],1,
265                                 size=(75,-1))
266    vsizer.Add(wdgt)
267    mainsizer.Add(vsizer,0,wx.ALIGN_LEFT|wx.ALL, 2)
268
269    vsizer = wx.BoxSizer(wx.HORIZONTAL)
270    vsizer.Add(wx.StaticText(dlg,wx.ID_ANY,u'Comments '),
271               0,wx.ALIGN_LEFT|wx.ALL, 2)
272    wdgt = G2G.ValidatedTxtCtrl(dlg,Comments,0,size=(250,-1))
273    vsizer.Add(wdgt)
274    mainsizer.Add(vsizer,0,wx.ALIGN_LEFT|wx.ALL, 2)
275
276    btnsizer = wx.StdDialogButtonSizer()
277    OKbtn = wx.Button(dlg, wx.ID_OK, 'Continue')
278    OKbtn.SetDefault()
279    OKbtn.Bind(wx.EVT_BUTTON,onClose)
280    btnsizer.AddButton(OKbtn) # not sure why this is needed
281    btnsizer.Realize()
282    mainsizer.Add(btnsizer, 1, wx.ALIGN_CENTER|wx.ALL|wx.EXPAND, 5)
283    dlg.SetSizer(mainsizer)
284    dlg.CenterOnParent()
285    dlg.ShowModal()
286   
287def LoadImage2Tree(imagefile,G2frame,Comments,Data,Npix,Image):
288    '''Load an image into the tree. Saves the location of the image, as well as the
289    ImageTag (where there is more than one image in the file), if defined.
290    '''
291    ImgNames = G2gd.GetPatternTreeDataNames(G2frame,['IMG ',])
292    TreeLbl = 'IMG '+os.path.basename(imagefile)
293    ImageTag = Data.get('ImageTag')
294    if ImageTag:
295        TreeLbl += ' #'+'%04d'%(ImageTag)
296        imageInfo = (imagefile,ImageTag)
297    else:
298        imageInfo = imagefile
299    TreeName = G2obj.MakeUniqueLabel(TreeLbl,ImgNames)
300    Id = G2frame.PatternTree.AppendItem(parent=G2frame.root,text=TreeName)
301    G2frame.PatternTree.SetItemPyData(G2frame.PatternTree.AppendItem(Id,text='Comments'),Comments)
302    Imax = np.amax(Image)
303    if G2frame.imageDefault:
304        Data = copy.copy(G2frame.imageDefault)
305        Data['showLines'] = True
306        Data['ring'] = []
307        Data['rings'] = []
308        Data['cutoff'] = 10
309        Data['pixLimit'] = 20
310        Data['edgemin'] = 100000000
311        Data['calibdmin'] = 0.5
312        Data['calibskip'] = 0
313        Data['ellipses'] = []
314        Data['calibrant'] = ''
315        Data['GonioAngles'] = [0.,0.,0.]
316        Data['DetDepthRef'] = False
317    else:
318        Data['type'] = 'PWDR'
319        Data['color'] = 'Paired'
320        Data['tilt'] = 0.0
321        Data['rotation'] = 0.0
322        Data['showLines'] = False
323        Data['ring'] = []
324        Data['rings'] = []
325        Data['cutoff'] = 10
326        Data['pixLimit'] = 20
327        Data['calibdmin'] = 0.5
328        Data['calibskip'] = 0
329        Data['edgemin'] = 100000000
330        Data['ellipses'] = []
331        Data['GonioAngles'] = [0.,0.,0.]
332        Data['DetDepth'] = 0.
333        Data['DetDepthRef'] = False
334        Data['calibrant'] = ''
335        Data['IOtth'] = [2.0,5.0]
336        Data['LRazimuth'] = [0.,180.]
337        Data['azmthOff'] = 0.0
338        Data['outChannels'] = 2500
339        Data['outAzimuths'] = 1
340        Data['centerAzm'] = False
341        Data['fullIntegrate'] = False
342        Data['setRings'] = False
343        Data['background image'] = ['',-1.0]                           
344        Data['dark image'] = ['',-1.0]
345        Data['Flat Bkg'] = 0.0
346    Data['setDefault'] = False
347    Data['range'] = [(0,Imax),[0,Imax]]
348    G2frame.PatternTree.SetItemPyData(G2frame.PatternTree.AppendItem(Id,text='Image Controls'),Data)
349    Masks = {'Points':[],'Rings':[],'Arcs':[],'Polygons':[],'Frames':[],'Thresholds':[(0,Imax),[0,Imax]]}
350    G2frame.PatternTree.SetItemPyData(G2frame.PatternTree.AppendItem(Id,text='Masks'),Masks)
351    G2frame.PatternTree.SetItemPyData(G2frame.PatternTree.AppendItem(Id,text='Stress/Strain'),
352        {'Type':'True','d-zero':[],'Sample phi':0.0,'Sample z':0.0,'Sample load':0.0})
353    G2frame.PatternTree.SetItemPyData(Id,[Npix,imageInfo])
354    G2frame.PickId = Id
355    G2frame.PickIdText = G2frame.GetTreeItemsList(G2frame.PickId)
356    G2frame.Image = Id
357
358def GetImageData(G2frame,imagefile,imageOnly=False,ImageTag=None,FormatName=''):
359    '''Read a single image with an image importer.
360
361    :param wx.Frame G2frame: main GSAS-II Frame and data object.
362    :param str imagefile: name of image file
363    :param bool imageOnly: If True return only the image,
364      otherwise  (default) return more (see below)
365    :param int/str ImageTag: specifies a particular image to be read from a file.
366      First image is read if None (default).
367    :param str formatName: the image reader formatName
368
369    :returns: an image as a numpy array or a list of four items:
370      Comments, Data, Npix and the Image, as selected by imageOnly
371
372    '''
373    # determine which formats are compatible with this file
374    primaryReaders = []
375    secondaryReaders = []
376    for rd in G2frame.ImportImageReaderlist:
377        flag = rd.ExtensionValidator(imagefile)
378        if flag is None: 
379            secondaryReaders.append(rd)
380        elif flag:
381            if not FormatName:
382                primaryReaders.append(rd)
383            elif FormatName == rd.formatName:
384                primaryReaders.append(rd)
385    if len(secondaryReaders) + len(primaryReaders) == 0:
386        print('Error: No matching format for file '+imagefile)
387        raise Exception('No image read')
388    fp = None
389    errorReport = ''
390    if not imagefile:
391        return
392    fp = open(imagefile,'Ur')
393    for rd in primaryReaders+secondaryReaders:
394        rd.ReInitialize() # purge anything from a previous read
395        fp.seek(0)  # rewind
396        rd.errors = "" # clear out any old errors
397        if not rd.ContentsValidator(fp): # rejected on cursory check
398            errorReport += "\n  "+rd.formatName + ' validator error'
399            if rd.errors: 
400                errorReport += ': '+rd.errors
401                continue
402        if imageOnly:
403            ParentFrame = None # prevent GUI access on reread
404        else:
405            ParentFrame = G2frame
406        if GSASIIpath.GetConfigValue('debug'):
407            flag = rd.Reader(imagefile,fp,ParentFrame,blocknum=ImageTag)
408        else:
409            flag = False
410            try:
411                flag = rd.Reader(imagefile,fp,ParentFrame,blocknum=ImageTag)
412            except rd.ImportException as detail:
413                rd.errors += "\n  Read exception: "+str(detail)
414            except Exception as detail:
415                import traceback
416                rd.errors += "\n  Unhandled read exception: "+str(detail)
417                rd.errors += "\n  Traceback info:\n"+str(traceback.format_exc())
418        if flag: # this read succeeded
419            if rd.Image is None:
420                raise Exception('No image read. Strange!')
421            if GSASIIpath.GetConfigValue('Transpose'):
422                print 'Transposing Image!'
423                rd.Image = rd.Image.T
424            #rd.readfilename = imagefile
425            if imageOnly:
426                return rd.Image
427            else:
428                return rd.Comments,rd.Data,rd.Npix,rd.Image
429    else:
430        print('Error reading file '+imagefile)
431        print('Error messages(s)\n'+errorReport)
432        raise Exception('No image read')   
433
434def ReadImages(G2frame,imagefile):
435    '''Read one or more images from a file and put them into the Tree
436    using image importers. Called only in :meth:`AutoIntFrame.OnTimerLoop`.
437
438    :param wx.Frame G2frame: main GSAS-II Frame and data object.
439    :param str imagefile: name of image file
440
441    :returns: a list of the id's of the IMG tree items created
442    '''
443    # determine which formats are compatible with this file
444    primaryReaders = []
445    secondaryReaders = []
446    for rd in G2frame.ImportImageReaderlist:
447        flag = rd.ExtensionValidator(imagefile)
448        if flag is None:
449            secondaryReaders.append(rd)
450        elif flag:
451            primaryReaders.append(rd)
452    if len(secondaryReaders) + len(primaryReaders) == 0:
453        print('Error: No matching format for file '+imagefile)
454        raise Exception('No image read')
455    errorReport = ''
456    fp = open(imagefile,'Ur')
457    rdbuffer = {} # create temporary storage for file reader
458    for rd in primaryReaders+secondaryReaders:
459        rd.ReInitialize() # purge anything from a previous read
460        fp.seek(0)  # rewind
461        rd.errors = "" # clear out any old errors
462        if not rd.ContentsValidator(fp): # rejected on cursory check
463            errorReport += "\n  "+rd.formatName + ' validator error'
464            if rd.errors: 
465                errorReport += ': '+rd.errors
466                continue
467        ParentFrame = G2frame
468        block = 0
469        repeat = True
470        CreatedIMGitems = []
471        while repeat: # loop if the reader asks for another pass on the file
472            block += 1
473            repeat = False
474            if GSASIIpath.GetConfigValue('debug'):
475                flag = rd.Reader(imagefile,fp,ParentFrame,blocknum=block,Buffer=rdbuffer)
476            else:
477                flag = False
478                try:
479                    flag = rd.Reader(imagefile,fp,ParentFrame,blocknum=block,Buffer=rdbuffer)
480                except rd.ImportException as detail:
481                    rd.errors += "\n  Read exception: "+str(detail)
482                except Exception as detail:
483                    import traceback
484                    rd.errors += "\n  Unhandled read exception: "+str(detail)
485                    rd.errors += "\n  Traceback info:\n"+str(traceback.format_exc())
486            if flag: # this read succeeded
487                if rd.Image is None:
488                    raise Exception('No image read. Strange!')
489                if GSASIIpath.GetConfigValue('Transpose'):
490                    print 'Transposing Image!'
491                    rd.Image = rd.Image.T
492                rd.Data['ImageTag'] = rd.repeatcount
493                LoadImage2Tree(imagefile,G2frame,rd.Comments,rd.Data,rd.Npix,rd.Image)
494                repeat = rd.repeat
495            CreatedIMGitems.append(G2frame.Image)
496        if CreatedIMGitems: return CreatedIMGitems
497    else:
498        print('Error reading file '+imagefile)
499        print('Error messages(s)\n'+errorReport)
500        return []
501        #raise Exception('No image read')   
502
503def SaveMultipleImg(G2frame):
504    if not G2frame.PatternTree.GetCount():
505        print 'no images!'
506        return
507    choices = G2gd.GetPatternTreeDataNames(G2frame,['IMG ',])
508    if len(choices) == 1:
509        names = choices
510    else:
511        dlg = G2G.G2MultiChoiceDialog(G2frame,'Stress/Strain fitting','Select images to fit:',choices)
512        dlg.SetSelections([])
513        names = []
514        if dlg.ShowModal() == wx.ID_OK:
515            names = [choices[sel] for sel in dlg.GetSelections()]
516        dlg.Destroy()
517    if not names: return
518    for name in names:
519        Id = G2gd.GetPatternTreeItemId(G2frame, G2frame.root, name)
520        Npix,imagefile,imagetag = G2frame.PatternTree.GetImageLoc(Id)
521        imroot = os.path.splitext(imagefile)[0]
522        if imagetag:
523            imroot += '_' + str(imagetag)
524        Data = G2frame.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(G2frame,Id, 'Image Controls'))
525        print('Writing '+imroot+'.imctrl')
526        File = open(imroot+'.imctrl','w')
527        keys = ['type','wavelength','calibrant','distance','center',
528                    'tilt','rotation','azmthOff','fullIntegrate','LRazimuth',
529                    'IOtth','outChannels','outAzimuths','invert_x','invert_y','DetDepth',
530                    'calibskip','pixLimit','cutoff','calibdmin','chisq','Flat Bkg',
531                    'binType','SampleShape','PolaVal','SampleAbs','dark image','background image']
532        for key in keys:
533            if key not in Data: continue    #uncalibrated!
534            File.write(key+':'+str(Data[key])+'\n')
535        File.close()
536        mask = G2frame.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(G2frame,Id, 'Masks'))
537        G2imG.CleanupMasks(mask)
538        print('Writing '+imroot+'.immask')
539        File = open(imroot+'.immask','w')
540        for key in ['Points','Rings','Arcs','Polygons','Frames','Thresholds']:
541            File.write(key+':'+str(mask[key])+'\n')
542        File.close()
543       
544def PutG2Image(filename,Comments,Data,Npix,image):
545    'Write an image as a python pickle - might be better as an .edf file?'
546    File = open(filename,'wb')
547    cPickle.dump([Comments,Data,Npix,image],File,1)
548    File.close()
549    return
550   
551# should get moved to importer when ready to test
552def GetEdfData(filename,imageOnly=False):   
553    'Read European detector data edf file'
554    if not imageOnly:
555        print 'Read European detector data edf file: ',filename
556    File = open(filename,'rb')
557    fileSize = os.stat(filename).st_size
558    head = File.read(3072)
559    lines = head.split('\n')
560    sizexy = [0,0]
561    pixSize = [154,154]     #Pixium4700?
562    cent = [0,0]
563    wave = 1.54187  #default <CuKa>
564    dist = 1000.
565    head = ['European detector data',]
566    for line in lines:
567        line = line.replace(';',' ').strip()
568        fields = line.split()
569        if 'Dim_1' in line:
570            sizexy[0] = int(fields[2])
571        elif 'Dim_2' in line:
572            sizexy[1] = int(fields[2])
573        elif 'DataType' in line:
574            dType = fields[2]
575        elif 'wavelength' in line:
576            wave = float(fields[2])
577        elif 'Size' in line:
578            imSize = int(fields[2])
579#        elif 'DataType' in lines:
580#            dType = fields[2]
581        elif 'pixel_size_x' in line:
582            pixSize[0] = float(fields[2])
583        elif 'pixel_size_y' in line:
584            pixSize[1] = float(fields[2])
585        elif 'beam_center_x' in line:
586            cent[0] = float(fields[2])
587        elif 'beam_center_y' in line:
588            cent[1] = float(fields[2])
589        elif 'refined_distance' in line:
590            dist = float(fields[2])
591        if line:
592            head.append(line)
593        else:   #blank line at end of header
594            break 
595    File.seek(fileSize-imSize)
596    if dType == 'UnsignedShort':       
597        image = np.array(np.frombuffer(File.read(imSize),dtype=np.int16),dtype=np.int32)
598    else:
599        image = np.array(np.frombuffer(File.read(imSize),dtype=np.int32),dtype=np.int32)
600    image = np.reshape(image,(sizexy[1],sizexy[0]))
601    data = {'pixelSize':pixSize,'wavelength':wave,'distance':dist,'center':cent,'size':sizexy}
602    Npix = sizexy[0]*sizexy[1]
603    File.close()   
604    if imageOnly:
605        return image
606    else:
607        return head,data,Npix,image
608       
609# should get moved to importer when ready to test
610def GetRigaku(filename,imageOnly=False):
611    'Read Rigaku R-Axis IV image file'
612    import array as ar
613    if not imageOnly:
614        print 'Read Rigaku R-Axis IV file: ',filename   
615    File = open(filename,'rb')
616    fileSize = os.stat(filename).st_size
617    Npix = (fileSize-6000)/2
618    File.read(6000)
619    head = ['Rigaku R-Axis IV detector data',]
620    image = np.array(ar.array('H',File.read(fileSize-6000)),dtype=np.int32)
621    print fileSize,image.shape
622    print head
623    if Npix == 9000000:
624        sizexy = [3000,3000]
625        pixSize = [100.,100.]       
626    elif Npix == 2250000:
627        sizexy = [1500,1500]
628        pixSize = [200.,200.]
629    else:
630        sizexy = [6000,6000]
631        pixSize = [50.,50.] 
632    image = np.reshape(image,(sizexy[1],sizexy[0]))       
633    data = {'pixelSize':pixSize,'wavelength':1.5428,'distance':250.0,'center':[150.,150.],'size':sizexy} 
634    File.close()   
635    if imageOnly:
636        return image
637    else:
638        return head,data,Npix,image
639   
640# should get moved to importer when ready to test       
641def GetImgData(filename,imageOnly=False):
642    'Read an ADSC image file'
643    import array as ar
644    if not imageOnly:
645        print 'Read ADSC img file: ',filename
646    File = open(filename,'rb')
647    head = File.read(511)
648    lines = head.split('\n')
649    head = []
650    center = [0,0]
651    for line in lines[1:-2]:
652        line = line.strip()[:-1]
653        if line:
654            if 'SIZE1' in line:
655                size = int(line.split('=')[1])
656                Npix = size*size
657            elif 'WAVELENGTH' in line:
658                wave = float(line.split('=')[1])
659            elif 'BIN' in line:
660                if line.split('=')[1] == '2x2':
661                    pixel=(102,102)
662                else:
663                    pixel = (51,51)
664            elif 'DISTANCE' in line:
665                distance = float(line.split('=')[1])
666            elif 'CENTER_X' in line:
667                center[0] = float(line.split('=')[1])
668            elif 'CENTER_Y' in line:
669                center[1] = float(line.split('=')[1])
670            head.append(line)
671    data = {'pixelSize':pixel,'wavelength':wave,'distance':distance,'center':center,'size':[size,size]}
672    image = []
673    pos = 512
674    File.seek(pos)
675    image = np.array(ar.array('H',File.read(2*Npix)),dtype=np.int32)
676    image = np.reshape(image,(size,size))
677#    image = np.zeros(shape=(size,size),dtype=np.int32)   
678#    while row < size:
679#        File.seek(pos)
680#        line = ar.array('H',File.read(2*size))
681#        image[row] = np.asarray(line)
682#        row += 1
683#        pos += 2*size
684    File.close()
685    if imageOnly:
686        return image
687    else:
688        return lines[1:-2],data,Npix,image
689       
690# should get moved to importer when ready to test
691def GetMAR345Data(filename,imageOnly=False):
692    'Read a MAR-345 image plate image'
693    try:
694        import pack_f as pf
695    except:
696        msg = wx.MessageDialog(None, message="Unable to load the GSAS MAR image decompression, pack_f",
697                               caption="Import Error",
698                               style=wx.ICON_ERROR | wx.OK | wx.STAY_ON_TOP)
699        msg.ShowModal()
700        return None,None,None,None
701
702    if not imageOnly:
703        print 'Read Mar345 file: ',filename
704    File = open(filename,'rb')
705    head = File.read(4095)
706    lines = head[128:].split('\n')
707    head = []
708    for line in lines:
709        line = line.strip()
710        if 'PIXEL' in line:
711            values = line.split()
712            pixel = (int(values[2]),int(values[4]))     #in microns
713        elif 'WAVELENGTH' in line:
714            wave = float(line.split()[1])
715        elif 'DISTANCE' in line:
716            distance = float(line.split()[1])           #in mm
717            if not distance:
718                distance = 500.
719        elif 'CENTER' in line:
720            values = line.split()
721            center = [float(values[2])/10.,float(values[4])/10.]    #make in mm from pixels
722        if line: 
723            head.append(line)
724    data = {'pixelSize':pixel,'wavelength':wave,'distance':distance,'center':center}
725    for line in head:
726        if 'FORMAT' in line[0:6]:
727            items = line.split()
728            sizex = int(items[1])
729            Npix = int(items[3])
730            sizey = int(Npix/sizex)
731    pos = 4096
732    data['size'] = [sizex,sizey]
733    File.seek(pos)
734    line = File.read(8)
735    while 'CCP4' not in line:       #get past overflow list for now
736        line = File.read(8)
737        pos += 8
738    pos += 37
739    File.seek(pos)
740    raw = File.read()
741    File.close()
742    image = np.zeros(shape=(sizex,sizey),dtype=np.int32)
743   
744    image = np.flipud(pf.pack_f(len(raw),raw,sizex,sizey,image).T)  #transpose to get it right way around & flip
745    if imageOnly:
746        return image
747    else:
748        return head,data,Npix,image
749       
750def ProjFileOpen(G2frame,showProvenance=True):
751    'Read a GSAS-II project file and load into the G2 data tree'
752    if not os.path.exists(G2frame.GSASprojectfile):
753        print ('\n*** Error attempt to open project file that does not exist:\n   '+
754               str(G2frame.GSASprojectfile))
755        return
756    LastSavedUsing = None
757    file = open(G2frame.GSASprojectfile,'rb')
758    if showProvenance: print 'loading from file: ',G2frame.GSASprojectfile
759    G2frame.SetTitle("GSAS-II data tree: "+
760                     os.path.split(G2frame.GSASprojectfile)[1])
761    wx.BeginBusyCursor()
762    try:
763        while True:
764            try:
765                data = cPickle.load(file)
766            except EOFError:
767                break
768            datum = data[0]
769           
770            Id = G2frame.PatternTree.AppendItem(parent=G2frame.root,text=datum[0])
771            if datum[0].startswith('PWDR'):               
772                if 'ranId' not in datum[1][0]: # patch: add random Id if not present
773                    datum[1][0]['ranId'] = ran.randint(0,sys.maxint)
774                G2frame.PatternTree.SetItemPyData(Id,datum[1][:3])  #temp. trim off junk (patch?)
775            elif datum[0].startswith('HKLF'): 
776                if 'ranId' not in datum[1][0]: # patch: add random Id if not present
777                    datum[1][0]['ranId'] = ran.randint(0,sys.maxint)
778                G2frame.PatternTree.SetItemPyData(Id,datum[1])
779            else:
780                G2frame.PatternTree.SetItemPyData(Id,datum[1])             
781                if datum[0] == 'Controls' and 'LastSavedUsing' in datum[1]:
782                    LastSavedUsing = datum[1]['LastSavedUsing']
783                if datum[0] == 'Controls' and 'PythonVersions' in datum[1] and GSASIIpath.GetConfigValue('debug') and showProvenance:
784                    print('Packages used to create .GPX file:')
785                    if 'dict' in str(type(datum[1]['PythonVersions'])):  #patch
786                        for p in sorted(datum[1]['PythonVersions'],key=lambda s: s.lower()):
787                            print({:<14s}: {:s}".format(p[0],p[1]))
788                    else:
789                        for p in datum[1]['PythonVersions']:
790                            print({:<12s} {:s}".format(p[0]+':',p[1]))
791            oldPDF = False
792            for datus in data[1:]:
793#patch - 1/23/17 PDF cleanup
794                if datus[0][:4] in ['I(Q)','S(Q)','F(Q)','G(R)']:
795                    oldPDF = True
796                    data[1][1][datus[0][:4]] = copy.deepcopy(datus[1][:2])
797                    continue
798#end PDF cleanup
799                sub = G2frame.PatternTree.AppendItem(Id,datus[0])
800#patch
801                if datus[0] == 'Instrument Parameters' and len(datus[1]) == 1:
802                    if datum[0].startswith('PWDR'):
803                        datus[1] = [dict(zip(datus[1][3],zip(datus[1][0],datus[1][1],datus[1][2]))),{}]
804                    else:
805                        datus[1] = [dict(zip(datus[1][2],zip(datus[1][0],datus[1][1]))),{}]
806                    for item in datus[1][0]:               #zip makes tuples - now make lists!
807                        datus[1][0][item] = list(datus[1][0][item])
808#end patch
809                G2frame.PatternTree.SetItemPyData(sub,datus[1])
810            if 'PDF ' in datum[0][:4] and oldPDF:
811                sub = G2frame.PatternTree.AppendItem(Id,'PDF Peaks')
812                G2frame.PatternTree.SetItemPyData(sub,{'Limits':[1.,5.],'Background':[2,[0.,-0.2*np.pi],False],'Peaks':[]})
813            if datum[0].startswith('IMG'):                   #retrieve image default flag & data if set
814                Data = G2frame.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(G2frame,Id,'Image Controls'))
815                if Data['setDefault']:
816                    G2frame.imageDefault = Data               
817        file.close()
818        if LastSavedUsing:
819            print('GPX load successful. Last saved with GSAS-II revision '+LastSavedUsing)
820        else:
821            print('project load successful')
822        G2frame.NewPlot = True
823    except:
824        msg = wx.MessageDialog(G2frame,message="Error reading file "+
825            str(G2frame.GSASprojectfile)+". This is not a GSAS-II .gpx file",
826            caption="Load Error",style=wx.ICON_ERROR | wx.OK | wx.STAY_ON_TOP)
827        msg.ShowModal()
828    finally:
829        wx.EndBusyCursor()
830        G2frame.Status.SetStatusText('Mouse RB click item to refresh/raise; RB drag/drop to reorder')
831   
832def ProjFileSave(G2frame):
833    'Save a GSAS-II project file'
834    if not G2frame.PatternTree.IsEmpty():
835        file = open(G2frame.GSASprojectfile,'wb')
836        print 'save to file: ',G2frame.GSASprojectfile
837        # stick the file name into the tree and version info into tree so they are saved.
838        # (Controls should always be created at this point)
839        try:
840            Controls = G2frame.PatternTree.GetItemPyData(
841                G2gd.GetPatternTreeItemId(G2frame,G2frame.root, 'Controls'))
842            Controls['LastSavedAs'] = os.path.abspath(G2frame.GSASprojectfile)
843            Controls['LastSavedUsing'] = str(GSASIIpath.GetVersionNumber())
844            Controls['PythonVersions'] = G2frame.PackageVersions
845        except:
846            pass
847        wx.BeginBusyCursor()
848        try:
849            item, cookie = G2frame.PatternTree.GetFirstChild(G2frame.root)
850            while item:
851                data = []
852                name = G2frame.PatternTree.GetItemText(item)
853                data.append([name,G2frame.PatternTree.GetItemPyData(item)])
854                item2, cookie2 = G2frame.PatternTree.GetFirstChild(item)
855                while item2:
856                    name = G2frame.PatternTree.GetItemText(item2)
857                    data.append([name,G2frame.PatternTree.GetItemPyData(item2)])
858                    item2, cookie2 = G2frame.PatternTree.GetNextChild(item, cookie2)                           
859                item, cookie = G2frame.PatternTree.GetNextChild(G2frame.root, cookie)                           
860                cPickle.dump(data,file,1)
861            file.close()
862            pth = os.path.split(os.path.abspath(G2frame.GSASprojectfile))[0]
863            if GSASIIpath.GetConfigValue('Save_paths'): G2G.SaveGPXdirectory(pth)
864            G2frame.LastGPXdir = pth
865        finally:
866            wx.EndBusyCursor()
867        print('project save successful')
868
869def SaveIntegration(G2frame,PickId,data,Overwrite=False):
870    'Save image integration results as powder pattern(s)'
871    azms = G2frame.Integrate[1]
872    X = G2frame.Integrate[2][:-1]
873    N = len(X)
874    Id = G2frame.PatternTree.GetItemParent(PickId)
875    name = G2frame.PatternTree.GetItemText(Id)
876    name = name.replace('IMG ',data['type']+' ')
877    Comments = G2frame.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(G2frame,Id, 'Comments'))
878    if 'PWDR' in name:
879        names = ['Type','Lam','Zero','Polariz.','U','V','W','X','Y','SH/L','Azimuth'] 
880        codes = [0 for i in range(11)]
881    elif 'SASD' in name:
882        names = ['Type','Lam','Zero','Azimuth'] 
883        codes = [0 for i in range(4)]
884        X = 4.*np.pi*npsind(X/2.)/data['wavelength']    #convert to q
885    Xminmax = [X[0],X[-1]]
886    Azms = []
887    dazm = 0.
888    if data['fullIntegrate'] and data['outAzimuths'] == 1:
889        Azms = [45.0,]                              #a poor man's average?
890    else:
891        for i,azm in enumerate(azms[:-1]):
892            if azm > 360. and azms[i+1] > 360.:
893                Azms.append(G2img.meanAzm(azm%360.,azms[i+1]%360.))
894            else:   
895                Azms.append(G2img.meanAzm(azm,azms[i+1]))
896        dazm = np.min(np.abs(np.diff(azms)))/2.
897    G2frame.IntgOutList = []
898    for i,azm in enumerate(azms[:-1]):
899        Aname = name+" Azm= %.2f"%((azm+dazm)%360.)
900        item, cookie = G2frame.PatternTree.GetFirstChild(G2frame.root)
901        # if Overwrite delete any duplicate
902        if Overwrite and G2gd.GetPatternTreeItemId(G2frame,G2frame.root,Aname):
903            print('Replacing '+Aname)
904            item = G2gd.GetPatternTreeItemId(G2frame,G2frame.root,Aname)
905            G2frame.PatternTree.Delete(item)
906        else:
907            nOcc = 0
908            while item:
909                Name = G2frame.PatternTree.GetItemText(item)
910                if Aname in Name:
911                    nOcc += 1
912                item, cookie = G2frame.PatternTree.GetNextChild(G2frame.root, cookie)
913            if nOcc:
914                Aname += '(%d)'%(nOcc)
915        Sample = G2pdG.SetDefaultSample()       #set as Debye-Scherrer
916        Sample['Gonio. radius'] = data['distance']
917        Sample['Omega'] = data['GonioAngles'][0]
918        Sample['Chi'] = data['GonioAngles'][1]
919        Sample['Phi'] = data['GonioAngles'][2]
920        Sample['Azimuth'] = (azm+dazm)%360.    #put here as bin center
921        polariz = 0.99    #set default polarization for synchrotron radiation!
922        for item in Comments:
923            if 'polariz' in item:
924                try:
925                    polariz = float(item.split('=')[1])
926                except:
927                    polariz = 0.99
928        if 'PWDR' in Aname:
929            parms = ['PXC',data['wavelength'],0.0,polariz,1.0,-0.10,0.4,0.30,1.0,0.0001,Azms[i]]
930        elif 'SASD' in Aname:
931            Sample['Trans'] = data['SampleAbs'][0]
932            parms = ['LXC',data['wavelength'],0.0,Azms[i]]
933        Y = G2frame.Integrate[0][i]
934        Ymin = np.min(Y)
935        Ymax = np.max(Y)
936        W = np.where(Y>0.,1./Y,1.e-6)                    #probably not true
937        Id = G2frame.PatternTree.AppendItem(parent=G2frame.root,text=Aname)
938        G2frame.IntgOutList.append(Id)
939        G2frame.PatternTree.SetItemPyData(G2frame.PatternTree.AppendItem(Id,text='Comments'),Comments)                   
940        G2frame.PatternTree.SetItemPyData(G2frame.PatternTree.AppendItem(Id,text='Limits'),[tuple(Xminmax),Xminmax])
941        if 'PWDR' in Aname:
942            G2frame.PatternTree.SetItemPyData(G2frame.PatternTree.AppendItem(Id,text='Background'),[['chebyschev',1,3,1.0,0.0,0.0],
943                {'nDebye':0,'debyeTerms':[],'nPeaks':0,'peaksList':[]}])
944        inst = [dict(zip(names,zip(parms,parms,codes))),{}]
945        for item in inst[0]:
946            inst[0][item] = list(inst[0][item])
947        G2frame.PatternTree.SetItemPyData(G2frame.PatternTree.AppendItem(Id,text='Instrument Parameters'),inst)
948        if 'PWDR' in Aname:
949            G2frame.PatternTree.SetItemPyData(G2frame.PatternTree.AppendItem(Id,text='Sample Parameters'),Sample)
950            G2frame.PatternTree.SetItemPyData(G2frame.PatternTree.AppendItem(Id,text='Peak List'),{'sigDict':{},'peaks':[]})
951            G2frame.PatternTree.SetItemPyData(G2frame.PatternTree.AppendItem(Id,text='Index Peak List'),[[],[]])
952            G2frame.PatternTree.SetItemPyData(G2frame.PatternTree.AppendItem(Id,text='Unit Cells List'),[])
953            G2frame.PatternTree.SetItemPyData(G2frame.PatternTree.AppendItem(Id,text='Reflection Lists'),{})
954        elif 'SASD' in Aname:             
955            G2frame.PatternTree.SetItemPyData(G2frame.PatternTree.AppendItem(Id,text='Substances'),G2pdG.SetDefaultSubstances())
956            G2frame.PatternTree.SetItemPyData(G2frame.PatternTree.AppendItem(Id,text='Sample Parameters'),Sample)
957            G2frame.PatternTree.SetItemPyData(G2frame.PatternTree.AppendItem(Id,text='Models'),G2pdG.SetDefaultSASDModel())
958        valuesdict = {
959            'wtFactor':1.0,
960            'Dummy':False,
961            'ranId':ran.randint(0,sys.maxint),
962            'Offset':[0.0,0.0],'delOffset':0.02*Ymax,'refOffset':-0.1*Ymax,'refDelt':0.1*Ymax,
963            'qPlot':False,'dPlot':False,'sqrtPlot':False,'Yminmax':[Ymin,Ymax]
964            }
965        G2frame.PatternTree.SetItemPyData(
966            Id,[valuesdict,
967                [np.array(X),np.array(Y),np.array(W),np.zeros(N),np.zeros(N),np.zeros(N)]])
968    return Id       #last powder pattern generated
969   
970def XYsave(G2frame,XY,labelX='X',labelY='Y',names=None):
971    'Save XY table data'
972    pth = G2G.GetExportPath(G2frame)
973    dlg = wx.FileDialog(
974        G2frame, 'Enter csv filename for XY table', pth, '',
975        'XY table file (*.csv)|*.csv',wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT)
976    try:
977        if dlg.ShowModal() == wx.ID_OK:
978            filename = dlg.GetPath()
979            filename = os.path.splitext(filename)[0]+'.csv'
980            File = open(filename,'w')
981        else:
982            filename = None
983    finally:
984        dlg.Destroy()
985    if not filename:
986        return
987    for i in range(len(XY)):
988        if names != None:
989            header = '%s,%s(%s)\n'%(labelX,labelY,names[i])
990        else:
991            header = '%s,%s(%d)\n'%(labelX,labelY,i)
992        File.write(header)
993        for x,y in XY[i].T:
994            File.write('%.3f,%.3f\n'%(x,y))   
995    File.close()
996    print ' XY data saved to: ',filename
997           
998def PDFSave(G2frame,exports,PDFsaves):
999    'Save a PDF I(Q), S(Q), F(Q) and G(r)  in column formats'
1000    import scipy.interpolate as scintp
1001    for export in exports:
1002        PickId = G2gd.GetPatternTreeItemId(G2frame, G2frame.root, export)
1003        PDFControls = G2frame.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(G2frame, PickId,'PDF Controls'))
1004        if PDFsaves[0]:     #I(Q)
1005            iqfilename = ospath.join(G2frame.dirname,export.replace(' ','_')[5:]+'.iq')
1006            iqdata = PDFControls['I(Q)'][0]
1007            iqfxn = scintp.interp1d(iqdata[0],iqdata[1],kind='linear')
1008            iqfile = open(iqfilename,'w')
1009            iqfile.write('#T I(Q) %s\n'%(export))
1010            iqfile.write('#L Q     I(Q)\n')
1011            qnew = np.arange(iqdata[0][0],iqdata[0][-1],0.005)
1012            iqnew = zip(qnew,iqfxn(qnew))
1013            for q,iq in iqnew:
1014                iqfile.write("%15.6g %15.6g\n" % (q,iq))
1015            iqfile.close()
1016            print ' I(Q) saved to: ',iqfilename
1017           
1018        if PDFsaves[1]:     #S(Q)
1019            sqfilename = ospath.join(G2frame.dirname,export.replace(' ','_')[5:]+'.sq')
1020            sqdata = PDFControls['S(Q)'][1]
1021            sqfxn = scintp.interp1d(sqdata[0],sqdata[1],kind='linear')
1022            sqfile = open(sqfilename,'w')
1023            sqfile.write('#T S(Q) %s\n'%(export))
1024            sqfile.write('#L Q     S(Q)\n')
1025            qnew = np.arange(sqdata[0][0],sqdata[0][-1],0.005)
1026            sqnew = zip(qnew,sqfxn(qnew))
1027            for q,sq in sqnew:
1028                sqfile.write("%15.6g %15.6g\n" % (q,sq))
1029            sqfile.close()
1030            print ' S(Q) saved to: ',sqfilename
1031           
1032        if PDFsaves[2]:     #F(Q)
1033            fqfilename = ospath.join(G2frame.dirname,export.replace(' ','_')[5:]+'.fq')
1034            fqdata = PDFControls['F(Q)'][1]
1035            fqfxn = scintp.interp1d(fqdata[0],fqdata[1],kind='linear')
1036            fqfile = open(sqfilename,'w')
1037            fqfile.write('#T F(Q) %s\n'%(export))
1038            fqfile.write('#L Q     F(Q)\n')
1039            qnew = np.arange(sqdata[0][0],sqdata[0][-1],0.005)
1040            fqnew = zip(qnew,fqfxn(qnew))
1041            for q,fq in fqnew:
1042                fqfile.write("%15.6g %15.6g\n" % (q,fq))
1043            fqfile.close()
1044            print ' F(Q) saved to: ',fqfilename
1045           
1046        if PDFsaves[3]:     #G(R)
1047            grfilename = ospath.join(G2frame.dirname,export.replace(' ','_')[5:]+'.gr')
1048            grdata = PDFControls['G(R)'][1]
1049            grfxn = scintp.interp1d(grdata[0],grdata[1],kind='linear')
1050            grfile = open(grfilename,'w')
1051            grfile.write('#T G(R) %s\n'%(export))
1052            grfile.write('#L R     G(R)\n')
1053            rnew = np.arange(grdata[0][0],grdata[0][-1],0.010)
1054            grnew = zip(rnew,grfxn(rnew))
1055            for r,gr in grnew:
1056                grfile.write("%15.6g %15.6g\n" % (r,gr))
1057            grfile.close()
1058            print ' G(R) saved to: ',grfilename
1059       
1060        if PDFsaves[4]: #pdfGUI file for G(R)
1061            pId = G2gd.GetPatternTreeItemId(G2frame, G2frame.root, 'PWDR'+export[4:])
1062            Inst = G2frame.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(G2frame, pId,'Instrument Parameters'))[0]
1063            Limits = G2frame.PatternTree.GetItemPyData(G2gd.GetPatternTreeItemId(G2frame, pId,'Limits'))
1064            grfilename = ospath.join(G2frame.dirname,export.replace(' ','_')[5:]+'.gr')
1065            grdata = PDFControls['G(R)'][1]
1066            qdata = PDFControls['I(Q)'][1][0]
1067            grfxn = scintp.interp1d(grdata[0],grdata[1],kind='linear')
1068            grfile = open(grfilename,'w')
1069            rnew = np.arange(grdata[0][0],grdata[0][-1],0.010)
1070            grnew = zip(rnew,grfxn(rnew))
1071
1072            grfile.write('[DEFAULT]\n')
1073            grfile.write('\n')
1074            grfile.write('version = GSAS-II-v'+str(GSASIIpath.GetVersionNumber())+'\n')
1075            grfile.write('\n')
1076            grfile.write('# input and output specifications\n')
1077            grfile.write('dataformat = Qnm\n')
1078            grfile.write('inputfile = %s\n'%(PDFControls['Sample']['Name']))
1079            grfile.write('backgroundfile = %s\n'%(PDFControls['Sample Bkg.']['Name']))
1080            grfile.write('outputtype = gr\n')
1081            grfile.write('\n')
1082            grfile.write('# PDF calculation setup\n')
1083            if 'x' in Inst['Type']:
1084                grfile.write('mode = %s\n'%('xray'))
1085            elif 'N' in Inst['Type']:
1086                grfile.write('mode = %s\n'%('neutron'))
1087            wave = G2mth.getMeanWave(Inst)
1088            grfile.write('wavelength = %.5f\n'%(wave))
1089            formula = ''
1090            for el in PDFControls['ElList']:
1091                formula += el
1092                num = PDFControls['ElList'][el]['FormulaNo']
1093                if num == round(num):
1094                    formula += '%d'%(int(num))
1095                else:
1096                    formula += '%.2f'%(num)
1097            grfile.write('composition = %s\n'%(formula))
1098            grfile.write('bgscale = %.3f\n'%(-PDFControls['Sample Bkg.']['Mult']))
1099            highQ = 2.*np.pi/G2lat.Pos2dsp(Inst,Limits[1][1])
1100            grfile.write('qmaxinst = %.2f\n'%(highQ))
1101            grfile.write('qmin = %.5f\n'%(qdata[0]))
1102            grfile.write('qmax = %.4f\n'%(qdata[-1]))
1103            grfile.write('rmin = %.2f\n'%(PDFControls['Rmin']))
1104            grfile.write('rmax = %.2f\n'%(PDFControls['Rmax']))
1105            grfile.write('rstep = 0.01\n')
1106           
1107           
1108            grfile.write('\n')
1109            grfile.write('# End of config '+63*'-')
1110            grfile.write('\n')
1111            grfile.write('#### start data\n')
1112            grfile.write('#S 1\n')
1113            grfile.write('#L r($\AA$)  G($\AA^{-2}$)\n')           
1114            for r,gr in grnew:
1115                grfile.write("%15.2F %15.6F\n" % (r,gr))
1116            grfile.close()
1117            print ' G(R) saved to: ',grfilename
1118           
1119           
1120           
1121   
1122def PeakListSave(G2frame,file,peaks):
1123    'Save powder peaks to a data file'
1124    print 'save peak list to file: ',G2frame.peaklistfile
1125    if not peaks:
1126        dlg = wx.MessageDialog(G2frame, 'No peaks!', 'Nothing to save!', wx.OK)
1127        try:
1128            dlg.ShowModal()
1129        finally:
1130            dlg.Destroy()
1131        return
1132    for peak in peaks:
1133        file.write("%10.4f %12.2f %10.3f %10.3f \n" % \
1134            (peak[0],peak[2],peak[4],peak[6]))
1135    print 'peak list saved'
1136             
1137def IndexPeakListSave(G2frame,peaks):
1138    'Save powder peaks from the indexing list'
1139    file = open(G2frame.peaklistfile,'wa')
1140    print 'save index peak list to file: ',G2frame.peaklistfile
1141    wx.BeginBusyCursor()
1142    try:
1143        if not peaks:
1144            dlg = wx.MessageDialog(G2frame, 'No peaks!', 'Nothing to save!', wx.OK)
1145            try:
1146                dlg.ShowModal()
1147            finally:
1148                dlg.Destroy()
1149            return
1150        for peak in peaks:
1151            file.write("%12.6f\n" % (peak[7]))
1152        file.close()
1153    finally:
1154        wx.EndBusyCursor()
1155    print 'index peak list saved'
1156   
1157def SetNewPhase(Name='New Phase',SGData=None,cell=None,Super=None):
1158    '''Create a new phase dict with default values for various parameters
1159
1160    :param str Name: Name for new Phase
1161
1162    :param dict SGData: space group data from :func:`GSASIIspc:SpcGroup`;
1163      defaults to data for P 1
1164
1165    :param list cell: unit cell parameter list; defaults to
1166      [1.0,1.0,1.0,90.,90,90.,1.]
1167
1168    '''
1169    if SGData is None: SGData = G2spc.SpcGroup('P 1')[1]
1170    if cell is None: cell=[1.0,1.0,1.0,90.,90,90.,1.]
1171    phaseData = {
1172        'ranId':ran.randint(0,sys.maxint),
1173        'General':{
1174            'Name':Name,
1175            'Type':'nuclear',
1176            'Modulated':False,
1177            'AtomPtrs':[3,1,7,9],
1178            'SGData':SGData,
1179            'Cell':[False,]+cell,
1180            'Pawley dmin':1.0,
1181            'Data plot type':'None',
1182            'SH Texture':{
1183                'Order':0,
1184                'Model':'cylindrical',
1185                'Sample omega':[False,0.0],
1186                'Sample chi':[False,0.0],
1187                'Sample phi':[False,0.0],
1188                'SH Coeff':[False,{}],
1189                'SHShow':False,
1190                'PFhkl':[0,0,1],
1191                'PFxyz':[0,0,1],
1192                'PlotType':'Pole figure',
1193                'Penalty':[['',],0.1,False,1.0]}},
1194        'Atoms':[],
1195        'Drawing':{},
1196        'Histograms':{},
1197        'Pawley ref':[],
1198        'RBModels':{},
1199        }
1200    if Super and Super.get('Use',False):
1201        phaseData['General'].update({'Modulated':True,'Super':True,'SuperSg':Super['ssSymb']})
1202        phaseData['General']['SSGData'] = G2spc.SSpcGroup(SGData,Super['ssSymb'])
1203        phaseData['General']['SuperVec'] = [Super['ModVec'],False,Super['maxH']]
1204
1205    return phaseData
1206       
1207class MultipleChoicesDialog(wx.Dialog):
1208    '''A dialog that offers a series of choices, each with a
1209    title and a wx.Choice widget. Intended to be used Modally.
1210    typical input:
1211
1212        *  choicelist=[ ('a','b','c'), ('test1','test2'),('no choice',)]
1213        *  headinglist = [ 'select a, b or c', 'select 1 of 2', 'No option here']
1214       
1215    selections are placed in self.chosen when OK is pressed
1216
1217    Also see GSASIIctrls
1218    '''
1219    def __init__(self,choicelist,headinglist,
1220                 head='Select options',
1221                 title='Please select from options below',
1222                 parent=None):
1223        self.chosen = []
1224        wx.Dialog.__init__(
1225            self,parent,wx.ID_ANY,head, 
1226            pos=wx.DefaultPosition,style=wx.DEFAULT_DIALOG_STYLE)
1227        panel = wx.Panel(self)
1228        mainSizer = wx.BoxSizer(wx.VERTICAL)
1229        mainSizer.Add((10,10),1)
1230        topLabl = wx.StaticText(panel,wx.ID_ANY,title)
1231        mainSizer.Add(topLabl,0,wx.ALIGN_CENTER_VERTICAL|wx.CENTER,10)
1232        self.ChItems = []
1233        for choice,lbl in zip(choicelist,headinglist):
1234            mainSizer.Add((10,10),1)
1235            self.chosen.append(0)
1236            topLabl = wx.StaticText(panel,wx.ID_ANY,' '+lbl)
1237            mainSizer.Add(topLabl,0,wx.ALIGN_LEFT,10)
1238            self.ChItems.append(wx.Choice(self, wx.ID_ANY, (100, 50), choices = choice))
1239            mainSizer.Add(self.ChItems[-1],0,wx.ALIGN_CENTER,10)
1240
1241        OkBtn = wx.Button(panel,-1,"Ok")
1242        OkBtn.Bind(wx.EVT_BUTTON, self.OnOk)
1243        cancelBtn = wx.Button(panel,-1,"Cancel")
1244        cancelBtn.Bind(wx.EVT_BUTTON, self.OnCancel)
1245        btnSizer = wx.BoxSizer(wx.HORIZONTAL)
1246        btnSizer.Add((20,20),1)
1247        btnSizer.Add(OkBtn)
1248        btnSizer.Add((20,20),1)
1249        btnSizer.Add(cancelBtn)
1250        btnSizer.Add((20,20),1)
1251        mainSizer.Add(btnSizer,0,wx.EXPAND|wx.BOTTOM|wx.TOP, 10)
1252        panel.SetSizer(mainSizer)
1253        panel.Fit()
1254        self.Fit()
1255       
1256    def OnOk(self,event):
1257        parent = self.GetParent()
1258        if parent is not None: parent.Raise()
1259        # save the results from the choice widgets
1260        self.chosen = []
1261        for w in self.ChItems:
1262            self.chosen.append(w.GetSelection())
1263        self.EndModal(wx.ID_OK)             
1264           
1265    def OnCancel(self,event):
1266        parent = self.GetParent()
1267        if parent is not None: parent.Raise()
1268        self.chosen = []
1269        self.EndModal(wx.ID_CANCEL)             
1270           
1271def ExtractFileFromZip(filename, selection=None, confirmread=True,
1272                       confirmoverwrite=True, parent=None,
1273                       multipleselect=False):
1274    '''If the filename is a zip file, extract a file from that
1275    archive.
1276
1277    :param list Selection: used to predefine the name of the file
1278      to be extracted. Filename case and zip directory name are
1279      ignored in selection; the first matching file is used.
1280
1281    :param bool confirmread: if True asks the user to confirm before expanding
1282      the only file in a zip
1283
1284    :param bool confirmoverwrite: if True asks the user to confirm
1285      before overwriting if the extracted file already exists
1286
1287    :param bool multipleselect: if True allows more than one zip
1288      file to be extracted, a list of file(s) is returned.
1289      If only one file is present, do not ask which one, otherwise
1290      offer a list of choices (unless selection is used).
1291   
1292    :returns: the name of the file that has been created or a
1293      list of files (see multipleselect)
1294
1295    If the file is not a zipfile, return the name of the input file.
1296    If the zipfile is empty or no file has been selected, return None
1297    '''
1298    import zipfile # do this now, since we can save startup time by doing this only on need
1299    import shutil
1300    zloc = os.path.split(filename)[0]
1301    if not zipfile.is_zipfile(filename):
1302        #print("not zip")
1303        return filename
1304
1305    z = zipfile.ZipFile(filename,'r')
1306    zinfo = z.infolist()
1307
1308    if len(zinfo) == 0:
1309        #print('Zip has no files!')
1310        zlist = [-1]
1311    if selection:
1312        choices = [os.path.split(i.filename)[1].lower() for i in zinfo]
1313        if selection.lower() in choices:
1314            zlist = [choices.index(selection.lower())]
1315        else:
1316            print('debug: file '+str(selection)+' was not found in '+str(filename))
1317            zlist = [-1]
1318    elif len(zinfo) == 1 and confirmread:
1319        result = wx.ID_NO
1320        dlg = wx.MessageDialog(
1321            parent,
1322            'Is file '+str(zinfo[0].filename)+
1323            ' what you want to extract from '+
1324            str(os.path.split(filename)[1])+'?',
1325            'Confirm file', 
1326            wx.YES_NO | wx.ICON_QUESTION)
1327        try:
1328            result = dlg.ShowModal()
1329        finally:
1330            dlg.Destroy()
1331        if result == wx.ID_NO:
1332            zlist = [-1]
1333        else:
1334            zlist = [0]
1335    elif len(zinfo) == 1:
1336        zlist = [0]
1337    elif multipleselect:
1338        # select one or more from a from list
1339        choices = [i.filename for i in zinfo]
1340        dlg = G2G.G2MultiChoiceDialog(parent,'Select file(s) to extract from zip file '+str(filename),
1341            'Choose file(s)',choices)
1342        if dlg.ShowModal() == wx.ID_OK:
1343            zlist = dlg.GetSelections()
1344        else:
1345            zlist = []
1346        dlg.Destroy()
1347    else:
1348        # select one from a from list
1349        choices = [i.filename for i in zinfo]
1350        dlg = wx.SingleChoiceDialog(parent,
1351            'Select file to extract from zip file'+str(filename),'Choose file',
1352            choices,)
1353        if dlg.ShowModal() == wx.ID_OK:
1354            zlist = [dlg.GetSelection()]
1355        else:
1356            zlist = [-1]
1357        dlg.Destroy()
1358       
1359    outlist = []
1360    for zindex in zlist:
1361        if zindex >= 0:
1362            efil = os.path.join(zloc, os.path.split(zinfo[zindex].filename)[1])
1363            if os.path.exists(efil) and confirmoverwrite:
1364                result = wx.ID_NO
1365                dlg = wx.MessageDialog(parent,
1366                    'File '+str(efil)+' already exists. OK to overwrite it?',
1367                    'Confirm overwrite',wx.YES_NO | wx.ICON_QUESTION)
1368                try:
1369                    result = dlg.ShowModal()
1370                finally:
1371                    dlg.Destroy()
1372                if result == wx.ID_NO:
1373                    zindex = -1
1374        if zindex >= 0:
1375            # extract the file to the current directory, regardless of it's original path
1376            #z.extract(zinfo[zindex],zloc)
1377            eloc,efil = os.path.split(zinfo[zindex].filename)
1378            outfile = os.path.join(zloc, efil)
1379            fpin = z.open(zinfo[zindex])
1380            fpout = file(outfile, "wb")
1381            shutil.copyfileobj(fpin, fpout)
1382            fpin.close()
1383            fpout.close()
1384            outlist.append(outfile)
1385    z.close()
1386    if multipleselect and len(outlist) >= 1:
1387        return outlist
1388    elif len(outlist) == 1:
1389        return outlist[0]
1390    else:
1391        return None
1392
1393######################################################################
1394# base classes for reading various types of data files
1395#   not used directly, only by subclassing
1396######################################################################
1397try:
1398    E,SGData = G2spc.SpcGroup('P 1') # data structure for default space group
1399except: # errors on doc build
1400    SGData = None
1401P1SGData = SGData
1402######################################################################
1403class ImportBaseclass(object):
1404    '''Defines a base class for the reading of input files (diffraction
1405    data, coordinates,...). See :ref:`Writing a Import Routine<Import_routines>`
1406    for an explanation on how to use a subclass of this class.
1407    '''
1408    class ImportException(Exception):
1409        '''Defines an Exception that is used when an import routine hits an expected error,
1410        usually in .Reader.
1411
1412        Good practice is that the Reader should define a value in self.errors that
1413        tells the user some information about what is wrong with their file.         
1414        '''
1415        pass
1416   
1417    UseReader = True  # in __init__ set value of self.UseReader to False to skip use of current importer
1418    def __init__(self,formatName,longFormatName=None,
1419                 extensionlist=[],strictExtension=False,):
1420        self.formatName = formatName # short string naming file type
1421        if longFormatName: # longer string naming file type
1422            self.longFormatName = longFormatName
1423        else:
1424            self.longFormatName = formatName
1425        # define extensions that are allowed for the file type
1426        # for windows, remove any extensions that are duplicate, as case is ignored
1427        if sys.platform == 'windows' and extensionlist:
1428            extensionlist = list(set([s.lower() for s in extensionlist]))
1429        self.extensionlist = extensionlist
1430        # If strictExtension is True, the file will not be read, unless
1431        # the extension matches one in the extensionlist
1432        self.strictExtension = strictExtension
1433        self.errors = ''
1434        self.warnings = ''
1435        # used for readers that will use multiple passes to read
1436        # more than one data block
1437        self.repeat = False
1438        self.selections = []
1439        self.repeatcount = 0
1440        self.readfilename = '?'
1441        #print 'created',self.__class__
1442
1443    def ReInitialize(self):
1444        'Reinitialize the Reader to initial settings'
1445        self.errors = ''
1446        self.warnings = ''
1447        self.repeat = False
1448        self.repeatcount = 0
1449        self.readfilename = '?'
1450
1451    def BlockSelector(self, ChoiceList, ParentFrame=None,title='Select a block',
1452        size=None, header='Block Selector',useCancel=True):
1453        ''' Provide a wx dialog to select a block if the file contains more
1454        than one set of data and one must be selected
1455        '''
1456        if useCancel:
1457            dlg = wx.SingleChoiceDialog(
1458                ParentFrame,title, header,ChoiceList)
1459        else:
1460            dlg = wx.SingleChoiceDialog(
1461                ParentFrame,title, header,ChoiceList,
1462                style=wx.DEFAULT_DIALOG_STYLE|wx.RESIZE_BORDER|wx.OK|wx.CENTRE)
1463        if size: dlg.SetSize(size)
1464        dlg.CenterOnParent()
1465        if dlg.ShowModal() == wx.ID_OK:
1466            sel = dlg.GetSelection()
1467            return sel
1468        else:
1469            return None
1470        dlg.Destroy()
1471
1472    def MultipleBlockSelector(self, ChoiceList, ParentFrame=None,
1473        title='Select a block',size=None, header='Block Selector'):
1474        '''Provide a wx dialog to select a block of data if the
1475        file contains more than one set of data and one must be
1476        selected.
1477
1478        :returns: a list of the selected blocks
1479        '''
1480        dlg = wx.MultiChoiceDialog(ParentFrame,title, header,ChoiceList+['Select all'],
1481            wx.CHOICEDLG_STYLE)
1482        dlg.CenterOnScreen()
1483        if size: dlg.SetSize(size)
1484        if dlg.ShowModal() == wx.ID_OK:
1485            sel = dlg.GetSelections()
1486        else:
1487            return []
1488        dlg.Destroy()
1489        selected = []
1490        if len(ChoiceList) in sel:
1491            return range(len(ChoiceList))
1492        else:
1493            return sel
1494        return selected
1495
1496    def MultipleChoicesDialog(self, choicelist, headinglist, ParentFrame=None, **kwargs):
1497        '''A modal dialog that offers a series of choices, each with a title and a wx.Choice
1498        widget. Typical input:
1499       
1500           * choicelist=[ ('a','b','c'), ('test1','test2'),('no choice',)]
1501           
1502           * headinglist = [ 'select a, b or c', 'select 1 of 2', 'No option here']
1503           
1504        optional keyword parameters are: head (window title) and title
1505        returns a list of selected indicies for each choice (or None)
1506        '''
1507        result = None
1508        dlg = MultipleChoicesDialog(choicelist,headinglist,
1509            parent=ParentFrame, **kwargs)         
1510        dlg.CenterOnParent()
1511        if dlg.ShowModal() == wx.ID_OK:
1512            result = dlg.chosen
1513        dlg.Destroy()
1514        return result
1515
1516    def ShowBusy(self):
1517        wx.BeginBusyCursor()
1518#        wx.Yield() # make it happen now!
1519
1520    def DoneBusy(self):
1521        wx.EndBusyCursor()
1522        wx.Yield() # make it happen now!
1523       
1524#    def Reader(self, filename, filepointer, ParentFrame=None, **unused):
1525#        '''This method must be supplied in the child class to read the file.
1526#        if the read fails either return False or raise an Exception
1527#        preferably of type ImportException.
1528#        '''
1529#        #start reading
1530#        raise ImportException("Error occurred while...")
1531#        self.errors += "Hint for user on why the error occur
1532#        return False # if an error occurs
1533#        return True # if read OK
1534
1535    def ExtensionValidator(self, filename):
1536        '''This methods checks if the file has the correct extension
1537        Return False if this filename will not be supported by this reader
1538        Return True if the extension matches the list supplied by the reader
1539        Return None if the reader allows un-registered extensions
1540        '''
1541        if filename:
1542            ext = os.path.splitext(filename)[1]
1543            if sys.platform == 'windows': ext = ext.lower()
1544            if ext in self.extensionlist: return True
1545            if self.strictExtension: return False
1546        return None
1547
1548    def ContentsValidator(self, filepointer):
1549        '''This routine will attempt to determine if the file can be read
1550        with the current format.
1551        This will typically be overridden with a method that
1552        takes a quick scan of [some of]
1553        the file contents to do a "sanity" check if the file
1554        appears to match the selected format.
1555        Expected to be called via self.Validator()
1556        '''
1557        #filepointer.seek(0) # rewind the file pointer
1558        return True
1559
1560    def CIFValidator(self, filepointer):
1561        '''A :meth:`ContentsValidator` for use to validate CIF files.
1562        '''
1563        for i,l in enumerate(filepointer):
1564            if i >= 1000: return True
1565            '''Encountered only blank lines or comments in first 1000
1566            lines. This is unlikely, but assume it is CIF anyway, since we are
1567            even less likely to find a file with nothing but hashes and
1568            blank lines'''
1569            line = l.strip()
1570            if len(line) == 0: # ignore blank lines
1571                continue 
1572            elif line.startswith('#'): # ignore comments
1573                continue 
1574            elif line.startswith('data_'): # on the right track, accept this file
1575                return True
1576            else: # found something invalid
1577                self.errors = 'line '+str(i+1)+' contains unexpected data:\n'
1578                if all([ord(c) < 128 and ord(c) != 0 for c in str(l)]): # show only if ASCII
1579                    self.errors += '  '+str(l)
1580                else: 
1581                    self.errors += '  (binary)'
1582                self.errors += '\n  Note: a CIF should only have blank lines or comments before'
1583                self.errors += '\n        a data_ statement begins a block.'
1584                return False 
1585
1586######################################################################
1587class ImportPhase(ImportBaseclass):
1588    '''Defines a base class for the reading of files with coordinates
1589
1590    Objects constructed that subclass this (in import/G2phase_*.py etc.) will be used
1591    in :meth:`GSASII.GSASII.OnImportPhase`.
1592    See :ref:`Writing a Import Routine<Import_Routines>`
1593    for an explanation on how to use this class.
1594
1595    '''
1596    def __init__(self,formatName,longFormatName=None,extensionlist=[],
1597        strictExtension=False,):
1598        # call parent __init__
1599        ImportBaseclass.__init__(self,formatName,longFormatName,
1600            extensionlist,strictExtension)
1601        self.Phase = None # a phase must be created with G2IO.SetNewPhase in the Reader
1602        self.Constraints = None
1603
1604    def PhaseSelector(self, ChoiceList, ParentFrame=None,
1605        title='Select a phase', size=None,header='Phase Selector'):
1606        ''' Provide a wx dialog to select a phase if the file contains more
1607        than one phase
1608        '''
1609        return self.BlockSelector(ChoiceList,ParentFrame,title,
1610            size,header)
1611
1612######################################################################
1613class ImportStructFactor(ImportBaseclass):
1614    '''Defines a base class for the reading of files with tables
1615    of structure factors.
1616
1617    Structure factors are read with a call to :meth:`GSASII.GSASII.OnImportSfact`
1618    which in turn calls :meth:`GSASII.GSASII.OnImportGeneric`, which calls
1619    methods :meth:`ExtensionValidator`, :meth:`ContentsValidator` and
1620    :meth:`Reader`.
1621
1622    See :ref:`Writing a Import Routine<Import_Routines>`
1623    for an explanation on how to use import classes in general. The specifics
1624    for reading a structure factor histogram require that
1625    the ``Reader()`` routine in the import
1626    class need to do only a few things: It
1627    should load :attr:`RefDict` item ``'RefList'`` with the reflection list,
1628    and set :attr:`Parameters` with the instrument parameters
1629    (initialized with :meth:`InitParameters` and set with :meth:`UpdateParameters`).
1630    '''
1631    def __init__(self,formatName,longFormatName=None,extensionlist=[],
1632        strictExtension=False,):
1633        ImportBaseclass.__init__(self,formatName,longFormatName,
1634            extensionlist,strictExtension)
1635
1636        # define contents of Structure Factor entry
1637        self.Parameters = []
1638        'self.Parameters is a list with two dicts for data parameter settings'
1639        self.InitParameters()
1640        self.RefDict = {'RefList':[],'FF':{},'Super':0}
1641        self.Banks = []             #for multi bank data (usually TOF)
1642        '''self.RefDict is a dict containing the reflection information, as read from the file.
1643        Item 'RefList' contains the reflection information. See the
1644        :ref:`Single Crystal Reflection Data Structure<XtalRefl_table>`
1645        for the contents of each row. Dict element 'FF'
1646        contains the form factor values for each element type; if this entry
1647        is left as initialized (an empty list) it will be initialized as needed later.
1648        '''
1649    def ReInitialize(self):
1650        'Reinitialize the Reader to initial settings'
1651        ImportBaseclass.ReInitialize(self)
1652        self.InitParameters()
1653        self.Banks = []             #for multi bank data (usually TOF)
1654        self.RefDict = {'RefList':[],'FF':{},'Super':0}
1655       
1656    def InitParameters(self):
1657        'initialize the instrument parameters structure'
1658        Lambda = 0.70926
1659        HistType = 'SXC'
1660        self.Parameters = [{'Type':[HistType,HistType], # create the structure
1661                            'Lam':[Lambda,Lambda]
1662                            }, {}]
1663        'Parameters is a list with two dicts for data parameter settings'
1664
1665    def UpdateParameters(self,Type=None,Wave=None):
1666        'Revise the instrument parameters'
1667        if Type is not None:
1668            self.Parameters[0]['Type'] = [Type,Type]
1669        if Wave is not None:
1670            self.Parameters[0]['Lam'] = [Wave,Wave]           
1671                       
1672######################################################################
1673class ImportPowderData(ImportBaseclass):
1674    '''Defines a base class for the reading of files with powder data.
1675
1676    Objects constructed that subclass this (in import/G2pwd_*.py etc.) will be used
1677    in :meth:`GSASII.GSASII.OnImportPowder`.
1678    See :ref:`Writing a Import Routine<Import_Routines>`
1679    for an explanation on how to use this class.
1680    '''
1681    def __init__(self,formatName,longFormatName=None,
1682        extensionlist=[],strictExtension=False,):
1683        ImportBaseclass.__init__(self,formatName,longFormatName,
1684            extensionlist,strictExtension)
1685        self.clockWd = None  # used in TOF
1686        self.ReInitialize()
1687       
1688    def ReInitialize(self):
1689        'Reinitialize the Reader to initial settings'
1690        ImportBaseclass.ReInitialize(self)
1691        self.powderentry = ['',None,None] #  (filename,Pos,Bank)
1692        self.powderdata = [] # Powder dataset
1693        '''A powder data set is a list with items [x,y,w,yc,yb,yd]:
1694                np.array(x), # x-axis values
1695                np.array(y), # powder pattern intensities
1696                np.array(w), # 1/sig(intensity)^2 values (weights)
1697                np.array(yc), # calc. intensities (zero)
1698                np.array(yb), # calc. background (zero)
1699                np.array(yd), # obs-calc profiles
1700        '''                           
1701        self.comments = []
1702        self.idstring = ''
1703        self.Sample = G2pdG.SetDefaultSample() # default sample parameters
1704        self.Controls = {}  # items to be placed in top-level Controls
1705        self.GSAS = None     # used in TOF
1706        self.repeat_instparm = True # Should a parm file be
1707        #                             used for multiple histograms?
1708        self.instparm = None # name hint from file of instparm to use
1709        self.instfile = '' # full path name to instrument parameter file
1710        self.instbank = '' # inst parm bank number
1711        self.instmsg = ''  # a label that gets printed to show
1712                           # where instrument parameters are from
1713        self.numbanks = 1
1714        self.instdict = {} # place items here that will be transferred to the instrument parameters
1715        self.pwdparms = {} # place parameters that are transferred directly to the tree
1716                           # here (typically from an existing GPX file)
1717######################################################################
1718class ImportSmallAngleData(ImportBaseclass):
1719    '''Defines a base class for the reading of files with small angle data.
1720    See :ref:`Writing a Import Routine<Import_Routines>`
1721    for an explanation on how to use this class.
1722    '''
1723    def __init__(self,formatName,longFormatName=None,extensionlist=[],
1724        strictExtension=False,):
1725           
1726        ImportBaseclass.__init__(self,formatName,longFormatName,extensionlist,
1727            strictExtension)
1728        self.ReInitialize()
1729       
1730    def ReInitialize(self):
1731        'Reinitialize the Reader to initial settings'
1732        ImportBaseclass.ReInitialize(self)
1733        self.smallangleentry = ['',None,None] #  (filename,Pos,Bank)
1734        self.smallangledata = [] # SASD dataset
1735        '''A small angle data set is a list with items [x,y,w,yc,yd]:
1736                np.array(x), # x-axis values
1737                np.array(y), # powder pattern intensities
1738                np.array(w), # 1/sig(intensity)^2 values (weights)
1739                np.array(yc), # calc. intensities (zero)
1740                np.array(yd), # obs-calc profiles
1741                np.array(yb), # preset bkg
1742        '''                           
1743        self.comments = []
1744        self.idstring = ''
1745        self.Sample = G2pdG.SetDefaultSample()
1746        self.GSAS = None     # used in TOF
1747        self.clockWd = None  # used in TOF
1748        self.numbanks = 1
1749        self.instdict = {} # place items here that will be transferred to the instrument parameters
1750
1751######################################################################
1752class ImportReflectometryData(ImportBaseclass):
1753    '''Defines a base class for the reading of files with small angle data.
1754    See :ref:`Writing a Import Routine<Import_Routines>`
1755    for an explanation on how to use this class.
1756    '''
1757    def __init__(self,formatName,longFormatName=None,extensionlist=[],
1758        strictExtension=False,):
1759           
1760        ImportBaseclass.__init__(self,formatName,longFormatName,extensionlist,
1761            strictExtension)
1762        self.ReInitialize()
1763       
1764    def ReInitialize(self):
1765        'Reinitialize the Reader to initial settings'
1766        ImportBaseclass.ReInitialize(self)
1767        self.reflectometryentry = ['',None,None] #  (filename,Pos,Bank)
1768        self.reflectometrydata = [] # SASD dataset
1769        '''A small angle data set is a list with items [x,y,w,yc,yd]:
1770                np.array(x), # x-axis values
1771                np.array(y), # powder pattern intensities
1772                np.array(w), # 1/sig(intensity)^2 values (weights)
1773                np.array(yc), # calc. intensities (zero)
1774                np.array(yd), # obs-calc profiles
1775                np.array(yb), # preset bkg
1776        '''                           
1777        self.comments = []
1778        self.idstring = ''
1779        self.Sample = G2pdG.SetDefaultSample()
1780        self.GSAS = None     # used in TOF
1781        self.clockWd = None  # used in TOF
1782        self.numbanks = 1
1783        self.instdict = {} # place items here that will be transferred to the instrument parameters
1784
1785######################################################################
1786class ImportImage(ImportBaseclass):
1787    '''Defines a base class for the reading of images
1788
1789    Images are read in only these places:
1790   
1791      * Initial reading is typically done from a menu item
1792        with a call to :meth:`GSASII.GSASII.OnImportImage`
1793        which in turn calls :meth:`GSASII.GSASII.OnImportGeneric`. That calls
1794        methods :meth:`ExtensionValidator`, :meth:`ContentsValidator` and
1795        :meth:`Reader`. This returns a list of reader objects for each read image.
1796
1797      * Images are read alternatively in :func:`GSASIIIO.ReadImages`, which puts image info
1798        directly into the data tree.
1799
1800      * Images are reloaded with :func:`GSASIIIO.GetImageData`.
1801
1802    .. _Image_import_routines:
1803
1804    When reading an image, the ``Reader()`` routine in the ImportImage class
1805    should set:
1806   
1807      * :attr:`Comments`: a list of strings (str),
1808      * :attr:`Npix`: the number of pixels in the image (int),
1809      * :attr:`Image`: the actual image as a numpy array (np.array)
1810      * :attr:`Data`: a dict defining image parameters (dict). Within this dict the following
1811        data items are needed:
1812       
1813         * 'pixelSize': size of each pixel in microns (such as ``[200,200]``.
1814         * 'wavelength': wavelength in Angstoms.
1815         * 'distance': distance of detector from sample in cm.
1816         * 'center': uncalibrated center of beam on detector (such as ``[204.8,204.8]``.
1817         * 'size': size of image (such as ``[2048,2048]``).
1818         * 'ImageTag': image number or other keyword used to retrieve image from
1819           a multi-image data file (defaults to ``1`` if not specified).
1820         * 'sumfile': holds sum image file name if a sum was produced from a multi image file
1821
1822    optional data items:
1823   
1824      * :attr:`repeat`: set to True if there are additional images to
1825        read in the file, False otherwise
1826      * :attr:`repeatcount`: set to the number of the image.
1827     
1828    Note that the above is initialized with :meth:`InitParameters`.
1829    (Also see :ref:`Writing a Import Routine<Import_Routines>`
1830    for an explanation on how to use import classes in general.)
1831    '''
1832    def __init__(self,formatName,longFormatName=None,extensionlist=[],
1833        strictExtension=False,):
1834        ImportBaseclass.__init__(self,formatName,longFormatName,
1835            extensionlist,strictExtension)
1836        self.InitParameters()
1837       
1838    def ReInitialize(self):
1839        'Reinitialize the Reader to initial settings -- not used at present'
1840        ImportBaseclass.ReInitialize(self)
1841        self.InitParameters()
1842       
1843    def InitParameters(self):
1844        'initialize the instrument parameters structure'
1845        self.Comments = ['No comments']
1846        self.Data = {}
1847        self.Npix = 0
1848        self.Image = None
1849        self.repeat = False
1850        self.repeatcount = 1
1851        self.sumfile = ''
1852
1853    def LoadImage(self,ParentFrame,imagefile,imagetag=None):
1854        '''Optionally, call this after reading in an image to load it into the tree.
1855        This saves time by preventing a reread of the same information.
1856        '''
1857        if ParentFrame:
1858            ParentFrame.ImageZ = self.Image   # store the image for plotting
1859            ParentFrame.oldImagefile = imagefile # save the name of the last image file read
1860            ParentFrame.oldImageTag = imagetag   # save the tag of the last image file read           
1861
1862######################################################################
1863def striphist(var,insChar=''):
1864    'strip a histogram number from a var name'
1865    sv = var.split(':')
1866    if len(sv) <= 1: return var
1867    if sv[1]:
1868        sv[1] = insChar
1869    return ':'.join(sv)
1870class ExportBaseclass(object):
1871    '''Defines a base class for the exporting of GSAS-II results.
1872
1873    This class is subclassed in the various exports/G2export_*.py files. Those files
1874    are imported in :meth:`GSASII.GSASII._init_Exports` which defines the
1875    appropriate menu items for each one and the .Exporter method is called
1876    directly from the menu item.
1877
1878    Routines may also define a .Writer method, which is used to write a single
1879    file without invoking any GUI objects.
1880    '''
1881    def __init__(self,G2frame,formatName,extension,longFormatName=None,):
1882        self.G2frame = G2frame
1883        self.formatName = formatName # short string naming file type
1884        self.extension = extension
1885        if longFormatName: # longer string naming file type
1886            self.longFormatName = longFormatName
1887        else:
1888            self.longFormatName = formatName
1889        self.OverallParms = {}
1890        self.Phases = {}
1891        self.Histograms = {}
1892        self.powderDict = {}
1893        self.xtalDict = {}
1894        self.parmDict = {}
1895        self.sigDict = {}
1896        # updated in InitExport:
1897        self.currentExportType = None # type of export that has been requested
1898        # updated in ExportSelect (when used):
1899        self.phasenam = None # a list of selected phases
1900        self.histnam = None # a list of selected histograms
1901        self.filename = None # name of file to be written (single export) or template (multiple files)
1902        self.dirname = '' # name of directory where file(s) will be written
1903        self.fullpath = '' # name of file being written -- full path
1904       
1905        # items that should be defined in a subclass of this class
1906        self.exporttype = []  # defines the type(s) of exports that the class can handle.
1907        # The following types are defined: 'project', "phase", "powder", "single"
1908        self.multiple = False # set as True if the class can export multiple phases or histograms
1909        # self.multiple is ignored for "project" exports
1910
1911    def InitExport(self,event):
1912        '''Determines the type of menu that called the Exporter and
1913        misc initialization.
1914        '''
1915        self.filename = None # name of file to be written (single export)
1916        self.dirname = '' # name of file to be written (multiple export)
1917        if event:
1918            self.currentExportType = self.G2frame.ExportLookup.get(event.Id)
1919
1920    def MakePWDRfilename(self,hist):
1921        '''Make a filename root (no extension) from a PWDR histogram name
1922
1923        :param str hist: the histogram name in data tree (starts with "PWDR ")
1924        '''
1925        file0 = ''
1926        file1 = hist[5:]
1927        # replace repeated blanks
1928        while file1 != file0:
1929            file0 = file1
1930            file1 = file0.replace('  ',' ').strip()
1931        file0 = file1.replace('Azm= ','A')
1932        # if angle has unneeded decimal places on aziumuth, remove them
1933        if file0[-3:] == '.00': file0 = file0[:-3]
1934        file0 = file0.replace('.','_')
1935        file0 = file0.replace(' ','_')
1936        return file0
1937
1938    def ExportSelect(self,AskFile='ask'):
1939        '''Selects histograms or phases when needed. Sets a default file name when
1940        requested into self.filename; always sets a default directory in self.dirname.
1941
1942        :param bool AskFile: Determines how this routine processes getting a
1943          location to store the current export(s).
1944         
1945          * if AskFile is 'ask' (default option), get the name of the file to be written;
1946            self.filename and self.dirname are always set. In the case where
1947            multiple files must be generated, the export routine should do this
1948            based on self.filename as a template.
1949          * if AskFile is 'dir', get the name of the directory to be used;
1950            self.filename is not used, but self.dirname is always set. The export routine
1951            will always generate the file name.
1952          * if AskFile is 'single', get only the name of the directory to be used when
1953            multiple items will be written (as multiple files) are used
1954            *or* a complete file name is requested when a single file
1955            name is selected. self.dirname is always set and self.filename used
1956            only when a single file is selected.
1957          * if AskFile is 'default', creates a name of the file to be used from
1958            the name of the project (.gpx) file. If the project has not been saved,
1959            then the name of file is requested.
1960            self.filename and self.dirname are always set. In the case where
1961            multiple file names must be generated, the export routine should do this
1962            based on self.filename.
1963          * if AskFile is 'default-dir', sets self.dirname from the project (.gpx)
1964            file. If the project has not been saved, then a directory is requested.
1965            self.filename is not used.
1966
1967        :returns: True in case of an error
1968        '''
1969       
1970        numselected = 1
1971        if self.currentExportType == 'phase':
1972            if len(self.Phases) == 0:
1973                self.G2frame.ErrorDialog(
1974                    'Empty project',
1975                    'Project does not contain any phases.')
1976                return True
1977            elif len(self.Phases) == 1:
1978                self.phasenam = self.Phases.keys()
1979            elif self.multiple: 
1980                choices = sorted(self.Phases.keys())
1981                phasenum = G2G.ItemSelector(choices,self.G2frame,multiple=True)
1982                if phasenum is None: return True
1983                self.phasenam = [choices[i] for i in phasenum]
1984                if not self.phasenam: return True
1985                numselected = len(self.phasenam)
1986            else:
1987                choices = sorted(self.Phases.keys())
1988                phasenum = G2G.ItemSelector(choices,self.G2frame)
1989                if phasenum is None: return True
1990                self.phasenam = [choices[phasenum]]
1991                numselected = len(self.phasenam)
1992        elif self.currentExportType == 'single':
1993            if len(self.xtalDict) == 0:
1994                self.G2frame.ErrorDialog(
1995                    'Empty project',
1996                    'Project does not contain any single crystal data.')
1997                return True
1998            elif len(self.xtalDict) == 1:
1999                self.histnam = self.xtalDict.values()
2000            elif self.multiple:
2001                choices = sorted(self.xtalDict.values())
2002                hnum = G2G.ItemSelector(choices,self.G2frame,multiple=True)
2003                if not hnum: return True
2004                self.histnam = [choices[i] for i in hnum]
2005                numselected = len(self.histnam)
2006            else:
2007                choices = sorted(self.xtalDict.values())
2008                hnum = G2G.ItemSelector(choices,self.G2frame)
2009                if hnum is None: return True
2010                self.histnam = [choices[hnum]]
2011                numselected = len(self.histnam)
2012        elif self.currentExportType == 'powder':
2013            if len(self.powderDict) == 0:
2014                self.G2frame.ErrorDialog(
2015                    'Empty project',
2016                    'Project does not contain any powder data.')
2017                return True
2018            elif len(self.powderDict) == 1:
2019                self.histnam = self.powderDict.values()
2020            elif self.multiple:
2021                choices = sorted(self.powderDict.values())
2022                hnum = G2G.ItemSelector(choices,self.G2frame,multiple=True)
2023                if not hnum: return True
2024                self.histnam = [choices[i] for i in hnum]
2025                numselected = len(self.histnam)
2026            else:
2027                choices = sorted(self.powderDict.values())
2028                hnum = G2G.ItemSelector(choices,self.G2frame)
2029                if hnum is None: return True
2030                self.histnam = [choices[hnum]]
2031                numselected = len(self.histnam)
2032        elif self.currentExportType == 'image':
2033            if len(self.Histograms) == 0:
2034                self.G2frame.ErrorDialog(
2035                    'Empty project',
2036                    'Project does not contain any images.')
2037                return True
2038            elif len(self.Histograms) == 1:
2039                self.histnam = self.Histograms.keys()
2040            else:
2041                choices = sorted(self.Histograms.keys())
2042                hnum = G2G.ItemSelector(choices,self.G2frame,multiple=self.multiple)
2043                if self.multiple:
2044                    if not hnum: return True
2045                    self.histnam = [choices[i] for i in hnum]
2046                else:
2047                    if hnum is None: return True
2048                    self.histnam = [choices[hnum]]
2049                numselected = len(self.histnam)
2050        if self.currentExportType == 'map':
2051            # search for phases with maps
2052            mapPhases = []
2053            choices = []
2054            for phasenam in sorted(self.Phases):
2055                phasedict = self.Phases[phasenam] # pointer to current phase info           
2056                if len(phasedict['General']['Map'].get('rho',[])):
2057                    mapPhases.append(phasenam)
2058                    if phasedict['General']['Map'].get('Flip'):
2059                        choices.append('Charge flip map: '+str(phasenam))
2060                    elif phasedict['General']['Map'].get('MapType'):
2061                        choices.append(
2062                            str(phasedict['General']['Map'].get('MapType'))
2063                            + ' map: ' + str(phasenam))
2064                    else:
2065                        choices.append('unknown map: '+str(phasenam))
2066            # select a map if needed
2067            if len(mapPhases) == 0:
2068                self.G2frame.ErrorDialog(
2069                    'Empty project',
2070                    'Project does not contain any maps.')
2071                return True
2072            elif len(mapPhases) == 1:
2073                self.phasenam = mapPhases
2074            else: 
2075                phasenum = G2G.ItemSelector(choices,self.G2frame,multiple=self.multiple)
2076                if self.multiple:
2077                    if not phasenum: return True
2078                    self.phasenam = [mapPhases[i] for i in phasenum]
2079                else:
2080                    if phasenum is None: return True
2081                    self.phasenam = [mapPhases[phasenum]]
2082            numselected = len(self.phasenam)
2083
2084        # items selected, now set self.dirname and usually self.filename
2085        if AskFile == 'ask' or (AskFile == 'single' and numselected == 1) or (
2086            AskFile == 'default' and not self.G2frame.GSASprojectfile
2087            ):
2088            filename = self.askSaveFile()
2089            if not filename: return True
2090            self.dirname,self.filename = os.path.split(filename)
2091        elif AskFile == 'dir' or AskFile == 'single' or (
2092            AskFile == 'default-dir' and not self.G2frame.GSASprojectfile
2093            ):
2094            self.dirname = self.askSaveDirectory()
2095            if not self.dirname: return True
2096        elif AskFile == 'default-dir' or AskFile == 'default':
2097            self.dirname,self.filename = os.path.split(
2098                os.path.splitext(self.G2frame.GSASprojectfile)[0] + self.extension
2099                )
2100        else:
2101            raise Exception('This should not happen!')
2102
2103    def loadParmDict(self):
2104        '''Load the GSAS-II refinable parameters from the tree into a dict (self.parmDict). Update
2105        refined values to those from the last cycle and set the uncertainties for the
2106        refined parameters in another dict (self.sigDict).
2107
2108        Expands the parm & sig dicts to include values derived from constraints.
2109        '''
2110        self.parmDict = {}
2111        self.sigDict = {}
2112        rigidbodyDict = {}
2113        covDict = {}
2114        consDict = {}
2115        Histograms,Phases = self.G2frame.GetUsedHistogramsAndPhasesfromTree()
2116        if self.G2frame.PatternTree.IsEmpty(): return # nothing to do
2117        item, cookie = self.G2frame.PatternTree.GetFirstChild(self.G2frame.root)
2118        while item:
2119            name = self.G2frame.PatternTree.GetItemText(item)
2120            if name == 'Rigid bodies':
2121                 rigidbodyDict = self.G2frame.PatternTree.GetItemPyData(item)
2122            elif name == 'Covariance':
2123                 covDict = self.G2frame.PatternTree.GetItemPyData(item)
2124            elif name == 'Constraints':
2125                 consDict = self.G2frame.PatternTree.GetItemPyData(item)
2126            item, cookie = self.G2frame.PatternTree.GetNextChild(self.G2frame.root, cookie)
2127        rbVary,rbDict =  G2stIO.GetRigidBodyModels(rigidbodyDict,Print=False)
2128        self.parmDict.update(rbDict)
2129        rbIds = rigidbodyDict.get('RBIds',{'Vector':[],'Residue':[]})
2130        Natoms,atomIndx,phaseVary,phaseDict,pawleyLookup,FFtables,BLtables,MFtables,maxSSwave =  G2stIO.GetPhaseData(
2131            Phases,RestraintDict=None,rbIds=rbIds,Print=False)
2132        self.parmDict.update(phaseDict)
2133        hapVary,hapDict,controlDict =  G2stIO.GetHistogramPhaseData(
2134            Phases,Histograms,Print=False,resetRefList=False)
2135        self.parmDict.update(hapDict)
2136        histVary,histDict,controlDict =  G2stIO.GetHistogramData(Histograms,Print=False)
2137        self.parmDict.update(histDict)
2138        self.parmDict.update(zip(
2139            covDict.get('varyList',[]),
2140            covDict.get('variables',[])))
2141        self.sigDict = dict(zip(
2142            covDict.get('varyList',[]),
2143            covDict.get('sig',[])))
2144        # expand to include constraints: first compile a list of constraints
2145        constList = []
2146        for item in consDict:
2147            if item.startswith('_'): continue
2148            constList += consDict[item]
2149        # now process the constraints
2150        G2mv.InitVars()
2151        constDict,fixedList,ignored = G2stIO.ProcessConstraints(constList)
2152        varyList = covDict.get('varyListStart')
2153        if varyList is None and len(constDict) == 0:
2154            # no constraints can use varyList
2155            varyList = covDict.get('varyList')
2156        elif varyList is None:
2157            # old GPX file from before pre-constraint varyList is saved
2158            print ' *** Old refinement: Please use Calculate/Refine to redo  ***'
2159            raise Exception(' *** Export aborted ***')
2160        else:
2161            varyList = list(varyList)
2162        try:
2163            groups,parmlist = G2mv.GroupConstraints(constDict)
2164            G2mv.GenerateConstraints(groups,parmlist,varyList,constDict,fixedList,self.parmDict)
2165        except:
2166            # this really should not happen
2167            print ' *** ERROR - constraints are internally inconsistent ***'
2168            errmsg, warnmsg = G2mv.CheckConstraints(varyList,constDict,fixedList)
2169            print 'Errors',errmsg
2170            if warnmsg: print 'Warnings',warnmsg
2171            raise Exception(' *** CIF creation aborted ***')
2172        # add the constrained values to the parameter dictionary
2173        G2mv.Dict2Map(self.parmDict,varyList)
2174        # and add their uncertainties into the esd dictionary (sigDict)
2175        if covDict.get('covMatrix') is not None:
2176            self.sigDict.update(G2mv.ComputeDepESD(covDict['covMatrix'],covDict['varyList'],self.parmDict))
2177
2178    def loadTree(self):
2179        '''Load the contents of the data tree into a set of dicts
2180        (self.OverallParms, self.Phases and self.Histogram as well as self.powderDict
2181        & self.xtalDict)
2182       
2183        * The childrenless data tree items are overall parameters/controls for the
2184          entire project and are placed in self.OverallParms
2185        * Phase items are placed in self.Phases
2186        * Data items are placed in self.Histogram. The key for these data items
2187          begin with a keyword, such as PWDR, IMG, HKLF,... that identifies the data type.
2188        '''
2189        self.OverallParms = {}
2190        self.powderDict = {}
2191        self.xtalDict = {}
2192        self.Phases = {}
2193        self.Histograms = {}
2194        self.SeqRefdata = None
2195        self.SeqRefhist = None
2196        if self.G2frame.PatternTree.IsEmpty(): return # nothing to do
2197        histType = None       
2198        if self.currentExportType == 'phase':
2199            # if exporting phases load them here
2200            sub = G2gd.GetPatternTreeItemId(self.G2frame,self.G2frame.root,'Phases')
2201            if not sub:
2202                print 'no phases found'
2203                return True
2204            item, cookie = self.G2frame.PatternTree.GetFirstChild(sub)
2205            while item:
2206                phaseName = self.G2frame.PatternTree.GetItemText(item)
2207                self.Phases[phaseName] =  self.G2frame.PatternTree.GetItemPyData(item)
2208                item, cookie = self.G2frame.PatternTree.GetNextChild(sub, cookie)
2209            return
2210        elif self.currentExportType == 'single':
2211            histType = 'HKLF'
2212        elif self.currentExportType == 'powder':
2213            histType = 'PWDR'
2214        elif self.currentExportType == 'image':
2215            histType = 'IMG'
2216
2217        if histType: # Loading just one kind of tree entry
2218            item, cookie = self.G2frame.PatternTree.GetFirstChild(self.G2frame.root)
2219            while item:
2220                name = self.G2frame.PatternTree.GetItemText(item)
2221                if name.startswith(histType):
2222                    if self.Histograms.get(name): # there is already an item with this name
2223                        print('Histogram name '+str(name)+' is repeated. Renaming')
2224                        if name[-1] == '9':
2225                            name = name[:-1] + '10'
2226                        elif name[-1] in '012345678':
2227                            name = name[:-1] + str(int(name[-1])+1)
2228                        else:                           
2229                            name += '-1'
2230                    self.Histograms[name] = {}
2231                    # the main info goes into Data, but the 0th
2232                    # element contains refinement results, carry
2233                    # that over too now.
2234                    self.Histograms[name]['Data'] = self.G2frame.PatternTree.GetItemPyData(item)[1]
2235                    self.Histograms[name][0] = self.G2frame.PatternTree.GetItemPyData(item)[0]
2236                    item2, cookie2 = self.G2frame.PatternTree.GetFirstChild(item)
2237                    while item2: 
2238                        child = self.G2frame.PatternTree.GetItemText(item2)
2239                        self.Histograms[name][child] = self.G2frame.PatternTree.GetItemPyData(item2)
2240                        item2, cookie2 = self.G2frame.PatternTree.GetNextChild(item, cookie2)
2241                item, cookie = self.G2frame.PatternTree.GetNextChild(self.G2frame.root, cookie)
2242            # index powder and single crystal histograms by number
2243            for hist in self.Histograms:
2244                if hist.startswith("PWDR"): 
2245                    d = self.powderDict
2246                elif hist.startswith("HKLF"): 
2247                    d = self.xtalDict
2248                else:
2249                    return                   
2250                i = self.Histograms[hist].get('hId')
2251                if i is None and not d.keys():
2252                    i = 0
2253                elif i is None or i in d.keys():
2254                    i = max(d.keys())+1
2255                d[i] = hist
2256            return
2257        # else standard load: using all interlinked phases and histograms
2258        self.Histograms,self.Phases = self.G2frame.GetUsedHistogramsAndPhasesfromTree()
2259        item, cookie = self.G2frame.PatternTree.GetFirstChild(self.G2frame.root)
2260        while item:
2261            name = self.G2frame.PatternTree.GetItemText(item)
2262            item2, cookie2 = self.G2frame.PatternTree.GetFirstChild(item)
2263            if not item2: 
2264                self.OverallParms[name] = self.G2frame.PatternTree.GetItemPyData(item)
2265            item, cookie = self.G2frame.PatternTree.GetNextChild(self.G2frame.root, cookie)
2266        # index powder and single crystal histograms
2267        for hist in self.Histograms:
2268            i = self.Histograms[hist]['hId']
2269            if hist.startswith("PWDR"): 
2270                self.powderDict[i] = hist
2271            elif hist.startswith("HKLF"): 
2272                self.xtalDict[i] = hist
2273
2274    def dumpTree(self,mode='type'):
2275        '''Print out information on the data tree dicts loaded in loadTree.
2276        Used for testing only.
2277        '''
2278        if self.SeqRefdata and self.SeqRefhist:
2279            print('Note that dumpTree does not show sequential results')
2280        print '\nOverall'
2281        if mode == 'type':
2282            def Show(arg): return type(arg)
2283        else:
2284            def Show(arg): return arg
2285        for key in self.OverallParms:
2286            print '  ',key,Show(self.OverallParms[key])
2287        print 'Phases'
2288        for key1 in self.Phases:
2289            print '    ',key1,Show(self.Phases[key1])
2290        print 'Histogram'
2291        for key1 in self.Histograms:
2292            print '    ',key1,Show(self.Histograms[key1])
2293            for key2 in self.Histograms[key1]:
2294                print '      ',key2,Show(self.Histograms[key1][key2])
2295
2296    def defaultSaveFile(self):
2297        return os.path.abspath(
2298            os.path.splitext(self.G2frame.GSASprojectfile
2299                             )[0]+self.extension)
2300       
2301    def askSaveFile(self):
2302        '''Ask the user to supply a file name
2303
2304        :returns: a file name (str) or None if Cancel is pressed
2305        '''
2306       
2307        pth = G2G.GetExportPath(self.G2frame)
2308        defnam = os.path.splitext(
2309            os.path.split(self.G2frame.GSASprojectfile)[1]
2310            )[0]+self.extension
2311        dlg = wx.FileDialog(
2312            self.G2frame, 'Input name for file to write', pth, defnam,
2313            self.longFormatName+' (*'+self.extension+')|*'+self.extension,
2314            wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT)
2315        dlg.CenterOnParent()
2316        try:
2317            if dlg.ShowModal() == wx.ID_OK:
2318                filename = dlg.GetPath()
2319                self.G2frame.LastExportDir = os.path.split(filename)[0]
2320                filename = os.path.splitext(filename)[0]+self.extension # make sure extension is correct
2321            else:
2322                filename = None
2323        finally:
2324            dlg.Destroy()
2325        return filename
2326
2327    def askSaveDirectory(self):
2328        '''Ask the user to supply a directory name. Path name is used as the
2329        starting point for the next export path search.
2330
2331        :returns: a directory name (str) or None if Cancel is pressed
2332        '''
2333        pth = G2G.GetExportPath(self.G2frame)
2334        dlg = wx.DirDialog(
2335            self.G2frame, 'Input directory where file(s) will be written', pth,
2336            wx.DD_DEFAULT_STYLE)
2337        dlg.CenterOnParent()
2338        try:
2339            if dlg.ShowModal() == wx.ID_OK:
2340                filename = dlg.GetPath()
2341                self.G2frame.LastExportDir = filename
2342            else:
2343                filename = None
2344        finally:
2345            dlg.Destroy()
2346        return filename
2347
2348    # Tools for file writing.
2349    def OpenFile(self,fil=None,mode='w'):
2350        '''Open the output file
2351
2352        :param str fil: The name of the file to open. If None (default)
2353          the name defaults to self.dirname + self.filename.
2354          If an extension is supplied, it is not overridded,
2355          but if not, the default extension is used.
2356        :returns: the file object opened by the routine which is also
2357          saved as self.fp
2358        '''
2359        if mode == 'd': # debug mode
2360            self.fullpath = '(stdout)'
2361            self.fp = sys.stdout
2362            return
2363        if not fil:
2364            if not os.path.splitext(self.filename)[1]:
2365                self.filename += self.extension
2366            fil = os.path.join(self.dirname,self.filename)
2367        self.fullpath = os.path.abspath(fil)
2368        self.fp = open(self.fullpath,mode)
2369        return self.fp
2370
2371    def Write(self,line):
2372        '''write a line of output, attaching a line-end character
2373
2374        :param str line: the text to be written.
2375        '''
2376        self.fp.write(line+'\n')
2377       
2378    def CloseFile(self,fp=None):
2379        '''Close a file opened in OpenFile
2380
2381        :param file fp: the file object to be closed. If None (default)
2382          file object self.fp is closed.
2383        '''
2384        if self.fp == sys.stdout: return # debug mode
2385        if fp is None:
2386            fp = self.fp
2387            self.fp = None
2388        fp.close()
2389       
2390    def SetSeqRef(self,data,hist):
2391        '''Set the exporter to retrieve results from a sequential refinement
2392        rather than the main tree
2393        '''
2394        self.SeqRefdata = data
2395        self.SeqRefhist = hist
2396        data_name = data[hist]
2397        for i,val in zip(data_name['varyList'],data_name['sig']):
2398            self.sigDict[i] = val
2399            self.sigDict[striphist(i)] = val
2400        for i in data_name['parmDict']:
2401            self.parmDict[striphist(i)] = data_name['parmDict'][i]
2402            self.parmDict[i] = data_name['parmDict'][i]
2403            # zero out the dA[xyz] terms, they would only bring confusion
2404            key = i.split(':')
2405            if len(key) < 3: continue
2406            if key[2].startswith('dA'):
2407                self.parmDict[i] = 0.0
2408        for i,(val,sig) in data_name.get('depParmDict',{}).iteritems():
2409            self.parmDict[i] = val
2410            self.sigDict[i] = sig
2411        #GSASIIpath.IPyBreak()
2412               
2413    # Tools to pull information out of the data arrays
2414    def GetCell(self,phasenam):
2415        """Gets the unit cell parameters and their s.u.'s for a selected phase
2416
2417        :param str phasenam: the name for the selected phase
2418        :returns: `cellList,cellSig` where each is a 7 element list corresponding
2419          to a, b, c, alpha, beta, gamma, volume where `cellList` has the
2420          cell values and `cellSig` has their uncertainties.
2421        """
2422        if self.SeqRefdata and self.SeqRefhist:
2423            return self.GetSeqCell(phasenam,self.SeqRefdata[self.SeqRefhist])
2424        phasedict = self.Phases[phasenam] # pointer to current phase info
2425        try:
2426            pfx = str(phasedict['pId'])+'::'
2427            A,sigA = G2stIO.cellFill(pfx,phasedict['General']['SGData'],self.parmDict,self.sigDict)
2428            cellSig = G2stIO.getCellEsd(pfx,phasedict['General']['SGData'],A,
2429                self.OverallParms['Covariance'])  # returns 7 vals, includes sigVol
2430            cellList = G2lat.A2cell(A) + (G2lat.calc_V(A),)
2431            return cellList,cellSig
2432        except KeyError:
2433            cell = phasedict['General']['Cell'][1:]
2434            return cell,7*[0]
2435           
2436    def GetSeqCell(self,phasenam,data_name):
2437        """Gets the unit cell parameters and their s.u.'s for a selected phase
2438        and histogram in a sequential fit
2439
2440        :param str phasenam: the name for the selected phase
2441        :param dict data_name: the sequential refinement parameters for the selected histogram
2442        :returns: `cellList,cellSig` where each is a 7 element list corresponding
2443          to a, b, c, alpha, beta, gamma, volume where `cellList` has the
2444          cell values and `cellSig` has their uncertainties.
2445        """
2446        phasedict = self.Phases[phasenam]
2447        SGdata = phasedict['General']['SGData']
2448        pId = phasedict['pId']
2449        RecpCellTerms = G2lat.cell2A(phasedict['General']['Cell'][1:7])
2450        ESDlookup = {}
2451        Dlookup = {}
2452        varied = [striphist(i) for i in data_name['varyList']]
2453        for item,val in data_name['newCellDict'].iteritems():
2454            if item in varied:
2455                ESDlookup[val[0]] = item
2456                Dlookup[item] = val[0]
2457        A = RecpCellTerms[:]
2458        for i in range(6):
2459            var = str(pId)+'::A'+str(i)
2460            if var in ESDlookup:
2461                A[i] = data_name['newCellDict'][ESDlookup[var]][1] # override with refined value
2462        cellDict = dict(zip([str(pId)+'::A'+str(i) for i in range(6)],A))
2463        zeroDict = {i:0.0 for i in cellDict}
2464        A,zeros = G2stIO.cellFill(str(pId)+'::',SGdata,cellDict,zeroDict)
2465        covData = {
2466            'varyList': [Dlookup.get(striphist(v),v) for v in data_name['varyList']],
2467            'covMatrix': data_name['covMatrix']
2468            }
2469        return list(G2lat.A2cell(A)) + [G2lat.calc_V(A)], G2stIO.getCellEsd(str(pId)+'::',SGdata,A,covData)
2470               
2471    def GetAtoms(self,phasenam):
2472        """Gets the atoms associated with a phase. Can be used with standard
2473        or macromolecular phases
2474
2475        :param str phasenam: the name for the selected phase
2476        :returns: a list of items for eac atom where each item is a list containing:
2477          label, typ, mult, xyz, and td, where
2478
2479          * label and typ are the atom label and the scattering factor type (str)
2480          * mult is the site multiplicity (int)
2481          * xyz is contains a list with four pairs of numbers:
2482            x, y, z and fractional occupancy and
2483            their standard uncertainty (or a negative value)
2484          * td is contains a list with either one or six pairs of numbers:
2485            if one number it is U\ :sub:`iso` and with six numbers it is
2486            U\ :sub:`11`, U\ :sub:`22`, U\ :sub:`33`, U\ :sub:`12`, U\ :sub:`13` & U\ :sub:`23`
2487            paired with their standard uncertainty (or a negative value)
2488        """
2489        phasedict = self.Phases[phasenam] # pointer to current phase info           
2490        cx,ct,cs,cia = phasedict['General']['AtomPtrs']
2491        cfrac = cx+3
2492        fpfx = str(phasedict['pId'])+'::Afrac:'       
2493        atomslist = []
2494        for i,at in enumerate(phasedict['Atoms']):
2495            if phasedict['General']['Type'] == 'macromolecular':
2496                label = '%s_%s_%s_%s'%(at[ct-1],at[ct-3],at[ct-4],at[ct-2])
2497            else:
2498                label = at[ct-1]
2499            fval = self.parmDict.get(fpfx+str(i),at[cfrac])
2500            fsig = self.sigDict.get(fpfx+str(i),-0.009)
2501            mult = at[cs+1]
2502            typ = at[ct]
2503            xyz = []
2504            for j,v in enumerate(('x','y','z')):
2505                val = at[cx+j]
2506                pfx = str(phasedict['pId']) + '::A' + v + ':' + str(i)
2507                val = self.parmDict.get(pfx, val)
2508                dpfx = str(phasedict['pId'])+'::dA'+v+':'+str(i)
2509                sig = self.sigDict.get(dpfx,-0.000009)
2510                xyz.append((val,sig))
2511            xyz.append((fval,fsig))
2512            td = []
2513            if at[cia] == 'I':
2514                pfx = str(phasedict['pId'])+'::AUiso:'+str(i)
2515                val = self.parmDict.get(pfx,at[cia+1])
2516                sig = self.sigDict.get(pfx,-0.0009)
2517                td.append((val,sig))
2518            else:
2519                for i,var in enumerate(('AU11','AU22','AU33','AU12','AU13','AU23')):
2520                    pfx = str(phasedict['pId'])+'::'+var+':'+str(i)
2521                    val = self.parmDict.get(pfx,at[cia+2+i])
2522                    sig = self.sigDict.get(pfx,-0.0009)
2523                    td.append((val,sig))
2524            atomslist.append((label,typ,mult,xyz,td))
2525        return atomslist
2526######################################################################
2527def ExportPowderList(G2frame):
2528    '''Returns a list of extensions supported by :func:`GSASIIIO:ExportPowder`
2529    This is used in :meth:`GSASIIimgGUI.AutoIntFrame` only.
2530   
2531    :param wx.Frame G2frame: the GSAS-II main data tree window
2532    '''
2533    extList = []
2534    for obj in G2frame.exporterlist:
2535        if 'powder' in obj.exporttype:
2536            try:
2537                obj.Writer
2538                extList.append(obj.extension)
2539            except AttributeError:
2540                pass
2541    return extList
2542
2543def ExportPowder(G2frame,TreeName,fileroot,extension):
2544    '''Writes a single powder histogram using the Export routines.
2545    This is used in :meth:`GSASIIimgGUI.AutoIntFrame` only.
2546
2547    :param wx.Frame G2frame: the GSAS-II main data tree window
2548    :param str TreeName: the name of the histogram (PWDR ...) in the data tree
2549    :param str fileroot: name for file to be written, extension ignored
2550    :param str extension: extension for file to be written (start with '.'). Must
2551      match a powder export routine that has a Writer object.
2552    '''
2553    filename = os.path.abspath(os.path.splitext(fileroot)[0]+extension)
2554    for obj in G2frame.exporterlist:
2555        if obj.extension == extension and 'powder' in obj.exporttype:
2556            obj.currentExportType = 'powder'
2557            obj.InitExport(None)
2558            obj.loadTree() # load all histograms in tree into dicts
2559            if TreeName not in obj.Histograms:
2560                raise Exception('Histogram not found: '+str(TreeName))
2561            try:
2562                obj.Writer
2563            except AttributeError:
2564                continue
2565            try:
2566                obj.Writer(TreeName,filename)
2567                return
2568            except Exception,err:
2569                print('Export Routine for '+extension+' failed.')
2570                print err
2571    else:
2572        print('No Export routine supports extension '+extension)
2573
2574def ExportSequential(G2frame,data,obj,exporttype):
2575    '''
2576    Used to export from every phase/dataset in a sequential refinement using
2577    a .Writer method for either projects or phases. Prompts to select histograms
2578    and for phase exports, which phase(s).
2579
2580    :param wx.Frame G2frame: the GSAS-II main data tree window
2581    :param dict data: the sequential refinement data object
2582    :param str exporttype: indicates the type of export ('project' or 'phase')
2583    '''
2584    if len(data['histNames']) == 0:
2585        G2G.G2MessageBox(G2frame,'There are no sequential histograms','Warning')
2586    obj.InitExport(None)
2587    obj.loadTree()
2588    obj.loadParmDict()
2589    if len(data['histNames']) == 1:
2590        histlist = data['histNames']
2591    else:
2592        dlg = G2G.G2MultiChoiceDialog(G2frame,'Select histograms to export from list',
2593                                 'Select histograms',data['histNames'])
2594        if dlg.ShowModal() == wx.ID_OK:
2595            histlist = [data['histNames'][l] for l in dlg.GetSelections()]
2596            dlg.Destroy()
2597        else:
2598            dlg.Destroy()
2599            return
2600    if exporttype == 'Phase':
2601        phaselist = obj.Phases.keys()
2602        if len(obj.Phases) == 0:
2603            G2G.G2MessageBox(G2frame,'There are no phases in sequential ref.','Warning')
2604            return
2605        elif len(obj.Phases) > 1:
2606            dlg = G2G.G2MultiChoiceDialog(G2frame,'Select phases to export from list',
2607                                    'Select phases', phaselist)
2608            if dlg.ShowModal() == wx.ID_OK:
2609                phaselist = [phaselist[l] for l in dlg.GetSelections()]
2610                dlg.Destroy()
2611            else:
2612                dlg.Destroy()
2613                return
2614        filename = obj.askSaveFile()
2615        if not filename: return True
2616        obj.dirname,obj.filename = os.path.split(filename)
2617        print('Writing output to file '+str(obj.filename)+"...")
2618        mode = 'w'
2619        for p in phaselist:
2620            for h in histlist:
2621                obj.SetSeqRef(data,h)
2622                #GSASIIpath.IPyBreak()
2623                obj.Writer(h,phasenam=p,mode=mode)
2624                mode = 'a'
2625        print('...done')
2626    elif exporttype == 'Project':
2627        filename = obj.askSaveFile()
2628        if not filename: return True
2629        obj.dirname,obj.filename = os.path.split(filename)
2630        print('Writing output to file '+str(obj.filename)+"...")
2631        mode = 'w'
2632        for h in histlist:
2633            obj.SetSeqRef(data,h)
2634            obj.Writer(h,mode=mode)
2635            print('\t'+str(h)+' written')
2636            mode = 'a'
2637        print('...done')
2638
2639def ReadDIFFaX(DIFFaXfile):
2640    print 'read ',DIFFaXfile
2641    Layer = {'Laue':'-1','Cell':[False,1.,1.,1.,90.,90.,90,1.],'Width':[[10.,10.],[False,False]],
2642        'Layers':[],'Stacking':[],'Transitions':[],'Toler':0.01,'AtInfo':{}}
2643    df = open(DIFFaXfile,'r')
2644    lines = df.readlines()
2645    df.close()
2646    struct = False
2647    Struct = []
2648    stack = False
2649    Stack = []
2650    trans = False
2651    Trans = []
2652    for diff in lines:
2653        diff = diff[:-1].lower()
2654        if '!'  in diff:
2655            continue
2656        while '}' in diff: #strip comments
2657            iB = diff.index('{')
2658            iF = diff.index('}')+1
2659            if iB:
2660                diff = diff[:iB]
2661            else:
2662                diff = diff[iF:]
2663        if not diff:
2664            continue
2665        if diff.strip() == 'instrumental':
2666            continue
2667        if diff.strip() == 'structural':
2668            struct = True
2669            continue
2670        elif diff.strip() == 'stacking':
2671            struct = False
2672            stack = True
2673            continue
2674        elif diff.strip() == 'transitions':
2675            stack = False
2676            trans = True
2677            continue
2678        diff = diff.strip()
2679        if struct:
2680            if diff:
2681                Struct.append(diff)
2682        elif stack:
2683            if diff:
2684                Stack.append(diff)
2685        elif trans:
2686            if diff:
2687                Trans.append(diff)
2688   
2689#STRUCTURE records
2690    laueRec = Struct[1].split()
2691    Layer['Laue'] = laueRec[0]
2692    if Layer['Laue'] == 'unknown' and len(laueRec) > 1:
2693        Layer['Toler'] = float(laueRec[1])    #tolerance for 'unknown'?
2694    if Layer['Laue'] == '2/m(1)': Layer['Laue'] = '2/m(c)'
2695    if Layer['Laue'] == '2/m(2)': Layer['Laue'] = '2/m(ab)'
2696    cell = Struct[0].split()
2697    Layer['Cell'] = [False,float(cell[0]),float(cell[1]),float(cell[2]),90.,90.,float(cell[3]),1.0]
2698    nLayers = int(Struct[2])
2699    N = 3
2700    if 'layer' not in Struct[3]:
2701        N = 4
2702        if Struct[3] != 'infinite':
2703            width = Struct[3].split()
2704            Layer['Width'][0] = [float(width[0]),float(width[1])]
2705    for nL in range(nLayers):
2706        if '=' in Struct[N]:
2707            name = Struct[N].split('=')
2708            sameas = int(name[1])-1
2709            Layer['Layers'].append({'Name':name[0],'SameAs':Layer['Layers'][sameas]['Name'],'Symm':'None','Atoms':[]})
2710            N += 1
2711            continue
2712        Symm = 'None'
2713        if 'centro' in Struct[N+1]: Symm = '-1'
2714        Layer['Layers'].append({'Name':Struct[N],'SameAs':'','Symm':Symm,'Atoms':[]})
2715        N += 2
2716        while 'layer' not in Struct[N]:
2717            atom = Struct[N][4:].split()
2718            atomType = G2el.FixValence(Struct[N][:4].replace(' ','').strip().capitalize())
2719            if atomType not in Layer['AtInfo']:
2720                Layer['AtInfo'][atomType] = G2el.GetAtomInfo(atomType)
2721            atomName = '%s(%s)'%(atomType,atom[0])
2722            newVals = []
2723            for val in atom[1:6]:
2724                if '/' in val:
2725                    newVals.append(eval(val+'.'))
2726                else:
2727                    newVals.append(float(val))               
2728            atomRec = [atomName,atomType,newVals[0],newVals[1],newVals[2],newVals[4],newVals[3]/78.9568]
2729            Layer['Layers'][-1]['Atoms'].append(atomRec)
2730            N += 1
2731            if N > len(Struct)-1:
2732                break
2733#TRANSITIONS records
2734    transArray = []
2735    N = 0
2736    for i in range(nLayers):
2737        transArray.append([])
2738        for j in range(nLayers):
2739            vals = Trans[N].split()
2740            newVals = []
2741            for val in vals[:4]:
2742                if '/' in val:
2743                    newVals.append(eval(val+'.'))
2744                else:
2745                    newVals.append(float(val))
2746            transArray[-1].append(newVals+['',False])
2747            N += 1
2748    Layer['Transitions'] = transArray
2749#STACKING records
2750    Layer['Stacking'] = [Stack[0],'']
2751    if Stack[0] == 'recursive':
2752        Layer['Stacking'][1] = Stack[1]
2753    elif Stack[0] == 'explicit':
2754        if Stack[1] == 'random':
2755            Layer['Stacking'][1] = Stack[1]
2756        else:
2757            Layer['Stacking'][1] = 'list'
2758            Layer['Stacking'].append('')
2759            for stack in Stack[2:]:
2760                Layer['Stacking'][2] += ' '+stack
2761    return Layer
2762
2763def ReadCIF(URLorFile):
2764    '''Open a CIF, which may be specified as a file name or as a URL using PyCifRW
2765    (from James Hester).
2766    The open routine gets confused with DOS names that begin with a letter and colon
2767    "C:\dir\" so this routine will try to open the passed name as a file and if that
2768    fails, try it as a URL
2769
2770    :param str URLorFile: string containing a URL or a file name. Code will try first
2771      to open it as a file and then as a URL.
2772
2773    :returns: a PyCifRW CIF object.
2774    '''
2775    import CifFile as cif # PyCifRW from James Hester
2776
2777    # alternate approach:
2778    #import urllib
2779    #ciffile = 'file:'+urllib.pathname2url(filename)
2780   
2781    try:
2782        fp = open(URLorFile,'r')
2783        cf = cif.ReadCif(fp)
2784        fp.close()
2785        return cf
2786    except IOError:
2787        return cif.ReadCif(URLorFile)
2788
2789if __name__ == '__main__':
2790    import GSASII
2791    application = GSASII.GSASIImain(0)
2792    G2frame = application.main
2793    #app = wx.PySimpleApp()
2794    #G2frame = wx.Frame(None) # create a frame
2795    #frm.Show(True)
2796    #filename = '/tmp/notzip.zip'
2797    #filename = '/tmp/all.zip'
2798    #filename = '/tmp/11bmb_7652.zip'
2799   
2800    #selection=None, confirmoverwrite=True, parent=None
2801    #print ExtractFileFromZip(filename, selection='11bmb_7652.fxye',parent=frm)
2802    #print ExtractFileFromZip(filename,multipleselect=True)
2803    #                         #confirmread=False, confirmoverwrite=False)
2804
2805    # choicelist=[ ('a','b','c'),
2806    #              ('test1','test2'),('no choice',)]
2807    # titles = [ 'a, b or c', 'tests', 'No option here']
2808    # dlg = MultipleChoicesDialog(
2809    #     choicelist,titles,
2810    #     parent=frm)
2811    # if dlg.ShowModal() == wx.ID_OK:
2812    #     print 'Got OK'
2813    imagefile = '/tmp/NDC5_00237_3.ge3'
2814    Comments, Data, Npix, Image = GetImageData(G2frame,imagefile,imageOnly=False,ImageTag=None)
2815
2816    print("\n\nResults loaded to Comments, Data, Npix and Image\n\n")
2817
2818    GSASIIpath.IPyBreak_base()
Note: See TracBrowser for help on using the repository browser.