source: trunk/GSASIIIO.py @ 2561

Last change on this file since 2561 was 2561, checked in by vondreele, 5 years ago

add Rmax to PDF Controls; default = 100.; will not be exact
PDF always generates 5000 points for 0-Rmax; independent of chosen Qmax or image binning.
Trap attempt to calculate PDF without chemical formula - now get error message.

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