In [ ]:
os.environ['XUVTOP']

This short tutorial will demonstrate some of the capabilities of ChiantiPy and the CHIANTI database. It assumes that you know what the CHIANTI database provides and why you want to use it. The current implementation uses Version 0.6.4 of ChiantiPy and version 8.0 of the CHIANTI database and mainly provides access to methods concerned with single ions. An ion such as Fe XIV is specified by the string ‘fe_14’, in the usual CHIANTI notation.

Bring up a JuPyter notebook

> jupyter3-notebook

or

> jupyter-notebook

It is easiest to do this in the directory where the notebook (.ipynb) resides.

Once you open QuickStart.ipynb in the browser window, you will see something like this.

In my Ipython 00-setup.py file I have already preformed

In [ ]:
import ChiantiPy
import ChiantiPy.core as ch
import numpy as np
import matplotlib.pyplot as plt
%matplotlib

the following import is also useful for this demo

In [ ]:
import ChiantiPy.tools.filters as chfilters
In [ ]:
ChiantiPy.__version__
In [ ]:
import IPython
print(' IPython version = %i.%i.%i'%(IPython.version_info[0],IPython.version_info[1],IPython.version_info[2]))

It is useful to open a qtconsole where are the calculations can be easily examined

In [ ]:
qtconsole

What we will really be interested in are various properties of the Fe XIV emissivities as a function of temperature and density. So, let’s define a numpy array of temperatures and a value for the electron density

In [ ]:
temp = 10.**(5.8 + 0.05*np.arange(21.))
edens = 1.e+9

In ChiantiPy, temperatures are currently given in degrees Kelvin and densities as the number electron density per cubic cm. Then, construct fe14 as would be typically done

In [ ]:
fe14 = ch.ion('fe_14', temperature=temp, eDensity=edens)

alternately, since temperature and eDensity are the first keyword arguments fe14 = ch.ion('fe_14', temp, edens)

Level populations

In [ ]:
fe14.popPlot()

produces a matplotlib plot window were the population of the top 10 (the default) levels are plotted as a function of temperature.

If the level populations had not already been calculated, popPlot() would have invoked the populate() method which calculates the level populations and stores them in the Population dictionary, with keys = [‘protonDensity’, ‘population’, ‘temperature’, ‘density’].

The populations vs. temperature is not particularly interesting. Plotting them vs. density is more interesting and will come latere

A ChiantiPy Convention

Classes and function of ChiantiPy start with lower case letters.

Data attached to the instantiation of a class will start with a capital letter. For example,

fe14.populate() creates fe14.Population containing the level population information

fe14.emiss() creates fe14.Emiss containing the line emissivity information

fe14.intensity() creates fe14.Intensity containing the line intensity information that includes the elemental abundance and the ionization equilibrium

fe14.spectrum() creates fe14.Spectrum contain the line and continuum spectrum information

Spectral Line Intensities

In [ ]:
fe14.intensity(em=1.e+27)
for akey in sorted(fe14.Intensity):
    print('%10s'%(akey))

the units for line intensities, the key intensity, are erg cm$^{-2}$ s$^{-1}$ sr$^{-1}$ / $\int N_e N_H d\ell $, if the emission meassure em not unspecified

In [ ]:
fe14.intensityPlot(wvlRange=[210.,220.],linLog='log')
In [ ]:
fe14.intensityPlot(index=2, wvlRange=[210.,220.],linLog='log')
In [ ]:
fe14.intensityList(wvlRange=[210.,220.])

here, we have used the default value for the keyword argument top which specified how many of the most intense lines to list. Also, index can be specified to give a different temperature, and relative can be set to 1 give relative emissivities

In [ ]:
fe14.intensityList(wvlRange=[210.,220.], relative=1, index=11)

optionally, an output file could also be created by setting the keyword outFile to the name of the desired name

Intensity Ratios

In [ ]:
fe14.intensityRatio(wvlRange=[210., 225.])

this brings up a plot showing the relative emissivities on the top 10 Fe XIV lines followed by a dialog where you can selector the numerator(s) and denominator(s) of the desired intensity ratio so the specified ratio is then plotted the intensityRatio as a function of temperature and density can be save to a text file with the following

In [ ]:
fe14.intensityRatioSave()
In [ ]:
fe14.intensityRatioSave(outFile='myratio.txt')

saves the ratio to the ascii file 'myratio.txt'

The role of the electron density in line intensities

In [ ]:
del fe14
temp = 2.e+6
dens = 10.**(6. + 0.1*np.arange(61))
fe14 = ch.ion('fe_14', temp, dens)
fe14.popPlot()

a plot of the population of the top 10 levels is produced as a function of the electron density

In [ ]:
fe14.intensityRatio(wvlRange=[210.,220.])

to obtain ratios of lines widely separated in wavelength, the wvlRanges keyword can be used

In [ ]:
temp = 10.**(5.8 + 0.05*np.arange(21.))
edens = 1.e+9
fe12 = ch.ion('fe_12', temperature=temp, eDensity=edens)
fe12.intensityRatio(wvlRanges=[[190.,200.],[1240.,1250.]])

the most recently calculated intensity ratio is stored in fe14.IntensityRatio, with the following keys:

In [ ]:
for akey in sorted(fe14.IntensityRatio):
    print('%10s'%(akey))

G(n,T) function or G(T)

When G(n,T), for specific spectral line, is multiplied by the line of sight emission measure, $\int$ n$_e$ n_$H$ d $\ell$, it provides the predicted value of the line intensity in units of erg cm$^{-2}$ s$^{-1}$ sr$^{-1}$, if the value for flux in the Defaults is set to energy (the default value). If flux is set to photon, the intensity is given in units of

photons cm$^{-2}$ s$^{-1}$ sr$^{-1}$.

A chiantirc file is included with the ChiantiPy distribution. If it is placed in $HOME/.chianti it will be read when ChiantiPy is initiated. Editing this file allows you to specify the values of flux that you want and other things such as the set of elemental abundances and the ionization equilibrium.

In [ ]:
del fe14
temp = 10.**(5.8 + 0.05*np.arange(21.))
dens = 1.e+9
fe14 = ch.ion('fe_14', temp, dens)
In [ ]:
fe14.gofnt(wvlRange=[210., 220.],top=5)

this brings up a plot of relative line ratios vs temperature and a single selection widget, similar to the intensityRatio process. Multiple lines can be selected with the control key and their G(T) functions will summed.

The g(n,T) calculation is stored in the Gofnt dictionary, with keys

In [ ]:
sorted(fe14.Gofnt.keys())
In [ ]:
fe14.Gofnt['wvl']

while the is a fairly straightforward way to get a G(T) function, it is not very practical to use for a more than a handful of lines. For if the fe_14 line at 211.3172 is in a list of lines to be analyzed, a more practical way is the following

In [ ]:
fe14.intensity()
dist = np.abs(np.asarray(fe14.Intensity['wvl']) - 211.3172)
idx = np.argmin(dist)
print(' wvl = %10.3f '%(fe14.Intensity['wvl'][idx]))
In [ ]:
plt.loglog(temp,fe14.Intensity['intensity'][:,idx])

once the axes are properly scaled, this produces the same values as fe14.Gofnt['gofnt']

Spectrum of a single ion

In [ ]:
del fe14
fe14 = ch.ion('fe_14', temperature = 2.e+6, eDensity = 1.e+9)
wvl = 200. + 0.125*np.arange(801)
fe14.spectrum(wvl, em=1.e+27)
plt.figure()
plt.plot(wvl, fe14.Spectrum['intensity'])

this calculates the spectrum of fe_14 over the specified wavelength range and filter it with the default filter which is a gaussian (filters.gaussianR) with a ‘resolving power’ of 1000 which gives a gaussian width of wvl/1000. Other filters available in chianti.filters include a boxcar filter and a gaussian filter where the gaussian width can be specified directly

the units of the vertical axis is erg cm$^{-2}$ s$^{-1}$ sr$^{-1}$ $\mathring A$$^{-1}$ since the value of the emission measure em has been specified

In [ ]:
if hasattr(fe14,'Em'):
    print(' Emission Measure = %12.2e'%(fe14.Em))
else:
    print(' the value for the emission measure is unspecified')
In [ ]:
fe14.spectrum(wvl,filter=(chfilters.gaussian,.4))
plt.figure()
plt.plot(wvl, fe14.Spectrum['intensity'])

calculates the spectrum of fe_14 for a gaussian filter with a width of 0.4 Angstroms. The current value of the spectrum is kept in fe14.Spectrum with the following keys:

In [ ]:
for akey in sorted(fe14.Spectrum.keys()):
    print(' %10s'%(akey))
In [ ]:
if hasattr(fe14,'Em'):
    print(' Emission Measure = %12.2e'%(fe14.Em))
else:
    print(' the value for the emission measure is unspecified')

Here, the previously value of the emission measure has been used

In [ ]:
plt.figure()
plt.plot(wvl,fe14.Spectrum['intensity'])
plt.xlabel(fe14.Spectrum['xlabel'])
plt.ylabel(fe14.Spectrum['ylabel'])

In ChiantiPy 0.6, the label keyword has been added to the ion.spectrum method, and also to the other various spectral classes. This allows several spectral calculations for different filters to be saved and compared

In [ ]:
del fe14
temp = 10.**(5.8 + 0.1*np.arange(11.))
dens = 1.e+9
fe14 = ch.ion('fe_14', temp, dens)
In [ ]:
emeas = np.ones(11,'float64')*1.e+27
wvl = 200. + 0.125*np.arange(801)
fe14.spectrum(wvl,filter=(chfilters.gaussian,.4),label='.4',em=emeas)
fe14.spectrum(wvl,filter=(chfilters.gaussian,1.),label='1.')
In [ ]:
for akey in sorted(fe14.Spectrum.keys()):
    print(' %10s'%(akey))
In [ ]:
for akey in sorted(fe14.Spectrum['.4'].keys()):
    print(' %10s'%(akey))
In [ ]:
plt.figure()
plt.plot(wvl,fe14.Spectrum['.4']['intensity'][5],label='0.4')
plt.plot(wvl,fe14.Spectrum['1.']['intensity'][5],'-r', label = '1.0')
plt.xlabel(fe14.Spectrum['.4']['xlabel'])
plt.ylabel(fe14.Spectrum['.4']['ylabel'])
plt.legend(loc='upper right')

Free-free and free-bound continuum

The module continuum provides the ability to calculate the free-free, free-bound continuum spectrum for a large number of individual ions. The two-photon continuum is produced only by the hydrogen-like and helium-like ions

In [ ]:
temperature = [2.e+7,3.e+7]
em = [2.e+27,1.e+27]
c = ch.continuum('fe_25', temperature = temperature, em=em)
wvl = 1. + 0.002*np.arange(4501)
c.freeFree(wvl)
plt.figure()
plt.plot(wvl, c.FreeFree['intensity'][0],label='ff')
c.freeBound(wvl)
plt.plot(wvl, c.FreeBound['intensity'][0],label='fb')
fe25=ch.ion('fe_25',temperature,1.e+9,em=em)
fe25.twoPhoton(wvl)
plt.plot(wvl,fe25.TwoPhoton['intensity'][0],label='2 photon')
plt.legend(loc='upper right')

In the continuum calculations, Fe XXV in this case, is the target ion for the free-free calculation. For the free-bound calculation, specified ion is also the target ion. In this case, the radiative recombination spectrum of Fe XXV recombining to form Fe XXIV is returned. Note this will bring up an error message. Things are probably OK for the present.

In [ ]:
for akey in  ChiantiPy.tools.data.Defaults.keys():
   print(' %10s - %s'%(akey,ChiantiPy.tools.data.Defaults[akey]))

The multi-ion class bunch

The multi-ion class bunch [new in v0.6] inherits a number of the same methods inherited by the ion class, for example intensityList, intensityRatio, and intensityRatioSave. As a short demonstration of its usefulness, Widing and Feldman (1989, ApJ, 344, 1046) used line ratios of Mg VI and Ne VI as diagnostics of elemental abundance variations in the solar atmosphere. For that to be accurate, it is necessary that the lines of the two ions have the same temperature response.

In [ ]:
t = 10.**(5.0+0.1*np.arange(11))
bnch=ch.bunch(t,1.e+9,wvlRange=[300.,500.],ionList=['ne_6','mg_6'],abundanceName='unity')
bnch.intensityRatio(wvlRange=[395.,405.],top=6)

there seems to be a significant temperature dependence to the ratio, even though both are formed near 4.e+5 K.

A new keyword argument keepIons has been added in v0.6 to the bunch and the 3 spectrum classes.

In [ ]:
dwvl = 0.01
nwvl = (406.-394.)/dwvl
wvl = 394. + dwvl*np.arange(nwvl+1)
In [ ]:
bnch2=ch.bunch(t, 1.e+9, wvlRange=[wvl.min(),wvl.max()], elementList=['ne','mg'], keepIons=1,em=1.e+27)
bnch2.convolve(wvl,filter=(chfilters.gaussian,5.*dwvl))
In [ ]:
plt.plot(wvl, bnch2.Spectrum['intensity'][6],label='Total')
plt.title('Temperature = %10.2e for t[6]'%(t[6]))
In [ ]:
for one in sorted(bnch2.IonInstances.keys()):
    print('%s'%(one))
In [ ]:
plt.plot(wvl,bnch2.IonInstances['mg_6'].Spectrum['intensity'][6],'r',label='mg_6')
In [ ]:
plt.legend(loc='upper left')

Spectra of multiple ions and continuum

The spectrum for a selection of all of the ions in the CHIANTI database can also be calculated. There are 3 spectral classes.

  1. spectrum - the single processor implementation that can be used anywhere
  2. mspectrum - uses the Python multiprocessing class and cannot be used in a IPython qtconsole or notebook
  3. ipymspectrum [new in v0.6] - uses the IPython parallel class and can be used in a IPython qtconsole or notebook

spectrum, mspectrum and ipymspectrum can all be instantiated with the same arguments and keyword arguments. Most of the examples below use the ipymspectrum class for speed.

The single processor spectrum class

In [ ]:
temp = [1.e+6, 2.e+6]
dens = 1.e+9
wvl = 200. + 0.05*np.arange(2001)
emeasure = [1.e+27 ,1.e+27]
s = ch.spectrum(temp, dens, wvl, filter = (chfilters.gaussian,.2), em = emeasure, doContinuum=0, minAbund=1.e-5)
plt.figure()
plt.subplot(311)
plt.plot(wvl, s.Spectrum['integrated'])
plt.title('integrated')
plt.subplot(312)
plt.plot(wvl, s.Spectrum['intensity'][0])
plt.subplot(313)
plt.plot(wvl, s.Spectrum['intensity'][1])

this may raise an error message regarding /usr/lib64/python2.7/site-packages/matplotlib/tri/triangulation.py. At present, this does not seem to be a problem

The multiple processor ipymspectrum class

next, we will use the ipymspectrum class. First, it is necessary to start up the cluster. In some shell

ipcluster start --n=4

or, if you are using Python3

ipcluster3 start --n=4

this will start 4 engines if you have 4 cores.

In [ ]:
temp = [1.e+6, 2.e+6]
dens = 1.e+9
wvl = 200. + 0.05*np.arange(2001)
emeasure = [1.e+27 ,1.e+27]
In [ ]:
s = ch.ipymspectrum(temp, dens, wvl, filter = (chfilters.gaussian,.2), em = emeasure, doContinuum=0, minAbund=1.e-5)
plt.figure()
plt.plot(wvl, s.Spectrum['integrated'])
plt.title('Integrated')

Using a 4 core processor running at about 3.5 GHz:

  • For a minAbund of 1.e-4, the spectra of 25 ions were calculated in 68 s. On a single processor it took 118 s.
  • For a minAbund of 1.e-5, then the spectra of 100 ions would be calculated in 72 s. The previous example with the single processor spectrum class took about 129 s on the same computer.
  • For a minAbund of 1.e-6, then the spectra of 170 ions would be calculated in 90 s. On a single processor it took 183 s.

Using only 3 cores:

  • For a minAbund of 1.e-5, then the spectra of 100 ions would be calculated in 73 s.

Using only 2 cores:

  • For a minAbund of 1.e-5, then the spectra of 100 ions would be calculated in 88 s.

Using only 1 core, with ch.spectrum:

  • For a minAbund of 1.e-5, then the spectra of 100 ions would be calculated in 133 s.

[New in version 0.6] One can also use a different abundance file than the default by specifying the abundanceName keyword. For example, abundanceName = ‘cosmic_1973_allen.abund’. If the specified file is not found in XUVTOP/abundance, then a widget will pop up and one can select the abundance file from a list.

It is also possible to specify a selection of ions by means of the ionList keyword, for example, ionList=[‘fe_11’,’fe_12’,’fe_13’] or with the elementList keyword, for example, elementList=[‘mg’,’si’]

In [ ]:
s2 = ch.ipymspectrum(temp, dens, wvl, filter = (chfilters.gaussian,.2), em = emeasure, doContinuum=0, keepIons=1, elementList=['si'], minAbund=1.e-4)
plt.subplot(211)
plt.plot(wvl,s2.Spectrum['intensity'][0])
plt.ylabel(r'erg cm$^{-2}$ s$^{-1}$ sr$^{-1} \AA^{-1}$')
plt.subplot(212)
plt.plot(wvl,s2.IonInstances['si_9'].Spectrum['intensity'][0])
plt.ylabel(r'erg cm$^{-2}$ s$^{-1}$ sr$^{-1} \AA^{-1}$')
plt.xlabel(r'Wavelength ($\AA$)')
plt.title('Si IX')

Because keepIons has been set, the ion instances of all of the ions are maintained in the s2.IonInstances dictionary. It has been possible to compare the spectrum of all of the ions with the spectrum of a single ion.

In [ ]:
temp=5.e+6
dens=1.e+9
dwvl = .005
nwvl = (50. - 1.)/dwvl
wvl=1. + dwvl*np.arange(nwvl+1)

by setting abundanceName to 1, a widget with a list of possible abundance sets will pop up and allow you to select one. Alternative, you can set abundanceName to the name of one of the abundance file (without the .abund suffix) and it will use that set.

In [ ]:
s3 = ch.ipymspectrum(temp, dens, wvl, filter = (chfilters.gaussian,5.*dwvl),doContinuum=1, em=1.e+40,minAbund=1.e-5,abundanceName=1)
plt.figure()
plt.plot(wvl, s3.Spectrum['intensity'])
In [ ]:
print(' number of ions calculated %i '%(len(s3.IonsCalculated)))

Since the continuum was calculated, it can be plotted separately

In [ ]:
plt.figure()
plt.plot(wvl, s3.FreeFree['intensity'],label='FF')
plt.plot(wvl, s3.FreeBound['intensity'],label = 'FB')
plt.plot(wvl, s3.TwoPhoton['intensity'], label = 'TP')
plt.plot(wvl,s3.FreeBound['intensity']+s3.FreeFree['intensity']+s3.TwoPhoton['intensity'],label='FF+FB+TP')
plt.legend(loc='upper right')
In [ ]:
temperature = 2.e+7
density = 1.e+9
em = 1.e+27
wvl = 1.84 + 0.0001*np.arange(601)
s4 = ch.ipymspectrum(temperature, density ,wvl, filter = (chfilters.gaussian,.0003), doContinuum=1, em=em, minAbund=1.e-5,verbose=0)
In [ ]:
plt.figure()
plt.plot(wvl,s4.Spectrum['intensity'])
plt.ylabel(r'erg cm$^{-2}$ s$^{-1}$ sr$^{-1} \AA^{-1}$')
plt.xlabel(r'Wavelength ($\AA$)')
In [ ]:
temp = 10.**(4.+0.05*np.arange(81))
dens = 1.e+9
rl = ch.radLoss(temp, dens, minAbund=2.e-5)
rl.radLossPlot()

the radiative losses are stored in the dictionary rl.RadLoss

it is also possible to set the keyword argument abundancedName to use different sets of abundances

In [ ]:
for akey in sorted(rl.RadLoss.keys()):
    print(' %s '%(akey))
In [ ]: