Está en la página 1de 281

PIMCD2013-python

Publicación 1.0

Luis Miguel Sánchez Brea

March 10, 2015


Índice general

1. Introducción 3
1.1. Precedentes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
1.2. ¿Por qué Python? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
1.3. Python vs. Matlab . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6
1.4. Python en el ámbito científico . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6

2. Puesta en Marcha 9
2.1. Windows . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
2.2. Linux . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10
2.3. Consolas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10
2.4. IDEs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11
2.5. Pythonpath . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14
2.6. Ayudas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15
2.7. Autoejecución . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23
2.8. Cabecera . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23
2.9. Generación de ayudas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24
2.10. Carga de Módulos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24
2.11. Definición de funciones . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25
2.12. Código ejecutable: main . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26
2.13. Sistema de verificación del código . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28

3. Lenguajes de Programación 29
3.1. Características de programación . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29

4. Módulos Científicos 37
4.1. Numpy . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37
4.2. Scipy . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41
4.3. Matplotlib . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 57

5. Ecuaciones de Maxwell en el vacío 71


5.1. Introducción . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 71
5.2. Programación . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 72
5.3. Haces de Gauss . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 80

6. Polarización 81
6.1. Introducción . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 82
6.2. Programa interactivo para el análisis de la polarización . . . . . . . . . . . . . . . . . . . . . . . 82
6.3. Definiciones de polarizadores . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 82
6.4. Definiciones de estados de luz polarizada . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 84
6.5. Cálculo de la intensidad de un vector de polarización . . . . . . . . . . . . . . . . . . . . . . . . 86
6.6. Polarización básica . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 86
6.7. Ley de Malus . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 86
6.8. Efecto Zenon . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 88

I
7. Interacción radiación-materia 91
7.1. Introducción . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 91
7.2. Dipolo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 93
7.3. Resonancia . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 96
7.4. Densidad espectral . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 98
7.5. Sección eficaz . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 99

8. Índice de refracción: modelo microscópico 103


8.1. Introducción . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 103
8.2. Índice de refracción para cargas ligadas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 104
8.3. Aproximación de Sellmeier . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 107
8.4. Aproximación de Cauchy . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 111
8.5. Frecuencia de plasma . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 113

9. Ecuaciones de Fresnel 117


9.1. Introducción . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 118
9.2. Funciones para calcular las ecuaciones de Fresnel . . . . . . . . . . . . . . . . . . . . . . . . . . 119
9.3. Ejemplos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 122
9.4. Ejercicios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 127
9.5. Referencias . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 128

10. Pulsos de luz 129


10.1. Introducción . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 130
10.2. Espectros de distintos tipos de pulso . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 130
10.3. Suma de dos ondas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 135

11. Interferencias 137


11.1. Introducción . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 138
11.2. Programación . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 138
11.3. Interferencia entre dos ondas plana . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 138
11.4. Interferencia entre dos haces de Gauss . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 146
11.5. Interferencia entre dos ondas esféricas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 150

12. Módulos de Óptica 157


12.1. Clase camposXY . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 157
12.2. Clase fuentesXY . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 160
12.3. Clase mascarasXY . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 170

13. Difracción en campo cercano 191


13.1. Introducción . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 192
13.2. Programación . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 192
13.3. Difracción por una abertura cuadrada . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 193
13.4. Difracción por una abertura cuadrada: varias distancias . . . . . . . . . . . . . . . . . . . . . . . 195
13.5. Difracción por una abertura circular . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 196
13.6. Difracción por un borde . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 199
13.7. Difracción por una doble rendija . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 202
13.8. Difracción por una lente . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 204
13.9. Difracción por una lente binaria . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 206

14. Difracción en campo lejano 209


14.1. Introducción . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 210
14.2. Difracción por un cuadrado . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 210
14.3. Difracción por una rendija . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 212
14.4. Difracción por un círculo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 214
14.5. Difracción por una doble rendija . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 215
14.6. Difracción por una estructura complicada: estrella . . . . . . . . . . . . . . . . . . . . . . . . . 217
14.7. Difracción por una estructura complicada: fotografía . . . . . . . . . . . . . . . . . . . . . . . . 218
14.8. Difracción por una red de sinusoidal . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 219
14.9. Difracción por una red de Ronchi . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 221

II
14.10. Propiedades de la transformada de Fourier . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 223

15. Redes de Difracción 233


15.1. Introducción . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 234
15.2. Red Sinusoidal . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 235
15.3. Red de Ronchi . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 236
15.4. Red de Binaria . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 238
15.5. Red Blazed . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 239
15.6. Red bidimensional . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 240
15.7. Red tipo Ajedrez . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 241
15.8. Red radial . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 242
15.9. Red angular . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 243
15.10. Red con borde sinusoidal . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 244
15.11. Red hiperbólica . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 245
15.12. Aplicación como redes de difracción en campo lejano . . . . . . . . . . . . . . . . . . . . . . . 247
15.13. Efecto Talbot . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 251
15.14. Efectos con dos redes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 253

16. Procesado óptico de la información 259


16.1. Funcionamiento de la clase procesadoOptico . . . . . . . . . . . . . . . . . . . . . . . . . . . . 260
16.2. Filtro pasa baja . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 262
16.3. Eliminación de ruido . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 264
16.4. Filtro pasa alta . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 266
16.5. Filtro pasa banda . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 271
16.6. Filtro de contraste de fase . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 273

III
IV
PIMCD2013-python, Publicación 1.0

Índice general 1
PIMCD2013-python, Publicación 1.0

2 Índice general
CAPÍTULO 1

Introducción

Autores José María Herrera Fernández, Luis Miguel Sánchez Brea

Contenidos de este capítulo:


Precedentes
¿Por qué Python?
Python vs. Matlab
Python en el ámbito científico

1.1 Precedentes

El manejo de aplicaciones informáticas, actualmente generalizado, no conlleva realmente el dominio de las nuevas
tecnologías informáticas. La programación y simulación se ha convertido en una tarea necesaria en la sociedad
y en el ámbito científico-técnico, y por supuesto en el ámbito académico. Sin embargo, la gran mayoría de los
alumnos que llegan a los primeros cursos universitarios no tienen ninguna experiencia en programación. Incluso en
cursos superiores, un porcentaje demasiado alto no han ido más allá de los ejercicios propuestos en las asignaturas
obligatorías de cálculo numérico.
Dentro de la asignatura “Óptica” del grado en Física de la Universidad Complutense de Madrid, Luis Miguel
Sánchez Brea y José María Herrera Fernández (profesor de la asignatura y alumno-tutor respectivamente) in-
corporaron en el curso 2011 - 2012 una serie de seminarios de “Computación científica de fenómenos ópticos
basados en el lenguaje de programación Python. Los seminarios se enfocaron como apoyo al aprendizaje de Óp-
tica utilizando la programación como medio de resolución de ejercicios en Óptica y fueron la semilla para este
proyecto docente donde pretendemos presentar ejemplos sencillos y de fácil comprensión del uso de Python como
herramienta en diferentes ámbitos.

1.2 ¿Por qué Python?

Python es un lenguaje de programación moderno, de alto nivel, ideal para pequeñas aplicaciones así como para
proyectos de gran envergadura. El código de Python es similar al pseudocódigo utilizado para esquematizar la
programación. Permite realizar todo lo necesario para una programación completa: diseño orientado a objetos,
unidades de testeo, generación de documentación en el código, sencilla interacción con el sistema operativo, etc.
Es compatible con la programación basada en objetos, así como la estructurada y por eventos. Tiene un excelente
conjunto de librerías para extender su funcionalidad, incluyendo librerías científicas que permiten realizar nume-
rosas tareas de tratamiento de datos, visualización, cálculo numérico y simbólico y otras aplicaciones específicas.
Además, existe una comunidad muy grande de usuarios que, debido a la filosofía “open source”, son mucho más
proclives a compartir su código.
A la hora de empezar a trabajar en computación lo primero que nos sorprende es la gran cantidad de lenguajes
de programación, por lo que se debe realizar una selección cuidadosa. Una lista de los lenguajes de programación

3
PIMCD2013-python, Publicación 1.0

más utilizados se puede encontrar en la Figura 1.

Figura 1.1: Figura 1. Lenguajes de programación más utilizados en el mundo. Python se mantiene es-
table como tercer lenguaje de programación después de la familia de C y de Java. Además com-
parándo con un lenguaje de ámbito científico como MATLAB, su posición es notablemente superior
(4,3 % de uso frente a 0,6 %). Fuente: TIOBE Programming Community Index for November 2013,
http://www.tiobe.com/index.php/content/paperinfo/tpci/index.html .

Por un lado existen lenguajes de uso muy extendido. Actualmente Java es el lenguaje más utilizado, pues se
utiliza para aplicaciones corporativas. El segundo lenguaje es C en sus diferentes versiones, un estándar en la
programación durante muchísimos años. La mayor parte de las más famosas aplicaciones de escritorio están
escritas en C++. Estos dos lenguajes dominan el negocio de la programación. PHP domina en la red. Mientras Java
lo utilizan las grandes organizaciones, PHP lo utilizan empresas más pequeñas e individuos para crear aplicaciones
web dinámicas. Por otro lado, hay numeros paquetes numéricos como Matlab, Mathematica y Maple que se
utilizan en centros de investigación.
La gran mayoría de los alumnos que llegan a los primeros cursos de Física no han programado nada o casi nada.
Es por ello que hemos elegido un lenguaje de programación lo más sencillo y completo posible. Python nos
ofrece una gran sencillez de programación, una gran cantidad de librerías en las que basar nuestros programas y

4 Capítulo 1. Introducción
PIMCD2013-python, Publicación 1.0

una importante comunidad de usuarios y desarrolladores que lo soportan. También tiene la ventaja de ser “open
source”, por lo no nos vamos a tener que asumir ningún coste debido a derechos de autor. Otra ventaja es ser un
sistema multiplataforma, así que podremos programar en cualquier sistema operativo como Windows, Linux o
Mac Os.
Veamos resumidas algunas de las ventajas del lenguaje de programación Python.
Python es un lenguaje de programación de alto nivel que permite una rápida implementación de aplicaciones
con muy poco código. Es ideal para prototipado rápido, pero tambien permite el desarrollo de grandes
aplicaciones.
El código de Python es legible, muy similar al pseudocódigo utilizado para esquematizar la programación.
Python es un lenguaje interpretado por una máquina virtual, no compilado, por lo que es multiplataforma.
Python permite realizar todo lo necesario para una programación de código moderna: diseño orientado a
objetos, unidades de testeo, generación de documentación en el código, sencilla interacción con el sistema
operativo, etc.
Tiene estructuras de datos de alto nivel, como las listas, tuplas y diccionarios
Permite la programación basada en objetos, aunque tambien soporta la programación funcional y la progra-
mación estructurada.
Es un lenguaje dinámico, pudiendose añadir nuevas funciones y clases a un objeto existente, incluso en
tiempo de ejecución.
Existen librerías científicas escritas en Phython maduras y con gran capacidad.
Permite extender trivialmente su aplicabilidad con módulos escritos en C, C++ y Fortran, de los que existen
numerosas librerías científicas. Al ser los procedimientos más rápidos y eficientes para desarrollo de algo-
ritmos matemáticos (aunque a veces mucho más complicados para otras tareas), se pueden programar las
partes más sensibles en C y realizar el resto de la aplicación en Python
Tiene un enorme conjunto de librerías para extender su funcionalidad: para acceseo a base de datos, para
almacenamiento de datos, para procesamiento de imágenes, de sonido, para programación con GUIs (Inter-
faces gráficas de usuario, ventanas), y otras muchisimas más.
Permite el acceso a toda la funcionalidad del sistema operativo (sobre todo en Linux).
Python se puede utilizar como software “glue” para combinar distintas aplicaciones en una sola (sobre todo
el Linux).
El uso de la indentación en lugar de los paréntesis permite una mayor legibilidad del código.
El código Python se escriben en archivos .py. Estos archivos se pueden agrupar en módulos, y se pueden
importar los elementos de los módulos a otros archivos.
Para sistemas Unix, se puede utilizar código para hacer el archivo autoejecutable indicando al sistema que
programa que el es un ejecutable (no hace falta que sea un archivo .exe). utilizar (#!)
El código es extraordinariamente sencillo de comprender.
Existen módulos de gran calidad y repositorios de código donde se puede encontrar numerosas aplicaciones.
En linux, gran parte de los módulos están en los repositorios estándar, por lo que su instalación es tán sencilla
como pulsar un botón.
Existe una comunidad muy grande de usuarios y debido a la filosofía open, los desarrolladores son mucho
más proclives a compartir su código con otros. Además no suelen existir problemas de uso del código de
otros, siempre que cumplas los requisitos de la licencia GLS.
Al ser Python un lenguaje de propósito general, el código que se genera se puede aprovechar de las ventajas
de otros módulos (como comprimir archivos, enviar datos, automatizar procesos, enlazar nuestros datos con
bases de datos, con servidores web....
Python se puede ejecutar en bastantes IDEs (editores), desde algunos muy sencillos, como IDLE, hasta otros
tan completos como Spyder, Eclipse o NetBeans.

1.2. ¿Por qué Python? 5


PIMCD2013-python, Publicación 1.0

Existen diversas técnica para creas sistemas de ventanas (Tkinter, wx, QT, etc.) de una forma sorprendente-
mente sencilla en comparación con otros sistemas operativos. Si se nos da bien el curso, intentaremos ver
QT4, a través de la herramienta de creación de ventanas QT Designer 4.

1.3 Python vs. Matlab

MATLAB (abreviatura de MATrix LABoratory, “laboratorio de matrices”) es un software matemático que ofrece
un entorno de desarrollo integrado (IDE) con un lenguaje de programación propio (lenguaje M). Está disponible
para las plataformas Unix, Windows y Apple Mac OS X. Entre sus prestaciones básicas se hallan: la manipulación
de matrices, la representación de datos y funciones, la implementación de algoritmos, la creación de interfaces de
usuario (GUI) y la comunicación con programas en otros lenguajes y con otros dispositivos hardware. El paquete
MATLAB dispone de dos herramientas adicionales que expanden sus prestaciones, a saber, Simulink (plataforma
de simulación multidominio) y GUIDE (editor de interfaces de usuario - GUI). Además, se pueden ampliar las
capacidades de MATLAB con las cajas de herramientas (toolboxes); y las de Simulink con los paquetes de bloques
(blocksets). Matlab tiene ventajas muy interesantes para la programación científica.
Las sesiones interactivas permiten “jugar” con los datos de una forma muy sencilla.
La sintaxis está muy bien desarrollada para trabajar con vectores y matrices.
La capacidad de visualización de datos y modificación de figuras es excepcionalemente buena.
Existen librerías (normalmente propietarias) de gran calidad y existe una comunidad muy grande de usua-
rios.
No obstante, la mayor parte de estas ventajas también las tiene Phython. Además, tiene una serie de desventajas
que lo desaconsejan como primer lenguaje de programación.
En primer lugar no es un lenguaje de programación “verdadero” ya que está muy dedicado al cálculo numéri-
co, aunque su funcionalidad se ha extendido con numerosas toolboxes (control de bases de datos, interacción
con el sistema operativo, etc...).
Adicionalmente, es un lenguaje propietario que requiere de licencias excesivamente caras para estudiantes.
No se puede ejecutar código de forma independiente (standalone). NOTA: Recientemente se ha proporcio-
nado una herramienta adicional llamada MATLAB Builder bajo la sección de herramientas “Application
Deployment” para utilizar funciones MATLAB como archivos de biblioteca que pueden ser usados con
ambientes de construcción de aplicación .NET o Java.
Es horrible hacer un código largo, pues cada función debe estar en un único archivo. Esto significan miles
de archivos y hace más complicado que varios programadores trabajen en el mismo código.
También Python tiene algunas desventajas respecto a Matlab:
No es tan sencillo manipular las figuras en tiempo real.
Al existir una cantidad ingente de programadores que aportan su código de forma libre, a veces es dificil
determinar cuales son los módulos “mejores” para una cierta aplicación. Hay más posiblidades donde elegir.

1.4 Python en el ámbito científico

Python es un lenguaje de programación de propósito general que, en un principio, no se pensó para aplicacio-
nes científicas. Con los años, siguiendo la filosofía GNU, se han desarrollado multitud de módulos científicos,
que permiten realizar numerosas tareas de tratamiento de datos, visualización, cálculo simbólico y aplicaciones
científicas específicas. Nosotros utilizaremos fundamentalmente los siguientes módulos científicos:
Numpy: Generación de tipos de datos científicos. http://www.numpy.org/
Scipy: Funciones científicas de uso general. http://www.scipy.org/
Matplotlib: Gráficas en 2D y 3D. http://matplotlib.org/
Mayavi: visualización de datos tridimensionales. http://code.enthought.com/projects/mayavi/

6 Capítulo 1. Introducción
PIMCD2013-python, Publicación 1.0

Visual Python: Permite realizar representaciones tridimensionales de objetos en movimiento.


http://www.vpython.org/
SymPy: Cálculo simbólico. http://sympy.org/en/index.html
Opencv: Biblioteca libre de visión artificial en C cuyas librerías se han adaptado a Python.
http://opencv.willowgarage.com/documentation/python/
Meep: cálculo de Diferencias finitas en el dominio del tiempo. http://ab-initio.mit.edu/wiki/index.php/Meep.
NOTA: Existe un “enlace” (binding) para Python, de forma que se puede programar dicho módulo de una
manera mucho más sencilla, https://launchpad.net/python-meep.
Existen asimismo otros muchos módulos bastante maduros escritos en Python como OpenFilters para el dise-
ño de filtros interferenciales, http://www.polymtl.ca/larfis/English_Version/OF/OF_English.htm y Empy donde se
implementa la técnica de RCWA, http://empy.sourceforge.net. Para más información el lector puede consultar el
siguiente enlace http://www.scipy.org/topical-software.html donde podrá encontrar otros modulos de interés.

1.4. Python en el ámbito científico 7


PIMCD2013-python, Publicación 1.0

8 Capítulo 1. Introducción
CAPÍTULO 2

Puesta en Marcha

Autores José María Herrera Fernández, Luis Miguel Sánchez Brea

Contenidos de este capítulo:


Windows
Linux
• Trabajar con Python
Consolas
IDEs
Pythonpath
Ayudas
• Mi primer programa
• Estructura de un programa .py
Autoejecución
Cabecera
Generación de ayudas
Carga de Módulos
Definición de funciones
Código ejecutable: main
Sistema de verificación del código

Como en casi todos los programas, dependiendo del sistema operativo del que disponga nuestro ordenador la
instalación de software variará ligeramente y Python no es una excepción. A fin de que el sistema operativo no
suponga un bache en nuestro camino hacia el aprendizaje de Python, en esta sección veremos la instalación paso a
paso en los dos sistemas operativos más extendidos en la actualidad. Para otros sistemas operativos recomendamos
al lector que en caso de duda acuda a la gran bibliografía existente en la red.

2.1 Windows

A la hora de instalar Python en el sistema operativo de Microsoft tenemos dos opciones según el uso que vayamos
a hacer del lenguaje de programación. Por una parte, como ya hemos mencionado en la introducción, Python es un
leguanje de programación de uso general por lo que, si éste es el caso, recomendamos la instalación del paquete
básico e incorporar los módulos que necesitemos. En el sitio oficial de Phython en la sección de descargas podemos
encontrar diferentes versiones. En la actualidad conviven las versiones 3.3.* y 2.7.* donde * es la última versión
estable. Las instalaciones de este libro fueron realizadas con la versión 2.7.2 aunque no varía sustancialmente
para versiones posteriores. Por otra parte, como la programación que vamos a realizar está orientada al ámbito
científico-académico e ingenieril, la opción que más se ajusta a nuestros intereses es el paquete Python(x,y).
Python(x,y) es un software de desarrollo gratuito que incorpora los principales módulos necesarios para cálculo
numérico junto con algún otro complemento que analizaremos posteriormente. La instalación es similar a otros
programas de Windows. Lo primero que debemos de hacer es descargarmos la última versión del programa desde
la sección de descargas de la web oficial, http://code.google.com/p/pythonxy/wiki/Downloads?tm=2.

9
PIMCD2013-python, Publicación 1.0

En la Figura 2 podemos ver que en el mismo enlace disponemos de las notas de instalación que recomendamos
repasar antes de iniciar el proceso. Son unos 420 MB (ya que tiene bastantes aplicaciones). Una vez instalado
observaremos como nuestros archivos con extensión .py se asocian con python por lo que si pinchamos dos veces
sobre ellos se ejecutarán.

Figura 2.1: Figura 2. Descarga de Python(x,y) desde la sección correspondiente en la web oficial. En las misma
página podemos encontrar las notas de instalación que debemos de tener en cuenta.

2.2 Linux

Python viene incorporado en la mayoría de las distribuciones existentes. Si en una terminal ejecutamos el comando
python - V

comprobaremos que versión de Python tenemos instalada. En caso de no ser así podemos instalarlo ejecutando
apt-get install python

Otra opción es, por ejemplo, desde el centro de software en Ubuntu. Basta con buscar python en el lugar co-
rrespondiente y nos indicará si lo tenemos instalado y si queremos instalarlo o desistalarlo. Para instalar nuevos
módulos se puede seguir el mismo procedimiento ya sea desde la terminal o desde el centro de software.

2.2.1 Trabajar con Python

A la hora de iniciarse en la programación es muy importante conocer la sintasis del lenguaje que se va a utilizar
para desarrollar codigo de una manera correcta. De igual importancia es conocer las herramientas de las que
disponemos para ejecutar y depurar el código desarrollado.

2.3 Consolas

Desde el mismo momento en el que Python se instala en el sistema podemos empezar a programar abriendo una
consola interactiva en una terminal. La consola interactiva más sencilla se ejecuta directamente en un terminal
(Linux) escribiendo
python

10 Capítulo 2. Puesta en Marcha


PIMCD2013-python, Publicación 1.0

Inmediantamente podremos ir introduciendo lineas de comandos y ejecutar de una en una. En Windows la forma
es equivalente.
Existen concolas interactivas más completas que nos van indicando ayudas inmediatas a medida que vamos escri-
biendo código. Se introduce la instruccion y se ejecuta de una en una. Esta forma de programación es adecuada
cuando somos principiantes y estamos desarrollando programas cortos donde nos interesa ver inmediatamente si
el resultado es correcto o no.

2.4 IDEs

Las consolas no es el mejor procedimiento para desarrollar programas extensos. El código escrito en la consola
no es fácil de seguir y de volver a utilizar. Una programación eficaz se realiza mediante un Entorno de Desarro-
llo Integrado (IDE, Integrated Developed Environment). Con este acrónimo inglés se describe a toda aplicación
de sofware que proporciona una facilidad comprensiva a la hora de la creación de programas informáticos sea
en el lenguaje que sea. Normalmente un IDE consta de un editor de código fuente, un compliador y/o un intér-
prete, herramientas de creación y un depurador. También suelen tener consolas interactivas. En esta sección nos
centraremos en tres IDEs en particular: IDLE, Spyder y Eclipse.
IDLE: es el IDE de Python basado en el entorno de ventantas thinter y desarrollado 100 % también en
Python. Es el más simple de los tres que veremos pero para principiantes quizás sea el más adecuado. Posee
modo depuración aunque para programadores experimentados se queda bastante corto. La instalación en
Windows se realiza con la instalación del propio lenguaje mientras que en el resto de SO la instalación se
realiza de la forma usual. Para más información podemos consultar la documentación oficial en el siguiente
enlace: http://docs.python.org/2/library/idle.html. En la Figura 3 se muestra un ejemplo de creación de un
array en la consola de IDLE.
Spyder: Spyder es un IDE específico para Python orientado a científicos y desarrollo ingenieril con una
interfaz muy similar a Matlab, por lo que es la opción más adecuada cuando se quiere migrar de Matlab
a Python. Si hemos instalado Python(x,y), al iniciar el programa se nos abre una ventana con diversas
caracterísitcas. Dentro de la pestaña “Shortcuts” nos encontramos el apartado “Spyder”. Bastará con pinchar
en el símbolo del programa (Figura 4) e inmediatamente se nos abrira el programa. Como siempre, si nos
encontramos en linux abrimos una terminal y ejecutamos la orden
sudo apt-get install spyder

y tras unos segundos nos aparecerá como opción en nuestro menu.


Al iniciar el programa por primera vez vemos que, si hemos sido usuarios de MATLAB, efectivamente nos encon-
tramos en un entorno familiar. En la Figura 5 vemos que en la zona izquierda de la pantalla tenemos un editor de
código. En la zona central nos encontramos un navegador con nuestros programas y en la zona derecha tenemos
la ayuda y la consola interactiva.
Para más información podemos consultar el manual oficial del IDE en http://packages.python.org/spyder/
Eclipse: Eclipse es un programa informático compuesto por un conjunto de herramientas de programación
de código abierto multiplataforma para desarrollar lo que el proyecto llama “Aplicaciones de Cliente Enri-
quecido”, opuesto a las aplicaciones “Cliente-liviano” basadas en navegadores. Esta plataforma, típicamente
ha sido usada para desarrollar IDEs, como el IDE de Java llamado Java Development Toolkit (JDT). Eclipse
puede ser usado con Python mediante la instalación de Pydev. Pydev es un IDE para Eclipse muy com-
pleto y configurable con un modo de depuración extraordinariamente potente. De los tres IDEs que hemos
visto, éste es el entorno más complicado por la cantidad de opciones que contiene. Sin embargo para el
programador avanzado será el que más comodo le resulte a la hora de realizar sus programas.
Si nos encontramos en el sistema operativo de Microsoft, lo primero que debemos hacer es decargarnos la ultima
versión del programa en el sitio oficial, http://www.eclipse.org/downloads/. En linux,
sudo apt-get install eclipse

Tanto en linux como en Windows si no los tenemos debemos instalar la ultima versión de Java Development Kit
(JDK) y el Java Runtime Environment (JRE). Para configurar PyDev en Eclipse debemos de seguir los siguientes
pasos:

2.4. IDEs 11
PIMCD2013-python, Publicación 1.0

Figura 2.2: Figura 3. Consola de IDLE.

12 Capítulo 2. Puesta en Marcha


PIMCD2013-python, Publicación 1.0

Figura 2.3: Figura 4. Spyder en Python(X,Y). Para iniciar el programa pinchamos en el símbolo resaltado en la
imagen.

2.4. IDEs 13
PIMCD2013-python, Publicación 1.0

Figura 2.4: Figura 5. Spyder con sus diferentes secciones.

1. Abrimos Eclipse y nos vamos a Help -> Install New Software. En la pantalla emergente añadimos
“http://pydev.org/updates” en el campo work with.
2. Pulsamos en Add, esperamos a que actualize el campo de abajo para poder seleccionar PyDev, luego pulsa-
mos en Next, y seguimos el asistente para completar la instalación.
3. Reiniciamos Eclipse.
4. Ahora deberemos especificar dónde se encuentra el interprete de python en las preferencias Eclipse, para
ello nos vamos a Eclipse -> preferences -> PyDev -> Interpreter –> Python. Pulsamos en auto Config,
marcamos todo (sino lo está) y aceptamos.
En este caso también tenemos a nuestra disposición un manual oficial muy completo en
http://pydev.org/manual_101_root.html. Si hemos realizado todos los pasos correctamente podremos ejecu-
tar programas realizados en python sin ningún problema. En la Figura [figEclipse] podemos ver un ejemplo de la
disposición de las secciones en Eclipse. Estas seccciones son totalmente configurables por lo que dependiendo de
vuestras preferencias la imagen puede o no coincidir con vuestra disposición.

2.5 Pythonpath

Dependiendo el IDE elegido se nos habrá creado una carpeta como directorio de trabajo. Sin embargo en ocasiones
puede interesarnos trabajar con archivos fuera de este directorio. Para ello debemos de añadir la nueva ruta de
nuestros archivos. Esta operación se realiza en Pythonpath.
Spyder:
Tools –> PYTHONPATH manager.
Se busca el directorio donde tenemos los programas.
Eclipse:

14 Capítulo 2. Puesta en Marcha


PIMCD2013-python, Publicación 1.0

Figura 2.5: Figura 6. Instalacion de Pydev en Eclipse.

Project –> Properties –>Pydev - PYTHONPATH.


Se busca el directorio donde tenemos los programas.

2.6 Ayudas

Las comandos de los lenguajes de programación son prácticamente infinitos y, sólo mediante práctica, consegui-
remos aprendernos algunos de ellos. Sin embargo, en la mayoría de los casos debereos acudir a la documentación
oficial para resolver nuestras dudas. Normalmente cada lenguaje de programación tiene algún comando específico
para acceder a la información que nos interese. Además muchos de los IDE facilitan el acceso a esta información
mediante algún acceso directo o alguna ventana permanente. En general los pasos a seguir cuando no sabemos
qué comando utilizar o la sintaxis de su utilización son los siguientes.
Google. El buscador de información en internet por excelencia. En algunas ocasiones no sabremos muy
bien por donde empezar o nos sabremos el comando equivalente en otro lenguaje de programación. Si el
lector ha tenido esa duda seguramente otra persona la haya tenido antes por lo que podremos encontrar la
respuesta sin demasiado esfuerzo.
Mediante el uso de comandos especificos. Como hemos mencionado, cada lenguaje de programación tiene
una serie de comandos para accecer a la información de los comandos acerca de los cuales tengamos dudas.
Por ejemplo si en una consola tecleamos
import numpy as np

help(np.linspace)

obtendremos la información de linspace en la misma consola,


Help on function linspace in module numpy.core.function_base:

2.6. Ayudas 15
PIMCD2013-python, Publicación 1.0

Figura 2.6: Figura 7. Instalacion de Pydev en Eclipse.

16 Capítulo 2. Puesta en Marcha


PIMCD2013-python, Publicación 1.0

Figura 2.7: Figura 8. Instalación de Pydev en Eclipse.

linspace(start, stop, num=50, endpoint=True, retstep=False)


Return evenly spaced numbers over a specified interval.

Returns ‘num‘ evenly spaced samples, calculated over the


interval [‘start‘, ‘stop‘ ].

The endpoint of the interval can optionally be excluded.

Parameters
----------
start : scalar
The starting value of the sequence.
stop : scalar
The end value of the sequence, unless ‘endpoint‘ is set to False.
In that case, the sequence consists of all but the last of ‘‘num + 1‘‘
evenly spaced samples, so that ‘stop‘ is excluded. Note that the step
size changes when ‘endpoint‘ is False.
num : int, optional
Number of samples to generate. Default is 50.
endpoint : bool, optional
If True, ‘stop‘ is the last sample. Otherwise, it is not included.
Default is True.
retstep : bool, optional
If True, return (‘samples‘, ‘step‘), where ‘step‘ is the spacing
between samples.

Returns
-------
samples : ndarray
There are ‘num‘ equally spaced samples in the closed interval
‘‘[start, stop]‘‘ or the half-open interval ‘‘[start, stop)‘‘
(depending on whether ‘endpoint‘ is True or False).
step : float (only if ‘retstep‘ is True)

2.6. Ayudas 17
PIMCD2013-python, Publicación 1.0

Figura 2.8: Figura 9. Eclipse con Pydev configurado.

18 Capítulo 2. Puesta en Marcha


PIMCD2013-python, Publicación 1.0

Figura 2.9: Figura 10. Carga de los directorios en el Pythonpath, para eclipse.

2.6. Ayudas 19
PIMCD2013-python, Publicación 1.0

Size of spacing between samples.

See Also
--------
arange : Similiar to ‘linspace‘, but uses a step size (instead of the
number of samples).
logspace : Samples uniformly distributed in log space.

Examples
--------
>>> np.linspace(2.0, 3.0, num=5)
array([ 2. , 2.25, 2.5 , 2.75, 3. ])
>>> np.linspace(2.0, 3.0, num=5, endpoint=False)
array([ 2. , 2.2, 2.4, 2.6, 2.8])
>>> np.linspace(2.0, 3.0, num=5, retstep=True)
(array([ 2. , 2.25, 2.5 , 2.75, 3. ]), 0.25)

Graphical illustration:

>>> import matplotlib.pyplot as plt


>>> N = 8
>>> y = np.zeros(N)
>>> x1 = np.linspace(0, 10, N, endpoint=True)
>>> x2 = np.linspace(0, 10, N, endpoint=False)
>>> plt.plot(x1, y, ’o’)
[<matplotlib.lines.Line2D object at 0x...>]
>>> plt.plot(x2, y + 0.5, ’o’)
[<matplotlib.lines.Line2D object at 0x...>]
>>> plt.ylim([-0.5, 1])
(-0.5, 1)
>>> plt.show()

En el caso concreto del IDE Spyder tenemos el Object inspector. Si escribimos en la caja object la palabra
que deseamos probablemente aparezca la ayuda de forma automática. Por ejemplo, si quiero ordenar una
lista y no se cómo, podemos escribir “sort” y aparecerá la forma de utilización. Además tanto en spyder
como en Eclipse, al escribir el comando nos aparece la ayuda de forma automática como podemos ver en la
Figura 11

2.6.1 Mi primer programa

Por tradición se suele decir que un lenguaje de programación es más o menos complicado cuanto más nos cuesta
implementar el Hola Mundo, es decir, imprimir en pantalla estas dos palabras. Python teniendo en cuenta este
criterio es de los lenguajes más fáciles ya que con una sola línea de código podemos obtener el resultado deseado
>>> print("Hola Mundo")
Hola Mundo

si comparamos con C++,


#include <iostream>

using namespace std;

int main() {

cout << "Hola Mundo" << endl;

return 0;

20 Capítulo 2. Puesta en Marcha


PIMCD2013-python, Publicación 1.0

Figura 2.10: Figura 10. Ayuda automática u Object inspector en Spyder.

2.6. Ayudas 21
PIMCD2013-python, Publicación 1.0

vemos que el código es notablemente más largo. Por curiosidad y a modo de avance para el tema siguiente podemos
ver como se representa una gráfica simple como la del seno en Python,
#!/usr/bin/env python
# -*- coding: utf-8 -*-

# Carga de los módulos necesarios


import scipy as sp
import matplotlib.pyplot as plt

# Creamos el array x de cero a cien con cien puntos


x = sp.linspace(0, 10, 100)

# Creamos el array y conde cada punto es el seno de cada elemento de x


y = sp.sin(x)

# Creamos una figura


plt.figure()

# Representamos
plt.plot(x,y)

# Mostramos en pantalla
plt.show()

1.0

0.5

0.0

0.5

1.00 2 4 6 8 10

2.6.2 Estructura de un programa .py

En la sección anterior hemos podido comprobar lo sencillo y legible que es un programa Python. No obstante,
con el fin de que el código sea lo más comprensible posible para cualquiera que se disponga a utilizarlo o modi-
ficarlo se suelen añadir por convenio una serie de apartados y líneas de códico complementarias que facilitan su
comprensión. Así, como normal general todos nuestros programas de Python deben tener los siguientes apartados

22 Capítulo 2. Puesta en Marcha


PIMCD2013-python, Publicación 1.0

2.7 Autoejecución
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from __future__ import division

La primera línea de código sirve para decir al sistema operativo que este archivo se puede ejecutar, es decir,
si se hace click sobre el archivo, se llama al programa python y se ejecuta. Es el equivalente a los archivos
ejecutables .exe en Windows, aunque aquí el programa no está compilado, sino que se interpreta (esto último
es más lento).
La segunda línea de código sirve para poder utilizar la codificacion utf-8. De esta forma podemos poner
acentos, ñ, etc.
La tercera línea de código carga un módulo adicional que permite que la división entre números enteros
pueda dar números fraccionarios. Al ser Python un lenguaje de propósito general la división entre enteros da
lugar a enteros, lo cual suele ser problematico en cálculos científicos. NOTA: También se puede solucionar
escribiendo, en lugar de 3/4, 3./4, pero de esta forma el riesgo de cometer errores por esta causa es menor.

2.8 Cabecera
# Nombre: ejemplo_estructura_programa.py
# Propósito: Ejemplo de archivo con la estructura de un programa
#
# Origen: Propio
# Autor: José María Herrera-Fernandez y Luis Miguel Sanchez Brea
#
# Creación: 12 de Septiembre de 2013
# Historia:
#
# Dependencias: scipy, matplotlib
# Licencia: GPL

Esta sección es estrictamente voluntaria, sin embargo es muy útil cuando varios usuarios programan en el mismo
archivo.
Nombre –> El nombre del archivo que vamos a crear cuando guardemos por primera vez. En nuestro caso
ejemplo_estructura_programa.py.
Propósito –> La finalizad que se persigue al crear el programa, * Crear gráfica del seno*, * Calcular los
coeficientes de un polinomio*, etc.
Origen –> Si el origen del programa no es propio conviene saber su procedencia por si se debe consultar de
nuevo la fuente original en un futuro.
Autor(es) –> Los nombres de los creadores del programa.
Creación –> Fecha de creación del programa.
Historia –> En caso de modificaciónes posteriores es en esta seción donde se indica la modificación, la fecha
de la realización y el autor de la misma.
Dependencias –> Módulos necesarios para la ejecución del programa. Por ejemplo scipy, matplotlib..
Licencia –> Apartado correspondiente a los derechos de autor.
Como se observa, para incluir un comentario se utiliza el caracter #

2.7. Autoejecución 23
PIMCD2013-python, Publicación 1.0

2.9 Generación de ayudas

En el apartado de Ayudas (2.2.4) aprendimos a utilizar el comando help para obtener información sobre el uso de
un determinado comando. El resultado es una información muy valiosa que nosotros debemos de añadir también
a nuestras funciones, programas etc. Es lo que se denomina Docstring. Las ayudas se deben de incluir inmediata-
mente despúes de la cabecera en el caso de ser una descripción general e inmediantamente después de cualquier
función, clase, etc. La metodología es simple, todo aquella información que creamos necesaria para el Docstring
debe de ir encerrada entre seis pares de comillas dobles (tres al inicio y tres al final). Además para funciones
deberemos de incluir los siguientes apartados:
(Entradas) –> (salidas). Se enumeran el tipo de entrados que necesita como entradas la función y el tipo de
salidas que nos devolverá, int, bool, float, etc
Descripción. Ecplicación de la función.
Explicación de cada entrada.
Explicación de cada salida.
Test. Ejemplo sencillo con el resultado que pueda servir como comprobador del buen uso de la función
creada.
Ejemplo de ayuda para un archivo general,
"""
Descripción: Archivo plantilla para generar programas en Python.

"""

Aunque todavía no hemos visto las funciones podemos ver un ejemplo de ayuda en una función que nos devuelve
el seno del número que entremos,
def f1(x = 3.3):
"""
* (float) --> (float)
* Descripción: Función que devuelve el seno de un entero dado.
* Entradas:
- x = entero.
* Salidas:
- y = seno(entero)
* Test:
>>> print("f1(0.7)")
0.64421768723769102
"""
y = sp.sin(x)
return y

2.10 Carga de Módulos

En Python no es necesario declarar las variables, lo cual supone una gran simplificación respecto a programas
como C. Sin embargo para llamar a funciones y clases que no están en el archivo que se está ejecutando, es
necesario decirle dónde están. Se pueden declarar un módulo, o llamar a sus funciones de diversas formas. En los
siguientes ejemplos cargamos algunas o todas las funciones del módulo científico scipy.
import scipy
import scipy as sp
from scipy import *
from scipy import sin
from scipy.fftpack import fft
import matplotlib.pyplot as plt

Veamos como llamaríamos a la funcion sin dependiendo la forma en la que hayamos cargado scipy

24 Capítulo 2. Puesta en Marcha


PIMCD2013-python, Publicación 1.0

import scipy –> “y = scipy.sin(x)”, Importamos scipy y llamamos a las funciones que tiene.
import scipy as sp –> “y = sp.sin(x)”, Renombramos scipy con un alias más corto.
from scipy import * –> “y = sin(x)”, importamos todas las funciones que contiene scipy
from scipy import sin, cos –> “y = sin(x)”, importamos únicamente sin y cos
from scipy.fftpack import fft –> Si los módulos son muy largos, se pueden hacer en varios archivos. Entonces
las funciones o clases están anidadas.
Esto es de gran utilidad por varios motivos:
Permite no estar pendiente del nombre de las funciones, clases, etc. Si el nombre se repite en dos módulos,
no hay problema, pues sabemos a qué módulo pertenece
Permite escribir código de forma modular. Para utilizar en módulo desarrollado por otro programado, por
ejemplo optica.py, lo único que hay que hacer es llamarlo antes, “import optica”

2.11 Definición de funciones

Seguidamente a las declaraciones de los módulos se pueden escribir las funciones y clases que vayamos a utilizar.
Esto no significa que el programa vaya a realizar ninguna acción. En el siguiente tema se verá cómo hay que
desarrollar el código para crear estas funciones y clases.
Una ventaja de Python es que en un mismo programa (o módulo, según definamos) podemos escribir muchas
funciones y clases a la vez, por lo que se simplifica la revisión de código y su mantenimiento. Asimismo, incluir
varias funciones en un mismo archivo permite poder pasar nuestro código a otros programadores de una forma
sencilla.
def f1(x = 3.3):
"""
* (float) --> (float)
* Descripción: Función que devuelve el seno de un entero dado.
* Entradas:
- x = entero.
* Salidas:
- y = seno(entero)
* Test:
>>> print("f1(0.7)")
0.64421768723769102
"""
y = sp.sin(x)
return y

def f2(x=3):
"""
* (float) --> (float)
* Descripción: Función que devuelve el seno de un entero dado.
* Entradas:
- x = entero.
* Salidas:
- y = cos(entero)
* Test:
>> print("f2(0.7)")
0.7648421872844885
"""
y = sp.cos(x)
return y

Python no utiliza paréntesis o llaves para decir qué código pertenece a una función. Es de esencial importancia
en Python seguir un sistema de indentación. Es decir, el código que está dentro de una función tiene que estár

2.11. Definición de funciones 25


PIMCD2013-python, Publicación 1.0

indentado dentro de la función como en el ejemplo anterior para que pertenezca a la función. Para salirnos de la
función, simplemente “des-indentamos” el código.

2.12 Código ejecutable: main

A una función de Python se la puede llamar de dos formas: a través de otra función python o directamente. El
comportamiento de la función puede ser distinto por lo que se debe de tener cuidado. Si, por ejemplo, tenemos el
siguiente codigo al final del archivo:
if __name__ == ’__main__’:

# Creamos el array x
x = sp.linspace(0,10,100)

# Calculamos el seno y el coseno mediante las funciones creadas.


y1 = f1(x)
y2 = f2(x)

# Dibujamos las dos funciones en una única figura.


plt.figure()
plt.plot(x,y1,’k’, label=’seno’)
plt.hold(True)
plt.plot(x,y2,’r’, label=’coseno’)
plt.show()

Llegados a este punto podemos ver nuestra programa completo que no servirá de plantilla para el resto de progra-
mas que creemos,
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from __future__ import division

#----------------------------------------------------------------------
# Nombre: ejemplo_estructura_programa.py
# Propósito: Ejemplo de archivo con la estructura de un programa
#
# Origen: Propio
# Autor: José María Herrera-Fernandez y Luis Miguel Sanchez Brea
#
# Creación: 12 de Septiembre de 2013
# Historia:
#
# Dependencias: scipy, matplotlib
# Licencia: GPL
#----------------------------------------------------------------------

"""
Descripción: Archivo plantilla para generar programas en Python.

"""

import scipy
import scipy as sp
from scipy import *
from scipy import sin
from scipy.fftpack import fft
import matplotlib.pyplot as plt

26 Capítulo 2. Puesta en Marcha


PIMCD2013-python, Publicación 1.0

def f1(x = 3.3):


"""
* (float) --> (float)
* Descripción: Función que devuelve el seno de un entero dado.
* Entradas:
- x = entero.
* Salidas:
- y = seno(entero)
* Test:
>>> print("f1(0.7)")
0.64421768723769102
"""
y = sp.sin(x)
return y

def f2(x=3):
"""
* (float) --> (float)
* Descripción: Función que devuelve el seno de un entero dado.
* Entradas:
- x = entero.
* Salidas:
- y = cos(entero)
* Test:
>> print("f2(0.7)")
0.7648421872844885
"""
y = sp.cos(x)
return y

if __name__ == ’__main__’:

# Creamos el array x
x = sp.linspace(0,10,100)

# Calculamos el seno y el coseno mediante las funciones creadas.


y1 = f1(x)
y2 = f2(x)

# Dibujamos las dos funciones en una única figura.


plt.figure()
plt.plot(x,y1,’k’, label=’seno’)
plt.hold(True)
plt.plot(x,y2,’r’, label=’coseno’)
plt.show()

Ejecutando,

2.12. Código ejecutable: main 27


PIMCD2013-python, Publicación 1.0

1.0

0.5

0.0

0.5

1.00 2 4 6 8 10

2.13 Sistema de verificación del código

En el apartado de generación de ayudas vimos que era conveniente añadir un apartado de verificación de la la fun-
ción que llamamos Test. Sin embargo, en ocasiones las funciones son demasiado grades para añadir este apartado.
En estas ocasiones la solución es simple, nos creamos otra función que invoque a la primera proporcionando datos
conocidos. De esta forma continuaremos probando el buen funcionamiento de nuestra función sin el inconveniente
del espacio.

28 Capítulo 2. Puesta en Marcha


CAPÍTULO 3

Lenguajes de Programación

Autores José María Herrera Fernández, Luis Miguel Sánchez Brea

Contenidos de este capítulo:


Características de programación
• Elementos del lenguaje
• Comentarios
• Variables
• Tipos de datos
• Listas y Tuplas
• Diccionarios
• Conjuntos
• Listas por comprensión
• Funciones
• Clases
• Condicionales
• Bucle for
• Bucle while
• Módulos
• Sistema de objetos

Existen numerosos manuales de programación, además de magnificos y extensos manuales oficiales. Sirva de
ejemplo, http://pyspanishdoc.sourceforge.net/tut/tut.html, una amplia guía en español que a pesar de ser escrita en
2005 nos proporcionará toda la informacíón que necesitamos. Nosotros en esta sección nos limitaremos a aportar
un resumen tomado de la wikipedia para que el lector pueda seguir el resto del libro de forma cómoda aunque
recomendamos visitar tanto la fuente original de la información, http://es.wikipedia.org/wiki/Python, como la guía
que acabamos de mencionar.

3.1 Características de programación

No hace falta definir los datos.


Las funciones se almacenan en módulos, que pueden ser del tamaño que deseemos.
Es necesario declarar los modulos y las funciones de otros módulos que se van a implementar.
Hay infinidad de módulos de cualquier aŕea de interés.

3.1.1 Elementos del lenguaje

Python fue diseñado para ser leído con facilidad. Una de sus características es el uso de palabras donde otros
lenguajes utilizarían símbolos. Por ejemplo, los operadores lógicos !, || y && en Python se escriben not, or y and,

29
PIMCD2013-python, Publicación 1.0

respectivamente.
El contenido de los bloques de código (bucles, funciones, clases, etc.) es delimitado mediante espacios o tabula-
dores, conocidos como indentación, antes de cada línea de órdenes pertenecientes al bloque. Python se diferencia
así de otros lenguajes de programación que mantienen como costumbre declarar los bloques mediante un conjunto
de caracteres, normalmente entre llaves {}. Se pueden utilizar tanto espacios como tabuladores para identar el
código, pero se recomienda no mezclarlos.
Debido al significado sintáctico de la indentación, una instrucción debe estar contenida en línea. No obstante, si
por legibilidad se quiere dividir la instrucción en varias líneas, añadiendo una barra invertida al final de una línea,
se indica que la instrucción continúa en la siguiente.
lista = [’valor 1’,’valor 2’,’valor 3’]
cadena = ’Esto es una cadena bastante larga’

que es equivalente a
lista = [’valor 1’,’valor 2’ \
,’valor 3’]
cadena = ’Esto es una cadena’ \
’bastante larga’

3.1.2 Comentarios

Los comentarios se pueden poner de dos formas. La primera y más apropiada para comentarios largos es utilizando
la notación ‘” comentario ‘’‘, tres apostrofes de apertura y tres de cierre.
’’’
Comentario más largo en una línea en Python
’’’

La segunda notación utiliza el símbolo # delante de la línea a comentar,


print "Hola mundo" # También es posible añadir un comentario al final de una línea de código

El intérprete no tiene en cuenta los comentarios, lo cual es útil si deseamos poner información adicional en nuestro
código como, por ejemplo, una explicación sobre el comportamiento de una sección del programa.

3.1.3 Variables

Las variables se definen de forma dinámica, lo que significa que no se tiene que especificar cuál es su tipo de
antemano y puede tomar distintos valores en otro momento, incluso de un tipo diferente al que tenía previamente.
Se usa el símbolo = para asignar valores.
x = 1
x = "Esta variable es una cadena" # Esto es posible porque los tipos son asignados dinámicamente.

3.1.4 Tipos de datos

Los tipos de datos se pueden resumir en esta tabla:

30 Capítulo 3. Lenguajes de Programación


PIMCD2013-python, Publicación 1.0

Tipo Clase Notas Ejemplo


str Cadena Inmutable ‘Cadena’
unicode Cadena Versión Unicode de str u’Cadena’
list Secuencia Mutable, puede contener objetos de diversos [4.0, ‘Cadena’, True]
tipos
tuple Secuencia Inmutable, puede contener objetos de diversos (4.0, ‘Cadena’, True)
tipos
set Conjunto Mutable, sin orden, no contiene duplicados set([4.0, ‘Cadena’, True])
frozen- Conjunto Inmutable, sin orden, no contiene duplicados frozenset([4.0, ‘Cadena’,
set True])
dict Mapping Grupo de pares clave:valor {‘key1’: 1.0, ‘key2’: False}
int Número entero Precisión fija, convertido en long en caso de 42
overflow.
long Número entero Precisión arbitraria 42L ó
456966786151987643L
float Número Coma flotante de doble precisión 3.1415927
decimal
com- Número Parte real y parte imaginaria j. (4.5 + 3j)
plex complejo
bool Booleano Valor booleano verdadero o falso True o False
Mutable: si su contenido (o dicho valor) puede cambiarse en tiempo de ejecución.
Inmutable: si su contenido (o dicho valor) no puede cambiarse en tiempo de ejecución.

3.1.5 Listas y Tuplas

Para declarar una lista se usan los corchetes [ ], en cambio, para declarar una tupla se usan los paréntesis (
). En ambas los elementos se separan por comas, y en el caso de las tuplas es necesario que tengan como
mínimo una coma.
Tanto las listas como las tuplas pueden contener elementos de diferentes tipos. No obstante las listas suelen
usarse para elementos del mismo tipo en cantidad variable mientras que las tuplas se reservan para elementos
distintos en cantidad fija.
Para acceder a los elementos de una lista o tupla se utiliza un índice entero (empezando por “0”, no por “1”).
Se pueden utilizar índices negativos para acceder elementos a partir del final, por ejemplo para acceder al
último índice deberíamos de usar “-1”.
Las listas se caracterizan por ser mutables, es decir, se puede cambiar su contenido en tiempo de ejecución,
mientras que las tuplas son inmutables ya que no es posible modificar el contenido una vez creada.
Listas
>>> lista = ["abc", 42, 3.1415] # Definición de una lista con elementos de distinto tipo.
>>> lista[0] # Acceder a un elemento por su índice, en este caso al primero.
’abc’
>>> lista[-1] # Acceder a un elemento usando un índice negativo, en este caso el último.
3.1415
>>> lista.append(True) # Añadir un elemento al final de la lista.
>>> lista
[’abc’, 42, 3.1415, True]
>>> del lista[3] # Borra un elemento de la lista usando un índice (en este caso: True).
>>> lista
[’abc’, 42, 3.1415]
>>> lista[0] = "xyz" # Re-asignar el valor del primer elemento de la lista.
>>> lista
[’xyz’, 42, 3.1415]
>>> lista[0:2] # Mostrar los elementos de la lista del índice "0" al "2" (sin incluir este último)
[’xyz’, 42]
>>> lista_anidada = [lista, [True, 42L]] # Es posible anidar listas
>>> lista_anidada

3.1. Características de programación 31


PIMCD2013-python, Publicación 1.0

[[’xyz’, 42, 3.1415], [True, 42L]]


>>> lista_anidada[1][0] # Acceder a un elemento de una lista dentro de otra lista (del segundo ele
True

Tuplas
>>> tupla = ("abc", 42, 3.1415) # Definición de una tupla con elementos de distinto tipo.
>>> tupla[0] # Acceder a un elemento por su índice en este caso al primero.
’abc’
>>> del tupla[0] # No es posible borrar (ni añadir) un elemento en una tupla, lo que provocará una
( Excepción )
# Tampoco es posible re-asignar el valor de un elemento en una tupla, lo que también provocará una
>>> tupla[0] = "xyz"
( Excepción )
>>> tupla[0:2] # Mostrar los elementos de la tupla del índice "0" al "2" (sin incluir este último)
(’abc’, 42)
>>> tupla_anidada = (tupla, (True, 3.1415)) # También es posible anidar tuplas.
>>> 1, 2, 3, "abc" # Esto también es una tupla, aunque es recomendable ponerla entre paréntesis (
(1, 2, 3, ’abc’)
>>> (1) # Aunque entre paréntesis, esto no es una tupla, ya que no posee al menos una coma, por lo
1
>>> (1,) # En cambio, en este otro caso, sí es una tupla.
(1,)
>>> (1, 2) # Con más de un elemento no es necesaria la coma final.
(1, 2)
>>> (1, 2,) # Aunque agregarla no modifica el resultado.
(1, 2)

3.1.6 Diccionarios

Para declarar un diccionario se usan las llaves { }. Contienen elementos separados por comas, donde cada
elemento está formado por un par clave : valor (el símbolo : separa la clave de su valor correspondiente).
Los diccionarios son mutables, es decir, se puede cambiar el contenido de un valor en tiempo de ejecución.
En cambio, las claves de un diccionario deben ser inmutables. Esto quiere decir, por ejemplo, que no podre-
mos usar ni listas ni diccionarios como claves.
El valor asociado a una clave puede ser de cualquier tipo de dato, incluso un diccionario.
>>> diccionario = {"cadena": "abc", "numero": 42, "lista": [True, 42L]} # Diccionario que tiene di
>>> diccionario["cadena"] # Usando una clave, se accede a su valor.
’abc’
>>> diccionario["lista"][0] # Acceder a un elemento de una lista dentro de un valor (del valor de
True
>>> diccionario["cadena"] = "xyz" # Re-asignar el valor de una clave.
>>> diccionario["cadena"]
’xyz’
>>> diccionario["decimal"] = 3.1415927 # Insertar un nuevo elemento clave:valor.
>>> diccionario["decimal"]
3.1415927
>>> diccionario_mixto = {"tupla": (True, 3.1415), "diccionario": diccionario} # También es posible
>>> diccionario_mixto["diccionario"]["lista"][1] # Acceder a un elemento dentro de una lista, que
42L
>>> diccionario = {("abc",): 42} # Sí es posible que una clave sea una tupla, pues es inmutable.
>>> diccionario = {["abc"]: 42} # No es posible que una clave sea una lista, pues es mutable, lo
( Excepción )

32 Capítulo 3. Lenguajes de Programación


PIMCD2013-python, Publicación 1.0

3.1.7 Conjuntos

Los conjuntos se construyen mediante set(items) donde items es cualquier objeto iterable, como listas o
tuplas. Los conjuntos no mantienen el orden ni contienen elementos duplicados.
Se suelen utilizar para eliminar duplicados de una secuencia, o para operaciones matemáticas como inter-
sección, unión, diferencia y diferencia simétrica.
>>> conjunto_inmutable = frozenset(["a", "b", "a"]) # Se utiliza una lista como objeto iterable.
>>> conjunto_inmutable
frozenset([’a’, ’b’])
>>> conjunto1 = set(["a", "b", "a"]) # Primer conjunto mutable.
>>> conjunto1
set([’a’, ’b’])
>>> conjunto2 = set(["a", "b", "c", "d"]) # Segundo conjunto mutable.
>>> conjunto2
set([’a’, ’c’, ’b’, ’d’]) # Recuerda, no mantienen el orden, como los diccionarios.
>>> conjunto1 & conjunto2 # Intersección
set([’a’, ’b’])
>>> conjunto1 | conjunto2 # Unión.
set([’a’, ’c’, ’b’, ’d’])
>>> conjunto1 - conjunto2 # Diferencia (1).
set([])
>>> conjunto2 - conjunto1 # Diferencia (2).
set([’c’, ’d’])
>>> conjunto1 ^ conjunto2 # Diferencia simétrica.
set([’c’, ’d’])

3.1.8 Listas por comprensión

Una lista por comprensión (en inglés: list comprehension) es una expresión compacta para definir listas. Al igual
que lambda, aparece en lenguajes funcionales. Ejemplos:
>>> range(5) # La función "range" devuelve una lista, empezando en 0 y terminando con el número in
[0, 1, 2, 3, 4]
>>> [i*i for i in range(5)] # Por cada elemento del rango, lo multiplica por sí mismo y lo agrega
[0, 1, 4, 9, 16]
>>> lista = [(i, i + 2) for i in range(5)].
>>> lista
[(0, 2), (1, 3), (2, 4), (3, 5), (4, 6)]

3.1.9 Funciones

Las funciones se definen con la palabra clave def, seguida del nombre de la función y sus argumentos entre
paréntesis para finalizar con :, es decir,
>>> def mi_funcion(arg1, arg2,..., argN):

Otra forma de escribir funciones, aunque menos utilizada, es con la palabra clave lambda. lambda es un
cosntructor que sirver para ejecutar funciones anónimas, es decir funciones sin ningún nombre pero que
crean una referencia a un objeto función. La sintaxis de lambda en python es
lambda <aParameterList> : <a Python expression using the parameters>

El valor devuelto en las funciones con def será el dado con la instrucción return. lambda no necesita de re-
turn para retornar un valor, ya que está implícito, puesto que la función entera debe ser una única expresión.
def

3.1. Características de programación 33


PIMCD2013-python, Publicación 1.0

>>> def suma(x, y = 2):


... return x + y # Devuelve la suma del valor de la variable "x" y el valor de "y".
...
>>> suma(4) # La variable "y" no se modifica, siendo su valor: 2
6
>>> suma(4, 10) # La variable "y" sí se modifica, siendo su nuevo valor: 10
14

lambda
>>> suma = lambda x, y = 2: x + y
>>> suma(4) # La variable "y" no se modifica, siendo su valor: 2
6
>>> suma(4, 10) # La variable "y" sí se modifica, siendo su nuevo valor: 10
14

3.1.10 Clases

Las clases se definen con la palabra clave class, seguida del nombre de la clase y, si hereda de otra clase, el
nombre de esta.
En Python 2.x es recomendable que una clase herede de “object”, en Python 3.x esto ya no hará falta.
En una clase un “método” equivale a una “función”, y una “propiedad” equivale a una “variable”.
“__init__” es un método especial que se ejecuta al instanciar la clase, se usa generalmente para inicializar
propiedades y ejecutar métodos necesarios. Al igual que todos los métodos en Python, debe tener al menos
un parámetro, generalmente se utiliza self. El resto de parámetros serán los que se indiquen al instanciar la
clase.
Las propiedades que se desee que sean accesibles desde fuera de la clase se deben declarar usando self.
delante del nombre.
En python no existe el concepto de encapsulación, por lo que el programador debe ser responsable de asignar
los valores a las propiedades
>>> class Persona(object):
... def __init__(self, nombre, edad):
... self.nombre = nombre # Una Propiedad cualquiera.
... self.edad = edad # Otra propiedad cualquiera.
... def mostrar_edad(self): # Es necesario que, al menos, tenga un parámetro, generalmente: "s
... print self.edad # mostrando una propiedad.
... def modificar_edad(self, edad): # Modificando Edad.
... if edad < 0 or edad > 150: # Se comprueba que la edad no sea menor de 0 (algo imposibl
... return False
... else: # Si está en el rango 0-150, entonces se modifica la variable.
... self.edad = edad # Se modifica la edad.
...
>>> p = Persona("Alicia", 20) # Instanciamos la clase, como se puede ver, no se especifica el valo
>>> p.nombre # La variable "nombre" del objeto sí es accesible desde fuera.
’Alicia’
>>> p.nombre = "Andrea" # Y por tanto, se puede cambiar su contenido.
>>> p.nombre
’Andrea’
>>> p.mostrar_edad() # Podemos llamar a un método de la clase.
20
>>> p.modificar_edad(21) # Y podemos cambiar la edad usando el método específico que hemos hecho p
>>> p.mostrar_edad()
21

34 Capítulo 3. Lenguajes de Programación


PIMCD2013-python, Publicación 1.0

3.1.11 Condicionales

Una sentencia condicional if ejecuta su bloque de código interno sólo si se cumple cierta condición. Se define
usando la palabra clave if seguida de la condición, y el bloque de código. Condiciones adicionales, si las hay, se
introducen usando elif seguida de la condición y su bloque de código. Todas las condiciones se evalúan secuen-
cialmente hasta encontrar la primera que sea verdadera, y su bloque de código asociado es el único que se ejecuta.
Opcionalmente, puede haber un bloque final (la palabra clave else seguida de un bloque de código) que se ejecuta
sólo cuando todas las condiciones resultan falsas.
>>> verdadero = True
>>> if verdadero: # No es necesario poner "verdadero == True".
... print "Verdadero"
... else:
... print "Falso"
...
Verdadero
>>> lenguaje = "Python"
>>> if lenguaje == "C": # lenguaje no es "C", por lo que este bloque se obviará y evaluará la sigu
... print "Lenguaje de programación: C"
... elif lenguaje == "Python": # Se pueden añadir tantos bloques "elif" como se quiera.
... print "Lenguaje de programación: Python"
... else: # En caso de que ninguna de las anteriores condiciones fuera cierta, se ejecutaría este
... print "Lenguaje de programación: indefinido"
...
Lenguaje de programación: Python
>>> if verdadero and lenguaje == "Python": # Uso de "and" para comprobar que ambas condiciones son
... print "Verdadero y Lenguaje de programación: Python"
...
Verdadero y Lenguaje de programación: Python

3.1.12 Bucle for

El bucle for es similar a foreach en otros lenguajes. Recorre un objeto iterable, como una lista, una tupla o un
generador, y por cada elemento del iterable ejecuta el bloque de código interno. Se define con la palabra clave for
seguida de un nombre de variable, seguido de in, seguido del iterable, y finalmente el bloque de código interno.
En cada iteración, el elemento siguiente del iterable se asigna al nombre de variable especificado:
>>> lista = ["a", "b", "c"]
>>> for i in lista: # Iteramos sobre una lista, que es iterable.
... print i
...
a
b
c
>>> cadena = "abcdef"
>>> for i in cadena: # Iteramos sobre una cadena, que también es iterable.
... print i, # Añadiendo una coma al final hacemos que no introduzca un salto de línea, sino u
...
a b c d e f

3.1.13 Bucle while

El bucle while evalúa una condición y, si es verdadera, ejecuta el bloque de código interno. Continúa evaluando y
ejecutando mientras la condición sea verdadera. Se define con la palabra clave while seguida de la condición, y a
continuación el bloque de código interno:
>>> numero = 0
>>> while numero < 10:
... numero += 1
... print numero,

3.1. Características de programación 35


PIMCD2013-python, Publicación 1.0

...
1 2 3 4 5 6 7 8 9

3.1.14 Módulos

Existen muchas propiedades que se pueden agregar al lenguaje importando módulos, que son “minicódigos” (la
mayoría escritos también en Python) que proveen de ciertas funciones y clases para realizar determinadas tareas.
Un ejemplo es el módulo Tkinter, que permite crear interfaces gráficas basadas en la biblioteca Tk. Otro ejemplo es
el módulo os, que provee acceso a muchas funciones del sistema operativo. Los módulos se agregan a los códigos
escribiendo import seguida del nombre del módulo que queramos usar.
>>> import os # Módulo que provee funciones del sistema operativo
>>> os.name # Devuelve el nombre del sistema operativo
’posix’
>>> os.mkdir("/tmp/ejemplo") # Crea un directorio en la ruta especificada
>>> import time # Módulo para trabajar con fechas y horas
>>> time.strftime("%Y-%m-%d %H:%M:%S") # Dándole un cierto formato, devuelve la fecha y/o hora ac
’2010-08-10 18:01:17’

NOTA: En http://pyspanishdoc.sourceforge.net/modindex.html podemos encontrar el índice de módulos estándar


de python.

3.1.15 Sistema de objetos

En Python todo es un objeto (incluso las clases). Las clases, al ser objetos, son instancias de una metaclase. Python
además soporta herencia múltiple y polimorfismo.
>>> cadena = "abc" # Una cadena es un objeto de "str"
>>> cadena.upper() # Al ser un objeto, posee sus propios métodos
’ABC’
>>> lista = [True, 3.1415] # Una lista es un objeto de "list"
>>> lista.append(42L) # Una lista también (al igual que todo) es un objeto, y también posee sus pr
>>> lista
[True, 3.1415, 42L]

36 Capítulo 3. Lenguajes de Programación


CAPÍTULO 4

Módulos Científicos

Con los módulos generales, Phyton es dificilmente aplicable al entorno científico. Afortunadamente la comunidad
de desarrolladores pronto puso solución a este inconveniente mediante la creación de potentes módulos de cálculo
numérico. Hoy día existen módulos específicos de casi todas las áreas cientifico-ingenieriles. En este tema presen-
taremos brevemente algunos de los módulos más utilizados y explicaremos diversos ejemplos que nos ayudaran a
aprender su utilización de forma rápida e intuitiva.
.

4.1 Numpy

Autores José María Herrera Fernández, Luis Miguel Sánchez Brea

Contenidos de este capítulo:


Carga de un archivo de datos
Arrays
Matrices
Operaciones y funciones

Uno de los módulos más importantes de Python es Numpy. El origen de Numpy se debe principalmente al dise-
ñador de software Jim Hugunin quien diseñó el módulo Numeric para dotar a Python de capacidades de cálculo
similares a las de otros softwares como MATLAB. Posteriormente, mejoró Numeric incorporando nuevas funcio-
nalidades naciendo lo que hoy conocemos como Numpy.
Numpy es el encargado de añadir toda la capacidad matemática y vectorial a Python haciendo posible operar
con cualquier dato numérico o array (posteriormente veremos qué es un array). Incorpora operaciones tan básicas
como la suma o la multiplicación u otras mucho más complejas como la transformada de Fourier o el álgebra lineal.
Además incorpora herramientas que nos permiten incorporar código fuente de otros lenguajes de programación
como C/C++ o Fortran lo que incrementa notablemente su compatibilidad e implementación.
Para poder utilizar este módulo sino hemos instalado python(x,y) lo primero que debemos de hacer es descargarnos
la última versión desde el sitio oficial http://www.scipy.org/Download. En caso de que nuestro sistema operativo
sea Linux probablemente venga instalado dentro de Python. De no ser así debemos instalarlo por el método
habitual. Para más información y otros sistemas operativos consultar las notas oficiales de instalación. Para cargar
el módulo en Python y poder llamarlo de formar más cómoda es de uso extendido la utilización del alias np,
import numpy as np

4.1.1 Carga de un archivo de datos

En el ámbito científico es frecuente almacenar y trasladar paquetes de datos en archivos escritos en, por ejemplo,
código ASCII. Python incorpora varias funciones que nos permiten la lectura y escritura en estos archivos facili-

37
PIMCD2013-python, Publicación 1.0

tando su manejo. Un ejemplo de ello es el comando “loadtxt” que nos permite cargar los datos contenidos en un
archivo de texto o dat.
Ejemplo 4.1
>>> import numpy as np # Importamos numpy como el alias np
# Cargamos los datos de archivo.txt en datos. En este caso el delimitador es la coma.
>>> datos = numpy.loadtxt(’C:\ruta\a\tu\archivo.txt’, delimiter = ’,’)

Ejemplo 4.2
>>> import numpy as np # Importamos numpy como el alias np.
>>> archivoDatos = np.loadtxt(’codigo\archivoDatos.dat’)
>>> print (archivoDatos) # Presentamos en pantalla los datos cargados.

Ejemplo 4.3
>>> from StringIO import StringIO # StringIO se comporta como un archivo objeto.
>>> c = StringIO("0 1\n2 3")
>>> np.loadtxt(c)
array([[ 0., 1.],
[ 2., 3.]])

4.1.2 Arrays

Con toda probabilidad, el lector que haya realizado un acercamiento a cualquier lenguaje de programación habrá
oído hablar de arrays. Un array es el termino que traslada el concepto matemático de vector o matriz a la progra-
mación añadiéndole la noción de almacenamiento en memoria. Los arrays disponen en su interior de una serie de
elementos dispuestos en filas y/o columnas dependiendo de la dimensión.
El desarrollo y la principal finalidad del módulo Numpy es la creación y modificación de arrays multidimensio-
nales. Para este fin utilizaremos las clase ndarray del ingles N-dimensional array o usando su alias simplemente
array (no confundir con la clase array.array que ofrece menos funcionalidad). En Python cada clase puede tener
atributos que se pueden llamar con el método visto anteriormente o simplemente escribiendo a continuación de
la clase un punto y el atributo. En la mayoría de los IDEs al cargar la clase y escribir el punto aparecen todos los
atributos disponibles en orden alfabético por lo que en caso de dudar siempre podemos utilizar este método para
escribir el comando. En el caso de ndarray los principales atributos son los siguientes:
ndarray.ndim –> Proporciona el número de dimensiones de nuestro array. El array identidad es un array
cuadrado con una diagonal principal unitaria.
ndarray.shape –> Devuelve la dimensión del array, es decir, una tupla de enteros indicando el tamaño del
array en cada dimensión. Para una matriz de n filas y m columnas obtendremos (n,m).
ndarray.size –> Es el número total de elementos del array.
ndarray.dtype –> Es un objeto que describe el tipo de elementos del array.
ndarray.itemsize –> devuelve el tamaño del array en bytes.
ndarray.data –> El buffer contiene los elementos actuales del array.
Veamos un ejemplo de su uso con un array sencillo de diez elementos,
Ejemplo 4.4
>>> import numpy as np # Importamos numpy como el alias np
>>> miArray = np.arange(10) # Creamos un array de 0 a 9 separados de uno en uno
>>> miArray # Presentamos en pantalla el array creado
array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
>>> type(miArray) #Comprobamos que es un ndarray
<type ’numpy.ndarray’>
>>> miArray.ndim # Consultamos el número de dimensiones
1
>>> miArray.shape # Consultamos la dimensión

38 Capítulo 4. Módulos Científicos


PIMCD2013-python, Publicación 1.0

(10,)
>>> miArray.size # Consultamos la dimensión
10
>>> miArray.dtype # Consultamos el tipo de elementos del array
dtype(’int64’)
>>> miArray.itemsize # tamaño en bytes
8
>>> miArray.data # Consultamos el buffer de memoria.
<read-write buffer for 0x2fb57a0, size 80, offset 0 at 0x2f664b0>

Como hemos mencionando anteriormente, los arrays son unos de los elementos más utilizados por los progra-
madores bajo cualquier lenguaje. Este gran uso hace que dispongamos de una gran variedad de comandos para
la creación de arrays: arrays unidimensionales, multidimensionales, nulos, unitarios, secuenciales, de números
aleatorios, etc. Dependiendo de las necesidades de nuestro problema escogeremos la opción más adecuada.
identity(n,dtype). Devuelve la matriz identidad, es decir, uma matriz cuadrada nula excepto en su diagonal
principal que es unitaria. n es el número de filas (y columnas) que tendrá la matriz y dtype es el tipo de dato.
Este argumento es opcional. Si no se establece, se toma por defecto como flotante.
ones(shape,dtype). Crea un array de unos compuesto de shape elementos.
zeros(shape, dtype). Crea un array de ceros compuesto de “shape” elementos”.
empty(shape, dtype). Crea un array de ceros compuesto de “shape” elementos” sin entradas.
eye(N, M, k, dtype). Crea un array bidimensional con unos en la diagonal k y ceros en el resto. Es similar
a identity. Todos los argumentos son opcionales. N es el número de filas, M el de columnas y k es el índice
de la diagonal. Cuando k=0 nos referimos a la diagonal principal y por tanto eye es similar a identity.
arange([start,]stop[,step,],dtype=None). Crea un array con valores distanciados step entre el valor inicial
star y el valor final stop. Si no se establece step python establecerá uno por defecto.
linspace(start,stop,num,endpoint=True,retstep=False). Crea un array con valor inicial start, valor final
stop y num elementos.
meshgrid(x,y). Genera una matriz de coordenadas a partir de dos los arrays x, y.
En el siguiente ejemplo podemos ver su utilización,
Ejemplo 4.5
>>> from numpy import * # Importamos todo el módulo numpy.
>>> zeros( (3,4) ) # Creamos un array nulo de 3 filas y 4 columnas.
array([[0., 0., 0., 0.],
[0., 0., 0., 0.],
[0., 0., 0., 0.]])
>>> ones(9) # Creamos un array unitario de 1 fila y 9 columnas,
array([ 1., 1., 1., 1., 1., 1., 1., 1., 1.])
>>> empty( (2,3) ) # Creamos un array sin entradas de 2x3.
array([[ 3.73603959e-262, 6.02658058e-154, 6.55490914e-260],
[ 5.30498948e-313, 3.14673309e-307, 1.00000000e+000]])
>>> arange( 10, 30, 5 ) # Creamos un array con inicio en 10 final en 30 y paso de 5.
array([10, 15, 20, 25])
>>> linspace( 0, 2, 9 ) # Array de 9 elementos de 0 a 2
array([ 0. , 0.25, 0.5 , 0.75, 1. , 1.25, 1.5 , 1.75, 2. ])
>>> x = linspace( 0, 2*pi, 100 ) # Para gráficas conviene tener muchos puntos, en este caso 100
>>> X, Y = np.meshgrid([1,2,3], [4,5,6,7]) # Generamos una matriz de coordenadas.
>>> X
array([[1, 2, 3],
[1, 2, 3],
[1, 2, 3],
[1, 2, 3]])
>>> Y
array([[4, 4, 4],
[5, 5, 5],

4.1. Numpy 39
PIMCD2013-python, Publicación 1.0

[6, 6, 6],
[7, 7, 7]])

4.1.3 Matrices

Un subtipo especial de array bidimensional de NumPy es la matriz. Una matriz es como una array, excepto que la
multiplicación de matrices (y exponenciación) reemplaza la multiplicación elemento a elemento. Las matrices se
crean mediante la función matrix.
Ejemplo 4.6
>>> from numpy import matrix # Importamos matrix del módulo numpy.
>>> a = matrix([[1,3,-5],[3,4,2],[-5,2,0]])
matrix([[ 1, 3, -5],
[ 3, 4, 2],
[-5, 2, 0]])
>>> b = matrix([[1],[5],[3]])
>>> b
matrix([[1],
[5],
[3]])
>>> a*b
matrix([[ 1],
[29],
[ 5]])

Definidas las matrices, podemos hacer uso de todas las funciones relacionadas con el álgebra lineal como la
transposición, la diagonalización etc.
Ejemplo 4.7
>>> b.T # Calculamos la transpuesta de *b*.
matrix([[1, 5, 3]])
>>> b.H # Calculamos la hermítica (transpuesta y conjugada) de *b*.
matrix([[1, 5, 3]])
>>> c = a.I # Calculamos la inversa de *b*.
>>> c
matrix([[ 0.02439024, 0.06097561, -0.15853659],
[ 0.06097561, 0.15243902, 0.10365854],
[-0.15853659, 0.10365854, 0.0304878 ]])
>>> a*c # Multiplicamos las matrices *a* y *c*.
matrix([[ 1.00000000e+00, -5.55111512e-17, -6.93889390e-18],
[ 0.00000000e+00, 1.00000000e+00, 4.16333634e-17],
[ 0.00000000e+00, 0.00000000e+00, 1.00000000e+00]])

Con el paquete linalg podemos calcular el determinante (det), resolver ecuaciones lineales (solve) y calcular auto-
valores y autovectores.
Ejemplo 4.8
>>> linalg.det(a) # Cálculo del determinante.
-164.0
>>> d = linalg.solve(a,b) # Resolución de ecuaciones lineales.
>>> d
matrix([[-0.14634146],
[ 1.13414634],
[ 0.45121951]])
>>> a*d
matrix([[ 1.],
[ 5.],
[ 3.]])
>>> e = linalg.eig(a) # Cálculo de autovalores y autovectores.
>>> e[0]

40 Capítulo 4. Módulos Científicos


PIMCD2013-python, Publicación 1.0

array([-5.78304165, 6.23396835, 4.5490733 ])


>>> e[1]
matrix([[ 0.65072855, -0.7001856 , -0.29375583],
[-0.33849942, -0.61380708, 0.71320335],
[ 0.67968412, 0.3646656 , 0.6364342 ]])
>>> u = e[1]
>>> u.T*a*u
matrix([[ -5.78304165e+00, -1.22688318e-15, -7.92985469e-16],
[ -1.62554432e-15, 6.23396835e+00, 1.43223107e-15],
[ -7.68916181e-16, 1.83533744e-15, 4.54907330e+00]])

La función eig devuelve una tupla donde el primer elemento es un array de autovalores y el segundo es una matriz
que contiene a los autovectores.

4.1.4 Operaciones y funciones

El módulo numpy contiene todas las operaciones usuales entre arrays como las matemáticas (suma, resta, multi-
plicación, etc), las lógicas (and, or, xor, etc), binarias, ... Lo más fácil y sencillo es en caso de duda consultar la
documentación oficial. De particular interés en óptica es la transformada de Fourier,
Ejemplo 4.9
>>> import numpy as np # Importamos numpy como el alias np.
>>> x=np.linspace(0,1,16)
>>> y=np.sin(x)
>>> print np.fft.fft(y)
array([ 6.82834723+0.j , -0.72886897+1.98380961j,
-0.46469805+0.9337634j , -0.42166285+0.57663662j,
-0.40711196+0.38478538j, -0.40066563+0.25695308j,
-0.39747563+0.15924205j, -0.39594637+0.07645992j,
-0.39548834+0.j , -0.39594637-0.07645992j,
-0.39747563-0.15924205j, -0.40066563-0.25695308j,
-0.40711196-0.38478538j, -0.42166285-0.57663662j,
-0.46469805-0.9337634j , -0.72886897-1.98380961j])

obsérvese como se ha formado en número complejo 2j. En python los números complejos se forman (parte real
signo jparte imaginaria), es decir, separando parte real e imaginaria y sin ningún signo multiplicativo entre el
coeficiente y el numero imaginario j.
Ejemplo 4.10
>>> numero_complejo = (1 + 1j) # Defino el número complejo.
>>> print(numero_complejo) # Lo presento en pantalla.
(1+1j)
>>> type(numero_complejo) # Compruebo el tipo.
complex

4.2 Scipy

Autores José María Herrera Fernández, Luis Miguel Sánchez Brea

4.2. Scipy 41
PIMCD2013-python, Publicación 1.0

Contenidos de este capítulo:


Ejemplo: Encontrar mínimos de Intensidad de la difracción provocada por una rendija.
Ejemplo: Encontrar mínimos en un intervalo definido.
Ejemplo: Ajuste de una función.
Ejemplo: Función de Bessel de orden cero y uno.
Ejemplo: Cálculo de integrales
Ejemplo: Interpolación
Ejemplo: Cálculo de las raices de un polinomio

Scipy es una librería de herramientas numéricas para Python que se distribuye libremente. El módulo scipy con-
fiere al lenguaje general Python capacidades de cálculo numérico de gran capacidad. Scipy además de poseer en
su núcleo a Numpy, posee módulos para optimización de funciones, integración, funciones especiales, resolución
de ecuaciones diferenciales ordinarias y otros muchos aspectos.

Figura 4.1: Figura 1. Scipy.

Su organización se estructura en subpaquetes, que se pueden considerar especializados en dominios científicos


determinados. Podemos encontrar estos paquetes, según la ayuda de scipy:
linalg – Algebra lineal
signal – Procesamiento de señales
stats – Funciones estadísticas
special – Funciones especiales
integrate – Integración
interpolate – Herramientas de interpolación
optimize – Herramientas de optimización
fftpack – Algortimos de transformada de Fourier
io – Entrada y salida de datos
lib.lapack – Wrappers a la librería LAPACK
lib.blas – Wrappers a la librería BLAS
lib – Wrappers a librerías externas
sparse – Matrices sparse
misc – otras utilidades
cluster – Vector Quantization / Kmeans

42 Capítulo 4. Módulos Científicos


PIMCD2013-python, Publicación 1.0

maxentropy – Ajuste a modelos con máxima entropía


Los subpaquetes de Scipy se tienen que importar de forma separada. Por ejemplo:
>>> from scipy import linalg, optimize.

Tanto Scipy como Numpy tienen versiones de su documentación en pdf y html en http://docs.scipy.org/. También
se puede obtener ayuda de las funciones y de los módulos mediante
>>> sp.info(funcion)

como por ejemplo


>>> import scipy as sp # Importamos el módulo scipy como el alias sp
>>> sp.info(sp.optimize.fmin) # Consultamos la información de la función fmin.

fmin(func, x0, args=(), xtol=0.0001, ftol=0.0001, maxiter=None, maxfun=None,


full_output=0, disp=1, retall=0, callback=None)

Minimize a function using the downhill simplex algorithm.

Parameters
----------
func : callable func(x,*args)
The objective function to be minimized.
x0 : ndarray
Initial guess.
args : tuple
Extra arguments passed to func, i.e. ‘‘f(x,*args)‘‘.
callback : callable
Called after each iteration, as callback(xk), where xk is the
current parameter vector.

Returns
-------
xopt : ndarray
Parameter that minimizes function.
fopt : float
Value of function at minimum: ‘‘fopt = func(xopt)‘‘.
iter : int
Number of iterations performed.
funcalls : int
Number of function calls made.
warnflag : int
1 : Maximum number of function evaluations made.
2 : Maximum number of iterations reached.
allvecs : list
Solution at each iteration.

Other parameters
----------------
xtol : float
Relative error in xopt acceptable for convergence.
ftol : number
Relative error in func(xopt) acceptable for convergence.
maxiter : int
Maximum number of iterations to perform.
maxfun : number
Maximum number of function evaluations to make.
full_output : bool
Set to True if fopt and warnflag outputs are desired.
disp : bool
Set to True to print convergence messages.
retall : bool

4.2. Scipy 43
PIMCD2013-python, Publicación 1.0

Set to True to return list of solutions at each iteration.

Notes
-----
Uses a Nelder-Mead simplex algorithm to find the minimum of function of
one or more variables.

Otra forma de buscar información es con el comando source, que lista el código de esa función,
>>> sp.source(sp.linspace)

En scipy las funciones universales (suma, resta, division, etc.) se han alterado para no producir errores de coma
flotante cuando se encuentran errores; por ejemplo se devuelve NaN e Inf en los arrays en lugar de errores. Para
ayudar a la detección de estos eventos, hay disponibles varias funciones como sp.isnan, sp.isinfinite, sp.isinf.
Además se han modificado algunas funciones (log, sqrt, funciones trigonometricas inversas) para devolver valores
complejos en lugar de NaN (por ejemplo sp.sqrt(-1) devuelve 1j).
Dada la gran cantidad de módulos contenidos en Scipy, en esta sección nos limitaremos en dar ejemplos de algunos
de ellos recordando que el camino ideal para el aprendizaje es la lectura de los manuales oficiales.

4.2.1 Ejemplo: Encontrar mínimos de Intensidad de la difracción provocada por


una rendija.

Desde la educación secundaría uno de los problemas matemáticos más comunes es el de encontrar el mínimo de
una función. Si la función es simple no necesitamos python para encontrar las posibles soluciones, sin embargo,
si la función es compleja o simplemente queremos automatizar el calculo la respuesta es fmin el cual se encuentra
en scipy.optimize. En nuestro caso vamos a tratar de encontrar algún mínimo de intensidad de la diffracción que
resulta a la salida de una rendija. Esta intensidad viene dada por la función sinc, (ecuación (4.1)).

𝑠𝑖𝑛(𝜋 · 𝑎 · 𝑠𝑖𝑛(𝑥))
𝐼 = 𝐼0 (4.1)
𝜋 · 𝑎 · 𝑠𝑖𝑛(𝑥)/𝜆

donde a es el ancho de la rendija, x es el ángulo y 𝜆 es la longitud de onda. Como nos proponemos mantener
nuestro programa, lo primero que vamos a hacer es escribir la autoejecución, la cabecera y la descripción de
nuestro programa tal y como aprendimos en el tema anterior.
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from __future__ import division

#----------------------------------------------------------------------
# Nombre: ejemplo_scipy_uso_fmin.py
# Propósito: Aprender el uso de fmin para minimizar funciones
#
# Origen: Propio
# Autor: José María Herrera-Fernandez
#
# Creación: 17 de Septiembre de 2013
# Historia:
#
# Dependencias: scipy, matplotlib
# Licencia: GPL
#----------------------------------------------------------------------

"""
Descripción: Ejemplo de cómo usar la función fmin perteneciente a scipy.optimize para
encontrar el mínimo de una función.
"""

A continuación cargaremos los módulos que vamos a necesitar,

44 Capítulo 4. Módulos Científicos


PIMCD2013-python, Publicación 1.0

from scipy.optimize import fmin # Importamos fmin de scipy.optimize


from scipy import sin, pi, linspace # Importamos la funcion seno y el valor pi.

Definimos el sistema físico de unidades y la función sinc(x) en python. Para ello recurrimos al comando def para
indicar que vamos a definir una función, el nombre que le vamos a dar a nuestra función que en este caso es sinc
y por último los argumentos de nuestra función. Para nuestro ejemplo con un único argumento nos valdrá, (x),
# Definimos el sistema físico de unidades
um = 1 # micras
mm = 100 * um # milímetros

# Definimos los parámetros físicos


anchura_rendija = 40 * um
longitud_onda = 0.630 * um

# Definimos la función "calcular_minimo_funcion".


def sinc (x):
"""
* (float) --> (float)
* Descripción: Función sinc de un número dado (sen(x)/x).
* Entradas:
- x = float.
* Salidas:
- funcion_sinc = seno(float) / float
* Test:
>>> print(sinc(5))
3.46751168769e-06
"""
# Definimos la función sinc normalizada.
funcion_sinc = (sin(pi*anchura_rendija*sin(x)/longitud_onda)/(pi*anchura_rendija*sin(x)/longitud
return funcion_sinc # La devolvemos.

la función fmin necesita de valores iniciales a modo de semilla para comenzar las iteraciones. En nuestro caso
hemos elegido como valor 0.001,
# fmin necesita una semilla así que se la proporcionamos.
semilla = 0.001

calculamos el valor del mínimo mediante fmin,


# Calculamos el valor mínimo.
x_minimo_intensidad = fmin(sinc,semilla)

y por último presentamos en pantalla el resultado mediante un print. Como podemos observar, tomamos direc-
tamente el valor calculado para el print. Esto lo conseguimos gracias al simbolo %. La utilización es sencilla, en
el lugar donde queramos que aparezca el valor de nuestra variable escribimos el símbolo % seguido del número
de digitos que queramos que tenga la parte real y por últipo el tipo de numero que queremos mostrar. En nuestro
caso %1.2f significa un digito real, dos decimales y el tipo float. Además fuera de las comillas del print debemos
de poner otro % y entre paréntesis la variable a la que llamamos (en nuestro caso x_minimo_intensidad y sinc
(x_minimo_intensidad) ).
# Presenta en pantalla el mínimo y el valor de la función en ese punto.
print (’El valor de x en el que se encuentra el mínimo es %1.2f que se corresponde con y = %1.2f’
% (x_minimo_intensidad , sinc (x_minimo_intensidad )))

Ejecutando,
Optimization terminated successfully.
Current function value: 0.000000
Iterations: 14
Function evaluations: 28
El valor de x en el que se encuentra el mínimo es 0.02 que se corrsponde con y = 0.00

4.2. Scipy 45
PIMCD2013-python, Publicación 1.0

La función sinc(x) tiene bastantes mínimos absolutos. En la imagen podemos ver señalados el encontrado y el
simétrico en rojo y la semilla en verde,

1.0 Difracción por una rendija

0.8

0.6
Intensidad

0.4

0.2

0.00.10 0.05 0.00 0.05 0.10


Ángulo (º)
Nota: En el archivo se añade también el código para la representación sin embargo se explicará cuando hablemos
del módulo Matplotlib.

4.2.2 Ejemplo: Encontrar mínimos en un intervalo definido.

Otro camino para encontrar mínimos es mediante el uso de fminbound como vemos en este ejemplo. Sea la función

(︁ 𝑎𝜋𝑥 )︁
𝑦 = −𝑐𝑜𝑠 + 𝑐𝑥𝑑 (4.2)
𝑏
donde a, b, c, d son coeficientes y x es la variable. Para definirla en python vamos a crear una función cuyos
argumentos sean estos parámetros.
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from __future__ import division

#----------------------------------------------------------------------
# Nombre: ejemplo_scipy_minimizacion.py
# Proposito: Aprender como encontrar minimos locales con python.
#
# Origen: Propio a partir de la documentacion oficial.
# Autor: Jose Maria Herrera-Fernandez, Luis Miguel Sanchez-Brea
#
# Creacion: 18 de Septiembre de 2013
# Historia:
#
# Dependencias: scipy

46 Capítulo 4. Módulos Científicos


PIMCD2013-python, Publicación 1.0

# Licencia: GPL
#----------------------------------------------------------------------

"""
Descripcion: Encuentra minimos locales de una funcion escalar en el intervalo (x1,x2 usando el met
"""

import scipy as sp # Importamos scipy como el alias sp


from scipy.optimize import fminbound # Importamos fmindbound desde scipy.optimize
import matplotlib.pyplot as plt # Importamos matplotlib.pyplot como el alias plt

def mi_funcion(x, a, b, c, d):


"""
(no definido, float, float,float,float,) -->(funcion simbolica)
* Descripcion: Funcion y = -cos(a*pi*x/b) + c*x*d
* Entradas:
- x = variable simbolica.
- a, b, c, d = Coeficientes de la funcion.
* Salidas:
- funcion simbolica y = -sp.cos(a*sp.pi*x/b) + c*x**d
* Test:
>>> import scipy as sp
>>> x = sp.arange(0,1,.01)
>>> print(mi_funcion(x, a = 1, b = 0.5, c = 0.2, d = 2))
[-1. -0.99800673 -0.9920347 -0.98210725 -0.96826316 -0.95055652
-0.92905649 -0.90384705 -0.87502668 -0.84270793 -0.80701699 -0.76809324
-0.72608863 -0.68116711 -0.63350399 -0.58328525 -0.53070679 -0.47597367
-0.41929929 -0.36090455 -0.30101699 -0.23986989 -0.17770131 -0.11475323
-0.05127052 0.0125 0.07631052 0.13991323 0.20306131 0.26550989
0.32701699 0.38734455 0.44625929 0.50353367 0.55894679 0.61228525
0.66334399 0.71192711 0.75784863 0.80093324 0.84101699 0.87794793
0.91158668 0.94180705 0.96849649 0.99155652 1.01090316 1.02646725
1.0381947 1.04604673 1.05 1.05004673 1.0461947 1.03846725
1.02690316 1.01155652 0.99249649 0.96980705 0.94358668 0.91394793
0.88101699 0.84493324 0.80584863 0.76392711 0.71934399 0.67228525
0.62294679 0.57153367 0.51825929 0.46334455 0.40701699 0.34950989
0.29106131 0.23191323 0.17231052 0.1125 0.05272948 -0.00675323
-0.06570131 -0.12386989 -0.18101699 -0.23690455 -0.29129929 -0.34397367
-0.39470679 -0.44328525 -0.48950399 -0.53316711 -0.57408863 -0.61209324
-0.64701699 -0.67870793 -0.70702668 -0.73184705 -0.75305649 -0.77055652
-0.78426316 -0.79410725 -0.8000347 -0.80200673]

"""
y = -sp.cos(a*sp.pi*x/b) + c*x**d
return y

Aleatoriamente escogemos a = 2, b = 0.5, c = 0.05 y d = 2


# Definimos los coeficientes a, b, c, d
a = 2
b = 0.5
c = 0.05
d = 2

y como intervalo (0.2,0.6),


# Definimos el intervalo de busqueda
x1 = 0.2
x2 = 0.6

Con estos datos ya podemos calcular el mínimo mediante fminbound

4.2. Scipy 47
PIMCD2013-python, Publicación 1.0

# Calculamos del minimo local de la funcion entre x1 y x2


x_minimo = fminbound(mi_funcion,x1,x2, args = (a,b,c,d))

y por último presentamos en pantalla


# Presentamos en pantalla el resultado
print (u’El minimo esta en x = %2.3f, y = %2.3f’ %(x_minimo, mi_funcion(x_minimo, a, b, c, d)))

El mínimo está en x = 0.500, y = -0.988

1.5 Busqueda de minimos en un intervalo dado

1.0

0.5
y

0.0

0.5

1.01.0 0.5 0.0 0.5 1.0 1.5 2.0


x

4.2.3 Ejemplo: Ajuste de una función.

Para este ejemplo imaginaremos que una heladería cuyas previsiones de venta en las cinco primeras horas viene
dada por la función,
−𝑏𝑥2
(︁ )︁
𝑣𝑒𝑛𝑡𝑎𝑠 = 𝑒 2𝑑2 + 𝑐𝑥 (4.3)

siendo a, b, c y d coeficientes desconocidos que el propietario quiere saber para mejorar las ventas. Lo primero
que vamos a hacer es crear nuestro archivo .py según la plantilla junto con la importación de los módulos que
vamos a utilizar.
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from __future__ import division

#----------------------------------------------------------------------
# Nombre: ejemplo_scipy_ajusteFunciones.py
# Propósito: Aprender el uso de curve_fit para ajustar funciones.
#
# Origen: Propio a partir del ejemplo de la documentación oficial.

48 Capítulo 4. Módulos Científicos


PIMCD2013-python, Publicación 1.0

# Autor: José María Herrera-Fernandez, Luis Miguel Sánchez-Brea


#
# Creación: 18 de Septiembre de 2013
# Historia:
#
# Dependencias: scipy, matplotlib
# Licencia: GPL
#----------------------------------------------------------------------

"""
Descripción: Ejemplo de cómo usar la función curve_fit perteneciente a scipy.optimize para
ajustar una función a una curva.
"""

from scipy.optimize import curve_fit # Importamos curve_fit de scipy.optimize


import scipy as sp # Importamos scipy como el alias sp
import matplotlib.pyplot as plt # Importamos matplotlib.pyplot como el alias plt.

Posteriormente definimos nuestra función de ventas,


def mi_funcion(x, a, b, c, d):
"""
* (x, float, float, float, float ) --> (mismo_tipo_x)
* Descripción: definición de una función general.
* Entradas:
- x = variable.
- a, b, c, d = Coeficientes de la función.
* Salidas:
- a*sp.exp(-b*x**2/(2*d**2)) + c * x
* Test:
>>> y = mi_funcion(5, 2.5, 1.3, 0.5,1)
2.50000021911
"""

return a*sp.exp(-b*x**2/(2*d**2)) + c * x

Al no tener datos experimentales reales, vamos a crearnos unos a partir de nuestra función. Es lo que en cálculo
científico se denomina añadir ruido. Para ell nos definimos una nueva función que a partir de unos coeficientes
inventados crea un array y al que añadimos un array de numeros aleatorios de la misma dimension que x mediante
sp.random.normal que ponderamos con el factor k.
# Añadimos ruido a la función

x = sp.linspace(0, 5,30)
y = mi_funcion(x, 2.5, 1.3, 0.5,1)

def ruido(x,y,k):
"""
* (array, array, float) --> (array)
* Descripción: definición de una función general que mete ruido a la función creada.
De esta forma simulamos puntos experimentales.
* Entradas:
- x, y = arrays dimensionales.
* Salidas:
- yn = array dimensional con los datos experimentales simulados
* Test:
>>> x = sp.linspace(-5, 5,30)
>>> y = mi_funcion(x, 2.5, 1.3, 0.5,1)
>>> yn = ruido(x,y,k)
[ -1.6507892 -4.94424381 5.15178988 2.2186972 -5.84069527
4.33003238 -1.7888872 -0.30041015 -7.1204506 -2.283254
6.81036412 5.39317282 -8.57465789 -1.53478417 10.00896415

4.2. Scipy 49
PIMCD2013-python, Publicación 1.0

4.6851419 4.97676784 8.65790741 1.10433784 6.66219667


-3.24728962 -2.48561578 6.97308239 1.3574805 14.38335617
7.68693699 7.89128524 -0.48446526 -6.50191937 10.92685615]
"""

yn = y + k * sp.random.normal(size = len(x))
return yn

Ajustamos nuestros datos experimentales recien creados a la función inicial mediante curve_fit. Los resultados los
vamos a almacener en coeficientes_optimizados y covarianza_estimada que es la covarianza de los coeficientes
optimizados,
# Ajustamos los datos experimentales a nuestra función y los almacenamos
coeficientes_optimizados, covarianza_estimada = curve_fit(mi_funcion, x, yn)

por último presentamos en pantalla,


# Mostramos los coeficientes calculados
print "Coeficientes optimizados:", coeficientes_optimizados
print "Covarianza estimada:", covarianza_estimada

obteniendo,
Coeficientes optimizados: [ 2.57706857 64.3221008 0.50208527 6.86175853]
Covarianza estimada: [[ 4.92845597e-03 5.04794116e+06 -6.29090049e-05 2.69252336e+05]
[ 5.04794117e+06 2.20479587e+16 -3.76113582e+05 1.17601699e+15]
[ -6.29090094e-05 -3.76113634e+05 6.21152142e-05 -2.00615411e+04]
[ 2.69252337e+05 1.17601699e+15 -2.00615406e+04 6.27276188e+13]]

Nota: En el archivo se añade también el código para la representación sin embargo se explicará cuando hablemos
del módulo Matplotlib.

4.2.4 Ejemplo: Función de Bessel de orden cero y uno.

Scipy dispone de un paquete muy interesante llamando special donde se encuentran funciones como airy o bessel,
http://docs.scipy.org/doc/scipy/reference/tutorial/special.html. Como ejemplo de este paquete nosotros vamos a
calcular la función de Bessel de orden cero. La ecuación diferencial de Bessel viene definida por,
𝑑2 𝑑𝑦 (︀ 2
𝑥2 + + 𝑥 − 𝛼2 𝑦 = 0
)︀
+𝑥 (4.4)
𝑑𝑥 2 𝑑𝑐
si 𝛼𝜖𝑁 ,
𝑑2 𝑑𝑦 (︀ 2
𝑥2 + + 𝑥 − 𝑛2 𝑦 = 0
)︀
+𝑥 (4.5)
𝑑𝑥 2 𝑑𝑐
cuya solución es,
∞ 𝑘 (︀ 𝑥 )︀𝑛+2𝑘
∑︁ (−1) 2
𝐽𝑛 (𝑥) = (4.6)
𝑘! (𝑛 + 𝑘)!
𝑘=0

para n = 0 y n = 1 obtenemos las funciones de Bessel de orden 0 y 1. Estas funciones son de gran inportancia ya
que las funciones del resto de ordenes se pueden escribir en función de estas dos.
(︀ 𝑥 )︀2 (︀ 𝑥 )︀4 (︀ 𝑥 )︀6
2 2 2 (4.7)
𝐽0 (𝑥) = 1 − 2 + 2 − 2 + ···
(1!) (2!) (3!)

[︃ (︀ 𝑥 )︀2 (︀ 𝑥 )︀4 (︀ 𝑥 )︀6 ]︃


𝑥
𝐽1 (𝑥) = 1 − 2 2 + 2 2 − 2 2 + ··· (4.8)
2 (1!2!) (2!3!) (3!4!)

en python,

50 Capítulo 4. Módulos Científicos


PIMCD2013-python, Publicación 1.0

#!/usr/bin/env python
# -*- coding: utf-8 -*-
from __future__ import division

#----------------------------------------------------------------------
# Nombre: ejemplo_scipy_bessel.py
# Propósito: Aprender el uso el paquete special de scipy.
#
# Origen: Propio.
# Autor: José María Herrera-Fernandez, Luis Miguel Sánchez-Brea
#
# Creación: 18 de Septiembre de 2013
# Historia:
#
# Dependencias: scipy, matplotlib
# Licencia: GPL
#----------------------------------------------------------------------

"""
Descripción: Cálculo de los coeficientes de orden cero y uno de la función de Bessel
contenida en el paquete special.
"""
from scipy import special # Importamos scipy.special
import scipy as sp # Importamos scipy como el alias sp
import matplotlib.pyplot as plt # Importamos matplotlib.pyplot como el alias plt.

# Creamos el array dimensional


x = sp.arange(0,50,.1)

# Calculamos los coeficientes de orden cero.


j0 = special.j0(x)

# Calculamos los coeficientes de orden uno.


j1 = special.j1(x)

4.2. Scipy 51
PIMCD2013-python, Publicación 1.0

Orden cero
1.0 Orden uno

0.5
J(x)

0.0

0.5
0 10 20 30 40 50
x

Nota: En el archivo se añade también el código para la representación sin embargo se explicará cuando hablemos
del módulo Matplotlib.

4.2.5 Ejemplo: Cálculo de integrales

Gracias a integrate de Scipy también podemos realizar integrales en Python. La primera integral que vamos a
realizar es

𝑦 = 𝑒(−𝑥) (4.9)

en el intervalo de 0 a ∞
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from __future__ import division

#----------------------------------------------------------------------
# Nombre: ejemplo_scipy_integrales.py
# Propósito: Aprender como integrar con python.
#
# Origen: Propio .
# Autor: José María Herrera-Fernandez, Luis Miguel Sánchez-Brea
#
# Creación: 18 de Septiembre de 2013
# Historia:
#
# Dependencias: scipy
# Licencia: GPL
#----------------------------------------------------------------------

52 Capítulo 4. Módulos Científicos


PIMCD2013-python, Publicación 1.0

"""
Descripción: Ejemplo de cómo usar la función integrate para realizar integrales
numéricas en python.
"""

import scipy as sp # Importamos scipy como el alias sp


from scipy import integrate # Importamos integrate de scipy

# Ejemplo 1: Definición de la integral para la función y = e^(-x).

def integral_1(limite_inferior, limite_superior, mostrar_resultados):


"""
(float,float, bool) -->(float, float)
* Descripción: Calculo de la integral de limite_inferior a limite_superior de
la función y = e^(-x)
* Entradas:
- limite_inferior = inicio del intervalo de integración.
- limite_superior = fin del intervalo de integración.
* Salidas:
- tupla con las soluciones de la integral
* Test:
>>> integral_1(limite_inferior = 0, limite_superior = 5)
La integral entre 0.00 y 5.00 es
(0.9932620530009145, 1.102742400728068e-14)
"""
# Defino la función e^(-x)
exponencial_decreciente = lambda x: sp.exp(-x)

# Presento en pantalla los resultados si quiero


if mostrar_resultados == True:
print (’La integral entre %2.2f y %2.2f es ’% (limite_inferior, limite_superior))
print(integrate.quad(exponencial_decreciente,limite_inferior,limite_superior))

# Los devuelvo
return integrate.quad(exponencial_decreciente ,limite_inferior,limite_superior)

donde podemos ver que hemos creado un if para mostrar o no los resultados en pantalla. Ejecutando,
>>> integral_1(limite_inferior = 0, limite_superior = sp.inf, mostrar_resultados = True)
La integral entre 0.00 y inf es
(1.0000000000000002, 5.842606742906004e-11)

De la misma forma podemos además tener en cuenta coeficientes de la función que pueden variar,
def integral_2(limite_inferior, limite_superior,a,b, mostrar_resultados):
"""
(float,float,float, float, bool) -->(float, float)
* Descripción: Calculo de la integral de limite_inferior a limite_superior de
la función y = ax^(b)
* Entradas:
- limite_inferior = inicio del intervalo de integración.
- limite_superior = fin del intervalo de integración.
- a,b = coeficientes de la función
* Salidas:
- y = integral
- err = error cometido.
* Test:
>>> y = integral_2(limite_inferior = 0, limite_superior = 1,a = 4, b = 5, mostrar_resultados =
La integral vale 0.67, y el error cometido 0.00
"""

# Defino la función y = a*x^(b)


funcion = lambda x,a,b : a*x**b

4.2. Scipy 53
PIMCD2013-python, Publicación 1.0

# Calculo la integral
y, err = integrate.quad(funcion, limite_inferior, limite_superior, args=(a,b,))

# Presento en pantalla los resultados si quiero


if mostrar_resultados:
print (’La integral vale %2.2f, y el error cometido %2.2f’ % (y, err))

# Los devuelvo
return y, err

donde también hemos añadido el if con una pequeña modificación. Python nos permite ahorrarnos “== True”.
Ejecutando,
>>> y = integral_2(limite_inferior = 0, limite_superior = 1,a = 2, b = 3, mostrar_resultados = Tru
La integral vale 0.50, y el error cometido 0.00

4.2.6 Ejemplo: Interpolación

Otra opción interesante de python es poder interpolar a partir de nuestros datos experimentales. Como ejemplo
interpolaremos en una dimensión mediante interpolate.interp1d,
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from __future__ import division

#----------------------------------------------------------------------
# Nombre: ejemplo_scipy_interpolar.py
# Propósito: Aprender como interpolar con python.
#
# Origen: Propio a partir de la documentación oficial.
# Autor: José María Herrera-Fernandez, Luis Miguel Sánchez-Brea
#
# Creación: 18 de Septiembre de 2013
# Historia:
#
# Dependencias: scipy
# Licencia: GPL
#----------------------------------------------------------------------

"""
Descripción: Ejemplo de cómo hacer una interpolación en python.
"""
import scipy as sp # Importamos scipy como el alias sp
from scipy import interpolate # Importamos interpolate de scipy
import matplotlib.pyplot as plt # Importamos matplotlib.pyplot como el alias plt

# Creamos el array dimensional


x = sp.linspace(0,3,10)

# Evaluamos x en la función y = e^(-x/3) (generando nuestros "datos experimentales")


y = sp.exp(-x/3.0)

# Interpolamos
interpolacion = interpolate.interp1d(x, y)

# Creamos un nuevo array dimensional con más puntos en el mismo intervalo


x2 = sp.linspace(0,3,100)

# Evaluamos x2 en la interpolación
y2 = interpolacion(x2)

54 Capítulo 4. Módulos Científicos


PIMCD2013-python, Publicación 1.0

# Creamos la figura
plt.figure

# Dibujamos las dos gráficas juntas


plt.plot(x, y, ’o’, x2, y2, ’-’)

# Añadimos la leyenda
plt.legend((’Datos conocidos’, ’Datos experimentales’))

# añadimos las etiquetas


plt.xlabel(’Eje x’)
plt.ylabel(’Eje y’)

# Mostramos en pantalla
plt.show()

1.0
Datos conocidos
0.9 Datos experimentales

0.8

0.7
Eje y

0.6

0.5

0.4

0.30.0 0.5 1.0 1.5 2.0 2.5 3.0


Eje x

4.2.7 Ejemplo: Cálculo de las raices de un polinomio

Scipy posibilita la opción de clacular las raices de un polinomio de una forma rápida y sencilla,
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from __future__ import division

#----------------------------------------------------------------------
# Nombre: ejemplo_scipy_raicesPolinomio.py
# Propósito: Aprender como calcular las raices de un polinomio con python.
#
# Origen: Propio a partir de la documentación oficial.
# Autor: José María Herrera-Fernandez, Luis Miguel Sánchez-Brea

4.2. Scipy 55
PIMCD2013-python, Publicación 1.0

#
# Creación: 20 de Septiembre de 2013
# Historia:
#
# Dependencias: scipy, matplotlib
# Licencia: GPL
#----------------------------------------------------------------------

"""
Descripción: Ejemplo de cómo calcular las raices de un polinomio mediante scipy.
"""

import scipy as sp # Importamos scipy como el alias sp


import matplotlib.pyplot as plt # Importamos matplotlib.pyplot como el alias plt

# Creamos un polinomio
polinomio = [1.3,4,.6,-1] # polinomio = 1.3 x^3 + 4 x^2 + 0.6 x - 1

# Creamos un array dimensional


x = sp.arange(-4,1,.05)

# Evaluamos el polinomio en x mediante polyval.


y = sp.polyval(polinomio,x)

# Calculamos las raices del polinomio


raices = sp.roots(polinomio)

# Evaluamos el polinomio en las raices


s = sp.polyval(polinomio,raices)

# Las presentamos en pantalla


print ("Las raices son %2.2f, %2.2f, %2.2f. " % (raices[0], raices[1], raices[2]))

obteniendo,
Las raices son -2.82, -0.67, 0.41.

56 Capítulo 4. Módulos Científicos


PIMCD2013-python, Publicación 1.0

5 Cálculo de las raices de un polinomio


y(x)
Raices del polinomio
0

10
y

15

20

25 4 3 2 1 0 1
x

4.3 Matplotlib

Autores José María Herrera Fernández, Luis Miguel Sánchez Brea

Contenidos de este capítulo:


Funciones principales
Ejemplo: gráfica simple en una dimensión
Ejemplo: gráfica completa en una dimensión
Ejemplo: Varías gráficas individuales en una figura
Ejemplo: Gráfica en dos dimensiones
Ejemplo: Gráfica en tres dimensiones

Matplotlib es el módulo de dibujo de gráficas 2D y 3D que vamos a utilizar aunque no es el único exis-
tente. Matplotlib tiene multitud de librerías de las cuales nosotros, por semejanza a Matlab, utilizaremos
pyplot. Recomendamos al lector visitar http://matplotlib.sourceforge.net/, donde puede encontrar multitud de
programas y ejemplos de como hacer dibujos con Matplotlib. La documentación oficial se encuentra en
http://matplotlib.sourceforge.net/contents.html no obstante, a lo largo de esta sección veremos algunas de las fun-
ciones más interesantes.
Para poder utilizar el módulo Matplotlib en nuestros programas primero debemos importarlo. Por comodidad los
módulos de nombre más extenso suelen renombrarse para optimizar su importación. En nuestro caso vamos a
importarlo como plt incluyendo en el inicio de nuestro programa la siguiente línea de código:
import matplotlib.pyplot as plt

En la Figura 3 podemos ver que si escribimos esta línea de comando en una consola, con solo poner plt se
nos muestra en pantalla la ayuda para este módulo. Si además tras plt añadimos un punto, se nos muestra un

4.3. Matplotlib 57
PIMCD2013-python, Publicación 1.0

Figura 4.2: Figura 2. Ejemplo de gráfica realizada con la librería pyplot de Matplotlib (DOE de fase).

desplegable con todas las funciones en este módulo. Si pinchamos en una de ellas, al igual que antes, se nos abre
una ventana de ayuda que nos indica el uso del comando.
como ya explicamos en temas anteriores también podemos importar matplotlib mediante la sentencia
from matplotlib import pyplot

donde para ejecutar la función debemos usar


pyplot.funcion()

o cargar todas las funciones,


from matplotlib import *

4.3.1 Funciones principales

El módulo Matplotlib (al igual que la mayoría de los módulos) es enorme y explicar todas y cada una de las
funciones nos llevaría demasiado tiempo así que analizaremos las más interesnates para un usuario principiante.
figure(num, figsize, dpi, facecolor, edgecolor, frameon) –> Crea una nueva figura. Se puede utilizar sin argu-
mentos. Devuelve un identificador a la figura.
num = numeración de la figura, si num = None, las figuras se numeran automáticamente.
figsize = w, h tuplas en pulgadas. Tamaño de la figura
dpi = Resolución de la imagen en puntos por pulgada.
facecolor = Color del rectángulo de la figura.
edgecolor = Color del perímetro de la figura.
frameon = Si es falso, elimina el marco de la figura.
>>> figure(num = None, figsize = (8, 6), dpi = 80, facecolor = ’w’, edgecolor = ’k’)

que nos crea el objeto


<matplotlib.figure.Figure object at 0x3938e50>

subplot(numRows, numCols, plotNum) –> Permite incluir varias gráficas en una única figura.
numRows = Número de filas

58 Capítulo 4. Módulos Científicos


PIMCD2013-python, Publicación 1.0

Figura 4.3: Figura 3. Importación del módulo matplotlib como plt en una consola interactiva de Eclipse.

4.3. Matplotlib 59
PIMCD2013-python, Publicación 1.0

numCols = Número de columnas


plotNum = Número de gráfica
>>> subplot(211)

es la primera gráfica de la primera línea. Ejecutando,


<matplotlib.axes.AxesSubplot object at 0x393de90>

plot(x, y, linestyle, linewidth, marker) –> Permite incluir varias gráficas en una única figura.
x = Abcisas.
y = Ordenadas. Tanto x como y pueden ser abcisas tuplas, listas o arrays. La única condición es que el
tamaño de ambas debe ser el mismo ya que en caso contrario python nos devolverá un fallo de tipo dimesión.
También se puede hacer una gráfica sin especificar la coordenada x.
linestyle = color y tipo de dibujar la gráfica. Por ejemplo ‘k- -‘
linewidth = ancho de línea.
marker = Marcador.
por ejemplo,
>>> plt.plot(x, y, ’k--’, linewidth = 2)

Dibuja la curva y(x) en trazo discontinuo de color negro con ancho de línea de tamaño 2. La función plot devuelve
una lista. El número de elementos depende del número de plots realizados. El tipo retornado para cada elemento
de la lista es matplotlib.lines.Lines2D. Analizando cada elemento se puede extraer información que caracteriza al
gráfico (formato de marcador, colores usados,...).
>>> from matplotlib import pyplot
>>> x = range(10)
>>> y = x
>>> a = pyplot.plot(x,y)
>>> len(a)
1
>>> a[0].get_color()
’b’
>>> a[0].get_ls()
’-’
>>> a[0].get_marker()
’None’

Tipos Los tipos más comunes son:


‘ - ‘ línea sólida
‘ - -‘ línea a rayas
‘ -. ‘ línea con puntos y rayas
‘ : ‘ línea punteada
Colores
‘b’ Azul
‘g’ Verde
‘r’ Rojo
‘c’ Cián
‘m’ Magenta
‘y’ Amarillo
‘k’ Negro

60 Capítulo 4. Módulos Científicos


PIMCD2013-python, Publicación 1.0

‘w’ Blanco
También se puede escribir el color de las siguientes formas: nombres (‘green’); cadenas hexadecimales
(‘#008000’); tuplas con convención RGB (0,1,0); intensidades de escala de grises (‘0.8’).
Marcadores
Los tipos principales son:
[ ‘+’ | ‘*’ | ‘,’ | ‘.’ | ‘1’ | ‘2’ | ‘3’ | ‘4’ | ‘<’ | ‘>’ | ‘D’ | ‘H’ | ‘^’ | ‘_’ | ‘d’ | ‘h’ | ‘o’ | ‘p’ | ‘s’ | ‘v’ | ‘x’ ].
Fillstyle Relleno de símbolo elegido para representar cada dato.
[‘full’ | ‘left’ | ‘right’ | ‘bottom’ | ‘top’]
show() Presenta las figuras en pantalla mediante una ventana en la que podemos interactuar. Este comando se
debe añadir siempre que necesitemos obervar nuestra gráfica ya que sino python realizará el cálculo pero no
presentará la imagen. Lo usual es ponerlo al final del programa posibilitando aasí la llamada a todas las figuras
que se encuentren en el mismo.
>>> plt.show()

legend(labels, loc) Coloca una leyenda. Cuando se presentan varías curvas simultáneamente este comando identi-
fica cada una. Puede utilizarse sin argumentos si en cada función plot (o equivalente) se ha incluido el argumento
‘label’, que es un texto para identificar la curva.
>>> plt.legend( (’Etiqueta1’, ’Etiqueta2’, ’Etiqueta3’), loc = ’upper left’)

plt.xlabel(‘s’, comandos_optativos) Etiqueta el eje de abcisas de la gráfica actual. plt.ylabel(‘s’, coman-


dos_optativos) Etiqueta el eje de abcisas de la gráfica actual. plt.title(‘s’, comandos_optativos) Etiqueta el eje
de abcisas de la gráfica actual.
s = Texto que aparecerá en el título
comandos_optativos = En esta etiqueta englobamos todos los modificadores de la fuente etc.
>>> plt.xlabel("Tiempo (s)", fontsize = 20)
>>> plt.ylabel("Distancia (m)", fontsize = 20)
>>> plt.title("velocidad (m/s)", fontsize = 20)

plt.text(x, y, s, comandos_optativos) Añade el texto s en las coordenadas espaciales x, y. El texto se puede


modificar como otro texto (tamaño, color, etc.).
x, y = Coordenadas espaciales horizontal y vertical.
s = Texto que queremos añadir
>>> plt.text(5, 7, "Más texto", fontsize = 12)

axis() Establece u obtiene las propiedades de los ejes. Podemos establecer el rango de coordenadas en x e y que
queremos mostrar en el gráfico. Del mismo modo, podemos seleccionar la relación de aspecto entre las coordena-
das x e y.
axis(), devuelve los límites de los ejes ([xmin, xmax, ymin, ymax])
axis(v), establece los valores límites de los ejes a v = [xmin, xmax, ymin, ymax]
axis(‘off’), elimina líneas de ejes y etiquetas
axis(‘equal’), cambia los límites de x e y para que los incrementos de x e y tengan la misma longitud (un
círculo parece un círculo)
axis(‘scaled’), cambia las dimensiones del plot para conseguir la misma longitud de intervalo en x e y.
axis(‘tight’), cambia los ejes para que se muestren todos los datos.
axhline(y, xmin, xmax) Con esos valores de los parámetros predeterminados establecidos, la línea se ajusta al
rango representado por los correspondientes ejes.
y, array con los datos

4.3. Matplotlib 61
PIMCD2013-python, Publicación 1.0

xmin = 0, valor mínimo


xmax = 1, valor máximo
>>> plt.axhline(y = .5, xmin = 0.25, xmax = 0.75

hold() Si el parámetro es True, podemos poner más curvas en la misma gráfica. Si el parámetro es False, entonces
al escribir una nueva curva, las anteriores se borran.
grid() Establece la malla a True (visible) o False (oculta).
>>> plt.grid(True)
>>> plt,grid(color = ’r’, linestyle = ’’, linewidth = 2)

savefig(ruta) Guarda la gráfica en un archivo.


ruta: ruta y nombre de archivo. Los tipos de datos pueden ser .png, .eps, .pdf, .ps, .svg.
dpi = None: resolución de la imagen en puntos por pulgada
facecolor = ‘w’: color del rectángulo de la figura
edgecolor = ‘w’: color del perímetro de la figura
orientation = ‘portrait’: orientación del papel (landscape)
format = None : (png, pdf, ps, eps y svg).
transparent = False, si es True, creará una gráfica de fondo transparente.
>>> plt.savefig(’figura3.eps’, dpi = 300) #guarda la gráfica con 300dpi (puntos por pulgada)

close() Cierra la gráfica


Asimismo, podemos usar la sintaxis de Latex para hacer más precisas y elegantes nuestras etiquetas. Lo único que
debemos hacer es poner nuestra cadena de texto de la siguiente forma: r’cadena latex’. Por ejemplo:
xlabel(r’$\lambda (\AA)$’)

se muestra como 𝜆()

4.3.2 Ejemplo: gráfica simple en una dimensión

#!/usr/bin/env python
# -*- coding: utf-8 -*-

# Carga de los módulos necesarios


import scipy as sp
import matplotlib.pyplot as plt

# Creamos el array x de cero a cien con cien puntos


x = sp.linspace(0, 10, 100)

# Creamos el array y conde cada punto es el seno de cada elemento de x


y = sp.sin(x)

# Creamos una figura


plt.figure()

# Representamos
plt.plot(x,y)

# Mostramos en pantalla
plt.show()

62 Capítulo 4. Módulos Científicos


PIMCD2013-python, Publicación 1.0

1.0

0.5

0.0

0.5

1.00 2 4 6 8 10

4.3.3 Ejemplo: gráfica completa en una dimensión

#!/usr/bin/env python
# -*- coding: utf-8 -*-
from __future__ import division

#----------------------------------------------------------------------
# Nombre: ejemplo_matplotlib_grafica_completa.py
# Propósito: Aprender a realizar una gráfica lo más completa posible
#
# Origen: Propio.
# Autor: Luis Miguel Sánchez-Brea,José María Herrera-Fernandez
#
# Creación: 18 de Septiembre de 2013
# Historia:
#
# Dependencias: numpy, matplotlib
# Licencia: GPL
#----------------------------------------------------------------------

"""
Descripción: Doble gráfica senoidal donde se muestran la mayoría de los complementos
que se pueden añadir a una gráfica
"""

import numpy as np # Cargamos numpy como el alias np


import matplotlib.pyplot as plt # Crgagamos matplotlib.pyplot como el alias plt

# Definimos el periodo de la función

4.3. Matplotlib 63
PIMCD2013-python, Publicación 1.0

periodo = 0.5

# Definimos el array dimensional


x = np.linspace(0, 2, 1000)

# Definimos la función senoidal


y = np.sin(2*np.pi*x/periodo)

# Creamos la figura
plt.figure()

# Dibujamos en negro discontinuo con etiqueta y1


plt.plot(x, y, ’k--’, linewidth = 2, label = ’y1’)

# Mantenemos la misma figura parta la siguiente gráfica


plt.hold(True)

# Esta vez dibujamos - y en rojo co etiqueta y2


plt.plot(x,-y,’r’, linewidth = 2, label = ’y2’)

# Añadimos la leyenda
plt.legend(loc = 2)

# Añadimos las etiquetas poniermo en Latex "mu" símbolo de micras


plt.xlabel(r"$x (\mu m)$", fontsize = 24, color = (1,0,0))
plt.ylabel(r"$y (\mu m)$", fontsize = 24, color = ’blue’)

# Añadimos texto
plt.text(x = 1, y = 0.0, s = u’T = 0.05’, fontsize = 24)

# Añadimos la rejilla
plt.grid(True)
plt.grid(color = ’0.5’, linestyle = ’--’, linewidth = 1)

# Añadimos los ejes


plt.axis(’tight’)

# Añadimos el título
plt.title(’(a)’,fontsize = 28, color = ’0.75’, verticalalignment = ’baseline’, horizontalalignment

# Guardamos
plt.savefig(’plotCompleta.png’)

# Mostramos en pantalla
plt.show()

64 Capítulo 4. Módulos Científicos


PIMCD2013-python, Publicación 1.0

(a)
y1
y2
0.5

T = 0.05
y(µm)

0.0

0.5

0.0 0.5 1.0 1.5 2.0


x(µm)
4.3.4 Ejemplo: Varías gráficas individuales en una figura

#!/usr/bin/env python
# -*- coding: utf-8 -*-
from __future__ import division

#----------------------------------------------------------------------
# Nombre: ejemplo_matplotlib_subplot.py
# Propósito: Aprender a mostrar varías gráficas separadas en una figura
#
# Origen: Propio.
# Autor: Luis Miguel Sánchez-Brea,José María Herrera-Fernandez
#
# Creación: 18 de Septiembre de 2013
# Historia:
#
# Dependencias: numpy, matplotlib
# Licencia: GPL
#----------------------------------------------------------------------

"""
Descripción: En este ejemplo se muestra como realizar la presentanción de varías
gráficas individuales en una misma figura mediante "subplot"
"""

import numpy as np # Cargamos numpy como el alias np


import matplotlib.pyplot as plt # Crgagamos matplotlib.pyplot como el alias plt

# Definimos el periodo de la gráfica senoidal

4.3. Matplotlib 65
PIMCD2013-python, Publicación 1.0

periodo = 2

# Definimos el array dimensional


x = np.linspace(0, 10, 1000)
# Definimos la función senoidal
y = np.sin(2*np.pi*x/periodo)

# Creamos la figura
plt.figure()

# Primera gráfica
plt.subplot(2,2,1)
plt.plot(x, y,’r’)

# Segunda gráfica
plt.subplot(2,2,2)
plt.plot(x, y,’g’)

# Tercera gráfica
plt.subplot(2,2,3)
plt.plot(x, y,’b’)

# Cuarta gráfica
plt.subplot(2,2,4)
plt.plot(x, y,’k’)

# Mostramos en pantalla
plt.show()

1.0 1.0

0.5 0.5

0.0 0.0

0.5 0.5

1.00 2 4 6 8 10 1.00 2 4 6 8 10
1.0 1.0

0.5 0.5

0.0 0.0

0.5 0.5

1.00 2 4 6 8 10 1.00 2 4 6 8 10

66 Capítulo 4. Módulos Científicos


PIMCD2013-python, Publicación 1.0

4.3.5 Ejemplo: Gráfica en dos dimensiones

#!/usr/bin/env python
# -*- coding: utf-8 -*-
from __future__ import division

#----------------------------------------------------------------------
# Nombre: ejemplo_matplotlib_imshow.py
# Propósito: Aprender a realizar una gráfica en 2D
#
# Origen: Propio.
# Autor: Luis Miguel Sánchez-Brea,José María Herrera-Fernandez
#
# Creación: 18 de Septiembre de 2013
# Historia:
#
# Dependencias: numpy, matplotlib
# Licencia: GPL
#----------------------------------------------------------------------

"""
Descripción: En este ejemplo se muestra como realizar una gráfica en dos dimensiones
mediante imshow
"""

import numpy as np # Cargamos numpy como el alias np


import matplotlib.pyplot as plt # Crgagamos matplotlib.pyplot como el alias plt

# Creamos una figura


plt.figure()

# Creamos los arrays dimensionales


x = np.arange(-5, 5, 0.01)
y = np.arange(-5, 5, 0.01)

# Obtenemos las corrdenadas resultantes de esos arrays


X, Y = np.meshgrid(x, y)

# Definimos la gráfica sen (x^2 + y^2)


fxy = np.sin(X**2+Y**2)

# Representamos
plt.imshow(fxy);

# Añadimos una colorbar


plt.colorbar();

# Mostramos en pantalla
plt.show()

4.3. Matplotlib 67
PIMCD2013-python, Publicación 1.0

0 1.0
0.8
200 0.6
0.4
400 0.2
0.0
600 0.2
0.4
800 0.6
0.8

0 200 400 600 800 1.0

4.3.6 Ejemplo: Gráfica en tres dimensiones

#!/usr/bin/env python
# -*- coding: utf-8 -*-
from __future__ import division

#----------------------------------------------------------------------
# Nombre: ejemplo_matplotlib_grafica_superficie3d.py
# Propósito: Aprender como leer una image y representar una grafica en 3D.
#
# Origen: Propio .
# Autor: José María Herrera-Fernandez
#
# Creación: 18 de Septiembre de 2013
# Historia:
#
# Dependencias: scipy, mpl_toolkits, matplotlib, numpy
# Licencia: GPL
#----------------------------------------------------------------------

"""
Descripción: Ejemplo de cómo usar la función integrate para realizar integrales
numéricas en python.
"""

from mpl_toolkits.mplot3d import Axes3D # Cargo Axes3D de mpl_toolkits.mplot3d


from scipy.misc import imread # Cargo imread de scipy.misc

68 Capítulo 4. Módulos Científicos


PIMCD2013-python, Publicación 1.0

import numpy as np # Cargo numpy como el aliaas np


import matplotlib.pyplot as plt # Cargo matplotlib.pyplot en el alias sp

# Leo una imagen y la almaceno en imagen_superficial


imagen_superficial = imread(’fondo.jpg’)

# Creo una figura


plt.figure()

# Muestro la imagen en pantalla


plt.imshow(imagen_superficial)

# Añado etiquetas
plt.title(’Imagen que usaremos de superficie’)
plt.xlabel(u’# de píxeles’)
plt.ylabel(u’# de píxeles’)

# Creo otra figura y la almaceno en figura_3d


figura_3d = plt.figure()

# Indicamos que vamos a representar en 3D


ax = figura_3d.gca(projection = ’3d’)

# Creamos los arrays dimensionales de la misma dimensión que imagen_superficial


X = np.linspace(-5, 5, imagen_superficial.shape[0])
Y = np.linspace(-5, 5, imagen_superficial.shape[1])

# Obtenemos las coordenadas a partir de los arrays creados


X, Y = np.meshgrid(X, Y)

# Defino la función que deseo representar


R = np.sqrt(X ** 2 + Y ** 2)
Z = np.sin(R)

# Reescalamos de RGB a [0-1]


imagen_superficial = imagen_superficial.swapaxes(0, 1) / 255.

# meshgrid orienta los ejes al revés luego hay que voltear


ax.plot_surface(X, Y, Z, facecolors = np.flipud(imagen_superficial))

# Fijamos la posición inicial de la grafica


ax.view_init(45, -35)

# Añadimos etiquetas
plt.title(u’Imagen sobre una grafica 3D’)
plt.xlabel(’Eje x’)
plt.ylabel(’Eje y’)
# Mostramos en pantalla
plt.show()

4.3. Matplotlib 69
PIMCD2013-python, Publicación 1.0

0
Imagen que usaremos de superficie

20

40
# de píxeles

60

80

100

0 20 40 60 80 100 120 140


# de píxeles

Imagen sobre una grafica 3D

1.0
0.5
0.0
0.5
6
4 1.0
2 6
Eje 0 4
x 2
2 0 y
4 4
2 Eje
6 6

70 Capítulo 4. Módulos Científicos


CAPÍTULO 5

Ecuaciones de Maxwell en el vacío

Autor Luis Miguel Sánchez Brea

Contenidos de este capítulo:


Introducción
Programación
Haces de Gauss

Herramientas utilizadas en este tutorial:


numpy: manipulación de datos
scipy: Herramientas matemáticas
Archivos necesarios para la clase interferencias:
camposXY.py. Archivo con las funciones principales de campos camposXY.py
fuentesXY.py. Archivo con las funciones principales de fuentes fuentesXY.py
Descarga de archivos:
Ejemplo de onda esférica onda_esferica.py
Ejemplo animado de onda esférica onda_esferica_animado.py
Ejemplo de ondas planas onda_plana.py
Ejemplo de haces de gauss haz_gauss.py

5.1 Introducción

E = E1 + E2

también es una solución. Sin embargo, el campo no es un parámetro observable, sino que lo es el promedio
temporal del vector de Poynting, que se define como
1
S≡ E ∧ B,
𝜇0

con tiene unidades de [S] = 𝑊/𝑚2 . Al particularizar al caso de ondas armónicas planas tenemos
√︂
1 𝜀0 2
⟨S⟩ = |E| u𝑘 ,
2 𝜇0
: 𝑚𝑎𝑡ℎ : ‘(𝑡, 𝑟)‘

71
PIMCD2013-python, Publicación 1.0

5.2 Programación

Debido a la sencillez conceptual de las interferencias, las utilizaremos para analizar los distintos tipos de pro-
gramación que se puede realizar con Python, como es la programación con scripts (todo el código en un mismo
archivo lineal), la programación funcional (se escriben funciones que simplifican lo cálculos) y la programación
con clases (donde funciones y datos están íntimamente relacionados). Finalmente veremos un procedimiento sen-
cillo para desarrollar vídeos, simulando un interferómetro de Michelson.
tamano=100*um
x=sp.linspace(-tamano,tamano, 256)
y=sp.linspace(-tamano,tamano, 256)
X,Y=sp.meshgrid(x,y)

#!/usr/local/bin/python
# -*- coding: utf-8 -*-
#------------------------------------
# Autor: Luis Miguel Sanchez Brea
# Fecha 2013/10/24 (version 1.0)
# Licencia: GPL
# Objetivo: onda plana
#-------------------------------------

from __future__ import division

import scipy as sp
import matplotlib.pyplot as plt
import sys
sys.path.append(’../’)

from camposXY import campoXY, um, mm, grados


from fuentesXY import fuenteXY

def onda_plana(A=1, teta=0*grados, phi=0*grados):


"""
Generacion de una onda plana
"""

#definicion de parametros iniciales


tamano = 250 * um
npixels = 256
#Generacion de la clase
x = sp.linspace(-tamano / 2, tamano / 2, npixels)
y = sp.linspace(-tamano / 2, tamano / 2, npixels)
X, Y = sp.meshgrid(x, y)
wavelength = 0.6328 * um
k = 2 * sp.pi / wavelength

u = A * sp.exp(1.j * k * (X * sp.sin(teta) * sp.sin(phi) + Y * sp.cos(teta) * sp.sin(phi))


#carga de la onda plana
#dibujar y guardar

IDfig = plt.figure()
IDax = IDfig.add_subplot(111)
extension = [x.min(), x.max(), y.min(), y.max()]
angulo = sp.angle(u) #/ sp.pi
angulo[angulo == 1] = -1
IDimagen = plt.imshow(angulo, interpolation = ’bilinear’, aspect = ’auto’,origin = ’lower’
plt.xlabel("$x (\mu m)$", fontsize = 20)
plt.ylabel("$y (\mu m)$", fontsize = 20)
titulo="$A =$ %4.2f $\phi =$ %4.2f $^{0}, \Theta =$ %4.2f $^0$" %(A, phi*180/sp.pi, teta
plt.suptitle(titulo, fontsize = 20)

72 Capítulo 5. Ecuaciones de Maxwell en el vacío


PIMCD2013-python, Publicación 1.0

plt.axis(’scaled’)
plt.axis(extension)
plt.colorbar()
IDimagen.set_cmap("RdBu")
plt.clim(vmin = -sp.pi, vmax = sp.pi)

onda_plana(A=1, teta=45*grados, phi=1*grados)


onda_plana(A=1, teta=0*grados, phi=0*grados)
plt.show()

A = 1.00 φ = 1.00 0 ,Θ = 45.00 0


100 2.4

1.6
50
0.8
y(µm)

0 0.0

0.8
50
1.6

100 2.4

100 50 0 50 100
x(µm)

5.2. Programación 73
PIMCD2013-python, Publicación 1.0

A = 1.00 φ = 0.00 0 ,Θ = 0.00 0


100 2.4

1.6
50
0.8
y(µm)

0 0.0

0.8
50
1.6

100 2.4

100 50 0 50 100
x(µm)
#!/usr/local/bin/python
# -*- coding: utf-8 -*-
#------------------------------------
# Autor: Luis Miguel Sanchez Brea
# Fecha 2013/10/24 (version 1.0)
# Licencia: GPL
# Objetivo: onda esferica
#-------------------------------------

from __future__ import division

import scipy as sp
import matplotlib.pyplot as plt

um=1

def onda_esferica(A = 1, z0 = -1000 * um):


"""
Generacion de una onda plana
"""

#definicion de parametros iniciales


tamano = 250 * um
npixels = 256
#Generacion de la clase
x = sp.linspace(-tamano / 2, tamano / 2, npixels)
y = sp.linspace(-tamano / 2, tamano / 2, npixels)
X, Y = sp.meshgrid(x, y)
wavelength = 0.6328 * um
k = 2 * sp.pi / wavelength

74 Capítulo 5. Ecuaciones de Maxwell en el vacío


PIMCD2013-python, Publicación 1.0

#Centrado radio de la mascara y distancia al origen emisor


R2 = X ** 2 + Y** 2
Rz = sp.sqrt(X ** 2 + Y ** 2 + z0 ** 2)

#Onda esferica
u = A * sp.exp(-1.j * sp.sign(z0) * k * Rz) / Rz

IDfig = plt.figure()
IDax = IDfig.add_subplot(111)
extension = [x.min(), x.max(), y.min(), y.max()]
angulo = sp.angle(u)
angulo[angulo == 1] = -1
IDimagen = plt.imshow(angulo, interpolation = ’bilinear’, aspect = ’auto’,origin = ’lower’
plt.xlabel("$x (\mu m)$", fontsize = 20)
plt.ylabel("$y (\mu m)$", fontsize = 20)
titulo="fase A = %4.2f z0 = %4.2f mm" %(A, z0/1000*um)
plt.suptitle(titulo, fontsize = 20)
plt.axis(’scaled’)
plt.axis(extension)
plt.colorbar()
IDimagen.set_cmap("RdBu")
plt.clim(vmin = -sp.pi, vmax = sp.pi)

onda_esferica(A=1, z0 = -1000 * um)


onda_esferica(A=1, z0 = -5000 * um)
onda_esferica(A=1, z0 = 5000 * um)

plt.show()

fase A = 1.00 z0 = -1.00 mm


100 2.4

1.6
50
0.8
y(µm)

0 0.0

0.8
50
1.6

100 2.4

100 50 0 50 100
x(µm)

5.2. Programación 75
PIMCD2013-python, Publicación 1.0

fase A = 1.00 z0 = -5.00 mm


100 2.4

1.6
50
0.8
y(µm)

0 0.0

0.8
50
1.6

100 2.4

100 50 0 50 100
x(µm)
fase A = 1.00 z0 = 5.00 mm
100 2.4

1.6
50
0.8
y(µm)

0 0.0

0.8
50
1.6

100 2.4

100 50 0 50 100
x(µm)

76 Capítulo 5. Ecuaciones de Maxwell en el vacío


PIMCD2013-python, Publicación 1.0

#!/usr/local/bin/python
# -*- coding: utf-8 -*-
#------------------------------------
# Autor: Luis Miguel Sanchez Brea
# Fecha 2013/10/24 (version 1.0)
# Licencia: GPL
# Objetivo: Emision de una onda esferica
#-------------------------------------

import time, gobject, gtk #@UnusedImport


import scipy as sp
import matplotlib
matplotlib.use(’GTKAgg’)
import matplotlib.pyplot as plt
import matplotlib.cm as cm

um=1; mm=1000*um; m=1000*mm; s=1; fs=1e-15*s

wavelength=0.6328*um
k = 2 * sp.pi / wavelength
c= 299792.458 *m/s

w=c*k

#area de visualizacion
tamano=1.25*um
x=sp.linspace(-tamano, tamano, 250)
z=sp.linspace(-tamano, tamano, 250)
X,Z=sp.meshgrid(x, z)

#calculo de la intensidad
R = sp.sqrt(X** 2 + Z ** 2)
E_theta=sp.cos(k*R)/R
intensidad = abs(E_theta) ** 2
intensidad=sp.log(intensidad+1)
#fase=sp.angle(E_theta)

#Se dibuja por primera vez


fig = plt.figure(1)
a = plt.subplot(111)
im = a.imshow(intensidad, cmap=cm.jet)
manager = plt.get_current_fig_manager()

#tiempo
t=sp.linspace(0,50000*fs,1001)

#bucle
i=0
def updatefig(*args):
global t, i
E_theta=sp.cos((k*R-w*t[i]))/R
intensidad = abs(E_theta) ** 2
intensidad=sp.log(intensidad+1)
#fase=sp.angle(E_theta)

im.set_array(intensidad)
titulo="t = %4.0f fs" %(t[i]/fs)
plt.title(titulo)
manager.canvas.draw()
i += 1
if i==len(t):
return False
return True

5.2. Programación 77
PIMCD2013-python, Publicación 1.0

gobject.idle_add(updatefig)
plt.show()

50

100

150

200

0 50 100 150 200

#!/usr/local/bin/python
# -*- coding: utf-8 -*-
#------------------------------------
# Autor: Luis Miguel Sanchez Brea
# Fecha 2013/10/24 (version 1.0)
# Licencia: GPL
# Objetivo: haz gaussiano
#-------------------------------------

from __future__ import division

import scipy as sp
import matplotlib.pyplot as plt
import sys
sys.path.append(’../’)

from camposXY import campoXY, um, mm, grados


from fuentesXY import fuenteXY

def haz_gauss(A = 1, w = (250 * um, 250 * um)):


#definicion de parametros iniciales
tamano = 1000 * um
x = sp.linspace(-tamano / 2, tamano / 2, 256)
y = sp.linspace(-tamano / 2, tamano / 2, 256)
X,Y=sp.meshgrid(x,y)
wavelength = 0.6238 * um
k = 2 * sp.pi / wavelength
wx, wy = w

78 Capítulo 5. Ecuaciones de Maxwell en el vacío


PIMCD2013-python, Publicación 1.0

#Definicion de la amplitud
u = A * sp.exp(-X ** 2 / (2 * wx ** 2) - Y** 2 / (2 * wy ** 2))
intensidad=abs(u)**2

IDfig = plt.figure()
IDax = IDfig.add_subplot(111)
extension = [x.min(), x.max(), y.min(), y.max()]
IDimagen = plt.imshow(intensidad ,origin = ’lower’, extent = extension)
plt.xlabel("$x (\mu m)$", fontsize = 20)
plt.ylabel("$y (\mu m)$", fontsize = 20)
plt.axis(’scaled’)
plt.axis(extension)
plt.colorbar()
IDimagen.set_cmap("gist_heat")

haz_gauss( w = (250 * um, 250 * um))


haz_gauss( w = (250 * um, 125 * um))
plt.show()

1.0
400 0.9
0.8
200 0.7
0.6
y(µm)

0 0.5
0.4
200 0.3
0.2
400 0.1

400 200 0 200 400 0.0


x(µm)

5.2. Programación 79
PIMCD2013-python, Publicación 1.0

1.0
400 0.9
0.8
200 0.7
0.6
y(µm)

0 0.5
0.4
200 0.3
0.2
400 0.1

400 200 0 200 400 0.0


x(µm)

5.3 Haces de Gauss

Veamos ahora el proceso de interferencia de 2 haces de Gauss. Estos hace se pueden definir como

𝜌2 𝜌2
(︂ )︂ (︂ )︂
𝜔0
𝐸(𝑥, 𝑦, 𝑧) = 𝐴0 exp − 2 exp 𝑖𝑘𝑧 + 𝑖𝑘 − 𝑖𝜁(𝑧)
𝜔(𝑧) 𝜔 (𝑧) 2𝑅(𝑧)

donde
𝑧0 𝜆
𝜔02 =
𝜋

√︃ (︂ )︂2
𝑧
𝜔(𝑧) = 𝜔0 1+
𝑧0

[︂ ]︂
𝑧
𝑅(𝑧) = 𝑧 1 + ( )2
𝑧0

Este tipo de haces se puede programar fácilmente. Asumamos que estamos en el foco del haz z=0 y los haces están
rotados. Entonces también se van a producir interferencias entre los haces.

80 Capítulo 5. Ecuaciones de Maxwell en el vacío


CAPÍTULO 6

Polarización

Autores Luis Miguel Sánchez Brea y José Luis Vilas Prieto

Contenidos de este capítulo:


Introducción
Programa interactivo para el análisis de la polarización
Definiciones de polarizadores
Definiciones de estados de luz polarizada
Cálculo de la intensidad de un vector de polarización
Polarización básica
Ley de Malus
Efecto Zenon

Herramientas utilizadas en este tutorial:


Módulo con las funciones principales polarizacion.py
Contenidos de los archivos:
polarizacion.py:
• rotacion(). Matriz de un rotador.
• polarizadorLineal(). Matriz de un polarizador lineal.
• polarizadorRetardador(). Matriz de un retardador lineal.
• laminaLambdaCuartos(). Matriz de un retardador de cuarto de onda.
• laminaLambdaMedios(). Matriz de un retardador de media onda.
• luzLineal(). Vector de Jones de luz linealmente polarizada.
• luzCircular(). Vector de Jones de luz polarizada circular.
• luzEliptica(). Vector de Jones de luz elípticamente polarizada.
• intensidad(). Calcula la intensidad de luz de un estado.
ley_malus.py:
• ley_malus(). Representacion de la intensidad de la luz al atravesar dos polarizadores lineales.
efecto_zenon.py:
• nPolarizadores(). Producto de N polarizadores lineales.
• efectoZenon(). Intensidad transmitida por un conjunto de N polarizadores.
Descarga de archivos:
Módulo interactivo emanim12.py

81
PIMCD2013-python, Publicación 1.0

Módulo con las funciones principales polarizacion.py


Archivo con la descripción de la ley de Malus ley_malus.py
Archivo con el ejemplo del efecto Zenón efecto_zenon.py

6.1 Introducción

Desde un punto de vista clásico, la luz es una onda electromagnética constituida por dos campos, eléctrico y
magnético oscilando normales a la dirección de propagación y normalmente entre sí. Bajo esta definición se llama
polarización de la luz a la trayectoria del vector campo eléctrico. Si la trayectoria de la luz es caótica se dice que
la luz está despolarizada, así mismo si la trayectoria está bien definida hablamos de estados de polarización o
también llamados estados puros.
La polarización de la luz está intrínsecamente ligada al concepto de onda plana (ver sección, Fuentes de luz-Onda
plana).

6.2 Programa interactivo para el análisis de la polarización

Andras Szilagyi ha realizado un módulo interactivo muy interesante para el análisis de la polarizacion:
emanim12.py. Se puede encontrar en http://www.enzim.hu/~szia/emanim for more information.
En la siguiente Figura se muestra un ejemplo de utilización de este módulo. Tiene un menú y botones para selec-
cionar el tipo de análisis de polarización y parámetros utilizados

Figura 6.1: Figura. Ejemplo de uso del programa enamin.py

6.3 Definiciones de polarizadores

La luz polarizada puede cambiar de estado al atravesar distintos elementos ópticos. La caracterización y modelado
de estos medios resulta crítica en óptica. El cálculo de Jones permite no solo definir estados de polarización sino

82 Capítulo 6. Polarización
PIMCD2013-python, Publicación 1.0

además, representar los elementos ópticos mediante matrices, tales que el vector de Jones a la salida del sistema
sea la acción de la matriz del medio sobre un vector de Jones.
Rotador
Un rotador es un sistema capaz de rotar el vector de Jones pero no modificar su estado. Por ello están representados
por matrices de rotación. Para su implementación basta con definir la matriz
(︂ )︂
cos 𝜃 sin 𝜃
,
− sin 𝜃 cos 𝜃

def rotacion(theta = 0):


"""matriz de rotacion
- theta: angulo de rotacion
"""
#Definicion de la matriz
return sp.matrix(sp.array([[ cos(theta), sin(theta)],
[-sin(theta), cos(theta)]]), dtype = float)

Polarizador lineal
Los polarizadores lineales son elementos ópticos tales que independientemente del estado de polarización a la
entrada transforman dicho estado en luz linealmente polarizada con acimut 𝜃. Donde 𝜃 representa el ángulo del
eje del polarizador. La matriz de Jones de un polarizador lineal viene representada por

cos2 𝜃
(︂ )︂
sin 𝜃 cos 𝜃
,
sin 𝜃 cos 𝜃 sin2 𝜃

def polarizadorLineal(theta = 0):


"""Polarizador lineal
- theta: angulo de entrada en radianes
"""
#Metodo directo
#return sp.matrix(sp.array([[cos(theta) ** 2, sin(theta) * cos(theta)],
# [sin(theta) * cos(theta), sin(theta) ** 2]]), dtype = float)

PL=sp.matrix(sp.array([[1, 0], [0, 0]]), dtype = float)


return rotacion(-theta) * PL * rotacion(theta)

Polarizador Retardador
Un retardador lineal es un elemento óptico compuesto por un material birrefringente capaz de introducir un re-
tardo entre las componentes paralela y perpendicular de la luz incidente. Está constituido por dos ejes o también
llamadas líneas neutras con índices de refracción asociados ordinario y extraordinario, la orientación de los ejes y
el espesor determinan la acción de la lámina sobre la luz incidente. El retardo se introduce fruto de la diferencia
de camino óptico asociada a cada eje. La matriz de Jones asociada a un retardador viene dada por

cos 2𝛿 + 𝑖 2𝛿 cos 2𝜃 𝑖 sin 2𝛿 sin 2𝜃


(︂ )︂

𝑖 sin 2𝛿 sin 2𝜃 cos 2𝛿 − 𝑖 sin 2𝛿 cos 2𝜃

def polarizadorRetardador(desfase = 0, ne = 1, no = 1, d = 1 * um, wavelength = 0.6328 * um, theta


"""Retardador
Se puede calcular de dos formas. Si desfase es distinto de None, entonces es lo que man
Si desfase=None, entonces se calcula el desfase con el resto de cosas.
- desfase: desfase del rotador definido como 2*pi*(ne-no)*d/lambda
- ne: indice extraordinario, no: indice ordinario, d: espesor de la lamina, wavelength:
- theta: angulo de rotacion
"""
#Definicion del retardo
if desfase == None:
desfase = 2 * sp.pi * (ne - no) * d / wavelength

6.3. Definiciones de polarizadores 83


PIMCD2013-python, Publicación 1.0

#Definicion de la matriz del retardador


Lretardo = sp.matrix(sp.array([[ 1, 0],
[ 0, sp.exp(1j * desfase)]]), dtype = complex)

return rotacion(-theta) * Lretardo * rotacion(theta)

Lámina Lambda/4
Las láminas retardadoras hacen uso de la birrefringencia de la luz. Una lámina de cuarto de onda introduce un
retardo de 𝜋/2 entre las componentes del vector de Jones de la luz incidente sobre la lámina. La matriz de Jones
del retardador de cuarto de onda es
(︂ )︂
1 + 𝑖 cos 2𝜃 𝑖 sin 2𝜃
,
𝑖 sin 2𝜃 1 − 𝑖 cos 2𝜃

def laminaLambdaCuartos(theta = 0 * grados):


"""lamina lambda/4. Muy utilizada porque pasa de luz linealmente polarizada a luz circular
"""
#Definicion de la matriz
return polarizadorRetardador(desfase = sp.pi / 2, theta = theta)

Lámina Lambda/2
Las láminas de media onda, alteran el sentido de giro del vector luz, derechas a izquierda o viceversa. Por tanto
los estados linealmente polarizados son invariantes bajo estos elementos ópticos, mientras que si se incide sobre
una lámina de media onda con luz circular a derechas, a la salida mantiene la circularidad pero el sentido de giro
será levógiro. Idem para estados elípticamente polarizados. La matriz de Jones del retardador de media onda es
(︂ )︂
cos 2𝜃 sin 2𝜃
,
sin 2𝜃 − cos 2𝜃

def laminaLambdaMedios(theta = 0 * grados):


"""lamina lambda/2. Muy utilizada porque gira el estado de polarizacion
"""
#Definicion de la matriz
return polarizadorRetardador(desfase = sp.pi, theta = theta)

6.4 Definiciones de estados de luz polarizada

Considérese una onda plana monocromática de frecuencia 𝜔, vector de onda ⃗𝑘, y amplitud 𝐸0 representada como

𝐸𝑥 = 𝐸0𝑥 𝑒𝑖𝑘⃗𝑟−𝜔𝑡+𝛿𝑥 ,


𝐸𝑦 = 𝐸0𝑦 𝑒𝑖𝑘⃗𝑟−𝜔𝑡+𝛿𝑥 .

Es posible definir esta señal únicamente con la parte espacial. Así denotando el desfase como 𝛿 = 𝛿𝑦 − 𝛿𝑥 se
escribe
(︂ )︂
⃗ 𝐸0𝑥
𝐸= .
𝐸0𝑦 𝑒𝑖𝛿

Este vector recibe el nombre de vector de Jones. Obsérvese como este vector representa la proyección trayectoria
del campo eléctrico sobre un plano perpendicular a la dirección de propagación. Así se puede probar como el
conjunto de trayectorias posibles de acuerdo a estos vectores están definidas por la ecuación

𝑥2 𝑦2 𝑥𝑦 cos 𝛿
2 + 2 −2 = sin2 𝛿.
𝐸0𝑥 𝐸0𝑦 𝐸0𝑥 𝐸0𝑦

84 Capítulo 6. Polarización
PIMCD2013-python, Publicación 1.0

Esta ecuación representa una elipse, la llamada elipse de polarización y de ella derivan el conjunto de estados
puros. De este modo, si 𝛿 = 𝑛𝜋 con 𝑛, entero, o si 𝐸0𝑥 = 0 o 𝐸0𝑦 = 0, la elipse de polarización degenera en una
recta, y se dice que el estado de la luz es linealmente polarizado, así mismo, si 𝐸0𝑥 = 𝐸0𝑦 y 𝛿 = ±𝜋/2, la elipse
degenera en una circunferencia, y por tanto la luz es circularmente polarizada. En cualquier otro caso distinto de
estos dos la luz se dice que posee polarización elíptica.
Luz Lineal
Un vector de Jones unitario de luz linealmente polarizada es siempre de la forma
(︂ )︂
⃗ cos 𝛼
𝐽= .
sin 𝛼

Donde 𝛼 representa el azimut del estado de luz, es decir, la inclinación del vector campo eléctrico. luzLineal
implementa este vector.
def luzLineal(alfa = 0):
"""Luz linealmente polarizada
- alfa, angulo en el cual esta polarizada
"""
#Definicion del vector de Jones
return sp.matrix([[ cos(alfa)], [sin(alfa)]])

Luz Circular
Bajo la condición 𝛿 = ±𝜋/2 con 𝐸0𝑥 = 𝐸0𝑦 se obtienen los vectores de Jones
El doble signo denota el sentido de giro del vector campo eléctrico, si es positivo, entonces la luz será circularmente
polarizada a derechas o dextrógira. En caso contrario se dice que es circularmente polarizada a izquierdas o
levógira. La elección entre circular a derechas o izquierdas se especifica mediante una d o una i en el argumento
de la función luzCircular
def luzCircular(tipo = ’d’):
"""Luz circularmente polarizada
- tipo, ’d’: derecha ’i’: izquierda
"""
#Definicion del vector de Jones a dextrogiro o levogiro
if tipo == ’d’:
return sp.matrix([[1], [1j]]) / sp.sqrt(2)
elif tipo == ’i’:
return sp.matrix([[1], [-1j]]) / sp.sqrt(2)

Luz Elíptica
Si el vector de Jones no es ninguno de los dos casos anteriores, lineal o circular, entonces es necesariamente
elípticamente polarizado. Su definición como función de Python debe emplear la forma más general del vector de
Jones,
(︂ )︂
𝑎
.
𝑏𝑒𝑖𝛿

def luzEliptica(a = 1, b = 1, desfase = 0, theta = 0):


"""Luz elipticamente polarizada
- a: amplitud de eje x
- b: amplitud de eje y
- desfase: desfase entre ambos ejes
- theta: rotacion del haz respecto de eje x
"""
#Definicion del vector de Jones
M = sp.matrix([[a], [b * sp.exp(1j * desfase)]], dtype = complex)
return rotacion(theta) * M

6.4. Definiciones de estados de luz polarizada 85


PIMCD2013-python, Publicación 1.0

6.5 Cálculo de la intensidad de un vector de polarización

La intensidad de la luz no es más que el módulo del vector de Jones. La función intensidad cumple este cometido
def intensidad(vectorPolarizacion):
#Calculo de la intensidad
return float(abs(vectorPolarizacion[0]) ** 2 + abs(vectorPolarizacion[1]) ** 2)

6.6 Polarización básica

La polarización de la luz se puede ver afectada al atravesar esta distintos elementos ópticos. El formalismo de Jones
constituye una representación tanto de los elementos ópticos, mediante matrices 2x2, como de estados puros de
polarización, mediante vectores. De este modo, el estado de polarización a la salida de un elemento óptico queda
determinado por la acción de la matriz de Jones del elemento sobre el vector de Jones que representa el estado de
polarización incidente.
Así supónganse los siguientes ejemplos
Polarizador lineal
Un haz de luz linealmente polarizado según el eje x incide sobre un polarizador cuyo eje está alineado con el eje
y. Obviamente no hay transmisión de luz, por ello el vector de Jones a la salida del polarizador debe ser el vector
nulo, y en consecuencia la intensidad de la luz es cero. En efecto
(︂ )︂ (︂ )︂ (︂ )︂
0 0 1 0
=
0 1 0 0

Esto se programaría de la siguiente forma:


PolarizadorLineal(90)*luzLineal(0)

Lamina de media de onda


Un haz de luz circularmente polarizado a derechas incide sobre una lámina de media onda cuyo eje rápido puede
estar aleatoriamente dispuesto, por sencillez supóngase que está alineado con el eje de abscisas. El resultado de
este experimento es la obtención de luz circularmente polarizada a izquierdas, como muestra el cálculo:
laminaLambdaMedios(0)*luzCircular(’d’)

(︂ )︂ (︂ )︂ (︂ )︂
1 0 1 1
=
0 −1 𝑖 −𝑖

Lámina de cuarto de onda


Un haz de luz linealmente polarizado según el eje x incide sobre una lámina retardadora cuyo eje rápido está
inclinado 45º respecto de la horizontal. Probaremos como la luz a la salida del sistema es circularmente polarizada:
laminaLambdaCuartos(45)*luzLineal(0)

(︂ )︂ (︂ )︂ (︂ )︂
1 𝑖 1 1
=
𝑖 1 0 𝑖

6.7 Ley de Malus

La ley de Malus establece que la intensidad de la luz a la salida de un sistema de dos polarizadores es directamente
proporcional a cos2 𝜃 siendo 𝜃 el ángulo comprendido entre los ejes de los polarizadores. La demostración es
sencilla, basta considerar un estado de luz lineal incidiendo sobre un polarizador, y calcular el vector de Jones a

86 Capítulo 6. Polarización
PIMCD2013-python, Publicación 1.0

la salida, el módulo cuadrado de este vector de Jones posee una dependencia directamente proporcional a cos2 𝜃.
Este hecho se muestra en el siguiente código.
"""Ley de Malus"""
#Importamos modulos scipy, sys y polarizacion
import scipy as sp
import sys
sys.path.append(’../’)
from polarizacion import *

#Angulo entre los polarizadores


angulos = sp.linspace(0,2*sp.pi,100)
#Se predefine la intensidad para un bucle posterior
I= sp.zeros_like(angulos)

#haz incidente
haz=luzLineal(alfa=0*grados)
P1 = polarizadorLineal(theta = 0*grados)

#Calculo de la intensidad para cada angulo


for i in range(len(angulos)):
P2 = polarizadorLineal(theta = angulos[i])
vectorFinal = P2*P1*haz
I[i]=intensidad(vectorFinal)

#Representacion de la intensidad
plt.figure()
plt.plot(angulos/grados,I,’k’,lw=2)
plt.xlabel(r’$\theta$ (grados)’,fontsize=24)
plt.ylabel(’Intensidad transmitida’,fontsize=24)
plt.xlim([0,angulos[-1]/grados])
plt.show()

1.0
Intensidad transmitida

0.8

0.6

0.4

0.2

0.00 50 100 150 200 250 300 350


θ (grados)
6.7. Ley de Malus 87
PIMCD2013-python, Publicación 1.0

6.8 Efecto Zenon

El efecto Zenón es el paso de luz a través de un sistema de 𝑁 polarizadores. Si bien se extingue la luz bajo cuando
dos polarizadores posicionan sus ejes perpendicularmente, bajo otros ángulos la luz atraviesa el sistema. Así, si se
disponen N polarizadores con ángulo entre sus ejes 360/𝑁 aumentar la transmitancia al aumentar el número de
polarizadores.
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#----------------------------------------------------------------------
# Autor: Luis Miguel Sanchez Brea
# Fecha 2013/10/24 (version 1.0)
# Licencia: GPL
#----------------------------------------------------------------------
"""
Efecto Zenon, n polarizadores rotados igual entre si.
"""
import scipy as sp
import sys
sys.path.append(’../’)
from polarizacion import *

def nPolarizadores(N = 4):


#Define un sistema de una pila de N polarizadores

#Angulo de cada polarizador


angulos = sp.linspace(sp.pi / 2, 0, N)

#Definicion de la matriz del polarizador


M = polarizadorLineal(theta = angulos[0])
#Calculo de la matriz producto
for i in range(1, len(angulos)):
polarizador = polarizadorLineal(theta = angulos[i])
M = M * polarizador

#Vector de Jones a la salida del sistema de N polarizadores bajo incidencia de luz


#polarizada circular dextrogira
vectorFinal = M * luzCircular(tipo = ’d’)
return intensidad(vectorFinal)

def efectoZenon(numMaxPolarizadores = 5):


#Numero de polarizadores
numPolarizadores = sp.array(range(2, numMaxPolarizadores + 1))
#Intensidad a la salida, se preinicializa con el zeros y posteriormente se calcula
intensidad = sp.zeros(len(numPolarizadores), dtype = float)
for i in range(len(numPolarizadores)):
intensidad[i] = nPolarizadores(N = numPolarizadores[i])

#Representacion de la intensidad
plt.figure()
plt.plot(numPolarizadores, intensidad, ’ko’)
plt.xlabel(’# polarizadores’, fontsize = 20)
plt.ylabel(’$I_{transmitida}$’, fontsize = 24)
plt.title(’efecto zenon’, fontsize = 24)

if __name__ == ’__main__’:
efectoZenon(numMaxPolarizadores = 75)
plt.show()

88 Capítulo 6. Polarización
PIMCD2013-python, Publicación 1.0

0.5 efecto zenon

0.4
Itransmitida

0.3

0.2

0.1

0.00 10 20 30 40 50 60 70 80
# polarizadores

6.8. Efecto Zenon 89


PIMCD2013-python, Publicación 1.0

90 Capítulo 6. Polarización
CAPÍTULO 7

Interacción radiación-materia

Autor Luis Miguel Sánchez Brea

Contenidos de este capítulo:


Introducción
Dipolo
Resonancia
Densidad espectral
Sección eficaz

Herramientas utilizadas en este tutorial:


numpy: manipulación de datos
scipy: Herramientas matemáticas
Descarga de archivos:
Campo generado por un dipolo - modo dibujo dipolo.py
Campo generado por un dipolo - genera contornos dipolo_contour.py
Campo generado por un dipolo - modo animado dipolo_animado.py
Campo generado por un dipolo - modo video dipolo_video.py
Resonancia resonancia.py

7.1 Introducción
#!/usr/local/bin/python
# -*- coding: utf-8 -*-
#------------------------------------
# Autor: Luis Miguel Sanchez Brea
# Fecha 2013/10/24 (version 1.0)
# Licencia: GPL
# Objetivo: Ejemplos de difraccion en campo lejano
#-------------------------------------

import scipy as sp
import matplotlib.pyplot as plt

m = 1 #metros
kg = 1 #kilogramo
sg = 1 #segundos

V = 1 #voltio

91
PIMCD2013-python, Publicación 1.0

C = 1 #culombios
Hz = 1 / sg #hertzio

nm = 1e-9 * m

qe = -1.602176565 * 10 ** -19 * C #carga del electron


me = 9.10938291 * 10 ** -31 * kg #masa del electron

def resonancia(E0 = 1000 * V / m, w0 = 1e15 * Hz, gamma = 1e8 * Hz):

""" (float, float) --> (complex)

- Descripcion: En la interaccion luz-atomo ligado un parametro esencial es el calc


de la distancia etre el electron y el nucleo, pues esto pr
que generara la radiacion dispersada.

- Precondicion: g<<w0

- Entradas:
* w0: frecuencia de resonancia
* gamma: amortiguamiento

- Salidas:
* r: distancia compleja
"""

#frecuencia de la luz. Array con diversas frecuencias en torno a la resonancia


n_puntos = 1000
anchura = 10
w = sp.linspace(w0 - anchura * gamma, w0 + anchura * gamma, n_puntos)

#distancia electron-nucleo
r = (qe / me) * E0 / (w ** 2 - w0 ** 2 - 1.j * gamma * w)

#diversos dibujos

#distancia
plt.figure(figsize = (11, 9), facecolor = ’w’, edgecolor = ’k’)
plt.subplot(221)
plt.plot(w, abs(r) / nm, ’k’, linewidth = 2)
plt.xlabel(’$\omega \; (Hz)$’, fontsize = 18)
plt.ylabel(’$r\;(nm)$’, fontsize = 18)

#desfase
plt.subplot(222)
plt.plot(w, sp.angle(r) * 180 / sp.pi, ’k’, linewidth = 2)
plt.xlabel(’$\omega \; (Hz)$’, fontsize = 18)
plt.ylabel(’$\phi \;(grados)$’, fontsize = 18)

#parte real
plt.subplot(223)
plt.plot(w, sp.real(r), ’k’, linewidth = 2)
plt.xlabel(’$\omega \; (Hz)$’, fontsize = 18)
plt.ylabel(’$Re(r)$’, fontsize = 18)

#parte imaginaria
plt.subplot(224)
plt.plot(w, sp.imag(r), ’k’, linewidth = 2)
plt.xlabel(’$\omega \; (Hz)$’, fontsize = 18)
plt.ylabel(’$Im(r)$’, fontsize = 18)

92 Capítulo 7. Interacción radiación-materia


PIMCD2013-python, Publicación 1.0

resonancia(E0 = 1000 * V / m, w0 = 1e15 * Hz, gamma = 1e8 * Hz)


plt.tight_layout()

plt.show()

1.8 0
1.6 20
1.4 40
1.2 60

φ (grados)
r (nm)

1.0 80
0.8 100
0.6 120
0.4 140
0.2 160
0.00.0 0.5 1.0 1.5 2.0 1800.0 0.5 1.0 1.5 2.0
ω (Hz) 1e9+9.99999e14 ω (Hz) 1e9+9.99999e14

1.0 1e 9 0.0 1e 9
0.2
0.5 0.4
0.6
0.8
Im(r)
Re(r)

0.0
1.0
1.2
0.5 1.4
1.6
1.00.0 0.5 1.0 1.5 2.0 1.80.0 0.5 1.0 1.5 2.0
ω (Hz) 1e9+9.99999e14 ω (Hz) 1e9+9.99999e14

7.2 Dipolo

Una carga eléctrica oscilante genera un campo electromagnético. En el caso de un movimiento sinusoidal de
frecuencia w, el campo generado resulta

−𝑞𝑟𝑜 𝑘 3 2 2𝑖
𝐸𝑟 = cos 𝜃( − )𝑒𝑖(𝑘𝑅−𝜔𝑡)
4𝜋𝜀𝑜 (𝑘𝑅)3 (𝑘𝑅)2

−𝑞𝑟𝑜 𝑘 3 1 𝑖 1 𝑖(𝑘𝑅−𝜔𝑡)
𝐸𝜃 = sin 𝜃( − − )𝑒
4𝜋𝜀𝑜 (𝑘𝑅)3 (𝑘𝑅)2 𝑘𝑅

𝐸𝜑 = 0

Esto es lo que se ha programado en las siguientes figuras, para el caso de un plano y para un instante de tiempo
t=0.

7.2. Dipolo 93
PIMCD2013-python, Publicación 1.0

#!/usr/local/bin/python
# -*- coding: utf-8 -*-
#------------------------------------
# Autor: Luis Miguel Sanchez Brea
# Fecha 2013/10/24 (version 1.0)
# Licencia: GPL
# Objetivo: Emision del dipolo
#-------------------------------------

import scipy as sp
import matplotlib
import matplotlib.pyplot as plt
import matplotlib.cm as cm

um=1; mm=1000*um; m=1000*mm; s=1; fs=1e-15*s

wavelength=0.6328*um
k = 2 * sp.pi / wavelength
c= 299792.458 *m/s
w=c*k
q=1; r0=1; epsilon0=1; #normalizacion

#area de visualizacion
tamano=2.5*um
x=sp.linspace(-tamano/2, tamano/2, 250)
z=sp.linspace(-tamano/2, tamano/2, 250)
X,Z=sp.meshgrid(x, z)

#calculo de la intensidad
R = sp.sqrt(X** 2 + Z ** 2)
sinTheta=X/R

E_theta=q*r0*k**2/(4*sp.pi*R*epsilon0)*sinTheta*sp.cos((k*R))
intensidad = abs(E_theta) ** 2
intensidad=sp.log(intensidad+1)

#Se dibuja por primera vez


fig = plt.figure(1)
a = plt.subplot(111)
im = a.imshow(intensidad, cmap=cm.jet)
plt.show()

94 Capítulo 7. Interacción radiación-materia


PIMCD2013-python, Publicación 1.0

50

100

150

200

0 50 100 150 200

contourf contour
4 4

2 2

0 0

2 2

4 4
4 2 0 2 4 4 2 0 2 4

0.0 1.5 3.0 4.5 6.0 7.5 9.0 10.512.0 1.5 3.0 4.5 6.0 7.5 9.0 10.5

Como la fluctuación es temporal, en los siguientes archivos se muestra este movimiento. Uno de ellos genera una

7.2. Dipolo 95
PIMCD2013-python, Publicación 1.0

animación y el otro un vídeo:

50

100

150

200

0 50 100 150 200

7.3 Resonancia

El efecto que tiene la luz sobre el movimiento de los electrones depende fuertemente de la frecuencia de la
onda electromagnética incidente en comparación con la frecuencia de resonancia del electrón en el átomo. En la
siguiente figura se muestra cuál es el desplazamiento máximo producido en el átomo en funcion de los parámetros
del átomo y de la frecuencia de la luz incidente.
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# -*- coding: cp1252 -*- " Codigo para poder usar tildes"
#------------------------------------
# Autor: Luis Miguel Sanchez Brea
# Fecha 2013/10/24 (version 1.0)
# Licencia: GPL
#-------------------------------------

from __future__ import division


import locale
locale.setlocale(locale.LC_NUMERIC, ’C’)

import scipy as sp
import matplotlib.pyplot as plt

m = 1 #metros;
kg = 1 #kilogramo
sg = 1 #segundos
V = 1 #voltio
C = 1 #culombios

96 Capítulo 7. Interacción radiación-materia


PIMCD2013-python, Publicación 1.0

Hz = 1 / sg #hertzio
nm = 1e-9 * m
qe = -1.602176565 * 10 ** -19 * C #carga del electron
me = 9.10938291 * 10 ** -31 * kg #masa del electron

def resonancia(E0 = 1000 * V / m, w0 = 1e15 * Hz, gamma = 1e8 * Hz):


""" (float, float) --> (complex)
- Descripcion: En la interaccion luz-atomo ligado un parametro esencial es el calc
de la distancia entre el electron y el nucleo, pues esto produce el dipolo
que generara la radiacion dispersada.
- Precondicion: g<<w0
- Entradas: * w0: frecuencia de resonancia * gamma: amortiguamiento
- Salidas: * r: distancia compleja"""
#frecuencia de la luz. Array con diversas frecuencias en torno a la resonancia
n_puntos = 1000
anchura = 10
w = sp.linspace(w0 - anchura * gamma, w0 + anchura * gamma, n_puntos)
#distancia electron-nucleo
r = (qe / me) * E0 / (w ** 2 - w0 ** 2 - 1.j * gamma * w)
#distancia
plt.figure(figsize = (11, 5), facecolor = ’w’, edgecolor = ’k’)
plt.subplot(121)
plt.plot(w, abs(r) / nm, ’k’, linewidth = 2)
plt.title("r" , fontsize=24)
plt.xlabel(’$\omega \; (Hz)$’, fontsize = 18)
plt.ylabel(’$r\;(nm)$’, fontsize = 18)
#desfase
plt.subplot(122)
plt.plot(w, sp.angle(r) * 180 / sp.pi, ’k’, linewidth = 2)
plt.xlabel(’$\omega \; (Hz)$’, fontsize = 18)
plt.ylabel(’$\phi \;(grados)$’, fontsize = 18)
plt.title("desfase" , fontsize=24)
return r

resonancia(E0 = 1000 * V / m, w0 = 1e15 * Hz, gamma = 1e8 * Hz)


plt.tight_layout()
plt.show()

1.8 r 0 desfase
1.6 20
1.4 40
1.2 60
φ (grados)
r (nm)

1.0 80
0.8 100
0.6 120
0.4 140
0.2 160
0.00.0 0.5 1.0 1.5 2.0 1800.0 0.5 1.0 1.5 2.0
ω (Hz) 1e9+9.99999e14 ω (Hz) 1e9+9.99999e14

7.3. Resonancia 97
PIMCD2013-python, Publicación 1.0

7.4 Densidad espectral

la densidad de energía espectral, i.e, la cantidad de energía electromagnética por unidad espectral que llega por
unidad de superficie
𝛾 2 /4
𝐽(𝜔) = 𝜀𝑜 𝑐|𝐸𝜔 |2 = 𝐽(𝜔𝑜 ) ,
𝛾 2 /4 + (𝜔 − 𝜔𝑜 )2
donde 𝛾 es el campo a la salida del elemento difra, 𝜔𝑜 es la frecuencia de resonancia y 𝜔 es la frecuencia del haz
incidente
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# -*- coding: cp1252 -*- " Codigo para poder usar tildes"
#------------------------------------
# Autor: Luis Miguel Sanchez Brea
# Fecha 2013/10/24 (version 1.0)
# Licencia: GPL
#-------------------------------------

from __future__ import division


import locale
locale.setlocale(locale.LC_NUMERIC, ’C’)

import scipy as sp
import matplotlib.pyplot as plt

m = 1 #metros;
kg = 1 #kilogramo
sg = 1 #segundos
V = 1 #voltio
C = 1 #culombios
Hz = 1 / sg #hertzio
nm = 1e-9 * m
qe = -1.602176565 * 10 ** -19 * C #carga del electron
me = 9.10938291 * 10 ** -31 * kg #masa del electron
epsilon0=8.8541878176e-12
c=3e8*m/sg

def densidadEspectral(E0 = 1000 * V / m, w0 = 1e15 * Hz, gamma = 1e8 * Hz):


""" (float, float) --> (complex)

- Descripcion: En la interaccion luz-atomo ligado un parametro esencial es el calc


de la distancia entre el electron y el nucleo, pues esto produce el dipolo
que generara la radiacion dispersada.
- Precondicion: g<<w0
- Entradas: * w0: frecuencia de resonancia * gamma: amortiguamiento
- Salidas: * r: distancia compleja"""

#frecuencia de la luz. Array con diversas frecuencias en torno a la resonancia


n_puntos = 1000
anchura = 10
w = sp.linspace(w0 - anchura * gamma, w0 + anchura * gamma, n_puntos)

#distancia electron-nucleo
J=epsilon0*c*abs(E0)**2/(gamma**2/4+(w-w0)**2)

#distancia
plt.figure(facecolor = ’w’, edgecolor = ’k’)
plt.plot(w, J, ’k’, linewidth = 2)
plt.xlabel(’$\omega \; (Hz)$’, fontsize = 18)
plt.ylabel(’$J(\omega)$’, fontsize = 18)

98 Capítulo 7. Interacción radiación-materia


PIMCD2013-python, Publicación 1.0

densidadEspectral(E0 = 1000 * V / m, w0 = 1e15 * Hz, gamma = 1e8 * Hz)


plt.tight_layout()
plt.show()

1.2 1e 12

1.0

0.8
J ( ω)

0.6

0.4

0.2

0.00.0 0.5 1.0 1.5 2.0


ω (Hz) 1e9+9.99999e14

Este parámetro está relacionado con el espectro emitido por el átomo.

7.5 Sección eficaz

Esta sección eficaz de extinción la denotamos 𝜎𝑒 y su relación con la potencia extraída es la siguiente

⟨𝑃 ⟩ 𝑞2 𝛾𝜔 2
𝜎𝑒 = =
⟨|𝑆|⟩ 𝑚𝑐𝜀𝑜 (𝜔 2 − 𝜔𝑜2 )2 + 𝜔 2 𝛾 2

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# -*- coding: cp1252 -*- " Codigo para poder usar tildes"
#------------------------------------
# Autor: Luis Miguel Sanchez Brea
# Fecha 2013/10/24 (version 1.0)
# Licencia: GPL
#-------------------------------------

from __future__ import division


import locale
locale.setlocale(locale.LC_NUMERIC, ’C’)

import scipy as sp
import matplotlib.pyplot as plt

7.5. Sección eficaz 99


PIMCD2013-python, Publicación 1.0

m = 1; kg = 1; s = 1; V = 1; C = 1; Hz = 1 / s
nm = 1e-9 * m
qe = -1.602176565e-19 * C #carga del electron
me = 9.10938291e-31 * kg #masa del electron
epsilon0=8.8541878176e-12
c= 299792458*m/s

def seccion_eficaz_extincion(w0 = 1e15 * Hz, gamma = 1e8 * Hz):


""" (float, float) --> (complex)
- Descripcion: En la interaccion luz-atomo ligado un parametro esencial es el calc
de la distancia entre el electron y el nucleo, pues esto produce el dipolo que generara la
- Precondicion: g<<w0: Entradas: * w0: frecuencia de resonancia * gamma: amo
- Salidas: * r: distancia compleja"""
#frecuencia de la luz. Array con diversas frecuencias en torno a la resonancia
n_puntos = 1000
anchura = 10
w = sp.linspace(w0 - anchura * gamma, w0 + anchura * gamma, n_puntos)
#seccion eficaz
sigma=(qe**2/(me*c*epsilon0))*gamma*w**2/((w**2-w0**2)**2+w**2*gamma**2)
#distancia
plt.figure(facecolor = ’w’, edgecolor = ’k’)
plt.plot(w, sigma, ’k’, linewidth = 2)
plt.xlabel(’$\omega \; (Hz)$’, fontsize = 18)
plt.ylabel(’$\sigma(\omega)$’, fontsize = 18)
plt.title("Seccion eficaz extincion", fontsize=24)
texto="radio = %4.4g m" %(sp.sqrt(sigma.max()/sp.pi))
plt.text(w.min(),sigma.max()/2,texto,fontsize=16)
plt.tight_layout()

def seccion_eficaz_scattering(w0 = 1e15 * Hz, gamma = 1e8 * Hz):


""" (float, float) --> (complex)
- Descripcion: En la interaccion luz-atomo ligado un parametro esencial es el calc
de la distancia entre el electron y el nucleo, pues esto produce el dipolo que generara la
- Precondicion: g<<w0: Entradas: * w0: frecuencia de resonancia * gamma: amo
- Salidas: * r: distancia compleja"""
#frecuencia de la luz. Array con diversas frecuencias en torno a la resonancia
n_puntos = 1000
anchura = 10
w = sp.linspace(w0 - anchura * gamma, w0 + anchura * gamma, n_puntos)
#seccion eficaz
sigma_T=qe**4/(6*sp.pi*me**2*c**4*epsilon0**2)

sigma=sigma_T*w**4/((w**2-w0**2)**2+w**2*gamma**2)
#distancia
plt.figure(facecolor = ’w’, edgecolor = ’k’)
plt.plot(w, sigma, ’k’, linewidth = 2)
plt.xlabel(’$\omega \; (Hz)$’, fontsize = 18)
plt.ylabel(’$\sigma(\omega)$’, fontsize = 18)
plt.title("Seccion eficaz de scattering", fontsize=24)
texto="radio = %4.4g m" %(sp.sqrt(sigma_T/sp.pi))
plt.text(w.min(),sigma.max()/2,texto,fontsize=16)
plt.tight_layout()

seccion_eficaz_extincion(w0 = 1e15 * Hz, gamma = 1e8 * Hz)


seccion_eficaz_scattering(w0 = 1e15 * Hz, gamma = 1e8 * Hz)

plt.show()

100 Capítulo 7. Interacción radiación-materia


PIMCD2013-python, Publicación 1.0

1.0 1e 49 Seccion eficaz


0.8

0.6
σ(ω)

0.4

0.2

0.00.0 0.5 1.0 1.5 2.0


ω (Hz) 1e15

7.5. Sección eficaz 101


PIMCD2013-python, Publicación 1.0

102 Capítulo 7. Interacción radiación-materia


CAPÍTULO 8

Índice de refracción: modelo microscópico

Autor Luis Miguel Sánchez Brea

Contenidos de este capítulo:


Introducción
Índice de refracción para cargas ligadas
Aproximación de Sellmeier
Aproximación de Cauchy
Frecuencia de plasma

Herramientas utilizadas en este tutorial:


numpy: manipulación de datos
scipy: Herramientas matemáticas
Descarga de archivos:
Ejemplo de interferencias con ondas planas resonancia.py
Ejemplo de interferencias con ondas planas indice_refraccion.py
Ejemplo de interferencias con ondas planas indice_cauchy.py
Ejemplo de interferencias con ondas planas indice_sellsmeier.py
Ejemplo de interferencias con ondas planas frecuencia_plasma.py

8.1 Introducción

Los ejemplos que aquí se desarrollan son autocontenidos, es decir, no necesitan de librerías especificas excepto
las estándar. El objetivo de este tema es, por una parte, conocer la relación entre el modelo microscópico clásico
de interacción radiación-materia con el índice de refracción. Asimismo, se pretende analizar diversas expresiones
analíticas para el índice de refracción, conociendo las propiedades del índice de refracción tales como la absorción
y la dispersión.
Se compara las definiciones macroscópica y microscópica de la polarización. La primera se calcula a través las
ecuaciones macroscópicas de Maxwell.
P = 𝜀0 𝜒𝑒 E𝑚𝑎𝑐 ,
La segunda se calcula a partir de las trayectorias de los átomos analizadas en el átomo de Lorentz. Mediante esta
comparación se obtiene una ecuación para la constante dieléctrica en función de los parámetros microscópicos:
𝑁
𝑁 1 ∑︁
P=𝛼 E𝑚𝑖𝑐,𝑗 .
∆𝑉 𝑁 𝑗=1
⏟ ⏞

103
PIMCD2013-python, Publicación 1.0

De esta forma se calcula la constante dieléctrica


⎛ ⎞
2 ∑︁
𝑞 𝑓 𝑗
𝜀 = 𝜀0 ⎝1 + 𝑁𝑉 ⎠,
𝑚𝜖0 𝑗 𝜔02 − 𝜔 2 − 𝑖𝛾𝑗 𝜔

Como también es conocido, se puede obtener el índice de refracción a partir de la constante dieléctrica
√︂
𝜀𝑔𝑒𝑛
𝑛𝑐 = .
𝜀0

8.2 Índice de refracción para cargas ligadas

En el caso de que no haya cargas libres, es decir, que el medio sea dieléctrico, el índice de refracción se puede
simplificar a
⎛ ⎞
2 ∑︁
𝑞 𝑓𝑗
𝜀 = 𝜀0 ⎝1 + 𝑁𝑉 ⎠,
𝑚𝜖0 𝑗 𝜔02 − 𝜔 2 − 𝑖𝛾𝑗 𝜔

donde la sumatoria representa los distintos tipos de resonancias que existen en el material.
Sacando las partes real e imaginaria se puede obtener el índice de refracción y de absorción.
2
𝑞 2 ∑︁ (𝜔0,𝑗 − 𝜔 2 )𝑓𝑗
𝑛 = 1 + 𝑁𝑉 2 − 𝜔 2 )2 + 𝛾 2 𝜔 ,
2𝑚𝜖0 𝑗 (𝜔0𝑗 𝑗

𝑞 2 ∑︁ 𝜔𝛾𝑗 𝑓𝑗
𝜅 = 𝑁𝑉 2 − 𝜔 2 )2 + 𝛾 2 𝜔 2 .
2𝑚𝜖0 𝑗 (𝜔0𝑗 𝑗

En el siguiente ejemplo se muestra el índice de refracción para dos ejemplos. En el primero se muestra el índice
de refracción debido solamente a una resonancia.
En el segundo ejemplo se muestra el índice de refracción cuando el medio tiene 3 resonancias. Se observa un
carácter acumulativo. Para bajas frecuencias, el índice de refracción no es la unidad, sino mayor, mientras que
para altas frecuencias, por encima de la resonancia, la luz no observa la materia y el índice de refracción es la
unidad.
#!/usr/local/bin/python
# -*- coding: utf-8 -*-
#------------------------------------
# Autor: Luis Miguel Sanchez Brea
# Fecha 2013/10/24 (version 1.0)
# Licencia: GPL
# Objetivo: Ejemplos de difraccion en campo lejano
#-------------------------------------

import scipy as sp
import matplotlib.pyplot as plt

m = 1; kg = 1; sg = 1; V = 1; C = 1;
Hz = 1 / sg; nm = 1e-9 * m

q=1.6021765e-19*C
m=9.109382911e-31*kg
epsilon0=8.8541878176e-12

def indice_refraccion(Nv=1e15, f=(0.5,), w0 = (1e15 * Hz,), gamma = (1e8 * Hz,)):

#frecuencia de la luz. Array con diversas frecuencias en torno a la resonancia

104 Capítulo 8. Índice de refracción: modelo microscópico


PIMCD2013-python, Publicación 1.0

n_puntos = 1000
anchura = 20
w = sp.linspace(min(w0) - anchura * max(gamma), max(w0) + anchura * max(gamma), n_puntos)

#distancia electron-nucleo
n2=1

for fj, w0j, gammaj in zip(f,w0,gamma):


n2=n2+Nv*q**2/(m*epsilon0)*fj/(w0j**2-w**2-1j*w*gammaj)

nc=sp.sqrt(n2)
n=sp.real(nc)
kappa=sp.imag(nc)

plt.figure()
#parte real
plt.subplot(211)
plt.plot(w, n, ’k’, linewidth = 2)
plt.xlabel(’$\omega \; (Hz)$’, fontsize = 18)
plt.xlim(max(0,w[0]),w[-1])
plt.ylabel(’$n$’, fontsize = 18)
plt.grid(True)

#parte imaginaria
plt.subplot(212)
plt.plot(w, kappa, ’k’, linewidth = 2)
plt.xlabel(’$\omega \; (Hz)$’, fontsize = 18)
plt.xlim(max(0,w[0]),w[-1])
plt.ylabel(’$\kappa$’, fontsize = 18)
plt.grid(True)
plt.tight_layout()

#ejemplo de 1 resonancia
indice_refraccion(Nv=1e18, f=(1,), w0 = (1e13 * Hz,), gamma = (1e8 * Hz,))

#ejemplo con 2 resonancias


indice_refraccion(Nv=3e24, f=(0.33,0.33), w0 = (2e14 * Hz,4e14*Hz), gamma = (1e12 * Hz,1e12 * Hz))

#muchas resonancias
f=(0.1,0.1,0.1,0.1,0.1,0.1,0.1,0.1,0.1)
w0= sp.array([1.0,1.2,1.4,1.6,1.8,2.0,2.2,2.4,2.6,2.8])*4e14*Hz
gamma= sp.array([1.0,1.1,1.2,1.3,1.4,1.5,1.6,1.7,1.8,1.9,2.0])*1e13*Hz
indice_refraccion(Nv=1e26, f=f, w0 = w0, gamma = gamma)

plt.show()

8.2. Índice de refracción para cargas ligadas 105


PIMCD2013-python, Publicación 1.0

1.8
1.6
1.4
1.2
n

1.0
0.8
0.6
0.40.0 0.5 1.0 1.5 2.0 2.5 3.0 3.5 4.0
ω (Hz) 1e9+9.998e12
1.2
1.0
0.8
0.6
κ

0.4
0.2
0.00.0 0.5 1.0 1.5 2.0 2.5 3.0 3.5 4.0
ω (Hz) 1e9+9.998e12

3.5
3.0
2.5
2.0
n

1.5
1.0
0.5
0.0 2.0 2.5 3.0 3.5 4.0
ω (Hz) 1e14
3.5
3.0
2.5
2.0
κ

1.5
1.0
0.5
0.0 2.0 2.5 3.0 3.5 4.0
ω (Hz) 1e14

106 Capítulo 8. Índice de refracción: modelo microscópico


PIMCD2013-python, Publicación 1.0

3.0
2.5
2.0
1.5
n

1.0
0.5
0.00.0 0.2 0.4 0.6 0.8 1.0 1.2 1.4
ω (Hz) 1e15
2.0
1.5
1.0
κ

0.5
0.00.0 0.2 0.4 0.6 0.8 1.0 1.2 1.4
ω (Hz) 1e15

8.3 Aproximación de Sellmeier

La ecuación anteriormente descrita es interesante, pero a la hora de trabajar con ella es mejor realizar algún tipo
de aproximación. En primer lugar porque en Óptica se suele trabajar con longitudes de onda y no con frecuencias.
La segunda es para hacer casar las ecuaciones de la dispersión de materiales, conocidas previamente y utilizadas
en la óptica geométrica.
Una de ellas es la ec. de Sellmeier.
𝑏𝜆2 𝑐𝜆2
𝑛2 (𝜆) − 1 = 𝑎 + + + ...
𝜆2 − 𝜆21 𝜆2 − 𝜆22

Se pueden encontrar los parámetros en muchas páginas web. Por ejemplo, hemos utilizado los siguientes materia-
les bastante comunes
Fused_Silica_B=(6.96166300E-01, 4.07942600E-01, 8.97479400E-01)
Fused_Silica_C=(4.67914826E-03, 1.35120631E-02, 9.79340025E01)

BK7_B=(1.03961212E00, 2.31792344E-01, 1.01046945E00)


BK7_C=(6.00069867E-03, 2.00179144E-02, 1.03560653E02)

MgF_B=(4.13440230E-01, 5.04974990E-01, 2.49048620E00)


MgF_C=(1.35737865E-03, 8.23767167E-03, 5.65107755E02)

Zafiro_B=(1.50397590E00, 5.50691410E-01, 6.59273790E00)


Zafiro_C=(5.48041129E-03, 1.47994281E-02, 4.02895140E02)

Veamos algunas de las gráficas y el código para programarlos. Este parámetro se denomina dispersión cromática.

8.3. Aproximación de Sellmeier 107


PIMCD2013-python, Publicación 1.0

#!/usr/local/bin/python
# -*- coding: utf-8 -*-
#------------------------------------
# Autor: Luis Miguel Sanchez Brea
# Fecha 2013/10/24 (version 1.0)
# Licencia: GPL
# Objetivo: Ejemplos de difraccion en campo lejano
#-------------------------------------

import scipy as sp
import matplotlib.pyplot as plt

wavelength=sp.linspace(0.4, 1 ,1000)

MgF_B=(4.13440230E-01, 5.04974990E-01, 2.49048620E00)


MgF_C=(1.35737865E-03, 8.23767167E-03, 5.65107755E02)

Zafiro_B=(1.50397590E00, 5.50691410E-01, 6.59273790E00)


Zafiro_C=(5.48041129E-03, 1.47994281E-02, 4.02895140E02)

Fused_Silica_B=(6.96166300E-01, 4.07942600E-01, 8.97479400E-01)


Fused_Silica_C=(4.67914826E-03, 1.35120631E-02, 9.79340025E01)

BK7_B=(1.03961212E00, 2.31792344E-01, 1.01046945E00)


BK7_C=(6.00069867E-03, 2.00179144E-02, 1.03560653E02)
print sp.sqrt(Zafiro_C)

#la longitud de onda debe ser en micras

#no es la definicion de clase sino wikipedia - que esta en otros

def indice_refraccion(wavelength, B,C):

i=2
n2=sp.zeros_like(wavelength)+1
for bi,ci in zip(B,C):
n2=n2+bi*wavelength**2/(wavelength**2-ci)

n=sp.sqrt(n2)
return n

def dibujar_n(wavelength,n, titulo=’’):


#diversos dibujos
plt.figure()
plt.plot(wavelength, n, ’k’, linewidth = 2)
plt.xlabel(’$\lambda (\mu m)$’, fontsize = 18)
plt.xlim(wavelength[0],wavelength[-1])
plt.ylabel(’$n$’, fontsize = 18)
plt.title(titulo, fontsize=20)
plt.grid(True)

n=indice_refraccion(wavelength, BK7_B,BK7_C)
dibujar_n(wavelength,n,’Sellmeier: BK7’)

n=indice_refraccion(wavelength, Fused_Silica_B,Fused_Silica_C)
dibujar_n(wavelength,n,’Sellmeier: Fused_Silica’)

n=indice_refraccion(wavelength, Zafiro_B,Zafiro_C)

108 Capítulo 8. Índice de refracción: modelo microscópico


PIMCD2013-python, Publicación 1.0

dibujar_n(wavelength,n,’Sellmeier: Zafiro’)

"""
#tambien se puede obtener el valor para 1 dato solo, sin dibujar
wavelength=0.5
n05=indice_refraccion(wavelength, BK7_B,BK7_C)
print "el indice de refraccion BK7 para %4.4f um es %4.6f" %(wavelength,n05)
"""

plt.show()

1.535 Sellmeier: BK7

1.530

1.525

1.520
n

1.515

1.510

1.5050.4 0.5 0.6 0.7 0.8 0.9 1.0


λ(µm)

8.3. Aproximación de Sellmeier 109


PIMCD2013-python, Publicación 1.0

1.475 Sellmeier: Fused_Silica

1.470

1.465
n

1.460

1.455

1.4500.4 0.5 0.6 0.7 0.8 0.9 1.0


λ(µm)

1.780 Sellmeier: Zafiro


1.775

1.770

1.765
n

1.760

1.755

1.750

1.7450.4 0.5 0.6 0.7 0.8 0.9 1.0


λ(µm)
A partir de esta ecuación se puede calcular el número de Abbe, que es un parámetro para calcular la dispersión del

110 Capítulo 8. Índice de refracción: modelo microscópico


PIMCD2013-python, Publicación 1.0

material.

8.4 Aproximación de Cauchy

Otra de las expresiones típicas para ajustar el indice de refracción con la longitud de onda es la ecuación de
Cauchy,
𝐵 𝐶
𝑛(𝜆) = 𝐴 + + 4 + ..
𝜆2 𝜆

#!/usr/local/bin/python
# -*- coding: utf-8 -*-
#------------------------------------
# Autor: Luis Miguel Sanchez Brea
# Fecha 2013/10/24 (version 1.0)
# Licencia: GPL
# Objetivo: Ejemplos de difraccion en campo lejano
#-------------------------------------

import scipy as sp
import matplotlib.pyplot as plt

m = 1; kg = 1; sg = 1; V = 1; C = 1;
Hz = 1 / sg; nm = 1e-9 * m; um=1e-6

q=1.6021765e-19*C
m=9.109382911e-31*kg
epsilon0=8.8541878176e-12

wavelength=sp.linspace(400*nm, 1000*nm,1000)

Fused_silica=(1.4580,0.00354*um**2)
Borosilicate_BK7=(1.5046,0.00420*um**2)
Hard_crown_K5=(1.5220,0.00459*um**2)
Barium_crown_BaK4 =(1.5690,0.00531*um**2)
Barium_BaF10=(1.6700,0.00743*um**2)
Dense_flint_SF10=(1.7280,0.01342*um**2)

def indice_refraccion(wavelength, param):


i=0
n=sp.zeros_like(wavelength)
for letra in param:
n=n+letra/wavelength**i
i=i+2
return n

def dibujar_n(wavelength,n, titulo=’’):


plt.figure()
plt.plot(wavelength/nm, n, ’k’, linewidth = 2)
plt.xlabel(’$\lambda (nm)$’, fontsize = 18)
plt.xlim(wavelength[0]/nm,wavelength[-1]/nm)
plt.ylabel(’$n$’, fontsize = 18)
plt.title(titulo, fontsize=20)
plt.grid(True)

#ejemplo de 1 resonancia
n=indice_refraccion(wavelength, Fused_silica)
dibujar_n(wavelength,n, titulo=’Cauchy: Fused_silica’)

8.4. Aproximación de Cauchy 111


PIMCD2013-python, Publicación 1.0

n=indice_refraccion(wavelength, Borosilicate_BK7)
dibujar_n(wavelength,n, titulo=’Cauchy: Borosilicate_BK7’)

"""
wavelength=0.5
n05=indice_refraccion(wavelength,Borosilicate_BK7)
print "el indice de refraccion BK7 para %4.4f um es %4.6f" %(wavelength,n05)
"""

plt.show()

1.485 Cauchy: Fused_silica

1.480

1.475
n

1.470

1.465

1.460400 500 600 700 800 900 1000


λ(nm)

112 Capítulo 8. Índice de refracción: modelo microscópico


PIMCD2013-python, Publicación 1.0

1.535 Cauchy: Borosilicate_BK7

1.530

1.525

1.520
n

1.515

1.510

1.505400 500 600 700 800 900 1000


λ(nm)

8.5 Frecuencia de plasma

En los medios conductores tenemos cargas ligadas (electrones internos) y cargas libres. Sin embargo, éstos últimos
son los que más afectan al índice de refracción. Por ello, suponemos que sólo hay cargas libres. La ecuación de
índice de refracción se simplifica a

𝑞2 1
𝑛2𝑐 = 1 − 𝑁𝑉′ .
𝑚𝜖0 𝜔 (𝜔 + 𝑖𝛾)

Esta expresión se puede aproximar a

𝜔𝑝2
𝑛2𝑐 (𝜔) ≈ 1 − .
𝜔2
donde
√︃
𝑞2
𝜔𝑝 = 𝑁𝑉′
𝑚𝜖0

Veamos algunos ejemplos con la expresión sin simplificar:


#!/usr/local/bin/python
# -*- coding: utf-8 -*-
#------------------------------------
# Autor: Luis Miguel Sanchez Brea
# Fecha 2013/10/24 (version 1.0)
# Licencia: GPL
# Objetivo: Frecuencia del plasma
#-------------------------------------

8.5. Frecuencia de plasma 113


PIMCD2013-python, Publicación 1.0

import scipy as sp
import matplotlib.pyplot as plt

m = 1; kg = 1; sg = 1; V = 1; C = 1;
Hz = 1 / sg; nm = 1e-9 * m; um=1e-6

q=1.6021765e-19*C; m=9.109382911e-31*kg
epsilon0=8.8541878176e-12; c=299792458*m/sg

def indice_refraccion(w, Nv, gamma):


nc2=1-Nv*q**2/(m*epsilon0*w*(w+1j*gamma))
nc=sp.sqrt(nc2)
return nc

def dibujar_n(w,nc, wp=0):


plt.figure()
n=sp.real(nc)
kappa=sp.imag(nc)

plt.subplot(211) #parte real

plt.plot(w, n, ’k’, linewidth = 2)


plt.xlabel(’$\omega \; (Hz)$’, fontsize = 18)
plt.xlim(w[0],w[-1])
plt.ylabel(’$n$’, fontsize = 18)
plt.grid(True)

plt.subplot(212) #parte imaginaria


plt.plot(w, kappa, ’k’, linewidth = 2)
plt.xlabel(’$\omega \; (Hz)$’, fontsize = 18)
plt.xlim(w[0],w[-1])
plt.ylabel(’$\kappa$’, fontsize = 18)
plt.grid(True)

if wp>0:
plt.subplot(211)
plt.axvline(x=wp, ymin=0, ymax=1, lw=2, color=’k’, linestyle=’--’)
plt.subplot(212)
plt.axvline(x=wp, ymin=0, ymax=1, lw=2, color=’k’, linestyle=’--’)

plt.tight_layout()

def ejemplo(Nv,gamma):
wp=sp.sqrt(Nv*q**2/(m*epsilon0))
""" print "la frecuencia de resonancia es %4.6g" %(wp)
print "la longitud de onda de resonancia es %4.6g" %(2*sp.pi*c/wp)"""
w=sp.linspace(wp/4,wp*4,1000)
nc=indice_refraccion(w, Nv,gamma)
dibujar_n(w,nc,wp)

ejemplo(Nv=1e25, gamma=1e12)
ejemplo(Nv=1e25, gamma=1e14)
plt.show()

114 Capítulo 8. Índice de refracción: modelo microscópico


PIMCD2013-python, Publicación 1.0

1.0
0.8
0.6
n

0.4
0.2
0.0 1 2 3 4 5 6 7
ω (Hz) 1e14
4.0
3.5
3.0
2.5
2.0
κ

1.5
1.0
0.5
0.0 1 2 3 4 5 6 7
ω (Hz) 1e14

1.6
1.4
1.2
1.0
n

0.8
0.6
0.4 1 2 3 4 5 6 7
ω (Hz) 1e14
2.0
1.5
1.0
κ

0.5
0.0 1 2 3 4 5 6 7
ω (Hz) 1e14

8.5. Frecuencia de plasma 115


PIMCD2013-python, Publicación 1.0

116 Capítulo 8. Índice de refracción: modelo microscópico


CAPÍTULO 9

Ecuaciones de Fresnel

autor Luis Miguel Sánchez Brea


revisor José Luis Vilas Prieto

Contenidos de este capítulo:


Introducción
Funciones para calcular las ecuaciones de Fresnel
• Cálculo de los coeficientes de Fresnel: medios dieléctricos
• Cálculo de la transmitancia y reflectancia: medios dieléctricos
• Cálculo de los coeficientes de Fresnel: medios absorbentes
• Cálculo de la transmitancia y reflectancia: medios absorbentes
• Funciones de dibujo
Ejemplos
• Calculo de los coeficientes de fresnel
• Interfaz aire-vidrio
• Interfaz vidrio-aire
• Interfaz aire-metal
Ejercicios
Referencias

Herramientas utilizadas en este tutorial:


scipy: manipulación de arrays de datos
matplotlib.pyplot: generación de dibujos similar a Matlab
Archivos necesarios para las ecuaciones de Fresnel:
fresnel.py. Archivo con las funciones principales fresnel.py
Contenidos de los archivos:
fresnel.py:
• coeficientesFresnel_dielectricos(). Calcula los coeficientes de Fresnel rs, rp, ts y tp
para medios dieléctricos.
• reflectancia_transmitancia_dielectricos(). Calcula las reflectancias y transmitan-
cias R_perp, R_par, T_perp, T_par para medios dieléctricos
• coeficientesFresnel_complejo(). Calcula los coeficientes de Fresnel rs, rp, ts y tp para
medios absorbentes.
• reflectancia_transmitancia_complejo(). Calcula las reflectancias y transmitancias
R_perp, R_par, T_perp, T_par para medios absorbentes.
• dibujarCoeficientes(). Dibuja los coeficientes rs, rp, ts y tp.
• dibujarTransmitancias(). Dibuja los coeficientes R_perp, R_par, T_perp, T_par.

117
PIMCD2013-python, Publicación 1.0

Descarga de archivos:
fresnel.py. Archivo con las funciones principales fresnel.py
fresnel_aire_vidrio.py. Archivo con el ejemplo del del calculo de la amplitud, fase e intensidad
de las componentes paralela y perpendicular en una intercara aire-vidrio fresnel_aire_vidrio.py
fresnel_vidrio_aire.py. Archivo con el ejemplo del del calculo de la amplitud, fase e intensidad
de las componentes paralela y perpendicular en una intercara aire-vidrio fresnel_vidrio_aire.py
fresnel_aire_metal.py. Archivo con el ejemplo del del calculo de la amplitud, fase e intensidad de
las componentes paralela y perpendicular en una intercara aire-vidrio fresnel_aire_metal.py

9.1 Introducción

Las ecuaciones de Fresnel permiten calcular los campos transmitido y reflejado cuando una onda armónica plana
incide sobre una interfaz entre dos medios con índice de refracción distinto. Tienen una dependencia del estado
de polarización del haz incidente (paralelo al plano de incidencia, perpendicular al plano de incidencia o una
combinación), de los índices de refracción del medio de entrada y de salida (𝑛1 , 𝑛2 ) y del ángulo de incidencia 𝜃𝑖 .
El ángulo del haz transmitido 𝜃𝑡 se obtiene a través de la ley de Snell: 𝑛1 𝑠𝑖𝑛(𝜃𝑖 ) = 𝑛2 𝑠𝑖𝑛(𝜃𝑡 ).
Dichos campos reflejados y transmitido son lineales con el campo incidente y los coeficientes de proporcionalidad
son los coeficientes de fresnel. Para el caso de los campos reflejados

𝐸𝑟‖ = 𝑟‖ * 𝐸𝑖 ,

𝐸𝑟⊥ = 𝑟⊥ * 𝐸𝑖 ,

y para los campos transmitidos



𝐸𝑡 = 𝑡‖ * 𝐸𝑖 ,

𝐸𝑡⊥ = 𝑡⊥ * 𝐸𝑖 .

Un análisis riguroso de cómo calcular dichos coeficientes de Fresnel a partir de las ecuaciones de Maxwell se
pueden encontrar en el libro de Cabrera 1 (capítulos 9 y 10). Para el caso de que los dos medios sean dieléctricos
estas ecuaciones resultan ser
𝑛2 𝑐𝑜𝑠(𝜃𝑖 ) − 𝑛1 𝑐𝑜𝑠(𝜃𝑡 )
𝑟‖ = ,
𝑛2 𝑐𝑜𝑠(𝜃𝑖 ) + 𝑛1 𝑐𝑜𝑠(𝜃𝑡 )

𝑛1 𝑐𝑜𝑠(𝜃𝑖 ) − 𝑛2 𝑐𝑜𝑠(𝜃𝑡 )
𝑟⊥ = ,
𝑛1 𝑐𝑜𝑠(𝜃𝑖 ) + 𝑛2 𝑐𝑜𝑠(𝜃𝑡 )

y para los campos transmitidos

2 𝑛1 𝑐𝑜𝑠(𝜃𝑖 )
𝑡‖ = ,
𝑛2 𝑐𝑜𝑠(𝜃𝑖 ) + 𝑛1 𝑐𝑜𝑠(𝜃𝑡 )

2 𝑛1 𝑐𝑜𝑠(𝜃𝑖 )
𝑡⊥ = .
𝑛1 𝑐𝑜𝑠(𝜃𝑖 ) + 𝑛2 𝑐𝑜𝑠(𝜃𝑡 )

Para el caso de que uno de los medios tenga índice de refracción comlejo, es decir sea un medio absorbente,
entonces las ecuaciones son algo más complicadas. Se pueden encontrar en el libro de Cabrera 1 , capítulo 9.
1 J.M. Cabrera, F.J. López, F. Agulló López ; Óptica Electromagnética: Fundamentos; Addison-Wesley (2001) ISBN 9788478290215

118 Capítulo 9. Ecuaciones de Fresnel


PIMCD2013-python, Publicación 1.0

9.2 Funciones para calcular las ecuaciones de Fresnel

En el archivo fresnel.py se han implementado las funciones que permiten el cálculo de los coeficientes y las
reflectancias. Dichas funciones tienen la entrada de los índices de refracción n1, n2 y de un array de números
que representan el ángulo de incidencia. La salida de estas funciones son: r_perp, r_par, t_perp, t_par cuando se
calculan coeficientes y R_perp, R_par, T_perp, T_par cuando se calculan reflectancias o transmitancias. Estos
parámetros los aceptan las funciones para dibujar.
La variable theta_i puede ser un número o un array generado con numpy. De esta forma se simplifican las repre-
sentaciones y se acelera enormemente el tiempo de cálculo. Por consiguiente, las funciones de cálculo devuelven
un array para los coeficientes 𝑟‖ , 𝑟⊥ , 𝑡‖ y 𝑡⊥ cuyo tamaño es igual al 𝜃𝑖 .

9.2.1 Cálculo de los coeficientes de Fresnel: medios dieléctricos

El cálculo de los coefientes de Fresnel en medios dieléctricos se ha implementado, y es mostrado en el siguiente


extracto de código. Para ello se ha creado la función coeficientesFresnel_dielectricos e introducido
las ecuaciones de Fresnel de los coeficientes de reflexión y transmisión para ls variables de entrada del ángulo de
incidencia y los índices de refracción del medio y del material dieléctrico.
def coeficientesFresnel_dielectricos(n1, theta_i, n2):
#
# Calcula las componentes rs, rp, ts y tp mediante las eq. de Fresnel
# solamente se pueden admitir medios dielectricos, es decir indices de refraccion reales.
# Las ecuaciones programadas estan en Cabrera pagina 262

#ley de sell para conseguir theta_t


theta_t=sp.arcsin(n1*sin(theta_i)/n2)

#Precalculo para acelerar los calculos


cos_i=cos(theta_i)
cos_t=cos(theta_t)

#Coeficientes de Fresnell
r_par=(n2*cos_i-n1*cos_t)/(n2*cos_i+n1*cos_t)
r_perp=(n1*cos_i-n2*cos_t)/(n1*cos_i+n2*cos_t)
t_par=(2*n1*cos_i)/(n2*cos_i+n1*cos_t)
t_perp=(2*n1*cos_i)/(n1*cos_i+n2*cos_t)

return r_perp, r_par, t_perp, t_par

9.2.2 Cálculo de la transmitancia y reflectancia: medios dieléctricos

Definidos los coeficientes de Fresnel se puede calcular la transmitancia y la reflectancia entendiendo estas magni-
tudes como la relación entre la intensidad reflejada o transmitida y la intensidad del haz incidente(más detalles en
1
). Así estos parámetros pueden ser derivados de los coeficientes de Fresnell como se muestra en el código a partir
de la función reflectancia_transmitancia_dielectricos.
def reflectancia_transmitancia_dielectricos(n1, theta_i, n2):
""""Cabrera. pag 266"""
#Se calculan lo coeficientes de Fresnel
r_perp, r_par, t_perp, t_par= coeficientesFresnel_dielectricos(n1, theta_i, n2)

#Ley de sell para conseguir theta_t


theta_t=sp.arcsin(n1*sin(theta_i)/n2)

#acelera los calculos


cos_i=cos(theta_i)
cos_t=cos(theta_t)

9.2. Funciones para calcular las ecuaciones de Fresnel 119


PIMCD2013-python, Publicación 1.0

#calculo de transmitancia y reflectancia


R_perp=abs(r_perp)**2
R_par=abs(r_par)**2
T_perp=abs(t_perp)**2*(n2*cos_t)/(n1*cos_i)
T_par=abs(t_par)**2*(n2*cos_t)/(n1*cos_i)

return R_perp, R_par, T_perp, T_par

9.2.3 Cálculo de los coeficientes de Fresnel: medios absorbentes

Los medios absorbentes se caracterizan por tener un índice de refracción complejo. Nuevamente las relaciones de
Fresnel permiten determinar los coeficientes de transmisión y reflexión.
def coeficientesFresnel_complejo(n1, theta_i, n2c):
"""
Calcula las componentes rs y rp mediante las eq. de Fresnel
n^=n-ik
ejemplo:
theta_i=sp.linspace(0*grados,90*grados,10),

Los parametros de entrada pueden ser arrays de numeros.


Para dibujarlos el array debe ser theta_i
n2c puede ser complejo
"""
#Precalculos
kiz=cos(theta_i)
ktcz=sp.sqrt(n2c**2-n1**2*sin(theta_i)**2)
ktc2= n2c**2
ki2 = n1**2

#Calculo de los coeficientes de Fresnel


r_perp= (kiz-ktcz)/(kiz+ktcz)
t_perp= 2*kiz/(kiz+ktcz)
r_par= (kiz*ktc2-ktcz*ki2)/(kiz*ktc2+ktcz*ki2)
t_par= 2*kiz*ktc2/(kiz*ktc2+ktcz*ki2)

theta_i = theta_i/ grados

return r_perp, r_par, t_perp, t_par

9.2.4 Cálculo de la transmitancia y reflectancia: medios absorbentes

En general la intensidad de la onda incidente no es la superposición de la transmitida y la refleja-


da, sino que el material posee una cierta capacidad de absorción de la radiación. Así, la suma de
la absorbancia, la reflectancia y la transmitancia es la unidad conservándose la energía. La función
reflectancia_transmitancia_complejo permite calcular la transmitancia y reflectancia.
def reflectancia_transmitancia_complejo(n1, theta_i, n2c):
"""
Calcula las componentes rs y rp mediante las eq. de Fresnel
n^=n-ik
ejemplo:
theta_i=sp.linspace(0*grados,90*grados,10),

Los parametros de entrada pueden ser arrays de numeros.


Para dibujarlos el array debe ser theta_i
n2c puede ser complejo
"""
#Coeficientes de Fresnel
r_perp, r_par, t_perp, t_par = coeficientesFresnel_complejo(n1, theta_i, n2c)

120 Capítulo 9. Ecuaciones de Fresnel


PIMCD2013-python, Publicación 1.0

#Reflectancia
R_perp=abs(r_perp)**2
R_par=abs(r_par)**2
#Precalculo
kiz=cos(theta_i)
ki2 = n1**2
ktcz=sp.sqrt(n2c**2-n1**2*sin(theta_i)**2)
ktc2=n2c**2
n2R=sp.real(n2c)
kappa2=sp.imag(n2c)
B=n2R**2-kappa2**2-n1**2*sin(theta_i)**2
ktz=sp.sqrt(0.5*(B+sp.sqrt(B**2+4*n2R**2*kappa2**2)))
#Transmitancias
T_perp=ktz*abs(t_perp)**2/kiz
T_par=ki2*sp.real(ktcz/ktc2)*abs(t_par)**2/kiz

return R_perp, R_par, T_perp, T_par

9.2.5 Funciones de dibujo

Hasta el moemnto se han implementado fisntintas funciones que permiten determinar los coeficientes de Fres-
nel en función del ángulo del haz incidente y de los índices de refracción del medio y del material. En es-
te epígrafe, se pretende definir una función de debujo capaz de dibujar los coeficientes de Fresnel en fun-
ción de 𝜃𝑖 . Los datos que dibuja son los resultantes de coeficientesFresnel_dielectricos() o de
coeficientesFresnel_complejo().
def dibujarCoeficientes(n1, theta_i, n2, r_perp, r_par, t_perp, t_par, nombreArchivo = ’’):
"""
Dibuja las ecuaciones de fresnel en funcion del angulo de entrada
"""
#Generacion della figura
plt.figure(figsize=(12,6))

#Amplitud
plt.subplot(1,2, 1)
plt.plot(theta_i / grados, abs(r_perp)*sp.sign(r_perp) , ’k--’, lw = 2,\
label = u"$r_{\perp}$")
plt.plot(theta_i / grados, abs(r_par)*sp.sign(r_par) , ’k’, lw = 2,\
label = u"$r_{\parallel}$")
plt.plot(theta_i / grados, abs(t_perp)*sp.sign(t_perp) , ’r--’, lw = 2,\
label = r"$t_{\perp}$")
plt.plot(theta_i / grados, abs(t_par)*sp.sign(t_par) , ’r’, lw = 2,\
label = r"$t_{\parallel}$")
#Leyenda de los ejes
plt.xlabel(r"$\phi (grados)$", fontsize = 22)
plt.ylabel(r"$Amplitud$", fontsize = 22)
plt.ylim(-1., 2.01)
plt.legend(loc=2,prop={’size’:18})

#Fase
plt.subplot(1,2, 2)
plt.plot(theta_i / grados, sp.unwrap(sp.angle(r_perp),2*sp.pi), ’k--’, lw = 2,\
label = r"$r_{\perp}$")
plt.plot(theta_i / grados, sp.unwrap(sp.angle(r_par),2*sp.pi), ’k’, lw = 2,\
label = r"$r_{\parallel}$")
plt.plot(theta_i / grados, sp.unwrap(sp.angle(t_perp),2*sp.pi), ’r--’, lw = 2,\
label = r"$t_{\perp}$")
plt.plot(theta_i / grados, sp.unwrap(sp.angle(t_par),2*sp.pi), ’r’, lw = 2,\
label = r"$t_{\parallel}$")
#Leyenda de los ejes
plt.xlabel(r"$\phi (grados)$", fontsize = 22)

9.2. Funciones para calcular las ecuaciones de Fresnel 121


PIMCD2013-python, Publicación 1.0

plt.ylabel(r"$fase$", fontsize = 22)


#plt.ylim(-sp.pi - 0.01, sp.pi + 0.01)
plt.legend(loc=2,prop={’size’:18})

if not nombreArchivo == ’’:


plt.savefig(nombreArchivo, dpi = 100, bbox_inches = ’tight’, pad_inches = 0.1)

La función que dibuja la reflectancia y transmitancia es


def dibujarTransmitancias(n1, theta_i, n2, R_perp, R_par, T_perp, T_par, nombreArchivo = ’’):
"""
Dibuja las ecuaciones de fresnel en funcion del angulo de entrada
"""
#Generacion de la figura
plt.figure()
#dibujo
plt.subplot(1, 1, 1)
plt.plot(theta_i / grados, abs(R_perp) , ’k--’, lw = 2, label = u"$R_{\perp}$")
plt.plot(theta_i / grados, abs(R_par) , ’k’, lw = 2, label = u"$R_{\parallel}$")
plt.xlabel(r"$\phi (grados)$", fontsize = 22)
plt.ylabel(r"$Amplitud$", fontsize = 22)
plt.ylim(-0.01, 1.01)
plt.plot(theta_i / grados, abs(T_perp) , ’r--’, lw = 2, label = r"$T_{\perp}$")
plt.plot(theta_i / grados, abs(T_par) , ’r’, lw = 2, label = r"$T_{\parallel}$")
plt.xlabel(r"$\phi (grados)$", fontsize = 22)
plt.ylabel(r"$I$", fontsize = 22)
plt.ylim(-0.01, 1.01)
plt.legend(loc=3,prop={’size’:18})

if not nombreArchivo == ’’:


plt.savefig(nombreArchivo, dpi = 100, bbox_inches = ’tight’, pad_inches = 0.1)

9.3 Ejemplos

Los ejemplos de utilización del archivo fresnel.py se presentan en el archivo fresnel_ejemplos.py.


Como se puede observar en el código siguiente, con muy poco código se obtienen los ejemplos, pues simple-
mente es necesario declarar los parámetros, calcular los coeficientes y dibujar. Todas estas funciones ya han sido
desarrolladas previamente.

9.3.1 Calculo de los coeficientes de fresnel

El el siguiente ejemplo se muestra cómo llamar a la función coeficientesFresnel_dielectricos para


un único valor de los parámetros
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#----------------------------------------------------------------------
# Autor: Luis Miguel Sanchez Brea
# Fecha 2013/10/24 (version 1.0)
# Licencia: GPL
#----------------------------------------------------------------------
"""
ejemplos para las ecuaciones de Fresnel
"""
#Paquetes
import sys
sys.path.append(’../’)
from fresnel import * #@UnusedWildImport

122 Capítulo 9. Ecuaciones de Fresnel


PIMCD2013-python, Publicación 1.0

def coeficientes():
"interfaz aire-vidrio"
#Coeficientes de Fresnel para un caso particular
r_perp, r_par, t_perp, t_par = coeficientesFresnel_dielectricos(n1 = 1.5,\
theta_i = 45 * grados, n2 = 1.0)
#Muestra de resultados
print "coeficientes de reflexion paralelo: r_par= (%2.4f,%2.4f)" % (r_par.real,\
r_par.imag)
print "coeficientes de reflexion perpendicular: r_perp= (%2.4f,%2.4f)" % (r_perp.real,\
r_perp.imag)
print "coeficientes de transmision paralelo: t_par= (%2.4f,%2.4f)" % (t_par.real,\
t_par.imag)
print "coeficientes de transmision perpendicular: t_perp= (%2.4f,%2.4f)" % (t_perp.real,\
t_perp.imag)

if __name__ == ’__main__’:
coeficientes()

Se supone la propagación de un haz en aire, que incide a 45 grados sobre un vidrio dieléctrico con índice 1.5. Así,
el resultado es:
>> coeficientes de reflexion paralelo: r_par= (0.2800,-0.9600)
>> coeficientes de reflexion perpendicular: r_perp= (0.8000,-0.6000)
>> coeficientes de transmision paralelo: t_par= (1.9200,-1.4400)
>> coeficientes de transmision perpendicular: t_perp= (1.8000,-0.6000)

9.3.2 Interfaz aire-vidrio

En este ejemplo se considera la incidencia de un haz de luz en el aire sobre una superficie de vidrio con índice
1.5. Se representan la amplitud transmitida y reflejada, así como la fase reflejada en función del ángulo de inci-
dencia. Como se puede ver en las curvas de amplitud, la transmitancia paralela y perpendicular son relativamente
similares. Si se representan las transmitancias y reflectancias, se observa que cuando la reflectancia es mínima la
transmitancia es máxima. Relación lógica puesto que R+T=1. No obstante es de especial interés este extremal,
llamandose ángulo de Brewster a aquel para el que la reflectancia es mínima. Así se observa que la fase cambia
radicalmente en dicho punto, y la luz reflejada resulta perfectamente polarizada.
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#----------------------------------------------------------------------
# Autor: Luis Miguel Sanchez Brea
# Fecha 2013/10/24 (version 1.0)
# Licencia: GPL
#----------------------------------------------------------------------
"""
ejemplos para las ecuaciones de Fresnel
"""
#Carga de paquetes
import sys
sys.path.append(’../’)
from fresnel import * #@UnusedWildImport

def fresnel_aire_vidrio():
"interfaz aire-vidrio"
n1 = 1.
n2 = 1.5
theta_i = sp.linspace(0 * grados, 90 * grados, 500)

#se calculan los coeficientes de Fresnel para un array


r_perp, r_par, t_perp, t_par = coeficientesFresnel_dielectricos(n1 = n1,\
theta_i = theta_i, n2 = n2)
dibujarCoeficientes(n1, theta_i, n2, r_perp, r_par, t_perp, t_par)

9.3. Ejemplos 123


PIMCD2013-python, Publicación 1.0

#Transmitancia y reflectancia
R_perp, R_par, T_perp, T_par= reflectancia_transmitancia_dielectricos(n1, theta_i, n2)
dibujarTransmitancias(n1, theta_i, n2, R_perp, R_par, T_perp, T_par)

if __name__ == ’__main__’:
fresnel_aire_vidrio()
plt.show()

2.0 3.5
r r
1.5 r 3.0 r
t t
2.5
1.0 t t
Amplitud

2.0

fase
0.5
1.5
0.0
1.0

0.5 0.5

1.00 10 20 30 40 50 60 70 80 90 0.00 10 20 30 40 50 60 70 80 90
φ(grados) φ(grados)

1.0

0.8

0.6
I

0.4
R
R
0.2
T
T
0.0
0 10 20 30 40 50 60 70 80 90
φ(grados)

124 Capítulo 9. Ecuaciones de Fresnel


PIMCD2013-python, Publicación 1.0

9.3.3 Interfaz vidrio-aire

Este ejemplo es el contrario al anterior, se supone un haz de luz propagandose en vidrio, y se analiza el comporta-
miento del haz a la salida del material. Nuevamente se observa la reflexión de Brewster en un ángulo cercano a los
40 grados, y como cerca de este la transmitancia cambia abruptamente hasta el punto que únicamente se produce
reflexión del haz.
def fresnel_vidrio_aire():
"interfaz vidrio-aire"
n1 = 1.5
n2 = 1.
theta_i = sp.linspace(0 * grados, 90 * grados, 500)

#Se calculan los coeficientes de Fresnel para un array


r_perp, r_par, t_perp, t_par = coeficientesFresnel_complejo(n1 = n1,\
theta_i = theta_i, n2c = n2)
dibujarCoeficientes(n1, theta_i, n2, r_perp, r_par, t_perp, t_par)
#Reflectancia y transmitancia
R_perp, R_par, T_perp, T_par= reflectancia_transmitancia_complejo(n1, theta_i, n2)
dibujarTransmitancias(n1, theta_i, n2, R_perp, R_par, T_perp, T_par)

2.0 4
r r
3
1.5 r r
t 2 t
1.0 t t
1
Amplitud

fase

0.5 0

1
0.0
2
0.5
3

1.00 10 20 30 40 50 60 70 80 90 40 10 20 30 40 50 60 70 80 90
φ(grados) φ(grados)

9.3. Ejemplos 125


PIMCD2013-python, Publicación 1.0

1.0

0.8

0.6
I

0.4
R
R
0.2
T
T
0.0
0 10 20 30 40 50 60 70 80 90
φ(grados)
9.3.4 Interfaz aire-metal

Los metales se caracterizan por tener un índice de refracción complejo siendo negativa la parte imaginaria del
mismo. Así se supone la incidence de un haz sobre una intercara aire-metal, con índice de refracción 𝑛 = 0,769 −
𝑖6,08. Es destacable como la variación en la fase de la componente paralela, es prácticamente invariante bajo
cambios en el ángulo de incidencia.
def fresnel_aire_metal():
"interfaz aire-metal"
n1 = 1.
#el metal se introduce con numero complejo cuya parte imaginaria es negativa
n2c = 0.769 - 6.08j
theta_i = sp.linspace(0 * grados, 90 * grados, 500)

#se calculan los coeficientes de Fresnel para un array


r_perp, r_par, t_perp, t_par = coeficientesFresnel_complejo(n1, theta_i, n2c)
dibujarCoeficientes(n1, theta_i, n2c, r_perp, r_par, t_perp, t_par)
#Reflectancia y transmitancia
R_perp, R_par, T_perp, T_par= reflectancia_transmitancia_complejo(n1, theta_i, n2c)
dibujarTransmitancias(n1, theta_i, n2c, R_perp, R_par, T_perp, T_par)

126 Capítulo 9. Ecuaciones de Fresnel


PIMCD2013-python, Publicación 1.0

2.0 4
r r
3
1.5 r r
t 2 t
1.0 t t
1
Amplitud

fase
0.5 0

1
0.0
2
0.5
3

1.00 10 20 30 40 50 60 70 80 90 40 10 20 30 40 50 60 70 80 90
φ(grados) φ(grados)

1.0

0.8

0.6
I

0.4
R
R
0.2
T
T
0.0
0 10 20 30 40 50 60 70 80 90
φ(grados)
9.4 Ejercicios

Determinar el valor de la reflectancia y transmitancia en incidencia normal cuando el medio incidente es el


vacío (n=1) y el segundo medio es aluminio.
Hacer una función para dibujar los coeficientes de reflexión y transmisión cuando un haz incide de forma
normal desde el vacío sobre un medio dieléctrico cuyo índice de refracción varía entre 1 y 2.
Calcular mediante una gráfica para qué ángulo hay un mínimo de Reflectancia en la componente paralela

9.4. Ejercicios 127


PIMCD2013-python, Publicación 1.0

en el oro (se puede mirar su índice de refracción en la wikipedia).


Sea un haz policromático entre 400 nm y 600 nm que incide sobre un medio dispersivo en el vacio. El índice
de refración es n(lambda)=1.5-0.1/lambda. Representar las transmitancias para 400,450,500,550 y 600 nm
en una misma gráfica.

9.5 Referencias

128 Capítulo 9. Ecuaciones de Fresnel


CAPÍTULO 10

Pulsos de luz

Autor Luis Miguel Sánchez Brea


Revisor José Luis Vilas Prieto
Desde el punto de vista docente, muchos de los aspectos ópticos se tratan con ondas armónicas planas, que tienen
una extensión espacio-temporal infinita. Más usuales desde el punto de vista práctico son los pulsos de luz, que
tienen una extensión temporal finita. Las ondas que tienen una extensión espacial finita se tratan en temas de
difracción.

Contenidos de este capítulo:


Introducción
Espectros de distintos tipos de pulso
• Pulso gaussiano
• Pulso exponencial: perfil de Lorentz
• Pulso cuadrado
Suma de dos ondas

Herramientas utilizadas en este tutorial:


numpy: manipulación básica de arrays
scipy: tratamiento de datos matemáticos
matplotlib.pyplot: visualización de datos similar a matplotlib
Descarga de archivos:
Generación de espectros de un haz cuadrado pulso_cuadrado.py
Generación de espectros tipo gaussiano pulso_gauss.py
Generación de espectros tipo lorentziano pulso_lorentz.py

Superposicion de dos ondas en el vacío dos_ondas.py


Superposicion de dos ondas en un medio dispersivo dos_ondas_dispersivo.py
Superposicion de dos ondas en un medio dispersivo animación dos_ondas_animacion.py
Superposicion de n ondas n_ondas.py
Superposicion de n ondas (animación) n_ondas_animacion.py

Propagación de un pulso en el vacio propagacion_pulso_vacio.py


Propagación de un pulso en un medio dispersivo propagacion_pulso_dispersivo.py

129
PIMCD2013-python, Publicación 1.0

Propagación de un pulso (animación) propagacion_pulso_dispesivo.py


Propagación de un pulso, siguiendo la posición del máximo (animación)
propagacion_pulso_en_ola_animacion.py

10.1 Introducción

Un pulso temporal ‘plano’ se puede tratar como una suma infinita de ondas armónicas planas, pues forman una
base. Para calcular los componentes de cada una de las ondas armónicas de frecuencia 𝜔 que componen el pulso
se tiene que realizar una transformación de Fourier temporal,
∫︁ ∞
E (𝑧, 𝑡) = E0,𝜔 𝑒𝑖(𝑘𝜔 𝑧−𝜔𝑡) 𝑑𝜔,
−∞

donde E0,𝜔 es la amplitud compleja de cada una de las ondas planas. Para calcular esta amplitud se tiene que
recurir a la transformación de Fourier inversa
∫︁ ∞
1
E0,𝜔 = √ E (𝑧 = 0, 𝑡) 𝑒𝑖𝜔𝑡 𝑑𝑡,
2𝜋 −∞
Existen dos casos particularmente útiles como son el pulso gaussiano y el pulso de exponencial, que discutiremos
brevemente a continuación.

10.2 Espectros de distintos tipos de pulso

10.2.1 Pulso gaussiano

El caso gaussiano tiene la forma

(𝜔 − 𝜔0 )2
[︂ ]︂
E0,𝜔 = E0 𝑒𝑥𝑝 − .
2∆𝜔 2

donde 𝜔0 es la frecuencia central del pulso y ∆𝜔 2 es la anchura espectral del pulso.


Si la onda se propaga por un medio no dispersivo de índice de refracción n, introduciendo 𝐸0,𝜔 en 𝐸(𝑧, 𝑡) se
obtiene que el campo resulta
√ (𝑧 𝑛/𝑐 − 𝑡)2 𝑖(𝑧 𝑛/𝑐−𝑡)𝜔0
[︂ ]︂
E (𝑧, 𝑡) = 2𝜋∆𝜔E0 𝑒𝑥𝑝 − 𝑒 .
2/∆𝜔 2

donde se observa que la amplitud y la fase se mueven con la misma velocidad.


En el siguiente código se muestra cómo generar una gráfica con el espectro gaussiano y el pulso generado.
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#------------------------------------
# Autor: Luis Miguel Sanchez Brea
# Fecha 2013/10/24 (version 1.0)
# Licencia: GPL
#-------------------------------------
"""pulso de gauss"""

from __future__ import division


import locale

#Modulos empleados
import scipy as sp
import matplotlib.pyplot as plt

130 Capítulo 10. Pulsos de luz


PIMCD2013-python, Publicación 1.0

#Unidades
sg = 1
Hz = 1 / sg #hertzio

def pulso_gauss(w0=1, gamma=.1):


"""Entradas: w0: frecuencia de resonancia
gamma: amortiguamiento
"""
#frecuencia de la luz. Array con diversas frecuencias en torno a la resonancia
n_puntos = 1000
anchura = 10
w = sp.linspace(w0 - anchura * gamma, w0 + anchura * gamma, n_puntos)

t= sp.linspace(-3/gamma,3/gamma,n_puntos)

#campo electrico normalizado


campo=sp.exp(-(gamma*t)**2/2)*sp.cos(w0*t)
#espectro normalizado
Espectro= sp.exp (-0.5*(w-w0)**2/(gamma)**2)

#Figura con campo y espectro


plt.figure(figsize=(10,5))

#Representacion del campo


plt.subplot(1,2,1)
plt.plot(t, campo, ’k’, linewidth = 2)
plt.xlabel(’$t \; (s)$’, fontsize = 18)
plt.ylabel(’$E(t)$’, fontsize = 18)

#Representacion del espectro


plt.subplot(1,2,2)
plt.plot(w, Espectro, ’k’, linewidth = 2)
plt.xlabel(’$\omega \; (Hz)$’, fontsize = 18)
plt.ylabel(’perfil Gaussiano’, fontsize = 18)
plt.tight_layout()

#Representacion de un pulso ejemplo con w0=3*10**15*Hz y gamma=10**14*Hz


pulso_gauss(w0=3*10**15*Hz, gamma=10**14*Hz)
plt.show()

1.0 1.0

0.8
0.5
perfil Gaussiano

0.6
E(t)

0.0
0.4

0.5
0.2

1.0 3 2 1 0 1 2 3 0.02.0 2.5 3.0 3.5 4.0


t (s) 1e 14 ω (Hz) 1e15

10.2. Espectros de distintos tipos de pulso 131


PIMCD2013-python, Publicación 1.0

10.2.2 Pulso exponencial: perfil de Lorentz

Otro pulso que se utiliza frecuentemente es el pulso exponencial, pues es el modelo clásico de cómo los átomos
emiten luz. Una vez excitados pierden energía hasta que vuelven al estado estacionario. El modelo de Lorentz
asume que el desplazamiento resulta

r(𝑡) = r𝑜 𝑒−𝛾𝑡/2 𝑒−𝑖𝜔𝑜 𝑡 .

donde 𝛾 es la constante de decaimiento. Según este modelo de Lorentz el campo generado

E(𝑧 = 0, 𝑡) = 𝐸0 u𝑥 𝑒−𝛾𝑡/2 𝑒−𝑖𝜔0 𝑡 .

Al realizar la transformada de Fourier resulta que la distribución de frecuencias es compleja (se producen desfases).
𝐸𝑜 ux
E𝜔 =
𝛾/2 − 𝑖(𝜔 − 𝜔𝑜 )
En este caso, la densidad de energía espectral, esto es, la cantidad de energía electromagnética por unidad espectral
que llega por unidad de superficie

𝛾 2 /4 1
𝐽(𝜔) = 𝜀𝑜 𝑐|𝐸𝜔 |2 = 𝐽(𝜔𝑜 ) , = 𝐽(𝜔𝑜 ) ,
𝛾 2 /4 + (𝜔 − 𝜔𝑜 )2 1 + 4(𝜔 − 𝜔𝑜 )2 /𝛾 2
En el siguiente código se muestra cómo generar una gráfica con el espectro gaussiano y el pulso generado.
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#------------------------------------
# Autor: Luis Miguel Sanchez Brea
# Fecha 2013/10/24 (version 1.0)
# Licencia: GPL
#-------------------------------------
"""pulso de gauss"""

from __future__ import division


import locale
locale.setlocale(locale.LC_NUMERIC, ’C’)
import scipy as sp
import matplotlib.pyplot as plt

#Definicion de frecuencia
sg= 1
Hz = 1 / sg #hertzio

def Lorentziana(w0=1, gamma=.1):


"""Entradas: w0: frecuencia de resonancia
gamma: amortiguamiento
"""
#frecuencia de la luz. Array con diversas frecuencias en torno a la resonancia
n_puntos = 1000
anchura = 10
w = sp.linspace(w0 - anchura * gamma, w0 + anchura * gamma, n_puntos)
t= sp.linspace(0,100/w0,n_puntos)

#campo
campo=sp.exp(-gamma*t)*sp.cos(w0*t)

#Definicion del espectro espectro


Espectro= 1 / (1 + (w-w0)**2/(gamma/2)**2)

#Figuras
plt.figure(figsize=(10,5))

#Representacion del campo

132 Capítulo 10. Pulsos de luz


PIMCD2013-python, Publicación 1.0

plt.subplot(1,2,1)
plt.plot(t, campo, ’k’, linewidth = 2)
plt.xlabel(’$t \; (s)$’, fontsize = 18)
plt.ylabel(’$E(t)$’, fontsize = 18)

#Representacion del espectro


plt.subplot(1,2,2)
plt.plot(w, Espectro, ’k’, linewidth = 2)
plt.xlabel(’$\omega \; (Hz)$’, fontsize = 18)
plt.ylabel(’perfil Lorenziano’, fontsize = 18)
plt.tight_layout()

#Representacioni de un pulso lorentziano con w0=3*10**15*Hz, gamma=10**14*Hz


Lorentziana(w0=3*10**15*Hz, gamma=10**14*Hz)
plt.show()

1.0 1.0

0.8
0.5
perfil Lorenziano
0.6
E(t)

0.0
0.4

0.5
0.2

1.00.0 0.5 1.0 1.5 2.0 2.5 3.0 3.5 0.02.0 2.5 3.0 3.5 4.0
t (s) 1e 14 ω (Hz) 1e15

10.2.3 Pulso cuadrado

Desde el punto de vista teórico también es importante un pulso cuadrado, que se obtiene como una función coseno
dentro del intervalo y 0 fuera.

r(𝑡) = r𝑜 𝑒−𝛾𝑡/2 𝑒−𝑖𝜔𝑜 𝑡 .

#!/usr/bin/env python
# -*- coding: utf-8 -*-
#------------------------------------
# Autor: Luis Miguel Sanchez Brea
# Fecha 2013/10/24 (version 1.0)
# Licencia: GPL
#-------------------------------------
"""pulso de gauss"""

from __future__ import division


import locale

#Modulos empleados
import scipy as sp
import matplotlib.pyplot as plt

10.2. Espectros de distintos tipos de pulso 133


PIMCD2013-python, Publicación 1.0

#Unidades
sg = 1
Hz = 1 / sg #hertzio

def pulso_gauss(w0=1, gamma=.1):


"""Entradas: w0: frecuencia de resonancia
gamma: amortiguamiento
"""
#frecuencia de la luz. Array con diversas frecuencias en torno a la resonancia
n_puntos = 1000
anchura = 10
w = sp.linspace(w0 - anchura * gamma, w0 + anchura * gamma, n_puntos)

t= sp.linspace(-3/gamma,3/gamma,n_puntos)

#campo electrico normalizado


campo=sp.exp(-(gamma*t)**200/2)*sp.cos(w0*t)
#espectro normalizado
Espectro= sp.sinc ((w-w0)/(gamma))

#Figura con campo y espectro


plt.figure(figsize=(10,5))

#Representacion del campo


plt.subplot(1,2,1)
plt.plot(t, campo, ’k’, linewidth = 2)
plt.xlabel(’$t \; (s)$’, fontsize = 18)
plt.ylabel(’$E(t)$’, fontsize = 18)

#Representacion del espectro


plt.subplot(1,2,2)
plt.plot(w, Espectro, ’k’, linewidth = 2)
plt.xlabel(’$\omega \; (Hz)$’, fontsize = 18)
plt.ylabel(’perfil sinc’, fontsize = 18)
plt.tight_layout()

#Representacion de un pulso ejemplo con w0=3*10**15*Hz y gamma=10**14*Hz


pulso_gauss(w0=3*10**15*Hz, gamma=10**14*Hz)
plt.show()

1.0 1.0

0.8
0.5
0.6
perfil sinc

0.4
E(t)

0.0
0.2

0.0
0.5
0.2

1.0 3 2 1 0 1 2 3 0.42.0 2.5 3.0 3.5 4.0


t (s) 1e 14 ω (Hz) 1e15

134 Capítulo 10. Pulsos de luz


PIMCD2013-python, Publicación 1.0

10.3 Suma de dos ondas

Para analizar la propagación de un pulso de luz, en primer lugar haremos la aproximación de

10.3. Suma de dos ondas 135


PIMCD2013-python, Publicación 1.0

136 Capítulo 10. Pulsos de luz


CAPÍTULO 11

Interferencias

Autor Luis Miguel Sánchez Brea


Revisor José Luis Vilas Prieto

Contenidos de este capítulo:


Introducción
Programación
Interferencia entre dos ondas plana
• Método lineal
• Método funcional
• Método con clases
Interferencia entre dos haces de Gauss
Interferencia entre dos ondas esféricas
• Interferómetro de Young
• Interferómetro de Michelson
• Desplazamiento combinado
• Simulación del interferómetro de Michelson

Herramientas utilizadas en este tutorial:


numpy: manipulación de datos
scipy: Herramientas matemáticas
Archivos necesarios para la clase interferencias:
camposXY.py. Archivo con las funciones principales de campos camposXY.py
fuentesXY.py. Archivo con las funciones principales de fuentes fuentesXY.py
Descarga de archivos:
Ejemplo de interferencias con ondas planas interferencias_ondasPlanas1.py
Ejemplo de interferencias con ondas planas interferencias_ondasPlanas2.py
Ejemplo de interferencias con ondas planas interferencias_ondasPlanas3.py
Ejemplo de interferencias con ondas esféricas interferencias_ondasEsfericas1.py
Ejemplo de interferencias con ondas esféricas interferencias_ondasEsfericas2.py
Ejemplo de interferencias con ondas esféricas interferencias_ondasEsfericas3.py
Ejemplo de interferencias con haces de Gauss interferencias_gauss1.py
Ejemplo de interferencias con haces de Gauss interferencias_gauss2.py
Ejemplo de interferómetro de Michelson interferencias_michelson.py

137
PIMCD2013-python, Publicación 1.0

11.1 Introducción

El efecto de las interferencias surge directamente de que las ecuaciones de Maxwell son lineales en los campos
eléctricos y magnéticos. Supongamos que E1 y E2 son soluciones. Entonces

E = E1 + E2

también es una solución. Sin embargo, el campo no es un parámetro observable, sino que lo es el promedio
temporal del vector de Poynting, que se define como
1
S≡ E ∧ B,
𝜇0

con tiene unidades de [S] = 𝑊/𝑚2 . Al particularizar al caso de ondas armónicas planas tenemos
√︂
1 𝜀0 2
⟨S⟩ = |E| u𝑘 ,
2 𝜇0

que es cuadrático con el campo eléctrico.


Si definimos la intensidad como
⟨ ⟩
2
𝐼 = |E| ,

entonces la intensidad de la composición de las dos ondas resulta


⟨ ⟩ ⟨ ⟩ ⟨ ⟩
2 2 2
𝐼 = |E1 + E2 | = |E1 | + |E2 | + 2 ⟨|E1 E*2 |⟩ = 𝐼1 + 𝐼2 + 2 ⟨|E1 E*2 |⟩ .

Si no existiera el tercer término, entonces ocurre que 𝐼 = 𝐼1 + 𝐼2 , que es lo que normalmente vemos en la
naturaleza.
Para que se cumplan las interferencias, pues necesitamos que la luz esté polarizada, sea bastante monocromática
y sea coherente. En particular, se deben verificar:
Únicamente interfieren ondas con la misma frecuencia, 𝜔1 = 𝜔2
Las ondas que interfieren deben tener estados de polarización no ortogonales. E01 · E02 ̸= 0
Coherencia: Las fases en el término interferencial deben tener poca dependencia de (𝑡, 𝑟)
En este capítulo asumiremos las mejores condiciones para producir las interferencias. Las 2 ondas tienen la misma
polarización, son puramente monocromáticas y coherentes. De esta forma, desaparece el carácter vectorial de las
interferencias.

11.2 Programación

Debido a la sencillez conceptual de las interferencias, las utilizaremos para analizar los distintos tipos de pro-
gramación que se puede realizar con Python, como es la programación con scripts (todo el código en un mismo
archivo lineal), la programación funcional (se escriben funciones que simplifican lo cálculos) y la programación
con clases (donde funciones y datos están íntimamente relacionados). Finalmente veremos un procedimiento sen-
cillo para desarrollar vídeos, simulando un interferómetro de Michelson.

11.3 Interferencia entre dos ondas plana

En representación compleja una onda plana se expresa como

𝐸(r, 𝑡) = 𝐴 𝑒𝑖(k r−𝜔𝑡)

138 Capítulo 11. Interferencias


PIMCD2013-python, Publicación 1.0

Podemos desciribr el vector k en coordenadas esfericas, de forma que tenemos


2𝜋
k= (sin 𝜃 sin 𝜑, cos 𝜃 sin 𝜑, cos 𝜑).
𝜆
2𝜋
Entonces, en el plano 𝑧 = 0, la componente k · r = 𝜆 (𝑠𝑖𝑛𝜃𝑠𝑖𝑛𝜑 𝑥 + 𝑐𝑜𝑠𝜃𝑠𝑖𝑛𝜑 𝑦).
Esto se puede programar en Python como
k*(X*sp.sin(theta1)*sp.sin(phi1)+Y*sp.cos(theta1)*sp.sin(phi1))

para poder representar esta ecuación en muchas posiciones x,y entonces X e Y son matrices generadas como
tamano=100*um
x=sp.linspace(-tamano,tamano, 256)
y=sp.linspace(-tamano,tamano, 256)
X,Y=sp.meshgrid(x,y)

11.3.1 Método lineal

El método lineal es quizás el más sencillo para iniciarse, pues la programación está en el mismo archivo y el
código se ejecuta de arriba hacia abajo en el programa. Un ejemplo de esta programación está en la función
interferencias_ondasPlanas1.py. El archivo consiste en la definición de dos ondas de la misma longitud de onda y
la posterior superposición de las mismas, para finalmente obtener la intensidad. Así se obtiene el patrón de franjas
de la figura.
#!/usr/local/bin/python
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#------------------------------------
# Autor: Luis Miguel Sanchez Brea
# Fecha 2013/10/24 (version 1.0)
# Licencia: GPL
#-------------------------------------
"""
Interferencia entre ondas planas.
Programacion lineal.
"""

#Carga de paquetes y librerias


import scipy as sp
import matplotlib.pyplot as plt

#variables globales
um=1
grados=sp.pi/180

#Longitud de onda y vector de onda


wavelength=0.6328*um
k=2*sp.pi/wavelength

#Tamano
tamano=100*um
x=sp.linspace(-tamano,tamano, 256)
y=sp.linspace(-tamano,tamano, 256)
X,Y=sp.meshgrid(x,y)

#amplitud y direccion de la primera onda


A1=1
theta1=90*grados
phi1=-1*grados

#direccion de la segunda onda (distinta direccion)

11.3. Interferencia entre dos ondas plana 139


PIMCD2013-python, Publicación 1.0

A2=1
theta2=90*grados
phi2=1*grados

#definicion de una onda plana (observese la generacion de numeros complejos)


u1=A1*sp.exp(1.j*k*(X*sp.sin(theta1)*sp.sin(phi1)+Y*sp.cos(theta1)*sp.sin(phi1)))
u2=A2*sp.exp(1.j*k*(X*sp.sin(theta2)*sp.sin(phi2)+Y*sp.cos(theta2)*sp.sin(phi2)))

#proceso interferencial
u=u1+u2

#calculo de la intensidad
I=abs(u)**2

#grafica de la intensidad
plt.figure() #creacion de la figura

#datos para ajustar el dibujo a la escala


extension = [x.min(), x.max(), y.min(), y.max()]

#dibujar intensidad + acondicionamiento de la grafica


h1 = plt.imshow(I, interpolation = ’bilinear’, aspect = ’auto’,
origin = ’lower’, extent = extension)

#escalado de la grafica
plt.axis(’scaled’)

#etiquetas
plt.xlabel("$x (\mu m)$", fontsize = 22)
plt.ylabel("$y (\mu m)$", fontsize = 22)
plt.title("$Intensidad(x,y)$", fontsize = 22)

#color
h1.set_cmap("gist_heat")

#dibujar barra de colores


plt.colorbar()

#muestra la grafica
plt.show()

140 Capítulo 11. Interferencias


PIMCD2013-python, Publicación 1.0

100
Intensidad(x,y) 4.0
3.6
3.2
50
2.8
2.4
y(µm)

0 2.0
1.6
1.2
50
0.8
0.4
100100 50 0 50 100 0.0
x(µm)
11.3.2 Método funcional

Si nos damos cuenta en el código anterior, la onda plana se generar dos veces, una para 𝐸1 , y otra para 𝐸2 . Esto
se puede evitar definiendo una función que denominamos onda_plana() a la cual llamamos dos veces. Los
parámetros de entrada de la función son aquellos que alguna vez podemos variar. También podemos generar una
función que realiza la gráfica. De esta forma la podemos llamar también varias veces.
#!/usr/local/bin/python
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#------------------------------------
# Autor: Luis Miguel Sanchez Brea
# Fecha 2013/10/24 (version 1.0)
# Licencia: GPL
#-------------------------------------
"""
Interferencia entre ondas planas.
Programacion con funciones.
"""

#Carga de librerias y paquetes.


import scipy as sp
import matplotlib.pyplot as plt

#variables globales
um=1
grados=sp.pi/180

#parametros opticos
wavelength=0.6328*um

11.3. Interferencia entre dos ondas plana 141


PIMCD2013-python, Publicación 1.0

k=2*sp.pi/wavelength

#Tamano
tamano=100*um
x=sp.linspace(-tamano,tamano, 256)
y=sp.linspace(-tamano,tamano, 256)
X,Y=sp.meshgrid(x,y)

def onda_plana(A, theta, phi):


#definicion de una onda plana (observese la generacion de numeros complejos)
u=A*sp.exp(1.j*k*(X*sp.sin(theta)*sp.sin(phi)+Y*sp.cos(theta)*sp.sin(phi)))
return u

#definicion de la grafica
def dibujar_grafica(I):
#grafica de la intensidad
plt.figure() #creacion de la figura
#datos para ajustar el dibujo a la escala
extension = [x.min(), x.max(), y.min(), y.max()]
#dibujar intensidad + acondicionamiento de la grafica
h1 = plt.imshow(I, interpolation = ’bilinear’, aspect = ’auto’,
origin = ’lower’, extent = extension)
#escalado de la grafica
plt.axis(’scaled’)
#etiquetas
plt.xlabel("$x (\mu m)$", fontsize = 22)
plt.ylabel("$y (\mu m)$", fontsize = 22)
plt.title("$Intensidad(x,y)$", fontsize = 22)
#color
h1.set_cmap("gist_heat") #RdBu
#dibujar barra de colores
plt.colorbar()

#generacion de las ondas planas,


#ahora no es necesario definir los parametros globalmente, sino como parametros de la funcion
u1=onda_plana(A=1,theta=90*grados,phi=-1*grados)
u2=onda_plana(A=1,theta=90*grados,phi=1*grados)
#proceso interferencial
u=u1+u2
#calculo de la intensidad
I=abs(u)**2
dibujar_grafica(I)

#El codigo se puede reutilizar para otro ejemplo


u3=onda_plana(A=1,theta=90*grados,phi=0*grados)
u4=onda_plana(A=1,theta=90*grados,phi=4*grados)
#proceso interferencial
u=u3+u4
#calculo de la intensidad
I=abs(u)**2
dibujar_grafica(I)
plt.show()

142 Capítulo 11. Interferencias


PIMCD2013-python, Publicación 1.0

100
Intensidad(x,y) 4.0
3.6
3.2
50
2.8
2.4
y(µm)

0 2.0
1.6
1.2
50
0.8
0.4
100100 50 0 50 100 0.0
x(µm)
100
Intensidad(x,y) 4.0
3.6
3.2
50
2.8
2.4
y(µm)

0 2.0
1.6
1.2
50
0.8
0.4
100100 50 0 50 100 0.0
x(µm)
Como se ve, el código incluido en las funciones se puede reutilizar. No obstante, las variables que utiliza la funcion

11.3. Interferencia entre dos ondas plana 143


PIMCD2013-python, Publicación 1.0

onda_plana() tienen dos orígenes. Uno como variables globales definidas anteriormente (como puede ser k,
X, Y y otras variables que se introducen a través de la definción de la función A, 𝜃, 𝜑.

11.3.3 Método con clases

En las clases, las funciones y los datos están todos bajo un mismo marco, por lo que no existe el peligro de
modificar las variables globales de forma inadvertida. Normalmente la clase se define en una función (compleja)
y las aplicaciones de la clase llaman a esta función. Esta forma permite generar las figuras mostradas debajo del
código
#!/usr/local/bin/python
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#------------------------------------
# Autor: Luis Miguel Sanchez Brea
# Fecha 2013/10/24 (version 1.0)
# Licencia: GPL
#-------------------------------------
"""
Metodo que utiliza las clases
"""

#Carga de paquetes y librerias


import scipy as sp
import matplotlib.pyplot as plt
import sys
sys.path.append(’../’)
from fuentesXY import fuenteXY, grados

#genera una instancia a una fuente


#estamos utilizando los parametros predeterminados para las variables de inicializacion
# (x, y, wavelength)

u1 = fuenteXY()
u2 = fuenteXY()

#rellena la instancia con una onda plana


u1.onda_plana(A = 1, theta = sp.pi / 2, phi = .5 * grados)
u2.onda_plana(A = 1, theta = sp.pi / 2, phi = -.5 * grados)

#segun la definicion de la clase la suma de dos ondas tambien es una onda


u12 = u1 + u2

#llama a la funcion de dibujar,


#esta funcion tiene varios metodos y elegimos dibujar la intensidad
u12.dibujar(tipo=’intensidad’)

#ahora elegimos dibujar el campo


u12.dibujar(tipo=’campo’)

plt.show()

144 Capítulo 11. Interferencias


PIMCD2013-python, Publicación 1.0

4.0
200 3.6
3.2
100 2.8
2.4
y(µm)

0 2.0
1.6
100 1.2
0.8
200 0.4

200 100 0 100 200 0.0


x(µm)
amplitud fase
200 200

100 100
y(µm)

y(µm)

0 0

100 100

200 200
200 100 0 100 200 200 100 0 100 200
x(µm) x(µm)
0.2 0.4 0.6 0.8 1.0 1.2 1.4 1.6 1.8 2.0 0.0 0.4 0.8 1.2 1.6 2.0 2.4 2.8

Dentro de la clase fuentesXY (no es necesario comprender todas las funciones todavía), las funciones que
hemos utilizado son:
__init__()
onda_plana()
def __init__(self, x = x0, y = y0, wavelength = lambda0):
"""Se inicia un nuevo experimento"""
#arrays de entrada

11.3. Interferencia entre dos ondas plana 145


PIMCD2013-python, Publicación 1.0

self.x = x
self.y = y
#Longitud de onda
self.wavelength = wavelength
#Generacion de un mallado
self.X, self.Y = sp.meshgrid(x, y)
#Predefinicion del campo
self.u = sp.zeros(sp.shape(self.X), dtype = complex)

def onda_plana(self, A = 1, theta = 0 * grados, phi = 0 * grados):


"""onda plana"""
#Definicion del vector de onda
k = 2 * sp.pi / self.wavelength

#Definicion de la onda plana (coordenadas esfericas)


self.u = A * sp.exp(1.j * k * (self.X * sp.sin(theta) * sp.sin(phi) \
+ self.Y * sp.cos(theta) * sp.sin(phi)))

11.4 Interferencia entre dos haces de Gauss

Veamos ahora el proceso de interferencia de 2 haces de Gauss. Estos hace se pueden definir como

𝜌2 𝜌2
(︂ )︂ (︂ )︂
𝜔0
𝐸(𝑥, 𝑦, 𝑧) = 𝐴0 exp − 2 exp 𝑖𝑘𝑧 + 𝑖𝑘 − 𝑖𝜁(𝑧)
𝜔(𝑧) 𝜔 (𝑧) 2𝑅(𝑧)
donde
𝑧0 𝜆
𝜔02 =
𝜋

√︃ (︂ )︂2
𝑧
𝜔(𝑧) = 𝜔0 1+
𝑧0

[︂ ]︂
𝑧 2
𝑅(𝑧) = 𝑧 1 + ( )
𝑧0
Este tipo de haces se puede programar fácilmente. Asumamos que estamos en el foco del haz z=0 y los haces están
rotados. Entonces también se van a producir interferencias entre los haces.
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#------------------------------------
# Autor: Luis Miguel Sanchez Brea
# Fecha 2013/10/24 (version 1.0)
# Licencia: GPL
#-------------------------------------
"""
ejemplos de interferencias
"""

#Carga de paquetes y librerias


import scipy as sp
import matplotlib.pyplot as plt

#variables globales
um=1
grados=sp.pi/180

#Longitud de onda y mallado

146 Capítulo 11. Interferencias


PIMCD2013-python, Publicación 1.0

wavelength=0.6328*um
tamano=100*um
x=sp.linspace(-tamano,tamano, 256)
y=sp.linspace(-tamano,tamano, 256)
X,Y=sp.meshgrid(x,y)

def gauss(A=1, r0=(0*um,0*um), w=100*um, theta=0.*grados, phi=0*grados):


"""haz gaussiano."""
x0,y0=r0
#vector de onda
k=2*sp.pi/wavelength
#Amplitud de la onda
amplitud=A*sp.exp(-(X-x0)**2/(2*w**2)-(Y-y0)**2/(2*w**2))
#Fase de la onda
fase=sp.exp(1.j*k*(X*sp.sin(theta)*sp.sin(phi)+Y*sp.cos(theta)*sp.sin(phi))) #rotacion
#Campo
u=amplitud*fase
return u

def dibujar(Intensidad, titulo):


"""Dibuja el patron de intensidad"""
#Creamos una figura
plt.figure()
h1 = plt.imshow(Intensidad)
#Mapa de color
h1.set_cmap("gist_heat") #RdBu
#Titulo y escala de color
plt.title(titulo, fontsize=28)
plt.colorbar()

def interferenciasGauss(theta, titulo=’’):


#Campos de las ondas que interfieren
u1=gauss(A=1, r0=(0*um,0*um), w=50*um, theta=theta, phi=2*grados)
u2=gauss(A=1, r0=(0*um,0*um), w=50*um, theta=-theta, phi=2*grados)
#Superposicion
u=u1+u2
#Intensidad de la superposicion
Intensidad=abs(u)**2
#Representacion
dibujar(Intensidad, titulo)

if __name__ == ’__main__’:
interferenciasGauss(theta=20*grados, titulo=r’$\theta=20^{0}$’)
interferenciasGauss(theta=40*grados, titulo=r’$\theta=40^{0}$’)
plt.show()

11.4. Interferencia entre dos haces de Gauss 147


PIMCD2013-python, Publicación 1.0

0
θ =200
3.6
50 3.2
2.8
100 2.4
2.0
150 1.6
1.2
200 0.8
0.4
250 0.0
0 50 100 150 200 250

0
θ =400
3.6
50 3.2
2.8
100 2.4
2.0
150 1.6
1.2
200 0.8
0.4
250 0.0
0 50 100 150 200 250

Aquí está todo programado en funciones, tanto la amplitud del haz de Gauss en z=0, como el procedimiento de

148 Capítulo 11. Interferencias


PIMCD2013-python, Publicación 1.0

dibujar y la función de ejecución __main__. De esta forma se puede reutilizar el código de una forma muy
eficiente, además de entenderse mejor el código. Los parámetros entre las funciones se pasan a través de las
variables de entrada.
Finalmente, vemos cómo se puede representar esto a partir de la clase FuenteXY. La forma de programar las
interferencias es mucho más sencilla y clara, casi pseudocódigo
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#------------------------------------
# Autor: Luis Miguel Sanchez Brea
# Fecha 2013/10/24 (version 1.0)
# Licencia: GPL
#-------------------------------------
"""
ejemplos de interferencias
"""
#Carga de paquetes y librerias
import scipy as sp
import matplotlib.pyplot as plt
import sys
sys.path.append(’../’)
from fuentesXY import fuenteXY, grados, um

#definicion de parametros
x=sp.linspace(-100*um,100*um,256)
y=sp.linspace(-100*um,100*um,256)
wavelength=0.6328*um
#onda 1
u1 = fuenteXY(x,y,wavelength)
u1.haz_gauss(A=1, r0=(0*um,0*um), w=(50*um, 50*um), theta=20.*grados, phi=2*grados)
#onda 2
u2 = fuenteXY(x,y,wavelength)
u2.haz_gauss(A=1, r0=(0*um,0*um), w=(50*um, 50*um), theta=-20.*grados, phi=2*grados)
#Superposicion
u12 = u1 + u2
#Representacion
u12.dibujar(tipo=’intensidad’)
plt.show()

11.4. Interferencia entre dos haces de Gauss 149


PIMCD2013-python, Publicación 1.0

100
3.6
3.2
50
2.8
2.4
y(µm)

0 2.0
1.6
1.2
50
0.8
0.4
100100 50 0 50 100 0.0
x(µm)

11.5 Interferencia entre dos ondas esféricas

Como ejemplo de programación con clases veremos las interferencias entre ondas esféricas. Estas son de gran
importancia teórica pues nos permiten analizar los interferómetros de Young y de Michelson. La diferencia fun-
damental estriba en la posición de las fuentes. Mientras que en el interferómetro de Young las dos fuentes están
desplazadas en el eje x, en el interferometro de Michelson están desplzadas en el eje z.
Como veremos, la programación con clases nos permite centrarnos en el problema, ya que la programación es de
alto nivel (el profesor ya desarrolla las funciones de generación de las fuentes).

11.5.1 Interferómetro de Young

Como hemos dicho las dos fuentes están desplazadas en el eje x. Esto genera una distribución de intensidad en
forma de elipses que, si nos centramos en el eje, son muy próximas a franjas verticales, como puede ver en la
siguiente función.
#!/usr/local/bin/python
# -*- coding: utf-8 -*-
#------------------------------------
# Autor: Luis Miguel Sanchez Brea
# Fecha 2013/10/24 (version 1.0)
# Licencia: GPL
# Objetivo: Interferencias entre 2 ondas esfericas desplazadas en el eje x
#-------------------------------------

#Carga de librerias y paquetas


import scipy as sp
import matplotlib.pyplot as plt

150 Capítulo 11. Interferencias


PIMCD2013-python, Publicación 1.0

import sys
sys.path.append(’../’)
from fuentesXY import fuenteXY, grados, um

#definicion de las ondas


u1 = fuenteXY();
u1.onda_esferica(A = 1, r0 = (-10 * um, 0 * um), z0 = -1000 * um, radio = 200 * um, mascara = True
u2 = fuenteXY()
u2.onda_esferica(A = 1, r0 = (+10 * um, 0 * um), z0 = -1000 * um, radio = 200 * um, mascara = True

#definicion de las interferencias


u12 = u1 + u2
u12.dibujar(tipo=’intensidad’)

plt.show()

200 3.6
3.2
100 2.8
2.4
y(µm)

0 2.0
1.6
100 1.2
0.8
200 0.4

200 100 0 100 200 0.0


x(µm)
11.5.2 Interferómetro de Michelson

Ahora las fuentes están desplazadas en el eje z. Esto genera una distribución de intensidad en forma de anillos,
como se puede ver en la siguiente función.
#!/usr/local/bin/python
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#------------------------------------
# Autor: Luis Miguel Sanchez Brea
# Fecha 2013/10/24 (version 1.0)
# Licencia: GPL
#-------------------------------------
"""

11.5. Interferencia entre dos ondas esféricas 151


PIMCD2013-python, Publicación 1.0

Interferencias
"""
#Carga de paquetes y librerias
import scipy as sp
import matplotlib.pyplot as plt
import sys
sys.path.append(’../’)
from fuentesXY import fuenteXY, grados, um

#Definicion de las ondas


u1 = fuenteXY()
u1.onda_esferica(A = 1, r0 = (0 * um, 0 * um), z0 = -1000 * um, radio = 200 * um, mascara = True)
u2 = fuenteXY()
u2.onda_esferica(A = 1, r0 = (0 * um, 0 * um), z0 = -1500 * um, radio = 200 * um, mascara = True)

#Superposicion y dibujo de la intensidad


u12 = u1 + u2
u12.dibujar()

plt.show()

0.0000027
200
0.0000024
0.0000021
100
0.0000018
0.0000015
y(µm)

0
0.0000012

100 0.0000009
0.0000006

200 0.0000003

200 100 0 100 200 0.0000000


x(µm)
11.5.3 Desplazamiento combinado

Cuando tenemos un desplazamiento combinado x-z entre las dos fuentes, entonces la situación no es tan sencilla,
apareciendo unas elipses que nos son familiares si hemos ajustado un interferómetro de Michelson.
#!/usr/local/bin/python
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#------------------------------------

152 Capítulo 11. Interferencias


PIMCD2013-python, Publicación 1.0

# Autor: Luis Miguel Sanchez Brea


# Fecha 2013/10/24 (version 1.0)
# Licencia: GPL
#-------------------------------------
"""
Interferencias
"""

#Carga de paquetes y librerias


import scipy as sp
import matplotlib.pyplot as plt
import sys
sys.path.append(’../’)
from fuentesXY import fuenteXY, grados, um

#Definicion de las ondas


u1 = fuenteXY()
u1.onda_esferica(A = 1, r0 = (-20 * um, 0 * um), z0 = -1000 * um, radio = 100 * um, mascara = Fals
u2 = fuenteXY()
u2.onda_esferica(A = 1, r0 = (+20 * um, 0 * um), z0 = -1100 * um, radio = 100 * um, mascara = Fals

#Superposicion de las ondas


u12 = u1 + u2

#Representacion
u12.dibujar(tipo=’intensidad’)
plt.show()

0.0000036
200 0.0000032
0.0000028
100
0.0000024
0.0000020
y(µm)

0
0.0000016
0.0000012
100
0.0000008

200 0.0000004

200 100 0 100 200


x(µm)

11.5. Interferencia entre dos ondas esféricas 153


PIMCD2013-python, Publicación 1.0

11.5.4 Simulación del interferómetro de Michelson

Para finalizar mostramos un código que ejecuta un vídeo de qué ocurre en un interferómetro de Michelson cuando
uno de los espejos se desplaza.
#!/usr/local/bin/python
# -*- coding: utf-8 -*-
#------------------------------------
# Autor: Luis Miguel Sanchez Brea
# Fecha 2013/10/24 (version 1.0)
# Licencia: GPL
# Objetivo: grafica animada simulando un interferometro de Michelson
#-------------------------------------

import time
import gobject
import gtk #@UnusedImport

import scipy as sp

import matplotlib
matplotlib.use(’GTKAgg’)
import matplotlib.pyplot as plt
import matplotlib.cm as cm

um=1;
mm=1000*um;

fig = plt.figure(1)
a = plt.subplot(111)

wavelength=0.6328*um
k=2*sp.pi/wavelength

d=.25*mm #separacion entre espejos


x=sp.linspace(-15*mm,15*mm,256)
y=sp.linspace(-15*mm,15*mm,256)
z=250*mm #camino recorrido por la luz

X,Y=sp.meshgrid(x,y)

#acelera los calculos


cos_theta=z/sp.sqrt(X**2+Y**2+z**2)

I=1+sp.cos(2*k*d*cos_theta)

im = a.imshow( I, cmap=cm.gist_heat)

manager = plt.get_current_fig_manager()
cnt = 0
tstart = time.time()

def updatefig(*args):
#parametros globales definidos fuera de la funcion
global x, y, cnt, start, k, cos_theta, d
d-=.025*um
I=1+sp.cos(2*k*d*cos_theta)
im.set_array(I)
manager.canvas.draw()
cnt += 1
if cnt==250:
FPS=cnt/(time.time() - tstart)

154 Capítulo 11. Interferencias


PIMCD2013-python, Publicación 1.0

print "FPS: %4.2f" %FPS


return False
return True

cnt = 0

gobject.idle_add(updatefig)
plt.show()
tmp=raw_input("pulse enter para cerrar:")

11.5. Interferencia entre dos ondas esféricas 155


PIMCD2013-python, Publicación 1.0

156 Capítulo 11. Interferencias


CAPÍTULO 12

Módulos de Óptica

Una vez que conocemos los módulos científicos, en el Departamento de Óptica de la UCM hemos desarrollado
módulos específicos para aplicaciones ópticas. .

12.1 Clase camposXY

Autor Luis Miguel Sánchez Brea

Contenidos de este capítulo:


Introducción
Inicialización de la clase
Funciones que operan sobre los campos
Funciones que discretizan los campos
Funciones que propagan los campos
Funciones que dibujan los campos

Herramientas utilizadas en este tutorial:


Archivo con las funciones principales camposXY.py
Archivos necesarios para el análisis de camposXY:
Archivo con funciones de verificación de las gráficas camposXY_testsDibujar.py
Archivo con funciones de verificación de las operaciones con los campos
camposXY_testsOperaciones.py
Archivo con funciones de verificación de herramientas ópticas camposXY_testsOptica.py

12.1.1 Introducción

La mayor parte de las veces observamos un campo óptico en un plano XY, como es en el caso de la detección
sobre una pantalla o sobre una cámara CCD o CMOS.
Aquí es donde vamos a definir los campos eléctricos de observación. En este módulo realizaremos un tratamiento
escalar, que permitirá realizar operaciones sobre el campo eléctrico. Asimismo, será utilizado por otros módulos
como mascarasXY.py y fuentesXY.py, puesto que las máscaras y las fuentes se pueden tratar de idéntica forma.
En este capítulo se pretende estudiar el funcionamiento del módulo, no desarrollar ejemplos y aplicaciones ópticas.

12.1.2 Inicialización de la clase

Se inicializa con la función __init__ que dice las operaciones cuando se carga.

157
PIMCD2013-python, Publicación 1.0

def __init__(self, x = x0, y = y0, wavelength = lambda0):

Esta función inicia la clase y genera los arrays de datos necesarios


x, y - arrays unidimensionales de tamaño numx y numy, con las posiciones de los pixeles de la máscara
X, Y - arrays bidimensionales de tamaño (numx,numy) con las posiciones de los pixeles de la máscara
wavelength - longitud de onda incidente (monocromático)
u - campo o transmitancia generada por la fuente / máscara. Matriz bidimensional con el tamaño
(numx,numy).
Ejemplo de uso:
tamano = 250 * um
x0 = sp.linspace(-tamano * um, tamano * um, 256)
y0 = sp.linspace(-tamano * um, tamano * um, 256)
wavelength = 0.6328 * um

campo = campoXY(x = x0, y = y0, wavelength = wavelength)

El campo self.u es nulo hasta que no se utilice una función que lo rellena.

12.1.3 Funciones que operan sobre los campos

Archivo con funciones de verificación de las operaciones con los campos


camposXY_testsOperaciones.py
Se ejecutan las siguientes funciones:
def testCamposXY():
"""poner un comentario # en aquellas funciones que no se quieren ejecutar"""
#test_getAmplitud()
#test_getFase()
#test_quitarAmplitud()
#test_quitarFase()
#test_binarizarAmplitud()
#test_binarizarFase()
#test_campoXY_is()
test_campoXY_recortar()
#test_campoXY_extender()
#test_campoXY_redimensionar()
#test_campoXY_normalizar()
#test_cargarGuardatDatoscPickle()

Veamos algunas de las funciones que se utilizan:


def __add__(self, other):
def __mul__(self, other):

Devuelven un campo nuevo que es la suma o multiplicación:


u3.u = self.u + other.u
u3.u = self.u * other.u

Los dos campos que se suman o multiplican tienen que tener la misma dimensión. Un pequeño ejemplo de uso.
Utilizamos la clase fuenteXY, pero es lo mismo, pues una hereda las funciones de la otra.
u1 = fuenteXY()
u2 = fuenteXY()

#rellena la instancia con una onda plana


u1.onda_plana(A = 1, theta = sp.pi / 2, phi = .5 * grados)
u2.onda_plana(A = 1, theta = sp.pi / 2, phi = -.5 * grados)

158 Capítulo 12. Módulos de Óptica


PIMCD2013-python, Publicación 1.0

#segun la definicion de la clase la suma de dos ondas tambien es una onda


u12 = u1 + u2

Devuelven True o False si son solo de amplitud o de fase.


def isFase(self, nivelContraste = 0.01):
def isAmplitud(self, nivelContraste = 0.01):

Devuelven True o False si son solo de amplitud o de fase.


def recortar(self, xmin = 0, xmax = 1, ymin = 0, ymax = 1, campoNuevo = True):
def extender(self, xmin = 0, xmax = 1, ymin = 0, ymax = 1, ufondo = 0, campoNuevo = True):
def redimensionar(self, npuntos = 512, campoNuevo = True):

Otro tipo de operaciones que extrae información de los campos:


def getAmplitud(self, matriz = False, newField = False):
def getFase(self, matriz = False, newField = False):
def quitarAmplitud(self, matriz = False, newField = False):
def quitarFase(self, signo = False, matriz = False, newField = False):
def amplitud2fase(self, matriz = False, newField = False):
def fase2amplitud(self, matriz = False, newField = False):
def normalizar(self, tipo = ’intensidad’):

def binarizar(self, tipo = "amplitud", corte = None, nivel0 = None, nivel1 = None, campoNuevo = Fa
def perfil(self, punto1=’’, punto2=’’, npixels=1000, tipo=’intensidad’, order=2):

12.1.4 Funciones que discretizan los campos

Archivo con funciones de verificación de herramientas ópticas camposXY_testsDiscretizar.py


Se ejecutan las siguientes funciones:
def testCamposXY():
"""poner un comentario # en aquellas funciones que no se quieren ejecutar"""
test_discretizar_1()
test_discretizar_2()
test_discretizar_3()
test_discretizar_4()

def discretizar(self, tipo = ’amplitud’, numNiveles = 2, factor = 1, desfaseInicial = 0, campoNuev

12.1.5 Funciones que propagan los campos

Archivo con funciones de verificación de herramientas ópticas camposXY_testsOptica.py


Las funciones del modulo son las siguientes:
Transformada de Fourier
def fft(self, focal = 10 * mm, shift = True, quitar0 = True, matriz = False):
def ifft(self, focal = 10 * mm, shift = True, quitar0 = True, matriz = False):

Rayleigh-Sommerfeld:
def RS(self, z = 10 * mm, newField = True, xout = None, yout = None):
def progresion(self, zs=sp.linspace(1*mm,2*mm,500), tipo=’intensidad’, generarTemporales=True):

Los tests de verificación de estas funciones son las siguientes:

12.1. Clase camposXY 159


PIMCD2013-python, Publicación 1.0

def testCamposXY():
"""poner un comentario # en aquellas funciones que no se quieren ejecutar"""
#test_FFT_ondaPlana()
#test_FFT_Gauss()
#test_FFTrectangulo()
#test_RS()
test_RS_tamano_distinto()
#test_RS_inversa()
#test_variosPasos()

12.1.6 Funciones que dibujan los campos

Archivo con funciones de verificación de las gráficas camposXY_testsDibujar.py


Se ejecutan las siguientes funciones:
def testCamposXY():
test_dibujar_campo()
test_dibujar_perfil()
test_dibujar_perfil_manual()
test_dibujar_varios_campos()

Las ejemplos se basan en la función self.dibujar() que dibuja los datos que hay en los campos. Hay diversos tipos
de dibujos ‘intensidad’, ‘amplitud’, ‘fase’, campo’, ‘campoReal’.
def dibujar(self, tipo = ’intensidad’, logaritmico = False, normalizar = False,\
titulo = " ", nombreArchivo = ’’, dibujar = True, valorCorte = 1):
""" * tipo: ’intensidad’, ’amplitud’, ’fase’, campo’, ’campoReal’
* logartimico: True o False
* normalizar: ’maximo’, ’area’, ’intensidad’ """

Asimismo es muy sencillo realizar dibujos logaritmicos, simplemente incluyendo True o False en la variable.
Si la variable nombreArchivo no es nula, se guarda un dibujo.
Si la variable normalizar es True el valor máximo del dibujo es la unidad.
Si se incluye un valorCorte, entonces se satura el dibujo al valor determinado.
Otra función que también es interesante es la de dibujar_perfil, que permite sacar una gráfica 1D del camp entre 2
puntos.
def dibujar_perfil(self, punto1=’’, punto2=’’, npixels=1000, tipo=’intensidad’, normalizar=’’, ord

12.2 Clase fuentesXY

Autor Luis Miguel Sánchez Brea


Revisor José Luis Vilas Prieto

Contenidos de este capítulo:


Introducción
Cómo funciona la clase fuentesXY
Onda plana
Onda esférica
Haz de Gauss
Vórtice
Haz de Laguerre
Aberraciones: Haces de Zernike

160 Capítulo 12. Módulos de Óptica


PIMCD2013-python, Publicación 1.0

Archivos necesarios en esta sección:


fuentesXY.py. Archivo con las funciones principales fuentesXY.py
camposXY.py. clase con distintas funciones tales como inicialización de parámetros o discretización entre
otros camposXY.py
test_onda_plana.py. Archivo con las funciones principales test_onda_plana.py
test_onda_esferica.py. Archivo con las funciones principales test_onda_esferica.py
test_laguerre.py. Archivo con las funciones principales test_laguerre.py
test_1laguerre.py. Archivo con las funciones principales test_1laguerre.py
test_vortices.py. Archivo con las funciones principales test_vortices.py
test_zernikes.py. Archivo con las funciones principales test_zernikes.py
Contenidos de los archivos:
fuentesXY.py:
• onda_plana(). Simula una onda plana.
• onda_esferica(). Simula una onda plana.
• haz_gauss(). Genera un haz gaussiano.
• haz_vortice(). Genera un haz con estructura tipo vórtice.
• haz_laguerre(). Simula un haz de Laguerre.
• fZernike(). Simula haces con aberraciones de Zernike.

12.2.1 Introducción

Para poder realizar tratamientos numéricos con haces de luz, lo primero que tenemos que hacer es definirlos. La
clase fuentesXY hereda las propiedades de la clase campoXY. Añade fuentes que se observan en el plano XY.
Para ello hemos desarrollado una clase que contiene distintos tipos de fuentes tales como ondas planas, ondas
esféricas, haces de Gauss, haces de Laguerre, vórtices y Zernikes. Veremos aquí las definiciones y los ejemplos
de utilización.

12.2.2 Cómo funciona la clase fuentesXY

La clase fuentesXY está constituida por un conjunto de funciones. Estas funciones describen distintos tipos de
onda o fuentes de luz en forma de: onda plana, esférica, haz gaussiano, haz de Laguerre, o aberraciones de Zernike.
En el archivo fuentesXY_ejemplos, se muestra la obtención de las figuras de cada apartado.
La clase FuentesXY hereda funciones de la clase camposXY, que más general. No es necesario conocer el funcio-
namiento de esta clase camposXY. Cuando haga falta se hará referencia a ella como, por ejemplo, al realizar las
gráficas.
A modo de ejemplo veamos como se define y utiliza una fuente:
#definicion de parametros iniciales
tamano = 250 * um
npixels = 256
x0=sp.linspace(-tamano / 2, tamano / 2, npixels)
y0=sp.linspace(-tamano / 2, tamano / 2, npixels)
#Generacion de la clase
campo = fuenteXY(x = x0 , y = y0 , wavelength = 0.6328 * um)
#carga de la onda plana
campo.onda_plana(phi = 2 * grados, theta = 5 * grados)
#dibujar y guardar (si se escribe nombreArchivo)
campo.dibujar(tipo = ’fase’)

12.2. Clase fuentesXY 161


PIMCD2013-python, Publicación 1.0

12.2.3 Onda plana

Se define una onda plana como aquella que su frente de onda es plano y normal a la velocidad de fase. Son
uno de los tipos de ondas más habituales en física. Y están definidas en regiones suficientemente alejadas de
fuentes de carga. Su representación matemática se realiza mediante funciones armónicas, así sea una onda plana
propagándose en el eje z con amplitud A, frecuencia constante, 𝜔, y con vector de onda ⃗𝑘 = 2𝜋 𝜆 (sin(𝜃) sin 𝜑⃗
𝑥+
cos(𝜃) sin 𝜑⃗𝑦 ), la orientación de los frentes de onda queda definida por dicho vector. De este modo, una onda
plana se expresa como

𝐸 = 𝐴𝑒𝑖𝑘⃗𝑟 .

En rigor, estamos obviando la parte temporal, puesto que a nuestros fines carece de importancia.
En representación compleja una onda plana se expresa como

𝐸(r, 𝑡) = 𝐴 𝑒𝑖(k r−𝜔𝑡)

Podemos describir el vector k en coordenadas esféricas, de forma que tenemos


2𝜋
k= (sin 𝜃𝑠𝑖𝑛𝜑, cos 𝜃 sin 𝜑, cos 𝜑)
𝜆
2𝜋
Entonces, en el plano 𝑧 = 0, la componente k · r = 𝜆 (𝑠𝑖𝑛𝜃𝑠𝑖𝑛𝜑 𝑥 + 𝑐𝑜𝑠𝜃𝑠𝑖𝑛𝜑 𝑦).
Esto se puede programar en Python como
k*(X*sp.sin(theta1)*sp.sin(phi1)+Y*sp.cos(theta1)*sp.sin(phi1))

para poder representar esta ecuación en muchas posiciones x,y entonces X e Y son matrices generadas como
tamano=100*um
x=sp.linspace(-tamano,tamano, 256)
y=sp.linspace(-tamano,tamano, 256)
X,Y=sp.meshgrid(x,y)

La definición de la onda plana es sencilla:


Se puede realizar una representación en el plano XY de los frentes de onda como se muestra en la figura. La
inclinación de las franjas obedece a la dirección del vector de onda. Finalmente, puede apreciarse como los frentes
de onda son normales a dicho vector.
#!/usr/bin/env python
# -*- coding: utf-8 -*-

#----------------------------------------------------------------------
# Name: fuentesXY.py
#Author: Luis Miguel Sanchez Brea
# Licence: GPL
#----------------------------------------------------------------------
"""
Clases campoXY y Fuentes2D con las fuentes utilizadas para propagacion
"""

from __future__ import division

import scipy as sp
import matplotlib.pyplot as plt
import sys
sys.path.append(’../’)

from camposXY import campoXY, um, mm, grados


from fuentesXY import fuenteXY

def test_onda_plana():
"""Generacion de una onda plana"""

162 Capítulo 12. Módulos de Óptica


PIMCD2013-python, Publicación 1.0

#definicion de parametros iniciales


tamano = 250 * um
npixels = 256
#Generacion de la clase
x0=sp.linspace(-tamano / 2, tamano / 2, npixels)
y0=sp.linspace(-tamano / 2, tamano / 2, npixels)

campo = fuenteXY(x = x0 , y = y0 , wavelength = 0.6328 * um)


#carga de la onda plana
campo.onda_plana(phi = 2 * grados, theta = 5 * grados)
#dibujar y guardar
campo.dibujar(tipo = ’fase’)

test_onda_plana()
plt.show()

100 2.4

1.6
50
0.8
y(µm)

0 0.0

0.8
50
1.6

100 2.4

100 50 0 50 100
x(µm)
12.2.4 Onda esférica

De manera análoga a la definición de onda plana, una onda esférica es aquella cuyos frentes de onda son superficies
esféricas. Las ecuaciones de Maxwell postulan la existencia de ondas electromagnéticas mediante la ecuación de
ondas. La resolución en esféricas de esta ecuación da lugar a ondas esféricas como soluciones a las ecuaciones de
Maxwell. Así se puede probar como la magnitud del campo eléctrico viene definida mediante
𝐴 𝑖𝑘𝑟
𝐸= 𝑒 .
𝑟
Las ondas esféricas han sido implementadas dentro de la clase fuenteXY mediante la función onda_esferica,
como se muestra en el código a debajo. La onda encuentra su fuente de emisión centrada en el punto de coorde-
nadas x0, y0. Debido a que los frentes de onda son círculos concéntricos, se ha definido una máscara circular
con el fin de no cortar ningún frente de onda, representando así únicamente los frentes de onda en el interior de

12.2. Clase fuentesXY 163


PIMCD2013-python, Publicación 1.0

la máscara circular. Esto se logra con el último if del código. Finalmente se muestran las representaciones de la
amplitud y la fase.
En la figura se muestra la máscara de amplitud a la izquierda y a la derecha la fase en el plano XY. Es destacable
el decaimiento de la misma de con la distancia.

amplitud fase
300 300

200 200

100 100
y(µm)

y(µm)
0 0

100 100

200 200

300 300

300 200 100 0 100 200 300 300 200 100 0 100 200 300
x(µm) x(µm)

amplitud fase
300 300

200 200

100 100
y(µm)

y(µm)

0 0

100 100

200 200

300 300

300 200 100 0 100 200 300 300 200 100 0 100 200 300
x(µm) x(µm)

12.2.5 Haz de Gauss

Un haz gaussiano es aquel en el que cualquier plano normal a la dirección de propagación a distribución de
intensidades del campo eléctrico se ajusta a una gaussiana. Así sea una gaussiana de varianza w, centrada en el
punto (x0,y0). Así se define amplitud como
(𝑥−𝑥0 )2 (𝑦−𝑦0 )2
𝑖( 2
2𝑤𝑥
− 2
2𝑤𝑦
)
𝐴𝑚 = 𝐴𝑒
Mientras que la fase vale

𝛼 = 𝑒𝑖𝑘(𝑥 sin(𝜃) sin(𝜑)+𝑦 cos(𝜃) sin(𝜑))

164 Capítulo 12. Módulos de Óptica


PIMCD2013-python, Publicación 1.0

De este modo, el haz queda caracterizado por el producto de las mismas

𝐴𝑚 𝛼

Esta última ecuación desglosada en fase y amplitud es la que se implementa en la función haz_gauss
A continuación se muestra una representación del perfil de intensidades para un haz gaussiano.

1.0
400 0.9
0.8
200 0.7
0.6
y(µm)

0 0.5
0.4
200 0.3
0.2
400 0.1

400 200 0 200 400 0.0


x(µm)
12.2.6 Vórtice

Las estructuras tipo vórtice son aquellas en las que la fase se comporta linealmente con la variable angular (en
coordenadas cilíndricas), esto es 𝜑(𝑟, 𝜃, 𝑧) = 𝑚𝜃, siendo m el orden del vórtice.
Ahora veremos cómo se puede llamar a esta función

12.2. Clase fuentesXY 165


PIMCD2013-python, Publicación 1.0

amplitud mvortice =3 fase


300 300
200 200
100 100
y(µm)

y(µm)
0 0
100 100
200 200
300 300
300 200 100 0 100 200 300 300 200 100 0 100 200 300
x(µm) x(µm)
0.00 0.15 0.30 0.45 0.60 0.75 0.90 2.4 1.6 0.8 0.0 0.8 1.6 2.4

Ahora N vórtices
Vortices
(1) (2) (3) (4) (5)

12.2.7 Haz de Laguerre

Similarmente a los haces gaussianos se definen los modos de Laguerre como aquellos haces en los que el perfil de
intensidades sigue una distribución de modo de Laguerre, es decir una distribución
2
𝑘𝑟
−𝑖 2𝑅(𝑧)
2
𝑟
− 𝑤(𝑧) 𝑝 𝑟 2
2 +𝑖(2𝑝+𝑙+1)𝜁(𝑧) −𝑖𝑙𝜃 (−1) ( 𝑤(𝑧)2 )
𝑙/2 2𝑟2
𝑈 (𝑟, 𝜃, 𝑧) = 𝑒 𝑒 𝑒 𝐿𝑙𝑝 ( )
𝑤(𝑧)2

La función haz_laguerre realiza exactamente este cometido, pero desglosa el producto en pequeños términos
t1,t2,t3,t4.
Así se pueden extraer la amplitud y la fase del haz y ser representadas análogamente a los casos anteriores.

166 Capítulo 12. Módulos de Óptica


PIMCD2013-python, Publicación 1.0

amplitud n =2,k =2 fase


300 300
200 200
100 100
y(µm)

y(µm)
0 0
100 100
200 200
300 300
300 200 100 0 100 200 300 300 200 100 0 100 200 300
x(µm) x(µm)
0.0 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 2.4 1.6 0.8 0.0 0.8 1.6 2.4

Finalmente mostrados distintos modos de Laguerre para diferentes pares (l,p). Al aumentar l aumenta el número
de anillos, así como la intensidad local se reduce si se incrementa p.

(0,0) (1,0)
amplitud (2,0) (3,0)

(0,1) (1,1) (2,1) (3,1)

(0,2) (1,2) (2,2) (3,2)

(0,3) (1,3) (2,3) (3,3)

12.2. Clase fuentesXY 167


PIMCD2013-python, Publicación 1.0

(0,0) (1,0)
fase (2,0) (3,0)

(0,1) (1,1) (2,1) (3,1)

(0,2) (1,2) (2,2) (3,2)

(0,3) (1,3) (2,3) (3,3)

12.2.8 Aberraciones: Haces de Zernike

Los frentes de onda de un haz de luz pueden presentar perturbaciones, debidas a la propia propagación en el medio
o introducidas por elementos ópticos estas perturbaciones reciben el nombre de aberraciones del haz. La forma
más común de clasificar las aberraciones es en las llamadas aberraciones de Zernike, las cuales hacen uso de la
base de polinomio que les da nombre, polinomios de Zernike. Estos polinomios están definidos sobre el círculo
unidad, en términos de dos variables radial, 𝑟 y acimutal, 𝜃 junto con dos parámetros, (m,n) que representan el
orden del Zernike. Así se define si n es par

𝑍𝑛𝑚 (𝜌, 𝜙) = 𝑅𝑛𝑚 (𝜌) cos(𝑚 𝜙)

Mientra que si n es impar

𝑍𝑛−𝑚 (𝜌, 𝜙) = 𝑅𝑛𝑚 (𝜌) sin(𝑚 𝜙)

Donde
(𝑛−𝑚)/2
∑︁ (−1)𝑘 (𝑛 − 𝑘)!
𝑅𝑛𝑚 (𝜌) = 𝜌𝑛−2 𝑘
𝑘! ((𝑛 + 𝑚)/2 − 𝑘)! ((𝑛 − 𝑚)/2 − 𝑘)!
𝑘=0

Esto ha sido implementado en la clase fuentesXY mediante la función fZernike, con argumentos, n, m y radio
La gran utilidad de los polinomios de Zernike es que permiten expresar cualquier aberración del frente de ondas
𝑛
como una suma de términos 𝑍𝑚 . Así, si 𝑐𝑛𝑚 son los coeficientes del polinomio de Zernike, cualquier aberración
en la fase podrá escribirse como
∑︁
𝑛
𝜑= 𝑐𝑛𝑚 𝑍𝑚
𝑛𝑚

Por tanto, haciendo uso de los polinomios definidos con fZernike se determina la aberración del frente de ondas
calculando la variación en la fase.

168 Capítulo 12. Módulos de Óptica


PIMCD2013-python, Publicación 1.0

Así se ha obtenido mediante una representación cualquiera, la amplitud y la fase de un haz aberrado.

1000
amplitud 1000
fase

500 500
y(µm)

y(µm)
0 0

500 500

10001000 500 0 500 1000 10001000 500 0 500 1000


x(µm) x(µm)
0.0 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1.0 2.4 1.6 0.8 0.0 0.8 1.6 2.4

Existen distintos pares (𝑛, 𝑚) de especial importancia en óptica cuyo término de Zernike asociado recibe nombre
propio. Estos son:
(0, 0) Pistón.
(1, ±1) Tilt.
(2, −2) Astigmatismo.
(2, 0) Desenfoque.
(4, 0) Aberración Esférica.
A continuación se muestra una representación en fase y amplitud de las aberraciones más importantes

12.2. Clase fuentesXY 169


PIMCD2013-python, Publicación 1.0

polinomios de Zernike (n,m)


(0,0)

(1,-1) (1,1)

(2,-2) (2,0) (2,2)

(3,-3) (3,-1) (3,1) (3,3)

(4,-4) (4,-2) (4,0) (4,2) (4,4)

12.3 Clase mascarasXY

Autor Luis Miguel Sánchez Brea


Revisor José Luis Vilas Prieto
Para poder realizar tratamientos numéricos de difracción, es necesario realizar un sistema de máscaras que modi-
fiquen el haz de entrada. En esta sección veremos las máscaras en el plano (X,Y) que hemos definido, mediante la
clase mascaraXY. Las máscaras pueden ser de múltiples tipos, sin embargo en este capítulos nos limitaremos a
máscaras planas.
Además de las técnicas de diseño de máscaras veremos ejemplos de cómo utilizarlas. Adicionalmente, en el tema
de Difracción se muestra como la propagación de la luz que es modificada por estas máscaras.

170 Capítulo 12. Módulos de Óptica


PIMCD2013-python, Publicación 1.0

Contenidos de este capítulo:


Funcionamiento de la clase mascarasXY
Operaciones con máscaras
• Sumar Máscaras
• Restar Máscaras
• Rotar máscaras
Rendija
Doble Rendija
Cuadrados y rectángulos
Círculos y elipses
Anillo
Cruz
Dos niveles
Imagen
Escala de grises
Lentes
Axicón
Espiral de Arquímedes
Espiral de Laguerre
Red Forked
Superficie rugosa

Herramientas utilizadas en este tutorial:


Archivo con las funciones principales de campos mascarasXY.py
Archivo con las funciones principales de fuentes fuentesXY.py
Archivo con las funciones principales de mascaras mascarasXY.py
Descarga de archivos:
Clase máscaras con las funciones de los distintos tipos de máscara mascarasXY.py
Archivo con el ejemplo de una rendija ejemplo_rendija.py
Archivo con el ejemplo de una doble rendija ejemplo_doble_rendija.py
Archivo con el ejemplo de una máscara cuadrada ejemplo_cuadrado.py
Archivo con el ejemplo de una máscara circular ejemplo_circulo.py
Archivo con el ejemplo de una máscara imagen ejemplo_imagen.py
Archivo con el ejemplo de una máscara binaria ejemplo_dosNiveles.py
Ejemplo de una escala de grises ejemplo_escala_grises.py
Ejemplo de una máscara tipo espiral de Arquímedes ejemplo_espiral.py
Ejemplo de una máscara tipo espiral de Laguerre ejemplo_espiral.py
Archivo con el ejemplo de una lente ejemplo_lentes.py
Ejemplo de una red tipo Forked ejemplo_red_forked.py
Archivo con el ejemplo de una superficie rugosa ejemplo_superficie_rugosa.py
Contenidos de los archivos
mascarasXY.py :
• rendija() Simula una máscara tipo rendija plana.
• dobleRendija() Define una doble rendija.
• cuadrado() Simula una máscara cuadrada con transmitancia en el interior del cuadrado.

12.3. Clase mascarasXY 171


PIMCD2013-python, Publicación 1.0

• circulo() Simula una máscara circular con transmitancia en el interior del círculo.
• anillo() Define una máscara anular con transmitancia únicamente en la corona del anillo, no per-
mitiendo el paso de luz ni en el centro ni en el exterior del anillo.
• cruz() Simulación de una máscara con forma de cruz con transmitancia unidad en el interior de la
misma.
• dosNiveles() Discretiza una imagen en dos niveles de intensidad definidos.
• imagen() Permite modificar algunas propiedades de imágenes tales como normalizar su intensidad,
seleccionar un calar de color, invertir la imagen, entre otros.
• escalaGrises() Construye una escala de grises.
• lente() Determina la amplitud y la fase de la luz al atravesar una lente.
• axicon() Modela una lente tipo axicon.
• redForked() Modela una red tipo Forked.
• rugosidad() Simula una superficie rugosa.

12.3.1 Funcionamiento de la clase mascarasXY

Las máscaras están guardados en una clase, muy sencilla, que sirve de repositorio de funciones, cada una de ellas,
rellena la variable self.u que es la matriz donde se almacena la máscara en forma de coeficientes de transmisión
t(x,y).
Adicionalmente a las máscaras, se incluyen 4 operaciones sobre las máscaras (__add__, __sub__, __mul__, __ro-
tar__). Las tres primeras son operaciones son predefinidas de la clase y actuan sobre los simbolos +,-,*.
Las máscaras que se han definido dentro de la clase mascarasXY se muestran a continuación y representan distintos
tipos de máscaras bidimensionales que habitualmente se emplean en óptica, tales como rendijas, dobles rendijas,
máscaras cuadradas, circulares, anillo, en cruz, entre otras.
La mayoría de las máscaras definidas tienen la forma:
def nombre(self, r0, parametros, angulo):

En este caso r0 representa la posición (x,y) de la máscara, parametros son los parámetros propios de la máscara,
como el tamaño, y angulo es la rotación.

12.3.2 Operaciones con máscaras

Para facilitar la creación de nuevas máscaras, se han incluido las operaciones sobre las máscaras __add__,
__sub__, __mul__, __rotar__, ya definidas, las cuales respectivamente, suman, restan multiplican y rotan. Para
la definición de estas funciones se requiere de la clase macarasXY y camposXY.
Hay otra operación __rotar__ (no se implementa a través de un operador) y que se utiliza en las definiciones de
las máscaras para rotarlas.

Sumar Máscaras

En este primer ejemplo veremos la suma de dos mascaras una cuadrada y otra circular. El código para definir
este tipo de máscaras se explica más abajo. La estructura de la implementación en python consiste en declarar
las dos máscaras, t1, t2 y posteriormente sumarlas t2=t2+t1. Finalmente se dibujan a fin de obtener una
representación.
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#------------------------------------
# Autor: Luis Miguel Sanchez Brea

172 Capítulo 12. Módulos de Óptica


PIMCD2013-python, Publicación 1.0

# Fecha 2013/10/24 (version 1.0)


# Licencia: GPL
#-------------------------------------
"""Ejemplo de uso de mascaras"""

import sys
sys.path.append(’../’)
from mascarasXY import *

def sumar_mascaras():
ndatos = 128
tamano = 250 * um
x = sp.linspace(-tamano / 2, tamano / 2, ndatos)
y = sp.linspace(-tamano / 2, tamano / 2, ndatos)
wavelength = 0.6328 * um
#Mascara 1
t1 = mascaraXY(x, y, wavelength)
t1.cuadrado(r0 = (-50 * um, 0 * um), size = (50 * um, 50 * um), angulo = 0 * grados)
#Mascara 2
t2 = mascaraXY(x, y, wavelength)
t2.circulo(r0 = (50 * um, 0 * um), radius = (25 * um, 25 * um), angulo = 0 * grados)
#suma
t3 = t2 + t1
#funcion para dibujar varias mascaras a la vez
dibujar_varios_campos(campos=(t1,t2,t3),titulos=(’mascara 1’,’mascara 2’, ’suma’))

sumar_mascaras()
plt.show()

mascara 1 mascara 2 suma


100 100 100
50 50 50
0 0 0
50 50 50
100 100 100
100 50 0 50 100 100 50 0 50 100 100 50 0 50 100

0.00.10.20.30.40.50.60.70.80.91.0 0.00.10.20.30.40.50.60.70.80.91.0 0.00.10.20.30.40.50.60.70.80.91.0

En este primer ejemplo incluimos todo el código del archivo. A partir de ahora solo incluiremos la función. Es
decir, quitaremos el cabecero, las importaciones necesarias, que en estos ejemplos son
import sys
sys.path.append(’../’)
from mascarasXY import *

y la llamada final a la función


sumar_mascaras()
plt.show()

12.3. Clase mascarasXY 173


PIMCD2013-python, Publicación 1.0

Hay que tener cuidado qué ocurre cuando las dos máscaras solapan: el coeficiente de transmisión no puede ser
superior a 1 (no son medios activos). Si nos fijamos en mascaraXY.__add__ esto está considerado.

mascara 1 mascara 2 suma


100 100 100
50 50 50
0 0 0
50 50 50
100 100 100
100 50 0 50 100 100 50 0 50 100 100 50 0 50 100

0.00.10.20.30.40.50.60.70.80.91.0 0.00.10.20.30.40.50.60.70.80.91.0 0.00.10.20.30.40.50.60.70.80.91.0

Restar Máscaras

Con dos máscaras también se puede quitar la parte común entre ellas. El procedimiento a seguir en python es
análogo al caso de la suma. Nuevamente las máscaras cuadrada y circular se definirán más abajo en esta sección.

mascara 1 mascara 2 resta


30 30 30
20 20 20
10 10 10
0 0 0
10 10 10
20 20 20
30 30 30
30 20 10 0 10 20 30 30 20 10 0 10 20 30 30 20 10 0 10 20 30

0.00.10.20.30.40.50.60.70.80.91.0 0.00.10.20.30.40.50.60.70.80.91.0 0.00.10.20.30.40.50.60.70.80.91.0

Rotar máscaras

En este ejemplo se genera un rectángulo (ya veremos como) y se cambia su orientación representando así máscaras
rectangulares a 0, 45 y 90 grados. Nuevamente las máscaras cuadrada y circular se definirán más abajo en esta
sección.

174 Capítulo 12. Módulos de Óptica


PIMCD2013-python, Publicación 1.0

60
0 grados 60
45 grados 60
90 grados
40 40 40
20 20 20
0 0 0
20 20 20
40 40 40
60 60 60
60 40 20 0 20 40 60 60 40 20 0 20 40 60 60 40 20 0 20 40 60

0.00.10.20.30.40.50.60.70.80.91.0 0.00.10.20.30.40.50.60.70.80.91.0 0.00.10.20.30.40.50.60.70.80.91.0

El uso de la clase máscara se puede ver en el módulo correspondiente. Aquí pasamos a ver diversas máscaras que
luego se pueden utilizar en difracción y en otros ejemplos de óptica.

12.3.3 Rendija

Una rendija consiste en una región del plano de la máscara comprendida entre dos rectas paralelas, tal que en
dicha región se permite el paso de la luz, fuera de esta zona, la luz no atraviesa la máscara. Así dentro de la clase
mascarasXY se ha definido la función rendija. En ella se dan de argumentos el centro de la misma x0, la
anchura de la rendija um y el ángulo de inclinación en grados. El código define dos rectas verticales xmin y max,
cuyo espacio abarcado determina el paso de luz, la rendija. Estas dos rectas son verticales, por ello se aplica la
rotación para describir la inclinación de la rendija. Finalmente el if, determina la región del plano que puede ser
atravesada por la luz.
A modo de ejemplo se muestra la representación de una rendija con una anchura 100 um.

12.3. Clase mascarasXY 175


PIMCD2013-python, Publicación 1.0

1.0
100 0.9
0.8
50 0.7
0.6
y(µm)

0 0.5
0.4
50 0.3
0.2
100 0.1

100 50 0 50 100 0.0


x(µm)
12.3.4 Doble Rendija

Análogamente con el caso de una rendija, se puede definir una doble rendija como una composición o suma de dos
rendijas simples. El estudio de dobles rendijas es de vital importancia en Óptica constituyendo una de las bases
de la difracción y a interferometría. Obsérvese como el código Python de la función doblerendija consiste en una
suma de dos rendijas simples separadas una distancia definida por el argumento separación.
Así se obtiene un ejemplo de representación de la doble rendija:

176 Capítulo 12. Módulos de Óptica


PIMCD2013-python, Publicación 1.0

1.0
10 0.9
0.8
5 0.7
0.6
y(µm)

0 0.5
0.4
5 0.3
0.2
10 0.1

10 5 0 5 10 0.0
x(µm)
12.3.5 Cuadrados y rectángulos

Otro tipo de máscara de amplio uso en óptica son las máscaras cuadradas o rectangulares. Estas máscaras con-
sisten en una región del espacio comprendida entre cuatro rectas paralelas dos a dos, tales que los ángulos que
forman son todos rectos, de modo que la transmitancia en cualquier punto interior a dicha región es la unidad. La
implementación del código de python supone la definición del centro del cuadrado/rectángulo, así como el tamaño
de sus lados. Finalmente se define una rotación y los puntos de transmitancia unidad.
Su representación queda así

12.3. Clase mascarasXY 177


PIMCD2013-python, Publicación 1.0

cuadrado rectangulo
100 100

50 50

0 0

50 50

100 100
100 50 0 50 100 100 50 0 50 100

0.0 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1.0 0.0 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1.0

12.3.6 Círculos y elipses

De manera análoga a la máscara cuadrada, se define una máscara circular como aquella que permite el paso de luz
para aquellos rayos que incidan en el interior del círculo o elipse. Para ello se define el origen del círculo con r0
y los semiejes de la elipse radiox y radioy, en caso de ser iguales, se obtiene un círculo. La línea de código
ipasa = (Xrot - x0) * * 2 / radiusx * * 2 + (Yrot - y0) * * 2 / radiusy * * 2 < 1

establece el paso de luz únicamente en el interior del círculo o elipse.


Así resulta la representación de la máscara

circulo elipse
100 100

50 50

0 0

50 50

100 100
100 50 0 50 100 100 50 0 50 100

0.0 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1.0 0.0 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1.0

La elipse utiliza la misma programación y se cambian los parámetros.


t1.circulo(r0 = (0 * um, 0 * um), radius = (100 * um, 50 * um), angulo = 45 * grados)

178 Capítulo 12. Módulos de Óptica


PIMCD2013-python, Publicación 1.0

12.3.7 Anillo

Una estructura tipo anillo consiste en dos regiones circulares o elípticas concéntricas, tales que el paso de luz
sucede únicamente en la región comprendida entre ambas. Para su implementación se definen dos vectores con
dos compontes cada uno, llamados radio1 y radio2 tales que sus componentes representan los semiejes de
las elipses interior y exterior. Nuevamente el ángulo theta describe la inclinación del anillo. Por tanto se definen
dos círculos o elipses concéntricos tales que se permite el paso de luz en su interior, estos están binarizados, es
una máscara o matriz con 1 en aquellos puntos interior y cero en los exteriores. Para construir la estructura tipo
anillo, se restan ambos círculos (máscaras), de este modo que la corona es la única región con unos en su interior.
Como resultado se tiene

anillo otro tipo


100 100

50 50

0 0

50 50

100 100
100 50 0 50 100 100 50 0 50 100

0.0 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1.0 0.0 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1.0

También se pueden hacer figuras más interesantes si no se mantiene que el tamaño del anillo 2 sea mayor que el
anillo uno, como en el segundo ejemplo, donde
t1.anillo(r0 = (0 * um, 0 * um), radius1 = (50 * um, 100 * um), radius2 = (100 * um, 50 * um), ang

12.3.8 Cruz

Las máscaras tipo cruz se pueden interpretar como la superposición de dos máscaras tipo cuadrado rotadas entre
sí 90º permitiento el paso de luz únicamente en la región interior a la cruz. Por tanto para la implementación de
este tipo de máscara, bastará con definir las dos máscaras cuadradas, mediante su origen (común) r0 y los lados
de cada cuadrado. De este modo se definen t1 y t2 como dos máscaras cuadradas rotadas entre sí 90º. La suma de
las matrices de cada máscara, da lugar a la máscara de cruz, no obstante hay puntos en la matriz suma t1+t2 con
transmitancia mayor que la unidad por ello en la última línea de código se redefinen todos los puntos con valor
mayor a uno, reescalándolos a la unidad.
Así queda representada como

12.3. Clase mascarasXY 179


PIMCD2013-python, Publicación 1.0

1.0
100 0.9
0.8
50 0.7
0.6
y(µm)

0 0.5
0.4
50 0.3
0.2
100 0.1

100 50 0 50 100 0.0


x(µm)
12.3.9 Dos niveles

Esta función discretiza una imagen en dos niveles de intensidad definidos por los argumentos nivel1 y nivel2. Esta
discretización se hace acorde a un nivel de corte o de referencia, xcorte, de modo que todos aquellos puntos de
la imagen con intensidad menor xcorte adquieren el valor nivel1, así mismo, los niveles mayores a xcorte
son asignados con intensidad nivel2. La estructura del programa realiza este proceso en dos líneas, la primera
crea una matriz de nivel1 con el mismo tamaño que la imagen, X. La segunda línea asigna la intensidad nivel2
a aquellas columnas de la matriz con nivel de intensidad mayor que el nivel de corte, xcorte.
Así resulta

180 Capítulo 12. Módulos de Óptica


PIMCD2013-python, Publicación 1.0

0.250
100 0.225
0.200
50 0.175
0.150
y(µm)

0 0.125
0.100
50 0.075
0.050
100 0.025

100 50 0 50 100 0.000


x(µm)
12.3.10 Imagen

Si no sabemos crear una máscara, o resulta demasiado complicada la podemos dibujar en otro programa, como
Gimp, Inkscape. Entonces la podemos cargar como máscara a través de la función imagen. Para ello necesi-
tamos la librería Image específica de python para trabajar con imágenes. Posteriormente se carga la imagen‘
especificada con el argumento nombre, para ello se hace uso de dos funciones específicas cargarImagen y
visualizarimagen, las cuales cargar una imagen desde un archivo con localización específica, y la visuali-
zan respectivamente. No entramos a explicar el funcionamiento de estas dos funciones, estando definidas en una
clase externa imagenesLM, y sin poseer mayor dificultad. Image.open(nombre) abre la imagen pero no la
muestran en pantalla, sino que la almacena como variable im, recuérdese que la imagen ya está siendo visualizada,
puesto que se empleó visualizarImagen, en la línea anterior. im.transpose(1), transpone la imagen,
intercambiando filas por columnas. Mediante im.split() se descompone la imagen en colores extrayendo un
array con las componentes RGB de la imagen. Debido a que no interesa el color sino la intensidad, se selecciona la
componente de color canal especificada en el argumento de nuestra función imagen con colores[canal].
El resto del programa son if y suponen una serie de condiciones. El primero, cambia el tamaño de la imagen
manteniendo la relación de aspecto permitiendo además una rotación de la imagen. Para ello se extraen las di-
mensiones de la imagen y se definen dos arrays para finalmente generar un mallado y aplicar una rotación. El
siguiente if plantea una inversión de intensidad, ello lo logra restando la intensidad máxima de la imagen menos
la intensidad de la imagen en cada punto y generando así una imagen invertida en intensidades. Finalmente, el
último if, normaliza las intensidades a la unidad.
Veamos un ejemplo de sencillo de cómo cargar una imagen.

12.3. Clase mascarasXY 181


PIMCD2013-python, Publicación 1.0

1.0
100 0.9
0.8
50 0.7
0.6
y(µm)

0 0.5
0.4
50 0.3
0.2
100 0.1

100 50 0 50 100 0.0


x(µm)
12.3.11 Escala de grises

La función escalaGrises construye un vector con el número de niveles de gris establecidos en el argumento
NumNiveles, con intensidades máxima nivelMax y mínima nivelMin. Para ello se define un vector t de
ceros a fín de generar la estructura de nuestro vector salida, sobre este vector sobrescribiremos los datos finales.
Posteriormente, se define un array, niveles con intensidades entre nivelMin y nivelMax del tamaño del vector
x definido en la clase mascarasXY. Así mismo, otro vector xpos de números enteros con valores entre 0 y el
entero más próximo a la longitud de x, con numNiveles + 1 componentes. El objetivo de este vector será de-
terminar las filas de t a las que se asocia cada nivel de gris. Esto último se logra mediante el bucle for, asociando
a las componentes de t comprendidas entre los valores de dos compontes consecutivas de xpos el valor de gris
especificado.
De este modo se obtiene la figura

182 Capítulo 12. Módulos de Óptica


PIMCD2013-python, Publicación 1.0

1.0
100 0.9
0.8
50 0.7
0.6
y(µm)

0 0.5
0.4
50 0.3
0.2
100 0.1

100 50 0 50 100 0.0


x(µm)
12.3.12 Lentes

Mediante la función lente calculamos la fase y la amplitud de la luz al atravesar una lente con astigmatismo. Así
sean f1 y f2 las focales, de modo que la fase, 𝜑 de la luz viene determinada por
Así mismo, la transmitancia se supone determinada por la superficie de la lente, la cual modelamos mediante
una máscara circular. Por tanto se define el centro de la lente, con r0 y la focal f1, f2, para definir la máscara
circular/elíptica con orientación determinada por el ángulo especificado en el argumento de la función conforme
se describió en la sección de la máscara círculo. De este modo, queda definida la amplitud mediante la máscara.
Finalmente, para la fase basta implementar la ecuación anterior.
En los siguientes ejemplos se muestra una representación de la amplitud y la fase queda

12.3. Clase mascarasXY 183


PIMCD2013-python, Publicación 1.0

amplitud fase
400 400

200 200
y(µm)

y(µm)
0 0

200 200

400 400

400 200 0 200 400 400 200 0 200 400


x(µm) x(µm)

amplitud fase
400 400

200 200
y(µm)

y(µm)

0 0

200 200

400 400

400 200 0 200 400 400 200 0 200 400


x(µm) x(µm)
En el caso de una lente con estigmatismo, simplemente es cambiar las focales
t1.lente(r0 = (0 * um, 0 * um), radius = (500 * um, 250 * um), focal = (50 * mm, 12.5 * mm))

12.3.13 Axicón

Un axicón es un tipo particular de lente con superficie cónica. Su función principal es la de transformar haces co-
limados en anillos. La función axicon con argumentos, radio y altura del cono, así como índice de refracción
del vidrio, calcula la intensidad y la fase de la luz al atravesar dicho elemento óptico. Así una vez definidos el
origen r0, el vector de onda k y la distancia, r, de la generatriz al eje del cono, se implementa la superficie del
elemento óptico como h en términos de r. Definida la superficie del axicon, queda imponer una serie de restric-
ciones tales como que la altura, h no puede ser nunca negativa, esto es, el axicon, no se extiende en el espacio
objeto. Así mismo, se determina la región de transmitancia de la luz como aquella en la que incide la luz sobre el
axicon, es decir, únicamente son válidos los puntos de la base del axicon, r<radius. Finalmente se determina
la fase con la relación

184 Capítulo 12. Módulos de Óptica


PIMCD2013-python, Publicación 1.0

..:math:: e^{ik(n-1)h}
En el ejemplo se muestra la amplitud y la fase a la salida de un axicon

amplitud fase
100 100

50 50
y(µm)

y(µm)
0 0

50 50

100 100
100 50 0 50 100 100 50 0 50 100
x(µm) x(µm)
0.0 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1.0 2.4 1.6 0.8 0.0 0.8 1.6 2.4

En el caso de una lente con estigmatismo, simplemente es cambiar las focales


t1.lente(r0 = (0 * um, 0 * um), radius = (500 * um, 250 * um), focal = (50 * mm, 12.5 * mm))

12.3.14 Espiral de Arquímedes

Se muestra cómo desarrollar la espiral de Arquímedes. Resulta un poco complicado entender como se desarrolla
(yo tampoco lo entiendo),

100 potencia 1 100 potencia 2


50 50

0 0

50 50

100100 50 0 50 100 100100 50 0 50 100

0.0 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1.0 0.0 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1.0

12.3. Clase mascarasXY 185


PIMCD2013-python, Publicación 1.0

12.3.15 Espiral de Laguerre

Esta función hace uso de las clases fuentesXY así como de máscaras. Por defecto la
EspiralLaguerreGauss obtiene la espiral en amplitud, salvo que se especifique lo contrario. Se im-
porta la clase fuentesXY, y se genera un haz tipo Laguerre conforme se vio en la sección de fuentes. A
continuación definimos el radio del haz que se va a representar siendo este la mitad del cuadrado de represen-
tación. Resta implementar la espiral, para ello se llama a la clase mascarasXY, y se implemente la función
círculo ya definida anteriormente. Posteriormente se extrae la fase con angle y se normalizan las intensidades,
finalmente se realiza un binarización. Alternativamente se puede especificar la fase en el argumento de la función,
el último if es el encargado de esta tarea.

1.0
100 0.9
0.8
50 0.7
0.6
y(µm)

0 0.5
0.4
50 0.3
0.2
100 0.1

100 50 0 50 100 0.0


x(µm)

186 Capítulo 12. Módulos de Óptica


PIMCD2013-python, Publicación 1.0

amplitud fase
100 100

50 50
y(µm)

y(µm)
0 0

50 50

100 100
100 50 0 50 100 100 50 0 50 100
x(µm) x(µm)
0.0 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1.0 0.0 0.4 0.8 1.2 1.6 2.0 2.4 2.8

También se puede hacer de fase, cambiando el tipo


t.EspiralLaguerreGauss(tipo = ’fase’, l = 4, r0 = (0 * um, 0 * um), w0 = 20 * um, z = 0.01 * um)

12.3.16 Red Forked

Una red tipo Forked es un tipo de máscara binaria de tipo franjas en la que existe una macla, punto en el cual un
conjunto de franjas convergen. Este tipo de red se puede definir mediante el siguiente código.
El resultado es

12.3. Clase mascarasXY 187


PIMCD2013-python, Publicación 1.0

1.0
100 0.9
0.8
50 0.7
0.6
y(µm)

0 0.5
0.4
50 0.3
0.2
100 0.1

100 50 0 50 100 0.0


x(µm)
12.3.17 Superficie rugosa

La superficies rugosas son de gran importancia práctica en Óptica pues muchas de las superficies tienen un cierto
grado de rugosidad. La rugosidad es un proceso aleatorio que se caracteriza a través de ciertos parámetros como
la altura media y la longitud de correlación, que puede ser distinta en las direcciones x e y, por los procesos de
fabricación de las superficies. En el siguiente código se muestra un ejemplo de cómo generar una superficie rugosa
con unos parámetros dados.
La fase y amplitud quedan representadas como sigue

188 Capítulo 12. Módulos de Óptica


PIMCD2013-python, Publicación 1.0

100 2.4

1.6
50
0.8
y(µm)

0 0.0

0.8
50
1.6

100 2.4

100 50 0 50 100
x(µm)
topografia
1.6
100
1.2

50 0.8

0.4
y(µm)

0
0.0

50 0.4

0.8
100
1.2
100 50 0 50 100
x(µm)

12.3. Clase mascarasXY 189


PIMCD2013-python, Publicación 1.0

190 Capítulo 12. Módulos de Óptica


CAPÍTULO 13

Difracción en campo cercano

Autor Luis Miguel Sánchez Brea


Revisor José Luis Vilas Prieto

Contenidos de este capítulo:


Introducción
Programación
Difracción por una abertura cuadrada
Difracción por una abertura cuadrada: varias distancias
Difracción por una abertura circular
Difracción por un borde
Difracción por una doble rendija
Difracción por una lente
Difracción por una lente binaria

Herramientas utilizadas en este tutorial:


Módulo de campos camposXY.py.
Módulo de fuentes fuentesXY.py.
Módulo de máscaras mascarasXY.py.
Archivos necesarios para esta sección:
Ejemplo de difracción en campo cercano por una máscara cuadrada difraccion_cuadrado.py.
Ejemplo de difracción en campo cercano por una mascara cuadrada a varias distancias
difraccion_cuadrado_varias_distancias.py.
Ejemplo de difracción en campo cercano por un círculo difraccion_circulo.py.
Ejemplo de difracción en campo cercano por círculo a varias distancias
difraccion_circulo_varias_distancias.py.
Ejemplo de difracción en campo cercano por un borde difraccion_borde.py.
Ejemplo de difracción en campo cercano por un borde a varias distancias
difraccion_borde_varias_distancias.py.
Ejemplo de difracción en campo cercano por una doble rendija difraccion_doble_rendija.py.
Ejemplo de difracción en campo cercano por una lente difraccion_lente.py.
Ejemplo de difracción en campo cercano por una lente binaria difraccion_lente_binaria.py.

ejemplos de visualización 3D

191
PIMCD2013-python, Publicación 1.0

Ejemplo de difracción 3D en campo cercano por una lente difraccionlente3D_1.py.


Ejemplo de difracción 3D en campo cercano por una lente difraccionlente3D_2.py.

13.1 Introducción

En esta sección analizaremos la difracción en campo cercano bajo la aproximación de Fresnel.


Según esta ecuación, el campo propagado a una cierta distancia 𝑧 de un elemento difractor, se calcula a través de
la siguiente integral
∫︁ ∫︁
1 𝑖𝑘𝑧 2 2
E0 (𝜉, 𝜂)𝑒𝑖 2𝑧 [(𝑥−𝜉) +(𝑦−𝜂) ] 𝑑𝜉𝑑𝜂
𝑘
E(𝑥, 𝑦, 𝑧) = 𝑒
𝑖𝜆𝑧

donde E0 (𝜉, 𝜂) = 𝑡(𝜉, 𝜂)E𝑖𝑛𝑐 (𝜉, 𝜂) es el campo a la salida del elemento difractor que, bajo la aproximación
de elemento delgado se obtiene como la multiplicación del campo incidente E𝑖𝑛𝑐 (𝜉, 𝜂) por el coeficiente de
transmisión de la máscara difractora 𝑡(𝜉, 𝜂)

13.2 Programación

Para el desarrollo de los ejemplos de esta sección hemos programado numéricamente el cálculo de esta integral a
partir de &apos;fast-Fourier-transform based direct integration (FFT-DI) method&apos; según el artículo de Shen
y Wang 1 . Este algoritmo lo hemos implementado en la clase camposXY (que es heredada por mascarasXY y
fuentesXY) con el nombre de RS (a partir de Rayleigh-Sommerfed, que es una versión ligeramente mejorada de
la integral de Fresnel).
Por consiguiente, si tenemos un campo, máscara o fuente, tendrá un método RS que podemos aplicar. Si tenemos
un campo de tamaño NxM, el resultado de la propagación será también NxM, aunque existe un parámetro de
amplificación que nos permite calcular tamaños del plano de observación mayores (jN)x(jM).
La ventaja que tiene es que nos devuelve un parametro de calidad que nos informa si la aproximación se ha
realizado debidamente. Si el parámetro de calidad (almacenado en self.calidad) tiene un valor superior a 1, la
propagación se ha realizado convenientemente.
def RS(self, z = 10 * mm, newField = True, tipo=’z’, xout = None, yout = None):
"""Metodo de Fast-Fourier-Transform para la integracion numerica
de la Formula de la difraccion de Rayleigh-Sommerfeld.
Extraido de Applied Optics vol 45 num 6 (1102-1110) - 2006
- z es la distancia (si es <0 se realiza la propagacion inversa
- newfield, si es False el calculo se queda en la instancia, si es True se genera
- xout, yout se utilizan para decir las coordenadas de salida (para la amplificaci

el tamaño del pixel debe ser igual, aunque la máscara puede tener distinto tamaño
"""

xin = self.x; yin = self.y


x1 = self.x[0]; y1 = self.y[0]
xin1 = self.x[0]; yin1 = self.y[0]

if xout == None:
xout = self.x
if yout == None:
yout = self.y

nx = len(xout); ny = len(yout)
1 Fabin Shen and Anbo Wang &quot;Fast-Fourier-transform based numerical integration method for the RayleighSommerfeld diffraction

formula&quot; Applied Optics vol. 45 num 6. pp. 1102-1110 (2006).

192 Capítulo 13. Difracción en campo cercano


PIMCD2013-python, Publicación 1.0

dx = xout[1] - xout[0]; dy = yout[1] - yout[0]

#parametro de calidad
dr_real = sp.sqrt(dx ** 2 + dy ** 2)
rmax = sp.sqrt((xout ** 2).max() + (yout ** 2).max())
dr_ideal = sp.sqrt(self.wavelength ** 2 + rmax ** 2 + 2 * self.wavelength * sp.sqr
self.calidad = dr_ideal / dr_real
#siempre que se hace un cálculo se informa por consola de la calidad de este.
if(self.calidad > 1):
print ’Buena aproximacion: factor ’, self.calidad
else:
print ’Necesita un muestreo mayor: factor’, self.calidad

#matriz computada
W = 1
U = sp.zeros((2 * ny - 1, 2 * nx - 1), dtype = complex)
U[0:ny, 0:nx] = W*self.u #el transpose lo he cambiado porque daba problemas para
xext = x1 - xin[::-1] #da la vuelta
xext = xext[0:-1]
xext = sp.concatenate((xext, self.x - xin1))
yext = y1 - yin[::-1]
yext = yext[0:-1]
yext = sp.concatenate((yext, self.y - yin1))
Xext, Yext = sp.meshgrid(xext, yext)

#permite calcula la propagacion y la propagacion inversa, cuando z<0.


if z > 0:
H = kernelRS(Xext, Yext, self.wavelength, z, tipo=tipo)
else:
H = kernelRSinversa(Xext, Yext, self.wavelength, z, tipo=tipo)

#calculo de la transformada de Fourier


S = ifft2(fft2(U) * fft2(H)) * dx * dy #el transpose lo he cambiado porque daba p
Usalida = S[ny - 1:, nx - 1:] #hasta el final

#los calculos se pueden dejar en la instancia o crear un nuevo campo (más usual).
if newField == True:
campoSalida = campoXY(self.x, self.y, self.wavelength)
campoSalida.u = Usalida / z
campoSalida.calidad=self.calidad
return campoSalida
else:
self.u = Usalida / z

Todos los cálculos realizados en esta sección serán númericos, lo cual nos da una gran ventaja computacional:
solamente tenemos que describir la fuente y la máscara, y tendremos el campo difractado a una distancia z.

13.3 Difracción por una abertura cuadrada

Como primer ejemplo realizaremos la propagación en campo cercano de una abertura bidimensional cuadrada.
Utilizaremos este ejemplo para explicar el proceso.
Se definen los parámetros dimensionales de las máscaras y los parámetros ópticos (longitud de onda).
Se crea una fuente de iluminación a través de la clase fuenteXY().
Se crea una máscara con las dimensiones apropiadas.
Se realiza la Aproximación de elemento delgado: u2 = u1 * t1
se realiza la propagación: u3 = u2.RS(z = z_difraccion, newField = True)
Se dibuja el resultado.

13.3. Difracción por una abertura cuadrada 193


PIMCD2013-python, Publicación 1.0

El código empleado y las gráficas resultantes son las siguientes.


#!/usr/local/bin/python
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#------------------------------------
# Autor: Luis Miguel Sanchez Brea
# Fecha 2013/10/24 (version 1.0)
# Licencia: GPL
#-------------------------------------

import sys
sys.path.append(’../’)
sys.path.append(’../general/’)

from camposXY import * #@UnusedWildImport


from fuentesXY import fuenteXY
from mascarasXY import mascaraXY

def difraccion_cuadrado():
#tamano de area de visualizacion
tamano = 100 * um
ndatos = 256
x0 = sp.linspace(-tamano / 2, tamano / 2, ndatos)
y0 = sp.linspace(-tamano / 2, tamano / 2, ndatos)
#longitud de onda
lambda0 = 0.6238 * um

#fuente de iluminacion
u1 = fuenteXY(x = x0, y = y0, wavelength = lambda0);
u1.onda_plana(A = 1, theta = 0 * grados, phi = 0 * grados)

#mascara
radio = 20 * um
t1 = mascaraXY(x = x0, y = y0, wavelength = lambda0)
t1.cuadrado(r0 = (0 * um, 0 * um), size = (60 * um, 60 * um), angulo = 0 * grados)
u2 = u1 * t1

#plano de observacion
z_difraccion = 500*um

#propagacion y acondicionamiento grafica


u3 = u2.RS(z = z_difraccion, newField = True)
texto = "z=%d $\mu m$" % (z_difraccion)

dibujar_varios_campos(campos=(u2,u3),titulos=(’cuadrado’, texto))

difraccion_cuadrado()
plt.show()

194 Capítulo 13. Difracción en campo cercano


PIMCD2013-python, Publicación 1.0

cuadrado z=500 µm
40 40

20 20

0 0

20 20

40 40
40 20 0 20 40 40 20 0 20 40

0.0 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1.0 0.0 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1.0

13.4 Difracción por una abertura cuadrada: varias distancias

En el ejemplo anterior se vislumbra el comportamiento general en campo cercano: la distribución de intensidad se


asemeja algo al objeto, pero los efectos difractivos producen fluctuaciones de intensidad que aumentan a medida
que nos separamos del objeto difractor. Para ver esto de forma más rigurosa, en el siguiente ejemplo se muestra
la distribución de intensidad en varios planos. Para ello generamos dos funciones, mascara_cuadrado() que
calcula el campo inicial
def mascara_cuadrado():
tamano = 25 * um
ndatos = 256
x0 = sp.linspace(-tamano / 2, tamano / 2, ndatos)
y0 = sp.linspace(-tamano / 2, tamano / 2, ndatos)
lambda0 = 0.6238 * um

u1 = fuenteXY(x = x0, y = y0, wavelength = lambda0);


u1.onda_plana(A = 1, theta = 0 * grados, phi = 0 * grados)

t1 = mascaraXY(x = x0, y = y0, wavelength = lambda0)


t1.cuadrado(r0 = (0 * um, 0 * um), size = (20 * um, 20 * um), angulo = 0 * grados)

u2 = u1 * t1
return u2

y otra que recibe el campo u2 y genera un bucle con difrerentes distancias


def difraccion_varias_posiciones(u2, distancias):

plt.figure(figsize=(12,6))

num_figuras = len(distancias)

filas = [1, 1, 1, 2, 2, 2, 3, 3, 3, 2, 4, 4, 4, 4, 5, 4]
columnas = [1, 2, 3, 2, 3, 3, 3, 3, 3, 5, 3, 3, 4, 4, 3, 4]

extension=[u2.x[0],u2.x[-1],u2.y[0],u2.y[-1]]
plt.subplot(filas[num_figuras], columnas[num_figuras], 1)

13.4. Difracción por una abertura cuadrada: varias distancias 195


PIMCD2013-python, Publicación 1.0

h1 = plt.imshow(abs(u2.u) ** 2, interpolation = ’bilinear’,


origin = ’lower’, extent = extension)
h1.set_cmap("gist_heat")
plt.title(u’mascara’)

for z, i in zip(distancias, range(num_figuras)):


#difraccion a la distancia estipulada
u3 = u2.RS(z = z, newField = True)
texto = "z=%d $\mu m$" % (z)
#carga de los dibujos
plt.subplot(filas[num_figuras], columnas[num_figuras], i + 2)
plt.axis(’off’)
plt.title(texto)
h1 = plt.imshow(abs(u3.u) ** 2, interpolation = ’bilinear’,
origin = ’lower’, extent = extension)
h1.set_cmap("gist_heat")
plt.tight_layout()

El campo total se genera llamando a las dos funciones secuencialmente, transmitiendo el valor u2 de una a la otra
función:
u2=mascara_cuadrado()
difraccion_varias_posiciones(u2, distancias = sp.linspace(25 * um, 2* mm, 5))
plt.show()

mascara z=25 µm z=34 µm z=43 µm z=53 µm


10
5
0
5
10
10 5 0 5 10

z=62 µm z=71 µm z=81 µm z=90 µm z=100 µm

Eventualmente, si la distancia de propagación fuera mucho mayor que la distancia de Fresnel, 𝑧 >> 𝑎2 /𝜆, el
campo difractado sería la transformada de Fourier del objeto (pues estamos iluminando con onda plana), en este
caso una función sinc.

13.5 Difracción por una abertura circular

Una máscara de gran importancia en la óptica es la abertura circular. Según hemos definido la clase mascarasXY,
para determinar el campo generado por la máscara circular, los ejemplos anteriores son completamente válidos, y
solamente es necesaio intercambiar la función de creación de la máscara
t1.circulo(r0 = (0 * um, 0 * um), radius = (radio, radio))

Se muestran los resultados, pero no el código que es casi idéntico

196 Capítulo 13. Difracción en campo cercano


PIMCD2013-python, Publicación 1.0

circulo z=1282 µm
40 40

20 20

0 0

20 20

40 40
40 20 0 20 40 40 20 0 20 40

0.0 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1.0 0.0 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1.0

1.5
Intensidad

1.0

0.5

40 20 0 20 40
h (perfil)
Para este ejemplo se ha elegido que la distancia sea exactamente la mitad de la distancia de Fresnel
t1.circulo(r0 = (0 * um, 0 * um), radius = (radio, radio))
#distancia de fresnel
z_fresnel = radio ** 2 / lambda0

#plano de observacion

13.5. Difracción por una abertura circular 197


PIMCD2013-python, Publicación 1.0

z_difraccion = .5 * z_fresnel

puesto que a esta distancia aparece un anillo bien definido.


En el archivo difraccion_circulo_varias_distancias.py se muestra la difracción en campo cer-
cano a varias distancias. Nótese que en la función difraccion_varias_posiciones las variables filas y
columnas permiten reordenar las figuras (hasta 16) según su número.
#!/usr/local/bin/python
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#------------------------------------
# Autor: Luis Miguel Sanchez Brea
# Fecha 2013/10/24 (version 1.0)
# Licencia: GPL
#-------------------------------------

import sys
sys.path.append(’../’)
sys.path.append(’../general/’)

from camposXY import * #@UnusedWildImport


from fuentesXY import fuenteXY
from mascarasXY import mascaraXY

def difraccion_varias_posiciones(u2, distancias):

plt.figure(figsize=(12,8))

num_figuras = len(distancias)

filas = [1, 1, 1, 2, 2, 2, 3, 3, 3, 2, 4, 4, 4, 4, 5, 4]
columnas = [1, 2, 3, 2, 3, 3, 3, 3, 3, 5, 3, 3, 4, 4, 3, 4]

extension=[u2.x[0],u2.x[-1],u2.y[0],u2.y[-1]]
plt.subplot(filas[num_figuras], columnas[num_figuras], 1)
h1 = plt.imshow(abs(u2.u) ** 2, interpolation = ’bilinear’,
origin = ’lower’, extent = extension)
h1.set_cmap("gist_heat")

plt.title(u’circle’)

for z, i in zip(distancias, range(num_figuras)):


#difraccion a la distancia estipulada
u3 = u2.RS(z = z, newField = True)
texto = "z=%d $\mu m$" % (z)
#carga de los dibujos
plt.subplot(filas[num_figuras], columnas[num_figuras], i + 2)
plt.axis(’off’)
plt.title(texto)
h1 = plt.imshow(abs(u3.u) ** 2, interpolation = ’bilinear’,
origin = ’lower’, extent = extension)
h1.set_cmap("gist_heat")
plt.tight_layout()

def mascara_circulo():
tamano = 100 * um
ndatos = 256

198 Capítulo 13. Difracción en campo cercano


PIMCD2013-python, Publicación 1.0

x0 = sp.linspace(-tamano / 2, tamano / 2, ndatos)


y0 = sp.linspace(-tamano / 2, tamano / 2, ndatos)
lambda0 = 0.6238 * um

u1 = fuenteXY(x = x0, y = y0, wavelength = lambda0);


u1.onda_plana(A = 1, theta = 0 * grados, phi = 0 * grados)

t1 = mascaraXY(x = x0, y = y0, wavelength = lambda0)


t1.circulo(r0 = (0 * um, 0 * um), radius = (40*um, 40*um))

num_figuras = 3
z_fresnel = (tamano) ** 2 / lambda0
distancias_difraccion = sp.linspace(25 * um, .25 * z_fresnel, num_figuras)

u2 = u1 * t1
return u2

u2=mascara_circulo()
difraccion_varias_posiciones(u2, distancias = sp.linspace(25 * um, 1* mm, 3))
plt.show()

13.6 Difracción por un borde

En este epígrafe se considera como elemento difractor un borde. Así se considera como máscara una máscara
binaria, y se calcula la propagacióna una distancia de 25 um de la máscara. El proceder es análogo a una máscara
cuadrada.
#!/usr/local/bin/python
# -*- coding: utf-8 -*-
#------------------------------------
# Autor: Luis Miguel Sanchez Brea
# Fecha 2013/10/24 (version 1.0)
# Licencia: GPL
# Objetivo: Ejemplos de difraccion en campo lejano
#-------------------------------------

import sys
sys.path.append(’../’)

from camposXY import * #@UnusedWildImport


from fuentesXY import fuenteXY
from mascarasXY import mascaraXY

def difraccion_borde():
#tamano de area de visualizacion
tamano = 100 * um
ndatos = 256
x0 = sp.linspace(-tamano / 2, tamano / 2, ndatos)
y0 = sp.linspace(-tamano / 2, tamano / 2, ndatos)
#longitud de onda
lambda0 = 0.6238 * um

#fuente de iluminacion
u1 = fuenteXY(x = x0, y = y0, wavelength = lambda0);
u1.onda_plana(A = 1, theta = 0 * grados, phi = 0 * grados)

#mascara

13.6. Difracción por un borde 199


PIMCD2013-python, Publicación 1.0

t1 = mascaraXY(x = x0, y = y0, wavelength = lambda0)


t1.dosNiveles(nivel1 = 0, nivel2 = 1, xcorte = 0 * um)
u2 = u1 * t1

#plano de observacion
z_difraccion = 25*um

u3 = u2.RS(z = z_difraccion, newField = True)


texto = "z=%d $\mu m$" % (z_difraccion)
dibujar_varios_campos(campos=(u2,u3),titulos=(’circulo’, texto))
u3.dibujar_perfil(punto1=(-15, 0),punto2=(15,0), tipo=’intensidad’)
h,perfil,p1,p2=t1.perfil(punto1=(-15, 0),punto2=(15,0), tipo=’intensidad’,order=0)
plt.hold(True)
plt.plot(h,perfil,’r’,lw=2)

difraccion_borde()
plt.show()

circulo z=25 µm
40 40

20 20

0 0

20 20

40 40
40 20 0 20 40 40 20 0 20 40

0.0 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1.0 0.0 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1.0

200 Capítulo 13. Difracción en campo cercano


PIMCD2013-python, Publicación 1.0

1.4

1.2

1.0
Intensidad

0.8

0.6

0.4

0.2

15 10 5 0 5 10
h (perfil)
Debido a la difracción, la luz al propagarse se introduce en la sombra, como se observa en el perfil.
Veamos que ocurre a varias distancias. A medida que nos separamos del borde, más luz entra en la zona de sombra
y mas perceptibles son los efectos difractivos
#!/usr/local/bin/python
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#------------------------------------
# Autor: Luis Miguel Sanchez Brea
# Fecha 2013/10/24 (version 1.0)
# Licencia: GPL
#-------------------------------------

import sys
sys.path.append(’../’)
sys.path.append(’../general/’)

from camposXY import * #@UnusedWildImport


from fuentesXY import fuenteXY
from mascarasXY import mascaraXY

def difraccion_varias_posiciones(u2, distancias):

plt.figure(figsize=(12,6))

num_figuras = len(distancias)

filas = [1, 1, 1, 2, 2, 2, 3, 3, 3, 2, 4, 4, 4, 4, 5, 4]

13.6. Difracción por un borde 201


PIMCD2013-python, Publicación 1.0

columnas = [1, 2, 3, 2, 3, 3, 3, 3, 3, 5, 3, 3, 4, 4, 3, 4]

plt.subplot(filas[num_figuras], columnas[num_figuras], 1)
h1 = plt.imshow(abs(u2.u) ** 2)
h1.set_cmap("gist_heat")

plt.title(u’mascara’)

for z, i in zip(distancias, range(num_figuras)):


#difraccion a la distancia estipulada
u3 = u2.RS(z = z, newField = True)
texto = "z=%d $\mu m$" % (z)
#carga de los dibujos
plt.subplot(filas[num_figuras], columnas[num_figuras], i + 2)
plt.axis(’off’)
plt.title(texto)
h1 = plt.imshow(abs(u3.u) ** 2)
h1.set_cmap("gist_heat")

def mascara_borde():
tamano = 100 * um
ndatos = 256
x0 = sp.linspace(-tamano / 2, tamano / 2, ndatos)
y0 = sp.linspace(-tamano / 2, tamano / 2, ndatos)
lambda0 = 0.6238 * um

u1 = fuenteXY(x = x0, y = y0, wavelength = lambda0);


u1.onda_plana(A = 1, theta = 0 * grados, phi = 0 * grados)

t1 = mascaraXY(x = x0, y = y0, wavelength = lambda0)


t1.dosNiveles(nivel1 = 0, nivel2 = 1, xcorte = 0 * um)

num_figuras = 8
z_fresnel = (tamano) ** 2 / lambda0
distancias_difraccion = sp.linspace(25 * um, .25 * z_fresnel, num_figuras)

u2 = u1 * t1
return u2

u2=mascara_borde()
difraccion_varias_posiciones(u2, distancias = sp.linspace(10 * um, 100* um, 2))
plt.tight_layout()
plt.show()

13.7 Difracción por una doble rendija

En este caso el elemento difractor es una doble rendija. Se calcula la propagación a varias disntancias talesque la
distribución de intensidad no está en la posición de la zona clara, sino en la zona opaca, en contradicción con la
teoría geométrica (cosas que tienen los efectos difractivos), es decir entre las dos rendijas.
#!/usr/local/bin/python
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#------------------------------------
# Autor: Luis Miguel Sanchez Brea
# Fecha 2013/10/24 (version 1.0)
# Licencia: GPL

202 Capítulo 13. Difracción en campo cercano


PIMCD2013-python, Publicación 1.0

# Objetivo: difraccion por doble rendija


#-------------------------------------

import sys
sys.path.append(’../’)
sys.path.append(’../general/’)

from camposXY import * #@UnusedWildImport


from fuentesXY import fuenteXY
from mascarasXY import mascaraXY

def difraccionDobleRendija():
tamano = 250 * um
numdatos = 128
x0 = sp.linspace(-tamano / 2 , tamano / 2, numdatos)
y0 = sp.linspace(-tamano / 2 , tamano / 2, numdatos)
lambda0 = 0.6238 * um
z = 4 * mm

u1 = fuenteXY(x = x0, y = y0, wavelength = lambda0);


u1.onda_plana(A = 1, theta = 0 * grados, phi = 0 * grados)

t = mascaraXY(x = x0, y = y0, wavelength = lambda0);


t.dobleRendija(x0 = 0 * um, size = 25 * um, separacion = 50 * um, angulo = 0 * grados)

u2 = u1 * t
u3 = u2.RS(z=z, newField = True)

texto = "z=%d $\mu m$" % (z)


dibujar_varios_campos(campos=(u2,u3),titulos=(’doble rendija’, texto))

difraccionDobleRendija()
plt.show()

doble rendija z=4000 µm


100 100

50 50

0 0

50 50

100 100
100 50 0 50 100 100 50 0 50 100

0.0 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1.0 0.0 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1.0

Nótese como aunque la óptica geométrica predice la no existencia de luz en dicha región, experimentalmente (en
este caso simulación) se observa un patrón de intensidad en dicha región. Este muestra como la difracción es la
capacidad de la luz para sortear bordes y agujeros.

13.7. Difracción por una doble rendija 203


PIMCD2013-python, Publicación 1.0

13.8 Difracción por una lente

Otro ejemplo es la difracción por una lente, de la cual mostramos la intensidad y la fase. Debido a que la lente
se ha suspuesto rotacionalmente simétrica, el patrón de difracción es perfectamente simétrico. Así se muestra un
perfil de intensidad.
#!/usr/local/bin/python
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#------------------------------------
# Autor: Luis Miguel Sanchez Brea
# Fecha 2013/10/24 (version 1.0)
# Licencia: GPL
#-------------------------------------

import sys
sys.path.append(’../’)
sys.path.append(’../general/’)

from camposXY import * #@UnusedWildImport


from fuentesXY import fuenteXY
from mascarasXY import mascaraXY

def difraccion_lente():
tamano = 200 * um
numdatos = 256
x0 = sp.linspace(-tamano / 2 , tamano / 2, numdatos)
y0 = sp.linspace(-tamano / 2 , tamano / 2, numdatos)
lambda0 = 0.6238 * um

u1 = fuenteXY(x = x0, y = y0, wavelength = lambda0);


u1.onda_plana();

t1 = mascaraXY(x = x0, y = y0, wavelength = lambda0);


t1.circulo(r0 = (0 * um, 0 * um), radius = (90 * um, 90 * um), angulo = 45 * grados)

t2 = mascaraXY(x = x0, y = y0, wavelength = lambda0);


t2.lente(r0 = (0 * um, 0 * um), radius = (90 * um, 90 * um), focal = (2 * mm, 2 * mm), ang

t = t1 * t2
t.dibujar(tipo = ’campo’)

u3 = u1 * t
u3 = u3.RS(z = 2 * mm, newField = True)
u3.dibujar(tipo = ’intensidad’)

u3.dibujar_perfil(punto1=(-30*um, 0),punto2=(30*um,0), tipo=’intensidad’)

difraccion_lente()
plt.show()

204 Capítulo 13. Difracción en campo cercano


PIMCD2013-python, Publicación 1.0

100
amplitud 100
fase

50 50
y(µm)

y(µm)
0 0

50 50

100100 50 0 50 100 100100 50 0 50 100


x(µm) x(µm)
0.0 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1.0 2.4 1.6 0.8 0.0 0.8 1.6 2.4

100 400

350
50 300

250
y(µm)

0 200

150

50 100

50

100100 50 0 50 100 0
x(µm)

13.8. Difracción por una lente 205


PIMCD2013-python, Publicación 1.0

400

300
Intensidad

200

100

0 30 20 10 0 10 20
h (perfil)
13.9 Difracción por una lente binaria

En este último ejemplo se supone una lente de Fresnel. Este tipo de lentes es puramente difractiva. Así se obtiene
#!/usr/local/bin/python
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#------------------------------------
# Autor: Luis Miguel Sanchez Brea
# Fecha 2013/10/24 (version 1.0)
# Licencia: GPL
#-------------------------------------

import sys
sys.path.append(’../’)
from camposXY import * #@UnusedWildImport
from fuentesXY import fuenteXY
from mascarasXY import mascaraXY

def difraccion_lente_binaria():
tamano = 200 * um
numdatos = 256
x0 = sp.linspace(-tamano / 2 , tamano / 2, numdatos)
y0 = sp.linspace(-tamano / 2 , tamano / 2, numdatos)
lambda0 = 0.6238 * um

u1 = fuenteXY(x = x0, y = y0, wavelength = lambda0);


u1.onda_plana();

206 Capítulo 13. Difracción en campo cercano


PIMCD2013-python, Publicación 1.0

t1 = mascaraXY(x = x0, y = y0, wavelength = lambda0);


t1.circulo(r0 = (0 * um, 0 * um), radius = (90 * um, 90 * um), angulo = 45 * grados)

t2 = mascaraXY(x = x0, y = y0, wavelength = lambda0);


t2.lente(r0 = (0 * um, 0 * um), radius = (90 * um, 90 * um), focal = (2 * mm, 2 * mm), ang
t2.discretizar(tipo = ’fase’, numNiveles = 2, factor = 1, campoNuevo = False, matriz = Fal

t = t1 * t2
t.dibujar(tipo = ’campo’)
u3 = u1 * t
u3 = u3.RS(z = 2 * mm, newField = True)
u3.dibujar(tipo = ’intensidad’)
u3.dibujar_perfil(punto1=(-30*um, 0),punto2=(30*um,0), tipo=’intensidad’)

difraccion_lente_binaria()
plt.show()

100
amplitud 100
fase

50 50
y(µm)

y(µm)

0 0

50 50

100100 50 0 50 100 100100 50 0 50 100


x(µm) x(µm)
0.0 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1.0 0.0 0.4 0.8 1.2 1.6 2.0 2.4 2.8

13.9. Difracción por una lente binaria 207


PIMCD2013-python, Publicación 1.0

100
160

140
50
120

100
y(µm)

0 80

60
50 40

20

100100 50 0 50 100 0
x(µm)

150
Intensidad

100

50

0 30 20 10 0 10 20
h (perfil)

208 Capítulo 13. Difracción en campo cercano


CAPÍTULO 14

Difracción en campo lejano

Autor Luis Miguel Sánchez Brea


Revisor José Luis Vilas Prieto

Contenidos de este capítulo:


Introducción
Difracción por un cuadrado
Difracción por una rendija
Difracción por un círculo
Difracción por una doble rendija
Difracción por una estructura complicada: estrella
Difracción por una estructura complicada: fotografía
Difracción por una red de sinusoidal
Difracción por una red de Ronchi
Propiedades de la transformada de Fourier
• Desplazamiento del objeto
• Rotación del objeto
• Haz con distinto ángulo de entrada
• Misma máscara, distinto haz
• Máscara con distinto tamaño
• Distinta longitud de onda

Herramientas utilizadas en este tutorial:


numpy: manipulación de datos
scipy: Herramientas matemáticas
Archivos necesarios para esta sección:
camposXY.py. Archivo con las funciones principales camposXY.py
fuentesXY.py. Archivo con las funciones principales fuentesXY.py
mascarasXY.py. Archivo con las funciones principales mascarasXY.py
Descarga de archivos:
Ejemplo de difracción por un cuadrado difraccion_cuadrado.py
Ejemplo de difracción por un rectángulo difraccion_rectangulo.py
Ejemplo de difracción por una rendija difraccion_rendija.py
Ejemplo de difracción por un circulo difraccion_circulo.py
Ejemplo de difracción por una doble rendija difraccion_doble_rendija.py
Ejemplo de difracción por una estrella difraccion_estrella.py

209
PIMCD2013-python, Publicación 1.0

Ejemplo de difracción por una estructura compleja difraccion_lena.py


Ejemplo de difracción por una red sinusoidal difraccion_red_seno.py
Ejemplo de difracción por una red de Ronchi difraccion_red_ronchi.py
Ejemplo de desplazamiento difraccion_desplazamiento.py
Ejemplo de difracción con rotación difraccion_rotacion.py
Ejemplo de difracción con un haz inclinado difraccion_haz_inclinado.py
Ejemplo de difracción con dos haces diferentes difraccion_distinto_haz.py
Ejemplo de difracción al cambiar el tamaño difraccion_tamano.py
Ejemplo de difracción con distinta longitud de onda difraccion_longitud_onda.py

14.1 Introducción

En esta sección analizaremos la difracción en campo lejano bajo la aproximación de Fraunhofer.


El efecto de las interferencias surgen directamente de que las ecuaciones de Maxwell son lineales en los campos
eléctricos y magnéticos. Supongamos que E1 y E2 son soluciones. Entonces

𝜋𝑎2
𝑧≫
𝜆
donde a es el tamaño máximo del elemento difractor. Bajo esta aproximación, el campo difractado se puede
calcular como una integral
𝑥2 +𝑦 2
𝑒𝑖𝑘(𝑧+ 2𝑧 ) ∫︁ ∫︁
𝑘
E(𝑥, 𝑦, 𝑧) = E0 (𝜉, 𝜂)𝑒−𝑖 𝑧 (𝑥𝜉+𝑦𝜂) 𝑑𝜉𝑑𝜂.
𝑖𝜆𝑧

donde E0 (𝜉, 𝜂) es el campo justo a la salida del elemento difractor.


La aproximación más usual para este campo es considerar que se obtiene como el producto del campo incidente
E𝑖𝑛 (𝜉, 𝜂) por el coeficiente de transmisión del máscara 𝑡(𝜉, 𝜂)E𝑖𝑛 (𝜉, 𝜂)

E𝑜𝑢𝑡 (𝜉, 𝜂) = 𝑡(𝜉, 𝜂)E𝑖𝑛 (𝜉, 𝜂).

La gran ventaja de esta ecuación es que se puede escribir como una transformada de Fourier, que es una operación
bastante conocida en matemáticas
𝑥2 +𝑦 2
𝑒𝑖𝑘(𝑧+ 2𝑧 )
E(𝑥, 𝑦, 𝑧) = 𝑇 𝐹 [E0 (𝜉, 𝜂)] .
𝑖𝜆𝑧
Como el primer término delante de la transformada de Fourier es casi constante para un plano z determinado, se
suele eliminar.

14.2 Difracción por un cuadrado

Vamos a calcular mediante implementación de código de Python el campo difractado a partir de una máscara
cuadrada. Se observa el perfil con la típica figura de 𝑠𝑖𝑛𝑐2 (𝑥)
#!/usr/local/bin/python
# -*- coding: utf-8 -*-
#------------------------------------
# Autor: Luis Miguel Sanchez Brea
# Fecha 2013/10/24 (version 1.0)
# Licencia: GPL
# Objetivo: Ejemplos de difraccion en campo lejano

210 Capítulo 14. Difracción en campo lejano


PIMCD2013-python, Publicación 1.0

#-------------------------------------

#Anadimos las direcciones de las clases que se utilizan


import sys
sys.path.append(’../’)
#Se cargan las clases que se utilizan
from fuentesXY import *
from mascarasXY import mascaraXY, dibujar_varios_campos

#Datos de tamanos predeterminados


tamano = 250 * um
npuntos = 1024
x0 = sp.linspace(-tamano * um, tamano * um, npuntos)
y0 = sp.linspace(-tamano * um, tamano * um, npuntos)

#Longitud de onda predeterminada


lambda0 = 0.6238 * um

def difraccion_cuadrado():
#Se carga la clase fuentes
u1 = fuenteXY(x = x0, y = y0, wavelength = lambda0)
#Condicion de onda plana
u1.onda_plana(A = 1, theta = sp.pi / 2, phi = 0 * grados)
#Trabajaremos con la clase mascaras
t1 = mascaraXY(x = x0, y = y0, wavelength = lambda0)
#Mascara cuadrada
t1.cuadrado(r0 = (0 * um, 0 * um), size = (15 * um, 15 * um), angulo = 0 * grados)
#Campo por mascara
u2 = u1 * t1
#Transformada de Fourier
u3 = u2.fft(quitar0 = False)
#Representacion
dibujar_varios_campos(campos=(t1,u3), titulos=(’mascara’,’campo lejano’), logaritmico=True
plt.axis([u3.x.min()/3, u3.x.max()/3, u3.y.min()/3, u3.y.max()/3])
punto1=(u3.x.min(),0)
punto2=(u3.x.max(), 0)
u3.dibujar_perfil(punto1,punto2, normalizar=’maximo’)

difraccion_cuadrado()
plt.show()

mascara campo lejano


2000
200

1000
100

0 0

100
1000

200
2000
200 100 0 100 200 2000 1000 0 1000 2000

14.2. Difracción por un cuadrado 211


PIMCD2013-python, Publicación 1.0

1.0

0.8
Intensidad

0.6

0.4

0.2

0.0 6000 4000 2000 0 2000 4000 6000


h (perfil)
Tras haber ejecutado el código se obtiene la máscara y el patrón de intensidades, así mismo se muestra un perfil
de intensidad.

14.3 Difracción por una rendija

En este ejemplo calcular el campo difractado Por una rendija. Es muy parecido al cuadrado, pero en una de las
direcciones colapsa a una línea, pues la transformada de Fourier de un campo plano (en la dirección y) es una delta
de Dirac.
def difraccion_rendija():
#Clase fuentes
u1 = fuenteXY(x = x0, y = y0, wavelength = lambda0)
#Iluminacion de onda plana
u1.onda_plana(A = 1, theta = sp.pi / 2, phi = 0 * grados)
#Clase mascaras
t1 = mascaraXY(x = x0, y = y0, wavelength = lambda0)
#Rendija
t1.rendija(x0 = 0, size = 5 * um, angulo = 0 * grados)
#Campo transmitido
u2 = u1 * t1
#Campo difractado
u3 = u2.fft(quitar0 = False)
#Representacion
dibujar_varios_campos(campos=(u2, u3),titulos=(’mascara’, ’fft’),
titulo=’tamano’, logaritmico = True, normalizar = ’maximo’ )
plt.set_cmap("gray")
#perfiles
punto1=(u3.x.min()/2,0)
punto2=(u3.x.max()/2,0)

212 Capítulo 14. Difracción en campo lejano


PIMCD2013-python, Publicación 1.0

h, z_perfil, punto1, punto2=u3.dibujar_perfil(punto1,punto2, npixels=npuntos, \


normalizar = ’maximo’)

Tras ejecutar el código se obtiene la máscara y el patrón de intensidades, así mismo se muestra un perfil de
intensidad.

mascara tamano fft


100 10000

50 5000

0 0

50 5000

100 10000
100 50 0 50 100 10000 5000 0 5000 10000

0.0 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1.0 0.0 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1.0

1.0

0.8
Intensidad

0.6

0.4

0.2

6000 4000 2000 0 2000 4000 6000


h (perfil)

14.3. Difracción por una rendija 213


PIMCD2013-python, Publicación 1.0

14.4 Difracción por un círculo

Se calcula el campo difractado por una máscara circular. El resultado muestra el perfil con la típica figura de
𝐽02 (𝑥).
def difraccion_circulo():
#Clase fuentes
u1 = fuenteXY(x = x0, y = y0, wavelength = lambda0)
#Iluminacion onda plana
u1.onda_plana(A = 1, theta = sp.pi / 2, phi = 0 * grados)
#Clase mascaras
t1 = mascaraXY(x = x0, y = y0, wavelength = lambda0)
#Mascara
t1.circulo(r0 = (0 * um, 0 * um), radius = (15 * um, 15 * um), angulo = 0 * grados)
#Campo transmitido
u2 = u1 * t1
#Campo difractado
u3 = u2.fft(quitar0 = False)
#Representacion
punto1=(u3.x.min()/5,0)
punto2=(u3.x.max()/5,0)
dibujar_varios_campos(campos=(t1,u3), titulos=(’mascara’,’campo lejano’), logaritmico=True
plt.axis([u3.x.min()/3, u3.x.max()/3, u3.y.min()/3, u3.y.max()/3])
u3.dibujar_perfil(punto1,punto2, normalizar=’maximo’)

Tras ejecutar el código se obtiene la máscara y el patrón de intensidades, así mismo se muestra un perfil de
intensidad.

mascara 2000
campo lejano
200

100 1000

0 0

100 1000

200
2000
200 100 0 100 200 2000 1000 0 1000 2000

0.0 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1.0 0.0 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1.0

214 Capítulo 14. Difracción en campo lejano


PIMCD2013-python, Publicación 1.0

0.8
Intensidad

0.6

0.4

0.2

0.0 1000 500 0 500 1000


h (perfil)
14.5 Difracción por una doble rendija

Se muestra también la figura de difracción de una única rendija y se observa que es la envolvente de la doble
rendija: doble rendija = difracción de una rendija + interferencia entre dos rendijas.
def difraccion_doble_rendija():
#Clase fuentes
u1 = fuenteXY(x = x0, y = y0, wavelength = lambda0)
#Iluminacion onda plana
u1.onda_plana(A = 1, theta = sp.pi / 2, phi = 0 * grados)
#Doble rendija
t1 = mascaraXY(x = x0, y = y0, wavelength = lambda0)
t1.dobleRendija(x0 = 0, size = 5 * um, separacion = 20 * um, angulo = 0 * grados)
#Campo transmitido y difractado 1
u2 = u1 * t1
u3 = u2.fft(quitar0 = False)

#Rendija (para ver que es la convolucion de la doble rendija)


t3 = mascaraXY(x = x0, y = y0, wavelength = lambda0)
t3.rendija(x0 = 0, size = 5 * um, angulo = 0 * grados)
#Campo transmitido y difractado 2
u4 = u1* t3
u5 = u4.fft(quitar0 = False)

#Dibujar mascara y transformada


dibujar_varios_campos(campos=(u2,u3), titulos=(’mascara’,’fft’), \
logaritmico=True, normalizar=’maximo’)
#Perfiles
punto1=(u5.x.min(),0)

14.5. Difracción por una doble rendija 215


PIMCD2013-python, Publicación 1.0

punto2=(u5.x.max(),0)
h, z_perfil, punto1, punto2=u5.perfil(punto1,punto2)
u3.dibujar_perfil(punto1,punto2, normalizar = ’maximo’)
plt.plot(h,z_perfil/z_perfil.max(),’r’,lw=2)
plt.legend((’doble rendija’, ’una rendija’))
plt.xlim(h.min()/3,h.max()/3)

Tras ejecutar el código se obtiene la máscara y el patrón de intensidades, así mismo se muestra un perfil de
intensidad. Nótese como la envolvente del patrón de intensidades en la pantalla es la intensidad de una única
rendija.

100 mascara 15000


fft
10000
50
5000
0 0
5000
50
10000

100100 15000
50 0 50 100 1500010000 5000 0 5000 1000015000

0.0 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1.0 0.0 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1.0

216 Capítulo 14. Difracción en campo lejano


PIMCD2013-python, Publicación 1.0

doble rendija
una rendija
0.8
Intensidad

0.6

0.4

0.2

0.0 4000 2000 0 2000 4000


h (perfil)
14.6 Difracción por una estructura complicada: estrella

Las estructuras más complejas tienen una distribución en el plano de Fourier también más complejo. En este
ejemplo la máscara ha sido una estrella, esta se ha cargado a partir de una imagen binarizada, por tanto no ha sido
programada o tomada de la clase máscaraXY.
def difraccion_estrella():
u1 = fuenteXY(x = x0, y = y0, wavelength = lambda0)
u1.onda_plana(A = 1, theta = sp.pi / 2, phi = 0 * grados)
t1 = mascaraXY(x = x0, y = y0, wavelength = lambda0)
t1.imagen(nombre=’estrella_pequena.png’)

u2 = u1 * t1
u3 = u2.fft(quitar0 = False)

dibujar_varios_campos(campos=(u2,u3), titulos=(’mascara’,’fft’), \
logaritmico=True, normalizar=’maximo’)

Tras ejecutar el código se obtiene la máscara y el patrón de intensidades.

14.6. Difracción por una estructura complicada: estrella 217


PIMCD2013-python, Publicación 1.0

mascara 3000
fft
200
2000
100
1000
0 0
1000
100
2000
200
3000
200 100 0 100 200 3000 2000 1000 0 1000 2000 3000

0.0 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1.0 0.0 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1.0

14.7 Difracción por una estructura complicada: fotografía

Las estructuras más complejas tienen una distribución en el plano de Fourier también más complejo, este es el
caso de una fotografía real, en la cual debido a los múltiples detalles, la transformada de Fourier se muestra como
un patrón del que podría decirse que carece de orden, sin embargo conserva la información de la imagen original.
def difraccion_lena():
u1 = fuenteXY(x = x0, y = y0, wavelength = lambda0)
u1.onda_plana(A = 1, theta = sp.pi / 2, phi = 0 * grados)
t1 = mascaraXY(x = x0, y = y0, wavelength = lambda0)
t1.imagen(nombre=’lena.png’)

u2 = u1 * t1
u3 = u2.fft(quitar0 = False)

dibujar_varios_campos(campos=(u2,u3), titulos=(’mascara’,’fft’), \
logaritmico=True, normalizar=’maximo’)

Tras ejecutar el código se obtiene la máscara y el patrón de intensidades.

218 Capítulo 14. Difracción en campo lejano


PIMCD2013-python, Publicación 1.0

mascara 3000
fft
200
2000
100
1000
0 0
1000
100
2000
200
3000
200 100 0 100 200 3000 2000 1000 0 1000 2000 3000

0.0 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1.0 0.0 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1.0

14.8 Difracción por una red de sinusoidal

La red sinusoidal tiene solamente tres frecuencias espaciales, que son las que se observan en la figura de difracción.
def difraccion_red_seno():
#tamano de area de visualizacion
tamano = 1000 * um
ndatos = 512
x0 = sp.linspace(-tamano / 2, tamano / 2, ndatos)
y0 = sp.linspace(-tamano / 2, tamano / 2, ndatos)
#longitud de onda
lambda0 = 0.6238 * um

#fuente de iluminacion
u1 = fuenteXY(x = x0, y = y0, wavelength = lambda0);
u1.onda_plana(A = 1, theta = 0 * grados, phi = 0 * grados)

#mascara
t1 = mascaraXY(x = x0, y = y0, wavelength = lambda0)
t1.red_seno(periodo=40*um)
u2 = u1 * t1

u3 = u2.fft(quitar0 = False)

u2.dibujar()

punto1=(u3.x.min(),0)
punto2=(u3.x.max(),0)

u3.dibujar_perfil(punto1,punto2, normalizar=’maximo’, order=2)

Tras ejecutar el código se obtiene la máscara y el perfil de intensidades en la pantalla.

14.8. Difracción por una red de sinusoidal 219


PIMCD2013-python, Publicación 1.0

1.0
400 0.9
0.8
200 0.7
0.6
y(µm)

0 0.5
0.4
200 0.3
0.2
400 0.1

400 200 0 200 400 0.0


x(µm)

0.8

0.6
Intensidad

0.4

0.2

0.0

1500 1000 500 0 500 1000 1500


h (perfil)

220 Capítulo 14. Difracción en campo lejano


PIMCD2013-python, Publicación 1.0

14.9 Difracción por una red de Ronchi

La red de Ronchi tiene más frecuencias espaciales, todas equidistanciadas, que son las que se observan en la figura
2
de difracción. La altura de cada pico es el cuadrado del valor del coeficiente de Fourier 𝐼𝑝𝑖𝑐𝑜,𝑛 = |𝑎𝑛 | .
def difraccion_red_ronchi():
#tamano de area de visualizacio
tamano = 1000 * um
ndatos = 512
x0 = sp.linspace(-tamano / 2, tamano / 2, ndatos)
y0 = sp.linspace(-tamano / 2, tamano / 2, ndatos)
#longitud de onda
lambda0 = 0.6238 * um

#fuente de iluminacion
u1 = fuenteXY(x = x0, y = y0, wavelength = lambda0);
u1.onda_plana(A = 1, theta = 0 * grados, phi = 0 * grados)

#mascara
t1 = mascaraXY(x = x0, y = y0, wavelength = lambda0)
t1.red_ronchi( periodo = 40 * um, x0 = 0 * um, fillFactor = 0.5, angulo = 0 * grados)
u2 = u1 * t1

u3 = u2.fft(quitar0 = False)

u2.dibujar()

punto1=(u3.x.min(),0)
punto2=(u3.x.max(),0)

u3.dibujar_perfil(punto1,punto2, normalizar=’maximo’, order=2)

Tras ejecutar el código se obtiene la máscara y el perfil de intensidades en la pantalla.

14.9. Difracción por una red de Ronchi 221


PIMCD2013-python, Publicación 1.0

1.0
400 0.9
0.8
200 0.7
0.6
y(µm)

0 0.5
0.4
200 0.3
0.2
400 0.1

400 200 0 200 400 0.0


x(µm)

0.8

0.6
Intensidad

0.4

0.2

0.0

1500 1000 500 0 500 1000 1500


h (perfil)

222 Capítulo 14. Difracción en campo lejano


PIMCD2013-python, Publicación 1.0

14.10 Propiedades de la transformada de Fourier

En esta sección se analizan las propiedades matemáticas de la transformada de Fourier, a traves de la observación
de la misma en el plano de Fourier.

14.10.1 Desplazamiento del objeto

En este epígrafe mostramos como desplazar un elemento difractivo, la distribución de intensidad es la misma (no
la fase). Esto matemáticamente se corresponde con la propiedad:

𝑇 𝐹 [𝑓 (𝑡 − 𝑎)](𝜔) = 𝑒−𝑖𝜔𝑎 𝑇 𝐹 [𝑓 (𝑡)](𝜔)

Así queda patente al ejecutar el siguiente código, el cual considera la difracción por una máscara cuadrada. Las
figuras muestras la máscara y su desplazamiento así como la obsercación en el plano de Fourier de sendas másca-
ras.
def difraccion_desplazamiento():
u1 = fuenteXY(x = x0, y = y0, wavelength = lambda0)
u1.onda_plana(A = 1, theta = sp.pi / 2, phi = 0 * grados)
t1 = mascaraXY(x = x0, y = y0, wavelength = lambda0)
t1.cuadrado(r0 = (0 * um, 0 * um), size = (15 * um, 15 * um), angulo = 0 * grados)

t2 = mascaraXY(x = x0, y = y0, wavelength = lambda0)


t2.cuadrado(r0 = (100 * um, 0 * um), size = (15 * um, 15 * um), angulo = 0 * grados)

u2 = u1 * t1
u3 = u2.fft(quitar0 = False)

u4 = u1 * t2
u5 = u4.fft(quitar0=False)

dibujar_varios_campos(campos=(t1,t2), titulos=(’x=0 um’,’x=100*um’), titulo=’desplazamient


logaritmico=True, normalizar=’maximo’)

dibujar_varios_campos(campos=(u3,u5), titulos=(’x=0 um’,’x=100*um’), \


titulo=’desplazamiento’, logaritmico=True, normalizar=’max

x=0 um desplazamiento x=100*um


200 200

100 100

0 0

100 100

200 200
200 100 0 100 200 200 100 0 100 200

0.0 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1.0 0.0 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1.0

14.10. Propiedades de la transformada de Fourier 223


PIMCD2013-python, Publicación 1.0

6000
x=0 um desplazamiento
6000
x=100*um
4000 4000
2000 2000
0 0
2000 2000
4000 4000
6000 6000
6000 4000 2000 0 2000 4000 6000 6000 4000 2000 0 2000 4000 6000

0.0 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1.0 0.0 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1.0

14.10.2 Rotación del objeto

En este epígrafe se considera el mismo elemento difractivo del ejemplo anterior pero aplicándole una rotación, así
resulta una rotación de la transformada de Fourier.
def difraccion_rotacion():
u1 = fuenteXY(x = x0, y = y0, wavelength = lambda0)
u1.onda_plana(A = 1, theta = sp.pi / 2, phi = 0 * grados)
t1 = mascaraXY(x = x0, y = y0, wavelength = lambda0)
t1.cuadrado(r0 = (0 * um, 0 * um), size = (15 * um, 15 * um), angulo = 0 * grados)

t2 = mascaraXY(x = x0, y = y0, wavelength = lambda0)


t2.cuadrado(r0 = (0 * um, 0 * um), size = (15 * um, 15 * um), angulo = 45 * grados)

u2 = u1 * t1
u3 = u2.fft(quitar0 = False)

u4 = u1 * t2
u5 = u4.fft(quitar0=False)

dibujar_varios_campos(campos=(t1,t2), titulos=(’0 grados’,’45 grados’), \


titulo=’desplazamiento’, logaritmico=True,\
normalizar=’maximo’)

dibujar_varios_campos(campos=(u3,u5), titulos=(’0 grados’,’45 grados’), \


titulo=’rotacion’, logaritmico=True,\
normalizar=’maximo’)

224 Capítulo 14. Difracción en campo lejano


PIMCD2013-python, Publicación 1.0

0 grados desplazamiento 45 grados


200 200

100 100

0 0

100 100

200 200
200 100 0 100 200 200 100 0 100 200

0.0 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1.0 0.0 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1.0

0 grados rotacion 45 grados


6000 6000
4000 4000
2000 2000
0 0
2000 2000
4000 4000
6000 6000
6000 4000 2000 0 2000 4000 6000 6000 4000 2000 0 2000 4000 6000

0.0 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1.0 0.0 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1.0

14.10.3 Haz con distinto ángulo de entrada

Si el ángulo del incidencia es variado, la posición en el plano de Fourier del orden cero de difracción se ve
desplazada.
#!/usr/local/bin/python
# -*- coding: utf-8 -*-
#------------------------------------
# Autor: Luis Miguel Sanchez Brea
# Fecha 2013/10/24 (version 1.0)
# Licencia: GPL
# Objetivo: Ejemplos de difraccion en campo lejano
#-------------------------------------

#anadimos las direcciones de las clases que se utilizan


import sys

14.10. Propiedades de la transformada de Fourier 225


PIMCD2013-python, Publicación 1.0

sys.path.append(’../’)

#se cargan las clases que se utilizan


from fuentesXY import *
from mascarasXY import mascaraXY, dibujar_varios_campos

#datos de tamanos predeterminados


tamano = 250 * um
npuntos = 1024
x0 = sp.linspace(-tamano * um, tamano * um, npuntos)
y0 = sp.linspace(-tamano * um, tamano * um, npuntos)

#longitud de onda predeterminada


lambda0 = 0.6238 * um

def difraccion_haz_inclinado(haz_incidente):
u1=haz_incidente
t1 = mascaraXY(x = x0, y = y0, wavelength = lambda0)
t1.cuadrado(r0 = (0 * um, 0 * um), size = (15 * um, 15 * um), angulo = 0 * grados)
u2 = u1 * t1
u3 = u2.fft(quitar0 = False)

return u2, u3

u1 = fuenteXY(x = x0, y = y0, wavelength = lambda0)


u1.onda_plana(A = 1, theta = sp.pi / 2, phi = 0 * grados)
t1, fft1= difraccion_haz_inclinado(u1)

u2 = fuenteXY(x = x0, y = y0, wavelength = lambda0)


u2.onda_plana(A = 1, theta = sp.pi / 2, phi = 15 * grados)
t2, fft2= difraccion_haz_inclinado(u2)

dibujar_varios_campos(campos=(fft1,fft2), titulos=(’0 grados’,’15 grados’), \


titulo=’fft’, logaritmico=True, normalizar=’maximo’)

plt.show()

0 grados fft 15 grados


6000 6000
4000 4000
2000 2000
0 0
2000 2000
4000 4000
6000 6000
6000 4000 2000 0 2000 4000 6000 6000 4000 2000 0 2000 4000 6000

0.0 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1.0 0.0 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1.0

226 Capítulo 14. Difracción en campo lejano


PIMCD2013-python, Publicación 1.0

14.10.4 Misma máscara, distinto haz

Se compara la difracción por un máscara cuadrada bajo dos fuentes de iluminación, un haz de entrada en primer
lugar considerado una onda plana y en segudno, una onda gaussiana con un tamaño comparable al elemento
difractor. El resultado, muestra como en el caso del haz gausiano, la mayor parte de la energía del haz difractado
se concentra en los órdenes más bajo de difracción. Mientras que en el caso de una onda plana, la energía está más
repartida entre los distintos órdenes.
#!/usr/local/bin/python
# -*- coding: utf-8 -*-

#----------------------------------------------------------------------
#!/usr/local/bin/python
# -*- coding: utf-8 -*-
#------------------------------------
# Autor: Luis Miguel Sanchez Breaje
# Fecha 2013/10/24 (version 1.0)
# Licencia: GPL
# Objetivo: Ejemplos de difraccion en campo lejano
#-------------------------------------

#anadimos las direcciones de las clases que se utilizan


import sys
sys.path.append(’../’)

#se cargan las clases que se utilizan


from fuentesXY import *
from mascarasXY import mascaraXY, dibujar_varios_campos

#datos de tamanos predeterminados


tamano = 250 * um
npuntos = 1024
x0 = sp.linspace(-tamano * um, tamano * um, npuntos)
y0 = sp.linspace(-tamano * um, tamano * um, npuntos)

#longitud de onda predeterminada


lambda0 = 0.6238 * um

def difraccion_rectangulo_onda_plana(haz_incidente):

u1=haz_incidente

t1 = mascaraXY(x = x0, y = y0, wavelength = lambda0)


t1.cuadrado(r0 = (0 * um, 0 * um), size = (15 * um, 15 * um), angulo = 0 * grados)

u2 = u1 * t1
u2.dibujar(tipo = ’intensidad’, logaritmico = False, normalizar = ’maximo’)
plt.axis([-15*um,15*um,-15*um,15*um])

u3 = u2.fft(quitar0 = False)

return u2, u3

u1 = fuenteXY(x = x0, y = y0, wavelength = lambda0)


u1.onda_plana(A = 1, theta = sp.pi / 2, phi = 0 * grados)
t1, fft1= difraccion_rectangulo_onda_plana(u1)

u2 = fuenteXY(x = x0, y = y0, wavelength = lambda0)


u2.haz_gauss(A = 1, r0 = (0 * um, 0 * um), w = (5 * um, 5 * um), \
theta = 0.*grados, phi = 0 * grados)
t2, fft2= difraccion_rectangulo_onda_plana(u2)

14.10. Propiedades de la transformada de Fourier 227


PIMCD2013-python, Publicación 1.0

dibujar_varios_campos(campos=(fft1,fft2), titulos=(’plana’,’gauss’), titulo=’fft’, logaritmico=Tru

plt.show()

15 1.0
0.9
10
0.8
0.7
5
0.6
y(µm)

0 0.5
0.4
5
0.3
0.2
10
0.1
1515 10 5 0 5 10 15 0.0
x(µm)

228 Capítulo 14. Difracción en campo lejano


PIMCD2013-python, Publicación 1.0

15 1.0
0.9
10
0.8
0.7
5
0.6
y(µm)

0 0.5
0.4
5
0.3
0.2
10
0.1
1515 10 5 0 5 10 15 0.0
x(µm)
plana fft gauss
6000 6000
4000 4000
2000 2000
0 0
2000 2000
4000 4000
6000 6000
6000 4000 2000 0 2000 4000 6000 6000 4000 2000 0 2000 4000 6000

0.0 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1.0 0.0 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1.0

14.10.5 Máscara con distinto tamaño

Considérense dos máscaras con la misma topología pero de distinto tamaño. La observación del haz difractado en
el plano de Fourier cambia de escala, esto es mantiene la misma topología pero no su tamaño.
#!/usr/local/bin/python
# -*- coding: utf-8 -*-
#------------------------------------

14.10. Propiedades de la transformada de Fourier 229


PIMCD2013-python, Publicación 1.0

# Autor: Luis Miguel Sanchez Breaje


# Fecha 2013/10/24 (version 1.0)
# Licencia: GPL
# Objetivo: Ejemplos de difraccion en campo lejano
#-------------------------------------

#anadimos las direcciones de las clases que se utilizan


import sys
sys.path.append(’../’)

#se cargan las clases que se utilizan


from fuentesXY import *
from mascarasXY import mascaraXY, dibujar_varios_campos

#datos de tamanos predeterminados


tamano = 250 * um
npuntos = 1024
x0 = sp.linspace(-tamano * um, tamano * um, npuntos)
y0 = sp.linspace(-tamano * um, tamano * um, npuntos)

#longitud de onda predeterminada


lambda0 = 0.6238 * um

def difraccion_tamano(anchura):
u1 = fuenteXY(x = x0, y = y0, wavelength = lambda0)
u1.onda_plana(A = 1, theta = sp.pi / 2, phi = 0 * grados)
t1 = mascaraXY(x = x0, y = y0, wavelength = lambda0)
t1.cuadrado(r0 = (0 * um, 0 * um), size = (anchura,anchura), angulo = 0 * grados)

u2 = u1 * t1
u3 = u2.fft(quitar0 = False)

return t1, u3

fft_pequeno, t1=difraccion_tamano(5*um)
fft_grande, t2 =difraccion_tamano(15*um)

dibujar_varios_campos(campos=(t1,t2), titulos=(r’$5\,\mu m$’, r’$15\,\mu m$’), \


titulo=’desplazamiento’, logaritmico=True, normalizar=’maximo’)

dibujar_varios_campos(campos=(fft_pequeno,fft_grande),titulos=(r’$5\,\mu m$’, r’$15\,\mu m$’),


titulo=’tamano’, logaritmico = True, normalizar = ’maximo’ )

plt.show()

230 Capítulo 14. Difracción en campo lejano


PIMCD2013-python, Publicación 1.0

5 µm desplazamiento 15 µm
6000 6000
4000 4000
2000 2000
0 0
2000 2000
4000 4000
6000 6000
6000 4000 2000 0 2000 4000 6000 6000 4000 2000 0 2000 4000 6000

0.0 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1.0 0.0 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1.0

5 µm tamano 15 µm
200 200

100 100

0 0

100 100

200 200
200 100 0 100 200 200 100 0 100 200

0.0 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1.0 0.0 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1.0

14.10.6 Distinta longitud de onda

Si se considera una misma máscara pero distintas longitudes de onda de luz incidente en el plano de Fourier se
observa que la distribución de intensidad es la misma, pero no su tamaño. Este hecho queda patente en las figuras
ejemplo fruto de ejecutar el código de abajo. Si tuvieran distinta escala se verían diferente.
#!/usr/local/bin/python
# -*- coding: utf-8 -*-
#------------------------------------
# Autor: Luis Miguel Sanchez Breaje
# Fecha 2013/10/24 (version 1.0)
# Licencia: GPL
# Objetivo: Ejemplos de difraccion en campo lejano
#-------------------------------------

#anadimos las direcciones de las clases que se utilizan

14.10. Propiedades de la transformada de Fourier 231


PIMCD2013-python, Publicación 1.0

import sys
sys.path.append(’../’)

#se cargan las clases que se utilizan


from fuentesXY import *
from mascarasXY import mascaraXY, dibujar_varios_campos

#datos de tamanos predeterminados


tamano = 250 * um
npuntos = 1024
x0 = sp.linspace(-tamano * um, tamano * um, npuntos)
y0 = sp.linspace(-tamano * um, tamano * um, npuntos)

def difraccion_cuadrado(longitud_onda):
u1 = fuenteXY(x = x0, y = y0, wavelength = longitud_onda)
u1.onda_plana(A = 1, theta = sp.pi / 2, phi = 0 * grados)
t1 = mascaraXY(x = x0, y = y0, wavelength = longitud_onda)
t1.cuadrado(r0 = (0 * um, 0 * um), size = (15 * um, 15 * um), angulo = 0 * grados)

u2 = u1 * t1
u3 = u2.fft(quitar0 = False)

return u3

s1=difraccion_cuadrado(0.405*um)
s2=difraccion_cuadrado(0.6328*um)
dibujar_varios_campos(campos=(s1,s2), titulos=(r’$0.405\,\mu m$’, r’$0.6238\,\mu m$’), \
logaritmico=True, normalizar=’maximo’)

plt.show()

4000
0.405 µm 0.6238 µm
6000
3000
4000
2000
1000 2000
0 0
1000 2000
2000
4000
3000
4000 6000
4000300020001000 0 1000200030004000 6000 4000 2000 0 2000 4000 6000

0.0 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1.0 0.0 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1.0

232 Capítulo 14. Difracción en campo lejano


CAPÍTULO 15

Redes de Difracción

Autor Luis Miguel Sánchez Brea


Revisor José Luis Vilas Prieto
Las redes de difracción son estructuras periódicas que modulan la transmitancia de un haz incidente. Son de gran
importancia en áreas como espectroscopía, para separar el espectro, o en metrología óptica, donde se utilizan como
escalas.

Contenidos de este capítulo:


Introducción
Red Sinusoidal
Red de Ronchi
Red de Binaria
Red Blazed
Red bidimensional
Red tipo Ajedrez
Red radial
Red angular
Red con borde sinusoidal
Red hiperbólica
Aplicación como redes de difracción en campo lejano
• Difracción por una red de sinusoidal
• Difracción por una red de Ronchi
Efecto Talbot
Efectos con dos redes

Herramientas utilizadas en este tutorial:


numpy: manipulación de datos
scipy: Herramientas matemáticas
Archivos necesarios para la definición de redes de difracción:
Archivo con las funciones principales mascarasXY.py
Ejemplo de una red sinusoidal ejemplo_red_seno.py
Ejemplo de una red de Ronchi ejemplo_red_ronchi.py
Ejemplo de una red blazed ejemplo_red_blazed.py
Ejemplo de una red binaria ejemplo_red_binaria.py
Ejemplo de una red tipo damero ejemplo_red_ajedrez.py
Ejemplo de una red bidimensional ejemplo_red_2D.py
Ejemplo de una red radial ejemplo_red_2D.py

233
PIMCD2013-python, Publicación 1.0

Ejemplo de una red angular ejemplo_red_2D.py


Ejemplo de una red con borde sinusidal ejemplo_red_borde_sinusoidal.py
Ejemplo numérico del efecto Talbot efecto_talbot_numerico.py
Efecto Talbot animado efecto_talbot_animado.py
Efecto Talbot efecto_talbot.py
Efecto Talbot efecto_talbot_haz_gauss.py
Efecto Talbot para red radial efecto_talbot_red_radial.py
Efecto Vernier por variación de ángulo efecto_vernier_angulo.py
Efecto Vernier por variación de ángulo animado efecto_vernier_angulo_animado.py
Efecto Vernier por variación de periodo efecto_vernier_periodo.py
Efecto Vernier por variación de periodo animado efecto_vernier_periodo_animado.py
Desplazamiento Talbot desplazamiento_talbot_animado.py
Desplazamiento Vernier por variación de ángulo desplazamiento_vernier_angulo_animado.py
Desplazamiento Vernier por variación de periodo desplazamiento_vernier_periodo_animado.py
Contenidos de los archivos:
mascarasXY.py: (parte de redes)
• red_seno(). Definición de una red sinusoidal.
• red_blazed(). Red tipo diente de sierra.
• red_ronchi(). Implementación de una red de Ronchi.
• red_binaria(). Genera una red de franjas.
• red2D(). Define una red bidimensional de franjas horizontales y verticales.
• red2D_ajedrez(). Genera una red con formato tipo damero.
• ejemplo_red_seno.py. Ejemplo de una red sinusoidal.
• ejemplo_red_ronchi.py. Ejemplo de una red de Ronchi.
• ejemplo_red_blazed.py. Ejemplo de una red Blazed.
• ejemplo_red_binaria.py. Ejemplo de una red binaria.
• ejemplo_red_ajedrez.py. Ejemplo de una red tipo damero.
• ejemplo_red_2D.py. Ejemplo de una red bidimensional.
• ejemplo_talbot_numerico.py. Ejemplo numérico del efecto Talbot.

15.1 Introducción

Podemos definir una red de difracción como un elemento óptico que modula de forma periódica alguna propiedad
de la luz. Normalmente existen redes de amplitud y de fase, aunque se puede modular de forma periódica cualquier
otra propiedad de la luz, tal como la polarización o la coherencia. Como la modulación se produce de forma
periódica, el análisis de cómo se comporta la red se puede realizar mediante un análisis de Fourier, de forma que
la transmitancia se puede describir matemáticamente mediante
∞ (︂ )︂
∑︁ 2𝜋𝑖𝑙𝑥
𝑡(𝑥) = 𝑎𝑙 exp .
𝑝
𝑙=−∞

234 Capítulo 15. Redes de Difracción


PIMCD2013-python, Publicación 1.0

donde 𝑝 es el periodo y 𝑎𝑙 son los coeficientes de Fourier que se obtienen mediante


∫︁ 𝑝/2 (︂ )︂
1 𝑥
𝑎𝑙 = 𝑡(𝑥) exp −2𝜋𝑖𝑙 𝑑𝑥.
𝑝 −𝑝/2 𝑝

15.2 Red Sinusoidal

Una red sinusoidal es un conjunto de franjas equiespaciadas cuya intensidad puede ser ajustada a una función
sinusoidal. La orientación de las franjas puede ser arbitraria. En la función que nos atañe su ángulo de inclinación
aparece en el argumento como angulo. Así se llama a la función __rotar__ dentro de la clase a fin de rotar
las franjas. Finalmente se implementa la intensidad de las franjas, entre una intensidad mínima amp_min y una
máxima amp_max. La modulación de la intensidad ha sido definida como
[︂ (︂ )︂]︂
𝐴𝑚𝑎𝑥 − 𝐴𝑚𝑖𝑛 2𝜋(𝑥 + 𝛿)
𝑡(𝑥) = 𝐴𝑚𝑖𝑛 + 1 + cos
2 𝑝

donde p define el periodo de las franjas.


def red_seno(self, periodo = 40 * um, amp_min = 0, amp_max = 1, desfase = 0 * um, angulo =

#Se define la inclinacion de la red


Xrot, Yrot = self.__rotar__(angulo)

#Definicion de la sinusoidal
self.u = amp_min + (amp_max - amp_min) * (1 + sp.cos(2 * sp.pi * (Xrot + desfase)

La representación de las franjas resulta ser


#!/usr/bin/env python
# -*- coding: utf-8 -*-
#------------------------------------
# Autor: Luis Miguel Sanchez Brea
# Fecha 2013/10/24 (version 1.0)
# Licencia: GPL
#-------------------------------------
"""
ejemplos redes
"""

import sys
sys.path.append(’../’)
from mascarasXY import * #@UnusedWildImport

def ejemplo_redSeno():
x = sp.linspace(-250 * um, 250 * um, 512)
y = sp.linspace(-250 * um, 250 * um, 512)
wavelength = 0.6238 * um
periodo = 75 * um
t = mascaraXY(x, y, wavelength)
t.red_seno(periodo= periodo, amp_min=0, amp_max=1,\
desfase=periodo / 2, angulo=0 * grados)
t.dibujar(tipo=’intensidad’, titulo=’red seno’)

if __name__ == ’__main__’:
ejemplo_redSeno()
plt.show()

15.2. Red Sinusoidal 235


PIMCD2013-python, Publicación 1.0

red seno
1.0
200 0.9
0.8
100 0.7
0.6
y(µm)

0 0.5
0.4
100 0.3
0.2
200 0.1

200 100 0 100 200 0.0


x(µm)
Los coeficientes de Fourier para una red sinusoidal
𝑥 1 𝑥 1 𝑥
𝑡(𝑥) = 1 + cos(2𝜋 ) = 1 + 𝑒2𝜋𝑖 𝑝 + 𝑒−2𝜋𝑖 𝑝
𝑝 2 2
son

𝑎−1 = 1/2, 𝑎0 = 1, 𝑎1 = 1/2.

15.3 Red de Ronchi

Una red de Ronchi es un conjunto de franjas equiespaciadas tales que los máximos y los mínimos de intensidad
poseen la misma anchura. Para la implementación de una red de Ronchi se hará uso de la clase mascaraXY
así como de la función rendija ya vista anteriormente en la sección máscaras. Así en primer lugar tras llamar a la
clase y generar nuestro vector salida, t con ceros a fin de optimizar nuestra función se calcula el número de franjas,
numperiodos a partir de redondear al entero más próximo el cociente
𝑥𝑚𝑎𝑥 − 𝑥𝑚𝑖𝑛
𝑝
donde p es el periodo, x nuestro vector de entrada y xmax y xmin, los máximos y mínimos de x. Finalmente
sumamos 5 con la intención de tener un número de periodos suficientes y no quedarnos cortos debido al redondeo.
Con el fin de definir la anchura de la rendija se multiplica el factor de relleno por el periodo. Para definir las franjas
se llama para cada línea a la función rendija, así es preciso definir un punto de inicio para la implementación de
las franjas, xinicial siendo este el valor mínimo del vector de entrada menos el valor el puto central, x0.
Finalmente, mediante un bucle for se definen las franjas con inclinación angulo. Esto se hace superponiendo
nuestro vector de ceros, t, cuyos valores son los mínimos con los máximos que dan lugar del uso de la función
rendija.

236 Capítulo 15. Redes de Difracción


PIMCD2013-python, Publicación 1.0

def red_ronchi(self, periodo = 40 * um, x0 = 20 * um, fillFactor = 0.5, angulo = 0 * grado


t = t2 = mascaraXY(self.x, self.y, self.wavelength);
t.u = sp.zeros(self.X.shape)

#Numero de periodos
numperiodos = int(round((self.x.max() - self.x.min()) / periodo + 5))
#Anchura de la rendija
anchuraRendija = fillFactor * periodo;

#Se define un origen


xinicial = self.x.min() - x0

#Generacion de la red
for i in range(-3, numperiodos + 1):
t2.rendija(xinicial + i * periodo, anchuraRendija, angulo)
t = t + t2

self.u = t.u

La representación de las franjas es (solo mostramos la función, pues el resto es igual al ejemplo anterior)
def ejemplo_redRonchi():
x = sp.linspace(-250 * um, 250 * um, 512)
y = sp.linspace(-250 * um, 250 * um, 512)
wavelength = 0.6238 * um
periodo = 40 * um
t = mascaraXY(x, y, wavelength)
t.red_ronchi(periodo=periodo, x0=20 * um, fillFactor=0.5, angulo=0 * grados)
t.dibujar(tipo=’intensidad’, titulo=’red Ronchi’)

red Ronchi
1.0
200 0.9
0.8
100 0.7
0.6
y(µm)

0 0.5
0.4
100 0.3
0.2
200 0.1

200 100 0 100 200 0.0


x(µm)

15.3. Red de Ronchi 237


PIMCD2013-python, Publicación 1.0

15.4 Red de Binaria

Una red binaria está constituida por un conjunto de franjas equiespaciadas entre sí una distancia determinada, en
este caso, a través de los parámetros de entrada podemos controlar la amplitud mínima (amin), máxima (max), el
desfase (desfase) y el factor de forma (fillFactor), además del ángulo de rotación (angulo)
def red_binaria(self, periodo = 40 * um, amin = 0, amax = 1, desfase = 0 * sp.pi / 2, x0 = 0, fill

def red_binaria(self, periodo = 40 * um, amin = 0, amax = 1, desfase = 0 * sp.pi / 2, x0 =


t = mascaraXY(self.x, self.y, self.wavelength);
t2 = mascaraXY(self.x, self.y, self.wavelength);
t.u = sp.zeros(self.X.shape)

#Numero de periodos
numperiodos = int(round((self.x.max() - self.x.min()) / periodo + 5))
#Anchura de la rendija
anchuraRendija = fillFactor * periodo;
#Se define un origen
xinicial = self.x.min() - x0

#Generacion de la red
for i in range(-3, numperiodos):
t2.rendija(xinicial + i * periodo + anchuraRendija, anchuraRendija, angulo
t.u = t.u + amax * t2.u * sp.exp(1.j * desfase)
t2.rendija(xinicial + i * periodo, periodo - anchuraRendija, angulo)
t.u = t.u + amin * t2.u

self.u = t.u

La representación queda:
def ejemplo_redBinaria():
x = sp.linspace(-250 * um, 250 * um, 512)
y = sp.linspace(-250 * um, 250 * um, 512)
wavelength = 0.6238 * um
t = mascaraXY(x, y, wavelength)
t.red_binaria( periodo = 40 * um, amin=.5, amax=1, desfase=1 * sp.pi / 2,\
x0=0, fillFactor=0.5)
t.dibujar(tipo=’campo’, titulo=’red Binaria’)

amplitud red Binaria fase


200 200

100 100
y(µm)

y(µm)

0 0

100 100

200 200
200 100 0 100 200 200 100 0 100 200
x(µm) x(µm)
0.500.550.600.650.700.750.800.850.900.951.00 0.0 0.2 0.4 0.6 0.8 1.0 1.2 1.4

238 Capítulo 15. Redes de Difracción


PIMCD2013-python, Publicación 1.0

Los coeficientes de Fourier de una red binaria (que incluye a la red de Ronchi como caso especial son

𝑎0 = 𝛼 𝑒−𝑖𝛿 − 1 + 1
(︀ )︀

𝑎𝑙 = 𝛼 𝑒−𝑖𝛿 − 1 𝑠𝑖𝑛𝑐(𝜋𝑙𝛼)
(︀ )︀

2𝜋 4𝜋
donde el desfase resulta ser 𝛿 = 𝜆 (𝑛 − 1)ℎ para el caso de transmisión y 𝛿 = 𝜆 (𝑛 − 1)ℎ para el caso de
reflexión.

15.5 Red Blazed

Normalmente en una red de difracción convencional la luz difractada se concentra en su mayor parte en el orden
cero. Una red Blazed es un tipo particular de red de difracción que permite homogeneizar la luz difractada en los
distintos órdenes Esto se logra mediante una red con forma de dientes de sierra. Las redes de difracción trabajan
bajo determinadas condiciones de iluminación como es la longitud de onda del haz y el ángulo de incidente, que
en general se supone normal a la red. Así para implementar una red Blazed se comienza definiendo el vector de
onda. La definición de los dientes de sierra de la red se hace mediante el periodo de la red y la altura del diente, el
cociente de estas dos cantidades representa la pendiente de cada diente. La altura en cada punto de la red será por
tanto, la pendiente de los dientes por la posición x. La fase viene dado por el camino óptico como se muestra en k
* (indice - 1) * h la cual debe estar normalizada entre 0 y 2𝜋, esto se logra con remainder. Dado que
es relativa se fija un origen de referencia, el cual está especificado por el mínimo. Finalmente se devuelve la fase
def red_blazed(self, periodo = 40 * um, altura = 2 * um, indice = 1.5, angulo = 0 * grados
#Vector de onda
k = 2 * sp.pi / self.wavelength
#Inclinacion de las franjas
Xrot, Yrot = self.__rotar__(angulo)

#Calculo de la pendiente
pendiente = altura / periodo
#Calculo de la altura
h = Xrot * pendiente

#Calculo del a fase


fase = k * (indice - 1) * h
#Definicion del origen
fase = fase - fase.min()
#Normalizacion entre 0 y 2pi
fase = np.remainder(fase, 2 * sp.pi)
self.u = sp.exp(1j * fase)

Si llamamos a esta función se obtiene


def ejemplo_redBlazed():
x = sp.linspace(-250 * um, 250 * um, 512)
y = sp.linspace(-250 * um, 250 * um, 512)
wavelength = 0.6238 * um
periodo = 100 * um
t = mascaraXY(x, y, wavelength)
t.red_blazed(periodo= periodo, altura=2 * um, indice=1.5, angulo=0 * grados)
t.dibujar(tipo=’fase’, titulo=’red blazed’)

15.5. Red Blazed 239


PIMCD2013-python, Publicación 1.0

red blazed
200 2.4

1.6
100
0.8
y(µm)

0 0.0

0.8
100
1.6

200 2.4

200 100 0 100 200


x(µm)

15.6 Red bidimensional

Una red bidimensional es aquella constituida por un conjunto de franjas verticales y horizontales, tales que per-
mite la transmisión de luz únicamente en dichas franjas. La implementación es sencilla basta con definir los dos
conjuntos de franjas horizontal y vertical y superponerlas. La superposición es equivalente a multiplicar las franjas
puesto que cada una es un matriz de ceros (no hay transmisión) y unos (transmisión permitida.
def red2D(self, periodo = 40.*um, amin = 0, amax = 1., desfase = 0.*sp.pi / 2, x0 = 0, fil
#Se inicializa
t1 = mascaraXY(self.x, self.y, self.wavelength);
t2 = mascaraXY(self.x, self.y, self.wavelength);
#Red horizontal
t1.red_binaria(periodo, amin, amax, desfase, x0, fillFactor, angulo)
#Red vertical
t2.red_binaria(periodo, amin, amax, desfase, x0, fillFactor, angulo + 90.*grados)
#Red binaria
self.u = t1.u * t2.u

La representación de la red bidimensional queda


def ejemplo_red2D():
x = sp.linspace(-250 * um, 250 * um, 512)
y = sp.linspace(-250 * um, 250 * um, 512)
wavelength = 0.6238 * um
periodo = 50 * um
t = mascaraXY(x, y, wavelength)
t.red2D(periodo=periodo, amin=0, amax=1., desfase=0 * sp.pi / 2,\
x0=0, fillFactor=0.5, angulo=0 * grados)
t.dibujar(tipo=’intensidad’, titulo=’red 2D’)

240 Capítulo 15. Redes de Difracción


PIMCD2013-python, Publicación 1.0

red 2D
1.0
200 0.9
0.8
100 0.7
0.6
y(µm)

0 0.5
0.4
100 0.3
0.2
200 0.1

200 100 0 100 200 0.0


x(µm)

15.7 Red tipo Ajedrez

Como su propio nombre indica una red tipo ajedrez genera un damero con regiones de transmitancia unidad y
nula. Debido a como se ha definido la función, se pueden implementar distintos tipos de dameros con regiones de
transmitancia mayores y menores como se muestra en el ejemplo. La implementación del a función es completa-
mente análoga a la de la red bidimensional cambiando únicamente la última línea del código, en la cual en vez de
realizar el producto de las dos matrices, se realiza una función xor entre ellas. Esta función toda dos argumentos
binarios, y su resultado únicamente nulo cuando los dos argumentos adquieren simultáneamente el mismo valor
(0,0), o (1,1), de lo contrario el resultado es la unidad.
def red2D_ajedrez(self, periodo = 40 * um, amin = 0, amax = 1, desfase = 0 * sp.pi / 2,\
x0 = 0, fillFactor = 0.75, angulo = 0 * grados):
#Mismo proceder que en 2D
t1 = mascaraXY(self.x, self.y, self.wavelength);
t2 = mascaraXY(self.x, self.y, self.wavelength);
t1.red_binaria(periodo, amin, amax, desfase, x0, fillFactor, angulo)
t2.red_binaria(periodo, amin, amax, desfase, x0, fillFactor, angulo + 90.*grados)
#Actuacion de la XOR
self.u = sp.logical_xor(t1.u, t2.u)

Así resulta la representación


def ejemplo_redAjedrez1():
x = sp.linspace(-250 * um, 250 * um, 1024)
y = sp.linspace(-250 * um, 250 * um, 1024)
wavelength = 0.6238 * um
periodo = 50 * um
t = mascaraXY(x, y, wavelength)
t.red2D_ajedrez(periodo=periodo, amin=0, amax=1., desfase=0 * sp.pi / 2, x0=0,\

15.7. Red tipo Ajedrez 241


PIMCD2013-python, Publicación 1.0

fillFactor=0.5, angulo=0 * grados)


return t

red 1 red 2
200 200

100 100

0 0

100 100

200 200
200 100 0 100 200 200 100 0 100 200

0.0 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1.0 0.0 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1.0

Para definir el segundo dibujo los parámetros resultan


t.red2D_ajedrez(periodo=periodo, amin=0, amax=1., desfase=0 * sp.pi / 2, x0=0, fillFactor=0
angulo=45 * grados)

15.8 Red radial

Las redes de difracción también pueden tener simetría radial. Son parecidos a los axicones, pero con transmitancia
1 o 0.
def red_radial(self, r0 = (0 * um, 0 * um), periodo = 20 * um, desfase=0*um, radius=400*um
#si solamente hay un numero, es que las posiciones y radios son los mismos para am
if len(r0) == 1:
r0 = (r0[0], r0[0])
#Vector de onda
k = 2 * sp.pi / self.wavelength
x0, y0 = r0
#Distancia de la generatriz al eje del cono
r = sp.sqrt((self.X - x0) ** 2 + (self.Y - y0) ** 2)
#hago un seno y luego binarizo
t = 0.5*(1+sp.sin(2*sp.pi*(r+desfase)/periodo))
if binaria==True:
i0 = t <= 0.5; t[i0] = 0
i1 = t > 0.5; t[i1] = 1
#Region de transmitancia
u = sp.zeros(sp.shape(self.X))
ipasa = r < radius
u[ipasa] = 1
self.u = u * t

Así resulta la representación


def ejemplo_red_radial():
ndatos = 512
tamano = 250 * um
x = sp.linspace(-tamano / 2, tamano / 2, ndatos)

242 Capítulo 15. Redes de Difracción


PIMCD2013-python, Publicación 1.0

y = sp.linspace(-tamano / 2, tamano / 2, ndatos)


wavelength = 0.6328 * um

t1 = mascaraXY(x, y, wavelength)
t1.red_radial(r0 = (0 * um, 0 * um), periodo=20*um, desfase=10*um, radius = 100 * um)
t1.dibujar(tipo = ’intensidad’)

1.0
100 0.9
0.8
50 0.7
0.6
y(µm)

0 0.5
0.4
50 0.3
0.2
100 0.1

100 50 0 50 100 0.0


x(µm)

15.9 Red angular

Las redes de difracción también pueden tener simetría angular. El periodo va en grados.
def red_angular(self, r0 = (0 * um, 0 * um), periodo = 20*grados, desfase=0*grados, radius
#si solamente hay un numero, es que las posiciones y radios son los mismos para am
if len(r0) == 1:
r0 = (r0[0], r0[0])
#Vector de onda
k = 2 * sp.pi / self.wavelength
x0, y0 = r0
#Distancia de la generatriz al eje del cono
r = sp.sqrt((self.X - x0) ** 2 + (self.Y - y0) ** 2)
theta=sp.arctan((self.Y - y0)/(self.X - x0))
#Region de transmitancia
t = (1+sp.sin(2*sp.pi*(theta-desfase)/periodo))/2
if binaria==True:
i0 = t <= 0.5; t[i0] = 0
i1 = t > 0.5; t[i1] = 1

u = sp.zeros(sp.shape(self.X))
ipasa = r < radius
u[ipasa] = 1

15.9. Red angular 243


PIMCD2013-python, Publicación 1.0

self.u = u * t

Así resulta la representación

1.0
100 0.9
0.8
50 0.7
0.6
y(µm)

0 0.5
0.4
50 0.3
0.2
100 0.1

100 50 0 50 100 0.0


x(µm)

15.10 Red con borde sinusoidal

Este tipo de redes puede tener incidencia para eliminar armónicos en la dirección x
def red_borde_sinusoidal(self, r0 = (0 * um, 0 * um), periodo = 20*grados, lp=10*um, ap=2*
#si solamente hay un numero, es que las posiciones y radios son los mismos para am
#lp es la longitud del periodo del borde, ap es la amplitud del periodo del borde
if len(r0) == 1:
r0 = (r0[0], r0[0])
#Vector de onda
k = 2 * sp.pi / self.wavelength
x0, y0 = r0
#Distancia de la generatriz al eje del cono
r = sp.sqrt((self.X - x0) ** 2 + (self.Y - y0) ** 2)
theta=sp.arctan((self.Y - y0)/(self.X - x0))
#Region de transmitancia
Desfase=desfase+ap*sp.sin(2*sp.pi*self.Y/lp)

t = (1+sp.sin(2*sp.pi*(self.X-Desfase)/periodo))/2
if binaria==True:
i0 = t <= 0.5; t[i0] = 0
i1 = t > 0.5; t[i1] = 1

u = sp.zeros(sp.shape(self.X))
ipasa = r < radius

244 Capítulo 15. Redes de Difracción


PIMCD2013-python, Publicación 1.0

u[ipasa] = 1

self.u = u * t

Así resulta la representación, se produce una variación períodica del desfase.


def ejemplo_red_borde_sinusoidal():
ndatos = 1024
tamano = 250 * um
x = sp.linspace(-tamano / 2, tamano / 2, ndatos)
y = sp.linspace(-tamano / 2, tamano / 2, ndatos)
wavelength = 0.6328 * um
t1 = mascaraXY(x, y, wavelength)
t1.red_borde_sinusoidal(r0 = (0 * um, 0 * um), periodo = 20*um, lp=20*um, ap=10*um, desfas
t1.dibujar(tipo = ’intensidad’)

1.0
100 0.9
0.8
50 0.7
0.6
y(µm)

0 0.5
0.4
50 0.3
0.2
100 0.1

100 50 0 50 100 0.0


x(µm)

15.11 Red hiperbólica

def red_hiperbolica(self, r0 = (0 * um, 0 * um), periodo = 20*grados, desfase=0*grados, ra


#si solamente hay un numero, es que las posiciones y radios son los mismos para am
if len(r0) == 1:
r0 = (r0[0], r0[0])
#Vector de onda
k = 2 * sp.pi / self.wavelength
x0, y0 = r0
#Distancia de la generatriz al eje del cono

15.11. Red hiperbólica 245


PIMCD2013-python, Publicación 1.0

#rotacion de la lente
Xrot, Yrot = self.__rotar__(angulo)

r = sp.sqrt((self.X - x0) ** 2 + (self.Y - y0) ** 2)


x_posiciones = sp.sqrt(abs((Xrot - x0) ** 2 - (Yrot - y0) ** 2))
#Region de transmitancia
t = (1+sp.sin(2*sp.pi*x_posiciones/periodo))/2
if binaria==True:
i0 = t <= 0.5; t[i0] = 0
i1 = t > 0.5; t[i1] = 1

u = sp.zeros(sp.shape(self.X))
ipasa = r < radius
u[ipasa] = 1

self.u = u * t

Así resulta la representación, se produce una variación períodica del desfase.


def ejemplo_red_hiperbolica(periodo=20*um, potencia=2):
ndatos = 512
tamano = 200 * um
x = sp.linspace(-tamano / 2, tamano / 2, ndatos)
y = sp.linspace(-tamano / 2, tamano / 2, ndatos)
wavelength = 0.6328 * um

t1 = mascaraXY(x, y, wavelength)
t1.red_hiperbolica( r0 = (0 * um, 0 * um), periodo = 10*um, desfase=45*grados, radius=200*
t1.dibujar(tipo=’intensidad’)

100 1.0
0.9
0.8
50
0.7
0.6
y(µm)

0 0.5
0.4
0.3
50
0.2
0.1
100100 50 0 50 100 0.0
x(µm)

246 Capítulo 15. Redes de Difracción


PIMCD2013-python, Publicación 1.0

15.12 Aplicación como redes de difracción en campo lejano

El efecto de las interferencias surge directamente de que las ecuaciones de Maxwell son lineales en los campos
eléctricos y magnéticos. Supongamos que E1 y E2 son soluciones. Entonces

𝜋𝑎2
𝑧≫
𝜆
donde a es el tamaño máximo del elemento difractor. Bajo esta aproximación, el campo difractado se puede
calcular como una integral
𝑥2 +𝑦 2
𝑒𝑖𝑘(𝑧+ 2𝑧 ) ∫︁ ∫︁
𝑘
E(𝑥, 𝑦, 𝑧) = E0 (𝜉, 𝜂)𝑒−𝑖 𝑧 (𝑥𝜉+𝑦𝜂) 𝑑𝜉𝑑𝜂.
𝑖𝜆𝑧

donde E0 (𝜉, 𝜂) es el campo justo a la salida del elemento difractor.


La aproximación más usual para este campo es considerar que se obtiene como el producto del campo incidente
E𝑖𝑛 (𝜉, 𝜂) por el coeficiente de transmisión del máscara 𝑡(𝜉, 𝜂)E𝑖𝑛 (𝜉, 𝜂)

E𝑜𝑢𝑡 (𝜉, 𝜂) = 𝑡(𝜉, 𝜂)E𝑖𝑛 (𝜉, 𝜂).

La gran ventaja de esta ecuación es que se puede escribir como una transformada de Fourier, que es una operación
bastante conocida en matemáticas
𝑥2 +𝑦 2
𝑒𝑖𝑘(𝑧+ 2𝑧 )
E(𝑥, 𝑦, 𝑧) = 𝑇 𝐹 [E0 (𝜉, 𝜂)] .
𝑖𝜆𝑧
Como el primer término delante de la transformada de Fourier es casi constante para un plano z determinado, se
suele eliminar.

15.12.1 Difracción por una red de sinusoidal

La red sinusoidal tiene solamente tres frecuencias espaciales, que son las que se observan en la figura de difracción.
def difraccion_red_seno():
#tamano de area de visualizacion
tamano = 1000 * um
ndatos = 512
x0 = sp.linspace(-tamano / 2, tamano / 2, ndatos)
y0 = sp.linspace(-tamano / 2, tamano / 2, ndatos)
#longitud de onda
lambda0 = 0.6238 * um

#fuente de iluminacion
u1 = fuenteXY(x = x0, y = y0, wavelength = lambda0);
u1.onda_plana(A = 1, theta = 0 * grados, phi = 0 * grados)

#mascara
t1 = mascaraXY(x = x0, y = y0, wavelength = lambda0)
t1.red_seno(periodo=40*um)
u2 = u1 * t1

u3 = u2.fft(quitar0 = False)

u2.dibujar()

punto1=(u3.x.min(),0)
punto2=(u3.x.max(),0)

u3.dibujar_perfil(punto1,punto2, normalizar=’maximo’, order=2)

15.12. Aplicación como redes de difracción en campo lejano 247


PIMCD2013-python, Publicación 1.0

Tras ejecutar el código se obtiene la máscara y el perfil de intensidades en la pantalla.

1.0
400 0.9
0.8
200 0.7
0.6
y(µm)

0 0.5
0.4
200 0.3
0.2
400 0.1

400 200 0 200 400 0.0


x(µm)

0.8

0.6
Intensidad

0.4

0.2

0.0

1500 1000 500 0 500 1000 1500


h (perfil)
248 Capítulo 15. Redes de Difracción
PIMCD2013-python, Publicación 1.0

15.12.2 Difracción por una red de Ronchi

La red de Ronchi tiene más frecuencias espaciales, todas equidistanciadas, que son las que se observan en la figura
2
de difracción. La altura de cada pico es el cuadrado del valor del coeficiente de Fourier 𝐼𝑝𝑖𝑐𝑜,𝑛 = |𝑎𝑛 | .
def difraccion_red_ronchi():
#tamano de area de visualizacio
tamano = 1000 * um
ndatos = 512
x0 = sp.linspace(-tamano / 2, tamano / 2, ndatos)
y0 = sp.linspace(-tamano / 2, tamano / 2, ndatos)
#longitud de onda
lambda0 = 0.6238 * um

#fuente de iluminacion
u1 = fuenteXY(x = x0, y = y0, wavelength = lambda0);
u1.onda_plana(A = 1, theta = 0 * grados, phi = 0 * grados)

#mascara
t1 = mascaraXY(x = x0, y = y0, wavelength = lambda0)
t1.red_ronchi( periodo = 40 * um, x0 = 0 * um, fillFactor = 0.5, angulo = 0 * grados)
u2 = u1 * t1

u3 = u2.fft(quitar0 = False)

u2.dibujar()

punto1=(u3.x.min(),0)
punto2=(u3.x.max(),0)

u3.dibujar_perfil(punto1,punto2, normalizar=’maximo’, order=2)

Tras ejecutar el código se obtiene la máscara y el perfil de intensidades en la pantalla.

15.12. Aplicación como redes de difracción en campo lejano 249


PIMCD2013-python, Publicación 1.0

1.0
400 0.9
0.8
200 0.7
0.6
y(µm)

0 0.5
0.4
200 0.3
0.2
400 0.1

400 200 0 200 400 0.0


x(µm)

0.8

0.6
Intensidad

0.4

0.2

0.0

1500 1000 500 0 500 1000 1500


h (perfil)

250 Capítulo 15. Redes de Difracción


PIMCD2013-python, Publicación 1.0

15.13 Efecto Talbot

Para conocer la distribución de intensidad en campo cercano utilizaremos la aproximación de Fresnel (ver di-
fracción en campo cercano), considerando la invariancia de la red de difracción en el eje 𝜂. Entonces el campo
difractado se puede calcular a partir de
√︂ ∫︁
𝑖 𝑖𝑘𝑧 𝑘 2
E (𝑥, 𝑧) = 𝑒 E0 (𝜉)𝑒𝑖 2𝑧 (𝑥−𝜉) 𝑑𝜉
𝜆𝑧
Resolviendo la integral para la estructura periódica de una red de difracción, el campo resulta
∞ (︂ )︂ (︂ )︂
∑︁ 𝑥 𝜆
E (𝑥, 𝑧) = 𝑎𝑙 exp 2𝜋𝑖𝑙 exp 𝑖2𝜋𝑙2 2 𝑧
𝑝 2𝑝
𝑙=−∞

y, por tanto, la intensidad


∞ ∞ [︂ ]︂ [︂ ]︂
∑︁ ∑︁ 𝑥 )︀ 𝜆
𝑎𝑙 𝑎*𝑙′ ′ ′2
(︀ 2
𝐼 (𝑥, 𝑧) = exp 2𝜋𝑖 (𝑙 − 𝑙 ) exp 2𝜋𝑖 𝑙 − 𝑙 𝑧
𝑝 2𝑝2
𝑙′ =−∞ 𝑙=−∞

Esta ecuación se ha implementado numéricamente para observar el aspecto de la distribución de intensidad en


campo cercano producido por una red de difracción.

150
5.6
100
4.8
50 4.0
x (microns)

0 3.2

2.4
50
1.6
100
0.8

1500 2000 4000 6000 8000 10000 0.0


z (microns)
En la figura se observa el fenómeno de las autoimágenes, al ser periódico en el eje z. Se forman réplicas de la red
a distancias conocidas como distancia de Talbot, que resulta ser

𝑧𝑇 = 2𝑝2 /𝜆.

La parte principal del código, que es la obtencion del campo, es la siguiente

15.13. Efecto Talbot 251


PIMCD2013-python, Publicación 1.0

def efectoTalbot(t, z, periodo=40*um):


""" esta funcion calcula el efecto talbot"""

zT=2*periodo**2/t.wavelength
X,Z=sp.meshgrid(t.x,z)
periodo=50*um
ordenes=sp.array([-3, -1, 0, 1, 3])
coefs=sp.array([.25, 0.5, 1, 0.5, .25])

red=sp.transpose([ordenes, coefs])
u2=sp.zeros(X.shape,complex)

for j2,aj in red:


u2+=aj*sp.exp(1j*2*sp.pi*j2*X/periodo)*sp.exp(-1j*2*sp.pi*j2**2*Z/zT)

campoxz=sp.real(u2)
return campoxz

Podemos observar el aspecto de las automágenes simplemente calculando la distribución de intensidad en campo
cercano mediante la aproximación de Rayleigh-Sommerfeld. Esto lo hemos hecho para 3 distancias significativas
en la red de Ronchi, 𝑧𝑇 , 𝑧𝑇 /2 y 𝑧𝑇 /4:
efecto talbot
zT zT/2 zT/4
100 100 100
50 50 50
0 0 0
50 50 50
100 100 100
100 50 0 50 100 100 50 0 50 100 100 50 0 50 100

0.00.10.20.30.40.50.60.70.80.91.0 0.00.10.20.30.40.50.60.70.80.91.0 0.10.20.30.40.50.60.70.80.91.0

Veamos ahora que ocurre cuando sobre la red de difracción se incide con un haz gaussiano. Al propagarse la luz,
los distintos órdenes de tamaño finito, empiezan a no interferir entre sí, al no estar espacialmente en la mismas
posiciones. Entonces, deja de producirse el fenómeno interferencial y tenemos los distintos ordenes aislados. Esto
ocurre en campo cercano, y no solamente cuando estamos muy lejos de la red.

252 Capítulo 15. Redes de Difracción


PIMCD2013-python, Publicación 1.0

zT efecto talbot 2zT


200 200
100 100
0 0
100 100
200 200
200 100 0 100 200 200 100 0 100 200
0.0 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1.0 0.0 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1.0
3zT 4zT
200 200
100 100
0 0
100 100
200 200
200 100 0 100 200 200 100 0 100 200
0.0 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1.0 0.0 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1.0

15.14 Efectos con dos redes

Los sistemas de doble red se utilizan frecuentemente en metrología óptica, como en deflectometría moiré para
medir la topografía 3D de objetos o en sistemas Talbot, Lau o autoimágenes generalizadas para medir desplaza-
mientos con gran precisión.
Veremos ahora algunos efectos de doble red. En el desplazamiento Talbot (movimiento de dos redes cuando es
iluminado con onda plana). Se muestra una animación

15.14. Efectos con dos redes 253


PIMCD2013-python, Publicación 1.0

200

100

100

200

400 200 0 200 400

Otro efecto interesante es el efecto vernier, donde aparecen franjas cuando a) el periodo de las dos redes es distinto
y b) el ángulo entre las dos redes es distinto.
Se muestra estos dos casos mostrando una imagen y una animación:
Distinto periodo:

254 Capítulo 15. Redes de Difracción


PIMCD2013-python, Publicación 1.0

efecto vernier

1000

500
y(µm)

500

10002000 1500 1000 500 0 500 1000 1500 2000


x(µm)

100

200

300

400

500
0 100 200 300 400 500

Distinto ángulo:

15.14. Efectos con dos redes 255


PIMCD2013-python, Publicación 1.0

efecto vernier
1000

500
y(µm)

500

10001000 500 0 500 1000


x(µm)
0

100

200

300

400

500
0 100 200 300 400 500

Estos dos efectos permiten medir (macroscópicamente) el desplazamiento, puesto que las franjas de vernier (las

256 Capítulo 15. Redes de Difracción


PIMCD2013-python, Publicación 1.0

grandes) se desplazan de forma amplificada al desplazamiento relativo entre las redes.

400

200

200

400

400 200 0 200 400

400

200

200

400

400 200 0 200 400

15.14. Efectos con dos redes 257


PIMCD2013-python, Publicación 1.0

258 Capítulo 15. Redes de Difracción


CAPÍTULO 16

Procesado óptico de la información

Autor Luis Miguel Sánchez Brea


Revisor José Luis Vilas Prieto
Se entiende por procesado de imágenes a cualquier transformación que pueda ser realizada en la imagen ya sea
esta digital u óptica. El procedimiento para cualquiera de los dos tipos de imágenes es el mismo, y en gran parte
del procesado de imágenes implica pasar al espacio de frecuencias es decir, realizar la transformada de Fourier. Se
puede probar como un haz colimado focaliza en el foco de la lente, obteniéndose en dicho punto la transformada
de Fourier del objeto. La transformada de Fourier de la imagen, contiene la misma información pero en el espacio
de frecuencias, ubicándose en los bajos órdenes de difracción las bajas frecuencias los cuales se corresponden con
la imagen general del objeto. Así mismo todos aquellos puntos alejados del origen, esto es los ordenas más altos de
difracción representan aquellos pequeños detalles de la imagen, por tanto si se eliminan se pierde definición pero
se mantiene la estructura de la imagen. El objetivo de los filtros de frecuencias consiste en obtener la transformada
de Fourier de una imagen y tapar la región de la transformada con las frecuencias a eliminar, finalmente se realizará
la transformada inversa para recuperar la imagen filtrada.

Contenidos de este capítulo:


Funcionamiento de la clase procesadoOptico
Filtro pasa baja
Eliminación de ruido
Filtro pasa alta
Filtro pasa banda
Filtro de contraste de fase

Herramientas utilizadas en este tutorial:


scipy: manipulación de arrays de datos.
matplotlib.pyplot: generación de dibujos similar a Matlab.
fuentesXY: Definición de haces de luz.
camposXY: Diversas funciones de tratamiento de imágenes e inicialización.
Archivos necesarios para el procesado de imágenes:
Archivo con las funciones principales procesadoOptico.py
Ejemplo filtro pasa baja test_filtroPasaBaja.py
Ejemplo filtro detección de bordes test_filtroPasaAlta.py
Ejemplo filtro pasa Banda test_filtroPasaAlta2.py
Ejemplo filtro pasa Banda test_filtroPasaBanda.py
Ejemplo filtro de eliminación de ruido test_filtroEliminarRuido.py
Ejemplo filtro contraste de fase test_filtroContrasteFase.py

259
PIMCD2013-python, Publicación 1.0

Contenidos de los archivos:


procesadoOptico.py:
• procesar(). Realiza el procesamiento de la imagen, desde la definición del campo inicial, el plano
de Fourier, la aplicación del filtro y la obtención del campo filtrado.
• dibujar(). Representa el proceso de filtrado, intensidad del objeto, su fase, el plano de Fourier, el
filtro de intensidad, el filtro de fase y el campo filtrado.
• dibujarCampoFiltrado (). Representa el campo filtrado.

16.1 Funcionamiento de la clase procesadoOptico

A continuación se muestran una serie de funciones necesarias para el procesado óptico.


Al igual que en un experimento físico se dispone de una fuente, un objeto y un filtro, en simulación es preciso
definir estos ‘parámetros’ del experimento para realizar un procesamiento óptico de la imagen. Así se inicia la
clase con la función __init__ especificando tales condiciones. Esta función dispone de un if, de modo que en
ausencia de cualquiera de estos tres elementos no se realice filtrado alguno.
def __init__(self, fuente, objeto, filtro):
"""Se inicia un nuevo experimento"""
#Se definen los parametros de describen la fuente, el objeto y el filtro
self.fuente = fuente
self.objeto = objeto
self.filtro = filtro

#Si alguno de ellos falla no puede realizarse procesamiento alguno


if fuente == None or objeto == None or filtro == None:
self.planoFourier = None
self.campoFiltrado = None
else:
self.procesar()

Así mismo se define la función de procesamiento, la cual calcula el campo producto del objeto con la ilumi-
nación. Puesto que se trabaja en el plano focal y en este se obtiene la transformada de Fourier del objeto bajo
un haz colimado, se calcula la transformada de Fourier. Finalmente se aplica el filtro realizando el producto de
la transformada de Fourier por la función filtro. Finalmente, se deshace la transformada, para obtener el campo
filtrado.
def procesar(self):
#Campo inicial
campoInicial = self.fuente * self.objeto

#Transformada de Fourier del campo inicial


planoFourier = campoInicial.fft(quitar0 = False, shift = True)

#Aplicacion del filtro


planoFourierFiltrado = planoFourier * self.filtro

#Transformada de Fourier para obtener el campo filtrado


campoFiltrado = planoFourierFiltrado.fft(quitar0 = True, shift = False)

self.planoFourier = planoFourier
self.campoFiltrado = campoFiltrado

Resta realizar una representación de los campos a fin de comprar el incidente y el filtrado, esto se lo hace la función
dibujar. Así se genera un array de imágenes donde se ubicarán el objeto, su fase, la transformada de Fourier,
el filtro de intensidad, el filtro de fase y finalmente el campo filtrado. Así se selecciona la componente del array
donde representar la imagen (objeto, fase, transformada de Fourier...). Finalmente se dibuja con plt, el resto de
comandos determinan el título, los ejes, entre otros.

260 Capítulo 16. Procesado óptico de la información


PIMCD2013-python, Publicación 1.0

def dibujar(self, nombreArchivo = ’’):


#Se crea uan figura
id = plt.figure(figsize = (9, 6), facecolor = ’w’, edgecolor = ’k’)

#objeto intensidad
#Seleccion de la region dela figura donde se pretende representar
plt.subplot(2, 3, 1)
#Intensidad
dibujo = abs(self.objeto.u) ** 2
#Representacion
so=self.objeto
h1 = plt.imshow(dibujo, interpolation = ’bilinear’, aspect = ’auto’,
origin = ’lower’, extent = [so.x.min(), so.x.max(), so.y.min(),\

#Titulo
plt.title(’objeto intensidad’, fontsize = 18)
#Seleccion del mapa de color
h1.set_cmap("gist_heat") #RdBu
#Los ejes no se representan
plt.axis(’off’)

#objeto fase
plt.subplot(2, 3, 2)
dibujo = sp.angle(self.objeto.u)
#Representacion
h1 = plt.imshow(dibujo, interpolation = ’bilinear’, aspect = ’auto’,
origin = ’lower’, extent = [so.x.min(), so.x.max(), so.y.min(),\

#Titulo
plt.title(’objeto fase’, fontsize = 18)
#Seleccion del mapa de color
h1.set_cmap("RdBu") #RdBu
#Los ejes no se representan
plt.axis(’off’)

#filtro intensidad
plt.subplot(2, 3, 4)
#Intensidad
dibujo = abs(self.filtro.u) ** 2
#Representacion
sf=self.filtro
h1 = plt.imshow(dibujo, interpolation = ’bilinear’, aspect = ’auto’,\
origin = ’lower’, \
extent = [sf.x.min(), sf.x.max(), sf.y.min(), sf.y.max()])
#Titulo
plt.title(’filtro intensidad’, fontsize = 18)
#Seleccion del mapa de color
h1.set_cmap("gist_heat") #RdBu
#Los ejes no se representan
plt.axis(’off’)

#filtro fase
plt.subplot(2, 3, 5)
dibujo = sp.angle(self.filtro.u)
#Representacion
h1 = plt.imshow(dibujo, interpolation = ’bilinear’, aspect = ’auto’,
origin = ’lower’, extent = [sf.x.min(), sf.x.max(), sf.y.min(),\

#Titulo
plt.title(’filtro fase’, fontsize = 18)
#Seleccion del mapa de color
h1.set_cmap("RdBu") #RdBu
#Los ejes no se representan

16.1. Funcionamiento de la clase procesadoOptico 261


PIMCD2013-python, Publicación 1.0

plt.axis(’off’)

#planoFourier
plt.subplot(2, 3, 3)
dibujo = sp.log(abs(self.planoFourier.u) ** 2 + 1)
#Representacion
pf=self.planoFourier
h1 = plt.imshow(dibujo, interpolation = ’bilinear’, aspect = ’auto’,
origin = ’lower’, extent = [pf.x.min(), pf.x.max(), pf.y.min(),\

plt.title(’plano Fourier’, fontsize = 18)


#Seleccion del mapa de color
h1.set_cmap("gist_heat") #RdBu
#Los ejes no se representan
plt.axis(’off’)

#campoFiltrado
plt.subplot(2, 3, 6)
dibujo = sp.fliplr(sp.flipud(abs(self.campoFiltrado.u) ** 2))
#Representacion
cf=self.campoFiltrado
h1 = plt.imshow(dibujo, interpolation = ’bilinear’, aspect = ’auto’,
origin = ’lower’, extent = [cf.x.min(), cf.x.max(), cf.y.min(),\

#Titulo
plt.title(’campo Filtrado’, fontsize = 18)
#Seleccion del mapa de color
h1.set_cmap("gist_heat") #RdBu
#Los ejes no se representan
plt.axis(’off’)

if not nombreArchivo == ’’:


plt.savefig(nombreArchivo, dpi = 100, bbox_inches = ’tight’,\
pad_inches = 0.1)

16.2 Filtro pasa baja

Un filtro pasa es aquel que atenúa las altas frecuencias y permitiendo únicamente el paso de las bajas frecuencias.
De este modo, un filtro pasa baja conserva los detalles más generales de la imagen, eliminando los pequeños
detalles. Esto se logra en el plano de Fourier diafragmando los mayores órdenes de difracción, por ser estos los
correspondientes a altas frecuencias. En las imágenes de plano Fourier y filtro intensidad se puede apreciar este
hecho. La abertura del filtro de intensidad se ubica en el plano de Fourier, atravesando el filtro las bajas frecuencias.
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#------------------------------------
# Autor: Luis Miguel Sanchez Brea
# Fecha 2013/10/24 (version 1.0)
# Licencia: GPL
#-------------------------------------
"""Simulaciones relacionadas con la leccion de filtrado optico"""

#Importacion de paquetes y librerias


import locale
locale.setlocale(locale.LC_NUMERIC, ’C’)
import sys
sys.path.append(’../’)
import scipy as sp
from procesadoOptico import *
from fuentesXY import fuenteXY

262 Capítulo 16. Procesado óptico de la información


PIMCD2013-python, Publicación 1.0

from mascarasXY import mascaraXY

#Tamano
tamano = 1000 * um
x0 = sp.linspace(-tamano/2 * um, tamano/2 * um, 512)
y0 = sp.linspace(-tamano/2 * um, tamano/2 * um, 512)

#Definicion de la fuente
fuente1 = fuenteXY(x=x0, y=y0)
fuente1.onda_plana(A=1, theta=0*grados, phi=0*grados)

def test_filtroPasaBaja(): #amplitud


#Definicion del objeto
objeto = mascaraXY(x=x0, y=y0)
objeto.imagen(nombre=’lena.png’, normalizar=True, canal=0, tamanoImagen=False, invertir=Fa
#Definicion del filtro
filtro = mascaraXY(x=x0, y=y0)
filtro.circulo(r0=(0*um,0*um), radius=(20*um,20*um), angulo=0*grados)
#Campo filtrado, procesado
p1=procesadoOptico(fuente1, objeto, filtro)
p1.dibujar2(idfigs=(1,0,2,3,0,4))
p1.dibujarCampoFiltrado()

if __name__ == ’__main__’:
test_filtroPasaBaja()
plt.show()

objeto intensidad FFT

filtro intensidad Imagen

16.2. Filtro pasa baja 263


PIMCD2013-python, Publicación 1.0

16.3 Eliminación de ruido

Una aplicación interesante del filtro pasa baja es la eliminación de ruido en una imagen. Para comprobarlo, toma-
mos la foto de lena.png y añadimos un ruido aleatorio.
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#------------------------------------
# Autor: Luis Miguel Sanchez Brea
# Fecha 2013/10/24 (version 1.0)
# Licencia: GPL
#-------------------------------------
"""Simulaciones relacionadas con la leccion de filtrado optico"""

#Carga de paquetes y librerias


import locale
locale.setlocale(locale.LC_NUMERIC, ’C’)
import sys
sys.path.append(’../’)
import scipy as sp
from procesadoOptico import *

264 Capítulo 16. Procesado óptico de la información


PIMCD2013-python, Publicación 1.0

from fuentesXY import fuenteXY


from mascarasXY import mascaraXY

tamano = 1000 * um
x0 = sp.linspace(-tamano/2 * um, tamano/2 * um, 512)
y0 = sp.linspace(-tamano/2 * um, tamano/2 * um, 512)

def test_filtroEliminarRuido():
#Definicion de la fuente
fuente1 = fuenteXY(x=x0, y=y0)
fuente1.onda_plana(A=1, theta=0*grados, phi=0*grados)
#Definicion del objeto
objeto = mascaraXY(x=x0, y=y0)
objeto.imagen(nombre=’lena.png’, normalizar=True, canal=0, tamanoImagen=False, invertir=Fa
objeto.u=objeto.u+sp.rand(len(x0),len(y0))
#Definicion del filtro
filtro = mascaraXY(x=x0, y=y0)
filtro.circulo(r0=(0*um,0*um), radius=(50*um,50*um), angulo=0*grados)
#Campo filtrado, procesado de la imagen
p1=procesadoOptico(fuente1, objeto, filtro)
p1.dibujar2(idfigs=(1,0,2,3,0,4))
p1.dibujarCampoFiltrado()

test_filtroEliminarRuido()
plt.show()

objeto intensidad FFT

filtro intensidad Imagen

16.3. Eliminación de ruido 265


PIMCD2013-python, Publicación 1.0

16.4 Filtro pasa alta

Este tipo de filtro es el opuesto al filtro pasa baja, conserva las altas frecuencias y atenúa las bajas. De este modo,
los detalles más gruesos de la imagen ser pierden, pero los pequeños se mantienen. Por ejemplo un conjunto de
franjas equiespaciadas muy separadas entre sí, se atenuarían respecto de otro conjunto de franjas poco espaciadas
entre sí. La consecución de este tipo de filtro se logra tapando los órdenes más bajos de difracción en el plano de
Fourier, atravesando únicamente el filtro, los más altos. Nuevamente, véase la imagen, como el plano de Fourier el
filtro de intensidad tapa dichos órdenes, de modo que el campo filtrado muestra los pequeños detalles de la imagen
(altas frecuencias).
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#------------------------------------
# Autor: Luis Miguel Sanchez Brea
# Fecha 2013/10/24 (version 1.0)
# Licencia: GPL
#-------------------------------------
"""Simulaciones relacionadas con la leccion de filtrado optico"""

#Carga de librerias y paquetes

266 Capítulo 16. Procesado óptico de la información


PIMCD2013-python, Publicación 1.0

import sys
sys.path.append(’../’)
from procesadoOptico import *
from fuentesXY import fuenteXY
from mascarasXY import mascaraXY

#Tamano
tamano = 1000 * um
x0 = sp.linspace(-tamano/2 * um, tamano/2 * um, 512)
y0 = sp.linspace(-tamano/2 * um, tamano/2 * um, 512)

def test_filtroPasaAlta():
#Definicion de la fuente
fuente1 = fuenteXY(x=x0, y=y0)
fuente1.onda_plana(A=1, theta=0*grados, phi=0*grados)
#Definicion del Objeto
objeto = mascaraXY(x=x0, y=y0)
objeto.imagen(nombre=’lena.png’, normalizar=True, canal=0, tamanoImagen=False, invertir=Fa
#Definicion del filtro
filtro = mascaraXY(x=x0, y=y0)
filtro.circulo(r0=(0*um,0*um), radius=(50*um,50*um), angulo=0*grados)
filtro.inversaAmplitud()
#Campo filtrado, procesado de la imagen
p1=procesadoOptico(fuente1, objeto, filtro)
p1.dibujar2(idfigs=(1,0,2,3,0,4))
p1.dibujarCampoFiltrado()

test_filtroPasaAlta()
plt.show()

16.4. Filtro pasa alta 267


PIMCD2013-python, Publicación 1.0

objeto intensidad FFT

filtro intensidad Imagen

268 Capítulo 16. Procesado óptico de la información


PIMCD2013-python, Publicación 1.0

veamos otro ejemplo de detección de bordes, donde éstos están más definidos.
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#------------------------------------
# Autor: Luis Miguel Sanchez Brea
# Fecha 2013/10/24 (version 1.0)
# Licencia: GPL
#-------------------------------------
"""Simulaciones relacionadas con la leccion de filtrado optico"""

#Carga de librerias y paquetes


import sys
sys.path.append(’../’)
from procesadoOptico import *
from fuentesXY import fuenteXY
from mascarasXY import mascaraXY

tamano = 1000 * um
x0 = sp.linspace(-tamano/2 * um, tamano/2 * um, 512)
y0 = sp.linspace(-tamano/2 * um, tamano/2 * um, 512)

def test_filtroPasaAlta2():

16.4. Filtro pasa alta 269


PIMCD2013-python, Publicación 1.0

#Definicion de la fuente
fuente1 = fuenteXY(x=x0, y=y0)
fuente1.onda_plana(A=1, theta=0*grados, phi=0*grados)
#Definicion del bjeto
objeto = mascaraXY(x=x0, y=y0)
objeto.cuadrado(r0=(0,0), size=(250*um,250*um), angulo=45*grados)
#Definicion del filtro
filtro = mascaraXY(x=x0, y=y0)
filtro.circulo(r0=(0*um,0*um), radius=(40*um,40*um), angulo=0*grados)
filtro.inversaAmplitud()
#Campo filtrado, procesamiento de la imagen
p1=procesadoOptico(fuente1, objeto, filtro)
p1.dibujar2(idfigs=(1,0,2,3,0,4))
p1.dibujarCampoFiltrado()

test_filtroPasaAlta2()
plt.show()

objeto intensidad FFT

filtro intensidad Imagen

270 Capítulo 16. Procesado óptico de la información


PIMCD2013-python, Publicación 1.0

16.5 Filtro pasa banda

Los filtros pasa banda se ubican entre los filtros pasa alta y los filtros pasa baja, permitiendo únicamente el paso de
frecuencias dentro de una banda espectral, filtrando las altas y las bajas frecuencias. Para ello, se bloquean en el
plano de Fourier los bajos y altos órdenes de difracción, así el filtro de intensidad tendrá forma de corona circular,
siendo los puntos interiores a la corona, aquellos que permiten en paso de luz.
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#------------------------------------
# Autor: Luis Miguel Sanchez Brea
# Fecha 2013/10/24 (version 1.0)
# Licencia: GPL
#-------------------------------------
"""Simulaciones relacionadas con la leccion de filtrado optico"""

#Carga de librerias y paquetes


import sys
sys.path.append(’../’)
from procesadoOptico import *

16.5. Filtro pasa banda 271


PIMCD2013-python, Publicación 1.0

from fuentesXY import fuenteXY


from mascarasXY import mascaraXY

tamano = 1000 * um
x0 = sp.linspace(-tamano/2 * um, tamano/2 * um, 512)
y0 = sp.linspace(-tamano/2 * um, tamano/2 * um, 512)

def test_filtroPasaBanda(): #amplitud


#Definicion de la fuente
fuente1 = fuenteXY(x=x0, y=y0)
fuente1.onda_plana(A=1, theta=0*grados, phi=0*grados)
#Definicion del objeto
objeto = mascaraXY(x=x0, y=y0)
objeto.imagen(nombre=’lena.png’, normalizar=True, canal=0, tamanoImagen=False, invertir=Fa
#Definicion del filtro
filtro = mascaraXY(x=x0, y=y0)
filtro.anillo(r0=(0*um,0*um), radius1=(50*um,50*um), radius2=(80*um,80*um), angulo=0*grado
#Campo filtrado, procesado de la imagen
p1=procesadoOptico(fuente1, objeto, filtro)
p1.dibujar2(idfigs=(1,0,2,3,0,4))
p1.dibujarCampoFiltrado()

test_filtroPasaBanda()
plt.show()

objeto intensidad FFT

filtro intensidad Imagen

272 Capítulo 16. Procesado óptico de la información


PIMCD2013-python, Publicación 1.0

16.6 Filtro de contraste de fase

Un filtro de contraste de fase potencia las diferencias de fase entre las distintas regiones del objeto. Para ello se
ilumina el objeto con luz colimada y se introduce una lámina/anillo desfasador posterior al sistema de iluminación,
el objetivo de este anillo es retardar la fase de la luz.
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#------------------------------------
# Autor: Luis Miguel Sanchez Brea
# Fecha 2013/12/10 (version 1.5) - dibujar 2
# Licencia: GPL
#-------------------------------------
"""Simulaciones relacionadas con la leccion de filtrado optico"""

#Carga de librerias y paquetes


import sys
sys.path.append(’../’)
from procesadoOptico import *
from fuentesXY import fuenteXY
from mascarasXY import mascaraXY

16.6. Filtro de contraste de fase 273


PIMCD2013-python, Publicación 1.0

def filtroContrasteFase():
#Definicion de la fuente
fuente1 = fuenteXY(x=x0, y=y0)
fuente1.onda_plana(A=1, theta=0*grados, phi=0*grados)
#Definicion del objeto
objeto = mascaraXY(x=x0, y=y0)
objeto.imagen(nombre=’lena.png’, normalizar=True, canal=0, tamanoImagen=False, invertir=Fa
objeto.setFase(q=1, fase_min=0, fase_max=sp.pi)
#Definicion del filtro
filtro = mascaraXY(x=x0, y=y0)
filtro.circulo(r0=(0*um,0*um), radius=(4*um,4*um), angulo=0*grados)
filtro.setFase(q=1, fase_min=0, fase_max=sp.pi)
#Campo filtrado y procesado de la imagen
p1=procesadoOptico(fuente1, objeto, filtro)
p1.dibujar2(idfigs=(0,1,2,0,3,4))
p1.dibujarCampoFiltrado()
filtroContrasteFase()
plt.show()

objeto fase FFT

filtro fase Imagen

274 Capítulo 16. Procesado óptico de la información


PIMCD2013-python, Publicación 1.0

16.6. Filtro de contraste de fase 275

También podría gustarte