Tutorial

Importing the package in Python

Import the radioactivedecay package by:

>>> import radioactivedecay as rd

Creating inventories of nuclides

Create an inventory of nuclides as follows:

>>> inv_t0 = rd.Inventory({'U-238': 99.274, 'U-235': 0.720, 'U-234': 0.005}, 'mol')

This is an inventory of natural uranium. The amounts of each nuclide were specified in moles.

Within the code, the Inventory class keeps track of the number of atoms of each nuclide it contains. The following commands can be used to show the contents in terms of activities, numbers of atoms, moles, masses, or abundances:

>>> inv_t0.activities('MBq')
{'U-234': 269.4016050086039, 'U-235': 13.528246506057048, 'U-238': 293.90300567125}
>>> inv_t0.nuclides
['U-234', 'U-235', 'U-238']
>>> inv_t0.numbers()
{'U-234': 3.01107038e+21, 'U-235': 4.3359413471999995e+23, 'U-238': 5.9784200180824e+25}
>>> inv_t0.masses('g')
{'U-234': 1.17020475148, 'U-235': 169.23162824424, 'U-238': 23632.253822284463}
>>> inv_t0.mass_fractions()
{'U-234': 4.916278117985593e-05, 'U-235': 0.007109779290811992, 'U-238': 0.992841057928008}
>>> inv_t0.moles()
{'U-234': 0.005, 'U-235': 0.72, 'U-238': 99.274}
>>> inv_t0.mole_fractions()
{'U-234': 5.000050000500006e-05, 'U-235': 0.0072000720007200075, 'U-238': 0.992749927499275}
>>> inv_t0
Inventory activities (Bq): {'U-234': 269401605.0086039, 'U-235': 13528246.506057048, 'U-238': 293903005.67125}, decay dataset: icrp107_ame2020_nubase2020

By default the dictionary passed to the Inventory() constructor is assumed to contain activities in Bq. The user can easily specify different units:

# initialize an inventory using activities:
>>> inv = rd.Inventory({'C-14': 5.0, 'H-3': 2.0})
>>> inv.activities('Bq')
{'C-14': 5.0, 'H-3': 2.0}
>>> inv.numbers()
{'C-14': 1297520091697.4946, 'H-3': 1121785791.5588164}

# initialize an inventory using number of atoms:
>>> inv = rd.Inventory({'U-238': 2000.0, 'U-235': 3000.0, 'U-234': 1500.0}, 'num')
>>> inv.activities('Bq')
{'U-234': 1.3420556696283726e-10, 'U-235': 9.360075764027427e-14, 'U-238': 9.832129719300668e-15}
>>> inv.numbers()
{'U-234': 1500.0, 'U-235': 3000.0, 'U-238': 2000.0}

It is also possible to create an inventory by reading directly from a CSV-type file via the rd.read_csv() function. The file’s first column should contain the nuclides, and the second column the amount of each nuclide.

An optional third column can be provided with the unit, which can differ for each nuclide. This makes it possible to load an inventory from mixed activity, moles or mass units for each nuclide.

Example CSV file, saved as e.g. example_file.csv:

nuclide|amount|units
C-14|5.0|Ci
H-3|0.2|g
He-3|1|mol

Read command - skip_rows=1 is required to ignore the header row:

>>> inv = rd.read_csv('example_file.csv', delimiter='|', skip_rows=1)

Radioactive decay calculations

Use decay() to perform a radioactive decay calculation on the natural uranium inventory:

>>> inv_t1 = inv_t0.decay(1E9, 'y')
>>> inv_t1.activities('Bq')
{'Ac-227': 5054315.0114205815, 'At-218': 50337.39144073731,
 'At-219': 4.184972829456502, 'Bi-210': 251686958.45501313,
 'Bi-211': 5054319.0714315465, 'Bi-214': 251686906.8663001,
 'Bi-215': 4.059423644572891, 'Fr-223': 69749.54715760818,
 'Hg-206': 4.782052210630576, 'Pa-231': 5054314.855110146,
 'Pa-234': 402670.06690959306, 'Pa-234m': 251668791.81845263,
 'Pb-206': 0.0, 'Pb-207': 0.0,
 'Pb-210': 251686958.45423996, 'Pb-211': 5054319.071431517,
 'Pb-214': 251636619.8122487, 'Po-210': 251686958.47635475,
 'Po-211': 13949.920637151068, 'Po-214': 251634102.95324954,
 'Po-215': 5054319.071431024, 'Po-218': 251686957.20368654,
 'Ra-223': 5054315.01200738, 'Ra-226': 251686957.20309648,
 'Rn-218': 50.33739144073732, 'Rn-219': 5054315.01200738,
 'Rn-222': 251686957.20368624, 'Th-227': 4984565.464625097,
 'Th-230': 251686867.07347885, 'Th-231': 5054079.657163195,
 'Th-234': 251668791.81845245, 'Tl-206': 337.00883737124855,
 'Tl-207': 5040369.15079446, 'Tl-210': 52854.250441923046,
 'U-234': 251682620.8433893, 'U-235': 5054079.657142295,
 'U-238': 251668791.8147358}

The decay() method takes two arguments: the decay time period and its units. Units can be entered using 'ps', 'ns', 'us', 'ms', 's', 'm', 'h', 'd', 'y', 'ky', 'My', 'Gy', 'Ty' and 'Py' for picoseconds, nanoseconds, microseconds, milliseconds, seconds, minutes, hours, days, years, kiloyears, megayears, gigayears, terayears and petayears, respectively. In the above case we decayed for one billion years.

The decay_time_series_pandas() method can be used if, rather than just the values at the end of the time period, access to finer resolution decay data is required. The method runs decay(), storing the data at each iteration and returning the complete data set as a pandas dataframe. The only required argument is the decay time period as a number, or as a numpy array with the individual decay times data is needed for. Optional arguments are the decay time units in the same format as for the decay() method, the decay units, whether the time scale should be linear or logarithmic and, if the decay time is given as a float, how many decay points should be calculated.

Using decay_time_series_pandas() to interrogate how the mass fraction of 14C decays over 20,000 years with 14N taking it’s place. The default value for the number of point to calculate is 501 so we will limit to 10 for this example

>>> inv = Inventory({'C-14': 1.0})
>>> inv.decay_time_series_pandas(time_period=20, time_units='ky', decay_units='mass_frac', npoints=10)
               C-14      N-14
Time (ky)
0.000000   1.000000  0.000000
2.222222   0.763204  0.236796
4.444444   0.582480  0.417520
6.666667   0.444550  0.555450
8.888889   0.339282  0.660718
11.111111  0.258941  0.741059
13.333333  0.197624  0.802376
15.555556  0.150827  0.849173
17.777778  0.115112  0.884888
20.000000  0.087854  0.912146

Or if we know the exact times for which we want to know the mass fractions, we can pass a numpy array of those values:

>>> import numpy as np
>>> time_points = np.array([1.0, 4.5, 4.75, 5.0, 50.0])
>>> inv.decay_time_series_pandas(time_period=time_points, time_units='ky', decay_units='mass_frac')
               C-14      N-14
Time (ky)
1.00       0.885499  0.114501
4.50       0.578558  0.421442
4.75       0.561234  0.438766
5.00       0.544429  0.455571
50.00      0.002288  0.997712

Note that if you pass an array for the time_period as well as a value for npoints, the value specified by npoints will be silently ignored.

Once the data is stored in a pandas dataframe, we gain access to the pandas ecosystem and the functionality on offer. For example, if we want to track the progeny, of a uranium compound over time, but are only interested in those that are, or where, present above a certain number:

# Initialize the inventory
>>> inv = rd.Inventory({'U-238': 2000.0, 'U-235': 3000.0, 'U-234': 1500.0}, 'num')
# Get the decay data for the required amount of time
>>> df = inv.decay_time_series_pandas(time_period=1E9, time_units='y', decay_units='num', npoints=10)
# Printing the dataframe to see what is originally created
>>> df
                Ac-227        At-218        At-219        Bi-210        Bi-211        Bi-214        Bi-215        Fr-223        Hg-206  ...    Th-230        Th-231        Th-234        Tl-206        Tl-207        Tl-210        U-234        U-235        U-238
Time (y)                                                                                                                                ...
0.000000e+00  0.000000  0.000000e+00  0.000000e+00  0.000000e+00  0.000000e+00  0.000000e+00  0.000000e+00  0.000000e+00  0.000000e+00  ...  0.000000  0.000000e+00  0.000000e+00  0.000000e+00  0.000000e+00  0.000000e+00  1500.000000  3000.000000  2000.000000
1.111111e+08  0.000083  4.183008e-18  5.612824e-18  6.039192e-09  1.554277e-11  1.664837e-11  4.433329e-17  2.205038e-12  1.295477e-19  ...  0.033168  1.112054e-08  2.903132e-08  4.704880e-18  3.454878e-11  2.283922e-16     0.108020  2689.120153  1965.820782
2.222222e+08  0.000075  4.111522e-18  5.031186e-18  5.935985e-09  1.393213e-11  1.636385e-11  3.973918e-17  1.976537e-12  1.273338e-19  ...  0.032601  9.968160e-09  2.853519e-08  4.624475e-18  3.096861e-11  2.244890e-16     0.106174  2410.455732  1932.225673
3.333333e+08  0.000067  4.041257e-18  4.509821e-18  5.834541e-09  1.248839e-11  1.608420e-11  3.562115e-17  1.771716e-12  1.251577e-19  ...  0.032044  8.935193e-09  2.804754e-08  4.545445e-18  2.775944e-11  2.206526e-16     0.104360  2160.668362  1899.204692
4.444444e+08  0.000060  3.972194e-18  4.042484e-18  5.734831e-09  1.119426e-11  1.580933e-11  3.192985e-17  1.588119e-12  1.230188e-19  ...  0.031496  8.009269e-09  2.756821e-08  4.467765e-18  2.488282e-11  2.168817e-16     0.102577  1936.765612  1866.748026
5.555556e+08  0.000054  3.904311e-18  3.623575e-18  5.636825e-09  1.003423e-11  1.553915e-11  2.862107e-17  1.423547e-12  1.209165e-19  ...  0.030958  7.179296e-09  2.709708e-08  4.391413e-18  2.230430e-11  2.131753e-16     0.100824  1736.065147  1834.846033
6.666667e+08  0.000048  3.837588e-18  3.248076e-18  5.540494e-09  8.994421e-12  1.527360e-11  2.565516e-17  1.276030e-12  1.188501e-19  ...  0.030429  6.435330e-09  2.663401e-08  4.316365e-18  1.999298e-11  2.095322e-16     0.099100  1556.162591  1803.489231
7.777778e+08  0.000043  3.772005e-18  2.911489e-18  5.445809e-09  8.062360e-12  1.501258e-11  2.299660e-17  1.143799e-12  1.168190e-19  ...  0.029909  5.768458e-09  2.617884e-08  4.242600e-18  1.792117e-11  2.059514e-16     0.097407  1394.902728  1772.668305
8.888889e+08  0.000039  3.707543e-18  2.609781e-18  5.352742e-09  7.226885e-12  1.475602e-11  2.061354e-17  1.025271e-12  1.148226e-19  ...  0.029398  5.170692e-09  2.573145e-08  4.170096e-18  1.606406e-11  2.024318e-16     0.095742  1250.353679  1742.374097
1.000000e+09  0.000035  3.644182e-18  2.339338e-18  5.261266e-09  6.477987e-12  1.450384e-11  1.847743e-17  9.190258e-13  1.128603e-19  ...  0.028895  4.634871e-09  2.529171e-08  4.098830e-18  1.439940e-11  1.989723e-16     0.094106  1120.783759  1712.597605

[10 rows x 37 columns]
# Slice the result, keeping only those progeny that ever existed above a specific quantity
>>> df.loc[:, df.max() > 100]
                   Pb-206       Pb-207        U-234        U-235        U-238
Time (y)
0.000000e+00     0.000000     0.000000  1500.000000  3000.000000  2000.000000
1.111111e+08  1534.039370   310.754872     0.108020  2689.120153  1965.820782
2.222222e+08  1567.636948   589.432493     0.106174  2410.455732  1932.225673
3.333333e+08  1600.660357   839.231695     0.104360  2160.668362  1899.204692
4.444444e+08  1633.119409  1063.145051     0.102577  1936.765612  1866.748026
5.555556e+08  1665.023749  1263.855025     0.100824  1736.065147  1834.846033
6.666667e+08  1696.382856  1443.766102     0.099100  1556.162591  1803.489231
7.777778e+08  1727.206048  1605.033604     0.097407  1394.902728  1772.668305
8.888889e+08  1757.502483  1749.589500     0.095742  1250.353679  1742.374097
1.000000e+09  1787.281165  1879.165558     0.094106  1120.783759  1712.597605

For more information on the use of dataframes, see the pandas documentation.

To be consistent with the rest of the module, the method decay_time_series() is also provided and this returns a tuple of a list and and dictionary containing the time elements and decay data respectively.

>>> inv = rd.Inventory({"C-14": 1.0})
>>> times, data = inv.decay_time_series(time_period=20, time_units='ky', decay_units='mass_frac', npoints=5)
>>> times
[0.0, 5.0, 10.0, 15.0, 20.0]
>>> data
{'C-14': [1.0, 0.5444286529294111, 0.2964018201597633, 0.16136902316828455, 0.08785351725411072], 'N-14': [0.0, 0.45557134707058894, 0.7035981798402366, 0.8386309768317154, 0.9121464827458893]}

High numerical precision radioactive decay calculations

The InventoryHP class can be used for high numerical precision calculations. This class uses SymPy arbitrary-precision numerical calculation routines. The InventoryHP.decay() method can give more accurate decay calculation results for chains containing radionuclides with long and short half-lives, or when extremely long or short decay times are required. Note computation times are longer when using the InventoryHP class as compared to the Inventory class.

>>> high_precision_inv_t0 = rd.InventoryHP({'U-238': 99.274, 'U-235': 0.720, 'U-234': 0.005}, 'mol')
>>> high_precision_inv_t1 = high_precision_inv_t0.decay(1E9, 'y')
>>> high_precision_inv_t1.activities()
{'Ac-227': 5054315.0114205815, 'At-218': 50337.391440737316,
 'At-219': 4.184972829456501, 'Bi-210': 251686958.4550132,
 'Bi-211': 5054319.071431547, 'Bi-214': 251686906.86630014,
 'Bi-215': 4.059423644572889, 'Fr-223': 69749.54715760818,
 'Hg-206': 4.782052210630577, 'Pa-231': 5054314.855110147,
 'Pa-234': 402670.0669095932, 'Pa-234m': 251668791.81845266,
 'Pb-206': 0.0, 'Pb-207': 0.0,
 'Pb-210': 251686958.45424002, 'Pb-211': 5054319.071431518,
 'Pb-214': 251636619.8122487, 'Po-210': 251686958.4763548,
 'Po-211': 13949.92063715107, 'Po-214': 251634102.95324966,
 'Po-215': 5054319.071431025, 'Po-218': 251686957.2036866,
 'Ra-223': 5054315.01200738, 'Ra-226': 251686957.20309657,
 'Rn-218': 50.33739144073732, 'Rn-219': 5054315.012007381,
 'Rn-222': 251686957.20368624, 'Th-227': 4984565.464625096,
 'Th-230': 251686867.07347894, 'Th-231': 5054079.657163196,
 'Th-234': 251668791.81845254, 'Tl-206': 337.0088373712486,
 'Tl-207': 5040369.150794461, 'Tl-210': 52854.25044192306,
 'U-234': 251682620.84338942, 'U-235': 5054079.6571422955,
 'U-238': 251668791.8147359}

Calculating total number of decays

The cumulative_decays() method can be used to calculate the total number of decays that occur for each radionuclide over a decay period. With a normal precision Inventory:

>>> inv = rd.Inventory({'Sr-90': 10.0}, 'num')
>>> inv.cumulative_decays(1.0 'My')
{'Sr-90': 10.0, 'Y-90': 10.000000000000002}

So in this calculation, 10 atoms of strontium-90 and 10 atoms of its progeny, yttrium-90, decayed over the million year time period.

Using a high precision inventory fixes the floating-point rounding error:

>>> inv = rd.InventoryHP({'Sr-90': 10.0}, 'num')
>>> inv.cumulative_decays(1.0 'My')
{'Sr-90': 10.0, 'Y-90': 10.0}

Note the cumulative_decays() method does not report the total number of decays of stable nuclides (as these are all zero).

Nuclide name formatting and metastable states

Nuclides can be specified in four equivalent ways. These are all equivalent ways of creating an inventory of radon-222:

>>> inv = rd.Inventory({'Rn-222': 1.0})
>>> inv = rd.Inventory({'Rn222': 1.0})
>>> inv = rd.Inventory({'222Rn': 1.0})
>>> inv = rd.Inventory({862220000: 1.0})

For the last instance, the ‘canonical id’ of the nuclide was used. This number is in zzzaaammmm format, where the leftmost digits are the atomic number of radon, the next three digits are its atomic mass number, and the last four are for specifing its metastability. For nuclides with atomic mass numbers less than 100, zeroes must be included as placeholders (ex. aaa = 003 for H-3).

Metastable states of nuclides can be inputted by appending 'm', 'n', etc. to the nuclide string, or 0001, 0002, etc. to the id, for first, second… metastable states, respectively:

# using nuclide strings:
>>> inv = rd.Inventory({'Ir-192m': 1.0})
>>> inv = rd.Inventory({'Ir-192n': 1.0})

# or, equivalently, using canonical ids:
>>> inv = rd.Inventory({771920001: 1.0})
>>> inv = rd.Inventory({771920002: 1.0})

Equivalently we could have specified these metastable states using 'Ir192m' or '192mIr' for Ir-192m, or 'Ir192n' or '192nIr' for Ir-192n.

Note canonical ids are also used by PyNE.

Fetching atomic and decay data

The Nuclide class can be used to obtain atomic data for any specific nuclide, and decay data for radionuclides. They are built similarly to inventories:

>>> nuc = rd.Nuclide('Rn-222')
>>> nuc = rd.Nuclide('Rn222')
>>> nuc = rd.Nuclide('222Rn')
>>> nuc = rd.Nuclide(862220000)

The atomic data for a nuclide can be accessed through the Nuclide object’s Z, A and atomic_mass methods:

>>> nuc = rd.Nuclide('K-40')
>>> nuc.Z  # proton number
19
>>> nuc.A  # nucleon number
40
>>> nuc.atomic_mass  # atomic mass in g/mol
39.963998165

Additionally, the canonical id of a nuclide, in zzzaaammmm format, can be retrieved using the id method:

>>> nuc = rd.Nuclide('Co-58m')
>>> nuc.id
270580001

Decay data for radionuclides can also be accessed using Nuclide objects. For example, to get the half-life of iodine-123:

>>> nuc = rd.Nuclide('I123')
>>> nuc.half_life()
47772.0

The default time unit is seconds if no time unit argument is supplied to half_life().

If you do not know the natural time unit for expressing the radionuclide half-life, supply 'readable' as the time argument. A human-readable string with the half-life and time unit is returned:

>>> nuc.half_life('readable')
'13.27 h'

Use the progeny(), branching_fractions() and decay_modes() methods to obtain the progeny, branching fractions and decay modes of a radionuclide:

>>> nuc.progeny()
['Te-123', 'Te-123m']
>>> nuc.branching_fractions()
[0.99996, 4.442e-05]
>>> nuc.decay_modes()
['EC', 'EC']

These methods return data for the direct progeny of the radionuclide. 'EC' is an abbreviation for electron capture decay.

The decay_modes() method reports each decay mode of the parent radionuclide resulting in each progeny. The types of decay mode in the ICRP-107 dataset are α (alpha decay), β- (beta minus decay), β+ (positron emission), EC (electron capture), IT (isomeric transition) and SF (spontaneous fission). Note that the decay mode string is not a comprehensive list of all the radiation types released when the parent radionuclide decays. Other radiation types, such as gamma rays, x-rays, decay electrons and Auger electrons, may also be released due to various nuclear and atomic relaxation processes that follow α, β-, β+ etc. decays.

Decay data can be accessed for all nuclides in an Inventory by using the half_lives(), progeny(), branching_fractions() and decay_modes() methods:

>>> inv = rd.Inventory({'C-14': 1.0, 'K-40': 2.0})
>>> inv.half_lives('y')
{'C-14': 5700.0, 'K-40': 1251000000.0}
>>> inv.progeny()
{'C-14': ['N-14'], 'K-40': ['Ca-40', 'Ar-40']}
>>> inv.branching_fractions()
{'C-14': [1.0], 'K-40': [0.8914, 0.1086]}
>>> inv.decay_modes()
{'C-14': ['β-'], 'K-40': ['β-', 'β+ & EC']}

Decay data can also be accessed directly from the decay datasets. Query the data in ICRP-107, which is the default dataset in radioactivedecay, by:

>>> rd.DEFAULTDATA.dataset_name
'icrp107_ame2020_nubase2020'
>>> rd.DEFAULTDATA.half_life('Cs-137', 'y')
30.1671
>>> rd.DEFAULTDATA.branching_fraction('Cs-137', 'Ba-137m')
0.94399
>>> rd.DEFAULTDATA.decay_mode('Cs-137', 'Ba-137m')
'β-'

Adding and removing nuclides from inventories

It is easy to add nuclides to an Inventory using the add() method:

>>> inv = rd.Inventory({'H-3': 1.0, 'Be-10': 2.0})
>>> inv.activities()
{'Be-10': 2.0, 'H-3': 1.0}
>>> inv.add({'C-14': 3.0, 'K-40': 4.0})
>>> inv.activities()
{'Be-10': 2.0, 'C-14': 3.0, 'H-3': 1.0, 'K-40': 4.0}

Similarly, subtract nuclides from an Inventory using the subtract() method:

>>> inv.subtract({'Be-10': 1.0, 'K-40': 2.0})
>>> inv.activities()
{'Be-10': 1.0, 'C-14': 3.0, 'H-3': 1.0, 'K-40': 2.0}

Likewise use remove() to erase one or more nuclide from an Inventory:

>>> inv.remove('H-3')
>>> inv.activities()
{'Be-10': 1.0, 'C-14': 3.0, 'K-40': 2.0}
>>> inv.remove(['Be-10', 'K-40'])
>>> inv.activities()
{'C-14': 3.0}

The add() and subtract() methods also accept the 'unit' argument for inputs other than activities, and mixing input types is allowed:

>>> inv.add({'H-3': 1.3E9}, 'num')
>>> inv.activities()
{'C-14': 3.0, 'H-3': 2.3177330463306007}
>>> inv.subtract({'C-14': 7.1E-12}, 'g')
>>> inv.activities()
{'C-14': 1.8233790683016682, 'H-3': 2.3177330463306007}

You can also supply Nuclide objects instead of strings to the Inventory constructor, and the add() and remove() methods:

>>> H3 = rd.Nuclide('H-3')
>>> inv = rd.Inventory({H3: 1.0})
>>> inv.activities()
{'H-3': 1.0}
>>> Be10 = rd.Nuclide('Be-10')
>>> inv.add({Be10: 2.0})
>>> inv.activities()
{'Be-10': 2.0, 'H-3': 1.0}
>>> inv.remove(H3)
>>> inv.activities()
{'Be-10': 2.0}

Note if the decay dataset of the Nuclide instance is different to that of the Inventory instance, the former will be ignored and the existing decay dataset of the Inventory will be used instead.

Inventory arithmetic

You can add the contents of different inventories together to create a new inventory:

>>> inv1 = rd.Inventory({'H-3': 1.0}, 'g')
>>> inv2 = rd.Inventory({'C-14': 1.0}, 'g')
>>> inv = inv1 + inv2
>>> inv.masses()
{'C-14': 1.0, 'H-3': 1.0}

It is also possible to subtract the contents of one inventory from another:

>>> inv = inv - inv1
>>> inv.masses()
{'C-14': 1.0, 'H-3': 0.0}

Multiplication and division on inventories

You can multiply or divide the amounts of all nuclides in an inventory by a constant as follows:

>>> inv = rd.Inventory({'Sr-90': 1.0, 'Cs-137': 1.0}, 'num')
>>> inv = 2*inv
>>> inv.numbers()
{'Sr-90': 2.0, 'Cs-137': 2.0}
>>> inv = inv / 2
>>> inv.numbers()
{'Sr-90': 1.0, 'Cs-137': 1.0}

Writing results to a file

Similar to rd.read_csv(), inventory objects have a .to_csv() method for writing out the contents of an inventory to a CSV-type file. The user specifies the filename, the units to be used, whether the units should be written into the file (via the third column), the delimiter e.g. comma for a CSV file, tab ('\t') for a TSV file, and the header line for the file:

>>> inv = rd.Inventory({'Cs-137': 1.02, 'Sr-90': 3.05}, 'Bq')
>>> inv.to_csv('test_output.csv', units='mBq', delimiter='|', write_units=True, header=["nuclide", "quantity", "units"])

This produces a file named “test_output.csv” containing:

nuclide|amount|units
Cs-137|1020.0|mBq
Sr-90|3050.0|mBq