aboutsummaryrefslogtreecommitdiffstats
path: root/fg21sim/extragalactic/pointsources/base.py
blob: 1544f5b42890566fe47ba384058455fd71e0af2d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
# Copyright (c) 2016 Zhixian MA <zxma_sjtu@qq.com>
# MIT license

"""
Simulation of point sources for 21cm signal detection

Point sources types
-------------------
1. Star forming (SF) galaxies
2. Star bursting (SB) galaxies
3. Radio quiet AGN (RQ_AGN)
4. Faranoff-Riley I (FRI)
5. Faranoff-Riley II (FRII)

References
----------
[1] Wilman et al.,
    "A semi-empirical simulation of the extragalactic radio continuum
    sky for next generation radio telescopes",
    2008, MNRAS, 388, 1335-1348.
    http://adsabs.harvard.edu/abs/2008MNRAS.388.1335W
[2] Jelic et al.,
    "Foreground simulations for the LOFAR-Epoch of Reionization
    Experiment",
    2008, MNRAS, 389, 1319-1335.
    http://adsabs.harvard.edu/abs/2008MNRAS.389.1319W
[3] Spherical uniform distribution
    https://www.jasondavies.com/maps/random-points/
"""
import os
import numpy as np
import pandas as pd
import healpy as hp

from .psparams import PixelParams


class BasePointSource:
    """
    The basic class of point sources

    Parameters
    ----------
    z: float;
        Redshift, z ~ U(0,20)
    dA: au.Mpc;
        Angular diameter distance, which is calculated according to the
        cosmology constants. In this work, it is calculated by module
        basic_params
        lumo: au.Jy;
            Luminosity at the reference frequency.
    lat: au.deg;
        The colatitude angle in the spherical coordinate system
    lon: au.deg;
        The longtitude angle in the spherical coordinate system
    area: au.sr;
        Area of the point sources, sr = rad^2

    """
    # Init

    def __init__(self, configs):
        # configures
        self.configs = configs
        # PS_list information
        self.columns = ['z', 'dA (Mpc)', 'luminosity (Jy)', 'Lat (deg)',
                        'Lon (deg)', 'Area (sr)']
        self.nCols = len(self.columns)
        self._set_configs()

    def _set_configs(self):
        """
        Load the configs and set the corresponding class attributes.
        """
        comp = "extragalactic/pointsources/"
        # common
        self.nside = self.configs.getn("common/nside")
        # resolution
        self.resolution = self.configs.getn(comp+"resolution")
        # save flag
        self.save = self.configs.getn(comp+"save")
        # Output_dir
        self.output_dir = self.configs.get_path(comp+"output_dir")

    def calc_number_density(self):
        pass

    def calc_cdf(self):
        """
        Calculate cumulative distribution functions for simulating of
        samples with corresponding reshift and luminosity.

        Parameter
        -----------
        rho_mat: np.ndarray rho(lumo,z)
            The number density matrix (joint-distribution of z and flux)
            of this type of PS.

        Returns
        -------
        cdf_z, cdf_lumo: np.ndarray
            Cumulative distribution functions of redshift and flux.
        """
        # Normalization
        rho_mat = self.rho_mat
        rho_sum = np.sum(rho_mat)
        rho_norm = rho_mat / rho_sum
        # probability distribution of redshift
        pdf_z = np.sum(rho_norm, axis=0)
        pdf_lumo = np.sum(rho_norm, axis=1)
        # Cumulative function
        cdf_z = np.zeros(pdf_z.shape)
        cdf_lumo = np.zeros(pdf_lumo.shape)
        for i in range(len(pdf_z)):
            cdf_z[i] = np.sum(pdf_z[:i])
        for i in range(len(pdf_lumo)):
            cdf_lumo[i] = np.sum(pdf_lumo[:i])

        return cdf_z, cdf_lumo

    def get_lumo_redshift(self):
        """
        Randomly generate redshif and luminosity at ref frequency using
        the CDF functions.

        Paramaters
        ----------
        df_z, cdf_lumo: np.ndarray
            Cumulative distribution functions of redshift and flux.
        zbin,lumobin: np.ndarray
            Bins of redshif and luminosity.

        Returns
        -------
        z: float
            Redshift.
        lumo: au.W/Hz/sr
            Luminosity.
         """
        # Uniformlly generate random number in interval [0,1]
        rnd_z = np.random.uniform(0, 1)
        rnd_lumo = np.random.uniform(0, 1)
        # Get redshift
        dist_z = np.abs(self.cdf_z - rnd_z)
        idx_z = np.where(dist_z == dist_z.min())
        z = self.zbin[idx_z[0]]
        # Get luminosity
        dist_lumo = np.abs(self.cdf_lumo - rnd_lumo)
        idx_lumo = np.where(dist_lumo == dist_lumo.min())
        lumo = 10 ** self.lumobin[idx_lumo[0]]

        return float(z), float(lumo)

    def gen_single_ps(self):
        """
        Generate single point source, and return its data as a list.
        """
        # Redshift and luminosity
        self.z, self.lumo = self.get_lumo_redshift()
        # angular diameter distance
        self.param = PixelParams(self.z)
        self.dA = self.param.dA
        # W/Hz/Sr to Jy
        dA = self.dA * 3.0856775814671917E+22  # Mpc to meter
        self.lumo = self.lumo / dA**2 / (10.0**-24)  # [Jy]
        # Position
        x = np.random.uniform(0, 1)
        self.lat = (np.arccos(2 * x - 1) / np.pi * 180 - 90)   # [deg]
        self.lon = np.random.uniform(0, np.pi * 2) / np.pi * 180  # [deg]
        # Area
        npix = hp.nside2npix(self.nside)
        self.area = 4 * np.pi / npix  # [sr]

        ps_list = [self.z, self.dA, self.lumo, self.lat, self.lon, self.area]
        return ps_list

    def gen_catalog(self):
        """
        Generate num_ps of point sources and save them into a csv file.
        """
        # Init
        ps_table = np.zeros((self.num_ps, self.nCols))
        for x in range(self.num_ps):
            ps_table[x, :] = self.gen_single_ps()

        # Transform into Dataframe
        self.ps_catalog = pd.DataFrame(ps_table, columns=self.columns,
                                       index=list(range(self.num_ps)))

    def save_as_csv(self):
        """Save the catalog"""
        if not os.path.exists(self.output_dir):
            os.mkdir(self.output_dir)

        pattern = "{prefix}.csv"
        filename = pattern.format(prefix=self.prefix)

        # save to csv
        if self.save:
            file_name = os.path.join(self.output_dir, filename)
            self.ps_catalog.to_csv(file_name)

        return file_name