From a30a9410c77f23f57dca2bff7e0df9e01f828687 Mon Sep 17 00:00:00 2001 From: Davide Grilli Date: Thu, 14 May 2026 11:11:48 +0200 Subject: [PATCH] docs: aggiorna README e CLAUDE.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit README: aggiunge algoritmo.md e clear.sh alla struttura e alle voci menu. CLAUDE.md: riscrittura completa — rimuove script obsoleti e valori hardcoded, documenta architettura attuale (induttoreX/, report dinamico, T∞ da start/report.md, corporate identity in report_completo.py). Co-Authored-By: Claude Sonnet 4.6 --- CLAUDE.md | 100 +++++++++++++++++++++++++++++++++++------------------- README.md | 22 ++++++++++++ 2 files changed, 88 insertions(+), 34 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index 273276c..cd2ad2e 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -4,7 +4,12 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co ## Project purpose -Thermal analysis of a box passing through a production oven, measured with IR sensors. The goal is to find the mathematical law that best interpolates the temperature data in `data.csv`. The user directs the analysis and Claude implements the requested models in Python. +Thermal analysis of boxes passing through a production oven (Cevolani Italia s.r.l.), measured with IR sensors. The pipeline accepts any `data.csv` that respects the column specifications in `README.md` and produces: + +- A Newton's cooling fit for each inductor zone (`induttoreX/appX.py`) +- A professional PDF report (`report_completo.pdf`) aggregating all results + +The user directs the analysis (defines time windows, chooses exclusion zones); Claude implements the requested models and pipeline changes in Python. ## Setup @@ -14,53 +19,80 @@ venv\Scripts\activate pip install -r requirements.txt ``` -## Running scripts +## Project architecture -```bat -python .py +```text +main.py ← interactive menu, auto-discovers induttore*/ folders +start/ + calcola_t_amb.py ← computes T∞ (weighted mean), writes start/report.md + plot_temperatura.py ← plots raw temp_obj and temp_amb over [T0, T_END] +induttoreX/ + appX.py ← nonlinear fit for one inductor zone; params hardcoded by user +report_completo.py ← reads all induttore*/report*.md, generates .tex + .pdf +algoritmo.md ← detailed explanation of the TRF algorithm +clear.sh ← deletes all generated files (*.md, *.png, report_completo.*) ``` -Each script is standalone and produces a PNG plot. No test suite exists. - ## Data -`data.csv` columns used for fitting: -- `time since start [ms]` — converted to seconds in every script (`/1000`) -- `temp_obj IR [C]` — box temperature, the fitting target +`data.csv` required columns (exact names): +- `time since start [ms]` — converted to seconds (`/1000`) in every script +- `temp_obj IR [C]` — object/box temperature, the fitting target - `temp_amb IR [C]` — ambient temperature, used to compute T∞ -All fits start from t = 105 s. The transition zone [115.9–117.2 s] is excluded by setting `sigma = 1e10` for those points (weighted least squares, not deletion). T∞ = 22.99°C is a fixed constant computed once in `calcola_t_amb.py` and hardcoded in the other scripts. +T∞ is computed once by `calcola_t_amb.py` (weighted mean over the full acquisition) and stored in `start/report.md`. All other scripts read T∞ from there — it is **not hardcoded**. ## Fitting architecture -All fits use `scipy.optimize.curve_fit(..., method="trf")` (Trust Region Reflective, nonlinear least squares). The base pattern shared across every fit script: +All fits use `scipy.optimize.curve_fit(..., method="trf")` (Trust Region Reflective, nonlinear least squares). Newton's cooling model: -```python -popt, pcov = curve_fit(model, t_fit, T_fit, p0=[...], bounds=([...], [...]), - sigma=sigma, absolute_sigma=True, method="trf") -residuals = T_fit - model(t_fit, *popt) -ss_res = np.sum((residuals / sigma_clean)**2) # ignores excluded zone -ss_tot = np.sum((T_fit[mask] - np.mean(T_fit[mask]))**2) -R2 = 1 - ss_res / ss_tot -perr = np.sqrt(np.diag(pcov)) # parameter uncertainties (1σ) +```text +T(t) = T∞ + A · exp(-(t - t0) / τ) ``` -### Models implemented +Standard pattern in every `appX.py`: -| Script | Model | Fixed params | -|--------|-------|-------------| -| `fit_raffreddamento_intero.py` | `T∞ + A·exp(-(t-t0)/τ)` — full range, weighted | T∞, t0=115.0s | -| `fit_raffreddamento_1tratto.py` | same — segment [115.0–115.9s] | T∞, t0=115.0s | -| `fit_raffreddamento_2tratto.py` | same — segment [117.5s–end] | T∞, t0=117.5s | -| `fit_doppio_esponenziale.py` | `T∞ + A1·exp(-(t-t1)/τ1) + A2·exp(-(t-t2)/τ2)` | T∞, t1=115.0s, t2=117.5s | +```python +T0, T_END = ... # time window [s], set by user +W0_START, W0_END = ... # optional exclusion zone [s], set by user -The double exponential (`fit_doppio_esponenziale.py`) is the best model: R² = 0.9991, decomposing cooling into a fast surface component (τ₁ ≈ 1.8 s) and a slow core component (τ₂ ≈ 17.6 s). +df = pd.read_csv(ROOT / "data.csv") +df["time_s"] = df["time since start [ms]"] / 1000.0 +mask = (df["time_s"] >= T0) & (df["time_s"] <= T_END) +t_fit = df.loc[mask, "time_s"].values +T_fit = df.loc[mask, "temp_obj IR [C]"].values -## Adding a new fit +sigma = np.where((t_fit >= W0_START) & (t_fit <= W0_END), 1e10, 1.0) +mask_clean = sigma < 1e10 -1. Copy an existing fit script as a template. -2. Define the model function with T∞ baked in or passed as a fixed parameter. -3. Set `p0` from physical intuition or from a preliminary simpler fit. -4. Use `sigma = np.where(mask_exclude, 1e10, 1.0)` to down-weight bad zones. -5. Compute R² only over the unmasked points (`mask_clean`). -6. Save output as a new PNG; do not overwrite existing plots. +popt, pcov = curve_fit(model, t_fit, T_fit, p0=[...], bounds=([...], [...]), + sigma=sigma, absolute_sigma=True, method="trf") +residuals = T_fit[mask_clean] - model(t_fit[mask_clean], *popt) +ss_res = np.sum(residuals**2) +ss_tot = np.sum((T_fit[mask_clean] - np.mean(T_fit[mask_clean]))**2) +R2 = 1 - ss_res / ss_tot +perr = np.sqrt(np.diag(pcov)) +``` + +If no exclusion zone is needed, `sigma = np.ones(len(t_fit))` and `mask_clean = slice(None)`. + +## Adding a new inductor + +1. Copy an existing `induttoreX/appX.py` into a new folder `induttoreY/appY.py`. +2. Set `T0`, `T_END` to the desired time window. +3. Set `W0_START`, `W0_END` if a sub-interval must be excluded (or remove the exclusion logic). +4. Run via `main.py` — the menu discovers it automatically. +5. The script produces `induttoreY/induttoreY.png` and `induttoreY/reportY.md`. +6. Running `report_completo.py` includes it automatically in the PDF. + +## Report generation + +`report_completo.py` is fully dynamic: +- Discovers all `induttore*/report*.md` via glob +- Reads T∞ from `start/report.md` +- Renders equations and math symbols as PNG via matplotlib mathtext (no system LaTeX required) +- Outputs `report_completo.tex` and `report_completo.pdf` + +Corporate identity constants (editable at the top of `report_completo.py`): + +`SOCIETA`, `DIPARTIMENTO`, `DOC_NUM`, `REVISIONE`, `STATO`, `CLASSIFICAZIONE`. diff --git a/README.md b/README.md index b0133f4..d13918c 100644 --- a/README.md +++ b/README.md @@ -30,6 +30,8 @@ temperatura/ ├── data.csv # dati acquisiti (input) ├── main.py # menu interattivo ├── report_completo.py # generatore report PDF + LaTeX +├── algoritmo.md # descrizione dell'algoritmo di regressione +├── clear.sh # elimina tutti i file generati ├── requirements.txt ├── start/ │ ├── calcola_t_amb.py # calcola T∞ media ponderata @@ -75,6 +77,14 @@ Per aggiungere un nuovo induttore: copiare uno script esistente, aggiornare `T0` --- +### Algoritmo di regressione + +**File:** `algoritmo.md` + +Spiega l'algoritmo usato per tutti i fit: minimi quadrati non lineari, metodo Trust Region Reflective (TRF), gestione dei pesi, stima iniziale dei parametri e metriche di qualità (R², incertezze dalla matrice di covarianza). + +--- + ### Report completo (PDF + LaTeX) **Script:** `report_completo.py` @@ -88,6 +98,18 @@ Il PDF viene generato direttamente da Python tramite `reportlab` (nessuna dipend --- +### Pulizia file generati + +**Script:** `clear.sh` + +Elimina tutti i file prodotti dagli script (`*.md` e `*.png` in `start/` e `induttore*/`, più `report_completo.tex` e `report_completo.pdf`). I sorgenti e `data.csv` non vengono toccati. + +```bash +bash clear.sh +``` + +--- + ## Requisiti di data.csv ### Formato