Coverage for larch/io/gse_escan.py: 5%

680 statements  

« prev     ^ index     » next       coverage.py v7.6.0, created at 2024-10-16 21:04 +0000

1#!/usr/bin/env python 

2 

3import os 

4import sys 

5import time 

6import gc 

7 

8import numpy 

9from larch import Group 

10from larch.utils import isotime 

11 

12def _cleanfile(x): 

13 for o in ' ./?(){}[]",&%^#@$': x = x.replace(o,'_') 

14 return x 

15 

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') 

23 

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() 

31 

32 self.progress = None 

33 self.message = self.message_printer 

34 

35 for k in args.keys(): 

36 if (k == 'progress'): self.progress = args[k] 

37 if (k == 'message'): self.message = args[k] 

38 

39 if self.path not in ('',None): 

40 self.status = self.read_data_file(fname=self.path) 

41 

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= [] 

53 

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 

69 

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 = [] 

81 

82 self.x = numpy.array(0) 

83 self.y = numpy.array(0) 

84 if self.bad_channels is None: 

85 self.bad_channels = [] 

86 

87 

88 def message_printer(self,s,val): 

89 sys.stdout.write("%s\n" % val) 

90 

91 def my_progress(self,val): 

92 sys.stdout.write("%f .. " % val) 

93 sys.stdout.flush() 

94 

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' 

105 

106 except IOError: 

107 pass 

108 

109 return None 

110 

111 def get_map(self,name=None,norm=None): 

112 return self.get_data(name=name,norm=norm) 

113 

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 

121 

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] 

127 

128 # look for exact match 

129 for nam in dnames: 

130 if s == nam: return dnames.index(nam) 

131 

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) 

137 

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 

145 

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("") 

152 

153 def ShowMessage(self,val,state='state'): 

154 if (self.message != None): 

155 self.message(state,val) 

156 

157 def PrintMessage(self,s): 

158 sys.stdout.write(s) 

159 sys.stdout.flush() 

160 

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 

172 

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] 

211 

212 return None 

213 

214 

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 

220 

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 

231 

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 

237 

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) 

258 

259 

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')]) 

266 

267 self.pos = dat[0:npos,:] 

268 self.det = dat[npos:,:] 

269 

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) 

284 

285 

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 

296 

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][:]) 

322 

323 self.sums = numpy.array(self.sums) 

324 

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. 

328 

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)) 

336 

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 

345 

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)) 

350 

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) 

356 

357 self.x = self.pos[0,0,:] 

358 else: 

359 self.x = self.pos[0] 

360 nx = len(self.x) 

361 self.y = [] 

362 

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) 

369 

370 # finally, icr/ocr corrected sums 

371 self.det_corr = self.det[:]*1.0 

372 self.sums_corr = self.sums[:]*1.0 

373 

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,:] 

385 

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 

397 

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 

402 

403 maxlines = len(lines) 

404 

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 

419 

420 if (len(raw) < 3): continue 

421 self.ShowProgress( iline* 100.0 /(maxlines+1)) 

422 

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 

436 

437 elif mode == 'data': # real numeric column data 

438 tmp_dat.append(numpy.array([float(i) for i in raw.split()])) 

439 

440 elif mode == '-----': 

441 if col_legend is None: 

442 col_legend = lines.pop()[1:].strip().split() 

443 

444 elif mode in ( '=====', 'n_points'): 

445 pass 

446 

447 elif mode == 'user titles': 

448 self.user_titles.append(raw[1:].strip()) 

449 

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) 

469 

470 elif mode == 'scan regions': 

471 self.scan_regions.append(raw[1:].strip()) 

472 

473 elif mode == 'scan ended at': 

474 self.stop_time = raw[20:].strip() 

475 

476 elif mode == 'scan began at': 

477 self.start_time = raw[20:].strip() 

478 

479 elif mode == 'column labels': 

480 col_details.append(raw[1:].strip()) 

481 

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])) 

489 

490 else: 

491 print( 'UNKOWN MODE = ',mode, raw[:20]) 

492 

493 del lines 

494 

495 try: 

496 col_details.pop(0) 

497 

498 except IndexError: 

499 print( 'Empty Scan File') 

500 return -2 

501 

502 if len(self.user_titles) > 1: self.user_titles.pop(0) 

503 if len(self.scan_regions) > 1: self.scan_regions.pop(0) 

504 

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 

515 

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 

525 

526 self.xaddr = self.pos_addr[0].strip() 

527 

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 

531 

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)) 

535 

536 def read_fullxrf(self,xrfname, n_xin, n_yin): 

537 inpf = open(xrfname,'r') 

538 

539 atime = os.stat(xrfname)[8] 

540 

541 prefix = os.path.splitext(xrfname)[0] 

542 print('Reading Full XRF spectra from %s' % xrfname) 

543 

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 

549 

550 self.has_fullxrf = True 

551 isHeader= True 

552 nheader = 0 

553 header = {'CAL_OFFSET':None,'CAL_SLOPE':None,'CAL_QUAD':None} 

554 rois = [] 

555 

556 n_energies = 2048 

557 

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]))) 

568 

569 # end of header: read one last line 

570 line = inpf.readline() 

571 nelem = self.nelem = len(header['CAL_OFFSET']) 

572 

573 nheader = nheader + 1 

574 # print('==rois==' , len(rois), len(rois)/nelem, nelem) 

575 

576 allrois = [] 

577 nrois = len(rois)/nelem 

578 

579 for i in range(nrois): 

580 tmp = [rois[i+j*nrois] for j in range(nelem)] 

581 allrois.append( tuple(tmp) ) 

582 

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) 

595 

596 roi_template ="""ROI_%i_LEFT: %i %i %i %i 

597ROI_%i_RIGHT: %i %i %i %i 

598ROI_%i_LABEL: %s & %s & %s & %s & """ 

599 

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)) 

604 

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""" 

616 

617 

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']) 

622 

623 obuff ="%s\n%s" % (xrf_header % tuple(hout), '\n'.join(rout)) 

624 rois = [] 

625 allrois = [] 

626 self.xrf_header = obuff 

627 

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)) 

636 

637 self.xrf_energies = numpy.array(self.xrf_energies) 

638 

639 self.xrf_dict = {} 

640 processing = True 

641 iyold = 1 

642 ix = 0 

643 iy = 0 

644 # lines = inpf.readlines() 

645 

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:] 

654 

655 if iy != iyold: 

656 iyold = iy 

657 if iy>1: self.PrintMessage('. ') 

658 self.xrf_dict['%i/%i' % (ix,iy)] = dat 

659 

660 inpf.close() 

661 

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 

668 

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 

685 

686 self.xrf_corr = self.xrf_data * xrf_dt_factor 

687 

688 # merge XRF data 

689 

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 

708 

709 else: 

710 self.xrf_merge = self.xrf_data[:,0,:]*1.0 

711 self.xrf_merge_corr = self.xrf_corr[:,0,:]*1.0 

712 

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 

724 

725 self.progress = progress_save 

726 inpf.close() 

727 self.xrf_dict = None 

728 

729 

730 def save_sums_ascii(self,fname=None, correct=True,extension='dat'): 

731 if fname is None: 

732 fname = self.path 

733 

734 map = None 

735 correct = correct and hasattr(self,'det_corr') 

736 

737 outf = _cleanfile(fname) 

738 

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)) 

744 

745 fout.write("# Dead Time Correction applied: %s\n" % correct) 

746 fout.write("#-----------------------------------------\n") 

747 

748 labels = [self.xdesc] 

749 if self.dimension == 2: 

750 ydesc = self.ydesc 

751 if ydesc in ('',None): ydesc = 'Y' 

752 labels.append(ydesc) 

753 

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) 

758 

759 sums = self.sums 

760 if correct: sums = self.sums_corr 

761 

762 

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) ) 

768 

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)) 

775 

776 fout.close() 

777 

778 

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') 

784 

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) 

790 

791 group.array_labels = group.pos_desc + group.sums_names 

792 group.get_data = escan.get_data 

793 return group 

794 

795 

796 

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) ' ] 

810 

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) ' ] 

824 

825 

826 

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') 

837 

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 

847 

848 chans = list(sg.sums_list[ix]) 

849 chans.pop() 

850 chans = numpy.array(chans) 

851 dchans = chans - chans[0] 

852 

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 

861 

862 npts = len(energy) 

863 header = GSE_header_IDE[:] 

864 if 'BM' in sg.scan_prefix: 

865 header = GSE_header_BMD[:] 

866 

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') 

876 

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) 

886 

887 thead = ["# ///", 

888 "# summed %s fluorescence data from %s" % (channelname, fname), 

889 "# Dead-time correction applied", 

890 "#---------------------------------"] 

891 

892 arrlabel = "# energy mufluor i0 fluor_corr fluor_raw" 

893 if i1 is not None: 

894 arrlabel = "%s itrans" % arrlabel 

895 

896 if iref is not None: 

897 arrlabel = "%s irefer" % arrlabel 

898 

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) 

909 

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) 

924 

925 return sg