#!/usr/bin/env python3 # # Copyright (c) 2016 Aaron LI # MIT license # # Created: 2016-06-24 # # Change logs: # 2016-07-13: # * Rename from 'calc_mass_potential.py' to 'calc_mass.py' # * Split out the potential profile calculation -> 'calc_potential.py' # 2016-07-11: # * Use a default config to allow a minimal user config # 2016-07-10: # * Allow disable the calculation of potential profile # * Use class 'SmoothSpline' from module 'spline.py' # 2016-07-04: # * Remove unnecessary configuration options # * Update radii unit to be "kpc", mass unit to be "Msun" # 2016-06-29: # * Update "plot()" to support electron number density profile # 2016-06-28: # * Implement plot function # * Adjust integration tolerances and progress report # * Fit smoothing splines to profiles using R `mgcv::gam()` # 2016-06-27: # * Implement potential profile calculation: # methods 'calc_density_total()' and 'calc_potential()' # 2016-06-26: # * Add document on gravitational potential calculation # 2016-06-25: # * Rename method 'interpolate()' to 'fit_spline()' # * Use 'InterpolatedUnivariateSpline' instead of 'interp1d' # * Implement 'calc_mass_total()' # * Update documentation # 2016-06-24: # * Update method 'gen_radius()' # """ Calculate the (gas and gravitational) mass profile and gravitational potential profile from the electron number density profile, under the assumption of hydrostatic equilibrium. The electron density profile and temperature profile are required. Assuming that the gas is in hydrostatic equilibrium with the gravitational potential and a spherically-symmetric distribution of the gas, we can write the hydrostatic equilibrium equation (HEE) of the ICM as (ref.[1], eq.(6)): derivative(P_gas, r) / rho_gas = - derivative(phi, r) = - G M_tot( g/cm^3 c = au.solMass.to(au.g) / au.kpc.to(au.cm)**3 for i, r in enumerate(self.radius): dM_dr = derivative(lambda r: self.eval_spline("mass_total", r), r, dx=0.01) rho_total[i] = (dM_dr / (4 * np.pi * r**2)) * c self.rho_total = rho_total return rho_total def plot(self, profile, with_spline=True, ax=None, fig=None): x = self.radius xlabel = "3D Radius" xunit = "kpc" xscale = "log" yscale = "log" x_spl, y_spl = None, None if profile == "electron": x = self.r y = self.ne ylabel = "Electron number density" yunit = "cm$^{-3}$" if with_spline: x_spl = self.radius y_spl = self.eval_spline(spline="electron", x=self.radius) elif profile == "mass_gas": y = self.m_gas ylabel = "Gas mass" yunit = "M$_{\odot}$" elif profile == "mass_total": y = self.m_total ylabel = "Total mass" yunit = "M$_{\odot}$" elif profile == "rho_total": y = self.rho_total ylabel = "Total mass density" yunit = "g/cm$^3$" else: raise ValueError("unknown profile name: %s" % profile) # if ax is None: fig, ax = plt.subplots(1, 1) ax.plot(x, y, linewidth=2) if with_spline and y_spl is not None: ax.plot(x_spl, y_spl, linewidth=2, linestyle="dashed") ax.set_xscale(xscale) ax.set_yscale(yscale) ax.set_xlim(min(x), max(x)) y_min, y_max = min(y), max(y) y_min = y_min/1.2 if y_min > 0 else y_min*1.2 y_max = y_max*1.2 if y_max > 0 else y_max/1.2 ax.set_ylim(y_min, y_max) ax.set_xlabel(r"%s (%s)" % (xlabel, xunit)) ax.set_ylabel(r"%s (%s)" % (ylabel, yunit)) fig.tight_layout() return (fig, ax) def save(self, profile, outfile): if profile == "mass_gas": data = np.column_stack([self.radius, self.radius_err, self.m_gas]) header = "radius[kpc] radius_err[kpc] mass_gas(