report_completo: genera .tex e PDF professionale (cover, header/footer, paginazione)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
davideandClaude Sonnet 4.6 committed 2026-05-14 10:20:38 +02:00
1 parent 777b3de338
commit b4f8c67d51
2 files changed
+454 -281

No files matched your search

+324 -123
View File
@@ -1,171 +1,372 @@
import re
from pathlib import Path
from reportlab.lib.pagesizes import A4
from reportlab.lib.units import cm
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
from reportlab.lib import colors
from reportlab.platypus import (SimpleDocTemplate, Paragraph, Spacer, Table,
TableStyle, Image, HRFlowable, PageBreak)
from reportlab.lib.enums import TA_CENTER, TA_RIGHT
from reportlab.platypus import (BaseDocTemplate, PageTemplate, Frame,
Paragraph, Spacer, Table, TableStyle,
Image, HRFlowable, PageBreak, NextPageTemplate)
from reportlab.pdfgen import canvas as rl_canvas
from datetime import date
BASE = Path(__file__).parent
INDUTTORI = [
{
"nome": "Induttore 1",
"intervallo": "110.81 – 111.56 s",
"t0": "110.81",
"t_end": "111.56",
"n_punti": "5",
"zona_exc": None,
"A": "87.3140",
"A_err": "0.0260",
"tau": "15.7231",
"tau_err": "0.2021",
"r2": "0.999505",
"png": BASE / "induttore1" / "induttore1.png",
},
{
"nome": "Induttore 2",
"intervallo": "112.02 – 113.95 s",
"t0": "112.02",
"t_end": "113.95",
"n_punti": "13",
"zona_exc": None,
"A": "158.3430",
"A_err": "0.2088",
"tau": "13.2844",
"tau_err": "0.2183",
"r2": "0.997314",
"png": BASE / "induttore2" / "induttore2.png",
},
{
"nome": "Induttore 3",
"intervallo": "115.0 – 133.7 s",
"t0": "115.0",
"t_end": "133.7",
"n_punti": "125",
"zona_exc": "115.9 – 117.2 s",
"A": "185.1847",
"A_err": "0.2706",
"tau": "16.2654",
"tau_err": "0.0464",
"r2": "0.993827",
"png": BASE / "induttore3" / "induttore3.png",
},
]
# ─── Palette colori ───────────────────────────────────────────────────────────
C_BLUE = colors.HexColor("#1a3a5c")
C_BLUE_LIGHT= colors.HexColor("#2c5f8a")
C_ACCENT = colors.HexColor("#e8f0f7")
C_GRAY = colors.HexColor("#f5f5f5")
C_BORDER = colors.HexColor("#c0c8d0")
HEADER_COLOR = colors.HexColor("#2c5f8a")
LIGHT_GRAY = colors.HexColor("#f2f2f2")
TABLE_STYLE = TableStyle([
("BACKGROUND", (0, 0), (-1, 0), HEADER_COLOR),
("TEXTCOLOR", (0, 0), (-1, 0), colors.white),
("FONTNAME", (0, 0), (-1, 0), "Helvetica-Bold"),
("FONTSIZE", (0, 0), (-1, 0), 10),
("ROWBACKGROUNDS", (0, 1), (-1, -1), [colors.white, LIGHT_GRAY]),
("FONTNAME", (0, 1), (-1, -1), "Helvetica"),
("FONTSIZE", (0, 1), (-1, -1), 9),
("GRID", (0, 0), (-1, -1), 0.5, colors.HexColor("#cccccc")),
("TOPPADDING", (0, 0), (-1, -1), 5),
TABLE_HDR = TableStyle([
("BACKGROUND", (0, 0), (-1, 0), C_BLUE_LIGHT),
("TEXTCOLOR", (0, 0), (-1, 0), colors.white),
("FONTNAME", (0, 0), (-1, 0), "Helvetica-Bold"),
("FONTSIZE", (0, 0), (-1, 0), 10),
("ROWBACKGROUNDS",(0, 1), (-1, -1), [colors.white, C_GRAY]),
("FONTNAME", (0, 1), (-1, -1), "Helvetica"),
("FONTSIZE", (0, 1), (-1, -1), 9),
("GRID", (0, 0), (-1, -1), 0.4, C_BORDER),
("TOPPADDING", (0, 0), (-1, -1), 5),
("BOTTOMPADDING", (0, 0), (-1, -1), 5),
("LEFTPADDING", (0, 0), (-1, -1), 8),
("LEFTPADDING", (0, 0), (-1, -1), 8),
("RIGHTPADDING", (0, 0), (-1, -1), 8),
("VALIGN", (0, 0), (-1, -1), "MIDDLE"),
])
TABLE_SUMMARY = TableStyle([
("BACKGROUND", (0, 0), (-1, 0), C_BLUE),
("TEXTCOLOR", (0, 0), (-1, 0), colors.white),
("FONTNAME", (0, 0), (-1, 0), "Helvetica-Bold"),
("FONTSIZE", (0, 0), (-1, 0), 10),
("ROWBACKGROUNDS",(0, 1), (-1, -1), [colors.white, C_ACCENT]),
("FONTNAME", (0, 1), (-1, -1), "Helvetica"),
("FONTSIZE", (0, 1), (-1, -1), 9),
("GRID", (0, 0), (-1, -1), 0.4, C_BORDER),
("TOPPADDING", (0, 0), (-1, -1), 6),
("BOTTOMPADDING", (0, 0), (-1, -1), 6),
("LEFTPADDING", (0, 0), (-1, -1), 10),
("RIGHTPADDING", (0, 0), (-1, -1), 10),
("ALIGN", (2, 0), (-1, -1), "CENTER"),
("VALIGN", (0, 0), (-1, -1), "MIDDLE"),
])
def build_doc():
out = str(BASE / "report_completo.pdf")
doc = SimpleDocTemplate(out, pagesize=A4,
leftMargin=2.5*cm, rightMargin=2.5*cm,
topMargin=2.5*cm, bottomMargin=2.5*cm)
# ─── Parser markdown ──────────────────────────────────────────────────────────
def parse_md(md_path: Path) -> dict:
text = md_path.read_text(encoding="utf-8")
data = {}
styles = getSampleStyleSheet()
style_title = styles["Title"]
style_h1 = styles["Heading1"]
style_h2 = styles["Heading2"]
style_body = styles["BodyText"]
style_eq = ParagraphStyle("Equation", parent=styles["BodyText"],
fontName="Courier", fontSize=10,
leftIndent=20, spaceAfter=6)
m = re.search(r"^# (.+)$", text, re.MULTILINE)
data["nome"] = m.group(1).strip() if m else md_path.parent.name
kv = dict(re.findall(r"^\| (.+?) \| (.+?) \|$", text, re.MULTILINE))
kv = {k.strip(): v.strip() for k, v in kv.items() if k.strip() != "---"}
data["t0"] = kv.get("Inizio", "").replace(" s", "")
data["t_end"] = kv.get("Fine", "").replace(" s", "")
data["n_punti"] = kv.get("Numero di punti", "")
data["zona_exc"] = kv.get("Zona esclusa (peso = 0)", None)
data["r2"] = kv.get("R²", "")
data["intervallo"] = f"{data['t0']} – {data['t_end']} s"
m = re.search(r"\$\$(.+?)\$\$", text, re.DOTALL)
data["equazione_latex"] = m.group(1).strip() if m else ""
eq = data["equazione_latex"].replace(r"\cdot", "·").replace(r"\infty", "∞")
data["equazione"] = eq
rows3 = re.findall(r"^\| (.+?) \| (.+?) \| (.+?) \|$", text, re.MULTILINE)
params = {r[0].strip(): (r[1].strip(), r[2].strip())
for r in rows3 if r[0].strip() not in ("Parametro", "---")}
data["A"] = params.get("A", ("", ""))[0].replace(" °C", "")
data["A_err"] = (params.get("A", ("", ""))[1]
.replace("± ", "").replace("+/- ", "").replace(" °C", ""))
data["tau"] = params.get("τ", ("", ""))[0].replace(" s", "")
data["tau_err"] = (params.get("τ", ("", ""))[1]
.replace("± ", "").replace("+/- ", "").replace(" s", ""))
pngs = sorted(md_path.parent.glob("*.png"))
data["png"] = pngs[0] if pngs else None
return data
def collect_induttori() -> list:
return [parse_md(md)
for folder in sorted(BASE.glob("induttore*/"))
for md in sorted(folder.glob("report*.md"))]
# ─── Genera .tex ──────────────────────────────────────────────────────────────
def generate_tex(induttori: list):
lines = []
lines += [
r"\documentclass[11pt,a4paper]{article}",
r"\usepackage[utf8]{inputenc}",
r"\usepackage[T1]{fontenc}",
r"\usepackage[margin=2.5cm]{geometry}",
r"\usepackage{amsmath,amssymb}",
r"\usepackage{booktabs}",
r"\usepackage{graphicx}",
r"\usepackage{float}",
r"\usepackage{caption}",
r"\usepackage[table]{xcolor}",
r"\usepackage{hyperref}",
r"\definecolor{hdrblue}{HTML}{1a3a5c}",
r"\usepackage{fancyhdr}",
r"\pagestyle{fancy}",
r"\fancyhf{}",
r"\rhead{\textcolor{hdrblue}{\small Analisi Termica Forno}}",
r"\lhead{\textcolor{hdrblue}{\small Cevolani}}",
r"\rfoot{\thepage}",
r"\title{\textbf{Report Analisi Termica Forno}}",
r"\author{Analisi IR -- Cevolani}",
r"\date{\today}",
r"\begin{document}",
r"\maketitle",
r"\tableofcontents",
r"\newpage",
"",
r"\section{Modello di fitting}",
r"Ogni intervallo è interpolato con il modello di raffreddamento di Newton:",
r"\begin{equation}",
r" T(t) = T_\infty + A \cdot e^{-(t-t_0)/\tau}",
r"\end{equation}",
r"dove $T_\infty = 22.99\,^\circ\mathrm{C}$ è la temperatura ambiente media ponderata.",
r"La regressione non lineare usa il metodo \textit{Trust Region Reflective} (TRF)",
r"di \texttt{scipy.optimize.curve\_fit}.",
"",
]
for ind in induttori:
lines += [
rf"\section{{{ind['nome']}}}",
r"\subsection{Intervallo di campionamento}",
r"\begin{tabular}{ll}",
r"\toprule",
r"\textbf{Parametro} & \textbf{Valore} \\",
r"\midrule",
rf"Inizio & ${ind['t0']}$ s \\",
rf"Fine & ${ind['t_end']}$ s \\",
rf"Numero di punti & {ind['n_punti']} \\",
]
if ind["zona_exc"]:
lines.append(rf"Zona esclusa (peso = 0) & {ind['zona_exc']} \\")
lines += [
r"\bottomrule",
r"\end{tabular}",
"",
r"\subsection{Equazione trovata}",
r"\begin{equation}",
f" {ind['equazione_latex']}",
r"\end{equation}",
r"\begin{tabular}{lll}",
r"\toprule",
r"\textbf{Parametro} & \textbf{Valore} & \textbf{Incertezza (1$\sigma$)} \\",
r"\midrule",
rf"$A$ & ${ind['A']}\,^\circ\mathrm{{C}}$ & $\pm\,{ind['A_err']}\,^\circ\mathrm{{C}}$ \\",
rf"$\tau$ & ${ind['tau']}\,\mathrm{{s}}$ & $\pm\,{ind['tau_err']}\,\mathrm{{s}}$ \\",
rf"$R^2$ & \multicolumn{{2}}{{l}}{{{ind['r2']}}} \\",
r"\bottomrule",
r"\end{tabular}",
"",
r"\subsection{Grafico}",
r"\begin{figure}[H]",
r"\centering",
rf"\includegraphics[width=0.9\textwidth]{{{ind['png']}}}",
rf"\caption{{Fit esponenziale -- {ind['nome']} [{ind['intervallo']}]}}",
r"\end{figure}",
r"\newpage",
"",
]
# Riepilogo
lines += [
r"\section{Riepilogo}",
r"\begin{tabular}{lllll}",
r"\toprule",
r"\textbf{Induttore} & \textbf{Intervallo [s]} & \textbf{A [°C]} & \textbf{$\tau$ [s]} & \textbf{$R^2$} \\",
r"\midrule",
]
for ind in induttori:
lines.append(
rf"{ind['nome']} & {ind['intervallo']} & {ind['A']} & {ind['tau']} & {ind['r2']} \\"
)
lines += [r"\bottomrule", r"\end{tabular}", "", r"\end{document}"]
out = BASE / "report_completo.tex"
out.write_text("\n".join(lines), encoding="utf-8")
print("report_completo.tex generato.")
# ─── Header / footer per reportlab ───────────────────────────────────────────
class NumberedCanvas(rl_canvas.Canvas):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self._saved_page_states = []
def showPage(self):
self._saved_page_states.append(dict(self.__dict__))
self._startPage()
def save(self):
num_pages = len(self._saved_page_states)
for state in self._saved_page_states:
self.__dict__.update(state)
self._draw_page_decoration(num_pages)
super().showPage()
super().save()
def _draw_page_decoration(self, page_count):
w, h = A4
self.saveState()
if self._pageNumber > 1:
# Header
self.setFillColor(C_BLUE)
self.rect(2.5*cm, h - 2*cm, w - 5*cm, 0.6*cm, fill=1, stroke=0)
self.setFillColor(colors.white)
self.setFont("Helvetica-Bold", 9)
self.drawString(2.8*cm, h - 1.65*cm, "Analisi Termica Forno")
self.setFont("Helvetica", 9)
self.drawRightString(w - 2.5*cm, h - 1.65*cm, "Cevolani")
# Footer
self.setStrokeColor(C_BLUE_LIGHT)
self.setLineWidth(0.5)
self.line(2.5*cm, 1.8*cm, w - 2.5*cm, 1.8*cm)
self.setFillColor(C_BLUE)
self.setFont("Helvetica", 8)
self.drawString(2.5*cm, 1.3*cm, f"Report generato il {date.today().strftime('%d/%m/%Y')}")
self.drawRightString(w - 2.5*cm, 1.3*cm,
f"Pag. {self._pageNumber} / {page_count}")
self.restoreState()
# ─── Genera PDF ───────────────────────────────────────────────────────────────
def generate_pdf(induttori: list):
out = str(BASE / "report_completo.pdf")
w, h = A4
margin = 2.5*cm
frame_normal = Frame(margin, 2.5*cm, w - 2*margin, h - 5*cm,
id="normal", topPadding=0.8*cm)
frame_cover = Frame(margin, margin, w - 2*margin, h - 2*margin,
id="cover")
doc = BaseDocTemplate(out, pagesize=A4)
doc.addPageTemplates([
PageTemplate(id="cover", frames=[frame_cover]),
PageTemplate(id="normal", frames=[frame_normal]),
])
styles = getSampleStyleSheet()
s_body = styles["BodyText"]
s_h1 = ParagraphStyle("H1", fontName="Helvetica-Bold", fontSize=14,
textColor=C_BLUE, spaceBefore=14, spaceAfter=6,
borderPad=4)
s_h2 = ParagraphStyle("H2", fontName="Helvetica-Bold", fontSize=11,
textColor=C_BLUE_LIGHT, spaceBefore=10, spaceAfter=4)
s_eq = ParagraphStyle("EQ", fontName="Courier", fontSize=10,
leftIndent=24, spaceBefore=4, spaceAfter=6,
backColor=C_ACCENT, borderPad=6)
s_cover_title = ParagraphStyle("CoverTitle", fontName="Helvetica-Bold",
fontSize=28, textColor=C_BLUE,
alignment=TA_CENTER, spaceAfter=10)
s_cover_sub = ParagraphStyle("CoverSub", fontName="Helvetica",
fontSize=14, textColor=C_BLUE_LIGHT,
alignment=TA_CENTER, spaceAfter=6)
s_cover_date = ParagraphStyle("CoverDate", fontName="Helvetica",
fontSize=11, textColor=colors.grey,
alignment=TA_CENTER)
story = []
# --- Titolo ---
story.append(Paragraph("Report Analisi Termica Forno", style_title))
story.append(Paragraph("Analisi IR – Cevolani", styles["Normal"]))
# ── Cover page ──
story.append(Spacer(1, 5*cm))
story.append(HRFlowable(width="100%", thickness=3, color=C_BLUE))
story.append(Spacer(1, 0.5*cm))
story.append(HRFlowable(width="100%", thickness=1, color=HEADER_COLOR))
story.append(Paragraph("Report Analisi Termica Forno", s_cover_title))
story.append(Spacer(1, 0.3*cm))
story.append(Paragraph("Analisi IR – Cevolani", s_cover_sub))
story.append(Spacer(1, 0.5*cm))
story.append(HRFlowable(width="100%", thickness=3, color=C_BLUE))
story.append(Spacer(1, 1*cm))
story.append(Paragraph(f"Data: {date.today().strftime('%d %B %Y')}", s_cover_date))
story.append(Paragraph(f"Induttori analizzati: {len(induttori)}", s_cover_date))
story.append(NextPageTemplate("normal"))
story.append(PageBreak())
# --- Modello ---
story.append(Paragraph("Modello di fitting", style_h1))
story.append(Paragraph(
"Ogni intervallo è interpolato con il modello di raffreddamento di Newton:",
style_body))
# ── Modello ──
story.append(Paragraph("Modello di fitting", s_h1))
story.append(HRFlowable(width="100%", thickness=1, color=C_BLUE_LIGHT))
story.append(Spacer(1, 0.2*cm))
story.append(Paragraph("T(t) = T_inf + A · exp( -(t - t0) / τ )", style_eq))
story.append(Paragraph(
"Ogni intervallo è interpolato con il modello di raffreddamento di Newton:", s_body))
story.append(Paragraph("T(t) = T_inf + A · exp( -(t - t0) / τ )", s_eq))
story.append(Paragraph(
"dove <b>T_inf = 22.99 °C</b> è la temperatura ambiente media ponderata. "
"La regressione non lineare usa il metodo <i>Trust Region Reflective</i> (TRF) "
"di <font name='Courier'>scipy.optimize.curve_fit</font>.",
style_body))
story.append(Spacer(1, 0.4*cm))
"La regressione usa il metodo <i>Trust Region Reflective</i> (TRF) "
"di <font name='Courier'>scipy.optimize.curve_fit</font>.", s_body))
story.append(Spacer(1, 0.5*cm))
# --- Sezione per ogni induttore ---
for ind in INDUTTORI:
story.append(HRFlowable(width="100%", thickness=0.5, color=colors.lightgrey))
# ── Sezione per ogni induttore ──
for ind in induttori:
story.append(Paragraph(ind["nome"], s_h1))
story.append(HRFlowable(width="100%", thickness=1, color=C_BLUE_LIGHT))
story.append(Spacer(1, 0.2*cm))
story.append(Paragraph(ind["nome"], style_h1))
# Tabella intervallo
story.append(Paragraph("Intervallo di campionamento", style_h2))
# Intervallo
story.append(Paragraph("Intervallo di campionamento", s_h2))
rows = [["Parametro", "Valore"],
["Inizio", f"{ind['t0']} s"],
["Fine", f"{ind['t_end']} s"],
["Numero di punti", ind["n_punti"]]]
if ind["zona_exc"]:
rows.append(["Zona esclusa (peso = 0)", ind["zona_exc"]])
t = Table(rows, colWidths=[8*cm, 6*cm])
t.setStyle(TABLE_STYLE)
story.append(t)
story.append(Table(rows, colWidths=[8*cm, 6*cm], style=TABLE_HDR))
story.append(Spacer(1, 0.3*cm))
# Equazione
story.append(Paragraph("Equazione trovata", style_h2))
eq = (f"T(t) = 22.99 + {ind['A']} · exp( -(t - {ind['t0']}) / {ind['tau']} )")
story.append(Paragraph(eq, style_eq))
story.append(Paragraph("Equazione trovata", s_h2))
story.append(Paragraph(ind["equazione"], s_eq))
story.append(Spacer(1, 0.2*cm))
# Tabella parametri
param_rows = [
["Parametro", "Valore", "Incertezza (1σ)"],
["A", f"{ind['A']} °C", f"± {ind['A_err']} °C"],
["τ", f"{ind['tau']} s", f"± {ind['tau_err']} s"],
["R²", ind["r2"], ""],
]
t2 = Table(param_rows, colWidths=[4*cm, 6*cm, 5*cm])
t2.setStyle(TABLE_STYLE)
story.append(t2)
story.append(Table([
["Parametro", "Valore", "Incertezza (1σ)"],
["A", f"{ind['A']} °C", f"± {ind['A_err']} °C"],
["τ", f"{ind['tau']} s", f"± {ind['tau_err']} s"],
["R²", ind["r2"], ""],
], colWidths=[3.5*cm, 6*cm, 5*cm], style=TABLE_HDR))
story.append(Spacer(1, 0.3*cm))
# Plot
story.append(Paragraph("Grafico", style_h2))
img = Image(str(ind["png"]), width=14*cm, height=8.4*cm)
story.append(img)
story.append(Spacer(1, 0.5*cm))
if ind["png"] and Path(ind["png"]).exists():
story.append(Paragraph("Grafico", s_h2))
story.append(Image(str(ind["png"]), width=14*cm, height=8.4*cm))
story.append(Spacer(1, 0.4*cm))
# --- Tabella riepilogativa ---
# ── Riepilogo ──
story.append(PageBreak())
story.append(Paragraph("Riepilogo", style_h1))
story.append(Paragraph("Riepilogo", s_h1))
story.append(HRFlowable(width="100%", thickness=1, color=C_BLUE_LIGHT))
story.append(Spacer(1, 0.3*cm))
summary = [["Induttore", "Intervallo [s]", "A [°C]", "τ [s]", "R²"]]
for ind in INDUTTORI:
for ind in induttori:
summary.append([ind["nome"], ind["intervallo"],
ind["A"], ind["tau"], ind["r2"]])
t3 = Table(summary, colWidths=[3.5*cm, 4.5*cm, 3*cm, 3*cm, 3*cm])
t3.setStyle(TABLE_STYLE)
story.append(t3)
story.append(Table(summary,
colWidths=[3.5*cm, 4.5*cm, 3*cm, 3*cm, 3*cm],
style=TABLE_SUMMARY))
doc.build(story)
print(f"report_completo.pdf generato.")
doc.build(story, canvasmaker=NumberedCanvas)
print(f"report_completo.pdf generato ({len(induttori)} induttori).")
# ─── Entry point ──────────────────────────────────────────────────────────────
if __name__ == "__main__":
build_doc()
induttori = collect_induttori()
if not induttori:
print("Nessun report*.md trovato nelle cartelle induttore*/.")
else:
generate_tex(induttori)
generate_pdf(induttori)
+130 -158
View File
@@ -1,177 +1,149 @@
\documentclass{article}%
\usepackage[T1]{fontenc}%
\usepackage[utf8]{inputenc}%
\usepackage{lmodern}%
\usepackage{textcomp}%
\usepackage{lastpage}%
\usepackage{geometry}%
\geometry{margin=2.5cm}%
\usepackage{booktabs}%
\usepackage{graphicx}%
\usepackage{amsmath}%
\usepackage{float}%
\usepackage{caption}%
%
\title{Report Analisi Termica Forno}%
\author{Analisi IR {-}{-} Cevolani}%
\date{\today}%
%
\begin{document}%
\normalsize%
\maketitle%
\section{Modello di fitting}%
\label{sec:Modellodifitting}%
Ogni intervallo è interpolato con il modello di raffreddamento di Newton:%
\[%
T(t) = T_\infty + A \cdot e^{-(t - t_0)/\tau}%
\]%
dove $T_\infty = 22.99\,^\circ\mathrm{C}$ è la temperatura ambiente media ponderata. La regressione non lineare usa il metodo \textit{Trust Region Reflective} (TRF) di \texttt{scipy.optimize.curve\_fit}.
\documentclass[11pt,a4paper]{article}
\usepackage[utf8]{inputenc}
\usepackage[T1]{fontenc}
\usepackage[margin=2.5cm]{geometry}
\usepackage{amsmath,amssymb}
\usepackage{booktabs}
\usepackage{graphicx}
\usepackage{float}
\usepackage{caption}
\usepackage[table]{xcolor}
\usepackage{hyperref}
\definecolor{hdrblue}{HTML}{1a3a5c}
\usepackage{fancyhdr}
\pagestyle{fancy}
\fancyhf{}
\rhead{\textcolor{hdrblue}{\small Analisi Termica Forno}}
\lhead{\textcolor{hdrblue}{\small Cevolani}}
\rfoot{\thepage}
\title{\textbf{Report Analisi Termica Forno}}
\author{Analisi IR -- Cevolani}
\date{\today}
\begin{document}
\maketitle
\tableofcontents
\newpage
%
\section{Induttore 1}%
\label{sec:Induttore1}%
\subsection{Intervallo di campionamento}%
\label{subsec:Intervallodicampionamento}%
\begin{tabular}{|l|l|}%
\hline%
\textbf{Parametro}&\textbf{Valore}\\%
\hline%
Inizio&110.81 s\\%
Fine&111.56 s\\%
Numero di punti&5\\%
\hline%
\section{Modello di fitting}
Ogni intervallo è interpolato con il modello di raffreddamento di Newton:
\begin{equation}
T(t) = T_\infty + A \cdot e^{-(t-t_0)/\tau}
\end{equation}
dove $T_\infty = 22.99\,^\circ\mathrm{C}$ è la temperatura ambiente media ponderata.
La regressione non lineare usa il metodo \textit{Trust Region Reflective} (TRF)
di \texttt{scipy.optimize.curve\_fit}.
\section{Report Induttore 1}
\subsection{Intervallo di campionamento}
\begin{tabular}{ll}
\toprule
\textbf{Parametro} & \textbf{Valore} \\
\midrule
Inizio & $110.81$ s \\
Fine & $111.56$ s \\
Numero di punti & 5 \\
\bottomrule
\end{tabular}
%
\subsection{Equazione trovata}%
\label{subsec:Equazionetrovata}%
\[%
T(t) = 22.99 + 87.3140 \cdot e^{-(t - 110.81) / 15.7231}%
\]%
\vspace{4pt}%
\begin{tabular}{|l|l|l|}%
\hline%
\textbf{Parametro}&\textbf{Valore}&\textbf{Incertezza (1σ)}\\%
\hline%
A&$87.3140\,^\circ\mathrm{C}$&$\pm\,0.0260\,^\circ\mathrm{C}$\\%
$\tau$&$15.7231\,\mathrm{s}$&$\pm\,0.2021\,\mathrm{s}$\\%
\hline%
\end{tabular}%
\vspace{4pt}%
$R^2 = 0.999505$
\subsection{Equazione trovata}
\begin{equation}
T(t) = 22.99 + 87.3140 \cdot e^{-(t - 110.81) / 15.7231}
\end{equation}
\begin{tabular}{lll}
\toprule
\textbf{Parametro} & \textbf{Valore} & \textbf{Incertezza (1$\sigma$)} \\
\midrule
$A$ & $87.3140\,^\circ\mathrm{C}$ & $\pm\,0.0260\,^\circ\mathrm{C}$ \\
$\tau$ & $15.7231\,\mathrm{s}$ & $\pm\,0.2021\,\mathrm{s}$ \\
$R^2$ & \multicolumn{2}{l}{0.999505} \\
\bottomrule
\end{tabular}
%
\subsection{Grafico}%
\label{subsec:Grafico}%
\begin{figure}[H]%
\centering%
\includegraphics[width=0.9\textwidth]{C:/Users/davide.grilli/Desktop/vs/temperatura/induttore1/induttore1.png}%
\caption{Fit esponenziale — Induttore 1 {[}110.81 – 111.56 s{]}}%
\subsection{Grafico}
\begin{figure}[H]
\centering
\includegraphics[width=0.9\textwidth]{C:\Users\davide.grilli\Desktop\vs\temperatura\induttore1\induttore1.png}
\caption{Fit esponenziale -- Report Induttore 1 [110.81 – 111.56 s]}
\end{figure}
\newpage
%
\section{Induttore 2}%
\label{sec:Induttore2}%
\subsection{Intervallo di campionamento}%
\label{subsec:Intervallodicampionamento}%
\begin{tabular}{|l|l|}%
\hline%
\textbf{Parametro}&\textbf{Valore}\\%
\hline%
Inizio&112.02 s\\%
Fine&113.95 s\\%
Numero di punti&13\\%
\hline%
\section{Report Induttore 2}
\subsection{Intervallo di campionamento}
\begin{tabular}{ll}
\toprule
\textbf{Parametro} & \textbf{Valore} \\
\midrule
Inizio & $112.02$ s \\
Fine & $113.95$ s \\
Numero di punti & 12 \\
\bottomrule
\end{tabular}
%
\subsection{Equazione trovata}%
\label{subsec:Equazionetrovata}%
\[%
T(t) = 22.99 + 158.3430 \cdot e^{-(t - 112.02) / 13.2844}%
\]%
\vspace{4pt}%
\begin{tabular}{|l|l|l|}%
\hline%
\textbf{Parametro}&\textbf{Valore}&\textbf{Incertezza (1σ)}\\%
\hline%
A&$158.3430\,^\circ\mathrm{C}$&$\pm\,0.2088\,^\circ\mathrm{C}$\\%
$\tau$&$13.2844\,\mathrm{s}$&$\pm\,0.2183\,\mathrm{s}$\\%
\hline%
\end{tabular}%
\vspace{4pt}%
$R^2 = 0.997314$
\subsection{Equazione trovata}
\begin{equation}
T(t) = 22.99 + 158.3430 \cdot e^{-(t - 112.02) / 13.2844}
\end{equation}
\begin{tabular}{lll}
\toprule
\textbf{Parametro} & \textbf{Valore} & \textbf{Incertezza (1$\sigma$)} \\
\midrule
$A$ & $158.3430\,^\circ\mathrm{C}$ & $\pm\,0.2088\,^\circ\mathrm{C}$ \\
$\tau$ & $13.2844\,\mathrm{s}$ & $\pm\,0.2183\,\mathrm{s}$ \\
$R^2$ & \multicolumn{2}{l}{0.997314} \\
\bottomrule
\end{tabular}
%
\subsection{Grafico}%
\label{subsec:Grafico}%
\begin{figure}[H]%
\centering%
\includegraphics[width=0.9\textwidth]{C:/Users/davide.grilli/Desktop/vs/temperatura/induttore2/induttore2.png}%
\caption{Fit esponenziale — Induttore 2 {[}112.02 – 113.95 s{]}}%
\subsection{Grafico}
\begin{figure}[H]
\centering
\includegraphics[width=0.9\textwidth]{C:\Users\davide.grilli\Desktop\vs\temperatura\induttore2\induttore2.png}
\caption{Fit esponenziale -- Report Induttore 2 [112.02 – 113.95 s]}
\end{figure}
\newpage
%
\section{Induttore 3}%
\label{sec:Induttore3}%
\subsection{Intervallo di campionamento}%
\label{subsec:Intervallodicampionamento}%
\begin{tabular}{|l|l|}%
\hline%
\textbf{Parametro}&\textbf{Valore}\\%
\hline%
Inizio&115.0 s\\%
Fine&133.7 s\\%
Numero di punti&125\\%
Zona esclusa (peso = 0)&115.9 – 117.2 s\\%
\hline%
\section{Report Induttore 3}
\subsection{Intervallo di campionamento}
\begin{tabular}{ll}
\toprule
\textbf{Parametro} & \textbf{Valore} \\
\midrule
Inizio & $115.0$ s \\
Fine & $133.7$ s \\
Numero di punti & 125 \\
\bottomrule
\end{tabular}
%
\subsection{Equazione trovata}%
\label{subsec:Equazionetrovata}%
\[%
T(t) = 22.99 + 185.1847 \cdot e^{-(t - 115.0) / 16.2654}%
\]%
\vspace{4pt}%
\begin{tabular}{|l|l|l|}%
\hline%
\textbf{Parametro}&\textbf{Valore}&\textbf{Incertezza (1σ)}\\%
\hline%
A&$185.1847\,^\circ\mathrm{C}$&$\pm\,0.2706\,^\circ\mathrm{C}$\\%
$\tau$&$16.2654\,\mathrm{s}$&$\pm\,0.0464\,\mathrm{s}$\\%
\hline%
\end{tabular}%
\vspace{4pt}%
$R^2 = 0.993827$
\subsection{Equazione trovata}
\begin{equation}
T(t) = 22.99 + 185.1847 \cdot e^{-(t - 115.0) / 16.2654}
\end{equation}
\begin{tabular}{lll}
\toprule
\textbf{Parametro} & \textbf{Valore} & \textbf{Incertezza (1$\sigma$)} \\
\midrule
$A$ & $185.1847\,^\circ\mathrm{C}$ & $\pm\,0.2706\,^\circ\mathrm{C}$ \\
$\tau$ & $16.2654\,\mathrm{s}$ & $\pm\,0.0464\,\mathrm{s}$ \\
$R^2$ & \multicolumn{2}{l}{0.993827} \\
\bottomrule
\end{tabular}
%
\subsection{Grafico}%
\label{subsec:Grafico}%
\begin{figure}[H]%
\centering%
\includegraphics[width=0.9\textwidth]{C:/Users/davide.grilli/Desktop/vs/temperatura/induttore3/induttore3.png}%
\caption{Fit esponenziale — Induttore 3 {[}115.0 – 133.7 s{]}}%
\subsection{Grafico}
\begin{figure}[H]
\centering
\includegraphics[width=0.9\textwidth]{C:\Users\davide.grilli\Desktop\vs\temperatura\induttore3\induttore3.png}
\caption{Fit esponenziale -- Report Induttore 3 [115.0 – 133.7 s]}
\end{figure}
\newpage
%
\section{Riepilogo}%
\label{sec:Riepilogo}%
\begin{tabular}{|l|l|l|l|l|}%
\hline%
\textbf{Induttore}&\textbf{Intervallo {[}s{]}}&\textbf{A {[}°C{]}}&\textbf{$\tau$ [s]}&\textbf{$R^2$}\\%
\hline%
Induttore 1&110.81 – 111.56 s&87.3140&15.7231&0.999505\\%
Induttore 2&112.02 – 113.95 s&158.3430&13.2844&0.997314\\%
Induttore 3&115.0 – 133.7 s&185.1847&16.2654&0.993827\\%
\hline%
\section{Riepilogo}
\begin{tabular}{lllll}
\toprule
\textbf{Induttore} & \textbf{Intervallo [s]} & \textbf{A [°C]} & \textbf{$\tau$ [s]} & \textbf{$R^2$} \\
\midrule
Report Induttore 1 & 110.81 – 111.56 s & 87.3140 & 15.7231 & 0.999505 \\
Report Induttore 2 & 112.02 – 113.95 s & 158.3430 & 13.2844 & 0.997314 \\
Report Induttore 3 & 115.0 – 133.7 s & 185.1847 & 16.2654 & 0.993827 \\
\bottomrule
\end{tabular}
%
\end{document}