Advanced Spectrum Generation

In addition to generating a basic spectrum as demonstrated in the annotated example, the user can also customize the generated spectrum in a variety of ways. One can choose which spectral lines to deposit or choose different settings for the characteristics of the spectrograph, and more. The following code goes through the process of setting these properties and shows what impact it has on resulting spectra.

For this demonstation, we’ll be using a light ray passing through a very dense disk of gas, taken from the initial output from the AGORA isolated box simulation using ART-II in Kim et al. (2016). If you’d like to try to reproduce the spectra included below you can get the LightRay file from the Trident sample data using the command:

$ wget http://trident-project.org/data/sample_data/ART-II_ray.h5

Now, we’ll load up the ray using yt:

import yt
import trident
ray = yt.load('ART-II_ray.h5')

Setting the spectrograph

Let’s set the characteristics of the spectrograph we will use to create this spectrum. We can either choose the wavelength range and resolution and line spread function explicitly, or we can choose one of the preset instruments that come with Trident. To list the presets and their respective values, use this command:

print(trident.valid_instruments)

Currently, we have three settings for the Cosmic Origins Spectrograph available: COS-G130M, COS-G140L, and COS-G160M, but we plan to add more instruments soon. To use one of them, we just use the name string in the SpectrumGenerator class:

sg = trident.SpectrumGenerator('COS-G130M')

But instead, let’s just set our wavelength range manually from 1150 angstroms to 1250 angstroms with a resolution of 0.01 angstroms:

sg = trident.SpectrumGenerator(lambda_min=1150, lambda_max=1250, dlambda=0.01)

From here, we can pass the ray to the SpectrumGenerator object to use in the construction of a spectrum.

Choosing what absorption features to include

There is a LineDatabase class that controls which spectral lines you can add to your spectrum. Trident provides you with a default LineDatabase with 213 spectral lines commonly used in CGM and IGM studies, but you can create your own LineDatabase with different lines. To see a list of all the lines included in the default line list:

ldb = trident.LineDatabase('lines.txt')
print(ldb)

which is reading lines from the ‘lines.txt’ file present in the data directory (see where is Trident installed?) We can specify any subset of these spectral lines to use when creating the spectrum from our master line list. So if you’re interested in just looking at neutral hydrogen lines in your spectrum, you can see what lines will be included with the command:

print(ldb.parse_subset('H I'))

As a first pass, we’ll create a spectrum that just include lines produced by hydrogen:

sg.make_spectrum(ray, lines=['H'])
sg.plot_spectrum('spec_H.png')

The resulting spectrum contains a nice, big Lyman-alpha feature.

_images/spec_H.png

If, instead, we want to shows the lines that would be in our spectral range due to carbon, nitrogen, and oxygen, we can do the following:

sg.make_spectrum(ray, lines=['C', 'N', 'O'])
sg.plot_spectrum('spec_CNO.png')

And now we have:

_images/spec_CNO.png

We can see how these two spectra combined when we include all of the same lines:

sg.make_spectrum(ray, lines=['H', 'C', 'N', 'O'])
sg.plot_spectrum('spec_HCNO.png')

which gives:

_images/spec_HCNO.png

We can get even more specific, by generating a spectrum that only contains lines due to a single ion species. For example, we might just want the lines from four-times-ionized nitrogen, N V:

sg.make_spectrum(ray, lines=['N V'])
sg.plot_spectrum('spec_NV.png')

This spectrum only shows a couple of small lines on the right hand side.

_images/spec_NV.png

But if that level of specificity isn’t enough, we can request individual lines:

sg.make_spectrum(ray, lines=['C I 1193', 'C I 1194'])
sg.plot_spectrum('spec_CI_1193_1194.png')

And we end up with:

_images/spec_CI_1193_1194.png

Or we can just include all of the available lines in our LineDatabase with:

sg.make_spectrum(ray, lines='all')
sg.plot_spectrum('spec_all.png')

Giving us:

_images/spec_all.png

To understand how to further customize your spectra, look at the documentation for the SpectrumGenerator and LineDatabase classes and other API documentation.

Setting Wavelength Bounds Automatically

If you are interested in creating a spectrum that contains all possible absorption features for a given set of lines, the SpectrumGenerator can be configured to automatically enlarge the wavelength window until all absorption is captured. This is done by setting the lambda_min and lambda_max keywords to ‘auto’ and specifying a bin size with the dlambda keyword:

sg = trident.SpectrumGenerator(lambda_min='auto', lambda_max='auto',
                               dlambda=0.01)
sg.make_spectrum("ray.h5", lines=['H I 1216'])
sg.plot_spectrum('spec_auto.png')
_images/spec_auto.png

Note, the above example is for a different ray than is used in the previous examples. The resulting spectrum will minimally contain all absorption present in the ray. This should be used with care when depositing multiple lines as this can lead to an extremely large spectrum.

Making Spectra in Velocity Space

Trident can be configured to create spectra in velocity space instead of wavelength space where velocity corresponds to the velocity offset from the rest wavelength of a given line. This can be done by providing the keyword bin_space='velocity' to the SpectrumGenerator:

sg = trident.SpectrumGenerator(lambda_min='auto', lambda_max='auto',
                               dlambda=1., bin_space='velocity')
sg.make_spectrum("ray.h5", lines=['H I 1216'])
sg.plot_spectrum('spec_velocity.png')
_images/spec_velocity.png

When working in velocity space, limits and bin sizes should be provided in km/s. If more than one transition is added to the spectrum (e.g., Ly-a and Ly-b), the zero point will correspond to the rest wavelength of the first transition added.

Making Spectra from a Subset of a Ray

The situation may arise where you want to see the spectrum that is generated by only a portion of the gas along a line of sight. For example, you may want to see the spectrum of only the cold gas. This can be done by creating a YTCutRegion from a loaded ray dataset:

import trident
import yt

ds = yt.load('ray.h5')
all_data = ds.all_data()
cold_gas = ds.cut_region(all_data, 'obj["gas", "temperature"] < 10000')

sg = trident.SpectrumGenerator(lambda_min=1200, lambda_max=1225,
                               dlambda=0.01)

# spectrum of entire ray
sg.make_spectrum(all_data, lines=['H I 1216'])
all_spectrum = sg.flux_field[:]

# spectrum of cold gas
sg.make_spectrum(cold_gas, lines=['H I 1216'])
cold_spectrum = sg.flux_field[:]

trident.plot_spectrum(sg.lambda_field, [all_spectrum, cold_spectrum],
                      label=['all gas', 'cold gas'], stagger=None)
_images/spec_cutregion.png