Source code for gdsfactory.components.bend_euler

from __future__ import annotations

from functools import partial

import numpy as np

import gdsfactory as gf
from gdsfactory.component import Component
from gdsfactory.components.straight import straight
from gdsfactory.components.wire import wire_corner
from gdsfactory.cross_section import strip
from gdsfactory.path import euler
from gdsfactory.typings import CrossSectionSpec


[docs] @gf.cell def bend_euler( radius: float | None = None, angle: float = 90.0, p: float = 0.5, with_arc_floorplan: bool = True, npoints: int | None = None, direction: str = "ccw", cross_section: CrossSectionSpec = "xs_sc", **kwargs, ) -> Component: """Euler bend with changing bend radius. 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: radius: in um. Defaults to cross_section_radius. 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). cross_section: specification (CrossSection, string, CrossSectionFactory dict). kwargs: additional cross_section arguments. .. code:: o2 | / / / o1_____/ """ x = gf.get_cross_section(cross_section, **kwargs) radius = radius or x.radius if radius is None: return wire_corner(cross_section=x) c = Component() p = euler( radius=radius, angle=angle, p=p, use_eff=with_arc_floorplan, npoints=npoints ) ref = c << p.extrude(x) if direction == "cw": ref.mirror(p1=[0, 0], p2=[1, 0]) c.add_ports(ref.ports) 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"] = radius c.info["width"] = x.width x.validate_radius(radius) c.absorb(ref) c.add_route_info( cross_section=x, length=c.info["length"], n_bend_90=abs(angle / 90.0) ) return c
bend_euler180 = partial(bend_euler, angle=180)
[docs] @gf.cell def bend_euler_s(**kwargs) -> Component: r"""Sbend made of 2 euler bends. Keyword 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). cross_section: specification (CrossSection, string, CrossSectionFactory dict). kwargs: cross_section settings. .. code:: _____ o2 / / / / | / / / o1_____/ """ c = Component() b = bend_euler(**kwargs) b1 = c.add_ref(b) b2 = c.add_ref(b) b2.mirror() b2.connect("o1", b1.ports["o2"]) c.add_port("o1", port=b1.ports["o1"]) c.add_port("o2", port=b2.ports["o2"]) c.info["length"] = 2 * b.info["length"] return c
[docs] @gf.cell def bend_straight_bend( straight_length: float = 10.0, angle: float = 90, p: float = 0.5, with_arc_floorplan: bool = True, npoints: int = 720, direction: str = "ccw", cross_section: CrossSectionSpec = strip, radius: float | None = None, **kwargs, ) -> Component: """Sbend made of 2 euler bends and straight section in between. Args: straight_length: in um. 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). cross_section: specification (CrossSection, string, CrossSectionFactory dict). radius: in um. Defaults to cross_section_radius. kwargs: additional cross_section arguments. """ c = Component() b = bend_euler( angle=angle, p=p, with_arc_floorplan=with_arc_floorplan, npoints=npoints, direction="ccw", cross_section=cross_section, radius=radius, **kwargs, ) b1 = c.add_ref(b) if direction == "cw": b1.mirror_y() b2 = c.add_ref(b) if direction == "cw": b2.mirror_y() s = c << straight( length=straight_length, cross_section=cross_section, ) s.connect("o1", b1.ports["o2"]) b2.mirror() b2.connect("o1", s.ports["o2"]) c.add_port("o2", port=b2.ports["o2"]) c.add_port("o1", port=b1.ports["o1"]) return c
def _compare_bend_euler180() -> None: """Compare 180 bend euler with 2 90deg euler bends.""" import gdsfactory as gf p1 = gf.Path() p1.append([gf.path.euler(angle=90), gf.path.euler(angle=90)]) p2 = gf.path.euler(angle=180) x = gf.cross_section.strip() c1 = gf.path.extrude(p1, x) c1.name = "two_90_euler" c2 = gf.path.extrude(p2, x) c2.name = "one_180_euler" c1.show() def _compare_bend_euler90(): """Compare bend euler with 90deg circular bend.""" import gdsfactory as gf c = gf.Component() radius = 10 b1 = bend_euler(radius=radius) b2 = gf.components.bend_circular(radius=radius) print(b1.info["length"]) print(b2.info["length"]) _ = c << b1 _ = c << b2 return c if __name__ == "__main__": xs = gf.cross_section.strip(bbox_layers=[(111, 0)], bbox_offsets=[3]) c = bend_euler(cross_section=xs, angle=30) c.show(show_ports=True)