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 loginor 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]
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-1p1")
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'.
/home/runner/work/_tool/Python/3.12.12/x64/lib/python3.12/site-packages/pydantic/_internal/_generate_schema.py:2249: UnsupportedFieldAttributeWarning: The 'repr' attribute with value False was provided to the `Field()` function, which has no effect in the context it was used. 'repr' is field-specific metadata, and can only be attached to a model field using `Annotated` metadata or by assignment. This may have happened because an `Annotated` type alias using the `type` statement was used, or if the `Field()` function was attached to a single member of a union type.
warnings.warn(
/home/runner/work/_tool/Python/3.12.12/x64/lib/python3.12/site-packages/pydantic/_internal/_generate_schema.py:2249: UnsupportedFieldAttributeWarning: The 'frozen' attribute with value True was provided to the `Field()` function, which has no effect in the context it was used. 'frozen' is field-specific metadata, and can only be attached to a model field using `Annotated` metadata or by assignment. This may have happened because an `Annotated` type alias using the `type` statement was used, or if the `Field()` function was attached to a single member of a union type.
warnings.warn(
Step Time Energy fmax
LBFGS: 0 07:03:55 -300.219297 551683.125000
LBFGS: 1 07:03:55 -299.850502 1.369265
LBFGS: 2 07:03:55 -299.860924 1.331234
LBFGS: 3 07:03:56 -299.904234 1.057896
LBFGS: 4 07:03:56 -299.905397 1.062374
LBFGS: 5 07:03:56 -299.912411 1.145730
LBFGS: 6 07:03:56 -299.912733 1.149351
LBFGS: 7 07:03:56 -299.913897 1.140788
LBFGS: 8 07:03:56 -299.914194 1.122735
LBFGS: 9 07:03:57 -299.914309 1.108778
LBFGS: 10 07:03:57 -299.914354 1.105523
LBFGS: 11 07:03:57 -299.914446 1.105923
LBFGS: 12 07:03:57 -299.914516 1.113078
LBFGS: 13 07:03:57 -299.914555 1.122086
LBFGS: 14 07:03:57 -299.914572 1.126456
LBFGS: 15 07:03:58 -299.914591 1.129017
LBFGS: 16 07:03:58 -299.914612 1.129812
LBFGS: 17 07:03:58 -299.914644 1.127138
LBFGS: 18 07:03:58 -299.914663 1.122384
LBFGS: 19 07:03:58 -299.914671 1.118687
LBFGS: 20 07:03:59 -299.914690 1.115617
/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 07:03:59 -329.009718 516189.250000
LBFGS: 1 07:03:59 -328.272557 2.982887
LBFGS: 2 07:03:59 -328.572585 2.571670
LBFGS: 3 07:04:00 -328.782168 1.967546
LBFGS: 4 07:04:00 -328.865000 1.609709
LBFGS: 5 07:04:00 -328.996357 1.055519
LBFGS: 6 07:04:00 -329.045947 1.039578
LBFGS: 7 07:04:00 -329.065999 1.010804
LBFGS: 8 07:04:00 -329.085146 0.964081
LBFGS: 9 07:04:01 -329.108182 0.899608
LBFGS: 10 07:04:01 -329.137213 0.926771
LBFGS: 11 07:04:01 -329.156691 0.954693
LBFGS: 12 07:04:01 -329.176533 0.961741
LBFGS: 13 07:04:01 -329.205042 0.950737
LBFGS: 14 07:04:02 -329.243001 0.922470
LBFGS: 15 07:04:02 -329.285567 0.951646
LBFGS: 16 07:04:02 -329.324755 0.865006
LBFGS: 17 07:04:02 -329.352135 0.862124
LBFGS: 18 07:04:02 -329.378317 0.872029
LBFGS: 19 07:04:02 -329.425505 0.916514
LBFGS: 20 07:04:03 -329.470816 0.926848
Step Time Energy fmax
LBFGS: 0 07:04:03 -328.863085 538544.000000
LBFGS: 1 07:04:03 -328.376616 3.059124
LBFGS: 2 07:04:03 -328.676308 1.796373
LBFGS: 3 07:04:03 -328.897011 1.245206
LBFGS: 4 07:04:04 -328.967021 1.135825
LBFGS: 5 07:04:04 -329.099827 0.962671
LBFGS: 6 07:04:04 -329.139771 0.890220
LBFGS: 7 07:04:04 -329.162440 0.859414
LBFGS: 8 07:04:04 -329.195115 0.828956
LBFGS: 9 07:04:04 -329.226897 0.810591
LBFGS: 10 07:04:05 -329.253039 0.805043
LBFGS: 11 07:04:05 -329.271203 0.806553
LBFGS: 12 07:04:05 -329.298417 0.801531
LBFGS: 13 07:04:05 -329.338913 0.776329
LBFGS: 14 07:04:05 -329.394562 0.894055
LBFGS: 15 07:04:06 -329.460867 0.863187
LBFGS: 16 07:04:06 -329.522318 0.690688
LBFGS: 17 07:04:06 -329.562075 0.675819
LBFGS: 18 07:04:06 -329.602498 0.676771
LBFGS: 19 07:04:06 -329.648345 0.695469
LBFGS: 20 07:04:06 -329.681653 0.733665
Step Time Energy fmax
LBFGS: 0 07:04:07 -329.256671 865734.937500
LBFGS: 1 07:04:07 -328.868081 2.722295
LBFGS: 2 07:04:07 -329.079318 1.398606
LBFGS: 3 07:04:07 -329.214642 1.103763
LBFGS: 4 07:04:07 -329.261324 0.981613
LBFGS: 5 07:04:08 -329.342568 0.751000
LBFGS: 6 07:04:08 -329.365359 0.707372
LBFGS: 7 07:04:08 -329.376475 0.703676
LBFGS: 8 07:04:08 -329.386906 0.701389
LBFGS: 9 07:04:08 -329.403328 0.698599
LBFGS: 10 07:04:08 -329.418635 0.696957
LBFGS: 11 07:04:09 -329.428975 0.696390
LBFGS: 12 07:04:09 -329.440098 0.695378
LBFGS: 13 07:04:09 -329.457133 0.692206
LBFGS: 14 07:04:09 -329.479815 0.684976
LBFGS: 15 07:04:09 -329.506167 0.672336
LBFGS: 16 07:04:10 -329.531071 0.655254
LBFGS: 17 07:04:10 -329.550124 0.639355
LBFGS: 18 07:04:10 -329.572827 0.627330
LBFGS: 19 07:04:10 -329.610251 0.738664
LBFGS: 20 07:04:10 -329.638848 0.599467
Step Time Energy fmax
LBFGS: 0 07:04:10 -329.205536 1056765.625000
LBFGS: 1 07:04:11 -328.775235 2.596575
LBFGS: 2 07:04:11 -329.001916 1.320922
LBFGS: 3 07:04:11 -329.146334 1.071145
LBFGS: 4 07:04:11 -329.191165 0.950951
LBFGS: 5 07:04:11 -329.272757 0.804868
LBFGS: 6 07:04:12 -329.294652 0.759862
LBFGS: 7 07:04:12 -329.306004 0.745713
LBFGS: 8 07:04:12 -329.319096 0.740958
LBFGS: 9 07:04:12 -329.338540 0.741776
LBFGS: 10 07:04:12 -329.357165 0.748645
LBFGS: 11 07:04:12 -329.370474 0.756110
LBFGS: 12 07:04:13 -329.386288 0.762771
LBFGS: 13 07:04:13 -329.409976 0.768379
LBFGS: 14 07:04:13 -329.441577 0.780069
LBFGS: 15 07:04:13 -329.479273 0.773502
LBFGS: 16 07:04:13 -329.516938 0.762952
LBFGS: 17 07:04:14 -329.546500 0.735170
LBFGS: 18 07:04:14 -329.579749 0.691990
LBFGS: 19 07:04:14 -329.621357 0.672803
LBFGS: 20 07:04:14 -329.655379 0.686714
Step Time Energy fmax
LBFGS: 0 07:04:14 -329.211249 821025.562500
LBFGS: 1 07:04:14 -328.874709 2.454445
LBFGS: 2 07:04:15 -329.089730 1.255736
LBFGS: 3 07:04:15 -329.224555 1.070862
LBFGS: 4 07:04:15 -329.270123 1.009353
LBFGS: 5 07:04:15 -329.351273 0.865038
LBFGS: 6 07:04:15 -329.374611 0.814809
LBFGS: 7 07:04:16 -329.387003 0.797578
LBFGS: 8 07:04:16 -329.400345 0.792286
LBFGS: 9 07:04:16 -329.420573 0.794411
LBFGS: 10 07:04:16 -329.441935 0.805628
LBFGS: 11 07:04:16 -329.457593 0.819804
LBFGS: 12 07:04:16 -329.476763 0.836861
LBFGS: 13 07:04:17 -329.505093 0.857227
LBFGS: 14 07:04:17 -329.541672 0.876435
LBFGS: 15 07:04:17 -329.584731 0.890813
LBFGS: 16 07:04:17 -329.623593 0.896122
LBFGS: 17 07:04:17 -329.645819 0.892989
LBFGS: 18 07:04:18 -329.663853 0.886925
LBFGS: 19 07:04:18 -329.691702 0.877560
LBFGS: 20 07:04:18 -329.720681 0.864958
Step Time Energy fmax
LBFGS: 0 07:04:18 -329.135083 832710.750000
LBFGS: 1 07:04:18 -328.662415 2.547099
LBFGS: 2 07:04:18 -328.901522 1.285437
LBFGS: 3 07:04:19 -329.048972 1.175766
LBFGS: 4 07:04:19 -329.108267 1.121042
LBFGS: 5 07:04:19 -329.217484 1.093731
LBFGS: 6 07:04:19 -329.272294 0.895949
LBFGS: 7 07:04:19 -329.300862 0.853287
LBFGS: 8 07:04:20 -329.334433 0.818757
LBFGS: 9 07:04:20 -329.382062 0.787278
LBFGS: 10 07:04:20 -329.435673 0.787379
LBFGS: 11 07:04:20 -329.470976 0.826931
LBFGS: 12 07:04:20 -329.505461 0.862228
LBFGS: 13 07:04:20 -329.543692 0.872370
LBFGS: 14 07:04:21 -329.593035 0.847277
LBFGS: 15 07:04:21 -329.650067 0.847150
LBFGS: 16 07:04:21 -329.701167 0.795304
LBFGS: 17 07:04:21 -329.728713 0.761443
LBFGS: 18 07:04:21 -329.745633 0.742153
LBFGS: 19 07:04:22 -329.764462 0.704734
LBFGS: 20 07:04:22 -329.788828 0.727826
Step Time Energy fmax
LBFGS: 0 07:04:22 -328.733914 979540.437500
LBFGS: 1 07:04:22 -328.528988 3.092413
LBFGS: 2 07:04:22 -328.787276 1.450804
LBFGS: 3 07:04:22 -328.954165 1.014360
LBFGS: 4 07:04:23 -329.017626 1.135988
LBFGS: 5 07:04:23 -329.119908 0.908928
LBFGS: 6 07:04:23 -329.172133 0.643255
LBFGS: 7 07:04:23 -329.191281 0.624556
LBFGS: 8 07:04:23 -329.206162 0.621516
LBFGS: 9 07:04:24 -329.231179 0.701830
LBFGS: 10 07:04:24 -329.266814 0.840431
LBFGS: 11 07:04:24 -329.302439 0.700889
LBFGS: 12 07:04:24 -329.331698 0.721405
LBFGS: 13 07:04:24 -329.373850 0.736121
LBFGS: 14 07:04:24 -329.431754 0.948242
LBFGS: 15 07:04:25 -329.502261 1.187393
LBFGS: 16 07:04:25 -329.580355 1.055123
LBFGS: 17 07:04:25 -329.646681 0.709085
LBFGS: 18 07:04:25 -329.679413 0.687026
LBFGS: 19 07:04:25 -329.714016 0.709920
LBFGS: 20 07:04:26 -329.764107 0.754927
Step Time Energy fmax
LBFGS: 0 07:04:26 -328.812853 1020185.250000
LBFGS: 1 07:04:26 -328.478581 3.005310
LBFGS: 2 07:04:26 -328.767445 1.760269
LBFGS: 3 07:04:26 -328.933873 1.247884
LBFGS: 4 07:04:26 -328.994130 1.086529
LBFGS: 5 07:04:27 -329.090457 0.987897
LBFGS: 6 07:04:27 -329.125271 0.968220
LBFGS: 7 07:04:27 -329.143090 0.955036
LBFGS: 8 07:04:27 -329.161332 0.930684
LBFGS: 9 07:04:27 -329.189467 0.922902
LBFGS: 10 07:04:28 -329.231885 0.902695
LBFGS: 11 07:04:28 -329.265151 0.873864
LBFGS: 12 07:04:28 -329.291050 0.840540
LBFGS: 13 07:04:28 -329.322247 0.791381
LBFGS: 14 07:04:28 -329.362237 0.920958
LBFGS: 15 07:04:28 -329.411461 1.109301
LBFGS: 16 07:04:29 -329.463086 0.911627
LBFGS: 17 07:04:29 -329.502171 0.844303
LBFGS: 18 07:04:29 -329.527884 0.826471
LBFGS: 19 07:04:29 -329.567265 0.849089
LBFGS: 20 07:04:29 -329.616328 0.953814
Step Time Energy fmax
LBFGS: 0 07:04:30 -329.314452 619833.687500
LBFGS: 1 07:04:30 -328.875342 2.685486
LBFGS: 2 07:04:30 -329.097218 1.731071
LBFGS: 3 07:04:30 -329.243464 1.406475
LBFGS: 4 07:04:30 -329.288817 1.245209
LBFGS: 5 07:04:30 -329.367861 1.031459
LBFGS: 6 07:04:31 -329.383362 0.996871
LBFGS: 7 07:04:31 -329.391596 0.980443
LBFGS: 8 07:04:31 -329.400713 0.966010
LBFGS: 9 07:04:31 -329.413193 0.946945
LBFGS: 10 07:04:31 -329.423613 0.932496
LBFGS: 11 07:04:32 -329.431740 0.925620
LBFGS: 12 07:04:32 -329.442799 0.920529
LBFGS: 13 07:04:32 -329.459658 0.914740
LBFGS: 14 07:04:32 -329.480689 0.907586
LBFGS: 15 07:04:32 -329.500676 0.899751
LBFGS: 16 07:04:32 -329.514769 0.892275
LBFGS: 17 07:04:33 -329.525645 0.885418
LBFGS: 18 07:04:33 -329.544644 0.875178
LBFGS: 19 07:04:33 -329.576142 0.863328
LBFGS: 20 07:04:33 -329.606198 0.861917
Step Time Energy fmax
LBFGS: 0 07:04:33 -329.228390 723478.062500
LBFGS: 1 07:04:34 -328.838864 2.964931
LBFGS: 2 07:04:34 -329.072484 1.507791
LBFGS: 3 07:04:34 -329.226706 1.130408
LBFGS: 4 07:04:34 -329.277188 1.009304
LBFGS: 5 07:04:34 -329.360264 0.836308
LBFGS: 6 07:04:34 -329.381066 0.816109
LBFGS: 7 07:04:35 -329.393046 0.800821
LBFGS: 8 07:04:35 -329.407660 0.779534
LBFGS: 9 07:04:35 -329.427895 0.746651
LBFGS: 10 07:04:35 -329.447768 0.711471
LBFGS: 11 07:04:35 -329.461466 0.725418
LBFGS: 12 07:04:36 -329.476496 0.761854
LBFGS: 13 07:04:36 -329.498215 0.808924
LBFGS: 14 07:04:36 -329.526742 0.859836
LBFGS: 15 07:04:36 -329.559004 0.897144
LBFGS: 16 07:04:36 -329.589013 0.900096
LBFGS: 17 07:04:36 -329.611458 0.863098
LBFGS: 18 07:04:37 -329.638682 0.787103
LBFGS: 19 07:04:37 -329.680916 0.748177
LBFGS: 20 07:04:37 -329.710478 0.684008
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': -329.78882842081714, 'forces': [[0.... |
| 1 | {'atoms': (Atom('Cu', [np.float64(-1.300046521... | (Atom('Cu', [np.float64(-1.3000465215529715), ... | {'energy': -329.7641072751873, 'forces': [[0.0... |
| 2 | {'atoms': (Atom('Cu', [np.float64(-1.300046521... | (Atom('Cu', [np.float64(-1.3000465215529715), ... | {'energy': -329.7206807615156, 'forces': [[0.0... |
| 3 | {'atoms': (Atom('Cu', [np.float64(-1.300046521... | (Atom('Cu', [np.float64(-1.3000465215529715), ... | {'energy': -329.71047835367847, 'forces': [[0.... |
| 4 | {'atoms': (Atom('Cu', [np.float64(-1.300046521... | (Atom('Cu', [np.float64(-1.3000465215529715), ... | {'energy': -329.6816525937908, 'forces': [[0.0... |
| 5 | {'atoms': (Atom('Cu', [np.float64(-1.300046521... | (Atom('Cu', [np.float64(-1.3000465215529715), ... | {'energy': -329.6553788663738, 'forces': [[0.0... |
| 6 | {'atoms': (Atom('Cu', [np.float64(-1.300046521... | (Atom('Cu', [np.float64(-1.3000465215529715), ... | {'energy': -329.63884787577325, 'forces': [[0.... |
| 7 | {'atoms': (Atom('Cu', [np.float64(-1.300046521... | (Atom('Cu', [np.float64(-1.3000465215529715), ... | {'energy': -329.6163278104656, 'forces': [[0.0... |
| 8 | {'atoms': (Atom('Cu', [np.float64(-1.300046521... | (Atom('Cu', [np.float64(-1.3000465215529715), ... | {'energy': -329.60619788187677, 'forces': [[0.... |
| 9 | {'atoms': (Atom('Cu', [np.float64(-1.300046521... | (Atom('Cu', [np.float64(-1.3000465215529715), ... | {'energy': -329.47081618326837, '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}/")