Files

77 lines
11 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.
# Assunzioni
Ogni assunzione ha un identificativo (A1, A2, …) richiamato nel codice e negli altri documenti. Per ognuna: cosa si assume, perché è
ragionevole, **dove** il codice ne dipende e **cosa succede se è falsa**. Le assunzioni sul segnale valgono per i dati del generatore
`extern/gen_data` (sezione finale): su dati reali vanno verificate.
## Dati
| ID | Assunzione | Dove | Se è falsa |
|---|---|---|---|
| **A1** | Il tempo è **strettamente crescente**, anche non uniforme (jitter del campionamento) | `dati.leggi_csv` | il CSV è rifiutato (exit 4). Il programma non riordina né deduplica |
| **A2** | Il CSV ha l'intestazione e tutte le colonne richieste da `[dati]` (tempo e serie), tutte numeriche, senza celle vuote | `dati.leggi_csv` | exit 4 con il nome della colonna mancante |
| **A3** | Il tempo è in `ms` o `s` come dichiarato in `[dati] unita_tempo` | `dati.leggi_csv` | tutti i tempi e le costanti $\tau$ risultano scalati di 1000 senza avvisi: controlla l'unità |
| **A4** | Le serie condividono gli **stessi domini** (stessi istanti di rottura) | `analisi.analizza_domini`, `domini.proponi` | i domini sono cercati solo su `serie_riferimento` (default `T_C`); una serie con gradini diversi non è gestita: si lancia l'analisi due volte |
## Struttura del segnale
| ID | Assunzione | Dove | Se è falsa |
|---|---|---|---|
| **A5** | I **gradini di riscaldamento** sono salite positive molto più grandi del rumore **e** degli scarti casuali: la **salita complessiva** dei campioni consecutivi in salita supera `soglia_gradino_c` (20 °C), mentre uno scarto casuale è ≤ 4 °C | `domini.trova_gradini` | gradini con salita complessiva sotto la soglia non sono visti (il segnale resta un unico tratto); scarti consecutivi che sommati superano la soglia sono scambiati per gradini. Soluzione: adattare la soglia o correggere `domini.toml` a mano. La soglia è un valore fisso, non derivato dai dati: misure e alternative in [limiti.md](limiti.md) |
| **A6** | La **salita dura pochi campioni** (2–4): la costante di tempo del sensore ($\tau_\text{sensore} \approx 0.04$–$0.12$ s) è dell'ordine dell'intervallo di campionamento (≈ 0.15 s), quindi un gradino si distribuisce su 2–4 campioni, anche ognuno sotto la soglia | `domini.trova_gradini` (campioni in salita raggruppati, soglia sulla salita complessiva), `analisi._gradino` | con un sensore più lento la salita avrebbe più campioni: sarebbe comunque raggruppata finché ogni salto supera `soglia_bordo_sigma`$\cdot\sigma_d$ e i campioni in salita distano meno di `distanza_min_salti_campioni`, ma non sarebbe fittata |
| **A7** | Dopo ogni gradino la temperatura **decade esponenzialmente** verso un asintoto: $y = c + A\,e^{-t'/\tau}$ | `modelli.MODELLI["esp_decad"]` | residui sistematici, `ρ₁` alto, «modello non adeguato». Con più processi di raffreddamento servirebbe un modello a più esponenziali (non presente, vedi [limiti.md](limiti.md)) |
| **A8** | Tutti i raffreddamenti hanno la **stessa costante di tempo $\tau$ e lo stesso asintoto $c$** (stesso processo fisico: dispersione verso l'ambiente). Anche nel generatore il decadimento dopo più gradini è la somma di esponenziali con lo stesso $k$, quindi un'unica esponenziale | `analisi.analizza_domini` (parametri `raffreddamento_intermedio_fissa_da_finale`) | gli intermedi sono forzati a $\tau$ e $c$ del finale: se in realtà differiscono, il fit dell'ampiezza $A$ è distorto e i residui lo mostrano. Per rimuovere il vincolo: `raffreddamento_intermedio_fissa_da_finale = []` |
| **A9** | Il **raffreddamento finale** è l'ultimo dominio ed è abbastanza lungo (centinaia di campioni) da stimare $c$, $A$, $\tau$ **liberamente**; i raffreddamenti intermedi, anche di 4–6 campioni, bastano per stimare la sola $A$ con $c$ e $\tau$ fissi (`n_min_punti_parametri_fissi`) | `analisi.analizza_domini`, `analisi._serie` | se il CSV finisce subito dopo un gradino, il finale è corto e $\tau$ può risultare non identificabile; allora anche gli intermedi restano liberi (con `n_min_punti`) e possono essere indeterminati. Tratti intermedi con meno di 4 campioni (gradini a meno di ≈ 1.2 s) restano indeterminati |
| **A10** | Il **riposo** (prima del primo gradino) è una baseline costante, lineare o esponenziale lenta (l'ambiente che si assesta) | `[fit.modelli_per_tipo] riposo` | il modello scelto è solo descrittivo: con una deriva lenta vince `lineare` o `esp_decad` |
| **A11** | Ogni dominio è **indipendente**: nessun vincolo di continuità tra domini | `analisi` | i modelli adiacenti possono non combaciare ai confini; il «salto» tra due raffreddamenti è il gradino |
| **A12** | Non esistono gradini **discendenti** (un calo brusco non è un evento): si cercano solo salti positivi | `domini.trova_gradini` (`d > soglia`) | un calo brusco resterebbe dentro un raffreddamento, aumentando RMSE e $\rho_1$ |
| **A25** | Il **ritardo di risposta del sensore** ($\tau_\text{sensore} \approx 0.04$–$0.12$ s) è trascurabile rispetto a $\tau$ del raffreddamento: il modello `esp_decad` non lo include | `modelli.MODELLI` (`esp_decad`) | un esponenziale filtrato da un filtro del primo ordine ha lo stesso $\tau$ ma ampiezza moltiplicata per $1/(1-\tau_\text{sensore}/\tau)$: $A$ risulta sovrastimata di circa $\tau_\text{sensore}/\tau \approx 0.5$ % (misurato: +0.45 % in media su 60 seed). $\tau$ non è affetta |
## Rumore
| ID | Assunzione | Dove | Se è falsa |
|---|---|---|---|
| **A13** | Il rumore è circa **gaussiano bianco**, con $\sigma$ stimabile per dominio da $\sigma = 1.4826\,\mathrm{MAD}(\Delta^2 y)/\sqrt{6}$ **su almeno 8 campioni**: con meno, la MAD di pochi $\Delta^2 y$ quantizzati può annullarsi | `domini.sigma_rumore` (`N_MIN_SIGMA`) | $\sigma$ sottostimata: l'adeguatezza è valutata male. Sotto 8 campioni $\sigma$ è «non stimabile» (`null`): scala della perdita 1, adeguatezza non valutabile. La stima ignora la parte lenta del rumore (vedi A14) |
| **A14** | Sul rumore bianco si aggiungono **disturbi che il modello non descrive**: deriva lenta (Ornstein–Uhlenbeck, $\tau \approx 3.5$ s) e **scarti casuali** (±4 °C, rientro < 1 s) | `fit.adatta` (perdita robusta), `uscita` (residui) | sono trattati come outlier dalla perdita `soft_l1` (non spostano molto il fit) ma lasciano residui autocorrelati ($\rho_1 \approx 0.7$) e intervalli di confidenza troppo stretti. Non è un errore del fit |
| **A15** | La **quantizzazione** (T a 0.01 °C, ambiente a 0.1 °C) è trascurabile rispetto al rumore | — | nessun trattamento specifico |
## Statistica
| ID | Assunzione | Dove | Se è falsa |
|---|---|---|---|
| **A16** | Gli intervalli al 95 % sono **gaussiani asintotici**: $\pm 1.96\,\sigma_p$ con $\mathrm{cov} = s^2 (J^\top J)^{-1}$, $s^2 = \mathrm{RSS}/(n-k)$ | `fit.adatta` | con pochi campioni o residui non gaussiani sono approssimati |
| **A17** | L'autocorrelazione dei residui è di tipo AR(1): gli errori si allargano di $\sqrt{(1+\rho_1)/(1-\rho_1)}$ se $\rho_1$ supera `soglia_acf` | `fit.adatta` | **approssimazione insufficiente**: su 60 seed il $\tau$ vero cade nell'intervallo al 95 % nel 73 % dei casi. La deviazione standard dell'errore su $\tau$ è 3.7 volte l'errore standard non corretto, perché gli scarti casuali sono concentrati all'inizio del raffreddamento (dove la sensibilità a $\tau$ è massima), quindi non stazionari. Provate e scartate: correzione a più lag di Bartlett (77 %) e stimatore sandwich HAC (≤ 62 %). Vedi [limiti.md](limiti.md) |
| **A18** | Per i parametri **fissi** l'incertezza non si propaga: gli intervalli di $A$ nei raffreddamenti intermedi sono **condizionati** a $c$ e $\tau$ del finale | `fit.adatta`, `uscita._params` | sono sottostimati rispetto all'incertezza totale |
| **A19** | AICc e BIC si calcolano da $\mathrm{RSS}$ come se i residui fossero gaussiani, anche se il fit usa la perdita robusta | `fit.adatta` | confronto tra modelli approssimato; sensibile agli outlier |
| **A20** | Se $\sigma$ non è stimabile (meno di 8 campioni, o nulla) la scala della perdita robusta è 1 e l'adeguatezza non è valutabile | `fit.adatta`, `analisi.scegli` | solo robustezza ridotta; il fit resta valido |
## Progetto
| ID | Assunzione | Dove |
|---|---|---|
| **A21** | La **suddivisione in domini la decide l'utente**: la proposta automatica è modificabile (`domini.toml`) e il fit non la cambia mai | `cli._passo_domini`, `domini.leggi_toml` |
| **A22** | Il programma è **deterministico**: nessuna partenza casuale, nessun seme | `fit._partenza` (griglia su $\tau$, minimi quadrati lineari) |
| **A23** | Il **riscaldamento non si fitta** (2–4 campioni). Il salto $\Delta$ si stima nell'ordine da: modelli dei domini adiacenti valutati all'istante del gradino; misura riportata a quell'istante con $c$ e $\tau$ del raffreddamento finale; misura sui campioni ($y_\text{fine salita} - y_\text{prima}$), che è per difetto (fino a ≈ 10–20 %: parte della salita cade sul campione precedente e l'ultimo campione è già decaduto) | `analisi._gradino`, `analisi._affina_salti` |
| **A24** | I **modelli provati** sono fissati per tipo di dominio in configurazione, non scelti dal programma | `[fit.modelli_per_tipo]` |
## Origine dei dati di prova (`extern/gen_data`)
I CSV di prova si generano con `extern/gen_data`, che a ogni corsa (seed) **sorteggia** i parametri da intervalli: ogni seed è un segnale diverso
(dettaglio: `extern/gen_data/docs/SPEC.md` e `extern/gen_data/config.toml`). Gli intervalli che fondano le assunzioni sopra:
| Aspetto | Valore nel generatore | Assunzioni |
|---|---|---|
| intervallo di campionamento | 150.5 ms con jitter ±0.6 ms (fisso) | A1, A6 |
| numero e istanti dei gradini | 1–3; il primo a 8–25 s, distanza tra gradini 1–6 s | A5, A9 |
| ampiezza dei gradini | 40–90 °C | A5, A7 |
| raffreddamento | $k = 0.05$–$0.09\ \mathrm{s^{-1}}$, cioè $ au = 1/k = 11$–$20$ s | A7, A8 |
| risposta del sensore | filtro del primo ordine, $ au_ ext{sensore} = 0.04$–$0.12$ s | A6 |
| rumore bianco | $\sigma = 0.06$–$0.16$ °C a freddo, $0.15$–$0.30$ a caldo | A13 |
| deriva lenta | Ornstein–Uhlenbeck, $\sigma = 0.02$–$0.08$ °C, $ au = 2$–$6$ s | A14 |
| scarti casuali | in riscaldamento: 0.4–1.2 Hz, $\sigma$ 1.5–3.5 °C, rientro 0.3 s; in raffreddamento: 0.1–0.5 Hz, media −1 °C, $\sigma$ 1.5–3.5 °C, rientro 0.7 s; ampiezza ≤ 4 °C | A5, A14 |
| ambiente | 20–26 °C a regime, deriva iniziale ±0.8 °C con $ au$ 30–90 s; non sale quando T è alta | A10; $c$ di `dT_C` ≈ 0 |
| `dT_C` | $T - T_\text{amb}$ calcolato dopo rumore e quantizzazione di entrambi i canali | A4 |
Poiché il sorteggio è riproducibile dal seed (`genera.sorteggia`), per ogni seed si conoscono i **valori veri** (gradini, ampiezze, $\tau$): il test
`tests/test_validazione.py` li confronta con i risultati di `fitseg` su 20 seed ([test.md](test.md)).