Traducción del original en coreano escrito por el autor. Leer el original (한국어)
Guía de implementación del modelo de 5 ecuaciones: Solucionador de flujo multifásico con Python
El paso de tiempo no lo fija esos 24 m/s. Lo aprieta el agua detrás del choque, cerca de 2530 m/s.
Implementación directa del modelo de 5 ecuaciones#
El agua detrás del choque está a presión . El agua por delante está a . En medio hay una burbuja de aire, y la fracción de volumen salta entre casi 0 y casi 1. El modelo de interfaz difusa de 5 ecuaciones en 1D transporta ese salto con cinco ecuaciones. Los dos fluidos comparten una velocidad y una presión. Solo la fracción de volumen viaja aparte.
Al bajar a 0 el control de difusión artificial, el frente de fracción de volumen se corta en una sola celda.
5-방정식 모델의 핵심: 부피분율 α의 이류. upwind 만으로는 계면이 점차 번지므로 실제 코드는 THINC / interface sharpening을 추가한다.
Al subir el control, el frente se reparte en varias celdas. El de esas celdas entra directo en el denominador de la presión de mezcla.
Resumen de las ecuaciones de gobierno#
En 1D las variables conservadas y el flujo son estos.
La quinta componente del flujo es 0. es no conservativa, así que se actualiza aparte.
Cinco ecuaciones es lo que queda al reducir ambas fases a una velocidad y una presión. Devolver un sistema a cada fase es el modelo de siete ecuaciones de Baer–Nunziato.
Ecuación de estado: Stiffened Gas EOS#
Los dos fluidos usan un gas endurecido (stiffened gas).
La presión de mezcla sale de esta regla.
es la presión del fluido , su energía interna, y , las constantes de la EOS. El denominador es .
Implementación en Python#
Configuración inicial y EOS#
import numpy as np
import matplotlib.pyplot as plt
# Dominio
N = 1000
x = np.linspace(0, 1, N)
dx = x[1] - x[0]
# Parámetros de la EOS
# Fluido 1: Aire (gas ideal)
gamma1, pinf1 = 1.4, 0.0
# Fluido 2: Agua (gas endurecido)
gamma2, pinf2 = 4.4, 6e8
def pressure(alpha1, arho1, arho2, E, u):
"""Calcula la presión de la mezcla a partir de las variables conservadas."""
alpha2 = 1.0 - alpha1
rho = arho1 + arho2
ke = 0.5 * rho * u**2
rhoe = E - ke
num = rhoe - alpha1*gamma1*pinf1/(gamma1-1) - alpha2*gamma2*pinf2/(gamma2-1)
den = alpha1/(gamma1-1) + alpha2/(gamma2-1)
return num / den
def sound_speed(alpha1, arho1, arho2, p):
"""Calcula la velocidad del sonido de la mezcla (fórmula de Wood)."""
alpha2 = 1.0 - alpha1
rho = arho1 + arho2
rho1 = arho1 / np.maximum(alpha1, 1e-12)
rho2 = arho2 / np.maximum(alpha2, 1e-12)
c1sq = gamma1 * (p + pinf1) / rho1
c2sq = gamma2 * (p + pinf2) / rho2
# Fórmula de Wood
inv_rhoc2 = alpha1/(rho1*c1sq) + alpha2/(rho2*c2sq)
return np.sqrt(1.0 / (rho * inv_rhoc2))pressure es la regla de mezcla reescrita sobre variables conservadas. En sound_speed, es la velocidad del sonido del fluido puro.
Cálculo del flujo HLLC#
HLLC cubre las cuatro leyes de conservación. La velocidad de contacto vuelve junto al flujo y luego transporta .
def hllc_flux(UL, UR, alpha1L, alpha1R):
"""Flujo numérico HLLC para las 4 ecuaciones de conservación."""
arho1L, arho2L, momL, EL = UL
arho1R, arho2R, momR, ER = UR
rhoL = arho1L + arho2L
rhoR = arho1R + arho2R
uL = momL / rhoL
uR = momR / rhoR
pL = pressure(alpha1L, arho1L, arho2L, EL, uL)
pR = pressure(alpha1R, arho1R, arho2R, ER, uR)
cL = sound_speed(alpha1L, arho1L, arho2L, pL)
cR = sound_speed(alpha1R, arho1R, arho2R, pR)
# Estimaciones de la velocidad de onda
SL = min(uL - cL, uR - cR)
SR = max(uL + cL, uR + cR)
Sstar = (pR - pL + rhoL*uL*(SL-uL) - rhoR*uR*(SR-uR)) \
/ (rhoL*(SL-uL) - rhoR*(SR-uR))
FL = np.array([arho1L*uL, arho2L*uL, momL*uL + pL, (EL+pL)*uL])
FR = np.array([arho1R*uR, arho2R*uR, momR*uR + pR, (ER+pR)*uR])
if SL >= 0:
return FL, Sstar
elif Sstar >= 0:
coeff = rhoL * (SL - uL) / (SL - Sstar)
UstarL = coeff * np.array([
arho1L/rhoL,
arho2L/rhoL,
Sstar,
EL/rhoL + (Sstar - uL)*(Sstar + pL/(rhoL*(SL-uL)))
])
return FL + SL*(UstarL - UL), Sstar
elif SR > 0:
coeff = rhoR * (SR - uR) / (SR - Sstar)
UstarR = coeff * np.array([
arho1R/rhoR,
arho2R/rhoR,
Sstar,
ER/rhoR + (Sstar - uR)*(Sstar + pR/(rhoR*(SR-uR)))
])
return FR + SR*(UstarR - UR), Sstar
else:
return FR, SstarIntegración temporal (1er orden)#
El avance temporal es Euler hacia adelante. El paso es , con CFL 0.5.
def solve(U, alpha1, dx, t_end, cfl=0.5):
"""Integración temporal de Euler hacia adelante."""
t = 0.0
while t < t_end:
rho = U[0] + U[1]
u = U[2] / rho
p = pressure(alpha1, U[0], U[1], U[3], u)
c = sound_speed(alpha1, U[0], U[1], p)
dt = cfl * dx / np.max(np.abs(u) + c)
if t + dt > t_end:
dt = t_end - t
# Calcular flujos en las interfaces de las celdas
flux = np.zeros((4, N+1))
uface = np.zeros(N+1)
for i in range(N+1):
iL = max(i-1, 0)
iR = min(i, N-1)
UL_local = U[:4, iL]
UR_local = U[:4, iR]
flux[:, i], uface[i] = hllc_flux(
UL_local, UR_local, alpha1[iL], alpha1[iR]
)
# Actualizar variables conservadas
for k in range(4):
U[k, 1:-1] -= dt/dx * (flux[k, 2:-1] - flux[k, 1:-2])
# Actualizar alpha1 (no conservativo, upwind)
for i in range(1, N-1):
if uface[i] >= 0:
alpha1[i] -= dt/dx * uface[i] * (alpha1[i] - alpha1[i-1])
else:
alpha1[i] -= dt/dx * uface[i] * (alpha1[i+1] - alpha1[i])
alpha1 = np.clip(alpha1, 1e-8, 1-1e-8)
t += dt
return U, alpha1La actualización de no es un flujo conservativo. El signo de la velocidad de cara uface elige el lado upwind.
Condiciones iniciales: Onda de choque - Burbuja de gas#
Antes de la posición 0.25 el agua ya cruzó el choque: , , . Entre 0.4 y 0.6 está la burbuja de aire, , , . El resto es agua ambiente, , , .
# Condición inicial: choque golpeando una burbuja de aire en agua
U = np.zeros((4, N))
alpha1 = np.zeros(N)
for i in range(N):
if x[i] < 0.25:
# Agua post-choque (alta presión)
rho2 = 1100.0; u_val = 50.0; p_val = 1e9
alpha1[i] = 1e-8
arho1 = alpha1[i] * 1.0 # aire insignificante
arho2 = (1 - alpha1[i]) * rho2
elif 0.4 < x[i] < 0.6:
# Burbuja de aire
rho1 = 1.0; u_val = 0.0; p_val = 1e5
alpha1[i] = 1 - 1e-8
arho1 = alpha1[i] * rho1
arho2 = (1 - alpha1[i]) * 1000.0
else:
# Agua ambiente
rho2 = 1000.0; u_val = 0.0; p_val = 1e5
alpha1[i] = 1e-8
arho1 = alpha1[i] * 1.0
arho2 = (1 - alpha1[i]) * rho2
rho = arho1 + arho2
E = compute_total_energy(alpha1[i], arho1, arho2, p_val, u_val)
U[0, i] = arho1
U[1, i] = arho2
U[2, i] = rho * u_val
U[3, i] = Ecompute_total_energy no está en este fragmento. La EOS ya dice cómo volver de la presión a la energía: .
Consideraciones importantes#
1. Limitación del rango de #
Si vale exactamente 0 o 1, el producto conservado no se puede dividir entre . El código recorta entre y .
2. Velocidad del sonido de la mezcla: Fórmula de Wood#
Una celda mezclada puede ser más lenta que cualquiera de los dos fluidos puros. La fórmula lo dice, y el CFL lee esa misma velocidad.
Los estados puros de la condición inicial ya fijan el número. El aire de la burbuja tiene , , , . Entonces . El agua ambiente tiene , , , , así que . El agua detrás del choque tiene , , así que . El grupo es cerca de 2.67 veces el del agua ambiente, mientras la densidad solo es veces mayor. La energía cinética a es . Eso es cerca del 0.13% de la densidad de energía interna en la misma celda. La energía de este choque está en , no en los 50 m/s.
Al mezclar aire de la burbuja y agua ambiente con y una presión común , en la fórmula de Wood es casi por completo el término del aire. , mientras . La densidad de mezcla es , y . Ni 374 ni 1625.
El denominador de la presión es sensible en el mismo sitio. El agua pura tiene . Si se filtra en esa agua, el denominador pasa a , cerca de un 7.5% más. La presión se divide por ese denominador, así que un error de en una interfaz emborronada aparece como error de presión.
El paso CFL es , y es el espaciado de linspace(0, 1, 1000), o sea . No lo fija la celda mezclada. Detrás del choque, , de modo que . Una celda a 24 m/s permitiría un paso cerca de 110 veces mayor. Una velocidad de mezcla baja no aprieta el paso de tiempo. Lo que hace es dejar la acústica de la interfaz casi quieta dentro de ese mismo .
3. Extensión a alta precisión#
El integrador de arriba es de primer orden, y la actualización espacial usa el valor de la celda tal cual. El segundo orden pide MUSCL más un slope limiter. Por encima, WENO. La precisión temporal sube con Runge–Kutta. Nada de eso quita la actualización no conservativa de ni la velocidad de Wood.
Interpretación de resultados#
Cuando el choque entra en la burbuja, un choque transmitido sale por el lado de aguas abajo y una rarefacción reflejada vuelve a la izquierda. La burbuja se aplasta y sube su presión interna. Un chorro en la pared de aguas abajo pide 2D o 3D. Las explosiones submarinas y la litotricia por ondas de choque se apoyan en esta figura.
Un α solo upwind deja que la presión de contacto se mueva primero#
El quinto flujo es 0, y se pone upwind según el signo de la velocidad de cara. El salto del 7.5% en el denominador de la presión cuando se filtra en el agua significa que una celda mala de interfaz puede mover la presión de equilibrio. Esta actualización de primer orden no impone ese equilibrio por su cuenta. La discretización del término no conservativo es lo que decide si la presión se queda quieta.
La comprobación de ese equilibrio es un contacto donde no debería pasar nada más. Dónde se escapa la presión en ese contacto es la pregunta del criterio de Abgrall y el término no conservativo.
Relacionados
Comparte si te resultó útil.