Getting started with ROSAT All Sky Survey data

Contents

Getting started with ROSAT All Sky Survey data#

Learning Goals#

By the end of this tutorial, you will be able to:

  • Fetch a catalog from Vizier and cross-match it with a catalog hosted by HEASARC.

  • Identify and fetch ROSAT All-Sky Survey (RASS) data relevant to a sample of sources.

  • Examine pregenerated RASS images.

  • Generate new RASS images with custom spatial binning and energy bands.

  • Acquire the ROSAT-PSPCC Redistribution Matrix File (RMF), extract RASS spectra, and generate Ancillary Response Files (ARFs) for a sample of sources; then fit models using PyXSPEC and extract results.

Introduction#

The ROSAT All Sky Survey (RASS) was, unsurprisingly, a survey that observed the entire sky using the ROSAT (standing for ‘Röntgensatellit’) X-ray mission. ROSAT launched in 1990 and was active until the beginning of 1999, when it was shut down after significant deterioration of the satellite’s navigational systems.

Three X-ray instruments could be moved into the focal plane of the single X-ray telescope mounted on the spacecraft (though they could not be used simultaneously):

  • High Resolution Imager (HRI) - A micro-channel plate (MCP) imager very similar to the one flown on the Einstein Observatory in 1978. High spatial resolution (~\(2^{\prime\prime}\)), but effectively no spectral resolution.

  • Position Sensitive Proportional Counter B (PSPCB) - One of a pair of proportional counters that could measure the position and energy of an incident photon using the charge produced when it was absorbed by the detector gas. Had moderate spatial resolution (~\(25^{\prime\prime}\)), low spectral resolution (~41% at 1 keV), and was sensitive in the 0.07–2.4 keV range.

  • Position Sensitive Proportional Counter C (PSPCC) - The second of a pair of proportional counters, PSPCC was the primary instrument, and was used to perform the ROSAT All-Sky Survey at the beginning of the mission. It was destroyed in 1991 after an error caused ROSAT to slew across the Sun.

ROSAT also had an extreme ultraviolet (XUV) imager called the Wide Field Camera (WFC), with a 5\(^{\circ}\) diameter field of view (FoV), a spatial resolution of ~\(2.3^{\prime}\), and was sensitive between 62–206 eV (~60–100 Å).

The ROSAT All-Sky Survey was taken using the ROSAT-PSPCC instrument, though it was left incomplete following the destruction of the instrument in 1991. Follow-up observations to fill in the gaps were performed using the PSPCB instrument much later in the mission’s life, but were taken in ‘pointed’ rather than ‘scanning’ mode, and as such are not included in the RASS archive. Instead, they are archived with all other pointed ROSAT observations and will not be used in this demonstration.

The effective angular resolution of RASS was worse than that of the PSPC instruments, at ~45\(^{\prime\prime}\), as the spacecraft was constantly slewing while taking the observations.

RASS’ data are organized into ‘skyfields’, each with their own sequence ID. Each skyfield represents a \(6.4^{\circ}\times6.4^{\circ}\) area of the sky, and is built from multiple slewing observations.

This tutorial will give you the skills required to start using RASS observations to measure X-ray properties of a set of sources. To demonstrate, we will be using a sample of over 700 M dwarf stars from the ‘CARMENES input catalogue of M dwarfs’ (Alonso-Floriano F. J. et al. 2015). We won’t be analyzing the entire dataset. However, there will still be a substantial number of sources to work with, which will give you an idea of how to use RASS data for large samples (one of the best use cases).

We also hope to make clear the limitations of what you can do with RASS data; ROSAT is one of the older X-ray missions and utilized less sophisticated instrumentation and optics than more modern observatories. That does impose restrictions on what we can reasonably expect to achieve, in terms of energy range coverage, sensitivity, and spectral/spatial resolution.

On the other hand, the ROSAT All-Sky Survey is still (as of early 2026), the only publicly available all-sky X-ray imaging dataset, with over 1.35e+5 sources in the ‘Second ROSAT all-sky survey’ source catalog (2RXS; Boller T. et al. 2016). The scientific potential of the RASS archive is still very great, and being able to directly analyze the data, rather than rely solely on catalogs, may help you with your own research interests.

Inputs#

Outputs#

  • Visualizations of pre-processed RASS images.

  • Newly generated RASS images.

  • Source/background region files and spectra.

  • Result table from fitting spectral models using PyXSPEC, and accompanying visualizations of spectra.

Runtime#

As of 12th March 2026, this notebook takes ~13 m to run to completion on Fornax using the ‘medium’ server with 16GB RAM/ 4 cores.

Imports#

import contextlib
import multiprocessing as mp
import os
from random import randint
from shutil import copyfile, rmtree
from typing import Tuple
from warnings import warn

import heasoftpy as hsp
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
import pyvo as vo
from astropy.coordinates import SkyCoord
from astropy.io import fits
from astropy.units import Quantity
from astropy.wcs import WCS
from astroquery.heasarc import Heasarc
from astroquery.vizier import Vizier
from matplotlib.ticker import FuncFormatter
from regions import CircleAnnulusSkyRegion, CircleSkyRegion, Regions, SkyRegion
from tqdm import tqdm
from xga.imagetools.misc import pix_deg_scale
from xga.products import EventList, ExpMap, Image, RateMap
/opt/envs/heasoft/lib/python3.12/site-packages/xga/utils.py:39: DeprecationWarning: The XGA 'find_all_wcs' function should be imported from imagetools.misc, in the future it will be removed from utils.
  warn(message, DeprecationWarning)
/opt/envs/heasoft/lib/python3.12/site-packages/xga/utils.py:619: UserWarning: SAS_DIR environment variable is not set, unable to verify SAS is present on system, as such all functions in xga.sas will not work.
  warn("SAS_DIR environment variable is not set, unable to verify SAS is present on system, as such "
/opt/envs/heasoft/lib/python3.12/site-packages/xga/__init__.py:6: UserWarning: This is the first time you've used XGA; to use most functionality you will need to configure /home/jovyan/.config/xga/xga.cfg to match your setup, though you can use product classes regardless.
  from .utils import xga_conf, CENSUS, OUTPUT, NUM_CORES, XGA_EXTRACT, BASE_XSPEC_SCRIPT, CROSS_ARF_XSPEC_SCRIPT, \
/opt/envs/heasoft/lib/python3.12/site-packages/xga/products/relation.py:12: DeprecationWarning: `scipy.odr` is deprecated as of version 1.17.0 and will be removed in SciPy 1.19.0. Please use `https://pypi.org/project/odrpack/` instead.
  import scipy.odr as odr
/opt/envs/heasoft/lib/python3.12/site-packages/xga/products/relation.py:12: DeprecationWarning: `scipy.odr` is deprecated as of version 1.17.0 and will be removed in SciPy 1.19.0. Please use `https://pypi.org/project/odrpack/` instead.
  import scipy.odr as odr

Global Setup#

Functions#

Hide code cell source

def gen_rass_image(
    event_file: str,
    out_dir: str,
    cur_seq_id: str,
    lo_en: Quantity,
    hi_en: Quantity,
    im_bin: int = 90,
):
    """
    This function wraps the HEASoft 'extractor' tool and is used to spatially bin
    ROSAT-PSPC event lists into images. The HEASoftPy interface to 'extractor' is used.

    Both the energy band and the image binning factor, which controls how
    many 'pixels' in the native SKY X-Y coordinate of the event list are binned into
    a single image pixel, can be specified.

    Default `im_bin` will produce a 512x512 image for RASS data.

    :param str event_file: Path to the event list (usually cleaned, but not
        necessarily) we wish to generate an image from.
    :param str out_dir: The directory where output files should be written.
    :param str cur_seq_id: RASS sequence ID (as found in HEASARC RASS table).
    :param Quantity lo_en: Lower bound of the energy band within which we will
        generate the image.
    :param Quantity hi_en: Upper bound of the energy band within which we will
        generate the image.
    :param int im_bin: Number of ROSAT-PSPC SKY X-Y pixels to bin into a single image
        pixel.
    """
    # Make sure the lower and upper energy limits make sense
    if lo_en > hi_en:
        raise ValueError(
            "The lower energy limit must be less than or equal to the upper "
            "energy limit."
        )
    else:
        lo_en_val = lo_en.to("keV").value
        hi_en_val = hi_en.to("keV").value

    # Convert the energy limits to channel limits, rounding down and up to the nearest
    #  integer channel for the lower and upper bounds respectively.
    lo_ch = np.floor((lo_en / PSPC_EV_PER_CHAN).to("chan")).value.astype(int)
    hi_ch = np.ceil((hi_en / PSPC_EV_PER_CHAN).to("chan")).value.astype(int)

    # Create modified input event list file path, where we use the just-calculated
    #  PI channel limits to subset the events
    evt_file_chan_sel = f"{event_file}[PI={lo_ch}:{hi_ch}]"

    # Set up the output file name for the image we're about to generate.
    im_out = os.path.basename(IM_PATH_TEMP).format(
        oi=cur_seq_id, ibf=im_bin, lo=lo_en_val, hi=hi_en_val
    )

    # Create a temporary working directory
    temp_work_dir = os.path.join(
        out_dir, "im_extractor_{}".format(randint(0, int(1e8)))
    )
    os.makedirs(temp_work_dir)

    # Using dual contexts, one that moves us into the output directory for the
    #  duration, and another that creates a new set of HEASoft parameter files (so
    #  there are no clashes with other processes).
    with contextlib.chdir(temp_work_dir), hsp.utils.local_pfiles_context():
        out = hsp.extractor(
            filename=evt_file_chan_sel,
            imgfile=im_out,
            noprompt=True,
            clobber=True,
            binf=im_bin,
            xcolf="X",
            ycolf="Y",
            gti="STDGTI",
            events="STDEVT",
            chatter=TASK_CHATTER,
            regionfile="NONE",
        )

    # Move the output image file to the proper output directory from
    #  the temporary working directory
    os.rename(os.path.join(temp_work_dir, im_out), os.path.join(out_dir, im_out))

    # Make sure to remove the temporary directory
    rmtree(temp_work_dir)

    return out


def gen_rass_spectrum(
    event_file: str,
    out_dir: str,
    cur_seq_id: str,
    source_name: str,
    rel_src_reg: SkyRegion,
    src_reg_file: str,
    back_reg_file: str,
    wmap_im_bin: int = 8,
) -> Tuple[hsp.core.HSPResult, hsp.core.HSPResult, str, str]:
    """
    Function that wraps the HEASoftPy interface to the HEASoft extractor tool, set
    up to generate spectra from ROSAT-PSPC observations. The function will
    generate a spectrum for the source region and a background spectrum for
    the background region.

    This function will only extract events from PI channels 0-256; everything
    above 256 is dicarded. ROSAT PSPC RMF files only cover channels 0-256, so
    there is no point including anything else.

    Input region files MUST be in the Sky X-Y coordinate system. The 'rel_src_reg'
    input must be a SkyRegion object (i.e., in the RA-Dec coordinate system) defining
    the source region - we will extract RA, Dec, and radius value from it.

    :param str event_file: Path to the event list (usually cleaned, but not
        necessarily) we wish to generate a ROSAT-PSPC spectrum from.
    :param str out_dir: The directory where output files should be written.
    :param str cur_seq_id: RASS sequence ID (as found in HEASARC RASS table).
    :param str source_name: The name of the source for which we are
        generating a spectrum.
    :param SkyRegion rel_src_reg: The SkyRegion object (i.e., in the RA-Dec coordinate
        system) defining the region from which we wish to generate a source spectrum.
        RA, Dec, and radius values will be extracted from this object.
    :param str src_reg_file: Path to the region file (IN THE SKY X-Y COORDINATE SYSTEM)
        defining the source region for which we wish to generate a spectrum.
    :param str back_reg_file: Path to the region file (IN THE SKY X-Y COORDINATE SYSTEM)
        defining the background region for which we wish to generate a spectrum.
    :param int wmap_im_bin: Number of ROSAT-PSPC SKY X-Y pixels to bin into a
        single image pixel for the 'weighted map' included in ROSAT spectra.
        Default is 8. BEWARE - very low values may cause you to run
        out of memory when generating spectra from all-sky data tiles.
    """

    # Get RA, Dec, and radius values in the right format
    ra_val = rel_src_reg.center.ra.to("deg").value.round(6)
    dec_val = rel_src_reg.center.dec.to("deg").value.round(6)
    rad_val = rel_src_reg.radius.to("deg").value.round(4)

    # Set up the output file names for the source and background spectra we're
    #  about to generate.
    sp_out = os.path.basename(SP_PATH_TEMP).format(
        oi=cur_seq_id, ra=ra_val, dec=dec_val, rad=rad_val, sn=source_name
    )
    sp_back_out = os.path.basename(BACK_SP_PATH_TEMP).format(
        oi=cur_seq_id, ra=ra_val, dec=dec_val, sn=source_name
    )

    # Create a temporary working directory
    temp_work_dir = os.path.join(
        out_dir, "spec_extractor_{}".format(randint(0, int(1e8)))
    )
    os.makedirs(temp_work_dir)

    # Using dual contexts, one that moves us into the output directory for the
    #  duration, and another that creates a new set of HEASoft parameter files (so
    #  there are no clashes with other processes).
    with contextlib.chdir(temp_work_dir), hsp.utils.local_pfiles_context():
        # We append a PI channel limit to match the number of
        #  channels in the PSPC RMF file
        src_out = hsp.extractor(
            filename=os.path.relpath(event_file) + "[PI=0:256]",
            phafile=sp_out,
            regionfile=os.path.relpath(src_reg_file),
            xcolf="X",
            ycolf="Y",
            xcolh="X",
            ycolh="Y",
            binh=wmap_im_bin,
            ecol="PI",
            gti="STDGTI",
            events="STDEVT",
            fullimage=False,
            noprompt=True,
            clobber=True,
            chatter=TASK_CHATTER,
        )

        # Now for the background spectrum
        back_out = hsp.extractor(
            filename=os.path.relpath(event_file) + "[PI=0:256]",
            phafile=sp_back_out,
            regionfile=os.path.relpath(back_reg_file),
            xcolf="X",
            ycolf="Y",
            xcolh="X",
            ycolh="Y",
            binh=wmap_im_bin,
            ecol="PI",
            gti="STDGTI",
            events="STDEVT",
            fullimage=False,
            noprompt=True,
            clobber=True,
            chatter=TASK_CHATTER,
        )

    # Move the spectra up from the temporary directory
    fin_sp_out = os.path.join(out_dir, sp_out)
    os.rename(os.path.join(temp_work_dir, sp_out), fin_sp_out)

    fin_bsp_out = os.path.join(out_dir, sp_back_out)
    os.rename(os.path.join(temp_work_dir, sp_back_out), fin_bsp_out)

    # Make sure to remove the temporary directory
    rmtree(temp_work_dir)

    return src_out, back_out, fin_sp_out, fin_bsp_out


def gen_rosat_pspc_arf(
    out_dir: str,
    spec_file: str,
    rmf_file: str = "CALDB",
) -> Tuple[hsp.core.HSPResult, str]:
    """
    A wrapper function for the HEASoft `pcarf` task, which we use to generate
    ARFs for ROSAT-PSPC spectra.

    :param str out_dir: The directory where output files should be written.
    :param str spec_file: The path to the spectrum file for which to generate an ARF.
    :param str rmf_file: The path to the RMF file necessary to generate an ARF.
    """

    # Create a temporary working directory
    temp_work_dir = os.path.join(out_dir, "pcarf_{}".format(randint(0, int(1e8))))
    os.makedirs(temp_work_dir)

    # We can use the spectrum file name to set up the output ARF file name
    arf_out = os.path.basename(spec_file).replace("-spectrum.fits", ".arf")

    # Using dual contexts, one that moves us into the output directory for the
    #  duration, and another that creates a new set of HEASoft parameter files (so
    #  there are no clashes with other processes).
    with contextlib.chdir(temp_work_dir), hsp.utils.local_pfiles_context():

        out = hsp.pcarf(
            phafil=os.path.relpath(spec_file),
            outfil=os.path.relpath(arf_out),
            rmffile=rmf_file,
            noprompt=True,
            clobber=True,
            chatter=TASK_CHATTER,
        )

    # Move the ARF file up from the temporary directory
    fin_arf_out = os.path.join(out_dir, arf_out)
    os.rename(os.path.join(temp_work_dir, arf_out), fin_arf_out)

    # Make sure to remove the temporary directory
    rmtree(temp_work_dir)

    return out, fin_arf_out

Constants#

Hide code cell source

# Controls the verbosity of all HEASoftPy tasks
TASK_CHATTER = 0

# The approximate energy per channel for ROSAT-PSPC
PSPC_EV_PER_CHAN = Quantity(9.9, "eV/chan")

Configuration#

Hide code cell source

# ------------- Configure global package settings --------------
# Raise Python exceptions if a heasoftpy task fails
# TODO Remove once this becomes a default in heasoftpy
hsp.Config.allow_failure = False

# Set up the method for spawning processes.
mp.set_start_method("fork", force=True)
# --------------------------------------------------------------


# ------------- Setting how many cores we can use --------------
# We use a service called CircleCI to execute, test, and validate these notebooks
#  as we're writing and maintaining them. Unfortunately we have to treat the
#  determination of the number of cores we can use differently, as the
#  'os.cpu_count()' call will return the number of cores of the host machine, rather
#  than the number that have actually been allocated to us.
if "CIRCLECI" in os.environ and bool(os.environ["CIRCLECI"]):
    # Here we read the CPU quota (total CPU time allowed) and the CPU period (how
    #  long the scheduling window is) from a cgroup (a linux kernel feature) file.
    # Dividing one by t'other provides the number of cores we've been allocated.
    with open("/sys/fs/cgroup/cpu.max", "r") as cpu_maxo:
        quota, period = cpu_maxo.read().strip().split()
        NUM_CORES = int(quota) // int(period)

# If you, the reader, are running this notebook yourself, this is the
#  part that is relevant to you - you can override the default number of cores
#  used by setting this variable to an integer value.
else:
    NUM_CORES = None

# Determines the number of CPU cores available
total_cores = os.cpu_count()

# If NUM_CORES is None, then we use the number of cores returned by 'os.cpu_count()'
if NUM_CORES is None:
    NUM_CORES = total_cores
# Otherwise, NUM_CORES has been overridden (either by the user, or because we're
#  running on CircleCI, and we do a validity check.
elif not isinstance(NUM_CORES, int):
    raise TypeError(
        "If manually overriding 'NUM_CORES', you must set it to an integer value."
    )
elif isinstance(NUM_CORES, int) and NUM_CORES > total_cores:
    raise ValueError(
        f"If manually overriding 'NUM_CORES', the value must be less than or "
        f"equal to the total available cores ({total_cores})."
    )
# --------------------------------------------------------------


# -------------- Set paths and create directories --------------
if os.path.exists("../../../_data"):
    ROOT_DATA_DIR = "../../../_data/RASS/"
else:
    ROOT_DATA_DIR = "RASS/"

ROOT_DATA_DIR = os.path.abspath(ROOT_DATA_DIR)

# Make sure the download directory exists.
os.makedirs(ROOT_DATA_DIR, exist_ok=True)

# Setup path and directories into which we save output files from this example.
ROOT_OUT_PATH = os.path.abspath("RASS_output")

# We're dealing with a sample of sources - some data products we generate will
#  be valid for any source in a particular RASS sequence, but others will
#  be specific to a source.
# As such, we have two skews of output paths: one for the source-specific products,
#  and one for the global products.
SEQ_OUT_PATH = os.path.join(ROOT_OUT_PATH, "global_products")
os.makedirs(SEQ_OUT_PATH, exist_ok=True)

SRC_OUT_PATH = os.path.join(ROOT_OUT_PATH, "source_products")
os.makedirs(SRC_OUT_PATH, exist_ok=True)

# --------------------------------------------------------------


# ------------- Set up output file path templates --------------
# --------- IMAGES ---------
IM_PATH_TEMP = os.path.join(
    SEQ_OUT_PATH,
    "{oi}",
    "rosat-pspc-seqid{oi}-imbinfactor{ibf}-en{lo}_{hi}keV-image.fits",
)
# --------------------------

# -------- REGIONS ---------
SRC_REG_PATH_TEMP = os.path.join(
    SRC_OUT_PATH,
    "{sn}",
    "rosat-pspc-seqid{oi}-name{sn}-source.reg",
)

BCK_REG_PATH_TEMP = os.path.join(
    SRC_OUT_PATH,
    "{sn}",
    "rosat-pspc-seqid{oi}-name{sn}-background.reg",
)
# --------------------------

# -------- SPECTRA ---------
SP_PATH_TEMP = os.path.join(
    SRC_OUT_PATH,
    "{sn}",
    "rosat-pspc-seqid{oi}-name{sn}-ra{ra}-dec{dec}-radius{rad}deg-"
    "enALL-spectrum.fits",
)

BACK_SP_PATH_TEMP = os.path.join(
    SRC_OUT_PATH,
    "{sn}",
    "rosat-pspc-seqid{oi}-name{sn}-ra{ra}-dec{dec}-enALL-back-spectrum.fits",
)
# --------------------------

# ---- GROUPED SPECTRA -----
GRP_SP_PATH_TEMP = SP_PATH_TEMP.replace("-spectrum", "-{gt}grp{gs}-spectrum")
# --------------------------

# ---------- RMF -----------
RMF_PATH_TEMP = os.path.join(SRC_OUT_PATH, "{sn}", "rosat-pspc-seqid{oi}.rmf")
# --------------------------

# ---------- ARF -----------
ARF_PATH_TEMP = SP_PATH_TEMP.replace("-spectrum.fits", ".arf")
# --------------------------
# --------------------------------------------------------------


# ---------- Set up preprocessed file path templates -----------
# --------- EVENTS ---------
PREPROC_EVT_PATH_TEMP = os.path.join(
    ROOT_DATA_DIR,
    "{loi}",
    "{loi}_bas.fits.Z",
)
# --------------------------

# --------- IMAGES ---------
# Specifically 'band 1' images between 0.07-2.4 keV
PREGEN_IMAGE_PATH_TEMP = os.path.join(
    ROOT_DATA_DIR,
    "{loi}",
    "{loi}_im1.fits.Z",
)
# --------------------------

# -------- EXPMAPS ---------
PREGEN_EXPMAP_PATH_TEMP = os.path.join(
    ROOT_DATA_DIR,
    "{loi}",
    "{loi}_mex.fits",
)
# --------------------------
# --------------------------------------------------------------

1. Fetching the CARMENES M dwarf catalog and matching to a RASS catalog#

We stated in the introduction that we would use the CARMENES ‘input catalog of M dwarfs’ as the starting point for this demonstration. That way, we can show you how to approach RASS data analysis for a sample of sources.

To use the catalog, we’re going to need to acquire it from somewhere. In this case, that somewhere is the VizieR service (DOI:10.26093/cds/vizier), which we will access using the Astroquery Python module (Ginsburg et al. 2019).

Getting the CARMENES catalog from VizieR#

We have already imported the Vizier class from Astroquery, so we can now set up an instance of it (with some non-default arguments) that can be used to fetch our catalog of interest.

The row_limit=-1 argument tells Astroquery to return all rows from the catalog, and the columns=["**", "_RAJ2000", "_DEJ2000"] tells it to also return every column (as well as the VizieR-standard decimal degree RA and Dec values):

viz = Vizier(row_limit=-1, columns=["**", "_RAJ2000", "_DEJ2000"])
viz
<astroquery.vizier.core.VizierClass at 0x76a5f8948890>

We already know the ‘bibcode’ of the CARMENES catalog (J/A+A/577/A128), but if you didn’t, you could search VizieR using the viz object we created.

By passing a list of keywords (every keyword must be associated with a catalog for that catalog to be returned) to the find_catalogs() method, we find a few possible matches. To narrow them down further, we can display the short description of each returned catalog:

cat_search = viz.find_catalogs(["CARMENES", "input"])

# Return is an ordered dictionary, with bibcode keys and catalog object values
for cur_bibcode, cur_cat in viz.find_catalogs(["CARMENES", "input"]).items():
    print(cur_bibcode, "-", cur_cat.description)
J/A+A/577/A128 - CARMENES input catalogue of M dwarfs. I (Alonso-Floriano+, 2015)
J/A+A/597/A47 - CARMENES input catalogue of M dwarfs II (Cortes-Contreras+ 2017)
J/A+A/614/A76 - CARMENES input catalogue of M dwarfs. III. (Jeffers+, 2018)
J/A+A/621/A126 - CARMENES input catalogue of M dwarfs. IV. (Diez Alonso+ 2019)
J/A+A/642/A115 - CARMENES input catalogue of M dwarfs. V. (Cifuentes+, 2020)
J/A+A/652/A116 - CARMENES time-resolved CaII H&K catalog (Perdelwitz+, 2021)
J/A+A/684/A9 - Rotation periods for 261 M dwarfs (Shan+, 2024)
J/A+A/692/A206 - CARMENES input cat. of M dwarfs VIII (Cortes-Contreras+, 2024)
J/A+A/693/A228 - CARMENES input catalogue of M dwarfs. IX. (Cifuentes+, 2025)

With the short descriptions shown above, you should be able to find the bibcode of the catalog you’re interested in.

Passing the bibcode of your chosen catalog to the get_catalogs() method presents us with a TableList object that contains one entry per table included in the catalog.

The CARMENES catalog we’re looking at contains two tables:

  • The first is the catalog of M dwarfs we’re going to use.

  • The second contains the literature references from which the catalog was compiled.

carm_samp = viz.get_catalogs("J/A+A/577/A128")
carm_samp
TableList with 2 tables:
	'0:J/A+A/577/A128/Mstars' with 40 column(s) and 753 row(s) 
	'1:J/A+A/577/A128/refs' with 5 column(s) and 61 row(s) 

We pull out the main catalog table, which is an Astropy Table object:

carm_cat = carm_samp[0]
carm_cat
Table length=753
_RAJ2000_DEJ2000recnoNoKarmnNameGl/GJRAJ2000DEJ2000JmagDateNexptexpNexp2texp2PC1TiO2TiO5VOCol-MCaH2CaH3zetapEWae_pEWaE_pEWaSpTlr_SpTll_SpTbSpTbl_SpTcSpTcSpT2SpT5SpTPSpTRSpTCl_SpTSpTSimbad
degdegmagss0.1 nm0.1 nm0.1 nm
float64float64int32int16str13str23str8str11str11float32str10uint8float32uint8int16float32float32float32float32float32float32float32float32float32float32float32str15str16str2float32str2float32float32float32float32float32float32str1str7str6
1.6633333-7.093138911J00066-070AB2MASS J00063925-070535400 06 39.20-07 05 35.39.832012-08-0411000.0----1.2690.4920.3171.1481.7780.4040.6540.973-2.30.50.3M3.5V+m4.5:Reid07,Jan124.54.54.54.54.04.54.5M4.5VSimbad
1.927500060.381750022J00077+603ABG 217-03200 07 42.60+60 22 54.38.912012-09-241600.0----1.1980.5320.3691.1111.4050.4080.6310.883-6.70.40.3M4.5VLep134.04.04.54.03.54.03.5M4.0VSimbad
2.882583359.144444433J00115+591LSR J0011+590800 11 31.82+59 08 40.09.952012-01-112700.0----1.5110.3780.2021.2222.9020.2810.5640.970-1.60.40.2M5.5VLep036.06.05.56.05.55.55.5M5.5VSimbad
2.970958322.984638944J00118+229LP 348-4000 11 53.03+22 59 04.78.862011-12-071250.0----1.2150.6060.4051.0901.4040.4920.7511.052-0.50.20.2M3.5VReid043.53.53.53.54.04.03.5M3.5VSimbad
2.985583333.054944455J00119+330G 130-05300 11 56.54+33 03 17.89.072011-12-071220.0----1.1670.6340.4271.0721.2930.5030.7481.023-0.30.20.1M3.5VGiz973.53.03.53.53.53.53.5M3.5VSimbad
3.055875030.478972266J00122+3041RXS J001213.6+30290600 12 13.41+30 28 44.310.242011-11-121600.0----1.2960.4970.3541.1541.7550.4270.6850.972-8.70.50.4M5.0VAbe144.54.54.54.04.55.04.5M4.5VSimbad
3.331333327.558638977J00133+275[ACM2014] J0013+273300 13 19.52+27 33 31.110.432011-11-121900.0----1.2960.5120.3391.1441.8050.4250.6860.994-4.00.40.2M4.5VAbe144.54.54.54.54.54.54.5M4.5VSimbad
3.411291780.665777888J00136+806G 242-0483014 A00 13 38.71+80 39 56.87.762012-09-011300.0----1.0270.7700.6011.0120.9010.6440.8121.0020.00.20.2M1.5VPMSU1.51.51.51.51.51.51.5M1.5VSimbad
3.650666720.206694499J00146+202chi Peg00 14 36.16+20 12 24.11.762012-01-1111.0----0.9550.7290.5201.0050.6430.8390.9462.7540.80.10.1M2IIIGar89,Kir91--------------MIIISimbad
........................................................................................................................
355.592125034.9743611745745J23423+349PM I23423+345823 42 22.11+34 58 27.79.322012-01-041350.0----1.2150.5910.3831.0941.4470.4670.7321.029-0.60.40.24.04.04.04.04.04.03.5M4.0VSimbad
355.639583339.2398056746746J23425+392LP 291-00723 42 33.50+39 14 23.39.642012-01-0911000.0----0.9280.9040.8230.9820.7390.8750.9211.054-1.60.30.40.00.0-0.5-0.50.00.00.0M0.0VSimbad
355.971250061.0376944747747J23438+610G 217-01823 43 53.10+61 02 15.79.392012-01-041500.0----1.1100.6410.4341.0591.1760.4920.7310.9740.00.40.4k:Simbad3.03.03.53.52.53.03.0M3.0VSimbad
357.2595833-8.4085833748748J23490-086G 273-14423 49 02.30-08 24 30.99.502012-08-021272.0----1.0540.7370.5341.0290.9990.5630.7750.9470.00.40.4M2.5VReid042.02.02.02.52.02.02.0M2.0VSimbad
358.9800000-13.3566111749749J23559-133NLTT 5844123 55 55.20-13 21 23.89.262012-09-021250.0----1.1840.5680.4051.0941.3310.4720.7000.959-4.20.40.5M3.0VSch053.53.54.03.53.54.03.5M3.5VSimbad
359.001208315.0280278750750J23560+150LP 523-07823 56 00.29+15 01 40.99.382011-12-071250.0----1.1020.7090.5171.0401.1100.5600.7830.9900.00.40.42.52.52.52.52.52.52.5M2.5VSimbad
359.229583323.0840833751751J23569+230G 129-04523 56 55.10+23 05 02.79.152011-12-071300.0----1.0120.8010.6401.0070.9140.6800.8481.0450.00.40.4K7VSte861.51.01.51.51.51.01.5M1.5VSimbad
359.625875024.2013333752752J23585+242G 131-00623 58 30.21+24 12 04.89.132012-09-04190.0----0.8980.9210.8360.9770.6770.9050.9311.1050.50.20.3K7VLee84-1.0-1.0-1.0-1.0-1.0-0.5-0.5K7VSimbad
359.751750020.8607778753753J23590+208G 129-05123 59 00.42+20 51 38.89.072011-12-071180.0----1.1110.6680.4691.0591.0970.5610.7851.0950.00.40.42.52.03.03.02.53.02.5M2.5VSimbad

Setting up a connection to the HEASARC TAP service#

So, we have the catalog of M dwarfs that we want to examine using the RASS data archive. At this point we could just feed the whole set of stars into the RASS analyses we perform later in this tutorial.

However, to simplify this demonstration, we would rather deal only with sources that have been detected in the ROSAT All-Sky Survey. To that end, we will perform a simple spatial cross-match between the CARMENES catalog and the 2RXS (Boller T. et al. 2016) catalog of RASS sources.

We will use the HEASARC Table Access Protocol (TAP) service to perform the cross-match, uploading the CARMENES table we just retrieved.

In order for us to be able to do that, we need to set up a connection to the HEASARC TAP service. Here we use the PyVO Python module to search for the right service:

tap_services = vo.regsearch(servicetype="tap", keywords=["heasarc"])
tap_services
<DALResultsTable length=1>
              ivoid                    res_type     ... cap_descriptions
                                                    ...                 
              object                    object      ...      object     
--------------------------------- ----------------- ... ----------------
ivo://nasa.heasarc/services/xamin vs:catalogservice ...                 

We can extract the first entry from that search return, and we have our connection!

heasarc_vo = tap_services[0]

Writing a query to match CARMENES to 2RXS#

Now we have a connection to the HEASARC TAP service, we will be able to upload our CARMENES table and perform a simple cross-match.

All that’s left is to write and submit an Astronomical Data Query Language (ADQL) query (almost a tautology) that tells the HEASARC TAP service to try and identify a 2RXS entry within a search radius of each CARMENES M dwarf.

We already know the HEASARC name for the 2RXS catalog (which we store in a variable below). However, if you want to match to a different catalog that you don’t already know the HEASARC name for you might want to look at the ‘Find specific HEASARC catalogs using Python demonstration.

heasarc_cat_name = "rass2rxs"

We select a search radius of 8\(^{\prime\prime}\), though you should consider your own choice carefully as it will depend on your science goals and the type of objects you want to look at:

MATCH_RADIUS = Quantity(8, "arcsec")

Finally, we write the query itself. As ADQL queries go, it’s fairly simple; the only matching (and filtering) criteria we apply is that a 2RXS source must be within the search radius of a CARMENES source to be considered a match.

It’s worth noting that we will be able to run this query on all the CARMENES sources at once, rather than having to run it separately for every entry.

Breaking down the query:

  • SELECT * will return all columns from both tables.

  • FROM {hcn} as rasscat will ‘load’ the HEASARC catalog with the alias ‘rasscat’ ({hcn} will be replaced by ‘rass2rxs’ in this case).

  • FROM ... tap_upload.carmenes as carm will ‘load’ the table we upload (see the query submission subsection) with the alias ‘carm’.

  • WHERE contains(point('ICRS',cat.ra,cat.dec), circle('ICRS',carm.{cra},carm.{cdec},{md}))=1 will require that a 2RXS coordinate (cat.ra and cat.dec) be within the search radius of a CARMENES coordinate (carm.{cra} and carm.{cdec}) to be considered a match.

query = (
    "SELECT * "
    "FROM {hcn} as rasscat, tap_upload.carmenes as carm "
    "WHERE "
    "contains(point('ICRS',rasscat.ra,rasscat.dec), "
    "circle('ICRS',carm.{cra},carm.{cdec},{md}))=1".format(
        md=MATCH_RADIUS.to("deg").value.round(4),
        cra="_RAJ2000",
        cdec="_DEJ2000",
        hcn=heasarc_cat_name,
    )
)

query
"SELECT * FROM rass2rxs as rasscat, tap_upload.carmenes as carm WHERE contains(point('ICRS',rasscat.ra,rasscat.dec), circle('ICRS',carm._RAJ2000,carm._DEJ2000,0.0022))=1"

Preparing the CARMENES catalog for upload#

Actually, writing the query wasn’t really the last thing we needed to do. Before we upload the CARMENES table and submit the matching query we have to make some adjustments to the CARMENES catalog.

These adjustments are necessary to avoid errors when using the HEASARC TAP service to run the matching query. Firstly, the HEASARC TAP service will change all column names to their lowercase equivalents. So, if there are any columns that are identically named, apart from the case of some letters, we have to rename them:

carm_cat.rename_column("e_pEWa", "pEWa_errmi")
carm_cat.rename_column("E_pEWa", "pEWa_errpl")

carm_cat.rename_column("SpTC", "SpTColor")

Additionally, if you include RA and Dec columns that are in sexagesimal format (as opposed to decimal degrees), you may encounter an error since the distance-calculation function does not work on string data types. As such, and because the author of this tutorial is biased against sexagesimal coordinates, we will just remove those columns:

carm_cat.remove_columns(["RAJ2000", "DEJ2000"])

Finally, we add a new column with a clean identifying name for each CARMENES source, based on the ‘No’ column containing the CARMENES unique entry number. When we start generating data products, it’s good to know you have IDs to include in file and directory names that don’t include special characters or spaces.

We note that the ‘Karmn’ column included in the CARMENES catalog would be another good candidate for this purpose.

carm_cat.add_column(
    ["CARMENES-" + str(carm_id) for carm_id in carm_cat["No"]], name="id_name"
)

Submitting the query to the HEASARC TAP service#

All the pieces have come together, and we can run the CARMENES-2RXS cross-match query by passing it to the service.run_sync(...) method of the HEASARC TAP service connection we retrieved earlier.

The CARMENES catalog can be passed straight into the uploads argument as it is an Astropy Table object. Note that the key of the dictionary passed to the uploads argument must match the name of the table in the query defined previously.

carm_2rxs_match = heasarc_vo.service.run_sync(query, uploads={"carmenes": carm_cat})

We can then convert the return to an Astropy Table and visualize it:

carm_2rxs_match = carm_2rxs_match.to_table()
carm_2rxs_match
Table length=83
rasscat___rowrasscat_entry_numberrasscat_namerasscat_skyfield_numberrasscat_skyfield_source_numberrasscat_detection_likelihoodrasscat_countsrasscat_counts_errorrasscat_count_raterasscat_count_rate_errorrasscat_exposurerasscat_rarasscat_decrasscat_liirasscat_biirasscat_lambdarasscat_betarasscat_source_extentrasscat_source_extent_errorrasscat_source_extent_probrasscat_hardness_ratio_1rasscat_hardness_ratio_1_errorrasscat_hardness_ratio_2rasscat_hardness_ratio_2_errorrasscat_unique_flagrasscat_extended_region_flagrasscat_nearby_src_det_flagrasscat_source_quality_flagrasscat_max_amplituderasscat_mean_count_raterasscat_mean_count_rate_errorrasscat_lc_countsrasscat_min_count_raterasscat_min_count_rate_errorrasscat_max_count_raterasscat_max_count_rate_errorrasscat_lc_chi2rasscat_excess_variancerasscat_excess_variance_errorrasscat_excess_variance_sigmarasscat_nh_galrasscat_plaw_nhrasscat_plaw_nh_errorrasscat_plaw_normrasscat_plaw_norm_errorrasscat_plaw_photon_indexrasscat_plaw_photon_index_errorrasscat_plaw_count_raterasscat_plaw_fluxrasscat_plaw_chi2_reducedrasscat_plaw_chi2rasscat_plaw_number_data_ptsrasscat_plaw_dofrasscat_mekal_nhrasscat_mekal_nh_errorrasscat_mekal_normrasscat_mekal_norm_errorrasscat_mekal_temperaturerasscat_mekal_temperature_errorrasscat_mekal_count_raterasscat_mekal_fluxrasscat_mekal_chi2_reducedrasscat_mekal_chi2rasscat_mekal_number_data_ptsrasscat_mekal_dofrasscat_bb_nhrasscat_bb_nh_errorrasscat_bb_normrasscat_bb_norm_errorrasscat_bb_temperaturerasscat_bb_temperature_errorrasscat_bb_count_raterasscat_bb_fluxrasscat_bb_chi2_reducedrasscat_bb_chi2rasscat_bb_number_data_ptsrasscat_bb_dofrasscat_x_pixelrasscat_x_pixel_errorrasscat_y_pixelrasscat_y_pixel_errorrasscat_x_sky_pixelrasscat_y_sky_pixelrasscat_extraction_radiusrasscat_extraction_radius_fracrasscat_total_photonsrasscat_bkg_in_extr_regrasscat_vignetting_factorrasscat_remarksrasscat_band_a_tot_countsrasscat_band_b_tot_countsrasscat_band_c_tot_countsrasscat_band_d_tot_countsrasscat_band_a_bkg_countsrasscat_band_b_bkg_countsrasscat_band_c_bkg_countsrasscat_band_d_bkg_countsrasscat_remaining_bkg_arearasscat_band_a_countsrasscat_band_b_countsrasscat_band_c_countsrasscat_band_d_countsrasscat_xmmsl1_number_ctrprtsrasscat_xmmsl1_nearestrasscat_xmmsl1_separationrasscat_xmmsl1_namerasscat_xmmsl1_rarasscat_xmmsl1_decrasscat_xmmsl1_bb_count_raterasscat_xmmsl1_bb_count_rate_errrasscat_xmmsl1_sb_count_raterasscat_xmmsl1_sb_count_rate_errrasscat_threexmm_number_ctrprtsrasscat_threexmm_nearestrasscat_threexmm_separationrasscat_threexmm_namerasscat_threexmm_rarasscat_threexmm_decrasscat_threexmm_count_raterasscat_threexmm_count_rate_errrasscat_threexmm_fluxrasscat_threexmm_flux_errorrasscat_tworxp_number_ctrprtsrasscat_tworxp_nearestrasscat_tworxp_separationrasscat_tworxp_namerasscat_tworxp_rarasscat_tworxp_decrasscat_tworxp_count_raterasscat_tworxp_count_rate_errorrasscat_tworxp_exposurerasscat_tworxp_obsidrasscat_onerxs_number_ctrprtsrasscat_onerxs_nearestrasscat_onerxs_separationrasscat_onerxs_namerasscat_onerxs_rarasscat_onerxs_decrasscat_onerxs_count_raterasscat_onerxs_count_rate_errorrasscat_onerxs_countsrasscat_onerxs_counts_errorrasscat_onerxs_det_likelihoodrasscat_onerxs_exposurerasscat_onerxs_hr_1rasscat_onerxs_hr_1_errorrasscat_onerxs_hr_2rasscat_onerxs_hr_2_errorrasscat_veron_number_ctrprtsrasscat_veron_nearestrasscat_veron_separationrasscat_veron_namerasscat_veron_typerasscat_veron_vmagrasscat_veron_redshiftrasscat_veron_source_numberrasscat_veron_rarasscat_veron_decrasscat_tycho2_number_ctrprtsrasscat_tycho2_nearestrasscat_tycho2_separationrasscat_tycho2_rarasscat_tycho2_decrasscat_tycho2_source_numberrasscat_tycho2_vmagrasscat_tycho2_bmagrasscat_tycho2_tyc1_numberrasscat_tycho2_tyc2_numberrasscat_tycho2_tyc3_numberrasscat_bsc_number_ctrprtsrasscat_bsc_nearestrasscat_bsc_separationrasscat_bsc_rarasscat_bsc_decrasscat_bsc_vmagrasscat_bsc_spect_typerasscat_bsc_source_numberrasscat_hd_source_numberrasscat_hmxb_number_ctrprtsrasscat_hmxb_nearestrasscat_hmxb_separationrasscat_hmxb_namerasscat_hmxb_alt_namerasscat_hmxb_rarasscat_hmxb_decrasscat_hmxb_vmagrasscat_lmxb_number_ctrprtsrasscat_lmxb_nearestrasscat_lmxb_separationrasscat_lmxb_namerasscat_lmxb_alt_namerasscat_lmxb_rarasscat_lmxb_decrasscat_lmxb_vmagrasscat_atnf_number_ctrprtsrasscat_atnf_nearestrasscat_atnf_separationrasscat_atnf_namerasscat_atnf_rarasscat_atnf_decrasscat_atnf_pulsar_typerasscat_atnf_pulse_periodrasscat_fuhr_number_ctrprtsrasscat_fuhr_nearestrasscat_fuhr_separationrasscat_fuhr_namerasscat_fuhr_rarasscat_fuhr_decrasscat_fuhr_source_numberrasscat_onesxps_number_ctrprtsrasscat_onesxps_nearestrasscat_onesxps_separationrasscat_onesxps_rarasscat_onesxps_decrasscat_onesxps_exposurerasscat_onesxps_det_flagrasscat_onesxps_total_det_flagrasscat_onesxps_soft_det_flagrasscat_onesxps_medium_det_flagrasscat_onesxps_hard_det_flagrasscat_onesxps_source_numberrasscat_onesxps_count_raterasscat_onesxps_count_rate_errorrasscat_onerxh_number_ctrprtsrasscat_onerxh_nearestrasscat_onerxh_separationrasscat_onerxh_namerasscat_onerxh_rarasscat_onerxh_decrasscat_onerxh_count_raterasscat_onerxh_count_rate_errorrasscat_onerxh_exposurerasscat_onerxh_snrrasscat_flem_number_ctrprtsrasscat_flem_nearestrasscat_flem_separationrasscat_flem_namerasscat_flem_rarasscat_flem_decrasscat_flem_typerasscat_flem_wfc_detection_flagrasscat_flem_count_raterasscat_flem_count_rate_errorrasscat_wdcat_number_ctrprtsrasscat_wdcat_nearestrasscat_wdcat_separationrasscat_wdcat_namerasscat_wdcat_rarasscat_wdcat_decrasscat_wdcat_vmagrasscat_wdcat_vsphotrasscat_sdss_number_ctrprtsrasscat_sdss_nearestrasscat_sdss_separationrasscat_sdss_namerasscat_sdss_rarasscat_sdss_decrasscat_sdss_lambdarasscat_sdss_betarasscat_tworxs_number_ctrprtsrasscat_tworxs_nearest_src_numrasscat_tworxs_nearest_src_indexrasscat_tworxs_separationrasscat_tworxs_skyfield_numberrasscat_tworxs_skyfield_src_numrasscat_tworxs_det_likelihoodrasscat_tworxs_count_raterasscat_tworxs_rarasscat_tworxs_decrasscat_tworxs_subfield_det_cellrasscat_tworxs_nearby_flagrasscat_tworxs_selected_bkgrasscat_tworxs_x_pixel_sky_bkg1rasscat_tworxs_y_pixel_sky_bkg1rasscat_tworxs_x_pixel_sky_bkg2rasscat_tworxs_y_pixel_sky_bkg2rasscat_onerxs_bkg_count_raterasscat_tworxs_bkg_count_raterasscat_var_flagrasscat_count_rate_6srasscat_count_rate_6s_errorrasscat_excess_var_6srasscat_excess_var_6s_errorrasscat_number_pts_in_lcrasscat_number_pts_lessthan_6srasscat_number_pts_lessthan_1srasscat_number_pts_gtrthan_6srasscat_min_count_rate_6srasscat_max_count_rate_6srasscat_min_count_rate_6s_errorrasscat_max_count_rate_6s_errorrasscat_counts_notused_6rasscat_excess_var_lessthan_6rasscat_sum_count_rate_sigmarasscat_spect_plot_flagrasscat_lc_plot_flagrasscat_clock_timerasscat_clock_end_timerasscat_timerasscat_end_timerasscat___x_ra_decrasscat___y_ra_decrasscat___z_ra_deccarm__raj2000carm__dej2000carm_recnocarm_nocarm_karmncarm_namecarm_gl_gjcarm_jmagcarm_datecarm_nexpcarm_texpcarm_nexp2carm_texp2carm_pc1carm_tio2carm_tio5carm_vocarm_col_mcarm_cah2carm_cah3carm_zetacarm_pewacarm_pewa_errmicarm_pewa_errplcarm_sptlcarm_r_sptlcarm_l_sptbcarm_sptbcarm_l_sptccarm_sptccarm_spt2carm_spt5carm_sptpcarm_sptrcarm_sptcolorcarm_l_sptcarm_sptcarm_simbadcarm_id_name
ctctct / sct / ssdegdegdegdegdegdegpixpixct / sct / sctct / sct / sct / sct / ssigma1 / cm21 / cm21 / cm2ct / serg/s/cm^21 / cm21 / cm2ct / serg/s/cm^21 / cm21 / cm2ct / serg/s/cm^2pixpixpixpixpixpixpix1 / pixctctctctctctctctarcmin2ctctctctarcsecdegdegct / sct / sc / sc / sarcsecdegdegct / sct / serg/s/cm^2erg/s/cm^2arcsecdegdegct / sct / ssarcsecdegdegct / sct / ssarcsecmagdegdegarcsecdegdegmagmagarcsecdegdegmagarcsecdegdegmagarcsecdegdegmagarcsecdegdegsarcsecdegdegarcsecdegdegsct / sct / sarcsecdegdegct / sct / ssarcsecdegdegct / sct / sarcsecdegdegmagmagarcsecdegdegdegdegarcsecct / sdegdegpixpixpixpixct/s/arcmin^2ct/s/arcmin^2ct / sct / sct / sct / sct / sct / sssdddegdegmagss0.1 nm0.1 nm0.1 nm
int32int32objectint32int16float64float64float64float64float64float64float64float64float64float64float64float64float64float64float64float64float64float64float64int16int16int16int16float64float64float64float64float64float64float64float64float64float64float64float64float64float64float64float64float64float64float64float64float64float64float64int16int16float64float64float64float64float64float64float64float64float64float64int16int16float64float64float64float64float64float64float64float64float64float64int16int16float64float64float64float64float64float64int16float64int16float64float64objectfloat64float64float64float64float64float64float64float64float64float64float64float64float64int16int32float64objectfloat64float64float64float64float64float64int16int32float64objectfloat64float64float64float64float64float64int16int32float64objectfloat64float64float64float64int32objectint16int32float64objectfloat64float64float64float64float64float64int16int32float64float64float64float64int16int32float64objectobjectfloat64float64int32float64float64int16int32float64float64float64int32float64float64int16int32int16int16int16float64float64float64float64objectint16int32int16int16float64objectobjectfloat64float64float64int16int16float64objectobjectfloat64float64float64int16int16float64objectfloat64float64objectobjectint16int16float64objectfloat64float64objectint16int32float64float64float64int32int16int16int16int16int16int32float64float64int16int32float64objectfloat64float64float64float64int32float64int16int16float64objectfloat64float64objectobjectfloat64float64int16int32float64objectfloat64float64float64float64int16int16float64objectfloat64float64float64float64int16int32int32float64int32int16float64float64float64float64int16int16int16float64float64float64float64float64float64int16float64float64float64float64int16int16int16int16float64float64float64float64float64float64float64int16int16float64float64float64float64float64float64float64float64float64int32int16objectobjectobjectfloat32objectint32float32int32int16float32float32float32float32float32float32float32float32float32float32float32objectobjectobjectfloat32objectfloat32float32float32float32float32float32objectobjectobjectobject
924192412RXS J000742.3+602250930601119194.80100.2010.7091720.18770.0201533.721.9263460.38077117.54983-2.0331836.1638752.277690.3630.2872.37-0.4440.0790.0450.16951000.15920.182980.10279158.510.028170.060300.357230.109580.0550.003453410.1511960.0228415.79e+211.96e+198.443e+190.00032090.0001265-1.86000.60940.2033001.515e-120.93888.44901295.128e+217.815e+220.00094330.0018710.4975009.7170.0586000.0000007.38766.481291e+181.062e+200.0021260.0014580.1429000.024850.1756001.297e-121.9817.82129395.91790.112527372.24420.11365512547.1110416.486001.01340.21651.5438135.9931.6633.9965.6582.8335.1236.4871.60235.243108.3319.9321.8141.7411736212.3XMMSL1 J000742.8+6023021.9286660.38399----0.6870530.326382000.00.000000.00000--------000.00.000000.00000------195360.61RXS J000742.4+6022511.9266760.380830.1697000.01947089.092510.221750166525-0.410.1000000.00.200000000.0------0.000000.00000000.00.000000.00000------------000.00.000000.00000------000.00.000000.00000--000.00.000000.00000--000.00.000000.00000000.00.000000.00000000.00.000000.00000------------------000.00.000000.00000--------000.00.000000.00000----000.00.000000.00000----000.00.000000.00000----1170851708555.493060112056.540.07361.9156460.3663151114713.207334.8210334.6213051.470.0007960.00072104700.182980.102790.003453410.1511961717000.0281720.3572260.0602970.109584--0.0228410.2857671120123548.020331015.048276.91143548279.3126740.01661348762343590.4939543574317940.8693291004004921.927560.3817522J00077+603ABG 217-0328.912012-09-241600.0----1.1980.5320.3691.1111.4050.4080.6310.883-6.70.40.3M4.5VLep134.04.04.54.03.54.03.5M4.0VSimbadCARMENES-2
60653606532RXS J005017.9+08373593150314544.6130.676.3099670.07370.0152416.2412.574718.62657122.45008-54.2441114.921772.980450.0000.0000.00-0.0960.1310.3080.18451000.21060.053910.0948636.96-0.110310.056290.260800.104180.1560.09637971.483210.0649815.37e+201e+181.968e+200.00021140.0001519-1.24301.210.0848808.171e-132.29204.5830529.412e+215.329e+230.0020980.25520.49630023.030.0444000.0000006.86913.74521e+183.022e+200.00066650.00075350.2128000.083960.0673406.095e-134.4788.95552374.16920.169616466.00900.17199710589.7318855.316001.0550.14331.608753.1413.3624.4637.8252.389.5315.6025.13235.24335.6510.1819.2529.43000.00.000000.00000--------000.00.000000.00000--------000.00.000000.00000------1546371.71RXS J005017.9+08373412.574588.626110.0674000.01455027.6345.965500454100.10.2100000.480.230000000.0------0.000000.00000000.00.000000.00000------------000.00.000000.00000------000.00.000000.00000--000.00.000000.00000--000.00.000000.00000000.00.000000.00000000.00.000000.00000------------------000.00.000000.00000--------000.00.000000.00000----000.00.000000.00000----000.00.000000.00000----1103110103110221.99315031477.400.029912.630008.5980951212184.2615468.029416.1922115.870.0006320.00061187300.053910.094860.09637971.48321131300-0.1103120.2608040.0562940.104181--0.0649810.1487690118072840.018234244.048253.17638948255.0444910.2152494603471540.9649712511767490.1499938472826112.57304178.62613893535J00502+086RX J0050.2+08379.752012-01-101750.0----1.2940.5010.3431.1451.6090.4370.7011.019-6.70.30.24.54.54.54.54.54.54.0M4.5VSimbadCARMENES-35
43309433092RXS J005447.5+27310693120311522.4318.735.1214140.05560.0152336.7513.6980827.51853123.84373-35.3472823.6017419.899450.0000.0000.00-0.0790.1641.00.14561000.15390.068070.0993737.12-0.046240.053840.248300.086820.1150.6186620.8706440.7105795.51e+206.19e+193.875e+200.00021860.0002036-1.46901.8380.0792008.789e-130.78541.5710525.138e+216.653e+230.00064590.020720.50880081.450.0396300.0000004.1378.273521e+183.024e+200.00069840.0011040.1765000.074990.0634305.294e-131.9183.83652385.54920.215406304.05710.21017111613.934279.646001.0410.12521.690235.575.6125.8331.4535.9423.1110.8733.98235.24323.570.0022.2120.10000.00.000000.00000--------000.00.000000.00000--------000.00.000000.00000------1402031.11RXS J005447.6+27310613.6983327.518330.0501400.01427017.14794.880340253420.150.2800000.890.540000000.0------0.000000.00000000.00.000000.00000------------000.00.000000.00000------000.00.000000.00000--000.00.000000.00000--000.00.000000.00000000.00.000000.00000000.00.000000.00000------------------000.00.000000.00000--------000.00.000000.00000----000.00.000000.00000----000.00.000000.00000----00--0.0--------0.000000.00000----213058.77977.0510197.967584.380.0006750.00066101800.068070.099370.6186620.870644101000-0.0462420.2483030.0538400.086825--0.7105790.1674390118988886.019945417.048263.77877348274.8497340.2100137487234980.8616365022368950.46203545682130613.70012527.51766673737J00548+275G 069-03210.342012-09-242600.0----1.2940.5210.3531.1451.6820.4290.6910.983-5.30.25.3M4.6VShk094.54.54.54.04.54.54.5M4.5VSimbadCARMENES-37
72340723402RXS J015615.1+00060393170610557.0338.387.0908620.09110.0168421.4729.063240.10106155.34838-58.6305027.05322-11.046840.0000.0000.00-0.2330.1400.3350.21761000.11010.085780.0971344.04-0.321980.292900.223090.142020.2670.2263391.008360.2244622.66e+206.42e+193.958e+200.00014170.0001521-2.05802.2540.0837107.63e-132.42007.2590631e+183.101e+200.00014670.00029590.2066000.097140.0715400.0000002.6667.997631e+183.358e+200.00085010.0017310.1514000.06320.0717305.507e-132.5237.5763406.41970.154501248.42070.14993113492.27-727.646001.0780.23741.535179.8214.9821.8936.88126.0121.5018.6540.16235.24337.757.8015.6623.47000.00.000000.00000--------000.00.000000.00000--------000.00.000000.00000------1653260.51RXS J015615.2+00060329.063330.100970.0824200.01702031.64936.53568050384-0.260.1800000.410.29000024074224.1SDSS J01564+0007A18.8600.361407429.121250.12361000.00.000000.00000------------000.00.000000.00000------000.00.000000.00000--000.00.000000.00000--000.00.000000.00000000.00.000000.00000000.00.000000.00000------------------000.00.000000.00000--------000.00.000000.00000----000.00.000000.00000----14886.2SDSS J015614.92+000608.729.062200.1024327.052717-11.04519400--0.0--------0.000000.00000----114579.78-4526.8812030.152206.590.001010.0010012500.085780.097130.2263391.00836131300-0.3219770.2230850.2929040.142023--0.2244620.1829120119133454.019294803.048265.45201448267.3194790.4857739272886120.8740827080285170.0017638288274918629.06208330.10247226363J01562+001RX J0156.2+00069.492012-08-031700.0----1.120.6160.461.0631.1070.50.7130.917-5.20.20.33.03.03.53.03.03.02.5M3.0VSimbadCARMENES-63
43451434512RXS J015645.4+3033339312051523.7021.005.4327390.06680.0173314.4529.1892630.55922139.20690-30.2398638.0098717.423060.0000.0000.00-0.2580.2320.5210.40611000.15540.054840.1007433.64-0.105710.053200.243870.141030.1590.4679592.149230.2177335.02e+201e+182.855e+205.996e-050.0001787-2.19202.8650.0666503.594e-131.58001.5800411e+185.911e+220.00055121.8460.03043014.640.0454000.0000001.6591.659411e+183.921e+200.00084180.0020940.1255000.20280.0671704.485e-132.2092.20941169.83030.22459361.37810.225928-7800.77-17561.476001.0520.25531.512352.2114.5311.8726.40104.6736.2112.3748.58235.24317.262.447.7410.18000.00.000000.00000--------000.00.000000.00000--------000.00.000000.00000------1403255.01RXS J015645.8+30333229.1908330.558890.0436700.01534012.35864.34122013283-0.540.300000-0.721.390000000.0------0.000000.00000000.00.000000.00000------------000.00.000000.00000------000.00.000000.00000--000.00.000000.00000--000.00.000000.00000000.00.000000.00000000.00.000000.00000------------------000.00.000000.00000--------000.00.000000.00000----000.00.000000.00000----000.00.000000.00000----00--0.0--------0.000000.00000----1-6460.99-20726.74-9152.99-14047.960.001320.0014431900.054840.100740.4679592.149239900-0.1057130.2438730.0531950.141027--0.2177330.1555820120291215.020418014.048278.85202548280.3196060.4199570529294720.7517555278289070.50842865780114529.190458330.5586464J01567+305Koenigstuhl 4A10.322011-11-121900.0----1.2950.4570.3331.1591.8780.3970.630.925-16.00.40.4M5.0VAbe144.54.55.04.54.55.04.5M4.5VSimbadCARMENES-64
60827608272RXS J020012.4+13031793150614121.4052.157.6602790.17770.0261293.4230.0520413.05491147.65455-46.4893132.512650.755890.0000.0000.00-0.1880.1170.1070.18681000.18540.146300.1253667.19-0.198120.233120.386030.165600.1620.1902240.4780880.3978855.35e+201.594e+204.021e+200.00035550.0002188-2.23501.790.1595002.213e-120.96472.8940632.772e+211.964e+230.00061220.0061450.50530023.950.0753700.0000004.05712.17631e+182.003e+200.0016480.0018690.1720000.047510.1476001.216e-121.3394.01863325.50920.108388112.03300.1075876210.32-13002.536001.0690.11471.532262.1318.8521.9940.8547.5114.1813.4927.67235.24346.2714.1217.4931.61000.00.000000.00000--------000.00.000000.00000--------000.00.000000.00000------1547820.71RXS J020012.5+13031730.0520913.054720.1674000.02495049.3837.360250111295-0.270.1400000.010.250000248339177.7SDSS J02003+1304Q20.0701.8634833930.0970813.07750113243072.530.0313713.0554613238811.77711.96462913061000.00.000000.00000------000.00.000000.00000--000.00.000000.00000--000.00.000000.00000000.00.000000.00000000.00.000000.00000------------------000.00.000000.00000--------000.00.000000.00000----000.00.000000.00000----000.00.000000.00000----00--0.0--------0.000000.00000----17638.22-16655.615178.00-9888.500.0006840.00069480700.146300.125360.1902240.478088101000-0.1981200.3860270.2331230.165599--0.3978850.2716621119611528.020043698.048270.98527848275.9872450.487843069880160.8431993949091910.2258847484773530.053291713.05311116767J02002+130TZ Ari83.17.512011-11-111500.0----1.1540.6580.4861.1221.4980.5570.81.08-2.00.30.2M4.5VPMSU3.53.53.03.03.04.54.0M3.5:VSimbadCARMENES-67
49626496262RXS J023644.5+2240299313074277.6138.466.7588440.12850.0226299.2239.1855222.67499152.53628-34.0726743.899486.995390.2630.2481.40-0.4440.1310.2790.26891000.38680.099330.1714838.83-0.068070.039400.471570.113450.2611.232380.657221.8751449.13e+201e+181.467e+200.00015960.0001584-1.91601.2410.1246007.795e-130.74751.4950521e+181.24e+200.00072880.0092082.65000032.240.1032000.0000003.3286.657521e+182.492e+200.0012750.0022080.1176000.084080.1007006.343e-132.9865.97352195.61990.117221242.33910.119071-5479.70-1274.986001.0560.13431.464658.949.1014.1723.2857.5310.7313.1123.84235.24339.735.529.8015.32000.00.000000.00000--------000.00.000000.00000--------000.00.000000.00000------1455982.31RXS J023644.4+22402839.1850022.674580.1103000.02351027.24415.80697063247-0.10.2000000.430.260000000.0------0.000000.00000000.00.000000.00000------------000.00.000000.00000------000.00.000000.00000--000.00.000000.00000--000.00.000000.000001923.51RXS J023644.4+22402839.1852122.675920092000.00.000000.00000------------------000.00.000000.00000--------000.00.000000.00000----000.00.000000.00000----000.00.000000.00000----185959859591.19313074174.170.118839.1854422.67527901-4335.84-4723.37-6610.112108.020.0009310.00079792900.099330.171481.232380.65722101000-0.0680720.4715690.0393970.113455--1.8751440.2708140137068627.037281866.048472.91473448475.3827780.5829967877967980.7151936397461030.38550331138050839.18387522.67402787979J02367+226G 036-02610.082012-09-222600.0----1.4060.4280.281.1782.1580.3610.6270.964-5.50.40.5M5.0VReid045.05.05.05.05.05.05.0M5.0VSimbadCARMENES-79
66725667252RXS J032338.7+0541179316105613.3313.104.7136420.02960.0107442.0350.911405.68828176.93852-40.7059450.01197-12.490980.0000.0000.00-0.6350.567-1.00.55761000.17570.028330.0852230.18-0.108910.095540.344200.181860.2422.231635.894320.3786071.19e+218.46e+191.005e+211e-050.0001341-3.474010.980.0318303.248e-131.30501.3050411e+188.705e+271.191e-0612810.0100008.728e+050.0000000.0000003.7423.742413.478e+191.192e+210.00054750.0058460.0653900.62140.0293801.394e-131.1351.13541457.53410.195219251.00920.18838518092.57-494.686001.0460.26371.501052.3711.367.2518.61136.5111.3439.8551.18235.2436.807.570.001.52000.00.000000.00000--------000.00.000000.00000--------000.00.000000.00000------1599710.61RXS J032338.7+05411750.911255.688190.0198700.0091468.325533.83217410419-0.290.360000-0.81.510000000.0------0.000000.00000000.00.000000.00000------------000.00.000000.00000------000.00.000000.00000--000.00.000000.00000--000.00.000000.00000000.00.000000.00000000.00.000000.00000------------------000.00.000000.00000--------000.00.000000.00000----000.00.000000.00000----000.00.000000.00000----11122361122360.69316105712.930.028350.911535.6881960119073.26-3893.6817253.403072.540.00110.0010607400.028330.085222.231635.89432141400-0.1089120.3441990.0955360.181864--0.3786070.113558015044151.037718615.048102.26112248480.4377420.772349900814350.6274166146398280.099116206474830350.91316675.6875833103103J03236+0561RXS J032338.7+0541179.872012-01-101800.0----1.3540.4880.321.1641.7550.4170.6931.022-7.90.30.24.54.54.54.55.05.04.5M4.5VSimbadCARMENES-103
55615556152RXS J033733.8+17510593141047105.1758.328.2225570.16280.0230358.1154.3908617.85139169.48750-29.6289456.32951-1.522680.3950.3780.65-0.1390.107-0.1820.1619100-0.00020.149010.0843992.740.050010.092260.337000.194890.047-0.1823260.277148-0.6578641.3e+213.68e+191.763e+200.0003410.0001589-1.70101.0280.1658001.486e-121.09905.4970851e+184.876e+190.00035910.00029390.6436000.27560.1249000.0000001.6258.127851e+181.634e+200.0017730.001580.1671000.034410.1565001.271e-121.15.49885248.49610.156995178.39580.160021-720.85-7029.886001.0850.14211.570877.1126.5222.4048.9269.917.5317.2824.81235.24353.7624.0116.6340.64000.00.000000.00000--------000.00.000000.00000--------000.00.000000.00000------1505051.41RXS J033733.9+17510554.3912517.851530.1408000.02107053.64488.02767091381-0.090.140000-0.230.220000000.0------0.000000.00000000.00.000000.00000------------000.00.000000.00000------000.00.000000.00000--000.00.000000.00000--000.00.000000.00000000.00.000000.00000000.00.000000.00000------------------000.00.000000.00000--------000.00.000000.00000----000.00.000000.00000----000.00.000000.00000----29547695476235.7931410449.710.038154.3297217.88142911359.88-10641.25-1312.47-3638.110.0007190.00070542400.149010.08439-0.1823260.2771481212000.0500090.3369950.0922650.194890---0.6578640.233401115609140.05782204.048108.80034548110.8034000.7738654808595180.5542200138460670.30654917025908354.39112517.8501389118118J03375+178SABLP 413-0193240 B9.192012-09-241200.0----1.1450.6670.4591.1091.3090.5170.7771.029-6.80.50.6M3.0V+M4.3PMSU,Shk103.53.53.03.03.04.03.5M3.5VSimbadCARMENES-118
..................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................
76910769102RXS J213740.2+01371093175830133.3271.029.0732370.19610.0251362.19324.417741.6196656.57949-35.25927327.2754814.909620.2830.2670.990.0040.1080.0560.14881000.05660.177840.0815998.580.004570.134120.318490.123200.036-0.1121350.198376-0.5652664.82e+201e+181.271e+200.00041320.0001792-1.38700.81370.1862001.631e-121.775010.6500961e+183.355e+190.00059580.00034680.7379000.28360.1793000.0000001.4448.666961e+181.638e+200.0020060.0015980.1835000.03620.1861001.581e-121.9711.8296178.12530.106943126.95040.109402-7054.22-11659.966001.0960.21191.500777.4731.9231.5563.4784.5625.4915.9741.46235.24349.2323.4126.2249.632162197.0XMMSL1 J213740.0+013704324.417501.617710.9010860.3505940.7974740.245102000.00.000000.00000--------000.00.000000.00000------1698520.71RXS J213740.3+013711324.417911.619720.1807000.02447066.13628.956019127366-0.140.130000-0.060.200000000.0------0.000000.000001117568110.7324.428421.5908111752610.62111.4025438551000.00.000000.00000------000.00.000000.00000--000.00.000000.00000--000.00.000000.00000000.00.000000.00000000.00.000000.00000------------------000.00.000000.00000--------000.00.000000.00000----000.00.000000.00000----000.00.000000.00000----00--0.0--------0.000000.00000----1-6035.37-15045.55-8447.30-8261.250.0009440.0010402100.177840.08159-0.1121350.1983761212000.0000000.3184890.0000000.123197---0.5652660.2594301113885495.014052658.048204.59148348206.526240-0.5816387169973240.8129560340490570.0282646350221409324.41751.6205694694J21376+0162E 44988.82012-09-241300.0----1.3010.5020.3441.1461.5040.4340.6880.998-11.90.81.3M4.5VLep134.54.54.54.54.54.54.0M4.5VSimbadCARMENES-694
36156361562RXS J221124.4+4100009310493560.0148.438.1923250.08090.0137598.45332.8517241.0001893.14011-12.43462355.3191547.638380.0000.0000.000.1270.1030.0410.13471000.01380.114540.06611117.250.022580.068320.343920.239250.048-0.2837740.267307-1.0616051.8e+212.38e+203.923e+200.00032170.0001429-2.28001.3190.1154002.087e-120.99174.9590955.988e+183.943e+190.00039170.00023820.7175000.23830.1175000.0000001.0925.462951e+181.57e+200.0012030.00086580.1869000.041210.1128009.662e-130.86684.33495432.57760.171967123.66470.16966615846.48-11955.676001.01010.26221.607079.9142.2242.2484.4794.3336.9329.4566.38232.89948.1029.7732.3162.09000.00.000000.00000--------000.00.000000.00000--------000.00.000000.00000------1340121.51RXS J221124.3+410000332.8512341.000000.0686300.01317036.37396.980100535300.110.1800000.10.250000000.0------0.000000.00000280052897.0332.8387040.9751080037212.13012.90532039191000.00.000000.00000------000.00.000000.00000--000.00.000000.00000--000.00.000000.00000000.00.000000.00000000.00.000000.00000------------------000.00.000000.00000--------000.00.000000.00000----000.00.000000.00000----000.00.000000.00000----363252632523.09310493656.930.0757332.8506241.0001070117591.55-14794.5614009.32-8548.120.0007880.00077894800.114540.06611-0.2837740.2673071919000.0225790.3439190.0683220.239247---1.0616050.1806590116320206.016562197.048232.77100648235.571826-0.3443691759420890.6715603547863260.656061399977343332.850708340.9996389705705J22114+4091RXS J221124.3+4100009.732012-01-033600.0----1.4680.3990.2641.1922.4860.3750.6851.051-5.00.50.9M5.5VAbe145.55.55.55.55.55.05.5M5.5VSimbadCARMENES-705
42751427512RXS J222329.1+3227379311521072483.58712.9627.1484571.29100.0492552.24335.8715332.4603390.04138-20.79885352.3842239.022620.2940.16510.65--------4100--1.249290.51335------------0.1739060.03599964.830773--5.22e+193.9883e+190.00229660.00030691-1.91430.2343591.3055759.1245e-121.575383.493156531e+187.698e+180.00402310.000639920.8030250.0775581.1730640.0000002.5286134.0256531e+183.499e+190.0136140.00272820.1554220.00923031.2124429.1101e-122.0454108.415653338.81780.037436359.18440.0375297408.119241.106001.07850.23861.5028source nrby?--------------------------000.00.000000.00000--------000.00.000000.00000--------000.00.000000.00000------1397071.21RXS J222329.1+322738335.8712532.460561.2630000.049300697.17627.2136002360552-0.20.0300000.020.060000000.0------0.000000.0000026671673.2335.8712932.4594566702111.58913.311273813901000.00.000000.00000------000.00.000000.00000--000.00.000000.00000--000.00.000000.00000111603.9AC+31 68884335.8708332.461221160000.00.000000.00000------------------000.00.000000.00000--------000.00.000000.00000----000.00.000000.00000----000.00.000000.00000----274285742857.69311521061971.350.9934335.8723332.462334118902.356144.005611.5212548.340.000730.00076794201.249290.513350.1739060.03599961616000.0000002.8723650.0000000.371467--4.8307731.7626341116078084.016285516.048229.96866848232.369501-0.3449169124067340.7700446440628960.536715538889879335.871041732.4592778708708J22234+324ABWolf 1225856 AB6.92012-09-23180.0----1.130.6570.471.0621.1150.5160.7270.936-5.30.30.6M3.0VPMSU3.02.53.03.03.03.02.5M3.0VSimbadCARMENES-708
59935599352RXS J224343.6+191651931460775.7147.327.7366220.11490.0188411.66340.9319519.2809885.79559-34.22299350.3573025.189610.1650.2610.10-0.1590.120-0.6110.15881000.13340.122870.1013385.49-0.006670.077770.316710.112200.071-0.04877650.343989-0.1417975.31e+203.07e+191.766e+200.00016980.0001375-1.98801.2090.1147008.693e-131.87107.4840741e+181.23e+200.00025940.00020940.2252000.074520.1270000.0000000.43031.721741e+181.718e+200.0014940.0016030.1476000.041140.1248009.42e-131.0324.12974186.13800.12354463.91750.126560-6333.08-17332.926001.0810.18961.489469.5033.2413.7947.0371.7419.8022.0841.89235.24345.5526.636.4233.05000.00.000000.00000--------000.00.000000.00000--------000.00.000000.00000------000.00.000000.00000--------------------000.0------0.000000.00000000.00.000000.00000------------000.00.000000.00000------000.00.000000.00000--000.00.000000.00000--000.00.000000.00000000.00.000000.00000000.00.000000.00000------------------000.00.000000.00000--------000.00.000000.00000----000.00.000000.00000----000.00.000000.00000----00--0.0--------0.000000.00000----1-5046.33-20789.78-8073.38-14255.69--0.00081858700.122870.10133-0.04877650.343989141400-0.0066730.3167140.0777690.112201---0.1417970.2242030115916451.016095149.048228.09791648230.166179-0.3083670221595120.892119405573160.330201068811514340.932416719.2818056715715J22437+192RX J2243.7+19169.242012-01-101300.0----1.1110.6760.4831.0561.1130.5360.7580.984-2.50.40.5M3.0VReid073.02.53.03.02.53.02.5M3.0VSimbadCARMENES-715
24123241232RXS J225055.0+4959159308419238.0630.576.5861060.06450.0139473.61342.7294549.98776103.89254-8.3757511.9131351.135740.1660.2690.07-0.2350.164-0.5450.28921000.18490.096850.0997576.44-0.061210.070550.279490.085250.092-0.1803610.507736-0.3552271.92e+211e+181.76e+200.00015360.0001646-1.61001.3760.0852006.45e-131.32103.9620631.735e+222.483e+230.0067160.15840.49080026.540.0321600.0000005.32115.96631.809e+191.129e+210.0012770.031820.0477600.3770.0593202.111e-132.0326.0976368.39090.173793302.83830.164689-16930.324169.946001.0740.22921.556661.8630.0923.0853.1650.6825.1934.4459.63149.21935.1916.834.9521.78000.00.000000.00000--------000.00.000000.00000--------000.00.000000.00000------12338816.31RXS J225056.4+495906342.7350249.985000.0597300.01422025.4456.05772030426-0.270.2200000.420.380000000.0------0.000000.00000000.00.000000.00000------------000.00.000000.00000------000.00.000000.00000--000.00.000000.00000--000.00.000000.00000000.00.000000.0000011455274.0342.7307449.987041100000-1-11455270.0119000.004150000.00.000000.00000--------000.00.000000.00000----000.00.000000.00000----000.00.000000.00000----14271942719333.79308419411.100.0350342.7498349.89599201-14738.621650.20-19374.067159.840.0009120.0008604400.096850.09975-0.1803610.507736161600-0.0612130.2794940.0705480.085249---0.3552270.196606013733630.019777793.048087.09305648272.909641-0.1908819938192260.6139628379039410.765907108015414342.72937549.987718718J22509+4991RXS J225056.4+4959069.82012-01-031850.0----1.2430.4910.3431.1381.4030.4280.6830.99-8.90.60.84.04.04.54.54.04.53.5M4.0VSimbadCARMENES-718
30036300362RXS J230251.9+43381693094710746.8730.846.3304970.06320.0130488.38345.7164543.63789102.94627-14.980518.9682344.763210.0000.0000.00-0.7120.165-1.00.59051000.00280.082300.0597663.850.003500.077700.174610.090610.038-0.38620.407572-0.9475621.57e+218.35e+193.32e+205.474e-050.0001277-2.74802.8380.0639906.004e-132.02404.0490522.024e+205.867e+210.0011940.085440.0569802.6180.0537000.0000002.4844.968521e+182.494e+200.0008270.0010470.1230000.14670.0657504.314e-131.6613.32252432.06050.159895362.17730.15763515799.949510.466001.0590.16991.542452.4517.3611.2428.6156.2631.2737.4368.70235.24333.676.920.005.67000.00.000000.00000--------000.00.000000.00000--------000.00.000000.00000------1286402.01RXS J230251.9+433814345.7162543.637360.0498000.01193024.4025.84570037490-0.720.170000-0.480.560000000.0------0.000000.00000000.00.000000.00000------------000.00.000000.00000------000.00.000000.00000--000.00.000000.00000--000.00.000000.00000000.00.000000.00000000.00.000000.00000------------------000.00.000000.00000--------000.00.000000.00000----000.00.000000.00000----000.00.000000.00000----00--0.0--------0.000000.00000----217645.226106.9613961.9712294.050.0006470.0006185500.082300.05976-0.38620.4075721414000.0035000.1746070.0776990.090611---0.9475620.142067013479873.017536036.048084.15605348246.963380-0.1785556991253530.7013431473188420.690098291563425345.718791743.6376944724724J23028+436LSPM J2302+43389.322011-12-081400.0----1.2630.5210.3541.1331.5880.4040.6430.912-5.80.90.8M4.0VReid074.04.54.54.04.04.54.0M4.0VSimbadCARMENES-724
77321773212RXS J232057.7-01474093176310576.0640.977.0390070.11680.0201350.91350.24064-1.7946178.55281-56.65861350.326752.215730.0000.0000.00-0.3480.1440.8520.20451000.16560.142060.0947176.96-0.086050.086050.268530.102930.0750.04177720.288450.1448334.33e+201e+181.498e+200.00024880.0001792-1.55901.0690.1313001.027e-121.09903.2960631e+182.008e+200.0011950.029628.80700080.320.1269000.0000001.7295.187631e+182.244e+200.0013330.002040.1262000.040460.1065007.143e-132.8648.59263362.19740.103502400.05050.1030709512.2712919.046001.0700.18501.510566.667.7223.9231.6476.1616.5318.7835.32203.47237.261.3416.6718.01000.00.000000.00000--------000.00.000000.00000--------000.00.000000.00000------1702001.31RXS J232057.7-014739350.24042-1.794310.1070000.01988036.386.75920068340-0.260.1700000.850.470000000.0------0.000000.00000000.00.000000.00000------------000.00.000000.00000------000.00.000000.00000--000.00.000000.00000--000.00.000000.00000000.00.000000.00000000.00.000000.00000------------------000.00.000000.00000--------000.00.000000.00000----000.00.000000.00000----000.00.000000.00000----00--0.0--------0.000000.00000----211062.889789.258236.9816412.100.0009740.00093714600.142060.094710.04177720.28845121200-0.0860540.2685320.0860540.102929--0.1448330.2367720115921844.016083263.048228.16033448230.028610-0.16942736174110.985044989522444-0.0313167321120638350.2404167-1.7936944733733J23209-017ABLP 642-0489.362012-08-061400.0----1.2210.5480.3971.1061.30.4590.6850.941-10.20.71.0M4.0V+m4.0:Riaz06,Dae074.04.04.03.54.04.03.5M4.0VSimbadCARMENES-733
30111301112RXS J234155.1+4410469309488794.0261.788.6777520.17040.0239362.63355.4798944.17970109.99195-16.9383717.4959541.443820.4720.6411.560.2540.099-0.050.12691000.35460.149740.1616195.29-0.062350.062350.508770.154160.1560.4545480.4125491.1018031.06e+215.101e+206.288e+200.00054440.0002353-2.93001.2580.1575007.63e-120.38931.9470856.346e+192.319e+200.00035970.00035010.2369000.060280.1397000.0000001.0925.46856.483e+192.361e+200.0020820.0019670.1749000.037620.1544001.563e-120.6001385300.69030.169089321.91790.2152333976.635887.116001.0830.17691.622559.6944.9336.7881.7147.8822.9911.0534.04196.48440.5535.7432.3668.10000.00.000000.00000--------000.00.000000.00000--------000.00.000000.00000------1287241.91RXS J234155.0+441047355.4791644.179720.1772000.02381062.028.3335001003500.150.140000-0.160.180000000.0------0.000000.00000181287787.1355.5018844.1980581274010.66310.98732441241000.00.000000.00000------000.00.000000.00000--000.00.000000.00000--000.00.000000.00000112032.11RXS J234155.0+441047355.4797144.179141203000.00.000000.00000------------------000.00.000000.00000--------000.00.000000.00000----000.00.000000.00000----000.00.000000.00000----00--0.0--------0.000000.00000----15669.523072.221918.269218.030.0008220.00086701200.149740.161610.4545480.412549141400-0.0623500.5087740.0623500.154157--1.1018030.3113561118268234.018487146.048255.43789448257.971597-0.05651846704469720.7149270189405790.6969110563563355.47912544.178744744J23419+441HH And9056.882011-11-111400.0----1.3860.4670.2931.172.3720.4090.711.072-1.00.30.4M5.0VPMSU5.05.05.05.05.05.05.0M5.0VSimbadCARMENES-744
88096880962RXS J235555.0-13212693196413149.9832.406.2503310.10330.0199313.62358.97921-13.3572576.89265-71.07952353.67667-11.833220.3940.3381.90-0.5430.1460.4440.34431000.14230.055500.1039412.52-0.324440.225120.142520.099560.7312.099342.449960.8568872.5e+201.418e+206.74e+202.915e-050.0001461-3.54504.6760.0657101.067e-120.93581.8720529.368e+221.323e+270.2568245700.505300845800.0038860.0000003.6057.21522.835e+194.486e+200.0010460.00077850.0916200.34940.0661503.967e-130.83451.66952117.07320.178337425.47320.176610-12548.9115207.096001.0540.15841.506856.036.359.7116.0672.5011.198.7319.92235.24331.822.616.809.41000.00.000000.00000--------000.00.000000.00000--------000.00.000000.00000------1800524.21RXS J235555.3-132126358.98041-13.357220.0838500.01888025.65815.77728034306-0.690.1600000.591.420000000.0------0.000000.00000000.00.000000.00000------------000.00.000000.00000------000.00.000000.00000--000.00.000000.00000--000.00.000000.00000000.00.000000.00000000.00.000000.00000------------------000.00.000000.00000--------000.00.000000.00000----000.00.000000.00000----000.00.000000.00000----00--0.0--------0.000000.00000----1-11171.5011626.94-14058.4518223.660.0009410.00089780700.055500.103942.099342.44996101000-0.3244370.1425150.2251200.099557--0.8568870.1594360116215550.016376885.048231.55970948233.427013-0.0173332763528350.972794110896969-0.231022023484886358.98-13.3566111749749J23559-133NLTT 584419.262012-09-021250.0----1.1840.5680.4051.0941.3310.4720.70.959-4.20.40.5M3.0VSch053.53.54.03.53.54.03.5M3.5VSimbadCARMENES-749

It is easy to determine the number (and percentage) of CARMENES sources that had a match in the 2RXS catalog - we have shrunk the original catalog significantly, but we still have a lot of sources to work with:

num_match = len(carm_2rxs_match)
perc_match = round((num_match / len(carm_cat)) * 100, 1)

print("Number of CARMENES sources matched:", num_match)
print("Percentage of CARMENES sources matched:", f"{perc_match}%")
Number of CARMENES sources matched: 83
Percentage of CARMENES sources matched: 11.0%

Finally, it might also be helpful to see a list of all the column names. Note that the HEASARC TAP service has prepended the column names with the name of the table (or at least the alias we defined in the query) they originated from:

carm_2rxs_match.colnames
['rasscat___row',
 'rasscat_entry_number',
 'rasscat_name',
 'rasscat_skyfield_number',
 'rasscat_skyfield_source_number',
 'rasscat_detection_likelihood',
 'rasscat_counts',
 'rasscat_counts_error',
 'rasscat_count_rate',
 'rasscat_count_rate_error',
 'rasscat_exposure',
 'rasscat_ra',
 'rasscat_dec',
 'rasscat_lii',
 'rasscat_bii',
 'rasscat_lambda',
 'rasscat_beta',
 'rasscat_source_extent',
 'rasscat_source_extent_error',
 'rasscat_source_extent_prob',
 'rasscat_hardness_ratio_1',
 'rasscat_hardness_ratio_1_error',
 'rasscat_hardness_ratio_2',
 'rasscat_hardness_ratio_2_error',
 'rasscat_unique_flag',
 'rasscat_extended_region_flag',
 'rasscat_nearby_src_det_flag',
 'rasscat_source_quality_flag',
 'rasscat_max_amplitude',
 'rasscat_mean_count_rate',
 'rasscat_mean_count_rate_error',
 'rasscat_lc_counts',
 'rasscat_min_count_rate',
 'rasscat_min_count_rate_error',
 'rasscat_max_count_rate',
 'rasscat_max_count_rate_error',
 'rasscat_lc_chi2',
 'rasscat_excess_variance',
 'rasscat_excess_variance_error',
 'rasscat_excess_variance_sigma',
 'rasscat_nh_gal',
 'rasscat_plaw_nh',
 'rasscat_plaw_nh_error',
 'rasscat_plaw_norm',
 'rasscat_plaw_norm_error',
 'rasscat_plaw_photon_index',
 'rasscat_plaw_photon_index_error',
 'rasscat_plaw_count_rate',
 'rasscat_plaw_flux',
 'rasscat_plaw_chi2_reduced',
 'rasscat_plaw_chi2',
 'rasscat_plaw_number_data_pts',
 'rasscat_plaw_dof',
 'rasscat_mekal_nh',
 'rasscat_mekal_nh_error',
 'rasscat_mekal_norm',
 'rasscat_mekal_norm_error',
 'rasscat_mekal_temperature',
 'rasscat_mekal_temperature_error',
 'rasscat_mekal_count_rate',
 'rasscat_mekal_flux',
 'rasscat_mekal_chi2_reduced',
 'rasscat_mekal_chi2',
 'rasscat_mekal_number_data_pts',
 'rasscat_mekal_dof',
 'rasscat_bb_nh',
 'rasscat_bb_nh_error',
 'rasscat_bb_norm',
 'rasscat_bb_norm_error',
 'rasscat_bb_temperature',
 'rasscat_bb_temperature_error',
 'rasscat_bb_count_rate',
 'rasscat_bb_flux',
 'rasscat_bb_chi2_reduced',
 'rasscat_bb_chi2',
 'rasscat_bb_number_data_pts',
 'rasscat_bb_dof',
 'rasscat_x_pixel',
 'rasscat_x_pixel_error',
 'rasscat_y_pixel',
 'rasscat_y_pixel_error',
 'rasscat_x_sky_pixel',
 'rasscat_y_sky_pixel',
 'rasscat_extraction_radius',
 'rasscat_extraction_radius_frac',
 'rasscat_total_photons',
 'rasscat_bkg_in_extr_reg',
 'rasscat_vignetting_factor',
 'rasscat_remarks',
 'rasscat_band_a_tot_counts',
 'rasscat_band_b_tot_counts',
 'rasscat_band_c_tot_counts',
 'rasscat_band_d_tot_counts',
 'rasscat_band_a_bkg_counts',
 'rasscat_band_b_bkg_counts',
 'rasscat_band_c_bkg_counts',
 'rasscat_band_d_bkg_counts',
 'rasscat_remaining_bkg_area',
 'rasscat_band_a_counts',
 'rasscat_band_b_counts',
 'rasscat_band_c_counts',
 'rasscat_band_d_counts',
 'rasscat_xmmsl1_number_ctrprts',
 'rasscat_xmmsl1_nearest',
 'rasscat_xmmsl1_separation',
 'rasscat_xmmsl1_name',
 'rasscat_xmmsl1_ra',
 'rasscat_xmmsl1_dec',
 'rasscat_xmmsl1_bb_count_rate',
 'rasscat_xmmsl1_bb_count_rate_err',
 'rasscat_xmmsl1_sb_count_rate',
 'rasscat_xmmsl1_sb_count_rate_err',
 'rasscat_threexmm_number_ctrprts',
 'rasscat_threexmm_nearest',
 'rasscat_threexmm_separation',
 'rasscat_threexmm_name',
 'rasscat_threexmm_ra',
 'rasscat_threexmm_dec',
 'rasscat_threexmm_count_rate',
 'rasscat_threexmm_count_rate_err',
 'rasscat_threexmm_flux',
 'rasscat_threexmm_flux_error',
 'rasscat_tworxp_number_ctrprts',
 'rasscat_tworxp_nearest',
 'rasscat_tworxp_separation',
 'rasscat_tworxp_name',
 'rasscat_tworxp_ra',
 'rasscat_tworxp_dec',
 'rasscat_tworxp_count_rate',
 'rasscat_tworxp_count_rate_error',
 'rasscat_tworxp_exposure',
 'rasscat_tworxp_obsid',
 'rasscat_onerxs_number_ctrprts',
 'rasscat_onerxs_nearest',
 'rasscat_onerxs_separation',
 'rasscat_onerxs_name',
 'rasscat_onerxs_ra',
 'rasscat_onerxs_dec',
 'rasscat_onerxs_count_rate',
 'rasscat_onerxs_count_rate_error',
 'rasscat_onerxs_counts',
 'rasscat_onerxs_counts_error',
 'rasscat_onerxs_det_likelihood',
 'rasscat_onerxs_exposure',
 'rasscat_onerxs_hr_1',
 'rasscat_onerxs_hr_1_error',
 'rasscat_onerxs_hr_2',
 'rasscat_onerxs_hr_2_error',
 'rasscat_veron_number_ctrprts',
 'rasscat_veron_nearest',
 'rasscat_veron_separation',
 'rasscat_veron_name',
 'rasscat_veron_type',
 'rasscat_veron_vmag',
 'rasscat_veron_redshift',
 'rasscat_veron_source_number',
 'rasscat_veron_ra',
 'rasscat_veron_dec',
 'rasscat_tycho2_number_ctrprts',
 'rasscat_tycho2_nearest',
 'rasscat_tycho2_separation',
 'rasscat_tycho2_ra',
 'rasscat_tycho2_dec',
 'rasscat_tycho2_source_number',
 'rasscat_tycho2_vmag',
 'rasscat_tycho2_bmag',
 'rasscat_tycho2_tyc1_number',
 'rasscat_tycho2_tyc2_number',
 'rasscat_tycho2_tyc3_number',
 'rasscat_bsc_number_ctrprts',
 'rasscat_bsc_nearest',
 'rasscat_bsc_separation',
 'rasscat_bsc_ra',
 'rasscat_bsc_dec',
 'rasscat_bsc_vmag',
 'rasscat_bsc_spect_type',
 'rasscat_bsc_source_number',
 'rasscat_hd_source_number',
 'rasscat_hmxb_number_ctrprts',
 'rasscat_hmxb_nearest',
 'rasscat_hmxb_separation',
 'rasscat_hmxb_name',
 'rasscat_hmxb_alt_name',
 'rasscat_hmxb_ra',
 'rasscat_hmxb_dec',
 'rasscat_hmxb_vmag',
 'rasscat_lmxb_number_ctrprts',
 'rasscat_lmxb_nearest',
 'rasscat_lmxb_separation',
 'rasscat_lmxb_name',
 'rasscat_lmxb_alt_name',
 'rasscat_lmxb_ra',
 'rasscat_lmxb_dec',
 'rasscat_lmxb_vmag',
 'rasscat_atnf_number_ctrprts',
 'rasscat_atnf_nearest',
 'rasscat_atnf_separation',
 'rasscat_atnf_name',
 'rasscat_atnf_ra',
 'rasscat_atnf_dec',
 'rasscat_atnf_pulsar_type',
 'rasscat_atnf_pulse_period',
 'rasscat_fuhr_number_ctrprts',
 'rasscat_fuhr_nearest',
 'rasscat_fuhr_separation',
 'rasscat_fuhr_name',
 'rasscat_fuhr_ra',
 'rasscat_fuhr_dec',
 'rasscat_fuhr_source_number',
 'rasscat_onesxps_number_ctrprts',
 'rasscat_onesxps_nearest',
 'rasscat_onesxps_separation',
 'rasscat_onesxps_ra',
 'rasscat_onesxps_dec',
 'rasscat_onesxps_exposure',
 'rasscat_onesxps_det_flag',
 'rasscat_onesxps_total_det_flag',
 'rasscat_onesxps_soft_det_flag',
 'rasscat_onesxps_medium_det_flag',
 'rasscat_onesxps_hard_det_flag',
 'rasscat_onesxps_source_number',
 'rasscat_onesxps_count_rate',
 'rasscat_onesxps_count_rate_error',
 'rasscat_onerxh_number_ctrprts',
 'rasscat_onerxh_nearest',
 'rasscat_onerxh_separation',
 'rasscat_onerxh_name',
 'rasscat_onerxh_ra',
 'rasscat_onerxh_dec',
 'rasscat_onerxh_count_rate',
 'rasscat_onerxh_count_rate_error',
 'rasscat_onerxh_exposure',
 'rasscat_onerxh_snr',
 'rasscat_flem_number_ctrprts',
 'rasscat_flem_nearest',
 'rasscat_flem_separation',
 'rasscat_flem_name',
 'rasscat_flem_ra',
 'rasscat_flem_dec',
 'rasscat_flem_type',
 'rasscat_flem_wfc_detection_flag',
 'rasscat_flem_count_rate',
 'rasscat_flem_count_rate_error',
 'rasscat_wdcat_number_ctrprts',
 'rasscat_wdcat_nearest',
 'rasscat_wdcat_separation',
 'rasscat_wdcat_name',
 'rasscat_wdcat_ra',
 'rasscat_wdcat_dec',
 'rasscat_wdcat_vmag',
 'rasscat_wdcat_vsphot',
 'rasscat_sdss_number_ctrprts',
 'rasscat_sdss_nearest',
 'rasscat_sdss_separation',
 'rasscat_sdss_name',
 'rasscat_sdss_ra',
 'rasscat_sdss_dec',
 'rasscat_sdss_lambda',
 'rasscat_sdss_beta',
 'rasscat_tworxs_number_ctrprts',
 'rasscat_tworxs_nearest_src_num',
 'rasscat_tworxs_nearest_src_index',
 'rasscat_tworxs_separation',
 'rasscat_tworxs_skyfield_number',
 'rasscat_tworxs_skyfield_src_num',
 'rasscat_tworxs_det_likelihood',
 'rasscat_tworxs_count_rate',
 'rasscat_tworxs_ra',
 'rasscat_tworxs_dec',
 'rasscat_tworxs_subfield_det_cell',
 'rasscat_tworxs_nearby_flag',
 'rasscat_tworxs_selected_bkg',
 'rasscat_tworxs_x_pixel_sky_bkg1',
 'rasscat_tworxs_y_pixel_sky_bkg1',
 'rasscat_tworxs_x_pixel_sky_bkg2',
 'rasscat_tworxs_y_pixel_sky_bkg2',
 'rasscat_onerxs_bkg_count_rate',
 'rasscat_tworxs_bkg_count_rate',
 'rasscat_var_flag',
 'rasscat_count_rate_6s',
 'rasscat_count_rate_6s_error',
 'rasscat_excess_var_6s',
 'rasscat_excess_var_6s_error',
 'rasscat_number_pts_in_lc',
 'rasscat_number_pts_lessthan_6s',
 'rasscat_number_pts_lessthan_1s',
 'rasscat_number_pts_gtrthan_6s',
 'rasscat_min_count_rate_6s',
 'rasscat_max_count_rate_6s',
 'rasscat_min_count_rate_6s_error',
 'rasscat_max_count_rate_6s_error',
 'rasscat_counts_notused_6',
 'rasscat_excess_var_lessthan_6',
 'rasscat_sum_count_rate_sigma',
 'rasscat_spect_plot_flag',
 'rasscat_lc_plot_flag',
 'rasscat_clock_time',
 'rasscat_clock_end_time',
 'rasscat_time',
 'rasscat_end_time',
 'rasscat___x_ra_dec',
 'rasscat___y_ra_dec',
 'rasscat___z_ra_dec',
 'carm__raj2000',
 'carm__dej2000',
 'carm_recno',
 'carm_no',
 'carm_karmn',
 'carm_name',
 'carm_gl_gj',
 'carm_jmag',
 'carm_date',
 'carm_nexp',
 'carm_texp',
 'carm_nexp2',
 'carm_texp2',
 'carm_pc1',
 'carm_tio2',
 'carm_tio5',
 'carm_vo',
 'carm_col_m',
 'carm_cah2',
 'carm_cah3',
 'carm_zeta',
 'carm_pewa',
 'carm_pewa_errmi',
 'carm_pewa_errpl',
 'carm_sptl',
 'carm_r_sptl',
 'carm_l_sptb',
 'carm_sptb',
 'carm_l_sptc',
 'carm_sptc',
 'carm_spt2',
 'carm_spt5',
 'carm_sptp',
 'carm_sptr',
 'carm_sptcolor',
 'carm_l_spt',
 'carm_spt',
 'carm_simbad',
 'carm_id_name']

Extracting CARMENES coordinates for the matched sources#

In preparation for the rest of this notebook, we extract the CARMENES M dwarf RA-Dec coordinates for the matched sources and place them in an Astropy SkyCoord object:

matched_carm_coords = SkyCoord(
    carm_2rxs_match["carm__raj2000"].value,
    carm_2rxs_match["carm__dej2000"].value,
    unit="deg",
)

matched_carm_coords[:6]
<SkyCoord (ICRS): (ra, dec) in deg
    [( 1.9275   , 60.38175  ), (12.5730417,  8.6261389),
     (13.700125 , 27.5176667), (29.0620833,  0.1024722),
     (29.1904583, 30.558    ), (30.0532917, 13.0531111)]>

Map CARMENES ID names to accepted names of the M dwarfs#

Once again in preparation for the rest of this demonstration, we define a dictionary to make it easy to map between the ‘CARMENES-{ID}’ names we created earlier, and the recognized names of the CARMENES stars:

id_name_to_actual = {en["carm_id_name"]: en["carm_name"] for en in carm_2rxs_match}
id_name_to_actual
{'CARMENES-2': 'G 217-032',
 'CARMENES-35': 'RX J0050.2+0837',
 'CARMENES-37': 'G 069-032',
 'CARMENES-63': 'RX J0156.2+0006',
 'CARMENES-64': 'Koenigstuhl 4A',
 'CARMENES-67': 'TZ Ari',
 'CARMENES-79': 'G 036-026',
 'CARMENES-103': '1RXS J032338.7+054117',
 'CARMENES-118': 'LP 413-019',
 'CARMENES-139': 'LSPM J0417+4103',
 'CARMENES-151': 'IN Tau',
 'CARMENES-155': 'V1103 Tau',
 'CARMENES-157': '1RXS J043100.0+364800',
 'CARMENES-161': 'LP 415-1582',
 'CARMENES-168': 'NLTT 13733',
 'CARMENES-169': 'LP 415-345',
 'CARMENES-173': 'RX J0447.2+2038',
 'CARMENES-174': 'G 081-034',
 'CARMENES-183': '1RXS J050156.7+010845',
 'CARMENES-196': 'HD 34751 B',
 'CARMENES-216': '1RXS J054232.1+152459',
 'CARMENES-224': '1RXS J055009.0+051154',
 'CARMENES-226': '1RXS J055641.0-101837',
 'CARMENES-233': 'TYC 1313-1482-1',
 'CARMENES-234': 'LP 086-173',
 'CARMENES-266': 'HD 50281 A',
 'CARMENES-284': 'BL Lyn',
 'CARMENES-301': 'LP 005-088',
 'CARMENES-307': 'BD+21 1764B',
 'CARMENES-342': '1RXS J092010.8+034731',
 'CARMENES-355': 'G 161-071',
 'CARMENES-358': 'TYC 4902-210-1',
 'CARMENES-359': 'NLTT 23096',
 'CARMENES-363': 'G 195-055',
 'CARMENES-371': 'AD Leo',
 'CARMENES-377': 'RX J1035.9+2853',
 'CARMENES-378': 'LP 127-502',
 'CARMENES-387': 'BD-10 3166B',
 'CARMENES-391': 'HH Leo BC',
 'CARMENES-396': 'HD 97584 A',
 'CARMENES-399': 'SZ Crt A',
 'CARMENES-411': '1RXS J114728.8+664405',
 'CARMENES-412': 'G 010-052',
 'CARMENES-413': 'BD+36 2219',
 'CARMENES-417': 'HD 104923 B',
 'CARMENES-424': 'G 013-033',
 'CARMENES-430': 'RX J1241.7+5645',
 'CARMENES-456': 'LP 323-169',
 'CARMENES-491': 'CE Boo',
 'CARMENES-517': 'G 256-025',
 'CARMENES-524': 'LSPM J1604+2331',
 'CARMENES-526': '1RXS J161204.8+031850',
 'CARMENES-537': 'LSPM J1631+4710',
 'CARMENES-559': 'V639 Her',
 'CARMENES-561': 'V475 Her',
 'CARMENES-563': 'LSPM J1723+1338',
 'CARMENES-564': 'LSPM J1724+6147',
 'CARMENES-578': 'RX J1752.0+5636',
 'CARMENES-585': 'LP 071-082',
 'CARMENES-594': '1RXS J181115.2-010111',
 'CARMENES-603': 'RX J1831.3+6454',
 'CARMENES-605': 'BD+45 2743',
 'CARMENES-613': '1RXS J184646.9+004320',
 'CARMENES-618': '1RXS J185504.7+425952',
 'CARMENES-630': 'G 185-023',
 'CARMENES-632': 'PM I19282-0009',
 'CARMENES-633': 'G 125-015',
 'CARMENES-641': '1RXS J194354.7-054634',
 'CARMENES-645': 'V1581 Cyg',
 'CARMENES-646': 'G 208-045',
 'CARMENES-669': '1RXS J203813.6+230750',
 'CARMENES-675': '1RXS J205405.4+601811',
 'CARMENES-678': 'LSPM J2059+5303',
 'CARMENES-681': 'G 211-009',
 'CARMENES-694': '2E 4498',
 'CARMENES-705': '1RXS J221124.3+410000',
 'CARMENES-708': 'Wolf 1225',
 'CARMENES-715': 'RX J2243.7+1916',
 'CARMENES-718': '1RXS J225056.4+495906',
 'CARMENES-724': 'LSPM J2302+4338',
 'CARMENES-733': 'LP 642-048',
 'CARMENES-744': 'HH And',
 'CARMENES-749': 'NLTT 58441'}

2. Downloading relevant ROSAT All-Sky Survey data#

At this point we’ve defined a subset of the original CARMENES M dwarf catalog whose entries all have a match in the 2RXS catalog. We now need to download the RASS data that is relevant to those sources.

Getting relevant RASS sequence IDs#

An added bonus we get from matching the CARMENES M dwarfs to the 2RXS catalog is that the resulting match table contains the RASS ‘skyfield number’ which uniquely identifies the ROSAT All-Sky Survey region that contains the source.

We need to retrieve the RASS data for each skyfield represented in the match table.

Extracting the skyfield numbers from the match table allows us to build a list of RASS ‘sequence IDs’ which can be used to fetch the correct data from the HEASARC:

uniq_seq_ids = np.unique(carm_2rxs_match["rasscat_skyfield_number"].value.data).astype(
    str
)
uniq_seq_ids = "RS" + uniq_seq_ids + "N00"
uniq_seq_ids
array(['RS930204N00', 'RS930311N00', 'RS930411N00', 'RS930514N00',
       'RS930522N00', 'RS930601N00', 'RS930609N00', 'RS930624N00',
       'RS930625N00', 'RS930629N00', 'RS930721N00', 'RS930730N00',
       'RS930809N00', 'RS930819N00', 'RS930820N00', 'RS930838N00',
       'RS930841N00', 'RS930934N00', 'RS930938N00', 'RS930940N00',
       'RS930947N00', 'RS930948N00', 'RS931010N00', 'RS931049N00',
       'RS931111N00', 'RS931118N00', 'RS931128N00', 'RS931132N00',
       'RS931145N00', 'RS931149N00', 'RS931152N00', 'RS931203N00',
       'RS931205N00', 'RS931226N00', 'RS931242N00', 'RS931307N00',
       'RS931312N00', 'RS931313N00', 'RS931321N00', 'RS931327N00',
       'RS931341N00', 'RS931345N00', 'RS931350N00', 'RS931353N00',
       'RS931410N00', 'RS931412N00', 'RS931413N00', 'RS931415N00',
       'RS931416N00', 'RS931432N00', 'RS931440N00', 'RS931460N00',
       'RS931503N00', 'RS931506N00', 'RS931547N00', 'RS931610N00',
       'RS931616N00', 'RS931625N00', 'RS931627N00', 'RS931632N00',
       'RS931644N00', 'RS931706N00', 'RS931714N00', 'RS931749N00',
       'RS931751N00', 'RS931752N00', 'RS931758N00', 'RS931763N00',
       'RS931819N00', 'RS931827N00', 'RS931830N00', 'RS931834N00',
       'RS931853N00', 'RS931916N00', 'RS931926N00', 'RS931930N00',
       'RS931964N00', 'RS932114N00', 'RS932129N00'], dtype='<U16')

For convenience, we also define a dictionary that maps the ‘CARMENES-{ID}’ name we gave each CARMENES source to the RASS sequence ID relevant to that source:

src_seq_ids = {
    en["carm_id_name"]: "RS" + str(en["rasscat_skyfield_number"]) + "N00"
    for en in carm_2rxs_match
}

Identifying the ROSAT All-Sky Survey ‘master’ table#

We’re going to use Astroquery’s Heasarc object to fetch the name of the ‘master’, or observation summary, table for the ROSAT All-Sky Survey. This table has one entry per RASS sequence ID, and in the next subsection we’ll indirectly use it to retrieve links to the data files we need.

To find the right table, we pass master=True, to indicate we are only interested in retrieving mission master tables, and a string of space-separated keywords (both of which must be matched for a table to be returned):

rass_obs_tab_name = Heasarc.list_catalogs(keywords="RASS ROSAT", master=True)[0]["name"]
rass_obs_tab_name
np.str_('rassmaster')

Note

While most missions archived by HEASARC have only one ‘master’ table associated with them, ROSAT has two; ‘rassmaster’, which we’re using in this demonstration, and ‘rosmaster’, which contains information on the observations taken during the pointed phase of ROSAT’s mission.

Downloading the relevant RASS observation data#

To download the data files, we can simply pass the data links table to the Heasarc.download_data(...) method, with additional arguments specifying that we want to download the data from the HEASARC AWS S3 bucket (rather than the HEASARC FTP server), and that we want to store the downloaded data in the ROOT_DATA_DIR directory specified in the Global Setup: Constants section:

Heasarc.download_data(rass_data_links, "aws", ROOT_DATA_DIR)

Hide code cell output

INFO: Downloading data AWS S3 ... [astroquery.heasarc.core]
INFO: Enabling anonymous cloud data access ... [astroquery.heasarc.core]
INFO: downloading s3://nasa-heasarc/rosat/data/pspc/processed_data/900000/rs932114n00/ [astroquery.heasarc.core]
INFO: downloading s3://nasa-heasarc/rosat/data/pspc/processed_data/900000/rs932129n00/ [astroquery.heasarc.core]
INFO: downloading s3://nasa-heasarc/rosat/data/pspc/processed_data/900000/rs931916n00/ [astroquery.heasarc.core]
INFO: downloading s3://nasa-heasarc/rosat/data/pspc/processed_data/900000/rs931926n00/ [astroquery.heasarc.core]
INFO: downloading s3://nasa-heasarc/rosat/data/pspc/processed_data/900000/rs931930n00/ [astroquery.heasarc.core]
INFO: downloading s3://nasa-heasarc/rosat/data/pspc/processed_data/900000/rs931964n00/ [astroquery.heasarc.core]
INFO: downloading s3://nasa-heasarc/rosat/data/pspc/processed_data/900000/rs931819n00/ [astroquery.heasarc.core]
INFO: downloading s3://nasa-heasarc/rosat/data/pspc/processed_data/900000/rs931827n00/ [astroquery.heasarc.core]
INFO: downloading s3://nasa-heasarc/rosat/data/pspc/processed_data/900000/rs931830n00/ [astroquery.heasarc.core]
INFO: downloading s3://nasa-heasarc/rosat/data/pspc/processed_data/900000/rs931834n00/ [astroquery.heasarc.core]
INFO: downloading s3://nasa-heasarc/rosat/data/pspc/processed_data/900000/rs931853n00/ [astroquery.heasarc.core]
INFO: downloading s3://nasa-heasarc/rosat/data/pspc/processed_data/900000/rs931706n00/ [astroquery.heasarc.core]
INFO: downloading s3://nasa-heasarc/rosat/data/pspc/processed_data/900000/rs931714n00/ [astroquery.heasarc.core]
INFO: downloading s3://nasa-heasarc/rosat/data/pspc/processed_data/900000/rs931749n00/ [astroquery.heasarc.core]
INFO: downloading s3://nasa-heasarc/rosat/data/pspc/processed_data/900000/rs931751n00/ [astroquery.heasarc.core]
INFO: downloading s3://nasa-heasarc/rosat/data/pspc/processed_data/900000/rs931752n00/ [astroquery.heasarc.core]
INFO: downloading s3://nasa-heasarc/rosat/data/pspc/processed_data/900000/rs931758n00/ [astroquery.heasarc.core]
INFO: downloading s3://nasa-heasarc/rosat/data/pspc/processed_data/900000/rs931763n00/ [astroquery.heasarc.core]
INFO: downloading s3://nasa-heasarc/rosat/data/pspc/processed_data/900000/rs931610n00/ [astroquery.heasarc.core]
INFO: downloading s3://nasa-heasarc/rosat/data/pspc/processed_data/900000/rs931616n00/ [astroquery.heasarc.core]
INFO: downloading s3://nasa-heasarc/rosat/data/pspc/processed_data/900000/rs931625n00/ [astroquery.heasarc.core]
INFO: downloading s3://nasa-heasarc/rosat/data/pspc/processed_data/900000/rs931627n00/ [astroquery.heasarc.core]
INFO: downloading s3://nasa-heasarc/rosat/data/pspc/processed_data/900000/rs931632n00/ [astroquery.heasarc.core]
INFO: downloading s3://nasa-heasarc/rosat/data/pspc/processed_data/900000/rs931644n00/ [astroquery.heasarc.core]
INFO: downloading s3://nasa-heasarc/rosat/data/pspc/processed_data/900000/rs931503n00/ [astroquery.heasarc.core]
INFO: downloading s3://nasa-heasarc/rosat/data/pspc/processed_data/900000/rs931506n00/ [astroquery.heasarc.core]
INFO: downloading s3://nasa-heasarc/rosat/data/pspc/processed_data/900000/rs931547n00/ [astroquery.heasarc.core]
INFO: downloading s3://nasa-heasarc/rosat/data/pspc/processed_data/900000/rs931410n00/ [astroquery.heasarc.core]
INFO: downloading s3://nasa-heasarc/rosat/data/pspc/processed_data/900000/rs931412n00/ [astroquery.heasarc.core]
INFO: downloading s3://nasa-heasarc/rosat/data/pspc/processed_data/900000/rs931413n00/ [astroquery.heasarc.core]
INFO: downloading s3://nasa-heasarc/rosat/data/pspc/processed_data/900000/rs931415n00/ [astroquery.heasarc.core]
INFO: downloading s3://nasa-heasarc/rosat/data/pspc/processed_data/900000/rs931416n00/ [astroquery.heasarc.core]
INFO: downloading s3://nasa-heasarc/rosat/data/pspc/processed_data/900000/rs931432n00/ [astroquery.heasarc.core]
INFO: downloading s3://nasa-heasarc/rosat/data/pspc/processed_data/900000/rs931440n00/ [astroquery.heasarc.core]
INFO: downloading s3://nasa-heasarc/rosat/data/pspc/processed_data/900000/rs931460n00/ [astroquery.heasarc.core]
INFO: downloading s3://nasa-heasarc/rosat/data/pspc/processed_data/900000/rs931307n00/ [astroquery.heasarc.core]
INFO: downloading s3://nasa-heasarc/rosat/data/pspc/processed_data/900000/rs931312n00/ [astroquery.heasarc.core]
INFO: downloading s3://nasa-heasarc/rosat/data/pspc/processed_data/900000/rs931313n00/ [astroquery.heasarc.core]
INFO: downloading s3://nasa-heasarc/rosat/data/pspc/processed_data/900000/rs931321n00/ [astroquery.heasarc.core]
INFO: downloading s3://nasa-heasarc/rosat/data/pspc/processed_data/900000/rs931327n00/ [astroquery.heasarc.core]
INFO: downloading s3://nasa-heasarc/rosat/data/pspc/processed_data/900000/rs931341n00/ [astroquery.heasarc.core]
INFO: downloading s3://nasa-heasarc/rosat/data/pspc/processed_data/900000/rs931345n00/ [astroquery.heasarc.core]
INFO: downloading s3://nasa-heasarc/rosat/data/pspc/processed_data/900000/rs931350n00/ [astroquery.heasarc.core]
INFO: downloading s3://nasa-heasarc/rosat/data/pspc/processed_data/900000/rs931353n00/ [astroquery.heasarc.core]
INFO: downloading s3://nasa-heasarc/rosat/data/pspc/processed_data/900000/rs931203n00/ [astroquery.heasarc.core]
INFO: downloading s3://nasa-heasarc/rosat/data/pspc/processed_data/900000/rs931205n00/ [astroquery.heasarc.core]
INFO: downloading s3://nasa-heasarc/rosat/data/pspc/processed_data/900000/rs931226n00/ [astroquery.heasarc.core]
INFO: downloading s3://nasa-heasarc/rosat/data/pspc/processed_data/900000/rs931242n00/ [astroquery.heasarc.core]
INFO: downloading s3://nasa-heasarc/rosat/data/pspc/processed_data/900000/rs931111n00/ [astroquery.heasarc.core]
INFO: downloading s3://nasa-heasarc/rosat/data/pspc/processed_data/900000/rs931118n00/ [astroquery.heasarc.core]
INFO: downloading s3://nasa-heasarc/rosat/data/pspc/processed_data/900000/rs931128n00/ [astroquery.heasarc.core]
INFO: downloading s3://nasa-heasarc/rosat/data/pspc/processed_data/900000/rs931132n00/ [astroquery.heasarc.core]
INFO: downloading s3://nasa-heasarc/rosat/data/pspc/processed_data/900000/rs931145n00/ [astroquery.heasarc.core]
INFO: downloading s3://nasa-heasarc/rosat/data/pspc/processed_data/900000/rs931149n00/ [astroquery.heasarc.core]
INFO: downloading s3://nasa-heasarc/rosat/data/pspc/processed_data/900000/rs931152n00/ [astroquery.heasarc.core]
INFO: downloading s3://nasa-heasarc/rosat/data/pspc/processed_data/900000/rs931010n00/ [astroquery.heasarc.core]
INFO: downloading s3://nasa-heasarc/rosat/data/pspc/processed_data/900000/rs931049n00/ [astroquery.heasarc.core]
INFO: downloading s3://nasa-heasarc/rosat/data/pspc/processed_data/900000/rs930934n00/ [astroquery.heasarc.core]
INFO: downloading s3://nasa-heasarc/rosat/data/pspc/processed_data/900000/rs930938n00/ [astroquery.heasarc.core]
INFO: downloading s3://nasa-heasarc/rosat/data/pspc/processed_data/900000/rs930940n00/ [astroquery.heasarc.core]
INFO: downloading s3://nasa-heasarc/rosat/data/pspc/processed_data/900000/rs930947n00/ [astroquery.heasarc.core]
INFO: downloading s3://nasa-heasarc/rosat/data/pspc/processed_data/900000/rs930948n00/ [astroquery.heasarc.core]
INFO: downloading s3://nasa-heasarc/rosat/data/pspc/processed_data/900000/rs930809n00/ [astroquery.heasarc.core]
INFO: downloading s3://nasa-heasarc/rosat/data/pspc/processed_data/900000/rs930819n00/ [astroquery.heasarc.core]
INFO: downloading s3://nasa-heasarc/rosat/data/pspc/processed_data/900000/rs930820n00/ [astroquery.heasarc.core]
INFO: downloading s3://nasa-heasarc/rosat/data/pspc/processed_data/900000/rs930838n00/ [astroquery.heasarc.core]
INFO: downloading s3://nasa-heasarc/rosat/data/pspc/processed_data/900000/rs930841n00/ [astroquery.heasarc.core]
INFO: downloading s3://nasa-heasarc/rosat/data/pspc/processed_data/900000/rs930721n00/ [astroquery.heasarc.core]
INFO: downloading s3://nasa-heasarc/rosat/data/pspc/processed_data/900000/rs930730n00/ [astroquery.heasarc.core]
INFO: downloading s3://nasa-heasarc/rosat/data/pspc/processed_data/900000/rs930601n00/ [astroquery.heasarc.core]
INFO: downloading s3://nasa-heasarc/rosat/data/pspc/processed_data/900000/rs930609n00/ [astroquery.heasarc.core]
INFO: downloading s3://nasa-heasarc/rosat/data/pspc/processed_data/900000/rs930624n00/ [astroquery.heasarc.core]
INFO: downloading s3://nasa-heasarc/rosat/data/pspc/processed_data/900000/rs930625n00/ [astroquery.heasarc.core]
INFO: downloading s3://nasa-heasarc/rosat/data/pspc/processed_data/900000/rs930629n00/ [astroquery.heasarc.core]
INFO: downloading s3://nasa-heasarc/rosat/data/pspc/processed_data/900000/rs930514n00/ [astroquery.heasarc.core]
INFO: downloading s3://nasa-heasarc/rosat/data/pspc/processed_data/900000/rs930522n00/ [astroquery.heasarc.core]
INFO: downloading s3://nasa-heasarc/rosat/data/pspc/processed_data/900000/rs930411n00/ [astroquery.heasarc.core]
INFO: downloading s3://nasa-heasarc/rosat/data/pspc/processed_data/900000/rs930311n00/ [astroquery.heasarc.core]
INFO: downloading s3://nasa-heasarc/rosat/data/pspc/processed_data/900000/rs930204n00/ [astroquery.heasarc.core]
INFO: Enabling anonymous cloud data access ... [astroquery.heasarc.core]
INFO: downloading s3://nasa-heasarc/rosat/data/pspc/processed_data/900000/rs932114n00/ [astroquery.heasarc.core]
INFO: downloading s3://nasa-heasarc/rosat/data/pspc/processed_data/900000/rs932129n00/ [astroquery.heasarc.core]
INFO: downloading s3://nasa-heasarc/rosat/data/pspc/processed_data/900000/rs931916n00/ [astroquery.heasarc.core]
INFO: downloading s3://nasa-heasarc/rosat/data/pspc/processed_data/900000/rs931926n00/ [astroquery.heasarc.core]
INFO: downloading s3://nasa-heasarc/rosat/data/pspc/processed_data/900000/rs931930n00/ [astroquery.heasarc.core]
INFO: downloading s3://nasa-heasarc/rosat/data/pspc/processed_data/900000/rs931964n00/ [astroquery.heasarc.core]
INFO: downloading s3://nasa-heasarc/rosat/data/pspc/processed_data/900000/rs931819n00/ [astroquery.heasarc.core]
INFO: downloading s3://nasa-heasarc/rosat/data/pspc/processed_data/900000/rs931827n00/ [astroquery.heasarc.core]
INFO: downloading s3://nasa-heasarc/rosat/data/pspc/processed_data/900000/rs931830n00/ [astroquery.heasarc.core]
INFO: downloading s3://nasa-heasarc/rosat/data/pspc/processed_data/900000/rs931834n00/ [astroquery.heasarc.core]
INFO: downloading s3://nasa-heasarc/rosat/data/pspc/processed_data/900000/rs931853n00/ [astroquery.heasarc.core]
INFO: downloading s3://nasa-heasarc/rosat/data/pspc/processed_data/900000/rs931706n00/ [astroquery.heasarc.core]
INFO: downloading s3://nasa-heasarc/rosat/data/pspc/processed_data/900000/rs931714n00/ [astroquery.heasarc.core]
INFO: downloading s3://nasa-heasarc/rosat/data/pspc/processed_data/900000/rs931749n00/ [astroquery.heasarc.core]
INFO: downloading s3://nasa-heasarc/rosat/data/pspc/processed_data/900000/rs931751n00/ [astroquery.heasarc.core]
INFO: downloading s3://nasa-heasarc/rosat/data/pspc/processed_data/900000/rs931752n00/ [astroquery.heasarc.core]
INFO: downloading s3://nasa-heasarc/rosat/data/pspc/processed_data/900000/rs931758n00/ [astroquery.heasarc.core]
INFO: downloading s3://nasa-heasarc/rosat/data/pspc/processed_data/900000/rs931763n00/ [astroquery.heasarc.core]
INFO: downloading s3://nasa-heasarc/rosat/data/pspc/processed_data/900000/rs931610n00/ [astroquery.heasarc.core]
INFO: downloading s3://nasa-heasarc/rosat/data/pspc/processed_data/900000/rs931616n00/ [astroquery.heasarc.core]
INFO: downloading s3://nasa-heasarc/rosat/data/pspc/processed_data/900000/rs931625n00/ [astroquery.heasarc.core]
INFO: downloading s3://nasa-heasarc/rosat/data/pspc/processed_data/900000/rs931627n00/ [astroquery.heasarc.core]
INFO: downloading s3://nasa-heasarc/rosat/data/pspc/processed_data/900000/rs931632n00/ [astroquery.heasarc.core]
INFO: downloading s3://nasa-heasarc/rosat/data/pspc/processed_data/900000/rs931644n00/ [astroquery.heasarc.core]
INFO: downloading s3://nasa-heasarc/rosat/data/pspc/processed_data/900000/rs931503n00/ [astroquery.heasarc.core]
INFO: downloading s3://nasa-heasarc/rosat/data/pspc/processed_data/900000/rs931506n00/ [astroquery.heasarc.core]
INFO: downloading s3://nasa-heasarc/rosat/data/pspc/processed_data/900000/rs931547n00/ [astroquery.heasarc.core]
INFO: downloading s3://nasa-heasarc/rosat/data/pspc/processed_data/900000/rs931410n00/ [astroquery.heasarc.core]
INFO: downloading s3://nasa-heasarc/rosat/data/pspc/processed_data/900000/rs931412n00/ [astroquery.heasarc.core]
INFO: downloading s3://nasa-heasarc/rosat/data/pspc/processed_data/900000/rs931413n00/ [astroquery.heasarc.core]
INFO: downloading s3://nasa-heasarc/rosat/data/pspc/processed_data/900000/rs931415n00/ [astroquery.heasarc.core]
INFO: downloading s3://nasa-heasarc/rosat/data/pspc/processed_data/900000/rs931416n00/ [astroquery.heasarc.core]
INFO: downloading s3://nasa-heasarc/rosat/data/pspc/processed_data/900000/rs931432n00/ [astroquery.heasarc.core]
INFO: downloading s3://nasa-heasarc/rosat/data/pspc/processed_data/900000/rs931440n00/ [astroquery.heasarc.core]
INFO: downloading s3://nasa-heasarc/rosat/data/pspc/processed_data/900000/rs931460n00/ [astroquery.heasarc.core]
INFO: downloading s3://nasa-heasarc/rosat/data/pspc/processed_data/900000/rs931307n00/ [astroquery.heasarc.core]
INFO: downloading s3://nasa-heasarc/rosat/data/pspc/processed_data/900000/rs931312n00/ [astroquery.heasarc.core]
INFO: downloading s3://nasa-heasarc/rosat/data/pspc/processed_data/900000/rs931313n00/ [astroquery.heasarc.core]
INFO: downloading s3://nasa-heasarc/rosat/data/pspc/processed_data/900000/rs931321n00/ [astroquery.heasarc.core]
INFO: downloading s3://nasa-heasarc/rosat/data/pspc/processed_data/900000/rs931327n00/ [astroquery.heasarc.core]
INFO: downloading s3://nasa-heasarc/rosat/data/pspc/processed_data/900000/rs931341n00/ [astroquery.heasarc.core]
INFO: downloading s3://nasa-heasarc/rosat/data/pspc/processed_data/900000/rs931345n00/ [astroquery.heasarc.core]
INFO: downloading s3://nasa-heasarc/rosat/data/pspc/processed_data/900000/rs931350n00/ [astroquery.heasarc.core]
INFO: downloading s3://nasa-heasarc/rosat/data/pspc/processed_data/900000/rs931353n00/ [astroquery.heasarc.core]
INFO: downloading s3://nasa-heasarc/rosat/data/pspc/processed_data/900000/rs931203n00/ [astroquery.heasarc.core]
INFO: downloading s3://nasa-heasarc/rosat/data/pspc/processed_data/900000/rs931205n00/ [astroquery.heasarc.core]
INFO: downloading s3://nasa-heasarc/rosat/data/pspc/processed_data/900000/rs931226n00/ [astroquery.heasarc.core]
INFO: downloading s3://nasa-heasarc/rosat/data/pspc/processed_data/900000/rs931242n00/ [astroquery.heasarc.core]
INFO: downloading s3://nasa-heasarc/rosat/data/pspc/processed_data/900000/rs931111n00/ [astroquery.heasarc.core]
INFO: downloading s3://nasa-heasarc/rosat/data/pspc/processed_data/900000/rs931118n00/ [astroquery.heasarc.core]
INFO: downloading s3://nasa-heasarc/rosat/data/pspc/processed_data/900000/rs931128n00/ [astroquery.heasarc.core]
INFO: downloading s3://nasa-heasarc/rosat/data/pspc/processed_data/900000/rs931132n00/ [astroquery.heasarc.core]
INFO: downloading s3://nasa-heasarc/rosat/data/pspc/processed_data/900000/rs931145n00/ [astroquery.heasarc.core]
INFO: downloading s3://nasa-heasarc/rosat/data/pspc/processed_data/900000/rs931149n00/ [astroquery.heasarc.core]
INFO: downloading s3://nasa-heasarc/rosat/data/pspc/processed_data/900000/rs931152n00/ [astroquery.heasarc.core]
INFO: downloading s3://nasa-heasarc/rosat/data/pspc/processed_data/900000/rs931010n00/ [astroquery.heasarc.core]
INFO: downloading s3://nasa-heasarc/rosat/data/pspc/processed_data/900000/rs931049n00/ [astroquery.heasarc.core]
INFO: downloading s3://nasa-heasarc/rosat/data/pspc/processed_data/900000/rs930934n00/ [astroquery.heasarc.core]
INFO: downloading s3://nasa-heasarc/rosat/data/pspc/processed_data/900000/rs930938n00/ [astroquery.heasarc.core]
INFO: downloading s3://nasa-heasarc/rosat/data/pspc/processed_data/900000/rs930940n00/ [astroquery.heasarc.core]
INFO: downloading s3://nasa-heasarc/rosat/data/pspc/processed_data/900000/rs930947n00/ [astroquery.heasarc.core]
INFO: downloading s3://nasa-heasarc/rosat/data/pspc/processed_data/900000/rs930948n00/ [astroquery.heasarc.core]
INFO: downloading s3://nasa-heasarc/rosat/data/pspc/processed_data/900000/rs930809n00/ [astroquery.heasarc.core]
INFO: downloading s3://nasa-heasarc/rosat/data/pspc/processed_data/900000/rs930819n00/ [astroquery.heasarc.core]
INFO: downloading s3://nasa-heasarc/rosat/data/pspc/processed_data/900000/rs930820n00/ [astroquery.heasarc.core]
INFO: downloading s3://nasa-heasarc/rosat/data/pspc/processed_data/900000/rs930838n00/ [astroquery.heasarc.core]
INFO: downloading s3://nasa-heasarc/rosat/data/pspc/processed_data/900000/rs930841n00/ [astroquery.heasarc.core]
INFO: downloading s3://nasa-heasarc/rosat/data/pspc/processed_data/900000/rs930721n00/ [astroquery.heasarc.core]
INFO: downloading s3://nasa-heasarc/rosat/data/pspc/processed_data/900000/rs930730n00/ [astroquery.heasarc.core]
INFO: downloading s3://nasa-heasarc/rosat/data/pspc/processed_data/900000/rs930601n00/ [astroquery.heasarc.core]
INFO: downloading s3://nasa-heasarc/rosat/data/pspc/processed_data/900000/rs930609n00/ [astroquery.heasarc.core]
INFO: downloading s3://nasa-heasarc/rosat/data/pspc/processed_data/900000/rs930624n00/ [astroquery.heasarc.core]
INFO: downloading s3://nasa-heasarc/rosat/data/pspc/processed_data/900000/rs930625n00/ [astroquery.heasarc.core]
INFO: downloading s3://nasa-heasarc/rosat/data/pspc/processed_data/900000/rs930629n00/ [astroquery.heasarc.core]
INFO: downloading s3://nasa-heasarc/rosat/data/pspc/processed_data/900000/rs930514n00/ [astroquery.heasarc.core]
INFO: downloading s3://nasa-heasarc/rosat/data/pspc/processed_data/900000/rs930522n00/ [astroquery.heasarc.core]
INFO: downloading s3://nasa-heasarc/rosat/data/pspc/processed_data/900000/rs930411n00/ [astroquery.heasarc.core]
INFO: downloading s3://nasa-heasarc/rosat/data/pspc/processed_data/900000/rs930311n00/ [astroquery.heasarc.core]
INFO: downloading s3://nasa-heasarc/rosat/data/pspc/processed_data/900000/rs930204n00/ [astroquery.heasarc.core]

What is included in the downloaded data?#

Examining the contents of one of the directories we just downloaded, we find that there really aren’t that many files in there. The most interesting ones are:

  • {RASS SEQUENCE ID}_bas.fits.Z - The event list for this RASS skyfield, containing tables of accepted and rejected events, as well as tables of Good Time Intervals (GTIs).

  • {RASS SEQUENCE ID}_im{BAND}.fits.Z - Whole skyfield images generated in different energy bands;

    • 1 - 0.07-2.4 keV [full energy range of ROSAT-PSPC]

    • 2 - 0.4-2.4 keV [ROSAT-PSPC ‘hard band’, though a soft band by modern standards]

    • 3 - 0.07-0.4 keV [ROSAT-PSPC ‘soft band’]

  • {RASS SEQUENCE ID}_bk{BAND}.fits.Z - Maps of skyfield background in different energy bands.

  • {RASS SEQUENCE ID}_mex.fits.Z - The exposure map for the skyfield.

  • {RASS SEQUENCE ID}_anc.fits.Z - Ancillary information about orbit and pointing of the spacecraft.

os.listdir(os.path.join(ROOT_DATA_DIR, uniq_seq_ids[0].lower()))
['rs930204n00.public_contents',
 'rs930204n00.public_contents_mpe',
 'rs930204n00_anc.fits.Z',
 'rs930204n00_bas.fits.Z',
 'rs930204n00_bk1.fits.Z',
 'rs930204n00_bk2.fits.Z',
 'rs930204n00_bk3.fits.Z',
 'rs930204n00_im1.fits.Z',
 'rs930204n00_im1.gif',
 'rs930204n00_im2.fits.Z',
 'rs930204n00_im2.gif',
 'rs930204n00_im3.fits.Z',
 'rs930204n00_im3.gif',
 'rs930204n00_ime.fits.Z',
 'rs930204n00_ime.gif',
 'rs930204n00_mex.fits']

Examining pregenerated RASS images#

We can immediately take advantage of the pregenerated images and exposure maps included in each skyfield’s data directory by loading them into XGA Image and ExpMap classes, setting up count-rate maps, and visualizing the regions surrounding our 2RXS-matched subset of CARMENES M dwarfs.

This sets up the count-rate map objects and stores them in a dictionary for later use:

# Dictionary to store instantiated pregenerated ratemaps
pregen_ratemaps = {}

for cur_src_name, cur_seq_id in src_seq_ids.items():
    cur_im = Image(
        PREGEN_IMAGE_PATH_TEMP.format(loi=cur_seq_id.lower()),
        cur_seq_id,
        "",
        "",
        "",
        "",
        Quantity(0.07, "keV"),
        Quantity(2.4, "keV"),
    )

    cur_ex_path = PREGEN_EXPMAP_PATH_TEMP.format(loi=cur_seq_id.lower())
    # The archive inconsistenly provides compressed exposure maps
    if not os.path.exists(cur_ex_path):
        cur_ex_path += ".Z"

    cur_ex = ExpMap(
        cur_ex_path,
        cur_seq_id,
        "",
        "",
        "",
        "",
        Quantity(0.07, "keV"),
        Quantity(2.4, "keV"),
    )

    cur_rt = RateMap(cur_im, cur_ex)
    cur_rt.src_name = cur_src_name

    pregen_ratemaps[cur_src_name] = cur_rt

Note

The RASS exposure maps ({RASS SEQUENCE ID}_mex.fits(.Z)) archived by HEASARC are not consistently compressed. Some are compressed using Zlib (with a .Z extension), while others are not compressed at all.

Now we can create a fairly large figure that visualizes the RASS data for every source in our matched subset of CARMENES M dwarfs. Each panel is centered on the CARMENES coordinate of the M dwarf and has a half-side length configured by ZOOM_HALF_SIDE_ANG.

# Half-side length for zoomed-in images centered on our sources
ZOOM_HALF_SIDE_ANG = Quantity(3, "arcmin")

The displayed maps are in counts-per-second, but they are not consistently scaled, and we have not added a colorbar to indicate pixel values, so this figure is not meant for scientific interpretation, merely visual inspection:

Hide code cell source

num_cols = 4
fig_side_size = 3

num_ims = len(pregen_ratemaps)

num_rows = int(np.ceil(num_ims / num_cols))

fig, ax_arr = plt.subplots(
    ncols=num_cols,
    nrows=num_rows,
    figsize=(fig_side_size * num_cols, fig_side_size * num_rows),
)
plt.subplots_adjust(wspace=0.02, hspace=0.02)

ax_ind = 0
for ax_arr_ind, ax in np.ndenumerate(ax_arr):
    if ax_ind >= num_ims:
        ax.set_visible(False)
        continue

    ax.set_axis_off()

    cur_src_name, cur_rt = list(pregen_ratemaps.items())[ax_ind]

    # Fetch the actual source name from the CARMENES catalog
    cur_actual_name = carm_2rxs_match["carm_name"][ax_ind]

    # Fetch the CARMENES coordinate of the current source
    cur_coord = matched_carm_coords[ax_ind]
    # Turn the coord into an Astropy quantity, which the current version of
    #  XGA requires instead of a SkyCoord object.
    cur_coord_quan = Quantity([cur_coord.ra, cur_coord.dec], "deg")

    pd_scale = pix_deg_scale(cur_coord_quan, cur_rt.radec_wcs)
    pix_half_size = (ZOOM_HALF_SIDE_ANG / pd_scale).to("pix").astype(int)

    pix_coord = cur_rt.coord_conv(cur_coord_quan, "pix")
    x_lims = [
        (pix_coord[0] - pix_half_size).value,
        (pix_coord[0] + pix_half_size).value,
    ]
    y_lims = [
        (pix_coord[1] - pix_half_size).value,
        (pix_coord[1] + pix_half_size).value,
    ]

    cur_rt.get_view(
        ax,
        zoom_in=True,
        manual_zoom_xlims=x_lims,
        manual_zoom_ylims=y_lims,
        custom_title=cur_actual_name,
    )

    ax_ind += 1

plt.tight_layout()
plt.show()
../../../_images/881a2418ab31a02906aff704d915feb61f112b63e1ba61654803e3750f0a1c7c.png

An important part of working with large datasets, be they of one object or multiple (as in this case) is memory management.

It might be tempting to load every image/exposure map into memory (and keep them there) as even laptops tend to have 8–16 GB of RAM at this point. Also, reading data from disk is slower than accessing it from memory.

However, memory saturation can creep up on you (with unpredictable consequences).

It’s important to get into good habits, even with an older mission like ROSAT whose data files are generally fairly small, and even with the relatively diminutive sample of M dwarfs we’re dealing with here.

As we’ve finished using the data associated with the pregenerated count-rate maps we can free up some RAM by deleting the data arrays.

We do make use of the exposure maps to correct spectrum exposure times later on in the demonstration, but because we’re using XGA product classes, the exposure map data will be automatically re-loaded from disk when needed:

for cur_rt in pregen_ratemaps.values():
    # An upcoming XGA release includes better memory management, which
    #  will remove the necessity of much of this
    del cur_rt.image.data
    del cur_rt.expmap.data

    del cur_rt._data
    cur_rt._data = None

3. Generating new RASS images#

We’ve already made use of the pregenerated images included in the ROSAT All-Sky Survey archive, but what if we wanted to generate new versions? This section will take you through that process.

There are some practical limitations to what you can expect from RASS data:

  • Both the spatial and energy resolutions of ROSAT All-Sky Survey data are quite coarse; so if you want more finely binned images to tease out some spatial features, for instance, then be cautious.

  • Similarly, if you’re defining custom energy ranges, you will have to carefully consider the energy bounds you’re using so as to include enough spectral channels for there to be a usable number of photons in each pixel.

Making event lists easily accessible#

In preparation for the generation of our new RASS images (and the extraction of new spectra later on in this demonstration), we will load our skyfield event lists into XGA EventList objects.

These objects won’t read the event list tables into memory, at least not automatically (we won’t be interacting with them through Python in this demonstration, so that data won’t be required).

Instead, they will provide a convenient interface to the event list headers:

preproc_event_lists = {}

for cur_src_name, cur_seq_id in src_seq_ids.items():
    cur_evt_path = PREPROC_EVT_PATH_TEMP.format(loi=cur_seq_id.lower())
    cur_evts = EventList(cur_evt_path, obs_id=cur_seq_id)
    cur_evts.src_name = cur_src_name

    preproc_event_lists[cur_src_name] = cur_evts

preproc_event_lists
{'CARMENES-2': <xga.products.misc.EventList at 0x76a5eaeae990>,
 'CARMENES-35': <xga.products.misc.EventList at 0x76a5eae83860>,
 'CARMENES-37': <xga.products.misc.EventList at 0x76a5ea5f5130>,
 'CARMENES-63': <xga.products.misc.EventList at 0x76a5e8f0bf50>,
 'CARMENES-64': <xga.products.misc.EventList at 0x76a5ea4b4830>,
 'CARMENES-67': <xga.products.misc.EventList at 0x76a5e8cfd7f0>,
 'CARMENES-79': <xga.products.misc.EventList at 0x76a5e8d4b920>,
 'CARMENES-103': <xga.products.misc.EventList at 0x76a5ea6d4b60>,
 'CARMENES-118': <xga.products.misc.EventList at 0x76a5ea24e000>,
 'CARMENES-139': <xga.products.misc.EventList at 0x76a5f05fbc50>,
 'CARMENES-151': <xga.products.misc.EventList at 0x76a5e964d490>,
 'CARMENES-155': <xga.products.misc.EventList at 0x76a5ea7dc1a0>,
 'CARMENES-157': <xga.products.misc.EventList at 0x76a5e9164620>,
 'CARMENES-161': <xga.products.misc.EventList at 0x76a5e8fb0410>,
 'CARMENES-168': <xga.products.misc.EventList at 0x76a5e8fd3fe0>,
 'CARMENES-169': <xga.products.misc.EventList at 0x76a5ea46fe00>,
 'CARMENES-173': <xga.products.misc.EventList at 0x76a5e90c84a0>,
 'CARMENES-174': <xga.products.misc.EventList at 0x76a5e9485fa0>,
 'CARMENES-183': <xga.products.misc.EventList at 0x76a5e8f3d010>,
 'CARMENES-196': <xga.products.misc.EventList at 0x76a5eaa9f920>,
 'CARMENES-216': <xga.products.misc.EventList at 0x76a5ea34a270>,
 'CARMENES-224': <xga.products.misc.EventList at 0x76a5ea2bcbf0>,
 'CARMENES-226': <xga.products.misc.EventList at 0x76a5e8f97fe0>,
 'CARMENES-233': <xga.products.misc.EventList at 0x76a5e8d8dc40>,
 'CARMENES-234': <xga.products.misc.EventList at 0x76a5f0606a20>,
 'CARMENES-266': <xga.products.misc.EventList at 0x76a5ea0b9490>,
 'CARMENES-284': <xga.products.misc.EventList at 0x76a5f060fa10>,
 'CARMENES-301': <xga.products.misc.EventList at 0x76a5e8ec4f50>,
 'CARMENES-307': <xga.products.misc.EventList at 0x76a5e97baf90>,
 'CARMENES-342': <xga.products.misc.EventList at 0x76a5e8f76e10>,
 'CARMENES-355': <xga.products.misc.EventList at 0x76a5e8da4050>,
 'CARMENES-358': <xga.products.misc.EventList at 0x76a5e8da4260>,
 'CARMENES-359': <xga.products.misc.EventList at 0x76a5e8dc40e0>,
 'CARMENES-363': <xga.products.misc.EventList at 0x76a5e8dc8170>,
 'CARMENES-371': <xga.products.misc.EventList at 0x76a5e8dedcd0>,
 'CARMENES-377': <xga.products.misc.EventList at 0x76a5e8c44740>,
 'CARMENES-378': <xga.products.misc.EventList at 0x76a5e8eafc80>,
 'CARMENES-387': <xga.products.misc.EventList at 0x76a5e8d7c890>,
 'CARMENES-391': <xga.products.misc.EventList at 0x76a5e908cf20>,
 'CARMENES-396': <xga.products.misc.EventList at 0x76a5e95db9e0>,
 'CARMENES-399': <xga.products.misc.EventList at 0x76a5e8c4d760>,
 'CARMENES-411': <xga.products.misc.EventList at 0x76a5ea7bc4d0>,
 'CARMENES-412': <xga.products.misc.EventList at 0x76a5e8fb0350>,
 'CARMENES-413': <xga.products.misc.EventList at 0x76a5e9484890>,
 'CARMENES-417': <xga.products.misc.EventList at 0x76a5e94852b0>,
 'CARMENES-424': <xga.products.misc.EventList at 0x76a5e8c33f20>,
 'CARMENES-430': <xga.products.misc.EventList at 0x76a5f0637590>,
 'CARMENES-456': <xga.products.misc.EventList at 0x76a5e9384410>,
 'CARMENES-491': <xga.products.misc.EventList at 0x76a5ea5c30b0>,
 'CARMENES-517': <xga.products.misc.EventList at 0x76a5e8f09130>,
 'CARMENES-524': <xga.products.misc.EventList at 0x76a5e90cbec0>,
 'CARMENES-526': <xga.products.misc.EventList at 0x76a5e9415130>,
 'CARMENES-537': <xga.products.misc.EventList at 0x76a5e8c98d40>,
 'CARMENES-559': <xga.products.misc.EventList at 0x76a5e8dc61e0>,
 'CARMENES-561': <xga.products.misc.EventList at 0x76a5e8dc8320>,
 'CARMENES-563': <xga.products.misc.EventList at 0x76a5e8dec7d0>,
 'CARMENES-564': <xga.products.misc.EventList at 0x76a5e8ca9160>,
 'CARMENES-578': <xga.products.misc.EventList at 0x76a5e8c714f0>,
 'CARMENES-585': <xga.products.misc.EventList at 0x76a5e908f9e0>,
 'CARMENES-594': <xga.products.misc.EventList at 0x76a5f1223ec0>,
 'CARMENES-603': <xga.products.misc.EventList at 0x76a5e8d7c590>,
 'CARMENES-605': <xga.products.misc.EventList at 0x76a5e8cba840>,
 'CARMENES-613': <xga.products.misc.EventList at 0x76a5e8eacb30>,
 'CARMENES-618': <xga.products.misc.EventList at 0x76a5e8c4c110>,
 'CARMENES-630': <xga.products.misc.EventList at 0x76a5e8cbb230>,
 'CARMENES-632': <xga.products.misc.EventList at 0x76a5e8c472f0>,
 'CARMENES-633': <xga.products.misc.EventList at 0x76a5e8c14110>,
 'CARMENES-641': <xga.products.misc.EventList at 0x76a5f0620ad0>,
 'CARMENES-645': <xga.products.misc.EventList at 0x76a5e8ce1b80>,
 'CARMENES-646': <xga.products.misc.EventList at 0x76a5e8ccef90>,
 'CARMENES-669': <xga.products.misc.EventList at 0x76a5f05fb530>,
 'CARMENES-675': <xga.products.misc.EventList at 0x76a5e8c30c20>,
 'CARMENES-678': <xga.products.misc.EventList at 0x76a5e8c98350>,
 'CARMENES-681': <xga.products.misc.EventList at 0x76a5f060f140>,
 'CARMENES-694': <xga.products.misc.EventList at 0x76a5f0606ba0>,
 'CARMENES-705': <xga.products.misc.EventList at 0x76a5e8c33560>,
 'CARMENES-708': <xga.products.misc.EventList at 0x76a5e8ed5ac0>,
 'CARMENES-715': <xga.products.misc.EventList at 0x76a5e8caa3c0>,
 'CARMENES-718': <xga.products.misc.EventList at 0x76a5e8c721b0>,
 'CARMENES-724': <xga.products.misc.EventList at 0x76a5e8dc94c0>,
 'CARMENES-733': <xga.products.misc.EventList at 0x76a5e8def980>,
 'CARMENES-744': <xga.products.misc.EventList at 0x76a5e8dec260>,
 'CARMENES-749': <xga.products.misc.EventList at 0x76a5e908d190>}

Defining energy bands for new images#

To make images with custom energy bounds, we need to know the mapping between the ROSAT-PSPC PI channels and energies, as the image generation tool we’re about to use wants us to specify channel bounds, rather than energy bounds.

The energy-channel scaling for ROSAT-PSPC is well known, and we have defined a PSPC_EV_PER_CHAN Astropy Quantity constant in the Global Setup: Constants section. You could also derive this value from the ROSAT-PSPCC Redistribution Matrix File (RMF), which describes the relationship between channels and energy - we fetch the RMF in a later section.

Now we get to define the energy bounds for the images we want to generate.

As we’ve previously mentioned, RASS’ energy range is quite limited by modern standards, only 0.07–2.4 keV. For this demonstration we make new images in two energy bands; 0.5–2.0 keV and 1.0–2.0 keV.

The 0.5–2.0 keV band is often referred to as the ‘soft band’, at least in X-ray galaxy cluster studies (every field seems to have its own definition of what ‘soft’ means), and might be useful for comparisons to images from other missions.

rass_im_en_bounds = Quantity([[0.5, 2.0], [1.0, 2.0]], "keV")

Note

If you run this demonstration with a modified rass_im_en_bounds variable, note that even a single energy band should be defined as though it were part of a list (e.g., Quantity([[0.5, 2.0]], "keV")), to make it compatible with the image generation function we use later in the notebook.

Converting those energy bounds to channel bounds is straightforward, we simply divide the energy values by our assumed mapping between energy and channel.

The resulting lower and upper bound channel values are rounded down and up to the nearest integer channel respectively.

rass_im_ch_bounds = (rass_im_en_bounds / PSPC_EV_PER_CHAN).to("chan")
rass_im_ch_bounds[:, 0] = np.floor(rass_im_ch_bounds[:, 0])
rass_im_ch_bounds[:, 1] = np.ceil(rass_im_ch_bounds[:, 1])
rass_im_ch_bounds = rass_im_ch_bounds.astype(int)
rass_im_ch_bounds
\[[[50,~203],~ [101,~203]] \; \mathrm{chan}\]

Note

Though we demonstrate how to convert energy bounds to channel bounds above, the wrapper function for image generation will repeat this exercise, as it will write energy bounds into output file names.

Image binning factor#

The final choice we have to make before generating new images is the ‘binning factor’ (or factors). These control the spatial resolution of the output images, and are essentially the number of RASS’ Sky X-Y ‘pixels’ that get binned into a single output image pixel.

Archived RASS images were created with a binning factor of 90, resulting in a 512x512 grid, and a pixel scale of 45\(^{\prime\prime}\).

Calculating the binning factor required for a particular image pixel scale is quite straightforward. We can pull the intrinsic Sky X-Y pixel scale from the header of an events list, then divide our desired pixel scale by that number.

As we’re extracting the Sky X-Y pixel scale from only the TCDLT1 entry (there is another equivalent value for the y-direction stored under TCDLT2) there is an implicit assumption here that the Sky X-Y pixels are square, but that is reasonable.

Here we demonstrate calculating the binning factor for a pixel scale of \(1^{\prime}\); the chain of method calls (.to('').round(0).astype(int).value) applied to the calculation:

  1. Ensures the Astropy quantity result is dimensionless, rather than in units of \(\frac{\prime}{\circ}\).

  2. Rounds to the nearest integer.

  3. Converts the data type to integer and then reads out the integer value from the Astropy quantity.

cur_evts = list(preproc_event_lists.values())[0]
cur_skyxy_ps = abs(Quantity(cur_evts.event_header["TCDLT1"], "deg/pix"))

calc_ibf = (Quantity(1, "arcmin/pix") / cur_skyxy_ps).to("").round(0).astype(int).value
calc_ibf
np.int64(120)

We have somewhat arbitrarily chosen two coarser binning factors for this demonstration, corresponding to pixel scales of \(90^{\prime\prime}\) and \(135^{\prime\prime}\) respectively:

# List of binning factors for the new images
bin_factors = [180, 270]

Danger

Choosing very small values for the binning factor, for instance 1, will mean that generation of new images will consume a great deal of memory, and output files will be very large.

Incidentally, there would be very little point to generating images at the Sky X-Y pixel scale for RASS, as it would dramatically oversample the practical angular resolution of the survey.

Running image generation#

There is no HEASoft tool specifically intended to generate RASS images, but there is a generalized HEASoft image (and other data product) generation task that we can use.

If you have previously generated images, light curves, or spectra from HEASARC-hosted X-ray data on the command line, you may well have come across XSELECT; a HEASoft tool for interactively generating data products from event lists.

When creating data products, XSELECT calls the HEASoft extractor task, which we will now use to demonstrate the creation of RASS images.

As with all uses of HEASoft tasks in this notebook, our call to extractor will be through the HEASoftPy Python interface - specifically the hsp.extractor function.

We have implemented a wrapper to this function in the Global Setup: Functions section of this notebook, primarily so that we can easily run the generation of new images in parallel:

arg_combs = [
    [
        cur_evts.path,
        os.path.join(SEQ_OUT_PATH, cur_evts.obs_id),
        cur_evts.obs_id,
        *cur_bnds,
        cur_bf,
    ]
    for cur_evts in preproc_event_lists.values()
    for cur_bnds in rass_im_en_bounds
    for cur_bf in bin_factors
]

with mp.Pool(NUM_CORES) as p:
    im_result = p.starmap(gen_rass_image, arg_combs)
/opt/envs/heasoft/lib/python3.12/multiprocessing/popen_fork.py:66: DeprecationWarning: This process (pid=938) is multi-threaded, use of fork() may lead to deadlocks in the child.
  self.pid = os.fork()

Example visualization of new images#

To show off the various images we just created for each RASS skytile relevant to our M dwarf sample, we create a figure that displays them in a grid; each column corresponds to a different energy band, and each row to a different binning factor:

Hide code cell source

demo_evts = list(preproc_event_lists.values())[0]
demo_seq_id = demo_evts.obs_id

im_side_size = 5.5

num_cols = len(rass_im_en_bounds)
num_rows = len(bin_factors)

fig, ax_arr = plt.subplots(
    ncols=num_cols,
    nrows=num_rows,
    figsize=(im_side_size * num_cols, im_side_size * num_rows),
)
plt.subplots_adjust(wspace=0.0, hspace=0.06)

for cur_bnd_ind, cur_bnd in enumerate(rass_im_en_bounds):
    cur_lo = cur_bnd[0].to("keV").value
    cur_hi = cur_bnd[1].to("keV").value

    for cur_bf_ind, cur_bf in enumerate(bin_factors):
        cur_im_path = IM_PATH_TEMP.format(
            oi=demo_seq_id,
            ibf=cur_bf,
            lo=cur_bnd[0].to("keV").value,
            hi=cur_bnd[1].to("keV").value,
        )
        cur_im = Image(cur_im_path, demo_seq_id, "", "", "", "", *cur_bnd)

        cur_ax = ax_arr[cur_bf_ind, cur_bnd_ind]
        cur_ax.set_axis_off()

        cur_im.get_view(
            cur_ax,
            zoom_in=True,
            custom_title=f"{demo_seq_id} - {cur_lo}-{cur_hi} keV - "
            f"binning factor {cur_bf}",
        )

plt.show()
../../../_images/074dc349a4e12a14942b828f4939882bd0713c30d15b7cd5706298c28a3fd02f.png

4. Generating RASS spectra for our sample#

The image data products we generated in the previous section were general, valid for any source that happens to be within those skytiles. It might not even be necessary to make new skytile images for your science case, the archived images could well be sufficient.

Now, though, we generate spectra - these data products are specific to the sources we want to study and are not contained in the RASS archive.

The energy resolution/range of ROSAT All-Sky Survey data is quite limited, as was the sensitivity of the PSPC instrument and the average exposure time across the survey.

However, as we’ve previously stated, this is still the only publicly available imaging X-ray dataset across the entire sky - it is quite possible that RASS spectra can still lend insight to your research.

Defining the size of source and background regions#

To make a useful spectrum, we have to define a spatial region that we have decided will contain photons emitted by our source. That will have to be done for each source we want to study.

For this particular demonstration, we will also define another region per M dwarf, which will define where the background spectrum is extracted from. Though there are other methods for handling the ROSAT-PSPC background (the pcparpha tool will generate particle background spectra for ROSAT-PSPC, for instance), they are outside the scope of this demonstration.

In a more complex case - for instance, if we were studying an extended source like a galaxy cluster or a supernova remnant - we might also have to worry about excluding regions unrelated, contaminating, sources. That is outside the scope of this getting started guide, however, and we do not exclude any regions in this demonstration.

This tutorial creates identically sized source and background regions for each source in our sample. Source regions are circles, with their angular radii set to the value of SRC_ANG_RAD, centered on the CARMENES RA-Dec coordinate; background regions are also centered on the CARMENES coordinate, but are circular annuli, with inner radii set to INN_BACK_FACTOR\(\times\)SRC_ANG_RAD and outer radii set to OUT_BACK_FACTOR\(\times\)SRC_ANG_RAD:

SRC_ANG_RAD = Quantity(2, "arcmin")

INN_BACK_FACTOR = 1.05
OUT_BACK_FACTOR = 1.5

You could alter the values above to adjust the size of the regions for your own use. It would also be straightforward to modify the setting up Astropy regions section to be able to specify different region sizes for different sources.

Constructing RA-Dec ↔ RASS sky pixel WCSes#

In the previous section, we talked about defining the size and location of source/background regions in terms of angular radius and RA-Dec central coordinates.

Unfortunately, the extractor tool we’re going to use to make our spectra wants us to pass regions that are in the RASS Sky X-Y coordinate system (there can be issues with the generation of ‘weighting maps’ for RASS data if not) - so we need to make sure that we’re able to convert from RA-Dec to Sky X-Y.

Luckily for us, the RASS event lists we’ll be creating new spectra from contain the necessary information to construct Astropy World Coordinate System (WCS) objects that can do just that.

Here we set up one WCS per skytile (which is the same thing as saying one per source in this case) and store them in a dictionary for later access and use. The following information is being pulled from the event list’s STDEVT table header (through an XGA EventList object):

  • Pixel scale - One entry per direction (i.e., X and Y), and assigned to the cdelt property, these describe how many degrees a single RASS Sky X-Y pixel step corresponds to.

  • Critical pixel - An X-Y pixel coordinate that acts as a reference point for how Sky X-Y coordinates map onto RA-Dec coordinates, works in concert with the next entry.

  • RA-Dec at critical pixel - The RA-Dec coordinate corresponding to the Sky X-Y coordinate defined by the previous entry.

  • Sky X-Y ↔ RA-Dec projection - Which projection is used to map the cartesian Sky X-Y coordinate system onto the spherical RA-Dec coordinate system.

radec_skyxy_wcses = {}

for cur_src_name, cur_evts in preproc_event_lists.items():
    cur_wcs = WCS(naxis=2)
    cur_wcs.wcs.cdelt = [
        cur_evts.event_header["TCDLT1"],
        cur_evts.event_header["TCDLT2"],
    ]

    cur_wcs.wcs.crpix = [
        cur_evts.event_header["TCRPX1"],
        cur_evts.event_header["TCRPX2"],
    ]

    cur_wcs.wcs.crval = [
        cur_evts.event_header["TCRVL1"],
        cur_evts.event_header["TCRVL2"],
    ]

    cur_wcs.wcs.ctype = [
        cur_evts.event_header["TCTYP1"],
        cur_evts.event_header["TCTYP2"],
    ]

    radec_skyxy_wcses[cur_src_name] = cur_wcs

Setting up Astropy regions representing source and background regions#

We are now ready to set up our source and background regions; first in RA-Dec coordinates (with angular radii), and then also in Sky X-Y coordinates (and sky pixel radii).

The latter regions will be saved to disk as region files and ultimately passed to the HEASoft extractor tool when we create our new spectra.

In order to simplify the definition of these regions, we will use the Astropy-affiliated regions module. It’s also worth considering that this approach scales well to more complicated cases where we need to exclude a set of contaminating regions before we extract a spectrum.

Iterating through each source in our sample, we:

  1. Fetch the appropriate WCS for the current source (set up in the previous section).

  2. Fetch the CARMENES RA-Dec coordinate for the current source.

  3. Create an Astropy CircleSkyRegion instance, centered on the CARMENES RA-Dec, with a radius taken from SRC_ANG_RAD, and store it in a dictionary for later.

  4. Repeat the last step but instantiate a CircleAnnulusSkyRegion for the background region, with an inner radius of INN_BACK_FACTOR\(\times\)SRC_ANG_RAD and an outer radius of OUT_BACK_FACTOR\(\times\)SRC_ANG_RAD.

  5. Convert both source and background regions to the Sky X-Y coordinate system and write them to disk as DS9-formatted region files.

src_bck_radec_regs = {n: {"src": None, "bck": None} for n in src_seq_ids.keys()}
src_bck_skyxy_reg_files = {n: {"src": None, "bck": None} for n in src_seq_ids.keys()}

for cur_src_ind, cur_name_wcs in enumerate(radec_skyxy_wcses.items()):
    cur_src_name, cur_wcs = cur_name_wcs

    cur_src_coord = matched_carm_coords[cur_src_ind]

    cur_src_radec_reg = CircleSkyRegion(cur_src_coord, SRC_ANG_RAD)
    src_bck_radec_regs[cur_src_name]["src"] = cur_src_radec_reg

    cur_bck_radec_reg = CircleAnnulusSkyRegion(
        cur_src_coord, SRC_ANG_RAD * INN_BACK_FACTOR, SRC_ANG_RAD * OUT_BACK_FACTOR
    )
    src_bck_radec_regs[cur_src_name]["bck"] = cur_bck_radec_reg

    # Convert to Sky X-Y coordinates
    cur_src_skyxy_reg = cur_src_radec_reg.to_pixel(cur_wcs)
    cur_bck_skyxy_reg = cur_bck_radec_reg.to_pixel(cur_wcs)

    # Write Sky X-Y regions to files
    os.makedirs(os.path.join(SRC_OUT_PATH, cur_src_name), exist_ok=True)

    cur_src_skyxy_reg_path = SRC_REG_PATH_TEMP.format(
        sn=cur_src_name, oi=src_seq_ids[cur_src_name]
    )
    with open(cur_src_skyxy_reg_path, "w") as srco:
        srco.write(
            Regions([cur_src_skyxy_reg])
            .serialize(format="ds9")
            .replace("image", "physical")
        )
    src_bck_skyxy_reg_files[cur_src_name]["src"] = cur_src_skyxy_reg_path

    cur_bck_skyxy_reg_path = BCK_REG_PATH_TEMP.format(
        sn=cur_src_name, oi=src_seq_ids[cur_src_name]
    )
    with open(cur_bck_skyxy_reg_path, "w") as bcko:
        bcko.write(
            Regions([cur_bck_skyxy_reg])
            .serialize(format="ds9")
            .replace("image", "physical")
        )
    src_bck_skyxy_reg_files[cur_src_name]["bck"] = cur_bck_skyxy_reg_path

Note

During the preparation of the RASS Sky X-Y coordinate system region files using the Astropy-affiliated regions module, we generate a serialization (the string contents of the final file) of each region, rather than simply writing directly to disk using `Regions([…]).write(region file path, format).

This is because we need to replace the coordinate system name that is automatically used for all non-RA-Dec files written by the regions module (image), with physical, which is what the extractor tool will be expecting.

Defining image binning for the ‘weighting maps’#

As it turns out, when we create a new spectrum with HEASoft’s extractor task, we’re also generating an image and storing it in a FITS image extension of the spectrum file.

The new image stored in each spectrum is a ‘weighted map’ (or ‘WMAP’, and no not the CMB observatory), and will be used during the generation of Ancillary Response Files (ARFs).

ARFs describe the ‘effective area’ (i.e., sensitivity) as a function of incident-photon-energy. The ARFs used during normal analyses are a combination of the X-ray optics’ (called the X-ray Mirror Assembly, or XMA, for ROSAT) and the energy-dependent efficiency of the detector.

A WMAP is essentially the same as a ‘normal’ X-ray image and allows ARF calculation to find the average of ROSAT-PSPC response across the source region, weighted by the number of photons arriving at each point.

Weighted ARF calculation is particularly important for scanning-mode observations such as those that comprise the ROSAT All-Sky Survey, as the X-ray sky is drifting through the PSPCC FoV, see Belloni T., et al. (1994) for a discussion. Using the WMAP in ARF generation is meant to help account for this movement across the instrument, but its efficacy has not been well explored.

Also worth noting is that weighting ARFs is also very important for the analysis of extended sources (true of all spectro-imaging X-ray missions), though we are treating all our M dwarfs as point sources for this tutorial.

All that said, we need to choose a binning factor (the same idea as when we generated new images in the last section) for the WMAPs that will be generated with our new spectra. We select the same binning factor as was used to make archived RASS images; you may wish to experiment with different values to see how they affect the resulting ARFs.

wmap_bin_factor = 90

Running spectrum generation#

Here we run the actual generation of spectra for each M dwarf in our sample; just like with our image generation, individual products will be generated in parallel, maximizing the use of our computing resources and saving us some time.

The creation of new spectra from ROSAT All-Sky Survey data is achieved through the use of HEASoft’s extractor task (again, just like our image generation) - this demonstration uses the Python interface provided by HEASoftPy; hsp.extractor(...).

We have set up a wrapper function, gen_rass_spectrum(...), to generate RASS spectra in the ‘Global Setup: Functions’ section of this notebook, mostly to make it easier to parallelize.

Now we generate one source, and one background, spectrum per M dwarf, passing paths to the region files we set up, the RA-Dec source region object (so that coordinates and radius can be extracted and included in the file name), and the binning factor we defined above:

arg_combs = [
    [
        cur_evts.path,
        os.path.join(SRC_OUT_PATH, cur_name),
        cur_evts.obs_id,
        cur_name,
        src_bck_radec_regs[cur_name]["src"],
        src_bck_skyxy_reg_files[cur_name]["src"],
        src_bck_skyxy_reg_files[cur_name]["bck"],
        wmap_bin_factor,
    ]
    for cur_name, cur_evts in preproc_event_lists.items()
]

with mp.Pool(NUM_CORES) as p:
    sp_result = p.starmap(gen_rass_spectrum, arg_combs)
/opt/envs/heasoft/lib/python3.12/multiprocessing/popen_fork.py:66: DeprecationWarning: This process (pid=938) is multi-threaded, use of fork() may lead to deadlocks in the child.
  self.pid = os.fork()

Important

Technically the ROSAT-PSPC PI channel range goes up to 500, but only the first 256 are actually usable for analysis. Still, extractor(...) would create 500-channel spectra if you let it, and those files would be incompatible with 256-channel RMF we will fetch in the next section.

As such, the file path passed to extractor in the gen_rass_spectrum() function (see the ‘Global Setup: Functions’ section of this notebook), has a channel filter command appended to it - “[PI=0:256]”. This ensures that only the valid channels are considered, and makes the spectrum compatible with the RMF.

Generating supporting files#

A spectrum tells us how many photons were observed by ROSAT-PSPC in each detector channel, but to turn that into information about what was emitted by a source, we need a couple more data products.

Redistribution Matrix File (RMF)#

RMFs describe how a detector’s channels correspond to the energies of incident photons - knowing that takes the spectrum from photons observed in a particular channel, to photons observed at a particular energy.

That is clearly useful, as different astrophysical processes can be responsible for different photon energies, and we need an energy spectrum in our observer’s frame to be able to explore the source’s rest frame spectrum.

For RASS, we need to fetch the ROSAT-PSPCC RMF from the HEASARC Calibration Database (CALDB). HEASoft’s quzcif tool (we’re using the HEASoftpy interface) allows us to query the HEASARC CALDB for specific files - it can both return the names of matching files and download the file itself.

Many arguments can be passed to this tool to narrow down the files that are returned, including the name of the mission, instrument, and the type of CALDB file we’re looking for.

In this case we set mission="rosat" (of course), the instrument as ‘pspcc’ (as ROSAT-PSPCC was used for the all-sky survey, and PSPCB for pointed observations), and the CALDB codename="MATRIX" (RMFs are stored under this codename).

The only other argument we pass to filter the search results is expr="pich.eq.256"; this translates as “return matching files where a PI channel boundary is equal to 256”. In this case 256 is the upper boundary of the valid PI range of the RMF we wish to retrieve.

ROSAT’s CALDB entry includes a PROS 34-channel variant of the RMF, which would be invalid for our purposes. It is important to know, though, that the 256-channel RMF greatly oversamples the energy response of the PSPC. The 34-channel PROS response is a better match to the intrinsic energy resolution.

As we set retrieve=True the RMF will be downloaded to our current directory, so we use a context manager that temporarily changes the working directory to ROOT_DATA_DIR, and set up a variable containing the path to the freshly acquired RMF:

# This will find and download (retrieve=True) the ROSAT-PSPCC RMF file for
#  the 256 standard channel data
with contextlib.chdir(ROOT_DATA_DIR):
    caldb_rmf_ret = hsp.quzcif(
        mission="rosat",
        instrument="pspcc",
        codename="MATRIX",
        filter="-",
        date="-",
        time="-",
        expr="pich.eq.256",
        noprompt=True,
        retrieve=True,
        clobber=True,
    )

    # Store the path to the downloaded RMF in a variable, we'll use this later
    single_rmf_path = os.path.join(ROOT_DATA_DIR, caldb_rmf_ret.output[0].split(" ")[0])

Important

RMFs fundamentally describe the calibration of an instrument’s channels and event energies. Both the understanding of that calibration, and the behaviors of the instrument itself, often evolve with time. As such, if you are retrieving other RMFs using quzcif you should set the time and date filters to ensure you retrieve a file that matches your observation.

We do not set times and dates here because only one RMF was released for ROSAT-PSPCC, as the instrument was destroyed fairly early in the mission’s lifetime.

We just downloaded the appropriate RMF for RASS, so now we’ll make a copy for each spectrum we’ve generated (a little wasteful, but it is a small file):

rmf_paths = []

for cur_src_name, cur_seq_id in src_seq_ids.items():
    out_rmf_path = RMF_PATH_TEMP.format(oi=cur_seq_id, sn=cur_src_name)
    copyfile(single_rmf_path, out_rmf_path)

    rmf_paths.append(out_rmf_path)

Ancillary Response Files (ARF)#

HEASoft’s pcarf task is specifically intended to make ARFs for ROSAT-PSPC data, so we will make good use of it!

We discussed what ARFs are in an earlier section - the benefit of understanding the instrument’s sensitivity as a function of energy is that we can model what the original ‘real’ spectrum emitted by the source would have to be, to match the spectrum said instrument has observed.

That modeling is basically what X-ray spectral fitting is all about - various tools exist to perform the task, but in Section 5 we’ll use the PyXSPEC module.

We wish to parallelize the generation of ARFs for different spectra, and so create a wrapper function (gen_rosat_pspc_arf(...), see the ‘Global Setup: Functions’ section) around the pcarf task to make that easier.

The inputs are very limited, only the directory to write the ARF to, the path to the source spectrum file (extracted from the return of the spectrum generation step; sp_result[cur_ind][2]), and a path to the RMF file are required (the RMF path can also be set to ‘CALDB’ to automatically acquire it for this process):

arg_combs = [
    [
        os.path.join(SRC_OUT_PATH, cur_name),
        sp_result[cur_ind][2],
        single_rmf_path,
    ]
    for cur_ind, cur_name in enumerate(preproc_event_lists)
]

with mp.Pool(NUM_CORES) as p:
    arf_result = p.starmap(gen_rosat_pspc_arf, arg_combs)

Correcting RASS exposure times in spectral files#

An important quirk of ROSAT All-Sky Survey data is that the ‘LIVETIME’ information (the amount of time the detector was ‘on source’ and collecting data) contained in the event list is unhelpful/incorrect in two ways:

  1. It reports the total ‘live time’ for the entire skytile, which was observed in many passes; this means a much larger live time than was actually spent on a source.

  2. The live time entry is deliberately negative (e.g., a whole-skytile livetime of 17961 s is reported as -17961 s) to ensure it isn’t accidentally used in analyses.

Our newly generated spectra will have inherited that incorrect information, and so we need to fix it.

Thankfully, that’s pretty straightforward; we can use the exposure maps included in the archived RASS data directories (the contents of which we discussed in a previous section) to look up the actual exposure time at the coordinates of each spectrum’s source.

Exposure times at source coordinates are fetched from the ExpMap instances we set up earlier by passing the relevant source coordinate to the get_exp(...) method.

The FITS headers of the source and background spectra are then updated so that the “EXPOSURE” entry is set to the newly extracted exposure time:

for cur_ind, cur_src_name in enumerate(preproc_event_lists):

    cur_sp_path = sp_result[cur_ind][2]
    cur_bsp_path = sp_result[cur_ind][3]

    cur_coord = matched_carm_coords[cur_ind]
    cur_coord_quan = Quantity([cur_coord.ra, cur_coord.dec], "deg")

    cur_ex = pregen_ratemaps[cur_src_name].expmap
    del cur_ex.data
    cur_exp_time = cur_ex.get_exp(cur_coord_quan)

    with fits.open(cur_sp_path, mode="update") as speco:
        for en in speco:
            if "EXPOSURE" in en.header:
                del en.header["EXPOSURE"]
            en.header["EXPOSURE"] = cur_exp_time.to("s").value.round(5)

    with fits.open(cur_bsp_path, mode="update") as bspeco:
        for en in bspeco:
            if "EXPOSURE" in en.header:
                del en.header["EXPOSURE"]
            en.header["EXPOSURE"] = cur_exp_time.to("s").value.round(5)

Grouping spectral channels#

RASS spectra are likely to be low signal-to-noise due to the small effective area of ROSAT-PSPC (relative to many modern missions), and the short mean exposure time of the survey (~400 s). That said, due to the scanning pattern of the ROSAT All-Sky Survey, the ecliptic pole regions have considerably longer total exposure times. As such, sources in these regions (the Magellanic Clouds, for instance) can have quite high signal-to-noise compared to the rest of the sky.

As such, it is normally going to be a good idea to ‘group’ the channels of a RASS spectrum; combining sequential channels into a single bin until a particular quality metric is reached (e.g., a minimum number of counts, or a minimum signal-to-noise).

Some missions have created their own tools to perform this task, but HEASoft includes a generalized task called ftgrouppha that can be applied to any spectrum.

Several grouping metrics are implemented in ftgrouppha; we’ll take the simplest option and require a minimum number of counts per channel. The following will be passed to the task and will group channels until there are at least three counts:

spec_group_type = "min"
spec_group_scale = 3

Now we will apply ftgrouppha to each source spectrum, and save the output as a new grouped spectrum file. That grouped spectrum file is what will be used for model fitting in Section 5.

Grouping a spectrum in this manner is not particularly computationally expensive, so we have not bothered to write a wrapper function for ftgrouppha and parallelize the process as we did for the product generation tasks. Note, however, that if you are working on a much larger sample, you may want to take the time to parallelize this step.

The paths to the initial spectra are retrieved from the output of the spectrum generation step (sp_result[cur_ind][2]), and the paths to the output files are stored in a dictionary (grouped_sp_paths) for later use:

grp_spec_paths = {}

for cur_ind, cur_src_name in enumerate(preproc_event_lists):

    cur_sp_path = sp_result[cur_ind][2]

    cur_grp_spec = cur_sp_path.replace(
        "-spectrum", f"-{spec_group_type}grp{spec_group_scale}-spectrum"
    )

    hsp.ftgrouppha(
        infile=cur_sp_path,
        outfile=cur_grp_spec,
        grouptype=spec_group_type,
        groupscale=spec_group_scale,
        clobber=True,
        chatter=TASK_CHATTER,
        noprompt=True,
    )

    grp_spec_paths[cur_src_name] = cur_grp_spec

Adding supporting file paths to spectrum headers#

The very last thing we want to do before we can fit models to our spectra is to alter their FITS headers so they point to the ARF, RMF, and background files.

We don’t absolutely need to do this, as the paths to supporting files can be manually passed to PyXSPEC (and command-line XSPEC) as the data are loaded in.

However, including the paths in the headers means the ARF, RMF, and background spectra can be loaded in automatically, so we might as well:

for cur_ind, cur_src_name in enumerate(preproc_event_lists):

    cur_gsp_path = grp_spec_paths[cur_src_name]
    cur_bsp_path = sp_result[cur_ind][3]

    cur_arf_path = arf_result[cur_ind][1]
    cur_rmf_path = rmf_paths[cur_ind]

    with fits.open(cur_gsp_path, mode="update") as speco:

        del speco["SPECTRUM"].header["RESPFILE"]
        speco["SPECTRUM"].header["RESPFILE"] = os.path.basename(cur_rmf_path)

        del speco["SPECTRUM"].header["ANCRFILE"]
        speco["SPECTRUM"].header["ANCRFILE"] = os.path.basename(cur_arf_path)

        del speco["SPECTRUM"].header["BACKFILE"]
        speco["SPECTRUM"].header["BACKFILE"] = os.path.basename(cur_bsp_path)

Caution

We add the RESPFILE, ANCRFILE, and BACKFILE keywords after grouping because some FITS file modifications (such as using ftgrouppha) can add ‘&’ characters to the end of long strings in FITS headers. That then causes PyXSPEC to fail to read in supporting files for the spectrum.

As HEASARC-tutorials demonstrations lean toward using easy-to-read, informative, file names that are by necessity quite long, we add the correct paths using the Astropy fits module.

5. Fitting spectral models using PyXSPEC#

Having gone to the trouble of generating ROSAT All-Sky Survey spectra for our sample of M dwarfs, we’ll now fit some models and try to extract some properties!

Using the Python interface (PyXSPEC) to the ubiquitous XSPEC model fitting software, we will:

  1. Fit both power-law and blackbody models to each spectrum.

  2. Calculate and store the model fluxes.

  3. Extract and store the model parameters and uncertainties.

  4. Prepare to create visualizations of the fitted spectra.

Note that this demonstration was not written by an expert in the X-ray emission of M dwarfs (or indeed any kind of star), so please don’t necessarily take these models as a recommendation for your own work!

Setting up PyXSPEC#

Firstly, we configure how PyXSPEC is going to behave. This includes:

  • Setting the ‘chatter’ to zero, to minimize the outputs that XSPEC prints. Note that, at the time of writing, there are some PyXSPEC outputs that cannot be suppressed.

  • Telling PyXSPEC to use the Cash statistic; generally considered a good choice for low-count spectra.

  • Making sure that PyXSPEC won’t ask us for input at any point (xs.Fit.query = "no").

# The strange comment on the end of this line is for the benefit of our
#  automated code-checking processes. You shouldn't import modules anywhere but
#  the top of your file, but this is unfortunately necessary at the moment
import xspec as xs  # noqa: E402

# Limits the amount of output from XSPEC that PyXspec will display
xs.Xset.chatter = 0

# Other xspec settings
xs.Plot.area = True
xs.Plot.xAxis = "keV"
xs.Plot.background = True
xs.Fit.statMethod = "cstat"
xs.Fit.query = "no"
xs.Fit.nIterations = 500

We also define a variable that will control whether warnings of our own creation are displayed; see the code cell in the next section. By default, we will not display those warnings, but the possible problems they describe will still be dealt with:

show_warn = False

Loading spectra and fitting models#

This section contains the entirety of our interaction with PyXSPEC in this notebook; the slightly ugly for loop below will load each spectrum individually, restrict the energy range, fit models, and create the visualization data (though it won’t plot it).

What we’re doing here represents a fairly simple use of PyXSPEC; some of our other demonstrations, such as ‘Getting started with Swift-XRT, contain more complex examples; the simultaneous fitting of a model to multiple spectra, for instance.

The most important steps are:

  1. Once a spectrum is loaded, we restrict our analysis to data points between 0.11–2.02 keV, also excluding any marked as ‘bad’ by ftgrouppha.

  2. Plotting information for the data is then generated and stored for later.

  3. We move on to model fitting only if \(>2\) channels are valid (very low SNR spectra may have one or two); having the same number of channels (or fewer) as there are model parameters would mean an invalid fit.

  4. Looping through models (power law and blackbody in this case), they are fit to the data (using default starting parameter values), parameter errors and then model fluxes are calculated, and the results are stored in dictionaries.

  5. Plotting information for the models is generated and stored for later.

  6. Finally, the dictionaries of model parameters, uncertainties, and fluxes for each source are combined into Pandas DataFrames, for easier visualization, interaction, and saving.

The low SNR of some of these spectra will almost inevitably lead to poor fits in some cases, or cause trouble calculating model parameter uncertainties. We keep an eye out for the latter case especially, as we can use a string returned by XSPEC’s error command to check if it has flagged any potential problems with the uncertainties.

We can display a warning when this happens (the visibility of which is controlled by the show_warn variable defined in the last section), but we always write a boolean value to the storage dictionaries indicating if there were problems with a particular model parameter’s uncertainty calculation.

spec_plot_data = {}
fit_plot_data = {}
fit_parameters = {}
fit_fluxes = {}

# Iterating through all the ObsIDs
with tqdm(desc="PyXspec - loading RASS spectra", total=len(grp_spec_paths)) as onwards:
    for gsp_ind, cur_name_spec in enumerate(grp_spec_paths.items()):
        cur_src_name, cur_grp_spec = cur_name_spec

        xs.AllData.clear()
        xs.AllModels.clear()

        with contextlib.chdir(os.path.dirname(cur_grp_spec)):
            xs.AllData(cur_grp_spec)
            spec = xs.AllData(1)

        spec.ignore("**-0.11 2.02-**")
        xs.AllData.ignore("bad")

        num_chan_noticed = len(xs.AllData(1).noticed)

        xs.Plot()
        spec_plot_data[cur_src_name] = [
            xs.Plot.x(1),
            xs.Plot.xErr(1),
            xs.Plot.y(1),
            xs.Plot.yErr(1),
        ]

        fit_parameters.setdefault(cur_src_name, {})
        fit_fluxes.setdefault(cur_src_name, {})
        if num_chan_noticed > 2:
            fit_plot_data.setdefault(cur_src_name, {})

            for cur_model_name in ["bbody", "powerlaw"]:

                xs.Model(cur_model_name)
                xs.Fit.perform()

                xs.Plot()
                fit_plot_data[cur_src_name][cur_model_name] = xs.Plot.model(1)

                xs.Fit.error("2.706 1 2")

                xs.AllModels.calcFlux("0.5 2.0 err")
                en_fl, en_fl_min, en_fl_max, ph_fl, ph_fl_min, ph_fl_max = spec.flux

                fit_fluxes[cur_src_name].update(
                    {
                        f"{cur_model_name}_0.5-2.0keV_flux": en_fl,
                        f"{cur_model_name}_0.5-2.0keV_flux_err-": en_fl_min,
                        f"{cur_model_name}_0.5-2.0keV_flux_err+": en_fl_max,
                    }
                )

                for cur_par_id in range(1, xs.AllModels(1).nParameters + 1):
                    cur_par_name = xs.AllModels(1)(cur_par_id).name

                    cur_par_val = xs.AllModels(1)(cur_par_id).values[0]
                    cur_par_lims_out = xs.AllModels(1)(cur_par_id).error

                    # Check the error string output by XSPEC's error command and
                    #  show a warning if there might be a problem
                    error_good = True
                    if cur_par_lims_out[2] != "FFFFFFFFF":
                        if show_warn:
                            warn(
                                f"Error calculation for the {cur_par_name} parameter "
                                f"of {cur_model_name} indicated a possible problem "
                                f"({cur_par_lims_out[2]}) [{cur_src_name}]",
                                stacklevel=2,
                            )
                        error_good = False

                    fit_parameters[cur_src_name].update(
                        {
                            f"{cur_model_name}_{cur_par_name}": cur_par_val,
                            f"{cur_model_name}_{cur_par_name}_err-": cur_par_val
                            - cur_par_lims_out[0],
                            f"{cur_model_name}_{cur_par_name}_err+": cur_par_lims_out[1]
                            - cur_par_val,
                            f"{cur_model_name}_{cur_par_name}_good_err": error_good,
                        }
                    )

        onwards.update(1)

fit_parameters = pd.DataFrame.from_dict(fit_parameters, orient="index")
fit_fluxes = pd.DataFrame.from_dict(fit_fluxes, orient="index")

Hide code cell output

PyXspec - loading RASS spectra:   0% 0/83 [00:00<?, ?it/s]

***XSPEC Error:  No variable parameters for fit 
***XSPEC Error:  No variable parameters for fit 
PyXspec - loading RASS spectra:   2% 2/83 [00:00<00:06, 11.62it/s]

***XSPEC Error:  No variable parameters for fit 
***XSPEC Error:  No variable parameters for fit 
***XSPEC Error:  No variable parameters for fit 
***XSPEC Error:  No variable parameters for fit

PyXspec - loading RASS spectra:   5% 4/83 [00:00<00:06, 12.28it/s]or:  No variable parameters for fit 
***XSPEC Error:  No variable parameters for fit

***XSPEC Error:  No variable parameters for fit 
***XSPEC Error:  No variable parameters for fit 
***XSPEC Error:  No variable parameters for fit 
***XSPEC Error:  No variable parameters for fit 
***XSPEC Error:  No variable parameters for fit
PyXspec - loading RASS spectra:   7% 6/83 [00:00<00:06, 11.57it/s]

***XSPEC Error:  No variable parameters for fit 
***XSPEC Error:  No variable parameters for fit 
***XSPEC Error:  No variable parameters for fit 
***XSPEC Error:  No variable parameters for fit 
***XSPEC Erro
r:  No variable parameters for fit 
***XSPEC Error:  No variable parameters for fit 
***XSPEC Error:  No variable parameters for fit 
***XSPEC Error:  No variable parameters for fit 
***XSPEC Error:  No variable parameters for fit 
***XSPEC Error:  No variable parameters for fit
PyXspec - loading RASS spectra:  10% 8/83 [00:00<00:07, 10.53it/s]

***XSPEC Error:  No variable parameters for fit 
PyXspec - loading RASS spectra:  12% 10/83 [00:00<00:06, 10.98it/s]

***XSPEC Error:  No variable parameters for fit 
***XSPEC Error:  No variable parameters for fit 
***XSPEC Error:  No variable parameters for fit 
***XSPEC Error:  No variable parameters for fit

***XSPEC Error:  No variable parameters for fit
PyXspec - loading RASS spectra:  14% 12/83 [00:01<00:06, 10.52it/s]
***XSPEC Error:  No variable parameters for fit

***XSPEC Error:  No variable parameters for fit 
***XSPEC Error:  No variable parameters for fit 
***XSPEC Error:  No variable parameters for fit

PyXspec - loading RASS spectra:  17% 14/83 [00:01<00:06, 11.33it/s]
:  No variable parameters for fit

PyXspec - loading RASS spectra:  19% 16/83 [00:01<00:05, 12.13it/s]
***XSPEC Error:  No variable parameters for fit

***XSPEC Error:  No variable parameters for fit 
***XSPEC Error:  No variable parameters for fit
PyXspec - loading RASS spectra:  22% 18/83 [00:01<00:05, 12.59it/s]
PyXspec - loading RASS spectra:  24% 20/83 [00:01<00:04, 13.07it/s]

***XSPEC Error:  No variable parameters for fit 
***XSPEC Error:  No variable parameters for fit 
***XSPEC Error:  No variable parameters for fit 
***XSPEC Error:  No variable parameters for fit 
***XSPEC Error:  No variable parameters for fit 
***XSPEC Error:  No variable parameters for fit

PyXspec - loading RASS spectra:  27% 22/83 [00:01<00:05, 10.38it/s]

***XSPEC Error:  No variable parameters for fit 
***XSPEC Error:  No variable parameters for fit

***XSPEC Error:  No variable parameters for fit 
***XSPEC Error:  No variable parameters for fit 
***XSPEC Error:  No variable parameters for fit 
***XSPEC Error:  No variable parameters for fit
PyXspec - loading RASS spectra:  29% 24/83 [00:02<00:05, 10.11it/s]

PyXspec - loading RASS spectra:  31% 26/83 [00:02<00:05, 10.88it/s]
PyXspec - loading RASS spectra:  34% 28/83 [00:02<00:04, 11.18it/s]

***XSPEC Error:  No variable parameters for fit 
***XSPEC Error:  No variable parameters for fit 
***XSPEC Error:  No variable parameters for fit 
PyXspec - loading RASS spectra:  36% 30/83 [00:02<00:04, 10.96it/s]
PyXspec - loading RASS spectra:  39% 32/83 [00:02<00:04, 11.63it/s]

***XSPEC Error:  No variable parameters for fit 
***XSPEC Error:  No variable parameters for fit 
***XSPEC Error:  No variable parameters for fit 
***XSPEC Error:  No variable parameters for fit 
***XSPEC Error:  No variable parameters for fit 
PyXspec - loading RASS spectra:  41% 34/83 [00:03<00:04, 11.42it/s]

***XSPEC Error:  No variable parameters for fit

***XSPEC Error:
PyXspec - loading RASS spectra:  43% 36/83 [00:03<00:04, 11.43it/s]  No variable parameters for fit 
***XSPEC Error:  No variable parameters for fit 
***XSPEC Error:  No variable parameters for fit

***XSPEC Error:  No variable parameters for fit 
***XSPEC Error:  No variable parameters for fit 
***XSPEC Error:  No variable parameters for fit 
***XSPEC Error:  No variable parameters for fit 
***XSPEC Error:  No variable parameters for fit 
***XSPEC Error:  No variable parameters for fit 
PyXspec - loading RASS spectra:  46% 38/83 [00:03<00:04, 10.99it/s]**XSPEC Error:  No variable parameters for fit 
***XSPEC Error:  No variable parameters for fit

PyXspec - loading RASS spectra:  48% 40/83 [00:03<00:04, 10.66it/s]
Error:  No variable parameters for fit 
***XSPEC Error:  No variable parameters for fit 
PyXspec - loading RASS spectra:  51% 42/83 [00:03<00:03, 11.09it/s]C Error:  No variable parameters for fit 
***XSPEC Error:  No variable parameters for fit 
***XSPEC Error:  No variable parameters for fit

***XSP
EC Error:  No variable parameters for fit 
***XSPEC Error:  No variable parameters for fit 
***XSPEC Error:  No variable parameters for fit 
PyXspec - loading RASS spectra:  53% 44/83 [00:03<00:03, 10.82it/s]
***XSP
EC Error:  No variable parameters for fit

PyXspec - loading RASS spectra:  55% 46/83 [00:04<00:03, 11.11it/s]EC Error:  No variable parameters for fit 
***XSPEC Error:  No variable parameters for fit 
***XSPEC Error:  No variable parameters for fit
PyXspec - loading RASS spectra:  58% 48/83 [00:04<00:02, 12.02it/s]

PyXspec - loading RASS spectra:  60% 50/83 [00:04<00:02, 11.46it/s]
ror:  No variable parameters for fit 
***XSPEC Error:  No variable parameters for fit 
***XSPEC Error:  No variable parameters for fit 
***XSPEC Error:  No variable parameters for fit

***XSPEC Error:  No variable parameters for fit 
***XSPEC Error:  No variable parameters for fit 
PyXspec - loading RASS spectra:  63% 52/83 [00:04<00:02, 11.84it/s]
***XSPEC Error:  No variable parameters for fit 
***XSPEC Error:  No variable parameters for fit

***XSPEC Erro
r:  No variable parameters for fit 
***XSPEC Error:  No variable parameters for fit 
PyXspec - loading RASS spectra:  65% 54/83 [00:04<00:02, 11.39it/s]

***XSPEC Error:  No variable parameters for fit 
***XSPEC Error:  No variable parameters for fit

PyXspec - loading RASS spectra:  67% 56/83 [00:04<00:02, 10.99it/s]rror:  No variable parameters for fit 
***XSPEC Error:  No variable parameters for fit 
***XSPEC Error:  No variable parameters for fit

PyXspec - loading RASS spectra:  70% 58/83 [00:05<00:02, 11.19it/s]

***XSPEC Error:  No variable parameters for fit 
***XSPEC Error:  No variable parameters for fit 
PyXspec - loading RASS spectra:  72% 60/83 [00:05<00:02, 10.52it/s]

***XSPEC Error:  No variable parameters for fit 
PyXspec - loading RASS spectra:  75% 62/83 [00:05<00:02, 10.33it/s]
***XSPEC Error:  No variable parameters for fit

***XSPEC Error:  No variable parameters for fit 
***XSPEC Error:  No variable parameters for fit 
***XSPEC Error:  No variable parameters for fit 
***XSPEC Error:  No variable parameters for fit

***XSPEC Error:  No variable parameters for fit 
***XSPEC Error:  No variable parameters for fit 
***XSPEC Error:  No variable parameters for fit 
***XSPEC Error:  No variable parameters for fit 
***XSPEC Error:  No variable parameters for fit 
***XSPEC Error:  No variable parameters for fit 
PyXspec - loading RASS spectra:  77% 64/83 [00:05<00:02,  8.07it/s]

***XSPEC Error:  No variable parameters for fit 
PyXspec - loading RASS spectra:  80% 66/83 [00:06<00:01,  9.20it/s]

PyXspec - loading RASS spectra:  82% 68/83 [00:06<00:01,  9.80it/s]ariable parameters for fit 
***XSPEC Error:  No variable parameters for fit 
***XSPEC Error:  No variable parameters for fit 
***XSPEC Error:  No variable parameters for fit 
***XSPEC Error:  No variable parameters for fit 
***XSPEC Error:  No variable parameters for fit
PyXspec - loading RASS spectra:  84% 70/83 [00:06<00:01, 10.78it/s]

***XSPEC Error:  No variable parameters for fit

***XSPEC Error:  No variable parameters for fit 
***XSPEC Error:  No variable parameters for fit 
***XSPEC Error:  No variable parameters for fit 
***XSPEC Error:  No variable parameters for fit 
***XSPEC Error:  No variable parameters for fit 
***XSPEC Error:  No variable parameters for fit 
PyXspec - loading RASS spectra:  87% 72/83 [00:06<00:01, 10.74it/s]

***XSPEC Error:  No variable parameters for fit

PyXspec - loading RASS spectra:  89% 74/83 [00:06<00:00, 10.38it/s]PEC Error:  No variable parameters for fit 
***XSPEC Error:  No variable parameters for fit 
***XSPEC Error:  No variable parameters for fit 
***XSPEC Error:  No variable parameters for fit 
***XSPEC Error:  No variable parameters for fit 
***XSPEC Error:  No variable parameters for fit

***XSPEC Error:  No variable parameters for fit 
***XSPEC Error:  No variable parameters for fit 
***XSPEC Error:  No variable parameters for fit 
***XSPEC Error:  No variable parameters for fit 
***XSPEC Error:  No variable parameters for fit 
***XSPEC Error:  No variable parameters for fit 
***XSPEC Error:  No variable parameters for fit 
PyXspec - loading RASS spectra:  92% 76/83 [00:06<00:00, 10.54it/s]

***XSPEC Error:  No variable parameters for fit 
***XSPEC Error:  No variable parameters for fit 
PyXspec - loading RASS spectra:  94% 78/83 [00:07<00:00, 10.57it/s]

***XSPEC Error:  No variable parameters for fit 
***XSPEC Error:  No variable parameters for fit 
***XSPEC Error:  No variable parameters for fit 
***XSPEC Error:  No variable parameters for fit 
***XSPEC Error:  No variable parameters for fit 
PyXspec - loading RASS spectra:  96% 80/83 [00:07<00:00, 10.95it/s]

***XSPEC Error:  No variable parameters for fit 
PyXspec - loading RASS spectra:  99% 82/83 [00:07<00:00, 11.69it/s]
***XSPEC Error:  No variable parameters for fit

***XSPEC Error:  No variable parameters for fit 
***XSPEC Error:  No variable parameters for fit 
***XSPEC Error:  No variable parameters for fit 
***XSPEC Error:  No variable parameters for fit 
PyXspec - loading RASS spectra: 100% 83/83 [00:07<00:00, 10.97it/s]

***XSPEC Error:  No variable parameters for fit 
***XSPEC Error:  No variable parameters for fit 
PyXspec - loading RASS spectra:   2% 2/83 [00:00<00:06, 11.62it/s]

***XSPEC Error:  No variable parameters for fit 
***XSPEC Error:  No variable parameters for fit 
***XSPEC Error:  No variable parameters for fit 
***XSPEC Error:  No variable parameters for fit

PyXspec - loading RASS spectra:   5% 4/83 [00:00<00:06, 12.28it/s]or:  No variable parameters for fit 
***XSPEC Error:  No variable parameters for fit

***XSPEC Error:  No variable parameters for fit 
***XSPEC Error:  No variable parameters for fit 
***XSPEC Error:  No variable parameters for fit 
***XSPEC Error:  No variable parameters for fit 
***XSPEC Error:  No variable parameters for fit
PyXspec - loading RASS spectra:   7% 6/83 [00:00<00:06, 11.57it/s]

***XSPEC Error:  No variable parameters for fit 
***XSPEC Error:  No variable parameters for fit 
***XSPEC Error:  No variable parameters for fit 
***XSPEC Error:  No variable parameters for fit 
***XSPEC Erro
r:  No variable parameters for fit 
***XSPEC Error:  No variable parameters for fit 
***XSPEC Error:  No variable parameters for fit 
***XSPEC Error:  No variable parameters for fit 
***XSPEC Error:  No variable parameters for fit 
***XSPEC Error:  No variable parameters for fit
PyXspec - loading RASS spectra:  10% 8/83 [00:00<00:07, 10.53it/s]

***XSPEC Error:  No variable parameters for fit 
PyXspec - loading RASS spectra:  12% 10/83 [00:00<00:06, 10.98it/s]

***XSPEC Error:  No variable parameters for fit 
***XSPEC Error:  No variable parameters for fit 
***XSPEC Error:  No variable parameters for fit 
***XSPEC Error:  No variable parameters for fit

***XSPEC Error:  No variable parameters for fit
PyXspec - loading RASS spectra:  14% 12/83 [00:01<00:06, 10.52it/s]
***XSPEC Error:  No variable parameters for fit

***XSPEC Error:  No variable parameters for fit 
***XSPEC Error:  No variable parameters for fit 
***XSPEC Error:  No variable parameters for fit

PyXspec - loading RASS spectra:  17% 14/83 [00:01<00:06, 11.33it/s]
:  No variable parameters for fit

PyXspec - loading RASS spectra:  19% 16/83 [00:01<00:05, 12.13it/s]
***XSPEC Error:  No variable parameters for fit

***XSPEC Error:  No variable parameters for fit 
***XSPEC Error:  No variable parameters for fit
PyXspec - loading RASS spectra:  22% 18/83 [00:01<00:05, 12.59it/s]
PyXspec - loading RASS spectra:  24% 20/83 [00:01<00:04, 13.07it/s]

***XSPEC Error:  No variable parameters for fit 
***XSPEC Error:  No variable parameters for fit 
***XSPEC Error:  No variable parameters for fit 
***XSPEC Error:  No variable parameters for fit 
***XSPEC Error:  No variable parameters for fit 
***XSPEC Error:  No variable parameters for fit

PyXspec - loading RASS spectra:  27% 22/83 [00:01<00:05, 10.38it/s]

***XSPEC Error:  No variable parameters for fit 
***XSPEC Error:  No variable parameters for fit

***XSPEC Error:  No variable parameters for fit 
***XSPEC Error:  No variable parameters for fit 
***XSPEC Error:  No variable parameters for fit 
***XSPEC Error:  No variable parameters for fit
PyXspec - loading RASS spectra:  29% 24/83 [00:02<00:05, 10.11it/s]

PyXspec - loading RASS spectra:  31% 26/83 [00:02<00:05, 10.88it/s]
PyXspec - loading RASS spectra:  34% 28/83 [00:02<00:04, 11.18it/s]

***XSPEC Error:  No variable parameters for fit 
***XSPEC Error:  No variable parameters for fit 
***XSPEC Error:  No variable parameters for fit 
PyXspec - loading RASS spectra:  36% 30/83 [00:02<00:04, 10.96it/s]
PyXspec - loading RASS spectra:  39% 32/83 [00:02<00:04, 11.63it/s]

***XSPEC Error:  No variable parameters for fit 
***XSPEC Error:  No variable parameters for fit 
***XSPEC Error:  No variable parameters for fit 
***XSPEC Error:  No variable parameters for fit 
***XSPEC Error:  No variable parameters for fit 
PyXspec - loading RASS spectra:  41% 34/83 [00:03<00:04, 11.42it/s]

***XSPEC Error:  No variable parameters for fit

***XSPEC Error:
PyXspec - loading RASS spectra:  43% 36/83 [00:03<00:04, 11.43it/s]  No variable parameters for fit 
***XSPEC Error:  No variable parameters for fit 
***XSPEC Error:  No variable parameters for fit

***XSPEC Error:  No variable parameters for fit 
***XSPEC Error:  No variable parameters for fit 
***XSPEC Error:  No variable parameters for fit 
***XSPEC Error:  No variable parameters for fit 
***XSPEC Error:  No variable parameters for fit 
***XSPEC Error:  No variable parameters for fit 
PyXspec - loading RASS spectra:  46% 38/83 [00:03<00:04, 10.99it/s]**XSPEC Error:  No variable parameters for fit 
***XSPEC Error:  No variable parameters for fit

PyXspec - loading RASS spectra:  48% 40/83 [00:03<00:04, 10.66it/s]
Error:  No variable parameters for fit 
***XSPEC Error:  No variable parameters for fit 
PyXspec - loading RASS spectra:  51% 42/83 [00:03<00:03, 11.09it/s]C Error:  No variable parameters for fit 
***XSPEC Error:  No variable parameters for fit 
***XSPEC Error:  No variable parameters for fit

***XSP
EC Error:  No variable parameters for fit 
***XSPEC Error:  No variable parameters for fit 
***XSPEC Error:  No variable parameters for fit 
PyXspec - loading RASS spectra:  53% 44/83 [00:03<00:03, 10.82it/s]
***XSP
EC Error:  No variable parameters for fit

PyXspec - loading RASS spectra:  55% 46/83 [00:04<00:03, 11.11it/s]EC Error:  No variable parameters for fit 
***XSPEC Error:  No variable parameters for fit 
***XSPEC Error:  No variable parameters for fit
PyXspec - loading RASS spectra:  58% 48/83 [00:04<00:02, 12.02it/s]

PyXspec - loading RASS spectra:  60% 50/83 [00:04<00:02, 11.46it/s]
ror:  No variable parameters for fit 
***XSPEC Error:  No variable parameters for fit 
***XSPEC Error:  No variable parameters for fit 
***XSPEC Error:  No variable parameters for fit

***XSPEC Error:  No variable parameters for fit 
***XSPEC Error:  No variable parameters for fit 
PyXspec - loading RASS spectra:  63% 52/83 [00:04<00:02, 11.84it/s]
***XSPEC Error:  No variable parameters for fit 
***XSPEC Error:  No variable parameters for fit

***XSPEC Erro
r:  No variable parameters for fit 
***XSPEC Error:  No variable parameters for fit 
PyXspec - loading RASS spectra:  65% 54/83 [00:04<00:02, 11.39it/s]

***XSPEC Error:  No variable parameters for fit 
***XSPEC Error:  No variable parameters for fit

PyXspec - loading RASS spectra:  67% 56/83 [00:04<00:02, 10.99it/s]rror:  No variable parameters for fit 
***XSPEC Error:  No variable parameters for fit 
***XSPEC Error:  No variable parameters for fit

PyXspec - loading RASS spectra:  70% 58/83 [00:05<00:02, 11.19it/s]

***XSPEC Error:  No variable parameters for fit 
***XSPEC Error:  No variable parameters for fit 
PyXspec - loading RASS spectra:  72% 60/83 [00:05<00:02, 10.52it/s]

***XSPEC Error:  No variable parameters for fit 
PyXspec - loading RASS spectra:  75% 62/83 [00:05<00:02, 10.33it/s]
***XSPEC Error:  No variable parameters for fit

***XSPEC Error:  No variable parameters for fit 
***XSPEC Error:  No variable parameters for fit 
***XSPEC Error:  No variable parameters for fit 
***XSPEC Error:  No variable parameters for fit

***XSPEC Error:  No variable parameters for fit 
***XSPEC Error:  No variable parameters for fit 
***XSPEC Error:  No variable parameters for fit 
***XSPEC Error:  No variable parameters for fit 
***XSPEC Error:  No variable parameters for fit 
***XSPEC Error:  No variable parameters for fit 
PyXspec - loading RASS spectra:  77% 64/83 [00:05<00:02,  8.07it/s]

***XSPEC Error:  No variable parameters for fit 
PyXspec - loading RASS spectra:  80% 66/83 [00:06<00:01,  9.20it/s]

PyXspec - loading RASS spectra:  82% 68/83 [00:06<00:01,  9.80it/s]ariable parameters for fit 
***XSPEC Error:  No variable parameters for fit 
***XSPEC Error:  No variable parameters for fit 
***XSPEC Error:  No variable parameters for fit 
***XSPEC Error:  No variable parameters for fit 
***XSPEC Error:  No variable parameters for fit
PyXspec - loading RASS spectra:  84% 70/83 [00:06<00:01, 10.78it/s]

***XSPEC Error:  No variable parameters for fit

***XSPEC Error:  No variable parameters for fit 
***XSPEC Error:  No variable parameters for fit 
***XSPEC Error:  No variable parameters for fit 
***XSPEC Error:  No variable parameters for fit 
***XSPEC Error:  No variable parameters for fit 
***XSPEC Error:  No variable parameters for fit 
PyXspec - loading RASS spectra:  87% 72/83 [00:06<00:01, 10.74it/s]

***XSPEC Error:  No variable parameters for fit

PyXspec - loading RASS spectra:  89% 74/83 [00:06<00:00, 10.38it/s]PEC Error:  No variable parameters for fit 
***XSPEC Error:  No variable parameters for fit 
***XSPEC Error:  No variable parameters for fit 
***XSPEC Error:  No variable parameters for fit 
***XSPEC Error:  No variable parameters for fit 
***XSPEC Error:  No variable parameters for fit

***XSPEC Error:  No variable parameters for fit 
***XSPEC Error:  No variable parameters for fit 
***XSPEC Error:  No variable parameters for fit 
***XSPEC Error:  No variable parameters for fit 
***XSPEC Error:  No variable parameters for fit 
***XSPEC Error:  No variable parameters for fit 
***XSPEC Error:  No variable parameters for fit 
PyXspec - loading RASS spectra:  92% 76/83 [00:06<00:00, 10.54it/s]

***XSPEC Error:  No variable parameters for fit 
***XSPEC Error:  No variable parameters for fit 
PyXspec - loading RASS spectra:  94% 78/83 [00:07<00:00, 10.57it/s]

***XSPEC Error:  No variable parameters for fit 
***XSPEC Error:  No variable parameters for fit 
***XSPEC Error:  No variable parameters for fit 
***XSPEC Error:  No variable parameters for fit 
***XSPEC Error:  No variable parameters for fit 
PyXspec - loading RASS spectra:  96% 80/83 [00:07<00:00, 10.95it/s]

***XSPEC Error:  No variable parameters for fit 
PyXspec - loading RASS spectra:  99% 82/83 [00:07<00:00, 11.69it/s]
***XSPEC Error:  No variable parameters for fit

***XSPEC Error:  No variable parameters for fit 
***XSPEC Error:  No variable parameters for fit 
***XSPEC Error:  No variable parameters for fit 
***XSPEC Error:  No variable parameters for fit 
PyXspec - loading RASS spectra: 100% 83/83 [00:07<00:00, 10.97it/s]

Note

We ignore any channels that are outside the 0.11-2.02 keV energy range, which was chosen using advice from the ROSAT-PSPC energy calibration table. Any channels that have been marked as ‘bad’ by ftgrouppha are also excluded.

Visualizing fitted spectra#

At this stage we have fitted two models to most of the spectra (those with \(<3\) valid channels were excluded). We now might want to take a look at them plotted on top of the spectrum they were fitted to (we also have the necessary data to plot those spectra with no model fits).

We set up a many-panel figure to display the fitted, background-subtracted, spectrum for each of our M dwarfs. The x-axis energy scales are consistent across all panels, and the y-axis scale is consistent across rows.

Here we set up the colors assigned to each model:

nice_model_names = {"bbody": "Blackbody", "powerlaw": "Power-law"}
nice_model_colors = {"bbody": "firebrick", "powerlaw": "teal"}

Hide code cell source

num_sps = len(grp_spec_paths)
num_cols = 2
num_rows = int(np.ceil(num_sps / num_cols))

fig_side_size = 5
width_multi = 1.4

fig, ax_arr = plt.subplots(
    ncols=num_cols,
    nrows=num_rows,
    figsize=((fig_side_size * width_multi) * num_cols, fig_side_size * num_rows),
    sharey="row",
    sharex=True,
)
plt.subplots_adjust(wspace=0.0, hspace=0.0)

ax_ind = 0
for ax_arr_ind, ax in np.ndenumerate(ax_arr):
    if ax_ind >= num_sps:
        ax.set_visible(False)
        continue

    cur_src_name, cur_seq_id = list(src_seq_ids.items())[ax_ind]
    cur_actual_name = id_name_to_actual[cur_src_name]

    cur_sp_data = spec_plot_data[cur_src_name]

    ax.minorticks_on()
    ax.tick_params(which="both", direction="in", top=True, right=True)
    ax.tick_params(which="minor", labelsize=8)

    ax.errorbar(
        cur_sp_data[0],
        cur_sp_data[2],
        xerr=cur_sp_data[1],
        yerr=cur_sp_data[3],
        fmt="kx",
        capsize=1.5,
        label="ROSAT All-Sky Data",
        lw=0.6,
        alpha=0.7,
    )

    if cur_src_name in fit_plot_data:
        for cur_model_name, cur_fit_data in fit_plot_data[cur_src_name].items():
            ax.plot(
                cur_sp_data[0],
                cur_fit_data,
                label="XSPEC " + nice_model_names[cur_model_name] + " fit",
                color=nice_model_colors[cur_model_name],
            )

    ax.legend(loc="upper right")

    ax.set_xlim(0.098, 2.08)
    ax.set_xscale("log")

    ax.xaxis.set_major_formatter(FuncFormatter(lambda inp, _: "{:g}".format(inp)))
    ax.xaxis.set_minor_formatter(FuncFormatter(lambda inp, _: "{:g}".format(inp)))

    ax.set_xlabel("Energy [keV]", fontsize=15)
    if ax_arr_ind[1] == 0:
        ax.set_ylabel(r"Spectrum [ct cm$^{-2}$ s$^{-1}$ keV$^{-1}$]", fontsize=15)

    names_title = f"{cur_src_name}/\n{cur_actual_name}"
    ax.set_title(
        names_title,
        y=0.8,
        x=0.02,
        fontsize=15,
        color="dimgrey",
        fontweight="bold",
        horizontalalignment="left",
    )

    ax_ind += 1

plt.show()
../../../_images/be93a23d358aa1a8c8209cc525f3453d6c57080dcaed8c4b17b6784b47cf241c.png

Saving PyXSPEC fit results#

If you’re fitting X-ray spectra as part of a research project, you’re probably going to want to save the properties you derived to a file, so you can use them later without re-running the analysis.

In the section where we loaded and fit the spectra, we mentioned converting the parameter storage dictionaries into Pandas DataFrame objects, one for fluxes (fit_fluxes) and another for model parameters and uncertainties (fit_parameters).

Here we combine them into a single dataframe by performing a table merge, matching the dataframe indexes (which, due to the way we created the dataframes, are the “CARMENES-{ID}” style names we assigned to each source earlier).

fit_parameters = fit_parameters.round(5)
fit_fluxes = fit_fluxes.round(16)
rass_results = pd.merge(fit_parameters, fit_fluxes, left_index=True, right_index=True)

rass_results = pd.merge(
    carm_cat.to_pandas()[["Karmn", "Name", "id_name"]],
    rass_results,
    right_index=True,
    left_on="id_name",
)

rass_results = rass_results.set_index("id_name")

The combined dataframe is then saved as a comma-separated values (CSV) file:

# Using a convenience method of the Pandas DataFrame class
rass_results.to_csv("carmenes_mdwarf_rass_properties.csv", index=True)

We can also take a peek at the first few rows, to see what information it contains:

rass_results.head(6)
Karmn Name bbody_kT bbody_kT_err- bbody_kT_err+ bbody_kT_good_err bbody_norm bbody_norm_err- bbody_norm_err+ bbody_norm_good_err ... powerlaw_norm powerlaw_norm_err- powerlaw_norm_err+ powerlaw_norm_good_err bbody_0.5-2.0keV_flux bbody_0.5-2.0keV_flux_err- bbody_0.5-2.0keV_flux_err+ powerlaw_0.5-2.0keV_flux powerlaw_0.5-2.0keV_flux_err- powerlaw_0.5-2.0keV_flux_err+
id_name
CARMENES-2 J00077+603AB G 217-032 0.11565 0.05673 0.03053 True 0.00019 0.00010 0.00009 True ... 0.00376 0.00224 0.00270 True 5.649700e-12 3.232400e-12 7.584400e-12 8.359300e-12 5.365400e-12 1.173900e-11
CARMENES-35 J00502+086 RX J0050.2+0837 0.23491 0.08101 0.81219 True 0.00014 0.00006 0.00175 True ... 0.00600 0.00306 0.00443 True 8.756700e-12 3.722600e-12 1.087490e-11 1.570520e-11 1.032890e-11 2.456970e-11
CARMENES-37 J00548+275 G 069-032 0.33519 0.27162 -0.33519 False 0.00010 0.00010 -0.00010 False ... 0.00293 0.00204 0.00272 True 6.361300e-12 0.000000e+00 8.165400e-12 8.219500e-12 3.422000e-12 1.655730e-11
CARMENES-63 J01562+001 RX J0156.2+0006 8.70989 8.70989 -8.70989 False 0.36867 0.36867 341.85858 False ... 0.00403 0.00300 0.00347 True 1.730010e-11 0.000000e+00 4.007000e-13 9.360400e-12 6.027800e-12 1.427760e-11
CARMENES-64 J01567+305 Koenigstuhl 4A 0.13833 0.13833 -0.13833 False 0.00011 0.00003 0.00009 False ... 0.00430 0.00430 0.00692 False 4.280000e-12 0.000000e+00 3.929000e-13 1.002590e-11 3.223800e-12 1.753530e-11
CARMENES-67 J02002+130 TZ Ari 0.16316 0.04146 0.04612 True 0.00025 0.00010 0.00010 True ... 0.00732 0.00356 0.00482 True 1.245000e-11 7.935700e-12 1.522280e-11 1.687610e-11 1.005530e-11 2.388580e-11

6 rows × 24 columns

Briefly examining the fit results#

The dataframe of fit results can very easily be leveraged to examine the X-ray property distributions of the CARMENES M dwarf sample. One possible place to start is to examine the distributions of fitted model parameter values.

We’ve not taken a very rigorous approach to fitting and extracting these parameters; if you’re working on a ‘real’ project, you might want to take more care. That could include:

  • Setting reasonable starting values for the model parameters in the fitting section.

  • Extracting, saving, and examining goodness-of-fit information for each fit.

  • Checking the background spectrum regions for contaminating sources when defining them.

Here all we’re going to do is check for obviously unphysical parameter values, as well as excluding any that were flagged as having potentially problematic uncertainties at the fitting stage.

Blackbody temperature#

The blackbody temperatures of our M dwarfs might be quite interesting, so we’ll make a quick histogram to check them out!

To hopefully ensure we only include values from successful fits, we’re going to filter our dataframe.

We’re first going to make a boolean numpy array (sel_posi) that we will use as a mask to select only the rows where both the blackbody temperature, and both of its uncertainties, are positive (to avoid any unphysical results).

The > 0 check on the three columns we retrieve from the dataframe will produce a new dataframe with those same column names but the values will have been replaced by True or False, depending on whether the entry was positive or not.

That information is retrieved as a numpy array (using the .values property), and the .all(axis=1) call will return a 1D array of booleans, one entry per row, indicating whether all three column values were positive.

We then use the already-boolean “bbody_kT_good_err” column as another mask, to make sure that we don’t include any data points that had possibly problematic uncertainties:

res_bbtx_cut = rass_results.copy()

sel_posi = (
    res_bbtx_cut[["bbody_kT", "bbody_kT_err-", "bbody_kT_err+"]] > 0
).values.all(axis=1)
res_bbtx_cut = res_bbtx_cut[sel_posi]

res_bbtx_cut = res_bbtx_cut[res_bbtx_cut["bbody_kT_good_err"]]
res_bbtx_cut.info()
<class 'pandas.DataFrame'>
Index: 48 entries, CARMENES-2 to CARMENES-744
Data columns (total 24 columns):
 #   Column                         Non-Null Count  Dtype  
---  ------                         --------------  -----  
 0   Karmn                          48 non-null     str    
 1   Name                           48 non-null     str    
 2   bbody_kT                       48 non-null     float64
 3   bbody_kT_err-                  48 non-null     float64
 4   bbody_kT_err+                  48 non-null     float64
 5   bbody_kT_good_err              48 non-null     bool   
 6   bbody_norm                     48 non-null     float64
 7   bbody_norm_err-                48 non-null     float64
 8   bbody_norm_err+                48 non-null     float64
 9   bbody_norm_good_err            48 non-null     bool   
 10  powerlaw_PhoIndex              48 non-null     float64
 11  powerlaw_PhoIndex_err-         48 non-null     float64
 12  powerlaw_PhoIndex_err+         48 non-null     float64
 13  powerlaw_PhoIndex_good_err     48 non-null     bool   
 14  powerlaw_norm                  48 non-null     float64
 15  powerlaw_norm_err-             48 non-null     float64
 16  powerlaw_norm_err+             48 non-null     float64
 17  powerlaw_norm_good_err         48 non-null     bool   
 18  bbody_0.5-2.0keV_flux          48 non-null     float64
 19  bbody_0.5-2.0keV_flux_err-     48 non-null     float64
 20  bbody_0.5-2.0keV_flux_err+     48 non-null     float64
 21  powerlaw_0.5-2.0keV_flux       48 non-null     float64
 22  powerlaw_0.5-2.0keV_flux_err-  48 non-null     float64
 23  powerlaw_0.5-2.0keV_flux_err+  48 non-null     float64
dtypes: bool(4), float64(18), str(2)
memory usage: 9.1+ KB

Now we can use that filtered dataframe to make a histogram of blackbody temperature, which shows that most of the M dwarfs (or at least their hot outer atmospheres) have a temperature of around 0.15 keV (~1.7 million Kelvin).

That is consistent with the range of temperatures considered for M dwarfs stars in a recent census of local M dwarfs by Caramazza M. et al. (2023).

There may also be another collection of M dwarfs with slightly (but only slightly) cooler outer atmospheres, peaking around 0.12 keV (~1.4 million Kelvin):

Hide code cell source

# Create a histogram of the blackbody temperature distribution
kt_bins = np.arange(0, res_bbtx_cut["bbody_kT"].max() * 1.1, 0.0125)

plt.figure(figsize=(6, 6))

plt.minorticks_on()
plt.tick_params(which="both", direction="in", top=True, right=True)

plt.hist(
    res_bbtx_cut["bbody_kT"],
    bins=kt_bins,
    histtype="stepfilled",
    fc="seagreen",
    ec="black",
)

plt.ylabel("N", fontsize=15)
plt.xlabel(r"Blackbody $T_{\rm{X}}$ [keV]", fontsize=15)

plt.tight_layout()
plt.show()
../../../_images/e94cd4026d23f25c116f4f625e11373a8e94ca475d0da83279e50da22b604573.png

Power-law index#

We repeat the same exercise as in the last section, this time for the power-law index parameter.

Unfortunately, it’s a little harder to define an ‘unphysical’ index value than it was to define an unphysical temperature.

The maximum value of an XSPEC power law photon index (by default) is 10. If a value is in that territory, it’s usually (though not guaranteed to be) the product of a bad fit, so we exclude any rows where the index is \(>9.5\).

We also exclude rows where either of the power-law index uncertainties are negative, or where the uncertainty was flagged as potentially bad at the fitting stage:

res_plind_cut = rass_results.copy()

sel_good = (res_plind_cut["powerlaw_PhoIndex"] < 9.5).values

sel_err_posi = (
    res_plind_cut[["powerlaw_PhoIndex_err-", "powerlaw_PhoIndex_err+"]] > 0
).values.all(axis=1)

res_plind_cut = res_plind_cut[sel_good & sel_err_posi]

res_plind_cut = res_plind_cut[res_plind_cut["powerlaw_PhoIndex_good_err"]]

res_plind_cut.info()
<class 'pandas.DataFrame'>
Index: 65 entries, CARMENES-2 to CARMENES-749
Data columns (total 24 columns):
 #   Column                         Non-Null Count  Dtype  
---  ------                         --------------  -----  
 0   Karmn                          65 non-null     str    
 1   Name                           65 non-null     str    
 2   bbody_kT                       65 non-null     float64
 3   bbody_kT_err-                  65 non-null     float64
 4   bbody_kT_err+                  65 non-null     float64
 5   bbody_kT_good_err              65 non-null     bool   
 6   bbody_norm                     65 non-null     float64
 7   bbody_norm_err-                65 non-null     float64
 8   bbody_norm_err+                65 non-null     float64
 9   bbody_norm_good_err            65 non-null     bool   
 10  powerlaw_PhoIndex              65 non-null     float64
 11  powerlaw_PhoIndex_err-         65 non-null     float64
 12  powerlaw_PhoIndex_err+         65 non-null     float64
 13  powerlaw_PhoIndex_good_err     65 non-null     bool   
 14  powerlaw_norm                  65 non-null     float64
 15  powerlaw_norm_err-             65 non-null     float64
 16  powerlaw_norm_err+             65 non-null     float64
 17  powerlaw_norm_good_err         65 non-null     bool   
 18  bbody_0.5-2.0keV_flux          65 non-null     float64
 19  bbody_0.5-2.0keV_flux_err-     65 non-null     float64
 20  bbody_0.5-2.0keV_flux_err+     65 non-null     float64
 21  powerlaw_0.5-2.0keV_flux       65 non-null     float64
 22  powerlaw_0.5-2.0keV_flux_err-  65 non-null     float64
 23  powerlaw_0.5-2.0keV_flux_err+  65 non-null     float64
dtypes: bool(4), float64(18), str(2)
memory usage: 10.9+ KB

Finally, we make the histogram:

Hide code cell source

# Create a histogram of the power law photon index distribution
pho_bins = np.arange(
    res_plind_cut["powerlaw_PhoIndex"].min() * 0.9,
    res_plind_cut["powerlaw_PhoIndex"].max() * 1.1,
    0.1,
)

plt.figure(figsize=(6, 6))

plt.minorticks_on()
plt.tick_params(which="both", direction="in", top=True, right=True)

plt.hist(
    rass_results["powerlaw_PhoIndex"], bins=pho_bins, histtype="step", ec="peru", lw=1.8
)

plt.ylabel("N", fontsize=15)
plt.xlabel(r"Power-law Photon Index", fontsize=15)

plt.tight_layout()
plt.show()
../../../_images/d4abf4bc569fc3b8497d2e0997e6c9dd0d79a6a1c17f7ab6d84d79e96988fab0.png

About this notebook#

Author: David Turner, HEASARC Staff Scientist

Author: Mike Corcoran, Associate Research Professor

Updated On: 2026-03-12

Additional Resources#

Support: HEASARC Helpdesk

PyVO GitHub Repository

Latest PyVO Documentation

ROSAT-PSPC energy calibration table

Astropy regions GitHub

Astropy regions documentation

HEASoft quzcif help

HEASARC Calibration Database (CALDB) introduction

Acknowledgements#

References#

Alonso-Floriano F. J., Morales J. C., Caballero J. A., Montes D. et al. (2015) - CARMENES input catalogue of M dwarfs. I. Low-resolution spectroscopy with CAFOS

Boller T., Freyberg M.J., Trümper J. et al. (2016) - Second ROSAT all-sky survey (2RXS) source catalogue

The VizieR service DOI: 10.26093/cds/vizier

Ginsburg, Sipőcz, Brasseur et al. (2019) - astroquery: An Astronomical Web-querying Package in Python

Belloni T., Hasinger G., Izzo C. (1994) - Procedures for the interactive analysis of point sources from the ROSAT XRT/PSPC all-sky survey

Caramazza M., Stelzer B., Magaudda E., Raetz St., Güdel M., Orlando S., Poppenhäger K. (2023) - Complete X-ray census of M dwarfs in the solar neighborhood. I. GJ 745 AB: Coronal-hole stars in the 10 pc sample