Modeling Adsorption Isotherms with AdsorpR

Jajati Mandal

University of Salford, United Kingdom
J.Mandal2@salford.ac.uk

Sandipan Samanta

Independent Researcher
ssondiponsamanta@gmail.com

2025-04-09

๐Ÿ“ฆ Introduction

The AdsorpR package provides functions for modeling four classical adsorption isotherms:

These models are commonly used in environmental and chemical engineering studies to describe sorption mechanisms of contaminants onto solid adsorbents.

๐Ÿงช Sample Dataset

We demonstrate model usage using a simple example dataset representing equilibrium concentration (Ce) and the amount adsorbed (Qe):

Ce <- c(1, 2, 3, 4, 5)
Qe <- c(0.8, 1.5, 2.1, 2.6, 2.9)

๐Ÿ“ Langmuir Isotherm

result_l <- langmuir_model(Ce, Qe)
print(result_l[1:2])        # Qmax and KL
#> $`Langmuir Qmax (mg/g)`
#>       Ce 
#> 8.669989 
#> 
#> $`Langmuir KL (L/mg)`
#>        Ce 
#> 0.1040153
print(result_l$`Model Summary`)
#> 
#> Call:
#> lm(formula = Ce_by_Qe ~ Ce)
#> 
#> Residuals:
#>         1         2         3         4         5 
#>  0.025780 -0.006227 -0.026329 -0.031780  0.038556 
#> 
#> Coefficients:
#>             Estimate Std. Error t value Pr(>|t|)    
#> (Intercept)  1.10888    0.03778   29.35 8.69e-05 ***
#> Ce           0.11534    0.01139   10.12  0.00205 ** 
#> ---
#> Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
#> 
#> Residual standard error: 0.03602 on 3 degrees of freedom
#> Multiple R-squared:  0.9716, Adjusted R-squared:  0.9621 
#> F-statistic: 102.5 on 1 and 3 DF,  p-value: 0.002052
result_l$Plot
#> `geom_smooth()` using formula = 'y ~ x'

๐Ÿ“ Freundlich Isotherm

result_f <- freundlich_model(Ce, Qe)
print(result_f[1:2])        # Kf and n
#> $`Freundlich Kf`
#> (Intercept) 
#>   0.8265344 
#> 
#> $`Freundlich n`
#>   log_Ce 
#> 1.228491
print(result_f$`Model Summary`)
#> 
#> Call:
#> lm(formula = log_Qe ~ log_Ce)
#> 
#> Residuals:
#>         1         2         3         4         5 
#> -0.014171  0.013790  0.016579  0.007632 -0.023829 
#> 
#> Coefficients:
#>             Estimate Std. Error t value Pr(>|t|)    
#> (Intercept) -0.08274    0.01818  -4.551 0.019877 *  
#> log_Ce       0.81401    0.03759  21.653 0.000216 ***
#> ---
#> Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
#> 
#> Residual standard error: 0.02075 on 3 degrees of freedom
#> Multiple R-squared:  0.9936, Adjusted R-squared:  0.9915 
#> F-statistic: 468.8 on 1 and 3 DF,  p-value: 0.0002156
result_f$Plot
#> `geom_smooth()` using formula = 'y ~ x'

๐Ÿ“ BET Isotherm

result_b <- bet_model(Ce, Qe)
print(result_b[1:2])        # Qm and Cb
#> $`BET Qm (mg/g)`
#>    BET_x 
#> 0.346352 
#> 
#> $`BET Cb`
#>     BET_x 
#> -3.023631
print(result_b$`Model Summary`)
#> 
#> Call:
#> lm(formula = BET_y ~ BET_x)
#> 
#> Residuals:
#>        1        2        3        4        5 
#>  0.53410 -0.06129 -0.56939 -0.81374  0.91032 
#> 
#> Coefficients:
#>             Estimate Std. Error t value Pr(>|t|)  
#> (Intercept)  -0.9549     0.8783  -1.087   0.3565  
#> BET_x         3.8421     1.4566   2.638   0.0778 .
#> ---
#> Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
#> 
#> Residual standard error: 0.8375 on 3 degrees of freedom
#> Multiple R-squared:  0.6987, Adjusted R-squared:  0.5983 
#> F-statistic: 6.958 on 1 and 3 DF,  p-value: 0.0778
result_b$Plot
#> `geom_smooth()` using formula = 'y ~ x'

๐Ÿ“ Temkin Isotherm

result_t <- temkin_model(Ce, Qe)
print(result_t[1:2])        # A and B
#> $`Temkin A`
#> (Intercept) 
#>    1.712047 
#> 
#> $`Temkin B`
#>    ln_Ce 
#> 1.324248
print(result_t$`Model Summary`)
#> 
#> Call:
#> lm(formula = adjusted_Qe ~ ln_Ce)
#> 
#> Residuals:
#>      1      2      3      4      5 
#>  217.9 -321.9 -165.7  129.2  140.4 
#> 
#> Coefficients:
#>             Estimate Std. Error t value Pr(>|t|)    
#> (Intercept)   1764.1      234.5   7.522 0.004870 ** 
#> ln_Ce         3280.9      210.6  15.577 0.000575 ***
#> ---
#> Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
#> 
#> Residual standard error: 267.7 on 3 degrees of freedom
#> Multiple R-squared:  0.9878, Adjusted R-squared:  0.9837 
#> F-statistic: 242.7 on 1 and 3 DF,  p-value: 0.0005749
result_t$Plot
#> `geom_smooth()` using formula = 'y ~ x'

๐Ÿ” Non-linear Isotherm Modeling

Non-linear Langmuir

Ce <- c(1, 2, 4, 6, 8, 10)
Qe <- c(0.9, 1.6, 2.3, 2.7, 2.9, 3.0)
result <- nonlinear_langmuir(Ce, Qe)
print(result$`Langmuir Qmax (mg/g)`)
#>     Qmax 
#> 4.000269
print(result$`Langmuir KL (L/mg)`)
#>        KL 
#> 0.3262891
print(result$AIC)
#> [1] -12.78784
print(result$`Pseudo R2`)
#> [1] 0.9955326
print(result$Plot)
#> `geom_smooth()` using formula = 'y ~ x'

Non-linear Freundlich

Ce <- c(0.5, 1, 2, 4, 6, 8)
Qe <- c(0.3, 0.8, 1.6, 2.4, 2.9, 3.2)
result <- nonlinear_freundlich(Ce, Qe)
print(result$`Freundlich Kf`)
#>        Kf 
#> 0.9214474
print(result$`Freundlich n`)
#>        n 
#> 1.598388
print(result$AIC)
#> [1] 3.105419
print(result$`Pseudo R2`)
#> [1] 0.9680789
print(result$Plot)
#> `geom_smooth()` using formula = 'y ~ x'
#> Warning in simpleLoess(y, x, w, span, degree = degree, parametric = parametric,
#> : Chernobyl! trL>n 6
#> Warning in simpleLoess(y, x, w, span, degree = degree, parametric = parametric,
#> : Chernobyl! trL>n 6
#> Warning in sqrt(sum.squares/one.delta): NaNs produced

Non-linear BET

Ce <- c(1, 2.5, 4, 5.5, 7)
Qe <- c(0.4, 1.0, 1.7, 2.3, 2.7)
result <- nonlinear_bet(Ce, Qe)
#> Warning in nls(Qe ~ (Qm * Cb * Ce)/((Cs - Ce) * (1 + (Cb - 1) * Ce/Cs)), : step
#> factor 5.82077e-11 reduced below 'minFactor' of 1e-10
print(result$`BET Qm (mg/g)`)
#>        Qm 
#> 0.3091857
print(result$`BET Cb`)
#>           Cb 
#> 6.875806e+18
print(result$AIC)
#> [1] 18.24174
print(result$`Pseudo R2`)
#> [1] 0.03452733
print(result$Plot)
#> `geom_smooth()` using formula = 'y ~ x'
#> Warning in simpleLoess(y, x, w, span, degree = degree, parametric = parametric,
#> : span too small.  fewer data values than degrees of freedom.
#> Warning in simpleLoess(y, x, w, span, degree = degree, parametric = parametric,
#> : pseudoinverse used at 0.97
#> Warning in simpleLoess(y, x, w, span, degree = degree, parametric = parametric,
#> : neighborhood radius 3.03
#> Warning in simpleLoess(y, x, w, span, degree = degree, parametric = parametric,
#> : reciprocal condition number 0
#> Warning in simpleLoess(y, x, w, span, degree = degree, parametric = parametric,
#> : There are other near singularities as well. 9.1809

Non-linear Temkin

Ce <- c(0.5, 1.5, 3, 4.5, 6)
Qe <- c(0.7, 1.3, 2.0, 2.4, 2.7)
result <- nonlinear_temkin(Ce, Qe)
print(result$`Temkin A`)
#>       AT 
#> 4.126122
print(result$`Temkin B`)
#>        bT 
#> 0.8129749
print(result$AIC)
#> [1] -2.230427
print(result$`Pseudo R2`)
#> [1] 0.9788445
print(result$Plot)
#> `geom_smooth()` using formula = 'y ~ x'
#> Warning in simpleLoess(y, x, w, span, degree = degree, parametric = parametric,
#> : span too small.  fewer data values than degrees of freedom.
#> Warning in simpleLoess(y, x, w, span, degree = degree, parametric = parametric,
#> : pseudoinverse used at 0.4725
#> Warning in simpleLoess(y, x, w, span, degree = degree, parametric = parametric,
#> : neighborhood radius 2.5275
#> Warning in simpleLoess(y, x, w, span, degree = degree, parametric = parametric,
#> : reciprocal condition number 0
#> Warning in simpleLoess(y, x, w, span, degree = degree, parametric = parametric,
#> : There are other near singularities as well. 9.1658

๐Ÿ“ Conclusion

The AdsorpR package offers a clean and structured way to fit adsorption isotherm models and visualize results using ggplot2. It is useful for: