Source code for gvtt.components.bend_euler
from __future__ import annotations
import gdsfactory as gf
import numpy as np
from gdsfactory.add_padding import get_padding_points
from gdsfactory.component import Component
from gdsfactory.components.wire import wire_corner
from gdsfactory.path import euler
from gdsfactory.typings import CrossSectionSpec, Optional
from gvtt.tech import xs_sc
def _eulerR_1550(angle: float) -> float:
if angle == 0:
return 0.0
p, v, a0 = 0.79, 2093.0, 18.75 # for 1550 nm, TE, 1.875 um
return (v / max(abs(angle), a0)) ** (1.0 / p)
[docs]
@gf.cell
def bend_euler(
angle: float = 90.0,
p: float = 1.0,
with_arc_floorplan: bool = False,
npoints: Optional[int] = None,
direction: str = "ccw",
with_bbox: bool = True,
cross_section: CrossSectionSpec = xs_sc,
**kwargs,
) -> Component:
"""Returns an euler bend that transitions from straight to curved.
By default, `radius` corresponds to the minimum radius of curvature of the bend.
However, if `with_arc_floorplan` is True, `radius` corresponds to the effective
radius of curvature (making the curve a drop-in replacement for an arc). If
p < 1.0, will create a "partial euler" curve as described in Vogelbacher et.
al. https://dx.doi.org/10.1364/oe.27.031394
default p = 0.5 based on this paper
https://www.osapublishing.org/oe/fulltext.cfm?uri=oe-25-8-9150&id=362937
Args:
angle: total angle of the curve.
p: Proportion of the curve that is an Euler curve.
with_arc_floorplan: If False: `radius` is the minimum radius of curvature
If True: The curve scales such that the endpoints match a bend_circular
with parameters `radius` and `angle`.
npoints: Number of points used per 360 degrees.
direction: cw (clock-wise) or ccw (counter clock-wise).
with_bbox: add bbox_layers and bbox_offsets to avoid DRC sharp edges.
cross_section: specification (CrossSection, string, CrossSectionFactory dict).
kwargs: cross_section settings.
.. code::
o2
|
/
/
/
o1_____/
"""
dx = gf.get_cross_section(cross_section, **kwargs)
radius = _eulerR_1550(abs(angle))
if radius is None:
return wire_corner(cross_section=dx)
c = Component()
p = euler(
radius=radius, angle=angle, p=p, use_eff=with_arc_floorplan, npoints=npoints
)
ref = c << p.extrude(dx)
c.info["length"] = float(np.round(p.length(), 3))
c.info["dy"] = float(np.round(abs(float(p.points[0][0] - p.points[-1][0])), 3))
c.info["radius_min"] = float(np.round(p.info["Rmin"], 3))
c.info["radius"] = float(p.dxmax)
c.info["width"] = float(dx.width)
if with_bbox and dx.bbox_layers:
padding = []
angle = int(angle)
for offset in dx.bbox_offsets:
top = offset if angle in {180, -180, -90} else 0
bottom = 0 if angle in {-90} else offset
points = get_padding_points(
component=c,
default=0,
bottom=bottom,
right=offset,
top=top,
)
padding.append(points)
for layer, points in zip(dx.bbox_layers, padding):
c.add_polygon(points, layer=layer)
if direction == "cw":
ref.dmirror(p1=[0, 0], p2=[1, 0])
c.add_ports(ref.ports)
return c
if __name__ == "__main__":
c = bend_euler()
c.show()