def test_consistent_init(): rdx = pyradex.Radex(species='co', collider_densities={'H2': 1e4}, column_per_bin=1e14, deltav=1.0, temperature=30, tbackground=2.73) log.debug('crate at init: {0}'.format(rdx.radex.collie.crate[0, 1])) log.debug(str((rdx.density, rdx.temperature, rdx.column, rdx._cddv))) rdx.run_radex() log.debug('crate at run: {0}'.format(rdx.radex.collie.crate[0, 1])) tex0 = rdx.tex[0].value crate0 = np.copy(rdx.radex.collie.crate) rdx = pyradex.Radex(species='co', collider_densities={'H2': 1e4}, column_per_bin=1e14, deltav=1.0, temperature=25, tbackground=2.73) log.debug('crate at temchange: {0}'.format(rdx.radex.collie.crate[0, 1])) rdx = pyradex.Radex(species='co', collider_densities={'H2': 1e4}, column_per_bin=1e14, deltav=1.0, temperature=30, tbackground=2.73) log.debug('crate at init2: {0}'.format(rdx.radex.collie.crate[0, 1])) log.debug(str((rdx.density, rdx.temperature, rdx.column, rdx._cddv))) rdx.run_radex() log.debug('crate at run2: {0}'.format(rdx.radex.collie.crate[0, 1])) tex1 = rdx.tex[0].value crate1 = np.copy(rdx.radex.collie.crate) assert tex0 == tex1 assert np.all(crate0 == crate1)
def test_thermal_opr(): # Check if H2 is specified as total H2, the thermal fraction of O/P H2 is used rdx = pyradex.Radex(species='co', collider_densities={'H2': 1e4}, column_per_bin=1e14, deltav=1.0, temperature=30, tbackground=2.73) opr = 9.0 * np.exp(-170.6 / 30) fortho = opr / (1 + opr) np.testing.assert_almost_equal(rdx.density['oH2'].value, fortho * 1e4) np.testing.assert_almost_equal(rdx.density['pH2'].value, (1 - fortho) * 1e4) rdx.temperature = 50 opr = 9.0 * np.exp(-170.6 / 50) fortho = opr / (1 + opr) np.testing.assert_almost_equal(rdx.density['oH2'].value, fortho * 1e4) np.testing.assert_almost_equal(rdx.density['pH2'].value, (1 - fortho) * 1e4) # Check that if ortho is specified, density remains unchanged rdx = pyradex.Radex(species='co', collider_densities={ 'oH2': 1e4, 'pH2': 0 }, column_per_bin=1e14, deltav=1.0, temperature=30, tbackground=2.73) assert rdx.density['oH2'].value == 1e4 rdx.temperature = 50 assert rdx.density['oH2'].value == 1e4
def test_radex_class(): R = pyradex.Radex(datapath='examples/', species='co', abundance=1e-4, column=1e15, collider_densities=None, temperature=20) assert hasattr(R, 'radex')
def test_synthspec(): R = pyradex.Radex(column=1e15, temperature=20, density=1e3) R.run_radex() wcs = np.linspace(103.0, 103.1, 1000) * u.GHz S = pyradex.synthspec.SyntheticSpectrum.from_RADEX(wcs, R, linewidth=10 * u.km / u.s)
def test_change_abundance(): R = pyradex.Radex(datapath='examples/', species='co', abundance=1e-4, column=1e15, collider_densities=None, temperature=20) totdens = R.total_density R.abundance = 1e-6 assert totdens == R.total_density
def formaldehyde_pyradex(xarr, density=4, column=13, temperature=20, xoff_v=0.0, opr=1.0, width=1.0, tbackground=2.73, grid_vwidth=1.0, debug=False, verbose=False, **kwargs): """ Use a grid of RADEX-computed models to make a model line spectrum The RADEX models have to be available somewhere. OR they can be passed as arrays. If as arrays, the form should be: texgrid = ((minfreq1,maxfreq1,texgrid1),(minfreq2,maxfreq2,texgrid2)) xarr must be a SpectroscopicAxis instance xoff_v, width are both in km/s grid_vwidth is the velocity assumed when computing the grid in km/s this is important because tau = modeltau / width (see, e.g., Draine 2011 textbook pgs 219-230) """ raise NotImplementedError("Not done yet.") import pyradex # Convert X-units to frequency in GHz xarr = xarr.as_unit('Hz', quiet=True) tb_nu_cumul = np.zeros(len(xarr)) R = pyradex.Radex( molecule='oh2co-h2', column=column, temperature=temperature, density=10**density, tbackground=tbackground, ) spec = np.sum( [(formaldehyde_vtau(xarr, Tex=float(tex[ii]), tau=float(tau[ii]), xoff_v=xoff_v, width=width, **kwargs) * (xarr.as_unit('GHz') > minfreq[ii]) * (xarr.as_unit('GHz') < maxfreq[ii])) for ii in xrange(len(tex))], axis=0) return spec
def test_consistent_abund(): with pytest.raises(ValueError): R = pyradex.Radex(datapath='examples/', species='co', abundance=1e-4, column=1e15, density=1e3) with pytest.raises(ValueError): R = pyradex.Radex(datapath='examples/', species='co', abundance=1e-4, column=1e15, collider_densities={'H2': 1e3}) with pytest.raises(ValueError): R = pyradex.Radex(datapath='examples/', species='co', abundance=1e-4, column_per_bin=1e15) with pytest.raises(ValueError): R = pyradex.Radex(datapath='examples/', species='co', abundance=None, column=None)
def test_selfconsistent_density(): rdx = pyradex.Radex(species='co', collider_densities={'H2': 1e3}, column_per_bin=1e13, temperature=20) np.testing.assert_almost_equal(rdx.total_density.value, 1e3) rdx.temperature = 30 np.testing.assert_almost_equal(rdx.total_density.value, 1e3) rdx.density = rdx.density np.testing.assert_almost_equal(rdx.total_density.value, 1e3) rdx.density = {'H2': 1e3} np.testing.assert_almost_equal(rdx.total_density.value, 1e3) rdx.density = {'oH2': 990, 'pH2': 10} np.testing.assert_almost_equal(rdx.total_density.value, 1e3)
def test_consistent_parchanges(): rdx = pyradex.Radex(species='co', collider_densities={'H2': 1e3}, column_per_bin=1e13, temperature=20) np.testing.assert_almost_equal(rdx.abundance, 1e13 / (1e3 * (u.pc.to(u.cm)))) assert rdx.locked_parameter == 'column' rdx.abundance = 1e-9 assert rdx.locked_parameter == 'abundance' np.testing.assert_almost_equal( rdx.total_density.to(u.cm**-3).value, 1e13 / 1e-9 / u.pc.to(u.cm)) rdx.density = 1e3 rdx.column_per_bin = 1e13 np.testing.assert_almost_equal(rdx.abundance, 1e13 / (1e3 * (u.pc.to(u.cm))))
def debugging_function_2(): abundance = 1e-8 # Here we have to specify 2 of 3: abundance, temperature, density. R = pyradex.Radex(species='h13cn@xpol', abundance=abundance, temperature=100, column=1e16, escapeProbGeom='lvg') density_array = np.logspace(4, 8.5, 100) temperature_list = [50, 100, 200] f_c_array_50K = np.zeros_like(density_array) f_c_array_100K = np.zeros_like(density_array) f_c_array_200K = np.zeros_like(density_array) f_c_array_list = [f_c_array_50K, f_c_array_100K, f_c_array_200K] list_of_tables = [] for j, temp in enumerate(temperature_list): R.temperature = temp for i, rho in enumerate(density_array): new_T = R(collider_densities={'H2': rho}, temperature=temp) list_of_tables.append(new_T) # use the correction_factor code here! f_c = correction_factor_given_radex_table(new_T, h13cn_J_lower_list) if min(new_T['Tex']) < 0: print "negative Tex encountered" print "conditions: T={0}, n={1}".format(temp, rho) f_c_array_list[j][i] = np.nan elif np.sum(new_T['lowerlevelpop']) < 0.99: print "unphysical population statistics encountered" print "conditions: T={0}, n={1}".format(temp, rho) f_c_array_list[j][i] = np.nan else: f_c_array_list[j][i] = f_c return list_of_tables, f_c_array_list
def test_radex_results(): # default parameters for radex online rdx = pyradex.Radex(species='co', collider_densities={'H2': 1e4}, column_per_bin=1e14, deltav=1.0, temperature=30, tbackground=2.73) rdx.run_radex() assert rdx.temperature.value == 30.0 # no approximates allowed assert rdx.column.value == 1e14 # LINE E_UP FREQ WAVEL T_EX TAU T_R POP POP FLUX FLUX # (K) (GHz) (um) (K) (K) UP LOW (K*km/s) (erg/cm2/s) # 1 -- 0 5.5 115.2712 2600.7576 56.131 1.786E-03 9.378E-02 3.640E-01 1.339E-01 9.983E-02 1.969E-09 # RADEX online: # Transition Frequency Tex tau TR # 1 -- 0 115.2712 54.863 1.824E-03 9.349E-02 np.testing.assert_approx_equal(rdx.tex[0].value, 56.131, 5) np.testing.assert_approx_equal(rdx.tau[0], 1.786E-03, 4) np.testing.assert_approx_equal(rdx.upperlevelpop[0], 3.640E-01, 4) np.testing.assert_approx_equal(rdx.lowerlevelpop[0], 1.339E-01, 4)
def test_thick_co(): density = { 'oH2': 100, 'pH2': 900, } RR = pyradex.Radex(datapath='examples/', species='co', column=1e17, density=density, temperature=20) FF = pyradex.fjdu.Fjdu(datapath='examples/', species='co', column=1e17, density=density, temperature=20) rtbl = RR() ftbl = FF() diff = rtbl['upperlevelpop'] - ftbl['upperlevelpop'] log.info(diff)
def debugging_function(): abundance = 1e-8 n_points = 20 # Here we have to specify 2 of 3: abundance, temperature, density. R = pyradex.Radex(species='h13cn@xpol', abundance=abundance, column=1e16, temperature=50, escapeProbGeom='lvg') density_list = [1e6] temperature_array = np.linspace(10, 400, n_points) f_c_array_n6 = np.zeros_like(temperature_array) f_c_array_list = [f_c_array_n6] list_of_tables = [] for j, density in enumerate(density_list): R.density = density for i, temp in enumerate(temperature_array): R.temperature = temp new_T = R(temperature=temp) list_of_tables.append(new_T) # use the correction_factor code here! f_c = correction_factor_given_radex_table(new_T, h13cn_J_lower_list) f_c_array_list[j][i] = f_c return list_of_tables, f_c_array_list[0]
m_logdensities = 8 min_logdens = 4 max_logdens = 9 temperatures = np.linspace(min_temp, max_temp, n_temperatures) logdensities = np.linspace(min_logdens, max_logdens, m_logdensities) sanity_grid = np.zeros((n_temperatures, m_logdensities)) iters_grid = np.zeros((n_temperatures, m_logdensities)) # Initialize radex object initial_column = 1e08 initial_density = 1e4 R = pyradex.Radex(species='h13cn@xpol', temperature=20, density=initial_density, column=initial_column) R.maxiter = 1000 # abundance_list = [2e-9, 2e-8, 7e-8] # for abundance in abundance_list: # R.abundance = abundance for i, temp in enumerate(temperatures): R.temperature = temp for j, logdens in enumerate(logdensities):
flux = CO_data['flux'].data * Jykms eflux = CO_data['eflux'].data * Jykms return z, T_d, line_width, Jup, flux, eflux # the pyradex.Radex object MUST be declared at the module level for # lnprob to work properly # .... Setup the Radex parameters # Note that N_CO is totaly degenerated with deltav, so in # principle we should fit for N_CO/deltav R = pyradex.Radex(species='co', datapath="radex_moldata", density={'oH2':fortho*10.**10.0,'pH2':(1-fortho)*10.**10.0}, column=10.**6.0, temperature=20.0, tbackground=2.7315, deltav=1.0, escapeProbGeom='lvg') # R = pyradex.fjdu.Fjdu(species='co', datapath="radex_moldata", # density={'oH2':fortho*10.**10,'pH2':(1-fortho)*10.**10}, # column=10**6, # temperature=20, # tbg=2.7315, # deltav=1, # escapeProbGeom='lvg') def replot(source):
import pyradex import pylab as pl import numpy as np R = pyradex.Radex(column=1e16, temperature=20) R.maxiter = 1000 #print R.radex.impex.molfile,R.molpath for column in np.linspace(1e14, 1e17, 10): R.column = column #R.debug = 1 R.run_radex(reuse_last=True) pl.figure(1) pl.plot(R.level_population, label="c=%e" % column) pl.figure(2) pl.plot(R.frequency, R.tau, label="c=%e" % column) pl.figure(3) pl.plot(R.frequency, R.tex, label="c=%e" % column) f1 = pl.figure(1) ax1 = f1.gca() ax1.set_xlim(0, 10) ax1.set_xlabel("Energy Level") ax1.set_ylabel("Population") f2 = pl.figure(2)
def c18o_cf_iras16293(save=True, print_timing=False, abundance=5e-7, n_points=20): """ Generates my version of Fig. 3 from Plume+'12 """ # Plume '12 claims to use an abundance of 2e-7 (i.e. one C18O for every 500 x 10^4 H2 atoms) # but I find that 5e-7 matches the appearance much more closely. start = time.time() # Figure 3's caption says that Ju=10 is included, but the text omits it. # I have had better luck reproducing the plot when I omit Ju=10, # so I infer that the caption is incorrect. J_upper_list = [2, 3, 5, 6, 7, 8, 9, 10] J_lower_list = [x - 1 for x in J_upper_list] # Here we have to specify 2 of 3: abundance, temperature, density. R = pyradex.Radex(species='c18o', abundance=abundance, column=5e16, temperature=50, escapeProbGeom='lvg') fig = plt.figure() ax = fig.add_subplot(111) density_list = [1e5, 1e6, 1e7, 1e8] temperature_array = np.linspace(10, 400, n_points) f_c_array_n5 = np.zeros_like(temperature_array) f_c_array_n6 = np.zeros_like(temperature_array) f_c_array_n7 = np.zeros_like(temperature_array) f_c_array_n8 = np.zeros_like(temperature_array) f_c_array_list = [f_c_array_n5, f_c_array_n6, f_c_array_n7, f_c_array_n8] for j, density in enumerate(density_list): R.density = density for i, temp in enumerate(temperature_array): new_T = R(temperature=temp) # use the correction_factor code here! f_c = correction_factor_given_radex_table(new_T, J_lower_list) f_c_array_list[j][i] = f_c ax.plot(temperature_array, f_c_array_n8, '-.', color='purple', lw=2, label=r'$n = 10^8\ \rm{cm}^{-3}$') ax.plot(temperature_array, f_c_array_n7, 'b-', lw=1.5, label=r'$n = 10^7\ \rm{cm}^{-3}$') ax.plot(temperature_array, f_c_array_n6, 'g--', lw=1.5, label=r'$n = 10^6\ \rm{cm}^{-3}$') ax.plot(temperature_array, f_c_array_n5, 'r:', lw=2, label=r'$n = 10^5\ \rm{cm}^{-3}$') ax.legend(frameon=False, loc='upper center', fontsize=18) ax.set_xlim(0, 400) ax.set_ylim(1.2, 2.4) ax.set_xticks([0, 100, 200, 300, 400]) # ax.set_yticks([1.6,1.7,1.8,1.9,2]) fontdict = {'family': 'serif', 'fontsize': 16} ax.set_ylabel("Correction Factor", fontdict=fontdict) ax.set_xlabel("Temp (K)", fontdict=fontdict) ax.minorticks_on() if save: fig.savefig("c18o_iras16293.pdf") np.savetxt("c18o_fc_v_T_myr_temperature_array.txt", temperature_array) np.savetxt("c18o_fc_v_T_myr_fc_array_n5.txt", f_c_array_n5) np.savetxt("c18o_fc_v_T_myr_fc_array_n6.txt", f_c_array_n6) np.savetxt("c18o_fc_v_T_myr_fc_array_n7.txt", f_c_array_n7) np.savetxt("c18o_fc_v_T_myr_fc_array_n8.txt", f_c_array_n8) plt.show() end = time.time() if print_timing: print end - start return fig
tex_nh322 = {} tex_h2co = {} tex_h2co321 = {} for temperature in temperatures: densities = np.logspace(1, 9, 100) tex_nh311[temperature] = [] tex_nh322[temperature] = [] tex_h2co[temperature] = [] tex_h2co321[temperature] = [] Rh2co = pyradex.Radex(species='ph2co-h2', column=1e12, density={ 'oH2': density * fortho, 'pH2': density * (1 - fortho) }, temperature=temperature) for density in ProgressBar(densities): Rh2co.density = { 'oH2': density * fortho, 'pH2': density * (1 - fortho), } Rh2co.temperature = temperature Rh2co.run_radex() tex_h2co[temperature].append(Rh2co.Tex[2]) tex_h2co321[temperature].append(Rh2co.Tex[9])
import paths import pyradex import pyradex.fjdu import warnings warnings.filterwarnings('once') import matplotlib matplotlib.rc_file(paths.pcpath('pubfiguresrc')) pl.ion() RR = pyradex.Radex(species='ph2co-h2', column=1e14, temperature=50, escapeProbGeom='lvg', deltav=5, collider_densities={ 'oH2': 1e3, 'pH2': 1e4 }) ff = 0.1 density = 1e5 fortho = 0.1 column = 1e14 temperatures = np.linspace(25, 200, 20) temperatures = np.logspace(np.log10(25), np.log10(200), 40) linewidth = 5.0 xarr = np.arange(-35, 36, 1, dtype='float')
""" I am experimenting with pyradex. """ from __future__ import division import numpy as np import matplotlib as plt import pyradex # how do I work with pyradex? R = pyradex.Radex(species='c18o', abundance=1e-8, column=1e14, temperature=20) R.run_radex() # Ok CAN I DO A THING T = R(collider_densities={'H2': 1000}, column=10**12) T.pprint() # tom, let's do the shameful thing and do a LOOP rho_array = np.logspace(1, 6.5, 20) Tex_array_1_0 = np.zeros_like(rho_array) Tex_array_2_1 = np.zeros_like(rho_array) Tex_array_3_2 = np.zeros_like(rho_array) for i, rho in enumerate(np.logspace(1, 6.5, 20)): new_T = R(collider_densities={'H2': rho}) Tex_array_1_0[i] = new_T[0]['Tex']
def crude_attempt_at_matching(): """ Here we are trying to... convince correction_factor and RADEX to be consistent across different kinds of runs! Step 1: do temperature then density Step 2: do density then temperature Step 3: make sure they are consistent dude Step 4: do them in reverse order and try again I have a vague sense that (because stuff is directly interfacing with underlying FORTRAN code) """ # Here we have to specify 2 of 3: abundance, temperature, density. R_step1 = pyradex.Radex(species='h13cn@xpol', abundance=1e-8, temperature=100, column=1e16, escapeProbGeom='lvg') density = 1e6 temperature_list = [50, 100, 200] f_c_dict = {} for j, temp in enumerate(temperature_list): R_step1.temperature = temp new_T = R_step1(collider_densities={'H2': density}) f_c = correction_factor_given_radex_table(new_T, h13cn_J_lower_list) f_c_dict[temp] = f_c print f_c_dict del R_step1 # Step 2 R_step2 = pyradex.Radex(species='h13cn@xpol', abundance=1e-8, column=1e16, temperature=50, escapeProbGeom='lvg') f_c_dict_2 = {} R_step2.density = density for i, temp in enumerate(temperature_list): new_T = R_step2(temperature=temp) # use the correction_factor code here! f_c = correction_factor_given_radex_table(new_T, h13cn_J_lower_list) f_c_dict_2[temp] = f_c print f_c_dict_2 return
def ph2cogrid(ntemp=50, trange=[10,200], abundances=(1.2e-9,), logdensities=(4,5), opr=3, deltav=5.0, # km/s ): import pyradex temperatures=np.linspace(trange[0],trange[1],ntemp) # initial density; will be modified later density = 1e4 fortho = opr/(1.+opr) R = pyradex.Radex(species='ph2co-h2', abundance=abundances[0], collider_densities={'H2':density}, deltav=deltav, column=None, temperature=temperatures[0], ) Xarr = {} for abundance in abundances: Xarr[abundance] = {} densities = [10**x for x in logdensities] ratio1 = {d:[] for d in densities} ratio2 = {d:[] for d in densities} f1 = {d:[] for d in densities} f2 = {d:[] for d in densities} f3 = {d:[] for d in densities} for density in densities: #R.density = {'H2': density} R.density = {'oH2':density*fortho,'pH2':density*(1.-fortho)} for temperature in temperatures: R.temperature = temperature R.abundance = abundance log.info("niter={0} col={1:0.1f} dens={2:0.1f} tem={3:0.1f} abund={4:0.2g}" .format(R.run_radex(validate_colliders=False, reload_molfile=False), np.log10(R.column.value), np.log10(R.density['oH2'].value), R.temperature, R.abundance)) #import ipdb; ipdb.set_trace() F1 = R.T_B[2] # 218.222192 3_0_3 F2 = R.T_B[12] # 218.760066 3_2_1 F3 = R.T_B[9] # 218.475632 3_2_2 ratio1[density].append(F2/F1) ratio2[density].append(F3/F1) f3[density].append(F3) f2[density].append(F2) f1[density].append(F1) print() f1 = {d:np.array([x.value for x in f1[d]]) for d in densities} f2 = {d:np.array([x.value for x in f2[d]]) for d in densities} f3 = {d:np.array([x.value for x in f3[d]]) for d in densities} ratio1 = {d:np.array(ratio1[d]) for d in densities} ratio2 = {d:np.array(ratio2[d]) for d in densities} Xarr[abundance] = {'flux1':f1, 'flux2':f2, 'flux3':f3, 'ratio1':ratio1, 'ratio2':ratio2} return Xarr
def from_pyradex(self, integrated_flux, mol_data, line_width=1.0 * u.km / u.s, escapeProbGeom='lvg', iter=100, collider_density={'H2': 900*2.2}): """ Calculate production rate from the Non-LTE iterative code pyradex Presently, only the LAMDA catalog is supported by this function. In the future a function will be provided by sbpy to build your own molecular data file from JPLSpec for use in this function. Collider is assumed to be H2O for the default setting since we are only considering comet production rates. See documentation for pyradex installation tips Parameters ---------- integrated_flux : `~astropy.units.Quantity` The integrated flux in K * km/s mol_data : `sbpy.data.phys` `sbpy.data.phys` object that contains AT LEAST the following data: | mol_tag: molecule of interest as string or int JPLSpec identifier | temp: kinetic temperature in gas coma (unit K) | cdensity : cdensity estimate (can be calculated from cdensity_Bockelee) (unit 1/cm^2) | temp_back: (optional) background temperature in K (default is 2.730 K) | lamda_name: (optional) LAMDA molecule identifier to avoid ambiguity. `Lamda.molecule_dict` provides list Keywords that can be used for these values are found under `~sbpy.data.conf.fieldnames` documentation. Make sure to inform yourself on the values needed for each function, their units, and their interchangeable keywords as part of the Phys data class. line_width : `~astropy.units.Quantity` The FWHM line width (really, the single-zone velocity width to scale the column density by: this is most sensibly interpreted as a velocity gradient (dv/length)) in km/s (default is 1.0 km/s) escapeProbGeom : str Which escape probability method to use, available choices are 'sphere', 'lvg', and 'slab' iter : int Number of iterations you wish to perform. Default is 100, more iterations will take more time to do but will offer a better range of results to compare against. The range of guesses is built by having the column density guess and subtracting/adding an order of magnitude for the start and end values of the loop, respectively. i.e. a guess of 1e15 will create a range between 1e14 and 1e16 collider_density : dict Dictionary of colliders and their densities in cm^-3. Allowed entries are any of the following : h2,oh2,ph2,e,He,H,H+ See `~Pyradex` documentation for more information. Default dictionary is {'H2' : 900*2.2} where 900 is the collider density of H2 and 2.2 is the value for the square root of the ratio of reduced masses of H2O/H2 as follows: (mu_H2O/mu_H2)**0.5 = ((18**2/18*2)/((18*2)/(18+2)))**0.5 = 2.2 in order to scale the collisional excitation to H2O as the main collisional partner. (Walker, et al. 2014; de val Borro, et al. 2017; & Schoier, et al. 2004) Returns ------- column density : `~astropy.units.Quantity` column density to use for the Haser model ratio calculation Note: it is normal for pyradex/RADEX to output warnings depending on the setup the user gives it (such as not having found a molecular data file so it searches for it on LAMDA catalog) Examples -------- >>> from sbpy.activity import NonLTE >>> from sbpy.data import Phys >>> import astropy.units as u >>> transition_freq = (177.196 * u.GHz).to(u.MHz) >>> mol_tag = 29002 >>> cdensity_guess = (1.89*10.**(14) / (u.cm * u.cm)) >>> temp_estimate = 20. * u.K >>> mol_data = Phys.from_jplspec(temp_estimate, transition_freq, ... mol_tag) # doctest: +REMOTE_DATA >>> mol_data.apply([cdensity_guess.value] * cdensity_guess.unit, ... name= 'cdensity') # doctest: +REMOTE_DATA >>> mol_data.apply(['HCO+@xpol'], ... name='lamda_name') # doctest: +REMOTE_DATA >>> nonLTE = NonLTE() >>> try: # doctest: +SKIP ... cdensity = nonLTE.from_pyradex(1.234 * u.K * u.km / u.s, ... mol_data, iter=600, ... collider_density={'H2': 900}) # doctest: +REMOTE_DATA ... print(cdensity) # doctest: +REMOTE_DATA ... except ImportError: ... pass Closest Integrated Flux:[1.24925956] K km / s Given Integrated Flux: 1.234 K km / s [1.06363773e+14] 1 / cm2 References ---------- Haser 1957, Bulletin de la Societe Royale des Sciences de Liege 43, 740. Walker, et al., On the Validity of Collider-mass Scaling for Molecular Rotational Excitation, APJ, August 2014. van der Tak, et al., A computer program for fast non-LTE analysis of interstellar line spectra. With diagnostic plots to interpret observed line intensity ratios. A&A, February 12 2013. de Val Borro, et al., Measuring molecular abundances in comet C/2014 Q2 (Lovejoy) using the APEX telescope. Monthly Notices of the Royal Astronomical Society, October 27 2017. Schoier, et al., An atomic and molecular database for analysis of submillimetre line observations. A&A, November 4 2004. """ try: import pyradex except ImportError: raise ImportError('Pyradex not installed. Please see \ https://github.com/keflavich/pyradex/blob/master/INSTALL.rst') if not isinstance(mol_data, Phys): raise ValueError('mol_data must be a `sbpy.data.phys` instance.') register('Production Rates', {'Radex': '2007A&A...468..627V'}) # convert mol_tag JPLSpec identifier to verbose name if needed try: mol_data['lamda_name'] name = mol_data['lamda_name'][0] name = name.lower() except KeyError: if not isinstance(mol_data['mol_tag'][0], str): cat = JPLSpec.get_species_table() mol = cat[cat['TAG'] == mol_data['mol_tag'][0]] name = mol['NAME'].data[0] name = name.lower() else: name = mol_data['mol_tag'][0] name = name.lower() # try various common instances of molecule names and check them against LAMDA before complaining try: Lamda.molecule_dict[name] except KeyError: try_name = "{}@xpol".format(name) try: Lamda.molecule_dict[try_name] name = try_name except KeyError: print('Molecule name {} not found in LAMDA, module tried {} and also\ found no molecule with this identifier within LAMDA. Please\ enter LAMDA identifiable name using mol_data["lamda_name"]\ . Use Lamda.molecule_dict to see all available options.'.format(name, try_name)) raise # define Temperature temp = mol_data['temp'] # check for optional values within mol_data if 'temp_back' in mol_data: tbackground = mol_data['temp_back'] else: tbackground = 2.730 * u.K # define cdensity and iteration parameters cdensity = mol_data['cdensity'].to(1 / (u.cm * u.cm)) cdensity_low = cdensity - (cdensity*0.9) cdensity_high = cdensity + (cdensity*9) # range for 400 iterations cdensity_range = np.linspace(cdensity_low, cdensity_high, iter) fluxes = [] column_density = [] with tempfile.TemporaryDirectory() as datapath: for i in cdensity_range: R = pyradex.Radex(column=i, deltav=line_width, tbackground=tbackground, species=name, temperature=temp, datapath=datapath, escapeProbGeom=escapeProbGeom, collider_densities=collider_density) table = R() # find closest matching frequency to user defined indx = (np.abs(table['frequency']-mol_data['t_freq'])).argmin() radexfreq = table['frequency'][indx] # get table for that frequency values = table[table['frequency'] == radexfreq] # use eq in io.f from Pyradex to get integrated flux in K * km/s int_flux_pyradex = 1.0645 * values['T_B'] * line_width fluxes.append(int_flux_pyradex) column_density.append(i) # closest matching integrated flux from pyradex fluxes = np.array(fluxes) index_flux = (np.abs(fluxes-integrated_flux.to(u.K * u.km / u.s).value)).argmin() # corresponding column density in 1/cm^2 column_density = column_density[index_flux] print('Closest Integrated Flux:{}'.format(fluxes[index_flux] * u.K * u.km / u.s)) print('Given Integrated Flux: {}'.format(integrated_flux)) return column_density
import pyradex import pylab as pl R = pyradex.Radex(column=1e16) R.maxiter = 1000 #print R.radex.impex.molfile,R.molpath for temperature in [5, 10, 20, 30, 40, 50, 60]: R.temperature = temperature R.run_radex() pl.figure(1) pl.plot(R.level_population, label="T=%s" % R.temperature._repr_latex_()) pl.figure(2) pl.plot(R.frequency, R.tau, label="T=%s" % R.temperature._repr_latex_()) pl.figure(3) pl.plot(R.frequency, R.tex, label="T=%s" % R.temperature._repr_latex_()) f1 = pl.figure(1) ax1 = f1.gca() ax1.set_xlim(0, 10) ax1.set_xlabel("Energy Level") ax1.set_ylabel("Population") f2 = pl.figure(2) ax2 = f2.gca() ax2.set_xlabel("Frequency") ax2.set_ylabel("Optical Depth")
texgrid_71M = np.empty([ndens,ntemp]) fluxgrid_71M = np.empty([ndens,ntemp]) taugrid_145 = np.empty([ndens,ntemp]) texgrid_145 = np.empty([ndens,ntemp]) fluxgrid_145 = np.empty([ndens,ntemp]) taugrid_355M = np.empty([ndens,ntemp]) texgrid_355M = np.empty([ndens,ntemp]) fluxgrid_355M = np.empty([ndens,ntemp]) columngrid = np.empty([ndens,ntemp]) import os if not os.path.exists('ph2co-h2.dat'): import urllib urllib.urlretrieve('http://home.strw.leidenuniv.nl/~moldata/datafiles/ph2co-h2.dat') R = pyradex.Radex(species='ph2co-h2', abundance=abundance) R.run_radex() # get the table so we can look at the frequency grid table = R.get_table() # Target frequencies: table[np.array([6,1,11])].pprint() for ii,tt in enumerate(temperatures): R.temperature = tt for jj,dd in enumerate(densities): R.density = {'oH2':dd*fortho,'pH2':dd*(1-fortho)} R.abundance = abundance # reset column to the appropriate value R.run_radex(reuse_last=False, reload_molfile=True)
""" Example: Plot the relative intensities of the 3mm CH3CN (methyl cyanide) lines """ import pyradex import pylab as pl from astropy import units as u import numpy as np import matplotlib as mpl R = pyradex.Radex(species='ch3cn', abundance=1e-11, column=None) # There are 5 lines of interest in this band nlines = 5 fluxes = {ii: [] for ii in xrange(nlines)} # Temperature range: 20-500 K is allowed (by CH3CN data file) temperatures = np.linspace(20, 500, 8) temperatures = [20, 50, 100, 300, 500] # set up figure pl.figure(1) pl.clf() for ii, temperature in enumerate(temperatures): R.temperature = temperature R.run_radex() #wcs = pyradex.synthspec.FrequencyArray(91.95*u.GHz,92*u.GHz,1000) wcs = np.linspace(91.95, 92, 1000) * u.GHz S = pyradex.synthspec.SyntheticSpectrum.from_RADEX(
def runRADEX(species, widths, temperatures, densities, columns, **kwargs): """Calls pyradex iteratively to produce a table of intensities.""" # Make sure all the provided values are iterable. temperatures, tnum, tmin, tmax = formatInput(temperatures, log=False) densities, dnum, dmin, dmax = formatInput(densities, log=True) columns, cnum, cmin, cmax = formatInput(columns, log=True) widths, wnum, wmin, wmax = formatInput(widths, log=False) # Check that the collisional rate file exists. # This is hardwired to where the collisional rates are. rates_path = os.path.expanduser(os.getenv('RADEX_DATAPATH')) print('{}/{}.dat'.format(rates_path, species)) if not os.path.isfile('{}/{}.dat'.format(rates_path, species)): raise ValueError('Did not find collisional rates.') rates = ratefile('{}/{}.dat'.format(rates_path, species)) # We assume that the density is n(H2) and the ortho/para ratio is 3. # Check if oH2 and pH2 are valid colliders in the collisional rate file. # If they are, recalculate the densities. opr = kwargs.get('opr', False) if ('oH2' in rates.partners and 'pH2' in rates.partners and opr): opr_flag = True # print 'Assuming an ortho / para ratio of {}.'.format(opr) else: opr_flag = False # Dummy array to hold the results. # Hold up the 'jmax' transition, default is 10. # Saves both the brightness temperature and optical depth. jmax = kwargs.get('jmax', 9) + 1 Tb = np.zeros((jmax, 3, wnum, tnum, dnum, cnum)) # First initialise pyradex, then iterate through all the permutations. # Select the correct escape problem geometry, by default we assume slab. escapegeom = kwargs.pop('escapegeom', 'slab') radex = pyradex.Radex(species=species, temperature=temperatures[0], density=densityDict(densities[0], opr, opr_flag), column=columns[0], deltav=widths[0], escapeProbGeom=escapegeom, **kwargs) t0 = time.time() tlast = np.nan for l, width in enumerate(widths): radex.deltav = width for t, temp in enumerate(temperatures): radex.temperature = temp for d, dens in enumerate(densities): radex.density = densityDict(dens, opr, opr_flag) for c, col in enumerate(columns): radex.column = col # Reload the molfile if temperature has changed. with np.errstate(divide='ignore'): if tlast == temp: radex.run_radex() else: radex.run_radex(reload_molfile=True) tlast = temp # Parse the results. Tb[:, 0, l, t, d, c] = radex.T_B[:jmax] Tb[:, 1, l, t, d, c] = radex.Tex[:jmax] Tb[:, 2, l, t, d, c] = radex.tau[:jmax] Tb = np.nan_to_num(Tb) t1 = time.time() if kwargs.get('verbose', True): print('Generated table in {}.'.format(seconds2hms(t1-t0))) # Save the file. fn = '{}_'.format(species) fn += '{:.2f}_{:.2f}_{:d}_'.format(wmin, wmax, wnum) fn += '{:.2f}_{:.2f}_{:d}_'.format(tmin, tmax, tnum) fn += '{:.2f}_{:.2f}_{:d}_'.format(dmin, dmax, dnum) fn += '{:.2f}_{:.2f}_{:d}.npy'.format(cmin, cmax, cnum) np.save(kwargs.get('path', './') + fn, Tb) return
import pyradex import pylab as pl import numpy as np import paths import dust_emissivity from astropy import units as u from astropy.table import Table rr = pyradex.Radex(species='nacl', temperature=1000, density=1e8, column=4e13) rslt = rr() v0_76 = (rslt['upperlevel'] == '0_7 ') & (rslt['lowerlevel'] == '0_6 ') v1_76 = (rslt['upperlevel'] == '1_7 ') & (rslt['lowerlevel'] == '1_6 ') v2_76 = (rslt['upperlevel'] == '2_7 ') & (rslt['lowerlevel'] == '2_6 ') v3_76 = (rslt['upperlevel'] == '3_7 ') & (rslt['lowerlevel'] == '3_6 ') v10 = (rslt['upperlevel'] == '1_7 ') & (rslt['lowerlevel'] == '0_6 ') v21 = (rslt['upperlevel'] == '2_7 ') & (rslt['lowerlevel'] == '1_6 ') v32 = (rslt['upperlevel'] == '3_7 ') & (rslt['lowerlevel'] == '2_6 ') for density in (1e2, 1e5, 1e8, 1e11, 1e14): rslt = rr(density={'H2': density}) critdens = { rr.quantum_number[iupp]: rr.radex.rmolec.aeinst[rr.radex.imolec.iupp == iupp].sum() / (rr.radex.collie.ctot[iupp - 1] / rr.total_density) for iupp in np.arange(1, rr.radex.imolec.iupp.max()) } print(density, critdens[b'0_7 '], rr.level_population[7], critdens[b'1_7 '], rr.level_population[68], "\n", rslt[v0_76 | v1_76])
# Check correctness before doing timing tests import pyradex py_pop = [ pyradex.pyradex(collider_densities={ 'oH2': 900, 'pH2': 100 }, column=n, temperature=20)['pop_up'][0] for n in 10**np.arange(12, 18) ] R = pyradex.Radex(collider_densities={ 'oH2': 900, 'pH2': 100 }, column=1e15, temperature=20) R_noreload_pop = [] for n in 10**np.arange(12, 18): R.column = n R.run_radex(reload_molfile=False, validate_colliders=False) R_noreload_pop.append(R.level_population[1]) R_pop = [] for n in 10**np.arange(12, 18): R.column = n R.run_radex(reload_molfile=True, validate_colliders=True) R_pop.append(R.level_population[1])
abundance = 1.2e-9 # Johnston / Ao opr = 0.01 # assume primarily para opr = 3 fortho = opr / (1 + opr) import os if not os.path.exists('ph2co-h2.dat'): import urllib urllib.urlretrieve( 'http://home.strw.leidenuniv.nl/~moldata/datafiles/ph2co-h2.dat') # 1e23 from Johnston 2014 R = pyradex.Radex(species='ph2co-h2', abundance=abundance, temperature=50, collider_densities={ 'oH2': 2e4 * fortho, 'pH2': 2e4 * (1 - fortho) }) print R.escapeProbGeom # DEBUG R.run_radex() # get the table so we can look at the frequency grid table = R.get_table() # Target frequencies: table[np.array([6, 1, 11])].pprint() key_303 = np.where((table['upperlevel'] == '3_0_3') & (table['frequency'] > 218) & (table['frequency'] < 220))[0]