5. EDP Parabólica - Método implícito con Sympy
Para el ejercicio presentado en el numeral 1, se desarrolla el método implícito.
Las diferencias finitas centradas y hacia atrás se definen en el diccionario dif_dividida.
Para desarrollar la expresión discreta, se incorpora la función edp_sustituye_L(), que busca el en cada término de la suma los componentes de λ y los sustituye por la variable L.
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.
Por la extensión de los pasos a mostrar, para las iteraciones en cada fila, solo se presentan para la primera fila. En caso de requerir observar más filas, puede cambiar el parámetro en el bloque de salida, en el condicional:
elif (tipo==dict):
if entrada<2
Los resultados para el algoritmo son:
EDP Parabólica contínua a discreta
ordenDx : 2
ordenDy : 1
edp=0 :
2
∂ ∂
───(u(x, y)) - 4⋅──(u(x, y)) = 0
2 ∂y
∂x
K_ : 4
Lambda_L :
Dy
─────
2
4⋅Dx
Lambda L_k : 0.250000000000000
(discreta=0)*Dy**ordeny :
2⋅Dy⋅U(i, j) Dy⋅U(i - 1, j) Dy⋅U(i + 1, j)
-4⋅U(i, j) + 4⋅U(i, j - 1) - ──────────── + ────────────── + ──────────────
2 2 2
Dx Dx Dx
discreta_L=0 :
4⋅L⋅U(i - 1, j) + 4⋅L⋅U(i + 1, j) + (-8⋅L - 4)⋅U(i, j) + 4⋅U(i, j - 1) = 0
discreta :
-6⋅U(i, j) + 4⋅U(i, j - 1) + U(i - 1, j) + U(i + 1, j) = 0
Método implícito EDP Parabólica
j: 1 ; i: 1
U(0, 1) + 4⋅U(1, 0) - 6⋅U(1, 1) + U(2, 1) = 0
-6⋅U(1, 1) + U(2, 1) = -160.0
j: 1 ; i: 2
U(1, 1) + 4⋅U(2, 0) - 6⋅U(2, 1) + U(3, 1) = 0
U(1, 1) - 6⋅U(2, 1) + U(3, 1) = -100.0
j: 1 ; i: 3
U(2, 1) + 4⋅U(3, 0) - 6⋅U(3, 1) + U(4, 1) = 0
U(2, 1) - 6⋅U(3, 1) + U(4, 1) = -100.0
j: 1 ; i: 4
U(3, 1) + 4⋅U(4, 0) - 6⋅U(4, 1) + U(5, 1) = 0
U(3, 1) - 6⋅U(4, 1) + U(5, 1) = -100.0
j: 1 ; i: 5
U(4, 1) + 4⋅U(5, 0) - 6⋅U(5, 1) + U(6, 1) = 0
U(4, 1) - 6⋅U(5, 1) + U(6, 1) = -100.0
j: 1 ; i: 6
U(5, 1) + 4⋅U(6, 0) - 6⋅U(6, 1) + U(7, 1) = 0
U(5, 1) - 6⋅U(6, 1) + U(7, 1) = -100.0
j: 1 ; i: 7
U(6, 1) + 4⋅U(7, 0) - 6⋅U(7, 1) + U(8, 1) = 0
U(6, 1) - 6⋅U(7, 1) + U(8, 1) = -100.0
j: 1 ; i: 8
U(7, 1) + 4⋅U(8, 0) - 6⋅U(8, 1) + U(9, 1) = 0
U(7, 1) - 6⋅U(8, 1) + U(9, 1) = -100.0
j: 1 ; i: 9
U(8, 1) + 4⋅U(9, 0) - 6⋅U(9, 1) + U(10, 1) = 0
U(8, 1) - 6⋅U(9, 1) = -140.0
A :
[[-6. 1. 0. 0. 0. 0. 0. 0. 0.]
[ 1. -6. 1. 0. 0. 0. 0. 0. 0.]
[ 0. 1. -6. 1. 0. 0. 0. 0. 0.]
[ 0. 0. 1. -6. 1. 0. 0. 0. 0.]
[ 0. 0. 0. 1. -6. 1. 0. 0. 0.]
[ 0. 0. 0. 0. 1. -6. 1. 0. 0.]
[ 0. 0. 0. 0. 0. 1. -6. 1. 0.]
[ 0. 0. 0. 0. 0. 0. 1. -6. 1.]
[ 0. 0. 0. 0. 0. 0. 0. 1. -6.]]
B :
[-160. -100. -100. -100. -100. -100. -100. -100. -140.]
X :
[31.01 26.03 25.18 25.03 25.01 25.01 25.08 25.44 27.57]
Resultados para U(x,y)
xi: [0. 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1. ]
yj: [0. 0.01 0.02 0.03 0.04]
j, U[i,j]
4 [60. 40.67 30.61 26.75 25.51 25.18 25.24 25.76 27.41 31.71 40. ]
3 [60. 38.34 29.06 26.09 25.28 25.09 25.13 25.47 26.74 30.72 40. ]
2 [60. 35.25 27.49 25.55 25.12 25.03 25.05 25.24 26.07 29.39 40. ]
1 [60. 31.01 26.03 25.18 25.03 25.01 25.01 25.08 25.44 27.57 40. ]
0 [60. 25. 25. 25. 25. 25. 25. 25. 25. 25. 40.]
6. Algoritmo en Python
Para el método implícito se añade la sección donde se reemplazan los valores en los bordes y en la barra en la matriz U(x,y) en el algoritmo identificada como u_xy . Para diferenciar si el valor existe se usa la matriz de estados u_mask con estados True/False.
También se incorpora la función edp_sustituyeValorU() que para cada término suma de la ecuación busca en la matriz u_xy si el valor U(i,j) existe y lo cambia. Luego pasa el valor al lado derecho de la ecuación par formar el sistema de ecuaciones.
Con los valores existentes antes de la iteración, se obtienen las ecuaciones por cada punto i,j de una fila para el sistema de ecuaciones en la forma A.x=B. El sistema se resuelve con las instrucciones de Numpy. El resultado se actualiza en la matriz u_xy y la matriz u_mask para indicar que el valor ya está disponible.
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
# EDP Parabólicas contínua a discreta con Sympy
import numpy as np
import sympy as sym
# u(x,y) funciones continuas y variables simbólicas usadas
t = sym.Symbol('t',real=True) # independiente
x = sym.Symbol('x',real=True)
y = sym.Symbol('y',real=True)
u = sym.Function('u')(x,y) # funcion
f = sym.Function('f')(x,y) # funcion complemento
K = sym.Symbol('K',real=True)
# U[i,j] funciones discretas y variables simbólicas usadas
i = sym.Symbol('i',integer=True,positive=True) # indices
j = sym.Symbol('j',integer=True,positive=True)
Dt = sym.Symbol('Dt',real=True,positive=True)
Dx = sym.Symbol('Dx',real=True,positive=True)
Dy = sym.Symbol('Dy',real=True,positive=True)
L = sym.Symbol('L',real=True)
U = sym.Function('U')(i,j)
# INGRESO
fxy = 0*x+0*y # f(x,y) = 0 , ecuacion complementaria
# ecuacion edp : LHS=RHS
LHS = sym.diff(u,x,2)
RHS = 4*sym.diff(u,y,1) + fxy
edp = sym.Eq(LHS-RHS,0)
# centrada, atras
dif_dividida ={sym.diff(u,x,2): (U.subs(i,i-1)-2*U+U.subs(i,i+1))/(Dx**2),
sym.diff(u,y,1): (U - U.subs(j,j-1))/Dy}
buscar = U.subs(j,j+1) # U[i,j+1] método explícito
# Valores de frontera
fya = lambda y: 60 +0*y # izquierda
fyb = lambda y: 40 +0*y # derecha
fxc = lambda x: 25 +0*x # inferior, función inicial
# [a,b] dimensiones de la barra
a = 0 # longitud en x
b = 1
y0 = 0 # tiempo inicial, aumenta con dt en n iteraciones
# muestreo en ejes, discreto
x_tramos = 10
dx = (b-a)/x_tramos
dy = dx/10
n_iteraciones = 4 # iteraciones en tiempo
verdigitos = 2 # para mostrar en tabla de resultados
casicero = 1e-15 # para redondeo de términos en ecuacion
# PROCEDIMIENTO
def edp_coef_Dx(edp,x,ordenx):
''' Extrae el coeficiente de la derivada Dx de ordenx,
edp es la ecuación como lhs=rhs
'''
coeff_x = 1.0 # valor inicial
# separa cada término de suma
term_suma = sym.Add.make_args(edp.lhs)
for term_k in term_suma:
if term_k.is_Mul: # mas de un factor
factor_Mul = sym.Mul.make_args(term_k)
coeff_temp = 1; coeff_usar = False
# separa cada factor de término
for factor_k in factor_Mul:
if not(factor_k.is_Derivative):
coeff_temp = coeff_temp*factor_k
else: # factor con derivada de ordenx
partes = factor_k.args
if partes[1]==(x,ordenx):
coeff_usar = True
if coeff_usar==True:
coeff_x = coeff_x*coeff_temp
return(coeff_x)
def redondea_coef(ecuacion, precision=6,casicero = 1e-15):
''' redondea coeficientes de términos suma de una ecuacion
ecuación como lhs=rhs
'''
tipo = type(ecuacion)
tipo_eq = False
if tipo == sym.core.relational.Equality:
RHS = ecuacion.rhs # separa lado derecho
ecuacion = ecuacion.lhs # analiza lado izquierdo
tipo = type(ecuacion)
tipo_eq = True
if tipo == sym.core.add.Add: # términos suma de ecuacion
term_sum = sym.Add.make_args(ecuacion)
ecuacion = sym.S.Zero # vacia
for term_k in term_sum:
# factor multiplicativo de termino suma
term_mul = sym.Mul.make_args(term_k)
producto = sym.S.One
for factor in term_mul:
if not(factor.has(sym.Symbol)): # es numerico
factor = np.around(float(factor),precision)
if (abs(factor)%1)<casicero: # si es entero
factor = int(factor)
producto = producto*factor
ecuacion = ecuacion + producto
if tipo == sym.core.mul.Mul: # termino único, busca factores
term_mul = sym.Mul.make_args(ecuacion)
producto = sym.S.One
for factor in term_mul:
if not(factor.has(sym.Symbol)): # es numerico
factor = np.around(float(factor),precision)
if (abs(factor)%1)<casicero: # si es entero
factor = int(factor)
producto = producto*factor
ecuacion = producto
if tipo == float: # solo un numero
if (abs(ecuacion)%1)<casicero:
ecuacion = int(ecuacion)
if tipo_eq==True: # era igualdad, integra lhs=rhs
ecuacion = sym.Eq(ecuacion,RHS)
return(ecuacion)
def edp_sustituye_L(resultado):
''' sustituye lambda con Dy**ordeny/Dx**x/K_
por L, al simplificar Lambda
'''
discreta = resultado['(discreta=0)*Dy**ordeny']
ordenDy = resultado['ordenDy']
ordenDx = resultado['ordenDx']
discreta_L = 0
# separa cada término de suma
term_suma = sym.Add.make_args(discreta)
for term_k in term_suma:
# busca partes de L y cambia por valor L
cambiar = False # por orden de derivada
if term_k.has(Dx) and term_k.has(Dy):
partes = term_k.args
ordeny = 1
ordenx = 1
for unaparte in partes:
if unaparte.has(Dy):
if unaparte.is_Pow:
partey = unaparte.args
ordeny = partey[1]
if unaparte.has(Dx):
if unaparte.is_Pow:
partey = unaparte.args
ordenx = partey[1]
if (ordeny<=ordenDy and ordenx<=-ordenDx):
cambiar = True
if cambiar==True:
term_k = term_k*L/resultado['Lambda_L']
discreta_L = discreta_L + term_k
# simplifica unos con decimal a entero
discreta_L = redondea_coef(discreta_L)
return(discreta_L)
resultado = {} # resultados en diccionario
# orden de derivada por x, y
edp_x = edp.subs(x,0)
edp_y = edp.subs(y,0)
ordenDx = sym.ode_order(edp_x,u)
ordenDy = sym.ode_order(edp_y,u)
resultado['ordenDx'] = ordenDx # guarda en resultados
resultado['ordenDy'] = ordenDy
# coeficiente derivada orden mayor a 1 (d2u/dx2)
coeff_x = edp_coef_Dx(edp,x,ordenDx)
LHS = edp.lhs # lado izquierdo de edp
RHS = edp.rhs # lado derecho de edp
if not(coeff_x==1):
LHS = LHS/coeff_x
RHS = RHS/coeff_x
# toda la expresión a la izquierda
edp = sym.Eq(LHS-RHS,0)
K_ = -edp_coef_Dx(edp,y,ordenDy)
if abs(K_)%1<casicero: # si es entero
K_ = int(K_)
resultado['edp=0'] = edp
resultado['K_'] = K_
# lambda en ecuacion edp
lamb = (Dy**ordenDy)/(Dx**ordenDx)
if ordenDy==1 and ordenDx==2:
lamb = lamb/K_
resultado['Lambda_L'] = lamb
# valor de Lambda en ecuacion edp
L_k = lamb.subs([(Dx,dx),(Dy,dy)])
if abs(L_k)%1<casicero: # si es entero
L_k = int(L_k)
resultado['Lambda L_k'] = L_k
discreta = edp.lhs # EDP discreta
for derivada in dif_dividida:
discreta = discreta.subs(derivada,dif_dividida[derivada])
# simplifica con Lambda L
discreta_L = sym.expand(discreta*(Dy**ordenDy),mul=True)
resultado['(discreta=0)*Dy**ordeny'] = discreta_L
discreta_L = edp_sustituye_L(resultado)
discreta_L = sym.collect(discreta_L,U) # agrupa u[,]
discreta_L = sym.Eq(discreta_L,0)
resultado['discreta_L=0'] = discreta_L
# sustituye constantes en ecuación a iterar
discreta_L = discreta_L.subs([(Dx,dx),(Dy,dy),(L,L_k)])
discreta_L = redondea_coef(discreta_L)
resultado['discreta'] = discreta_L
# SALIDA
np.set_printoptions(precision=verdigitos)
algun_numero = [int,float,str,'Lambda L_k']
print('EDP Parabólica contínua a discreta')
for entrada in resultado:
tipo = type(resultado[entrada])
if tipo in algun_numero or entrada in algun_numero:
print('',entrada,':',resultado[entrada])
else:
print('\n',entrada,':')
sym.pprint(resultado[entrada])
# Método implícito EDP Parabólica ------------
# ITERAR para cada i,j dentro de U
# x[i] , y[j] valor en posición en cada eje
xi = np.arange(a,b+dx/2,dx)
yj = np.arange(y0,n_iteraciones*dy+dy/2,dy)
n = len(xi)
m = len(yj)
# Matriz U
u_xy = np.zeros(shape=(n,m),dtype = float)
u_xy = u_xy*np.nan # valor inicial dentro de u
# llena u con valores en fronteras
u_xy[:,0] = fxc(xi) # inferior Tc
u_xy[0,:] = fya(yj) # izquierda Ta
u_xy[n-1,:] = fyb(yj) # derecha Tb
u0 = np.copy(u_xy) # matriz u inicial
# u_mask[i,j] con valores iniciales o calculados:True
u_mask = np.zeros(shape=(n,m),dtype=bool)
u_mask[0,:] = True # izquierda Ta
u_mask[-1,:] = True # derecha Tb
u_mask[:,0] = True # inferior Tc
# Método implícito para EDP Parabólica
# a usar en iteraciones
discreta = resultado['discreta']
def edp_sustituyeValorU(discreta,xi,yj,u_xy,u_mask):
'''Sustituye en edp discreta los valores conocidos de U[i,j]
tomados desde u_xy, marcados con u_mask
u_mask indica si el valor se ha calculado con edp.
'''
LHS = discreta.lhs # lado izquierdo de ecuacion
RHS = discreta.rhs # lado derecho
# sustituye U[i,j] con valores conocidos
A_diagonal = [] # lista de i,j para matriz de coeficientes A
# Separa términos suma
term_suma = sym.Add.make_args(LHS)
for term_k in term_suma:
# busca U[i,j] y cambia por valor uxt[i,j]
cambiar = False ; cambiar_valor = 0 ; cambiar_factor = 0
# separa cada factor de término
factor_Mul = sym.Mul.make_args(term_k)
for factor_k in factor_Mul:
# busca U[i,j] en matriz uxt[i,j]
if factor_k.is_Function:
[i_k,j_k] = factor_k.args
if not(np.isnan(u_xy[i_k,j_k])):
cambiar = u_mask[i_k,j_k]
cambiar_factor = factor_k
cambiar_valor = u_xy[i_k,j_k]
else:
A_diagonal.append([i_k,j_k,term_k/factor_k])
# encontró valor U[i,j],term_k va a la derecha de ecuación
if cambiar == True:
LHS = LHS - term_k
term_ki = term_k.subs(cambiar_factor,cambiar_valor)
RHS = RHS - term_ki
discreta = sym.Eq(LHS,RHS)
B_diagonal = RHS
resultado = [discreta,A_diagonal,B_diagonal]
return (resultado)
# ITERAR para plantear las ecuaciones en [i,j]
resultado = {} # resultados en diccionario
eq_itera = [] ; tamano = (n-2)
A = np.zeros(shape=(tamano,tamano),dtype=float)
B = np.zeros(tamano,dtype=float)
for j_k in range(1,m,1): # no usar valores en bordes
resultado[j_k] ={}
for i_k in range(1,n-1,1):
eq_conteo = i_k-1
discreta_ij = discreta.subs({i:i_k,j:j_k,
x:xi[i_k],y:yj[j_k]})
resultado[j_k][eq_conteo]= {'j':j_k, 'i':i_k,
'discreta_ij': discreta_ij}
# usa valores de frontera segun u_mask con True
discreta_k = edp_sustituyeValorU(discreta_ij,
xi,yj,u_xy,u_mask)
discreta_ij = discreta_k[0]
A_diagonal = discreta_k[1] # lista de (i,j,coeficiente)
B_diagonal = discreta_k[2] # constante para B
resultado[j_k][eq_conteo]['discreta_k'] = discreta_k[0]
# añade ecuacion a resolver
eq_itera.append(discreta_ij)
# Aplica coeficientes de ecuacion en A y B:
# A_diagonal tiene lista de (i,j,coeficiente)
for uncoeff in A_diagonal:
columna = uncoeff[0]-1
fila = eq_conteo
A[fila,columna] = uncoeff[2]
B[eq_conteo] = float(B_diagonal) # discreta_ij.rhs
# resuelve el sistema A.X=B en Numpy
X_k = np.linalg.solve(A,B)
# actualiza uxt[i,j] , u_mask segun X_k
u_xy[1:n-1,j_k] = X_k
u_mask[1:n-1,j_k] = True
# almacena resultados
resultado[j_k]['A'] = np.copy(A)
resultado[j_k]['B'] = np.copy(B)
resultado[j_k]['X'] = np.copy(X_k)
# SALIDA
np.set_printoptions(precision=verdigitos)
algun_numero = [int,float,str,'Lambda L_k']
print('\nMétodo implícito EDP Parabólica')
for entrada in resultado:
tipo = type(resultado[entrada])
if tipo in algun_numero or entrada in algun_numero:
print('',entrada,':',resultado[entrada])
elif (tipo==dict):
if entrada<2:
eq_conteo = resultado[entrada].keys()
for una_eq in eq_conteo:
if una_eq=='A' or una_eq=='B' or una_eq=='X':
print(una_eq,':')
print(resultado[entrada][una_eq])
else:
print('j:',resultado[entrada][una_eq]['j'],'; '
'i:',resultado[entrada][una_eq]['i'])
sym.pprint(resultado[entrada][una_eq]['discreta_ij'])
sym.pprint(resultado[entrada][una_eq]['discreta_k'])
else:
print('\n',entrada,': \n',resultado[entrada])
print('Resultados para U(x,y)')
print('xi:',xi)
print('yj:',yj)
print(' j, U[i,j]')
for j_k in range(m-1,-1,-1):
print(j_k, (u_xy[:,j_k]))