Domini corretti: riscaldamento = salita rapida, raffreddamento = decadimento dopo ogni gradino

- 1 + 2N domini: riposo, poi (riscaldamento, raffreddamento) per ogni gradino
- riscaldamento non fittato: descritto come salto (delta, da -> a, durata)
- raffreddamenti intermedi fissano c e tau a quelli del raffreddamento finale
- un colore per ogni raffreddamento nei grafici, zoom sui gradini
- sigma None (non NaN) nei domini non fittati: JSON valido

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
davideandClaude Sonnet 5.5 committed 2026-10-29 10:28:00 +01:00
1 parent da0964be4d
commit b49af5c112
12 files changed
+277 -142

No files matched your search

+11 -3
View File
@@ -14,13 +14,21 @@ def tipi(t, serie, cfg):
def test_tre_gradini(sintetico, cfg):
t, serie = sintetico
assert tipi(t, serie, cfg) == ["riposo", "riscaldamento", "riscaldamento", "raffreddamento"]
assert tipi(t, serie, cfg) == ["riposo", "riscaldamento", "raffreddamento", "riscaldamento", "raffreddamento",
"riscaldamento", "raffreddamento"]
def test_riscaldamento_e_la_salita_rapida(sintetico, cfg):
t, serie = sintetico
for d in domini.proponi(t, serie, cfg):
if d["tipo"] == "riscaldamento":
assert d["i1"] - d["i0"] <= 3 # pochi campioni
def test_confini_vicini_agli_istanti_dei_gradini(sintetico, cfg):
t, serie = sintetico
ds = domini.proponi(t, serie, cfg)
for d, (tj, _) in zip(ds[1:], GRADINI, strict=True):
for d, (tj, _) in zip(ds[1::2], GRADINI, strict=True): # i riscaldamenti sono i domini 2, 4, 6
assert abs(t[d["i0"]] - tj) < 0.5
@@ -38,7 +46,7 @@ def test_senza_gradini_un_solo_riposo(cfg):
def test_un_solo_gradino(cfg):
t, serie = segnale(gradini=[(20.0, 80.0)])
assert tipi(t, serie, cfg) == ["riposo", "raffreddamento"]
assert tipi(t, serie, cfg) == ["riposo", "riscaldamento", "raffreddamento"]
def test_gradino_sotto_soglia_ignorato(cfg):
+29 -18
View File
@@ -55,28 +55,39 @@ def test_tau_libero_non_identificabile_su_tratto_corto_e_rumoroso(cfg, seed):
def test_analisi_completa_sintetico(sintetico, cfg):
t, serie = sintetico
ris = analizza_domini(t, serie, domini.proponi(t, serie, cfg), cfg)
assert [r["tipo"] for r in ris] == ["riposo", "riscaldamento", "riscaldamento", "raffreddamento"]
assert [r["tipo"] for r in ris] == ["riposo", "riscaldamento", "raffreddamento", "riscaldamento", "raffreddamento",
"riscaldamento", "raffreddamento"]
for nome in serie:
assert ris[0]["serie"][nome]["scelta"] in ("costante", "lineare")
raff = next(x for x in ris[3]["serie"][nome]["fit"] if x["modello"] == "esp_decad")["parametri"]
assert raff["tau"] == pytest.approx(TAU, rel=0.03)
for r in ris[1:3]: # riscaldamento: c e τ ereditati dal raffreddamento
fit = next(x for x in r["serie"][nome]["fit"] if x["modello"] == "esp_decad")
assert r["serie"][nome]["scelta"] == "esp_decad" and fit["fissi"] == ["c", "tau"]
assert fit["parametri"]["tau"] == raff["tau"] and fit["parametri"]["c"] == raff["c"]
# ampiezza del secondo tratto = somma dei decadimenti dei gradini precedenti + il nuovo, in t'=0
t1 = ris[1]["t0_s"]
attesa = sum(a * np.exp(-(t1 - tj) / TAU) for tj, a in GRADINI[:1])
fit1 = next(x for x in ris[1]["serie"]["dT_C"]["fit"] if x["modello"] == "esp_decad")
assert fit1["parametri"]["A"] == pytest.approx(attesa, rel=0.05)
finale = next(x for x in ris[6]["serie"][nome]["fit"] if x["modello"] == "esp_decad")
assert finale["fissi"] == [] and finale["parametri"]["tau"] == pytest.approx(TAU, rel=0.03)
for i in (2, 4): # raffreddamenti intermedi: c e τ ereditati dal finale
fit = next(x for x in ris[i]["serie"][nome]["fit"] if x["modello"] == "esp_decad")
assert ris[i]["serie"][nome]["scelta"] == "esp_decad" and fit["fissi"] == ["c", "tau"]
assert fit["parametri"]["tau"] == finale["parametri"]["tau"] and fit["parametri"]["c"] == finale["parametri"]["c"]
# ampiezza dei raffreddamenti intermedi = somma dei decadimenti dei gradini già avvenuti, in t' = 0
for i, n_gradini in ((2, 1), (4, 2)):
t0 = ris[i]["t0_s"]
attesa = sum(a * np.exp(-(t0 - tj) / TAU) for tj, a in GRADINI[:n_gradini])
fit = next(x for x in ris[i]["serie"]["dT_C"]["fit"] if x["modello"] == "esp_decad")
assert fit["parametri"]["A"] == pytest.approx(attesa, rel=0.05)
def test_senza_raffreddamento_riscaldamento_resta_libero(cfg):
t, serie = segnale(gradini=[(20.0, 80.0)], durata=40.0)
ds = [{"tipo": "riposo", "i0": 0, "i1": 100}, {"tipo": "riscaldamento", "i0": 100, "i1": len(t)}]
ris = analizza_domini(t, serie, ds, cfg) # nessun dominio di raffreddamento: niente parametri da ereditare
for x in ris[1]["serie"]["T_C"]["fit"]:
assert x["fissi"] == []
def test_riscaldamento_descritto_come_salto_non_fittato(sintetico, cfg):
t, serie = sintetico
ris = analizza_domini(t, serie, domini.proponi(t, serie, cfg), cfg)
for i, (_, ampiezza) in zip((1, 3, 5), GRADINI, strict=True):
vs = ris[i]["serie"]["dT_C"]
assert vs["fit"] == [] and vs["scelta"] is None
assert vs["gradino"]["delta"] == pytest.approx(ampiezza, rel=0.15) # salto sul canale ΔT, meno il decadimento
assert vs["gradino"]["n"] <= 3
def test_un_solo_raffreddamento_resta_libero(cfg):
t, serie = segnale(gradini=[(20.0, 80.0)], durata=60.0)
ris = analizza_domini(t, serie, domini.proponi(t, serie, cfg), cfg)
assert [r["tipo"] for r in ris] == ["riposo", "riscaldamento", "raffreddamento"]
assert all(x["fissi"] == [] for x in ris[2]["serie"]["T_C"]["fit"])
def test_dominio_troppo_corto_indeterminato(sintetico, cfg):
+10 -9
View File
@@ -15,7 +15,8 @@ def analizza(csv, cfg):
def test_domini_su_csv_gen(csv_gen, cfg):
ds, _ = analizza(csv_gen, cfg)
assert [d["tipo"] for d in ds] == ["riposo", "riscaldamento", "riscaldamento", "raffreddamento"]
assert [d["tipo"] for d in ds] == ["riposo", "riscaldamento", "raffreddamento", "riscaldamento", "raffreddamento",
"riscaldamento", "raffreddamento"]
def test_raffreddamento_tau_nominale(csv_gen, cfg):
@@ -26,13 +27,13 @@ def test_raffreddamento_tau_nominale(csv_gen, cfg):
assert fit["parametri"]["tau"] == pytest.approx(15.9, rel=0.1)
def test_riscaldamento_eredita_costanti(csv_gen, cfg):
def test_raffreddamenti_intermedi_ereditano_costanti(csv_gen, cfg):
_, ris = analizza(csv_gen, cfg)
for nome in ris[0]["serie"]:
raff = next(x for x in ris[-1]["serie"][nome]["fit"] if x["modello"] == "esp_decad")["parametri"]
for r in ris[1:3]:
fit = next(x for x in r["serie"][nome]["fit"] if x["modello"] == "esp_decad")
assert fit["fissi"] == ["c", "tau"] and fit["parametri"]["tau"] == raff["tau"] and fit["parametri"]["A"] > 0
finale = next(x for x in ris[-1]["serie"][nome]["fit"] if x["modello"] == "esp_decad")["parametri"]
for i in (2, 4):
fit = next(x for x in ris[i]["serie"][nome]["fit"] if x["modello"] == "esp_decad")
assert fit["fissi"] == ["c", "tau"] and fit["parametri"]["tau"] == finale["tau"] and fit["parametri"]["A"] > 0
def test_cli_domini_e_analizza(tmp_path, capsys):
@@ -45,12 +46,12 @@ def test_cli_domini_e_analizza(tmp_path, capsys):
assert (out / "01_domini/domini.toml").exists() and (out / "01_domini/domini.png").exists()
assert not (out / "03_report").exists() or not any((out / "03_report").iterdir()) # `domini` non fa nessun fit
cli.main([*base, "analizza", str(csv), "--domini", str(out / "01_domini/domini.toml")]) # domini corretti a mano
for f in ("02_sovrapposizione/sovrapposizione_T_C.png", "02_sovrapposizione/dominio4_T_C.png",
for f in ("02_sovrapposizione/sovrapposizione_T_C.png", "02_sovrapposizione/dominio7_T_C.png",
"03_report/report.md", "03_report/risultati.json"):
assert (out / f).exists(), f
report = (out / "03_report/report.md").read_text()
assert "../01_domini/domini.png" in report and "../02_sovrapposizione/dominio4_T_C.png" in report
assert "dominio 4 raffreddamento" in capsys.readouterr().out
assert "../01_domini/domini.png" in report and "../02_sovrapposizione/dominio7_T_C.png" in report
assert "dominio 7 raffreddamento" in capsys.readouterr().out
def test_cli_errori(tmp_path):