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]
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 22:07:13 -300.219297 866963.562500
LBFGS: 1 22:07:14 -299.811815 1.540575
LBFGS: 2 22:07:14 -299.838224 1.419908
LBFGS: 3 22:07:14 -299.929449 0.753185
LBFGS: 4 22:07:14 -299.932594 0.741285
LBFGS: 5 22:07:14 -299.937439 0.726191
LBFGS: 6 22:07:14 -299.942257 0.693959
LBFGS: 7 22:07:15 -299.944948 0.664779
LBFGS: 8 22:07:15 -299.945654 0.699435
LBFGS: 9 22:07:15 -299.945858 0.711887
LBFGS: 10 22:07:15 -299.946091 0.717098
LBFGS: 11 22:07:15 -299.946367 0.710962
LBFGS: 12 22:07:16 -299.946526 0.696013
LBFGS: 13 22:07:16 -299.946590 0.685268
LBFGS: 14 22:07:16 -299.946629 0.679904
LBFGS: 15 22:07:16 -299.946701 0.675322
LBFGS: 16 22:07:16 -299.946798 0.674938
LBFGS: 17 22:07:17 -299.946888 0.681082
LBFGS: 18 22:07:17 -299.946941 0.688874
LBFGS: 19 22:07:17 -299.946968 0.693902
LBFGS: 20 22:07:17 -299.947020 0.697578
/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 22:07:18 -329.009720 542338.250000
LBFGS: 1 22:07:18 -328.299479 3.431023
LBFGS: 2 22:07:18 -328.638218 2.737544
LBFGS: 3 22:07:18 -328.827651 2.047147
LBFGS: 4 22:07:18 -328.887991 1.751954
LBFGS: 5 22:07:19 -328.990956 1.286735
LBFGS: 6 22:07:19 -329.039227 1.155875
LBFGS: 7 22:07:19 -329.063193 1.181388
LBFGS: 8 22:07:19 -329.096837 1.180261
LBFGS: 9 22:07:19 -329.131573 1.144974
LBFGS: 10 22:07:20 -329.175391 1.065136
LBFGS: 11 22:07:20 -329.205821 0.984647
LBFGS: 12 22:07:20 -329.236374 0.895410
LBFGS: 13 22:07:20 -329.277377 0.905304
LBFGS: 14 22:07:20 -329.330466 1.048305
LBFGS: 15 22:07:21 -329.387881 1.124757
LBFGS: 16 22:07:21 -329.440226 0.953406
LBFGS: 17 22:07:21 -329.474669 0.911009
LBFGS: 18 22:07:21 -329.504439 0.859300
LBFGS: 19 22:07:21 -329.554879 0.875937
LBFGS: 20 22:07:22 -329.598908 0.756896
Step Time Energy fmax
LBFGS: 0 22:07:22 -328.863085 625930.437500
LBFGS: 1 22:07:22 -328.552697 3.060331
LBFGS: 2 22:07:22 -328.839333 1.523453
LBFGS: 3 22:07:22 -329.041113 1.199497
LBFGS: 4 22:07:23 -329.107102 1.096281
LBFGS: 5 22:07:23 -329.224913 0.837819
LBFGS: 6 22:07:23 -329.260363 0.691485
LBFGS: 7 22:07:23 -329.278834 0.673669
LBFGS: 8 22:07:23 -329.304331 0.648844
LBFGS: 9 22:07:24 -329.331734 0.685819
LBFGS: 10 22:07:24 -329.357968 0.592008
LBFGS: 11 22:07:24 -329.376376 0.591883
LBFGS: 12 22:07:24 -329.403107 0.692595
LBFGS: 13 22:07:24 -329.444167 0.727092
LBFGS: 14 22:07:25 -329.501429 0.936776
LBFGS: 15 22:07:25 -329.571450 0.991950
LBFGS: 16 22:07:25 -329.639647 0.690751
LBFGS: 17 22:07:25 -329.680977 0.671066
LBFGS: 18 22:07:25 -329.716124 0.643156
LBFGS: 19 22:07:26 -329.758154 0.603842
LBFGS: 20 22:07:26 -329.790927 0.566191
Step Time Energy fmax
LBFGS: 0 22:07:26 -329.256671 465383.687500
LBFGS: 1 22:07:26 -328.957602 2.695088
LBFGS: 2 22:07:26 -329.155676 1.376816
LBFGS: 3 22:07:27 -329.269343 1.165007
LBFGS: 4 22:07:27 -329.306017 1.065925
LBFGS: 5 22:07:27 -329.371356 0.876038
LBFGS: 6 22:07:27 -329.390647 0.838882
LBFGS: 7 22:07:27 -329.402035 0.834536
LBFGS: 8 22:07:28 -329.413823 0.840992
LBFGS: 9 22:07:28 -329.432667 0.857738
LBFGS: 10 22:07:28 -329.450076 0.870691
LBFGS: 11 22:07:28 -329.461295 0.862536
LBFGS: 12 22:07:28 -329.472392 0.837296
LBFGS: 13 22:07:29 -329.488829 0.792343
LBFGS: 14 22:07:29 -329.510996 0.739821
LBFGS: 15 22:07:29 -329.536807 0.700600
LBFGS: 16 22:07:29 -329.560282 0.681150
LBFGS: 17 22:07:29 -329.577324 0.668548
LBFGS: 18 22:07:30 -329.597157 0.653068
LBFGS: 19 22:07:30 -329.630021 0.694851
LBFGS: 20 22:07:30 -329.657664 0.630607
Step Time Energy fmax
LBFGS: 0 22:07:30 -329.205535 788509.812500
LBFGS: 1 22:07:30 -328.900048 2.789602
LBFGS: 2 22:07:31 -329.136260 1.368142
LBFGS: 3 22:07:31 -329.279730 1.120531
LBFGS: 4 22:07:31 -329.322102 1.069309
LBFGS: 5 22:07:31 -329.399754 0.938374
LBFGS: 6 22:07:31 -329.419604 0.892125
LBFGS: 7 22:07:32 -329.430052 0.872548
LBFGS: 8 22:07:32 -329.442355 0.856926
LBFGS: 9 22:07:32 -329.458224 0.841131
LBFGS: 10 22:07:32 -329.472586 0.831186
LBFGS: 11 22:07:32 -329.483059 0.830186
LBFGS: 12 22:07:33 -329.496516 0.834139
LBFGS: 13 22:07:33 -329.517319 0.843171
LBFGS: 14 22:07:33 -329.545273 0.856114
LBFGS: 15 22:07:33 -329.576462 0.867460
LBFGS: 16 22:07:33 -329.604477 0.868805
LBFGS: 17 22:07:34 -329.626692 0.857563
LBFGS: 18 22:07:34 -329.658515 0.832161
LBFGS: 19 22:07:34 -329.704732 0.891778
LBFGS: 20 22:07:34 -329.743544 0.753837
Step Time Energy fmax
LBFGS: 0 22:07:34 -329.211251 528382.812500
LBFGS: 1 22:07:35 -329.028540 2.553465
LBFGS: 2 22:07:35 -329.248066 1.278247
LBFGS: 3 22:07:35 -329.377024 0.976450
LBFGS: 4 22:07:35 -329.416239 0.851659
LBFGS: 5 22:07:35 -329.486296 0.740130
LBFGS: 6 22:07:36 -329.505734 0.584402
LBFGS: 7 22:07:36 -329.517529 0.572419
LBFGS: 8 22:07:36 -329.532534 0.551963
LBFGS: 9 22:07:36 -329.555132 0.602492
LBFGS: 10 22:07:36 -329.579930 0.508007
LBFGS: 11 22:07:37 -329.599068 0.577287
LBFGS: 12 22:07:37 -329.623372 0.686766
LBFGS: 13 22:07:37 -329.658999 0.721361
LBFGS: 14 22:07:37 -329.706112 0.855360
LBFGS: 15 22:07:37 -329.763514 0.879543
LBFGS: 16 22:07:38 -329.818570 0.571633
LBFGS: 17 22:07:38 -329.848897 0.504646
LBFGS: 18 22:07:38 -329.866664 0.476499
LBFGS: 19 22:07:38 -329.887970 0.436936
LBFGS: 20 22:07:38 -329.909949 0.658028
Step Time Energy fmax
LBFGS: 0 22:07:39 -329.135081 1100458.750000
LBFGS: 1 22:07:39 -328.957814 2.553975
LBFGS: 2 22:07:39 -329.194333 1.264629
LBFGS: 3 22:07:39 -329.333146 1.094323
LBFGS: 4 22:07:39 -329.386752 1.012332
LBFGS: 5 22:07:40 -329.485751 1.032412
LBFGS: 6 22:07:40 -329.533238 0.790211
LBFGS: 7 22:07:40 -329.557347 0.756304
LBFGS: 8 22:07:40 -329.586104 0.728163
LBFGS: 9 22:07:40 -329.626942 0.718104
LBFGS: 10 22:07:41 -329.677243 0.725453
LBFGS: 11 22:07:41 -329.712832 0.675993
LBFGS: 12 22:07:41 -329.750975 0.786454
LBFGS: 13 22:07:41 -329.794159 0.830749
LBFGS: 14 22:07:41 -329.849024 0.836063
LBFGS: 15 22:07:42 -329.912993 0.942718
LBFGS: 16 22:07:42 -329.970810 0.656514
LBFGS: 17 22:07:42 -329.998074 0.602976
LBFGS: 18 22:07:42 -330.008973 0.605560
LBFGS: 19 22:07:42 -330.018191 0.595315
LBFGS: 20 22:07:43 -330.032773 0.621279
Step Time Energy fmax
LBFGS: 0 22:07:43 -328.733918 711683.312500
LBFGS: 1 22:07:43 -328.304774 3.188456
LBFGS: 2 22:07:43 -328.594645 1.672457
LBFGS: 3 22:07:43 -328.808920 1.369197
LBFGS: 4 22:07:44 -328.889103 1.239159
LBFGS: 5 22:07:44 -329.004447 0.975695
LBFGS: 6 22:07:44 -329.057473 0.905262
LBFGS: 7 22:07:44 -329.075184 0.872653
LBFGS: 8 22:07:44 -329.092639 0.838918
LBFGS: 9 22:07:45 -329.122584 0.789448
LBFGS: 10 22:07:45 -329.165371 0.907821
LBFGS: 11 22:07:45 -329.203461 0.923324
LBFGS: 12 22:07:45 -329.232241 0.956610
LBFGS: 13 22:07:45 -329.271523 0.952237
LBFGS: 14 22:07:46 -329.323906 0.952343
LBFGS: 15 22:07:46 -329.391139 1.183091
LBFGS: 16 22:07:46 -329.468173 1.050320
LBFGS: 17 22:07:46 -329.537531 0.875770
LBFGS: 18 22:07:46 -329.574791 0.869608
LBFGS: 19 22:07:47 -329.611559 0.836367
LBFGS: 20 22:07:47 -329.663678 0.786337
Step Time Energy fmax
LBFGS: 0 22:07:47 -328.812855 497899.125000
LBFGS: 1 22:07:47 -328.456025 2.905259
LBFGS: 2 22:07:47 -328.741614 1.934515
LBFGS: 3 22:07:48 -328.913653 1.499305
LBFGS: 4 22:07:48 -328.977006 1.279219
LBFGS: 5 22:07:48 -329.076472 1.040522
LBFGS: 6 22:07:48 -329.111775 1.063200
LBFGS: 7 22:07:48 -329.129630 1.064095
LBFGS: 8 22:07:49 -329.147309 1.031809
LBFGS: 9 22:07:49 -329.175231 0.961009
LBFGS: 10 22:07:49 -329.215605 0.840180
LBFGS: 11 22:07:49 -329.247046 0.747540
LBFGS: 12 22:07:49 -329.272574 0.767714
LBFGS: 13 22:07:50 -329.305581 0.747304
LBFGS: 14 22:07:50 -329.349579 0.957906
LBFGS: 15 22:07:50 -329.403332 1.142795
LBFGS: 16 22:07:50 -329.457718 0.931765
LBFGS: 17 22:07:50 -329.496237 0.872125
LBFGS: 18 22:07:51 -329.518536 0.872490
LBFGS: 19 22:07:51 -329.553341 0.838134
LBFGS: 20 22:07:51 -329.597930 0.930428
Step Time Energy fmax
LBFGS: 0 22:07:51 -329.314454 544616.812500
LBFGS: 1 22:07:51 -328.734799 573092.312500
LBFGS: 2 22:07:52 -327.841198 3.688785
LBFGS: 3 22:07:52 -328.167605 3.116565
LBFGS: 4 22:07:52 -328.475448 2.249623
LBFGS: 5 22:07:52 -328.564036 2.099959
LBFGS: 6 22:07:52 -328.686108 1.831953
LBFGS: 7 22:07:53 -328.715973 1.713903
LBFGS: 8 22:07:53 -328.740027 1.581694
LBFGS: 9 22:07:53 -328.769881 1.391427
LBFGS: 10 22:07:53 -328.798771 1.290855
LBFGS: 11 22:07:53 -328.813347 1.334507
LBFGS: 12 22:07:54 -328.822611 1.316703
LBFGS: 13 22:07:54 -328.834569 1.239261
LBFGS: 14 22:07:54 -328.848746 1.114897
LBFGS: 15 22:07:54 -328.862816 1.133684
LBFGS: 16 22:07:54 -328.873423 1.118309
LBFGS: 17 22:07:55 -328.881697 1.077454
LBFGS: 18 22:07:55 -328.893481 1.012756
LBFGS: 19 22:07:55 -328.911662 0.958144
LBFGS: 20 22:07:55 -328.929621 1.055701
Step Time Energy fmax
LBFGS: 0 22:07:55 -329.228394 1224425.625000
LBFGS: 1 22:07:56 -329.037942 2.938863
LBFGS: 2 22:07:56 -329.256732 1.495567
LBFGS: 3 22:07:56 -329.387908 0.961751
LBFGS: 4 22:07:56 -329.428940 0.908328
LBFGS: 5 22:07:57 -329.497887 0.617262
LBFGS: 6 22:07:57 -329.514990 0.594792
LBFGS: 7 22:07:57 -329.524981 0.581798
LBFGS: 8 22:07:57 -329.536845 0.568258
LBFGS: 9 22:07:57 -329.554179 0.560320
LBFGS: 10 22:07:58 -329.572331 0.531524
LBFGS: 11 22:07:58 -329.586812 0.521795
LBFGS: 12 22:07:58 -329.604107 0.526027
LBFGS: 13 22:07:58 -329.630824 0.643668
LBFGS: 14 22:07:58 -329.667613 0.792460
LBFGS: 15 22:07:59 -329.710484 0.797748
LBFGS: 16 22:07:59 -329.750384 0.507676
LBFGS: 17 22:07:59 -329.774035 0.499846
LBFGS: 18 22:07:59 -329.792779 0.474715
LBFGS: 19 22:07:59 -329.825228 0.581874
LBFGS: 20 22:08:00 -329.853341 0.459280
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.032772588908, 'forces': [[0.0,... |
1 | {'atoms': (Atom('Cu', [np.float64(-1.300046521... | (Atom('Cu', [np.float64(-1.3000465215529715), ... | {'energy': -329.90994887369806, 'forces': [[0.... |
2 | {'atoms': (Atom('Cu', [np.float64(-1.300046521... | (Atom('Cu', [np.float64(-1.3000465215529715), ... | {'energy': -329.85334067362476, 'forces': [[0.... |
3 | {'atoms': (Atom('Cu', [np.float64(-1.300046521... | (Atom('Cu', [np.float64(-1.3000465215529715), ... | {'energy': -329.7909265043133, 'forces': [[0.0... |
4 | {'atoms': (Atom('Cu', [np.float64(-1.300046521... | (Atom('Cu', [np.float64(-1.3000465215529715), ... | {'energy': -329.74354414957696, 'forces': [[0.... |
5 | {'atoms': (Atom('Cu', [np.float64(-1.300046521... | (Atom('Cu', [np.float64(-1.3000465215529715), ... | {'energy': -329.6636777402752, 'forces': [[0.0... |
6 | {'atoms': (Atom('Cu', [np.float64(-1.300046521... | (Atom('Cu', [np.float64(-1.3000465215529715), ... | {'energy': -329.6576638700361, 'forces': [[0.0... |
7 | {'atoms': (Atom('Cu', [np.float64(-1.300046521... | (Atom('Cu', [np.float64(-1.3000465215529715), ... | {'energy': -329.59890799540216, 'forces': [[0.... |
8 | {'atoms': (Atom('Cu', [np.float64(-1.300046521... | (Atom('Cu', [np.float64(-1.3000465215529715), ... | {'energy': -329.5979295255535, 'forces': [[0.0... |
9 | {'atoms': (Atom('Cu', [np.float64(-1.300046521... | (Atom('Cu', [np.float64(-1.3000465215529715), ... | {'energy': -328.9296212674969, '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}/")