source: trunk/GSASIIIO.py @ 1084

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

installer uses xterm on GNOME shortcut

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