Quickstart

This notebook provides a quick introduction in how to use Carsus.

Prerequisites

  • Install Carsus (preferably as a git repository)

  • Install ChiantiPy (from source or pip, both versions work) and set the environment variable XUVTOP = /path/to/chianti/root

  • We highly recommend installing the carsus-db repository as it includes the files of several sources that cannot easily be downloaded

Creating a database

To start working with a database you need to initialize it. This requires an url, for example sqlite:///path/to/example.db. In this quickstart, we’ll use a in-memory sqlite database, so we leave the url empty:

[2]:
from carsus import init_db
session = init_db()
Initializing the database at sqlite://
Ingesting basic atomic data

Now, we have a SQLAlchemy Session object named session. We can use session to make simple queries. As you can see from the output, some “basic atomic data” has been ingested into our database. Let’s examine it:

[3]:
from carsus.model import Atom

print('Number of Atoms in the database: {}'.format(session.query(Atom).count()))
si = session.query(Atom).get(14)
print("Symbol: {}, atomic_number: {}, name: {}".format(si.symbol, si.atomic_number, si.name))
Number of Atoms in the database: 118
Symbol: Si, atomic_number: 14, name: Silicon

So, our database already contains basic information about atoms. But this informaition has not been commited to the database yet. It is always your responsibility to commit data!

[4]:
session.commit()

To store more interesting data we are going to use ingesters. For each data source supported by Carsus there is a corresponding ingester class. For example, let’s ingest atomic weights from the NIST Atomic Weights and Isotopic Compositions database:

[5]:
from carsus.io.nist import NISTWeightsCompIngester

weightscomp_ingester = NISTWeightsCompIngester(session)
weightscomp_ingester.ingest()
session.commit()
Downloading data from the NIST Atomic Weights and Isotopic Compositions database.
Ingesting atomic weights from nist

And now atoms have atomic weights:

[6]:
print(si.weights)
[<Quantity: 28.085 u>]

Other ingesters are just the same, but they often take specific arguments:

[7]:
from carsus.io.nist import NISTIonizationEnergiesIngester

# Ingest ionization energies and ground levels from the NIST Atomic Spectra Database
ioniz_energies_ingester = NISTIonizationEnergiesIngester(session, spectra="Si")
ioniz_energies_ingester.ingest(ionization_energies=True, ground_levels=True)
session.commit()
Downloading ionization energies from the NIST Atomic Spectra Database
Ingesting ionization energies from nist-asd
Ingesting ground levels from nist-asd

IMPRTANT: it’s necessary ingest NIST ionization energies before using GFALLIngester

[8]:
from carsus.io.kurucz import GFALLIngester

# Ingest levels and lines from the Kurucz line list
gfall_ingester = GFALLIngester(session, fname="./gfall.dat", ions="Si 1-2")
gfall_ingester.ingest(levels=True, lines=False)
session.commit()
[carsus.io.kurucz.gfall][WARNING]  A specific combination to identify unique levels from the gfall data has not been given. Defaulting to ["energy", "j"]. (gfall.py:68)
[carsus.io.kurucz.gfall][INFO   ]  Parsing GFALL ./gfall.dat (gfall.py:116)
Ingesting levels from ku_latest
Ingesting levels for Si 1
Ingesting levels for Si 2
[9]:
from carsus.io.chianti_ import ChiantiIngester

# Ingest levels and lines from Chianti database
chianti_ingester = ChiantiIngester(session, ions="Si 1-2")
chianti_ingester.ingest(levels=True, lines=True)
session.commit()
 ChiantiPy version 0.8.4
 found PyQt5 widgets
 using PyQt5 widgets
Ingesting levels from chianti_v9.0
Ingesting levels for Si 1
Ingesting levels for Si 2
Ingesting lines from chianti_v9.0
Ingesting lines for Si 1
Ingesting lines for Si 2
[10]:
from carsus.io.zeta import KnoxLongZetaIngester

# Ingest Knox Long's zeta data
zeta_ingester = KnoxLongZetaIngester(session, './carsus/data/knox_long_recombination_zeta.dat')
zeta_ingester.ingest()
session.commit()

Now we have ionization energies, levels and lines for ions of Si (Note: Si 1 = Si II, Si 2 = Si III, etc.). You should always use arabic numerals when specifying ions:

[11]:
from carsus.model import Ion

si_1 = session.query(Ion).get((14,1))
print(len(si_1.levels))
190

This is it! Now you can explore the your database or create output HDF5 files.

Creating output HDF5 files

Once you have a database it is very easy to create atomic data files for TARDIS. To do this, you need to use a special class called AtomData. The class takes session as its first argument; other important parameters are selected_atoms and chianti_ions. Only data for selected_atoms will be stored in the output DataFrames and levels and lines for chianti_ions will be taken from the data source with the same name. Let’s create an AtomData instance:

[12]:
from carsus.io.output import AtomData
atom_data = AtomData(session, selected_atoms="Si", chianti_short_name='chianti_v9.0', chianti_ions="Si 1-2")

The output DataFrames can be easily accessed as “prepared” attributes of atom_data:

[13]:
print(atom_data.atom_masses_prepared)
              symbol        name    mass
atomic_number
14             b'Si'  b'Silicon'  28.085
[14]:
print(atom_data.levels_prepared.head(20))
                                          energy  g  metastable
atomic_number ion_number level_number
14            0          0              0.000000  1        True
              1          0              0.000000  2        True
                         1              0.035613  4        True
                         2              5.309535  2        True
                         3              5.322966  4        True
                         4              5.344700  6        True
                         5              6.857485  4       False
                         6              6.859448  6       False
                         7              8.121023  2       False
                         8              9.505292  2       False
                         9              9.836720  4       False
                         10             9.838768  6       False
                         11            10.066443  2       False
                         12            10.073880  4       False
                         13            10.390118  2       False
                         14            10.415201  4       False
                         15            12.146991  2       False
                         16            12.525262  4       False
                         17            12.525423  6       False
                         18            12.839311  8       False
[15]:
print(atom_data.macro_atom_prepared.head(20))
    atomic_number  ion_number  source_level_number  destination_level_number  \
0              14           1                    0                         5
1              14           1                    0                         7
2              14           1                    0                         8
3              14           1                    0                         9
4              14           1                    0                        13
5              14           1                    0                        14
6              14           1                    0                        15
7              14           1                    0                        16
8              14           1                    0                        24
9              14           1                    0                        25
10             14           1                    1                         7
11             14           1                    1                         8
12             14           1                    1                         9
13             14           1                    1                        13
14             14           1                    1                        14
15             14           1                    1                        15
16             14           1                    1                        16
17             14           1                    1                        24
18             14           1                    1                        25
19             14           1                    1                         6

    transition_type  transition_probability  transition_line_id
0                 1            0.000000e+00                   4
1                 1            0.000000e+00                   5
2                 1            0.000000e+00                   6
3                 1            0.000000e+00                   7
4                 1            0.000000e+00                   8
5                 1            0.000000e+00                   9
6                 1            0.000000e+00                  10
7                 1            0.000000e+00                  11
8                 1            0.000000e+00                  12
9                 1            0.000000e+00                  13
10                1            3.573898e+08                  19
11                1            1.898953e+08                  20
12                1            2.517380e+08                  21
13                1            3.241519e+08                  23
14                1            1.582578e+09                  24
15                1            2.789657e+07                  25
16                1            3.114504e+07                  26
17                1            8.794534e+06                  28
18                1            6.684619e+06                  29
19                1            6.417095e+06                  18

To create an HDF5 file you need to use the to_hdf() method. You should specify every DataFrame that you want to store:

[16]:
atom_data.to_hdf("example_store.h5", store_atom_masses=True, store_ionization_energies=True,
                store_levels=True, store_lines=True, store_macro_atom=True, store_zeta_data=True)
Signing AtomData:
MD5: 444891792f77d8977cc1b632c5b0322f
UUID1: 7e20522ca39011e98329c83dd46c2c53

You are done! Now you can use the created HDF5 atomic file to run TARDIS simulations!