Files
davideandClaude Opus 5 97ebab844e Esporta in CSV la temperatura simulata e la lettura del sensore
Nuova azione `python main.py csv`: esporta_csv.py integra il campo FEM e scrive
output/csv/run_XXXX.csv con la temperatura vera della parete nel punto
osservato, la lettura del sensore reale e il massimo nodale istante per
istante, più un metadata.csv con una riga per analisi. La cartella è ricreata a
ogni esecuzione.

Le righe sono campionate a FEM["frequenza_campionamento_hz"],
indipendente dal passo di integrazione, e FEM["num_run"] analisi vengono
esportate di seguito: con la randomizzazione abilitata ognuna ha parametri
diversi ma riproducibili dallo stesso seed.

simula_campo_fem accetta salva_campi=False, perché all'export servono solo le
serie scalari e non i campi nodali di ogni campione, e restituisce anche la
temperatura nodale massima per campione.

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

228 lines
13 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Analisi Termica FEM — Fascetta, Sorgenti a Induzione Mobili, Sensore IR Fisso
Questo progetto calcola il transitorio termico di una fascetta (anello cilindrico
sottile) riscaldata da un gruppo di sorgenti a induzione in movimento, osservata da un
sensore a infrarossi fisso. La fascetta è discretizzata con una mesh a elementi shell
quadrangolari e il campo di temperatura è risolto a elementi finiti, poi visualizzato in
un'animazione 3D isometrica o esportato in CSV come serie temporale (temperatura vera
della parete e lettura del sensore).
## Geometria
La fascetta è un anello cilindrico definito da tre dimensioni:
- **diametro** (default 70 mm) — il diametro esterno del cilindro;
- **spessore** (default 0.18 mm) — lo spessore della parete;
- **lunghezza** (default 100 mm) — l'estensione assiale lungo `x`.
Il dominio discretizzato è la **superficie media** del cilindro (raggio =
(diametro spessore)/2). Lo spessore non è discretizzato: è un attributo degli
elementi shell.
```text
sorgenti (induttori), in moto lungo x sulla superficie esterna
▼ ▼ ▼
┌───────────────────────────────────┐
x = 0 │ superficie media del cilindro │ x = lunghezza
└───────────────────────────────────┘
┆ linea di vista
sensore IR (fisso, x = 50 mm, theta = 0,
a 10 mm dalla parete interna)
```
- **x** = asse del cilindro, da `0` a `lunghezza`.
- **theta** = coordinata circonferenziale, con `theta = 0` sul piano del sensore;
nelle formule si usa la lunghezza d'arco `s = R·theta`.
- Coordinate globali per la visualizzazione: `x`, `y = R·sin(theta)`,
`z = R·cos(theta)`.
Il **sensore** è un pirometro a infrarossi posto all'interno della fascetta, a una
distanza configurabile dalla parete interna (default 10 mm). Essendo senza contatto, la
distanza non influenza la misura: il sensore legge la temperatura della parete nel nodo
più vicino a `x = 50 mm` sul piano `theta = 0`.
## Modello fisico
L'equazione risolta è quella del calore per unità di superficie media:
```text
rho·cp·t ∂T/∂t = k·t ∇²T + q(x, s, t) (h_est + h_int)·(T T_amb)
```
con `t` = spessore della parete e `∇²` il laplaciano nel piano `(x, s)`.
1. **Collasso dello spessore** — la temperatura è uniforme attraverso la parete. Con
spessore 0.18 mm il numero di Biot `h·t/k` vale ~1e-7 e il tempo di diffusione
attraverso la parete `t²/alpha` è di pochi millisecondi, molto più rapido del
transito delle sorgenti. Di conseguenza la profondità di penetrazione del
riscaldamento a induzione non entra nel modello: conta solo il flusso totale
assorbito per unità di superficie. La skin depth
`δ = √(2ρₑ/(ωμ))` è comunque calcolata dalla frequenza dell'induttore
(`frequenza_hz`, oppure imposta con `skin_depth_fissa_m`) e riportata come
grandezza diagnostica: per la banda stagnata a 20 kHz risulta ≈ 0.14 mm,
confrontabile con lo spessore, il che conferma che la parete è quasi isoterma
attraverso lo spessore.
2. **Sorgenti gaussiane in moto** — ogni sorgente ha un'impronta gaussiana isotropa nel
piano `(x, s)` di raggio `sigma_punto_m`. Un gruppo di `numero_sorgenti` sorgenti
equidistanti (`distanza_sorgenti_m`) si muove rigidamente a velocità costante:
`q(x, s, t) = Σᵢ q_picco · efficienza · exp(-((x - xᵢ(t))² + (s - s₀)²) / (2σ²))`.
La distanza circonferenziale è valutata sull'immagine più vicina, perché la
superficie è chiusa. L'offset `s₀` del percorso rispetto al piano del sensore è
`offset_y_percorso_m`.
3. **Scambi con l'ambiente** — convezione sulla faccia esterna (`h_esterno_W_m2K`) e su
quella interna (`h_interno_W_m2K`) su tutta la superficie, più convezione sui due
bordi anulari `x = 0` e `x = lunghezza` (`h_bordi_W_m2K`, su un'area pari a
spessore × perimetro).
4. **Temperatura iniziale** — il campo parte uniformemente alla temperatura ambiente
dell'analisi (randomizzabile).
5. **Sensore reale** — la lettura aggiunge alla temperatura vera della parete nel punto
osservato: inerzia del primo ordine (`costante_tempo_s`), rumore gaussiano
(`rumore_std_C`) e quantizzazione (`quantizzazione_C`), gli ultimi due applicati a
ogni campionamento.
6. **Randomizzazione** — con `RANDOMIZZAZIONE["abilitata"]` ogni analisi perturba
velocità, flusso di picco, sigma dello spot, offset y del percorso, temperatura
ambiente e rumore del sensore con estrazioni gaussiane/uniformi da un RNG con seed
fisso (`FEM["seed"]`): analisi diverse dello stesso setup differiscono come
differirebbero due passaggi reali, restando riproducibili.
Irraggiamento non modellato e proprietà dei materiali costanti con la temperatura: a
~210 °C le perdite radiative non sono del tutto trascurabili rispetto alla convezione, e
per gli acciai ferromagnetici la skin depth reale varia fortemente con temperatura e
campo (punto di Curie non modellato).
## Metodo numerico
- **Mesh** (`mesh.py`): griglia strutturata `n_elementi_x × n_elementi_circonferenza`
di quadrilateri a 4 nodi (default 40 × 48), chiusa lungo la circonferenza senza nodi
duplicati sulla cucitura.
- **Elementi**: shell bilineari a 4 nodi. Tutti gli elementi sono rettangoli identici
`dx × ds`, quindi le matrici di elemento (massa e rigidezza) sono calcolate una volta
sola e assemblate in forma vettorizzata. La matrice di massa di superficie è riusata
per capacità termica, convezione sulle facce e carico della sorgente; la convezione
sui bordi anulari usa una matrice di massa 1D sull'anello di nodi.
- **Integrazione temporale**: Eulero implicito con passo `FEM["dt_s"]` (default 1 ms).
Tutti i termini sono lineari e costanti, quindi la matrice di sistema è **assemblata e
fattorizzata LU una sola volta** (`scipy.sparse.linalg.splu`) in `prepara_stato_fem`;
ogni passo assembla solo il termine noto e risolve i sistemi triangolari.
- **Flusso nodale**: l'impronta gaussiana isotropa è separabile,
`q(x, s) = q_x(x) · q_s(s)`, e il fattore circonferenziale è costante nel tempo
(precalcolato). A ogni passo si valuta quindi solo il fattore assiale sui `n_x + 1`
nodi distinti in x, espanso con un prodotto esterno.
- Se in futuro si introducessero proprietà dipendenti dalla temperatura, la matrice
andrebbe ricostruita e rifattorizzata a ogni passo.
## File
```text
main.py punto di ingresso da riga di comando
config.py tutti i parametri (geometria, mesh, FEM, aria, sorgente, sensore)
materials.py proprietà termofisiche ed elettriche dei materiali
mesh.py generazione della mesh a elementi shell
fem.py solutore termico transitorio a elementi finiti
plot_mesh.py disegno della sola mesh
plot_animazione_fem.py animazione 3D del campo di temperatura
esporta_csv.py export CSV della serie temporale
output/ immagini e GIF salvate quando il backend non è interattivo
output/csv/ CSV esportati (ricreata a ogni esecuzione)
```
## Installazione
```bash
python -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
```
## Uso
```bash
python main.py # elenco delle azioni disponibili
python main.py mesh # disegna la sola mesh a elementi shell
python main.py fem # integra il campo FEM e lo anima in 3D
python main.py csv # esporta la serie temporale in output/csv/
```
Ogni modulo resta eseguibile anche direttamente (`python plot_mesh.py`,
`python plot_animazione_fem.py`, `python esporta_csv.py`).
Gli script aprono finestre interattive (backend Qt); se il backend non è interattivo
(es. sessione senza display) salvano automaticamente PNG/GIF in `output/`.
L'animazione FEM mostra due pannelli: la fascetta in vista isometrica con il campo di
temperatura dipinto sugli elementi shell e i marker delle sorgenti in transito, e la
storia della temperatura nel punto osservato dal sensore (valore nodale vero e lettura
del sensore reale). Finestra temporale iniziale e cadenza dei fotogrammi si
regolano con le costanti in testa a `plot_animazione_fem.py`; la durata simulata è
`FEM["durata_s"]`.
## Output CSV
`python main.py csv` cancella e ricrea `output/csv/`, poi esporta `FEM["num_run"]` analisi
(una per file `run_XXXX.csv`) più un `metadata.csv` con una riga per analisi. Le righe
sono campionate a `FEM["frequenza_campionamento_hz"]`, indipendente dal passo di
integrazione.
### `output/csv/run_XXXX.csv` — serie temporale
| Colonna | Significato |
|-------------------------|-----------------------------------------------------------------|
| `id_run` | identificativo dell'analisi |
| `tempo_s` | tempo simulato |
| `x_sorgente_m` | posizione della sorgente di riferimento del gruppo |
| `T_vera_sensore_C` | temperatura vera della parete nel punto osservato (valore nodale FEM) |
| `T_misurata_sensore_C` | lettura del sensore (inerzia + rumore + quantizzazione) |
| `T_massima_fascetta_C` | temperatura nodale massima su tutta la fascetta in quell'istante |
| `T_ambiente_C` | temperatura ambiente dell'analisi |
| `offset_y_sorgente_m`, `velocita_m_s`, `sigma_punto_m`, `flusso_picco_W_m2` | parametri effettivi dell'analisi (variano se la randomizzazione è attiva) |
| `skin_depth_m` | skin depth diagnostica |
| `materiale` | chiave del materiale |
### `output/csv/metadata.csv` — una riga per analisi
Tutti i parametri effettivi (geometria, mesh, coefficienti di scambio, sorgenti, sensore)
e i valori di picco: `T_vera_max_sensore_C`, `T_misurata_max_sensore_C`,
`T_massima_fascetta_C`.
## Configurazione
Tutto si modifica in `config.py`. I dizionari principali:
| Dizionario | Contenuto |
|-------------|---------------------------------------------------------------------------|
| `FASCETTA` | diametro, lunghezza, spessore, materiale |
| `MESH` | numero di elementi shell lungo x e lungo la circonferenza |
| `FEM` | passo temporale, durata, seed, campionamento CSV, numero di analisi |
| `ARIA` | temperatura ambiente e coefficienti di convezione (facce e bordi) |
| `SORGENTE` | corsa, velocità (il segno dà il verso), gruppo di sorgenti, gaussiana, flusso, frequenza |
| `SENSORE` | posizione (x e distanza dalla parete), inerzia, rumore, quantizzazione |
| `RANDOMIZZAZIONE` | entità delle perturbazioni per analisi |
| `USCITA` | cartella per immagini e animazioni salvate |
Punti da conoscere:
- **Corsa delle sorgenti**: `x_inizio_m` è la distanza dal sensore, all'inizio della
corsa, della sorgente più avanzata (quella che lo raggiunge per prima);
`x_fine_m` è la distanza dal sensore, a fine corsa, della sorgente più arretrata
(quella che lo supera per ultima). Con `zero_dopo_fine` ogni sorgente si spegne
alla fine della propria corsa.
- **Materiale**: `FASCETTA["materiale"]` deve essere una chiave di `MATERIALI` in
`materials.py`. Per aggiungere un materiale basta una nuova voce nel dizionario
(conducibilità termica, densità, calore specifico, resistività elettrica,
permeabilità relativa).
- **Proprietà di `banda_stagnata`**: la banda stagnata è un nastro di acciaio a basso
tenore di carbonio (0,150,25% C) rivestito su entrambe le facce da un sottile
strato di stagno elettrolitico, dello spessore di pochi micrometri — trascurabile
rispetto allo spessore tipico della parete (es. 0,18 mm) e quindi ininfluente sulle
proprietà termiche, elettriche e magnetiche in massa. I valori in `materials.py` sono
quindi quelli dell'acciaio dolce sottostante:
[Banda stagnata: tutto quello che c'è da sapere (MUNDOLATAS)](https://mundolatas.com/it/banda-stagnata-tutto-quello-che-ce-da-sapere/),
[Bande stagnate elettrolitiche (EUROPERF)](https://www.europerf.it/it/banda-stagnata-elettrolitica.php),
[Differenza tra banda stagnata e acciaio inossidabile (Wuxi Bright Packing)](https://it.brightmetalcan.com/info/difference-between-tinplate-and-stainless-stee-48700260.html).