source: trunk/GSASIIIO.py @ 1102

Last change on this file since 1102 was 1102, checked in by toby, 8 years ago

change exports to always have lists of phases & histograms; new export examples; CIF export fixes

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