Files
davideandClaude Opus 5 14226234bd Aggiunge la mesh a elementi shell della fascetta e il main.py
Introduce mesh.py, che costruisce una griglia strutturata di quadrilateri
a 4 nodi sulla superficie media del cilindro, chiusa lungo la
circonferenza, come base per la futura analisi FEM. plot_mesh.py la
visualizza in vista isometrica e main.py raccoglie tutte le azioni del
progetto dietro un unico punto di ingresso.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-03 09:10:11 +02:00

115 lines
4.3 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Generazione della mesh a elementi shell quadrangolari della fascetta.
#
# La fascetta è modellata come la superficie media di un cilindro: gli
# elementi sono quadrilateri a 4 nodi disposti su una griglia strutturata
# n_elementi_x × n_elementi_circonferenza. Lo spessore della parete non è
# discretizzato, è un attributo degli elementi shell.
#
# Sistema di coordinate globale (coerente con plot_animazione_3d.py):
# - x = asse del cilindro, da 0 a lunghezza
# - y, z = piano della sezione circolare, con y = R·sin(theta), z = R·cos(theta)
# - theta = 0 corrisponde al piano del sensore (y = 0), cresce in verso
# antiorario nel piano y-z
#
# La mesh è chiusa lungo la circonferenza: l'ultima colonna di elementi
# richiude sulla prima riga di nodi, senza nodi duplicati sulla cucitura.
import numpy as np
from config import FASCETTA, MESH
def raggio_medio_m(fascetta: dict = FASCETTA) -> float:
# Raggio della superficie media della parete: il diametro configurato è
# quello esterno, la shell sta a metà dello spessore.
diametro_m = fascetta["diametro_mm"] / 1000.0
spessore_m = fascetta["spessore_mm"] / 1000.0
return (diametro_m - spessore_m) / 2.0
def genera_mesh(fascetta: dict = FASCETTA, mesh: dict = MESH) -> dict:
"""Costruisce la mesh shell quadrangolare della fascetta.
Restituisce un dizionario con:
- nodi: array (n_nodi, 3) con le coordinate globali [m]
- elementi: array (n_elementi, 4) con gli indici dei nodi, in verso
antiorario visto dall'esterno del cilindro
- indice_nodo: array (n_x + 1, n_theta) che mappa (i, j) sull'indice
globale del nodo
- x_nodi_m, theta_nodi_rad: coordinate parametriche della griglia
- spessore_m, raggio_m, lunghezza_m: geometria della shell
"""
n_x = int(mesh["n_elementi_x"])
n_theta = int(mesh["n_elementi_circonferenza"])
if n_x < 1 or n_theta < 3:
raise ValueError(
"Servono almeno 1 elemento lungo x e 3 lungo la circonferenza."
)
lunghezza_m = fascetta["lunghezza_mm"] / 1000.0
spessore_m = fascetta["spessore_mm"] / 1000.0
raggio_m = raggio_medio_m(fascetta)
# Lungo x la griglia è aperta (n_x + 1 file di nodi), lungo theta è
# chiusa (n_theta file, l'ultima si ricongiunge alla prima).
x_nodi_m = np.linspace(0.0, lunghezza_m, n_x + 1)
theta_nodi_rad = np.linspace(0.0, 2.0 * np.pi, n_theta, endpoint=False)
X, Theta = np.meshgrid(x_nodi_m, theta_nodi_rad, indexing="ij")
nodi = np.column_stack(
(
X.ravel(),
(raggio_m * np.sin(Theta)).ravel(),
(raggio_m * np.cos(Theta)).ravel(),
)
)
indice_nodo = np.arange((n_x + 1) * n_theta).reshape(n_x + 1, n_theta)
i = np.arange(n_x)[:, None]
j = np.arange(n_theta)[None, :]
j_succ = (j + 1) % n_theta
elementi = np.stack(
(
np.broadcast_to(indice_nodo[i, j], (n_x, n_theta)),
np.broadcast_to(indice_nodo[i + 1, j], (n_x, n_theta)),
np.broadcast_to(indice_nodo[i + 1, j_succ], (n_x, n_theta)),
np.broadcast_to(indice_nodo[i, j_succ], (n_x, n_theta)),
),
axis=-1,
).reshape(-1, 4)
return {
"nodi": nodi,
"elementi": elementi,
"indice_nodo": indice_nodo,
"x_nodi_m": x_nodi_m,
"theta_nodi_rad": theta_nodi_rad,
"n_elementi_x": n_x,
"n_elementi_circonferenza": n_theta,
"spessore_m": spessore_m,
"raggio_m": raggio_m,
"lunghezza_m": lunghezza_m,
}
def riepilogo_mesh(mesh_dati: dict) -> str:
n_nodi = len(mesh_dati["nodi"])
n_elementi = len(mesh_dati["elementi"])
passo_x_mm = 1000.0 * mesh_dati["lunghezza_m"] / mesh_dati["n_elementi_x"]
passo_circ_mm = (
1000.0
* 2.0
* np.pi
* mesh_dati["raggio_m"]
/ mesh_dati["n_elementi_circonferenza"]
)
return (
f"Mesh shell: {n_elementi} elementi quadrangolari, {n_nodi} nodi\n"
f" griglia: {mesh_dati['n_elementi_x']} (x) × "
f"{mesh_dati['n_elementi_circonferenza']} (circonferenza)\n"
f" passo elemento: {passo_x_mm:.2f} mm (x), {passo_circ_mm:.2f} mm (circonferenza)\n"
f" raggio medio: {1000.0 * mesh_dati['raggio_m']:.2f} mm, "
f"spessore shell: {1000.0 * mesh_dati['spessore_m']:.3f} mm"
)