LightWave2D 0.0.7__tar.gz → 0.0.8__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.
- lightwave2d-0.0.8/LightWave2D/VERSION +1 -0
- lightwave2d-0.0.8/LightWave2D/__pycache__/__init__.cpython-311.pyc +0 -0
- {lightwave2d-0.0.7 → lightwave2d-0.0.8}/LightWave2D/__pycache__/components.cpython-311.pyc +0 -0
- {lightwave2d-0.0.7 → lightwave2d-0.0.8}/LightWave2D/__pycache__/detector.cpython-311.pyc +0 -0
- lightwave2d-0.0.8/LightWave2D/__pycache__/experiment.cpython-311.pyc +0 -0
- {lightwave2d-0.0.7 → lightwave2d-0.0.8}/LightWave2D/__pycache__/grid.cpython-311.pyc +0 -0
- {lightwave2d-0.0.7 → lightwave2d-0.0.8}/LightWave2D/__pycache__/physics.cpython-311.pyc +0 -0
- {lightwave2d-0.0.7 → lightwave2d-0.0.8}/LightWave2D/__pycache__/pml.cpython-311.pyc +0 -0
- lightwave2d-0.0.8/LightWave2D/__pycache__/source.cpython-311.pyc +0 -0
- {lightwave2d-0.0.7 → lightwave2d-0.0.8}/LightWave2D/__pycache__/utils.cpython-311.pyc +0 -0
- {lightwave2d-0.0.7 → lightwave2d-0.0.8}/LightWave2D/components.py +11 -12
- {lightwave2d-0.0.7 → lightwave2d-0.0.8}/LightWave2D/experiment.py +45 -38
- {lightwave2d-0.0.7 → lightwave2d-0.0.8}/LightWave2D/source.py +115 -51
- {lightwave2d-0.0.7 → lightwave2d-0.0.8}/LightWave2D.egg-info/PKG-INFO +1 -1
- {lightwave2d-0.0.7 → lightwave2d-0.0.8}/PKG-INFO +1 -1
- lightwave2d-0.0.8/developments/testings_0.py +80 -0
- lightwave2d-0.0.7/LightWave2D/VERSION +0 -1
- lightwave2d-0.0.7/LightWave2D/__pycache__/__init__.cpython-311.pyc +0 -0
- lightwave2d-0.0.7/LightWave2D/__pycache__/experiment.cpython-311.pyc +0 -0
- lightwave2d-0.0.7/LightWave2D/__pycache__/source.cpython-311.pyc +0 -0
- lightwave2d-0.0.7/developments/testings_0.py +0 -82
- {lightwave2d-0.0.7 → lightwave2d-0.0.8}/LICENSE +0 -0
- {lightwave2d-0.0.7 → lightwave2d-0.0.8}/LightWave2D/__init__.py +0 -0
- {lightwave2d-0.0.7 → lightwave2d-0.0.8}/LightWave2D/detector.py +0 -0
- {lightwave2d-0.0.7 → lightwave2d-0.0.8}/LightWave2D/directories.py +0 -0
- {lightwave2d-0.0.7 → lightwave2d-0.0.8}/LightWave2D/grid.py +0 -0
- {lightwave2d-0.0.7 → lightwave2d-0.0.8}/LightWave2D/physics.py +0 -0
- {lightwave2d-0.0.7 → lightwave2d-0.0.8}/LightWave2D/pml.py +0 -0
- {lightwave2d-0.0.7 → lightwave2d-0.0.8}/LightWave2D/utils.py +0 -0
- {lightwave2d-0.0.7 → lightwave2d-0.0.8}/LightWave2D.egg-info/SOURCES.txt +0 -0
- {lightwave2d-0.0.7 → lightwave2d-0.0.8}/LightWave2D.egg-info/dependency_links.txt +0 -0
- {lightwave2d-0.0.7 → lightwave2d-0.0.8}/LightWave2D.egg-info/requires.txt +0 -0
- {lightwave2d-0.0.7 → lightwave2d-0.0.8}/LightWave2D.egg-info/top_level.txt +0 -0
- {lightwave2d-0.0.7 → lightwave2d-0.0.8}/MANIFEST.in +0 -0
- {lightwave2d-0.0.7 → lightwave2d-0.0.8}/README.rst +0 -0
- {lightwave2d-0.0.7 → lightwave2d-0.0.8}/setup.cfg +0 -0
- {lightwave2d-0.0.7 → lightwave2d-0.0.8}/setup.py +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
0.0.8
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -4,12 +4,10 @@
|
|
|
4
4
|
from typing import NoReturn, Union, Tuple
|
|
5
5
|
from pydantic.dataclasses import dataclass
|
|
6
6
|
import numpy
|
|
7
|
-
from LightWave2D.physics import Physics
|
|
8
7
|
import matplotlib.pyplot as plt
|
|
9
8
|
import shapely.geometry as geo
|
|
10
9
|
from matplotlib.patches import PathPatch
|
|
11
10
|
from matplotlib import patches
|
|
12
|
-
import numpy as np
|
|
13
11
|
from matplotlib.path import Path
|
|
14
12
|
from matplotlib.collections import PatchCollection
|
|
15
13
|
from LightWave2D.grid import Grid
|
|
@@ -80,8 +78,8 @@ class BaseComponent():
|
|
|
80
78
|
PatchCollection: The collection of patches added to the axis.
|
|
81
79
|
"""
|
|
82
80
|
path = Path.make_compound_path(
|
|
83
|
-
Path(
|
|
84
|
-
*[Path(
|
|
81
|
+
Path(numpy.asarray(self.polygon.exterior.coords)[:, :2]),
|
|
82
|
+
*[Path(numpy.asarray(ring.coords)[:, :2]) for ring in self.polygon.interiors])
|
|
85
83
|
|
|
86
84
|
patch = PathPatch(path, facecolor=self.facecolor, edgecolor=self.edgecolor, alpha=0.4)
|
|
87
85
|
collection = PatchCollection([patch], facecolor=self.facecolor, edgecolor=self.edgecolor, alpha=self.alpha)
|
|
@@ -110,7 +108,7 @@ class BaseComponent():
|
|
|
110
108
|
Add the component's permittivity to the provided mesh.
|
|
111
109
|
|
|
112
110
|
Args:
|
|
113
|
-
epsilon_r_mesh (
|
|
111
|
+
epsilon_r_mesh (numpy.ndarray): The permittivity mesh to be updated.
|
|
114
112
|
"""
|
|
115
113
|
epsilon_r_mesh[self.idx] = self.epsilon_r
|
|
116
114
|
|
|
@@ -119,7 +117,7 @@ class BaseComponent():
|
|
|
119
117
|
Add the component's sigma to the provided mesh.
|
|
120
118
|
|
|
121
119
|
Args:
|
|
122
|
-
sigma_mesh (
|
|
120
|
+
sigma_mesh (numpy.ndarray): The permittivity mesh to be updated.
|
|
123
121
|
"""
|
|
124
122
|
sigma_mesh[self.idx] = self.sigma
|
|
125
123
|
|
|
@@ -128,12 +126,13 @@ class BaseComponent():
|
|
|
128
126
|
Add non-linear effects to the field.
|
|
129
127
|
|
|
130
128
|
Args:
|
|
131
|
-
field (
|
|
129
|
+
field (numpy.ndarray): The field to which non-linear effects will be added.
|
|
132
130
|
"""
|
|
133
|
-
return
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
131
|
+
return field
|
|
132
|
+
chi_3 = 1e-8 # Third-order non-linear susceptibility (example value)
|
|
133
|
+
intensity = numpy.abs(field) ** 2
|
|
134
|
+
nonlinear_term = chi_3 * intensity * field
|
|
135
|
+
return field + self.grid.dt * nonlinear_term
|
|
137
136
|
|
|
138
137
|
|
|
139
138
|
@dataclass(config=config_dict)
|
|
@@ -239,7 +238,7 @@ class Triangle(BaseComponent):
|
|
|
239
238
|
"""
|
|
240
239
|
Compute the polygon of the triangular scatterer.
|
|
241
240
|
"""
|
|
242
|
-
height = (
|
|
241
|
+
height = (numpy.sqrt(3) / 2) * self.side_length
|
|
243
242
|
|
|
244
243
|
# Vertices of an equilateral triangle centered at (coordinate.x, coordinate.y)
|
|
245
244
|
p0 = (self.coordinate.x - self.side_length / 2, self.coordinate.y - height / 3)
|
|
@@ -6,8 +6,8 @@ from typing import Tuple, NoReturn, Optional, Union, List
|
|
|
6
6
|
import numpy
|
|
7
7
|
from LightWave2D.physics import Physics
|
|
8
8
|
from LightWave2D.grid import Grid
|
|
9
|
-
from LightWave2D
|
|
10
|
-
from LightWave2D
|
|
9
|
+
from LightWave2D import components
|
|
10
|
+
from LightWave2D import source
|
|
11
11
|
from LightWave2D.detector import PointDetector
|
|
12
12
|
from LightWave2D.pml import PML
|
|
13
13
|
from MPSPlots import colormaps
|
|
@@ -116,82 +116,89 @@ class Experiment:
|
|
|
116
116
|
return self.pml
|
|
117
117
|
|
|
118
118
|
@add_to_component
|
|
119
|
-
def add_circle(self, **kwargs) -> Circle:
|
|
119
|
+
def add_circle(self, **kwargs) -> components.Circle:
|
|
120
120
|
"""
|
|
121
|
-
Method to add a Circle to the simulation.
|
|
121
|
+
Method to add a components.Circle to the simulation.
|
|
122
122
|
"""
|
|
123
|
-
return Circle(grid=self.grid, **kwargs)
|
|
123
|
+
return components.Circle(grid=self.grid, **kwargs)
|
|
124
124
|
|
|
125
125
|
@add_to_component
|
|
126
|
-
def add_ellipse(self, **kwargs) -> Ellipse:
|
|
126
|
+
def add_ellipse(self, **kwargs) -> components.Ellipse:
|
|
127
127
|
"""
|
|
128
|
-
Method to add a Ellipse to the simulation.
|
|
128
|
+
Method to add a components.Ellipse to the simulation.
|
|
129
129
|
"""
|
|
130
|
-
return Ellipse(grid=self.grid, **kwargs)
|
|
130
|
+
return components.Ellipse(grid=self.grid, **kwargs)
|
|
131
131
|
|
|
132
132
|
@add_to_component
|
|
133
|
-
def add_square(self, **kwargs) -> Square:
|
|
133
|
+
def add_square(self, **kwargs) -> components.Square:
|
|
134
134
|
"""
|
|
135
135
|
open()
|
|
136
|
-
Method to add a Square to the simulation.
|
|
136
|
+
Method to add a components.Square to the simulation.
|
|
137
137
|
"""
|
|
138
|
-
return Square(grid=self.grid, **kwargs)
|
|
138
|
+
return components.Square(grid=self.grid, **kwargs)
|
|
139
139
|
|
|
140
140
|
@add_to_component
|
|
141
|
-
def add_triangle(self, **kwargs) ->
|
|
141
|
+
def add_triangle(self, **kwargs) -> components.Triangle:
|
|
142
142
|
"""
|
|
143
|
-
Method to add a Triangle to the simulation.
|
|
143
|
+
Method to add a components.Triangle to the simulation.
|
|
144
144
|
"""
|
|
145
|
-
return Triangle(grid=self.grid, **kwargs)
|
|
145
|
+
return components.Triangle(grid=self.grid, **kwargs)
|
|
146
146
|
|
|
147
147
|
@add_to_component
|
|
148
|
-
def add_lense(self, **kwargs) ->
|
|
148
|
+
def add_lense(self, **kwargs) -> components.Lense:
|
|
149
149
|
"""
|
|
150
|
-
Method to add a Lense to the simulation.
|
|
150
|
+
Method to add a components.Lense to the simulation.
|
|
151
151
|
"""
|
|
152
|
-
return Lense(grid=self.grid, **kwargs)
|
|
152
|
+
return components.Lense(grid=self.grid, **kwargs)
|
|
153
153
|
|
|
154
154
|
@add_to_component
|
|
155
|
-
def add_grating(self, **kwargs) ->
|
|
155
|
+
def add_grating(self, **kwargs) -> components.Grating:
|
|
156
156
|
"""
|
|
157
|
-
Method to add a Grating to the simulation.
|
|
157
|
+
Method to add a components.Grating to the simulation.
|
|
158
158
|
"""
|
|
159
|
-
return Grating(grid=self.grid, **kwargs)
|
|
159
|
+
return components.Grating(grid=self.grid, **kwargs)
|
|
160
160
|
|
|
161
161
|
@add_to_component
|
|
162
|
-
def add_ring_resonator(self, **kwargs) ->
|
|
162
|
+
def add_ring_resonator(self, **kwargs) -> components.RingResonator:
|
|
163
163
|
"""
|
|
164
|
-
Method to add a RingResonator to the simulation.
|
|
164
|
+
Method to add a components.RingResonator to the simulation.
|
|
165
165
|
"""
|
|
166
|
-
return RingResonator(grid=self.grid, **kwargs)
|
|
166
|
+
return components.RingResonator(grid=self.grid, **kwargs)
|
|
167
167
|
|
|
168
168
|
@add_to_component
|
|
169
|
-
def add_waveguide(self, **kwargs) -> Waveguide:
|
|
169
|
+
def add_waveguide(self, **kwargs) -> components.Waveguide:
|
|
170
170
|
"""
|
|
171
|
-
Method to add a Waveguide to the simulation.
|
|
171
|
+
Method to add a components.Waveguide to the simulation.
|
|
172
172
|
"""
|
|
173
|
-
return Waveguide(grid=self.grid, **kwargs)
|
|
173
|
+
return components.Waveguide(grid=self.grid, **kwargs)
|
|
174
174
|
|
|
175
175
|
@add_to_source
|
|
176
|
-
def add_point_source(self, **kwargs) -> PointSource:
|
|
176
|
+
def add_point_source(self, **kwargs) -> source.PointSource:
|
|
177
177
|
"""
|
|
178
|
-
Method to add a PointSource to the simulation.
|
|
178
|
+
Method to add a source.PointSource to the simulation.
|
|
179
179
|
"""
|
|
180
|
-
return PointSource(grid=self.grid, **kwargs)
|
|
180
|
+
return source.PointSource(grid=self.grid, **kwargs)
|
|
181
181
|
|
|
182
182
|
@add_to_source
|
|
183
|
-
def add_point_impulsion(self, **kwargs) -> PointImpulsion:
|
|
183
|
+
def add_point_impulsion(self, **kwargs) -> source.PointImpulsion:
|
|
184
184
|
"""
|
|
185
|
-
Method to add a Impulsion to the simulation.
|
|
185
|
+
Method to add a source.Impulsion to the simulation.
|
|
186
186
|
"""
|
|
187
|
-
return PointImpulsion(grid=self.grid, **kwargs)
|
|
187
|
+
return source.PointImpulsion(grid=self.grid, **kwargs)
|
|
188
188
|
|
|
189
189
|
@add_to_source
|
|
190
|
-
def add_line_source(self, **kwargs) -> LineSource:
|
|
190
|
+
def add_line_source(self, **kwargs) -> source.LineSource:
|
|
191
191
|
"""
|
|
192
|
-
Method to add a LineSource to the simulation.
|
|
192
|
+
Method to add a source.LineSource to the simulation.
|
|
193
193
|
"""
|
|
194
|
-
return LineSource(grid=self.grid, **kwargs)
|
|
194
|
+
return source.LineSource(grid=self.grid, **kwargs)
|
|
195
|
+
|
|
196
|
+
@add_to_source
|
|
197
|
+
def add_line_impulsion(self, **kwargs) -> source.LineImpulsion:
|
|
198
|
+
"""
|
|
199
|
+
Method to add a source.LineSource to the simulation.
|
|
200
|
+
"""
|
|
201
|
+
return source.LineImpulsion(grid=self.grid, **kwargs)
|
|
195
202
|
|
|
196
203
|
@add_to_detector
|
|
197
204
|
def add_point_detector(self, **kwargs) -> PointDetector:
|
|
@@ -276,10 +283,10 @@ class Experiment:
|
|
|
276
283
|
Ez *= absorption_factor
|
|
277
284
|
|
|
278
285
|
for component in self.components:
|
|
279
|
-
component.add_non_linear_effect_to_field(Ez)
|
|
286
|
+
Ez = component.add_non_linear_effect_to_field(Ez)
|
|
280
287
|
|
|
281
|
-
for
|
|
282
|
-
|
|
288
|
+
for element in self.sources:
|
|
289
|
+
element.add_source_to_field(Ez, time=t)
|
|
283
290
|
|
|
284
291
|
self.Ez_t[iteration] = Ez
|
|
285
292
|
|
|
@@ -25,15 +25,16 @@ config_dict = {
|
|
|
25
25
|
|
|
26
26
|
|
|
27
27
|
@dataclass(kw_only=True, config=config_dict)
|
|
28
|
-
class BaseSource
|
|
28
|
+
class BaseSource:
|
|
29
29
|
"""
|
|
30
|
-
|
|
30
|
+
Base class for sources in a 2D light wave simulation.
|
|
31
31
|
|
|
32
32
|
Attributes:
|
|
33
33
|
grid (Grid): The grid of the simulation mesh.
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
34
|
+
facecolor (str): Color of the source face.
|
|
35
|
+
edgecolor (str): Color of the source edge.
|
|
36
|
+
alpha (float): Transparency level of the source.
|
|
37
|
+
rotation (float): Rotation angle of the source.
|
|
37
38
|
"""
|
|
38
39
|
grid: Grid
|
|
39
40
|
facecolor: str = 'red'
|
|
@@ -42,73 +43,63 @@ class BaseSource():
|
|
|
42
43
|
rotation: float = 0
|
|
43
44
|
|
|
44
45
|
def __post_init__(self):
|
|
45
|
-
|
|
46
|
-
self.coordinate = self.grid.get_coordinate(x=x0, y=y0)
|
|
47
|
-
|
|
46
|
+
self.coordinate = self.grid.get_coordinate(x=self.position[0], y=self.position[1])
|
|
48
47
|
self.build_object()
|
|
49
48
|
|
|
50
49
|
def build_object(self) -> NoReturn:
|
|
51
50
|
"""
|
|
52
|
-
Build the
|
|
51
|
+
Build the source object and initialize its properties.
|
|
53
52
|
"""
|
|
54
53
|
self.compute_polygon()
|
|
55
|
-
|
|
56
54
|
self.path = Path(self.polygon.exterior.coords)
|
|
57
|
-
|
|
58
55
|
self.path = self.path.transformed(matplotlib.transforms.Affine2D().rotate_around(self.coordinate.x, self.coordinate.y, self.rotation))
|
|
59
|
-
|
|
60
56
|
x_mesh, y_mesh = numpy.meshgrid(self.grid.x_stamp, self.grid.y_stamp)
|
|
61
|
-
|
|
62
57
|
coordinates = numpy.c_[x_mesh.T.flatten(), y_mesh.T.flatten()]
|
|
58
|
+
self.idx = self.path.contains_points(coordinates).astype(bool).reshape(self.grid.shape)
|
|
63
59
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
def add_to_ax(self, ax: plt.axis) -> PatchCollection:
|
|
60
|
+
def add_to_ax(self, ax: plt.Axes) -> PatchCollection:
|
|
67
61
|
"""
|
|
68
|
-
Add the
|
|
62
|
+
Add the source to the provided axis as a scatter point or patch.
|
|
69
63
|
|
|
70
64
|
Args:
|
|
71
|
-
ax (
|
|
65
|
+
ax (plt.Axes): The axis to which the source will be added.
|
|
72
66
|
"""
|
|
73
67
|
path = Path.make_compound_path(
|
|
74
68
|
Path(numpy.asarray(self.polygon.exterior.coords)[:, :2]),
|
|
75
|
-
*[Path(numpy.asarray(ring.coords)[:, :2]) for ring in self.polygon.interiors]
|
|
76
|
-
|
|
77
|
-
patch = PathPatch(path,
|
|
78
|
-
collection = PatchCollection([patch],
|
|
79
|
-
|
|
69
|
+
*[Path(numpy.asarray(ring.coords)[:, :2]) for ring in self.polygon.interiors]
|
|
70
|
+
)
|
|
71
|
+
patch = PathPatch(path, facecolor=self.facecolor, edgecolor=self.edgecolor, alpha=self.alpha)
|
|
72
|
+
collection = PatchCollection([patch], facecolor=self.facecolor, edgecolor=self.edgecolor, alpha=self.alpha)
|
|
80
73
|
ax.add_collection(collection, autolim=True)
|
|
81
74
|
ax.autoscale_view()
|
|
82
75
|
return collection
|
|
83
76
|
|
|
84
77
|
def plot(self) -> NoReturn:
|
|
78
|
+
"""
|
|
79
|
+
Plot the source on a 2D grid.
|
|
80
|
+
"""
|
|
85
81
|
figure, ax = plt.subplots(1, 1, figsize=(6, 6))
|
|
86
82
|
ax.set_title('FDTD Simulation')
|
|
87
83
|
ax.set_xlabel(r'x position [$\mu$m]')
|
|
88
|
-
ax.
|
|
84
|
+
ax.set_ylabel(r'y position [$\mu$m]')
|
|
89
85
|
ax.set_aspect('equal')
|
|
90
|
-
|
|
91
86
|
self.add_to_ax(ax)
|
|
92
|
-
ax.autoscale_view()
|
|
93
|
-
|
|
94
87
|
plt.show()
|
|
95
88
|
|
|
96
89
|
|
|
97
|
-
@dataclass(
|
|
90
|
+
@dataclass(config=config_dict)
|
|
98
91
|
class PointSource(BaseSource):
|
|
99
92
|
"""
|
|
100
93
|
Represents a point source in a 2D light wave simulation.
|
|
101
94
|
|
|
102
95
|
Attributes:
|
|
103
|
-
wavelength (float): Wavelength of the source.
|
|
104
|
-
position (
|
|
105
|
-
amplitude (float): Amplitude of the electric field
|
|
96
|
+
wavelength (Union[float, List[float], numpy.ndarray]): Wavelength of the source.
|
|
97
|
+
position (Tuple[Union[float, str], Union[float, str]]): Position (x, y) of the source.
|
|
98
|
+
amplitude (float): Amplitude of the electric field.
|
|
106
99
|
"""
|
|
107
100
|
wavelength: Union[float | List[float] | numpy.ndarray]
|
|
108
101
|
position: Tuple[float | str, float | str]
|
|
109
|
-
amplitude: float
|
|
110
|
-
facecolor: str = 'red'
|
|
111
|
-
edgecolor: str = 'red'
|
|
102
|
+
amplitude: float
|
|
112
103
|
|
|
113
104
|
def __post_init__(self):
|
|
114
105
|
self.wavelength = numpy.atleast_1d(self.wavelength)
|
|
@@ -144,22 +135,21 @@ class PointSource(BaseSource):
|
|
|
144
135
|
field[self.p0.x_index, self.p0.y_index] = self.amplitude / len(self.omega) * numpy.sin(self.omega * time).sum()
|
|
145
136
|
|
|
146
137
|
|
|
147
|
-
@dataclass(
|
|
138
|
+
@dataclass(config=config_dict)
|
|
148
139
|
class PointImpulsion(BaseSource):
|
|
149
140
|
"""
|
|
150
|
-
Represents a point source in a 2D light wave simulation.
|
|
141
|
+
Represents a point impulsion source in a 2D light wave simulation.
|
|
151
142
|
|
|
152
143
|
Attributes:
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
144
|
+
amplitude (float): Amplitude of the electric field.
|
|
145
|
+
duration (float): Duration of the impulsion.
|
|
146
|
+
position (Tuple[Union[float, str], Union[float, str]]): Position (x, y) of the source.
|
|
147
|
+
delay (float): Delay before the impulsion starts.
|
|
156
148
|
"""
|
|
149
|
+
amplitude: float
|
|
157
150
|
duration: float
|
|
158
151
|
position: Tuple[float | str, float | str]
|
|
159
152
|
delay: float = 0
|
|
160
|
-
amplitude: float = 1.0
|
|
161
|
-
facecolor: str = 'red'
|
|
162
|
-
edgecolor: str = 'red'
|
|
163
153
|
|
|
164
154
|
def __post_init__(self):
|
|
165
155
|
x, y = self.position
|
|
@@ -183,10 +173,10 @@ class PointImpulsion(BaseSource):
|
|
|
183
173
|
|
|
184
174
|
def add_source_to_field(self, field: numpy.ndarray, time: float) -> NoReturn:
|
|
185
175
|
"""
|
|
186
|
-
Add the source's effect to the simulation field.
|
|
176
|
+
Add the impulsion source's effect to the simulation field.
|
|
187
177
|
|
|
188
178
|
Args:
|
|
189
|
-
field (
|
|
179
|
+
field (np.ndarray): The simulation field to which the source's effect will be added.
|
|
190
180
|
time (float): The current simulation time.
|
|
191
181
|
"""
|
|
192
182
|
source_field = numpy.exp(-((time - self.delay) / self.duration) ** 2)
|
|
@@ -194,21 +184,21 @@ class PointImpulsion(BaseSource):
|
|
|
194
184
|
field[self.p0.x_index, self.p0.y_index] = self.amplitude * source_field
|
|
195
185
|
|
|
196
186
|
|
|
197
|
-
@dataclass(
|
|
187
|
+
@dataclass(config=config_dict)
|
|
198
188
|
class LineSource(BaseSource):
|
|
199
189
|
"""
|
|
200
190
|
Represents a line source in a 2D light wave simulation.
|
|
201
191
|
|
|
202
192
|
Attributes:
|
|
193
|
+
amplitude (float): Amplitude of the electric field.
|
|
203
194
|
wavelength (float): Wavelength of the source.
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
amplitude (float): Amplitude of the electric field, default is 1.0.
|
|
195
|
+
position_0 (Tuple[Union[float, str], Union[float, str]]): Starting position (x, y) of the source.
|
|
196
|
+
position_1 (Tuple[Union[float, str], Union[float, str]]): Ending position (x, y) of the source.
|
|
207
197
|
"""
|
|
198
|
+
amplitude: float
|
|
208
199
|
wavelength: float
|
|
209
200
|
position_0: Tuple[float | str, float | str]
|
|
210
201
|
position_1: Tuple[float | str, float | str]
|
|
211
|
-
amplitude: float = 1.0
|
|
212
202
|
|
|
213
203
|
def __post_init__(self):
|
|
214
204
|
self.frequency = Physics.c / self.wavelength
|
|
@@ -252,11 +242,85 @@ class LineSource(BaseSource):
|
|
|
252
242
|
|
|
253
243
|
def add_source_to_field(self, field: numpy.ndarray, time: float) -> NoReturn:
|
|
254
244
|
"""
|
|
255
|
-
Add the source's effect to the simulation field.
|
|
245
|
+
Add the line source's effect to the simulation field.
|
|
256
246
|
|
|
257
247
|
Args:
|
|
258
|
-
field (
|
|
248
|
+
field (np.ndarray): The simulation field to which the source's effect will be added.
|
|
259
249
|
time (float): The current simulation time.
|
|
260
250
|
"""
|
|
261
251
|
rows, cols = self.slice_indexes
|
|
262
252
|
field[rows, cols] = self.amplitude * numpy.sin(self.omega * time)
|
|
253
|
+
|
|
254
|
+
|
|
255
|
+
@dataclass(config=config_dict)
|
|
256
|
+
class LineImpulsion(BaseSource):
|
|
257
|
+
"""
|
|
258
|
+
Represents a line impulsion source in a 2D light wave simulation.
|
|
259
|
+
|
|
260
|
+
Attributes:
|
|
261
|
+
duration (float): Duration of the impulsion.
|
|
262
|
+
amplitude (float): Amplitude of the electric field.
|
|
263
|
+
position_0 (Tuple[Union[float, str], Union[float, str]]): Starting position (x, y) of the source.
|
|
264
|
+
position_1 (Tuple[Union[float, str], Union[float, str]]): Ending position (x, y) of the source.
|
|
265
|
+
delay (float): Delay before the impulsion starts.
|
|
266
|
+
"""
|
|
267
|
+
duration: float
|
|
268
|
+
amplitude: float
|
|
269
|
+
position_0: Tuple[float | str, float | str]
|
|
270
|
+
position_1: Tuple[float | str, float | str]
|
|
271
|
+
delay: float = 0
|
|
272
|
+
|
|
273
|
+
def __post_init__(self):
|
|
274
|
+
self.build_object()
|
|
275
|
+
|
|
276
|
+
def build_object(self) -> NoReturn:
|
|
277
|
+
"""
|
|
278
|
+
Build the line source object and calculate the line's coordinates.
|
|
279
|
+
"""
|
|
280
|
+
self.p0 = self.grid.get_coordinate(x=self.position_0[0], y=self.position_0[1])
|
|
281
|
+
self.p1 = self.grid.get_coordinate(x=self.position_1[0], y=self.position_1[1])
|
|
282
|
+
|
|
283
|
+
position = bresenham_line(
|
|
284
|
+
x0=self.p0.x_index,
|
|
285
|
+
y0=self.p0.y_index,
|
|
286
|
+
x1=self.p1.x_index,
|
|
287
|
+
y1=self.p1.y_index
|
|
288
|
+
)
|
|
289
|
+
|
|
290
|
+
rows, cols = zip(*position.T)
|
|
291
|
+
self.slice_indexes = rows, cols
|
|
292
|
+
|
|
293
|
+
p0 = geo.Point(self.p0.x, self.p0.y)
|
|
294
|
+
p1 = geo.Point(self.p1.x, self.p1.y)
|
|
295
|
+
self.polygon = geo.LineString((p0, p1))
|
|
296
|
+
|
|
297
|
+
def add_to_ax(self, ax: plt.axis) -> NoReturn:
|
|
298
|
+
"""
|
|
299
|
+
Add the scatterer to the provided axis as a circle.
|
|
300
|
+
|
|
301
|
+
Args:
|
|
302
|
+
ax (Axis): The axis to which the scatterer will be added.
|
|
303
|
+
"""
|
|
304
|
+
ax.plot(
|
|
305
|
+
self.polygon.xy[0],
|
|
306
|
+
self.polygon.xy[1],
|
|
307
|
+
color=self.facecolor,
|
|
308
|
+
label='source'
|
|
309
|
+
)
|
|
310
|
+
|
|
311
|
+
def add_source_to_field(self, field: numpy.ndarray, time: float) -> NoReturn:
|
|
312
|
+
"""
|
|
313
|
+
Add the line impulsion source's effect to the simulation field.
|
|
314
|
+
|
|
315
|
+
Args:
|
|
316
|
+
field (np.ndarray): The simulation field to which the source's effect will be added.
|
|
317
|
+
time (float): The current simulation time.
|
|
318
|
+
"""
|
|
319
|
+
rows, cols = self.slice_indexes
|
|
320
|
+
|
|
321
|
+
source_field = numpy.exp(-((time - self.delay) / self.duration) ** 2)
|
|
322
|
+
|
|
323
|
+
field[rows, cols] = self.amplitude * source_field
|
|
324
|
+
|
|
325
|
+
|
|
326
|
+
# -
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Experiment: Circular Scatterer
|
|
3
|
+
==============================
|
|
4
|
+
|
|
5
|
+
This example demonstrates the setup and execution of a circular scatterer experiment using LightWave2D.
|
|
6
|
+
We will define the simulation grid, add a circular scatterer and a line source, apply a perfectly matched layer (PML), run the simulation, and visualize the results.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
# %%
|
|
10
|
+
# Importing the necessary packages
|
|
11
|
+
from LightWave2D.grid import Grid
|
|
12
|
+
from LightWave2D.experiment import Experiment
|
|
13
|
+
from MPSPlots import colormaps
|
|
14
|
+
|
|
15
|
+
# %%
|
|
16
|
+
# Define the simulation grid
|
|
17
|
+
grid = Grid(
|
|
18
|
+
resolution=0.1e-6, # Grid resolution in meters
|
|
19
|
+
size_x=32e-6, # Grid size in the x direction in meters
|
|
20
|
+
size_y=20e-6, # Grid size in the y direction in meters
|
|
21
|
+
n_steps=300 # Number of time steps for the simulation
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
# Initialize the experiment with the defined grid
|
|
25
|
+
experiment = Experiment(grid=grid)
|
|
26
|
+
|
|
27
|
+
# %%
|
|
28
|
+
# Add a circular scatterer to the experiment
|
|
29
|
+
scatterer = experiment.add_circle(
|
|
30
|
+
position=('30%', '50%'), # Center position of the scatterer
|
|
31
|
+
epsilon_r=1.5, # Relative permittivity of the scatterer
|
|
32
|
+
radius=3e-6, # Radius of the circular scatterer in meters
|
|
33
|
+
# sigma=1e6
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
# %%
|
|
37
|
+
# Add a line source to the experiment
|
|
38
|
+
source = experiment.add_line_source(
|
|
39
|
+
wavelength=1550e-9, # Wavelength of the source in meters
|
|
40
|
+
position_0=('10%', '80%'), # Starting position of the source
|
|
41
|
+
position_1=('10%', '20%'), # Ending position of the source
|
|
42
|
+
amplitude=10 # Amplitude of the source
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
# %%
|
|
46
|
+
# Add a perfectly matched layer (PML) to absorb boundary reflections
|
|
47
|
+
experiment.add_pml(
|
|
48
|
+
order=1, # Order of the PML polynomial profile
|
|
49
|
+
width='10%', # Width of the PML region as a percentage of grid size
|
|
50
|
+
sigma_max=5000 # Maximum conductivity for the PML
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
# %%
|
|
54
|
+
# Plot the entire experiment setup
|
|
55
|
+
experiment.plot()
|
|
56
|
+
|
|
57
|
+
# Run the FDTD simulation
|
|
58
|
+
experiment.run_fdtd()
|
|
59
|
+
|
|
60
|
+
# %%
|
|
61
|
+
# Plot the last time frame of the computed fields
|
|
62
|
+
experiment.plot_frame(
|
|
63
|
+
frame_number=-1, # Plot the last frame
|
|
64
|
+
scale_max=1, # Maximum scale for the field visualization
|
|
65
|
+
colormap=colormaps.polytechnique.red_black_blue, # Colormap for the plot
|
|
66
|
+
# show_intensity=True
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
# %%
|
|
70
|
+
# Render an animation of the field propagation over time
|
|
71
|
+
animation = experiment.render_propagation(
|
|
72
|
+
skip_frame=5, # Number of frames to skip in the animation
|
|
73
|
+
unit_size=5, # Size of each unit in the animation
|
|
74
|
+
colormap=colormaps.polytechnique.red_black_blue, # Colormap for the animation
|
|
75
|
+
scale_max=1,
|
|
76
|
+
auto_adjust_clim=True
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
# Save the animation as a GIF file
|
|
80
|
+
animation.save('./circular_scatterer_propagation.gif', writer='Pillow', fps=10)
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
0.0.7
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -1,82 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
Experiment: elliptic scatterer
|
|
3
|
-
==============================
|
|
4
|
-
|
|
5
|
-
"""
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
# %%
|
|
9
|
-
# Importing the package
|
|
10
|
-
from LightWave2D.grid import Grid
|
|
11
|
-
from LightWave2D.experiment import Experiment
|
|
12
|
-
from MPSPlots import colormaps
|
|
13
|
-
|
|
14
|
-
# %%
|
|
15
|
-
# We define here the grid on which to build the experiemnt
|
|
16
|
-
grid = Grid(
|
|
17
|
-
resolution=0.05e-6,
|
|
18
|
-
size_x=50e-6,
|
|
19
|
-
size_y=15e-6,
|
|
20
|
-
n_steps=1200
|
|
21
|
-
)
|
|
22
|
-
|
|
23
|
-
experiment = Experiment(grid=grid)
|
|
24
|
-
|
|
25
|
-
# scatterer = experiment.add_ellipse(
|
|
26
|
-
# position=('30%', '40%'),
|
|
27
|
-
# width=4e-6,
|
|
28
|
-
# height=4e-6,
|
|
29
|
-
# epsilon_r=1,
|
|
30
|
-
# )
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
scatterer = experiment.add_waveguide(
|
|
34
|
-
position_0=('0%', '50%'),
|
|
35
|
-
position_1=('100%', '50%'),
|
|
36
|
-
width=2e-6,
|
|
37
|
-
epsilon_r=2,
|
|
38
|
-
)
|
|
39
|
-
|
|
40
|
-
source = experiment.add_line_source(
|
|
41
|
-
wavelength=1550e-9,
|
|
42
|
-
position_0=('20%', '45%'),
|
|
43
|
-
position_1=('20%', '55%'),
|
|
44
|
-
amplitude=10,
|
|
45
|
-
)
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
# source = experiment.add_impulsion(
|
|
49
|
-
# duration=1e-15,
|
|
50
|
-
# delay=0,
|
|
51
|
-
# position=('50%', '50%'),
|
|
52
|
-
# amplitude=1000,
|
|
53
|
-
# )
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
# %%
|
|
57
|
-
# We add a perfectly matched layer to avoid reflection at the boundary of the mesh
|
|
58
|
-
experiment.add_pml(order=1, width='10%', sigma_max=5000)
|
|
59
|
-
|
|
60
|
-
experiment.run_fdtd()
|
|
61
|
-
|
|
62
|
-
experiment.plot()
|
|
63
|
-
|
|
64
|
-
# %%
|
|
65
|
-
# Plotting the last time frame of the computed fields
|
|
66
|
-
# experiment.plot_frame(
|
|
67
|
-
# frame_number=-1,
|
|
68
|
-
# colormap=colormaps.polytechnique.red_black_blue,
|
|
69
|
-
# scale_max=1
|
|
70
|
-
# )
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
animation = experiment.render_propagation(
|
|
74
|
-
skip_frame=10,
|
|
75
|
-
colormap=colormaps.polytechnique.red_black_blue,
|
|
76
|
-
scale_max=2
|
|
77
|
-
)
|
|
78
|
-
|
|
79
|
-
_ = animation.save('./resonator.gif', writer='Pillow', fps=20)
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
# -
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|