yushuang88 commited on
Commit
f220c93
·
verified ·
1 Parent(s): 4ef78b5

Upload folder using huggingface_hub

Browse files
.gitattributes CHANGED
@@ -1,35 +1,49 @@
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
+
 
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
+
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
48
+
49
+ navier_stokes/cylinder_nektar_wake.mat 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
+ - physics-informed neural networks
9
+ - long-horizon physical prediction
10
+ frameworks: PyTorch
11
+ ---
12
+ <p align="center">
13
+ <strong>
14
+ <span style="font-size: 30px;">PINNsformer</span>
15
+ </strong>
16
+ </p>
17
+
18
+ # Model Overview
19
+
20
+ PINNsFormer is a Transformer-based physics-informed neural network framework developed by researchers at the Georgia Institute of Technology and Carnegie Mellon University. It enables rapid prediction of solutions to time-dependent partial differential equations and their associated physical fields.
21
+
22
+ Paper: [PINNsFormer: A Transformer-Based Framework For Physics-Informed Neural Networks](https://arxiv.org/abs/2307.11833).
23
+
24
+ # Model Description
25
+
26
+ PINNsFormer uses a Transformer encoder–decoder with multi-head attention to numerically solve time-dependent partial differential equations, including convection, reaction, wave, and Navier–Stokes equations.
27
+
28
+
29
+
30
+ # Use Cases
31
+
32
+ | Use Case | Description |
33
+ | :--- | :--- |
34
+ | Time-dependent PDE solving | Train a continuous-field surrogate constrained by physical residuals, boundary conditions, and initial conditions |
35
+ | Physics-informed neural network validation | Rapidly validate the PINNsFormer network, loss functions, weight serialization, and inference pipeline |
36
+ | One-dimensional reaction equation example | Generate target fields from an analytical solution for pipeline validation and error analysis |
37
+ | ModelScope/OneCode execution | Download the standalone model package, install its dependencies, and run the provided scripts |
38
+
39
+
40
+ # Usage
41
+
42
+ ## 1. OneCode
43
+
44
+ Use the online OneCode environment for an intelligent, one-click AI for Science (AI4S) programming experience:
45
+
46
+ [Launch OneCode for one-click AI4S programming](https://web-2069360198568017922-iaaj.ksai.scnet.cn:58043/home)
47
+
48
+ ## 2. Manual Setup
49
+
50
+ **Hardware Requirements**
51
+
52
+ * A CPU can be used for small-scale pipeline validation.
53
+ * A GPU or DCU is recommended for training on larger grids or for more epochs.
54
+ * DCU users must install DTK in advance. DTK 25.04.2 or later, or the OneScience-recommended version for the target cluster, is recommended.
55
+
56
+ ### Download the Model Package
57
+
58
+ ```bash
59
+ hf download --model OneScience-Sugon/PINNsformer --local-dir ./PINNsformer
60
+ cd PINNsformer
61
+ ```
62
+
63
+ ### Set Up the Runtime Environment
64
+
65
+
66
+ **DCU Environment**
67
+
68
+ ```bash
69
+ # Activate DTK and Conda first
70
+ conda create -n onescience311 python=3.11 -y
71
+ conda activate onescience311
72
+ # Installation with uv is also supported
73
+ pip install onescience[cfd-dcu] -i http://mirrors.onescience.ai:3141/pypi/simple/ --trusted-host mirrors.onescience.ai
74
+ ```
75
+
76
+ **GPU Environment**
77
+ ```bash
78
+ # Activate Conda first
79
+ conda create -n onescience311 python=3.11 -y libstdcxx-ng=12 libgcc-ng=12 gcc_linux-64=12 gxx_linux-64=12
80
+ conda activate onescience311
81
+ # Installation with uv is also supported
82
+ pip install onescience[cfd-gpu] -i http://mirrors.onescience.ai:3141/pypi/simple/ --trusted-host mirrors.onescience.ai
83
+ ```
84
+
85
+ ### Training Data
86
+
87
+ To use real data, download it from the link below and set `data.data_dir` in `conf/config.yaml` to the correct path.
88
+
89
+ | Source | Link | Extraction Code | Destination |
90
+ |---|---|---|---|
91
+ | Baidu Netdisk | https://pan.baidu.com/s/1pM4ICc6FJX5pLF7WEoozxQ?pwd=5gha | `5gha` | `convection/convection.mat` and `navier_stokes/cylinder_nektar_wake.mat` |
92
+
93
+ ### Training
94
+
95
+ ```bash
96
+ python scripts/train.py
97
+ ```
98
+ The default configuration uses a smaller grid and fewer L-BFGS iterations for rapid end-to-end validation. To restore the scale of the original example, edit `conf/config.yaml`:
99
+
100
+ ```yaml
101
+ data:
102
+ x_num: 101
103
+ t_num: 101
104
+
105
+ training:
106
+ epochs: 500
107
+ ```
108
+
109
+ ### Model Weights
110
+ This repository will provide PINNsFormer model weights in the `weights/` directory. The weights will be uploaded soon.
111
+
112
+ ### Inference
113
+
114
+ ```bash
115
+ python scripts/inference.py
116
+ ```
117
+
118
+ Inference loads `weight/1dreaction_pinnsformer.pt` and saves:
119
+
120
+ ```text
121
+ result/prediction.npz
122
+ ```
123
+
124
+ ### Evaluation and Visualization
125
+
126
+ ```bash
127
+ python scripts/result.py
128
+ ```
129
+
130
+ # Official OneScience Resources
131
+
132
+ | Platform | OneScience Repository | Skills Repository |
133
+ | --- | --- | --- |
134
+ | Gitee | https://gitee.com/onescience-ai/onescience | https://gitee.com/onescience-ai/oneskills |
135
+ | GitHub | https://github.com/onescience-ai/OneScience | https://github.com/onescience-ai/oneskills |
136
+
137
+ # Citations and License
138
+
139
+ * Original PINNsformer paper: [PINNsFormer: A Transformer-Based Framework For Physics-Informed Neural Networks](https://arxiv.org/abs/2307.11833).
140
+ * This repository retains the relevant source and attribution notices. Follow all applicable license requirements when using, modifying, or distributing its contents.
conf/config.yaml ADDED
@@ -0,0 +1,46 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ project:
2
+ name: PINNsformer
3
+ task: 1d_reaction
4
+
5
+ runtime:
6
+ seed: 0
7
+ device: auto
8
+
9
+ model:
10
+ name: PINNsformer1D
11
+ d_out: 1
12
+ d_hidden: 128
13
+ d_model: 32
14
+ num_layers: 1
15
+ heads: 2
16
+
17
+ data:
18
+ x_range: [0.0, 6.283185307179586]
19
+ t_range: [0.0, 1.0]
20
+ x_num: 31
21
+ t_num: 31
22
+ sequence:
23
+ num_step: 5
24
+ step: 0.0001
25
+
26
+ equation:
27
+ reaction_rate: 5.0
28
+ initial:
29
+ center: 3.141592653589793
30
+ sigma: 0.7853981633974483
31
+
32
+ training:
33
+ epochs: 2
34
+ optimizer:
35
+ name: LBFGS
36
+ lr: 1.0
37
+ max_iter: 5
38
+ line_search_fn: strong_wolfe
39
+ checkpoint: weight/1dreaction_pinnsformer.pt
40
+
41
+ paths:
42
+ fake_data: data/reaction_fake.npz
43
+ prediction: result/prediction.npz
44
+ metrics: result/metrics.json
45
+ loss: result/train_loss.npy
46
+ figure: result/1dreaction_pinnsformer.png
configuration.json ADDED
@@ -0,0 +1 @@
 
 
1
+ {"framework":"Pytorch","task":"other","model":"PINNsformer","domain":"cfd"}
model/__init__.py ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ from .pinnsformer import PINNsformer1D, PINNsformer2D, WaveAct
2
+
3
+ __all__ = ["PINNsformer1D", "PINNsformer2D", "WaveAct"]
model/pinnsformer.py ADDED
@@ -0,0 +1,146 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # implementation of PINNsformer
2
+ # paper: PINNsFormer: A Transformer-Based Framework For Physics-Informed Neural Networks
3
+ # link: https://arxiv.org/abs/2307.11833
4
+
5
+ import torch
6
+ import torch.nn as nn
7
+
8
+ from onescience.utils.pinnsformer_util import get_clones
9
+
10
+ class WaveAct(nn.Module):
11
+ def __init__(self):
12
+ super(WaveAct, self).__init__()
13
+ self.w1 = nn.Parameter(torch.ones(1), requires_grad=True)
14
+ self.w2 = nn.Parameter(torch.ones(1), requires_grad=True)
15
+
16
+ def forward(self, x):
17
+ return self.w1 * torch.sin(x)+ self.w2 * torch.cos(x)
18
+
19
+ class FeedForward(nn.Module):
20
+ def __init__(self, d_model, d_ff=256):
21
+ super(FeedForward, self).__init__()
22
+ self.linear = nn.Sequential(*[
23
+ nn.Linear(d_model, d_ff),
24
+ WaveAct(),
25
+ nn.Linear(d_ff, d_ff),
26
+ WaveAct(),
27
+ nn.Linear(d_ff, d_model)
28
+ ])
29
+
30
+ def forward(self, x):
31
+ return self.linear(x)
32
+
33
+
34
+ class EncoderLayer(nn.Module):
35
+ def __init__(self, d_model, heads):
36
+ super(EncoderLayer, self).__init__()
37
+
38
+ self.attn = nn.MultiheadAttention(embed_dim=d_model, num_heads=heads, batch_first=True)
39
+ self.ff = FeedForward(d_model)
40
+ self.act1 = WaveAct()
41
+ self.act2 = WaveAct()
42
+
43
+ def forward(self, x):
44
+ x2 = self.act1(x)
45
+ # pdb.set_trace()
46
+ x = x + self.attn(x2,x2,x2)[0]
47
+ x2 = self.act2(x)
48
+ x = x + self.ff(x2)
49
+ return x
50
+
51
+
52
+ class DecoderLayer(nn.Module):
53
+ def __init__(self, d_model, heads):
54
+ super(DecoderLayer, self).__init__()
55
+
56
+ self.attn = nn.MultiheadAttention(embed_dim=d_model, num_heads=heads, batch_first=True)
57
+ self.ff = FeedForward(d_model)
58
+ self.act1 = WaveAct()
59
+ self.act2 = WaveAct()
60
+
61
+ def forward(self, x, e_outputs):
62
+ x2 = self.act1(x)
63
+ x = x + self.attn(x2, e_outputs, e_outputs)[0]
64
+ x2 = self.act2(x)
65
+ x = x + self.ff(x2)
66
+ return x
67
+
68
+
69
+ class Encoder(nn.Module):
70
+ def __init__(self, d_model, N, heads):
71
+ super(Encoder, self).__init__()
72
+ self.N = N
73
+ self.layers = get_clones(EncoderLayer(d_model, heads), N)
74
+ self.act = WaveAct()
75
+
76
+ def forward(self, x):
77
+ for i in range(self.N):
78
+ x = self.layers[i](x)
79
+ return self.act(x)
80
+
81
+
82
+ class Decoder(nn.Module):
83
+ def __init__(self, d_model, N, heads):
84
+ super(Decoder, self).__init__()
85
+ self.N = N
86
+ self.layers = get_clones(DecoderLayer(d_model, heads), N)
87
+ self.act = WaveAct()
88
+
89
+ def forward(self, x, e_outputs):
90
+ for i in range(self.N):
91
+ x = self.layers[i](x, e_outputs)
92
+ return self.act(x)
93
+
94
+
95
+
96
+ class PINNsformer1D(nn.Module):
97
+ def __init__(self, d_out, d_model, d_hidden, N, heads):
98
+ super(PINNsformer1D, self).__init__()
99
+
100
+ self.linear_emb = nn.Linear(2, d_model)
101
+
102
+ self.encoder = Encoder(d_model, N, heads)
103
+ self.decoder = Decoder(d_model, N, heads)
104
+ self.linear_out = nn.Sequential(*[
105
+ nn.Linear(d_model, d_hidden),
106
+ WaveAct(),
107
+ nn.Linear(d_hidden, d_hidden),
108
+ WaveAct(),
109
+ nn.Linear(d_hidden, d_out)
110
+ ])
111
+
112
+ def forward(self, x, t):
113
+ src = torch.cat((x,t), dim=-1)
114
+ src = self.linear_emb(src)
115
+
116
+ e_outputs = self.encoder(src)
117
+ d_output = self.decoder(src, e_outputs)
118
+ output = self.linear_out(d_output)
119
+ # pdb.set_trace()
120
+ # raise Exception('stop')
121
+ return output
122
+
123
+
124
+ class PINNsformer2D(nn.Module):
125
+ def __init__(self, d_out, d_model, d_hidden, N, heads):
126
+ super(PINNsformer2D, self).__init__()
127
+
128
+ self.linear_emb = nn.Linear(3, d_model)
129
+
130
+ self.encoder = Encoder(d_model, N, heads)
131
+ self.decoder = Decoder(d_model, N, heads)
132
+ self.linear_out = nn.Sequential(*[
133
+ nn.Linear(d_model, d_hidden),
134
+ WaveAct(),
135
+ nn.Linear(d_hidden, d_hidden),
136
+ WaveAct(),
137
+ nn.Linear(d_hidden, d_out)
138
+ ])
139
+
140
+ def forward(self, x, y, t):
141
+ src = torch.cat((x,y,t), dim=-1)
142
+ src = self.linear_emb(src)
143
+ e_outputs = self.encoder(src)
144
+ d_output = self.decoder(src, e_outputs)
145
+ output = self.linear_out(d_output)
146
+ return output
result/.gitkeep ADDED
@@ -0,0 +1 @@
 
 
1
+
scripts/common.py ADDED
@@ -0,0 +1,100 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+
3
+ import json
4
+ import random
5
+ import sys
6
+ from pathlib import Path
7
+ from typing import Any
8
+
9
+ import numpy as np
10
+ import torch
11
+ import yaml
12
+
13
+ PROJECT_ROOT = Path(__file__).resolve().parents[1]
14
+ if str(PROJECT_ROOT) not in sys.path:
15
+ sys.path.insert(0, str(PROJECT_ROOT))
16
+
17
+
18
+ def load_config(config_path: str | Path | None = None) -> dict[str, Any]:
19
+ path = Path(config_path) if config_path else PROJECT_ROOT / "conf" / "config.yaml"
20
+ if not path.is_absolute():
21
+ path = PROJECT_ROOT / path
22
+ with path.open("r", encoding="utf-8") as f:
23
+ return yaml.safe_load(f)
24
+
25
+
26
+ def project_path(path: str | Path) -> Path:
27
+ path = Path(path)
28
+ return path if path.is_absolute() else PROJECT_ROOT / path
29
+
30
+
31
+ def seed_everything(seed: int) -> None:
32
+ random.seed(seed)
33
+ np.random.seed(seed)
34
+ torch.manual_seed(seed)
35
+ if torch.cuda.is_available():
36
+ torch.cuda.manual_seed_all(seed)
37
+
38
+
39
+ def select_device(value: str = "auto") -> torch.device:
40
+ if value == "auto":
41
+ return torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
42
+ return torch.device(value)
43
+
44
+
45
+ def ensure_parent(path: Path) -> None:
46
+ path.parent.mkdir(parents=True, exist_ok=True)
47
+
48
+
49
+ def ensure_runtime_dirs(cfg: dict[str, Any]) -> None:
50
+ for key in ("fake_data", "prediction", "metrics", "loss", "figure"):
51
+ ensure_parent(project_path(cfg["paths"][key]))
52
+ ensure_parent(project_path(cfg["training"]["checkpoint"]))
53
+
54
+
55
+ def build_model(cfg: dict[str, Any]) -> torch.nn.Module:
56
+ from model import PINNsformer1D
57
+
58
+ model_cfg = cfg["model"]
59
+ return PINNsformer1D(
60
+ d_out=int(model_cfg["d_out"]),
61
+ d_hidden=int(model_cfg["d_hidden"]),
62
+ d_model=int(model_cfg["d_model"]),
63
+ N=int(model_cfg["num_layers"]),
64
+ heads=int(model_cfg["heads"]),
65
+ )
66
+
67
+
68
+ def initial_condition(x: np.ndarray | torch.Tensor, cfg: dict[str, Any]):
69
+ initial = cfg["equation"]["initial"]
70
+ center = initial["center"]
71
+ sigma = initial["sigma"]
72
+ if isinstance(x, torch.Tensor):
73
+ return torch.exp(-((x - center) ** 2) / (2 * sigma**2))
74
+ return np.exp(-((x - center) ** 2) / (2 * sigma**2))
75
+
76
+
77
+ def exact_reaction_solution(
78
+ x: np.ndarray | torch.Tensor,
79
+ t: np.ndarray | torch.Tensor,
80
+ cfg: dict[str, Any],
81
+ ):
82
+ h = initial_condition(x, cfg)
83
+ rate = cfg["equation"]["reaction_rate"]
84
+ if isinstance(h, torch.Tensor):
85
+ exp_term = torch.exp(rate * t)
86
+ else:
87
+ exp_term = np.exp(rate * t)
88
+ return h * exp_term / (h * exp_term + 1 - h)
89
+
90
+
91
+ def relative_errors(pred: np.ndarray, target: np.ndarray) -> dict[str, float]:
92
+ return {
93
+ "relative_l1": float(np.sum(np.abs(target - pred)) / np.sum(np.abs(target))),
94
+ "relative_l2": float(np.sqrt(np.sum((target - pred) ** 2) / np.sum(target**2))),
95
+ }
96
+
97
+
98
+ def write_json(path: Path, payload: dict[str, Any]) -> None:
99
+ ensure_parent(path)
100
+ path.write_text(json.dumps(payload, indent=2), encoding="utf-8")
scripts/fake_data.py ADDED
@@ -0,0 +1,31 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+
3
+ import argparse
4
+
5
+ import numpy as np
6
+
7
+ from common import exact_reaction_solution, load_config, project_path
8
+
9
+
10
+ def main() -> None:
11
+ parser = argparse.ArgumentParser(description="Generate fake PINNsformer reaction data.")
12
+ parser.add_argument("--config", default=None, help="Path to config.yaml.")
13
+ args = parser.parse_args()
14
+
15
+ cfg = load_config(args.config)
16
+ data_cfg = cfg["data"]
17
+ x = np.linspace(*data_cfg["x_range"], int(data_cfg["x_num"]), dtype=np.float32)
18
+ t = np.linspace(*data_cfg["t_range"], int(data_cfg["t_num"]), dtype=np.float32)
19
+ x_mesh, t_mesh = np.meshgrid(x, t)
20
+ u = exact_reaction_solution(x_mesh, t_mesh, cfg).astype(np.float32)
21
+
22
+ output_path = project_path(cfg["paths"]["fake_data"])
23
+ output_path.parent.mkdir(parents=True, exist_ok=True)
24
+ np.savez(output_path, x=x, t=t, u=u)
25
+
26
+ print(f"fake data saved to {output_path}")
27
+ print(f"x={x.shape}, t={t.shape}, u={u.shape}")
28
+
29
+
30
+ if __name__ == "__main__":
31
+ main()
scripts/inference.py ADDED
@@ -0,0 +1,85 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+
3
+ import argparse
4
+
5
+ import numpy as np
6
+ import torch
7
+
8
+ from onescience.utils.pinnsformer_util import get_data, make_time_sequence
9
+
10
+ from common import (
11
+ build_model,
12
+ exact_reaction_solution,
13
+ load_config,
14
+ project_path,
15
+ relative_errors,
16
+ select_device,
17
+ )
18
+
19
+
20
+ def main() -> None:
21
+ parser = argparse.ArgumentParser(description="Run PINNsformer inference for the 1D reaction equation.")
22
+ parser.add_argument("--config", default=None, help="Path to config.yaml.")
23
+ parser.add_argument("--checkpoint", default=None, help="Path to a trained checkpoint.")
24
+ parser.add_argument("--device", default=None, help="Override runtime.device.")
25
+ args = parser.parse_args()
26
+
27
+ cfg = load_config(args.config)
28
+ device = select_device(args.device or cfg["runtime"]["device"])
29
+ checkpoint = project_path(args.checkpoint or cfg["training"]["checkpoint"])
30
+ if not checkpoint.exists():
31
+ raise FileNotFoundError(f"Checkpoint not found: {checkpoint}. Run scripts/train.py first.")
32
+
33
+ model = build_model(cfg).to(device)
34
+ try:
35
+ state = torch.load(checkpoint, map_location=device, weights_only=True)
36
+ except TypeError:
37
+ state = torch.load(checkpoint, map_location=device)
38
+ model.load_state_dict(state.get("model_state_dict", state))
39
+ model.eval()
40
+
41
+ data_cfg = cfg["data"]
42
+ res, _, _, _, _ = get_data(
43
+ data_cfg["x_range"],
44
+ data_cfg["t_range"],
45
+ int(data_cfg["x_num"]),
46
+ int(data_cfg["t_num"]),
47
+ )
48
+ seq = make_time_sequence(
49
+ res,
50
+ num_step=int(data_cfg["sequence"]["num_step"]),
51
+ step=float(data_cfg["sequence"]["step"]),
52
+ )
53
+ res_tensor = torch.tensor(seq, dtype=torch.float32, requires_grad=False, device=device)
54
+ x_test, t_test = res_tensor[:, :, 0:1], res_tensor[:, :, 1:2]
55
+
56
+ with torch.no_grad():
57
+ pred = model(x_test, t_test)[:, 0:1].detach().cpu().numpy().reshape(
58
+ int(data_cfg["t_num"]),
59
+ int(data_cfg["x_num"]),
60
+ )
61
+
62
+ x = res[:, 0].reshape(int(data_cfg["t_num"]), int(data_cfg["x_num"]))
63
+ t = res[:, 1].reshape(int(data_cfg["t_num"]), int(data_cfg["x_num"]))
64
+ target = exact_reaction_solution(x, t, cfg).astype(np.float32)
65
+ metrics = relative_errors(pred, target)
66
+
67
+ output_path = project_path(cfg["paths"]["prediction"])
68
+ output_path.parent.mkdir(parents=True, exist_ok=True)
69
+ np.savez(
70
+ output_path,
71
+ x=x.astype(np.float32),
72
+ t=t.astype(np.float32),
73
+ prediction=pred.astype(np.float32),
74
+ target=target,
75
+ absolute_error=np.abs(pred - target).astype(np.float32),
76
+ relative_l1=metrics["relative_l1"],
77
+ relative_l2=metrics["relative_l2"],
78
+ )
79
+ print(f"prediction saved to {output_path}")
80
+ print(f"relative L1 error: {metrics['relative_l1']:.6f}")
81
+ print(f"relative L2 error: {metrics['relative_l2']:.6f}")
82
+
83
+
84
+ if __name__ == "__main__":
85
+ main()
scripts/result.py ADDED
@@ -0,0 +1,71 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+
3
+ import argparse
4
+
5
+ import numpy as np
6
+
7
+ from common import load_config, project_path, relative_errors, write_json
8
+
9
+
10
+ def plot_result(prediction: np.ndarray, target: np.ndarray, cfg: dict, figure_path) -> bool:
11
+ try:
12
+ import matplotlib
13
+
14
+ matplotlib.use("Agg")
15
+ import matplotlib.pyplot as plt
16
+ except Exception as exc:
17
+ print(f"matplotlib unavailable, skip figure: {exc}")
18
+ return False
19
+
20
+ figure_path.parent.mkdir(parents=True, exist_ok=True)
21
+ fig, axes = plt.subplots(1, 3, figsize=(12, 4))
22
+ x_min, x_max = cfg["data"]["x_range"]
23
+ t_min, t_max = cfg["data"]["t_range"]
24
+ items = [
25
+ (prediction, "Predicted u(x,t)"),
26
+ (target, "Exact u(x,t)"),
27
+ (np.abs(prediction - target), "Absolute Error"),
28
+ ]
29
+ for ax, (data, title) in zip(axes, items):
30
+ image = ax.imshow(data, extent=[x_min, x_max, t_max, t_min], aspect="auto")
31
+ ax.set_xlabel("x")
32
+ ax.set_ylabel("t")
33
+ ax.set_title(title)
34
+ fig.colorbar(image, ax=ax)
35
+
36
+ plt.tight_layout()
37
+ plt.savefig(figure_path)
38
+ plt.close(fig)
39
+ return True
40
+
41
+
42
+ def main() -> None:
43
+ parser = argparse.ArgumentParser(description="Evaluate and plot PINNsformer inference output.")
44
+ parser.add_argument("--config", default=None, help="Path to config.yaml.")
45
+ parser.add_argument("--prediction", default=None, help="Path to prediction .npz.")
46
+ args = parser.parse_args()
47
+
48
+ cfg = load_config(args.config)
49
+ prediction_path = project_path(args.prediction or cfg["paths"]["prediction"])
50
+ if not prediction_path.exists():
51
+ raise FileNotFoundError(f"Prediction file not found: {prediction_path}. Run scripts/inference.py first.")
52
+
53
+ data = np.load(prediction_path)
54
+ prediction = data["prediction"]
55
+ target = data["target"]
56
+ metrics = relative_errors(prediction, target)
57
+
58
+ metrics_path = project_path(cfg["paths"]["metrics"])
59
+ write_json(metrics_path, metrics)
60
+ figure_path = project_path(cfg["paths"]["figure"])
61
+ plotted = plot_result(prediction, target, cfg, figure_path)
62
+
63
+ print(f"metrics saved to {metrics_path}")
64
+ if plotted:
65
+ print(f"figure saved to {figure_path}")
66
+ print(f"relative L1 error: {metrics['relative_l1']:.6f}")
67
+ print(f"relative L2 error: {metrics['relative_l2']:.6f}")
68
+
69
+
70
+ if __name__ == "__main__":
71
+ main()
scripts/train.py ADDED
@@ -0,0 +1,172 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+
3
+ import argparse
4
+ from pathlib import Path
5
+
6
+ import numpy as np
7
+ import torch
8
+ import torch.nn as nn
9
+ from torch.optim import Adam, LBFGS
10
+
11
+ from onescience.utils.pinnsformer_util import get_data, get_n_params, make_time_sequence
12
+
13
+ from common import (
14
+ build_model,
15
+ ensure_runtime_dirs,
16
+ initial_condition,
17
+ load_config,
18
+ project_path,
19
+ seed_everything,
20
+ select_device,
21
+ )
22
+
23
+
24
+ def init_weights(module: nn.Module) -> None:
25
+ if isinstance(module, nn.Linear):
26
+ torch.nn.init.xavier_uniform_(module.weight)
27
+ module.bias.data.fill_(0.01)
28
+
29
+
30
+ def tensorize(array: np.ndarray, device: torch.device) -> torch.Tensor:
31
+ return torch.tensor(array, dtype=torch.float32, requires_grad=True, device=device)
32
+
33
+
34
+ def prepare_tensors(cfg: dict, device: torch.device, args: argparse.Namespace):
35
+ data_cfg = cfg["data"]
36
+ x_num = int(args.x_num or data_cfg["x_num"])
37
+ t_num = int(args.t_num or data_cfg["t_num"])
38
+ num_step = int(args.num_step or data_cfg["sequence"]["num_step"])
39
+ step = float(data_cfg["sequence"]["step"])
40
+
41
+ res, b_left, b_right, b_upper, b_lower = get_data(
42
+ data_cfg["x_range"],
43
+ data_cfg["t_range"],
44
+ x_num,
45
+ t_num,
46
+ )
47
+
48
+ tensors = []
49
+ for values in (res, b_left, b_right, b_upper, b_lower):
50
+ tensors.append(tensorize(make_time_sequence(values, num_step=num_step, step=step), device))
51
+
52
+ return tuple(tensors)
53
+
54
+
55
+ def loss_components(model: nn.Module, tensors: tuple[torch.Tensor, ...], cfg: dict):
56
+ res, b_left, b_right, b_upper, b_lower = tensors
57
+ x_res, t_res = res[:, :, 0:1], res[:, :, 1:2]
58
+ x_left, t_left = b_left[:, :, 0:1], b_left[:, :, 1:2]
59
+ x_upper, t_upper = b_upper[:, :, 0:1], b_upper[:, :, 1:2]
60
+ x_lower, t_lower = b_lower[:, :, 0:1], b_lower[:, :, 1:2]
61
+
62
+ pred_res = model(x_res, t_res)
63
+ pred_left = model(x_left, t_left)
64
+ pred_upper = model(x_upper, t_upper)
65
+ pred_lower = model(x_lower, t_lower)
66
+
67
+ u_t = torch.autograd.grad(
68
+ pred_res,
69
+ t_res,
70
+ grad_outputs=torch.ones_like(pred_res),
71
+ retain_graph=True,
72
+ create_graph=True,
73
+ )[0]
74
+
75
+ rate = float(cfg["equation"]["reaction_rate"])
76
+ target_ic = initial_condition(x_left[:, 0, :], cfg)
77
+ loss_res = torch.mean((u_t - rate * pred_res * (1 - pred_res)) ** 2)
78
+ loss_bc = torch.mean((pred_upper - pred_lower) ** 2)
79
+ loss_ic = torch.mean((pred_left[:, 0, :] - target_ic) ** 2)
80
+ loss = loss_res + loss_bc + loss_ic
81
+ return loss, (loss_res, loss_bc, loss_ic)
82
+
83
+
84
+ def build_optimizer(model: nn.Module, cfg: dict):
85
+ opt_cfg = cfg["training"]["optimizer"]
86
+ name = opt_cfg["name"].lower()
87
+ if name == "adam":
88
+ return Adam(model.parameters(), lr=float(opt_cfg.get("lr", 1e-3)))
89
+ if name == "lbfgs":
90
+ return LBFGS(
91
+ model.parameters(),
92
+ lr=float(opt_cfg.get("lr", 1.0)),
93
+ max_iter=int(opt_cfg.get("max_iter", 20)),
94
+ line_search_fn=opt_cfg.get("line_search_fn", "strong_wolfe"),
95
+ )
96
+ raise ValueError(f"Unsupported optimizer: {opt_cfg['name']}")
97
+
98
+
99
+ def save_checkpoint(path: Path, model: nn.Module, cfg: dict, loss_history: list[list[float]]) -> None:
100
+ path.parent.mkdir(parents=True, exist_ok=True)
101
+ torch.save(
102
+ {
103
+ "model_state_dict": model.state_dict(),
104
+ "config": cfg,
105
+ "loss_history": loss_history,
106
+ },
107
+ path,
108
+ )
109
+
110
+
111
+ def main() -> None:
112
+ parser = argparse.ArgumentParser(description="Train PINNsformer on the 1D reaction equation.")
113
+ parser.add_argument("--config", default=None, help="Path to config.yaml.")
114
+ parser.add_argument("--epochs", type=int, default=None, help="Override training epochs.")
115
+ parser.add_argument("--x-num", type=int, default=None, help="Override x grid count.")
116
+ parser.add_argument("--t-num", type=int, default=None, help="Override t grid count.")
117
+ parser.add_argument("--num-step", type=int, default=None, help="Override pseudo-sequence length.")
118
+ parser.add_argument("--device", default=None, help="Override runtime.device.")
119
+ args = parser.parse_args()
120
+
121
+ cfg = load_config(args.config)
122
+ ensure_runtime_dirs(cfg)
123
+ seed_everything(int(cfg["runtime"]["seed"]))
124
+ device = select_device(args.device or cfg["runtime"]["device"])
125
+
126
+ tensors = prepare_tensors(cfg, device, args)
127
+ model = build_model(cfg).to(device)
128
+ model.apply(init_weights)
129
+ optimizer = build_optimizer(model, cfg)
130
+ epochs = int(args.epochs or cfg["training"]["epochs"])
131
+
132
+ print(model)
133
+ print(f"parameters: {get_n_params(model)}")
134
+ print(f"device: {device}")
135
+
136
+ loss_history: list[list[float]] = []
137
+ for epoch in range(epochs):
138
+ latest: dict[str, float] = {}
139
+
140
+ def closure():
141
+ loss, parts = loss_components(model, tensors, cfg)
142
+ optimizer.zero_grad()
143
+ loss.backward()
144
+ latest["loss"] = float(loss.detach().cpu())
145
+ latest["loss_res"] = float(parts[0].detach().cpu())
146
+ latest["loss_bc"] = float(parts[1].detach().cpu())
147
+ latest["loss_ic"] = float(parts[2].detach().cpu())
148
+ return loss
149
+
150
+ if isinstance(optimizer, LBFGS):
151
+ optimizer.step(closure)
152
+ else:
153
+ closure()
154
+ optimizer.step()
155
+
156
+ loss_history.append([latest["loss_res"], latest["loss_bc"], latest["loss_ic"], latest["loss"]])
157
+ print(
158
+ f"epoch {epoch + 1}/{epochs} "
159
+ f"loss={latest['loss']:.6f} "
160
+ f"res={latest['loss_res']:.6f} "
161
+ f"bc={latest['loss_bc']:.6f} "
162
+ f"ic={latest['loss_ic']:.6f}"
163
+ )
164
+
165
+ checkpoint = project_path(cfg["training"]["checkpoint"])
166
+ save_checkpoint(checkpoint, model, cfg, loss_history)
167
+ np.save(project_path(cfg["paths"]["loss"]), np.asarray(loss_history, dtype=np.float32))
168
+ print(f"checkpoint saved to {checkpoint}")
169
+
170
+
171
+ if __name__ == "__main__":
172
+ main()
weight/.gitkeep ADDED
@@ -0,0 +1 @@
 
 
1
+