LineList Class¶
Overview¶
This class organizes information about atomic and molecular transition lines (e.g. HI Lya, CIV 1548, Hydrogen Balmer series) observed in astrophysical environments.
The following lists are currently avaliable:
- ‘ISM’ : “All” ISM lines (can be overwhelming!)
- ‘Strong’ : Strong ISM lines (most common absorption line transitions observed)
- ‘HI’ : HI Lyman series
- ‘H2’ : H2 (Lyman-Werner)
- ‘CO’ : CO UV band-heads
- ‘EUV’ : Extreme UV lines
- ‘Galaxy’ : Lines typically identified in galaxy spectra
- ‘AGN’: Lines tipically identified in AGN spectra
Instantiation¶
The LineList Class may be instantiated using one of the keys in the list above:
from linetools.lists.linelist import LineList
hi = LineList('HI')
#
euv = LineList('EUV')
hi
, for example, contains only HI Lyman series transitions
(e.g. HI Lya), and euv
contains both HI Lyman series and extreme
UV metal transitions (e.g. HI Lyb, NeVIII, MgX).
Accessing single transitions¶
We can now easily access atomic information regarding individual transitions either by the rest-frame wavelength:
wrest = 1215.67 * u.AA # HI Lya
hi[wrest]
or by the name convention within linetools, which in the case of HI
Lya is HI 1215
:
name = 'HI 1215' # We adopt the convention of *not* rounding in the name
hi[name]
Both cases will provide the following dictionary:
{'A': <Quantity 626500000.0 1 / s>, # Einstein coefficient
'Am': 0, # Mass number (often written as "A"; only used for D)
'Ej': <Quantity 0.0 1 / cm>, # Energy of lower level (relative to ground state)
'Ek': <Quantity 2259.163 1 / cm>, # Energy of upper level (relative to ground state)
'Ex': <Quantity 0.0 1 / cm>, # Excitation energy (cm^-1)
'Jj': 0.0, # Tot ang mom (z projection) of lower state (or rotation level)
'Jk': 0.0, # Tot ang mom (z projection) of upper state (or rotation level)
'Ref': 'Morton2003', # References
'Z': 1, # Atomic number (for atoms)
'col0': masked, # (Reserved)
'col6': masked, # (Reserved)
'el': 0, # Electronic transition (2=Lyman (B-X), 3=Werner (C-X))
'f': 0.41639999999999999, # Oscillator strength
'gamma': <Quantity 626500000.0 1 / s>,# Sum of A
'gj': 2, # Lower statistical weight (2J+1)
'gk': 6, # Upper statistical weight (2J+1)
'group': 1, # Flag for grouping
'ion': 1, # Ionic state (1=Neutral)
'mol': '', # Molecular name (H2, HD, CO, C13O)
'name': 'HI 1215', # Name
'nj': 0, # Orbital level of lower state (or vibrational level)
'nk': 0, # Orbital level of upper state (or vibrational level)
'wrest': <Quantity 1215.67 Angstrom>} # Rest Wavelength (Quantity)
which summarizes the most important atomic information of HI Lya transition, including the reference where these values come from (i.e., Morton2003). One can therefore access any of these by calling its dictionary keywords:
hi['HI 1215']['wrest']
<Quantity 1215.67 Angstrom>
is the rest-frame wavelength of the HI Lya transition. Similarly,:
euv['NeVIII 780']['f']
0.050500001758337021
is the oscillator strength of the NeVIII 780 transition.
Methods¶
subset_lines()¶
This method provides a way to define a subset of lines drawn from the original` LineList` object. Consider that for some reason you may want only HI Lya and Lyb in your LineList, then you can achieve this by:
hi = LineList('HI')
hi = hi.subset_lines(['HI 1215', 'HI 1025'])
Which has only those two transitions loaded.
You may also want to use rest-frame wavelength to define a subset, for instance:
ism = LineList('ISM')
lines = [2796.3543, 2803.5315, 1548.195, 1550.77] * u.AA
ism = ism.subset_lines(lines)
print(ism)
<LineList: ISM; 4 transitions>
selects only those four transitions of MgII and CIV. In order to
avoid loading the LineList('ISM')
again, you can use the keyword
reset_data in subset_lines() to make another arbitrarily different
subset of lines from the original LineList:
lines = ['HI 1215', 'HI 1025']
ism = ism.subset_lines(lines, reset_data=True)
print(ism)
<LineList: ISM; 2 transitions>
which now has only HI Lya and Lyb.
Finally, if you want the transitions to be sorted by rest-frame wavelength you can use the optional keyword sort:
lines = [2796.3543, 2803.5315, 1548.195, 1550.77] * u.AA
ism = ism.subset_lines(lines, reset_data=True, sort=True)
ism._data['wrest']
<Quantity [ 1548.195 , 1550.77 , 2796.3543, 2803.5315] Angstrom>
set_lines()¶
Another way to reset the LineList to its original form is by using set_lines(). Following the previous example, we have a ism Linelist with only 4 transitions:
print(ism._data['name'])
name
---------
CIV 1548
CIV 1550
MgII 2796
MgII 2803
print(ism)
<LineList: ISM; 4 transitions>
ism.set_lines()
print(ism)
<LineList: ISM; 412 transitions>
Give us the original ism LineList with 412 unique transitions.
You may also want to use rest-frame wavelength to define a subset, for instance:
ism = LineList('ISM')
sub_lines = [2796.3543, 2803.5315, 1548.195, 1550.77] * u.AA
civ_mgii = ism.subset(sub_lines)
all_transitions()¶
Sometimes it may be useful to know all the transitions associated to a given ion species. This can be achieved by the all_transitions() method:
ism = LineList('ISM')
mgii = ism.all_transitions('MgII')
Which gives us the information of all the 6 transitions of MgII:
print(mgii)
A el nj nk group name Ek ... Jk Z gk gj gamma col0 col6
1 / s 1 / cm ... 1 / s
----------- --- --- --- ----- --------- --------- ... --- --- --- --- ----------- ---- ----
2350000.0 0 0 0 1 MgII 1025 97468.92 ... 0.0 12 4 2 2350000.0 -- --
2480000.0 0 0 0 1 MgII 1026 97455.12 ... 0.0 12 2 2 2480000.0 -- --
1370000.0 0 0 0 1 MgII 1239 80650.02 ... 0.0 12 4 2 1370000.0 -- --
1540000.0 0 0 0 1 MgII 1240 80619.5 ... 0.0 12 2 2 1540000.0 -- --
262500000.0 0 0 0 1 MgII 2796 35760.848 ... 0.0 12 4 2 262500000.0 -- --
259500000.0 0 0 0 1 MgII 2803 35669.298 ... 0.0 12 2 2 259500000.0 -- --
In this case mgii
is a Table because
more than 1 transition was found. In cases were only 1 transition
exists, the output of all_transitions() is a dictionary
with the same keywords as the columns of ism._data
QTable:
ciii = ism.all_transitions('CIII')
type(ciii)
dict
print(ciii)
{'A': <Quantity 1760000000.0 1 / s>,
'Am': 0,
'Ej': <Quantity 0.0 1 / cm>,
'Ek': <Quantity 2352.04 1 / cm>,
'Ex': <Quantity 0.0 1 / cm>,
'Jj': 0.0,
'Jk': 0.0,
'Ref': 'Morton2003',
'Z': 6,
'col0': masked,
'col6': masked,
'el': 0,
'f': 0.75700000000000001,
'gamma': <Quantity 1760000000.0 1 / s>,
'gj': 1,
'gk': 3,
'group': 1,
'ion': 3,
'mol': '',
'name': 'CIII 977',
'nj': 0,
'nk': 0,
'wrest': <Quantity 977.0201 Angstrom>}
Alternatively, we could call this method via tuple, e.g., (8,6), where the first entry is the atomic number (of O) and the second is the ionization state (VI)
ovi = ism.all_transitions((8,6))
print(ovi['name', 'wrest', 'f'])
name wrest f
Angstrom
-------- --------- ------
OVI 1031 1031.9261 0.1325
OVI 1037 1037.6167 0.0658
You can also use a rest-frame wavelength to identify the ion species of interest:
wrest = 1260.4221 * u.AA
si2 = ism.all_transitions(wrest)
print(si2['name', 'wrest', 'f'])
name wrest f
Angstrom
--------- --------- ---------------
SiII 889 889.7228 0.0434000007808
SiII 989 989.8731 0.171
SiII 1020 1020.6989 0.0168
SiII 1190 1190.4158 0.292
SiII 1193 1193.2897 0.582
SiII 1260 1260.4221 1.18
SiII 1304 1304.3702 0.0863
SiII 1526 1526.707 0.127
SiII 1808 1808.0129 0.00208
SiII 2335 2335.123 4.25e-06
For the purposes of all_transitions, it does not matter which transition of a given ion species you choose, it will still retrieve the same answer, e.g.:
hi = ism.all_transitions('HI 1215')
hi = ism.all_transitions('HI 1025')
hi = ism.all_transitions(972.5367 * u.AA)
hi = ism.all_transitions('HI')
are all equivalent. Note that in the last example we only used the
root name of the transition (i.e. the string before the blank space,
'HI'
), so no prior knowledge of the linetools naming convention is
needed.
strongest_transitions()¶
Sometimes it is useful to know the strongest transition for an ion in the LineList within some wavelength range. strongest_transitions() gives the strongest n_max transitions of a given ion between a wavelength range, sorted by relative strength (defined as the product of its rest-frame wavelength wrest and oscillator strength f):
wvlims = [1000, 3000] * u.AA
line = 'SiII'
si2_strong = ism.strongest_transitions(line, wvlims, n_max=4)
print(si2_strong['name'])
name
---------
SiII 1260
SiII 1193
SiII 1190
SiII 1526
The syntax is the same as for all_transitions(). Note that you will
get the same result if you use line='SiII'
, line='SiII 1190'
,
line='SiII 889'
, line=889.7228*u.AA
, or line=(8,6)
. By default n_max=3.
Depending on the wavelength range, however, the output may vary:
wvlims = [500, 1100] * u.AA
line = 'SiII 1260'
si2_strong = ism.strongest_transitions(line, wvlims, n_max=4)
print(si2_strong['name'])
name
---------
SiII 989
SiII 889
SiII 1020
Note that despite n_max=4 we have only retrieved the 3 transitions
satisfying the criteria of belonging to wvlims = [500, 1100] * u.AA
.
Again, note that even though SiII 1260 is out of wvlims
range, it
can still be used to identify that you are interested in the SiII ion
species.
If you would like to retrieve all the transitions in a given wvlims
regardless of its relative strength, you can set n_max=None.
Following the convention within LineList, if only 1 transition is retrieved, the output of strongest_transitions() is a dictionary; if more than 1 transition are retrieved the output is a QTable. If no transition exist the output is None.
available_transitions()¶
Sometimes it may be useful to know what are the available transition in a given wavelength range found in the LineList regardless of the ion species. This is particularly the case when someone is trying to identify unknown emission/absorption lines in a spectrum. Let us then illustrate the use of this method with an example. Imagine that you have an observed spectrum covering the following wavelength range:
wvlims = [3500,5000] * u.AA
Let us now imagine that we are interested in a particular redshift, say
z=0.67
. Then, we can do:
z = 0.67
transitions = ism.available_transitions(wvlims/(1+z), n_max_tuple=None, min_strength=0.)
print(len(transitions))
33
Will give the 33 transitions available that could correspond to having
z=0.67
in the form of a QTable. The output is sorted by strength of
the strongest available transition per ion species, and strength is defined
as log10(wrest * fosc * abundance), where abundance is that of the solar
composition given by Asplund2009. As optional keyword parameters one can
specify a minimum strength as min_strength, so transitions below this
value are omitted, e.g.:
transitions = ism.available_transitions(wvlims/(1+z), n_max_tuple=None, min_strength=10.5)
print(len(transitions))
3
Which correspond to MgI 2852, MgII 2796 and MgII 2803. Note than this method does not correct for ionization state. Similarly, one can also specify the maximum number of transitions per ion species tuple using the optional keyword parameter n_max_tuple, e.g.:
transitions = ism.available_transitions(wvlims/(1+z), n_max_tuple=1, min_strength=0.)
print(transitions['name'])
name
-----------
MgI 2852
MgII 2796
FeII 2382
FeII* 2396b
MnII 2576
VII 2683
...
Which for the case of MgII only retrieves 'MgII 2796'
. Again, following the convention within
LineList, if only 1 transition is retrieved, the output of available_transitions()
is a dictionary; if more than 1 transition are retrieved the output is a QTable. If no
transition exist satisfying the criteria the output is None.