Files
EquationMiner/src/fitseg/domini.py
T
davideandClaude Sonnet 5.5 b49af5c112 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>
2026-10-29 10:28:00 +01:00

89 lines
3.7 KiB
Python
Raw 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.
"""Riconoscimento dei domini: riposo, riscaldamento (salita rapida), raffreddamento (decadimento) — nessun LLM."""
import numpy as np
def sigma_robusto(d):
"""σ robusto di un campione di differenze (MAD)."""
return 1.4826 * np.median(np.abs(d - np.median(d)))
def sigma_rumore(y):
"""σ del rumore bianco dalla derivata seconda discreta: 1.4826·MAD(d²y)/√6."""
if len(y) < 4:
return float("nan")
return sigma_robusto(np.diff(y, 2)) / np.sqrt(6)
def trova_gradini(y, cfg):
"""(i0, i1) dei campioni di salita di ogni gradino di riscaldamento (i1 escluso): salto positivo > soglia,
esteso ai campioni adiacenti ancora in salita (salto > soglia_bordo_sigma · σ dei salti)."""
c = cfg["domini"]
d = np.diff(y)
forti = np.flatnonzero(d > c["soglia_gradino_c"])
if len(forti) == 0:
return []
soglia_bordo = c["soglia_bordo_sigma"] * sigma_robusto(d)
gruppi, corrente = [], [forti[0]]
for i in forti[1:]:
if i - corrente[-1] < c["distanza_min_salti_campioni"]:
corrente.append(i)
else:
gruppi.append(corrente)
corrente = [i]
gruppi.append(corrente)
gradini = []
for g in gruppi:
inizio, fine = int(g[0]), int(g[-1])
while inizio > 0 and d[inizio - 1] > soglia_bordo:
inizio -= 1
while fine + 1 < len(d) and d[fine + 1] > soglia_bordo:
fine += 1
gradini.append((inizio + 1, min(fine + 2, len(y))))
return gradini
def proponi(t, serie, cfg):
"""Lista di domini {tipo, i0, i1} (i1 escluso): riposo, poi per ogni gradino un `riscaldamento` (la salita rapida,
pochi campioni) seguito da un `raffreddamento` (il decadimento fino al gradino successivo o alla fine)."""
y = serie[cfg["domini"]["serie_riferimento"]]
domini, prec = [], 0
for g0, g1 in trova_gradini(y, cfg):
if g0 > prec:
domini.append({"tipo": "riposo" if not domini else "raffreddamento", "i0": prec, "i1": g0})
domini.append({"tipo": "riscaldamento", "i0": g0, "i1": g1})
prec = g1
if prec < len(y):
domini.append({"tipo": "riposo" if not domini else "raffreddamento", "i0": prec, "i1": len(y)})
return domini
def scrivi_toml(percorso, domini, t):
"""domini.toml: modificabile a mano (tipo, t0_s, t1_s); gli indici si ricalcolano dal tempo."""
righe = ["# domini.toml — proposta automatica, modificabile: t0_s incluso, t1_s escluso (ultimo dominio: incluso)\n"]
for d in domini:
t1 = t[d["i1"]] if d["i1"] < len(t) else t[-1]
righe.append(f'[[dominio]]\ntipo = "{d["tipo"]}" # riposo | riscaldamento | raffreddamento\n'
f't0_s = {t[d["i0"]]:.4f}\nt1_s = {t1:.4f}\n')
percorso.write_text("\n".join(righe), encoding="utf-8")
def leggi_toml(percorso, t):
import tomllib
with open(percorso, "rb") as f:
voci = tomllib.load(f).get("dominio", [])
if not voci:
raise ValueError("domini.toml senza [[dominio]]")
domini = []
for k, v in enumerate(voci):
if v["tipo"] not in ("riposo", "riscaldamento", "raffreddamento"):
raise ValueError(f"dominio {k + 1}: tipo '{v['tipo']}' non valido")
i0 = int(np.searchsorted(t, v["t0_s"] - 1e-6))
i1 = len(t) if k == len(voci) - 1 else int(np.searchsorted(t, v["t1_s"] - 1e-6))
if i1 <= i0:
raise ValueError(f"dominio {k + 1}: intervallo vuoto o in ordine errato")
domini.append({"tipo": v["tipo"], "i0": i0, "i1": i1})
if any(domini[k]["i1"] != domini[k + 1]["i0"] for k in range(len(domini) - 1)):
raise ValueError("i domini devono essere contigui (t1_s di uno = t0_s del successivo)")
return domini