"""Jensen-Seaton isotherm model."""
import numpy
from scipy import integrate
from scipy import optimize
from pygaps.modelling.base_model import IsothermBaseModel
from pygaps.utilities.exceptions import CalculationError
[docs]class JensenSeaton(IsothermBaseModel):
r"""
Jensen-Seaton isotherm model.
.. math::
n(p) = K p \Big[1 + \Big(\frac{K p}{(a (1 + b p)}\Big)^c\Big]^{-1/c}
Notes
-----
When modelling adsorption in micropores, a requirement was highlighted by
Jensen and Seaton in 1996 [#]_, that at sufficiently high pressures the
adsorption isotherm should not reach a horizontal plateau corresponding
to saturation but that this asymptote should continue to rise due to
the compression of the adsorbate in the pores. They came up with a
semi-empirical equation to describe this phenomenon based on a function
that interpolates between two asymptotes: the Henry’s law asymptote
at low pressure and an asymptote reflecting the compressibility of the
adsorbate at high pressure.
Here :math:`K` is the Henry constant, :math:`b` is the compressibility of the
adsorbed phase and :math:`c` an empirical constant.
The equation can be used to model both absolute and excess adsorption as
the pore volume can be incorporated into the definition of :math:`b`,
although this can lead to negative adsorption slopes for the
compressibility asymptote. This equation has been found to provide a
better fit for experimental data from microporous solids than the Langmuir
or Toth equation, in particular for adsorbent/adsorbate systems with
high Henry’s constants where the amount adsorbed increases rapidly at
relatively low pressures and then slows down dramatically.
References
----------
.. [#] Jensen, C. R. C.; Seaton, N. A., An Isotherm Equation for Adsorption to High
Pressures in Microporous Adsorbents. Langmuir 1996, 12, (Copyright (C) 2012
American Chemical Society (ACS). All Rights Reserved.), 2866-2867.
"""
# Model parameters
name = 'JensenSeaton'
formula = r"n(p) = K p [1 + (\frac{K p}{(a (1 + b p)})^c]^{-1/c}"
calculates = 'loading'
param_names = ("K", "a", "b", "c")
param_default_bounds = (
(0., numpy.inf),
(0., numpy.inf),
(0., numpy.inf),
(0., numpy.inf),
)
[docs] def loading(self, pressure):
"""
Calculate loading at specified pressure.
Parameters
----------
pressure : float
The pressure at which to calculate the loading.
Returns
-------
float
Loading at specified pressure.
"""
Kp = self.params["K"] * pressure
a = self.params["a"]
b = self.params["b"]
c = self.params["c"]
return Kp / (1 + (Kp / (a * (1 + b * pressure)))**c)**(1 / c)
[docs] def pressure(self, loading):
"""
Calculate pressure at specified loading.
For the Jensen-Seaton model, the pressure will
be computed numerically as no analytical inversion is possible.
Parameters
----------
loading : float
The loading at which to calculate the pressure.
Returns
-------
float
Pressure at specified loading.
"""
def fun(x):
return self.loading(x) - loading
opt_res = optimize.root(fun, numpy.zeros_like(loading), method='hybr')
if not opt_res.success:
raise CalculationError(f"Root finding for value {loading} failed.")
return opt_res.x
[docs] def spreading_pressure(self, pressure):
r"""
Calculate spreading pressure at specified gas pressure.
Function that calculates spreading pressure by solving the
following integral at each point i.
.. math::
\pi = \int_{0}^{p_i} \frac{n_i(p_i)}{p_i} dp_i
The integral for the Jensen-Seaton model cannot be solved analytically
and must be calculated numerically.
Parameters
----------
pressure : float
The pressure at which to calculate the spreading pressure.
Returns
-------
float
Spreading pressure at specified pressure.
"""
return integrate.quad(lambda x: self.loading(x) / x, 0, pressure)[0]
[docs] def initial_guess(self, pressure, loading):
"""
Return initial guess for fitting.
Parameters
----------
pressure : ndarray
Pressure data.
loading : ndarray
Loading data.
Returns
-------
dict
Dictionary of initial guesses for the parameters.
"""
saturation_loading, langmuir_k = super().initial_guess(pressure, loading)
guess = {"K": saturation_loading * langmuir_k, "a": 1, "b": 1, "c": 1}
guess = self.initial_guess_bounds(guess)
return guess