source: trunk/imports/G2pwd_fxye.py

Last change on this file was 5577, checked in by toby, 2 weeks ago

finish docs reformatting with changes to G2tools and GSASIIscripts; ReadTheDocs? html now looks good

  • Property svn:eol-style set to native
  • Property svn:keywords set to Date Author Revision URL Id
File size: 16.9 KB
Line 
1# -*- coding: utf-8 -*-
2########### SVN repository information ###################
3# $Date: 2023-05-11 23:08:12 +0000 (Thu, 11 May 2023) $
4# $Author: toby $
5# $Revision: 5577 $
6# $URL: trunk/imports/G2pwd_fxye.py $
7# $Id: G2pwd_fxye.py 5577 2023-05-11 23:08:12Z toby $
8########### SVN repository information ###################
9'''
10'''
11from __future__ import division, print_function
12import os.path as ospath
13import platform
14import numpy as np
15import GSASIIobj as G2obj
16import GSASIIpath
17GSASIIpath.SetVersionNumber("$Revision: 5577 $")
18
19class GSAS_ReaderClass(G2obj.ImportPowderData):
20    'Routines to import powder data from a GSAS files'
21    def __init__(self):
22        super(self.__class__,self).__init__( # fancy way to self-reference
23            extensionlist=('.fxye','.raw','.gsas','.gda','.gsa','.gss','.RAW','.GSAS','.GDA','.GSA','.dat'),
24            strictExtension=False,
25            formatName = 'GSAS powder data',
26            longFormatName = 'GSAS powder data files (.fxye, .raw, .gsas...)'
27            )
28        self.clockWd = {}
29        self.TimeMap = {}
30        self.dnames = []
31        self.scriptable = True
32
33    # Validate the contents -- look for a bank line
34    def ContentsValidator(self, filename):
35        'Validrate by checking to see if the file has BANK lines & count them'
36        #print 'ContentsValidator: '+self.formatName
37        nBanks= 0
38        if '2' in platform.python_version_tuple()[0]:
39            fp = open(filename,'Ur')
40        else:
41            fp = open(filename,'r',encoding='latin-1')
42        fname = ospath.basename(fp.name)
43        for i,line in enumerate(fp):
44            self.GSAS = True
45            if i==0: # first line is always a comment
46                continue
47            if i==1 and line[:4].lower() == 'inst':
48                # 2nd line is optional instrument parameter file
49                continue
50            if line[0] == '#': continue
51            if line[:4] == 'BANK':
52                self.dnames.append(fname+' '+' '.join(line.split()[:2]))
53                nBanks += 1
54                continue
55            elif line[:7] == 'Monitor': continue
56            elif line [:8] == 'TIME_MAP':          #LANSCE TOF data
57                continue
58#            else:
59#                if not all(c in ' 0123456789.eE+-' for c in line[:-1]):
60#                    self.errors = 'Unexpected information in line: '+str(i+1)
61#                    self.errors += '  '+str(line)
62#                    return False
63        if nBanks:
64            if not len(self.selections):
65                self.selections = range(nBanks)
66            fp.close()
67            return True
68        self.errors = 'No BANK records found'
69        fp.close()
70        return False # no bank records
71
72    def Reader(self,filename, ParentFrame=None, **kwarg):
73        '''Read a GSAS (old formats) file of type FXY, FXYE, ESD or STD types.
74        If multiple datasets are requested, use self.repeat and buffer caching.
75        '''
76        def GetFXYEdata(File,Pos,Bank):
77            File.seek(Pos)
78            x = []
79            y = []
80            w = []
81            S = File.readline()
82            while S and S[:4] != 'BANK' and S[0] != '#':
83                vals = S.split()
84                x.append(float(vals[0])/100.)               #CW: from centidegrees to degrees
85                f = float(vals[1])
86                s = float(vals[2])
87                if f <= 0.0 or s <= 0.0:
88                    y.append(0.0)
89                    w.append(0.0)
90                else:
91                    y.append(float(vals[1]))
92                    w.append(1.0/float(vals[2])**2)
93                S = File.readline()
94            N = len(x)
95            return [np.array(x),np.array(y),np.array(w),np.zeros(N),np.zeros(N),np.zeros(N)]   
96           
97        def GetFXYdata(File,Pos,Bank):
98            File.seek(Pos)
99            x = []
100            y = []
101            w = []
102            S = File.readline()
103            while S and S[:4] != 'BANK' and S[0] != '#':
104                vals = S.split()
105                x.append(float(vals[0])/100.)               #CW: from centidegrees to degrees
106                f = float(vals[1])
107                if f > 0.0:
108                    y.append(f)
109                    w.append(1.0/f)
110                else:             
111                    y.append(0.0)
112                    w.append(0.0)
113                S = File.readline()
114            N = len(x)
115            return [np.array(x),np.array(y),np.array(w),np.zeros(N),np.zeros(N),np.zeros(N)]
116           
117        def GetESDdata(File,Pos,Bank):
118            File.seek(Pos)
119            cons = Bank.split()
120            if 'TIME_MAP' == cons[4]:
121                start = 0
122                step = 1
123            else:
124                start = float(cons[5])/100.0               #CW: from centidegrees to degrees
125                step = float(cons[6])/100.0
126            x = []
127            y = []
128            w = []
129            S = File.readline()
130            j = 0
131            while S and S[:4] != 'BANK' and S[0] != '#':
132                if 'TIME_MAP' in S:
133                    break
134                for i in range(0,80,16):
135                    if S[i:i+8] == 8*' ':
136                        break
137                    xi = start+step*j
138                    yi = sfloat(S[i:i+8])
139                    ei = sfloat(S[i+8:i+16])
140                    x.append(xi)
141                    if yi > 0.0:
142                        y.append(yi)
143                        w.append(1.0/ei**2)
144                    else:             
145                        y.append(0.0)
146                        w.append(0.0)
147                    j += 1
148                S = File.readline()
149            N = len(x)
150            if self.clockWd:
151                if len(self.clockWd) == 1:
152                    x = Tmap2TOF(self.TimeMap['1'],self.clockWd['1'])
153                else:
154                    x = Tmap2TOF(self.TimeMap[cons[5]],self.clockWd[cons[5]])
155            return [np.array(x),np.array(y),np.array(w),np.zeros(N),np.zeros(N),np.zeros(N)]
156       
157        def GetSTDdata(File,Pos,Bank):
158            File.seek(Pos)
159            cons = Bank.split()
160            Nch = int(cons[2])
161            if self.clockWd:
162                start = 0
163                step = 1
164            else:
165                start = float(cons[5])/100.0               #CW: from centidegrees to degrees
166                step = float(cons[6])/100.0                 #NB TOF 0.1*ms!
167            x = []
168            y = []
169            w = []
170            S = File.readline()
171            j = 0
172            Ecoef = np.zeros(4)
173            if 'EDS' in cons[4]:
174               Ecoef = np.fromstring(' '.join(cons[5:9]),sep=' ')
175            while S and S[:4] != 'BANK' and S[0] != '#':
176                if 'TIME_MAP' in S or '\x1a' in S:
177                    break
178                for i in range(0,80,8):
179                    if S[i:i+10] == 10*' ':
180                        break
181                    xi = start+step*j
182                    if 'EDS' in Bank:   #Energy dispersive - convert CN to EkeV
183                        xi = Ecoef[0]+(Ecoef[1]+(Ecoef[2]+Ecoef[3]*j)*j)*j
184                    ni = max(sint(S[i:i+2]),1)
185                    yi = max(sfloat(S[i+2:i+8]),0.0)
186                    if yi:
187                        vi = yi/ni
188                    else:
189                        yi = 0.0
190                        vi = 0.0
191                    j += 1
192                    if j < Nch:
193                        x.append(xi)
194                        if vi <= 0.:
195                            y.append(0.)
196                            w.append(0.)
197                        else:
198                            y.append(yi)
199                            w.append(1.0/vi)
200                S = File.readline()
201            N = len(x)
202            if self.clockWd:
203                if len(self.clockWd) == 1:
204                    x = Tmap2TOF(self.TimeMap['1'],self.clockWd['1'])
205                else:
206                    x = Tmap2TOF(self.TimeMap[cons[5]],self.clockWd[cons[5]])
207            return [np.array(x),np.array(y),np.array(w),np.zeros(N),np.zeros(N),np.zeros(N)]
208           
209        def GetALTdata(File,Pos,Bank):
210            File.seek(Pos)
211            cons = Bank.split()
212            x = []
213            y = []
214            w = []
215            S = File.readline()
216            j = 0
217            while S and S[:4] != 'BANK' and S[0] != '#':
218                for i in range(0,80,20):
219                    if S[i:i+8] == 8*' ':
220                        break
221                    xi = sfloat(S[i:i+8])/3200.
222                    if not xi:
223                        break
224                    yi = sfloat(S[i+8:i+15])/1000.
225                    ei = sfloat(S[i+15:i+21])/1000.
226                    x.append(xi)
227                    if yi > 0.0:
228                        y.append(yi)
229                        w.append(1.0/ei**2)
230                    else:             
231                        y.append(0.0)
232                        w.append(0.0)
233                    j += 1
234                S = File.readline()
235            N = len(x)
236            if self.clockWd:
237                x = Tmap2TOF(self.TimeMap[cons[5]],self.clockWd[cons[5]])
238            return [np.array(x),np.array(y),np.array(w),np.zeros(N),np.zeros(N),np.zeros(N)]
239           
240        def GetTimeMap(File,Pos,TimeMap):
241            File.seek(Pos)
242            cons = TimeMap[8:].split()
243            mapNo = cons[0]
244            if 'TIME_MAP10' in TimeMap: mapNo = '1'   #HIPD cluge!
245            Nch = int(cons[1])
246            Nrec = int(cons[2])
247            clockWd = float(cons[4])/1000.          #in mus
248            TMap = np.zeros(Nch+2,dtype=int)
249            ind = 0
250            for i in range(Nrec):
251                S = File.readline().rstrip('\n')
252                vals = S.split()
253                for val in vals:
254                    TMap[ind] = int(val)
255                    ind += 1
256            TMap = np.reshape(TMap,(-1,3))
257            TMax = TMap[-1][0]
258            Nch = TMap[-2][0]+(TMax-TMap[-2][1]+TMap[-2][2]-1)/TMap[-2][2]
259            TMap[-1] = [Nch+1,TMax,0]
260            TMap = TMap.T
261            TMap[0] -= 1
262            return TMap.T,clockWd,mapNo
263           
264        def Tmap2TOF(TMap,clockWd):
265            TOF = []
266            Tch,T,Step = TMap[0]
267            for tmap in TMap[1:]:
268                tch,t,step = tmap
269                TOF += [T+Step*(i-Tch) for i in range(Tch,tch)]
270                Tch,T,Step = tmap
271            TOF = np.array(TOF)*clockWd
272            return TOF
273
274        Banks = []
275        Pos = []
276        rdbuffer = kwarg.get('buffer')
277        title = ''
278        comments = None
279        if '2' in platform.python_version_tuple()[0]:
280            fp = open(filename,'Ur')
281        else:
282            fp = open(filename,'r',encoding='latin-1')
283        # reload previously saved values - used for multibank reads
284        if self.repeat and rdbuffer is not None:
285            Banks = rdbuffer.get('Banks')
286            Pos = rdbuffer.get('Pos')
287            self.selections = rdbuffer.get('selections')
288            comments = rdbuffer.get('comments')
289
290        # read through the file and find the beginning of each bank
291        # Save the offset (Pos), BANK line (Banks), comments for each bank
292        #
293        # This is going to need a fair amount of work to track line numbers
294        # in the input file.
295        if len(Banks) != len(Pos) or len(Banks) == 0:
296            i = -1
297            while True:
298                i += 1
299                S = fp.readline()
300                if len(S) == 0: break
301                   
302                if i==0: # first line is always a comment
303                    self.errors = 'Error reading title'
304                    title = S[:-1]
305                    comments = [[title,]]
306                    continue
307                if i==1 and S[:4].lower() == 'inst' and ':' in S:
308                    # 2nd line is instrument parameter file (optional)
309                    self.errors = 'Error reading instrument parameter filename'
310                    self.instparm = S.split(':')[1].strip('[]').strip()
311                    continue
312                if S[0] == '#': # allow comments anywhere in the file
313                    # comments in fact should only preceed BANK lines
314                    comments[-1].append(S[:-1])
315                    continue
316                if S[:4] == 'BANK':
317                    self.errors = 'Error reading bank:'
318                    self.errors += '  '+str(S)
319                    comments.append([title,])
320                    Banks.append(S)
321                    Pos.append(fp.tell())
322                if S[:8] == 'TIME_MAP':     #assumes one time map; HIPPO has multiple time maps
323                    if len(Banks) == 0:
324                        self.errors = 'Error reading time map before any bank lines'
325                    else:
326                        self.errors = 'Error reading time map after bank:\n  '+str(Banks[-1])
327                    timemap,clockwd,mapNo = GetTimeMap(fp,fp.tell(),S)
328                    self.TimeMap[mapNo] = timemap
329                    self.clockWd[mapNo] = clockwd
330                   
331
332        # Now select the bank to read
333        if not Banks: # use of ContentsValidator should prevent this error
334            print (self.formatName+' scan error: no BANK records')
335            selblk = None # no block to choose
336            self.errors = 'No BANK records found (strange!)'
337            return False
338        elif len(Banks) == 1: # only one Bank, don't ask
339            selblk = 0
340        elif self.repeat and self.selections is not None:
341            # we were called to repeat the read
342            #print 'debug: repeat #',self.repeatcount,'selection',self.selections[self.repeatcount]
343            selblk = self.selections[self.repeatcount]
344            self.repeatcount += 1
345            if self.repeatcount >= len(self.selections): self.repeat = False
346        else:                       # choose from options
347            selblk = self.selections[0] # select first in list
348            if len(self.selections) > 1: # prepare to loop through again
349                self.repeat = True
350                self.repeatcount = 1
351                if rdbuffer is not None:
352                    rdbuffer['Banks'] = Banks
353                    rdbuffer['Pos'] = Pos
354                    rdbuffer['selections'] = self.selections
355                    rdbuffer['comments'] = comments
356
357        # got a selection, now read it
358        Bank = Banks[selblk]
359        try:
360            bnkNo = int(Bank.split()[1])
361        except ValueError:
362            bnkNo = 1
363        if 'FXYE' in Bank:
364            self.errors = 'Error reading FXYE data in Bank\n  '+Banks[selblk]
365            self.powderdata = GetFXYEdata(fp,Pos[selblk],Banks[selblk])
366        elif 'FXY' in Bank:
367            self.errors = 'Error reading FXY data in Bank\n  '+Banks[selblk]
368            self.powderdata = GetFXYdata(fp,Pos[selblk],Banks[selblk])
369        elif 'ESD' in Bank:
370            self.errors = 'Error reading ESD data in Bank\n  '+Banks[selblk]
371            self.powderdata = GetESDdata(fp,Pos[selblk],Banks[selblk])
372        elif 'STD' in Bank:
373            self.errors = 'Error reading STD data in Bank\n  '+Banks[selblk]
374            self.powderdata = GetSTDdata(fp,Pos[selblk],Banks[selblk])
375        elif 'ALT' in Bank:
376            self.errors = 'Error reading ALT data in Bank\n  '+Banks[selblk]
377            self.powderdata = GetALTdata(fp,Pos[selblk],Banks[selblk])
378        else:
379            self.errors = 'Error reading STD data in Bank\n  '+Banks[selblk]
380            self.powderdata = GetSTDdata(fp,Pos[selblk],Banks[selblk])
381
382        self.errors = 'Error processing information after read complete'
383        if comments is not None:
384            self.comments = comments[selblk]
385        self.powderentry[0] = filename
386        self.powderentry[1] = Pos # position offset (never used, I hope)
387        self.powderentry[2] = bnkNo #selblk+1 # bank number
388        self.idstring = ospath.basename(filename) + ' Bank '+str(bnkNo) #selblk+1)
389        self.numbanks=len(Banks)
390        # scan comments for temperature & radius
391        Temperature = 300.
392        for S in self.comments:
393            if 'Temp' in S.split('=')[0]:
394                try:
395                    Temperature = float(S.split('=')[1])
396                except:
397                    pass
398            elif 'Gonio' in S.split('=')[0]:
399                try:
400                    self.Sample['Gonio. radius'] = float(S.split('=')[1])
401                except:
402                    pass
403            elif 'Omega' in S.split('=')[0] or 'Theta' in S.split('=')[0]:  #HIPD weirdness
404                try:
405                    self.Sample['Omega'] = float(S.split('=')[1])
406                except:
407                    pass
408            elif 'Chi' in S.split('=')[0]:
409                try:
410                    self.Sample['Chi'] = float(S.split('=')[1])
411                except:
412                    pass                   
413            elif 'Phi' in S.split('=')[0]:
414                try:
415                    self.Sample['Phi'] = float(S.split('=')[1])
416                except:
417                    pass                   
418        self.Sample['Temperature'] = Temperature
419        fp.close()
420        return True       
421
422def sfloat(S):
423    'convert a string to a float, treating an all-blank string as zero'
424    if S.strip():
425        return float(S)
426    else:
427        return 0.0
428
429def sint(S):
430    'convert a string to an integer, treating an all-blank string as zero'
431    if S.strip():
432        return int(S)
433    else:
434        return 0
435
Note: See TracBrowser for help on using the repository browser.