From b40ee49528ad755871ac451c0758c2aaf1fe5594 Mon Sep 17 00:00:00 2001 From: Davide Grilli Date: Sun, 5 Jul 2026 14:25:57 +0200 Subject: [PATCH] Aggiunge gruppo di sorgenti multiple rigide lungo x Sostituisce la singola sorgente con un gruppo di N sorgenti equidistanti che si muovono insieme, sommando i contributi di flusso sul sensore. --- config.py | 17 ++++++++-- simulate.py | 92 ++++++++++++++++++++++++++++++++++++++--------------- 2 files changed, 81 insertions(+), 28 deletions(-) diff --git a/config.py b/config.py index e7499e9..f49f07d 100644 --- a/config.py +++ b/config.py @@ -66,8 +66,12 @@ ARIA = { } SORGENTE = { - # La sorgente parte da x = x_inizio_m e si muove verso x_fine_m. - "x_inizio_m": -1.0, + # Distanza dal sensore, all'inizio della corsa, della sorgente del + # gruppo più vicina al sensore (quella che lo raggiunge per prima). + "x_inizio_m": 1.0, + + # Distanza dal sensore, alla fine della corsa, della sorgente del + # gruppo più lontana dal sensore (quella che lo supera per ultima). "x_fine_m": 5.0, # Coordinata laterale fissa della proiezione del sensore sul lato caldo. @@ -81,11 +85,18 @@ SORGENTE = { # Velocità della sorgente lungo x. "velocita_m_s": 2.0, + # Numero di sorgenti equidistanti che si muovono insieme come un gruppo + # rigido (stessa velocità, sigma, flusso ed efficienza). + "numero_sorgenti": 3, + + # Distanza lungo x tra sorgenti consecutive del gruppo. + "distanza_sorgenti_m": 0.5, + # Raggio del punto gaussiano. Valore più alto = impronta termica più larga. "sigma_punto_m": 0.012, # Flusso termico incidente massimo prima della correzione per efficienza. - "flusso_termico_picco_W_m2": 16500000.0, + "flusso_termico_picco_W_m2": 5500000.0, # Frazione del flusso incidente che diventa effettivamente calore nella piastra. "efficienza_riscaldamento": 0.35, diff --git a/simulate.py b/simulate.py index 3c97883..f306cae 100644 --- a/simulate.py +++ b/simulate.py @@ -27,46 +27,86 @@ def calcola_skin_depth_m(materiale: dict, frequenza_hz: float) -> float: return math.sqrt(2.0 * rho_e / (omega * mu)) +def _spread_sorgenti_m(sorgente: dict) -> float: + # Distanza lungo x tra la prima e l'ultima sorgente del gruppo. + numero_sorgenti = sorgente.get("numero_sorgenti", 1) + distanza = sorgente.get("distanza_sorgenti_m", 0.0) + return (numero_sorgenti - 1) * distanza + + +def _x_riferimento_iniziale_m(sorgente: dict) -> float: + # Posizione a t=0 della sorgente di indice 0 (quella più arretrata nel + # verso di marcia). x_inizio_m è la distanza dal sensore della sorgente + # più avanzata (che quindi lo raggiunge per prima). + spread = _spread_sorgenti_m(sorgente) + x_sensore = sorgente["x_sensore_m"] + x_inizio = sorgente["x_inizio_m"] + if sorgente["velocita_m_s"] >= 0: + return (x_sensore - x_inizio) - spread + return (x_sensore + x_inizio) + spread + + +def _x_riferimento_finale_m(sorgente: dict) -> float: + # Posizione di fine corsa della sorgente di indice 0. x_fine_m è la + # distanza dal sensore della sorgente più arretrata (che quindi lo + # supera per ultima). + x_sensore = sorgente["x_sensore_m"] + x_fine = sorgente["x_fine_m"] + if sorgente["velocita_m_s"] >= 0: + return x_sensore + x_fine + return x_sensore - x_fine + + def x_sorgente_al_tempo(sorgente: dict, t_s: float) -> float: - return sorgente["x_inizio_m"] + sorgente["velocita_m_s"] * t_s + return _x_riferimento_iniziale_m(sorgente) + sorgente["velocita_m_s"] * t_s -def sorgente_attiva(sorgente: dict, x_m: float) -> bool: - if not sorgente.get("zero_dopo_fine", True): - return True - - inizio = sorgente["x_inizio_m"] - fine = sorgente["x_fine_m"] - v = sorgente["velocita_m_s"] - +def _intervallo_attivo(inizio: float, fine: float, v: float, x_m: float) -> bool: if v >= 0: return inizio <= x_m <= fine return fine <= x_m <= inizio def flusso_termico_incidente_W_m2(sorgente: dict, t_s: float) -> tuple[float, float]: - # Restituisce x_sorgente_m e flusso_termico_efficace_W_m2. + # Restituisce x_sorgente_m (posizione della sorgente di riferimento) e + # flusso_termico_efficace_W_m2 (somma dei contributi di tutte le sorgenti). # - # Il movimento è rappresentato con un'impronta gaussiana centrata sulla - # sorgente in moto. Il modello 1D vede solo il flusso lungo la linea - # che passa per il sensore fisso. - x = x_sorgente_al_tempo(sorgente, t_s) + # Il movimento è rappresentato con un'impronta gaussiana centrata su + # ciascuna sorgente in moto. Più sorgenti equidistanti si muovono insieme + # come un gruppo rigido: condividono velocità, sigma e flusso di picco, e + # sono sfalsate lungo x di un multiplo di "distanza_sorgenti_m". Il + # modello 1D vede solo il flusso lungo la linea che passa per il sensore + # fisso, sommando il contributo di tutte le sorgenti attive. + x_rif_iniziale = _x_riferimento_iniziale_m(sorgente) + x_rif_finale = _x_riferimento_finale_m(sorgente) + x_riferimento = x_rif_iniziale + sorgente["velocita_m_s"] * t_s - if not sorgente_attiva(sorgente, x): - return x, 0.0 - - dx = x - sorgente["x_sensore_m"] + numero_sorgenti = sorgente.get("numero_sorgenti", 1) + distanza = sorgente.get("distanza_sorgenti_m", 0.0) + v = sorgente["velocita_m_s"] + zero_dopo_fine = sorgente.get("zero_dopo_fine", True) dy = sorgente["offset_y_percorso_m"] sigma = sorgente["sigma_punto_m"] - gaussiana = math.exp(-0.5 * (dx * dx + dy * dy) / (sigma * sigma)) + q_totale = 0.0 + for i in range(numero_sorgenti): + x_i = x_riferimento + i * distanza - q = ( - sorgente["flusso_termico_picco_W_m2"] - * sorgente["efficienza_riscaldamento"] - * gaussiana - ) - return x, q + if zero_dopo_fine: + inizio_i = x_rif_iniziale + i * distanza + fine_i = x_rif_finale + i * distanza + if not _intervallo_attivo(inizio_i, fine_i, v, x_i): + continue + + dx = x_i - sorgente["x_sensore_m"] + gaussiana = math.exp(-0.5 * (dx * dx + dy * dy) / (sigma * sigma)) + q_totale += ( + sorgente["flusso_termico_picco_W_m2"] + * sorgente["efficienza_riscaldamento"] + * gaussiana + ) + + return x_riferimento, q_totale def riscaldamento_volumetrico_W_m3( @@ -315,6 +355,8 @@ def simula_singolo(cfg_run: dict, output_csv: Path, rng: random.Random) -> dict: "x_sensore_m": sorgente["x_sensore_m"], "offset_y_percorso_m": sorgente["offset_y_percorso_m"], "velocita_m_s": sorgente["velocita_m_s"], + "numero_sorgenti": sorgente.get("numero_sorgenti", 1), + "distanza_sorgenti_m": sorgente.get("distanza_sorgenti_m", 0.0), "sigma_punto_m": sorgente["sigma_punto_m"], "flusso_termico_picco_W_m2": sorgente["flusso_termico_picco_W_m2"], "efficienza_riscaldamento": sorgente["efficienza_riscaldamento"],