import marimo __generated_with = "0.25.2" app = marimo.App(width="medium") @app.cell def _(): import marimo as mo import numpy as np import pandas as pd import sympy as sp from scipy import stats from pathlib import Path import statistica _outputs, defs = statistica.app.run() Data = defs["Data"] compat = defs["compat"] media_pesata = defs["media_pesata"] rimuovi_outlier = defs["rimuovi_outlier"] plot_gauss = defs["plot_gauss"] cartella_csv = Path(__file__).resolve().parent / "csv" / "velocita-C" # cartella_immagini = Path(__file__).resolve().parent / "LaTeX" / "immagini" def leggi_csv(nome): dati = pd.read_csv(cartella_csv / nome, sep=";", decimal=",") dati.columns = dati.columns.astype(str).str.strip() dati = dati.dropna(axis=1, how="all") return dati.apply(pd.to_numeric, errors="coerce") return Data, compat, leggi_csv, mo, pd, sp @app.cell(hide_code=True) def _(mo): mo.md(r""" # Velocità della luce — metodo di Foucault $$c = \frac{4 f_2 D^2 \left|\omega_{fin}-\omega_{in}\right|}{(D+a-f_2)\left|x_{fin}-x_{in}\right|}, \qquad \omega = 2\pi f_r$$ dove $\omega_{in},\ \omega_{fin}$ sono le pulsazioni di rotazione e $x_{in},\ x_{fin}$ le posizioni del fascio (zero arbitrario) relative alle due letture dello stesso punto sperimentale. Le stime di $c$ dei singoli punti vengono combinate con una media pesata. """) return @app.cell(hide_code=True) def _(mo): mo.md(r""" ## Costanti geometriche (parte sistematica) """) return @app.cell def _(Data, leggi_csv, pd, sp): _D1 = Data(leggi_csv("D1.csv")) _D1.stat_chauv("D", 0.01, False) _D2 = Data(leggi_csv("D2.csv")) _D2.stat_chauv("D", 0.01, False) _D3 = Data(leggi_csv("D3.csv")) _D3.stat_chauv("D", 0.01, False) _D_tot = Data(None, analisi_stat = pd.DataFrame({ "L1": [_D1.analisi_stat.D[0]], "uL1": [_D1.analisi_stat.uD[0]], "L2": [_D2.analisi_stat.D[0]], "uL2": [_D2.analisi_stat.uD[0]], "L3": [_D3.analisi_stat.D[0]], "uL3": [_D3.analisi_stat.uD[0]] })) L1, L2, L3 = sp.symbols("L1 L2 L3", positive=True) D = L1 + L2 + L3 _D_tot.calc_var(D, "D") D, uD = _D_tot.analisi_stat.D[0], _D_tot.analisi_stat.uD[0] # Distanza specchio rotante - specchio concavo [m] a, ua = 0.376, 0.002 # Distanza lente - specchio rotante [m] f2, uf2 = 0.2536, 0.0005 # Lunghezza focale lente L2 [m] return D, a, f2, uD, ua, uf2 @app.cell(hide_code=True) def _(mo): mo.md(r""" ## Dati grezzi Dati simulati, usati solo per verificare il procedimento. - `C.csv`: una riga per punto sperimentale, con le quattro letture `f_iniziale`, `f_finale` in Hz e `x_iniziale`, `x_finale` in mm; - le frequenze sono negative per il verso di rotazione assunto; - `D1.csv`, `D2.csv`, `D3.csv`: le tre tratte che compongono $D=L_1+L_2+L_3$. Dopo l'analisi statistica le posizioni vengono convertite da mm a m. """) return @app.cell def _(Data, leggi_csv): err_strum = { "f_finale" : 1, "f_iniziale": 1, "x_finale" : 0.01, "x_iniziale": 0.01 } # risoluzione strumentale: Hz, mm dati = Data(leggi_csv("C.csv")) dati.stat_chauv(list(err_strum.keys()), list(err_strum.values())) return (dati,) @app.cell def _(D, a, dati, f2, uD, ua, uf2): dati.analisi_stat["D"] = D dati.analisi_stat["uD"] = uD dati.analisi_stat["a"] = a dati.analisi_stat["ua"] = ua dati.analisi_stat["f2"] = f2 dati.analisi_stat["uf2"] = uf2 dati.analisi_stat return @app.cell(hide_code=True) def _(mo): mo.md(r""" ## Analisi con criterio di Chauvenet Stessa analisi (`stat_chauv`) applicata sia ai punti con rotazione sia alla baseline. Poi si converte `d` da mm a m e si riportano $f_{r0}, d_0$ (baseline) come colonne costanti su tutte le righe dei punti principali, dato che è la stessa misura per tutti. """) return @app.cell def _(dati): # conversione mm -> m per le colonne di lunghezza for _col in ["x_finale", "ux_finale", "x_iniziale", "ux_iniziale"]: dati.analisi_stat[_col] = dati.analisi_stat[_col] * 1e-3 dati.analisi_stat return @app.cell(hide_code=True) def _(mo): mo.md(r""" ## Calcolo delle pulsazioni $\omega_{in}=2\pi f_{in}$ e $\omega_{fin}=2\pi f_{fin}$ """) return @app.cell def _(dati, sp): _f_iniziale, _f_finale = sp.symbols("f_iniziale f_finale") dati.calc_var(2 * sp.pi * _f_iniziale, "omega_iniziale") dati.calc_var(2 * sp.pi * _f_finale, "omega_finale") dati.analisi_stat return @app.cell(hide_code=True) def _(mo): mo.md(r""" ## Calcolo di $c$ punto per punto $D$, $a$ e $f_2$ sono riportate come colonne costanti **con le loro incertezze** (`uD`, `ua`, `uf2`), quindi `calc_var` propaga la parte geometrica insieme a quella sperimentale. Le colonne `%uc_f_iniziale`, `%uc_f_finale`, `uc_x_...` danno il peso percentuale di ciascun termine e mostrano quale incertezza domina il risultato. """) return @app.cell(hide_code=True) def _(formula_c, mo, sp): mo.md(f""" $c={sp.latex(formula_c)}$ """) return @app.cell def _(dati, sp): _D, _a, _f2, _omega_iniziale, _omega_finale, _x_iniziale, _x_finale= sp.symbols("D a f2 omega_iniziale omega_finale x_iniziale x_finale", real=True) formula_c = 4 * _f2 * _D**2 * sp.Abs(_omega_finale - _omega_iniziale) / ((_D + _a - _f2) * sp.Abs(_x_finale - _x_iniziale)) dati.calc_var(formula_c, "c") dati.analisi_stat return (formula_c,) @app.cell(hide_code=True) def _(mo): mo.md(r""" ## Media pesata delle stime di $c$ Le stime indipendenti di $c$ — una per riga di `C.csv` — vengono combinate con pesi $w_i = 1/u_{c,i}^2$. Prima di combinarle, `media_pesata` applica il criterio di Chauvenet fra le stime; i punti rimossi sono riportati in `out`. Si riportano $\tilde\chi^2$ (chi quadro ridotto), i gradi di libertà e $P(\chi^2)$ riferiti ai soli punti mantenuti. """) return @app.cell def _(dati): c_stat, uc_stat, chi2, dof, P, out= dati.media_pesata("c", info_outlier=True) print(f"c (media pesata) = {c_stat:.6e} +- {uc_stat:.2e}") print(f"chi2 = {chi2:.3f}, dof = {dof}, P(chi2) = {P:.3f}") print(out) return c_stat, uc_stat @app.cell(hide_code=True) def _(mo): mo.md(r""" ## Risultato finale e confronto col valore vero """) return @app.cell def _(c_stat, compat, mo, uc_stat): c_vero = 299792458.0 _k, _diff = compat(c_stat, c_vero, uc_stat, 0.0) mo.md( f""" $$c = \\left({c_stat/1e8:.3f} \\pm {uc_stat/1e8:.3f}\\right)\\times 10^8\\ \\mathrm{{m/s}}$$ Confronto col valore tabulato $c_{{tab}} = {c_vero/1e8:.3f}\\times 10^8\\ \\mathrm{{m/s}}$: differenza ${_diff:.2E}$ m/s, fattore di compatibilita **k = {_k:.2f}**, calcolato con $u_{{tab}}=0$, cioe $k=|c-c_{{tab}}|/u_c$. {"✅ compatibile ($k<2$)" if _k < 2 else "⚠️ non compatibile ($k\\ge 2$)"} """ ) return if __name__ == "__main__": app.run()