Source code for pulsar_spectra.catalogue

"""
Loads all the data required by vcstools from the data directory.
"""

import glob
import logging
import os
import re

import numpy as np
import psrqpy
import yaml

logger = logging.getLogger(__name__)

# Hard code the path of the flux catalogue directories
CAT_DIR = os.path.join(os.path.dirname(__file__), "catalogue_papers")

# Grab all the catalogue yamls
CAT_YAMLS = glob.glob("{}/*yaml".format(CAT_DIR))

# atnf version to be used with all psrqpy querys
ATNF_VER = "2.6.2"

# dictionary of ADS links
ADS_REF = {
    "Sieber_1973": "https://ui.adsabs.harvard.edu/abs/1973A%26A....28..237S",
    "Bartel_1978": "https://ui.adsabs.harvard.edu/abs/1978A%26A....68..361B",
    "Izvekova_1981": "https://ui.adsabs.harvard.edu/abs/1981Ap%26SS..78...45I",
    "Lorimer_1995": "https://ui.adsabs.harvard.edu/abs/1995ApJ...439..933L",
    "van_Ommen_1997": "https://ui.adsabs.harvard.edu/abs/1997MNRAS.287..307V",
    "Maron_2000": "https://ui.adsabs.harvard.edu/abs/2000A%26AS..147..195M",
    "Malofeev_2000": "https://ui.adsabs.harvard.edu/abs/2000ARep...44..436M",
    "Karastergiou_2005": "https://ui.adsabs.harvard.edu/abs/2005MNRAS.359..481K",
    "Johnston_2006": "https://ui.adsabs.harvard.edu/abs/2006MNRAS.369.1916J",
    "Kijak_2007": "https://ui.adsabs.harvard.edu/abs/2007A%26A...462..699K",
    "Keith_2011": "https://ui.adsabs.harvard.edu/abs/2011MNRAS.416..346K",
    "Bates_2011": "https://ui.adsabs.harvard.edu/abs/2011MNRAS.411.1575B",
    "Kijak_2011": "https://ui.adsabs.harvard.edu/abs/2011A%26A...531A..16K",
    "Zakharenko_2013": "https://ui.adsabs.harvard.edu/abs/2013MNRAS.431.3624Z",
    "Dai_2015": "https://ui.adsabs.harvard.edu/abs/2015MNRAS.449.3223D",
    "Basu_2016": "https://ui.adsabs.harvard.edu/abs/2016MNRAS.458.2509B",
    "Bell_2016": "https://ui.adsabs.harvard.edu/abs/2016MNRAS.461..908B",
    "Bilous_2016": "https://ui.adsabs.harvard.edu/abs/2016A%26A...591A.134B",
    "Han_2016": "https://ui.adsabs.harvard.edu/abs/2016RAA....16..159H",
    "Murphy_2017": "https://ui.adsabs.harvard.edu/abs/2017PASA...34...20M",
    "Kijak_2017": "https://ui.adsabs.harvard.edu/abs/2017ApJ...840..108K",
    "Hobbs_2004a": "https://ui.adsabs.harvard.edu/abs/2004MNRAS.352.1439H",
    "Johnston_1993": "https://ui.adsabs.harvard.edu/abs/1993Natur.361..613J",
    "Stovall_2015": "https://ui.adsabs.harvard.edu/abs/2015ApJ...808..156S",
    "Xue_2017": "https://ui.adsabs.harvard.edu/abs/2017PASA...34...70X",
    "Jankowski_2018": "https://ui.adsabs.harvard.edu/abs/2018MNRAS.473.4436J",
    "Bondonneau_2020": "https://ui.adsabs.harvard.edu/abs/2020A%26A...635A..76B",
    "Johnston_2021": "https://ui.adsabs.harvard.edu/abs/2021MNRAS.502.1253J",
    "Taylor_1993": "https://ui.adsabs.harvard.edu/abs/1993ApJS...88..529T",
    "Mignani_2017": "https://ui.adsabs.harvard.edu/abs/2017ApJ...851L..10M",
    "Johnston_2018": "https://ui.adsabs.harvard.edu/abs/2018MNRAS.474.4629J",
    "Jankowski_2019": "https://ui.adsabs.harvard.edu/abs/2019MNRAS.484.3691J",
    "Sanidas_2019": "https://ui.adsabs.harvard.edu/abs/2019A%26A...626A.104S",
    "Zhao_2019": "https://ui.adsabs.harvard.edu/abs/2019ApJ...874...64Z",
    "Bilous_2020": "https://ui.adsabs.harvard.edu/abs/2020A%26A...635A..75B",
    "Stappers_2008": "https://ui.adsabs.harvard.edu/abs/2008AIPC..983..593S",
    "McEwen_2020": "https://ui.adsabs.harvard.edu/abs/2020ApJ...892...76M",
    "Lorimer_2006": "https://ui.adsabs.harvard.edu/abs/2006MNRAS.372..777L",
    "Kramer_2003a": "https://ui.adsabs.harvard.edu/abs/2003MNRAS.342.1299K",
    "Han_2021": "https://ui.adsabs.harvard.edu/abs/2021RAA....21..107H",
    "Dembska_2014": "https://ui.adsabs.harvard.edu/abs/2014MNRAS.445.3105D",
    "Camilo_1995": "https://ui.adsabs.harvard.edu/abs/1995ApJ...445..756C",
    "Robinson_1995": "https://ui.adsabs.harvard.edu/abs/1995MNRAS.274..547R",
    "McConnell_1991": "https://ui.adsabs.harvard.edu/abs/1991MNRAS.249..654M",
    "Manchester_1996": "https://ui.adsabs.harvard.edu/abs/1996MNRAS.279.1235M",
    "Qiao_1995": "https://ui.adsabs.harvard.edu/abs/1995MNRAS.274..572Q",
    "Manchester_1993": "https://ui.adsabs.harvard.edu/abs/1993ApJ...403L..29M",
    "Zepka_1996": "https://ui.adsabs.harvard.edu/abs/1996ApJ...456..305Z",
    "Manchester_1978a": "https://ui.adsabs.harvard.edu/abs/1978MNRAS.185..409M",
    "Lundgren_1995": "https://ui.adsabs.harvard.edu/abs/1995ApJ...453..419L",
    "Dewey_1985": "https://ui.adsabs.harvard.edu/abs/1985ApJ...294L..25D",
    "Nicastro_1995": "https://ui.adsabs.harvard.edu/abs/1995MNRAS.273L..68N",
    "Johnston_1992": "https://ui.adsabs.harvard.edu/abs/1992MNRAS.255..401J",
    "Wolszczan_1992": "https://ui.adsabs.harvard.edu/abs/1992Natur.355..145W",
    "Xie_2019": "https://ui.adsabs.harvard.edu/abs/2019RAA....19..103X",
    "Lorimer_1995b": "https://ui.adsabs.harvard.edu/abs/1995MNRAS.273..411L",
    "Kaur_2019": "https://ui.adsabs.harvard.edu/abs/2019ApJ...882..133K",
    "Manchester_2001": "https://ui.adsabs.harvard.edu/abs/2001MNRAS.328...17M",
    "Morris_2002": "https://ui.adsabs.harvard.edu/abs/2002MNRAS.335..275M",
    "Kondratiev_2016": "https://ui.adsabs.harvard.edu/abs/2016A%26A...585A.128K",
    "Kravtsov_2022": "https://ui.adsabs.harvard.edu/abs/2022MNRAS.512.4324K",
    "Toscano_1998": "https://ui.adsabs.harvard.edu/abs/1998ApJ...506..863T",
    "Kuzmin_2001": "https://ui.adsabs.harvard.edu/abs/2001A%26A...368..230K",
    "Stairs_1999": "https://ui.adsabs.harvard.edu/abs/1999ApJS..123..627S",
    "Spiewak_2022": "https://ui.adsabs.harvard.edu/abs/2022PASA...39...27S",
    "Zhang_2019": "https://ui.adsabs.harvard.edu/abs/2019ApJ...885L..37Z",
    "Lommen_2000": "https://ui.adsabs.harvard.edu/abs/2000ApJ...545.1007L",
    "Alam_2021": "https://ui.adsabs.harvard.edu/abs/2021ApJS..252....4A",
    "Bondonneau_2021": "https://ui.adsabs.harvard.edu/abs/2021A%26A...652A..34B",
    "Kramer_1998": "https://ui.adsabs.harvard.edu/abs/1998ApJ...501..270K",
    "Kramer_1999": "https://ui.adsabs.harvard.edu/abs/1999ApJ...526..957K",
    "Frail_2016": "https://ui.adsabs.harvard.edu/abs/2016ApJ...829..119F",
    "Lee_2022": "https://ui.adsabs.harvard.edu/abs/2022PASA...39...42L",
    "Bhat_2023": "https://ui.adsabs.harvard.edu/abs/2023PASA...40...20B",
    "Aloisi_2019": "https://ui.adsabs.harvard.edu/abs/2019ApJ...875...19A",
    "Bailes_1997": "https://ui.adsabs.harvard.edu/abs/1997ApJ...481..386B",
    "Basu_2018": "https://ui.adsabs.harvard.edu/abs/2018MNRAS.475.1469B",
    "Biggs_1996": "https://ui.adsabs.harvard.edu/abs/1996MNRAS.282..691B",
    "Boyles_2013": "https://ui.adsabs.harvard.edu/abs/2013ApJ...763...80B",
    "Brinkman_2018": "https://ui.adsabs.harvard.edu/abs/2018MNRAS.474.2012B",
    "Champion_2005a": "https://ui.adsabs.harvard.edu/abs/2005MNRAS.363..929C",
    "Champion_2005b": "https://ui.adsabs.harvard.edu/abs/2005PhDT.......282C",
    "Crawford_2001": "https://ui.adsabs.harvard.edu/abs/2001AJ....122.2001C",
    "Crawford_2007": "https://ui.adsabs.harvard.edu/abs/2007AJ....134.1231C",
    "Deller_2009": "https://ui.adsabs.harvard.edu/abs/2009ApJ...701.1243D",
    "Dembska_2015": "https://ui.adsabs.harvard.edu/abs/2015MNRAS.449.1869D",
    "Demorest_2013": "https://ui.adsabs.harvard.edu/abs/2013ApJ...762...94D",
    "Esamdin_2004": "https://ui.adsabs.harvard.edu/abs/2004A&A...425..949E",
    "Freire_2007": "https://ui.adsabs.harvard.edu/abs/2007ApJ...662.1177F",
    "Gentile_2018": "https://ui.adsabs.harvard.edu/abs/2018ApJ...862...47G",
    "Giacani_2001": "https://ui.adsabs.harvard.edu/abs/2001AJ....121.3133G",
    "Han_1999": "https://ui.adsabs.harvard.edu/abs/1999A&AS..136..571H",
    "Hoensbroech_1997": "https://ui.adsabs.harvard.edu/abs/1997A%26AS..126..121V",
    "Joshi_2009": "https://ui.adsabs.harvard.edu/abs/2009MNRAS.398..943J",
    "Kaspi_1997": "https://ui.adsabs.harvard.edu/abs/1997ApJ...485..820K",
    "Kijak_1997": "https://ui.adsabs.harvard.edu/abs/1997A%26A...318L..63K",
    "Kijak_1998": "https://ui.adsabs.harvard.edu/abs/1998A%26AS..127..153K",
    "Kramer_1997": "https://ui.adsabs.harvard.edu/abs/1997ApJ...488..364K",
    "Kuniyoshi_2015": "https://ui.adsabs.harvard.edu/abs/2015MNRAS.453..828K",
    "Lewandowski_2004": "https://ui.adsabs.harvard.edu/abs/2004ApJ...600..905L",
    "Lorimer_1996": "https://ui.adsabs.harvard.edu/abs/1996MNRAS.283.1383L",
    "Lorimer_2005": "https://ui.adsabs.harvard.edu/abs/2005MNRAS.359.1524L",
    "Lorimer_2007": "https://ui.adsabs.harvard.edu/abs/2007MNRAS.379..282L",
    "Lynch_2012": "https://ui.adsabs.harvard.edu/abs/2012ApJ...745..109L",
    "Lynch_2013": "https://ui.adsabs.harvard.edu/abs/2013ApJ...763...81L",
    "Manchester_1995": "https://ui.adsabs.harvard.edu/abs/1995ApJ...441L..65M",
    "Manchester_2013": "https://ui.adsabs.harvard.edu/abs/2013PASA...30...17M",
    "Michilli_2020": "https://ui.adsabs.harvard.edu/abs/2020MNRAS.491..725M",
    "Mickaliger_2012": "https://ui.adsabs.harvard.edu/abs/2012ApJ...759..127M",
    "Mikhailov_2016": "https://ui.adsabs.harvard.edu/abs/2016A%26A...593A..21M",
    "Ng_2015": "https://ui.adsabs.harvard.edu/abs/2015MNRAS.450.2922N",
    "Rozko_2018": "https://ui.adsabs.harvard.edu/abs/2018MNRAS.479.2193R",
    "Rozko_2021": "https://ui.adsabs.harvard.edu/abs/2021ApJ...922..125R",
    "Sayer_1997": "https://ui.adsabs.harvard.edu/abs/1997ApJ...474..426S",
    "Seiradakis_1995": "https://ui.adsabs.harvard.edu/abs/1995A%26AS..111..205S",
    "Shapiro_Albert_2021": "https://ui.adsabs.harvard.edu/abs/2021ApJ...909..219S",
    "Stovall_2014": "https://ui.adsabs.harvard.edu/abs/2014ApJ...791...67S",
    "Surnis_2019": "https://ui.adsabs.harvard.edu/abs/2019ApJ...870....8S",
    "Titus_2019": "https://ui.adsabs.harvard.edu/abs/2019MNRAS.487.4332T",
    "Zhao_2017": "https://ui.adsabs.harvard.edu/abs/2017ApJ...845..156Z",
    "Gitika_2023": "https://ui.adsabs.harvard.edu/abs/2023MNRAS.526.3370G",
    "Crowter_2020": "https://ui.adsabs.harvard.edu/abs/2020MNRAS.495.3052C",
    "Janssen_2009": "https://ui.adsabs.harvard.edu/abs/2009A%26A...498..223J",
    "Weisberg_1999": "https://ui.adsabs.harvard.edu/abs/1999ApJS..121..171W",
    "Stokes_1985": "https://ui.adsabs.harvard.edu/abs/1985Natur.317..787S",
    "Stokes_1986": "https://ui.adsabs.harvard.edu/abs/1986ApJ...311..694S",
    "Tan_2020": "https://ui.adsabs.harvard.edu/abs/2020MNRAS.492.5878T",
    "McLean_1973": "https://ui.adsabs.harvard.edu/abs/1973MNRAS.165..133M",
    "McGary_2001": "https://ui.adsabs.harvard.edu/abs/2001AJ....121.1192M",
    "Lazarus_2015": "https://ui.adsabs.harvard.edu/abs/2015ApJ...812...81L",
    "Curylo_2020": "https://ui.adsabs.harvard.edu/abs/2020MNRAS.495.3052C",
    "Shrauner_1998": "https://ui.adsabs.harvard.edu/abs/1998ApJ...509..785S",
    "Foster_1991": "https://ui.adsabs.harvard.edu/abs/1991ApJ...378..687F",
    "Bhattacharyya_2016": "https://ui.adsabs.harvard.edu/abs/2016ApJ...817..130B",
    "Kouwenhoven_2000": "https://ui.adsabs.harvard.edu/abs/2000A%26AS..145..243K",
    "Dowell_2013": "https://ui.adsabs.harvard.edu/abs/2013ApJ...775L..28D",
    "Deneva_2016": "https://ui.adsabs.harvard.edu/abs/2016ApJ...821...10D",
    "Malofeev_1993": "https://ui.adsabs.harvard.edu/abs/1993AstL...19..138M",
    "Slee_1986": "https://ui.adsabs.harvard.edu/abs/1986AuJPh..39..103S",
    "Fruchter_1988": "https://ui.adsabs.harvard.edu/abs/1988Natur.333..237F",
    "Fruchter_1990": "https://ui.adsabs.harvard.edu/abs/1990ApJ...351..642F",
    "Bailes_1994": "https://ui.adsabs.harvard.edu/abs/1994ApJ...425L..41B",
    "Navarro_1995": "https://ui.adsabs.harvard.edu/abs/1995ApJ...455L..55N",
    "Camilo_1996": "https://ui.adsabs.harvard.edu/abs/1996ApJ...469..819C",
    "Maron_2004": "https://ui.adsabs.harvard.edu/abs/2004A%26A...413L..19M",
    "Wielebinski_1993": "https://ui.adsabs.harvard.edu/abs/1993A%26A...272L..13W",
    "Champion_2008": "https://ui.adsabs.harvard.edu/abs/2008Sci...320.1309C",
    "Hessels_2011": "https://ui.adsabs.harvard.edu/abs/2011AIPC.1357...40H",
    "Kowalinska_2012": "https://ui.adsabs.harvard.edu/abs/2012ASPC..466..101K",
    "Levin_2016": "https://ui.adsabs.harvard.edu/abs/2016ApJ...818..166L",
    "Wang_2024": "https://ui.adsabs.harvard.edu/abs/2024ApJ...961...48W",
    "Keith_2024": "https://ui.adsabs.harvard.edu/abs/2024MNRAS.530.1581K",
    "Kumar_2025": "https://ui.adsabs.harvard.edu/abs/2025ApJ...982..132K",
    "Deneva_2024": "https://ui.adsabs.harvard.edu/abs/2024ApJS..271...23D",
    "Parent_2022": "https://ui.adsabs.harvard.edu/abs/2022ApJ...924..135P",
    "Martsen_2022": "https://ui.adsabs.harvard.edu/abs/2022ApJ...941...22M",
    "Bangale_2024": "https://ui.adsabs.harvard.edu/abs/2024ApJ...966..161B",
    "Fiore_2023": "https://ui.adsabs.harvard.edu/abs/2023ApJ...956...40F",
    "Lee_2025": "https://ui.adsabs.harvard.edu/abs/2025PASA...42..117L",
    "Mantovanini_2025": "https://ui.adsabs.harvard.edu/abs/2025MNRAS.tmp.1383M",
    "Kijak_2021": "https://ui.adsabs.harvard.edu/abs/2021ApJ...923..211K",
}


[docs] def get_atnf_references(): """Wrapper for psrqpy.get_references() that ensures the cache is only Updated once.""" ref_dict = psrqpy.get_references(version=ATNF_VER) if not isinstance(ref_dict, dict): # Reference error so update the cache ref_dict = psrqpy.get_references(version=ATNF_VER, updaterefcache=True) return ref_dict
[docs] def convert_atnf_ref(ref_code, ref_dict=None): """Converts an ATNF psrcat reference code to a reference in the format "Author Year" Parameters ---------- ref_code : `str` An ATNF psrcat reference code as found from `psrqpy.get_references(updaterefcache=True)` and https://www.atnf.csiro.au/research/pulsar/psrcat/psrcat_ref.html. ref_dict : `dict`, optional A previous psrqpy.get_references query. Can be supplied to prevent performing a new query. Returns ------- ref : `str` Reference in the format "Author Year". """ # These one doesn't even have a title so returning maunally if ref_code == "san16": return "Sanpa-arsa_2016" elif ref_code == "gg74": return "Gomez-Gonzalez_1974" if ref_dict is None: ref_dict = get_atnf_references() try: ref_string = ref_dict[ref_code] except KeyError: return None # Find the parts we need if ref_string.startswith("eds "): # Remove the eds part which I think is a typo ref_string = ref_string[4:] author = ref_string.split(",")[0].replace(" ", "") # Get only author year part of the string, example string: # Anderson, S. B., Wolszczan, A., Kulkarni, S. R. & Prince, T. A., 1997. Observations of two millisecond pulsars in the globular cluster NGC 5904. ApJ, 482, 870-873. if "ArXiv" in ref_string: author_year_title = ref_string.split(". ArXiv")[0] elif "arXiv" in ref_string: author_year_title = ref_string.split(". arXiv")[0] elif "ApJ" in ref_string: author_year_title = ref_string.split(". ApJ")[0] elif "-" not in ref_string: # Has no refence code so skip the removal author_year_title = ref_string else: author_year_title = ref_string[: ref_string[:-1].rfind(".")] if "New York" in author_year_title: # Different format for American Institute of Physics, New York references author_year = author_year_title elif "IAU Circ. No" in author_year_title: # Different format for IAU Circular references author_year = author_year_title.split("IAU Circ. No")[0].replace("M.", "").replace("M5.", "") else: removal_patterns = [ ". Phys. Rev", # This journal isn't removed in previous logic so remove it here ". ATel", # This journal isn't removed in previous logic so remove it here "(", # Remove the brackets ":", # Remove the colins "1E", # Remove weird name convertion "NGC", # NGC often in titles that ruin formatting "PSR", # Parts of pulsar names are mistaken for years "Sgr", # Parts of soft gamma ray repeaters are mistaken for years ] for pattern in removal_patterns: author_year_title = author_year_title.split(pattern)[0] author_year = author_year_title[: author_year_title.rfind(".")] # Loop through what is left to find the year for ref_part in author_year.split(): if ref_part.endswith("."): # Remove trailing full stop ref_part = ref_part[:-1] if len(ref_part) == 4 and ref_part.isnumeric(): year = ref_part elif len(ref_part) == 5 and ref_part[:-1].isnumeric(): year = ref_part return f"{author}_{year}"
[docs] def flux_from_atnf(pulsar, query=None, ref_dict=None, assumed_error=0.5): """Queries the ATNF database for flux info on a particular pulsar at all frequencies. Parameters ---------- pulsar : `str` The Jname of the pulsar. query : psrqpy object, optional A previous psrqpy.QueryATNF query. Can be supplied to prevent performing a new query. ref_dict : `dict`, optional A previous psrqpy.get_references query. Can be supplied to prevent performing a new query. assumed_error : `float`, optional If no error found, apply this factor to flux to make an assumed error. |br| Default: 0.5. Returns ------- freq_all : `list` All frequencies in Hz with flux values on ATNF. band_all : `list` All frequencies in Hz with flux values on ATNF. Note: since the ATNF catalogue does not currently store bandwidth information, the list will be filled with `None` values. flux_all : `list` The flux values corresponding to the freq_all list in mJy. flux_err_all : `list` The uncertainty in the flux_all values. references : `list` The reference keys from: https://www.atnf.csiro.au/research/pulsar/psrcat/psrcat_ref.html """ # Handle psrqpy queries if None were given if query is None: query = psrqpy.QueryATNF(version=ATNF_VER, psrs=[pulsar]).pandas if ref_dict is None: ref_dict = get_atnf_references() query_id = list(query["PSRJ"]).index(pulsar) # Find all flux queries from keys flux_queries = [] for table_param in query.keys(): if re.match(r"S\d*\d$", table_param) or re.match(r"S\d*G$", table_param): flux_queries.append(table_param) freq_all = [] band_all = [] flux_all = [] flux_err_all = [] references = [] # Get all available data from dataframe and check for missing values for flux_query in flux_queries: flux = query[flux_query][query_id] # Check for flux if not np.isnan(flux): flux_all.append(flux) # in mJy # Check for flux error. Sometimes error values don't exist, causing a key error in pandas flux_error_found = True try: flux_err = query[flux_query + "_ERR"][query_id] if np.isnan(flux_err) or flux_err == 0.0: flux_error_found = False except KeyError: flux_error_found = False if not flux_error_found: logger.debug( "{0} flux error for query: {1}, is zero. Assuming {2:.1f}% uncertainty".format( pulsar, flux_query, assumed_error * 100 ) ) flux_err = flux * assumed_error flux_err_all.append(flux_err) # in mJy # Converts key to frequency in MHz if flux_query.endswith("G"): # In GHz to convert to MHz freq = int(flux_query[1:-1]) * 1e3 else: freq = int(flux_query[1:]) freq_all.append(freq) # The ATNF catalogue does not include bandwidth information, so we use a default # bandwidth of 1 MHz for all ATNF data. The accuracy of this approximation depends on # the fractional bandwidth of the telescope and the accuracy of the reported centre # frequency in the catalogue. We have therefore included a warning to notify the user # about this in `pulsar_spectra.spectral_fit.find_best_spectral_fit()`. band_all.append(1.0) # Grab reference code and convert to "Author Year" format # If reference is not found, fallback to ref_code ref_code = query[flux_query + "_REF"][query_id] ref = convert_atnf_ref(ref_code, ref_dict=ref_dict) if ref is None: logger.warning(f"no name found for reference {ref_code}") ref = ref_code references.append(f"{ref}_ATNF") return freq_all, band_all, flux_all, flux_err_all, references
[docs] def all_flux_from_atnf(query=None, adjust_errors=True): """Queries the ATNF database for flux info for all pulsar at all frequencies. Parameters ---------- query : psrqpy object, optional A previous psrqpy.QueryATNF query. Can be supplied to prevent performing a new query. adjust_errors : `bool`, optional Whether to adjust the errors to be at least 50% of the flux value. Default: True. Returns ------- jname_cat_dict : `dict` Catalgoues dictionary with the keys in the format jname_cat_dict[jname][ref]['Frequency MHz', 'Bandwidth MHz', 'Flux Density mJy', 'Flux Density error mJy'] ``'jname'`` : `str` The pulsar's Jname. ``'ref'`` : `str` The reference label. ``'Frequency MHz'`` The observing frequency in MHz. ``'Bandwidth MHz'`` The observing bandwidth in MHz. ``'Flux Density mJy'`` The flux density in mJy. ``'Flux Density error mJy'`` The error of the flux density in mJy. """ if query is None: query = psrqpy.QueryATNF(version=ATNF_VER).pandas ref_dict = get_atnf_references() jnames = list(query["PSRJ"]) jname_cat = {} for jname in jnames: jname_cat[jname] = {} freq_all, band_all, flux_all, flux_err_all, references = flux_from_atnf(jname, query=query, ref_dict=ref_dict) for freq, band, flux, flux_err, ref in zip(freq_all, band_all, flux_all, flux_err_all, references): if ref not in jname_cat[jname].keys(): jname_cat[jname][ref] = { "Frequency MHz": [], "Bandwidth MHz": [], "Flux Density mJy": [], "Flux Density error mJy": [], } jname_cat[jname][ref]["Frequency MHz"] += [freq] # Add Nones so the software can easily tell there are missing bandwidths jname_cat[jname][ref]["Bandwidth MHz"] += [band] jname_cat[jname][ref]["Flux Density mJy"] += [flux] if adjust_errors: jname_cat[jname][ref]["Flux Density error mJy"] += [flux_err if flux_err >= 0.5 * flux else 0.5 * flux] else: jname_cat[jname][ref]["Flux Density error mJy"] += [flux_err] return jname_cat
[docs] def collect_catalogue_fluxes(only_use=None, exclude=None, query=None, use_atnf=True, adjust_errors=True): """Collect the fluxes from all of the catalogues recorded in this repo. Parameters ---------- only_use : `list`, optional A list of reference labels (in the format 'Author_year') of all the papers you want to use. exclude : `list`, optional A list of reference labels (in the format 'Author_year') of all the papers you want to exclude. query : psrqpy object, optional A previous psrqpy.QueryATNF query. Can be supplied to prevent performing a new query. use_atnf: `bool`, optional Whether the ATNF values should be included. Default: True. adjust_errors : `bool`, optional Whether to adjust the errors to be at least 50% of the flux value. Default: True. Returns ------- jname_cat_list[jname] : `dict` Catalgoues dictionary with the keys: ``'jname'`` : `str` The pulsar's Jname. Each dictionary contains a list of lists with the following: Frequency MHz : `list` The observing frequency in MHz. Bandwidth MHz : `list` The observing bandwidth in MHz. Flux Density mJy : `list` The flux density in mJy. Flux Density error mJy : `list` The error of the flux density in mJy. ref : `list` The reference label (in the format 'Author_year'). """ if query is None: query = psrqpy.QueryATNF(version=ATNF_VER).pandas # Make a dictionary for each pulsar jnames = list(query["PSRJ"]) jname_cat_list = {} for jname in jnames: # freq, band,flux, flux_err, references jname_cat_list[jname] = [[], [], [], [], []] # Work out which yamls/catalogues to use if only_use is None: # Use all yamls yamls_to_use = CAT_YAMLS else: yamls_to_use = [] for yaml_label in only_use: y_dir = f"{CAT_DIR}/{yaml_label}.yaml" if os.path.isfile(y_dir): yamls_to_use.append(y_dir) else: logger.warning(f"{yaml_label} not found in {CAT_DIR}") # Work out which yamls/catalogues to exclude if exclude is not None: yamls_to_check = yamls_to_use yamls_to_use = [] for y_dir in yamls_to_check: yaml_label = os.path.basename(y_dir).split(".")[0] if yaml_label not in exclude: yamls_to_use.append(y_dir) # Loop over catalogues and put them into a dictionary yamls_to_use.sort() for cat_file in yamls_to_use: cat_label = os.path.basename(cat_file).split(".")[0] # Load in the dict with open(cat_file, "r") as stream: cat_dict = yaml.safe_load(stream) obs_span = cat_dict["Paper Metadata"]["Observation Span"] # Find which pulsars in the dictionary for jname in jnames: if jname in cat_dict.keys(): # Adjust uncertainties based on observations span fluxes = np.array(cat_dict[jname]["Flux Density mJy"]) flux_errs = np.array(cat_dict[jname]["Flux Density error mJy"]) if obs_span == "Single-epoch" and adjust_errors: # Use 50% flux error if the error is less than this flux_errs = np.maximum(flux_errs, 0.5 * fluxes) elif obs_span == "Several-epoch" and adjust_errors: flux_errs = np.maximum(flux_errs, 0.3 * fluxes) # Do nothing for "Multiple-epoch" as the errors should be accurate # Update list jname_cat_list[jname][0] += cat_dict[jname]["Frequency MHz"] jname_cat_list[jname][1] += cat_dict[jname]["Bandwidth MHz"] jname_cat_list[jname][2] += cat_dict[jname]["Flux Density mJy"] jname_cat_list[jname][3] += list(flux_errs) jname_cat_list[jname][4] += [cat_label] * len(cat_dict[jname]["Frequency MHz"]) if not use_atnf: # return before including atnf return jname_cat_list # Add the atnf to the cataogues atnf_dict = all_flux_from_atnf(query=query, adjust_errors=adjust_errors) # refs that have errors that we plan to inform ATNF about atnf_incorrect_refs = [ "Zhao_2019", "Mignani_2017", "Bell_2016", "Robinson_1995", "Johnston_1994", "Manchester_1996", "Xie_2019", "Han_2016", "Kramer_1999", "Kondratiev_2015", "Crawford_2001", "Michilli_2020", "Manchester_2013", "Brinkman_2018", "Fruchter_1990", ] # refs that are correct but where scaled to by their spectral index for the ATNF frequencies atnf_adjusted_refs = [ "Lorimer_1995b", "Stovall_2015", "Sanidas_2019", "Wolszczan_1992", "Dembska_2014", "Kaur_2019", "Alam_2021", "Foster_1991", ] # refs that were rounded to different decimal places than the publications atnf_rounded_refs = [ "Johnston_2018", "Dai_2015", "McEwen_2020", "McConnell_1991", "Bondonneau_2020", "Johnston_2021", "Bates_2011", "Han_2021", "Sayer_1997", "Lynch_2012", "Stovall_2014", "Crowter_2020", "Bilous_2016", "Frail_2016", "Gitika_2023", "Dembska_2015", "Wang_2024", "Keith_2024", "Deneva_2024", ] # refs that have different uncertainties than published atnf_uncert_refs = [ "Stairs_1999", "Kuzmin_2001", "Jankowski_2019", "Jankowski_2018", "Kramer_2003a", "Manchester_2001", "Morris_2002", "Zhang_2019", "Bangale_2024", "Martsen_2022", ] atnf_other_refs = [ "Taylor_1993", # excluding due to duplication of other references "Ahmad_2024", # need to add this to the pulsar_spectra catalogue properly "Spiewak_2022", # delibrately excluded for pulsars with Gitika_2023 data, see Issue #108 "Ro.Zko_2018", # named Rozko_2018 in pulsar_spectra catalogue, so strings don't match "Ro.zko_2021", # named Rozko_2021 in pulsar_spectra catalogue, so strings don't match "Kijak_2021", # frequencies were rounded to nearest 100 MHz ] for jname in jnames: for ref in atnf_dict[jname].keys(): # Remove "_atnf" from the end of the reference raw_ref = ref[:-5] # Check if only_use or exclude allow this ref if only_use is not None: if raw_ref not in only_use: # Not in only_use so skip continue if exclude is None: exclude = [] if ( raw_ref in exclude + atnf_incorrect_refs + atnf_adjusted_refs + atnf_rounded_refs + atnf_uncert_refs + atnf_other_refs ): # exclude by skipping continue for freq, band, flux, flux_err in zip( atnf_dict[jname][ref]["Frequency MHz"], atnf_dict[jname][ref]["Bandwidth MHz"], atnf_dict[jname][ref]["Flux Density mJy"], atnf_dict[jname][ref]["Flux Density error mJy"], ): # Check for redundant data if ( flux in jname_cat_list[jname][2] and flux_err in jname_cat_list[jname][3] and raw_ref in jname_cat_list[jname][4] ): logger.debug( f"Redundant ATNF data removed: pulsar:{jname} ref:{raw_ref} freq:{freq} flux:{flux} flux_err:{flux_err}" ) else: # Update list jname_cat_list[jname][0] += [freq] jname_cat_list[jname][1] += [band] jname_cat_list[jname][2] += [flux] jname_cat_list[jname][3] += [flux_err] jname_cat_list[jname][4] += [ref] return jname_cat_list
[docs] def convert_cat_list_to_dict(jname_cat_list): """ Returns ------- jname_cat_dict : `dict` Catalgoues dictionary with the keys in the format jname_cat_dict[jname][ref]['Frequency MHz', 'Flux Density mJy', 'Flux Density error mJy'] ``'jname'`` : `str` The pulsar's Jname. ``'ref'`` : `str` The reference label. ``'Frequency MHz'`` The observing frequency in MHz. ``'Flux Density mJy'`` The flux density in mJy. ``'Flux Density error mJy'`` The error of the flux density in mJy. """ jname_cat_dict = {} for jname in jname_cat_list.keys(): freqs, bands, fluxs, flux_errs, refs = jname_cat_list[jname] jname_cat_dict[jname] = {} # Loop over and put references into the same dict for freq, band, flux, flux_err, ref in zip(freqs, bands, fluxs, flux_errs, refs): if ref in jname_cat_dict[jname].keys(): # Update jname_cat_dict[jname][ref]["Frequency MHz"] += [freq] jname_cat_dict[jname][ref]["Bandwidth MHz"] += [band] jname_cat_dict[jname][ref]["Flux Density mJy"] += [flux] jname_cat_dict[jname][ref]["Flux Density error mJy"] += [flux_err] else: # Make new jname_cat_dict[jname][ref] = {} jname_cat_dict[jname][ref]["Frequency MHz"] = [freq] jname_cat_dict[jname][ref]["Bandwidth MHz"] = [band] jname_cat_dict[jname][ref]["Flux Density mJy"] = [flux] jname_cat_dict[jname][ref]["Flux Density error mJy"] = [flux_err] return jname_cat_dict