diff --git a/report_completo.py b/report_completo.py index 28b7d6e..e8e7860 100644 --- a/report_completo.py +++ b/report_completo.py @@ -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 T_inf = 22.99 °C è la temperatura ambiente media ponderata. " - "La regressione non lineare usa il metodo Trust Region Reflective (TRF) " - "di scipy.optimize.curve_fit.", - style_body)) - story.append(Spacer(1, 0.4*cm)) + "La regressione usa il metodo Trust Region Reflective (TRF) " + "di scipy.optimize.curve_fit.", 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) diff --git a/report_completo.tex b/report_completo.tex index 51a7204..7154ac3 100644 --- a/report_completo.tex +++ b/report_completo.tex @@ -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} \ No newline at end of file