Files
simulatore-induttori/CLAUDE.md
T
2026-08-03 00:18:00 +02:00

7.8 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

# Generare il dataset (scrive dataset/run_XXXX.csv + dataset/metadata.csv)
python simulate.py

# Visualizzare il primo run
python plot_csv.py

# Animazione della superficie sviluppata e vista 3D isometrica
python plot_animazione.py
python plot_animazione_3d.py

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

Non sono configurati linter. La validazione del solutore si esegue a mano:

python valida_shell.py

Va rieseguita dopo ogni modifica alla fisica o all'assemblaggio: confronta le matrici e il solutore con soluzioni analitiche (raffreddamento convettivo, modi sinusoidali circonferenziali, conservazione dell'energia, potenza della sorgente, periodicità) e stampa gli errori.

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 medio, spessore e lunghezza configurabili. Il dominio simulato è l'intera superficie cilindrica, parametrizzata da x (lunghezza, le sorgenti si muovono in direzione -x) e theta (circonferenza, periodica). Lo spessore non è discretizzato: la parete è termicamente sottile, quindi faccia esterna e faccia interna hanno la stessa temperatura. Il sensore è un pirometro a infrarossi dentro la fascetta che misura la superficie interna in un punto x fisso, sulla generatrice theta = 0; l'offset circonferenziale delle sorgenti è misurato rispetto a quella generatrice.

Flusso dei dati:

  1. config.py — tutti i parametri configurabili (dizionari SIMULAZIONE, FASCETTA, ARIA, SORGENTE, SENSORE, RANDOMIZZAZIONE)
  2. materials.py — dizionario MATERIALI con proprietà termofisiche ed elettriche per materiale
  3. mesh.py — nodi, connettività Q4 e periodicità circonferenziale della shell cilindrica
  4. elementi_shell.py — funzioni di forma bilineari, quadratura di Gauss 2×2, matrici del singolo elemento
  5. assemblaggio.py — assemblaggio delle matrici globali e del vettore della sorgente
  6. sorgente.py — moto del gruppo di sorgenti e distribuzione gaussiana del flusso
  7. solutore.py — Eulero implicito e fattorizzazione LU sparsa
  8. sensore.py — interpolazione della temperatura, inerzia, rumore, quantizzazione
  9. simulate.py — motore principale: genera N run randomizzati, scrive i CSV, scrive metadata.csv
  10. plot_csv.py, plot_animazione.py, plot_animazione_3d.py — visualizzazioni autonome

Pipeline fisica in simulate.py:

  • prepara_stato_termico costruisce la mesh, assembla C, K_cond e K_conv, fattorizza LU una volta per run e prepara l'assemblatore della sorgente e l'interpolatore del sensore
  • La conduzione assiale e quella circonferenziale nascono dall'assemblaggio: elementi adiacenti condividono i nodi del bordo comune, quindi non serve alcun accoppiamento esplicito tra elementi
  • Convezione su entrambe le facce (h_esterno + h_interno su tutta l'area) e sui due bordi assiali (h_bordi · spessore lungo il perimetro), entrambe incorporate in K_conv e nel termine noto costante f_ambiente
  • Le sorgenti gaussiane in movimento producono q''(x, theta, t); la distanza circonferenziale è riavvolta su 2πR, quindi la sorgente scalda anche i punti raggiunti dall'altro verso. Il vettore di carico è ∫ N^T q'' dA, integrato con la stessa quadratura di Gauss delle matrici
  • Il flusso è separabile in un fattore assiale variabile nel tempo e uno circonferenziale costante: assembla_sorgente sfrutta la separazione, non è un'approssimazione
  • passo_termico avanza il campo risolvendo (C/dt + K) T = C/dt · T_prec + f_ambiente + f_sorgente
  • La temperatura iniziale del campo è la temperatura ambiente (randomizzata per run)
  • L'output del sensore aggiunge inerzia del primo ordine, rumore gaussiano e quantizzazione

La skin depth (calcola_skin_depth_m) e il numero di Fourier sullo spessore (numero_fourier_spessore) sono calcolati e registrati in metadata.csv come diagnostiche: non entrano nella soluzione, servono a verificare che l'ipotesi di parete termicamente sottile regga.

prepara_stato_termico e passo_termico sono condivisi con plot_animazione.py, che riproduce la fisica di run_0001 per animare la superficie, e da lì con plot_animazione_3d.py: 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 circonferenziale, 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_max_fascetta_C, T_media_fascetta_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, le dimensioni della mesh, le diagnostiche 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 è una shell con una temperatura per nodo: lo spessore non è discretizzato e la temperatura della faccia esterna coincide con quella della faccia interna. L'ipotesi regge finché numero_fourier_spessore (α · σ/|v| / spessore²) resta molto maggiore di 1; sotto ~1 servirebbe una formulazione multistrato. Con i valori di default vale circa 5.4, e il gradiente misurato nello spessore col vecchio modello 2D era di 0.002 °C al picco.
  • La mesh cilindrica è strutturata e uniforme, quindi tutti gli elementi sono lo stesso rettangolo: le matrici elementari si calcolano una volta e si replicano. Se si introducessero mesh non uniformi o spessore variabile, assemblaggio.py dovrebbe ciclare sugli elementi.
  • La periodicità circonferenziale è strutturale, non un vincolo imposto: non esiste un nodo separato per theta = 2π, la connettività riavvolge l'ultimo elemento sul primo.
  • 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.
  • La matrice è simmetrica definita positiva: splu usa permc_spec="MMD_AT_PLUS_A", che dimezza circa il riempimento rispetto al COLAMD di default.
  • 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.