PyNOT-redux 2.1.3__tar.gz → 2.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (118) hide show
  1. {pynot_redux-2.1.3 → pynot_redux-2.2}/PKG-INFO +1 -1
  2. {pynot_redux-2.1.3 → pynot_redux-2.2}/PyNOT_redux.egg-info/PKG-INFO +1 -1
  3. {pynot_redux-2.1.3 → pynot_redux-2.2}/PyNOT_redux.egg-info/SOURCES.txt +21 -2
  4. pynot_redux-2.2/pynot/VERSION +1 -0
  5. {pynot_redux-2.1.3 → pynot_redux-2.2}/pynot/extract_gui.py +4 -22
  6. {pynot_redux-2.1.3 → pynot_redux-2.2}/pynot/fitsio.py +32 -14
  7. {pynot_redux-2.1.3 → pynot_redux-2.2}/pynot/main.py +52 -1
  8. {pynot_redux-2.1.3 → pynot_redux-2.2}/pynot/scombine.py +13 -6
  9. pynot_redux-2.2/pynot/viewer/__init__.py +0 -0
  10. pynot_redux-2.2/pynot/viewer/containers.py +82 -0
  11. pynot_redux-2.2/pynot/viewer/default_linelists.json +103 -0
  12. pynot_redux-2.2/pynot/viewer/dust.py +149 -0
  13. pynot_redux-2.2/pynot/viewer/linelists.py +170 -0
  14. pynot_redux-2.2/pynot/viewer/messages.py +34 -0
  15. pynot_redux-2.2/pynot/viewer/models.py +300 -0
  16. pynot_redux-2.2/pynot/viewer/notes.py +255 -0
  17. pynot_redux-2.2/pynot/viewer/spectrum.py +386 -0
  18. pynot_redux-2.2/pynot/viewer/tablemodels.py +258 -0
  19. pynot_redux-2.2/pynot/viewer/targets.py +115 -0
  20. pynot_redux-2.2/pynot/viewer/templates/Assef_2010.dat +323 -0
  21. pynot_redux-2.2/pynot/viewer/templates/FeII.Vestergaard.dat +4182 -0
  22. pynot_redux-2.2/pynot/viewer/templates/FeIII.Vestergaard.dat +4182 -0
  23. pynot_redux-2.2/pynot/viewer/templates/Hernan_Caballero_quasar_NIR_template.txt +512 -0
  24. pynot_redux-2.2/pynot/viewer/templates/QSO_selsing.dat +5334 -0
  25. pynot_redux-2.2/pynot/viewer/templates/QSO_template.Krawczyk_all.dat +273 -0
  26. pynot_redux-2.2/pynot/viewer/templates/QSO_template.Krawczyk_high.dat +273 -0
  27. pynot_redux-2.2/pynot/viewer/templates/QSO_template.Krawczyk_low.dat +273 -0
  28. pynot_redux-2.2/pynot/viewer/viewer.py +944 -0
  29. pynot_redux-2.1.3/pynot/VERSION +0 -1
  30. pynot_redux-2.1.3/pynot/india.py +0 -163
  31. {pynot_redux-2.1.3 → pynot_redux-2.2}/.gitattributes +0 -0
  32. {pynot_redux-2.1.3 → pynot_redux-2.2}/.github/dependabot.yml +0 -0
  33. {pynot_redux-2.1.3 → pynot_redux-2.2}/.gitignore +0 -0
  34. {pynot_redux-2.1.3 → pynot_redux-2.2}/LICENSE +0 -0
  35. {pynot_redux-2.1.3 → pynot_redux-2.2}/MANIFEST.in +0 -0
  36. {pynot_redux-2.1.3 → pynot_redux-2.2}/PyNOT_redux.egg-info/dependency_links.txt +0 -0
  37. {pynot_redux-2.1.3 → pynot_redux-2.2}/PyNOT_redux.egg-info/entry_points.txt +0 -0
  38. {pynot_redux-2.1.3 → pynot_redux-2.2}/PyNOT_redux.egg-info/requires.txt +0 -0
  39. {pynot_redux-2.1.3 → pynot_redux-2.2}/PyNOT_redux.egg-info/top_level.txt +0 -0
  40. {pynot_redux-2.1.3 → pynot_redux-2.2}/README.md +0 -0
  41. {pynot_redux-2.1.3 → pynot_redux-2.2}/pynot/.extract_msg +0 -0
  42. {pynot_redux-2.1.3 → pynot_redux-2.2}/pynot/.identify_msg +0 -0
  43. {pynot_redux-2.1.3 → pynot_redux-2.2}/pynot/.instrument.cfg +0 -0
  44. {pynot_redux-2.1.3 → pynot_redux-2.2}/pynot/.response_msg +0 -0
  45. {pynot_redux-2.1.3 → pynot_redux-2.2}/pynot/__init__.py +0 -0
  46. {pynot_redux-2.1.3 → pynot_redux-2.2}/pynot/alfosc.py +0 -0
  47. {pynot_redux-2.1.3 → pynot_redux-2.2}/pynot/calib/HeAr_linelist.dat +0 -0
  48. {pynot_redux-2.1.3 → pynot_redux-2.2}/pynot/calib/HeNe_linelist.dat +0 -0
  49. {pynot_redux-2.1.3 → pynot_redux-2.2}/pynot/calib/ThAr_linelist.dat +0 -0
  50. {pynot_redux-2.1.3 → pynot_redux-2.2}/pynot/calib/al-gr18_pixeltable.dat +0 -0
  51. {pynot_redux-2.1.3 → pynot_redux-2.2}/pynot/calib/al-gr19_pixeltable.dat +0 -0
  52. {pynot_redux-2.1.3 → pynot_redux-2.2}/pynot/calib/al-gr4_pixeltable.dat +0 -0
  53. {pynot_redux-2.1.3 → pynot_redux-2.2}/pynot/calib/al-gr7_pixeltable.dat +0 -0
  54. {pynot_redux-2.1.3 → pynot_redux-2.2}/pynot/calib/alfosc_filters.dat +0 -0
  55. {pynot_redux-2.1.3 → pynot_redux-2.2}/pynot/calib/default_options.yml +0 -0
  56. {pynot_redux-2.1.3 → pynot_redux-2.2}/pynot/calib/default_options_img.yml +0 -0
  57. {pynot_redux-2.1.3 → pynot_redux-2.2}/pynot/calib/ef-gr13_pixeltable.dat +0 -0
  58. {pynot_redux-2.1.3 → pynot_redux-2.2}/pynot/calib/ef-gr14_pixeltable.dat +0 -0
  59. {pynot_redux-2.1.3 → pynot_redux-2.2}/pynot/calib/ef-gr1_pixeltable.dat +0 -0
  60. {pynot_redux-2.1.3 → pynot_redux-2.2}/pynot/calib/ef-gr3_pixeltable.dat +0 -0
  61. {pynot_redux-2.1.3 → pynot_redux-2.2}/pynot/calib/efosc_filters.dat +0 -0
  62. {pynot_redux-2.1.3 → pynot_redux-2.2}/pynot/calib/lapalma.ext +0 -0
  63. {pynot_redux-2.1.3 → pynot_redux-2.2}/pynot/calib/lasilla.ext +0 -0
  64. {pynot_redux-2.1.3 → pynot_redux-2.2}/pynot/calib/paranal.ext +0 -0
  65. {pynot_redux-2.1.3 → pynot_redux-2.2}/pynot/calib/std/bd174708.dat +0 -0
  66. {pynot_redux-2.1.3 → pynot_redux-2.2}/pynot/calib/std/bd262606.dat +0 -0
  67. {pynot_redux-2.1.3 → pynot_redux-2.2}/pynot/calib/std/bd332642.dat +0 -0
  68. {pynot_redux-2.1.3 → pynot_redux-2.2}/pynot/calib/std/bd75325.dat +0 -0
  69. {pynot_redux-2.1.3 → pynot_redux-2.2}/pynot/calib/std/eg21.dat +0 -0
  70. {pynot_redux-2.1.3 → pynot_redux-2.2}/pynot/calib/std/feige110.dat +0 -0
  71. {pynot_redux-2.1.3 → pynot_redux-2.2}/pynot/calib/std/feige34.dat +0 -0
  72. {pynot_redux-2.1.3 → pynot_redux-2.2}/pynot/calib/std/gd153.dat +0 -0
  73. {pynot_redux-2.1.3 → pynot_redux-2.2}/pynot/calib/std/gd50.dat +0 -0
  74. {pynot_redux-2.1.3 → pynot_redux-2.2}/pynot/calib/std/gd71.dat +0 -0
  75. {pynot_redux-2.1.3 → pynot_redux-2.2}/pynot/calib/std/hd19445.dat +0 -0
  76. {pynot_redux-2.1.3 → pynot_redux-2.2}/pynot/calib/std/hd84937.dat +0 -0
  77. {pynot_redux-2.1.3 → pynot_redux-2.2}/pynot/calib/std/hd93521.dat +0 -0
  78. {pynot_redux-2.1.3 → pynot_redux-2.2}/pynot/calib/std/he3.dat +0 -0
  79. {pynot_redux-2.1.3 → pynot_redux-2.2}/pynot/calib/std/hiltner600.dat +0 -0
  80. {pynot_redux-2.1.3 → pynot_redux-2.2}/pynot/calib/std/ltt3864.dat +0 -0
  81. {pynot_redux-2.1.3 → pynot_redux-2.2}/pynot/calib/std/tcs_namelist.txt +0 -0
  82. {pynot_redux-2.1.3 → pynot_redux-2.2}/pynot/calib/std/wolf1346.dat +0 -0
  83. {pynot_redux-2.1.3 → pynot_redux-2.2}/pynot/calibs.py +0 -0
  84. {pynot_redux-2.1.3 → pynot_redux-2.2}/pynot/data/__init__.py +0 -0
  85. {pynot_redux-2.1.3 → pynot_redux-2.2}/pynot/data/alfosc.rules +0 -0
  86. {pynot_redux-2.1.3 → pynot_redux-2.2}/pynot/data/efosc.rules +0 -0
  87. {pynot_redux-2.1.3 → pynot_redux-2.2}/pynot/data/help/welcome_msg_extract.html +0 -0
  88. {pynot_redux-2.1.3 → pynot_redux-2.2}/pynot/data/help/welcome_msg_identify.html +0 -0
  89. {pynot_redux-2.1.3 → pynot_redux-2.2}/pynot/data/help/welcome_msg_response.html +0 -0
  90. {pynot_redux-2.1.3 → pynot_redux-2.2}/pynot/data/io.py +0 -0
  91. {pynot_redux-2.1.3 → pynot_redux-2.2}/pynot/data/obs.py +0 -0
  92. {pynot_redux-2.1.3 → pynot_redux-2.2}/pynot/data/organizer.py +0 -0
  93. {pynot_redux-2.1.3 → pynot_redux-2.2}/pynot/efosc.py +0 -0
  94. {pynot_redux-2.1.3 → pynot_redux-2.2}/pynot/extraction.py +0 -0
  95. {pynot_redux-2.1.3 → pynot_redux-2.2}/pynot/functions.py +0 -0
  96. {pynot_redux-2.1.3 → pynot_redux-2.2}/pynot/identify_gui.py +0 -0
  97. {pynot_redux-2.1.3 → pynot_redux-2.2}/pynot/images.py +0 -0
  98. {pynot_redux-2.1.3 → pynot_redux-2.2}/pynot/insconfig.py +0 -0
  99. {pynot_redux-2.1.3 → pynot_redux-2.2}/pynot/logging.py +0 -0
  100. {pynot_redux-2.1.3 → pynot_redux-2.2}/pynot/operations.py +0 -0
  101. {pynot_redux-2.1.3 → pynot_redux-2.2}/pynot/phot.py +0 -0
  102. {pynot_redux-2.1.3 → pynot_redux-2.2}/pynot/phot_redux.py +0 -0
  103. {pynot_redux-2.1.3 → pynot_redux-2.2}/pynot/redux.py +0 -0
  104. {pynot_redux-2.1.3 → pynot_redux-2.2}/pynot/reports.py +0 -0
  105. {pynot_redux-2.1.3 → pynot_redux-2.2}/pynot/response.py +0 -0
  106. {pynot_redux-2.1.3 → pynot_redux-2.2}/pynot/response_gui.py +0 -0
  107. {pynot_redux-2.1.3 → pynot_redux-2.2}/pynot/scired.py +0 -0
  108. {pynot_redux-2.1.3 → pynot_redux-2.2}/pynot/skysub.py +0 -0
  109. {pynot_redux-2.1.3 → pynot_redux-2.2}/pynot/tasks.py +0 -0
  110. {pynot_redux-2.1.3 → pynot_redux-2.2}/pynot/transients.py +0 -0
  111. {pynot_redux-2.1.3 → pynot_redux-2.2}/pynot/txtio.py +0 -0
  112. {pynot_redux-2.1.3 → pynot_redux-2.2}/pynot/wavecal.py +0 -0
  113. {pynot_redux-2.1.3 → pynot_redux-2.2}/pynot/wcs.py +0 -0
  114. {pynot_redux-2.1.3 → pynot_redux-2.2}/pynot/welcome.py +0 -0
  115. {pynot_redux-2.1.3 → pynot_redux-2.2}/pyproject.toml +0 -0
  116. {pynot_redux-2.1.3 → pynot_redux-2.2}/requirements.txt +0 -0
  117. {pynot_redux-2.1.3 → pynot_redux-2.2}/setup.cfg +0 -0
  118. {pynot_redux-2.1.3 → pynot_redux-2.2}/setup.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: PyNOT-redux
3
- Version: 2.1.3
3
+ Version: 2.2
4
4
  Summary: Data Reduction Pipeline for NOT/ALFOSC
5
5
  Home-page: https://github.com/jkrogager/PyNOT
6
6
  Author: Jens-Kristian Krogager
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: PyNOT-redux
3
- Version: 2.1.3
3
+ Version: 2.2
4
4
  Summary: Data Reduction Pipeline for NOT/ALFOSC
5
5
  Home-page: https://github.com/jkrogager/PyNOT
6
6
  Author: Jens-Kristian Krogager
@@ -28,7 +28,6 @@ pynot/fitsio.py
28
28
  pynot/functions.py
29
29
  pynot/identify_gui.py
30
30
  pynot/images.py
31
- pynot/india.py
32
31
  pynot/insconfig.py
33
32
  pynot/logging.py
34
33
  pynot/main.py
@@ -92,4 +91,24 @@ pynot/data/obs.py
92
91
  pynot/data/organizer.py
93
92
  pynot/data/help/welcome_msg_extract.html
94
93
  pynot/data/help/welcome_msg_identify.html
95
- pynot/data/help/welcome_msg_response.html
94
+ pynot/data/help/welcome_msg_response.html
95
+ pynot/viewer/__init__.py
96
+ pynot/viewer/containers.py
97
+ pynot/viewer/default_linelists.json
98
+ pynot/viewer/dust.py
99
+ pynot/viewer/linelists.py
100
+ pynot/viewer/messages.py
101
+ pynot/viewer/models.py
102
+ pynot/viewer/notes.py
103
+ pynot/viewer/spectrum.py
104
+ pynot/viewer/tablemodels.py
105
+ pynot/viewer/targets.py
106
+ pynot/viewer/viewer.py
107
+ pynot/viewer/templates/Assef_2010.dat
108
+ pynot/viewer/templates/FeII.Vestergaard.dat
109
+ pynot/viewer/templates/FeIII.Vestergaard.dat
110
+ pynot/viewer/templates/Hernan_Caballero_quasar_NIR_template.txt
111
+ pynot/viewer/templates/QSO_selsing.dat
112
+ pynot/viewer/templates/QSO_template.Krawczyk_all.dat
113
+ pynot/viewer/templates/QSO_template.Krawczyk_high.dat
114
+ pynot/viewer/templates/QSO_template.Krawczyk_low.dat
@@ -0,0 +1 @@
1
+ 2.2
@@ -51,27 +51,6 @@ def run_gui(input_fname, output_fname, app=None, **ext_kwargs):
51
51
  del gui
52
52
 
53
53
 
54
- # def save_ascii_spectrum(fname, wl, flux, err, hdr, bg=None):
55
- # """Write spectrum to an ascii text file with header saved to separate text file."""
56
- # if bg is not None:
57
- # data_table = np.column_stack([wl, flux, err, bg])
58
- # fmt = "%12.4f % .3e %.3e %.3e"
59
- # col_names = "# Wavelength Flux Error Sky"
60
- # else:
61
- # data_table = np.column_stack([wl, flux, err])
62
- # fmt = "%12.4f % .3e %.3e"
63
- # col_names = "# Wavelength Flux Error"
64
- #
65
- # basename, ext = os.path.splitext(fname)
66
- # header_fname = basename + '_hdr.txt'
67
- #
68
- # with open(fname, 'w') as output:
69
- # output.write(col_names + "\n")
70
- # np.savetxt(output, data_table, fmt=fmt)
71
- # hdr.tofile(header_fname, sep='\n', endcard=False, padding=False, overwrite=True)
72
- # return True, "File saved successfully"
73
-
74
-
75
54
  def get_FWHM(y, x=None):
76
55
  """
77
56
  Measure the FWHM of the profile given as `y`.
@@ -1669,7 +1648,10 @@ class ExtractGUI(QtWidgets.QMainWindow):
1669
1648
  def update_xmask_in_points(self):
1670
1649
  # Clear old shapes:
1671
1650
  for item in self.xmask:
1672
- item.remove()
1651
+ try:
1652
+ item.remove()
1653
+ except NotImplementedError:
1654
+ pass
1673
1655
  self.xmask = list()
1674
1656
  xmin, xmax, ymin, ymax = self.get_limits()
1675
1657
  if xmin > 0 or xmax < self.image2d.data.shape[1]+1:
@@ -3,6 +3,8 @@ __author__ = "Jens-Kristian Krogager"
3
3
 
4
4
  import warnings
5
5
  from astropy.io import fits
6
+ from astropy import units as u
7
+ from astropy.table import QTable
6
8
  import numpy as np
7
9
  import os
8
10
 
@@ -129,8 +131,8 @@ def get_wavelength_from_header(hdr):
129
131
 
130
132
  # -- These names are used to define proper column names for Wavelength, Flux and Error:
131
133
  wavelength_column_names = ['wl', 'lam', 'lambda', 'loglam', 'wave', 'wavelength', 'awav']
132
- flux_column_names = ['data', 'spec', 'flux', 'flam', 'fnu', 'flux_density']
133
- error_column_names = ['err', 'sig', 'error', 'ivar', 'sigma', 'var', 'err_flux']
134
+ flux_column_names = ['data', 'spec', 'flux', 'flam', 'fnu', 'flux_density', 'reduced_flux']
135
+ error_column_names = ['err', 'sig', 'error', 'ivar', 'sigma', 'var', 'err_flux', 'flux_ivar', 'flux_var', 'flux_err']
134
136
  mask_column_names = ['mask', 'qual', 'dq', 'qc']
135
137
 
136
138
  # -- These names are used to define proper ImageHDU names for Flux and Error:
@@ -139,7 +141,7 @@ error_HDU_names = ['ERR', 'ERRS', 'SIG', 'SIGMA', 'ERROR', 'ERRORS', 'IVAR', 'VA
139
141
  mask_HDU_names = ['MASK', 'QUAL', 'QC', 'DQ']
140
142
 
141
143
 
142
- def get_spectrum_fits_table(tbdata):
144
+ def get_spectrum_fits_table(table_hdu):
143
145
  """
144
146
  Scan the TableData for columns containing wavelength, flux, error and mask.
145
147
  All arrays of {wavelength, flux and error} must be present.
@@ -163,7 +165,12 @@ def get_spectrum_fits_table(tbdata):
163
165
  Numpy boolean array of pixel mask. `True` if the pixel is 'good',
164
166
  `False` if the pixel is bad and should not be used.
165
167
  """
166
- table_names = [name.lower() for name in tbdata.names]
168
+ tbdata = QTable.read(table_hdu)
169
+ # table_names = [name.lower() for name in tbdata.names]
170
+ for colname in tbdata.colnames:
171
+ tbdata.rename_column(colname, colname.lower())
172
+ table_names = tbdata.colnames
173
+
167
174
  wl_in_table = False
168
175
  for colname in wavelength_column_names:
169
176
  if colname in table_names:
@@ -185,9 +192,10 @@ def get_spectrum_fits_table(tbdata):
185
192
  for colname in error_column_names:
186
193
  if colname in table_names:
187
194
  error_in_table = True
188
- if colname == 'ivar':
189
- error = 1./np.sqrt(tbdata[colname])
190
- elif colname == 'var':
195
+ if 'ivar' in colname.lower():
196
+ with np.errstate(divide='ignore', invalid='ignore'):
197
+ error = 1 / np.sqrt(tbdata[colname])
198
+ elif 'var' in colname.lower():
191
199
  error = np.sqrt(tbdata[colname])
192
200
  else:
193
201
  error = tbdata[colname]
@@ -365,17 +373,19 @@ def load_fits_spectrum(fname, ext=None, iraf_obj=None):
365
373
  else:
366
374
  is_fits_table = isinstance(HDUlist[1], fits.BinTableHDU) or isinstance(HDUlist[1], fits.TableHDU)
367
375
  if is_fits_table:
368
- if ext:
369
- tbdata = HDUlist[ext].data
370
- data_hdr = HDUlist[ext].header
371
- else:
372
- tbdata = HDUlist[1].data
373
- data_hdr = HDUlist[1].header
376
+ if not ext:
377
+ ext = 1
378
+
379
+ table_hdu = HDUlist[ext]
380
+ tbdata = table_hdu.data
381
+ data_hdr = table_hdu.header
374
382
 
375
383
  has_multi_extensions = len(HDUlist) > 2
376
384
  if has_multi_extensions and (ext is None):
377
385
  msg = "[WARNING] - More than one data extension detected in the file"
378
- wavelength, data, error, mask = get_spectrum_fits_table(tbdata)
386
+ wavelength, data, error, mask = get_spectrum_fits_table(table_hdu)
387
+ if 'CUNIT1' in data_hdr:
388
+ wavelength *= u.Unit(data_hdr['CUNIT1'])
379
389
  return wavelength, data, error, mask, data_hdr, msg
380
390
 
381
391
  elif len(HDUlist) == 2:
@@ -538,3 +548,11 @@ def create_error_image(base_fname, overwrite=False):
538
548
  hdu.append(ext)
539
549
  output_msg = " - Successfully created an error image"
540
550
  return output_msg
551
+
552
+
553
+ def detect_4most_MEC(fname):
554
+ prim = fits.getheader(fname)
555
+ is_qmost = prim.get('INSTRUME', 'NONE').strip() == 'QMOST'
556
+ hdr = fits.getheader(fname, 1)
557
+ has_mec_extname = hdr.get('EXTNAME') in ['OBMETATAB', 'SPECTAB', 'FIBMETATAB']
558
+ return is_qmost & has_mec_extname
@@ -372,6 +372,10 @@ def main(inspect=False):
372
372
  help="Set this option for 2D spectra of extended sources to turn off automatic localization")
373
373
  parser_scomb.add_argument("--mef", action="store_false",
374
374
  help="Set this option to save output as a multiextension FITS file instead of a FITS table.")
375
+ parser_scomb.add_argument("--imin", type=int, default=0,
376
+ help="Slice each input spectrum along dispersion axis [imin:imax], only for 1D spectra")
377
+ parser_scomb.add_argument("--imax", type=int, default=None,
378
+ help="Slice each input spectrum along dispersion axis [imin:imax], only for 1D spectra")
375
379
 
376
380
 
377
381
  # -- extract :: Extraction of 1D spectrum from 2D
@@ -389,6 +393,31 @@ def main(inspect=False):
389
393
  set_default_pars(parser_ext, section='extract', default_type=int,
390
394
  ignore_pars=['interactive'])
391
395
 
396
+ # -- view :: View 1D spectra in an interactive window
397
+ parser_view = tasks.add_parser('view', formatter_class=set_help_width(31),
398
+ help="Display and interact with 1D spectra")
399
+ parser_view.add_argument("files", type=str, nargs='*',
400
+ help="Filenames of spectral data to load. Each file is loaded as one target")
401
+ parser_view.add_argument("-t", "--table", type=str,
402
+ help="Filename of association table. Each line in the file gives a comma-separated list of filenames. "
403
+ "All files in one line are loaded as a single target.")
404
+ parser_view.add_argument("-c", "--container", action="store_true",
405
+ help="Load the file(s) as a FITS container "
406
+ "(such as 4MOST MEC, SDSS bricks, or a large collection of files)")
407
+ parser_view.add_argument("-l", "--list", type=str, default='',
408
+ help="File list. Name of a text file where each line is the path of one spectrum."
409
+ "(Useful for loading many files in `container` mode with `-c`)")
410
+ # redshift_table=None, z_col=None, name_col=None, cls_col=None
411
+ parser_view.add_argument("-z", type=str, default='',
412
+ help="Filename of redshift catalog with a name, redshift and spectral type column. "
413
+ "Set column names using `--ncol`, `--zcol`, `--tcol`")
414
+ parser_view.add_argument("--ncol", type=str, default='',
415
+ help="Name of the target name column in `-z REDSHIFT_TABLE`")
416
+ parser_view.add_argument("--zcol", type=str, default='',
417
+ help="Name of the redshift column in `-z REDSHIFT_TABLE`")
418
+ parser_view.add_argument("--tcol", type=str, default='',
419
+ help="Name of the spectral type column in `-z REDSHIFT_TABLE`")
420
+
392
421
 
393
422
  # Spectral Redux:
394
423
  parser_redux = tasks.add_parser('spex', formatter_class=set_help_width(30),
@@ -770,7 +799,7 @@ def main(inspect=False):
770
799
 
771
800
  if data_is_1d:
772
801
  out_args = combine_1d(filelist, output=args.output, method=args.method,
773
- scale=args.scale, table_output=args.mef)
802
+ scale=args.scale, table_output=args.mef, imin=args.imin, imax=args.imax)
774
803
  else:
775
804
  out_args = combine_2d(filelist, output=args.output, method=args.method, trim=args.trim,
776
805
  scale=args.scale, extended=args.extended, dispaxis=args.axis)
@@ -796,6 +825,28 @@ def main(inspect=False):
796
825
  gui.show()
797
826
  app.exit(app.exec_())
798
827
 
828
+ elif task == 'view':
829
+ from PyQt5 import QtWidgets
830
+ from pynot.viewer.viewer import MainWindow
831
+
832
+ app = QtWidgets.QApplication(sys.argv)
833
+ screenSize = app.primaryScreen().size()
834
+ ratio = 0.85
835
+ if args.list:
836
+ with open(args.list) as l:
837
+ input_files = [line.strip() for line in l.readlines()]
838
+ else:
839
+ input_files = args.files
840
+ main = MainWindow(input_files,
841
+ assn_table=args.table,
842
+ container_mode=args.container,
843
+ width=ratio*screenSize.width(),
844
+ height=ratio*screenSize.height(),
845
+ redshift_table=args.z,
846
+ z_col=args.zcol, name_col=args.ncol, cls_col=args.tcol,
847
+ )
848
+ main.show()
849
+ app.exit(app.exec_())
799
850
 
800
851
  # -- Imaging tasks:
801
852
  elif task == 'phot':
@@ -353,7 +353,7 @@ def combine_2d(files, output=None, method='mean', scale=False, extended=False, d
353
353
 
354
354
 
355
355
 
356
- def combine_1d(files, output=None, method='mean', scale=False, table_output=True):
356
+ def combine_1d(files, output=None, method='mean', scale=False, table_output=True, imin=0, imax=None):
357
357
  """Combine a list of 1d-spectra using either median or mean combination.
358
358
  For median combination, only the overlapping parts of the spectra will be
359
359
  combined. The mean combination uses a weighted average over the entire
@@ -376,6 +376,12 @@ def combine_1d(files, output=None, method='mean', scale=False, table_output=True
376
376
 
377
377
  table_output : bool [default=True]
378
378
  Use FITS table for the output format? Otherwise use a MultiExtension Fits File
379
+
380
+ imin : int
381
+ Apply slicing to each spectrum from imin to imax.
382
+
383
+ imax : int | None
384
+ Apply slicing to each spectrum from imin to imax.
379
385
  """
380
386
 
381
387
  wl_all = list()
@@ -385,6 +391,7 @@ def combine_1d(files, output=None, method='mean', scale=False, table_output=True
385
391
  size_all = list()
386
392
  scales = list()
387
393
 
394
+ cut = slice(imin, imax)
388
395
  msg = list()
389
396
  for fname in files:
390
397
  if fname.endswith('.fits') or fname.endswith('.fit'):
@@ -401,11 +408,11 @@ def combine_1d(files, output=None, method='mean', scale=False, table_output=True
401
408
  msg.append(load_msg)
402
409
  msg.append(" - Loaded ASCII spectrum: %s" % fname)
403
410
 
404
- wl_all.append(wl)
405
- flux_all.append(flux)
406
- err_all.append(err)
407
- mask_all.append(mask)
408
- size_all.append(len(wl))
411
+ wl_all.append(wl[cut])
412
+ flux_all.append(flux[cut])
413
+ err_all.append(err[cut])
414
+ mask_all.append(mask[cut])
415
+ size_all.append(len(wl[cut]))
409
416
  if scale:
410
417
  nonzero = flux.nonzero()[0]
411
418
  idx_0 = min(nonzero) + len(nonzero)/2
File without changes
@@ -0,0 +1,82 @@
1
+ import os
2
+ import numpy as np
3
+ from astropy.io import fits
4
+ import astropy.units as u
5
+
6
+ from pynot.viewer.spectrum import Spectrum
7
+ from pynot.viewer.targets import Target
8
+
9
+
10
+ class GenericFileContainer:
11
+ def __init__(self, filelist):
12
+ self.filelist = filelist
13
+ self.view = []
14
+ for f in self.filelist:
15
+ for key in ['OBJ_NME', 'OBJ_UID', 'OBJECT']:
16
+ try:
17
+ name = fits.getval(f, key)
18
+ except Exception:
19
+ pass
20
+ else:
21
+ name = f
22
+ self.view.append(os.path.basename(name))
23
+
24
+ def __len__(self):
25
+ return len(self.filelist)
26
+
27
+ def __getitem__(self, index):
28
+ target = Target(name=self.view[index])
29
+ spec = Spectrum.read(self.filelist[index])
30
+ target.add_spectrum(spec)
31
+ return target
32
+
33
+
34
+ class QMEC:
35
+ def __init__(self, filename, view):
36
+ self.filename = filename
37
+ self.view = view
38
+
39
+ def __len__(self):
40
+ return len(self.view)
41
+
42
+ def __getitem__(self, index):
43
+ return self.get_target(index)
44
+
45
+ def get_target(self, index):
46
+ target = Target(name=self.view[index])
47
+ with fits.open(self.filename) as hdu:
48
+ data = hdu['SPECTAB'].data[index]
49
+ hdr = hdu['SPECTAB'].header
50
+
51
+ flux = data['FLUX'].flatten()
52
+ try:
53
+ ivar = data['FLUX_IVAR'].flatten()
54
+ with np.errstate(divide='ignore', invalid='ignore'):
55
+ error = 1 / np.sqrt(ivar)
56
+ except Exception:
57
+ error = data['ERR_FLUX'].flatten()
58
+ npix = len(flux)
59
+ wavelength = np.arange(npix) * hdr['1CDLT1'] + hdr['1CRVL1']
60
+ wavelength *= u.Unit(hdr['1CUNI1'])
61
+ flux_unit = u.Unit(hdr['TUNIT1'])
62
+ spectrum = Spectrum(wavelength=wavelength,
63
+ flux=flux*flux_unit,
64
+ error=error*flux_unit,
65
+ name=f"[{index}]",
66
+ filename=self.filename+f"[{index}]",
67
+ meta=dict(hdr))
68
+ target.add_spectrum(spectrum)
69
+ return target
70
+
71
+
72
+ @staticmethod
73
+ def read(filename):
74
+ with fits.open(filename) as hdu:
75
+ names_columns = ['OBJECT', 'OBJ_NME', 'OBJ_UID']
76
+ for colname in names_columns:
77
+ if colname in hdu['FIBMETATAB'].data.names:
78
+ view = hdu['FIBMETATAB'].data[colname]
79
+ break
80
+ else:
81
+ view = [f"Object #{num}" for num in range(len(hdu['FIBMETATAB'].data))]
82
+ return QMEC(filename, view)
@@ -0,0 +1,103 @@
1
+ {
2
+ "": [],
3
+
4
+ "Common Lines": [
5
+ ["Ly-α", 1215.67, true],
6
+ ["N V 1240", 1240.14, true],
7
+ ["Si IV / O IV]", 1399, true],
8
+ ["C IV", 1549.06, true],
9
+ ["C III]", 1908.73, true],
10
+ ["Mg II", 2798.75, true],
11
+ ["H-α", 6564.61, true],
12
+ ["H-β", 4862.68, true],
13
+ ["H-γ", 4341.68, true],
14
+ ["H-δ", 4102.89, true],
15
+ ["H-ε", 3971.20, true],
16
+ ["[O II] 3727", 3727.09, true],
17
+ ["[O II] 3729", 3729.88, true],
18
+ ["[Ne III] 3870", 3869.86, true],
19
+ ["[O III] 4364", 4364.44, true],
20
+ ["[O III] 4960", 4960.30, true],
21
+ ["[O III] 5008", 5008.24, true],
22
+ ["[N II] 6550", 6549.86, true],
23
+ ["[N II] 6585", 6585.27, true],
24
+ ["[S II] 6718", 6718.29, true],
25
+ ["[S II] 6733", 6732.67, true],
26
+ ["Ca II K", 3934.78, true],
27
+ ["Ca II H", 3969.59, true],
28
+ ["Mg I b", 5176.70, true],
29
+ ["Na I D", 5891.58, true]
30
+ ],
31
+
32
+ "Quasar Lines": [
33
+ ["O VI", 1033.83, true],
34
+ ["Ly-α", 1215.67, true],
35
+ ["Ly-β", 1025.72, true],
36
+ ["N V 1240", 1240.14, true],
37
+ ["O I", 1304.35, true],
38
+ ["Si IV / O IV]", 1399, true],
39
+ ["C IV", 1549.06, true],
40
+ ["He II 1640", 1640.42, true],
41
+ ["C III]", 1908.73, true],
42
+ ["Mg II", 2798.75, true],
43
+ ["H-α", 6564.61, true],
44
+ ["H-β", 4862.68, true],
45
+ ["H-γ", 4341.68, true],
46
+ ["H-δ", 4102.89, true],
47
+ ["H-ε", 3971.20, true],
48
+ ["[O II] 3727", 3727.09, true],
49
+ ["[O II] 3729", 3729.88, true],
50
+ ["[Ne III] 3870", 3869.86, true],
51
+ ["[O III] 4960", 4960.30, true],
52
+ ["[O III] 5008", 5008.24, true],
53
+ ["[N II] 6550", 6549.86, true],
54
+ ["[N II] 6585", 6585.27, true],
55
+ ["[S II] 6718", 6718.29, true],
56
+ ["[S II] 6733", 6732.67, true]
57
+ ],
58
+
59
+ "ISM Absorption": [
60
+ ["CI_1656", 1656.93, true],
61
+ ["CI_1560", 1560.31, true],
62
+ ["CI_1328", 1328.83, true],
63
+ ["SII_1250", 1250.578, true],
64
+ ["SII_1253", 1253.805, true],
65
+ ["SII_1259", 1259.518, true],
66
+ ["OI_1302", 1302.17, true],
67
+ ["FeII_1608", 1608.45, true],
68
+ ["SiII_1260", 1260.42, true],
69
+ ["SiII_1304", 1304.37, true],
70
+ ["CII_1334", 1334.53, true],
71
+ ["SiIV_1393", 1393.76, true],
72
+ ["SiIV_1402", 1402.77, true],
73
+ ["SiII_1526", 1526.71, true],
74
+ ["CIV_1548", 1548.19, true],
75
+ ["CIV_1550", 1550.77, true],
76
+ ["FeII_1608", 1608.45, true],
77
+ ["AlII_1670", 1670.79, true],
78
+ ["SiII_1808", 1808.01, true],
79
+ ["AlIII_1854", 1854.72, true],
80
+ ["AlIII_1862", 1862.79, true],
81
+ ["TiII_1910", 1910.61, true],
82
+ ["TiII_3073", 3073.86, true],
83
+ ["TiII_3242", 3242.91, true],
84
+ ["TiII_3384", 3384.73, true],
85
+ ["FeII_2344", 2344.21, true],
86
+ ["FeII_2374", 2374.46, true],
87
+ ["FeII_2382", 2382.76, true],
88
+ ["MnII_2576", 2576.87, true],
89
+ ["FeII_2586", 2586.65, true],
90
+ ["MnII_2594", 2594.50, true],
91
+ ["FeII_2600", 2600.17, true],
92
+ ["MnII_2606", 2606.46, true],
93
+ ["MgII_2796", 2796.35, true],
94
+ ["MgII_2803", 2803.53, true],
95
+ ["MgI_2852", 2852.96, true],
96
+ ["NaI_5891", 5891.58, true],
97
+ ["NaI_5897", 5897.56, true],
98
+ ["ZnII_2026", 2026.14, true],
99
+ ["ZnII_2062", 2062.66, true],
100
+ ["CaII_3934", 3934.77, true],
101
+ ["CaII_3969", 3969.59, true]
102
+ ]
103
+ }
@@ -0,0 +1,149 @@
1
+
2
+ import numpy as np
3
+ from scipy.interpolate import UnivariateSpline as spline
4
+
5
+ parameters = {
6
+ 'SMC': {'c1': -4.959, 'c2': 2.264, 'c3': 0.389, 'c4': 0.0, 'x0': 4.579, 'gamma': 0.934, 'Rv': 2.74},
7
+ 'LMC': {'c1': -0.89, 'c2': 0.998, 'c3': 2.719, 'c4': 0.0, 'x0': 4.579, 'gamma': 0.934, 'Rv': 3.41},
8
+ }
9
+
10
+
11
+ def MW_reddening(wl, Rv=3.1):
12
+ """
13
+ Average Galactic reddening law parametrized by Fitzpatrick & Massa (2007).
14
+
15
+ INPUT
16
+ wl: wavelength in Angstrom
17
+
18
+ Rv: if not given, the average value of 3.1 is assumed.
19
+
20
+ Returns A(l)/A(V) evaluated at the input wavelengths.
21
+ """
22
+
23
+ if isinstance(wl, float):
24
+ wl = np.array([wl])
25
+ convert2float = True
26
+ else:
27
+ convert2float = False
28
+
29
+ k_in = 1./(wl*1.e-4)
30
+
31
+ c4 = 0.319
32
+ c3 = 2.991
33
+ c2 = 5.0/Rv - 0.85
34
+ c1 = 2.09 - 2.84*c2
35
+ x0 = 4.592
36
+ gamma = 0.922
37
+ pars_avg = {'c1': c1, 'c2': c2, 'c3': c3, 'c4': c4,
38
+ 'x0': x0, 'gamma': gamma, 'Rv': Rv}
39
+
40
+ ksi = FM2007(k_in, **pars_avg)
41
+
42
+ if convert2float:
43
+ ksi = float(ksi)
44
+ return ksi
45
+
46
+
47
+ def FM2007(wl, c1, c2, c3, c4, Rv, x0=4.579, gamma=0.934):
48
+ """
49
+ Fitzpatrick & Mazza extinction curve
50
+ Return ksi = A(lambda)/A(V) instead of E(B-lambda)/E(B-V).
51
+ Using O2 and O3 as free parameters for optical spline.
52
+ O2 = 1.33 +/- 0.01
53
+ O3 = 2.0 +/- 0.1
54
+ O1 is kept fixed to ensure correct normalization.
55
+
56
+ Parameters
57
+ ----------
58
+ wl : np.ndarray
59
+ Wavelength array in angstrom
60
+
61
+ pars : dict
62
+ Parameters for the extinction curve:
63
+ (c1, c2, c3, c4, x0, gamma, Rv)
64
+
65
+ Returns
66
+ -------
67
+ ksi : np.ndarray
68
+ Array of A(lambda) / A(v) for the given input wavelength array.
69
+ """
70
+ k = 1 / (wl*1.e-4)
71
+
72
+ D = k**2/((k**2 - x0**2)**2 + k**2*gamma**2)
73
+
74
+ F = 0.5392*(k-5.9)**2 + 0.05644*(k-5.9)**3
75
+ F[k < 5.9] = 0.
76
+
77
+ # Use Fitzpatrick & Massa 1990 original formulation
78
+ # with fixed UV polynomial:
79
+ Ebv = c1 + c2*k + c3*D + c4*F
80
+
81
+ # Use IR power-law from Fitzpatrick & Massa 2007
82
+ # assuming the correlation ketween k_IR and Rv.
83
+ # Their eq. 7:
84
+ Ebv_IR = (-0.83 + 0.63*Rv)*k**1.84 - Rv
85
+
86
+ # Use spline points from 1 < x < 3.7
87
+ # Anchor to UV and IR parts to make smooth transition: (FM2007)
88
+ U1 = 3.85 # anchor at 2600A
89
+ U2 = 3.7 # anchor at 2700A
90
+ O_UV1 = c1 + c2*U1 + c3*U1**2/((U1**2 - x0**2)**2 + U1**2*gamma**2)
91
+ O_UV2 = c1 + c2*U2 + c3*U2**2/((U2**2 - x0**2)**2 + U2**2*gamma**2)
92
+ O_IRopt = (-0.83 + 0.63*Rv)*1.0**1.84 - Rv # anchor at 1.0
93
+ O_IR = (-0.83 + 0.63*Rv)*0.75**1.84 - Rv # anchor at 0.75
94
+
95
+ # Array of anchor points for spline
96
+ O = np.array([O_IR, O_IRopt, 0., O_UV2, O_UV1])
97
+
98
+ # Array of inverse wavelength for spline anchors:
99
+ k_anchor = np.array([0.75, 1., 1.808, U2, U1])
100
+
101
+ Ebv_spline_func = spline(k_anchor, O)
102
+ Ebv_spline = Ebv_spline_func(k)
103
+
104
+ # stitch together the pieces
105
+ Ebv = Ebv*(k >= 3.7) + Ebv_spline*(k < 3.7)*(k > 1.) + Ebv_IR*(k <= 1.)
106
+
107
+ ksi = Ebv/Rv + 1.
108
+
109
+ return ksi
110
+
111
+
112
+ class DustModel:
113
+ name = ''
114
+
115
+ def __init__(self, name: str, pars: dict[str, float], functor=FM2007):
116
+ self.name = name
117
+ self.pars = pars
118
+ self.functor = functor
119
+
120
+ def __call__(self, x: np.ndarray, Av=1.0):
121
+ model = 10**(-0.4 * self.functor(x, **self.pars) * Av)
122
+ return 10**(-0.4 * self.functor(x, **self.pars) * Av)
123
+
124
+ def __repr__(self):
125
+ return self.__str__()
126
+
127
+ def __str__(self):
128
+ return self.name
129
+
130
+
131
+ class SMCDustModel(DustModel):
132
+ name = 'SMC'
133
+
134
+ def __init__(self):
135
+ super().__init__(name='SMC', pars=parameters['SMC'])
136
+
137
+
138
+ class LMCDustModel(DustModel):
139
+ name = 'LMC'
140
+
141
+ def __init__(self):
142
+ super().__init__(name='LMC', pars=parameters['LMC'])
143
+
144
+
145
+ class MWDustModel(DustModel):
146
+ name = 'MW'
147
+
148
+ def __init__(self):
149
+ super().__init__(name='MW', pars={'Rv': 3.1}, functor=MW_reddening)