diff --git a/radio_beam/beam.py b/radio_beam/beam.py index 1fa41bd..9955236 100644 --- a/radio_beam/beam.py +++ b/radio_beam/beam.py @@ -27,6 +27,16 @@ def _to_area(major,minor): u.arcmin: "'"} +def _with_default_unit(type_str, value, unit): + if not hasattr(value, 'unit'): + return value * unit + + if value.unit.is_equivalent(unit): + return value + else: + raise u.UnitsError(f"{value.unit} for {type_str} is not equivalent to {equiv_unit}") + + class Beam(u.Quantity): """ An object to handle single radio beams. @@ -51,6 +61,8 @@ def __new__(cls, major=None, minor=None, pa=None, area=None, Gaussian beam. default_unit : :class:`~astropy.units.Unit` The unit to impose on major, minor if they are specified as floats + meta : dict, optional + A dictionary of metadata to store with the beam. """ # improve to some kwargs magic later @@ -71,29 +83,18 @@ def __new__(cls, major=None, minor=None, pa=None, area=None, # give specified values priority if major is not None: - if u.deg.is_equivalent(major): - major = major - else: - warnings.warn("Assuming major axis has been specified in degrees") - major = major * u.deg - if minor is not None: - if u.deg.is_equivalent(minor): - minor = minor - else: - warnings.warn("Assuming minor axis has been specified in degrees") - minor = minor * u.deg + major = _with_default_unit("major", major, default_unit) + if pa is not None: - if u.deg.is_equivalent(pa): - pa = pa - else: - warnings.warn("Assuming position angle has been specified in degrees") - pa = pa * u.deg + pa = _with_default_unit("pa", pa, u.deg) else: - pa = 0.0 * u.deg + pa = 0 * u.deg # some sensible defaults if minor is None: minor = major + else: + minor = _with_default_unit("minor", minor, default_unit) if minor > major: raise ValueError("Minor axis greater than major axis.") diff --git a/radio_beam/multiple_beams.py b/radio_beam/multiple_beams.py index 56b149b..6561162 100644 --- a/radio_beam/multiple_beams.py +++ b/radio_beam/multiple_beams.py @@ -6,7 +6,7 @@ import numpy as np import warnings -from .beam import Beam, _to_area, SIGMA_TO_FWHM +from .beam import Beam, _to_area, SIGMA_TO_FWHM, _with_default_unit from .commonbeam import commonbeam from .utils import InvalidBeamOperationError @@ -30,12 +30,14 @@ def __new__(cls, major=None, minor=None, pa=None, The FWHM minor axes pa : :class:`~astropy.units.Quantity` with angular equivalency The beam position angles - area : :class:`~astropy.units.Quantity` with steradian equivalency + areas : :class:`~astropy.units.Quantity` with steradian equivalency The area of the beams. This is an alternative to specifying the major/minor/PA, and will create those values assuming a circular Gaussian beam. default_unit : :class:`~astropy.units.Unit` The unit to impose on major, minor if they are specified as floats + meta : dict, optional + A dictionary of metadata to include in the header. beams : List of :class:`~radio_beam.Beam` objects List of individual `Beam` objects. The resulting `Beams` object will have major and minor axes in degrees. @@ -59,33 +61,23 @@ def __new__(cls, major=None, minor=None, pa=None, # give specified values priority if major is not None: - if u.deg.is_equivalent(major.unit): - pass - else: - warnings.warn("Assuming major axes has been specified in degrees") - major = major * u.deg - if minor is not None: - if u.deg.is_equivalent(minor.unit): - pass - else: - warnings.warn("Assuming minor axes has been specified in degrees") - minor = minor * u.deg + major = _with_default_unit("major", major, default_unit) + + if pa is not None: if len(pa) != len(major): raise ValueError("Number of position angles must match number of major axis lengths") - if u.deg.is_equivalent(pa.unit): - pass - else: - warnings.warn("Assuming position angles has been specified in degrees") - pa = pa * u.deg + pa = _with_default_unit("pa", pa, u.deg) else: - pa = np.zeros_like(major.value) * u.deg + pa = np.zeros(major.shape) * u.deg # some sensible defaults if minor is None: minor = major elif len(minor) != len(major): raise ValueError("Minor and major axes must have same number of values") + else: + minor = _with_default_unit("minor", minor, default_unit) if np.any(minor > major): raise ValueError("Minor axis greater than major axis.") diff --git a/radio_beam/tests/test_beam.py b/radio_beam/tests/test_beam.py index 222bdea..1a05c2d 100644 --- a/radio_beam/tests/test_beam.py +++ b/radio_beam/tests/test_beam.py @@ -62,7 +62,7 @@ def test_fits_from_casa(): def test_manual(): # Instantiate from command line - man_beam_val = Beam(0.1, 0.1, 30) + man_beam_val = Beam(0.1, 0.1, 30, default_unit=u.deg) npt.assert_almost_equal(man_beam_val.value, 3.451589629868801e-06) man_beam_rad = Beam(0.1*u.rad, 0.1*u.rad, 30*u.deg) npt.assert_almost_equal(man_beam_rad.value, 0.011330900354567986) diff --git a/radio_beam/tests/test_beams.py b/radio_beam/tests/test_beams.py index c09f54f..008e398 100644 --- a/radio_beam/tests/test_beams.py +++ b/radio_beam/tests/test_beams.py @@ -321,7 +321,7 @@ def test_extrema_beams(beams, majors, minors, pas): assert extrema[1].minor.value == minors[mask].max().value -@pytest.mark.parametrize("majors", [[1, 1, 1, 2, np.NaN, 4], +@pytest.mark.parametrize("majors", [[1, 1, 1, 2, np.nan, 4], [0, 1, 1, 2, 3, 4]]) def test_beams_with_invalid(majors): diff --git a/radio_beam/tests/test_kernels.py b/radio_beam/tests/test_kernels.py index ef74cbc..1b125e8 100644 --- a/radio_beam/tests/test_kernels.py +++ b/radio_beam/tests/test_kernels.py @@ -16,7 +16,7 @@ reason="Must have astropy version >1.1") def test_gauss_kernel(): - fake_beam = radio_beam.Beam(10) + fake_beam = radio_beam.Beam(10*u.deg) # Let pixscale be 0.1 deg/pix kernel = fake_beam.as_kernel(0.1*u.deg) @@ -33,7 +33,7 @@ def test_gauss_kernel(): reason="Must have astropy version >1.1") def test_tophat_kernel(): - fake_beam = radio_beam.Beam(10) + fake_beam = radio_beam.Beam(10*u.deg) # Let pixscale be 0.1 deg/pix kernel = fake_beam.as_tophat_kernel(0.1*u.deg)