s2Eva_2023PAOI_T1 Material para medalla de academia

Ejercicio: 2Eva_2023PAOI_T1 Material para medalla de academia

medalla área con integral numéricof(x)=28(12x)2 f(x) = 2-8\Big( \frac{1}{2} - x \Big)^2

0x<1 0 \le x \lt 1 g(x)=(1x)ln(1x) g(x) = -\Big( 1-x\Big)\ln \Big( 1- x \Big)

Para f(x) se usará Simpson de 1/3 que requiere al menos dos tramos para aplicar:

a. Realice el planteamiento de las ecuaciones para el ejercicio.

Ih3[f(x0)+4f(x1)+f(x2)] I\cong \frac{h}{3}[f(x_0)+4f(x_1) + f(x_2)]

b. Describa el criterio usado para determinar el número de tramos usado en cada caso.

h=ba2=102=0.5h = \frac{b-a}{2} = \frac{1-0}{2} = 0.5

c. Desarrolle las expresiones completas del ejercicio para cada función.

Ifx0.53[f(0)+4f(0.5)+f(1)] I_{fx}\cong \frac{0.5}{3}[f(0)+4f(0.5) + f(1)] f(0)=28(12(0))2=0 f(0) = 2-8\Big( \frac{1}{2} - (0) \Big)^2 = 0 f(0.5)=28(12(0.5))2=2 f(0.5) = 2-8\Big( \frac{1}{2} - (0.5) \Big)^2 = 2 f(1)=28(12(1))2=0 f(1) = 2-8\Big( \frac{1}{2} - (1) \Big)^2 = 0 Ifx=16[0+4(2)+0]=86=43=1.3333 I_{fx} = \frac{1}{6}[0+4(2) + 0] = \frac{8}{6} = \frac{4}{3} = 1.3333

cota de error O(h5) = O(0.55)= O(0.03125)

Para g(x) se usará Simpson de 3/8 que requiere al menos tres tramos para aplicar:

I3h8[f(x0)+3f(x1)+3f(x2)+f(x3)] I\cong \frac{3h}{8}[f(x_0)+3f(x_1) +3 f(x_2)+f(x_3)] h=ba3=103=0.3333h = \frac{b-a}{3} = \frac{1-0}{3} = 0.3333 Igx3(0.3333)8[f(0)+3f(0.3333)+3f(0.6666)+f(1)] I_{gx}\cong \frac{3(0.3333)}{8}[f(0)+3f(0.3333) +3 f(0.6666)+f(1)] g(0)=(10)ln(10)=0 g(0) = -\Big( 1-0\Big)\ln \Big( 1- 0 \Big) = 0 g(0.3333)=(10.3333)ln(10.3333)=0.2703 g(0.3333) = -\Big( 1-0.3333\Big)\ln \Big( 1- 0.3333 \Big) = 0.2703 g(0.6666)=(10.6666)ln(10.6666)=0.3662 g(0.6666) = -\Big( 1-0.6666\Big)\ln \Big( 1- 0.6666 \Big) = 0.3662 g(0.9999)=(10.9999)ln(10.9999)=0 g(0.9999) = -\Big( 1-0.9999\Big)\ln \Big( 1- 0.9999 \Big) = 0

para la evaluación numérica de 1 se usa un valor muy cercano desplazado con la tolerancia aplicada.

Igx3(0.3333)8[0+3(0.2703)+3(0.3662)+0]=0.2387 I_{gx}\cong \frac{3(0.3333)}{8}[0+3(0.2703) + 3(0.3662)+0] = 0.2387

d. Indique el resultado obtenido para el área requerida y la cota de error
Area = I_{fx} – I_{gx} = 1.3333 – 0.2387 = 1.0945

cota de error = O(0.03125) + O(0.00411) = 0.03536

e. Encuentre el valor del tamaño de paso si se requiere una cota de error de 0.00032

Si el factor de mayor error es de Simpson 1/3, se considera como primera aproximación que:

cota de error O(h5) = O(0.00032), h = (0.00032)(1/5) = 0.2
es decir el número de tramos es de al menos (b-a)/tramos = 0.2 , tramos = 5.
El número de tramos debe ser par en Simpson de 1/3, por lo que se toma el entero mayor tramos=6 y el tamaño de paso recomendado es al menos 1/6. EL error al aplicar 3 veces la formula es 3(O((1/6)5)) = 0.0003858.

Lo que podría indicar que es necesario al menos dos tramos adicionales con h=1/8 y error O(0,00012) que cumple con el requerimiento.

Se puede aplicar el mismo criterio para Simpson 3/8 y se combinan los errores para verificar que cumplen con el requerimiento.

Algoritmo con Python

Resultados

Ifx:  1.3333332933359998
Igx:  0.238779092876627
Area:  1.094554200459373

medalla area con integral numerico

Instrucciones en Python usando las funciones

# 2Eva_2023PAOI_T1 Material para medalla de academia
import numpy as np
import matplotlib.pyplot as plt

def integrasimpson13_fi(xi,fi,tolera = 1e-10):
    ''' sobre muestras de fi para cada xi
        integral con método de Simpson 1/3
        respuesta es np.nan para tramos desiguales,
        no hay suficientes puntos.
    '''
    n = len(xi)
    i = 0
    suma = 0
    while not(i>=(n-2)):
        h = xi[i+1]-xi[i]
        dh = abs(h - (xi[i+2]-xi[i+1]))
        if dh<tolera:# tramos iguales
            unS13 = (h/3)*(fi[i]+4*fi[i+1]+fi[i+2])
            suma = suma + unS13
        else:  # tramos desiguales
            suma = 'tramos desiguales'
        i = i + 2
    if i<(n-1): # incompleto, faltan tramos por calcular
        suma = 'tramos incompletos, faltan '
        suma = suma ++str((n-1)-i)+' tramos'
    return(suma)

def integrasimpson38_fi(xi,fi,tolera = 1e-10):
    ''' sobre muestras de fi para cada xi
        integral con método de Simpson 3/8
        respuesta es np.nan para tramos desiguales,
        no hay suficientes puntos.
    '''
    n = len(xi)
    i = 0
    suma = 0
    while not(i>=(n-3)):
        h  = xi[i+1]-xi[i]
        h1 = (xi[i+2]-xi[i+1])
        h2 = (xi[i+3]-xi[i+2])
        dh = abs(h-h1)+abs(h-h2)
        if dh<tolera:# tramos iguales
            unS38 = fi[i]+3*fi[i+1]+3*fi[i+2]+fi[i+3]
            unS38 = (3/8)*h*unS38
            suma = suma + unS38
        else:  # tramos desiguales
            suma = 'tramos desiguales'
        i = i + 3
    if (i+1)<n: # incompleto, tramos por calcular
        suma = 'tramos incompletos, faltan '
        suma = suma +str(n-(i+1))+' tramos'
    return(suma)

# INGRESO
fx = lambda x: 2-8*(0.5-x)**2
gx = lambda x: -(1-x)*np.log(1-x)
a = 0
b = 1-1e-4
muestras1 = 2+1
muestras2 = 3+1

# PROCEDIMIENTO
xi1 = np.linspace(a,b,muestras1)
xi2 = np.linspace(a,b,muestras2)
fi = fx(xi1)
gi = gx(xi2)

Ifx = integrasimpson13_fi(xi1,fi)
Igx = integrasimpson38_fi(xi2,gi)
Area = Ifx - Igx

# SALIDA
print('Ifx: ', Ifx)
print('Igx: ', Igx)
print('Area: ', Area)

plt.plot(xi1,fi,'ob',label='f(x)')
plt.plot(xi2,gi,'or', label='g(x)')
plt.grid()
plt.legend()
plt.xlabel('xi')

# curvas suave con mas muestras (no en evaluación)
xi = np.linspace(a,b,51)
fxi = fx(xi)
gxi = gx(xi)
plt.fill_between(xi,fxi,gxi,color='navajowhite')
plt.plot(xi,fxi,color='blue',linestyle='dotted')
plt.plot(xi,gxi,color='red',linestyle='dotted')

plt.show()

2Eva_2023PAOI_T1 Material para medalla de academia

2da Evaluación 2023-2024 PAO I. 29/Agosto/2023

Tema 1 (30 puntos) medalla area con integral numerico
Una academia encarga a un joyero un modelo de medalla cuyo costo unitario se determina por el área descrita entre las funciones f(x) y g(x) presentadas.

Se considera que el grosor de la medalla es único e independiente de la forma de la medalla.

f(x)=28(12x)2 f(x) = 2-8\Big( \frac{1}{2} - x \Big)^2 0x<1 0 \le x \lt 1 g(x)=(1x)ln(1x) g(x) = -\Big( 1-x\Big)\ln \Big( 1- x \Big)

Para el desarrollo numérico, use diferentes métodos de Simpson para cada función.

a. Realice el planteamiento de las ecuaciones para el ejercicio.

b. Describa el criterio usado para determinar el número de tramos usado en cada caso.

c. Desarrolle las expresiones completas del ejercicio para cada función.

d. Indique el resultado obtenido para el área requerida y la cota de error.

e. Encuentre el valor del tamaño de paso si se requiere una cota de error de 0.00032

Nota: en Python ln(x) se escribe np.log(x).

Rúbrica: literal a (5 puntos), literal b (5 puntos), literal c (10 puntos), literal d (5 puntos), literal e (5 puntos)

Referencia: Star Trek https://intl.startrek.com/
¿A quien se le ocurrió crear la moneda? | Discovery en Español Youtube.com 8 nov 2016.

7.5.2 EDP Elípticas – analítico con Sympy-Python

EDP Elípticas [ contínua a discreta ] || [ sympy_iterativo ] [ sympy_implícito ]
..


1. Ejercicio

Referencia: Chapra 29.1 p866, Rodríguez 10.3 p425, Burden 12.1 p694

2ux2+2uy2=0 \frac{\partial ^2 u}{\partial x^2} + \frac{\partial ^2 u}{ \partial y^2} = 0

Valores de frontera: Ta = 60, Tb = 25, Tc = 50, Td = 70
Longitud en x0 = 0, xn = 2, y0 = 0, yn = 1.5
Tamaño de paso dx = 0.25, dy = dx
iteramax=100, tolera = 0.0001

(ecuación de Laplace, Ecuación de Poisson con f(x,y)=0)

EDP Elípticas [ contínua a discreta ] || [ sympy_iterativo ] [ sympy_implícito ]

..


2. EDP Elípticas contínua a discreta

El desarrollo analítico inicia convirtiendo la ecuación diferencial parcial elíptica de la forma contínua a su representación discreta usando las expresiones de derivadas en  diferencias divididas. El cambio de la expresión se realiza usando  Sympy.

La EDP se escribe en formato Sympy en el bloque de ingreso.  La ecuación EDP se compone del lado izquierdo (LHS) y lado derecho (RHS), indicando u como una función de las variables x,y.

# INGRESO
fxy = 0*x+0*y # f(x,y) = 0 , ecuacion de Poisson
# ecuacion: LHS=RHS
LHS = sym.diff(u,x,2) + sym.diff(u,y,2)
RHS = fxy
EDP = sym.Eq(LHS-RHS,0)

las diferencias divididas se ingresan como un diccionario:

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,2): (U.subs(j,j-1)-2*U+U.subs(j,j+1))/(Dy**2),
               sym.diff(u,y,1): (U - U.subs(j,j-1))/Dy}

las condiciones iniciales en los bordes, pueden ser funciones matemáticas, por lo que se usa el formato lambda. En el ejercicio básico presentado en la parte teórica, los bordes tienen temperaturas constantes, por lo que en ésta sección se escriben las condiciones como la constante mas cero por la variable independiente, facilitando la evaluación de un vector xi por ejemplo.

# Condiciones iniciales en los bordes
fya = lambda y: 60 +0*y  # izquierda
fyb = lambda y: 25 +0*y  # derecha
fxc = lambda x: 50 +0*x  # inferior, función inicial 
fxd = lambda x: 70 +0*x  # superior, función inicial 

los demás parámetros del ejercicio se ingresan más adelante de forma semejante al ejercicio con Numpy.

Todas las expresiones se escriben al lado izquierdo (LHS) de la igualdad, la parte del lado derecho(RHS) se la prefiere mantener en cero. La expresión se organiza manteniendo el coeficiente en 1 para el termino de Dx de mayor orden. Se sustituyen las derivadas por diferencias divididas, para luego simplificar la expresión usando λ como referencia.

El resultado de las operaciones en la expresión usando el algoritmo es:

EDP Elíptica contínua a discreta
 ordenDx : 2
 ordenDy : 2

 edp=0 :
  2              2             
 d              d              
---(u(x, y)) + ---(u(x, y)) = 0
  2              2             
dx             dy              
 K_ : 1.0

 discreta=0 :
-2*U(i, j) + U(i, j - 1) + U(i, j + 1)   -2*U(i, j) + U(i - 1, j) + U(i + 1, j)
-------------------------------------- + --------------------------------------
                   2                                        2                 
                 Dy                                       Dx                  
 Lambda_L :
  2
Dy 
---
  2
Dx 
 Lambda L_k : 1

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

 discreta :
-4*U(i, j) + U(i, j - 1) + U(i, j + 1) + U(i - 1, j) + U(i + 1, j) = 0

 discreta_iterativa :
          U(i, j - 1) + U(i, j + 1) + U(i - 1, j) + U(i + 1, j)
U(i, j) = -----------------------------------------------------
                                    4                          

Instrucciones en Python

Los resultados al aplicar una operación a la expresión se guardan en un diccionario, para revisar cada paso intermedio al final

# Ecuaciones Diferenciales Parciales Elipticas
# EDP Elípticas contínua a discreta con Sympy
import numpy as np
import sympy as sym

# funciones continuas y variables simbólicas usadas
y = sym.Symbol('y',real=True)
x = sym.Symbol('x',real=True)
u = sym.Function('u')(x,y)
f = sym.Function('f')(x,y) # funcion complemento
# funciones discretas y variables simbólicas usadas
i  = sym.Symbol('i',integer=True,positive=True)
j  = sym.Symbol('j',integer=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 de Poisson
# ecuacion edp : LHS=RHS
LHS = sym.diff(u,x,2) + sym.diff(u,y,2)
RHS = fxy
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,y,2): (U.subs(j,j-1)-2*U+U.subs(j,j+1))/(Dy**2),
               sym.diff(u,y,1): (U - U.subs(j,j-1))/Dy}

# Condiciones iniciales en los bordes
fya = lambda y: 60 +0*y  # izquierda
fyb = lambda y: 25 +0*y  # derecha
fxc = lambda x: 50 +0*x  # inferior 
fxd = lambda x: 70 +0*x  # superior 

# dimensiones de la placa
x0 = 0    # longitud en x
xn = 2
y0 = 0    # longitud en y
yn = 1.5
# discretiza, supone dx=dy
dx = 0.25  # Tamaño de paso
dy = dx
iteramax = 100
tolera = 0.0001

verdigitos = 2 # para mostrar en tabla de resultados

# PROCEDIMIENTO
def edp_discreta(edp,dif_dividida,x,y,u):
    ''' EDP contínua a discreta, usa diferencias divididas
        proporcionadas en parámetros, indica las variables x,y
        con función u de (x,y)
    '''
    resultado={}
    # expresión todo a la izquierda LHS (Left Hand side)
    LHS = edp.lhs
    RHS = edp.rhs
    if not(edp.rhs==0):
        LHS = LHS-RHS
        RHS = 0
        edp = sym.Eq(LHS,RHS)
    # para 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
    resultado['ordenDy'] = ordenDy
    # convierte coeficiente d2u/dx2 a 1
    coeff_x = edp_coef_Dx(edp,x,ordenDx)
    if not(coeff_x==1):
        LHS = LHS/coeff_x
        RHS = RHS/coeff_x
    edp = sym.Eq(LHS,RHS)
    K_ = edp_coef_Dx(edp,y,ordenDy)
    resultado['edp=0']  = edp
    resultado['K_']  = K_
    # edp discreta
    discreta = edp.lhs 
    for derivada in dif_dividida:
        discreta = discreta.subs(derivada,dif_dividida[derivada])
    resultado['discreta=0'] = discreta
    return (resultado)

def edp_coef_Dx(edp,x,ordenx):
    ''' Extrae el coeficiente de la derivada Dx de ordenx
    '''
    coeff_x = 1.0
    # 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: # término de mas de un factor
            factor_Mul = sym.Mul.make_args(term_k)
            coeff_temp = 1; coeff_usar=0
            # 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 = 1
            if coeff_usar==1:
                coeff_x = coeff_x*coeff_temp
    return(coeff_x)

def edp_simplificaLamba(resultado,dx,dy):
    '''simplifica ecuacion con valores de lambda, dx y dy
    entregando la edp discreta simplificada
    '''
    discreta = resultado['discreta=0']
    ordenDy = resultado['ordenDy']
    ordenDx = resultado['ordenDx']
    K_ = resultado['K_']
    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),(1.0,1)])
    resultado['Lambda L_k'] = L_k
    # 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 = discreta_L.subs(lamb,L)
    discreta_L = sym.collect(discreta_L,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),(1.0,1)])
    resultado['discreta'] = discreta_L
    return(resultado)

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 = 0 # 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=1
        if cambiar:
            term_k = term_k*L/resultado['Lambda_L']
        discreta_L = discreta_L + term_k
        # simplifica unos con decimal a entero 
        discreta_L = discreta_L.subs(1.0,1)
    return(discreta_L)

# PROCEDIMIENTO
# transforma edp continua a discreta
resultado = edp_discreta(edp,dif_dividida,x,y,u)
resultado = edp_simplificaLamba(resultado,dx,dy)
discreta = resultado['discreta']

# Método iterativo para EDP Elíptica
# separa término U[j,j] del centro,con valor no conocido
buscar = U.subs(j,j)
discreta = sym.solve(discreta,buscar)
discreta = sym.factor_terms(discreta[0])
discreta = sym.Eq(buscar,discreta)
resultado['discreta_iterativa'] = discreta

# SALIDA
np.set_printoptions(precision=verdigitos)
algun_numero = [int,float,str,'Lambda L_k']
print('EDP Elíptica 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])

EDP Elípticas [ contínua a discreta ] || [ sympy_iterativo ] [ sympy_implícito ]
..


3. EDP Elípticas – Método iterativo con Sympy-Python

El desarrollo del método iterativo para una ecuación diferencial parcial elíptica usando Sympy, sigue a continuación del anterior.

Se añaden las instrucciones para iterar la expresión discreta para cada i,j dentro de la matriz U creada para el ejercicio. Se aplican las condiciones iniciales a los bordes, para luego proceder a iterar.

Como en las actividades del curso se requiere realizar al menos 3 iteraciones para las expresiones del algoritmo con papel y lápiz, solo se presentarán los resultados para la primera fila en j=0

Los resultados del algoritmo  luego del resultado del algoritmo anterior se presentan como:

 discreta_iterativa :
          U(i, j - 1) + U(i, j + 1) + U(i - 1, j) + U(i + 1, j)
U(i, j) = -----------------------------------------------------
                                    4                          

 iterar i,j:
j:  1  ;  i:  1
          U(0, 1)   U(1, 0)   U(1, 2)   U(2, 1)
U(1, 1) = ------- + ------- + ------- + -------
             4         4         4         4   
U(i, j) = 53.125
j:  1  ;  i:  2
          U(1, 1)   U(2, 0)   U(2, 2)   U(3, 1)
U(2, 1) = ------- + ------- + ------- + -------
             4         4         4         4   
U(i, j) = 51.40625
j:  1  ;  i:  3
          U(2, 1)   U(3, 0)   U(3, 2)   U(4, 1)
U(3, 1) = ------- + ------- + ------- + -------
             4         4         4         4   
U(i, j) = 50.9765625
j:  1  ;  i:  4
          U(3, 1)   U(4, 0)   U(4, 2)   U(5, 1)
U(4, 1) = ------- + ------- + ------- + -------
             4         4         4         4   
U(i, j) = 50.869140625
j:  1  ;  i:  5
          U(4, 1)   U(5, 0)   U(5, 2)   U(6, 1)
U(5, 1) = ------- + ------- + ------- + -------
             4         4         4         4   
U(i, j) = 50.84228515625
j:  1  ;  i:  6
          U(5, 1)   U(6, 0)   U(6, 2)   U(7, 1)
U(6, 1) = ------- + ------- + ------- + -------
             4         4         4         4   
U(i, j) = 50.8355712890625
j:  1  ;  i:  7
          U(6, 1)   U(7, 0)   U(7, 2)   U(8, 1)
U(7, 1) = ------- + ------- + ------- + -------
             4         4         4         4   
U(i, j) = 44.271392822265625

Método iterativo EDP Elíptica
continuar el desarrollo con: 
xi: [0.   0.25 0.5  0.75 1.   1.25 1.5  1.75 2.  ]
yj: [0.   0.25 0.5  0.75 1.   1.25 1.5 ]
 j, U[i,j]
2 [60.   51.25 51.25 51.25 51.25 51.25 51.25 51.25 25.  ]
1 [60.   53.12 51.41 50.98 50.87 50.84 50.84 44.27 25.  ]
0 [50. 50. 50. 50. 50. 50. 50. 50. 50.]
>>> 

Se pueden sustituir los valores de las expresiones, evaluando cada u[i,j] y completando la matriz y para la gráfica. Esta parte queda como tarea.

Instrucciones en Python

Las instrucciones adicionales al algoritmo de EDP elíptica contínua a discreta empiezan con la creación de la matriz u_xy para los valores, los vectores xi,yj y una matriz u_mask que indica si se dispone de  un valor calculado o conocido en ese punto (x,y) . Para el método iterativo, la matriz se rellena con el promedio de los valores máximos de las funciones dadas para los bordes de la placa.

Tarea: El algoritmo desarrolla el cálculo para j_k=1, solo la primera fila. Se deja como tarea desarrollar para toda la matriz.

# ITERAR para cada i,j dentro de U ------------
# x[i] , y[j]  valor en posición en cada eje
xi = np.arange(x0,xn+dx,dx)
yj = np.arange(y0,yn+dy,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,:]   = fya(yj)  # izquierda Ta
u_xy[n-1,:] = fyb(yj)  # derecha   Tb
u_xy[:,0]   = fxc(xi)  # inferior  Tc
u_xy[:,m-1] = fxd(xi)  # superior  Td
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
u_mask[-1,:] = True # derecha
u_mask[:,0]  = True # inferior
u_mask[:,-1] = True # superior

# valor inicial de iteración dentro de u
# promedio = (Ta+Tb+Tc+Td)/4
promedio = (np.max(fya(yj))+np.max(fyb(yj))+\
            np.max(fxc(xi))+np.max(fxd(xi)))/4
u_xy[1:n-1,1:m-1]   = promedio
u_mask[1:n-1,1:m-1] = True
u0 = np.copy(u_xy) # copia para revisión

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 = 0 ; 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:
            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)

# Desarrollo de iteraciones en cada nodo i,j, para la fila j_k
# genera el valor en cada punto
# Nota: Solo la primera fila, Tarea desarrollar para la matriz
print('\n iterar i,j:')
j_k = 1
for i_k in range(1,n-1,1):
    print('j: ',j_k,' ; ','i: ',i_k)
    discreta_ij = discreta.subs({i:i_k,j:j_k,
                                 x:xi[i_k],y:yj[j_k]})
    sym.pprint(discreta_ij)
    RHS = discreta_ij.rhs
    discreta_ij = sym.Eq(RHS,0)
    # usa valores de frontera segun u_mask con True
    discreta_k = edp_sustituyeValorU(discreta_ij,
                                    xi,yj,u_xy,u_mask)
    u_xy[i_k,j_k] = sym.Float(-discreta_k[2])
    print(buscar,'=',u_xy[i_k,j_k])
    
# SALIDA
np.set_printoptions(precision=verdigitos)
print('\nMétodo iterativo EDP Elíptica')
print('continuar el desarrollo con: ')
print('xi:',xi)
print('yj:',yj)
print(' j, U[i,j]')
for j_k in range(2,-1,-1):
    print(j_k, (u_xy[:,j_k]))

EDP Elípticas [ contínua a discreta ] || [ sympy_iterativo ] [ sympy_implícito ]
..


4. EDP Elípticas – Método Implícito con Sympy-Python

Desarrollo analítico del método implicito para una ecuación diferencial parcial elíptica usando Sympy. El algoritmo reutiliza el algoritmo para la EDP Elíptica de contínua a discreta, la creación de la matriz de valores u_xy, y la función
edp_sustituyeValorU() para buscar los valores conocidos de la u(x,y).

El algoritmo usa la ecuación discreta para en cada iteración i,j reemplazar los valores de U conocidos. Los valores de U se escriben en una matriz u_xy, para diferenciar si el valor existe se usa la matriz de estados u_mask.

Con las ecuaciones de cada iteración se llena la matriz A de coeficientes y el vector B de las constantes. Al resolver el sistema de ecuaciones se obtienen todos los valores de la matriz U, completando el ejercicio. Se desarrola la solución al sistema de ecuaciones usando Sympy como alternativa a usar numpy con np.linalg.solve(A.B) que se encuentra como comentario entre las instrucciones.

 discreta :
-4*U(i, j) + U(i, j - 1) + U(i, j + 1) + U(i - 1, j) + U(i + 1, j) = 0

Método Implícito - EDP Elíptica
j: 1 ; i: 1 ; ecuacion: 0
U(0, 1) + U(1, 0) - 4*U(1, 1) + U(1, 2) + U(2, 1) = 0
-4*U(1, 1) + U(1, 2) + U(2, 1) = -110.0
j: 1 ; i: 2 ; ecuacion: 1
U(1, 1) + U(2, 0) - 4*U(2, 1) + U(2, 2) + U(3, 1) = 0
U(1, 1) - 4*U(2, 1) + U(2, 2) + U(3, 1) = -50.0
j: 1 ; i: 3 ; ecuacion: 2
U(2, 1) + U(3, 0) - 4*U(3, 1) + U(3, 2) + U(4, 1) = 0
U(2, 1) - 4*U(3, 1) + U(3, 2) + U(4, 1) = -50.0
j: 1 ; i: 4 ; ecuacion: 3
U(3, 1) + U(4, 0) - 4*U(4, 1) + U(4, 2) + U(5, 1) = 0
U(3, 1) - 4*U(4, 1) + U(4, 2) + U(5, 1) = -50.0
j: 1 ; i: 5 ; ecuacion: 4
U(4, 1) + U(5, 0) - 4*U(5, 1) + U(5, 2) + U(6, 1) = 0
U(4, 1) - 4*U(5, 1) + U(5, 2) + U(6, 1) = -50.0
j: 1 ; i: 6 ; ecuacion: 5
U(5, 1) + U(6, 0) - 4*U(6, 1) + U(6, 2) + U(7, 1) = 0
U(5, 1) - 4*U(6, 1) + U(6, 2) + U(7, 1) = -50.0
j: 1 ; i: 7 ; ecuacion: 6
U(6, 1) + U(7, 0) - 4*U(7, 1) + U(7, 2) + U(8, 1) = 0
U(6, 1) - 4*U(7, 1) + U(7, 2) = -75.0
j: 2 ; i: 1 ; ecuacion: 7
U(0, 2) + U(1, 1) - 4*U(1, 2) + U(1, 3) + U(2, 2) = 0
U(1, 1) - 4*U(1, 2) + U(1, 3) + U(2, 2) = -60.0
j: 2 ; i: 2 ; ecuacion: 8
U(1, 2) + U(2, 1) - 4*U(2, 2) + U(2, 3) + U(3, 2) = 0
j: 2 ; i: 3 ; ecuacion: 9
U(2, 2) + U(3, 1) - 4*U(3, 2) + U(3, 3) + U(4, 2) = 0
j: 2 ; i: 4 ; ecuacion: 10
U(3, 2) + U(4, 1) - 4*U(4, 2) + U(4, 3) + U(5, 2) = 0
j: 2 ; i: 5 ; ecuacion: 11
U(4, 2) + U(5, 1) - 4*U(5, 2) + U(5, 3) + U(6, 2) = 0
j: 2 ; i: 6 ; ecuacion: 12
U(5, 2) + U(6, 1) - 4*U(6, 2) + U(6, 3) + U(7, 2) = 0
j: 2 ; i: 7 ; ecuacion: 13
U(6, 2) + U(7, 1) - 4*U(7, 2) + U(7, 3) + U(8, 2) = 0
U(6, 2) + U(7, 1) - 4*U(7, 2) + U(7, 3) = -25.0
j: 3 ; i: 1 ; ecuacion: 14
U(0, 3) + U(1, 2) - 4*U(1, 3) + U(1, 4) + U(2, 3) = 0
U(1, 2) - 4*U(1, 3) + U(1, 4) + U(2, 3) = -60.0
j: 3 ; i: 2 ; ecuacion: 15
U(1, 3) + U(2, 2) - 4*U(2, 3) + U(2, 4) + U(3, 3) = 0
j: 3 ; i: 3 ; ecuacion: 16
U(2, 3) + U(3, 2) - 4*U(3, 3) + U(3, 4) + U(4, 3) = 0
j: 3 ; i: 4 ; ecuacion: 17
U(3, 3) + U(4, 2) - 4*U(4, 3) + U(4, 4) + U(5, 3) = 0
j: 3 ; i: 5 ; ecuacion: 18
U(4, 3) + U(5, 2) - 4*U(5, 3) + U(5, 4) + U(6, 3) = 0
j: 3 ; i: 6 ; ecuacion: 19
U(5, 3) + U(6, 2) - 4*U(6, 3) + U(6, 4) + U(7, 3) = 0
j: 3 ; i: 7 ; ecuacion: 20
U(6, 3) + U(7, 2) - 4*U(7, 3) + U(7, 4) + U(8, 3) = 0
U(6, 3) + U(7, 2) - 4*U(7, 3) + U(7, 4) = -25.0
j: 4 ; i: 1 ; ecuacion: 21
U(0, 4) + U(1, 3) - 4*U(1, 4) + U(1, 5) + U(2, 4) = 0
U(1, 3) - 4*U(1, 4) + U(1, 5) + U(2, 4) = -60.0
j: 4 ; i: 2 ; ecuacion: 22
U(1, 4) + U(2, 3) - 4*U(2, 4) + U(2, 5) + U(3, 4) = 0
j: 4 ; i: 3 ; ecuacion: 23
U(2, 4) + U(3, 3) - 4*U(3, 4) + U(3, 5) + U(4, 4) = 0
j: 4 ; i: 4 ; ecuacion: 24
U(3, 4) + U(4, 3) - 4*U(4, 4) + U(4, 5) + U(5, 4) = 0
j: 4 ; i: 5 ; ecuacion: 25
U(4, 4) + U(5, 3) - 4*U(5, 4) + U(5, 5) + U(6, 4) = 0
j: 4 ; i: 6 ; ecuacion: 26
U(5, 4) + U(6, 3) - 4*U(6, 4) + U(6, 5) + U(7, 4) = 0
j: 4 ; i: 7 ; ecuacion: 27
U(6, 4) + U(7, 3) - 4*U(7, 4) + U(7, 5) + U(8, 4) = 0
U(6, 4) + U(7, 3) - 4*U(7, 4) + U(7, 5) = -25.0
j: 5 ; i: 1 ; ecuacion: 28
U(0, 5) + U(1, 4) - 4*U(1, 5) + U(1, 6) + U(2, 5) = 0
U(1, 4) - 4*U(1, 5) + U(2, 5) = -130.0
j: 5 ; i: 2 ; ecuacion: 29
U(1, 5) + U(2, 4) - 4*U(2, 5) + U(2, 6) + U(3, 5) = 0
U(1, 5) + U(2, 4) - 4*U(2, 5) + U(3, 5) = -70.0
j: 5 ; i: 3 ; ecuacion: 30
U(2, 5) + U(3, 4) - 4*U(3, 5) + U(3, 6) + U(4, 5) = 0
U(2, 5) + U(3, 4) - 4*U(3, 5) + U(4, 5) = -70.0
j: 5 ; i: 4 ; ecuacion: 31
U(3, 5) + U(4, 4) - 4*U(4, 5) + U(4, 6) + U(5, 5) = 0
U(3, 5) + U(4, 4) - 4*U(4, 5) + U(5, 5) = -70.0
j: 5 ; i: 5 ; ecuacion: 32
U(4, 5) + U(5, 4) - 4*U(5, 5) + U(5, 6) + U(6, 5) = 0
U(4, 5) + U(5, 4) - 4*U(5, 5) + U(6, 5) = -70.0
j: 5 ; i: 6 ; ecuacion: 33
U(5, 5) + U(6, 4) - 4*U(6, 5) + U(6, 6) + U(7, 5) = 0
U(5, 5) + U(6, 4) - 4*U(6, 5) + U(7, 5) = -70.0
j: 5 ; i: 7 ; ecuacion: 34
U(6, 5) + U(7, 4) - 4*U(7, 5) + U(7, 6) + U(8, 5) = 0
U(6, 5) + U(7, 4) - 4*U(7, 5) = -95.0

 A : 
 [[-4.  1.  0. ...  0.  0.  0.]
 [ 1. -4.  1. ...  0.  0.  0.]
 [ 0.  1. -4. ...  0.  0.  0.]
 ...
 [ 0.  0.  0. ... -4.  1.  0.]
 [ 0.  0.  0. ...  1. -4.  1.]
 [ 0.  0.  0. ...  0.  1. -4.]]

 B : 
 [-110.  -50.  -50.  -50.  -50.  -50.  -75.  -60.    0.    0.    0.    0.
    0.  -25.  -60.    0.    0.    0.    0.    0.  -25.  -60.    0.    0.
    0.    0.    0.  -25. -130.  -70.  -70.  -70.  -70.  -70.  -95.]
Resultados para U(x,y)
xi: [0.   0.25 0.5  0.75 1.   1.25 1.5  1.75 2.  ]
yj: [0.   0.25 0.5  0.75 1.   1.25 1.5 ]
 j, U[i,j]
6 [70. 70. 70. 70. 70. 70. 70. 70. 70.]
5 [60.   64.02 64.97 64.71 63.62 61.44 57.16 47.96 25.  ]
4 [60.   61.1  61.14 60.25 58.35 54.98 49.23 39.67 25.  ]
3 [60.   59.23 58.25 56.81 54.53 50.89 45.13 36.48 25.  ]
2 [60.   57.56 55.82 54.19 52.09 48.92 43.91 36.14 25.  ]
1 [60.   55.21 53.27 52.05 50.73 48.78 45.46 39.15 25.  ]
0 [50. 50. 50. 50. 50. 50. 50. 50. 50.]
>>> 

Instrucciones en Python

Las instrucciones completas con Sympy-Python son:

# Ecuaciones Diferenciales Parciales Elipticas
# EDP Elípticas contínua a discreta con Sympy
import numpy as np
import sympy as sym

# funciones continuas y variables simbólicas usadas
y = sym.Symbol('y',real=True)
x = sym.Symbol('x',real=True)
u = sym.Function('u')(x,y)
f = sym.Function('f')(x,y) # funcion complemento
# funciones discretas y variables simbólicas usadas
i  = sym.Symbol('i',integer=True,positive=True)
j  = sym.Symbol('j',integer=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 de Poisson
# ecuacion edp : LHS=RHS
LHS = sym.diff(u,x,2) + sym.diff(u,y,2)
RHS = fxy
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,y,2): (U.subs(j,j-1)-2*U+U.subs(j,j+1))/(Dy**2),
               sym.diff(u,y,1): (U - U.subs(j,j-1))/Dy}

# Condiciones iniciales en los bordes
fya = lambda y: 60 +0*y  # izquierda
fyb = lambda y: 25 +0*y  # derecha
fxc = lambda x: 50 +0*x  # inferior, función inicial 
fxd = lambda x: 70 +0*x  # superior, función inicial 

# dimensiones de la placa
x0 = 0    # longitud en x
xn = 2
y0 = 0    # longitud en y
yn = 1.5
# discretiza, supone dx=dy
dx = 0.25  # Tamaño de paso
dy = dx
iteramax = 100
tolera = 0.0001

verdigitos = 2 # para mostrar en tabla de resultados

# PROCEDIMIENTO
def edp_discreta(edp,dif_dividida,x,y,u):
    ''' EDP contínua a discreta, usa diferencias divididas
        proporcionadas en parámetros, indica las variables x,y
        con función u de (x,y)
    '''
    resultado={}
    # expresión todo a la izquierda LHS (Left Hand side)
    LHS = edp.lhs
    RHS = edp.rhs
    if not(edp.rhs==0):
        LHS = LHS-RHS
        RHS = 0
        edp = sym.Eq(LHS,RHS)
    # para 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
    resultado['ordenDy'] = ordenDy
    # convierte coeficiente d2u/dx2 a 1
    coeff_x = edp_coef_Dx(edp,x,ordenDx)
    if not(coeff_x==1):
        LHS = LHS/coeff_x
        RHS = RHS/coeff_x
    edp = sym.Eq(LHS,RHS)
    K_ = edp_coef_Dx(edp,y,ordenDy)
    resultado['edp=0']  = edp
    resultado['K_']  = K_
    # edp discreta
    discreta = edp.lhs 
    for derivada in dif_dividida:
        discreta = discreta.subs(derivada,dif_dividida[derivada])
    resultado['discreta=0'] = discreta
    return (resultado)

def edp_coef_Dx(edp,x,ordenx):
    ''' Extrae el coeficiente de la derivada Dx de ordenx
    '''
    coeff_x = 1.0
    # 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: # término de mas de un factor
            factor_Mul = sym.Mul.make_args(term_k)
            coeff_temp = 1; coeff_usar=0
            # 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 = 1
            if coeff_usar==1:
                coeff_x = coeff_x*coeff_temp
    return(coeff_x)

def edp_simplificaLamba(resultado,dx,dy):
    '''simplifica ecuacion con valores de lambda, dx y dy
    entregando la edp discreta simplificada
    '''
    discreta = resultado['discreta=0']
    ordenDy = resultado['ordenDy']
    ordenDx = resultado['ordenDx']
    K_ = resultado['K_']
    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),(1.0,1)])
    resultado['Lambda L_k'] = L_k
    # 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 = discreta_L.subs(lamb,L)
    discreta_L = sym.collect(discreta_L,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),(1.0,1)])
    resultado['discreta'] = discreta_L
    return(resultado)

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 = 0 # 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=1
        if cambiar:
            term_k = term_k*L/resultado['Lambda_L']
        discreta_L = discreta_L + term_k
        # simplifica unos con decimal a entero 
        discreta_L = discreta_L.subs(1.0,1)
    return(discreta_L)

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 = 0 ; 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:
            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)


# PROCEDIMIENTO
# transforma edp continua a discreta
resultado = edp_discreta(edp,dif_dividida,x,y,u)
resultado = edp_simplificaLamba(resultado,dx,dy)
discreta = resultado['discreta']

# SALIDA
np.set_printoptions(precision=verdigitos)
algun_numero = [int,float,str,'Lambda L_k']
print('EDP Elíptica 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])

# ITERAR para cada i,j dentro de U ------------
# x[i] , y[j]  valor en posición en cada eje
xi = np.arange(x0,xn+dx,dx)
yj = np.arange(y0,yn+dy,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,:]   = fya(yj)  # izquierda Ta
u_xy[n-1,:] = fyb(yj)  # derecha   Tb
u_xy[:,0]   = fxc(xi)  # inferior  Tc
u_xy[:,m-1] = fxd(xi)  # superior  Td
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
u_mask[-1,:] = True # derecha
u_mask[:,0]  = True # inferior
u_mask[:,-1] = True # superior

# Método implícito para EDP Elíptica
# ITERAR para plantear las ecuaciones en [i,j]
resultado = {}
eq_itera = [] ; tamano = (n-2)*(m-2)
A = np.zeros(shape=(tamano,tamano),dtype=float)
B = np.zeros(tamano,dtype=float)
for j_k in range(1,m-1,1): # no usar valores en bordes
    for i_k in range(1,n-1,1): 
        eq_conteo = (j_k - 1)*(n-2)+(i_k-1)
        discreta_ij = discreta.subs({i:i_k,j:j_k,
                                     x:xi[i_k],y:yj[j_k]})
        resultado[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]
        resultado[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[1]-1)*(n-2)+(uncoeff[0]-1)
            fila = (j_k - 1)*(n-2)+(i_k-1)
            A[fila,columna] = uncoeff[2]
        B[eq_conteo] = float(B_diagonal) # discreta_ij.rhs
resultado['A'] = np.copy(A)
resultado['B'] = np.copy(B)

# resuelve el sistema de ecuaciones en eq_itera en Sympy
X_k = sym.solve(eq_itera)[0]
# actualiza uxt[i,j] , u_mask segun X_k en Sympy
for nodo_Uij in X_k: 
    [i_k,j_k] = nodo_Uij.args
    u_xy[i_k,j_k] = X_k[nodo_Uij]
    u_mask[i_k,j_k] = True
# resuelve el sistema A.X=B en Numpy
#X = np.linalg.solve(A,B)
# tarea: llevar valores X a u_xy

# SALIDA
np.set_printoptions(precision=verdigitos)
algun_numero = [int,float,str,'Lambda L_k']
print('\nMétodo Implícito - EDP Elíptica')
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):
        print('j:',resultado[entrada]['j'],'; '
              'i:',resultado[entrada]['i'],'; '
              'ecuacion:',entrada)
        sym.pprint(resultado[entrada]['discreta_ij'])
        if resultado[entrada]['discreta_k'].rhs!=0:
            sym.pprint(resultado[entrada]['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]))

EDP Elípticas [ contínua a discreta ] || [ sympy_iterativo ] [ sympy_implícito ]

7.5.1 EDP Parabólica – analítico con Sympy-Python

EDP Parabólica [ contínua a discreta ] || [ sympy_explícito ] [sympy_ implícito ]
..


1. Ejercicio

Referencia:  Chapra 30.2 p888 pdf912, Burden 9Ed 12.2 p725, Rodríguez 10.2 p406

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

Valores de frontera: Ta = 60, Tb = 40, T0 = 25
Longitud en x a = 0, b = 1,  Constante K= 4
Tamaño de paso dx = 0.1, dt = dx/10

EDP Parabólica [ contínua a discreta ] || [ sympy_explícito ] [sympy_ implícito ]
..


2. EDP Parabólica contínua a discreta

El desarrollo analítico comienza en escribir la ecuación diferencial parcial parabólica de la forma contínua a la representación discreta, siguiendo los criterios indicados en la parte teórica usando derivadas expresadas en diferencias divididas.

La EDP se escribe en formato Sympy en el bloque de ingreso en dos partes: lado izquierdo (LHS) de la ecuación y lado derecho (RHS), definiendo u como una función de variables x,y.

# 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)

La ecuación f(x,y) se añade para ser usada con otros ejercicios que puede revisar en la sección de Evaluaciones.

Las expresiones de diferencias divididas seleccionadas se ingresan como un diccionario:

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.subs(j,j+1)-U)/Dy}

Las condiciones iniciales en los extremos a y b, y la temperatura en la barra, pueden ser funciones matemáticas, por lo que se propone usar el formato lambda de las variables x o y. En el ejercicio básico presentado en la parte teórica, la barra tiene temperatura constante, por lo que escribe como una  constante mas cero por la variable independiente, esta forma facilita la evaluación de un vector xi por ejemplo, en caso de disponer una expresión diferente.

# 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

los demás parámetros del ejercicio se ingresan más adelante de forma semejante al ejercicio con Numpy.

Todas las expresiones se escriben al lado izquierdo (LHS) de la igualdad, la parte del lado derecho(RHS) se la prefiere mantener en cero. La expresión se organiza manteniendo el coeficiente en 1 para el termino de Dx de mayor orden. Se sustituyen las derivadas por diferencias divididas, para luego simplificar la expresión usando λ como referencia.

Al desarrollar el método explícito se requiere indicar el nodo u(i,j+1) a buscar

buscar = U.subs(j,j+1) # método explícito

El valor de K se puede determinar en la expresión al reordenar para que el término de Dx de mayor grado sea la unidad. El valor de K será el del término Dy con signo negativo, al encontrarse en el lado izquierdo (LHS) de la expresión, en la propuesta teórica se encontraba como positivo del lado derecho.

El término de mayor grado se puede obtener al revisar la expresión por cada término de la suma, buscando el factor que contiene Dx o Dy en cada caso. Por ser un poco ordenado se añade la función edp_coef_Dx().

El resultado del algoritmo para el ejercicio propuesto es:

EDP Parabólica contínua a discreta
 ordenDx : 2
 ordenDy : 1

 edp=0 :
  2                             
 d               d              
---(u(x, y)) - 4*--(u(x, y)) = 0
  2              dy             
dx                              
 K_ : 4.0

 Lambda L :
0.25*Dy
-------
    2  
  Dx   
 Lambda L_k : 0.24999999999999997

 (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 :
/    2*Dy\                           Dy*U(i - 1, j)   Dy*U(i + 1, j)    
|4 - ----|*U(i, j) - 4*U(i, j + 1) + -------------- + -------------- = 0
|      2 |                                  2                2          
\    Dx  /                                Dx               Dx           

 discreta :
2.0*U(i, j) - 4*U(i, j + 1) + 1.0*U(i - 1, j) + 1.0*U(i + 1, j) = 0

>>>

Instrucciones en Python

Para revisar los resultados de algunos pasos para obtener la expresión discreta de EDP, se usa un diccionario a ser usado en el bloque de salida. Para las expresiones de ecuación se usa sym.pprint().

# Ecuaciones Diferenciales Parciales Parabólicas
# EDP Parabólicas contínua a discreta con Sympy
import numpy as np
import sympy as sym

# funciones continuas y variables simbólicas usadas
t = sym.Symbol('t',real=True)
y = sym.Symbol('y',real=True)
x = sym.Symbol('x',real=True)
u = sym.Function('u')(x,y)
K = sym.Symbol('K',real=True)
f = sym.Function('f')(x,y) # funcion complemento
# funciones discretas y variables simbólicas usadas
i  = sym.Symbol('i',integer=True,positive=True)
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)

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.subs(j,j+1)-U)/Dy}

buscar = U.subs(j,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

# dimensiones de la barra
a = 0  # longitud en x
b = 1
y0 = 0 # tiempo inicial, aumenta con dt en n iteraciones

# discretiza, supone dx=dy
x_tramos = 10
dx  = (b-a)/x_tramos
dy  = dx/10

verdigitos = 2 # para mostrar en tabla de resultados

n_iteraciones = 4 # iteraciones en tiempo

# PROCEDIMIENTO

def edp_coef_Dx(edp,x,ordenx):
    ''' Extrae el coeficiente de la derivada Dx de ordenx
    '''
    coeff_x = 1.0
    # 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=0
            # 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 = 1
            if coeff_usar==1:
                coeff_x = coeff_x*coeff_temp
    return(coeff_x)

resultado={}
# expresión todo a la izquierda LHS (Left Hand side)
if not(edp.rhs==0):
    LHS = EDP.lhs
    RHS = EDP.rhs
    edp = sym.Eq(LHS,RHS)
# para 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
resultado['ordenDy'] = ordenDy
# convierte coeficiente d2u/dx2 a 1
coeff_x = edp_coef_Dx(edp,x,ordenDx)
if not(coeff_x==1):
    LHS = LHS/coeff_x
    RHS = RHS/coeff_x
edp = sym.Eq(LHS-RHS,0)
K_ = -edp_coef_Dx(edp,y,ordenDy)
resultado['edp=0']  = edp
resultado['K_']  = float(K_)

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),(1.0,1)])
resultado['Lambda L_k'] = float(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 = discreta_L.subs(lamb,L)
discreta_L = sym.collect(discreta_L,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),(1.0+0*x,1)])
discreta_L = discreta_L.subs(1.0+0*x,1)
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])

EDP Parabólica [ contínua a discreta ] || [ sympy_explícito ] [sympy_ implícito ]

..


3. EDP Parabólica – Método explícito con Sympy

El desarrollo del método explícito para una ecuación diferencial parcial parabólica usando Sympy, requiere añadir instrucciones para procesar la expresión obtenida en el algoritmo anterior con los valores de i,j.

Se añaden las instrucciones para iterar la expresión discreta para cada i,j dentro de la matriz U creada para el ejercicio. Se aplican las condiciones iniciales a los bordes de la matriz, para luego proceder a iterar.

Como en las actividades del curso se requiere realizar al menos 3 iteraciones para las expresiones del algoritmo con papel y lápiz, solo se presentarán los resultados de las expresiones para la primera fila en j=0.

El algoritmo para la EDP del ejemplo muestra el siguiente resultado:

Método explícito EDP Parabólica
 discreta_itera : U(i, j + 1) = 0.5*U(i, j) + 0.25*U(i - 1, j) + 0.25*U(i + 1, j) 
 iterar i,j:
j:  0  ;  i:  1  ;  ecuacion: 0
U(1, 1) = 0.25*U(0, 0) + 0.5*U(1, 0) + 0.25*U(2, 0)
U(1, 1) = 33.75
j:  0  ;  i:  2  ;  ecuacion: 1
U(2, 1) = 0.25*U(1, 0) + 0.5*U(2, 0) + 0.25*U(3, 0)
U(2, 1) = 25.0
j:  0  ;  i:  3  ;  ecuacion: 2
U(3, 1) = 0.25*U(2, 0) + 0.5*U(3, 0) + 0.25*U(4, 0)
U(3, 1) = 25.0
j:  0  ;  i:  4  ;  ecuacion: 3
U(4, 1) = 0.25*U(3, 0) + 0.5*U(4, 0) + 0.25*U(5, 0)
U(4, 1) = 25.0
j:  0  ;  i:  5  ;  ecuacion: 4
U(5, 1) = 0.25*U(4, 0) + 0.5*U(5, 0) + 0.25*U(6, 0)
U(5, 1) = 25.0
j:  0  ;  i:  6  ;  ecuacion: 5
U(6, 1) = 0.25*U(5, 0) + 0.5*U(6, 0) + 0.25*U(7, 0)
U(6, 1) = 25.0
j:  0  ;  i:  7  ;  ecuacion: 6
U(7, 1) = 0.25*U(6, 0) + 0.5*U(7, 0) + 0.25*U(8, 0)
U(7, 1) = 25.0
j:  0  ;  i:  8  ;  ecuacion: 7
U(8, 1) = 0.25*U(7, 0) + 0.5*U(8, 0) + 0.25*U(9, 0)
U(8, 1) = 25.0
j:  0  ;  i:  9  ;  ecuacion: 8
U(9, 1) = 0.25*U(8, 0) + 0.5*U(9, 0) + 0.25*U(10, 0)
U(9, 1) = 28.75
... continua calculando

x: [0.  0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1. ]
t: [0.   0.01 0.02 0.03 0.04]
 j, U[i,j]
4 [60.   42.77 31.29 26.37 25.14 25.   25.06 25.59 27.7  32.62 40.  ]
3 [60.   40.86 29.38 25.55 25.   25.   25.   25.23 26.88 31.8  40.  ]
2 [60.   38.12 27.19 25.   25.   25.   25.   25.   25.94 30.62 40.  ]
1 [60.   33.75 25.   25.   25.   25.   25.   25.   25.   28.75 40.  ]
0 [60. 25. 25. 25. 25. 25. 25. 25. 25. 25. 40.]
>>> 

Instrucciones en Python

# Método explí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,dx)
yj = np.arange(y0,n_iteraciones*dy+dy,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

# busca el término no conocido
u_explicito = sym.solve(discreta_L,buscar)
u_explicito = sym.Eq(buscar,u_explicito[0])
resultado['discreta_itera'] = u_explicito

print('\nMétodo explícito EDP Parabólica')
print('discreta_itera')
sym.pprint(u_explicito)
# iterando
print('\n iterar i,j:')
j_k = 0
for j_k in range(0,m-1,1):
    for i_k in range(1,n-1,1):
        eq_conteo = j_k*(n-2)+(i_k-1)
        discreta_ij = u_explicito.subs({i:i_k,j:j_k})
        valorij = 0 # calcula valor de nodo ij
        # separa cada término de suma
        term_suma = sym.Add.make_args(discreta_ij.rhs)
        for term_k in term_suma:
            term_ki = 1
            if term_k.is_Function:
                [i_f,j_f] = term_k.args
                term_ki = u_xy[i_f,j_f]
            elif term_k.is_Mul: # mas de un factor
                factor_Mul = sym.Mul.make_args(term_k)
                # separa cada factor de término 
                for factor_k in factor_Mul:
                    if factor_k.is_Function:
                        [i_f,j_f] = factor_k.args
                        term_ki = term_ki*u_xy[i_f,j_f]
                    else:
                        term_ki = term_ki*factor_k
            else:
                term_ki = term_k
            valorij = valorij + term_ki
        [i_f,j_f] = discreta_ij.lhs.args
        u_xy[i_f,j_f] = float(valorij) # actualiza matriz u
        # muestra las primeras m iteraciones
        if eq_conteo<(n-2):
            print('j: ',j_k,' ; ','i: ',i_k,
                  ' ; ','ecuacion:',eq_conteo)
            sym.pprint(discreta_ij)
            print(discreta_ij.lhs,'=',float(valorij))
print('... continua calculando')

# SALIDA
np.set_printoptions(precision=verdigitos)
print('\nx:',xi)
print('t:',yj)
print(' j, U[i,j]')
for j_k in range(m-1,-1,-1):
    print(j_k, (u_xy[:,j_k]))

EDP Parabólica [ contínua a discreta ] || [ sympy_explícito ] [sympy_ implícito ]
..


4. 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 atras 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                             
 d               d              
---(u(x, y)) - 4*--(u(x, y)) = 0
  2              dy             
dx                              
 K_ : 4.0

 Lambda_L :
0.25*Dy
-------
    2  
  Dx   
 Lambda L_k : 0.24999999999999997

 (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.0*L*U(i - 1, j) + 4.0*L*U(i + 1, j) + (-8.0*L - 4)*U(i, j) + 4*U(i, j - 1) =
 0

 discreta :
-6.0*U(i, j) + 4*U(i, j - 1) + 1.0*U(i - 1, j) + 1.0*U(i + 1, j) = 0

Método implícito EDP Parabólica
j: 1 ; i: 1
1.0*U(0, 1) + 4*U(1, 0) - 6.0*U(1, 1) + 1.0*U(2, 1) = 0
-6.0*U(1, 1) + 1.0*U(2, 1) = -160.0
j: 1 ; i: 2
1.0*U(1, 1) + 4*U(2, 0) - 6.0*U(2, 1) + 1.0*U(3, 1) = 0
1.0*U(1, 1) - 6.0*U(2, 1) + 1.0*U(3, 1) = -100.0
j: 1 ; i: 3
1.0*U(2, 1) + 4*U(3, 0) - 6.0*U(3, 1) + 1.0*U(4, 1) = 0
1.0*U(2, 1) - 6.0*U(3, 1) + 1.0*U(4, 1) = -100.0
j: 1 ; i: 4
1.0*U(3, 1) + 4*U(4, 0) - 6.0*U(4, 1) + 1.0*U(5, 1) = 0
1.0*U(3, 1) - 6.0*U(4, 1) + 1.0*U(5, 1) = -100.0
j: 1 ; i: 5
1.0*U(4, 1) + 4*U(5, 0) - 6.0*U(5, 1) + 1.0*U(6, 1) = 0
1.0*U(4, 1) - 6.0*U(5, 1) + 1.0*U(6, 1) = -100.0
j: 1 ; i: 6
1.0*U(5, 1) + 4*U(6, 0) - 6.0*U(6, 1) + 1.0*U(7, 1) = 0
1.0*U(5, 1) - 6.0*U(6, 1) + 1.0*U(7, 1) = -100.0
j: 1 ; i: 7
1.0*U(6, 1) + 4*U(7, 0) - 6.0*U(7, 1) + 1.0*U(8, 1) = 0
1.0*U(6, 1) - 6.0*U(7, 1) + 1.0*U(8, 1) = -100.0
j: 1 ; i: 8
1.0*U(7, 1) + 4*U(8, 0) - 6.0*U(8, 1) + 1.0*U(9, 1) = 0
1.0*U(7, 1) - 6.0*U(8, 1) + 1.0*U(9, 1) = -100.0
j: 1 ; i: 9
1.0*U(8, 1) + 4*U(9, 0) - 6.0*U(9, 1) + 1.0*U(10, 1) = 0
1.0*U(8, 1) - 6.0*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]
j: 2 ; i: 1
1.0*U(0, 2) + 4*U(1, 1) - 6.0*U(1, 2) + 1.0*U(2, 2) = 0
-6.0*U(1, 2) + 1.0*U(2, 2) = -184.020210038146
...
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.]
>>>

Instrucciones 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

# funciones continuas y variables simbólicas usadas
t = sym.Symbol('t',real=True)
y = sym.Symbol('y',real=True)
x = sym.Symbol('x',real=True)
u = sym.Function('u')(x,y)
K = sym.Symbol('K',real=True)
f = sym.Function('f')(x,y) # funcion complemento
# funciones discretas y variables simbólicas usadas
i  = sym.Symbol('i',integer=True,positive=True)
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)

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}

# 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

# dimensiones de la barra
a = 0  # longitud en x
b = 1
y0 = 0 # tiempo inicial, aumenta con dt en n iteraciones

# discretiza
x_tramos = 10
dx  = (b-a)/x_tramos
dy  = dx/10

verdigitos = 2 # para mostrar en tabla de resultados

n_iteraciones = 4 # iteraciones en tiempo

# PROCEDIMIENTO

def edp_coef_Dx(edp,x,ordenx):
    ''' Extrae el coeficiente de la derivada Dx de ordenx
    '''
    coeff_x = 1.0
    # 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=0
            # 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 = 1
            if coeff_usar==1:
                coeff_x = coeff_x*coeff_temp
    return(coeff_x)

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 = 0 # 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=1
        if cambiar:
            term_k = term_k*L/resultado['Lambda_L']
        discreta_L = discreta_L + term_k
        # simplifica unos con decimal a entero 
        discreta_L = discreta_L.subs(1.0,1)
    return(discreta_L)

resultado={}
# expresión todo a la izquierda LHS (Left Hand side)
if not(edp.rhs==0):
    LHS = EDP.lhs
    RHS = EDP.rhs
    edp = sym.Eq(LHS,RHS)
# para 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
resultado['ordenDy'] = ordenDy
# convierte coeficiente d2u/dx2 a 1
coeff_x = edp_coef_Dx(edp,x,ordenDx)
if not(coeff_x==1):
    LHS = LHS/coeff_x
    RHS = RHS/coeff_x
edp = sym.Eq(LHS-RHS,0)
K_ = -edp_coef_Dx(edp,y,ordenDy)
resultado['edp=0']  = edp
resultado['K_']  = float(K_)

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),(1.0,1)])
resultado['Lambda L_k'] = float(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)
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),(1.0+0*x,1)])
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,dx)
yj = np.arange(y0,n_iteraciones*dy+dy,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 = 0 ; 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:
            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 = {}
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]))

EDP Parabólica [ contínua a discreta ] || [ sympy_explícito ] [sympy_ implícito ]

7.4 Métodos para EDP con gráficos animados en Python

Solo para fines didácticos, y como complemento para los ejercicios presentados en la unidad para Ecuaciones Diferenciales Parciales, se presentan las instrucciones para las animaciones usadas en la presentación de los conceptos y ejercicios. Los algoritmos para animación NO son necesarios para realizar los ejercicios, que requieren una parte analítica con al menos tres iteraciones en papel y lápiz. Se lo adjunta como una herramienta didáctica de asistencia para las clases.


EDP Parabólica [ concepto ] [ ejercicio ]

1. EDP Parabólicas método explícito


resultados del algoritmo

Método explícito EDP Parabólica
lambda:  0.25
x: [0.  0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1. ]
t: [0.   0.01 0.02 0.03 0.04 0.05 0.06 0.07 0.08 0.09 0.1 ] ... 1.0
Tabla de resultados en malla EDP Parabólica
j, U[:,: 10 ], primeras iteraciones
10 [60.   48.23 38.42 31.65 27.85 26.33 26.43 27.89 30.76 34.96 40.  ]
9 [60.   47.67 37.58 30.86 27.29 25.96 26.11 27.53 30.39 34.71 40.  ]
8 [60.   47.02 36.63 30.03 26.75 25.64 25.82 27.16 29.99 34.44 40.  ]
7 [60.   46.25 35.56 29.15 26.25 25.37 25.56 26.78 29.53 34.11 40.  ]
6 [60.   45.34 34.34 28.23 25.79 25.17 25.35 26.38 29.   33.72 40.  ]
5 [60.   44.21 32.93 27.29 25.41 25.05 25.18 25.98 28.4  33.23 40.  ]
4 [60.   42.77 31.29 26.37 25.14 25.   25.06 25.59 27.7  32.62 40.  ]
3 [60.   40.86 29.38 25.55 25.   25.   25.   25.23 26.88 31.8  40.  ]
2 [60.   38.12 27.19 25.   25.   25.   25.   25.   25.94 30.62 40.  ]
1 [60.   33.75 25.   25.   25.   25.   25.   25.   25.   28.75 40.  ]
0 [60. 25. 25. 25. 25. 25. 25. 25. 25. 25. 40.]
>>>

Instrucciones en Python

# EDP parabólicas d2u/dx2  = K du/dt
# método explícito,usando diferencias divididas
# Referencia: Chapra 30.2 p.888 pdf.912, Rodriguez 10.2 p.406
import numpy as np

# INGRESO
# Valores de frontera
Ta = 60
Tb = 40
#T0 = 25 # estado inicial de barra
fx = lambda x: 25 + 0*x # función inicial para T0
a = 0  # longitud en x
b = 1

K = 4     # Constante K
dx = 0.1  # Tamaño de paso
dt = dx/10

n = 100 # iteraciones en tiempo
verdigitos = 2   # decimales a mostrar en tabla de resultados

# PROCEDIMIENTO
# iteraciones en longitud
xi = np.arange(a,b+dx,dx)
fi = fx(xi)
m = len(xi)
ultimox = m-1
ultimot = n-1
# Resultados en tabla u[x,t]
u = np.zeros(shape=(m,n+1), dtype=float)

# valores iniciales de u[:,j]
j = 0
u[0,:]= Ta
u[1:ultimox,j] = fi[1:ultimox]
u[ultimox,:] = Tb

# factores P,Q,R
lamb = dt/(K*dx**2)
P = lamb
Q = 1 - 2*lamb
R = lamb

# Calcula U para cada tiempo + dt
tj = np.arange(0,(n+1)*dt,dt)
j = 0
while not(j>ultimot): # igual con lazo for
    for i in range(1,ultimox,1):
        u[i,j+1] = P*u[i-1,j] + Q*u[i,j] + R*u[i+1,j]
    j=j+1

# SALIDA
np.set_printoptions(precision=verdigitos)
print('Método explícito EDP Parabólica')
print('lambda: ',np.around(lamb,verdigitos))
print('x:',xi)
print('t:',tj[0:len(xi)],'...',tj[-1])
print('Tabla de resultados en malla EDP Parabólica')
print('j, U[:,:',ultimox,'], primeras iteraciones')
for j in range(ultimox,-1,-1):
    print(j,u[:,j])

# GRAFICA ------------
import matplotlib.pyplot as plt
tramos = 10
salto = int(n/tramos) # evita muchas líneas
if (salto == 0):
    salto = 1
for j in range(0,n,salto):
    vector = u[:,j]
    plt.plot(xi,vector)
    plt.plot(xi,vector, '.',color='red')
plt.xlabel('x[i]')
plt.ylabel('u[j]')
plt.title('Solución EDP parabólica')
#plt.show()

1.2 Gráficas EDP Parabólica, método explícito en 3D

Esta sección es la base para la generación de las animaciones siguientes, por lo que debe ser incluida antes de las dos siguientes animaciones 2D o 3D. Al menos hasta la parte donde se realiza la transpuesta de U.

# GRAFICA en 3D ------
tj = np.arange(0,n*dt,dt)
tk = np.zeros(tramos,dtype=float)

# Extrae parte de la matriz U,acorde a los tramos
U = np.zeros(shape=(m,tramos),dtype=float)
for k in range(0,tramos,1):
    U[:,k] = u[:,k*salto]
    tk[k] = tj[k*salto]
# Malla para cada eje X,Y
Xi, Yi = np.meshgrid(xi,tk)
U = np.transpose(U)

fig_3D = plt.figure()
graf_3D = fig_3D.add_subplot(111, projection='3d')
graf_3D.plot_wireframe(Xi,Yi,U, color ='blue')
graf_3D.plot(xi[0],tk[1],U[1,0],'o',color ='orange')
graf_3D.plot(xi[1],tk[1],U[1,1],'o',color ='green')
graf_3D.plot(xi[2],tk[1],U[1,2],'o',color ='green')
graf_3D.plot(xi[1],tk[2],U[2,1],'o',color ='salmon',
             label='U[i,j+1]')
graf_3D.set_title('EDP Parabólica')
graf_3D.set_xlabel('x')
graf_3D.set_ylabel('t')
graf_3D.set_zlabel('U')
graf_3D.legend()
graf_3D.view_init(35, -45)
plt.show()

1.3 Gráficas EDP Parabólica, método explícito en 2D con animación

La animación en 2D para la EDP parabólica método explícito se añade, luego de comentar el #plt.show() anterior

# GRAFICA CON ANIMACION 2D--------
# import matplotlib.pyplot as plt
import matplotlib.animation as animation
unmetodo = 'EDP Parabólica - Método explícito'
narchivo = 'EdpParabolica' # nombre archivo GIF

# Parametros de trama/foto
retardo = 500   # milisegundos entre tramas

# GRAFICA animada en fig_ani
fig_ani, graf_ani = plt.subplots()

# Lineas y puntos base
linea_f, = graf_ani.plot(xi,u[:,0])
graf_ani.axhline(0, color='k')  # Eje en x=0

maximoy = np.max(u) 
txt_y = maximoy*(1-0.1)
txt_f = graf_ani.text(xi[0],txt_y,'tiempo:',
                      horizontalalignment='left')

# Configura gráfica
graf_ani.set_xlim([xi[0],xi[ultimox]])
graf_ani.set_ylim([ np.min(u),maximoy])
graf_ani.set_title(unmetodo)
graf_ani.set_xlabel('x')
graf_ani.set_ylabel('u')
#graf_ani.legend()
graf_ani.grid()

# Nueva Trama
def unatrama(i,xi,u):
    # actualiza cada linea
    linea_f.set_ydata(u[:,i])
    linea_f.set_xdata(xi)
    linea_f.set_label('tiempo linea: '+str(i))
    if (i<=9): # color de la línea
        lineacolor = 'C'+str(i)
    else:
        numcolor = i%10
        lineacolor = 'C'+str(numcolor)
    linea_f.set_color(lineacolor)
    txt_f.set_text('tiempo['+str(i)+'] = ' + str(i*dt))
    return linea_f, txt_f,

# Limpia Trama anterior
def limpiatrama(): 
    linea_f.set_ydata(np.ma.array(xi, mask=True))
    linea_f.set_label('')
    txt_f.set_text('')
    return linea_f, txt_f,

# contador de tramas
i = np.arange(0,len(tk),1)
ani = animation.FuncAnimation(fig_ani, unatrama,
                              i ,
                              fargs=(xi,u),
                              init_func=limpiatrama,
                              interval=retardo,
                              blit=True)
# Graba Archivo GIFAnimado y video
ani.save(narchivo+'_animado.gif', writer='imagemagick')
#ani.save(narchivo+'.mp4')
plt.draw()
plt.show()

1.4 Gráficas EDP Parabólica, método explícito en 3D con animación

Ecuación diferencial parcial Parabolica animado

Para el caso de animación 3D, se cambia la sección de animación del algoritmo anterior por:

# GRAFICA CON ANIMACION 3D--------
# import matplotlib.pyplot as plt
import matplotlib.animation as animation
unmetodo = 'EDP Parabólica - Método explícito'
narchivo = 'EdpParabolica2' # nombre archivo GIF

# Parametros de trama/foto
retardo = 500   # milisegundos entre tramas

# GRAFICA animada en fig_ani
fig_ani3D = plt.figure()
graf_ani3 = fig_ani3D.add_subplot(projection='3d')

# Lineas y puntos base
##graf_ani3.plot_wireframe(Xi,Yi,U,
##                         color ='blue',label='EDP Parabólica')
punto_a, = graf_ani3.plot(xi[0],tk[0],U[1,0],'o',color ='green')
punto_b, = graf_ani3.plot(xi[0],tk[0],U[1,0],'o',
                          color ='salmon',label='U[i,j+1]')
linea_c, = graf_ani3.plot(xi[0],tk[0],U[1,0],'-',color ='blue')
# Configura gráfica
graf_ani3.set_xlim(min(xi),max(xi))
graf_ani3.set_ylim(min(tk),max(tk))
graf_ani3.set_title(unmetodo)
graf_ani3.set_xlabel('x')
graf_ani3.set_ylabel('t')
graf_ani3.set_zlabel('U')
graf_ani3.legend()
graf_ani3.grid()
graf_ani3.view_init(35, -45)

mallaEDP = None
# Nueva Trama
def unatrama(i,xi,tk,U,k):
    f = i//k
    c = i%k
    # actualiza cada punto
    punto_a.set_data([[xi[c],xi[c+1],xi[c+2]],
                      [tk[f],tk[f],tk[f]]])
    punto_a.set_3d_properties([U[f,c],U[f,c+1],U[f,c+2]])
    punto_b.set_data([xi[c+1]],[tk[f+1]])
    punto_b.set_3d_properties([U[f+1,c+1]])
    linea_c.set_data(xi[0:c+2],[tk[f+1]]*(c+2))
    linea_c.set_3d_properties([U[f+1,0:c+2]])
    
    global mallaEDP
    if mallaEDP:
        graf_ani3.collections.remove(mallaEDP)
    mallaEDP = graf_ani3.plot_wireframe(Xi[0:f+1,:],
                                        Yi[0:f+1,:],
                                        U[0:f+1,:],
                                        color ='blue',
                                        label='EDP Parabólica')
    return (punto_a,punto_b,linea_c)


# contador de tramas
k = len(xi)-2
k2 = k*(len(tk)-1)
i = np.arange(0,k2,1)
ani = animation.FuncAnimation(fig_ani3D, unatrama,
                              i ,
                              fargs=(xi,tk,U,k),
                              interval=retardo,
                              blit=False)
# Graba Archivo GIFAnimado y video
ani.save(narchivo+'_animado.gif', writer='imagemagick')
#ani.save(narchivo+'.mp4')
#plt.draw()
plt.show()

EDP Parabólica [ concepto ] [ ejercicio ]


2. EDP Parabólicas método implícito

Ecuación Diferencial Parcial Parabólica método Implicito animado

Puesto que la solución de la tabla de resultados en U es la misma que el los dos métodos explícito e implícito, solo se adjunta la parte de la animación.

Para el caso del la gráfica 3D del método implícito se cambia la sección de gráfica animada por:

# GRAFICA CON ANIMACION 3D--------
# import matplotlib.pyplot as plt
import matplotlib.animation as animation
unmetodo = 'EDP Parabólica - Método implícito'
narchivo = 'EdpParabImpli' # nombre archivo GIF

# Parametros de trama/foto
retardo = 500   # milisegundos entre tramas

# GRAFICA animada en fig_ani
fig_ani3D = plt.figure()
graf_ani3 = fig_ani3D.add_subplot(projection='3d')

# Lineas y puntos base
##graf_ani3.plot_wireframe(Xi,Yi,U,
##                         color ='blue',label='EDP Parabólica')
punto_a, = graf_ani3.plot(xi[0],tk[0],U[1,0],'o',color ='salmon')
punto_b, = graf_ani3.plot(xi[0],tk[0],U[1,0],'o',
                          color ='green',label='U[i,j-1]')
linea_c, = graf_ani3.plot(xi[0],tk[0],U[1,0],'o',color ='blue')
# Configura gráfica
graf_ani3.set_xlim(min(xi),max(xi))
graf_ani3.set_ylim(min(tk),max(tk))
graf_ani3.set_title(unmetodo)
graf_ani3.set_xlabel('x')
graf_ani3.set_ylabel('t')
graf_ani3.set_zlabel('U')
graf_ani3.legend()
graf_ani3.grid()
graf_ani3.view_init(35, -45)

mallaEDP = None
# Nueva Trama
def unatrama(i,xi,tk,U,k):
    f = i//k
    c = i%k
    # actualiza cada punto
    punto_a.set_data([[xi[c],xi[c+1],xi[c+2]],
                      [tk[f+1],tk[f+1],tk[f+1]]])
    punto_a.set_3d_properties([U[f+1,c],U[f+1,c+1],U[f+1,c+2]])
    punto_b.set_data([xi[c+1]],[tk[f]])
    punto_b.set_3d_properties([U[f,c+1]])
    linea_c.set_data(xi[0:c],[tk[f+1]]*(c))
    linea_c.set_3d_properties([U[f+1,0:c]])
    
    global mallaEDP
    if mallaEDP:
        graf_ani3.collections.remove(mallaEDP)
    mallaEDP = graf_ani3.plot_wireframe(Xi[0:f+1,:],
                                        Yi[0:f+1,:],
                                        U[0:f+1,:],
                                        color ='blue',
                                        label='EDP Parabólica')
    return (punto_a,punto_b,linea_c)


# contador de tramas
k = len(xi)-2
k2 = k*(len(tk)-1)
i = np.arange(0,k2,1)
ani = animation.FuncAnimation(fig_ani3D, unatrama,
                              i ,
                              fargs=(xi,tk,U,k),
                              interval=retardo,
                              blit=False)
# Graba Archivo GIFAnimado y video
ani.save(narchivo+'_animado.gif', writer='imagemagick')
#ani.save(narchivo+'.mp4')
#plt.draw()
plt.show()

EDP Parabólica [ concepto ] [ ejercicio ]


2. EDP Elípticas método iterativo

Edp Eliptica Itera animado

Al algoritmo desarrollado en EDP Elípticas método iterativo, se crean una lista para almacenar los valores de la matriz U por cada iteración, se usan para graficar cada iteración.

# Ecuaciones Diferenciales Parciales
# Elipticas. Método iterativo
import numpy as np

# INGRESO
# Condiciones iniciales en los bordes
Ta = 60     # izquierda
Tb = 25     # derecha
#Tc = 50    # inferior 
fxc = lambda x: 50+0*x # función inicial inferior
# Td = 70   # superior
fxd = lambda x: 70+0*x # función inicial superior

# dimensiones de la placa
x0 = 0    # longitud en x
xn = 2
y0 = 0    # longitud en y
yn = 1.5
# discretiza, supone dx=dy
dx = 0.25  # Tamaño de paso
dy = dx
iteramax = 100
tolera = 0.0001
verdigitos = 2   # decimales a mostrar en tabla de resultados

# PROCEDIMIENTO
xi = np.arange(x0,xn+dx,dx)
yj = np.arange(y0,yn+dy,dy)
n = len(xi)
m = len(yj)

# Matriz u
u = np.zeros(shape=(n,m),dtype = float)
# llena u con valores en fronteras
u[0,:]   = Ta       # izquierda
u[n-1,:] = Tb       # derecha
fic = fxc(xi)
u[:,0]   = fic  # Tc inferior
fid = fxd(xi)
u[:,m-1] = fid  # Td superior
# valor inicial de iteración dentro de u
# promedio = (Ta+Tb+Tc+Td)/4
promedio = (Ta+Tb+np.max(fic)+np.max(fid))/4
u[1:n-1,1:m-1] = promedio
np.set_printoptions(precision=verdigitos)
print('u inicial:')
print(np.transpose(u))

# iterar puntos interiores
U_3D = [np.copy(u)]
U_error = ['--']
itera = 0
converge = False
while (itera<=iteramax and converge==False):
    itera = itera +1
    Uantes = np.copy(u)
    for i in range(1,n-1):
        for j in range(1,m-1):
            u[i,j] = (u[i-1,j]+u[i+1,j]+u[i,j-1]+u[i,j+1])/4
    diferencia = u - Uantes
    errado_u = np.max(np.abs(diferencia))
    if (errado_u<tolera):
        converge=True
    U_3D.append(np.copy(u))
    U_error.append(np.around(errado_u,verdigitos))
    if itera<=3:
        np.set_printoptions(precision=verdigitos)
        print('itera:',itera-1,'  ; ','u:')
        print(np.transpose(u))
        print('errado_u: ', errado_u)
    if itera==4:
        print('... continua')
        
# SALIDA
print('Método Iterativo EDP Elíptica')
print('iteraciones:',itera,' converge = ', converge)
print('xi:', xi)
print('yj:', yj)
print('Tabla de resultados en malla EDP Elíptica')
print('j, U[i,j]')
for j in range(m-1,-1,-1):
    print(j,u[:,j])

# GRAFICA en 3D ------
import matplotlib.pyplot as plt
# Malla para cada eje X,Y
Xi, Yi = np.meshgrid(xi, yj)
U = np.transpose(u) # ajuste de índices fila es x

fig_3D = plt.figure()
graf_3D = fig_3D.add_subplot(111, projection='3d')
graf_3D.plot_wireframe(Xi,Yi,U,
                       color ='blue')
graf_3D.plot(Xi[1,0],Yi[1,0],U[1,0],'o',color ='orange')
graf_3D.plot(Xi[1,1],Yi[1,1],U[1,1],'o',color ='red',
             label='U[i,j]')
graf_3D.plot(Xi[1,2],Yi[1,2],U[1,2],'o',color ='salmon')
graf_3D.plot(Xi[0,1],Yi[0,1],U[0,1],'o',color ='green')
graf_3D.plot(Xi[2,1],Yi[2,1],U[2,1],'o',color ='salmon')

graf_3D.set_title('EDP elíptica')
graf_3D.set_xlabel('x')
graf_3D.set_ylabel('y')
graf_3D.set_zlabel('U')
graf_3D.legend()
graf_3D.view_init(35, -45)
plt.show()

# GRAFICA CON ANIMACION 3D--------
# import matplotlib.pyplot as plt
import matplotlib.animation as animation
unmetodo = 'EDP Eliptica - Método iterativo'
narchivo = 'EdpElipticaItera' # nombre archivo GIF

# Parametros de trama/foto
retardo = 500   # milisegundos entre tramas

# GRAFICA animada en fig_ani
fig_ani3D = plt.figure()
graf_ani3 = fig_ani3D.add_subplot(projection='3d')

# Lineas y puntos base
punto_a, = graf_ani3.plot(xi[0],yj[0],U[0,0],'.',color ='blue')

# Configura gráfica
graf_ani3.set_xlim(min(xi),max(xi))
graf_ani3.set_ylim(min(yj),max(yj))
graf_ani3.set_title(unmetodo)
graf_ani3.set_xlabel('x')
graf_ani3.set_ylabel('t')
graf_ani3.set_zlabel('U')
# graf_ani3.legend()
graf_ani3.grid()
graf_ani3.view_init(35, -45)

etiqueta_i ='itera: '+str(0)
etiqueta_err = 'errado['+str(0)+']: '+str(U_error[0]) 
txt_i = graf_ani3.text2D(0.05, 0.95, etiqueta_i,
                         transform=graf_ani3.transAxes)
txt_errado = graf_ani3.text2D(0.05, 0.90, etiqueta_err,
                              transform=graf_ani3.transAxes)

mallaEDP = None
# Nueva Trama
def unatrama(i,xi,yj,U_3D,U_error):

    etiqueta_i ='itera: '+str(i)
    txt_i.set_text(etiqueta_i)
    etiqueta_err = 'errado['+str(i)+']: '+str(U_error[i])
    txt_errado.set_text(etiqueta_err)
    global mallaEDP
    if mallaEDP:
        graf_ani3.collections.remove(mallaEDP)
    mallaEDP = graf_ani3.plot_wireframe(Xi,Yi,
                                        np.transpose(U_3D[i]),
                                        color ='blue')
    return (txt_i,txt_errado,)


# contador de tramas
k = 11 #len(U_3D)
i = np.arange(0,k,1)
ani = animation.FuncAnimation(fig_ani3D, unatrama,
                              i ,
                              fargs=(xi,yj,U_3D,U_error),
                              interval=retardo,
                              blit=False)
# Graba Archivo GIFAnimado y video
ani.save(narchivo+'_animado.gif', writer='imagemagick')
#ani.save(narchivo+'.mp4')
#plt.draw()
plt.show()

 

6.5.1 EDO lineal – ecuaciones auxiliar, general y complementaria con Sympy-Python

Referencia: Sympy ODE solver. Stewart James. Cálculo de varias variables. 17.2 p1148 pdf545. Lathi Ejemplo 2.1.a p155, Ejemplo 2.9 p175, Oppenheim 2.4.1 p117, ejemplo 1 de Modelo entrada-salida,

Continuando con el ejercicio de la sección anterior de una ecuación diferencial ordinaria, lineal y de coeficientes constantes, se obtuvo la solución homogénea o ecuación complementaria con dsolve().  Para el desarrollo analítico de la solución se requieren describir los pasos tales como la ecuación auxiliar, como se describe a continuación como un ejercicio de análisis de expresiones con Sympy.

Ejemplo 1. Ecuación diferencial de un circuito RLC, ecuación complementaria.circuito RLC

El circuito RLC con entrada x(t) de la figura tiene una corriente y(t) o salida del sistema que se representa  por medio de una ecuación diferencial.

d2y(t)dt2+3dy(t)dt+2y(t)=dx(t)dt \frac{d^{2}y(t)}{dt^{2}} + 3\frac{dy(t)}{dt} + 2y(t) = \frac{dx(t)}{dt}

Las condiciones iniciales del sistema a tiempo t=0 son y(0)=0 , y'(0)=-5

1. Ecuación diferencial y condiciones de frontera o iniciales

La ecuación diferencial del ejercicio con Sympy se escribe con el operador sym.diff(y,t,1). Indicando la variable independiente ‘t‘, que ‘y‘ es una función y el orden de la derivada con 1. esquema que se mantiene para la descripción de las condiciones iniciales.

# ecuacion: lado izquierdo = lado derecho
#           Left Hand Side = Right Hand Side
LHS = sym.diff(y(t),t,2) + 3*sym.diff(y(t),t,1) + 2*y(t)
RHS = sym.diff(x(t),t,1)
ecuacion = sym.Eq(LHS,RHS)

# condiciones de frontera o iniciales
y_cond = {y(0) : 0,
          sym.diff(y(t),t,1).subs(t,0) : -5}

2. Clasificar la ecuación diferencial ordinaria

Sympy permite revisar el tipo de EDO a resolver usando la instrucción:

>>> sym.classify_ode(ecuacion, y(t))
('factorable', 
 'nth_linear_constant_coeff_variation_of_parameters', 
 'nth_linear_constant_coeff_variation_of_parameters_Integral')
>>>

3. Ecuación homogenea

Para el análisis de la respuesta del circuito, se inicia haciendo la entrada x(t)=0, también conocida como ecuación para «respuesta a entrada cero» o ecuación homogenea.

d2y(t)dt2+3dy(t)dt+2y(t)=0 \frac{d^{2}y(t)}{dt^{2}} + 3\frac{dy(t)}{dt} + 2y(t) = 0

En el algoritmo, La ecuación homogénea se obtiene al substituir x(t)=0 en cada lado de la ecuación, que también es un paso para encontrar la respuesta a entrada cero. La ecuacion homogenea se escribe de la forma f(t)=0.

# ecuación homogénea x(t)=0, entrada cero
RHSx0 = ecuacion.rhs.subs(x(t),0).doit()
LHSx0 = ecuacion.lhs.subs(x(t),0).doit()
homogenea = LHSx0 - RHSx0

4. Ecuación auxiliar o característica.

En la ecuación homogenea , se procede a sustituir en la ecuación el operador dy/dt de la derivada con una variable r elevada al orden de la derivada de cada término . El resultado es una ecuación algebraica que se analiza encontrando las raíces de r.

r2+3r+2=0 r ^2 + 3r +2 = 0

Los valores de ‘r’ que resuelven la ecuación permiten estimar la forma de la solución para y(t) conocida como la ecuación general

Se usan diferentes formas para mostrar la ecuación auxiliar, pues en algunos casos se requiere usar la forma de factores, en otros casos los valores de las raíces y las veces que se producen. Formas usadas para generar diagramas de polos y ceros, o expresiones de transformada de Laplace. Motivo por el que los resultados se los presenta en un diccionario, y asi usar la respuesta que sea de interés en cada caso.

homogenea :
                        2          
           d           d           
2*y(t) + 3*--(y(t)) + ---(y(t)) = 0
           dt           2          
                      dt           
auxiliar : r**2 + 3*r + 2
Q : r**2 + 3*r + 2
Q_factor : (r + 1)*(r + 2)
Q_raiz : {-1: 1, -2: 1}
>>> 

Instrucciones con Python

# Ecuación Diferencial Ordinaria EDO
# ecuación auxiliar o característica 
# Lathi 2.1.a pdf 155, (D^2+ 3D + 2)y = Dx
import sympy as sym

# INGRESO
t = sym.Symbol('t', real=True)
r = sym.Symbol('r')
y = sym.Function('y')
x = sym.Function('x')

# ecuacion: lado izquierdo = lado derecho
#           Left Hand Side = Right Hand Side
LHS = sym.diff(y(t),t,2) + 3*sym.diff(y(t),t,1) + 2*y(t)
RHS = sym.diff(x(t),t,1)
ecuacion = sym.Eq(LHS,RHS)

# condiciones de frontera o iniciales
y_cond = {y(0) : 0,
          sym.diff(y(t),t,1).subs(t,0) : -5}

# PROCEDIMIENTO
def edo_lineal_auxiliar(ecuacion,
                 t = sym.Symbol('t'),r = sym.Symbol('r'),
                 y = sym.Function('y'),x = sym.Function('x')):
    ''' ecuacion auxiliar o caracteristica de EDO
        t independiente
    '''
    # ecuación homogénea x(t)=0, entrada cero
    RHSx0 = ecuacion.rhs.subs(x(t),0).doit()
    LHSx0 = ecuacion.lhs.subs(x(t),0).doit()
    homogenea = LHSx0 - RHSx0
    homogenea = sym.expand(homogenea,t)

    # ecuación auxiliar o característica
    Q = 0*r
    term_suma = sym.Add.make_args(homogenea)
    for term_k in term_suma:
        orden_k = sym.ode_order(term_k,y)
        coef = 1 # coefientes del término suma
        factor_mul = sym.Mul.make_args(term_k)
        for factor_k in factor_mul:
            cond = factor_k.has(sym.Derivative)
            cond = cond or factor_k.has(y(t))
            if not(cond):
                coef = coef*factor_k
        Q = Q + coef*(r**orden_k)
               
    # Q factores y raices
    Q_factor = sym.factor(Q,r)
    Q_poly   = sym.poly(Q,r)
    Q_raiz   = sym.roots(Q_poly)
    
    auxiliar = {'homogenea' : sym.Eq(homogenea,0),
                'auxiliar'  : Q,
                'Q'         : Q,
                'Q_factor'  : Q_factor,
                'Q_raiz'    : Q_raiz }
    return(auxiliar)

# analiza la ecuación diferencial
edo_tipo = sym.classify_ode(ecuacion, y(t))
auxiliar = edo_lineal_auxiliar(ecuacion,t,r)

# SALIDA
print('clasifica EDO:')
for untipo in edo_tipo:
    print(' ',untipo)
print('ecuacion auxiliar:')
for entrada in auxiliar:
    print(' ',entrada,':',auxiliar[entrada])

Otra forma de mostrar el resultado es usando un procedimiento creado para mostrar elementos del diccionario según corresponda a cada tipo. el procedimiento se adjunta al final.

Las funciones se incorporan a los algoritmos del curso en matg1052.py


6. Ecuación diferencial, ecuación general y complementaria con Sympy-Python

La ecuación general se encuentra con la instrucción sym.dsolve(), sin condiciones iniciales, por que el resultado presenta constantes Ci por determinar.

    # solucion general de ecuación homogénea
    general = sym.dsolve(homogenea, y(t))
    general = general.expand()

el resultado para el ejercicio es

general :
           -t       -2*t
y(t) = C1*e   + C2*e

Para encontrar los valores de las constantes, se aplica cada una de las condiciones iniciales a la ecuación general, obteniendo un sistema de ecuaciones.

     # Aplica condiciones iniciales o de frontera
    eq_condicion = []
    for cond_k in y_cond: # cada condición
        valor_k = y_cond[cond_k]
        orden_k = sym.ode_order(cond_k,y)
        if orden_k==0: # condicion frontera
            t_k = cond_k.args[0] # f(t_k)
            expr_k = general.rhs.subs(t,t_k)
        else: # orden_k>0
            # f.diff(t,orden_k).subs(t,t_k)
            subs_param = cond_k.args[2] # en valores
            t_k = subs_param.args[0]  # primer valor
            dyk = general.rhs.diff(t,orden_k)
            expr_k = dyk.subs(t,t_k)
        eq_condicion.append(sym.Eq(valor_k,expr_k))

con el siguiente resultado:

eq_condicion :
0 = C1 + C2
-5 = -C1 - 2*C2

El sistema de ecuaciones se resuelve con la instrucción

constante = sym.solve(eq_condicion)

que entrega un diccionario con cada valor de la constante

constante : {C1: -5, C2: 5}

La ecuación complementaria se obtiene al sustituir en la ecuación general los valores de las constantes.

El resultado del algoritmo

homogenea :
                        2          
           d           d           
2*y(t) + 3*--(y(t)) + ---(y(t)) = 0
           dt           2          
                      dt           
general :
           -t       -2*t
y(t) = C1*e   + C2*e    
eq_condicion :
0 = C1 + C2
-5 = -C1 - 2*C2
constante : {C1: -5, C2: 5}
complementaria :
            -t      -2*t
y(t) = - 5*e   + 5*e    
>>>

Instrucciones con Python

Las instrucciones detalladas se presentan en el algoritmo integrado.

# Solución complementaria a una Ecuación Diferencial Ordinaria EDO
# Lathi 2.1.a pdf 155, (D^2+ 3D + 2)y = Dx
import sympy as sym
equivalentes = [{'DiracDelta': lambda x: 1*(x==0)},
                {'Heaviside': lambda x,y: np.heaviside(x, 1)},
                'numpy',]
import matg1052 as fcnm

# INGRESO
t = sym.Symbol('t', real=True)
r = sym.Symbol('r')
y = sym.Function('y')
x = sym.Function('x')

# ecuacion: lado izquierdo = lado derecho
#           Left Hand Side = Right Hand Side
LHS = sym.diff(y(t),t,2) + 3*sym.diff(y(t),t,1) + 2*y(t)
RHS = sym.diff(x(t),t,1)
ecuacion = sym.Eq(LHS,RHS)

# condiciones de frontera o iniciales
y_cond = {y(0) : 0,
          sym.diff(y(t),t,1).subs(t,0) : -5}

# PROCEDIMIENTO
def edo_lineal_complemento(ecuacion,y_cond):
    # ecuación homogénea x(t)=0, entrada cero
    RHSx0 = ecuacion.rhs.subs(x(t),0).doit()
    LHSx0 = ecuacion.lhs.subs(x(t),0).doit()
    homogenea = LHSx0 - RHSx0

    # solucion general de ecuación homogénea
    general = sym.dsolve(homogenea, y(t))
    general = general.expand()

    # Aplica condiciones iniciales o de frontera
    eq_condicion = []
    for cond_k in y_cond: # cada condición
        valor_k = y_cond[cond_k]
        orden_k = sym.ode_order(cond_k,y)
        if orden_k==0: # condicion frontera
            t_k    = cond_k.args[0] # f(t_k)
            expr_k = general.rhs.subs(t,t_k)
        else: # orden_k>0
            subs_param = cond_k.args[2] # en valores
            t_k = subs_param.args[0]  # primer valor
            dyk = sym.diff(general.rhs,t,orden_k)
            expr_k = dyk.subs(t,t_k)
        eq_condicion.append(sym.Eq(valor_k,expr_k))

    constante = sym.solve(eq_condicion)

    # ecuacion complementaria
    # reemplaza las constantes en general
    yc = general
    for Ci in constante:
        yc = yc.subs(Ci, constante[Ci])
    
    complemento = {'homogenea'      : sym.Eq(homogenea,0),
                   'general'        : general,
                   'eq_condicion'   : eq_condicion,
                   'constante'      : constante,
                   'complementaria' : yc}
    return(complemento)

# analiza ecuacion diferencial
edo_tipo = sym.classify_ode(ecuacion, y(t))
auxiliar = fcnm.edo_lineal_auxiliar(ecuacion,t,r)
complemento = edo_lineal_complemento(ecuacion,y_cond)
yc = complemento['complementaria'].rhs

# SALIDA
print('clasifica EDO:')
for elemento in edo_tipo:
    print(' ',elemento)

fcnm.print_resultado_dict(auxiliar)
fcnm.print_resultado_dict(complemento)

7. Gráfica de la ecuación complementaria yc

Para la gráfica se procede a convertir la ecuación yc en la forma numérica con sym.lambdify(). Se usa un intervalo de tiempo entre [0,5] y 51 muestras con el siguiente resultado:

solucion homogenea EDO

Instrucciones en Python

# GRAFICA ------------------
import numpy as np
import matplotlib.pyplot as plt

# INGRESO
t_a = 0 ; t_b = 5 # intervalo tiempo [t_a,t_b]
muestras = 51

# PROCEDIMIENTO
yt = sym.lambdify(t,yc, modules=equivalentes) 
ti = np.linspace(t_a,t_b,muestras)
yi = yt(ti)

# Grafica
plt.plot(ti,yi, color='orange', label='yc(t)')
untitulo = 'yc(t)=$'+ str(sym.latex(yc))+'$'
plt.title(untitulo)
plt.xlabel('t')
plt.ylabel('yc(t)')
plt.legend()
plt.grid()
plt.show()

8.  Mostrar el resultado del diccionario según el tipo de entrada

Para mostrar los resultados de una forma más fácil de leer, se usará sym.pprint() para las ecuaciones, y print() simple para los demás elementos del resultado

def print_resultado_dict(resultado):
    ''' print de diccionario resultado
        formato de pantalla
    '''
    for entrada in resultado:
        tipo = type(resultado[entrada])
        if tipo == sym.core.relational.Equality:
            print(entrada,':')
            sym.pprint(resultado[entrada])
        elif tipo==list or tipo==tuple:
            tipoelem = type(resultado[entrada][0])
            if tipoelem==sym.core.relational.Equality:
                print(entrada,':')
                for fila in resultado[entrada]:
                    sym.pprint(fila)
            else:
                print(entrada,':')
                for fila in resultado[entrada]:
                    print(' ',fila)
        else:
            print(entrada,':',resultado[entrada])
    return()

s1Eva_2023PAOI_T3 Recoger los restos de sumergible

ejercicio: 1Eva_2023PAOI_T3 Recoger los restos de sumergible

literal a

Para realizar el planteamiento del ejercicio, se realiza la gráfica para observar mejor los puntos.

recorrido de polinomio

Se podría observar los puntos y plantear un primer recorrido como el siguiente

Rov Sumergible Polinomio01a

los puntos corresponden a los índices j = [0 ,2, 5, 6, 11]:

# INGRESO
xj = np.array([0.2478, 0.6316, 1.3802, 2.4744, 2.7351, 2.8008,
               3.5830, 3.6627, 3.7213, 4.2796, 4.3757, 4.6794])
fj = np.array([1.8108, 0.1993, 4.7199, 2.4529, 2.3644, 3.5955,
               2.6558, 4.3657, 4.1932, 4.6998, 4.7536, 1.5673])

# selecionando puntos
j = [0, 2, 5, 6, 11]
xi = xj[j]
fi = fj[j]

literal b

Partiendo del algoritmo de Diferencias divididas de Newton, y añadiendo vectores xj y fj para todos los puntos dados en el ejercicio.

Tabla Diferencia Dividida
[['i   ', 'xi  ', 'fi  ', 'F[1]', 'F[2]', 'F[3]', 'F[4]', 'F[5]']]
[[ 0.      0.2478  1.8108  2.569  -1.3163  0.3389 -0.0561  0.    ]
 [ 1.      1.3802  4.7199 -0.7915 -0.1861  0.09    0.      0.    ]
 [ 2.      2.8008  3.5955 -1.2014  0.111   0.      0.      0.    ]
 [ 3.      3.583   2.6558 -0.9928  0.      0.      0.      0.    ]
 [ 4.      4.6794  1.5673  0.      0.      0.      0.      0.    ]]
dDividida: 
[ 2.569  -1.3163  0.3389 -0.0561  0.    ]
polinomio: 
2.56896856234546*x - 0.0561488275125463*(x - 3.583)*(x - 2.8008)*(x - 1.3802)*(x - 0.2478) 
+ 0.338875729488637*(x - 2.8008)*(x - 1.3802)*(x - 0.2478) 
- 1.31628089036375*(x - 1.3802)*(x - 0.2478) + 1.17420959025079
polinomio simplificado: 
-0.0561488275125463*x**4 + 0.788728905753656*x**3 
- 3.98331084054791*x**2 + 7.41286537452727*x 
+ 0.206696844067185
>>>

usando la tabla de diferencias divididas se plantea la expresión para el polinomio:

p(x)=1.8108+2.569(x0.2478)+ p(x) = 1.8108 + 2.569 (x-0.2478) + +(1.3163)(x0.2478)(x1.3802)+ + (-1.3163)(x-0.2478)(x-1.3802) + +0.3389(x0.2478)(x1.3802)(x2.8008)+ + 0.3389 (x-0.2478)(x-1.3802)(x-2.8008) + +(0.0561)(x0.2478)(x1.3802)(x2.8008)(x3.583) + (-0.0561)(x-0.2478)(x-1.3802)(x-2.8008)(x-3.583)

el algoritmo simplifica la expresión al siguiente polinomio,

p(x)=0.0561x4+0.7887x33.9833x2+7.4128x+0.2066 p(x) = -0.0561 x^4 + 0.7887 x^3 - 3.9833 x^2 + 7.4128 x + 0.2066

literal c

Se usa el algoritmo Interpolación polinómica de Lagrange con Python para desarrollar el polinomio a partir de los puntos no usados en el literal anterior.

j = [3,4,7,8,9,10]

Para evitar oscilaciones grandes, se excluye el punto con índice 1, y se obtiene una opción mostrada:

Rov Sumergible Polinomio 02a

con el siguiente resultado:

    valores de fi:  [2.4529 2.3644 4.3657 4.1932 4.6998 4.7536]
divisores en L(i):  [-1.32578996  0.60430667 -0.02841115  0.02632723 -0.09228243  0.13986517]

Polinomio de Lagrange, expresiones
-1.85014223337671*(x - 4.3757)*(x - 4.2796)*(x - 3.7213)*(x - 3.6627)*(x - 2.7351) 
+ 3.9125829809921*(x - 4.3757)*(x - 4.2796)*(x - 3.7213)*(x - 3.6627)*(x - 2.4744) 
- 153.661523787291*(x - 4.3757)*(x - 4.2796)*(x - 3.7213)*(x - 2.7351)*(x - 2.4744) 
+ 159.272361555669*(x - 4.3757)*(x - 4.2796)*(x - 3.6627)*(x - 2.7351)*(x - 2.4744) 
- 50.928437685792*(x - 4.3757)*(x - 3.7213)*(x - 3.6627)*(x - 2.7351)*(x - 2.4744) 
+ 33.9870187307222*(x - 4.2796)*(x - 3.7213)*(x - 3.6627)*(x - 2.7351)*(x - 2.4744)

Polinomio de Lagrange: 
-9.26814043907703*x**5 + 163.708008152209*x**4 
- 1143.16428460497*x**3 + 3941.13583467614*x**2 
- 6701.74628786718*x + 4496.64364271972
>>> 

Para presentar la parte analítica puede seleccionar menos puntos, se busca mostrar la aplicación del algoritmo, no necesariamente cuan largo puede ser.

p(x)= p(x) = +2.4529(x4.3757)(x4.2796)(x3.7213)(x3.6627)(x2.7351)(2.47444.3757)(2.47444.2796)(2.47443.7213)(2.47443.6627)(2.47442.7351) + 2.4529\frac{(x - 4.3757)(x - 4.2796)(x - 3.7213)(x - 3.6627)(x - 2.7351) }{(2.4744 - 4.3757)(2.4744 - 4.2796)(2.4744 - 3.7213)(2.4744 - 3.6627)(2.4744 - 2.7351)} +2.3644(x4.3757)(x4.2796)(x3.7213)(x3.6627)(x2.4744)(2.73514.3757)(2.73514.2796)(2.73513.7213)(2.73513.6627)(2.73512.4744) + 2.3644\frac{ (x - 4.3757)(x - 4.2796)(x - 3.7213)(x - 3.6627)(x - 2.4744) }{(2.7351 - 4.3757)(2.7351 - 4.2796)(2.7351 - 3.7213)(2.7351 - 3.6627)(2.7351 - 2.4744)} +4.3657(x4.3757)(x4.2796)(x3.7213)(x2.7351)(x2.4744)(3.66274.3757)(3.66274.2796)(3.66273.7213)(3.66272.7351)(3.66272.4744) +4.3657\frac{(x - 4.3757)(x - 4.2796)(x - 3.7213)(x - 2.7351)(x - 2.4744) }{(3.6627 - 4.3757)(3.6627 - 4.2796)(3.6627 - 3.7213)(3.6627 - 2.7351)(3.6627 - 2.4744)} +4.1932(x4.3757)(x4.2796)(x3.6627)(x2.7351)(x2.4744)(3.72134.3757)(3.72134.2796)(3.72133.6627)(3.72132.7351)(3.72132.4744) + 4.1932 \frac{(x - 4.3757)(x - 4.2796)(x - 3.6627)(x - 2.7351)(x - 2.4744) }{(3.7213 - 4.3757)(3.7213 - 4.2796)(3.7213 - 3.6627)(3.7213 - 2.7351)(3.7213 - 2.4744)} +4.6998(x4.3757)(x3.7213)(x3.6627)(x2.7351)(x2.4744)(4.27964.3757)(4.27963.7213)(4.27963.6627)(4.27962.7351)(4.27962.4744) +4.6998\frac{(x - 4.3757)(x - 3.7213)(x - 3.6627)(x - 2.7351)(x - 2.4744) }{(4.2796 - 4.3757)(4.2796 - 3.7213)(4.2796 - 3.6627)(4.2796 - 2.7351)(4.2796 - 2.4744)} +4.7536(x4.2796)(x3.7213)(x3.6627)(x2.7351)(x2.4744)(4.37574.2796)(4.37573.7213)(4.37573.6627)(4.37572.7351)(4.37572.4744) + 4.7536 \frac{(x - 4.2796)(x - 3.7213)(x - 3.6627)(x - 2.7351)(x - 2.4744)}{(4.3757 - 4.2796)(4.3757 - 3.7213)(4.3757 - 3.6627)(4.3757 - 2.7351)(4.3757 - 2.4744)}

literal d

las gráficas se han presentado en el planteamiento para justificar los criterios usados.

literal e

Los errores de encuentran como la diferencia de los puntos no usados en el polinomio, y evaluando el polinomio en cada coordenada x.

Como las propuestas de polinomio pueden ser variadas se obtendrán diferentes respuestas para cada literal.

Se revisa la aplicación de conceptos en las propuestas.

 

Algoritmos usados

literal b. Diferencias divididas de Newton

# 1Eva_2023PAOI_T3 Recoger los restos de sumergible
# Polinomio interpolación
# Diferencias Divididas de Newton
# Tarea: Verificar tamaño de vectores,
#        verificar puntos equidistantes en x
import numpy as np
import sympy as sym
import matplotlib.pyplot as plt

# INGRESO
xj = np.array([0.2478, 0.6316, 1.3802, 2.4744, 2.7351, 2.8008,
               3.5830, 3.6627, 3.7213, 4.2796, 4.3757, 4.6794])
fj = np.array([1.8108, 0.1993, 4.7199, 2.4529, 2.3644, 3.5955,
               2.6558, 4.3657, 4.1932, 4.6998, 4.7536, 1.5673])

# selecionando puntos
j = [0, 2, 5, 6, 11]
xi = xj[j]
fi = fj[j]


# PROCEDIMIENTO

# Tabla de Diferencias Divididas Avanzadas
titulo = ['i   ','xi  ','fi  ']
n = len(xi)
ki = np.arange(0,n,1)
tabla = np.concatenate(([ki],[xi],[fi]),axis=0)
tabla = np.transpose(tabla)

# diferencias divididas vacia
dfinita = np.zeros(shape=(n,n),dtype=float)
tabla = np.concatenate((tabla,dfinita), axis=1)

# Calcula tabla, inicia en columna 3
[n,m] = np.shape(tabla)
diagonal = n-1
j = 3
while (j < m):
    # Añade título para cada columna
    titulo.append('F['+str(j-2)+']')

    # cada fila de columna
    i = 0
    paso = j-2 # inicia en 1
    while (i < diagonal):
        denominador = (xi[i+paso]-xi[i])
        numerador = tabla[i+1,j-1]-tabla[i,j-1]
        tabla[i,j] = numerador/denominador
        i = i+1
    diagonal = diagonal - 1
    j = j+1

# POLINOMIO con diferencias Divididas
# caso: puntos equidistantes en eje x
dDividida = tabla[0,3:]
n = len(dfinita)

# expresión del polinomio con Sympy
x = sym.Symbol('x')
polinomio = fi[0]
for j in range(1,n,1):
    factor = dDividida[j-1]
    termino = 1
    for k in range(0,j,1):
        termino = termino*(x-xi[k])
    polinomio = polinomio + termino*factor

# simplifica multiplicando entre (x-xi)
polisimple = polinomio.expand()

# polinomio para evaluacion numérica
px = sym.lambdify(x,polisimple)

# Puntos para la gráfica
muestras = 101
a = np.min(xi)
b = np.max(xi)
pxi = np.linspace(a,b,muestras)
pfi = px(pxi)

# SALIDA
np.set_printoptions(precision = 4)
print('Tabla Diferencia Dividida')
print([titulo])
print(tabla)
print('dDividida: ')
print(dDividida)
print('polinomio: ')
print(polinomio)
print('polinomio simplificado: ' )
print(polisimple)

# Gráfica
plt.plot(xj,fj,'o',color='red')
plt.plot(xi,fi,'o', label = 'Puntos')
##for i in range(0,n,1):
##    plt.axvline(xi[i],ls='--', color='yellow')

plt.plot(pxi,pfi, label = 'Polinomio')
plt.legend()
plt.grid()
plt.xlabel('xi')
plt.ylabel('fi')
plt.title('Diferencias Divididas - Newton')
plt.show()

literal c. Polinomio de Lagrange

# 1Eva_2023PAOI_T3 Recoger los restos de sumergible
# Interpolacion de Lagrange
# divisoresL solo para mostrar valores
import numpy as np
import sympy as sym
import matplotlib.pyplot as plt

# INGRESO , Datos de prueba
xj = np.array([0.2478, 0.6316, 1.3802, 2.4744, 2.7351, 2.8008,
               3.5830, 3.6627, 3.7213, 4.2796, 4.3757, 4.6794])
fj = np.array([1.8108, 0.1993, 4.7199, 2.4529, 2.3644, 3.5955,
               2.6558, 4.3657, 4.1932, 4.6998, 4.7536, 1.5673])

# selecionando puntos
#j = [0, 2, 5, 6, 11]
j = [3,4,7,8,9,10]
xi = xj[j]
fi = fj[j]

# PROCEDIMIENTO
# Polinomio de Lagrange
n = len(xi)
x = sym.Symbol('x')
polinomio = 0
divisorL = np.zeros(n, dtype = float)
for i in range(0,n,1):
    
    # Termino de Lagrange
    numerador = 1
    denominador = 1
    for j  in range(0,n,1):
        if (j!=i):
            numerador = numerador*(x-xi[j])
            denominador = denominador*(xi[i]-xi[j])
    terminoLi = numerador/denominador

    polinomio = polinomio + terminoLi*fi[i]
    divisorL[i] = denominador

# simplifica el polinomio
polisimple = polinomio.expand()

# para evaluación numérica
px = sym.lambdify(x,polisimple)

# Puntos para la gráfica
muestras = 101
a = np.min(xi)
b = np.max(xi)
pxi = np.linspace(a,b,muestras)
pfi = px(pxi)

# SALIDA
print('    valores de fi: ',fi)
print('divisores en L(i): ',divisorL)
print()
print('Polinomio de Lagrange, expresiones')
print(polinomio)
print()
print('Polinomio de Lagrange: ')
print(polisimple)

# Gráfica
plt.plot(xj,fj,'o',color='red')
plt.plot(xi,fi,'o', label = 'Puntos')
plt.plot(pxi,pfi, label = 'Polinomio')
plt.legend()
plt.grid()
plt.xlabel('xi')
plt.ylabel('fi')
plt.title('Interpolación Lagrange')
plt.show()

 

1Eva_2023PAOI_T3 Recoger los restos de sumergible

1ra Evaluación 2023-2024 PAO I. 4/Julio/2023

Tema 3 (35 puntos) Suponga que se requiere planificar la operación de recolección de restos del accidente del sumergible ocurrido en junio del 2023 usando un robot autónomo sumergible. rov sumergible

Existen limitaciones debidas a la profundidad del lecho submarino, las fuertes corrientes que fluyen desde el oeste, limitada energía en las baterías del robot, etc.

El recorrido del robot se puede programar usando un polinomio para el intervalo de las coordenadas presentadas. recorrido de polinomio

Se estima poder realizar tan solo dos recorridos desde el oeste hacia el este.

a. Plantee el ejercicio usando interpolación polinómica para realizar un recorrido para la recolección de restos, describiendo los criterios usados para la selección de los puntos.

b. Desarrolle un primer polinomio usando el método de diferencias divididas de Newton

c. Excluyendo los puntos usados en el literal anterior, desarrolle otro polinomio usando el método de Lagrange.

d. Grafique los resultados obtenidos para cada caso.

e. Determine el error de cada polinomio como la distancia entre cada punto por el que no pasa el polinomio y la coordenada equivalente en el polinomio. Realice observaciones a los resultados.

xi = np.array([0.2478, 0.6316, 1.3802, 2.4744, 2.7351, 2.8008,
               3.5830, 3.6627, 3.7213, 4.2796, 4.3757, 4.6794])
fi = np.array([1.8108, 0.1993, 4.7199, 2.4529, 2.3644, 3.5955,
               2.6558, 4.3657, 4.1932, 4.6998, 4.7536, 1.5673])

Nota: Los valores de la tabla se establecen para el ejercicio y no corresponden a una referencia publicada.

Rúbrica: literal a (5 puntos), literal b (10 puntos), literal c (10 puntos), literal d (5 puntos), literal e (5 puntos)

Referencia: [1] La Guardia Costera de EE.UU. anuncia una investigación oficial sobre la implosión del sumergible Titán. RTVE.es / EFE. 26.06.2023 https://www.rtve.es/noticias/20230626/eeuu-anuncia-investigacion-oficial-sumergible-titan/2450440.shtml

[2] Así será la recuperación de los restos del sumergible Titán, según un experto militar. CNN en Español. 23 jun 2023.

s1Eva_2023PAOI_T2 Productos en combo por subida de precio

Ejercicio: 1Eva_2023PAOI_T2 Productos en combo por subida de precio

literal a

Para el ejercicio solo es posible plantear tres ecuaciones, se muestran datos solo para tres semanas. Se tienen 4 incógnitas, por lo que tendría infinitas soluciones y no se podría resolver.

500a+600b+400c+90d=1660 500 a + 600 b + 400 c + 90 d = 1660 800a+450b+300c+100d=1825 800 a + 450 b + 300 c + 100 d = 1825 400a+300b+600c+80d=1430 400 a + 300 b + 600 c + 80 d = 1430

Sin embargo desde el punto de vista práctico se puede usar una variable libre, considerando algunos criterios como:

– que en la nota se da el precio para la docena de huevos a 2.5 USD
– que la variable para huevos es la de menor cantidad se puede incurrir en un menor error si el precio ha variado en los últimos días respecto a lo que se podría haber tenido como referencia.

500a+600b+400c=166090d 500 a + 600 b + 400 c = 1660 - 90 d 800a+450b+300c=1825100d 800 a + 450 b + 300 c = 1825 - 100 d 400a+300b+600c=143080d 400 a + 300 b + 600 c = 1430 - 80 d

que al sustituir con d = 2.5

500a+600b+400c=166090(2.5)=1435 500 a + 600 b + 400 c = 1660 - 90 (2.5) =1435 800a+450b+300c=1825100(2.5)=1575 800 a + 450 b + 300 c = 1825 - 100 (2.5) = 1575 400a+300b+600c=143080(2.5)=1230 400 a + 300 b + 600 c = 1430 - 80 (2.5) = 1230

Nota: el estudiante puede realizar una justificación diferente para el uso de otra variable libre.

literal b

La forma matricial del sistema de ecuaciones se convierte a:

(500600400143580045030015754003006001230) \begin{pmatrix} 500 & 600 & 400 & \Big| & 1435 \\ 800 & 450 & 300 & \Big| & 1575 \\ 400 & 300 & 600 &\Big| & 1230 \end{pmatrix}

literal c

pivoteando la matriz aumentada, se encuentra que para la primera columna se pivotean la fila 1 y 2, por ser 800 el valor de mayor magnitud:

(800450300157550060040014354003006001230) \begin{pmatrix} 800 & 450 & 300 & \Big| & 1575 \\ 500 & 600 & 400 & \Big| & 1435 \\ 400 & 300 & 600 &\Big| & 1230 \end{pmatrix}

luego, observando la segunda columna desde el elemento de la diagonal hacia abajo, se observa que el mayor valor en magnitud ya se encuentra en la diagonal. No se requieren intercambios de fila para la tercera columna.

literal d

Para el método iterativo de Gauss-Seidel se requiere el vector inicial, tomando las observaciones de la nota. Se da un precio de 50 USD por un quintal métrico de 100Kg, por lo que se estima que el precio por kilogramo ronda 50/100= 0.5 USD. Se indica además que los demás valores se encuentran en el mismo orden de magnitud, por lo que se tiene como vector inicial

X0 = [0.5, 0.5, 0.5]

Para un método iterativo se despejan las ecuaciones como:

a=1575450b300c800 a = \frac {1575 - 450 b - 300 c}{800} b=1435500a400c600 b = \frac {1435 -500 a - 400 c}{600} c=1230400a300b600 c = \frac {1230 - 400 a - 300 b}{600}

Para el cálculo del error se considera que se busca un precio, lo regular es considerar el centavo, es decir una tolerancia 10-2. Para el desarrollo se considera usar cuatro decimales por si se considera truncar o redondear.

itera = 0

a=1575450(0.5)300(0.5)800=1.5 a = \frac {1575 - 450 (0.5) - 300 (0.5)}{800} = 1.5 b=1435500(1.5)400(0.5)600=0.8083 b = \frac {1435 -500(1.5) - 400 (0.5)}{600} = 0.8083 c=1230400(1.5)300(0.8083)600=0.6458 c = \frac {1230 - 400(1.5) - 300 (0.8083)}{600} =0.6458

errado = max|[1.5-0.5, 0.8083-0.5, 0.6458-0.5]|
errado = max|[1, 0.3083, 0.1458]| = 1

itera = 1

X1 = [1.5, 0.8083, 0.6458]

a=1575450(0.8083)300(0.6458)800=1.2719 a = \frac {1575 - 450 (0.8083) - 300 (0.6458)}{800} = 1.2719 b=1435500(1.2719)400(0.6458)600=0.9012 b = \frac {1435 -500(1.2719) - 400 (0.6458)}{600} = 0.9012 c=1230400(1.2719)300(0.9012)600=0.7514 c = \frac {1230 - 400 (1.2719) - 300 (0.9012)}{600} = 0.7514

errado = max|[1.2719-1.5, 0.9012-0.8083, 0.7514-0.6458]|
errado = max|[-0.2281, 0.0929, 0.1056]| = 0.2281

el error disminuye entre iteraciones.

itera = 2

X2 = [1.2719 , 0.9012, 0.7514]

a=1575450(0.9012)300(0.7514)800=1.1805 a = \frac {1575 - 450 (0.9012) - 300 (0.7514)}{800} = 1.1805 b=1435500(1.1805)400(0.7514)600=0.9069 b = \frac {1435 -500 (1.1805) - 400 (0.7514)}{600} = 0.9069 c=1230400(1.1805)300(0.9069)600=0.8095 c = \frac {1230 - 400 (1.1805) - 300 (0.9069)}{600} = 0.8095

errado = max|[ 1.1805-1.2719 , 0.9069-0.9012, 0.8095-0.7514]|
errado = max|[-0.0914, 0.0057, 0.1056]| = 0.1056

el error disminuye entre iteraciones.

literal e

Si el error entre iteraciones disminuye, se considera que el método converge.

usando el algoritmo Método de Gauss-Seidel con Python, se tiene como resultado en 13 iteraciones:

[1.5        0.80833333 0.64583333] 1.0
[1.271875   0.90121528 0.75147569] 0.2281249999999999
[1.18001302 0.90733869 0.80965531] 0.09186197916666661
[1.15475125 0.88960375 0.83536396] 0.025708648304880177
[1.1550864  0.87218536 0.84384972] 0.01741839593330019
[1.16170209 0.86101511 0.84502438] 0.011170246538965367
[1.16754486 0.85536303 0.84395525] 0.005842764350612484
[1.17112508 0.85309227 0.84270381] 0.0035802211655899807
[1.17287167 0.85247107 0.84185002] 0.0017465903731879173
[1.17354127 0.85248226 0.84139802] 0.0006695985845832642
[1.17370447 0.85264759 0.84120656] 0.00019146597344232852
[1.17368327 0.8527929  0.84114804] 0.00014530950399282982
[1.17362348 0.85288174 0.84114348] 8.884049018109685e-05
respuesta X: 
[[1.17362348]
 [0.85288174]
 [0.84114348]]
verificar A.X=B: 
[[1575.03861029]
 [1434.99817609]
 [1230.        ]]
iteraciones 13
>>> 

instrucciones en Python

Se debe recordar que las matrices y vectores deben ser tipo real (float) .

# Método de Gauss-Seidel
# solución de sistemas de ecuaciones
# por métodos iterativos

import numpy as np

# INGRESO
A = np.array([[800, 450, 300],
              [500, 600, 400],
              [400, 300, 600]], dtype=float)

B = np.array([1575, 1435,1230], dtype=float)

X0  = np.array([0.5,0.5,0.5])

tolera = 0.0001
iteramax = 100

# PROCEDIMIENTO

# Gauss-Seidel
tamano = np.shape(A)
n = tamano[0]
m = tamano[1]
#  valores iniciales
X = np.copy(X0)
diferencia = np.ones(n, dtype=float)
errado = 2*tolera

itera = 0
while not(errado<=tolera or itera>iteramax):
    # por fila
    for i in range(0,n,1):
        # por columna
        suma = 0 
        for j in range(0,m,1):
            # excepto diagonal de A
            if (i!=j): 
                suma = suma-A[i,j]*X[j]
        
        nuevo = (B[i]+suma)/A[i,i]
        diferencia[i] = np.abs(nuevo-X[i])
        X[i] = nuevo
    errado = np.max(diferencia)
    print(X,errado)
    itera = itera + 1

# Respuesta X en columna
X = np.transpose([X])

# revisa si NO converge
if (itera>iteramax):
    X=0
# revisa respuesta
verifica = np.dot(A,X)

# SALIDA
print('respuesta X: ')
print(X)
print('verificar A.X=B: ')
print(verifica)
print('iteraciones',itera)

1Eva_2023PAOI_T2 Productos en combo por subida de precio

1ra Evaluación 2023-2024 PAO I. 4/Julio/2023

Tema 2 (35 puntos) Un restaurante usa materia prima como: arroz, cebolla, papa, huevos, leche, etc.mercado alimentos

Sin embargo los precios han presentado variaciones en el mercado presentando como causas: las lluvias fuertes, el fenómeno del niño, daños en las cosechas y variaciones en insumos por la guerra Ucrania.

Un proveedor mayorista ofrece “combo” de productos por un valor total registrado en varias semanas como:

arroz [Kg] cebolla [Kg] papa [Kg] huevo [docena] Total Pagado [USD]
Semana 1 500 600 400 90 1660
Semana 2 800 450 300 100 1825
Semana 3 400 300 600 80 1430

Nota: Los valores de la tabla se establecen para el ejercicio y no corresponden a una referencia publicada.

Se requiere conocer el precio unitario de los insumos para estimar el presupuesto operativo semanal del restaurante.

a. Plantee el ejercicio usando los datos de la tabla. Considere justificar el uso de una variable libre.

b. Establezca la forma matricial del sistema de ecuaciones y como matriz aumentada

c. De ser necesario realice el pivoteo parcial por filas

d. Use el método iterativo de Gauss-Seidel, realizando al menos 3 iteraciones. Estime la tolerancia, y justifique.

e. Comente sobre la convergencia del método y justifique sus observaciones usando los errores entre iteraciones.

Nota: considere el precio de 50 USD para el quintal métrico (100Kg) de arroz, y precios menores del mismo orden de magnitud para los demás productos. La docena de huevo a 2,5 USD.

 Rúbrica: literal a (2 puntos), literal b (5 puntos), literal c (3puntos), literal d (15 puntos), literal e (5 puntos)

Referencia: [1] Papa, arroz, huevos, cebolla y gas escasean en las islas Galápagos por problemas de abastecimiento. Eluniverso.com. 9 Junio 2023. https://www.eluniverso.com/noticias/ecuador/escasez-de-productos-en-islas-galapagos-persiste-nota/

[2] El Gobierno importará arroz para controlar que el precio no suba por especulación o escasez. Ecuavisa. 16 junio 2023. https://www.ecuavisa.com/noticias/ecuador/el-gobierno-importara-arroz-para-controlar-que-el-precio-no-suba-por-especulacion-o-escasez-CK5396253

[3] Estos son los alimentos de la canasta básica que se han encarecido por las lluvias. Ecuavisa. 05 junio 2023. https://www.ecuavisa.com/la-noticia-a-fondo/estos-son-los-alimentos-de-la-canasta-basica-que-se-han-encarecido-por-las-lluvias-FX5303236

[4] Cebolla, arroz, piña y tomate son los productos que han subido su precio en mercados de Quito. La Hora. Junio 23, 2023. https://www.lahora.com.ec/pais/cebolla-arroz-pina-y-tomate-son-los-productos-que-han-subido-su-precio-en-mercados-de-quito/

[5] Escasez de arroz ya se refleja en los mercados de Ecuador | Televistazo | Ecuavisa. 14 jun 2023