126 lines
5.1 KiB
Python
126 lines
5.1 KiB
Python
# Sorgenti a induzione in movimento sulla superficie esterna della fascetta.
|
|||
|
|
#
|
||
|
|
# Il gruppo è formato da più sorgenti equidistanti che traslano insieme lungo
|
||
|
|
# x con la stessa velocità. Tutte percorrono la stessa generatrice, cioè la
|
||
|
|
# stessa posizione circonferenziale, definita dall'offset rispetto al punto
|
||
|
|
# osservato dal sensore.
|
||
|
|
#
|
||
|
|
# Il flusso termico incidente su un punto della superficie è
|
||
|
|
#
|
||
|
|
# q''(x, arco, t) = q_picco * somma_i exp(-((x - x_i(t))^2) / (2 sigma^2))
|
||
|
|
# * exp(-(delta_arco^2) / (2 sigma^2))
|
||
|
|
#
|
||
|
|
# dove delta_arco è la distanza circonferenziale minima tra il punto e il
|
||
|
|
# percorso delle sorgenti, misurata lungo la circonferenza e riavvolta su
|
||
|
|
# 2*pi*R: passare da un lato o dall'altro della fascetta è equivalente.
|
||
|
|
#
|
||
|
|
# Il flusso è quindi il prodotto di un fattore che dipende solo da x e varia
|
||
|
|
# nel tempo e di un fattore che dipende solo dall'arco ed è costante: questa
|
||
|
|
# separazione è sfruttata dall'assemblaggio del vettore sorgente.
|
||
|
|
|
||
|
|
import math
|
||
|
|
|
||
|
|
import numpy as np
|
||
|
|
|
||
|
|
|
||
|
|
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, x_sensore_m: float) -> float:
|
||
|
|
# Posizione a t=0 della sorgente di indice 0. Le sorgenti i sono a
|
||
|
|
# x_i = riferimento + i * distanza, quindi per v >= 0 l'indice 0 è la
|
||
|
|
# più arretrata nel verso di marcia, per v < 0 è la più avanzata.
|
||
|
|
# x_inizio_m è la distanza dal sensore della sorgente più avanzata
|
||
|
|
# (quella che lo raggiunge per prima).
|
||
|
|
spread = _spread_sorgenti_m(sorgente)
|
||
|
|
x_inizio = sorgente["x_inizio_m"]
|
||
|
|
if sorgente["velocita_m_s"] >= 0:
|
||
|
|
return (x_sensore_m - x_inizio) - spread
|
||
|
|
return x_sensore_m + x_inizio
|
||
|
|
|
||
|
|
|
||
|
|
def x_riferimento_finale_m(sorgente: dict, x_sensore_m: float) -> float:
|
||
|
|
# Posizione di fine corsa della sorgente di indice 0. x_fine_m è la
|
||
|
|
# distanza dal sensore della sorgente più arretrata nel verso di marcia
|
||
|
|
# (quella che lo supera per ultima).
|
||
|
|
spread = _spread_sorgenti_m(sorgente)
|
||
|
|
x_fine = sorgente["x_fine_m"]
|
||
|
|
if sorgente["velocita_m_s"] >= 0:
|
||
|
|
return x_sensore_m + x_fine
|
||
|
|
return (x_sensore_m - x_fine) - spread
|
||
|
|
|
||
|
|
|
||
|
|
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 theta_percorso_rad(sorgente: dict, raggio_medio_m: float) -> float:
|
||
|
|
# Posizione circonferenziale del percorso delle sorgenti, ricavata
|
||
|
|
# dall'offset in arco rispetto al sensore, che sta per convenzione a
|
||
|
|
# theta = 0.
|
||
|
|
return sorgente["offset_y_percorso_m"] / raggio_medio_m
|
||
|
|
|
||
|
|
|
||
|
|
def fattore_circonferenziale(
|
||
|
|
sorgente: dict, raggio_medio_m: float, arco_m: np.ndarray
|
||
|
|
) -> np.ndarray:
|
||
|
|
# Attenuazione gaussiana del flusso in funzione della distanza
|
||
|
|
# circonferenziale dal percorso delle sorgenti. La distanza è riavvolta
|
||
|
|
# sulla circonferenza, così la sorgente scalda anche i punti raggiunti
|
||
|
|
# "dall'altra parte" della fascetta.
|
||
|
|
circonferenza = 2.0 * math.pi * raggio_medio_m
|
||
|
|
delta = arco_m - sorgente["offset_y_percorso_m"]
|
||
|
|
delta = (delta + circonferenza / 2.0) % circonferenza - circonferenza / 2.0
|
||
|
|
sigma = sorgente["sigma_punto_m"]
|
||
|
|
return np.exp(-0.5 * (delta * delta) / (sigma * sigma))
|
||
|
|
|
||
|
|
|
||
|
|
def profilo_flusso_x_W_m2(
|
||
|
|
sorgente: dict, x_sensore_m: float, t_s: float, x_m: np.ndarray
|
||
|
|
) -> tuple[float, np.ndarray]:
|
||
|
|
# Restituisce la posizione della sorgente di riferimento e il fattore
|
||
|
|
# assiale del flusso efficace [W/m²], somma dei contributi di tutte le
|
||
|
|
# sorgenti attive valutata nei punti x richiesti.
|
||
|
|
x_rif_iniziale = x_riferimento_iniziale_m(sorgente, x_sensore_m)
|
||
|
|
x_rif_finale = x_riferimento_finale_m(sorgente, x_sensore_m)
|
||
|
|
x_riferimento = x_rif_iniziale + sorgente["velocita_m_s"] * t_s
|
||
|
|
|
||
|
|
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)
|
||
|
|
sigma = sorgente["sigma_punto_m"]
|
||
|
|
|
||
|
|
q_picco = (
|
||
|
|
sorgente["flusso_termico_picco_W_m2"] * sorgente["efficienza_riscaldamento"]
|
||
|
|
)
|
||
|
|
|
||
|
|
q_x = np.zeros_like(x_m)
|
||
|
|
for i in range(numero_sorgenti):
|
||
|
|
x_i = x_riferimento + i * distanza
|
||
|
|
|
||
|
|
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_m - x_i
|
||
|
|
q_x += q_picco * np.exp(-0.5 * (dx * dx) / (sigma * sigma))
|
||
|
|
|
||
|
|
return x_riferimento, q_x
|
||
|
|
|
||
|
|
|
||
|
|
def posizioni_sorgenti_m(sorgente: dict, x_riferimento_m: float) -> np.ndarray:
|
||
|
|
# Posizioni lungo x di tutte le sorgenti del gruppo a partire da quella
|
||
|
|
# di riferimento.
|
||
|
|
numero_sorgenti = sorgente.get("numero_sorgenti", 1)
|
||
|
|
distanza = sorgente.get("distanza_sorgenti_m", 0.0)
|
||
|
|
return x_riferimento_m + np.arange(numero_sorgenti) * distanza
|