Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add SourceField and subclasses, rework Source #405

Open
wants to merge 16 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 10 additions & 5 deletions scopesim/effects/fits_headers.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
from astropy.table import Table

from . import Effect
from ..source.source_fields import HDUSourceField, TableSourceField
from ..utils import from_currsys, find_file


Expand Down Expand Up @@ -530,19 +531,23 @@ def apply_to(self, hdul, **kwargs):
if (src := opt_train._last_source) is not None:
prefix = self.meta["keyword_prefix"]
for i, field in enumerate(src.fields):
src_class = field.__class__.__name__
src_class = field.field.__class__.__name__
src_dic = deepcopy(src._meta_dicts[i])
if isinstance(field, fits.ImageHDU):
print(src)
print(src.meta)
print(src_dic)
print(src.fields[0].meta)
teutoburg marked this conversation as resolved.
Show resolved Hide resolved
if isinstance(field, HDUSourceField):
hdr = field.header
for key in hdr:
src_dic = {key: [hdr[key], hdr.comments[key]]}

elif isinstance(field, Table):
elif isinstance(field, TableSourceField):
src_dic.update(field.meta)
src_dic["length"] = len(field)
for j, name in enumerate(field.colnames):
for j, name in enumerate(field.field.colnames):
src_dic[f"col{j}_name"] = name
src_dic[f"col{j}_unit"] = str(field[name].unit)
src_dic[f"col{j}_unit"] = str(field.field[name].unit)

self.dict_list = [{"ext_number": self.meta["ext_number"],
"keywords": {
Expand Down
8 changes: 4 additions & 4 deletions scopesim/effects/spectral_efficiency.py
Original file line number Diff line number Diff line change
Expand Up @@ -106,10 +106,10 @@
logger.warning("No grating efficiency for trace %s", trace_id)
return obj

wcs = WCS(obj.hdu.header).spectral
wave_cube = wcs.all_pix2world(np.arange(obj.hdu.data.shape[0]), 0)[0]
wave_cube = (wave_cube * u.Unit(wcs.wcs.cunit[0])).to(u.AA)
obj.hdu = apply_throughput_to_cube(obj.hdu, effic.throughput)
swcs = WCS(obj.hdu.header).spectral
with u.set_enabled_equivalencies(u.spectral()):
wave = swcs.pixel_to_world(np.arange(swcs.pixel_shape[0])) << u.um
obj.hdu = apply_throughput_to_cube(obj.hdu, effic.throughput, wave)

Check warning on line 112 in scopesim/effects/spectral_efficiency.py

View check run for this annotation

Codecov / codecov/patch

scopesim/effects/spectral_efficiency.py#L109-L112

Added lines #L109 - L112 were not covered by tests
return obj

def plot(self):
Expand Down
16 changes: 9 additions & 7 deletions scopesim/effects/ter_curves.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
from ..base_classes import SourceBase, FOVSetupBase
from ..optics.surface import SpectralSurface
from ..source.source import Source
from ..source.source_fields import CubeSourceField
from ..utils import (from_currsys, quantify, check_keys, find_file,
figure_factory, get_logger)

Expand Down Expand Up @@ -113,13 +114,14 @@
thru = self.throughput

# apply transmission to source spectra
for isp, spec in enumerate(obj.spectra):
obj.spectra[isp] = combine_two_spectra(spec, thru, "multiply",
wave_min, wave_max)

# apply transmission to cube fields
for icube, cube in enumerate(obj.cube_fields):
obj.cube_fields[icube] = apply_throughput_to_cube(cube, thru)
for fld in obj.fields:
if isinstance(fld, CubeSourceField):
fld.field = apply_throughput_to_cube(fld.field, thru)
continue

Check warning on line 120 in scopesim/effects/ter_curves.py

View check run for this annotation

Codecov / codecov/patch

scopesim/effects/ter_curves.py#L119-L120

Added lines #L119 - L120 were not covered by tests

for isp, spec in fld.spectra.items():
fld.spectra[isp] = combine_two_spectra(
spec, thru, "multiply", wave_min, wave_max)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I find this a bit risky, as we haven't checked whether fld has a spectra attribute. Maybe

Suggested change
if isinstance(fld, CubeSourceField):
fld.field = apply_throughput_to_cube(fld.field, thru)
continue
for isp, spec in fld.spectra.items():
fld.spectra[isp] = combine_two_spectra(
spec, thru, "multiply", wave_min, wave_max)
if isinstance(fld, CubeSourceField):
fld.field = apply_throughput_to_cube(fld.field, thru)
elif isinstance(fld, SpectrumSourceField):
fld.spectra = {
isp: combine_two_spectra(spec, thru, "multiply", wave_min, wave_max)
for isp, spec in fld.spectra.items()
}


# add the effect background to the source background field
if self.background_source is not None:
Expand Down
29 changes: 18 additions & 11 deletions scopesim/effects/ter_curves_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
from astropy.table import Table, QTable
from astropy.io.votable import parse_single_table
from astropy.io import ascii as ioascii
from astropy.wcs import WCS
from astropy.io import fits
from synphot import SpectralElement, SourceSpectrum, Empirical1D, Observation
from synphot.units import PHOTLAM

Expand Down Expand Up @@ -353,25 +353,32 @@ def scale_spectrum(spectrum, filter_name, amplitude):
return spectrum


def apply_throughput_to_cube(cube, thru):
def apply_throughput_to_cube(
cube: fits.ImageHDU,
thru: SpectralElement | SourceSpectrum,
wave_cube: u.Quantity,
) -> fits.ImageHDU:
"""
Apply throughput curve to a spectroscopic cube.

Parameters
----------
cube : ImageHDU
Three-dimensional image, dimension 0 (in python convention) is the
spectral dimension. WCS is required.
thru : synphot.SpectralElement, synphot.SourceSpectrum
cube : fits.ImageHDU
Three-dimensional image, dimension 0 (in python convention) is the
spectral dimension.
thru : SpectralElement | SourceSpectrum
Throughput curve, spectrum or filter.
wave_cube : u.Quantity["length"]
Wavelength axis of the cube.

Returns
-------
cube : ImageHDU, header unchanged, data multiplied with
wavelength-dependent throughput.
cube : fits.ImageHDU
Header unchanged, data multiplied with wavelength-dependent throughput.

"""
wcs = WCS(cube.header).spectral
wave_cube = wcs.all_pix2world(np.arange(cube.data.shape[0]), 0)[0]
teutoburg marked this conversation as resolved.
Show resolved Hide resolved
wave_cube = (wave_cube * u.Unit(wcs.wcs.cunit[0])).to(u.AA)
# Note: wave_cube used to be converted to AA, but synphot understands um
# or whatever just as well...
cube.data *= thru(wave_cube).value[:, None, None]
return cube

Expand Down
2 changes: 1 addition & 1 deletion scopesim/optics/fov.py
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ def extract_from(self, src):
if not isinstance(src, SourceBase):
raise ValueError(f"source must be a Source object: {type(src)}")

fields_in_fov = [field for field in src.fields
fields_in_fov = [field.field for field in src.fields
if fu.is_field_in_fov(self.header, field)]
if not fields_in_fov:
logger.warning("No fields in FOV.")
Expand Down
4 changes: 4 additions & 0 deletions scopesim/optics/fov_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
from synphot import SourceSpectrum, Empirical1D

from . import image_plane_utils as imp_utils
from ..source.source_fields import SourceField
from ..utils import from_currsys, quantify, quantity_from_table, get_logger


Expand All @@ -31,6 +32,9 @@ def is_field_in_fov(fov_header, field, wcs_suffix=""):
is_inside_fov : bool

"""
if isinstance(field, SourceField):
teutoburg marked this conversation as resolved.
Show resolved Hide resolved
field = field.field

if isinstance(field, fits.ImageHDU) and \
field.header.get("BG_SRC") is not None:
is_inside_fov = True
Expand Down
2 changes: 1 addition & 1 deletion scopesim/optics/image_plane_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ def _get_headers(hdus_or_tables):
else:
raise TypeError(
"hdu_or_table_list may only contain fits.ImageHDU, Table "
"or fits.Header, found {type(hdu_or_table)}.")
f"or fits.Header, found {type(hdu_or_table)}.")
if tables:
yield _make_bounding_header_for_tables(*tables,
pixel_scale=pixel_scale)
Expand Down
2 changes: 1 addition & 1 deletion scopesim/optics/optical_train.py
Original file line number Diff line number Diff line change
Expand Up @@ -277,7 +277,7 @@ def prepare_source(self, source):
# Convert to PHOTLAM per arcsec2
# ..todo: this is not sufficiently general

for ispec, spec in enumerate(source.spectra):
for ispec, spec in source.spectra.items():
# Put on fov wavegrid
wave_min = min(fov.meta["wave_min"] for fov in self.fov_manager.fovs)
wave_max = max(fov.meta["wave_max"] for fov in self.fov_manager.fovs)
Expand Down
2 changes: 1 addition & 1 deletion scopesim/rc.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
"""Global configurations for ScopeSim."""
"""Global configurations for ScopeSim (rc ... runtime configuration)."""

from pathlib import Path
import yaml
Expand Down
Loading