Cuando FTCS tiembla, el upwind se entumece — esquema advectivo de primer orden, viscosidad artificial y el trueque de CFL
Por qué la diferencia centrada explota, por qué el upwind sobrevive y qué paga por hacerlo
A comienzos de los años sesenta, un colega de John von Neumann conectó la PDE más sencilla imaginable — advección lineal unidimensional — con la diferencia centrada más natural. El resultado fue una explosión: un pulso cuadrado que debería haber derivado tranquilamente comenzó a generar oscilaciones en su borde de salida y disparó fuera de la malla en pocos pasos. Este artículo sigue la pista de ese descontrol, explora por qué una diferencia upwind de una sola celda lo domina pero solo difuminando la imagen, y desentraña qué dice realmente la línea invisible trazada entre el paso espacial y el temporal (la condición CFL). Al final, cincuenta líneas de Python y una demostración interactiva ponen tres esquemas a competir en la misma malla.
La diferencia más natural cae primero#
La ecuación de advección lineal en 1D es una sola línea.
Aquí es la cantidad transportada y es una velocidad constante. La solución es trivial — la forma inicial se traslada con velocidad .
Si se toma Euler hacia adelante en el tiempo y diferencia centrada (segundo orden) en el espacio, la regla de actualización sale limpia.
El número de Courant mide cuántas celdas avanza la onda en un paso temporal. Segundo orden en espacio, primer orden en tiempo, así que el LTE (error de truncamiento local) es . Y aun así el esquema explota por pequeño que sea .
La razón cabe en una línea de análisis de von Neumann. Sustituyendo se obtiene el factor de amplificación
de modo que . Cada modo de Fourier crece en cada paso. Basta con un modo con para arruinarlo todo.
La información solo fluye desde un lado#
El problema de fondo es la causalidad, no el álgebra. Cuando , el valor de en debería depender únicamente de información aguas arriba (). Pero la diferencia centrada arrastra del lado aguas abajo con peso igual. El algoritmo extrae información de un futuro que el flujo aún desconoce.
La diferencia upwind impone causalidad.
Ahora von Neumann nos dice que
y . Para resulta . Cada modo decae o se queda quieto.
Upwind = centrada + una viscosidad oculta#
Hay una forma más directa de ver por qué el upwind es estable. Descompongamos la derivada upwind de primer orden mediante una identidad algebraica.
El primer término de la derecha es la diferencia centrada; el segundo es exactamente una segunda derivada discreta. Por lo tanto el upwind de primer orden está resolviendo la ecuación modificada
Queríamos resolver advección. El algoritmo nos entregó silenciosamente una difusión de intensidad como bonificación. Esto es la viscosidad artificial (artificial viscosity, también llamada numerical diffusion).
Esa viscosidad mata el descontrol. Pero no es gratis. Las esquinas del pulso cuadrado se gausianizan en cada paso, con anchura . Refinar la malla (menor ) reduce la viscosidad, pero hay que dar más pasos para llegar al mismo tiempo final. Ambos efectos se equilibran exactamente, así que la difuminación nunca desaparece del todo.
CFL — cuando la malla no alcanza la característica#
Hay una razón geométrica para que el upwind solo sea estable con . El valor de en proviene de donde estaba el fluido en , es decir . Si ese punto cae entre y , los dos valores de la malla pueden interpolarlo. En cuanto , el punto pie queda más allá de — y la plantilla del algoritmo no llega tan lejos.
Ése es el corazón de la condición de Courant–Friedrichs–Lewy.
El dominio numérico de dependencia (la plantilla) debe contener al dominio físico de dependencia (la característica). CFL es necesaria para todo esquema explícito pero nunca suficiente. FTCS cumple CFL y aun así detona — su problema es la causalidad, no el alcance de la plantilla.
Tres esquemas en cincuenta líneas de NumPy#
Misma malla, misma condición inicial, mismo CFL. Que corran y a mirar.
import numpy as np
N = 200
dx = 1.0 / N
u = 1.0
cfl = 0.8
dt = cfl * dx / u
n_steps = 400
x = (np.arange(N) + 0.5) * dx
q0 = ((x > 0.2) & (x < 0.4)).astype(float) # pulso cuadrado
def ftcs_advect(q, c):
return q - 0.5 * c * (np.roll(q, -1) - np.roll(q, 1))
def upwind_advect(q, c):
return q - c * (q - np.roll(q, 1)) # u > 0
def lax_wendroff_advect(q, c):
flux_diff = 0.5 * c * (np.roll(q, -1) - np.roll(q, 1))
visc = 0.5 * c * c * (np.roll(q, -1) - 2 * q + np.roll(q, 1))
return q - flux_diff + visc
q_ftcs, q_up, q_lw = q0.copy(), q0.copy(), q0.copy()
for _ in range(n_steps):
q_ftcs = ftcs_advect(q_ftcs, cfl)
q_up = upwind_advect(q_up, cfl)
q_lw = lax_wendroff_advect(q_lw, cfl)
# Solucion exacta: una vuelta completa volviendo al inicio
q_exact = q0
print("FTCS L1 err:", np.abs(q_ftcs - q_exact).sum() * dx)
print("Upwind L1 err:", np.abs(q_up - q_exact).sum() * dx)
print("LW L1 err:", np.abs(q_lw - q_exact).sum() * dx)Con cfl=0.8 y 400 pasos (una revolución completa), la respuesta exacta es la forma original. Resultados típicos:
- FTCS: la amplitud crece aproximadamente veces y revienta en NaN o
inf. - Upwind: el pulso se ensancha como un manchón gaussiano, el error L1 se asienta cerca de 0.04.
- Lax–Wendroff: la amplitud se conserva, pero las esquinas dejan rizos (Gibbs) detrás.
Un guion de juguete reproduce todas las afirmaciones del artículo: explosión, difuminación, oscilación.
Tócalo tú mismo#
Prueba la simulación de abajo. Al deslizar CFL de 0.05 a 1.5, los tres esquemas siguen destinos muy distintos sobre la misma malla.
Pon CFL exactamente en 1.0 y la curva upwind (cian) queda casi exactamente sobre el desplazamiento analítico (línea discontinua). El upwind de primer orden es exacto en — el coeficiente de viscosidad artificial se anula ahí. Baja CFL a 0.5 y la difuminación del upwind alcanza su pico mientras FTCS sigue divergiendo. Sube CFL a 1.05 y tanto upwind como Lax–Wendroff explotan — la plantilla no alcanza a seguir la característica.
El primer orden es muy romo, pero el segundo trae nuevos dolores de cabeza#
La viscosidad artificial del upwind es bienvenida cerca de choques — aplana las oscilaciones no físicas. Pero actúa igual en regiones suaves, así que la precisión queda atascada en errores de segundo orden. Los códigos CFD de producción eligen una de dos rutas.
- Usar un esquema de orden superior (Lax–Wendroff, MUSCL, WENO) y atornillar un limitador de flujo o limitador de pendiente que reduce el orden cerca de los choques.
- Resolver la estructura causal con exactitud mediante un solver de Riemann (Godunov, HLLC, Roe) en cada cara de celda.
Ambas rutas son mecanismos para decidir dónde se acepta y dónde se rechaza la viscosidad artificial. El upwind de primer orden la acepta en todas partes — el más simple, el más seguro, el más difusivo.
Tres líneas para recordar#
- FTCS centrado diverge sin importar CFL. El descontrol es causal, no de plantilla.
- El upwind de primer orden compra estabilidad con viscosidad artificial . Estable para ; exacto en .
- CFL es necesaria para todo esquema explícito pero nunca suficiente. La estabilidad requiere causalidad y alcance de plantilla a la vez.
Comparte si te resultó útil.