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>
This commit is contained in:
@@ -21,9 +21,10 @@ pip install -r requirements.txt
|
|||||||
# Punto di ingresso unico: `python main.py` elenca le azioni disponibili
|
# 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 mesh # disegna la sola mesh a elementi shell
|
||||||
python main.py fem # animazione 3D del campo FEM sulla mesh shell
|
python main.py fem # animazione 3D del campo FEM sulla mesh shell
|
||||||
|
python main.py csv # esporta output/csv/run_XXXX.csv + metadata.csv
|
||||||
```
|
```
|
||||||
|
|
||||||
Ogni modulo resta eseguibile anche direttamente (`python plot_mesh.py`, `python plot_animazione_fem.py`).
|
Ogni modulo resta eseguibile anche direttamente (`python plot_mesh.py`, `python plot_animazione_fem.py`, `python esporta_csv.py`).
|
||||||
|
|
||||||
Attivare sempre il venv (`source .venv/bin/activate`) prima di eseguire qualsiasi comando Python.
|
Attivare sempre il venv (`source .venv/bin/activate`) prima di eseguire qualsiasi comando Python.
|
||||||
|
|
||||||
@@ -37,11 +38,11 @@ Analisi termica transitoria a elementi finiti di una fascetta (anello cilindrico
|
|||||||
|
|
||||||
**Flusso dei dati:**
|
**Flusso dei dati:**
|
||||||
|
|
||||||
1. `main.py` — dispatcher da riga di comando che seleziona l'azione (mesh, fem)
|
1. `main.py` — dispatcher da riga di comando che seleziona l'azione (mesh, fem, csv)
|
||||||
2. `config.py` — tutti i parametri configurabili (dizionari FASCETTA, MESH, FEM, ARIA, SORGENTE, SENSORE, RANDOMIZZAZIONE, USCITA)
|
2. `config.py` — tutti i parametri configurabili (dizionari FASCETTA, MESH, FEM, ARIA, SORGENTE, SENSORE, RANDOMIZZAZIONE, USCITA)
|
||||||
3. `materials.py` — dizionario MATERIALI con proprietà termofisiche ed elettriche per materiale
|
3. `materials.py` — dizionario MATERIALI con proprietà termofisiche ed elettriche per materiale
|
||||||
4. `mesh.py` — mesh a elementi shell quadrangolari della superficie media del cilindro, visualizzata da `plot_mesh.py`
|
4. `mesh.py` — mesh a elementi shell quadrangolari della superficie media del cilindro, visualizzata da `plot_mesh.py`
|
||||||
5. `fem.py` — solutore termico transitorio a elementi finiti sulla mesh shell, animato da `plot_animazione_fem.py`
|
5. `fem.py` — solutore termico transitorio a elementi finiti sulla mesh shell, animato da `plot_animazione_fem.py` ed esportato in CSV da `esporta_csv.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 coordinate globali x = asse, y = R·sin(theta), z = R·cos(theta), con theta = 0 sul piano del sensore.
|
**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 coordinate globali x = asse, y = R·sin(theta), z = R·cos(theta), con theta = 0 sul piano del sensore.
|
||||||
|
|
||||||
@@ -49,6 +50,8 @@ Analisi termica transitoria a elementi finiti di una fascetta (anello cilindrico
|
|||||||
|
|
||||||
**Sensore e randomizzazione:** il campo nodale è la temperatura vera della parete; `simula_campo_fem` restituisce anche `T_lette`, la lettura del sensore degradata da inerzia del primo ordine (a ogni passo dt), rumore gaussiano e quantizzazione (a ogni campionamento di frame). `configurazione_randomizzata` perturba velocità, flusso di picco, sigma dello spot, offset y, temperatura ambiente e rumore del sensore con un RNG con seed `FEM["seed"]`, così due analisi restano riproducibili ma diverse; con `RANDOMIZZAZIONE["abilitata"] = False` restituisce i valori nominali di `config.py`.
|
**Sensore e randomizzazione:** il campo nodale è la temperatura vera della parete; `simula_campo_fem` restituisce anche `T_lette`, la lettura del sensore degradata da inerzia del primo ordine (a ogni passo dt), rumore gaussiano e quantizzazione (a ogni campionamento di frame). `configurazione_randomizzata` perturba velocità, flusso di picco, sigma dello spot, offset y, temperatura ambiente e rumore del sensore con un RNG con seed `FEM["seed"]`, così due analisi restano riproducibili ma diverse; con `RANDOMIZZAZIONE["abilitata"] = False` restituisce i valori nominali di `config.py`.
|
||||||
|
|
||||||
|
**Export CSV (`esporta_csv.py`):** `FEM["num_run"]` analisi (una per file `output/csv/run_XXXX.csv`), righe campionate a `FEM["frequenza_campionamento_hz"]` indipendentemente da `FEM["dt_s"]`. Colonne: `id_run, tempo_s, x_sorgente_m, T_vera_sensore_C, T_misurata_sensore_C, T_massima_fascetta_C, T_ambiente_C, offset_y_sorgente_m, velocita_m_s, sigma_punto_m, flusso_picco_W_m2, skin_depth_m, materiale`. `metadata.csv` ha una riga per analisi con tutti i parametri e i picchi. L'export chiama `simula_campo_fem(salva_campi=False)`: usa solo le serie scalari, i campi nodali non vengono accumulati. La cartella `output/csv` è cancellata e ricreata a ogni esecuzione (`shutil.rmtree`).
|
||||||
|
|
||||||
## Convenzioni su `config.py`
|
## Convenzioni su `config.py`
|
||||||
|
|
||||||
Ogni parametro in [config.py](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.
|
Ogni parametro in [config.py](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.
|
||||||
|
|||||||
@@ -4,7 +4,8 @@ Questo progetto calcola il transitorio termico di una fascetta (anello cilindric
|
|||||||
sottile) riscaldata da un gruppo di sorgenti a induzione in movimento, osservata da un
|
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
|
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
|
quadrangolari e il campo di temperatura è risolto a elementi finiti, poi visualizzato in
|
||||||
un'animazione 3D isometrica.
|
un'animazione 3D isometrica o esportato in CSV come serie temporale (temperatura vera
|
||||||
|
della parete e lettura del sensore).
|
||||||
|
|
||||||
## Geometria
|
## Geometria
|
||||||
|
|
||||||
@@ -126,7 +127,9 @@ mesh.py generazione della mesh a elementi shell
|
|||||||
fem.py solutore termico transitorio a elementi finiti
|
fem.py solutore termico transitorio a elementi finiti
|
||||||
plot_mesh.py disegno della sola mesh
|
plot_mesh.py disegno della sola mesh
|
||||||
plot_animazione_fem.py animazione 3D del campo di temperatura
|
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/ immagini e GIF salvate quando il backend non è interattivo
|
||||||
|
output/csv/ CSV esportati (ricreata a ogni esecuzione)
|
||||||
```
|
```
|
||||||
|
|
||||||
## Installazione
|
## Installazione
|
||||||
@@ -143,10 +146,11 @@ pip install -r requirements.txt
|
|||||||
python main.py # elenco delle azioni disponibili
|
python main.py # elenco delle azioni disponibili
|
||||||
python main.py mesh # disegna la sola mesh a elementi shell
|
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 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`,
|
Ogni modulo resta eseguibile anche direttamente (`python plot_mesh.py`,
|
||||||
`python plot_animazione_fem.py`).
|
`python plot_animazione_fem.py`, `python esporta_csv.py`).
|
||||||
|
|
||||||
Gli script aprono finestre interattive (backend Qt); se il backend non è interattivo
|
Gli script aprono finestre interattive (backend Qt); se il backend non è interattivo
|
||||||
(es. sessione senza display) salvano automaticamente PNG/GIF in `output/`.
|
(es. sessione senza display) salvano automaticamente PNG/GIF in `output/`.
|
||||||
@@ -158,6 +162,34 @@ 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 è
|
regolano con le costanti in testa a `plot_animazione_fem.py`; la durata simulata è
|
||||||
`FEM["durata_s"]`.
|
`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
|
## Configurazione
|
||||||
|
|
||||||
Tutto si modifica in `config.py`. I dizionari principali:
|
Tutto si modifica in `config.py`. I dizionari principali:
|
||||||
@@ -166,7 +198,7 @@ Tutto si modifica in `config.py`. I dizionari principali:
|
|||||||
|-------------|---------------------------------------------------------------------------|
|
|-------------|---------------------------------------------------------------------------|
|
||||||
| `FASCETTA` | diametro, lunghezza, spessore, materiale |
|
| `FASCETTA` | diametro, lunghezza, spessore, materiale |
|
||||||
| `MESH` | numero di elementi shell lungo x e lungo la circonferenza |
|
| `MESH` | numero di elementi shell lungo x e lungo la circonferenza |
|
||||||
| `FEM` | passo temporale, durata dell'analisi transitoria, seed |
|
| `FEM` | passo temporale, durata, seed, campionamento CSV, numero di analisi |
|
||||||
| `ARIA` | temperatura ambiente e coefficienti di convezione (facce e bordi) |
|
| `ARIA` | temperatura ambiente e coefficienti di convezione (facce e bordi) |
|
||||||
| `SORGENTE` | corsa, velocità (il segno dà il verso), gruppo di sorgenti, gaussiana, flusso, frequenza |
|
| `SORGENTE` | corsa, velocità (il segno dà il verso), gruppo di sorgenti, gaussiana, flusso, frequenza |
|
||||||
| `SENSORE` | posizione (x e distanza dalla parete), inerzia, rumore, quantizzazione |
|
| `SENSORE` | posizione (x e distanza dalla parete), inerzia, rumore, quantizzazione |
|
||||||
|
|||||||
@@ -56,6 +56,14 @@ FEM = {
|
|||||||
|
|
||||||
# Seed per la riproducibilità di randomizzazione e rumore del sensore.
|
# Seed per la riproducibilità di randomizzazione e rumore del sensore.
|
||||||
"seed": 42,
|
"seed": 42,
|
||||||
|
|
||||||
|
# Frequenza di campionamento delle righe del CSV esportato.
|
||||||
|
# Esempio: 10 Hz significa una riga ogni 0.1 s.
|
||||||
|
"frequenza_campionamento_hz": 10.0,
|
||||||
|
|
||||||
|
# Numero di analisi da esportare in CSV, una per file.
|
||||||
|
# Con la randomizzazione abilitata ogni analisi ha parametri diversi.
|
||||||
|
"num_run": 1,
|
||||||
}
|
}
|
||||||
|
|
||||||
ARIA = {
|
ARIA = {
|
||||||
|
|||||||
+153
@@ -0,0 +1,153 @@
|
|||||||
|
# Export CSV della serie temporale calcolata dal solutore FEM (fem.py).
|
||||||
|
#
|
||||||
|
# Per ogni analisi viene scritto un file run_XXXX.csv con la temperatura vera
|
||||||
|
# della parete nel punto osservato dal sensore e la lettura del sensore reale
|
||||||
|
# (inerzia, rumore, quantizzazione), più i parametri effettivi dell'analisi.
|
||||||
|
# `metadata.csv` riassume un'analisi per riga.
|
||||||
|
#
|
||||||
|
# Le righe sono campionate a FEM["frequenza_campionamento_hz"], indipendente
|
||||||
|
# dal passo di integrazione FEM["dt_s"]. I campi nodali non vengono accumulati:
|
||||||
|
# servono solo le serie scalari.
|
||||||
|
|
||||||
|
import csv
|
||||||
|
import random
|
||||||
|
import shutil
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
from config import FEM, USCITA
|
||||||
|
from fem import configurazione_randomizzata, simula_campo_fem
|
||||||
|
from mesh import genera_mesh, riepilogo_mesh
|
||||||
|
|
||||||
|
INTESTAZIONE = [
|
||||||
|
"id_run",
|
||||||
|
"tempo_s",
|
||||||
|
"x_sorgente_m",
|
||||||
|
"T_vera_sensore_C",
|
||||||
|
"T_misurata_sensore_C",
|
||||||
|
"T_massima_fascetta_C",
|
||||||
|
"T_ambiente_C",
|
||||||
|
"offset_y_sorgente_m",
|
||||||
|
"velocita_m_s",
|
||||||
|
"sigma_punto_m",
|
||||||
|
"flusso_picco_W_m2",
|
||||||
|
"skin_depth_m",
|
||||||
|
"materiale",
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
def esporta_run(
|
||||||
|
id_run: str,
|
||||||
|
percorso_csv: Path,
|
||||||
|
mesh_dati: dict,
|
||||||
|
rng: random.Random,
|
||||||
|
) -> dict:
|
||||||
|
"""Integra un'analisi FEM, ne scrive il CSV e restituisce i suoi metadati."""
|
||||||
|
cfg = configurazione_randomizzata(rng)
|
||||||
|
dati = simula_campo_fem(
|
||||||
|
dt_frame_s=1.0 / FEM["frequenza_campionamento_hz"],
|
||||||
|
cfg=cfg,
|
||||||
|
mesh_dati=mesh_dati,
|
||||||
|
rng=rng,
|
||||||
|
salva_campi=False,
|
||||||
|
)
|
||||||
|
|
||||||
|
fascetta = cfg["fascetta"]
|
||||||
|
aria = cfg["aria"]
|
||||||
|
sorgente = cfg["sorgente"]
|
||||||
|
sensore = cfg["sensore"]
|
||||||
|
skin_depth = dati["skin_depth_m"]
|
||||||
|
|
||||||
|
percorso_csv.parent.mkdir(parents=True, exist_ok=True)
|
||||||
|
with percorso_csv.open("w", newline="") as f:
|
||||||
|
writer = csv.writer(f)
|
||||||
|
writer.writerow(INTESTAZIONE)
|
||||||
|
for k, t in enumerate(dati["tempi"]):
|
||||||
|
writer.writerow([
|
||||||
|
id_run,
|
||||||
|
f"{t:.6f}",
|
||||||
|
f"{dati['x_riferimenti'][k]:.9f}",
|
||||||
|
f"{dati['T_vere'][k]:.6f}",
|
||||||
|
f"{dati['T_lette'][k]:.6f}",
|
||||||
|
f"{dati['T_massime'][k]:.6f}",
|
||||||
|
f"{aria['temperatura_ambiente_C']:.6f}",
|
||||||
|
f"{sorgente['offset_y_percorso_m']:.9f}",
|
||||||
|
f"{sorgente['velocita_m_s']:.9f}",
|
||||||
|
f"{sorgente['sigma_punto_m']:.9f}",
|
||||||
|
f"{sorgente['flusso_termico_picco_W_m2']:.6f}",
|
||||||
|
f"{skin_depth:.9e}",
|
||||||
|
fascetta["materiale"],
|
||||||
|
])
|
||||||
|
|
||||||
|
return {
|
||||||
|
"id_run": id_run,
|
||||||
|
"file_csv": percorso_csv.name,
|
||||||
|
"materiale": fascetta["materiale"],
|
||||||
|
"diametro_m": fascetta["diametro_mm"] / 1000.0,
|
||||||
|
"lunghezza_m": fascetta["lunghezza_mm"] / 1000.0,
|
||||||
|
"spessore_m": fascetta["spessore_mm"] / 1000.0,
|
||||||
|
"n_elementi_x": mesh_dati["n_elementi_x"],
|
||||||
|
"n_elementi_circonferenza": mesh_dati["n_elementi_circonferenza"],
|
||||||
|
"durata_s": FEM["durata_s"],
|
||||||
|
"dt_s": FEM["dt_s"],
|
||||||
|
"frequenza_campionamento_hz": FEM["frequenza_campionamento_hz"],
|
||||||
|
"temperatura_ambiente_C": aria["temperatura_ambiente_C"],
|
||||||
|
"h_esterno_W_m2K": aria["h_esterno_W_m2K"],
|
||||||
|
"h_interno_W_m2K": aria["h_interno_W_m2K"],
|
||||||
|
"h_bordi_W_m2K": aria["h_bordi_W_m2K"],
|
||||||
|
"x_inizio_m": sorgente["x_inizio_m"],
|
||||||
|
"x_fine_m": sorgente["x_fine_m"],
|
||||||
|
"x_sensore_m": dati["stato"]["x_sensore_m"],
|
||||||
|
"distanza_sensore_parete_m": sensore["distanza_parete_mm"] / 1000.0,
|
||||||
|
"offset_y_percorso_m": sorgente["offset_y_percorso_m"],
|
||||||
|
"velocita_m_s": sorgente["velocita_m_s"],
|
||||||
|
"numero_sorgenti": sorgente.get("numero_sorgenti", 1),
|
||||||
|
"distanza_sorgenti_m": sorgente.get("distanza_sorgenti_m", 0.0),
|
||||||
|
"sigma_punto_m": sorgente["sigma_punto_m"],
|
||||||
|
"flusso_termico_picco_W_m2": sorgente["flusso_termico_picco_W_m2"],
|
||||||
|
"efficienza_riscaldamento": sorgente["efficienza_riscaldamento"],
|
||||||
|
"frequenza_hz": sorgente["frequenza_hz"],
|
||||||
|
"skin_depth_m": skin_depth,
|
||||||
|
"costante_tempo_sensore_s": sensore["costante_tempo_s"],
|
||||||
|
"rumore_std_sensore_C": sensore["rumore_std_C"],
|
||||||
|
"quantizzazione_sensore_C": sensore["quantizzazione_C"],
|
||||||
|
"T_vera_max_sensore_C": float(dati["T_vere"].max()),
|
||||||
|
"T_misurata_max_sensore_C": float(dati["T_lette"].max()),
|
||||||
|
"T_massima_fascetta_C": float(dati["T_massime"].max()),
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def main() -> None:
|
||||||
|
mesh_dati = genera_mesh()
|
||||||
|
print(riepilogo_mesh(mesh_dati))
|
||||||
|
|
||||||
|
# La cartella dei CSV è ricreata da zero a ogni esecuzione, così non
|
||||||
|
# restano run di esecuzioni precedenti con parametri diversi.
|
||||||
|
cartella = Path(USCITA["cartella"]) / "csv"
|
||||||
|
if cartella.exists():
|
||||||
|
shutil.rmtree(cartella)
|
||||||
|
cartella.mkdir(parents=True, exist_ok=True)
|
||||||
|
|
||||||
|
rng = random.Random(FEM["seed"])
|
||||||
|
righe_metadata = []
|
||||||
|
for indice in range(1, FEM["num_run"] + 1):
|
||||||
|
id_run = f"run_{indice:04d}"
|
||||||
|
print(f"Integrazione FEM di {id_run} in corso...")
|
||||||
|
riga = esporta_run(id_run, cartella / f"{id_run}.csv", mesh_dati, rng)
|
||||||
|
print(
|
||||||
|
f" T vera max al sensore: {riga['T_vera_max_sensore_C']:.1f} °C — "
|
||||||
|
f"T misurata max: {riga['T_misurata_max_sensore_C']:.1f} °C"
|
||||||
|
)
|
||||||
|
righe_metadata.append(riga)
|
||||||
|
|
||||||
|
percorso_metadata = cartella / "metadata.csv"
|
||||||
|
with percorso_metadata.open("w", newline="") as f:
|
||||||
|
writer = csv.DictWriter(f, fieldnames=list(righe_metadata[0].keys()))
|
||||||
|
writer.writeheader()
|
||||||
|
writer.writerows(righe_metadata)
|
||||||
|
|
||||||
|
print(f"Esportate {len(righe_metadata)} analisi in: {cartella.resolve()}")
|
||||||
|
print(f"Metadata: {percorso_metadata.resolve()}")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
@@ -387,12 +387,16 @@ def simula_campo_fem(
|
|||||||
durata_s: float | None = None,
|
durata_s: float | None = None,
|
||||||
mesh_dati: dict | None = None,
|
mesh_dati: dict | None = None,
|
||||||
rng: random.Random | None = None,
|
rng: random.Random | None = None,
|
||||||
|
salva_campi: bool = True,
|
||||||
) -> dict:
|
) -> dict:
|
||||||
"""Integra il campo FEM fino a durata_s, salvando un campo ogni dt_frame_s.
|
"""Integra il campo FEM fino a durata_s, campionando ogni dt_frame_s.
|
||||||
|
|
||||||
Il campo nodale è la temperatura vera della parete; la lettura del sensore
|
Il campo nodale è la temperatura vera della parete; la lettura del sensore
|
||||||
ne è la versione degradata: inerzia del primo ordine, rumore gaussiano e
|
ne è la versione degradata: inerzia del primo ordine, rumore gaussiano e
|
||||||
quantizzazione, questi ultimi due applicati a ogni campionamento.
|
quantizzazione, questi ultimi due applicati a ogni campionamento.
|
||||||
|
|
||||||
|
Con `salva_campi = False` i campi nodali non vengono accumulati (utile per
|
||||||
|
l'export CSV, che usa solo le serie scalari): `campi` resta una lista vuota.
|
||||||
"""
|
"""
|
||||||
if rng is None:
|
if rng is None:
|
||||||
rng = random.Random(FEM["seed"])
|
rng = random.Random(FEM["seed"])
|
||||||
@@ -415,7 +419,7 @@ def simula_campo_fem(
|
|||||||
tau_sensore = max(sensore["costante_tempo_s"], 1e-9)
|
tau_sensore = max(sensore["costante_tempo_s"], 1e-9)
|
||||||
|
|
||||||
tempi, campi, x_riferimenti = [], [], []
|
tempi, campi, x_riferimenti = [], [], []
|
||||||
T_vere, T_lette = [], []
|
T_vere, T_lette, T_massime = [], [], []
|
||||||
|
|
||||||
prossimo_frame_t = 0.0
|
prossimo_frame_t = 0.0
|
||||||
t = 0.0
|
t = 0.0
|
||||||
@@ -430,10 +434,12 @@ def simula_campo_fem(
|
|||||||
letta = quantizza(letta, sensore["quantizzazione_C"])
|
letta = quantizza(letta, sensore["quantizzazione_C"])
|
||||||
|
|
||||||
tempi.append(t)
|
tempi.append(t)
|
||||||
campi.append(T.copy())
|
if salva_campi:
|
||||||
|
campi.append(T.copy())
|
||||||
x_riferimenti.append(x_rif)
|
x_riferimenti.append(x_rif)
|
||||||
T_vere.append(float(T[indice_sensore]))
|
T_vere.append(float(T[indice_sensore]))
|
||||||
T_lette.append(letta)
|
T_lette.append(letta)
|
||||||
|
T_massime.append(float(T.max()))
|
||||||
prossimo_frame_t += dt_frame_s
|
prossimo_frame_t += dt_frame_s
|
||||||
|
|
||||||
t += dt
|
t += dt
|
||||||
@@ -446,6 +452,8 @@ def simula_campo_fem(
|
|||||||
"x_riferimenti": np.array(x_riferimenti),
|
"x_riferimenti": np.array(x_riferimenti),
|
||||||
"T_vere": np.array(T_vere),
|
"T_vere": np.array(T_vere),
|
||||||
"T_lette": np.array(T_lette),
|
"T_lette": np.array(T_lette),
|
||||||
|
"T_massime": np.array(T_massime),
|
||||||
|
"cfg": cfg,
|
||||||
"sorgente": sorgente,
|
"sorgente": sorgente,
|
||||||
"T_ambiente_C": stato["T_ambiente_C"],
|
"T_ambiente_C": stato["T_ambiente_C"],
|
||||||
"skin_depth_m": stato["skin_depth_m"],
|
"skin_depth_m": stato["skin_depth_m"],
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
#
|
#
|
||||||
# python main.py mesh # disegna la sola mesh a elementi shell
|
# python main.py mesh # disegna la sola mesh a elementi shell
|
||||||
# python main.py fem # animazione 3D del campo FEM sulla mesh shell
|
# python main.py fem # animazione 3D del campo FEM sulla mesh shell
|
||||||
|
# python main.py csv # esporta in CSV la serie temporale del FEM
|
||||||
#
|
#
|
||||||
# Senza argomenti stampa l'elenco delle azioni disponibili.
|
# Senza argomenti stampa l'elenco delle azioni disponibili.
|
||||||
|
|
||||||
@@ -22,10 +23,17 @@ def _azione_fem() -> None:
|
|||||||
plot_animazione_fem.main()
|
plot_animazione_fem.main()
|
||||||
|
|
||||||
|
|
||||||
|
def _azione_csv() -> None:
|
||||||
|
import esporta_csv
|
||||||
|
|
||||||
|
esporta_csv.main()
|
||||||
|
|
||||||
|
|
||||||
# Chiave da riga di comando -> (funzione, descrizione mostrata nell'help).
|
# Chiave da riga di comando -> (funzione, descrizione mostrata nell'help).
|
||||||
AZIONI = {
|
AZIONI = {
|
||||||
"mesh": (_azione_mesh, "Disegna la mesh a elementi shell della fascetta"),
|
"mesh": (_azione_mesh, "Disegna la mesh a elementi shell della fascetta"),
|
||||||
"fem": (_azione_fem, "Animazione 3D del campo FEM sulla mesh a elementi shell"),
|
"fem": (_azione_fem, "Animazione 3D del campo FEM sulla mesh a elementi shell"),
|
||||||
|
"csv": (_azione_csv, "Esporta in CSV la temperatura simulata e la lettura del sensore"),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user