Source code for festim.concentration.traps.trap
from festim import Concentration, k_B, Material, Theta, RadioactiveDecay
from fenics import *
import sympy as sp
[docs]
class Trap(Concentration):
"""
Args:
k_0 (float, list): trapping pre-exponential factor (m3 s-1)
E_k (float, list): trapping activation energy (eV)
p_0 (float, list): detrapping pre-exponential factor (s-1)
E_p (float, list): detrapping activation energy (eV)
materials (list, str, festim.Material): the materials the
trap is living in. The material's name.
density (sp.Add, float, list, fenics.Expresion, fenics.UserExpression):
the trap density (m-3)
id (int, optional): The trap id. Defaults to None.
Raises:
ValueError: if duplicates are found in materials
.. note::
Should multiple traps in muliple materials be used, to save on
dof's, traps can be conglomerated and described in lists in the
format::
festim.Trap(
k_0=[1, 2],
E_k=[1, 2],
p_0=[1, 2],
E_p=[1, 2],
materials=[1, 2]
density=[1, 2])
This will act as a singular trap but with seperate properties for
respective materials. Parameters k_0, E_k, p_0, E_p, materials and
density MUST have the same length for this method to be valid.
"""
def __init__(self, k_0, E_k, p_0, E_p, materials, density, id=None):
super().__init__()
self.id = id
self.k_0 = k_0
self.E_k = E_k
self.p_0 = p_0
self.E_p = E_p
self.materials = materials
self.density = []
self.make_density(density)
self.sources = []
@property
def materials(self):
return self._materials
@materials.setter
def materials(self, value):
if not isinstance(value, list):
value = [value]
for entry in value:
if not isinstance(entry, (str, Material)):
raise TypeError(
"Accepted types for materials are str or festim.Material"
)
self._materials = value
[docs]
def make_materials(self, materials):
"""Ensure all entries in self.materials are of type festim.Material
Args:
materials (festim.Materials): the materials
Raises:
ValueError: if some duplicates are found
"""
new_materials = []
for material in self.materials:
new_materials.append(materials.find_material(material))
self.materials = new_materials
if len(self.materials) != len(list(set(self.materials))):
raise ValueError("Duplicate materials in trap")
def make_density(self, densities):
if type(densities) is not list:
densities = [densities]
for i, density in enumerate(densities):
if density is not None:
# if density is already a fenics Expression, use it as is
if isinstance(density, (Expression, UserExpression)):
self.density.append(density)
# else assume it's a sympy expression
else:
density_expr = sp.printing.ccode(density)
self.density.append(
Expression(
density_expr,
degree=2,
t=0,
name="density_{}_{}".format(self.id, i),
)
)
[docs]
def create_form(self, mobile, materials, T, dx, dt=None):
"""Creates the general form associated with the trap
d ct/ dt = k c_m (n - c_t) - p c_t + S
Args:
mobile (festim.Mobile): the mobile concentration of the simulation
materials (festim.Materials): the materials of the simulation
T (festim.Temperature): the temperature of the simulation
dx (fenics.Measure): the dx measure of the sim
dt (festim.Stepsize, optional): If None assuming steady state.
Defaults to None.
"""
self.F = 0
self.create_trapping_form(mobile, materials, T, dx, dt)
if self.sources is not None:
self.create_source_form(dx)
[docs]
def create_trapping_form(self, mobile, materials, T, dx, dt=None):
"""d ct/ dt = k c_m (n - c_t) - p c_t
Args:
mobile (festim.Mobile): the mobile concentration of the simulation
materials (festim.Materials): the materials of the simulation
T (festim.Temperature): the temperature of the simulation
dx (fenics.Measure): the dx measure of the sim
dt (festim.Stepsize, optional): If None assuming steady state.
Defaults to None.
"""
solution = self.solution
prev_solution = self.previous_solution
test_function = self.test_function
if not all(isinstance(mat, Material) for mat in self.materials):
self.make_materials(materials)
expressions_trap = []
F_trapping = 0 # initialise the form
if dt is not None:
# d(c_t)/dt in trapping equation
F_trapping += ((solution - prev_solution) / dt.value) * test_function * dx
else:
# if the sim is steady state and
# if a trap is not defined in one subdomain
# add c_t = 0 to the form in this subdomain
for mat in materials:
if mat not in self.materials:
F_trapping += solution * test_function * dx(mat.id)
for i, mat in enumerate(self.materials):
if type(self.k_0) is list:
k_0 = self.k_0[i]
E_k = self.E_k[i]
p_0 = self.p_0[i]
E_p = self.E_p[i]
density = self.density[i]
else:
k_0 = self.k_0
E_k = self.E_k
p_0 = self.p_0
E_p = self.E_p
density = self.density[0]
# add the density to the list of
# expressions to be updated
expressions_trap.append(density)
if isinstance(mobile, Theta) and mat.solubility_law == "henry":
raise NotImplementedError(
"Henry law of solubility is not implemented with traps"
)
c_0, c_0_n = mobile.get_concentration_for_a_given_material(mat, T)
# k(T)*c_m*(n - c_t) - p(T)*c_t
F_trapping += (
-k_0
* exp(-E_k / k_B / T.T)
* c_0
* (density - solution)
* test_function
* dx(mat.id)
)
F_trapping += (
p_0 * exp(-E_p / k_B / T.T) * solution * test_function * dx(mat.id)
)
self.F_trapping = F_trapping
self.F += self.F_trapping
self.sub_expressions += expressions_trap
[docs]
def create_source_form(self, dx):
"""Create the source form for the trap
Args:
dx (fenics.Measure): the dx measure of the sim
"""
for source in self.sources:
if isinstance(source, RadioactiveDecay):
source.value = source.form(self.solution)
self.F_source = -source.value * self.test_function * dx(source.volume)
self.F += self.F_source
if isinstance(source.value, (Expression, UserExpression)):
self.sub_expressions.append(source.value)