{
"cells": [
{
"cell_type": "markdown",
"metadata": {
"id": "Og8we7iGfPKt"
},
"source": [
"## Líneas características del flujo\n",
"\n",
"Asignatura: Mecánica de Fluidos\n",
"\n",
"Departamento: Ciencia y Tecnología de Materiales y Fluidos\n",
"\n",
"Centro: Escuela Universitaria Politécnica de Teruel\n",
"\n",
"Profesor: Adrián Navas Montilla"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "gALENIHKS9e7"
},
"source": [
"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:\n",
"\n",
"- Líneas de corriente\n",
"- Trayectorias\n",
"- Trazas\n",
"\n",
"\n",
"\n",
"\n",
"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:\n",
"\n",
"$$\\vec{\\bf{v}}(x,t)=\\left(\\begin{array}{c}\n",
"2 \\\\\n",
"12t\\cos(\\pi x) + 0.5\n",
"\\end{array}\n",
"\\right)$$\n"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "0BKOqqdzS9e7"
},
"source": [
"### Línea de corriente (streamline)\n",
"\n",
"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.\n",
"\n",
"Denotemos $\\vec{\\bf{r}}=(x,y,z)$ al vector posición. Por definición:\n",
"\n",
"$$\\vec{\\bf{v}}\\times d\\vec{\\bf{r}}=0$$\n",
"\n",
"$$\\frac{u}{dx}=\\frac{v}{dy}=\\frac{w}{dz}$$\n",
"\n",
"\n",
"\n",
"\n",
"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."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"id": "O0tEMzDQS9e8",
"tags": [
"hide-cell"
]
},
"outputs": [],
"source": [
"import math\n",
"import numpy as np # Librería para poder trabajar con matrices y vectores\n",
"import matplotlib.pyplot as plt # Librería para poder dibujar gráficas\n",
"from myst_nb import glue\n",
"from scipy.interpolate import Rbf\n",
"from matplotlib import animation\n",
"from IPython.display import HTML\n",
"\n",
"nq=2 #separacion entre vectores en quiver plot\n",
"N=21\n",
"L=1.0\n",
"xp = np.linspace(0, L, N)\n",
"yp = np.linspace(0, L, N)\n",
"X, Y = np.meshgrid(xp, yp)\n",
"\n",
"t=0.0\n",
"\n",
"fig, ax = plt.subplots(figsize=(7, 7))\n",
"\n",
"ax.set_title(\"Descripción Euleriana\")\n",
"plt.tight_layout()\n",
"\n",
"plt.close()\n",
"\n",
"nframes=100 #frames de la animacion\n",
"tf=2*math.pi/20 #tiempo total\n",
"dt=tf/nframes #paso de tiempo\n",
"\n",
"seed_points = np.array([[0.1, 0.2, 0.3], [0.4, 0.4, 0.4]])\n",
"\n",
"\n",
"def update_plot(num):\n",
" t = dt*num\n",
" dx=t*2.0\n",
" U = 2*np.ones(np.shape(X))\n",
" #V = 2*np.cos(np.pi * X + 0.25*np.sin(20*t)) * np.sin(np.pi * Y)\n",
" V = 2*np.cos(np.pi * X) * 6*t + 0.5\n",
" ax.cla()\n",
" ax.quiver(X[::nq,::nq], Y[::nq,::nq], U[::nq,::nq], V[::nq,::nq])\n",
" ax.streamplot(X, Y, U, V, start_points=seed_points.T, color='tab:red', linewidth=2)\n",
" ax.plot(seed_points[0],seed_points[1],'go')\n",
" ax.set_xlabel(\"x\")\n",
" ax.set_ylabel(\"y\")\n",
"\n",
" return\n",
"\n",
"anim = animation.FuncAnimation(fig, update_plot, frames=nframes, interval=100, blit=False)\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"id": "pcWFTqqoS9e9",
"outputId": "b33d3542-ef11-410c-c87f-d3c60104b1c2"
},
"outputs": [
{
"data": {
"text/html": [
""
],
"text/plain": [
""
]
},
"execution_count": 2,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"HTML(anim.to_html5_video())"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "fF3A1KDjS9e-"
},
"source": [
"### Trayectoria (pathline)\n",
"\n",
"\n",
"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).\n",
"\n",
"Se calcula aplicando la definición de velocidad de una partícula fluida en un contexto lagrangiano:\n",
"\n",
"$$\\frac{d\\vec{\\bf{r}}}{dt}=\\vec{\\bf{v}}$$\n",
"\n",
"e integrando $d\\vec{\\bf{r}}=\\vec{\\bf{v}} dt$.\n",
"\n",
"\n",
"\n",
"\n",
"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)$."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"id": "hIIUr0yzS9e_",
"tags": [
"hide-cell"
]
},
"outputs": [],
"source": [
"nq=2 #separacion entre vectores en quiver plot\n",
"N=21\n",
"L=1.0\n",
"xp = np.linspace(0, L, N)\n",
"yp = np.linspace(0, L, N)\n",
"X, Y = np.meshgrid(xp, yp)\n",
"\n",
"t=0.0\n",
"\n",
"fig, ax = plt.subplots(figsize=(7, 7))\n",
"\n",
"plt.tight_layout()\n",
"\n",
"plt.close()\n",
"\n",
"nframes=100 #frames de la animacion\n",
"tf=2*math.pi/20 #tiempo total\n",
"dt=tf/nframes #paso de tiempo\n",
"\n",
"XP = np.transpose(np.array([[0.1, 0.2, 0.3], [0.4, 0.4, 0.4]]))\n",
"\n",
"U = 2*np.ones(np.shape(X))\n",
"#V = 2*np.cos(np.pi * X + 0.25*np.sin(20*t)) * np.sin(np.pi * Y)\n",
"V = 2*np.cos(np.pi * X) * 6*t + 0.5\n",
"Q = ax.quiver(X[::nq,::nq], Y[::nq,::nq], U[::nq,::nq], V[::nq,::nq])\n",
"\n",
"#ax.plot(seed_points[0],seed_points[1],'go')\n",
"P, = ax.plot(XP[:,0],XP[:,1], 'go', ms=10)\n",
"\n",
"def update_plot(num):\n",
" t = dt*num\n",
" dx=t*2.0\n",
" U = 2*np.ones(np.shape(X))\n",
" #V = 2*np.cos(np.pi * X + 0.25*np.sin(20*t)) * np.sin(np.pi * Y)\n",
" V = 2*np.cos(np.pi * X) * 6*t + 0.5\n",
" Q.set_UVC(U[::nq,::nq],V[::nq,::nq])\n",
" interpx = Rbf(X, Y, U)\n",
" interpy = Rbf(X, Y, V)\n",
" XP[:,0] = XP[:,0] + interpx(XP[:,0],XP[:,1])*dt\n",
" XP[:,1] = XP[:,1] + interpy(XP[:,0],XP[:,1])*dt\n",
" ax.plot(XP[:,0],XP[:,1], '.',color=\"tab:orange\", ms=7)\n",
" P.set_data(XP[:,0],XP[:,1])\n",
" ax.set_xlabel(\"x\")\n",
" ax.set_ylabel(\"y\")\n",
"\n",
" return\n",
"\n",
"anim = animation.FuncAnimation(fig, update_plot, frames=nframes, interval=100, blit=False)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"id": "kiGnH50kS9e_",
"outputId": "6db86b19-1892-4552-e7ec-394840211dcd"
},
"outputs": [
{
"data": {
"text/html": [
""
],
"text/plain": [
""
]
},
"execution_count": 4,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"HTML(anim.to_html5_video())"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "w_YKtxJGS9e_"
},
"source": [
"### Traza (streakline)\n",
"\n",
"\n",
"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.\n",
"\n",
"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.\n",
"\n",
"\n",
"\n",
"\n",
"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)$."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"id": "davN7FV0S9fA",
"tags": [
"hide-cell"
]
},
"outputs": [],
"source": [
"nq=2 #separacion entre vectores en quiver plot\n",
"N=21\n",
"L=1.0\n",
"xp = np.linspace(0, L, N)\n",
"yp = np.linspace(0, L, N)\n",
"X, Y = np.meshgrid(xp, yp)\n",
"\n",
"t=0.0\n",
"\n",
"fig, ax = plt.subplots(figsize=(7, 7))\n",
"\n",
"plt.tight_layout()\n",
"\n",
"plt.close()\n",
"\n",
"nframes=100 #frames de la animacion\n",
"tf=2*math.pi/20 #tiempo total\n",
"dt=tf/nframes #paso de tiempo\n",
"\n",
"XP = np.array([0.2,0.4],ndmin=2)\n",
"XP = np.repeat(XP, nframes, axis=0)\n",
"\n",
"\n",
"\n",
"U = 2*np.ones(np.shape(X))\n",
"#V = 2*np.cos(np.pi * X + 0.25*np.sin(20*t)) * np.sin(np.pi * Y)\n",
"V = 2*np.cos(np.pi * X) * 6*t + 0.5\n",
"Q = ax.quiver(X[::nq,::nq], Y[::nq,::nq], U[::nq,::nq], V[::nq,::nq])\n",
"\n",
"#ax.plot(seed_points[0],seed_points[1],'go')\n",
"P, = ax.plot(XP[0,0],XP[0,1], 'go', ms=10)\n",
"Ptraza, = ax.plot(XP[:,0],XP[:,1], '.',color='tab:green', ms=7)\n",
"\n",
"def update_plot(num):\n",
" t = dt*num\n",
" dx=t*2.0\n",
" U = 2*np.ones(np.shape(X))\n",
" #V = 2*np.cos(np.pi * X + 0.25*np.sin(20*t)) * np.sin(np.pi * Y)\n",
" V = 2*np.cos(np.pi * X) * 6*t + 0.5\n",
" Q.set_UVC(U[::nq,::nq],V[::nq,::nq])\n",
" interpx = Rbf(X, Y, U)\n",
" interpy = Rbf(X, Y, V)\n",
" ii=num\n",
" XP[0:ii,0] = XP[0:ii,0] + interpx(XP[0:ii,0],XP[0:ii,1])*dt\n",
" XP[0:ii,1] = XP[0:ii,1] + interpy(XP[0:ii,0],XP[0:ii,1])*dt\n",
" Ptraza.set_data(XP[0:ii,0],XP[0:ii,1])\n",
" P.set_data(XP[0,0],XP[0,1])\n",
" ax.set_xlabel(\"x\")\n",
" ax.set_ylabel(\"y\")\n",
"\n",
" return\n",
"\n",
"anim = animation.FuncAnimation(fig, update_plot, frames=nframes, interval=100, blit=False)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"id": "Cs_CAmI1S9fA",
"outputId": "ac8c976d-ddd2-46cc-c12f-a835732706ad"
},
"outputs": [
{
"data": {
"text/html": [
""
],
"text/plain": [
""
]
},
"execution_count": 6,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"HTML(anim.to_html5_video())"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "6wiLxXJRS9fA"
},
"source": [
"#### Comparación de trazas y trayectorias\n",
"\n",
"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."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"id": "Si2KctBKS9fB",
"tags": [
"hide-cell"
]
},
"outputs": [],
"source": [
"nq=2 #separacion entre vectores en quiver plot\n",
"N=21\n",
"L=1.0\n",
"xp = np.linspace(0, L, N)\n",
"yp = np.linspace(0, L, N)\n",
"X, Y = np.meshgrid(xp, yp)\n",
"\n",
"t=0.0\n",
"\n",
"fig, ax = plt.subplots(figsize=(7, 7))\n",
"\n",
"plt.tight_layout()\n",
"\n",
"plt.close()\n",
"\n",
"nframes=100 #frames de la animacion\n",
"tf=2*math.pi/20 #tiempo total\n",
"dt=tf/nframes #paso de tiempo\n",
"\n",
"XP = np.array([0.2,0.4],ndmin=2)\n",
"XP = np.repeat(XP, nframes, axis=0)\n",
"\n",
"\n",
"\n",
"U = 2*np.ones(np.shape(X))\n",
"#V = 2*np.cos(np.pi * X + 0.25*np.sin(20*t)) * np.sin(np.pi * Y)\n",
"V = 2*np.cos(np.pi * X) * 6*t + 0.5\n",
"Q = ax.quiver(X[::nq,::nq], Y[::nq,::nq], U[::nq,::nq], V[::nq,::nq])\n",
"\n",
"#ax.plot(seed_points[0],seed_points[1],'go')\n",
"P, = ax.plot(XP[0,0],XP[0,1], 'go', ms=10)\n",
"Ptraza, = ax.plot(XP[:,0],XP[:,1], '.',color='tab:green', ms=7)\n",
"\n",
"seed_points = np.array([[0.2], [0.4]])\n",
"\n",
"\n",
"def update_plot(num):\n",
" t = dt*num\n",
" dx=t*2.0\n",
" U = 2*np.ones(np.shape(X))\n",
" #V = 2*np.cos(np.pi * X + 0.25*np.sin(20*t)) * np.sin(np.pi * Y)\n",
" V = 2*np.cos(np.pi * X) * 6*t + 0.5\n",
" Q.set_UVC(U[::nq,::nq],V[::nq,::nq])\n",
" interpx = Rbf(X, Y, U)\n",
" interpy = Rbf(X, Y, V)\n",
" ii=num\n",
" XP[0:ii,0] = XP[0:ii,0] + interpx(XP[0:ii,0],XP[0:ii,1])*dt\n",
" XP[0:ii,1] = XP[0:ii,1] + interpy(XP[0:ii,0],XP[0:ii,1])*dt\n",
" #ax.streamplot(X, Y, U, V, start_points=seed_points.T, color='tab:red', linewidth=2)\n",
" ax.plot(XP[0,0],XP[0,1], '.',color=\"tab:orange\", ms=7)\n",
" Ptraza.set_data(XP[0:ii,0],XP[0:ii,1])\n",
" P.set_data(XP[0,0],XP[0,1])\n",
" ax.set_xlabel(\"x\")\n",
" ax.set_ylabel(\"y\")\n",
"\n",
" return\n",
"\n",
"anim = animation.FuncAnimation(fig, update_plot, frames=nframes, interval=100, blit=False)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"id": "7FCeTV9SS9fB",
"outputId": "761cb2df-457d-4de0-bd61-f00b62c978f5"
},
"outputs": [
{
"data": {
"text/html": [
""
],
"text/plain": [
""
]
},
"execution_count": 8,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"HTML(anim.to_html5_video())"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "EiuhU8CWS9fB"
},
"source": [
"Pero... **¿qué ocurrirá cuando tengamos un flujo estacionario?**\n",
"\n",
"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**."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"id": "cY0viK9PS9fB",
"tags": [
"hide-cell"
]
},
"outputs": [],
"source": [
"nq=2 #separacion entre vectores en quiver plot\n",
"N=21\n",
"L=1.0\n",
"xp = np.linspace(0, L, N)\n",
"yp = np.linspace(0, L, N)\n",
"X, Y = np.meshgrid(xp, yp)\n",
"\n",
"t=0.0\n",
"\n",
"fig, ax = plt.subplots(figsize=(7, 7))\n",
"\n",
"plt.tight_layout()\n",
"\n",
"plt.close()\n",
"\n",
"nframes=100 #frames de la animacion\n",
"tf=2*math.pi/20 #tiempo total\n",
"dt=tf/nframes #paso de tiempo\n",
"\n",
"XP = np.array([0.2,0.4],ndmin=2)\n",
"XP = np.repeat(XP, nframes, axis=0)\n",
"\n",
"\n",
"\n",
"U = 2*np.ones(np.shape(X))\n",
"#V = 2*np.cos(np.pi * X + 0.25*np.sin(20*t)) * np.sin(np.pi * Y)\n",
"V = 2*np.cos(np.pi * X) + 0.5\n",
"Q = ax.quiver(X[::nq,::nq], Y[::nq,::nq], U[::nq,::nq], V[::nq,::nq])\n",
"\n",
"#ax.plot(seed_points[0],seed_points[1],'go')\n",
"P, = ax.plot(XP[0,0],XP[0,1], 'go', ms=10)\n",
"Ptraza, = ax.plot(XP[:,0],XP[:,1], '.',color='tab:green', ms=8)\n",
"\n",
"seed_points = np.array([[0.2], [0.4]])\n",
"\n",
"ax.streamplot(X, Y, U, V, start_points=seed_points.T, color='tab:red', linewidth=2)\n",
"\n",
"def update_plot(num):\n",
" t = dt*num\n",
" dx=t*2.0\n",
" U = 2*np.ones(np.shape(X))\n",
" #V = 2*np.cos(np.pi * X + 0.25*np.sin(20*t)) * np.sin(np.pi * Y)\n",
" V = 2*np.cos(np.pi * X) + 0.5\n",
" Q.set_UVC(U[::nq,::nq],V[::nq,::nq])\n",
" interpx = Rbf(X, Y, U)\n",
" interpy = Rbf(X, Y, V)\n",
" ii=num\n",
" XP[0:ii,0] = XP[0:ii,0] + interpx(XP[0:ii,0],XP[0:ii,1])*dt\n",
" XP[0:ii,1] = XP[0:ii,1] + interpy(XP[0:ii,0],XP[0:ii,1])*dt\n",
" #ax.streamplot(X, Y, U, V, start_points=seed_points.T, color='tab:red', linewidth=2)\n",
" ax.plot(XP[0,0],XP[0,1], '.',color=\"tab:orange\", ms=5)\n",
" Ptraza.set_data(XP[0:ii,0],XP[0:ii,1])\n",
" P.set_data(XP[0,0],XP[0,1])\n",
" ax.set_xlabel(\"x\")\n",
" ax.set_ylabel(\"y\")\n",
"\n",
" return\n",
"\n",
"anim2 = animation.FuncAnimation(fig, update_plot, frames=nframes, interval=100, blit=False)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"id": "Z_n6mwiGS9fC",
"outputId": "88274e5e-07cf-462f-e735-857d24412f2e"
},
"outputs": [
{
"data": {
"text/html": [
""
],
"text/plain": [
""
]
},
"execution_count": 10,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"HTML(anim2.to_html5_video())"
]
}
],
"metadata": {
"celltoolbar": "Edit Metadata",
"colab": {
"provenance": []
},
"kernelspec": {
"display_name": "Python 3",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.7.6"
}
},
"nbformat": 4,
"nbformat_minor": 1
}