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_ - MMI_ DirectionalCoupler ------------------------------ Import the base building blocks created in step 1(:doc:`Step1`), 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: .. image:: ../images/DirectionalCoupler.png MMI ------------------------------------------ Import the base building blocks created in step 1(:doc:`Step1`), 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: .. image:: ../images/MMI12.png