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

MAST: Cutouts limit #2693

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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGES.rst
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,10 @@ mast

- Resolved issue making PANSTARRS catalog queries when columns and sorting is specified. [#2727]

- Added warning message for ``Tesscut`` requests when input cutout size reaches a
limit of 30 pixels in either dimension, and enables users to modify the request
timeout upper limit from the default 600 seconds. [#2693]

nist
^^^^

Expand Down
91 changes: 85 additions & 6 deletions astroquery/mast/cutouts.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,9 @@
from astropy.table import Table
from astropy.io import fits

from ..exceptions import InputWarning, NoResultsWarning, InvalidQueryError
from . import conf
from .. import log
from ..exceptions import InputWarning, LargeQueryWarning, NoResultsWarning, InvalidQueryError

from .utils import parse_input_location
from .core import MastQueryWithLogin
Expand All @@ -34,7 +36,7 @@
__all__ = ["TesscutClass", "Tesscut", "ZcutClass", "Zcut"]


def _parse_cutout_size(size):
def _parse_cutout_size(size, timeout=None, mission=None):
"""
Take a user input cutout size and parse it into the regular format
[ny,nx] where nx/ny are quantities with units either pixels or degrees.
Expand All @@ -48,6 +50,17 @@
``(ny, nx)`` order. Scalar numbers in ``size`` are assumed to be in
units of pixels. `~astropy.units.Quantity` objects must be in pixel or
angular units.
mission : str, optional
The mission for which the size parsing is being done. This parameter
is mainly meant to trigger a cutout size warning specifically for TESSCut
requests. Default is None.
timeout : int or float, optional
The modified request timeout limit.
The request processing time by default is 600 seconds, meaning an attempt at communicating
with the API will take 600 seconds before timing out. In the context of this function, this
parameter is meant to keep track of whether or not the timeout limit has been modified, which
will affect whether or not a warning message about the cutout size gets triggered.
Default is None.

Returns
-------
Expand All @@ -56,35 +69,67 @@
either pixels or degrees.
"""

# This local variable will change to True if input cutout size exceeds recommended limits for TESS
limit_reached = False

# Checking 2d size inputs for the recommended cutout size
if (mission == 'TESS') & (not isinstance(size, (int, float, u.Quantity))):
if len(size) == 2:
if np.isscalar(size[0]):
size = [size[0] * u.pixel, size[1] * u.pixel]

Check warning on line 79 in astroquery/mast/cutouts.py

View check run for this annotation

Codecov / codecov/patch

astroquery/mast/cutouts.py#L77-L79

Added lines #L77 - L79 were not covered by tests
Copy link
Contributor Author

@jaymedina jaymedina Apr 6, 2023

Choose a reason for hiding this comment

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

I convert the individual elements in size to pixels rather than the whole data structure because unit conversion for the whole data structure happens 3 lines below, so doing that twice for the pixel unit case would result in a unit of pix^2 which leads to incompatible dimensions being compared. This is the error that comes up:

image

Copy link
Member

Choose a reason for hiding this comment

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

yes, I would think you don't need the size[0].unit in your example and then it would work (you already have that in the screenshot below)


with u.set_enabled_equivalencies(u.pixel_scale(21 * u.arcsec / u.pixel)):
limit_reached = (size * size[0].unit > 30 * u.pixel).any()

Check warning on line 82 in astroquery/mast/cutouts.py

View check run for this annotation

Codecov / codecov/patch

astroquery/mast/cutouts.py#L81-L82

Added lines #L81 - L82 were not covered by tests
Copy link
Contributor Author

@jaymedina jaymedina Apr 6, 2023

Choose a reason for hiding this comment

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

For set_enabled_equivalencies, the entire list needs to be assigned a unit rather than the individual elements, in order for a conversion to take place. Otherwise this line results in an error.

image

Copy link
Member

Choose a reason for hiding this comment

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

here you could put the list into a quantity and then do the check.

size2 = u.Quantity(size2)

So, maybe distinguish in the isinstance conditional between Quantity and not yet quantity inputs?


# Making size into an array [ny, nx]
if np.isscalar(size):
jaymedina marked this conversation as resolved.
Show resolved Hide resolved
size = np.repeat(size, 2)

if mission == 'TESS':
limit_reached = (size > 30).any()

if isinstance(size, u.Quantity):
size = np.atleast_1d(size)

if len(size) == 1:
size = np.repeat(size, 2)

# Based on the literature, TESS resolution is approx. 21 arcseconds per pixel.
# We will convert the recommended upper limit for a dimension from pixels
# to the unit being passed.
if mission == 'TESS':
with u.set_enabled_equivalencies(u.pixel_scale(21 * u.arcsec / u.pixel)):
limit_reached = (size > 30 * u.pixel).any()

Check warning on line 102 in astroquery/mast/cutouts.py

View check run for this annotation

Codecov / codecov/patch

astroquery/mast/cutouts.py#L100-L102

Added lines #L100 - L102 were not covered by tests

if len(size) > 2:
warnings.warn("Too many dimensions in cutout size, only the first two will be used.",
InputWarning)

# Getting x and y out of the size

if np.isscalar(size[0]):
x = size[1]
y = size[0]
units = "px"

elif size[0].unit == u.pixel:
x = size[1].value
y = size[0].value
units = "px"

elif size[0].unit.physical_type == 'angle':
x = size[1].to(u.deg).value
y = size[0].to(u.deg).value
units = "d"

else:
raise InvalidQueryError("Cutout size must be in pixels or angular quantity.")

if (limit_reached) & (not timeout):
warnings.warn("You have selected a large cutout size that may result in a timeout error. We suggest limiting"

Check warning on line 129 in astroquery/mast/cutouts.py

View check run for this annotation

Codecov / codecov/patch

astroquery/mast/cutouts.py#L129

Added line #L129 was not covered by tests
" the size of your requested cutout, or changing the request timeout limit from its"
" default 600 seconds to something higher, using the timeout argument.", LargeQueryWarning)

return {"x": x, "y": y, "units": units}


Expand All @@ -108,6 +153,7 @@

def get_sectors(self, *, coordinates=None, radius=0*u.deg, product='SPOC', objectname=None,
moving_target=False, mt_type=None):

"""
Get a list of the TESS data sectors whose footprints intersect
with the given search area.
Expand Down Expand Up @@ -223,7 +269,8 @@
return Table(sector_dict)

def download_cutouts(self, *, coordinates=None, size=5, sector=None, product='SPOC', path=".",
inflate=True, objectname=None, moving_target=False, mt_type=None, verbose=False):
inflate=True, objectname=None, moving_target=False, mt_type=None, verbose=False,
timeout=None):
"""
Download cutout target pixel file(s) around the given coordinates with indicated size.

Expand Down Expand Up @@ -280,12 +327,24 @@
first majorbody is tried and then smallbody if a matching majorbody is not found.

NOTE: If moving_target is supplied, this argument is ignored.
timeout : int or float, optional
The modified request timeout limit.
The request processing time by default is 600 seconds, meaning an attempt at communicating
with the API will take 600 seconds before timing out. The timeout upper limit can be modified
using this argument for large cutout requests via TESSCut. Default is None.

Returns
-------
response : `~astropy.table.Table`
"""

# Modify TIMEOUT attribute if necessary (usually this is modified for large requests)
if timeout:
default_timeout = conf.timeout
self._service_api_connection.TIMEOUT = timeout
log.info(f"Request timeout upper limit is being changed to {self._service_api_connection.TIMEOUT}"

Check warning on line 345 in astroquery/mast/cutouts.py

View check run for this annotation

Codecov / codecov/patch

astroquery/mast/cutouts.py#L343-L345

Added lines #L343 - L345 were not covered by tests
" seconds.")

if moving_target:

# The Moving Targets service is currently only available for SPOC
Expand Down Expand Up @@ -315,7 +374,7 @@
astrocut_request = f"astrocut?ra={coordinates.ra.deg}&dec={coordinates.dec.deg}"

# Adding the arguments that are common between moving/still astrocut requests
size_dict = _parse_cutout_size(size)
size_dict = _parse_cutout_size(size, timeout=timeout, mission='TESS')
astrocut_request += f"&y={size_dict['y']}&x={size_dict['x']}&units={size_dict['units']}"

# Making sure input product is either SPOC or TICA,
Expand Down Expand Up @@ -356,10 +415,14 @@
os.remove(zipfile_path)

localpath_table['Local Path'] = [path+x for x in cutout_files]

if timeout:
self._service_api_connection.TIMEOUT = default_timeout

Check warning on line 420 in astroquery/mast/cutouts.py

View check run for this annotation

Codecov / codecov/patch

astroquery/mast/cutouts.py#L420

Added line #L420 was not covered by tests

return localpath_table

def get_cutouts(self, *, coordinates=None, size=5, product='SPOC', sector=None,
objectname=None, moving_target=False, mt_type=None):
objectname=None, moving_target=False, mt_type=None, timeout=None):
"""
Get cutout target pixel file(s) around the given coordinates with indicated size,
and return them as a list of `~astropy.io.fits.HDUList` objects.
Expand Down Expand Up @@ -408,14 +471,26 @@
first majorbody is tried and then smallbody if a matching majorbody is not found.

NOTE: If moving_target is supplied, this argument is ignored.
timeout : int or float, optional
The modified request timeout limit.
The request processing time by default is 600 seconds, meaning an attempt at communicating
with the API will take 600 seconds before timing out. The timeout upper limit can be modified
using this argument for large cutout requests via TESSCut. Default is None.

Returns
-------
response : A list of `~astropy.io.fits.HDUList` objects.
"""

# Modify TIMEOUT attribute if necessary (usually this is modified for large requests)
if timeout:
default_timeout = conf.timeout
self._service_api_connection.TIMEOUT = timeout
log.info(f"Request timeout upper limit is being changed to {self._service_api_connection.TIMEOUT}"

Check warning on line 489 in astroquery/mast/cutouts.py

View check run for this annotation

Codecov / codecov/patch

astroquery/mast/cutouts.py#L487-L489

Added lines #L487 - L489 were not covered by tests
" seconds.")

# Setting up the cutout size
param_dict = _parse_cutout_size(size)
param_dict = _parse_cutout_size(size, timeout=timeout, mission='TESS')

# Add sector if present
if sector:
Expand Down Expand Up @@ -485,6 +560,9 @@
# preserve the original filename in the fits object
cutout_hdus_list[-1].filename = name

if timeout:
self._service_api_connection.TIMEOUT = default_timeout

Check warning on line 564 in astroquery/mast/cutouts.py

View check run for this annotation

Codecov / codecov/patch

astroquery/mast/cutouts.py#L564

Added line #L564 was not covered by tests

return cutout_hdus_list


Expand Down Expand Up @@ -595,6 +673,7 @@
response : `~astropy.table.Table`
Cutout file(s) for given coordinates
"""

# Get Skycoord object for coordinates/object
coordinates = parse_input_location(coordinates)
size_dict = _parse_cutout_size(size)
Expand Down
24 changes: 21 additions & 3 deletions astroquery/mast/tests/test_mast_remote.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@
from astroquery import mast

from ..utils import ResolverError
from ...exceptions import (InputWarning, InvalidQueryError, MaxResultsWarning,
NoResultsWarning)
from ...exceptions import (InputWarning, InvalidQueryError, LargeQueryWarning,
MaxResultsWarning, NoResultsWarning)


OBSID = '1647157'
Expand Down Expand Up @@ -949,7 +949,7 @@ def test_tesscut_download_cutouts_mt(self, tmpdir):
assert error_tica_mt in str(error_msg.value)

@pytest.mark.parametrize("product", ["tica", "spoc"])
def test_tesscut_get_cutouts(self, product):
def test_tesscut_get_cutouts(self, product, caplog):

coord = SkyCoord(107.18696, -70.50919, unit="deg")

Expand All @@ -975,6 +975,14 @@ def test_tesscut_get_cutouts(self, product):
assert len(cutout_hdus_list) >= 1
assert isinstance(cutout_hdus_list[0], fits.HDUList)

# Check that an INFO message is returned when timeout is adjusted
mast.Tesscut.get_cutouts(product=product, coordinates=coord, size=5, timeout=1000)
jaymedina marked this conversation as resolved.
Show resolved Hide resolved
with caplog.at_level("INFO", logger="astroquery"):
assert "timeout upper limit is being changed" in caplog.text

# Ensure that timeout returns to default (600 seconds) after adjusted in previous call
assert mast.Tesscut._service_api_connection.TIMEOUT == 600

def test_tesscut_get_cutouts_mt(self):

# Moving target functionality testing
Expand Down Expand Up @@ -1027,6 +1035,16 @@ def test_tesscut_get_cutouts_mt(self):
moving_target=True)
assert error_tica_mt in str(error_msg.value)

@pytest.mark.xfail(raises=LargeQueryWarning)
@pytest.mark.parametrize("product", ["tica", "spoc"])
@pytest.mark.parametrize("size", [31, [5, 60], 0.2 * u.deg, [0.1 * u.deg, 0.2 * u.deg],
5000 * u.arcsec, 20 * u.arcmin])
def test_tesscut_timeout_param(self, product, size):

# Check that a warning comes up when cutout size too big
coordinates = '60 60'
mast.Tesscut.get_cutouts(product=product, coordinates=coordinates, size=size)

###################
# ZcutClass tests #
###################
Expand Down
23 changes: 23 additions & 0 deletions docs/mast/mast.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1065,6 +1065,29 @@ and because the TICA products are not available for sectors 1-26, we request cut
----------------------------------------------------------
./tica-s0027-4-2_107.186960_-70.509190_21x14_astrocut.fits

It is important to be mindful of the requested cutout size when using either `~astroquery.mast.TesscutClass.download_cutouts` or `~astroquery.mast.TesscutClass.get_cutouts`,
as it will affect the time it takes to retrieve your cutouts. By default, any request that ``astroquery.mast`` makes to an
API is capped at 600 seconds. Queries that take longer than this will yield a timeout error.
The recommended cutout size for TESSCut is no larger than 30 pixels in either the X or Y
direction, so a user will be met with a warning message if the input cutout size exceeds
those limits. Below is an example of a request using `~astroquery.mast.TesscutClass.get_cutouts` for cutouts of size 0.2 x 0.2 degrees-squared, which is
around 34 x 34 pixels-squared.

.. doctest-skip::

>>> import astropy.units as u
>>> from astroquery.mast import Tesscut
>>> from astropy.coordinates import SkyCoord
...
>>> cutout_coord = SkyCoord(107.18696, -70.50919, unit="deg")
>>> hdulist = Tesscut.get_cutouts(coordinates=cutout_coord, size=0.2*u.deg)
WARNING: LargeQueryWarning: You have selected a large cutout size that may result in a timeout error.
We suggest limiting the size of your requested cutout, or changing the request timeout limit from
its default 600 seconds to something higher, using the timeout argument. [astroquery.mast.cutouts]

At this point, users may choose to decrease their cutout size or extend the request timeout limit from
600 seconds to something longer.

Sector information
------------------

Expand Down