source: trunk/GSASIIIO.py @ 1359

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

start allowing reading of multiple HKLF files.
put an IndexAllIds? into UpdateConstraints?
edf image fixes
more places in Pawley d-min
format of penalty fxn chi2
allow one Bank selection set to be used for multiple files
new reader for TOF HKLF2 data - nonworking just now

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