OpenFOAM: generazione e controllo delle mesh con blockMesh e snappyHexMesh
Sesta guida operativa su OpenFOAM installato su un server Linux e usato da Windows, dopo quelle su server e trasferimenti, monitoraggio e convergenza, analisi quantitativa con ParaView, verifica e validazione e campagne parametriche. Questa torna a monte del calcolo: la generazione e il controllo della mesh con blockMesh e snappyHexMesh. I riquadri di approfondimento si aprono con un clic: contengono i file completi del pacchetto, i conti attesi per verificare ogni passaggio e procedure aggiuntive.
Dalla geometria CAD a blockMesh e snappyHexMesh
Manuale operativo per OpenFOAM Foundation 14 · Windows come postazione di lavoro · server Linux per il calcolo
Edizione: ottobre 2026 · Ambito: pre-processing e diagnostica della mesh · Livello: intermedio, con richiami facoltativi · Licenza degli esempi: GNU GPL v3 o successiva.
Obiettivo della guida. Produrre una mesh controllabile e riproducibile; comprendere che cosa è stato discretizzato, quale fluido è stato mantenuto e se la mesh è idonea alla successiva simulazione. La guida non promette che una mesh che supera checkMesh garantisca un risultato CFD accurato: l’indipendenza dalla mesh resta oggetto della Guida 4.

1. Il posto della mesh nel workflow scientifico
Nelle guide precedenti abbiamo preparato i casi, interpretato i log, analizzato i risultati e affrontato la verifica numerica. Qui torniamo a monte: il dominio continuo deve essere rappresentato da celle, facce e patch di contorno. La geometria dell’oggetto e la geometria del dominio fluido non coincidono: il solido può essere una superficie interna da escludere, mentre le condizioni al contorno sono applicate sulle facce che delimitano il fluido.
Il percorso delle due esercitazioni è:
Esercitazione A: system/blockMeshDict → blockMesh → constant/polyMesh → checkMesh → vista in ParaView.
Esercitazione B: griglia di sfondo di blockMesh + constant/triSurface/sphere.stl → surfaceCheck → snappyHexMesh → checkMesh → vista in ParaView.
I due casi contenuti nel pacchetto riguardano solo la generazione della mesh: non comprendono ancora una simulazione di velocità o pressione. Ciò permette di individuare i problemi geometrici prima di aggiungere i modelli fisici.
Richiamo — che cosa è una cella FV? In un metodo ai volumi finiti, le equazioni di conservazione vengono integrate su piccoli volumi: le facce servono a determinare i flussi scambiati con i volumi adiacenti. Una cella più piccola può migliorare la risoluzione spaziale, ma non compensa da sola geometrie sbagliate, condizioni al contorno errate o algoritmi inadeguati.
Approfondimento · Facce, owner e neighbour: come OpenFOAM descrive una mesh
OpenFOAM usa una descrizione basata sulle facce: ogni cella è un poliedro qualsiasi delimitato da facce, e i file di constant/polyMesh contengono soltanto liste. points elenca le coordinate dei vertici; faces elenca ogni faccia come sequenza di indici di punti; owner assegna a ogni faccia la cella proprietaria; neighbour assegna alle sole facce interne la cella confinante; boundary raggruppa le facce di contorno in patch.
- Per una faccia interna, la cella owner ha l’indice più basso e la normale della faccia punta dall’owner verso il neighbour. Il flusso
phisu quella faccia è positivo quando esce dall’owner. - Le facce di contorno hanno solo l’owner e la normale punta verso l’esterno del dominio. Sono memorizzate in fondo alla lista
faces, patch dopo patch: per questo ogni patch inboundaryè descritta dastartFaceenFaces.
Per il canale dell’esercitazione A i numeri si possono prevedere prima di lanciare blockMesh e confrontare poi con il log:
| Grandezza | Calcolo | Valore |
|---|---|---|
| celle | 60 × 20 × 10 | 12 000 |
| punti | 61 × 21 × 11 | 14 091 |
| facce interne | 59·20·10 + 60·19·10 + 60·20·9 | 34 000 |
| inlet / outlet | 20 × 10 ciascuna | 200 + 200 |
| walls | 2·(60·10) + 2·(60·20) | 3 600 |
| facce totali | 34 000 + 4 000 | 38 000 |
Di conseguenza in constant/polyMesh/boundary ci si aspetta una struttura di questo tipo (OpenFOAM può aggiungere voci come inGroups):
3
(
inlet
{
type patch;
nFaces 200;
startFace 34000;
}
outlet
{
type patch;
nFaces 200;
startFace 34200;
}
walls
{
type wall;
nFaces 3600;
startFace 34400;
}
)
Approfondimento · Dove nasce l’errore di discretizzazione e perché dipende dalla forma delle celle
Nel metodo ai volumi finiti di OpenFOAM le incognite sono memorizzate al centro delle celle e i flussi si calcolano sulle facce interpolando i valori delle due celle adiacenti. Con il teorema di Gauss, il gradiente di cella diventa una somma sulle facce: ∇φ ≈ (1/V)·Σ S_f·φ_f. Uno schema «del secondo ordine» lo è davvero solo se tre condizioni geometriche sono rispettate: il segmento fra i centri è parallelo alla normale della faccia (ortogonalità), lo attraversa nel suo centro (assenza di skewness) e le due celle hanno dimensioni simili (rapporto di volume vicino a 1).
Quando una di queste condizioni viene meno compaiono termini di errore proporzionali alla deviazione geometrica che non diminuiscono con il quadrato della dimensione di cella: in pratica l’ordine effettivo scende verso 1. È il motivo per cui checkMesh misura proprio non-ortogonalità, skewness e rapporti di volume, ed è la ragione per cui nella Guida 4 l’ordine osservato può risultare inferiore a quello nominale degli schemi.
Approfondimento · Il dominio dell’esercitazione visto da un ingegnere: bloccaggio e distanze
| Grandezza | Valore | Commento |
|---|---|---|
| rapporto di bloccaggio | π·0.025² / (0.2·0.1) ≈ 9.8 % | in aerodinamica esterna si cerca di restare sotto qualche punto percentuale |
| distanza a monte | 0.195 m ≈ 3.9 D | l’ingresso uniforme è vicino all’ostacolo |
| distanza a valle | 0.355 m ≈ 7.1 D | scia corta per studi di distacco |
| gioco verso le pareti in z | 0.025 m = 0.5 D | confinamento forte nello spessore |
| gioco verso le pareti in y | 0.075 m = 1.5 D | — |
Il caso è pensato per esercitarsi sulla mesh, non per misurare la resistenza di una sfera isolata: con questi rapporti la parete e l’uscita influenzano il campo. In un progetto reale le dimensioni del dominio si decidono prima della mesh, in base alla grandezza da misurare, e si verificano con uno studio di sensibilità alla posizione dei contorni, separato da quello sulla risoluzione.
2. Prima di iniziare: strumenti e convenzioni
Si assumono OpenFOAM Foundation 14 sul server Linux, un collegamento SSH da PowerShell, FileZilla in SFTP e Notepad++ per modificare i file sul PC Windows. Per ispezionare la geometria si usa ParaView 5.13.2 collegato a pvserver, come spiegato nelle Guide 1 e 3. Non sono richiesti editor remoti aggiuntivi.
Nel pacchetto troverai le cartelle examples/01_channel_blockMesh/ e examples/02_sphere_snappy/, lo script scripts/run_mesh_case.sh e figure originali. Trasferisci l’intero pacchetto delle esercitazioni in una directory personale sul server, senza sovrascrivere altri casi. I file Linux di shell devono conservare i fine riga LF, non CRLF; in Notepad++ scegli Modifica → Conversione fine riga → Unix (LF), codifica UTF-8. Dopo il trasferimento puoi verificare i permessi dello script.
ssh utente@server.example
source /opt/OpenFOAM-14/etc/bashrc # modifica se installato altrove
foamVersion
cd ~/OpenFOAM
chmod +x mesh-guide6/scripts/run_mesh_case.sh
Il percorso ~/OpenFOAM/mesh-guide6 è solo un esempio: adatta cd alla directory in cui hai caricato lo ZIP estratto. I file di geometria di questa guida usano metri. Un CAD esportato in millimetri non diventa automaticamente metrico quando è salvato come STL.
Approfondimento · Controllare l’ambiente prima di ogni sessione
Le utility di meshing fanno parte della stessa installazione del solver: se manca anche una sola, l’ambiente non è stato caricato o l’installazione è incompleta. Il controllo richiede pochi secondi ed evita errori fuorvianti a metà procedura.
source /opt/OpenFOAM-14/etc/bashrc
foamVersion
which blockMesh snappyHexMesh surfaceCheck checkMesh surfaceFeatures
echo "$WM_PROJECT_DIR"
# per caricarlo a ogni login (adattare il percorso):
echo 'source /opt/OpenFOAM-14/etc/bashrc' >> ~/.bashrc
Il file STL può essere ASCII (testo, inizia con solid) o binario. SFTP trasferisce sempre i byte senza modificarli, quindi entrambi arrivano integri; la conversione dei fine riga riguarda solo gli script e i dizionari modificati con Notepad++. Il file vuoto mesh.foam presente nei due casi serve soltanto a ParaView per riconoscere la directory come caso OpenFOAM.
Approfondimento · STL ASCII o binario, e trasferire mesh grandi
Lo STL ASCII della sfera occupa 238 410 byte per 896 triangoli, circa 266 byte per triangolo. Lo stesso contenuto in formato binario occupa 80 + 4 + 50·896 = 44 884 byte, cinque volte meno. Per superfici CAD da milioni di triangoli il formato binario è d’obbligo; il formato ASCII resta comodo per esempi didattici perché si legge con un editor. OpenFOAM legge entrambi.
Per mesh grandi non si trasferisce polyMesh file per file con FileZilla: si comprime sul server e si trasferisce un solo archivio, oppure non si trasferisce affatto e si guarda la mesh via pvserver. In controlDict, writeFormat binary; e writeCompression on; riducono ulteriormente lo spazio occupato.
tar czf sphere_mesh.tar.gz constant/polyMesh logs
du -sh constant/polyMesh sphere_mesh.tar.gz
3. Struttura di un caso e ruolo dei file
| Posizione | Funzione | Modifica tipica |
|---|---|---|
system/blockMeshDict | Vertici, blocchi, gradi di raffinamento, patch | Dimensioni e numero di celle |
system/snappyHexMeshDict | Superficie, refinement, snapping, strati | Livelli e controlli |
system/controlDict | Parametri generali richiesti dalle utility | Raramente nel meshing |
constant/triSurface/*.stl | Superfici triangolate in coordinate del caso | Geometria CAD |
constant/polyMesh/ | Mesh risultante, scritta dalle utility | Non modificare a mano |
logs/ | Evidenza dei controlli eseguiti | Conservare e analizzare |
Dentro polyMesh, i file points, faces, owner, neighbour e boundary codificano coordinate, connettività e nomi delle patch. Non vanno scambiati per file CAD modificabili da Notepad++. La documentazione ufficiale OpenFOAM 14 descrive questa rappresentazione basata sulle facce.
Approfondimento · Il controlDict dei casi di meshing, completo
Anche se non si risolve alcuna equazione, le utility leggono system/controlDict per sapere in quale istante scrivere, con che formato e precisione. Il file è identico nei due esempi:
/* OpenFOAM Foundation 14 - educational meshing example; GPL-3.0-or-later */
FoamFile
{
version 2.0;
format ascii;
class dictionary;
location "system";
object controlDict;
}
application foamRun;
startFrom startTime;
startTime 0;
stopAt endTime;
endTime 1;
deltaT 1;
writeControl timeStep;
writeInterval 1;
purgeWrite 0;
writeFormat ascii;
writePrecision 8;
writeCompression off;
timeFormat general;
timePrecision 6;
runTimeModifiable true;
writeFormat asciirende leggibili con un editor i file dipolyMesh; per mesh grandibinaryriduce spazio e tempi di scrittura.writePrecision 8è la precisione con cui vengono scritte le coordinate dei punti: con domini grandi e celle piccole conviene aumentarla, altrimenti l’arrotondamento può degradare la qualità di celle molto sottili.application foamRunnon viene usata dalle utility di meshing: è pronta per quando al caso si aggiungeranno campi e fisica.
Approfondimento · Set e zone: che cosa compare in polyMesh oltre alla mesh
constant/polyMesh/sets/: insiemi di celle, facce o punti scritti per esempio dacheckMesh(le entità che non superano i controlli) o datopoSet. Servono per diagnosi e per costruire zone; il solver non li usa direttamente.cellZones,faceZones,pointZones: sottoinsiemi con nome che fanno parte della mesh e che il solver può usare, per esempio per un mezzo poroso, una sorgente localizzata o una zona rotante.snappyHexMeshpuò crearle da superfici chiuse concellZoneefaceZoneinrefinementSurfaces.- Con
writeFormat asciile prime righe di ogni file indicano classe e numero di elementi:head -n 25 constant/polyMesh/ownerriporta nell’intestazione anche il numero di punti, celle e facce.
4. Esercitazione A: canale 3D con blockMesh
Il primo caso costruisce un prisma con lunghezza 0,60 m, altezza 0,20 m e spessore 0,10 m, suddiviso in 60 × 20 × 10 = 12.000 celle. La dimensione nominale della cella è 0,01 m in tutte le direzioni. Le patch si chiamano inlet, outlet e walls. Il dominio è tridimensionale, non un caso bidimensionale con patch empty.
Nel file fornito system/blockMeshDict sono fondamentali:
scale 1;
vertices
(
(0 0 0) (0.6 0 0) (0.6 0.2 0) (0 0.2 0)
(0 0 0.1) (0.6 0 0.1) (0.6 0.2 0.1) (0 0.2 0.1)
);
blocks
(
hex (0 1 2 3 4 5 6 7) (60 20 10)
simpleGrading (1 1 1)
);
Ogni faccia esterna deve comparire nella sezione boundary o essere esplicitamente gestita. La faccia di ingresso è (0 4 7 3); quella di uscita è (1 2 6 5). Le altre quattro facce sono raggruppate in walls. Il verso di numerazione dei vertici è importante per evitare blocchi invertiti.
Dopo aver caricato i file sul server, lancia:
cd ~/OpenFOAM/mesh-guide6
./scripts/run_mesh_case.sh channel
Lo script crea una nuova copia nella cartella results/channel_..., genera la mesh e conserva logs/log.blockMesh e logs/log.checkMesh.base. In alternativa, per imparare i comandi separatamente, lavora sempre su una copia nuova del caso:
blockMesh -case /percorso/nuova-copia-del-caso
checkMesh -case /percorso/nuova-copia-del-caso \
-allGeometry -allTopology
Il numero di celle atteso dal blockMesh è 12.000. Se è diverso, verifica quale dizionario e quale directory del caso siano stati effettivamente utilizzati.
Approfondimento — distribuzione non uniforme. simpleGrading (1 1 1) indica uniformità; altri rapporti permettono di accumulare celle vicino a una parete o in una regione. Non usare un grading aggressivo per compensare l’assenza di geometria locale: transizioni troppo rapide possono deteriorare qualità e stabilità.
Approfondimento · Il blockMeshDict completo e il verso delle facce
/* OpenFOAM Foundation 14 - educational meshing example; GPL-3.0-or-later */
FoamFile
{
version 2.0;
format ascii;
class dictionary;
location "system";
object blockMeshDict;
}
scale 1;
vertices
(
(0 0 0) // 0
(0.6 0 0) // 1
(0.6 0.2 0) // 2
(0 0.2 0) // 3
(0 0 0.1) // 4
(0.6 0 0.1) // 5
(0.6 0.2 0.1) // 6
(0 0.2 0.1) // 7
);
blocks
(
hex (0 1 2 3 4 5 6 7) (60 20 10) simpleGrading (1 1 1)
);
edges ();
boundary
(
inlet { type patch; faces ((0 4 7 3)); }
outlet { type patch; faces ((1 2 6 5)); }
walls { type wall; faces ((0 1 5 4) (3 7 6 2) (0 3 2 1) (4 5 6 7)); }
);
mergePatchPairs ();
Nel blocco hex (0 1 2 3 4 5 6 7) i primi quattro vertici descrivono la faccia inferiore e gli ultimi quattro quella superiore, nello stesso ordine; la sequenza 0→1→2→3 deve girare in senso antiorario guardando dal lato dei vertici 4–7 (regola della mano destra). Così l’asse locale x1 va da 0 a 1, x2 da 0 a 3, x3 da 0 a 4, e i tre numeri (60 20 10) si riferiscono a questi assi.
Le facce di contorno vanno scritte in modo che la normale ottenuta con la regola della mano destra punti fuori dal blocco. Verifica sulla faccia inlet (0 4 7 3): con a = P4 − P0 = (0, 0, 0.1) e b = P7 − P4 = (0, 0.2, 0) il prodotto vettoriale vale a × b = (−0.02, 0, 0), cioè punta verso −x, fuori dal dominio. Se una faccia è scritta al contrario, blockMesh segnala l’errore o produce un orientamento incoerente che checkMesh rileva.
# numeri da confrontare con la tabella della sezione 1
grep -E "^\s*(points|faces|internal faces|cells):" results/channel_*/logs/log.checkMesh.base
grep -A3 "Patch" results/channel_*/logs/log.checkMesh.base
Approfondimento · Grading: rapporto di espansione e dimensione della prima cella
In simpleGrading ogni numero è il rapporto fra l’ultima e la prima cella lungo quell’asse. Con n celle e rapporto totale E, le celle crescono in progressione geometrica di ragione r = E^(1/(n−1)) e la prima cella vale Δ₁ = L·(r − 1)/(rⁿ − 1).
Esempio sul canale: altezza L = 0,2 m, n = 20, E = 4. Si ottiene r ≈ 1,0757, prima cella Δ₁ ≈ 4,58 mm, ultima ≈ 18,3 mm. Così però si infittisce solo una parete. Per infittire entrambe si usa il grading a più tratti, in cui ogni terna indica (frazione della lunghezza, frazione delle celle, rapporto di espansione):
hex (0 1 2 3 4 5 6 7) (60 20 10)
simpleGrading
(
1
((0.5 0.5 4) (0.5 0.5 0.25))
1
)
Ogni metà (0,1 m, 10 celle) ha r ≈ 1,1665: prima cella a parete ≈ 4,54 mm, cella al centro ≈ 18,2 mm. Un rapporto fra celle adiacenti oltre circa 1,2–1,3 tende a peggiorare l’accuratezza degli schemi del secondo ordine: in questo esempio è 1,17, accettabile. Per una parete più fitta conviene aumentare il numero di celle, non soltanto il rapporto.
Approfondimento · Lo stesso canale come caso 2D: patch empty e una sola cella nello spessore
OpenFOAM risolve sempre in 3D; un caso bidimensionale si ottiene con una sola cella nella direzione trascurata e con le due facce opposte di tipo empty. Il canale dell’esercitazione diventa:
blocks
(
hex (0 1 2 3 4 5 6 7) (60 20 1) simpleGrading (1 1 1)
);
boundary
(
inlet { type patch; faces ((0 4 7 3)); }
outlet { type patch; faces ((1 2 6 5)); }
walls { type wall; faces ((0 1 5 4) (3 7 6 2)); }
frontAndBack
{
type empty;
faces ((0 3 2 1) (4 5 6 7));
}
);
Celle: 60 × 20 × 1 = 1 200. Le patch empty non ricevono condizioni al contorno fisiche (nei campi si scrive type empty;) e checkMesh riconosce il caso come 2D. Per un caso assialsimmetrico si usa invece un cuneo con patch wedge di pochi gradi. Le patch symmetryPlane e symmetry si usano quando il piano di simmetria è fisico, non per ridurre la dimensionalità.
Approfondimento · Blocchi multipli, spigoli curvi e controllo della topologia
Geometrie più complesse si ottengono con più blocchi che condividono vertici: le facce coincidenti vengono fuse automaticamente, purché il numero di celle sui lati comuni sia lo stesso. Gli spigoli curvi si descrivono nella lista edges, per esempio con un arco per tre punti:
edges
(
// arco dal vertice 1 al vertice 2 passando per il punto intermedio
arc 1 2 (0.65 0.1 0)
arc 5 6 (0.65 0.1 0.1) // lo stesso arco sulla faccia opposta
);
Prima di generare la mesh conviene controllare blocchi, numerazione e facce con l’opzione che scrive solo la topologia dei blocchi, da aprire in ParaView; un vertice sbagliato si vede subito come blocco attorcigliato:
blockMesh -blockTopology
ls *.obj # file della topologia dei blocchi, apribili in ParaView
blockMesh -help
5. Passare dal CAD alla mesh: i file STL
Il secondo esempio utilizza una sfera chiusa in STL, con raggio 0,025 m e centro (0.22, 0.10, 0.05) m, collocata all’interno dello stesso canale. La superficie è inclusa nel pacchetto: non è necessario installare Python sul server per eseguire l’esercitazione.
Quando si parte da un CAD in FreeCAD o da un software equivalente:
- Definisci la scala reale del componente e controlla le unità dopo l’export.
- Verifica che ogni superficie che delimita il solido sia chiusa e orientata; controlla eventuali fori, facce duplicate e intersezioni.
- Esporta uno STL con triangolazione adeguata alla curvatura e ai dettagli che vuoi risolvere.
- Trasferiscilo con FileZilla in
constant/triSurface/. - Esegui
surfaceChecke verifica la bounding box e la continuità della superficie.
surfaceCheck constant/triSurface/sphere.stl
Questo comando va lanciato dalla directory del caso (oppure passando il percorso assoluto dello STL). Lo STL non contiene informazioni affidabili sulle unità: surfaceCheck permette di rilevare coordinate incompatibili con quelle del blockMeshDict. Per esempio, un oggetto di 25 mm esportato come 25 unità e interpretato come metri sarà mille volte troppo grande.
Richiamo — STL e accuratezza geometrica. Aumentare la densità della mesh volumetrica non elimina l’errore introdotto da una superficie STL troppo grossolana. Distinguere la risoluzione della triangolazione CAD da quella della mesh CFD.
Approfondimento · Misurare lo STL prima di usarlo: volume, area, lunghezza dei lati
Questo script, solo libreria standard Python, legge uno STL ASCII e calcola bounding box, area, volume racchiuso (teorema della divergenza) e lunghezza dei lati dei triangoli. Si esegue sul server o su Windows:
#!/usr/bin/env python3
"""Bounding box, area, enclosed volume and edge lengths of an ASCII STL (standard library only)."""
import math, sys
tri, cur = [], []
with open(sys.argv[1], encoding="utf-8", errors="replace") as f:
for line in f:
w = line.split()
if w and w[0] == "vertex":
cur.append(tuple(map(float, w[1:4])))
elif w and w[0] == "endloop":
tri.append(cur); cur = []
lo, hi = [math.inf] * 3, [-math.inf] * 3
vol = area = 0.0
edges = []
for a, b, c in tri:
for p in (a, b, c):
lo = [min(lo[i], p[i]) for i in range(3)]
hi = [max(hi[i], p[i]) for i in range(3)]
# signed volume of the tetrahedron (origin, a, b, c): divergence theorem
vol += (a[0]*(b[1]*c[2]-b[2]*c[1]) - a[1]*(b[0]*c[2]-b[2]*c[0]) + a[2]*(b[0]*c[1]-b[1]*c[0])) / 6
u = [b[i]-a[i] for i in range(3)]; v = [c[i]-a[i] for i in range(3)]
n = (u[1]*v[2]-u[2]*v[1], u[2]*v[0]-u[0]*v[2], u[0]*v[1]-u[1]*v[0])
area += 0.5 * math.sqrt(sum(x*x for x in n))
edges += [math.dist(a, b), math.dist(b, c), math.dist(c, a)]
print(f"triangles {len(tri)}")
print(f"bounding box {lo} -> {hi}")
print(f"area {area:.6e}")
print(f"volume {vol:.6e} (negative = normals pointing inwards)")
print(f"edge length min {min(edges):.3e} mean {sum(edges)/len(edges):.3e} max {max(edges):.3e}")
python3 stl_info.py constant/triSurface/sphere.stl
Sulla sfera del pacchetto (896 triangoli) i valori sono:
| Grandezza | STL | Sfera esatta, r = 0,025 m | Scarto |
|---|---|---|---|
| bounding box x | 0.19514 … 0.24486 | 0.195 … 0.245 | — |
| bounding box z | 0.025 … 0.075 | 0.025 … 0.075 | — |
| area [m²] | 7.786·10⁻³ | 7.854·10⁻³ | −0.87 % |
| volume [m³] | 6.432·10⁻⁵ | 6.545·10⁻⁵ | −1.73 % |
| lato medio [mm] | 4.95 (min 1.02, max 7.15) | — | — |
Lo STL è un poliedro inscritto: volume e area sono leggermente minori di quelli della sfera vera, e in x e y la bounding box non raggiunge ±0,025 perché nessun vertice cade esattamente sull’equatore in quelle direzioni. È l’errore geometrico di cui parla il richiamo: nessun raffinamento della mesh lo elimina. Il volume del dominio fluido atteso è quindi 0,012 − 6,432·10⁻⁵ ≈ 0,011936 m³, da confrontare con il «Total volume» di checkMesh. Un volume negativo indicherebbe triangoli con normali rivolte verso l’interno.
Approfondimento · Unità sbagliate: riscalare lo STL senza tornare al CAD
Se surfaceCheck mostra una bounding box mille volte troppo grande (CAD in millimetri), la superficie si può riscalare con surfaceTransformPoints, scrivendo un nuovo file e lasciando intatto l’originale. La sintassi delle trasformazioni è cambiata fra le versioni: controllare quella della propria installazione con -help.
surfaceTransformPoints -help
# forma usata nelle versioni Foundation recenti:
surfaceTransformPoints "scale=(0.001 0.001 0.001)" \
constant/triSurface/part_mm.stl constant/triSurface/part_m.stl
surfaceCheck constant/triSurface/part_m.stl
Nelle righe di surfaceCheck cercare la bounding box, l’indicazione che la superficie è chiusa (ogni lato condiviso da due triangoli) e l’assenza di triangoli illegali o di intersezioni. Uno STL non chiuso può comunque essere usato da snappyHexMesh, ma la separazione fra fluido e solido diventa inaffidabile: il fluido può «passare» attraverso il foro e la regione da eliminare non viene più riconosciuta.
Approfondimento · Tolleranza di esportazione: quanto si allontana lo STL dalla superficie vera
Per un lato di triangolo s su una superficie di raggio di curvatura R, la massima distanza fra la corda e l’arco (freccia) vale circa δ ≈ s² / (8R). I CAD chiamano questo parametro «deviazione» o «chordal tolerance» nell’esportazione STL.
| Lato del triangolo | δ = s²/(8·0.025 m) | Rispetto alla cella di livello 2 (2,5 mm) |
|---|---|---|
| 4.95 mm (medio) | ≈ 0.12 mm | ≈ 5 % |
| 7.15 mm (max) | ≈ 0.26 mm | ≈ 10 % |
Regola pratica: la deviazione dello STL deve essere piccola rispetto alla cella più fine prevista sulla superficie e rispetto agli spessori di parete che interessano. Se si prevedono strati di parete di decimi di millimetro, una freccia di 0,26 mm è troppo grande: occorre esportare con tolleranza più stretta.
Approfondimento · Uno STL con più regioni per avere patch distinte
Uno STL ASCII può contenere più blocchi solid nome … endsolid nome. snappyHexMesh li tratta come regioni della stessa superficie e può assegnare a ciascuna livello e tipo di patch diversi: è il modo corretto di ottenere, da un unico file CAD, ingresso, uscita e pareti distinti. In esportazione si assegna un nome a ogni gruppo di facce; in snappyHexMeshDict:
refinementSurfaces
{
part.stl
{
level (2 2);
regions
{
inlet { level (2 2); patchInfo { type patch; } }
outlet { level (2 2); patchInfo { type patch; } }
walls { level (2 3); patchInfo { type wall; } }
}
}
}
I nomi delle patch risultanti combinano nome della geometria e della regione; si leggono in constant/polyMesh/boundary e vanno riportati identici nei campi di 0/. surfaceCheck elenca le regioni presenti nel file.
6. Esercitazione B: snappyHexMesh attorno alla sfera
snappyHexMesh comincia con una mesh esaedrica di sfondo e procede con castellation, snapping e, facoltativamente, layer addition. In questa prima prova gli strati sono disattivati: si isola così la corretta rappresentazione della parete solida.

Nel file completo system/snappyHexMeshDict sono presenti i comandi fondamentali:
castellatedMesh true;
snap true;
addLayers false;
geometry
{
sphere.stl { type triSurfaceMesh; }
}
castellatedMeshControls
{
// altri controlli nel file completo
refinementSurfaces
{
sphere.stl { level (2 2); patchInfo { type wall; } }
}
locationInMesh (0.517 0.113 0.057);
}
Il locationInMesh è un punto nel fluido da conservare, e non dentro la sfera, non sulla superficie e non su una faccia della mesh. È uno dei controlli più importanti: scegliere un punto nel volume errato può causare la rimozione della regione desiderata. Nella configurazione fornita features (); e lo snapping implicito sono sufficienti per una superficie liscia; per geometrie CAD con spigoli è spesso opportuno ricavare e utilizzare le feature edges con surfaceFeatures, come indicato dalla guida ufficiale.

Per generare la mesh:
cd ~/OpenFOAM/mesh-guide6
./scripts/run_mesh_case.sh sphere
Lo script effettua la copia in results/sphere_..., esegue blockMesh, il primo checkMesh, surfaceCheck, snappyHexMesh -overwrite solo sulla copia appena creata, poi il checkMesh finale. Il flag -overwrite sostituisce la mesh di sfondo all’interno della nuova copia, non i casi originali; non usarlo su una simulazione già pubblicata senza avere predisposto una copia di lavoro.
In caso di errore, consulta il file di log pertinente in logs/. Un errore in surfaceCheck richiede attenzione alla superficie; un problema in snappyHexMesh impone di controllare la selezione della regione fluida, le distanze locali, i livelli di raffinamento e la qualità delle celle.
Approfondimento · Il snappyHexMeshDict completo, voce per voce
/* OpenFOAM Foundation 14 - educational meshing example; GPL-3.0-or-later */
FoamFile
{
version 2.0;
format ascii;
class dictionary;
location "system";
object snappyHexMeshDict;
}
castellatedMesh true;
snap true;
addLayers false; // optional extension: see the guide
geometry
{
sphere.stl
{
type triSurfaceMesh;
}
}
castellatedMeshControls
{
maxLocalCells 150000;
maxGlobalCells 350000;
minRefinementCells 0;
maxLoadUnbalance 0.10;
nCellsBetweenLevels 3;
features ();
refinementSurfaces
{
sphere.stl
{
level (2 2);
patchInfo { type wall; }
}
}
resolveFeatureAngle 30;
refinementRegions {};
locationInMesh (0.517 0.113 0.057); // fluid region; not on a face
allowFreeStandingZoneFaces true;
}
snapControls
{
nSmoothPatch 3;
tolerance 2.0;
nSolveIter 30;
nRelaxIter 5;
nFeatureSnapIter 10;
implicitFeatureSnap true;
explicitFeatureSnap false;
multiRegionFeatureSnap false;
}
// Layers are off in the main exercise. Add them only after a clean base mesh.
addLayersControls
{
relativeSizes true;
layers {};
expansionRatio 1.2;
finalLayerThickness 0.3;
minThickness 0.1;
nGrow 0;
featureAngle 60;
nRelaxIter 5;
nSmoothSurfaceNormals 1;
nSmoothNormals 3;
nSmoothThickness 10;
maxFaceThicknessRatio 0.5;
maxThicknessToMedialRatio 0.3;
minMedianAxisAngle 90;
nBufferCellsNoExtrude 0;
nLayerIter 50;
}
meshQualityControls
{
maxNonOrtho 65;
maxBoundarySkewness 20;
maxInternalSkewness 4;
maxConcave 80;
minVol 1e-13;
minTetQuality -1e30;
minArea -1;
minTwist 0.02;
minDeterminant 0.001;
minFaceWeight 0.02;
minVolRatio 0.01;
minTriangleTwist -1;
nSmoothScale 4;
errorReduction 0.75;
}
mergeTolerance 1e-6;
| Voce | Che cosa fa |
|---|---|
maxLocalCells / maxGlobalCells | limite di celle per processore e totale durante il raffinamento; oltre il secondo, il raffinamento si ferma prima del livello richiesto |
minRefinementCells | se le celle da raffinare sono meno di questo numero, il ciclo di raffinamento termina |
nCellsBetweenLevels | strati di celle fra due livelli di raffinamento: più alto = transizione più graduale |
resolveFeatureAngle | dove l’angolo fra le normali della superficie supera questo valore, le celle vengono raffinate fino al livello massimo |
level (2 2) | livello minimo e massimo sulla superficie; il massimo si applica dove la curvatura supera resolveFeatureAngle |
tolerance 2.0 | distanza massima, in multipli della dimensione locale delle celle, entro cui un punto viene attratto sulla superficie |
nSmoothPatch, nSolveIter, nRelaxIter | iterazioni di levigatura della superficie, di rilassamento del campo di spostamento e di correzione delle celle cattive |
implicitFeatureSnap | ricava gli spigoli dalla superficie stessa, senza file di feature |
meshQualityControls | limiti che lo snapping e gli strati non devono violare; se una modifica li viola viene annullata localmente |
mergeTolerance | tolleranza relativa all’estensione del dominio usata per unire punti coincidenti |
Approfondimento · I conti del raffinamento attorno alla sfera e la scelta del punto interno
La mesh di sfondo ha celle di 10 mm. Ogni livello dimezza il lato: livello 1 = 5 mm, livello 2 = 2,5 mm. La sfera ha diametro 50 mm, quindi copre 5 celle di sfondo, ma circa 20 celle al livello 2; la circonferenza (≈ 157 mm) è descritta da circa 63 celle. Il lato medio dei triangoli dello STL è circa 4,9 mm: con celle di 2,5 mm la mesh «vede» le sfaccettature del poliedro, quindi raffinare ancora senza rendere più fine lo STL non avvicina la superficie a una sfera vera.
Il punto (0.517 0.113 0.057) è stato scelto con criterio: dista circa 0,297 m dal centro della sfera, quindi è nel fluido; non è un multiplo né di 10 mm né di 2,5 mm o 1,25 mm in nessuna coordinata (0,517/0,0025 = 206,8), quindi non cade su una faccia neppure dopo il raffinamento. È buona norma scegliere coordinate «irregolari» proprio per questo.
Nelle versioni più recenti della OpenFOAM Foundation la stessa voce compare nei tutorial anche con il nome insidePoint. Per sapere quale forma usa la propria installazione basta cercarla nei tutorial:
grep -rl --include=snappyHexMeshDict -E "insidePoint|locationInMesh" "$FOAM_TUTORIALS" | head
grep -rh --include=snappyHexMeshDict -E "^\s*(insidePoint|locationInMesh)" "$FOAM_TUTORIALS" | sort | uniq -c
Approfondimento · Vedere le tre fasi separatamente: senza -overwrite
Senza -overwrite, snappyHexMesh scrive ogni fase in una directory temporale distinta: la mesh «castellata» nella prima, quella dopo lo snapping nella seconda e, se attivi, gli strati nella terza; la mesh di sfondo resta in constant/polyMesh. Aprendo il caso in ParaView e scorrendo i tempi si osserva l’effetto di ciascuna fase. Da fare in una copia dedicata, mai nel caso da cui partirà la simulazione.
cp -a examples/02_sphere_snappy "$HOME/mesh-runs/sphere_stages"
cd "$HOME/mesh-runs/sphere_stages"
blockMesh > log.blockMesh 2>&1
snappyHexMesh > log.snappyHexMesh 2>&1
ls -d [0-9]*/ # una directory per fase
Approfondimento · Spigoli vivi: estrarre le feature edges con surfaceFeatures
La sfera non ha spigoli, per questo il dizionario usa features (); e lo snapping implicito. Un componente CAD con spigoli vivi (flange, gradini, bordi d’attacco) richiede invece che la mesh segua le linee di spigolo, altrimenti gli spigoli risultano arrotondati. In OpenFOAM Foundation l’estrazione si fa con surfaceFeatures, che legge system/surfaceFeaturesDict e scrive un file .eMesh in constant/. Un dizionario minimo:
FoamFile
{
format ascii;
class dictionary;
object surfaceFeaturesDict;
}
surfaces ("part.stl");
// mantiene gli spigoli in cui le facce formano un angolo minore di 150°
includedAngle 150;
Poi in snappyHexMeshDict si elencano i file in features ( { file "part.eMesh"; level 2; } ); e si attiva explicitFeatureSnap true; (di solito con implicitFeatureSnap false;). Il tutorial motorBike della propria installazione mostra la sequenza completa.
Approfondimento · Che cosa succede davvero in castellation, snapping e layer
- Castellation: le celle di sfondo tagliate dalla superficie, o vicine a feature e regioni, vengono suddivise in 8 fino al livello richiesto, rispettando
nCellsBetweenLevels. Poi le celle sono classificate rispetto alla superficie e si conserva solo la regione connessa al punto interno; le facce di separazione diventano la patch. Il risultato è una mesh a gradini, ancora tutta di esaedri. - Snapping: i punti della patch a gradini vengono proiettati sulla superficie (entro
tolerancevolte la dimensione locale), e lo spostamento viene propagato all’interno risolvendo un problema di rilassamento. Dove una cella violameshQualityControls, lo spostamento viene ridotto localmente (errorReduction,nSmoothScale). Le celle vicino alla superficie diventano poliedri. - Layer: la mesh viene «ritirata» dalla parete e lo spazio liberato è riempito con prismi estrusi lungo le normali. Dove l’estrusione violerebbe i controlli di qualità, gli strati vengono ridotti o eliminati localmente: per questo la copertura effettiva va sempre controllata.
Due regole pratiche che discendono da questo funzionamento: la mesh di sfondo deve avere celle il più possibile cubiche (rapporto d’aspetto vicino a 1), altrimenti il raffinamento produce celle allungate e lo snapping peggiora; e la cella di sfondo deve essere abbastanza piccola rispetto ai dettagli perché, dopo il raffinamento, ogni fessura del fluido contenga più celle.
Approfondimento · I controlli di qualità di snappyHexMesh, voce per voce
| Voce (valore nel pacchetto) | Che cosa limita |
|---|---|
maxNonOrtho 65 | angolo massimo fra normale di faccia e congiungente dei centri, in gradi |
maxBoundarySkewness 20 / maxInternalSkewness 4 | skewness massima sulle facce di contorno e interne |
maxConcave 80 | angolo massimo di concavità di una faccia |
minVol 1e-13 | volume minimo di cella: esclude celle degeneri o invertite |
minTetQuality -1e30 | qualità minima della decomposizione in tetraedri; il valore molto negativo disattiva il controllo |
minArea -1 | area minima di faccia; negativo = disattivato |
minTwist 0.02 | torsione minima: allineamento fra la normale della faccia e quelle dei suoi triangoli |
minDeterminant 0.001 | determinante di cella (1 per un esaedro perfetto): valori piccoli indicano celle schiacciate |
minFaceWeight 0.02 | peso di interpolazione sulla faccia (0–0,5): valori piccoli significano celle adiacenti molto diverse |
minVolRatio 0.01 | rapporto minimo fra i volumi di celle adiacenti |
minTriangleTwist -1 | torsione dei triangoli; negativo = disattivato |
nSmoothScale 4, errorReduction 0.75 | iterazioni e fattore con cui lo spostamento viene ridotto dove i limiti sono violati |
Allentare questi limiti fa «passare» snappy ma sposta il problema sul solver. La direzione corretta di solito è un’altra: mesh di sfondo più regolare, livello o tolleranza di snap diversi, STL più pulito.
Approfondimento · Leggere log.snappyHexMesh senza perdersi
Il log è lungo; per un controllo ingegneristico bastano poche informazioni: numero di celle dopo ogni ciclo di raffinamento, celle rimosse dalla selezione della regione, iterazioni di morphing e celle che violano i controlli, eventuale tabella dei layer e tempo totale.
grep -n -E "Refinement phase|Surface refinement iteration|Removing|cells:|Morph iteration|Snapped mesh|Layer|Finished meshing|ExecutionTime" logs/log.snappyHexMesh | tail -n 60
grep -n -i -E "warning|error|illegal" logs/log.snappyHexMesh
Un numero di celle che smette di crescere prima del livello richiesto, insieme a un valore vicino a maxGlobalCells, indica un raffinamento troncato; molte celle che violano i controlli dopo l’ultima iterazione di morphing indicano uno snapping incompleto.
Approfondimento · Generare la mesh in parallelo
Per mesh grandi snappyHexMesh si esegue in parallelo come un solver: si decompone la mesh di sfondo, si lancia con -parallel e si può far partire il calcolo direttamente sul caso decomposto, senza ricostruire. Il numero di sottodomini in system/decomposeParDict deve coincidere con quello dei processi (vedi la Guida 5).
blockMesh
decomposePar
mpirun -np 4 snappyHexMesh -parallel -overwrite > logs/log.snappyHexMesh 2>&1
mpirun -np 4 checkMesh -parallel -allGeometry -allTopology > logs/log.checkMesh.final 2>&1
# per riunire la mesh in un unico caso, vedere le opzioni della propria versione:
reconstructPar -help
Il bilanciamento del carico durante il raffinamento è controllato da maxLoadUnbalance (0,10 nel pacchetto): oltre questo squilibrio fra processori la mesh viene ridistribuita.
7. Capire i parametri di snappyHexMesh
| Parametro | Significato pratico | Errore tipico |
|---|---|---|
refinementSurfaces.level (2 2) | Due livelli locali sulla sfera | Troppe celle o geometria poco definita |
nCellsBetweenLevels | Transizione fra zone con diversa risoluzione | Transizione troppo brusca |
maxGlobalCells | Limite alla crescita della mesh | La procedura termina prima del dettaglio atteso |
locationInMesh | Punto che identifica la regione da mantenere | Fluido eliminato o dominio sbagliato |
snapControls | Spostamento della mesh verso la superficie | Celle distorte o scarsa aderenza |
addLayers | Attiva gli strati vicino alle pareti | Numero/qualità degli strati non garantiti |
meshQualityControls | Limiti e criteri di qualità durante la generazione | Controlli troppo permissivi |
Un livello di raffinamento in ciascuna direzione dimezza circa la dimensione locale della cella; in una regione 3D interamente raffinata il numero di celle può crescere di un fattore vicino a 8 per livello. Per questo è preferibile raffinare dove la fisica e la geometria lo richiedono.

Approfondimento · Raffinare una regione: la scia dietro la sfera
Oltre alla superficie, si può raffinare un volume. Le geometrie ausiliarie si dichiarano in geometry e si usano in refinementRegions in tre modi: inside, outside o distance (livelli in funzione della distanza dalla superficie). Esempio per una scia lunga circa 9 diametri:
geometry
{
sphere.stl { type triSurfaceMesh; }
wakeBox
{
type box; // nelle versioni meno recenti: searchableBox
min (0.20 0.07 0.02);
max (0.60 0.13 0.08);
}
}
castellatedMeshControls
{
// ...
refinementRegions
{
wakeBox { mode inside; levels ((1E15 1)); }
sphere.stl { mode distance; levels ((0.01 2) (0.03 1)); }
}
}
In distance le coppie (distanza, livello) vanno in ordine di distanza crescente e livello decrescente: qui livello 2 entro 10 mm dalla sfera e livello 1 entro 30 mm. Il nome del tipo di geometria va verificato nella propria versione, per esempio con grep -rh --include=snappyHexMeshDict "type *[a-zA-Z]*Box\|type *box" "$FOAM_TUTORIALS" | sort | uniq -c.
Approfondimento · Quante celle costa un livello
In 3D ogni livello divide una cella in 8: una regione interamente raffinata al livello L costa 8^L volte le celle di partenza. Il canale ha 12 000 celle di sfondo: raffinato uniformemente diventerebbe 96 000 celle al livello 1, 768 000 al livello 2, oltre 6 milioni al livello 3. Il raffinamento locale serve proprio a evitare questa crescita: il costo dipende dall’area raffinata, non dal volume del dominio.
Per la sola superficie l’ordine di grandezza è area / Δ² celle per ogni strato: la sfera (7,8·10⁻³ m²) con celle di 2,5 mm richiede circa 1 250 celle per strato, a cui si sommano gli strati di transizione imposti da nCellsBetweenLevels. Il numero effettivo si legge alla fine di log.snappyHexMesh e va confrontato con maxGlobalCells: se è vicino al limite, il raffinamento potrebbe essere stato troncato.
Approfondimento · Memoria e tempo: misurarli invece di indovinarli
Il fabbisogno di memoria di snappyHexMesh cresce con il numero di celle e con la complessità della superficie; in letteratura e nei forum si citano ordini di grandezza di circa un gigabyte per milione di celle, ma il dato che conta è quello misurato sul proprio server con una mesh pilota più piccola, da estrapolare linearmente prima di lanciare quella definitiva.
/usr/bin/time -v snappyHexMesh -overwrite > logs/log.snappyHexMesh 2> logs/time.snappyHexMesh
grep -E "Maximum resident set size|Elapsed" logs/time.snappyHexMesh
grep -E "^\s*cells:" logs/log.checkMesh.final
Il rapporto fra memoria massima e numero di celle della mesh pilota, moltiplicato per il numero di celle previsto, dà una stima affidabile; se supera la RAM libera del nodo, la mesh va generata in parallelo su più processi o con un livello più basso.
8. Strati di parete e controllo di y+
Gli strati prismatici sono rilevanti quando il modello di turbolenza e le grandezze richieste necessitano di risoluzione vicino alle pareti. Si introducono dopo aver ottenuto una mesh di base corretta. Per il caso distribuito è intenzionale addLayers false;.
Una stima preliminare della quota adimensionale del primo centro di cella è:
Formula: y⁺ = (uτ · y) / ν, con uτ = √(τw / ρ).
Dove y è la distanza effettiva parete–centro cella, non automaticamente lo spessore complessivo del primo layer. Il valore desiderato dipende dal modello fisico, dal wall treatment e dall’obiettivo dell’analisi: non esiste una soglia universale. Verifica gli strati effettivamente creati, non soltanto quelli richiesti nel dizionario.
Richiamo — perché y+ non è una proprietà della sola mesh? Il valore dipende dalla tensione tangenziale locale, quindi anche dal campo di moto e dalla soluzione. Prima della simulazione si usa una stima; dopo la simulazione va controllata la distribuzione reale.
Approfondimento · Stimare lo spessore del primo strato: un esempio numerico
Prima del calcolo si stima τw con una correlazione per lastra piana in regime turbolento, per esempio Cf ≈ 0,0592·Re_x^(−1/5), con τw = ½·ρ·U²·Cf. È una stima di ordine di grandezza, da verificare sul campo calcolato.
| Passo | Acqua, U = 1 m/s, x = 0,2 m, ν = 10⁻⁶ m²/s |
|---|---|
| Re_x = U·x/ν | 2·10⁵ |
| Cf | ≈ 5,15·10⁻³ |
| τw = ½·ρ·U²·Cf | ≈ 2,58 Pa |
| uτ = √(τw/ρ) | ≈ 0,0508 m/s |
| y per y⁺ = 1 | ≈ 0,020 mm |
| y per y⁺ = 30 | ≈ 0,59 mm |
Poiché y è la distanza del centro della prima cella, lo spessore del primo strato è circa 2·y. La differenza fra le due righe finali (un fattore 30) spiega perché la scelta del trattamento di parete, risolta o con funzioni di parete, va fatta prima di costruire gli strati. A velocità molto più basse il moto può essere laminare e la stima turbolenta non si applica.
Approfondimento · Attivare gli strati sulla sfera e controllare quelli ottenuti
Partendo da una copia nuova del caso, gli strati si attivano con addLayers true; e indicando le patch in addLayersControls/layers. Il nome della patch creata sulla sfera si legge in constant/polyMesh/boundary dopo il primo snappyHexMesh; un’espressione regolare evita di doverlo indovinare:
addLayers true;
addLayersControls
{
relativeSizes true;
layers
{
"sphere.*" { nSurfaceLayers 3; }
}
expansionRatio 1.2;
finalLayerThickness 0.3;
minThickness 0.1;
// le altre voci restano come nel file fornito
}
Con relativeSizes true gli spessori sono frazioni della cella adiacente: qui l’ultimo strato è il 30 % della cella di 2,5 mm. Alla fine della fase di layering il log riporta, per ogni patch, quante facce hanno ricevuto strati e lo spessore medio ottenuto: se la copertura è bassa, gli strati richiesti non esistono davvero in quelle zone, e il calcolo di y⁺ va fatto sulla mesh reale.
Approfondimento · Spessore totale degli strati e salto con la prima cella esterna
Con primo strato t₁, rapporto di espansione r e n strati, lo spessore totale vale T = t₁·(rⁿ − 1)/(r − 1) e l’ultimo strato t₁·r^(n−1). Esempio per y⁺ ≈ 1 nell’acqua a 1 m/s (centro della prima cella a circa 0,02 mm, quindi t₁ ≈ 0,04 mm), con r = 1,2 e n = 10:
| Grandezza | Valore |
|---|---|
| spessore totale T | ≈ 1.04 mm |
| ultimo strato | ≈ 0.21 mm |
| cella esterna al livello 2 | 2.5 mm |
| rapporto ultimo strato / cella esterna | ≈ 0.08 |
Un salto di oltre dieci volte fra l’ultimo strato e la cella esterna è una discontinuità di risoluzione che degrada gradienti e flussi proprio nella zona più importante. Le correzioni possibili: più strati, rapporto di espansione un po’ più alto, livello di superficie più fine (cella esterna più piccola), oppure un trattamento di parete con funzioni di parete e primo strato più spesso.
Intervalli tipici, da verificare per il modello usato: modelli a bassi Reynolds che risolvono il sottostrato viscoso con y⁺ dell’ordine di 1; funzioni di parete standard con il primo centro di cella nella regione logaritmica, tipicamente fra circa 30 e qualche centinaio. La zona intermedia (circa 5–30) è da evitare con le funzioni di parete classiche.
Approfondimento · Le voci di addLayersControls
| Voce | Significato |
|---|---|
relativeSizes | spessori espressi come frazione della cella adiacente (true) o in metri (false) |
expansionRatio | rapporto fra spessori di strati consecutivi |
finalLayerThickness | spessore dell’ultimo strato (il più lontano dalla parete) |
minThickness | spessore sotto il quale lo strato non viene creato |
nGrow | facce di bordo senza strati per favorire la transizione vicino alle patch adiacenti |
featureAngle | angolo oltre il quale l’estrusione si ferma sugli spigoli |
nSmoothSurfaceNormals, nSmoothNormals, nSmoothThickness | levigatura delle normali di estrusione e dello spessore |
maxFaceThicknessRatio | riduce gli strati su facce molto deformate |
maxThicknessToMedialRatio, minMedianAxisAngle | limitano gli strati nelle fessure strette, dove le due pareti si avvicinano |
nBufferCellsNoExtrude | celle di raccordo fra zone con e senza strati |
nLayerIter, nRelaxIter | iterazioni massime per l’aggiunta degli strati e per il rilassamento |
9. checkMesh: interpretare la diagnostica
checkMesh è un controllo di coerenza geometrica e topologica, non una certificazione dell’accuratezza fisica. Per questo usiamo i controlli estesi:
checkMesh -case /percorso/caso -allGeometry -allTopology
Le principali famiglie di segnali sono:

- Topologia: dominio disconnesso, facce o celle problematiche, regioni che non comunicano.
- Geometria: celle con volume piccolo o negativo, non-ortogonalità, skewness, degenerazioni.
- Rilevanza scientifica: risoluzione della geometria, distribuzione dei gradienti, raffinamento in scie e strati limite; questo richiede verifiche aggiuntive.
Non adottare un’unica soglia valida per qualunque solver: una non-ortogonalità molto elevata può richiedere schemi e correzioni specifici; un piccolo numero di celle problematiche in prossimità di un punto critico può essere più rilevante di molte celle mediocri in una zona irrilevante. Registra numero di celle, qualità massima/minima, numero di violazioni e posizione dei problemi.
Approfondimento · Che cosa misurano non-ortogonalità, skewness e aspect ratio
- Non-ortogonalità: angolo fra la normale di una faccia e il segmento che unisce i centri delle due celle. Con 0° il gradiente normale si calcola esattamente dai due valori di cella; al crescere dell’angolo serve una correzione (
nNonOrthogonalCorrectorsinfvSolution, schemicorrected/limitedinfvSchemes).checkMeshsegnala come gravi le facce oltre 70°. - Skewness: distanza fra il punto in cui il segmento fra i centri attraversa la faccia e il centro della faccia, normalizzata. Introduce un errore nell’interpolazione dei valori sulla faccia. Le soglie di segnalazione di
checkMeshsono 4 per le facce interne e 20 per quelle di contorno, gli stessi valori usati damaxInternalSkewnessemaxBoundarySkewnessnel dizionario di snappy. - Aspect ratio: rapporto fra la dimensione massima e minima della cella. Valori alti sono normali e voluti negli strati di parete; diventano un problema quando l’allungamento non è allineato con la direzione dei gradienti.
- Regioni:
checkMeshconta le regioni connesse. Più di una regione significa che parte del dominio non comunica con il resto: nel caso della sfera, l’interno non eliminato.
Per il canale a blocchi uniforme i valori attesi sono: non-ortogonalità massima 0, skewness praticamente nulla, aspect ratio 1 (celle cubiche da 10 mm), volume totale 0,012 m³, una sola regione, «Mesh OK.». Ogni scostamento indica che non si sta guardando il caso che si crede.
Approfondimento · Estrarre i numeri dai log e localizzare le celle cattive
Questo script riassume tutti i log di checkMesh prodotti da run_mesh_case.sh: si lancia dalla cartella del pacchetto e il risultato si può copiare nella tabella della sezione 12.
#!/usr/bin/env bash
# Summarise the key numbers of every checkMesh log under a results directory.
for f in "${1:-results}"/*/logs/log.checkMesh.*; do
echo "== $f"
grep -E "^\s*(points|faces|internal faces|cells):" "$f"
grep -E "Overall domain bounding box|Total volume|Max aspect ratio|Mesh non-orthogonality|Max skewness" "$f"
grep -E "\*\*\*|Mesh OK|Failed [0-9]+ mesh checks" "$f"
done
Le righe che iniziano con *** sono i controlli non superati; checkMesh scrive inoltre in constant/polyMesh/sets/ gli insiemi delle facce e celle problematiche, che in ParaView si visualizzano attivando i sets nel lettore per vedere dove sono. Per scrivere campi di qualità da colorare in ParaView, controllare le opzioni disponibili con checkMesh -help.
Approfondimento · Dalla diagnostica di checkMesh alle scelte in fvSchemes e fvSolution
La non-ortogonalità massima riportata da checkMesh orienta direttamente le impostazioni numeriche. Una prassi diffusa, da adattare al caso:
| Non-ortogonalità massima | laplacianSchemes / snGradSchemes | nNonOrthogonalCorrectors |
|---|---|---|
| < 60° | Gauss linear corrected / corrected | 0 |
| 60°–70° | Gauss linear corrected / corrected | 1 |
| 70°–80° | Gauss linear limited corrected 0.5 / limited corrected 0.5 | 1–2 |
| > 80° | rivedere la mesh | — |
Con limited la correzione non-ortogonale viene limitata per stabilità, a scapito dell’accuratezza: è un compromesso consapevole, da documentare. La skewness alta si compensa in parte con schemi di gradiente limitati (cellLimited Gauss linear 1) ma il rimedio vero è geometrico. Le parti 5 e 6 del percorso trattano fvSchemes e fvSolution in dettaglio.
Approfondimento · Rendere il controllo automatico: fallire quando la mesh fallisce
In una catena automatica (Guida 5) conviene che una mesh che non supera i controlli fermi la procedura invece di produrre un calcolo da buttare. Il controllo si fa sul testo del log:
log=logs/log.checkMesh.final
if grep -q "Mesh OK" "$log"; then
echo "mesh accepted"
else
grep -E "\*\*\*|Failed" "$log"
echo "mesh rejected: see $log" >&2
exit 3
fi
Alcune versioni offrono anche un controllo rispetto a soglie definite in un dizionario dedicato (checkMesh -meshQuality, che legge system/meshQualityDict): verificare la disponibilità con checkMesh -help e, se presente, usare gli stessi limiti di meshQualityControls.
10. Visualizzare la mesh sul PC Windows
Apri la connessione pvserver in ParaView Windows come descritto nella Guida 1. Nel file manager di ParaView collegato al server raggiungi il caso generato e apri mesh.foam. Se necessario, abilita il caricamento delle regioni/patch nel pannello del reader, premi Apply e passa alla rappresentazione Surface With Edges. Con un filtro Slice puoi controllare la distribuzione interna; con Extract Surface puoi osservare la sfera. La struttura completa dei dati CFD resta sul server: non è necessario scaricare tutta polyMesh per visualizzarla via pvserver.
Controlla che inlet, outlet, walls e la patch della sfera siano presenti, che la cavità della sfera sia esclusa dalla regione fluida e che la densità di celle cresca dove richiesto. Le immagini della guida sono schemi didattici originali, non schermate acquisite da ParaView.
Approfondimento · Controlli quantitativi in ParaView, oltre all’occhio
- Volume del fluido:
Integrate Variablessulla regione interna restituisce il volume; per la sfera deve essere vicino a 0,011936 m³ (valore calcolato dallo STL nella sezione 5). - Celle intere: un
Clipcon l’opzione Crinkle clip mostra le celle complete invece di tagliarle, e rende visibili i livelli di raffinamento e le transizioni. - Patch: nel pannello del lettore, in Mesh Regions, selezionare solo la patch della sfera per vederne la triangolazione rispetto allo STL originale, caricato come secondo file.
- Poliedri: snappy crea celle poliedriche vicino alla superficie; l’opzione del lettore che decompone i poliedri cambia solo la visualizzazione, non la mesh usata dal solver.
Approfondimento · Il filtro Mesh Quality di ParaView e le mappe di qualità
ParaView dispone del filtro Mesh Quality, che calcola per ogni cella metriche come aspect ratio, skew o jacobiano scalato per esaedri e tetraedri; sui poliedri generici prodotti da snappy molte metriche non sono definite. Per questo è più affidabile colorare la mesh con i campi o i set scritti da checkMesh, che usano le stesse definizioni del solver. Per localizzare le celle peggiori: Threshold sul campo di qualità, poi Extract Cells By Region o un Clip con Crinkle clip attorno alla zona.
11. Tre errori reali da saper diagnosticare
A. La sfera appare gigantesca o non interseca la mesh. Confronta bounding box di surfaceCheck e blockMesh. È spesso un errore di unità o di coordinate nel CAD.
B. Rimane soltanto l’interno del solido o sparisce la regione voluta. Verifica locationInMesh e l’integrità dello STL. Un punto sulla mesh di sfondo o sul bordo non identifica in modo robusto il fluido.
C. La mesh è prodotta ma ha celle pessime vicino alla superficie. Raffinamento, transizioni, tolleranze di snap, dimensioni dell’ostacolo rispetto alla griglia iniziale e triangolazione interagiscono. Modifica un parametro alla volta, rigenera in una nuova cartella e confronta i log.
Approfondimento · Comandi rapidi per i tre scenari
# A. confronto delle bounding box
grep -i "bounding box" logs/log.surfaceCheck logs/log.checkMesh.base
# B. quante regioni sono rimaste e quale volume
grep -E "regions|Total volume" logs/log.checkMesh.final
# C. controlli falliti e celle segnalate
grep -E "\*\*\*|Failed|severely" logs/log.checkMesh.final
ls constant/polyMesh/sets/ 2>/dev/null
Per lo scenario B il volume è il controllo più rapido: se la regione mantenuta fosse l’interno della sfera, il volume sarebbe circa 6,4·10⁻⁵ m³ invece di circa 0,0119 m³.
Approfondimento · Altri due guasti tipici: regione che «perde» e punto interno fuori dominio
- D. Il fluido «attraversa» il solido. Se lo STL ha un foro più grande delle celle al livello raggiunto, interno ed esterno risultano connessi: la cavità non viene eliminata e il volume del dominio coincide con quello del canale pieno (0,012 m³). Rimedio: chiudere la superficie (
surfaceChecksegnala i lati aperti), non aumentare il livello. - E. Il punto interno non è nella mesh. Se
locationInMeshcade fuori dalla bounding box della mesh di sfondo (per esempio perché la geometria è stata riscalata o spostata),snappyHexMeshsi ferma con un errore fatale che cita il punto. Rimedio: ricalcolare il punto nelle nuove coordinate e controllarlo contro la bounding box diblockMesh.
12. Piano minimo di verifica prima del solver
| Verifica | Evidenza da conservare | Criterio |
|---|---|---|
| Geometria CAD/STL | Dimensioni, bounding box e surfaceCheck | Scala e chiusura coerenti |
| Mesh di sfondo | log.blockMesh | Numero celle e patch attesi |
| Mesh finale | log.snappyHexMesh | Geometria e regione corrette |
| Qualità | log.checkMesh.final | Nessuna anomalia non giustificata |
| Ispezione visiva | Viste ParaView con bordi e sezioni | Raffinamento e patch corretti |
| Sensibilità | Studio della Guida 4 | Osservabili stabili al raffinamento |
Per confrontare mesh diverse conserva nomi e definizioni delle patch, metodo numerico, condizioni al contorno e misure fisiche costanti, cambiando intenzionalmente soltanto la discretizzazione. Una mesh con il maggior numero di celle non è necessariamente la migliore.
Approfondimento · Una scheda di accettazione già compilata per il canale
| Verifica | Atteso | Dove leggerlo |
|---|---|---|
| celle | 12 000 | log.checkMesh.base |
| punti / facce | 14 091 / 38 000 | log.checkMesh.base |
| patch | inlet 200, outlet 200, walls 3 600 | constant/polyMesh/boundary |
| bounding box | (0 0 0) (0.6 0.2 0.1) | log.checkMesh.base |
| volume totale | 0.012 m³ | log.checkMesh.base |
| non-ortogonalità max | 0 | log.checkMesh.base |
| esito | Mesh OK. | log.checkMesh.base |
Per la sfera la stessa scheda si compila con i valori ricavati in queste note: bounding box dello STL (sezione 5), volume del fluido ≈ 0,011936 m³, una sola regione, numero di celle al di sotto di maxGlobalCells.
Approfondimento · Una scheda di accettazione con soglie numeriche dichiarate
In un contesto di qualità la scheda non dice solo «ok», ma confronta ogni valore con una soglia scritta prima di generare la mesh. Esempio di soglie per un caso incomprimibile stazionario come quelli del percorso, da adattare:
| Metrica | Soglia dichiarata | Motivazione |
|---|---|---|
| non-ortogonalità massima | ≤ 65° | uguale a maxNonOrtho del dizionario |
| skewness interna massima | ≤ 4 | soglia di segnalazione di checkMesh |
| regioni connesse | 1 | nessuna cavità residua |
| scarto di volume rispetto al valore atteso | ≤ 0.1 % | confronto con il volume calcolato dallo STL |
| celle sul diametro dell’ostacolo | ≥ 20 | risoluzione minima della curvatura |
| copertura degli strati (se attivi) | ≥ 95 % | strati effettivamente presenti |
13. Esercizio di approfondimento: piccola campagna di raffinamento
Prendi una nuova copia del caso della sfera e cambia refinementSurfaces.level da (2 2) a (1 1) e, in un’altra copia ancora, a (3 3). Non mescolare esecuzioni nello stesso caso. Per ciascuna variante registra: numero di celle finali, volume del dominio fluido, skewness, non-ortogonalità, tempo di generazione e osservazione della superficie in ParaView. Non confrontare soltanto il numero di celle: identifica anche dove viene aggiunta la risoluzione e se la triangolazione STL è sufficientemente fine.
La stessa procedura si può applicare successivamente a una superficie CAD di un reattore, una schiuma porosa o una rete espansa: si deve però distinguere la geometria reale, le approssimazioni CAD e il dominio computazionale che il modello effettivamente rappresenta.
Approfondimento · Che cosa aspettarsi dalle tre varianti
| Livello | Cella sulla superficie | Celle sul diametro | Cella / lato STL medio |
|---|---|---|---|
| (1 1) | 5 mm | ≈ 10 | ≈ 1.0 |
| (2 2) | 2.5 mm | ≈ 20 | ≈ 0.5 |
| (3 3) | 1.25 mm | ≈ 40 | ≈ 0.25 |
Con (1 1) le celle sono grandi quanto i triangoli: la superficie è descritta in modo grossolano. Con (3 3) le celle sono quattro volte più piccole dei triangoli: la mesh riproduce fedelmente le sfaccettature, quindi il guadagno geometrico rispetto a (2 2) è limitato, mentre il numero di celle vicino alla superficie cresce di circa 4 volte. È un caso in cui conviene rifare lo STL più fine prima di aumentare il livello. Il volume del fluido deve restare praticamente uguale nelle tre varianti; uno scostamento sensibile indica un problema di snapping.
for L in 1 3; do
d="$HOME/mesh-runs/sphere_level$L"
cp -a examples/02_sphere_snappy "$d"
sed -i "s/level (2 2);/level ($L $L);/" "$d/system/snappyHexMeshDict"
grep -n "level (" "$d/system/snappyHexMeshDict"
done
Approfondimento · Livelli di superficie e studio di convergenza: attenzione al significato di h
Cambiare solo refinementSurfaces.level raffina la zona vicino alla sfera ma lascia invariata la mesh lontana: non è un raffinamento sistematico e un GCI calcolato su queste tre mesh non ha il significato della Guida 4, perché non esiste un unico rapporto di raffinamento. Per uno studio di convergenza con snappy si dimezza la cella di sfondo (in blockMeshDict 120 × 40 × 20 invece di 60 × 20 × 10) mantenendo gli stessi livelli: tutte le celle, ovunque, si dimezzano e r = 2.
Il costo cresce di un fattore circa 8 per ogni dimezzamento in 3D: con 12 000, 96 000 e 768 000 celle di sfondo la terza mesh è già impegnativa. Per questo, nei casi industriali, si usano spesso rapporti più piccoli (per esempio r ≈ 1,5) e la formula generale del GCI per rapporti non interi.
14. Inventario del pacchetto ed esecuzione sicura
mesh-guide6/
├── examples/
│ ├── 01_channel_blockMesh/system/{blockMeshDict,controlDict}
│ ├── 02_sphere_snappy/system/{blockMeshDict,controlDict,snappyHexMeshDict}
│ └── 02_sphere_snappy/constant/triSurface/sphere.stl
├── scripts/run_mesh_case.sh
├── figures/
└── README e documentazione nelle due lingue
Lo script si rifiuta di scrivere in una directory di destinazione già esistente, conserva i template e genera log separati. Per impostare una destinazione nuova:
./scripts/run_mesh_case.sh sphere "$HOME/mesh-runs/sphere_trial_01"
Se la directory esiste già, il comando si interrompe. Per un nuovo tentativo specifica un nome diverso. Non eliminare i risultati precedenti senza una decisione esplicita; documentali come esperimenti separati.
Approfondimento · Lo script run_mesh_case.sh completo
#!/usr/bin/env bash
# Educational OpenFOAM 14 mesh-only workflow, GPL-3.0-or-later.
# Always copies templates to a NEW output path; never alters source templates.
set -euo pipefail
SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"
ROOT="$(cd -- "$SCRIPT_DIR/.." && pwd)"
case_name="${1:-}"
case "$case_name" in
channel) src="$ROOT/examples/01_channel_blockMesh" ;;
sphere) src="$ROOT/examples/02_sphere_snappy" ;;
*) echo "Usage: $0 channel|sphere [NEW-output-directory]" >&2; exit 2 ;;
esac
out="${2:-$ROOT/results/${case_name}_$(date +%Y%m%d_%H%M%S)}"
if [[ -e "$out" ]]; then echo "Refusing existing path: $out" >&2; exit 2; fi
if ! command -v blockMesh >/dev/null 2>&1; then echo "OpenFOAM environment not loaded. Try source /opt/OpenFOAM-14/etc/bashrc" >&2; exit 127; fi
if [[ "$case_name" == sphere ]] && ! command -v snappyHexMesh >/dev/null 2>&1; then echo "snappyHexMesh unavailable" >&2; exit 127; fi
mkdir -p -- "$(dirname -- "$out")"
cp -a -- "$src" "$out"
mkdir -p -- "$out/logs"
echo "Case: $out"
echo "Generating base mesh..."
blockMesh -case "$out" > "$out/logs/log.blockMesh" 2>&1
checkMesh -case "$out" -allGeometry -allTopology > "$out/logs/log.checkMesh.base" 2>&1
if [[ "$case_name" == sphere ]]; then
echo "Checking STL geometry..."
surfaceCheck "$out/constant/triSurface/sphere.stl" > "$out/logs/log.surfaceCheck" 2>&1
# -overwrite is safe here: it changes ONLY the fresh output copy.
echo "Running snappyHexMesh on fresh copy..."
snappyHexMesh -case "$out" -overwrite > "$out/logs/log.snappyHexMesh" 2>&1
checkMesh -case "$out" -allGeometry -allTopology > "$out/logs/log.checkMesh.final" 2>&1
fi
printf 'Result stored in: %s\n' "$out"
set -euo pipefail: il primo comando che fallisce interrompe lo script; il messaggio d’errore è nel log della fase in corso, perché l’output di ogni utility è rediretto inlogs/.- Codici di uscita: 2 per uso errato o destinazione già esistente, 127 se l’ambiente OpenFOAM non è caricato o manca
snappyHexMesh; negli altri casi il codice è quello dell’utility fallita. - La destinazione predefinita include data e ora, quindi due lanci successivi non si sovrascrivono mai.
- Il buon esito dello script significa che le utility sono terminate, non che la mesh sia buona: l’esito di
checkMeshva letto nel log («Mesh OK.» oppure «Failed n mesh checks.»).
Approfondimento · Gli altri file del pacchetto
Il blockMeshDict del caso della sfera è identico a quello del canale (la sfera è inserita dentro lo stesso dominio) e lo stesso vale per il controlDict. I metadati della geometria, in examples/GEOMETRY_METADATA.txt:
Units: metres. STL sphere radius: 0.025 m; center: (0.22, 0.10, 0.05) m.
Triangles: 896. Mesh is closed and manifold by edge-incidence check.
BlockMesh box: 0.60 x 0.20 x 0.10 m; cell count = 60 x 20 x 10 = 12000.
Example meshes are NOT simulated; test with OpenFOAM Foundation 14.
Lo STL è ASCII, con 896 triangoli e il nome solid sphere; i primi righi hanno questa forma:
solid sphere
facet normal 0.104522924211 0.0102946094885 0.994469194762
outer loop
vertex 0.22 0.1 0.075
vertex 0.22519779227 0.1 0.0744536900183
vertex 0.225097918149 0.101014038968 0.0744536900183
endloop
endfacet
...
Approfondimento · Rendere lo script più rigoroso senza modificarne la logica
Lo script del pacchetto è volutamente semplice. In uso reale conviene aggiungere, in coda, il controllo della versione, la registrazione dell’ambiente e il rifiuto automatico di una mesh che non supera checkMesh. Un esempio da inserire dopo l’ultimo checkMesh:
{ date -u; uname -a; foamVersion; } > "$out/logs/environment.txt" 2>&1
last="$out/logs/log.checkMesh.base"
[[ -f "$out/logs/log.checkMesh.final" ]] && last="$out/logs/log.checkMesh.final"
if ! grep -q "Mesh OK" "$last"; then
echo "checkMesh reported failures: see $last" >&2
exit 3
fi
Una copia modificata dello script va salvata con un nome diverso e documentata: lo script originale resta il riferimento della guida.
15. Bibliografia e letture ufficiali
- OpenFOAM Foundation 14, User Guide — 5.4
blockMesh: https://doc.cfd.direct/openfoam/user-guide-v14/blockmesh - OpenFOAM Foundation 14, User Guide — 5.5
snappyHexMesh: https://doc.cfd.direct/openfoam/user-guide-v14/snappyhexmesh - OpenFOAM Foundation 14, User Guide — 5.2 Mesh files: https://doc.cfd.direct/openfoam/user-guide-v14/mesh-files
- OpenFOAM Foundation 14, User Guide — contents: https://doc.cfd.direct/openfoam/user-guide-v14
- OpenFOAM Foundation 14, tutorials and examples: https://github.com/OpenFOAM/OpenFOAM-14
Nota di verifica. Il dizionario del canale, la superficie STL e gli script allegati sono stati controllati staticamente, incluse la chiusura topologica della superficie, la sintassi Bash e le dimensioni geometriche. L’esecuzione delle utility e la qualità della mesh generata devono essere confermate sul server del lettore. Gli schemi e i dati numerici di costo sono illustrativi.
Approfondimento · Letture di approfondimento sul metodo e sulla qualità della mesh
- H. Jasak, Error Analysis and Estimation for the Finite Volume Method with Applications to Fluid Flows, PhD thesis, Imperial College London, 1996. Fondamento della discretizzazione e della correzione non-ortogonale usate in OpenFOAM.
- F. Moukalled, L. Mangani, M. Darwish, The Finite Volume Method in Computational Fluid Dynamics, Springer, 2016. Trattazione completa, con esempi riferiti a OpenFOAM.
- J. H. Ferziger, M. Perić, R. L. Street, Computational Methods for Fluid Dynamics, 4th ed., Springer, 2020. Errori di discretizzazione, qualità della griglia, stime di errore.
- H. Schlichting, K. Gersten, Boundary-Layer Theory, Springer. Correlazioni di attrito e struttura dello strato limite usate per la stima di y⁺.
Guide collegate: server Linux da Windows, monitoraggio e convergenza, ParaView per l’analisi quantitativa, verifica, validazione e indipendenza dalla mesh, campagne parametriche e calcolo parallelo e il percorso Imparare OpenFOAM 14.