diff --git a/CHANGES.rst b/CHANGES.rst index e571f02403..562a188a17 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -19,6 +19,13 @@ gaia - Rename datalink retrieval types EPOCH_SPECTRUM_RVS, EPOCH_SPECTRUM_XP_SSO, EPOCH_SPECTRUM_XP_CROWDING, MEAN_SPECTRUM_XP, EPOCH_SPECTRUM_XP and MEAN_SPECTRUM_XP_GRAVLENS. [#3382] +mast +^^^^ + +- Deprecated the ``product`` parameter in the ``Tesscut.get_sectors``, ``Tesscut.get_cutouts``, and ``Tesscut.download_cutouts`` methods. + Support for TESS Image Calibration (TICA) high-level science products has been removed; only Science Processing Operations Center (SPOC) + products are now supported. [#3391] + Service fixes and enhancements ------------------------------ diff --git a/astroquery/mast/cutouts.py b/astroquery/mast/cutouts.py index 2da8833dbf..8ce8a342ce 100644 --- a/astroquery/mast/cutouts.py +++ b/astroquery/mast/cutouts.py @@ -8,25 +8,21 @@ """ - import warnings import time import json import zipfile import os - from io import BytesIO import numpy as np - import astropy.units as u from astropy.coordinates import Angle - from astropy.table import Table from astropy.io import fits +from astropy.utils.decorators import deprecated_renamed_argument from ..exceptions import InputWarning, NoResultsWarning, InvalidQueryError - from .utils import parse_input_location from .core import MastQueryWithLogin @@ -96,9 +92,7 @@ class TesscutClass(MastQueryWithLogin): """ def __init__(self): - super().__init__() - services = {"sector": {"path": "sector"}, "astrocut": {"path": "astrocut"}, "mt_sector": {"path": "moving_target/sector"}, @@ -106,6 +100,65 @@ def __init__(self): } self._service_api_connection.set_service_params(services, "tesscut") + def _validate_target_input(self, coordinates, objectname, moving_target): + """ + Validate the input parameters for target selection. + + Parameters + ---------- + coordinates : str or `astropy.coordinates` object, optional + The target around which to search. It may be specified as a + string or as the appropriate `astropy.coordinates` object. + objectname : str, optional + The target around which to search, by name (objectname="M104") + or TIC ID (objectname="TIC 141914082"). If moving_target is True, input must be the name or ID + (as understood by the `JPL ephemerides service `__) + of a moving target such as an asteroid or comet. + moving_target : bool, optional + Indicate whether the object is a moving target or not. Default is set to False, in other words, + not a moving target. + + Raises + ------- + InvalidQueryError + If ``moving_target`` is True and ``coordinates`` is provided. + If ``moving_target`` is True and ``objectname`` is not provided. + If both ``coordinates`` and ``objectname`` are provided. + """ + if moving_target: + if coordinates: + raise InvalidQueryError("Only one of moving_target and coordinates may be specified. " + "Please remove coordinates if using moving_target and objectname.") + + if not objectname: + raise InvalidQueryError("Please specify the object name or ID (as understood by the " + "`JPL ephemerides service `__) " + "of a moving target such as an asteroid or comet.") + else: + if coordinates and objectname: + raise InvalidQueryError("Only one of objectname and coordinates may be specified. " + "Please remove objectname if using coordinates.") + + def _validate_product(self, product): + """ + Validate the product type. "SPOC" is the only valid option. + + Parameters + ---------- + product : str + The product type to validate. Must be "SPOC". + + Raises + ------- + InvalidQueryError + If the product is not "SPOC". + """ + if product.upper() != "SPOC": + raise InvalidQueryError("Input product must be SPOC.") + + @deprecated_renamed_argument('product', None, since='0.4.11', message='Tesscut no longer supports operations on ' + 'TESS Image Calibrator (TICA) products. ' + 'The `product` argument is deprecated and will be removed in a future version.') def get_sectors(self, *, coordinates=None, radius=0*u.deg, product='SPOC', objectname=None, moving_target=False, mt_type=None, resolver=None): """ @@ -128,13 +181,9 @@ def get_sectors(self, *, coordinates=None, radius=0*u.deg, product='SPOC', objec NOTE: If moving_target is supplied, this argument is ignored. product : str - Default is 'SPOC'. - The product whose sectors will be returned. Options are: 'SPOC' or 'TICA', for the Science Processing - Operations Center (SPOC) products, and the TESS Image CAlibration (TICA) high-level science products, - respectively. TICA products will usually be available for the latest sectors sooner than their SPOC - counterparts, but are not available for sectors 1-26. - - NOTE: TICA is currently not available for moving targets. + Deprecated. Default is 'SPOC'. + The product that the cutouts will be made out of. The only valid value for this parameter is 'SPOC', for the + Science Processing Operations Center (SPOC) products. objectname : str, optional The target around which to search, by name (objectname="M104") or TIC ID (objectname="TIC 141914082"). If moving_target is True, input must be the name or ID @@ -163,72 +212,41 @@ def get_sectors(self, *, coordinates=None, radius=0*u.deg, product='SPOC', objec response : `~astropy.table.Table` Sector/camera/chip information for given coordinates/objectname/moving_target. """ + self._validate_product(product) + self._validate_target_input(coordinates, objectname, moving_target) if moving_target: - - # The Moving Targets service is currently only available for SPOC - if product.upper() != "SPOC": - raise InvalidQueryError("Only SPOC is available for moving targets queries.") - - # Check that objectname has been passed in and coordinates - # is not - if coordinates: - raise InvalidQueryError("Only one of moving_target and coordinates may be specified. " - "Please remove coordinates if using moving_target and objectname.") - - if not objectname: - raise InvalidQueryError("Please specify the object name or ID (as understood by the " - "`JPL ephemerides service `__) " - "of a moving target such as an asteroid or comet.") - - params = {"product": product.upper(), "obj_id": objectname} - - # Add optional parameter is present - if mt_type: + params = {"obj_id": objectname} + if mt_type: # Add optional parameter if present params["obj_type"] = mt_type - - response = self._service_api_connection.service_request_async("mt_sector", params) - + service = "mt_sector" else: - # Get Skycoord object for coordinates/object coordinates = parse_input_location(coordinates=coordinates, objectname=objectname, resolver=resolver) - # If radius is just a number we assume degrees radius = Angle(radius, u.deg) - - # Making sure input product is either SPOC or TICA - if product.upper() not in ['TICA', 'SPOC']: - raise InvalidQueryError("Input product must either be SPOC or TICA.") - params = {"ra": coordinates.ra.deg, "dec": coordinates.dec.deg, - "radius": radius.deg, - "product": product.upper()} - - response = self._service_api_connection.service_request_async("sector", params) - - # Raise any errors - response.raise_for_status() + "radius": radius.deg} + service = "sector" - sector_json = response.json()['results'] - sector_dict = {'sectorName': [], - 'sector': [], - 'camera': [], - 'ccd': []} - - for entry in sector_json: - sector_dict['sectorName'].append(entry['sectorName']) - sector_dict['sector'].append(int(entry['sector'])) - sector_dict['camera'].append(int(entry['camera'])) - sector_dict['ccd'].append(int(entry['ccd'])) + response = self._service_api_connection.service_request_async(service, params) + response.raise_for_status() # Raise any errors + sector_json = response.json().get("results", []) - if not len(sector_json): + if not sector_json: warnings.warn("Coordinates are not in any TESS sector.", NoResultsWarning) - return Table(sector_dict) + return Table(rows=[ + (entry['sectorName'], int(entry['sector']), int(entry['camera']), int(entry['ccd'])) + for entry in sector_json + ], names=["sectorName", "sector", "camera", "ccd"]) + + @deprecated_renamed_argument('product', None, since='0.4.11', message='Tesscut no longer supports operations on ' + 'TESS Image Calibrator (TICA) products. ' + 'The `product` argument is deprecated and will be removed in a future version.') def download_cutouts(self, *, coordinates=None, size=5, sector=None, product='SPOC', path=".", inflate=True, objectname=None, moving_target=False, mt_type=None, resolver=None, verbose=False): @@ -255,13 +273,9 @@ def download_cutouts(self, *, coordinates=None, size=5, sector=None, product='SP The TESS sector to return the cutout from. If not supplied, cutouts from all available sectors on which the coordinate appears will be returned. product : str - Default is 'SPOC'. - The product that the cutouts will be made out of. Options are: 'SPOC' or 'TICA', for the Science Processing - Operations Center (SPOC) products, and the TESS Image CAlibration (TICA) high-level science products, - respectively. TICA products will usually be available for the latest sectors sooner than their SPOC - counterparts, but are not available for sectors 1-26. - - NOTE: TICA is currently not available for moving targets. + Deprecated. Default is 'SPOC'. + The product that the cutouts will be made out of. The only valid value for this parameter is 'SPOC', for the + Science Processing Operations Center (SPOC) products. path : str Optional. The directory in which the cutouts will be saved. @@ -298,53 +312,30 @@ def download_cutouts(self, *, coordinates=None, size=5, sector=None, product='SP ------- response : `~astropy.table.Table` """ + self._validate_product(product) + self._validate_target_input(coordinates, objectname, moving_target) + params = _parse_cutout_size(size) - if moving_target: - - # The Moving Targets service is currently only available for SPOC - if product.upper() != "SPOC": - raise InvalidQueryError("Only SPOC is available for moving targets queries.") - - # Check that objectname has been passed in and coordinates - # is not - if coordinates: - raise InvalidQueryError("Only one of moving_target and coordinates may be specified. " - "Please remove coordinates if using moving_target and objectname.") - - if not objectname: - raise InvalidQueryError("Please specify the object name or ID (as understood by the " - "`JPL ephemerides service `__) " - "of a moving target such as an asteroid or comet.") - - astrocut_request = f"moving_target/astrocut?obj_id={objectname}&product={product.upper()}" - if mt_type: - astrocut_request += f"&obj_type={mt_type}" + if sector: + params["sector"] = sector + if moving_target: + params["obj_id"] = objectname + if mt_type: # Add optional parameter if present + params["obj_type"] = mt_type + request_path = "moving_target/astrocut" else: - - # Get Skycoord object for coordinates/object coordinates = parse_input_location(coordinates=coordinates, objectname=objectname, resolver=resolver) + params.update({"ra": coordinates.ra.deg, "dec": coordinates.dec.deg}) + request_path = "astrocut" - astrocut_request = f"astrocut?ra={coordinates.ra.deg}&dec={coordinates.dec.deg}" + query = "&".join(f"{key}={value}" for key, value in params.items()) + astrocut_url = f"{self._service_api_connection.REQUEST_URL}{request_path}?{query}" - # Adding the arguments that are common between moving/still astrocut requests - size_dict = _parse_cutout_size(size) - astrocut_request += f"&y={size_dict['y']}&x={size_dict['x']}&units={size_dict['units']}" - - # Making sure input product is either SPOC or TICA, - # and adding the argument to the request URL - if product.upper() not in ['TICA', 'SPOC']: - raise InvalidQueryError("Input product must either be SPOC or TICA.") - astrocut_request += f"&product={product.upper()}" - - if sector: - astrocut_request += "§or={}".format(sector) - - astrocut_url = self._service_api_connection.REQUEST_URL + astrocut_request - path = os.path.join(path, '') - zipfile_path = "{}tesscut_{}.zip".format(path, time.strftime("%Y%m%d%H%M%S")) + os.makedirs(path, exist_ok=True) # Ensure the path exists + zipfile_path = os.path.join(path, f"tesscut_{time.strftime('%Y%m%d%H%M%S')}.zip") self._download_file(astrocut_url, zipfile_path) localpath_table = Table(names=["Local Path"], dtype=[str]) @@ -363,16 +354,18 @@ def download_cutouts(self, *, coordinates=None, size=5, sector=None, product='SP if verbose: print("Inflating...") - # unzipping the zipfile - zip_ref = zipfile.ZipFile(zipfile_path, 'r') - cutout_files = zip_ref.namelist() - zip_ref.extractall(path, members=cutout_files) - zip_ref.close() + # Unzipping the zipfile + with zipfile.ZipFile(zipfile_path, 'r') as zip_ref: + cutout_files = zip_ref.namelist() + zip_ref.extractall(path, members=cutout_files) os.remove(zipfile_path) - localpath_table['Local Path'] = [path+x for x in cutout_files] + localpath_table['Local Path'] = [os.path.join(path, file) for file in cutout_files] return localpath_table + @deprecated_renamed_argument('product', None, since='0.4.11', message='Tesscut no longer supports operations on ' + 'TESS Image Calibrator (TICA) products. ' + 'The `product` argument is deprecated and will be removed in a future version.') def get_cutouts(self, *, coordinates=None, size=5, product='SPOC', sector=None, objectname=None, moving_target=False, mt_type=None, resolver=None): """ @@ -395,13 +388,9 @@ def get_cutouts(self, *, coordinates=None, size=5, product='SPOC', sector=None, units of pixels. `~astropy.units.Quantity` objects must be in pixel or angular units. product : str - Default is 'SPOC'. - The product that the cutouts will be made out of. Options are: 'SPOC' or 'TICA', for the Science Processing - Operations Center (SPOC) products, and the TESS Image CAlibration (TICA) high-level science products, - respectively. TICA products will usually be available for the latest sectors sooner than their SPOC - counterparts, but are not available for sectors 1-26. - - NOTE: TICA is currently not available for moving targets. + Deprecated. Default is 'SPOC'. + The product that the cutouts will be made out of. The only valid value for this parameter is 'SPOC', for the + Science Processing Operations Center (SPOC) products. sector : int Optional. The TESS sector to return the cutout from. If not supplied, cutouts @@ -433,82 +422,47 @@ def get_cutouts(self, *, coordinates=None, size=5, product='SPOC', sector=None, ------- response : A list of `~astropy.io.fits.HDUList` objects. """ + self._validate_product(product) + self._validate_target_input(coordinates, objectname, moving_target) - # Setting up the cutout size - param_dict = _parse_cutout_size(size) - - # Add sector if present + params = _parse_cutout_size(size) if sector: - param_dict["sector"] = sector + params["sector"] = sector if moving_target: - - # The Moving Targets service is currently only available for SPOC - if product.upper() != "SPOC": - raise InvalidQueryError("Only SPOC is available for moving targets queries.") - - param_dict['product'] = product.upper() - - # Check that objectname has been passed in and coordinates - # is not - if coordinates: - raise InvalidQueryError("Only one of moving_target and coordinates may be specified. " - "Please remove coordinates if using moving_target and objectname.") - - if not objectname: - raise InvalidQueryError("Please specify the object name or ID (as understood by the " - "`JPL ephemerides service `__) " - "of a moving target such as an asteroid or comet.") - - param_dict["obj_id"] = objectname - - # Add optional parameter if present - if mt_type: - param_dict["obj_type"] = mt_type - - response = self._service_api_connection.service_request_async("mt_astrocut", param_dict) - + params["obj_id"] = objectname + if mt_type: # Add optional parameter if present + params["obj_type"] = mt_type + service = "mt_astrocut" else: - - # Making sure input product is either SPOC or TICA, then add the `product` param - if product.upper() not in ['TICA', 'SPOC']: - raise InvalidQueryError("Input product must either be SPOC or TICA.") - - param_dict['product'] = product.upper() - # Get Skycoord object for coordinates/object coordinates = parse_input_location(coordinates=coordinates, objectname=objectname, resolver=resolver) + params.update({"ra": coordinates.ra.deg, "dec": coordinates.dec.deg}) + service = "astrocut" - param_dict["ra"] = coordinates.ra.deg - param_dict["dec"] = coordinates.dec.deg - - response = self._service_api_connection.service_request_async("astrocut", param_dict) - + response = self._service_api_connection.service_request_async(service, params) response.raise_for_status() # Raise any errors try: - ZIPFILE = zipfile.ZipFile(BytesIO(response.content), 'r') + with zipfile.ZipFile(BytesIO(response.content), 'r') as zf: + cutouts = [] + for name in zf.namelist(): + # Open all the contained fits files: + # Since we cannot seek on a compressed zip file, + # we have to read the data, wrap it in another BytesIO object, + # and then open that using fits.open + file_data = BytesIO(zf.read(name)) + hdulist = fits.open(file_data) + hdulist.filename = name # preserve the original filename in the fits object + cutouts.append(hdulist) + return cutouts except zipfile.BadZipFile: message = response.json() warnings.warn(message['msg'], NoResultsWarning) return [] - # Open all the contained fits files: - # Since we cannot seek on a compressed zip file, - # we have to read the data, wrap it in another BytesIO object, - # and then open that using fits.open - cutout_hdus_list = [] - for name in ZIPFILE.namelist(): - CUTOUT = BytesIO(ZIPFILE.open(name).read()) - cutout_hdus_list.append(fits.open(CUTOUT)) - - # preserve the original filename in the fits object - cutout_hdus_list[-1].filename = name - - return cutout_hdus_list - Tesscut = TesscutClass() diff --git a/astroquery/mast/tests/test_mast.py b/astroquery/mast/tests/test_mast.py index d0f3a118fb..8c5ba17d25 100644 --- a/astroquery/mast/tests/test_mast.py +++ b/astroquery/mast/tests/test_mast.py @@ -15,6 +15,7 @@ import astropy.units as u from requests import HTTPError, Response +from astropy.utils.exceptions import AstropyDeprecationWarning from astroquery.mast.services import _json_to_table from astroquery.utils.mocks import MockResponse from astroquery.exceptions import (InvalidQueryError, InputWarning, MaxResultsWarning, NoResultsWarning, @@ -1012,7 +1013,8 @@ def test_tesscut_get_sector(patch_post): # Exercising the search by moving target sector_table = mast.Tesscut.get_sectors(objectname="Ceres", - moving_target=True) + moving_target=True, + mt_type='small_body') assert isinstance(sector_table, Table) assert len(sector_table) == 1 assert sector_table['sectorName'][0] == "tess-s0001-1-3" @@ -1020,26 +1022,31 @@ def test_tesscut_get_sector(patch_post): assert sector_table['camera'][0] == 1 assert sector_table['ccd'][0] == 3 + # Invalid queries # Testing catch for multiple designators' error_str = ("Only one of moving_target and coordinates may be specified. " "Please remove coordinates if using moving_target and objectname.") - with pytest.raises(InvalidQueryError) as invalid_query: mast.Tesscut.get_sectors(objectname='Ceres', moving_target=True, coordinates=coord) assert error_str in str(invalid_query.value) - # Testing invalid queries - with pytest.raises(InvalidQueryError) as invalid_query: - mast.Tesscut.get_sectors(objectname="M101", product="spooc") - assert "Input product must either be SPOC or TICA." in str(invalid_query.value) + # Error when no object name with moving target + with pytest.raises(InvalidQueryError, match='Please specify the object name or ID'): + mast.Tesscut.get_sectors(moving_target=True) + # Error when both object name and coordinates are specified + with pytest.raises(InvalidQueryError, match='Please remove objectname if using coordinates'): + mast.Tesscut.get_sectors(objectname='Ceres', coordinates=coord) + + # Testing invalid queries + # Invalid product type with pytest.raises(InvalidQueryError) as invalid_query: - mast.Tesscut.get_sectors(objectname="M101", product="TICA", moving_target=True) - assert "Only SPOC is available for moving targets queries." in str(invalid_query.value) + with pytest.warns(AstropyDeprecationWarning, match="Tesscut no longer supports"): + mast.Tesscut.get_sectors(objectname="M101", product="spooc") + assert "Input product must be SPOC." in str(invalid_query.value) def test_tesscut_download_cutouts(patch_post, tmpdir): - coord = SkyCoord(107.27, -70.0, unit="deg") # Testing with inflate @@ -1067,6 +1074,8 @@ def test_tesscut_download_cutouts(patch_post, tmpdir): # Exercising the search by moving target manifest = mast.Tesscut.download_cutouts(objectname="Eleonora", moving_target=True, + mt_type='small_body', + sector=1, size=5, path=str(tmpdir)) assert isinstance(manifest, Table) @@ -1088,16 +1097,12 @@ def test_tesscut_download_cutouts(patch_post, tmpdir): # Testing invalid queries with pytest.raises(InvalidQueryError) as invalid_query: - mast.Tesscut.download_cutouts(objectname="M101", product="spooc") - assert "Input product must either be SPOC or TICA." in str(invalid_query.value) - - with pytest.raises(InvalidQueryError) as invalid_query: - mast.Tesscut.download_cutouts(objectname="M101", product="TICA", moving_target=True) - assert "Only SPOC is available for moving targets queries." in str(invalid_query.value) + with pytest.warns(AstropyDeprecationWarning, match="Tesscut no longer supports"): + mast.Tesscut.download_cutouts(objectname="M101", product="spooc") + assert "Input product must be SPOC." in str(invalid_query.value) def test_tesscut_get_cutouts(patch_post, tmpdir): - coord = SkyCoord(107.27, -70.0, unit="deg") cutout_hdus_list = mast.Tesscut.get_cutouts(coordinates=coord, size=5) assert isinstance(cutout_hdus_list, list) @@ -1113,6 +1118,8 @@ def test_tesscut_get_cutouts(patch_post, tmpdir): # Exercising the search by object name cutout_hdus_list = mast.Tesscut.get_cutouts(objectname='Eleonora', moving_target=True, + mt_type='small_body', + sector=1, size=5) assert isinstance(cutout_hdus_list, list) assert len(cutout_hdus_list) == 1 @@ -1131,12 +1138,9 @@ def test_tesscut_get_cutouts(patch_post, tmpdir): # Testing invalid queries with pytest.raises(InvalidQueryError) as invalid_query: - mast.Tesscut.get_cutouts(objectname="M101", product="spooc") - assert "Input product must either be SPOC or TICA." in str(invalid_query.value) - - with pytest.raises(InvalidQueryError) as invalid_query: - mast.Tesscut.get_cutouts(objectname="M101", product="TICA", moving_target=True) - assert "Only SPOC is available for moving targets queries." in str(invalid_query.value) + with pytest.warns(AstropyDeprecationWarning, match="Tesscut no longer supports"): + mast.Tesscut.get_cutouts(objectname="M101", product="spooc") + assert "Input product must be SPOC." in str(invalid_query.value) ###################### diff --git a/astroquery/mast/tests/test_mast_remote.py b/astroquery/mast/tests/test_mast_remote.py index f377f39d07..a9536c03d0 100644 --- a/astroquery/mast/tests/test_mast_remote.py +++ b/astroquery/mast/tests/test_mast_remote.py @@ -1330,31 +1330,25 @@ def test_catalogs_download_hsc_spectra(self, tmpdir): # TesscutClass tests # ###################### - @pytest.mark.parametrize("product", ["tica", "spoc"]) - def test_tesscut_get_sectors(self, product): + def test_tesscut_get_sectors(self): def check_sector_table(sector_table): assert isinstance(sector_table, Table) assert len(sector_table) >= 1 - assert f"{name}-s00" in sector_table['sectorName'][0] + assert "tess-s00" in sector_table['sectorName'][0] assert sector_table['sector'][0] > 0 assert sector_table['camera'][0] > 0 assert sector_table['ccd'][0] > 0 coord = SkyCoord(349.62609, -47.12424, unit="deg") - name = "tess" if product == "spoc" else product - sector_table = Tesscut.get_sectors(coordinates=coord, product=product) + sector_table = Tesscut.get_sectors(coordinates=coord) check_sector_table(sector_table) - sector_table = Tesscut.get_sectors(objectname="M104", product=product) + sector_table = Tesscut.get_sectors(objectname="M104") check_sector_table(sector_table) def test_tesscut_get_sectors_mt(self): - # Moving target functionality testing - - coord = SkyCoord(349.62609, -47.12424, unit="deg") moving_target_name = 'Eleonora' - sector_table = Tesscut.get_sectors(objectname=moving_target_name, moving_target=True) assert isinstance(sector_table, Table) @@ -1364,45 +1358,12 @@ def test_tesscut_get_sectors_mt(self): assert sector_table['camera'][0] == 1 assert sector_table['ccd'][0] == 1 - error_noname = ("Please specify the object name or ID (as understood by the " - "`JPL ephemerides service `__) " - "of a moving target such as an asteroid or comet.") error_nameresolve = f"Could not resolve {moving_target_name} to a sky position." - error_mt_coord = "Only one of moving_target and coordinates may be specified." - error_name_coord = "Only one of objectname and coordinates may be specified." - error_tica_mt = "Only SPOC is available for moving targets queries." - - with pytest.raises(InvalidQueryError) as error_msg: - Tesscut.get_sectors(moving_target=True) - assert error_noname in str(error_msg.value) - with pytest.raises(ResolverError) as error_msg: Tesscut.get_sectors(objectname=moving_target_name) assert error_nameresolve in str(error_msg.value) - with pytest.raises(InvalidQueryError) as error_msg: - Tesscut.get_sectors(coordinates=coord, moving_target=True) - assert error_mt_coord in str(error_msg.value) - - with pytest.raises(InvalidQueryError) as error_msg: - Tesscut.get_sectors(objectname=moving_target_name, coordinates=coord) - assert error_name_coord in str(error_msg.value) - - with pytest.raises(InvalidQueryError) as error_msg: - Tesscut.get_sectors(objectname=moving_target_name, - coordinates=coord, - moving_target=True) - assert error_mt_coord in str(error_msg.value) - - # The TICA product option is not available for moving targets - with pytest.raises(InvalidQueryError) as error_msg: - sector_table = Tesscut.get_sectors(objectname=moving_target_name, product='tica', - moving_target=True) - assert error_tica_mt in str(error_msg.value) - - @pytest.mark.parametrize("product", ["tica", "spoc"]) - def test_tesscut_download_cutouts(self, tmpdir, product): - + def test_tesscut_download_cutouts(self, tmpdir): def check_manifest(manifest, ext="fits"): assert isinstance(manifest, Table) assert len(manifest) >= 1 @@ -1411,29 +1372,27 @@ def check_manifest(manifest, ext="fits"): assert os.path.isfile(row['Local Path']) coord = SkyCoord(349.62609, -47.12424, unit="deg") - manifest = Tesscut.download_cutouts(product=product, coordinates=coord, size=1, path=str(tmpdir)) + manifest = Tesscut.download_cutouts(coordinates=coord, size=1, path=str(tmpdir)) check_manifest(manifest) coord = SkyCoord(107.18696, -70.50919, unit="deg") - manifest = Tesscut.download_cutouts(product=product, coordinates=coord, size=1, sector=27, + manifest = Tesscut.download_cutouts(coordinates=coord, size=1, sector=27, path=str(tmpdir)) check_manifest(manifest) - manifest = Tesscut.download_cutouts(product=product, coordinates=coord, size=[1, 1]*u.pix, sector=33, + manifest = Tesscut.download_cutouts(coordinates=coord, size=[1, 1]*u.pix, sector=33, path=str(tmpdir)) check_manifest(manifest) - manifest = Tesscut.download_cutouts(product=product, coordinates=coord, size=1, sector=33, + manifest = Tesscut.download_cutouts(coordinates=coord, size=1, sector=33, path=str(tmpdir), inflate=False) check_manifest(manifest, ".zip") - manifest = Tesscut.download_cutouts(product=product, objectname="TIC 32449963", size=1, path=str(tmpdir)) + manifest = Tesscut.download_cutouts(objectname="TIC 32449963", size=1, path=str(tmpdir)) check_manifest(manifest, "fits") def test_tesscut_download_cutouts_mt(self, tmpdir): - # Moving target functionality testing - coord = SkyCoord(349.62609, -47.12424, unit="deg") moving_target_name = 'Eleonora' manifest = Tesscut.download_cutouts(objectname=moving_target_name, @@ -1447,79 +1406,37 @@ def test_tesscut_download_cutouts_mt(self, tmpdir): for row in manifest: assert os.path.isfile(row['Local Path']) - error_noname = ("Please specify the object name or ID (as understood by the " - "`JPL ephemerides service `__) of " - "a moving target such as an asteroid or comet.") error_nameresolve = f"Could not resolve {moving_target_name} to a sky position." - error_mt_coord = "Only one of moving_target and coordinates may be specified." - error_name_coord = "Only one of objectname and coordinates may be specified." - error_tica_mt = "Only SPOC is available for moving targets queries." - - with pytest.raises(InvalidQueryError) as error_msg: - Tesscut.download_cutouts(moving_target=True) - assert error_noname in str(error_msg.value) - with pytest.raises(ResolverError) as error_msg: Tesscut.download_cutouts(objectname=moving_target_name) assert error_nameresolve in str(error_msg.value) - with pytest.raises(InvalidQueryError) as error_msg: - Tesscut.download_cutouts(coordinates=coord, moving_target=True) - assert error_mt_coord in str(error_msg.value) - - with pytest.raises(InvalidQueryError) as error_msg: - Tesscut.download_cutouts(objectname=moving_target_name, coordinates=coord) - assert error_name_coord in str(error_msg.value) - - with pytest.raises(InvalidQueryError) as error_msg: - Tesscut.download_cutouts(objectname=moving_target_name, - coordinates=coord, - moving_target=True) - assert error_mt_coord in str(error_msg.value) - - # The TICA product option is not available for moving targets - - with pytest.raises(InvalidQueryError) as error_msg: - Tesscut.download_cutouts(objectname=moving_target_name, product='tica', - moving_target=True) - 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): def check_cutout_hdu(cutout_hdus_list): assert isinstance(cutout_hdus_list, list) assert len(cutout_hdus_list) >= 1 assert isinstance(cutout_hdus_list[0], fits.HDUList) coord = SkyCoord(107.18696, -70.50919, unit="deg") - - cutout_hdus_list = Tesscut.get_cutouts(product=product, - coordinates=coord, + cutout_hdus_list = Tesscut.get_cutouts(coordinates=coord, size=1, sector=33) check_cutout_hdu(cutout_hdus_list) coord = SkyCoord(349.62609, -47.12424, unit="deg") - - cutout_hdus_list = Tesscut.get_cutouts(product=product, - coordinates=coord, + cutout_hdus_list = Tesscut.get_cutouts(coordinates=coord, size=[1, 1]*u.arcmin, sector=[28, 68]) check_cutout_hdu(cutout_hdus_list) - cutout_hdus_list = Tesscut.get_cutouts(product=product, - objectname="TIC 32449963", + cutout_hdus_list = Tesscut.get_cutouts(objectname="TIC 32449963", size=1, sector=37) check_cutout_hdu(cutout_hdus_list) def test_tesscut_get_cutouts_mt(self): - # Moving target functionality testing - coord = SkyCoord(349.62609, -47.12424, unit="deg") moving_target_name = 'Eleonora' - cutout_hdus_list = Tesscut.get_cutouts(objectname=moving_target_name, moving_target=True, sector=6, @@ -1528,44 +1445,11 @@ def test_tesscut_get_cutouts_mt(self): assert len(cutout_hdus_list) == 1 assert isinstance(cutout_hdus_list[0], fits.HDUList) - error_noname = ("Please specify the object name or ID (as understood by the " - "`JPL ephemerides service `__) of " - "a moving target such as an asteroid or comet.") error_nameresolve = f"Could not resolve {moving_target_name} to a sky position." - error_mt_coord = "Only one of moving_target and coordinates may be specified." - error_name_coord = "Only one of objectname and coordinates may be specified." - error_tica_mt = "Only SPOC is available for moving targets queries." - - with pytest.raises(InvalidQueryError) as error_msg: - Tesscut.get_cutouts(moving_target=True) - assert error_noname in str(error_msg.value) - with pytest.raises(ResolverError) as error_msg: Tesscut.get_cutouts(objectname=moving_target_name) assert error_nameresolve in str(error_msg.value) - with pytest.raises(InvalidQueryError) as error_msg: - Tesscut.get_cutouts(coordinates=coord, moving_target=True) - assert error_mt_coord in str(error_msg.value) - - with pytest.raises(InvalidQueryError) as error_msg: - Tesscut.get_cutouts(objectname=moving_target_name, - coordinates=coord) - assert error_name_coord in str(error_msg.value) - - with pytest.raises(InvalidQueryError) as error_msg: - Tesscut.get_cutouts(objectname=moving_target_name, - coordinates=coord, - moving_target=True) - assert error_mt_coord in str(error_msg.value) - - # The TICA product option is not available for moving targets - - with pytest.raises(InvalidQueryError) as error_msg: - Tesscut.get_cutouts(objectname=moving_target_name, product='tica', - moving_target=True) - assert error_tica_mt in str(error_msg.value) - ################### # ZcutClass tests # ################### diff --git a/docs/mast/mast_cut.rst b/docs/mast/mast_cut.rst index 0f2e0b267d..810fdb59f5 100644 --- a/docs/mast/mast_cut.rst +++ b/docs/mast/mast_cut.rst @@ -7,23 +7,20 @@ TESSCut ======= TESSCut is MAST's tool to provide full-frame image (FFI) cutouts from the Transiting -Exoplanet Survey Satellite (TESS). The cutouts can be made from either the Science -Processing Operation's Center (`SPOC `__) FFI products, -or the TESS Image CAlibrator (`TICA `__) high-level science products. -Cutouts from the TICA products are not available for sectors 1-26, -but are available for sector 27 onwards. These products are available up to 3 weeks sooner than -their SPOC counterparts for the latest sector, so it is recommended to request TICA cutouts -for users working with time-sensitive observations. The cutouts from either SPOC or TICA products -are returned in the form of target pixel files that follow the same format as TESS pipeline target +Exoplanet Survey Satellite (TESS). Cutouts can be made from the Science +Processing Operation's Center (`SPOC `__) FFI products. +The cutouts are returned in the form of target pixel files that follow the same format as TESS pipeline target pixel files. This tool can be accessed in Astroquery by using the Tesscut class. +As of August 2025, the option to create cutouts using TESS Image CAlibration +(`TICA `__) full frame images +was discontinued. Individual TICA full frame images remain available from the +`MAST TICA homepage `__. Cutouts using SPOC data remain available through TESSCut. + **Note:** TESScut limits each user to no more than 10 simultaneous calls to the service. After the user has reached this limit TESScut will return a ``503 Service Temporarily Unavailable Error``. -**Note:** The moving targets functionality does not currently support making cutouts from -TICA products, so the product argument will always default to SPOC. - If you use TESSCut for your work, please cite Brasseur et al. 2019 https://ui.adsabs.harvard.edu/abs/2019ascl.soft05007B/abstract @@ -31,8 +28,7 @@ https://ui.adsabs.harvard.edu/abs/2019ascl.soft05007B/abstract Cutouts ------- -The `~astroquery.mast.TesscutClass.get_cutouts` function takes a product type -("TICA" or "SPOC", but defaults to "SPOC"), coordinate, object name (e.g. "M104" or "TIC 32449963"), +The `~astroquery.mast.TesscutClass.get_cutouts` function takes a coordinate, object name (e.g. "M104" or "TIC 32449963"), or moving target (e.g. "Eleonora") and cutout size (in pixels or an angular quantity, default is 5 pixels) and returns the cutout target pixel file(s) as a list of `~astropy.io.fits.HDUList` objects. @@ -44,8 +40,6 @@ Requesting a cutout by coordinate or objectname accesses the `MAST TESScut API `__ and returns a target pixel file, with format described `here `__. -Note that the product argument will default to request for SPOC cutouts when -not explicitly called for TICA. .. doctest-remote-data:: @@ -62,22 +56,6 @@ not explicitly called for TICA. 2 APERTURE 1 ImageHDU 82 (5, 5) int32 -For users with time-sensitive targets who would like cutouts from the latest observations, -we recommend requesting for the TICA product. Using the same target from the example above, -this example shows a request for TICA cutouts: - -.. doctest-remote-data:: - - >>> 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, - ... product='tica', - ... sector=28) - >>> hdulist[0][0].header['FFI_TYPE'] # doctest: +IGNORE_OUTPUT - 'TICA' - The following example will request SPOC cutouts using the objectname argument, rather than a set of coordinates. @@ -118,23 +96,9 @@ simply with either the objectname or coordinates. 1 PIXELS 1 BinTableHDU 150 355R x 16C [D, E, J, 25J, 25E, 25E, 25E, 25E, J, E, E, 38A, D, D, D, D] 2 APERTURE 1 ImageHDU 97 (2136, 2078) int32 -Note that the moving targets functionality does not currently support TICA, so the product -parameter will result in an error when set to 'TICA'. - -.. doctest-remote-data:: - >>> from astroquery.mast import Tesscut - ... - >>> hdulist = Tesscut.get_cutouts(objectname="Eleonora", - ... product='tica', - ... moving_target=True, - ... sector=6) - Traceback (most recent call last): - ... - astroquery.exceptions.InvalidQueryError: Only SPOC is available for moving targets queries. - -The `~astroquery.mast.TesscutClass.download_cutouts` function takes a product type ("TICA" or "SPOC", but defaults to "SPOC"), -coordinate, cutout size (in pixels or an angular quantity), or object name (e.g. "M104" or "TIC 32449963") and moving target +The `~astroquery.mast.TesscutClass.download_cutouts` function takes a coordinate, cutout size +(in pixels or an angular quantity), or object name (e.g. "M104" or "TIC 32449963") and moving target (True or False). It uses these parameters to download the cutout target pixel file(s). If a given coordinate/object/moving target appears in more than one TESS sector, a target pixel file @@ -157,26 +121,6 @@ pixel file will be produced for each one. ---------------------------------------------------------- ./tess-s0009-4-1_107.186960_-70.509190_15x15_astrocut.fits -The query from the example above defaults to downloading cutouts from SPOC. The following example is a query for -the same target from above, but with the product argument passed as TICA to explicitly request for TICA cutouts, -and because the TICA products are not available for sectors 1-26, we request cutouts from sector 27 rather than sector 9. - -.. doctest-remote-data:: - - >>> from astroquery.mast import Tesscut - >>> from astropy.coordinates import SkyCoord - >>> import astropy.units as u - ... - >>> cutout_coord = SkyCoord(107.18696, -70.50919, unit="deg") - >>> manifest = Tesscut.download_cutouts(coordinates=cutout_coord, - ... product='tica', - ... size=[5, 7]*u.arcmin, - ... sector=27) # doctest: +IGNORE_OUTPUT - Downloading URL https://mast.stsci.edu/tesscut/api/v0.1/astrocut?ra=107.18696&dec=-70.50919&y=0.08333333333333333&x=0.11666666666666667&units=d&product=TICA§or=27 to ./tesscut_20230214150644.zip ... [Done] - >>> print(manifest) # doctest: +IGNORE_OUTPUT - Local Path - ---------------------------------------------------------- - ./tica-s0027-4-2_107.186960_-70.509190_21x14_astrocut.fits Sector information ------------------ @@ -197,25 +141,6 @@ To access sector information for a particular coordinate, object, or moving targ tess-s0008-1-1 8 1 1 tess-s0034-1-2 34 1 2 -Note that because of the delivery cadence of the -TICA high level science products, later sectors will be available sooner with TICA than with -SPOC. Also note that TICA is not available for sectors 1-26. The following example is the same -query as above, but for TICA. Notice that products for sector 8 are no longer available, -but are now available for sector 61. - -.. doctest-remote-data:: - - >>> from astroquery.mast import Tesscut - >>> from astropy.coordinates import SkyCoord - ... - >>> coord = SkyCoord(135.1408, -5.1915, unit="deg") - >>> sector_table = Tesscut.get_sectors(coordinates=coord, product='tica') - >>> print(sector_table) # doctest: +IGNORE_OUTPUT - sectorName sector camera ccd - -------------- ------ ------ --- - tica-s0034-1-2 34 1 2 - tica-s0061-1-2 61 1 2 - The following example will request SPOC cutouts using the objectname argument, rather than a set of coordinates. @@ -243,9 +168,6 @@ The following example requests SPOC cutouts for a moving target. tess-s0043-3-3 43 3 3 tess-s0044-2-4 44 2 4 -Note that the moving targets functionality is not currently available for TICA, -so the query will always default to SPOC. - Zcut ====