# Integrazione temporale implicita del sistema semidiscreto della shell. # # Dopo l'assemblaggio il problema è # # C * dT/dt + K * T = f(t) con K = K_cond + K_conv # # Con Eulero implicito: # # (C/dt + K) * T^(n+1) = (C/dt) * T^n + f^(n+1) # # La matrice A = C/dt + K dipende solo da mesh, materiale, passo temporale e # condizioni convettive: nessuno di questi cambia durante un run, quindi si # fattorizza LU una volta sola e a ogni passo si risolvono solo i sistemi # triangolari. Se in futuro si introducessero proprietà dipendenti dalla # temperatura, A andrebbe ricostruita e rifattorizzata a ogni passo. import scipy.sparse as sp from scipy.sparse.linalg import splu def costruisci_solutore(C, K, dt_s: float) -> dict: # A è simmetrica e definita positiva: capacità, conduzione e convezione # lo sono tutte. L'ordinamento MMD sul grafo di A + A^T sfrutta la # simmetria e riduce il riempimento della fattorizzazione rispetto al # COLAMD di default, pensato per matrici non simmetriche. A = (C / dt_s + K).tocsc() return { "lu": splu(A, permc_spec="MMD_AT_PLUS_A"), "C_su_dt": (C / dt_s).tocsr(), "dt_s": dt_s, } def passo_implicito(solutore: dict, T, f_costante, f_sorgente): # Avanza il campo di un passo dt. f_costante raccoglie i termini noti # indipendenti dal tempo (convezione verso l'ambiente su facce e bordi), # f_sorgente il carico della sorgente all'istante di arrivo. rhs = solutore["C_su_dt"] @ T + f_costante + f_sorgente return solutore["lu"].solve(rhs)