Files
simulatore-induttori/CLAUDE.md
T
davideandClaude Opus 5 0f46f3d479 Aggiunge il solutore termico FEM sulla mesh shell e la sua animazione 3D
fem.py risolve l'equazione del calore transitoria sulla superficie media del
cilindro con elementi shell quadrangolari bilineari: la circonferenza diventa
una direzione risolta (niente più attenuazione gaussiana né sink di aletta),
mentre lo spessore è collassato perché con parete da 0.18 mm il numero di
Biot è ~1e-7. Eulero implicito con matrice costante fattorizzata una volta
per run; le matrici di elemento sono identiche per tutti gli elementi e
l'assemblaggio è vettorizzato.

plot_animazione_fem.py dipinge il campo calcolato direttamente sugli elementi
della mesh, con ombreggiatura ricavata dalla normale radiale perché i colori
delle facce cambiano a ogni fotogramma.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-03 09:29:35 +02:00

9.3 KiB
Raw Blame History

CLAUDE.md

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

Lingua

L'utente comunica in italiano. Tutto il codice, i commenti, la documentazione e i messaggi di commit devono essere scritti in italiano.

Comandi

Usare sempre il virtual environment:

# Creare e attivare il venv (prima volta)
python -m venv .venv
source .venv/bin/activate

# Installare le dipendenze
pip install -r requirements.txt

# Punto di ingresso unico: `python main.py` elenca le azioni disponibili
python main.py mesh      # disegna la sola mesh a elementi shell
python main.py simula    # genera dataset/run_XXXX.csv + dataset/metadata.csv
python main.py grafico   # grafici del primo run
python main.py anima     # animazione 2D della sezione
python main.py anima3d   # animazione 3D isometrica del barattolo
python main.py fem       # animazione 3D del campo FEM sulla mesh shell

Ogni modulo resta eseguibile anche direttamente (python simulate.py, python plot_csv.py, ...).

Attivare sempre il venv (source .venv/bin/activate) prima di eseguire qualsiasi comando Python.

Non sono configurati test o linter.

Architettura

Generatore di dataset per misurazioni termiche pseudo-realistiche di una fascetta (anello cilindrico sottile) riscaldata da sorgenti a induzione in movimento.

Geometria: la fascetta ha diametro, spessore e lunghezza configurabili. Il dominio simulato è la sezione rettangolare lunghezza × spessore, con origine (0, 0) nel vertice in alto a sinistra: x = lunghezza (le sorgenti si muovono in direzione -x sul lato esterno), z = spessore (0 = lato esterno, spessore = lato interno). La coordinata circonferenziale y non è risolta: l'offset y delle sorgenti è collassato in un'attenuazione gaussiana del flusso. Il sensore è un pirometro a infrarossi dentro la fascetta che misura la superficie interna in un punto x fisso.

Flusso dei dati:

  1. main.py — dispatcher da riga di comando che seleziona l'azione (mesh, simula, grafico, anima, anima3d)
  2. config.py — tutti i parametri configurabili (dizionari SIMULAZIONE, FASCETTA, MESH, ARIA, SORGENTE, SENSORE, RANDOMIZZAZIONE)
  3. materials.py — dizionario MATERIALI con proprietà termofisiche ed elettriche per materiale
  4. simulate.py — motore principale: genera N run randomizzati, scrive i CSV, scrive metadata.csv
  5. plot_csv.py — visualizzazione autonoma per un singolo run
  6. mesh.py — mesh a elementi shell quadrangolari della superficie media del cilindro, visualizzata da plot_mesh.py
  7. fem.py — solutore termico transitorio a elementi finiti sulla mesh shell, animato da plot_animazione_fem.py

Mesh shell (mesh.py): griglia strutturata n_elementi_x × n_elementi_circonferenza di quadrilateri a 4 nodi sulla superficie media (raggio = (diametro spessore)/2); lo spessore non è discretizzato, è un attributo della shell. La mesh è chiusa lungo la circonferenza (nessun nodo duplicato sulla cucitura) e usa lo stesso sistema di coordinate globale di plot_animazione_3d.py: x = asse, y = R·sin(theta), z = R·cos(theta), con theta = 0 sul piano del sensore.

Due modelli termici distinti

Il progetto contiene due discretizzazioni indipendenti della stessa fisica; scambiano solo la cinematica delle sorgenti (_x_riferimento_iniziale_m, _x_riferimento_finale_m, _intervallo_attivo in simulate.py), che deve restare unica.

volumi finiti (simulate.py) elementi finiti (fem.py)
dominio sezione x-z (spessore risolto) superficie media x-s (circonferenza risolta)
circonferenza y attenuazione gaussiana + sink di aletta direzione risolta, conduzione reale
spessore n_nodi_z celle, deposizione esponenziale da skin depth collassato: T uniforme nella parete
griglia FASCETTA["n_nodi_x"], n_nodi_z MESH["n_elementi_x"], n_elementi_circonferenza
passo temporale SIMULAZIONE["dt_interno_s"] FEM["dt_s"]
output dataset CSV + animazioni 2D/3D animazione 3D sulla mesh

Pipeline FEM dentro fem.py: equazione risolta per unità di superficie media, rho·cp·t ∂T/∂t = k·t ∇²T + q(x, s, t) (h_est + h_int)(T T_amb), con convezione h_bordi sui due bordi anulari (area = spessore × perimetro). Il collasso in spessore è lecito perché Biot ≈ 1e-7 e il tempo di diffusione nella parete è di pochi ms: la skin depth non entra nel modello shell, conta solo il flusso assorbito. L'impronta della sorgente è una gaussiana isotropa in (x, s) con distanza circonferenziale valutata sull'immagine più vicina (superficie chiusa). Gli elementi sono bilineari a 4 nodi e tutti identici (rettangoli dx × ds), quindi le matrici di elemento (MASSA_RIF, RIGIDEZZA_X, RIGIDEZZA_S) sono calcolate una volta e assemblate in forma vettorizzata; la matrice di massa di superficie è riusata per capacità, convezione e carico della sorgente. Eulero implicito con matrice costante fattorizzata LU una volta per run (prepara_stato_fem), poi passo_implicito_fem assembla solo il termine noto.

Pipeline fisica dentro simula_singolo() in simulate.py:

  • La skin depth è calcolata dalla resistività elettrica del materiale e dalla frequenza di induzione (calcola_skin_depth_m)
  • Le sorgenti gaussiane in movimento producono un profilo di flusso termico superficiale q(x) sul lato esterno, variabile nel tempo (profilo_flusso_incidente_W_m2)
  • Quel flusso è ridistribuito volumetricamente attraverso lo spessore con decadimento esponenziale in z (profilo_deposizione_z_1_m): q_vol(x, z) = q(x) · p(z)
  • Uno schema 2D a volumi finiti con Eulero implicito integra l'equazione del calore su n_nodi_x × n_nodi_z celle: prepara_stato_termico costruisce griglia, coefficienti e matrice sparsa fattorizzata LU una volta per run (costruisci_solutore_implicito_2d, che restituisce l'oggetto splu), poi passo_implicito avanza il campo risolvendo solo il sistema triangolare
  • Le condizioni al contorno sono incorporate nella matrice: convezione su tutti e quattro i lati della sezione, più un termine di conduzione circonferenziale (y) verso il resto della fascetta assunto a temperatura ambiente. Il termine è un'equazione di aletta ricavata sull'intero volume del cilindro: il calore conduce lungo y attraverso l'intero spessore mentre le superfici esterna e interna dell'intero cilindro scambiano per convezione, dando q_y = -(h_esterno + h_interno)/spessore · (T - T_amb), senza parametri di conduzione y configurabili a parte
  • La temperatura iniziale del campo è la temperatura ambiente (randomizzata per run)
  • L'output del sensore aggiunge inerzia del primo ordine, rumore gaussiano e quantizzazione

prepara_stato_termico e passo_implicito sono condivisi con plot_animazione.py, che riproduce la fisica di run_0001 per animare la sezione: ogni modifica alla fisica va fatta lì, non duplicata.

Randomizzazione per run (configurazione_randomizzata): ogni run perturba velocità, flusso di picco, sigma del punto, offset y, temperatura ambiente e rumore del sensore con estrazioni gaussiane/uniformi da un RNG con seed fisso, garantendo riproducibilità.

Schema di output (dataset/run_XXXX.csv): serie temporale con colonne id_run, tempo_s, x_sorgente_m, offset_y_sorgente_m, flusso_termico_sorgente_W_m2, skin_depth_m, T_vera_lato_sensore_C, T_misurata_sensore_C, T_lato_caldo_C, T_ambiente_C, velocita_m_s, sigma_punto_m, flusso_picco_W_m2, materiale. metadata.csv ha una riga per run con tutti i parametri e le temperature di picco.

Convenzioni su config.py

Ogni parametro in config.py ha un commento che spiega solo cos'è (il suo significato fisico/funzionale), mai perché è impostato a un valore specifico. Quando si modifica un parametro per ottenere un certo comportamento (es. una velocità diversa, un range di temperatura target), va cambiato solo il valore: non aggiungere commenti che giustificano o motivano quel valore, perché diventano obsoleti/fuorvianti alla prossima modifica.

Vincoli progettuali chiave

  • Il modello ai volumi finiti di simulate.py è 2D nella sezione (x = lunghezza, z = spessore). La coordinata circonferenziale y non è risolta spazialmente — l'offset y del percorso delle sorgenti è collassato in un'attenuazione gaussiana del flusso, e la conduzione lungo y è un termine di scambio lineare verso la temperatura ambiente; il diametro è registrato solo come geometria del setup. Il modello FEM di fem.py fa il contrario (circonferenza risolta, spessore collassato): non modificare uno assumendo che valgano le ipotesi dell'altro.
  • Le posizioni di inizio/fine corsa delle sorgenti (x_inizio_m, x_fine_m) sono distanze dal punto x del sensore lungo il verso di marcia; il segno di velocita_m_s determina il verso (negativo = -x).
  • La matrice implicita è costruita e fattorizzata una volta per run (proprietà del materiale costanti, nessun coefficiente dipendente dalla temperatura). Se si aggiungono proprietà dipendenti dalla temperatura, la matrice deve essere ricostruita e rifattorizzata ad ogni passo temporale.
  • simulate.py cancella e ricrea l'intera cartella di output ad ogni esecuzione (shutil.rmtree).
  • Aggiungere un nuovo materiale richiede solo una nuova voce nel dizionario MATERIALI in materials.py; la chiave del materiale va poi impostata in FASCETTA["materiale"] in config.py.