pinnforge 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pinnforge-0.1.0/LICENSE +21 -0
- pinnforge-0.1.0/PKG-INFO +218 -0
- pinnforge-0.1.0/README.md +194 -0
- pinnforge-0.1.0/pyproject.toml +38 -0
- pinnforge-0.1.0/setup.cfg +4 -0
- pinnforge-0.1.0/src/pinnforge/__init__.py +57 -0
- pinnforge-0.1.0/src/pinnforge/auto/__init__.py +4 -0
- pinnforge-0.1.0/src/pinnforge/auto/config.py +93 -0
- pinnforge-0.1.0/src/pinnforge/auto/solver.py +335 -0
- pinnforge-0.1.0/src/pinnforge/benchmarks/__init__.py +4 -0
- pinnforge-0.1.0/src/pinnforge/benchmarks/metrics.py +106 -0
- pinnforge-0.1.0/src/pinnforge/benchmarks/runner.py +237 -0
- pinnforge-0.1.0/src/pinnforge/core/__init__.py +4 -0
- pinnforge-0.1.0/src/pinnforge/core/pinn.py +423 -0
- pinnforge-0.1.0/src/pinnforge/core/solvers.py +119 -0
- pinnforge-0.1.0/src/pinnforge/examples/__init__.py +4 -0
- pinnforge-0.1.0/src/pinnforge/examples/burgers.py +142 -0
- pinnforge-0.1.0/src/pinnforge/examples/heat.py +152 -0
- pinnforge-0.1.0/src/pinnforge/physics/__init__.py +17 -0
- pinnforge-0.1.0/src/pinnforge/physics/pdes.py +115 -0
- pinnforge-0.1.0/src/pinnforge/symbolic/__init__.py +3 -0
- pinnforge-0.1.0/src/pinnforge/symbolic/pde.py +127 -0
- pinnforge-0.1.0/src/pinnforge/utils/__init__.py +17 -0
- pinnforge-0.1.0/src/pinnforge/utils/data.py +106 -0
- pinnforge-0.1.0/src/pinnforge/utils/logging.py +127 -0
- pinnforge-0.1.0/src/pinnforge.egg-info/PKG-INFO +218 -0
- pinnforge-0.1.0/src/pinnforge.egg-info/SOURCES.txt +30 -0
- pinnforge-0.1.0/src/pinnforge.egg-info/dependency_links.txt +1 -0
- pinnforge-0.1.0/src/pinnforge.egg-info/requires.txt +13 -0
- pinnforge-0.1.0/src/pinnforge.egg-info/top_level.txt +1 -0
- pinnforge-0.1.0/tests/test_pdes.py +74 -0
- pinnforge-0.1.0/tests/test_pinn.py +66 -0
pinnforge-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 VEDAUV RAJKUMAR LEELAVATHI
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
pinnforge-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: pinnforge
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Solve PDEs in 3 lines of code
|
|
5
|
+
Author-email: Vedauv Rajkumar Leelavathi <vedauvrajkumarleelavathi@gmail.com>
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Repository, https://github.com/ved-rl/pinnforge
|
|
8
|
+
Requires-Python: >=3.8
|
|
9
|
+
Description-Content-Type: text/markdown
|
|
10
|
+
License-File: LICENSE
|
|
11
|
+
Requires-Dist: torch>=1.9.0
|
|
12
|
+
Requires-Dist: numpy>=1.21.0
|
|
13
|
+
Requires-Dist: sympy>=1.9.0
|
|
14
|
+
Requires-Dist: matplotlib>=3.4.0
|
|
15
|
+
Requires-Dist: scipy>=1.7.0
|
|
16
|
+
Requires-Dist: pyyaml>=5.4.0
|
|
17
|
+
Requires-Dist: tqdm>=4.62.0
|
|
18
|
+
Provides-Extra: dev
|
|
19
|
+
Requires-Dist: pytest>=6.0.0; extra == "dev"
|
|
20
|
+
Requires-Dist: pytest-cov>=2.12.0; extra == "dev"
|
|
21
|
+
Requires-Dist: black>=21.0.0; extra == "dev"
|
|
22
|
+
Requires-Dist: flake8>=3.9.0; extra == "dev"
|
|
23
|
+
Dynamic: license-file
|
|
24
|
+
|
|
25
|
+
# PINNForge
|
|
26
|
+
|
|
27
|
+
**Solve partial differential equations in 3 lines of code.**
|
|
28
|
+
|
|
29
|
+

|
|
30
|
+
|
|
31
|
+
PINNForge is a lightweight wrapper around PyTorch that handles the boilerplate of Physics-Informed Neural Networks so you can focus on the physics.
|
|
32
|
+
|
|
33
|
+
## Install
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
pip install pinnforge
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## Quick Start
|
|
40
|
+
|
|
41
|
+
```python
|
|
42
|
+
from pinnforge import solve_pde
|
|
43
|
+
|
|
44
|
+
result = solve_pde("heat", epochs=5000)
|
|
45
|
+
print(f"Relative L2 error: {result['metrics']['relative_l2_error']:.2e}")
|
|
46
|
+
# Relative L2 error: 3.42e-02
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
That's it. The library automatically builds the network, generates training points, trains, validates, and returns metrics.
|
|
50
|
+
|
|
51
|
+
## Why PINNForge?
|
|
52
|
+
|
|
53
|
+
Setting up a PINN in the standard tooling requires 25+ lines of boilerplate before you see your first result. PINNForge gets you there in 3.
|
|
54
|
+
|
|
55
|
+
### Side-by-side: solving the 1D Heat equation
|
|
56
|
+
|
|
57
|
+
<table>
|
|
58
|
+
<tr>
|
|
59
|
+
<th width="50%">PINNForge (3 lines)</th>
|
|
60
|
+
<th width="50%">DeepXDE (~25 lines)</th>
|
|
61
|
+
</tr>
|
|
62
|
+
<tr>
|
|
63
|
+
<td>
|
|
64
|
+
|
|
65
|
+
```python
|
|
66
|
+
from pinnforge import solve_pde
|
|
67
|
+
|
|
68
|
+
result = solve_pde("heat", epochs=5000)
|
|
69
|
+
print(result['metrics']['relative_l2_error'])
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
That's the whole program. Everything else is handled by the library.
|
|
73
|
+
|
|
74
|
+
</td>
|
|
75
|
+
<td>
|
|
76
|
+
|
|
77
|
+
```python
|
|
78
|
+
import deepxde as dde
|
|
79
|
+
import numpy as np
|
|
80
|
+
|
|
81
|
+
def pde(x, y):
|
|
82
|
+
dy_t = dde.grad.jacobian(y, x, i=0, j=1)
|
|
83
|
+
dy_xx = dde.grad.hessian(y, x, i=0, j=0)
|
|
84
|
+
return dy_t - dy_xx
|
|
85
|
+
|
|
86
|
+
geom = dde.geometry.Interval(0, 1)
|
|
87
|
+
timedomain = dde.geometry.TimeDomain(0, 1)
|
|
88
|
+
geomtime = dde.geometry.GeometryXTime(geom, timedomain)
|
|
89
|
+
|
|
90
|
+
def boundary(x, on_boundary):
|
|
91
|
+
return on_boundary
|
|
92
|
+
|
|
93
|
+
def initial(x):
|
|
94
|
+
return np.sin(np.pi * x[:, 0:1])
|
|
95
|
+
|
|
96
|
+
bc = dde.icbc.DirichletBC(geomtime, lambda x: 0, boundary)
|
|
97
|
+
ic = dde.icbc.IC(geomtime, initial, lambda _, on_initial: on_initial)
|
|
98
|
+
|
|
99
|
+
data = dde.data.TimePDE(
|
|
100
|
+
geomtime, pde, [bc, ic],
|
|
101
|
+
num_domain=2000, num_boundary=100, num_initial=100,
|
|
102
|
+
)
|
|
103
|
+
|
|
104
|
+
net = dde.nn.FNN([2] + [64] * 3 + [1], "tanh", "Glorot normal")
|
|
105
|
+
model = dde.Model(data, net)
|
|
106
|
+
model.compile("adam", lr=1e-3)
|
|
107
|
+
model.train(epochs=15000)
|
|
108
|
+
model.compile("L-BFGS")
|
|
109
|
+
losshistory, train_state = model.train()
|
|
110
|
+
|
|
111
|
+
# Then write validation and plotting code yourself
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
The setup is 9x longer and you still need to write your own validation and plots after training.
|
|
115
|
+
|
|
116
|
+
</td>
|
|
117
|
+
</tr>
|
|
118
|
+
</table>
|
|
119
|
+
|
|
120
|
+
### Feature comparison
|
|
121
|
+
|
|
122
|
+
| Feature | PINNForge | DeepXDE | PINNs-Torch |
|
|
123
|
+
|---------|-----------|---------|-------------|
|
|
124
|
+
| One-line solve API | ✅ | ❌ | ❌ |
|
|
125
|
+
| Symbolic PDE definition (SymPy) | ✅ | ❌ | ❌ |
|
|
126
|
+
| Auto-generated validation metrics | ✅ | ❌ | ❌ |
|
|
127
|
+
| Auto-generated plots | ✅ | ❌ | ❌ |
|
|
128
|
+
| Sensible defaults per PDE | ✅ | ❌ | ❌ |
|
|
129
|
+
| Built-in numerical solvers | ✅ | ❌ | ❌ |
|
|
130
|
+
| Multi-backend (TF / JAX / PyTorch) | PyTorch only | ✅ | PyTorch |
|
|
131
|
+
| 3D+ problems | Limited | ✅ | Limited |
|
|
132
|
+
| Battle-tested since 2019 | ❌ (v0.1) | ✅ | ❌ |
|
|
133
|
+
|
|
134
|
+
**The trade-off is on purpose.** PINNForge trades depth for time-to-result. If you need complex PDEs or production-scale 3D problems, use DeepXDE. If you want to try an idea in 5 minutes with minimal code, use PINNForge.
|
|
135
|
+
|
|
136
|
+
## What's Included
|
|
137
|
+
|
|
138
|
+
- **`solve_pde()`** — one-line API for common PDEs (Heat, Burgers, Wave)
|
|
139
|
+
- **`AutoPINN`** — configurable solver with auto-selected hyperparameters
|
|
140
|
+
- **`PINN`** — the underlying neural network module
|
|
141
|
+
- **`SymbolicPDE`** — define custom PDEs using SymPy math notation
|
|
142
|
+
- **`NumericalSolver`** — finite-difference reference solutions
|
|
143
|
+
- **`ExperimentLogger`** — automatic logging of training metrics and checkpoints
|
|
144
|
+
|
|
145
|
+
## Custom PDEs with SymPy
|
|
146
|
+
|
|
147
|
+
For problems not in the built-in registry, define them symbolically:
|
|
148
|
+
|
|
149
|
+
```python
|
|
150
|
+
import sympy as sp
|
|
151
|
+
from pinnforge import AutoPINN
|
|
152
|
+
|
|
153
|
+
x, t, u = sp.symbols('x t u')
|
|
154
|
+
equation = sp.diff(u, t) + u * sp.diff(u, x) - 0.01 * sp.diff(u, x, 2)
|
|
155
|
+
|
|
156
|
+
solver = AutoPINN.from_symbolic(equation, [x, t], u)
|
|
157
|
+
result = solver.solve()
|
|
158
|
+
print(result['metrics'])
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
## Known Limitations (v0.1.0)
|
|
162
|
+
|
|
163
|
+
This is an early release. Here's what doesn't work yet:
|
|
164
|
+
|
|
165
|
+
- **Fourier feature embeddings are implemented but disabled by default.** They currently cause training collapse on test problems (error jumps from 1e-2 to 1.2). Help wanted — see [#3](https://github.com/ved-rl/pinnforge/issues/3).
|
|
166
|
+
- **Adaptive loss weighting is implemented but disabled by default.** Same reason.
|
|
167
|
+
- **Adaptive activation (SA-PINN) is disabled by default.** Interacts badly with Fourier features.
|
|
168
|
+
- **Burgers equation validation returns `{}`.** The analytical solution currently in the code is the Heat equation's solution, not Burgers'. Burgers has no simple closed-form solution for this initial condition. See [#4](https://github.com/ved-rl/pinnforge/issues/4).
|
|
169
|
+
- **Only 1D problems are supported.** Multi-dimensional PDEs are planned for v0.2.0.
|
|
170
|
+
- **No CLI yet.** Everything is Python-only.
|
|
171
|
+
|
|
172
|
+
## Contributing
|
|
173
|
+
|
|
174
|
+
This is a young project and there are several tasks that would help a lot. If you're looking for a place to contribute to SciML tooling, this project is a place where help is welcome.
|
|
175
|
+
|
|
176
|
+
### Good First Issues
|
|
177
|
+
|
|
178
|
+
| Task | Difficulty | Description |
|
|
179
|
+
|------|------------|-------------|
|
|
180
|
+
| **Fix Burgers analytical solution** | Easy | The `BurgersEquation.analytical_solution` method returns the Heat solution. Replace with `NotImplementedError` and fall back to the numerical solver. See [#4](https://github.com/ved-rl/pinnforge/issues/4). |
|
|
181
|
+
| **Add tests for Wave equation** | Easy | `WaveEquation` exists in `pdes.py` but has no test coverage. Add a basic smoke test to `tests/test_pdes.py`. |
|
|
182
|
+
| **Add a quickstart notebook** | Easy | Create `examples/quickstart.ipynb` that walks through the 3-line API and shows results inline. |
|
|
183
|
+
| **Improve error messages** | Easy | Several places raise generic errors. Add context-specific messages for common failure modes. |
|
|
184
|
+
|
|
185
|
+
### Larger Contributions Welcome
|
|
186
|
+
|
|
187
|
+
| Task | Difficulty | Description |
|
|
188
|
+
|------|------------|-------------|
|
|
189
|
+
| **Fix Fourier feature implementation** | Medium | Features collapse to a constant output. Current implementation uses only `output_dim // 2` random projections; standard RFF uses 128+. See [#3](https://github.com/ved-rl/pinnforge/issues/3). |
|
|
190
|
+
| **L-BFGS fine-tuning** | Medium | Adding L-BFGS as a second-stage optimizer (as DeepXDE does) would improve final accuracy by 10x. |
|
|
191
|
+
| **2D PDE support** | Hard | Currently limited to 1D spatial domains. Requires changes to `pdes.py`, `data.py`, and the `PINN` class. |
|
|
192
|
+
| **JAX backend** | Hard | Optional JAX backend for 5-10x faster training. See `jinns` for reference. |
|
|
193
|
+
| **Adaptive sampling (RAR)** | Medium | Residual-based adaptive refinement. Sample new collocation points where the PDE residual is highest. |
|
|
194
|
+
|
|
195
|
+
### How to Contribute
|
|
196
|
+
|
|
197
|
+
1. Fork the repo
|
|
198
|
+
2. Create a feature branch (`git checkout -b fix-fourier-features`)
|
|
199
|
+
3. Make your changes
|
|
200
|
+
4. Run `pytest tests/ -v` and make sure everything passes
|
|
201
|
+
5. Open a Pull Request
|
|
202
|
+
|
|
203
|
+
See `CONTRIBUTING.md` for the full guide. First-time contributors are very welcome — if you're unsure where to start, open an issue and ask.
|
|
204
|
+
|
|
205
|
+
## Tech Stack
|
|
206
|
+
|
|
207
|
+
- Python 3.8+
|
|
208
|
+
- PyTorch
|
|
209
|
+
- SymPy
|
|
210
|
+
- NumPy / SciPy
|
|
211
|
+
|
|
212
|
+
## License
|
|
213
|
+
|
|
214
|
+
MIT — see `LICENSE`.
|
|
215
|
+
|
|
216
|
+
## Acknowledgments
|
|
217
|
+
|
|
218
|
+
Built on the original Physics-Informed Neural Networks formulation by Raissi, Perikaris, and Karniadakis (2019). Inspired by the usability goals of DeepXDE but with a focus on time-to-first-result.
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
# PINNForge
|
|
2
|
+
|
|
3
|
+
**Solve partial differential equations in 3 lines of code.**
|
|
4
|
+
|
|
5
|
+

|
|
6
|
+
|
|
7
|
+
PINNForge is a lightweight wrapper around PyTorch that handles the boilerplate of Physics-Informed Neural Networks so you can focus on the physics.
|
|
8
|
+
|
|
9
|
+
## Install
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
pip install pinnforge
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## Quick Start
|
|
16
|
+
|
|
17
|
+
```python
|
|
18
|
+
from pinnforge import solve_pde
|
|
19
|
+
|
|
20
|
+
result = solve_pde("heat", epochs=5000)
|
|
21
|
+
print(f"Relative L2 error: {result['metrics']['relative_l2_error']:.2e}")
|
|
22
|
+
# Relative L2 error: 3.42e-02
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
That's it. The library automatically builds the network, generates training points, trains, validates, and returns metrics.
|
|
26
|
+
|
|
27
|
+
## Why PINNForge?
|
|
28
|
+
|
|
29
|
+
Setting up a PINN in the standard tooling requires 25+ lines of boilerplate before you see your first result. PINNForge gets you there in 3.
|
|
30
|
+
|
|
31
|
+
### Side-by-side: solving the 1D Heat equation
|
|
32
|
+
|
|
33
|
+
<table>
|
|
34
|
+
<tr>
|
|
35
|
+
<th width="50%">PINNForge (3 lines)</th>
|
|
36
|
+
<th width="50%">DeepXDE (~25 lines)</th>
|
|
37
|
+
</tr>
|
|
38
|
+
<tr>
|
|
39
|
+
<td>
|
|
40
|
+
|
|
41
|
+
```python
|
|
42
|
+
from pinnforge import solve_pde
|
|
43
|
+
|
|
44
|
+
result = solve_pde("heat", epochs=5000)
|
|
45
|
+
print(result['metrics']['relative_l2_error'])
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
That's the whole program. Everything else is handled by the library.
|
|
49
|
+
|
|
50
|
+
</td>
|
|
51
|
+
<td>
|
|
52
|
+
|
|
53
|
+
```python
|
|
54
|
+
import deepxde as dde
|
|
55
|
+
import numpy as np
|
|
56
|
+
|
|
57
|
+
def pde(x, y):
|
|
58
|
+
dy_t = dde.grad.jacobian(y, x, i=0, j=1)
|
|
59
|
+
dy_xx = dde.grad.hessian(y, x, i=0, j=0)
|
|
60
|
+
return dy_t - dy_xx
|
|
61
|
+
|
|
62
|
+
geom = dde.geometry.Interval(0, 1)
|
|
63
|
+
timedomain = dde.geometry.TimeDomain(0, 1)
|
|
64
|
+
geomtime = dde.geometry.GeometryXTime(geom, timedomain)
|
|
65
|
+
|
|
66
|
+
def boundary(x, on_boundary):
|
|
67
|
+
return on_boundary
|
|
68
|
+
|
|
69
|
+
def initial(x):
|
|
70
|
+
return np.sin(np.pi * x[:, 0:1])
|
|
71
|
+
|
|
72
|
+
bc = dde.icbc.DirichletBC(geomtime, lambda x: 0, boundary)
|
|
73
|
+
ic = dde.icbc.IC(geomtime, initial, lambda _, on_initial: on_initial)
|
|
74
|
+
|
|
75
|
+
data = dde.data.TimePDE(
|
|
76
|
+
geomtime, pde, [bc, ic],
|
|
77
|
+
num_domain=2000, num_boundary=100, num_initial=100,
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
net = dde.nn.FNN([2] + [64] * 3 + [1], "tanh", "Glorot normal")
|
|
81
|
+
model = dde.Model(data, net)
|
|
82
|
+
model.compile("adam", lr=1e-3)
|
|
83
|
+
model.train(epochs=15000)
|
|
84
|
+
model.compile("L-BFGS")
|
|
85
|
+
losshistory, train_state = model.train()
|
|
86
|
+
|
|
87
|
+
# Then write validation and plotting code yourself
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
The setup is 9x longer and you still need to write your own validation and plots after training.
|
|
91
|
+
|
|
92
|
+
</td>
|
|
93
|
+
</tr>
|
|
94
|
+
</table>
|
|
95
|
+
|
|
96
|
+
### Feature comparison
|
|
97
|
+
|
|
98
|
+
| Feature | PINNForge | DeepXDE | PINNs-Torch |
|
|
99
|
+
|---------|-----------|---------|-------------|
|
|
100
|
+
| One-line solve API | ✅ | ❌ | ❌ |
|
|
101
|
+
| Symbolic PDE definition (SymPy) | ✅ | ❌ | ❌ |
|
|
102
|
+
| Auto-generated validation metrics | ✅ | ❌ | ❌ |
|
|
103
|
+
| Auto-generated plots | ✅ | ❌ | ❌ |
|
|
104
|
+
| Sensible defaults per PDE | ✅ | ❌ | ❌ |
|
|
105
|
+
| Built-in numerical solvers | ✅ | ❌ | ❌ |
|
|
106
|
+
| Multi-backend (TF / JAX / PyTorch) | PyTorch only | ✅ | PyTorch |
|
|
107
|
+
| 3D+ problems | Limited | ✅ | Limited |
|
|
108
|
+
| Battle-tested since 2019 | ❌ (v0.1) | ✅ | ❌ |
|
|
109
|
+
|
|
110
|
+
**The trade-off is on purpose.** PINNForge trades depth for time-to-result. If you need complex PDEs or production-scale 3D problems, use DeepXDE. If you want to try an idea in 5 minutes with minimal code, use PINNForge.
|
|
111
|
+
|
|
112
|
+
## What's Included
|
|
113
|
+
|
|
114
|
+
- **`solve_pde()`** — one-line API for common PDEs (Heat, Burgers, Wave)
|
|
115
|
+
- **`AutoPINN`** — configurable solver with auto-selected hyperparameters
|
|
116
|
+
- **`PINN`** — the underlying neural network module
|
|
117
|
+
- **`SymbolicPDE`** — define custom PDEs using SymPy math notation
|
|
118
|
+
- **`NumericalSolver`** — finite-difference reference solutions
|
|
119
|
+
- **`ExperimentLogger`** — automatic logging of training metrics and checkpoints
|
|
120
|
+
|
|
121
|
+
## Custom PDEs with SymPy
|
|
122
|
+
|
|
123
|
+
For problems not in the built-in registry, define them symbolically:
|
|
124
|
+
|
|
125
|
+
```python
|
|
126
|
+
import sympy as sp
|
|
127
|
+
from pinnforge import AutoPINN
|
|
128
|
+
|
|
129
|
+
x, t, u = sp.symbols('x t u')
|
|
130
|
+
equation = sp.diff(u, t) + u * sp.diff(u, x) - 0.01 * sp.diff(u, x, 2)
|
|
131
|
+
|
|
132
|
+
solver = AutoPINN.from_symbolic(equation, [x, t], u)
|
|
133
|
+
result = solver.solve()
|
|
134
|
+
print(result['metrics'])
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
## Known Limitations (v0.1.0)
|
|
138
|
+
|
|
139
|
+
This is an early release. Here's what doesn't work yet:
|
|
140
|
+
|
|
141
|
+
- **Fourier feature embeddings are implemented but disabled by default.** They currently cause training collapse on test problems (error jumps from 1e-2 to 1.2). Help wanted — see [#3](https://github.com/ved-rl/pinnforge/issues/3).
|
|
142
|
+
- **Adaptive loss weighting is implemented but disabled by default.** Same reason.
|
|
143
|
+
- **Adaptive activation (SA-PINN) is disabled by default.** Interacts badly with Fourier features.
|
|
144
|
+
- **Burgers equation validation returns `{}`.** The analytical solution currently in the code is the Heat equation's solution, not Burgers'. Burgers has no simple closed-form solution for this initial condition. See [#4](https://github.com/ved-rl/pinnforge/issues/4).
|
|
145
|
+
- **Only 1D problems are supported.** Multi-dimensional PDEs are planned for v0.2.0.
|
|
146
|
+
- **No CLI yet.** Everything is Python-only.
|
|
147
|
+
|
|
148
|
+
## Contributing
|
|
149
|
+
|
|
150
|
+
This is a young project and there are several tasks that would help a lot. If you're looking for a place to contribute to SciML tooling, this project is a place where help is welcome.
|
|
151
|
+
|
|
152
|
+
### Good First Issues
|
|
153
|
+
|
|
154
|
+
| Task | Difficulty | Description |
|
|
155
|
+
|------|------------|-------------|
|
|
156
|
+
| **Fix Burgers analytical solution** | Easy | The `BurgersEquation.analytical_solution` method returns the Heat solution. Replace with `NotImplementedError` and fall back to the numerical solver. See [#4](https://github.com/ved-rl/pinnforge/issues/4). |
|
|
157
|
+
| **Add tests for Wave equation** | Easy | `WaveEquation` exists in `pdes.py` but has no test coverage. Add a basic smoke test to `tests/test_pdes.py`. |
|
|
158
|
+
| **Add a quickstart notebook** | Easy | Create `examples/quickstart.ipynb` that walks through the 3-line API and shows results inline. |
|
|
159
|
+
| **Improve error messages** | Easy | Several places raise generic errors. Add context-specific messages for common failure modes. |
|
|
160
|
+
|
|
161
|
+
### Larger Contributions Welcome
|
|
162
|
+
|
|
163
|
+
| Task | Difficulty | Description |
|
|
164
|
+
|------|------------|-------------|
|
|
165
|
+
| **Fix Fourier feature implementation** | Medium | Features collapse to a constant output. Current implementation uses only `output_dim // 2` random projections; standard RFF uses 128+. See [#3](https://github.com/ved-rl/pinnforge/issues/3). |
|
|
166
|
+
| **L-BFGS fine-tuning** | Medium | Adding L-BFGS as a second-stage optimizer (as DeepXDE does) would improve final accuracy by 10x. |
|
|
167
|
+
| **2D PDE support** | Hard | Currently limited to 1D spatial domains. Requires changes to `pdes.py`, `data.py`, and the `PINN` class. |
|
|
168
|
+
| **JAX backend** | Hard | Optional JAX backend for 5-10x faster training. See `jinns` for reference. |
|
|
169
|
+
| **Adaptive sampling (RAR)** | Medium | Residual-based adaptive refinement. Sample new collocation points where the PDE residual is highest. |
|
|
170
|
+
|
|
171
|
+
### How to Contribute
|
|
172
|
+
|
|
173
|
+
1. Fork the repo
|
|
174
|
+
2. Create a feature branch (`git checkout -b fix-fourier-features`)
|
|
175
|
+
3. Make your changes
|
|
176
|
+
4. Run `pytest tests/ -v` and make sure everything passes
|
|
177
|
+
5. Open a Pull Request
|
|
178
|
+
|
|
179
|
+
See `CONTRIBUTING.md` for the full guide. First-time contributors are very welcome — if you're unsure where to start, open an issue and ask.
|
|
180
|
+
|
|
181
|
+
## Tech Stack
|
|
182
|
+
|
|
183
|
+
- Python 3.8+
|
|
184
|
+
- PyTorch
|
|
185
|
+
- SymPy
|
|
186
|
+
- NumPy / SciPy
|
|
187
|
+
|
|
188
|
+
## License
|
|
189
|
+
|
|
190
|
+
MIT — see `LICENSE`.
|
|
191
|
+
|
|
192
|
+
## Acknowledgments
|
|
193
|
+
|
|
194
|
+
Built on the original Physics-Informed Neural Networks formulation by Raissi, Perikaris, and Karniadakis (2019). Inspired by the usability goals of DeepXDE but with a focus on time-to-first-result.
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=61.0.0", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "pinnforge"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "Solve PDEs in 3 lines of code"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.8"
|
|
11
|
+
license = "MIT"
|
|
12
|
+
license-files = ["LICENSE"]
|
|
13
|
+
authors = [
|
|
14
|
+
{ name = "Vedauv Rajkumar Leelavathi", email = "vedauvrajkumarleelavathi@gmail.com" }
|
|
15
|
+
]
|
|
16
|
+
dependencies = [
|
|
17
|
+
"torch>=1.9.0",
|
|
18
|
+
"numpy>=1.21.0",
|
|
19
|
+
"sympy>=1.9.0",
|
|
20
|
+
"matplotlib>=3.4.0",
|
|
21
|
+
"scipy>=1.7.0",
|
|
22
|
+
"pyyaml>=5.4.0",
|
|
23
|
+
"tqdm>=4.62.0"
|
|
24
|
+
]
|
|
25
|
+
|
|
26
|
+
[project.urls]
|
|
27
|
+
Repository = "https://github.com/ved-rl/pinnforge"
|
|
28
|
+
|
|
29
|
+
[project.optional-dependencies]
|
|
30
|
+
dev = [
|
|
31
|
+
"pytest>=6.0.0",
|
|
32
|
+
"pytest-cov>=2.12.0",
|
|
33
|
+
"black>=21.0.0",
|
|
34
|
+
"flake8>=3.9.0"
|
|
35
|
+
]
|
|
36
|
+
|
|
37
|
+
[tool.setuptools.packages.find]
|
|
38
|
+
where = ["src"]
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
"""
|
|
2
|
+
PINNForge - Physics-Informed Neural Networks made easy
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from pinnforge.core.pinn import PINN, PINNTrainer, FourierFeatureMapping
|
|
6
|
+
from pinnforge.physics.pdes import BurgersEquation, HeatEquation, WaveEquation, PDE, create_pde
|
|
7
|
+
from pinnforge.utils.data import generate_simulation_data, add_noise
|
|
8
|
+
from pinnforge.utils.logging import ExperimentLogger
|
|
9
|
+
|
|
10
|
+
# Auto-solver
|
|
11
|
+
from pinnforge.auto import AutoPINN, solve_pde
|
|
12
|
+
|
|
13
|
+
# Symbolic
|
|
14
|
+
try:
|
|
15
|
+
from pinnforge.symbolic import SymbolicPDE, SymbolicPINNTrainer
|
|
16
|
+
except ImportError:
|
|
17
|
+
SymbolicPDE = None
|
|
18
|
+
|
|
19
|
+
# Benchmarks
|
|
20
|
+
try:
|
|
21
|
+
from pinnforge.benchmarks import compute_relative_l2_error, compute_metrics, BenchmarkRunner
|
|
22
|
+
except ImportError:
|
|
23
|
+
compute_relative_l2_error = None
|
|
24
|
+
|
|
25
|
+
__version__ = "0.1.0"
|
|
26
|
+
|
|
27
|
+
__all__ = [
|
|
28
|
+
# One-line API
|
|
29
|
+
"solve_pde",
|
|
30
|
+
"AutoPINN",
|
|
31
|
+
|
|
32
|
+
# Core
|
|
33
|
+
"PINN",
|
|
34
|
+
"PINNTrainer",
|
|
35
|
+
"FourierFeatureMapping",
|
|
36
|
+
|
|
37
|
+
# Physics
|
|
38
|
+
"BurgersEquation",
|
|
39
|
+
"HeatEquation",
|
|
40
|
+
"WaveEquation",
|
|
41
|
+
"PDE",
|
|
42
|
+
"create_pde",
|
|
43
|
+
|
|
44
|
+
# Utils
|
|
45
|
+
"generate_simulation_data",
|
|
46
|
+
"add_noise",
|
|
47
|
+
"ExperimentLogger",
|
|
48
|
+
|
|
49
|
+
# Symbolic
|
|
50
|
+
"SymbolicPDE",
|
|
51
|
+
"SymbolicPINNTrainer",
|
|
52
|
+
|
|
53
|
+
# Benchmarks
|
|
54
|
+
"compute_relative_l2_error",
|
|
55
|
+
"compute_metrics",
|
|
56
|
+
"BenchmarkRunner",
|
|
57
|
+
]
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Auto-configuration heuristics for PINNs.
|
|
3
|
+
Based on PDE characteristics, auto-selects hyperparameters.
|
|
4
|
+
"""
|
|
5
|
+
import numpy as np
|
|
6
|
+
from dataclasses import dataclass, field
|
|
7
|
+
from typing import Tuple, List, Optional
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
@dataclass
|
|
11
|
+
class AutoConfig:
|
|
12
|
+
"""
|
|
13
|
+
Auto-selected configuration for a PINN solve.
|
|
14
|
+
"""
|
|
15
|
+
# Network
|
|
16
|
+
layers: List[int] = field(default_factory=lambda: [2, 64, 64, 64, 1])
|
|
17
|
+
activation: str = "tanh"
|
|
18
|
+
|
|
19
|
+
# Fourier features
|
|
20
|
+
use_fourier: bool = False
|
|
21
|
+
fourier_scale: float = 8.0
|
|
22
|
+
|
|
23
|
+
# Training
|
|
24
|
+
n_collocation: int = 10000
|
|
25
|
+
n_boundary: int = 200
|
|
26
|
+
n_initial: int = 200
|
|
27
|
+
epochs: int = 5000
|
|
28
|
+
learning_rate: float = 1e-3
|
|
29
|
+
batch_size: int = 1000
|
|
30
|
+
|
|
31
|
+
# Loss
|
|
32
|
+
use_adaptive_weights: bool = False
|
|
33
|
+
weight_bc: float = 1.0
|
|
34
|
+
weight_ic: float = 1.0
|
|
35
|
+
|
|
36
|
+
# Precision
|
|
37
|
+
precision: str = "float32"
|
|
38
|
+
|
|
39
|
+
@classmethod
|
|
40
|
+
def for_pde(cls, pde_name: str, nu: Optional[float] = None,
|
|
41
|
+
domain: Optional[List[Tuple]] = None) -> "AutoConfig":
|
|
42
|
+
"""
|
|
43
|
+
Auto-select config based on PDE type and parameters.
|
|
44
|
+
|
|
45
|
+
Heuristics based on PINNacle findings and common failure modes:
|
|
46
|
+
- Stiff problems (low nu) need more collocation + larger Fourier scale
|
|
47
|
+
- High-frequency problems need wider networks
|
|
48
|
+
- Long time domains need more collocation points
|
|
49
|
+
"""
|
|
50
|
+
config = cls()
|
|
51
|
+
|
|
52
|
+
if pde_name.lower() == "burgers":
|
|
53
|
+
if nu is not None and nu < 0.02:
|
|
54
|
+
# Stiff Burgers: needs more points, larger Fourier scale
|
|
55
|
+
config.layers = [2, 128, 128, 128, 1]
|
|
56
|
+
config.fourier_scale = 15.0
|
|
57
|
+
config.n_collocation = 20000
|
|
58
|
+
config.epochs = 10000
|
|
59
|
+
config.learning_rate = 5e-4
|
|
60
|
+
else:
|
|
61
|
+
config.layers = [2, 64, 64, 64, 1]
|
|
62
|
+
config.fourier_scale = 10.0
|
|
63
|
+
|
|
64
|
+
elif pde_name.lower() == "heat":
|
|
65
|
+
config.layers = [2, 64, 64, 64, 1]
|
|
66
|
+
config.fourier_scale = 8.0
|
|
67
|
+
config.epochs = 3000
|
|
68
|
+
|
|
69
|
+
elif pde_name.lower() == "wave":
|
|
70
|
+
# Wave equations are high-frequency sensitive
|
|
71
|
+
config.layers = [2, 96, 96, 96, 1]
|
|
72
|
+
config.fourier_scale = 20.0
|
|
73
|
+
config.epochs = 8000
|
|
74
|
+
config.precision = "float64" # Waves need precision
|
|
75
|
+
|
|
76
|
+
elif pde_name.lower() == "navier-stokes":
|
|
77
|
+
# NS is the hardest: needs big network + precision
|
|
78
|
+
config.layers = [3, 128, 128, 128, 128, 2] # (x,y,t) -> (u,v)
|
|
79
|
+
config.fourier_scale = 12.0
|
|
80
|
+
config.n_collocation = 50000
|
|
81
|
+
config.epochs = 20000
|
|
82
|
+
config.learning_rate = 1e-4
|
|
83
|
+
config.precision = "float64"
|
|
84
|
+
|
|
85
|
+
# Domain-based adjustments
|
|
86
|
+
if domain:
|
|
87
|
+
# Longer time domains need more collocation
|
|
88
|
+
t_range = domain[1] if len(domain) > 1 else (0, 1)
|
|
89
|
+
t_span = t_range[1] - t_range[0]
|
|
90
|
+
if t_span > 2:
|
|
91
|
+
config.n_collocation = int(config.n_collocation * t_span / 1.0)
|
|
92
|
+
|
|
93
|
+
return config
|