File size: 2,964 Bytes
f220c93
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
from __future__ import annotations

import json
import random
import sys
from pathlib import Path
from typing import Any

import numpy as np
import torch
import yaml

PROJECT_ROOT = Path(__file__).resolve().parents[1]
if str(PROJECT_ROOT) not in sys.path:
    sys.path.insert(0, str(PROJECT_ROOT))


def load_config(config_path: str | Path | None = None) -> dict[str, Any]:
    path = Path(config_path) if config_path else PROJECT_ROOT / "conf" / "config.yaml"
    if not path.is_absolute():
        path = PROJECT_ROOT / path
    with path.open("r", encoding="utf-8") as f:
        return yaml.safe_load(f)


def project_path(path: str | Path) -> Path:
    path = Path(path)
    return path if path.is_absolute() else PROJECT_ROOT / path


def seed_everything(seed: int) -> None:
    random.seed(seed)
    np.random.seed(seed)
    torch.manual_seed(seed)
    if torch.cuda.is_available():
        torch.cuda.manual_seed_all(seed)


def select_device(value: str = "auto") -> torch.device:
    if value == "auto":
        return torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
    return torch.device(value)


def ensure_parent(path: Path) -> None:
    path.parent.mkdir(parents=True, exist_ok=True)


def ensure_runtime_dirs(cfg: dict[str, Any]) -> None:
    for key in ("fake_data", "prediction", "metrics", "loss", "figure"):
        ensure_parent(project_path(cfg["paths"][key]))
    ensure_parent(project_path(cfg["training"]["checkpoint"]))


def build_model(cfg: dict[str, Any]) -> torch.nn.Module:
    from model import PINNsformer1D

    model_cfg = cfg["model"]
    return PINNsformer1D(
        d_out=int(model_cfg["d_out"]),
        d_hidden=int(model_cfg["d_hidden"]),
        d_model=int(model_cfg["d_model"]),
        N=int(model_cfg["num_layers"]),
        heads=int(model_cfg["heads"]),
    )


def initial_condition(x: np.ndarray | torch.Tensor, cfg: dict[str, Any]):
    initial = cfg["equation"]["initial"]
    center = initial["center"]
    sigma = initial["sigma"]
    if isinstance(x, torch.Tensor):
        return torch.exp(-((x - center) ** 2) / (2 * sigma**2))
    return np.exp(-((x - center) ** 2) / (2 * sigma**2))


def exact_reaction_solution(
    x: np.ndarray | torch.Tensor,
    t: np.ndarray | torch.Tensor,
    cfg: dict[str, Any],
):
    h = initial_condition(x, cfg)
    rate = cfg["equation"]["reaction_rate"]
    if isinstance(h, torch.Tensor):
        exp_term = torch.exp(rate * t)
    else:
        exp_term = np.exp(rate * t)
    return h * exp_term / (h * exp_term + 1 - h)


def relative_errors(pred: np.ndarray, target: np.ndarray) -> dict[str, float]:
    return {
        "relative_l1": float(np.sum(np.abs(target - pred)) / np.sum(np.abs(target))),
        "relative_l2": float(np.sqrt(np.sum((target - pred) ** 2) / np.sum(target**2))),
    }


def write_json(path: Path, payload: dict[str, Any]) -> None:
    ensure_parent(path)
    path.write_text(json.dumps(payload, indent=2), encoding="utf-8")