Coverage for /Users/Newville/Codes/xraylarch/larch/io/gse_escan.py: 5%
680 statements
« prev ^ index » next coverage.py v7.3.2, created at 2023-11-09 10:08 -0600
« prev ^ index » next coverage.py v7.3.2, created at 2023-11-09 10:08 -0600
1#!/usr/bin/env python
3import os
4import sys
5import time
6import gc
8import numpy
9from larch import Group
10from larch.utils import isotime
12def _cleanfile(x):
13 for o in ' ./?(){}[]",&%^#@$': x = x.replace(o,'_')
14 return x
16class EscanData:
17 """ Epics Scan Data """
18 mode_names = ('2d', 'epics scan',
19 'user titles', 'pv list',
20 '-----','=====','n_points',
21 'scan began at', 'scan ended at',
22 'column labels', 'scan regions','data')
24 def __init__(self, fname=None, bad=None, **args):
25 self.path = suffix = fname
26 if fname is not None:
27 pref, suffix = os.path.split(fname)
28 self.filename = suffix
29 self.bad_channels = bad
30 self.clear_data()
32 self.progress = None
33 self.message = self.message_printer
35 for k in args.keys():
36 if (k == 'progress'): self.progress = args[k]
37 if (k == 'message'): self.message = args[k]
39 if self.path not in ('',None):
40 self.status = self.read_data_file(fname=self.path)
42 def clear_data(self):
43 self.xdesc = ''
44 self.ydesc = ''
45 self.xaddr = ''
46 self.yaddr = ''
47 self.start_time = ''
48 self.stop_time = ''
49 self.dimension = 1
50 self.scan_prefix = ''
51 self.user_titles = []
52 self.scan_regions= []
54 self.env_desc = []
55 self.env_addr = []
56 self.env_val = []
57 self.pos = []
58 self.det = []
59 self.data = []
60 self.pos_desc = []
61 self.pos_addr = []
62 self.det_desc = []
63 self.det_addr = []
64 self.det_mcas = []
65 self.sums = []
66 self.sums_names = []
67 self.sums_list = []
68 self.dt_factor = None
70 self.has_fullxrf = False
71 self.xrf_data = []
72 self.xrf_sum = []
73 self.xrf_energies = []
74 self.xrf_header = ''
75 self.xrf_dict = {}
76 self.xrf_merge = None
77 self.xrf_merge_corr = None
78 self.roi_names = []
79 self.roi_llim = []
80 self.roi_hlim = []
82 self.x = numpy.array(0)
83 self.y = numpy.array(0)
84 if self.bad_channels is None:
85 self.bad_channels = []
88 def message_printer(self,s,val):
89 sys.stdout.write("%s\n" % val)
91 def my_progress(self,val):
92 sys.stdout.write("%f .. " % val)
93 sys.stdout.flush()
95 def filetype(self,fname=None):
96 """ checks file type of file, returning:
97 'escan' for Epics Scan
98 None otherwise
99 """
100 try:
101 u = open(fname,'r')
102 t = u.readline()
103 u.close()
104 if 'Epics Scan' in t: return 'escan'
106 except IOError:
107 pass
109 return None
111 def get_map(self,name=None,norm=None):
112 return self.get_data(name=name,norm=norm)
114 def get_data(self, name=None, norm=None, correct=True):
115 """return data array by name"""
116 dat = self._getarray(name, correct=correct)
117 if norm is not None and dat is not None:
118 norm = self._getarray(norm, correct=True)
119 dat = dat/norm
120 return dat
122 def match_detector_name(self, sname, strict=False):
123 """return index in self.det_desc most closely matching supplied string"""
124 s = sname.lower()
125 sw = s.split()
126 dnames = [i.lower() for i in self.det_desc]
128 # look for exact match
129 for nam in dnames:
130 if s == nam: return dnames.index(nam)
132 # look for inexact match 1: compare 1st words
133 for nam in dnames:
134 sx = nam.split()
135 if sw[0] == sx[0]:
136 return dnames.index(i)
138 # check for 1st word in the det name
139 if not strict:
140 for dnam in dnames:
141 j = dnam.find(sw[0])
142 if (j >= 0): return dnames.index(i)
143 # found no matches
144 return -1
146 def ShowProgress(self,val,row=-1):
147 if (self.progress != None):
148 self.progress(val)
149 elif (row>-1):
150 print( " %3i " % (row),)
151 if (row %10 == 0): print("")
153 def ShowMessage(self,val,state='state'):
154 if (self.message != None):
155 self.message(state,val)
157 def PrintMessage(self,s):
158 sys.stdout.write(s)
159 sys.stdout.flush()
161 def read_data_file(self,fname=None):
162 """generic data file reader"""
163 if fname is None:
164 fname = self.path
165 read_ascii = True
166 if read_ascii:
167 retval = self.read_ascii(fname=fname)
168 if retval is not None:
169 msg = "problem reading file %s" % fname
170 self.ShowMessage(msg)
171 return retval
173 def _getarray(self, name=None, correct=True):
174 i = None
175 arr = None
176 name = name.lower()
177 for ip, pname in enumerate(self.pos_desc):
178 if name.lower() == pname.lower():
179 return self.pos[ip]
180 if 'mca' in name:
181 name = name.replace('(', '').replace(')', '')
182 words = name.replace('mca', '@@').split('@@', 2)
183 name = words[0].strip().lower()
184 mca = int(words[1])
185 if len(self.det_mcas) < 1:
186 self.det_mcas = [None for i in self.det_desc]
187 for idet, addr in enumerate(self.det_addr):
188 a = addr.lower().split('.')[0]
189 if 'mca' in a:
190 w = a.split('mca')[1]
191 self.det_mcas[idet] = int(w)
192 for idet, nam in enumerate(self.det_desc):
193 name1 = nam.strip().lower()
194 # print( idet, name, name1, mca, self.det_mcas[idet])
195 if name == name1 and mca == self.det_mcas[idet]:
196 i = idet
197 break
198 # print('GETARRAY with mca in name: ', name, mca, ' --> ', i)
199 arr = self.det
200 if correct: arr = self.det_corr
201 elif name in self.sums_names:
202 i = self.sums_names.index(name)
203 arr = self.sums
204 if correct: arr = self.sums_corr
205 else:
206 i = self.match_detector_name(name)
207 arr = self.det
208 if correct: arr = self.det_corr
209 if i is not None:
210 return arr[i]
212 return None
215 def _open_ascii(self,fname=None):
216 """open ascii file, return lines after some checking"""
217 if fname is None:
218 fname = self.path
219 if fname is None: return None
221 self.ShowProgress(1.0)
222 # self.ShowMessage("opening file %s ... " % fname)
223 if True: # try:
224 f = open(fname,'r')
225 lines = f.readlines()
226 lines.reverse()
227 f.close()
228 # except:
229 # self.ShowMessage("ERROR: general error reading file %s " % fname)
230 # return None
232 line1 = lines.pop()
233 if 'Epics Scan' not in line1:
234 self.ShowMessage("Error: %s is not an Epics Scan file" % fname)
235 return None
236 return lines
238 def _getline(self,lines):
239 "return mode keyword,"
240 inp = lines.pop()
241 is_comment = True
242 mode = None
243 if len(inp) > 2:
244 is_comment = inp[0] in (';','#')
245 s = inp[1:].strip().lower()
246 for j in self.mode_names:
247 if s.startswith(j):
248 mode = j
249 break
250 if mode is None and not is_comment:
251 w1 = inp.strip().split()[0]
252 try:
253 x = float(w1)
254 mode = 'data'
255 except ValueError:
256 pass
257 return (mode, inp)
260 def _make_arrays(self, tmp_dat, col_legend, col_details):
261 # convert tmp_dat to numpy 2d array
262 dat = numpy.array(tmp_dat).transpose()
263 # make raw position and detector data, using column labels
264 npos = len( [i for i in col_legend if i.lower().startswith('p')])
265 ndet = len( [i for i in col_legend if i.lower().startswith('d')])
267 self.pos = dat[0:npos,:]
268 self.det = dat[npos:,:]
270 # parse detector labels
271 for i in col_details:
272 try:
273 key,detail = i.split('=')
274 except:
275 break
276 label,pvname = [i.strip() for i in detail.split('-->')]
277 label = label[1:-1].lower()
278 if key.startswith('P'):
279 self.pos_desc.append(label)
280 self.pos_addr.append(pvname)
281 else:
282 self.det_desc.append(label)
283 self.det_addr.append(pvname)
286 # make sums of detectors with same name and isolate icr / ocr
287 self.sums = []
288 self.sums_names = []
289 self.sums_list = []
290 self.dt_factor = None
291 self.correct_deadtime = False
292 icr, ocr = [], []
293 detpvs = []
294 sum_name = None
295 isum = -1
297 for i, det in enumerate(self.det_desc):
298 thisname, thispv = det, self.det_addr[i]
299 # avoid pvs listed more than once
300 if thispv in detpvs:
301 continue
302 else:
303 detpvs.append(thispv)
304 if 'mca' in thisname and ':' in thisname:
305 thisname = thisname.replace('mca','').split(':')[1].strip()
306 if thisname != sum_name:
307 sum_name = thisname
308 self.sums_names.append(sum_name)
309 isum = isum + 1
310 self.sums.append( self.det[i][:] )
311 o = [i]
312 self.sums_list.append(o)
313 else:
314 if i not in self.bad_channels:
315 self.sums[isum] = self.sums[isum] + self.det[i][:]
316 o.append(i)
317 self.sums_list[isum] = o
318 if 'inputcountrate' in thisname.lower():
319 icr.append(self.det[i][:])
320 self.correct_deadtime = True
321 if 'outputcountrate' in thisname.lower(): ocr.append(self.det[i][:])
323 self.sums = numpy.array(self.sums)
325 # print( '_make arrays: ICR OCR ', len(icr), len(icr[0]))
326 # if icr/ocr data is included, pop them from
327 # the detector lists.
329 self.dt_factor = None
330 if len(icr)>0 and len(ocr)==len(icr):
331 try:
332 self.dt_factor = numpy.array(icr)/numpy.array(ocr)
333 self.dt_factor[numpy.where(numpy.isnan(self.dt_factor))] = 1.0
334 except:
335 self.dt_factor = numpy.ones(len(icr))
337 n_icr = self.dt_factor.shape[0]
338 self.det = self.det[0:-2*n_icr]
339 self.sums = self.sums[0:-2*n_icr]
340 self.sums_list = self.sums_list[:-2*n_icr]
341 self.sums_names = self.sums_names[:-2*n_icr]
342 self.det_desc = self.det_desc[:-2*n_icr]
343 self.det_addr = self.det_addr[:-2*n_icr]
344 self.correct_deadtime = True
346 if self.dimension == 2:
347 ny = len(self.y)
348 nx = int(len(tmp_dat)/ny)
349 print( '2D ', len(self.y), nx, len(tmp_dat))
351 self.det.shape = (self.det.shape[0], ny, nx)
352 self.pos.shape = (self.pos.shape[0], ny, nx)
353 self.sums.shape = (self.sums.shape[0], ny, nx)
354 if self.dt_factor is not None:
355 self.dt_factor.shape = (self.dt_factor.shape[0], ny, nx)
357 self.x = self.pos[0,0,:]
358 else:
359 self.x = self.pos[0]
360 nx = len(self.x)
361 self.y = []
363 self.data = numpy.vstack((self.pos, self.sums))
364 tnsums = [len(i) for i in self.sums_list]
365 tnsums.sort()
366 nsums = tnsums[-1]
367 for s in self.sums_list:
368 while len(s) < nsums: s.append(-1)
370 # finally, icr/ocr corrected sums
371 self.det_corr = self.det[:]*1.0
372 self.sums_corr = self.sums[:]*1.0
374 if self.correct_deadtime:
375 idet = -1
376 nmca = -1
377 for label, pvname in zip(self.det_desc,self.det_addr):
378 idet = idet + 1
379 if 'mca' in pvname:
380 nmca = int(pvname.split('mca')[1].split('.')[0]) -1
381 if idet in self.bad_channels:
382 self.det_corr[idet,:] *= 0
383 else:
384 self.det_corr[idet,:] *= self.dt_factor[nmca,:]
386 isum = -1
387 for sumlist in self.sums_list:
388 isum = isum + 1
389 if isinstance(sumlist, (list,tuple)):
390 self.sums_corr[isum] = self.det_corr[sumlist[0]]
391 for i in sumlist[1:]:
392 if i > 0 and i not in self.bad_channels:
393 self.sums_corr[isum] += self.det_corr[i]
394 else:
395 self.sums_corr[isum] = self.det_corr[sumlist]
396 return
398 def read_ascii(self,fname=None):
399 """read ascii data file"""
400 lines = self._open_ascii(fname=fname)
401 if lines is None: return -1
403 maxlines = len(lines)
405 iline = 1
406 ndata_points = None
407 tmp_dat = []
408 tmp_y = []
409 col_details = []
410 col_legend = None
411 ntotal_at_2d = []
412 ny_counter = 0
413 mode = None
414 while lines:
415 key, raw = self._getline(lines)
416 iline= iline+1
417 if key is not None and key != mode:
418 mode = key
420 if (len(raw) < 3): continue
421 self.ShowProgress( iline* 100.0 /(maxlines+1))
423 if mode == '2d':
424 self.dimension = 2
425 sx = raw.split()
426 yval = float(sx[2])
427 tmp_y.append(yval)
428 self.yaddr = sx[1].strip()
429 if self.yaddr.endswith(':'): self.yaddr = self.yaddr[:-1]
430 mode = None
431 if len(tmp_dat)>0:
432 ntotal_at_2d.append(len(tmp_dat))
433 elif mode == 'epics scan': # real numeric column data
434 print( 'Warning: file appears to have a second scan appended!')
435 break
437 elif mode == 'data': # real numeric column data
438 tmp_dat.append(numpy.array([float(i) for i in raw.split()]))
440 elif mode == '-----':
441 if col_legend is None:
442 col_legend = lines.pop()[1:].strip().split()
444 elif mode in ( '=====', 'n_points'):
445 pass
447 elif mode == 'user titles':
448 self.user_titles.append(raw[1:].strip())
450 elif mode == 'pv list':
451 str = raw[1:].strip().replace('not connected',' = not connected')
452 if str.lower().startswith(mode): continue
453 desc = str
454 addr = ''
455 val = 'unknown'
456 try:
457 x = str.split('=')
458 desc = x[0].replace('\t','').strip()
459 val = x[1].strip()
460 if '(' in desc and desc.endswith(')'):
461 n = desc.rfind('(')
462 addr = desc[n+1:-1]
463 desc = desc[:n].rstrip()
464 except:
465 pass
466 self.env_addr.append(addr)
467 self.env_desc.append(desc)
468 self.env_val.append(val)
470 elif mode == 'scan regions':
471 self.scan_regions.append(raw[1:].strip())
473 elif mode == 'scan ended at':
474 self.stop_time = raw[20:].strip()
476 elif mode == 'scan began at':
477 self.start_time = raw[20:].strip()
479 elif mode == 'column labels':
480 col_details.append(raw[1:].strip())
482 elif mode is None:
483 sx = [i.strip() for i in raw[1:].split('=')]
484 if len(sx)>1:
485 if sx[0] == 'scan prefix':
486 self.scan_prefix = sx[1]
487 if sx[0] == 'scan dimension':
488 self.dimension = int(float(sx[1]))
490 else:
491 print( 'UNKOWN MODE = ',mode, raw[:20])
493 del lines
495 try:
496 col_details.pop(0)
498 except IndexError:
499 print( 'Empty Scan File')
500 return -2
502 if len(self.user_titles) > 1: self.user_titles.pop(0)
503 if len(self.scan_regions) > 1: self.scan_regions.pop(0)
505 # check that 2d maps are of consistent size
506 if self.dimension == 2:
507 ntotal_at_2d.append(len(tmp_dat))
508 np_row0 = ntotal_at_2d[0]
509 nrows = len(ntotal_at_2d)
510 npts = len(tmp_dat)
511 if npts != np_row0 * nrows:
512 for i,n in enumerate(ntotal_at_2d):
513 if n == np_row0*(i+1):
514 nrows,npts_total = i+1,n
516 if len(tmp_y) > nrows or len(tmp_dat)> npts_total:
517 print( 'Warning: Some trailing data may be lost!')
518 tmp_y = tmp_y[:nrows]
519 tmp_dat = tmp_dat[:npts_total]
520 #
521 self.y = numpy.array(tmp_y)
522 # done reading file
523 self._make_arrays(tmp_dat,col_legend,col_details)
524 tmp_dat = None
526 self.xaddr = self.pos_addr[0].strip()
528 for addr,desc in zip(self.env_addr,self.env_desc):
529 if self.xaddr == addr: self.xdesc = desc
530 if self.yaddr == addr: self.ydesc = desc
532 self.has_fullxrf = False
533 if os.path.exists("%s.fullxrf" %fname):
534 self.read_fullxrf("%s.fullxrf" %fname, len(self.x), len(self.y))
536 def read_fullxrf(self,xrfname, n_xin, n_yin):
537 inpf = open(xrfname,'r')
539 atime = os.stat(xrfname)[8]
541 prefix = os.path.splitext(xrfname)[0]
542 print('Reading Full XRF spectra from %s' % xrfname)
544 first_line = inpf.readline()
545 if not first_line.startswith('; MCA Spectra'):
546 print('Warning: %s is not a QuadXRF File' % xrffile)
547 inpf.close()
548 return
550 self.has_fullxrf = True
551 isHeader= True
552 nheader = 0
553 header = {'CAL_OFFSET':None,'CAL_SLOPE':None,'CAL_QUAD':None}
554 rois = []
556 n_energies = 2048
558 while isHeader:
559 line = inpf.readline()
560 nheader = nheader + 1
561 isHeader = line.startswith(';') and not line.startswith(';----')
562 words = line[2:-1].split(':')
563 if words[0] in header.keys():
564 header[words[0]] = [float(i) for i in words[1].split()]
565 elif words[0].startswith('ROI'):
566 roinum = int(words[0][3:])
567 rois.append((words[1].strip(),int(words[2]),int(words[3])))
569 # end of header: read one last line
570 line = inpf.readline()
571 nelem = self.nelem = len(header['CAL_OFFSET'])
573 nheader = nheader + 1
574 # print('==rois==' , len(rois), len(rois)/nelem, nelem)
576 allrois = []
577 nrois = len(rois)/nelem
579 for i in range(nrois):
580 tmp = [rois[i+j*nrois] for j in range(nelem)]
581 allrois.append( tuple(tmp) )
583 for i in range(nrois):
584 nam = []
585 lo = []
586 hi = []
587 for j in range(nelem):
588 r = rois[i+j*nrois]
589 nam.append(r[0])
590 lo.append(r[1])
591 hi.append(r[2])
592 self.roi_names.append(nam)
593 self.roi_llim.append(lo)
594 self.roi_hlim.append(hi)
596 roi_template ="""ROI_%i_LEFT: %i %i %i %i
597ROI_%i_RIGHT: %i %i %i %i
598ROI_%i_LABEL: %s & %s & %s & %s & """
600 rout = []
601 for i in range(nrois):
602 vals = [i] + self.roi_llim[i] + [i] + self.roi_hlim[i] + [i] + self.roi_names[i]
603 rout.append(roi_template % tuple(vals))
605 xrf_header= """VERSION: 3.1
606ELEMENTS: %i
607DATE: %s
608CHANNELS: %i
609ROIS: %i %i %i %i
610REAL_TIME: 1.0 1.0 1.0 1.0
611LIVE_TIME: 1.0 1.0 1.0 1.0
612CAL_OFFSET: %15.8e %15.8e %15.8e %15.8e
613CAL_SLOPE: %15.8e %15.8e %15.8e %15.8e
614CAL_QUAD: %15.8e %15.8e %15.8e %15.8e
615TWO_THETA: 10.0000000 10.0000000 10.0000000 10.0000000"""
618 hout = [nelem, time.ctime(atime),n_energies, nrois, nrois, nrois, nrois]
619 hout.extend( header['CAL_OFFSET'])
620 hout.extend( header['CAL_SLOPE'])
621 hout.extend( header['CAL_QUAD'])
623 obuff ="%s\n%s" % (xrf_header % tuple(hout), '\n'.join(rout))
624 rois = []
625 allrois = []
626 self.xrf_header = obuff
628 # dir = prefix
629 self.xrf_energies = []
630 x_en = numpy.arange(n_energies)*1.0
631 for i in range(nelem):
632 off = header['CAL_OFFSET'][i]
633 slope = header['CAL_SLOPE'][i]
634 quad = header['CAL_QUAD'][i]
635 self.xrf_energies.append(off + x_en * (slope + x_en * quad))
637 self.xrf_energies = numpy.array(self.xrf_energies)
639 self.xrf_dict = {}
640 processing = True
641 iyold = 1
642 ix = 0
643 iy = 0
644 # lines = inpf.readlines()
646 progress_save = self.progress
647 self.progress = self.my_progress
648 # slow part: ascii text to numpy array
649 for line in inpf:# enumerate(lines):
650 raw = numpy.fromstring(line[:-1],sep=' ')
651 ix = raw[0]
652 iy = raw[1]
653 dat = raw[2:]
655 if iy != iyold:
656 iyold = iy
657 if iy>1: self.PrintMessage('. ')
658 self.xrf_dict['%i/%i' % (ix,iy)] = dat
660 inpf.close()
662 xrf_shape = (n_xin, nelem, n_energies)
663 if self.dimension == 2:
664 xrf_shape = (n_yin, n_xin, nelem, n_energies)
665 # print( 'xrf_shape ', xrf_shape)
666 self.xrf_data = -1*numpy.ones(xrf_shape)
667 xrf_dt_factor = self.dt_factor * 1.0
669 if self.dimension == 2:
670 xrf_dt_factor = xrf_dt_factor.transpose((1,2,0))[:,:,:,numpy.newaxis]
671 for iy in range(n_yin):
672 for ix in range(n_xin):
673 key = '%i/%i' % (ix+1,iy+1)
674 if key in self.xrf_dict:
675 d = numpy.array(self.xrf_dict[key])
676 d.shape = (nelem,n_energies)
677 self.xrf_data[iy,ix,:,:] = d
678 else:
679 xrf_dt_factor = xrf_dt_factor.transpose((1,0))[:,:,numpy.newaxis]
680 for ix in range(n_xin):
681 key = '%i/%i' % (ix+1,iy)
682 d = numpy.array(self.xrf_dict[key])
683 d.shape = (nelem, n_energies)
684 self.xrf_data[ix,:,:] = d
686 self.xrf_corr = self.xrf_data * xrf_dt_factor
688 # merge XRF data
690 en_merge = self.xrf_energies[0]
691 if self.dimension == 2:
692 self.xrf_merge = self.xrf_data[:,:,0,:]*1.0
693 self.xrf_merge_corr = self.xrf_corr[:,:,0,:]*1.0
694 self.PrintMessage('\n')
695 for iy in range(n_yin):
696 self.PrintMessage('. ')
697 for ix in range(n_xin):
698 sum_r = self.xrf_merge[iy,ix,:]*1.0
699 sum_c = self.xrf_merge_corr[iy,ix,:]*1.0
700 for idet in range(1,nelem):
701 en = self.xrf_energies[idet]
702 dat_r = self.xrf_data[iy,ix,idet,:]
703 dat_c = self.xrf_corr[iy,ix,idet,:]
704 sum_r += numpy.interp(en_merge, en, dat_r)
705 sum_c += numpy.interp(en_merge, en, dat_c)
706 self.xrf_merge[iy,ix,:] = sum_r
707 self.xrf_merge_corr[iy,ix,:] = sum_c
709 else:
710 self.xrf_merge = self.xrf_data[:,0,:]*1.0
711 self.xrf_merge_corr = self.xrf_corr[:,0,:]*1.0
713 for ix in range(n_xin):
714 sum_r = self.xrf_merge[ix,:]*1.0
715 sum_c = self.xrf_merge_corr[ix,:]*1.0
716 for idet in range(1,nelem):
717 en = self.xrf_energies[idet]
718 dat_r = self.xrf_data[ix,idet,:]
719 dat_c = self.xrf_corr[ix,idet,:]
720 sum_r += numpy.interp(en_merge, en, dat_r)
721 sum_c += numpy.interp(en_merge, en, dat_c)
722 self.xrf_merge[ix,:] = sum_r
723 self.xrf_merge_corr[ix,:] = sum_c
725 self.progress = progress_save
726 inpf.close()
727 self.xrf_dict = None
730 def save_sums_ascii(self,fname=None, correct=True,extension='dat'):
731 if fname is None:
732 fname = self.path
734 map = None
735 correct = correct and hasattr(self,'det_corr')
737 outf = _cleanfile(fname)
739 fout = open("%s.%s" % (outf,extension),'w')
740 fout.write("# ASCII data from %s\n" % self.filename)
741 fout.write("# x positioner %s = %s\n" % (self.xaddr,self.xdesc))
742 if self.dimension==2:
743 fout.write("# y positioner %s = %s\n" % (self.yaddr,self.ydesc))
745 fout.write("# Dead Time Correction applied: %s\n" % correct)
746 fout.write("#-----------------------------------------\n")
748 labels = [self.xdesc]
749 if self.dimension == 2:
750 ydesc = self.ydesc
751 if ydesc in ('',None): ydesc = 'Y'
752 labels.append(ydesc)
754 labels.extend(self.sums_names)
755 labels = ["%5s" % _cleanfile(l) for l in labels]
756 olabel = ' '.join(labels)
757 fout.write("# %s\n" % olabel)
759 sums = self.sums
760 if correct: sums = self.sums_corr
763 if self.dimension ==1:
764 for i,x in enumerate(self.x):
765 o = ["%10.5f" % x]
766 o.extend(["%12g" % s for s in sums[:,i]])
767 fout.write(" %s\n" % " ".join(o) )
769 else:
770 for i,x in enumerate(self.x):
771 for j,y in enumerate(self.y):
772 o = [" %10.5f" % x, " %10.5f" % y]
773 o.extend(["%12g" % s for s in sums[:,j,i]])
774 fout.write(" %s\n" % " ".join(o))
776 fout.close()
779def gsescan_group(fname, bad=None, **kws):
780 """simple mapping of EscanData file to larch groups"""
781 escan = EscanData(fname, bad=bad)
782 if escan.status is not None:
783 raise ValueError('Not a valid Escan Data file')
785 group = Group()
786 group.__name__ ='GSE Escan Data file %s' % fname
787 for key, val in escan.__dict__.items():
788 if not key.startswith('_'):
789 setattr(group, key, val)
791 group.array_labels = group.pos_desc + group.sums_names
792 group.get_data = escan.get_data
793 return group
797GSE_header_IDE= ['# XDI/1.0 GSE/1.0',
798 '# Beamline.name: 13-ID-E, GSECARS',
799 '# Monochromator.name: Si 111, LN2 Cooled',
800 '# Monochromator.dspacing: 3.13477',
801 '# Facility.name: APS',
802 '# Facility.xray_source: 3.6 cm undulator',
803 '# Detectors.i0: 20cm ion chamber, He',
804 '# Detectors.ifluor: Si SDD Vortex ME-4, XIA xMAP, 4 elements',
805 '# Column.1: energy eV',
806 '# Column.2: mufluor',
807 '# Column.3: i0',
808 '# Column.4: ifluor (corrected for deadtime)',
809 '# Column.5: ifluor_raw (not corrected) ' ]
811GSE_header_BMD = ['# XDI/1.0 GSE/1.0',
812 '# Beamline.name: 13-BM-D, GSECARS',
813 '# Monochromator.name: Si 111, water cooled ',
814 '# Monochromator.dspacing: 3.13477',
815 '# Facility.name: APS',
816 '# Facility.xray_source: bending magnet',
817 '# Detectors.i0: 10cm ion chamber, N2',
818 '# Detectors.ifluor: Ge SSD detector, XIA xMAP, 12 elements',
819 '# Column.1: energy eV',
820 '# Column.2: mufluor',
821 '# Column.3: i0',
822 '# Column.4: ifluor (corrected for deadtime)',
823 '# Column.5: ifluor_raw (not corrected) ' ]
827def gsescan_deadtime_correct(fname, channelname, subdir='DT_Corrected',
828 bad=None):
829 """convert GSE ESCAN fluorescence XAFS scans to dead time corrected files"""
830 try:
831 sg = gsescan_group(fname, bad=bad)
832 except:
833 print('%s is not a valid ESCAN file' % fname)
834 return
835 energy = sg.x
836 i0 = sg.get_data('i0')
838 ix = -1
839 channelname = channelname.lower()
840 for ich, ch in enumerate(sg.sums_names):
841 if ch.lower().startswith(channelname):
842 ix = ich
843 break
844 if ix < 0:
845 print('Cannot find Channel %s in file %s ' % (channelname, fname))
846 return
848 chans = list(sg.sums_list[ix])
849 chans.pop()
850 chans = numpy.array(chans)
851 dchans = chans - chans[0]
853 fl_raw = sg.det[chans].sum(axis=0)
854 fl_corr = (sg.dt_factor[dchans] * sg.det[chans]).sum(axis=0)
855 mufluor = fl_corr / i0
856 sg.i0 = i0
857 sg.fl_raw = fl_raw
858 sg.fl_corr = fl_corr
859 sg.mufluor = mufluor
860 sg.energy = energy
862 npts = len(energy)
863 header = GSE_header_IDE[:]
864 if 'BM' in sg.scan_prefix:
865 header = GSE_header_BMD[:]
867 i1, iref = None, None
868 ncol, lref = 6, 'iref'
869 labels = [l.lower() for l in sg.sums_names]
870 if 'i1' in labels:
871 i1 = sg.get_data('i1')
872 if 'iref' in labels:
873 iref = sg.get_data('iref')
874 elif 'i2' in labels:
875 iref = sg.get_data('i2')
877 if i1 is not None:
878 header.append('# Column.%i: itrans ' % ncol)
879 ncol += 1
880 if iref is not None:
881 header.append('# Column.%i: irefer ' % ncol)
882 ncol += 1
883 buff = [l.strip() for l in header]
884 buff.append("# Scan.start_time: %s" %
885 isotime(os.stat(fname).st_ctime), with_tzone=True)
887 thead = ["# ///",
888 "# summed %s fluorescence data from %s" % (channelname, fname),
889 "# Dead-time correction applied",
890 "#---------------------------------"]
892 arrlabel = "# energy mufluor i0 fluor_corr fluor_raw"
893 if i1 is not None:
894 arrlabel = "%s itrans" % arrlabel
896 if iref is not None:
897 arrlabel = "%s irefer" % arrlabel
899 thead.append(arrlabel)
900 buff.extend(thead)
901 fmt = " %.3f %.5f %.1f %.5f %.1f"
902 for i in range(npts):
903 dline = fmt % (energy[i], mufluor[i], i0[i], fl_corr[i], fl_raw[i])
904 if i1 is not None:
905 dline = "%s %.1f" % (dline, i1[i])
906 if iref is not None:
907 dline = "%s %.1f" % (dline, iref[i])
908 buff.append(dline)
910 ofile = fname[:]
911 if ofile.startswith('..'):
912 ofile = ofile[3:]
913 ofile = ofile.replace('.', '_') + '.dat'
914 ofile = os.path.join(subdir, ofile)
915 if not os.path.exists(subdir):
916 os.mkdir(subdir)
917 try:
918 fout = open(ofile, 'w')
919 fout.write("\n".join(buff))
920 fout.close()
921 print("wrote %s, npts=%i, channel='%s'" % (ofile, npts, channelname))
922 except:
923 print("could not open / write to output file %s" % ofile)
925 return sg