Source code for fibermodes.simulator.simulator

# This file is part of FiberModes.
#
# FiberModes 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.
#
# FiberModes 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 FiberModes.  If not, see <http://www.gnu.org/licenses/>.

"""The simulator allows to link fibers with wavelengths, to simulate
an array of parameters.

"""

from fibermodes import FiberFactory, Wavelength, Mode, ModeFamily
from fibermodes.slrc import SLRC
from functools import reduce, partial
import operator


class _FSimulator(object):

    def __init__(self, fiber, wavelengths,
                 numax, mmax, vectorial, scalar, delta):
        self._fiber = fiber
        self._wavelengths = wavelengths
        self._modes = None

        self._numax = numax
        self._mmax = mmax
        self._vectorial = vectorial
        self._scalar = scalar
        self._delta = delta

    def modes(self):
        if self._modes is None:
            numax = self._numax
            mmax = self._mmax
            self._modes = [set() for _ in self._wavelengths]
            for i, wl in enumerate(self._wavelengths):
                if self._vectorial:
                    self._modes[i] |= self._fiber.findVmodes(wl, numax, mmax)
                if self._scalar:
                    self._modes[i] |= self._fiber.findLPmodes(wl, numax, mmax)

                numax = max(m.nu for m in self._modes[i])
                mmax = [max((m.m for m in self._modes[i] if m.nu == nu),
                            default=0)
                        for nu in range(numax+1)]
        return self._modes

    def cutoff(self):
        co = [{} for _ in self._wavelengths]
        for i, wl in enumerate(self._wavelengths):
            for m in self.modes()[i]:
                co[i][m] = self._fiber.cutoff(m)
        return co

    def cutoffWl(self):
        co = {}
        for m in reduce(operator.or_, self.modes(), set()):
            co[m] = self._fiber.toWl(self._fiber.cutoff(m))

        cod = [{} for _ in self._wavelengths]
        for i, wl in enumerate(self._wavelengths):
            for m in self._modes[i]:
                cod[i][m] = co[m]
        return cod

    def _beta(self, p):
        r = [{} for _ in self._wavelengths]
        for i, wl in enumerate(self._wavelengths):
            for m in self.modes()[i]:
                lowbound = self._lowbound(m, i)
                r[i][m] = self._fiber.beta(wl.omega, m, p=p,
                                           delta=self._delta,
                                           lowbound=lowbound)
        return r

    def beta0(self):
        return self._beta(0)

    def beta1(self):
        return self._beta(1)

    def beta2(self):
        return self._beta(2)

    def beta3(self):
        return self._beta(3)

    def _neff(self, mode, wlidx):
        lowbound = self._lowbound(mode, wlidx)
        wl = self._wavelengths[wlidx]
        return self._fiber.neff(mode, wl, delta=self._delta, lowbound=lowbound)

    def _lowbound(self, mode, i):
        wl = self._wavelengths[i]
        lowbound = max(layer.maxIndex(wl) for layer in self._fiber.layers)

        if i > 0:
            lowbound = min(lowbound, self._neff(mode, i-1))

        pm = None
        if mode.family is ModeFamily.EH:
            pm = Mode(ModeFamily.HE, mode.nu, mode.m)
        elif mode.m > 1:
            if mode.family is ModeFamily.HE:
                pm = Mode(ModeFamily.EH, mode.nu, mode.m - 1)
            else:
                pm = Mode(mode.family, mode.nu, mode.m - 1)
        if pm is not None:
            lowbound = min(lowbound, self._neff(pm, i))

        if (mode.family is ModeFamily.LP and mode.nu > 0):  # or mode.nu > 1:
            pm = Mode(mode.family, mode.nu-1, mode.m)
            lowbound = min(lowbound, self._neff(pm, i))

        return lowbound

    def _apply_fct(self, fct):
        r = [{} for _ in self._wavelengths]
        for i, wl in enumerate(self._wavelengths):
            for m in self.modes()[i]:
                lowbound = self._lowbound(m, i)
                r[i][m] = fct(m, wl, delta=self._delta, lowbound=lowbound)
        return r

    def __getattr__(self, name):
        if name[0] != '_':
            fct = getattr(self._fiber, name)
            return partial(self._apply_fct, fct)
        return getattr(super(), name)


[docs]class Simulator(object): """The Simulator links :py:class:`~fibermodes.fiber.factory.FiberFactory` with a list of wavelengths, and provides a convenient way to compute a range of modal properties. Args: factory(:py:class:`~fibermodes.fiber.factory.FiberFactory`): A FiberFactory object. wavelengths(list): A list of wavelengths. numax(int): Maximum nu parameter used when finding modes, or None to find all modes. mmax(int): Maximum m parameter used when finding modes, or None to find all modes. vectorial(bool): Find vector modes. scalar(bool): Find scalar modes. delta(float): Delta parameter used for mode solver (smaller is mode precise, bigger is faster). clone(Simulator): Simulator object to clone. """ def __init__(self, factory=None, wavelengths=None, numax=None, mmax=None, vectorial=True, scalar=False, delta=1e-6, clone=None): if clone is not None: self._fibers = clone._fibers self._wavelengths = clone._wavelengths self._numax = clone._numax self._mmax = clone._mmax self._vectorial = clone._vectorial self._scalar = clone._scalar self.delta = clone.delta self.factory = clone.factory else: self._fibers = None self._fsims = None self._wavelengths = None self._numax = numax self._mmax = mmax self._vectorial = vectorial self._scalar = scalar self.delta = delta self.set_factory(factory) if wavelengths is not None: self.set_wavelengths(wavelengths) self._build_fsims() def _build_fsims(self): if self.initialized: self._fsims = tuple(_FSimulator(fiber, self._wavelengths, self.numax, self.mmax, self.vectorial, self.scalar, self.delta) for fiber in self._fibers)
[docs] def set_wavelengths(self, value): """Set the list of wavelengths. It can be used if this was not done in the constructor, or to modify the current list of wavelengths. Args: value(list): List of wavelengths (in meters) """ self._wavelengths = tuple(Wavelength(w) for w in iter(SLRC(value))) self._build_fsims()
[docs] def set_factory(self, factory): """Set the FiberFactory. It can be used if this was not done in the constructor, or to modify the current FiberFactory. Args: factory(FiberFactory): FiberFactory object to use in simulator. """ if isinstance(factory, str): factory = FiberFactory(factory) self.factory = factory if factory is not None: self._fibers = tuple(iter(factory)) self._build_fsims()
@property def fibers(self): """List of fibers, generated from the FiberFactory. Raises: ValueError: No FiberFactory was initialized. """ if self._fibers is None: raise ValueError("Object not initialized. You must call " "set_factory first.") return self._fibers @property def wavelengths(self): """List of wavelengths. This list always is sorted. Raises: ValueError: List of wavelengths was not initialized. """ if self._wavelengths is None: raise ValueError("Object not initialized. You must call " "set_wavelengths first.") return self._wavelengths @property def numax(self): """Maximum nu when finding modes.""" return self._numax @numax.setter def numax(self, value): self._numax = value self._build_fsims() @property def mmax(self): """Maximum m when finding modes.""" return self._mmax @mmax.setter def mmax(self, value): self._mmax = value self._build_fsims() @property def vectorial(self): """Whether to search for vector modes.""" return self._vectorial @vectorial.setter def vectorial(self, value): self._vectorial = value self._build_fsims() @property def scalar(self): """Whether to search for scalar modes.""" return self._scalar @scalar.setter def scalar(self, value): self._scalar = value self._build_fsims() @property def initialized(self): """Whether FiberFactory and wavelengths are set.""" return not (self._fibers is None or self._wavelengths is None) def __getattr__(self, name): def wrapper(): for fsim in self._fsims: fct = getattr(fsim, name) yield fct() return wrapper