Step 2: Build basic circuits with basic building blocks

This case implements the building of two basic circuits using the base building blocks, including:

DirectionalCoupler

Import the base building blocks created in step 1(Step 1: Build basic building blocks), where bend waveguides and straight waveguides are used:

from step.step1.straight import Straight
from step.step1.bend_circular import BendCircular

Define class DirectionalCouplerBend to easily call directly after:

class DirectionalCouplerBend(fp.PCell):

      coupler_spacing: float = fp.PositiveFloatParam(default=0.7, doc="Spacing between the two waveguide centre lines.")
      coupler_length: float = fp.PositiveFloatParam(default=6, doc="Length of the directional coupler")
      bend_radius: float = fp.PositiveFloatParam(required=False, doc="Bend radius for the auto-generated bends")
      straight_after_bend: float = fp.PositiveFloatParam(default=6, doc="Length of the straight waveguide after the bend")
      waveguide_type: CoreCladdingWaveguideType = fp.WaveguideTypeParam(type=CoreCladdingWaveguideType, doc="Waveguide parameters")
      port_names: fp.IPortOptions = fp.PortOptionsParam(count=4, default=["op_0", "op_1", "op_2", "op_3"])

      def _default_waveguide_type(self):
          return get_technology().WG.FWG.C.WIRE

      def build(self) -> Tuple[fp.InstanceSet, fp.ElementSet, fp.PortSet]:
          insts, elems, ports = super().build()
          # fmt: off
          coupler_spacing = self.coupler_spacing
          coupler_length = self.coupler_length
          bend_radius = self.bend_radius
          straight_after_bend = self.straight_after_bend
          waveguide_type = self.waveguide_type
          port_names = self.port_names

          if bend_radius is None:
              bend_radius = cast(float, waveguide_type.BEND_CIRCULAR.radius_eff)  # type: ignore

          dy = coupler_spacing / 2 + waveguide_type.core_width

          left_straight_after_bend = Straight(name="lafterbend", length=straight_after_bend, waveguide_type=waveguide_type)
          right_straight_after_bend = Straight(name="rafterbend",length=straight_after_bend, waveguide_type=waveguide_type)
          left_bend = BendCircular(name="lbend", degrees=90, radius=bend_radius, waveguide_type=waveguide_type)
          right_bend = BendCircular(name="rbend", degrees=90, radius=bend_radius, waveguide_type=waveguide_type)
          straight_coupler = Straight(name="coupler", length=coupler_length, anchor=fp.Anchor.CENTER, waveguide_type=waveguide_type, transform=fp.translate(0, -dy))

          bottom_half = fp.Connected(
              name="bottom",
              joints=[
                  straight_coupler["op_0"] <= left_bend["op_0"],
                  left_bend["op_1"] <= left_straight_after_bend["op_1"],
                  #
                  straight_coupler["op_1"] <= right_bend["op_1"],
                  right_bend["op_0"] <= right_straight_after_bend["op_0"],
              ],
              ports=[
                  left_straight_after_bend["op_0"],
                  right_straight_after_bend["op_1"],
              ],
          )
          insts += bottom_half
          top_half = bottom_half.v_mirrored()
          insts += top_half
          ports += top_half["op_0"].with_name(port_names[0])
          ports += bottom_half["op_0"].with_name(port_names[1]),
          ports += bottom_half["op_1"].with_name(port_names[2]),
          ports += top_half["op_1"].with_name(port_names[3]),

          # fmt: on
          return insts, elems, ports

call class, generate directional coupler layout file and plot:

library += DirectionalCouplerBend(name="f", coupler_spacing=0.5, coupler_length=6, bend_radius=10, straight_after_bend=6, waveguide_type=TECH.WG.FWG.C.WIRE)
  fp.export_gds(library, file=gds_file)
  fp.plot(library) += BendCircular(name="s", radius=15, waveguide_type=TECH.WG.FWG.C.WIRE)
  fp.export_gds(library, file=gds_file)
  fp.plot(library)

Directional Coupler:

../_images/DirectionalCoupler.png

MMI

Import the base building blocks created in step 1(Step 1: Build basic building blocks), where straight waveguides and taper waveguides are used:

from step.step1.straight import Straight
  from step.step1.taper_linear import TaperLinear

Define class MMI1x2, to easily call directly after:

class MMI1x2(fp.PCell):
      mid_wav_core_width: float = fp.PositiveFloatParam(default=5)
      wav_core_width: float = fp.PositiveFloatParam(default=1.5)
      length: float = fp.PositiveFloatParam(default=10)
      transition_length: float = fp.PositiveFloatParam(default=5)
      trace_spacing: float = fp.PositiveFloatParam(default=1)
      waveguide_type: CoreCladdingWaveguideType = fp.WaveguideTypeParam(type=CoreCladdingWaveguideType)

      def _default_waveguide_type(self):
          return get_technology().WG.FWG.C.WIRE

      def build(self) -> Tuple[fp.InstanceSet, fp.ElementSet, fp.PortSet]:
          insts, elems, ports = super().build()
          # fmt: off
          mid_wav_core_width = self.mid_wav_core_width
          wav_core_width=self.wav_core_width
          length = self.length
          transition_length = self.transition_length
          trace_spacing = self.trace_spacing
          waveguide_type = self.waveguide_type

          center_force_cladding_width = mid_wav_core_width+waveguide_type.cladding_width
          center_type = waveguide_type.updated(core_layout_width=mid_wav_core_width, cladding_layout_width=center_force_cladding_width)
          center = Straight(length=length, waveguide_type=center_type, anchor=fp.Anchor.START)
          insts += center

          wide_type = waveguide_type.updated(core_layout_width=wav_core_width, cladding_layout_width=waveguide_type.cladding_width + wav_core_width)
          narrow_type = waveguide_type
          taper_left = TaperLinear(length=transition_length, left_type=narrow_type, right_type=wide_type, anchor=fp.Anchor.END)
          taper_right = TaperLinear(length=transition_length, left_type=wide_type, right_type=narrow_type, anchor=fp.Anchor.START)


          taper_left_inst = taper_left.translated(0, 0)
          insts += taper_left_inst
          ports += taper_left_inst["op_0"].with_name("op_0")

          taper_right_inst1 = taper_right.translated(length, -(wav_core_width+trace_spacing)/2)
          insts += taper_right_inst1
          ports += taper_right_inst1["op_1"].with_name(f"op_1")
          taper_right_inst2 = taper_right.translated(length, (wav_core_width+trace_spacing)/2)
          insts += taper_right_inst2
          ports += taper_right_inst2["op_1"].with_name(f"op_2")

          # fmt: on
          return insts, elems, ports

call class, generate MMI layout file and plot:

library += MMI1x2()
  fp.export_gds(library, file=gds_file)
  fp.plot(library)

MMI:

../_images/MMI12.png