# !/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
This module generates Vector_mask_XY class for defining vector masks. Its parent is Vector_field_XY.
The main atributes are:
self.x (numpy.array): linear array with equidistant positions. The number of data is preferibly 2**n.
self.y (numpy.array): linear array with equidistant positions. The number of data is preferibly 2**n.
self.wavelength (float): wavelength of the incident field.
self.Ex (numpy.array): Electric_x field
self.Ey (numpy.array): Electric_y field
*Class for bidimensional vector XY masks*
*Functions*
* unique_masks
* equal_masks
* global_mask
* complementary_masks
* from_py_pol
* polarizer_linear
* quarter_waveplate
* half_wave
* polarizer_retarder
"""
import copy
from py_pol.jones_matrix import Jones_matrix
from . import degrees, np, number_types, plt
from .config import CONF_DRAWING
from .scalar_masks_XY import Scalar_mask_XY
from .utils_optics import field_parameters
from .vector_fields_XY import Vector_field_XY
from .vector_sources_XY import Vector_source_XY
[docs]
class Vector_mask_XY(Vector_field_XY):
def __init__(self, x, y, wavelength, info=''):
super().__init__(x, y, wavelength, info)
self.type = 'Vector_mask_XY'
self.M00 = np.zeros_like(self.X, dtype=complex)
self.M01 = np.zeros_like(self.X, dtype=complex)
self.M10 = np.zeros_like(self.X, dtype=complex)
self.M11 = np.zeros_like(self.X, dtype=complex)
del self.Ex, self.Ey, self.Ez
def __add__(self, other, kind='standard'):
"""adds two Vector_mask_XY. For example two masks
Parameters:
other (Vector_mask_XY): 2nd field to add
kind (str): instruction how to add the fields:
Returns:
Vector_mask_XY: `M3 = M1 + M2`
"""
if other.type in ('Vector_mask_XY'):
m3 = Vector_mask_XY(self.x, self.y, self.wavelength)
m3.M00 = other.M00 + self.M00
m3.M01 = other.M01 + self.M01
m3.M10 = other.M10 + self.M10
m3.M11 = other.M11 + self.M11
return m3
def __mul__(self, other):
"""
Multilies the Vector_mask_XY matrix by another Vector_mask_XY.
Parameters:
other (Vector_mask_XY): 2nd object to multiply.
Returns:
v_mask_XY (Vector_mask_XY): Result.
"""
if isinstance(other, number_types):
m3 = Vector_mask_XY(self.x, self.y, self.wavelength)
m3.M00 = self.M00 * other
m3.M01 = self.M01 * other
m3.M10 = self.M10 * other
m3.M11 = self.M11 * other
elif other.type in ('Vector_mask_XY', 'Vector_field_XY'):
m3 = Vector_mask_XY(self.x, self.y, self.wavelength)
m3.M00 = other.M00 * self.M00 + other.M01 * self.M10
m3.M01 = other.M00 * self.M01 + other.M01 * self.M11
m3.M10 = other.M10 * self.M00 + other.M11 * self.M10
m3.M11 = other.M10 * self.M01 + other.M11 * self.M11
else:
raise ValueError('other thype ({}) is not correct'.format(
type(other)))
return m3
def __rmul__(self, other):
"""
Multilies the Vector_mask_XY matrix by another Vector_mask_XY.
Parameters:
other (Vector_mask_XY): 2nd object to multiply.
Returns:
v_mask_XY (Vector_mask_XY): Result.
"""
if isinstance(other, number_types):
m3 = Vector_mask_XY(self.x, self.y, self.wavelength)
m3.M00 = self.M00 * other
m3.M01 = self.M01 * other
m3.M10 = self.M10 * other
m3.M11 = self.M11 * other
# print("numero * matriz")
elif other.type in ('Vector_source_XY', 'Vector_field_XY'):
m3 = Vector_source_XY(self.x, self.y, self.wavelength)
m3.Ex = self.M00 * other.Ex + self.M01 * other.Ey
m3.Ey = self.M10 * other.Ex + self.M11 * other.Ey
return m3
[docs]
def duplicate(self, clear=False):
"""Duplicates the instance"""
new_field = copy.deepcopy(self)
if clear is True:
new_field.clear_field()
return new_field
[docs]
def apply_circle(self, r0=None, radius=None):
"""The same circular mask is applied to all the Jones Matrix.
Parameters:
r0 (float, float): center, if None it is generated
radius (float, float): radius, if None it is generated
"""
if radius is None:
x_min, x_max = self.x[0], self.x[-1]
y_min, y_max = self.y[0], self.y[-1]
x_radius, y_radius = (x_max - x_min) / 2, (y_max - y_min) / 2
radius = (x_radius, y_radius)
if r0 is None:
x_center, y_center = (x_min + x_max) / 2, (y_min + y_max) / 2
r0 = (x_center, y_center)
u_pupil = Scalar_mask_XY(self.x, self.y, self.wavelength)
u_pupil.circle(r0=r0, radius=radius)
self.M00 = self.M00 * u_pupil.u
self.M01 = self.M01 * u_pupil.u
self.M10 = self.M10 * u_pupil.u
self.M11 = self.M11 * u_pupil.u
[docs]
def pupil(self, r0=None, radius=None, angle=0 * degrees):
"""place a pupil in the mask. If r0 or radius are None, they are computed using the x,y parameters.
Parameters:
r0 (float, float): center of circle/ellipse
radius (float, float) or (float): radius of circle/ellipse
angle (float): angle of rotation in radians
Example:
pupil(r0=(0 * um, 0 * um), radius=(250*um, 125*um), angle=0*degrees)
"""
if r0 is None:
x0 = (self.x[-1] + self.x[0]) / 2
y0 = (self.y[-1] + self.y[0]) / 2
r0 = (x0, y0)
if radius is None:
radiusx = (self.x[-1] - self.x[0]) / 2
radiusy = (self.y[-1] - self.y[0]) / 2
radius = (radiusx, radiusy)
x0, y0 = r0
if isinstance(radius, (float, int, complex)):
radiusx, radiusy = (radius, radius)
else:
radiusx, radiusy = radius
# Rotacion del circula/elipse
Xrot, Yrot = self.__rotate__(angle, (x0, y0))
# Definicion de la transmitancia
pupil0 = np.zeros(np.shape(self.X))
ipasa = (Xrot)**2 / (radiusx + 1e-15)**2 + \
(Yrot)**2 / (radiusy**2 + 1e-15) < 1
pupil0[ipasa] = 1
self.M00 = self.M00 * pupil0
self.M01 = self.M01 * pupil0
self.M10 = self.M10 * pupil0
self.M11 = self.M11 * pupil0
[docs]
def scalar_to_vector_mask(self, mask, state, is_intensity=True):
"""The same mask (binary) is applied to all the Jones Matrix.
Parameters:
mask (scalar_mask_XY): mask to apply.
state (Jones Matrix or Jones_matrix): Polarization state to apply
is intensity (bool): If True, abs(mask)**2 is applied.
"""
if isinstance(state, Jones_matrix):
state = state.M.squeeze()
# if is_intensity:
# t = np.abs(mask.u)**2
# else:
t = mask.u
self.M00 = state[0, 0] * t
self.M01 = state[0, 1] * t
self.M10 = state[1, 0] * t
self.M11 = state[1, 1] * t
# Change elements = -0 to 0, to represent correctly phases.
self.M01.real = np.where(np.real(self.M01) == -0, 0, np.real(self.M01))
self.M10.real = np.where(np.real(self.M10) == -0, 0, np.real(self.M10))
self.M00.real = np.where(np.real(self.M00) == -0, 0, np.real(self.M00))
self.M11.real = np.where(np.real(self.M11) == -0, 0, np.real(self.M11))
[docs]
def complementary_masks(self, mask, state_0, state_1, is_binarized=True):
"""Creates a vector mask from a scalar mask. It assign an state_0 to 0 values and a state_1 to 1 values..
For generality, ik mask is a decimal number between 0 and 1, it takes the linear interpolation.
Parameters:
mask (scalar_mask_XY): Mask preferently binary. if not, it is binarized
state_0 (2x2 numpy.array or Jones_matrix): Jones matrix for 0s.
state_1 (2x2 numpy.array or Jones_matrix): Jones matrix for 1s.
Warning:
TODO: Mask should be binary. Else the function should binarize it.
"""
if isinstance(state_0, Jones_matrix):
state_0 = state_0.M.squeeze()
if isinstance(state_1, Jones_matrix):
state_1 = state_1.M.squeeze()
t = np.abs(mask.u)**2
if is_binarized:
t = t / t.max()
t[t < 0.5] = 0
t[t >= 0.5] = 1
self.M00 = t * state_1[0, 0] + (1 - t) * state_0[0, 0]
self.M01 = t * state_1[0, 1] + (1 - t) * state_0[0, 1]
self.M10 = t * state_1[1, 0] + (1 - t) * state_0[1, 0]
self.M11 = t * state_1[1, 1] + (1 - t) * state_0[1, 1]
[docs]
def multilevel_mask(self, mask, states, discretize=True, normalize=True):
"""Generates a multilevel vector mask, based in a scalar_mask_XY. The levels should be integers in amplitude (0,1,..., N).
If it is not like this, discretize generates N levels.
Usually masks are 0-1. Then normalize generates levels 0-N.
Parameters:
mask (scalar_mask_XY): 0-N discrete scalar mask.
states (np.array or Jones_matrix): Jones matrices to assign to each level
discretize (bool): If True, a continuous mask is converted to N levels.
normalize (bool): If True, levels are 0,1,.., N.
"""
mask_new = mask.duplicate()
num_levels = len(states)
if discretize is True:
mask_new.discretize(num_levels=num_levels, new_field=False)
if normalize is True:
mask_new.u = mask_new.u / mask_new.u.max()
mask_new.u = mask_new.u * num_levels - 0.5
mask_new.u = np.real(mask_new.u)
mask_new.u = mask_new.u.astype(int)
for i, state in enumerate(states):
i_level = (mask_new.u == i)
self.M00[i_level] = state.M[0, 0, 0]
self.M01[i_level] = state.M[0, 1, 0]
self.M11[i_level] = state.M[1, 1, 0]
self.M10[i_level] = state.M[1, 0, 0]
[docs]
def from_py_pol(self, polarizer):
"""Generates a constant polarization mask from py_pol Jones_matrix.
This is the most general function to obtain a polarizer.
Parameters:
polarizer (2x2 numpy.matrix): Jones_matrix
"""
if isinstance(polarizer, Jones_matrix):
M = polarizer.M
else:
M = polarizer
uno = np.ones_like(self.X, dtype=complex)
M = np.asarray(M)
self.M00 = uno * M[0, 0]
self.M01 = uno * M[0, 1]
self.M10 = uno * M[1, 0]
self.M11 = uno * M[1, 1]
[docs]
def polarizer_linear(self, azimuth=0 * degrees):
"""Generates an XY linear polarizer.
Parameters:
angle (float): linear polarizer angle
"""
PL = Jones_matrix('m0')
PL.diattenuator_perfect(azimuth=azimuth)
self.from_py_pol(PL)
[docs]
def quarter_waveplate(self, azimuth=0 * degrees):
"""Generates an XY quarter wave plate.
Parameters:
azimuth (float): polarizer angle
"""
PL = Jones_matrix('m0')
PL.quarter_waveplate(azimuth=azimuth)
self.from_py_pol(PL)
[docs]
def half_waveplate(self, azimuth=0 * degrees):
"""Generates an XY half wave plate.
Parameters:
azimuth (float): polarizer angle
"""
PL = Jones_matrix('m0')
PL.half_waveplate(azimuth=azimuth)
self.from_py_pol(PL)
[docs]
def polarizer_retarder(self,
R=0 * degrees,
p1=1,
p2=1,
azimuth=0 * degrees):
"""Generates an XY retarder.
Parameters:
R (float): retardance between Ex and Ey components.
p1 (float): transmittance of fast axis.
p2 (float): transmittance of slow axis.
azimuth (float): linear polarizer angle
"""
PL = Jones_matrix('m0')
PL.diattenuator_retarder_linear(R=R, p1=p1, p2=p2, azimuth=azimuth)
self.from_py_pol(PL)
[docs]
def to_py_pol(self):
"""Pass mask to py_pol.jones_matrix
Returns:
py_pol.jones_matrix
"""
m0 = Jones_matrix(name="from Diffractio")
m0.from_components((self.M00, self.M01, self.M10, self.M11))
m0.shape = self.M00.shape
return m0
[docs]
def draw(self, kind='amplitudes', range_scale='um'):
"""Draws the mask. It must be different to sources.
Parameters:
kind (str): 'amplitudes', 'phases', 'jones', 'jones_ap'.
'jones' is for real and imaginary parts.
'jones_ap' is for amplitud and phase.
"""
from matplotlib import rcParams
# def draw_masks(self, kind='fields'):
xsize, ysize = rcParams['figure.figsize']
extension = np.array([self.x[0], self.x[-1], self.y[0], self.y[-1]])
if range_scale == 'mm':
extension = extension / 1000.
a00, int00, phase00 = field_parameters(self.M00,
has_amplitude_sign=False)
a01, int01, phase01 = field_parameters(self.M01,
has_amplitude_sign=False)
a10, int10, phase10 = field_parameters(self.M10,
has_amplitude_sign=False)
a11, int11, phase11 = field_parameters(self.M11,
has_amplitude_sign=False)
a_max = np.abs((a00, a01, a10, a11)).max()
if kind in ('amplitudes', 'jones_ap'):
plt.set_cmap(CONF_DRAWING['color_intensity'])
fig, axs = plt.subplots(2,
2,
sharex='col',
sharey='row',
gridspec_kw={
'hspace': 0.25,
'wspace': 0.025
})
fig.set_figwidth(xsize)
fig.set_figheight(1.25 * ysize)
im1 = axs[0, 0].imshow(a00, extent=extension, origin='lower')
im1.set_clim(0, a_max)
axs[0, 0].set_title("J00")
im1 = axs[0, 1].imshow(a01, extent=extension, origin='lower')
im1.set_clim(0, a_max)
axs[0, 1].set_title("J01")
im1 = axs[1, 0].imshow(a10, extent=extension, origin='lower')
im1.set_clim(0, a_max)
axs[1, 0].set_title("J10")
im1 = axs[1, 1].imshow(a11, extent=extension, origin='lower')
im1.set_clim(0, a_max)
axs[1, 1].set_title("J11")
plt.suptitle("amplitudes", fontsize=15)
cax = plt.axes([0.89, 0.2, 0.03, 0.6])
cbar = plt.colorbar(im1, cax=cax, shrink=0.66)
cbar.set_ticks([0, 0.25, 0.5, 0.75, 1.0])
if range_scale == 'um':
axs[1, 0].set_xlabel(r'x ($\mu$m)')
axs[1, 0].set_ylabel(r'y($\mu$m)')
elif range_scale == 'mm':
axs[1, 0].set_xlabel(r'x (mm)')
axs[1, 0].set_ylabel(r'y (mm)')
if kind in ('phases', 'jones_ap'):
plt.set_cmap(CONF_DRAWING['color_phase'])
fig, axs = plt.subplots(2,
2,
sharex='col',
sharey='row',
gridspec_kw={
'hspace': 0.25,
'wspace': 0.025
})
fig.set_figwidth(xsize)
fig.set_figheight(1.25 * ysize)
im1 = axs[0, 0].imshow(np.angle(self.M00) / degrees,
extent=extension,
origin='lower')
im1.set_clim(-180, 180)
axs[0, 0].set_title("J00")
im1 = axs[0, 1].imshow(np.angle(self.M01) / degrees,
extent=extension,
origin='lower')
im1.set_clim(-180, 180)
axs[0, 1].set_title("J01")
im1 = axs[1, 0].imshow(np.angle(self.M10) / degrees,
extent=extension,
origin='lower')
im1.set_clim(-180, 180)
axs[1, 0].set_title("J10")
im1 = axs[1, 1].imshow(np.angle(self.M11) / degrees,
extent=extension,
origin='lower')
im1.set_clim(-180, 180)
axs[1, 1].set_title("J12")
plt.suptitle("phases", fontsize=15)
cax = plt.axes([.89, 0.2, 0.03, 0.6])
cbar = plt.colorbar(im1, cax=cax, shrink=0.66)
cbar.set_ticks([-180, -135, -90, -45, 0, 45, 90, 135, 180])
if range_scale == 'um':
axs[1, 0].set_xlabel(r'x ($\mu$m)')
axs[1, 0].set_ylabel(r'y($\mu$m)')
elif range_scale == 'mm':
axs[1, 0].set_xlabel(r'x (mm)')
axs[1, 0].set_ylabel(r'y (mm)')
if kind in ('jones'):
plt.set_cmap(CONF_DRAWING['color_stokes'])
fig, axs = plt.subplots(2,
2,
sharex='col',
sharey='row',
gridspec_kw={
'hspace': 0.25,
'wspace': 0.025
})
fig.set_figwidth(xsize)
fig.set_figheight(1.25 * ysize)
im1 = axs[0, 0].imshow(np.real(self.M00),
extent=extension,
origin='lower')
im1.set_clim(-1, 1)
axs[0, 0].set_title("J00")
im1 = axs[0, 1].imshow(np.real(self.M01),
extent=extension,
origin='lower')
im1.set_clim(-1, 1)
axs[0, 1].set_title("J01")
im1 = axs[1, 0].imshow(np.real(self.M10),
extent=extension,
origin='lower')
im1.set_clim(-1, 1)
axs[1, 0].set_title("J10")
im1 = axs[1, 1].imshow(np.real(self.M11),
extent=extension,
origin='lower')
im1.set_clim(-1, 1)
axs[1, 1].set_title("J11")
#intensity_max = np.real(self.M00.max())
plt.tight_layout()
plt.suptitle("$\Re$ (Jones)", fontsize=15)
cax = plt.axes([0.89, 0.2, 0.03, 0.6])
cbar = plt.colorbar(im1, cax=cax, shrink=0.66)
fig, axs = plt.subplots(2,
2,
sharex='col',
sharey='row',
gridspec_kw={
'hspace': 0.25,
'wspace': 0.025
})
fig.set_figwidth(xsize)
fig.set_figheight(1.25 * ysize)
im1 = axs[0, 0].imshow(np.imag(self.M00),
extent=extension,
origin='lower')
im1.set_clim(-1, 1)
axs[0, 0].set_title("J00")
im1 = axs[0, 1].imshow(np.imag(self.M01),
extent=extension,
origin='lower')
im1.set_clim(-1, 1)
axs[0, 1].set_title("J01")
im1 = axs[1, 0].imshow(np.imag(self.M10),
extent=extension,
origin='lower')
im1.set_clim(-1, 1)
axs[1, 0].set_title("J10")
im1 = axs[1, 1].imshow(np.imag(self.M11),
extent=extension,
origin='lower')
im1.set_clim(-1, 1)
axs[1, 1].set_title("J11")
#intensity_max = np.real(self.M00.max())
plt.tight_layout()
plt.suptitle("$\Im$ (Jones)", fontsize=15)
cax = plt.axes([0.89, 0.2, 0.03, 0.6])
cbar = plt.colorbar(im1, cax=cax, shrink=0.66)
[docs]
def rotation_matrix_Jones(angle):
"""Creates an array of Jones 2x2 rotation matrices.
Parameters:
angle (np.array): array of angle of rotation, in radians.
Returns:
numpy.array: 2x2 matrix
"""
M = np.array([[np.cos(angle), np.sin(angle)],
[-np.sin(angle), np.cos(angle)]])
return M