Upload folder using huggingface_hub
Browse files- README.md +161 -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_MP.pth.tar +3 -0
- weight/MatRIS_10M_OAM.pth.tar +3 -0
README.md
ADDED
|
@@ -0,0 +1,161 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
---
|
| 2 |
+
license: bsd-3-clause
|
| 3 |
+
tasks:
|
| 4 |
+
- materials-simulation
|
| 5 |
+
- structure-relaxation
|
| 6 |
+
- property-prediction
|
| 7 |
+
frameworks:
|
| 8 |
+
- pytorch
|
| 9 |
+
language:
|
| 10 |
+
- en
|
| 11 |
+
tags:
|
| 12 |
+
- OneScience
|
| 13 |
+
- materials-science
|
| 14 |
+
- materials-representation
|
| 15 |
+
- crystal-structure
|
| 16 |
+
- energy-prediction
|
| 17 |
+
- force-prediction
|
| 18 |
+
- stress-prediction
|
| 19 |
+
- magnetic-moment-prediction
|
| 20 |
+
- structure-relaxation
|
| 21 |
+
datasets:
|
| 22 |
+
- OneScience-Sugon/pbe
|
| 23 |
+
---
|
| 24 |
+
|
| 25 |
+
<p align="center">
|
| 26 |
+
<strong>
|
| 27 |
+
<span style="font-size: 30px;">MatRIS</span>
|
| 28 |
+
</strong>
|
| 29 |
+
</p>
|
| 30 |
+
|
| 31 |
+
# Model Introduction
|
| 32 |
+
|
| 33 |
+
MatRIS, short for Materials Representation and Interaction Simulation, is a foundation model for material representation and interaction simulation. It predicts the energy, forces, stress, and magnetic moments of crystal structures and supports structure relaxation using ASE Atoms and pymatgen Structure objects.
|
| 34 |
+
|
| 35 |
+
# Model Description
|
| 36 |
+
|
| 37 |
+
MatRIS uses a graph neural network architecture trained on materials datasets such as OMat24 and MPTrj. It supports energy, force, stress, and magnetic-moment prediction and structure optimization for crystalline materials.
|
| 38 |
+
|
| 39 |
+
# Use Cases
|
| 40 |
+
|
| 41 |
+
| Use case | Description |
|
| 42 |
+
| :---: | :--- |
|
| 43 |
+
| Crystal energy prediction | Predict system energy from a CIF file, pymatgen Structure, or ASE Atoms object |
|
| 44 |
+
| Force and stress prediction | Estimate forces and stress for structure relaxation, molecular dynamics, or downstream simulations |
|
| 45 |
+
| Magnetic-moment prediction | Return structure-dependent magnetic moments for the `efsm` task |
|
| 46 |
+
| Structure-relaxation preprocessing | Optimize atomic positions and unit cells of candidate crystals with `StructOptimizer` |
|
| 47 |
+
| Environment connectivity check | Use `cif_file/demo.cif` and a lightweight MatRIS model to verify the OneScience MatChem environment |
|
| 48 |
+
|
| 49 |
+
# Usage
|
| 50 |
+
|
| 51 |
+
## 1. Using OneCode
|
| 52 |
+
|
| 53 |
+
Try intelligent, one-click AI4S programming in the OneCode online environment:
|
| 54 |
+
|
| 55 |
+
[Try intelligent, one-click AI4S programming](https://web-2069360198568017922-iaaj.ksai.scnet.cn:58043/home)
|
| 56 |
+
|
| 57 |
+
## 2. Manual Installation and Usage
|
| 58 |
+
|
| 59 |
+
**Hardware requirements**
|
| 60 |
+
|
| 61 |
+
- A GPU or DCU is recommended.
|
| 62 |
+
- A CPU can be used for module checks and small forward-pass tests, but structure relaxation will be slow and is not recommended for production batch inference.
|
| 63 |
+
- DCU users must install DTK in advance. DTK 25.04.2 or later, or the OneScience-recommended version matching the current cluster, is recommended.
|
| 64 |
+
|
| 65 |
+
### Download the Model Package
|
| 66 |
+
|
| 67 |
+
```bash
|
| 68 |
+
hf download --model OneScience-Sugon/MatRIS --local-dir ./matris
|
| 69 |
+
cd matris
|
| 70 |
+
```
|
| 71 |
+
|
| 72 |
+
### Install the Runtime Environment
|
| 73 |
+
|
| 74 |
+
**DCU environment**
|
| 75 |
+
|
| 76 |
+
```bash
|
| 77 |
+
# Activate DTK and conda first
|
| 78 |
+
conda create -n onescience311 python=3.11 -y
|
| 79 |
+
conda activate onescience311
|
| 80 |
+
# uv installation is also supported
|
| 81 |
+
pip install onescience[matchem-dcu] -i http://mirrors.onescience.ai:3141/pypi/simple/ --trusted-host mirrors.onescience.ai
|
| 82 |
+
```
|
| 83 |
+
|
| 84 |
+
**GPU environment**
|
| 85 |
+
|
| 86 |
+
```bash
|
| 87 |
+
# Activate conda first
|
| 88 |
+
conda create -n onescience311 python=3.11 -y libstdcxx-ng=12 libgcc-ng=12 gcc_linux-64=12 gxx_linux-64=12
|
| 89 |
+
conda activate onescience311
|
| 90 |
+
# uv installation is also supported
|
| 91 |
+
pip install onescience[matchem-gpu] -i http://mirrors.onescience.ai:3141/pypi/simple/ --trusted-host mirrors.onescience.ai
|
| 92 |
+
```
|
| 93 |
+
|
| 94 |
+
### Trained Weights
|
| 95 |
+
|
| 96 |
+
Inference requires a pretrained model. MatRIS provides the following model keys:
|
| 97 |
+
|
| 98 |
+
| Model key | Description |
|
| 99 |
+
| --- | --- |
|
| 100 |
+
| `matris_10m_omat` | Trained on the OMat24 dataset |
|
| 101 |
+
| `matris_10m_oam` | Trained on OMat24 and fine-tuned on sAlex+MPtrj |
|
| 102 |
+
| `matris_10m_mp` | Trained on the MPTrj dataset |
|
| 103 |
+
|
| 104 |
+
The `weight/` directory in this repository already contains pretrained weights. `scripts/test_relaxation.py` loads them from that directory.
|
| 105 |
+
|
| 106 |
+
### Inference
|
| 107 |
+
|
| 108 |
+
**Run the modularity check**
|
| 109 |
+
|
| 110 |
+
```bash
|
| 111 |
+
python scripts/test_modularization.py
|
| 112 |
+
```
|
| 113 |
+
|
| 114 |
+
This script instantiates a lightweight MatRIS model with randomly initialized weights and runs one CPU forward pass to verify that the model modules work together.
|
| 115 |
+
|
| 116 |
+
**Run structure-relaxation inference**
|
| 117 |
+
|
| 118 |
+
```bash
|
| 119 |
+
python scripts/test_relaxation.py
|
| 120 |
+
```
|
| 121 |
+
|
| 122 |
+
This script reads `cif_file/demo.cif` and performs structure relaxation with `StructOptimizer`.
|
| 123 |
+
|
| 124 |
+
After inference, the log shows the relaxation process, while the energy, forces, stress, magnetic moments, and final structure object are available in memory.
|
| 125 |
+
|
| 126 |
+
**Other inference examples**
|
| 127 |
+
|
| 128 |
+
In addition to structure relaxation, you can use `MatRISCalculator` to predict the energy, forces, stress, and magnetic moments of a single structure:
|
| 129 |
+
|
| 130 |
+
```python
|
| 131 |
+
import torch
|
| 132 |
+
from ase.build import bulk
|
| 133 |
+
from onescience.utils.matris import MatRISCalculator
|
| 134 |
+
|
| 135 |
+
device = "cuda" if torch.cuda.is_available() else "cpu"
|
| 136 |
+
calc = MatRISCalculator(
|
| 137 |
+
model="matris_10m_oam",
|
| 138 |
+
task="efsm",
|
| 139 |
+
device=device,
|
| 140 |
+
)
|
| 141 |
+
|
| 142 |
+
atoms = bulk("Cu", a=5.43, cubic=True)
|
| 143 |
+
atoms.calc = calc
|
| 144 |
+
|
| 145 |
+
energy = atoms.get_potential_energy() # Total energy (eV)
|
| 146 |
+
forces = atoms.get_forces() # Forces (eV/Å)
|
| 147 |
+
stress = atoms.get_stress() # Stress (eV/ų)
|
| 148 |
+
magmoms = atoms.get_magnetic_moments() # Magnetic moments (μB)
|
| 149 |
+
```
|
| 150 |
+
|
| 151 |
+
# Official OneScience Resources
|
| 152 |
+
|
| 153 |
+
| Platform | OneScience Main Repository | Skills Repository |
|
| 154 |
+
| --- | --- | --- |
|
| 155 |
+
| Gitee | https://gitee.com/onescience-ai/onescience | https://gitee.com/onescience-ai/oneskills |
|
| 156 |
+
| GitHub | https://github.com/onescience-ai/OneScience | https://github.com/onescience-ai/oneskills |
|
| 157 |
+
|
| 158 |
+
# Citation and License
|
| 159 |
+
|
| 160 |
+
- The upstream MatRIS materials use the BSD 3-Clause License. This repository retains source attribution and has been organized for automated execution in the OneScience Hugging Face environment.
|
| 161 |
+
- If you use MatRIS results in research, please cite the original MatRIS project and the relevant OneScience projects, as well as the materials datasets, structure-optimization tools, or downstream analysis tools used for your 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_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
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:c033abc53601a74f10d9b7fec0f658220c013c3b11d4d405f1d32136d4c2b067
|
| 3 |
+
size 42273174
|