Files
LOFM/velocitaC.py
T
2026-11-09 18:20:54 +01:00

263 lines
7.1 KiB
Python

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()