OneScience commited on
Commit
99eaa37
·
verified ·
1 Parent(s): 8b9764b

Upload folder using huggingface_hub

Browse files
.gitattributes CHANGED
@@ -1,35 +1,47 @@
1
  *.7z filter=lfs diff=lfs merge=lfs -text
2
  *.arrow filter=lfs diff=lfs merge=lfs -text
3
  *.bin filter=lfs diff=lfs merge=lfs -text
 
4
  *.bz2 filter=lfs diff=lfs merge=lfs -text
5
- *.ckpt filter=lfs diff=lfs merge=lfs -text
6
  *.ftz filter=lfs diff=lfs merge=lfs -text
7
  *.gz filter=lfs diff=lfs merge=lfs -text
8
  *.h5 filter=lfs diff=lfs merge=lfs -text
9
  *.joblib filter=lfs diff=lfs merge=lfs -text
10
  *.lfs.* filter=lfs diff=lfs merge=lfs -text
11
- *.mlmodel filter=lfs diff=lfs merge=lfs -text
12
  *.model filter=lfs diff=lfs merge=lfs -text
13
  *.msgpack filter=lfs diff=lfs merge=lfs -text
14
- *.npy filter=lfs diff=lfs merge=lfs -text
15
- *.npz filter=lfs diff=lfs merge=lfs -text
16
  *.onnx filter=lfs diff=lfs merge=lfs -text
17
  *.ot filter=lfs diff=lfs merge=lfs -text
18
  *.parquet filter=lfs diff=lfs merge=lfs -text
19
  *.pb filter=lfs diff=lfs merge=lfs -text
20
- *.pickle filter=lfs diff=lfs merge=lfs -text
21
- *.pkl filter=lfs diff=lfs merge=lfs -text
22
  *.pt filter=lfs diff=lfs merge=lfs -text
23
  *.pth filter=lfs diff=lfs merge=lfs -text
24
  *.rar filter=lfs diff=lfs merge=lfs -text
25
- *.safetensors filter=lfs diff=lfs merge=lfs -text
26
  saved_model/**/* filter=lfs diff=lfs merge=lfs -text
27
  *.tar.* filter=lfs diff=lfs merge=lfs -text
28
- *.tar filter=lfs diff=lfs merge=lfs -text
29
  *.tflite filter=lfs diff=lfs merge=lfs -text
30
  *.tgz filter=lfs diff=lfs merge=lfs -text
31
- *.wasm filter=lfs diff=lfs merge=lfs -text
32
  *.xz filter=lfs diff=lfs merge=lfs -text
33
  *.zip filter=lfs diff=lfs merge=lfs -text
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
- *tfevents* filter=lfs diff=lfs merge=lfs -text
 
1
  *.7z filter=lfs diff=lfs merge=lfs -text
2
  *.arrow filter=lfs diff=lfs merge=lfs -text
3
  *.bin filter=lfs diff=lfs merge=lfs -text
4
+ *.bin.* filter=lfs diff=lfs merge=lfs -text
5
  *.bz2 filter=lfs diff=lfs merge=lfs -text
 
6
  *.ftz filter=lfs diff=lfs merge=lfs -text
7
  *.gz filter=lfs diff=lfs merge=lfs -text
8
  *.h5 filter=lfs diff=lfs merge=lfs -text
9
  *.joblib filter=lfs diff=lfs merge=lfs -text
10
  *.lfs.* filter=lfs diff=lfs merge=lfs -text
 
11
  *.model filter=lfs diff=lfs merge=lfs -text
12
  *.msgpack filter=lfs diff=lfs merge=lfs -text
 
 
13
  *.onnx filter=lfs diff=lfs merge=lfs -text
14
  *.ot filter=lfs diff=lfs merge=lfs -text
15
  *.parquet filter=lfs diff=lfs merge=lfs -text
16
  *.pb filter=lfs diff=lfs merge=lfs -text
 
 
17
  *.pt filter=lfs diff=lfs merge=lfs -text
18
  *.pth filter=lfs diff=lfs merge=lfs -text
19
  *.rar filter=lfs diff=lfs merge=lfs -text
 
20
  saved_model/**/* filter=lfs diff=lfs merge=lfs -text
21
  *.tar.* filter=lfs diff=lfs merge=lfs -text
 
22
  *.tflite filter=lfs diff=lfs merge=lfs -text
23
  *.tgz filter=lfs diff=lfs merge=lfs -text
 
24
  *.xz filter=lfs diff=lfs merge=lfs -text
25
  *.zip filter=lfs diff=lfs merge=lfs -text
26
+ *.zstandard filter=lfs diff=lfs merge=lfs -text
27
+ *.tfevents* filter=lfs diff=lfs merge=lfs -text
28
+ *.db* filter=lfs diff=lfs merge=lfs -text
29
+ *.ark* filter=lfs diff=lfs merge=lfs -text
30
+ **/*ckpt*data* filter=lfs diff=lfs merge=lfs -text
31
+ **/*ckpt*.meta filter=lfs diff=lfs merge=lfs -text
32
+ **/*ckpt*.index filter=lfs diff=lfs merge=lfs -text
33
+ *.safetensors filter=lfs diff=lfs merge=lfs -text
34
+ *.ckpt filter=lfs diff=lfs merge=lfs -text
35
+ *.gguf* filter=lfs diff=lfs merge=lfs -text
36
+ *.ggml filter=lfs diff=lfs merge=lfs -text
37
+ *.llamafile* filter=lfs diff=lfs merge=lfs -text
38
+ *.pt2 filter=lfs diff=lfs merge=lfs -text
39
+ *.mlmodel filter=lfs diff=lfs merge=lfs -text
40
+ *.npy filter=lfs diff=lfs merge=lfs -text
41
+ *.npz filter=lfs diff=lfs merge=lfs -text
42
+ *.pickle filter=lfs diff=lfs merge=lfs -text
43
+ *.pkl filter=lfs diff=lfs merge=lfs -text
44
+ *.tar filter=lfs diff=lfs merge=lfs -text
45
+ *.wasm filter=lfs diff=lfs merge=lfs -text
46
  *.zst filter=lfs diff=lfs merge=lfs -text
47
+ *tfevents* filter=lfs diff=lfs merge=lfs -text
README.md ADDED
@@ -0,0 +1,140 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ license: apache-2.0
3
+ language:
4
+ - en
5
+ tags:
6
+ - OneScience
7
+ - fluid dynamics
8
+ - unsteady turbulent flow prediction
9
+ - long-range mesh dependency modeling
10
+ frameworks: PyTorch
11
+ ---
12
+ <p align="center">
13
+ <strong>
14
+ <span style="font-size: 30px;">EagleMeshTransformer</span>
15
+ </strong>
16
+ </p>
17
+
18
+ # Model Overview
19
+
20
+ EagleMeshTransformer is a multiscale Mesh Transformer developed by the LIRIS research laboratory in Lyon, France, for fluid prediction on dynamic unstructured meshes. It is particularly well suited to unsteady turbulent flows and problems involving long-range dependencies in flow fields.
21
+
22
+ Paper: [EAGLE: Large-scale Learning of Turbulent Fluid Dynamics with Mesh Transformers](https://arxiv.org/abs/2302.10803).
23
+
24
+ # Model Description
25
+
26
+ EagleMeshTransformer uses a multiscale Mesh Transformer architecture trained on the EAGLE dataset to predict velocity and pressure fields in complex unsteady flows.
27
+
28
+
29
+
30
+ ## Use Cases
31
+
32
+ | Use Case | Description |
33
+ |---|---|
34
+ | Unsteady turbulent flow prediction | Predict velocity and pressure fields in complex, aperiodic turbulent flows involving drones, jets, wakes, and similar systems |
35
+ | Unstructured-mesh simulation | Process irregular mesh data defined on complex geometries |
36
+ | CFD surrogate acceleration | Provide fast approximations of conventional Navier–Stokes and CFD simulations |
37
+ | Long-horizon physical prediction | Predict the evolution of physical states through autoregressive rollouts |
38
+
39
+ # Usage
40
+
41
+ ## 1. OneCode
42
+
43
+ Use the online OneCode environment for an intelligent, one-click AI for Science (AI4S) programming experience:
44
+
45
+ [Launch OneCode for one-click AI4S programming](https://web-2069360198568017922-iaaj.ksai.scnet.cn:58043/home)
46
+
47
+ ## 2. Manual Setup
48
+
49
+ **Hardware Requirements**
50
+
51
+ - A GPU or DCU is recommended.
52
+ - A CPU can be used for import checks and small-scale pipeline validation, but full training and inference will be slow.
53
+ - DCU users must install DTK in advance. DTK 25.04.2 or later, or the OneScience-recommended version for the target cluster, is recommended.
54
+
55
+ ### Download the Model Package
56
+
57
+ ```bash
58
+ modelscope download --model OneScience/EagleMeshTransformer --local_dir ./EagleMeshTransformer
59
+ cd EagleMeshTransformer
60
+ ```
61
+
62
+ ### Set Up the Runtime Environment
63
+
64
+ **DCU Environment**
65
+
66
+ ```bash
67
+ # Activate DTK and Conda first
68
+ conda create -n onescience311 python=3.11 -y
69
+ conda activate onescience311
70
+ # Installation with uv is also supported
71
+ pip install onescience[cfd-dcu] -i http://mirrors.onescience.ai:3141/pypi/simple/ --trusted-host mirrors.onescience.ai
72
+ ```
73
+
74
+ **GPU Environment**
75
+ ```bash
76
+ # Activate Conda first
77
+ conda create -n onescience311 python=3.11 -y libstdcxx-ng=12 libgcc-ng=12 gcc_linux-64=12 gxx_linux-64=12
78
+ conda activate onescience311
79
+ # Installation with uv is also supported
80
+ pip install onescience[cfd-gpu] -i http://mirrors.onescience.ai:3141/pypi/simple/ --trusted-host mirrors.onescience.ai
81
+ ```
82
+
83
+ ### Synthetic Data Validation
84
+
85
+ The default configuration points to the synthetic data directory in this repository and sets `training.max_epoch` to `1`. Generate a minimal EAGLE NPZ dataset to validate the training and inference pipelines:
86
+
87
+ ```bash
88
+ python scripts/fake_data.py
89
+ ```
90
+ ### Training Data
91
+ The OneScience community provides the EAGLE dataset for training. Download it with the command below and verify that the data path in `conf/config.yaml` is configured correctly.
92
+
93
+ ```bash
94
+ modelscope download --dataset OneScience/eagle --local_dir ./data
95
+ ```
96
+
97
+ ### Training
98
+
99
+ Single GPU:
100
+
101
+ ```bash
102
+ python scripts/train.py
103
+ ```
104
+
105
+ Multiple GPUs:
106
+
107
+ ```bash
108
+ torchrun --nproc_per_node=8 --nnodes=1 --rdzv_id=1000 --rdzv_backend=c10d --max_restarts=0 --master_addr="localhost" --master_port=29500 scripts/train.py
109
+ ```
110
+
111
+ Training saves `best_model.pth` in the `weight/` directory.
112
+
113
+ ### Model Weights
114
+ This repository will provide pretrained EagleMeshTransformer weights in the `weights/` directory. The weights will be uploaded soon.
115
+
116
+ ### Inference
117
+
118
+ ```bash
119
+ python scripts/inference.py
120
+ ```
121
+
122
+ Inference results are saved to `result/output/`.
123
+
124
+ ### Evaluation and Visualization
125
+
126
+ ```bash
127
+ python scripts/result.py
128
+ ```
129
+
130
+
131
+ # Official OneScience Resources
132
+
133
+ | Platform | OneScience Repository | Skills Repository |
134
+ | --- | --- | --- |
135
+ | Gitee | https://gitee.com/onescience-ai/onescience | https://gitee.com/onescience-ai/oneskills |
136
+ | GitHub | https://github.com/onescience-ai/OneScience | https://github.com/onescience-ai/oneskills |
137
+
138
+ # Citations and License
139
+ - Original EagleMeshTransformer paper: [EAGLE: Large-scale Learning of Turbulent Fluid Dynamics with Mesh Transformers](https://arxiv.org/abs/2302.10803).
140
+ - This repository retains source attribution and has been adapted for automated execution through OneScience and ModelScope.
config/config.yaml ADDED
@@ -0,0 +1,41 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ model:
2
+ name: "EagleMeshTransformer"
3
+ architecture: "GraphViT"
4
+ state_size: 4
5
+ w_size: 32
6
+
7
+ datapipe:
8
+ source:
9
+ data_dir: "./data/fake/Eagle_dataset"
10
+ cluster_dir: "./data/fake/Eagle_dataset"
11
+ splits_dir: "./data/fake/splits"
12
+
13
+ verbose: false
14
+
15
+ data:
16
+ window_length_train: 3
17
+ window_length_val: 3
18
+ window_length_test: 3
19
+ n_cluster: 1
20
+ normalized: true
21
+ type_as_onehot: true
22
+ with_cells: true
23
+
24
+ dataloader:
25
+ batch_size: 1
26
+ num_workers: 0
27
+
28
+ training:
29
+ max_epoch: 1
30
+ lr: 1.0e-4
31
+ loss_alpha: 0.1
32
+ patience: 10
33
+ checkpoint_dir: "./weight"
34
+ seed: 0
35
+ device: "cpu"
36
+
37
+ inference:
38
+ checkpoint_path: "./weight/best_model.pth"
39
+ output_dir: "./result/output"
40
+ batch_size: 1
41
+ device: "cpu"
configuration.json ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "framework": "Pytorch",
3
+ "task": "other",
4
+ "model": {
5
+ "type": "EagleMeshTransformer",
6
+ "architecture": "GraphViT"
7
+ },
8
+ "allow_remote": false
9
+ }
data/splits/test.txt ADDED
@@ -0,0 +1,118 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ Spl/184/2
2
+ Tri/194/2
3
+ Cre/185/1
4
+ Tri/199/1
5
+ Spl/188/1
6
+ Tri/182/2
7
+ Spl/181/2
8
+ Spl/199/1
9
+ Spl/187/2
10
+ Tri/189/2
11
+ Cre/182/1
12
+ Spl/181/1
13
+ Spl/182/2
14
+ Tri/197/2
15
+ Tri/181/2
16
+ Spl/199/2
17
+ Spl/188/2
18
+ Tri/185/1
19
+ Spl/182/1
20
+ Spl/183/1
21
+ Spl/185/2
22
+ Spl/191/2
23
+ Spl/191/1
24
+ Cre/191/1
25
+ Tri/195/1
26
+ Tri/191/1
27
+ Spl/189/1
28
+ Cre/191/2
29
+ Spl/185/1
30
+ Tri/200/2
31
+ Cre/183/1
32
+ Tri/187/2
33
+ Cre/200/1
34
+ Tri/186/2
35
+ Tri/198/2
36
+ Cre/188/1
37
+ Cre/192/1
38
+ Tri/197/1
39
+ Tri/185/2
40
+ Cre/189/2
41
+ Cre/193/1
42
+ Spl/200/1
43
+ Cre/182/2
44
+ Spl/198/2
45
+ Spl/200/2
46
+ Tri/193/1
47
+ Tri/194/1
48
+ Cre/181/1
49
+ Tri/182/1
50
+ Tri/184/2
51
+ Cre/190/1
52
+ Tri/198/1
53
+ Spl/190/1
54
+ Cre/194/1
55
+ Tri/184/1
56
+ Cre/186/2
57
+ Spl/193/1
58
+ Tri/199/2
59
+ Spl/183/2
60
+ Spl/197/1
61
+ Cre/184/2
62
+ Spl/195/2
63
+ Cre/195/2
64
+ Cre/198/1
65
+ Cre/197/2
66
+ Tri/190/2
67
+ Tri/193/2
68
+ Spl/186/1
69
+ Cre/199/2
70
+ Cre/193/2
71
+ Tri/195/2
72
+ Cre/199/1
73
+ Spl/184/1
74
+ Cre/187/2
75
+ Tri/188/2
76
+ Tri/181/1
77
+ Tri/183/2
78
+ Cre/198/2
79
+ Cre/189/1
80
+ Cre/195/1
81
+ Tri/192/1
82
+ Cre/196/2
83
+ Cre/192/2
84
+ Spl/197/2
85
+ Tri/186/1
86
+ Tri/196/2
87
+ Tri/188/1
88
+ Spl/196/1
89
+ Tri/200/1
90
+ Spl/190/2
91
+ Cre/194/2
92
+ Spl/192/1
93
+ Cre/190/2
94
+ Cre/187/1
95
+ Spl/198/1
96
+ Spl/189/2
97
+ Cre/186/1
98
+ Cre/188/2
99
+ Spl/193/2
100
+ Spl/195/1
101
+ Tri/187/1
102
+ Spl/192/2
103
+ Cre/184/1
104
+ Tri/189/1
105
+ Spl/187/1
106
+ Tri/192/2
107
+ Tri/190/1
108
+ Cre/181/2
109
+ Cre/185/2
110
+ Tri/196/1
111
+ Cre/200/2
112
+ Cre/183/2
113
+ Cre/196/1
114
+ Spl/186/2
115
+ Spl/196/2
116
+ Tri/183/1
117
+ Tri/191/2
118
+ Cre/197/1
data/splits/train.txt ADDED
@@ -0,0 +1,947 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ Spl/20/2
2
+ Spl/26/1
3
+ Spl/85/1
4
+ Cre/55/2
5
+ Spl/38/2
6
+ Tri/124/1
7
+ Tri/78/1
8
+ Cre/103/2
9
+ Cre/148/2
10
+ Tri/96/2
11
+ Tri/66/2
12
+ Cre/63/2
13
+ Spl/22/2
14
+ Tri/110/2
15
+ Tri/35/2
16
+ Tri/26/2
17
+ Cre/108/1
18
+ Spl/124/2
19
+ Spl/73/1
20
+ Tri/15/2
21
+ Tri/125/1
22
+ Spl/53/2
23
+ Tri/79/2
24
+ Spl/57/2
25
+ Cre/109/2
26
+ Spl/32/1
27
+ Spl/51/2
28
+ Cre/145/2
29
+ Tri/102/1
30
+ Tri/8/1
31
+ Cre/28/2
32
+ Tri/17/1
33
+ Tri/73/2
34
+ Spl/137/1
35
+ Spl/2/1
36
+ Tri/1/2
37
+ Spl/106/2
38
+ Tri/49/2
39
+ Cre/73/1
40
+ Cre/25/1
41
+ Tri/106/2
42
+ Tri/140/2
43
+ Cre/143/1
44
+ Tri/10/2
45
+ Tri/40/1
46
+ Tri/122/1
47
+ Cre/111/2
48
+ Cre/41/2
49
+ Spl/71/1
50
+ Spl/146/1
51
+ Spl/50/2
52
+ Spl/136/1
53
+ Cre/104/1
54
+ Cre/152/1
55
+ Spl/30/1
56
+ Tri/105/1
57
+ Tri/118/2
58
+ Spl/149/1
59
+ Cre/138/2
60
+ Cre/122/2
61
+ Spl/43/2
62
+ Cre/31/2
63
+ Spl/120/2
64
+ Cre/69/2
65
+ Spl/153/1
66
+ Spl/44/1
67
+ Spl/155/2
68
+ Spl/155/1
69
+ Tri/103/2
70
+ Tri/42/1
71
+ Tri/101/2
72
+ Tri/74/1
73
+ Cre/122/1
74
+ Spl/34/1
75
+ Spl/34/2
76
+ Cre/141/1
77
+ Cre/35/1
78
+ Spl/129/1
79
+ Spl/70/1
80
+ Tri/130/1
81
+ Tri/133/2
82
+ Tri/44/2
83
+ Spl/121/1
84
+ Cre/13/2
85
+ Tri/60/2
86
+ Cre/23/2
87
+ Cre/134/2
88
+ Tri/100/1
89
+ Tri/6/1
90
+ Tri/99/2
91
+ Spl/116/2
92
+ Tri/100/2
93
+ Tri/6/2
94
+ Tri/124/2
95
+ Cre/101/2
96
+ Cre/63/1
97
+ Tri/147/2
98
+ Spl/103/2
99
+ Cre/18/1
100
+ Cre/131/1
101
+ Cre/81/1
102
+ Spl/74/1
103
+ Cre/18/2
104
+ Spl/139/2
105
+ Cre/74/2
106
+ Spl/20/1
107
+ Cre/15/2
108
+ Cre/153/1
109
+ Cre/70/2
110
+ Tri/104/1
111
+ Spl/58/2
112
+ Spl/102/2
113
+ Cre/136/1
114
+ Tri/25/1
115
+ Cre/28/1
116
+ Spl/48/1
117
+ Tri/30/1
118
+ Cre/35/2
119
+ Tri/103/1
120
+ Spl/59/1
121
+ Spl/84/2
122
+ Tri/5/2
123
+ Cre/151/2
124
+ Tri/1/1
125
+ Cre/65/1
126
+ Cre/117/1
127
+ Spl/10/1
128
+ Spl/117/2
129
+ Spl/101/2
130
+ Cre/152/2
131
+ Tri/140/1
132
+ Cre/62/2
133
+ Tri/133/1
134
+ Spl/133/2
135
+ Tri/19/2
136
+ Spl/45/2
137
+ Tri/109/1
138
+ Cre/44/2
139
+ Cre/126/1
140
+ Tri/134/1
141
+ Cre/23/1
142
+ Spl/150/2
143
+ Cre/88/2
144
+ Tri/147/1
145
+ Cre/58/1
146
+ Tri/37/2
147
+ Spl/150/1
148
+ Tri/159/2
149
+ Spl/96/2
150
+ Spl/66/1
151
+ Spl/158/2
152
+ Spl/122/1
153
+ Spl/35/2
154
+ Spl/126/1
155
+ Tri/157/1
156
+ Tri/18/1
157
+ Spl/58/1
158
+ Tri/52/1
159
+ Tri/73/1
160
+ Tri/151/2
161
+ Cre/53/1
162
+ Spl/35/1
163
+ Spl/28/1
164
+ Spl/118/2
165
+ Cre/119/1
166
+ Spl/4/1
167
+ Cre/57/2
168
+ Spl/81/2
169
+ Spl/139/1
170
+ Cre/46/1
171
+ Spl/12/2
172
+ Cre/131/2
173
+ Cre/55/1
174
+ Spl/60/1
175
+ Tri/9/2
176
+ Spl/36/2
177
+ Cre/71/1
178
+ Spl/32/2
179
+ Tri/81/1
180
+ Spl/100/2
181
+ Tri/122/2
182
+ Spl/33/2
183
+ Spl/37/1
184
+ Spl/91/2
185
+ Spl/27/2
186
+ Spl/156/1
187
+ Spl/47/1
188
+ Cre/6/2
189
+ Tri/46/1
190
+ Cre/54/2
191
+ Tri/137/1
192
+ Spl/114/2
193
+ Tri/78/2
194
+ Spl/23/2
195
+ Spl/84/1
196
+ Spl/101/1
197
+ Spl/40/1
198
+ Cre/29/2
199
+ Tri/65/1
200
+ Spl/60/2
201
+ Tri/142/1
202
+ Spl/154/1
203
+ Cre/40/1
204
+ Tri/38/2
205
+ Spl/87/1
206
+ Cre/14/1
207
+ Tri/95/1
208
+ Tri/118/1
209
+ Spl/81/1
210
+ Spl/152/1
211
+ Tri/61/2
212
+ Tri/129/2
213
+ Cre/119/2
214
+ Cre/142/2
215
+ Tri/96/1
216
+ Cre/47/1
217
+ Spl/86/2
218
+ Spl/147/2
219
+ Tri/26/1
220
+ Tri/49/1
221
+ Cre/91/2
222
+ Cre/45/1
223
+ Spl/156/2
224
+ Tri/19/1
225
+ Cre/59/1
226
+ Cre/146/1
227
+ Spl/25/2
228
+ Cre/110/2
229
+ Spl/127/2
230
+ Tri/136/1
231
+ Cre/4/1
232
+ Tri/148/1
233
+ Cre/48/1
234
+ Spl/2/2
235
+ Spl/14/2
236
+ Spl/99/2
237
+ Tri/22/2
238
+ Cre/116/1
239
+ Tri/114/1
240
+ Cre/94/2
241
+ Spl/109/1
242
+ Cre/85/1
243
+ Cre/27/1
244
+ Tri/97/1
245
+ Tri/77/2
246
+ Cre/132/2
247
+ Cre/137/1
248
+ Cre/77/1
249
+ Tri/144/1
250
+ Tri/127/1
251
+ Spl/135/1
252
+ Cre/3/2
253
+ Spl/96/1
254
+ Cre/90/1
255
+ Cre/89/1
256
+ Tri/88/1
257
+ Tri/92/2
258
+ Tri/37/1
259
+ Tri/125/2
260
+ Tri/86/2
261
+ Spl/46/1
262
+ Cre/65/2
263
+ Cre/84/1
264
+ Spl/134/1
265
+ Cre/137/2
266
+ Tri/17/2
267
+ Spl/133/1
268
+ Spl/11/2
269
+ Tri/155/1
270
+ Tri/57/1
271
+ Cre/140/1
272
+ Cre/26/1
273
+ Spl/113/2
274
+ Spl/63/2
275
+ Spl/105/1
276
+ Tri/51/2
277
+ Tri/88/2
278
+ Tri/36/2
279
+ Tri/80/2
280
+ Tri/92/1
281
+ Cre/79/1
282
+ Tri/28/1
283
+ Spl/56/2
284
+ Tri/59/2
285
+ Tri/98/1
286
+ Spl/6/2
287
+ Tri/120/1
288
+ Spl/75/1
289
+ Cre/147/1
290
+ Tri/27/2
291
+ Tri/71/1
292
+ Spl/33/1
293
+ Tri/27/1
294
+ Spl/77/2
295
+ Tri/48/2
296
+ Spl/15/2
297
+ Spl/89/1
298
+ Spl/140/1
299
+ Cre/117/2
300
+ Spl/148/1
301
+ Tri/43/2
302
+ Spl/119/1
303
+ Cre/101/1
304
+ Spl/152/2
305
+ Cre/49/1
306
+ Tri/39/1
307
+ Spl/157/1
308
+ Spl/11/1
309
+ Tri/32/2
310
+ Spl/21/1
311
+ Cre/76/2
312
+ Spl/72/1
313
+ Cre/87/1
314
+ Cre/158/1
315
+ Cre/120/2
316
+ Tri/66/1
317
+ Cre/16/1
318
+ Cre/29/1
319
+ Spl/3/1
320
+ Tri/23/1
321
+ Cre/50/2
322
+ Cre/89/2
323
+ Tri/144/2
324
+ Tri/76/1
325
+ Spl/95/1
326
+ Tri/136/2
327
+ Tri/104/2
328
+ Cre/159/1
329
+ Tri/36/1
330
+ Spl/77/1
331
+ Spl/61/2
332
+ Tri/94/2
333
+ Spl/88/2
334
+ Tri/158/1
335
+ Cre/13/1
336
+ Tri/79/1
337
+ Cre/99/2
338
+ Cre/120/1
339
+ Cre/157/2
340
+ Spl/79/2
341
+ Tri/138/1
342
+ Cre/91/1
343
+ Tri/76/2
344
+ Tri/13/1
345
+ Tri/138/2
346
+ Tri/58/1
347
+ Cre/126/2
348
+ Tri/116/2
349
+ Tri/112/2
350
+ Tri/105/2
351
+ Tri/60/1
352
+ Cre/15/1
353
+ Cre/86/2
354
+ Cre/74/1
355
+ Spl/63/1
356
+ Spl/18/2
357
+ Tri/83/1
358
+ Tri/145/2
359
+ Cre/16/2
360
+ Spl/67/1
361
+ Spl/13/2
362
+ Spl/83/1
363
+ Cre/78/2
364
+ Cre/68/1
365
+ Cre/127/1
366
+ Tri/61/1
367
+ Tri/106/1
368
+ Cre/66/1
369
+ Cre/146/2
370
+ Cre/130/2
371
+ Tri/129/1
372
+ Spl/62/1
373
+ Spl/125/2
374
+ Tri/115/1
375
+ Cre/6/1
376
+ Spl/75/2
377
+ Tri/120/2
378
+ Cre/156/2
379
+ Cre/100/1
380
+ Cre/124/1
381
+ Cre/107/2
382
+ Spl/68/1
383
+ Cre/138/1
384
+ Cre/75/1
385
+ Cre/53/2
386
+ Tri/34/1
387
+ Tri/152/2
388
+ Tri/90/2
389
+ Tri/160/2
390
+ Cre/154/2
391
+ Tri/115/2
392
+ Spl/86/1
393
+ Spl/10/2
394
+ Spl/24/1
395
+ Cre/147/2
396
+ Cre/9/2
397
+ Cre/129/2
398
+ Cre/136/2
399
+ Spl/1/2
400
+ Cre/130/1
401
+ Cre/96/1
402
+ Cre/118/2
403
+ Tri/8/2
404
+ Cre/39/1
405
+ Spl/15/1
406
+ Spl/65/1
407
+ Cre/115/2
408
+ Tri/35/1
409
+ Cre/4/2
410
+ Spl/134/2
411
+ Spl/31/2
412
+ Cre/34/1
413
+ Cre/58/2
414
+ Tri/77/1
415
+ Cre/11/1
416
+ Cre/133/1
417
+ Tri/135/1
418
+ Tri/45/2
419
+ Spl/99/1
420
+ Cre/121/1
421
+ Tri/47/2
422
+ Tri/62/2
423
+ Spl/19/2
424
+ Spl/143/2
425
+ Tri/130/2
426
+ Spl/78/1
427
+ Tri/56/2
428
+ Cre/40/2
429
+ Tri/41/2
430
+ Spl/49/2
431
+ Tri/84/1
432
+ Tri/74/2
433
+ Cre/10/1
434
+ Cre/83/1
435
+ Tri/50/1
436
+ Spl/40/2
437
+ Tri/32/1
438
+ Tri/21/2
439
+ Spl/73/2
440
+ Cre/3/1
441
+ Cre/57/1
442
+ Cre/157/1
443
+ Tri/128/2
444
+ Tri/110/1
445
+ Tri/64/2
446
+ Tri/107/1
447
+ Tri/3/2
448
+ Spl/79/1
449
+ Cre/128/2
450
+ Tri/54/1
451
+ Spl/37/2
452
+ Tri/68/1
453
+ Tri/14/2
454
+ Cre/127/2
455
+ Spl/43/1
456
+ Tri/65/2
457
+ Cre/109/1
458
+ Tri/15/1
459
+ Tri/4/1
460
+ Cre/104/2
461
+ Spl/149/2
462
+ Spl/66/2
463
+ Spl/122/2
464
+ Cre/153/2
465
+ Spl/18/1
466
+ Cre/39/2
467
+ Tri/52/2
468
+ Cre/140/2
469
+ Cre/93/2
470
+ Spl/64/2
471
+ Cre/118/1
472
+ Tri/113/1
473
+ Cre/27/2
474
+ Cre/38/1
475
+ Tri/159/1
476
+ Cre/56/1
477
+ Spl/128/1
478
+ Cre/11/2
479
+ Tri/5/1
480
+ Cre/54/1
481
+ Cre/66/2
482
+ Spl/148/2
483
+ Cre/128/1
484
+ Cre/31/1
485
+ Spl/45/1
486
+ Tri/33/2
487
+ Cre/116/2
488
+ Tri/72/1
489
+ Tri/152/1
490
+ Tri/20/2
491
+ Tri/117/1
492
+ Tri/62/1
493
+ Tri/51/1
494
+ Spl/135/2
495
+ Cre/30/1
496
+ Spl/117/1
497
+ Cre/50/1
498
+ Spl/88/1
499
+ Tri/97/2
500
+ Spl/16/2
501
+ Cre/150/2
502
+ Tri/155/2
503
+ Spl/159/2
504
+ Spl/74/2
505
+ Tri/29/2
506
+ Tri/156/1
507
+ Tri/57/2
508
+ Cre/112/1
509
+ Tri/91/1
510
+ Tri/70/2
511
+ Spl/112/1
512
+ Tri/42/2
513
+ Tri/150/2
514
+ Cre/5/1
515
+ Spl/127/1
516
+ Spl/47/2
517
+ Spl/51/1
518
+ Cre/102/1
519
+ Cre/44/1
520
+ Cre/34/2
521
+ Cre/45/2
522
+ Tri/131/1
523
+ Cre/86/1
524
+ Cre/80/2
525
+ Spl/24/2
526
+ Spl/41/2
527
+ Cre/20/1
528
+ Spl/72/2
529
+ Tri/95/2
530
+ Tri/143/2
531
+ Spl/57/1
532
+ Spl/65/2
533
+ Tri/16/2
534
+ Spl/138/2
535
+ Tri/83/2
536
+ Tri/87/1
537
+ Spl/144/1
538
+ Spl/160/2
539
+ Spl/83/2
540
+ Cre/154/1
541
+ Tri/9/1
542
+ Tri/47/1
543
+ Tri/148/2
544
+ Cre/37/1
545
+ Tri/108/2
546
+ Cre/92/2
547
+ Cre/158/2
548
+ Cre/25/2
549
+ Spl/50/1
550
+ Tri/85/2
551
+ Tri/141/1
552
+ Cre/151/1
553
+ Cre/67/1
554
+ Tri/156/2
555
+ Tri/43/1
556
+ Tri/25/2
557
+ Cre/149/1
558
+ Spl/94/1
559
+ Cre/84/2
560
+ Cre/139/1
561
+ Cre/22/1
562
+ Tri/14/1
563
+ Spl/129/2
564
+ Tri/29/1
565
+ Tri/13/2
566
+ Spl/123/1
567
+ Spl/76/1
568
+ Cre/78/1
569
+ Cre/71/2
570
+ Cre/5/2
571
+ Tri/33/1
572
+ Cre/107/1
573
+ Cre/92/1
574
+ Tri/116/1
575
+ Tri/31/2
576
+ Tri/63/2
577
+ Tri/75/2
578
+ Tri/160/1
579
+ Tri/139/1
580
+ Spl/7/2
581
+ Tri/28/2
582
+ Tri/2/1
583
+ Spl/4/2
584
+ Spl/158/1
585
+ Cre/22/2
586
+ Cre/10/2
587
+ Cre/143/2
588
+ Cre/80/1
589
+ Cre/59/2
590
+ Tri/18/2
591
+ Spl/132/2
592
+ Cre/69/1
593
+ Cre/132/1
594
+ Spl/61/1
595
+ Tri/72/2
596
+ Spl/85/2
597
+ Spl/59/2
598
+ Cre/42/2
599
+ Tri/44/1
600
+ Spl/97/1
601
+ Cre/52/1
602
+ Cre/94/1
603
+ Cre/144/1
604
+ Cre/155/2
605
+ Cre/102/2
606
+ Cre/61/2
607
+ Spl/120/1
608
+ Cre/51/1
609
+ Tri/111/2
610
+ Cre/2/1
611
+ Spl/130/1
612
+ Cre/110/1
613
+ Cre/2/2
614
+ Spl/159/1
615
+ Tri/54/2
616
+ Spl/8/1
617
+ Cre/8/2
618
+ Spl/94/2
619
+ Cre/43/2
620
+ Spl/130/2
621
+ Cre/97/1
622
+ Tri/53/2
623
+ Cre/26/2
624
+ Spl/46/2
625
+ Cre/160/2
626
+ Spl/121/2
627
+ Tri/94/1
628
+ Spl/93/2
629
+ Cre/1/1
630
+ Tri/150/1
631
+ Spl/26/2
632
+ Spl/80/1
633
+ Tri/111/1
634
+ Tri/69/2
635
+ Cre/81/2
636
+ Spl/157/2
637
+ Cre/33/2
638
+ Tri/69/1
639
+ Cre/85/2
640
+ Cre/24/2
641
+ Tri/55/1
642
+ Spl/9/2
643
+ Tri/46/2
644
+ Cre/17/1
645
+ Spl/16/1
646
+ Spl/119/2
647
+ Tri/34/2
648
+ Spl/140/2
649
+ Spl/7/1
650
+ Cre/115/1
651
+ Spl/22/1
652
+ Cre/82/2
653
+ Tri/12/1
654
+ Cre/99/1
655
+ Tri/145/1
656
+ Spl/125/1
657
+ Spl/131/1
658
+ Spl/14/1
659
+ Tri/53/1
660
+ Spl/55/1
661
+ Spl/143/1
662
+ Cre/68/2
663
+ Spl/56/1
664
+ Spl/5/1
665
+ Tri/143/1
666
+ Spl/128/2
667
+ Spl/154/2
668
+ Tri/153/1
669
+ Tri/21/1
670
+ Tri/87/2
671
+ Cre/123/2
672
+ Cre/70/1
673
+ Cre/144/2
674
+ Spl/54/1
675
+ Spl/69/2
676
+ Spl/78/2
677
+ Cre/41/1
678
+ Tri/75/1
679
+ Cre/7/1
680
+ Tri/128/1
681
+ Cre/79/2
682
+ Spl/36/1
683
+ Spl/17/2
684
+ Tri/39/2
685
+ Spl/126/2
686
+ Spl/13/1
687
+ Cre/160/1
688
+ Spl/114/1
689
+ Tri/132/1
690
+ Tri/50/2
691
+ Spl/29/2
692
+ Tri/82/1
693
+ Cre/7/2
694
+ Cre/90/2
695
+ Spl/21/2
696
+ Tri/67/1
697
+ Tri/146/2
698
+ Cre/61/1
699
+ Spl/146/2
700
+ Tri/112/1
701
+ Tri/16/1
702
+ Cre/83/2
703
+ Cre/82/1
704
+ Spl/27/1
705
+ Cre/30/2
706
+ Spl/108/1
707
+ Spl/25/1
708
+ Spl/70/2
709
+ Cre/87/2
710
+ Tri/132/2
711
+ Tri/81/2
712
+ Tri/24/2
713
+ Spl/52/1
714
+ Cre/103/1
715
+ Spl/62/2
716
+ Spl/92/2
717
+ Spl/52/2
718
+ Spl/104/1
719
+ Cre/21/1
720
+ Tri/80/1
721
+ Tri/98/2
722
+ Spl/76/2
723
+ Cre/93/1
724
+ Tri/64/1
725
+ Tri/20/1
726
+ Spl/112/2
727
+ Spl/12/1
728
+ Cre/37/2
729
+ Cre/114/1
730
+ Cre/36/2
731
+ Spl/97/2
732
+ Spl/71/2
733
+ Tri/158/2
734
+ Tri/58/2
735
+ Tri/119/2
736
+ Cre/12/2
737
+ Spl/87/2
738
+ Cre/32/1
739
+ Spl/53/1
740
+ Cre/108/2
741
+ Tri/86/1
742
+ Cre/64/1
743
+ Spl/48/2
744
+ Cre/95/2
745
+ Cre/100/2
746
+ Cre/12/1
747
+ Spl/64/1
748
+ Spl/80/2
749
+ Tri/30/2
750
+ Spl/123/2
751
+ Spl/91/1
752
+ Cre/62/1
753
+ Spl/102/1
754
+ Tri/151/1
755
+ Spl/100/1
756
+ Spl/6/1
757
+ Spl/110/2
758
+ Cre/14/2
759
+ Spl/141/2
760
+ Spl/29/1
761
+ Spl/144/2
762
+ Tri/114/2
763
+ Spl/5/2
764
+ Tri/134/2
765
+ Cre/24/1
766
+ Cre/113/1
767
+ Spl/38/1
768
+ Cre/75/2
769
+ Spl/138/1
770
+ Spl/131/2
771
+ Tri/59/1
772
+ Spl/109/2
773
+ Cre/88/1
774
+ Tri/31/1
775
+ Cre/113/2
776
+ Spl/90/2
777
+ Cre/60/2
778
+ Cre/139/2
779
+ Spl/55/2
780
+ Cre/9/1
781
+ Cre/125/1
782
+ Cre/98/1
783
+ Tri/12/2
784
+ Spl/3/2
785
+ Spl/106/1
786
+ Tri/90/1
787
+ Cre/60/1
788
+ Spl/98/2
789
+ Tri/7/1
790
+ Cre/43/1
791
+ Tri/23/2
792
+ Spl/39/2
793
+ Spl/9/1
794
+ Spl/19/1
795
+ Tri/93/1
796
+ Tri/71/2
797
+ Cre/141/2
798
+ Tri/137/2
799
+ Tri/55/2
800
+ Tri/142/2
801
+ Spl/145/1
802
+ Cre/111/1
803
+ Cre/121/2
804
+ Tri/108/1
805
+ Cre/142/1
806
+ Tri/109/2
807
+ Spl/151/2
808
+ Tri/41/1
809
+ Spl/30/2
810
+ Spl/108/2
811
+ Spl/39/1
812
+ Spl/115/1
813
+ Cre/20/2
814
+ Cre/77/2
815
+ Cre/67/2
816
+ Spl/68/2
817
+ Spl/28/2
818
+ Cre/46/2
819
+ Cre/36/1
820
+ Tri/84/2
821
+ Spl/132/1
822
+ Spl/17/1
823
+ Tri/154/2
824
+ Cre/17/2
825
+ Cre/96/2
826
+ Cre/97/2
827
+ Cre/95/1
828
+ Cre/149/2
829
+ Cre/124/2
830
+ Tri/135/2
831
+ Tri/3/1
832
+ Spl/160/1
833
+ Spl/89/2
834
+ Tri/146/1
835
+ Spl/115/2
836
+ Tri/68/2
837
+ Cre/148/1
838
+ Tri/141/2
839
+ Spl/110/1
840
+ Tri/149/2
841
+ Spl/147/1
842
+ Tri/117/2
843
+ Spl/98/1
844
+ Tri/11/1
845
+ Tri/121/1
846
+ Cre/64/2
847
+ Spl/42/2
848
+ Cre/150/1
849
+ Tri/123/1
850
+ Cre/155/1
851
+ Cre/56/2
852
+ Tri/91/2
853
+ Tri/101/1
854
+ Tri/4/2
855
+ Cre/42/1
856
+ Cre/47/2
857
+ Tri/70/1
858
+ Tri/119/1
859
+ Spl/118/1
860
+ Cre/73/2
861
+ Spl/92/1
862
+ Cre/123/1
863
+ Cre/114/2
864
+ Tri/131/2
865
+ Cre/98/2
866
+ Cre/33/1
867
+ Spl/41/1
868
+ Tri/7/2
869
+ Spl/142/1
870
+ Cre/125/2
871
+ Tri/11/2
872
+ Spl/104/2
873
+ Tri/93/2
874
+ Tri/154/1
875
+ Cre/1/2
876
+ Spl/1/1
877
+ Spl/23/1
878
+ Spl/82/2
879
+ Spl/103/1
880
+ Spl/44/2
881
+ Tri/45/1
882
+ Spl/49/1
883
+ Cre/48/2
884
+ Cre/8/1
885
+ Tri/121/2
886
+ Spl/93/1
887
+ Cre/32/2
888
+ Cre/134/1
889
+ Tri/107/2
890
+ Spl/124/1
891
+ Tri/67/2
892
+ Spl/153/2
893
+ Tri/123/2
894
+ Spl/8/2
895
+ Tri/153/2
896
+ Tri/2/2
897
+ Tri/63/1
898
+ Tri/22/1
899
+ Cre/52/2
900
+ Tri/127/2
901
+ Tri/82/2
902
+ Tri/10/1
903
+ Spl/31/1
904
+ Spl/116/1
905
+ Spl/141/1
906
+ Cre/38/2
907
+ Spl/54/2
908
+ Cre/51/2
909
+ Cre/159/2
910
+ Cre/76/1
911
+ Spl/142/2
912
+ Tri/149/1
913
+ Spl/95/2
914
+ Spl/42/1
915
+ Tri/113/2
916
+ Cre/129/1
917
+ Spl/90/1
918
+ Tri/157/2
919
+ Cre/49/2
920
+ Spl/67/2
921
+ Cre/72/2
922
+ Cre/145/1
923
+ Cre/72/1
924
+ Tri/99/1
925
+ Cre/112/2
926
+ Spl/145/2
927
+ Spl/137/2
928
+ Tri/139/2
929
+ Tri/24/1
930
+ Tri/85/1
931
+ Cre/156/1
932
+ Spl/105/2
933
+ Spl/82/1
934
+ Spl/151/1
935
+ Tri/48/1
936
+ Tri/89/2
937
+ Tri/102/2
938
+ Cre/133/2
939
+ Cre/21/2
940
+ Tri/56/1
941
+ Spl/136/2
942
+ Spl/69/1
943
+ Tri/38/1
944
+ Spl/113/1
945
+ Tri/89/1
946
+ Cre/105/1
947
+ Cre/105/2
data/splits/valid.txt ADDED
@@ -0,0 +1,118 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ Spl/178/1
2
+ Cre/167/1
3
+ Tri/177/1
4
+ Spl/172/1
5
+ Tri/173/1
6
+ Cre/165/2
7
+ Spl/177/1
8
+ Cre/177/1
9
+ Tri/175/1
10
+ Spl/171/1
11
+ Tri/175/2
12
+ Cre/171/1
13
+ Cre/168/2
14
+ Cre/163/2
15
+ Spl/161/2
16
+ Cre/161/2
17
+ Tri/166/1
18
+ Spl/172/2
19
+ Tri/174/1
20
+ Tri/171/2
21
+ Tri/165/1
22
+ Tri/180/1
23
+ Tri/178/1
24
+ Spl/163/1
25
+ Tri/162/2
26
+ Cre/178/1
27
+ Tri/172/2
28
+ Cre/170/2
29
+ Spl/170/1
30
+ Tri/180/2
31
+ Tri/166/2
32
+ Tri/170/2
33
+ Spl/162/2
34
+ Cre/168/1
35
+ Spl/171/2
36
+ Tri/179/2
37
+ Cre/165/1
38
+ Cre/180/2
39
+ Spl/180/1
40
+ Cre/161/1
41
+ Tri/176/2
42
+ Spl/161/1
43
+ Spl/164/1
44
+ Tri/171/1
45
+ Cre/162/1
46
+ Cre/169/2
47
+ Spl/173/2
48
+ Spl/176/2
49
+ Cre/176/1
50
+ Spl/168/2
51
+ Spl/180/2
52
+ Cre/176/2
53
+ Tri/176/1
54
+ Cre/179/2
55
+ Cre/174/2
56
+ Tri/164/2
57
+ Cre/178/2
58
+ Tri/161/2
59
+ Spl/173/1
60
+ Cre/177/2
61
+ Spl/165/2
62
+ Spl/168/1
63
+ Cre/162/2
64
+ Spl/175/2
65
+ Tri/167/1
66
+ Tri/163/2
67
+ Spl/177/2
68
+ Tri/173/2
69
+ Cre/166/2
70
+ Cre/180/1
71
+ Tri/179/1
72
+ Cre/164/2
73
+ Tri/164/1
74
+ Spl/175/1
75
+ Cre/166/1
76
+ Spl/179/2
77
+ Cre/163/1
78
+ Spl/163/2
79
+ Cre/164/1
80
+ Tri/178/2
81
+ Spl/167/1
82
+ Spl/169/2
83
+ Cre/173/2
84
+ Cre/175/1
85
+ Spl/165/1
86
+ Tri/172/1
87
+ Tri/168/1
88
+ Spl/174/1
89
+ Tri/163/1
90
+ Tri/169/1
91
+ Cre/174/1
92
+ Cre/169/1
93
+ Tri/161/1
94
+ Tri/174/2
95
+ Spl/170/2
96
+ Spl/166/1
97
+ Tri/169/2
98
+ Spl/179/1
99
+ Spl/176/1
100
+ Spl/164/2
101
+ Spl/162/1
102
+ Tri/167/2
103
+ Spl/174/2
104
+ Cre/171/2
105
+ Tri/165/2
106
+ Tri/162/1
107
+ Spl/166/2
108
+ Tri/170/1
109
+ Tri/168/2
110
+ Cre/167/2
111
+ Cre/173/1
112
+ Cre/179/1
113
+ Tri/177/2
114
+ Spl/169/1
115
+ Cre/170/1
116
+ Cre/175/2
117
+ Spl/178/2
118
+ Spl/167/2
model/graphViT.py ADDED
@@ -0,0 +1,173 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import torch
2
+ import torch.nn as nn
3
+
4
+ from onescience.modules.decoder.graphvit_decoder import GraphViTDecoder
5
+ from onescience.modules.embedding.fourier_pos_embedding import FourierPosEmbedding
6
+ from onescience.modules.encoder.graphvit_encoder import GraphViTEncoder
7
+ from onescience.modules.pooling.rnn_cluster_pooling import RNNClusterPooling
8
+ from onescience.modules.transformer.preln_transformer_block import (
9
+ PreLNTransformerBlock,
10
+ )
11
+
12
+ # 节点类型常量
13
+ NODE_NORMAL = 0
14
+ NODE_INPUT = 4
15
+ NODE_OUTPUT = 5
16
+ NODE_WALL = 6
17
+ NODE_DISABLE = 2
18
+
19
+ class GraphViT(nn.Module):
20
+ """
21
+
22
+ 该模型利用图神经网络提取局部特征,通过聚类池化在潜在空间进行全局 Transformer 交互,
23
+ 最后解码回物理空间进行下一时刻的状态预测。
24
+
25
+ Args:
26
+ state_size (int): 物理状态的维度(如速度 u, v, w)。
27
+ w_size (int): 潜在空间(Cluster Level)的特征维度。默认值: 512。
28
+ n_attention (int): Transformer 注意力层的数量。默认值: 4。
29
+ nb_gn (int): Encoder 中 GNN 的层数。默认值: 4。
30
+ n_heads (int): Transformer 注意力头数。默认值: 4。
31
+ """
32
+ def __init__(self, state_size, w_size=512, n_attention=4, nb_gn=4, n_heads=4):
33
+ super(GraphViT, self).__init__()
34
+
35
+ # 傅里叶位置编码参数
36
+ pos_start = -3
37
+ pos_length = 8
38
+
39
+ # 1. 编码器: Mesh Space -> Latent Graph
40
+ self.encoder = GraphViTEncoder(
41
+ nb_gn=nb_gn,
42
+ state_size=state_size,
43
+ pos_length=pos_length
44
+ )
45
+
46
+ # 2. 池化层: Node Features -> Cluster Features
47
+ self.graph_pooling = RNNClusterPooling(
48
+ w_size=w_size,
49
+ pos_length=pos_length
50
+ )
51
+
52
+ # 3. 解码器: Cluster Features -> Node State Update
53
+ self.graph_retrieve = GraphViTDecoder(
54
+ w_size=w_size,
55
+ pos_length=pos_length,
56
+ state_size=state_size
57
+ )
58
+
59
+ # 4. 潜在空间 Transformer 交互层
60
+ self.attention = nn.ModuleList([
61
+ PreLNTransformerBlock(
62
+ w_size=w_size,
63
+ pos_length=pos_length,
64
+ n_heads=n_heads
65
+ )
66
+ for _ in range(n_attention)
67
+ ])
68
+
69
+ self.ln = nn.LayerNorm(w_size)
70
+ self.noise_std = 0.0
71
+
72
+ # 5. 坐标嵌入层
73
+ self.positional_encoder = FourierPosEmbedding(
74
+ pos_start=pos_start,
75
+ pos_length=pos_length
76
+ )
77
+
78
+ def forward(
79
+ self,
80
+ mesh_pos,
81
+ edges,
82
+ state,
83
+ node_type,
84
+ clusters,
85
+ clusters_mask,
86
+ apply_noise=False,
87
+ ):
88
+ """
89
+ 前向传播 (自回归滚动预测)。
90
+
91
+ 参数:
92
+ mesh_pos: (B, T, N, 3) 节点坐标
93
+ edges: (B, T, M, 2) 边索引
94
+ state: (B, T, N, state_size) 节点物理状态
95
+ node_type: (B, T, N, 9) 节点类型 (One-hot)
96
+ clusters: (B, T, K, C_max) 簇索引
97
+ clusters_mask: (B, T, K, C_max) 簇掩码
98
+ apply_noise: 是否在初始状态施加噪声 (Training 时通常为 True)
99
+ """
100
+ if apply_noise:
101
+ mask = torch.logical_or(
102
+ node_type[:, 0, :, NODE_NORMAL] == 1,
103
+ node_type[:, 0, :, NODE_OUTPUT] == 1,
104
+ )
105
+ noise = torch.randn_like(state[:, 0]).to(state.device) * self.noise_std
106
+ state[:, 0][mask] = state[:, 0][mask] + noise[mask]
107
+
108
+ state_hat = [state[:, 0]]
109
+ output_hat = []
110
+ target = []
111
+
112
+ # 从 t=1 开始预测,基于 t-1 的状态
113
+ for t in range(1, state.shape[1]):
114
+ # 1. 生成位置编码
115
+ mesh_posenc, cluster_posenc = self.positional_encoder(
116
+ mesh_pos[:, t - 1], clusters[:, t - 1], clusters_mask[:, t - 1]
117
+ )
118
+
119
+ # 2. 图节点和边编码
120
+ V, E = self.encoder(
121
+ mesh_pos[:, t - 1],
122
+ edges[:, t - 1],
123
+ state_hat[-1],
124
+ node_type[:, t - 1],
125
+ mesh_posenc,
126
+ )
127
+
128
+ # 3. 聚类池化
129
+ W = self.graph_pooling(
130
+ V, clusters[:, t - 1], mesh_posenc, clusters_mask[:, t - 1]
131
+ )
132
+
133
+ # 4. 构建 Attention Mask
134
+ attention_mask = clusters_mask[:, t - 1].sum(-1, keepdim=True) == 0
135
+ attention_mask = (
136
+ attention_mask.unsqueeze(1)
137
+ .repeat(1, len(self.attention), 1, W.shape[1])
138
+ .view(-1, W.shape[1], W.shape[1])
139
+ )
140
+ attention_mask[:, torch.eye(W.shape[1], dtype=torch.bool)] = False
141
+ attention_mask = attention_mask.transpose(-1, -2)
142
+
143
+ # 5. 潜在空间 Transformer 交互
144
+ for a in self.attention:
145
+ W = a(W, attention_mask, cluster_posenc)
146
+ W = self.ln(W)
147
+
148
+ # 6. 解码预测更新量
149
+ next_output = self.graph_retrieve(
150
+ W, V, clusters[:, t - 1], mesh_posenc, edges[:, t - 1], E
151
+ )
152
+
153
+ # 7. 更新下一时刻状态
154
+ next_state = state_hat[-1] + next_output
155
+ target.append(state[:, t] - state_hat[-1])
156
+
157
+ # 8. 强制边界条件 (Mask 覆盖)
158
+ mask = torch.logical_or(
159
+ node_type[:, t, :, NODE_INPUT] == 1, node_type[:, t, :, NODE_WALL] == 1
160
+ )
161
+ mask = torch.logical_or(mask, node_type[:, t, :, NODE_DISABLE] == 1)
162
+ next_state[mask, :] = state[:, t][mask, :]
163
+
164
+ state_hat.append(next_state)
165
+ output_hat.append(next_output)
166
+
167
+ # 堆叠时间步
168
+ velocity_hat = torch.stack(state_hat, dim=1)
169
+ output_hat = torch.stack(output_hat, dim=1)
170
+ target = torch.stack(target, dim=1)
171
+
172
+ # velocity_hat: [B, T, N, S], output_hat: [B, T-1, N, S]
173
+ return velocity_hat, output_hat, target
scripts/fake_data.py ADDED
@@ -0,0 +1,86 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import sys
3
+ from pathlib import Path
4
+
5
+ import numpy as np
6
+
7
+ PROJECT_ROOT = Path(__file__).resolve().parents[1]
8
+ sys.path.insert(0, str(PROJECT_ROOT / "model"))
9
+
10
+
11
+ def write_split(split_dir: Path, name: str, samples):
12
+ split_dir.mkdir(parents=True, exist_ok=True)
13
+ with open(split_dir / f"{name}.txt", "w", encoding="utf-8") as handle:
14
+ for sample in samples:
15
+ handle.write(f"{sample}\n")
16
+
17
+
18
+ def make_sample(path: Path, seed: int, steps: int = 990, nodes: int = 6):
19
+ rng = np.random.default_rng(seed)
20
+ path.mkdir(parents=True, exist_ok=True)
21
+
22
+ base = np.array(
23
+ [
24
+ [0.0, 0.0],
25
+ [1.0, 0.0],
26
+ [0.0, 1.0],
27
+ [1.0, 1.0],
28
+ [0.5, 0.2],
29
+ [0.5, 0.8],
30
+ ],
31
+ dtype=np.float32,
32
+ )
33
+ drift = np.linspace(0.0, 0.04, steps, dtype=np.float32).reshape(steps, 1, 1)
34
+ pointcloud = base.reshape(1, nodes, 2) + drift
35
+
36
+ t = np.arange(steps, dtype=np.float32).reshape(steps, 1)
37
+ x = pointcloud[..., 0]
38
+ y = pointcloud[..., 1]
39
+ vx = np.sin(x + t * 0.1).astype(np.float32)
40
+ vy = np.cos(y + t * 0.1).astype(np.float32)
41
+ ps = (x + y + 0.01 * rng.standard_normal((steps, nodes))).astype(np.float32)
42
+ pg = (x - y + 0.01 * rng.standard_normal((steps, nodes))).astype(np.float32)
43
+ mask = np.zeros((steps, nodes), dtype=np.int64)
44
+
45
+ triangles = np.array(
46
+ [
47
+ [0, 1, 4],
48
+ [0, 4, 2],
49
+ [1, 3, 4],
50
+ [2, 4, 5],
51
+ [3, 5, 4],
52
+ ],
53
+ dtype=np.int64,
54
+ )
55
+ triangles = np.repeat(triangles.reshape(1, -1, 3), steps, axis=0)
56
+
57
+ np.savez(
58
+ path / "sim.npz",
59
+ pointcloud=pointcloud,
60
+ VX=vx,
61
+ VY=vy,
62
+ PS=ps,
63
+ PG=pg,
64
+ mask=mask,
65
+ )
66
+ np.save(path / "triangles.npy", triangles)
67
+
68
+
69
+ def main():
70
+ os.chdir(PROJECT_ROOT)
71
+ data_root = PROJECT_ROOT / "data" / "fake" / "Eagle_dataset"
72
+ split_dir = PROJECT_ROOT / "data" / "fake" / "splits"
73
+ samples = ["Cre/fake_case_000/1", "Cre/fake_case_001/1", "Cre/fake_case_002/1"]
74
+
75
+ for idx, sample in enumerate(samples):
76
+ make_sample(data_root / sample, seed=idx)
77
+
78
+ write_split(split_dir, "train", samples[:2])
79
+ write_split(split_dir, "valid", samples[2:])
80
+ write_split(split_dir, "test", samples[2:])
81
+ print(f"Fake Eagle data written to: {data_root}")
82
+ print(f"Fake splits written to: {split_dir}")
83
+
84
+
85
+ if __name__ == "__main__":
86
+ main()
scripts/inference.py ADDED
@@ -0,0 +1,121 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import ctypes
2
+ import os
3
+ import sys
4
+ import sysconfig
5
+ from pathlib import Path
6
+
7
+
8
+ def preload_python_shared_library():
9
+ """Make libpython visible to native extensions loaded with ctypes."""
10
+ libdir = sysconfig.get_config_var("LIBDIR")
11
+ version = sysconfig.get_config_var("VERSION")
12
+ if not libdir or not version:
13
+ return
14
+
15
+ candidates = [
16
+ Path(libdir) / f"libpython{version}.so.1.0",
17
+ Path(libdir) / f"libpython{version}.so",
18
+ ]
19
+ for libpython in candidates:
20
+ if libpython.exists():
21
+ ctypes.CDLL(str(libpython), mode=ctypes.RTLD_GLOBAL)
22
+ return
23
+
24
+
25
+ preload_python_shared_library()
26
+
27
+ import numpy as np
28
+ import torch
29
+ from tqdm import tqdm
30
+
31
+ # 获取项目根目录(train.py上级的上级)
32
+ root_path = Path(__file__).parent.parent
33
+ sys.path.insert(0, str(root_path))
34
+
35
+ from model.graphViT import GraphViT
36
+ from onescience.distributed.manager import DistributedManager
37
+ from onescience.utils.YParams import YParams
38
+ from onescience.datapipes.cfd import EagleDatapipe
39
+
40
+
41
+ def resolve_project_path(path):
42
+ path = Path(path)
43
+ return path if path.is_absolute() else root_path / path
44
+
45
+
46
+ def fix_single_cluster_path(datapipe, cfg_data):
47
+ if int(cfg_data.data.n_cluster) != 1:
48
+ return
49
+
50
+ cluster_path = Path(cfg_data.source.cluster_dir)
51
+ for dataset_name in ("train_dataset", "val_dataset", "test_dataset"):
52
+ dataset = getattr(datapipe, dataset_name, None)
53
+ if dataset is not None and getattr(dataset, "cluster_path", None) is None:
54
+ dataset.cluster_path = cluster_path
55
+
56
+
57
+ def main():
58
+ os.chdir(root_path)
59
+ DistributedManager.initialize()
60
+ manager = DistributedManager()
61
+
62
+ config_path = root_path / "config" / "config.yaml"
63
+ cfg_model = YParams(config_path, "model")
64
+ cfg_data = YParams(config_path, "datapipe")
65
+ cfg_infer = YParams(config_path, "inference")
66
+
67
+ checkpoint_path = resolve_project_path(cfg_infer.checkpoint_path)
68
+ if not checkpoint_path.exists():
69
+ raise FileNotFoundError(
70
+ f"Checkpoint not found: {checkpoint_path}. Run `python scripts/train.py` first."
71
+ )
72
+
73
+ datapipe = EagleDatapipe(params=cfg_data, distributed=False)
74
+ fix_single_cluster_path(datapipe, cfg_data)
75
+ dataloader, _ = datapipe.test_dataloader(batch_size=int(cfg_infer.batch_size))
76
+ device_name = cfg_infer.get("device", "auto")
77
+ device = manager.device if device_name == "auto" else torch.device(device_name)
78
+ model = GraphViT(state_size=cfg_model.state_size, w_size=cfg_model.w_size).to(device)
79
+ checkpoint = torch.load(checkpoint_path, map_location=device, weights_only=False)
80
+ model.load_state_dict(checkpoint.get("model_state_dict", checkpoint))
81
+
82
+ output_dir = resolve_project_path(cfg_infer.output_dir)
83
+ output_dir.mkdir(parents=True, exist_ok=True)
84
+
85
+ model.eval()
86
+ saved = 0
87
+ with torch.no_grad():
88
+ for idx, x in enumerate(tqdm(dataloader, desc="Inference")):
89
+ if not x:
90
+ continue
91
+ mesh_pos = x["mesh_pos"].to(device)
92
+ edges = x["edges"].to(device).long()
93
+ velocity = x["velocity"].to(device)
94
+ pressure = x["pressure"].to(device)
95
+ node_type = x["node_type"].to(device)
96
+ clusters = x["cluster"].to(device).long()
97
+ clusters_mask = x["cluster_mask"].to(device).long()
98
+
99
+ state = torch.cat([velocity, pressure], dim=-1)
100
+ state_hat, output, target = model(
101
+ mesh_pos,
102
+ edges,
103
+ state,
104
+ node_type,
105
+ clusters,
106
+ clusters_mask,
107
+ apply_noise=False,
108
+ )
109
+ velocity_hat, pressure_hat = dataloader.dataset.denormalize(
110
+ state_hat[..., :2], state_hat[..., 2:]
111
+ )
112
+ pred = torch.cat([velocity_hat, pressure_hat], dim=-1).cpu().numpy()
113
+ np.save(output_dir / f"prediction_{idx:04d}.npy", pred)
114
+ saved += 1
115
+
116
+ print(f"Saved {saved} prediction file(s) to {output_dir}")
117
+ manager.cleanup()
118
+
119
+
120
+ if __name__ == "__main__":
121
+ main()
scripts/result.py ADDED
@@ -0,0 +1,23 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import sys
2
+ from pathlib import Path
3
+
4
+ import numpy as np
5
+
6
+ PROJECT_ROOT = Path(__file__).resolve().parents[1]
7
+
8
+
9
+ def main():
10
+ output_dir = PROJECT_ROOT / "result" / "output"
11
+ files = sorted(output_dir.glob("prediction_*.npy"))
12
+ if not files:
13
+ print(f"No prediction files found in {output_dir}")
14
+ return
15
+
16
+ print(f"Found {len(files)} prediction file(s) in {output_dir}")
17
+ for file in files[:5]:
18
+ arr = np.load(file)
19
+ print(f"{file.name}: shape={arr.shape}, mean={arr.mean():.6f}, std={arr.std():.6f}")
20
+
21
+
22
+ if __name__ == "__main__":
23
+ main()
scripts/train.py ADDED
@@ -0,0 +1,246 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import ctypes
2
+ import logging
3
+ import os
4
+ import random
5
+ import sys
6
+ import sysconfig
7
+ import time
8
+ from pathlib import Path
9
+
10
+
11
+ def preload_python_shared_library():
12
+ """Make libpython visible to native extensions loaded with ctypes."""
13
+ libdir = sysconfig.get_config_var("LIBDIR")
14
+ version = sysconfig.get_config_var("VERSION")
15
+ if not libdir or not version:
16
+ return
17
+
18
+ candidates = [
19
+ Path(libdir) / f"libpython{version}.so.1.0",
20
+ Path(libdir) / f"libpython{version}.so",
21
+ ]
22
+ for libpython in candidates:
23
+ if libpython.exists():
24
+ ctypes.CDLL(str(libpython), mode=ctypes.RTLD_GLOBAL)
25
+ return
26
+
27
+
28
+ preload_python_shared_library()
29
+
30
+ import numpy as np
31
+ import torch
32
+ import torch.nn as nn
33
+ import torch.distributed as dist
34
+ from torch.nn.parallel import DistributedDataParallel
35
+ from tqdm import tqdm
36
+
37
+ # 获取项目根目录(train.py上级的上级)
38
+ root_path = Path(__file__).parent.parent
39
+ sys.path.insert(0, str(root_path))
40
+
41
+ from model.graphViT import GraphViT
42
+ from onescience.distributed.manager import DistributedManager
43
+ from onescience.utils.YParams import YParams
44
+ from onescience.datapipes.cfd import EagleDatapipe
45
+
46
+
47
+
48
+ def save_best_model(model, optimizer, checkpoint_dir: str):
49
+ Path(checkpoint_dir).mkdir(parents=True, exist_ok=True)
50
+ model_to_save = model.module if hasattr(model, "module") else model
51
+ torch.save(
52
+ {
53
+ "model_state_dict": model_to_save.state_dict(),
54
+ "optimizer_state_dict": optimizer.state_dict(),
55
+ },
56
+ Path(checkpoint_dir) / "best_model.pth",
57
+ )
58
+
59
+
60
+ def load_best_model(model, checkpoint_dir: str, device: torch.device):
61
+ ckpt_path = Path(checkpoint_dir) / "best_model.pth"
62
+ checkpoint = torch.load(ckpt_path, map_location=device, weights_only=False)
63
+ state_dict = checkpoint.get("model_state_dict", checkpoint)
64
+ model.load_state_dict(state_dict)
65
+
66
+
67
+ def setup_logging(rank: int):
68
+ logging.basicConfig(
69
+ level=logging.INFO if rank == 0 else logging.WARNING,
70
+ format="%(asctime)s - %(levelname)s - %(message)s",
71
+ stream=sys.stdout,
72
+ force=True,
73
+ )
74
+ return logging.getLogger("train")
75
+
76
+
77
+ def get_loss(velocity, pressure, output, state_hat, target, mask, alpha):
78
+ velocity = velocity[:, 1:]
79
+ pressure = pressure[:, 1:]
80
+ velocity_hat = state_hat[:, 1:, :, :2]
81
+ pressure_hat = state_hat[:, 1:, :, 2:]
82
+ mask = mask[:, 1:].unsqueeze(-1)
83
+
84
+ loss_velocity = torch.sqrt(((velocity * mask - velocity_hat * mask) ** 2).mean(dim=-1)).mean()
85
+ loss_pressure = torch.sqrt(((pressure * mask - pressure_hat * mask) ** 2).mean(dim=-1)).mean()
86
+ mse = nn.MSELoss()
87
+ loss = mse(target[..., :2] * mask, output[..., :2] * mask)
88
+ loss = loss + alpha * mse(target[..., 2:] * mask, output[..., 2:] * mask)
89
+ return {"loss": loss, "MSE_velocity": loss_velocity, "MSE_pressure": loss_pressure}
90
+
91
+
92
+ def move_batch(x, device):
93
+ return {
94
+ "mesh_pos": x["mesh_pos"].to(device),
95
+ "edges": x["edges"].to(device).long(),
96
+ "velocity": x["velocity"].to(device),
97
+ "pressure": x["pressure"].to(device),
98
+ "node_type": x["node_type"].to(device),
99
+ "mask": x["mask"].to(device),
100
+ "cluster": x["cluster"].to(device).long(),
101
+ "cluster_mask": x["cluster_mask"].to(device).long(),
102
+ }
103
+
104
+
105
+ def fix_single_cluster_path(datapipe, cfg_data):
106
+ if int(cfg_data.data.n_cluster) != 1:
107
+ return
108
+
109
+ cluster_path = Path(cfg_data.source.cluster_dir)
110
+ for dataset_name in ("train_dataset", "val_dataset", "test_dataset"):
111
+ dataset = getattr(datapipe, dataset_name, None)
112
+ if dataset is not None and getattr(dataset, "cluster_path", None) is None:
113
+ dataset.cluster_path = cluster_path
114
+
115
+
116
+ def validate(model, dataloader, device, alpha, manager):
117
+ model.eval()
118
+ total_loss, count = 0.0, 0
119
+ with torch.no_grad():
120
+ for x in dataloader:
121
+ if not x:
122
+ continue
123
+ batch = move_batch(x, device)
124
+ state = torch.cat([batch["velocity"], batch["pressure"]], dim=-1)
125
+ state_hat, output, target = model(
126
+ batch["mesh_pos"],
127
+ batch["edges"],
128
+ state,
129
+ batch["node_type"],
130
+ batch["cluster"],
131
+ batch["cluster_mask"],
132
+ apply_noise=False,
133
+ )
134
+ dataset = dataloader.dataset
135
+ state_hat[..., :2], state_hat[..., 2:] = dataset.denormalize(
136
+ state_hat[..., :2], state_hat[..., 2:]
137
+ )
138
+ velocity, pressure = dataset.denormalize(batch["velocity"], batch["pressure"])
139
+ costs = get_loss(velocity, pressure, output, state_hat, target, batch["mask"], alpha)
140
+ if manager.world_size > 1:
141
+ dist.all_reduce(costs["loss"], op=dist.ReduceOp.AVG)
142
+ total_loss += costs["loss"].item()
143
+ count += 1
144
+ return total_loss / max(count, 1)
145
+
146
+
147
+ def main():
148
+ os.chdir(root_path)
149
+ DistributedManager.initialize()
150
+ manager = DistributedManager()
151
+ logger = setup_logging(manager.rank)
152
+
153
+ config_path = root_path / "config" / "config.yaml"
154
+ cfg_model = YParams(config_path, "model")
155
+ cfg_data = YParams(config_path, "datapipe")
156
+ cfg_train = YParams(config_path, "training")
157
+
158
+ seed = int(cfg_train.get("seed", 0))
159
+ torch.manual_seed(seed)
160
+ random.seed(seed)
161
+ np.random.seed(seed)
162
+
163
+ datapipe = EagleDatapipe(params=cfg_data, distributed=(manager.world_size > 1))
164
+ fix_single_cluster_path(datapipe, cfg_data)
165
+ train_loader, train_sampler = datapipe.train_dataloader()
166
+ val_loader, val_sampler = datapipe.val_dataloader()
167
+
168
+ device_name = cfg_train.get("device", "auto")
169
+ device = manager.device if device_name == "auto" else torch.device(device_name)
170
+ model = GraphViT(state_size=cfg_model.state_size, w_size=cfg_model.w_size).to(device)
171
+ if manager.world_size > 1:
172
+ model = DistributedDataParallel(model, device_ids=[manager.local_rank], output_device=manager.local_rank)
173
+
174
+ optimizer = torch.optim.Adam(model.parameters(), lr=float(cfg_train.lr))
175
+ best_valid_loss = float("inf")
176
+ best_epoch = 0
177
+
178
+ for epoch in range(int(cfg_train.max_epoch)):
179
+ start = time.time()
180
+ if manager.world_size > 1:
181
+ train_sampler.set_epoch(epoch)
182
+ if val_sampler:
183
+ val_sampler.set_epoch(epoch)
184
+
185
+ model.train()
186
+ train_loss, count = 0.0, 0
187
+ pbar = tqdm(train_loader, desc=f"Epoch {epoch + 1}", disable=(manager.rank != 0))
188
+ for x in pbar:
189
+ if not x:
190
+ continue
191
+ batch = move_batch(x, device)
192
+ state = torch.cat([batch["velocity"], batch["pressure"]], dim=-1)
193
+ state_hat, output, target = model(
194
+ batch["mesh_pos"],
195
+ batch["edges"],
196
+ state,
197
+ batch["node_type"],
198
+ batch["cluster"],
199
+ batch["cluster_mask"],
200
+ apply_noise=True,
201
+ )
202
+ state_hat[..., :2], state_hat[..., 2:] = train_loader.dataset.denormalize(
203
+ state_hat[..., :2], state_hat[..., 2:]
204
+ )
205
+ velocity, pressure = train_loader.dataset.denormalize(batch["velocity"], batch["pressure"])
206
+ costs = get_loss(velocity, pressure, output, state_hat, target, batch["mask"], cfg_train.loss_alpha)
207
+
208
+ optimizer.zero_grad()
209
+ costs["loss"].backward()
210
+ optimizer.step()
211
+
212
+ train_loss += costs["loss"].item()
213
+ count += 1
214
+ pbar.set_postfix(loss=f"{costs['loss'].item():.6f}")
215
+
216
+ train_loss /= max(count, 1)
217
+ valid_loss = validate(model, val_loader, device, cfg_train.loss_alpha, manager)
218
+ if manager.rank == 0:
219
+ logger.info(
220
+ "Epoch %s/%s | %.2fs | train %.6f | valid %.6f",
221
+ epoch + 1,
222
+ cfg_train.max_epoch,
223
+ time.time() - start,
224
+ train_loss,
225
+ valid_loss,
226
+ )
227
+ if valid_loss < best_valid_loss:
228
+ best_valid_loss = valid_loss
229
+ best_epoch = epoch
230
+ save_best_model(model, optimizer, cfg_train.checkpoint_dir)
231
+ logger.info("Saved checkpoint to %s/best_model.pth", cfg_train.checkpoint_dir)
232
+ if epoch - best_epoch > int(cfg_train.patience):
233
+ break
234
+
235
+ if manager.rank == 0:
236
+ final_model = GraphViT(state_size=cfg_model.state_size, w_size=cfg_model.w_size).to(device)
237
+ load_best_model(final_model, cfg_train.checkpoint_dir, device)
238
+ test_loader, _ = datapipe.test_dataloader()
239
+ test_loss = validate(final_model, test_loader, device, cfg_train.loss_alpha, manager)
240
+ logger.info("Final test loss: %.6f", test_loss)
241
+
242
+ manager.cleanup()
243
+
244
+
245
+ if __name__ == "__main__":
246
+ main()