AdsorbML tutorial#
The AdsorbML paper showed that pre-trained machine learning potentials were now viable to find and prioritize the best adsorption sites for a given surface. The results were quite impressive, especially if you were willing to do a DFT single-point calculation on the best calculations.
The latest UMA models are now total-energy models, and the results for the adsorption energy are even more impressive (see the paper for details and benchmarks). The AdsorbML package helps you with automated multi-adsorbate placement, and will automatically run calculations using the ML models to find the best sites to sample.
Define desired adsorbate+slab system#
from __future__ import annotations
import pandas as pd
from fairchem.data.oc.core import Adsorbate, Bulk, Slab
bulk_src_id = "mp-30"
adsorbate_smiles = "*CO"
bulk = Bulk(bulk_src_id_from_db=bulk_src_id)
adsorbate = Adsorbate(adsorbate_smiles_from_db=adsorbate_smiles)
slabs = Slab.from_bulk_get_specific_millers(bulk=bulk, specific_millers=(1, 1, 1))
# There may be multiple slabs with this miller index.
# For demonstrative purposes we will take the first entry.
slab = slabs[0]
/home/runner/work/_tool/Python/3.12.11/x64/lib/python3.12/site-packages/torchtnt/utils/version.py:12: UserWarning: pkg_resources is deprecated as an API. See https://setuptools.pypa.io/en/latest/pkg_resources.html. The pkg_resources package is slated for removal as early as 2025-11-30. Refrain from using this package or pin to Setuptools<81.
import pkg_resources
Downloading /home/runner/work/fairchem/fairchem/src/fairchem/data/oc/databases/pkls/bulks.pkl...
Run heuristic/random adsorbate placement and ML relaxations#
Now that we’ve defined the bulk, slab, and adsorbates of interest, we can quickly use the pre-trained UMA model as a calculator and the helper script fairchem.core.components.calculate.recipes.adsorbml.run_adsorbml
. More details on the automated pipeline can be found at https://github.com/facebookresearch/fairchem/blob/main/src/fairchem/core/components/calculate/recipes/adsorbml.py#L316.
from ase.optimize import LBFGS
from fairchem.core import FAIRChemCalculator, pretrained_mlip
from fairchem.core.components.calculate.recipes.adsorbml import run_adsorbml
predictor = pretrained_mlip.get_predict_unit("uma-s-1")
calc = FAIRChemCalculator(predictor, task_name="oc20")
outputs = run_adsorbml(
slab=slab,
adsorbate=adsorbate,
calculator=calc,
optimizer_cls=LBFGS,
fmax=0.02,
steps=20, # Increase to 200 for practical application, 20 is used for demonstrations
num_placements=10, # Increase to 100 for practical application, 10 is used for demonstrations
reference_ml_energies=True, # True if using a total energy model (i.e. UMA)
relaxed_slab_atoms=None,
place_on_relaxed_slab=False,
)
WARNING:root:device was not explicitly set, using device='cuda'.
Step Time Energy fmax
LBFGS: 0 14:41:55 -300.187736 0.048987
LBFGS: 1 14:41:55 -300.188278 0.046745
LBFGS: 2 14:41:56 -300.194191 0.005680
/home/runner/work/fairchem/fairchem/src/fairchem/data/oc/core/adsorbate.py:89: UserWarning: An adsorbate with that SMILES string was not found. Choosing one at random instead.
warnings.warn(
Step Time Energy fmax
LBFGS: 0 14:41:56 -328.984726 2.953960
LBFGS: 1 14:41:56 -329.236283 1.465739
LBFGS: 2 14:41:56 -329.368321 0.975025
LBFGS: 3 14:41:57 -329.411925 0.865682
LBFGS: 4 14:41:57 -329.484347 0.709034
LBFGS: 5 14:41:57 -329.510096 0.527276
LBFGS: 6 14:41:57 -329.522002 0.364701
LBFGS: 7 14:41:57 -329.533896 0.409464
LBFGS: 8 14:41:58 -329.551823 0.613544
LBFGS: 9 14:41:58 -329.578272 0.635087
LBFGS: 10 14:41:58 -329.601906 0.540919
LBFGS: 11 14:41:58 -329.624724 0.598157
LBFGS: 12 14:41:58 -329.656123 0.557765
LBFGS: 13 14:41:59 -329.698384 0.891972
LBFGS: 14 14:41:59 -329.748229 1.073947
LBFGS: 15 14:41:59 -329.798472 0.871521
LBFGS: 16 14:41:59 -329.833901 0.410417
LBFGS: 17 14:41:59 -329.854884 0.495821
LBFGS: 18 14:42:00 -329.888215 0.747302
LBFGS: 19 14:42:00 -329.929125 0.870695
LBFGS: 20 14:42:00 -329.956610 0.545014
Step Time Energy fmax
LBFGS: 0 14:42:00 -328.837575 2.988140
LBFGS: 1 14:42:00 -329.102131 1.448323
LBFGS: 2 14:42:01 -329.271563 1.138231
LBFGS: 3 14:42:01 -329.324202 1.035577
LBFGS: 4 14:42:01 -329.416979 0.755249
LBFGS: 5 14:42:01 -329.444867 0.464499
LBFGS: 6 14:42:01 -329.459904 0.476710
LBFGS: 7 14:42:02 -329.481162 0.503661
LBFGS: 8 14:42:02 -329.508284 0.711561
LBFGS: 9 14:42:02 -329.538130 0.614075
LBFGS: 10 14:42:02 -329.560936 0.592175
LBFGS: 11 14:42:02 -329.591586 0.754416
LBFGS: 12 14:42:03 -329.639134 0.837055
LBFGS: 13 14:42:03 -329.704163 0.958753
LBFGS: 14 14:42:03 -329.784205 1.102113
LBFGS: 15 14:42:03 -329.863705 0.750931
LBFGS: 16 14:42:03 -329.912833 0.455514
LBFGS: 17 14:42:04 -329.939454 0.465982
LBFGS: 18 14:42:04 -329.967389 0.400290
LBFGS: 19 14:42:04 -329.982033 0.904934
LBFGS: 20 14:42:04 -329.992787 0.561242
Step Time Energy fmax
LBFGS: 0 14:42:04 -329.232621 2.785361
LBFGS: 1 14:42:05 -329.434826 1.426486
LBFGS: 2 14:42:05 -329.552573 0.935652
LBFGS: 3 14:42:05 -329.587481 0.870920
LBFGS: 4 14:42:05 -329.644984 0.459739
LBFGS: 5 14:42:05 -329.659258 0.337845
LBFGS: 6 14:42:06 -329.667714 0.365911
LBFGS: 7 14:42:06 -329.678448 0.353777
LBFGS: 8 14:42:06 -329.695450 0.543573
LBFGS: 9 14:42:06 -329.713511 0.500583
LBFGS: 10 14:42:06 -329.728119 0.442552
LBFGS: 11 14:42:07 -329.745278 0.531791
LBFGS: 12 14:42:07 -329.770920 0.588909
LBFGS: 13 14:42:07 -329.804667 0.693584
LBFGS: 14 14:42:07 -329.843125 0.743754
LBFGS: 15 14:42:08 -329.877799 0.451531
LBFGS: 16 14:42:08 -329.900715 0.311983
LBFGS: 17 14:42:08 -329.922461 0.458246
LBFGS: 18 14:42:08 -329.954024 0.591158
LBFGS: 19 14:42:08 -329.979840 0.437327
LBFGS: 20 14:42:09 -329.994582 0.345022
Step Time Energy fmax
LBFGS: 0 14:42:09 -329.181641 2.745973
LBFGS: 1 14:42:09 -329.400376 1.341007
LBFGS: 2 14:42:09 -329.519526 0.929938
LBFGS: 3 14:42:09 -329.551396 0.839111
LBFGS: 4 14:42:10 -329.609389 0.617686
LBFGS: 5 14:42:10 -329.624665 0.401470
LBFGS: 6 14:42:10 -329.633763 0.342216
LBFGS: 7 14:42:10 -329.645676 0.410204
LBFGS: 8 14:42:10 -329.661955 0.493729
LBFGS: 9 14:42:11 -329.677849 0.360942
LBFGS: 10 14:42:11 -329.690508 0.423476
LBFGS: 11 14:42:11 -329.706881 0.527181
LBFGS: 12 14:42:11 -329.733443 0.587378
LBFGS: 13 14:42:11 -329.772357 0.836412
LBFGS: 14 14:42:12 -329.820515 0.854184
LBFGS: 15 14:42:12 -329.867676 0.516315
LBFGS: 16 14:42:12 -329.896792 0.297710
LBFGS: 17 14:42:12 -329.918290 0.407303
LBFGS: 18 14:42:12 -329.949523 0.529418
LBFGS: 19 14:42:13 -329.978056 0.485797
LBFGS: 20 14:42:13 -329.997733 0.435520
Step Time Energy fmax
LBFGS: 0 14:42:13 -329.183528 2.526200
LBFGS: 1 14:42:13 -329.396344 1.305542
LBFGS: 2 14:42:13 -329.518508 0.967263
LBFGS: 3 14:42:14 -329.555483 0.843603
LBFGS: 4 14:42:14 -329.622084 0.715370
LBFGS: 5 14:42:14 -329.641148 0.471000
LBFGS: 6 14:42:14 -329.652819 0.333921
LBFGS: 7 14:42:14 -329.667328 0.421909
LBFGS: 8 14:42:15 -329.689393 0.594303
LBFGS: 9 14:42:15 -329.713381 0.513935
LBFGS: 10 14:42:15 -329.731850 0.548616
LBFGS: 11 14:42:15 -329.755326 0.665698
LBFGS: 12 14:42:15 -329.790093 0.712151
LBFGS: 13 14:42:16 -329.837222 0.841103
LBFGS: 14 14:42:16 -329.895533 0.900734
LBFGS: 15 14:42:16 -329.952967 0.574750
LBFGS: 16 14:42:16 -329.983800 0.271991
LBFGS: 17 14:42:16 -329.999795 0.291566
LBFGS: 18 14:42:17 -330.019351 0.338948
LBFGS: 19 14:42:17 -330.042021 0.687644
LBFGS: 20 14:42:17 -330.052216 0.565042
Step Time Energy fmax
LBFGS: 0 14:42:17 -329.107939 2.569486
LBFGS: 1 14:42:17 -329.339272 1.265316
LBFGS: 2 14:42:18 -329.466763 1.088365
LBFGS: 3 14:42:18 -329.512549 0.987016
LBFGS: 4 14:42:18 -329.597621 0.957663
LBFGS: 5 14:42:18 -329.639290 0.660149
LBFGS: 6 14:42:18 -329.661442 0.505564
LBFGS: 7 14:42:19 -329.689051 0.572717
LBFGS: 8 14:42:19 -329.731663 0.705913
LBFGS: 9 14:42:19 -329.782105 0.740837
LBFGS: 10 14:42:19 -329.820109 0.624037
LBFGS: 11 14:42:19 -329.864001 0.792345
LBFGS: 12 14:42:20 -329.905419 0.855008
LBFGS: 13 14:42:20 -329.962422 0.842008
LBFGS: 14 14:42:20 -330.023667 0.867487
LBFGS: 15 14:42:20 -330.078959 0.615979
LBFGS: 16 14:42:20 -330.103653 0.426098
LBFGS: 17 14:42:21 -330.108267 0.472993
LBFGS: 18 14:42:21 -330.119996 0.280182
LBFGS: 19 14:42:21 -330.127625 0.199836
LBFGS: 20 14:42:21 -330.143982 0.215881
Step Time Energy fmax
LBFGS: 0 14:42:21 -328.707255 3.175000
LBFGS: 1 14:42:22 -328.962889 1.490763
LBFGS: 2 14:42:22 -329.127276 1.072373
LBFGS: 3 14:42:22 -329.187443 1.160827
LBFGS: 4 14:42:22 -329.280058 0.885009
LBFGS: 5 14:42:22 -329.329108 0.519729
LBFGS: 6 14:42:23 -329.348326 0.409967
LBFGS: 7 14:42:23 -329.366932 0.424906
LBFGS: 8 14:42:23 -329.399401 0.800162
LBFGS: 9 14:42:23 -329.447159 0.988421
LBFGS: 10 14:42:23 -329.494025 0.718776
LBFGS: 11 14:42:24 -329.531621 0.687195
LBFGS: 12 14:42:24 -329.582991 0.681038
LBFGS: 13 14:42:24 -329.646903 1.014703
LBFGS: 14 14:42:24 -329.722218 1.204474
LBFGS: 15 14:42:24 -329.804854 1.007227
LBFGS: 16 14:42:25 -329.873011 0.506048
LBFGS: 17 14:42:25 -329.909091 0.561189
LBFGS: 18 14:42:25 -329.950247 0.793869
LBFGS: 19 14:42:25 -329.994269 0.621139
LBFGS: 20 14:42:25 -330.019143 0.744946
Step Time Energy fmax
LBFGS: 0 14:42:26 -328.789984 3.125681
LBFGS: 1 14:42:26 -329.078579 1.656942
LBFGS: 2 14:42:26 -329.234881 1.066799
LBFGS: 3 14:42:26 -329.289353 0.939877
LBFGS: 4 14:42:26 -329.375107 0.762550
LBFGS: 5 14:42:27 -329.406493 0.587734
LBFGS: 6 14:42:27 -329.421664 0.382509
LBFGS: 7 14:42:27 -329.437810 0.476480
LBFGS: 8 14:42:27 -329.462109 0.750184
LBFGS: 9 14:42:27 -329.503396 0.839584
LBFGS: 10 14:42:28 -329.541354 0.638301
LBFGS: 11 14:42:28 -329.574467 0.688377
LBFGS: 12 14:42:28 -329.614764 0.616274
LBFGS: 13 14:42:28 -329.666003 0.966111
LBFGS: 14 14:42:28 -329.726800 1.236778
LBFGS: 15 14:42:29 -329.792498 1.116583
LBFGS: 16 14:42:29 -329.843403 0.445843
LBFGS: 17 14:42:29 -329.867459 0.490640
LBFGS: 18 14:42:29 -329.896237 0.647570
LBFGS: 19 14:42:29 -329.937657 0.840805
LBFGS: 20 14:42:30 -329.963154 0.535597
Step Time Energy fmax
LBFGS: 0 14:42:30 -329.290158 2.706752
LBFGS: 1 14:42:30 -329.492587 1.389166
LBFGS: 2 14:42:30 -329.604992 0.881604
LBFGS: 3 14:42:30 -329.635054 0.794012
LBFGS: 4 14:42:31 -329.687184 0.475315
LBFGS: 5 14:42:31 -329.697535 0.285422
LBFGS: 6 14:42:31 -329.704152 0.292163
LBFGS: 7 14:42:31 -329.712561 0.352844
LBFGS: 8 14:42:31 -329.724186 0.427478
LBFGS: 9 14:42:32 -329.734517 0.315892
LBFGS: 10 14:42:32 -329.743111 0.365751
LBFGS: 11 14:42:32 -329.754944 0.448977
LBFGS: 12 14:42:32 -329.774600 0.546191
LBFGS: 13 14:42:32 -329.802659 0.674905
LBFGS: 14 14:42:33 -329.833886 0.624353
LBFGS: 15 14:42:33 -329.858248 0.315087
LBFGS: 16 14:42:33 -329.872372 0.290158
LBFGS: 17 14:42:33 -329.889281 0.493070
LBFGS: 18 14:42:33 -329.920741 0.719314
LBFGS: 19 14:42:34 -329.953606 0.674803
LBFGS: 20 14:42:34 -329.971020 0.295010
Step Time Energy fmax
LBFGS: 0 14:42:34 -329.202818 2.884898
LBFGS: 1 14:42:34 -329.410704 1.477146
LBFGS: 2 14:42:34 -329.528945 0.927804
LBFGS: 3 14:42:35 -329.564318 0.867511
LBFGS: 4 14:42:35 -329.624400 0.489448
LBFGS: 5 14:42:35 -329.639779 0.332687
LBFGS: 6 14:42:35 -329.649306 0.328516
LBFGS: 7 14:42:35 -329.661103 0.379927
LBFGS: 8 14:42:36 -329.678977 0.561780
LBFGS: 9 14:42:36 -329.698084 0.503048
LBFGS: 10 14:42:36 -329.713706 0.470572
LBFGS: 11 14:42:36 -329.732087 0.537155
LBFGS: 12 14:42:36 -329.760134 0.640156
LBFGS: 13 14:42:37 -329.799008 0.793452
LBFGS: 14 14:42:37 -329.843985 0.806433
LBFGS: 15 14:42:37 -329.884224 0.496624
LBFGS: 16 14:42:37 -329.906899 0.363125
LBFGS: 17 14:42:37 -329.924395 0.416264
LBFGS: 18 14:42:38 -329.955746 0.580726
LBFGS: 19 14:42:38 -329.985705 0.504596
LBFGS: 20 14:42:38 -330.001919 0.304095
top_candidates = outputs["adslabs"]
global_min_candidate = top_candidates[0]
top_candidates = outputs["adslabs"]
pd.DataFrame(top_candidates)
input_atoms | atoms | results | |
---|---|---|---|
0 | {'atoms': (Atom('Cu', [np.float64(-1.300046521... | (Atom('Cu', [np.float64(-1.3000465215529715), ... | {'energy': -330.1439824582928, 'forces': [[0.0... |
1 | {'atoms': (Atom('Cu', [np.float64(-1.300046521... | (Atom('Cu', [np.float64(-1.3000465215529715), ... | {'energy': -330.05221610087085, 'forces': [[0.... |
2 | {'atoms': (Atom('Cu', [np.float64(-1.300046521... | (Atom('Cu', [np.float64(-1.3000465215529715), ... | {'energy': -330.0019193174236, 'forces': [[0.0... |
3 | {'atoms': (Atom('Cu', [np.float64(-1.300046521... | (Atom('Cu', [np.float64(-1.3000465215529715), ... | {'energy': -329.99773268717456, 'forces': [[0.... |
4 | {'atoms': (Atom('Cu', [np.float64(-1.300046521... | (Atom('Cu', [np.float64(-1.3000465215529715), ... | {'energy': -329.99458174723316, 'forces': [[0.... |
5 | {'atoms': (Atom('Cu', [np.float64(-1.300046521... | (Atom('Cu', [np.float64(-1.3000465215529715), ... | {'energy': -329.9927869321697, 'forces': [[0.0... |
6 | {'atoms': (Atom('Cu', [np.float64(-1.300046521... | (Atom('Cu', [np.float64(-1.3000465215529715), ... | {'energy': -329.971020269572, 'forces': [[0.0,... |
7 | {'atoms': (Atom('Cu', [np.float64(-1.300046521... | (Atom('Cu', [np.float64(-1.3000465215529715), ... | {'energy': -329.9631543638103, 'forces': [[0.0... |
8 | {'atoms': (Atom('Cu', [np.float64(-1.300046521... | (Atom('Cu', [np.float64(-1.3000465215529715), ... | {'energy': -329.9566102506511, 'forces': [[0.0... |
Write VASP input files#
If you want to verify the results, you should run VASP. This assumes you have access to VASP pseudopotentials. The default VASP flags (which are equivalent to those used to make OC20) are located in ocdata.utils.vasp
. Alternatively, you may pass your own vasp flags to the write_vasp_input_files
function as vasp_flags
. Note that to run this you need access to the VASP pseudopotentials and need to have those set up in ASE.
import os
from fairchem.data.oc.utils.vasp import write_vasp_input_files
# Grab the 5 systems with the lowest energy
top_5_candidates = top_candidates[:5]
# Write the inputs
for idx, config in enumerate(top_5_candidates):
os.makedirs(f"data/{idx}", exist_ok=True)
write_vasp_input_files(config["atoms"], outdir=f"data/{idx}/")