OneScience commited on
Commit
45e5a32
·
verified ·
1 Parent(s): c9b3a2b

Upload folder using huggingface_hub

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