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.
Files changed (37) hide show
  1. lightwave2d-0.0.8/LightWave2D/VERSION +1 -0
  2. lightwave2d-0.0.8/LightWave2D/__pycache__/__init__.cpython-311.pyc +0 -0
  3. {lightwave2d-0.0.7 → lightwave2d-0.0.8}/LightWave2D/__pycache__/components.cpython-311.pyc +0 -0
  4. {lightwave2d-0.0.7 → lightwave2d-0.0.8}/LightWave2D/__pycache__/detector.cpython-311.pyc +0 -0
  5. lightwave2d-0.0.8/LightWave2D/__pycache__/experiment.cpython-311.pyc +0 -0
  6. {lightwave2d-0.0.7 → lightwave2d-0.0.8}/LightWave2D/__pycache__/grid.cpython-311.pyc +0 -0
  7. {lightwave2d-0.0.7 → lightwave2d-0.0.8}/LightWave2D/__pycache__/physics.cpython-311.pyc +0 -0
  8. {lightwave2d-0.0.7 → lightwave2d-0.0.8}/LightWave2D/__pycache__/pml.cpython-311.pyc +0 -0
  9. lightwave2d-0.0.8/LightWave2D/__pycache__/source.cpython-311.pyc +0 -0
  10. {lightwave2d-0.0.7 → lightwave2d-0.0.8}/LightWave2D/__pycache__/utils.cpython-311.pyc +0 -0
  11. {lightwave2d-0.0.7 → lightwave2d-0.0.8}/LightWave2D/components.py +11 -12
  12. {lightwave2d-0.0.7 → lightwave2d-0.0.8}/LightWave2D/experiment.py +45 -38
  13. {lightwave2d-0.0.7 → lightwave2d-0.0.8}/LightWave2D/source.py +115 -51
  14. {lightwave2d-0.0.7 → lightwave2d-0.0.8}/LightWave2D.egg-info/PKG-INFO +1 -1
  15. {lightwave2d-0.0.7 → lightwave2d-0.0.8}/PKG-INFO +1 -1
  16. lightwave2d-0.0.8/developments/testings_0.py +80 -0
  17. lightwave2d-0.0.7/LightWave2D/VERSION +0 -1
  18. lightwave2d-0.0.7/LightWave2D/__pycache__/__init__.cpython-311.pyc +0 -0
  19. lightwave2d-0.0.7/LightWave2D/__pycache__/experiment.cpython-311.pyc +0 -0
  20. lightwave2d-0.0.7/LightWave2D/__pycache__/source.cpython-311.pyc +0 -0
  21. lightwave2d-0.0.7/developments/testings_0.py +0 -82
  22. {lightwave2d-0.0.7 → lightwave2d-0.0.8}/LICENSE +0 -0
  23. {lightwave2d-0.0.7 → lightwave2d-0.0.8}/LightWave2D/__init__.py +0 -0
  24. {lightwave2d-0.0.7 → lightwave2d-0.0.8}/LightWave2D/detector.py +0 -0
  25. {lightwave2d-0.0.7 → lightwave2d-0.0.8}/LightWave2D/directories.py +0 -0
  26. {lightwave2d-0.0.7 → lightwave2d-0.0.8}/LightWave2D/grid.py +0 -0
  27. {lightwave2d-0.0.7 → lightwave2d-0.0.8}/LightWave2D/physics.py +0 -0
  28. {lightwave2d-0.0.7 → lightwave2d-0.0.8}/LightWave2D/pml.py +0 -0
  29. {lightwave2d-0.0.7 → lightwave2d-0.0.8}/LightWave2D/utils.py +0 -0
  30. {lightwave2d-0.0.7 → lightwave2d-0.0.8}/LightWave2D.egg-info/SOURCES.txt +0 -0
  31. {lightwave2d-0.0.7 → lightwave2d-0.0.8}/LightWave2D.egg-info/dependency_links.txt +0 -0
  32. {lightwave2d-0.0.7 → lightwave2d-0.0.8}/LightWave2D.egg-info/requires.txt +0 -0
  33. {lightwave2d-0.0.7 → lightwave2d-0.0.8}/LightWave2D.egg-info/top_level.txt +0 -0
  34. {lightwave2d-0.0.7 → lightwave2d-0.0.8}/MANIFEST.in +0 -0
  35. {lightwave2d-0.0.7 → lightwave2d-0.0.8}/README.rst +0 -0
  36. {lightwave2d-0.0.7 → lightwave2d-0.0.8}/setup.cfg +0 -0
  37. {lightwave2d-0.0.7 → lightwave2d-0.0.8}/setup.py +0 -0
@@ -0,0 +1 @@
1
+ 0.0.8
@@ -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(np.asarray(self.polygon.exterior.coords)[:, :2]),
84
- *[Path(np.asarray(ring.coords)[:, :2]) for ring in self.polygon.interiors])
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 (np.ndarray): The permittivity mesh to be updated.
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 (np.ndarray): The permittivity mesh to be updated.
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 (np.ndarray): The field to which non-linear effects will be added.
129
+ field (numpy.ndarray): The field to which non-linear effects will be added.
132
130
  """
133
- return
134
- chi_2 = 0
135
-
136
- field += self.idx * self.grid.dt**2 / (self.epsilon_r_mesh * Physics.epsilon_0 * Physics.mu_0) * chi_2 * field ** 2
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 = (np.sqrt(3) / 2) * self.side_length
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.components import Circle, Square, Ellipse, Triangle, Lense, Grating, RingResonator, Waveguide
10
- from LightWave2D.source import PointSource, LineSource, PointImpulsion
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) -> Square:
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) -> Square:
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) -> Square:
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) -> Square:
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 source in self.sources:
282
- source.add_source_to_field(Ez, time=t)
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
- Represents an elliptical scatterer in a simulation grid.
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
- angle (float): Rotation angle of the ellipse.
35
- facecolor (str): Color of the scatterer face.
36
- edgecolor (str): Color of the scatterer edge.
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
- x0, y0 = self.position
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 permittivity mesh for the square scatterer.
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
- self.idx = self.path.contains_points(coordinates).astype(bool).reshape(self.epsilon_r_mesh.shape)
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 scatterer to the provided axis as a circle.
62
+ Add the source to the provided axis as a scatter point or patch.
69
63
 
70
64
  Args:
71
- ax (Axis): The axis to which the scatterer will be added.
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, color=self.facecolor, edgecolor=self.edgecolor, alpha=0.4)
78
- collection = PatchCollection([patch], color=self.facecolor, edgecolor=self.edgecolor, alpha=self.alpha)
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.set_xlabel(r'y position [$\mu$m]')
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(kw_only=True, config=config_dict)
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 (tuple): Position (x, y) of the source.
105
- amplitude (float): Amplitude of the electric field, default is 1.0.
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 = 1.0
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(kw_only=True, config=config_dict)
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
- wavelength (float): Wavelength of the source.
154
- position (tuple): Position (x, y) of the source.
155
- amplitude (float): Amplitude of the electric field, default is 1.0.
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 (numpy.ndarray): The simulation field to which the source's effect will be added.
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(kw_only=True, config=config_dict)
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
- point_0 (Tuple[float, float]): Starting position (x, y) of the source.
205
- point_1 (Tuple[float, float]): Ending position (x, y) of the source.
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 (numpy.ndarray): The simulation field to which the source's effect will be added.
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
+ # -
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: LightWave2D
3
- Version: 0.0.7
3
+ Version: 0.0.8
4
4
  Summary: A package for finite-difference time domain computation for light propagation.
5
5
  Home-page: https://github.com/MartinPdeS/LightWave2D
6
6
  Author: Martin Poinsinet de Sivry
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: LightWave2D
3
- Version: 0.0.7
3
+ Version: 0.0.8
4
4
  Summary: A package for finite-difference time domain computation for light propagation.
5
5
  Home-page: https://github.com/MartinPdeS/LightWave2D
6
6
  Author: Martin Poinsinet de Sivry
@@ -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
@@ -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