7.1.4 EDP Parabólicas método implícito – analítico con Sympy-Python

Referencia:  Chapra 30.3 p893 pdf917, Burden 9Ed 12.2 p729, Rodriguez 10.2.4 p415

Para el ejercicio desarrollado en forma analítica como EDP Parabólicas método implícito, se desarrollan los pasos esta vez  usando Sympy

\frac{\partial ^2 u}{\partial x ^2} = K\frac{\partial u}{\partial t}

Las diferencias finitas centradas y hacia atras se definen en el diccionario dif_dividida.

Para cada valor de j, se crean las ecuaciones desde la forma discreta de la EDP, moviendo a la derecha los valores conocidos para generar un sistema de ecuaciones A.X=B. Se resuelve el sistema de ecuaciones  y se actualiza la matriz u[i,j] de resultados.

 EDP=0  :
                    2             
    d              d              
- K*--(u(x, t)) + ---(u(x, t)) = 0
    dt              2             
                  dx              

 Lambda L  :
  Dt 
-----
  2  
Dx *K

 discreta = 0  :
-2*U(i, j) + U(i - 1, j) + U(i + 1, j)   K*(U(i, j) - U(i, j - 1))
-------------------------------------- - -------------------------
                   2                                 Dt           
                 Dx                                               

 discreta_L = 0  :
L*U(i - 1, j) + L*U(i + 1, j) + (-2*L - 1)*U(i, j) + U(i, j - 1) = 0

Ecuaciones de iteraciones j:
 j = 1
-1.5*U(1, 1) + 0.25*U(2, 1) = -40.0
0.25*U(1, 1) - 1.5*U(2, 1) + 0.25*U(3, 1) = -25.0
0.25*U(2, 1) - 1.5*U(3, 1) + 0.25*U(4, 1) = -25.0
0.25*U(3, 1) - 1.5*U(4, 1) + 0.25*U(5, 1) = -25.0
0.25*U(4, 1) - 1.5*U(5, 1) + 0.25*U(6, 1) = -25.0
0.25*U(5, 1) - 1.5*U(6, 1) + 0.25*U(7, 1) = -25.0
0.25*U(6, 1) - 1.5*U(7, 1) + 0.25*U(8, 1) = -25.0
0.25*U(7, 1) - 1.5*U(8, 1) + 0.25*U(9, 1) = -25.0
0.25*U(8, 1) - 1.5*U(9, 1) = -35.0
 j = 2
-1.5*U(1, 2) + 0.25*U(2, 2) = -46.0050525095364
0.25*U(1, 2) - 1.5*U(2, 2) + 0.25*U(3, 2) = -26.0303150572187
0.25*U(2, 2) - 1.5*U(3, 2) + 0.25*U(4, 2) = -25.1768378337756
0.25*U(3, 2) - 1.5*U(4, 2) + 0.25*U(5, 2) = -25.030711945435
0.25*U(4, 2) - 1.5*U(5, 2) + 0.25*U(6, 2) = -25.0074338388344
0.25*U(5, 2) - 1.5*U(6, 2) + 0.25*U(7, 2) = -25.0138910875712
0.25*U(6, 2) - 1.5*U(7, 2) + 0.25*U(8, 2) = -25.0759126865931
0.25*U(7, 2) - 1.5*U(8, 2) + 0.25*U(9, 2) = -25.4415850319874
0.25*U(8, 2) - 1.5*U(9, 2) = -37.5735975053312
 j = 3
-1.5*U(1, 3) + 0.25*U(2, 3) = -50.2512763902537
0.25*U(1, 3) - 1.5*U(2, 3) + 0.25*U(3, 3) = -27.4874483033763
0.25*U(2, 3) - 1.5*U(3, 3) + 0.25*U(4, 3) = -25.5521532011296
0.25*U(3, 3) - 1.5*U(4, 3) + 0.25*U(5, 3) = -25.1181195682987
0.25*U(4, 3) - 1.5*U(5, 3) + 0.25*U(6, 3) = -25.0337164269226
0.25*U(5, 3) - 1.5*U(6, 3) + 0.25*U(7, 3) = -25.0544436378994
0.25*U(6, 3) - 1.5*U(7, 3) + 0.25*U(8, 3) = -25.2373810501889
0.25*U(7, 3) - 1.5*U(8, 3) + 0.25*U(9, 3) = -26.0661919168616
0.25*U(8, 3) - 1.5*U(9, 3) = -39.3934303230311
 j = 4
-1.5*U(1, 4) + 0.25*U(2, 4) = -53.3449104170748
0.25*U(1, 4) - 1.5*U(2, 4) + 0.25*U(3, 4) = -29.0643569414341
0.25*U(2, 4) - 1.5*U(3, 4) + 0.25*U(4, 4) = -26.0914380180247
0.25*U(3, 4) - 1.5*U(4, 4) + 0.25*U(5, 4) = -25.2756583621956
0.25*U(4, 4) - 1.5*U(5, 4) + 0.25*U(6, 4) = -25.0900338819544
0.25*U(5, 4) - 1.5*U(6, 4) + 0.25*U(7, 4) = -25.1296792218401
0.25*U(6, 4) - 1.5*U(7, 4) + 0.25*U(8, 4) = -25.4702668974885
0.25*U(7, 4) - 1.5*U(8, 4) + 0.25*U(9, 4) = -26.7423979623353
0.25*U(8, 4) - 1.5*U(9, 4) = -40.7193532090766

Solución u:
[[60.         60.         60.         60.         60.        ]
 [25.         31.00505251 35.25127639 38.34491042 40.6652135 ]
 [25.         26.03031506 27.4874483  29.06435694 30.61163935]
 [25.         25.17683783 25.5521532  26.09143802 26.74719483]
 [25.         25.03071195 25.11811957 25.27565836 25.50577757]
 [25.         25.00743384 25.03371643 25.09003388 25.18483712]
 [25.         25.01389109 25.05444364 25.12967922 25.24310963]
 [25.         25.07591269 25.23738105 25.4702669  25.75510376]
 [25.         25.44158503 26.06619192 26.74239796 27.40644533]
 [25.         27.57359751 29.39343032 30.71935321 31.71397636]
 [40.         40.         40.         40.         40.        ]]
>>> 

Instrucciones en Python

El desarrollo de la ecuación en forma discreta se realizó en EDP Parabólica método explícito – analítico con Sympy-Python, por lo que se incorpora a éste algoritmo como una función, desde donde se inicia los parsos para el método implicito.

Compare los resultados con el método numérico y mida los tiempos de ejecución de cada algoritmo para escribir sus observaciones.

# Ecuaciones Diferenciales Parciales Parabólicas
# método implícito con Sympy
import numpy as np
import sympy as sym

# variables continuas
t = sym.Symbol('t',real=True)
x = sym.Symbol('x',real=True)
u = sym.Function('u')(x,t)
K = sym.Symbol('K',real=True)
# variables discretas
i  = sym.Symbol('i',integer=True,positive=True)
j  = sym.Symbol('j',integer=True,positive=True)
Dx = sym.Symbol('Dx',real=True,positive=True)
Dt = sym.Symbol('Dt',real=True,positive=True)
L  = sym.Symbol('L',real=True)
U  = sym.Function('U')(i,j)

# INGRESO
# ecuacion: LHS=RHS
LHS = sym.diff(u,x,2)
RHS = K*sym.diff(u,t,1)
EDP = sym.Eq(LHS-RHS,0)

dif_dividida ={sym.diff(u,x,2): (U.subs(i,i-1)-2*U+U.subs(i,i+1))/(Dx**2),
               sym.diff(u,t,1): (U - U.subs(j,j-1))/Dt}

# parametros
Ta = 60 ; Tb = 40 ; T0 = 25
K_ = 4
x_a = 0 ; x_b = 1
x_tramos = 10
t_a = 0 # t_b depende de t_tramos y dt
t_tramos = 4

dx  = (x_b-x_a)/x_tramos
dt  = dx/10
L_k = dt/(dx**2)/K_

# PROCEDIMIENTO
def edp_discreta(EDP,dif_dividida):
    ''' EDP contínua y diferencias divididas a usar
    '''
    desarrollo={}
    # expresión a la izquierda LHS (Left Hand side)
    if EDP.rhs!=0:
        LHS = EDP.lhs
        RHS = EDP.rhs
        EDP = sym.Eq(LHS-RHS,0)
    desarrollo['EDP=0'] = EDP

    lamb = Dt/K/(Dx**2)
    desarrollo['Lambda L'] = lamb

    discreta = EDP.lhs # EDP discreta
    for derivada in dif_dividida:
        discreta = discreta.subs(derivada,dif_dividida[derivada])
    desarrollo['discreta = 0'] = discreta

    # simplifica con lambda L
    discreta_L = sym.expand(discreta*Dt/K)
    discreta_L = discreta_L.subs(Dt/K/(Dx**2),L)
    discreta_L = sym.collect(discreta_L,U)
    discreta_L = sym.Eq(discreta_L,0)

    desarrollo['discreta_L = 0'] = discreta_L
    return(desarrollo)

edp_discreta = edp_discreta(EDP,dif_dividida)

# u[x,t] resultados, valores iniciales
xi = np.arange(x_a,x_b+dx,dx)
ti = np.arange(t_a,t_a+t_tramos*dt+dt,dt)

uxt = np.zeros(shape=(x_tramos+1,t_tramos+1),
               dtype=float)
uxt = uxt*np.nan
uxt[0,:] = Ta
uxt[1:x_tramos+1,0] = T0
uxt[x_tramos,:] = Tb


# EDP parabólicas método implícito 
# conoce valor de uxt[i,j] 
conoce = np.zeros(shape=(x_tramos+1,t_tramos+1),
                  dtype=bool)
conoce[:,0]  = 1 # inferior
conoce[0,:]  = 1 # izquierda
conoce[-1,:] = 1 # derecha

# ecuaciones por iteracion j
eq_itera = []
for jk in range(1,t_tramos+1,1):
    eq_jk = []
    for ik in range(1,x_tramos,1):
        LHS = edp_discreta['discreta_L = 0'].lhs
        RHS = 0*t
        LHS = LHS.subs([(i,ik),(j,jk),(L,L_k)])
        # revisa terminos de suma
        term_suma = sym.Add.make_args(LHS)
        for term_k in term_suma:
            cambiar = False
            cambiar_valor = 0 ; cambiar_factor = 0
            # revisa factores en termino de suma
            factor_Mul = sym.Mul.make_args(term_k)
            for factor_k in factor_Mul:
                if factor_k.is_Function: # es U(i,j)
                    [i_k,j_k] = factor_k.args
                    cambiar = conoce[i_k,j_k]
                    cambiar_factor = factor_k
                    cambiar_valor = uxt[i_k,j_k]
            # U(i,j)conocidos pasan a la derecha
            if cambiar: 
                RHS = RHS-term_k
                RHS = RHS.subs(cambiar_factor,
                               cambiar_valor)
                LHS = LHS-term_k
        # agrupa ecuaciones de iteración i
        eq_jk.append(sym.Eq(LHS,RHS))

    # resuelve sistema ecuaciones A.X=B
    X_jk = sym.solve(eq_jk)[0]
    
    # actualiza uxt(i,j) , conoce[i,j]
    for nodoij in X_jk:
        [i_k,j_k] = nodoij.args
        uxt[i_k,j_k] = X_jk[nodoij]
        conoce[i_k,j_k] = True

    # agrupa ecuaciones por cada j
    eq_itera.append(eq_jk)
        
# SALIDA
for k in edp_discreta:
    print('\n',k,' :')
    sym.pprint(edp_discreta[k])
print('\nEcuaciones de iteraciones j:')
for jk in range(0,t_tramos,1):
    print(' j =',jk+1)
    for una_eq in eq_itera[jk]:
        sym.pprint(una_eq)
print('\nSolución u:')
print(uxt)