|

FAIRChem v2 UMA for Multidomain Atomistic Simulation across Molecules, Catalysts, Materials, Vibrations, and Molecular Dynamics

In this tutorial, we discover FAIRChem v2 and the UMA common machine-learning interatomic potential as a unified framework for atomistic simulation across molecular chemistry, catalysis, and inorganic supplies. We configure an atmosphere, authenticate with Hugging Face to entry the gated UMA mannequin weights, and initialize task-specific calculators for the omol, oc20, and omat domains. We then apply the identical pretrained potential to a broad set of computational chemistry workflows, together with single-point power and power prediction, molecular geometry optimization, spin-state comparability, reaction-energy estimation, vibrational evaluation, floor adsorption, crystal-cell leisure, equation-of-state becoming, molecular dynamics, and potential-energy floor scanning. Throughout the tutorial, we combine FAIRChem with the Atomic Simulation Environment to handle atomic constructions, optimizers, constraints, thermodynamic calculations, and trajectory evaluation whereas utilizing GPU acceleration each time it’s obtainable.

import importlib.util, subprocess, sys, os
def _pip(*pkgs):
   subprocess.check_call([sys.executable, "-m", "pip", "install", "-q", *pkgs])
if importlib.util.find_spec("fairchem") is None:
   print(">> Installing fairchem-core, ase, and helpers (takes ~2-4 min)...")
   _pip("fairchem-core", "ase", "matplotlib", "huggingface_hub")
   print(">> Installation finished.")
else:
   print(">> fairchem already put in.")
from huggingface_hub import login, whoami
def hf_authenticate():
   token = None
   attempt:
       from google.colab import userdata
       token = userdata.get("HF_TOKEN")
   besides Exception:
       cross
   token = token or os.environ.get("HF_TOKEN")
   attempt:
       whoami()
       print(">> Already authenticated with Hugging Face.")
       return
   besides Exception:
       cross
   if token is None:
       from getpass import getpass
       token = getpass("Paste your Hugging Face entry token: ").strip()
   login(token=token)
   print(">> Hugging Face login OK.")
hf_authenticate()
import numpy as np
import torch
import matplotlib.pyplot as plt
from fairchem.core import pretrained_mlip, FAIRChemCalculator
DEVICE = "cuda" if torch.cuda.is_available() else "cpu"
print(f">> Using system: {DEVICE}")
MODEL = "uma-s-1p2"
predictor = pretrained_mlip.get_predict_unit(MODEL, system=DEVICE)
calc_mol  = FAIRChemCalculator(predictor, task_name="omol")
calc_cat  = FAIRChemCalculator(predictor, task_name="oc20")
calc_mat  = FAIRChemCalculator(predictor, task_name="omat")
print(f">> Loaded {MODEL} with omol / oc20 / omat calculators.")

We set up the required FAIRChem, ASE, visualization, and Hugging Face dependencies whereas guaranteeing the setup stays secure to rerun in Google Colab. We authenticate with Hugging Face to entry the gated UMA mannequin weights and mechanically detect whether or not GPU acceleration is obtainable. We then load the UMA predictor and create separate calculators for molecular, catalysis, and supplies simulations.

from ase.construct import molecule
from ase import Atoms
print("n" + "="*70)
print("SECTION 2: Single-point energetics of water (omol activity)")
print("="*70)
h2o = molecule("H2O")
h2o.data.replace({"cost": 0, "spin": 1})
h2o.calc = calc_mol
E_h2o = h2o.get_potential_energy()
F_h2o = h2o.get_forces()
print(f"E(H2O)            = {E_h2o:.4f} eV")
print(f"Max |power|       = {np.abs(F_h2o).max():.4f} eV/A")
def atom_energy(image, spin):
   a = Atoms(image, positions=[[0, 0, 0]])
   a.data.replace({"cost": 0, "spin": spin})
   a.calc = calc_mol
   return a.get_potential_energy()
E_O = atom_energy("O", spin=3)
E_H = atom_energy("H", spin=2)
E_atomization = -(E_h2o - E_O - 2 * E_H)
print(f"Atomization power of H2O = {E_atomization:.3f} eV "
     f"(experiment ~ 9.5 eV incl. ZPE results)")
from ase.optimize import LBFGS
print("n" + "="*70)
print("SECTION 3: Relaxing a intentionally distorted water molecule")
print("="*70)
h2o_bad = molecule("H2O")
h2o_bad.positions[1] += [0.25, -0.10, 0.05]
h2o_bad.data.replace({"cost": 0, "spin": 1})
h2o_bad.calc = calc_mol
choose = LBFGS(h2o_bad, logfile=None)
energies_opt = []
choose.connect(lambda: energies_opt.append(h2o_bad.get_potential_energy()))
choose.run(fmax=0.01, steps=200)
d_OH = h2o_bad.get_distance(0, 1)
ang  = h2o_bad.get_angle(1, 0, 2)
print(f"Converged in {choose.get_number_of_steps()} steps")
print(f"O-H bond size   = {d_OH:.3f} A   (expt ~0.958 A)")
print(f"H-O-H angle       = {ang:.1f} deg (expt ~104.5 deg)")
plt.determine(figsize=(5, 3.2))
plt.plot(energies_opt, "o-")
plt.xlabel("Optimizer step"); plt.ylabel("Energy (eV)")
plt.title("H2O geometry optimization"); plt.tight_layout(); plt.present()

We use the molecular UMA calculator to guage the power, atomic forces, and atomization power of a water molecule. We outline remoted hydrogen and oxygen reference atoms with the right spin multiplicities to assemble the atomization-energy calculation. We then distort the water geometry, chill out it with the LBFGS optimizer, and analyze the converged bond size, bond angle, and power trajectory.

print("n" + "="*70)
print("SECTION 4: CH2 singlet-triplet hole (UMA is spin-aware!)")
print("="*70)
singlet = molecule("CH2_s1A1d"); singlet.data.replace({"cost": 0, "spin": 1})
triplet = molecule("CH2_s3B1d"); triplet.data.replace({"cost": 0, "spin": 3})
singlet.calc = FAIRChemCalculator(predictor, task_name="omol")
triplet.calc = FAIRChemCalculator(predictor, task_name="omol")
hole = triplet.get_potential_energy() - singlet.get_potential_energy()
print(f"E(triplet) - E(singlet) = {hole:.3f} eV  "
     f"(detrimental => triplet floor state; expt ~ -0.39 eV)")
print("n" + "="*70)
print("SECTION 5: Reaction power of CH4 + 2 O2 -> CO2 + 2 H2O")
print("="*70)
def relaxed_energy(identify, spin=1):
   m = molecule(identify)
   m.data.replace({"cost": 0, "spin": spin})
   m.calc = FAIRChemCalculator(predictor, task_name="omol")
   LBFGS(m, logfile=None).run(fmax=0.02, steps=200)
   return m.get_potential_energy()
E = {
   "CH4": relaxed_energy("CH4"),
   "O2":  relaxed_energy("O2", spin=3),
   "CO2": relaxed_energy("CO2"),
   "H2O": relaxed_energy("H2O"),
}
dE_rxn = (E["CO2"] + 2*E["H2O"]) - (E["CH4"] + 2*E["O2"])
print(f"Delta E (digital) = {dE_rxn:.2f} eV = {dE_rxn*96.485:.0f} kJ/mol")
print("Experimental combustion enthalpy ~ -890 kJ/mol (ZPE/thermal not included right here)")
from ase.vibrations import Vibrations
print("n" + "="*70)
print("SECTION 6: Vibrational frequencies of relaxed H2O")
print("="*70)
vib = Vibrations(h2o_bad, identify="vib_h2o")
vib.run()
freqs = np.actual(vib.get_frequencies())
real_modes = [f for f in freqs if f > 200]
print("Vibrational modes (cm^-1):", ", ".be part of(f"{f:.0f}" for f in real_modes))
print("Experimental H2O: 1595 (bend), 3657 (sym stretch), 3756 (asym stretch)")
print(f"Zero-point power = {vib.get_zero_point_energy():.3f} eV")
vib.clear()

We examine the singlet and triplet digital states of methylene to calculate its spin-state power hole. We chill out methane, oxygen, carbon dioxide, and water earlier than combining their predicted energies to estimate the digital response power of methane combustion. We additionally carry out a finite-difference vibrational evaluation of relaxed water to acquire its normal-mode frequencies and zero-point power.

from ase.construct import fcc100, add_adsorbate
from ase.constraints import RepairAtoms
print("n" + "="*70)
print("SECTION 7: CO/Cu(100) leisure + adsorption power (oc20 activity)")
print("="*70)
slab = fcc100("Cu", dimension=(3, 3, 3), vacuum=8.0, periodic=True)
slab.set_constraint(RepairAtoms(masks=[a.tag > 1 for a in slab]))
add_adsorbate(slab, molecule("CO"), peak=2.0, place="bridge")
slab.calc = calc_cat
choose = LBFGS(slab, logfile=None)
choose.run(fmax=0.05, steps=300)
E_slab_ads = slab.get_potential_energy()
print(f"Relaxed CO/Cu(100) in {choose.get_number_of_steps()} steps, "
     f"E = {E_slab_ads:.3f} eV")
clear = fcc100("Cu", dimension=(3, 3, 3), vacuum=8.0, periodic=True)
clear.set_constraint(RepairAtoms(masks=[a.tag > 1 for a in clean]))
clear.calc = FAIRChemCalculator(predictor, task_name="oc20")
LBFGS(clear, logfile=None).run(fmax=0.05, steps=300)
E_clean = clear.get_potential_energy()
co = molecule("CO"); co.data.replace({"cost": 0, "spin": 1})
co.calc = FAIRChemCalculator(predictor, task_name="omol")
LBFGS(co, logfile=None).run(fmax=0.02, steps=100)
E_co = co.get_potential_energy()
E_ads = E_slab_ads - E_clean - E_co
print(f"E(clear slab) = {E_clean:.3f} eV, E(CO fuel) = {E_co:.3f} eV")
print(f"Adsorption power (naive cycle) = {E_ads:.3f} eV")
print("(oc20 makes use of its personal DFT reference scheme; for publication-grade numbers")
print(" hold all species inside a constant activity/reference framework.)")

We assemble a periodic Cu(100) slab, place a carbon monoxide molecule at a bridge adsorption web site, and constrain the decrease copper layers. We chill out the adsorbate–floor system with the OC20 calculator and individually optimize the clear slab and gas-phase carbon monoxide references. We then consider a pedagogical adsorption-energy cycle whereas recognizing that OC20 makes use of a task-specific power reference conference.

from ase.construct import bulk
from ase.optimize import FIRE
from ase.filters import FrechetCellFilter
from ase.eos import EquationOfState
print("n" + "="*70)
print("SECTION 8: BCC iron — full cell leisure and bulk modulus (omat)")
print("="*70)
fe = bulk("Fe", "bcc", a=2.9)
fe.calc = calc_mat
FIRE(FrechetCellFilter(fe), logfile=None).run(fmax=0.02, steps=300)
a_relaxed = fe.cell.cellpar()[0]
print(f"Relaxed BCC Fe lattice fixed = {a_relaxed:.3f} A (expt ~2.866 A)")
volumes, energies = [], []
cell0 = fe.get_cell()
for scale in np.linspace(0.94, 1.06, 9):
   s = fe.copy()
   s.set_cell(cell0 * scale, scale_atoms=True)
   s.calc = FAIRChemCalculator(predictor, task_name="omat")
   volumes.append(s.get_volume())
   energies.append(s.get_potential_energy())
eos = EquationOfState(volumes, energies, eos="birchmurnaghan")
v0, e0, B = eos.match()
from ase.models import GPa as _GPa
B_GPa = B / _GPa
print(f"Equilibrium quantity = {v0:.2f} A^3/cell")
print(f"Bulk modulus       = {B_GPa:.0f} GPa (expt ~170 GPa for Fe)")
plt.determine(figsize=(5, 3.2))
plt.plot(volumes, energies, "o", label="UMA factors")
vfit = np.linspace(min(volumes), max(volumes), 100)
plt.plot(vfit, [eos.func(v, *eos.eos_parameters) for v in vfit], "-", label="BM match")
plt.xlabel("Volume (A^3)"); plt.ylabel("Energy (eV)")
plt.title("BCC Fe equation of state"); plt.legend(); plt.tight_layout(); plt.present()
from ase import models
from ase.md.langevin import Langevin
from ase.md.velocitydistribution import MaxwellBoltzmannDistribution
print("n" + "="*70)
print("SECTION 9: 0.5 ps Langevin MD of a water molecule at 300 Ok")
print("="*70)
md_atoms = molecule("H2O")
md_atoms.data.replace({"cost": 0, "spin": 1})
seed = int(np.random.randint(0, np.iinfo(np.int32).max))
md_predictor = pretrained_mlip.get_predict_unit(MODEL, system=DEVICE, seed=seed)
md_atoms.calc = FAIRChemCalculator(md_predictor, task_name="omol")
MaxwellBoltzmannDistribution(md_atoms, temperature_K=300)
dyn = Langevin(md_atoms, timestep=0.5 * models.fs,
              temperature_K=300, friction=0.01 / models.fs)
occasions, temps, epots, d_oh1 = [], [], [], []
def log_md():
   t = dyn.get_number_of_steps() * 0.5
   occasions.append(t)
   temps.append(md_atoms.get_temperature())
   epots.append(md_atoms.get_potential_energy())
   d_oh1.append(md_atoms.get_distance(0, 1))
dyn.connect(log_md, interval=5)
dyn.run(steps=1000)
print(f"MD finished. <T> = {np.imply(temps[10:]):.0f} Ok, "
     f"<d(O-H)> = {np.imply(d_oh1):.3f} A")
fig, ax = plt.subplots(1, 3, figsize=(12, 3.2))
ax[0].plot(occasions, temps);  ax[0].set_title("Temperature (Ok)")
ax[1].plot(occasions, epots);  ax[1].set_title("Potential power (eV)")
ax[2].plot(occasions, d_oh1);  ax[2].set_title("O-H bond size (A)")
for a in ax: a.set_xlabel("time (fs)")
plt.tight_layout(); plt.present()

We chill out the atomic positions and simulation cell of BCC iron utilizing the materials-domain UMA calculator and a Frechet cell filter. We pattern energies across a spread of compressed and expanded volumes, match a Birch–Murnaghan equation of state, and estimate the equilibrium quantity and bulk modulus. We additionally run Langevin molecular dynamics for water at 300 Ok and monitor its temperature, potential power, and O–H bond size over time.

print("n" + "="*70)
print("SECTION 10: O-H bond stretch scan in water")
print("="*70)
distances = np.linspace(0.7, 2.5, 25)
pes = []
for d in distances:
   a = molecule("H2O")
   a.data.replace({"cost": 0, "spin": 1})
   vec = a.positions[1] - a.positions[0]
   a.positions[1] = a.positions[0] + vec / np.linalg.norm(vec) * d
   a.calc = FAIRChemCalculator(predictor, task_name="omol")
   pes.append(a.get_potential_energy())
pes = np.array(pes) - min(pes)
plt.determine(figsize=(5.5, 3.4))
plt.plot(distances, pes, "o-")
plt.axvline(0.958, ls="--", c="grey", label="expt r_e")
plt.xlabel("O-H distance (A)"); plt.ylabel("Relative power (eV)")
plt.title("O-H stretch PES from UMA"); plt.legend()
plt.tight_layout(); plt.present()
print("n" + "="*70)
print("TUTORIAL COMPLETE!")
print("="*70)
print("""
Next steps to discover:
 * Swap MODEL to "uma-m-1p1" for greater accuracy (wants extra GPU reminiscence).
 * Try task_name="odac" with a MOF CIF, or "omc" for molecular crystals.
 * Larger MD: fairchem helps multi-GPU inference by way of staff=N
   (pip set up fairchem-core[extras]).
 * Docs: https://fair-chem.github.io/
""")

We scan the potential-energy floor of water by systematically stretching one O–H bond across a specific distance vary. We consider the molecular power at every geometry, normalize the energies relative to the minimal, and visualize the ensuing dissociation profile. We conclude the tutorial by figuring out higher-accuracy UMA fashions, further chemical domains, and bigger multi-GPU simulations as potential extensions.

In conclusion, we constructed an entire atomistic simulation workflow round FAIRChem v2 and demonstrated how UMA gives a shared discovered potential across chemically distinct domains with out requiring a separate mannequin for each activity. We used molecular calculations to guage energies, forces, atomization habits, spin gaps, response energetics, vibrational modes, and bond-stretch profiles; we used the catalysis area to chill out CO on a Cu(100) floor and look at adsorption energetics; and we used the supplies area to chill out BCC iron and estimate its bulk modulus from an equation-of-state match. We additionally ran Langevin molecular dynamics to watch finite-temperature structural and energetic fluctuations over time. By combining UMA inference with ASE construction builders, optimizers, filters, vibrational instruments, and molecular-dynamics utilities, we established a reusable basis for extending the workflow to bigger molecules, catalytic interfaces, crystalline supplies, metal-organic frameworks, molecular crystals, and higher-accuracy UMA mannequin variants.


Check out the Full Code hereAlso, be happy to observe us on Twitter and don’t overlook to hitch our 150k+ML SubReddit and Subscribe to our Newsletter. Wait! are you on telegram? now you can join us on telegram as well.

Need to companion with us for selling your GitHub Repo OR Hugging Face Page OR Product Release OR Webinar and many others.? Connect with us

The put up FAIRChem v2 UMA for Multidomain Atomistic Simulation across Molecules, Catalysts, Materials, Vibrations, and Molecular Dynamics appeared first on MarkTechPost.

Similar Posts