Co-permeation of H and D through Pd#
This case is taken and adapted from [3] based on the experimental data reporeted in [13]. The system is first simulated with pure D2 permeation, then extended to the co-permeation regime, in which both D2 and H2 dissociate on the Pd upstream surface, diffuse through the membrane, and recombine on the downstream surface to form H2, D2 and HD.
Permeation of pure D2#
Below we present the implementation of pure D2 permeation through a Pd membrane. Two membrane thicknesses are considered (0.025 mm and 0.05 mm), and simulations are performed at temperatures of 825 K and 865 K. The corresponding experimental setup and model parameters are described in [3].
Implementation#
import festim as F
import numpy as np
def make_festim_model_dlr(pd_thickness, temperature, upstream_d2_pressure):
my_model = F.HydrogenTransportProblem()
D = F.Species("D")
my_model.species = [D]
my_model.mesh = F.Mesh1D(vertices=np.linspace(0, pd_thickness, 100))
my_mat = F.Material(
name="Pd",
D_0=2.636e-4,
E_D=1315.8 * F.k_B,
)
vol = F.VolumeSubdomain1D(id=1, borders=[0, pd_thickness], material=my_mat)
left = F.SurfaceSubdomain1D(id=1, x=0)
right = F.SurfaceSubdomain1D(id=2, x=pd_thickness)
my_model.subdomains = [vol, left, right]
my_model.temperature = temperature
n = 0.9297
K_S_0 = 1.511e23
K_S_E = 5918 * F.k_B
surface_reaction_dd_left = F.FixedConcentrationBC(
subdomain=left,
species=D,
value=K_S_0 * np.exp(-K_S_E / F.k_B / temperature) * upstream_d2_pressure**n,
)
my_model.boundary_conditions = [
surface_reaction_dd_left,
F.FixedConcentrationBC(
species=D,
value=0.0,
subdomain=right,
),
]
D_flux_right = F.SurfaceFlux(D, right)
D_flux_left = F.SurfaceFlux(D, left)
my_model.exports = [D_flux_left, D_flux_right]
my_model.settings = F.Settings(atol=1e8, rtol=1e-10, transient=False)
return my_model
/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
upstream_d_pressures = np.geomspace(1e-4, 3, num=6)
thicknesses = [0.025e-3, 0.05e-3]
prms = [
# Pd thickness, temperature
(0.025e-3, 825),
(0.05e-3, 825),
(0.025e-3, 865),
]
results = []
for pd_thickness, temperature in prms:
print(f"Pd thickness: {pd_thickness:.2e} m")
print(f"Temperature: {temperature:.2f} K")
dd_desorption_fluxes = []
models = []
for d2_pressure in upstream_d_pressures:
my_model = make_festim_model_dlr(
pd_thickness=pd_thickness,
temperature=temperature,
upstream_d2_pressure=d2_pressure,
)
my_model.initialise()
my_model.run()
# ------ Post processsing ------ #
models.append(my_model)
# convert all data to mol
for export in my_model.exports:
avogadro = 6.022e23
# convert from particles to moles
export.data = np.array(export.data) / avogadro
# convert from atoms to molecules
export.data *= 1 / 2
DD_flux = my_model.exports[1]
dd_desorption_fluxes.append(np.abs(DD_flux.data)[-1])
results.append(
{
"thickness": pd_thickness,
"temperature": temperature,
"upstream_d_pressures": upstream_d_pressures,
"dd_desorption_fluxes": dd_desorption_fluxes,
"models": models,
}
)
Pd thickness: 2.50e-05 m
Temperature: 825.00 K
Pd thickness: 5.00e-05 m
Temperature: 825.00 K
Pd thickness: 2.50e-05 m
Temperature: 865.00 K
Results#
Below is the evolution of the downstream D2 flux as a function of upstream D2 pressure. There is a good agreement between FESTIM and the experimental data. The change of the slope in the experimentally measured flux at higher pressures suggest a transition to the diffusion-limited regime.
Co-permeation of H and D#
The co-permeation of H and D through a Pd membrane is simulated using FESTIM within a one-dimensional transient framework. A Pd membrane with thicknesses of 0.025 mm and 0.05 mm is discretized using a 1D mesh, with H and D treated as distinct diffusing species within the same solid phase. Temperature-dependent bulk diffusion coefficients for each isotope are prescribed using Arrhenius relations. Surface reactions at both the upstream and downstream boundaries are explicitly modeled via surface reaction boundary conditions. At each surface, adsorption and desorption reactions are defined for the H–H, D–D, and mixed H–D, enabling the formation of H2, D2 and HD molecules. The corresponding kinetic parameters and material properties are taken from the TMAP7 verification and validation report [3] with system-level effects such as enclosures, pumping, etc. not included.
Upstream boundary conditions are imposed through effective H2, D2 gas pressures, while the downstream boundary is maintained at zero gas pressure. Surface-integrated fluxes of H, D, H2, HD, and D2 are evaluated using custom surface flux exports. Fluxes of H2, HD, and D2 are subsequently post-processed and converted to molar units to enable direct comparison with experimental co-permeation data at temperatures of 825 K and 865 K.
Implementation#
pd_thickness = 0.025e-3 # m
temperature = 870 # K
upstream_effective_H_pressure = 0.063 # Pa
my_model = F.HydrogenTransportProblem()
H = F.Species("H")
D = F.Species("D")
my_model.species = [H, D]
my_model.mesh = F.Mesh1D(vertices=np.linspace(0, pd_thickness, 100))
my_mat = F.Material(
name="Pd",
D_0={
H: 3.728e-4,
D: 2.636e-4,
},
E_D={
H: 1315.8 * F.k_B,
D: 1315.8 * F.k_B,
},
)
vol = F.VolumeSubdomain1D(id=1, borders=[0, pd_thickness], material=my_mat)
left = F.SurfaceSubdomain1D(id=1, x=0)
right = F.SurfaceSubdomain1D(id=2, x=pd_thickness)
my_model.subdomains = [vol, left, right]
my_model.temperature = temperature
E_kr = 11836 * F.k_B
surface_reaction_hd_left = F.SurfaceReactionBC(
reactant=[H, D],
gas_pressure=0, # free parameter
k_r0=2.502e-24 / (3 * temperature) ** 0.5,
E_kr=E_kr,
k_d0=2.1897e22 / (3 * temperature) ** 0.5,
E_kd=0,
subdomain=left,
)
surface_reaction_hh_left = F.SurfaceReactionBC(
reactant=[H, H],
gas_pressure=0, # free parameter
k_r0=2.502e-24 / (2 * temperature) ** 0.5,
E_kr=E_kr,
k_d0=2.1897e22 / (2 * temperature) ** 0.5,
E_kd=0,
subdomain=left,
)
surface_reaction_dd_left = F.SurfaceReactionBC(
reactant=[D, D],
gas_pressure=0, # free parameter
k_r0=2.502e-24 / (4 * temperature) ** 0.5,
E_kr=E_kr,
k_d0=2.1897e22 / (4 * temperature) ** 0.5,
E_kd=0,
subdomain=left,
)
surface_reaction_hd_right = F.SurfaceReactionBC(
reactant=[H, D],
gas_pressure=0,
k_r0=2.502e-24 / (3 * temperature) ** 0.5,
E_kr=E_kr,
k_d0=2.1897e22 / (3 * temperature) ** 0.5,
E_kd=0,
subdomain=right,
)
surface_reaction_hh_right = F.SurfaceReactionBC(
reactant=[H, H],
gas_pressure=0,
k_r0=2.502e-24 / (2 * temperature) ** 0.5,
E_kr=E_kr,
k_d0=2.1897e22 / (2 * temperature) ** 0.5,
E_kd=0,
subdomain=right,
)
surface_reaction_dd_right = F.SurfaceReactionBC(
reactant=[D, D],
gas_pressure=0,
k_r0=2.502e-24 / (4 * temperature) ** 0.5,
E_kr=E_kr,
k_d0=2.1897e22 / (4 * temperature) ** 0.5,
E_kd=0,
subdomain=right,
)
my_model.boundary_conditions = [
surface_reaction_hd_left,
surface_reaction_hh_left,
surface_reaction_dd_left,
surface_reaction_hd_right,
surface_reaction_hh_right,
surface_reaction_dd_right,
]
H_flux_right = F.SurfaceFlux(H, right)
H_flux_left = F.SurfaceFlux(H, left)
D_flux_right = F.SurfaceFlux(D, right)
D_flux_left = F.SurfaceFlux(D, left)
HD_flux = FluxFromSurfaceReaction(surface_reaction_hd_right)
HH_flux = FluxFromSurfaceReaction(surface_reaction_hh_right)
DD_flux = FluxFromSurfaceReaction(surface_reaction_dd_right)
# needed to compute D_global even if not used
for flux in [HH_flux, HD_flux, DD_flux]:
flux.field = H
my_model.exports = [
H_flux_left,
H_flux_right,
D_flux_left,
D_flux_right,
HD_flux,
HH_flux,
DD_flux,
]
my_model.settings = F.Settings(atol=1e11, rtol=1e-6, final_time=10, transient=True)
my_model.settings.stepsize = 0.2
all_d_desorption_fluxes = []
hh_desorption_fluxes = []
hd_desorption_fluxes = []
dd_desorption_fluxes = []
upstream_d_pressures = np.geomspace(4e-3, 1, num=5)
for effective_d_pressure in upstream_d_pressures:
upstream_d2_pressure = effective_d_pressure
upstream_h2_pressure = upstream_effective_H_pressure
print(f"Upstream D2 pressure: {upstream_d2_pressure:.2e} Pa")
print(f"Upstream H2 pressure: {upstream_h2_pressure:.2e} Pa")
for flux_bc in surface_reaction_dd_left.flux_bcs:
flux_bc.gas_pressure = effective_d_pressure
for flux_bc in surface_reaction_hh_left.flux_bcs:
flux_bc.gas_pressure = upstream_h2_pressure
my_model.initialise()
my_model.run()
# convert all data to mol
for export in my_model.exports:
avogadro = 6.022e23
export.data = np.array(export.data) / avogadro
all_d_desorption_fluxes.append(np.abs(D_flux_right.data)[-1])
print(
f"Desorption flux at {effective_d_pressure:.2e} Pa: {all_d_desorption_fluxes[-1]:.2e} mol/m^2/s"
)
hh_desorption_fluxes.append(np.abs(HH_flux.data)[-1])
hd_desorption_fluxes.append(np.abs(HD_flux.data)[-1])
dd_desorption_fluxes.append(np.abs(DD_flux.data)[-1])
Upstream D2 pressure: 4.00e-03 Pa
Upstream H2 pressure: 6.30e-02 Pa
Desorption flux at 4.00e-03 Pa: 1.40e-06 mol/m^2/s
Upstream D2 pressure: 1.59e-02 Pa
Upstream H2 pressure: 6.30e-02 Pa
Desorption flux at 1.59e-02 Pa: 6.46e-06 mol/m^2/s
Upstream D2 pressure: 6.32e-02 Pa
Upstream H2 pressure: 6.30e-02 Pa
Desorption flux at 6.32e-02 Pa: 3.31e-05 mol/m^2/s
Upstream D2 pressure: 2.51e-01 Pa
Upstream H2 pressure: 6.30e-02 Pa
Desorption flux at 2.51e-01 Pa: 1.53e-04 mol/m^2/s
Upstream D2 pressure: 1.00e+00 Pa
Upstream H2 pressure: 6.30e-02 Pa
Desorption flux at 1.00e+00 Pa: 6.17e-04 mol/m^2/s
Results#
Below is the evolution of H2, HD, and D2 fluxes as a function of the effective deuterium upstream pressure.
There is a reasonable agreement between the experimental data and the FESTIM simulation.
A better agreement could potentially be obtained by setting the surface rates and diffusivities as free parameters and then perform some parametric optimisation.
Better experimental data with better measurements of the upstream partial pressures would be required to better constrain the model.