Adsorption Energies#

Pre-trained ODAC models are versatile across various MOF-related tasks. To begin, we’ll start with a fundamental application: calculating the adsorption energy for a single CO2 molecule. This serves as an excellent and simple demonstration of what you can achieve with these datasets and models.

For predicting the adsorption energy of a single CO2 molecule within a MOF structure, the adsorption energy (Eads) is defined as:

(1)Eads=EMOF+CO2EMOFECO2

Each term on the right-hand side represents the energy of the relaxed state of the indicated chemical system. For a comprehensive understanding of our methodology for computing these adsorption energies, please refer to our paper.

Loading Pre-trained Models#

A pre-trained model can be loaded using FAIRChemCalculator. In this example, we’ll employ UMA to determine the CO2 adsorption energies.

from fairchem.core import FAIRChemCalculator, pretrained_mlip

predictor = pretrained_mlip.get_predict_unit("uma-s-1p1")
calc = FAIRChemCalculator(predictor, task_name="odac")
WARNING:root:device was not explicitly set, using device='cuda'.

Adsorption in rigid MOFs: CO2 Adsorption Energy in Mg-MOF-74#

Let’s apply our knowledge to Mg-MOF-74, a widely studied MOF known for its excellent CO2 adsorption properties. Its structure comprises magnesium atomic complexes connected by a carboxylated and oxidized benzene ring, serving as an organic linker. Previous studies consistently report the CO2 adsorption energy for Mg-MOF-74 to be around -0.40 eV [1] [2] [3].

Our goal is to verify if we can achieve a similar value by performing a simple single-point calculation using UMA. In the ODAC23 dataset, all MOF structures are identified by their CSD (Cambridge Structural Database) code. For Mg-MOF-74, this code is OPAGIX. We’ve extracted a specific OPAGIX+CO2 configuration from the dataset, which exhibits the lowest adsorption energy among its counterparts.

import matplotlib.pyplot as plt
from ase.io import read
from ase.visualize.plot import plot_atoms

mof_co2 = read("structures/OPAGIX_w_CO2.cif")
mof = read("structures/OPAGIX.cif")
co2 = read("structures/co2.xyz")

fig, ax = plt.subplots(figsize=(5, 4.5), dpi=250)
plot_atoms(mof_co2, ax)
ax.set_axis_off()
../../_images/1bc2dace0cab255f6fbec12f630170db9bcb837b8d23bf087fcfddfd8d5d110f.png

The final step in calculating the adsorption energy involves connecting the FAIRChemCalculator to each relaxed structure: OPAGIX+CO2, OPAGIX, and CO2. The structures used here are already relaxed from ODAC23. For simplicity, we assume here that further relaxations can be neglected. We will show how to go beyond this assumption in the next section.

mof_co2.calc = calc
mof.calc = calc
co2.calc = calc

E_ads = (
    mof_co2.get_potential_energy()
    - mof.get_potential_energy()
    - co2.get_potential_energy()
)

print(f"Adsorption energy of CO2 in Mg-MOF-74: {E_ads:.3f} eV")
Adsorption energy of CO2 in Mg-MOF-74: -0.459 eV

Adsorption in flexible MOFs#

The adsorption energy calculation method outlined above is typically performed with rigid MOFs for simplicity. Both experimental and modeling literature have shown, however, that MOF flexibility can be important in accurately capturing the underlying chemistry of adsorption [1] [2] [3]. In particular, uptake can be improved by treating MOFs as flexible. Two types of MOF flexibility can be considered: intrinsic flexibility and deformation induced by guest molecules. In the Open DAC Project, we consider the latter MOF deformation by allowing the atomic positions of the MOF to relax during geometry optimization [4]. The addition of additional degrees of freedoms can complicate the computation of the adsorption energy and necessitates an extra step in the calculation procedure.

The figure below shows water adsorption in the MOF with CSD code WOBHEB with added defects (WOBHEB_0.11_0) from a DFT simulation. A typical adsorption energy calculation would only seek to capture the effects shaded in purple, which include both chemisorption and non-bonded interactions between the host and guest molecule. When allowing the MOF to relax, however, the adsorption energy also includes the energetic effect of the MOF deformation highlighted in green.

To account for this deformation, it is vital to use the most energetically favorable MOF geometry for the empty MOF term in Eqn. 1. Including MOF atomic coordinates as degrees of freedom can result in three possible outcomes:

  1. The MOF does not deform, so the energies of the relaxed empty MOF and the MOF in the adsorbed state are the same

  2. The MOF deforms to a less energetically favorable geometry than its ground state

  3. The MOF locates a new energetically favorable geoemtry relative to the empty MOF relaxation

The first outcome requires no additional computation because the MOF rigidity assumption is valid. The second outcome represents physical and reversible deformation where the MOF returns to its empty ground state upon removal of the guest molecule. The third outcome is often the result of the guest molecule breaking local symmetry. We also found cases in ODAC in which both outcomes 2 and 3 occur within the same MOF.

To ensure the most energetically favorable empty MOF geometry is found, an addition empty MOF relaxation should be performed after MOF + adsorbate relaxation. The guest molecule should be removed, and the MOF should be relaxed starting from its geometry in the adsorbed state. If all deformation is reversible, the MOF will return to its original empty geometry. Otherwise, the lowest energy (most favorable) MOF geometry should be taken as the reference energy, EMOF, in Eqn. 1.

H2O Adsorption Energy in Flexible WOBHEB with UMA#

The first part of this tutorial demonstrates how to perform a single point adsorption energy calculation using UMA. To treat MOFs as flexible, we perform all calculations on geometries determined by geometry optimization. The following example corresponds to the figure shown above (H2O adsorption in WOBHEB_0.11_0).

In this tutorial, Ex(ry) corresponds to the energy of x determined from geometry optimization of y.

First, we obtain the energy of the empty MOF from relaxation of only the MOF: EMOF(rMOF)

import ase.io
from ase.optimize import BFGS

mof = ase.io.read("structures/WOBHEB_0.11.cif")
mof.calc = calc
relax = BFGS(mof)
relax.run(fmax=0.05)
E_mof_empty = mof.get_potential_energy()
print(f"Energy of empty MOF: {E_mof_empty:.3f} eV")
      Step     Time          Energy          fmax
BFGS:    0 01:39:47    -1077.274064        0.206406
BFGS:    1 01:39:47    -1077.276780        0.152729
BFGS:    2 01:39:48    -1077.281940        0.169926
BFGS:    3 01:39:48    -1077.284769        0.155780
BFGS:    4 01:39:48    -1077.288838        0.108779
BFGS:    5 01:39:48    -1077.291023        0.086441
BFGS:    6 01:39:49    -1077.293361        0.093435
BFGS:    7 01:39:49    -1077.295415        0.100095
BFGS:    8 01:39:49    -1077.297831        0.102518
BFGS:    9 01:39:49    -1077.300016        0.091608
BFGS:   10 01:39:50    -1077.302011        0.078980
BFGS:   11 01:39:50    -1077.304140        0.105576
BFGS:   12 01:39:50    -1077.306720        0.087929
BFGS:   13 01:39:50    -1077.309519        0.086369
BFGS:   14 01:39:51    -1077.312262        0.086852
BFGS:   15 01:39:51    -1077.314701        0.106260
BFGS:   16 01:39:51    -1077.316988        0.106196
BFGS:   17 01:39:51    -1077.319482        0.085481
BFGS:   18 01:39:52    -1077.322265        0.109655
BFGS:   19 01:39:52    -1077.325133        0.148706
BFGS:   20 01:39:52    -1077.327768        0.125920
BFGS:   21 01:39:52    -1077.329919        0.069115
BFGS:   22 01:39:52    -1077.331953        0.087283
BFGS:   23 01:39:53    -1077.334270        0.125252
BFGS:   24 01:39:53    -1077.336837        0.166678
BFGS:   25 01:39:53    -1077.339540        0.145561
BFGS:   26 01:39:53    -1077.342146        0.087615
BFGS:   27 01:39:54    -1077.344543        0.076205
BFGS:   28 01:39:54    -1077.346890        0.148949
BFGS:   29 01:39:54    -1077.349788        0.170234
BFGS:   30 01:39:54    -1077.352532        0.109299
BFGS:   31 01:39:55    -1077.354749        0.070312
BFGS:   32 01:39:55    -1077.356775        0.089657
BFGS:   33 01:39:55    -1077.358660        0.124320
BFGS:   34 01:39:55    -1077.360601        0.108063
BFGS:   35 01:39:56    -1077.362472        0.068663
BFGS:   36 01:39:56    -1077.364167        0.070245
BFGS:   37 01:39:56    -1077.365713        0.105391
BFGS:   38 01:39:56    -1077.367258        0.104402
BFGS:   39 01:39:57    -1077.368765        0.062637
BFGS:   40 01:39:57    -1077.370126        0.057785
BFGS:   41 01:39:57    -1077.371344        0.063997
BFGS:   42 01:39:57    -1077.372385        0.066973
BFGS:   43 01:39:58    -1077.373365        0.057745
BFGS:   44 01:39:58    -1077.374396        0.042126
Energy of empty MOF: -1077.374 eV

Next, we add the H2O guest molecule and relax the MOF + adsorbate to obtain EMOF+H2O(rMOF+H2O).

mof_h2o = ase.io.read("structures/WOBHEB_H2O.cif")
mof_h2o.calc = calc
relax = BFGS(mof_h2o)
relax.run(fmax=0.05)
E_combo = mof_h2o.get_potential_energy()
print(f"Energy of MOF + H2O: {E_combo:.3f} eV")
      Step     Time          Energy          fmax
BFGS:    0 01:39:58    -1091.565588        1.145035
BFGS:    1 01:39:59    -1091.585062        0.314149
BFGS:    2 01:39:59    -1091.590212        0.243429
BFGS:    3 01:39:59    -1091.608171        0.237254
BFGS:    4 01:39:59    -1091.614633        0.227932
BFGS:    5 01:40:00    -1091.625217        0.186813
BFGS:    6 01:40:00    -1091.632355        0.178921
BFGS:    7 01:40:00    -1091.640630        0.175038
BFGS:    8 01:40:00    -1091.648041        0.184528
BFGS:    9 01:40:01    -1091.656144        0.160912
BFGS:   10 01:40:01    -1091.663841        0.178473
BFGS:   11 01:40:01    -1091.672298        0.188756
BFGS:   12 01:40:01    -1091.682088        0.157566
BFGS:   13 01:40:02    -1091.692983        0.177231
BFGS:   14 01:40:02    -1091.704439        0.158181
BFGS:   15 01:40:02    -1091.715512        0.191696
BFGS:   16 01:40:02    -1091.725712        0.197831
BFGS:   17 01:40:03    -1091.735331        0.163695
BFGS:   18 01:40:03    -1091.745539        0.151478
BFGS:   19 01:40:03    -1091.754027        0.170794
BFGS:   20 01:40:03    -1091.761500        0.153326
BFGS:   21 01:40:04    -1091.767892        0.152617
BFGS:   22 01:40:04    -1091.774205        0.166014
BFGS:   23 01:40:04    -1091.780886        0.135356
BFGS:   24 01:40:05    -1091.788355        0.180989
BFGS:   25 01:40:05    -1091.794283        0.204480
BFGS:   26 01:40:05    -1091.800638        0.131343
BFGS:   27 01:40:05    -1091.806519        0.189936
BFGS:   28 01:40:06    -1091.812303        0.199082
BFGS:   29 01:40:06    -1091.817181        0.151631
BFGS:   30 01:40:06    -1091.822210        0.100074
BFGS:   31 01:40:06    -1091.826300        0.125182
BFGS:   32 01:40:07    -1091.832529        0.177259
BFGS:   33 01:40:07    -1091.837117        0.246105
BFGS:   34 01:40:07    -1091.842046        0.112816
BFGS:   35 01:40:07    -1091.845773        0.331133
BFGS:   36 01:40:08    -1091.850859        0.173914
BFGS:   37 01:40:08    -1091.858471        0.159588
BFGS:   38 01:40:08    -1091.865280        0.141787
BFGS:   39 01:40:08    -1091.872006        0.139607
BFGS:   40 01:40:09    -1091.878240        0.266114
BFGS:   41 01:40:09    -1091.879824        0.597780
BFGS:   42 01:40:09    -1091.887409        0.530006
BFGS:   43 01:40:09    -1091.893885        0.247738
BFGS:   44 01:40:10    -1091.900308        0.178915
BFGS:   45 01:40:10    -1091.915874        0.233623
BFGS:   46 01:40:10    -1091.924363        0.389300
BFGS:   47 01:40:10    -1091.937921        0.267920
BFGS:   48 01:40:11    -1091.953105        0.451334
BFGS:   49 01:40:11    -1091.972261        0.650966
BFGS:   50 01:40:11    -1091.965140        1.527650
BFGS:   51 01:40:11    -1092.005189        0.412145
BFGS:   52 01:40:12    -1092.020963        0.279931
BFGS:   53 01:40:12    -1092.069785        0.308777
BFGS:   54 01:40:12    -1092.085938        0.302366
BFGS:   55 01:40:12    -1092.118688        0.691811
BFGS:   56 01:40:13    -1092.123713        0.369363
BFGS:   57 01:40:13    -1092.139152        0.209754
BFGS:   58 01:40:13    -1092.151227        0.237076
BFGS:   59 01:40:14    -1092.168240        0.349448
BFGS:   60 01:40:14    -1092.178640        0.385496
BFGS:   61 01:40:14    -1092.189915        0.336816
BFGS:   62 01:40:14    -1092.200339        0.255397
BFGS:   63 01:40:15    -1092.213032        0.183374
BFGS:   64 01:40:15    -1092.222258        0.193552
BFGS:   65 01:40:15    -1092.230834        0.158116
BFGS:   66 01:40:15    -1092.237093        0.104141
BFGS:   67 01:40:16    -1092.242582        0.112126
BFGS:   68 01:40:16    -1092.248284        0.143935
BFGS:   69 01:40:16    -1092.253348        0.149438
BFGS:   70 01:40:16    -1092.258586        0.116323
BFGS:   71 01:40:17    -1092.263462        0.101921
BFGS:   72 01:40:17    -1092.267491        0.133185
BFGS:   73 01:40:17    -1092.271215        0.149521
BFGS:   74 01:40:17    -1092.274783        0.132079
BFGS:   75 01:40:18    -1092.278109        0.111358
BFGS:   76 01:40:18    -1092.281503        0.096331
BFGS:   77 01:40:18    -1092.284606        0.094698
BFGS:   78 01:40:18    -1092.287030        0.095055
BFGS:   79 01:40:19    -1092.289597        0.079531
BFGS:   80 01:40:19    -1092.291638        0.071188
BFGS:   81 01:40:19    -1092.293462        0.072202
BFGS:   82 01:40:19    -1092.294991        0.080777
BFGS:   83 01:40:20    -1092.296340        0.081011
BFGS:   84 01:40:20    -1092.297769        0.076610
BFGS:   85 01:40:20    -1092.299244        0.082901
BFGS:   86 01:40:20    -1092.301079        0.071464
BFGS:   87 01:40:21    -1092.302674        0.061577
BFGS:   88 01:40:21    -1092.304527        0.056023
BFGS:   89 01:40:21    -1092.306063        0.064720
BFGS:   90 01:40:21    -1092.307444        0.054114
BFGS:   91 01:40:22    -1092.308748        0.053171
BFGS:   92 01:40:22    -1092.309763        0.061913
BFGS:   93 01:40:22    -1092.310655        0.061019
BFGS:   94 01:40:23    -1092.311391        0.054056
BFGS:   95 01:40:23    -1092.312157        0.044976
Energy of MOF + H2O: -1092.312 eV

We can now isolate the MOF atoms from the relaxed MOF + H2O geometry and see that the MOF has adopted a geometry that is less energetically favorable than the empty MOF by ~0.2 eV. The energy of the MOF in the adsorbed state corresponds to EMOF(rMOF+H2O).

mof_adsorbed_state = mof_h2o[:-3]
mof_adsorbed_state.calc = calc
E_mof_adsorbed_state = mof_adsorbed_state.get_potential_energy()
print(f"Energy of MOF in the adsorbed state: {E_mof_adsorbed_state:.3f} eV")
Energy of MOF in the adsorbed state: -1077.091 eV

H2O adsorption in this MOF appears to correspond to Case #2 as outlined above. We can now perform re-relaxation of the empty MOF starting from the rMOF+H2O geometry.

relax = BFGS(mof_adsorbed_state)
relax.run(fmax=0.05)
E_mof_rerelax = mof_adsorbed_state.get_potential_energy()
print(f"Energy of re-relaxed empty MOF: {E_mof_rerelax:.3f} eV")
      Step     Time          Energy          fmax
BFGS:    0 01:40:23    -1077.090842        0.986442
BFGS:    1 01:40:23    -1077.123006        0.873281
BFGS:    2 01:40:24    -1077.172142        0.818488
BFGS:    3 01:40:24    -1077.210928        0.524014
BFGS:    4 01:40:24    -1077.230435        0.438096
BFGS:    5 01:40:24    -1077.246901        0.281661
BFGS:    6 01:40:25    -1077.257650        0.260293
BFGS:    7 01:40:25    -1077.266931        0.248066
BFGS:    8 01:40:25    -1077.276498        0.223251
BFGS:    9 01:40:25    -1077.283446        0.156257
BFGS:   10 01:40:26    -1077.288391        0.140787
BFGS:   11 01:40:26    -1077.292383        0.135912
BFGS:   12 01:40:26    -1077.295841        0.155181
BFGS:   13 01:40:26    -1077.300817        0.165285
BFGS:   14 01:40:27    -1077.305075        0.147316
BFGS:   15 01:40:27    -1077.309342        0.132313
BFGS:   16 01:40:27    -1077.313278        0.159774
BFGS:   17 01:40:27    -1077.317558        0.162286
BFGS:   18 01:40:28    -1077.322187        0.150076
BFGS:   19 01:40:28    -1077.325886        0.124297
BFGS:   20 01:40:28    -1077.328480        0.117647
BFGS:   21 01:40:28    -1077.330847        0.112662
BFGS:   22 01:40:29    -1077.333412        0.120923
BFGS:   23 01:40:29    -1077.336332        0.113709
BFGS:   24 01:40:29    -1077.339196        0.097602
BFGS:   25 01:40:29    -1077.341490        0.088436
BFGS:   26 01:40:30    -1077.343554        0.081498
BFGS:   27 01:40:30    -1077.345213        0.071598
BFGS:   28 01:40:30    -1077.347019        0.083949
BFGS:   29 01:40:30    -1077.348591        0.100711
BFGS:   30 01:40:31    -1077.350343        0.077874
BFGS:   31 01:40:31    -1077.351663        0.052155
BFGS:   32 01:40:31    -1077.352979        0.061598
BFGS:   33 01:40:31    -1077.354324        0.079844
BFGS:   34 01:40:32    -1077.355739        0.085233
BFGS:   35 01:40:32    -1077.357378        0.074236
BFGS:   36 01:40:32    -1077.358918        0.069354
BFGS:   37 01:40:32    -1077.360228        0.072352
BFGS:   38 01:40:33    -1077.361457        0.074052
BFGS:   39 01:40:33    -1077.362876        0.087442
BFGS:   40 01:40:33    -1077.364028        0.069788
BFGS:   41 01:40:33    -1077.365151        0.052757
BFGS:   42 01:40:34    -1077.366261        0.063634
BFGS:   43 01:40:34    -1077.367399        0.068825
BFGS:   44 01:40:34    -1077.368753        0.079137
BFGS:   45 01:40:34    -1077.370054        0.048170
Energy of re-relaxed empty MOF: -1077.370 eV

The MOF returns to its original empty reference energy upon re-relaxation, confirming that this deformation is physically relevant and is induced by the adsorbate molecule. In Case #3, this re-relaxed energy will be more negative (more favorable) than the original empty MOF relaxation. Thus, we take the reference empty MOF energy (EMOF in Eqn. 1) to be the minimum of the original empty MOF energy and the re-relaxed MOf energy:

E_mof = min(E_mof_empty, E_mof_rerelax)

# get adsorbate reference energy
h2o = mof_h2o[-3:]
h2o.calc = calc
E_h2o = h2o.get_potential_energy()

# compute adsorption energy
E_ads = E_combo - E_mof - E_h2o
print(f"Adsorption energy of H2O in WOBHEB_0.11_0: {E_ads:.3f} eV")
Adsorption energy of H2O in WOBHEB_0.11_0: -0.689 eV

This adsorption energy closely matches that from DFT (–0.699 eV) [1]. The strong adsorption energy is a consequence of both H2O chemisorption and MOF deformation. We can decompose the adsorption energy into contributions from these two factors. Assuming rigid H2O molecules, we define Eint and EMOF,deform, respectively, as

(2)Eint=EMOF+H2O(rMOF+H2O)EMOF(rMOF+H2O)EH2O(rMOF+H2O)
(3)EMOF,deform=EMOF(rMOF+H2O)EMOF(rMOF)

Eint describes host host–guest interactions for the MOF in the adsorbed state only. EMOF,deform quantifies the magnitude of deformation between the MOF in the adsorbed state and the most energetically favorable empty MOF geometry determined from the workflow presented here. It can be shown that

(4)Eads=Eint+EMOF,deform

For H2O adsorption in WOBHEB_0.11, we have

E_int = E_combo - E_mof_adsorbed_state - E_h2o
print(f"E_int: {E_int}")
E_int: -0.9726162552831692
E_mof_deform = E_mof_adsorbed_state - E_mof_empty
print(f"E_mof_deform: {E_mof_deform}")
E_mof_deform: 0.2835540771484375
E_ads = E_int + E_mof_deform
print(f"E_ads: {E_ads}")
E_ads: -0.6890621781347317

Eint is equivalent to Eads when the MOF is assumed to be rigid. In this case, failure to consider adsorbate-induced deformation would result in an overestimation of the adsorption energy magnitude.

Acknowledgements & Authors#

Logan Brabson and Sihoon Choi (Georgia Tech) and the OpenDAC project.