witwin-maxwell 0.2.0__cp310-cp310-win_amd64.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- witwin/maxwell/__init__.py +137 -0
- witwin/maxwell/adapters/__init__.py +1 -0
- witwin/maxwell/adapters/gds.py +203 -0
- witwin/maxwell/adapters/tidy3d.py +663 -0
- witwin/maxwell/adjoint_inputs.py +66 -0
- witwin/maxwell/compiler/__init__.py +11 -0
- witwin/maxwell/compiler/materials.py +761 -0
- witwin/maxwell/compiler/monitors.py +164 -0
- witwin/maxwell/compiler/sources.py +214 -0
- witwin/maxwell/fdfd/__init__.py +3 -0
- witwin/maxwell/fdfd/adjoint/__init__.py +3 -0
- witwin/maxwell/fdfd/adjoint/bridge.py +178 -0
- witwin/maxwell/fdfd/ams.py +300 -0
- witwin/maxwell/fdfd/krylov.py +280 -0
- witwin/maxwell/fdfd/plotting.py +110 -0
- witwin/maxwell/fdfd/postprocess.py +33 -0
- witwin/maxwell/fdfd/solver.py +1034 -0
- witwin/maxwell/fdtd/__init__.py +4 -0
- witwin/maxwell/fdtd/adjoint/__init__.py +22 -0
- witwin/maxwell/fdtd/adjoint/bridge.py +480 -0
- witwin/maxwell/fdtd/adjoint/core.py +2477 -0
- witwin/maxwell/fdtd/adjoint/dispatch.py +415 -0
- witwin/maxwell/fdtd/adjoint/profiler.py +181 -0
- witwin/maxwell/fdtd/adjoint/reference.py +1003 -0
- witwin/maxwell/fdtd/adjoint/reverse_common.py +467 -0
- witwin/maxwell/fdtd/adjoint/seeds.py +479 -0
- witwin/maxwell/fdtd/boundary/__init__.py +37 -0
- witwin/maxwell/fdtd/boundary/bloch.py +90 -0
- witwin/maxwell/fdtd/boundary/common.py +41 -0
- witwin/maxwell/fdtd/boundary/cpml.py +522 -0
- witwin/maxwell/fdtd/boundary/runtime.py +209 -0
- witwin/maxwell/fdtd/checkpoint.py +175 -0
- witwin/maxwell/fdtd/coords.py +32 -0
- witwin/maxwell/fdtd/cuda/__init__.py +11 -0
- witwin/maxwell/fdtd/cuda/backend.py +2520 -0
- witwin/maxwell/fdtd/cuda/build.py +242 -0
- witwin/maxwell/fdtd/cuda/extension.cpp +1235 -0
- witwin/maxwell/fdtd/cuda/kernels/adjoint.cu +3244 -0
- witwin/maxwell/fdtd/cuda/kernels/boundary.cu +354 -0
- witwin/maxwell/fdtd/cuda/kernels/common.cu +63 -0
- witwin/maxwell/fdtd/cuda/kernels/common.cuh +82 -0
- witwin/maxwell/fdtd/cuda/kernels/dispersive.cu +475 -0
- witwin/maxwell/fdtd/cuda/kernels/electric.cu +2298 -0
- witwin/maxwell/fdtd/cuda/kernels/magnetic.cu +981 -0
- witwin/maxwell/fdtd/cuda/kernels/observers.cu +350 -0
- witwin/maxwell/fdtd/cuda/kernels/projection.cu +214 -0
- witwin/maxwell/fdtd/cuda/kernels/sources.cu +1485 -0
- witwin/maxwell/fdtd/cuda/kernels/spectral.cu +300 -0
- witwin/maxwell/fdtd/cuda/launch.h +16 -0
- witwin/maxwell/fdtd/cuda/prebuilt/witwin_maxwell_fdtd_cuda.pyd +0 -0
- witwin/maxwell/fdtd/cuda/runtime/__init__.py +6 -0
- witwin/maxwell/fdtd/cuda/runtime/dispatcher.py +6 -0
- witwin/maxwell/fdtd/cuda/runtime/graph.py +33 -0
- witwin/maxwell/fdtd/cuda/runtime/parity.py +9 -0
- witwin/maxwell/fdtd/cuda/tensors.h +22 -0
- witwin/maxwell/fdtd/dispersion.py +48 -0
- witwin/maxwell/fdtd/evaluation.py +290 -0
- witwin/maxwell/fdtd/excitation/__init__.py +25 -0
- witwin/maxwell/fdtd/excitation/injection.py +881 -0
- witwin/maxwell/fdtd/excitation/modes.py +1380 -0
- witwin/maxwell/fdtd/excitation/spatial.py +431 -0
- witwin/maxwell/fdtd/excitation/temporal.py +458 -0
- witwin/maxwell/fdtd/excitation/tfsf_apply.py +227 -0
- witwin/maxwell/fdtd/excitation/tfsf_common.py +390 -0
- witwin/maxwell/fdtd/excitation/tfsf_specs.py +491 -0
- witwin/maxwell/fdtd/excitation/tfsf_state.py +723 -0
- witwin/maxwell/fdtd/io.py +31 -0
- witwin/maxwell/fdtd/material_pullback.py +273 -0
- witwin/maxwell/fdtd/meshing/__init__.py +3 -0
- witwin/maxwell/fdtd/meshing/autogrid.py +313 -0
- witwin/maxwell/fdtd/observers.py +1381 -0
- witwin/maxwell/fdtd/plotting.py +121 -0
- witwin/maxwell/fdtd/postprocess.py +205 -0
- witwin/maxwell/fdtd/runtime/__init__.py +123 -0
- witwin/maxwell/fdtd/runtime/initialization.py +211 -0
- witwin/maxwell/fdtd/runtime/materials.py +695 -0
- witwin/maxwell/fdtd/runtime/module_cache.py +31 -0
- witwin/maxwell/fdtd/runtime/spectral.py +588 -0
- witwin/maxwell/fdtd/runtime/stepping.py +1213 -0
- witwin/maxwell/fdtd/solver.py +510 -0
- witwin/maxwell/materials.py +3 -0
- witwin/maxwell/media.py +413 -0
- witwin/maxwell/monitors.py +733 -0
- witwin/maxwell/ports.py +101 -0
- witwin/maxwell/postprocess/RCS.py +139 -0
- witwin/maxwell/postprocess/__init__.py +38 -0
- witwin/maxwell/postprocess/diffraction.py +219 -0
- witwin/maxwell/postprocess/directivity.py +152 -0
- witwin/maxwell/postprocess/emission.py +67 -0
- witwin/maxwell/postprocess/modal.py +322 -0
- witwin/maxwell/postprocess/nfft.py +249 -0
- witwin/maxwell/postprocess/scattering_parameters.py +225 -0
- witwin/maxwell/postprocess/stratton_chu.py +914 -0
- witwin/maxwell/result.py +967 -0
- witwin/maxwell/scene.py +1261 -0
- witwin/maxwell/simulation.py +644 -0
- witwin/maxwell/sources.py +855 -0
- witwin/maxwell/visualization/__init__.py +28 -0
- witwin/maxwell/visualization/interactive.py +484 -0
- witwin/maxwell/visualization/plots.py +568 -0
- witwin_maxwell-0.2.0.dist-info/METADATA +177 -0
- witwin_maxwell-0.2.0.dist-info/RECORD +104 -0
- witwin_maxwell-0.2.0.dist-info/WHEEL +4 -0
- witwin_maxwell-0.2.0.dist-info/licenses/COPYING +674 -0
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
from witwin.core import Box, Cone, Cylinder, Ellipsoid, HollowBox, Mesh, Prism, Pyramid, Sphere, Structure, Torus
|
|
2
|
+
from witwin.core.geometry import ComplexPolySlab, PolySlab
|
|
3
|
+
from .media import DebyePole, DiagonalTensor3, DrudePole, LorentzPole, Material, Tensor3x3
|
|
4
|
+
from .monitors import (
|
|
5
|
+
ClosedSurfaceMonitor,
|
|
6
|
+
DiffractionMonitor,
|
|
7
|
+
DipoleEmissionMonitor,
|
|
8
|
+
FieldTimeMonitor,
|
|
9
|
+
FinitePlaneMonitor,
|
|
10
|
+
FluxMonitor,
|
|
11
|
+
FluxTimeMonitor,
|
|
12
|
+
MediumMonitor,
|
|
13
|
+
ModeMonitor,
|
|
14
|
+
PermittivityMonitor,
|
|
15
|
+
PlaneMonitor,
|
|
16
|
+
PointMonitor,
|
|
17
|
+
)
|
|
18
|
+
from .ports import ModePort
|
|
19
|
+
from . import postprocess
|
|
20
|
+
from .result import Result
|
|
21
|
+
from .scene import (
|
|
22
|
+
BoundaryKind,
|
|
23
|
+
BoundarySpec,
|
|
24
|
+
Domain,
|
|
25
|
+
GridSpec,
|
|
26
|
+
LayerRefinementSpec,
|
|
27
|
+
MaterialRegion,
|
|
28
|
+
MeshOverrideStructure,
|
|
29
|
+
Scene,
|
|
30
|
+
SceneModule,
|
|
31
|
+
SubpixelSpec,
|
|
32
|
+
)
|
|
33
|
+
from .sources import (
|
|
34
|
+
AstigmaticGaussianBeam,
|
|
35
|
+
CurrentDataset,
|
|
36
|
+
CustomCurrentSource,
|
|
37
|
+
CustomFieldSource,
|
|
38
|
+
CustomSourceTime,
|
|
39
|
+
CW,
|
|
40
|
+
FieldDataset,
|
|
41
|
+
GaussianBeam,
|
|
42
|
+
GaussianPulse,
|
|
43
|
+
ModeSource,
|
|
44
|
+
PlaneWave,
|
|
45
|
+
PointDipole,
|
|
46
|
+
RickerWavelet,
|
|
47
|
+
TFSF,
|
|
48
|
+
UniformCurrentSource,
|
|
49
|
+
)
|
|
50
|
+
from .simulation import (
|
|
51
|
+
AbsorberKind,
|
|
52
|
+
FDFDConfig,
|
|
53
|
+
FDTDConfig,
|
|
54
|
+
GMRES,
|
|
55
|
+
Simulation,
|
|
56
|
+
SimulationMethod,
|
|
57
|
+
SpectralSampler,
|
|
58
|
+
SpectralWindowKind,
|
|
59
|
+
TimeConfig,
|
|
60
|
+
run,
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
__all__ = [
|
|
64
|
+
"AstigmaticGaussianBeam",
|
|
65
|
+
"BoundarySpec",
|
|
66
|
+
"BoundaryKind",
|
|
67
|
+
"Box",
|
|
68
|
+
"ClosedSurfaceMonitor",
|
|
69
|
+
"ComplexPolySlab",
|
|
70
|
+
"DiffractionMonitor",
|
|
71
|
+
"Cone",
|
|
72
|
+
"CurrentDataset",
|
|
73
|
+
"CustomCurrentSource",
|
|
74
|
+
"CustomFieldSource",
|
|
75
|
+
"CW",
|
|
76
|
+
"CustomSourceTime",
|
|
77
|
+
"Cylinder",
|
|
78
|
+
"DebyePole",
|
|
79
|
+
"DipoleEmissionMonitor",
|
|
80
|
+
"Domain",
|
|
81
|
+
"DrudePole",
|
|
82
|
+
"Ellipsoid",
|
|
83
|
+
"FieldDataset",
|
|
84
|
+
"FieldTimeMonitor",
|
|
85
|
+
"FluxMonitor",
|
|
86
|
+
"FluxTimeMonitor",
|
|
87
|
+
"FDFDConfig",
|
|
88
|
+
"FDTDConfig",
|
|
89
|
+
"FinitePlaneMonitor",
|
|
90
|
+
"GaussianBeam",
|
|
91
|
+
"GaussianPulse",
|
|
92
|
+
"GMRES",
|
|
93
|
+
"GridSpec",
|
|
94
|
+
"HollowBox",
|
|
95
|
+
"LayerRefinementSpec",
|
|
96
|
+
"LorentzPole",
|
|
97
|
+
"Material",
|
|
98
|
+
"MaterialRegion",
|
|
99
|
+
"MediumMonitor",
|
|
100
|
+
"Mesh",
|
|
101
|
+
"MeshOverrideStructure",
|
|
102
|
+
"ModeMonitor",
|
|
103
|
+
"ModePort",
|
|
104
|
+
"ModeSource",
|
|
105
|
+
"PermittivityMonitor",
|
|
106
|
+
"PlaneMonitor",
|
|
107
|
+
"PlaneWave",
|
|
108
|
+
"PointDipole",
|
|
109
|
+
"PointMonitor",
|
|
110
|
+
"PolySlab",
|
|
111
|
+
"postprocess",
|
|
112
|
+
"Prism",
|
|
113
|
+
"Pyramid",
|
|
114
|
+
"Result",
|
|
115
|
+
"RickerWavelet",
|
|
116
|
+
"Scene",
|
|
117
|
+
"SceneModule",
|
|
118
|
+
"Simulation",
|
|
119
|
+
"SimulationMethod",
|
|
120
|
+
"SpectralSampler",
|
|
121
|
+
"SpectralWindowKind",
|
|
122
|
+
"Sphere",
|
|
123
|
+
"Structure",
|
|
124
|
+
"SubpixelSpec",
|
|
125
|
+
"Tensor3x3",
|
|
126
|
+
"TimeConfig",
|
|
127
|
+
"TFSF",
|
|
128
|
+
"Torus",
|
|
129
|
+
"UniformCurrentSource",
|
|
130
|
+
"DiagonalTensor3",
|
|
131
|
+
"AbsorberKind",
|
|
132
|
+
"run",
|
|
133
|
+
]
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
def __getattr__(name):
|
|
137
|
+
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Backend adapter subpackage."""
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
"""GDS layout import/export for extruded polygon geometry.
|
|
2
|
+
|
|
3
|
+
Usage::
|
|
4
|
+
|
|
5
|
+
slabs = from_gds("layout.gds", layer=1, bounds=(0.0, 220e-9))
|
|
6
|
+
to_gds_file(slabs, "layout_out.gds")
|
|
7
|
+
|
|
8
|
+
Requires ``gdstk`` as an optional dependency.
|
|
9
|
+
|
|
10
|
+
**Unit convention**: maxwell uses metres; GDS libraries carry an explicit
|
|
11
|
+
user unit in metres (``library.unit``, typically 1e-6 for micrometres).
|
|
12
|
+
``from_gds`` multiplies layout coordinates by ``length_scale``, defaulting
|
|
13
|
+
to the file's ``library.unit`` so imported vertices land in metres.
|
|
14
|
+
``to_gds_file`` multiplies metre coordinates by ``length_scale`` (default
|
|
15
|
+
1e6) and writes the library with ``unit = 1 / length_scale`` so user units
|
|
16
|
+
always mean what ``length_scale`` says.
|
|
17
|
+
|
|
18
|
+
Only the 2D cross-section travels through GDS: extrusion ``bounds``,
|
|
19
|
+
``axis``, ``sidewall_angle``, and ``reference_plane`` are supplied by the
|
|
20
|
+
caller on import and dropped on export.
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
from __future__ import annotations
|
|
24
|
+
|
|
25
|
+
from pathlib import Path
|
|
26
|
+
from typing import TYPE_CHECKING
|
|
27
|
+
|
|
28
|
+
import numpy as np
|
|
29
|
+
import torch
|
|
30
|
+
|
|
31
|
+
from witwin.core.geometry import PolySlab
|
|
32
|
+
|
|
33
|
+
if TYPE_CHECKING:
|
|
34
|
+
import gdstk
|
|
35
|
+
|
|
36
|
+
# Default length conversion factor for export: metres to micrometres.
|
|
37
|
+
_M_TO_UM = 1e6
|
|
38
|
+
|
|
39
|
+
# In-plane world-coordinate indices per extrusion axis (matches _axial_split:
|
|
40
|
+
# axis z -> (x, y), y -> (x, z), x -> (y, z)).
|
|
41
|
+
_PLANE_INDICES = {"z": (0, 1), "y": (0, 2), "x": (1, 2)}
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def _ensure_gdstk():
|
|
45
|
+
"""Import and return gdstk, raising a clear error if missing."""
|
|
46
|
+
try:
|
|
47
|
+
import gdstk
|
|
48
|
+
return gdstk
|
|
49
|
+
except ImportError:
|
|
50
|
+
raise ImportError(
|
|
51
|
+
"gdstk is required for GDS import/export. "
|
|
52
|
+
"Install it with: pip install gdstk"
|
|
53
|
+
) from None
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def _resolve_cell(library, cell: str | None):
|
|
57
|
+
"""Return the requested cell, or the unique top-level cell when *cell* is None."""
|
|
58
|
+
names = [candidate.name for candidate in library.cells]
|
|
59
|
+
if cell is None:
|
|
60
|
+
top = library.top_level()
|
|
61
|
+
if len(top) != 1:
|
|
62
|
+
raise ValueError(
|
|
63
|
+
f"GDS library has {len(top)} top-level cells; pass cell= to pick one. "
|
|
64
|
+
f"Available cells: {names}"
|
|
65
|
+
)
|
|
66
|
+
return top[0]
|
|
67
|
+
for candidate in library.cells:
|
|
68
|
+
if candidate.name == cell:
|
|
69
|
+
return candidate
|
|
70
|
+
raise ValueError(f"Cell '{cell}' not found in GDS library. Available cells: {names}")
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def from_gds(
|
|
74
|
+
gds_path,
|
|
75
|
+
*,
|
|
76
|
+
layer: int,
|
|
77
|
+
bounds,
|
|
78
|
+
datatype: int = 0,
|
|
79
|
+
cell: str | None = None,
|
|
80
|
+
axis="z",
|
|
81
|
+
sidewall_angle=0.0,
|
|
82
|
+
reference_plane: str = "middle",
|
|
83
|
+
length_scale: float | None = None,
|
|
84
|
+
) -> list[PolySlab]:
|
|
85
|
+
"""Load polygons from a GDS file as extruded ``PolySlab`` geometries.
|
|
86
|
+
|
|
87
|
+
Cell references are flattened recursively (with repetitions applied) and
|
|
88
|
+
paths are converted to their polygonal representation, so the result covers
|
|
89
|
+
the full selected layer of the resolved cell. GDS polygons are always
|
|
90
|
+
simple boundary loops (the format has no hole concept), so each polygon
|
|
91
|
+
becomes one plain ``PolySlab``; holes drawn as separate loops import as
|
|
92
|
+
separate slabs.
|
|
93
|
+
|
|
94
|
+
Args:
|
|
95
|
+
gds_path: Path to the ``.gds`` file.
|
|
96
|
+
layer: GDS layer number to import.
|
|
97
|
+
bounds: ``(min, max)`` extrusion extent along *axis*, in metres.
|
|
98
|
+
datatype: GDS datatype number to import.
|
|
99
|
+
cell: Cell name to read. ``None`` selects the unique top-level cell.
|
|
100
|
+
axis: Extrusion axis (``'x'``/``'y'``/``'z'`` or 0/1/2).
|
|
101
|
+
sidewall_angle: ``PolySlab`` sidewall angle in radians.
|
|
102
|
+
reference_plane: ``PolySlab`` reference plane
|
|
103
|
+
(``'bottom'``/``'middle'``/``'top'``).
|
|
104
|
+
length_scale: Metres per GDS user unit. ``None`` uses the file's
|
|
105
|
+
``library.unit`` (typically 1e-6).
|
|
106
|
+
|
|
107
|
+
Returns:
|
|
108
|
+
One ``PolySlab`` per polygon on ``(layer, datatype)``.
|
|
109
|
+
"""
|
|
110
|
+
gdstk = _ensure_gdstk()
|
|
111
|
+
path = Path(gds_path)
|
|
112
|
+
if not path.is_file():
|
|
113
|
+
raise ValueError(f"GDS file not found: {path}")
|
|
114
|
+
library = gdstk.read_gds(str(path))
|
|
115
|
+
selected = _resolve_cell(library, cell)
|
|
116
|
+
scale = float(library.unit) if length_scale is None else float(length_scale)
|
|
117
|
+
polygons = selected.get_polygons(
|
|
118
|
+
include_paths=True, depth=None, layer=int(layer), datatype=int(datatype)
|
|
119
|
+
)
|
|
120
|
+
if not polygons:
|
|
121
|
+
raise ValueError(
|
|
122
|
+
f"No polygons on layer ({int(layer)}, {int(datatype)}) in cell '{selected.name}'."
|
|
123
|
+
)
|
|
124
|
+
return [
|
|
125
|
+
PolySlab(
|
|
126
|
+
torch.from_numpy(np.asarray(polygon.points, dtype=np.float64) * scale),
|
|
127
|
+
bounds,
|
|
128
|
+
axis=axis,
|
|
129
|
+
sidewall_angle=sidewall_angle,
|
|
130
|
+
reference_plane=reference_plane,
|
|
131
|
+
)
|
|
132
|
+
for polygon in polygons
|
|
133
|
+
]
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
def to_gds_file(
|
|
137
|
+
geometries,
|
|
138
|
+
gds_path,
|
|
139
|
+
*,
|
|
140
|
+
cell_name: str = "TOP",
|
|
141
|
+
layer: int = 0,
|
|
142
|
+
datatype: int = 0,
|
|
143
|
+
length_scale: float = _M_TO_UM,
|
|
144
|
+
):
|
|
145
|
+
"""Write ``PolySlab`` cross-sections to a GDS file.
|
|
146
|
+
|
|
147
|
+
*geometries* is a single ``PolySlab`` / ``ComplexPolySlab`` or an iterable
|
|
148
|
+
of them, written to ``(layer, datatype)`` with the in-plane component of
|
|
149
|
+
``position`` applied. GDS boundaries on one layer union, so multi-loop
|
|
150
|
+
geometry is folded with even-odd XOR into simple keyhole-cut polygons that
|
|
151
|
+
preserve ``ComplexPolySlab`` hole semantics. Extrusion ``bounds``,
|
|
152
|
+
``sidewall_angle``, and ``reference_plane`` have no GDS representation and
|
|
153
|
+
are dropped: the reference-plane cross-section is written. Geometries with
|
|
154
|
+
a non-identity ``rotation`` are rejected.
|
|
155
|
+
|
|
156
|
+
Args:
|
|
157
|
+
geometries: ``PolySlab`` geometry or iterable of them.
|
|
158
|
+
gds_path: Output ``.gds`` file path.
|
|
159
|
+
cell_name: Name of the written cell.
|
|
160
|
+
layer: GDS layer number for all polygons.
|
|
161
|
+
datatype: GDS datatype number for all polygons.
|
|
162
|
+
length_scale: GDS user units per metre (default 1e6, i.e. micrometre
|
|
163
|
+
user units; the library is written with ``unit = 1 / length_scale``).
|
|
164
|
+
"""
|
|
165
|
+
gdstk = _ensure_gdstk()
|
|
166
|
+
if isinstance(geometries, PolySlab):
|
|
167
|
+
geometries = [geometries]
|
|
168
|
+
# Database grid: the GDS-conventional 1e-3 user units, clamped to at most
|
|
169
|
+
# 1 nm physical so coarse user units cannot silently snap coordinates away.
|
|
170
|
+
unit = 1.0 / float(length_scale)
|
|
171
|
+
library = gdstk.Library(unit=unit, precision=min(unit * 1e-3, 1e-9))
|
|
172
|
+
cell = library.new_cell(cell_name)
|
|
173
|
+
identity = torch.tensor([1.0, 0.0, 0.0, 0.0])
|
|
174
|
+
for geometry in geometries:
|
|
175
|
+
if not isinstance(geometry, PolySlab):
|
|
176
|
+
raise ValueError(
|
|
177
|
+
"Only extruded-polygon geometry (PolySlab / ComplexPolySlab) maps to "
|
|
178
|
+
f"GDS polygons; got {type(geometry).__name__}."
|
|
179
|
+
)
|
|
180
|
+
if not torch.allclose(geometry.rotation.detach().cpu(), identity):
|
|
181
|
+
raise ValueError("Cannot export rotated PolySlab geometry to GDS.")
|
|
182
|
+
offset = geometry.position.detach().cpu().to(torch.float64).numpy()
|
|
183
|
+
offset = offset[list(_PLANE_INDICES[geometry.axis])]
|
|
184
|
+
vertices = geometry.vertices.detach().cpu().to(torch.float64).numpy() + offset
|
|
185
|
+
loops = []
|
|
186
|
+
start = 0
|
|
187
|
+
for size in geometry.loop_sizes:
|
|
188
|
+
loops.append(vertices[start:start + size] * float(length_scale))
|
|
189
|
+
start += size
|
|
190
|
+
if len(loops) == 1:
|
|
191
|
+
cell.add(gdstk.Polygon(loops[0], layer=int(layer), datatype=int(datatype)))
|
|
192
|
+
else:
|
|
193
|
+
# GDS boundaries on one layer union, so reproduce the even-odd rule
|
|
194
|
+
# by folding XOR over the loops: holes become keyhole-cut simple
|
|
195
|
+
# polygons instead of solid boundaries.
|
|
196
|
+
merged = [loops[0]]
|
|
197
|
+
for loop in loops[1:]:
|
|
198
|
+
merged = gdstk.boolean(
|
|
199
|
+
merged, [loop], "xor", layer=int(layer), datatype=int(datatype)
|
|
200
|
+
)
|
|
201
|
+
for polygon in merged:
|
|
202
|
+
cell.add(polygon)
|
|
203
|
+
library.write_gds(str(gds_path))
|