Upload folder using huggingface_hub
Browse files- README.md +143 -0
- cif_file/demo.cif +36 -0
- model/__init__.py +33 -0
- model/matris.py +319 -0
- scripts/test_modularization.py +165 -0
- scripts/test_relaxation.py +44 -0
- weight/.MatRIS_10M_OAM.pth.tar.efileUploading +3 -0
- weight/MatRIS_10M_MP.pth.tar +3 -0
- weight/MatRIS_10M_OAM.pth.tar.efilePart +3 -0
README.md
ADDED
|
@@ -0,0 +1,143 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<p align="center">
|
| 2 |
+
<strong>
|
| 3 |
+
<span style="font-size: 30px;">MatRIS</span>
|
| 4 |
+
</strong>
|
| 5 |
+
</p>
|
| 6 |
+
|
| 7 |
+
|
| 8 |
+
# Model Introduction
|
| 9 |
+
|
| 10 |
+
MatRIS is a foundation model for materials representation and interaction simulation, short for Materials Representation and Interaction Simulation. It can predict energy, forces, stress, and magnetic moments for crystal structures, and supports structure relaxation based on ASE and pymatgen structure objects.
|
| 11 |
+
|
| 12 |
+
|
| 13 |
+
# Model Description
|
| 14 |
+
|
| 15 |
+
MatRIS is based on a graph neural network architecture and is trained on materials datasets such as OMat24 and MPTrj. It performs energy, force, stress, and magnetic-moment prediction and structure optimization for crystalline materials.
|
| 16 |
+
|
| 17 |
+
# Applicable Scenarios
|
| 18 |
+
|
| 19 |
+
| Scenario | Description |
|
| 20 |
+
| :---: | :---: |
|
| 21 |
+
| Crystal energy prediction | Input a CIF, pymatgen Structure, or ASE Atoms object and predict the system energy |
|
| 22 |
+
| Force and stress prediction | Provide force and stress estimates for structure relaxation, molecular dynamics, or downstream simulation |
|
| 23 |
+
| Magnetic moment prediction | Output structure-related magnetic-moment results under the `efsm` task |
|
| 24 |
+
| Structure relaxation pre-processing | Use `StructOptimizer` to optimize atomic positions and unit cells of candidate crystal structures |
|
| 25 |
+
| Environment connectivity check | Use `cif_file/demo.cif` and a lightweight MatRIS model to check whether the OneScience matchem environment is available |
|
| 26 |
+
|
| 27 |
+
|
| 28 |
+
# Usage Instructions
|
| 29 |
+
|
| 30 |
+
## 1. Using OneCode
|
| 31 |
+
|
| 32 |
+
You can try out intelligent one-click AI4S programming in the OneCode online environment:
|
| 33 |
+
|
| 34 |
+
[Try intelligent one-click AI4S programming](https://web-2069360198568017922-iaaj.ksai.scnet.cn:58043/home)
|
| 35 |
+
|
| 36 |
+
## 2. Manual Installation and Usage
|
| 37 |
+
|
| 38 |
+
**Hardware Requirements**
|
| 39 |
+
|
| 40 |
+
- GPU or DCU is recommended.
|
| 41 |
+
- CPU can be used for module verification and small-scale forward checks, but structure relaxation will be slow. CPU is not recommended for formal batch inference.
|
| 42 |
+
- DCU users need to install DTK in advance. DTK 25.04.2 or above, or the OneScience-recommended version matching the current cluster, is suggested.
|
| 43 |
+
|
| 44 |
+
### Download the Model Package
|
| 45 |
+
|
| 46 |
+
```bash
|
| 47 |
+
modelscope download --model OneScience/MatRIS --local_dir ./matris
|
| 48 |
+
cd matris
|
| 49 |
+
```
|
| 50 |
+
|
| 51 |
+
|
| 52 |
+
### Install the Runtime Environment
|
| 53 |
+
|
| 54 |
+
**DCU Environment**
|
| 55 |
+
|
| 56 |
+
```bash
|
| 57 |
+
# Please activate DTK and CONDA first
|
| 58 |
+
conda create -n onescience311 python=3.11 -y
|
| 59 |
+
conda activate onescience311
|
| 60 |
+
# uv installation is also supported
|
| 61 |
+
pip install onescience[matchem-dcu] -i http://mirrors.onescience.ai:3141/pypi/simple/ --trusted-host mirrors.onescience.ai
|
| 62 |
+
```
|
| 63 |
+
|
| 64 |
+
**GPU Environment**
|
| 65 |
+
|
| 66 |
+
```bash
|
| 67 |
+
# Please activate CONDA first
|
| 68 |
+
conda create -n onescience311 python=3.11 -y libstdcxx-ng=12 libgcc-ng=12 gcc_linux-64=12 gxx_linux-64=12
|
| 69 |
+
conda activate onescience311
|
| 70 |
+
# uv installation is also supported
|
| 71 |
+
pip install onescience[matchem-gpu] -i http://mirrors.onescience.ai:3141/pypi/simple/ --trusted-host mirrors.onescience.ai
|
| 72 |
+
```
|
| 73 |
+
|
| 74 |
+
### Training Weights
|
| 75 |
+
|
| 76 |
+
Inference testing requires a pre-trained model. MatRIS provides the following model keys:
|
| 77 |
+
|
| 78 |
+
| Model Key | Description |
|
| 79 |
+
| --- | --- |
|
| 80 |
+
| `matris_10m_omat` | Trained on the OMat24 dataset |
|
| 81 |
+
| `matris_10m_oam` | Trained on OMat24 and fine-tuned on sAlex+MPtrj |
|
| 82 |
+
| `matris_10m_mp` | Trained on the MPTrj dataset |
|
| 83 |
+
|
| 84 |
+
The `weight/` directory in this repository already contains pre-trained weights. `scripts/test_relaxation.py` will load them from `weight/` when run.
|
| 85 |
+
|
| 86 |
+
|
| 87 |
+
### Inference
|
| 88 |
+
|
| 89 |
+
**Run Modular Verification**
|
| 90 |
+
|
| 91 |
+
```bash
|
| 92 |
+
python scripts/test_modularization.py
|
| 93 |
+
```
|
| 94 |
+
|
| 95 |
+
This script instantiates a lightweight MatRIS model (randomly initialized weights) and completes one CPU forward pass to verify the connectivity of model modules.
|
| 96 |
+
|
| 97 |
+
**Run Structure Relaxation Inference**
|
| 98 |
+
|
| 99 |
+
```bash
|
| 100 |
+
python scripts/test_relaxation.py
|
| 101 |
+
```
|
| 102 |
+
|
| 103 |
+
This script reads `cif_file/demo.cif` and performs structure relaxation through `StructOptimizer`.
|
| 104 |
+
|
| 105 |
+
After inference completes, the log will output the relaxation process, and energy, forces, stress, magnetic moments, and the final structure object will be obtained in memory.
|
| 106 |
+
|
| 107 |
+
**Other Inference Examples**
|
| 108 |
+
|
| 109 |
+
Besides structure relaxation, you can also use `MatRISCalculator` to predict energy, forces, stress, and magnetic moments for a single structure:
|
| 110 |
+
|
| 111 |
+
```python
|
| 112 |
+
import torch
|
| 113 |
+
from ase.build import bulk
|
| 114 |
+
from onescience.utils.matris import MatRISCalculator
|
| 115 |
+
|
| 116 |
+
device = "cuda" if torch.cuda.is_available() else "cpu"
|
| 117 |
+
calc = MatRISCalculator(
|
| 118 |
+
model="matris_10m_oam",
|
| 119 |
+
task="efsm",
|
| 120 |
+
device=device,
|
| 121 |
+
)
|
| 122 |
+
|
| 123 |
+
atoms = bulk("Cu", a=5.43, cubic=True)
|
| 124 |
+
atoms.calc = calc
|
| 125 |
+
|
| 126 |
+
energy = atoms.get_potential_energy() # total energy (eV)
|
| 127 |
+
forces = atoms.get_forces() # forces (eV/Å)
|
| 128 |
+
stress = atoms.get_stress() # stress (eV/ų)
|
| 129 |
+
magmoms = atoms.get_magnetic_moments() # magnetic moments (μB)
|
| 130 |
+
```
|
| 131 |
+
|
| 132 |
+
# OneScience Official Information
|
| 133 |
+
|
| 134 |
+
| Platform | OneScience Main Repository | Skills Repository |
|
| 135 |
+
| --- | --- | --- |
|
| 136 |
+
| Gitee | https://gitee.com/onescience-ai/onescience | https://gitee.com/onescience-ai/oneskills |
|
| 137 |
+
| GitHub | https://github.com/onescience-ai/OneScience | https://github.com/onescience-ai/oneskills |
|
| 138 |
+
|
| 139 |
+
# Citation and License
|
| 140 |
+
|
| 141 |
+
- MatRIS upstream materials are released under the BSD-3-Clause License. This repository retains the source attribution and is organized for OneScience ModelScope automatic execution scenarios.
|
| 142 |
+
|
| 143 |
+
- If you use MatRIS results in scientific research, we recommend citing the original MatRIS project, the relevant OneScience project information, and adding citations for the materials datasets, structure optimization tools, or downstream analysis tools used according to the actual task.
|
cif_file/demo.cif
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# generated using pymatgen
|
| 2 |
+
data_Pu2Si3
|
| 3 |
+
_symmetry_space_group_name_H-M 'P 1'
|
| 4 |
+
_cell_length_a 4.26022438
|
| 5 |
+
_cell_length_b 7.16692875
|
| 6 |
+
_cell_length_c 7.16692875
|
| 7 |
+
_cell_angle_alpha 90.00000000
|
| 8 |
+
_cell_angle_beta 90.00000000
|
| 9 |
+
_cell_angle_gamma 90.00000000
|
| 10 |
+
_symmetry_Int_Tables_number 1
|
| 11 |
+
_chemical_formula_structural Pu2Si3
|
| 12 |
+
_chemical_formula_sum 'Pu4 Si6'
|
| 13 |
+
_cell_volume 218.82586168
|
| 14 |
+
_cell_formula_units_Z 2
|
| 15 |
+
loop_
|
| 16 |
+
_symmetry_equiv_pos_site_id
|
| 17 |
+
_symmetry_equiv_pos_as_xyz
|
| 18 |
+
1 'x, y, z'
|
| 19 |
+
loop_
|
| 20 |
+
_atom_site_type_symbol
|
| 21 |
+
_atom_site_label
|
| 22 |
+
_atom_site_symmetry_multiplicity
|
| 23 |
+
_atom_site_fract_x
|
| 24 |
+
_atom_site_fract_y
|
| 25 |
+
_atom_site_fract_z
|
| 26 |
+
_atom_site_occupancy
|
| 27 |
+
Pu Pu0 1 0.50000000 0.67597000 0.82403000 1.0
|
| 28 |
+
Pu Pu1 1 0.50000000 0.32403000 0.17597000 1.0
|
| 29 |
+
Pu Pu2 1 0.50000000 0.17597000 0.67597000 1.0
|
| 30 |
+
Pu Pu3 1 0.50000000 0.82403000 0.32403000 1.0
|
| 31 |
+
Si Si4 1 0.00000000 0.88257100 0.61742900 1.0
|
| 32 |
+
Si Si5 1 0.00000000 0.11742900 0.38257100 1.0
|
| 33 |
+
Si Si6 1 0.00000000 0.38257100 0.88257100 1.0
|
| 34 |
+
Si Si7 1 0.00000000 0.61742900 0.11742900 1.0
|
| 35 |
+
Si Si8 1 0.00000000 0.00000000 0.00000000 1.0
|
| 36 |
+
Si Si9 1 0.00000000 0.50000000 0.50000000 1.0
|
model/__init__.py
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
MatRIS: Material Representation Learning with Interatomic Structure
|
| 3 |
+
|
| 4 |
+
A deep learning model for predicting material properties including energy,
|
| 5 |
+
forces, stress, and magnetic moments.
|
| 6 |
+
"""
|
| 7 |
+
|
| 8 |
+
import sys
|
| 9 |
+
|
| 10 |
+
from importlib.metadata import PackageNotFoundError, version
|
| 11 |
+
|
| 12 |
+
try:
|
| 13 |
+
__version__ = version(__name__) # read from pyproject.toml
|
| 14 |
+
except PackageNotFoundError:
|
| 15 |
+
__version__ = "unknown"
|
| 16 |
+
|
| 17 |
+
# Import main model
|
| 18 |
+
from model.matris import MatRIS
|
| 19 |
+
|
| 20 |
+
# Import graph components
|
| 21 |
+
from onescience.datapipes.materials.matris import GraphConverter, RadiusGraph
|
| 22 |
+
|
| 23 |
+
# 把本仓库 model 注册为 onescience.models.matris,使得 OneScience 的 MatRISCalculator、
|
| 24 |
+
# StructOptimizer 等工具类在加载预训练权重时,直接使用本仓库的 MatRIS.load 方法。
|
| 25 |
+
# 默认权重会下载到本仓库根目录下的 weight/ 中。
|
| 26 |
+
sys.modules["onescience.models.matris"] = sys.modules[__name__]
|
| 27 |
+
|
| 28 |
+
__all__ = [
|
| 29 |
+
"MatRIS",
|
| 30 |
+
"GraphConverter",
|
| 31 |
+
"RadiusGraph",
|
| 32 |
+
"__version__",
|
| 33 |
+
]
|
model/matris.py
ADDED
|
@@ -0,0 +1,319 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from typing import Literal, Union, Sequence
|
| 2 |
+
|
| 3 |
+
import torch
|
| 4 |
+
from torch import Tensor, nn
|
| 5 |
+
import os
|
| 6 |
+
from pathlib import Path
|
| 7 |
+
from collections.abc import Sequence
|
| 8 |
+
|
| 9 |
+
from onescience.datapipes.materials.matris import RadiusGraph, GraphConverter, datatype
|
| 10 |
+
from onescience.modules.func_utils.matris_reference import AtomRef
|
| 11 |
+
from onescience.modules.func_utils.matris_graph import process_graphs
|
| 12 |
+
from onescience.modules.embedding.matris_embedding import (
|
| 13 |
+
ThreebodyFourierExpansion,
|
| 14 |
+
AtomTypeEmbedding,
|
| 15 |
+
EdgeBasisEmbedding,
|
| 16 |
+
ThreebodyEmbedding
|
| 17 |
+
)
|
| 18 |
+
from onescience.modules.func_utils.matris_func_utils import (
|
| 19 |
+
MLP,
|
| 20 |
+
GatedMLP,
|
| 21 |
+
get_normalization
|
| 22 |
+
)
|
| 23 |
+
from onescience.modules.layer.matris_interaction import Interaction_Block
|
| 24 |
+
from onescience.modules.head.matris_head import (
|
| 25 |
+
EnergyHead,
|
| 26 |
+
MagmomHead,
|
| 27 |
+
ForceStressHead,
|
| 28 |
+
)
|
| 29 |
+
|
| 30 |
+
|
| 31 |
+
class MatRIS(nn.Module):
|
| 32 |
+
""" Init MatRIS Potential """
|
| 33 |
+
|
| 34 |
+
def __init__(
|
| 35 |
+
self,
|
| 36 |
+
num_layers: int = 6,
|
| 37 |
+
node_feat_dim: int = 128,
|
| 38 |
+
edge_feat_dim: int = 128,
|
| 39 |
+
three_body_feat_dim: int = 128,
|
| 40 |
+
mlp_hidden_dims: Union[int, Sequence[int]] = (128, 128),
|
| 41 |
+
dropout: float = 0.0,
|
| 42 |
+
use_bias: bool = False,
|
| 43 |
+
distance_expansion: str = "Bessel",
|
| 44 |
+
three_body_expansion: str = "SH",
|
| 45 |
+
num_radial: int = 7,
|
| 46 |
+
num_angular: int = 7,
|
| 47 |
+
max_l: int = 4,
|
| 48 |
+
max_n: int = 4,
|
| 49 |
+
envelope_exponent: int = 8,
|
| 50 |
+
graph_conv_mlp: str = "GateMLP",
|
| 51 |
+
activation_type: str = "silu",
|
| 52 |
+
norm_type: str = "rms",
|
| 53 |
+
pairwise_cutoff: float = 6,
|
| 54 |
+
three_body_cutoff: float = 4,
|
| 55 |
+
use_smoothed_for_delta_edge: bool = False,
|
| 56 |
+
learnable_basis: bool = True,
|
| 57 |
+
is_intensive: bool = True,
|
| 58 |
+
is_conservation: bool = True,
|
| 59 |
+
reference_energy: str | None = None,
|
| 60 |
+
):
|
| 61 |
+
"""
|
| 62 |
+
Args:
|
| 63 |
+
num_layers (int): message passing layers.
|
| 64 |
+
node_feat_dim (int): atom feature embedding dim.
|
| 65 |
+
edge_feat_dim (int): edge(pairwise) feature embedding dim.
|
| 66 |
+
three_body_feat_dim (int): angle(three body) feature embedding dim.
|
| 67 |
+
mlp_hidden_dims (List or int): hidden dims of MLP.
|
| 68 |
+
Can be 'int' or 'list'.
|
| 69 |
+
dropout (float): dropout rate in MLP.
|
| 70 |
+
use_bias (bool): whether use bias in Interaction block.
|
| 71 |
+
distance_expansion (str): The function of pairwise basis.
|
| 72 |
+
Can be "Bessel" or "Gaussian".
|
| 73 |
+
three_body_expansion (str): The function of three body basis.
|
| 74 |
+
Can be "Fourier(fourier)" or "Spherical Harmonics(sh)".
|
| 75 |
+
num_radial (int): number of radial basis used in Bessel and Gaussian basis.
|
| 76 |
+
num_angular (int): number of three_body basis used in Fourier basis.
|
| 77 |
+
max_l (int): Maximum l value for Spherical Harmonics basis (SH).
|
| 78 |
+
max_n (int): Maximum n value for Spherical Harmonics basis (SH).
|
| 79 |
+
envelope_exponent (int): exponent of 'PolynomialEnvelope'.
|
| 80 |
+
graph_conv_mlp (str): The type of MLP in mp layers.
|
| 81 |
+
Can be "MLP", "GatedMLP" and "MoE".
|
| 82 |
+
See fucntion.py for more informations.
|
| 83 |
+
activation_type (str): activation function.
|
| 84 |
+
Can be "SiLU(silu)", "Sigmoid(sigmoid)", "ReLU(relu)"...
|
| 85 |
+
See fucntion.py for more informations.
|
| 86 |
+
norm_type (str): normalization function used in MLP.
|
| 87 |
+
Can be "LayerNorm(layer)", "BatchNorm(batch)", "RMSNorm(rms)"...
|
| 88 |
+
See fucntion.py for more informations.
|
| 89 |
+
pairwise_cutoff (float): The cutoff of Atom graph.
|
| 90 |
+
three_body_cutoff (float): The cutoff of Line graph.
|
| 91 |
+
use_smoothed_for_delta_edge (bool): Whether to use the smoothed features for edge feature update.
|
| 92 |
+
learnable_basis (bool): Whether the basis functions are learnable.
|
| 93 |
+
is_intensive (bool): whether the model outputs energy per atom (True) or total energy (False).
|
| 94 |
+
is_conservation (bool): whether use conservate force and stress.
|
| 95 |
+
reference_energy (str): refernece energy of 'str'(eg. MPtrj, OMat..) dataset(Caculated by linear regression).
|
| 96 |
+
more details can be found at reference_energy.py.
|
| 97 |
+
"""
|
| 98 |
+
|
| 99 |
+
super().__init__()
|
| 100 |
+
# model configs
|
| 101 |
+
self.config = { k: v for k, v in locals().items() if k not in ["self", "__class__"] }
|
| 102 |
+
|
| 103 |
+
self.is_intensive = is_intensive
|
| 104 |
+
|
| 105 |
+
self.reference_energy = None
|
| 106 |
+
if reference_energy is not None:
|
| 107 |
+
self.reference_energy = AtomRef(
|
| 108 |
+
reference_energy=reference_energy,
|
| 109 |
+
is_intensive=is_intensive
|
| 110 |
+
)
|
| 111 |
+
|
| 112 |
+
# Define Graph Converter
|
| 113 |
+
self.graph_converter = GraphConverter(
|
| 114 |
+
atom_graph_cutoff=pairwise_cutoff,
|
| 115 |
+
line_graph_cutoff=three_body_cutoff,
|
| 116 |
+
)
|
| 117 |
+
|
| 118 |
+
# ====== embedding layers ========
|
| 119 |
+
self.atom_embedding = AtomTypeEmbedding(atom_feat_dim=node_feat_dim)
|
| 120 |
+
self.edge_embedding = EdgeBasisEmbedding(
|
| 121 |
+
pairwise_cutoff=pairwise_cutoff,
|
| 122 |
+
three_body_cutoff=three_body_cutoff,
|
| 123 |
+
num_radial=num_radial,
|
| 124 |
+
edge_feat_dim=edge_feat_dim,
|
| 125 |
+
envelope_exponent=envelope_exponent,
|
| 126 |
+
learnable=learnable_basis,
|
| 127 |
+
distance_expansion=distance_expansion,
|
| 128 |
+
)
|
| 129 |
+
self.three_body_embedding = ThreebodyEmbedding(
|
| 130 |
+
num_angular = num_angular, # Fourier
|
| 131 |
+
max_n=max_n, max_l=max_l, cutoff=pairwise_cutoff, # Spherical Harmonics
|
| 132 |
+
three_body_feat_dim = three_body_feat_dim,
|
| 133 |
+
three_body_expansion = three_body_expansion,
|
| 134 |
+
learnable = learnable_basis
|
| 135 |
+
)
|
| 136 |
+
# ====== Interaction layers ========
|
| 137 |
+
interaction_block = [
|
| 138 |
+
Interaction_Block(
|
| 139 |
+
node_feat_dim=node_feat_dim,
|
| 140 |
+
edge_feat_dim=edge_feat_dim,
|
| 141 |
+
three_body_feat_dim=three_body_feat_dim,
|
| 142 |
+
num_radial=num_radial,
|
| 143 |
+
num_angular=num_angular,
|
| 144 |
+
dropout=dropout,
|
| 145 |
+
use_bias=use_bias,
|
| 146 |
+
use_smoothed_for_delta_edge=use_smoothed_for_delta_edge,
|
| 147 |
+
mlp_type=graph_conv_mlp,
|
| 148 |
+
norm_type=norm_type,
|
| 149 |
+
activation_type=activation_type,
|
| 150 |
+
)
|
| 151 |
+
for _ in range(num_layers)
|
| 152 |
+
]
|
| 153 |
+
self.interaction_block = nn.ModuleList(interaction_block)
|
| 154 |
+
|
| 155 |
+
# ====== Readout layers ========
|
| 156 |
+
self.readout_norm = get_normalization(norm_type, dim=node_feat_dim)
|
| 157 |
+
|
| 158 |
+
self.energy_head = EnergyHead(
|
| 159 |
+
feat_dim = node_feat_dim,
|
| 160 |
+
hidden_dim = mlp_hidden_dims,
|
| 161 |
+
output_dim = 1,
|
| 162 |
+
mlp_type = "mlp",
|
| 163 |
+
activation_type = activation_type,
|
| 164 |
+
)
|
| 165 |
+
self.magmom_head = MagmomHead(
|
| 166 |
+
feat_dim = node_feat_dim,
|
| 167 |
+
hidden_dim = 2 * node_feat_dim,
|
| 168 |
+
output_dim = 1,
|
| 169 |
+
mlp_type = "mlp",
|
| 170 |
+
activation_type = activation_type,
|
| 171 |
+
)
|
| 172 |
+
self.force_stress_head = ForceStressHead(
|
| 173 |
+
is_conservation = is_conservation,
|
| 174 |
+
feat_dim = edge_feat_dim, # is_conservation == False
|
| 175 |
+
hidden_dim = mlp_hidden_dims, # is_conservation == False
|
| 176 |
+
output_dim = 3, # is_conservation == False
|
| 177 |
+
mlp_type = "mlp", # is_conservation == False
|
| 178 |
+
activation_type = activation_type, # is_conservation == False
|
| 179 |
+
)
|
| 180 |
+
|
| 181 |
+
if not torch.distributed.is_initialized() or torch.distributed.get_rank() == 0:
|
| 182 |
+
print(f"MatRIS initialized with {self.get_params()} parameters")
|
| 183 |
+
|
| 184 |
+
def forward(
|
| 185 |
+
self,
|
| 186 |
+
graphs: Sequence[RadiusGraph],
|
| 187 |
+
task: str = "ef",
|
| 188 |
+
is_training: bool = False,
|
| 189 |
+
) -> dict[str, Tensor]:
|
| 190 |
+
"""
|
| 191 |
+
Args:
|
| 192 |
+
graphs (List): a list of RadiusGraph.
|
| 193 |
+
task (str): the prediction task. Can be 'e', 'em', 'ef', 'efs', 'efsm'.
|
| 194 |
+
"""
|
| 195 |
+
prediction = {}
|
| 196 |
+
# ======== Graph processing ========
|
| 197 |
+
batch_graph = process_graphs(graphs, compute_stress="s" in task)
|
| 198 |
+
|
| 199 |
+
# ======== Feature embedding ========
|
| 200 |
+
node_feat = self.atom_embedding( batch_graph['atomic_numbers'] - 1 ) # atom type feature init (use 0 for 'H')
|
| 201 |
+
edge_feat, smooth_weight = self.edge_embedding(graphs=batch_graph) # pairwise feature init
|
| 202 |
+
threebody_feat = None
|
| 203 |
+
if len(batch_graph['line_graph_dict']['line_graph']) != 0:
|
| 204 |
+
threebody_feat = self.three_body_embedding(graphs=batch_graph) # three body feature init
|
| 205 |
+
|
| 206 |
+
# ======== Interaction Block =======
|
| 207 |
+
for mp_layer in self.interaction_block:
|
| 208 |
+
node_feat, edge_feat, threebody_feat = mp_layer(
|
| 209 |
+
batch_graph=batch_graph,
|
| 210 |
+
node_feat=node_feat,
|
| 211 |
+
edge_feat=edge_feat,
|
| 212 |
+
threebody_feat=threebody_feat,
|
| 213 |
+
smooth_weight=smooth_weight,
|
| 214 |
+
)
|
| 215 |
+
|
| 216 |
+
# ======== Readout Block =======
|
| 217 |
+
node_feat = self.readout_norm(node_feat)
|
| 218 |
+
|
| 219 |
+
total_energy = self.energy_head(batch_graph = batch_graph, node_feat = node_feat)
|
| 220 |
+
|
| 221 |
+
force_stress_dict = self.force_stress_head(
|
| 222 |
+
batch_graph = batch_graph,
|
| 223 |
+
compute_force="f" in task,
|
| 224 |
+
compute_stress="s" in task,
|
| 225 |
+
total_energy = total_energy,
|
| 226 |
+
node_feat = node_feat,
|
| 227 |
+
edge_feat = edge_feat,
|
| 228 |
+
is_training = is_training)
|
| 229 |
+
prediction.update(force_stress_dict)
|
| 230 |
+
|
| 231 |
+
if "m" in task:
|
| 232 |
+
magmom = self.magmom_head(batch_graph = batch_graph, node_feat = node_feat)
|
| 233 |
+
prediction["m"] = magmom
|
| 234 |
+
|
| 235 |
+
atoms_per_graph_tensor = torch.tensor(batch_graph['atoms_per_graph'],
|
| 236 |
+
dtype=torch.int32,
|
| 237 |
+
device=total_energy.device)
|
| 238 |
+
if self.is_intensive:
|
| 239 |
+
energy_per_atom = total_energy / atoms_per_graph_tensor
|
| 240 |
+
prediction["e"] = energy_per_atom
|
| 241 |
+
else:
|
| 242 |
+
prediction["e"] = total_energy
|
| 243 |
+
|
| 244 |
+
prediction["atoms_per_graph"] = atoms_per_graph_tensor
|
| 245 |
+
|
| 246 |
+
ref_energy = (
|
| 247 |
+
0 if self.reference_energy is None else self.reference_energy(graphs)
|
| 248 |
+
)
|
| 249 |
+
prediction["e"] += ref_energy
|
| 250 |
+
prediction["ref_energy"] = ref_energy
|
| 251 |
+
return prediction
|
| 252 |
+
|
| 253 |
+
def get_params(self) -> int:
|
| 254 |
+
"""Return the number of parameters in the model."""
|
| 255 |
+
return sum(p.numel() for p in self.parameters())
|
| 256 |
+
|
| 257 |
+
@classmethod
|
| 258 |
+
def from_dict(cls, dct: dict):
|
| 259 |
+
matris = MatRIS(**dct["config"])
|
| 260 |
+
matris.load_state_dict(dct["state_dict"])
|
| 261 |
+
return matris
|
| 262 |
+
|
| 263 |
+
@classmethod
|
| 264 |
+
def load(
|
| 265 |
+
cls,
|
| 266 |
+
model_name: str = "matris_10m_oam",
|
| 267 |
+
device: str | None = None,
|
| 268 |
+
):
|
| 269 |
+
"""Load pretrained model."""
|
| 270 |
+
model_name = model_name.lower()
|
| 271 |
+
supported_models = ["matris_10m_oam", "matris_10m_mp"]
|
| 272 |
+
if model_name not in supported_models:
|
| 273 |
+
raise ValueError(f"Unsupported model_name: {model_name}. Supported models are: {supported_models}")
|
| 274 |
+
|
| 275 |
+
if device is None:
|
| 276 |
+
device = "cuda" if torch.cuda.is_available() else "cpu"
|
| 277 |
+
|
| 278 |
+
# 默认权重目录:本仓库根目录下的 weight/;可通过 ONESCIENCE_MODELS_DIR 覆盖
|
| 279 |
+
default_weight_dir = Path(__file__).resolve().parent.parent / "weight"
|
| 280 |
+
cache_dir = os.environ.get("ONESCIENCE_MODELS_DIR", str(default_weight_dir))
|
| 281 |
+
os.makedirs(cache_dir, exist_ok=True)
|
| 282 |
+
|
| 283 |
+
checkpoint_files = {
|
| 284 |
+
"matris_10m_omat": "MatRIS_10M_OMAT.pth.tar",
|
| 285 |
+
"matris_10m_oam": "MatRIS_10M_OAM.pth.tar",
|
| 286 |
+
"matris_10m_mp": "MatRIS_10M_MP.pth.tar",
|
| 287 |
+
"matris_6m_mp": "MatRIS_6M_MP.pth.tar",
|
| 288 |
+
}
|
| 289 |
+
|
| 290 |
+
DOWNLOAD_URLS = {
|
| 291 |
+
"matris_10m_omat": "", # TODO
|
| 292 |
+
"matris_10m_oam": "https://figshare.com/ndownloader/files/59142728",
|
| 293 |
+
"matris_10m_mp": "https://figshare.com/ndownloader/files/59143058",
|
| 294 |
+
"matris_6m_mp": "", # TODO
|
| 295 |
+
}
|
| 296 |
+
|
| 297 |
+
ckpt_filename = checkpoint_files[model_name]
|
| 298 |
+
|
| 299 |
+
ckpt_path = os.path.join(cache_dir, ckpt_filename)
|
| 300 |
+
if not os.path.exists(ckpt_path):
|
| 301 |
+
url = DOWNLOAD_URLS.get(model_name)
|
| 302 |
+
if not url:
|
| 303 |
+
raise ValueError(f"No download URL provided for model: {model_name}")
|
| 304 |
+
|
| 305 |
+
print(f"Checkpoint not found, downloading to {ckpt_path} ...")
|
| 306 |
+
torch.hub.download_url_to_file(url, ckpt_path)
|
| 307 |
+
|
| 308 |
+
|
| 309 |
+
ckpt_state = torch.load(
|
| 310 |
+
ckpt_path,
|
| 311 |
+
map_location=torch.device("cpu"),
|
| 312 |
+
weights_only=False
|
| 313 |
+
)
|
| 314 |
+
model = MatRIS.from_dict(ckpt_state)
|
| 315 |
+
|
| 316 |
+
model = model.to(device)
|
| 317 |
+
print(f"Loading {model_name} successfully, running on {device}.")
|
| 318 |
+
|
| 319 |
+
return model
|
scripts/test_modularization.py
ADDED
|
@@ -0,0 +1,165 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
验证 MatRIS 模块化重构后各模块能否被正确导入与运行。
|
| 3 |
+
|
| 4 |
+
测试内容:
|
| 5 |
+
1. 分散在各目录的 matris 模块能否正常 import
|
| 6 |
+
2. MatRIS 模型能否正常实例化
|
| 7 |
+
3. 模型能否执行一次简单的前向传播(energy/force/stress/magmom)
|
| 8 |
+
"""
|
| 9 |
+
|
| 10 |
+
import sys
|
| 11 |
+
from pathlib import Path
|
| 12 |
+
|
| 13 |
+
# 把本仓库根目录放到 sys.path 最前面,避免 PYTHONPATH 中其他同名 model 包干扰
|
| 14 |
+
sys.path.insert(0, str(Path(__file__).resolve().parent.parent))
|
| 15 |
+
|
| 16 |
+
import model # noqa: E402
|
| 17 |
+
|
| 18 |
+
import torch
|
| 19 |
+
|
| 20 |
+
def test_imports():
|
| 21 |
+
"""测试所有分散模块的导入路径"""
|
| 22 |
+
print("[1/4] 测试模块导入...")
|
| 23 |
+
|
| 24 |
+
# layer
|
| 25 |
+
from onescience.modules.layer.matris_radial import (
|
| 26 |
+
PolynomialEnvelope, BesselExpansion, GaussianExpansion,
|
| 27 |
+
FourierExpansion, SphericalExpansion
|
| 28 |
+
)
|
| 29 |
+
from onescience.modules.layer.matris_interaction import Interaction_Block
|
| 30 |
+
|
| 31 |
+
# embedding
|
| 32 |
+
from onescience.modules.embedding.matris_embedding import (
|
| 33 |
+
AtomTypeEmbedding, EdgeBasisEmbedding, ThreebodyEmbedding
|
| 34 |
+
)
|
| 35 |
+
|
| 36 |
+
# func_utils
|
| 37 |
+
from onescience.modules.func_utils.matris_func_utils import (
|
| 38 |
+
MLP, GatedMLP, get_activation, get_normalization, aggregate
|
| 39 |
+
)
|
| 40 |
+
from onescience.modules.func_utils.matris_reference import AtomRef
|
| 41 |
+
from onescience.modules.func_utils.matris_graph import process_graphs
|
| 42 |
+
|
| 43 |
+
# head
|
| 44 |
+
from onescience.modules.head.matris_head import EnergyHead, MagmomHead, ForceStressHead
|
| 45 |
+
|
| 46 |
+
# utils
|
| 47 |
+
from onescience.utils.matris import StructOptimizer, MatRISCalculator
|
| 48 |
+
|
| 49 |
+
print(" 所有模块导入成功 ✓")
|
| 50 |
+
|
| 51 |
+
|
| 52 |
+
def test_model_instantiate():
|
| 53 |
+
"""测试模型能否正常实例化"""
|
| 54 |
+
print("[2/4] 测试模型实例化...")
|
| 55 |
+
|
| 56 |
+
from model import MatRIS
|
| 57 |
+
|
| 58 |
+
model = MatRIS(
|
| 59 |
+
num_layers=2, # 用小层数加速测试
|
| 60 |
+
node_feat_dim=64,
|
| 61 |
+
edge_feat_dim=64,
|
| 62 |
+
three_body_feat_dim=64,
|
| 63 |
+
num_radial=5,
|
| 64 |
+
num_angular=5,
|
| 65 |
+
pairwise_cutoff=5.0,
|
| 66 |
+
three_body_cutoff=3.0,
|
| 67 |
+
reference_energy=None, # 不加载参考能量
|
| 68 |
+
)
|
| 69 |
+
|
| 70 |
+
num_params = sum(p.numel() for p in model.parameters())
|
| 71 |
+
print(f" MatRIS 实例化成功,参数量: {num_params:,} ✓")
|
| 72 |
+
return model
|
| 73 |
+
|
| 74 |
+
|
| 75 |
+
def test_forward_cpu():
|
| 76 |
+
"""测试 CPU 前向传播"""
|
| 77 |
+
print("[3/4] 测试 CPU 前向传播...")
|
| 78 |
+
|
| 79 |
+
from model import MatRIS
|
| 80 |
+
from onescience.datapipes.materials.matris import GraphConverter
|
| 81 |
+
from pymatgen.core.structure import Structure
|
| 82 |
+
from pymatgen.core.lattice import Lattice
|
| 83 |
+
|
| 84 |
+
# 构建一个极简晶体:2原子 Si
|
| 85 |
+
lattice = Lattice.cubic(5.43)
|
| 86 |
+
structure = Structure(lattice, ["Si", "Si"], [[0, 0, 0], [0.25, 0.25, 0.25]])
|
| 87 |
+
|
| 88 |
+
model = MatRIS(
|
| 89 |
+
num_layers=2,
|
| 90 |
+
node_feat_dim=64,
|
| 91 |
+
edge_feat_dim=64,
|
| 92 |
+
three_body_feat_dim=64,
|
| 93 |
+
num_radial=5,
|
| 94 |
+
num_angular=5,
|
| 95 |
+
pairwise_cutoff=5.0,
|
| 96 |
+
three_body_cutoff=3.0,
|
| 97 |
+
reference_energy=None,
|
| 98 |
+
)
|
| 99 |
+
model.eval()
|
| 100 |
+
|
| 101 |
+
# 构建图
|
| 102 |
+
graph_converter = GraphConverter(
|
| 103 |
+
atom_graph_cutoff=5.0,
|
| 104 |
+
line_graph_cutoff=3.0,
|
| 105 |
+
)
|
| 106 |
+
graph = graph_converter(structure)
|
| 107 |
+
|
| 108 |
+
out = model([graph], task="efsm")
|
| 109 |
+
|
| 110 |
+
print(f" 预测能量 (eV/atom): {out['e'].item():.4f} ✓")
|
| 111 |
+
print(f" 预测力数量: {len(out['f'])} 组 ✓")
|
| 112 |
+
print(f" 预测应力数量: {len(out['s'])} 组 ✓")
|
| 113 |
+
print(f" 预测磁矩数量: {len(out['m'])} 组 ✓")
|
| 114 |
+
|
| 115 |
+
|
| 116 |
+
def test_cuda_available():
|
| 117 |
+
"""若存在 GPU,测试 CUDA 前向传播"""
|
| 118 |
+
print("[4/4] 测试 CUDA 可用性...")
|
| 119 |
+
|
| 120 |
+
if not torch.cuda.is_available():
|
| 121 |
+
print(" 无可用 GPU,跳过 CUDA 测试")
|
| 122 |
+
return
|
| 123 |
+
|
| 124 |
+
from model import MatRIS
|
| 125 |
+
from onescience.datapipes.materials.matris import GraphConverter
|
| 126 |
+
from pymatgen.core.structure import Structure
|
| 127 |
+
from pymatgen.core.lattice import Lattice
|
| 128 |
+
|
| 129 |
+
lattice = Lattice.cubic(5.43)
|
| 130 |
+
structure = Structure(lattice, ["Si", "Si"], [[0, 0, 0], [0.25, 0.25, 0.25]])
|
| 131 |
+
|
| 132 |
+
model = MatRIS(
|
| 133 |
+
num_layers=2,
|
| 134 |
+
node_feat_dim=64,
|
| 135 |
+
edge_feat_dim=64,
|
| 136 |
+
three_body_feat_dim=64,
|
| 137 |
+
num_radial=5,
|
| 138 |
+
num_angular=5,
|
| 139 |
+
pairwise_cutoff=5.0,
|
| 140 |
+
three_body_cutoff=3.0,
|
| 141 |
+
reference_energy=None,
|
| 142 |
+
).cuda()
|
| 143 |
+
model.eval()
|
| 144 |
+
|
| 145 |
+
graph_converter = GraphConverter(5.0, 3.0)
|
| 146 |
+
graph = graph_converter(structure).to("cuda")
|
| 147 |
+
|
| 148 |
+
out = model([graph], task="efsm")
|
| 149 |
+
|
| 150 |
+
print(f" CUDA 前向传播成功,能量: {out['e'].item():.4f} ✓")
|
| 151 |
+
|
| 152 |
+
|
| 153 |
+
if __name__ == "__main__":
|
| 154 |
+
print("=" * 60)
|
| 155 |
+
print("MatRIS 模块化重构验证测试")
|
| 156 |
+
print("=" * 60)
|
| 157 |
+
|
| 158 |
+
test_imports()
|
| 159 |
+
test_model_instantiate()
|
| 160 |
+
test_forward_cpu()
|
| 161 |
+
test_cuda_available()
|
| 162 |
+
|
| 163 |
+
print("=" * 60)
|
| 164 |
+
print("全部测试通过 ✓")
|
| 165 |
+
print("=" * 60)
|
scripts/test_relaxation.py
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import sys
|
| 2 |
+
from pathlib import Path
|
| 3 |
+
|
| 4 |
+
# 把本仓库根目录放到 sys.path 最前面,避免 PYTHONPATH 中其他同名 model 包干扰
|
| 5 |
+
sys.path.insert(0, str(Path(__file__).resolve().parent.parent))
|
| 6 |
+
|
| 7 |
+
import model # noqa: E402
|
| 8 |
+
|
| 9 |
+
import ase
|
| 10 |
+
from ase.build import bulk
|
| 11 |
+
import torch
|
| 12 |
+
from pymatgen.core.structure import Structure
|
| 13 |
+
from onescience.utils.matris import StructOptimizer
|
| 14 |
+
|
| 15 |
+
model_name = "matris_10m_oam"
|
| 16 |
+
device = "cuda" if torch.cuda.is_available() else "cpu"
|
| 17 |
+
|
| 18 |
+
matris_opt = StructOptimizer(
|
| 19 |
+
model=model_name,
|
| 20 |
+
task = "efsm",
|
| 21 |
+
optimizer = "FIRE", # FIRE, BFGS ...
|
| 22 |
+
device=device
|
| 23 |
+
)
|
| 24 |
+
|
| 25 |
+
atom = Structure.from_file("cif_file/demo.cif")
|
| 26 |
+
|
| 27 |
+
max_steps = 500
|
| 28 |
+
fmax = 0.05
|
| 29 |
+
opt_result = matris_opt.relax(
|
| 30 |
+
atoms=atom, # pymatgen.Structure or ase.Atoms
|
| 31 |
+
verbose=True,
|
| 32 |
+
steps=max_steps,
|
| 33 |
+
fmax=fmax,
|
| 34 |
+
relax_cell=max_steps > 0,
|
| 35 |
+
ase_filter="FrechetCellFilter",
|
| 36 |
+
)
|
| 37 |
+
|
| 38 |
+
trajectory = opt_result['trajectory']
|
| 39 |
+
energy = trajectory.energies[-1]
|
| 40 |
+
force = trajectory.forces[-1]
|
| 41 |
+
stress = trajectory.stresses[-1]
|
| 42 |
+
magmom = trajectory.magmoms[-1]
|
| 43 |
+
|
| 44 |
+
final_structure = opt_result['final_structure']
|
weight/.MatRIS_10M_OAM.pth.tar.efileUploading
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:85c1091b5a28d582f309e3bd0f663236d57313346dfca8f5aa364541234d2af6
|
| 3 |
+
size 8
|
weight/MatRIS_10M_MP.pth.tar
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:333b6019ec901a4bce9735ef28136d7da637899272825ff7ef9c84587b22f2d9
|
| 3 |
+
size 42184483
|
weight/MatRIS_10M_OAM.pth.tar.efilePart
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:c033abc53601a74f10d9b7fec0f658220c013c3b11d4d405f1d32136d4c2b067
|
| 3 |
+
size 42273174
|