# ---
# jupyter:
#   jupytext:
#     text_representation:
#       extension: .py
#       format_name: light
#       format_version: '1.5'
#       jupytext_version: 1.14.4
#   kernelspec:
#     display_name: Python 3
#     language: python
#     name: python3
# ---

# # Intermediate Python II
#
# Please indicate your name below, since you will need to submit this notebook completed latest the day after the datalab.
#
# Don't forget to save your progress during the datalab to avoid any loss due to crashes.

name=''


# During the previous datalab we learnt about the basic principles of numpy, matplotlib, scipy, and reviewed how to perform pattern searh with regular expressions. 
#
# In this datalab we progress further and learn about several things. 
#
# 1. We will review how to write a class in python (our first step towards object oriented programming). Although you will not be required to build classes during this course,  later when we will use the OpenMC Monte Carlo particle transport code, it will be adventageous if you have a basic understanding of classes. Also most python packages provide an API where to understand classes is inevitable.
#
# 2. There are several data formats or markup languages to store data, here we will get acquinted with three which is often used in nuclear engineering software and for nuclear data:
#     - csv: comma separated values
#     - XML: eXtensible Markup Language
#     - JSON: JavaScript Object Notation
#
# 3. We will learn about an other mainstream libary: pandas, which is often used for data analysis. The reason we will need some basic idea about this package is that we will later extract OpenMC results as pandas DataFrames.
#
# 4. We will learn how to use python as an internet browser!
# 5. Finally we review how to install and use not mainstream python packages. 
#
# So let's get started.
#
# ## Class
#
# A Class is a collection of data and functionality. It allows us to define a new type of object, and after the Class definition we can create new instances of that class. An instance will have **attributes** (variables describing the state of the instance) and **methods**, functions which allow us to modify the state of the instance.
#
# As with everything else in Python, there is no "best" way to define and use classes, here we are presenting only one general solution, and we are omitting the demonstration of several functionality (for example inheritence). We however encourage you to further develop your understanding with reading the official [python site](https://docs.python.org/3/tutorial/classes.html).
#
# Let's try to make this more clear with an example. Consider that we are writing a program, in which we often define materials (for example for a reactor physics application). The materials are being described by the nuclide content, and the density (attributes), and it has some methods to modify these attributes. We can define a class with the `class` keyword, which is then followed by statements. A class can have a special method `__init__`, which will be executed when an instance of the class is created, to create an initial state. Our class also has an other special method `__repr__`, which returns the object representation in string format. None of this is mandatory. Besides these we define three methods to add nuclides, set the density and to convert the density to other units. One could access and update the state of the instance directly with for example `myMaterial.density = 10.0`, but it is often better programming practice to define a method to do such updates. Notice that all the methods have a parameter `self`, this is how you refer to the instance itself, and when you call `myMaterial.set_density(10.0)`, then infact `myMaterial` is also an input of the method. 

# +
class Material:
    """A class used to represent a Material.
    Parameters
    ----------
    nuclides : dict
      Dictionary to store nuclides. Keys nuclide identifiers, values atomic fraction.
    density : dict
      Density of material in g/cm3
    Attributes
    ----------
    nuclides : dict
        Dictionary to store nuclides. Keys nuclide identifiers, values atomic fraction.
    density : float
        Density of material in g/cm3
    """

    def __init__(self, nuclides={},density=None):
        """Function to initialize an instance of Material()"""
        self.nuclides = nuclides
        self.density = density
        
    def __repr__(self):
        if self.density is None:
            return "This is a material with no specified density"
        else:
            return "This is a material with density %.2f" %self.density

    def add_nuclide(self, symbol, fraction):
        """The method adds or updates the atomic fraction of a nuclide.
        
        Parameters
        ----------
        symbol : str
            Symbol of the nuclide (eg. U-238)
        fraction : float
            Atomic fraction of the nuclide.
        """
        self.nuclides[symbol]=fraction
        
    def set_density(self, density=None):
        """Method to set the density of the Material.
        
        Parameters
        ----------
        density : float
            density of material in g/cm2
        """
        self.density=density
        
    def get_density(self,unit='g/cm3'):
        """Method to get the density of the instance."""
        if self.density is None:
            raise ValueError('Density is not specified yet')
        elif unit=='g/cm3':
            return self.density
        elif unit=='kg/m3':
            return self.density*1000
        else:
            ValueError('Only g/cm3 and kg/m3 are available')
    
        
# -

# Let's create an instance of our `Material()` class.

iron=Material()

iron

# But we can create an Material instance by immediately specifying the atomic percentages of nuclides and the density.

aluminium = Material({'Al27': 0.5, 'Al25': 0.5},density=3.6)

aluminium

aluminium.get_density(unit='kg/m3')

iron.set_density(6.3)

iron

iron.add_nuclide('Fe55',0.02)

iron.nuclides

# That's about what you have to know about classes for this course.
#
# Now let's move onto an other topic and review various data formats often used to store data.
#
# ## Data formats
#
# When writing a software which is intended to produce data we often want to export it in some format. Some legacy nuclear engineering codes would produce lengthy ASCII files, which are easy to read as a human, but as we saw before, might be tricky to read with a computer. To overcome this, it is better to export our data into standardized formats which are easy to interpret during post-processing of the data. Of course there are plenty of such formats to pick from, and we do not intend to review all of them. Notably, we are not going to look into the [hierarchical data format (HDF)](https://en.wikipedia.org/wiki/Hierarchical_Data_Format) which became rather popular recently to store large amount of data (and as we will see later openMC also exports all data into hdf5 files). 
#
# We will however take a look at some "simpler" (ie. readable by humans) formats: like JSON, csv, XML, which are often used in nuclear engineering applications. Along this notebook you will find three example files named '03-sample.json','03-sample.xml' and '03-sample.csv'. Although we will look into how to read these files with Python, it is also important that you just visually inspect them with a text editor.
#
# The power of these standardized formats is that most of the programming languages provide readily available libraries and functions to parse these formats. So it is simple to transfer data between various applications.
#
# ### JSON
#
# JSON stands for JavaScript Object Notation, and is a standard file format which uses human-readable text to store data, and consists of attribute-value pairs and arrays. Remember dictionaries and lists? Dictionaries were attribute (key)-value pairs, and lists were arrays. So although JSON originates from JavaScript, in fact the syntax of the JSON format resembles the syntax of Python. 
#
# Just to appreciate even more why is it worth learning about JSON: after this lecture you will be well prepared to write your own scripts to visualize various aspects of [Covid-data](https://covid.ourworldindata.org/data/owid-covid-data.json), what others might not have thought of yet.
#
# The file '03-sample.json' contains the results of a calculation where a piece of iron was irradiated with 14 MeV neutrons (the file was created with an activation and transmutation code called FISPACT-II, later during the course we will learn more about transmutation calculations). If we review the content of the file, we see that indeed it very much looks like a dictionary with keys called `run_data` and `inventory_data`, and stores lists and nested dictionaries as values.
#
# ```json
#     {
#       "run_data": {
#         "timestamp": "16:03:07 20 April 2021",
#         "run_name": "* Iron irradiation",
#         "flux_name": "Neutron generator Spectrum"
#       },
#       "inventory_data": [
#         {
#           "irradiation_time": 0.0E+0,
#           "cooling_time": 0.0E+0,
#           "flux": 0.21733749239999996E+7,
#           "total_heat": 0.0E+0,
#           "alpha_heat": 0.0E+0,
#           "beta_heat": 0.0E+0,
#           "gamma_heat": 0.0E+0,
#           "ingestion_dose": 0.0E+0,
#           "inhalation_dose": 0.0E+0,
#           "dose_rate": {
#             "type": "",
#             "distance": 0.0E+0,
#             "mass": 0.0E+0,
#             "dose": 0.0E+0
#           },
#           "nuclides": [
#             {
#               "element": "Fe",
#               "isotope": 54,
#               "state": "",
#               "half_life": 0.0E+0,
#           ...
# ```
#
# So let's go ahead and open this file! Hopefully you are not going to be surprised that for this we are going to use the python package called `json`.

import json
with open('03-sample.json') as json_file:
    inventory = json.load(json_file)

# Wow, that was it! It was as simple as reading a file with `read()`. Now we have a dictionary storing everything. Pretty cool, huh? 
#
# Shall we take a look at the content?

inventory.keys()

# I will actually tell you that `inventory['inventory_data']` is a list containing some data (activity of the irradiated material, and the nuclides in the material) at certain irradiation steps. So you of course have to know the basic structure of your JSON file to make sense out of it, and reach the content you really want to work with. And if it is a large amount data nested deeply, this takes some thinking (but the good news is that you are university students, so thinking is part of the deal!). And you might end up with some pretty nasty looking indexing. 
#
# For example this is the mass of Fe-54 after 7200 s (90 mins) of irradiation. One needs to review first the JSON file (either with a script or by reading it) to know that the first index goes through the time steps, and the second goes through the nuclides.

#           index 4 belongs to 7200 s   index 19 is Fe-54
inventory['inventory_data'][4]['nuclides'][19]['grams']

# And this file is not even that bad!
#
# If we have a dictionary writing it into a JSON file is also simple:
#
# ```python
#     with open('myfile.json', 'w') as outfile:
#         json.dump(myDict, outfile, indent=4)
# ```
#
# But let's leave JSON files behind for now, and start to look at an other format.
#
# ## XML
#
# XML stands for eXtensible Markup Language and defines rules for encoding data in a format which is both human-readable and machine-readable. One can define attributes and elements within starting and closing tags, and also attributes between empty-element tags. XML is a bit more difficult to interpret than the JSON format. The main reason we take a quick look at XML is because openMC has XML inputs. Nevertheless, we will not interact with openMC through these inputs, but through a python API. 
#
# An example openMC input looks like '03-sample.xml'. This file describes materials for the code. We have a main or parent tag (`<materials>`), which has children tags (`<material>`) etc. This input defines three materials: `uo2` (uranium-dioxide, $UO_2$) with density 10.5 g/cm3 containing the isotopes U235, U238 ad O16, `zirconium` with density 6.6 g/cm3 containing various zirconium isotopes, and `h2o` (water) with density 0.75 g/cm3 made of H1 and O16.
#
# ```xml
#     <?xml version='1.0' encoding='utf-8'?>
#     <materials>
#       <material depletable="true" id="1" name="uo2">
#         <density units="g/cm3" value="10.5" />
#         <nuclide ao="0.04" name="U235" />
#         <nuclide ao="0.96" name="U238" />
#         <nuclide ao="2.0" name="O16" />
#       </material>
#       <material id="2" name="zirconium">
#         <density units="g/cm3" value="6.6" />
#         <nuclide ao="0.1122" name="Zr91" />
#         <nuclide ao="0.028" name="Zr96" />
#         <nuclide ao="0.1715" name="Zr92" />
#         <nuclide ao="0.1738" name="Zr94" />
#         <nuclide ao="0.5145" name="Zr90" />
#       </material>
#       <material id="3" name="h2o">
#         <density units="g/cm3" value="0.75" />
#         <nuclide ao="2.0" name="H1" />
#         <nuclide ao="1.0" name="O16" />
#         <sab name="c_H_in_H2O" />
#       </material>
#     </materials>
# ```
#
# Parsing such a file is a bit trickier (lucky that you will not need to do it too much!), and can be done with the `xml.etree.ElementTree` module.

import xml.etree.ElementTree as ET
import re
tree = ET.parse('03-sample.xml')
root = tree.getroot()

root

# We are not much smarter! Apparently root is some sort of instance of a class, and not the actual values stored in it. Somehow we have to access those. If we make a list of this, we just seem to get several other "Element" objects. Then they need to have some attributes!

list(root)

root[0].attrib

list(root[0])

root[0][0].attrib

# Heureka, the `root` contains three 'material' elements, and apparently we can access the attributes of those as dictionaries! Also we see that the 'material' elements have further elements, which have attributes.
#
# It's like going through a tree and picking different branches to find the final leaf which contains the data we need. Let's try to print the material names and their densities.

for child in root:
    name=child.attrib['name']
    for grandchild in child:
        if 'value' in grandchild.attrib:
            density=grandchild.attrib['value']
            print(name,density)

# Well, this was not very straightforward, but one can get used to it! If you are interested in exploring the further beauties of XML, there is an example in the notebook demonstrating how to create the  figures in the lecture notes, where an even more complex XML file is parsed.
#
# But for today I think we can all agree that our heads are overwhelmed with XML, so let's get back to simpler formats.
#
# ## csv
#
# Csv stands for comma separated values, and is a very straightforward, although not fully standardized format. Essentially the file contains data records (rows), and a record contains of one or more fields (columns), separated by comma. You can imagine it as a typical spreadsheet (in fact in most systems csv files will by default be opened by spreadsheet applications). 
#
# Let's take a look at '03-sample.csv'. This file stores the concentration (in $\text{cm}^{-1}\text{barn}^{-1}$ units) of some nuclides for uranium-dioxed fuels with various initial enrichment (IE), irradiated for various time length (BU: burnup), and cooled for different times (CT) calculated with a software called Serpent2. We will learn later more about the fuel evolution, and how to calculate such concentrations. For now the important part is to notice, that the first row seems to be a header, describing "keys" for each column. The following rows contain the data (one record belongs to one fuel). 
#
# ```
#     ,BU,CT,IE,Cs134,Cs137,Eu154,U235,Pu239
#     0,23,3102.5,1.5,1.88424E-07,2.84566917879002E-05,2.69679813433217E-07,8.22522171634031E-05,0.000121476
#     1,30,7482.5,1.5,5.20168E-09,2.79799245808655E-05,1.52985963215638E-07,5.42325572566305E-05,0.000129026
#     2,37,4197.5,1.5,1.45199E-07,4.21499771616296E-05,4.17562528731865E-07,3.59841349105039E-05,0.000134992
#     3,52.5,6752.5,1.5,2.16965E-08,4.9979226189458E-05,3.50484414413579E-07,1.50402501420245E-05,0.00014544
#     4,65.5,3102.5,1.5,8.08478E-07,7.74388092788155E-05,9.6942317979626E-07,7.48810453458104E-06,0.00015283
#     5,30,91.25,1.6,4.63841E-06,4.46352992585013E-05,7.77532100448014E-07,6.15403234287554E-05,0.0001299283
#     6,34.5,8395,1.6,2.7162412E-09,3.01868876171812E-05,1.5011569350238E-07,4.75259853623482E-05,0.00013365
#     7,57,1368.75,1.6,3.42660E-06,7.59309237451187E-05,1.25129574849322E-06,1.35365167615274E-05,0.00014867
#     8,57,5840,1.6,5.59338E-08,5.7224184909055E-05,4.66071542695002E-07,1.35888359894115E-05,0.000148643804
#     9,27.5,365,1.7,3.08791E-06,4.02748433202162E-05,6.37972051142385E-07,8.0098420206549E-05,0.00012842678
#     10,35,7847.5,1.7,4.55574E-09,3.16746996946991E-05,1.7181072908398E-07,5.23717817097577E-05,0.000134991
# ```
#
# We could read csv files with the built-in csv reader (and interested readers can follow [this tutorial](https://realpython.com/python-csv/)). We can also use the `numpy.loadtxt` function we learnt about last time, just now by setting the delimiter to ',' and skipping the header row:

import numpy as np
import matplotlib.pyplot as plt
fuelmatrix=np.loadtxt('03-sample.csv',delimiter=',',skiprows=1)
fuelmatrix

# Now we remember from before how to access given columns of this 2D numpy array, and we can go ahead and plot the Uranium-235 (index 7) content against the burnup (index 1).

plt.figure()
plt.plot(fuelmatrix[:,1],fuelmatrix[:,7],'x')
plt.xlabel('Burnup')
plt.ylabel('U-235 concentration')
plt.show()

# We see that there is a general trend that with increasing burnup (more time under irradiation) we have less U-235 (since we loose it in fission events as we will learn more about this). The markers are not aligned along a straight line, because each marker belongs to a fuel with a unique initial enrichment.
#
# This works pretty well, but apparently we lost the information about the header, so we need to keep in mind the right index for the columns (or have some workound which maps the columns to the right key). This is fine when we have few columns but what if we had hundreds?
#
# To resolve this, now we will look into an other mainstream data analysis library called pandas, which has a data container called DataFrame which combines the power of numpy arrays and dictionaries!
#
# ## pandas
#
# Pandas is a data analysis and manipulation tool. The main reason we have to take a look at it (besides it being very powerful) is that later we will handle openMC results as pandas DataFrames.
#
# We will use the `read_csv` method of pandas to read in the '03-sample.csv' file as a DataFrame, and look at the basic functionality. Notice that we can specify which row contains the header information (so we will be able to refer to the columns with keys) and we can preselect the columns which we would like to import. DataFrames will be rendered as nice looking tables in jupyter.

# +
import pandas as pd

fueldata=pd.read_csv('03-sample.csv',header = 0, usecols=['BU','CT','IE','Cs137','U235'])
# -

# Creating the same plot as before can be done without remembering the index of the columns.

plt.figure()
plt.plot(fueldata['BU'],fueldata['U235'],'x')
plt.xlabel('Burnup (MWd/kgU)')
plt.ylabel('U-235 concentration')
plt.show()

# We can filter certain parts of the dataset similarly as we learnt for numpy arrays previously. This is to be done by applying conditions. For example if we would like to filter a subset of fuels for which $40<BU<50$, we can do the following:

# +
subfuel=fueldata[(fueldata['BU']>40) & (fueldata['BU']<50)]
print(len(subfuel))

subfuel.head()
# -

plt.figure()
plt.plot(fueldata['BU'],fueldata['U235'],'x')
plt.plot(subfuel['BU'],subfuel['U235'],'rx')
plt.show()

# Pandas provides a lot of further functionality (remember, with hitting TAB on `fueldata.` you can list the available methods), which are relevant for data analysis, although in this course we will not use them. Just to demonstrate its further power let see how we can calculate the correlation of the different parameters:

fueldata.corr()

# This table gives us the correlation matrix. What we see is that the diagonal is filled with ones. Of course, since a parameter is fully correlated to itself. Due to similar considerations the matrix is symmetric along the diagonal. We can also notice that the BU, CT and IE parameters are not correlated. What we can however notice is that the Cs-137 content is strongly correlated to the burnup (the longer the fuel is irradiated, the more is created, as we will see in later lectures), and is also strongly correlated (even if the value is negative) to the cooling time (the longer the fuel is cooled, the the more Cs-137 has decayed away). How would you interpret the correlation of U235 to the other parameters?
#
# So this is very neat, you did a great job and learnt about several data storage options, if you invest some time and practice into this topic you will simply be awesome!
#
# But now let's turn our attention to a slightly different topic.
#
# ## Browsing the internet
#
# Let's be honest, we do spend an awful lot of time on the internet, reading the news, streaming movies, and sometimes digging up nuclear data. It would be just nice if we could automatize part of this. Let's say that the part we want to automatize is the digging nuclear data (movie streaming part would be also nice, but it is outside of the scope of this course). The way how we access data from the internet is most often with a browser: we type in a URL, and based on that our browser sends a request to a server (with the GET method of the Hypertext Transfer Protocol) containing the data we are interested in, then the data is sent back to us, and the browser renders it. But actually we can do the same with python, for example with the `urllib.request` package. And how it is being implemented is not that different at all from how we would read a file on our computer.
#
# Let's try to access the U-235 fission cross section data from the KAERI site (if you are not familiar with the side, you can watch the related short video). Note, that this might take few seconds depending on your connection. In case the connection times out, just try to run it again. (Also note, that the two lines related to SSL is needed because the page does not provide an SSL certificate, those lines are not necessarily needed for other webpages.)

# +
import urllib.request
import ssl
ssl._create_default_https_context = ssl._create_unverified_context

website='http://atom.kaeri.re.kr/nuchart/getData.jsp?target=jeff3.2,92,235,9228,3,18'

with urllib.request.urlopen(website) as response:
    content235 = response.readlines()
# -

content235[:15] #inspect the first 15 lines


# So the data received is in HTML. In order to make any sense of it, we will need to parse it. There are some readily available parsers for HTML (a popular one is called Beautiful Soup), but since we are not going to parse very difficult HTML code, instead of overwhelming ourselves by reviewing yet an other parser library, I just provide you with a function which can extract the data:

def getXS(content):
    """Function to extract data from the html content provided by KAERI.
    the content has a shape as follows like:    
    Energy(eV) XS(b)<br>
    1.00000E-05 3.07139<br>"""
    energy=[]
    xs=[]
    flag=False
    i=0
    for line in content:
        x=line.strip().split()
        if  x[0]==b'Energy(eV)':
            flag=True
            continue
        if x[0]==b'</span>':
            flag=False
        if flag:
            energy.append(float(x[0]))
            xs.append(float(x[1][:-4]))
    return np.array(energy), np.array(xs)


# +
energy,xs=getXS(content235)

plt.figure()
plt.loglog(energy,xs)
plt.xlabel('Energy (eV)')
plt.ylabel('Cross section (barn)')
plt.show()
# -

# And we can do a lot more! We could download zip files, extract the content etc. The important take away message here is, that with python any file manipulation, even if it is stored somewhere on the world wide web, can be done relatively easily. Should you have any idea like that, just search and you will find solutions. 
#
# ## Installing and using other packages
#
# It was mentioned a lot of times that you can find several packages, and use them. But how to do that? It is fairly simple. Once you found the package you need, you can install it with the python package installer `pip` (or with `conda`, depending on how you installed Python), which you can run from your terminal or command prompt. Some packages are registered to [PyPI](https://pypi.org/) the Python package index, these you can simply install with `pip install nameofpackage`. There are some which are not registered at PyPI and deployed at some other site, like github. These you can usually install with `pip install urlofpackage`. Nevertheless, any well maintained package will let you know how to install it, and you just have to follow the instructions. Also a well maintained package will include a 'setup.py' file, which lists the dependencies of the package (for example that it requires NumPy), from which `pip` will figure out whether any other dependent packages need to be installed as well.
#
# As a typical example we can take a look at a package called `radioactivedecay`: https://pypi.org/project/radioactivedecay/. The site clearly describes that you can install it with `pip install radioactivedecay`. Once that is done, you can import the package in your scripts. Also developers usually provide examples, which with the knowledge you have acquired up to now, you can easily follow. For example the usage of this package is the following:
#
# ```python
#     >>> import radioactivedecay as rd
#     >>> inv_t0 = rd.Inventory({'Mo-99': 2.0})
#     >>> inv_t1 = inv_t0.decay(20.0, 'h')
#     >>> inv_t1.contents
#     {'Mo-99': 1.6207863893776937,
#     'Tc-99': 9.05304236308454e-09,
#     'Tc-99m': 1.3719829376710406}
# ```
#
# First, you import the library. Then, you create an instance of the `Inventory()` class, which takes a dictionary as as parameter which is supposed to contain the activity of isotopes. Then, you call the `.decay()` method while specifying the decay time, and you can access the `contents` of the inventory after the decay time elapsed. 
#
# Since most of the Python packages are open source, you can build on them, or modify them if you find that some functionality is missing. 
#
# Again, the take away message here is that if you have a problem to be solved, do not hesitate to first look for a package which already solves it.

# # Exercises 
#
# ## 1
#
# Extend the `Material()` class by including a method which converts the mass density to atom density. First, renormalize the atomic fractions to make sure that it sums to 1. Then use `re` to get the atomic number of the nuclides in the nuclide vector. 

# +
#your code comes here
# -

# ## 2
#
# Inspect the '03-sample.json' file. Extract the ingestion dose values (`ingestion_dose`) over time, and extract the time. Notice that after a while the irradiation ended, and the `inventory['inventory_data'][i]['irradiation_time']` values do not increase, however the target was let to cool (ie. undergoes decay), and the `inventory['inventory_data'][i]['cooling_time']` increases (the values are in second). Find out the total elapsed time. Try to use list comprehension (see 1st datalab) to extract both the ingestion dose and time with one line of code.

# +
#your code comes here
# -

# ## 3
#
# Use the '03-sample.csv' dataset, and filter it to get a subset of fuels for which $40<BU<50$, $2.5<IE<3.5$ and $10y<CT<20y$. How many of such samples are available (Note, that the cooling time is given in days in the dataset)

# +
#your code comes here
# -

# Your conclusion comes here.

# ## 4 
#
# Notice that the URL we used to access cross section data has some numbers in it: 'http://atom.kaeri.re.kr/nuchart/getData.jsp?target=jeff3.2,92,235,9228,3,18'. You probably figured out that the number 92 is the Z, and 235 is the A. The other data also have some meaning. 9228 is something called the MAT number, 3 is the MF number, and 18 is the MT number. Scientists working on nuclear data just like to have everything in order! The MAT number refers to the nuclide, the MF number refers to the type of data (MF=3 stands for cross section vs energy), and the MT number describes the reaction type (MT=18 is fission, MT=1 is total cross section). That said, it seems we can automatically create strings describing urls to reach various cross section data. And that is exactly what we are going to do now!
#
# You have the following dictionary storing MAT and MT numbers (MF should be always 3 in our case, since we want to download cross sections vs energy), try to write a `for` loop to iterate through them and use `urllib.request` to download the HTML code describing the data, Then use the previously defined `getXS()` function to extract the energy and cross section data, and update the empty lists with that (eg. `data['Pu239fiss']['xs']=...`). Plot each cross section in the same figure. Which isotope of plutonium seems to be fissile?

# +
data={'Pu239fiss': {'Z':94, 'A':239,'MAT':9437,'MT':18,
                  'energy':[],'xs':[],'col':'r-'},
      'Pu239tot': {'Z':94, 'A':239,'MAT':9437,'MT':1,
                  'energy':[],'xs':[],'col':'r--'},
      'Pu240fiss': {'Z':94, 'A':240,'MAT':9440,'MT':18,
                  'energy':[],'xs':[],'col':'b-'},
      'Pu240tot': {'Z':94, 'A':240,'MAT':9440,'MT':1,
                  'energy':[],'xs':[],'col':'b--'},
      'Pu242fiss': {'Z':94, 'A':242,'MAT':9446,'MT':18,
                  'energy':[],'xs':[],'col':'g-'},
      'Pu242tot': {'Z':94, 'A':242,'MAT':9446,'MT':1,
                  'energy':[],'xs':[],'col':'g--'}}

#your code comes here
# -

# Your conclusion comes here.

# ## 5
#
# Open a terminal window (if you are using the virtual machine, otherwise a command prompt), and install the package called `radioactivedecay`, and then after importing it, execute the examples from https://pypi.org/project/radioactivedecay/.
#
# (Note that you might need to restart jupyter for the installation take effect, make sure to save your work first.)

# +
#your code comes here

StarcoderdataJupyterScriptsDedupFiltered