From 9bf9087c25ff0c5df8eea954ff95f7d289f82358 Mon Sep 17 00:00:00 2001 From: Timur Bazhirov Date: Tue, 23 Sep 2025 16:47:40 -0700 Subject: [PATCH 1/4] wip: initial Mtrsm examples --- other/experiments/mtrsm/README.md | 19 +++++++++++++ other/experiments/mtrsm/minimal_test.py | 16 +++++++++++ other/experiments/mtrsm/phonon_dispersion.py | 26 +++++++++++++++++ .../mtrsm/structure_optimization.py | 23 +++++++++++++++ other/experiments/mtrsm/vacancy_energies.py | 28 +++++++++++++++++++ 5 files changed, 112 insertions(+) create mode 100644 other/experiments/mtrsm/README.md create mode 100644 other/experiments/mtrsm/minimal_test.py create mode 100644 other/experiments/mtrsm/phonon_dispersion.py create mode 100644 other/experiments/mtrsm/structure_optimization.py create mode 100644 other/experiments/mtrsm/vacancy_energies.py diff --git a/other/experiments/mtrsm/README.md b/other/experiments/mtrsm/README.md new file mode 100644 index 00000000..ac1c2e07 --- /dev/null +++ b/other/experiments/mtrsm/README.md @@ -0,0 +1,19 @@ +# Installation + +As below. With `pyenv`. + +```bash +pyenv local 3.10.13 +python -m venv .venv-3.10.13 +source .venv-3.10.13/bin/activate +pip install mattersim +``` + +# Usage + +As below. + +```bash +source .venv-3.10.13/bin/activate +python +``` diff --git a/other/experiments/mtrsm/minimal_test.py b/other/experiments/mtrsm/minimal_test.py new file mode 100644 index 00000000..c96ff3e8 --- /dev/null +++ b/other/experiments/mtrsm/minimal_test.py @@ -0,0 +1,16 @@ +import torch +from loguru import logger +from ase.build import bulk +from ase.units import GPa +from mattersim.forcefield import MatterSimCalculator + +device = "cuda" if torch.cuda.is_available() else "cpu" +logger.info(f"Running MatterSim on {device}") + +si = bulk("Si", "diamond", a=5.43) +si.calc = MatterSimCalculator(device=device) +logger.info(f"Energy (eV) = {si.get_potential_energy()}") +logger.info(f"Energy per atom (eV/atom) = {si.get_potential_energy()/len(si)}") +logger.info(f"Forces of first atom (eV/A) = {si.get_forces()[0]}") +logger.info(f"Stress[0][0] (eV/A^3) = {si.get_stress(voigt=False)[0][0]}") +logger.info(f"Stress[0][0] (GPa) = {si.get_stress(voigt=False)[0][0] / GPa}") diff --git a/other/experiments/mtrsm/phonon_dispersion.py b/other/experiments/mtrsm/phonon_dispersion.py new file mode 100644 index 00000000..a757e402 --- /dev/null +++ b/other/experiments/mtrsm/phonon_dispersion.py @@ -0,0 +1,26 @@ +import numpy as np +from ase.build import bulk +from ase.units import GPa +from ase.visualize import view +from mattersim.forcefield.potential import MatterSimCalculator +from mattersim.applications.phonon import PhononWorkflow + +# initialize the structure of silicon +si = bulk("Si") + +# attach the calculator to the atoms object +si.calc = MatterSimCalculator() + +ph = PhononWorkflow( + atoms=si, + find_prim = False, + work_dir = "./tmp/phonon_si_example", + amplitude = 0.01, + supercell_matrix = np.diag([4,4,4]), +) + +has_imag, phonons = ph.run() +print(f"Has imaginary phonon: {has_imag}") +print(f"Phonon frequencies: {phonons}") + + diff --git a/other/experiments/mtrsm/structure_optimization.py b/other/experiments/mtrsm/structure_optimization.py new file mode 100644 index 00000000..5c575480 --- /dev/null +++ b/other/experiments/mtrsm/structure_optimization.py @@ -0,0 +1,23 @@ +import numpy as np +from ase.build import bulk +from ase.units import GPa +from mattersim.forcefield.potential import MatterSimCalculator +from mattersim.applications.relax import Relaxer + +# initialize the structure of silicon +si = bulk("Si", "diamond", a=5.43) + +# perturb the structure +si.positions += 0.1 * np.random.randn(len(si), 3) + +# attach the calculator to the atoms object +si.calc = MatterSimCalculator() + +# initialize the relaxation object +relaxer = Relaxer( + optimizer="BFGS", # the optimization method + filter="ExpCellFilter", # filter to apply to the cell + constrain_symmetry=True, # whether to constrain the symmetry +) + +relaxed_structure = relaxer.relax(si, steps=500) diff --git a/other/experiments/mtrsm/vacancy_energies.py b/other/experiments/mtrsm/vacancy_energies.py new file mode 100644 index 00000000..b655d3bd --- /dev/null +++ b/other/experiments/mtrsm/vacancy_energies.py @@ -0,0 +1,28 @@ +import numpy as np +from ase.build import bulk +from mattersim.forcefield.potential import MatterSimCalculator +from mattersim.applications.relax import Relaxer + +# 1. Build and relax the pristine supercell +si_bulk = bulk("Si", "diamond", a=5.43).repeat((3, 3, 3)) +si_bulk.positions += 0.05 * np.random.randn(len(si_bulk), 3) +si_bulk.calc = MatterSimCalculator() + +relaxer = Relaxer(optimizer="BFGS", constrain_symmetry=True) +relaxer.relax(si_bulk, steps=500) # In-place relaxation +energy_bulk = si_bulk.get_potential_energy() +n_atoms_bulk = len(si_bulk) + +# 2. Create a supercell with a vacancy and relax +vacancy_idx = [n_atoms_bulk // 2] # Remove central atom for minimal defect interaction +si_vacancy = si_bulk.copy() +del si_vacancy[vacancy_idx] +si_vacancy.calc = MatterSimCalculator() +relaxer.relax(si_vacancy, steps=500) +energy_vacancy = si_vacancy.get_potential_energy() +n_atoms_vac = len(si_vacancy) + +# 3. Calculate vacancy formation energy +e_vacancy = energy_vacancy - (n_atoms_vac / n_atoms_bulk * energy_bulk) +print(f"Vacancy formation energy: {e_vacancy:.4f} eV") + From a21fffd1e528a0ebf718fd48ab818907785c84de Mon Sep 17 00:00:00 2001 From: VsevolodX Date: Mon, 6 Oct 2025 16:46:29 -0700 Subject: [PATCH 2/4] update: add NB for mattersim vacancy --- ...lculate_energy_for_vacancy_mattersim.ipynb | 269 ++++++++++++++++++ 1 file changed, 269 insertions(+) create mode 100644 other/experiments/mtrsm/calculate_energy_for_vacancy_mattersim.ipynb diff --git a/other/experiments/mtrsm/calculate_energy_for_vacancy_mattersim.ipynb b/other/experiments/mtrsm/calculate_energy_for_vacancy_mattersim.ipynb new file mode 100644 index 00000000..95088906 --- /dev/null +++ b/other/experiments/mtrsm/calculate_energy_for_vacancy_mattersim.ipynb @@ -0,0 +1,269 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "ba05a8b053f7ed1f", + "metadata": {}, + "source": [ + "# Calculate Vacancy Formation Energy in Silicon using MatterSim Potential\n", + "\n", + "This notebook demonstrates how to compute the vacancy formation energy in silicon using the MatterSim potential within the Mat3ra framework.\n", + "\n", + "## Methodology\n", + "\n", + "The vacancy formation energy is calculated using the following approach:\n", + "\n", + "1. **Create pristine supercell**: Build a silicon supercell from the unit cell and relax it using MatterSim potential\n", + "2. **Create vacancy**: Remove a single atom from the center of the supercell\n", + "3. **Relax vacancy structure**: Optimize the geometry of the defective supercell\n", + "4. **Calculate formation energy**: Compute the energy difference according to the formula:\n", + " \n", + " E_formation = E_vacancy - (N_vacancy/N_bulk) × E_bulk\n", + " \n", + " where:\n", + " - E_vacancy: Total energy of the supercell with vacancy\n", + " - E_bulk: Total energy of the pristine supercell\n", + " - N_vacancy: Number of atoms in vacancy supercell\n", + " - N_bulk: Number of atoms in pristine supercell" + ] + }, + { + "cell_type": "code", + "id": "39067e0f5d2c0ffb", + "metadata": {}, + "source": [ + "# Install required packages if not already installed\n", + "# INFO: if not installed correctly, clear environment and run `pip install .[mattersim]` in the terminal\n", + "try:\n", + " import mattersim\n", + "except ImportError:\n", + " import subprocess, sys\n", + " subprocess.run([sys.executable, \"-m\", \"pip\", \"install\", \".[mattersim]\", \"--quiet\"], check=False)\n", + " import mattersim" + ], + "outputs": [], + "execution_count": null + }, + { + "cell_type": "markdown", + "id": "ad71b3bcdb13e36a", + "metadata": {}, + "source": [ + "## 1. Prepare materials\n", + "### 1.1. Load material data" + ] + }, + { + "cell_type": "code", + "id": "2819d078b48216b9", + "metadata": {}, + "source": [ + "from mat3ra.standata.materials import Materials\n", + "from mat3ra.made.material import Material\n", + "\n", + "material = Material.create(Materials.get_by_name_first_match(\"Si\"))" + ], + "outputs": [], + "execution_count": null + }, + { + "cell_type": "markdown", + "id": "f011303e40ab0ee5", + "metadata": {}, + "source": [ + "### 1.2. Add vacancy to material" + ] + }, + { + "cell_type": "code", + "id": "b7e53d295d59fe89", + "metadata": {}, + "source": [ + "from mat3ra.made.tools.helpers import create_vacancy, create_supercell\n", + "\n", + "supercell = create_supercell(material, scaling_factor=[2,2,2])\n", + "material_with_vacancy = create_vacancy(supercell, coordinate=[0.5, 0.5, 0.5], placement_method=\"closest_site\")" + ], + "outputs": [], + "execution_count": null + }, + { + "cell_type": "markdown", + "id": "45c3dade8bcd52b3", + "metadata": {}, + "source": [ + "## 1.3. Visualize materials" + ] + }, + { + "cell_type": "code", + "id": "4b91a2ae59c983a3", + "metadata": {}, + "source": [ + "from utils.visualize import visualize_materials\n", + "\n", + "visualize_materials([material, supercell, material_with_vacancy], rotation=\"-90x,-90y\")" + ], + "outputs": [], + "execution_count": null + }, + { + "cell_type": "markdown", + "id": "8ce27a1c8864c29f", + "metadata": {}, + "source": [ + "## 2. Setup calculation\n", + "### 2.1. Convert to ASE atoms" + ] + }, + { + "cell_type": "code", + "id": "62b389e5458ce7c2", + "metadata": {}, + "source": [ + "from mat3ra.made.tools.convert import to_ase\n", + "\n", + "material_atoms = to_ase(material)\n", + "material_with_vacancy_atoms = to_ase(material_with_vacancy)" + ], + "outputs": [], + "execution_count": null + }, + { + "cell_type": "markdown", + "id": "c4c3b2a928a369ad", + "metadata": {}, + "source": [ + "### 2.2. Setup MatterSim calculator" + ] + }, + { + "cell_type": "code", + "id": "361cd269cb3e2db4", + "metadata": {}, + "source": [ + "from mattersim.forcefield.potential import MatterSimCalculator\n", + "from mattersim.applications.relax import Relaxer\n", + "\n", + "material_atoms.calc = MatterSimCalculator()\n", + "material_with_vacancy_atoms.calc = MatterSimCalculator()\n" + ], + "outputs": [], + "execution_count": null + }, + { + "cell_type": "markdown", + "id": "8119275fe9ffb1d8", + "metadata": {}, + "source": [ + "### 2.3. Relax structures" + ] + }, + { + "cell_type": "code", + "id": "48cbd2f29d0cbd2c", + "metadata": {}, + "source": [ + "relaxer = Relaxer(optimizer=\"BFGS\", constrain_symmetry=True)\n", + "relaxer.relax(material_atoms, steps=500) # In-place relaxation\n", + "relaxer.relax(material_with_vacancy_atoms, steps=500) # In-place relaxation\n" + ], + "outputs": [], + "execution_count": null + }, + { + "cell_type": "markdown", + "id": "116af57af6793bb", + "metadata": {}, + "source": [ + "### 2.4. Visualize relaxed structures" + ] + }, + { + "cell_type": "code", + "id": "6426cc56c5d51eef", + "metadata": {}, + "source": [ + "from mat3ra.made.tools.convert import from_ase\n", + "\n", + "relaxed_material = Material.create(from_ase(material_atoms))\n", + "relaxed_material_with_vacancy = Material.create(from_ase(material_with_vacancy_atoms))\n", + "\n", + "visualize_materials([relaxed_material, supercell, relaxed_material_with_vacancy], rotation=\"-90x,-90y\")" + ], + "outputs": [], + "execution_count": null + }, + { + "cell_type": "markdown", + "id": "7754f5d01964b082", + "metadata": {}, + "source": [ + "## 3. Calculate vacancy formation energy\n", + "\n", + "### 3.1. Get energies and atom counts" + ] + }, + { + "cell_type": "code", + "id": "2f4694776e7d1430", + "metadata": {}, + "source": [ + "energy_bulk = material_atoms.get_potential_energy()\n", + "n_atoms_bulk = len(material_atoms)\n", + "energy_vacancy = material_with_vacancy_atoms.get_potential_energy()\n", + "n_atoms_vac = len(material_with_vacancy_atoms)" + ], + "outputs": [], + "execution_count": null + }, + { + "cell_type": "markdown", + "id": "baae670f86c9b639", + "metadata": {}, + "source": [ + "### 3.2. Calculate vacancy formation energy" + ] + }, + { + "cell_type": "code", + "id": "430936d099f5b816", + "metadata": {}, + "source": [ + "e_vacancy = energy_vacancy - (n_atoms_vac / n_atoms_bulk * energy_bulk)\n", + "print(f\"Vacancy formation energy: {e_vacancy:.4f} eV\")\n" + ], + "outputs": [], + "execution_count": null + }, + { + "metadata": {}, + "cell_type": "code", + "source": "", + "id": "fa73a94409748e1c", + "outputs": [], + "execution_count": null + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 2 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython2", + "version": "2.7.6" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} From 0bd22ddda31de930936e96b3ea48ab56d2c041c4 Mon Sep 17 00:00:00 2001 From: VsevolodX Date: Mon, 6 Oct 2025 16:48:24 -0700 Subject: [PATCH 3/4] update: add ff installation option --- .../mtrsm/calculate_energy_for_vacancy_mattersim.ipynb | 4 ++-- pyproject.toml | 6 ++++++ 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/other/experiments/mtrsm/calculate_energy_for_vacancy_mattersim.ipynb b/other/experiments/mtrsm/calculate_energy_for_vacancy_mattersim.ipynb index 95088906..064959e6 100644 --- a/other/experiments/mtrsm/calculate_energy_for_vacancy_mattersim.ipynb +++ b/other/experiments/mtrsm/calculate_energy_for_vacancy_mattersim.ipynb @@ -33,12 +33,12 @@ "metadata": {}, "source": [ "# Install required packages if not already installed\n", - "# INFO: if not installed correctly, clear environment and run `pip install .[mattersim]` in the terminal\n", + "# INFO: if not installed correctly, clear environment and run `pip install .[forcefields]` in the terminal\n", "try:\n", " import mattersim\n", "except ImportError:\n", " import subprocess, sys\n", - " subprocess.run([sys.executable, \"-m\", \"pip\", \"install\", \".[mattersim]\", \"--quiet\"], check=False)\n", + " subprocess.run([sys.executable, \"-m\", \"pip\", \"install\", \".[forcefields]\", \"--quiet\"], check=False)\n", " import mattersim" ], "outputs": [], diff --git a/pyproject.toml b/pyproject.toml index 27b66a5b..f5feb0f7 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -37,6 +37,12 @@ docs = [ "mkdocs-jupyter>=0.24.1", "nbstripout>=0.6.1", ] +forcefields = [ + "mattersim>=1.2.0", + # WARNING: mattersim will automatically upgrade numpy and pymatgen + # This WILL conflict with base project dependencies + # Use this optional dependency ONLY in isolated environments +] [project.urls] homepage = "https://exabyte-io.github.io/api-examples" From 5b5b202098f8815caed778b5425e9b191ac8b2e7 Mon Sep 17 00:00:00 2001 From: Timur Bazhirov Date: Mon, 6 Oct 2025 17:13:39 -0700 Subject: [PATCH 4/4] Update README.md --- other/experiments/mtrsm/README.md | 32 ++++++++++++++++++++++++++----- 1 file changed, 27 insertions(+), 5 deletions(-) diff --git a/other/experiments/mtrsm/README.md b/other/experiments/mtrsm/README.md index ac1c2e07..12aef8db 100644 --- a/other/experiments/mtrsm/README.md +++ b/other/experiments/mtrsm/README.md @@ -1,6 +1,8 @@ -# Installation +## Usage -As below. With `pyenv`. +### With standalone scripts + +Install dependencies (using pyenv): ```bash pyenv local 3.10.13 @@ -9,11 +11,31 @@ source .venv-3.10.13/bin/activate pip install mattersim ``` -# Usage - -As below. +Run the script(s): ```bash source .venv-3.10.13/bin/activate python ``` + +### With JupyterLab + +Install dependencies: + +```bash +pyenv local 3.10.13 +python -m venv .venv-3.10.13 +source .venv-3.10.13/bin/activate +pip install "mat3ra-api-examples[forcefields]" +``` + +As below. With `pyenv`. + +```bash +pyenv local 3.10.13 +python -m venv .venv-3.10.13 +source .venv-3.10.13/bin/activate +pip install mattersim +``` + +See the included notebook.