def _fit(self, file_path: str, delimiter: str, conv_func: List[Callable], conv_factor: List[float]) -> List[Callable]: # conv factor first before conv func func = [] data, names = util.read_csv(file_path, delimiter) if (not len(data)): util.warning_terminal("The csv file provided is either empty or " "do not comply with correct synthax.") else: x = np.asarray(data[0]) if (len(conv_factor)): x *= conv_factor[0] if (len(conv_func)): x = conv_func[0](x) if (len(data) < 1): util.warning_terminal("The csv file provided contains only " "one column, must provid at least two for fitting.") else: for i in range(1, len(data)): y = np.asarray(data[i]) if (len(conv_factor) > i): y *= conv_factor[i] if (len(conv_func) > i): y = conv_func[i](y) # Make sure x data are increasing (needed for spl) if (x[0] > x[-1]): func.append(interpolate.splrep(x[::-1], y[::-1])) else: func.append(interpolate.splrep(x, y)) return func
def __init__(self, f: SOLVER_CALLABLE_TYPE, method: Optional[str]) -> None: """ Parameters ---------- f : The function to compute. method : The computation method. Call the __call__ function of the equation if None. """ self.name: str self._method: METHOD_SOLVER_CALLABLE_TYPE if (method is None): # analytical solution, no need numerical self.name = 'f_call' self._method = getattr(self, self.name) elif (hasattr(self, method.lower())): # Force to be static method self.name = method.lower() self._method = getattr(self, self.name) else: util.warning_terminal("The solver method '{}' does not exist, " "default solver '{}' is set.".format( method, self.__class__._default_method)) self.name = self.__class__._default_method self._method = getattr(self, self.__class__._default_method) self.f: SOLVER_CALLABLE_TYPE = f
def calc_V(omega, core_radius=cst.CORE_RADIUS, NA=None, n_core=None, n_clad=None): r"""Calculate the V parameter. Parameters ---------- omega : The angular frequency. :math:`[rad\cdot ps^{-1}]` core_radius : The radius of the core. :math:`[\mu m]` NA : The numerical aperture. n_core : The refractive index of the core. n_clad : The refractive index of the cladding. Returns ------- : Value of the V parameter. Notes ----- Considering: .. math:: V = k_0 a \text{NA} = \frac{\omega_0}{c} a \text{NA} = \frac{\omega_0}{c} a (n_{co}^2 - n_{cl}^2)^{\frac{1}{2}} """ # Unit conversion core_radius *= 1e3 # um -> nm if (isinstance(omega, float)): res = 0.0 else: res = np.zeros(omega.shape) factor = core_radius * omega / cst.LIGHT_SPEED if (NA is not None): res = factor * NA elif ((n_core is not None) and (n_clad is not None)): if (isinstance(omega, float)): res = factor * math.sqrt(n_core**2 - n_clad**2) else: res = factor * np.sqrt(np.square(n_core) - np.square(n_clad)) else: util.warning_terminal( "Not enough information to calculate the " "V parameter, must specified at least the Numerical Aperture " "or the refractive index of the core and the cladding. Will " "return 0.") return res
def __init__(self, medium: str = 'sio2', Bs: Optional[List[float]] = None, Cs: Optional[List[float]] = None) -> None: r""" Parameters ---------- medium : The medium in which the wave propagates. Bs : B coefficients. Cs : C coefficients. """ super().__init__(medium) self._Bs: List[float] = [0.0] self._Cs: List[float] = [0.0] if (Cs is not None and Bs is not None): self._Bs = Bs self._Cs = Cs elif (self._medium in media): self._Bs = coeff_values[self._medium][0] self._Cs = coeff_values[self._medium][1] else: util.warning_terminal("The medium is not recognised or no " "coefficients provided in Sellmeier equations, coefficients " "set to zero.")
def output_ports(self, input_ports: List[int]) -> List[int]: """Return a list of the corresponding output port(s) to the specified input port(s) depending on all ports in the provided list. Parameters ---------- input_ports : The inputs ports. Returns ------- : The output ports. """ output_ports: List[int] = [] if (not self._port_policy): util.warning_terminal("No policy for port management for " "component {}, no fields propagated.".format( self.name)) else: uni_ports = util.unique(input_ports) uni_output_ports = self._port_policy.get(tuple(uni_ports)) if (uni_output_ports is None): util.warning_terminal( "The input ports {} provided for " "component {} do not match any policy, no fields " "propagated.".format(input_ports, self.name)) else: for i in range(len(input_ports)): index = uni_ports.index(input_ports[i]) output_ports.append(uni_output_ports[index]) return output_ports
def _get_waiting(self, comp: AbstractComponent, neighbor: AbstractComponent, port: int, field: Field, input_port: int) -> Tuple[List[int], List[Field]]: ports: List[int] = [] fields: List[Field] = [] if (self._stack_waiting.get(neighbor) is not None): # The last field should be the current one, debug: (unitest) if (field != self._stack_waiting[neighbor][1][-1]): util.warning_terminal("The last field of coprop stack should " "be the current field.") wait_policy = self._stack_wait_policy[neighbor] #debug if (wait_policy is None): util.warning_terminal( "wait_policy shouldn't be None if " "self._stack_waiting.get(neighbor) is not None") ports_ = [] fields_ = [] for i, elem in enumerate(self._stack_waiting[neighbor][0][:-1]): if (elem in wait_policy): ports.append(elem) fields.append(self._stack_waiting[neighbor][1][i]) else: ports_.append(elem) fields_.append(self._stack_waiting[neighbor][1][i]) self._stack_waiting[neighbor] = (ports_, fields_) # Clear variables if (not self._stack_waiting[neighbor][0]): self._stack_waiting.pop(neighbor) self._stack_wait_policy.pop(neighbor) return ports, fields
def _del_edge(self, comp_1: AbstractComponent, port_comp_1: int, comp_2: AbstractComponent, port_comp_2: int) -> None: """Delete an edge in the Layout. Parameters ---------- comp_1 : AbstractComponent The first component of the edge. port_comp_1 : int The port of the first component. comp_2 : AbstractComponent The second component of the edge. port_comp_2 : int The port of the second component. """ link = (comp_1.is_linked_to(port_comp_1, comp_2, port_comp_2) and comp_2.is_linked_to(port_comp_2, comp_1, port_comp_1)) link_unidir = (comp_1.is_linked_to(port_comp_1, comp_2, port_comp_2) and comp_2.is_linked_unidir_to(port_comp_2, comp_1)) # Link suppression --------------------------------------------- if (link or link_unidir): comp_1.del_port(port_comp_1) comp_2.del_port(port_comp_2) # Link does not exist ------------------------------------------ else: util.warning_terminal("Can not delete a nonexistent edge from " "port {} of component '{}' to port {} of component '{}', " "action aborted:" .format(port_comp_1, comp_1.name, port_comp_2, comp_2.name)) comp_1.print_port_state(port_comp_1) comp_2.print_port_state(port_comp_2)
def extend(self, storage: Storage) -> None: check_channels = (self._nbr_channels == storage.nbr_channels) check_samples = (self._samples == storage.samples) if (check_samples): channels = storage.channels time = storage.time if (not check_channels): diff = storage.nbr_channels - self._nbr_channels if (diff > 0): to_add = np.zeros(((diff, ) + self.channels[0].shape)) self.channels = np.vstack((self.channels, to_add)) to_add = np.zeros(((diff, ) + self.time[0].shape)) self.time = np.vstack((self.time, to_add)) self._nbr_channels = storage.nbr_channels else: to_add = np.zeros(((abs(diff), ) + channels[0].shape)) channels = np.vstack((channels, to_add)) to_add = np.zeros(((abs(diff), ) + time[0].shape)) time = np.vstack((time, to_add)) self.channels = np.hstack((self.channels, channels)) self.time = np.hstack((self.time, time)) space_to_add = storage.space[0] + np.sum(self._space) self._space = np.hstack((self._space, space_to_add)) else: util.warning_terminal("Storages extension aborted: same number of " "samples is needed for extension.") return self
def rk4ip(f: AbstractEquation, waves: Array[cst.NPFT], h: float, z: float) -> Array[cst.NPFT]: if (len(waves) == 1): A = copy.deepcopy(waves[0]) h_h = 0.5 * h A_lin = f.exp_term_lin(waves, 0, h_h) waves[0] = f.term_non_lin(waves, 0) k_0 = h * f.exp_term_lin(waves, 0, h_h) waves[0] = A + 0.5 * k_0 k_1 = h * f.term_non_lin(waves, 0) waves[0] = A + 0.5 * k_1 k_2 = h * f.term_non_lin(waves, 0) waves[0] = A_lin + k_2 waves[0] = f.exp_term_lin(waves, 0, h_h) k_3 = h * f.term_non_lin(waves, 0) waves[0] = A_lin + k_0 / 6 + (k_1 + k_2) / 3 waves[0] = k_3 / 6 + f.exp_term_lin(waves, 0, h_h) return waves else: util.warning_terminal("rk4ip with more than one field " "currently not supported") return waves
def add_port_policy(self, *policy: Tuple[List[int], List[int], bool]) -> None: """Append a new policy to automatically designate output port depending on the input port. Parameters ---------- policy : The policy (a, b, flag) assigns input ports in list a to output ports in list b. If flag is True, also assign input ports of b to output ports of a. If there is -1 in list b, the field entering at the corresponding port in list a will not be transmitted. N.B.: if (len(a) < len(b)), pad b with length of a. len(a) must be >= len(b) """ for pol in policy: # Entry ports list must be same size as exit ports list if (len(pol[0]) >= len(pol[1])): pol_in = util.permutations(pol[0]) pol_out = util.permutations( util.pad_with_last_elem(pol[1], len(pol[0]))) for i in range(len(pol_in)): self._port_policy[tuple(pol_in[i])] = tuple(pol_out[i]) if (pol[2]): self._port_policy[tuple(pol_out[i])] = tuple(pol_in[i]) else: util.warning_terminal( "The number of entry ports must be " "equal or greater than the number of exit ports.")
def rk4ip_gnlse(f: AbstractEquation, waves: Array[cst.NPFT], h: float, z: float) -> Array[cst.NPFT]: if (len(waves) == 1): h_h = 0.5 * h A = copy.deepcopy(waves[0]) exp_op_lin = f.exp_op_lin(waves, 0, h_h) #if (Solver.rk4ip_gnlse.first_rk4ip_gnlse_iter): A_lin = exp_op_lin * FFT.fft(A) # Solver.rk4ip_gnlse.first_rk4ip_gnlse_iter = False #else: # A_lin = exp_op_lin * Solver.rk4ip_gnlse.fft_A_next k_0 = h * exp_op_lin * f.op_non_lin_rk4ip(waves, 0) waves[0] = FFT.ifft(A_lin + k_0 / 2) k_1 = h * f.op_non_lin_rk4ip(waves, 0) waves[0] = FFT.ifft(A_lin + k_1 / 2) k_2 = h * f.op_non_lin_rk4ip(waves, 0) waves[0] = FFT.ifft(exp_op_lin * (A_lin + k_0 / 2)) k_3 = h * f.op_non_lin_rk4ip(waves, 0) #Solver.rk4ip_gnlse.fft_A_next = k_3/6 + (exp_op_lin # * (A_lin + k_0/6 + (k_1+k_2)/3)) waves[0] = FFT.ifft(k_3 / 6 + (exp_op_lin * (A_lin + k_0 / 6 + (k_1 + k_2) / 3))) return waves else: util.warning_terminal("rk4ip with more than two fields " "currently not supported") return waves
def __setitem__(self, key: int, channel: Array[cst.NPFT, 1, ...]) -> None: if (len(channel) == len(self._channels[key])): self._channels[key] = channel.astype(cst.NPFT) else: util.warning_terminal("All channels must have the same dimension, " "can not change channel.")
def __call__(self, domain: Domain) -> Tuple[List[int], List[Field]]: output_ports: List[int] = [] output_fields: List[Field] = [] field = Field(domain, cst.OPTI, self.field_name) # Check offset ------------------------------------------------- for i in range(len(self.offset_nu)): if (abs(self.offset_nu[i]) > domain.nu_window): self.offset_nu[i] = 0.0 util.warning_terminal( "The offset of channel {} in component " "{} is bigger than half the frequency window, offset will " "be ignored.".format(str(i), self.name)) # Field initialization ----------------------------------------- if (self.energy): peak_power: List[float] = [] time_window = domain.time_window * 1e-12 # ps -> s for i in range(len(self.energy)): peak_power.append(self.energy[i] / time_window) else: peak_power = self.peak_power rep_freq = np.nan for i in range(self.channels): # Nbr of channels res = np.zeros(domain.time.shape, dtype=cst.NPFT) phi = (self.init_phi[i] - Domain.nu_to_omega(self.offset_nu[i]) * domain.time) res += math.sqrt(peak_power[i]) * np.exp(1j * phi) field.add_channel(res, Domain.lambda_to_omega(self.center_lambda[i]), rep_freq) output_fields.append(field) output_ports.append(0) return output_ports, output_fields
def __call__(self, domain: Domain, ports: List[int], fields: List[Field]) -> Tuple[List[int], List[Field]]: output_ports: List[int] = [] output_fields: List[Field] = [] # Check if the fields are of same types ------------------------ compatible = True for i in range(1, len(fields)): if (fields[i].type != fields[i - 1].type): compatible = False util.warning_terminal("Fields of different types can not be " "combined.") # Combine the wave in list fields ------------------------------ if (compatible): if (self._combine): fields[0] *= math.sqrt(self._ratios[ports[0]]) for i in range(1, len(fields)): ratio = math.sqrt(self._ratios[ports[i]]) fields[0].add(fields[i] * math.sqrt(self._ratios[ports[i]])) for i in range(len(fields) - 1, 0, -1): del fields[i] return self.output_ports([ports[0]]), [fields[0]] else: for i in range(len(fields)): output_fields.append(fields[i] * math.sqrt(self._ratios[ports[i]])) return self.output_ports(ports), output_fields
def __init__(self, SPM: bool = False, XPM: bool = False, FWM: bool = False, sigma: float = cst.XPM_COEFF) -> None: r""" Parameters ---------- SPM : If True, trigger the self-phase modulation. XPM : If True, trigger the cross-phase modulation. FWM : If True, trigger the Four-Wave mixing. sigma : Positive term multiplying the XPM term. """ super().__init__() self._SPM: bool = SPM self._XPM: bool = XPM self._FWM: bool = FWM if (self._FWM): util.warning_terminal("FWM effect currently not taken into" "account.") self._sigma: float = sigma
def newton_raphson(func, z, h, error=1e-6, max_iter=1e6): eval = func(z) if (not isinstance(z, np.ndarray)): root_0 = 0. root_1 = z i = 0 while (not i or abs(root_1 - root_0) > error and i < max_iter): eval = func(root_1) jacob = Jacobian.calc_jacobian(func, root_1, h) jacob_inv = 1 / jacob root_0 = root_1 root_1 = root_1 - jacob_inv * eval i += 1 else: if (len(z) != len(eval)): util.warning_terminal( "The number of variables and vector " "components must be equal in order to calculate " "determinant, return zeros.") return np.zeros_like(z) else: root_0 = np.zeros_like(z) root_1 = z i = 0 while (not i or np.linalg.norm(root_1 - root_0) > error and i < max_iter): eval = func(root_1) jacob = Jacobian.calc_jacobian(func, z, h) jacob_inv = np.linalg.inv(jacob) root_0 = root_1 root_1 = root_1 - np.matmul(jacob_inv, eval) i += 1 return root_1
def _del_edge(self, port_1: Port, port_2: Port) -> None: """Delete an edge in the Layout. Parameters ---------- port_1 : Port The first port of the edge. port_2 : Port The second port of the edge. """ link = (port_1.is_linked_to(port_2) and port_2.is_linked_to(port_1)) # Link suppression --------------------------------------------- if (link): port_1.reset() port_2.reset() # Link does not exist ------------------------------------------ else: util.warning_terminal( "Can not delete a nonexistent edge from " "port {} of component '{}' to port {} of component '{}', " "action aborted:".format(port_1.port, port_1.comp.name, port_2.port, port_2.comp.name)) print(port_1) print(port_2)
def __call__(self, domain: Domain) -> Tuple[List[int], List[Field]]: output_ports: List[int] = [] output_fields: List[Field] = [] field = Field(domain, cst.OPTI) # Bit rate initialization -------------------------------------- nbr_pulses = [] for i in range(self.channels): if (self.bit_rate[i]): nbr_temp = math.floor(domain.time_window * self.bit_rate[i]) if (nbr_temp): nbr_pulses.append(nbr_temp) else: util.warning_terminal( "In component {}: the time window " "is too thin for the bit rate specified, bit rate " "will be ignored".format(self.name)) nbr_pulses.append(1) else: nbr_pulses.append(1) rel_pos = [] for i in range(self.channels): pos_step = 1 / nbr_pulses[i] if (nbr_pulses[i] % 2): # Odd dist_from_center = nbr_pulses[i] // 2 * pos_step else: dist_from_center = (nbr_pulses[i] // 2 - 1) * pos_step + pos_step / 2 rel_pos.append( np.linspace(self.position[i] - dist_from_center, self.position[i] + dist_from_center, num=nbr_pulses[i])) # Check offset ------------------------------------------------- for i in range(len(self.offset_nu)): if (abs(self.offset_nu[i]) > domain.nu_window): self.offset_nu[i] = 0.0 util.warning_terminal( "The offset of channel {} in component " "{} is bigger than half the frequency window, offset will " "be ignored.".format(str(i), self.name)) # Field initialization ----------------------------------------- for i in range(self.channels): # Nbr of channels res = np.zeros(domain.time.shape, dtype=cst.NPFT) for j in range(len(rel_pos[i])): norm_time = domain.get_shift_time( rel_pos[i][j]) / self.width[i] var_time = np.power(norm_time, 2) phi = (self.init_phi[i] - Domain.nu_to_omega(self.offset_nu[i]) * domain.time - 0.5 * self.chirp[i] * var_time) res += (math.sqrt(self.peak_power[i]) / np.cosh(norm_time) * np.exp(1j * phi)) field.append(res, Domain.lambda_to_omega(self.center_lambda[i])) output_fields.append(field) output_ports.append(0) return output_ports, output_fields
def calc_cross_section(omega, dopant ="Yb", predict=None, center_omega=None, N_0=None, N_1=None, T=None): r"""Calculate the emission cross section. There is no analytical formula for the emission cross section. Calculate first the absorption cross sections with the fitting formula from ref. [5]_ and then use the McCumber relations to deduce the emission cross sections. Parameters ---------- omega : The angular frequency. :math:`[rad\cdot ps^{-1}]` dopant : The type of the doped medium. predict : A callable object to predict the cross sections. The variable must be the wavelength. :math:`[nm]` center_omega : The center angular frequency. :math:`[rad\cdot ps^{-1}]` N_0 : The population in ground state. :math:`[nm^{-3}]` N_1 : The population in the excited state. :math:`[nm^{-3}]` T : The temperature. :math:`[K]` Returns ------- : Value of the emission cross section. :math:`[nm^2]` References ---------- .. [5] Valley, G.C., 2001. Modeling cladding-pumped Er/Yb fiber amplifiers. Optical Fiber Technology, 7(1), pp.21-44. """ if (isinstance(omega, float)): res = 0.0 else: res = np.zeros_like(omega) Lambda = Domain.omega_to_lambda(omega) if (predict is None): if (N_0 is not None and N_1 is not None and T is not None and center_omega is not None): sigma_a = Absorption.calc_cross_section(omega, dopant) res = McCumber.calc_cross_section_emission(sigma_a, omega, center_omega, N_0, N_1, T) else: util.warning_terminal("Not enough information to calculate " "the value of the emission cross sections, will return " "null values.") else: # Interpolate the data from csv file with scipy fct res = predict(Lambda)[0] # Take only zeroth deriv. return res
def _fixed_step(self, waves: Array[cst.NPFT], solvers: List[Solver], steps: int, forward: bool = True, start: Optional[int] = None) -> Array[cst.NPFT]: # Saving management -------------------------------------------- enough_space: bool if (self.save_all): if (self._max_nbr_steps): enough_space = True # N.B. -1 when modulo is zero save_step = max(1, int(steps / (self._max_nbr_steps - 1)) - 1) nbr_steps = (steps // save_step) nbr_steps = (nbr_steps + 1) if save_step != 1 else nbr_steps else: enough_space = False nbr_steps = 2 util.warning_terminal("Not enough space for storage.") size_storage = (waves.shape[0], nbr_steps, waves.shape[1]) self._channels = np.zeros(size_storage, dtype=cst.NPFT) self._space = np.zeros(0) # Computation -------------------------------------------------- zs, h = np.linspace(0.0, self._length, steps + 1, True, True) zs = zs[:-1] # Need to make it storage dependent here if (start is not None and self.save_all and enough_space): if (start > 0): for i in range(start): for j in range(len(waves)): self._channels[j][i] = waves[j] self._space = np.append(self._space, zs[i]) if (start < -1): for i in range(1, abs(start)): for j in range(len(waves)): self._channels[j][-i] = waves[j] self._space = np.append(self._space, zs[-i]) if (forward): iter_method = 1 stop = len(zs) start = 0 if start is None else start else: iter_method = -1 stop = -1 start = len(zs) - 1 if start is None else len(zs) + start for i in range(start, stop, iter_method): for id in range(len(solvers)): waves = solvers[id](waves, h, zs[i]) if (self.save_all and enough_space and not i % save_step): for j in range(len(waves)): self._channels[j][int(i / save_step)] = waves[j] self._space = np.append(self._space, zs[i]) return waves
def _respect_port_valid(self, comp: AbstractComponent, neighbor: AbstractComponent, port: int, field: Field, input_port: int) -> bool: """Check if type of field correspond to type of port.""" flag = neighbor.is_port_type_valid(input_port, field) if (not flag): util.warning_terminal("Wrong field type to enter port {} " "of component {}, field will be ignored." .format(input_port, neighbor.name), '') return flag
def _respect_port_in(self, comp: AbstractComponent, neighbor: AbstractComponent, port: int, field: Field, input_port: int) -> bool: """Check if the port allows an input.""" flag = neighbor.is_port_type_in(input_port) if (not flag): util.warning_terminal( "Port {} of component {} does not " "accept input, field will be ignored." .format(input_port, neighbor.name), '') return flag
def is_port_valid(self, port_nbr: int) -> bool: if (0 <= port_nbr < self._nbr_ports): return True else: util.warning_terminal("Port {} out of range, component '{}' have " "only {} port(s).".format( port_nbr, self.name, self._nbr_ports)) return False
def _propagate(self, comp: AbstractComponent, output_ports: List[int], output_fields: List[Field]) -> None: self._update_constraints(comp, output_ports, output_fields) # Debug if (len(output_ports) != len(output_fields)): util.warning_terminal("The length of the output_ports list and " "output_fields list must be equal.") # Propagate for i in range(len(output_ports)): if (output_ports[i] != -1): # Explicit no propag. port policy self._init_propagation(comp, output_ports[i], output_fields[i])
def __init__( self, dopant: str = cst.DEF_FIBER_DOPANT, medium: str = cst.DEF_FIBER_DOPANT, stark_energies: STARK_ENERGIES_TYPE = ([], []) ) -> None: """ Parameters ---------- dopant : The doping medium. medium : The fiber medium. stark_energies : The stark energies of the first level and the second level. The first element of the tuple is a list with all stark energies of the first level and the second element of the tuple is a list with all stark energies of the second level. :math:`[cm^{-1}]` (dopant and medium will be ignored if stark_energies are provided) """ self._dopant: str = util.check_attr_value(dopant.lower(), DOPANT, cst.DEF_FIBER_DOPANT) self._medium: str = util.check_attr_value(medium.lower(), MEDIA[self._dopant], cst.DEF_FIBER_MEDIUM) self._stark_energies: STARK_ENERGIES_TYPE = ([], []) if (stark_energies != ([], [])): self._stark_energies = stark_energies else: data = STARK.get(self._dopant) if (data is not None): stark = data.get(self._medium) if (stark is not None): self._stark_energies = stark else: util.warning_terminal( "The specify medium {} for the " "dopant {} for Mccumber relations is not valid.". format(self._medium, self._dopant)) stark_ = data.get(cst.DEF_FIBER_MEDIUM) if (stark_ is not None): # Should be obvious (for typing) self._stark_energies = stark_ else: util.warning_terminal( "The specify dopant medium {} for " "Mccumber relations is not valid.".format(self._dopant)) data_ = STARK.get(cst.DEF_FIBER_DOPANT) if (data_ is not None): # Should be obvious (for typing) stark_ = data_.get(cst.DEF_FIBER_MEDIUM) if (stark_ is not None): # Same, it is obvious self._stark_energies = stark_
def h_R(self) -> Array[float]: if (self._h_R is None): if (self._time is None): util.warning_terminal("Must specified time array to" "calculate the Raman response function") else: self._h_R = self.calc_h_R(self._time, self._tau_1, self._tau_2, self._tau_b, self._f_a, self._f_b, self._f_c) # Save fft of h_R bcs might be reused and not change self._fft_h_R = FFT.fft(self._h_R) return self._h_R
def S(self) -> Array[float]: if (self._S is not None): return self._S else: if (self._omega is None): util.warning_terminal("Must specified the center omega " "to calculate the self steepening coefficient, has been " "evaluated at zero.") return np.zeros(self._center_omega.shape) else: return 1.0 / self._center_omega
def _shooting_method(self, waves: Array[cst.NPFT], solvers: List[Solver], eqs: List[AbstractEquation], steps: int, step_method: STEP_METHOD_TYPE) -> Array[cst.NPFT]: # N.B.: take last equation to get criterion by defaults # -> need to handle different cases that might pop up def apply_ic(eqs, waves_init, end): res = np.zeros_like(waves_init) for eq in eqs: res = eq.initial_condition(waves_init, end) return res error_bound = self._error # First iteration for co-prop or counter prop scheme if (self._start_shooting_forward): waves_f = apply_ic(eqs, waves, False) waves_f = step_method(waves_f, solvers, steps, True, 1) else: waves_b = apply_ic(eqs, waves, True) waves_b = step_method(waves_b, solvers, steps, False, -2) waves_f = apply_ic(eqs, waves, False) waves_f = step_method(waves_f, solvers, steps, True, 1) # Start loop till convergence criterion is met cached_criterion = 0.0 error = error_bound * 1e10 while (error > error_bound): waves_f_back_up = waves_f waves_b = apply_ic(eqs, waves_f, True) waves_b = step_method(waves_b, solvers, steps, False, -2) waves_f = apply_ic(eqs, waves_b, False) waves_f = step_method(waves_f, solvers, steps, True, 1) new_cached_criterion = eqs[-1].get_criterion(waves_f, waves_b) old_error = error error = abs(cached_criterion - new_cached_criterion) util.print_terminal( "Shooting method is running and got error = {}".format(error), '') cached_criterion = new_cached_criterion if (old_error < error): util.warning_terminal( "Shooting method stopped before " "reaching error bound value as error is increasing.") error = 0.0 waves_f = waves_f_back_up return waves_f
def _respect_max_pass(self, comp: AbstractComponent, neighbor: AbstractComponent, port: int, field: Field, input_port: int) -> bool: """Check if the number of time a field can pass by input_port of component neighbor is not overpassed. """ neighbor.inc_counter_pass(input_port) flag = neighbor.is_counter_below_max_pass(input_port) if (not flag): util.warning_terminal( "Max number of times a field can go through " "port {} of component '{}' has been reached, field will be " "ignored.".format(input_port, neighbor.name), '') return flag
def _get_coprop(self, comp: AbstractComponent, neighbor: AbstractComponent, port: int, field: Field, input_port: int) -> Tuple[List[int], List[Field]]: ports: List[int] = [] fields: List[Field] = [] if (self._stack_coprop.get((comp, port)) is not None): # The last field should be the current one, debug: (unitest) if (field != self._stack_coprop[(comp, port)][0][-1]): util.warning_terminal("The last field of coprop stack should " "be the current field.") fields = self._stack_coprop[(comp, port)][0][:-1] ports = [input_port for i in range(len(fields))] self._stack_coprop.pop((comp, port)) return ports, fields