Ecuación de Calor Unidimensional en Python

La ecuación de calor unidimensional es una ecuación diferencial parcial de tipo parabólica y esta dada por la siguiente ecuación correspondiente.

$${\large \begin{align*} \frac{\partial U(x,t)}{\partial t} &= \alpha \frac{\partial^2 U(x,t)}{\partial x^2} \end{align*}}$$

Donde ${\large \alpha }$ es la constante de difusión o de conducción de calor.


Conducción de Calor


Solución Analítica de la EDP de Calor 1D

Usando el método de separación de variables 

${\large U(x,t) = X(x) \cdot T(t) }$ 

Reemplazando U(x,t) en la EDP de calor unidimensional

$${\large \begin{align*} \frac{\partial U(x,t)}{\partial t} &= \alpha \frac{\partial^2 U(x,t)}{\partial x^2} \\ X(x)T^{'}(t) &= \alpha X^{''}(x)T(t) \\ \frac{T^{'}(t)}{T(t)} &= \alpha \frac{X^{''}(x)}{X(x)} \end{align*} }$$

  • Igualando a una constante ${\large k > 0 }$

Para el tiempo
$${\large \begin{align*} \frac{T^{'}(t)}{T(t)} &= k \\ T^{'}(t) &= kT(t) \\ \frac{dT(t)}{dt} &= kT(t) \\ \ln{(T(t))} &= kt \\ T(t) &= Ae^{kt} \end{align*} }$$

Para la posición
$${\large \begin{align*} \alpha \frac{X^{''}(x)}{X(x)} &= k \\ X^{''}(x) &= (k/\alpha)X(x) \\ \frac{d^2 X(x)}{dx^2} &= \frac{k}{\alpha}X(x) \\ X(x) &= C_{1}e^{x\sqrt{k/\alpha}} + C_{2}e^{-x\sqrt{k/\alpha}} \end{align*} }$$

Por consiguiente, la solución general viene dada por:
$${\large \begin{align*} U(x,t) &= B_{1}e^{x\sqrt{k/\alpha} + kt} + B_{2}e^{-x\sqrt{k/\alpha} + kt} \end{align*} }$$


  • Igualando a una constante ${\large k > 0 }$

Para el tiempo
$${\large \begin{align*} \frac{T^{'}(t)}{T(t)} &= -k \\ T^{'}(t) &= -kT(t) \\ \frac{dT(t)}{dt} &= -kT(t) \\ \ln{(T(t))} &= -kt \\ T(t) &= Ae^{-kt} \end{align*} }$$

Para la posición
$${\large \begin{align*} \alpha \frac{X^{''}(x)}{X(x)} &= -k \\ X^{''}(x) &= (-k/\alpha)X(x) \\ \frac{d^2 X(x)}{dx^2} &= -\frac{k}{\alpha}X(x) \\ X(x) &= C_{1}\cos{\left(\sqrt{\frac{k}{\alpha}}x\right)} + C_{2}\sin{\left(\sqrt{\frac{k}{\alpha}}x\right)} \end{align*}}$$

Por consiguiente, la solución general viene dada por:

$${\large \begin{align*} U(x,t) &= e^{-kt}\left( B_{1}\cos{\left(\sqrt{\frac{k}{\alpha}}x\right)} + B_{2}\sin{\left(\sqrt{\frac{k}{\alpha}}x\right)} \right) \end{align*} }$$


Para encontrar las constantes presentas en la solución general, ya sea para un k negativo o para un k positivo. Entonces la EDP de calor unidimensional debe cumplir ciertas condiciones de frontera respectivamente. Por ejemplo: 


Problema con condiciones de Dirichlet
$${\large \begin{align*} \frac{\partial U(x,t)}{\partial t} &= \alpha \frac{\partial^2 U(x,t)}{\partial x^2} \end{align*}}$$

Donde:
${\large U(0,t) = 0}$
${\large U(L,t) = 0}$
${\large U(x,0) = f(x)}$

la solución de la EDP de calor es la siguiente:

$${\large \begin{align*} U(x,t) &= \sum_{n=1}^{\infty} \left[ B_{n}\exp{\left(-\frac{n^2 \pi^2 \alpha }{L^2}t\right)} \sin{\left(\frac{n\pi x}{L}\right)} \right] \hspace{1cm} \wedge \hspace{1cm} B_{n} = \frac{2}{L}\int_{0}^{L} \left( f(x) \sin{\left(\frac{n\pi x}{L}\right)} \right) dx \end{align*} }$$

Ahora una simulación con constante de difusión ${\large \alpha = 1 }$

EDP de calor 1D con $ U(x,0) = x^2 $ para 50 términos de la serie

El código para realizar la animación en Python es el siguiente

import numpy as np
from sympy import *
import sympy as sym
from sympy.abc import j, x
import matplotlib.pyplot as plt
from celluloid import Camera
from IPython.display import HTML

def calorD(alpha, L, n, fx, T):
    
    """ alpha: es la constante de difusión
        L: es la longitud de la varilla 
        n: es el número de términos de la serie
        fx: es la condición inicial 
        T: es el tiempo de simulación """
    
    fig = plt.figure(figsize=(14,6))
    camera = Camera(fig)
    
    init_printing(use_unicode=True)
    params = {'xtick.labelsize': 20, 'ytick.labelsize': 20}
    plt.rcParams.update(params)
    
    X =np.linspace(0, L, 1000, endpoint=True)
    
    bj = (2/L)*sym.integrate(fx*sym.sin(j*sym.pi*x/L),(x,0,L))
    dominio = np.arange(1,n+1,1)
    bn = sym.lambdify(j, bj, np)
    fn = bn(dominio)
        
    t = np.linspace(0, T, 100, endpoint=True)
    
    for l in range(101):
        u = 0
        for i in range(1, n+1):
            k = (alpha*(i*np.pi/L)**2) 
            u += fn[i-1]*np.exp(-k*t[l-1])*np.sin(i*np.pi*X/L)
        plt.plot(X, u, color='k', lw=5)
        plt.text(0, 0.8*max(u), 't = {:.2f} s'.format(t[l-1]), color='b', size=25)
        camera.snap()
    
    plt.xlabel("x", fontsize = 25)
    plt.title("Ecuación de Calor 1D", fontsize=25)
    plt.ylabel("U(x,t)", fontsize = 25, rotation=0, labelpad=35)
    
    animation = camera.animate(interval=100, blit=True)
    return HTML(animation.to_html5_video()) 


Problema con condiciones de Neumann
$${\large \begin{align*} \frac{\partial U(x,t)}{\partial t} &= \alpha \frac{\partial^2 U(x,t)}{\partial x^2} \end{align*}}$$

Donde:
${\large U_{x}(0,t) = 0}$
${\large U_{x}(L,t) = 0}$
${\large U(x,0) = f(x)}$

la solución de la EDP de calor es la siguiente:

$${\large \begin{align*} U(x,t) &= \sum_{n=1}^{\infty} \left[ B_{n}\exp{\left(-\frac{n^2 \pi^2 \alpha }{L^2}t\right)} \cos{\left(\frac{n\pi x}{L}\right)} \right] \hspace{1cm} \wedge \hspace{1cm} B_{n} = \frac{2}{L}\int_{0}^{L} \left( f(x) \cos{\left(\frac{n\pi x}{L}\right)} \right) dx \end{align*} }$$

Ahora una simulación con constante de difusión ${\large \alpha = 1 }$

EDP de calor 1D con $ U(x,0) = x $ para 70 términos de la serie.

El código para realizar la animación en Python es el siguiente

import numpy as np
from sympy import *
import sympy as sym
from sympy.abc import j, x
import matplotlib.pyplot as plt
from celluloid import Camera
from IPython.display import HTML

def calorN(alpha, L, n, fx, T):
    
    """ alpha: es la constante de difusión
        L: es la longitud de la varilla 
        n: es el número de terminos de la serie
        fx: es la condición inicial 
        T: es el tiempo de simulación """
    
    fig = plt.figure(figsize=(14,6))
    camera = Camera(fig)
    
    init_printing(use_unicode=True)
    params = {'xtick.labelsize': 20, 'ytick.labelsize': 20}
    plt.rcParams.update(params)
    
    X =np.linspace(0, L, 1000, endpoint=True)
    
    bj = (2/L)*sym.integrate(fx*sym.cos(j*sym.pi*x/L),(x,0,L))
    dominio = np.arange(1,n+1,1)
    bn = sym.lambdify(j, bj, np)
    fn = bn(dominio)
        
    t = np.linspace(0, T, 100, endpoint=True)
    
    for l in range(101):
        u = 0
        for i in range(1, n+1):
            k = (alpha*(i*np.pi/L)**2) 
            u += fn[i-1]*np.exp(-k*t[l-1])*np.cos(i*np.pi*X/L)
        plt.plot(X, u, color='k', lw=5)
        plt.text(0, 0.8*max(u), 't = {:.2f} s'.format(t[l-1]), color='b', size=25)
        camera.snap()
    
    plt.xlabel("x", fontsize = 25)
    plt.title("Ecuación de Calor 1D", fontsize=25)
    plt.ylabel("U(x,t)", fontsize = 25, rotation=0, labelpad=30)
    
    animation = camera.animate(interval=100, blit=True)
    return HTML(animation.to_html5_video()) 


Comentarios