Note: This tutorial was generated from an IPython notebook that can be downloaded here.

Radex

from spectralradex import radex
from multiprocessing import Pool
import numpy as np
import time

The simplest use case for SpectralRadex is to be a simple python wrapper for RADEX. This allows large grids of RADEX models or complex parameter inference procedures to be run in an environment suited to those tasks.

If one wishes to run radex, we simply need a dictionary of the parameters RADEX expects. An example can be obtained using the get_default_parameters() function like so

params = radex.get_default_parameters()
print("{")
for key,value in params.items():
    print(f"\t{key} : {value}")
print("}")
{
    molfile : co.dat
    tkin : 30.0
    tbg : 2.73
    cdmol : 10000000000000.0
    h2 : 100000.0
    h : 0.0
    e- : 0.0
    p-h2 : 0.0
    o-h2 : 0.0
    h+ : 0.0
    linewidth : 1.0
    fmin : 0.0
    fmax : 1000.0
    geometry : 1
}

Note that each possible collisional partner has a separated entry for its density. You can check the collisional partners in your datafile with get_collisional_partners()

radex.get_collisional_partners("co.dat")
{'Number': 2, 'Partners': ['p-h2', 'o-h2']}

You only need to provide densities for the partners you wish to include in the calculation but you must include at least one of the partners. Two species cases are:

  • RADEX will sum the o-h2 and p-h2 density values to produce a h2 value.
  • A small number of datafiles have p-H2 collsions only and you may wish to place your total h2 density in that entry to approximate the o-h2 collisions

Once your parameter dictionary is set up, we pass that to the run() function.

output = radex.run(params)
output.head()
E_UP (K) freq WAVEL (um) T_ex tau T_R (K) POP UP POP LOW FLUX (K*km/s) FLUX (erg/cm2/s) Qup Qlow
0 5.53 115.271202 2600.757633 31.666252 0.000223 0.006275 0.246666 0.097917 0.006680 1.317591e-10 1 0
1 16.60 230.538000 1300.403656 29.262261 0.000735 0.017551 0.281677 0.246666 0.018683 2.947981e-09 2 1
2 33.19 345.795990 866.963374 26.640080 0.001112 0.021294 0.211510 0.281677 0.022667 1.207049e-08 3 2
3 55.32 461.040768 650.251515 24.363876 0.001022 0.015261 0.109663 0.211510 0.016246 2.050309e-08 4 3
4 82.97 576.267931 520.231028 22.798547 0.000605 0.007078 0.039845 0.109663 0.007535 1.856956e-08 5 4

Parameter Grids

It is more likely that one will want to run the code over many combinations of input parameters. This can be achieved via the run_grid() function. This function also takes a parameter dictionary of the same format as run(). However, variables which are too be varied over the grid should be supplied as iterables.

Furthermore, to keep things simple, the desired RADEXtakes iterables for the three variables (density, temperature and column density) as well as fixed values for the other RADEX parameters. It then produces the RADEX output for all combinations of the three iterables.

We’ll use an example grid which can be acquired using the get_example_grid_parameters() function.

parameters=radex.get_example_grid_parameters()
parameters
{'molfile': 'co.dat',
 'tkin': array([ 10. ,  82.5, 155. , 227.5, 300. ]),
 'tbg': 2.73,
 'cdmol': array([1.e+14, 1.e+15, 1.e+16, 1.e+17, 1.e+18]),
 'h2': array([   10000.        ,    56234.13251903,   316227.76601684,
         1778279.41003892, 10000000.        ]),
 'h': 0.0,
 'e-': 0.0,
 'p-h2': 0.0,
 'o-h2': 0.0,
 'h+': 0.0,
 'linewidth': 1.0,
 'fmin': 0.0,
 'fmax': 800.0,
 'geometry': 1}
tic = time.perf_counter()

grid_df = radex.run_grid(parameters,target_value="T_R (K)")
toc = time.perf_counter()
print(f"run_grid took {toc-tic:0.4f} seconds without a pool")
run_grid took 2.8573 seconds without a pool
grid_df.iloc[:,0:6].head()
tkin cdmol h2 (1)-(0)[115.2712018 GHz] (2)-(1)[230.538 GHz] (3)-(2)[345.7959899 GHz]
0 10.0 1.000000e+14 10000.0 0.114622 0.108152 0.022018
1 10.0 1.000000e+15 10000.0 1.048925 0.958338 0.215099
2 10.0 1.000000e+16 10000.0 5.189712 4.045272 1.567682
3 10.0 1.000000e+17 10000.0 6.561081 5.156221 3.411413
4 10.0 1.000000e+18 10000.0 6.639451 5.259944 3.822848

Parallelization

In order to be as flexible as possible, SpectralRadex has no built in multiprocessing. However, the run_grid() function does take the optional parameter pool which should be an object with map(), join(), and close() methods that allow functions to be evaluated in parallel. For example, the python standard multiprocessing.pool obect or Schwimmbad’s MPIPool.

If such an object is supplied, the grid will be evaluated in parallel. Note the time in the example below compared to the grid above.

tic = time.perf_counter()
pool=Pool(8)
grid_df = radex.run_grid(parameters,target_value="T_R (K)",pool=pool)
toc = time.perf_counter()
print(f"run_grid took {toc-tic:0.4f} seconds with a pool of 8 workers")
grid_df.iloc[:,0:6].head()
run_grid took 0.7338 seconds with a pool of 8 workers
tkin cdmol h2 (1)-(0)[115.2712018 GHz] (2)-(1)[230.538 GHz] (3)-(2)[345.7959899 GHz]
0 10.0 1.000000e+14 10000.0 0.114622 0.108152 0.022018
1 10.0 1.000000e+15 10000.0 1.048925 0.958338 0.215099
2 10.0 1.000000e+16 10000.0 5.189712 4.045272 1.567682
3 10.0 1.000000e+17 10000.0 6.561081 5.156221 3.411413
4 10.0 1.000000e+18 10000.0 6.639451 5.259944 3.822848