Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Serialize output for variable width/offsets in Section and CrossSection #2466

Merged
merged 4 commits into from Jan 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
19 changes: 18 additions & 1 deletion gdsfactory/cross_section.py
Expand Up @@ -15,7 +15,8 @@
from types import ModuleType
from typing import TYPE_CHECKING, Any, Literal

from pydantic import BaseModel, ConfigDict, Field
import numpy as np
from pydantic import BaseModel, ConfigDict, Field, field_serializer

from gdsfactory.config import CONF, ErrorType

Expand Down Expand Up @@ -93,6 +94,13 @@ class Section(BaseModel):

model_config = ConfigDict(extra="forbid", frozen=True)

@field_serializer("width_function", "offset_function")
def serialize_functions(self, func: Callable | None) -> str | None:
if func is None:
return None
t_values = np.linspace(0, 1, 11)
return ",".join([str(round(width, 3)) for width in func(t_values)])


class ComponentAlongPath(BaseModel):
"""A ComponentAlongPath object to place along an extruded path.
Expand Down Expand Up @@ -386,6 +394,15 @@ class Transition(CrossSection):
cross_section2: CrossSectionSpec
width_type: WidthTypes | Callable = "sine"

@field_serializer("width_type")
def serialize_width(self, width_type: WidthTypes | Callable) -> str | None:
if isinstance(width_type, str):
return width_type
t_values = np.linspace(0, 1, 10)
return ",".join(
[str(round(width, 3)) for width in width_type(t_values, *self.width)]
)

@property
def width(self) -> tuple[float, float]:
return (
Expand Down
2 changes: 1 addition & 1 deletion gdsfactory/path.py
Expand Up @@ -617,7 +617,7 @@ def dWdx(w, x, neff_w, wavelength, alpha):
def transition(
cross_section1: CrossSectionSpec,
cross_section2: CrossSectionSpec,
width_type: WidthTypes = "sine",
width_type: WidthTypes | Callable = "sine",
) -> Transition:
"""Returns a smoothly-transitioning between two CrossSections.

Expand Down
29 changes: 29 additions & 0 deletions tests/test_cross_section.py
Expand Up @@ -147,6 +147,35 @@ def test_extrude_transition_component():
assert c["o2"].width == w2


def test_cross_section_variable_width_section():
"""Make sure serialization for Section with variable width is different for different width functions.
This will fail if @gf.cell is reusing the first Component."""
import numpy as np

def get_custom_width_func(n: int = 1):
def _width_func(t):
return 3 + np.cos(2 * np.pi * t * n)

return _width_func

P = gf.path.straight(length=40, npoints=100)

s1 = gf.Section(
width=0, width_function=get_custom_width_func(n=1), offset=0, layer=(1, 0)
)
s2 = gf.Section(
width=0, width_function=get_custom_width_func(n=5), offset=0, layer=(1, 0)
)

X1 = gf.CrossSection(sections=(s1,))
X2 = gf.CrossSection(sections=(s2,))

p1 = gf.path.extrude(P, cross_section=X1)
p2 = gf.path.extrude(P, cross_section=X2)

assert p1 is not p2


if __name__ == "__main__":
# test_transition_names()
# test_copy()
Expand Down
56 changes: 56 additions & 0 deletions tests/test_paths_transition4.py
@@ -0,0 +1,56 @@
from __future__ import annotations

import numpy as np

import gdsfactory as gf


def get_custom_width_func(n: int = 5):
def _width_func(t, y1, y2):
return (y2 - y1) + np.cos(2 * np.pi * t * n)

return _width_func


def test_transition_type_callable_multiple() -> None:
"""Ensure that the width_type serialization works properly"""
width1 = 1.0
width2 = 2.0
x1 = gf.cross_section.strip(width=width1)
x2 = gf.cross_section.strip(width=width2)

p = gf.path.straight(length=10, npoints=100)

xt1 = gf.path.transition(
cross_section1=x1, cross_section2=x2, width_type=get_custom_width_func(n=1)
)
xt2 = gf.path.transition(
cross_section1=x1, cross_section2=x2, width_type=get_custom_width_func(n=5)
)
s1 = p.extrude(cross_section=xt1)
s2 = p.extrude(cross_section=xt2)
assert s1 is not s2


if __name__ == "__main__":
test_transition_type_callable_multiple()

width1 = 1.0
width2 = 2.0
x1 = gf.cross_section.strip(width=width1)
x2 = gf.cross_section.strip(width=width2)

p = gf.path.straight(length=10, npoints=100)

xt1 = gf.path.transition(
cross_section1=x1, cross_section2=x2, width_type=get_custom_width_func(n=1)
)
xt2 = gf.path.transition(
cross_section1=x1, cross_section2=x2, width_type=get_custom_width_func(n=5)
)
c = gf.Component()
s1 = c << p.extrude(cross_section=xt1)
s2 = c << p.extrude(cross_section=xt2)
s2.movey(-10)

c.show()