Files
pinn/CLAUDE.md
T

5.1 KiB
Raw Blame History

CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

Commit Messages

Write all git commit messages in Italian.

Commands

source .venv/bin/activate          # always first

python app.py                      # forward PINN: train / evaluate / visualize
python fdm/app.py                  # FDM reference solver menu
python -m inverse.app              # inverse PINN menu (to be implemented)

pytest                             # all tests
pytest -m "not slow"               # skip full-training tests
pytest tests/test_model.py         # single file

Delete models/best_heat_pinn_model.pth to retrain from scratch.

Architecture

config.py        ← all physical + numerical parameters
model.py         ← HeatPINN (5-layer FC) + heat_pinn_loss()
engine.py        ← prepare_data(), train_model(), evaluate_model()
app.py           ← forward PINN CLI
visualizer.py    ← PINN vs FDM plots (Plotly HTML)
fdm/solver.py    ← FTCS explicit scheme, returns T_matrix[NX, NT]
inverse/         ← inverse PINN (to implement — see plan below)
tests/           ← pytest suite (42 tests); conftest.py has device, small_data, pinn_model fixtures

Key design decisions

Output scaling (model.py:forward):

T = T_AMB + (Q_VAL · L / K) · net(x_norm, t_norm)

This keeps net outputs in [0,1] and ∂T/∂x at O(1). Do not remove.

Loss normalization (model.py:heat_pinn_loss): all four terms are scaled to O(1) via _T_char = Q_VAL·L/K and _bc_scale. Changing physical params in config.py does not require retuning weights.

Collocation clustering (engine.py:prepare_data): 25% extra points near X_SRC (source gradient) and T_STEP (flux discontinuity). First lever to pull if accuracy is poor: increase N_F.

Training sequence: Adam (early stopping + ReduceLROnPlateau) → L-BFGS fine-tuning. L-BFGS uses a _last closure dict to capture loss components without double-calling the loss outside a grad context.

FDM Robin BCs (fdm/solver.py): implicit-like update T[0] = (T[1] + robin_coeff·T_amb) / (1 + robin_coeff). Point source added after BCs: T[i_src] += Q·α·dt/(k·dx).


Inverse PINN — implementation plan

Goal: identify unknown physical parameters (ALPHA, K, H_CONV) from sparse noisy temperature measurements. The network learns T(x,t) and the physics parameters simultaneously.

Files to create (in order)

inverse/config_inverse.py

  • N_SENSORS, SENSOR_POSITIONS (list of x positions)
  • NOISE_STD — Gaussian noise std on measurements [°C]
  • IDENTIFY = ['alpha', 'k', 'h_conv']
  • ALPHA_INIT, K_INIT, H_CONV_INIT — initial guesses (25× off from true values)
  • EPOCHS_INV, LR_ADAM_INV, W_DATA = 10.0
  • MODELS_DIR, DATA_PATH

inverse/data.py

  • generate_measurements(noise_std, sensor_positions): call fdm.solver.solve() with true params from config.py, sample at nearest FDM nodes, add noise, save to inverse/data/measurements.csv (columns: x, t, T)
  • load_measurements(device): load CSV → tensors (x_s, t_s, T_meas) on device

inverse/model.pyInverseHeatPINN(nn.Module)

  • Same 5-layer architecture as HeatPINN
  • Unknown params as log-space nn.Parameter (guarantees positivity without constraints):
    self.log_alpha  = nn.Parameter(torch.log(torch.tensor(ALPHA_INIT)))
    self.log_k      = nn.Parameter(torch.log(torch.tensor(K_INIT)))
    self.log_h_conv = nn.Parameter(torch.log(torch.tensor(H_CONV_INIT)))
    
  • Properties alpha, k, h_conv that return exp(log_*)
  • forward() uses same output scaling as HeatPINN but with self.k and self.alpha
  • Never .detach() the learned params inside the loss — gradients must flow through them

inverse/loss.pyinverse_heat_pinn_loss(..., x_s, t_s, T_meas)

  • Same PDE/IC/BC structure as heat_pinn_loss() but uses model.alpha, model.k, model.h_conv
  • Normalization scales must be computed from the current learned params (not config constants), otherwise there is no gradient signal toward the physics params
  • Adds data fit term: L_data = mean((T_pred(x_s, t_s) T_meas)²) / T_char²
  • Total: w_pde·L_pde + w_ic·L_ic + w_bc·L_bc + w_data·L_data

inverse/engine.py

  • prepare_data_inverse(): same clustering strategy as engine.prepare_data()
  • train_inverse(data, measurements): Adam only (no L-BFGS — unstable when physics params are learnable because loss curvature differs by orders of magnitude between network weights and physics params); print identified param values every 100 epochs
  • evaluate_inverse(model): print table of true vs identified params with relative error %; also compute L2 error of T field vs FDM

inverse/app.py — CLI menu: (1) Generate measurements, (2) Train, (3) Evaluate, (0) Exit

inverse/__init__.py — empty

Pitfalls

  • If W_DATA is too high, BC/IC are ignored and the net overfits measurements (physics collapses)
  • Sensors far from x=0 and x=L → poor identification of H_CONV (weak boundary signal)
  • Do not resample sensor points each epoch — (x_s, t_s, T_meas) are fixed throughout training