source: trunk/GSASIIIO.py @ 1459

Last change on this file since 1459 was 1459, checked in by vondreele, 9 years ago

add instrument parameters (flight path & detector 2-theta) needed for TOF
rework reflection list for TOF
change default diff curve & reflection marker offsets
change weighting to instrument constants calibration to be 1/esd2 from peak fit positions - works a lot better
1st shot at TOF Rietveld refinement with derivatives - need to be checked for correctness (some are wrong)

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