Source code for xopto.mcbase.mcutil.fiber

# -*- coding: utf-8 -*-
################################ Begin license #################################
# Copyright (C) Laboratory of Imaging technologies,
#               Faculty of Electrical Engineering,
#               University of Ljubljana.
#
# This file is part of PyXOpto.
#
# PyXOpto is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# PyXOpto is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with PyXOpto. If not, see <https://www.gnu.org/licenses/>.
################################# End license ##################################

from typing import Tuple

import numpy as np

from xopto.mcbase.mcutil.lut import EmissionLut, CollectionLut
from xopto.mcbase import cltypes
from xopto.mcbase import mcobject
from xopto.mcbase import mctypes


[docs]class MultimodeFiber: ''' Class representing Multimode step-index fibers with an ideal emission and collection characteristics defined by the numerical aperture. '''
[docs] @staticmethod def cl_type(mc: mcobject.McObject) -> cltypes.Structure: T = mc.types class ClMultimodeFiber(cltypes.Structure): _field_ = [ ('ncore', T.mc_fp_t), ('ncladding', T.mc_fp_t), ('rcore', T.mc_fp_t), ('rcladding', T.mc_fp_t), ('na', T.mc_fp_t), ] return ClMultimodeFiber
[docs] @staticmethod def compute_na(ncore: float, ncladding: float) -> float: ''' Computes the numerical aperture of the fiber core given the refractive indices of the fiber core and fiber cladding. Parameters ---------- ncore: float Refractive index of the fiber core. ncladding: float Refractive index of the fiber cladding. Returns ------- na: float Numerical aperture of the fiber core. ''' return (ncore**2 - ncladding**2)**0.5
[docs] @staticmethod def compute_ncladding(ncore: float, na: float) -> float: ''' Computes the refractive index of the fiber cladding given the refractive index and numerical aperture of the fiber core. Parameters ---------- ncore: float Refractive index of the fiber core. na: float Numerical aperture of the fiber core. Returns ------- ncladding: float Refractive index of the fiber cladding. ''' return (ncore**2 - na**2)**0.5
def __init__(self, dcore: float, dcladding: float, ncore: float, na: float): ''' Multimode fiber constructor. Parameters ---------- dcore: float Diameter (m) of the fiber core. dcladding: float Diameter (m) of the fiber cladding. ncore: float Refractive index of the fiber core. na: float Numerical aperture of the fiber core. ''' self._dcore = float(dcore) self._dcladding = float(dcladding) self._ncore = float(ncore) self._na = float(na)
[docs] def validate(self): ''' Check the integrity of the object parameters. Raises ValueError. ''' if self._dcore > self._dcladding: raise ValueError( 'Fiber core diameter must be smaller than the fiber ' 'cladding diameter!')
def _get_dcore(self) -> float: return self._dcore def _set_dcore(self, value: float): self._dcore = float(value) dcore = property(_get_dcore, _set_dcore, None, 'Diameter (m) of the fiber core.') def _get_dcladding(self) -> float: return self._dcladding def _set_dcladding(self, value: float): self._dcladding = float(value) dcladding = property(_get_dcladding, _set_dcladding, None, 'Diameter (m) of the fiber cladding.') def _get_ncore(self) -> float: return self._ncore def _set_ncore(self, value: float): self._ncore = float(value) ncore = property(_get_ncore, _set_ncore, None, 'Refractive index of the fiber core.') def _get_ncladding(self) -> float: return MultimodeFiber.compute_ncladding(self._ncore, self._na) ncladding = property(_get_ncladding, None, None, 'Refractive index of the fiber cladding computed as ' '$\\sqrt(ncore^2 - na^2)$') def _get_na(self) -> float: return self._na def _set_na(self, value: float): self._na = float(value) na = property(_get_na, _set_na, None, 'Numerical aperture (NA) of the fiber core.')
[docs] def cl_pack(self, mc: mcobject.McObject, target: cltypes.Structure = None) \ -> cltypes.Structure: ''' Fills the OpenCL Structure (target) with the data required by the Monte Carlo simulator. See the :py:meth:`~MultimodeFiber.cl_type` method for a detailed list of fields. Parameters ---------- mc: McObject Simulator instance. target: cltypes.Structure Target OpenCL structure for packing. Returns ------- target: cltypes.Structure Target structure received as an input argument or a new instance of ClMultimodeFiber if the input argument target is None. ''' if target is None: target_type = self.cl_type() target = target_type() target.ncore = self.ncore target.ncladding = self.ncladding target.rcore = self.dcore*0.5 target.dcladding = self.dcladding*0.5 target.na = self.na return target
[docs] def todict(self) -> dict: ''' Export object to a dict. ''' return {'dcore': self._dcore, 'dcladding': self._dcladding, 'ncore': self._ncore, 'na': self._na, 'type': self.__class__.__name__}
[docs] @staticmethod def fromdict(data: dict) -> 'MultimodeFiber': ''' Create an accumulator instance from a dictionary. Parameters ---------- data: dict Dictionary created by the py:meth:`MultimodeFiber.todict` method. ''' obj_type = data.pop('type') if obj_type != 'MultimodeFiber': raise TypeError('Expected "MultimodeFiber" type but ' 'got "{}"!'.format(obj_type)) return MultimodeFiber(**data)
def __repr__(self): return 'MultimodeFiber(dcore={:f}, dcladding={:f}, ' \ 'ncore={:f}, na={:f})'.format( self._dcore, self._dcladding, self._ncore, self._na) def __str__(self): return self.__repr__()
[docs]class MultimodeFiberLut: ''' Class representing multimode fibers with arbitrary emission and collection characteristics defined by a linear lookup table. '''
[docs] @staticmethod def compute_na(ncore: float, ncladding: float) -> float: ''' Computes the numerical aperture of the fiber core given the refractive indices of the fiber core and fiber cladding. Parameters ---------- ncore: float Refractive index of the fiber core. ncladding: float Refractive index of the fiber cladding. Returns ------- na: float Numerical aperture of the fiber core. ''' return (ncore**2 - ncladding**2)**0.5
[docs] @staticmethod def compute_ncladding(ncore: float, na: float) -> float: ''' Computes the refractive index of the fiber cladding given the refractive index and numerical aperture of the fiber core. Parameters ---------- ncore: float Refractive index of the fiber core. na: float Numerical aperture of the fiber core. Returns ------- ncladding: float Refractive index of the fiber cladding. ''' return (ncore**2 - na**2)**0.5
def __init__(self, dcore: float, dcladding: float, ncore: float, ncladding: float, emission: EmissionLut=None, collection: CollectionLut=None): ''' Multimode fiber constructor. Parameters ---------- dcore: float Diameter (m) of the fiber core. dcladding: float Diameter (m) of the fiber cladding. ncore: float Refractive index of the fiber core. ncladding: float Refractive index of the fiber cladding. emission: EmissionLut or np.ndarray or str Lookup table for numerical sampling of the emission angles. Can be an instance of EmissionLut or a file from which to load the EmissionLut. collection: CollectionLut or str Collection sensitivity/efficiency of the fiber defined by a linear lookup table array. Can be an instance of CollectionLut or a file from which to load the CollectionLut. ''' self._dcore = float(dcore) self._dcladding = float(dcladding) self._ncore = float(ncore) if ncladding is None: self._ncladding = self._ncore if isinstance(emission, str): emission = EmissionLut.fromfile(emission) elif isinstance(emission, np.ndarray): emission = EmissionLut(emission) if isinstance(collection, str): collection = CollectionLut.fromfile(collection) self._emission_lut = emission self._collection_lut = collection
[docs] def validate(self): ''' Check the integrity of parameters. Raises a ValueError. ''' if self._dcore > self._dcladding: raise ValueError( 'Fiber core diameter must be smaller than the fiber ' 'cladding diameter!')
def _get_dcore(self) -> float: return self._dcore def _set_dcore(self, value:float): self._dcore = float(value) dcore = property(_get_dcore, _set_dcore, None, 'Diameter (m) of the fiber core.') def _get_dcladding(self) -> float: return self._dcladding def _set_dcladding(self, value: float): self._dcladding = float(value) dcladding = property(_get_dcladding, _set_dcladding, None, 'Diameter (m) of the fiber cladding.') def _get_ncore(self) -> float: return self._ncore def _set_ncore(self, value: float): self._ncore = float(value) ncore = property(_get_ncore, _set_ncore, None, 'Refractive index of the fiber core.') def _get_ncladding(self) -> float: return self._ncladding ncladding = property(_get_ncladding, None, None, 'Refractive index of the fiber cladding computed as ' '$\\sqrt(ncore^2 - na^2)$') def _get_emission_lut(self) -> EmissionLut: return self._emission_lut def _set_emission_lut(self, value: EmissionLut): self._nemission_lut = EmissionLut(value) emission = property(_get_emission_lut, _set_emission_lut, None, 'Emission lookup table.') def _get_collection_lut(self) -> CollectionLut: return self._collection_lut def _set_collection_lut(self, value: CollectionLut): self._collection_lut = CollectionLut(value) collection = property(_get_collection_lut, _set_collection_lut, None, 'Collection lookup table.')
[docs] def todict(self) -> dict: ''' Export object to a dict. ''' return {'dcore': self._dcore, 'dcladding': self._dcladding, 'ncore': self._ncore, 'emission': self._emission_lut, 'collection': self._collection_lut, 'type': self.__class__.__name__}
def __repr__(self): return 'MultimodeFiberLut(dcore={:f}, dcladding={:f}, ' \ 'ncore={:f}, emission={}, collection={})'.format( self._dcore, self._dcladding, self._ncore, self._emission_lut, self._collection_lut) def __str__(self): return self.__repr__()
[docs]class FiberLayout: def __init__(self, fiber: MultimodeFiber or MultimodeFiberLut or 'FiberLayout', position: Tuple[float, float, float] = (0.0, 0.0, 0.0), direction: Tuple[float, float, float] = (0.0, 0.0 , 1.0)): ''' Optical fiber layout constructor. Parameters ---------- fiber: MultimodeFiber or MultimodeFiberLut or FiberLayout A multimode optical fiber instance or a fiber layout instance. If a fiber layout instance, a new copy is created. position: tuple(float, float, float) Position of the fiber tip center. direction: Direction/orientation of the fiber. -------- ''' if isinstance(fiber, FiberLayout): af = fiber fiber = af.fiber position = af.position direction = af.direction self._fiber = fiber self._position = np.zeros((3,)) self._direction = np.array((0.0, 0.0, 1.0)) self._set_position(position) self._set_direction(direction) def _get_fiber(self) -> MultimodeFiber or MultimodeFiberLut: return self._fiber def _set_fiber(self, value: MultimodeFiber or MultimodeFiberLut): self._fiber = value fiber = property(_get_fiber, _set_fiber, None, 'Properties of the optical fiber.') def _get_position(self) -> Tuple[float, float, float]: return self._position def _set_position(self, value: float or Tuple[float, float] or Tuple[float, float, float]): if hasattr(value, '__len__'): self._position[:len(value)] = value else: self._position[:] = value position = property(_get_position, _set_position, None, 'Position of the fiber as a tuple (x, y, z).') def _get_direction(self) -> Tuple[float, float, float]: return self._direction def _set_direction(self, direction: Tuple[float, float, float]): self._direction[:] = direction norm = np.linalg.norm(self._direction) if norm == 0.0: raise ValueError( 'Fiber direction vector norm/length must not be 0!') self._direction *= 1.0/norm direction = property(_get_direction, _set_direction, None, 'Fiber reference direction.')
[docs] def todict(self) -> dict: ''' Export object to a dict. ''' return {'fiber': self._fiber.todict(), 'position': self._position.tolist(), 'direction': self._direction.tolist(), 'type': self.__class__.__name__}
[docs] @staticmethod def fromdict(data: dict) -> 'MultimodeFiber': ''' Create an accumulator instance from a dictionary. Parameters ---------- data: dict Dictionary created by the py:meth:`FiberLayout.todict` method. ''' obj_type = data.pop('type') if obj_type != 'FiberLayout': raise TypeError('Expected "FiberLayout" type but ' 'got "{}"!'.format(obj_type)) return FiberLayout(fiber=MultimodeFiber(data.pop('fiber')), **data)
def __str__(self): return 'FiberLayout(fiber={}, position=({}, {}, {}), ' \ 'direction=({}, {}, {}))'.format( self._fiber, *self._position, *self._direction) def __repr__(self): return '{} #{}'.format(self.__str__(), id(self))