OpenFOAM: monitoraggio, diagnostica e verifica della convergenza

di giux CFD e OpenFOAM, in evidenza 23 min di lettura

Questa è la seconda guida operativa su OpenFOAM installato su un server Linux e usato da Windows. La prima guida spiega come trasferire i casi con FileZilla, entrare in SSH da PowerShell, lanciare i calcoli in tmux e collegare ParaView al server. Questa parte da un caso che già funziona e riguarda ciò che viene dopo il lancio: monitoraggio, diagnostica e verifica della convergenza, con OpenFOAM Foundation 14.

Monitoraggio, diagnostica e verifica della convergenza
Dal lancio del calcolo alla valutazione dei risultati — OpenFOAM Foundation 14
Edizione: 16 ottobre 2026 · Manuale operativo generale · Windows + server Linux

Scopo. Questa guida parte da un caso OpenFOAM già funzionante. Spiega come controllare il calcolo, distinguere i segnali di allarme, registrare grandezze fisiche durante l’esecuzione e produrre una verifica ripetibile. Si assume che il trasferimento tramite FileZilla e l’accesso SSH da PowerShell siano già noti. Il testo non richiede un IDE particolare: Notepad++ è sufficiente.

Percorso di lettura. I capitoli 1–3 costituiscono il controllo minimo di ogni simulazione; 4–6 aggiungono il monitoraggio quantitativo; 7 è un’esercitazione; 8 e gli allegati sono materiale di riferimento. I riquadri Richiamo sono opzionali per chi conosce già la fluidodinamica.

1. Che cosa significa «simulazione riuscita»?

Una simulazione che termina senza messaggi di errore non è automaticamente affidabile. Separare almeno quattro domande:

LivelloDomandaEvidenza minima
EsecuzioneIl programma è terminato correttamente?Codice di uscita, log completo, assenza di errori fatali
NumericaLe equazioni si stanno risolvendo stabilmente?Residui, continuità, Courant, assenza di oscillazioni incontrollate
FisicaLa soluzione rispetta i vincoli del problema?Bilanci, ordini di grandezza, condizioni al contorno, Δp, Q
CredibilitàIl risultato dipende troppo dalla discretizzazione?Sensibilità a mesh, passo temporale, schemi e modello

Il controllo dei residui riguarda soprattutto la seconda riga. Per accettare una simulazione servono anche controlli delle grandezze fisiche e, in una successiva fase, uno studio di sensibilità numerica.

Richiamo facoltativo — residui e accuratezza. Il residuo misura quanto bene viene soddisfatto il sistema algebrico discretizzato nella particolare iterazione: un residuo piccolo non dimostra che la discretizzazione rappresenti accuratamente le equazioni continue. Una simulazione può convergere verso una soluzione coerente con una mesh troppo grossolana.

Quattro verifiche indipendenti
Quattro verifiche indipendenti
Approfondimento · Verifica e validazione: due domande diverse

Nella letteratura di riferimento sulla credibilità della CFD (per esempio le linee guida AIAA G-077 e i lavori di Roache) si distinguono due attività:

  • Verifica: «sto risolvendo correttamente le equazioni?». Riguarda il codice e la soluzione numerica: convergenza iterativa (residui, bilanci), convergenza rispetto alla mesh e al passo temporale, stima dell’errore di discretizzazione.
  • Validazione: «sto risolvendo le equazioni giuste?». Confronta il risultato con la realtà fisica (esperimenti, soluzioni esatte di problemi rappresentativi) e mette alla prova il modello: laminare o turbolento, condizioni al contorno, proprietà del fluido.

Le righe «Numerica» e «Credibilità» della tabella sono verifica; la riga «Fisica» fa da ponte verso la validazione. Questa guida copre la convergenza iterativa e i bilanci. La convergenza rispetto alla mesh è il passo successivo: nel caso 02 del percorso OpenFOAM 14 è svolta con il confronto con la soluzione di Poiseuille.

2. Prima del lancio: la checklist essenziale

Lavora su una copia del caso, non sul caso di riferimento o su una simulazione in corso. Conserva i file 0/, constant/, system/ e annota quale variante stai usando. Da PowerShell:

ssh UTENTE@SERVER

Sul server, dopo avere caricato l’ambiente OpenFOAM nella shell:

foamVersion
cd ~/cases/mioCaso
pwd
ls -la
grep -E '^[[:space:]]*(solver|startFrom|endTime|deltaT|adjustTimeStep|writeControl|writeInterval)' system/controlDict
checkMesh > "log.checkMesh_$(date +%Y%m%d_%H%M%S)" 2>&1
tail -n 25 log.checkMesh_*

L’ultimo comando è comodo solo se nella cartella non vi sono molti log storici; in alternativa annota il nome del log creato e apri quello. Controlla Mesh OK e soprattutto eventuali avvisi su skewness, non-ortogonalità, volumi negativi, patch inattese e celle fortemente distorte. Mesh OK non significa «mesh abbastanza fine».

Per i casi incomprimibili OpenFOAM Foundation 14, il solver modulare è generalmente foamRun, con solver incompressibleFluid; in system/controlDict. Non usare meccanicamente ricette per vecchi solver senza verificare la versione e il caso.

Prima dell’avvio verifica la geometria e le unità SI, il nome dei patch in constant/polyMesh/boundary, la coerenza dei campi in 0/, la configurazione di fvSchemes/fvSolution, lo spazio libero sul disco e i permessi di scrittura. Ricontrolla le dimensioni del campo p: nei casi incomprimibili standard è spesso pressione cinematica, non Pa.

Approfondimento · Leggere checkMesh: le soglie che fanno scattare gli avvisi

Le righe che iniziano con *** sono gli avvisi. Le principali, con le soglie predefinite di checkMesh:

ControlloAvviso quandoChe cosa comporta
Non ortogonalitàangolo oltre 70° («severely non-orthogonal»)il gradiente normale alle facce richiede correzioni; servono nNonOrthogonalCorrectors e schemi limited
Skewnessvalore massimo oltre 4l’interpolazione al centro faccia perde accuratezza
Rapporto d’aspettooltre 1000possibili difficoltà dei solutori lineari
Volumi e determinantivolumi negativi o nulli, determinante bassomesh non utilizzabile: va corretta

Per un controllo più severo prima di un calcolo lungo:

checkMesh -allGeometry -allTopology > log.checkMesh_full 2>&1
grep -n '\*\*\*' log.checkMesh_full

Il significato di non ortogonalità e skewness per gli schemi è sviluppato in Parte 5: fvSchemes e per i correttori in Parte 6: fvSolution.

Approfondimento · Controlli rapidi prima del lancio da riga di comando
foamDictionary -entry solver system/controlDict          # modulo di solver
foamDictionary -entry dimensions 0/p                      # [0 2 -2 0 0 0 0] = pressione cinematica
grep -E '^[[:space:]]+[A-Za-z]' constant/polyMesh/boundary | head -n 20   # nomi dei patch
df -h .                                                   # spazio libero
touch .scrittura_ok && rm .scrittura_ok && echo "scrittura OK"   # permessi

Annotare l’esito di questi comandi nella scheda di verifica della sezione 7 rende confrontabili le varianti successive.

3. Leggere il log mentre la simulazione lavora

Esegui nella directory del caso in una sessione persistente (tmux se disponibile), salvando un nuovo log identificabile:

LOG="log.foamRun_$(date +%Y%m%d_%H%M%S)"
echo "Log: $LOG"
foamRun > "$LOG" 2>&1
printf 'Exit code: %s\n' "$?"

Per osservare un calcolo già avviato, usa un’altra sessione SSH:

tail -n 50 -f log.foamRun_20261016_153000

Sostituisci il nome con quello reale. Ctrl+C interrompe solo tail, non il solver avviato altrove. Se avvii il solver direttamente in foreground nella stessa shell, Ctrl+C interrompe invece il solver: non confondere le due situazioni.

Un estratto tipico, inventato a scopo didattico, può apparire così:

Time = 120
smoothSolver: Solving for Ux, Initial residual = 0.00038, Final residual = 4.2e-07, No Iterations 3
GAMG: Solving for p, Initial residual = 0.0016, Final residual = 8.1e-06, No Iterations 5
time step continuity errors : sum local = 2.3e-07, global = -1.1e-09, cumulative = 7.0e-06
ExecutionTime = 54.12 s  ClockTime = 55 s
Voce nel logInterpretazione correttaSegnale da investigare
Time = ...Tempo simulato o contatore pseudo-temporale, secondo il casoNon interpretarlo automaticamente come secondi reali
Initial residualResiduo prima della soluzione del sistema linearePlateau alto, crescita o oscillazioni persistenti
Final residualResiduo alla fine di quella soluzione lineareDa solo non prova la convergenza globale
No IterationsIterazioni del solutore lineare per il campoCrescita rapida o raggiungimento sistematico del limite
continuity errorsIndicatore della conservazione locale/globale nel passoDeriva cumulativa o errori globali persistenti
Courant NumberVincolo di trasporto temporale se riportatoPicchi non giustificati; soglia dipendente dal metodo
ExecutionTime/ClockTimeTempi CPU / tempo trascorsoSbilanciamento o rallentamenti anomali

Richiamo facoltativo — steady vs transient. In un caso stazionario il campo deve stabilizzarsi e gli osservabili tendere a valori costanti. In un problema transitorio fisicamente non stazionario possono persistere oscillazioni reali: occorre verificare convergenza delle iterazioni per passo, bilanci e indipendenza dal passo temporale, non pretendere residui monotonamente decrescenti su tutta la serie.

Ricerche rapide sul server (se presenti):

grep -nE 'FOAM FATAL|Floating point exception|nan|NaN|not converged|Courant Number|continuity errors' log.foamRun_20261016_153000 | tail -n 40
grep 'ExecutionTime' log.foamRun_20261016_153000 | tail -n 5

Su Windows puoi scaricare il log con FileZilla / SFTP e aprirlo con klogg o Notepad++; per cercare da PowerShell:

Select-String -Path 'C:\CFD\log\log.foamRun_20261016_153000' -Pattern 'FOAM FATAL','Courant Number','continuity errors'

Il log scaricato durante l’esecuzione è una fotografia parziale: scaricalo nuovamente se vuoi analizzare gli ultimi passi.

Approfondimento · Come OpenFOAM normalizza i residui

Per un sistema lineare A x = b, OpenFOAM non riporta la norma grezza di b − A x, che dipenderebbe dalle unità e dalla dimensione del problema, ma un residuo normalizzato:

r = Σ|b − A x| / ( Σ|A x − A x̄| + Σ|b − A x̄| + 10−20 )

dove x̄ è la media del campo. Il denominatore misura l’ampiezza delle variazioni del campo: per questo un residuo di 10−3 ha un significato simile per pressione e velocità. Due conseguenze pratiche: un campo quasi uniforme (denominatore piccolo) può mostrare residui alti anche con errori modesti; un buon «Initial residual» all’inizio di un passo transitorio dice che il campo cambia poco da un passo all’altro, non che la soluzione sia accurata.

Tolleranze, relTol e iterazioni sono trattate in Parte 6.

Approfondimento · Codice di uscita, tee e pipefail

Nel comando della guida l’uscita va direttamente nel file, per cui $? è il codice di uscita di foamRun: 0 se il solver è terminato normalmente, diverso da 0 in caso di errore fatale. Se invece si usa tee per vedere e salvare insieme, $? diventa quello di tee (quasi sempre 0) e l’errore resterebbe nascosto. Due rimedi in bash:

set -o pipefail
foamRun 2>&1 | tee "$LOG"
echo "Exit code: $?"

# oppure / or
foamRun 2>&1 | tee "$LOG"; echo "Exit code: ${PIPESTATUS[0]}"

In tmux conviene una finestra per il solver e una per tail -f e grep: Ctrl+B poi C crea la seconda finestra, Ctrl+B poi N passa dall’una all’altra.

4. Registrare residui e sonde con le function objects

OpenFOAM 14 fornisce la configurazione riutilizzabile delle function objects. È preferibile usare i file distribuiti con l’installazione invece di copiare ricette scritte per varianti o versioni diverse.

Dalla directory del caso di prova, esegui:

foamPostProcess -list | less
foamInfo residuals
foamGet residuals
foamGet probes

I comandi foamGet creano i corrispondenti file in system/ (se nella tua installazione sono già presenti, fai prima un backup e verifica cosa sta per essere sovrascritto). Modifica system/probes con Notepad++ e FileZilla, scegliendo coordinate interne al dominio e campi esistenti. Estratto indicativo:

fields (p U);
probeLocations
(
    (0.010 0.000 0.000)
    (0.040 0.000 0.000)
);

Le coordinate sono segnaposto illustrativi: verifica nel tuo dominio con ParaView prima di usarle. Le sonde probes leggono i valori della cella più vicina; per misure su sezioni e interpolazioni precise scegli altri metodi di campionamento.

Nel file system/functions (crealo se assente; se esiste integrane il contenuto preservando le voci) inserisci:

#includeFunc residuals
#includeFunc probes

A seconda della configurazione i risultati appariranno, per esempio, in:

postProcessing/
  residuals/0/residuals.dat
  probes/0/p
  probes/0/U

La sottocartella 0 è solo un esempio: può cambiare al riavvio o con un diverso startTime. Controlla con find postProcessing -maxdepth 3 -type f | sort.

Se il server dispone di un ambiente grafico e di gnuplot, puoi anche seguire i residui sul server con:

foamMonitor -l postProcessing/residuals/0/residuals.dat

Per il lavoro prevalentemente da Windows non serve eseguire foamMonitor: scarica periodicamente quel file con FileZilla e rappresentalo con Calc, Excel o Python. foamMonitor è un’opzione, non una dipendenza obbligatoria.

Per verificare senza aspettare il prossimo run puoi consultare la configurazione o usare il post-processing sui tempi già salvati, in funzione del tipo di object:

foamPostProcess -help
foamPostProcess -list

Non tutte le function objects possono ricostruire ogni grandezza a posteriori: se una grandezza dipende da valori non salvati, configurala prima del calcolo.

Esempio didattico di serie di residui
Esempio didattico di serie di residui
Approfondimento · Formato dei file e uno script Python per i residui

I file di postProcessing sono testo con righe di commento che iniziano con #. residuals.dat ha un’intestazione con i nomi delle colonne (il tempo e un residuo iniziale per campo, con eventuali N/A quando un campo non è risolto in quel passo). I file delle sonde hanno una riga di commento per sonda con le coordinate; nel file U ogni valore è un vettore tra parentesi (Ux Uy Uz).

Uno script che legge l’intestazione reale invece di presupporre le colonne (richiede Python con matplotlib su Windows):

# plot_residuals.py  -  python plot_residuals.py residuals.dat
import sys
import matplotlib.pyplot as plt

names, rows = None, []
for line in open(sys.argv[1], encoding='utf-8', errors='replace'):
    s = line.strip()
    if not s:
        continue
    if s.startswith('#'):
        tok = s.lstrip('#').split()
        if tok and tok[0] == 'Time':
            names = tok                    # ultima riga di intestazione con i nomi delle colonne
        continue
    rows.append(s.split())

for j, name in enumerate(names[1:], start=1):
    t, r = [], []
    for row in rows:
        if j < len(row) and row[j] not in ('N/A', 'nan'):
            t.append(float(row[0])); r.append(float(row[j]))
    if r:
        plt.semilogy(t, r, label=name)
plt.xlabel('Time'); plt.ylabel('Initial residual'); plt.grid(True, which='both', alpha=0.3)
plt.legend(); plt.tight_layout(); plt.savefig('residuals.png', dpi=150)
print('grafico salvato in residuals.png')

Per il file U delle sonde, prima di importarlo, si possono togliere le parentesi sul server con sed 's/[()]//g' postProcessing/probes/0/U > U_probes.txt: ogni riga diventa tempo seguito da tre componenti per sonda.

Approfondimento · Dove si trovano le configurazioni distribuite

foamGet copia nel caso un file preso da $FOAM_ETC/caseDicts/functions. Prima di usarlo conviene leggerne i parametri con foamInfo e vedere che cosa contiene la copia:

ls $FOAM_ETC/caseDicts/functions
foamInfo probes
cat system/probes

Oltre a residuals e probes la stessa cartella contiene configurazioni pronte per medie e flussi sui patch, campionamenti lungo linee, forze e altro. La pagina 7.4 Sampling and monitoring data della User Guide v14 descrive le principali.

5. Misurare Δp e portata, non soltanto i residui

Per un problema di flusso in un canale, lo studente o l’operatore può verificare mentre la simulazione procede la pressione media su inlet/outlet e la portata attraverso gli stessi patch. Un metodo generale di OpenFOAM 14 è surfaceFieldValue.

Aggiungi in system/functions, oltre alle due direttive precedenti, questo esempio per patch effettivamente chiamati inlet e outlet:

pInlet
{
    type            surfaceFieldValue;
    libs            ("libfieldFunctionObjects.so");
    patch           inlet;
    operation       areaAverage;
    writeFields     false;
    fields          (p);
    writeControl    timeStep;
    writeInterval   1;
}

pOutlet
{
    type            surfaceFieldValue;
    libs            ("libfieldFunctionObjects.so");
    patch           outlet;
    operation       areaAverage;
    writeFields     false;
    fields          (p);
    writeControl    timeStep;
    writeInterval   1;
}

qInlet
{
    type            surfaceFieldValue;
    libs            ("libfieldFunctionObjects.so");
    patch           inlet;
    operation       sum;
    writeFields     false;
    fields          (phi);
    writeControl    timeStep;
    writeInterval   1;
}

qOutlet
{
    type            surfaceFieldValue;
    libs            ("libfieldFunctionObjects.so");
    patch           outlet;
    operation       sum;
    writeFields     false;
    fields          (phi);
    writeControl    timeStep;
    writeInterval   1;
}

Il patch deve esistere; p e phi devono essere disponibili nel caso. Le quattro serie saranno sotto postProcessing/pInlet/..., pOutlet/..., qInlet/..., qOutlet/...; i nomi dei file e l’intestazione vanno controllati nel risultato effettivo. Questa sintassi fa riferimento a OpenFOAM Foundation v14, non all’omonima implementazione di altre distribuzioni che usa talvolta regionType/name.

Calcoli da effettuare dopo l’esportazione:

  • Caduta media di pressione: Δp = p_media,in − p_media,out.
  • Per un incomprimibile standard con p cinematica [0 2 -2 0 0 0 0]: ΔP [Pa] = ρ [kg/m³] × Δp [m²/s²], a densità costante.
  • Con p già in Pa, non moltiplicare per la densità.
  • Con phi volumetrica: Q_in è tipicamente negativo perché la normale del patch punta verso l’esterno, Q_out positivo. Con phi massica, il risultato è in kg/s; verifica le dimensioni.
  • Per un ingresso e un’uscita, errore relativo di bilancio: ε_Q = |Q_in + Q_out| / ((|Q_in|+|Q_out|)/2) × 100%. Con più patch di flusso, includere tutte le sezioni aperte.

Esempio puramente illustrativo: p_in − p_out = 6.2 m²/s², ρ = 1000 kg/m³ → ΔP = 6200 Pa; Q_in = −2.500×10⁻⁴ m³/s, Q_out = +2.495×10⁻⁴ m³/s → ε_Q ≈ 0.20%. Questi numeri non sono soglie di accettazione.

Richiamo facoltativo — quale pressione? Per un confronto con un manometro, definisci sezioni confrontabili e chiarisci se stai osservando pressione statica, differenza di pressione idraulica, carico totale o perdite localizzate. Una media di pressione sulle superfici d’ingresso e uscita non equivale sempre alla perdita irreversibile di pressione: possono contare accelerazione, profili non uniformi e quote gravitazionali.

Due osservabili con andamenti differenti
Due osservabili con andamenti differenti
Approfondimento · Un controllo di ordine di grandezza sul canale del percorso

Nel canale trainingChannel della Parte 3 (sezione 10 × 5 mm, velocità d’ingresso uniforme 0,05 m/s) la portata attesa è Q = U · A = 0,05 × 5,0×10⁻⁵ = 2,5×10⁻⁶ m³/s. Il valore di qInlet deve quindi essere vicino a −2,5×10⁻⁶ fin dal primo passo, perché è imposto dalla condizione al contorno, mentre qOutlet tende a +2,5×10⁻⁶ man mano che il campo si assesta.

Per la caduta di pressione tra due lastre piane parallele a distanza H in regime laminare sviluppato vale Δp_cin = 12 ν L U_media / H² (pressione cinematica): è la relazione usata nel caso 01 per la validazione con Poiseuille. In un condotto rettangolare il coefficiente cambia con il rapporto dei lati, ma l’ordine di grandezza resta un controllo utile prima di analizzare i decimali.

Approfondimento · Alternative pronte: patchAverage e patchFlowRate

Tra le configurazioni distribuite con OpenFOAM 14 ci sono funzioni già impostate per medie e portate sui patch, richiamabili con argomenti in una riga di system/functions. Prima di usarle leggi i parametri accettati dalla tua installazione:

foamInfo patchAverage
foamInfo patchFlowRate

Con i parametri confermati da foamInfo, la forma è del tipo:

#includeFunc patchAverage(patch=inlet, fields=(p))
#includeFunc patchAverage(patch=outlet, fields=(p))
#includeFunc patchFlowRate(patch=inlet)
#includeFunc patchFlowRate(patch=outlet)

Il risultato è equivalente ai quattro blocchi espliciti; i blocchi della guida restano preferibili quando si vuole vedere e controllare ogni opzione (operation, writeControl, writeInterval).

Arresto automatico e sua interpretazione

In alcuni casi stazionari la sezione SIMPLE/residualControl di system/fvSolution definisce criteri per fermare il calcolo automaticamente. Controlla che cosa è configurato:

grep -n -A 14 'residualControl' system/fvSolution

Un messaggio di soluzione convergente indica che le condizioni d’arresto impostate dal caso sono state soddisfatte: non sostituisce i controlli di portata, pressione e discretizzazione. Nei casi transitori la logica di controllo e il significato delle iterazioni cambiano.

Approfondimento · Com’è fatto residualControl
SIMPLE
{
    nNonOrthogonalCorrectors 0;

    residualControl
    {
        p               1e-2;
        U               1e-3;
        "(k|epsilon|omega)" 1e-3;
    }
}

Ogni valore è la soglia sull’Initial residual del campo: quando tutti i campi elencati scendono sotto la propria soglia nella stessa iterazione, il solver scrive l’ultimo tempo ed esce con un messaggio del tipo SIMPLE solution converged in ... iterations. Le soglie non hanno nulla di universale: con soglie larghe il calcolo può fermarsi mentre Δp sta ancora cambiando. Per questo la guida chiede di confrontare la fermata con gli osservabili della figura 3. Gli algoritmi SIMPLE e PIMPLE sono spiegati in Parte 6.

6. Tre segnali tipici e come reagire

Caso osservatoDiagnosi possibilePrima azione controllata
Residui diminuiscono, ma Δp deriva ancoraConvergenza numerica locale, osservabile globale non stabilizzatoContinua il monitoraggio; verifica intervallo e stabilità del Δp
Residui e continuità crescono, picchi di CourantInstabilità numerica, condizioni iniziali o BC inadatte, Δt eccessivoRivedi BC, mesh, passo temporale; confronta con variante precedente
Residui bassi e Δp stabile, ma bilancio scorrettoPatch mancanti, flussi errati, problemi di setup o calcoloElenca tutti i patch aperti; confronta Σphi e sorgenti volumetriche
Avviso mesh severo già in checkMeshCausa geometrica/discretizzazioneNon compensare a caso con tolleranze: correggi e ricontrolla la mesh
FOAM FATAL ERROR o Floating point exceptionInterruzione anomala, non risultato validoConserva log e configurazione; identifica il primo messaggio causale

La presenza di oscillazioni non identifica automaticamente un errore: possono essere fisiche in flussi transitori. La risposta corretta dipende dal modello e dagli obiettivi. Non cambiare contemporaneamente mesh, schemi, rilassamento e condizioni al contorno: perdi la capacità di identificare la causa.

Politica operativa per il debug: conserva una baseline, modifica un fattore alla volta, salva un log nuovo e confronta un insieme costante di indicatori.

Approfondimento · Trovare il primo messaggio causale e confrontare due varianti

In un log che termina con errore, l’ultimo messaggio è spesso una conseguenza. Si cerca la prima occorrenza e si leggono le righe che la precedono:

grep -n -m1 -E 'FOAM FATAL|Floating point exception|nan' log.foamRun_20261016_153000
sed -n '1180,1240p' log.foamRun_20261016_153000      # intervallo attorno alla riga trovata / range around the line found

Con due copie del caso (baseline e variante) la differenza dei dizionari dice esattamente che cosa è stato cambiato:

diff -ru mioCaso_v01/system mioCaso_v02/system
diff -ru mioCaso_v01/constant/momentumTransport mioCaso_v02/constant/momentumTransport
diff -ru mioCaso_v01/0 mioCaso_v02/0

Se il diff mostra più di un fattore cambiato, il confronto tra i due log non permette di attribuire la causa.

7. Esercitazione guidata: una verifica riproducibile

L’esercitazione usa un qualsiasi caso già testato di flusso incomprimibile con due patch inlet/outlet; se i nomi differiscono, adattali. Non è richiesto ricostruire una geometria per usare questa guida.

  1. Duplica il caso in una nuova cartella, ad esempio mioCaso_monitor_v01; non cancellare i dati della versione precedente.
  2. Esegui checkMesh e conserva il suo log con data/ora.
  3. Genera system/residuals e system/probes con foamGet, controllando gli eventuali file già presenti.
  4. Crea o integra system/functions; aggiungi i monitor di pressione/portata della sezione 5 dopo aver verificato i nomi dei patch.
  5. Avvia foamRun in tmux o altra sessione persistente e registra il nome esatto del log.
  6. In un’altra sessione osserva tail -f ...; cerca messaggi di errore, continuità, eventuale Courant e tempi di calcolo.
  7. Con FileZilla scarica solo log.* e postProcessing/: per grafici di convergenza normalmente non servono tutti i volumi dei campi CFD.
  8. Su Windows apri il log con klogg; importa le serie .dat, p, U in LibreOffice Calc, Excel o Python, rispettando intestazioni e commenti.
  9. Realizza almeno tre grafici: residui (asse logaritmico se utile), Δp o ΔP contro iterazione/tempo, errore di bilancio ε_Q contro iterazione/tempo.
  10. Riporta intervallo finale analizzato, numero di campioni e la variabilità di Δp nell’intervallo: per esempio max−min relativo alla media (se la media è non nulla). Indica se è un run steady o transient.

Piccola trappola didattica. Se il grafico dei residui suggerisce un plateau, ma Δp cambia del 5% nell’ultimo intervallo, il calcolo non è ancora dimostrato stabile per l’osservabile che interessa. Se i residui diminuiscono di altri due ordini di grandezza ma Δp non cambia, approfondisci piuttosto il bilancio e la sensibilità alla discretizzazione. Il 5% è un esempio, non una soglia universale.

Approfondimento · Uno script per Δp, ε_Q e la finestra di stabilità

Lo script legge le quattro serie scaricate con FileZilla (la cartella postProcessing copiata su Windows), calcola Δp e ε_Q passo per passo e riassume le ultime N righe: media, variazione relativa (max−min)/|media| e pendenza. Usa solo la libreria standard di Python.

# monitor_report.py  -  python monitor_report.py <cartella postProcessing scaricata> [N]
import sys, re, pathlib, statistics as st

def read_series(path):
    # restituisce (tempi, valori) leggendo la prima colonna dopo il tempo; ignora i commenti '#'
    t, v = [], []
    for line in open(path, encoding='utf-8', errors='replace'):
        if not line.strip() or line.lstrip().startswith('#'):
            continue
        tok = line.split()
        try:
            t.append(float(tok[0])); v.append(float(tok[1]))
        except (ValueError, IndexError):
            pass
    return t, v

def find(root, name):
    files = sorted(pathlib.Path(root, name).rglob('*.dat'))
    if not files:
        sys.exit('nessun file .dat in ' + str(pathlib.Path(root, name)))
    return files[-1]          # l'ultima sottocartella di partenza (restart)

root = sys.argv[1]
N = int(sys.argv[2]) if len(sys.argv) > 2 else 100
series = {k: read_series(find(root, k)) for k in ('pInlet', 'pOutlet', 'qInlet', 'qOutlet')}
n = min(len(s[1]) for s in series.values())
pin, pout = series['pInlet'][1][:n], series['pOutlet'][1][:n]
qin, qout = series['qInlet'][1][:n], series['qOutlet'][1][:n]
t = series['pInlet'][0][:n]
dp = [a - b for a, b in zip(pin, pout)]
eps = [abs(a + b) / ((abs(a) + abs(b)) / 2) * 100 for a, b in zip(qin, qout)]
w = slice(max(0, n - N), n)
dpw, epw, tw = dp[w], eps[w], t[w]
m = st.mean(dpw)
slope = (dpw[-1] - dpw[0]) / (tw[-1] - tw[0]) if tw[-1] != tw[0] else 0.0
print(f'campioni totali {n}, finestra finale {len(dpw)} campioni, da t={tw[0]} a t={tw[-1]}')
print(f'dp medio {m:.6g}   (max-min)/|media| = {(max(dpw)-min(dpw))/abs(m)*100:.3g} %   pendenza {slope:.3g} per unita di tempo')
print(f'eps_Q medio {st.mean(epw):.3g} %   massimo {max(epw):.3g} %   Q_in {st.mean(qin[w]):.6g}   Q_out {st.mean(qout[w]):.6g}')
with open('monitor_report.csv', 'w') as f:
    f.write('time,dp,eps_Q_percent\n')
    for a, b, e in zip(t, dp, eps):
        f.write(f'{a},{b},{e}\n')
print('serie completa scritta in monitor_report.csv')

Uso: python monitor_report.py C:\CFD\log\mioCaso\postProcessing 200. La pendenza, moltiplicata per la durata della finestra, dice di quanto Δp sta ancora cambiando: se è dello stesso ordine della variazione ammessa, la finestra non è ancora stabile. Il file monitor_report.csv si apre direttamente in Excel o Calc per i grafici.

Scheda di verifica da allegare a ogni simulazione

Parametro o controlloValore da registrare
Identificativo caso e revisione dei dizionariNome, data, cartella, note
Versione OpenFOAM e modellofoamVersion, solver, regime fisico
MeshCelle, risultati checkMesh, modifiche
Discretizzazione temporale e numericadeltaT/maxCo, fvSchemes, fvSolution
Stato di esecuzioneConcluso/interrotto, exit code, nome del log
Diagnostica numericaResidui, continuità, Courant (se pertinente)
Grandezze fisicheΔp / ΔP, Q_in, Q_out, ε_Q e unità
StabilizzazioneFinestra scelta, media e variazione degli osservabili
Validazione successivaConfronti con teoria/esperimento e mesh/time-step study

8. Errori comuni nell’analisi da Windows

Errore: un .dat è vuoto o contiene soltanto un’intestazione. Il run può non avere ancora scritto un passo valido; controlla writeControl/writeInterval, eventuali messaggi nel log e la sottocartella di postProcessing corrispondente alla partenza del run.

Errore: FileZilla mostra dati vecchi. Il file locale è una copia statica: fai un nuovo download e verifica la dimensione e l’ora di modifica. Non lavorare su una simulazione remota pensando che l’editor locale la aggiorni automaticamente.

Errore: grafici insensati importati in Excel. I file hanno spesso commenti #, separatori di spazi e colonne vettoriali del tipo (Ux Uy Uz). Scegli l’importazione come testo delimitato con opzioni adeguate, oppure separa e normalizza le componenti. Non trattare il file U automaticamente come una semplice tabella di quattro colonne.

Errore: p è interpretata sempre come Pa. Apri il file del campo, per esempio 0/p, e guarda la voce dimensions: [0 2 -2 0 0 0 0] indica pressione cinematica, [1 -1 -2 0 0 0 0] indica pressione dinamica in Pa.

Errore: epsilon_Q è interpretato come criterio sufficiente. Un buon bilancio globale non esclude fenomeni locali non risolti, errori sulle pareti, cattiva distribuzione di velocità o mesh troppo grossolana.

Errore: si usa una soglia unica per ogni modello. Residui, Courant e stabilizzazione vanno riferiti agli obiettivi della simulazione e al regime fisico; documenta i criteri prima di interpretarli.

Approfondimento · Importare in Excel con le impostazioni italiane

OpenFOAM scrive i numeri con il punto decimale (2.5e-06). Un Excel impostato in italiano usa la virgola: se si apre il file con doppio clic, molti valori diventano testo o date. La procedura corretta:

  1. Dati → Da testo/CSV, scegliere il file;
  2. origine del file: UTF-8; delimitatore: Spazio, con «considera delimitatori consecutivi come uno solo»;
  3. in Trasforma dati (Power Query) eliminare le righe che iniziano con #;
  4. cambiare il tipo delle colonne con Usando impostazioni locali → Inglese (Stati Uniti), così il punto è letto come separatore decimale.

In LibreOffice Calc: Lingua → Inglese (USA) nella finestra di importazione testo, separatore spazio, «Unisci i delimitatori». Lo script monitor_report.py della sezione 7 evita il problema producendo un CSV con virgole come separatori di campo e punti come separatori decimali, che conviene importare con la stessa impostazione.

9. Checklist conclusiva e riferimenti

Prima di dichiarare affidabile un risultato: verifica che non vi siano errori fatali, che la mesh sia utilizzabile, che i campi e le BC abbiano unità coerenti, che i residui e gli indicatori numerici siano appropriati, che i bilanci siano soddisfacenti, che le grandezze di interesse siano stabili nel senso fisicamente corretto e che siano stati considerati gli effetti della discretizzazione.

Fonti tecniche primarie (OpenFOAM Foundation 14):

  1. OpenFOAM v14 — User Guide — struttura generale, solutori modulari e tutorial.
  2. 7.4 — Sampling and monitoring data — foamGet, probes, residuals, foamMonitor.
  3. 2.1 — Backward-facing step — esecuzione, log e criteri di arresto nel caso stazionario.
  4. API v14 — surfaceFieldValue — sintassi patch, operazioni sum e areaAverage.
  5. API v14 — Function objects — system/functions e controllo esecuzione/scrittura.
  6. 7.3 — Post-processing functionality — catalogo e riferimenti alle quantità dimensionali.
  7. klogg — software facoltativo per consultare grandi log da Windows.

I grafici di questa guida sono schemi/dati sintetici a scopo didattico, non risultati CFD misurati. I frammenti di dizionario richiedono l’adattamento ai nomi, ai campi e alla geometria del proprio caso.

Altri riferimenti utili

  • klogg — visualizzatore facoltativo per consultare grandi log da Windows.

Guide collegate: OpenFOAM su server Linux, lavorando da Windows (trasferimenti, SSH, tmux, ParaView) e il percorso Imparare OpenFOAM 14, in particolare controlDict e fvSolution.

#CFD #convergenza #functionObjects #monitoraggio #OpenFOAM