import ufl
import festim as F
[docs]
class Material:
"""
Material class
Args:
D_0 (float or dict): the pre-exponential factor of the
diffusion coefficient (m2/s)
E_D (float or dict): the activation energy of the diffusion
coeficient (eV)
K_S_0 (float or dict): the pre-exponential factor of the
solubility coefficient (H/m3/Pa0.5)
E_K_S (float or dict): the activation energy of the solubility
coeficient (eV)
name (str): the name of the material
thermal_conductivity (float, callable): the thermal conductivity of the material (W/m/K)
density (float, callable): the density of the material (kg/m3)
heat_capacity (float, callable): the heat capacity of the material (J/kg/K)
solubility_law (str): the solubility law of the material ("sievert" or "henry")
Attributes:
D_0 (float or dict): the pre-exponential factor of the
diffusion coefficient (m2/s)
E_D (float or dict): the activation energy of the diffusion
coeficient (eV)
K_S_0 (float or dict): the pre-exponential factor of the
solubility coefficient (H/m3/Pa0.5)
E_K_S (float or dict): the activation energy of the solubility
coeficient (eV)
name (str): the name of the material
thermal_conductivity (float, callable): the thermal conductivity of the material (W/m/K)
density (float, callable): the density of the material (kg/m3)
heat_capacity (float, callable): the heat capacity of the material (J/kg/K)
Examples:
.. testsetup:: Material
from festim import Material
.. testcode:: Material
# if only one species:
Material(D_0=1.9e-7, E_D=0.2, name="my_mat")
# if several species:
Material(
D_0={"Species_1": 1.9e-7, "Species_2": 2.0e-7},
E_D={"Species_1": 0.2, "Species_2": 0.3},
name="my_mat"
)
"""
def __init__(
self,
D_0,
E_D,
K_S_0=None,
E_K_S=None,
thermal_conductivity=None,
density=None,
heat_capacity=None,
name=None,
solubility_law=None,
) -> None:
self.D_0 = D_0
self.E_D = E_D
self.K_S_0 = K_S_0
self.E_K_S = E_K_S
self.thermal_conductivity = thermal_conductivity
self.density = density
self.heat_capacity = heat_capacity
self.name = name
self.solubility_law = solubility_law
[docs]
def get_D_0(self, species=None):
"""Returns the pre-exponential factor of the diffusion coefficient
Args:
species (festim.Species or str, optional): the species we want the pre-exponential
factor of the diffusion coefficient of. Only needed if D_0 is a dict.
Returns:
float: the pre-exponential factor of the diffusion coefficient
"""
if isinstance(self.D_0, (float, int)):
return self.D_0
elif isinstance(self.D_0, dict):
if species is None:
raise ValueError("species must be provided if D_0 is a dict")
if species in self.D_0:
return self.D_0[species]
elif species.name in self.D_0:
return self.D_0[species.name]
else:
raise ValueError(f"{species} is not in D_0 keys")
else:
raise TypeError("D_0 must be either a float, int or a dict")
[docs]
def get_E_D(self, species=None):
"""Returns the activation energy of the diffusion coefficient
Args:
species (festim.Species or str, optional): the species we want the activation
energy of the diffusion coefficient of. Only needed if E_D is a dict.
Returns:
float: the activation energy of the diffusion coefficient
"""
if isinstance(self.E_D, (float, int)):
return self.E_D
elif isinstance(self.E_D, dict):
if species is None:
raise ValueError("species must be provided if E_D is a dict")
if species in self.E_D:
return self.E_D[species]
elif species.name in self.E_D:
return self.E_D[species.name]
else:
raise ValueError(f"{species} is not in E_D keys")
else:
raise TypeError("E_D must be either a float, int or a dict")
[docs]
def get_K_S_0(self, species=None) -> float:
"""Returns the pre-exponential factor of the solubility coefficient
Args:
species: the species we want the pre-exponential
factor of the solubility coefficient of. Only needed if K_S_0 is a dict.
Returns:
the pre-exponential factor of the solubility coefficient
"""
if isinstance(self.K_S_0, (float, int)):
return self.K_S_0
elif isinstance(self.K_S_0, dict):
if species is None:
raise ValueError("species must be provided if K_S_0 is a dict")
if species in self.K_S_0:
return self.K_S_0[species]
elif species.name in self.K_S_0:
return self.K_S_0[species.name]
else:
raise ValueError(f"{species} is not in K_S_0 keys")
else:
raise TypeError("K_S_0 must be either a float, int or a dict")
[docs]
def get_E_K_S(self, species=None) -> float:
"""Returns the activation energy of the solubility coefficient
Args:
species: the species we want the activation
energy of the solubility coefficient of. Only needed if E_K_S is a dict.
Returns:
the activation energy of the solubility coefficient
"""
if isinstance(self.E_K_S, (float, int)):
return self.E_K_S
elif isinstance(self.E_K_S, dict):
if species is None:
raise ValueError("species must be provided if E_K_S is a dict")
if species in self.E_K_S:
return self.E_K_S[species]
elif species.name in self.E_K_S:
return self.E_K_S[species.name]
else:
raise ValueError(f"{species} is not in E_K_S keys")
else:
raise TypeError("E_K_S must be either a float, int or a dict")
[docs]
def get_diffusion_coefficient(self, mesh, temperature, species=None):
"""Defines the diffusion coefficient
Args:
mesh (dolfinx.mesh.Mesh): the domain mesh
temperature (dolfinx.fem.Constant): the temperature
species (festim.Species, optional): the species we want the diffusion
coefficient of. Only needed if D_0 and E_D are dicts.
Returns:
ufl.algebra.Product: the diffusion coefficient
"""
# TODO use get_D_0 and get_E_D to refactore this method, something like:
# D_0 = self.get_D_0(species=species)
# E_D = self.get_E_D(species=species)
# D_0 = F.as_fenics_constant(D_0, mesh)
# E_D = F.as_fenics_constant(E_D, mesh)
# return D_0 * ufl.exp(-E_D / F.k_B / temperature)
if isinstance(self.D_0, (float, int)) and isinstance(self.E_D, (float, int)):
D_0 = F.as_fenics_constant(self.D_0, mesh)
E_D = F.as_fenics_constant(self.E_D, mesh)
return D_0 * ufl.exp(-E_D / F.k_B / temperature)
elif isinstance(self.D_0, dict) and isinstance(self.E_D, dict):
# check D_0 and E_D have the same keys
# this check should go in a setter
if list(self.D_0.keys()) != list(self.E_D.keys()):
raise ValueError("D_0 and E_D have different keys")
if species is None:
raise ValueError("species must be provided if D_0 and E_D are dicts")
if species in self.D_0:
D_0 = F.as_fenics_constant(self.D_0[species], mesh)
elif species.name in self.D_0:
D_0 = F.as_fenics_constant(self.D_0[species.name], mesh)
else:
raise ValueError(f"{species} is not in D_0 keys")
if species in self.E_D:
E_D = F.as_fenics_constant(self.E_D[species], mesh)
elif species.name in self.E_D:
E_D = F.as_fenics_constant(self.E_D[species.name], mesh)
return D_0 * ufl.exp(-E_D / F.k_B / temperature)
else:
raise ValueError("D_0 and E_D must be either floats or dicts")
[docs]
def get_solubility_coefficient(self, mesh, temperature, species=None):
"""Defines the solubility coefficient
Args:
mesh (dolfinx.mesh.Mesh): the domain mesh
temperature (dolfinx.fem.Constant): the temperature
species (festim.Species, optional): the species we want the solubility
coefficient of. Only needed if K_S_0 and E_K_S are dicts.
Returns:
ufl.algebra.Product: the solubility coefficient
"""
# TODO use get_K_S0 and get_E_K_S to refactore this method, something like:
# K_S_0 = self.get_K_S_0(species=species)
# E_K_S = self.get_E_K_S(species=species)
# K_S_0 = F.as_fenics_constant(K_S_0, mesh)
# E_K_S = F.as_fenics_constant(E_K_S, mesh)
# return K_S_0 * ufl.exp(-E_K_S / F.k_B / temperature)
if isinstance(self.K_S_0, (float, int)) and isinstance(
self.E_K_S, (float, int)
):
K_S_0 = F.as_fenics_constant(self.K_S_0, mesh)
E_K_S = F.as_fenics_constant(self.E_K_S, mesh)
return K_S_0 * ufl.exp(-E_K_S / F.k_B / temperature)
elif isinstance(self.K_S_0, dict) and isinstance(self.E_K_S, dict):
# check D_0 and E_D have the same keys
# this check should go in a setter
if list(self.K_S_0.keys()) != list(self.E_K_S.keys()):
raise ValueError("K_S_0 and E_K_S have different keys")
if species is None:
raise ValueError(
"species must be provided if K_S_0 and E_K_S are dicts"
)
if species in self.K_S_0:
K_S_0 = F.as_fenics_constant(self.K_S_0[species], mesh)
elif species.name in self.K_S_0:
K_S_0 = F.as_fenics_constant(self.K_S_0[species.name], mesh)
else:
raise ValueError(f"{species} is not in K_S_0 keys")
if species in self.E_K_S:
E_K_S = F.as_fenics_constant(self.E_K_S[species], mesh)
elif species.name in self.E_K_S:
E_K_S = F.as_fenics_constant(self.E_K_S[species.name], mesh)
return K_S_0 * ufl.exp(-E_K_S / F.k_B / temperature)
else:
raise ValueError("K_S_0 and E_K_S must be either floats or dicts")