Source code for sacc.tracers

import numpy as np
from astropy.table import Table
from .utils import (Namespace, hide_null_values,
                    remove_dict_null_values, unique_list)
import warnings

standard_quantities = Namespace('galaxy_shear',
                                'galaxy_density',
                                'galaxy_convergence',
                                'cluster_density',
                                'cmb_temperature',
                                'cmb_polarization',
                                'cmb_convergence',
                                'cmb_tSZ',
                                'cmb_kSZ',
                                'cluster_mass_count_wl',
                                'cluster_mass_count_xray',
                                'cluster_mass_count_tSZ',
                                'generic')


[docs]class BaseTracer: """ A class representing some kind of tracer of astronomical objects. Generically, SACC data points correspond to some combination of tracers for example, tomographic two-point data has two tracers for each data point, indicating the n(z) for the corresponding tomographic bin. All Tracer objects have at least a name attribute. Different subclassses have other requirements. For example, n(z) tracers require z and n(z) arrays. In general you don't need to create tracer objects yourself - the Sacc.add_tracer method will construct them for you. """ _tracer_classes = {} def __init__(self, name, **kwargs): # We encourage people to use existing quantity names, and issue a # warning if they do not to prod them in the right direction. quantity = kwargs.pop('quantity', 'generic') if quantity not in standard_quantities: warnings.warn(f"Unknown quantity {quantity}. " "If possible use a pre-defined quantity, or " "add to the list.") self.name = name self.quantity = quantity self.metadata = kwargs.pop('metadata', {}) def __init_subclass__(cls, tracer_type): cls._tracer_classes[tracer_type.lower()] = cls cls.tracer_type = tracer_type
[docs] @classmethod def make(cls, tracer_type, name, *args, **kwargs): """ Select a Tracer subclass based on tracer_type and instantiate in instance of it with the remaining arguments. Parameters ---------- tracer_type: str Must correspond to the tracer_type of a subclass name: str The name for this specific tracer. Returns ------- instance: Tracer object An instance of a Tracer subclass """ subclass = cls._tracer_classes[tracer_type.lower()] obj = subclass(name, *args, **kwargs) return obj
[docs] @classmethod def to_tables(cls, instance_list): """Convert a list of tracers to a list of astropy tables This is used when saving data to a file. This class method converts a list of tracers, each of which can instances of any subclass of BaseTracer, and turns them into a list of astropy tables, ready to be saved to disk. Some tracers generate a single table for all of the different instances, and others generate one table per instance. Parameters ---------- instance_list: list List of tracer instances Returns ------- tables: list List of astropy tables """ tables = [] for name, subcls in cls._tracer_classes.items(): tracers = [t for t in instance_list if type(t) == subcls] # If the list is empty, we don't want to append any tables. if tracers: tables += subcls.to_tables(tracers) return tables
[docs] @classmethod def from_tables(cls, table_list): """Convert a list of astropy tables into a dictionary of tracers This is used when loading data from a file. This class method takes a list of tracers, such as those read from a file, and converts them into a list of instances. It is not quite the inverse of the to_tables method, since it returns a dict instead of a list. Subclasses overrides of this method do the actual work, but should *NOT* call this parent base method. Parameters ---------- table_list: list List of astropy tables Returns ------- tracers: dict Dict mapping string names to tracer objects. """ tracers = {} # Figure out the different subclasses that are present subclass_names = unique_list(table.meta['SACCCLSS'].lower() for table in table_list) subclasses = [cls._tracer_classes[name] for name in subclass_names] # For each subclass find the tables representing that subclass. # We do it like this because we might want to represent one tracer with # multiple tables, or one table can have multiple tracers - # it depends on the tracers class and how complicated it is. for name, subcls in zip(subclass_names, subclasses): subcls_table_list = [table for table in table_list if table.meta['SACCCLSS'].lower() == name] # and ask the subclass to read from those tables. tracers.update(subcls.from_tables(subcls_table_list)) return tracers
[docs]class MiscTracer(BaseTracer, tracer_type='Misc'): """A Tracer type for miscellaneous other data points. MiscTracers do not have any attributes except for their name, so can be used for tagging external data, for example. Parameters ---------- name: str The name of the tracer """ def __init__(self, name, **kwargs): super().__init__(name, **kwargs)
[docs] @classmethod def to_tables(cls, instance_list): """Convert a list of MiscTracer instances to a astropy tables. This is used when saving data to file. All the instances are converted to a single table, which is returned in a list with one element so that it can be used in combination with the parent. You can use the parent class to_tables class method to convert a mixed list of different tracer types. You shouldn't generally need to call this method directly. Parameters ---------- instance_list: list list of MiscTracer objects Returns ------- tables: list List containing one astropy table """ metadata_cols = set() for obj in instance_list: metadata_cols.update(obj.metadata.keys()) metadata_cols = list(metadata_cols) cols = [[obj.name for obj in instance_list], [obj.quantity for obj in instance_list]] for name in metadata_cols: cols.append([obj.metadata.get(name) for obj in instance_list]) table = Table(data=cols, names=['name', 'quantity'] + metadata_cols) table.meta['SACCTYPE'] = 'tracer' table.meta['SACCCLSS'] = cls.tracer_type table.meta['EXTNAME'] = f'tracer:{cls.tracer_type}' hide_null_values(table) return [table]
[docs] @classmethod def from_tables(cls, table_list): """Convert a list of astropy table into a dictionary of MiscTracer instances. In general table_list should have a single element in, since all the MiscTracers are stored in a single table during to_tables Parameters ---------- table_list: List[astropy.table.Table] Returns ------- tracers: Dict[str: MiscTracer] """ tracers = {} for table in table_list: metadata_cols = [col for col in table.colnames if col not in ['name', 'quantity']] for row in table: name = row['name'] quantity = row['quantity'] metadata = {key: row[key] for key in metadata_cols} remove_dict_null_values(metadata) tracers[name] = cls(name, quantity=quantity, metadata=metadata) return tracers
[docs]class MapTracer(BaseTracer, tracer_type='Map'): """ A Tracer type for a sky map. Takes at least two arguments, defining the map beam. Parameters ---------- name: str The name for this specific tracer object. ell: array Array of multipole values at which the beam is defined. beam: array Beam multipoles at each value of ell. beam_extra: array Other beam-related arrays (e.g. uncertainties, principal components, alternative measurements, whatever). map_unit: str Map units (e.g. 'uK_CMB'). 'none' by default. """ def __init__(self, name, spin, ell, beam, beam_extra=None, map_unit='none', **kwargs): super().__init__(name, **kwargs) self.spin = spin self.map_unit = map_unit self.ell = np.array(ell) self.beam = np.array(beam) self.beam_extra = {} if beam_extra is None else beam_extra
[docs] @classmethod def to_tables(cls, instance_list): tables = [] for tracer in instance_list: # Beams names = ['ell', 'beam'] cols = [tracer.ell, tracer.beam] for beam_id, col in tracer.beam_extra.items(): names.append(str(beam_id)) cols.append(col) table = Table(data=cols, names=names) table.meta['SACCTYPE'] = 'tracer' table.meta['SACCCLSS'] = cls.tracer_type table.meta['SACCNAME'] = tracer.name table.meta['SACCQTTY'] = tracer.quantity extname = f'tracer:{cls.tracer_type}:{tracer.name}:beam' table.meta['EXTNAME'] = extname table.meta['MAP_UNIT'] = tracer.map_unit table.meta['SPIN'] = tracer.spin for key, value in tracer.metadata.items(): table.meta['META_'+key] = value remove_dict_null_values(table.meta) tables.append(table) return tables
[docs] @classmethod def from_tables(cls, table_list): tracers = {} # Collect beam and bandpass tables describing the same tracer tr_tables = {} for table in table_list: # Read name and table type name = table.meta['SACCNAME'] quantity = table.meta.get('SACCQTTY', 'generic') tabtyp = table.meta['EXTNAME'].split(':')[-1] if tabtyp not in ['beam']: raise KeyError("Unknown table type " + table.meta['EXTNAME']) # If not present yet, create new tracer entry if name not in tr_tables: tr_tables[name] = {} # Add table tr_tables[name][tabtyp] = table # Now loop through different tracers and build them from their tables for n, dt in tr_tables.items(): quantity = [] metadata = {} map_unit = 'none' ell = [] beam = [] beam_extra = {} spin = 0 if 'beam' in dt: table = dt['beam'] name = table.meta['SACCNAME'] quantity = table.meta.get('SACCQTTY', 'generic') ell = table['ell'] beam = table['beam'] for col in table.columns.values(): if col.name not in ['ell', 'beam']: beam_extra[col.name] = col.data map_unit = table.meta['MAP_UNIT'] spin = table.meta['SPIN'] for key, value in table.meta.items(): if key.startswith("META_"): metadata[key[5:]] = value tracers[name] = cls(name, spin, ell, beam, quantity=quantity, beam_extra=beam_extra, map_unit=map_unit, metadata=metadata) return tracers
[docs]class NuMapTracer(BaseTracer, tracer_type='NuMap'): """ A Tracer type for a sky map at a given frequency. Takes at least four arguments, defining the bandpass and beam. Parameters ---------- name: str The name for this specific tracer, e.g. a frequency band identifier. spin: int Spin for this observable. Either 0 (e.g. intensity) or 2 (e.g. polarization). nu: array Array of frequencies. bandpass: array Bandpass transmission. bandpass_extra: array Other bandpass-related arrays (e.g. uncertainties, principal components, alternative measurements, whatever). ell: array Array of multipole values at which the beam is defined. beam: array Beam. beam_extra: array Other beam-related arrays (e.g. uncertainties, principal components, alternative measurements, whatever). nu_unit: str Frequency units ('GHz' by default). map_unit: str Map units (e.g. 'uK_CMB'). 'none' by default. """ def __init__(self, name, spin, nu, bandpass, ell, beam, bandpass_extra=None, beam_extra=None, nu_unit='GHz', map_unit='none', **kwargs): super().__init__(name, **kwargs) self.spin = spin self.nu = np.array(nu) self.nu_unit = nu_unit self.map_unit = map_unit self.bandpass = np.array(bandpass) self.bandpass_extra = {} if bandpass_extra is None else bandpass_extra self.ell = np.array(ell) self.beam = np.array(beam) self.beam_extra = {} if beam_extra is None else beam_extra
[docs] @classmethod def to_tables(cls, instance_list): tables = [] for tracer in instance_list: # Bandpasses names = ['nu', 'bandpass'] cols = [tracer.nu, tracer.bandpass] for bandpass_id, col in tracer.bandpass_extra.items(): names.append(str(bandpass_id)) cols.append(col) table = Table(data=cols, names=names) table.meta['SACCTYPE'] = 'tracer' table.meta['SACCCLSS'] = cls.tracer_type table.meta['SACCNAME'] = tracer.name table.meta['SACCQTTY'] = tracer.quantity extname = f'tracer:{cls.tracer_type}:{tracer.name}:bandpass' table.meta['EXTNAME'] = extname table.meta['NU_UNIT'] = tracer.nu_unit table.meta['SPIN'] = tracer.spin for key, value in tracer.metadata.items(): table.meta['META_'+key] = value remove_dict_null_values(table.meta) tables.append(table) # Beams names = ['ell', 'beam'] cols = [tracer.ell, tracer.beam] for beam_id, col in tracer.beam_extra.items(): names.append(str(beam_id)) cols.append(col) table = Table(data=cols, names=names) table.meta['SACCTYPE'] = 'tracer' table.meta['SACCCLSS'] = cls.tracer_type table.meta['SACCNAME'] = tracer.name table.meta['SACCQTTY'] = tracer.quantity extname = f'tracer:{cls.tracer_type}:{tracer.name}:beam' table.meta['EXTNAME'] = extname table.meta['MAP_UNIT'] = tracer.map_unit table.meta['SPIN'] = tracer.spin for key, value in tracer.metadata.items(): table.meta['META_'+key] = value remove_dict_null_values(table.meta) tables.append(table) return tables
[docs] @classmethod def from_tables(cls, table_list): tracers = {} # Collect beam and bandpass tables describing the same tracer tr_tables = {} for table in table_list: # Read name and table type name = table.meta['SACCNAME'] quantity = table.meta.get('SACCQTTY', 'generic') tabtyp = table.meta['EXTNAME'].split(':')[-1] if tabtyp not in ['bandpass', 'beam']: raise KeyError("Unknown table type " + table.meta['EXTNAME']) # If not present yet, create new tracer entry if name not in tr_tables: tr_tables[name] = {} # Add table tr_tables[name][tabtyp] = table # Now loop through different tracers and build them from their tables for n, dt in tr_tables.items(): quantity = [] metadata = {} nu = [] bandpass = [] bandpass_extra = {} nu_unit = 'GHz' map_unit = 'none' ell = [] beam = [] beam_extra = {} spin = 0 if 'bandpass' in dt: table = dt['bandpass'] name = table.meta['SACCNAME'] quantity = table.meta.get('SACCQTTY', 'generic') nu = table['nu'] bandpass = table['bandpass'] for col in table.columns.values(): if col.name not in ['nu', 'bandpass']: bandpass_extra[col.name] = col.data nu_unit = table.meta['NU_UNIT'] spin = table.meta['SPIN'] for key, value in table.meta.items(): if key.startswith("META_"): metadata[key[5:]] = value if 'beam' in dt: table = dt['beam'] name = table.meta['SACCNAME'] quantity = table.meta.get('SACCQTTY', 'generic') ell = table['ell'] beam = table['beam'] for col in table.columns.values(): if col.name not in ['ell', 'beam']: beam_extra[col.name] = col.data map_unit = table.meta['MAP_UNIT'] spin = table.meta['SPIN'] for key, value in table.meta.items(): if key.startswith("META_"): metadata[key[5:]] = value tracers[name] = cls(name, spin, nu, bandpass, ell, beam, quantity=quantity, bandpass_extra=bandpass_extra, beam_extra=beam_extra, map_unit=map_unit, nu_unit=nu_unit, metadata=metadata) return tracers
[docs]class NZTracer(BaseTracer, tracer_type='NZ'): """ A Tracer type for tomographic n(z) data. Takes two arguments arrays of z and n(z) Parameters ---------- name: str The name for this specific tracer, e.g. a tomographic bin identifier. z: array Redshift sample values nz: array Number density n(z) at redshift sample points. extra_columns: dict[str: array] or dict[int: array] Additional estimates of the same n(z), by name """ def __init__(self, name, z, nz, extra_columns=None, **kwargs): """ Create a tracer corresponding to a distribution in redshift n(z), for example of galaxies. Parameters ---------- name: str The name for this specific tracer, e.g. a tomographic bin identifier. z: array Redshift sample values nz: array Number density n(z) at redshift sample points. extra_columns: dict[str:array] Optional, default=None. Additional realizations or estimates of the same n(z), by name. Returns ------- instance: NZTracer object An instance of this class """ super().__init__(name, **kwargs) self.z = np.array(z) self.nz = np.array(nz) self.extra_columns = {} if extra_columns is None else extra_columns
[docs] @classmethod def to_tables(cls, instance_list): """Convert a list of NZTracers to a list of astropy tables This is used when saving data to a file. One table is generated per tracer. Parameters ---------- instance_list: list List of tracer instances Returns ------- tables: list List of astropy tables """ tables = [] for tracer in instance_list: names = ['z', 'nz'] cols = [tracer.z, tracer.nz] for nz_id, col in tracer.extra_columns.items(): names.append(str(nz_id)) cols.append(col) table = Table(data=cols, names=names) table.meta['SACCTYPE'] = 'tracer' table.meta['SACCCLSS'] = cls.tracer_type table.meta['SACCNAME'] = tracer.name table.meta['SACCQTTY'] = tracer.quantity table.meta['EXTNAME'] = f'tracer:{cls.tracer_type}:{tracer.name}' for key, value in tracer.metadata.items(): table.meta['META_'+key] = value remove_dict_null_values(table.meta) tables.append(table) return tables
[docs] @classmethod def from_tables(cls, table_list): """Convert an astropy table into a dictionary of tracers This is used when loading data from a file. A single tracer object is read from the table. Parameters ---------- table_list: list[astropy.table.Table] Must contain the appropriate data, for example as saved by to_table. Returns ------- tracers: dict Dict mapping string names to tracer objects. Only contains one key/value pair for the one tracer. """ tracers = {} for table in table_list: name = table.meta['SACCNAME'] quantity = table.meta.get('SACCQTTY', 'generic') z = table['z'] nz = table['nz'] extra_columns = {} for col in table.columns.values(): if col.name not in ['z', 'nz']: extra_columns[col.name] = col.data metadata = {} for key, value in table.meta.items(): if key.startswith("META_"): metadata[key[5:]] = value tracers[name] = cls(name, z, nz, quantity=quantity, extra_columns=extra_columns, metadata=metadata) return tracers
[docs]class QPNZTracer(BaseTracer, tracer_type='QPNZ'): """ A Tracer type for tomographic n(z) data preresented as a `qp.Ensemble` Takes a `qp.Ensemble` Parameters ---------- name: str The name for this specific tracer, e.g. a tomographic bin identifier. ensemble: qp.Ensemble The qp.ensemble in questions """ def __init__(self, name, ens, **kwargs): """ Create a tracer corresponding to a distribution in redshift n(z), for example of galaxies. Parameters ---------- name: str The name for this specific tracer, e.g. a tomographic bin identifier. ensemble: qp.Ensemble The qp.ensemble in questions Returns ------- instance: NZTracer object An instance of this class """ super().__init__(name, **kwargs) self.ensemble = ens
[docs] @classmethod def to_tables(cls, instance_list): """Convert a list of NZTracers to a list of astropy tables This is used when saving data to a file. Two or three tables are generated per tracer. Parameters ---------- instance_list: list List of tracer instances Returns ------- tables: list List of astropy tables """ from tables_io.convUtils import convertToApTables tables = [] for tracer in instance_list: table_dict = tracer.ensemble.build_tables() ap_tables = convertToApTables(table_dict) data_table = ap_tables['data'] meta_table = ap_tables['meta'] ancil_table = ap_tables.get('ancil', None) meta_table.meta['SACCTYPE'] = 'tracer' meta_table.meta['SACCCLSS'] = cls.tracer_type meta_table.meta['SACCNAME'] = tracer.name meta_table.meta['SACCQTTY'] = tracer.quantity meta_table.meta['EXTNAME'] = f'tracer:{cls.tracer_type}:{tracer.name}:meta' data_table.meta['SACCTYPE'] = 'tracer' data_table.meta['SACCCLSS'] = cls.tracer_type data_table.meta['SACCNAME'] = tracer.name data_table.meta['SACCQTTY'] = tracer.quantity data_table.meta['EXTNAME'] = f'tracer:{cls.tracer_type}:{tracer.name}:data' for kk, vv in tracer.metadata.items(): meta_table.meta['META_'+kk] = vv tables.append(data_table) tables.append(meta_table) if ancil_table: ancil_table.meta['SACCTYPE'] = 'tracer' ancil_table.meta['SACCCLSS'] = cls.tracer_type ancil_table.meta['SACCNAME'] = tracer.name ancil_table.meta['SACCQTTY'] = tracer.quantity ancil_table.meta['EXTNAME'] = f'tracer:{cls.tracer_type}:{tracer.name}:ancil' tables.append(ancil_table) return tables
[docs] @classmethod def from_tables(cls, table_list): """Convert an astropy table into a dictionary of tracers This is used when loading data from a file. A single tracer object is read from the table. Parameters ---------- table_list: list[astropy.table.Table] Must contain the appropriate data, for example as saved by to_table. Returns ------- tracers: dict Dict mapping string names to tracer objects. Only contains one key/value pair for the one tracer. """ import qp tracers = {} sorted_dict = {} for table_ in table_list: tokens = table_.meta['EXTNAME'].split(':') table_key = f'{tokens[0]}:{tokens[1]}:{tokens[2]}' table_type = f'{tokens[3]}' if table_key not in sorted_dict: sorted_dict[table_key] = {table_type: table_} else: sorted_dict[table_key][table_type] = table_ for key, val in sorted_dict.items(): meta_table = val['meta'] ensemble = qp.from_tables(val) name = meta_table.meta['SACCNAME'] quantity = meta_table.meta.get('SACCQTTY', 'generic') ensemble = qp.from_tables(val) metadata = {} for key, value in meta_table.meta.items(): if key.startswith("META_"): metadata[key[5:]] = value tracers[name] = cls(name, ensemble, quantity=quantity, metadata=metadata) return tracers
[docs]class BinZTracer(BaseTracer, tracer_type="bin_z"): # type: ignore """A tracer for a single redshift bin. The tracer shall be used for binned data where we want a desired quantity per interval of redshift, such that we only need the data for a given interval instead of at individual redshifts.""" def __init__(self, name: str, lower: float, upper: float, **kwargs): """ Create a tracer corresponding to a single redshift bin. :param name: The name of the tracer :param lower: The lower bound of the redshift bin :param upper: The upper bound of the redshift bin """ super().__init__(name, **kwargs) self.lower = lower self.upper = upper def __eq__(self, other) -> bool: """Test for equality. If :python:`other` is not a :python:`BinZTracer`, then it is not equal to :python:`self`. Otherwise, they are equal if names, and the z-range of the bins, are equal.""" if not isinstance(other, BinZTracer): return False return ( self.name == other.name and self.lower == other.lower and self.upper == other.upper )
[docs] @classmethod def to_tables(cls, instance_list): """Convert a list of BinZTracers to a single astropy table This is used when saving data to a file. One table is generated with the information for all the tracers. :param instance_list: List of tracer instances :return: List with a single astropy table """ names = ["name", "quantity", "lower", "upper"] cols = [ [obj.name for obj in instance_list], [obj.quantity for obj in instance_list], [obj.lower for obj in instance_list], [obj.upper for obj in instance_list], ] table = Table(data=cols, names=names) table.meta["SACCTYPE"] = "tracer" table.meta["SACCCLSS"] = cls.tracer_type table.meta["EXTNAME"] = f"tracer:{cls.tracer_type}" return [table]
[docs] @classmethod def from_tables(cls, table_list): """Convert an astropy table into a dictionary of tracers This is used when loading data from a file. One tracer object is created for each "row" in each table. :param table_list: List of astropy tables :return: Dictionary of tracers """ tracers = {} for table in table_list: for row in table: name = row["name"] quantity = row["quantity"] lower = row["lower"] upper = row["upper"] tracers[name] = cls(name, quantity=quantity, lower=lower, upper=upper) return tracers
[docs]class BinLogMTracer(BaseTracer, tracer_type="bin_logM"): # type: ignore """A tracer for a single log-mass bin. The tracer shall be used for binned data where we want a desired quantity per interval of log(mass), such that we only need the data for a given interval instead of at individual masses.""" def __init__(self, name: str, lower: float, upper: float, **kwargs): """ Create a tracer corresponding to a single log-mass bin. :param name: The name of the tracer :param lower: The lower bound of the log-mass bin :param upper: The upper bound of the log-mass bin """ super().__init__(name, **kwargs) self.lower = lower self.upper = upper def __eq__(self, other) -> bool: """Test for equality. If :python:`other` is not a :python:`BinLogMTracer`, then it is not equal to :python:`self`. Otherwise, they are equal if names, and the z-range of the bins, are equal.""" if not isinstance(other, BinLogMTracer): return False return ( self.name == other.name and self.lower == other.lower and self.upper == other.upper )
[docs] @classmethod def to_tables(cls, instance_list): """Convert a list of BinLogMTracers to a single astropy table This is used when saving data to a file. One table is generated with the information for all the tracers. :param instance_list: List of tracer instances :return: List with a single astropy table """ names = ["name", "quantity", "lower", "upper"] cols = [ [obj.name for obj in instance_list], [obj.quantity for obj in instance_list], [obj.lower for obj in instance_list], [obj.upper for obj in instance_list], ] table = Table(data=cols, names=names) table.meta["SACCTYPE"] = "tracer" table.meta["SACCCLSS"] = cls.tracer_type table.meta["EXTNAME"] = f"tracer:{cls.tracer_type}" return [table]
[docs] @classmethod def from_tables(cls, table_list): """Convert an astropy table into a dictionary of tracers This is used when loading data from a file. One tracer object is created for each "row" in each table. :param table_list: List of astropy tables :return: Dictionary of tracers """ tracers = {} for table in table_list: for row in table: name = row["name"] quantity = row["quantity"] lower = row["lower"] upper = row["upper"] tracers[name] = cls(name, quantity=quantity, lower=lower, upper=upper) return tracers
[docs]class BinRichnessTracer(BaseTracer, tracer_type="bin_richness"): # type: ignore """A tracer for a single richness bin. The tracer shall be used for binned data where we want a desired quantity per interval of log(richness), such that we only need the data for a given interval instead of at individual richness.""" def __eq__(self, other) -> bool: """Test for equality. If :python:`other` is not a :python:`BinRichnessTracer`, then it is not equal to :python:`self`. Otherwise, they are equal if names and the richness-range of the bins, are equal.""" if not isinstance(other, BinRichnessTracer): return False return ( self.name == other.name and self.lower == other.lower and self.upper == other.upper ) def __init__(self, name: str, lower: float, upper: float, **kwargs): """ Create a tracer corresponding to a single richness bin. :param name: The name of the tracer :param lower: The lower bound of the richness bin in log10. :param upper: The upper bound of the richness bin in log10. """ super().__init__(name, **kwargs) self.lower = lower self.upper = upper
[docs] @classmethod def to_tables(cls, instance_list): """Convert a list of BinZTracers to a list of astropy tables This is used when saving data to a file. One table is generated with the information for all the tracers. :param instance_list: List of tracer instances :return: List with a single astropy table """ names = ["name", "quantity", "lower", "upper"] cols = [ [obj.name for obj in instance_list], [obj.quantity for obj in instance_list], [obj.lower for obj in instance_list], [obj.upper for obj in instance_list], ] table = Table(data=cols, names=names) table.meta["SACCTYPE"] = "tracer" table.meta["SACCCLSS"] = cls.tracer_type table.meta["EXTNAME"] = f"tracer:{cls.tracer_type}" return [table]
[docs] @classmethod def from_tables(cls, table_list): """Convert an astropy table into a dictionary of tracers This is used when loading data from a file. One tracer object is created for each "row" in each table. :param table_list: List of astropy tables :return: Dictionary of tracers """ tracers = {} for table in table_list: for row in table: name = row["name"] quantity = row["quantity"] lower = row["lower"] upper = row["upper"] tracers[name] = cls( name, quantity=quantity, lower=lower, upper=upper, ) return tracers
[docs]class BinRadiusTracer(BaseTracer, tracer_type="bin_radius"): # type: ignore """A tracer for a single radial bin, e.g. when dealing with cluster shear profiles. It gives the bin edges and the value of the bin "center". The latter would typically be returned by CLMM and correspond to the average radius of the galaxies in that radial bin. """ def __eq__(self, other) -> bool: """Test for equality. If :python:`other` is not a :python:`BinRadiusTracer`, then it is not equal to :python:`self`. Otherwise, they are equal if names and the r-range and centers of the bins, are equal.""" if not isinstance(other, BinRadiusTracer): return False return ( self.name == other.name and self.lower == other.lower and self.center == other.center and self.upper == other.upper ) def __init__(self, name: str, lower: float, upper: float, center: float, **kwargs): """ Create a tracer corresponding to a single radial bin. :param name: The name of the tracer :param lower: The lower bound of the radius bin :param upper: The upper bound of the radius bin :param center: The value to use if a single point-estimate is needed. Note that :python:`center` need not be the midpoint between :python:`lower` and :python:`upper`'. """ super().__init__(name, **kwargs) self.lower = lower self.upper = upper self.center = center
[docs] @classmethod def to_tables(cls, instance_list): """Convert a list of BinRadiusTracers to a single astropy table This is used when saving data to a file. One table is generated with the information for all the tracers. :param instance_list: List of tracer instances :return: List with a single astropy table """ names = ["name", "quantity", "lower", "upper", "center"] cols = [ [obj.name for obj in instance_list], [obj.quantity for obj in instance_list], [obj.lower for obj in instance_list], [obj.upper for obj in instance_list], [obj.center for obj in instance_list], ] table = Table(data=cols, names=names) table.meta["SACCTYPE"] = "tracer" table.meta["SACCCLSS"] = cls.tracer_type table.meta["EXTNAME"] = f"tracer:{cls.tracer_type}" return [table]
[docs] @classmethod def from_tables(cls, table_list): """Convert an astropy table into a dictionary of tracers This is used when loading data from a file. One tracer object is created for each "row" in each table. :param table_list: List of astropy tables :return: Dictionary of tracers """ tracers = {} for table in table_list: for row in table: name = row["name"] quantity = row["quantity"] lower = row["lower"] upper = row["upper"] center = row["center"] tracers[name] = cls( name, quantity=quantity, lower=lower, upper=upper, center=center, ) return tracers
[docs]class SurveyTracer(BaseTracer, tracer_type="survey"): # type: ignore """A tracer for the survey definition. It shall be used to filter data related to a given survey and to provide the survey sky-area of analysis.""" def __eq__(self, other) -> bool: """Test for equality. If :python:`other` is not a :python:`SurveyTracer`, then it is not equal to :python:`self`. Otherwise, they are equal if names and the sky-areas are equal.""" if not isinstance(other, SurveyTracer): return False return self.name == other.name and self.sky_area == other.sky_area def __init__(self, name: str, sky_area: float, **kwargs): """ Create a tracer corresponding to the survey definition. :param name: The name of the tracer :param sky_area: The survey's sky area in square degrees """ super().__init__(name, **kwargs) self.sky_area = sky_area
[docs] @classmethod def to_tables(cls, instance_list): """Convert a list of SurveyTracer to a list of astropy tables This is used when saving data to a file. One table is generated with the information for all the tracers. :param instance_list: List of tracer instances :return: List of astropy tables with one table """ names = ["name", "quantity", "sky_area"] cols = [ [obj.name for obj in instance_list], [obj.quantity for obj in instance_list], [obj.sky_area for obj in instance_list], ] table = Table(data=cols, names=names) table.meta["SACCTYPE"] = "tracer" table.meta["SACCCLSS"] = cls.tracer_type table.meta["EXTNAME"] = f"tracer:{cls.tracer_type}" return [table]
[docs] @classmethod def from_tables(cls, table_list): """Convert an astropy table into a dictionary of tracers This is used when loading data from a file. One tracer object is created for each "row" in each table. :param table_list: List of astropy tables :return: Dictionary of tracers """ tracers = {} for table in table_list: for row in table: name = row["name"] quantity = row["quantity"] sky_area = row["sky_area"] tracers[name] = cls( name, quantity=quantity, sky_area=sky_area, ) return tracers