La barrera de entrada de OpenFOAM (y por qué merece la pena)
OpenFOAM intimida. No tiene interfaz gráfica, todo se configura con archivos de texto, y la documentación tradicional asume que ya dominas la mecánica de fluidos. Pero esa curva de aprendizaje esconde la herramienta de simulación más potente del software libre. En este tutorial vamos a recorrer juntos el caso más clásico de la dinámica de fluidos computacional: la cavidad cúbica con tapa móvil (lid-driven cavity). Al terminarlo, habrás ejecutado tu primera simulación CFD y tendrás un mapa mental claro de cómo funciona OpenFOAM por dentro. Sin magia negra, sin atajos — solo física y código.
Lo que necesitas
Para seguir este tutorial necesitas tres cosas: una instalación de OpenFOAM (vale tanto la versión de OpenCFD como la de la Fundación), ParaView para visualizar los resultados, y una terminal. Si estás en Windows, la forma más sencilla es instalar WSL2 y OpenFOAM a través de Docker o del repositorio de Ubuntu. En Linux y macOS la instalación es directa con el gestor de paquetes. No necesitas experiencia previa con CFD — este tutorial empieza desde cero.
Paso 1: La estructura de directorios
Los tres bloques de un caso OpenFOAM
Cada simulación en OpenFOAM vive dentro de una carpeta con tres subdirectorios obligatorios que reflejan la arquitectura del solver. El directorio 0/ contiene las condiciones iniciales y de contorno para cada variable del flujo: velocidad, presión, y las variables turbulentas si las hubiera. El directorio constant/ guarda las propiedades físicas (viscosidad, modelo de turbulencia) y, opcionalmente, la geometría en constant/polyMesh/. El directorio system/ contiene los archivos que gobiernan la simulación: esquemas numéricos, solvers lineales, y parámetros de ejecución.
Puedes copiar la estructura base desde el tutorial que viene con OpenFOAM. El caso cavity está en $FOAM_TUTORIALS/incompressible/simpleFoam/cavity/. Navega hasta tu directorio de trabajo, copia la carpeta, y renómbrala. Ya tienes el esqueleto listo para modificar.
Paso 2: blockMesh y la malla
Definiendo la geometría en blockMeshDict
El archivo system/blockMeshDict define la malla computacional de forma paramétrica. Para nuestra cavidad de 1 m × 1 m en 2D, definimos 8 vértices (los de un cubo), un bloque hexaédrico, y cuatro parches con nombre: movingWall (la tapa superior), fixedWalls (las otras tres paredes), y frontAndBack (las caras frontal y trasera declaradas como empty para indicar simulación 2D).
/* Fragmento de system/blockMeshDict — cavidad 2D */
vertices
(
(0 0 0) (1 0 0) (1 1 0) (0 1 0)
(0 0 1) (1 0 1) (1 1 1) (0 1 1)
);
blocks
(
hex (0 1 2 3 4 5 6 7) (20 20 1) simpleGrading (1 1 1)
);
boundary
(
movingWall { type wall; faces ( (3 7 6 2) ); }
fixedWalls { type wall; faces ( (0 4 7 3) (2 6 5 1) (1 5 4 0) ); }
frontAndBack { type empty; faces ( (0 3 2 1) (4 5 6 7) ); }
);
El parámetro (20 20 1) indica 20 celdas en X, 20 en Y, y 1 en Z (una sola celda de espesor para flujo 2D). simpleGrading (1 1 1) significa que la malla es uniforme en todas las direcciones, sin refinamiento hacia las paredes.
Ejecuta blockMesh en la terminal. Si todo sale bien, verás el mensaje "Mesh OK" y tendrás la carpeta constant/polyMesh/ poblada con los archivos points, faces, owner, neighbour y boundary. Ya tienes la malla lista.
Paso 3: Condiciones de contorno
Velocidad y presión en el directorio 0/
Para la cavidad con tapa móvil necesitamos que la pared superior se desplace a velocidad constante U = 1 m/s en la dirección X. El resto de paredes tienen velocidad cero (condición de no deslizamiento). La presión tiene gradiente cero en todas las paredes (condición de Neumann), lo que es físicamente correcto para flujo incompresible donde el gradiente normal de presión en una pared es nulo. Las caras frontales usan el tipo empty para forzar una simulación 2D.
/* Fragmento de 0/U — campo de velocidad */
dimensions [0 1 -1 0 0 0 0];
internalField uniform (0 0 0);
boundaryField
{
movingWall
{
type fixedValue;
value uniform (1 0 0);
}
fixedWalls
{
type noSlip;
}
frontAndBack
{
type empty;
}
}
En 0/p, la condición para todas las paredes es zeroGradient, y las caras frontAndBack llevan empty. Para el campo de presión no necesitas un valor fijo porque el solver lo calcula a partir de la ecuación de continuidad. Solo se necesita un punto de referencia para que el sistema lineal no sea singular, y OpenFOAM lo maneja internamente.
Paso 4: Propiedades físicas
En constant/transportProperties definimos la viscosidad cinemática nu del fluido. Para el caso laminar clásico a Re = 100 (con U = 1 m/s y L = 1 m), necesitamos nu = 0.01 m²/s. En constant/turbulenceProperties seleccionamos simulationType laminar — no necesitamos modelo de turbulencia para este primer caso. La simulación resolverá directamente las ecuaciones de Navier-Stokes sin promediado de Reynolds.
Paso 5: Archivos de control del sistema
controlDict, fvSchemes y fvSolution
system/controlDict define la duración de la simulación. Para Re = 100, el flujo alcanza el estado estacionario en pocas iteraciones. Configuramos application simpleFoam, startFrom startTime, startTime 0, stopAt endTime, endTime 0.5, deltaT 0.005, y writeInterval 10 (escribe resultados cada 10 pasos de tiempo).
/* Fragmento de system/controlDict */
application simpleFoam;
startFrom startTime;
startTime 0;
stopAt endTime;
endTime 0.5;
deltaT 0.005;
writeControl timeStep;
writeInterval 10;
purgeWrite 0;
writeFormat ascii;
writePrecision 6;
runTimeModifiable true;
system/fvSchemes define la discretización espacial. Empezamos con esquemas de primer orden por estabilidad: Euler para el término temporal, Gauss linear para el gradiente, Gauss linearUpwind para la divergencia. system/fvSolution configura los solvers lineales: GAMG para la presión con tolerancia 1e-6, y smoothSolver para la velocidad con tolerancia 1e-7. El acoplamiento presión-velocidad se maneja con el algoritmo SIMPLE (Semi-Implicit Method for Pressure Linked Equations).
Paso 6: Ejecutar simpleFoam y monitorizar residuales
Lanza simpleFoam desde la terminal dentro de la carpeta del caso. Verás la salida de los residuales en pantalla: Ux, Uy, y presión. Un residual por debajo de 1e-4 indica buena convergencia. Para este caso sencillo, la simulación completa tarda segundos. Si ves el mensaje "Solution converged in X iterations", lo has conseguido.
Los residuales miden el error de la solución en cada iteración. No necesitan llegar a cero — con que bajen tres o cuatro órdenes de magnitud desde el valor inicial, la solución es perfectamente usable para ingeniería. Si los residuales se estancan, revisa que las condiciones de contorno sean consistentes y que el deltaT no sea demasiado grande.
Paso 7: Visualizar en ParaView
Abre ParaView y carga el caso creando un archivo vacío con extensión .foam en la raíz del caso (ejecuta touch cavidad.foam). Abre ese archivo en ParaView y selecciona Apply para cargar los datos. Crea un slice en el plano Z = 0.5 con el filtro Slice. Añade vectores de velocidad con el filtro Glyph — verás el vórtice característico que ocupa el centro de la cavidad. Para visualizar la presión, usa el filtro Contour sobre el campo p.
El resultado clásico muestra un gran vórtice principal que ocupa la mayor parte de la cavidad, girando en sentido horario impulsado por la tapa móvil. Si tu malla es suficientemente fina, verás vórtices secundarios más pequeños en las esquinas inferiores — un sello distintivo de que la simulación está bien resuelta. Si tu imagen coincide con esta descripción, has ejecutado correctamente tu primera simulación CFD.
¿Y ahora qué? Próximos pasos
Este caso es solo la puerta de entrada. El siguiente paso natural es modificar el número de Reynolds: cambia nu a 0.001 en transportProperties para Re = 1000, activa un modelo de turbulencia como k-omega SST en turbulenceProperties, y refina la malla cerca de las paredes añadiendo simpleGrading con expansión. Después puedes explorar geometrías complejas con snappyHexMesh, el mallador de OpenFOAM que genera mallas a partir de archivos STL, o pasar a casos multifásicos con interFoam.
OpenFOAM se aprende caso a caso, y cada nuevo caso te enseña una pieza del sistema. La comunidad es enorme, el código está abierto, y las posibilidades son ilimitadas. Lo más difícil ya lo has hecho: arrancar. El resto es práctica.