.. highlight:: rest .. _LineList: ************** LineList Class ************** .. index:: LineList Overview ======== This class organizes information about atomic and molecular transition lines (e.g. `HI Lya`, `CIV 1548`, Hydrogen Balmer series) observed in astrophysical environments. .. (:ref:`AbsLine Class`). add this back in when written 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': , # Einstein coefficient 'Am': 0, # Mass number (often written as "A"; only used for D) 'Ej': , # Energy of lower level (relative to ground state) 'Ek': , # Energy of upper level (relative to ground state) 'Ex': , # 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': ,# 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': } # 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'] 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) 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) 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'] 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) ism.set_lines() print(ism) 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': , 'Am': 0, 'Ej': , 'Ek': , 'Ex': , 'Jj': 0.0, 'Jk': 0.0, 'Ref': 'Morton2003', 'Z': 6, 'col0': masked, 'col6': masked, 'el': 0, 'f': 0.75700000000000001, 'gamma': , 'gj': 1, 'gk': 3, 'group': 1, 'ion': 3, 'mol': '', 'name': 'CIII 977', 'nj': 0, 'nk': 0, 'wrest': } 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`.