b8301a4329
- pytest.ini: configura testpaths, marker slow, output verboso - tests/conftest.py: fixture condivise (device, small_data, pinn_model) - tests/test_config.py: sanità parametri fisici e numerici, CFL, _pde_scale - tests/test_model.py: HeatPINN.forward e heat_pinn_loss (shape, finiti, zero-weight analytici per IC e BC, scaling dei pesi) - tests/test_engine_data.py: set_seed, _get_device, prepare_data (shape, bounds, device consistency, determinismo) - tests/test_integration_pinn.py: pipeline dati→modello→loss→backward - tests/test_e2e.py: FDM completo, visualizer FDM/PINN con tmp_path, training breve (2 test @slow) - requirements.txt: aggiunge pytest>=7.0.0 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
88 lines
3.3 KiB
Python
88 lines
3.3 KiB
Python
import sys
|
|
import os
|
|
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
|
|
|
import pytest
|
|
import numpy as np
|
|
import torch
|
|
import config
|
|
|
|
|
|
# ── FDM ───────────────────────────────────────────────────────────────────────
|
|
|
|
def test_fdm_full_run():
|
|
"""Il solver FDM produce un campo di temperatura fisicamente corretto."""
|
|
from fdm.solver import solve
|
|
T, x, t = solve()
|
|
|
|
assert T.shape == (config.NX, config.NT)
|
|
assert np.isfinite(T).all()
|
|
assert T[:, -1].mean() > config.T0 # la sorgente ha scaldato il dominio
|
|
assert T.max() > config.T0 # picco sopra la IC
|
|
assert T.min() >= config.T0 - 1e-6 # nessun raffreddamento sotto T0 (no sorgenti fredde)
|
|
|
|
|
|
def test_fdm_visualizer_creates_html(tmp_path, monkeypatch):
|
|
"""Il visualizer FDM scrive almeno un file HTML senza errori."""
|
|
import fdm.visualizer as fdm_vis
|
|
monkeypatch.setattr(fdm_vis, 'BASE_DIR', str(tmp_path))
|
|
|
|
from fdm.solver import solve
|
|
T, x, t = solve()
|
|
fdm_vis.visualize_fdm(T, x, t)
|
|
|
|
html_files = list(tmp_path.rglob('*.html'))
|
|
assert len(html_files) >= 1, "Nessun file HTML generato dal visualizer FDM"
|
|
|
|
|
|
def test_pinn_visualizer_creates_html(tmp_path, monkeypatch):
|
|
"""Il visualizer PINN scrive i tre file HTML senza errori."""
|
|
import visualizer as pinn_vis
|
|
monkeypatch.setattr(pinn_vis, 'BASE_DIR', str(tmp_path))
|
|
|
|
from fdm.solver import solve as fdm_solve
|
|
T_fdm, _, _ = fdm_solve()
|
|
|
|
nx, nt = 20, 20
|
|
x_vals = np.linspace(0, config.L, nx)
|
|
t_vals = np.linspace(0, config.T_END, nt)
|
|
T_pred = np.full((nx, nt), config.T_AMB) # predizione costante (dummy)
|
|
|
|
pinn_vis.visualize_heat_field(T_pred, x_vals, t_vals, T_fdm)
|
|
|
|
html_files = list(tmp_path.rglob('*.html'))
|
|
assert len(html_files) == 3, f"Attesi 3 HTML, trovati {len(html_files)}"
|
|
|
|
|
|
# ── PINN training (lento) ─────────────────────────────────────────────────────
|
|
|
|
@pytest.mark.slow
|
|
def test_pinn_training_saves_checkpoint(tmp_path, monkeypatch):
|
|
"""Training per 30 epoche: il checkpoint viene salvato."""
|
|
import engine
|
|
save_path = str(tmp_path / 'model.pth')
|
|
monkeypatch.setattr(engine, 'MODEL_SAVE_PATH', save_path)
|
|
monkeypatch.setattr(engine, 'MODELS_DIR', str(tmp_path))
|
|
|
|
from engine import prepare_data, train_model
|
|
data = prepare_data(N_f=300, N_ic=100, N_bc=100)
|
|
train_model(data, epochs=30, patience=30)
|
|
|
|
assert os.path.exists(save_path)
|
|
ckpt = torch.load(save_path, map_location='cpu', weights_only=True)
|
|
assert 'state_dict' in ckpt
|
|
|
|
|
|
@pytest.mark.slow
|
|
def test_pinn_evaluate_after_training(tmp_path, monkeypatch):
|
|
"""evaluate_model gira senza errori dopo un training minimo."""
|
|
import engine
|
|
save_path = str(tmp_path / 'model.pth')
|
|
monkeypatch.setattr(engine, 'MODEL_SAVE_PATH', save_path)
|
|
monkeypatch.setattr(engine, 'MODELS_DIR', str(tmp_path))
|
|
|
|
from engine import prepare_data, train_model, evaluate_model
|
|
data = prepare_data(N_f=300, N_ic=100, N_bc=100)
|
|
train_model(data, epochs=30, patience=30)
|
|
evaluate_model(data)
|