Skip to content

feat(lumerical): add LayerStack sidewall angles - #767

Open
joamatab wants to merge 2 commits into
mainfrom
dev
Open

feat(lumerical): add LayerStack sidewall angles#767
joamatab wants to merge 2 commits into
mainfrom
dev

Conversation

@joamatab

@joamatab joamatab commented Aug 13, 2026

Copy link
Copy Markdown
Contributor

Summary

  • use Lumerical Layer Builder for LayerStack layers with a nonzero sidewall angle
  • map gdsfactory’s normal-relative sidewall angle to Lumerical’s vertical-relative convention
  • retain GDS import for layers with vertical sidewalls

Validation

  • uv run pytest gplugins/lumerical/tests/test_write_sparameters_lumerical.py gplugins/lumerical/tests/test_background_layers.py gplugins/lumerical/tests/test_netlist.py gplugins/lumerical/tests/test_netlist_get_routes.py -q
  • uv run ruff check gplugins/lumerical/write_sparameters_lumerical.py gplugins/lumerical/tests/test_write_sparameters_lumerical.py

Summary by Sourcery

Support sidewall angles in Lumerical exports by using Layer Builder for non-vertical LayerStack layers while retaining GDS import for vertical layers.

New Features:

  • Handle LayerStack layers with nonzero sidewall angles in Lumerical via Layer Builder and map sidewall angle conventions between gdsfactory and Lumerical.

Enhancements:

  • Require material database names when using Layer Builder sidewall angles to prevent invalid configurations.

Tests:

  • Extend Lumerical S-parameters tests to cover use of Layer Builder for layers with sidewall angles and verify correct sidewall angle mapping.

@sourcery-ai

sourcery-ai Bot commented Aug 13, 2026

Copy link
Copy Markdown
Contributor

Reviewer's Guide

This PR updates the Lumerical s-parameters writer to use the Layer Builder for layers with nonzero sidewall angles while keeping the existing GDS import path for vertical sidewalls, and adds tests to validate the sidewall angle mapping and Layer Builder behavior.

Sequence diagram for Layer Builder vs GDS import in write_sparameters_lumerical

sequenceDiagram
    participant write_sparameters_lumerical
    participant LayerStack
    participant LumericalSession_s

    write_sparameters_lumerical->>LayerStack: iterate layers
    loop for each level in layer_stack.layers.values
        write_sparameters_lumerical->>LayerStack: get level.sidewall_angle
        alt level.sidewall_angle is nonzero
            write_sparameters_lumerical->>LumericalSession_s: addlayerbuilder()
            write_sparameters_lumerical->>LumericalSession_s: set(name = gplugins_layer_builder)
            write_sparameters_lumerical->>LumericalSession_s: loadgdsfile(gdspath)
            write_sparameters_lumerical->>LumericalSession_s: addlayer(layer_layer_tuple)
            write_sparameters_lumerical->>LumericalSession_s: setlayer(layer_layer_tuple, "layer number", layer_tuple)
            write_sparameters_lumerical->>LumericalSession_s: setlayer(layer_layer_tuple, "start position", zmin)
            write_sparameters_lumerical->>LumericalSession_s: setlayer(layer_layer_tuple, "thickness", thickness)
            write_sparameters_lumerical->>LumericalSession_s: setlayer(layer_layer_tuple, "sidewall angle", 90 - level.sidewall_angle)
            write_sparameters_lumerical->>LumericalSession_s: setlayer(layer_layer_tuple, "pattern material", material)
        else level.sidewall_angle is zero
            write_sparameters_lumerical->>LumericalSession_s: gdsimport(gdspath, "top", layer_tuple)
            write_sparameters_lumerical->>LumericalSession_s: setnamed(GDS_LAYER_layer_tuple, "z", z)
            write_sparameters_lumerical->>LumericalSession_s: setnamed(GDS_LAYER_layer_tuple, "z span", thickness)
            write_sparameters_lumerical->>LumericalSession_s: set_material(session = s, structure = GDS_LAYER_layer_tuple, material = material)
        end
    end
Loading

File-Level Changes

Change Details Files
Use Lumerical Layer Builder for non-vertical sidewall layers and keep GDS import for vertical layers.
  • Introduce layer_builder_name and has_layer_builder flags to manage a single Layer Builder instance per export.
  • For levels with sidewall_angle set, create and configure a Layer Builder: addlayerbuilder, name it, and load the GDS file once.
  • For sidewall layers, add a Layer Builder layer, set layer number, start position, thickness, and convert gdsfactory’s sidewall_angle (normal-relative) to Lumerical’s vertical-relative convention via 90 - sidewall_angle.
  • Require material to be a string (database name) for Layer Builder usage and raise a ValueError otherwise.
  • Retain the previous gdsimport-based path unchanged for layers with zero sidewall_angle, including z and z span settings and material assignment.
gplugins/lumerical/write_sparameters_lumerical.py
Extend the Lumerical session test double and add a test to validate Layer Builder sidewall angle handling.
  • Add layer_settings storage on the _Session test double to record setlayer calls.
  • Stub out addlayerbuilder, set, loadgdsfile, addlayer, and implement setlayer to append arguments to layer_settings in the _Session class.
  • Add a test that builds a LayerStack with a nonzero sidewall_angle, runs write_sparameters_lumerical, and asserts the Layer Builder sidewall angle is mapped to 90 - sidewall_angle (i.e., 80 degrees for 10-degree input).
gplugins/lumerical/tests/test_write_sparameters_lumerical.py

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@github-actions

Copy link
Copy Markdown

Label error. Requires at least 1 of: breaking, bug, github_actions, documentation, dependencies, enhancement, feature, maintenance, security. Found:

@sourcery-ai sourcery-ai Bot left a comment

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.

Hey - I've found 1 issue, and left some high level feedback:

  • The if level.sidewall_angle: check will skip using the layer builder when sidewall_angle is 0 but also if it is None; consider explicitly distinguishing between None and numeric values to avoid surprises if sidewall_angle=0.0 is ever meaningful.
  • The material type validation for sidewall-angle layers is done inside the loop; if non-string materials are allowed elsewhere, it might be clearer to validate or convert materials before this branch so the behaviour is consistent across layer types.
  • The layer_builder_name constant is set but only used in a single set call; consider inlining it or moving it to a shared constant if it needs to be reused/configured in other places.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- The `if level.sidewall_angle:` check will skip using the layer builder when `sidewall_angle` is 0 but also if it is `None`; consider explicitly distinguishing between `None` and numeric values to avoid surprises if `sidewall_angle=0.0` is ever meaningful.
- The material type validation for sidewall-angle layers is done inside the loop; if non-string materials are allowed elsewhere, it might be clearer to validate or convert materials before this branch so the behaviour is consistent across layer types.
- The `layer_builder_name` constant is set but only used in a single `set` call; consider inlining it or moving it to a shared constant if it needs to be reused/configured in other places.

## Individual Comments

### Comment 1
<location path="gplugins/lumerical/write_sparameters_lumerical.py" line_range="521-526" />
<code_context>
+                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)
+            else:
+                s.gdsimport(str(gdspath), "top", f"{layer_tuple[0]}:{layer_tuple[1]}")
</code_context>
<issue_to_address>
**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.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment on lines +521 to +526
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)

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant