witwin-maxwell 0.3.0__py3-none-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.
Files changed (107) hide show
  1. witwin/maxwell/__init__.py +164 -0
  2. witwin/maxwell/adapters/__init__.py +1 -0
  3. witwin/maxwell/adapters/gds.py +203 -0
  4. witwin/maxwell/adapters/tidy3d.py +1669 -0
  5. witwin/maxwell/adjoint_inputs.py +70 -0
  6. witwin/maxwell/compiler/__init__.py +11 -0
  7. witwin/maxwell/compiler/materials.py +1355 -0
  8. witwin/maxwell/compiler/monitors.py +164 -0
  9. witwin/maxwell/compiler/sources.py +222 -0
  10. witwin/maxwell/fdfd/__init__.py +3 -0
  11. witwin/maxwell/fdfd/adjoint/__init__.py +3 -0
  12. witwin/maxwell/fdfd/adjoint/bridge.py +180 -0
  13. witwin/maxwell/fdfd/ams.py +300 -0
  14. witwin/maxwell/fdfd/krylov.py +280 -0
  15. witwin/maxwell/fdfd/plotting.py +110 -0
  16. witwin/maxwell/fdfd/postprocess.py +33 -0
  17. witwin/maxwell/fdfd/solver.py +1057 -0
  18. witwin/maxwell/fdtd/__init__.py +4 -0
  19. witwin/maxwell/fdtd/adjoint/__init__.py +21 -0
  20. witwin/maxwell/fdtd/adjoint/bridge.py +746 -0
  21. witwin/maxwell/fdtd/adjoint/capabilities.py +29 -0
  22. witwin/maxwell/fdtd/adjoint/core.py +3614 -0
  23. witwin/maxwell/fdtd/adjoint/dispatch.py +684 -0
  24. witwin/maxwell/fdtd/adjoint/native.py +2561 -0
  25. witwin/maxwell/fdtd/adjoint/profiler.py +195 -0
  26. witwin/maxwell/fdtd/adjoint/reverse_common.py +207 -0
  27. witwin/maxwell/fdtd/adjoint/seeds.py +524 -0
  28. witwin/maxwell/fdtd/boundary/__init__.py +37 -0
  29. witwin/maxwell/fdtd/boundary/bloch.py +90 -0
  30. witwin/maxwell/fdtd/boundary/common.py +41 -0
  31. witwin/maxwell/fdtd/boundary/cpml.py +527 -0
  32. witwin/maxwell/fdtd/boundary/runtime.py +209 -0
  33. witwin/maxwell/fdtd/checkpoint.py +227 -0
  34. witwin/maxwell/fdtd/coords.py +32 -0
  35. witwin/maxwell/fdtd/cuda/__init__.py +11 -0
  36. witwin/maxwell/fdtd/cuda/backend.py +3532 -0
  37. witwin/maxwell/fdtd/cuda/build.py +292 -0
  38. witwin/maxwell/fdtd/cuda/extension.cpp +2053 -0
  39. witwin/maxwell/fdtd/cuda/kernels/adjoint.cu +5290 -0
  40. witwin/maxwell/fdtd/cuda/kernels/aniso.cu +1704 -0
  41. witwin/maxwell/fdtd/cuda/kernels/boundary.cu +424 -0
  42. witwin/maxwell/fdtd/cuda/kernels/common.cu +74 -0
  43. witwin/maxwell/fdtd/cuda/kernels/common.cuh +82 -0
  44. witwin/maxwell/fdtd/cuda/kernels/dispersive.cu +782 -0
  45. witwin/maxwell/fdtd/cuda/kernels/electric.cu +3613 -0
  46. witwin/maxwell/fdtd/cuda/kernels/magnetic.cu +781 -0
  47. witwin/maxwell/fdtd/cuda/kernels/observers.cu +347 -0
  48. witwin/maxwell/fdtd/cuda/kernels/projection.cu +211 -0
  49. witwin/maxwell/fdtd/cuda/kernels/sources.cu +1482 -0
  50. witwin/maxwell/fdtd/cuda/kernels/spectral.cu +297 -0
  51. witwin/maxwell/fdtd/cuda/launch.h +23 -0
  52. witwin/maxwell/fdtd/cuda/prebuilt/witwin_maxwell_fdtd_cuda.pyd +0 -0
  53. witwin/maxwell/fdtd/cuda/runtime/__init__.py +6 -0
  54. witwin/maxwell/fdtd/cuda/runtime/dispatcher.py +6 -0
  55. witwin/maxwell/fdtd/cuda/runtime/graph.py +33 -0
  56. witwin/maxwell/fdtd/cuda/tensors.h +35 -0
  57. witwin/maxwell/fdtd/dispersion.py +48 -0
  58. witwin/maxwell/fdtd/evaluation.py +290 -0
  59. witwin/maxwell/fdtd/excitation/__init__.py +27 -0
  60. witwin/maxwell/fdtd/excitation/injection.py +1102 -0
  61. witwin/maxwell/fdtd/excitation/modes.py +1675 -0
  62. witwin/maxwell/fdtd/excitation/spatial.py +469 -0
  63. witwin/maxwell/fdtd/excitation/temporal.py +510 -0
  64. witwin/maxwell/fdtd/excitation/tfsf_apply.py +264 -0
  65. witwin/maxwell/fdtd/excitation/tfsf_common.py +421 -0
  66. witwin/maxwell/fdtd/excitation/tfsf_specs.py +490 -0
  67. witwin/maxwell/fdtd/excitation/tfsf_state.py +778 -0
  68. witwin/maxwell/fdtd/io.py +31 -0
  69. witwin/maxwell/fdtd/material_pullback.py +371 -0
  70. witwin/maxwell/fdtd/meshing/__init__.py +3 -0
  71. witwin/maxwell/fdtd/meshing/autogrid.py +335 -0
  72. witwin/maxwell/fdtd/observers.py +1453 -0
  73. witwin/maxwell/fdtd/plotting.py +121 -0
  74. witwin/maxwell/fdtd/postprocess.py +205 -0
  75. witwin/maxwell/fdtd/runtime/__init__.py +119 -0
  76. witwin/maxwell/fdtd/runtime/initialization.py +236 -0
  77. witwin/maxwell/fdtd/runtime/materials.py +1563 -0
  78. witwin/maxwell/fdtd/runtime/module_cache.py +31 -0
  79. witwin/maxwell/fdtd/runtime/spectral.py +581 -0
  80. witwin/maxwell/fdtd/runtime/stepping.py +1987 -0
  81. witwin/maxwell/fdtd/solver.py +502 -0
  82. witwin/maxwell/geometry/__init__.py +5 -0
  83. witwin/maxwell/geometry/polyslab.py +299 -0
  84. witwin/maxwell/materials.py +3 -0
  85. witwin/maxwell/media.py +1467 -0
  86. witwin/maxwell/monitors.py +743 -0
  87. witwin/maxwell/ports.py +113 -0
  88. witwin/maxwell/postprocess/RCS.py +139 -0
  89. witwin/maxwell/postprocess/__init__.py +42 -0
  90. witwin/maxwell/postprocess/diffraction.py +219 -0
  91. witwin/maxwell/postprocess/directivity.py +152 -0
  92. witwin/maxwell/postprocess/emission.py +67 -0
  93. witwin/maxwell/postprocess/modal.py +324 -0
  94. witwin/maxwell/postprocess/nfft.py +266 -0
  95. witwin/maxwell/postprocess/scattering_parameters.py +358 -0
  96. witwin/maxwell/postprocess/stratton_chu.py +1193 -0
  97. witwin/maxwell/result.py +967 -0
  98. witwin/maxwell/scene.py +1338 -0
  99. witwin/maxwell/simulation.py +659 -0
  100. witwin/maxwell/sources.py +917 -0
  101. witwin/maxwell/visualization/__init__.py +28 -0
  102. witwin/maxwell/visualization/interactive.py +484 -0
  103. witwin/maxwell/visualization/plots.py +568 -0
  104. witwin_maxwell-0.3.0.dist-info/METADATA +184 -0
  105. witwin_maxwell-0.3.0.dist-info/RECORD +107 -0
  106. witwin_maxwell-0.3.0.dist-info/WHEEL +4 -0
  107. witwin_maxwell-0.3.0.dist-info/licenses/LICENSE +13 -0
@@ -0,0 +1,164 @@
1
+ from witwin.core import Box, Cone, Cylinder, Ellipsoid, HollowBox, Mesh, Prism, Pyramid, Sphere, Structure, Torus
2
+ from .geometry import ComplexPolySlab, PolySlab
3
+ from .media import (
4
+ CustomDebyePole,
5
+ CustomDrudePole,
6
+ CustomLorentzPole,
7
+ DebyePole,
8
+ DiagonalTensor3,
9
+ DrudePole,
10
+ Graphene,
11
+ LorentzPole,
12
+ LossyMetalMedium,
13
+ Material,
14
+ Medium2D,
15
+ ModulationSpec,
16
+ NonlinearSusceptibility,
17
+ PerturbationMedium,
18
+ Tensor3x3,
19
+ TwoPhotonAbsorption,
20
+ )
21
+ from .monitors import (
22
+ ClosedSurfaceMonitor,
23
+ DiffractionMonitor,
24
+ DipoleEmissionMonitor,
25
+ FieldTimeMonitor,
26
+ FinitePlaneMonitor,
27
+ FluxMonitor,
28
+ FluxTimeMonitor,
29
+ MediumMonitor,
30
+ ModeMonitor,
31
+ PermittivityMonitor,
32
+ PlaneMonitor,
33
+ PointMonitor,
34
+ )
35
+ from .ports import ModePort
36
+ from . import postprocess
37
+ from .result import Result
38
+ from .scene import (
39
+ BoundaryKind,
40
+ BoundarySpec,
41
+ Domain,
42
+ GridSpec,
43
+ LayerRefinementSpec,
44
+ MaterialRegion,
45
+ MeshOverrideStructure,
46
+ Scene,
47
+ SceneModule,
48
+ SubpixelSpec,
49
+ )
50
+ from .sources import (
51
+ AstigmaticGaussianBeam,
52
+ CurrentDataset,
53
+ CustomCurrentSource,
54
+ CustomFieldSource,
55
+ CustomSourceTime,
56
+ CW,
57
+ FieldDataset,
58
+ GaussianBeam,
59
+ GaussianPulse,
60
+ ModeSource,
61
+ PlaneWave,
62
+ PointDipole,
63
+ RickerWavelet,
64
+ TFSF,
65
+ UniformCurrentSource,
66
+ )
67
+ from .simulation import (
68
+ AbsorberKind,
69
+ FDFDConfig,
70
+ FDTDConfig,
71
+ GMRES,
72
+ Simulation,
73
+ SimulationMethod,
74
+ SpectralSampler,
75
+ SpectralWindowKind,
76
+ TimeConfig,
77
+ run,
78
+ )
79
+
80
+ __all__ = [
81
+ "AstigmaticGaussianBeam",
82
+ "BoundarySpec",
83
+ "BoundaryKind",
84
+ "Box",
85
+ "ClosedSurfaceMonitor",
86
+ "ComplexPolySlab",
87
+ "DiffractionMonitor",
88
+ "Cone",
89
+ "CurrentDataset",
90
+ "CustomCurrentSource",
91
+ "CustomFieldSource",
92
+ "CW",
93
+ "CustomDebyePole",
94
+ "CustomDrudePole",
95
+ "CustomLorentzPole",
96
+ "CustomSourceTime",
97
+ "Cylinder",
98
+ "DebyePole",
99
+ "DipoleEmissionMonitor",
100
+ "Domain",
101
+ "DrudePole",
102
+ "Ellipsoid",
103
+ "FieldDataset",
104
+ "FieldTimeMonitor",
105
+ "FluxMonitor",
106
+ "FluxTimeMonitor",
107
+ "FDFDConfig",
108
+ "FDTDConfig",
109
+ "FinitePlaneMonitor",
110
+ "GaussianBeam",
111
+ "GaussianPulse",
112
+ "GMRES",
113
+ "Graphene",
114
+ "GridSpec",
115
+ "HollowBox",
116
+ "LayerRefinementSpec",
117
+ "LorentzPole",
118
+ "LossyMetalMedium",
119
+ "Material",
120
+ "MaterialRegion",
121
+ "Medium2D",
122
+ "MediumMonitor",
123
+ "Mesh",
124
+ "MeshOverrideStructure",
125
+ "ModeMonitor",
126
+ "ModePort",
127
+ "ModeSource",
128
+ "ModulationSpec",
129
+ "NonlinearSusceptibility",
130
+ "PermittivityMonitor",
131
+ "PerturbationMedium",
132
+ "PlaneMonitor",
133
+ "PlaneWave",
134
+ "PointDipole",
135
+ "PointMonitor",
136
+ "PolySlab",
137
+ "postprocess",
138
+ "Prism",
139
+ "Pyramid",
140
+ "Result",
141
+ "RickerWavelet",
142
+ "Scene",
143
+ "SceneModule",
144
+ "Simulation",
145
+ "SimulationMethod",
146
+ "SpectralSampler",
147
+ "SpectralWindowKind",
148
+ "Sphere",
149
+ "Structure",
150
+ "SubpixelSpec",
151
+ "Tensor3x3",
152
+ "TimeConfig",
153
+ "TFSF",
154
+ "Torus",
155
+ "TwoPhotonAbsorption",
156
+ "UniformCurrentSource",
157
+ "DiagonalTensor3",
158
+ "AbsorberKind",
159
+ "run",
160
+ ]
161
+
162
+
163
+ def __getattr__(name):
164
+ 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 ..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))