Skip to content

Commit 8dec0cb

Browse files
feat(tidy3d): FXC-3982-support-lydrc-in-klayout-plugin
1 parent e73fbb4 commit 8dec0cb

File tree

3 files changed

+62
-13
lines changed

3 files changed

+62
-13
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
2929
- Added configurable local simulation result caching with checksum validation, eviction limits, and per-call overrides across `web.run`, `web.load`, and job workflows.
3030
- Added `DirectivityMonitorSpec` for automated creation and configuration of directivity radiation monitors in `TerminalComponentModeler`.
3131
- Added multimode support to `WavePort` in the smatrix plugin, allowing multiple modes to be analyzed per port.
32+
- Added support for `.lydrc` files for design rule checking in the `klayout` plugin.
3233

3334
### Breaking Changes
3435
- Edge singularity correction at PEC and lossy metal edges defaults to `True`.

tests/test_plugins/klayout/drc/test_drc.py

Lines changed: 54 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,25 @@ def good_drcrunset_content():
5353
report("DRC results", $resultsfile)
5454
"""
5555

56+
@staticmethod
57+
def wrap_drc_to_lydrc(body: str):
58+
"""Return the XML-wrapped .lydrc runset content."""
59+
xml = f"""\
60+
<?xml version="1.0" encoding="utf-8"?>
61+
<klayout-macro>
62+
<description>Test DRC runset</description>
63+
<version/>
64+
<category>drc</category>
65+
<prolog/>
66+
<epilog/>
67+
<text>
68+
{body}
69+
</text>
70+
</klayout-macro>
71+
"""
72+
73+
return xml
74+
5675
@staticmethod
5776
@pytest.fixture(scope="class")
5877
def bad_drcrunset_content_source():
@@ -147,22 +166,34 @@ def mock_run_drc_on_gds(config):
147166
)
148167

149168
@pytest.mark.parametrize("verbose", [True, False])
169+
@pytest.mark.parametrize("drc_file_suffix", [".drc", ".lydrc"])
150170
def test_valid_run_on_gds(
151-
self, monkeypatch, tmp_path, verbose, geom, geom_to_gds_kwargs, good_drcrunset_content
171+
self,
172+
monkeypatch,
173+
tmp_path,
174+
verbose,
175+
geom,
176+
geom_to_gds_kwargs,
177+
good_drcrunset_content,
178+
drc_file_suffix,
152179
):
153180
"""Test that no error is raised when runs on a gds are valid"""
154181
geom.to_gds_file(tmp_path / "test.gds", **geom_to_gds_kwargs)
155-
self.write_drcrunset(tmp_path, "good_drcfile.drc", good_drcrunset_content)
182+
drc_content = good_drcrunset_content
183+
if drc_file_suffix == ".lydrc":
184+
drc_content = TestDRCRunner.wrap_drc_to_lydrc(drc_content)
185+
self.write_drcrunset(tmp_path, f"good_drcfile{drc_file_suffix}", drc_content)
156186
self.run(
157187
monkeypatch=monkeypatch,
158-
drc_runsetfile=tmp_path / "good_drcfile.drc",
188+
drc_runsetfile=tmp_path / f"good_drcfile{drc_file_suffix}",
159189
verbose=verbose,
160190
source=tmp_path / "test.gds",
161191
td_object_gds_savefile=tmp_path / "test.gds",
162192
resultsfile=filepath / "drc_results.lyrdb",
163193
)
164194

165195
@pytest.mark.parametrize("verbose", [True, False])
196+
@pytest.mark.parametrize("drc_file_suffix", [".drc", ".lydrc"])
166197
@pytest.mark.parametrize(
167198
"td_object, obj_to_gds_kwargs",
168199
[
@@ -180,12 +211,16 @@ def test_valid_run_on_td_object(
180211
td_object,
181212
obj_to_gds_kwargs,
182213
good_drcrunset_content,
214+
drc_file_suffix,
183215
):
184216
"""Test that no error is raised when runs on a Geometry, Structure, or Simulation are valid"""
185-
self.write_drcrunset(tmp_path, "good_drcfile.drc", good_drcrunset_content)
217+
drc_content = good_drcrunset_content
218+
if drc_file_suffix == ".lydrc":
219+
drc_content = TestDRCRunner.wrap_drc_to_lydrc(drc_content)
220+
self.write_drcrunset(tmp_path, f"good_drcfile{drc_file_suffix}", drc_content)
186221
self.run(
187222
monkeypatch=monkeypatch,
188-
drc_runsetfile=tmp_path / "good_drcfile.drc",
223+
drc_runsetfile=tmp_path / f"good_drcfile{drc_file_suffix}",
189224
verbose=verbose,
190225
source=request.getfixturevalue(td_object),
191226
td_object_gds_savefile=tmp_path / "test.gds",
@@ -196,18 +231,27 @@ def test_valid_run_on_td_object(
196231
@pytest.mark.parametrize(
197232
"bad_drcrunset_content", ["bad_drcrunset_content_source", "bad_drcrunset_content_report"]
198233
)
234+
@pytest.mark.parametrize("drc_file_suffix", [".drc", ".lydrc"])
199235
def test_check_drcfile_format_invalid(
200-
self, request, monkeypatch, tmp_path, geom, geom_to_gds_kwargs, bad_drcrunset_content
236+
self,
237+
request,
238+
monkeypatch,
239+
tmp_path,
240+
geom,
241+
geom_to_gds_kwargs,
242+
bad_drcrunset_content,
243+
drc_file_suffix,
201244
):
202245
"""Tests that ValidationError is raised when the drc file content is invalid"""
203246
geom.to_gds_file(tmp_path / "test.gds", **geom_to_gds_kwargs)
204-
self.write_drcrunset(
205-
tmp_path, "bad_drcrunset.drc", request.getfixturevalue(bad_drcrunset_content)
206-
)
247+
drc_content = request.getfixturevalue(bad_drcrunset_content)
248+
if drc_file_suffix == ".lydrc":
249+
drc_content = TestDRCRunner.wrap_drc_to_lydrc(drc_content)
250+
self.write_drcrunset(tmp_path, f"bad_drcrunset{drc_file_suffix}", drc_content)
207251
with pytest.raises(pd.ValidationError) as e:
208252
self.run(
209253
monkeypatch=monkeypatch,
210-
drc_runsetfile=tmp_path / "bad_drcrunset.drc",
254+
drc_runsetfile=tmp_path / f"bad_drcrunset{drc_file_suffix}",
211255
verbose=True,
212256
source=tmp_path / "test.gds",
213257
td_object_gds_savefile=None,

tidy3d/plugins/klayout/drc/drc.py

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@
2424
from tidy3d.plugins.klayout.drc.results import DRCResults
2525
from tidy3d.plugins.klayout.util import check_installation
2626

27+
SUPPORTED_DRC_SUFFIXES: frozenset[str] = frozenset({".drc", ".lydrc"})
28+
2729

2830
class DRCConfig(Tidy3dBaseModel):
2931
"""Configuration for KLayout DRC."""
@@ -54,9 +56,11 @@ def _validate_gdsfile_filetype(cls, v: pd.FilePath) -> pd.FilePath:
5456

5557
@validator("drc_runset")
5658
def _validate_drc_runset_filetype(cls, v: pd.FilePath) -> pd.FilePath:
57-
"""Check DRC runset filetype is ``.drc``."""
58-
if v.suffix != ".drc":
59-
raise ValidationError(f"DRC runset file '{v}' must end with '.drc'.")
59+
"""Check DRC runset filetype is ``.drc`` or ``.lydrc``."""
60+
if v.suffix not in SUPPORTED_DRC_SUFFIXES:
61+
raise ValidationError(
62+
f"DRC runset file '{v}' must end with one of {', '.join(SUPPORTED_DRC_SUFFIXES)}."
63+
)
6064
return v
6165

6266
@validator("drc_runset")

0 commit comments

Comments
 (0)