Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

added plot_decay_heat method #42

Merged
merged 13 commits into from
Jun 9, 2023
3 changes: 3 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -44,3 +44,6 @@ jobs:
# plot_isotope_charts_fisson_irradiation.py

python examples/pulse_schedule/plot_pulse_schedule.py

# simulation requires more nuclear data that available on the CI
# python examples/decay_heat_vs_time/decay_heat_vs_time.py
96 changes: 96 additions & 0 deletions examples/decay_heat_vs_time/decay_heat_vs_time.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@

# based on CoNDERC data
# https://www-nds.iaea.org/conderc/fusion/element/SS304
shimwell marked this conversation as resolved.
Show resolved Hide resolved
import openmc
import openmc_depletion_plotter
import openmc.deplete
import math
from pathlib import Path

# these should be set to where you have the chain and cross section file saved
openmc.config['cross_sections'] = Path(__file__).parents[2]/'tests'/'cross_sections.xml'
openmc.config['chain_file'] = Path(__file__).parents[2]/'tests'/'chain-nndc-b7.1.xml'

# makes a simple material
my_material = openmc.Material()
my_material.add_nuclide('Fe56', 71.17, percent_type='ao')
my_material.set_density('g/cm3', 7.9)

sphere_radius = 100
volume_of_sphere = (4/3) * math.pi * math.pow(sphere_radius, 3)
my_material.volume = volume_of_sphere # a volume is needed so openmc can find the number of atoms in the cell/material
my_material.depletable = True # depletable = True is needed to tell openmc to update the material with each time step

materials = openmc.Materials([my_material])
materials.export_to_xml()

# surfaces
sph1 = openmc.Sphere(r=sphere_radius, boundary_type='vacuum')

# cells, makes a simple sphere cell
shield_cell = openmc.Cell(region=-sph1)
shield_cell.fill = my_material

# sets the geometry to the universe that contains just the one cell
universe = openmc.Universe(cells=[shield_cell])
geometry = openmc.Geometry(universe)

# creates a 14MeV neutron point source
source = openmc.Source()
source.space = openmc.stats.Point((0, 0, 0))
source.angle = openmc.stats.Isotropic()
source.energy = openmc.stats.Discrete([14e6], [1])
source.particles = 'neutron'

# SETTINGS

# Instantiate a Settings object
settings = openmc.Settings()
settings.batches = 2
settings.inactive = 0
settings.particles = 10000
settings.source = source
settings.run_mode = 'fixed source'

model = openmc.model.Model(geometry, materials, settings)

operator = openmc.deplete.CoupledOperator(
model=model,
normalization_mode="source-rate", # set for fixed source simulation, otherwise defaults to fission simulation
dilute_initial=0, # set to zero to avoid adding small amounts of isotopes, defaults to adding small amounts of fissionable isotopes
reduce_chain=True, # reduced to only the isotopes present in depletable materials and their possible progeny
reduce_chain_level=5,
)

# We define timesteps together with the source rate to make it clearer
timesteps_and_source_rates = [
(5*60, 1.116E+10),
(5*60, 0),
(5*60, 0),
(5*60, 0),
]

# Uses list Python comprehension to get the timesteps and source_rates separately
timesteps = [item[0] for item in timesteps_and_source_rates]
source_rates = [item[1] for item in timesteps_and_source_rates]

integrator = openmc.deplete.PredictorIntegrator(
operator=operator,
timesteps=timesteps,
source_rates=source_rates,
timestep_units='s'
)

integrator.integrate()

results = openmc.deplete.ResultsList.from_hdf5("depletion_results.h5")

plot = results.plot_decay_heat_vs_time(
x_scale='log',
y_scale='log',
excluded_material=my_material,
show_top=10
)
plot.show()

plot.write_html('decay_heat.html')
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ write_to = "src/openmc_depletion_plotter/_version.py"

[project.optional-dependencies]
tests = [
"pytest"
"pytest",
]

[project.scripts]
Expand Down
1 change: 1 addition & 0 deletions src/openmc_depletion_plotter/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
from .utils import find_most_active_nuclides_in_material
from .utils import find_most_active_nuclides_in_materials
from .utils import get_nuclide_activities_from_materials
from .utils import get_decay_heat_from_materials
from .utils import get_atoms_from_material
from .utils import create_base_plot
from .utils import add_stables
Expand Down
28 changes: 26 additions & 2 deletions src/openmc_depletion_plotter/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ def main():

activity_or_atoms = st.sidebar.selectbox(
label="Plot",
options=("activity", "number of atoms"),
options=("activity", "number of atoms", "decay heat"),
index=0,
key="activity_or_atoms",
help="",
Expand Down Expand Up @@ -142,6 +142,14 @@ def main():
key="activity_units",
help="",
)
if activity_or_atoms == "decay heat":
decay_heat_units = st.sidebar.selectbox(
label="Decay heat units",
options=('W', 'W/g', 'W/cm3'),
index=0,
key="decay_heat_units",
help="",
)
# todo horizontal_lines

if number_of_depleted_materials == 1:
Expand Down Expand Up @@ -201,6 +209,23 @@ def main():
include_total=include_total,
path=materials_file.name,
)
elif activity_or_atoms == "decay heat":
print('x_scale line 213 ',x_scale)
plot = results.plot_decay_heat_vs_time(
excluded_material=material_to_exclude,
time_units=time_units,
units=decay_heat_units,
show_top=show_top,
x_scale=x_scale,
y_scale=y_scale,
include_total=include_total,
# x_axis_title=None,
plotting_backend=backend,
# units="W/g", # TODO add drop down option
# threshold=None,
title="Decay heat of nuclides in material",
material_index=material_index,
)
elif activity_or_atoms == "number of atoms":
plot = results.plot_atoms_vs_time(
# todo allow no materials to be excluded
Expand All @@ -222,7 +247,6 @@ def main():
'activity_or_atoms must be either "activity" or "number of atoms" to plot'
)


if backend == "matplotlib":
st.pyplot(plot)
else:
Expand Down
148 changes: 135 additions & 13 deletions src/openmc_depletion_plotter/results.py
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
import matplotlib.pyplot as plt
import numpy as np
import openmc
import openmc.deplete
from .utils import add_scale_buttons
from .utils import find_most_active_nuclides_in_materials
from .utils import get_nuclide_activities_from_materials
from .utils import get_nuclide_activities_from_materials
from .utils import find_total_activity_in_materials
from .utils import stable_nuclides
from .utils import create_base_plot
from .utils import get_nuclide_atoms_from_materials
from .utils import find_most_abundant_nuclides_in_materials
from .utils import find_total_nuclides_in_materials
import plotly.graph_objects as go
import numpy as np
import matplotlib.pyplot as plt

from .utils import (add_scale_buttons, create_base_plot,
find_most_abundant_nuclides_in_materials,
find_most_active_nuclides_in_materials,
find_total_activity_in_materials,
find_total_decay_heat_in_materials,
find_total_nuclides_in_materials,
get_decay_heat_from_materials,
get_nuclide_activities_from_materials,
get_nuclide_atoms_from_materials, stable_nuclides)

lots_of_nuclides = []
elements = list(openmc.data.ATOMIC_SYMBOL.values())
Expand Down Expand Up @@ -162,7 +162,7 @@ def plot_atoms_vs_time(
x_axis_title=None,
plotting_backend="plotly",
threshold=None,
title="Number of of nuclides in material",
title="Number of nuclides in material",
material_index=0, # zero index as one material in problem
path="materials.xml",
):
Expand All @@ -174,6 +174,7 @@ def plot_atoms_vs_time(
materials = self.export_to_materials(nuc_with_data=lots_of_nuclides,burnup_index=counter, path=path)[
material_index
]

all_materials.append(materials)

most_abundant = find_most_abundant_nuclides_in_materials(
Expand Down Expand Up @@ -213,6 +214,8 @@ def plot_atoms_vs_time(
fig = plt.figure()
plt.xlabel(x_axis_title)
plt.ylabel("Number of atoms")
plt.xscale(x_scale)
plt.yscale(y_scale)
plt.title(title)
else:
msg = 'only "plotly" and "matplotlib" plotting_backend are supported. {plotting_backend} is not an option'
Expand Down Expand Up @@ -265,5 +268,124 @@ def plot_atoms_vs_time(
return plt


def plot_decay_heat_vs_time(
self,
excluded_material=None,
time_units="d",
show_top=None,
x_scale="linear",
y_scale="linear",
include_total=False,
x_axis_title=None,
plotting_backend="plotly",
units="W/g",
threshold=None,
title="Decay heat of nuclides in material",
material_index=0, # zero index as one material in problem
path="materials.xml",
):

time_steps = self.get_times(time_units=time_units)

all_materials = []
for counter, step in enumerate(time_steps):
materials = self.export_to_materials(nuc_with_data=lots_of_nuclides,burnup_index=counter, path=path)[
material_index
]

all_materials.append(materials)

most_abundant = find_most_abundant_nuclides_in_materials(
materials=all_materials, exclude=excluded_material
)
if show_top is not None:
nuclides = most_abundant[:show_top]
else:
nuclides = most_abundant

all_nuclides_with_decay_heat = get_decay_heat_from_materials(
nuclides=nuclides, materials=all_materials, units=units
)

if x_axis_title is None:
x_axis_title = f"Time [{time_units}]"
if plotting_backend == "plotly":

figure = create_base_plot(
x_title=x_axis_title,
y_title=f"Decay heat [{units}]",
title=title,
x_scale=x_scale,
y_scale=y_scale,
)

if threshold:
total = (
find_total_nuclides_in_materials(
all_materials, exclude=excluded_material
),
)
figure.update_layout(yaxis_range=[threshold, max(total)])
add_scale_buttons(figure, x_scale, y_scale)
elif plotting_backend == "matplotlib":
plt.cla
fig = plt.figure()
plt.xlabel(x_axis_title)
plt.ylabel(f"Decay heat [{units}]")
plt.xscale(x_scale)
plt.yscale(y_scale)
plt.title(title)
else:
msg = 'only "plotly" and "matplotlib" plotting_backend are supported. {plotting_backend} is not an option'
raise ValueError(msg)

for key, value in all_nuclides_with_decay_heat.items():
if sum(value) != 0:
if threshold:
value = np.array(value)
value[value < threshold] = np.nan
if key in stable_nuclides:
name = key + " stable"
else:
name = key
if plotting_backend == "plotly":
figure.add_trace(
go.Scatter(
mode="lines",
x=time_steps,
y=value,
name=name,
# line=dict(shape="hv", width=0),
)
)
else:
plt.plot(time_steps, value, label=key)

if include_total:
total = find_total_decay_heat_in_materials(
materials=all_materials, units=units, exclude=excluded_material
)
if plotting_backend == "plotly":
figure.add_trace(
go.Scatter(
mode="lines",
x=time_steps,
y=total,
name="total",
line=dict(dash="longdashdot", color="black"),
)
)
else:
plt.plot(time_steps, total, 'k--', label='total')

if plotting_backend == "plotly":
return figure
else:
plt.legend(bbox_to_anchor=(1.25, 1.0))
plt.tight_layout()
return plt


openmc.deplete.Results.plot_activity_vs_time = plot_activity_vs_time
openmc.deplete.Results.plot_atoms_vs_time = plot_atoms_vs_time
openmc.deplete.Results.plot_decay_heat_vs_time = plot_decay_heat_vs_time
Loading