def __init__(self, name: str = default_name, channels: int = 1, center_lambda: List[float] = [cst.DEF_LAMBDA], peak_power: List[float] = [1e-3], total_power: Optional[List[float]] = None, offset_nu: List[float] = [0.0], init_phi: List[float] = [0.0], save: bool = False) -> None: r""" Parameters ---------- name : The name of the component. channels : The number of channels in the field. center_lambda : The center wavelength of the channels. :math:`[nm]` peak_power : Peak power of the pulses. :math:`[W]` total_power : Total power of the pulses. :math:`[W]` (peak_power will be ignored if total_power provided) offset_nu : The offset frequency. :math:`[THz]` init_phi : The initial phase of the pulses. save : If True, the last wave to enter/exit a port will be saved. """ # Parent constructor ------------------------------------------- ports_type = [cst.OPTI_OUT] super().__init__(name, default_name, ports_type, save) # Attr types check --------------------------------------------- util.check_attr_type(channels, 'channels', int) util.check_attr_type(center_lambda, 'center_lambda', float, list) util.check_attr_type(peak_power, 'peak_power', float, list) util.check_attr_type(total_power, 'total_power', None, float, list) util.check_attr_type(offset_nu, 'offset_nu', float, list) util.check_attr_type(init_phi, 'init_phi', float, list) # Attr --------------------------------------------------------- self.channels: int = channels self.center_lambda: List[float] = util.make_list( center_lambda, channels) self.peak_power: List[float] = util.make_list(peak_power, channels) self.total_power: List[float] = [] if (total_power is not None): self.total_power = util.make_list(total_power, channels) self.offset_nu: List[float] = util.make_list(offset_nu, channels) self.init_phi: List[float] = util.make_list(init_phi, channels)
def __init__(self, kappa: Optional[List[float]] = None, V: float = cst.V, a: float = cst.CORE_RADIUS, d: float = cst.C2C_SPACING, lambda_0: float = cst.DEF_LAMBDA, n_0: float = cst.REF_INDEX, omega: Optional[Array[float]] = None) -> None: r""" Parameters ---------- kappa : The coupling coefficients. :math:`[km^{-1}]` V : The fiber parameter. a : The core radius. :math:`[\mu m]` d : The center to center spacing between the two cores. :math:`[\mu m]` lambda_0 : The wavelength in the vacuum for the considered wave. :math:`[nm]` n_0 : The refractive index outside of the two fiber cores. omega : The angular frequency. :math:`[ps^{-1}]` """ super().__init__(omega) self._kappa: List[float] if (kappa is None): self._kappa = [Coupling.calc_kappa(V, a, d, lambda_0, n_0)] else: self._kappa = util.make_list(kappa)
def __init__(self, kappa: Optional[List[float]] = None, V: float = cst.V, a: float = cst.CORE_RADIUS, d: float = cst.C2C_SPACING, n_0: float = cst.REF_INDEX) -> None: r""" Parameters ---------- kappa : The coupling coefficients. :math:`[km^{-1}]` V : The fiber parameter. a : The core radius. :math:`[\mu m]` d : The center to center spacing between the two cores. :math:`[\mu m]` n_0 : The refractive index outside of the two fiber cores. """ super().__init__() self._kappa: List[float] self._predict: Optional[Callable] = None self._V: float = V self._a: float = a self._d: float = d self._n_0: float = n_0 if (kappa is not None): kappa = util.make_list(kappa) # make sure is list self._kappa = np.asarray(kappa).reshape((1,-1)) else: self._predict = self.get_kappa
def phase_shift(self, phase_shift: Union[List[float], List[Callable]]) -> None: phase_shift_ = util.make_list(phase_shift, 2, 0.0) self._phasemod_1 = IdealPhaseMod(name='nocount', phase_shift=phase_shift_[0]) self._phasemod_2 = IdealPhaseMod(name='nocount', phase_shift=phase_shift_[1])
def __init__(self, name: str = default_name, fields: Union[Field, List[Field]] = [], save: bool = False, pre_call_code: str = '', post_call_code: str = '') -> None: r""" Parameters ---------- name : The name of the component. fields : A field or a list of Field to launch into the simulation. pre_call_code : A string containing code which will be executed prior to the call to the function :func:`__call__`. The two parameters `input_ports` and `input_fields` are available. post_call_code : A string containing code which will be executed posterior to the call to the function :func:`__call__`. The two parameters `output_ports` and `output_fields` are available. """ # Parent constructor ------------------------------------------- ports_type = [cst.ANY_OUT] super().__init__(name, default_name, ports_type, save, pre_call_code=pre_call_code, post_call_code=post_call_code) # Attr types check --------------------------------------------- util.check_attr_type(fields, 'fields', Field, list) # Attr --------------------------------------------------------- self.fields: List[Field] = util.make_list(fields)
def __init__(self, kappa: Union[List[float], Callable], order_taylor: int = 1, start_taylor: int = 0, skip_taylor: List[int] = []) -> None: r""" Parameters ---------- kappa : The coupling coefficients. :math:`[km^{-1}]` If a callable is provided, variable must be angular frequency. :math:`[ps^{-1}]` order_taylor : The order of kappa coefficients Taylor series expansion to take into account. (will be set to the length of the kappa array if one is provided) start_taylor : The order of the derivative from which to start the Taylor series expansion. skip_taylor : The order_taylors of the derivative to not consider. """ super().__init__() self._order_taylor: int = order_taylor self._start_taylor: int = start_taylor self._skip_taylor: List[int] = skip_taylor # The coupling coefficient ------------------------------------- self._op: np.ndarray = np.array([]) self._kappa_op: np.ndarray = np.array([]) self._kappa: Callable if (callable(kappa)): self._kappa = kappa else: kappa_ = np.asarray(util.make_list(kappa)) self._kappa = lambda omega: util.hstack_like(kappa_, omega) self._order_taylor = len(kappa_) - 1
def transfer_function(time: np.ndarray, v_pi: List[float], v_bias: List[float], v_mod: List[Union[float, Callable]]) -> np.ndarray: v_pi = util.make_list(v_pi, 2) v_bias = util.make_list(v_bias, 2) v_mod = util.make_list(v_mod, 2) v_mod_: List[Callable] = [] for v in v_mod: if (callable(v)): v_mod_.append(v) else: v_mod_.append(lambda t: v) print(v_pi, v_bias, v_mod_) phase_shift = [ lambda t: cst.PI * (v_bias[0] + v_mod_[0](t)) / v_pi[0], lambda t: cst.PI * (v_bias[1] + v_mod_[1](t)) / v_pi[1] ] tf = np.cos((phase_shift[0](time) - phase_shift[1](time)) / 2.) return Field.temporal_power(tf)
def add_single_plot(plt_to_add, x_data, y_data, x_label, y_label, x_range, y_range, plot_title, plot_label, plot_linestyle, plot_color, opacity): if (y_data.ndim == 1): # not multidimentsional y_data = y_data.reshape((1, -1)) if (cst.AUTO_PAD_PLOT): x_data = np.asarray(x_data) x_data, y_data = util.auto_pad(x_data, y_data) multi_channel = len(y_data) > 1 labels_on_plot = plot_label is not None colors_on_plot = plot_color is not None if (multi_channel): plot_label = util.make_list(plot_label, len(y_data)) for i in range(len(y_data)): if (multi_channel): if (labels_on_plot): plot_label_temp = plot_label[i] + " (ch.{})".format(i) else: plot_label_temp = "channel {}".format(i) else: plot_label_temp = plot_label if (not colors_on_plot): if (labels_on_plot or multi_channel): plt_to_add.plot(x_data, y_data[i], ls=plot_linestyle, label=plot_label_temp) else: plt_to_add.plot(x_data, y_data[i], ls=plot_linestyle) plt_to_add.fill_between(x_data, y_data[i], alpha=opacity) else: if (labels_on_plot or multi_channel): plt_to_add.plot(x_data, y_data[i], ls=plot_linestyle, c=plot_color, label=plot_label_temp) else: plt_to_add.plot(x_data, y_data[i], ls=plot_linestyle, c=plot_color) plt_to_add.fill_between(x_data, y_data[i], alpha=opacity, facecolor=plot_color) add_subplot_para(plt_to_add, x_label, y_label, x_range, y_range, plot_title) if (labels_on_plot or multi_channel): plt_to_add.legend(loc="best")
def __init__( self, name: str = default_name, ratios_ports: List[List[float]] = [[0.5, 0.5]], max_nbr_pass: Optional[List[int]] = None, save: bool = False, ) -> None: """ Parameters ---------- name : The name of the component. ratios_ports : Each element of the list contain a list with the two dividing percentages for the two output ports. max_nbr_pass : The maximum number of times a field can enter at the corresponding index-number port. save : If True, the last wave to enter/exit a port will be saved. """ # Parent constructor ------------------------------------------- ports_type = [cst.ANY_ALL for i in range(4)] super().__init__(name, default_name, ports_type, save, max_nbr_pass=max_nbr_pass) # Attr types check --------------------------------------------- util.check_attr_type(ratios_ports, 'ratios_ports', list) # Attr --------------------------------------------------------- ratios_ports = util.make_list(ratios_ports, 4) # N.B. name='nocount' to avoid inc. default name counter self._divider_0 = IdealDivider(name='nocount', arms=2, divide=True, ratios=ratios_ports[0]) self._divider_1 = IdealDivider(name='nocount', arms=2, divide=True, ratios=ratios_ports[1]) self._divider_2 = IdealDivider(name='nocount', arms=2, divide=True, ratios=ratios_ports[2]) self._divider_3 = IdealDivider(name='nocount', arms=2, divide=True, ratios=ratios_ports[3])
def __init__(self, coeff: Union[List[float], Callable], order_taylor: int = 1, start_taylor: int = 0, skip_taylor: List[int] = [], UNI_OMEGA: bool = False) -> None: r""" Parameters ---------- coeff : The derivatives of the coefficients. order_taylor : The order of coeff coefficients Taylor series expansion to take into account. (will be set to the length of the coeff array if one is provided) start_taylor : The order of the derivative from which to start the Taylor series expansion. skip_taylor : The order_taylors of the derivative to not consider. UNI_OMEGA : If True, consider only the center omega for computation. Otherwise, considered omega discretization. """ super().__init__() self._UNI_OMEGA = UNI_OMEGA self._order_taylor: int = order_taylor self._start_taylor: int = start_taylor self._skip_taylor: List[int] = skip_taylor # The attenuation constant ------------------------------------- self._op: np.ndarray = np.array([]) self._coeff_op: np.ndarray = np.array([]) self._coeff: Union[np.ndarray, Callable] if (callable(coeff)): self._coeff = coeff else: self._coeff_values = np.asarray(util.make_list(coeff)) #fct2pickle = lambda omega, order: util.hstack_like(coeff_, omega) #self._coeff = CallableContainer(fct2pickle) self._coeff = self._hstack_like max_order_taylor: int = len(self._coeff_values) - 1 if (self._order_taylor > max_order_taylor): self._order_taylor = max_order_taylor warning_message = ( "The requested order is higher than the " "provided coefficients, max order of {} will be set.". format(max_order_taylor)) warnings.warn(warning_message, TaylorOrderWarning)
def add_2D_subplot(plt_to_add, x_data, y_data, x_label, y_label, x_range, y_range, plot_title, line_label, line_style, line_width, line_color, line_opacity): """Plot a 2D graph.""" x_data_ = np.array([x_data]) if (x_data.ndim < 2) else x_data y_data_ = np.array([y_data]) if (y_data.ndim < 2) else y_data x_data_ = util.modify_length_ndarray(x_data_, len(y_data_)) multi_channel = len(y_data_) > 1 labels_on_plot = line_label is not None colors_on_plot = line_color is not None if (multi_channel): line_label = util.make_list(line_label, len(y_data_)) for i in range(len(y_data_)): if (multi_channel): if (labels_on_plot): line_label_ = line_label[i] + " (ch.{})".format(i) else: line_label_ = "channel {}".format(i) else: line_label_ = line_label if (not colors_on_plot): line_color = linecolors[add_2D_subplot.counter % len(linecolors)] add_2D_subplot.counter += 1 if (labels_on_plot or multi_channel): plt_to_add.plot(x_data_[i], y_data_[i], ls=line_style, lw=line_width, c=line_color, label=line_label_) else: plt_to_add.plot(x_data_[i], y_data_[i], ls=line_style, lw=line_width, c=line_color) plt_to_add.fill_between(x_data_[i], y_data_[i], alpha=line_opacity, facecolor=line_color) add_subplot_para(plt_to_add, x_label=x_label, y_label=y_label, x_range=x_range, y_range=y_range, plot_title=plot_title) if (labels_on_plot or multi_channel): plt_to_add.legend(loc="best")
def __init__(self, beta: Optional[Union[List[float], Callable]] = None, order: int = 2, medium: str = cst.DEF_FIBER_MEDIUM, start_taylor: int = 0, skip_taylor: List[int] = []) -> None: r""" Parameters ---------- beta : The derivatives of the propagation constant. :math:`[km^{-1}, ps\cdot km^{-1}, ps^2\cdot km^{-1}, ps^3\cdot km^{-1}, \ldots]` If a callable is provided, variable must be angular frequency. :math:`[ps^{-1}]` order : The order of beta coefficients to take into account. (will be ignored if beta values are provided - no file) medium : The medium in which the dispersion is considered. start_taylor : The order of the derivative from which to start the Taylor Series expansion. """ super().__init__() self._order: int = order self._medium: str = medium self._start_taylor: int = start_taylor self._skip_taylor: List[int] = skip_taylor self._predict: Optional[Callable] = None self._beta: Array[float] self._class_n: Optional[Callable] = None if (beta is not None): if (callable(beta)): self._predict = beta else: beta = util.make_list(beta) # make sure is list self._beta = np.asarray(beta).reshape((1, -1)) self._order = self._beta.shape[1] - 1 else: self._predict = self.calc_beta_coeffs self._class_n = Sellmeier(medium)
def __init__(self, alpha: Optional[Union[List[float], Callable]] = None, order: int = 1, medium: str = cst.DEF_FIBER_MEDIUM, start_taylor: int = 0, skip_taylor: List[int] = []) -> None: r""" Parameters ---------- alpha : The derivatives of the attenuation coefficients. :math:`[km^{-1}, ps\cdot km^{-1}, ps^2\cdot km^{-1}, ps^3\cdot km^{-1}, \ldots]` If a callable is provided, variable must be angular frequency. :math:`[ps^{-1}]` order : The order of alpha coefficients to take into account. (will be ignored if alpha values are provided - no file) medium : The medium in which the attenuation is considered. start_taylor : The order of the derivative from which to start the Taylor Series expansion. """ super().__init__() self._order: int = order self._medium: str = medium self._start_taylor: int = start_taylor self._skip_taylor: List[int] = skip_taylor self._predict: Optional[Callable] = None self._alpha: Array[float] self._future_eq_to_calc_alpha: bool = False if (alpha is not None): if (callable(alpha)): self._predict = alpha else: # alpha is a array of float alpha = util.make_list(alpha) # make sure is list self._alpha = np.asarray(alpha).reshape((1, -1)) self._order = self._alpha.shape[1] - 1 else: self._future_eq_to_calc_alpha = True
def __init__(self, re: REFiber, alpha: Optional[List[float]] = None) -> None: r""" Parameters ---------- re : REFiber The rate equations object. alpha : The attenuation coefficient. :math:`[km^{-1}]` """ super().__init__() self._re: REFiber = re self._alpha: Array[float] if (alpha is None): self._alpha = np.zeros(0.0) else: alpha = util.make_list(alpha) self._alpha = np.asarray(alpha) self._factor: Array[float]
def add_2D_subplot(plt_to_add, x_data, y_data, x_label, y_label, x_range, y_range, plot_title, plot_label, plot_linestyle, plot_color, opacity): x_data_temp = np.asarray(x_data) x_data = np.array([]) y_data_temp = np.asarray(y_data) y_data = np.array([]) x_data, y_data = util.auto_pad(x_data_temp, y_data_temp) multi_channel = len(y_data) > 1 labels_on_plot = plot_label is not None colors_on_plot = plot_color is not None if (multi_channel): plot_label = util.make_list(plot_label, len(y_data)) print('in plot', x_data.shape, y_data.shape) print(x_data) for i in range(len(y_data)): if (multi_channel): if (labels_on_plot): plot_label_temp = plot_label[i] + " (ch.{})".format(i) else: plot_label_temp = "channel {}".format(i) else: plot_label_temp = plot_label if (not colors_on_plot): plot_color = linecolors[add_2D_subplot.counter] add_2D_subplot.counter += 1 if (labels_on_plot or multi_channel): plt_to_add.plot(x_data , y_data[i], ls=plot_linestyle, c=plot_color, label=plot_label_temp) else: plt_to_add.plot(x_data , y_data[i], ls=plot_linestyle, c=plot_color) plt_to_add.fill_between(x_data , y_data[i], alpha=opacity, facecolor=plot_color) add_subplot_para(plt_to_add, x_label=x_label, y_label=y_label, x_range=x_range, y_range=y_range, plot_title=plot_title) if (labels_on_plot or multi_channel): plt_to_add.legend(loc = "best")
def __init__(self, name: str = default_name, channels: int = 1, center_lambda: List[float] = [cst.DEF_LAMBDA], position: List[float] = [0.5], width: List[float] = [10.0], fwhm: Optional[List[float]] = None, peak_power: List[float] = [1e-3], rep_freq: List[float] = [0.0], offset_nu: List[float] = [0.0], chirp: List[float] = [0.0], init_phi: List[float] = [0.0], noise: Optional[np.ndarray] = None, field_name: str = '', save: bool = False, pre_call_code: str = '', post_call_code: str = '') -> None: r""" Parameters ---------- name : The name of the component. channels : The number of channels in the field. center_lambda : The center wavelength of the channels. :math:`[nm]` position : Relative position of the pulses in the time window. :math:`\in [0,1]` width : Half width of the pulse. :math:`[ps]` fwhm : Full band width at half maximum. :math:`[ps]` If fwhm is provided, the width will be ignored. If fwhm is not provided or set to None, will use the width. peak_power : Peak power of the pulses. :math:`[W]` rep_freq : The repetition frequency of the pulse in the time window. :math:`[THz]` offset_nu : The offset frequency. :math:`[THz]` chirp : The chirp parameter for chirped pulses. init_phi : The initial phase of the pulses. noise : The initial noise along the pulses. field_name : The name of the field. save : If True, the last wave to enter/exit a port will be saved. pre_call_code : A string containing code which will be executed prior to the call to the function :func:`__call__`. The two parameters `input_ports` and `input_fields` are available. post_call_code : A string containing code which will be executed posterior to the call to the function :func:`__call__`. The two parameters `output_ports` and `output_fields` are available. """ # Parent constructor ------------------------------------------- ports_type = [cst.OPTI_OUT] super().__init__(name, default_name, ports_type, save, pre_call_code=pre_call_code, post_call_code=post_call_code) # Attr types check --------------------------------------------- util.check_attr_type(channels, 'channels', int) util.check_attr_type(center_lambda, 'center_lambda', float, list) util.check_attr_type(position, 'position', float, list) util.check_attr_type(width, 'width', float, list) util.check_attr_type(fwhm, 'fwhm', float, list, None) util.check_attr_type(peak_power, 'peak_power', float, list) util.check_attr_type(rep_freq, 'rep_freq', float, list) util.check_attr_type(offset_nu, 'offset_nu', float, list) util.check_attr_type(chirp, 'chirp', float, list) util.check_attr_type(init_phi, 'init_phi', float, list) util.check_attr_type(noise, 'noise', None, np.ndarray) util.check_attr_type(field_name, 'field_name', str) # Attr --------------------------------------------------------- self.channels: int = channels self.center_lambda: List[float] = util.make_list( center_lambda, channels) self.position: List[float] = util.make_list(position, channels) self.width: List[float] = util.make_list(width, channels) self._fwhm: Optional[List[float]] self.fwhm = fwhm self.peak_power: List[float] = util.make_list(peak_power, channels) self.rep_freq: List[float] = util.make_list(rep_freq, channels) self.offset_nu: List[float] = util.make_list(offset_nu, channels) self.chirp: List[float] = util.make_list(chirp, channels) self.init_phi: List[float] = util.make_list(init_phi, channels) self.noise: Optional[np.ndarray] = noise self.field_name: str = field_name
def __init__(self, name: str = default_name, phase_shift: Union[List[float], List[Callable]] = [0.0, 0.0], loss: float = 0.0, extinction: Optional[float] = None, v_pi: Optional[List[float]] = None, v_bias: Optional[List[float]] = None, v_mod: Optional[List[Callable]] = None, save: bool = False, max_nbr_pass: Optional[List[int]] = None, pre_call_code: str = '', post_call_code: str = '') -> None: r""" Parameters ---------- name : The name of the component. phase_shift : The phase difference induced between the two arms of the MZ. Can be a list of callable with time variable. :math:`[ps]` (will be ignored if (v_pi and v_bias) or (v_pi and v_mod) are provided) loss : The loss induced by the MZ. :math:`[dB]` extinction : The extinction ratio. :math:`[dB]` v_pi : The half-wave voltage. :math:`[V]` v_bias : The bias voltage. :math:`[V]` v_mod : The modulation voltage :math:`[V]`. Must be a callable with time variable. :math:`[ps]` save : If True, the last wave to enter/exit a port will be saved. max_nbr_pass : No fields will be propagated if the number of fields which passed through a specific port exceed the specified maximum number of pass for this port. pre_call_code : A string containing code which will be executed prior to the call to the function :func:`__call__`. The two parameters `input_ports` and `input_fields` are available. post_call_code : A string containing code which will be executed posterior to the call to the function :func:`__call__`. The two parameters `output_ports` and `output_fields` are available. """ # Parent constructor ------------------------------------------- ports_type = [cst.ANY_ALL, cst.ANY_ALL] super().__init__(name, default_name, ports_type, save, max_nbr_pass=max_nbr_pass, pre_call_code=pre_call_code, post_call_code=post_call_code) # Attr types check --------------------------------------------- util.check_attr_type(phase_shift, 'phase_shift', float, Callable, list) util.check_attr_type(loss, 'loss', float) util.check_attr_type(extinction, 'extinction', None, float) util.check_attr_type(v_pi, 'v_pi', None, float, list) util.check_attr_type(v_bias, 'v_bias', None, float, list) util.check_attr_type(v_mod, 'v_mod', None, Callable, list) # Attr --------------------------------------------------------- self._v_pi: Optional[List[float]] self._v_pi = v_pi if v_pi is None else util.make_list(v_pi, 2) self._v_bias: Optional[List[float]] self._v_bias = v_bias if v_bias is None else util.make_list(v_bias, 2) self._v_mod: Optional[List[Callable]] self._v_mod = v_mod if v_mod is None else util.make_list(v_mod, 2) if (v_pi is not None and (v_bias is not None or v_mod is not None)): self._update_phase_shift() else: self.phase_shift = phase_shift self.loss = loss self._extinction: Optional[float] self.extinction = extinction self._divider = IdealDivider(name='nocount', arms=2, divide=True, ratios=[0.5, 0.5]) # Policy ------------------------------------------------------- self.add_port_policy(([0], [1], True))
def fwhm(self, fwhm: Optional[List[float]]) -> None: if (fwhm is None): self._fwhm = None else: self._fwhm = util.make_list(fwhm, self.channels)
def __init__(self, solvers: List[AbstractSolver], noise_solvers: List[Optional[AbstractSolver]] = [None], length: float = 1.0, steps: List[int] = [100], step_method: List[str] = [FIXED], solver_order: str = FOLLOWING, stepper_method: List[str] = [FORWARD], solver_sequence: Optional[List[int]] = None, boundary_cond: Optional[AbstractBoundaryConditions] = None, conv_checker: Optional[AbstractConvergenceChecker] = None, save_all: bool = False) -> None: r""" Parameters ---------- solvers : The solvers used to solve the equations. nlse_solvers : The solvers used to solve the noise equations. length : The length over which the equation is considered. :math:`[km]` steps : The number of steps used for the computation. step_method : The method used to update the step size. solver_order : The order type in which the methods will be computed. (will be ignored if solver_sequence is provided) stepper_method : The method used to converge to solution. solver_sequence : The sequence in which the methods will be computed. boundary_cond : AbstractBoundaryConditions The boundaries conditions. conv_checker : AbstractConvergenceChecker The convergence checker. save_all : If True, will save all channels of all fields at each space step during the equation resolution. The number of step recorded depends on the :attr:`memory_storage` attribute of :class:`layout.Layout`. """ # Attr types check --------------------------------------------- util.check_attr_type(length, 'length', int, float) util.check_attr_type(steps, 'steps', int, list) util.check_attr_type(step_method, 'step_method', str, list) util.check_attr_type(solver_order, 'solver_order', str) util.check_attr_type(stepper_method, 'stepper_method', str, list) util.check_attr_type(boundary_cond, 'boundary_cond', AbstractBoundaryConditions, None) util.check_attr_type(conv_checker, 'conv_checker', AbstractConvergenceChecker, None) util.check_attr_type(save_all, 'save_all', bool) # Attr --------------------------------------------------------- self._solvers: List[AbstractSolver] = util.make_list(solvers) self._nbr_solvers: int = len(self._solvers) self._noise_solvers: List[Optional[AbstractSolver]] self._noise_solvers = util.make_list(noise_solvers, self._nbr_solvers, None) self._eqs: List[SOLVER_CALLABLE_TYPE] self._eqs = [self._solvers[i].f for i in range(self._nbr_solvers)] self._steps: List[int] = util.make_list(steps, self._nbr_solvers) self._length: float = length # Stepper method stepper_method = util.make_list(stepper_method, self._nbr_solvers) self._stepper_method_str: List[str] = [] self._stepper_method: List[STEPPER_METHOD_TYPE] = [] for i in range(len(stepper_method)): self._stepper_method_str.append( util.check_attr_value(stepper_method[i].lower(), STEPPER_METHODS, FORWARD)) self._stepper_method.append( getattr( self, "_{}_method".format(self._stepper_method_str[i].lower()))) # Step method step_method = util.make_list(step_method, self._nbr_solvers) self._step_method_str: List[str] = [] self._step_method: List[STEP_METHOD_TYPE] = [] for i in range(len(step_method)): self._step_method_str.append( util.check_attr_value(step_method[i].lower(), STEP_METHODS, FIXED)) self._step_method.append( getattr(self, "_{}_step".format(self._step_method_str[i].lower()))) # Solver order solver_order = util.check_attr_value(solver_order.lower(), SOLVER_ORDERS, FOLLOWING) if (solver_sequence is None): if (solver_order.lower() == ALTERNATING): solver_sequence = [0 for i in range(self._nbr_solvers)] if (solver_order.lower() == FOLLOWING): solver_sequence = [i for i in range(self._nbr_solvers)] else: solver_sequence = solver_sequence self._solver_seq_ids: List[List[int]] =\ self._get_seq_ids(solver_sequence) self.save_all: bool = save_all self._avail_memory: float = 0. self._save_step: int = 0 self._enough_space: bool = False self._storage: Storage = Storage() self._channels: np.ndarray = np.array([]) self._noises: np.ndarray = np.array([]) self._space: np.ndarray = np.array([]) self._conv_checker: Optional[AbstractConvergenceChecker] = conv_checker self._boundary_cond: Optional[ AbstractBoundaryConditions] = boundary_cond
def __init__(self, eqs: List[AbstractEquation], method: List[str] = [cst.DEFAULT_SOLVER], length: float = 1.0, steps: List[int] = [100], step_method: List[str] = [FIXED], solver_order: str = FOLLOWING, stepper_method: List[str] = [FORWARD], solver_sequence: Optional[List[int]] = None, error: float = 0.01, save_all: bool = False) -> None: r""" Parameters ---------- eqs : list of AbstractEquation The equation to solve. method : The method used to solve the equation. length : The length over which the equation is considered. :math:`[km]` steps : The number of steps used for the computation. step_method : The method used to update the step size. solver_order : The order type in which the methods will be computed. (will be ignored if solver_sequence is provided) stepper_method : The method used to converge to solution. solver_sequence : The sequence in which the methods will be computed. error : The error for convergence criterion of stepper resolution. save_all : If True, will save all channels of all fields at each space step during the equation resolution. The number of step recorded depends on the :attr:`memory_storage` attribute of :class:`layout.Layout`. """ self._eqs: List[AbstractEquation] = util.make_list(eqs) self._nbr_solvers: int = len(self._eqs) self._methods: List[str] = util.make_list(method, self._nbr_solvers) self._steps: List[int] = util.make_list(steps, self._nbr_solvers) self._length: float = length self._solvers: List[Solver] = [ Solver(self._eqs[i], self._methods[i]) for i in range(self._nbr_solvers) ] stepper_method = util.make_list(stepper_method, self._nbr_solvers) self._stepper_method_str = stepper_method self._stepper_method = [ getattr(self, "_{}_method".format(stepper_method[i].lower())) for i in range(self._nbr_solvers) ] step_method = util.make_list(step_method, self._nbr_solvers) self._step_method_str = step_method self._step_method = [ getattr(self, "_{}_step".format(step_method[i].lower())) for i in range(self._nbr_solvers) ] self._start_shooting_forward: bool = True if (solver_sequence is None): if (solver_order.lower() == ALTERNATING): solver_sequence = [0 for i in range(self._nbr_solvers)] if (solver_order.lower() == FOLLOWING): solver_sequence = [i for i in range(self._nbr_solvers)] else: solver_sequence = solver_sequence self._solver_seq_ids: List[List[int]] =\ self._get_seq_ids(solver_sequence) self._error: float = error self.save_all: bool = save_all self._storage: Storage = Storage()
def open(self, domain: Domain, *fields: List[Field]) -> None: super().open(domain, *fields) # Initialize angular frequency --------------------------------- self._samples = len(self._omega) self._omega_s = self._in_eq_waves(self._omega_all, 0) self._omega_p = self._in_eq_waves(self._omega_all, 1) # Iniate array for center omegas data -------------------------- self._center_omega_s = self._in_eq_waves(self._center_omega, 0) self._center_omega_p = self._in_eq_waves(self._center_omega, 1) # Signal channel width ---------------------------------------- signal_width = np.array( util.make_list(self._signal_width, len(self._center_omega_s))) self._width_omega_s = (1.0 / signal_width) * 2.0 * cst.PI # Initiate the variables array - power and pop. density -------- self._shape_step_s = (len(self._center_omega_s), self._samples) self._power_s_f = np.zeros((1, ) + self._shape_step_s) self._power_s_b = np.zeros((1, ) + self._shape_step_s) self._power_s_ref = np.zeros(self._shape_step_s) self._power_ase_f = np.zeros((1, ) + self._shape_step_s) self._power_ase_b = np.zeros((1, ) + self._shape_step_s) self._shape_step_p = (len(self._center_omega_p), self._samples) self._power_p_f = np.zeros((1, ) + self._shape_step_p) self._power_p_b = np.zeros((1, ) + self._shape_step_p) self._power_p_ref = np.zeros(self._shape_step_p) self._N_1 = np.zeros(1) # Initiate refractive index ------------------------------------ if (self._calc_n_core): self._n_0_s = self._calc_n_core.n(self._omega_s) self._n_0_p = self._calc_n_core.n(self._omega_p) else: self._n_0_s = np.ones(self._shape_step_s) * self._n_0_s_value self._n_0_p = np.ones(self._shape_step_p) * self._n_0_p_value if (self._calc_n_clad): NA_s = np.ones(self._shape_step_s) * self._NA_value_s NA_p = np.ones(self._shape_step_p) * self._NA_value_p self._n_clad_s = self._calc_n_clad(NA_s, self._n_0_s) self._n_clad_p = self._calc_n_clad(NA_p, self._n_0_p) else: self._n_clad_s = np.ones(self._shape_step_s) * self._n_clad_s_value self._n_clad_p = np.ones(self._shape_step_p) * self._n_clad_p_value self._n_tot_s = np.zeros(self._shape_step_s) self._n_tot_p = np.zeros(self._shape_step_p) # Initiate cross sections for each frequency ------------------- self._A_eff_s = np.zeros(self._shape_step_s) self._Gamma_s = np.zeros(self._shape_step_s) for i in range(len(self._center_omega_s)): NA = NumericalAperture.calc_NA(self._n_0_s[i], self._n_clad_s[i]) self._A_eff_s[i] = self._eff_area_s(self._omega_s[i], NA) self._Gamma_s[i] = self._overlap_s(self._A_eff_s[i]) self._A_eff_p = np.zeros(self._shape_step_p) self._Gamma_p = np.zeros(self._shape_step_p) for i in range(len(self._center_omega_p)): NA = NumericalAperture.calc_NA(self._n_0_p[i], self._n_clad_p[i]) self._A_eff_p[i] = self._eff_area_s(self._omega_p[i], NA) self._Gamma_p[i] = self._overlap_p(self._A_eff_p[i]) # Initiate cross sections for each frequency ------------------- if (self._absorp is not None): self._sigma_a_s = np.zeros(self._shape_step_s) self._sigma_a_p = np.zeros(self._shape_step_p) for i in range(len(self._center_omega_s)): self._sigma_a_s[i] =\ self._absorp.get_cross_section(self._omega_s[i]) for i in range(len(self._center_omega_p)): self._sigma_a_p[i] =\ self._absorp.get_cross_section(self._omega_p[i]) else: self._sigma_a_s = np.ones( self._shape_step_s) * self._sigma_a_s_value self._sigma_a_p = np.ones( self._shape_step_p) * self._sigma_a_p_value if (self._stimu is not None): self._sigma_e_s = np.zeros(self._shape_step_s) self._sigma_e_p = np.zeros(self._shape_step_p) for i in range(len(self._center_omega_s)): self._sigma_e_s[i] =\ self._stimu.get_cross_section(self._omega_s[i]) for i in range(len(self._center_omega_p)): self._sigma_e_p[i] =\ self._stimu.get_cross_section(self._omega_p[i]) else: if (self._sigma_e_mccumber): self._sigma_e_s = np.zeros(self._shape_step_s) self._sigma_e_p = np.zeros(self._shape_step_p) # must initiate here else: self._sigma_e_s = (np.ones(self._shape_step_s) * self._sigma_e_s_value) self._sigma_e_p = (np.ones(self._shape_step_p) * self._sigma_e_p_value) # Initiate counter --------------------------------------------- self._iter = -1 # -1 bcs increment in the first init. cond. self._call_counter = 0 self._step = 0 self._forward = True
def __init__(self, name: str = default_name, phase_shift: Union[List[float], List[Callable]] = [0.0, 0.0], loss: float = 0.0, ext_ratio: float = 0.0, v_pi: Optional[List[float]] = None, v_bias: Optional[List[float]] = None, v_mod: Optional[List[Callable]] = None, save: bool = False, max_nbr_pass: Optional[List[int]] = None) -> None: r""" Parameters ---------- name : The name of the component. phase_shift : The phase difference induced between the two arms of the MZ. Can be a list of callable with time variable. :math:`[ps]` (will be ignored if (v_pi and v_bias) or (v_pi and v_mod) are provided) loss : The loss induced by the MZ. :math:`[dB]` ext_ratio : The extinction ratio. v_pi : The half-wave voltage. :math:`[V]` v_bias : The bias voltage. :math:`[V]` v_mod : The modulation voltage :math:`[V]`. Must be a callable with time variable. :math:`[ps]` save : If True, the last wave to enter/exit a port will be saved. max_nbr_pass : No fields will be propagated if the number of fields which passed through a specific port exceed the specified maximum number of pass for this port. """ # Parent constructor ------------------------------------------- ports_type = [cst.ANY_ALL, cst.ANY_ALL] super().__init__(name, default_name, ports_type, save, max_nbr_pass=max_nbr_pass) # Attr types check --------------------------------------------- util.check_attr_type(phase_shift, 'phase_shift', float, Callable, list) util.check_attr_type(loss, 'loss', float) util.check_attr_type(ext_ratio, 'ext_ratio', float) util.check_attr_type(v_pi, 'v_pi', None, float, list) util.check_attr_type(v_bias, 'v_bias', None, float, list) util.check_attr_type(v_mod, 'v_mod', None, Callable, list) # Attr --------------------------------------------------------- if (v_pi is not None and (v_bias is not None or v_mod is not None)): pi_ = util.make_list(v_pi, 2) bias_ = util.make_list(v_bias, 2) if v_bias is not None\ else [0.0, 0.0] mod_ = util.make_list(v_mod, 2) if v_mod is not None\ else [lambda t: 0.0, lambda t: 0.0] phase_shift_ = [ lambda t: cst.PI * (bias_[0] + mod_[0](t)) / pi_[0], lambda t: cst.PI * (bias_[1] + mod_[1](t)) / pi_[1] ] else: phase_shift_ = util.make_list(phase_shift, 2, 0.0) # N.B. name='nocount' to avoid inc. default name counter self._divider = IdealDivider(name='nocount', arms=2, divide=True, ratios=[0.5, 0.5]) self._combiner = IdealCombiner(name='nocount', arms=2, combine=True, ratios=[0.5, 0.5]) self._phasemod_1 = IdealPhaseMod(name='nocount', phase_shift=phase_shift_[0]) self._phasemod_2 = IdealPhaseMod(name='nocount', phase_shift=phase_shift_[1]) self._amp = IdealAmplifier(name='nocount', gain=-loss) # Policy ------------------------------------------------------- self.add_port_policy(([0], [1], True))
def __init__(self, name: str = default_name, channels: int = 1, center_lambda: List[float] = [cst.DEF_LAMBDA], position: List[float] = [0.5], width: List[float] = [10.0], peak_power: List[float] = [1e-3], bit_rate: List[float] = [0.0], offset_nu: List[float] = [0.0], chirp: List[float] = [0.0], init_phi: List[float] = [0.0], save: bool = False) -> None: r""" Parameters ---------- name : The name of the component. channels : The number of channels in the field. center_lambda : The center wavelength of the channels. :math:`[nm]` position : Relative position of the pulses in the time window. :math:`\in [0,1]` width : Half width of the pulse. :math:`[ps]` peak_power : Peak power of the pulses. :math:`[W]` bit_rate : Bit rate (repetition rate) of the pulse in the time window. :math:`[THz]` offset_nu : The offset frequency. :math:`[THz]` chirp : The chirp parameter for chirped pulses. init_phi : The initial phase of the pulses. save : If True, the last wave to enter/exit a port will be saved. """ # Parent constructor ------------------------------------------- ports_type = [cst.OPTI_OUT] super().__init__(name, default_name, ports_type, save) # Attr types check --------------------------------------------- util.check_attr_type(channels, 'channels', int) util.check_attr_type(center_lambda, 'center_lambda', float, list) util.check_attr_type(position, 'position', float, list) util.check_attr_type(width, 'width', float, list) util.check_attr_type(peak_power, 'peak_power', float, list) util.check_attr_type(bit_rate, 'bit_rate', float, list) util.check_attr_type(offset_nu, 'offset_nu', float, list) util.check_attr_type(chirp, 'chirp', float, list) util.check_attr_type(init_phi, 'init_phi', float, list) # Attr --------------------------------------------------------- self.channels: int = channels self.center_lambda: List[float] = util.make_list( center_lambda, channels) self.position: List[float] = util.make_list(position, channels) self.width: List[float] = util.make_list(width, channels) self.peak_power: List[float] = util.make_list(peak_power, channels) self.bit_rate: List[float] = util.make_list(bit_rate, channels) self.offset_nu: List[float] = util.make_list(offset_nu, channels) self.chirp: List[float] = util.make_list(chirp, channels) self.init_phi: List[float] = util.make_list(init_phi, channels)
def __init__(self, nbr_fibers: int, beta: Optional[Union[List[List[float]], Callable]], kappa: Optional[List[List[List[float]]]], sigma_cross: List[List[float]], c2c_spacing: List[List[float]], core_radius: List[float], V: List[float], n_0: List[float], ASYM: bool, COUP: bool, XPM: bool, medium: str) -> None: r""" Parameters ---------- nbr_fibers : The number of fibers in the coupler. beta : The derivatives of the propagation constant. :math:`[km^{-1}, ps\cdot km^{-1}, ps^2\cdot km^{-1}, ps^3\cdot km^{-1}, \ldots]` kappa : The coupling coefficients. :math:`[km^{-1}]` sigma_cross : Positive term multiplying the XPM term of the NLSE inbetween the fibers. c2c_spacing : The center to center distance between two cores. :math:`[\mu m]` core_radius : The core radius. :math:`[\mu m]` V : The fiber parameter. n_0 : The refractive index outside of the waveguides. ASYM : If True, trigger the asymmetry effects between cores. COUP : If True, trigger the coupling effects between cores. XPM : If True, trigger the cross-phase modulation. medium : The main medium of the fiber. """ super().__init__(nbr_fibers) if (beta is not None): beta_: Union[List[List[float]], List[List[Callable]]] =\ util.make_matrix(beta, nbr_fibers, nbr_fibers) sigma_cross = util.make_matrix(sigma_cross, nbr_fibers, nbr_fibers, sym=True) if (kappa is not None): kappa = util.make_tensor(kappa, nbr_fibers, nbr_fibers, 0) V = util.make_list(V, nbr_fibers) n_0 = util.make_list(n_0, nbr_fibers) c2c_spacing = util.make_matrix(c2c_spacing, nbr_fibers, nbr_fibers, sym=True) core_radius = util.make_list(core_radius, nbr_fibers) for i in range(nbr_fibers): for j in range(nbr_fibers): if (i != j): if (ASYM): if (beta is not None): self._effects_lin[i][j].append( Asymmetry(beta_01=beta_[i][0], beta_02=beta_[j][0])) else: self._effects_lin[i][j].append( Asymmetry(medium=medium)) if (COUP): if (kappa is not None): self._effects_all[i][j].append( Coupling(kappa[i][j])) else: same_V = sum(V) / len(V) == V[0] same_a = (sum(core_radius) / len(core_radius) == core_radius[0]) same_n_0 = sum(n_0) / len(n_0) == n_0[0] if (not (same_V and same_a and same_n_0)): util.warning_terminal( "Automatic calculation " "of coupling coefficient assumes same " "V, core_radius and n_0 for now, " "different ones provided, might lead to " "unrealistic results.") self._effects_all[i][j].append( Coupling(V=V[i], a=core_radius[i], d=c2c_spacing[i][j], n_0=n_0[i])) if (XPM): self._effects_non_lin[i][j].append( Kerr(SPM=False, XPM=True, FWM=False, sigma=sigma_cross[i][j]))
def __init__(self, re: REFiber, alpha: Optional[Union[List[float], Callable]], alpha_order: int, beta: Optional[Union[List[float], Callable]], beta_order: int, gamma: Optional[Union[float, Callable]], gain_order: int, R_0: float, R_L: float, nl_index: Optional[Union[float, Callable]], ATT: bool, DISP: bool, GS: bool, medium: str, dopant: str) -> None: r""" Parameters ---------- re : A fiber rate equation object. alpha : The derivatives of the attenuation coefficients. :math:`[km^{-1}, ps\cdot km^{-1}, ps^2\cdot km^{-1}, ps^3\cdot km^{-1}, \ldots]` If a callable is provided, variable must be angular frequency. :math:`[ps^{-1}]` alpha_order : The order of alpha coefficients to take into account. (will be ignored if alpha values are provided - no file) beta : The derivatives of the propagation constant. :math:`[km^{-1}, ps\cdot km^{-1}, ps^2\cdot km^{-1}, ps^3\cdot km^{-1}, \ldots]` If a callable is provided, variable must be angular frequency. :math:`[ps^{-1}]` beta_order : The order of beta coefficients to take into account. (will be ignored if beta values are provided - no file) gamma : The non linear coefficient. :math:`[rad\cdot W^{-1}\cdot km^{-1}]` If a callable is provided, variable must be angular frequency. :math:`[ps^{-1}]` gain_order : The order of the gain coefficients to take into account. (from the Rate Equations resolution) R_0 : The reflectivity at the fiber start. R_L : The reflectivity at the fiber end. nl_index : The non linear index. Used to calculate the non linear parameter. :math:`[m^2\cdot W^{-1}]` ATT : If True, trigger the attenuation. DISP : If True, trigger the dispersion. GS : If True, trigger the gain saturation. medium : The main medium of the fiber amplifier. dopant : The doped medium of the fiber amplifier. """ # keep all methods from NLSE but bypass constructor AbstractEquation.__init__(self) # grand parent constructor self._medium: str = medium self._dopant: str = dopant self._re: REFiber = re self._R_L: float = R_L self._R_0: float = R_0 self._delay_time: Array[float] self._coprop: bool # Effects ------------------------------------------------------ self._att_ind: int = -1 self._disp_ind: int = -1 self._gs_ind: int = -1 self._gain_ind: int = -1 self._gain_order: int = gain_order self._beta_order: int = beta_order if (ATT): self._effects_lin.append(Attenuation(alpha, alpha_order)) self._att_ind = len(self._effects_lin) - 1 if (DISP): self._effects_lin.append(Dispersion(beta, beta_order)) self._disp_ind = len(self._effects_lin) - 1 if (GS): start_taylor_gain = 1 self._effects_lin.append(GainSaturation(re, [0.0])) self._gs_ind = len(self._effects_lin) - 1 else: start_taylor_gain = 0 alpha_temp = [0.0 for i in range(self._gain_order + 1)] self._effects_lin.append( Attenuation(alpha_temp, start_taylor=start_taylor_gain)) self._gain_ind = len(self._effects_lin) - 1 # Gamma -------------------------------------------------------- self._nl_index: Union[float, Callable] = NLIndex(medium=medium) if\ (nl_index is None) else nl_index self._gamma: Array[float] self._predict_gamma: Optional[Callable] = None self._custom_gamma: bool = False if (gamma is not None): if (callable(gamma)): self._custom_gamma = True self._predict_gamma = gamma else: self._gamma = np.asarray(util.make_list(gamma)) else: self._predict_gamma = NLCoefficient.calc_nl_coefficient
def v_pi(self, v_pi: Optional[List[float]]) -> None: self._v_pi = util.make_list(v_pi, 2) self._update_phase_shift()
def __init__(self, sigma_a: Optional[Union[List[float], Callable]] = None, sigma_e: Optional[Union[List[float], Callable]] = None, n_core: Optional[Union[float, List[float]]] = None, n_clad: Optional[Union[float, List[float]]] = None, NA: Optional[Union[float, List[float]]] = None, temperature: float = 293.15, tau_meta: float = cst.TAU_META, N_T: float = cst.N_T, core_radius: float = cst.CORE_RADIUS, clad_radius: float = cst.CLAD_RADIUS, area_doped: Optional[float] = None, eta_s: float = cst.ETA_SIGNAL, eta_p: float = cst.ETA_PUMP, R_0: float = cst.R_0, R_L: float = cst.R_L, signal_width: List[float] = [1.0], medium: str = cst.DEF_FIBER_MEDIUM, dopant: str = cst.DEF_FIBER_DOPANT, step_update: bool = False) -> None: r""" Parameters ---------- sigma_a : The absorption cross sections of the signal and the pump (1<=len(sigma_a)<=2). :math:`[nm^2]` If a callable is provided, varibale must be wavelength. :math:`[nm]` sigma_e : The emission cross sections of the signal and the pump (1<=len(sigma_a)<=2). :math:`[nm^2]` If a callable is provided, varibale must be wavelength. :math:`[nm]` n_core : The refractive index of the core. If the medium is not recognised by Optcom, at least two elements should be provided out of those three: n_core, n_clad, NA. n_clad : The refractive index of the cladding. If the medium is not recognised by Optcom, at least two elements should be provided out of those three: n_core, n_clad, NA. NA : The numerical aperture. If the medium is not recognised by Optcom, at least two elements should be provided out of those three: n_core, n_clad, NA. temperature : The temperature of the medium. :math:`[K]` tau_meta : The metastable level lifetime. :math:`[\mu s]` N_T : The total doping concentration. :math:`[nm^{-3}]` core_radius : The radius of the core. :math:`[\mu m]` clad_radius : The radius of the cladding. :math:`[\mu m]` area_doped : The doped area. :math:`[\mu m^2]` If None, will be approximated to the core area. eta_s : The background signal loss. :math:`[km^{-1}]` eta_p : The background pump loss. :math:`[km^{-1}]` R_0 : The reflectivity at the fiber start. R_L : The reflectivity at the fiber end. signal_width : The width of each channel of the signal. :math:`[ps]` medium : The main medium of the fiber amplifier. dopant : The doped medium of the fiber amplifier. step_update : If True, update the signal and pump power from the wave arrays at each space step. """ super().__init__(2) # 2 equations # Variable declaration ----------------------------------------- self._power_s_f: Array[float] = np.array([]) self._power_s_b: Array[float] = np.array([]) self._power_s_ref: Array[float] = np.array([]) self._power_p_f: Array[float] = np.array([]) self._power_p_b: Array[float] = np.array([]) self._power_p_ref: Array[float] = np.array([]) self._power_ase_f: Array[float] = np.array([]) self._power_ase_b: Array[float] = np.array([]) self._N_1: Array[float] = np.array([]) self._sigma_a_s: Array[float] = np.array([]) self._sigma_a_p: Array[float] = np.array([]) self._sigma_e_s: Array[float] = np.array([]) self._sigma_e_p: Array[float] = np.array([]) self._n_tot_s: Array[float] = np.array([]) self._n_tot_p: Array[float] = np.array([]) self._n_0_s: Array[float] = np.array([]) self._n_0_p: Array[float] = np.array([]) self._n_clad_s: Array[float] = np.array([]) self._n_clad_p: Array[float] = np.array([]) self._Gamma_s: Array[float] = np.array([]) self._Gamma_p: Array[float] = np.array([]) self._A_eff_s: Array[float] = np.array([]) self._A_eff_p: Array[float] = np.array([]) # Variable initialization -------------------------------------- self._coprop: bool = True self._step_update: bool = step_update self._signal_width = signal_width self._medium: str = medium self._dopant: str = dopant self._T: float = temperature self._N_T: float = N_T self._tau: float = tau_meta * 1e6 # us -> ps self._decay: float = 1 / self._tau if (area_doped is None): area_doped = (cst.PI * core_radius**2) # TO DO: make option cladding doped and thus A_d_p = A_cladding A_d_p = (cst.PI * core_radius**2) A_d_s = area_doped self._overlap_s = OverlapFactor(A_d_s) self._overlap_p = OverlapFactor(A_d_p) self._A_d_s = A_d_s * 1e6 # um^2 -> nm^2 # Doping region area, can be cladding or core self._A_d_p = area_doped * 1e6 # um^2 -> nm^2 self._core_radius = core_radius self._res_index: ResonantIndex = ResonantIndex(medium=self._dopant) # Set n_core and n_clad depending on NA (forget NA afterwards) # Assume that only core medium can be calculated with Sellmeier # equations. Could be changed later. Would be easier to have # also cladding medium set by Sellmeier but cladding medium # not always available. -> To Discuss self._calc_n_core: Optional[Sellmeier] = None self._calc_n_clad: Optional[Callable] = None if (NA is None and n_clad is None): util.warning_terminal("Must specify at least NA or n_clad, " "n_clad will be set to 0.") self._n_clad_s_value = 0.0 self._n_clad_p_value = 0.0 if (n_clad is not None): # default is NA is None n_clad_temp = util.make_list(n_clad, 2) self._n_clad_s_value = n_clad_temp[0] self._n_clad_p_value = n_clad_temp[1] if (n_core is None): if (NA is not None and n_clad is not None): n_clad_temp = util.make_list(n_clad, 2) self._n_0_s_value =\ NumericalAperture.calc_n_core(NA, n_clad_temp[0]) self._n_0_p_value =\ NumericalAperture.calc_n_core(NA, n_clad_temp[1]) else: self._calc_n_core = Sellmeier(medium=self._medium) if (NA is not None and n_clad is None): self._calc_n_clad = NumericalAperture.calc_n_clad NA_temp = util.make_list(NA, 2) self._NA_value_s = NA_temp[0] self._NA_value_p = NA_temp[1] else: n_core_: List[float] = util.make_list(n_core, 2) self._n_0_s_value = n_core_[0] self._n_0_p_value = n_core_[1] if (NA is not None and n_clad is None): self._n_clad_s_value =\ NumericalAperture.calc_n_clad(NA, n_core_[0]) self._n_clad_p_value =\ NumericalAperture.calc_n_clad(NA, n_core_[1]) self._eff_area_s = EffectiveArea(core_radius) self._eff_area_p = EffectiveArea(core_radius) self._eta_s = eta_s * 1e-12 # km^{-1} -> nm^{-1} self._eta_p = eta_p * 1e-12 # km^{-1} -> nm^{-1} self._factor_s = 1 / (cst.HBAR * self._A_d_s) self._factor_p = 1 / (cst.HBAR * self._A_d_p) self._absorp: Optional[Absorption] = None if (sigma_a is None): self._absorp = Absorption(dopant=dopant) else: if (callable(sigma_a)): self._absorp = Absorption(predict=sigma_a) else: sigma_a_: List[float] = util.make_list(sigma_a, 2) self._sigma_a_s_value = sigma_a_[0] self._sigma_a_p_value = sigma_a_[1] self._sigma_e_mccumber = False self._stimu: Optional[StimulatedEmission] = None if (sigma_e is None): self._sigma_e_mccumber = True else: if (callable(sigma_e)): self._stimu = StimulatedEmission(predict=sigma_e) else: sigma_e_: List[float] = util.make_list(sigma_e, 2) self._sigma_e_s_value = sigma_e_[0] self._sigma_e_p_value = sigma_e_[1] self._R_0 = R_0 self._R_L = R_L
def v_bias(self, v_bias: Optional[List[float]]) -> None: self._v_bias = util.make_list(v_bias, 2) self._update_phase_shift()
def __init__(self, name: str, default_name: str, ports_type: List[int], save: bool, wait: bool = False, max_nbr_pass: Optional[List[int]] = None) -> None: """ Parameters ---------- name : The name of the component. default_name : The default name of the component. ports_type : Type of each port of the component, give also the number of ports in the component. For types, see :mod:`optcom/utils/constant_values/port_types`. save : If True, will save each field going through each port. The recorded fields can be accessed with the attribute :attr:`fields`. wait : If True, will wait for specified waiting port policy added with the function :func:`AbstractComponent.add_wait_policy`. max_nbr_pass : If not None, no fields will be propagated if the number of fields which passed through a specific port exceed the specified maximum number of pass for this port. """ # Attr type check ---------------------------------------------- util.check_attr_type(name, 'name', str) util.check_attr_type(default_name, 'default_name', str) util.check_attr_type(ports_type, 'ports_type', list) util.check_attr_type(save, 'save', bool) util.check_attr_type(wait, 'wait', bool) util.check_attr_type(max_nbr_pass, 'max_nbr_pass', None, list) # Nbr of instances and default name management ----------------- self.inc_nbr_instances() self.name: str = name if (name == default_name): if (self._nbr_instances_with_default_name): self.name += ' ' + str(self._nbr_instances_with_default_name) self.inc_nbr_instances_with_default_name() # Others var --------------------------------------------------- self._nbr_ports: int = len(ports_type) self.save: bool = save self._storages: Storage = [] self._ports: Port = [] for i in range(self._nbr_ports): self._ports.append(Port(self, i, ports_type[i])) self._port_policy: Dict[Tuple[int, ...], Tuple[int, ...]] = {} self._wait_policy: List[List[int]] = [] self._wait: bool = wait self._counter_pass: List[int] = [0 for i in range(self._nbr_ports)] self._max_nbr_pass: List[int] if (max_nbr_pass is None): # 999 bcs max default recursion depth with python self._max_nbr_pass = [999 for i in range(self._nbr_ports)] else: self._max_nbr_pass = util.make_list(max_nbr_pass, self._nbr_ports)
def v_mod(self, v_mod: Optional[List[Callable]]) -> None: self._v_mod = util.make_list(v_mod, 2) self._update_phase_shift()