OpenFOAM 14, caso 03: mesh Gmsh importata con gmshToFoam

di giux CFD e OpenFOAM 7 min di lettura

Terzo caso del percorso Imparare OpenFOAM 14: procedura completa e riproducibile, importazione della mesh e validazione analitica. Rifaccio il canale laminare del caso 01 (già usato nello studio di convergenza del caso 02) con una mesh esterna generata da Gmsh invece che con blockMesh.

1. Scopo

Questa dispensa riproduce il benchmark del canale laminare usando una mesh esterna generata con Gmsh invece di blockMesh. Il modello fisico, le condizioni al contorno, le impostazioni del solutore e il riferimento analitico sono mantenuti volutamente identici a quelli del caso di formazione CFD 01. L’unica variazione controllata è il percorso di generazione della mesh.

Il flusso di lavoro è:

geometria Gmsh → mesh Gmsh → gmshToFoam → OpenFOAM → validazione analitica.

L’esercizio mostra come separare geometria e generazione della mesh dal solutore CFD, conservando un benchmark verificabile.

2. Risultato atteso

GrandezzaRisultato
Generatore di meshGmsh
Utility di importazionegmshToFoam
Formato della meshMSH 2.2 ASCII
Celle8000
Tipo di cella8000 esaedri
checkMeshMesh OK
Ux massima campionata dalla CFD0,0149625470 m/s
Ux massima teorica0,0150000000 m/s
RMSE normalizzato0,150343 %
Confronto con il caso 01numericamente indistinguibile

3. Prerequisiti

Verificare gli strumenti necessari:

gmsh --version
command -v gmshToFoam
command -v changeDictionary
command -v checkMesh
command -v foamRun

Se Gmsh non è installato su Debian:

su -
apt update
apt install gmsh
exit

L’ambiente di OpenFOAM 14 deve essere già caricato.

4. Creare la cartella del caso

Il nuovo caso riusa la fisica validata del caso 01, in modo che cambi soltanto il metodo di generazione della mesh.

cd $FOAM_RUN
mkdir -p 03_gmsh_channel/{0,constant,system,geometry}

Copiare dal caso 01 i dizionari fisici e numerici già validati:

cp 01_laminar_channel/0/U \
   01_laminar_channel/0/p \
   03_gmsh_channel/0/

cp 01_laminar_channel/constant/physicalProperties \
   01_laminar_channel/constant/momentumTransport \
   03_gmsh_channel/constant/

cp 01_laminar_channel/system/controlDict \
   01_laminar_channel/system/fvSchemes \
   01_laminar_channel/system/fvSolution \
   03_gmsh_channel/system/

cd $FOAM_RUN/03_gmsh_channel

In questo caso non si usa nessun blockMeshDict.

5. Geometria Gmsh e mesh strutturata

Creare geometry/channel.geo con il seguente contenuto completo:

// Laminar channel benchmark
// Geometry: 0.5 x 0.01 x 0.001 m
// Structured mesh: 200 x 40 x 1 cells

Mesh.MshFileVersion = 2.2;
Mesh.Binary = 0;

// Points
Point(1) = {0.0, -0.005, 0.0};
Point(2) = {0.5, -0.005, 0.0};
Point(3) = {0.5,  0.005, 0.0};
Point(4) = {0.0,  0.005, 0.0};

Point(5) = {0.0, -0.005, 0.001};
Point(6) = {0.5, -0.005, 0.001};
Point(7) = {0.5,  0.005, 0.001};
Point(8) = {0.0,  0.005, 0.001};

// Edges
Line(1) = {1,2};
Line(2) = {2,3};
Line(3) = {3,4};
Line(4) = {4,1};

Line(5) = {5,6};
Line(6) = {6,7};
Line(7) = {7,8};
Line(8) = {8,5};

Line(9)  = {1,5};
Line(10) = {2,6};
Line(11) = {3,7};
Line(12) = {4,8};

// Surfaces
Curve Loop(1) = {1,2,3,4};
Plane Surface(1) = {1};

Curve Loop(2) = {5,6,7,8};
Plane Surface(2) = {2};

Curve Loop(3) = {1,10,-5,-9};
Plane Surface(3) = {3};

Curve Loop(4) = {2,11,-6,-10};
Plane Surface(4) = {4};

Curve Loop(5) = {3,12,-7,-11};
Plane Surface(5) = {5};

Curve Loop(6) = {4,9,-8,-12};
Plane Surface(6) = {6};

// Volume
Surface Loop(1) = {-1,2,3,4,5,6};
Volume(1) = {1};

// Structured resolution
Transfinite Curve {1,3,5,7} = 201;
Transfinite Curve {2,4,6,8} = 41;
Transfinite Curve {9,10,11,12} = 2;

Transfinite Surface {1,2,3,4,5,6};
Recombine Surface {1,2,3,4,5,6};
Transfinite Volume {1} = {1,2,3,4,5,6,7,8};

// OpenFOAM boundary names
Physical Surface("frontAndBack") = {1,2};
Physical Surface("walls")        = {3,5};
Physical Surface("outlet")       = {4};
Physical Surface("inlet")        = {6};

Physical Volume("fluid") = {1};

Le definizioni Transfinite specificano il numero di nodi. Di conseguenza:

  • 201 nodi in x → 200 celle
  • 41 nodi in y → 40 celle
  • 2 nodi in z → 1 cella

Totale:

200 × 40 × 1 = 8000 celle.

6. Generare la mesh con Gmsh

Eseguire:

gmsh -3 geometry/channel.geo \
     -format msh2 \
     -o geometry/channel.msh

Verificare che il file esista:

ls -lh geometry/channel.msh

Il formato msh2 è usato deliberatamente perché è semplice da gestire per gmshToFoam e conserva i nomi fisici necessari per la mappatura delle patch.

7. Importare la mesh in OpenFOAM

Eseguire:

gmshToFoam geometry/channel.msh | tee log.gmshToFoam

L’importazione validata ha riportato:

Vertices to be read: 16482
Cells to be read: 24480

Cells:
    total:8000
    hex :8000
    prism:0
    pyr :0
    tet :0

Patch 0 gets name frontAndBack
Patch 1 gets name walls
Patch 2 gets name outlet
Patch 3 gets name inlet

Writing zone 0 to cellZone fluid and cellSet
End

I nomi delle Physical Surface di Gmsh vengono quindi trasferiti ai nomi delle patch di OpenFOAM.

8. Correggere i tipi di patch di OpenFOAM

Dopo gmshToFoam i gruppi di superfici importati sono di norma patch generiche. Il solutore richiede:

  • walls → wall
  • frontAndBack → empty

Creare system/changeDictionaryDict:

FoamFile
{
    format      ascii;
    class       dictionary;
    object      changeDictionaryDict;
}

boundary
{
    walls
    {
        type wall;
    }

    frontAndBack
    {
        type empty;
    }
}

Applicare le modifiche:

changeDictionary

9. Verificare la mesh importata

Eseguire:

checkMesh | tee log.checkMesh

Estrarre i valori principali:

grep -E \
"points:|faces:|cells:|hexahedra:|max aspect ratio|Mesh OK" \
log.checkMesh

Risultato validato:

points:               16482
faces:                32240
cells:                8000
hexahedra:            8000
Mesh OK.

Verificare il file boundary:

grep -A6 -E \
"inlet|outlet|walls|frontAndBack" \
constant/polyMesh/boundary

Tipi di patch operativi attesi:

frontAndBack   type empty
walls          type wall
outlet         type patch
inlet          type patch

10. File fisici e numerici

I seguenti file sono volutamente identici a quelli del caso 01:

  • 0/U
  • 0/p
  • constant/physicalProperties
  • constant/momentumTransport
  • system/controlDict
  • system/fvSchemes
  • system/fvSolution

Questo è essenziale per il confronto: il generatore di mesh è l’unica variazione voluta.

I valori fisici principali restano:

Uinlet = (0.01 0 0) m/s
nu     = 1e-6 m^2/s
simulationType = laminar

Il calcolo stazionario usa solver incompressibleFluid; e endTime 2000;, esattamente come nel caso 01.

11. Eseguire il calcolo CFD

Eseguire:

foamRun | tee log.foamRun

Esaminare la parte finale del log:

tail -40 log.foamRun

Il calcolo deve terminare normalmente con:

End

12. Ricreare il campionamento del profilo di velocità

Creare system/graphUniform:

FoamFile
{
    format      ascii;
    class       dictionary;
    object      graphUniform;
}

start       (0.45 -0.004875 0.0005);
end         (0.45  0.004875 0.0005);
nPoints     40;

fields      (U);

axis        y;

#includeEtc "caseDicts/postProcessing/graphs/graphUniform.cfg"

Eseguire:

foamPostProcess \
    -solver incompressibleFluid \
    -func graphUniform \
    -latestTime

Individuare l’output:

find postProcessing/graphUniform -name line.xy

File atteso:

postProcessing/graphUniform/2000/line.xy

13. Validazione analitica quantitativa

Eseguire lo stesso confronto usato per il benchmark con blockMesh:

f=$(find postProcessing/graphUniform -name line.xy | sort -V | tail -1)

awk '
BEGIN {
    H=0.01
    Umean=0.01
    UmaxTheory=1.5*Umean
    umax=-1
}
$1 !~ /^#/ && NF >= 2 {
    y=$1
    u=$2

    uth=UmaxTheory*(1-(2*y/H)^2)

    e=u-uth
    ae=(e<0 ? -e : e)

    sumsq += e*e
    n++

    if (ae > maxerr)
        maxerr=ae

    if (u > umax)
        umax=u
}
END {
    rmse=sqrt(sumsq/n)

    printf "Points                 : %d\n",n
    printf "CFD maximum Ux         : %.10f m/s\n",umax
    printf "Theoretical Umax       : %.10f m/s\n",UmaxTheory
    printf "RMSE                   : %.10e m/s\n",rmse
    printf "Normalized RMSE        : %.6f %%\n",100*rmse/UmaxTheory
    printf "Maximum abs error      : %.10e m/s\n",maxerr
    printf "Max error / Umax       : %.6f %%\n",100*maxerr/UmaxTheory
}' "$f"

Risultato validato con Gmsh:

Points              : 40
CFD maximum Ux      : 0.0149625470 m/s
Theoretical Umax    : 0.0150000000 m/s
RMSE                : 2.2551524240e-05 m/s
Normalized RMSE     : 0.150343 %
Maximum abs error   : 2.8078000000e-05 m/s
Max error / Umax    : 0.187187 %

14. Confronto diretto con il benchmark blockMesh

Percorso di generazione della meshCelleUmax [m/s]NRMSE [%]
blockMesh – caso 0180000,01496254700,150343
Gmsh -> gmshToFoam – caso 0380000,01496254700,150343

I valori di RMSE differiscono soltanto di circa

5,31 × 10−12 m/s,

una differenza trascurabile per questo benchmark. Le due soluzioni CFD sono numericamente indistinguibili.

Ciò conferma che la soluzione non è legata a blockMesh: una mesh strutturata equivalente importata da Gmsh produce lo stesso risultato fisico.

15. Visualizzazione in ParaView

Creare un file marcatore:

cd $FOAM_RUN/03_gmsh_channel
touch gmsh_channel.foam

Se pvserver è già in esecuzione e il client è connesso, non serve riconnettersi. In ParaView:

File -> Open

e aprire:

$FOAM_RUN/03_gmsh_channel/gmsh_channel.foam

Selezionare internalMesh, abilitare U e p, quindi premere Apply.

Visualizzazione consigliata:

Representation = Surface With Edges
Coloring       = U -> Magnitude

L’immagine seguente mostra il campo di velocità validato sulla mesh importata da Gmsh.

ParaView 5.13.2 che mostra il modulo della velocità sul canale con la mesh importata da Gmsh, rappresentazione Surface With Edges
Figura 1: modulo della velocità sulla mesh importata da Gmsh, in ParaView.

16. Controllo delle linee di corrente

Si possono usare le stesse impostazioni dello Stream Tracer del caso 01:

ProprietàValore
VectorsU
Integration DirectionFORWARD
Seed TypeLine
Point1(0.001, -0.0045, 0.0005)
Point2(0.001, +0.0045, 0.0005)
Resolution20
Maximum Streamline Length0,5 m

Le linee di corrente devono restare rette e parallele all’asse del canale, come nella soluzione ottenuta con blockMesh.

17. Risoluzione dei problemi più comuni

SintomoControllo / correzione
i nomi fisici di Gmsh non compaiono come patch di OpenFOAMassicurarsi che le superfici siano dichiarate come gruppi Physical Surface
il comportamento dell’importazione è incoerentegenerare un file MSH 2.2 ASCII con -format msh2
walls resta una patch genericaeseguire changeDictionary con il dizionario fornito
frontAndBack resta una patch genericaimpostarne il tipo a empty prima del calcolo CFD
il numero di celle di checkMesh non è 8000verificare il numero di nodi transfinite: 201, 41 e 2
ParaView non mostra il file del casocrearlo con touch gmsh_channel.foam
non compare nessun file di post-processingusare foamPostProcess -solver incompressibleFluid -func graphUniform -latestTime

18. Criteri di accettazione

Criterio di accettazioneStato
Gmsh genera la mesh di volume strutturataSUPERATO
gmshToFoam importa tutte le patch con nomeSUPERATO
la mesh importata contiene 8000 esaedriSUPERATO
checkMesh riporta Mesh OKSUPERATO
walls è wall e frontAndBack è emptySUPERATO
il calcolo OpenFOAM converge normalmenteSUPERATO
il profilo di velocità concorda con la teoria di PoiseuilleSUPERATO
NRMSE = 0,150343 %SUPERATO
il risultato con Gmsh coincide con il benchmark blockMeshSUPERATO
la visualizzazione in ParaView è correttaSUPERATO

Stato finale: CASO 03 COMPLETATO E VALIDATO.

19. Lista di controllo per la riproducibilità

  • Verificare Gmsh e le utility di OpenFOAM.
  • Creare 03_gmsh_channel.
  • Copiare dal caso 01 i file fisici/numerici validati.
  • Creare il file completo geometry/channel.geo.
  • Generare geometry/channel.msh in formato MSH 2.2.
  • Importare con gmshToFoam.
  • Creare changeDictionaryDict ed eseguire changeDictionary.
  • Eseguire checkMesh e verificare 8000 esaedri più Mesh OK.
  • Eseguire foamRun.
  • Creare graphUniform e campionare il profilo di velocità finale.
  • Confrontare il profilo campionato con la soluzione analitica di Poiseuille.
  • Creare gmsh_channel.foam.
  • Aprire il caso tramite la connessione client-server di ParaView già esistente.
  • Verificare U, p, gli spigoli della mesh e le linee di corrente.

Il caso dimostra un flusso di lavoro completo con mesh esterna, mantenendo la stessa fisica CFD validata del caso di riferimento.

Caso precedente: caso 02 – studio di convergenza della mesh. Caso successivo: caso 04 – mesh parametrica con Gmsh e Python, dove la stessa geometria viene generata da uno script Python parametrico.

#CFD #Gmsh #mesh #OpenFOAM #ParaView

Lascia un commento

Il tuo indirizzo email non sarà pubblicato. I campi obbligatori sono contrassegnati *