From f02c5f2bbed6c288b400bec87d3dfc8184fa1c10 Mon Sep 17 00:00:00 2001 From: Davide Grilli Date: Thu, 14 May 2026 14:28:00 +0200 Subject: [PATCH] =?UTF-8?q?PINN:=20vincolo=20IC=20hard=20=E2=80=94=20molti?= =?UTF-8?q?plica=20output=20per=20t=5Fnorm=20per=20evitare=20trivial=20sol?= =?UTF-8?q?ution?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 --- model.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/model.py b/model.py index b4dbda3..65c9a4f 100644 --- a/model.py +++ b/model.py @@ -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)