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

Use common beam instead of beam averaging #656

Open
wants to merge 63 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
63 commits
Select commit Hold shift + click to select a range
1a4ce01
Switch avg -> common beam
e-koch Aug 15, 2020
208a68a
Update beam warning messages for common beam
e-koch Aug 15, 2020
1133a4e
Change test checks to ensure the common beam is being returned
e-koch Aug 15, 2020
9d88227
Add test beam set with similar areas
e-koch Aug 15, 2020
428e185
Looks like we had a duplicated function -> see base_class.py
e-koch Aug 21, 2020
928a51c
Separate beam area checks from avg beam and checks for bad beams; def…
e-koch Aug 21, 2020
7a55cf3
thresholds all default to VRSC.beam_threshold
e-koch Aug 21, 2020
f119270
Missed one threshold input
e-koch Aug 21, 2020
a821462
Alter test_mask_beam_beams to check for varying setting, but no opera…
e-koch Aug 21, 2020
51dc3ad
More information error message
e-koch Aug 21, 2020
90cc768
Add tests for failure and pass for spectral operation due to beam dif…
e-koch Aug 21, 2020
803468a
Make note for adding in beam area check for DaskVRSC (no checks right…
e-koch Aug 22, 2020
69f734d
Add new fixture to ALL_DATA_FIXTURES
e-koch Aug 22, 2020
8ad3259
Add extra attribute names to handle DaskVRSC w/ a beams check
e-koch Aug 25, 2020
cab4b3e
Add VRSC tests for beam checks that fail and pass
e-koch Aug 25, 2020
8cec35c
Remove to-do now handled directly in VRSC
e-koch Aug 25, 2020
0c5e9fb
Clean-up
e-koch Aug 25, 2020
e1a50a8
common beam operations require scipy
e-koch Aug 26, 2020
fe27f9a
Add back average beams, which now just passes to `set_common_beam` wi…
e-koch Aug 26, 2020
59c52b3
Add setting for a "strict" beam matching mode (i.e., threshold = 0.0 …
e-koch Aug 26, 2020
8b4c076
Add strict beam match mode test
e-koch Aug 26, 2020
b010653
Add checks for strict_beam_match in _check_beam_areas. Users will get…
e-koch Aug 26, 2020
a000f93
Need to specify axis=0 for spectral operations to trigger the beam ar…
e-koch Aug 26, 2020
ac848f3
Clean tests up; use common list of spectral operations
e-koch Aug 26, 2020
179ffbd
Add deprecation test for average_beams
e-koch Aug 26, 2020
aa4e61b
Extra self
e-koch Aug 26, 2020
9a3392f
Add a single line unit conversion example for VRSC
e-koch Aug 26, 2020
e8a9525
Update smoothing docs for new common beam operations
e-koch Aug 26, 2020
d7bc388
Add a bunch of common/bad beam handling text to the docs
e-koch Aug 26, 2020
2cc72f1
Add link to beam handling for common errors
e-koch Aug 26, 2020
24d6458
Just use **kwargs to pass to common beam algorithm
e-koch Aug 27, 2020
162d323
Change default common beam masking to just use goodbeams_mask; descri…
e-koch Aug 27, 2020
3191df5
Check for array mask first. Otherwise the == comparisons give a Futur…
e-koch Aug 27, 2020
2d8315b
Add test for different set_common_beam mask options
e-koch Aug 27, 2020
2dead55
Remove "combeam_kwargs"
e-koch Aug 27, 2020
0d1f6f2
Add unmasked_channels property to cache any(axis=(1,2)) calls on masks
e-koch Aug 28, 2020
f9eddca
Add test for `cube.unmasked_channels`
e-koch Aug 28, 2020
5f0bdf3
Need to make an approx comparisons for slightly different rounding in…
e-koch Aug 28, 2020
41d2110
Use unmasked_channels in set_common_beam
e-koch Aug 28, 2020
b80e990
Add _mask_include for all Dask cube types
e-koch Aug 28, 2020
5315641
Change default mask for deprecated average_beams
e-koch Aug 28, 2020
01cfc61
Fix wrong numpy testing function
e-koch Aug 28, 2020
ce99b95
Fix logic using unmasked_channels and separate da.Array checks
e-koch Aug 28, 2020
7776c03
Fix handling 3D masks
e-koch Aug 28, 2020
8c3b9ba
Minor docs comments from @keflavich
e-koch Sep 23, 2020
a9be9e8
Update docs/beam_handling.rst
e-koch Sep 23, 2020
bb39274
Update docs/beam_handling.rst
e-koch Sep 23, 2020
145a2bc
Update docs/beam_handling.rst
e-koch Sep 23, 2020
2156ea9
Small fixes from @astrofrog
e-koch Oct 5, 2020
400308c
Update docs/beam_handling.rst
e-koch Oct 5, 2020
3202da0
Allow for computing common beam on init; state set by initial data + …
e-koch Oct 15, 2020
70f2527
Add convenience functions for convolving to a common beam
e-koch Oct 15, 2020
a0be2b5
Add `with_bad_beams_masked` and deprecated `mask_out_bad_beams`
e-koch Oct 15, 2020
0aca1a6
PR #87 will raise a BeamError for beams that cannot be deconvolved; a…
e-koch Oct 15, 2020
e8a93ac
Try accessing the common beam before any other checks to see if it ex…
e-koch Oct 15, 2020
f64f75c
compute_common_beam shouldn't be private
e-koch Oct 15, 2020
cf14a69
Change common beam masking changes to test output, not the attribute
e-koch Oct 22, 2020
6f48baf
Update compute_common_beam docs; with_common_beam moving to own PR
e-koch Oct 22, 2020
5206ff4
Fix inline string in docs
e-koch Nov 11, 2020
6e86c8a
casa io tests didn't get removed in the rebase...
e-koch Nov 11, 2020
612326f
Add da.Array as accepted type for masks in low dim objects
e-koch Nov 19, 2020
2044657
Remove wrong warning about triggering a whole cube convolution; that …
e-koch Nov 19, 2020
147d9be
Remove the unneeded beam test against the old averaged beam
e-koch Nov 19, 2020
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
127 changes: 115 additions & 12 deletions docs/beam_handling.rst
Original file line number Diff line number Diff line change
Expand Up @@ -53,19 +53,122 @@ Multi-beam cubes
----------------

Varying resolution (multi-beam) cubes are somewhat trickier to work with in
general, though unit conversion is easy. You can perform the same sort of unit
conversion with `~spectral_cube.spectral_cube.VaryingResolutionSpectralCube` s
as with regular `~spectral_cube.spectral_cube.SpectralCube` s; ``spectral-cube``
will use a different beam and frequency for each plane.
general, though some operations are similar to a single resolution cube.
Unit conversion is one case where the operation is called the same for
`~spectral_cube.spectral_cube.VaryingResolutionSpectralCube` s
as with regular `~spectral_cube.spectral_cube.SpectralCube` s. For example, to
convert from Jy / beam to K::

You can identify channels with bad beams (i.e., beams that differ from a reference beam,
which by default is the median beam) using
>>> vrsc_cube_K = vrsc_cube.to(u.K) # doctest: +SKIP

``VaryingResolutionSpectralCube`` will use a different beam and frequency for each plane.

Handling variations in the beams is often a source of difficulty. Some spectral
operations (e.g., moment maps) require a common resolution. However, a few channels
may have large discprepancies in the beam compared to most of the others (e.g., if
more data in a particular channel was bad and had to be flagged).
`~spectral_cube.spectral_cube.VaryingResolutionSpectralCube` deals with these problems in two ways.

Finding and convolving to a common resolution
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

When applying spectral operations on a
`~spectral_cube.spectral_cube.VaryingResolutionSpectralCube`, it is often
necessary to first convolve the data to have a common beam. To retain the
maximum amount of spatial information, the data can be convolved to the
smallest common beam.

Tools for finding the smallest common beam and convolution operations are described
in detail in :doc:`smoothing`.


Small beam variations: Do I need to convolve first?
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Formally, operations on multiple spectral channels require the data to have a common
resolution. In practice, data cubes with a high spectral resolution will have the beam
vary by a very small factor between adjacent channels. It is then a safe approximation
to ignore small changes in the beam to avoid having to convolve the entire data cube to
a common beam (which might be a slow and expensive operation for large cubes).

`~spectral_cube.spectral_cube.VaryingResolutionSpectralCube` allows for small variations
in the beam by setting
:meth:`~spectral_cube.spectral_cube.VaryingResolutionSpectralCube.beam_threshold`. The
`beam_threshold` is the largest allowed fractional change relative to the smallest common beam that will be allowed in spectral operations. For example, the default 0.01 allows up
to a 1% variation from the smallest common beam. If the beams vary more than this, a
`ValueError` will be raised prior to the spectral operation. Otherwise, variations between
the beams will be ignored, a warning will be printed describing this approximation, and
the spectral operation will be computed.

The beam threshold can be changed from the default 0.01 with::

>>> vrsc_cube.beam_threshold = 0.02 # doctest: +SKIP

.. note::

Setting `vrsc_cube.beam_threshold = 1.0`, or greater, will allow for large
beam variations without raising an error. Large values for the beam threshold
should not be used for scientific results.

For most spectral-line data cubes covering a single spectral line, the fine resolution
and small frequency range will often allow the above approximation to work.
However, if you are working with wideband data, this approximation will not hold and you
will need to convolve to a common beam or extract spectral slabs from the data cube.

Strict mode
***********

To disable allowing small variations in the beam,
`~spectral_cube.spectral_cube.VaryingResolutionSpectralCube` has a strict checking mode
than can be enabled with::

>>> vrsc_cube.strict_beam_mode = True # doctest: +SKIP

When strict mode is enabled, all beam must be identical for spectral operations to work.
Note that this is equivalent to setting the beam threshold to 0.


Identifying channels with bad beams
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Some cubes may have channels with beams that vary drastically over small ranges in spectral
channels. This is often the case where a range of channels has poor data or is affected
by radio frequency interference, leading to most of the data in that channel being flagged.
If these channels are kept, the smallest common beam (see :doc:`smoothing`) may be much
larger due to these channels.

You can identify and mask channels with bad beams using
`~spectral_cube.spectral_cube.VaryingResolutionSpectralCube.identify_bad_beams`::

>>> goodbeams = vrsc_cube.identify_bad_beams() # doctest: +SKIP

This will return a 1D boolean mask where `True` means the channel beam is good.
By default,
`~spectral_cube.spectral_cube.VaryingResolutionSpectralCube.identify_bad_beams`
(the returned value is a mask array where ``True`` means the channel is good),
mask channels with undesirable beams using
`~spectral_cube.spectral_cube.VaryingResolutionSpectralCube.mask_out_bad_beams`,
and in general mask out individual channels using
will use
`~spectral_cube.spectral_cube.VaryingResolutionSpectralCube.beam_threshold` (default
of 0.01; see above).
However, the comparison here is to the **median** major and minor axis rather than
the smallest common beam used above.
This is because bad beams are identified as outliers in the set of beams.

To mask the channels with bad beams, use
`~spectral_cube.spectral_cube.VaryingResolutionSpectralCube.with_bad_beams_masked`.


>>> masked_vrsc_cube = vrsc_cube.with_bad_beams_masked() # doctest: +SKIP

The masked cube without the bad beams will now exlude channels with bad beams and
can be used, for example, to convolve to a better representative common beam
resolution (see above).

We also note that, in general, you can mask out individual channels using
`~spectral_cube.spectral_cube.VaryingResolutionSpectralCube.mask_channels`.

For other sorts of operations, discussion of how to deal with these cubes via
smoothing to a common resolution is in the :doc:`smoothing` document.
.. note::

The common beam is not used to find discrepant and bad beams since they are
identified as outliers from the set. We note that this is an approximate method of
finding channels with outlier beams and may fail in some cases. Please
`raise an issue <https://github.com/radio-astro-tools/spectral-cube/issues>`_ if
this method does not work well for your data cube.
7 changes: 5 additions & 2 deletions docs/errors.rst
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ operations spanning the spectral axis, for example ``cube.moment0(axis=0)`` or
This occurs if the beam sizes are different by more than the specified
threshold factor. A default threshold of 1% is set because for most
interferometric images, beam differences on this scale are negligible (they
correspond to flux measurement errors of 10^-4).
correspond to flux measurement errors of 10^-4).

To inspect the beam properties, look at the ``beams`` attribute, for example:

Expand Down Expand Up @@ -54,9 +54,12 @@ There are several options to manage this problem:
.. code::

good_beams = cube.identify_bad_beams(threshold=0.1)
mcube = cube.mask_out_bad_beams(threshold=0.1)
mcube = cube.with_bad_beams_masked(threshold=0.1)


Please see the more detailed explanation of the beam threshold treatment in
:doc:`beam_handling` for more information about this warning.



Moment-2 or FWHM calculations give unexpected NaNs
Expand Down
33 changes: 26 additions & 7 deletions docs/smoothing.rst
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,31 @@ will be different for each slice.

Common Beam selection
^^^^^^^^^^^^^^^^^^^^^
You may want to convolve your cube to the smallest beam that is still larger
than all contained beams. To do this, you can use the
`~radio_beam.Beams.common_beam` tool. For example::

common_beam = cube.beams.common_beam()
new_cube = cube.convolve_to(common_beam)
Many spectral operations on a cube with a varying resolution will require first
convolving to a common resolution. You may want to retain the finest spatial
resolution possible by finding the smallest common beam that encloses all beams
in the cube. To do this, we can use
:meth:`~spectral_cube.VaryingResolutionSpectralCube.set_common_beam`::

cube.set_common_beam()
Copy link
Member

Choose a reason for hiding this comment

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

Is there a reason why we need to make a stateful change on the cube and set common_beam? We could instead do something like:

common_beam = cube.find_common_beam()
cube.convolve_to(common_beam)

? Having it stateful means it could get out of sync if e.g. some channels are masked and so on. In general we might want to avoid stateful changes to cubes if we can?

With find_common_beam above one could even find a common beam with different settings and compare them without having to make a stateful change each time.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The only issue is that the common beam operation can be slow for cubes with many (>1e3) channels. And baked into the checks for deviations is the need to check for variation from the common beam, where we had the average beam before. That's our only check (currently) for whether to ignore small beam changes for operations along the spectral axis.

Allowing a stateful change could also work if we force the beam masking to not be stateful (which I think it can be now)


This method wraps the `~radio_beam.Beams.common_beam`, and keyword arguments for
the common beam algorithm can be passed directly in
:meth:`~spectral_cube.VaryingResolutionSpectralCube.set_common_beam`.
The common beam can then be accessed with::

cube.common_beam

A new common beam will be computed each time
:meth:`~spectral_cube.VaryingResolutionSpectralCube.set_common_beam` is called.
This can be used to make changes to the settings for the common beam algorithm
or if some beams are masked (see :doc:`beam_handling`).

The cube can then be convolved to the smallest common beam using the normal
call for convolution::

new_cube = cube.convolve_to(cube.common_beam)

Sometimes, you'll encounter the error "Could not find common beam to deconvolve
all beams." This is a real issue, as the algorithms we have in hand so far do
Expand All @@ -45,7 +64,7 @@ algorithm to converge to a valid common beam:
`~radio_beam.commonbeam.getMinVolEllipse` code by
passing ``tolerance=1e-5`` to the common beam function::

cube.beams.common_beam(tolerance=1e-5)
cube.set_common_beam(tolerance=1e-5)

Convergence may be met by either increasing or decreasing the tolerance; it
depends on having the algorithm not step within the minimum enclosing ellipse,
Expand All @@ -57,7 +76,7 @@ and will take longer to run.
to overestimate the beam size, ensuring that solutions that are marginally
smaller than the common beam will not be found by the algorithm::

cube.beams.common_beam(epsilon=1e-3)
cube.set_common_beam(epsilon=1e-3)

The default value of ``epsilon=1e-3`` will sample points 0.1% larger than the
edge of each beam in the set. Increasing ``epsilon`` ensures that a valid common
Expand Down
2 changes: 1 addition & 1 deletion setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ install_requires =
dask[array]
joblib
casa-formats-io
scipy

[options.extras_require]
test =
Expand All @@ -34,7 +35,6 @@ novis =
pvextractor
regions ; python_version<'3.8'
reproject
scipy
all =
zarr
fsspec
Expand Down
Loading