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 (\(E_{\mathrm{ads}}\)) is defined as:

\[ E_{\mathrm{ads}} = E_{\mathrm{MOF+CO2}} - E_{\mathrm{MOF}} - E_{\mathrm{CO2}} \tag{1}\]

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/22c07917ac2a3c80689e2f0f17ca387429cde669faecbba218569be0d505a678.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, \(E_{\mathrm{MOF}}\), 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, \(E_{x}(r_{y})\) 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: \(E_{\mathrm{MOF}}(r_{\mathrm{MOF}})\)

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 23:46:40    -1077.274064        0.206406
BFGS:    1 23:46:40    -1077.276781        0.152729
BFGS:    2 23:46:41    -1077.281940        0.169932
BFGS:    3 23:46:41    -1077.284770        0.155752
BFGS:    4 23:46:41    -1077.288840        0.108767
BFGS:    5 23:46:41    -1077.291021        0.086426
BFGS:    6 23:46:42    -1077.293363        0.093423
BFGS:    7 23:46:42    -1077.295416        0.100158
BFGS:    8 23:46:42    -1077.297829        0.102529
BFGS:    9 23:46:42    -1077.300019        0.091597
BFGS:   10 23:46:43    -1077.302008        0.079056
BFGS:   11 23:46:43    -1077.304135        0.105575
BFGS:   12 23:46:43    -1077.306722        0.087926
BFGS:   13 23:46:43    -1077.309517        0.086351
BFGS:   14 23:46:43    -1077.312260        0.086877
BFGS:   15 23:46:44    -1077.314698        0.106261
BFGS:   16 23:46:44    -1077.316991        0.106186
BFGS:   17 23:46:44    -1077.319481        0.085492
BFGS:   18 23:46:44    -1077.322264        0.109640
BFGS:   19 23:46:45    -1077.325136        0.148660
BFGS:   20 23:46:45    -1077.327767        0.125908
BFGS:   21 23:46:45    -1077.329922        0.069111
BFGS:   22 23:46:45    -1077.331952        0.087275
BFGS:   23 23:46:45    -1077.334273        0.125234
BFGS:   24 23:46:46    -1077.336836        0.166665
BFGS:   25 23:46:46    -1077.339542        0.145552
BFGS:   26 23:46:46    -1077.342150        0.087668
BFGS:   27 23:46:46    -1077.344543        0.076199
BFGS:   28 23:46:47    -1077.346893        0.148936
BFGS:   29 23:46:47    -1077.349787        0.170250
BFGS:   30 23:46:47    -1077.352527        0.109283
BFGS:   31 23:46:47    -1077.354749        0.070359
BFGS:   32 23:46:48    -1077.356777        0.089706
BFGS:   33 23:46:48    -1077.358658        0.124283
BFGS:   34 23:46:48    -1077.360599        0.108061
BFGS:   35 23:46:48    -1077.362474        0.068557
BFGS:   36 23:46:48    -1077.364165        0.070179
BFGS:   37 23:46:49    -1077.365715        0.105489
BFGS:   38 23:46:49    -1077.367264        0.104138
BFGS:   39 23:46:49    -1077.368768        0.062953
BFGS:   40 23:46:49    -1077.370141        0.058087
BFGS:   41 23:46:50    -1077.371381        0.059231
BFGS:   42 23:46:50    -1077.372479        0.063188
BFGS:   43 23:46:50    -1077.373496        0.055415
BFGS:   44 23:46:50    -1077.374462        0.050243
BFGS:   45 23:46:51    -1077.375325        0.049323
Energy of empty MOF: -1077.375 eV

Next, we add the H2O guest molecule and relax the MOF + adsorbate to obtain \(E_{\mathrm{MOF+H2O}}(r_{\mathrm{MOF+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 23:46:51    -1091.565590        1.145035
BFGS:    1 23:46:51    -1091.585063        0.314149
BFGS:    2 23:46:51    -1091.590209        0.243429
BFGS:    3 23:46:52    -1091.608171        0.237244
BFGS:    4 23:46:52    -1091.614633        0.227934
BFGS:    5 23:46:52    -1091.625220        0.186791
BFGS:    6 23:46:52    -1091.632353        0.178915
BFGS:    7 23:46:53    -1091.640634        0.175116
BFGS:    8 23:46:53    -1091.648042        0.184548
BFGS:    9 23:46:53    -1091.656148        0.160865
BFGS:   10 23:46:53    -1091.663842        0.178489
BFGS:   11 23:46:53    -1091.672295        0.188741
BFGS:   12 23:46:54    -1091.682092        0.157477
BFGS:   13 23:46:54    -1091.692980        0.177288
BFGS:   14 23:46:54    -1091.704437        0.158157
BFGS:   15 23:46:54    -1091.715514        0.191638
BFGS:   16 23:46:55    -1091.725713        0.197806
BFGS:   17 23:46:55    -1091.735328        0.163717
BFGS:   18 23:46:55    -1091.745539        0.151477
BFGS:   19 23:46:55    -1091.754018        0.170813
BFGS:   20 23:46:56    -1091.761496        0.153756
BFGS:   21 23:46:56    -1091.767893        0.152688
BFGS:   22 23:46:56    -1091.774207        0.165916
BFGS:   23 23:46:56    -1091.780886        0.135424
BFGS:   24 23:46:57    -1091.788355        0.180997
BFGS:   25 23:46:57    -1091.794279        0.204388
BFGS:   26 23:46:57    -1091.800638        0.131335
BFGS:   27 23:46:57    -1091.806521        0.189935
BFGS:   28 23:46:57    -1091.812304        0.199238
BFGS:   29 23:46:58    -1091.817179        0.151748
BFGS:   30 23:46:58    -1091.822213        0.100106
BFGS:   31 23:46:58    -1091.826302        0.125235
BFGS:   32 23:46:58    -1091.832528        0.177245
BFGS:   33 23:46:59    -1091.837119        0.246157
BFGS:   34 23:46:59    -1091.842042        0.112780
BFGS:   35 23:46:59    -1091.845766        0.331919
BFGS:   36 23:46:59    -1091.850842        0.173766
BFGS:   37 23:47:00    -1091.858469        0.159533
BFGS:   38 23:47:00    -1091.865270        0.141728
BFGS:   39 23:47:00    -1091.872005        0.139146
BFGS:   40 23:47:00    -1091.878214        0.269879
BFGS:   41 23:47:01    -1091.879683        0.601867
BFGS:   42 23:47:01    -1091.887585        0.510347
BFGS:   43 23:47:01    -1091.893892        0.252370
BFGS:   44 23:47:01    -1091.900512        0.184841
BFGS:   45 23:47:02    -1091.915679        0.238803
BFGS:   46 23:47:02    -1091.924291        0.383538
BFGS:   47 23:47:02    -1091.937842        0.259535
BFGS:   48 23:47:02    -1091.952532        0.492882
BFGS:   49 23:47:02    -1091.971463        0.616047
BFGS:   50 23:47:03    -1091.976817        1.201520
BFGS:   51 23:47:03    -1092.007659        0.488919
BFGS:   52 23:47:03    -1092.026344        0.283448
BFGS:   53 23:47:03    -1092.070273        0.309760
BFGS:   54 23:47:04    -1092.086735        0.302988
BFGS:   55 23:47:04    -1092.114123        0.888135
BFGS:   56 23:47:04    -1092.127154        0.282823
BFGS:   57 23:47:04    -1092.138934        0.206809
BFGS:   58 23:47:05    -1092.154734        0.184987
BFGS:   59 23:47:05    -1092.169102        0.304657
BFGS:   60 23:47:05    -1092.179580        0.402925
BFGS:   61 23:47:05    -1092.190850        0.430888
BFGS:   62 23:47:06    -1092.202307        0.355534
BFGS:   63 23:47:06    -1092.213451        0.234739
BFGS:   64 23:47:06    -1092.223473        0.189225
BFGS:   65 23:47:06    -1092.231304        0.149308
BFGS:   66 23:47:06    -1092.237606        0.105289
BFGS:   67 23:47:07    -1092.243040        0.109119
BFGS:   68 23:47:07    -1092.248817        0.152045
BFGS:   69 23:47:07    -1092.253777        0.160793
BFGS:   70 23:47:07    -1092.259102        0.110961
BFGS:   71 23:47:08    -1092.263805        0.083820
BFGS:   72 23:47:08    -1092.267843        0.142819
BFGS:   73 23:47:08    -1092.271532        0.183711
BFGS:   74 23:47:08    -1092.275050        0.172322
BFGS:   75 23:47:09    -1092.278347        0.130603
BFGS:   76 23:47:09    -1092.281901        0.082327
BFGS:   77 23:47:09    -1092.284789        0.096476
BFGS:   78 23:47:09    -1092.287319        0.098089
BFGS:   79 23:47:10    -1092.289812        0.078816
BFGS:   80 23:47:10    -1092.291811        0.069876
BFGS:   81 23:47:10    -1092.293648        0.074598
BFGS:   82 23:47:10    -1092.295105        0.078925
BFGS:   83 23:47:10    -1092.296487        0.076115
BFGS:   84 23:47:11    -1092.297891        0.078786
BFGS:   85 23:47:11    -1092.299430        0.089542
BFGS:   86 23:47:11    -1092.301206        0.090174
BFGS:   87 23:47:11    -1092.302847        0.071311
BFGS:   88 23:47:12    -1092.304671        0.054187
BFGS:   89 23:47:12    -1092.306183        0.055911
BFGS:   90 23:47:12    -1092.307569        0.050963
BFGS:   91 23:47:12    -1092.308826        0.063236
BFGS:   92 23:47:13    -1092.309850        0.059955
BFGS:   93 23:47:13    -1092.310732        0.056242
BFGS:   94 23:47:13    -1092.311461        0.045677
Energy of MOF + H2O: -1092.311 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 \(E_{\mathrm{MOF}}(r_{\mathrm{MOF+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.090 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 \(r_{\mathrm{MOF+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 23:47:13    -1077.089691        0.987452
BFGS:    1 23:47:14    -1077.122219        0.874455
BFGS:    2 23:47:14    -1077.171948        0.830767
BFGS:    3 23:47:14    -1077.211110        0.540226
BFGS:    4 23:47:14    -1077.230776        0.437337
BFGS:    5 23:47:14    -1077.247092        0.285886
BFGS:    6 23:47:15    -1077.258055        0.260401
BFGS:    7 23:47:15    -1077.267414        0.247190
BFGS:    8 23:47:15    -1077.277168        0.220971
BFGS:    9 23:47:15    -1077.284057        0.157169
BFGS:   10 23:47:16    -1077.289013        0.142605
BFGS:   11 23:47:16    -1077.292994        0.144450
BFGS:   12 23:47:16    -1077.296491        0.154548
BFGS:   13 23:47:16    -1077.301512        0.168464
BFGS:   14 23:47:17    -1077.305746        0.148359
BFGS:   15 23:47:17    -1077.310046        0.137003
BFGS:   16 23:47:17    -1077.313998        0.158301
BFGS:   17 23:47:17    -1077.318335        0.164884
BFGS:   18 23:47:17    -1077.322997        0.147651
BFGS:   19 23:47:18    -1077.326634        0.123317
BFGS:   20 23:47:18    -1077.329182        0.116999
BFGS:   21 23:47:18    -1077.331529        0.113318
BFGS:   22 23:47:18    -1077.334076        0.122868
BFGS:   23 23:47:19    -1077.337007        0.116741
BFGS:   24 23:47:19    -1077.339778        0.098590
BFGS:   25 23:47:19    -1077.342085        0.086583
BFGS:   26 23:47:19    -1077.344124        0.083393
BFGS:   27 23:47:20    -1077.345780        0.072329
BFGS:   28 23:47:20    -1077.347546        0.080956
BFGS:   29 23:47:20    -1077.349094        0.099389
BFGS:   30 23:47:20    -1077.350842        0.079599
BFGS:   31 23:47:20    -1077.352117        0.053062
BFGS:   32 23:47:21    -1077.353434        0.063914
BFGS:   33 23:47:21    -1077.354743        0.079471
BFGS:   34 23:47:21    -1077.356176        0.088812
BFGS:   35 23:47:21    -1077.357836        0.075542
BFGS:   36 23:47:22    -1077.359345        0.072078
BFGS:   37 23:47:22    -1077.360601        0.073528
BFGS:   38 23:47:22    -1077.361828        0.070166
BFGS:   39 23:47:22    -1077.363282        0.086863
BFGS:   40 23:47:23    -1077.364411        0.071227
BFGS:   41 23:47:23    -1077.365529        0.050467
BFGS:   42 23:47:23    -1077.366587        0.064051
BFGS:   43 23:47:23    -1077.367721        0.069896
BFGS:   44 23:47:24    -1077.369077        0.083081
BFGS:   45 23:47:24    -1077.370354        0.051994
BFGS:   46 23:47:24    -1077.371362        0.047152
Energy of re-relaxed empty MOF: -1077.371 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 (\(E_{\mathrm{MOF}}\) 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.688 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 \(E_{\mathrm{int}}\) and \(E_{\mathrm{MOF,deform}}\), respectively, as

\[ E_{\mathrm{int}} = E_{\mathrm{MOF+H2O}}(r_{\mathrm{MOF+H2O}}) - E_{\mathrm{MOF}}(r_{\mathrm{MOF+H2O}}) - E_{\mathrm{H2O}}(r_{\mathrm{MOF+H2O}}) \tag{2}\]
\[ E_{\mathrm{MOF,deform}} = E_{\mathrm{MOF}}(r_{\mathrm{MOF+H2O}}) - E_{\mathrm{MOF}}(r_{\mathrm{MOF}}) \tag{3}\]

\(E_{\mathrm{int}}\) describes host host–guest interactions for the MOF in the adsorbed state only. \(E_{\mathrm{MOF,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

\[ E_{\mathrm{ads}} = E_{\mathrm{int}} + E_{\mathrm{MOF,deform}} \tag{4}\]

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.9734314680095348
E_mof_deform = E_mof_adsorbed_state - E_mof_empty
print(f"E_mof_deform: {E_mof_deform}")
E_mof_deform: 0.28563404083251953
E_ads = E_int + E_mof_deform
print(f"E_ads: {E_ads}")
E_ads: -0.6877974271770153

\(E_{\mathrm{int}}\) is equivalent to \(E_{\mathrm{ads}}\) 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.