From 570080abdb2bf4ae1b3a4a1b4da121daf8d6ba97 Mon Sep 17 00:00:00 2001 From: Joaquin Matres <4514346+joamatab@users.noreply.github.com> Date: Sat, 1 Jun 2024 16:38:11 -0700 Subject: [PATCH 01/15] updating to gdsfactory8 --- cspdk/si220/__init__.py | 2 +- cspdk/si220/cells.py | 12 ++--- cspdk/si220/models.py | 14 +++-- cspdk/si220/tech.py | 113 ++++++++++++--------------------------- cspdk/si500/__init__.py | 2 +- cspdk/si500/cells.py | 19 ++----- cspdk/si500/models.py | 14 +++-- cspdk/si500/tech.py | 50 +++++++---------- cspdk/sin300/__init__.py | 2 +- cspdk/sin300/cells.py | 6 +-- cspdk/sin300/models.py | 8 ++- cspdk/sin300/tech.py | 71 +++++++++--------------- 12 files changed, 107 insertions(+), 206 deletions(-) diff --git a/cspdk/si220/__init__.py b/cspdk/si220/__init__.py index 4e17f47..e1ce0b0 100644 --- a/cspdk/si220/__init__.py +++ b/cspdk/si220/__init__.py @@ -22,7 +22,7 @@ name="cornerstone_si220", cells=_cells, cross_sections=_cross_sections, - layers=dict(LAYER), + layers=LAYER, layer_stack=LAYER_STACK, layer_views=LAYER_VIEWS, models=_models, diff --git a/cspdk/si220/cells.py b/cspdk/si220/cells.py index 2251e7a..bd60f11 100644 --- a/cspdk/si220/cells.py +++ b/cspdk/si220/cells.py @@ -5,7 +5,7 @@ from gdsfactory.typings import ComponentSpec, CrossSectionSpec, LayerSpec from cspdk.si220.config import PATH -from cspdk.si220.tech import LAYER, xs_rc, xs_ro, xs_sc, xs_so +from cspdk.si220.tech import LAYER, TECH ################ # Straights @@ -163,9 +163,7 @@ def _float(x: Any) -> float: @gf.cell -def bend_sc( - radius: float = _float(xs_sc.radius), angle: float = 90.0, **kwargs -) -> gf.Component: +def bend_sc(radius: float = 5, angle: float = 90.0, **kwargs) -> gf.Component: """An euler bend in strip, c-band. Args: @@ -186,7 +184,7 @@ def bend_sc( @gf.cell def bend_so( - radius: float = _float(xs_so.radius), angle: float = 90.0, **kwargs + radius: float = TECH.radius_so, angle: float = 90.0, **kwargs ) -> gf.Component: """An euler bend in strip, o-band. @@ -208,7 +206,7 @@ def bend_so( @gf.cell def bend_rc( - radius: float = _float(xs_rc.radius), angle: float = 90.0, **kwargs + radius: float = TECH.radius_rc, angle: float = 90.0, **kwargs ) -> gf.Component: """An euler bend in rib, c-band. @@ -230,7 +228,7 @@ def bend_rc( @gf.cell def bend_ro( - radius: float = _float(xs_ro.radius), angle: float = 90.0, **kwargs + radius: float = TECH.radius_ro, angle: float = 90.0, **kwargs ) -> gf.Component: """An euler bend in rib, o-band. diff --git a/cspdk/si220/models.py b/cspdk/si220/models.py index 0c7b3f5..ee5fe14 100644 --- a/cspdk/si220/models.py +++ b/cspdk/si220/models.py @@ -7,8 +7,6 @@ import gplugins.sax.models as sm import jax.numpy as jnp import sax -from gdsfactory.pdk import get_cross_section_name -from gdsfactory.typings import CrossSectionSpec from gplugins.sax.models import bend as __bend from gplugins.sax.models import straight as __straight from numpy.typing import NDArray @@ -28,9 +26,9 @@ def _straight( wl: Float = 1.55, length: Float = 10.0, loss: Float = 0.0, - cross_section: CrossSectionSpec = "xs_sc", + cross_section: str = "xs_sc", ) -> sax.SDict: - if get_cross_section_name(cross_section).endswith("so"): + if cross_section.endswith("so"): return __straight( wl=wl, # type: ignore length=length, # type: ignore @@ -39,7 +37,7 @@ def _straight( neff=2.52, ng=4.33, ) - elif get_cross_section_name(cross_section).endswith("nc"): + elif cross_section.endswith("nc"): return __straight( wl=wl, # type: ignore length=length, # type: ignore @@ -48,7 +46,7 @@ def _straight( neff=1.6, ng=1.94, ) - elif get_cross_section_name(cross_section).endswith("no"): + elif cross_section.endswith("no"): return __straight( wl=wl, # type: ignore length=length, # type: ignore @@ -57,7 +55,7 @@ def _straight( neff=1.63, ng=2.00, ) - elif get_cross_section_name(cross_section).endswith("rc"): + elif cross_section.endswith("rc"): return __straight( wl=wl, # type: ignore length=length, # type: ignore @@ -66,7 +64,7 @@ def _straight( neff=2.38, ng=4.30, ) - elif get_cross_section_name(cross_section).endswith("ro"): + elif cross_section.endswith("ro"): return __straight( wl=wl, # type: ignore length=length, # type: ignore diff --git a/cspdk/si220/tech.py b/cspdk/si220/tech.py index 3e704fa..dcf84aa 100644 --- a/cspdk/si220/tech.py +++ b/cspdk/si220/tech.py @@ -28,7 +28,7 @@ class LayerMapCornerstone(LayerMap): LABEL_INSTANCE: Layer = (101, 0) -LAYER = LayerMapCornerstone() +LAYER = LayerMapCornerstone def get_layer_stack( @@ -94,16 +94,25 @@ def get_layer_stack( LAYER_VIEWS = gf.technology.LayerViews(PATH.lyp_yaml) +class Tech: + radius_sc = 5 + radius_so = 5 + radius_rc = 25 + radius_ro = 25 + + +TECH = Tech() + ############################ # Cross-sections functions ############################ cladding_layers_rib = (LAYER.SLAB,) cladding_offsets_rib = (5,) -xf_sc = partial(gf.cross_section.strip, layer=LAYER.WG, width=0.45) -xf_so = partial(xf_sc, width=0.40) +xs_sc = partial(gf.cross_section.strip, layer=LAYER.WG, width=0.45) +xs_so = partial(xs_sc, width=0.40) -xf_rc = partial( +xs_rc = partial( gf.cross_section.strip, layer=LAYER.WG, width=0.45, @@ -111,14 +120,14 @@ def get_layer_stack( radius=25, radius_min=25, ) -xf_ro = partial(xf_rc, width=0.40) -xf_rc_tip = partial( +xs_ro = partial(xs_rc, width=0.40) +xs_rc_tip = partial( gf.cross_section.strip, sections=(gf.Section(width=0.2, layer="SLAB", name="slab"),), ) -xf_sc_heater_metal = partial( +xs_sc_heater_metal = partial( gf.cross_section.strip_heater_metal, layer=LAYER.WG, heater_width=2.5, @@ -135,20 +144,6 @@ def get_layer_stack( radius=None, ) heater_metal = partial(metal_routing, width=4, layer=LAYER.HEATER) - -############################ -# Cross-sections -############################ -xs_sc = xf_sc() -xs_rc = xf_rc() -xs_so = xf_so() -xs_ro = xf_ro() -xs_rc_tip = xf_rc_tip() - -xs_sc_heater_metal = xf_sc_heater_metal() -xs_metal_routing = metal_routing() -xs_heater_metal = heater_metal() - cross_sections = get_cross_sections(sys.modules[__name__]) ############################ @@ -168,68 +163,26 @@ def get_layer_stack( straight="straight_ro", cross_section=xs_ro, bend="bend_ro", taper="taper_ro" ) -get_route_sc = partial(gf.routing.get_route, **_settings_sc) -get_route_so = partial(gf.routing.get_route, **_settings_so) -get_route_rc = partial(gf.routing.get_route, **_settings_rc) -get_route_ro = partial(gf.routing.get_route, **_settings_ro) - -get_route_from_steps_sc = partial( - gf.routing.get_route_from_steps, - **_settings_sc, -) -get_route_from_steps_so = partial( - gf.routing.get_route_from_steps, - **_settings_so, -) -get_route_from_steps_rc = partial( - gf.routing.get_route_from_steps, - **_settings_rc, -) -get_route_from_steps_ro = partial( - gf.routing.get_route_from_steps, - **_settings_ro, -) - -get_bundle_sc = partial(gf.routing.get_bundle, **_settings_sc) -get_bundle_so = partial(gf.routing.get_bundle, **_settings_so) -get_bundle_rc = partial(gf.routing.get_bundle, **_settings_rc) -get_bundle_ro = partial(gf.routing.get_bundle, **_settings_ro) +route_single_sc = partial(gf.routing.route_single, **_settings_sc) +route_single_so = partial(gf.routing.route_single, **_settings_so) +route_single_rc = partial(gf.routing.route_single, **_settings_rc) +route_single_ro = partial(gf.routing.route_single, **_settings_ro) -get_bundle_from_steps_sc = partial( - gf.routing.get_bundle_from_steps, - **_settings_sc, -) -get_bundle_from_steps_so = partial( - gf.routing.get_bundle_from_steps, - **_settings_so, -) -get_bundle_from_steps_rc = partial( - gf.routing.get_bundle_from_steps, - **_settings_rc, -) -get_bundle_from_steps_ro = partial( - gf.routing.get_bundle_from_steps, - **_settings_ro, -) +route_bundle_sc = partial(gf.routing.route_bundle, **_settings_sc) +route_bundle_so = partial(gf.routing.route_bundle, **_settings_so) +route_bundle_rc = partial(gf.routing.route_bundle, **_settings_rc) +route_bundle_ro = partial(gf.routing.route_bundle, **_settings_ro) routing_strategies = dict( - get_route_sc=get_route_sc, - get_route_so=get_route_so, - get_route_rc=get_route_rc, - get_route_ro=get_route_ro, - get_route_from_steps_sc=get_route_from_steps_sc, - get_route_from_steps_so=get_route_from_steps_so, - get_route_from_steps_rc=get_route_from_steps_rc, - get_route_from_steps_ro=get_route_from_steps_ro, - get_bundle_sc=get_bundle_sc, - get_bundle_so=get_bundle_so, - get_bundle_rc=get_bundle_rc, - get_bundle_ro=get_bundle_ro, - get_bundle_from_steps_sc=get_bundle_from_steps_sc, - get_bundle_from_steps_so=get_bundle_from_steps_so, - get_bundle_from_steps_rc=get_bundle_from_steps_rc, - get_bundle_from_steps_ro=get_bundle_from_steps_ro, + route_single_sc=route_single_sc, + route_single_so=route_single_so, + route_single_rc=route_single_rc, + route_single_ro=route_single_ro, + route_bundle_sc=route_bundle_sc, + route_bundle_so=route_bundle_so, + route_bundle_rc=route_bundle_rc, + route_bundle_ro=route_bundle_ro, ) @@ -243,7 +196,7 @@ def get_layer_stack( t = KLayoutTechnology( name="Cornerstone_si220", - layer_map=dict(LAYER), + layer_map=LAYER, layer_views=LAYER_VIEWS, layer_stack=LAYER_STACK, connectivity=connectivity, diff --git a/cspdk/si500/__init__.py b/cspdk/si500/__init__.py index be85515..4172738 100644 --- a/cspdk/si500/__init__.py +++ b/cspdk/si500/__init__.py @@ -22,7 +22,7 @@ name="cornerstone_si500", cells=_cells, cross_sections=_cross_sections, - layers=dict(LAYER), + layers=LAYER, layer_stack=LAYER_STACK, layer_views=LAYER_VIEWS, models=_models, diff --git a/cspdk/si500/cells.py b/cspdk/si500/cells.py index 5a561ab..0726380 100644 --- a/cspdk/si500/cells.py +++ b/cspdk/si500/cells.py @@ -5,7 +5,7 @@ from gdsfactory.typings import ComponentSpec, CrossSectionSpec, LayerSpec from cspdk.si500.config import PATH -from cspdk.si500.tech import LAYER, xs_rc +from cspdk.si500.tech import LAYER, TECH ################ # Straights @@ -110,7 +110,7 @@ def _float(x: Any) -> float: @gf.cell def bend_rc( - radius: float = _float(xs_rc.radius), angle: float = 90.0, **kwargs + radius: float = TECH.radius_rc, angle: float = 90.0, **kwargs ) -> gf.Component: """An euler bend in rib, c-band. @@ -369,7 +369,7 @@ def _gc_rectangular( fill_factor=fill_factor, length_taper=length_taper, fiber_angle=fiber_angle, - layer_grating=layer_grating, + layer=layer_grating, layer_slab=layer_slab, slab_offset=slab_offset, period=period, @@ -379,7 +379,7 @@ def _gc_rectangular( taper=taper, slab_xmin=slab_xmin, cross_section=cross_section, - ).flatten() + ) @gf.cell @@ -559,8 +559,6 @@ def rectangle( centered: bool = False, port_type: str = "electrical", port_orientations: tuple[float, float, float, float] = (180.0, 90.0, 0.0, -90.0), - round_corners_east_west: bool = False, - round_corners_north_south: bool = False, ) -> gf.Component: """A simple rectangle on the given layer. @@ -570,8 +568,6 @@ def rectangle( centered (bool, optional): if true, the rectangle's origin will be placed at the center (otherwise it will be bottom-left). Defaults to False. port_type (str, optional): the port type for ports automatically added to edges of the rectangle. Defaults to "electrical". port_orientations (tuple[float, float, float, float], optional): orientations of the ports to be automatically added. Defaults to (180.0, 90.0, 0.0, -90.0). - round_corners_east_west (bool, optional): if True, circles are added to the east and west edges, forming a horizontal pill shape. Defaults to False. - round_corners_north_south (bool, optional): if True, circles are added to the north and south edges, forming a vertical pill shape. Defaults to False. Returns: gf.Component: the component @@ -582,8 +578,6 @@ def rectangle( centered=centered, port_type=port_type, port_orientations=port_orientations, - round_corners_east_west=round_corners_east_west, - round_corners_north_south=round_corners_north_south, ) @@ -595,7 +589,6 @@ def grating_coupler_array( rotation: float = 0.0, with_loopback: bool = False, bend: ComponentSpec = _bend, - grating_coupler_spacing: float = 0.0, grating_coupler: ComponentSpec = gc_rectangular_rc, cross_section: CrossSectionSpec = "xs_sc", ) -> gf.Component: @@ -608,7 +601,7 @@ def grating_coupler_array( rotation (float, optional): rotation of the grating couplers, in degrees. Defaults to 0.0. with_loopback (bool, optional): if True, adds a loopback. Defaults to False. bend (ComponentSpec, optional): the bend to be used for the loopback. Defaults to _bend. - grating_coupler_spacing (float, optional): the spacing to be used in the loopback. Defaults to 0.0. + pitch (float, optional): the spacing to be used in the loopback. Defaults to 0.0. grating_coupler (ComponentSpec, optional): the grating coupler component to use. cross_section (CrossSectionSpec, optional): the cross section to be used for routing in the loopback. @@ -621,8 +614,6 @@ def grating_coupler_array( port_name=port_name, rotation=rotation, with_loopback=with_loopback, - bend=bend, - grating_coupler_spacing=grating_coupler_spacing, grating_coupler=grating_coupler, cross_section=cross_section, ) diff --git a/cspdk/si500/models.py b/cspdk/si500/models.py index 0c7b3f5..ee5fe14 100644 --- a/cspdk/si500/models.py +++ b/cspdk/si500/models.py @@ -7,8 +7,6 @@ import gplugins.sax.models as sm import jax.numpy as jnp import sax -from gdsfactory.pdk import get_cross_section_name -from gdsfactory.typings import CrossSectionSpec from gplugins.sax.models import bend as __bend from gplugins.sax.models import straight as __straight from numpy.typing import NDArray @@ -28,9 +26,9 @@ def _straight( wl: Float = 1.55, length: Float = 10.0, loss: Float = 0.0, - cross_section: CrossSectionSpec = "xs_sc", + cross_section: str = "xs_sc", ) -> sax.SDict: - if get_cross_section_name(cross_section).endswith("so"): + if cross_section.endswith("so"): return __straight( wl=wl, # type: ignore length=length, # type: ignore @@ -39,7 +37,7 @@ def _straight( neff=2.52, ng=4.33, ) - elif get_cross_section_name(cross_section).endswith("nc"): + elif cross_section.endswith("nc"): return __straight( wl=wl, # type: ignore length=length, # type: ignore @@ -48,7 +46,7 @@ def _straight( neff=1.6, ng=1.94, ) - elif get_cross_section_name(cross_section).endswith("no"): + elif cross_section.endswith("no"): return __straight( wl=wl, # type: ignore length=length, # type: ignore @@ -57,7 +55,7 @@ def _straight( neff=1.63, ng=2.00, ) - elif get_cross_section_name(cross_section).endswith("rc"): + elif cross_section.endswith("rc"): return __straight( wl=wl, # type: ignore length=length, # type: ignore @@ -66,7 +64,7 @@ def _straight( neff=2.38, ng=4.30, ) - elif get_cross_section_name(cross_section).endswith("ro"): + elif cross_section.endswith("ro"): return __straight( wl=wl, # type: ignore length=length, # type: ignore diff --git a/cspdk/si500/tech.py b/cspdk/si500/tech.py index dd868de..8db021c 100644 --- a/cspdk/si500/tech.py +++ b/cspdk/si500/tech.py @@ -28,7 +28,15 @@ class LayerMapCornerstone(LayerMap): LABEL_INSTANCE: Layer = (101, 0) -LAYER = LayerMapCornerstone() +LAYER = LayerMapCornerstone + + +class Tech: + radius_rc = 25 + radius_ro = 25 + + +TECH = Tech() def get_layer_stack( @@ -100,7 +108,7 @@ def get_layer_stack( cladding_layers_rib = (LAYER.SLAB,) cladding_offsets_rib = (5,) -xf_rc = partial( +xs_rc = partial( gf.cross_section.strip, layer=LAYER.WG, width=0.45, @@ -108,8 +116,8 @@ def get_layer_stack( radius=25, radius_min=25, ) -xf_ro = partial(xf_rc, width=0.40) -xf_rc_tip = partial( +xs_ro = partial(xs_rc, width=0.40) +xs_rc_tip = partial( gf.cross_section.strip, sections=(gf.Section(width=0.2, layer="SLAB", name="slab"),), ) @@ -123,15 +131,7 @@ def get_layer_stack( port_types=gf.cross_section.port_types_electrical, radius=None, ) -heater_metal = partial(metal_routing, width=4, layer=LAYER.HEATER) - -############################ -# Cross-sections -############################ -xs_rc = xf_rc() -xs_rc_tip = xf_rc_tip() -xs_metal_routing = metal_routing() -xs_heater_metal = heater_metal() +xs_heater_metal = heater_metal = partial(metal_routing, width=4, layer=LAYER.HEATER) cross_sections = get_cross_sections(sys.modules[__name__]) ############################ @@ -139,27 +139,15 @@ def get_layer_stack( ############################ _settings_rc = dict( - straight="straight_rc", cross_section=xs_rc, bend="bend_rc", taper="taper_rc" + straight="straight_rc", cross_section="xs_rc", bend="bend_rc", taper="taper_rc" ) -get_route_rc = partial(gf.routing.get_route, **_settings_rc) - -get_route_from_steps_rc = partial( - gf.routing.get_route_from_steps, - **_settings_rc, -) -get_bundle_rc = partial(gf.routing.get_bundle, **_settings_rc) - -get_bundle_from_steps_rc = partial( - gf.routing.get_bundle_from_steps, - **_settings_rc, -) +route_single_rc = partial(gf.routing.route_single, **_settings_rc) +route_bundle_rc = partial(gf.routing.route_bundle, **_settings_rc) routing_strategies = dict( - get_route_rc=get_route_rc, - get_route_from_steps_rc=get_route_from_steps_rc, - get_bundle_rc=get_bundle_rc, - get_bundle_from_steps_rc=get_bundle_from_steps_rc, + route_single_rc=route_single_rc, + route_bundle_rc=route_bundle_rc, ) @@ -173,7 +161,7 @@ def get_layer_stack( t = KLayoutTechnology( name="Cornerstone_si500", - layer_map=dict(LAYER), + layer_map=LAYER, layer_views=LAYER_VIEWS, layer_stack=LAYER_STACK, connectivity=connectivity, diff --git a/cspdk/sin300/__init__.py b/cspdk/sin300/__init__.py index 17f7cfb..872b5ef 100644 --- a/cspdk/sin300/__init__.py +++ b/cspdk/sin300/__init__.py @@ -22,7 +22,7 @@ name="cornerstone_sin300", cells=_cells, cross_sections=_cross_sections, - layers=dict(LAYER), + layers=LAYER, layer_stack=LAYER_STACK, layer_views=LAYER_VIEWS, models=_models, diff --git a/cspdk/sin300/cells.py b/cspdk/sin300/cells.py index 9b24d2a..3f31976 100644 --- a/cspdk/sin300/cells.py +++ b/cspdk/sin300/cells.py @@ -5,7 +5,7 @@ from gdsfactory.typings import ComponentSpec, CrossSectionSpec, LayerSpec from cspdk.sin300.config import PATH -from cspdk.sin300.tech import LAYER, xs_nc, xs_no +from cspdk.sin300.tech import LAYER, TECH ################ # Straights @@ -128,7 +128,7 @@ def _float(x: Any) -> float: @gf.cell def bend_nc( - radius: float = _float(xs_nc.radius), angle: float = 90.0, **kwargs + radius: float = TECH.radius_nc, angle: float = 90.0, **kwargs ) -> gf.Component: """An euler bend in nitride, c-band. @@ -150,7 +150,7 @@ def bend_nc( @gf.cell def bend_no( - radius: float = _float(xs_no.radius), angle: float = 90.0, **kwargs + radius: float = TECH.radius_no, angle: float = 90.0, **kwargs ) -> gf.Component: """An euler bend in nitride, o-band. diff --git a/cspdk/sin300/models.py b/cspdk/sin300/models.py index 5ded49c..f39be2d 100644 --- a/cspdk/sin300/models.py +++ b/cspdk/sin300/models.py @@ -7,8 +7,6 @@ import gplugins.sax.models as sm import jax.numpy as jnp import sax -from gdsfactory.pdk import get_cross_section_name -from gdsfactory.typings import CrossSectionSpec from gplugins.sax.models import bend as __bend from gplugins.sax.models import straight as __straight from numpy.typing import NDArray @@ -28,9 +26,9 @@ def _straight( wl: Float = 1.55, length: Float = 10.0, loss: Float = 0.0, - cross_section: CrossSectionSpec = "xs_sc", + cross_section: str = "xs_sc", ) -> sax.SDict: - if get_cross_section_name(cross_section).endswith("nc"): + if cross_section.endswith("nc"): return __straight( wl=wl, # type: ignore length=length, # type: ignore @@ -39,7 +37,7 @@ def _straight( neff=1.6, ng=1.94, ) - elif get_cross_section_name(cross_section).endswith("no"): + elif cross_section.endswith("no"): return __straight( wl=wl, # type: ignore length=length, # type: ignore diff --git a/cspdk/sin300/tech.py b/cspdk/sin300/tech.py index 0345aa5..cc54cbc 100644 --- a/cspdk/sin300/tech.py +++ b/cspdk/sin300/tech.py @@ -30,7 +30,15 @@ class LayerMapCornerstone(LayerMap): LABEL_INSTANCE: Layer = (101, 0) -LAYER = LayerMapCornerstone() +LAYER = LayerMapCornerstone + + +class Tech: + radius_nc = 25 + radius_no = 25 + + +TECH = Tech() def get_layer_stack( @@ -97,10 +105,10 @@ def get_layer_stack( ############################ # Cross-sections functions ############################ -xf_nc = partial(gf.cross_section.strip, layer=LAYER.NITRIDE, width=1.20, radius=25) -xf_no = partial(gf.cross_section.strip, layer=LAYER.NITRIDE, width=0.95, radius=25) +xs_nc = partial(gf.cross_section.strip, layer=LAYER.NITRIDE, width=1.20, radius=25) +xs_no = partial(gf.cross_section.strip, layer=LAYER.NITRIDE, width=0.95, radius=25) -xf_nc_heater_metal = partial( +xs_nc_heater_metal = partial( gf.cross_section.strip_heater_metal, layer=LAYER.NITRIDE, heater_width=2.5, @@ -116,17 +124,7 @@ def get_layer_stack( port_types=gf.cross_section.port_types_electrical, radius=None, ) -heater_metal = partial(metal_routing, width=4, layer=LAYER.HEATER) - -############################ -# Cross-sections -############################ -xs_nc = xf_nc() -xs_no = xf_no() - -xs_sc_heater_metal = xf_nc_heater_metal() -xs_metal_routing = metal_routing() -xs_heater_metal = heater_metal() +xs_heater_metal = heater_metal = partial(metal_routing, width=4, layer=LAYER.HEATER) cross_sections = get_cross_sections(sys.modules[__name__]) @@ -134,46 +132,25 @@ def get_layer_stack( # Routing functions ############################ _settings_nc = dict( - straight="straight_nc", cross_section=xs_nc, bend="bend_nc", taper="taper_nc" + straight="straight_nc", cross_section="xs_nc", bend="bend_nc", taper="taper_nc" ) _settings_no = dict( - straight="straight_no", cross_section=xs_no, bend="bend_no", taper="taper_no" + straight="straight_no", cross_section="xs_no", bend="bend_no", taper="taper_no" ) -get_route_nc = partial(gf.routing.get_route, **_settings_nc) -get_route_no = partial(gf.routing.get_route, **_settings_no) - -get_route_from_steps_nc = partial( - gf.routing.get_route_from_steps, - **_settings_nc, -) -get_route_from_steps_no = partial( - gf.routing.get_route_from_steps, - **_settings_no, -) +route_single_nc = partial(gf.routing.route_single, **_settings_nc) +route_single_no = partial(gf.routing.route_single, **_settings_no) -get_bundle_nc = partial(gf.routing.get_bundle, **_settings_nc) -get_bundle_no = partial(gf.routing.get_bundle, **_settings_no) -get_bundle_from_steps_nc = partial( - gf.routing.get_bundle_from_steps, - **_settings_nc, -) -get_bundle_from_steps_no = partial( - gf.routing.get_bundle_from_steps, - **_settings_no, -) +route_bundle_nc = partial(gf.routing.route_bundle, **_settings_nc) +route_bundle_no = partial(gf.routing.route_bundle, **_settings_no) routing_strategies = dict( - get_route_nc=get_route_nc, - get_route_no=get_route_no, - get_route_from_steps_nc=get_route_from_steps_nc, - get_route_from_steps_no=get_route_from_steps_no, - get_bundle_nc=get_bundle_nc, - get_bundle_no=get_bundle_no, - get_bundle_from_steps_nc=get_bundle_from_steps_nc, - get_bundle_from_steps_no=get_bundle_from_steps_no, + route_single_nc=route_single_nc, + route_single_no=route_single_no, + route_bundle_nc=route_bundle_nc, + route_bundle_no=route_bundle_no, ) @@ -187,7 +164,7 @@ def get_layer_stack( t = KLayoutTechnology( name="Cornerstone_sin300", - layer_map=dict(LAYER), + layer_map=LAYER, layer_views=LAYER_VIEWS, layer_stack=LAYER_STACK, connectivity=connectivity, From 9ca16d4018b7815fbf7ff8b05bc45144863f2714 Mon Sep 17 00:00:00 2001 From: Joaquin Matres <4514346+joamatab@users.noreply.github.com> Date: Sat, 1 Jun 2024 17:43:36 -0700 Subject: [PATCH 02/15] ignore --- .gitignore | 3 +++ cspdk/si500/cells.py | 4 ++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index 10a891c..2dbc747 100644 --- a/.gitignore +++ b/.gitignore @@ -8,6 +8,9 @@ Pipfile *.so *.fsp tests/gds_ref/*.oas +tests/gds_ref_si220/*.oas +tests/gds_ref_si500/*.oas +tests/gds_ref_sin300/*.oas tests/gds_ref/*.oas diff --git a/cspdk/si500/cells.py b/cspdk/si500/cells.py index 0726380..886ed07 100644 --- a/cspdk/si500/cells.py +++ b/cspdk/si500/cells.py @@ -647,13 +647,13 @@ def _die( cross_section=cross_section, ) left = c << gca - left.rotate(90) + left.drotate(90) left.xmax = x0 left.y = fp.y c.add_ports(left.ports, prefix="W") right = c << gca - right.rotate(-90) + right.drotate(-90) right.xmax = -x0 right.y = fp.y c.add_ports(right.ports, prefix="E") From 167b114e4440f3bde865dd2ec76bfaf353584014 Mon Sep 17 00:00:00 2001 From: Joaquin Matres <4514346+joamatab@users.noreply.github.com> Date: Sat, 1 Jun 2024 18:13:34 -0700 Subject: [PATCH 03/15] fix mmis --- cspdk/si220/cells.py | 37 +++++++------------ .../circuit_simulations_sc_with_routing.py | 2 +- cspdk/si220/samples/get_route_sc.py | 2 +- cspdk/si220/tech.py | 2 + cspdk/si500/cells.py | 14 +++---- .../circuit_simulations_rc500_with_routing.py | 2 +- cspdk/si500/samples/get_route_rc500.py | 2 +- cspdk/sin300/cells.py | 18 ++++----- .../circuit_simulations_nc_with_routing.py | 2 +- cspdk/sin300/samples/get_route_nc.py | 2 +- 10 files changed, 37 insertions(+), 46 deletions(-) diff --git a/cspdk/si220/cells.py b/cspdk/si220/cells.py index bd60f11..8031480 100644 --- a/cspdk/si220/cells.py +++ b/cspdk/si220/cells.py @@ -14,12 +14,12 @@ @gf.cell def _straight( - length: float = 10.0, - cross_section: CrossSectionSpec = "xs_sc", + length: float = 10.0, cross_section: CrossSectionSpec = "xs_sc", **kwargs ) -> gf.Component: return gf.components.straight( length=length, cross_section=cross_section, + **kwargs, ) @@ -129,7 +129,6 @@ def _bend( p: float = 0.5, with_arc_floorplan: bool = True, npoints: int | None = None, - direction: str = "ccw", cross_section: CrossSectionSpec = "xs_sc", ) -> gf.Component: return gf.components.bend_euler( @@ -138,7 +137,6 @@ def _bend( p=p, with_arc_floorplan=with_arc_floorplan, npoints=npoints, - direction=direction, cross_section=cross_section, ) @@ -996,7 +994,6 @@ def _mzi( length_y: float = 2.0, length_x: float = 0.1, cross_section: CrossSectionSpec = "xs_sc", - add_electrical_ports_bot: bool = True, bend: ComponentSpec = _bend, straight: ComponentSpec = _straight, splitter: ComponentSpec = _mmi1x2, @@ -1024,9 +1021,7 @@ def _mzi( cross_section_x_bot=cross_section, mirror_bot=False, add_optical_ports_arms=False, - add_electrical_ports_bot=add_electrical_ports_bot, min_length=0.01, - extend_ports_straight_x=None, ) @@ -1054,7 +1049,6 @@ def mzi_sc( delta_length=delta_length, length_y=length_y, length_x=length_x, - add_electrical_ports_bot=add_electrical_ports_bot, straight=straight_sc, bend=bend_sc, combiner=mmi1x2_sc, @@ -1069,7 +1063,6 @@ def mzi_so( delta_length: float = 10.0, length_y: float = 2.0, length_x: float = 0.1, - add_electrical_ports_bot: bool = True, **kwargs, ) -> gf.Component: """A Mach-Zehnder Interferometer (MZI) in strip, o-band. @@ -1078,7 +1071,6 @@ def mzi_so( delta_length (float, optional): the length differential between the two arms. Defaults to 10.0. length_y (float, optional): the common vertical length, in um. Defaults to 2.0. length_x (float, optional): the common horizontal length, in um. Defaults to 0.1. - add_electrical_ports_bot (bool, optional): if true, adds electrical ports to the bottom. Defaults to True. Returns: gf.Component: the component @@ -1089,7 +1081,6 @@ def mzi_so( delta_length=delta_length, length_y=length_y, length_x=length_x, - add_electrical_ports_bot=add_electrical_ports_bot, straight=straight_so, bend=bend_so, combiner=mmi1x2_so, @@ -1103,7 +1094,6 @@ def mzi_rc( delta_length: float = 10.0, length_y: float = 2.0, length_x: float = 0.1, - add_electrical_ports_bot: bool = True, **kwargs, ) -> gf.Component: """A Mach-Zehnder Interferometer (MZI) in rib, c-band. @@ -1112,7 +1102,6 @@ def mzi_rc( delta_length (float, optional): the length differential between the two arms. Defaults to 10.0. length_y (float, optional): the common vertical length, in um. Defaults to 2.0. length_x (float, optional): the common horizontal length, in um. Defaults to 0.1. - add_electrical_ports_bot (bool, optional): if true, adds electrical ports to the bottom. Defaults to True. Returns: gf.Component: the component @@ -1123,7 +1112,6 @@ def mzi_rc( delta_length=delta_length, length_y=length_y, length_x=length_x, - add_electrical_ports_bot=add_electrical_ports_bot, straight=straight_rc, bend=bend_rc, combiner=mmi1x2_rc, @@ -1307,15 +1295,15 @@ def _die( cross_section=cross_section, ) left = c << gca - left.rotate(90) - left.xmax = x0 - left.y = fp.y + left.drotate(90) + left.dxmax = x0 + left.dy = fp.y c.add_ports(left.ports, prefix="W") right = c << gca - right.rotate(-90) - right.xmax = -x0 - right.y = fp.y + right.drotate(-90) + right.dxmax = -x0 + right.dy = fp.y c.add_ports(right.ports, prefix="E") # Add electrical ports @@ -1325,7 +1313,7 @@ def _die( for i in range(npads): pad_ref = c << pad - pad_ref.xmin = x0 + i * pad_pitch + pad_ref.dxmin = x0 + i * pad_pitch pad_ref.ymin = y0 c.add_port( name=f"N{i}", @@ -1334,8 +1322,8 @@ def _die( for i in range(npads): pad_ref = c << pad - pad_ref.xmin = x0 + i * pad_pitch - pad_ref.ymax = -y0 + pad_ref.dxmin = x0 + i * pad_pitch + pad_ref.dymax = -y0 c.add_port( name=f"S{i}", port=pad_ref.ports["e2"], @@ -1476,7 +1464,8 @@ def crossing_sc() -> gf.Component: if __name__ == "__main__": - c = mzi_sc() + # c = mzi_rc() + c = trans_sc_rc20() # c = crossing_sc() c.show() # for name, func in list(globals().items()): diff --git a/cspdk/si220/samples/circuit_simulations_sc_with_routing.py b/cspdk/si220/samples/circuit_simulations_sc_with_routing.py index 0231f1c..a40288d 100644 --- a/cspdk/si220/samples/circuit_simulations_sc_with_routing.py +++ b/cspdk/si220/samples/circuit_simulations_sc_with_routing.py @@ -9,7 +9,7 @@ c = gf.Component() mzi1 = c << cells.mzi_sc(delta_length=10) mzi2 = c << cells.mzi_sc(delta_length=100) - mzi2.move((200, 200)) + mzi2.dmove((200, 200)) route = tech.get_route_sc(mzi1.ports["o2"], mzi2.ports["o1"]) c.add(route.references) c.add_port(name="o1", port=mzi1.ports["o1"]) diff --git a/cspdk/si220/samples/get_route_sc.py b/cspdk/si220/samples/get_route_sc.py index faffbe3..dada381 100644 --- a/cspdk/si220/samples/get_route_sc.py +++ b/cspdk/si220/samples/get_route_sc.py @@ -8,7 +8,7 @@ c = gf.Component("sample_connect") mmi1 = c << cells.mmi1x2_sc() mmi2 = c << cells.mmi1x2_sc() - mmi2.move((500, 50)) + mmi2.dmove((500, 50)) route = tech.get_route_sc( mmi1.ports["o3"], diff --git a/cspdk/si220/tech.py b/cspdk/si220/tech.py index dcf84aa..26f9f8d 100644 --- a/cspdk/si220/tech.py +++ b/cspdk/si220/tech.py @@ -117,6 +117,8 @@ class Tech: layer=LAYER.WG, width=0.45, sections=(gf.Section(width=10.45, layer="SLAB", name="slab", simplify=50 * nm),), + # cladding_layers=(LAYER.SLAB,), + # cladding_offsets=(5,), radius=25, radius_min=25, ) diff --git a/cspdk/si500/cells.py b/cspdk/si500/cells.py index 886ed07..1fb4599 100644 --- a/cspdk/si500/cells.py +++ b/cspdk/si500/cells.py @@ -648,14 +648,14 @@ def _die( ) left = c << gca left.drotate(90) - left.xmax = x0 - left.y = fp.y + left.dxmax = x0 + left.dy = fp.y c.add_ports(left.ports, prefix="W") right = c << gca right.drotate(-90) - right.xmax = -x0 - right.y = fp.y + right.dxmax = -x0 + right.dy = fp.y c.add_ports(right.ports, prefix="E") # Add electrical ports @@ -665,7 +665,7 @@ def _die( for i in range(npads): pad_ref = c << pad - pad_ref.xmin = x0 + i * pad_pitch + pad_ref.dxmin = x0 + i * pad_pitch pad_ref.ymin = y0 c.add_port( name=f"N{i}", @@ -674,8 +674,8 @@ def _die( for i in range(npads): pad_ref = c << pad - pad_ref.xmin = x0 + i * pad_pitch - pad_ref.ymax = -y0 + pad_ref.dxmin = x0 + i * pad_pitch + pad_ref.dymax = -y0 c.add_port( name=f"S{i}", port=pad_ref.ports["e2"], diff --git a/cspdk/si500/samples/circuit_simulations_rc500_with_routing.py b/cspdk/si500/samples/circuit_simulations_rc500_with_routing.py index bfe42b0..d25b149 100644 --- a/cspdk/si500/samples/circuit_simulations_rc500_with_routing.py +++ b/cspdk/si500/samples/circuit_simulations_rc500_with_routing.py @@ -9,7 +9,7 @@ c = gf.Component() mzi1 = c << cells.mzi_sc(delta_length=10) mzi2 = c << cells.mzi_sc(delta_length=100) - mzi2.move((200, 200)) + mzi2.dmove((200, 200)) route = tech.get_route_sc(mzi1.ports["o2"], mzi2.ports["o1"]) c.add(route.references) c.add_port(name="o1", port=mzi1.ports["o1"]) diff --git a/cspdk/si500/samples/get_route_rc500.py b/cspdk/si500/samples/get_route_rc500.py index da37e96..3b399b4 100644 --- a/cspdk/si500/samples/get_route_rc500.py +++ b/cspdk/si500/samples/get_route_rc500.py @@ -8,7 +8,7 @@ c = gf.Component("sample_connect") mmi1 = c << cells.mmi1x2_rc() mmi2 = c << cells.mmi1x2_rc() - mmi2.move((500, 50)) + mmi2.dmove((500, 50)) route = tech.get_route_rc( mmi1.ports["o3"], diff --git a/cspdk/sin300/cells.py b/cspdk/sin300/cells.py index 3f31976..ba91b24 100644 --- a/cspdk/sin300/cells.py +++ b/cspdk/sin300/cells.py @@ -919,15 +919,15 @@ def _die( cross_section=cross_section, ) left = c << gca - left.rotate(90) - left.xmax = x0 - left.y = fp.y + left.drotate(90) + left.dxmax = x0 + left.dy = fp.y c.add_ports(left.ports, prefix="W") right = c << gca - right.rotate(-90) - right.xmax = -x0 - right.y = fp.y + right.drotate(-90) + right.dxmax = -x0 + right.dy = fp.y c.add_ports(right.ports, prefix="E") # Add electrical ports @@ -937,7 +937,7 @@ def _die( for i in range(npads): pad_ref = c << pad - pad_ref.xmin = x0 + i * pad_pitch + pad_ref.dxmin = x0 + i * pad_pitch pad_ref.ymin = y0 c.add_port( name=f"N{i}", @@ -946,8 +946,8 @@ def _die( for i in range(npads): pad_ref = c << pad - pad_ref.xmin = x0 + i * pad_pitch - pad_ref.ymax = -y0 + pad_ref.dxmin = x0 + i * pad_pitch + pad_ref.dymax = -y0 c.add_port( name=f"S{i}", port=pad_ref.ports["e2"], diff --git a/cspdk/sin300/samples/circuit_simulations_nc_with_routing.py b/cspdk/sin300/samples/circuit_simulations_nc_with_routing.py index a624476..b6ba755 100644 --- a/cspdk/sin300/samples/circuit_simulations_nc_with_routing.py +++ b/cspdk/sin300/samples/circuit_simulations_nc_with_routing.py @@ -9,7 +9,7 @@ c = gf.Component() mzi1 = c << cells.mzi_nc(delta_length=10) mzi2 = c << cells.mzi_nc(delta_length=100) - mzi2.move((200, 200)) + mzi2.dmove((200, 200)) route = tech.get_route_nc(mzi1.ports["o2"], mzi2.ports["o1"]) c.add(route.references) c.add_port(name="o1", port=mzi1.ports["o1"]) diff --git a/cspdk/sin300/samples/get_route_nc.py b/cspdk/sin300/samples/get_route_nc.py index 066bd5a..fdc4207 100644 --- a/cspdk/sin300/samples/get_route_nc.py +++ b/cspdk/sin300/samples/get_route_nc.py @@ -8,7 +8,7 @@ c = gf.Component("sample_connect") mmi1 = c << cells.mmi1x2_nc() mmi2 = c << cells.mmi1x2_nc() - mmi2.move((500, 50)) + mmi2.dmove((500, 50)) route = tech.get_route_nc( mmi1.ports["o3"], From 1cfe29886a137e5d4df33c61ca92275d2185092c Mon Sep 17 00:00:00 2001 From: Joaquin Matres <4514346+joamatab@users.noreply.github.com> Date: Sat, 1 Jun 2024 18:53:07 -0700 Subject: [PATCH 04/15] fix taper --- cspdk/si220/cells.py | 123 ++++++++++------------------ cspdk/si220/tech.py | 11 +-- tests/gds_ref_si220/bend_so.gds | Bin 2082 -> 2954 bytes tests/gds_ref_si220/mmi2x2_rc.gds | Bin 756 -> 2314 bytes tests/gds_ref_si220/mmi2x2_ro.gds | Bin 756 -> 2312 bytes tests/gds_ref_si220/straight_ro.gds | Bin 246 -> 1610 bytes tests/gds_ref_si220/taper_ro.gds | Bin 242 -> 1766 bytes 7 files changed, 44 insertions(+), 90 deletions(-) diff --git a/cspdk/si220/cells.py b/cspdk/si220/cells.py index 8031480..6e238ed 100644 --- a/cspdk/si220/cells.py +++ b/cspdk/si220/cells.py @@ -366,36 +366,16 @@ def taper_ro( return _taper(length=length, width1=width1, width2=width2, port=port, **kwargs) -@gf.cell -def _taper_cross_section( - length: float = 10, - cross_section1: str = "xs_rc_tip", - cross_section2: str = "xs_rc", - linear: bool = True, - **kwargs, -) -> gf.Component: - if linear: - kwargs["npoints"] = 2 - return gf.components.taper_cross_section( - length=length, - cross_section1=cross_section1, - cross_section2=cross_section2, - linear=linear, - **kwargs, - ).flatten() - - @gf.cell def trans_sc_rc10() -> gf.Component: - """A 10um-long strip-rib transition in c-band. - - Returns: - gf.Component: the component - """ - return _taper_cross_section( + """A 10um-long strip-rib transition in c-band.""" + return gf.c.taper_strip_to_ridge( length=10, - cross_section1="xs_rc_tip", - cross_section2="xs_rc", + w_slab1=0.2, + w_slab2=10.45, + cross_section="xs_sc", + layer_wg=LAYER.WG, + layer_slab=LAYER.SLAB, ) @@ -406,10 +386,13 @@ def trans_sc_rc20() -> gf.Component: Returns: gf.Component: the component """ - return _taper_cross_section( + return gf.c.taper_strip_to_ridge( length=20, - cross_section1="xs_rc_tip", - cross_section2="xs_rc", + w_slab1=0.2, + w_slab2=10.45, + cross_section="xs_sc", + layer_wg=LAYER.WG, + layer_slab=LAYER.SLAB, ) @@ -420,10 +403,13 @@ def trans_sc_rc50() -> gf.Component: Returns: gf.Component: the component """ - return _taper_cross_section( + return gf.c.taper_strip_to_ridge( length=50, - cross_section1="xs_rc_tip", - cross_section2="xs_rc", + w_slab1=0.2, + w_slab2=10.45, + cross_section="xs_sc", + layer_wg=LAYER.WG, + layer_slab=LAYER.SLAB, ) @@ -797,39 +783,23 @@ def coupler_ro( ############################## -@gf.cell -def _gc_rectangular( - n_periods: int = 30, - fill_factor: float = 0.5, - length_taper: float = 350.0, - fiber_angle: float = 10.0, - layer_grating: LayerSpec = LAYER.GRA, - layer_slab: LayerSpec = LAYER.WG, - slab_offset: float = 0.0, - period: float = 0.75, - width_grating: float = 11.0, - polarization: str = "te", - wavelength: float = 1.55, - taper: ComponentSpec = _taper, - slab_xmin: float = -1.0, - cross_section: CrossSectionSpec = "xs_sc", -) -> gf.Component: - return gf.components.grating_coupler_rectangular( - n_periods=n_periods, - fill_factor=fill_factor, - length_taper=length_taper, - fiber_angle=fiber_angle, - layer_grating=layer_grating, - layer_slab=layer_slab, - slab_offset=slab_offset, - period=period, - width_grating=width_grating, - polarization=polarization, - wavelength=wavelength, - taper=taper, - slab_xmin=slab_xmin, - cross_section=cross_section, - ).flatten() +_gc_rectangular = partial( + gf.components.grating_coupler_rectangular, + n_periods=30, + fill_factor=0.5, + length_taper=350.0, + fiber_angle=10.0, + layer_grating=LAYER.GRA, + layer_slab=LAYER.WG, + slab_offset=0.0, + period=0.75, + width_grating=11.0, + polarization="te", + wavelength=1.55, + taper=_taper, + slab_xmin=-1.0, + cross_section="xs_sc", +) @gf.cell @@ -1166,7 +1136,7 @@ def pad( bbox_layers: None = None, bbox_offsets: None = None, port_inclusion: float = 0.0, - port_orientation: None = None, + port_orientation: float = 0, ) -> gf.Component: """An electrical pad. @@ -1198,8 +1168,6 @@ def rectangle( centered: bool = False, port_type: str = "electrical", port_orientations: tuple[float, float, float, float] = (180.0, 90.0, 0.0, -90.0), - round_corners_east_west: bool = False, - round_corners_north_south: bool = False, ) -> gf.Component: """A simple rectangle on the given layer. @@ -1209,8 +1177,6 @@ def rectangle( centered (bool, optional): if true, the rectangle's origin will be placed at the center (otherwise it will be bottom-left). Defaults to False. port_type (str, optional): the port type for ports automatically added to edges of the rectangle. Defaults to "electrical". port_orientations (tuple[float, float, float, float], optional): orientations of the ports to be automatically added. Defaults to (180.0, 90.0, 0.0, -90.0). - round_corners_east_west (bool, optional): if True, circles are added to the east and west edges, forming a horizontal pill shape. Defaults to False. - round_corners_north_south (bool, optional): if True, circles are added to the north and south edges, forming a vertical pill shape. Defaults to False. Returns: gf.Component: the component @@ -1221,8 +1187,6 @@ def rectangle( centered=centered, port_type=port_type, port_orientations=port_orientations, - round_corners_east_west=round_corners_east_west, - round_corners_north_south=round_corners_north_south, ) @@ -1231,9 +1195,8 @@ def grating_coupler_array( pitch: float = 127.0, n: int = 6, port_name: str = "o1", - rotation: float = 0.0, + rotation: float = -90, with_loopback: bool = False, - bend: ComponentSpec = _bend, grating_coupler_spacing: float = 0.0, grating_coupler: ComponentSpec = gc_rectangular_sc, cross_section: CrossSectionSpec = "xs_sc", @@ -1260,8 +1223,6 @@ def grating_coupler_array( port_name=port_name, rotation=rotation, with_loopback=with_loopback, - bend=bend, - grating_coupler_spacing=grating_coupler_spacing, grating_coupler=grating_coupler, cross_section=cross_section, ) @@ -1290,18 +1251,17 @@ def _die( n=ngratings, pitch=grating_pitch, with_loopback=True, - rotation=90, grating_coupler=grating_coupler, cross_section=cross_section, ) left = c << gca - left.drotate(90) + left.drotate(-90) left.dxmax = x0 left.dy = fp.y c.add_ports(left.ports, prefix="W") right = c << gca - right.drotate(-90) + right.drotate(+90) right.dxmax = -x0 right.dy = fp.y c.add_ports(right.ports, prefix="E") @@ -1464,8 +1424,9 @@ def crossing_sc() -> gf.Component: if __name__ == "__main__": + c = die_sc() # c = mzi_rc() - c = trans_sc_rc20() + # c = trans_sc_rc20() # c = crossing_sc() c.show() # for name, func in list(globals().items()): diff --git a/cspdk/si220/tech.py b/cspdk/si220/tech.py index 26f9f8d..e099a4d 100644 --- a/cspdk/si220/tech.py +++ b/cspdk/si220/tech.py @@ -106,8 +106,6 @@ class Tech: ############################ # Cross-sections functions ############################ -cladding_layers_rib = (LAYER.SLAB,) -cladding_offsets_rib = (5,) xs_sc = partial(gf.cross_section.strip, layer=LAYER.WG, width=0.45) xs_so = partial(xs_sc, width=0.40) @@ -116,17 +114,12 @@ class Tech: gf.cross_section.strip, layer=LAYER.WG, width=0.45, - sections=(gf.Section(width=10.45, layer="SLAB", name="slab", simplify=50 * nm),), - # cladding_layers=(LAYER.SLAB,), - # cladding_offsets=(5,), + bbox_layers=(LAYER.SLAB,), + bbox_offsets=(5,), radius=25, radius_min=25, ) xs_ro = partial(xs_rc, width=0.40) -xs_rc_tip = partial( - gf.cross_section.strip, - sections=(gf.Section(width=0.2, layer="SLAB", name="slab"),), -) xs_sc_heater_metal = partial( diff --git a/tests/gds_ref_si220/bend_so.gds b/tests/gds_ref_si220/bend_so.gds index c25f92d54624bff183ae0dfed7d442b996ac47b0..c3cdc0caf905717cb330c1e9ac91cbf89ec6f162 100644 GIT binary patch literal 2954 zcma);TWB0*6vxkAGP})flgV1GR*7!twl!`iO`_Sg)~4E<_97agwCXrXc9U%U3Yy<+Qk_UnUrO`6ZE)KhikLK$Yt?znC^BYMfS zZD-OnyrNuZsz~4PT`J4N)yW6jFzV=m}ks>KlbIaEIjt4XPD7) z{XygxDk6C;=L49v?|vvwWB@|w2WzbH6)t+QX#v~KU`UO$&ATBx@dMR#W>y#llLq+1>_%wQL^ z*ICzEPRp1G9IDgbR%gf5Y-`K3+rKFEuBxg^g?2!z(}C7HYa3+H3UfQqGoW>9-K6oW zZwaklD}-0y99q3rI9omY*PDl?(CW29SoMvvZr+-dt&&}Ecu*8|?^0(%hx-P4jlSVg z-7H#T{w~dA26DYU13_(z>U3ppl$m0AY}|WRUErClGf{IrYh!Vqj_Q6%$m)x#w{-u3 zme>5$J$|PYS#wc8Z|nG;=bki~-dulgP;tZmsJMQw;?fn8`|J}f6FkvkbtcB|O{g?) z3Zix9+4q_!$Fq(|XMHuJvwptV^}>ms%J?;7CdEDW$rmsJ7HtN z2^)JhICsvRIm3|opHD<0X|6mz2R{{wG6?b+d>tMaiRIy!@EH7tBW*MM9)1pg6=}E| z{sm7GgA-i2KZvRP1XqY8#^5^m8@xv(xd%Q7y?b&l8iyZ3&;GPX<9+1rgg?SvB2650 z(F5>j_=8BZftGIg9o#3EiCi|_-qp|2hNjp$cf&~_C>QD~f+la$DrZ>VtNUqdj$PA>YJ58Of`{6Z^o}^WD($}=0{}i#Qvw0YP zL47CSn{X3+g8I?eu?G^P<7WCp|2k4|3Hn%{BsMC2OZ*GbnZQ=%T~9fQwnLEr+V5gr zn7g(=p{Mb2){Q>2p(nl-jr1kP+@;pQ5*m*&2dru9diWGt=}VNCkQVBRkh7T{M;?Z( zNreBJ9;1-cX#5+Jv(bjLXk?yb3#3PaZSpw#0-NL@{2Hx?;SsnDvaZU0NWYaPIE~hQ u5IylzkQi|TQcvszWCmiJpr`RDTnlsXMl?MS^KdalpNLQYZ81TZxcmo99lM4A literal 2082 zcmaLYdyv&*7zglY&pCUm-L1`Tm($j^mver|$R#9{YnU`{Aq_j60Qbt05GXOHD2jB z>~bD#(NP@24LmfW^dcU?KX|kaXYx3^b>oSg!_y)vcIQ~8>z)@;nXY>=S8!@Xr_r3r zO}sUta~mJz4lanOn#I>5s?&N`Gp%=XM9m(0(Ou1A<|Dcs&VgLU6C%3WJ0@{GZ;GhB zfKPE7KZ@u!jekV!l8Y#5E zT0ILKMm=P&kLcBxt7yHPI`g%hz*$_#9WpG9=-oy&_uk0IBN|SXskt@0&NCzW?89d{ zN4DJ~`j+!D8hE)}`s%6Tpom5@tWay?-7>D^@3eNmQT&|G$a;80Q#F@xysZ24Ia<59 zv&?1CdnCZG^*P5=Ix{+&Skk*@; z*2|(?Qj@MeAM>y2|EK>>pWEprJlZ5AhuO#8MH#htwy3A(mv|`Mm*OGne2nsG zYT|zSxRWyOC&S`y`b+m|*7u*xb26RdKHt$f>@%*ThK5hLRemROom`!b!YbOcU{3kx z=}zVMgH zr*gD={F^NOR@_5(q2e&^>Ars>(>HlD$IuM(KYKs8hw0fa^L#$f5we}n5)SrWSu9)M zD|Nnl>c7-^@9~BA(AS*Ed9wYH=kp3WxApDbU)yL-S^d{n&>nTOX^pySw#xV>@4;VL z!*^wDR+$G`#i=sBp8BdS=Rg^oRc062TE9f?U9RLB+5W;8sm79rWcwY>srnpVF4NEG zbJZz4L8dD>l*71}ESIvG`?Fq_PqLE5@F!4)_i;PxxK(!g2>q95xL#IM_^E8Z=W3oy zGt8~!N}fb}X5Zp64&gg`S;)63)6|mt>6x^K)JXS9O@DUk@7SQoIeIyfwVhG|m(`i`WXQ#G>7^&JP*J$KhIiI+cMW3^+Y~-CNGH)9|VZ^0wjloR;wXz z%BocZ=s&hfI-@Iq_@lpQJa<-AM{DaAnbG*TlrIrl#5D%Fvs@&n>zQVbQOKU*Aia%7 zll2}68{eOg$QsOX8D#$Wwqt3H`l^fdsLgzr!q(r^jrs`yGGeZa|Kaih6pQ&XbEU&p zRoB|8Z$bG|!R34biu_g8EYr4CTdz6ZPTRD}BNkb%;Q-!k({gO3ptz5fKAdK;gxoym zK{=Jj?@iblHtD_OHWUkGri*2Sa+4C0O3N(UPyi3#PgQlc+Bz9SG}}(2rZw4V7=&xw ztcKQNh9c{m3OG4_266`64Z7qul)@BRc2{+d%Rmlbkc2X4HnPHFz!1QM0B zh60y@#HEnnQa}L|;#>+SjDmL@=w66%DWDJvQ7#44p+Mg2G(%7wVW$CcDL??Zm36~? zd#L_qJE|03{pf3tC-y!kg%F^qp~V!XStzH1sG}3ct@|^1^Dt3z}Jx$L{cBFQjyJ7!Jys&wI> z`!~R%;sQO1!fxT#~S7K{D^l(JW>0QD0~8*Yq0{WJM70A`8u!l6V*eauQs<4{d|h{vf}p( z!(8Y8^w&cm?K1}~-114(tP0l(SfgC$$NJU7UYS#$(-bw5!dV$nmFxUO|I(RoD;_IR zr!4*5itsAed95G#&I4A4{~s`tqC5xtJl7!Cd99ywqHaa@*C`9nb1=tq4RW0y>nG|L ngzsfVc@NC-URYJG^Ar88K)7aA`gsrB&wF8B4QpnTv}VvtDfy_sjlI9rd4y*4a0Fw+mf!|l2BeQ z;>tftK2Cfi5DPT4jx2Brj z)ItNwmx?~+3oyxFQ`NCuS96W37o4+Amu#@a<~I;QyJ!ClT`FoCoz( zoUk_`XSt;Jl3!3AG&)^E2<0YZB$t+1wEhT&p`WVeZ8Z%thGaKAv#QtGUFd^r+^Ck` zV1`N7w>N@!7=hdX|9CF>1+`c}%l@j)aTzEP3^FjonT?|I7%&9%KJ;)2C@P;oH{lDKVza~0p zJwU3!RM=tl;y%-^ZGf-e07518iR>Se%E@{V@#PRj8%<2z@nTGQs z>gUSf|3EsJf4w(_@sCZ6_jZ}?zrpz3BF0Psh8ncKrC09&e18rQ>QDm`Gp}enRqCVd PEe*%r+hy?yZ9#zlyVZgb literal 756 zcmZ{hJ4*vW6ot<|vTJ;VC>Cl0rbsC)q!Ak-U?JFAsEv;*0fVrW<_8EisRf(uQm5N4 zoxP1%1|$T;5DP&=#Os~8tQiw*<}CN{@y*VK2irbC-m?8?%wiQjiYS=hn76(2>REUi zUIEKaYP)>1xp&`u+uDhP=ehwpMJjb~@)Lgkv9{v)ak`RLS~w?=Q}@+Xb8`}$s2xH#ZSNq)cXTgT9WUf=j=^-lQ;aTCmJ+lfBlN^d=JgBH|b4&!cR0R m3qQ^Rr_uPBu00Kohh4q30vahV5qr;~FYsJ!& z{SSLDzkm7Ne*SIi^QSi*STD(OcM+=9YR^uNI|rv7dU*Weg#QJ#41g8|3N4A>dZj2M z&YYu%3Mio0(W`^b{-$=`GrP?5MJy*N`Si`!pqc=1Vn8GZ~z+xut zdH%p;0(Y5U0Cwd(=Z-n@E*PESYqWJk19cfdo#)kBK>_nkyMM;0@6jUz11wBv6?v_~ z138B~^lX;}Jg2JD;ML;sE3@Jq%k8r>5ndHsB7PZ~^835K7X&o0x-6;Kb-)wVVq^t2 zGsvOmvw#@n%6=d7I8BTro;x|UN~3uVTp{9Zv>`s;Ffjk>_+8Aj1X#)>?x1Wmv_xaP zA+x)t6L)kG?nzOuIfwyCCl_+MpJvDVX!#<$XhXKQcv_-zx25`Vj{f!u{+|+Nh z@WxpEEvb4;yln%E*6`}P{-gI;(_>=io{)01{X+c((>CmX| zki*csaHH2o(rQ#URPR&Y;4efXrs#VPeb4Oe#t&s$^ggVP>^+>@@d2w)}&o z%MSeov!g;7WLRXZP#1vFK%o;LaUcNGU~v{fgnm|Z{jaA(^h0Q%Lo*@TA@u+MKyk2scAyz73;=Z; BE!F@4 diff --git a/tests/gds_ref_si220/taper_ro.gds b/tests/gds_ref_si220/taper_ro.gds index f9dea2356dbf10430d2f0dad804e8f3223c0fb3b..b9aad006aa789deec9f88836ae073baa19da022c 100644 GIT binary patch literal 1766 zcmai#!EVz)5QZm?vkj#!X(>JRP_+?GAxhVQ0JTJ=0!6AQDThd{KxpL#r?HyYk-ce> z3kPnz0TLH(yaG?a%fNv%7qh#`CJpsEK6uyG{4?{7cgBD~(@tSMrGe_oYUt?j8Ng~f zv$y~G@vEQT|Lnf_vGeWg+ZwEA)Vei<^7;Jp<7(~Tv_=lAFOS(H=ve@?$W>^G-;24l zD)6=U=)MLTl#1x}L2bWaTr})D^@D*GI+VKJd1zfaLFoFPvHjG*C9_g08^ATGBu(3) zHfi`l+ooK1Inn?;QpdSy*#o~z$yB`ZZpk!2Uk1?U0XA0}lddTR@pD#CsR_SE(~eL@EvhcgrmzeC-+-7<>f-L~+t3=e1c@a(qq zVRAXkSSWLFH(7aw>{b$4%kj=>Q;?Z>Sm~vttm=Uuh9q?AbaVpyA^Y~mP`MaWaq%TB z)9Sgb;5<&Ng0Wy&h&g-Gng+JVYxhoUtN?GAm|aEuei-Q{;A{L+J1#!cj|(m$&c{?u zs(7V^VAL5mV!BD%vc7Nvo}JiO4|Z5u{KRFI{5XbxE~d`I2-y0c-+Y2MCiF;2k1aKQ zMoEvYF+J;TPNFO6v8AF<$M|=>hCf8-y*B9{Ezx$m(^4win6nEpc{jSAIIToN%8A8J zt(aqTkT~Ht(X1IKr+s>Q+DB1@M$`Q-fRST+vC8*i6&Z~F7MY1YihlQ_=+{TSvY*l? Q0PBlf<$J!433MI)0UQ&=PXGV_ literal 242 zcmZQzV_;&6V31*CVt>rQ#URPR&Y;4efXrs#VPeb4Oe#t&s$^ggVP>^+>@@d2w)}&o z%MSeov!g;7WLWX&Wn(KzEJ!ViFUn_N;b353<7HxCW?Fd==w!6A^IWo|NpP2L-a#vprbPx!1~#NX0R{-0O?mJN&o-= From aad929744e4ed986936768f7f18e904f7d13d94e Mon Sep 17 00:00:00 2001 From: Joaquin Matres <4514346+joamatab@users.noreply.github.com> Date: Sun, 2 Jun 2024 07:53:22 -0700 Subject: [PATCH 05/15] clean more tests --- cspdk/si220/cells.py | 64 +- tests/gds_ref_si220/bend_rc.gds | Bin 5394 -> 5940 bytes tests/gds_ref_si220/bend_ro.gds | Bin 5394 -> 5972 bytes tests/gds_ref_si220/bend_s.oas | Bin 2100 -> 0 bytes tests/gds_ref_si220/bend_sc.gds | Bin 2082 -> 2954 bytes tests/gds_ref_si220/die_rc.gds | Bin 24498 -> 56344 bytes tests/gds_ref_si220/die_ro.gds | Bin 25778 -> 57486 bytes tests/gds_ref_si220/die_sc.gds | Bin 14194 -> 54122 bytes tests/gds_ref_si220/die_sc.oas | Bin 21544 -> 0 bytes tests/gds_ref_si220/die_so.gds | Bin 15474 -> 55188 bytes tests/gds_ref_si220/gc_rectangular_rc.gds | Bin 4220 -> 7772 bytes tests/gds_ref_si220/gc_rectangular_ro.gds | Bin 5500 -> 8888 bytes tests/gds_ref_si220/gc_rectangular_sc.gds | Bin 4092 -> 7714 bytes tests/gds_ref_si220/gc_rectangular_so.gds | Bin 5372 -> 8824 bytes tests/gds_ref_si220/grating_coupler_array.gds | Bin 4402 -> 10366 bytes tests/gds_ref_si220/grating_coupler_array.oas | Bin 2760 -> 10366 bytes tests/gds_ref_si220/mmi1x2_rc.gds | Bin 628 -> 2016 bytes tests/gds_ref_si220/mmi1x2_ro.gds | Bin 628 -> 2016 bytes tests/gds_ref_si220/mzi_rc.gds | Bin 13490 -> 18572 bytes tests/gds_ref_si220/mzi_sc.gds | Bin 6226 -> 15142 bytes tests/gds_ref_si220/mzi_so.gds | Bin 6226 -> 15048 bytes tests/gds_ref_si220/straight_rc.gds | Bin 246 -> 1610 bytes tests/gds_ref_si220/straight_sc.oas | Bin 460 -> 460 bytes tests/gds_ref_si220/taper_rc.gds | Bin 242 -> 1766 bytes tests/gds_ref_si220/taper_sc.oas | Bin 456 -> 456 bytes tests/gds_ref_si220/trans_sc_rc10.gds | Bin 248 -> 4348 bytes tests/gds_ref_si220/trans_sc_rc10.oas | Bin 526 -> 0 bytes tests/gds_ref_si220/trans_sc_rc20.gds | Bin 248 -> 4348 bytes tests/gds_ref_si220/trans_sc_rc20.oas | Bin 526 -> 0 bytes tests/gds_ref_si220/trans_sc_rc50.gds | Bin 248 -> 4348 bytes tests/gds_ref_si220/trans_sc_rc50.oas | Bin 526 -> 0 bytes tests/test_netlists_si220.py | 10 +- .../test_netlists_bend_sc_.yml | 2 +- .../test_netlists_die_sc_.yml | 3935 ++++++++--------- tests/test_netlists_si500.py | 3 +- tests/test_netlists_sin300.py | 3 +- 36 files changed, 1780 insertions(+), 2237 deletions(-) delete mode 100644 tests/gds_ref_si220/bend_s.oas delete mode 100644 tests/gds_ref_si220/die_sc.oas delete mode 100644 tests/gds_ref_si220/trans_sc_rc10.oas delete mode 100644 tests/gds_ref_si220/trans_sc_rc20.oas delete mode 100644 tests/gds_ref_si220/trans_sc_rc50.oas diff --git a/cspdk/si220/cells.py b/cspdk/si220/cells.py index 6e238ed..69e6a20 100644 --- a/cspdk/si220/cells.py +++ b/cspdk/si220/cells.py @@ -1132,7 +1132,7 @@ def mzi_ro( @gf.cell def pad( size: tuple[float, float] = (100.0, 100.0), - layer: LayerSpec = LAYER.PAD, + layer: LayerSpec = "PAD", bbox_layers: None = None, bbox_offsets: None = None, port_inclusion: float = 0.0, @@ -1190,42 +1190,16 @@ def rectangle( ) -@gf.cell -def grating_coupler_array( - pitch: float = 127.0, - n: int = 6, - port_name: str = "o1", - rotation: float = -90, - with_loopback: bool = False, - grating_coupler_spacing: float = 0.0, - grating_coupler: ComponentSpec = gc_rectangular_sc, - cross_section: CrossSectionSpec = "xs_sc", -) -> gf.Component: - """An array of grating couplers. - - Args: - pitch (float, optional): the center-center pitch between grating couplers. Defaults to 127.0. - n (int, optional): the number of grating couplers to place. Defaults to 6. - port_name (str, optional): the routing port of the grating coupler to be placed. Defaults to "o1". - rotation (float, optional): rotation of the grating couplers, in degrees. Defaults to 0.0. - with_loopback (bool, optional): if True, adds a loopback. Defaults to False. - bend (ComponentSpec, optional): the bend to be used for the loopback. Defaults to _bend. - grating_coupler_spacing (float, optional): the spacing to be used in the loopback. Defaults to 0.0. - grating_coupler (ComponentSpec, optional): the grating coupler component to use. - cross_section (CrossSectionSpec, optional): the cross section to be used for routing in the loopback. - - Returns: - gf.Component: the component - """ - return gf.components.grating_coupler_array( - pitch=pitch, - n=n, - port_name=port_name, - rotation=rotation, - with_loopback=with_loopback, - grating_coupler=grating_coupler, - cross_section=cross_section, - ) +grating_coupler_array = partial( + gf.components.grating_coupler_array, + pitch=127, + n=6, + port_name="o1", + rotation=-90, + with_loopback=False, + grating_coupler="gc_rectangular_sc", + cross_section="xs_sc", +) @gf.cell @@ -1235,9 +1209,9 @@ def _die( npads: int = 31, grating_pitch: float = 250.0, pad_pitch: float = 300.0, - grating_coupler: ComponentSpec = gc_rectangular_sc, + grating_coupler: ComponentSpec = "gc_rectangular_sc", cross_section: CrossSectionSpec = "xs_sc", - pad: ComponentSpec = pad, + pad: ComponentSpec = "pad", ) -> gf.Component: c = gf.Component() @@ -1257,24 +1231,24 @@ def _die( left = c << gca left.drotate(-90) left.dxmax = x0 - left.dy = fp.y + left.dy = fp.dy c.add_ports(left.ports, prefix="W") right = c << gca right.drotate(+90) right.dxmax = -x0 - right.dy = fp.y + right.dy = fp.dy c.add_ports(right.ports, prefix="E") # Add electrical ports x0 = -4615 y0 = 2200 - pad = pad() + pad = gf.get_component(pad) for i in range(npads): pad_ref = c << pad pad_ref.dxmin = x0 + i * pad_pitch - pad_ref.ymin = y0 + pad_ref.dymin = y0 c.add_port( name=f"N{i}", port=pad_ref.ports["e4"], @@ -1424,8 +1398,8 @@ def crossing_sc() -> gf.Component: if __name__ == "__main__": - c = die_sc() - # c = mzi_rc() + # c = die_sc() + c = mzi_rc() # c = trans_sc_rc20() # c = crossing_sc() c.show() diff --git a/tests/gds_ref_si220/bend_rc.gds b/tests/gds_ref_si220/bend_rc.gds index c61debd5340e22d429ece5e512e7ae3a21840e63..2ec79bedee1ee99f74b1c7ab13c953bfc577fa22 100644 GIT binary patch literal 5940 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 VnRKQ1^Ys58<>Pd|^5iTh{uibyLLvYF diff --git a/tests/gds_ref_si220/bend_ro.gds b/tests/gds_ref_si220/bend_ro.gds index e4bba42ce5979bb76b2795b05072940632deff1b..172daa87fbaec9c63a31246f8995556cdc68bcee 100644 GIT binary patch literal 5972 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 PNeTaYHvjWnGYtI=kc(+C diff --git a/tests/gds_ref_si220/bend_sc.gds b/tests/gds_ref_si220/bend_sc.gds index 9531fe819ceafaa134dd99a3b40978777e4fd1f1..f398b3495a87039415e29a1b85574f271e90dd42 100644 GIT binary patch literal 2954 zcma);X=of}7{{MIGMi&_>~?LdMjG7cwl;2Oljd4$X53K=fi;UxDHyOH_U1uka z(GQ}a)o&h9g;uRat%9PWsHKP>L_tJEQB)K>(Rx8EczmnBnc3};?4$;=znS4V{?GG1 z@4O?51cN)JGY}NNzs8`lW%F8*M5tl)$OD7dzIWur@HKC)ICNmwsB|{?`z=(I?(Xh& z+qaFb-#Myn-nL;ow-Rm?QCb}@rOo;6=?M8Hp7UH;9+aS@dz7u~M@N>X_7wD-U9x7f zmAq{m=6EH$H*Zypl9^g|bxKWV2GVLu9G}2^K9hM{*9s+TQnwwyy}h1~$bf&IJw<({ zG-Ye&HuOWvRcqX=BWuz}z&7p7q7^SzG#;F;&4lS?qUXUnH0N$1XdDNj2T? zMZQq3@V7O~CBw9x(5ICwo=*u)&0A+%FOrr1(Q{U*Qqd~;oLyb<+qui7JY5Ut%cGm) z#k^YwtKxL?0$J^!M=usj)7qqAYF?+by+*01SCba>VkPg!NYu$#E?IUZo5|J|Bw1Y9 z?`2#dLvu3LtZhp-ou=(}FS6FK+SRYBYLD}Cvzsd}s;{>bMYCthPJyXX**0=|(VOU4 zopr6{H1$c(p*sB?b#_e6wzf>W-HUABimIx1-wvpCI?z^UZM_U?c6J9cLu!}WLmIdG zR^RH?tbg@U-|E%u>FU|P&OEgERb!o=)^R=0ylIks>Ha>i;)efGas6I}rQ0X>={H)^d!vQwOpMzbUun)1 z1nbPR>orG?V;zvL`fA2z+5{gbjHo zZ0MkO?wmSxYDx7({Z1s1;M(C6@OzOUgCIY`m*LMMAqG-@gKxtV9BCWizwkqR0wN7J zz*hJL>|`m~oPjIhw{S!xl7lxvVg_uH#xeK^`~ki$(sT>_8h%AiN^Bm5gOGT^+eBJ+ z!B?Pj|F=k#d?DgSKY$O3w61|aLG*@s-iC(oA$TvE_@Zb#4mtkXH+mY|52I}p_y54R zL^^mL;SlS17!sr7bF`&JVv8X6vFp*c65b78gfF0NDf|RJ1OGx>H^)4EYhtI-a;Sv7nqr(OF z^D$Hw^ zZ5Cxs7yLqfA3^%wG6J8c{@36=Fa>XPHq<{P1OZ4?mh@J=<;>1;m6MF}) zgXnFb53vXwKqG4xego3;@G3N77kU?Nfum^rm^nBOcR<#pmD++`a6NN`UGyj{!)5SN zwC)#?FJS;uXY(|qpG|+j4CGoAw0_FI_y%r-%thoVd=f5)_hE7n5?5)1^rPV}xD|~j r;RwvZK{S2_nSszIh=$-XNS}jgh@QY<*aa8DCFB;D?$;tM2#4iApER}g literal 2082 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 G?Ee5DdKo(a literal 24498 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