1.4. Líneas características del flujo#

Las líneas características del flujo son líneas imaginarias trazadas en el campo fluido con unas propiedades particulares. Estas líneas se utilizan para visualizar y analizar el comportamiento del flujo. Podemos definir 3 tipos de líneas características:

  • Líneas de corriente

  • Trayectorias

  • Trazas

../../../_images/Imagen16.png

Fig. 1.12 Visualización del flujo alrededor de un perfil aerodinámico (Wikimedia Commons).#

Para ejemplificar los distintos tipos de líneas características del flujo, vamos a considerar el siguiente campo vectorial en 2 dimensiones, con dependencia espacial y temporal:

\[\begin{split}\vec{\bf{v}}(x,t)=\left(\begin{array}{c} 2 \\ 12t\cos(\pi x) + 0.5 \end{array} \right)\end{split}\]

1.4.1. Línea de corriente (streamline)#

Una línea de corriente es una línea imaginaria trazada en un campo fluido que es tangente en cada punto al vector de velocidad del flujo en ese punto. En otras palabras, en cada punto a lo largo de la línea de corriente, la dirección del flujo coincide con la dirección de la línea de corriente. Se utilizan para visualizar el patrón general del flujo y entender cómo se mueven las partículas en el campo de flujo. Se trata de un concepto instantáneo equivalente a tomar una foto en un instante determinado.

Denotemos \(\vec{\bf{r}}=(x,y,z)\) al vector posición. Por definición:

\[\vec{\bf{v}}\times d\vec{\bf{r}}=0\]
\[\frac{u}{dx}=\frac{v}{dy}=\frac{w}{dz}\]
../../../_images/Imagen13.png

Fig. 1.13 Línea de corriente#

En la siguiente animación se muestran 3 líneas de corriente que pasan por los puntos \((0.1,0.4)\), \((0.2,0.4)\), \((0.3,0.4)\). Las líneas de corriente cambian en el tiempo ya que el flujo no es estacionario.

Hide code cell content
import math
import numpy as np              # Librería para poder trabajar con matrices y vectores
import matplotlib.pyplot as plt # Librería para poder dibujar gráficas
from myst_nb import glue
from scipy.interpolate import Rbf
from matplotlib import animation
from IPython.display import HTML

nq=2        #separacion entre vectores en quiver plot
N=21
L=1.0
xp = np.linspace(0, L, N)
yp = np.linspace(0, L, N)
X, Y = np.meshgrid(xp, yp)

t=0.0

fig, ax = plt.subplots(figsize=(7, 7))

ax.set_title("Descripción Euleriana")
plt.tight_layout()

plt.close()

nframes=100    #frames de la animacion
tf=2*math.pi/20        #tiempo total
dt=tf/nframes #paso de tiempo

seed_points = np.array([[0.1, 0.2, 0.3], [0.4, 0.4, 0.4]])


def update_plot(num):
    t = dt*num
    dx=t*2.0
    U = 2*np.ones(np.shape(X))
    #V =  2*np.cos(np.pi * X + 0.25*np.sin(20*t)) * np.sin(np.pi * Y)
    V =  2*np.cos(np.pi * X) * 6*t + 0.5
    ax.cla()
    ax.quiver(X[::nq,::nq], Y[::nq,::nq], U[::nq,::nq], V[::nq,::nq])
    ax.streamplot(X, Y, U, V, start_points=seed_points.T, color='tab:red', linewidth=2) 
    ax.plot(seed_points[0],seed_points[1],'go')
    ax.set_xlabel("x")
    ax.set_ylabel("y")
    
    return

anim = animation.FuncAnimation(fig, update_plot, frames=nframes, interval=100, blit=False)
HTML(anim.to_html5_video()) 

1.4.2. Trayectoria (pathline)#

La trayectoria contiene la información referente al camino o senda que recorre cada partícula fluida. Es el conjunto de puntos del espacio por los que ha pasado una particula fluida. Si la velocidad es constante en el tiempo la trayectoria coincide con la línea de corriente. Se trata de una historia temporal que muestra las posiciones que ha tenido una partícula fluida, por lo tanto no es un concepto instantáneo (el tiempo es la variable de integración).

Se calcula aplicando la definición de velocidad de una partícula fluida en un contexto lagrangiano:

\[\frac{d\vec{\bf{r}}}{dt}=\vec{\bf{v}}\]

e integrando \(d\vec{\bf{r}}=\vec{\bf{v}} dt\).

../../../_images/Imagen14.png

Fig. 1.16 Trayectoria#

En la siguiente animación se muestran 3 trayectorias que pasan por los puntos \((0.1,0.4)\), \((0.2,0.4)\), \((0.3,0.4)\).

Hide code cell content
nq=2        #separacion entre vectores en quiver plot
N=21
L=1.0
xp = np.linspace(0, L, N)
yp = np.linspace(0, L, N)
X, Y = np.meshgrid(xp, yp)

t=0.0

fig, ax = plt.subplots(figsize=(7, 7))

plt.tight_layout()

plt.close()

nframes=100    #frames de la animacion
tf=2*math.pi/20        #tiempo total
dt=tf/nframes #paso de tiempo

XP = np.transpose(np.array([[0.1, 0.2, 0.3], [0.4, 0.4, 0.4]]))

U = 2*np.ones(np.shape(X))
#V =  2*np.cos(np.pi * X + 0.25*np.sin(20*t)) * np.sin(np.pi * Y)  
V =  2*np.cos(np.pi * X) * 6*t + 0.5
Q = ax.quiver(X[::nq,::nq], Y[::nq,::nq], U[::nq,::nq], V[::nq,::nq])

#ax.plot(seed_points[0],seed_points[1],'go')
P, = ax.plot(XP[:,0],XP[:,1], 'go', ms=10)

def update_plot(num):
    t = dt*num
    dx=t*2.0
    U = 2*np.ones(np.shape(X))
    #V =  2*np.cos(np.pi * X + 0.25*np.sin(20*t)) * np.sin(np.pi * Y)    
    V =  2*np.cos(np.pi * X) * 6*t + 0.5
    Q.set_UVC(U[::nq,::nq],V[::nq,::nq])
    interpx = Rbf(X, Y, U)
    interpy = Rbf(X, Y, V)
    XP[:,0] = XP[:,0] + interpx(XP[:,0],XP[:,1])*dt
    XP[:,1] = XP[:,1] + interpy(XP[:,0],XP[:,1])*dt
    ax.plot(XP[:,0],XP[:,1], '.',color="tab:orange", ms=7)
    P.set_data(XP[:,0],XP[:,1])
    ax.set_xlabel("x")
    ax.set_ylabel("y")
    
    return

anim = animation.FuncAnimation(fig, update_plot, frames=nframes, interval=100, blit=False)
HTML(anim.to_html5_video()) 

1.4.3. Traza (streakline)#

La traza es el lugar geométrico de puntos del plano en el que se encuentran las partículas fluidas que han pasado por un mismo punto inicial. Imaginemos inyectar tinta o una serie de partículas en un fluido en movimiento. Estas partículas se moverán con el flujo y crearán una línea continua a medida que viajan. A esta línea se le llama traza.

En la figura inferior se muestra una ilustración de la traza formada por las partículas fluidas que han salido de una chimenea. En el tiempo \(t_0\), la partícula fluida azul sale de la chimenea. En el tiempo \(t_1\), la partícula fluida azul habrá sido arrastrada por el flujo siguiendo la dirección del viento, y una nueva partícula fluida (verde) saldrá de la chimenea. En el tiempo \(t_2\), las partículas azul y verde han sido arrastradas por el flujo siguiendo la dirección del viento y una nueva partícula fluida (naranja) saldrá de la chimenea. Las posiciones previas de las partículas fluidas se indican con círculos huecos. La traza será la línea formada por las distintas partículas fluidas que han salido de la chimenea en cierto tiempo (por ejemplo en \(t_3\)). Si unimos las posiciones previas de una misma partícula fluida, obtendremos la trayectoria.

../../../_images/Imagen11.png

Fig. 1.19 Traza#

En la siguiente animación se muestran 3 trazas que se originan en los puntos \((0.1,0.4)\), \((0.2,0.4)\), \((0.3,0.4)\).

Hide code cell content
nq=2        #separacion entre vectores en quiver plot
N=21
L=1.0
xp = np.linspace(0, L, N)
yp = np.linspace(0, L, N)
X, Y = np.meshgrid(xp, yp)

t=0.0

fig, ax = plt.subplots(figsize=(7, 7))

plt.tight_layout()

plt.close()

nframes=100    #frames de la animacion
tf=2*math.pi/20        #tiempo total
dt=tf/nframes #paso de tiempo

XP = np.array([0.2,0.4],ndmin=2)
XP = np.repeat(XP, nframes, axis=0)



U = 2*np.ones(np.shape(X))
#V =  2*np.cos(np.pi * X + 0.25*np.sin(20*t)) * np.sin(np.pi * Y)  
V =  2*np.cos(np.pi * X) * 6*t + 0.5
Q = ax.quiver(X[::nq,::nq], Y[::nq,::nq], U[::nq,::nq], V[::nq,::nq])

#ax.plot(seed_points[0],seed_points[1],'go')
P, = ax.plot(XP[0,0],XP[0,1], 'go', ms=10)
Ptraza, = ax.plot(XP[:,0],XP[:,1], '.',color='tab:green', ms=7)

def update_plot(num):
    t = dt*num
    dx=t*2.0
    U = 2*np.ones(np.shape(X))
    #V =  2*np.cos(np.pi * X + 0.25*np.sin(20*t)) * np.sin(np.pi * Y)    
    V =  2*np.cos(np.pi * X) * 6*t + 0.5
    Q.set_UVC(U[::nq,::nq],V[::nq,::nq])
    interpx = Rbf(X, Y, U)
    interpy = Rbf(X, Y, V)
    ii=num
    XP[0:ii,0] = XP[0:ii,0] + interpx(XP[0:ii,0],XP[0:ii,1])*dt
    XP[0:ii,1] = XP[0:ii,1] + interpy(XP[0:ii,0],XP[0:ii,1])*dt
    Ptraza.set_data(XP[0:ii,0],XP[0:ii,1])
    P.set_data(XP[0,0],XP[0,1])
    ax.set_xlabel("x")
    ax.set_ylabel("y")
    
    return

anim = animation.FuncAnimation(fig, update_plot, frames=nframes, interval=100, blit=False)
HTML(anim.to_html5_video()) 

1.4.3.1. Comparación de trazas y trayectorias#

A continuación se muestra una comparación de trazas (verde) y trayectorias (naranja). La diferencia principal es que, en la traza (verde), los puntos que la componen son “arrastrados” por el flujo en el tiempo, al igual que ocurre en un soluto que se transporta en el agua. Por el contrario, los puntos que forman la trayectoria son fijos en el tiempo, ya que indican la posición de la partícula fluida en un tiempo dado.

Hide code cell content
nq=2        #separacion entre vectores en quiver plot
N=21
L=1.0
xp = np.linspace(0, L, N)
yp = np.linspace(0, L, N)
X, Y = np.meshgrid(xp, yp)

t=0.0

fig, ax = plt.subplots(figsize=(7, 7))

plt.tight_layout()

plt.close()

nframes=100    #frames de la animacion
tf=2*math.pi/20        #tiempo total
dt=tf/nframes #paso de tiempo

XP = np.array([0.2,0.4],ndmin=2)
XP = np.repeat(XP, nframes, axis=0)



U = 2*np.ones(np.shape(X))
#V =  2*np.cos(np.pi * X + 0.25*np.sin(20*t)) * np.sin(np.pi * Y)  
V =  2*np.cos(np.pi * X) * 6*t + 0.5
Q = ax.quiver(X[::nq,::nq], Y[::nq,::nq], U[::nq,::nq], V[::nq,::nq])

#ax.plot(seed_points[0],seed_points[1],'go')
P, = ax.plot(XP[0,0],XP[0,1], 'go', ms=10)
Ptraza, = ax.plot(XP[:,0],XP[:,1], '.',color='tab:green', ms=7)

seed_points = np.array([[0.2], [0.4]])


def update_plot(num):
    t = dt*num
    dx=t*2.0
    U = 2*np.ones(np.shape(X))
    #V =  2*np.cos(np.pi * X + 0.25*np.sin(20*t)) * np.sin(np.pi * Y)    
    V =  2*np.cos(np.pi * X) * 6*t + 0.5
    Q.set_UVC(U[::nq,::nq],V[::nq,::nq])
    interpx = Rbf(X, Y, U)
    interpy = Rbf(X, Y, V)
    ii=num
    XP[0:ii,0] = XP[0:ii,0] + interpx(XP[0:ii,0],XP[0:ii,1])*dt
    XP[0:ii,1] = XP[0:ii,1] + interpy(XP[0:ii,0],XP[0:ii,1])*dt
    #ax.streamplot(X, Y, U, V, start_points=seed_points.T, color='tab:red', linewidth=2)
    ax.plot(XP[0,0],XP[0,1], '.',color="tab:orange", ms=7)
    Ptraza.set_data(XP[0:ii,0],XP[0:ii,1])
    P.set_data(XP[0,0],XP[0,1]) 
    ax.set_xlabel("x")
    ax.set_ylabel("y")
    
    return

anim = animation.FuncAnimation(fig, update_plot, frames=nframes, interval=100, blit=False)
HTML(anim.to_html5_video()) 

Pero… ¿qué ocurrirá cuando tengamos un flujo estacionario?

A continuación se muestran la línea de corriente, traza y trayectoria para un flujo similar al anterior, pero sin dependencia temporal. Se observa que todas las líneas coinciden.

Hide code cell content
nq=2        #separacion entre vectores en quiver plot
N=21
L=1.0
xp = np.linspace(0, L, N)
yp = np.linspace(0, L, N)
X, Y = np.meshgrid(xp, yp)

t=0.0

fig, ax = plt.subplots(figsize=(7, 7))

plt.tight_layout()

plt.close()

nframes=100    #frames de la animacion
tf=2*math.pi/20        #tiempo total
dt=tf/nframes #paso de tiempo

XP = np.array([0.2,0.4],ndmin=2)
XP = np.repeat(XP, nframes, axis=0)



U = 2*np.ones(np.shape(X))
#V =  2*np.cos(np.pi * X + 0.25*np.sin(20*t)) * np.sin(np.pi * Y)  
V =  2*np.cos(np.pi * X)  + 0.5
Q = ax.quiver(X[::nq,::nq], Y[::nq,::nq], U[::nq,::nq], V[::nq,::nq])

#ax.plot(seed_points[0],seed_points[1],'go')
P, = ax.plot(XP[0,0],XP[0,1], 'go', ms=10)
Ptraza, = ax.plot(XP[:,0],XP[:,1], '.',color='tab:green', ms=8)

seed_points = np.array([[0.2], [0.4]])

ax.streamplot(X, Y, U, V, start_points=seed_points.T, color='tab:red', linewidth=2)

def update_plot(num):
    t = dt*num
    dx=t*2.0
    U = 2*np.ones(np.shape(X))
    #V =  2*np.cos(np.pi * X + 0.25*np.sin(20*t)) * np.sin(np.pi * Y)    
    V =  2*np.cos(np.pi * X)  + 0.5
    Q.set_UVC(U[::nq,::nq],V[::nq,::nq])
    interpx = Rbf(X, Y, U)
    interpy = Rbf(X, Y, V)
    ii=num
    XP[0:ii,0] = XP[0:ii,0] + interpx(XP[0:ii,0],XP[0:ii,1])*dt
    XP[0:ii,1] = XP[0:ii,1] + interpy(XP[0:ii,0],XP[0:ii,1])*dt
    #ax.streamplot(X, Y, U, V, start_points=seed_points.T, color='tab:red', linewidth=2)
    ax.plot(XP[0,0],XP[0,1], '.',color="tab:orange", ms=5)
    Ptraza.set_data(XP[0:ii,0],XP[0:ii,1])
    P.set_data(XP[0,0],XP[0,1]) 
    ax.set_xlabel("x")
    ax.set_ylabel("y")
    
    return

anim2 = animation.FuncAnimation(fig, update_plot, frames=nframes, interval=100, blit=False)
HTML(anim2.to_html5_video())