LightWave2D 0.6.0__cp313-cp313-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.
- LightWave2D/__init__.py +9 -0
- LightWave2D/_version.py +34 -0
- LightWave2D/binary/__init__.py +0 -0
- LightWave2D/binary/interface_config.cp311-win_amd64.pyd +0 -0
- LightWave2D/binary/interface_config.cp312-win_amd64.pyd +0 -0
- LightWave2D/binary/interface_config.cp313-win_amd64.pyd +0 -0
- LightWave2D/binary/interface_field_set.cp311-win_amd64.pyd +0 -0
- LightWave2D/binary/interface_field_set.cp312-win_amd64.pyd +0 -0
- LightWave2D/binary/interface_field_set.cp313-win_amd64.pyd +0 -0
- LightWave2D/binary/interface_mesh_set.cp311-win_amd64.pyd +0 -0
- LightWave2D/binary/interface_mesh_set.cp312-win_amd64.pyd +0 -0
- LightWave2D/binary/interface_mesh_set.cp313-win_amd64.pyd +0 -0
- LightWave2D/binary/interface_simulator.cp311-win_amd64.pyd +0 -0
- LightWave2D/binary/interface_simulator.cp312-win_amd64.pyd +0 -0
- LightWave2D/binary/interface_simulator.cp313-win_amd64.pyd +0 -0
- LightWave2D/binary/interface_source.cp311-win_amd64.pyd +0 -0
- LightWave2D/binary/interface_source.cp312-win_amd64.pyd +0 -0
- LightWave2D/binary/interface_source.cp313-win_amd64.pyd +0 -0
- LightWave2D/binary/libcpp_config.a +0 -0
- LightWave2D/binary/libcpp_field_set.a +0 -0
- LightWave2D/binary/libcpp_mesh_set.a +0 -0
- LightWave2D/binary/libcpp_simulator.a +0 -0
- LightWave2D/binary/libcpp_source.a +0 -0
- LightWave2D/components.py +420 -0
- LightWave2D/detector.py +184 -0
- LightWave2D/directories.py +34 -0
- LightWave2D/experiment.py +497 -0
- LightWave2D/grid.py +184 -0
- LightWave2D/helper.py +59 -0
- LightWave2D/physics.py +17 -0
- LightWave2D/pml.py +103 -0
- LightWave2D/source.py +352 -0
- LightWave2D/utils.py +66 -0
- lightwave2d-0.6.0.dist-info/METADATA +351 -0
- lightwave2d-0.6.0.dist-info/RECORD +37 -0
- lightwave2d-0.6.0.dist-info/WHEEL +5 -0
- lightwave2d-0.6.0.dist-info/licenses/LICENSE +21 -0
LightWave2D/__init__.py
ADDED
LightWave2D/_version.py
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# file generated by setuptools-scm
|
|
2
|
+
# don't change, don't track in version control
|
|
3
|
+
|
|
4
|
+
__all__ = [
|
|
5
|
+
"__version__",
|
|
6
|
+
"__version_tuple__",
|
|
7
|
+
"version",
|
|
8
|
+
"version_tuple",
|
|
9
|
+
"__commit_id__",
|
|
10
|
+
"commit_id",
|
|
11
|
+
]
|
|
12
|
+
|
|
13
|
+
TYPE_CHECKING = False
|
|
14
|
+
if TYPE_CHECKING:
|
|
15
|
+
from typing import Tuple
|
|
16
|
+
from typing import Union
|
|
17
|
+
|
|
18
|
+
VERSION_TUPLE = Tuple[Union[int, str], ...]
|
|
19
|
+
COMMIT_ID = Union[str, None]
|
|
20
|
+
else:
|
|
21
|
+
VERSION_TUPLE = object
|
|
22
|
+
COMMIT_ID = object
|
|
23
|
+
|
|
24
|
+
version: str
|
|
25
|
+
__version__: str
|
|
26
|
+
__version_tuple__: VERSION_TUPLE
|
|
27
|
+
version_tuple: VERSION_TUPLE
|
|
28
|
+
commit_id: COMMIT_ID
|
|
29
|
+
__commit_id__: COMMIT_ID
|
|
30
|
+
|
|
31
|
+
__version__ = version = '0.6.0'
|
|
32
|
+
__version_tuple__ = version_tuple = (0, 6, 0)
|
|
33
|
+
|
|
34
|
+
__commit_id__ = commit_id = 'gc531feae7'
|
|
File without changes
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,420 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
|
|
4
|
+
from typing import Tuple
|
|
5
|
+
from pydantic.dataclasses import dataclass
|
|
6
|
+
import numpy as np
|
|
7
|
+
import matplotlib.pyplot as plt
|
|
8
|
+
import shapely.geometry as geo
|
|
9
|
+
from matplotlib.patches import PathPatch
|
|
10
|
+
from matplotlib import patches
|
|
11
|
+
from matplotlib.path import Path
|
|
12
|
+
from matplotlib.collections import PatchCollection
|
|
13
|
+
from shapely.affinity import scale, rotate
|
|
14
|
+
from TypedUnit import AnyUnit, Length, ureg
|
|
15
|
+
|
|
16
|
+
from LightWave2D.grid import Grid
|
|
17
|
+
from LightWave2D.utils import config_dict
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
@dataclass(config=config_dict, kw_only=True)
|
|
21
|
+
class BaseComponent:
|
|
22
|
+
"""
|
|
23
|
+
Represents a generic component in a simulation grid.
|
|
24
|
+
|
|
25
|
+
Parameters
|
|
26
|
+
----------
|
|
27
|
+
grid : Grid
|
|
28
|
+
The simulation grid.
|
|
29
|
+
facecolor : str
|
|
30
|
+
The color of the component's face (default is 'lightblue').
|
|
31
|
+
edgecolor : str
|
|
32
|
+
The color of the component's edge (default is 'blue').
|
|
33
|
+
alpha : float
|
|
34
|
+
Transparency level of the component (default is 0.3).
|
|
35
|
+
rotation : float
|
|
36
|
+
Rotation angle of the component (default is 0 degrees).
|
|
37
|
+
sigma : float
|
|
38
|
+
Conductivity of the component (default is 0).
|
|
39
|
+
"""
|
|
40
|
+
grid: Grid
|
|
41
|
+
facecolor: str = 'lightblue'
|
|
42
|
+
edgecolor: str = 'blue'
|
|
43
|
+
alpha: float = 0.3
|
|
44
|
+
rotation: float = 0
|
|
45
|
+
sigma: AnyUnit = 0 * (ureg.siemens / ureg.meter)
|
|
46
|
+
|
|
47
|
+
def __post_init__(self):
|
|
48
|
+
"""
|
|
49
|
+
Initialize the component by building its object and computing the coordinates.
|
|
50
|
+
"""
|
|
51
|
+
x0, y0 = self.position
|
|
52
|
+
self.coordinate = self.grid.get_coordinate(x=x0, y=y0)
|
|
53
|
+
self.build_object()
|
|
54
|
+
|
|
55
|
+
def build_object(self) -> None:
|
|
56
|
+
"""
|
|
57
|
+
Build the permittivity mesh for the component.
|
|
58
|
+
"""
|
|
59
|
+
self.epsilon_r_mesh = np.ones(self.grid.shape) # Background permittivity
|
|
60
|
+
self.compute_polygon()
|
|
61
|
+
self.polygon = rotate(self.polygon, angle=self.rotation)
|
|
62
|
+
self.polygon = self.polygon.intersection(self.grid.polygon)
|
|
63
|
+
self.path = Path(self.polygon.exterior.coords)
|
|
64
|
+
coordinates = np.c_[self.grid.x_mesh.T.flatten().to('meter').magnitude, self.grid.y_mesh.T.flatten().to('meter').magnitude]
|
|
65
|
+
self.idx = self.path.contains_points(coordinates).astype(bool).reshape(self.epsilon_r_mesh.shape)
|
|
66
|
+
|
|
67
|
+
def add_to_ax(self, ax: plt.Axes) -> PatchCollection:
|
|
68
|
+
"""
|
|
69
|
+
Add the component to the provided axis.
|
|
70
|
+
|
|
71
|
+
Parameters
|
|
72
|
+
----------
|
|
73
|
+
ax : plt.Axes
|
|
74
|
+
The axis to which the component will be added.
|
|
75
|
+
|
|
76
|
+
Returns
|
|
77
|
+
-------
|
|
78
|
+
PatchCollection
|
|
79
|
+
The collection of patches added to the axis.
|
|
80
|
+
"""
|
|
81
|
+
path = Path.make_compound_path(
|
|
82
|
+
Path(np.asarray(self.polygon.exterior.coords)[:, :2]),
|
|
83
|
+
*[Path(np.asarray(ring.coords)[:, :2]) for ring in self.polygon.interiors]
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
patch = PathPatch(path, facecolor=self.facecolor, edgecolor=self.edgecolor, alpha=0.4)
|
|
87
|
+
collection = PatchCollection([patch], facecolor=self.facecolor, edgecolor=self.edgecolor, alpha=self.alpha)
|
|
88
|
+
|
|
89
|
+
ax.add_collection(collection, autolim=True)
|
|
90
|
+
ax.autoscale_view()
|
|
91
|
+
return collection
|
|
92
|
+
|
|
93
|
+
def plot(self) -> None:
|
|
94
|
+
"""
|
|
95
|
+
Plot the component.
|
|
96
|
+
"""
|
|
97
|
+
figure, ax = plt.subplots(1, 1, figsize=(6, 6))
|
|
98
|
+
ax.set_title('FDTD Simulation')
|
|
99
|
+
ax.set_xlabel(r'x position [$\mu$m]')
|
|
100
|
+
ax.set_ylabel(r'y position [$\mu$m]')
|
|
101
|
+
ax.set_aspect('equal')
|
|
102
|
+
self.add_to_ax(ax)
|
|
103
|
+
ax.autoscale_view()
|
|
104
|
+
plt.show()
|
|
105
|
+
|
|
106
|
+
def add_to_epsilon_r_mesh(self, epsilon_r_mesh: np.ndarray) -> None:
|
|
107
|
+
"""
|
|
108
|
+
Add the component's permittivity to the provided mesh.
|
|
109
|
+
|
|
110
|
+
Parameters
|
|
111
|
+
----------
|
|
112
|
+
epsilon_r_mesh : np.ndarray
|
|
113
|
+
The permittivity mesh to be updated.
|
|
114
|
+
"""
|
|
115
|
+
epsilon_r_mesh[self.idx] = self.epsilon_r
|
|
116
|
+
|
|
117
|
+
def add_to_sigma_mesh(self, sigma_mesh: np.ndarray) -> None:
|
|
118
|
+
"""
|
|
119
|
+
Add the component's sigma to the provided mesh.
|
|
120
|
+
|
|
121
|
+
Parameters
|
|
122
|
+
----------
|
|
123
|
+
sigma_mesh : np.ndarray
|
|
124
|
+
The permittivity mesh to be updated.
|
|
125
|
+
"""
|
|
126
|
+
sigma_mesh[self.idx] = self.sigma
|
|
127
|
+
|
|
128
|
+
def add_non_linear_effect_to_field(self, field: np.ndarray) -> None:
|
|
129
|
+
"""
|
|
130
|
+
Add non-linear effects to the field.
|
|
131
|
+
|
|
132
|
+
Parameters
|
|
133
|
+
----------
|
|
134
|
+
field : np.ndarray
|
|
135
|
+
The field to which non-linear effects will be added.
|
|
136
|
+
"""
|
|
137
|
+
chi_3 = 1e-8 # Third-order non-linear susceptibility (example value)
|
|
138
|
+
intensity = np.abs(field) ** 2
|
|
139
|
+
nonlinear_term = chi_3 * intensity * field
|
|
140
|
+
return field + self.grid.dt * nonlinear_term
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
@dataclass(config=config_dict, kw_only=True)
|
|
144
|
+
class Waveguide(BaseComponent):
|
|
145
|
+
"""
|
|
146
|
+
Represents a waveguide scatterer in a simulation grid.
|
|
147
|
+
|
|
148
|
+
Parameters
|
|
149
|
+
----------
|
|
150
|
+
position_0 : Tuple[Length | str, Length | str]
|
|
151
|
+
The starting position of the waveguide.
|
|
152
|
+
position_1 : Tuple[Length | str, Length | str]
|
|
153
|
+
The ending position of the waveguide.
|
|
154
|
+
width : Length | str
|
|
155
|
+
The width of the waveguide.
|
|
156
|
+
epsilon_r : float
|
|
157
|
+
The relative permittivity inside the waveguide.
|
|
158
|
+
"""
|
|
159
|
+
position_0: Tuple[Length | str, Length | str]
|
|
160
|
+
position_1: Tuple[Length | str, Length | str]
|
|
161
|
+
width: Length | str
|
|
162
|
+
epsilon_r: float
|
|
163
|
+
|
|
164
|
+
def __post_init__(self):
|
|
165
|
+
"""
|
|
166
|
+
Initialize the waveguide by computing its coordinates and building its object.
|
|
167
|
+
"""
|
|
168
|
+
x0, y0 = self.position_0
|
|
169
|
+
x1, y1 = self.position_1
|
|
170
|
+
self.coordinate_0 = self.grid.get_coordinate(x=x0, y=y0)
|
|
171
|
+
self.coordinate_1 = self.grid.get_coordinate(x=x1, y=y1)
|
|
172
|
+
self.build_object()
|
|
173
|
+
|
|
174
|
+
def compute_polygon(self) -> Path:
|
|
175
|
+
"""
|
|
176
|
+
Compute the polygon of the waveguide.
|
|
177
|
+
"""
|
|
178
|
+
line = geo.LineString(
|
|
179
|
+
[
|
|
180
|
+
(self.coordinate_0.x.to('meter').magnitude, self.coordinate_0.y.to('meter').magnitude),
|
|
181
|
+
(self.coordinate_1.x.to('meter').magnitude, self.coordinate_1.y.to('meter').magnitude)
|
|
182
|
+
]
|
|
183
|
+
)
|
|
184
|
+
self.polygon = line.buffer(self.width.to('meter').magnitude / 2)
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
@dataclass(config=config_dict)
|
|
188
|
+
class Square(BaseComponent):
|
|
189
|
+
"""
|
|
190
|
+
Represents a square scatterer in a simulation grid.
|
|
191
|
+
|
|
192
|
+
Parameters
|
|
193
|
+
----------
|
|
194
|
+
position : Tuple[Union[float, str], Union[float, str]]
|
|
195
|
+
The center position of the scatterer.
|
|
196
|
+
epsilon_r : float
|
|
197
|
+
The relative permittivity inside the scatterer.
|
|
198
|
+
side_length : Length
|
|
199
|
+
The side length of the square scatterer.
|
|
200
|
+
"""
|
|
201
|
+
position: Tuple[Length | str, Length | str]
|
|
202
|
+
epsilon_r: float
|
|
203
|
+
side_length: Length
|
|
204
|
+
|
|
205
|
+
def compute_polygon(self) -> Path:
|
|
206
|
+
"""
|
|
207
|
+
Compute the polygon of the square scatterer.
|
|
208
|
+
"""
|
|
209
|
+
half_side = self.side_length / 2
|
|
210
|
+
x_start, x_end = self.coordinate.x - half_side, self.coordinate.x + half_side
|
|
211
|
+
y_start, y_end = self.coordinate.y - half_side, self.coordinate.y + half_side
|
|
212
|
+
|
|
213
|
+
self.polygon = geo.Polygon([
|
|
214
|
+
(x_start.to('meter').magnitude, y_start.to('meter').magnitude),
|
|
215
|
+
(x_start.to('meter').magnitude, y_end.to('meter').magnitude),
|
|
216
|
+
(x_end.to('meter').magnitude, y_end.to('meter').magnitude),
|
|
217
|
+
(x_end.to('meter').magnitude, y_start.to('meter').magnitude)
|
|
218
|
+
]).convex_hull
|
|
219
|
+
|
|
220
|
+
|
|
221
|
+
@dataclass(config=config_dict, kw_only=True)
|
|
222
|
+
class Circle(BaseComponent):
|
|
223
|
+
"""
|
|
224
|
+
Represents a circular scatterer in a simulation grid.
|
|
225
|
+
|
|
226
|
+
Parameters
|
|
227
|
+
----------
|
|
228
|
+
position : Tuple[Length | str, Length | str]
|
|
229
|
+
The center position of the scatterer.
|
|
230
|
+
epsilon_r : float
|
|
231
|
+
The relative permittivity inside the scatterer.
|
|
232
|
+
radius : float
|
|
233
|
+
The radius of the scatterer.
|
|
234
|
+
"""
|
|
235
|
+
position: Tuple[Length | str, Length | str]
|
|
236
|
+
epsilon_r: float
|
|
237
|
+
radius: Length
|
|
238
|
+
|
|
239
|
+
def compute_polygon(self) -> None:
|
|
240
|
+
"""
|
|
241
|
+
Compute the polygon of the circular scatterer.
|
|
242
|
+
"""
|
|
243
|
+
self.polygon = geo.Point(self.coordinate.x.to('meter').magnitude, self.coordinate.y.to('meter').magnitude).buffer(self.radius.to('meter').magnitude)
|
|
244
|
+
|
|
245
|
+
|
|
246
|
+
@dataclass(config=config_dict, kw_only=True)
|
|
247
|
+
class Triangle(BaseComponent):
|
|
248
|
+
"""
|
|
249
|
+
Represents a triangular scatterer in a simulation grid.
|
|
250
|
+
|
|
251
|
+
Parameters
|
|
252
|
+
----------
|
|
253
|
+
position : Tuple[Length | str, Length | str]
|
|
254
|
+
The center position of the scatterer.
|
|
255
|
+
epsilon_r : float
|
|
256
|
+
The relative permittivity inside the scatterer.
|
|
257
|
+
side_length : Length
|
|
258
|
+
The side length of the triangular scatterer.
|
|
259
|
+
"""
|
|
260
|
+
position: Tuple[Length | str, Length | str]
|
|
261
|
+
epsilon_r: float
|
|
262
|
+
side_length: Length
|
|
263
|
+
|
|
264
|
+
def compute_polygon(self) -> None:
|
|
265
|
+
"""
|
|
266
|
+
Compute the polygon of the triangular scatterer.
|
|
267
|
+
"""
|
|
268
|
+
height = (np.sqrt(3) / 2) * self.side_length
|
|
269
|
+
|
|
270
|
+
# Vertices of an equilateral triangle centered at (coordinate.x, coordinate.y)
|
|
271
|
+
p0 = (self.coordinate.x - self.side_length / 2, self.coordinate.y - height / 3)
|
|
272
|
+
p1 = (self.coordinate.x + self.side_length / 2, self.coordinate.y - height / 3)
|
|
273
|
+
p2 = (self.coordinate.x, self.coordinate.y + 2 * height / 3)
|
|
274
|
+
|
|
275
|
+
self.polygon = geo.Polygon([p0, p1, p2])
|
|
276
|
+
|
|
277
|
+
|
|
278
|
+
@dataclass(config=config_dict, kw_only=True)
|
|
279
|
+
class Ellipse(BaseComponent):
|
|
280
|
+
"""
|
|
281
|
+
Represents an elliptical scatterer in a simulation grid.
|
|
282
|
+
|
|
283
|
+
Parameters
|
|
284
|
+
----------
|
|
285
|
+
position : Tuple[Length | str, Length | str]
|
|
286
|
+
The center position of the scatterer.
|
|
287
|
+
width : Length | str
|
|
288
|
+
The width of the ellipse.
|
|
289
|
+
height : Length | str
|
|
290
|
+
The height of the ellipse.
|
|
291
|
+
epsilon_r : float
|
|
292
|
+
The relative permittivity inside the scatterer.
|
|
293
|
+
resolution : float, optional
|
|
294
|
+
The resolution of the ellipse (default is 100).
|
|
295
|
+
"""
|
|
296
|
+
position: Tuple[Length | str, Length | str]
|
|
297
|
+
width: Length | str
|
|
298
|
+
height: Length | str
|
|
299
|
+
epsilon_r: float
|
|
300
|
+
resolution_factor: int = 10e6
|
|
301
|
+
|
|
302
|
+
def compute_polygon(self) -> None:
|
|
303
|
+
"""
|
|
304
|
+
Compute the polygon of the elliptical scatterer.
|
|
305
|
+
"""
|
|
306
|
+
ellipse_patch = patches.Ellipse(
|
|
307
|
+
(self.coordinate.x.to('meter').magnitude, self.coordinate.y.to('meter').magnitude),
|
|
308
|
+
width=self.width.to('meter').magnitude * self.resolution_factor,
|
|
309
|
+
height=self.height.to('meter').magnitude * self.resolution_factor,
|
|
310
|
+
)
|
|
311
|
+
|
|
312
|
+
vertices = ellipse_patch.get_verts()
|
|
313
|
+
|
|
314
|
+
self.polygon = geo.Polygon(vertices)
|
|
315
|
+
|
|
316
|
+
self.polygon = scale(self.polygon, xfact=1 / self.resolution_factor, yfact=1 / self.resolution_factor)
|
|
317
|
+
|
|
318
|
+
|
|
319
|
+
@dataclass(config=config_dict, kw_only=True)
|
|
320
|
+
class Grating(BaseComponent):
|
|
321
|
+
"""
|
|
322
|
+
Represents a grating in a simulation grid.
|
|
323
|
+
|
|
324
|
+
Parameters
|
|
325
|
+
----------
|
|
326
|
+
position : Tuple[Length | str, Length | str]
|
|
327
|
+
The starting position of the grating.
|
|
328
|
+
epsilon_r : float
|
|
329
|
+
The relative permittivity inside the grating.
|
|
330
|
+
period : float
|
|
331
|
+
The period of the grating.
|
|
332
|
+
duty_cycle : float
|
|
333
|
+
The duty cycle of the grating.
|
|
334
|
+
num_periods : int
|
|
335
|
+
The number of periods in the grating.
|
|
336
|
+
"""
|
|
337
|
+
position: Tuple[Length | str, Length | str]
|
|
338
|
+
epsilon_r: float
|
|
339
|
+
period: float
|
|
340
|
+
duty_cycle: float
|
|
341
|
+
num_periods: int
|
|
342
|
+
|
|
343
|
+
def compute_polygon(self) -> None:
|
|
344
|
+
"""
|
|
345
|
+
Compute the polygon of the grating.
|
|
346
|
+
"""
|
|
347
|
+
bars = []
|
|
348
|
+
for i in range(self.num_periods):
|
|
349
|
+
x_start = self.coordinate.x + i * self.period
|
|
350
|
+
x_end = x_start + self.duty_cycle * self.period
|
|
351
|
+
bar = geo.box(x_start, self.coordinate.y - 0.5, x_end, self.coordinate.y + 0.5)
|
|
352
|
+
bars.append(bar)
|
|
353
|
+
|
|
354
|
+
self.polygon = geo.MultiPolygon(bars)
|
|
355
|
+
|
|
356
|
+
|
|
357
|
+
@dataclass(config=config_dict, kw_only=True)
|
|
358
|
+
class RingResonator(BaseComponent):
|
|
359
|
+
"""
|
|
360
|
+
Represents a ring resonator in a simulation grid.
|
|
361
|
+
|
|
362
|
+
Parameters
|
|
363
|
+
----------
|
|
364
|
+
position : Tuple[Length | str, Length | str]
|
|
365
|
+
The center position of the ring resonator.
|
|
366
|
+
epsilon_r : float
|
|
367
|
+
The relative permittivity inside the resonator.
|
|
368
|
+
inner_radius : float
|
|
369
|
+
The inner radius of the ring.
|
|
370
|
+
width : float
|
|
371
|
+
The width of the ring.
|
|
372
|
+
"""
|
|
373
|
+
grid: Grid
|
|
374
|
+
position: Tuple[Length | str, Length | str]
|
|
375
|
+
epsilon_r: float
|
|
376
|
+
inner_radius: Length
|
|
377
|
+
width: Length
|
|
378
|
+
|
|
379
|
+
def compute_polygon(self) -> None:
|
|
380
|
+
"""
|
|
381
|
+
Compute the polygon of the ring resonator.
|
|
382
|
+
"""
|
|
383
|
+
inner_circle = geo.Point(self.coordinate.x.to('meter').magnitude, self.coordinate.y.to('meter').magnitude).buffer(self.inner_radius.to('meter').magnitude)
|
|
384
|
+
outer_circle = geo.Point(self.coordinate.x.to('meter').magnitude, self.coordinate.y.to('meter').magnitude).buffer(self.inner_radius.to('meter').magnitude + self.width.to('meter').magnitude)
|
|
385
|
+
self.polygon = outer_circle.difference(inner_circle)
|
|
386
|
+
|
|
387
|
+
|
|
388
|
+
@dataclass(config=config_dict, kw_only=True)
|
|
389
|
+
class Lense(BaseComponent):
|
|
390
|
+
"""
|
|
391
|
+
Represents a lens in a simulation grid.
|
|
392
|
+
|
|
393
|
+
Parameters
|
|
394
|
+
----------
|
|
395
|
+
position : Tuple[Length | str, Length | str]
|
|
396
|
+
The center position of the lens.
|
|
397
|
+
epsilon_r : float
|
|
398
|
+
The relative permittivity inside the lens.
|
|
399
|
+
width : Length | str
|
|
400
|
+
The width of the lens.
|
|
401
|
+
curvature : Length | str
|
|
402
|
+
The curvature of the lens (positive for convex, negative for concave).
|
|
403
|
+
"""
|
|
404
|
+
position: Tuple[Length | str, Length | str]
|
|
405
|
+
epsilon_r: float
|
|
406
|
+
width: Length | str
|
|
407
|
+
curvature: Length | str
|
|
408
|
+
|
|
409
|
+
def compute_polygon(self) -> None:
|
|
410
|
+
"""
|
|
411
|
+
Compute the polygon of the lens.
|
|
412
|
+
"""
|
|
413
|
+
# Create two arcs for the lens shape
|
|
414
|
+
x0 = self.coordinate.x + self.curvature - self.width / 2
|
|
415
|
+
x1 = self.coordinate.x - self.curvature + self.width / 2
|
|
416
|
+
|
|
417
|
+
arc3 = geo.Point(x0.to('meter').magnitude, self.coordinate.y.to('meter').magnitude).buffer(self.curvature.to('meter').magnitude, resolution=100)
|
|
418
|
+
arc4 = geo.Point(x1.to('meter').magnitude, self.coordinate.y.to('meter').magnitude).buffer(self.curvature.to('meter').magnitude, resolution=100)
|
|
419
|
+
|
|
420
|
+
self.polygon = arc3.intersection(arc4)
|