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.
Files changed (37) hide show
  1. LightWave2D/__init__.py +9 -0
  2. LightWave2D/_version.py +34 -0
  3. LightWave2D/binary/__init__.py +0 -0
  4. LightWave2D/binary/interface_config.cp311-win_amd64.pyd +0 -0
  5. LightWave2D/binary/interface_config.cp312-win_amd64.pyd +0 -0
  6. LightWave2D/binary/interface_config.cp313-win_amd64.pyd +0 -0
  7. LightWave2D/binary/interface_field_set.cp311-win_amd64.pyd +0 -0
  8. LightWave2D/binary/interface_field_set.cp312-win_amd64.pyd +0 -0
  9. LightWave2D/binary/interface_field_set.cp313-win_amd64.pyd +0 -0
  10. LightWave2D/binary/interface_mesh_set.cp311-win_amd64.pyd +0 -0
  11. LightWave2D/binary/interface_mesh_set.cp312-win_amd64.pyd +0 -0
  12. LightWave2D/binary/interface_mesh_set.cp313-win_amd64.pyd +0 -0
  13. LightWave2D/binary/interface_simulator.cp311-win_amd64.pyd +0 -0
  14. LightWave2D/binary/interface_simulator.cp312-win_amd64.pyd +0 -0
  15. LightWave2D/binary/interface_simulator.cp313-win_amd64.pyd +0 -0
  16. LightWave2D/binary/interface_source.cp311-win_amd64.pyd +0 -0
  17. LightWave2D/binary/interface_source.cp312-win_amd64.pyd +0 -0
  18. LightWave2D/binary/interface_source.cp313-win_amd64.pyd +0 -0
  19. LightWave2D/binary/libcpp_config.a +0 -0
  20. LightWave2D/binary/libcpp_field_set.a +0 -0
  21. LightWave2D/binary/libcpp_mesh_set.a +0 -0
  22. LightWave2D/binary/libcpp_simulator.a +0 -0
  23. LightWave2D/binary/libcpp_source.a +0 -0
  24. LightWave2D/components.py +420 -0
  25. LightWave2D/detector.py +184 -0
  26. LightWave2D/directories.py +34 -0
  27. LightWave2D/experiment.py +497 -0
  28. LightWave2D/grid.py +184 -0
  29. LightWave2D/helper.py +59 -0
  30. LightWave2D/physics.py +17 -0
  31. LightWave2D/pml.py +103 -0
  32. LightWave2D/source.py +352 -0
  33. LightWave2D/utils.py +66 -0
  34. lightwave2d-0.6.0.dist-info/METADATA +351 -0
  35. lightwave2d-0.6.0.dist-info/RECORD +37 -0
  36. lightwave2d-0.6.0.dist-info/WHEEL +5 -0
  37. lightwave2d-0.6.0.dist-info/licenses/LICENSE +21 -0
@@ -0,0 +1,9 @@
1
+
2
+ try:
3
+ from ._version import version as __version__ # noqa: F401
4
+
5
+ except ImportError:
6
+ __version__ = "0.0.0"
7
+
8
+
9
+ # -
@@ -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
@@ -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)