Skip to content

Component library

ph.library builds parameterised photonic components as ready-to-use geometry plus labelled Port definitions.

Types

Component dataclass

Component(
    structures: Tuple[Structure, ...],
    ports: Tuple[Port, ...],
)

The geometry a builder emits plus the ports it exposes.

structures instance-attribute

structures: Tuple[Structure, ...]

ports instance-attribute

ports: Tuple[Port, ...]

Port dataclass

Port(
    name: str,
    center_um: Tuple[float, float, float],
    axis: str,
    width_um: float,
    out_direction: Optional[str] = None,
    thickness_um: Optional[float] = None,
    thickness_axis: str = "z",
    mode: str = "TE0",
    medium: Optional[Medium] = None,
    window_um: Optional[Tuple[float, float]] = None,
    source_offset_um: Optional[float] = None,
    dl_um: Optional[float] = None,
    supersample: int = 8,
    num_modes: Optional[int] = None,
)

A waveguide port: the plane where a mode is launched or read out.

center_um is the port's centre in the user's frame; its component along axis is the readout plane and the other two are the waveguide's transverse centre. width_um and thickness_um size the default mode window, width/2 + pad by thickness/2 + pad: the pad reaches 20 % beyond the distance at which the mode's intensity is 30 dB below its peak, at the longest wavelength the port reads, from the mode's effective index and the highest cladding index at the core's faces (the rule of photonhub.mode_window_um, refined by the port's own mode solve). window_um gives the two half-extents explicitly. mode is a family and an index in one string ("TE0", "TM1"). out_direction is the side of the plane that faces the domain wall, "+" or "-"; None lets the simulation infer it from where the port sits relative to the device. source_offset_um is how far behind the plane, toward the wall, a launch sits when this port is driven. It is a LENGTH, defaulting to a third of a wavelength in the background: what the launch injects that is not the guide's discrete mode interferes with it at this port's plane, at a phase set by the distance in microns, so a standoff counted in cells would move that phase with the mesh and this port's reading would drift with it. medium is the port waveguide's material, used to extend the guide through the wall. dl_um, supersample and num_modes are the mode-solve frame controls.

sim.ports reads each port back as a HeldPort, a Port whose center_um is in that simulation's stored frame, from the fitted box's low corner. dataclasses.replace and a copy of the object keep it so. A new Port built from its fields, Port(held.name, held.center_um, ...) or Port(**dataclasses.asdict(held)), is a new port in the user's frame.

name instance-attribute

name: str

center_um instance-attribute

center_um: Tuple[float, float, float]

axis instance-attribute

axis: str

width_um instance-attribute

width_um: float

out_direction class-attribute instance-attribute

out_direction: Optional[str] = None

thickness_um class-attribute instance-attribute

thickness_um: Optional[float] = None

thickness_axis class-attribute instance-attribute

thickness_axis: str = 'z'

mode class-attribute instance-attribute

mode: str = 'TE0'

medium class-attribute instance-attribute

medium: Optional[Medium] = None

window_um class-attribute instance-attribute

window_um: Optional[Tuple[float, float]] = None

source_offset_um class-attribute instance-attribute

source_offset_um: Optional[float] = None

dl_um class-attribute instance-attribute

dl_um: Optional[float] = None

supersample class-attribute instance-attribute

supersample: int = 8

num_modes class-attribute instance-attribute

num_modes: Optional[int] = None

monitor_name property

monitor_name: str

The portable filename token that names this port's readout monitor: the name itself when it already is one, otherwise + spelled _plus and any other character outside letters, digits, _, -, . replaced by _ (a library crossing's "x+" reads out as "x_plus"). Results are looked up by the port name, not this.

plane_um property

plane_um: float

The readout plane: the centre's component along axis.

in_direction property

in_direction: Optional[str]

The launch direction when this port is driven, into the device.

family_index

family_index() -> Tuple[str, int]

("TE", 0) for mode="TE0".

hv_center_um

hv_center_um() -> Tuple[float, float]

The transverse centre in the plane's natural (horizontal, vertical) order, the order the Yee mode solver takes.

with_out_direction

with_out_direction(out_direction: str) -> 'Port'

A copy with out_direction set (the simulation infers it when None).

SILICON module-attribute

SILICON = Medium(permittivity=12.25)

Components

straight

straight(
    length_um: float,
    *,
    width_um: float = DEFAULT_WIDTH_UM,
    thickness_um: float = DEFAULT_THICKNESS_UM,
    medium: Medium = SILICON,
    center_um: Vec3Um = (0.0, 0.0, 0.0),
    axis: AxisName = "x",
    thickness_axis: AxisName = "z",
) -> Component

A straight strip waveguide: one Box with input/output ports at the two ends along axis.

bend

bend(
    radius_um: float,
    *,
    width_um: float = DEFAULT_WIDTH_UM,
    thickness_um: float = DEFAULT_THICKNESS_UM,
    medium: Medium = SILICON,
    center_um: Vec3Um = (0.0, 0.0, 0.0),
    thickness_axis: AxisName = "z",
) -> Component

A 90-degree waveguide bend: an annular-sector Cylinder extruded along thickness_axis (the slab normal), with inner_radius / radius straddling radius_um +/- width_um/2 and a pi/2 sweep.

center_um is the bend's center of curvature. The cylinder sweeps the first quadrant in the transverse (u, v) plane, so the two arc ends point along the two in-plane axes. With the default thickness_axis="z" the transverse plane is (x, y): the start end (angle 0) sits at +u and has a tangent along +v; the stop end (angle pi/2) sits at +v with a tangent along -u. A port's axis names the normal of its mode plane.

taper

taper(
    length_um: float,
    width1_um: float,
    width2_um: float,
    *,
    thickness_um: float = DEFAULT_THICKNESS_UM,
    medium: Medium = SILICON,
    center_um: Vec3Um = (0.0, 0.0, 0.0),
    axis: AxisName = "x",
    thickness_axis: AxisName = "z",
) -> Component

A linear width taper: a trapezoidal Polygon (4 vertices) whose cross-section width grows width1_um -> width2_um along axis. Ports at each end with the local widths.

cosine_taper

cosine_taper(
    length_um: float,
    width1_um: float,
    width2_um: float,
    *,
    peak_width_um: float | None = None,
    n_points: int = 48,
    thickness_um: float = DEFAULT_THICKNESS_UM,
    medium: Medium = SILICON,
    center_um: Vec3Um = (0.0, 0.0, 0.0),
    axis: AxisName = "x",
    thickness_axis: AxisName = "z",
) -> Component

A cosine ("beam shaping") width taper: a curved-sidewall Polygon whose full width follows W(x) = W_m*cos(pi*x/(2*L0)) from width1_um to width2_um along axis (Chandran et al., Opt. Lett. 45, 6230 (2020)). peak_width_um (W_m) is the maximum width reached inside the taper, the convex lens that focuses the beam; it must be >= both end widths and defaults to the wider end (a monotonic cosine taper). The sidewall is discretized into n_points samples per edge; ports carry the end widths.

crossing

crossing(
    *,
    width_um: float = DEFAULT_WIDTH_UM,
    arm_length_um: Optional[float] = None,
    lead_um: Optional[float] = None,
    thickness_um: float = DEFAULT_THICKNESS_UM,
    medium: Medium = SILICON,
    center_um: Vec3Um = (0.0, 0.0, 0.0),
    thickness_axis: AxisName = "z",
) -> Component

A waveguide crossing: two Box waveguides crossed at 90 degrees in the slab plane. Four ports, one per arm end. The arm is given either as arm_length_um, port to port (default 3.0), or as lead_um, the straight guide from the crossing square's edge to each port.

cosine_taper_crossing

cosine_taper_crossing(
    *,
    wg_width_um: float,
    junction_width_um: float,
    peak_width_um: float,
    taper_length_um: float,
    arm_length_um: Optional[float] = None,
    lead_um: Optional[float] = None,
    n_points: int = 48,
    thickness_um: float = DEFAULT_THICKNESS_UM,
    medium: Medium = SILICON,
    center_um: Vec3Um = (0.0, 0.0, 0.0),
    thickness_axis: AxisName = "z",
) -> Component

A beam-shaped ultra-compact waveguide crossing (Chandran et al., Opt. Lett. 45, 6230 (2020)). Four convex cosine tapers connect the single-mode routing guide (wg_width_um = W_in) to a solid central junction of side junction_width_um (= W_out); each taper bulges to a peak width peak_width_um (= W_m) in its middle, the lens that focuses the beam across the intersection so it couples to the through arm instead of scattering into the cross arms.

Each arm runs center -> port along one in-plane axis: the shared central Box junction (W_out square), a cosine taper of taper_length_um (= L_t) from junction_width_um at the junction to wg_width_um at the routing end (peaking at peak_width_um inside), then a straight routing stub out to the port at arm_length_um from center. Four ports, each wg_width_um wide. arm_length_um must be >= junction_width_um/2 + taper_length_um; lead_um gives the stub's length instead, from the taper's end to the port. The paper's footprint is junction_width_um + 2*taper_length_um.

spline_taper_crossing

spline_taper_crossing(
    *,
    wg_width_um: float,
    taper_length_um: float,
    widths_um,
    arm_length_um: Optional[float] = None,
    lead_um: Optional[float] = None,
    interpolation: str = "natural",
    n_points: int = 121,
    thickness_um: float = DEFAULT_THICKNESS_UM,
    medium: Medium = SILICON,
    center_um: Vec3Um = (0.0, 0.0, 0.0),
    thickness_axis: AxisName = "z",
) -> Component

A single-etch waveguide crossing made of four identical knot-defined tapers that share a centre point (Ma et al., Opt. Express 21, 29374 (2013), and the Sanchis et al. 2009 / Zhang et al. 2013 designs it descends from).

Each arm runs centre -> port along one in-plane axis: a taper of taper_length_um (= L) whose full width is the interpolation curve (see spline_width_profile) through the equally spaced knots widths_um, listed the paper's way, w1 at the routing-waveguide end and wN at the crossing centre, followed by a straight routing stub of wg_width_um out to the port at arm_length_um from centre (or lead_um beyond the taper's end). The four tapers overlap at the centre; their union is the device (the wide knots next to the centre "merge into the cross-sectional region", as the paper puts it), so the near-centre width profile is buried inside the junction and only the outer profile is a visible sidewall. Each taper sidewall is sampled at n_points positions. Four ports (x-, x+, y-, y+), each wg_width_um wide. The paper's footprint is 2 * taper_length_um.

spline_width_profile

spline_width_profile(
    widths_um,
    length_um: float,
    *,
    interpolation: str = "natural",
)

The width-vs-position function w(s) of a knot-defined taper.

widths_um are N >= 2 knot widths equally spaced along the taper, listed from the routing-waveguide end (s = 0) to the far end (s = length_um). interpolation is the curve drawn through them: "natural" (cubic spline, zero end curvature, the default and the classic "spline interpolation" of the Ma et al. crossing), "not-a-knot" (SciPy's default cubic end condition), "pchip" (shape-preserving, never overshoots a knot) or "linear" (straight segments, the 13-segment convention of the classic Y-junction example). Returns a callable w(s) accepting a scalar or an array of positions in [0, length_um].

coupler

coupler(
    length_um: float,
    *,
    width_um: float = DEFAULT_WIDTH_UM,
    gap_um: float = 0.2,
    thickness_um: float = DEFAULT_THICKNESS_UM,
    medium: Medium = SILICON,
    center_um: Vec3Um = (0.0, 0.0, 0.0),
    axis: AxisName = "x",
    thickness_axis: AxisName = "z",
) -> Component

A directional coupler: two parallel straight waveguides separated edge-to-edge by gap_um (along the in-plane width axis). Four ports, two per guide at the ends.

ring

ring(
    radius_um: float,
    *,
    width_um: float = DEFAULT_WIDTH_UM,
    gap_um: float = 0.2,
    bus_width_um: float | None = None,
    bus_length_um: float | None = None,
    thickness_um: float = DEFAULT_THICKNESS_UM,
    medium: Medium = SILICON,
    center_um: Vec3Um = (0.0, 0.0, 0.0),
    axis_normal: AxisName = "z",
) -> Component

An all-pass ring resonator: a full-sweep annular Cylinder (inner = radius - width/2, outer = radius + width/2) plus a straight bus Box one gap_um away (edge-to-edge) from the ring's outer edge.

axis_normal is the slab normal (the ring/cylinder extrusion axis). The bus runs along the lower-indexed in-plane axis; it is offset along the higher-indexed in-plane axis. Two ports on the bus (input + through).

bragg_grating

bragg_grating(
    *,
    wg_width_um: float,
    corrugation_um: float,
    period_um: float,
    n_periods: int,
    arm_length_um: Optional[float] = None,
    lead_um: Optional[float] = None,
    duty: float = 0.5,
    thickness_um: float = DEFAULT_THICKNESS_UM,
    medium: Medium = SILICON,
    center_um: Vec3Um = (0.0, 0.0, 0.0),
    axis: AxisName = "x",
    thickness_axis: AxisName = "z",
) -> Component

A sidewall-corrugated waveguide Bragg grating (Wang et al., Opt. Express 20, 15547 (2012), strip variant). A straight strip of width wg_width_um whose two sidewalls corrugate SYMMETRICALLY (equal ± on each side, so the average width, hence the average effective index, is constant): over a period_um the full width alternates between wg_width + 2*corrugation (wide, a duty fraction of the period) and wg_width - 2*corrugation (narrow). n_periods teeth open a photonic stopband at the Bragg wavelength λ_B ≈ 2*n_eff*period; corrugation_um (the per-side tooth amplitude) sets the coupling coefficient κ, hence the stopband width. Plain routing stubs of the nominal wg_width run out to the two ports at ±arm_length_um (in at −axis, through at +axis), or lead_um beyond the grating's ends.

y_branch

y_branch(
    *,
    wg_width_um: float,
    splitting_length_um: float,
    gap_um: float,
    arm_length_um: Optional[float] = None,
    lead_um: Optional[float] = None,
    top_radii: Tuple[float, float],
    bot_radii: Tuple[float, float],
    top_straight_um: float = 0.0,
    bot_straight_um: float = 0.0,
    output_offset_final_um: float | None = None,
    fanout_length_um: float = 0.0,
    region_halfwidth_um: float | None = None,
    solid_junction: bool = False,
    branch_tip_frac: float | None = None,
    tip_gap_um: float = 0.0,
    n_points: int = 60,
    thickness_um: float = DEFAULT_THICKNESS_UM,
    medium: Medium = SILICON,
    center_um: Vec3Um = (0.0, 0.0, 0.0),
    axis: AxisName = "x",
    thickness_axis: AxisName = "z",
) -> Component

An asymmetric S-curve 1x2 Y-junction splitter with an arbitrary power splitting ratio (Lin & Shi, Opt. Express 27, 14338 (2019)). In the paper's widened solid_junction construction, top_radii=(r1, r2) and bot_radii=(R1, R2) describe TWO successive S-shaped OUTER-BOUNDARY segments per side: the first expands the input edge from w0/2 to the published half-width w and the second contracts it to the outer edge of the output guide. Each S segment is itself made from two equal-radius tangent arcs. The paper's straight l lies between the two top-boundary S segments; any remaining length is a straight lead-in at the input width. This is not an output-arm-centreline construction: treating (r1, r2) as the two arcs of one centreline S-bend erases most of the paper's split-ratio asymmetry.

solid_junction=False retains the generic constant-width two-arm routing construction. There, each arm follows one two-radius centreline S-bend.

Because the paper's two outputs sit only gap_um apart they would remain evanescently coupled (a directional coupler) and the per-arm power would slosh between them, so for a clean split-ratio readout the arms are fanned apart with a raised-cosine access bend over fanout_length_um to a decoupled output_offset_final_um (default = the junction offset, i.e. no fanout). Three ports: in (−axis), o_top / o_bot (+axis, offset in the width axis), at arm_length_um from the centre or lead_um beyond the splitting region and the access bend.