dadadaxi commited on
Commit
971ba3b
·
verified ·
1 Parent(s): 7f83c6a

Upload folder using huggingface_hub

Browse files
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