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 20:25:34 -300.187736 0.048987
LBFGS: 1 20:25:34 -300.188278 0.046745
LBFGS: 2 20:25:34 -300.194189 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:25:35 -328.984726 2.953961
LBFGS: 1 20:25:35 -329.236281 1.465739
LBFGS: 2 20:25:35 -329.368317 0.975025
LBFGS: 3 20:25:35 -329.411927 0.865681
LBFGS: 4 20:25:35 -329.484345 0.709035
LBFGS: 5 20:25:36 -329.510094 0.527276
LBFGS: 6 20:25:36 -329.522004 0.364704
LBFGS: 7 20:25:36 -329.533898 0.409460
LBFGS: 8 20:25:36 -329.551825 0.613491
LBFGS: 9 20:25:36 -329.578267 0.635173
LBFGS: 10 20:25:37 -329.601903 0.540939
LBFGS: 11 20:25:37 -329.624722 0.598115
LBFGS: 12 20:25:37 -329.656123 0.557814
LBFGS: 13 20:25:37 -329.698382 0.892124
LBFGS: 14 20:25:37 -329.748219 1.073954
LBFGS: 15 20:25:38 -329.798470 0.871474
LBFGS: 16 20:25:38 -329.833897 0.410503
LBFGS: 17 20:25:38 -329.854886 0.495866
LBFGS: 18 20:25:38 -329.888213 0.747313
LBFGS: 19 20:25:38 -329.929127 0.870638
LBFGS: 20 20:25:39 -329.956606 0.545080
Step Time Energy fmax
LBFGS: 0 20:25:39 -328.837575 2.988139
LBFGS: 1 20:25:39 -329.102131 1.448324
LBFGS: 2 20:25:39 -329.271563 1.138233
LBFGS: 3 20:25:39 -329.324204 1.035578
LBFGS: 4 20:25:40 -329.416981 0.755246
LBFGS: 5 20:25:40 -329.444869 0.464511
LBFGS: 6 20:25:40 -329.459904 0.476722
LBFGS: 7 20:25:40 -329.481162 0.503541
LBFGS: 8 20:25:41 -329.508288 0.711559
LBFGS: 9 20:25:41 -329.538128 0.614162
LBFGS: 10 20:25:41 -329.560933 0.592167
LBFGS: 11 20:25:41 -329.591582 0.754410
LBFGS: 12 20:25:41 -329.639132 0.837184
LBFGS: 13 20:25:42 -329.704155 0.958695
LBFGS: 14 20:25:42 -329.784199 1.102087
LBFGS: 15 20:25:42 -329.863699 0.750938
LBFGS: 16 20:25:42 -329.912829 0.455471
LBFGS: 17 20:25:42 -329.939452 0.466000
LBFGS: 18 20:25:43 -329.967387 0.400431
LBFGS: 19 20:25:43 -329.982028 0.905128
LBFGS: 20 20:25:43 -329.992798 0.561117
Step Time Energy fmax
LBFGS: 0 20:25:43 -329.232623 2.785361
LBFGS: 1 20:25:43 -329.434826 1.426486
LBFGS: 2 20:25:44 -329.552569 0.935652
LBFGS: 3 20:25:44 -329.587487 0.870920
LBFGS: 4 20:25:44 -329.644982 0.459740
LBFGS: 5 20:25:44 -329.659257 0.337854
LBFGS: 6 20:25:44 -329.667712 0.365901
LBFGS: 7 20:25:45 -329.678450 0.353726
LBFGS: 8 20:25:45 -329.695452 0.543691
LBFGS: 9 20:25:45 -329.713509 0.500533
LBFGS: 10 20:25:45 -329.728123 0.442611
LBFGS: 11 20:25:45 -329.745274 0.531728
LBFGS: 12 20:25:46 -329.770917 0.588895
LBFGS: 13 20:25:46 -329.804667 0.693593
LBFGS: 14 20:25:46 -329.843121 0.743784
LBFGS: 15 20:25:46 -329.877797 0.451476
LBFGS: 16 20:25:46 -329.900715 0.311971
LBFGS: 17 20:25:47 -329.922463 0.458281
LBFGS: 18 20:25:47 -329.954028 0.591171
LBFGS: 19 20:25:47 -329.979840 0.437265
LBFGS: 20 20:25:47 -329.994576 0.344973
Step Time Energy fmax
LBFGS: 0 20:25:48 -329.181643 2.745973
LBFGS: 1 20:25:48 -329.400378 1.341007
LBFGS: 2 20:25:48 -329.519524 0.929936
LBFGS: 3 20:25:48 -329.551396 0.839113
LBFGS: 4 20:25:48 -329.609387 0.617696
LBFGS: 5 20:25:49 -329.624665 0.401439
LBFGS: 6 20:25:49 -329.633763 0.342196
LBFGS: 7 20:25:49 -329.645678 0.410247
LBFGS: 8 20:25:49 -329.661953 0.493682
LBFGS: 9 20:25:49 -329.677849 0.360917
LBFGS: 10 20:25:50 -329.690514 0.423456
LBFGS: 11 20:25:50 -329.706877 0.527207
LBFGS: 12 20:25:50 -329.733445 0.587322
LBFGS: 13 20:25:50 -329.772360 0.836371
LBFGS: 14 20:25:50 -329.820519 0.854168
LBFGS: 15 20:25:51 -329.867680 0.516268
LBFGS: 16 20:25:51 -329.896792 0.297701
LBFGS: 17 20:25:51 -329.918288 0.407303
LBFGS: 18 20:25:51 -329.949523 0.529417
LBFGS: 19 20:25:51 -329.978060 0.485848
LBFGS: 20 20:25:52 -329.997733 0.435417
Step Time Energy fmax
LBFGS: 0 20:25:52 -329.183524 2.526199
LBFGS: 1 20:25:52 -329.396344 1.305542
LBFGS: 2 20:25:52 -329.518508 0.967263
LBFGS: 3 20:25:52 -329.555485 0.843603
LBFGS: 4 20:25:53 -329.622088 0.715370
LBFGS: 5 20:25:53 -329.641148 0.471002
LBFGS: 6 20:25:53 -329.652817 0.333921
LBFGS: 7 20:25:53 -329.667330 0.421839
LBFGS: 8 20:25:53 -329.689396 0.594264
LBFGS: 9 20:25:54 -329.713378 0.513911
LBFGS: 10 20:25:54 -329.731848 0.548636
LBFGS: 11 20:25:54 -329.755326 0.665694
LBFGS: 12 20:25:54 -329.790099 0.712244
LBFGS: 13 20:25:54 -329.837231 0.841051
LBFGS: 14 20:25:55 -329.895533 0.900720
LBFGS: 15 20:25:55 -329.952967 0.574739
LBFGS: 16 20:25:55 -329.983796 0.271992
LBFGS: 17 20:25:55 -329.999795 0.291603
LBFGS: 18 20:25:55 -330.019352 0.338972
LBFGS: 19 20:25:56 -330.042025 0.687590
LBFGS: 20 20:25:56 -330.052220 0.564933
Step Time Energy fmax
LBFGS: 0 20:25:56 -329.107943 2.569486
LBFGS: 1 20:25:56 -329.339270 1.265316
LBFGS: 2 20:25:56 -329.466763 1.088365
LBFGS: 3 20:25:57 -329.512549 0.987019
LBFGS: 4 20:25:57 -329.597619 0.957654
LBFGS: 5 20:25:57 -329.639292 0.660148
LBFGS: 6 20:25:57 -329.661442 0.505568
LBFGS: 7 20:25:57 -329.689051 0.572689
LBFGS: 8 20:25:58 -329.731665 0.705917
LBFGS: 9 20:25:58 -329.782103 0.740839
LBFGS: 10 20:25:58 -329.820109 0.624038
LBFGS: 11 20:25:58 -329.863999 0.792352
LBFGS: 12 20:25:58 -329.905419 0.855030
LBFGS: 13 20:25:59 -329.962431 0.842003
LBFGS: 14 20:25:59 -330.023665 0.867447
LBFGS: 15 20:25:59 -330.078959 0.615974
LBFGS: 16 20:25:59 -330.103655 0.426043
LBFGS: 17 20:25:59 -330.108275 0.472795
LBFGS: 18 20:26:00 -330.119996 0.280243
LBFGS: 19 20:26:00 -330.127621 0.199830
LBFGS: 20 20:26:00 -330.143988 0.215729
Step Time Energy fmax
LBFGS: 0 20:26:00 -328.707257 3.174999
LBFGS: 1 20:26:01 -328.962895 1.490763
LBFGS: 2 20:26:01 -329.127270 1.072372
LBFGS: 3 20:26:01 -329.187443 1.160830
LBFGS: 4 20:26:01 -329.280058 0.885001
LBFGS: 5 20:26:01 -329.329110 0.519742
LBFGS: 6 20:26:02 -329.348328 0.409967
LBFGS: 7 20:26:02 -329.366934 0.424907
LBFGS: 8 20:26:02 -329.399401 0.800118
LBFGS: 9 20:26:02 -329.447157 0.988472
LBFGS: 10 20:26:02 -329.494013 0.718815
LBFGS: 11 20:26:03 -329.531621 0.687201
LBFGS: 12 20:26:03 -329.582985 0.680955
LBFGS: 13 20:26:03 -329.646906 1.014713
LBFGS: 14 20:26:03 -329.722212 1.204553
LBFGS: 15 20:26:03 -329.804848 1.007259
LBFGS: 16 20:26:04 -329.873009 0.506051
LBFGS: 17 20:26:04 -329.909089 0.561160
LBFGS: 18 20:26:04 -329.950242 0.793953
LBFGS: 19 20:26:04 -329.994271 0.621098
LBFGS: 20 20:26:04 -330.019135 0.745142
Step Time Energy fmax
LBFGS: 0 20:26:05 -328.789984 3.125681
LBFGS: 1 20:26:05 -329.078579 1.656942
LBFGS: 2 20:26:05 -329.234877 1.066798
LBFGS: 3 20:26:05 -329.289349 0.939877
LBFGS: 4 20:26:05 -329.375109 0.762557
LBFGS: 5 20:26:06 -329.406487 0.587760
LBFGS: 6 20:26:06 -329.421664 0.382443
LBFGS: 7 20:26:06 -329.437810 0.476524
LBFGS: 8 20:26:06 -329.462109 0.750077
LBFGS: 9 20:26:06 -329.503405 0.839595
LBFGS: 10 20:26:07 -329.541354 0.638336
LBFGS: 11 20:26:07 -329.574471 0.688359
LBFGS: 12 20:26:07 -329.614773 0.616258
LBFGS: 13 20:26:07 -329.666003 0.966134
LBFGS: 14 20:26:07 -329.726798 1.236750
LBFGS: 15 20:26:08 -329.792500 1.116598
LBFGS: 16 20:26:08 -329.843407 0.445902
LBFGS: 17 20:26:08 -329.867457 0.490570
LBFGS: 18 20:26:08 -329.896239 0.647481
LBFGS: 19 20:26:08 -329.937657 0.840740
LBFGS: 20 20:26:09 -329.963158 0.535626
Step Time Energy fmax
LBFGS: 0 20:26:09 -329.290158 2.706752
LBFGS: 1 20:26:09 -329.492589 1.389167
LBFGS: 2 20:26:09 -329.604991 0.881611
LBFGS: 3 20:26:09 -329.635054 0.794012
LBFGS: 4 20:26:10 -329.687188 0.475331
LBFGS: 5 20:26:10 -329.697537 0.285420
LBFGS: 6 20:26:10 -329.704150 0.292157
LBFGS: 7 20:26:10 -329.712559 0.352840
LBFGS: 8 20:26:10 -329.724186 0.427476
LBFGS: 9 20:26:11 -329.734520 0.315872
LBFGS: 10 20:26:11 -329.743109 0.365789
LBFGS: 11 20:26:11 -329.754944 0.448979
LBFGS: 12 20:26:11 -329.774596 0.546188
LBFGS: 13 20:26:11 -329.802661 0.674880
LBFGS: 14 20:26:12 -329.833884 0.624333
LBFGS: 15 20:26:12 -329.858252 0.315081
LBFGS: 16 20:26:12 -329.872378 0.290104
LBFGS: 17 20:26:12 -329.889277 0.493051
LBFGS: 18 20:26:12 -329.920743 0.719286
LBFGS: 19 20:26:13 -329.953614 0.674811
LBFGS: 20 20:26:13 -329.971020 0.294956
Step Time Energy fmax
LBFGS: 0 20:26:13 -329.202820 2.884898
LBFGS: 1 20:26:13 -329.410708 1.477147
LBFGS: 2 20:26:13 -329.528943 0.927802
LBFGS: 3 20:26:14 -329.564318 0.867514
LBFGS: 4 20:26:14 -329.624402 0.489442
LBFGS: 5 20:26:14 -329.639775 0.332712
LBFGS: 6 20:26:14 -329.649306 0.328545
LBFGS: 7 20:26:15 -329.661101 0.380075
LBFGS: 8 20:26:15 -329.678977 0.561738
LBFGS: 9 20:26:15 -329.698086 0.503053
LBFGS: 10 20:26:15 -329.713704 0.470670
LBFGS: 11 20:26:15 -329.732092 0.537095
LBFGS: 12 20:26:16 -329.760138 0.640157
LBFGS: 13 20:26:16 -329.799008 0.793539
LBFGS: 14 20:26:16 -329.843977 0.806504
LBFGS: 15 20:26:16 -329.884226 0.496588
LBFGS: 16 20:26:16 -329.906897 0.363119
LBFGS: 17 20:26:17 -329.924391 0.416321
LBFGS: 18 20:26:17 -329.955740 0.580768
LBFGS: 19 20:26:17 -329.985701 0.504625
LBFGS: 20 20:26:17 -330.001919 0.303994
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.1439881803387, 'forces': [[0.0... |
1 | {'atoms': (Atom('Cu', [np.float64(-1.300046521... | (Atom('Cu', [np.float64(-1.3000465215529715), ... | {'energy': -330.0522199155681, 'forces': [[0.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.99457602518726, 'forces': [[0.... |
5 | {'atoms': (Atom('Cu', [np.float64(-1.300046521... | (Atom('Cu', [np.float64(-1.3000465215529715), ... | {'energy': -329.99279837626153, '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.96315817850757, 'forces': [[0.... |
8 | {'atoms': (Atom('Cu', [np.float64(-1.300046521... | (Atom('Cu', [np.float64(-1.3000465215529715), ... | {'energy': -329.95660643595386, '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}/")