"""Preset cross-section factory functions.
Includes strip, rib, slot, nitride, metal, and other standard waveguide
cross-sections.
"""
from __future__ import annotations
from typing import Any
from gdsfactory import typings
from gdsfactory.cross_section.base import (
CrossSection,
Section,
Sections,
nm,
port_names_electrical,
port_types_electrical,
)
from gdsfactory.cross_section.utils import cross_section, xsection
radius_nitride = 20
radius_rib = 20
[docs]
@xsection
def strip(
width: float = 0.5,
layer: typings.LayerSpec = "WG",
radius: float = 10.0,
radius_min: float = 3.5,
**kwargs: Any,
) -> CrossSection:
"""Return Strip cross_section.
Args:
width: main Section width (um).
layer: main section layer.
radius: routing bend radius (um).
radius_min: min acceptable bend radius.
kwargs: cross_section settings.
"""
return cross_section(
width=width,
layer=layer,
radius=radius,
radius_min=radius_min,
**kwargs,
)
[docs]
@xsection
def strip_no_ports(
width: float = 0.5,
layer: typings.LayerSpec = "WG",
radius: float = 10.0,
radius_min: float = 5,
port_names: typings.IOPorts = ("", ""),
**kwargs: Any,
) -> CrossSection:
"""Return Strip cross_section without ports.
Args:
width: main Section width (um).
layer: main section layer.
radius: routing bend radius (um).
radius_min: min acceptable bend radius.
port_names: for input and output ('o1', 'o2').
kwargs: cross_section settings.
"""
return cross_section(
width=width,
layer=layer,
radius=radius,
radius_min=radius_min,
port_names=port_names,
**kwargs,
)
[docs]
@xsection
def rib(
width: float = 0.5,
layer: typings.LayerSpec = "WG",
radius: float = radius_rib,
radius_min: float | None = 7,
cladding_layers: typings.LayerSpecs = ("SLAB90",),
cladding_offsets: typings.Floats = (3,),
cladding_simplify: typings.Floats = (50 * nm,),
**kwargs: Any,
) -> CrossSection:
"""Return Rib cross_section."""
return cross_section(
width=width,
layer=layer,
radius=radius,
radius_min=radius_min,
cladding_layers=cladding_layers,
cladding_offsets=cladding_offsets,
cladding_simplify=cladding_simplify,
**kwargs,
)
[docs]
@xsection
def rib_bbox(
width: float = 0.5,
layer: typings.LayerSpec = "WG",
radius: float = radius_rib,
radius_min: float | None = None,
bbox_layers: typings.LayerSpecs = ("SLAB90",),
bbox_offsets: typings.Floats = (3,),
**kwargs: Any,
) -> CrossSection:
"""Return Rib cross_section."""
return cross_section(
width=width,
layer=layer,
radius=radius,
radius_min=radius_min,
bbox_layers=bbox_layers,
bbox_offsets=bbox_offsets,
**kwargs,
)
[docs]
@xsection
def rib2(
width: float = 0.5,
layer: typings.LayerSpec = "WG",
layer_slab: typings.LayerSpec = "SLAB90",
radius: float = radius_rib,
radius_min: float | None = None,
width_slab: float = 6,
**kwargs: Any,
) -> CrossSection:
"""Return Rib cross_section."""
sections = (
Section(width=width_slab, layer=layer_slab, name="slab", simplify=50 * nm),
)
return cross_section(
width=width,
layer=layer,
radius=radius,
radius_min=radius_min,
sections=sections,
**kwargs,
)
[docs]
@xsection
def nitride(
width: float = 1.0,
layer: typings.LayerSpec = "WGN",
radius: float = radius_nitride,
radius_min: float | None = None,
**kwargs: Any,
) -> CrossSection:
"""Return Strip cross_section."""
return cross_section(
width=width,
layer=layer,
radius=radius,
radius_min=radius_min,
**kwargs,
)
[docs]
@xsection
def strip_rib_tip(
width: float = 0.5,
width_tip: float = 0.2,
layer: typings.LayerSpec = "WG",
layer_slab: typings.LayerSpec = "SLAB90",
radius: float = 10.0,
radius_min: float | None = 5,
**kwargs: Any,
) -> CrossSection:
"""Return Rib tip cross_section."""
sections = (Section(width=width_tip, layer=layer_slab, name="slab"),)
return cross_section(
width=width,
layer=layer,
radius=radius,
radius_min=radius_min,
sections=sections,
**kwargs,
)
[docs]
@xsection
def strip_nitride_tip(
width: float = 1.0,
layer: typings.LayerSpec = "WGN",
layer_silicon: typings.LayerSpec = "WG",
width_tip_nitride: float = 0.2,
width_tip_silicon: float = 0.1,
radius: float = radius_nitride,
radius_min: float | None = None,
**kwargs: Any,
) -> CrossSection:
"""Return the end of the nitride tip.
Args:
width: main Section width (um).
layer: main section layer.
layer_silicon: silicon layer.
width_tip_nitride: in um.
width_tip_silicon: in um.
radius: routing bend radius (um).
radius_min: min acceptable bend radius.
kwargs: cross_section settings.
"""
sections = (
Section(width=width_tip_nitride, layer=layer, name="tip_nitride"),
Section(width=width_tip_silicon, layer=layer_silicon, name="tip_silicon"),
)
return cross_section(
width=width,
layer=layer,
radius=radius,
radius_min=radius_min,
sections=sections,
**kwargs,
)
[docs]
@xsection
def slot(
width: float = 0.5,
layer: typings.LayerSpec = "WG_ABSTRACT",
slot_width: float = 0.04,
rail_layer: typings.LayerSpec = "WG",
sections: Sections | None = None,
**kwargs: Any,
) -> CrossSection:
"""Return CrossSection Slot (with an etched region in the center).
Args:
width: main Section width (um) or function parameterized from 0 to 1. \
the width at t==0 is the width at the beginning of the Path. \
the width at t==1 is the width at the end.
layer: main section layer.
slot_width: in um.
rail_layer: rail layer.
sections: list of Sections(width, offset, layer, ports).
kwargs: other cross section parameters.
.. plot::
:include-source:
import gdsfactory as gf
xs = gf.cross_section.slot(width=0.5, slot_width=0.05, layer='WG')
p = gf.path.arc(radius=10, angle=45)
c = p.extrude(xs)
c.plot()
"""
if slot_width >= width:
raise ValueError(f"{width=} must be greater than {slot_width=}")
rail_width = (width - slot_width) / 2
rail_offset = (rail_width + slot_width) / 2
section_list: list[Section] = list(sections or [])
section_list.extend(
[
Section(
width=rail_width,
offset=+rail_offset,
layer=rail_layer,
port_names=("o3", "o4"),
name="left_rail",
),
Section(
width=rail_width,
offset=-rail_offset,
layer=rail_layer,
port_names=("o5", "o6"),
name="right_rail",
),
]
)
return strip(
width=width,
layer=layer,
sections=section_list,
**kwargs,
)
[docs]
@xsection
def rib_with_trenches(
width: float = 0.5,
width_trench: float = 2.0,
slab_offset: float | None = 0.3,
width_slab: float | None = None,
simplify_slab: float | None = None,
layer: typings.LayerSpec = "WG",
layer_trench: typings.LayerSpec = "DEEP_ETCH",
wg_marking_layer: typings.LayerSpec = "WG_ABSTRACT",
sections: Sections | None = None,
**kwargs: Any,
) -> CrossSection:
"""Return CrossSection of rib waveguide defined by trenches.
Args:
width: main Section width (um) or function parameterized from 0 to 1. \
the width at t==0 is the width at the beginning of the Path. \
the width at t==1 is the width at the end.
width_trench: in um.
slab_offset: from the edge of the trench to the edge of the slab.
width_slab: in um.
simplify_slab: Optional Tolerance value for the simplification algorithm. \
All points that can be removed without changing the resulting\
polygon by more than the value listed here will be removed.
layer: slab layer.
layer_trench: layer to etch trenches.
wg_marking_layer: layer to draw over the actual waveguide. \
This can be useful for booleans, routing, placement ...
sections: list of Sections(width, offset, layer, ports).
kwargs: cross_section settings.
.. code::
┌─────────┐
│ │ wg_marking_layer
└─────────┘
┌────────┐ ┌────────┐
│ │ │ │layer_trench
└────────┘ └────────┘
┌─────────────────────────────────────────┐
│ layer │
│ │
└─────────────────────────────────────────┘
◄─────────►
width
┌─────┐ ┌────────┐ ┌───────┐
│ │ │ │ │ │
│ └─────────┘ └────────┘ │
│ ◄---------► ◄-------► │
└─────────────────────────────────────────┘
slab_offset
width_trench ──────►
|
◄────────────────────────────────────────►
width_slab
.. plot::
:include-source:
import gdsfactory as gf
xs = gf.cross_section.rib_with_trenches(width=0.5)
p = gf.path.arc(radius=10, angle=45)
c = p.extrude(xs)
c.plot()
"""
if slab_offset is None and width_slab is None:
raise ValueError("Must specify either slab_offset or width_slab")
if slab_offset is not None and width_slab is not None:
raise ValueError("Cannot specify both slab_offset and width_slab")
if slab_offset is not None:
width_slab = width + 2 * width_trench + 2 * slab_offset
trench_offset = width / 2 + width_trench / 2
section_list: list[Section] = list(sections or ())
assert width_slab is not None
section_list.append(
Section(width=width_slab, layer=layer, name="slab", simplify=simplify_slab)
)
section_list += [
Section(
width=width_trench, offset=offset, layer=layer_trench, name=f"trench_{i}"
)
for i, offset in enumerate([+trench_offset, -trench_offset])
]
return cross_section(
layer=wg_marking_layer,
width=width,
sections=tuple(section_list),
**kwargs,
)
[docs]
@xsection
def l_with_trenches(
width: float = 0.5,
width_trench: float = 2.0,
width_slab: float = 7.0,
layer: typings.LayerSpec = "WG",
layer_slab: typings.LayerSpec = "WG",
layer_trench: typings.LayerSpec = "DEEP_ETCH",
mirror: bool = False,
sections: Sections | None = None,
**kwargs: Any,
) -> CrossSection:
"""Return CrossSection of l waveguide defined by trenches.
Args:
width: main Section width (um) or function parameterized from 0 to 1. \
the width at t==0 is the width at the beginning of the Path. \
the width at t==1 is the width at the end.
width_trench: in um.
width_slab: in um.
layer: ridge layer. None adds only ridge.
layer_slab: slab layer.
layer_trench: layer to etch trenches.
mirror: this cross section is not symmetric and you can switch orientation.
sections: list of Sections(width, offset, layer, ports).
kwargs: cross_section settings.
.. code::
x = 0
|
|
_____ __________
| | |
|________| |
_________________________
<-------> |
width_trench
<-------->
width
|
<------------------------>
width_slab
.. plot::
:include-source:
import gdsfactory as gf
xs = gf.cross_section.l_with_trenches(width=0.5)
p = gf.path.arc(radius=10, angle=45)
c = p.extrude(xs)
c.plot()
"""
mult = 1 if mirror else -1
trench_offset = mult * (width / 2 + width_trench / 2)
section_list: list[Section] = list(sections or ())
section_list += [
Section(
width=width_slab,
layer=layer_slab,
offset=mult * (width_slab / 2 - width / 2),
)
]
section_list += [
Section(width=width_trench, offset=trench_offset, layer=layer_trench)
]
return cross_section(
width=width,
layer=layer,
sections=tuple(section_list),
**kwargs,
)
[docs]
@xsection
def gs(
trace_width: float = 140,
layer: typings.LayerSpec = "M3",
gap: float = 120,
layer_port: typings.LayerSpec = "M3_ABSTRACT",
radius: float | None = None,
**kwargs: Any,
) -> CrossSection:
"""Return Ground-Signal-Ground cross_section.
Args:
trace_width: in um.
layer: metal layer.
gap: between metal lines in um.
layer_port: port layer.
radius: bend radius. Optional, defaults to 2*width+gap.
kwargs: cross_section settings. (ignored)
"""
width = trace_width
sections = [
Section(
width=gap,
layer=layer_port,
offset=0,
port_names=port_names_electrical,
port_types=port_types_electrical,
),
Section(width=width, layer=layer, offset=+gap / 2 + width / 2),
Section(width=width, layer=layer, offset=-gap / 2 - width / 2),
]
return CrossSection(sections=tuple(sections), radius=radius or 2 * width + gap)
[docs]
@xsection
def gsg(
trace_width: float = 140,
layer: typings.LayerSpec = "M3",
gap: float = 100,
radius: float | None = None,
) -> CrossSection:
"""Return Ground-Signal-Ground cross_section.
Args:
trace_width: in um.
layer: metal layer.
gap: between metal lines in um.
layer_port: port layer.
radius: bend radius. Optional, defaults to 3*width+2*gap.
"""
width = trace_width
sections = [
Section(
width=width,
layer=layer,
offset=0,
port_names=port_names_electrical,
port_types=port_types_electrical,
),
Section(width=width, layer=layer, offset=-gap - width),
Section(width=width, layer=layer, offset=+gap + width),
]
return CrossSection(sections=tuple(sections), radius=radius or 3 * width + 2 * gap)
[docs]
@xsection
def npp(
width: float = 0.5,
layer: typings.LayerSpec = "NPP",
radius: float | None = None,
port_names: typings.IOPorts = port_names_electrical,
port_types: typings.IOPorts = port_types_electrical,
**kwargs: Any,
) -> CrossSection:
"""Return Doped NPP cross_section."""
return cross_section(
width=width,
layer=layer,
radius=radius,
port_names=port_names,
port_types=port_types,
**kwargs,
)