OpenFOAM 14, parte 6: fvSolution, solutori lineari e algoritmi di soluzione

di giux CFD e OpenFOAM 57 min di lettura

Parte 6 di 8 della serie sulla costruzione del caso nel percorso Imparare OpenFOAM 14. La Parte 5 ha mostrato come system/fvSchemes traduce le derivate delle equazioni di bilancio in operatori discreti; questa parte si occupa di ciò che succede dopo la discretizzazione: come ogni sistema algebrico viene risolto e come le equazioni di pressione e velocità vengono accoppiate, cioè del file system/fvSolution.

fvSolution: solutori lineari e algoritmi di soluzione. Il percorso è sempre lo stesso: concetto → dizionario → conseguenze numeriche → esercizio. Ogni riquadro «Approfondimento» espande un punto con la matematica che sta sotto, il modo in cui OpenFOAM Foundation 14 lo implementa, le alternative, gli errori tipici e i controlli pratici.

Riquadro scuro con la scritta A x = b, sistema lineare ai volumi finiti, e sotto tre voci: fvSchemes assembla A e b, fvSolution risolve e accoppia, foamRun itera i campi
Figura 1. Il filo conduttore di questa parte: fvSchemes assembla la matrice A e il termine noto b, fvSolution stabilisce come risolvere ciascun sistema e come accoppiare le equazioni, foamRun (con il modulo scelto) itera i campi nel tempo o fino alla convergenza.

Prerequisiti. Questa parte viene dopo i primi sette esercizi CFD del corso (il flusso di lavoro in sette passi applicato nei casi pratici, a partire dal flusso laminare in canale con validazione di Poiseuille) e dopo la Parte 5 sugli schemi numerici. Piattaforma: OpenFOAM Foundation 14.

Videolezione sulla Parte 6 (in italiano), generata con Gemini Notebook a partire da questo articolo.

Prima di iniziare: patto di apprendimento

L’obiettivo è leggere, giustificare, ispezionare e modificare system/fvSolution senza confondere le impostazioni dei solutori lineari con la discretizzazione, con i modelli fisici o con il controllo del tempo di simulazione.

Che cosa dovresti saper fare alla fine

  • Tradurre un’equazione ai volumi finiti nell’idea di un sistema algebrico sparso A x = b.
  • Individuare il solutore lineare e le impostazioni di convergenza usate per p, U e per gli eventuali campi dei modelli.
  • Distinguere tolerance, relTol, maxIter, gli obiettivi sui residui esterni e la convergenza fisica.
  • Spiegare lo scopo di SIMPLE, PIMPLE, dei correttori di pressione e del rilassamento.
  • Usare la User Guide di OpenFOAM Foundation v14 e i file dei tutorial per giustificare una modifica.

Versione · La versione conta
Gli esempi usano OpenFOAM Foundation v14, dove foamRun carica moduli come incompressibleFluid. Le guide più vecchie che parlano di simpleFoam o pimpleFoam vanno interpretate con attenzione alla versione.

Approfondimento · Da simpleFoam e pimpleFoam a foamRun: che cosa è cambiato davvero

Fino alla versione 10 della distribuzione Foundation ogni combinazione di fisica e algoritmo aveva un eseguibile dedicato: simpleFoam per i flussi incomprimibili stazionari con SIMPLE, pisoFoam e pimpleFoam per quelli transitori. Dalla versione 11 questi eseguibili sono stati sostituiti da moduli solutore caricati da un unico programma, foamRun; il modulo si sceglie in system/controlDict con la voce solver incompressibleFluid; (oppure con foamRun -solver incompressibleFluid).

La conseguenza per fvSolution è importante: in v14 non è più il nome dell’eseguibile a dire se l’algoritmo è SIMPLE o PIMPLE. Lo stesso modulo incompressibleFluid gestisce entrambi i casi: il comportamento stazionario si ottiene dichiarando ddtSchemes { default steadyState; } in fvSchemes e scrivendo un sotto-dizionario SIMPLE in fvSolution, come nel tutorial pitzDailySteady della guida v14. Le voci dei solutori lineari (solvers), invece, sono rimaste sostanzialmente le stesse: per questo un vecchio fvSolution è spesso ancora leggibile, ma il suo contesto (eseguibile, file dei modelli, nomi delle dimensioni) va verificato.

foamToC -solvers          # list the solver modules available to foamRun
foamInfo incompressibleFluid
foamDictionary -entry solver -value system/controlDict

Prima di cominciare

Dovresti già avere una copia funzionante del canale di riferimento, una conoscenza di base di 0/U, 0/p (Parte 3) e controlDict (Parte 4) e la capacità di ispezionare fvSchemes (Parte 5). Gli esperimenti vanno eseguiti su copie, mai sulla simulazione di riferimento.

Ambito
Questa unità insegna la configurazione e la sua interpretazione. Non sostiene che un residuo basso dimostri un risultato CFD fisicamente accurato. Restano necessari controlli indipendenti della conservazione, delle condizioni al contorno e degli effetti della mesh.

1. Architettura del caso: dove si colloca fvSolution

OpenFOAM separa i passaggi fisici, di discretizzazione e di algebra lineare di un caso CFD.

Tre riquadri affiancati: 0/ campi iniziali e condizioni al contorno; constant/ mesh e modelli fisici; system/ numerica e controllo dell'esecuzione
Figura 2. Le tre cartelle di un caso OpenFOAM: 0/ contiene campi iniziali e condizioni al contorno, constant/ la mesh e i modelli fisici, system/ la numerica e il controllo dell’esecuzione. fvSolution vive in system/.
FileChe cosa controllaChe cosa non controlla
system/fvSchemesApprossimazione degli operatori differenzialiIl tipo di solutore della matrice
system/fvSolutionSolutori lineari, tolleranze, cicli di accoppiamentoDimensioni della mesh o velocità di ingresso
system/controlDictTempo, durata dell’esecuzione e scrittura dei risultatiIl precondizionatore del solutore della pressione
constant/*Proprietà del fluido e modelli attiviLe tolleranze sui residui delle equazioni
0/U, 0/pValori iniziali e al contornoIl numero di iterazioni SIMPLE

La domanda a cui risponde questa parte

Una volta discretizzate le equazioni, quale algoritmo calcola una soluzione per ciascun campo, con quale accuratezza viene risolto ogni sistema algebrico, e come si coordinano tra loro gli aggiornamenti di pressione e velocità?

Importante · Trappola di vocabolario
La parola solver (solutore) ha due livelli: incompressibleFluid è un modulo solutore per fluidi; GAMG e smoothSolver sono solutori di algebra lineare configurati in fvSolution.

Approfondimento · Che cosa contiene fvSolution e quando viene letto

Secondo la User Guide v14 (§4.6), fvSolution contiene un sotto-dizionario solvers con le impostazioni del solutore lineare di ciascuna equazione, un sotto-dizionario relaxationFactors e un sotto-dizionario di controllo dell’algoritmo, SIMPLE per i casi stazionari oppure PIMPLE per quelli transitori. Altri moduli possono leggere ulteriori voci (per esempio i moduli multifase leggono controlli specifici per la frazione di fase), ma per incompressibleFluid questi tre blocchi sono tutto ciò che serve.

Il file non viene letto una sola volta. Le impostazioni di un solutore lineare vengono cercate nel dizionario solvers ogni volta che un’equazione viene risolta, e con runTimeModifiable attivo (il comportamento abituale, vedi Parte 4) OpenFOAM rilegge i file di system/ quando vengono modificati durante l’esecuzione. Si può quindi, per esempio, abbassare un fattore di rilassamento mentre il calcolo gira; la modifica ha effetto all’iterazione successiva e il log lo segnala con una riga che indica la rilettura del dizionario. È comodo, ma rende meno riproducibile la cronologia della simulazione: annota sempre la modifica e il tempo in cui l’hai fatta.

Errore tipico: se manca il blocco di un campo che il modulo deve risolvere, l’esecuzione si ferma con un FOAM FATAL IO ERROR del tipo «keyword p is undefined in dictionary .../system/fvSolution/solvers». Il messaggio indica sia il nome cercato sia il percorso del dizionario: è la prima cosa da leggere.

2. Quadro numerico: dalla PDE ad A x = b

Una volta scelti gli schemi numerici, ogni equazione discretizzata viene assemblata in un sistema algebrico.

Tre riquadri collegati: PDE bilanci di massa e quantità di moto; fvSchemes operatori discreti e flussi alle facce; fvSolution soluzione lineare e accoppiamento
Figura 3. Dalla PDE al sistema algebrico: le equazioni di bilancio (PDE) vengono tradotte da fvSchemes in operatori discreti e flussi alle facce; fvSolution governa la soluzione lineare e l’accoppiamento tra le equazioni.

Un’equazione schematica di cella ai volumi finiti

Equazione schematica dei coefficienti

aP * phiP + sum(aN * phiN) = bP

Assemblando insieme tutte le celle, i valori incogniti si possono scrivere come un vettore x; i coefficienti formano la matrice A, di norma sparsa, e i contributi noti formano b. Quindi A x = b.

Un caso incomprimibile reale non risolve tutte le equazioni di governo come un’unica, gigantesca matrice completamente accoppiata. L’approccio comune, detto segregato, risolve le equazioni un campo alla volta e usa un algoritmo esterno per mantenere l’accoppiamento pressione–velocità.

Richiamo CFD facoltativo · Matrice sparsa
Una matrice è sparsa quando la maggior parte dei suoi elementi è nulla. Una cella ai volumi finiti interagisce soprattutto con le celle vicine, quindi memorizzare e risolvere la matrice densa completa sprecherebbe memoria e calcolo.

Approfondimento · Un esempio numerico: tre celle, una matrice 3 × 3

Consideriamo la diffusione stazionaria monodimensionale d/dx(Γ dφ/dx) = 0 su un segmento lungo 3 diviso in tre celle uguali (Δx = 1, Γ = 1), con φ = 1 al contorno sinistro e φ = 0 al contorno destro. Integrando sulla cella P e approssimando il flusso diffusivo su ciascuna faccia con la differenza tra i centri, Γ(φN − φP)/d, si ottiene un’equazione per cella. Per una faccia interna d = Δx = 1 e il coefficiente vale 1; per una faccia di contorno il centro della cella dista d = Δx/2 dalla faccia e il coefficiente vale 2.

cell 1:  3*phi1 - 1*phi2           = 2   (2 = 2*1, from the left boundary value)
cell 2: -1*phi1 + 2*phi2 - 1*phi3  = 0
cell 3:          -1*phi2 + 3*phi3  = 0   (right boundary value is 0)

    | 3 -1  0 |        | 2 |
A = |-1  2 -1 |    b = | 0 |      x = (5/6, 1/2, 1/6)
    | 0 -1  3 |        | 0 |

La soluzione, x = (0,833; 0,5; 0,167), coincide con il profilo lineare esatto 1 − x/3 valutato nei centri delle celle (0,5; 1,5; 2,5). Nella notazione usata qui, aP è il termine diagonale (3, 2, 3), gli aN sono i coefficienti fuori diagonale (−1) e bP raccoglie i contributi noti, qui i valori al contorno. Il valore assoluto della diagonale è maggiore o uguale alla somma dei valori assoluti fuori diagonale in ogni riga: la matrice è diagonalmente dominante, ed è simmetrica perché il coefficiente che lega P a N è lo stesso che lega N a P. Con un milione di celle esaedriche la struttura è identica: ogni riga ha un elemento diagonale più uno per ciascuna faccia interna della cella (al massimo sei), quindi circa 7 milioni di elementi non nulli contro i 1012 di una matrice densa.

Approfondimento · Come OpenFOAM memorizza la matrice: il formato LDU

OpenFOAM non usa un formato sparso generico (come CSR), ma il formato LDU della classe lduMatrix, costruito direttamente sulla topologia della mesh: un vettore diag con un valore per cella, un vettore upper e un vettore lower con un valore per ogni faccia interna. Ogni faccia interna ha una cella owner (proprietaria, indice minore) e una cella neighbour (vicina): il coefficiente upper della faccia sta nella riga dell’owner e nella colonna del neighbour, il coefficiente lower nella posizione trasposta. Se la matrice è simmetrica, lower non viene nemmeno allocato.

Le facce di contorno non generano elementi fuori diagonale: la condizione al contorno (Parte 3) contribuisce con una parte implicita alla diagonale e con una parte esplicita al termine noto b. Per questo un fixedValue rafforza la diagonale della cella adiacente (come il 3 nell’esempio a tre celle), mentre un zeroGradient non aggiunge nulla. Le patch di processore nei calcoli paralleli (vedi il caso pratico sul calcolo parallelo MPI) sono trattate come «interfacce» che scambiano i valori tra sottodomini a ogni prodotto matrice-vettore: è il motivo per cui il numero di iterazioni di un solutore lineare può cambiare leggermente con il numero di processori.

Verifica di previsione

Se in fvSchemes cambia soltanto div(phi,U), la matrice assemblata può cambiare anche se l’algoritmo e le voci dei solutori in fvSolution restano identici. Viceversa, sostituire GAMG con un altro solutore lineare valido non ridefinisce la PDE sottostante.

Approfondimento · Che cosa cambia davvero, in A o in b, quando si cambia uno schema

Il «può» della verifica di previsione è preciso. In OpenFOAM molti schemi di ordine superiore sono implementati come correzione differita: la parte implicita, che finisce nella matrice, è quella dello schema upwind, mentre la differenza tra lo schema scelto e l’upwind viene calcolata con i valori correnti ed entra nel termine noto b. È il caso di linearUpwind: passando da bounded Gauss upwind a bounded Gauss linearUpwind grad(U) la matrice A dell’equazione della quantità di moto resta strutturalmente quella dell’upwind e cambia soprattutto b. Passando invece a Gauss linear (differenze centrate) cambiano i pesi di interpolazione e quindi i coefficienti di A: la dominanza diagonale si indebolisce e i solutori iterativi possono richiedere più iterazioni o diventare instabili su mesh grossolane.

Il secondo enunciato vale in entrambe le direzioni: un solutore lineare diverso, se converge alla stessa tolleranza, produce la stessa soluzione discreta a meno dell’errore algebrico residuo. Se cambiando solo il solutore lineare cambia in modo apprezzabile il risultato finale (per esempio la caduta di pressione), le tolleranze erano troppo larghe: è un test utile per verificarlo.

3. Struttura del dizionario: dentro il dizionario solvers

Il primo sotto-dizionario importante associa ai nomi dei campi le impostazioni di algebra lineare.

Estratto didattico: system/fvSolution

solvers
{
    p
    {
        solver          GAMG;
        tolerance       1e-7;
        relTol          0.01;
        smoother        DICGaussSeidel;
    }

    U
    {
        solver          smoothSolver;
        smoother        symGaussSeidel;
        tolerance       1e-5;
        relTol          0.1;
    }
}
VoceInterpretazione
p / UCampo di cui si risolve il sistema lineare discreto
solverMetodo iterativo di algebra lineare scelto
toleranceSoglia assoluta di arresto per il residuo
relTolRiduzione del residuo richiesta rispetto al valore iniziale
smootherOperazione iterativa locale, richiesta da alcuni metodi

Attenzione · Valori di esempio
I numeri mostrati illustrano la sintassi e uno schema comune nei tutorial; non sono tolleranze raccomandate per ogni mesh e ogni modello.

Approfondimento · Tutte le voci che un blocco di solutore lineare può contenere

La tabella qui sopra elenca le voci essenziali. La User Guide v14 ne documenta altre, con i relativi valori predefiniti:

VoceRuoloPredefinito / note
solverMetodo: PCG, PBiCG, PBiCGStab, smoothSolver, GAMG, diagonalobbligatoria
toleranceSoglia assoluta sul residuo normalizzatoda specificare (la guida la indica come richiesta)
relTolRapporto residuo finale / inizialeda specificare (la guida la indica come richiesta)
maxIterNumero massimo di iterazioni1000
preconditionerPer PCG, PBiCG, PBiCGStabDIC/DILU, FDIC, GAMG, diagonal, none
smootherPer smoothSolver e GAMGGaussSeidel, symGaussSeidel, DIC/DILU, DICGaussSeidel
nSweepsPassate dello smoother prima di ricalcolare il residuo (smoothSolver)1

Il solutore diagonal serve per le equazioni esplicite, la cui matrice è solo diagonale: non itera e non usa tolleranze. Per vedere quali nomi accetta la tua installazione si può provocare di proposito un errore su una copia (per esempio solver xyz;): OpenFOAM risponde con l’elenco dei solutori validi per quel tipo di matrice. Per controllare che cosa il modulo leggerà davvero dopo l’espansione delle macro, usa foamDictionary -expand -entry solvers system/fvSolution.

4. Configurazione transitoria: leggere un estratto completo in stile v14

Questo estratto segue la struttura illustrata nella descrizione ufficiale v14 di un caso transitorio con incompressibleFluid.

Schema illustrativo da tutorial v14

solvers
{
    p
    {
        solver          GAMG;
        tolerance       1e-7;
        relTol          0.01;
        smoother        DICGaussSeidel;
    }
    pFinal
    {
        $p;
        relTol          0;
    }
    "(U|k|epsilon)"
    {
        solver          smoothSolver;
        smoother        symGaussSeidel;
        tolerance       1e-5;
        relTol          0.1;
    }
    "(U|k|epsilon)Final"
    {
        $U;
        relTol          0;
    }
}
PIMPLE
{
    nNonOrthogonalCorrectors 0;
    nCorrectors              2;
}

Leggilo come uno schema riutilizzabile, non come un caso completo a sé stante: devono esistere anche i campi, la mesh, le proprietà, gli schemi e i controlli di esecuzione appropriati. Il gruppo con l’espressione regolare copre i campi di turbolenza solo quando sono presenti e usati dal modello scelto.

Approfondimento · Come OpenFOAM sceglie il blocco giusto: nomi esatti, regex e macro $

Quando il modulo deve risolvere, per esempio, l’equazione di k, cerca la voce k nel dizionario solvers con questa precedenza: prima una chiave con il nome esatto; se non c’è, le chiavi tra virgolette che sono espressioni regolari, esaminate dall’ultima alla prima nell’ordine del file. L’espressione deve corrispondere al nome per intero: "(U|k|epsilon)" corrisponde a k ma non a kFinal, ed è per questo che serve il secondo gruppo "(U|k|epsilon)Final".

La macro $p; copia nel blocco corrente tutte le voci del blocco p; le voci scritte dopo la macro le sovrascrivono (qui relTol 0;). Anche $U; funziona sebbene non esista una chiave letterale U: la ricerca della macro accetta le chiavi regex, quindi trova "(U|k|epsilon)". Il risultato espanso si controlla così:

foamDictionary -expand -entry solvers/pFinal system/fvSolution
foamDictionary -expand -entry solvers system/fvSolution     # whole block, macros expanded

Nel blocco PIMPLE di questo estratto nOuterCorrectors non compare: vale il predefinito 1, cioè la modalità «PISO» (un solo passaggio esterno per passo temporale con due correzioni di pressione), adatta quando il numero di Courant resta sotto 1. In un caso laminare come il canale di riferimento k ed epsilon non esistono: il gruppo regex è innocuo ma inutile, e conviene ridurlo a U e UFinal.

5. Residui e arresto: tre controlli di convergenza diversi

Le impostazioni tolerance, relTol e maxIter governano l’arresto di una singola soluzione lineare.

CriterioIdea matematicaSignificato
tolerancerfinal < TSi ferma sotto una soglia di residuo
relTolrfinal / rinitial < RSi ferma dopo una riduzione relativa sufficiente
maxIterN raggiunge il limiteSi ferma se si raggiunge il tetto di iterazioni

Un solutore lineare iterativo in generale si ferma quando viene raggiunta una qualsiasi di queste condizioni. Un solutore può quindi fermarsi perché è stato raggiunto l’obiettivo di riduzione relativa mentre il residuo finale è ancora sopra tolerance.

Voci in un blocco di solutore di campo

tolerance       1e-7;
relTol          0.01;
// maxIter è facoltativo; consulta i valori predefiniti della v14

Richiamo CFD facoltativo · Residuo ed errore fisico
Il residuo misura lo scarto tra la soluzione algebrica corrente e le equazioni discretizzate (con una normalizzazione specifica di OpenFOAM). Non quantifica direttamente l’errore di mesh, l’errore di modello né lo scostamento dall’esperimento.

Nella User Guide di Foundation v14 sia tolerance sia relTol vengono specificati per le voci dei solutori lineari; maxIter è facoltativo e ha un valore predefinito documentato (1000).

Approfondimento · La normalizzazione del residuo in OpenFOAM e perché il primo residuo vale spesso 1

Il residuo «grezzo» di un sistema è il vettore b − A x, che ha le unità dell’equazione e dipende dalla dimensione delle celle: un suo valore assoluto non direbbe nulla. OpenFOAM lo rende adimensionale dividendolo per un fattore di normalizzazione costruito con il valore medio x̄ del campo corrente:

            sum_cells | b - A x |
r = ---------------------------------------------------
     sum_cells ( | A x - A xbar | + | b - A xbar | ) + small

Il denominatore misura quanto il problema «si discosta» da una soluzione uniforme. Due conseguenze pratiche. Primo: se il campo iniziale è uniforme (x = x̄, come internalField uniform 0), il primo termine del denominatore si annulla e il secondo coincide con il numeratore, quindi il residuo iniziale del primo passo vale 1: non è un errore, è la normalizzazione. Secondo: il residuo è una somma (norma L1) su tutte le celle divisa per una somma analoga, quindi è una media; residui piccoli possono nascondere poche celle con errori grandi, motivo in più per i controlli fisici delle sezioni successive.

Su maxIter: se nel log compare No Iterations 1000 (o il valore che hai impostato), il solutore non ha raggiunto né tolerance né relTol ed è stato fermato dal tetto. Non è un messaggio d’errore, ma è quasi sempre il sintomo di un problema: matrice mal condizionata, solutore inadatto, mesh di cattiva qualità o una soluzione che sta divergendo.

6. Lettura numerica: un calcolo dei residui svolto

Un piccolo calcolo numerico è spesso più utile che imparare a memoria i nomi dei parametri del solutore.

Esempio svolto · Esempio illustrativo
Supponiamo che il residuo iniziale sia 2 × 10−3, con tolerance = 1 × 10−7 e relTol = 0,01. La soglia di arresto relativa è 2 × 10−5. L’iterazione lineare può quindi fermarsi sul criterio relativo prima di raggiungere 10−7.

Residuo durante la soluzioneRapporto relativoCriterio relativo raggiunto?
2,0 × 10−31,000No
8,0 × 10−40,400No
1,5 × 10−50,0075Sì

Che cosa cambia con relTol = 0?

Se relTol = 0, la condizione di riduzione relativa è di fatto disattivata. Il solutore prosegue allora verso la soglia tolerance, a meno che non incontri un’altra condizione di arresto come maxIter.

Verifica · Domanda
Se porti relTol da 0,1 a 0,01 mantenendo identici il residuo iniziale e tutte le altre condizioni, ti aspetteresti normalmente che la soluzione lineare faccia più o meno iterazioni? Spiegalo prima di provare. (Risposta commentata nella sezione 24.)

Approfondimento · Stimare il numero di iterazioni con un fattore di riduzione costante

Molti metodi iterativi, dopo le prime iterazioni, riducono il residuo di un fattore circa costante ρ a ogni iterazione (il fattore di convergenza). Nella tabella il primo passo porta il residuo da 2,0 × 10−3 a 8,0 × 10−4, cioè ρ ≈ 0,4. Se ρ restasse 0,4, dopo n iterazioni il rapporto sarebbe 0,4n, e si possono stimare le iterazioni necessarie per ciascun criterio:

CriterioRapporto da raggiungeren minimo (ρ = 0,4)
relTol 0.10,1ln 0,1 / ln 0,4 = 2,5 → 3
relTol 0.010,01ln 0,01 / ln 0,4 = 5,0 → 6
relTol 0, tolerance 1e-710−7 / 2 × 10−3 = 5 × 10−5ln(5 × 10−5) / ln 0,4 = 10,8 → 11

La stima mostra la regola generale: ogni decade in più di riduzione costa circa ln 10 / |ln ρ| iterazioni (qui circa 2,5). Il valore reale nella tabella scende più in fretta (1,5 × 10−5 corrisponde a un rapporto 0,0075), perché ρ non è costante: con GAMG in particolare ogni ciclo riduce il residuo molto più di un fattore 0,4. Il modo corretto di misurare ρ sul proprio caso è leggere nel log residuo iniziale, finale e numero di iterazioni: ρ ≈ (rfinal/rinitial)1/N. Con le righe di log della sezione 7: per p, (0,00008/0,014)1/5 ≈ 0,36.

7. Diagnostica dell’esecuzione: come leggere il log del solutore

OpenFOAM riporta i residui durante il calcolo. Tratta la singola soluzione della matrice separatamente dalla cronologia complessiva della convergenza CFD.

Righe di log illustrative, non output misurato

GAMG:  Solving for p, Initial residual = 0.014,
       Final residual = 0.00008, No Iterations 5

smoothSolver:  Solving for Ux, Initial residual = 0.0032,
       Final residual = 0.00021, No Iterations 2
Elemento del logCome interpretarlo
GAMG / smoothSolverQuale metodo lineare ha gestito l’equazione
p, UxQuale campo o componente veniva risolto
Initial residualScarto prima di questa particolare soluzione
Final residualScarto dopo questa particolare soluzione
No IterationsLavoro svolto da questa soluzione lineare

Non confondere due cronologie

Dentro una soluzione: il residuo scende mentre si itera su un singolo sistema matriciale. Attraverso i passi temporali o le iterazioni esterne: il residuo iniziale riportato può cambiare man mano che pressione, velocità e altri campi vengono aggiornati e le equazioni vengono riassemblate.

Verifica · Controlli fisici
Anche con residui riportati piccoli, controlla gli indicatori di continuità e di bilancio di massa, la coerenza delle condizioni al contorno e la grandezza osservabile che conta per la simulazione (per esempio la caduta di pressione).

Approfondimento · Anatomia di un passo temporale nel log di foamRun

Ecco come appare, in forma tipica, un passo di un caso transitorio incompressibleFluid 3D con PIMPLE in modalità PISO (nOuterCorrectors 1, nCorrectors 2). I numeri sono illustrativi; la struttura è quella che troverai nel tuo log.

Courant Number mean: 0.12 max: 0.45
Time = 0.0025s

smoothSolver:  Solving for Ux, Initial residual = 0.0032, Final residual = 0.00021, No Iterations 2
smoothSolver:  Solving for Uy, Initial residual = 0.0051, Final residual = 0.00030, No Iterations 2
smoothSolver:  Solving for Uz, Initial residual = 0.0047, Final residual = 0.00028, No Iterations 2
GAMG:  Solving for p, Initial residual = 0.014, Final residual = 0.00008, No Iterations 5
time step continuity errors : sum local = 2.1e-07, global = -3.4e-09, cumulative = -3.4e-09
GAMG:  Solving for p, Initial residual = 0.0021, Final residual = 9.1e-08, No Iterations 9
time step continuity errors : sum local = 3.0e-10, global = 1.2e-12, cumulative = -3.4e-09
ExecutionTime = 0.41 s  ClockTime = 0 s
  • Le tre righe Ux, Uy, Uz sono il predittore della quantità di moto: l’equazione vettoriale viene risolta componente per componente con la stessa matrice. In un caso 2D con patch empty la componente normale al piano non viene risolta e la sua riga manca.
  • Le due righe p sono le due correzioni di pressione (nCorrectors 2); la seconda usa le impostazioni di pFinal (sezione 8), quindi va fino a tolerance e richiede più iterazioni.
  • time step continuity errors è l’indicatore di bilancio di massa richiesto dal riquadro «Controlli fisici»: sum local è la media pesata del valore assoluto della divergenza dei flussi, global la sua somma con segno, cumulative la somma nel tempo. Valori che crescono passo dopo passo indicano che la pressione non è risolta abbastanza.
  • Con un precondizionatore il nome compare come prefisso: DICPCG: Solving for p, DILUPBiCGStab: Solving for Ux.
Approfondimento · Estrarre dal log le cronologie dei residui

Per confrontare due esecuzioni (come richiede il Laboratorio A) servono numeri, non impressioni. Alcuni modi, dal più semplice al più strutturato:

# 1. every pressure solve: initial residual and iteration count
grep "Solving for p" log.foamRun | awk -F'[=,]' '{print $3, $NF}' | head

# 2. total linear iterations for p over the whole run
grep "Solving for p" log.foamRun | awk '{s += $NF} END {print s}'

# 3. continuity errors only
grep "continuity errors" log.foamRun | tail -5

# 4. split the log into one file per quantity (logs/p_0, logs/Ux_0, ...)
foamLog log.foamRun

In alternativa la funzione residuals scrive i residui iniziali in un file di testo durante l’esecuzione: aggiungi #includeFunc residuals(p, U) al sotto-dizionario functions di controlDict (Parte 4) e troverai i dati in postProcessing/residuals/; controlla le opzioni con foamInfo residuals. Ricorda che questi file registrano il residuo iniziale di ogni soluzione: è la cronologia «attraverso i passi», non quella «dentro una soluzione».

8. Uno schema di efficienza: voci Final e riuso

Un campo può essere risolto più volte per passo temporale o per iterazione esterna. La soluzione finale può usare una regola di arresto diversa.

Soluzioni della pressione e della pressione finale

p
{
    solver          GAMG;
    tolerance       1e-7;
    relTol          0.01;
    smoother        DICGaussSeidel;
}
pFinal
{
    $p;
    relTol          0;
}
  • p definisce la configurazione ordinaria del solutore lineare della pressione.
  • pFinal è una configurazione con un nome speciale, usata quando l’algoritmo richiede la sua soluzione finale della pressione.
  • $p; riusa le voci di p; il successivo relTol 0; cambia la condizione di arresto relativa finale.
  • Questo schema evita di duplicare ogni parametro e aiuta a mantenere coerenti le configurazioni.

Nota · Il nome ha un significato
Un dizionario pFinal non è un altro file di campo fisico. Il suo significato è determinato dall’algoritmo del solutore quando sceglie le impostazioni per la soluzione algebrica finale.

Lo schema Final si usa soprattutto quando la pressione viene corretta più volte per passo. Permette alle prime correzioni di essere meno costose, mentre l’ultima viene portata a una convergenza più stretta.

Approfondimento · Quando esattamente viene usata la voce Final, e che cosa succede se manca

Nell’implementazione Foundation il suffisso Final viene aggiunto al nome del campo quando l’algoritmo si trova nell’ultima iterazione: per la velocità e gli altri campi durante l’ultimo ciclo esterno PIMPLE (con nOuterCorrectors 1, quindi, a ogni passo); per la pressione nell’ultima correzione di pressione dell’ultimo ciclo esterno (e, se ci sono correttori non ortogonali, nell’ultimo di essi). La User Guide v14 descrive lo scopo: risolvere l’equazione con impostazioni separate per il correttore finale, tipicamente relTol 0, in modo che ogni passo temporale converga fino a tolerance.

Perché conta nei casi transitori: l’errore algebrico lasciato a fine passo non viene più corretto e si accumula nel tempo come errore di continuità. Le correzioni intermedie, invece, verranno comunque superate dalla successiva: è inutile pagarle a caro prezzo. Nel log della sezione 7 la differenza è visibile: 5 iterazioni con relTol 0.01, 9 con relTol 0.

Se in un caso transitorio la voce manca, l’esecuzione si interrompe alla prima soluzione finale con un errore del tipo «keyword pFinal is undefined in dictionary .../system/fvSolution/solvers». Nei casi stazionari con SIMPLE (come il File didattico A della sezione 18 e il tutorial pitzDailySteady) le voci Final normalmente non compaiono.

9. Impostazioni condivise: espressioni regolari per i campi

Le chiavi dei dizionari OpenFOAM possono usare la corrispondenza con espressioni regolari, per evitare ripetizioni quando le equazioni di più campi richiedono le stesse impostazioni del solutore.

Campi raggruppati

"(U|k|epsilon)"
{
    solver          smoothSolver;
    smoother        symGaussSeidel;
    tolerance       1e-5;
    relTol          0.1;
}

"(U|k|epsilon)Final"
{
    $U;
    relTol          0;
}

Il primo gruppo può corrispondere a U, k o epsilon. Il secondo corrisponde ai nomi equivalenti che terminano con Final. Quando cambia il modello di turbolenza, verifica che la configurazione dei solutori copra i campi effettivamente presenti.

Una disciplina pratica

  • Usa prima i nomi espliciti dei campi; aggiungi i gruppi una volta capiti gli schemi.
  • Evita espressioni regolari troppo ampie, che catturano in silenzio campi estranei.
  • Ispeziona il file espanso e gli esempi dei tutorial prima di usare uno schema appena introdotto.

Verifica · Esercizio
Spiega perché omega non è coperto da "(U|k|epsilon)". Modifica lo schema per includerlo senza cambiare le altre alternative. (Soluzione nella sezione 24.)

Approfondimento · Sintassi delle chiavi regex e campi risolti dai modelli più comuni
  • Le virgolette doppie sono obbligatorie: è la virgolettatura che dice a OpenFOAM di trattare la chiave come espressione regolare. Senza virgolette, (U|k|epsilon) non è nemmeno una parola valida.
  • La corrispondenza è sull’intero nome: "U.*" cattura U, UFinal ma anche, per esempio, un eventuale Ua; ".*" cattura tutto ed è legittimo solo in contesti come relaxationFactors (sezione 19).
  • Il punto è un metacarattere: tra virgolette, "alpha.water" corrisponde anche a alphaXwater. Quando conta il punto letterale, la soluzione più sicura è scrivere il nome esatto senza virgolette (alpha.water), che viene confrontato carattere per carattere.
  • Precedenza: il nome esatto vince sempre; tra più regex vince l’ultima nel file. Un blocco esplicito U scritto dopo il gruppo prevale quindi sul gruppo per U.

Quali campi vadano coperti dipende dal modello in constant/momentumTransport (Parte 7):

ModelloCampi con equazione di trasporto
laminar (canale di riferimento)nessuno oltre a U e p
kEpsilon, realizableKE, RNGkEpsilonk, epsilon
kOmega, kOmegaSSTk, omega
SpalartAllmarasnuTilda

nut, la viscosità turbolenta, è calcolata dal modello e non ha equazione propria: non serve una voce in solvers. Se un campo risolto non è coperto, l’errore arriva alla prima soluzione di quel campo, con il messaggio «keyword omega is undefined in dictionary …/solvers».

10. Scegliere i solutori: matrici simmetriche e asimmetriche

Non tutti i solutori lineari sono adatti a ogni matrice: conta l’algebra della matrice.

SolutoreClasse di matrice tipicaOsservazione chiave
PCGSimmetricaMetodo del gradiente coniugato precondizionato
PBiCG / PBiCGStabAsimmetricaFamiglia del gradiente bi-coniugato
smoothSolverClassi supportatePassate iterative con uno smoother scelto
GAMGSistemi di pressione comuniAlgoritmo multilivello, correzione su griglia grossolana

La distinzione simmetrica/asimmetrica dipende dall’operatore assemblato. I termini di avvezione introducono spesso asimmetria; gli operatori legati alla diffusione possono portare a matrici simmetriche con una discretizzazione e un trattamento dei contorni adeguati.

Classe di errore · Un fallimento utile
Scegliere PCG per un’equazione asimmetrica può provocare un errore che elenca i solutori asimmetrici validi. È un’informazione diagnostica utile, non un motivo per cambiare a caso diverse impostazioni.

Richiamo CFD facoltativo · Simmetria della matrice
Una matrice è simmetrica se Aij = Aji. Sono la scelta degli operatori discretizzati e i coefficienti, non semplicemente il nome del campo fisico, a determinare se questa proprietà vale.

Approfondimento · Da dove nasce l’asimmetria: l’esempio a tre celle con convezione

Riprendiamo le tre celle della sezione 2 e aggiungiamo un flusso convettivo F = 1 diretto verso +x, discretizzato con l’upwind: sulla faccia di uscita di ogni cella il valore trasportato è quello della cella stessa (contributo +F sulla diagonale), sulla faccia di ingresso è quello della cella a monte (contributo −F sul coefficiente del vicino a monte). La riga della cella 2 diventa (2 + F) φ2 − (1 + F) φ1 − φ3 = 0:

diffusion only            diffusion + upwind convection (F = 1)
| 3 -1  0 |               | 4 -1  0 |
|-1  2 -1 |               |-2  3 -1 |
| 0 -1  3 |               | 0 -2  4 |
symmetric: A12 = A21      asymmetric: A21 = -2, A12 = -1

L’equazione della quantità di moto contiene sempre il termine convettivo div(phi,U), quindi la sua matrice è asimmetrica; l’equazione della pressione degli algoritmi SIMPLE/PIMPLE è invece un’equazione di Laplace (o di Poisson) ∇·((1/A)∇p) = ∇·(HbyA), con una matrice simmetrica. Ecco perché nei tutorial p usa GAMG o PCG, mentre U, k, epsilon usano smoothSolver o PBiCGStab.

Una precisazione sulla tabella: la User Guide v14 abbina PCG alle matrici simmetriche e PBiCG a quelle asimmetriche, ma presenta PBiCGStab come variante stabilizzata utilizzabile per entrambe le classi; l’output di foamSearch riportato nella guida mostra infatti tutorial che lo usano anche per p. La riga «asimmetrica» della tabella va letta come «adatta anche alle asimmetriche». GAMG e smoothSolver accettano entrambe le classi, a patto di scegliere uno smoother compatibile (sezione 11).

Approfondimento · Che aspetto ha l’errore di compatibilità

Secondo la guida v14, usare un solutore simmetrico su una matrice asimmetrica (o viceversa) produce un FOAM FATAL IO ERROR. La forma tipica è la seguente; l’elenco esatto dipende dalla versione e dalle librerie caricate:

--> FOAM FATAL IO ERROR:
Unknown asymmetric matrix solver PCG

Valid asymmetric matrix solvers are :
(
GAMG
PBiCG
PBiCGStab
smoothSolver
)

file: .../system/fvSolution/solvers/U from line ... to line ...

Leggilo come insegna il riquadro: la parola chiave sbagliata è solver, il file e il blocco sono indicati nell’ultima riga, la classe di matrice («asymmetric») è scritta nel messaggio e le alternative valide sono elencate. La correzione è una sola: cambiare solver (e di conseguenza preconditioner, perché DIC vale solo per matrici simmetriche: per PBiCGStab si usa DILU).

11. Controlli di algebra lineare: smoother e precondizionatori

Alcuni metodi iterativi usano impostazioni aggiuntive che migliorano la convergenza trasformando il problema o applicando iterazioni locali poco costose.

Precondizionatori

Un precondizionatore trasforma il problema algebrico in modo che il metodo iterativo converga più efficacemente. Tra gli esempi di Foundation v14 ci sono DIC, DILU e FDIC, per le strutture di matrice appropriate.

Sintassi illustrativa per un solutore asimmetrico

U
{
    solver          PBiCGStab;
    preconditioner  DILU;
    tolerance       1e-6;
    relTol          0.05;
}

Smoother

Uno smoother riduce le componenti di errore ad alta frequenza mediante iterazioni locali. Scelte comuni sono GaussSeidel, symGaussSeidel e DICGaussSeidel. Il metodo smoothSolver richiede un’impostazione smoother appropriata.

Esempio di smoothSolver

U
{
    solver          smoothSolver;
    smoother        symGaussSeidel;
    tolerance       1e-5;
    relTol          0.1;
}

Nota · Interpretazione
Non leggere smoother e preconditioner come parole chiave intercambiabili: il loro ruolo e le scelte accettabili dipendono dal solutore lineare selezionato.

Approfondimento · Che cosa fanno davvero Gauss-Seidel, DIC e DILU

Gauss-Seidel. Si percorrono le celle in ordine e si aggiorna ciascuna risolvendo la propria equazione con i valori più recenti dei vicini: φP ← (bP − Σ aN φN) / aP. Ogni aggiornamento propaga l’informazione di una sola cella, quindi gli errori che oscillano da cella a cella (alta frequenza) spariscono in poche passate, mentre quelli estesi su tutto il dominio decadono lentissimamente. symGaussSeidel esegue una passata in avanti e una all’indietro, eliminando la direzionalità legata alla numerazione delle celle: è lo smoother preferito nei tutorial per U e i campi di turbolenza. La voce nSweeps (predefinito 1) fissa quante passate fare prima di ricalcolare il residuo.

DIC e DILU. Sono fattorizzazioni incomplete: si costruisce un’approssimazione M ≈ A = L D U che ha la stessa struttura sparsa di A (nessun riempimento) e in cui, nella variante «diagonale» usata da OpenFOAM, viene modificata solo la diagonale. Risolvere con M costa quanto due passate di sostituzione, e il metodo di Krylov lavora sul sistema precondizionato M−1A x = M−1b, molto meglio condizionato. DIC (Cholesky incompleta) richiede una matrice simmetrica; DILU (LU incompleta) è la versione per matrici asimmetriche; FDIC è una versione più veloce di DIC che precalcola le quantità reciproche. Gli stessi oggetti possono fungere da smoother.

DICGaussSeidel applica uno smoothing DIC seguito da Gauss-Seidel ed è la scelta classica come smoother di GAMG per la pressione: essendo basato su DIC, è per matrici simmetriche (per un’equazione asimmetrica risolta con GAMG si usa GaussSeidel o symGaussSeidel). La guida v14 elenca inoltre GAMG stesso fra i precondizionatori (un ciclo multigrid per precondizionare PCG), diagonal (in genere non usato) e none.

SolutoreRichiedeEsempio di combinazione
PCGpreconditionerDIC o FDIC (simmetrica)
PBiCG, PBiCGStabpreconditionerDILU per U; DIC se usato per p
smoothSolversmoothersymGaussSeidel o GaussSeidel
GAMGsmootherDICGaussSeidel o GaussSeidel per p

12. Il concetto di multigrid: GAMG, perché più livelli?

Il multigrid generalizzato geometrico-algebrico (GAMG, Generalised geometric-algebraic multigrid) è comunemente usato per l’equazione della pressione nei tutorial di OpenFOAM.

Tre riquadri: griglia fine, risolve gli errori locali ad alta frequenza; livelli grossolani, trattano i modi di errore a variazione lenta; ritorno, applica le correzioni sui livelli più fini
Figura 4. Il principio di GAMG: sulla griglia fine si eliminano gli errori locali ad alta frequenza, sui livelli grossolani si trattano i modi di errore che variano lentamente, poi le correzioni vengono riportate sui livelli più fini.

Molti metodi iterativi eliminano rapidamente gli errori che variano velocemente ma faticano con quelli che variano lentamente. Una gerarchia di sistemi più grossolani può risolvere queste componenti a grande lunghezza d’onda in modo più economico.

Esempio di opzioni GAMG

p
{
    solver                GAMG;
    tolerance             1e-7;
    relTol                0.01;
    smoother              DICGaussSeidel;
    nCellsInCoarsestLevel 10;
}

Parametri da saper riconoscere

  • nCellsInCoarsestLevel: dimensione obiettivo approssimativa del livello più grossolano.
  • cacheAgglomeration: riusa le informazioni di agglomerazione, dove applicabile.
  • mergeLevels: strategia di sgrossamento multilivello; cambiarla può alterare robustezza e prestazioni.
  • nPreSweeps / nPostSweeps: lavoro di smoothing sui diversi livelli.

Flusso di lavoro · Non ottimizzare troppo presto
Per un primo esperimento mantieni le opzioni GAMG esistenti e cambia un solo parametro controllato, soltanto dopo aver registrato una situazione di riferimento. Una modifica che riduce il numero di iterazioni non è automaticamente una soluzione fisicamente migliore.

Approfondimento · Perché il multigrid funziona: frequenze dell’errore e costo per cella

Scomponiamo l’errore e = x − xesatto in modi di Fourier. Un’iterazione di Gauss-Seidel smorza di molto i modi la cui lunghezza d’onda è di poche celle, ma un modo esteso su n celle si riduce solo di un fattore circa 1 − c/n2 per passata: su una mesh di 200 celle per lato servirebbero decine di migliaia di passate. L’idea del multigrid è che un errore liscio sulla griglia fine diventa oscillante su una griglia più grossolana: dopo poche passate di smoothing (pre-sweeps) il residuo viene trasferito a un livello con meno celle, dove lo stesso smoother è di nuovo efficace; scendendo fino al livello più grossolano (circa nCellsInCoarsestLevel celle) il sistema è così piccolo da essere risolto quasi esattamente; la correzione viene poi interpolata verso l’alto e rifinita con altre passate (post-sweeps). Il risultato è un numero di cicli quasi indipendente dalla dimensione della mesh e un costo proporzionale al numero di celle.

Confronto con PCG: il numero di iterazioni del gradiente coniugato cresce come la radice del numero di condizionamento, e per un laplaciano questo cresce come (L/h)2; dimezzare la dimensione delle celle raddoppia quindi circa le iterazioni di PCG, mentre quelle di GAMG restano quasi uguali. È per questo che GAMG domina per la pressione su mesh medio-grandi, mentre su mesh piccole (come il canale didattico da poche migliaia di celle) la differenza è minima. «Algebrico» significa che i livelli grossolani non sono mesh costruite geometricamente ma agglomerazioni di celle decise dai coefficienti della matrice (nei tutorial con l’agglomeratore faceAreaPair, che unisce le celle a coppie attraverso le facce più grandi).

Approfondimento · Le opzioni GAMG documentate in v14 e i loro valori predefiniti
OpzionePredefinitoSignificato
smootherobbligatoriaSmoother usato su ogni livello
agglomeratorfaceAreaPair (nei tutorial)Metodo di agglomerazione delle celle
cacheAgglomerationtrueConserva la gerarchia tra una soluzione e l’altra (la mesh statica non cambia)
nCellsInCoarsestLevel10Dimensione obiettivo del livello più grossolano
directSolveCoarsestfalseSoluzione diretta sul livello più grossolano invece che iterativa
mergeLevels1Quanti livelli di agglomerazione fondere in un passo (1 = sgrossamento graduale)
nPreSweeps0Passate prima di scendere al livello grossolano
preSweepsLevelMultiplier / maxPreSweeps1 / 4Crescita e limite delle pre-passate con il livello
nPostSweeps2Passate dopo il ritorno dal livello grossolano
postSweepsLevelMultiplier / maxPostSweeps1 / 4Crescita e limite delle post-passate
nFinestSweeps2Passate sul livello più fine

Nota per la lettura della guida: la User Guide v14 scrive questa voce come directSolveCoarset; il nome usato nel codice e nei dizionari è directSolveCoarsest, ma prima di usarla verificane la grafia nei tutorial con foamSearch. Per il primo esperimento il consiglio del riquadro resta valido: questi valori predefiniti vanno bene nella stragrande maggioranza dei casi.

13. Fattori di rilassamento: sotto-rilassamento per la stabilità

Il sotto-rilassamento limita le variazioni da un’iterazione non lineare o segregata alla successiva ed è particolarmente importante per i calcoli stazionari.

Esempio in stile stazionario

relaxationFactors
{
    fields
    {
        p               0.3;
    }
    equations
    {
        U               0.7;
    }
}
PosizioneChe cosa viene rilassato
fieldsIl campo dopo la soluzione del sistema lineare
equationsL’equazione algebrica prima della soluzione

Un rilassamento inferiore a 1 rende in generale le variazioni più prudenti. Questo può migliorare la stabilità, ma può anche rallentare il progresso. Il fattore migliore dipende dalla qualità della mesh, dall’algoritmo di accoppiamento, dal modello fisico e dal comportamento dell’equazione.

Richiamo CFD facoltativo · Formula facoltativa
Per il rilassamento di un campo l’idea è φnuovo = φvecchio + α (φcandidato − φvecchio). Con α = 0,3 viene accettato solo il 30% dell’aggiornamento proposto. Il rilassamento delle equazioni agisce sui coefficienti della matrice invece di applicare direttamente questa miscela di campi.

Attenzione · Stazionario contro transitorio
La User Guide v14 tratta il sotto-rilassamento per i casi stazionari e osserva che, per i casi transitori con incompressibleFluid, si possono invece usare fattori di equazione pari a 1; non trapiantare acriticamente i valori di rilassamento di un caso stazionario.

Approfondimento · Il rilassamento delle equazioni in formule, e perché 1 non significa «nessun effetto»

Il rilassamento implicito delle equazioni (alla Patankar) divide la diagonale per α e aggiunge al termine noto un contributo che contiene il valore precedente:

(aP/alpha) * phiP + sum(aN * phiN) = bP + ((1 - alpha)/alpha) * aP * phiP_old

A convergenza φP = φP,old e i due termini aggiunti si cancellano: la soluzione finale è la stessa, cambia solo il percorso. Con α = 0,7 la diagonale cresce di 1/0,7 ≈ 1,43 volte: la matrice diventa più diagonalmente dominante (i solutori iterativi convergono meglio) e ogni iterazione fa un passo più corto. In OpenFOAM, prima di applicare α, la diagonale viene anche portata almeno al valore della somma dei moduli dei coefficienti fuori diagonale: per questo la guida v14 indica per i casi transitori un fattore 1 per tutte le equazioni (equations { ".*" 1; }, come nel File didattico B), che non rallenta nulla ma assicura quella che la guida chiama «uguaglianza diagonale», in generale necessaria alla convergenza di un’equazione matriciale.

Esempio sul campo. Se in una cella pvecchio = 10 m²/s² e la soluzione dell’equazione della pressione propone pcandidato = 20 m²/s², con α = 0,3 il valore accettato è 10 + 0,3 × 10 = 13 m²/s². Indicazioni della guida v14: nei tutorial stazionari si usano 0,3 per p e 0,7 per U e per i campi di turbolenza (raggruppati con una regex); valori fino a 0,9 possono essere stabili in alcuni casi, mentre valori molto sotto 0,2 rendono la convergenza proibitivamente lenta. Un campo o un’equazione assenti da relaxationFactors non vengono rilassati.

14. Algoritmo pressione–velocità: SIMPLE, accoppiamento stazionario

SIMPLE collega pressione e velocità mentre cerca una soluzione stazionaria delle equazioni di conservazione accoppiate.

Tre riquadri: quantità di moto, predice la velocità; pressione, impone la continuità; aggiornamento, corregge flussi e velocità
Figura 5. Un passo dell’algoritmo SIMPLE: predizione della velocità dall’equazione della quantità di moto, equazione della pressione che impone la continuità, correzione dei flussi e della velocità. Il ciclo si ripete a ogni iterazione stazionaria.

Il processo si ripete di iterazione in iterazione finché non sono soddisfatti i criteri di convergenza richiesti o una condizione di arresto. Non equivale a risolvere una sola volta un’unica matrice della pressione.

Schema didattico minimo, caso stazionario

SIMPLE
{
    nNonOrthogonalCorrectors 0;
    residualControl
    {
        p               1e-5;
        U               1e-6;
    }
}

Impostazioni dell’algoritmo e tolleranza lineare

  • solvers/p/tolerance governa una singola soluzione matriciale.
  • SIMPLE/residualControl specifica i controlli di convergenza per l’iterazione stazionaria complessiva, usando la misura di residuo di campo applicabile.
  • nNonOrthogonalCorrectors controlla le correzioni aggiuntive dell’equazione della pressione associate alla discretizzazione non ortogonale.

Esempio dalla versione 14 · Attenzione a SIMPLEC
Un tutorial che usa consistent yes; attiva una formulazione di tipo SIMPLEC, con scelte di rilassamento che possono essere tarate con cura. Non copiare quei coefficienti in casi SIMPLE ordinari senza comprenderne il ruolo.

Approfondimento · I passi di SIMPLE in formule

Scriviamo l’equazione discreta della quantità di moto di ogni cella separando la diagonale A dal resto: A UP = H(U) − ∇p, dove H raccoglie i contributi dei vicini e le sorgenti. Da qui U = HbyA − (1/A)∇p, con HbyA = H/A. Imponendo la continuità ∇·U = 0 si ottiene l’equazione della pressione. Un’iterazione SIMPLE di incompressibleFluid procede così:

  • Predittore della quantità di moto: si assembla l’equazione di U con la pressione corrente, la si rilassa (fattore equations/U) e la si risolve con le impostazioni solvers/U (righe Ux, Uy, Uz nel log).
  • Equazione della pressione: ∇·((1/A)∇p) = ∇·(HbyA), risolta con solvers/p, ripetuta nNonOrthogonalCorrectors volte in più se richiesto.
  • Correzione dei flussi: il flusso alle facce phi viene corretto con il gradiente di pressione appena calcolato, e diventa conservativo a meno della tolleranza della pressione.
  • Rilassamento della pressione (fattore fields/p, per esempio 0,3) e correzione della velocità U = HbyA − (1/A)∇p.
  • Equazioni dei modelli (turbolenza ecc.), poi l’iterazione successiva.

Nella formulazione SIMPLEC (consistent yes;) al posto di 1/A si usa 1/(A − ΣH1), dove ΣH1 è la somma dei coefficienti dei vicini: si tiene conto in modo approssimato della risposta delle celle vicine, la correzione di pressione è più «coerente» e il rilassamento della pressione diventa in gran parte superfluo. La guida v14 avverte però che nel tutorial pitzDailySteady la convergenza di SIMPLEC è molto sensibile ai fattori di rilassamento, che lì valgono 0,9 e sono stati tarati con cura: non sono adatti al SIMPLE standard, e SIMPLEC è in genere affidabile solo su geometrie semplici.

Approfondimento · Come funziona residualControl e che cosa scrive nel log

All’inizio di ogni iterazione stazionaria il modulo confronta il residuo iniziale della prima soluzione di ciascun campo elencato in residualControl con la soglia indicata (per un vettore come U si considerano le componenti risolte). Quando tutti i campi elencati sono sotto soglia, la simulazione scrive i risultati e termina prima di endTime, con un messaggio del tipo SIMPLE solution converged in 285 iterations (è il numero riportato dalla guida v14 per pitzDailySteady). I campi non elencati non vengono controllati. Se le soglie non vengono mai raggiunte, l’esecuzione prosegue fino a endTime di controlDict (Parte 4), dove nei casi stazionari il «tempo» è solo un contatore di iterazioni.

Attenzione alla coerenza tra i due livelli: il residuo iniziale di un’iterazione non può scendere molto sotto la riduzione che il solutore lineare produce a ogni iterazione. Con tolerance 1e-7 per p e residualControl p 1e-5 la combinazione è sensata; con una tolerance lineare più larga della soglia di residualControl la soglia potrebbe non essere mai raggiunta.

15. Cicli interni ed esterni: PIMPLE, accoppiamento transitorio

PIMPLE combina la correzione pressione–velocità con la ripetizione facoltativa del sistema accoppiato all’interno di un passo temporale.

Tre riquadri: passo temporale, avanza il tempo; ciclo esterno, aggiorna le equazioni; ciclo interno, correzioni di pressione
Figura 6. La struttura di PIMPLE: il passo temporale fa avanzare il tempo, il ciclo esterno riassembla e risolve il sistema accoppiato, il ciclo interno esegue le correzioni di pressione.

Voci di controllo di PIMPLE

PIMPLE
{
    nOuterCorrectors         1;
    nCorrectors              2;
    nNonOrthogonalCorrectors 0;
    momentumPredictor        yes;
}
ParametroChe cosa controlla
nOuterCorrectorsRipetizioni del sistema di equazioni accoppiato per passo
nCorrectorsCorrezioni pressione/velocità all’interno del passo
nNonOrthogonalCorrectorsCorrezioni di pressione aggiuntive per il contributo non ortogonale
momentumPredictorSe viene risolto un predittore iniziale della quantità di moto

Importante · I conteggi dei cicli non sono iterazioni lineari
Una soluzione della pressione eseguita due volte da PIMPLE non è la stessa cosa di un solutore lineare che compie due iterazioni dentro una soluzione della pressione. Questi livelli annidati devono restare concettualmente separati.

Approfondimento · Modalità PISO, cicli esterni e predittore della quantità di moto

Con nOuterCorrectors 1 PIMPLE si riduce all’algoritmo PISO: un predittore della quantità di moto e nCorrectors correzioni di pressione per passo, senza riassemblare l’equazione della quantità di moto. È accurato ed economico se il numero di Courant massimo resta indicativamente sotto 1 (vedi il controllo di deltaT nella Parte 4). Secondo la guida v14 nCorrectors vale tipicamente 2 o 3 e nOuterCorrectors tipicamente 1.

Con nOuterCorrectors > 1 l’intero sistema viene riassemblato e risolto più volte nello stesso passo: le non linearità (il flusso convettivo phi che compare nella matrice di U) vengono aggiornate, e diventano possibili passi temporali con Courant ben superiore a 1. In questo regime le iterazioni esterne si comportano come mini-iterazioni stazionarie e può servire il rilassamento anche nel transitorio. Per non sprecare cicli esterni quando il passo è già convergente, l’implementazione Foundation prevede un sotto-dizionario outerCorrectorResidualControl (soglie per campo che interrompono in anticipo i cicli esterni): prima di usarlo, cercane un esempio nei tutorial con grep -r outerCorrectorResidualControl $FOAM_TUTORIALS.

momentumPredictor no; salta la soluzione dell’equazione della quantità di moto e passa subito alla pressione, usando la velocità del passo precedente per costruire HbyA. La guida v14 indica che si può disattivare per alcuni flussi, per esempio a basso numero di Reynolds e multifase. Nel log la conseguenza è immediata: spariscono le righe Solving for Ux/Uy/Uz.

16. Criteri di arresto: tre livelli di iterazione annidati

Uno degli errori più comuni è interpretare ogni occorrenza della parola iterazione come lo stesso processo numerico.

Tre riquadri: livello 1, passi temporali o passi esterni stazionari; livello 2, cicli di accoppiamento SIMPLE/PIMPLE; livello 3, iterazioni lineari per A x = b
Figura 7. I tre livelli annidati di iterazione: passi temporali (o passi esterni stazionari), cicli di accoppiamento SIMPLE/PIMPLE, iterazioni lineari per risolvere A x = b.

Tre osservazioni diverse

OsservazioneGovernata da
Tempo di simulazione, eventi di scritturacontrolDict
Numero di passaggi di correzione della pressioneVoci SIMPLE/PIMPLE in fvSolution
Iterazioni per ridurre il residuo di p o Usolvers/p e solvers/U in fvSolution

Se un’esecuzione è costosa, individua quale ciclo consuma il lavoro prima di cambiare i parametri. Più correttori esterni possono aumentare il numero di sistemi assemblati e risolti; un relTol più stretto può aumentare le iterazioni dentro ogni soluzione lineare.

Da ragionare · Scenario svolto
Un passo transitorio può eseguire 2 correzioni di pressione; la prima soluzione della matrice della pressione può richiedere 4 iterazioni GAMG e quella finale 7. Sono due conteggi di iterazioni diversi, ciascuno dei quali descrive un livello diverso dell’algoritmo.

Approfondimento · Contare il lavoro di un passo: un esempio numerico

Prendiamo un passo PIMPLE con nOuterCorrectors 2, nCorrectors 2, nNonOrthogonalCorrectors 1 e momentumPredictor yes, in 3D. Il numero di sistemi lineari risolti è:

EquazioneConteggioImpostazioni usate
Quantità di moto2 cicli esterni × 3 componenti = 6U nel primo ciclo, UFinal nel secondo
Pressione2 esterni × 2 correttori × (1 + 1) non ortogonali = 8p per 7 soluzioni, pFinal per l’ultima

Se ogni soluzione di p costa in media 5 iterazioni GAMG e quella finale 12, il passo richiede circa 7 × 5 + 12 = 47 cicli GAMG; portando nOuterCorrectors a 1 le soluzioni di pressione scendono a 4 e il lavoro circa si dimezza, mentre portando relTol di p da 0,01 a 0,001 resterebbero 8 soluzioni ma ognuna più lunga. Sono due leve diverse su due livelli diversi: per sapere quale usare bisogna prima leggere nel log quale livello pesa di più (sezione 7).

17. Legame geometria–algoritmo: qualità della mesh e riferimento della pressione

Correzione di discretizzazione, non ortogonalità della mesh e riferimento della pressione sono questioni numericamente distinte.

Correzione non ortogonale

Le mesh non ortogonali possono richiedere correzioni trattate esplicitamente nel laplaciano dell’equazione della pressione. L’impostazione nNonOrthogonalCorrectors ripete la soluzione della pressione per aggiornare questi contributi, ma non ripara una mesh progettata male.

Configurazione illustrativa

PIMPLE
{
    nCorrectors              2;
    nNonOrthogonalCorrectors 1;
}
Approfondimento · Da dove viene la correzione non ortogonale e quando serve

Il flusso diffusivo attraverso una faccia richiede il gradiente normale alla faccia, (∇p)f · Sf. Su una mesh ortogonale il vettore d che unisce i centri delle due celle è parallelo alla normale Sf e il gradiente si ottiene esattamente dalla differenza (pN − pP)/|d|, una parte implicita che entra nella matrice. Se d e Sf formano un angolo θ (la non ortogonalità), si scompone Sf = Δ + k con Δ parallelo a d: la parte Δ resta implicita, la parte k · (∇p)f va calcolata con il gradiente della soluzione corrente ed entra in b come correzione esplicita. Ogni correttore non ortogonale risolve di nuovo l’equazione della pressione con il gradiente aggiornato.

Su una mesh blockMesh a blocco unico come il canale della Parte 2 θ = 0 ovunque e i correttori non servono: 0 è la scelta giusta. checkMesh riporta Mesh non-orthogonality Max: … average: … e segnala le facce oltre 70° come severely non-orthogonal. La guida v14 indica valori tipici di 0 per i casi stazionari e 1 per quelli transitori; su mesh molto distorte si combinano 1–2 correttori con schemi laplaciani limitati (Gauss linear limited corrected 0.33, Parte 5). Nei casi stazionari la non ortogonalità viene comunque «assorbita» dalle iterazioni SIMPLE successive, ed è per questo che vi basta 0.

Riferimento della pressione

In un dominio incomprimibile chiuso, senza un livello assoluto di pressione prescritto, la pressione può essere determinata solo a meno di una costante additiva. Una cella di riferimento e un valore di riferimento eliminano questa indeterminazione.

Dentro SIMPLE o PIMPLE, quando serve

pRefCell        0;
pRefValue       0;

Attenzione · Quando non serve
Un caso con un riferimento di pressione fisso adeguato su un contorno può non richiedere lo stesso riferimento interno. Stabiliscilo dalle condizioni al contorno effettive della pressione e dal comportamento del modulo, non da un modello generico.

Richiamo CFD facoltativo · Gauge della pressione
In molte formulazioni dei flussi incomprimibili sono i gradienti di pressione a muovere la velocità. Aggiungere una costante alla pressione non cambia quei gradienti; un riferimento fissa il livello arbitrario in un sistema chiuso.

Approfondimento · pRefCell e pRefValue in pratica
  • Le voci vengono lette solo se il campo della pressione ha bisogno di un riferimento, cioè se nessuna patch impone un valore (nessun fixedValue o tipo equivalente). Nel canale di riferimento, con fixedValue 0 all’uscita (Parte 3), sarebbero ignorate anche se presenti.
  • pRefCell è l’indice di una cella (0 è la prima cella della numerazione della mesh), pRefValue il valore di pressione attribuito lì, nelle unità del campo p (m²/s² per incompressibleFluid). La guida v14 li colloca abitualmente nel sotto-dizionario SIMPLE o PIMPLE.
  • L’esempio classico è la cavità con coperchio mobile: tutte pareti, zeroGradient per p ovunque, e quindi pRefCell 0; pRefValue 0;.
  • Se servono e mancano, il modulo si ferma con un errore che segnala l’assenza della voce pRefCell (o pRefPoint, l’alternativa che indica una posizione invece di un indice) nel dizionario dell’algoritmo. Senza alcun riferimento la matrice della pressione sarebbe singolare: il livello di p deriverebbe e il solutore lineare faticherebbe a convergere.

18. File di riferimento A: un fvSolution completo in stile stazionario

Configurazione illustrativa per un semplice caso stazionario incompressibleFluid con i campi p e U. Il caso circostante deve essere fisicamente e numericamente compatibile.

File didattico A: system/fvSolution

FoamFile
{
    format          ascii;
    class           dictionary;
    location        "system";
    object          fvSolution;
}

solvers
{
    p
    {
        solver          GAMG;
        tolerance       1e-7;
        relTol          0.01;
        smoother        DICGaussSeidel;
    }
    U
    {
        solver          smoothSolver;
        smoother        symGaussSeidel;
        tolerance       1e-6;
        relTol          0.1;
    }
}
SIMPLE
{
    nNonOrthogonalCorrectors 0;
    residualControl
    {
        p               1e-5;
        U               1e-6;
    }
}
relaxationFactors
{
    fields    { p 0.3; }
    equations { U 0.7; }
}

I valori sono scelti per spiegare i ruoli delle voci del dizionario; non sono impostazioni ottimali validate. Per un caso didattico laminare con steadyState in fvSchemes, adatta questo file alla configurazione effettiva del solutore.

Approfondimento · Leggere il File A riga per riga e adattarlo al canale laminare
  • Intestazione. Manca version 2.0;, che in v14 è facoltativa; class dictionary e object fvSolution devono corrispondere al file.
  • solvers. Solo p e U: è proprio ciò che serve a un caso laminar, senza campi di turbolenza. Nessuna voce Final, perché con SIMPLE non servono.
  • SIMPLE. Mesh ortogonale → 0 correttori; nessun pRefCell perché l’uscita fissa la pressione; residualControl con soglie piuttosto severe (1e-5 per p, 1e-6 per U, quest’ultima uguale alla tolerance lineare di U). Se le soglie non vengono raggiunte, l’esecuzione arriva fino a endTime: è una situazione da riconoscere nel log, non necessariamente un errore.
  • relaxationFactors. La forma compatta su una riga (fields { p 0.3; }) è sintatticamente identica a quella su più righe della sezione 13.

Prima di usarlo sul canale verifica la natura del caso: foamDictionary -entry ddtSchemes/default -value system/fvSchemes deve restituire steadyState. Se restituisce Euler o backward, il caso è transitorio e serve il File B della sezione successiva. Controlla anche constant/momentumTransport: con simulationType laminar; il File A è completo così com’è.

19. File di riferimento B: un fvSolution completo in stile transitorio

Un esempio PIMPLE separato, che include le voci finali dei solutori di pressione e velocità. Non mescolarlo indiscriminatamente con il caso stazionario.

File didattico B: system/fvSolution

FoamFile
{
    format          ascii;
    class           dictionary;
    location        "system";
    object          fvSolution;
}
solvers
{
    p
    {
        solver          GAMG;
        tolerance       1e-7;
        relTol          0.01;
        smoother        DICGaussSeidel;
    }
    pFinal
    {
        $p;
        relTol          0;
    }
    U
    {
        solver          smoothSolver;
        smoother        symGaussSeidel;
        tolerance       1e-6;
        relTol          0.1;
    }
    UFinal
    {
        $U;
        relTol          0;
    }
}
PIMPLE
{
    nOuterCorrectors         1;
    nCorrectors              2;
    nNonOrthogonalCorrectors 0;
}
relaxationFactors
{
    equations { ".*" 1; }
}
Approfondimento · File A e File B a confronto
ElementoFile A (stazionario)File B (transitorio)Perché
ddtSchemes attesosteadyStateEuler o backwardÈ lo schema temporale a decidere quale algoritmo ha senso
AlgoritmoSIMPLEPIMPLEIterazione verso lo stato stazionario contro avanzamento nel tempo
Voci FinalassentipFinal, UFinalSolo nel transitorio l’ultima correzione deve chiudere il passo fino a tolerance
Rilassamentop 0,3 (campo), U 0,7 (equazione)".*" 1 sulle equazioniNel transitorio il termine temporale stabilizza già; 1 assicura l’uguaglianza diagonale
ArrestoresidualControl o endTimeendTimeIl transitorio deve coprire l’intervallo di tempo fisico

Mescolare i due file produce errori tipici: un SIMPLE in un caso con Euler non viene letto come algoritmo transitorio; un caso transitorio senza pFinal si ferma all’ultima correzione del primo passo; un rilassamento 0,3/0,7 in un caso transitorio con nOuterCorrectors 1 rende ogni passo temporale inconsistente, perché la soluzione di fine passo non soddisfa più le equazioni. Nel File B momentumPredictor non compare: vale il predefinito, cioè il predittore viene risolto.

20. Flusso di lavoro Foundation v14: ispezionare fvSolution con gli strumenti da riga di comando

Gli strumenti di gestione del caso di Foundation v14 permettono un’ispezione mirata, invece di scorrere a mano dizionari lunghi.

Comandi di ispezione in sola lettura

foamDictionary system/fvSolution
foamDictionary -entry solvers system/fvSolution
foamDictionary -entry 'solvers/p/solver' \
    -value system/fvSolution
foamDictionary -entry 'solvers/p/relTol' \
    -value system/fvSolution

L’ordine esatto delle opzioni mostrato qui segue la User Guide di Foundation v14. Esegui questi comandi dalla radice del caso e confronta il risultato con il file originale.

Trovare i tipi di solutore usati nei tutorial

foamSearch -c "$FOAM_TUTORIALS" fvSolution \
    solvers/p/solver

Modifica sicura

Prima ispeziona, poi crea una copia di lavoro separata, quindi modifica solo il parametro previsto. Dopo, ispeziona di nuovo la voce risultante. Conservare una situazione di riferimento rende interpretabile il confronto numerico.

Sicurezza · Attenzione alle utility distruttive
Questo esercizio non richiede foamCleanCase né foamListTimes -rm. Queste utility possono cancellare mesh e risultati generati. Evitale sulla simulazione originale o su un caso di ricerca.

Approfondimento · Che cosa aspettarsi da questi comandi, e come modificare in modo tracciabile

Sul File didattico A le due interrogazioni con -value restituiscono solo il valore, senza punto e virgola: GAMG e 0.01. Senza -value si ottiene la voce completa (solver GAMG;). Altre forme utili, tutte documentate nella §4.7 della guida v14:

foamDictionary -entry solvers -keywords system/fvSolution   # p, U (or p, pFinal, "(U|k|epsilon)", ...)
foamDictionary -expand system/fvSolution                   # macros such as $p expanded
foamDictionary -entry solvers/p/relTol -set 0.001 system/fvSolution   # edit ONLY on a copy
diff -u ../step06_steady/system/fvSolution system/fvSolution          # record exactly what changed

foamSearch -c conta quante volte compare ciascun valore della voce cercata in tutti i file con quel nome sotto la cartella indicata. La guida v14 riporta come esempio 64 occorrenze di GAMG, 26 di PCG, 5 di smoothSolver e 2 di PBiCGStab per solvers/p/solver: i numeri della tua installazione possono differire, ma l’indicazione resta che GAMG è la scelta più comune per la pressione. La stessa tecnica funziona per qualsiasi voce, per esempio foamSearch -c "$FOAM_TUTORIALS" fvSolution SIMPLE/consistent per scoprire quali tutorial usano SIMPLEC.

21. Esercitazione guidata — Laboratorio A: un esperimento controllato sul solutore

Esegui l’esperimento su un clone isolato di un tutorial di Foundation v14, come pitzDailySteady, dopo averne confermato la posizione effettiva.

1. Crea una copia separata del caso

Solo copia di lavoro

mkdir -p "$HOME/OpenFOAM/training_step06"
cp -a "$FOAM_TUTORIALS/modules/incompressibleFluid/pitzDailySteady" \
   "$HOME/OpenFOAM/training_step06/step06_steady"
cd "$HOME/OpenFOAM/training_step06/step06_steady"

Se la distribuzione colloca il tutorial altrove, cercalo sotto $FOAM_TUTORIALS prima di copiarlo. Non sovrascrivere una cartella step06_steady già esistente.

Attenzione · Il percorso del tutorial nella guida v14
La User Guide v14 (§2.1) copia il tutorial da $FOAM_TUTORIALS/incompressibleFluid/pitzDailySteady, cioè senza la sottocartella modules/ usata nel comando precedente. Prima di copiare, verifica con find "$FOAM_TUTORIALS" -type d -name pitzDailySteady e usa il percorso che il comando restituisce.

2. Ispeziona e prepara

Non cambiare ancora alcun parametro

foamVersion
foamDictionary -entry solvers system/fvSolution
foamDictionary -entry SIMPLE system/fvSolution
blockMesh
checkMesh

3. Registra una situazione di riferimento

Esegui il caso con foamRun e salva il log del terminale. Annota il tipo di solutore di ciascun campo, i residui iniziali e finali tipici, il numero di iterazioni lineari e le informazioni di convergenza. Conserva il log e il fvSolution originale per il confronto.

4. Cambia un solo parametro

Su un altro clone dello stesso tutorial intatto, cambia solo la tolleranza relativa di una voce di solutore effettivamente presente. Mantieni identici mesh, schemi, modello fisico e controllo dell’esecuzione. Confronta il numero di iterazioni dei solutori e le cronologie globali dei residui.

Consegna
Confronto tra solutore di riferimento e solutore modificato e una conclusione motivata. Nessuna affermazione di accuratezza fisica migliorata.

Approfondimento · Il Laboratorio A passo per passo, con i comandi

Una possibile sequenza completa per i passi 3 e 4, con il percorso dei tutorial della guida v14 (adattalo a quello trovato con find). Il nome step06_relTol per il secondo clone è una scelta arbitraria.

# --- step 3: baseline
cd "$HOME/OpenFOAM/training_step06/step06_steady"
foamRun > log.foamRun 2>&1
grep "solution converged" log.foamRun            # iterations to residualControl, if reached
grep -c "Solving for p" log.foamRun              # number of pressure solves
grep "Solving for p" log.foamRun | awk '{s += $NF} END {print s}'   # total p iterations
foamDictionary -entry solvers/p/relTol -value system/fvSolution     # note the current value

# --- step 4: second pristine clone, one change only
test -e "$HOME/OpenFOAM/training_step06/step06_relTol" && echo "exists: choose another name"
cp -a "$FOAM_TUTORIALS/incompressibleFluid/pitzDailySteady" \
   "$HOME/OpenFOAM/training_step06/step06_relTol"
cd "$HOME/OpenFOAM/training_step06/step06_relTol"
foamDictionary -entry solvers/p/relTol -set 0.01 system/fvSolution  # choose a value different from the baseline
diff -u ../step06_steady/system/fvSolution system/fvSolution        # must show ONE changed line
blockMesh > log.blockMesh 2>&1
foamRun > log.foamRun 2>&1

Raccogli i risultati in una tabella con tre righe di confronto (iterazioni SIMPLE fino alla convergenza, iterazioni lineari totali di p, tempo di esecuzione finale ExecutionTime) e, separatamente, una grandezza fisica di controllo, per esempio la differenza di pressione tra ingresso e uscita. Una conclusione motivata ha la forma: «con relTol più stretto ogni soluzione di p richiede in media N iterazioni in più, il numero di iterazioni SIMPLE cambia di M e la grandezza di controllo varia meno di X%: il costo aumenta senza un cambiamento apprezzabile della soluzione». Notare che nei casi stazionari un relTol più stretto può anche ridurre il numero di iterazioni esterne: è proprio il tipo di effetto che il confronto deve misurare, non presumere.

22. Debug guidato — Laboratorio B: diagnosticare errori di configurazione

Usa estratti volutamente difettosi per imparare a individuare gli errori prima di toccare l’esecuzione.

Esempio 1: dizionario del solutore incompleto

Trova la parola chiave mancante

p
{
    solver          GAMG;
    tolerance       1e-7;
    smoother        DICGaussSeidel;
}

Domanda: quale parametro di arresto richiesto dalla User Guide v14 manca in questa configurazione minima di p?

Esempio 2: solutore della matrice sbagliato

Indaga la compatibilità con la matrice

U
{
    solver          PCG;
    preconditioner  DIC;
    tolerance       1e-6;
    relTol          0;
}

Domanda: se la matrice di U assemblata è asimmetrica a causa della convezione, quale classe di diagnostica potrebbe comparire? Quale famiglia di metodi dovresti esaminare?

Metodo · Non tirare a indovinare da una sola riga di log
Leggi il primo errore significativo, individua il suo file e la sua parola chiave, controlla il contesto di matrice, campo e modello, e consulta la documentazione v14 prima di cambiare la configurazione.

Approfondimento · Leggere il primo errore significativo, e un errore che non si manifesta

Gli errori fatali di OpenFOAM hanno sempre la stessa struttura: una riga --> FOAM FATAL ERROR o FOAM FATAL IO ERROR, il messaggio, poi il file con le righe coinvolte e la funzione che ha generato l’errore. Per isolarlo da un log lungo:

grep -n "FOAM FATAL" log.foamRun
grep -n -A 15 "FOAM FATAL" log.foamRun | head -40

L’Esempio 2 genera un errore fatale esplicito (sezione 10) alla prima soluzione di U, quindi è facile da diagnosticare. L’Esempio 1 è più insidioso: la User Guide v14 indica tolerance e relTol come richieste, ma a seconda dell’implementazione una voce omessa può non essere segnalata affatto e venire presa silenziosamente con un valore predefinito (per relTol lo zero, cioè nessun criterio relativo). In quel caso ogni soluzione di p andrebbe fino a tolerance e l’unico sintomo sarebbe un numero di iterazioni più alto del previsto nel log. Per questo la verifica corretta non è «il caso parte?», ma leggere il dizionario espanso con foamDictionary -expand -entry solvers/p system/fvSolution e confrontarlo con la documentazione.

23. Valutazione: esercizio autonomo e prova finale

A questo punto dovresti essere in grado di spiegare un fvSolution semplice e di apportare una modifica basata su evidenze.

Compiti autonomi

  • Individua un tutorial incompressibleFluid stazionario e uno transitorio nella distribuzione Foundation v14 installata.
  • Estrai i blocchi solvers; elenca ogni campo nominato e ogni metodo di soluzione.
  • Stabilisci se l’algoritmo usa SIMPLE o PIMPLE e quali sono i controlli di quell’algoritmo.
  • Spiega tutte le voci Final e i gruppi regex.
  • Annota gli eventuali valori di riferimento della pressione, i correttori non ortogonali e i fattori di rilassamento.
  • Descrivi una modifica di parametro, perché potrebbe influire sul lavoro del solutore lineare e come verificare empiricamente la previsione.
Approfondimento · Una traccia di lavoro per i compiti autonomi

I comandi seguenti svolgono la parte di raccolta dati senza modificare nulla. pitzDailySteady (stazionario) e pitzDaily (transitorio) sono le due coppie naturali, entrambe citate dalla guida v14; il percorso va confermato come nel Laboratorio A.

ls "$FOAM_TUTORIALS/incompressibleFluid"
for c in pitzDailySteady pitzDaily; do
    f="$FOAM_TUTORIALS/incompressibleFluid/$c/system/fvSolution"
    echo "== $c"
    foamDictionary -entry solvers -keywords "$f"
    foamDictionary -entry ddtSchemes/default -value "${f%fvSolution}fvSchemes"
    foamDictionary -entry SIMPLE "$f" 2>/dev/null || foamDictionary -entry PIMPLE "$f"
    foamDictionary -entry relaxationFactors "$f"
done

Per ogni tutorial compila una scheda con: elenco dei blocchi in solvers e, per ciascuno, solver, preconditioner/smoother, tolerance, relTol; quali nomi reali coprono le regex (confrontandoli con il modello in constant/momentumTransport); algoritmo e controlli (nCorrectors, nOuterCorrectors, nNonOrthogonalCorrectors, consistent, residualControl, pRefCell/pRefValue); fattori di rilassamento. Per l’ultimo compito usa la forma «modifica → livello interessato (sezione 16) → effetto previsto sulle iterazioni → come lo misuro nel log (sezione 7)».

Prove di apprendimento

DomandaRisposta minima accettabile
Perché fvSolution?Controlla le soluzioni algebriche e l’algoritmo di accoppiamento
Che cos’è GAMG?Un solutore multigrid di algebra lineare; non un modulo per fluidi
Che cosa fa relTol?Una soglia relativa di riduzione del residuo lineare
Che cos’è pFinal?Le impostazioni per una soluzione finale della pressione, quando l’algoritmo la usa
Che cos’è il rilassamento?Limita l’aggiornamento o la variazione dell’equazione per migliorare la robustezza
Che cosa rende valida un’esecuzione?Più dei residui bassi: anche fisica, conservazione e verifica

Condizione di superamento · Criterio di completamento
Sai ispezionare un fvSolution sconosciuto, identificarne i solutori di campo e i controlli di accoppiamento, spiegare l’effetto atteso di una modifica e poi convalidare quella previsione su una copia sicura.

24. Soluzioni degli esercizi di ragionamento

Prova a risolvere gli esercizi prima di leggere le soluzioni.

Problema del rapporto tra residui

Residuo iniziale = 0,002; relTol = 0,01 permette l’arresto quando il residuo scende sotto 0,00002. Nell’esempio, 0,000015 soddisfa il criterio relativo pur essendo sopra la tolleranza assoluta di 0,0000001. Impostare relTol = 0 elimina questa scorciatoia relativa.

Restringere relTol

Ridurre relTol da 0,1 a 0,01 richiede una diminuzione frazionaria maggiore prima che il criterio relativo sia soddisfatto; a parità di tutto il resto, ci si aspetta in generale più iterazioni lineari. Il numero effettivo dipende da quale criterio, quello assoluto o il tetto di iterazioni, viene raggiunto per primo.

Con il fattore di riduzione ρ ≈ 0,4 della sezione 6, il passaggio da 0,1 a 0,01 porta la stima da 3 a 6 iterazioni: una decade in più di riduzione costa circa ln 10 / |ln ρ| ≈ 2,5 iterazioni.

Raggruppamento dei campi con regex

"(U|k|epsilon)" non corrisponde a omega: l’espressione elenca tre alternative esatte e deve corrispondere all’intero nome. Uno schema riveduto possibile è "(U|k|epsilon|omega)" (e, coerentemente, "(U|k|epsilon|omega)Final"). Controlla quali campi esistono davvero prima di inserire la modifica.

Configurazione mancante

Nella voce p volutamente incompleta manca relTol. Secondo la descrizione dei solutori lineari della v14, tolerance e relTol vanno entrambe fornite.

Matrice asimmetrica

Un metodo solo simmetrico come PCG non può essere applicato a una matrice asimmetrica. Esamina metodi come PBiCGStab, PBiCG, smoothSolver o una configurazione GAMG appropriata, a seconda dell’equazione e del caso. Cambiando solutore va cambiato anche il precondizionatore: DIC è per matrici simmetriche, con PBiCGStab o PBiCG si usa DILU.

pFinal

pFinal è un sotto-dizionario di controllo del solutore, non un nuovo campo di pressione. Usare relTol = 0 può rendere più impegnativa la soluzione finale della pressione, perché punta alla tolleranza assoluta.

25. Riferimento rapido: scheda di configurazione

Una mappa compatta di che cosa cercare quando un caso OpenFOAM funzionante si comporta in modo inatteso.

Posizione / voceDomanda da porsi
system/fvSolution / solversQuale metodo lineare risolve l’equazione di ciascun campo?
toleranceQuale soglia assoluta di residuo ferma quella soluzione lineare?
relTolQuale riduzione relativa può fermarla prima?
maxIterIl solutore è stato fermato da un tetto di iterazioni?
preconditioner / smootherQuale accelerazione dell’iterazione è configurata?
pFinal / UFinalLe soluzioni finali sono configurate diversamente?
SIMPLE / PIMPLECome sono accoppiate pressione e velocità?
nCorrectorsQuante correzioni interne avvengono?
nOuterCorrectorsQuanti passaggi esterni possono avvenire a ogni passo temporale?
nNonOrthogonalCorrectorsSono richieste correzioni di pressione aggiuntive?
relaxationFactorsQuanto fortemente sono rilassati equazioni e campi?
pRefCell / pRefValueIn questo dominio chiuso serve un riferimento di pressione?

Da ricordare · Regola da conservare
fvSchemes determina le equazioni discrete; fvSolution determina come quelle equazioni vengono risolte e accoppiate. Un residuo algebrico più basso è un elemento di prova, non una validazione CFD completa.

Approfondimento · Sintomo → dove guardare: una tabella diagnostica
Sintomo nel logLivelloPrima verifica
No Iterations pari a maxIter3, lineareSolutore/precondizionatore adatti alla matrice? Qualità della mesh (checkMesh)?
Molte iterazioni per p a ogni soluzione3, linearePCG su mesh grande → GAMG; relTol molto piccolo nelle correzioni intermedie
Residui iniziali SIMPLE che oscillano o crescono2, accoppiamentoFattori di rilassamento, schemi convettivi (Parte 5), condizioni al contorno
Residui iniziali fermi su un valore costante2, accoppiamentoInstazionarietà fisica reale in un calcolo stazionario; soglie residualControl irraggiungibili
continuity errors che crescono nel tempo1–2pFinal con relTol 0? nCorrectors sufficienti? Courant (Parte 4)
FOAM FATAL IO ERROR su una parola chiaveconfigurazioneFile e voce indicati nel messaggio (sezione 22)

26. Riferimenti: documentazione ufficiale e passo successivo

Consulta la documentazione corrispondente all’implementazione e alla versione di OpenFOAM installate sul server del laboratorio. La documentazione delle release di OpenFOAM.com appartiene a una distribuzione separata (ESI) e non va sostituita senza verificarne la compatibilità.

Fonti ufficiali primarie

Sequenza di apprendimento suggerita

ParteTema
Parte 5Come discretizzare la PDE
Parte 6 (questa)Come risolvere e accoppiare le equazioni
Parte 7Proprietà fisiche e modelli

Dopo questa parte si passa alla Parte 7 — Proprietà fisiche e modelli. Lì si collega la configurazione di constant/ all’insieme di equazioni e di campi la cui soluzione è stata appena studiata.

Prossima unità · Parte 7 — Proprietà fisiche e modelli
Ora sai come le equazioni discrete vengono risolte e accoppiate. La parte successiva spiega quali equazioni e quali campi esistono in base alla fisica dichiarata in constant/: physicalProperties, momentumTransport e i modelli attivi.

Parte successiva: OpenFOAM 14, parte 7: proprietà fisiche e modelli. Parte precedente: OpenFOAM 14, parte 5: fvSchemes e discretizzazione. Torna all’indice del percorso Imparare OpenFOAM 14.

#CFD #fvSolution #OpenFOAM #solutori lineari