OpenFOAM 14, parte 8: leggere e creare casi in autonomia
Parte 8 di 8 – l’ultima unità – della serie sulla costruzione dei casi in Imparare OpenFOAM 14. Le sette parti precedenti hanno smontato, una alla volta, ciascun gruppo di file: il linguaggio dei dizionari, la mesh, i campi e le condizioni al contorno, il controllo dell’esecuzione, gli schemi, i solutori lineari e gli algoritmi, e infine le proprietà fisiche e i modelli nella Parte 7.
Questa parte rimette insieme l’intero insieme e cambia punto di vista. Non spiega più un singolo dizionario: insegna a trovare informazioni affidabili nella documentazione ufficiale di OpenFOAM Foundation v14 e negli strumenti installati, a trasformare quelle informazioni in una configurazione minima funzionante, a diagnosticare ciò che va storto e a costruire un caso completo partendo da una cartella vuota – il progetto finale – spiegando ogni file che contiene.

Prerequisiti. Unità finale, dopo le Parti 1–7. Piattaforma: OpenFOAM Foundation 14 (openfoam.org / CFD Direct), shell Bash con l’ambiente caricato. Il motto dell’unità, dalla sua copertina: capire → trovare → configurare → verificare → spiegare.
Il contratto di apprendimento finale
Questa è l’unità conclusiva del corso di costruzione dei casi con OpenFOAM 14. Non è un catalogo di tutte le parole chiave disponibili. L’obiettivo è individuare informazioni affidabili, trasformarle in una configurazione minima funzionante e spiegare ciò che è stato fatto.
Dovresti saper fare queste cinque cose
- Identificare il requisito fisico e il componente di OpenFOAM che ne è responsabile.
- Individuare il dizionario, il campo, il modello o l’utility v14 corretti.
- Scoprire la sintassi documentata e gli esempi invece di tirare a indovinare.
- Applicare una modifica piccola e controllata e diagnosticare qualsiasi errore.
- Assemblare, eseguire e documentare un nuovo caso CFD semplice.

Regola di lavoro
Trova le prove prima di fare una modifica. Una parola chiave valida in un altro solutore, in un altro modello fisico o in un’altra distribuzione di OpenFOAM non è necessariamente adatta a questo caso.
Approfondimento facoltativo: la documentazione come competenza ingegneristica
Gli errori di configurazione non sono semplici errori di battitura. Un file plausibile può codificare il modello fisico sbagliato. Lo scopo della capacità di leggere la documentazione è rendere ogni ipotesi esplicita e verificabile.
Approfondimento: il ciclo in cinque passi, passo per passo, con lo strumento che prova ciascun passo
Il flusso di lavoro della figura è un ciclo, non una linea: il passo “Spiegare” di una modifica genera la domanda successiva. Ciò che lo rende ingegneria e non procedere per tentativi è che ogni passo lascia una traccia verificabile. La tabella associa ciascun passo allo strumento di OpenFOAM v14 che produce quella traccia e alla parte di questa serie in cui lo strumento è stato introdotto.
| Passo | Domanda a cui risponde | Prova tipica in v14 | Dove nella serie |
|---|---|---|---|
| Domanda | Quale comportamento fisico o numerico deve cambiare? | Un’ipotesi di una riga che nomina file e voce | Sezione 01 più sotto |
| Documentazione | Quale oggetto lo implementa e che cosa richiede? | Sezione della User Guide, foamInfo, foamToC, il percorso di un tutorial | Sezioni 02, 03, 06–08 |
| Modifica minima | Qual è la modifica più piccola che lo esprime? | Una copia salvata più foamDictionary -set e diff -u | Sezione 05; Parte 1 |
| Verifica | È sintatticamente ammissibile e funziona? | foamDictionary -value, checkMesh, il log del solutore | Sezioni 12–15 |
| Spiegazione | Perché questa modifica è corretta per questa fisica? | Una registrazione scritta: file → voce → ruolo → fonte | Esercizi 16–18, progetto finale |
Ne seguono due conseguenze. Primo: una modifica che non si può ricondurre a una fonte non è finita, anche se il calcolo va a buon fine. Secondo: un fallimento non è tempo perso: se ogni passo è stato registrato, il punto in cui la catena si è interrotta è visibile e la diagnosi parte da lì invece che dall’inizio.
01 Partire dal requisito CFD
Un compito sconosciuto non si risolve aprendo file a caso. Prima classifica la richiesta: riguarda la geometria, i campi, le proprietà fisiche, gli schemi numerici, la soluzione lineare, il controllo dell’esecuzione o il post-processing?
| Richiesta | Da esaminare per primo |
|---|---|
| Cambiare l’altezza del canale | system/blockMeshDict; rigenerare e controllare la mesh |
| Cambiare la velocità in ingresso | 0/U e la patch di mesh corrispondente |
| Cambiare la viscosità cinematica | constant/physicalProperties |
| Cambiare gradiente/discretizzazione | system/fvSchemes |
| Cambiare la tolleranza del solutore della pressione | system/fvSolution |
| Cambiare la frequenza di scrittura | system/controlDict |
| Aggiungere una misura integrata | function object; capitolo 7 della guida |
Prima di modificare, scrivi un’ipotesi di una riga
Esempio: “Per imporre la velocità all’ingresso mi serve la patch inlet in 0/U, non una modifica del tipo di contorno della mesh.” Verifica che la patch esista già nella mesh.
Una distinzione utile
Una patch di mesh descrive geometria/topologia; una condizione al contorno di un campo descrive come si comporta una variabile fisica su quella patch.
Approfondimento: la tabella di instradamento, estesa alle richieste che la tabella non elenca
Le sette righe qui sopra coprono il progetto finale. In pratica la stessa classificazione si estende ad altre richieste frequenti. Le righe seguenti colmano le lacune più comuni; ciascuna indica il file v14 e il capitolo della User Guide in cui la sintassi è documentata.
| Richiesta | Da esaminare per primo | Guida / serie |
|---|---|---|
| Cambiare il passo temporale o il tempo finale | system/controlDict (deltaT, endTime; nei calcoli transitori adjustTimeStep, maxCo) | 4.4; Parte 4 |
| Passare da laminare a un modello di turbolenza | constant/momentumTransport, poi i nuovi campi (k, epsilon o omega, nut) in 0/ | 8.2; Parte 7 |
| Campo iniziale non uniforme | internalField di 0/<campo>, field functions e field sources | 6.1–6.2 |
| Aggiungere un termine sorgente o un vincolo | constant/fvModels, system/fvConstraints | foamToC -fvModels, -fvConstraints |
| Eseguire in parallelo | system/decomposeParDict | 3.4; caso svolto 5 |
| Raffinare la mesh | numero di celle e grading in system/blockMeshDict | 5.4; Parte 2, caso svolto 2 |
| Cambiare lo smoother del solutore lineare o il rilassamento | system/fvSolution | 4.6; Parte 6 |
La riga di ipotesi ha una forma precisa: “per ottenere X modifico la voce Y nel file Z e mi aspetto W.” Se non sai riempire Y e Z, la fase di classificazione non è finita; se non sai riempire W, non potrai capire se la modifica ha funzionato.
Approfondimento: tipo di patch, condizione al contorno e campo iniziale sono tre livelli diversi
La distinzione utile qui sopra è all’origine di molti errori, quindi conviene renderla concreta. Il livello della mesh sta in constant/polyMesh/boundary, generato da blockMesh a partire dalla lista boundary di blockMeshDict (Parte 2). Ogni patch ha un nome, un tipo geometrico (patch, wall, empty, symmetryPlane, wedge, cyclic…), un numero di facce e una faccia iniziale. Il livello del campo sta in 0/U, 0/p e così via (Parte 3): per ogni nome di patch una condizione al contorno dice al solutore come si comporta lì quella variabile. Il livello del campo iniziale è l’internalField degli stessi file: il valore di partenza nelle celle, che in un calcolo stazionario è solo una stima iniziale.
// constant/polyMesh/boundary (generated by blockMesh, excerpt)
frontAndBack
{
type empty;
nFaces 2560;
startFace 2656;
}
// 0/U (written by you)
frontAndBack { type empty; }
Due regole collegano i livelli. (1) Ogni nome di patch della mesh deve comparire nel boundaryField di ogni campo letto dal solutore (vale anche una chiave con espressione regolare come "(walls|frontAndBack)"). (2) I tipi di vincolo (empty, wedge, symmetryPlane, cyclic, processor) sono affermazioni geometriche, e il campo deve ripetere lo stesso tipo di vincolo su quella patch: una patch di mesh empty con una velocità fixedValue viene rifiutata alla lettura del campo. Le patch generiche patch e wall, invece, accettano qualsiasi condizione fisica compatibile – è lì che avviene la scelta di modellazione. Con molte patch è facile dimenticarne una; controllalo in modo meccanico:
# Patch names known to the mesh (checkMesh prints them with face counts)
checkMesh | sed -n '/Patch/,/^$/p'
# Patch names known to each field file
foamDictionary -entry boundaryField -keywords 0/U
foamDictionary -entry boundaryField -keywords 0/p
02 Orientarsi nella User Guide ufficiale v14
La documentazione è strutturata. Usa il suo indice prima di cercare con termini generici. I numeri qui sotto si riferiscono alla User Guide della versione 14 di OpenFOAM Foundation / CFD Direct.
| Posizione nella guida | A che cosa serve |
|---|---|
| Capitolo 2 | Casi svolti ed esempi |
| 4.1–4.2 | Cartelle del caso, dizionari, formato dei campi, inclusioni |
| 4.4 | controlDict e controlli di tempo e di scrittura |
| 4.5 | fvSchemes; operatori e discretizzazione |
| 4.6 | fvSolution; solutori e algoritmi di accoppiamento |
| 4.7 | foamDictionary, foamSearch, foamGet, foamInfo, foamToC, foamFind |
| Capitolo 5 | Generazione della mesh, tipi di patch e blockMesh |
| Capitolo 6 | Campi e condizioni al contorno |
| Capitolo 7 | Function object e post-processing |
| Capitolo 8 | Modelli di proprietà fisiche e di trasporto |
Riferimento principale
https://doc.cfd.direct/openfoam/user-guide-v14/
Controlla sempre il selettore di versione della pagina. Gli esempi che si trovano in rete sono spesso scritti per versioni diverse di OpenFOAM.
Approfondimento: collegamenti diretti alle sezioni v14 che userai di più
La guida ufficiale v14 ha 8 capitoli e 39 sezioni numerate. La tabella elenca le pagine che corrispondono agli argomenti di questa serie, con i titoli esatti (il capitolo 6 si intitola ufficialmente “Field and boundary initialisation”: oltre alle condizioni al contorno tratta le field functions e le field sources).
| Sezione | Titolo ufficiale | Parte della serie |
|---|---|---|
| 2.1 | Backward-facing step (tutorial incompressibleFluid/pitzDailySteady) | Esercizio 1 |
| 3.5 | Solver modules | Sezione 10 |
| 4.1 | File structure of OpenFOAM cases | Parte 1 |
| 4.2 | Basic input/output file format | Parte 1, sezione 29 |
| 4.4 | Time and data input/output control | Parte 4 |
| 4.5 | Numerical schemes | Parte 5 |
| 4.6 | Solution and algorithm control | Parte 6 |
| 4.7 | Case management tools (4.7.1–4.7.9) | Sezioni 04–09 |
| 5.4 | Mesh generation with the blockMesh utility | Parte 2 |
| 5.3 | Mesh boundary | Sezioni 01, 14 |
| 6.3 | Introduction to boundary conditions | Parte 3 |
| 6.4 | Derived boundary conditions | Esercizio 3 |
| 7.2 | Post-processing command line interface (CLI) | Sezioni 09, 28 |
| 8 | Models and physical properties | Parte 7 |
Due trappole quando si cerca in rete. Le pagine dell’altra famiglia di OpenFOAM (openfoam.com, con numeri di versione come v2312 o v2406) descrivono un codice diverso, con nomi di solutori e di file diversi. Le pagine di versioni Foundation più vecchie (per esempio v8 o v10) descrivono la stessa famiglia prima dei solutori modulari. In entrambi i casi l’URL o il selettore di versione lo dicono subito: per questo corso deve comparire user-guide-v14.
03 Imparare dai tutorial senza copiare alla cieca
Un tutorial è una configurazione svolta, non un’impostazione predefinita universale. Lo scopo della ricerca di esempi è scoprire il più piccolo schema pertinente e verificarlo rispetto al solutore e al modello fisico scelti.
Esplorazione in sola lettura in una shell OpenFOAM 14 caricata correttamente:
foamVersion
echo "$FOAM_TUTORIALS"
find "$FOAM_TUTORIALS" -name fvSolution | head
find "$FOAM_TUTORIALS" -name momentumTransport | head
La registrazione delle prove
Per un’opzione sconosciuta registra: (1) il comportamento richiesto; (2) la sezione della documentazione ufficiale; (3) uno o due esempi funzionanti; (4) la sintassi scelta; (5) perché si applica alla fisica in esame.
Esempio: condizione al contorno turbolenta
Un esempio con flowRateInletVelocity può richiedere voci che non esistono per fixedValue. Non sostituire la sola parola chiave type senza controllare i parametri richiesti.
Chiedi a OpenFOAM 14 l’uso e i casi di esempio:
foamInfo flowRateInletVelocity
Un errore frequente
Un caso stazionario e uno transitorio possono avere alberi di cartelle simili pur richiedendo schemi temporali e controlli d’algoritmo diversi. Esamina fvSchemes e fvSolution insieme.
Approfondimento: che cosa richiede davvero flowRateInletVelocity in v14 (una registrazione delle prove compilata)
L’intestazione del sorgente della condizione v14 (src/finiteVolume/fields/fvPatchFields/derived/flowRateInletVelocity/) la descrive come un ingresso di velocità con velocità normale alla patch, regolata per ottenere una portata massica, una portata volumetrica o una velocità media specificate, con un profilo facoltativo. Va data esattamente una delle tre specifiche di portata. Le proprietà documentate sono:
| Proprietà | Significato | Obbligatoria |
|---|---|---|
volumetricFlowRate | Portata volumetrica [m³/s] | una delle tre |
massFlowRate | Portata massica [kg/s]; usa rho (nome del campo di densità, predefinito rho) oppure rhoInlet | una delle tre |
meanVelocity | Velocità media [m/s] | una delle tre |
profile | Profilo di velocità (per es. laminarBL, turbulentBL) | no |
rho, rhoInlet, alpha | Nome del campo di densità, densità in ingresso, nome del campo di frazione volumica | no |
È proprio per questo che cambiare soltanto type fixedValue; in type flowRateInletVelocity; fallisce: la nuova condizione non legge un vettore imposto, le serve una grandezza di portata. Per il canale del progetto finale l’equivalente di U = 0,01 m/s attraverso un ingresso di 0,020 m × 0,001 m è Q = 0,01 × 0,020 × 0,001 = 2 × 10⁻⁷ m³/s:
inlet
{
type flowRateInletVelocity;
volumetricFlowRate 2e-07; // m^3/s = 0.01 m/s * 0.020 m * 0.001 m
// alternatives (one of the three):
// meanVelocity 0.01; // m/s
// massFlowRate ...; // kg/s, needs rho or rhoInlet
// profile laminarBL; // optional velocity profile
value uniform (0 0 0);
}
La registrazione delle prove per questa scelta sarebbe: (1) imporre la portata invece di un vettore velocità; (2) User Guide 4.7.5 (foamInfo) e l’intestazione del sorgente; (3) i casi di esempio elencati da foamInfo flowRateInletVelocity; (4) il listato qui sopra; (5) si applica perché l’ingresso è una patch piana con una normale ben definita e la grandezza d’interesse è la portata. Il titolo dell’esempio qui sopra lo chiama “turbolento”; in realtà la condizione non è specifica della turbolenza – si usa indifferentemente in casi laminari e turbolenti, e il profilo turbulentBL è solo una delle opzioni.
Approfondimento: come riconoscere in trenta secondi un caso stazionario da uno transitorio
Due casi con alberi di cartelle identici possono risolvere problemi diversi. Le firme stanno in tre file, ed è per questo che l’avvertenza dice di leggerli insieme:
| File / voce | Stazionario (tipo SIMPLE) | Transitorio (tipo PIMPLE) |
|---|---|---|
fvSchemes → ddtSchemes/default | steadyState | Euler, backward, CrankNicolson 0.9… |
fvSolution → dizionario dell’algoritmo | SIMPLE (spesso consistent yes;, residualControl) | PIMPLE (nOuterCorrectors, nCorrectors) |
fvSolution → relaxationFactors | Indispensabili (per es. p 0.3, U 0.7; oppure ≈0,9 con SIMPLEC) | Spesso assenti o pari a 1 |
controlDict → deltaT | Pseudo-tempo: 1 significa “un’iterazione” | Passo temporale fisico in secondi, spesso con adjustTimeStep e maxCo |
| Cartelle temporali scritte | Numeri di iterazione: 50, 100… | Tempi fisici: 0.01, 0.02… |
Copiare l’fvSolution di un tutorial transitorio in un caso stazionario (o viceversa) di solito non fallisce in fase di lettura; fallisce dopo, come divergenza o come calcolo assurdamente lento. Il tutorial v14 incompressibleFluid/pitzDailySteady è l’esempio stazionario di riferimento: steadyState in fvSchemes, un dizionario SIMPLE con consistent yes; e residualControl in fvSolution.
04 foamDictionary: lettura strutturale
L’utility foamDictionary analizza il linguaggio dei dizionari di OpenFOAM. Usala per esaminare la struttura effettiva invece di affidarti solo alla ricerca manuale nel testo.
Queste forme di interrogazione sono documentate nella sezione 4.7.2 della User Guide v14:
foamDictionary system/fvSchemes
foamDictionary -entry divSchemes system/fvSchemes
foamDictionary -entry "divSchemes/div(phi,U)" \
system/fvSchemes
foamDictionary -entry divSchemes -keywords \
system/fvSchemes
Che cosa dovresti osservare
- Un dizionario può contenere altri dizionari.
- Il carattere
/in un percorso di voce attraversa i sotto-dizionari annidati. - Parole chiave come
div(phi,U)contengono parentesi e vanno racchiuse tra virgolette nella shell. - Il parser espande le macro per determinate interrogazioni di voci; la semplice visualizzazione del testo può non farlo.
Restituisci il valore selezionato senza la sua parola chiave:
foamDictionary -entry "divSchemes/div(phi,U)" \
-value system/fvSchemes
Approfondimento: che cosa stampano le quattro interrogazioni, e le altre opzioni da conoscere
Eseguite sull’fvSchemes del progetto finale (sezione 26), le interrogazioni danno un risultato prevedibile. La prima (senza -entry) stampa l’intero file così come il parser lo ha capito, senza commenti e con impaginazione normalizzata: un modo rapido per vedere se una graffa fuori posto ha inghiottito metà del file. La seconda stampa solo il sotto-dizionario divSchemes. Le ultime due danno:
$ foamDictionary -entry divSchemes -keywords system/fvSchemes
default
div(phi,U)
div((nuEff*dev2(T(grad(U)))))
$ foamDictionary -entry "divSchemes/div(phi,U)" -value system/fvSchemes
bounded Gauss upwind
Perché le virgolette: senza di esse Bash interpreta le parentesi di div(phi,U) come sintassi della shell e si ferma con un errore di sintassi prima ancora che foamDictionary parta. Altre opzioni documentate nella User Guide 4.7.2, tutte utili per raccogliere prove:
foamDictionary -help # authoritative option list
foamDictionary -expand system/fvSchemes # macros and #include expanded
foamDictionary -entry divSchemes -keywords system/fvSchemes
foamDictionary -entry "divSchemes/turbulence" -add "bounded Gauss upwind" system/fvSchemes
foamDictionary -set "startFrom=startTime, startTime=0" system/controlDict
foamDictionary -entry boundaryField -keywords 0/U # patch names in a field file
-expand stampa il file dopo la sostituzione di #include e $macro, cioè ciò che il solutore riceverà davvero. -add crea una voce che non esiste ancora; -set ne modifica una esistente e accetta un elenco di assegnazioni parola=valore separate da virgole. L’ultima riga del listato applica la stessa lettura strutturale a un file di campo: le parole chiave di boundaryField sono i nomi di patch dichiarati dal campo – la prima cosa da confrontare con la mesh quando un errore dice che non si trova la voce di una patch. In caso di dubbio su un’opzione, foamDictionary -help sulla tua installazione fa testo.
05 foamDictionary: modifica controllata
Non modificare un caso di riferimento mentre sperimenti. Lavora su una copia tua, conserva la configurazione originale e cambia una cosa alla volta.
Una sintassi -set documentata in v14; la modifica è un esercizio, non lo schema migliore in assoluto:
cp system/fvSchemes system/fvSchemes.before-ex08
foamDictionary -entry "divSchemes/div(phi,U)" \
-set "bounded Gauss upwind" system/fvSchemes
foamDictionary -entry "divSchemes/div(phi,U)" \
-value system/fvSchemes
Tre punti di controllo
- Prima: salva il valore originale e dichiara l’effetto previsto.
- Dopo: interroga di nuovo la voce modificata; esamina la differenza.
- Poi: convalida con un controllo o un calcolo appropriato.
Rivedi la modifica con i normali strumenti di Linux:
diff -u system/fvSchemes.before-ex08 system/fvSchemes
Non confondere una sintassi valida con una buona modellazione
Uno schema può essere accettato dal parser ed essere comunque inadatto ai requisiti di accuratezza, stabilità o conservazione. La valutazione numerica viene dopo la verifica della sintassi.
Approfondimento: leggere il diff, tornare indietro, e quanto costa “bounded Gauss upwind” in accuratezza
Applicata a una copia del tutorial v14 pitzDailySteady (la cui voce originale è bounded Gauss linearUpwind grad(U)), la parte essenziale dell’output di diff -u è simile a questa: le righe che iniziano con - esistono solo nella copia salvata, quelle che iniziano con + solo nel file modificato, le altre sono contesto invariato.
--- system/fvSchemes.before-ex08
+++ system/fvSchemes
@@ (line numbers) @@
divSchemes
{
default none;
- div(phi,U) bounded Gauss linearUpwind grad(U);
+ div(phi,U) bounded Gauss upwind;
div(phi,k) bounded Gauss limitedLinear 1;
div(phi,epsilon) bounded Gauss limitedLinear 1;
Poiché foamDictionary -set riscrive il file passando dal parser, il diff può mostrare anche cambiamenti di impaginazione (allineamenti, commenti rimossi) altrove. È un motivo in più per tenere la copia .before-ex08: tornare indietro è un solo comando, e l’esperimento è reversibile per costruzione.
cp system/fvSchemes.before-ex08 system/fvSchemes # restore the original
Perché la modifica è “un esercizio, non lo schema migliore in assoluto”. Gauss upwind prende il valore sulla faccia dalla cella a monte: è limitato e robusto ma solo del primo ordine, e il suo errore di troncamento si comporta come una diffusione artificiale νnum ≈ |u|Δx/2. Per la mesh del progetto finale Δx = 0,30/80 = 3,75 mm e U = 0,01 m/s, quindi νnum ≈ 1,9 × 10⁻⁵ m²/s – circa il doppio della viscosità fisica di 1,0 × 10⁻⁵ m²/s nella direzione del flusso. Nella parte sviluppata di un canale i gradienti in direzione del flusso si annullano e il danno è piccolo; nella zona d’ingresso, o in qualunque flusso non allineato con la mesh, non lo è. Il numero di Péclet di cella U Δx/ν = 3,75 è superiore a 2, il valore oltre il quale le differenze centrate pure (Gauss linear) possono produrre oscillazioni: è il compromesso discusso in dettaglio nella Parte 5. Il prefisso bounded, usato nei calcoli stazionari, sottrae il termine (∇·φ)U in modo che la convezione discretizzata resti ben comportata mentre, durante le iterazioni, la continuità non è ancora soddisfatta.
06 foamSearch: scoprire gli schemi consolidati
Cerca le voci selezionate nella raccolta di tutorial installata. Lo script v14 usa foamDictionary per estrarre i valori, il che lo rende più utile della ricerca nel testo per molte domande che riguardano la struttura dei dizionari.
Il primo comando segue l’esempio ufficiale della User Guide v14:
foamSearch -c "$FOAM_TUTORIALS" fvSolution \
solvers/p/solver
foamSearch "$FOAM_TUTORIALS" fvSchemes \
"divSchemes/div(phi,U)"
Interpreta i risultati con attenzione
- Un’impostazione frequente non è automaticamente la migliore.
- I valori possono provenire da geometrie, qualità di mesh, modelli fisici e formulazioni temporali diversi.
- Il risultato della ricerca prova che una sintassi è usata; la documentazione spiega che cosa significa.
Esercizio
Trova due esempi di solutori per l’equazione della pressione. Registra i percorsi dei tutorial e stabilisci se usano formulazioni turbolente o temporali diverse. Spiega quali differenze impediscono un confronto semplice.
Teoria facoltativa: frequenza contro prova
Contare schemi o scelte di solutore tra i tutorial non è un confronto statistico dell’accuratezza numerica. La loro diffusione misura soltanto la frequenza d’uso in quella raccolta di tutorial.
Approfondimento: come funziona foamSearch e un metodo per l’esercizio
foamSearch (User Guide 4.7.3) riceve tre argomenti: una cartella, un nome di file e un percorso di voce. Trova ogni file con quel nome sotto la cartella, ne estrae la voce con foamDictionary e stampa i valori distinti trovati. Con -c conta anche quanti file contengono ciascun valore (nell’esempio della guida GAMG risulta il solutore di p più comune, seguito da PCG, smoothSolver e PBiCGStab). L’output dice quindi quali grafie esistono e quanto spesso, ma non dove. Per passare da un valore a un caso, combinalo con gli strumenti ordinari, poi interroga strutturalmente ciascun caso candidato:
# Which tutorials use each p solver, and with which time scheme?
foamSearch "$FOAM_TUTORIALS" fvSolution solvers/p/solver
grep -rl "GAMG" --include=fvSolution "$FOAM_TUTORIALS" | head -n 5
# For one candidate case directory <case>:
foamDictionary -entry ddtSchemes/default -value <case>/system/fvSchemes
foamDictionary -entry simulationType -value <case>/constant/momentumTransport
foamDictionary -entry solver -value <case>/system/controlDict
Per l’esercizio scegli due tutorial i cui solutori di p differiscono (per esempio GAMG e PCG/PBiCGStab) e compila una piccola tabella: percorso del tutorial; ddtSchemes/default (stazionario o transitorio); simulationType (laminar, RAS, LES); modulo solutore; dimensione della mesh (da checkMesh o dal blockMeshDict). Le differenze che impediscono un confronto semplice sono tipicamente: un’equazione della pressione transitoria viene risolta molte volte per passo temporale partendo da una buona stima iniziale, mentre una stazionaria viene risolta una volta per iterazione con un relTol largo; un caso turbolento aggiunge campi e cambia il condizionamento dell’equazione della quantità di moto; una mesh più grande favorisce il multigrid. La scelta del solutore risponde a tutti questi fattori insieme, quindi la sua frequenza non dice nulla sulla sua idoneità per il tuo caso.
07 foamInfo: interrogare un modello o una condizione
Quando l’incognita è un modello selezionabile, un modulo solutore o una condizione al contorno, usa foamInfo prima di cercare esempi qualsiasi in rete. In v14 l’utility può fornire descrizioni, intestazioni dei sorgenti, modelli correlati e casi di esempio.
Interrogazioni in sola lettura; adatta l’oggetto al compito:
foamInfo incompressibleFluid
foamInfo flowRateInletVelocity
foamInfo kOmegaSST
foamInfo linearRamp
Usare foamInfo correttamente
Per una condizione al contorno esamina sia le voci di dizionario richieste sia il tipo di campo a cui la condizione si applica. Una condizione per scalari non si può inserire indiscriminatamente in un campo vettoriale.
Scheda delle prove
| Voce | Osservazione dello studente |
|---|---|
| Oggetto selezionabile | per es. flowRateInletVelocity |
| Tipo/famiglia | Condizione al contorno, function object, modulo solutore… |
| Impostazioni richieste | Elenca ogni parola chiave obbligatoria |
| Esempi funzionanti | Almeno il percorso di un tutorial |
| Modifica proposta | File e patch di destinazione esatti |
Approfondimento: che cosa restituisce foamInfo e come leggerlo
foamInfo (User Guide 4.7.5) cerca nei sorgenti di OpenFOAM la classe che implementa il nome indicato, stampa le sezioni Description e Usage della sua intestazione – lo stesso testo che gli sviluppatori mantengono accanto al codice – ed elenca i tutorial in cui il nome compare. Le quattro interrogazioni qui sopra coprono quattro famiglie diverse: incompressibleFluid è un modulo solutore, flowRateInletVelocity una condizione al contorno, kOmegaSST un modello RAS di trasporto della quantità di moto, linearRamp una Function1 (una funzione del tempo o di un altro scalare, usata all’interno di molte condizioni). La guida documenta altri esempi come foamInfo kEpsilon, foamInfo Function1 e foamInfo triSurfaceMesh.
Come leggere la tabella Usage: la colonna “Required” separa le parole chiave che devono essere presenti da quelle che hanno un valore predefinito. Per una condizione al contorno è il nome stesso della classe a dire il tipo di campo: le condizioni …FvPatchVectorField si applicano a campi vettoriali come U, le …FvPatchScalarField a campi scalari come p o T, e le condizioni template …FvPatchField (come fixedValue, zeroGradient, uniformFixedValue) esistono per ogni tipo di campo. Una scheda compilata per la riga d’esempio:
| Voce | Osservazione |
|---|---|
| Oggetto selezionabile | flowRateInletVelocity |
| Tipo/famiglia | Condizione al contorno per volVectorField (solo vettori) |
| Impostazioni richieste | una tra volumetricFlowRate, massFlowRate, meanVelocity; facoltativi profile, rho, rhoInlet |
| Esempi funzionanti | il percorso di un tutorial dall’elenco di foamInfo |
| Modifica proposta | 0/U, boundaryField/inlet |
08 foamToC e foamFind
foamToC esamina le tabelle di selezione a runtime delle librerie compilate di OpenFOAM. foamFind individua file, compreso il codice sorgente. Sono strumenti complementari, non sostituti della User Guide.
Il nome della tabella nell’ultimo comando è un esempio tratto dalla documentazione v14:
foamToC -scalarBCs
foamToC -vectorBCs
foamToC -solvers
foamToC -search BirdCarreau
foamToC -table generalisedNewtonianViscosityModel
Ricerca nei sorgenti, non esecuzione di un caso:
foamFind wallHeatFlux.C
foamFind wallHeatFlux.C -search heat -numbers
foamFind -modules incompressibleFluid.C
Quando usare ciascuno
| Domanda | Strumento |
|---|---|
| Quali opzioni esistono in una categoria compilata? | foamToC |
| Dove è documentato e usato un modello? | foamInfo |
| Dove si trova il suo file di implementazione? | foamFind |
| Dove è usato un valore di dizionario? | foamSearch |
Approfondimento: la selezione a runtime, e l’errore che produce quando un nome è sbagliato
Quasi ogni parola chiave type, solver, model o viscosityModel di OpenFOAM seleziona una classe C++ a runtime in base al nome. Ogni famiglia registra i propri membri in una tabella di selezione a runtime quando la sua libreria viene caricata. foamToC (“table of contents”, User Guide 4.7.6) carica le librerie e stampa quelle tabelle: -scalarBCs e -vectorBCs elencano le condizioni al contorno per tipo di campo, -solvers i moduli solutori (equivale a -table solver), -functionObjects, -fvModels e -fvConstraints le famiglie corrispondenti, -tables i nomi di tutte le tabelle (è il comportamento predefinito senza opzioni), -table <nome> una tabella, e -search <nome> la tabella o le tabelle in cui un nome compare. -solver <modulo>, -libs e -noLibs controllano quali librerie vengono caricate, in modo che l’elenco corrisponda a ciò che un dato solutore vede davvero.
foamToC -vectorBCs | grep -i flowRate # is it a vector-field condition?
foamToC -scalarBCs | grep -i flowRate # (should not appear for scalars)
foamToC -tables | less # every run-time selection table
foamToC -solver incompressibleFluid -functionObjects
Il secondo comando è la versione meccanica dell’avvertenza della sezione 07: se una condizione compare sotto -vectorBCs ma non sotto -scalarBCs, non si può usare per p. La stessa tabella è ciò che il solutore stampa quando un nome è scritto male o appartiene a una libreria non caricata – lo schema del messaggio d’errore è riconoscibile:
--> FOAM FATAL IO ERROR:
Unknown patchField type flowRateInletVelocty for patch inlet of field U
Valid patchField types are :
... (the list of every vector condition that is loaded)
Come leggerlo: la prima riga riporta il nome sbagliato, la patch e il campo; l’elenco che segue è la tabella di selezione. Cercaci il nome voluto; se manca, la libreria che lo fornisce non è caricata oppure l’oggetto non esiste in v14. foamToC -search BirdCarreau risponde alla domanda “in quale tabella si trova questo nome?” (qui la tabella dei modelli di viscosità newtoniani generalizzati, ed è per questo che il comando successivo del listato nomina generalisedNewtonianViscosityModel).
Approfondimento: le opzioni di foamFind per raccogliere prove
foamFind (User Guide 4.7.8) individua un file nell’albero dei sorgenti di OpenFOAM (per impostazione predefinita in $FOAM_SRC) e può stamparlo (-print), cercarvi una parola (-search, senza distinzione tra maiuscole e minuscole -isearch), mostrare i numeri di riga (-numbers), aprirlo in un editor (-edit) o restituire i file di compilazione associati (-files per Make/files, -options per Make/options). La radice di ricerca si cambia con -applications, -modules, -tutorial o -dir: foamFind -modules incompressibleFluid.C cerca solo nei sorgenti dei moduli solutori. Usi tipici in questo corso: confermare la parola chiave esatta che una classe legge (cercando nel file .C lookup o la parola chiave stessa) e leggere il blocco Description/Usage di un’intestazione quando l’output di foamInfo non basta. Nessuno di questi comandi tocca il tuo caso.
09 Altre utility v14 utili
Usa questi strumenti in modo selettivo. Sono aiuti alla gestione dei casi, non sostituti della comprensione dei file.
| Utility | Uso tipico |
|---|---|
foamGet | Copiare nel caso corrente configurazioni di esempio già pronte |
foamUnits | Esaminare le dimensioni fisiche con nome e i fattori delle unità |
foamListTimes | Elencare le cartelle temporali disponibili |
foamCloneCase | Creare un nuovo caso da un altro senza copia manuale |
foamCleanCase | Rimuovere i dati generati del caso; da usare con cautela |
checkMesh | Esaminare topologia e geometria della mesh |
Esempi in sola lettura, salvo che checkMesh può scrivere un log se l’output viene rediretto:
foamUnits power
foamUnits mm
foamListTimes
checkMesh
Attenzione – operazioni distruttive
Non eseguire foamCleanCase o foamListTimes -rm sul caso principale/di ricerca. Questi strumenti rimuovono contenuti generati. Fai prima una copia usa-e-getta verificata.
Il comando foamGet può installare una configurazione nota di function object come patchFlowRate (vedi la sezione ufficiale 4.7.4), ma il file copiato va comunque rivisto prima dell’uso.
Approfondimento: che cosa fa esattamente ciascuna utility, e che cosa cancella
Dettagli dalla User Guide 4.7.1, 4.7.4, 4.7.7 e 4.7.9 che contano nella pratica:
foamListTimeselenca le cartelle temporali escludendo0per impostazione predefinita; con-rmcancella le cartelle elencate. Sul progetto finale dovrebbe stampare 50, 100, 150, 200, 250, 300.foamCleanCaseriporta un caso allo stato originale: rimuove le cartelle temporali diverse da0, l’output di post-processing (postProcessing,VTK), le cartelleprocessor*, il codice dinamico econstant/polyMesh. Dopo di esso bisogna rieseguireblockMesh. In un caso la cui mesh viene da un convertitore esterno, quella mesh è persa.foamCloneCase vecchioCaso nuovoCasocrea un nuovo caso dalle cartelle0,systemeconstantdel vecchio (con-processorcopia anche le cartelle processor): il modo giusto per fare la “copia usa-e-getta” che questa unità continua a chiedere.foamGet patchFlowRatecopia insystem/un file di configurazione già pronto;foamGet functionscopia un filefunctionsmodello. La User Guide v14 (7.2) consiglia di elencare i function object in un filefunctionsnella cartellasystemdel caso, con#includeFunc;foamPostProcess -listelenca quelli già pronti.foamUnitssenza argomenti elenca tutte le dimensioni e le unità con nome;foamUnits powermostra la dimensione della potenza (kg m² s⁻³, cioè watt);foamUnits mmmostra il fattore 0,001 rispetto ai metri cheunits [mm];usa nella Parte 2. Converte anche:foamUnits cal "lbf*m".checkMeshnon modifica la mesh, ma quando trova celle o facce che non superano i controlli le scrive come insiemi (set) inconstant/polyMesh/sets/per l’ispezione;-allGeometrye-allTopologyattivano controlli aggiuntivi.- Sempre in 4.7:
foamMergeCaseefoamUnMergeCasesovrappongono a un caso base i file di una variante e annullano la sovrapposizione – utile quando si mantengono più varianti dello stesso caso.
foamListTimes # 50 100 150 200 250 300 (0 is excluded by default)
foamListTimes -rm # DESTRUCTIVE: deletes the listed time directories
foamCloneCase step08_channel step08_channel_ex2 # copies 0, system, constant
foamGet patchFlowRate # copies a function-object file into system/
checkMesh -allGeometry -allTopology
10 Confermare la famiglia di OpenFOAM installata
Il corso Foundation 14 segue l’architettura a solutori modulari. Materiale scritto per un’altra famiglia di OpenFOAM, o per versioni molto più vecchie, può contenere nomi di file, nomi di moduli o parole chiave incompatibili.
Da eseguire in una shell in cui OpenFOAM 14 è già caricato:
foamVersion
echo "$WM_PROJECT_DIR"
echo "$FOAM_TUTORIALS"
command -v foamRun
foamInfo incompressibleFluid
Un calcolo modulare v14
Solo un estratto: un file completo ha bisogno anche della sua intestazione.
// system/controlDict (excerpt)
solver incompressibleFluid;
startFrom startTime;
startTime 0;
stopAt endTime;
endTime 300;
deltaT 1;
writeControl timeStep;
writeInterval 50;
La simulazione si esegue con foamRun, che carica il modulo solutore indicato in controlDict. Non dare per scontato che il nome di un vecchio eseguibile o una vecchia voce application siano intercambiabili.
Approfondimento: i segnali della famiglia giusta, e una tabella di traduzione per il materiale vecchio
Risultati attesi dei cinque comandi su un’installazione corretta: foamVersion riporta OpenFOAM-14; $WM_PROJECT_DIR punta a una cartella OpenFOAM-14; $FOAM_TUTORIALS alla sua sottocartella tutorials; command -v foamRun stampa il percorso dell’eseguibile (una risposta vuota significa che l’ambiente non è caricato); foamInfo incompressibleFluid stampa la descrizione del modulo. Se uno di questi fallisce, fermati: nulla di ciò che segue sarà affidabile.
Nell’architettura modulare un unico eseguibile, foamRun, legge solver da controlDict e carica la libreria del modulo corrispondente (l’opzione -solver di foamRun può sovrascriverlo). Il materiale scritto per versioni Foundation più vecchie o per la famiglia openfoam.com usa nomi diversi. Le traduzioni più frequenti:
// Older / other-family input that will NOT work as-is in Foundation v14
application simpleFoam; // v14: solver incompressibleFluid; + foamRun
// constant/transportProperties // v14: constant/physicalProperties
// constant/turbulenceProperties // v14: constant/momentumTransport
// postProcess -func ... // v14: foamPostProcess -func ...
Nella linea Foundation momentumTransport ha sostituito turbulenceProperties intorno alla v8, physicalProperties ha sostituito transportProperties intorno alla v10, e i moduli solutori con foamRun e foamPostProcess sono arrivati con la v11. La v14 accetta anche dimensioni con nome come [velocity] e [kinematicPressure] nei file di campo, equivalenti ai vettori numerici usati in questa unità. Se un esempio trovato in rete usa uno dei vecchi nomi, trattalo come un indizio sui concetti, mai come testo da incollare.
11 Costruire un caso da una cartella vuota
Lo studente dovrebbe essere in grado di partire da una cartella di lavoro vuota. Questo non vieta di consultare i file dei tutorial per un singolo schema documentato.
Adatta, se serve, il percorso della cartella padre alle regole del tuo account:
mkdir -p "$HOME/OpenFOAM/$USER-14/step08_channel"
cd "$HOME/OpenFOAM/$USER-14/step08_channel"
mkdir -p 0 constant system
find . -maxdepth 2 -type d -print
Crea i file in ordine di dipendenza
- Mesh: definisci la geometria, la risoluzione del blocco e le patch di contorno.
- Campi: assicurati che
Uepcorrispondano a ogni patch della mesh. - Fisica: definisci la viscosità e il modello di trasporto della quantità di moto.
- Numerica: fornisci
fvSchemesefvSolutioncoerenti con le equazioni. - Esecuzione: fornisci
controlDicted esegui per gradi.
Tappe di verifica
Dopo aver scritto blockMeshDict, esegui blockMesh e checkMesh prima di aggiungere complessità. Dopo aver creato gli altri dizionari, esamina le voci chiave con foamDictionary prima di provare foamRun.
Approfondimento: l’albero atteso e l’inventario completo dei file
Il comando find finale dovrebbe stampare esattamente quattro righe – la cartella del caso e le sue tre sottocartelle vuote:
.
./0
./constant
./system
Alla fine del progetto il caso contiene nove file modificabili più la mesh generata. L’inventario qui sotto è anche una lista di controllo: una riga per file, con la classe o la voce chiave che lo identifica.
step08_channel/
├── 0/
│ ├── U volVectorField, [0 1 -1 0 0 0 0]
│ └── p volScalarField, [0 2 -2 0 0 0 0]
├── constant/
│ ├── physicalProperties viscosityModel constant; nu
│ ├── momentumTransport simulationType laminar;
│ └── polyMesh/ generated by blockMesh, never edited by hand
└── system/
├── blockMeshDict
├── controlDict solver incompressibleFluid;
├── fvSchemes
└── fvSolution
Perché l’ordine conta. blockMesh ha bisogno che system/controlDict esista (ogni applicazione OpenFOAM lo legge all’avvio), quindi in pratica i primi due file scritti sono blockMeshDict e un controlDict minimo; la voce solver si può completare dopo. I campi dipendono dai nomi delle patch, che vengono dalla mesh. Gli schemi dipendono dai termini delle equazioni, che dipendono dalla fisica (un modello laminare non ha div(phi,k)). Scrivere in quest’ordine significa che ogni file può essere controllato rispetto a qualcosa che esiste già. Ogni file ha bisogno di un’intestazione FoamFile con almeno format, class e object (Parte 1); version e location sono facoltativi.
12 Diagnosticare gli errori in modo sistematico
Un calcolo fallito è una fonte di informazioni. Leggi il primo errore significativo e stabilisci se si tratta di un problema di lettura (parsing), di configurazione, di modello, di mesh o numerico.
Flusso di lavoro illustrativo; fermati ed esamina una fase fallita prima di proseguire:
blockMesh > log.blockMesh 2>&1
checkMesh > log.checkMesh 2>&1
foamRun > log.foamRun 2>&1
tail -n 70 log.foamRun
Una lista di sei domande per gli errori
- Quale eseguibile o modulo è fallito?
- Quale dizionario, file, regione o campo viene nominato?
- Quale parola chiave o tipo di dato era atteso?
- La mesh contiene la patch nominata?
- La classe del campo o l’insieme delle dimensioni sono compatibili?
- Il metodo numerico o il modello richiesto da questo calcolo è configurato?
Distinzione importante
Il fatto che un dizionario venga letto senza errori prova soltanto che l’input è sintatticamente ammissibile. Non stabilisce la qualità della mesh, la convergenza numerica né la validità scientifica.
Approfondimento: anatomia di un FOAM FATAL IO ERROR e come le sei domande vi si applicano
> log.x 2>&1 invia al log sia lo standard output sia lo standard error, così il messaggio d’errore – scritto sullo standard error – non va perso. Un errore fatale di input di OpenFOAM ha una struttura fissa (lo schema qui sotto è indicativo):
--> FOAM FATAL IO ERROR:
<what was expected and what was found>
file: <case>/system/fvSchemes/divSchemes at line <n>.
From function ...
in file ... at line ...
FOAM exiting
La riga del messaggio risponde alla domanda 3 (che cosa era atteso); la riga file: risponde alla domanda 2, e il suo percorso prosegue dentro il file fino al sotto-dizionario (.../fvSchemes/divSchemes), con il numero di riga; la riga From function nomina la funzione C++, che spesso rivela la famiglia coinvolta (il costruttore di una condizione al contorno, un selettore di schemi, un modello). La domanda 1 si ricava da quale log contiene l’errore. Le altre tre domande sono controlli che fai tu: checkMesh o il file boundary per la 4, la classe in FoamFile e la riga dimensions per la 5, il dizionario pertinente per la 6.
Un semplice FOAM FATAL ERROR (senza “IO”) riguarda di solito uno stato e non un file – per esempio un campo mancante o una dimensione incoerente in un’operazione. Gli avvisi (--> FOAM Warning) non fermano il calcolo ma spesso spiegano un fallimento successivo. Comandi utili:
set -o pipefail
foamRun 2>&1 | tee log.foamRun # watch and keep the log
grep -n "FOAM FATAL" log.* # first fatal message, with line number
grep -n -i "warning" log.foamRun | head # warnings printed before the failure
set -o pipefail fa sì che lo stato d’uscita di una pipeline sia quello del programma che fallisce e non quello di tee, così uno script si ferma alla fase giusta. Il “primo errore significativo” è il primo messaggio fatale, non le ultime righe di una lunga cascata di conseguenze.
13 Errori comuni: sintassi e struttura
| Sintomo | Prima azione diagnostica |
|---|---|
FOAM FATAL IO ERROR su una parola chiave | Apri il file nominato ed esamina la voce segnalata |
| Token inatteso / errore di lettura | Controlla graffe, parentesi e punti e virgola |
| Chiave di dizionario sconosciuta | Controlla grafia, versione e contesto del modello |
| File nominato mancante | Verifica la cartella del caso e il modulo selezionato |
Impossibile leggere una voce di boundaryField | Confronta con constant/polyMesh/boundary |
| Formato di lista o di vettore errato | Confronta classe del campo e tipo di dato atteso |
Esercizio di debug deliberato A
Una voce di contorno volutamente malformata: individua il delimitatore mancante.
inlet
{
type fixedValue;
value uniform (0.01 0 0)
}
Non iniziare a modificare le tolleranze numeriche per curare questo errore. È un problema di sintassi e va corretto sul posto.
Approfondimento: la soluzione dell’esercizio A e come la vede il parser
Il delimitatore mancante è il punto e virgola dopo value uniform (0.01 0 0). Nel linguaggio dei dizionari di OpenFOAM (Parte 1) una voce primitiva è una parola chiave seguita da token fino a un ;. Senza di esso il parser continua a leggere i token di value e arriva alla graffa di chiusura di inlet mentre la voce è ancora aperta; l’errore viene segnalato come voce malformata (mal definita) che inizia alla parola chiave value, con l’intervallo di righe che il parser ha tentato di leggere. Il numero di riga del messaggio è quindi spesso quello successivo al vero difetto: cerca all’indietro a partire da lì. La correzione è un solo carattere:
inlet
{
type fixedValue;
value uniform (0.01 0 0);
}
Altri difetti di sintassi e le loro firme: una graffa non bilanciata produce di solito un errore di fine file o inghiotte il resto del file (visibile subito con foamDictionary <file>, che stampa la struttura così come è stata capita); un vettore scritto senza parentesi, o con le virgole, produce un errore di token sbagliato quando il campo viene letto; uniform 0.01 in un campo vettoriale fallisce perché uno scalare non è un vettore. In tutti questi casi foamDictionary sul singolo file riproduce l’errore in una frazione di secondo, senza eseguire il solutore.
14 Errori comuni: fisica e campi
Coerenza delle patch
Il file boundary della mesh contiene nomi e tipi geometrici. Ogni campo richiesto deve fornire voci boundaryField compatibili per questi nomi. Una stringa che coincide è necessaria ma non sufficiente.
Dimensioni
Un solutore incomprimibile di OpenFOAM può usare la pressione cinematica, con dimensioni di una lunghezza al quadrato divisa per un tempo al quadrato, invece della pressione in pascal. Leggi la documentazione del solutore scelto invece di presumere che tutti i campi chiamati p rappresentino la stessa grandezza.
// Vettore delle dimensioni tipico della pressione cinematica
dimensions [0 2 -2 0 0 0 0];
// Vettore delle dimensioni della velocità
dimensions [0 1 -1 0 0 0 0];
Approfondimento facoltativo: analisi dimensionale
La variabile di pressione usata nei flussi incomprimibili a densità costante è spesso la pressione divisa per la densità. L’interpretazione fisica va fatta in base alla formulazione effettiva del solutore.
Campi del modello
Selezionare un modello RANS o LES può introdurre campi e condizioni al contorno aggiuntivi. Se il modello attivo è laminare, copiare da un altro tutorial i campi del trasporto turbolento aggiunge rumore e può mascherare errori concettuali.
Approfondimento: pressione cinematica, insiemi di dimensioni e che cosa succede con i Pa
Per densità ρ costante, l’equazione della quantità di moto incomprimibile si divide per ρ: ∂U/∂t + ∇·(UU) = −∇(p/ρ) + ∇·(ν∇U) + … La variabile risolta da incompressibleFluid è quindi pk = p/ρ, in m²/s². Il vettore delle dimensioni elenca gli esponenti delle sette dimensioni fondamentali nell’ordine fisso [massa lunghezza tempo temperatura moli corrente intensità luminosa] (kg, m, s, K, mol, A, cd): [0 2 -2 0 0 0 0] è m² s⁻², [0 1 -1 0 0 0 0] è m s⁻¹, mentre una pressione in pascal sarebbe [1 -1 -2 0 0 0 0]. In v14 si può scrivere lo stesso con le dimensioni con nome, [kinematicPressure] e [velocity] (il pitzDailySteady ufficiale usa dimensions [kinematicPressure];).
Se 0/p dichiara pascal, OpenFOAM controlla le dimensioni quando combina i campi: il calcolo si ferma con un errore fatale di dimensioni incoerenti in un’operazione, che nomina entrambi gli insiemi di dimensioni. Per riportare i risultati in pascal moltiplica per la densità del fluido che intendi: per una caduta di pressione cinematica del progetto finale di circa 9 × 10⁻⁴ m²/s² (vedi la sezione 28), aria con ρ ≈ 1,2 kg/m³ darebbe circa 1,1 × 10⁻³ Pa. La viscosità del progetto, 1,0 × 10⁻⁵ m²/s, è un valore illustrativo, vicino a quello dell’aria (≈1,5 × 10⁻⁵) e dieci volte quello dell’acqua (≈1,0 × 10⁻⁶); il caso è definito dal suo numero di Reynolds, non da un fluido specifico.
Campi del modello: con simulationType laminar; il solutore legge solo U e p. Un modello RAS kEpsilon aggiunge k, epsilon e nut (con funzioni di parete sulle pareti); kOmegaSST aggiunge k, omega e nut. I file in più lasciati in 0/ da un caso laminare semplicemente non vengono letti, ed è proprio per questo che sono pericolosi: suggeriscono un modello di turbolenza che non è attivo.
15 Errori comuni: comportamento numerico
Configurazione accettata ≠ soluzione convergente. Quando il solutore gira ma non converge, separa le prove in mesh, condizioni al contorno, scelta del modello e impostazioni numeriche.
| Osservazione | Possibile indagine |
|---|---|
| I residui oscillano o crescono | Schema temporale, rilassamento, controlli del solutore, condizioni al contorno |
| Errore di continuità molto alto | Coerenza dei flussi, qualità della mesh, condizioni al contorno |
| Forte sensibilità alla dimensione delle celle | Risoluzione e discretizzazione spaziale |
| Ricircolo inatteso | Geometria, scelta di ingresso/uscita e regime fisico |
| Avvisi del controllo della mesh | Qualità delle celle e geometria del contorno |
Protocollo di intervento controllato
Istantanea → ipotesi → singola modifica → nuova esecuzione → confronto. Mantieni la stessa geometria e le stesse ipotesi fisiche quando indaghi un parametro numerico. Se cambiano la geometria o la fisica, etichettalo come un nuovo esperimento.
Approfondimento facoltativo: convergenza non è validazione
Un insieme di residui bassi è un indicatore numerico utile; non prova che il modello rappresenti l’esperimento. La validazione richiede osservabili adatte e un confronto indipendente.
Approfondimento: leggere residui ed errori di continuità nel log
Ogni iterazione del progetto stazionario stampa un blocco come quello qui sotto (numeri illustrativi). In 2D con facce anteriore e posteriore empty si risolvono solo Ux e Uy: la direzione vuota non ha equazione.
Time = 300
smoothSolver: Solving for Ux, Initial residual = 1.2e-06, Final residual = 9.8e-08, No Iterations 2
smoothSolver: Solving for Uy, Initial residual = 3.4e-06, Final residual = 2.9e-07, No Iterations 2
GAMG: Solving for p, Initial residual = 4.1e-06, Final residual = 1.9e-07, No Iterations 3
time step continuity errors : sum local = 2.1e-10, global = -3.5e-12, cumulative = 1.7e-09
ExecutionTime = 1.9 s ClockTime = 2 s
- Initial residual: lo squilibrio normalizzato del sistema lineare all’inizio della soluzione, cioè quanto il campo attuale è lontano dal soddisfare l’equazione discretizzata. Il suo andamento tra un’iterazione e l’altra è l’indicatore di convergenza di un calcolo stazionario.
- Final residual e No Iterations: fin dove è arrivato questa volta il solutore lineare, governato da
toleranceerelTol(Parte 6). Un residuo finale che non raggiunge mai la tolleranza è normale conrelTol> 0. - time step continuity errors: la media pesata sul volume di |∇·φ| (moltiplicata per il passo temporale) dopo la correzione di pressione (“sum local”), la sua somma con segno (“global”) e il totale progressivo (“cumulative”). Valori piccoli e non crescenti (≪ 10⁻⁶ in questo caso) significano che i flussi conservano la massa.
foamLog log.foamRun # extracts residual histories into ./logs/
ls logs/ # Ux_0, Uy_0, p_0, contCumulative_0, ...
foamLog estrae dal log ogni storia dei residui in file separati che si possono tracciare in un grafico; in alternativa un function object residuals può scriverli durante il calcolo. Un grafico dei residui che scende di parecchi ordini di grandezza e poi si appiattisce è il comportamento stazionario tipico; un plateau con oscillazioni richiede la colonna di sinistra della tabella qui sopra.
Approfondimento: verifica contro validazione, con i numeri del progetto finale
Dietro “il risultato è giusto?” si nascondono due domande diverse. La verifica chiede se le equazioni sono risolte correttamente: convergenza iterativa (residui, continuità) ed errore di discretizzazione, misurato raffinando la mesh come nel caso svolto 2 (convergenza di mesh). La validazione chiede se le equazioni sono quelle giuste per la realtà, e richiede dati indipendenti – un esperimento, o una soluzione esatta per un caso idealizzato.
Il progetto finale ha una soluzione esatta lontano dall’ingresso: il flusso di Poiseuille piano. Con velocità media U = 0,01 m/s e altezza H = 0,020 m il profilo sviluppato è u(y) = 6U (y/H)(1 − y/H), con massimo umax = 1,5U = 0,015 m/s a metà altezza, e il gradiente di pressione cinematica è dp/dx = −12νU/H² = −3,0 × 10⁻³ m/s². Confrontare il profilo calcolato, per esempio a x = 0,25 m, con questi due numeri è una verifica rispetto a una soluzione analitica; la procedura completa è nel caso svolto 1 (canale laminare, validazione di Poiseuille). I soli residui bassi non rileverebbero, per esempio, una viscosità inserita come 1e-4 invece di 1e-5: il calcolo convergerebbe perfettamente al flusso sbagliato.
16 Esercizio 1 – esaminare un caso
Esercizio in sola lettura · 25–35 min.
Scegli un tutorial ufficiale di flusso incomprimibile di OpenFOAM 14 (nell’albero v14 si trovano sotto $FOAM_TUTORIALS/incompressibleFluid) e rispondi alle domande qui sotto senza modificare alcun file.
- Qual è la voce
solverincontrolDict? - Dove sono definiti
Uep? Hanno le dimensioni fisiche corrette? - Quali patch esistono e come sono rappresentate nei file dei campi?
- Il calcolo è stazionario o transitorio? Mostra la prova in
fvSchemes. - Quale solutore è configurato per
p? - Quale modello di
momentumTransportè selezionato? - Quale comando esegue il modulo solutore?
Esempio di come sostenere una risposta con una prova diretta:
foamDictionary -entry solver -value system/controlDict
foamDictionary -entry ddtSchemes system/fvSchemes
foamDictionary -entry solvers/p system/fvSolution
Elaborato da consegnare
Mappa della configurazione di una pagina: file → voce attiva → ruolo fisico o numerico → prova. Indica dove hai trovato ogni risposta (percorso del file e, se pertinente, sezione della User Guide).
Approfondimento: una mappa di configurazione modello, con le risposte per pitzDailySteady
Lavora su una copia (cp -r "$FOAM_TUTORIALS/incompressibleFluid/pitzDailySteady" .) anche se l’esercizio è in sola lettura: elimina ogni rischio di toccare l’installazione. Per questo tutorial la User Guide §2.1 (backward-facing step) e i file del tutorial danno le risposte che dovresti trovare:
| File | Voce attiva | Ruolo | Prova |
|---|---|---|---|
system/controlDict | solver incompressibleFluid; | Seleziona il modulo solutore caricato da foamRun | foamDictionary -entry solver -value; UG 2.1, 3.5 |
0/U, 0/p | velocità in m/s; [kinematicPressure] | Incognite primarie; p è p/ρ | classe nell’intestazione, dimensions; UG 2.1 |
boundaryField di 0/* | inlet, outlet, upperWall, lowerWall, frontAndBack | Stessi nomi della mesh; frontAndBack è empty (2D) | foamDictionary -entry boundaryField -keywords 0/p |
system/fvSchemes | ddtSchemes/default steadyState; | Calcolo stazionario | foamDictionary -entry ddtSchemes; UG 4.5 |
system/fvSolution | solvers/p/solver GAMG;, SIMPLE { consistent yes; } | Solutore lineare della pressione; accoppiamento SIMPLEC | foamDictionary -entry solvers/p; UG 4.6 |
constant/momentumTransport | simulationType RAS;, modello kEpsilon | Modello di turbolenza (aggiunge k, epsilon, nut) | UG 2.1, 8.2 |
| shell | foamRun | Esegue il modulo indicato in controlDict | UG 2.1 |
La mappa è l’elaborato in miniatura: chiunque può controllare ogni riga in pochi secondi, perché dice da dove viene la risposta.
17 Esercizio 2 – modifica controllata
Modificare una sola variabile · 30–45 min.
In una copia usa-e-getta di un caso laminare funzionante, modifica una sola impostazione di controllo della scrittura. Prevedi l’effetto sul numero di cartelle temporali scritte prima di eseguire.
cp system/controlDict system/controlDict.before-ex08
foamDictionary -entry writeInterval -value \
system/controlDict
# Scegli un intervallo di scrittura adatto al caso.
foamDictionary -entry writeInterval -set 20 \
system/controlDict
foamDictionary -entry writeInterval -value \
system/controlDict
Relazione dello studente
- Valore precedente e nuovo valore.
- Perché la modifica va fatta in
controlDict. - Sequenza di scrittura prevista.
- Cartelle osservate con
foamListTimesdopo il calcolo. - Eventuali differenze tra previsione e osservazione.
Vincolo
Non cambiare contemporaneamente startTime, endTime e writeControl. L’esercizio è pensato per isolare una sola causa.
Approfondimento: prevedere la sequenza di scrittura, svolto
Con writeControl timeStep; viene scritta una cartella temporale ogni volta che l’indice di iterazione è un multiplo di writeInterval. Sulla configurazione del progetto finale (deltaT 1, endTime 300):
| writeInterval | Cartelle previste (oltre a 0) | Numero |
|---|---|---|
| 50 (riferimento) | 50 100 150 200 250 300 | 6 |
| 20 (esercizio) | 20 40 60 … 280 300 | 15 |
| 40 | 40 80 … 280 – 300 non è multiplo di 40 | 7: l’iterazione 300 non è un istante di scrittura |
La terza riga è il tipo di differenza tra previsione e osservazione che la relazione chiede: non dare per scontato che l’ultima iterazione venga sempre scritta – controlla con foamListTimes. Altri valori di writeControl (Parte 4) cambiano il significato dell’intervallo: runTime e adjustableRunTime lo interpretano come tempo simulato, cpuTime e clockTime come secondi di calcolo; purgeWrite N conserva solo le ultime N cartelle. Poiché è impostato runTimeModifiable true;, controlDict viene riletto durante il calcolo, quindi una modifica fatta mentre il solutore gira può avere effetto a metà calcolo – un altro motivo per modificare solo copie usa-e-getta. Se in fvSolution fosse presente residualControl, il calcolo potrebbe fermarsi prima di 300 e cambiare la sequenza: è una seconda causa, e il vincolo qui sopra esiste proprio per tenere separate le cause.
18 Esercizio 3 – condizione al contorno sconosciuta
Ricerca autonoma nella documentazione · 45–60 min.
Il compito è sostituire la specifica d’ingresso con una condizione di velocità diversa e documentata, adatta al problema dimostrativo scelto. Non viene fornito alcun dizionario boundaryField completo.
Ricerca richiesta
- Individua la condizione proposta con
foamInfoo con la User Guide. - Conferma che sia adatta a un
volVectorField. - Trova un tutorial ufficiale che ne mostri le voci richieste.
- Decidi se la condizione ha senso per questa geometria.
- Modifica il caso copiato e controlla messaggi d’errore e risultati.
Prove da consegnare
| Prova | Contenuto richiesto |
|---|---|
| Specifica | Che cosa viene imposto fisicamente? |
| Fonte | Link alla guida o intestazione del sorgente + percorso del tutorial |
| Implementazione | Voce della patch in 0/U e parametri richiesti |
| Validazione | Letto/eseguibile, più un controllo fisico |
| Discussione | In che cosa differisce da fixedValue? |
Approfondimento: un percorso di ricerca per l’esercizio (prova prima da solo)
Elenco dei candidati: foamToC -vectorBCs mostra ogni condizione vettoriale; quelle con “inlet” o “velocity” nel nome sono i candidati. Tre scelte sensate per l’ingresso di un canale, ciascuna con un’affermazione fisica diversa:
| Condizione | Che cosa impone | Ha senso qui? |
|---|---|---|
flowRateInletVelocity | Una portata (o una velocità media), velocità normale alla patch, profilo facoltativo | Sì: il canale è definito dalla sua portata |
uniformFixedValue | Un vettore dato da una Function1 del tempo (uniformValue), per es. una linearRamp | Solo come rampa di avvio; in un calcolo stazionario il tempo è un contatore di iterazioni |
pressureInletOutletVelocity | Velocità ricavata dal flusso, per ingressi comandati dalla pressione | Solo se si impone invece la pressione d’ingresso – un problema diverso |
Controllo fisico per il primo candidato: la portata attraverso l’ingresso deve essere uguale al valore imposto, Q = 2 × 10⁻⁷ m³/s, e lo stesso attraverso l’uscita (con segno opposto, perché i flussi di faccia sono positivi verso l’esterno). Il controllo è un solo comando (lo mostra la sezione 28). Discussione rispetto a fixedValue: fixedValue impone un vettore su ogni faccia, compresa la sua direzione, e la portata ne consegue; flowRateInletVelocity impone la portata e ne ricava la velocità, sempre diretta verso l’interno lungo la normale alla faccia, quindi resta corretta se l’area o l’orientamento della patch cambiano. Senza profile entrambe danno, in questa geometria, una velocità d’ingresso uniforme, quindi i campi calcolati dovrebbero essere praticamente identici – il che è di per sé una validazione utile. Lettura ulteriore interessante: caso svolto 6 (sensibilità alle condizioni d’ingresso).
19 Progetto finale: costruire un caso in autonomia
Progetto finale · enunciato del problema.
Costruisci da una cartella vuota un caso di flusso in canale stazionario, laminare, incomprimibile e isotermo. Si possono consultare OpenFOAM 14 e la sua documentazione ufficiale. Non aprire le sezioni di riferimento (22–27 più sotto) finché non hai completato una prima implementazione.
| Requisito fisico | Valore |
|---|---|
| Dominio | Canale 2D rappresentato da un sottile blocco 3D |
| Lunghezza (x) | 0,30 m |
| Altezza (y) | 0,020 m |
| Profondità di estrusione (z) | 0,001 m, spessa una cella |
| Celle nominali | 80 × 16 × 1 |
| Velocità in ingresso | U = (0,01; 0; 0) m/s |
| Uscita | Riferimento di pressione cinematica p = 0 |
| Pareti | Aderenza (no-slip) sui contorni superiore e inferiore |
| Fronte / retro | empty (ipotesi bidimensionale) |
| Viscosità cinematica | nu = 1,0e-5 m²/s |
| Modulo | incompressibleFluid (Foundation 14) |
Il fluido è scelto per ottenere una dimostrazione a numero di Reynolds moderato: usa l’altezza del canale come lunghezza caratteristica dichiarata e calcola esplicitamente il numero di Reynolds.

Approfondimento: scrivi queste previsioni prima di iniziare
Come nella Parte 2, le previsioni indipendenti scritte in anticipo sono il rivelatore di errori più economico che esista. Tutti i valori discendono dalla tabella:
| Grandezza | Calcolo | Previsione |
|---|---|---|
| Dimensione cella in x | 0,30 m / 80 | 3,75 mm |
| Dimensione cella in y | 0,020 m / 16 | 1,25 mm |
| Dimensione cella in z | 0,001 m / 1 | 1,0 mm |
| Celle | 80 × 16 × 1 | 1.280 |
| Punti | 81 × 17 × 2 | 2.754 |
| Facce interne | 79 × 16 + 80 × 15 | 2.464 |
| Facce di contorno | inlet 16 + outlet 16 + walls 2 × 80 + frontAndBack 2 × 1.280 | 2.752 |
| Facce totali | 2.464 + 2.752 | 5.216 |
| Riquadro di ingombro | dai vertici | (0 0 0) (0.3 0.02 0.001) |
| Portata | U × H × profondità | 2 × 10⁻⁷ m³/s |
| Numero di Reynolds | U H / ν | 20 (sezione 28) |
checkMesh dovrebbe riportare anche due direzioni geometriche e due direzioni di soluzione, (1 1 0): la direzione z è eliminata dalle patch empty. Qualsiasi discrepanza tra queste previsioni e i log indica un file preciso.
20 Progetto finale: implementazione autonoma
Sequenza di costruzione
Fase A: geometria. Definisci otto vertici, un blocco hex, un’estrusione di una cella, quattro gruppi di patch fisiche con nome e le facce anteriore/posteriore empty. Esegui blockMesh e checkMesh.
Fase B: campi iniziali. Crea 0/U e 0/p con intestazioni, dimensioni e valori interni corretti e voci boundaryField complete.
Fase C: modello fisico. Seleziona in constant/ il modello a viscosità costante e il trasporto della quantità di moto laminare.
Fase D: modello numerico. Scegli una discretizzazione temporale stazionaria, uno schema per la convezione della quantità di moto, uno schema per la diffusione e i solutori dei campi. Spiega ciascuna scelta.
Fase E: esecuzione. Configura system/controlDict per un limite di 300 iterazioni e una scrittura ogni 50 iterazioni. Esegui foamRun.
Consegna attesa
- Una cartella del caso che contenga tutti i file di input modificabili necessari.
- Un breve README che descriva il ruolo di ciascun file.
- Una copia dell’output di
checkMeshe un log del solutore. - Una registrazione delle decisioni di configurazione con le prove e il primo errore significativo incontrato.
- Una breve interpretazione di U, p e del bilancio di massa.
Limite della validazione
Questo documento fornisce una configurazione di riferimento, ma l’esecuzione effettiva del solutore va verificata sulla tua installazione di OpenFOAM 14. Una pagina non può sostituire l’esecuzione del caso.
Approfondimento: modelli per il README e per la registrazione delle decisioni di configurazione
Una nota sulla Fase A: la soluzione di riferimento (sezione 23) ha quattro patch con nome – inlet, outlet, walls (entrambe le pareti in una patch) e frontAndBack (entrambe le facce vuote). “Quattro gruppi di patch fisiche con nome” va letto in questo senso; dividere le pareti in upperWall e lowerWall, come fa pitzDailySteady, è ugualmente valido purché i file dei campi si adeguino. A un README basta una riga per file e niente di più:
step08_channel - README
Purpose : steady laminar 2D channel, incompressibleFluid, OpenFOAM Foundation 14
0/U : velocity; inlet fixedValue (0.01 0 0) m/s; walls noSlip; outlet zeroGradient
0/p : kinematic pressure [m^2/s^2]; outlet fixedValue 0 (reference)
constant/physicalProperties : viscosityModel constant; nu 1e-05 m^2/s
constant/momentumTransport : simulationType laminar
system/blockMeshDict : 0.30 x 0.020 x 0.001 m, 80 x 16 x 1 cells, 4 patches
system/controlDict : solver incompressibleFluid; 300 iterations; write every 50
system/fvSchemes : steadyState; bounded Gauss upwind for div(phi,U)
system/fvSolution : GAMG for p, smoothSolver for U, SIMPLE with relaxation
Run : blockMesh; checkMesh; foamRun
Logs : log.blockMesh, log.checkMesh, log.foamRun
La registrazione delle decisioni di configurazione è una tabella con una riga per ogni decisione non ovvia, scritta mentre lavori e non dopo:
| Decisione | Alternative considerate | Scelta | Prova | Controllo |
|---|---|---|---|---|
| Condizione di velocità in uscita | zeroGradient, inletOutlet | zeroGradient | UG 2.1 (uscita di pitzDailySteady) | il calcolo converge, nessun riflusso in uscita |
| Schema di convezione | linear, linearUpwind, upwind | bounded Gauss upwind | UG 4.5; Parte 5 | confronto del profilo con Poiseuille |
| Primo errore significativo | — | per es. un punto e virgola mancante in 0/U | la riga del log | corretto; foamDictionary 0/U legge il file |
L’interpretazione di U, p e del bilancio di massa dovrebbe indicare: la velocità massima e dove il profilo diventa parabolico; la caduta di pressione lungo il canale e il suo confronto con −12νU/H²; le portate in ingresso e in uscita e la loro differenza (sezione 28).
21 Progetto finale: valutazione e diagnostica
Prima di consultare la soluzione di riferimento, prova i seguenti controlli autonomi.
foamVersion
blockMesh > log.blockMesh 2>&1
checkMesh > log.checkMesh 2>&1
foamDictionary -entry solver -value \
system/controlDict
foamDictionary -entry ddtSchemes/default -value \
system/fvSchemes
foamRun > log.foamRun 2>&1
tail -n 45 log.foamRun
Il successo è più di un codice d’uscita
- La mesh generata ha il numero atteso di celle e di patch.
- I nomi del contorno della mesh corrispondono a tutte le voci richieste dei campi.
- Il solutore legge tutti i dizionari e i campi richiesti.
- Il calcolo produce cartelle temporali scritte e campi U e p non banali.
- La continuità e il comportamento della soluzione sono ragionevoli per questo caso didattico.
- Lo studente sa spiegare ogni scelta e indicarne la fonte.
Regola di valutazione
Se il calcolo fallisce, la valutazione resta utile: consegna l’esatto primo errore, mostra la tua indagine e isola la causa. La diagnosi autonoma e basata sulle prove è essa stessa uno degli obiettivi del corso.
Approfondimento: trasformare ogni criterio di successo in un comando e in un risultato atteso
| Criterio | Come controllarlo | Atteso per il progetto finale |
|---|---|---|
| Celle e patch | log.checkMesh: “cells”, tabella delle patch | 1.280 celle; inlet 16, outlet 16, walls 160, frontAndBack 2.560 facce; “Mesh OK.” |
| Corrispondenza dei nomi | foamDictionary -entry boundaryField -keywords 0/U (e 0/p) | gli stessi quattro nomi della mesh |
| Dizionari letti | intestazione di log.foamRun e prima iterazione | nessun errore fatale; righe “Selecting …” per viscosità e trasporto della quantità di moto |
| Cartelle scritte | foamListTimes | 50 100 150 200 250 300 |
| Campi non banali | foamDictionary -entry internalField 300/U (o ParaView) | lista non uniforme; Ux fino a ≈0,015 m/s; p da ≈9 × 10⁻⁴ m²/s² (o poco più) all’ingresso fino a 0 |
| Continuità | ultima riga “time step continuity errors” | piccola, non crescente |
| Spiegazione | README e registrazione delle decisioni | una fonte per ogni scelta |
Valore atteso di ddtSchemes/default: steadyState; valore atteso di solver: incompressibleFluid. Nel log del solutore non deve comparire la riga “Solving for Uz” (la direzione vuota non viene risolta). Se il calcolo termina ma a 300 i campi sono ancora uniformi, le cause più probabili sono un campo non letto da 0/ o una condizione d’ingresso che non impone il valore atteso: torna alle sei domande della sezione 12.
22 Soluzione di riferimento A – blockMesh
Soluzioni · da non leggere prima di aver tentato il progetto finale.
Qui sotto è mostrata una geometria a blocco singolo valida. Le coordinate sono in metri. I nomi delle patch sono usati in modo coerente nelle sezioni seguenti.
system/blockMeshDict – prima metà:
FoamFile
{
format ascii;
class dictionary;
object blockMeshDict;
}
vertices
(
(0 0 0) (0.30 0 0)
(0.30 0.020 0) (0 0.020 0)
(0 0 0.001) (0.30 0 0.001)
(0.30 0.020 0.001) (0 0.020 0.001)
);
blocks
(
hex (0 1 2 3 4 5 6 7) (80 16 1)
simpleGrading (1 1 1)
);
La definizione del contorno continua nella sezione successiva. Questo listato è un riferimento, non un sostituto del ragionamento progettuale dello studente.
Approfondimento: leggere la geometria di riferimento
Non c’è alcuna voce units o convertToMeters, quindi le coordinate si leggono in metri (il valore predefinito SI; la Parte 2 usa invece units [mm];). I vertici sono scritti due per riga per compattezza: le loro etichette seguono comunque l’ordine della lista, da 0 a 7. I vertici 0–3 stanno nel piano z = 0, i vertici 4–7 in z = 0,001 m, con le coppie 0–4, 1–5, 2–6, 3–7 allineate nello spessore. In hex (0 1 2 3 4 5 6 7) le direzioni locali sono 0→1 (x, 80 celle), 1→2 (y, 16 celle), 0→4 (z, 1 cella); il prodotto misto (e₁ × e₂) · e₃ = 0,30 × 0,020 × 0,001 = 6 × 10⁻⁶ m³ è positivo, quindi il blocco è destrorso e il suo volume è il volume del dominio. simpleGrading (1 1 1) dà celle uniformi, adatte a un canale laminare a Re = 20 in un primo tentativo; uno studio raffinato potrebbe infittire le celle verso le pareti (Parte 2, sezione sul grading).
23 Soluzione di riferimento B – patch della mesh
Aggiungi questo a system/blockMeshDict:
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));
}
);
mergePatchPairs ();
Perché conta
La faccia d’ingresso è rivolta verso x negativo e quella d’uscita verso x positivo. Le facce anteriore e posteriore sono perpendicolari a z e c’è una sola cella in quella direzione, requisito di questa rappresentazione 2D.
Approfondimento: controllare l’orientamento delle facce e il requisito 2D
Ogni faccia è data da quattro etichette di vertice, ordinate in modo che la regola della mano destra dia una normale rivolta verso l’esterno del blocco (equivalentemente, in senso orario visto dall’interno). Verificane una: per l’ingresso (0 4 7 3), (r₄ − r₀) × (r₇ − r₄) = (0; 0; 0,001) × (0; 0,020; 0) = (−2 × 10⁻⁵; 0; 0): punta verso −x, fuori dal dominio. Lo stesso calcolo dà +x per l’uscita (1 2 6 5), −y e +y per le due facce di parete, −z e +z per (0 3 2 1) e (4 5 6 7). Tutte e sei le facce del blocco sono assegnate, quindi nessuna faccia finisce in una patch predefinita.
type wall per le pareti non è cosmetico: le marca per il calcolo della distanza dalla parete e per le funzioni di parete, se in seguito si sceglie un modello di turbolenza. type empty dice a OpenFOAM che non serve alcuna soluzione nella direzione normale a quelle facce; è accettato solo se la mesh è spessa una cella in quella direzione e se entrambe le facce opposte sono empty, e ogni campo deve allora usare type empty su quella patch. mergePatchPairs (); è una lista vuota, necessaria solo per mesh multiblocco con interfacce non conformi; lasciarla non fa danni.
24 Soluzione di riferimento C – velocità e pressione
0/U – esempio di campo completo:
FoamFile
{
format ascii;
class volVectorField;
object U;
}
dimensions [0 1 -1 0 0 0 0];
internalField uniform (0.01 0 0);
boundaryField
{
inlet { type fixedValue; value uniform (0.01 0 0); }
outlet { type zeroGradient; }
walls { type noSlip; }
frontAndBack { type empty; }
}
0/p – pressione cinematica per incompressibleFluid:
FoamFile
{
format ascii;
class volScalarField;
object p;
}
dimensions [0 2 -2 0 0 0 0];
internalField uniform 0;
boundaryField
{
inlet { type zeroGradient; }
outlet { type fixedValue; value uniform 0; }
walls { type zeroGradient; }
frontAndBack { type empty; }
}
Approfondimento: perché le condizioni vanno a coppie complementari
Su ogni contorno aperto una variabile è imposta (Dirichlet, fixedValue) e l’altra è lasciata libera (Neumann, zeroGradient):
| Patch | U | p | Ragionamento |
|---|---|---|---|
| inlet | fixedValue (0.01 0 0) | zeroGradient | La portata è imposta; la pressione si adegua per sostenerla |
| outlet | zeroGradient | fixedValue 0 | Livello di pressione fissato; il flusso sviluppato esce senza vincoli |
| walls | noSlip (U = 0) | zeroGradient | Nessun flusso attraverso la parete, nessuna pressione imposta |
| frontAndBack | empty | empty | Vincolo 2D, deve coincidere con il tipo della patch di mesh |
Fissare sia U sia p sullo stesso contorno sovravincola il problema; lasciarle entrambe libere su tutti i contorni lascia indefinito il livello della pressione (è allora che servono le impostazioni pRef in fvSolution – non qui, perché l’uscita fissa p = 0). Per un solutore incomprimibile contano solo le differenze di pressione, quindi p = 0 in uscita è un riferimento, non un valore atmosferico. noSlip equivale a fixedValue con uniform (0 0 0), scritto in modo più espressivo. L’internalField uniforme (0.01 0 0) è solo la stima iniziale delle iterazioni stazionarie. La forma su una riga inlet { type fixedValue; value uniform (0.01 0 0); } è lo stesso dizionario scritto in modo compatto (Parte 3).
25 Soluzione di riferimento D – fisica e controllo
constant/physicalProperties:
FoamFile
{
format ascii;
class dictionary;
object physicalProperties;
}
viscosityModel constant;
nu 1e-05;
constant/momentumTransport:
FoamFile
{
format ascii;
class dictionary;
object momentumTransport;
}
simulationType laminar;
system/controlDict:
FoamFile
{
format ascii;
class dictionary;
object controlDict;
}
solver incompressibleFluid;
startFrom startTime;
startTime 0;
stopAt endTime;
endTime 300;
deltaT 1;
writeControl timeStep;
writeInterval 50;
writeFormat ascii;
runTimeModifiable true;
Approfondimento: che cosa fa ogni voce, e le varianti che puoi incontrare
viscosityModel constant; seleziona un fluido newtoniano a viscosità cinematica nu costante; il pitzDailySteady ufficiale v14 usa esattamente nu 1e-05;. La v14 accetta anche un valore con unità esplicite – l’esempio pitzDailyWater della User Guide scrive nu 1 [cSt]; – che foamUnits sa decodificare. Le alternative non newtoniane (per esempio la famiglia newtoniana generalizzata che contiene BirdCarreau) sono trattate nella Parte 7.
simulationType laminar; seleziona il modello laminare di trasporto della quantità di moto, cioè lo sforzo viscoso newtoniano senza alcuna modellazione della turbolenza: non servono altri campi. In controlDict, solver incompressibleFluid; dice a foamRun quale modulo caricare; le voci di tempo producono 300 iterazioni stazionarie (deltaT 1, endTime 300) e una scrittura ogni 50 (writeControl timeStep, writeInterval 50) in ASCII (writeFormat ascii, leggibile con qualsiasi editor); runTimeModifiable true rilegge i dizionari durante il calcolo (Parte 4).
26 Soluzione di riferimento E – schemi
system/fvSchemes – punto di partenza didattico conservativo:
FoamFile
{
format ascii;
class dictionary;
object fvSchemes;
}
ddtSchemes
{
default steadyState;
}
gradSchemes
{
default Gauss linear;
}
divSchemes
{
default none;
div(phi,U) bounded Gauss upwind;
div((nuEff*dev2(T(grad(U))))) Gauss linear;
}
laplacianSchemes
{
default Gauss linear corrected;
}
interpolationSchemes
{
default linear;
}
snGradSchemes
{
default corrected;
}
Approfondimento: una riga per ogni termine delle equazioni
| Voce | Termine discretizzato | Commento |
|---|---|---|
ddtSchemes default steadyState | ∂/∂t | Derivata temporale eliminata: problema stazionario |
gradSchemes default Gauss linear | ∇p, ∇U | Teorema di Gauss con interpolazione lineare sulle facce; secondo ordine |
div(phi,U) bounded Gauss upwind | ∇·(φU), convezione | Primo ordine, robusto (vedi sezione 05) |
div((nuEff*dev2(T(grad(U))))) Gauss linear | ∇·(νeff dev2((∇U)ᵀ)) | Parte esplicita dello sforzo viscoso |
laplacianSchemes default Gauss linear corrected | ∇·(ν∇U), ∇·(∇p) | Diffusione con correzione di non ortogonalità |
interpolationSchemes default linear | valori cella → faccia | Interpolazione centrata |
snGradSchemes default corrected | gradiente normale sulle facce | Coerente con il laplaciano |
default none; in divSchemes obbliga a elencare esplicitamente ogni termine convettivo: se ne manca uno, il solutore si ferma con un errore che nomina la chiave mancante – esattamente il termine da aggiungere. Il nome div((nuEff*dev2(T(grad(U))))) è l’espressione letterale che il codice costruisce per la parte dello sforzo viscoso che contiene il gradiente di velocità trasposto; per un flusso incomprimibile la sua divergenza è formalmente nulla (∇·(∇U)ᵀ = ∇(∇·U)), ma viene mantenuta esplicitamente e le va assegnato uno schema. Il pitzDailySteady ufficiale usa lo stesso nome. Su una mesh ortogonale come questa corrected non costa nulla e non cambia nulla; su una mesh distorta conta (Parte 5).
27 Soluzione di riferimento F – soluzione
system/fvSolution – esempio didattico stazionario:
FoamFile
{
format ascii;
class dictionary;
object fvSolution;
}
solvers
{
p
{
solver GAMG;
smoother GaussSeidel;
tolerance 1e-7;
relTol 0.05;
}
U
{
solver smoothSolver;
smoother symGaussSeidel;
tolerance 1e-8;
relTol 0.1;
}
}
SIMPLE
{
nNonOrthogonalCorrectors 0;
}
relaxationFactors
{
fields { p 0.3; }
equations { U 0.7; }
}
Questi parametri sono una configurazione di partenza adatta a un semplice canale laminare ortogonale; non sono impostazioni numeriche predefinite universali.
Approfondimento: le scelte in fvSolution e un’alternativa documentata
GAMG (multigrid algebrico geometrico-agglomerato) con smoother GaussSeidel risolve l’equazione della pressione, che è simmetrica ed ellittica: il multigrid elimina l’errore a grande lunghezza d’onda che gli smoother semplici lasciano. smoothSolver con symGaussSeidel basta per le componenti della quantità di moto. relTol 0.05 e 0.1 fermano ogni soluzione lineare quando il residuo è sceso di quel fattore: in un calcolo stazionario risolvere ogni iterazione in modo esatto è fatica sprecata, perché i coefficienti cambiano all’iterazione successiva. tolerance è la soglia assoluta.
SIMPLE è l’algoritmo di accoppiamento pressione–velocità stazionario; nNonOrthogonalCorrectors 0 è giusto per una mesh a blocchi ortogonale. I fattori di rilassamento (campo p 0,3, equazione U 0,7) sono la coppia classica per il SIMPLE standard, che per la stabilità richiede p + U ≈ 1. Il pitzDailySteady ufficiale usa invece la variante SIMPLEC: consistent yes; con rilassamento delle equazioni 0,9 e nessun rilassamento del campo p, che di solito converge in meno iterazioni; aggiunge anche residualControl per fermarsi quando i residui raggiungono valori dati. Entrambe le scelte sono legittime; ciò che conta è sapere quale si sta usando e perché. Non servono voci pRef perché p è fissata all’uscita.
28 Ispezione di riferimento e verifica del caso
Controlla i tuoi file di riferimento rispetto agli obiettivi del corso
Da eseguire su OpenFOAM Foundation 14, nella nuova cartella del progetto finale:
foamDictionary system/controlDict
foamDictionary system/fvSchemes
foamDictionary system/fvSolution
foamDictionary constant/physicalProperties
foamDictionary constant/momentumTransport
blockMesh
checkMesh
foamRun
Semplice controllo fisico di plausibilità
Usando come lunghezza caratteristica l’altezza del canale H = 0,020 m, con U = 0,01 m/s e nu = 1e-5 m²/s: ReH = U H / nu = 20. Questo giustifica la scelta di un regime illustrativo laminare. La lunghezza d’ingresso e il profilo esatti dipendono dalla geometria e dalle ipotesi sull’ingresso.
Approfondimento: i numeri da confrontare con il tuo calcolo
Numero di Reynolds. ReH = 0,01 × 0,020 / 1,0 × 10⁻⁵ = 20. Per lastre parallele infinite il diametro idraulico è Dh = 2H = 0,040 m, quindi ReDh = 40; in entrambi i casi il flusso è molto al di sotto di qualunque soglia di transizione (dell’ordine di 10³ per un canale).
Lunghezza d’ingresso. Una correlazione molto usata per il flusso laminare in canale piano (Durst et al., 2005) dà Le/H ≈ 0,631/(1 + 0,044 Re) + 0,0442 Re ≈ 1,2 per Re = 20, cioè Le ≈ 0,024 m, circa l’8 % della lunghezza del canale; la stima più grossolana 0,05 ReDh Dh ≈ 0,08 m è più conservativa. In entrambi i casi il flusso è pienamente sviluppato ben prima dell’uscita, il che giustifica lì zeroGradient per U.
Flusso sviluppato. umax = 1,5 U = 0,015 m/s; gradiente di pressione cinematica −12νU/H² = −3,0 × 10⁻³ m/s²; su 0,30 m una stima a flusso pienamente sviluppato della caduta di pressione cinematica è 9,0 × 10⁻⁴ m²/s². La pressione d’ingresso calcolata sarà un po’ più alta, perché il profilo d’ingresso uniforme deve prima riorganizzarsi (perdita di pressione d’imbocco). Lo schema upwind su 80 celle aggiunge un suo piccolo errore – esattamente ciò che uno studio di raffinamento della mesh (caso svolto 2) quantificherebbe.
Bilancio di massa. Q = U H × profondità = 0,01 × 0,020 × 0,001 = 2,0 × 10⁻⁷ m³/s. Misurala con il function object già pronto patchFlowRate (User Guide 7.2):
foamPostProcess -func "patchFlowRate(name=inlet)" -latestTime
foamPostProcess -func "patchFlowRate(name=outlet)" -latestTime
Atteso: circa −2,0 × 10⁻⁷ m³/s all’ingresso (negativo: flusso entrante) e +2,0 × 10⁻⁷ m³/s all’uscita, con una differenza al livello degli errori di continuità. Se la tua installazione rifiuta il nome dell’argomento, esegui foamGet patchFlowRate e leggi nel file copiato la parola chiave esatta: la guida usa name= sulla riga di comando e patch= con #includeFunc.
29 Una funzionalità sconosciuta: un protocollo di ricerca
L’esercizio seguente va oltre il progetto finale minimo: studia un function object o una condizione al contorno che non sono comparsi in questa serie, poi spiega come si configurano.
Prove richieste
- Nome ufficiale dell’oggetto selezionabile e sua categoria.
- Una pagina della documentazione v14 o l’output di
foamInfo. - Un tutorial corrispondente o un esempio documentato.
- Il più piccolo sotto-dizionario completo necessario.
- Il file di destinazione e il motivo per cui va lì.
- Un controllo che provi che l’oggetto è caricato e produce il risultato atteso.
Direttive di input avanzate – riconoscerle prima di usarle
Potresti incontrare #include, l’espansione delle macro, #calc, #codeBlock, #codeStream, #codeDict e altre direttive. Lo scopo, a questo punto, è capire che l’input può essere calcolato o incluso dinamicamente; non aggiungere questi meccanismi se non risolvono un problema specifico.
Teoria facoltativa: leggere il codice sorgente
Per impostazioni poco comuni, esamina l’intestazione o il sorgente corrispondente con foamFind e conferma le voci di dizionario attese. Non è sviluppo in C++: è raccolta di prove.
Approfondimento: come appaiono le direttive e come vederne il risultato
Tutte le direttive iniziano con #; le macro iniziano con $. La User Guide 4.2 documenta il linguaggio. Il listato mostra le forme che è più probabile incontrare nei tutorial:
// Examples of directives you may meet (read UG 4.2 before using any of them)
#include "initialConditions" // insert another file here
#includeEtc "caseDicts/setConstraintTypes"
#includeFunc patchFlowRate(patch=outlet) // in system/functions
Umean 0.01;
H 0.020;
nu 1e-05;
Re #calc "$Umean*$H/$nu"; // computed when the file is read
inlet { type fixedValue; value uniform ($Umean 0 0); }
#include inserisce un file in quel punto; #includeEtc prende il file dalla cartella etc di OpenFOAM; #includeFunc carica un function object già pronto, con argomenti. $Umean sostituisce il valore di un’altra voce (nomi con ambito come $../H o $:subDict.key raggiungono altri livelli). #calc valuta un’espressione quando il file viene letto; la famiglia #code… compila al volo codice C++, il che richiede un compilatore funzionante ed è la più difficile da correggere – un motivo in più per usarla solo quando niente di più semplice funziona. Qualunque direttiva sia presente, questo comando mostra l’input che il solutore riceverà davvero:
foamDictionary -expand system/controlDict # shows the values after expansion
Un esempio svolto del protocollo, su un oggetto non usato in questa serie: il function object wallShearStress. Categoria: function object (foamToC -functionObjects); documentazione: foamInfo wallShearStress e User Guide 7.2, che mostra foamPostProcess -solver incompressibleFluid -func wallShearStress; destinazione: system/functions (oppure la riga di comando, per una valutazione una tantum); controllo: un campo wallShearStress scritto nelle cartelle temporali. Per il flusso sviluppato del progetto finale lo sforzo di taglio a parete cinematico atteso è ν · 6U/H = 3,0 × 10⁻⁵ m²/s² su ciascuna parete – un controllo quantitativo, non solo “è partito”.
30 Lista di controllo finale e riferimenti
Prove di competenza autonoma
- So trovare il file responsabile di una modifica richiesta.
- So distinguere tipo di patch, condizione al contorno e campo iniziale.
- So individuare dove si configurano proprietà fisiche, schemi e algoritmi.
- So interrogare e modificare i dizionari senza dipendere da un’interfaccia grafica.
- So trovare esempi validi e documentarne la provenienza.
- So leggere un errore di IO e isolarne l’origine probabile.
- So costruire un caso semplice e spiegare ogni file che contiene.
Riferimenti principali – OpenFOAM Foundation 14
- User Guide v14 – indice e tutti i capitoli
- 4.7 Strumenti di gestione dei casi (Case management tools)
- 4.2 Formato dei file di input/output (Basic input/output file format)
- 2.1 Tutorial backward-facing step
- Repository di OpenFOAM Foundation v14
Oltre questo corso
L’applicazione successiva è confrontare un caso didattico semplice con il caso di ricerca che esso prepara (nel corso originale, un reattore): individuare quali file e concetti restano invariati e quali ipotesi fisiche, geometriche o numeriche richiedono una nuova giustificazione. Non trasferire nel reattore i valori di addestramento senza validazione.
Approfondimento: una lista di controllo per passare dal caso di addestramento a un caso di ricerca
La struttura del caso non cambia: le stesse tre cartelle, gli stessi dizionari, gli stessi strumenti. Cambia la giustificazione dietro ogni valore. Un modo pratico di organizzare il confronto è una verifica a due colonne dei file del progetto finale:
| Elemento | Di solito invariato | Richiede una nuova giustificazione |
|---|---|---|
| Struttura del caso, intestazioni, linguaggio dei dizionari | ✓ | |
| Metodo di diagnosi, log, registrazione delle prove | ✓ | |
| Geometria e mesh | Dimensioni reali, flusso di lavoro CAD/Gmsh (caso svolto 3, caso svolto 4), convergenza di mesh (caso svolto 2) | |
| Proprietà fisiche | Viscosità e densità misurate, dipendenza dalla temperatura; eventualmente non newtoniano (Parte 7) | |
| Regime di flusso e modello | Re del dispositivo reale; laminare o turbolento; ipotesi 2D valida o no | |
| Condizioni al contorno | Portata e profilo d’ingresso misurati (caso svolto 6) | |
| Schemi e solutori | Schemi del secondo ordine una volta stabile la configurazione; qualità della mesh | |
| Risorse di calcolo | Decomposizione parallela (caso svolto 5) | |
| Validazione | Misure indipendenti sul dispositivo |
Fine della serie
Con questa parte la serie sulla costruzione dei casi è completa. Il percorso si chiude dove era cominciato: la Parte 1 – anatomia del caso e linguaggio dei dizionari ha introdotto la grammatica che foamDictionary analizza e a cui si riferisce ogni messaggio d’errore della sezione 12. Rileggerla adesso, dopo aver costruito un caso da una cartella vuota, è un buon test di quanto ne sia diventato automatico.
Tutte le parti, i sei casi svolti e i video sono raccolti nella pagina del progetto Imparare OpenFOAM 14.