diff --git a/.gitignore b/.gitignore index 6056deabe..2efb1bbbd 100644 --- a/.gitignore +++ b/.gitignore @@ -28,4 +28,7 @@ config.log tests/.tests/ *__pycache__/ _build.* +*.pickle +*.ecsv +*cache/* diff --git a/setup.py b/setup.py index 7b7c891bf..aaaecefdb 100644 --- a/setup.py +++ b/setup.py @@ -1,5 +1,6 @@ from setuptools import setup import os +import shutil import re reqs = [] @@ -14,6 +15,16 @@ with open('README.md') as file: long_description = file.read() +try: + import astropy + astropy_cache_dir = astropy.config.get_cache_dir() +except ImportError: + astropy_cache_dir = os.path.join(f"{os.path.expanduser('~')}", ".astropy", "cache") +os.makedirs(os.path.join(astropy_cache_dir, "astroquery", "Simbad"), exist_ok=True) +shutil.copytree("./tests/data/cache/astropy/astroquery/Simbad", + os.path.join(astropy_cache_dir, "astroquery", "Simbad"), + dirs_exist_ok=True) + # cf. http://stackoverflow.com/questions/458550/standard-way-to-embed-version-into-python-package version_file = os.path.join('spectractor', '_version.py') verstrline = open(version_file, "rt").read() diff --git a/spectractor/extractor/targets.py b/spectractor/extractor/targets.py index 9c4360f25..08396419e 100644 --- a/spectractor/extractor/targets.py +++ b/spectractor/extractor/targets.py @@ -4,10 +4,14 @@ import astropy.units as u from astropy.time import Time from astroquery.simbad import SimbadClass +from astropy.io import ascii +import astropy.config import matplotlib.pyplot as plt from scipy.interpolate import interp1d import numpy as np +import os +import shutil from spectractor import parameters from spectractor.config import set_logger @@ -24,6 +28,22 @@ _USE_NEW_SIMBAD = True +def _get_cache_dir(): + cache = os.path.join(astropy.config.get_cache_dir(), "astroquery", "Simbad") + os.makedirs(cache, exist_ok=True) + return cache + + +def _get_cache_file(tag): + filename = tag.replace("*", "").replace(" ", "_").replace(".", "_") + return filename + + +def _clean_cache_dir(): + cache = _get_cache_dir() + shutil.rmtree(cache) + + def load_target(label, verbose=False): """Load the target properties according to the type set by parameters.OBS_OBJECT_TYPE. @@ -265,33 +285,44 @@ def load(self): # ``Simbad...`` methods secretly makes an instance, which stays around, # has a connection go stale, and then raises an exception seemingly # at some random time later - simbadQuerier = SimbadClass() - patchSimbadURL(simbadQuerier) + if not getCalspec.is_calspec(self.label) and getCalspec.is_calspec(self.label.replace(".", " ")): + self.label = self.label.replace(".", " ") + astroquery_label = self.label + if getCalspec.is_calspec(self.label): + calspec = getCalspec.Calspec(self.label) + astroquery_label = calspec.Astroquery_Name + + cache_location = _get_cache_dir() + cache_file = _get_cache_file(astroquery_label) + if os.path.exists(os.path.join(cache_location, f"{cache_file}.ecsv")): + self.my_logger.debug(f"\n\tLoad {self.label} coordinates from cached file {cache_file}.ecsv") + self.simbad_table = ascii.read(os.path.join(cache_location, f"{cache_file}.ecsv")) + else: + simbadQuerier = SimbadClass() + patchSimbadURL(simbadQuerier) - if _USE_NEW_SIMBAD: - simbadQuerier.add_votable_fields('U', 'B', 'V', 'R', 'I', 'J', 'sp_type', - 'parallax', 'propermotions', 'rvz_redshift') + if _USE_NEW_SIMBAD: + simbadQuerier.add_votable_fields('U', 'B', 'V', 'R', 'I', 'J', 'sp_type', + 'parallax', 'propermotions', 'rvz_redshift') + else: + simbadQuerier.add_votable_fields( + 'flux(U)', 'flux(B)', 'flux(V)', 'flux(R)', 'flux(I)', 'flux(J)', 'sptype', + 'parallax', 'pm', 'z_value' + ) + self.my_logger.debug(f"\n\tDownload {self.label} coordinates from Simbad...") + self.simbad_table = simbadQuerier.query_object(astroquery_label) + self.simbad_table.write(os.path.join(cache_location,f"{cache_file}.ecsv"), overwrite=True) + + if "ra" in self.simbad_table.keys(): ra_key = "ra" dec_key = "dec" redshift_key = "rvz_redshift" else: - simbadQuerier.add_votable_fields( - 'flux(U)', 'flux(B)', 'flux(V)', 'flux(R)', 'flux(I)', 'flux(J)', 'sptype', - 'parallax', 'pm', 'z_value' - ) ra_key = "RA" dec_key = "DEC" redshift_key = "Z_VALUE" - if not getCalspec.is_calspec(self.label) and getCalspec.is_calspec(self.label.replace(".", " ")): - self.label = self.label.replace(".", " ") - astroquery_label = self.label - if getCalspec.is_calspec(self.label): - calspec = getCalspec.Calspec(self.label) - astroquery_label = calspec.Astroquery_Name - self.simbad_table = simbadQuerier.query_object(astroquery_label) - if self.simbad_table is not None: - if self.verbose or True: + if self.verbose: self.my_logger.info(f'\n\tSimbad:\n{self.simbad_table}') if _USE_NEW_SIMBAD: self.radec_position = SkyCoord(self.simbad_table[ra_key][0], self.simbad_table[dec_key][0], unit="deg") @@ -301,11 +332,11 @@ def load(self): ) else: raise RuntimeError(f"Target {self.label} not found in Simbad") - self.get_radec_position_after_pm(date_obs="J2000") - if not np.ma.is_masked(self.simbad_table[redshift_key]): - self.redshift = float(self.simbad_table[redshift_key]) + if not np.ma.is_masked(self.simbad_table[redshift_key][0]): + self.redshift = float(self.simbad_table[redshift_key][0]) else: self.redshift = 0 + self.get_radec_position_after_pm(date_obs="J2000") self.load_spectra() def load_spectra(self): @@ -405,7 +436,7 @@ def load_spectra(self): def get_radec_position_after_pm(self, date_obs): if self.simbad_table is not None: - if _USE_NEW_SIMBAD: + if "pmra" in self.simbad_table[0].keys(): pmra_key = 'pmra' pmdec_key = 'pmdec' plx_value_key = 'plx_value' diff --git a/tests/data/cache/astropy/astroquery/Simbad/HD_111980.ecsv b/tests/data/cache/astropy/astroquery/Simbad/HD_111980.ecsv new file mode 100644 index 000000000..69237ddf1 --- /dev/null +++ b/tests/data/cache/astropy/astroquery/Simbad/HD_111980.ecsv @@ -0,0 +1,242 @@ +# %ECSV 1.0 +# --- +# datatype: +# - name: MAIN_ID +# datatype: string +# description: Main identifier for an object +# meta: !!omap +# - {ucd: meta.id;meta.main} +# - {width: 22} +# - links: +# - {href: 'http://simbad.u-strasbg.fr/simbad/sim-id?Ident=${MAIN_ID}&NbIdent=1', value: '${MAIN_ID}'} +# - {_votable_string_dtype: char} +# subtype: json +# - name: RA +# unit: '"h:m:s"' +# datatype: string +# description: Right ascension +# meta: !!omap +# - {ucd: pos.eq.ra;meta.main} +# - {width: 13} +# - {precision: '8'} +# - {_votable_string_dtype: char} +# - name: DEC +# unit: '"d:m:s"' +# datatype: string +# description: Declination +# meta: !!omap +# - {ucd: pos.eq.dec;meta.main} +# - {width: 13} +# - {precision: '8'} +# - {_votable_string_dtype: char} +# - name: RA_PREC +# datatype: int16 +# description: Right ascension precision +# meta: !!omap +# - {width: 2} +# - name: DEC_PREC +# datatype: int16 +# description: Declination precision +# meta: !!omap +# - {width: 2} +# - name: COO_ERR_MAJA +# unit: mas +# datatype: float32 +# format: '{:6.3f}' +# description: Coordinate error major axis +# meta: !!omap +# - {ucd: phys.angSize.smajAxis;pos.errorEllipse;pos.eq} +# - {width: 6} +# - {precision: '3'} +# - name: COO_ERR_MINA +# unit: mas +# datatype: float32 +# format: '{:6.3f}' +# description: Coordinate error minor axis +# meta: !!omap +# - {ucd: phys.angSize.sminAxis;pos.errorEllipse;pos.eq} +# - {width: 6} +# - {precision: '3'} +# - name: COO_ERR_ANGLE +# unit: deg +# datatype: int16 +# description: Coordinate error angle +# meta: !!omap +# - {ucd: pos.posAng;pos.errorEllipse;pos.eq} +# - {width: 3} +# - name: COO_QUAL +# datatype: string +# description: Coordinate quality +# meta: !!omap +# - {ucd: meta.code.qual;pos.eq} +# - {width: 1} +# - {_votable_string_dtype: char} +# - name: COO_WAVELENGTH +# datatype: string +# description: Wavelength class for the origin of the coordinates (R,I,V,U,X,G) +# meta: !!omap +# - {ucd: instr.bandpass;pos.eq} +# - {width: 1} +# - {_votable_string_dtype: char} +# - name: COO_BIBCODE +# datatype: string +# description: Coordinate reference +# meta: !!omap +# - {ucd: meta.bib.bibcode;pos.eq} +# - {width: 19} +# - {_votable_string_dtype: char} +# subtype: json +# - name: FLUX_U +# unit: mag +# datatype: float32 +# description: Magnitude U +# meta: !!omap +# - {ucd: phot.mag;em.opt.U} +# - name: FLUX_B +# unit: mag +# datatype: float32 +# description: Magnitude B +# meta: !!omap +# - {ucd: phot.mag;em.opt.B} +# - name: FLUX_V +# unit: mag +# datatype: float32 +# description: Magnitude V +# meta: !!omap +# - {ucd: phot.mag;em.opt.V} +# - name: FLUX_R +# unit: mag +# datatype: float32 +# description: Magnitude R +# meta: !!omap +# - {ucd: phot.mag;em.opt.R} +# - name: FLUX_I +# unit: mag +# datatype: float32 +# description: Magnitude I +# meta: !!omap +# - {ucd: phot.mag;em.opt.I} +# - name: FLUX_J +# unit: mag +# datatype: float32 +# description: Magnitude J +# meta: !!omap +# - {ucd: phot.mag;em.IR.J} +# - name: SP_TYPE +# datatype: string +# description: MK spectral type +# meta: !!omap +# - {ucd: src.spType} +# - {width: 6} +# - {_votable_string_dtype: char} +# subtype: json +# - name: SP_QUAL +# datatype: string +# description: Spectral type quality +# meta: !!omap +# - {ucd: meta.code.qual;src.spType} +# - {width: 1} +# - {_votable_string_dtype: char} +# - name: SP_BIBCODE +# datatype: string +# description: spectral type reference +# meta: !!omap +# - {ucd: meta.bib.bibcode;src.spType} +# - {width: 19} +# - {_votable_string_dtype: char} +# subtype: json +# - name: PLX_VALUE +# unit: mas +# datatype: float64 +# format: '{:9.3f}' +# description: Parallax +# meta: !!omap +# - {ucd: pos.parallax.trig} +# - {width: 9} +# - {precision: '3'} +# - name: PLX_PREC +# datatype: int16 +# description: Parallax precision +# meta: !!omap +# - {width: 1} +# - name: PLX_ERROR +# unit: mas +# datatype: float32 +# description: Parallax error +# meta: !!omap +# - {ucd: stat.error;pos.parallax.trig} +# - name: PLX_QUAL +# datatype: string +# description: Parallax quality +# meta: !!omap +# - {ucd: meta.code.qual;pos.parallax.trig} +# - {width: 1} +# - {_votable_string_dtype: char} +# - name: PLX_BIBCODE +# datatype: string +# description: Parallax reference +# meta: !!omap +# - {ucd: meta.bib.bibcode;pos.parallax.trig} +# - {width: 19} +# - {_votable_string_dtype: char} +# subtype: json +# - name: PMRA +# unit: mas / yr +# datatype: float64 +# format: '{:9.3f}' +# description: Proper motion in RA +# meta: !!omap +# - {ucd: pos.pm;pos.eq.ra} +# - {width: 9} +# - {precision: '3'} +# - name: PMDEC +# unit: mas / yr +# datatype: float64 +# format: '{:9.3f}' +# description: Proper motion in DEC +# meta: !!omap +# - {ucd: pos.pm;pos.eq.dec} +# - {width: 9} +# - {precision: '3'} +# - name: PM_ERR_MAJA +# unit: mas / yr +# datatype: float32 +# format: '{:5.3f}' +# description: Proper motion error major axis +# meta: !!omap +# - {ucd: phys.angSize.smajAxis;pos.errorEllipse;pos.pm} +# - {width: 5} +# - {precision: '3'} +# - name: PM_ERR_MINA +# unit: mas / yr +# datatype: float32 +# format: '{:5.3f}' +# description: Proper motion error minor axis +# meta: !!omap +# - {ucd: phys.angSize.sminAxis;pos.errorEllipse;pos.pm} +# - {width: 5} +# - {precision: '3'} +# - name: PM_ERR_ANGLE +# unit: deg +# datatype: int16 +# description: Proper motion error angle +# meta: !!omap +# - {ucd: pos.posAng;pos.errorEllipse;pos.pm} +# - {width: 3} +# - name: Z_VALUE +# datatype: float64 +# format: '{:.7f}' +# description: Redshift +# meta: !!omap +# - {ucd: src.redshift} +# - {precision: '7'} +# - name: SCRIPT_NUMBER_ID +# datatype: int32 +# meta: !!omap +# - {ucd: meta.number} +# - {width: 2} +# - {precision: '1'} +# meta: {ID: SimbadScript, description: 'Simbad script executed on 2025.03.13CET16:08:13', name: default} +# schema: astropy-2.0 +MAIN_ID RA DEC RA_PREC DEC_PREC COO_ERR_MAJA COO_ERR_MINA COO_ERR_ANGLE COO_QUAL COO_WAVELENGTH COO_BIBCODE FLUX_U FLUX_B FLUX_V FLUX_R FLUX_I FLUX_J SP_TYPE SP_QUAL SP_BIBCODE PLX_VALUE PLX_PREC PLX_ERROR PLX_QUAL PLX_BIBCODE PMRA PMDEC PM_ERR_MAJA PM_ERR_MINA PM_ERR_ANGLE Z_VALUE SCRIPT_NUMBER_ID +"""HD 111980""" "12 53 15.0529" "-18 31 20.013" 14 14 0.0225 0.0183 90 A O """2020yCat.1350....0G""" 8.8 8.91 8.38 "" "" 7.176 """F7V""" C """1988MSS...C04....0H""" 12.9277 4 0.0261 A """2020yCat.1350....0G""" 299.359 -795.843 0.032 0.026 90 0.000517 1