from .._scarabee import (
NDLibrary,
DepletionChain,
MaterialComposition,
Material,
CrossSection,
MOCDriver,
DepletionChain,
DepletionMatrix,
build_depletion_matrix,
)
import numpy as np
from typing import Optional, List, Tuple
import copy
[docs]class BurnablePoisonRod:
"""
Represents a burnable poison rod in a PWR which is inserted into an empty
guide tube. This can be a wet annular burnable absorber (WABA) or a
borosilicate glass (BSG) burnable absorber.
Parameters
----------
center : optional Material
Material at the center of the poison pin. If None, the center will be
filled with moderator.
clad : Material
Material which describes the inner and outer cladding.
gap : Material
Material which describes the gap between the cladding and poison.
poison : Material
Material which describes the poison.
center_radius : float
Outer radius of the center of the poison rod.
inner_clad_radius : float
Outer radius of the inner cladding at the center of the rod.
inner_gap_radius : float
Outer radius of the inner gap between the cladding and poison.
poison_radius : float
Outer radius of the poison.
outer_gap_radius : float
Outer radius of the outer gap between the cladding and poison.
outer_clad_radius : float
Outer radius of the outer cladding of the poison rod.
Attributes
----------
center : optional Material
Material at the center of the poison pin. If None, the center will be
filled with moderator.
clad : Material
Material which describes the inner and outer cladding.
gap : Material
Material which describes the gap between the cladding and poison.
poison_materials : list of Material
Contains the poison material for each depletion time step.
center_radius : float
Outer radius of the center of the poison rod.
inner_clad_radius : float
Outer radius of the inner cladding at the center of the rod.
inner_gap_radius : float
Outer radius of the inner gap between the cladding and poison.
poison_radius : float
Outer radius of the poison.
outer_gap_radius : float
Outer radius of the outer gap between the cladding and poison.
outer_clad_radius : float
Outer radius of the outer cladding of the poison rod.
"""
[docs] def __init__(
self,
center: Optional[Material],
clad: Material,
gap: Material,
poison: Material,
center_radius: float,
inner_clad_radius: float,
inner_gap_radius: float,
poison_radius: float,
outer_gap_radius: float,
outer_clad_radius: float,
):
# Make sure all radii are sorted
tmp_radii_list = [
center_radius,
inner_clad_radius,
inner_gap_radius,
poison_radius,
outer_gap_radius,
outer_clad_radius,
]
if not sorted(tmp_radii_list):
raise ValueError("Burnable poison rod radii are not sorted.")
# Set materials
self._center = copy.deepcopy(center)
self._clad = copy.deepcopy(clad)
self._gap = copy.deepcopy(gap)
self._poison_materials = [copy.deepcopy(poison)]
# Set radii
self._center_radius = center_radius
self._inner_clad_radius = inner_clad_radius
self._inner_gap_radius = inner_gap_radius
self._poison_radius = poison_radius
self._outer_gap_radius = outer_gap_radius
self._outer_clad_radius = outer_clad_radius
# ======================================================================
# DANCOFF CORRECTION CALCULATION DATA
# ----------------------------------------------------------------------
self._center_dancoff_xs: Optional[CrossSection] = None
if self.center is not None:
self._center_dancoff_xs = CrossSection(
np.array([self.center.potential_xs]),
np.array([self.center.potential_xs]),
np.array([[0.0]]),
"BPR Clad",
)
self._clad_dancoff_xs: CrossSection = CrossSection(
np.array([self.clad.potential_xs]),
np.array([self.clad.potential_xs]),
np.array([[0.0]]),
"BPR Clad",
)
self._gap_dancoff_xs: CrossSection = CrossSection(
np.array([self.gap.potential_xs]),
np.array([self.gap.potential_xs]),
np.array([[0.0]]),
"BPR Gap",
)
# Make initial poison Dancoff xs with initial material
self._poison_dancoff_xs: CrossSection = CrossSection(
np.array([poison.potential_xs]),
np.array([poison.potential_xs]),
np.array([[0.0]]),
"BPR Poison",
)
# Center IDs and indices are only populated if the center material is
# not None, which indicates that the center should be moderator. If the
# center is moderator, then those IDs and indices are kept by the
# parent GuideTube object.
self._center_isolated_dancoff_fsr_ids = []
self._center_isolated_dancoff_fsr_inds = []
self._clad_isolated_dancoff_fsr_ids = []
self._clad_isolated_dancoff_fsr_inds = []
self._gap_isolated_dancoff_fsr_ids = []
self._gap_isolated_dancoff_fsr_inds = []
self._poison_isolated_dancoff_fsr_ids = []
self._poison_isolated_dancoff_fsr_inds = []
self._center_full_dancoff_fsr_ids = []
self._center_full_dancoff_fsr_inds = []
self._clad_full_dancoff_fsr_ids = []
self._clad_full_dancoff_fsr_inds = []
self._gap_full_dancoff_fsr_ids = []
self._gap_full_dancoff_fsr_inds = []
self._poison_full_dancoff_fsr_ids = []
self._poison_full_dancoff_fsr_inds = []
# ======================================================================
# TRANSPORT CALCULATION DATA
# ----------------------------------------------------------------------
self._center_xs: Optional[CrossSection] = None
self._clad_xs: Optional[CrossSection] = None
self._gap_xs: Optional[CrossSection] = None
self._poison_xs: Optional[CrossSection] = None
# FSR IDs and indices. Same rule for center as before.
self._center_fsr_ids: List[int] = []
self._center_fsr_inds: List[int] = []
self._clad_fsr_ids: List[int] = []
self._clad_fsr_inds: List[int] = []
self._gap_fsr_ids: List[int] = []
self._gap_fsr_inds: List[int] = []
self._poison_fsr_ids: List[int] = []
self._poison_fsr_inds: List[int] = []
# Flux spectrum of poison needed for depletion
self._poison_flux_spectrum: np.ndarray = np.array([])
# Initialize depletion matrices for the previous and current steps.
self._poison_prev_dep_mat: Optional[DepletionMatrix] = None
self._poison_current_dep_mat: Optional[DepletionMatrix] = None
@property
def center(self) -> Optional[Material]:
return self._center
@property
def clad(self) -> Material:
return self._clad
@property
def gap(self) -> Material:
return self._gap
@property
def poison_materials(self) -> List[Material]:
return self._poison_materials
@property
def center_radius(self) -> float:
return self._center_radius
@property
def inner_clad_radius(self) -> float:
return self._inner_clad_radius
@property
def inner_gap_radius(self) -> float:
return self._inner_gap_radius
@property
def poison_radius(self) -> float:
return self._poison_radius
@property
def outer_gap_radius(self) -> float:
return self._outer_gap_radius
@property
def outer_clad_radius(self) -> float:
return self._outer_clad_radius
[docs] def load_nuclides(self, ndl: NDLibrary) -> None:
"""
Loads all the nuclides for all current materials into the data library.
Parameters
----------
ndl : NDLibrary
Nuclear data library which should load the nuclides.
"""
if self.center is not None:
self.center.load_nuclides(ndl)
self.clad.load_nuclides(ndl)
self.gap.load_nuclides(ndl)
self.poison_materials[-1].load_nuclides(ndl)
# ==========================================================================
# Dancoff Correction Related Methods
def _make_dancoff_moc_cell(
self, moderator_xs: CrossSection
) -> Tuple[List[float], List[CrossSection]]:
"""
Returns the list of radii and list of cross sections for the poison pin.
"""
radii: List[float] = [
self.center_radius,
self.inner_clad_radius,
self.inner_gap_radius,
self.poison_radius,
self.outer_gap_radius,
self.outer_clad_radius,
]
xs: List[CrossSection] = [
moderator_xs,
self._clad_dancoff_xs,
self._gap_dancoff_xs,
self._poison_dancoff_xs,
self._gap_dancoff_xs,
self._clad_dancoff_xs,
]
if self._center_dancoff_xs is not None:
xs[0] = self._center_dancoff_xs
return radii, xs
[docs] def populate_dancoff_fsr_indexes(
self, isomoc: MOCDriver, fullmoc: MOCDriver
) -> None:
"""
Obtains the flat source region indexes for all of the flat source
regions used in the Dancoff correction calculations.
Parameters
----------
isomoc : MOCDriver
MOC simulation for the isolated pin.
fullmoc : MOCDriver
MOC simulation for the full geometry.
"""
self._center_isolated_dancoff_fsr_inds = []
self._clad_isolated_dancoff_fsr_inds = []
self._gap_isolated_dancoff_fsr_inds = []
self._poison_isolated_dancoff_fsr_inds = []
self._center_full_dancoff_fsr_inds = []
self._clad_full_dancoff_fsr_inds = []
self._gap_full_dancoff_fsr_inds = []
self._poison_full_dancoff_fsr_inds = []
for id in self._center_isolated_dancoff_fsr_ids:
self._center_isolated_dancoff_fsr_inds.append(isomoc.get_fsr_indx(id, 0))
for id in self._clad_isolated_dancoff_fsr_ids:
self._clad_isolated_dancoff_fsr_inds.append(isomoc.get_fsr_indx(id, 0))
for id in self._gap_isolated_dancoff_fsr_ids:
self._gap_isolated_dancoff_fsr_inds.append(isomoc.get_fsr_indx(id, 0))
for id in self._poison_isolated_dancoff_fsr_ids:
self._poison_isolated_dancoff_fsr_inds.append(isomoc.get_fsr_indx(id, 0))
for id in self._center_full_dancoff_fsr_ids:
self._center_full_dancoff_fsr_inds.append(fullmoc.get_fsr_indx(id, 0))
for id in self._clad_full_dancoff_fsr_ids:
self._clad_full_dancoff_fsr_inds.append(fullmoc.get_fsr_indx(id, 0))
for id in self._gap_full_dancoff_fsr_ids:
self._gap_full_dancoff_fsr_inds.append(fullmoc.get_fsr_indx(id, 0))
for id in self._poison_full_dancoff_fsr_ids:
self._poison_full_dancoff_fsr_inds.append(fullmoc.get_fsr_indx(id, 0))
[docs] def set_xs_for_dancoff_calculation(self) -> None:
"""
Sets the cross sections for the Dancoff calculations based on the most
recent poison composition.
"""
if self._center_dancoff_xs is not None and self.center is not None:
self._center_dancoff_xs.set(
CrossSection(
np.array([self.center.potential_xs]),
np.array([self.center.potential_xs]),
np.array([[0.0]]),
"BPR Clad",
)
)
self._clad_dancoff_xs.set(
CrossSection(
np.array([self.clad.potential_xs]),
np.array([self.clad.potential_xs]),
np.array([[0.0]]),
"BPR Clad",
)
)
self._gap_dancoff_xs.set(
CrossSection(
np.array([self.gap.potential_xs]),
np.array([self.gap.potential_xs]),
np.array([[0.0]]),
"BPR Gap",
)
)
# Make initial poison Dancoff xs with initial material
self._poison_dancoff_xs.set(
CrossSection(
np.array([self.poison_materials[-1].potential_xs]),
np.array([self.poison_materials[-1].potential_xs]),
np.array([[0.0]]),
"BPR Poison",
)
)
[docs] def set_isolated_dancoff_fuel_sources(
self, isomoc: MOCDriver, moderator: Material
) -> None:
"""
Initializes the fixed sources for the isolated MOC calculation required
in computing Dancoff corrections. Sources are set for a fuel Dancoff
correction calculation.
Parameters
----------
isomoc : MOCDriver
MOC simulation for the isolated geometry.
moderator : Material
Material definition for the moderator, used to obtain the potential
scattering cross section.
"""
# We only set sources for the center if it isn't moderator ! Otherwise,
# the GuideTube class is responsible for taking care of this. This case
# is indicated by the center material being None. For this case, the
# list of FSR indices should be empty.
if self.center is not None:
pot_xs = self.center.potential_xs
for ind in self._center_isolated_dancoff_fsr_inds:
isomoc.set_extern_src(ind, 0, pot_xs)
pot_xs = self.clad.potential_xs
for ind in self._clad_isolated_dancoff_fsr_inds:
isomoc.set_extern_src(ind, 0, pot_xs)
pot_xs = self.gap.potential_xs
for ind in self._gap_isolated_dancoff_fsr_inds:
isomoc.set_extern_src(ind, 0, pot_xs)
pot_xs = self.poison_materials[-1].potential_xs
for ind in self._poison_isolated_dancoff_fsr_inds:
isomoc.set_extern_src(ind, 0, pot_xs)
[docs] def set_isolated_dancoff_clad_sources(
self, isomoc: MOCDriver, moderator: Material, ndl: NDLibrary
) -> None:
"""
Initializes the fixed sources for the isolated MOC calculation required
in computing Dancoff corrections. Sources are set for a clad Dancoff
correction calculation.
The cladding of a burnable poison pin is no self-shielded. Therefore,
this method is an alias to set_isolated_dancoff_fuel_sources.
Parameters
----------
isomoc : MOCDriver
MOC simulation for the isolated geometry.
moderator : Material
Material definition for the moderator, used to obtain the potential
scattering cross section.
ndl : NDLibrary
Nuclear data library for obtaining potential scattering cross
sections.
"""
self.set_isolated_dancoff_fuel_sources(isomoc, moderator)
[docs] def set_full_dancoff_fuel_sources(
self, fullmoc: MOCDriver, moderator: Material
) -> None:
"""
Initializes the fixed sources for the full MOC calculation required
in computing Dancoff corrections. Sources are set for a fuel Dancoff
correction calculation.
Parameters
----------
fullmoc : MOCDriver
MOC simulation for the full geometry.
moderator : Material
Material definition for the moderator, used to obtain the potential
scattering cross section.
"""
# We only set sources for the center if it isn't moderator ! Otherwise,
# the GuideTube class is responsible for taking care of this. This case
# is indicated by the center material being None. For this case, the
# list of FSR indices should be empty.
if self.center is not None:
pot_xs = self.center.potential_xs
for ind in self._center_full_dancoff_fsr_inds:
fullmoc.set_extern_src(ind, 0, pot_xs)
pot_xs = self.clad.potential_xs
for ind in self._clad_full_dancoff_fsr_inds:
fullmoc.set_extern_src(ind, 0, pot_xs)
pot_xs = self.gap.potential_xs
for ind in self._gap_full_dancoff_fsr_inds:
fullmoc.set_extern_src(ind, 0, pot_xs)
pot_xs = self.poison_materials[-1].potential_xs
for ind in self._poison_full_dancoff_fsr_inds:
fullmoc.set_extern_src(ind, 0, pot_xs)
[docs] def set_full_dancoff_clad_sources(
self, fullmoc: MOCDriver, moderator: Material, ndl: NDLibrary
) -> None:
"""
Initializes the fixed sources for the full MOC calculation required
in computing Dancoff corrections. Sources are set for a clad Dancoff
correction calculation.
The cladding of a burnable poison pin is no self-shielded. Therefore,
this method is an alias to set_isolated_dancoff_fuel_sources.
Parameters
----------
isomoc : MOCDriver
MOC simulation for the isolated geometry.
moderator : Material
Material definition for the moderator, used to obtain the potential
scattering cross section.
ndl : NDLibrary
Nuclear data library for obtaining potential scattering cross
sections.
"""
self.set_full_dancoff_fuel_sources(fullmoc, moderator)
# ==========================================================================
# Transport Calculation Related Methods
[docs] def set_center_xs(self, ndl: NDLibrary) -> None:
"""
Constructs the CrossSection object for the material at the center of
the poison rod, so long as that material is not moderator.
Parameters
----------
ndl : NDLibrary
Nuclear data library to use for cross sections.
"""
if self.center is None:
return
if self._center_xs is None:
self._center_xs = self.center.dilution_xs([1.0e10] * self.center.size, ndl)
else:
self._center_xs.set(
self.center.dilution_xs([1.0e10] * self.center.size, ndl)
)
if self._center_xs.name == "":
self._center_xs.name = "BPR Center"
[docs] def set_gap_xs(self, ndl: NDLibrary) -> None:
"""
Constructs the CrossSection object for the gap between the poison and
the cladding.
Parameters
----------
ndl : NDLibrary
Nuclear data library to use for cross sections.
"""
if self._gap_xs is None:
self._gap_xs = self.gap.dilution_xs([1.0e10] * self.gap.size, ndl)
else:
self._gap_xs.set(self.gap.dilution_xs([1.0e10] * self.gap.size, ndl))
if self._gap_xs.name == "":
self._gap_xs.name = "BPR Gap"
[docs] def set_clad_xs(self, ndl: NDLibrary) -> None:
"""
Constructs the CrossSection object for the cladding of the poison rod.
The cladding of a poison rod uses infinitely dilute cross sections.
Parameters
----------
ndl : NDLibrary
Nuclear data library to use for cross sections.
"""
if self._clad_xs is None:
self._clad_xs = self.clad.dilution_xs([1.0e10] * self.clad.size, ndl)
else:
self._clad_xs.set(self.clad.dilution_xs([1.0e10] * self.clad.size, ndl))
if self._clad_xs.name == "":
self._clad_xs.name = "BPR Clad"
[docs] def set_poison_xs_for_depletion_step(self, t: int, ndl: NDLibrary) -> None:
"""
Constructs the CrossSection object for the poison at the specified
depletion step.
Parameters
----------
t : int
Index for the depletion step.
ndl : NDLibrary
Nuclear data library to use for cross sections.
"""
if self._poison_xs is None:
self._poison_xs = self.poison_materials[t].dilution_xs(
[1.0e10] * self.poison_materials[t].size, ndl
)
else:
self._poison_xs.set(
self.poison_materials[t].dilution_xs(
[1.0e10] * self.poison_materials[t].size, ndl
)
)
if self._poison_xs.name == "":
self._poison_xs.name = "BPR Poison"
def _make_moc_cell(
self, moderator_xs: CrossSection
) -> Tuple[List[float], List[CrossSection]]:
"""
Returns the list of radii and list of cross sections for the poison pin.
"""
"""
Returns the list of radii and list of cross sections for the poison pin.
"""
radii: List[float] = [
self.center_radius,
self.inner_clad_radius,
self.inner_gap_radius,
self.poison_radius,
self.outer_gap_radius,
self.outer_clad_radius,
]
xs: List[CrossSection] = [
moderator_xs,
self._clad_xs,
self._gap_xs,
self._poison_xs,
self._gap_xs,
self._clad_xs,
]
if self._center_xs is not None:
xs[0] = self._center_xs
return radii, xs
[docs] def populate_fsr_indexes(self, moc: MOCDriver) -> None:
"""
Obtains the flat source region indexes for all of the flat source
regions used in the full MOC calculations.
Parameters
----------
moc : MOCDriver
MOC simulation for the full calculations.
"""
self._center_fsr_inds = []
self._clad_fsr_inds = []
self._gap_fsr_inds = []
self._poison_fsr_inds = []
for id in self._center_fsr_ids:
self._center_fsr_inds.append(moc.get_fsr_indx(id, 0))
for id in self._clad_fsr_ids:
self._clad_fsr_inds.append(moc.get_fsr_indx(id, 0))
for id in self._gap_fsr_ids:
self._gap_fsr_inds.append(moc.get_fsr_indx(id, 0))
for id in self._poison_fsr_ids:
self._poison_fsr_inds.append(moc.get_fsr_indx(id, 0))
[docs] def obtain_flux_spectra(self, moc: MOCDriver) -> None:
"""
Computes average flux spectrum in the poison from the MOC simulation.
Parameters
----------
moc : MOCDriver
MOC simulation for the full calculations.
"""
self._poison_flux_spectrum = moc.homogenize_flux_spectrum(self._poison_fsr_inds)
[docs] def normalize_flux_spectrum(self, f) -> None:
"""
Applies a multiplicative factor to the flux spectra for the poison.
This permits normalizing the flux to a known assembly power.
Parameters
----------
f : float
Normalization factor.
"""
if f <= 0.0:
raise ValueError("Normalization factor must be > 0.")
self._poison_flux_spectrum *= f
[docs] def predict_depletion(
self,
chain: DepletionChain,
ndl: NDLibrary,
dt: float,
dtm1: Optional[float] = None,
) -> None:
"""
Performs the predictor in the integration of the Bateman equation.
If the argument for the previous time step is not provided, CE/LI will
be used. Otherwise, CE/LI is used on the first depletion step, and
LE/QI is used for all subsequent time steps. The predicted material
compositions are appended to the materials lists.
Parameters
----------
chain : DepletionChain
Depletion chain to use for radioactive decay and transmutation.
ndl : NDLibrary
Nuclear data library.
dt : float
Durration of the time step in seconds.
dtm1 : float, optional
Durration of the previous time step in seconds. Default is None.
"""
if dt <= 0:
raise ValueError("Predictor time step must be > 0.")
# Get the flux and initial material
flux = self._poison_flux_spectrum
mat = self._poison_materials[-1] # Use last available mat !
# Build depletion matrix for beginning of time step
A0 = build_depletion_matrix(chain, mat, flux, ndl)
# Save current matrix
self._poison_current_dep_mat = A0
# At this point, we can clear the xs data from the last material as
# depletion matrix is now built.
mat.clear_all_micro_xs_data()
# Initialize an array with the initial target number densities
N = np.zeros(A0.size)
nuclides = A0.nuclides
for i, nuclide in enumerate(nuclides):
N[i] = mat.atom_density(nuclide)
if self._poison_prev_dep_mat is None or dtm1 is None:
# Use CE/LI
A0 *= dt
# Do the matrix exponential
A0.exponential_product(N)
# Undo multiplication by time step on the matrix
A0 /= dt
else:
# Use LE/QI
Am1 = self._poison_prev_dep_mat
F1 = (-dt / (12.0 * dtm1)) * Am1 + ((6.0 * dtm1 + dt) / (12.0 * dtm1)) * A0
F1 *= dt
F2 = (-5.0 * dt / (12.0 * dtm1)) * Am1 + (
(6.0 * dtm1 + 5.0 * dt) / (12.0 * dtm1)
) * A0
F2 *= dt
# Do the matrix exponentials
F1.exponential_product(N)
F2.exponential_product(N)
# Now we can build a new material composition
new_mat_comp = MaterialComposition()
for i, nuclide in enumerate(nuclides):
if N[i] > 0.0:
new_mat_comp.add_nuclide(nuclide, N[i])
# Make the new material
new_mat = Material(new_mat_comp, mat.temperature, ndl)
self._poison_materials.append(new_mat)
[docs] def correct_depletion(
self,
chain: DepletionChain,
ndl: NDLibrary,
dt: float,
dtm1: Optional[float] = None,
) -> None:
"""
Performs the corrector in the integration of the Bateman equation.
If the argument for the previous time step is not provided, CE/LI will
be used. Otherwise, CE/LI is used on the first depletion step, and
LE/QI is used for all subsequent time steps. The corrected material
compositions replace the ones where were appended in the corrector step.
Parameters
----------
chain : DepletionChain
Depletion chain to use for radioactive decay and transmutation.
ndl : NDLibrary
Nuclear data library.
dt : float
Durration of the time step in seconds.
dtm1 : float, optional
Durration of the previous time step in seconds. Default is None.
"""
if dt <= 0:
raise ValueError("Corrector time step must be > 0.")
# Get the flux and initial material
flux = self._poison_flux_spectrum
mat_pred = self._poison_materials[-1] # Use last available mat !
# Get depletion matrix for beginning of time step
A0 = self._poison_current_dep_mat
# Build depletion matrix and multiply by time step
Ap1 = build_depletion_matrix(chain, mat_pred, flux, ndl)
# Initialize an array with the initial target number densities
mat_old = self._poison_materials[-2] # Go 2 steps back !!
N = np.zeros(Ap1.size)
nuclides = Ap1.nuclides
for i, nuclide in enumerate(nuclides):
N[i] = mat_old.atom_density(nuclide)
if self._poison_prev_dep_mat is None or dtm1 is None:
# Use CE/LI
F1 = (5.0 * dt / 12.0) * A0 + (dt / 12.0) * Ap1
F2 = (dt / 12.0) * A0 + (5.0 * dt / 12.0) * Ap1
F1.exponential_product(N)
F2.exponential_product(N)
else:
# Use LE/QI
# Get previous depletion matrix
Am1 = self._poison_prev_dep_mat
F3 = (
(-dt * dt / (12.0 * dtm1 * (dtm1 + dt))) * Am1
+ (
(5.0 * dtm1 * dtm1 + 6.0 * dtm1 * dt + dt * dt)
/ (12.0 * dtm1 * (dtm1 + dt))
)
* A0
+ (dtm1 / (12.0 * (dtm1 + dt))) * Ap1
)
F3 *= dt
F4 = (
(-dt * dt / (12.0 * dtm1 * (dtm1 + dt))) * Am1
+ (
(dtm1 * dtm1 + 2.0 * dtm1 * dt + dt * dt)
/ (12.0 * dtm1 * (dtm1 + dt))
)
* A0
+ ((5.0 * dtm1 + 4.0 * dt) / (12.0 * (dtm1 + dt))) * Ap1
)
F4 *= dt
F3.exponential_product(N)
F4.exponential_product(N)
# Now we can build a new material composition
new_mat_comp = MaterialComposition()
for i, nuclide in enumerate(nuclides):
if N[i] > 0.0:
new_mat_comp.add_nuclide(nuclide, N[i])
# Make the new material
new_mat = Material(new_mat_comp, mat_pred.temperature, ndl)
self._poison_materials[-1] = new_mat
# Save the current matrix as previous matrix for next step !
self._poison_prev_dep_mat = A0
self._poison_current_dep_mat = None