diff options
| author | Aaron LI <aaronly.me@outlook.com> | 2016-06-23 15:05:24 +0800 | 
|---|---|---|
| committer | Aaron LI <aaronly.me@outlook.com> | 2016-06-23 15:05:24 +0800 | 
| commit | 48cc73574908e770c43c914c93462620b3772707 (patch) | |
| tree | 7d5708498edcb923a82737254bd25edc7012181c | |
| parent | 81a09b9c496cf0490eed98536d5f86626ed90cf3 (diff) | |
| download | cexcess-48cc73574908e770c43c914c93462620b3772707.tar.bz2 | |
deproject_sbp.py: update plot support
| -rwxr-xr-x | deproject_sbp.py | 179 | 
1 files changed, 125 insertions, 54 deletions
diff --git a/deproject_sbp.py b/deproject_sbp.py index 8f28062..72e6397 100755 --- a/deproject_sbp.py +++ b/deproject_sbp.py @@ -2,9 +2,13 @@  #  # Weitian LI  # Created: 2016-06-10 -# Updated: 2016-06-22 +# Updated: 2016-06-23  #  # Change logs: +# 2016-06-23: +#   * Add plot function to class 'BrightnessProfile' +#   * Update sample configuration file +#   * Remove obsolete class 'SurfaceBrightnessProfile'  # 2016-06-22:  #   * Add class 'DensityProfile', the inversion to 'BrightnessProfile'  #   * Add classes 'AstroParams' and 'BrightnessProfile' @@ -107,26 +111,27 @@ sbpfit_config = sbpfit.conf  # input cooling function profile  coolfunc_profile = coolfunc_profile.txt +# redshift of the object (for pixel to distance conversion) +redshift = 0.0137 +  ## SBP extrapolation  # cut radius from which the SBP is fitted for extrapolation,  # specified by the ratio w.r.t sbpfit rc (default: 1.2 * rc)  sbpexp_rcut_ratio = 1.2  # output of the extrapolated SBP -sbpexp_outfile = sbpexp.txt +sbpexp_outfile = sbpexp.csv  # extrapolation model information  sbpexp_json = sbpexp.json  # plot of the SBP extrapolation  sbpexp_image = sbpexp.png -## SBP deprojection -# deprojected 3D emission measure profile -em_profile = em_profile.txt -# deprojected 3D gas density profile -density_profile = gas_density_profile.txt -# plot of the deprojected gas density profile -density_profile_image = gas_density_profile.png -# save the results of SBP deprojection -sbpdeproj_json = sbpdeproj.json +## Density profiles +# deprojected 3D electron number density profile +ne_profile = ne_profile.txt +# deprojected 3D gas mass density profile +rho_gas_profile = rho_gas_profile.txt +# image of the density profiles (electron density and/or gas density) +density_profile_image = density_profile.png  ------------------------------------------------------------  """ @@ -701,8 +706,9 @@ class DeprojectSBP:          if ax is None:              fig, ax = plt.subplots(1, 1)          # SBP data points -        ax.errorbar(self.r, self.s, xerr=self.r_err, yerr=self.s_err, -                    fmt="none", elinewidth=2, capthick=2) +        eb = ax.errorbar(self.r, self.s, xerr=self.r_err, yerr=self.s_err, +                         fmt="none", elinewidth=2, capthick=2, +                         label="Brightness profile")          r_min = 1.0          r_max = max(self.r + self.r_err)          s_min = min(self.s) / 1.2 @@ -715,14 +721,19 @@ class DeprojectSBP:          ax.set_ylabel(r"Surface Brightness (photons/cm$^2$/pixel$^2$/s)")          # deprojected EM profile          ax2 = ax.twinx() -        ax2.plot(self.r, self.em, color="black", -                 linestyle="solid", linewidth=2) +        line, = ax2.plot(self.r, self.em, color="black", +                         linestyle="solid", linewidth=2, +                         label="EM profile")          em_min = min(self.em) / 1.2          em_max = max(self.em) * 1.2          ax2.set_xlim(r_min, r_max)          ax2.set_ylim(em_min, em_max)          ax2.set_yscale(ax.get_yscale())          ax2.set_ylabel(r"Deprojected Emission Measure (???)") +        # legend +        handles = [eb, line] +        labels = [h.get_label() for h in handles] +        ax.legend(handles=handles, labels=labels, loc=0)          fig.tight_layout()          return (fig, ax, ax2) @@ -935,40 +946,6 @@ class SBP:          df.to_csv(outfile, header=True, index=False) -class GasDensityProfile: -    """ -    Derive the gas density profile from the emission measure (EM) profile -    by considering the cooling function profile. -    """ -    # radius and EM profile -    r = None -    em = None - -    def __init__(self, r, em=None): -        self.load_data(r=r, em=em) - -    def load_data(self, r, em=None): -        if r.ndim == 2 and r.shape[1] == 2: -            # 2-column data -            self.r = r[:, 0].copy() -            self.em = r[:, 2].copy() -        else: -            self.r = np.array(r) -            self.em = np.array(em) - -    def load_coolfunc(self, cf): -        self.cf_radius = cf[:, 0].copy() -        self.cf_value = cf[:, 1].copy() - -    def get_gas_density(self): -        """ -        Calculate the gas density profile from EM profile. - -        NOTE: we do not consider the consistency of units here! -        """ -        pass - -  ###################################################################### @@ -1015,10 +992,13 @@ class BrightnessProfile:      * The brightness should have unit [ photon s^-1 cm^-2 pixel^-2 ],        i.e., [ Flux pixel^-2 ] (radial profile column `SUR_FLUX`)      """ -    # input SBP data: [r, r_err, s] +    # allowed density profile types +    DENSITY_TYPES = ["electron", "gas"] +    # input SBP data: [r, r_err, s, s_err]      r = None      r_err = None      s = None +    s_err = None      # redshift of the source      z = None      # `ChandraPixel` instance for unit conversion @@ -1033,10 +1013,11 @@ class BrightnessProfile:          self.pixel = ChandraPixel(z)      def load_data(self, data): -        # 3-column SBP: [r, r_err, brightness] +        # 4-column SBP: [r, r_err, brightness, brightness_err]          self.r = data[:, 0].copy()          self.r_err = data[:, 1].copy()          self.s = data[:, 2].copy() +        self.s_err = data[:, 3].copy()      def load_cf_data(self, data):          # 2-column cooling function profile @@ -1055,12 +1036,13 @@ class BrightnessProfile:              self.r_err *= cm_per_pixel              self.cf_radius *= cm_per_pixel              self.s /= cm_per_pixel**2 +            self.s_err /= cm_per_pixel**2              self.units_converted = True      def get_radius(self):          return (self.r.copy(), self.r_err.copy()) -    def calc_electron_density(self): +    def calc_electron_density(self, outfile=None):          """          Deproject the surface brightness profile to derive the 3D          electron number density (and then gas mass density) profile @@ -1077,9 +1059,16 @@ class BrightnessProfile:          # emission measure per unit volume          em_v = s_deproj / cf_interp(self.r)          ne = np.sqrt(em_v * AstroParams.ratio_ne_np) +        self.ne = ne +        # save results to output if specified +        if outfile is not None: +            ne_profile = np.column_stack([self.r, self.r_err, ne]) +            np.savetxt(outfile, ne_profile, +                       header="radius[cm]  radius_err[cm]  " + +                              "electron_number_density[cm^-3]")          return ne -    def calc_gas_density(self): +    def calc_gas_density(self, outfile=None):          """          Calculate the gas mass density based the calculated electron          number density. @@ -1088,8 +1077,73 @@ class BrightnessProfile:          """          ne = self.calc_electron_density()          rho = ne * AstroParams.mu_e * AstroParams.m_atom +        self.rho_gas = rho +        # save results to output if specified +        if outfile is not None: +            rho_profile = np.column_stack([self.r, self.r_err, rho]) +            np.savetxt(outfile, rho_profile, +                       header="radius[cm]  radius_err[cm]  " + +                              "gas_mass_density[g/cm^3]")          return rho +    def plot(self, ax=None, fig=None, density_type="electron"): +        if density_type not in self.DENSITY_TYPES: +            raise ValueError("invalid density_types: %s" % density_type) +        if density_type == "electron": +            density = self.ne +            d_name = "Deprojected electron number density" +            d_unit = "cm$^{-3}$" +        else: +            density = self.rho_gas +            d_name = "Deprojected gas mass density" +            d_unit = "g/cm$^3$" +        # +        if self.units_converted: +            # convert from [cm] to [kpc] +            r = self.r * au.cm.to(au.kpc) +            r_err = self.r_err * au.cm.to(au.kpc) +            r_unit = "kpc" +            s_unit = "flux/cm$^2$" +        else: +            r = self.r +            r_err = self.r_err +            r_unit = "pixel" +            s_unit = "flux/pixel$^2$" +        # +        if ax is None: +            fig, ax = plt.subplots(1, 1) +        # SBP data points +        eb = ax.errorbar(r, self.s, xerr=r_err, yerr=self.s_err, +                         fmt="none", elinewidth=2, capthick=2, +                         label="Brightness profile") +        r_min = 1.0 +        r_max = max(r + r_err) +        s_min = min(self.s) / 1.2 +        s_max = max(self.s + self.s_err) * 1.2 +        ax.set_xlim(r_min, r_max) +        ax.set_ylim(s_min, s_max) +        ax.set_xscale("log") +        ax.set_yscale("log") +        ax.set_xlabel("Radius (%s)" % r_unit) +        ax.set_ylabel(r"Surface brightness (%s)" % s_unit) +        # deprojected density profile +        ax2 = ax.twinx() +        line, = ax2.plot(r, density, color="black", +                         linestyle="solid", linewidth=2, +                         label="Density profile") +        d_min = min(density) / 1.2 +        d_max = max(density) * 1.2 +        ax2.set_xlim(r_min, r_max) +        ax2.set_ylim(d_min, d_max) +        ax2.set_yscale(ax.get_yscale()) +        ax2.set_ylabel(r"%s (%s)" % (d_name, d_unit)) +        # legend +        handles = [eb, line] +        labels = [h.get_label() for h in handles] +        ax.legend(handles=handles, labels=labels, loc=0) +        fig.tight_layout() +        return (fig, ax, ax2) +  class DensityProfile:      """ @@ -1182,7 +1236,24 @@ def main():      sbp.plot(ax=ax, fig=fig)      fig.savefig(config["sbpexp_image"], dpi=150) -    print("SBP deprojection -> emission measure profile ...") +    # TODO: smooth the extrapolated SBP + +    print("SBP deprojection -> density profile ...") +    redshift = config.as_float("redshift") +    cf_data = np.loadtxt(config["coolfunc_profile"]) +    sbpdata_extrapolated = sbp.get_data() +    brightness_profile = BrightnessProfile(sbp_data=sbpdata_extrapolated, +                                           cf_data=cf_data, +                                           z=redshift) +    brightness_profile.convert_units() +    brightness_profile.calc_electron_density(outfile=config["ne_profile"]) +    brightness_profile.calc_gas_density(outfile=config["rho_gas_profile"]) +    fig = Figure(figsize=(10, 8)) +    FigureCanvas(fig) +    ax = fig.add_subplot(1, 1, 1) +    brightness_profile.plot(ax=ax, fig=fig) +    fig.savefig(config["density_profile_image"], dpi=150) +  if __name__ == "__main__":      main()  | 
