From f8a0754e751cdd26a1862e6da0896dcacd7ac2c6 Mon Sep 17 00:00:00 2001 From: Jaime Salcido Date: Wed, 28 Jul 2021 13:08:03 +0100 Subject: [PATCH 1/2] Added (working but unpolished) scripts to calculate hot has properties for Flamingo, e.g. density, pressure and entropy profiles, etc. --- flamingo/config.yml | 24 + .../interpolate_X_Ray_redshift.cpython-36.pyc | Bin 0 -> 9857 bytes .../__pycache__/load_sfh_data.cpython-36.pyc | Bin 4731 -> 4740 bytes flamingo/scripts/dummy.py | 3 + flamingo/scripts/hot_gas_plots.py | 460 ++++++++++++++++++ .../scripts/interpolate_X_Ray_redshift.py | 372 ++++++++++++++ 6 files changed, 859 insertions(+) create mode 100644 flamingo/scripts/__pycache__/interpolate_X_Ray_redshift.cpython-36.pyc create mode 100644 flamingo/scripts/dummy.py create mode 100644 flamingo/scripts/hot_gas_plots.py create mode 100644 flamingo/scripts/interpolate_X_Ray_redshift.py diff --git a/flamingo/config.yml b/flamingo/config.yml index 9ddc32ef..9498bf57 100644 --- a/flamingo/config.yml +++ b/flamingo/config.yml @@ -151,4 +151,28 @@ scripts: title: Wall-clock time per time-bin section: Run Performance output_file: wallclock_timebin_hist.png + - filename: scripts/hot_gas_plots.py + caption: X-ray luminosity - Halo mass. + output_file: hgas_Lx.png + section: Hot gas properties + title: X-ray luminosity + - filename: scripts/dummy.py + caption: Hot gas pressure profiles of galaxy groups. + output_file: hgas_P_P500_r.png + section: Hot gas properties + title: Pressure profiles + - filename: scripts/dummy.py + caption: Hot gas density profiles of galaxy groups. + output_file: hgas_rho_r.png + section: Hot gas properties + title: Density profiles + - filename: scripts/dummy.py + caption: Hot gas entropy profiles of galaxy groups. + output_file: hgas_S_S500_r.png + section: Hot gas properties + title: Entropy profiles + - filename: scripts/dummy.py + caption: Local SZ flux−halo mass relation. + output_file: hgas_tSZ.png + section: Hot gas properties diff --git a/flamingo/scripts/__pycache__/interpolate_X_Ray_redshift.cpython-36.pyc b/flamingo/scripts/__pycache__/interpolate_X_Ray_redshift.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..90f227916c15aacbdbee674138b85142526cbd8e GIT binary patch literal 9857 zcmeHNU2G%Qb)G-|iWDh|qNtzUrDc09*;;A6>s_y%wWYY*T|23j)~hxNlXN`N9g;(l zL;B9hD-pw&YW)@?8x$zeAP7(tZIOo<$WxPt<{@Z{=A~$#ip)#W8VDk!1^SRSM$k0< z&Yj_q)N1WrG(huE3ir&t=l;w+=bm%#chBf@Hk*Fo2S0i<{u`3?zBKYvk>5meehk7S zCby(&@RX|x%1TRZYgJ7qp4y7F$z$%r$-GJ=zfI9WC+IQfW^V|l@uia1lOAUK7HGtG*E zGaYeeSV?e-Y>}1O++Ddk!_KgIwt!rT$+xAIv&WED+FFr)t<^CYo@&!_S7N?g_m$nY zue7_qYVx`-JG<0!@Kcc4L~<5EOsOg}30jhwa#yOVOl2CL8jIl#aeuQAAkL|ty`a3?^<26W!dJ1-Hu(e>W<#%xO&}i)@v=JZQ1qCy0gozuIsFi%t!x> z{)Ta=bCWp@tLCnC4}HzDEtlTykdTtx%a62Qqsm>f75dRLfh2_Z8Zxfbls%crv#^&V z#glZTrs64cu$eijCe2CUsl;PiRMtc}7L{Y79FNLzQBFkVgqv)pSduJ>!FE`-9zT*X zZknY)C)gxtM)b)=eX>Hm`K{i3Ah2ufw9H6sXRX0%SA3cK8MAFUj`guD@d0 z&ibpSzJq6PCMdp6E$q5(w#>F^yS?dveZ#aJ%RLlL*L$-8bK7ipO>VgRJQ!oW4lRja z{VRV=zwd2?neT7ZFN@#%1J3bA*!bTEoNsUR=7Vu>!HeI9DvUbI6*HNBCTVg!T}(EMvQs4W$OXA5=jFVDB$ws5 z99Md!Q9B%V+Jd(xb=@}Frmp*GU2k{TJ{DK^GrIoPzR?P5I1SFv5s4ft$k8l#3WSpI z#iX261!o)1n6x|HL$}efBeRd89VKD*Uqpt?-i4_j$wzWuJveaHLFtC{g+?C+?!ny2 zG3KN_iNDBXPxZ9x(p!sB4eIwaUc*Ql+QAt>;R0ohIcOWyc?yk1jzrJV`cF%qiajK; z#c5+X7KB{*_?P(c^OXB<cs((^rih2iiCxHTgn^eIRrq?j225Rjq_Lis%)c6L~xoV`w zAQ!3;ZI@85Aqn-7>Q1T+Z~725B}UXl$_aFJiE@{T2<50l?-eo!ehP@tSM6(p14Dp+E;9uj(i0g6OfU36 zdEEiYnT9ECvrlpROn!O@8hTqrP3f#oRV^0;m?B~ zt#urQB$ANoE;24=MJw&fSV8O?(1Ro+12!_M*T&$TNHuP-VE}!ckV@ z!*#DF?ftemd;$v~=cuNzP3b#kt8?e1>LwzKXqc~~sYBi)DM`3WLAxZwAYMQ@G6-@J zQlC)WA0a~vJ=n^zvH!u?zQiADV-E~xn_-s+O87#54QP+S;g@8ZRTm^WtG2(nq4|e={Juwfg}`m z1L_ME77HwZ?2NWdSYH+jNR_4jq?hXF?!^vDLY4QmyCL1vzVJamFO-od@A72$(i9Yh zlE@dJJe5kQlTbGultjMhC8(x9gSUws-dOUcyy>Fkm5?Tp(q59zVK46GgZTw>^|V15 z)*RN*WDmUqL<_K*{m#J%Ezh2u6yaNe%< zs>bN}&|IQ1(XW~byX$nd^yTKsQ#xNa-QWU<_fe>%ff+e3gGkCe4r@(WA_RxKU=pUu=$|rHl5fV7a!Ed~WE6gjBm>N@xw~|%(~sgcbV(5>)(7eU2@~pN2-q+YrW%G8 z*d)u^Wjg&3tZ)-7|E|(UFal+&O*PX; z5}P_w_7K{LcoQK{-jw=jPY(H+k+wtLXxj;#U=!$*L65AL25?9>C&kTD>1PFR0=$Wc zHwo*S+>~0C(&tp9mugcJ;t31xy3Q=LdcyN<1U8W z(irzl$ekVIo(;L>G48pLJ2%FygxvWt?qkGt7XrCx9f5cAF5THlDwKf}#JL~fh~^ph zEbKz~Z!4dy<`JPqAgTQcv}ZUJklFH>yEkjVbo@ zyUGG-UG}hMz(6zX8MewUur;>MF0yCYC3YFTOKi%^d8Gv)rT#2hSFjre^Oe{Yx-Qes z6mqVPm7X6fU87PB`xr)f4C5?$Ghr{RowyPwn~y`zvirn?YQ6HQQqQ?h0=*@h=h1uK zeJaR%^JLADHW%Dwv{*rU+M5OrD+D!OK75wL=bSem<`&TQnMksQAT$736GvIHSlD0+ zyX*_W&8+^dv+w-mcfR$VL9p!~HtO%)`sZHpy?+{TemKbdc#uIrj`k8s`spC^!65Tr zgN(EpwowKdZIFo%GRaNEmtxF4M2wI^2EEUX#0x7OUI`;}6@#0V&c5qd49{A{ZJ3o| zFwE-M6%oIwAi7quJC&N%YUytqt$nm|D$ahl+pDr)^fm z1eYpqr{bU~;+U1L!Ch;&WjYTdEBY;sjMKaEDPCz=ZOg43cJ_HRR)qpt;tlAqV^;21 zZo{%4q(NU!_x4|l!u6!tJ5VNd+%>vgldV4bLQtO}>Ti&c+{_Ah9+3M?7?-&2l_|Zl^_Klx^O#do;;3xU`FaEC7->CmhIr(R^8DpdVpFjPp53c;lmpAI4 z&@%7*`LVrGFaG|Y8p%CWZT`h`k1L;lbECfTTJ7v_ey_7(eRU9WWrG`MSi}+**=!DNu!)qW26Bq})By!Gv+mAU7 zql?yE4!0QIg8gLUknv6(5zcr6A4kwKzH9KEj*Y0U<%Tt#gF|Ye*(Ug@wo$iDhnglF z3$bdm^5l|DMOh0Zo$V%g%xECNq_ zj*1kl5y)f++yn$$S@h!~QjW=r=eA!8KQZWS!*TQ)H+Ef^q~j+CVR(L)RDgT?z~TK= zPw#X^D}ICO;=w+_b5w{T{TOd=Jkl>X>1GGb>1})dWb{>lRPXqOvF`!_FFQ6bPKb?P zC-M@JkI^e9M@29lKONyr$1jGT4aS7ygv|H`kxe2L^Wv`%IT03{3M3Ps)q>AhUqLJ9 zO%O?0mWyg!POFpZs!47dui;fXZ|T zAXBG+O5%+J#Q;F2n)icB;*GY=coSX*pz>4ziFszd3_xWj0+l&$(#wUQ@}?ILLFM)T z1*p6LP?-g&j04_g2~+|o<^it*V42174fy~Qrd$dDg=KGQ3|36LfI|Ra1%OdGY(oHS z8hxU&H+^cKB48E14C71j8FrSPV-@xoAk>VPhxDMMeF&;W01RLm0IW0)-PqIY8T6h7 z0Ly!`0)Uk%<3&=GnT^uVt8v(GgOF*vdmBRT909dLy2RY~5`3Kc{ zbE6V82b!7(R8^tbMf6^9&jfjIK>)Bpn??65TAV|wctsDvgrK)K6+Y*}=e)NN<`&WR zu}Id{2!JI4z_7Yl+cW^!rC$#K#$SW~{y+Tsb%0;|6IhD>BLK!fiFg0%fEB+@!~Lpp zmB5A%23Jc%aK*RDs>hKP|6St!9+6KIiQp9f3>67Z@y`;e68Rj6pF}VSUtGF}L-@l@ zs;v>B8xE&njQ~B|qTC}vo-@2jefB{73052YONGPR#HNE)ARKN}u0w=C4et^mM8n@A z!ihLUTq65K-X?MiAmMkY;(*8@k%vVa<@?ZvPO#5#@(9&ATNr8pIx-&(I<#c|BOyi~ zBd#d?RXnz$=PW0n;m3j{nWcQ%xr2Y)ad7kLaQenBDt8P{7YJY5w+~%T*9cD62u{}s zPPm2BH6p;=^mRU1-K&%n@RYua1-SJV7=1nXkBgTnXes_7Dybq& XhrdN-vQWt4FN42qVX|;3{r3L=*`X@& literal 0 HcmV?d00001 diff --git a/flamingo/scripts/__pycache__/load_sfh_data.cpython-36.pyc b/flamingo/scripts/__pycache__/load_sfh_data.cpython-36.pyc index b66ef85f4e4698013c54876aa580a296591bc6a3..5766c1d747ad54bd865c8cdacb943b64ca6cb16a 100644 GIT binary patch delta 93 zcmeyZ(xS>?%*)HAX2_Vhk>fF=TbzD!esON1seVdgNuqvAX;DUEu6|l}dajX?zMGGu wucx28zkWexL26EBUaD?#eqLH;dU1S4eo1_KVzGW&PGW9mUV8p!4W?iL0DmSQVE_OC delta 84 zcmZos{jI`b%*)Fq|2QyVBgbP#+fe=F{Nmh1GyRmrl0^NK0s{jR{gh keV conversion +kb = 1.3807e-16 # Boltzmann's constant (erg/K) +mp = 1.6726e-24 # proton mass (g) +mpc = 3.0857e24 # Mpc -> cm conversion +mu = 0.6 +Zmet = 0.3 +G = 6.67408e-08 # cm3 / (g s2) + +z_tab = [0.0, 10 ** -1.5, 10 ** -1, 10 ** -0.5, 1, 10 ** 0.5] +''' +Xe = ne/nH (see Sutherland & Dopita 1993) +valid over the range 0 <= Z/Zsolar <= 3.16 +''' +xe = [1.128, 1.129, 1.131, 1.165, 1.209, 1.238] +xe_int = CubicSpline(z_tab, xe, bc_type=((2, 0.0), (2, 0.0)), extrapolate=False) +''' +Xi = ni/nH (see Sutherland & Dopita 1993) +valid over the range 0 <= Z/Zsolar <= 3.16 +''' +xi = [1.064, 1.064, 1.064, 1.08, 1.099, 1.103] +xi_int = CubicSpline(z_tab, xi, bc_type=((2, 0.0), (2, 0.0)), extrapolate=False) + +ne_n = xe_int(Zmet) / (xe_int(Zmet) + xi_int(Zmet)) + +@njit +def compute_Xs_mu(elements_array): + + all_elements = ['hydrogen', 'helium', 'carbon', 'nitrogen', 'oxygen', 'neon', 'magnesium', 'silicon', 'sulphur', 'calcium', 'iron'] + element_masses = [1.00794, 4.002602, 12.0107, 14.0067, 15.9994, 20.1797, 24.3050, 28.0855, 32.065, 40.078, 55.845] + agtomic_num = [1.0, 2.0, 6.0, 7.0, 8.0, 10.0, 12.0, 14.0, 16.0, 20.0, 26.0] + + # Hydrogen + ne_nH = np.full_like(elements_array[:, 0], 1.0) + ni_nH = np.full_like(elements_array[:, 0], 1.0) + mu = np.full_like(elements_array[:, 0], 0.5) + + # Every other element + for i in range(len(all_elements) - 1): + ne_nH += elements_array[:, i + 1] * (element_masses[0] / element_masses[i + 1]) * (agtomic_num[i + 1] / agtomic_num[0]) + ni_nH += elements_array[:, i + 1] * (element_masses[0] / element_masses[i + 1]) + mu += elements_array[:, i + 1] / (agtomic_num[0] + agtomic_num[i + 1]) + + return ne_nH, ni_nH, mu + +def read_hot_gas_props(run_directory, snapshot_name, cat_name, obs_data_dir, chunk): + + elements = ['hydrogen', 'helium', 'carbon', 'nitrogen', 'oxygen', 'neon', 'magnesium', 'silicon', 'iron'] + all_elements = ['hydrogen', 'helium', 'carbon', 'nitrogen', 'oxygen', 'neon', 'magnesium', 'silicon', 'sulphur', 'calcium', 'iron'] + SulphurOverSilicon = 0.6054160 + CalciumOverSilicon = 0.0941736 + + catalogue = load_catalogue(run_directory + '/' + cat_name) + groups = load_groups(run_directory + '/' + cat_name.replace('properties', 'catalog_groups'), catalogue=catalogue) + + df_chunk = pd.DataFrame() + halo_n = 1 + tot_h = len(chunk.index) + for index, row in chunk.iterrows(): + print('TaskID: ', multiprocessing.current_process()._identity[0], 'Processing halo = ', int(row.GroupNumber), '(', halo_n, '/', tot_h, ')') + particles, unbound_particles = groups.extract_halo(halo_id=int(row.GroupNumber)) + data, bmask = to_swiftsimio_dataset(particles, run_directory + '/' + snapshot_name, generate_extra_mask=True) + udata, umask = to_swiftsimio_dataset(unbound_particles, run_directory + '/' + snapshot_name, generate_extra_mask=True) + mask = np.logical_or(bmask.gas, umask.gas) + + meta = data.metadata + BoxSize = meta.boxsize[0].value + cosmo = meta.cosmology + aexp = meta.a + z = meta.redshift + ez = cosmo.efunc(z) + + df_elements = pd.DataFrame() + for element in elements: + df_elements[element] = getattr(data.gas.smoothed_element_mass_fractions, element) + + df_gas = pd.DataFrame(data.gas.coordinates.value, columns=['x', 'y', 'z']) + df_gas['temp'] = data.gas.temperatures.value + df_gas['mass'] = data.gas.masses.to('Msun').value.astype(np.float64) * u.Msun.to(u.gram) + # df_gas['metallicity'] = data.gas.metal_mass_fractions.value / 0.0134 + df_gas['sfr'] = data.gas.star_formation_rates.to('Msun/Gyr').value + df_gas['rho'] = data.gas.densities.to('g * cm ** -3').value + df_gas['nH'] = df_elements.hydrogen.values * df_gas.rho.values / mp + # df_gas['nH'] = np.full_like(df_gas.temp.values, 1e6) + df_gas['lambda_c'] = np.power(10, xray.interpolate_X_Ray_pandas(np.log10(df_gas.nH.values), np.log10(df_gas.temp.values), df_elements, z, obs_data_dir, band = '0.5-2.0keV', fill_value = -400)) / df_gas.nH.values ** 2 + df_gas['in_FOF'] = mask + + df_elements = df_elements.div(df_elements.hydrogen, axis=0) + df_elements['sulphur'] = df_elements.silicon * SulphurOverSilicon + df_elements['calcium'] = df_elements.silicon * CalciumOverSilicon + + df_elements = df_elements.reindex(columns=all_elements) + ne_nH, ni_nH, mu = compute_Xs_mu(df_elements.to_numpy()) + + df_gas['ne_nH'] = ne_nH + df_gas['ni_nH'] = ni_nH + df_gas['mu'] = mu + + df_gas['GroupNumber'] = int(row.GroupNumber) + df_gas['m500'] = row.m500 + df_gas['r500'] = row.r500 + df_gas['CoP_x'] = row.CoP_x + df_gas['CoP_y'] = row.CoP_y + df_gas['CoP_z'] = row.CoP_z + df_gas['S500'] = row.S500 + df_gas['P500'] = row.P500 + + df_gas['x'] = df_gas.x - df_gas.CoP_x + BoxSize / 2 + df_gas['y'] = df_gas.y - df_gas.CoP_y + BoxSize / 2 + df_gas['z'] = df_gas.z - df_gas.CoP_z + BoxSize / 2 + df_gas.x.loc[df_gas.x < 0] = df_gas.x.loc[df_gas.x < 0] + BoxSize + df_gas.y.loc[df_gas.y < 0] = df_gas.y.loc[df_gas.y < 0] + BoxSize + df_gas.z.loc[df_gas.z < 0] = df_gas.z.loc[df_gas.z < 0] + BoxSize + df_gas.x.loc[df_gas.x > BoxSize] = df_gas.x.loc[df_gas.x > BoxSize] - BoxSize + df_gas.y.loc[df_gas.y > BoxSize] = df_gas.y.loc[df_gas.y > BoxSize] - BoxSize + df_gas.z.loc[df_gas.z > BoxSize] = df_gas.z.loc[df_gas.z > BoxSize] - BoxSize + df_gas['distance'] = (((BoxSize / 2) - df_gas.x) ** 2 + ((BoxSize / 2) - df_gas.y) ** 2 + ((BoxSize / 2) - df_gas.z) ** 2) ** 0.5 + df_gas['distance'] = df_gas.distance / df_gas.r500 + df_gas = df_gas.loc[df_gas.distance <= 5] + + df_gas['Lx'] = df_gas.lambda_c * (df_gas.rho * (df_gas.ne_nH / ((df_gas.ne_nH + df_gas.ni_nH) * df_gas.mu * mp)).values.astype(np.float64) ** 2) * df_gas.mass.values / df_gas.ne_nH + # df_gas['Lx'] = df_gas.lambda_c * df_gas.mass.values / df_gas.rho + + df_gas['ypar'] = (sigma_t / (511 * ergs_keV)) * kb * df_gas.temp * (df_gas.mass.values * 0.752 * df_gas.ne_nH / mp ) / mpc / mpc # in Mpc**2 + df_gas['ypar'] = df_gas.ypar * (ez ** (-2./3)) + + df_chunk = pd.concat([df_chunk, df_gas]) + halo_n += 1 + + return(df_chunk) + +def plot_hot_gas_props(arguments): + # print('\033[34m TEST... \033[0m') + + run_names = arguments.name_list + run_directories = [f"{directory}" for directory in arguments.directory_list] + snapshot_names = [f"{snapshot}" for snapshot in arguments.snapshot_list] + cat_names = [f"{snapshot}" for snapshot in arguments.catalogue_list] + obs_data_dir = arguments.config_directory + '/../observational_data/data/HotGasProps/' + output_path = arguments.output_directory + + # bahamas_true_Lx = pd.read_csv(obs_data_dir + 'Bahamas_LX_true.csv') + # bahamas_obs_Lx = pd.read_csv(obs_data_dir + 'Bahamas_LX.csv') + + bahamas_LX = pd.read_csv(obs_data_dir + 'new_BAHAMAS_LX.csv') + + # bahamas_obs_tSZ = pd.read_csv(obs_data_dir + 'Bahamas_tSZ.csv') + # bahamas_true_tSZ = pd.read_csv(obs_data_dir + 'Bahamas_Ysz_true.csv') + + bahamas_Ysz = pd.read_csv(obs_data_dir + 'new_BAHAMAS_Ysz.csv') + planck_tSZ = pd.read_csv(obs_data_dir + 'Planck_tSZ.csv') + + + # bahamas_obs_rho = pd.read_csv(obs_data_dir + 'Bahamas_rhox.csv') + # bahamas_obs_P = pd.read_csv(obs_data_dir + 'BAHAMAS_P.csv') + + bahamas_rho = pd.read_csv(obs_data_dir + 'new_BAHAMAS_rho.csv') + bahamas_P = pd.read_csv(obs_data_dir + 'new_BAHAMAS_P.csv') + bahamas_S_S500 = pd.read_csv(obs_data_dir + 'new_BAHAMAS_S_S500.csv') + + + sun_ne = pd.read_csv(obs_data_dir + 'Sun2009_ne_profiles.txt', skiprows=4, delim_whitespace=True, names=['log_r', 'log_ne', 'log_err_m', 'log_err_p']) + sun_P = pd.read_csv(obs_data_dir + 'Sun2011_P_profiles.txt', skiprows=4, delim_whitespace=True, names=['log_r', 'log_P', 'log_err_m', 'log_err_p']) + sun_S = pd.read_csv(obs_data_dir + 'Sun09_entropy_profiles.txt') + ras_S = pd.read_csv(obs_data_dir + 'Rasmussen_entropy_profiles.txt') + + r_bins = np.linspace(-2, np.log10(4), 30) + centers = (r_bins[:-1] + r_bins[1:]) / 2. + r_bins = 10 ** r_bins + centers = 10 ** centers + + fig1, ax1 = plt.subplots() + fig2, ax2 = plt.subplots() + fig3, ax3 = plt.subplots() + fig4, ax4 = plt.subplots() + fig5, ax5 = plt.subplots() + fig6, ax6 = plt.subplots() + + for run_name, run_directory, snapshot_name, cat_name in zip( + run_names, run_directories, snapshot_names, cat_names): + # print('[run_name]:', run_name) + # print('[run_directory]:', run_directory) + # print('[snapshot_name]:', snapshot_name) + # print('[cat_name]:', cat_name) + + leg = run_name + try: + snap = load_snap(run_directory + '/' + snapshot_name) + catalogue = load_catalogue(run_directory + '/' + cat_name) + + meta = snap.metadata + BoxSize = meta.boxsize[0].value + cosmo = meta.cosmology + aexp = meta.a + z = meta.redshift + rho_crit = cosmo.critical_density(meta.z).value + ez = cosmo.efunc(z) + Ob = meta.cosmology.Ob(z) + Om = meta.cosmology.Om(z) + h = meta.cosmology.h + fb = Ob / Om + + hse_bias = 0.8 + print('Reading halos') + df_halo = pd.DataFrame() + df_halo['GroupNumber'] = catalogue.ids.id - 1 # Halo IDs + df_halo['structuretype'] = catalogue.structure_type.structuretype # Type of structure + df_halo['m500'] = catalogue.spherical_overdensities.mass_500_rhocrit.to('Msun') # M500 in Msun + df_halo['r500'] = catalogue.spherical_overdensities.r_500_rhocrit.to('Mpc') / aexp # co-moving r500 in Mpc + df_halo['m500'] = df_halo.m500 * hse_bias + df_halo['r500'] = df_halo.r500 * hse_bias ** (1. / 3) + df_halo['CoP_x'] = catalogue.positions.xcminpot.to('Mpc') / aexp # co-moving coordinates in Mpc + df_halo['CoP_y'] = catalogue.positions.ycminpot.to('Mpc') / aexp # co-moving coordinates in Mpc + df_halo['CoP_z'] = catalogue.positions.zcminpot.to('Mpc') / aexp # co-moving coordinates in Mpc + df_halo['kT500'] = G * df_halo.m500 * u.Msun.to(u.gram) * mu * mp / (2 * df_halo.r500 * aexp * u.Mpc.to(u.cm)) / ergs_keV # keV + df_halo['rho500'] = 500 * rho_crit * fb # g/cm^3 + df_halo['Pe500'] = df_halo.kT500 * ergs_keV * ne_n * (df_halo.rho500 / (mu * mp)) # ergs/cm^3 + df_halo['ne500'] = df_halo.Pe500 / (df_halo.kT500 * ergs_keV) # cm^-3 + df_halo['S500'] = df_halo.kT500 / (df_halo.ne500) ** (2./3.) # keV cm^2 + df_halo['P500'] = df_halo.kT500 * (df_halo.ne500) # keV cm^-3 + + df_halo = df_halo.loc[df_halo.m500 >= 1e13] + df_halo = df_halo.loc[df_halo.structuretype == 10] + df_halo.reset_index(inplace=True, drop=True) + # print(df_halo) + + n_halos = len(df_halo.index) + + num_processes = 16 + + if n_halos < num_processes: + num_processes = n_halos + + # calculate the chunk size as an integer + chunk_size = int(np.ceil(n_halos/num_processes)) + + # will work even if the length of the dataframe is not evenly divisible by num_processes + chunks = [df_halo.iloc[df_halo.index[j:j + chunk_size]] for j in range(0, n_halos, chunk_size)] + + print('Reading gas particles') + with closing(multiprocessing.Pool(processes=num_processes)) as pool: + func = partial(read_hot_gas_props, run_directory, snapshot_name, cat_name, obs_data_dir) + result = pool.map(func, chunks) + df_all = pd.concat(result, ignore_index=True) + + df_all.sort_values(by=['GroupNumber', 'distance'], inplace=True) + + df_all = df_all.loc[df_all.temp >= 1e5] + df_all = df_all.loc[df_all.sfr <= 0] + + bin_centers = np.arange(12.8, 15.4, 0.2) + bin_edges = (bin_centers[1:] + bin_centers[:-1]) / 2. + bin_centers = (bin_edges[1:] + bin_edges[:-1]) / 2. + + # Ysz flux + inside = df_all.loc[df_all.distance <= 5] + groups = inside.groupby(['GroupNumber', 'm500'], as_index=False) + + halos = groups.ypar.sum() + medians, bin_edges, binnumber = stats.binned_statistic(np.log10(halos.m500.values), np.log10(halos.ypar.values), statistic='median', bins=bin_edges) + n, bin_edges, binnumber = stats.binned_statistic(np.log10(halos.m500.values), np.log10(halos.ypar.values), statistic='count', bins=bin_edges) + per10, bin_edges, binnumber = stats.binned_statistic(np.log10(halos.m500.values), np.log10(halos.ypar.values), statistic=lambda y: np.percentile(y, 10), bins=bin_edges) + per90, bin_edges, binnumber = stats.binned_statistic(np.log10(halos.m500.values), np.log10(halos.ypar.values), statistic=lambda y: np.percentile(y, 90), bins=bin_edges) + mask = np.where(n >= 1) + + ax2.plot(bin_centers[mask], medians[mask], lw=2, label=leg) + + # df_all = df_all.loc[df_all.in_FOF == True] + # X-ray Luminosity + inside = df_all.loc[df_all.distance <= 1] + groups = inside.groupby(['GroupNumber', 'm500'], as_index=False) + + halos = groups.Lx.sum() + medians, bin_edges, binnumber = stats.binned_statistic(np.log10(halos.m500.values), np.log10(halos.Lx.values), statistic='median', bins=bin_edges) + n, bin_edges, binnumber = stats.binned_statistic(np.log10(halos.m500.values), np.log10(halos.Lx.values), statistic='count', bins=bin_edges) + per10, bin_edges, binnumber = stats.binned_statistic(np.log10(halos.m500.values), np.log10(halos.Lx.values), statistic=lambda y: np.percentile(y, 10), bins=bin_edges) + per90, bin_edges, binnumber = stats.binned_statistic(np.log10(halos.m500.values), np.log10(halos.Lx.values), statistic=lambda y: np.percentile(y, 90), bins=bin_edges) + mask = np.where(n >= 1) + + ax1.plot(bin_centers[mask], medians[mask], lw=2, alpha=1, zorder=10, label=leg) + + # Density and Entropy profiles + + inside = df_all.loc[(df_all.m500 >= 5.25e13) & (df_all.m500 <= 2e14)] + # inside = df_all.loc[(df_all.m500 >= 5.75e13) & (df_all.m500 <= 1.3e14)] + # inside = df_all.loc[(df_all.m500 >= 6e13) & (df_all.m500 <= 1.17e14)] + + print('median = %.2e' % inside.m500.median()) + + #old + # inside = df_all.loc[(df_all.m500 >= 5.25e13) & (df_all.m500 <= 2e14)] + # inside = df_all.loc[(df_all.m500 >= 5.75e13) & (df_all.m500 <= 1.5e14)] + + full_index = np.arange(len(inside.GroupNumber.unique())) + + + df_rho = pd.DataFrame() + df_S = pd.DataFrame() + df_S_S500 = pd.DataFrame() + df_P = pd.DataFrame() + for j in range(len(centers)): + v_j = 'v_' + str(j) + df_i = inside.copy() + df_i = df_i.loc[(df_i.distance >= r_bins[j]) & (df_i.distance <= r_bins[j + 1])] + df_i['volume'] = (4./3) * np.pi * (((df_i.r500 * aexp * u.Mpc.to(u.cm) * r_bins[j + 1]) ** 3.) - ((df_i.r500 * aexp * u.Mpc.to(u.cm) * r_bins[j]) ** 3.)) + df_i['rho_tot'] = df_i.mass / df_i.volume + df_i['x_i'] = df_i.ne_nH / ((df_i.ne_nH + df_i.ni_nH) * df_i.mu * mp) + df_i['ne'] = df_i.rho_tot * df_i.x_i + df_i['wtemp'] = df_i.temp * df_i.mass + groups = df_i.groupby(['GroupNumber', 'm500']) + kT = kb / ergs_keV * groups.wtemp.sum() / groups.mass.sum() + p = kT * groups.ne.sum() + s = kT / groups.ne.sum() ** (2./3) + p /= groups.head(1).P500.median() + df_S[v_j] = s.reset_index(drop=True).reindex(full_index).values + s /= groups.head(1).S500.median() + df_S_S500[v_j] = s.reset_index(drop=True).reindex(full_index).values + df_rho[v_j] = groups.rho_tot.sum().reset_index(drop=True).reindex(full_index).values / rho_crit + df_P[v_j] = p.reset_index(drop=True).reindex(full_index).values + + ax3.plot(centers, df_rho.median().values * (centers ** 2.), lw=2, label=leg) + # ax3.fill_between(centers, df_rho.quantile(.14).values * (centers ** 2.), df_rho.quantile(.84).values * (centers ** 2.), alpha=0.3) + + ax4.plot(centers, df_S.median().values, lw=2, label=leg) + # ax4.fill_between(centers, df_S.quantile(.14).values, df_S.quantile(.84).values, alpha=0.3) + + ax5.plot(centers, df_S_S500.median().values, lw=2, label=leg) + # ax5.fill_between(centers, df_S_S500.quantile(.14).values, df_S_S500.quantile(.84).values, alpha=0.3) + + ax6.plot(centers, df_P.median().values * (centers ** 2.), lw=2, label=leg) + + except: + pass + + # ax1.plot(bahamas_obs_Lx.x, bahamas_obs_Lx.y, c='C4', label='$\mathrm{BAHAMAS}_\mathrm{obs}$') + # ax1.plot(bahamas_true_Lx.x, bahamas_true_Lx.y, c='C9', label='$\mathrm{BAHAMAS}_\mathrm{true}$') + ax1.plot(bahamas_LX.x, bahamas_LX.y, c='C4', label='BAHAMAS') + ax1.fill_between(bahamas_LX.x, bahamas_LX.ymin, bahamas_LX.ymax, facecolor='C4', alpha=0.3) + ax1.set_xlabel('$\mathrm{log}_{10} (M_{500, \mathrm{hse}} \,\, [\mathrm{M}_\odot])$') + ax1.set_ylabel('$\mathrm{log}_{10} (L_{0.5-2.0 \mathrm{keV, hse}} \,\, [\mathrm{erg/s}])$') + ax1.set_xlim(13, 15) + ax1.set_ylim(41, 45.5) + ax1.set_xticks(np.arange(13, 15.1, 0.5)) + ax1.set_yticks(np.arange(41, 45.6, 1)) + ax1.legend(frameon=False, loc=2) + fig1.savefig(f'{output_path}/hgas_Lx.png') + + # ax2.plot(bahamas_obs_tSZ.x, bahamas_obs_tSZ.y, c='C4', label='BAHAMAS$_\mathrm{obs}$') + # ax2.plot(bahamas_true_tSZ.x, bahamas_true_tSZ.y, c='C9', label='BAHAMAS$_\mathrm{true}$') + ax2.plot(bahamas_Ysz.x, bahamas_Ysz.y, c='C4', label='BAHAMAS') + ax2.fill_between(bahamas_Ysz.x, bahamas_Ysz.ymin, bahamas_Ysz.ymax, facecolor='C4', alpha=0.3) + ax2.errorbar(planck_tSZ.x, planck_tSZ.y, yerr=[planck_tSZ.y - planck_tSZ.ymin, planck_tSZ.ymax - planck_tSZ.y], marker='o', ls='None', lw=1, capsize=2, ms=5, c='k', label='Planck (2015)') + ax2.set_xlabel('$\mathrm{log}_{10} (M_{500, \mathrm{hse}} \,\, [\mathrm{M}_\odot])$') + ax2.set_ylabel('$\mathrm{log}_{10} (\mathrm{E}(z)^{-2/3} Y_\mathrm{SZ, hse}(<5r_{500}) D_A^2 \,\, [\mathrm{Mpc}^2])$') + ax2.set_xlim(13, 15) + ax2.set_ylim(-6.5, -3.5) + ax2.legend(frameon=False, loc=2) + fig2.savefig(f'{output_path}/hgas_tSZ.png') + + # ax3.plot(bahamas_obs_rho.x, bahamas_obs_rho.y, c='C4', label='$\mathrm{BAHAMAS}_\mathrm{obs}$') + ax3.plot(bahamas_rho.x, bahamas_rho.y, c='C4', label='BAHAMAS') + ax3.fill_between(bahamas_rho.x, bahamas_rho.ymin, bahamas_rho.ymax, facecolor='C4', alpha=0.3) + x_rho = 10 ** sun_ne.iloc[:-2].log_r + y_rho = 10 ** sun_ne.iloc[:-2].log_ne * mu * mp / ne_n / rho_crit * x_rho ** 2 + y_rho1 = 10 ** sun_ne.iloc[:-2].log_err_m * mu * mp / ne_n / rho_crit * x_rho ** 2 + y_rho2 = 10 ** sun_ne.iloc[:-2].log_err_p * mu * mp / ne_n / rho_crit * x_rho ** 2 + ax3.errorbar(x_rho[::3], y_rho[::3], yerr=[y_rho[::3] - y_rho1[::3], y_rho2[::3] - y_rho[::3]], marker='o', ls='None', lw=1, capsize=2, ms=5, c='k', label='Sun+ (2009)') + ax3.set_xscale('log') + ax3.set_yscale('log') + ax3.set_xlim(.03,3) + ax3.set_ylim(0.5,100) + ax3.set_xlabel('$r/r_{500, \mathrm{hse}}$') + ax3.set_ylabel('$\\rho/\\rho_\mathrm{crit} \,\, (r/r_{500, \mathrm{hse}})^2$') + ax3.legend(frameon=False, loc=2) + fig3.savefig(f'{output_path}/hgas_rho_r.png') + + + ax4.set_xscale('log') + ax4.set_yscale('log') + ax4.set_xlim(.03,3) + ax4.set_ylim(3e1,2e3) + ax4.set_xlabel('$r/r_{500, \mathrm{hse}}$') + ax4.set_ylabel('$S \,\, [\mathrm{keV} \, \mathrm{cm}^2]$') + ax4.legend(frameon=False, loc=2) + fig4.savefig(f'{output_path}/hgas_S_r.png') + + ax5.errorbar(sun_S.r_r500, sun_S.S_S500_50, yerr=[sun_S.S_S500_50 - sun_S.S_S500_10, sun_S.S_S500_90 - sun_S.S_S500_50], marker='o', ls='None', lw=1, capsize=2, ms=5, c='k', label='Sun+ (2009)') + ax5.errorbar(ras_S.r_r500, ras_S.S_S500_50, yerr=[ras_S.S_S500_50 - ras_S.S_S500_10, ras_S.S_S500_90 - ras_S.S_S500_50], marker='s', ls='None', lw=1, capsize=2, ms=5, c='k', label='Rasmussen') + + ax5.plot(bahamas_S_S500.x, bahamas_S_S500.y, c='C4', label='BAHAMAS') + ax5.fill_between(bahamas_S_S500.x, bahamas_S_S500.ymin, bahamas_S_S500.ymax, facecolor='C4', alpha=0.3) + ax5.set_xscale('log') + ax5.set_yscale('log') + ax5.set_xlim(.03,3) + ax5.set_ylim(1e-1,10) + ax5.set_xlabel('$r/r_{500, \mathrm{hse}}$') + ax5.set_ylabel('$S / S_{500, \mathrm{hse}}$') + ax5.legend(frameon=False, loc=2) + fig5.savefig(f'{output_path}/hgas_S_S500_r.png') + + x_P = 10 ** sun_P.log_r + y_P = 10 ** sun_P.log_P / (fb / 0.175) * (0.73 / h) ** (8./3.) + + err_P_m = (y_P - 10 ** sun_P.log_err_m / (fb / 0.175) * (0.73 / h) ** (8./3.)) * x_P ** 2 + err_P_p = (10 ** sun_P.log_err_p / (fb / 0.175) * (0.73 / h) ** (8./3.) - y_P) * x_P ** 2 + + ax6.errorbar(x_P[1::3], y_P[1::3] * x_P[1::3] ** 2, yerr=[err_P_m[1::3], err_P_p[1::3]], marker='o', ls='None', lw=1, capsize=2, ms=5, c='k', label='Sun+ (2011)') + ax6.plot(bahamas_P.x, bahamas_P.y, c='C4', label='BAHAMAS') + ax6.fill_between(bahamas_P.x, bahamas_P.ymin, bahamas_P.ymax, facecolor='C4', alpha=0.3) + + ax6.set_xscale('log') + ax6.set_yscale('log') + ax6.set_xlim(.03,3) + ax6.set_ylim(3e-3,1) + ax6.set_xlabel('$r/r_{500, \mathrm{hse}}$') + ax6.set_ylabel('$P / P_{500, \mathrm{hse}} \,\, (r/r_{500, \mathrm{hse}})^2$') + ax6.legend(frameon=False, loc=2) + fig6.savefig(f'{output_path}/hgas_P_P500_r.png') + + +arguments = ScriptArgumentParser( + description="Creates hot has plots" +) + +plt.style.use(arguments.stylesheet_location) + +plot_hot_gas_props(arguments) \ No newline at end of file diff --git a/flamingo/scripts/interpolate_X_Ray_redshift.py b/flamingo/scripts/interpolate_X_Ray_redshift.py new file mode 100644 index 00000000..4939cc44 --- /dev/null +++ b/flamingo/scripts/interpolate_X_Ray_redshift.py @@ -0,0 +1,372 @@ +import h5py + + +import numpy as np +from swiftsimio import load +from numba import jit +from unyt import g, cm, mp, erg, s + +class interpolate: + def init(self): + pass + + def load_table(self, obs_data_dir, band): + self.table = h5py.File(obs_data_dir + 'X_Ray_table_redshifts.hdf5', 'r') + self.X_Ray = self.table[band]['emissivities'][()] + self.He_bins = self.table['/Bins/He_bins'][()] + self.missing_elements = self.table['/Bins/Missing_element'][()] + + self.density_bins = self.table['/Bins/Density_bins/'][()] + self.temperature_bins = self.table['/Bins/Temperature_bins/'][()] + self.redshift_bins = np.array([0.0, 0.2, 0.4, 0.6, 0.8, 1.0]) + self.dn = 0.2 + self.dT = 0.1 + + self.solar_metallicity = self.table['/Bins/Solar_metallicities/'][()] + +@jit(nopython = True) +def find_dx(subdata, bins, idx_0): + dx_p = np.zeros(len(subdata)) + for i in range(len(subdata)): + if (subdata[i] < bins[0]): + dx_p[i] = 0 + elif (subdata[i] > bins[-1]): + dx_p[i] = np.abs(bins[-1] - bins[-2]) + else: + dx_p[i] = np.abs(bins[idx_0[i]] - subdata[i]) + + return dx_p + +@jit(nopython = True) +def find_idx(subdata, bins): + idx_p = np.zeros((len(subdata), 2)) + for i in range(len(subdata)): + if (subdata[i] < bins[0]): + idx_p[i, :] = np.array([0, 1]) + elif (subdata[i] > bins[-1]): + idx_p[i, :] = np.array([len(bins)-2, len(bins)-1]) + else: + idx_p[i, :] = np.sort(np.argsort(np.abs(bins - subdata[i]))[:2]) + + return idx_p + +@jit(nopython = True) +def find_idx_z(subdata, bins): + idx_p = np.zeros(2) + if (subdata < bins[0]): + idx_p = np.array([0, 1]) + elif (subdata > bins[-1]): + idx_p = np.array([len(bins)-2, len(bins)-1]) + else: + idx_p = np.sort(np.argsort(np.abs(bins - subdata))[:2]) + + return idx_p + +@jit(nopython = True) +def find_dx_z(subdata, bins, idx_0): + dx_p = 0 + if (subdata < bins[0]): + dx_p = 0 + elif (subdata > bins[-1]): + dx_p = 1 + else: + dx_p = np.abs(subdata - bins[idx_0]) / (bins[idx_0 + 1] - bins[idx_0]) + + return dx_p + +@jit(nopython = True) +def find_idx_he(subdata, bins): + num_bins = len(bins) + idx_p = np.zeros((len(subdata), 2)) + for i in range(len(subdata)): + + # When closest to the highest bin, or above the highest bin, return the one but highest bin, + # otherwise we will select a second bin which is outside the binrange + bin_below = min(np.argsort(np.abs(bins[bins <= subdata[i]] - subdata[i]))[0], num_bins - 2) + idx_p[i, :] = np.array([bin_below, bin_below + 1]) + + return idx_p + +@jit(nopython = True) +def find_dx_he(subdata, bins, idx_0): + dx_p = np.zeros(len(subdata)) + for i in range(len(subdata)): + if (subdata[i] < bins[0]): + dx_p[i] = 0 + elif (subdata[i] > bins[-1]): + dx_p[i] = 1 + else: + dx_p[i] = np.abs(subdata[i] - bins[idx_0[i]]) / (bins[idx_0[i]+1] - bins[idx_0[i]]) + # dx_p1[i] = np.abs(bins[idx_0[i+1]] - subdata[i]) + + return dx_p + +@jit(nopython = True) +def get_table_interp(dn, dT, dx_T, dx_n, idx_T, idx_n, idx_he, dx_he, idx_z, dx_z, X_Ray, abundance_to_solar, z_index): + f_n_T_Z = np.zeros(len(idx_n[:, 0])) + + t_z = (1 - dx_z) + d_z = dx_z + + for i in range(len(idx_n[:, 0])): + t_T = (dT - dx_T[i]) / dT + d_T = dx_T[i] / dT + + t_n = (dn - dx_n[i]) / dn + d_n = dx_n[i] / dn + + d_he = dx_he[i] + t_he = (1 - dx_he[i]) + + # if i == len(idx_n[:, 0]) - 1: + # print(t_T, d_T, t_n, d_n, d_he, t_he) + # print(X_Ray.shape, z_index, idx_he, idx_T, idx_n) + f_n_T = t_T * t_n * t_he * t_z * X_Ray[idx_z[0], idx_he[i, 0], :, idx_T[i, 0], idx_n[i, 0]] + f_n_T += t_T * t_n * d_he * t_z * X_Ray[idx_z[0], idx_he[i, 1], :, idx_T[i, 0], idx_n[i, 0]] + f_n_T += t_T * d_n * t_he * t_z * X_Ray[idx_z[0], idx_he[i, 0], :, idx_T[i, 0], idx_n[i, 1]] + f_n_T += d_T * t_n * t_he * t_z * X_Ray[idx_z[0], idx_he[i, 0], :, idx_T[i, 1], idx_n[i, 0]] + f_n_T += t_T * d_n * d_he * t_z * X_Ray[idx_z[0], idx_he[i, 1], :, idx_T[i, 0], idx_n[i, 1]] + f_n_T += d_T * t_n * d_he * t_z * X_Ray[idx_z[0], idx_he[i, 1], :, idx_T[i, 1], idx_n[i, 0]] + f_n_T += d_T * d_n * t_he * t_z * X_Ray[idx_z[0], idx_he[i, 0], :, idx_T[i, 1], idx_n[i, 1]] + f_n_T += d_T * d_n * d_he * t_z * X_Ray[idx_z[0], idx_he[i, 1], :, idx_T[i, 1], idx_n[i, 1]] + + f_n_T += t_T * t_n * t_he * d_z * X_Ray[idx_z[1], idx_he[i, 0], :, idx_T[i, 0], idx_n[i, 0]] + f_n_T += t_T * t_n * d_he * d_z * X_Ray[idx_z[1], idx_he[i, 1], :, idx_T[i, 0], idx_n[i, 0]] + f_n_T += t_T * d_n * t_he * d_z * X_Ray[idx_z[1], idx_he[i, 0], :, idx_T[i, 0], idx_n[i, 1]] + f_n_T += d_T * t_n * t_he * d_z * X_Ray[idx_z[1], idx_he[i, 0], :, idx_T[i, 1], idx_n[i, 0]] + f_n_T += t_T * d_n * d_he * d_z * X_Ray[idx_z[1], idx_he[i, 1], :, idx_T[i, 0], idx_n[i, 1]] + f_n_T += d_T * t_n * d_he * d_z * X_Ray[idx_z[1], idx_he[i, 1], :, idx_T[i, 1], idx_n[i, 0]] + f_n_T += d_T * d_n * t_he * d_z * X_Ray[idx_z[1], idx_he[i, 0], :, idx_T[i, 1], idx_n[i, 1]] + f_n_T += d_T * d_n * d_he * d_z * X_Ray[idx_z[1], idx_he[i, 1], :, idx_T[i, 1], idx_n[i, 1]] + + # if i == len(idx_n[:, 0]) - 1: + # print(f_n_T[-1]) + + #Apply linear scaling for removed metals + f_n_T_Z_temp = f_n_T[-1] + for j in range(len(f_n_T) - 1): + f_n_T_Z_temp -= (f_n_T[-1] - f_n_T[j]) * abundance_to_solar[i, j] + + f_n_T_Z[i] = f_n_T_Z_temp + + return f_n_T_Z + +def interpolate_X_Ray(data_n, data_T, element_mass_fractions, redshift, band = 'soft', fill_value = None): + + z_options = np.array([0, 0.12, 0.25, 0.37, 0.5, 0.75, 1.0, 1.25, 1.5, 1.75, 2.0, 2.25, 2.5, 2.75, 3.0]) + z_index = np.argmin(np.abs(z_options - redshift)) + + #Initialise interpolation class + interp = interpolate() + interp.load_table(band) + + #Initialise the emissivity array which will be returned + emissivities = np.zeros_like(data_n, dtype = float) + + #Create density mask, round to avoid numerical errors + density_mask = (data_n >= np.round(interp.density_bins.min(), 1)) & (data_n <= np.round(interp.density_bins.max(), 1)) + #Create temperature mask, round to avoid numerical errors + temperature_mask = (data_T >= np.round(interp.temperature_bins.min(), 1)) & (data_T <= np.round(interp.temperature_bins.max(), 1)) + + #Combine masks + joint_mask = density_mask & temperature_mask + + #Check if within density and temperature bounds + density_bounds = np.sum(density_mask) == density_mask.shape[0] + temperature_bounds = np.sum(temperature_mask) == temperature_mask.shape[0] + if ~(density_bounds & temperature_bounds): + #If no fill_value is set, return an error with some explanation + if fill_value == None: + print('Temperature or density are outside of the interpolation range and no fill_value is supplied') + print('Temperature ranges between log(T) = 5 and log(T) = 9.5') + print('Density ranges between log(nH) = -8 and log(nH) = 6') + print('Set the kwarg "fill_value = some value" to set all particles outside of the interpolation range to "some value"') + print('Or limit your particle data set to be within the interpolation range') + print('\n') + print('Interpolation will be capped, all particles outside of the interpolation range will be set to the edges of that range') + joint_mask = data_n > -100 #Everything + else: + emissivities[~joint_mask] = fill_value + + + + + + + + mass_fraction = np.zeros((len(data_n[joint_mask]), 9)) + + #get individual mass fraction + mass_fraction[:, 0] = element_mass_fractions.hydrogen[joint_mask] + mass_fraction[:, 1] = element_mass_fractions.helium[joint_mask] + mass_fraction[:, 2] = element_mass_fractions.carbon[joint_mask] + mass_fraction[:, 3] = element_mass_fractions.nitrogen[joint_mask] + mass_fraction[:, 4] = element_mass_fractions.oxygen[joint_mask] + mass_fraction[:, 5] = element_mass_fractions.neon[joint_mask] + mass_fraction[:, 6] = element_mass_fractions.magnesium[joint_mask] + mass_fraction[:, 7] = element_mass_fractions.silicon[joint_mask] + mass_fraction[:, 8] = element_mass_fractions.iron[joint_mask] + + + # # From Cooling tables, integrate into X-Ray tables in a future version + # max_mass_fractions = np.array([7.5597578e-01, 2.5954419e-01, 7.1018077e-03, 2.0809614e-03, + # 1.7216533e-02, 3.7735226e-03, 2.1262325e-03, 1.9969980e-03, + # 3.8801527e-03]) + + # # At the moment the tables only go up to 0.5 * solar metallicity for He + # # Enforce this for all metals to have consistency + # clip_mass_fractions = max_mass_fractions + # # Reset such to mass fractions of Hydrogen = 1 + # clip_mass_fractions /= clip_mass_fractions[0] + # mass_fraction = np.clip(mass_fraction, a_min = 0, a_max = clip_mass_fractions) + + + #Find density offsets + idx_n = find_idx(data_n[joint_mask], interp.density_bins) + dx_n = find_dx(data_n[joint_mask], interp.density_bins, idx_n[:, 0].astype(int)) + + #Find temperature offsets + idx_T = find_idx(data_T[joint_mask], interp.temperature_bins) + dx_T = find_dx(data_T[joint_mask], interp.temperature_bins, idx_T[:, 0].astype(int)) + + #Find element offsets + #mass of ['hydrogen', 'helium', 'carbon', 'nitrogen', 'oxygen', 'neon', 'magnesium', 'silicon', 'iron'] + element_masses = [1, 4.0026, 12.0107, 14.0067, 15.999, 20.1797, 24.305, 28.0855, 55.845] + + #Calculate the abundance wrt to solar + # abundances = mass_fraction / np.array(element_masses) + abundances = (mass_fraction / np.expand_dims(mass_fraction[:, 0], axis = 1)) / np.array(element_masses) + + # Clip to solar metallicity + # abundances = np.clip(abundances, a_min = 0, a_max = 10**interp.solar_metallicity) + + #Calculate abundance offsets using solar mass fractions + # This should be less susceptible to changes in the hydrogen mass fraction + solar_mass_fractions = [7.0030355e-01, 2.5954419e-01, 7.1018077e-03, 2.0809614e-03, + 1.7216533e-02, 3.7735226e-03, 2.1262325e-03, 1.9969980e-03, + 3.8801527e-03] + abundance_to_solar = 1 - mass_fraction / solar_mass_fractions + + # Calculate abundance offsets using solar abundances + # abundance_to_solar = 1 - abundances / 10**interp.solar_metallicity + + abundance_to_solar = np.c_[abundance_to_solar[:, :-1], abundance_to_solar[:, -2], abundance_to_solar[:, -2], abundance_to_solar[:, -1]] #Add columns for Calcium and Sulphur and add Iron at the end + + #Find helium offsets + idx_he = find_idx_he(np.log10(abundances[:, 1]), interp.He_bins) + dx_he = find_dx_he(np.log10(abundances[:, 1]), interp.He_bins, idx_he[:, 0].astype(int)) + + + # Find redshift offsets + idx_z = find_idx_z(redshift, interp.redshift_bins) + dx_z = find_dx_z(redshift, interp.redshift_bins, idx_z[0].astype(int)) + + # print('Start interpolation') + # print(interp.X_Ray.shape, z_index, idx_he[0,:], idx_T[0,:], idx_n[0,:]) + emissivities[joint_mask] = get_table_interp(interp.dn, interp.dT, dx_T, dx_n, idx_T.astype(int), idx_n.astype(int), idx_he.astype(int), dx_he, idx_z.astype(int), dx_z, interp.X_Ray, abundance_to_solar[:, 2:], z_index) + + return emissivities + + + +def interpolate_X_Ray_pandas(data_n, data_T, df_elements, redshift, obs_data_dir, band = 'soft', fill_value = None): + + z_options = np.array([0, 0.12, 0.25, 0.37, 0.5, 0.75, 1.0, 1.25, 1.5, 1.75, 2.0, 2.25, 2.5, 2.75, 3.0]) + z_index = np.argmin(np.abs(z_options - redshift)) + + #Initialise interpolation class + interp = interpolate() + interp.load_table(obs_data_dir, band) + + #Initialise the emissivity array which will be returned + emissivities = np.zeros_like(data_n, dtype = float) + + #Create density mask, round to avoid numerical errors + density_mask = (data_n >= np.round(interp.density_bins.min(), 1)) & (data_n <= np.round(interp.density_bins.max(), 1)) + #Create temperature mask, round to avoid numerical errors + temperature_mask = (data_T >= np.round(interp.temperature_bins.min(), 1)) & (data_T <= np.round(interp.temperature_bins.max(), 1)) + + #Combine masks + joint_mask = density_mask & temperature_mask + + #Check if within density and temperature bounds + density_bounds = np.sum(density_mask) == density_mask.shape[0] + temperature_bounds = np.sum(temperature_mask) == temperature_mask.shape[0] + if ~(density_bounds & temperature_bounds): + #If no fill_value is set, return an error with some explanation + if fill_value == None: + print('Temperature or density are outside of the interpolation range and no fill_value is supplied') + print('Temperature ranges between log(T) = 5 and log(T) = 9.5') + print('Density ranges between log(nH) = -8 and log(nH) = 6') + print('Set the kwarg "fill_value = some value" to set all particles outside of the interpolation range to "some value"') + print('Or limit your particle data set to be within the interpolation range') + print('\n') + print('Interpolation will be capped, all particles outside of the interpolation range will be set to the edges of that range') + joint_mask = data_n > -100 #Everything + else: + emissivities[~joint_mask] = fill_value + + + #get individual mass fraction + mass_fraction = df_elements.to_numpy() + + # # From Cooling tables, integrate into X-Ray tables in a future version + # max_mass_fractions = np.array([7.5597578e-01, 2.5954419e-01, 7.1018077e-03, 2.0809614e-03, + # 1.7216533e-02, 3.7735226e-03, 2.1262325e-03, 1.9969980e-03, + # 3.8801527e-03]) + + # # At the moment the tables only go up to 0.5 * solar metallicity for He + # # Enforce this for all metals to have consistency + # clip_mass_fractions = max_mass_fractions + # # Reset such to mass fractions of Hydrogen = 1 + # clip_mass_fractions /= clip_mass_fractions[0] + # mass_fraction = np.clip(mass_fraction, a_min = 0, a_max = clip_mass_fractions) + + + #Find density offsets + idx_n = find_idx(data_n[joint_mask], interp.density_bins) + dx_n = find_dx(data_n[joint_mask], interp.density_bins, idx_n[:, 0].astype(int)) + + #Find temperature offsets + idx_T = find_idx(data_T[joint_mask], interp.temperature_bins) + dx_T = find_dx(data_T[joint_mask], interp.temperature_bins, idx_T[:, 0].astype(int)) + + #Find element offsets + #mass of ['hydrogen', 'helium', 'carbon', 'nitrogen', 'oxygen', 'neon', 'magnesium', 'silicon', 'iron'] + element_masses = [1, 4.0026, 12.0107, 14.0067, 15.999, 20.1797, 24.305, 28.0855, 55.845] + + #Calculate the abundance wrt to solar + # abundances = mass_fraction / np.array(element_masses) + abundances = (mass_fraction / np.expand_dims(mass_fraction[:, 0], axis = 1)) / np.array(element_masses) + + # Clip to solar metallicity + # abundances = np.clip(abundances, a_min = 0, a_max = 10**interp.solar_metallicity) + + #Calculate abundance offsets using solar mass fractions + # This should be less susceptible to changes in the hydrogen mass fraction + solar_mass_fractions = [7.0030355e-01, 2.5954419e-01, 7.1018077e-03, 2.0809614e-03, + 1.7216533e-02, 3.7735226e-03, 2.1262325e-03, 1.9969980e-03, + 3.8801527e-03] + abundance_to_solar = 1 - mass_fraction / solar_mass_fractions + + # Calculate abundance offsets using solar abundances + # abundance_to_solar = 1 - abundances / 10**interp.solar_metallicity + + abundance_to_solar = np.c_[abundance_to_solar[:, :-1], abundance_to_solar[:, -2], abundance_to_solar[:, -2], abundance_to_solar[:, -1]] #Add columns for Calcium and Sulphur and add Iron at the end + + #Find helium offsets + idx_he = find_idx_he(np.log10(abundances[:, 1]), interp.He_bins) + dx_he = find_dx_he(np.log10(abundances[:, 1]), interp.He_bins, idx_he[:, 0].astype(int)) + + + # Find redshift offsets + idx_z = find_idx_z(redshift, interp.redshift_bins) + dx_z = find_dx_z(redshift, interp.redshift_bins, idx_z[0].astype(int)) + + # print('Start interpolation') + # print(interp.X_Ray.shape, z_index, idx_he[0,:], idx_T[0,:], idx_n[0,:]) + emissivities[joint_mask] = get_table_interp(interp.dn, interp.dT, dx_T, dx_n, idx_T.astype(int), idx_n.astype(int), idx_he.astype(int), dx_he, idx_z.astype(int), dx_z, interp.X_Ray, abundance_to_solar[:, 2:], z_index) + + return emissivities + + \ No newline at end of file From 16e7b0f9c433daf434d2d9d72afd28a7caa51e9c Mon Sep 17 00:00:00 2001 From: Jaime Salcido Date: Thu, 19 Aug 2021 12:17:20 +0100 Subject: [PATCH 2/2] Clean scripts directory Deleted pycache directory. --- .../interpolate_X_Ray_redshift.cpython-36.pyc | Bin 9857 -> 0 bytes .../__pycache__/load_sfh_data.cpython-36.pyc | Bin 4740 -> 0 bytes 2 files changed, 0 insertions(+), 0 deletions(-) delete mode 100644 flamingo/scripts/__pycache__/interpolate_X_Ray_redshift.cpython-36.pyc delete mode 100644 flamingo/scripts/__pycache__/load_sfh_data.cpython-36.pyc diff --git a/flamingo/scripts/__pycache__/interpolate_X_Ray_redshift.cpython-36.pyc b/flamingo/scripts/__pycache__/interpolate_X_Ray_redshift.cpython-36.pyc deleted file mode 100644 index 90f227916c15aacbdbee674138b85142526cbd8e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 9857 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 XhrdN-vQWt4FN42qVX|;3{r3L=*`X@& diff --git a/flamingo/scripts/__pycache__/load_sfh_data.cpython-36.pyc b/flamingo/scripts/__pycache__/load_sfh_data.cpython-36.pyc deleted file mode 100644 index 5766c1d747ad54bd865c8cdacb943b64ca6cb16a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4740 zcma)^7E8OH$-d=W)S6e&xTL|L%S$fn*%QKBU$aizqu+)QJ8YCAcwRxvOa1Zj~3 z3xG+DS)Sw^+n(D_K0r@BCg=7O^onaHoze8tOFPq?`rjo4kus|a8ty*W=k50_7Q*iK z#6)~$OVsKsg7CdCaLJGl@Uu735TPOvkw{JPg+M|T>5)KW5`HU?upT-IRb&z;Q4)JA zR>EZLxsV@s@5djQL|1B-*-{>Bj#F>wUFCVTt~Hywr8uV2G&Q1_uWa4=Qga$+TWc!M z51%PSbF|I)DN+h~(GypVzy9ma%isOwhy5xWI=dtY2l&|;G`dg`!6y;15+V`_y%oSC zgCgurKOIc)31Yjq;tH;xnoVL~NZ(J*wAC=evWxXB*8}|Q9W+#=f+|o6tw=*?B`TwZ z^cwk(W7aBRI=LMoOe+%+9I~6^Nm=OUj%n$N?R0B3rO{Se`i(&KS|4^ z!qN-bHOmX@mIZm7=yug=bl7ZpQ?-WUG}?w*Gp$y)slijW^=8dtkIiD-R)jTCG=CDT zIJzHyS*V(JODh*xr3KQp>RPK%J2qOSQsMBkj~;*e+bqd++Ni>2S5&54K7h{sU@Xq7d zJac)10^r})TDq!waaC=Zq>Fh_y@aa%s;f19O;lBhSyfdlf#|%ffo7bIh0w5aZ8j6J zkSy^LFEzB*ICE8m1-m}LkIfc;%xuaGp(i?mBc7loXfr}ja`0gPC=lrzEHNVgMtT|P zg?e%?+>7+0y%>$aGJGP=VR6g}y$Hb{4b2IQLT`*jX(TQ5;!cR-HKDPzkP*;F(*liW z1VcIzd*goeSP)%C^h9r>H#rb#iYCa|42TdliD;AK0-fOg(^PydJ7GFSlXRNIPvjF( z6#SUuLorjmbZ>^HNMcb4VxnP=nBRchNu=VP|ftx15|fLQ01>cwMtigsywJ5gCP&9Ri6rdKy`=aF`gZwTDp{K zZ3NZ&6{t4ohEKH)D#&112i1m8g+8EKqw5&IK18*ADb?l(s=^hhinQoc6+i_U3Cm*td(_<)L3qzQy~{uYE0% z>6$bz(6hTuc1Ly)d5vuKy(!xymE9G-6MCj=*qJ$XeZ=mJ8yGcy{e9of6tFwO{-kr< zXxpV?aYwD1)+Tl%FR*otblV-RdTea->-&9!edq<@pZlk5bKs`9@3CezTBYLF7V|Q! z|4f~J`{y6_-Nm1=aYc6&t+}b(VFr2S7nVjE=8-$cJuQtIOs*aaMLul zik?{aqU>0KKwen0EbWaK(K;QyP5PVo7|d+E)_VQ}Pwr?=-HVuAr_*&R;W~S)p6FI& zSM7Ex5f|r#uiBMx)3EAhCB{3Zi1#I~jNnYLrD~NJ@0nfVOj2>?@Q1Xjp@t2z8vVe6zK2z`n!RiOM!mZ z*Y5^;E-}3qVr(qQo^P}l7;z~Ga?jV_3-ny#`Y4;AA`Hg*0;_%^5YJ86v~rUz9)z24 z@!W(>AvfvIAvYPAL>6@W%w+pK3K!3#uo>l1`culIoX;u8Ws~X)Zr`>)y>VSLaKvQC z*9(V8Z^FW7NCg7dUdKwd|BXfn%b}zwiCHNrCd8PSO)Q{Kh?9~mCbJ7tQc8%jIEy<; zoD46BS+32BDKQm{C8U{9PU?>&W%v)=Gp`JMl4A18_L7*9F2)#+nv_S`j2MfeDk&$$ z{M@W6NnDk!94dd37p-aZ`G}Q7=Y`wdR_Beiz+^0{*Lv0A?