Files

7.6 KiB
Raw Permalink Blame History

CLAUDE.md

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

fitseg legge un CSV t_ms,T_C,dT_C (temperatura di un sensore IR), lo divide in domini (riposo / riscaldamento = la salita rapida di un gradino, 2–4 campioni / raffreddamento = il decadimento dopo ogni gradino) e trova per ogni (dominio, serie) l'equazione che meglio lo approssima. Documentazione, commenti e messaggi sono in italiano: mantieni la lingua.

Fonti di verità (non duplicarne il contenuto): la documentazione in docs/ — assunzioni (A1–A25, richiamate nel codice), algoritmi, moduli, configurazione, output, test, limiti — e config.toml, unica fonte dei valori: nessun default nel codice, ogni chiave nuova va in CHIAVI di src/fitseg/config.py e in docs/configurazione.md. Il README è solo un indice con l'avvio rapido. Quando cambi comportamento aggiorna il file di docs corrispondente (e le assunzioni se ne cambia una).

Comandi

uv venv .venv && uv pip install -e ".[dev]"
.venv/bin/fitseg domini input/gen_7.csv                 # passo 1: output/gen_7/01_domini/ (domini.png, domini.toml), nessun fit
.venv/bin/fitseg analizza input/gen_7.csv [--domini output/gen_7/01_domini/domini.toml]   # tutti i passi → output/gen_7/{01_domini,02_sovrapposizione,03_report}
.venv/bin/pytest                                       # tutti; singolo: -k raffreddamento o tests/test_fit.py
.venv/bin/ruff check src tests
./scarica_gen_data.sh [-u]                              # clona/aggiorna gen_data in extern/ (serve a test_validazione)
./pulisci.sh [-n] [-y] [--solo-output]                  # svuota output/ e cancella input/*.csv (con conferma); -n dry-run, --solo-output lascia input/

--config/--output vanno prima del sottocomando. input/ contiene i CSV da analizzare; output/, input/*.csv e extern/ (clone di gen_data) sono ignorati da git. Ogni analisi scrive in output/<nome csv>/ (01_domini, 02_sovrapposizione, 03_report, vedi uscita.PASSI).

Architettura (src/fitseg/, un modulo per responsabilità)

cli.py (due sottocomandi, unico punto d'ingresso) → config.py, dati.py (lettura) → domini.py (gradini ⇒ domini, I/O di domini.toml) → analisi.py (ordina i domini, sceglie il modello) → fit.py (adatta) con modelli.py (registro MODELLI) → uscita.py (cartelle dei passi, grafici, report; il report incorpora le immagini con percorsi relativi ../01_domini/…, quindi i nomi dei file/cartelle vanno cambiati in modo coerente). Nel report ogni grandezza matematica è in LaTeX ($…$, $$…$$; l'equazione in MODELLI è LaTeX, i valori si formattano con uscita._num): usa \\ solo in stringhe raw e mantieni i $ bilanciati per riga (c'è un test).

  • I domini derivano solo dai gradini positivi di serie_riferimento (diff > soglia_gradino_c): la salita (estesa ai campioni adiacenti ancora in salita) è un riscaldamento, il tratto dopo è un raffreddamento; con N gradini i domini sono 1 + 2N. domini.toml è modificabile e deve essere contiguo.
  • analisi.analizza_domini: il raffreddamento finale (ultimo) si fitta per primo, libero; i raffreddamenti intermedi fissano c/tau ai suoi valori (raffreddamento_intermedio_fissa_da_finale, adatta(..., fissi=)). Il riscaldamento non si fitta: _gradino ne riporta il salto (vs["gradino"], sigma None). Modelli per tipo in [fit.modelli_per_tipo] (riposo, raffreddamento).
  • fit.adatta: least_squares con perdita robusta; l'esponenziale parte da una griglia su τ. "Non identificabile" = condizione > soglia o IC 95 % > |valore| per un parametro diverso da a/c. σ del rumore per dominio da MAD(d²y); tempo traslato a t' = t − t0.
  • Nuovo modello = voce in modelli.py + riga in docs/algoritmi.md §5.1 e in docs/moduli.md + nome in [fit.modelli_per_tipo]. Non ci sono partenze casuali: il programma è deterministico.

Test

.venv/bin/pytest (96 test, ≈ 40 s). Senza extern/gen_data i 20 di validazione sono saltati, gli altri non richiedono file esterni. Unitari su un segnale sintetico in tests/conftest.py (τ = 16 s, 3 gradini, nessun disturbo a salto); tests/test_validazione.py genera 20 seed con extern/gen_data (clone gitignorato creato da ./scarica_gen_data.sh; saltato se manca) e confronta gradini, τ, ampiezze e salti con i parametri veri sorteggiati (genera.sorteggia): è il test che protegge da regressioni sul comportamento reale. tests/test_report.py e tests/test_cli.py controllano report, grafici e CLI su CSV sintetici. gen_data sorteggia a ogni seed numero di gradini (1–3), k (τ = 11–20 s), rumore e scarti (≤ 4 °C, residui autocorrelati: limite noto, non un bug).

Gotcha

  • Il gradino si rileva sommando i campioni consecutivi in salita (soglia_bordo_sigma): la soglia soglia_gradino_c è sulla salita complessiva, non sul singolo salto (un gradino su 2 campioni può avere ogni salto sotto soglia). La salita appartiene al riscaldamento; se soglia_bordo_sigma è troppo alta un campione parziale può finire nel raffreddamento (scarta_dopo_salto_n lo esclude dal fit).
  • Raffreddamenti intermedi corti: con gli scarti casuali τ non è identificabile da solo (su dati puliti sì), quindi c e τ sono fissi ai valori del finale e basta n_min_punti_parametri_fissi (4) campioni invece di n_min_punti (6); gli IC di A sono condizionati. Senza un finale valido tornano liberi e possono risultare indeterminati.
  • Il salto Δ del riscaldamento è stimato dai modelli adiacenti (analisi._affina_salti, stima = "modelli"); se manca un modello vicino, dalla misura riportata all'istante del gradino con c e τ del finale ("misura corretta"); altrimenti dalla misura sui campioni, che è per difetto (fino a ≈ 10–20 %).
  • σ si stima con almeno 8 campioni (domini.N_MIN_SIGMA): con meno la MAD di pochi d²y quantizzati può annullarsi e la perdita robusta degenera (fit scartato come «non identificabile»). Sotto 8, sigma è null e l'adeguatezza null. risultati.json non deve mai contenere NaN (uscita._senza_nan, allow_nan=False).
  • «Adeguato» = scala robusta dei residui (1.4826·MAD) ≤ scala_residui_vs_rumore_max·σ, non la RMSE: gli scarti casuali gonfiano la RMSE ma non la scala robusta (vedi docs/algoritmi.md §6). scarta_dopo_salto_n vale 0 (la salita è già nel riscaldamento).
  • Gli intervalli di confidenza coprono il τ vero solo nel 73 % dei casi (disturbi non stazionari, concentrati a inizio raffreddamento): correzione a più lag e sandwich HAC provate e scartate (docs/limiti.md). Non cambiare lo stimatore senza rimisurare la copertura.
  • Limiti aperti (docs/limiti.md): falso «non adeguato» su ~metà dei raffreddamenti intermedi corti; soglia_gradino_c fissa (20 °C) non derivata dai dati; modello del riposo non unico (T_C e dT_C possono differire).
  • extern/gen_data non è fissato a un commit: ./scarica_gen_data.sh -u porta l'ultima versione, che può cambiare i seed. CSV di prova: (cd extern/gen_data && ../../.venv/bin/python genera.py config.toml --seed 7 -o ../../input/gen_7.csv).
  • Cambiando la config di gen_data (gradini, rumore) ricontrolla soglia_gradino_c e le tolleranze di tests/test_validazione.py.
  • Exit code: 0 ok · 2 uso · 3 config · 4 CSV · 6 domini non validi.