def marker_positions(self): center_port = Port(self._origin, self._angle, 1.) angle = max((2 + max(self._in_ports, self._out_ports)) / 2. * self._angular_spacing, np.pi / 4) radius = max(self._radius, 40) return [ center_port.rotated(angle).longitudinal_offset(radius).origin, center_port.rotated(angle).longitudinal_offset(-radius).origin, center_port.rotated(-angle).longitudinal_offset(radius).origin, center_port.rotated(-angle).longitudinal_offset(-radius).origin ]
class Spiral: def __init__(self, origin, angle, width, num, gap, inner_gap): """ Creates a Spiral around the given origin :param origin: position of the center of the spiral :param angle: angle of the outer two waveguides :param width: width of the waveguide :param num: number of turns :param gap: gap between two waveguides :param inner_gap: inner radius of the spiral """ self._origin_port = Port(origin, angle, width) self.gap = gap self.inner_gap = inner_gap self.num = num self.wg_in = None self.wg_out = None @classmethod def make_at_port(cls, port, num, gap, inner_gap, **kwargs): default_port_param = dict(port.get_parameters()) default_port_param.update(kwargs) del default_port_param['origin'] del default_port_param['angle'] del default_port_param['width'] return cls( port.parallel_offset(-num * (port.width + gap) - inner_gap).origin, port.angle, port.width, num, gap, inner_gap, **default_port_param) ### # Let's allow the user to change the values # hidden in _origin_port. Hence the internal use # of a Port is transparent. @property def origin(self): return self._origin_port.origin @origin.setter def origin(self, origin): self._origin_port.origin = origin @property def angle(self): return self._origin_port.angle @angle.setter def angle(self, angle): self._origin_port.angle = angle @property def width(self): return self._origin_port.width @width.setter def width(self, width): self._origin_port.width = width @property def in_port(self): return self._origin_port.inverted_direction.parallel_offset( -self.num * (self._origin_port.width + self.gap) - self.inner_gap) @property def out_port(self): return self._origin_port.parallel_offset( -self.num * (self._origin_port.width + self.gap) - self.inner_gap) @property def length(self): if not self.wg_in or not self.wg_out: self._generate() return self.wg_in.length + self.wg_out.length def _generate(self): def path(a): return (self.num * (self.width + self.gap) * np.abs(1 - a) + self.inner_gap) * np.array((np.sin( np.pi * a * self.num), np.cos(np.pi * a * self.num))) self.wg_in = Waveguide.make_at_port(self._origin_port) self.wg_in.add_parameterized_path(path) self.wg_out = Waveguide.make_at_port( self._origin_port.inverted_direction) self.wg_out.add_parameterized_path(path) self.wg_in.add_route_single_circle_to_port( self._origin_port.rotated(-np.pi * (self.num % 2))) self.wg_in.add_route_single_circle_to_port(self.wg_out.port) def get_shapely_object(self): if not self.wg_in or not self.wg_out: self._generate() return geometric_union([self.wg_in, self.wg_out])
def _calculate(self, do_in_wgs=True, do_out_wgs=True): angular_spacing = self._angular_spacing assert angular_spacing * self._out_ports < np.pi, 'Not enough space for output ports' assert angular_spacing * self._in_ports < np.pi, 'Not enough space for input ports' if do_out_wgs: # Do the output side out_origin_port = Port(self._origin, self._angle, 1.).longitudinal_offset( -self._displacement / 2) out_fanout_wgs = [ Waveguide.make_at_port( out_origin_port.rotated(angle).longitudinal_offset( self._radius)) for angle in (np.arange(self._out_ports) - (self._out_ports - 1) / 2.) * angular_spacing ] for wg in out_fanout_wgs: wg.add_parameterized_path( path=lambda t: [t * self._taper_length, np.zeros_like(t)], path_derivative=lambda t: [np.ones_like(t) * self._taper_length, np.zeros_like(t)], path_function_supports_numpy=True, width=self._taper_function, **self._taper_options) if self._minimal_final_spacing is None: for wg in out_fanout_wgs: wg.add_bend(normalize_phase(-wg.angle + self._angle), self._wg_bend_radius) else: offsets = ( np.arange(self._out_ports) - (self._out_ports - 1) / 2.) * self._minimal_final_spacing final_port_heights = [ out_origin_port.parallel_offset(offset) for offset in offsets ] for wg, final_port_height, offset in zip( out_fanout_wgs, final_port_heights, offsets): if np.isclose(offset, 0): continue try: wg.add_route_single_circle_to_port( final_port_height.inverted_direction, on_line_only=True) except AssertionError: # No curve possible, use normal bend wg.add_bend(normalize_phase(-wg.angle + self._angle), self._wg_bend_radius) final_ports = [wg.current_port for wg in out_fanout_wgs] for wg in out_fanout_wgs: wg.add_straight_segment_until_level_of_port(final_ports) self._out_wgs = out_fanout_wgs if do_in_wgs: # Do the input side in_origin_port = Port(self._origin, self._angle + np.pi, 1.).longitudinal_offset(-self._displacement / 2) in_fanout_wgs = [ Waveguide.make_at_port( in_origin_port.rotated(angle).longitudinal_offset( self._radius)) for angle in (np.arange(self._in_ports) - (self._in_ports - 1) / 2.) * angular_spacing ] for wg in in_fanout_wgs: wg.add_parameterized_path( path=lambda t: [t * self._taper_length, np.zeros_like(t)], path_derivative=lambda t: [np.ones_like(t) * self._taper_length, np.zeros_like(t)], path_function_supports_numpy=True, width=self._taper_function, **self._taper_options) if self._minimal_final_spacing is None: for wg in in_fanout_wgs: wg.add_bend( normalize_phase(-wg.angle + self._angle - np.pi), self._wg_bend_radius) else: offsets = ( np.arange(self._in_ports) - (self._in_ports - 1) / 2.) * self._minimal_final_spacing final_port_heights = [ in_origin_port.parallel_offset(offset) for offset in offsets ] for wg, final_port_height, offset in zip( in_fanout_wgs, final_port_heights, offsets): if np.isclose(offset, 0): continue # wg.add_route_single_circle_to_port(final_port_height.inverted_direction, on_line_only=True) try: wg.add_route_single_circle_to_port( final_port_height.inverted_direction, on_line_only=True) except AssertionError: # No curve possible, use normal bend wg.add_bend( normalize_phase(-wg.angle + self._angle - np.pi), self._wg_bend_radius) final_ports = [wg.current_port for wg in in_fanout_wgs] for wg in in_fanout_wgs: wg.add_straight_segment_until_level_of_port(final_ports) self._in_wgs = in_fanout_wgs