Tutorial: mapeo de resultados OpenFOAM entre mallas diferentes =============================================================== Objetivo -------- Este procedimiento permite transferir una solución CFD ya calculada desde una malla fuente hacia una malla destino diferente que representa la misma geometría física. Caso validado: * Fuente: ``B068-P180_coldFlow_AP03C_16proc`` * Malla fuente: aproximadamente 1.33 millones de celdas. * Tiempo fuente: ``0.05 s``. * Destino: ``B068-P180_coldFlow_AP04_validation_cleanNCC_v01``. * Malla destino: aproximadamente 3.15 millones de celdas. * Solver: ``multicomponentFluid``. * OpenFOAM: ``dev-4d09840bec89``. * Interfaz periódica: ``nonConformalCyclic`` (NCC). El mapeo efectivo se realizó con ``mapFields`` en modo no consistente, es decir, sin la opción ``-consistent``. Principio fundamental --------------------- ``mapFields`` no requiere que la malla fuente y la malla destino tengan las mismas celdas. Puede utilizarse para transferir campos entre discretizaciones diferentes de un mismo dominio físico. Debe mantenerse coherencia en: * sistema de coordenadas; * escala geométrica; * regiones físicas; * condiciones de frontera; * campos requeridos por el solver; * topología necesaria para que cada malla pueda abrirse correctamente. En casos con interfaces NCC, la malla no debe considerarse únicamente como ``constant/polyMesh``. También puede existir información dependiente de la malla en ``constant/fvMesh``, particularmente ``polyFaces``. Regla práctica para NCC ----------------------- En una malla con ``nonConformalCyclic`` se debe tratar como una unidad coherente: :: constant/polyMesh/ constant/fvMesh/ y, en particular: :: constant/fvMesh/polyFaces No debe sustituirse únicamente ``constant/polyMesh`` conservando un ``constant/fvMesh/polyFaces`` heredado de otra malla. Preparación de la nueva malla ----------------------------- La malla destino debe existir primero como malla geométrica válida, antes de realizar el mapeo. Una malla básica contiene normalmente: :: constant/polyMesh/ boundary faces neighbour owner points Verifique la malla con: .. code-block:: bash checkMesh -constant En el caso AP04 validado: :: cells = 3153056 Number of regions = 1 Los defectos conocidos de ``checkMesh`` deben estar evaluados previamente. El mapeo no debe utilizarse como mecanismo para ocultar problemas geométricos o topológicos. Clasificación de los patches físicos ------------------------------------ Cuando la superficie de referencia contiene regiones con nombres definidos, se puede reconstruir la clasificación de los patches mediante ``surfaceToPatch``. Ejemplo: .. code-block:: bash surfaceToPatch \ -tol 0.001 \ ../B068-P180_snappy/constant/triSurface/B068-P180_named_patches.stl Después de ejecutar la utilidad, se deben verificar explícitamente los patches y sus números de caras. En AP04 se obtuvieron: :: AIR_INLET_PLENUM 49401 GAS_INLET_STAGE_1 136 GAS_INLET_STAGE_2 313 GAS_INLET_STAGE_3 526 OUTLET 36116 PERIODIC_0 73548 PERIODIC_180 73718 WALLS 530093 La salida debe indicar: :: unmatched faces = 0 Si aparecen caras no clasificadas de forma inesperada, no se debe continuar. Verificación del tipo de patch ------------------------------ El nombre del patch no garantiza que su tipo OpenFOAM sea correcto. Por ejemplo, ``WALLS`` debe quedar como: .. code-block:: text type wall; y no simplemente como: .. code-block:: text type patch; Verifique el archivo ``boundary`` después de cualquier operación de reclasificación. En el estado pre-NCC utilizado para AP04: :: AIR_INLET_PLENUM type=patch GAS_INLET_STAGE_1 type=patch GAS_INLET_STAGE_2 type=patch GAS_INLET_STAGE_3 type=patch OUTLET type=patch PERIODIC_0 type=patch PERIODIC_180 type=patch WALLS type=wall Construcción de la NCC ---------------------- La NCC debe construirse una sola vez sobre una malla pre-NCC limpia. Ejemplo: .. code-block:: bash createNonConformalCouples \ -dict system/createNonConformalCouplesDict.AP04.reverse Para el caso validado se utilizaron: :: patches ( PERIODIC_180 PERIODIC_0 ); con transformación rotacional de 180 grados. La ejecución correcta produjo: :: 457704 couplings calculated fvMeshStitcher: Connecting Writing mesh to 0.050005 End y creó los patches auxiliares: :: periodic180_on_PERIODIC_180 periodic180_on_PERIODIC_0 nonConformalError_on_PERIODIC_180 nonConformalError_on_PERIODIC_0 Conservar conjuntamente polyMesh y fvMesh ----------------------------------------- Después de ``createNonConformalCouples`` se deben comprobar los dos objetos generados: .. code-block:: bash ls -lh \ 0.050005/polyMesh/boundary \ 0.050005/fvMesh/polyFaces Ambos deben pertenecer a la misma generación de la malla NCC. Para promover esa malla a ``constant``: .. code-block:: bash cp -a 0.050005/polyMesh constant/polyMesh cp -a 0.050005/fvMesh constant/fvMesh No copie solamente ``polyMesh``. Comprobación de polyFaces heredado ---------------------------------- Si el caso destino se creó inicialmente copiando otro caso, compare los ``polyFaces``: .. code-block:: bash cmp -s \ CASO_DESTINO/constant/fvMesh/polyFaces \ CASO_FUENTE/constant/fvMesh/polyFaces Un control práctico: .. code-block:: bash if cmp -s \ CASO_DESTINO/constant/fvMesh/polyFaces \ CASO_FUENTE/constant/fvMesh/polyFaces then echo "WARNING: polyFaces identical" else echo "polyFaces differs" fi Si las mallas son distintas y ``polyFaces`` es binariamente idéntico, debe considerarse sospechoso. En el caso validado: :: AP03C polyFaces = 821 KB AP04 polyFaces = 1.8 MB Esto confirmó que AP04 tenía su propio ``polyFaces``. Startup test antes del mapeo ---------------------------- Antes de ejecutar ``mapFields``, la malla destino debe ser capaz de abrirse correctamente con el solver. Configure temporalmente: .. code-block:: text startFrom startTime; startTime 0.05; stopAt endTime; endTime 0.05; Ejecute: .. code-block:: bash foamRun \ -solver multicomponentFluid \ -noFunctionObjects El objetivo es que OpenFOAM construya la malla, conecte la NCC, inicialice el modelo y termine sin avanzar tiempo. Una prueba satisfactoria alcanza: :: fvMeshStitcher: Connecting Selecting solver multicomponentFluid ... Starting time loop End Esto verifica independientemente: :: malla + NCC + fvMesh + diccionarios No se debe diagnosticar ``mapFields`` mientras la malla destino no supere este test. Preparación del tiempo destino ------------------------------ Debe existir el directorio temporal donde se escribirán los campos mapeados. Por ejemplo: :: 0.05/ con los campos requeridos por el solver: :: U p T k omega O2 N2 CH4 ... En un modelo multicomponente deben existir también todas las especies esperadas por el solver. Antes de mapear: .. code-block:: bash cp -a 0.05 0.05.preMap Configuración de mapFieldsDict ------------------------------ El procedimiento validado utilizó: .. code-block:: text FoamFile { format ascii; class dictionary; object mapFieldsDict; } patchMap ( ); cuttingPatches ( ); Mapeo efectivo -------------- Para dos mallas distintas de la misma geometría física, el procedimiento efectivo fue ejecutar ``mapFields`` sin ``-consistent``. Ejemplo general: .. code-block:: bash mapFields \ ../CASO_FUENTE \ -sourceTime 0.05 \ -noFunctionObjects Caso validado: .. code-block:: bash mapFields \ ../B068-P180_coldFlow_AP03C_16proc \ -sourceTime 0.05 \ -noFunctionObjects \ > log.mapFields.AP03C_to_AP04_005_nonConsistent_cleanNCC 2>&1 Verifique el código de retorno: .. code-block:: bash rc=$? echo "exit = $rc" Una ejecución correcta mostró: :: Source mesh size: 1333396 Target mesh size: 3153056 Mapping fields for time 0.05 interpolating OH interpolating CN ... interpolating k ... interpolating p interpolating T ... interpolating omega ... interpolating U End y: :: exit = 0 Uso de -consistent ------------------ No debe interpretarse ``-consistent`` simplemente como "misma geometría". La opción supone una correspondencia más estricta entre source y target. Para este caso, con dos discretizaciones NCC diferentes, la ruta efectiva y validada fue: .. code-block:: bash mapFields SOURCE -sourceTime TIME sin: :: -consistent Uso de source reconstruido --------------------------- Si el caso fuente dispone de una solución reconstruida en el directorio principal del caso, no es necesario mapear desde los ``processor*``. Por ejemplo: :: B068-P180_coldFlow_AP03C_16proc/ 0.05/ processor0/ ... processor15/ En este caso se utilizó directamente: .. code-block:: bash mapFields \ ../B068-P180_coldFlow_AP03C_16proc \ -sourceTime 0.05 sin ``-parallelSource``. Validación posterior al mapeo ----------------------------- Después del mapping, repita el startup test del solver con: .. code-block:: text startTime 0.05; endTime 0.05; Ejecute: .. code-block:: bash foamRun \ -solver multicomponentFluid \ -noFunctionObjects En el caso validado: :: exit = 0 El solver inicializó correctamente: :: multicomponentFluid kOmegaSST 53 species y alcanzó: :: Starting time loop End Como comprobación adicional, el Courant cambió después del mapping a: :: Courant Number mean: 0.004459740956 Courant Number max: 0.19484275 Esto confirmó que el campo ``U`` había sido realmente transferido al target. Descomposición del caso destino ------------------------------- La descomposición debe realizarse después del mapping. Elimine una descomposición anterior: .. code-block:: bash rm -rf processor* Descomponga nuevamente: .. code-block:: bash decomposePar -force \ > log.decomposePar 2>&1 Compruebe el número de particiones: .. code-block:: bash find . -maxdepth 1 -type d -name 'processor*' | wc -l Para 16 procesos: :: 16 En un caso NCC verifique también ``polyFaces``: .. code-block:: bash find processor* \ -path '*/constant/fvMesh/polyFaces' \ -type f | wc -l Resultado esperado: :: 16 Compruebe igualmente que el estado inicial fue descompuesto: .. code-block:: bash find processor* \ -path '*/0.05/U' \ -type f | wc -l Resultado esperado: :: 16 Ejecución paralela ------------------ Una vez validado el target: .. code-block:: bash mpirun -np 16 \ foamRun \ -solver multicomponentFluid \ -parallel \ > log.run 2>&1 & La simulación continúa sobre la nueva malla utilizando como estado inicial la solución interpolada desde la malla anterior. Secuencia recomendada --------------------- La secuencia completa y reproducible es: :: 1. Crear nueva malla | v 2. checkMesh | v 3. Crear o clasificar patches físicos | v 4. Confirmar tipos de patch | v 5. Guardar snapshot PRE-NCC | v 6. createNonConformalCouples una sola vez | v 7. Preservar juntos: polyMesh/ fvMesh/polyFaces | v 8. Startup test de la nueva malla | v 9. Preparar tiempo destino | v 10. Backup del tiempo destino | v 11. mapFields SOURCE -sourceTime TIME | v 12. Confirmar End y exit=0 | v 13. Startup test post-mapping | v 14. decomposePar | v 15. Confirmar polyFaces en todos los processors | v 16. Ejecutar CFD en paralelo Plantilla reutilizable ---------------------- Con el caso destino como directorio actual: .. code-block:: bash SOURCE=../Caso_malla_A TARGET_TIME=0.05 cp -a "$TARGET_TIME" "${TARGET_TIME}.preMap" mapFields \ "$SOURCE" \ -sourceTime "$TARGET_TIME" \ -noFunctionObjects \ > log.mapFields 2>&1 rc=$? echo "mapFields exit = $rc" tail -n 100 log.mapFields Solo continúe si obtiene: :: End mapFields exit = 0 Después haga el startup test, con ``startTime=endTime``: .. code-block:: bash foamRun \ -solver multicomponentFluid \ -noFunctionObjects Finalmente: .. code-block:: bash rm -rf processor* decomposePar -force \ > log.decomposePar 2>&1 mpirun -np 16 \ foamRun \ -solver multicomponentFluid \ -parallel \ > log.run 2>&1 & Conclusión ---------- Una solución CFD puede transferirse entre discretizaciones distintas de una misma geometría siempre que: * la nueva malla sea válida por sí misma; * sus patches físicos estén correctamente clasificados; * la NCC se haya construido sobre la malla definitiva; * ``polyMesh`` y ``fvMesh/polyFaces`` pertenezcan a la misma generación; * el solver pueda abrir la nueva malla antes del mapping; * ``mapFields`` se ejecute en el modo adecuado para mallas diferentes; * el estado mapeado se valide nuevamente antes de descomponer y correr. Para el caso B068-P180, la ruta validada quedó: :: AP03C @ 0.05 s | | mapFields no-consistent v AP04 @ 0.05 s | | startup validation v decomposePar | v continuación CFD sobre AP04 Este procedimiento permite reutilizar soluciones previamente calculadas después de una remallada, evitando reiniciar la simulación desde condiciones iniciales.