-
-
Notifications
You must be signed in to change notification settings - Fork 5
/
mmirs_qrp.py
618 lines (485 loc) · 21.1 KB
/
mmirs_qrp.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
"""
mmirs_qrp
=========
Python script that quickly reduces longslit and MOS spectra from MMT/MMIRS
"""
import sys, os
#Py3 compatibility
py_vers = sys.version_info.major
from os.path import exists
from astropy.io import ascii as asc
from astropy.io import fits
from scipy.ndimage.interpolation import shift
import numpy as np
import matplotlib.pyplot as plt
import glob
from astropy.table import Table
from astropy import log
# Mod on 07/11/2017
from astropy.stats import sigma_clip
#from ccdproc import cosmicray_median
from scipy.optimize import curve_fit
# + on 20/11/2017
from matplotlib.backends.backend_pdf import PdfPages
from pylab import subplots_adjust
# + on 12/11/2017
import astropy.units as u
# + on 17/03/2018
import logging
formatter = logging.Formatter('%(asctime)s - %(module)12s.%(funcName)20s - %(levelname)s: %(message)s')
sh = logging.StreamHandler(sys.stdout)
sh.setLevel(logging.INFO)
sh.setFormatter(formatter)
pscale = 0.2012008872545049 # arcsec/pix
bbox_props = dict(boxstyle="square,pad=0.15", fc="w", alpha=0.75, ec="none")
class mlog:
'''
Main class to log information to stdout and ASCII file
To execute:
mylog = mlog(rawdir)._get_logger()
Parameters
----------
rawdir : str
Full path for where raw files are
Returns
-------
Notes
-----
Created by Chun Ly, 17 March 2018
- Identical to logging function in MMTtools.mmirs_pipeline_taskfile
'''
def __init__(self,rawdir):
self.LOG_FILENAME = rawdir + 'mmirs_qrp.log'
self._log = self._get_logger()
def _get_logger(self):
loglevel = logging.INFO
log = logging.getLogger(self.LOG_FILENAME) # + Mod on 14/12/2017
if not getattr(log, 'handler_set', None):
log.setLevel(logging.INFO)
sh = logging.StreamHandler()
sh.setFormatter(formatter)
log.addHandler(sh)
fh = logging.FileHandler(self.LOG_FILENAME)
fh.setLevel(logging.INFO)
fh.setFormatter(formatter)
log.addHandler(fh)
log.setLevel(loglevel)
log.handler_set = True
return log
#enddef
def gauss1d(x, a0, a, x0, sigma):
return a0 + a * np.exp(-(x - x0)**2 / (2 * sigma**2))
def main(rawdir, prefix, bright=False, dither='ABApBp', flats=[],
max_restrict=False):
'''
Main function of mmirs_qrp
Parameters
----------
rawdir : str
Full path to MMIRS files
prefix : str
Prefix for files to process. Code will search for dcorr files
in rawdir + prefix + "*dcorr.fits*"
For example, if your files are <target>_longslit.????.fits, provide 'target_longslit'
bright : boolean
Indicate whether a bright star/target is in slit. If True,
code will use bright star to determine offsets to shift spectra.
If False, will use FITS header to determine dithered size
(to be implemented). Default: False
dither : str
Dither sequence type. Accepts 'ABApBp', 'ABAB' (to be implemented),
and 'ABBA' (to be implemented). Default: 'ABApBp'
flats: list
List of files or seqno for flats.
For example, flats=[1100,1101] or flats=['flat.1100','flat.1101']
If not provided, flat fielding will not be performed
NOTE: THERE ARE SOME BUGS WITH FLATFIELDING
max_restrict : boolean
Restrict search window for peak emission line when bright == True.
Default will search the entire data.
Returns
-------
Notes
-----
Created by Chun Ly, 13 October 2017
- Handle gzip files as inputs
- Change data cube dimensions
- Handle FITS overwrite
- Added dithering keyword
- Handle ABA'B' dithering to get "sky" frame
- AB subtraction for background removal
- Add difference data cube for combine
- Compute average from background-subtracted, image-shifted images
Modified by Chun Ly, 14 October 2017
- Bug in shift call. Need to specify as row,column shift values
Modified by Chun Ly, 16 October 2017
- Documentation added
Modified by Chun Ly, 19 October 2017
- Attempt CR rejection with ccdproc.cosmicray_median
Modified by Chun Ly, 20 October 2017
- Handle ABBA dithering to get "sky" frame
Modified by Chun Ly, 7 November 2017
- Use astropy.stats.sigma_clip to mask CRs
Modified by Chun Ly, 12 November 2017
- Get FITS header dither values, compute differences from first frame
- Write ASCII table with dither offsets
- Handle dithering for bright == False using FITS dither info
- Handle output ASCII table for bright == False
- Quality Assurance: Compute and plot FWHM
- Plot seqno on x-axis for FWHM plot
- Aesthetics for plots
- Require bright=True for FWHM calculations
Modified by Chun Ly, 17 November 2017
- Write npz file of arrays to expedite analysis
- Write npz file in compressed form
- Handle masked arrays
- Simplify npz to limit to just masked arrays
- Read in npz file, Use npz file for sigma_clip mask
- Bug fix: seqno handling of .gz files
Modified by Chun Ly, 18 November 2017
- Minor bug fix: NAXIS2 -> NAXIS1 and vice versa
Modified by Chun Ly, 20 November 2017
- Plot fit to PSF profiles
Modified by Chun Ly, 25 November 2017
- PSF profiles showed double peak with a lower peak that is 20% of max,
but visual inspection does not indicate double-peaked PSF.
- Unclear of the full cause, but ultimately computed FWHM in the central
200 pix without masking (masking seems to reject pixels from bright objects)
- Masking was part of the cause as it would produce a double peak
distribution
Modified by Chun Ly, 26 November 2017
- Run curve_fit to get accurate line center for shifting
- Set default shifting as integer pixels since CRs are present
in stack (may need to grow mask)
Modified by Chun Ly, 9 December 2017
- Compute transparency: Integrate flux for bright source and plot
- Normalize spectrum by exposure time for proper transparency computation
- Plotting aesthetic improvements: legend, labels
- Normalize transparency value to best, plotting aesthetics
- Plotting aesthetic improvements: different linestyle and widths, smaller
legend
- Incorporate flat fielding (still some unexpected problems)
Modified by Chun Ly, 11 December 2017
- Expand dither_tab stdout,
- Add annotation of avg/median/sigma for FWHM
- Handle unused FWHM subplots
- Minor stdout for avg/median/sigma for FWHM
- Annotation for source info in FWHM and transparency plots
Modified by Chun Ly, 18 March 2018
- Implement stdout and ASCII logging with mlog()
- Remove silent and verbose boolean keyword
Modified by Chun Ly, 27 November 2018
- Add max_restrict for restricting window to search for bright star
Modified by Chun Ly, 18 December 2018
- Do weighted stack with 1/FWHM^2
'''
mylog = mlog(rawdir)._get_logger() # + on 18/03/2018
mylog.info('Begin main ! ') # Mod on 18/03/2018
if rawdir[-1] != '/': rawdir = rawdir + '/'
dcorr_files = glob.glob(rawdir+prefix+'*dcorr.fits*')
n_files = len(dcorr_files)
if n_files == 0:
mylog.warn("!!! d-corr files not found. Using raw data !!!")
dcorr_yes = 0
raw_files = glob.glob(rawdir+prefix+'.????.fits*')
n_files = len(raw_files)
hdu0 = fits.getheader(raw_files[0], ext=1)
else:
dcorr_yes = 1
hdu0 = fits.getheader(dcorr_files[0])
naxis1, naxis2 = hdu0['NAXIS1'], hdu0['NAXIS2']
npz_file = rawdir+prefix+'.npz'
if exists(npz_file):
mylog.info('Reading : '+npz_file) # Mod on 18/03/2018
npz0 = np.load(npz_file)
d_cube0 = np.zeros((n_files, naxis2, naxis1))
peak_val = np.zeros(n_files)
peak_val0 = np.zeros(n_files) # + on 26/11/2017
shift_cube0 = np.zeros((n_files, naxis2, naxis1))
exptime0 = np.zeros(n_files) # + on 09/12/2017
# Define and combine flat files | + on 09/12/2017
do_flat = 0
if len(flats) != 0:
if 'flat' in str(flats[0]):
flats = [flat+'_dcorr.fits' for flat in flats]
else:
flats = ['flat.%04i_dcorr.fits' % t_seq for t_seq in flats]
flat_arr = np.zeros((len(flats), naxis2, naxis1))
for ff in range(len(flats)):
t_flat, flat_hdr = fits.getdata(rawdir+flats[ff], header=True)
flat_arr[ff] = t_flat / np.max(t_flat)
flat_arr_mask = sigma_clip(flat_arr, sigma=3., iters=3, axis=0)
flat_avg = np.ma.average(flat_arr_mask, axis=0)
flat0 = flat_avg.data
bad = np.where((np.isfinite(flat0) == False) | (flat0 == 0) |
(flat0 == np.min(flat0)))
if len(bad) > 0: flat0[bad] = 1.0
out_fits_file = rawdir+prefix+'_flat.fits'
mylog.info('Writing : '+out_fits_file) # Mod on 18/03/2018
fits.writeto(out_fits_file, flat0, flat_hdr, overwrite=True)
do_flat = 1
# Set this to use curvefit to compute fractional offset
do_curvefit_center = 0 # + on 26/11/2017
# Mod on 12/11/2017
dither_az = np.zeros(n_files)
dither_el = np.zeros(n_files)
diff_cube0 = np.zeros((n_files, naxis2, naxis1))
if dither == 'ABApBp' or dither == 'ABBA':
i_off = [1, -1] * np.int(n_files/2)
if n_files % 2 == 1: i_off.append(-1) # Odd number correction
i_sky = np.arange(n_files)+np.array(i_off)
mylog.info("i_sky : " + ", ".join(["%i" % sky for sky in i_sky]))
for ii in range(n_files):
if dcorr_yes:
d_data, t_hdr = fits.getdata(dcorr_files[ii], header=True)
else:
d_data0, t_hdr = fits.getdata(raw_files[ii], ext=1, header=True) #last
d_data1 = fits.getdata(raw_files[ii], ext=2) #first
d_data = np.float_(d_data0) - d_data1
if do_flat: d_data = d_data / flat0
d_cube0[ii] = d_data
# Mod on 12/11/2017
dither_az[ii] = t_hdr['INSTAZ']
dither_el[ii] = t_hdr['INSTEL']
exptime0[ii] = t_hdr['EXPTIME'] # + on 09/12/2017
## + on 19/10/2017. Mod on 07/11/2017
#data_crfree, crmask = cosmicray_median(d_cube0[ii], thresh=5, rbox=11)
#d_cube0[ii] = data_crfree
#Flag pixels that are outliers | Mod on 07/11/2017
#arr_med = np.median(d_cube0, axis=0)
#arr_std = np.std(d_cube0, axis=0)
#
#d_cube_mask = np.zeros_like(d_cube0)
#
#for ii in range(n_files):
# mask = np.where(np.absolute((d_cube0[ii] - arr_med)/arr_std) >= 4)
# d_cube_mask[ii][mask] = 1
# Mod on 19/10/2017
for ii in range(n_files):
t_sky = d_cube0[i_sky[ii]]
t_diff = d_cube0[ii] - t_sky
# Mod on 07/11/2017
#t_sky = np.ma.masked_array(d_cube0[i_sky[ii]],
# mask=d_cube_mask[i_sky[ii]])
#t_diff = np.ma.masked_array(d_cube0[ii], mask=d_cube_mask[ii]) - t_sky
diff_cube0[ii] = t_diff
# Compute offsets using bright source
if bright == True:
med0_row = np.median(t_diff, axis=1)
resize = np.repeat(med0_row, naxis1).reshape((naxis2,naxis1))
im_test = t_diff - resize
med0_col = np.median(im_test, axis=0)
if not max_restrict:
peak_val[ii] = np.argmax(med0_col)
else:
idx = range(450,1000)
peak_val[ii] = idx[0]+np.argmax(med0_col[idx])
# + on 26/11/2017
if do_curvefit_center:
p0 = [0.0, np.max(med0_col), peak_val[ii], 2.0]
x0 = np.arange(len(med0_col))
popt, pcov = curve_fit(gauss1d, x0, med0_col, p0=p0)
peak_val0[ii] = popt[2]
else:
if ii == 0: mylog.info('Using FITS dither information')
dither_diff = (dither_el[0] - dither_el) / pscale * u.pix
# Mod on 12/11/2017
if bright == True:
if do_curvefit_center: # Mod on 26/11/2017
shift_val = peak_val0[0] - peak_val0 # Mod on 26/11/2017
else:
shift_val = peak_val[0] - peak_val
else:
shift_val = dither_diff.value
mylog.info('Shift values for spectra : ') # Mod on 18/03/2018
# Mod on 12/11/2017
if dcorr_yes:
files_short = [str0.replace(rawdir,'') for str0 in dcorr_files]
else:
files_short = [str0.replace(rawdir,'') for str0 in raw_files]
if bright == True:
diff0 = dither_diff - shift_val * u.pix
arr0 = [files_short, dither_az * u.arcsec, dither_el * u.arcsec,
dither_diff, shift_val * u.pix, diff0]
names0 = ('file','dither_az','dither_el','dither_diff','shift_val',
'difference')
else:
arr0 = [files_short, dither_az * u.arcsec, dither_el * u.arcsec,
dither_diff]
names0 = ('file','dither_az','dither_el','dither_diff')
dither_tab = Table(arr0, names=names0)
dither_tab.pprint(max_lines=-1, max_width=-1)
# + on 12/11/2017
try:
import yaml
yaml_pass = 1
except ModuleNotFoundError or ImportError:
mylog.info("Failed yaml import")
yaml_pass = 0
if yaml_pass:
out_dither_file1 = rawdir+prefix+'_dither_ecsv.cat'
mylog.info('Writing : '+out_dither_file1) # Mod on 18/03/2018
dither_tab.write(out_dither_file1, format='ascii.ecsv', overwrite=True)
# + on 12/11/2017
out_dither_file2 = rawdir+prefix+'_dither.cat'
mylog.info('Writing : '+out_dither_file2) # Mod on 18/03/2018
dither_tab.write(out_dither_file2, format='ascii.fixed_width_two_line', overwrite=True)
#log.info('### '+" ".join([str(a) for a in shift_val]))
for ii in range(n_files):
# Bug fix - Mod on 14/10/2017
shift_cube0[ii] = shift(diff_cube0[ii], [0,shift_val[ii]])
# + on 07/11/2017
if not exists(npz_file):
shift_cube0_mask = sigma_clip(shift_cube0, sigma=3., iters=5, axis=0)
else:
shift_cube0_mask = np.ma.array(shift_cube0, mask=npz0['shift_cube0_mask'])
stack0 = np.ma.average(shift_cube0_mask, axis=0)
# + on 17/11/2017
mylog.info('Writing : '+npz_file) # Mod on 18/03/2018
np.savez_compressed(npz_file, dither_tab=dither_tab,
shift_cube0_mask=shift_cube0_mask.mask)
#stack0d=stack0.data, stack0m=stack0.mask,
# Mod on 07/11/2017
fits.writeto(rawdir+prefix+'_stack.fits', stack0.data, overwrite=True)
#fits.writeto(rawdir+prefix+'_stack.fits', stack0, overwrite=True)
if bright == True:
# Compute FWHM and plot | + on 12/11/2017
out_fwhm_pdf = rawdir+prefix+'_stack_FWHM.pdf'
# + on 20/11/2017
pp = PdfPages(out_fwhm_pdf)
ncols, nrows = 3, 2
FWHM0 = np.zeros(n_files)
if dcorr_yes:
seqno = [str0.replace('.gz','').replace('_dcorr.fits','')[-4:] for \
str0 in dcorr_files] # Mod on 17/11/2017
else:
seqno = [str0.replace('.gz','').replace('.fits','')[-4:] for \
str0 in raw_files]
for ii in range(n_files):
if ii % (ncols*nrows) == 0: # + on 20/11/2017
fig, ax = plt.subplots(nrows, ncols)
row, col = ii / ncols % nrows, ii % ncols # + on 20/11/2017
im0 = shift_cube0_mask[ii].data
med0 = np.median(im0[1024-100:1024+100], axis=0) # Mod on 25/11/2017
x0 = np.arange(len(med0))
x0_max = np.argmax(med0)
y0_max = np.max(med0)
p0 = [0.0, y0_max, x0_max, 2.0]
popt, pcov = curve_fit(gauss1d, x0, med0, p0=p0)
FWHM0[ii] = popt[3]*2*np.sqrt(2*np.log(2)) * pscale
# + on 20/11/2017
ax[row,col].plot((x0-popt[2])*pscale, med0/y0_max, color='black')
ax[row,col].plot((x0-popt[2])*pscale, gauss1d(x0, *popt)/y0_max,
color='blue', alpha=0.5)
# + on 20/11/2017
if row == nrows-1:
ax[row,col].set_xlabel('X [arcsec]')
else:
if ((n_files-1)-ii) > ncols-1:
ax[row,col].set_xticklabels([])
# + on 11/12/2017
if ii == n_files-1:
for cc in range(ncols): ax[row,cc].set_xlabel('X [arcsec]')
# + on 20/11/2017
if col == 0:
ax[row,col].set_ylabel('Normalized Flux')
else: ax[row,col].set_yticklabels([])
# + on 20/11/2017
ax[row,col].annotate(seqno[ii], [0.025,0.975], ha='left',
va='top', xycoords='axes fraction',
weight='bold', fontsize=10)
ax[row,col].annotate('FWHM = %.2f"' % FWHM0[ii], [0.975,0.975],
ha='right', va='top', xycoords='axes fraction',
weight='bold', fontsize=10)
# + on 20/11/2017
ax[row,col].set_xlim([-2.5,2.5])
ax[row,col].set_ylim([-0.05,1.1])
# + on 11/12/2017
if ii == n_files-1:
for cc in range(col+1,ncols): ax[row,cc].axis('off')
for rr in range(row+1,nrows):
for cc in range(ncols): ax[rr,cc].axis('off')
# + on 20/11/2017
if ii % (ncols*nrows) == ncols*nrows-1 or ii == n_files-1:
subplots_adjust(left=0.025, bottom=0.025, top=0.975,
right=0.975, wspace=0.02, hspace=0.02)
fig.set_size_inches(8,6)
fig.savefig(pp, format='pdf', bbox_inches='tight')
#endfor
fig, ax = plt.subplots() # + on 20/11/2017
ax.plot(seqno, FWHM0, marker='o', color='b', alpha=0.5)
ax.set_xlabel('Image Frame No.')
ax.set_ylabel('FWHM [arcsec]')
ax.minorticks_on()
filt0, disp0, ap0 = t_hdr['FILTER'], t_hdr['DISPERSE'], \
t_hdr['APERTURE']
label0 = '%s %s %s %s' % (prefix, filt0, disp0, ap0)
ax.annotate(label0, [0.025,0.975], ha='left', va='top', weight='bold',
xycoords='axes fraction', fontsize=11, bbox=bbox_props)
# + on 11/12/2017
avg_FWHM0 = np.average(FWHM0)
med_FWHM0 = np.median(FWHM0)
sig_FWHM0 = np.std(FWHM0)
txt0 = 'Average : %.2f"\n Median : %.2f"\n' % (avg_FWHM0, med_FWHM0)
txt0 += r'$\sigma$ : %.2f"' % sig_FWHM0
# Mod on 18/03/2018
mylog.info('Average : %.2f"' % avg_FWHM0)
mylog.info('Median : %.2f"' % med_FWHM0)
mylog.info('Sigma : %.2f"' % sig_FWHM0)
ax.annotate(txt0, [0.975,0.975], ha='right', va='top',
xycoords='axes fraction', fontsize=11, bbox=bbox_props)
# Mod on 20/11/2017
fig.set_size_inches(8,6)
fig.savefig(pp, format='pdf', bbox_inches='tight')
# + on 20/11/2017, Mod on 18/03/2018
mylog.info('Writing : '+out_fwhm_pdf)
pp.close()
# Compute transparency and plot | + on 09/12/2017
out_trans_pdf = rawdir+prefix+'_stack_trans.pdf'
pp = PdfPages(out_trans_pdf)
trans0 = np.zeros(n_files)
spec0 = np.zeros((n_files, naxis2))
fig, ax = plt.subplots()
lstyle0 = ['solid','dashed','dashdot','dotted',
'solid','dashed','dashdot','dotted']
lw0 = [0.25, 0.33, 0.50, 0.75, 0.25, 0.33, 0.50, 0.75]
for ii in range(n_files):
x0 = np.arange(naxis1)
t_sig = FWHM0[ii] / (2*np.sqrt(2*np.log(2))) / pscale
idx = np.where(np.abs(x0 - peak_val[ii])/t_sig <= 2.5)[0]
im0 = diff_cube0[ii] / exptime0[ii]
spec0[ii] = np.sum(im0[:,idx], axis=1)
trans0[ii] = np.sum(im0[:,idx])
lstyle, lw = lstyle0[ii / 7], lw0[ii / 7]
ax.plot(x0, spec0[ii], linewidth=lw, label=seqno[ii], alpha=0.5,
linestyle=lstyle)
ax.set_xlim([0,2100])
ax.set_xlabel('X [pixels]')
ax.set_ylabel('Flux [ADU/s]')
ax.legend(loc='upper left', fontsize='9', ncol=3, framealpha=0.5,
columnspacing=0.5)
fig.set_size_inches(8,6)
fig.savefig(pp, format='pdf', bbox_inches='tight')
fig, ax = plt.subplots() # + on 20/11/2017
i_max = np.argmax(trans0)
ax.plot(seqno, trans0/trans0[i_max], marker='o', color='b', alpha=0.5)
ax.set_xlabel('Image Frame No.')
ax.set_ylabel('Relative Transparency')
ax.set_ylim([0,1.1])
ax.annotate(label0, [0.025,0.975], ha='left', va='top', weight='bold',
xycoords='axes fraction', fontsize=11, bbox=bbox_props)
ax.minorticks_on()
# Mod on 20/11/2017
fig.set_size_inches(8,6)
fig.savefig(pp, format='pdf', bbox_inches='tight')
# + on 20/11/2017, Mod on 18/03/2018
mylog.info('Writing : '+out_trans_pdf)
pp.close()
# + on 18/12/2018
fwhm_wht0 = 1/FWHM0**2
stack0_wht = np.ma.average(shift_cube0_mask, axis=0, weights=fwhm_wht0)
out_fits_wht = rawdir+prefix+'_stack.wht.fits'
mylog.info('Writing : '+out_fits_wht)
fits.writeto(out_fits_wht, stack0_wht.data, overwrite=True)
mylog.info('End main ! ') # Mod on 18/03/2018
#enddef