source: trunk/GSASIIIO.py @ 1297

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

setup sequential small angle refinements & display
includes copy & copy flags over Models
stop pattern plots in multiple integrations; now only when done

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