263 lines
7.1 KiB
Python
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()
|