LightWave2D 0.0.3__tar.gz → 0.0.5__tar.gz

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 (51) hide show
  1. lightwave2d-0.0.5/LightWave2D/VERSION +1 -0
  2. lightwave2d-0.0.5/LightWave2D/__pycache__/__init__.cpython-311.pyc +0 -0
  3. lightwave2d-0.0.5/LightWave2D/__pycache__/components.cpython-311.pyc +0 -0
  4. lightwave2d-0.0.5/LightWave2D/__pycache__/detector.cpython-311.pyc +0 -0
  5. lightwave2d-0.0.5/LightWave2D/__pycache__/experiment.cpython-311.pyc +0 -0
  6. {lightwave2d-0.0.3 → lightwave2d-0.0.5}/LightWave2D/__pycache__/grid.cpython-311.pyc +0 -0
  7. {lightwave2d-0.0.3 → lightwave2d-0.0.5}/LightWave2D/__pycache__/physics.cpython-311.pyc +0 -0
  8. {lightwave2d-0.0.3 → lightwave2d-0.0.5}/LightWave2D/__pycache__/pml.cpython-311.pyc +0 -0
  9. lightwave2d-0.0.5/LightWave2D/__pycache__/source.cpython-311.pyc +0 -0
  10. lightwave2d-0.0.5/LightWave2D/__pycache__/utils.cpython-311.pyc +0 -0
  11. lightwave2d-0.0.5/LightWave2D/components.py +330 -0
  12. lightwave2d-0.0.5/LightWave2D/detector.py +156 -0
  13. {lightwave2d-0.0.3 → lightwave2d-0.0.5}/LightWave2D/experiment.py +107 -48
  14. {lightwave2d-0.0.3 → lightwave2d-0.0.5}/LightWave2D/pml.py +2 -3
  15. lightwave2d-0.0.5/LightWave2D/source.py +262 -0
  16. lightwave2d-0.0.5/LightWave2D/utils.py +45 -0
  17. lightwave2d-0.0.5/LightWave2D.egg-info/PKG-INFO +288 -0
  18. {lightwave2d-0.0.3 → lightwave2d-0.0.5}/LightWave2D.egg-info/SOURCES.txt +2 -6
  19. {lightwave2d-0.0.3 → lightwave2d-0.0.5}/LightWave2D.egg-info/requires.txt +1 -0
  20. lightwave2d-0.0.5/PKG-INFO +288 -0
  21. lightwave2d-0.0.5/README.rst +243 -0
  22. lightwave2d-0.0.5/developments/testings_0.py +73 -0
  23. {lightwave2d-0.0.3 → lightwave2d-0.0.5}/setup.cfg +1 -0
  24. lightwave2d-0.0.3/LightWave2D/VERSION +0 -1
  25. lightwave2d-0.0.3/LightWave2D/__pycache__/__init__.cpython-311.pyc +0 -0
  26. lightwave2d-0.0.3/LightWave2D/__pycache__/detector.cpython-311.pyc +0 -0
  27. lightwave2d-0.0.3/LightWave2D/__pycache__/experiment.cpython-311.pyc +0 -0
  28. lightwave2d-0.0.3/LightWave2D/__pycache__/source.cpython-311.pyc +0 -0
  29. lightwave2d-0.0.3/LightWave2D/__pycache__/utils.cpython-311.pyc +0 -0
  30. lightwave2d-0.0.3/LightWave2D/components/__init__.py +0 -4
  31. lightwave2d-0.0.3/LightWave2D/components/__pycache__/__init__.cpython-311.pyc +0 -0
  32. lightwave2d-0.0.3/LightWave2D/components/__pycache__/base_class.cpython-311.pyc +0 -0
  33. lightwave2d-0.0.3/LightWave2D/components/__pycache__/scatterers.cpython-311.pyc +0 -0
  34. lightwave2d-0.0.3/LightWave2D/components/base_class.py +0 -100
  35. lightwave2d-0.0.3/LightWave2D/components/scatterers.py +0 -185
  36. lightwave2d-0.0.3/LightWave2D/detector.py +0 -88
  37. lightwave2d-0.0.3/LightWave2D/source.py +0 -119
  38. lightwave2d-0.0.3/LightWave2D/utils.py +0 -95
  39. lightwave2d-0.0.3/LightWave2D.egg-info/PKG-INFO +0 -54
  40. lightwave2d-0.0.3/PKG-INFO +0 -54
  41. lightwave2d-0.0.3/README.rst +0 -10
  42. lightwave2d-0.0.3/developments/testings_0.py +0 -87
  43. {lightwave2d-0.0.3 → lightwave2d-0.0.5}/LICENSE +0 -0
  44. {lightwave2d-0.0.3 → lightwave2d-0.0.5}/LightWave2D/__init__.py +0 -0
  45. {lightwave2d-0.0.3 → lightwave2d-0.0.5}/LightWave2D/directories.py +0 -0
  46. {lightwave2d-0.0.3 → lightwave2d-0.0.5}/LightWave2D/grid.py +0 -0
  47. {lightwave2d-0.0.3 → lightwave2d-0.0.5}/LightWave2D/physics.py +0 -0
  48. {lightwave2d-0.0.3 → lightwave2d-0.0.5}/LightWave2D.egg-info/dependency_links.txt +0 -0
  49. {lightwave2d-0.0.3 → lightwave2d-0.0.5}/LightWave2D.egg-info/top_level.txt +0 -0
  50. {lightwave2d-0.0.3 → lightwave2d-0.0.5}/MANIFEST.in +0 -0
  51. {lightwave2d-0.0.3 → lightwave2d-0.0.5}/setup.py +0 -0
@@ -0,0 +1 @@
1
+ 0.0.5
@@ -0,0 +1,330 @@
1
+ #!/usr/bin/env python
2
+ # -*- coding: utf-8 -*-
3
+
4
+ from typing import NoReturn, Union, Tuple
5
+ from pydantic.dataclasses import dataclass
6
+ import numpy
7
+ from LightWave2D.physics import Physics
8
+ import matplotlib.pyplot as plt
9
+ import shapely.geometry as geo
10
+ from matplotlib.patches import PathPatch
11
+ from matplotlib import patches
12
+ import matplotlib as mpl
13
+ import numpy as np
14
+ from matplotlib.path import Path
15
+ from matplotlib.collections import PatchCollection
16
+ from LightWave2D.grid import Grid
17
+ from shapely.affinity import scale
18
+
19
+
20
+ # Configuration dictionary for dataclasses
21
+ config_dict = {
22
+ 'kw_only': True,
23
+ 'extra': 'forbid',
24
+ 'slots': True,
25
+ 'arbitrary_types_allowed': True
26
+ }
27
+
28
+
29
+ @dataclass(kw_only=True, config=config_dict)
30
+ class BaseComponent():
31
+ """
32
+ Represents a generic component in a simulation grid.
33
+
34
+ Attributes:
35
+ grid (Grid): The simulation grid.
36
+ facecolor (str): The color of the component's face.
37
+ edgecolor (str): The color of the component's edge.
38
+ alpha (float): Transparency level of the component.
39
+ rotation (float): Rotation angle of the component.
40
+ """
41
+ grid: Grid
42
+ facecolor: str = 'lightblue'
43
+ edgecolor: str = 'blue'
44
+ alpha: float = 0.3
45
+ rotation: float = 0
46
+
47
+ def __post_init__(self):
48
+ x0, y0 = self.position
49
+ self.coordinate = self.grid.get_coordinate(x=x0, y=y0)
50
+
51
+ self.build_object()
52
+
53
+ def build_object(self) -> NoReturn:
54
+ """
55
+ Build the permittivity mesh for the component.
56
+ """
57
+ self.epsilon_r_mesh = numpy.ones(self.grid.shape) # Background permittivity
58
+
59
+ self.compute_polygon()
60
+
61
+ self.path = Path(self.polygon.exterior.coords)
62
+
63
+ self.path = self.path.transformed(mpl.transforms.Affine2D().rotate_around(self.coordinate.x, self.coordinate.y, self.rotation))
64
+
65
+ x_mesh, y_mesh = numpy.meshgrid(self.grid.x_stamp, self.grid.y_stamp)
66
+
67
+ coordinates = numpy.c_[x_mesh.T.flatten(), y_mesh.T.flatten()]
68
+
69
+ self.idx = self.path.contains_points(coordinates).astype(bool).reshape(self.epsilon_r_mesh.shape)
70
+
71
+ self.epsilon_r_mesh[self.idx] = self.epsilon_r
72
+
73
+ def add_to_ax(self, ax: plt.axis) -> PatchCollection:
74
+ """
75
+ Add the component to the provided axis.
76
+
77
+ Args:
78
+ ax (plt.Axes): The axis to which the component will be added.
79
+
80
+ Returns:
81
+ PatchCollection: The collection of patches added to the axis.
82
+ """
83
+ path = Path.make_compound_path(
84
+ Path(np.asarray(self.polygon.exterior.coords)[:, :2]),
85
+ *[Path(np.asarray(ring.coords)[:, :2]) for ring in self.polygon.interiors])
86
+
87
+ patch = PathPatch(path, facecolor=self.facecolor, edgecolor=self.edgecolor, alpha=0.4)
88
+ collection = PatchCollection([patch], facecolor=self.facecolor, edgecolor=self.edgecolor, alpha=self.alpha)
89
+
90
+ ax.add_collection(collection, autolim=True)
91
+ ax.autoscale_view()
92
+ return collection
93
+
94
+ def plot(self) -> NoReturn:
95
+ """
96
+ Plot the component.
97
+ """
98
+ figure, ax = plt.subplots(1, 1, figsize=(6, 6))
99
+ ax.set_title('FDTD Simulation')
100
+ ax.set_xlabel(r'x position [$\mu$m]')
101
+ ax.set_xlabel(r'y position [$\mu$m]')
102
+ ax.set_aspect('equal')
103
+
104
+ self.add_to_ax(ax)
105
+ ax.autoscale_view()
106
+
107
+ plt.show()
108
+
109
+ def add_to_mesh(self, epsilon_r_mesh: numpy.ndarray) -> NoReturn:
110
+ """
111
+ Add the component's permittivity to the provided mesh.
112
+
113
+ Args:
114
+ epsilon_r_mesh (np.ndarray): The permittivity mesh to be updated.
115
+ """
116
+ epsilon_r_mesh += self.epsilon_r_mesh
117
+
118
+ def add_non_linear_effect_to_field(self, field: numpy.ndarray) -> NoReturn:
119
+ """
120
+ Add non-linear effects to the field.
121
+
122
+ Args:
123
+ field (np.ndarray): The field to which non-linear effects will be added.
124
+ """
125
+ chi_2 = 1e10
126
+
127
+ field += self.idx * self.grid.dt**2 / (self.epsilon_r_mesh * Physics.epsilon_0 * Physics.mu_0) * chi_2 * field ** 2
128
+
129
+
130
+ @dataclass(config=config_dict)
131
+ class Square(BaseComponent):
132
+ """
133
+ Represents a square scatterer in a simulation grid.
134
+
135
+ Attributes:
136
+ position (Tuple[Union[float, str], Union[float, str]]): The center position of the scatterer.
137
+ epsilon_r (float): The relative permittivity inside the scatterer.
138
+ side_length (float): The side length of the square scatterer.
139
+ """
140
+ position: Tuple[Union[float, str], Union[float, str]]
141
+ epsilon_r: float
142
+ side_length: float
143
+
144
+ def compute_polygon(self) -> Path:
145
+ """
146
+ Compute the polygon of the square scatterer.
147
+ """
148
+ half_side = self.side_length / 2
149
+ x_start, x_end = self.coordinate.x - half_side, self.coordinate.x + half_side
150
+ y_start, y_end = self.coordinate.y - half_side, self.coordinate.y + half_side
151
+
152
+ self.polygon = geo.Polygon([
153
+ (x_start, y_start),
154
+ (x_start, y_end),
155
+ (x_end, y_end),
156
+ (x_end, y_start)
157
+ ])
158
+
159
+
160
+ @dataclass(config=config_dict)
161
+ class Circle(BaseComponent):
162
+ """
163
+ Represents a circular scatterer in a simulation grid.
164
+
165
+ Attributes:
166
+ position (Tuple[Union[float, str], Union[float, str]]): The center position of the scatterer.
167
+ epsilon_r (float): The relative permittivity inside the scatterer.
168
+ radius (float): The radius of the scatterer.
169
+ """
170
+ position: Tuple[Union[float, str], Union[float, str]]
171
+ epsilon_r: float
172
+ radius: float
173
+
174
+ def compute_polygon(self) -> NoReturn:
175
+ """
176
+ Compute the polygon of the circular scatterer.
177
+ """
178
+ self.polygon = geo.Point(self.coordinate.x, self.coordinate.y).buffer(self.radius)
179
+
180
+
181
+ @dataclass(config=config_dict)
182
+ class Triangle(BaseComponent):
183
+ """
184
+ Represents a triangular scatterer in a simulation grid.
185
+
186
+ Attributes:
187
+ position (Tuple[Union[float, str], Union[float, str]]): The center position of the scatterer.
188
+ epsilon_r (float): The relative permittivity inside the scatterer.
189
+ side_length (float): The side length of the triangular scatterer.
190
+ """
191
+ position: Tuple[Union[float, str], Union[float, str]]
192
+ epsilon_r: float
193
+ side_length: float
194
+
195
+ def compute_polygon(self) -> NoReturn:
196
+ """
197
+ Compute the polygon of the triangular scatterer.
198
+ """
199
+ height = (np.sqrt(3) / 2) * self.side_length
200
+
201
+ # Vertices of an equilateral triangle centered at (coordinate.x, coordinate.y)
202
+ p0 = (self.coordinate.x - self.side_length / 2, self.coordinate.y - height / 3)
203
+ p1 = (self.coordinate.x + self.side_length / 2, self.coordinate.y - height / 3)
204
+ p2 = (self.coordinate.x, self.coordinate.y + 2 * height / 3)
205
+
206
+ self.polygon = geo.Polygon([p0, p1, p2])
207
+
208
+
209
+ @dataclass(config=config_dict)
210
+ class Ellipse(BaseComponent):
211
+ """
212
+ Represents an elliptical scatterer in a simulation grid.
213
+
214
+ Attributes:
215
+ position (Tuple[Union[float, str], Union[float, str]]): The center position of the scatterer.
216
+ width (float): The width of the ellipse.
217
+ height (float): The height of the ellipse.
218
+ epsilon_r (float): The relative permittivity inside the scatterer.
219
+ resolution (float): The resolution of the ellipse.
220
+ """
221
+ position: Tuple[Union[float, str], Union[float, str]]
222
+ width: float
223
+ height: float
224
+ epsilon_r: float
225
+ resolution: float = 100
226
+
227
+ def compute_polygon(self) -> NoReturn:
228
+ """
229
+ Compute the polygon of the elliptical scatterer.
230
+ """
231
+ ellipse_patch = patches.Ellipse(
232
+ (self.coordinate.x, self.coordinate.y),
233
+ width=self.resolution,
234
+ height=self.resolution * self.height / self.width,
235
+ )
236
+
237
+ vertices = ellipse_patch.get_verts()
238
+ polygon = geo.Polygon(vertices)
239
+
240
+ self.polygon = scale(polygon, xfact=self.width / self.resolution, yfact=self.width / self.resolution)
241
+
242
+
243
+ @dataclass(config=config_dict)
244
+ class Grating(BaseComponent):
245
+ """
246
+ Represents a grating in a simulation grid.
247
+
248
+ Attributes:
249
+ position (Tuple[Union[float, str], Union[float, str]]): The starting position of the grating.
250
+ epsilon_r (float): The relative permittivity inside the grating.
251
+ period (float): The period of the grating.
252
+ duty_cycle (float): The duty cycle of the grating.
253
+ num_periods (int): The number of periods in the grating.
254
+ """
255
+ position: Tuple[Union[float, str], Union[float, str]]
256
+ epsilon_r: float
257
+ period: float
258
+ duty_cycle: float
259
+ num_periods: int
260
+
261
+ def compute_polygon(self) -> NoReturn:
262
+ """
263
+ Compute the polygon of the grating.
264
+ """
265
+ bars = []
266
+ for i in range(self.num_periods):
267
+ x_start = self.coordinate.x + i * self.period
268
+ x_end = x_start + self.duty_cycle * self.period
269
+ bar = geo.box(x_start, self.coordinate.y - 0.5, x_end, self.coordinate.y + 0.5)
270
+ bars.append(bar)
271
+
272
+ self.polygon = geo.MultiPolygon(bars)
273
+
274
+
275
+ @dataclass(config=config_dict)
276
+ class RingResonator(BaseComponent):
277
+ """
278
+ Represents a ring resonator in a simulation grid.
279
+
280
+ Attributes:
281
+ position (Tuple[Union[float, str], Union[float, str]]): The center position of the ring resonator.
282
+ epsilon_r (float): The relative permittivity inside the resonator.
283
+ inner_radius (float): The inner radius of the ring.
284
+ width (float): The width of the ring.
285
+ """
286
+ grid: Grid
287
+ position: Tuple[Union[float, str], Union[float, str]]
288
+ epsilon_r: float
289
+ inner_radius: float
290
+ width: float
291
+
292
+ def compute_polygon(self) -> NoReturn:
293
+ """
294
+ Compute the polygon of the ring resonator.
295
+ """
296
+ inner_circle = geo.Point(self.coordinate.x, self.coordinate.y).buffer(self.inner_radius)
297
+ outer_circle = geo.Point(self.coordinate.x, self.coordinate.y).buffer(self.inner_radius + self.width)
298
+ self.polygon = outer_circle.difference(inner_circle)
299
+
300
+
301
+ @dataclass(config=config_dict)
302
+ class Lense(BaseComponent):
303
+ """
304
+ Represents a lens in a simulation grid.
305
+
306
+ Attributes:
307
+ position (Tuple[Union[float, str], Union[float, str]]): The center position of the lens.
308
+ epsilon_r (float): The relative permittivity inside the lens.
309
+ width (float): The width of the lens.
310
+ curvature (float): The curvature of the lens (positive for convex, negative for concave).
311
+ """
312
+ position: Tuple[Union[float, str], Union[float, str]]
313
+ epsilon_r: float
314
+ width: float
315
+ curvature: float
316
+
317
+ def compute_polygon(self) -> NoReturn:
318
+ """
319
+ Compute the polygon of the lens.
320
+ """
321
+ # Create two arcs for the lens shape
322
+ x0 = self.coordinate.x + self.curvature - self.width / 2
323
+ x1 = self.coordinate.x - self.curvature + self.width / 2
324
+
325
+ arc3 = geo.Point(x0, self.coordinate.y).buffer(self.curvature, resolution=100)
326
+ arc4 = geo.Point(x1, self.coordinate.y).buffer(self.curvature, resolution=100)
327
+
328
+ self.polygon = arc3.intersection(arc4)
329
+
330
+ # -
@@ -0,0 +1,156 @@
1
+ #!/usr/bin/env python
2
+ # -*- coding: utf-8 -*-
3
+
4
+ from typing import Tuple, Union, NoReturn
5
+ from dataclasses import field
6
+ import numpy
7
+ from LightWave2D.grid import Grid
8
+ import shapely.geometry as geo
9
+ from matplotlib.path import Path
10
+ from pydantic.dataclasses import dataclass
11
+ from matplotlib.collections import PatchCollection
12
+ from matplotlib.patches import PathPatch
13
+ import matplotlib.pyplot as plt
14
+ import matplotlib
15
+
16
+ config_dict = dict(
17
+ kw_only=True,
18
+ slots=True,
19
+ extra='forbid',
20
+ arbitrary_types_allowed=True
21
+ )
22
+
23
+
24
+ @dataclass(kw_only=True, config=config_dict)
25
+ class BaseDetector():
26
+ """
27
+ Represents an elliptical scatterer in a simulation grid.
28
+
29
+ Attributes:
30
+ grid (Grid): The grid of the simulation mesh.
31
+ angle (float): Rotation angle of the ellipse.
32
+ facecolor (str): Color of the scatterer face.
33
+ edgecolor (str): Color of the scatterer edge.
34
+ """
35
+ grid: Grid
36
+ facecolor: str = 'green'
37
+ edgecolor: str = 'green'
38
+ alpha: float = 0.8
39
+ rotation: float = 0
40
+
41
+ def __post_init__(self):
42
+ x0, y0 = self.position
43
+ self.coordinate = self.grid.get_coordinate(x=x0, y=y0)
44
+
45
+ self.build_object()
46
+
47
+ def build_object(self) -> NoReturn:
48
+ """
49
+ Build the permittivity mesh for the square scatterer.
50
+ """
51
+ self.compute_polygon()
52
+
53
+ self.path = Path(self.polygon.exterior.coords)
54
+
55
+ self.path = self.path.transformed(matplotlib.transforms.Affine2D().rotate_around(self.coordinate.x, self.coordinate.y, self.rotation))
56
+
57
+ def add_to_ax(self, ax: plt.axis) -> PatchCollection:
58
+ """
59
+ Add the scatterer to the provided axis as a circle.
60
+
61
+ Args:
62
+ ax (Axis): The axis to which the scatterer will be added.
63
+ """
64
+ path = Path.make_compound_path(
65
+ Path(numpy.asarray(self.polygon.exterior.coords)[:, :2]),
66
+ *[Path(numpy.asarray(ring.coords)[:, :2]) for ring in self.polygon.interiors])
67
+
68
+ patch = PathPatch(path, color=self.facecolor, edgecolor=self.edgecolor, alpha=0.4)
69
+ collection = PatchCollection([patch], color=self.facecolor, edgecolor=self.edgecolor, alpha=self.alpha)
70
+
71
+ ax.add_collection(collection, autolim=True)
72
+ ax.autoscale_view()
73
+ return collection
74
+
75
+ def plot(self) -> NoReturn:
76
+ figure, ax = plt.subplots(1, 1, figsize=(6, 6))
77
+ ax.set_title('FDTD Simulation')
78
+ ax.set_xlabel(r'x position [$\mu$m]')
79
+ ax.set_xlabel(r'y position [$\mu$m]')
80
+ ax.set_aspect('equal')
81
+
82
+ self.add_to_ax(ax)
83
+ ax.autoscale_view()
84
+
85
+ plt.show()
86
+
87
+
88
+ @dataclass(kw_only=True, config=config_dict)
89
+ class PointDetector(BaseDetector):
90
+ """
91
+ Represents a point detector within a simulation grid.
92
+
93
+ Attributes:
94
+ grid (Grid): The simulation mesh grid.
95
+ position (Tuple[float | str, float | str]): The (x, y) coordinates of the point detector on the grid.
96
+ data (numpy.ndarray): The data collected by the detector over time.
97
+ """
98
+ position: Union[Tuple[float | str, float | str]]
99
+ coherent: bool = True
100
+ data: numpy.ndarray = field(init=False)
101
+
102
+ def __post_init__(self):
103
+ """
104
+ Initialize the PointDetector by converting the given position into
105
+ grid coordinates and initializing the data array.
106
+ """
107
+ x, y = self.position
108
+
109
+ self.p0 = self.grid.get_coordinate(x=x, y=y)
110
+
111
+ self.polygon = geo.Point(self.p0.x, self.p0.y)
112
+
113
+ self.path = Path(self.polygon.coords)
114
+
115
+ self.data = numpy.zeros(self.grid.n_steps)
116
+
117
+ def update_data(self, field: numpy.ndarray) -> NoReturn:
118
+ """
119
+ Update the detector data based on the provided field values.
120
+
121
+ Parameters:
122
+ field (numpy.ndarray): The field values to update the detector data.
123
+ """
124
+ if self.coherent:
125
+ self.data = field[:, self.p0.x_index, self.p0.y_index]
126
+ else:
127
+ self.data = abs(field[:, self.p0.x_index, self.p0.y_index])
128
+
129
+ def plot_data(self) -> NoReturn:
130
+ """
131
+ Plot the detector data over time.
132
+
133
+ Returns:
134
+ SceneList: A scene list containing the plot.
135
+ """
136
+ figure, ax = plt.subplots(1, 1, figsize=(8, 4))
137
+ ax.plot(self.grid.time_stamp, self.data)
138
+ ax.set_ylabel('Amplitude')
139
+ ax.set_xlabel('Time [seconds]')
140
+ figure.show()
141
+
142
+ def add_to_ax(self, ax: plt.axis) -> NoReturn:
143
+ """
144
+ Add the scatterer to the provided axis as a circle.
145
+
146
+ Args:
147
+ ax (Axis): The axis to which the scatterer will be added.
148
+ """
149
+ ax.scatter(
150
+ self.p0.x,
151
+ self.p0.y,
152
+ color=self.facecolor,
153
+ label='detector'
154
+ )
155
+
156
+ # -