In [1]:
from IPython.display import HTML
from IPython.display import Image
from PIL import Image as ImagePIL
%pylab
%matplotlib inline
In [2]:
%run ../../utils/load_notebook.py # импортируем функцию загрузки ноутбука
In [3]:
from instabilities import * # загружаем функции
In [4]:
%run ../../utils/show_notebook.py
In [5]:
show_notebook("instabilities.ipynb") # показываем функции из ноутбука, чтобы можно было подглядеть в определение
Для случая бесконечного тонкого диска: $$\kappa=\frac{3}{R}\frac{d\Phi}{dR}+\frac{d^2\Phi}{dR^2}$$ где $\Phi$ - гравпотенциал, однако его знать не надо, т.к. есть проще формула: $$\kappa=\sqrt{2}\frac{\vartheta_c}{R}\sqrt{1+\frac{R}{\vartheta_c}\frac{d\vartheta_c}{dR}}$$
In [6]:
def model_kappa(R):
return 100.*np.exp(-R/90.)
test_points = np.arange(20., 250., 10.)
fig = plt.figure(figsize=[8, 6])
plt.plot(test_points, model_kappa(test_points), '-')
plt.xlabel('$R, arcsec$')
plt.ylabel('$\kappa,\, km/s/kpc$', fontsize=15)
plt.show()
In [7]:
def model_sigmaR(R):
return 200.*np.exp(-R/50.)
fig = plt.figure(figsize=[8, 6])
plt.plot(test_points, model_sigmaR(test_points), '-')
plt.xlabel('$R, arcsec$')
plt.ylabel('$\sigma_R,\, km/s$', fontsize=15)
plt.show()
In [8]:
def model_star(R):
return 500.*np.exp(-R/50.)
def model_gas(R):
return 20.*np.exp(-R/50.) + 15.*np.exp(-(R-50.)**2/50.)
fig = plt.figure(figsize=[8, 6])
plt.plot(test_points, model_gas(test_points), '-')
plt.plot(test_points, model_star(test_points), '-')
plt.xlabel('$R, arcsec$')
plt.ylabel('$\Sigma,\, M_{o}/pc^2$', fontsize=15)
plt.show()
In [9]:
sound_vel = 6 #скорость звука в газе, км/с
In [10]:
fig = plt.figure(figsize=[8, 6])
plt.plot(test_points, [Qs(epicycl=epicycl, sigma=sigma, star_density=star_density) for (epicycl, sigma, star_density) in zip(
model_kappa(test_points), model_sigmaR(test_points), model_star(test_points))], label='Qs')
plt.plot(test_points, [Qg(epicycl=epicycl, sound_vel=sound_vel_, gas_density=gas_density) for
(epicycl, sound_vel_, gas_density) in zip(model_kappa(test_points),
[sound_vel]*len(test_points),
model_gas(test_points))], label='Qg')
plt.legend()
plt.show()
Кинетическое приближение: $$\frac{1}{Q_{\mathrm{eff}}}=\frac{2}{Q_{\mathrm{s}}}\frac{1}{\bar{k}}\left[1-e^{-\bar{k}^{2}}I_{0}(\bar{k}^{2})\right]+\frac{2}{Q_{\mathrm{g}}}s\frac{\bar{k}}{1+\bar{k}^{2}s^{2}}>1\,$$
Гидродинамическое приближение: $$\frac{2\,\pi\, G\, k\,\Sigma_{\mathrm{s}}}{\kappa+k^{2}\sigma_{\mathrm{s}}}+\frac{2\,\pi\, G\, k\,\Sigma_{\mathrm{g}}}{\kappa+k^{2}c_{\mathrm{g}}}>1$$ или $$\frac{1}{Q_{\mathrm{eff}}}=\frac{2}{Q_{\mathrm{s}}}\frac{\bar{k}}{1+\bar{k}^{2}}+\frac{2}{Q_{\mathrm{g}}}s\frac{\bar{k}}{1+\bar{k}^{2}s^{2}}>1$$ для безразмерного волнового числа ${\displaystyle \bar{k}\equiv\frac{k\,\sigma_{\mathrm{s}}}{\kappa}},\, s=c/\sigma$
In [11]:
fig = plt.figure(figsize=[16, 6])
pp_ = np.linspace(0.1, 100., 1000)
plt.plot(pp_, [inverse_hydro_Qeff_from_k(l, Qg=1., Qs=1., s=0.2) for l in pp_], '-')
plt.plot(pp_, [inverse_kinem_Qeff_from_k(l, Qg=1., Qs=1., s=0.2) for l in pp_], '-')
plt.ylabel(r'$\frac{1}{Q_{eff}}$', fontsize=25)
plt.show()
Видно, что различия есть (как и показывал Rafikov), но с хорошей точностью значения совпадают.
Зависимость от величины параметров:
In [12]:
inv_qss = np.arange(0.01, 1.2, 0.1)
inv_qgs = np.arange(0.01, 1.2, 0.1)
inv_qeffs = []
for qg in inv_qgs:
for qs in inv_qss:
inv_qeff = max([inverse_kinem_Qeff_from_k(l, Qg=1./qg, Qs=1./qs, s=0.2) for l in np.arange(0.1, 10000., 1.0)])
inv_qeffs.append(inv_qeff)
In [13]:
fig = plt.figure(figsize=[8, 8])
ax1 = fig.add_subplot(111)
qeffs = np.array(inv_qeffs).reshape(len(inv_qgs), len(inv_qgs))
levels = np.linspace(qeffs.min(), qeffs.max(), 20)
cset=ax1.contour(qeffs, levels, hold='on', colors = 'k', origin='lower', extent=[inv_qgs[0],inv_qgs[-1],inv_qss[0],inv_qss[-1]])
ax1.clabel(cset, inline=1, fontsize=10, fmt='%1.2f')
ax1.grid()
ax1.set_xlabel('1/Qg')
ax1.set_ylabel('1/Qs')
ax1.set_title('1/Qeff, s = 0.2')
ax1.set_xticks(np.arange(0, 1.1, 0.1))
ax1.set_yticks(np.arange(0, 1.1, 0.1))
levels = np.array([1.0])
cset=ax1.contour(qeffs, levels, hold='on', colors = 'r', origin='lower', extent=[inv_qgs[0],inv_qgs[-1],inv_qss[0],inv_qss[-1]])
plt.show()
In [14]:
Image('rafikov_fig3.png')
Out[14]:
In [15]:
inv_qss = np.arange(0.01, 1.2, 0.1)
inv_qgs = np.arange(0.01, 1.2, 0.1)
s_params = [1.0, 0.5, 0.3, 0.2, 0.1]
inv_qeffs_k = [[],[],[],[],[]]
inv_qeffs_h = [[],[],[],[],[]]
for ind, s in enumerate(s_params):
for qg in inv_qgs:
for qs in inv_qss:
inv_qeff = max([inverse_kinem_Qeff_from_k(l, Qg=1./qg, Qs=1./qs, s=s) for l in np.arange(0.1, 10000., 1.0)])
inv_qeffs_k[ind].append(inv_qeff)
inv_qeff = max([inverse_hydro_Qeff_from_k(l, Qg=1./qg, Qs=1./qs, s=s) for l in np.arange(0.1, 10000., 1.0)])
inv_qeffs_h[ind].append(inv_qeff)
In [16]:
fig = plt.figure(figsize=[8, 8])
ax1 = fig.add_subplot(111)
levels = np.array([1.])
for ind, s in enumerate(s_params):
qeffs = np.array(inv_qeffs_h[ind]).reshape(len(inv_qgs), len(inv_qgs))
cset=ax1.contour(qeffs, levels, hold='on', colors = 'k', origin='lower',
extent=[inv_qgs[0],inv_qgs[-1],inv_qss[0],inv_qss[-1]], linestyles='dashed')
qeffs = np.array(inv_qeffs_k[ind]).reshape(len(inv_qgs), len(inv_qgs))
cset=ax1.contour(qeffs, levels, hold='on', colors = 'k', origin='lower',
extent=[inv_qgs[0],inv_qgs[-1],inv_qss[0],inv_qss[-1]])
ax1.grid()
ax1.set_xlabel('1/Qg')
ax1.set_ylabel('1/Qs')
ax1.set_title('1/Qeff, s = 0.2')
ax1.set_xticks(np.arange(0, 1.1, 0.1))
ax1.set_yticks(np.arange(0, 1.1, 0.1))
plt.show()
Успешно!
В гидродинамическом приближении вообще просто - это многочлен $$\frac{2}{Q_{\mathrm{s}}}\frac{\bar{k}}{1+\bar{k}^{2}}+\frac{2}{Q_{\mathrm{g}}}s\frac{\bar{k}}{1+\bar{k}^{2}s^{2}}>1$$ и у него можно найти максимум методами Sympy, взяв производную:
In [17]:
from sympy import *
x = Symbol('x', real=True)
a = 1.5
f = 2./a*x/(1+x**2) + 2/0.9*0.4*x/(1+x**2 * 0.4**2)
roots = solve(f.diff(), x)
print roots
print max([f.evalf(subs={x:r}) for r in roots])
In [18]:
from sympy.plotting import plot
plot(f, (x, 0., 10.))
Out[18]:
In [19]:
findInvHydroQeffSympy(1.32, 1.22, 0.52)
Out[19]:
Второй метод - тупо перебор по сетке:
In [20]:
findInvHydroQeffBrute(1.32, 1.22, 0.52, np.arange(0.01, 300., 0.01))
Out[20]:
Последний метод - используя производную, запускать brentq на нужном промежутке:
In [21]:
findInvHydroQeffBrentq(1.32, 1.22, 0.52, np.arange(0.01, 300., 0.01))
Out[21]:
In [22]:
cases_n = 1000
test_cases = zip(np.random.uniform(0.01, 3., cases_n),
np.random.uniform(0.01, 3., cases_n),
np.abs(np.random.normal(0.01, 0.5, cases_n)))
In [23]:
test_cases[0:10]
Out[23]:
In [24]:
%time bren = [findInvHydroQeffBrentq(case[0], case[1], case[2], np.arange(0.01, 60000., 1.)) for case in test_cases]
In [25]:
%time sym = [findInvHydroQeffSympy(case[0], case[1], case[2]) for case in test_cases]
In [26]:
%time bru = [findInvHydroQeffBrute(case[0], case[1], case[2], np.arange(0.01, 3000., 0.01)) for case in test_cases]
In [27]:
%time bru = [findInvHydroQeffBrute(c[0], c[1], c[2], np.arange(b[0]-100., b[0]+100., 0.001)) for c, b in zip(test_cases, bren)]
In [28]:
fig = plt.figure(figsize=[16, 8])
plt.plot(range(cases_n), abs(np.array(zip(*bren)[1])-np.array(zip(*bru)[1])), 's-')
plt.plot(range(cases_n), abs(np.array(zip(*bren)[1])-np.array(zip(*sym)[1])), 'd-')
plt.plot(range(cases_n), abs(np.array(zip(*sym)[1])-np.array(zip(*bru)[1])), 'v-')
plt.show()
In [29]:
fig, axes = plt.subplots(2, 3, figsize=[16, 10])
axes[0][0].plot(zip(*sym)[1], zip(*bru)[1], 'v')
axes[0][1].plot(zip(*sym)[1], zip(*bren)[1], '>')
axes[0][2].plot(zip(*bren)[1], zip(*bru)[1], '<')
axes[1][0].plot(zip(*sym)[0], zip(*bru)[0], '8')
axes[1][1].plot(zip(*sym)[0], zip(*bren)[0], 'p')
axes[1][2].plot(zip(*bren)[0], zip(*bru)[0], 'h')
for ax in axes:
for a in ax:
a.set_xlim(0., 10.)
a.set_ylim(0., 10.)
a.grid()
a.plot([0,10], [0, 10], '--')
plt.show()
Внешнее согласие отличное.
Теперь кинематическое приближение: $$\frac{2}{Q_{\mathrm{s}}}\frac{1}{\bar{k}}\left[1-e^{-\bar{k}^{2}}I_{0}(\bar{k}^{2})\right]+\frac{2}{Q_{\mathrm{g}}}s\frac{\bar{k}}{1+\bar{k}^{2}s^{2}}>1\,$$ Тут сложнее, честно уже не решить. остается два способа - брутфорсом и brentq, производная известна.
In [30]:
findInvKinemQeffBrute(1.32, 1.22, 0.52, np.arange(0.01, 300., 0.01))
Out[30]:
In [31]:
findInvKinemQeffBrentq(1.32, 1.22, 0.52, np.arange(0.01, 300., 0.01))
Out[31]:
In [32]:
cases_n = 1000
test_cases = zip(np.random.uniform(0.01, 3., cases_n),
np.random.uniform(0.01, 3., cases_n),
np.abs(np.random.normal(0.01, 0.5, cases_n)))
In [33]:
test_cases[0:10]
Out[33]:
In [34]:
%time bren = [findInvKinemQeffBrentq(case[0], case[1], case[2], np.arange(0.01, 60000., 1.)) for case in test_cases]
In [35]:
%time bru = [findInvKinemQeffBrute(case[0], case[1], case[2], np.arange(0.01, 3000., 0.01)) for case in test_cases]
In [36]:
%time bru = [findInvKinemQeffBrute(c[0], c[1], c[2], np.arange(b[0]-100., b[0]+100., 0.001)) for c, b in zip(test_cases, bren)]
In [37]:
fig = plt.figure(figsize=[16, 5])
plt.plot(range(cases_n), abs(np.array(zip(*bren)[1])-np.array(zip(*bru)[1])), 's-')
plt.show()
In [38]:
fig, axes = plt.subplots(1, 2, figsize=[12, 6])
axes[0].plot(zip(*bren)[1], zip(*bru)[1], '<')
axes[1].plot(zip(*bren)[0], zip(*bru)[0], 'h')
for ax in axes:
ax.set_xlim(0., 10.)
ax.set_ylim(0., 10.)
ax.grid()
ax.plot([0,10], [0, 10], '--')
plt.show()
Работает
In [39]:
cases_n = 200
test_cases = zip(np.random.uniform(0.01, 3., cases_n),
np.random.uniform(0.01, 3., cases_n),
np.abs(np.random.normal(0.01, 0.5, cases_n)))
In [40]:
test_cases[0:10]
Out[40]:
In [41]:
%time kin = [findInvKinemQeffBrentq(case[0], case[1], case[2], np.arange(0.01, 60000., 1.)) for case in test_cases]
In [42]:
%time hyd = [findInvHydroQeffBrentq(case[0], case[1], case[2], np.arange(0.01, 60000., 1.)) for case in test_cases]
In [43]:
fig = plt.figure(figsize=[16, 5])
plt.plot(range(cases_n), abs(np.array(zip(*kin)[1])-np.array(zip(*hyd)[1])), 's-', color='red')
plt.plot(range(cases_n), abs(np.array(zip(*kin)[0])-np.array(zip(*hyd)[0])), 'v-')
plt.ylim(0, 5.)
plt.show()
In [44]:
fig, axes = plt.subplots(1, 2, figsize=[16, 7])
axes[0].plot(zip(*kin)[1], zip(*hyd)[1], '<')
axes[1].plot(zip(*kin)[0], zip(*hyd)[0], 'h')
for ax in axes:
ax.set_xlim(0., 10.)
ax.set_ylim(0., 10.)
ax.grid()
ax.plot([0,10], [0, 10], '--')
plt.show()
Видно, что максимум иногда съезжает и Q иногда различается, но в целом все достаточно близко.
In [45]:
# используем сосчитанные выше кейсы
%time qgs = [1./case[1] for case in test_cases]
In [46]:
fig, axes = plt.subplots(1, 2, figsize=[16, 7])
axes[0].plot(qgs, zip(*hyd)[1], '<')
axes[1].plot(qgs, zip(*kin)[1], 'h')
for ax in axes:
ax.set_xlim(0., 10.)
ax.set_ylim(0., 10.)
ax.grid()
ax.plot([0,10], [0, 10], '--')
ax.set_xlabel('1/Qg')
ax.set_ylabel('1/Qeff')
plt.show()
Верно
In [47]:
Image('rafikov_fig1.png')
Out[47]: