"""
Represents a Mueller matrix.
See, e.g., https://en.wikipedia.org/wiki/Mueller_calculus
"""
import numpy
from crystalpy.util.StokesVector import StokesVector
[docs]class MuellerMatrix(object):
"""Constructor.
Parameters
----------
matrix :
Matrix as a numpy array (4,4).
"""
def __init__(self, matrix=numpy.zeros((4,4)) ):
self.matrix = matrix
[docs] @classmethod
def initialize_as_general_linear_polarizer(cls,theta=0.0):
"""Creates a MuellerMatrix instance with a linear polarized.
Parameters
----------
theta : float, optional
The the angle of the fast axis in rad. (Default value = 0.0)
"""
mm = MuellerMatrix()
mm.set_general_linear_polarizer(theta)
return mm
[docs] @classmethod
def initialize_as_linear_polarizer_horizontal(cls):
"""Creates a MuellerMatrix instance with a horizontal linear polarized."""
return cls.initialize_as_general_linear_polarizer(0.0)
[docs] @classmethod
def initialize_as_linear_polarizer_vertical(cls):
"""Creates a MuellerMatrix instance with a vertical linear polarized."""
return cls.initialize_as_general_linear_polarizer(numpy.pi/2)
[docs] @classmethod
def initialize_as_linear_polarizer_plus45(cls):
"""Creates a MuellerMatrix instance with a +45 deg linear polarized."""
return cls.initialize_as_general_linear_polarizer(numpy.pi/4)
[docs] @classmethod
def initialize_as_linear_polarizer_minus45(cls):
"""Creates a MuellerMatrix instance with a -45 deg linear polarized."""
return cls.initialize_as_general_linear_polarizer(-numpy.pi/4)
[docs] @classmethod
def initialize_as_general_linear_retarder(cls,theta=0.0, delta=0.0):
"""Creates a MuellerMatrix instance with a phase retarder.
Parameters
----------
theta : float, optional
The the angle of the fast axis in rad. (Default value = 0.0)
delta : float, optional
The phase difference between the fast and slow axis in rad. (Default value = 0.0)
"""
mm = MuellerMatrix()
mm.set_general_linear_retarder(theta,delta)
return mm
[docs] @classmethod
def initialize_as_quarter_wave_plate_fast_vertical(cls):
"""Creare a MuellerMatrix instance with a quarter wave plate with fast axis vertical."""
return cls.initialize_as_general_linear_retarder(numpy.pi/2,-numpy.pi/2)
[docs] @classmethod
def initialize_as_quarter_wave_plate_fast_horizontal(cls):
"""Creare a MuellerMatrix instance with a quarter wave plate with fast axis horizontal."""
return cls.initialize_as_general_linear_retarder(0.0,-numpy.pi/2)
[docs] @classmethod
def initialize_as_half_wave_plate(cls):
"""Creare a MuellerMatrix instance with a half wave plate."""
return cls.initialize_as_general_linear_retarder(0.0,numpy.pi)
[docs] @classmethod
def initialize_as_ideal_mirror(cls):
"""Creare a MuellerMatrix instance with a quarter wave plate with an ideal mirror."""
return cls.initialize_as_general_linear_retarder(0.0,numpy.pi)
[docs] @classmethod
def initialize_as_filter(cls,transmission=1.0):
"""Creare a MuellerMatrix instance with a quarter wave plate with a filter.
Parameters
----------
transmission : float, optional
The transmission value. (Default value = 1.0)
Returns
-------
"""
return cls.initialize_as_general_linear_retarder(0.0,0.0).matrix_by_scalar(transmission)
[docs] def from_matrix_to_elements(self, return_numpy=True):
"""Returns a list of flatten numpy array with the elements of a given matrix.
If a list is needed one can use the numpy.array.tolist() method.
Parameters
----------
return_numpy : boolean, optional
if True returns numpy.ndarray, if False returns list. (Default value = True)
Returns
-------
numpy array or list
[m00, m01, m02....mN0, mN1, mN2...]
"""
matrix = numpy.asarray(self.matrix)
result = matrix.flatten()
if return_numpy:
return result
return list(result)
[docs] def get_matrix(self):
"""Returns the muller matric (reference, not copied)."""
return self.matrix
[docs] def matrix_by_scalar(self, scalar):
"""Multiplies the matrix by a scalar.
Parameters
----------
scalar :
the scalar factor.
Returns
-------
MullerMatric instance
the new Mueller matrix.
"""
new_mueller_matrix = self.matrix * scalar
return MuellerMatrix(new_mueller_matrix)
[docs] def matrix_by_vector(self, vector, return_numpy=True):
"""Multiplies the matrix by a vector.
Parameters
----------
vector : numpy array
the vector factor.
return_numpy :
if True returns numpy.ndarray, if False returns list. (Default value = True)
Returns
-------
numpy array
matrix * vector.
"""
matrix = numpy.asarray(self.matrix)
result = numpy.dot(matrix, vector)
if return_numpy:
return result
return list(result)
[docs] def vector_by_matrix(self, vector, return_numpy=True):
"""Multiplies a vector by the Muller matrix.
Parameters
----------
vector : numpy array
the vector factor.
return_numpy : boolean, optional
if True returns numpy.ndarray, if False returns list. (Default value = True)
Returns
-------
numpy array
vector * matrix.
"""
matrix = numpy.asarray(self.matrix)
result = numpy.dot(vector, matrix)
if return_numpy:
return result
return list(result)
[docs] def mueller_times_mueller(self, matrix_2, mod=False):
"""Multiplies two Mueller matrices.
Parameters
----------
matrix_2 :
Mueller matrix factor.
mod : boolean, optional
matrix multiplication is not commutative
-> mod controls which of the two matrices is the first factor. (Default value = False, matrix_2 * mueller)
Returns
-------
MuellerMatrix instance
matrix * matrix_2 if mof=True
matrix_2 * matrix if mof=false
"""
matrix_1 = self.matrix
if mod:
product = numpy.dot(matrix_1, matrix_2)
else:
product = numpy.dot(matrix_2, matrix_1)
return MuellerMatrix(product)
def __eq__(self, candidate):
for i in range(4):
for j in range(4):
if self.matrix[i, j] != candidate.matrix[i, j]:
return False
return True
def __ne__(self, candidate):
return not self == candidate
[docs] def set_general_linear_polarizer(self, theta):
"""Sets the Muller matrix as a linear polarizer. See [rt]_.
Parameters
----------
theta : float
the angle of the fast axis in rad
References
----------
.. [rt] https://en.wikipedia.org/wiki/Mueller_calculus
"""
# First row.
self.matrix[0, 0] = 0.5
self.matrix[0, 1] = 0.5 * numpy.cos(2*theta)
self.matrix[0, 2] = 0.5 * numpy.sin(2*theta)
self.matrix[0, 3] = 0.0
# Second row.
self.matrix[1, 0] = 0.5 * numpy.cos(2*theta)
self.matrix[1, 1] = 0.5 * (numpy.cos(2*theta))**2
self.matrix[1, 2] = 0.5 * numpy.sin(2*theta) * numpy.cos(2*theta)
self.matrix[1, 3] = 0.0
# Third row.
self.matrix[2, 0] = 0.5 * numpy.sin(2*theta)
self.matrix[2, 1] = 0.5 * numpy.sin(2*theta) * numpy.cos(2*theta)
self.matrix[2, 2] = 0.5 * (numpy.sin(2*theta))**2
self.matrix[2, 3] = 0.0
# Fourth row.
self.matrix[3, 0] = 0.0
self.matrix[3, 1] = 0.0
self.matrix[3, 2] = 0.0
self.matrix[3, 3] = 0.0
[docs] def set_general_linear_retarder(self, theta, delta=0.0):
"""Sets the Muller matrix as a generic line retarder. See [rg]_.
Parameters
----------
theta : float
angle of fast axis in rad
delta :
phase difference in rad between the fast and slow axis in rad (Default value = 0.0)
References
----------
.. [rg] https://en.wikipedia.org/wiki/Mueller_calculus
"""
# First row.
self.matrix[0, 0] = 1.0
self.matrix[0, 1] = 0.0
self.matrix[0, 2] = 0.0
self.matrix[0, 3] = 0.0
# Second row. (numpy.cos(2*theta))**2 (numpy.sin(2*theta))**2 (numpy.cos(delta))**2 (numpy.sin(delta))**2
self.matrix[1, 0] = 0.0
self.matrix[1, 1] = (numpy.cos(2*theta))**2 + numpy.cos(delta) * (numpy.sin(2*theta))**2
self.matrix[1, 2] = numpy.cos(2*theta) * numpy.sin(2*theta) - numpy.cos(2*theta) * numpy.cos(delta) * numpy.sin(2*theta)
self.matrix[1, 3] = numpy.sin(2*theta) * numpy.sin(delta)
# Third row.
self.matrix[2, 0] = 0.0
self.matrix[2, 1] = numpy.cos(2*theta) * numpy.sin(2*theta) - numpy.cos(2*theta) * numpy.cos(delta) * numpy.sin(2*theta)
self.matrix[2, 2] = numpy.cos(delta) * (numpy.cos(2*theta))**2 + (numpy.sin(2*theta))**2
self.matrix[2, 3] = -numpy.cos(2*theta) * numpy.sin(delta)
# Fourth row.
self.matrix[3, 0] = 0.0
self.matrix[3, 1] = -numpy.sin(2*theta) * numpy.sin(delta)
self.matrix[3, 2] = numpy.cos(2*theta) * numpy.sin(delta)
self.matrix[3, 3] = numpy.cos(delta)
[docs] def calculate_stokes_vector(self, incoming_stokes_vector):
"""Takes an incoming Stokes vector, multiplies it by a Mueller matrix and gives an outgoing Stokes vector as a result.
Parameters
----------
incoming_stokes_vector : StokesVector instance
The incoming vector.
Returns
-------
StokesVector instance
The resulting stokes vector.
See Also
--------
crystalpy.util.StokesVector.StokesVector
"""
# incoming_stokes_vector = self.incoming_stokes_vector.get_array() # Stokes vector.
element_list = self.matrix_by_vector(incoming_stokes_vector.getList())
outgoing_stokes_vector = StokesVector(element_list)
return outgoing_stokes_vector