Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .changelog.d/767.added
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Lumerical simulations now honor LayerStack sidewall angles through the Layer Builder.
43 changes: 43 additions & 0 deletions gplugins/lumerical/tests/test_write_sparameters_lumerical.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
class _Session:
def __init__(self) -> None:
self.ports = 0
self.layer_settings: list[tuple[object, ...]] = []

def newproject(self) -> None:
pass
Expand All @@ -32,6 +33,21 @@ def addfdtd(self, **kwargs: object) -> None:
def gdsimport(self, *args: object) -> None:
pass

def addlayerbuilder(self) -> None:
pass

def set(self, *args: object) -> None:
pass

def loadgdsfile(self, *args: object) -> None:
pass

def addlayer(self, *args: object) -> None:
pass

def setlayer(self, *args: object) -> None:
self.layer_settings.append(args)

def addport(self) -> None:
self.ports += 1

Expand Down Expand Up @@ -76,3 +92,30 @@ def test_keeps_ports_on_layers_not_in_layer_stack(monkeypatch, tmp_path) -> None

assert result is session
assert session.ports == 2


def test_uses_layer_builder_for_sidewall_angle(monkeypatch, tmp_path) -> None:
monkeypatch.setitem(sys.modules, "lumapi", ModuleType("lumapi"))
component = gf.components.straight(length=10, cross_section="strip")
layer_stack = LayerStack(
layers={
"core": LayerLevel(
layer=(1, 0),
thickness=0.22,
zmin=0,
material="sio2",
sidewall_angle=10,
)
}
)
session = _Session()

write_sparameters_lumerical(
component,
session=session,
run=False,
dirpath=tmp_path,
layer_stack=layer_stack,
)

assert ("layer_1_0", "sidewall angle", 80) in session.layer_settings
34 changes: 29 additions & 5 deletions gplugins/lumerical/write_sparameters_lumerical.py
Original file line number Diff line number Diff line change
Expand Up @@ -459,6 +459,8 @@ def write_sparameters_lumerical(

exclude_layers = exclude_layers or []
polygons_per_layer = component_extended_beyond_pml.get_polygons_points(merge=True)
layer_builder_name = "gplugins_layer_builder"
has_layer_builder = False

for level in layer_stack.layers.values():
layer = level.layer
Expand Down Expand Up @@ -501,11 +503,33 @@ def write_sparameters_lumerical(
zmax = zmin + thickness
z = (zmax + zmin) / 2

s.gdsimport(str(gdspath), "top", f"{layer_tuple[0]}:{layer_tuple[1]}")
layername = f"GDS_LAYER_{layer_tuple[0]}:{layer_tuple[1]}"
s.setnamed(layername, "z", z * 1e-6)
s.setnamed(layername, "z span", thickness * 1e-6)
set_material(session=s, structure=layername, material=material)
if level.sidewall_angle:
if not has_layer_builder:
s.addlayerbuilder()
s.set("name", layer_builder_name)
s.loadgdsfile(str(gdspath))
has_layer_builder = True

layername = f"layer_{layer_tuple[0]}_{layer_tuple[1]}"
s.addlayer(layername)
s.setlayer(
layername, "layer number", f"{layer_tuple[0]}:{layer_tuple[1]}"
)
s.setlayer(layername, "start position", zmin * 1e-6)
s.setlayer(layername, "thickness", thickness * 1e-6)
s.setlayer(layername, "sidewall angle", 90 - level.sidewall_angle)
if not isinstance(material, str):
raise ValueError(
"Layer Builder sidewall angles require a material database name. "
f"Got {material!r} for layer {layer_tuple}."
)
s.setlayer(layername, "pattern material", material)
Comment on lines +521 to +526

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue (bug_risk): Material handling diverges between sidewall and non-sidewall paths and may break non-string material usages.

In the previous flow, set_material accepted various material representations for all layers. In the sidewall-angle branch you now enforce material to be a string and skip set_material, which will raise for existing callers that pass non-string materials that were previously valid. It also introduces inconsistent type requirements between sidewall and non-sidewall paths. Please either normalize material to a string earlier and continue using set_material, or otherwise ensure both paths accept the same material types and follow a single configuration mechanism.

else:
s.gdsimport(str(gdspath), "top", f"{layer_tuple[0]}:{layer_tuple[1]}")
layername = f"GDS_LAYER_{layer_tuple[0]}:{layer_tuple[1]}"
s.setnamed(layername, "z", z * 1e-6)
s.setnamed(layername, "z span", thickness * 1e-6)
set_material(session=s, structure=layername, material=material)
logger.info(
f"adding {layer_tuple}, thickness = {thickness} um, zmin = {zmin} um "
)
Expand Down
Loading