120 lines
4.3 KiB
Python
120 lines
4.3 KiB
Python
# Generazione della mesh a elementi shell quadrilateri sulla superficie
|
||
# cilindrica della fascetta.
|
||
#
|
||
# La superficie è parametrizzata da (x, theta): x è la coordinata assiale,
|
||
# theta quella circonferenziale. Al posto di theta si usa quasi ovunque
|
||
# l'ascissa curvilinea arco = raggio_medio * theta, così la shell diventa un
|
||
# rettangolo lunghezza × circonferenza e gli elementi sono tutti identici.
|
||
#
|
||
# I nodi formano una griglia (n_elementi_x + 1) × n_elementi_theta: in
|
||
# direzione circonferenziale non c'è un nodo finale distinto perché theta = 0
|
||
# e theta = 2*pi sono lo stesso nodo. La periodicità è quindi strutturale,
|
||
# nasce dalla connettività e non da vincoli imposti a posteriori.
|
||
#
|
||
# Ordine dei nodi locali di ogni elemento (antiorario nel piano x-arco):
|
||
#
|
||
# 4 ---- 3 arco
|
||
# | | ^
|
||
# | | |
|
||
# 1 ---- 2 +---> x
|
||
|
||
import numpy as np
|
||
|
||
|
||
def costruisci_mesh_cilindrica(
|
||
lunghezza_m: float,
|
||
raggio_medio_m: float,
|
||
n_elementi_x: int,
|
||
n_elementi_theta: int,
|
||
) -> dict:
|
||
# Costruisce nodi e connettività della shell cilindrica.
|
||
n_nodi_x = n_elementi_x + 1
|
||
n_nodi = n_nodi_x * n_elementi_theta
|
||
n_elementi = n_elementi_x * n_elementi_theta
|
||
|
||
lato_x_m = lunghezza_m / n_elementi_x
|
||
circonferenza_m = 2.0 * np.pi * raggio_medio_m
|
||
lato_arco_m = circonferenza_m / n_elementi_theta
|
||
|
||
x_nodi_m = np.arange(n_nodi_x) * lato_x_m
|
||
theta_nodi_rad = np.arange(n_elementi_theta) * (2.0 * np.pi / n_elementi_theta)
|
||
arco_nodi_m = theta_nodi_rad * raggio_medio_m
|
||
|
||
# Connettività: elemento (i, j) collega i nodi (i, j), (i+1, j),
|
||
# (i+1, j+1), (i, j+1), con j+1 riavvolto sulla circonferenza.
|
||
i_elem = np.repeat(np.arange(n_elementi_x), n_elementi_theta)
|
||
j_elem = np.tile(np.arange(n_elementi_theta), n_elementi_x)
|
||
j_succ = (j_elem + 1) % n_elementi_theta
|
||
|
||
def indice(i, j):
|
||
return i * n_elementi_theta + j
|
||
|
||
connettivita = np.stack(
|
||
[
|
||
indice(i_elem, j_elem),
|
||
indice(i_elem + 1, j_elem),
|
||
indice(i_elem + 1, j_succ),
|
||
indice(i_elem, j_succ),
|
||
],
|
||
axis=1,
|
||
)
|
||
|
||
# Segmenti dei due bordi assiali (x = 0 e x = lunghezza): anelli chiusi di
|
||
# n_elementi_theta segmenti, usati per la convezione sullo spessore.
|
||
j = np.arange(n_elementi_theta)
|
||
j_dopo = (j + 1) % n_elementi_theta
|
||
segmenti_bordo = np.concatenate(
|
||
[
|
||
np.stack([indice(0, j), indice(0, j_dopo)], axis=1),
|
||
np.stack(
|
||
[indice(n_elementi_x, j), indice(n_elementi_x, j_dopo)], axis=1
|
||
),
|
||
]
|
||
)
|
||
|
||
return {
|
||
"n_elementi_x": n_elementi_x,
|
||
"n_elementi_theta": n_elementi_theta,
|
||
"n_elementi": n_elementi,
|
||
"n_nodi_x": n_nodi_x,
|
||
"n_nodi": n_nodi,
|
||
"lunghezza_m": lunghezza_m,
|
||
"raggio_medio_m": raggio_medio_m,
|
||
"circonferenza_m": circonferenza_m,
|
||
"lato_x_m": lato_x_m,
|
||
"lato_arco_m": lato_arco_m,
|
||
"x_nodi_m": x_nodi_m,
|
||
"theta_nodi_rad": theta_nodi_rad,
|
||
"arco_nodi_m": arco_nodi_m,
|
||
"connettivita": connettivita,
|
||
"segmenti_bordo": segmenti_bordo,
|
||
"area_totale_m2": lunghezza_m * circonferenza_m,
|
||
}
|
||
|
||
|
||
def campo_su_griglia(mesh: dict, T: np.ndarray) -> np.ndarray:
|
||
# Rimappa il vettore nodale sulla griglia (n_nodi_x, n_elementi_theta),
|
||
# comoda per le mappe sviluppate e per le superfici 3D.
|
||
return T.reshape(mesh["n_nodi_x"], mesh["n_elementi_theta"])
|
||
|
||
|
||
def coordinate_3d(mesh: dict, chiudi_circonferenza: bool = True):
|
||
# Coordinate cartesiane dei nodi per la vista 3D: x lungo l'asse del
|
||
# cilindro, y e z sulla sezione circolare.
|
||
#
|
||
# Con chiudi_circonferenza si ripete la prima colonna in coda, così la
|
||
# superficie disegnata non mostra una fessura in theta = 0. È solo una
|
||
# necessità di disegno: il nodo ripetuto non è un grado di libertà.
|
||
theta = mesh["theta_nodi_rad"]
|
||
if chiudi_circonferenza:
|
||
theta = np.append(theta, 2.0 * np.pi)
|
||
X, Theta = np.meshgrid(mesh["x_nodi_m"], theta, indexing="ij")
|
||
R = mesh["raggio_medio_m"]
|
||
return X, R * np.sin(Theta), R * np.cos(Theta)
|
||
|
||
|
||
def chiudi_campo(campo: np.ndarray) -> np.ndarray:
|
||
# Ripete la prima colonna circonferenziale in coda, in accordo con
|
||
# coordinate_3d(chiudi_circonferenza=True).
|
||
return np.concatenate([campo, campo[:, :1]], axis=1)
|