def plot_edd_rate_line(MBH1d, eddrate=1, **kwargs): L_AGN_edd = compute_eddington_luminosity(MBH1d) x, y = log_lum(L_AGN_edd * eddrate), log_bhm(MBH1d) plt.plot(x, y, "--", label='$\lambda=%s$' % eddrate, **kwargs) plt.text(x[4], y[4], '$\lambda=%s$' % eddrate, va='center', ha='center', rotation=60, bbox=dict(color='white', pad=0), size=8)
# equation (9) R_in = 0.018 * u.pc * (L_opt_45)**0.5 x = numpy.linspace(0, 0.05, 1000) * u.pc z = compute_blr_height(MBH, M_dot, Z, x) #HR = y / x #HRmax = 2**-0.5 #y = numpy.where(HR > HRmax, 0, y) print("R_in:", R_in, M_dot) plt.plot( x, z, ls=ls, color=color, label= "$M_\mathrm{BH}=%d$ $\lambda=%.3f$ $L_{46}=%.2f$ $\dot{M}=%.1f M_\odot/yr$" % (log_bhm(MBH), eddrate, L_AGN_46, M_dot.to(u.Msun / u.yr).value)) plt.vlines(R_in.value, 0, 0.004, linestyles=[ls], colors=['k']) plt.plot(x, compute_sub_height(L_AGN, 1, x), '--', color='k', lw=1) plt.xlim(0, 0.05) plt.ylim(0, 0.03) plt.xlabel("R [pc]") plt.ylabel("z [pc]") plt.legend(loc="best", prop=dict(size=8)) plt.savefig("windyblr.pdf", bbox_inches="tight") plt.close()
r_d = compute_dust_sublimation_radius(L_AGN) r_0 = compute_heating_radius(L_AGN, rho_g=300 * u.Msun / u.pc**3) r_max = compute_rmax(MBH, L_AGN, theta, r_0, r_d, dust_to_gas_ratio=dust_to_gas_ratio, kappa=kappa) y, x = pol2cart(r_max, theta) plt.plot(x, y, ls=ls, color=color, label="$M_\mathrm{BH}=%d$ $\lambda=%s$" % (log_bhm(MBH), eddrate)) r_max = max(max(plt.ylim()), max(plt.xlim())) r_max = 20 plt.ylim(0.01, r_max) plt.xlim(0.01, r_max) plt.xlabel("x [pc]") plt.ylabel("y [pc]") plt.legend(loc="best", prop=dict(size=8)) #plt.xscale("log") #plt.yscale("log") plt.savefig("radfountain_2d-angle.pdf", bbox_inches="tight") plt.close() MBH_lo, MBH_hi = 6.5, 9.5 MBH1d = numpy.logspace(MBH_lo, MBH_hi, 100) * u.Msun
CF = get_blr_covering_factor(z1, z2, R) Rpeak, Hpeak = get_peak(z2, z1, R) z1, z2, zmax_dyn_nodust = compute_blr_shape(MBH, M_dot, L_AGN, R, Z, variant='dustfree', dyn_factor=2) l, = plt.plot( R, zmax_dyn, label= "$M_\mathrm{BH}=%d$ $\lambda=%.3f$ $L_{46}=%.2f$ $\dot{M}=%.1f M_\odot/yr$ CF=%.0f%%" % (log_bhm(MBH), eddrate, L_AGN_46, M_dot.to( u.Msun / u.yr).value, CF * 100)) color = l.get_color() #plt.plot(R.flatten(), zmax, ':', color=color) plt.plot(R.flatten(), zmax_dyn_nodust, '--', color=color) plt.plot(Rpeak, Hpeak, 'o', color=color) plt.fill_between(R.flatten(), 0, zmax_dyn, color=color, hatch='//') plt.vlines(R_in.to(u.pc).value, 0, Hpeak.to(u.pc).value, linestyles=[':'], colors=[color]) #break #plt.xlim(0, 0.1) #plt.xlim(0.001, R_in.value * 2)
def plot_log_agn_postcard(MBH, eddrate, # from Baskin & Laor (2017) rad_efficiency = 0.1, Z = 5, # metallicity # Radiative Fountain system assumptions dust_to_gas_ratio = 1/20., kappa = 1e3 * u.cm**2/u.g, rho_g = 300 * u.Msun / u.pc**3, show_BH = True, show_corona = True, show_disk = True, show_BLR = True, show_NLR = True, show_jet = True, show_TOR = True, show_SOI = True, show_viewing = True, colored_disk = True, show_flows = True, ): L_AGN_edd = compute_eddington_luminosity(MBH) L_AGN = eddrate * L_AGN_edd L_AGN_46 = (L_AGN / (1e46 * u.erg/u.s)).to(1) L_AGN_edd = compute_eddington_luminosity(MBH) r_grav = compute_grav_radius(MBH).to(u.pc) M_dot = (L_AGN / c.c**2 / rad_efficiency).to(u.Msun / u.yr) R_in = compute_sublimation_radius(MBH, M_dot) # from Kormendy & Ho+13, compute sigma from BH mass sigma = 200 * u.km / u.s * 10**((log10((MBH / (1e9 * u.Msun)).to(1)) + 0.510) / 4.377) r_infl = compute_sphere_of_influence(MBH, sigma).to(u.pc) R = numpy.logspace(log10(r_grav/R_in), 3, 1000) * R_in R = numpy.logspace(log10(r_grav/R_in), max(3, log10(r_infl/R_in)), 1000) * R_in title = "$M_\mathrm{BH}=%s$ $\lambda=%.3f$ $L_\mathrm{AGN}=%.1f$ $\dot{M}=%.1f M_\odot/yr$" % (log_bhm(MBH), eddrate, log_lum(L_AGN), M_dot.to(u.Msun / u.yr).value) #plt.title(title) colors = plt.rcParams['axes.prop_cycle'].by_key()['color'] textsize = 10 xlo, xhi = 8e-8 * u.pc, 50*100 * u.pc ylo, yhi = xlo / 100, 200*100 * u.pc # Sphere of influence: theta = numpy.linspace(0, pi/2, 400) y, x = pol2cart(r_infl, theta) if show_SOI: plt.plot(x, y, ls='--', lw=0.5, color=colors[8], label='Sphere of influence') #plt.text(x.max().value*1.1, r_infl.value / 1000, 'Sphere of influence', # va='bottom', ha='left', rotation=90) plt.text(x.max().value * 1.2, ylo.value * 2, 'Sphere of influence', va='bottom', ha='left', rotation=90) #plt.fill_between(x.value, y.value, y.value*0 + yhi.value, # color=colors[8], alpha=0.1) #plt.fill_between([x.max().value, xhi.value], [ylo.value]*2, [yhi.value]*2, # color=colors[8], alpha=0.1) #plt.fill_between(x.value, y.value, y.value*0 + ylo.value, # color=colors[8], alpha=0.06) # Accretion disk: R_disk = R z_disk = compute_alphadisk_height(MBH, M_dot, R_disk, alpha=1).to(u.pc) T_disk = compute_alphadisk_temperature(MBH, M_dot, R_disk, alpha=1).to(u.K) if show_disk: mask = R < r_infl if colored_disk: for color, R_part, z_disk_part in find_color_chunks(color_data_T, T_disk[mask], color_data_rgb, R_disk[mask], z_disk[mask]): plt.fill_between(R_part, z_disk_part, z_disk_part * 0 + ylo, color=color) else: plt.fill_between(R_disk[mask], z_disk[mask], z_disk[mask] * 0 + ylo, color='k', label="Accretion disk (SS)") # 'Accretion disk\n$\log \dot{M}=%.1f$' % rround(log10(M_dot.value)), plt.text(7 * r_grav.value, ylo.value * 2, 'Accretion disk\n$\log L_\mathrm{bol}=%.1f$' % log_lum(L_AGN), va='bottom', ha='left', color='k', size=textsize, bbox=dict(color='white')) #idx = numpy.where(R>20*r_grav)[0][0] #plt.text(R_disk[idx].value, z_disk[idx].value, 'Accretion disk', # va='top', ha='left', color='k', size=textsize) if show_flows: outflow = compute_outflow_rate(L_AGN, Mstar = 1e11 * u.Msun, SFR = 0*u.Msun/u.yr) inflow = M_dot # at accretion disk at least, or when considering steady-state # y = z_disk[mask][-1].value/10 plt.text(r_infl.value*2, 1e-3, '$\\uparrow$ Outflow\n$%s M_\odot/\mathrm{yr} \cdot M_{\star,11}^{-0.4}$\n$\leftarrow$ BH Inflow\n$%s M_\odot/\mathrm{yr}$' % (rround(outflow.to(u.Msun/u.yr).value), rround(inflow.to(u.Msun/u.yr).value)), va='top', ha='left', color='k', size=textsize) # Jet: R_fullrange = numpy.logspace(log10(r_grav.value), log10(R.value.max()*5), 400) * u.pc R_fullrange = numpy.logspace(log10(r_grav.value), log10(max(yhi, 1e8*r_grav).value), 400) * u.pc R_j = compute_jet_edge(MBH, R_fullrange) if show_jet: jetcolor = colors[4] plt.plot(R_j, R_fullrange, '--', color=jetcolor, label='Jet') plt.fill_betweenx(R_fullrange, R_j, color=jetcolor, label='Jet', alpha=0.05) plt.text(R_j.value[-1] / 5, R_fullrange.value[-1] / 2, 'Jet', color=jetcolor, va='top', ha='right', size=textsize) # BH: theta = numpy.linspace(0, pi/2, 4000) y, x = pol2cart(r_grav, theta[::-1]) if show_BH: plt.fill_between(x, 1e-10 + 0*y.value, y, color='k', label='Black hole') #plt.plot(x, y / 10, '-', color='k', label='Black hole 1') plt.text(r_grav.value * 0.8, r_grav.value / 3, 'Black Hole\n\n$\log M=%.1f$' % (log_bhm(MBH)) if MBH > 10**7.5 * u.Msun else 'BH\n\n$\log M=%.1f$' % (log_bhm(MBH)), va='top', ha='right', color='white', fontweight='bold', size=textsize) # Corona: y, x = pol2cart(6*r_grav, theta) if show_corona: plt.plot(x, y, ls=':', color=colors[1], label='X-ray Corona') plt.text(xlo.value*1.2, 6*r_grav.value, 'Corona', va='bottom', ha='left', color=colors[1]) # BLR: z1, z2, zmax = compute_blr_shape(MBH, M_dot, L_AGN, R, Z, variant='static') z1, z2, zmax_dyn = compute_blr_shape(MBH, M_dot, L_AGN, R, Z, variant='dynamic', dyn_factor=2) CF = get_blr_covering_factor(z1, z2, R) Rpeak, Hpeak = get_peak(z2, z1, R) z1, z2, zmax_dyn_nodust = compute_blr_shape(MBH, M_dot, L_AGN, R, Z, variant='dustfree', dyn_factor=2) idx, = numpy.where(numpy.logical_or(zmax_dyn_nodust > z_disk, zmax_dyn > z_disk)) if idx.any(): lo, hi = idx.min() - 1, idx.max() + 1 zmax_dyn_disk = numpy.where(zmax_dyn > z_disk, zmax_dyn, z_disk) zmax_dyn_nodust_disk = numpy.where(zmax_dyn_nodust > z_disk, zmax_dyn_nodust, z_disk) else: lo, hi = 0, -1 zmax_dyn_disk = zmax_dyn zmax_dyn_nodust_disk = zmax_dyn_nodust if show_BLR: color = colors[0] l, = plt.plot(R[lo:hi], zmax_dyn_nodust_disk[lo:hi], '--', label="Broad Line Region (BLCH, CF=%.0f%%)" % (CF*100), color=color, ) plt.plot(R[lo:hi], zmax_dyn_disk[lo:hi], '-', color=color, label='Dusty BLR') plt.fill_between(R[lo:hi], z_disk[lo:hi], zmax_dyn_disk[lo:hi], color=color) #plt.plot(Rpeak, Hpeak, 'o', color=color) plt.text(Rpeak.value, Hpeak.value, 'BLCH BLR\nCF=%.2f' % (CF), va='bottom', ha='center', color=color) #plt.fill_between(R.flatten(), 0, zmax_dyn, color=color, hatch='//') #plt.vlines(R_in.to(u.pc).value, 0, Hpeak.to(u.pc).value, linestyles=[':'], colors=[color]) # Radiative fountain r_d = compute_dust_sublimation_radius(L_AGN) r_0 = compute_heating_radius(L_AGN, rho_g = 300 * u.Msun / u.pc**3) r_max = compute_rmax(MBH, L_AGN, theta, r_0, r_d, dust_to_gas_ratio = dust_to_gas_ratio, kappa = kappa).to(u.pc) theta_crit = compute_critical_angle(MBH, L_AGN, r_0, r_d, dust_to_gas_ratio, kappa = kappa) CF = cos(theta_crit) color = colors[3] r_max[r_max > 1000 * u.pc] = numpy.nan z_min = numpy.interp(r_d.value, R.value, z_disk.value) theta_min = arctan2(z_min, r_d.value) y, x = pol2cart(r_max, theta) if show_TOR: #plt.plot(x, y, ls='-.', color=color, label="Radiative Fountain TOR") ## structure: rng = numpy.random.RandomState(1) Nrad = 100 # 100 pixels in radial direction rho = 10**rng.normal(size=(Nrad, Nrad)) rho[rho > 1000] = 1000 from scipy.signal import sepfir2d, convolve2d # we need 5 degree smoothing in theta according to Wada+ sims # but this could be partly due to resolution issues, # so I take twice that here correlation_degrees = 5 / 2 # same scale in radial direction as in vertical direction sigma_rad_pixels = len(rho) * sin(correlation_degrees * pi / 180) H_r = numpy.exp(-0.5 * numpy.linspace(-5, 5, int(sigma_rad_pixels)*10)**2) H_r /= H_r.sum() H_c = H_r kernel = H_r.reshape((-1, 1)) * H_c.reshape((1, -1)) kernel /= kernel.sum() rad = numpy.linspace(0, r_infl.to(u.pc).value, Nrad) X, Y = numpy.meshgrid(rad, rad) R, THETA = cart2pol(X, Y) low_density = 0 # use high values close to disk rho[THETA <= theta_min] = 10 # now erase disallowed regions: # empty jet X_jet = compute_jet_edge(MBH, Y * u.pc).to(u.pc).value rho[X < X_jet] = low_density RMAX = numpy.interp(pi/2 - THETA, xp=theta, fp=r_max.value) RMAX[~numpy.isfinite(RMAX)] = numpy.inf # only fill below r_max rho[R <= RMAX] = low_density # only fill within SOI rho[R > r_infl.to(u.pc).value] = low_density # do convolution convolved = sepfir2d(rho, H_r, H_c) #convolved = convolve2d(rho, kernel, mode='same', fillvalue=low_density) convolved[R <= RMAX] = low_density convolved[R > r_infl.to(u.pc).value] = low_density convolved[X < X_jet] = low_density #convolved[THETA <= theta_min] = 3 #plt.contourf(rad, rad, convolved/convolved[convolved>low_density].std(), # levels=numpy.linspace(-3, 3, 10), cmap='Reds', zorder=-10) #convolved[convolved > 3] = 3 #convolved[convolved < -3] = numpy.nan C = plt.contourf(rad, rad, convolved, levels=10, cmap='Reds', zorder=-10) C.collections[0].set_facecolor('white') ## end structure mask = theta > 89.7/180*pi if show_TOR and mask.any() and numpy.isfinite(x[mask][0].value): plt.text(x[mask][0].value, y[mask][0].value, 'RF TOR \nCF=%.2f ' % CF, va='bottom', ha='right', color=color) # NLR R_NLR_hi = get_nlr_size().to(u.pc) # these are chosen arbitrarily: the inner edge of the NLR and the maximum angle # in practice they are observationally and host galaxy limited, respectively R_NLR_lo = r_infl R_NLR = numpy.array([R_NLR_lo.value, R_NLR_hi.value]) * u.pc thetai = numpy.linspace(pi/400, pi/4, 10) for i, (thetalo, thetahi) in enumerate(zip(thetai[:-1], thetai[1:])): if not show_NLR: break ylo1, ylo2 = R_NLR.value xlo1, xlo2 = compute_jet_edge(MBH, R_NLR).value (yhi1, yhi2), (xhi1, xhi2) = pol2cart(R_NLR.value, thetahi) #(ylo1, ylo2), (xlo1, xlo2) = pol2cart(R_NLR_lo.value, numpy.array([thetalo, thetahi])) #(yhi1, yhi2), (xhi1, xhi2) = pol2cart(R_NLR_hi.value, numpy.array([thetalo, thetahi])) label = 'NLR' if i == 0 else None alpha = (1. - (i*1./len(thetai))**0.8)*0.5 plt.fill([xlo1, xhi1, xhi2, xlo2], [ylo1, yhi1, yhi2, ylo2], color=colors[2], alpha=alpha, lw=0, label=label) plt.text(xlo2, ylo2*0.9, 'NLR', color='white', va='top', ha='left', size=textsize) # sight-lines theta = numpy.array([1, 5, 15, 30, 60, 85]) theta[theta == 0] = 5 theta[theta == 90] = 85 for thetai in theta: if not show_viewing: break Rline = numpy.array([5 * r_grav.to(u.pc).value, 4]) #Rline = numpy.linspace(5 * r_grav.to(u.pc).value, 10, 1000) y, x = pol2cart(Rline, thetai / 180 * pi) #x = R_fullrange.value #y = x * sin(thetai / 180 * pi) #mask = y > 1 #y, x = y[mask], x[mask] plt.plot(x, y, ':', color='k', alpha=0.1) plt.text(x[-1], y[-1], '$%d^\circ$' % (thetai)) plt.xlabel("R [pc]") plt.ylabel("z [pc]") plt.xticks() #plt.legend(loc="lower right", prop=dict(size=12)) #plt.ylim(r_grav.value / 100, 200*100) #plt.xlim(r_grav.value / 2, 50*100) plt.ylim(8e-8 / 100, 200*100) plt.xlim(8e-8, 50*100) plt.xscale('log') plt.yscale('log')