Diffusion with Composite Material Layers#
This verification case is Val-1e of TMAP7’s V&V report [3]. It consists of a composite one-dimensional domain with two materials (PyC and SiC) without traps and the fixed concentration on the left boundary. The concentration at the opposite side is equal to zero.
The problem can be analytically solved. The steady state solution for the PyC is given in [3] as:
while the concentration profile for the SiC layer is given as:
where
\(x\) is the distance from free surface of PyC
\(a\) is the thickness of the PyC layer (\(33 \ \mathrm{\mu m}\))
\(l\) is the thickness of the SiC layer (\(66 \ \mathrm{\mu m}\))
\(C_0\) is the concentration at the PyC free surface (\(3.0537 \times 10^{25} \mathrm{m}^{-3}\))
\(D_{PyC}\) is the diffusivity in PyC (\(1.274 \times 10^{-7}~\mathrm{m}^{2}\mathrm{s}^{-1}\))
\(D_{SiC}\) is the diffusivity in SiC (\(2.622 \times 10^{-11}~\mathrm{m}^{2}\mathrm{s}^{-1}\))
The analytical transient solution from Table 3, row 1 in [10] for the concentration in the PyC and SiC side of the composite slab is given as:
and
where
and \(\lambda_n\) are the roots of
Note
In TMAP8 V&V there is a note stating that the expressions of the analytical solution for the transient case in TMAP4 and TMAP7 V&V books are inconsistent with the results, which suggest typographical errors. Therefore, we used the expression provided in TMAP8 V&V and taken from [10].
FESTIM Code#
import festim as F
import numpy as np
from dolfinx.geometry import bb_tree, compute_colliding_cells, compute_collisions_points
class PointValue(F.VolumeQuantity):
def __init__(self, field, volume, x0, filename=None):
super().__init__(field, volume, filename)
self.x0 = x0
def compute(self):
u = self.field.solution
mesh = u.function_space.mesh
tree = bb_tree(mesh, mesh.geometry.dim)
cell_candidates = compute_collisions_points(tree, self.x0)
cell = compute_colliding_cells(mesh, cell_candidates, self.x0).array
assert len(cell) > 0
first_cell = cell[0]
self.value = self.field.solution.eval(self.x0, first_cell)
self.data.append(self.value)
# Define input parameters
a = 33e-6
l = 66e-6
D0_PyC = 1.274e-7
D0_SiC = 2.622e-11
T = 1.0e3
c_left = 3.0537e25
# Create the FESTIM model
model = F.HydrogenTransportProblem()
vertices = np.concatenate(
[
np.linspace(0, a, num=500),
np.linspace(a, a + l, num=500),
]
)
model.mesh = F.Mesh1D(vertices)
material_PyC = F.Material(D_0=D0_PyC, E_D=0)
material_SiC = F.Material(D_0=D0_SiC, E_D=0)
left_volume = F.VolumeSubdomain1D(id=1, borders=[0, a], material=material_PyC)
right_volume = F.VolumeSubdomain1D(id=2, borders=[a, a + l], material=material_SiC)
left_surface = F.SurfaceSubdomain1D(id=3, x=0)
right_surface = F.SurfaceSubdomain1D(id=4, x=a + l)
model.subdomains = [left_volume, right_volume, left_surface, right_surface]
H = F.Species("H")
model.species = [H]
model.temperature = T
model.boundary_conditions = [
F.FixedConcentrationBC(subdomain=left_surface, value=c_left, species=H),
F.FixedConcentrationBC(subdomain=right_surface, value=0, species=H),
]
model.settings = F.Settings(atol=1e10, rtol=1e-10, max_iterations=30, final_time=100)
model.settings.stepsize = F.Stepsize(
initial_value=1e-4,
growth_factor=1.1,
cutback_factor=0.9,
target_nb_iterations=10,
max_stepsize=1,
)
x1 = 32e-6
x2 = 48.75e-6
point_value1 = PointValue(model.species[0], volume=left_volume, x0=np.array([x1, 0, 0]))
point_value2 = PointValue(
model.species[0], volume=right_volume, x0=np.array([x2, 0, 0])
)
model.exports = [point_value1, point_value2]
model.initialise()
model.run()
/home/docs/checkouts/readthedocs.org/user_builds/festim-vv-report/conda/festim-2/lib/python3.11/site-packages/festim/coupled_heat_hydrogen_problem.py:1: TqdmExperimentalWarning: Using `tqdm.autonotebook.tqdm` in notebook mode. Use `tqdm.tqdm` instead to force console mode (e.g. in jupyter console)
import tqdm.autonotebook
Comparison with exact solution#
import matplotlib.pyplot as plt
computed_solution = H.solution.x.array[:]
computed_x = model.mesh.mesh.geometry.x[:, 0]
analytical_solution = analytical_expression_steadystate(
computed_x, a, l, D0_PyC, D0_SiC
)
error = RMSPE(computed_solution, analytical_solution)
print(f"RMSPE={error:.2f}%")
plt.plot(computed_x * 1e6, computed_solution, label="FESTIM", linewidth=2)
plt.plot(
computed_x * 1e6, analytical_solution, label="analytical", linewidth=2, ls="dashed"
)
plt.ylabel(r"Cocentration, m$^{-3}$")
plt.xlabel(r"x, $\mathrm{\mu}$m")
plt.legend(frameon=False)
plt.show()
RMSPE=0.12%
for x, festim_data in zip([x1, x2], [point_value1, point_value2]):
t = np.array(festim_data.t)
indx = np.where(t >= 0.1)[0]
t = t[indx]
festim_solution = np.array(festim_data.data)[indx].flatten()
analytical_solution = analytical_expression_temporal(t, x, a, l, D0_PyC, D0_SiC)
error = RMSPE(festim_solution, analytical_solution)
print(f"RMSPE at x={x} m is {error:.2f}%")
plt.plot(
t, festim_solution, label=f"FESTIM: x={x*1e6}" + r" $\mathrm{\mu m}$", lw=2
)
plt.plot(
t,
analytical_solution,
ls="dashed",
label=f"Analytical: x={x*1e6}" + r" $\mathrm{\mu m}$",
)
plt.ylabel(r"Concentration, m$^{-3}$")
plt.xlabel("Time, s")
plt.legend(frameon=False)
plt.show()
RMSPE at x=3.2e-05 m is 0.04%
RMSPE at x=4.875e-05 m is 0.54%