PINN: vincolo IC hard — moltiplica output per t_norm per evitare trivial solution

La normalizzazione introdotta (x,t in [0,1]²) rendeva il minimo banale
net=0 (T=T_AMB ovunque) troppo accessibile, causando il collasso del training.
Soluzione: T = T_AMB + T_char * (t/T_END) * net(x_norm, t_norm).
Così T(x,0) = T_AMB per costruzione (vincolo hard) e la rete deve trovare
soluzioni non banali per t>0. La loss IC resta ma è sempre 0.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-05-14 14:28:00 +02:00
parent 9e77deffd5
commit f02c5f2bbe
+7 -5
View File
@@ -15,11 +15,13 @@ class HeatPINN(nn.Module):
def forward(self, xt):
# Normalize inputs to [0,1]x[0,1] so both dimensions share the same scale
scale = torch.tensor([config.L, config.T_END], device=xt.device, dtype=xt.dtype)
xt_norm = xt / scale
# Output scaled to physical range: T_AMB + (Q*L/K) * net
# net learns dimensionless perturbation in [0,1] range
return config.T_AMB + (config.Q_VAL * config.L / config.K) * self.net(xt_norm)
x_norm = xt[:, 0:1] / config.L
t_norm = xt[:, 1:2] / config.T_END
xt_norm = torch.cat([x_norm, t_norm], dim=1)
# Multiply by t_norm to enforce IC as hard constraint: T(x,0) = T_AMB exactly.
# This eliminates the trivial solution (net=0) and forces non-trivial learning for t>0.
T_char = config.Q_VAL * config.L / config.K
return config.T_AMB + T_char * t_norm.squeeze(1) * self.net(xt_norm).squeeze(1)
# Precomputed loss scales (depend only on config constants)