# CLAUDE.md This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. ## 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. ## Setup ```bat python -m venv venv venv\Scripts\activate pip install -r requirements.txt ``` ## Running scripts ```bat python .py ``` 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 - `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. ## 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: ```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σ) ``` ### Models implemented | 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 | 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). ## Adding a new fit 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.