Source code for rubin_sim.maf.maps.dust_map_3d

__all__ = ("DustMap3D",)

import warnings

import numpy as np

from rubin_sim.maf.maps import BaseMap
from rubin_sim.phot_utils import DustValues

from .ebv_3d_hp import ebv_3d_hp, get_x_at_nearest_y


[docs] class DustMap3D(BaseMap): """Add 3-d E(B-V) values to the slice points. See "notes" below for a discussion of the content of the map keys, and functionality that can be accessed by calling `DustMap3d.distance_at_mag` with the key values at a given slice point. Parameters ---------- nside : `int` Healpixel resolution (2^x) to read from disk. map_file : `str`, opt Path to dust map file. interp : `bool`, opt Should returned values be interpolated (True) or just nearest neighbor (False). Default True, but is ignored if 'pixels' is provided. filtername : 'str', opt Name of the filter (to match the lsst filter names in rubin_sim.photUtils.DustValues) in which to calculate dust extinction magnitudes dist_pc : `float`, opt Distance at which to precalculate the nearest ebv value (pc) d_mag : `float`, opt Calculate the maximum distance which matches this `d_mag` d_mag == m-mO (dust extinction + distance modulus) r_x : `dict` {`str`: `float`}, opt Per-filter dust extinction curve coefficients. Calculated by rubin_sim.photUtils.DustValues if "None". Notes ----- The slice point dictionary keys are expanded with the following keys: ebv3d_dists - the distances from the 3d dust map at each slice_point (in pc) `ebv3d_ebvs` - the E(B-V) values corresponding to each distance at each slice_point `ebv3d_ebv_at_<dist_pc>` - the (single) ebv value at the nearest distance to dist_pc `ebv3d_dist_at_<d_mag>` - the (single) distance value corresponding to where extinction and distance modulus combine to create a m-Mo value of d_mag, for the filter specified in filtername (in pc). Note that <dist_pc> and <d_mag> will be formatted with a single decimal place. The additional method 'distance_at_mag' can be called either with the distances and ebv values for the entire map or with the values from a single slice_point, in order to calculate the distance at which extinction and distance modulus combine to create a m-Mo value closest to 'dmag' in any filter. This is the same value as would be reported in ebv3d_dist_at_<d_mag>, but can be calculated on the fly, allowing variable filters and dmag values. """ def __init__( self, nside=64, map_file=None, interp=True, filtername="r", dist_pc=3000, d_mag=15.2, r_x=None, ): self.nside = nside self.interp = interp self.map_file = map_file self.filtername = filtername self.dist_pc = dist_pc self.d_mag = d_mag # r_x is the extinction coefficient (A_v = R_v * E(B-V) .. # A_x = r_x * E(B-V)) per filter # This is equivalent to calculating A_x # (using rubin_sim.photUtils.Sed.addDust) in each # filter and setting E(B-V) to 1 [so similar to the values # calculated in DustValues. if r_x is None: self.r_x = DustValues().r_x.copy() else: self.r_x = r_x # The values that will be added to the slicepoints self.keynames = [ "ebv3d_ebvs", "ebv3d_dists", f"ebv3d_ebv_at_{self.dist_pc:.1f}", f"ebv3d_dist_at_{self.d_mag:.1f}", ]
[docs] def run(self, slice_points): # If the slicer has nside, # it's a healpix slicer so we can read the map directly if "nside" in slice_points: if slice_points["nside"] != self.nside: warnings.warn( f"Slicer value of nside {slice_points['nside']} different " f"from map value {self.nside}, will use correct slicer value here." ) dists, ebvs = ebv_3d_hp( slice_points["nside"], pixels=slice_points["sid"], map_file=self.map_file, ) # Not a healpix slicer, # look up values based on RA,dec with possible interpolation else: dists, ebvs = ebv_3d_hp( self.nside, ra=slice_points["ra"], dec=slice_points["dec"], interp=self.interp, map_file=self.map_file, ) # Calculate the map ebv and dist values at the initialized distance dist_closest, ebv_at_dist = get_x_at_nearest_y(dists, ebvs, self.dist_pc) # Calculate the distances at which m_minus_Mo values # of 'dmag' are reached dist_dmag = self.distance_at_dmag(self.d_mag, dists, ebvs, self.filtername) slice_points["ebv3d_dists"] = dists slice_points["ebv3d_ebvs"] = ebvs slice_points[f"ebv3d_ebv_at_{self.dist_pc:.1f}"] = ebv_at_dist slice_points[f"ebv3d_dist_at_{self.d_mag:.1f}"] = dist_dmag return slice_points
[docs] def distance_at_dmag(self, dmag, dists, ebvs, filtername=None): """Calculate the distance at which a given change of magnitude would occur (including distance modulus and dust extinction). Parameters ---------- dmag : `float` The magnitude change expected. dists : `np.ndarray`, (N,) The distances corresponding to the ebv values. ebvs : `np.ndarray`, (N,) The ebv values at each distance. filtername : `str` or None The filter in which to evaluate the magnitude change. If None, uses the default filter for the map. The filter translates ebv into magnitudes of extinction. Returns ------- dist_dmag : `float` The distance at which the specified dmag occurs. """ # Provide this as a method which could be used for a single # slice_point as well as for whole map # (single slice_point means you could calculate this for any # arbitrary magnitude or filter if needed) # This method is here because some metrics require it. if filtername is None: filtername = self.filtername # calculate distance modulus for each distance dmods = 5.0 * np.log10(dists) - 5.0 # calculate dust extinction at each distance, for the filtername a_x = self.r_x[filtername] * ebvs # calculate the (m-Mo) = distmod + a_x -- combination of extinction # due to distance and dust m_minus__mo = dmods + a_x # Maximum distance for the given m-Mo (dmag) value # first do the 'within the map' closest distance m_minus__mo_at_mag, dist_closest = get_x_at_nearest_y(m_minus__mo, dists, dmag) # calculate distance modulus for an object with the maximum dust # extinction (and then the distance) if a_x.ndim == 2: dist_mods_far = dmag - a_x[:, -1] else: dist_mods_far = dmag - a_x.max() dists_far = 10.0 ** (0.2 * dist_mods_far + 1.0) # Use furthest of the two dist_dmag = np.where(dists_far > dist_closest, dists_far, dist_closest) return dist_dmag