aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--fg21sim/extragalactic/clusters.py167
-rw-r--r--fg21sim/galactic/snr.py2
2 files changed, 91 insertions, 78 deletions
diff --git a/fg21sim/extragalactic/clusters.py b/fg21sim/extragalactic/clusters.py
index 5abbc13..428474b 100644
--- a/fg21sim/extragalactic/clusters.py
+++ b/fg21sim/extragalactic/clusters.py
@@ -22,10 +22,11 @@ import healpy as hp
import pandas as pd
from ..sky import get_sky
+from ..utils.wcs import make_wcs
from ..utils.fits import write_fits_healpix
from ..utils.random import spherical_uniform
from ..utils.convert import Fnu_to_Tb_fast
-from ..utils.grid import make_grid_ellipse, map_grid_to_healpix
+from ..utils.grid import make_ellipse
logger = logging.getLogger(__name__)
@@ -90,7 +91,7 @@ class GalaxyClusters:
http://adsabs.harvard.edu/abs/2002ApJ...567..716R
"""
# Component name
- name = "clusters of galaxies"
+ name = "extraglalactic clusters of galaxies"
def __init__(self, configs):
self.configs = configs
@@ -103,7 +104,7 @@ class GalaxyClusters:
self.catalog_path = self.configs.get_path(comp+"/catalog")
self.catalog_outfile = self.configs.get_path(comp+"/catalog_outfile")
self.halo_fraction = self.configs.getn(comp+"/halo_fraction")
- self.resolution = self.configs.getn(comp+"/resolution") * au.arcmin
+ self.resolution = self.configs.getn(comp+"/resolution") # [ arcmin ]
self.prefix = self.configs.getn(comp+"/prefix")
self.save = self.configs.getn(comp+"/save")
self.output_dir = self.configs.get_path(comp+"/output_dir")
@@ -112,7 +113,6 @@ class GalaxyClusters:
self.use_float = self.configs.getn("output/use_float")
self.checksum = self.configs.getn("output/checksum")
self.clobber = self.configs.getn("output/clobber")
- self.nside = self.configs.getn("common/nside")
self.freq_unit = au.Unit(self.configs.getn("frequency/unit"))
# Cosmology model
self.H0 = self.configs.getn("cosmology/H0")
@@ -153,7 +153,7 @@ class GalaxyClusters:
def _save_catalog_inuse(self):
"""Save the effective/inuse clusters catalog data to a CSV file."""
if self.catalog_outfile is None:
- logger.warning("Catalog output file not set, so do NOT save.")
+ logger.warning("Catalog output file not set; skip saving.")
return
# Create directory if necessary
dirname = os.path.dirname(self.catalog_outfile)
@@ -243,7 +243,8 @@ class GalaxyClusters:
logger.info("Done add random positions for each cluster")
def _add_random_eccentricity(self):
- """Add random eccentricities for each cluster as column
+ """
+ Add random eccentricities for each cluster as column
"eccentricity" to the catalog data.
The eccentricity of a ellipse is defined as:
@@ -270,7 +271,8 @@ class GalaxyClusters:
logger.info("Done add random eccentricities to catalog")
def _add_random_rotation(self):
- """Add random rotation angles for each cluster as column "rotation"
+ """
+ Add random rotation angles for each cluster as column "rotation"
to the catalog data.
The rotation angles are uniformly distributed within [0, 360).
@@ -286,7 +288,8 @@ class GalaxyClusters:
logger.info("Done add random rotation angles to catalog")
def _calc_sizes(self):
- """Calculate the virial radii for each cluster from the masses,
+ """
+ Calculate the virial radii for each cluster from the masses,
and then calculate the elliptical angular sizes by considering
the added random eccentricities.
@@ -329,7 +332,8 @@ class GalaxyClusters:
logger.info("Done calculate the elliptical angular sizes")
def _calc_luminosity(self):
- """Calculate the radio luminosity (at 1.4 GHz) using empirical
+ """
+ Calculate the radio luminosity (at 1.4 GHz) using empirical
scaling relations.
First, calculate the X-ray luminosity L_X using the empirical
@@ -402,10 +406,11 @@ class GalaxyClusters:
self.catalog["luminosity"] = L_r
self.catalog_prop["luminosity_freq"] = 1400 * self.freq_unit
self.units["luminosity"] = au.W / au.Hz
- logger.info("Done Calculate the radio luminosity")
+ logger.info("Done calculate the radio luminosity")
def _calc_specindex(self):
- """Calculate the radio spectral indexes for each cluster.
+ """
+ Calculate the radio spectral indexes for each cluster.
Attributes
----------
@@ -418,12 +423,13 @@ class GalaxyClusters:
clusters, which may be improved by investigating more recent results.
"""
specindex = 1.2
- logger.info("Use common spectral index for all clusters: "
+ logger.info("Use same spectral index for all clusters: "
"{0}".format(specindex))
self.catalog["specindex"] = specindex
def _calc_Tb(self, luminosity, distance, specindex, frequency, size):
- """Calculate the brightness temperature at requested frequency
+ """
+ Calculate the brightness temperature at requested frequency
by assuming a power-law spectral shape.
Parameters
@@ -475,8 +481,9 @@ class GalaxyClusters:
return Tb
def _simulate_templates(self):
- """Simulate the template (HEALPix) images for each cluster, and
- cache these templates within the class.
+ """
+ Simulate the template sky images for each cluster, and cache
+ these templates within the class.
The template images/maps have values of (or approximate) ones for
these effective pixels, excluding the pixels corresponding to the
@@ -493,32 +500,38 @@ class GalaxyClusters:
Attributes
----------
templates : list
- A List containing the simulated templates for each cluster.
- Each element is a `(hpidx, hpval)` tuple with `hpidx` the
- indexes of effective HEALPix pixels (RING ordering) and `hpval`
- the values of the corresponding pixels.
- e.g., ``[ (hpidx1, hpval1), (hpidx2, hpval2), ... ]``
+ A list containing the simulated templates for each cluster/halo.
+ Each element is a `(idx, val)` tuple with `hpidx` the indexes
+ of effective image pixels and `hpval` the values of the
+ corresponding pixels.
+ e.g., ``[ (idx1, val1), (idx2, val2), ... ]``
"""
- logger.info("Simulating HEALPix templates for each cluster ...")
+ logger.info("Simulating sky templates for each cluster/halo ...")
templates = []
- resolution = self.resolution.to(au.deg).value
+ pixelsize_deg = self.resolution.to(au.deg).value
# Make sure the index is reset, therefore, the *row indexes* can be
# simply used to identify the corresponding template image.
self.catalog.reset_index(inplace=True)
# XXX/TODO: be parallel
for row in self.catalog.itertuples():
# TODO: progress bar
- center = (row.glon, row.glat)
- size = (row.size_major, row.size_minor) # already [ degree ]
- rotation = row.rotation # already [ degree ]
- grid = make_grid_ellipse(center, size, resolution, rotation)
- hpidx, hpval = map_grid_to_healpix(grid, self.nside)
- templates.append((hpidx, hpval))
+ gcenter = (row.glon, row.glat) # [ deg ]
+ radii = (int(np.ceil(row.size_major * 0.5 / pixelsize_deg)),
+ int(np.ceil(row.size_minor * 0.5 / pixelsize_deg)))
+ rmax = max(radii)
+ pcenter = (rmax, rmax)
+ image = make_ellipse(pcenter, radii, row.rotation)
+ wcs = make_wcs(center=gcenter, size=image.shape,
+ pixelsize=self.resolution,
+ frame="Galactic", projection="CAR")
+ idx, val = self.sky.reproject_from(image, wcs, squeeze=True)
+ templates.append((idx, val))
logger.info("Done simulate %d cluster templates" % len(templates))
self.templates = templates
def _simulate_single(self, data, frequency):
- """Simulate one single cluster at the specified frequency, based
+ """
+ Simulate one single cluster at the specified frequency, based
on the cached template image.
Parameters
@@ -533,30 +546,29 @@ class GalaxyClusters:
Returns
-------
- hpidx : 1D `~numpy.ndarray`
- The indexes (in RING ordering) of the effective HEALPix
- pixels for the SNR.
- hpval : 1D `~numpy.ndarray`
- The values (i.e., brightness temperature) of each HEALPix
- pixel with respect the above indexes.
+ idx : 1D `~numpy.ndarray`
+ The indexes of the effective map pixels for the single cluster.
+ val : 1D `~numpy.ndarray`
+ The values (i.e., brightness temperature) of each map pixel
+ with respect the above indexes.
See Also
--------
`self._simulate_template()` for more detailed description.
"""
- index = data.Index
- hpidx, hpval = self.templates[index]
+ idx, val = self.templates[data.Index]
# Calculate the brightness temperature
luminosity = data.luminosity
distance = data.distance
specindex = data.specindex
- size = (data.size_major, data.size_minor)
+ size = (data.size_major, data.size_minor) # [ deg ]
Tb = self._calc_Tb(luminosity, distance, specindex, frequency, size)
- hpval = hpval * Tb
- return (hpidx, hpval)
+ val = val * Tb
+ return (idx, val)
def _make_filepath(self, **kwargs):
- """Make the path of output file according to the filename pattern
+ """
+ Make the path of output file according to the filename pattern
and output directory loaded from configurations.
"""
data = {
@@ -568,12 +580,12 @@ class GalaxyClusters:
return filepath
def _make_header(self):
- """Make the header with detail information (e.g., parameters and
+ """
+ Make the header with detail information (e.g., parameters and
history) for the simulated products.
"""
header = fits.Header()
- header["COMP"] = ("Extragalactic clusters of galaxies",
- "Emission component")
+ header["COMP"] = (self.name, "Emission component")
header["UNIT"] = ("Kelvin", "Map unit")
header["CREATOR"] = (__name__, "File creator")
# TODO:
@@ -586,20 +598,17 @@ class GalaxyClusters:
self.header = header
logger.info("Created FITS header")
- def output(self, hpmap, frequency):
- """Write the simulated free-free map to disk with proper header
+ def output(self, skymap, frequency):
+ """
+ Write the simulated free-free map to disk with proper header
keywords and history.
Returns
-------
- filepath : str
- The (absolute) path to the output HEALPix map file.
+ outfile : str
+ The (absolute) path to the output sky map file.
"""
- if not os.path.exists(self.output_dir):
- os.mkdir(self.output_dir)
- logger.info("Created output dir: {0}".format(self.output_dir))
- #
- filepath = self._make_filepath(frequency=frequency)
+ outfile = self._make_filepath(frequency=frequency)
if not hasattr(self, "header"):
self._make_header()
header = self.header.copy()
@@ -609,14 +618,16 @@ class GalaxyClusters:
"File creation date"
)
if self.use_float:
- hpmap = hpmap.astype(np.float32)
- write_fits_healpix(filepath, hpmap, header=header,
- clobber=self.clobber, checksum=self.checksum)
- logger.info("Write simulated map to file: {0}".format(filepath))
- return filepath
+ skymap = skymap.astype(np.float32)
+ sky = self.sky.copy()
+ sky.data = skymap
+ sky.header = header
+ sky.write(outfile, clobber=self.clobber, checksum=self.checksum)
+ return outfile
def preprocess(self):
- """Perform the preparation procedures for the final simulations.
+ """
+ Perform the preparation procedures for the final simulations.
Attributes
----------
@@ -646,7 +657,8 @@ class GalaxyClusters:
self._preprocessed = True
def simulate_frequency(self, frequency):
- """Simulate the emission (HEALPix) map of all Galactic SNRs at
+ """
+ Simulate the sky map of all extragalactic clusters of galaxies at
the specified frequency.
Parameters
@@ -657,9 +669,9 @@ class GalaxyClusters:
Returns
-------
hpmap_f : 1D `~numpy.ndarray`
- The HEALPix map (RING ordering) at the input frequency.
+ The sky map at the input frequency.
filepath : str
- The (absolute) path to the output HEALPix file if saved,
+ The (absolute) path to the output sky file if saved,
otherwise ``None``.
See Also
@@ -670,22 +682,23 @@ class GalaxyClusters:
#
logger.info("Simulating {name} map at {freq} ({unit}) ...".format(
name=self.name, freq=frequency, unit=self.freq_unit))
- hpmap_f = np.zeros(hp.nside2npix(self.nside))
+ skymap_f = np.zeros(self.sky.shape)
# XXX/TODO: be parallel
for row in self.catalog.itertuples():
# TODO: progress bar
- hpidx, hpval = self._simulate_single(row, frequency)
- hpmap_f[hpidx] += hpval
+ index, value = self._simulate_single(row, frequency)
+ skymap_f[index] += value
#
if self.save:
- filepath = self.output(hpmap_f, frequency)
+ filepath = self.output(skymap_f, frequency)
else:
filepath = None
- return (hpmap_f, filepath)
+ return (skymap_f, filepath)
def simulate(self, frequencies):
- """Simulate the emission (HEALPix) maps of all Galactic SNRs for
- every specified frequency.
+ """
+ Simulate the sky maps of all extragalactic clusters of galaxies
+ for every specified frequency.
Parameters
----------
@@ -695,18 +708,18 @@ class GalaxyClusters:
Returns
-------
- hpmaps : list[1D `~numpy.ndarray`]
- List of HEALPix maps (in RING ordering) at each frequency.
+ skymaps : list[1D `~numpy.ndarray`]
+ List of sky maps at each frequency.
paths : list[str]
- List of (absolute) path to the output HEALPix maps.
+ List of (absolute) path to the output sky maps.
"""
- hpmaps = []
+ skymaps = []
paths = []
for f in np.array(frequencies, ndmin=1):
- hpmap_f, filepath = self.simulate_frequency(f)
- hpmaps.append(hpmap_f)
- paths.append(filepath)
- return (hpmaps, paths)
+ skymap_f, outfile = self.simulate_frequency(f)
+ skymaps.append(skymap_f)
+ paths.append(outfile)
+ return (skymaps, paths)
def postprocess(self):
"""Perform the post-simulation operations before the end."""
diff --git a/fg21sim/galactic/snr.py b/fg21sim/galactic/snr.py
index f715aa0..fdb58be 100644
--- a/fg21sim/galactic/snr.py
+++ b/fg21sim/galactic/snr.py
@@ -273,7 +273,7 @@ class SuperNovaRemnants:
``{ name1: (idx1, val1), name2: (idx2, val2), ... }``
"""
templates = {}
- logger.info("Simulate sky template for each SNR")
+ logger.info("Simulating sky template for each SNR ...")
for row in self.catalog.itertuples():
name = row.name
logger.debug("Simulate sky template for SNR: {0}".format(name))