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.
Need to install fairchem-core or get UMA access or getting permissions/401 errors?
Install the necessary packages using pip, uv etc
Get access to any necessary huggingface gated models
Get and login to your Huggingface account
Request access to https://huggingface.co/facebook/UMA
Create a Huggingface token at https://huggingface.co/settings/tokens/ with the permission “Permissions: Read access to contents of all public gated repos you can access”
Add the token as an environment variable using
huggingface-cli login
or by setting the HF_TOKEN environment variable.
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 20:13:43 -300.187736 0.048987
LBFGS: 1 20:13:43 -300.188278 0.046745
LBFGS: 2 20:13:44 -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 20:13:44 -328.984726 2.953960
LBFGS: 1 20:13:44 -329.236281 1.465739
LBFGS: 2 20:13:44 -329.368317 0.975025
LBFGS: 3 20:13:45 -329.411927 0.865682
LBFGS: 4 20:13:45 -329.484347 0.709057
LBFGS: 5 20:13:45 -329.510092 0.527251
LBFGS: 6 20:13:45 -329.522000 0.364714
LBFGS: 7 20:13:45 -329.533900 0.409456
LBFGS: 8 20:13:46 -329.551827 0.613559
LBFGS: 9 20:13:46 -329.578267 0.635071
LBFGS: 10 20:13:46 -329.601904 0.540950
LBFGS: 11 20:13:46 -329.624724 0.598074
LBFGS: 12 20:13:46 -329.656125 0.557806
LBFGS: 13 20:13:47 -329.698386 0.892002
LBFGS: 14 20:13:47 -329.748229 1.073884
LBFGS: 15 20:13:47 -329.798472 0.871550
LBFGS: 16 20:13:47 -329.833901 0.410499
LBFGS: 17 20:13:47 -329.854880 0.495808
LBFGS: 18 20:13:48 -329.888217 0.747374
LBFGS: 19 20:13:48 -329.929127 0.870662
LBFGS: 20 20:13:48 -329.956614 0.545108
Step Time Energy fmax
LBFGS: 0 20:13:48 -328.837573 2.988140
LBFGS: 1 20:13:48 -329.102130 1.448323
LBFGS: 2 20:13:49 -329.271563 1.138231
LBFGS: 3 20:13:49 -329.324200 1.035575
LBFGS: 4 20:13:49 -329.416979 0.755251
LBFGS: 5 20:13:49 -329.444869 0.464501
LBFGS: 6 20:13:49 -329.459904 0.476724
LBFGS: 7 20:13:50 -329.481164 0.503625
LBFGS: 8 20:13:50 -329.508286 0.711553
LBFGS: 9 20:13:50 -329.538132 0.614140
LBFGS: 10 20:13:50 -329.560935 0.592213
LBFGS: 11 20:13:50 -329.591580 0.754379
LBFGS: 12 20:13:51 -329.639136 0.837127
LBFGS: 13 20:13:51 -329.704157 0.958764
LBFGS: 14 20:13:51 -329.784197 1.102181
LBFGS: 15 20:13:51 -329.863699 0.750974
LBFGS: 16 20:13:51 -329.912831 0.455438
LBFGS: 17 20:13:52 -329.939450 0.466095
LBFGS: 18 20:13:52 -329.967394 0.399553
LBFGS: 19 20:13:52 -329.982070 0.903847
LBFGS: 20 20:13:52 -329.992693 0.564068
Step Time Energy fmax
LBFGS: 0 20:13:52 -329.232623 2.785361
LBFGS: 1 20:13:52 -329.434828 1.426486
LBFGS: 2 20:13:53 -329.552575 0.935652
LBFGS: 3 20:13:53 -329.587487 0.870923
LBFGS: 4 20:13:53 -329.644988 0.459739
LBFGS: 5 20:13:53 -329.659258 0.337845
LBFGS: 6 20:13:53 -329.667714 0.365911
LBFGS: 7 20:13:54 -329.678443 0.353756
LBFGS: 8 20:13:54 -329.695452 0.543582
LBFGS: 9 20:13:54 -329.713513 0.500575
LBFGS: 10 20:13:54 -329.728121 0.442596
LBFGS: 11 20:13:54 -329.745276 0.531727
LBFGS: 12 20:13:55 -329.770915 0.588909
LBFGS: 13 20:13:55 -329.804669 0.693607
LBFGS: 14 20:13:55 -329.843119 0.743786
LBFGS: 15 20:13:55 -329.877799 0.451467
LBFGS: 16 20:13:55 -329.900719 0.311922
LBFGS: 17 20:13:56 -329.922457 0.458195
LBFGS: 18 20:13:56 -329.954024 0.591216
LBFGS: 19 20:13:56 -329.979840 0.437195
LBFGS: 20 20:13:56 -329.994572 0.344802
Step Time Energy fmax
LBFGS: 0 20:13:56 -329.181645 2.745974
LBFGS: 1 20:13:57 -329.400376 1.341007
LBFGS: 2 20:13:57 -329.519524 0.929936
LBFGS: 3 20:13:57 -329.551398 0.839113
LBFGS: 4 20:13:57 -329.609383 0.617697
LBFGS: 5 20:13:57 -329.624667 0.401489
LBFGS: 6 20:13:58 -329.633767 0.342065
LBFGS: 7 20:13:58 -329.645676 0.410130
LBFGS: 8 20:13:58 -329.661957 0.493752
LBFGS: 9 20:13:58 -329.677853 0.360978
LBFGS: 10 20:13:58 -329.690514 0.423471
LBFGS: 11 20:13:59 -329.706877 0.527141
LBFGS: 12 20:13:59 -329.733445 0.587319
LBFGS: 13 20:13:59 -329.772360 0.836427
LBFGS: 14 20:13:59 -329.820513 0.854277
LBFGS: 15 20:14:00 -329.867680 0.516314
LBFGS: 16 20:14:00 -329.896792 0.297701
LBFGS: 17 20:14:00 -329.918290 0.407280
LBFGS: 18 20:14:00 -329.949521 0.529536
LBFGS: 19 20:14:00 -329.978056 0.485854
LBFGS: 20 20:14:01 -329.997738 0.435227
Step Time Energy fmax
LBFGS: 0 20:14:01 -329.183524 2.526200
LBFGS: 1 20:14:01 -329.396344 1.305542
LBFGS: 2 20:14:01 -329.518508 0.967263
LBFGS: 3 20:14:01 -329.555485 0.843603
LBFGS: 4 20:14:02 -329.622084 0.715370
LBFGS: 5 20:14:02 -329.641148 0.471002
LBFGS: 6 20:14:02 -329.652819 0.333921
LBFGS: 7 20:14:02 -329.667330 0.421887
LBFGS: 8 20:14:02 -329.689396 0.594383
LBFGS: 9 20:14:03 -329.713376 0.513934
LBFGS: 10 20:14:03 -329.731850 0.548584
LBFGS: 11 20:14:03 -329.755322 0.665713
LBFGS: 12 20:14:03 -329.790089 0.712191
LBFGS: 13 20:14:03 -329.837222 0.841073
LBFGS: 14 20:14:03 -329.895531 0.900688
LBFGS: 15 20:14:04 -329.952960 0.574827
LBFGS: 16 20:14:04 -329.983801 0.271921
LBFGS: 17 20:14:04 -329.999795 0.291591
LBFGS: 18 20:14:04 -330.019364 0.338693
LBFGS: 19 20:14:04 -330.042019 0.688312
LBFGS: 20 20:14:05 -330.052088 0.569066
Step Time Energy fmax
LBFGS: 0 20:14:05 -329.107939 2.569486
LBFGS: 1 20:14:05 -329.339272 1.265316
LBFGS: 2 20:14:05 -329.466759 1.088366
LBFGS: 3 20:14:05 -329.512549 0.987016
LBFGS: 4 20:14:06 -329.597621 0.957659
LBFGS: 5 20:14:06 -329.639290 0.660155
LBFGS: 6 20:14:06 -329.661442 0.505608
LBFGS: 7 20:14:06 -329.689049 0.572703
LBFGS: 8 20:14:06 -329.731667 0.705863
LBFGS: 9 20:14:07 -329.782107 0.740795
LBFGS: 10 20:14:07 -329.820105 0.624032
LBFGS: 11 20:14:07 -329.864001 0.792395
LBFGS: 12 20:14:07 -329.905419 0.855008
LBFGS: 13 20:14:07 -329.962430 0.841980
LBFGS: 14 20:14:08 -330.023671 0.867466
LBFGS: 15 20:14:08 -330.078965 0.615938
LBFGS: 16 20:14:08 -330.103659 0.425931
LBFGS: 17 20:14:08 -330.108279 0.472555
LBFGS: 18 20:14:08 -330.120003 0.280253
LBFGS: 19 20:14:09 -330.127625 0.199809
LBFGS: 20 20:14:09 -330.143996 0.215523
Step Time Energy fmax
LBFGS: 0 20:14:09 -328.707257 3.174999
LBFGS: 1 20:14:09 -328.962889 1.490763
LBFGS: 2 20:14:09 -329.127272 1.072372
LBFGS: 3 20:14:10 -329.187447 1.160827
LBFGS: 4 20:14:10 -329.280060 0.885015
LBFGS: 5 20:14:10 -329.329108 0.519734
LBFGS: 6 20:14:10 -329.348330 0.409967
LBFGS: 7 20:14:10 -329.366934 0.424907
LBFGS: 8 20:14:11 -329.399405 0.800152
LBFGS: 9 20:14:11 -329.447159 0.988434
LBFGS: 10 20:14:11 -329.494019 0.718827
LBFGS: 11 20:14:11 -329.531621 0.687209
LBFGS: 12 20:14:11 -329.582984 0.681019
LBFGS: 13 20:14:12 -329.646906 1.014739
LBFGS: 14 20:14:12 -329.722216 1.204511
LBFGS: 15 20:14:12 -329.804854 1.007213
LBFGS: 16 20:14:12 -329.873009 0.506015
LBFGS: 17 20:14:12 -329.909091 0.561330
LBFGS: 18 20:14:13 -329.950244 0.793846
LBFGS: 19 20:14:13 -329.994265 0.621167
LBFGS: 20 20:14:13 -330.019154 0.744756
Step Time Energy fmax
LBFGS: 0 20:14:13 -328.789982 3.125681
LBFGS: 1 20:14:13 -329.078579 1.656942
LBFGS: 2 20:14:14 -329.234881 1.066799
LBFGS: 3 20:14:14 -329.289349 0.939883
LBFGS: 4 20:14:14 -329.375109 0.762539
LBFGS: 5 20:14:14 -329.406493 0.587732
LBFGS: 6 20:14:14 -329.421664 0.382495
LBFGS: 7 20:14:15 -329.437808 0.476518
LBFGS: 8 20:14:15 -329.462113 0.750103
LBFGS: 9 20:14:15 -329.503405 0.839551
LBFGS: 10 20:14:15 -329.541356 0.638345
LBFGS: 11 20:14:15 -329.574469 0.688373
LBFGS: 12 20:14:16 -329.614764 0.616236
LBFGS: 13 20:14:16 -329.666001 0.966235
LBFGS: 14 20:14:16 -329.726796 1.236748
LBFGS: 15 20:14:16 -329.792504 1.116537
LBFGS: 16 20:14:16 -329.843401 0.445856
LBFGS: 17 20:14:17 -329.867463 0.490587
LBFGS: 18 20:14:17 -329.896243 0.647576
LBFGS: 19 20:14:17 -329.937655 0.840710
LBFGS: 20 20:14:17 -329.963152 0.535560
Step Time Energy fmax
LBFGS: 0 20:14:17 -329.290158 2.706751
LBFGS: 1 20:14:18 -329.492590 1.389167
LBFGS: 2 20:14:18 -329.604991 0.881611
LBFGS: 3 20:14:18 -329.635054 0.794012
LBFGS: 4 20:14:18 -329.687186 0.475329
LBFGS: 5 20:14:18 -329.697537 0.285421
LBFGS: 6 20:14:19 -329.704157 0.292160
LBFGS: 7 20:14:19 -329.712559 0.352846
LBFGS: 8 20:14:19 -329.724190 0.427482
LBFGS: 9 20:14:19 -329.734519 0.315836
LBFGS: 10 20:14:19 -329.743113 0.365826
LBFGS: 11 20:14:20 -329.754944 0.448891
LBFGS: 12 20:14:20 -329.774594 0.546122
LBFGS: 13 20:14:20 -329.802659 0.674836
LBFGS: 14 20:14:20 -329.833882 0.624299
LBFGS: 15 20:14:20 -329.858252 0.315117
LBFGS: 16 20:14:20 -329.872372 0.290148
LBFGS: 17 20:14:21 -329.889281 0.493037
LBFGS: 18 20:14:21 -329.920746 0.719245
LBFGS: 19 20:14:21 -329.953610 0.674786
LBFGS: 20 20:14:21 -329.971020 0.295016
Step Time Energy fmax
LBFGS: 0 20:14:21 -329.202824 2.884899
LBFGS: 1 20:14:22 -329.410706 1.477146
LBFGS: 2 20:14:22 -329.528943 0.927804
LBFGS: 3 20:14:22 -329.564322 0.867511
LBFGS: 4 20:14:22 -329.624405 0.489449
LBFGS: 5 20:14:22 -329.639777 0.332680
LBFGS: 6 20:14:23 -329.649312 0.328554
LBFGS: 7 20:14:23 -329.661103 0.380018
LBFGS: 8 20:14:23 -329.678973 0.561796
LBFGS: 9 20:14:23 -329.698088 0.502978
LBFGS: 10 20:14:23 -329.713706 0.470606
LBFGS: 11 20:14:24 -329.732092 0.537098
LBFGS: 12 20:14:24 -329.760138 0.640134
LBFGS: 13 20:14:24 -329.799008 0.793429
LBFGS: 14 20:14:24 -329.843985 0.806449
LBFGS: 15 20:14:24 -329.884228 0.496563
LBFGS: 16 20:14:25 -329.906897 0.363126
LBFGS: 17 20:14:25 -329.924389 0.416321
LBFGS: 18 20:14:25 -329.955746 0.580812
LBFGS: 19 20:14:25 -329.985701 0.504676
LBFGS: 20 20:14:25 -330.001929 0.304147
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.14399580973316, 'forces': [[0.... |
1 | {'atoms': (Atom('Cu', [np.float64(-1.300046521... | (Atom('Cu', [np.float64(-1.3000465215529715), ... | {'energy': -330.05208830851245, 'forces': [[0.... |
2 | {'atoms': (Atom('Cu', [np.float64(-1.300046521... | (Atom('Cu', [np.float64(-1.3000465215529715), ... | {'energy': -330.0019288541668, 'forces': [[0.0... |
3 | {'atoms': (Atom('Cu', [np.float64(-1.300046521... | (Atom('Cu', [np.float64(-1.3000465215529715), ... | {'energy': -329.9977384092205, 'forces': [[0.0... |
4 | {'atoms': (Atom('Cu', [np.float64(-1.300046521... | (Atom('Cu', [np.float64(-1.3000465215529715), ... | {'energy': -329.99457221049005, 'forces': [[0.... |
5 | {'atoms': (Atom('Cu', [np.float64(-1.300046521... | (Atom('Cu', [np.float64(-1.3000465215529715), ... | {'energy': -329.99269347208667, 'forces': [[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.96315245646167, 'forces': [[0.... |
8 | {'atoms': (Atom('Cu', [np.float64(-1.300046521... | (Atom('Cu', [np.float64(-1.3000465215529715), ... | {'energy': -329.95661406534845, 'forces': [[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}/")