Practica 7

Descargar como pdf o txt
Descargar como pdf o txt
Está en la página 1de 32

Universidad de Oviedo

Grado en Fı́sica

Introducción a la Fı́sica Computacional

Julio Manuel Fernández Dı́az, Rosario Dı́az Crespo

Curso 2017-18

Manejo de vectores y matrices con numpy


Práctica 7

Manejo de vectores y matrices con


numpy

Este módulo proporciona una manera conveniente y eficiente de manejar vectores numéricos (los
denominaremos arrays a partir de ahora) en Python. Los arrays son homogéneos (a diferencia de
las listas de Python que pueden ser heterogéneas), los elementos de un array pueden ser de tipo
booleano, entero, real o complejo (pero sin mezclarse). Un array puede tener varias dimensiones,
a las que se accede por ı́ndices (ordenados). Además numpy dispone de una serie de rutinas que
permiten operaciones rápidas sobre arrays. Con el fin de entender la potencia y la utilidad del uso
de numpy plantearemos el siguiente ejemplo:
Ejemplo 7.0.1. En el tratamiento de series de datos (temperatura, campo magnético, etc.) se utiliza
a veces la media móvil, es decir, sustituir un elemento de la serie por su media con puntos anteriores
y posteriores.

Por ejemplo, si x es un vector cuyos ı́ndices varı́an de 0 a n − 1, tendrı́amos:


y0 = x0 , yn−1 = xn−1 ,
1
yi = (xi−1 + xi + xi+1 ), i = 1, . . . , n − 2
3

Temperaturas en Mieres (ficticias)


20

15

10
t ºC

00 200 400 600 800 1000


da

1
Universidad de Oviedo Departamento de Fı́sica

Si los datos de temperatura los tenemos en el fichero np01.txt cuya estructura es de la forma:

# Temperaturas en Mieres (ficticas)


# Dı́a o C
0 5.5839
1 5.76949
2 4.65202
···

Una posible solución usando listas

# -*- coding: utf-8 -*-


def mediamovil (x):
’’’ calcula una media móvil de 3 puntos ’’’
n = len(x)
y = []
for i, x1 in enumerate(x):
if i == 0 or i == n-1:
y.append(x1)
else:
y.append((x[i-1]+x1+x[i+1])/(3.0))
return y
# lectura de datos
lines = open("np01.txt").readlines()
x = []
for line in lines:
if line[0] == "#": continue
l = line.split()
x.append(float(l[1]))

y = mediamovil(x)
# hacemos cuatro medias móviles más
for i in range(4): y = mediamovil(y)

Una posible solución usando arrays de numpy

# -*- coding: utf-8 -*-


from numpy import *
def mediamovil (x):
’’’ calcula una media móvil de 3 puntos ’’’
y = copy(x)
y[1:-1] = (x[0:-2]+x[1:-1]+x[2:])/(3.0)
return y

nx = loadtxt("np01.txt")
n, x = nx[:,0], nx[:,1]

y = mediamovil(x)
# hacemos cuatro medias móviles más
for i in range(4): y = mediamovil(y)

Introducción a la Fı́sica Computacional 2


Universidad de Oviedo Departamento de Fı́sica

7.1. Ventajas de usar numpy

En los lenguajes interpretados (como Python) los bucles son lentos. Para realizar cálculo numérico
es mejor que los bucles estén ‘escondidos’ en código compilado (que es mucho más rápido). Ası́
pues, cualquier mecanismo que nos evite los bucles será muy bienvenido.

La eliminación de bucles no es especialmente necesaria en lenguajes compilados como C o Fortran.


No obstante, con la llegada de ordenadores con varios procesadores muchas operaciones con arrays se
pueden efectuar en paralelo en varios de ellos: idealmente cada procesador se encargarı́a de obtener
la solución para un elemento del array.

Numpy nos permite eliminar bucles y eso también nos ahorra lı́neas de código (por tanto es más fácil
de leer) y el código se parece más a la notación matemática. Además, si las rutinas de numpy en
lenguaje compilado están desarrolladas para cálculo en paralelo también nos aprovechamos de los
múltiples procesadores que hubiera en el ordenador.

También, y no menos importante, nos ‘obliga’ a pensar en paralelo: las operaciones son las mismas
sobre diferentes elementos de los vectores. Los programas codificados con lenguajes ‘paralelos’ son
normalmente más cortos, más claros y más cercanos a las ideas cientı́ficas que queremos programar.

Adaptar el código (cuando es posible) para que funcione en paralelo se denomina vectorializar o
vectorizar. En general consiste en eliminar bucles con contadores. Más adelante, cuando conozcamos
la sintaxis de numpy y sus posibilidades, veremos ejemplos de cómo vectorializar código.

7.2. La estructura básica de datos

En numpy el array es la estructura básica. Consiste realmente en un conjunto homogéneo de


datos: int, float (por defecto), etc., que se disponen de manera adyacente en memoria en forma
unidimensional. Si se requiere que el array tenga dos o más dimensiones, numpy nos proporciona (de
manera transparente) la posibilidad de acceder a los elementos por sus ı́ndices.

También existe la posibilidad de usar la estructura matrix, aunque creemos más recomendable usar
array en su lugar (sobre todo en los comienzos del aprendizaje de numpy) pues su uso es más
homogéneo. Existen muy pocas diferencias entre ambas, la principal es la definición del producto de
dos matrices (pero veremos que es muy sencillo con array).

7.3. Usando numpy

Para utilizar numpy lo primero que se debe hacer es cargar el módulo de la siguiente manera (por
ejemplo):
from numpy import *
aunque también se puede usar el módulo con nombre:
import numpy as np
pero entonces en la invocación de las funciones tenemos que añadir delante np.

Introducción a la Fı́sica Computacional 3


Universidad de Oviedo Departamento de Fı́sica

La mayorı́a de las funciones asociadas a numpy que vamos a utilizar a lo largo de este curso poseen
más argumentos opcionales que los indicados. Para más información debe consultarse:
http://docs.scipy.org/doc/numpy/reference/

El manejo de arrays conlleva diversos mecanismos:

Creación de arrays.

Obtención de información sobre los arrays.

Acceso a elementos y secciones de arrays.

Operaciones con arrays.

Álgebra lineal y arrays aleatorios.

7.4. Creación de arrays

Existen tres maneras de crear arrays en numpy:

a partir de una lista o tupla (homogénea),

usando una función que retorna un array con valores,

leyendo datos desde un fichero.

7.4.1. Creación de arrays a partir de una lista

La función array nos permite crear un array a partir de una lista.

Ejemplo 7.4.1. Uso de la función array.

En el modo interactivo de python escriba el siguiente código:

>>> a = array([3, 4, 2]) # 1D


>>> b = array([[3, 2, 4], [3, 3, 2]]) # 2D
>>> print(a)
[3 4 2]
>>> print(b)
[[3 2 4]
[3 3 2]]

En ocasiones en un programa no tenemos claro si un objeto es un array o no y queremos convertirlo


a array. En ese caso es útil la función asarray.

Introducción a la Fı́sica Computacional 4


Universidad de Oviedo Departamento de Fı́sica

Ejemplo 7.4.2. Uso de la función asarray.

En el modo interactivo de python escriba el siguiente código:

>>> l = [3, 4, 2]
>>> a = array(l)
>>> print(a)
[3 4 2]
>>> b = asarray(a)
>>> print(b)
[3 4 2]

La operación inversa (convertir arrays en listas) se realiza con la función tolist (OJO tolist(a)
no funciona).

Ejemplo 7.4.3. Uso de la función tolist.

En el modo interactivo de python escriba el siguiente código:

>>> a = array([[3., 2., 4.], [3., 3., 2.]])


>>> a.tolist()
[[3.0, 2.0, 4.0], [3.0, 3.0, 2.0]]
>>> b = array([2., 0., -1.])
>>> b.tolist()
[2.0, 0.0, -1.0]

7.4.2. Creación de arrays usando una función que retorna un array con valores

Algunas de las funciones que crean arrays son:

arange, linspace, ones, zeros, eye, copy, concatenate, repeat, roll, logspace, mgrid y
ogrid.

La función arange

Es similar a range, pero en vez de crear una lista crea un array y además se puede usar un espaciado
de tipo float.

>>> a = arange(5)
>>> print(a)
[0 1 2 3 4]
>>> c = arange(1, 2, 0.2)
>>> print(c)
[ 1. 1.2 1.4 1.6 1.8]

Introducción a la Fı́sica Computacional 5


Universidad de Oviedo Departamento de Fı́sica

La función linspace

Esta función produce un array con un número determinado de elementos linealmente espaciados
entre dos lı́mites:
>>> b = linspace(1, 2, 6)
>>> print(b)
[1. 1.2 1.4 1.6 1.8 2.]

A diferencia de arange, se incluyen los extremos.

La función ones

Crea un array con todo unos con el número de elementos indicados:


>>> a = ones(3)
>>> print(a)
[1.0 1.0 1.0]

Si se pasa como argumento una tupla usa ese número de elementos en cada dimensión:
>>> b = ones((3, 2)) # 3 filas, 2 columnas
>>> print(b)
[[1.0 1.0]
[1.0 1.0]
[1.0 1.0]]

La función zeros

Crea un array con todo ceros con el número de elementos indicados:


>>> b = zeros((3, 2)) # 3 filas, 2 columnas
>>> print(b)
[[0.0 0.0]
[0.0 0.0]
[0.0 0.0]]

Para más de una dimensión el primer argumento de ones y zeros debe ser una tupla (de ahı́ los
dobles paréntesis).

La función zeros like

Crea un array con todo ceros con la misma forma que otro array:
>>> a = array([[1, 2, 3, 4], [4, 5, 6, 7]])
>>> b = zeros like(a) # 2 filas, 4 columnas
>>> print(b)
[[0 0 0 0]
[0 0 0 0]]

También existe la función ones like que crea un array con todos unos.

Introducción a la Fı́sica Computacional 6


Universidad de Oviedo Departamento de Fı́sica

La función eye

Crea un array con la diagonal principal llena de unos y el resto de los elementos ceros:
>>> b = eye(4, 3) # 4 filas, 3 columnas
>>> print(b)
[[1.0 0.0 0.0]
[0.0 1.0 0.0]
[0.0 0.0 1.0]
[0.0 0.0 0.0]]

Como vemos, no hace falta que el array sea cuadrado.

También se puede indicar que los unos estén por encima o debajo de la diagonal principal usando
un parámetro k (0 por defecto: diagonal principal) y puede tomar valores positivos (por encima de
la diagonal principal) y negativos (por debajo):
>>> b = eye(5, 4, k = -1)
>>> print(b)
[[ 0. 0. 0. 0.]
[ 1. 0. 0. 0.]
[ 0. 1. 0. 0.]
[ 0. 0. 1. 0.]
[ 0. 0. 0. 1.]]

La función copy

Cuando se asigna una variable a un array preexistente no se copia el array sino que se crea otro
nombre para el array:
>>> x = array([1, 2, 3])
>>> y = x # y es una ’vista’ de x
>>> x[0] = 10 # luego veremos esta orden
>>> print(y)
[10 2 3]

Realmente se puede considerar a y (también a x) como una ventana que mira en el verdadero array
(un objeto en memoria con caracterı́sticas de array).

La función copy realiza una copia a partir de otro array. En su versión más simple (y usual) serı́a:
>>> x = array([1, 2, 3])
>>> y = copy(x)
>>> x[0] = 10
>>> print(y)
[1 2 3]
El nuevo array al que referencia y no es el mismo que el antiguo x.

Introducción a la Fı́sica Computacional 7


Universidad de Oviedo Departamento de Fı́sica

La función concatenate

La función concatenate((a1, a2, ...), axis=0) concatena arrays según una de las
dimensiones. La forma de los arrays ha de ser la misma excepto según la dimensión correspondiente
a axis (por defecto 0):
>>> a = array([[1, 2], [3, 4]])
>>> b = array([[5, 6]])
>>> concatenate((a, b))
>>> # (OJO con los paréntesis)
array([[1, 2],
[3, 4],
[5, 6]])

Los arrays 1D concatenan generando un array 1D:


>>> x = array([0, 1, 2])
>>> y = array([-1, 0.2, 0.9])
>>> concatenate((x, y))
array([ 0. , 1. , 2. , -1. , 0.2, 0.9])

Si queremos concatenarlos generando varias filas se debe usar el siguiente ‘truco’:


>>> concatenate(([x], [y])) # fı́jemonos en los corchetes rojos
array([[ 0. , 1. , 2. ],
[-1. , 0.2, 0.9]])

La función repeat

La función repeat(a, copias, axis) sirve para extender arrays según una dimensión. En su modo
más simple de funcionamiento conviene indicar la dimensión (‘axis’) en la que queremos repetir el
array:
>>> a = array([[1, 2], [3, 4]])
>>> repeat(a, 2, axis=0)
array([[1, 2],
[1, 2],
[3, 4],
[3, 4]])
>>> repeat(a, 2, axis=1)
array([[1, 1, 2, 2],
[3, 3, 4, 4]])

Si no indicamos el eje:
>>> repeat(a, 2)
array([1, 1, 2, 2, 3, 3, 4, 4])

Introducción a la Fı́sica Computacional 8


Universidad de Oviedo Departamento de Fı́sica

La función roll

La función roll(a, desplazamiento, axis) desplaza de manera circular los elementos a lo largo
de una dimensión. En su modo más simple de funcionamiento conviene indicar la dimensión (‘axis’):
>>> a = array([[1, 2, 3, 4], [5, 6, 7, 8],[9,10,11,12]])
>>> roll(a, 1, axis=0)
array([[9, 10, 11, 12],
[1, 2, 3, 4],
[5, 6, 7, 8]])
>>> roll(a, 1, axis=1)
array([[4, 1, 2, 3],
[8, 5, 6, 7],
[12, 9, 10, 11]])
>>> roll(a, -1, axis=1)
array([[ 2, 3, 4, 1],
[6, 7, 8, 5 ],
[10, 11, 12, 9]])

Como vemos la rotación a lo largo de un eje puede ser hacia atrás (desplazamiento negativo).

Si no se indica axis (o se pone axis=None) entonces el array se convierte a 1D antes de hacer la


rotación y luego se restaura la forma original:
>>> a = array([[0, 1, 2, 3], [5, 6, 7, 8]])
>>> roll(a, 1)
array([[8, 0, 1, 2],
[3, 5, 6, 7]])

La función logspace

Produce un array con un número determinado de elementos logarı́tmicamente espaciados entre dos
lı́mites:
>>> b = logspace(1, 2, 6)
>>> print(b)
[ 10. , 15.8489319, 25.1188643, 39.8107171,
63.0957345, 100. ]

Se incluyen 6 puntos desde 101 hasta 102 . Como en el caso de linspace, se incluyen los extremos.

La función ogrid

Esta función se usa para generar arrays usados en mallados. Esta función nos devuelve una lista con
dos arrays, el primero con las coordenadas x y el segundo con las y:
>>> p = ogrid[0:2:0.5, 0:2] # no se usa con paréntesis
>>> print(p)

Introducción a la Fı́sica Computacional 9


Universidad de Oviedo Departamento de Fı́sica

[array([[ 0. ],
[ 0.5],
[ 1. ],
[ 1.5]]), array([[ 0., 1.]])]

La función mgrid

Se usa para generar arrays usados en mallados. Su sintaxis es la siguiente:


m = mgrid[rango x, rango y], siendo cada rango:

rango ≡ inicio incluyente : fin excluyente : paso

Nos devuelve un array 3D, siendo m[0,:,:] las coordenadas x de los puntos y m[1,:,:] las y.

OJO: mgrid no se usa con paréntesis.

>>> m = mgrid[0:2:0.5, 0:2]


>>> print(m)
[[[ 0. 0. ]
[ 0.5 0.5]
[ 1. 1. ]
[ 1.5 1.5]]

[[ 0. 1. ]
[ 0. 1. ]
[ 0. 1. ]
[ 0. 1. ]]]

7.4.3. Leyendo datos de un fichero

Existen varias funciones para leer datos (preparados previamente de cierta manera) a partir de
ficheros. La función básica es loadtxt. Su sintaxis es la siguiente:

out = loadtxt(fname, dtype=’float’, comments=’#’,


delimiter=None, skiprows=0, usecols=None)

Este ejemplo lee del fichero de texto cuyo nombre está guardado en la variable fname y los incluye
en el array out. Sólo se exponen algunos argumentos que tienen puestos los valores por defecto:

dtype indica el tipo de dato (entero, real, etc.), por defecto float.

comments indica un string por el que empiezan las lı́neas con comentarios (que se saltan entonces).
Por defecto es #.

delimiter indica los caracteres que se usan para separar datos, por defecto espacios.

skiprows indica cuántas filas iniciales se saltan antes de empezar a leer realmente el array (por
defecto 0). Las lı́neas de comentarios se cuentan también.

Introducción a la Fı́sica Computacional 10


Universidad de Oviedo Departamento de Fı́sica

usecols indica una secuencia de columnas para leer (por defecto todas), por ejemplo
usecols=(1,3,5) lee sólo esas columnas.

Ejemplo 7.4.4. Uso de la función loadtxt. Escriba el siguiente código en un fichero con el nombre
z.txt:

# z.txt ------
1 2 3
# otro comentario
4 5 6
7 8 9
#-------------

Una vez guardado el archivo escriba en el modo interactivo de python:

>>> out=loadtxt(’z.txt’, skiprows=2, usecols=(1,2), dtype=’int’)


>>> print(out)
[[5 6]
[8 9]]
>>> b = loadtxt(’z.txt’)
>>> print(b)
[[ 1. 2. 3.]
[ 4. 5. 6.]
[ 7. 8. 9.]]

7.5. Obteniendo información sobre arrays

Los arrays tienen asociados un número de dimensiones y un tamaño a lo largo de cada dimension
(‘forma’). Se puede acceder a esa información de diversas maneras, usando ndim, size, shape, len
y reshape.

7.5.1. Arrays con ‘vistas’

Los datos del array (o sea los valores de sus elementos) son los mismos independientemente de la
forma asociada (puesto que los arrays se guardan de manera secuencial en memoria).

Al cambiar la forma de un array se modifica la ‘vista’ (o sea, cómo se ve el array).

Situación real de a en memoria: 1 2 3 4 5 6

1 2 3
Vista con forma (2,3):
4 5 6

1 2
Vista con forma (3,2): 3 4
5 6

Introducción a la Fı́sica Computacional 11


Universidad de Oviedo Departamento de Fı́sica

Todas estas vistas son el mismo array.

7.5.2. Dimensiones de un array

Para determinar el número de dimensiones se utiliza ndim:


>>> a = array([[3, 2, 4], [3, 3, 2]])
>>> ndim(a)
2
>>> a.ndim
2
Como vemos a.ndim es un dato entero no una función.

7.5.3. Tamaño de un array

Para determinar el número total de elementos (tamaño) se utiliza size:


>>> a = array([[3, 2, 4], [3, 3, 2]])
>>> size(a)
6
>>> a.size
6
Como vemos a.size es un dato entero no una función.

7.5.4. Forma de un array

Para determinar la forma, o sea el número de elementos en cada dimensión se utiliza shape:
>>> a = array([[3, 2, 4], [3, 3, 2]])
>>> shape(a)
(2, 3)
>>> a.shape
(2, 3)
Como vemos a.shape es una tupla de enteros no una función.

7.5.5. La función len

Esta función aplicada a un array nos da el tamaño correspondiente a la primera dimensión:


>>> a = array([[3, 2, 4], [3, 3, 2]])
>>> a.shape[0]
2
>>> len(a)
2
igual que se obtendrı́a aplicada a listas de listas.

Introducción a la Fı́sica Computacional 12


Universidad de Oviedo Departamento de Fı́sica

7.5.6. Cambiando la forma

Se puede cambiar la forma de un array con la función reshape o asignando una nueva tupla a la
variable a.shape:
>>> a = array([[3, 2, 4], [3, 3, 2]])
>>> a.shape
(2, 3)
>>> a.shape = (a.size, )
>>> print(a)
[3 2 4 3 3 2]
Hemos cambiado las dimensiones del array, aunque no su tamaño (o sea 6 en este caso): a.size
no puede cambiar.

En el ejemplo anterior, a partir de este momento el array tiene una sola dimensión y se accede (como
veremos posteriormente) con un solo ı́ndice:
>>> a[0] = 100
>>> a.shape = (2, 3)
>>> print(a)
[[100 2 4]
[ 3 3 2]]
Vemos que las modificaciones realizadas en a se transmiten aunque se cambie de nuevo la forma.

También podemos cambiar la forma recurriendo a la función reshape, en este caso el procedimiento
será el siguiente:
>>> a = array([[1, 2, 3], [4, 5, 6]])
array([[1, 2],
[1, 2, 3],
[4, 5, 6]])
>>> a=a.reshape(3, 2)
>>> print(a)
array([[1, 2],
[3, 4],
[5, 6]])

7.5.7. La traspuesta de un array

Si a es un array, transpose(a), a.transpose() y a.T (que hacen lo mismo), nos devuelven una
vista traspuesta del array a:
>>> b = array([[1, 2, 3], [4, 5, 6]])
>>> print(b.T)
[[1 4]
[2 5]
[3 6]]

transpose por defecto invierte todas las dimensiones.

Se puede indicar el orden exacto de las dimensiones mediante una lista opcional de ejes, para

Introducción a la Fı́sica Computacional 13


Universidad de Oviedo Departamento de Fı́sica

comprobarlo escriba el siguiente código:


>>> a=arange(1,25)

Hemos creado un array a 1D, al que cambiaremos a 3D de la siguiente manera:


>>> a.shape=(2,3,4)

Compruebe la nueva forma de a imprimiéndolo en pantalla.

Realice la siguientes operaciones y compare las respuestas:


>>> transpose(a)
>>> transpose(a, axes=(1,2,0))
>>> transpose(a, axes=(2,1,0))
>>> transpose(a, axes=(2,0,1))

7.5.8. Arrays ‘planos’

La función ravel nos devuelve una vista de una única dimensión, mientras que flatten nos devuelve
una copia:
>>> b = array([[3, 2, 4], [3, 3, 2]])
>>> print(b)
[[3 2 4]
[3 3 2]]
>>> c = ravel(b)
>>> print(c)
[3 2 4 3 3 2]
>>> d =b. flatten()

Compruebe que si modificamos c se modifica b y que si modificamos d no se modifica b.

7.6. Acceso a elementos y secciones de un array

Para acceder a partes de un array existen diversos mecanismos:

acceso a un elemento concreto,

vistas de secciones (slices en inglés),

copia de secciones.

7.6.1. Acceso simple a los elementos de un array

Para acceder a los elementos de un array se usan ı́ndices enteros, en lectura o escritura (como a
continuación):
>>> b = array([[3, 2, 4], [3, 3, 2]])
>>> b[1][1] = 10 # una manera

Introducción a la Fı́sica Computacional 14


Universidad de Oviedo Departamento de Fı́sica

>>> b[0, 0] = 11 # otra manera más eficiente


>>> print(b)
[[11 2 4]
[3 10 2]]

7.6.2. Secciones de un array

La potencia de los arrays se manifiesta en el manejo de secciones (slices en inglés) de los mismos.

Las secciones siempre proporcionan vistas, no hacen copia de los arrays.

Una sección es un bloque (formado por elementos a veces no contiguos) dentro de un array. El
bloque se define a través de patrones que verifican los ı́ndices (tantos como dimensiones).

Supongamos un array, a, bidimensional por simplificar:

00 01 02 03 04
10 11 12 13 14 El primer ı́ndice indica la fila y el segundo la
20 21 22 23 24 columna.
30 31 32 33 34

Patrón: inicio:final:incremento.
El inicio es inclusivo y el final excluyente.
Ejemplo 7.6.1. Acceso a secciones de un array.

Introdúzca el array a en una consola de Python.

Se quiere extraer las columnas impares. Para ello generamos un patrón de fila : (que indica todo el
rango) y uno de columna 0::2.

Se genera una vista de los elementos que están indicados en rojo y negrita:

00 01 02 03 04
10 11 12 13 14
20 21 22 23 24
30 31 32 33 34

Para extraerlo tendrı́amos el siguiente código:


a[:, 0::2] (y también a[:, ::2])
que genera un array bidimensional:

00 02 04
10 12 14
20 22 24
30 32 34

Cualquier modificación en una sección del array a se manifiesta en cambios en el propio array a:
>>> a[:, 0::2] = 77 # Nota

Introducción a la Fı́sica Computacional 15


Universidad de Oviedo Departamento de Fı́sica

>>> print(a)
[[77 1 77 3 77]
[77 11 77 13 77]
[77 21 77 23 77]
[77 31 77 33 77]]

Nota: Esa asignación se denomina broadcasting, y es la conversión de un dato en el necesario en


una operación (asignación a un array en nuestro caso).

7.6.3. Arrays conformables

Este array a[:, 0::2] concreto es conformable con cualquier array con forma (4, 3).

Eso significa que puede sustituirse donde se necesite un array de esa forma. Por ejemplo se puede
sumar a un array de forma (4, 3).

Ejercicio 7.6.1. Introduzca en la terminal de Python un array de unos con forma (4, 3) y realice
la suma entre el array de unos y el array a[:, 0::2].

También se puede multiplicar matricialmente (luego lo veremos) con un array de forma (3, ...).

Ejercicio 7.6.2. Acceso a secciones del array a.

1. ¿Qué devuelve a[:,::-1]?

2. Obtenga las filas impares de a.

3. Obtenga la vista cuadrada eliminando la última columna.

4. Obtenga una vista con las dos primeras lı́neas.

5. Obtenga una vista con los elementos comunes de las lı́neas impares y las columnas impares.

7.6.4. Copiando secciones

Para generar una copia y no una vista se debe utilizar la función copy acompañada de la selección
de sección correspondiente:
>>> b = copy(a[:, ::2])
>>> print(b)
[[ 0 2 4]
[10 12 14]]
>>> b[0,0] = 1000 # no modifica a[0,0]

Introducción a la Fı́sica Computacional 16


Universidad de Oviedo Departamento de Fı́sica

7.6.5. Sub-arrays mediante listas

Se pueden utilizar también listas como patrones de selección de secciones de arrays. En este caso se
obtiene una copia y no una vista:
>>> c = a[:2:, ::2] # sección
>>> b = a[:2:, [0,2,4]] # copia
# también b = a[:2:, range(0,6,2)]
>>> print(b)
[[ 0 2 4]
[10 12 14]]
>>> b[0,0] = 1000
# no modifica a[0,0]
>>> c[0,0] = 2222
# sı́ modifica a[0,0]

7.7. Operaciones con arrays

Existen operaciones, algunas elemento a elemento (en inglés elementwise), y otras resultado de
llamadas a funciones que retornan otros arrays o escalares.

Las operaciones básicas son: suma (+), resta (-), multiplicación (*), división (/), potenciación (**) y
las comparaciones. También se aplican elemento a elemento las denominadas funciones universales.

Para poder operar entre sı́ dos arrays deben tener la misma forma. Realmente es algo más complicado,
pero no lo describiremos aquı́. En el argot del cálculo en paralelo se dice que los arrays deben ser
conformables.

Las funciones que devuelven valores son: ‘y‘ lógico (all), ‘o’ lógico (any), máximo (max), mı́nimo
(min), suma (sum), producto (prod), media (mean), desviación estándar o tı́pica (std), producto
matricial (dot), producto vectorial (cross), producto externo (outer).

7.7.1. Suma de arrays

Para poder sumar dos arrays deben tener la misma forma:


>>> a = array([[3, 2, 4], [3, 3, 2]])
>>> b = array([[-3, 2, -4], [0, 1, 2]])
>>> c = a+b
>>> print(c)
[[0 4 0]
[3 4 4]]
>>> print(a+array([1, 2, 3, 4])) # ERROR
La suma de arrays genera un nuevo array.

También se puede utilizar el operador += que no crea un nuevo array sino que modifica el que está
a la izquierda:

Introducción a la Fı́sica Computacional 17


Universidad de Oviedo Departamento de Fı́sica

>>> a = array([[3, 2, 4], [3, 3, 2]])


>>> b = array([[-3, 2, -4], [0, 1, 2]])
>>> a += b
>>> print(a)
[[0 4 0]
[3 4 4]]

7.7.2. Resta de arrays

Para poder restar dos arrays deben tener la misma forma:


>>> a = array([[3, 2, 4], [3, 3, 2]])
>>> b = array([[-3, 2, -4], [0, 1, 2]])
>>> c = a-b
>>> print(c)
[[6 0 8]
[3 2 0]]

También se puede usar -= de manera similar a +=.

7.7.3. Multiplicación de arrays

Para poder multiplicar (elemento a elemento) dos arrays deben tener la misma forma:
>>> a = array([[3, 2, 4], [3, 3, 2]])
>>> b = array([[-3, 2, -4], [0, 1, 2]])
>>> c = a*b
>>> print(c)
[[ -9 4 -16]
[ 0 3 4]]

También se puede usar *= de manera similar a +=.

7.7.4. División de arrays

Para poder dividir (elemento a elemento) dos arrays deben tener la misma forma:
>>> a = array([[3., 2., 4.], [3., 3., 2.]])
>>> b = array([[-3, 2, -4], [1, 2, 1]])
>>> c = a/b
>>> print(c)
[[-1. 1. -1. ]
[ 3. 1.5 2. ]]
Cuidado con la división entre enteros.

También se puede usar /= de manera similar a +=.

Introducción a la Fı́sica Computacional 18


Universidad de Oviedo Departamento de Fı́sica

7.7.5. Potenciación de arrays

La potencia también se aplica elemento a elemento y los dos arrays deben tener la misma forma:
>>> a = array([[3., 2., 4.], [3., 3., 2.]])
>>> b = array([[3, 2, 4], [1, 2, 1]])
>>> c = a**b # también c = pow(a, b)
>>> print(c)
[[ 27. 4. 256.]
[ 3. 9. 2.]])
También se puede usar **= de manera similar a +=.
Ejercicio 7.7.1. Demuestre que lı́mn→∞ (1 + n1 )n = e. Para hacer esto cree un vector n que tenga
los elementos: 1, 10, 100, 500, 1000, 2000, 4000 y 8000. Seguidamente cree un vector y en el cual
cada elemento será calculado a partir de los elementos de n mediante (1 + n1 )n .
Ejercicio 7.7.2. La profundidad de un pozo, h, en metros se puede determinar a partir del tiempo
que tarda en caer una piedra en su interior (con velocidad inicial 0). Este cálculo viene determinado
por: h = 12 gt2 , donde t es el tiempo en segundos y g = 9.81 m/s2 . Calcule h para t = 1, 2, 3, 4, 5,
6, 7, 8, 9 y 10 s.

7.7.6. Broadcasting

Este término describe cómo trata numpy arrays de diferentes formas en las operaciones aritméticas.

La aplicación en casos generales de las reglas de broadcasting es relativamente compleja para el nivel
de estos apuntes. Para más información, véase la documentación de numpy y también el artı́culo
expuesto en http://www.scipy.org/EricsBroadcastingDoc.

Hemos separado las reglas para mejor entendimiento:

1. Un escalar siempre se puede operar con cualquier array.

2. Dos dimensiones son compatibles en las operaciones aritméticas cuando son iguales o cuando
una de ellas es 1. Los tamaños de las dimensiones que son 1 se extienden hasta completar el
tamaño del otro array.

3. Cuando el número de dimensiones de los arrays es diferente, se compara el tamaño de


las dimensiones empezando por la derecha, y entonces los tamaños deben ser iguales,
extendiéndose el array más corto hasta completar el tamaño del más largo.

4. Las reglas anteriores pueden combinarse.

Ası́ pues, siempre que se pueda realizar broadcasting según las reglas anteriores, las dimensiones
menos completas de cada array se ‘extienden’ para que los arrays al operar parezcan tener la misma
forma. Todos los cálculos se hacen sin hacer realmente copias. Por tanto, el broadcasting es una
operación computacionalmente muy eficiente.

Veamos ejemplos de cada regla por separado.

Introducción a la Fı́sica Computacional 19


Universidad de Oviedo Departamento de Fı́sica

Empecemos por el caso 1, con operaciones entre escalares y arrays.

Ejemplo 7.7.1. En el modo interactivo escriba el siguiente código:

>>> a = array([[3., 2., 4.], [3., 3., 2.]])


>>> print(a+2)
[[ 5. 4. 6.]
[ 5. 5. 4.]]
>>> print(2**a)
[[ 8. 4. 16.]
[ 8. 8. 4.]]
El escalar 2 se extiende para formar un array de forma (2,3) antes de operar:
2 -> [[2 2 2]
[2 2 2]]

Pasemos al caso 2, con dos arrays de diferentes tamaños, por ejemplo:

a = [[3. 2. 4.] g = [[100]


[3. 3. 2.]] [200]]

Como vemos a.shape = (2, 3) y g.shape = (2, 1), y por tanto estamos en el caso expuesto.
El array g se puede ‘extender’ tres veces a lo largo de segunda dimensión, repitiéndose de la manera:

g -> [[100 100 100]


[200 200 200]]
antes de sumarse al array a.

Ejemplo 7.7.2. Escriba el siguiente código:

>>> a = array([[3., 2., 4.], [3., 3., 2.]])


>>> g = array([[100],[200]])
# a.shape = (2, 3), g.shape = (2, 1)
>>> print(a+g)
[[ 103. 102. 104.]
[ 203. 203. 202.]]

Pasemos al caso 3 con dos arrays de diferente forma (shape). En ese caso se comparan las
dimensiones a lo largo de cada eje (axis) empezando por la derecha. Tienen que coincidir hasta
acabarse el número de dimensiones del array más ‘corto’. Por ejemplo:

a = [[3. 2. 4.] b = [10, 11, 12]


[3. 3. 2.]]

Como vemos a.shape = (2, 3) y b.shape = (3,), y por tanto estamos en el caso expuesto: el
array b se puede ‘extender’ a lo largo de una nueva dimensión 0, repitiéndose de la manera:
b = [[10 11 12]
[10 11 12]]
antes de sumarse al array a.

Introducción a la Fı́sica Computacional 20


Universidad de Oviedo Departamento de Fı́sica

Ejemplo 7.7.3. Operaciones entre arrays de diferentes tamaños.

Escriba el siguiente código:

>>> a = array([[3., 2., 4.], [3., 3., 2.]])


>>> b = array([10, 11, 12])
# a.shape = (2, 3), b.shape = (3,)
>>> print(a+b)
[[ 13. 13. 16.]
[ 13. 14. 14.]]

Veamos un caso de la regla 4 :


z = [1 2] v = [[3]
[4]
[5]]

Como vemos z.shape = (2, ) y v.shape = (3, 1). Ambos arrays se ‘extienden’ lo necesario,
repitiéndose de la manera:

z -> [[1 2] v -> [[3 3]


[1 2] [4 4]
[1 2]] [5 5]]
antes de sumarse.

Ejemplo 7.7.4. Escriba el siguiente código:

>>> z = array([1, 2])


>>> v = array([[3], [4], [5]])
# z.shape = (2, ), v.shape = (3, 1)
>>> print(z+v)
[[4 5]
[5 6]
[6 7]]

Otro caso con más dimensiones serı́a sumar los arrays A y B con la estructura siguiente:
A (4d array): 8 x 1 x 6 x 1
B (3d array): 7 x 1 x 5
A+B (4d array): 8 x 7 x 6 x 5
Vemos cómo se extienden tanto A como B en las dimensiones que son 1, y en la que falta al principio
de B.

Ejercicio 7.7.3. Cree un array a de la forma (8,1,6,1) cuyos elementos sean números enteros y
un array b de la forma (7,1,5) que contenga números en coma flotante y realice las siguientes
operaciones aritméticas: a+b, a-b y a/b

Introducción a la Fı́sica Computacional 21


Universidad de Oviedo Departamento de Fı́sica

7.7.7. Comparación de arrays

Las operaciones de comparación devuelven arrays con valores lógicos:


>>> a = array([[3., 2., 4.], [3., 3., 2.]])
>>> b = a > 2
>>> print(b)
[[ True False True]
[ True True False]]

No obstante, si los operadores de comparación se usan en una operación, True se convierte en 1 y


False se convierte en 0:
>>> a = array([[3., 2., 4.], [3., 3., 2.]])
>>> c = 9*(a > 2)
>>> print(c)
[[9 0 9]
[9 9 0]]

7.7.8. La función where

La función where está relacionada con las operaciones condicionales. Cuando se invoca con un
único argumento devuelve una tupla con los ı́ndices de los elementos del array donde se cumple la
condición:
>>> a = array([[3., 2., 4.], [3., 3., 2.]])
>>> d = where (a > 2)
>>> print(d)
(array([0, 0, 1, 1]), array([0, 2, 0, 1]))

Cuando where se invoca con tres argumentos sustituye los valores que cumplen una condición por
un cierto valor y los que no por otros:
>>> a = array([[3., 2., 4.], [3., 3., 2.]])
>>> g = where (a > 2, a**2, -1.0)
>>> print(g)
[[ 9. -1. 16.]
[ 9. 9. -1.]]

7.8. Funciones universales

Estas funciones, denominadas en ocasiones ‘puras‘ y en el argot de Python ufuncs, no tienen efectos
laterales (no modifican por ejemplo ninguna variable global ni manejan ficheros).

Por ejemplo, sin es una ufunc. Lo son también la mayorı́a de las funciones matemáticas que se
aplican sobre escalares.

Las ufuncs se aplican elemento a elemento.

Las funciones del módulo math están redefinidas en numpy para que el cálculo sea elemento a

Introducción a la Fı́sica Computacional 22


Universidad de Oviedo Departamento de Fı́sica

elemento de los arrays, y además sea muy rápido:


>>> a = array([[3., 2., 4.], [3., 3., 2.]])
>>> c = a+3*sin(a)*log(a)
>>> print(c)
[[ 3.46510853 3.89083084 0.8525469 ]
[ 3.46510853 3.46510853 3.89083084]]

Las ufuncs crean nuevos arrays.

Entre las funciones universales se tienen: absolute, add, arccos, arccosh, arcsin, arcsinh,
arctan, arctan2, arctanh, ceil, conj, cos, cosh, deg2rad, divide, exp, exp2, floor, fmod,
hypot, log, log10, log2, mod, multiply, negative, power, rad2deg, reciprocal, remainder,
rint, sign, sin, sinh, sqrt, square, subtract, tan, tanh, trunc.

7.8.1. Definición de funciones que tienen como argumentos arrays

En ocasiones necesitamos definir funciones que usen como argumentos arrays. Deben ser funciones
puras. Por ejemplo:
>>> def f (a):
... return a+3*sin(a)*log(a)
...
>>> a = array([[3., 2., 4.], [3., 3., 2.]])
>>> f(a)
array([[ 3.46510853, 3.89083084, 0.8525469 ]
[ 3.46510853, 3.46510853, 3.89083084]])

7.8.2. Vectorizando funciones

No obstante, no todas las funciones puras que diseñemos para escalares pueden ser invocadas
directamente con arrays.
Ejemplo 7.8.1. Funciones que no admiten arrays como argumentos.

Escriba el siguiente código:

>>> def h (a):


... return a if a > 2 else -a
...
>>> a = array([[3., 2., 4.], [3., 3., 2.]])
>>> h(a)
ValueError: The truth value of an array . . .

En el caso anterior se necesita vectorizar la función. Ello se hace con un decorador, que es otra
función que se invoca previamente a la nuestra (y que hace ‘cosas’ con ella). En este caso se puede
poner @vectorize en la lı́nea anterior a def h(a):.

Introducción a la Fı́sica Computacional 23


Universidad de Oviedo Departamento de Fı́sica

Ejercicio 7.8.1. Repita el ejemplo anterior utilizando el decorador (@vectorize) para que la función
h(a) admita un array como argumento.

Para vectorizar una función ya definida previamente, se puede usar la función decoradora
(vectorize) para crear una nueva función.

Ejemplo 7.8.2. Crear una nueva función vectorizada a partir de h(a)

Escriba el siguiente código:


>>> hh = vectorize(h) # hh es h ’decorada’
>>> hh(a)
array([[ 3., -2., 4.],
[ 3., 3., -2.]])

La vectorización de funciones no es necesaria cuando aparecen operaciones básicas y llamadas a


ufuncs, como en el caso anteriormente expuesto:

def f (a): return a+3*sin(a)*log(a)

Es más, en este caso vectorizar f es contraproducente puesto que tarda más la llamada que en el
caso presente.

Recomendación: no vectorizar hasta que el intérprete de un error de vectorización, en ese caso


vectorizar la función que lo necesite, o rediseñar el código fuente.

7.9. Funciones que devuelven valores

Algunas de las funciones que trabajan con arrays y que devuelven valores son:

all, any, min, max, sum, prod, mean, std, dot, cross, outer, diag.

La función all

La función all realiza un ‘y’ lógico (and) con todos los elementos de un array (o con los elementos
a lo largo de un eje):
>>> a = array([[3, 2, 4], [3, 3, 0]])
>>> a.all()
False
>>> b = a.all(axis=1)
>>> print(b)
[ True False]
Recordemos que 0 (y 0.0) se toma como valor False y otro valor como True.

Introducción a la Fı́sica Computacional 24


Universidad de Oviedo Departamento de Fı́sica

La función any

La función any realiza un ‘o’ lógico (or) con todos los elementos de un array (o con los elementos
a lo largo de un eje):
>>> a = array([[3, 2, 4], [3, 3, 0]])
>>> a.any()
True
>>> c = a.any(axis=0)
>>> print(c)
[ True True True]
Recordemos que 0 (y 0.0) se toma como valor False y otro valor como True.

Las funciones max y min

La función a.min() (OJO min(a) no funciona) nos devuelve el mı́nimo valor del array. a.max()
(OJO max(a) no funciona) nos devuelve el máximo valor del array.
>>> a = array([[3., 2., 4.], [3., 3., 2.]])
>>> a.min()
2.0
>>> a.max()
4.0

También se pueden pedir mı́nimos y máximos a lo largo de algún eje:


>>> a = array([[3., 2., 4.], [3., 3., 2.]])
>>> a.min(axis=0)
array([ 3., 2., 2.])
>>> a.max(axis=1)
array([ 4., 3.])

Las funciones sum y prod

Son funciones similares a min, pero devuelven la suma (sum) y el producto (prod) de los elementos
del array:
>>> a = array([[3., 2., 4.], [3., 3., 2.]])
>>> a.sum(axis=0)
array([ 6., 5., 6.])
>>> a.prod(axis=1)
array([ 24., 18.])

Las funciones mean y std

La función mean devuelve la media y std la desviación estándar de los elementos del array:
>>> a = array([[3., 2., 4.], [3., 3., 2.]])
>>> a.mean(axis=1)

Introducción a la Fı́sica Computacional 25


Universidad de Oviedo Departamento de Fı́sica

array([ 3. , 2.66666667])
>>> a.std()
0.68718427093627676

La función dot

El producto escalar de dos vectores se obtiene mediante la función dot:


>>> a = array([3., 2., 4., 0.])
>>> b = array([2., 0., -1., 2.])
>>> dot(a, b)
2.0
El tamaño de ambos arrays debe ser el mismo.

La función dot también se usa para realizar el producto de matrices (no confundir con el producto
elemento a elemento visto antes):
>>> a = array([[3., 2., 4.], [3., 3., 2.]])
>>> b = array([2., 0., -1.])
>>> dot(a, b)
array([ 2., 4.])

Para poder multiplicar dos arrays, a y b, como matrices, el número de elementos a lo largo de la
última dimensión de a debe ser igual al número de elementos de la primera dimensión de b:
>>> a.shape
(2, 3)
>>> b.shape
(3,)

La función cross

El producto vectorial de dos vectores de 3 elementos se obtiene mediante la función cross:


>>> x = array([1, 2, 3])
>>> y = array([-1, 2, -4])
>>> cross(x, y)
array([-14, 1, 4])

El tamaño de ambos arrays debe ser 3.

La función outer

Dados dos vectores, a de longitud M y b de longitud N, se define de la siguiente manera:

cik = ai bk , i = 0, . . . , M − 1, k = 0, . . . , N − 1.

Para calcularlo se utiliza la función outer:


>>> a = array([1, 2, 3])
>>> b = array([-1, 2])

Introducción a la Fı́sica Computacional 26


Universidad de Oviedo Departamento de Fı́sica

>>> outer(a, b)
array([[-1, 2],
[-2, 4],
[-3, 6]])

La función diag

La función diag nos devuelve la diagonal de un array:


>>> a = array([[1, 2, 3, 4], [5, 6, 7, 8]])
>>> print(a)
[[1 2 3 4]
[5 6 7 8]]
>>> diag(a)
array([1, 6])
>>> diag(a, k=1) # 1 encima de la principal
array([2, 7])

7.10. Ejercicios

Ejercicio 7.10.1. Un proyectil se dispara con una velocidad de 750 m/s. Calcule la distancia d a la
que el proyectil alcanza el suelo si el ángulo de lanzamiento θ cambia de 5o a 85o en incrementos de
5o . Utilice operaciones elemento a elemento. Para visualizar los resultados cree una matriz de 17x2
en la cual los elementos de la primera columna sean los ángulos de lanzamiento, y los de la segunda
las correspondientes distancias redondeadas al entero más próximo.

Ejercicio 7.10.2. Dos proyectiles, A y B, se disparan en el mismo instante desde el mismo punto.
El proyectil A se dispara a una velocidad de 680 m/s con un ángulo de 65o , mientras que el proyectil
B se dispara a una velocidad de 780 m/s con una ángulo de 42o . Calcule qué proyectil llega antes
a tierra. Seguidamente, tome el tiempo de vuelo tv de ese proyectil y divı́dalo en diez incrementos,
creando para ello un vector t con 11 elementos igualmente espaciados (el primer elemento será 0 y
el último tv . Calcule la distancia entre los dos proyectiles para cada uno de los 11 valores de t.

Ejercicio 7.10.3. Un tren y un coche se aproximan a un cruce. En el instante t = 0, el tren está a


120 m al sur del cruce, viajando hacia el norte a una velocidad de 86 km/h. En el mismo instante,
el coche se encuentra a 60 m al oeste del cruce, viajando hacia el este a una velocidad de 45 km/h
y una aceleración de 4 m/s2 . Determine las posiciones del tren y del coche, la distancia entre ellos,
ası́ como la velocidad del tren relativa al coche en cada segundo, durante 10 s. Para presentar los
resultados cree una matriz de 11x6 en la que se muestren en columnas: el tiempo, la posición del
tren, la posición del coche, la distancia entre el tren y el coche, la velocidad del coche y la velocidad
del tren relativa al coche.

Introducción a la Fı́sica Computacional 27


Universidad de Oviedo Departamento de Fı́sica

7.11. Interés de vectorializar el código

Eliminar ı́ndices del código es posible siempre que las expresiones se puedan evaluar a la vez para
cualquier ı́ndice (si no es ası́ no se puede vectorializar el código). Además, siempre que se eliminen del
código ı́ndices que deban ser iterados con un bucle tanto mejor (en cualquier lenguaje interpretado
al menos). Pero eliminar ı́ndices implica utilizar más memoria y repensar el código (lo que a veces
es difı́cil).

Analizaremos varios casos de vectorialización de expresiones, algunos con más contenido fı́sico que
otros.

Ejemplo 7.11.1. Un caso simple

Sea b un array cuadrado de forma (n, n). La expresión:



i = 0, . . . , n − 2,
cik = bik + bi+1,k+1 ,
k = 0, . . . , n − 2,

se codifica:

c[:-1,:-1] = b[:-1,:-1] + b[1:,1:]

Ejemplo 7.11.2. Integración por rectángulos

Se define la integral de Riemann de una función f (x) entre dos abscisas a y b mediante:

Z b
I= f (x)dx.
a

y y

f ( x) f ( x)

I
0 1 i ... n −1

x h x
a b a b

La solución de este problema puede obtenerse numéricamente por el método de los rectángulos,
usando como altura de cada rectángulo el valor de la función en la abscisa media de cada rectángulo:

n−1  
X 1 b−a
I≈h f a + h + ih , h= .
2 n
i=0

Introducción a la Fı́sica Computacional 28


Universidad de Oviedo Departamento de Fı́sica

Se puede usar la siguiente función:

def integra (f, a, b, n=100):


h = (b-a)/float(n)
x = linspace(a+0.5*h, b-0.5*h, n)
return h*sum(f(x))

La vectorialización va implı́cita en la llamada a la función f para luego realizar la suma.

Ejemplo 7.11.3. Ecuación de Poisson en 2D

La ecuación de Poisson del electromagnetismo es en 2D:

∂2V ∂2V ρ
2
+ =− ,
∂x ∂y 2 ǫ0
siendo V el potencial eléctrico y ρ la densidad de carga en cada punto del recinto.

Discreticemos un recinto rectangular con un mallado h (tanto en Ox como en Oy) con nx nodos en
Ox y ny nodos en Oy, de tal manera que V (i, k) define el potencial en los puntos i = 0, 1, . . . , nx −1,
k = 0, 1, . . . , ny − 1. El problema de Dirichlet (potencial conocido en la frontera) se puede resolver
iterando en m:

h2
 
m+1 1 m m m m
Vi,k = Vi+1,k + Vi−1,k + Vi,k+1 + Vi,k−1 + ρi,k
4 ǫ0
i = 1, 2, . . . , n − 2, k = 1, 2, . . . , n − 2.

Denominando K a h2 /ǫ0 , y R a ρ tendrı́amos:

Vn[1:-1,1:-1] = (V[2:,1:-1]+V[:-2,1:-1]+ V[1:-1,2:]+V[1:-1,:-2]+


K*R[1:-1,1:-1])/4
El potencial en los bordes no se calcula en esta expresión (no varı́a con el tiempo). Para el siguiente
paso temporal se hace:
V[1:-1,1:-1] = copy(Vn[1:-1,1:-1])

Ejemplo 7.11.4. Difusión de neutrones en 1D

En un reactor nuclear de fisión (en 1D para simplificar) si denominamos y a la cantidad de neutrones


presentes, C a su tasa de creación y D al coeficiente de difusión, se verifica la siguiente ecuación
diferencial en derivadas parciales:
∂y ∂2y
= D 2 + Cy.
∂t ∂x
Se supone que y(x = 0) = y(x = L) = 0, siendo x = 0 y x = L los bordes del recinto.

Esta ecuación se puede discretizar en el espacio usando n intervalos, de tal manera que h = L/n,
y en el tiempo usando un incremento temporal τ . Con i = 1, 2, . . . , n − 2 para los puntos en el
espacio, e indicando m el instante temporal, tendrı́amos:
Dτ m
yim+1 = yim + (y m
+ yi−1 − 2yim ) + Cτ yim .
h2 i+1

Introducción a la Fı́sica Computacional 29


Universidad de Oviedo Departamento de Fı́sica

Este método permite estudiar el problema de la criticidad (automantenimiento de la reacción nuclear)


variando D, C y la condición inicial, yi0 .

Denominando K a Dτ /h2 , y R a 1 + Cτ − 2K tendrı́amos:

yn[1:-1] = R*y[1:-1] + K*(y[2:]+y[:-2])

Para el siguiente paso temporal se hace:

y[1:-1] = copy(yn[1:-1])

Ejemplo 7.11.5. Serie radiactiva (opcional)

Las series radiactivas sin bifurcaciones están formadas por radionúclidos que se desintegran unos en
otros: 0 → 1 → 2 → . . . → n, siendo el núclido n estable. Si Ni es el número de núcleos presentes
del radionúclido i se tiene:

Ṅ0 = − λ0 N0 ,
Ṅi = λi−1 Ni−1 − λi Ni .

λi es la denominada constante de desintegración del radionúclido i.

Se puede poner en forma matricial:


 
−λ0 0 0 ... 0
 λ0 −λ1 0 ... 0 
 
Ṅ = 
 0 λ 1 −λ 2 ... 0  N.

 ... ... ... ... 
0 0 0 . . . −λn−1

Si denominamos L al vector con las λ:


L = [L0, L1, ...]
y N al vector de número de núcleos:
N = [N0, N1, ...]
se vectorializa de la siguiente manera:
n = N.size # n = L.size, también
Np = dot((eye(n, n, k=-1)-eye(n, n))*L, N)

Al operar con L y N se realiza broadcasting.

Ejemplo 7.11.6. Eliminación sucesiva de ı́ndices (opcional)

Sea x un vector de longitud n. La expresión:


n−1
X
wi = |xi + xk |, i = 0, . . . , n − 1,
k=0

se puede codificar:
w = zeros like(x)

Introducción a la Fı́sica Computacional 30


Universidad de Oviedo Departamento de Fı́sica

indices = range(len(x))
for i in indices:
w[i] = sum(abs(x[i]+x))

Pero también mediante:


n = len(x)
x.shape = (n, 1)
aux = x.repeat(n, axis = 1)
w = sum(abs(aux+aux.T), axis = 0)

Este segundo método necesita mucha más memoria que el primero (el array bidimensional aux al
menos), pero es más rápido, ya que no lleva bucles.

Ejemplo 7.11.7. Tratamiento de un caso con excepciones (opcional)

Sea x un vector de longitud n. La expresión:


n−1
Y
wi = (xk − xi ), i = 0, . . . , n − 1,
k=0,k6=i

(la excepción es que k 6= i) se puede codificar:


w = zeros like(x)
indices = range(len(x))
for i in indices:
w[i] = prod(where(x != x[i], x-x[i], 1.0))

Pero es más rápido el trozo de código siguiente:

n = len(x)
x.shape = (n, 1)
aux = x.repeat(n, axis = 1)
aux = aux-aux.T
w = prod(where(aux != 0, aux, 1.0), axis = 0)
Como vemos, la eliminación de un ı́ndice ‘que se pierde’ (k cuando es igual al i) conlleva la utilización
de la función where, como máscara.

Este caso puede servir de partida, por ejemplo, para codificar el cálculo de la aceleración de los
planetas y el Sol en una simulación del sistema solar (se deja como ejercicio), ya que también hay
un ı́ndice ‘que se pierde’.

Introducción a la Fı́sica Computacional 31

También podría gustarte