Path Length Extraction
The i3.path_length
function enables length extraction.
The i3.path_report
function enables extracting a report of all components, paths, and lengths between the specified ports.
- ipkiss3.all.path_length(layout, port1, port2)
Get the path length between two ports. If multiple paths are present, it returns the length of the shortest path.
To calculate the length, the
trace_length
method of each component will be invoked to calculate the length of that component. If notrace_length
method is available the component will be ignored.The calculation of the path length can be customized by registering a custom implementation for the length of a component with the
i3.register_length
decorator.- Parameters:
- layout: LayoutView or Layout or InstanceDict
Circuit to analyze
- port1: str
The name of the first port
- port2: str
The name of the second port
- Returns:
- float
The length of the path
Examples
import si_fab.all as pdk import ipkiss3.all as i3 class Circuit(i3.PCell): class Layout(i3.LayoutView): def generate(self, layout): mmi = pdk.MMI1x2() wg = i3.Waveguide().Layout( shape=[ (0, 0), (20, 0), ] ) layout += i3.place_and_route( insts={ "wg": wg, "mmi1": mmi, "mmi2": mmi, }, specs=[ i3.Join("wg:out", "mmi1:in1"), i3.ConnectManhattan("mmi1:out1", "mmi2:out1"), i3.ConnectManhattan("mmi1:out2", "mmi2:out2", control_points=[i3.H(15.0)]), i3.FlipH("mmi2"), i3.Place("mmi2:in1", (100, -10)), ], ) layout += i3.expose_ports(layout, {"wg:in": "opt1", "mmi2:in1": "opt2"}) return layout layout = Circuit().Layout() layout.visualize(annotate=True) length1 = i3.path_length(layout, "opt1", "opt2") print(length1) # outputs: 81.4155365094152 (MMI lengths not included) @i3.register_length # register a custom length for the MMI def mmi_length(layout: pdk.MMI1x2.Layout, source, target): return 20.0 length2 = i3.path_length(layout, "opt1", "opt2") print(length2) # outputs: 121.4155365094152 (MMI lengths included) assert length2 == length1 + 2 * 20.0
import math import si_fab.all as pdk import ipkiss3.all as i3 @i3.register_length def dc_length(layout: pdk.SiDirectionalCouplerS.Layout, source, target): # Extract the length from the directional coupler's waveguides top, bottom = layout._get_wg_shapes() return top.length() class Circuit(i3.PCell): desired_length = i3.PositiveNumberProperty(default=140.0) class Layout(i3.LayoutView): def generate(self, layout): dc = pdk.SiDirectionalCouplerS() y_control = 20 temp_layout = i3.place_and_route( insts={ "dc1": dc, "dc2": dc, }, specs=[ i3.Place("dc2", (40, 0)), i3.ConnectManhattan("dc1:out1", "dc2:in1", control_points=[i3.H(-y_control)]), ], ) length = i3.path_length(temp_layout, "dc1:in1", "dc2:out1") y_control = y_control + (self.desired_length - length) / 2 layout += i3.place_and_route( insts={ "dc1": dc, "dc2": dc, }, specs=[ i3.Place("dc2", (40, 0)), i3.ConnectManhattan("dc1:out1", "dc2:in1", control_points=[i3.H(-y_control)]), i3.ConnectManhattan("dc1:out2", "dc2:in2"), ], ) length = i3.path_length(layout, "dc1:in1", "dc2:out1") assert math.isclose(length, self.desired_length) # Check if the final arm length is correct return layout layout = Circuit(desired_length=140.0).Layout() layout.visualize()
- ipkiss3.all.path_report(layout, port1, port2)
Outputs a report of all components, paths, and length calculations (results) between the specified ports.
The calculation of the path length can be customized by registering a custom implementation for the length of a component with the
i3.register_length
decorator.- Parameters:
- layout: LayoutView or Layout or InstanceDict
Circuit to analyze
- port1: str
The name of the first port
- port2: str
The name of the second port
- Returns:
- dict
A dictionary where each key is a string identifier for a unique trace (e.g., ‘trace0’). The value is another dictionary containing the detailed breakdown of that trace with the following keys:
‘components’ (list): An ordered list of the IPKISS PCell layout objects along the trace.
‘path’ (list): A list of strings describing the port-to-port connections for each component in the format ‘(start_port, end_port)@instance_name’.
‘results’ (list): A list of floats, where each float is the calculated optical path length (in micrometers) corresponding to each component in ‘components’.
Examples
import si_fab.all as pdk import ipkiss3.all as i3 class Circuit(i3.PCell): class Layout(i3.LayoutView): def generate(self, layout): mmi = pdk.MMI1x2() wg = i3.Waveguide().Layout( shape=[ (0, 0), (20, 0), ] ) layout += i3.place_and_route( insts={ "wg": wg, "mmi1": mmi, "mmi2": mmi, }, specs=[ i3.Join("wg:out", "mmi1:in1"), i3.ConnectManhattan("mmi1:out1", "mmi2:out1"), i3.ConnectManhattan("mmi1:out2", "mmi2:out2", control_points=[i3.H(15.0)]), i3.FlipH("mmi2"), i3.Place("mmi2:in1", (100, -10)), ], ) layout += i3.expose_ports(layout, {"wg:in": "opt1", "mmi2:in1": "opt2"}) return layout layout = Circuit().Layout() layout.visualize(annotate=True) @i3.register_length # register a custom length for the MMI def mmi_length(layout: pdk.MMI1x2.Layout, source, target): return 20.0 # Print a report of all components, paths, and length calculations (results) between "opt1" and "opt2". report = i3.path_report(layout, "opt1", "opt2") print(report) print(report["trace0"]["results"]) # outputs: [20.0, 20.0, 61.415536509415205, 20.0] print(report["trace1"]["results"]) # outputs: [20.0, 20.0, 65.41553650941526, 20.0]
Registering a custom length
A custom implementation of the length calculation can be registered with i3.register_length
.
- ipkiss3.all.register_length(func)
Decorator to register a custom implementation to extract the length.
- Parameters:
- func: Callable
Function to extract the length.
Examples
import si_fab.all as pdk @i3.register_length def mmi_length(layout: pdk.MMI1x2.Layout, source, target): return 20.0
import si_fab.all as pdk @i3.register_length def dc_length(layout: pdk.SiDirectionalCouplerS.Layout, source, target): # Extract the length from the directional coupler's waveguides # This way the returned length varies depending on the actual DC length top, bottom = layout._get_wg_shapes() return top.length()
import si_fab.all as pdk @i3.register_length def mmi_length(layout: pdk.MMI1x2.Layout, source, target): # use python's match/case statements # to declare the lengths between the port pairs match (source, target): case ("in1", "out1") | ("out1" | "in1"): return 20.0 case ("in1", "out2") | ("out2" | "in1"): return 20.0 case _: raise LookupError(f"unknown port pair ({source}, {target})