Beispiel #1
0
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)
Beispiel #2
0
         ms=8,
         mew=3)
plt.xlabel("Luminosity $L_{46}$ [erg/s]")
plt.ylabel("BLR size [lightdays]")
plt.savefig("windyblr_2d_BLRsize.pdf", bbox_inches="tight")
plt.close()

plt.imshow(
    CFgrid,
    origin="lower",
    aspect="auto",
    extent=[L_AGN_lo, L_AGN_hi, MBH_lo, MBH_hi],
    cmap="Oranges",  #cmap="gray_r"
)
cb = plt.colorbar()
CS = plt.contour(log_lum(L_AGN1d), log_bhm(MBH1d), numpy.transpose(CFgrid),
                 [0.02, 0.05, 0.1, 0.2, 0.3])
plt.clabel(CS, inline=True)
plt.xlim(L_AGN_lo, L_AGN_hi)
plt.ylim(MBH_lo, MBH_hi)

#from labellines import labelLine, labelLines


# compute eddington rate:
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],
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')
    kappa=kappa) / pi * 180

plt.imshow(
    theta_crit,
    origin="lower",
    aspect="auto",
    extent=[L_AGN_lo, L_AGN_hi, MBH_lo, MBH_hi],
    cmap="Oranges",  #cmap="gray_r"
)
cb = plt.colorbar()
plt.xlim(L_AGN_lo, L_AGN_hi)
plt.ylim(MBH_lo, MBH_hi)

# compute eddington rate:
L_AGN_edd = compute_eddington_luminosity(MBH1d)
plt.plot(log_lum(L_AGN_edd), log_bhm(MBH1d), "--", color="k")
plt.plot(log_lum(L_AGN_edd * 0.1), log_bhm(MBH1d), "--", color="k", alpha=0.5)
plt.plot(log_lum(L_AGN_edd * 0.01),
         log_bhm(MBH1d),
         "--",
         color="k",
         alpha=0.25)

plt.xlabel("Bolometric Luminosity $L_\mathrm{AGN}$  [erg/s]")
plt.ylabel("Black Hole Mass $M_\mathrm{BH}$ [$M_\odot$]")
cb.set_label(r'$\theta_\mathrm{crit}$')
plt.savefig("radfountain_2d_LAGN.pdf", bbox_inches="tight")
plt.close()

L_X_lo, L_X_hi = 41, 45
L_X1d = bolcorr_hardX(L_AGN1d)
		for logLAGN in numpy.arange(42, 47.4, 0.2):
			L_AGN = 10**logLAGN * (u.erg/u.s)
			L_AGN_edd = compute_eddington_luminosity(MBH)
			eddrate = L_AGN / L_AGN_edd
			plt.figure(figsize=(10,7))
			MBH = 10**logMBH * u.Msun
			#print(logMBH, eddrate)
			prefix = "img/combinedstructure_2d_MBH%s_LAGN%.1f" % (logMBH, logLAGN)
			plot_log_agn_postcard(MBH, eddrate)
			plt.savefig(prefix + '_log.pdf', bbox_inches="tight")
			plt.savefig(prefix + '_log.png', bbox_inches="tight")
			plt.close()
			
			Ls = dict(
				L_X = round(log_lum(bolcorr_hardX(L_AGN)), 3),
				L_B = round(log_lum(bolcorr_B(L_AGN)), 3),
				L_R = round(log_lum(compute_radiocore_luminosity(MBH, L_AGN)), 3),
				lambda_edd = rround(eddrate.to(1).value),
				M_in = round(log10((L_AGN / c.c**2 / 0.1).to(u.Msun / u.yr).value), 3),
				M_out = round(log10(compute_outflow_rate(L_AGN, Mstar = 1e11 * u.Msun, SFR = 0*u.Msun/u.yr).value), 3),
			)
			fL.write("\t\t[" + ",".join(["%s" % Ls[k] for k in lumlist]) + "],\n")
			fL.flush();

			plt.figure(figsize=(10,7))
			plot_log_agn_postcard(MBH, eddrate,
				show_BH = True,
				show_corona = True,
				show_disk = True,
				show_BLR = True,