Skip to content

Commit

Permalink
Merge pull request #16 from ClapeyronThermo/vini/rename
Browse files Browse the repository at this point in the history
Updated docs
  • Loading branch information
viniviena authored Oct 14, 2024
2 parents 2d7d76c + ac6469b commit f7ab442
Show file tree
Hide file tree
Showing 18 changed files with 503 additions and 153 deletions.
3 changes: 2 additions & 1 deletion docs/make.jl
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ makedocs(;
"Home" => "index.md",
"Background" => "tutorials/background.md",
"Getting Started" => "tutorials/getting_started.md",
"Reference" => "reference.md"
"Supported models" => "models/models.md",
"Reference" => "reference.md",
],
)

Expand Down
21 changes: 18 additions & 3 deletions docs/src/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,27 @@
CurrentModule = Langmuir
```

# Langmuir
# Langmuir.jl

Langmuir.jl is a library for describing multi and single component adsorption equilibrium.
Langmuir.jl is a powerful Julia library designed to model adsorption equilibria for both single and multi-component systems.

For single-component adsorption, the library offers a wide range of isotherms, from simple one-parameter models like Henry's law to more complex two- and three-parameter models such as the Langmuir (Single and MultiSite), Freundlich, Temkin, Redlich-Peterson, Toth, and Sips isotherms. These models include temperature-dependent parameters, which are essential for estimating the isosteric heat of adsorption from pressure-loading data sets at varying temperatures. For a complete list of available models, refer to the list of supported models [`supported_models`](@ref).

For multicomponent systems, Langmuir.jl employs the Ideal Adsorption Solution Theory (IAST) to predict the loading of different components, given their bulk pressure, temperature and individual isotherms. This approach allows for predictive modeling of multicomponent adsorption.

A complete workflow for using Langmuir.jl typically includes the following steps:

- **Data Preparation:** Load your dataset, which should include adsorption loading, pressure, temperature, and heat data.

- **Isotherm Fitting:** Fit the appropriate isotherm model to your data using a global optimization method to ensure accurate parameter estimation.

- **Multicomponent Loading** Estimation: Use Ideal Adsorption Solution Theory (IAST) to predict the loading of each component in a multicomponent system based on bulk pressure and temperature.

- **Isosteric Heat Calculation:** Estimate the isosteric heat of adsorption for single or multicomponent systems using the library's built-in functions, which account for temperature dependencies in the isotherm parameters.


### Authors

- [Andrés Riedemann](mailto:[email protected]), University of Concepción
- [Vinicius Santana](mailto:[email protected]), Norwegian University of Science and Technology
- [Vinicius Santana](mailto:[email protected]), Norwegian University of Science and Technology - NTNU.
- Pierre Walker - Caltech.
50 changes: 50 additions & 0 deletions docs/src/models/models.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
```@meta
CurrentModule = Langmuir
```

# [Supported Isotherms](@id supported_models)

Here you can find the list of supported isotherm models.

```@contents
Pages = ["models.md"]
```

```@index
Pages = ["models.md"]
```

## No-parameter Isotherm Models


## One-parameter Isotherm Models

```@docs
Langmuir.Henry
```

## Two-parameter Isotherm Models

```@docs
Langmuir.LangmuirS1
Langmuir.Freundlich
```

## Three-parameter Isotherm Models

```@docs
Langmuir.RedlichPeterson
Langmuir.Toth
Langmuir.Sips
Langmuir.LangmuirFreundlich
Langmuir.Unilan
Langmuir.Quadratic
Langmuir.BrunauerEmmettTeller
```

## Four and more parameters Isotherm Models

```@docs
Langmuir.MultiSite
```

21 changes: 19 additions & 2 deletions docs/src/reference.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
# Reference
```@meta
CurrentModule = Langmuir
```

## Contents

Expand All @@ -12,6 +15,20 @@ Pages = ["reference.md"]
Pages = ["reference.md"]
```

```@autodocs
Modules = [Langmuir]
```@docs
Langmuir.nlsolve
Langmuir.@MultiSite
Langmuir.isotherm_lower_bound
Langmuir.isosteric_heat
Langmuir.f∂f∂2f
Langmuir.pressure
Langmuir.saturated_loading
Langmuir.x_sol
Langmuir.loading
Langmuir.henry_coefficient
Langmuir.isotherm_upper_bound
Langmuir.sp_res
Langmuir.iast
Langmuir.@with_metadata
Langmuir.f∂f
```
8 changes: 4 additions & 4 deletions docs/src/tutorials/background.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,18 +27,18 @@ $Q_{st, i} = -T*(V_g - V_a)*\left( \frac{\frac{\partial N_i}{\partial T}\rvert_P

The basic equations of the IAST are the analogue of Raoult's law in vapour–liquid equilibrium:

$Py_i = P_i^0(\pi)x_i$ (1)
$Py_i = P_i^0(\pi)x_i$

where

$\pi = \pi_i = \int_{0}^{P_i^0} \frac{N_i^0(P)}{P}dP$ for $i = 1,...,N_c$ (2)
$\pi = \pi_i = \int_{0}^{P_i^0} \frac{N_i^0(P)}{P}dP$ for $i = 1,...,N_c$

$\sum_i^{N_c} x_i = 1$ (3)
$\sum_i^{N_c} x_i = 1$


Combining (1) and (3), the following nonlinear solve is set to:

$f(\pi) = 1 - \sum_1^{N_c}\frac{Py_i}{P_i^0\left(\pi\right)}$ = 0 (4)
$f(\pi) = 1 - \sum_1^{N_c}\frac{Py_i}{P_i^0\left(\pi\right)}$ = 0



Expand Down
49 changes: 31 additions & 18 deletions src/models/Toth.jl
Original file line number Diff line number Diff line change
@@ -1,40 +1,53 @@

"""
`Toth(M, K₀, E, f₀, β)`
Toth <: IsothermModel
Toth(M, K₀, E, f₀, β)
## Inputs
- `M`::T: maximum loading capacity of the adsorbent, `[mol/kg]`
- `K₀::T: equilibrium constant at zero coverage, `[1/Pa]`
- `E`::T: adsorption energy, `[J/mol]`
- `f₀`::T: Empirical parameter, `-`
- `β`::T: Empirical parameter, `K`
- `M::T`: maximum loading capacity of the adsorbent, `[mol/kg]`
- `K₀::T`: affinity parameter, `f(Pa)`
- `E::T`: energy parameter, `[J/mol]`
- `f₀::T`: Surface heterogeneity parameter at high temperature, `[-]`
- `β::T`: Surface heterogeneity coefficient, `[K]`
## Description
Toth isotherm model:
K = K₀*exp(-E/(RT))
f = f₀ + β/T
nᵢ = M*K*P/(1 + (K*P)ᶠ)¹/ᶠ
n = M*K*P/(1 + (K*P)ᶠ)¹/ᶠ
The adsorption energy E is related to the equilibrium constant K₀ by the equation:
K = K₀ × exp(-E / (R * T))
The exponent f is also temperature dependent and can be expressed as:
f = f₀ - β/T
where:
- R is the gas constant,
- T is the temperature.
"""
struct Toth{T} <: IsothermModel{T}
M::T
K₀::T
E::T
f₀::T
β::T
@with_metadata struct Toth{T} <: IsothermModel{T}
(M::T, (0.0, Inf), "saturation loading")
(K₀::T, (0.0, Inf), "affinity parameter")
(E::T, (-Inf, 0.0), "energy parameter")
(f₀::T, (0.0, Inf), "surface heterogeneity parameter at T → ∞")
(β::T, (0.0, Inf), "surface heterogeneity coefficient")
end

function loading(model::Toth, p, T)
M = model.M
K = model.K₀*exp(-model.E/(Rgas(model)*T))
f = model.f₀ + model.β/T
Kpf = abs(K*p)^f
return M*K*p/(1 + Kpf)^(1/f)
f = model.f₀ - model.β/T
Kpf = abs(K*p)^f #f has to be between 0 and 1
_1 = one(eltype(p))
return M*K*p/(_1 + Kpf)^(_1/f)
end

henry_coefficient(model::Toth, T) = model.M*model.K₀*exp(-model.E/(Rgas(model)*T))
Expand Down
42 changes: 37 additions & 5 deletions src/models/bet.jl
Original file line number Diff line number Diff line change
@@ -1,8 +1,40 @@
struct BrunauerEmmettTeller{T} <: IsothermModel{T}
K₀a::T
K₀b::T
M::T
E::T
"""
`BrunauerEmmettTeller(K₀a, K₀b, M, E)`
BrunauerEmmettTeller <: IsothermModel
The Brunauer-Emmett-Teller (BET) isotherm model describes multilayer adsorption on a homogeneous surface. It is widely used to characterize adsorption behavior in mesoporous materials.
## Inputs
- `K₀a::T`: Affinity parameter for adsorption, `[1/Pa]`
- `K₀b::T`: Affinity parameter for multilayer adsorption, `f(Pa)`
- `M::T`: Saturation loading, `[mol/kg]`
- `E::T`: Adsorption energy, `[J/mol]`
## Description
The BET equation is given by:
n = M × K₀a × p / (1 - K₀b × p) × (1 + (K₀a - K₀b) × p)
### Temperature dependence:
The affinity parameters `K₀a` and `K₀b` depend on temperature and are given by the following expressions:
K₀a = K₀a × exp(-E / (RT))
K₀b = K₀b × exp(-E / (RT))
Where:
- `R` is the gas constant,
- `T` is the absolute temperature.
"""
@with_metadata struct BrunauerEmmettTeller{T} <: IsothermModel{T}
(K₀a::T, (0.0, Inf), "affinity parameter")
(K₀b::T, (0.0, Inf), "affinity parameter")
(M::T, (0.0, Inf), "saturation loading")
(E::T, (-Inf, 0.0), "energy parameter")
end

const BET = BrunauerEmmettTeller
Expand Down
78 changes: 68 additions & 10 deletions src/models/freundlich.jl
Original file line number Diff line number Diff line change
@@ -1,3 +1,34 @@
"""
`Freundlich(K₀, f₀, β, E)`
Freundlich <: IsothermModel
## Inputs
- `K₀::T`: Affinity parameter, `[1/Pa]`
- `f₀::T`: Surface heterogeneity parameter at high temperature, `[-]`
- `β::T`: Surface heterogeneity coefficient (K)
- `E::T`: Adsorption energy, `[J/mol]`
## Description
The Freundlich isotherm is given by:
n = K_f × pᶠ
The affinity parameter `K_f` is a temperature dependent and can be linked to adsorption energy `E` by:
K_f = K₀ × exp(-E / (RT))
The exponent f is also temperature dependent and can be expressed as:
f = f₀ - β/T
Where:
- `R` is the gas constant,
- `T` is the temperature.
"""
@with_metadata struct Freundlich{T} <: IsothermModel{T}
(K₀::T, (0.0, Inf), "Affinity parameter")
(f₀::T, (0.0, Inf), "Surface heterogeneity parameter at T → ∞")
Expand All @@ -7,29 +38,56 @@ end

function sp_res(model::Freundlich, p, T)
K₀, f₀, β, E = model.K₀, model.f₀, model.β, model.E
f = f₀ + β/T
f = f₀ - β/T
K = K₀*exp(-E/(Rgas(model)*T))
return K*p^f/f
end

function pressure_impl(model::Freundlich, Π, T,::typeof(sp_res), approx)
K₀, f₀, β, E = model.K₀, model.f₀, model.β, model.E
f = f₀ + β/T
f = f₀ - β/T
K = K₀*exp(-E/(Rgas(model)*T))
_1 = one(eltype(model))
v = _1/f
return/(K*v))^v
end

function x0_guess_fit(::Type{T}, data::AdsIsoTData) where T <: Freundlich
l, p = data.l, data.p
#l = M*p^f
#log(l) = log(M) + f*log(p)
logp, logl = log.(p), log.(l)
_1 = one.(p)
logK, f = hcat(_1,logp) \ logl
K = exp(logK)
return T(K, f, one(T), zero(T))

Ts, l_p = split_data_by_temperature(data)

logKs = Vector{eltype(Ts)}(undef, length(l_p))
fs = Vector{eltype(Ts)}(undef, length(l_p))
_1_ = ones(eltype(Ts), length(first(first(l_p))))


_1 = one(eltype(Ts))
_0 = zero(eltype(Ts))
_1s = ones(eltype(Ts), length(Ts))

for i in eachindex(l_p)
l_i, p_i = l_p[i]
logl_i, logp_i = log.(l_i), log.(p_i)
logKs[i], fs[i] = hcat(_1_, logp_i) \ logl_i
end

#l = K*p^f
#log(l) = log(K) + f*log(p)


if length(l_p) > 1
logK0, E = hcat(_1s, _1./ (Rgas(T).*Ts)) \ logKs
f0, β = hcat(_1s, -_1./Ts) \ fs
K0 = exp(logK0)
else
K0 = first(exp(logKs))
f0 = _1
β = _0
E = _1
end


return T(K0, f0, β, -E)
end

export Freundlich
Loading

0 comments on commit f7ab442

Please sign in to comment.