Skip to content

Commit 3237a68

Browse files
authored
Check_spectrum_plottable fix for JWST data in Jy (#151)
* add optional format * better test for flux units * improvements to ingest_instrument * check_spectrum_plottable test * log message tweak * name the main logger * Spectrum1D -> Spectrum * delete dead code * require specutils>1.20.1 * remove check_spectrum_class helper
1 parent 67aaf6b commit 3237a68

File tree

7 files changed

+132
-161
lines changed

7 files changed

+132
-161
lines changed

astrodb_utils/__init__.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515

1616

1717

18-
logger = logging.getLogger(__name__)
18+
logger = logging.getLogger("astrodb_utils")
1919

2020
LOGFORMAT = logging.Formatter(
2121
"%(levelname)-8s - %(name)-15s - %(message)s")
@@ -30,7 +30,7 @@
3030
handler.setFormatter(LOGFORMAT)
3131
logger.addHandler(handler)
3232

33-
logger.info("astrodb_utils logger initialized")
33+
logger.info(f"Logger initialized: {logger.name}")
3434
logger.info(f"Logger level: {logging.getLevelName(logger.getEffectiveLevel()) }")
3535

3636
warnings.filterwarnings("ignore", module="astroquery.simbad")

astrodb_utils/spectra.py

Lines changed: 80 additions & 102 deletions
Original file line numberDiff line numberDiff line change
@@ -4,38 +4,84 @@
44

55
import astropy.units as u
66
import numpy as np
7-
from specutils import Spectrum1D
7+
from specutils import Spectrum
88

9-
from astrodb_utils import AstroDBError
9+
from astrodb_utils import AstroDBError, exit_function
1010

1111
matplotlib_check = importlib.util.find_spec("matplotlib")
1212
if matplotlib_check is not None:
1313
import matplotlib.pyplot as plt
1414

1515

16-
__all__ = [
17-
"check_spectrum_plottable"
18-
]
16+
__all__ = ["check_spectrum_plottable"]
1917

2018
logger = logging.getLogger(__name__)
2119

2220

23-
def check_spectrum_class(spectrum, raise_error=True):
24-
try:
25-
Spectrum1D.read(spectrum)
26-
return True
27-
except Exception as error_message:
28-
msg = f"Unable to load file as Spectrum1D object:{spectrum}"
29-
logger.debug(f"{error_message}")
30-
if raise_error:
31-
logger.error(msg)
32-
raise AstroDBError(msg)
33-
else:
34-
logger.warning(msg)
35-
return False
21+
def check_spectrum_plottable(
22+
spectrum_path: str | Spectrum, raise_error: bool = True, show_plot: bool = False, format: str = None
23+
):
24+
"""
25+
Check if spectrum is readable and plottable with specutils.
26+
show_plot = True requires matplotlib to be installed.
27+
28+
Parameters
29+
----------
30+
spectrum_path : str or Spectrum
31+
Path to spectrum file or Spectrum object
32+
33+
raise_error : bool. Default=True
34+
True: Raise error if spectrum is not plottable
35+
False: Do not raise error if spectrum is not plottable. Log warning instead.
36+
37+
show_plot : bool. Default=False
38+
True: Show plot of spectrum. Matplotlib must be installed.
39+
40+
format : str, optional
41+
Format of the spectrum file. If not provided, the format will be inferred by specutils.
42+
43+
Returns
44+
-------
45+
bool
46+
True: Spectrum is plottable
47+
False: Spectrum is not plottable
3648
49+
"""
50+
# check if spectrum is a Spectrum object or a file path
51+
# if it's a file path, check if it can be read as a Spectrum object
52+
if isinstance(spectrum_path, Spectrum):
53+
spectrum = spectrum_path
54+
elif isinstance(spectrum_path, str):
55+
try:
56+
spectrum = Spectrum.read(spectrum_path, format=format)
57+
except Exception as error_message:
58+
msg = f"Unable to load file as Spectrum object:{spectrum_path}:\n{error_message}"
59+
exit_function(msg, raise_error=raise_error)
60+
else:
61+
msg = f"Input is not a valid path or Spectrum object: {spectrum_path}"
62+
exit_function(msg, raise_error=raise_error)
63+
64+
# checking spectrum has good units
65+
wave_unit_check = _check_spectrum_wave_units(spectrum, raise_error=raise_error)
66+
if not wave_unit_check:
67+
return False
68+
69+
flux_unit_check = _check_spectrum_flux_units(spectrum, raise_error=raise_error)
70+
if not flux_unit_check:
71+
return False
72+
73+
# check for NaNs
74+
nan_check = _check_spectrum_not_nans(spectrum, raise_error=raise_error)
75+
if not nan_check:
76+
return False
3777

38-
def check_spectrum_not_nans(spectrum, raise_error=True):
78+
if show_plot:
79+
_plot_spectrum(spectrum)
80+
81+
return True
82+
83+
84+
def _check_spectrum_not_nans(spectrum, raise_error=True):
3985
nan_check: np.ndarray = ~np.isnan(spectrum.flux) & ~np.isnan(spectrum.spectral_axis)
4086
wave = spectrum.spectral_axis[nan_check]
4187
if not len(wave):
@@ -50,7 +96,7 @@ def check_spectrum_not_nans(spectrum, raise_error=True):
5096
return True
5197

5298

53-
def check_spectrum_wave_units(spectrum, raise_error=True):
99+
def _check_spectrum_wave_units(spectrum, raise_error=True):
54100
try:
55101
spectrum.spectral_axis.to(u.micron).value
56102
return True
@@ -83,31 +129,18 @@ def check_spectrum_wave_units(spectrum, raise_error=True):
83129
return False
84130

85131

86-
def check_spectrum_flux_units(spectrum, raise_error=True):
87-
try:
88-
spectrum.flux.to(u.erg / u.s / u.cm**2 / u.AA).value
132+
def _check_spectrum_flux_units(spectrum, raise_error=True):
133+
expected_units = [
134+
u.get_physical_type(u.erg / u.s / u.cm**2 / u.AA),
135+
u.get_physical_type(u.Jy),
136+
]
137+
138+
unit_type = u.get_physical_type(spectrum.flux.unit)
139+
140+
if unit_type in expected_units:
89141
return True
90-
except AttributeError as e:
91-
logger.debug(f"{e}")
92-
msg = f"Unable to parse flux: {spectrum}"
93-
if raise_error:
94-
logger.error(msg)
95-
raise AstroDBError(msg)
96-
else:
97-
logger.warning(msg)
98-
return False
99-
except u.UnitConversionError as e:
100-
logger.debug(f"{e}")
101-
msg = f"Unable to convert flux to erg/s/cm^2/Angstrom: {spectrum}"
102-
if raise_error:
103-
logger.error(msg)
104-
raise AstroDBError(msg)
105-
else:
106-
logger.warning(msg)
107-
return False
108-
except ValueError as e:
109-
logger.debug(f"{e}")
110-
msg = f"Value error: {spectrum}:"
142+
else:
143+
msg = f"flux units are not expected: {spectrum.flux.unit}. Expecting {expected_units}."
111144
if raise_error:
112145
logger.error(msg)
113146
raise AstroDBError(msg)
@@ -116,67 +149,12 @@ def check_spectrum_flux_units(spectrum, raise_error=True):
116149
return False
117150

118151

119-
def plot_spectrum(spectrum):
152+
def _plot_spectrum(spectrum):
120153
if "matplotlib" in sys.modules:
121154
plt.plot(spectrum.spectral_axis, spectrum.flux)
122-
plt.xlabel("Dispersion ({spectrum.spectral_axis.unit})")
123-
plt.ylabel("Flux ({spectrum.flux.unit})")
155+
plt.xlabel(f"Dispersion ({spectrum.spectral_axis.unit})")
156+
plt.ylabel(f"Flux ({spectrum.flux.unit})")
124157
plt.show()
125158
else:
126159
msg = "To display the spectrum, matplotlib most be installed."
127160
logger.warning(msg)
128-
129-
130-
def check_spectrum_plottable(spectrum_path, raise_error=True, show_plot=False):
131-
"""
132-
Check if spectrum is readable and plottable with specutils.
133-
show_plot = True requires matplotlib to be installed.
134-
135-
Parameters
136-
----------
137-
spectrum_path : str
138-
Path to spectrum file
139-
140-
raise_error : bool. Default=True
141-
True: Raise error if spectrum is not plottable
142-
False: Do not raise error if spectrum is not plottable. Log warning instead.
143-
144-
show_plot : bool. Default=False
145-
True: Show plot of spectrum. Matplotlib must be installed.
146-
147-
Returns
148-
-------
149-
bool
150-
True: Spectrum is plottable
151-
False: Spectrum is not plotable
152-
153-
"""
154-
# load the spectrum and make sure it's readable as a Spectrum1D object, has units, is not all NaNs.
155-
if isinstance(spectrum_path, Spectrum1D):
156-
spectrum = spectrum_path
157-
class_check = True
158-
else:
159-
class_check = check_spectrum_class(spectrum_path, raise_error=raise_error)
160-
if not class_check:
161-
return False
162-
else:
163-
spectrum = Spectrum1D.read(spectrum_path)
164-
165-
# checking spectrum has good units
166-
wave_unit_check = check_spectrum_wave_units(spectrum, raise_error=raise_error)
167-
if not wave_unit_check:
168-
return False
169-
170-
flux_unit_check = check_spectrum_flux_units(spectrum, raise_error=raise_error)
171-
if not flux_unit_check:
172-
return False
173-
174-
# check for NaNs
175-
nan_check = check_spectrum_not_nans(spectrum, raise_error=raise_error)
176-
if not nan_check:
177-
return False
178-
179-
if show_plot:
180-
plot_spectrum(spectrum)
181-
182-
return True

astrodb_utils/utils.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,7 @@ def ingest_instrument(db, *, telescope=None, instrument=None, mode=None):
142142
raise AstroDBError(msg)
143143

144144
msg_search = f"Searching for {telescope}, {instrument}, {mode} in database"
145-
logger.info(msg_search)
145+
logger.debug(msg_search)
146146

147147
# Search for the inputs in the database
148148
telescope_db = (
@@ -162,7 +162,7 @@ def ingest_instrument(db, *, telescope=None, instrument=None, mode=None):
162162

163163
if len(telescope_db) == 1 and len(mode_db) == 1:
164164
msg_found = (
165-
f"{telescope}, {instrument}, and {mode} are already in the database."
165+
f"{telescope}-{instrument}-{mode} is already in the database. Nothing added."
166166
)
167167
logger.info(msg_found)
168168
return
@@ -174,10 +174,10 @@ def ingest_instrument(db, *, telescope=None, instrument=None, mode=None):
174174
with db.engine.connect() as conn:
175175
conn.execute(db.Telescopes.insert().values(telescope_add))
176176
conn.commit()
177-
msg_telescope = f"{telescope} was successfully ingested in the database"
177+
msg_telescope = f"{telescope} was successfully added to the Telescopes table."
178178
logger.info(msg_telescope)
179179
except sqlalchemy.exc.IntegrityError as e: # pylint: disable=invalid-name
180-
msg = "Telescope could not be ingested"
180+
msg = f"{telescope} could not be added to the Telescopes table."
181181
logger.error(msg)
182182
raise AstroDBError(msg) from e
183183

@@ -195,10 +195,10 @@ def ingest_instrument(db, *, telescope=None, instrument=None, mode=None):
195195
with db.engine.connect() as conn:
196196
conn.execute(db.Instruments.insert().values(instrument_add))
197197
conn.commit()
198-
msg_instrument = f"{instrument} was successfully ingested in the database."
198+
msg_instrument = f"{telescope}-{instrument}-{mode} was successfully added to the Instruments table."
199199
logger.info(msg_instrument)
200200
except sqlalchemy.exc.IntegrityError as e: # pylint: disable=invalid-name
201-
msg = "Instrument/Mode could not be ingested"
201+
msg = f"{telescope}-{instrument}-{mode} could not be added to the Instruments table."
202202
logger.error(msg)
203203
raise AstroDBError(msg) from e
204204

pyproject.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ dependencies = [
2828
"tqdm",
2929
"ads",
3030
"dateparser",
31+
"specutils>=1.20.1",
3132
]
3233
dynamic = ["version"]
3334

tests/conftest.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ def db():
5252
ignore_ads=True,
5353
)
5454

55-
ingest_publication(db, doi="10.1086/161442", reference="Prob83")
55+
ingest_publication(db, doi="10.1086/161442", reference="Prob83", ignore_ads=True)
5656

5757
return db
5858

tests/data/WISEAJ2018-74MIRI.fits

14.1 KB
Binary file not shown.

0 commit comments

Comments
 (0)