Isotherm model fitting#

Overview#

In adsorption, a model is a physical or empirical relationship between bulk phase adsorbate pressure and the amount adsorbed on the material, or loading. Therefore it can be expressed as a function:

\[ \begin{align}\begin{aligned}n = f(p, ...)\\or\\p = f(n, ...)\end{aligned}\end{align} \]

Many types of theoretical models have been developed which attempt to describe the phenomenon of adsorption. While none can accurately describe all situations, different behaviours, interactions and pressure ranges, experimental data can be often be reliably fitted to a suitable model.

Caution

It is left to the best judgement of the user when to apply a specific model.

Modelling in pyGAPS#

In pyGAPS, the ModelIsotherm() is the class which contains a model and other needed metadata. While it is instantiated using discrete data, it does not store it directly. Another principal difference from the PointIsotherm class is that, while the former can contain both the adsorption and desorption branch of the physical isotherm, the latter contains a model for only one branch, determined at initialisation.

Currently the models implemented are:

For an explanation of each model, visit its respective reference page. Custom models can also be added to the list if you are willing to write them. See the procedure below.

Working with models#

A ModelIsotherm can be created from raw values, as detailed in the isotherms section. However, for most use case scenarios, the user will want to create a ModelIsotherm starting from a previously created PointIsotherm class.

To do so, the class includes a specific class method, from_pointisotherm(), which allows a PointIsotherm to be used. Alternatively, a utility function pygaps.modelling.model_iso is provided. An example is:

model_isotherm = pygaps.ModelIsotherm.from_pointisotherm(
    point_isotherm,
    branch='ads'
    model='Henry',
)
# or using the convenience function
import pygaps.modelling as pgm
model_isotherm = pgm.model_iso(
    point_isotherm,
    branch='ads'
    model='Henry',
)

Alternatively, a list of model names can be passed that will be fitted sequentially, returning the model with the best RMSE fit. If model='guess', pyGAPS will attempt to fit some of the common models.

Caution

This mode should be used carefully, as there's no guarantee that the the best fitting model is the one with any physical significance. It it also worth noting that, since a lot of models may be evaluated, this option will take significantly more resources than simply specifying the model manually.

As a consequence, some models which require a lot of overhead, such as the virial model, have been excluded from this option.

import pygaps.modelling as pgm

# Attempting all basic models
model_isotherm = pgm.model_iso(
    point_isotherm,
    branch='des',
    model='guess',
)

# With a subset of models instead
model_isotherm = pgm.model_iso(
    point_isotherm,
    branch='des',
    model=['Henry', 'Langmuir', 'BET', 'Virial'],
)

Once the a ModelIsotherm is generated, it can be used as a regular PointIsotherm, as it contains the same common methods. Some slight differences exist:

  • ModelIsotherm``s do not contain the ``isotherm.data() method, as they contain no data. Instead the user can access the isotherm.model.params property, to get a dictionary of the calculated model parameters.

  • The isotherm.loading() and isotherm.pressure() functions will return equidistant points over the whole range of the isotherm instead of returning actual datapoints.

  • Some models calculate pressure as a function of loading, others calculate loading as a function of pressure. If the model function cannot be inverted, the requested data will have to be computed using numerical methods. Depending on the model, the minimisation may or may not converge.

Comparing models and data#

ModelIsotherms can easily be plotted using the same function as PointIsotherms. For example, to graphically compare a model and an experimental isotherm:

import pygaps.graphing as pgg
pgg.plot_iso([model_isotherm, point_isotherm])

One may notice that the loading is calculated at different pressure points from the PointIsotherm. This is done to keep the plotting function general. If the user wants the pressure points to be identical one can pass the pressure or loading points in the plotting function as the x_points and y1_points, respectively.

pgg.plot_iso(
    [model_isotherm, point_isotherm],
    x_points=point_isotherm.loading(),
)

Turning a model to points#

Sometimes, a user might want to generate a PointIsotherm based on a model. A class method from_modelisotherm() is provided for this purpose. The function method takes as parameters a ModelIsotherm, and a pressure_points keyword. This can be used to specify the array of points where the loading is calculated. If a PointIsotherm is passed instead, the loading is calculated at each of the points of this isotherm.

# Create a PointIsotherm from the model
new_point_isotherm = pygaps.PointIsotherm.from_modelisotherm(
    model_isotherm,
    pressure_points=[1,2,3,4],
)

# Use a previous PointIsotherm as reference
new_point_isotherm = pygaps.PointIsotherm.from_modelisotherm(
    model_isotherm,
    pressure_points=point_isotherm,
)

Modelling examples#

Check out the Jupyter notebook in the examples section

Custom models#

Custom models can be implemented. In the pygaps/modelling/base_model.py folder, there is a model template (IsothermBaseModel) which contains the functions which should be inherited by a custom model.

The parameters to be specified are the following:

  • The model name name and whether it calculates pressure or loading calculates.

  • Dictionaries with the model parameters names param_names and possible bounds param_bounds.

  • A function that returns an initial guess for the model parameters (initial_guess()).

  • A fitting function that determines the model parameters starting from the loading and pressure data (fit()). Alternatively, the template fitting function can be used directly if suitable.

  • Functions that return the loading and pressure calculated from the model parameters (loading(pressure) and pressure(loading)). These can be calculated analytically or numerically.

  • A function which returns the spreading pressure, if the model is to be used for IAST calculations (spreading_pressure(pressure)).

Once the model is written, it should be added to the list of usable models. This can be found in the pygaps/modelling/__init__.py file.

Don't forget to write some tests to make sure that the model works as intended. You can find the current parametrised tests in tests/modelling/test_models_isotherm.py.