LightWave2D 0.0.3__tar.gz → 0.0.4__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.4/LightWave2D/VERSION +1 -0
- lightwave2d-0.0.4/LightWave2D/__pycache__/__init__.cpython-311.pyc +0 -0
- lightwave2d-0.0.4/LightWave2D/__pycache__/detector.cpython-311.pyc +0 -0
- lightwave2d-0.0.4/LightWave2D/__pycache__/experiment.cpython-311.pyc +0 -0
- {lightwave2d-0.0.3 → lightwave2d-0.0.4}/LightWave2D/__pycache__/grid.cpython-311.pyc +0 -0
- {lightwave2d-0.0.3 → lightwave2d-0.0.4}/LightWave2D/__pycache__/physics.cpython-311.pyc +0 -0
- {lightwave2d-0.0.3 → lightwave2d-0.0.4}/LightWave2D/__pycache__/pml.cpython-311.pyc +0 -0
- lightwave2d-0.0.4/LightWave2D/__pycache__/source.cpython-311.pyc +0 -0
- lightwave2d-0.0.4/LightWave2D/__pycache__/utils.cpython-311.pyc +0 -0
- lightwave2d-0.0.4/LightWave2D/components/__init__.py +5 -0
- lightwave2d-0.0.4/LightWave2D/components/__pycache__/__init__.cpython-311.pyc +0 -0
- lightwave2d-0.0.4/LightWave2D/components/__pycache__/base_class.cpython-311.pyc +0 -0
- lightwave2d-0.0.4/LightWave2D/components/__pycache__/basic.cpython-311.pyc +0 -0
- lightwave2d-0.0.4/LightWave2D/components/__pycache__/complex.cpython-311.pyc +0 -0
- {lightwave2d-0.0.3 → lightwave2d-0.0.4}/LightWave2D/components/base_class.py +50 -11
- lightwave2d-0.0.4/LightWave2D/components/basic.py +133 -0
- lightwave2d-0.0.4/LightWave2D/components/complex.py +105 -0
- {lightwave2d-0.0.3 → lightwave2d-0.0.4}/LightWave2D/detector.py +2 -5
- {lightwave2d-0.0.3 → lightwave2d-0.0.4}/LightWave2D/experiment.py +41 -38
- {lightwave2d-0.0.3 → lightwave2d-0.0.4}/LightWave2D/pml.py +1 -1
- lightwave2d-0.0.4/LightWave2D/source.py +130 -0
- lightwave2d-0.0.4/LightWave2D/utils.py +45 -0
- {lightwave2d-0.0.3 → lightwave2d-0.0.4/LightWave2D.egg-info}/PKG-INFO +40 -3
- {lightwave2d-0.0.3 → lightwave2d-0.0.4}/LightWave2D.egg-info/SOURCES.txt +4 -2
- {lightwave2d-0.0.3/LightWave2D.egg-info → lightwave2d-0.0.4}/PKG-INFO +40 -3
- lightwave2d-0.0.4/README.rst +47 -0
- lightwave2d-0.0.4/developments/testings_0.py +127 -0
- lightwave2d-0.0.3/LightWave2D/VERSION +0 -1
- lightwave2d-0.0.3/LightWave2D/__pycache__/__init__.cpython-311.pyc +0 -0
- lightwave2d-0.0.3/LightWave2D/__pycache__/detector.cpython-311.pyc +0 -0
- lightwave2d-0.0.3/LightWave2D/__pycache__/experiment.cpython-311.pyc +0 -0
- lightwave2d-0.0.3/LightWave2D/__pycache__/source.cpython-311.pyc +0 -0
- lightwave2d-0.0.3/LightWave2D/__pycache__/utils.cpython-311.pyc +0 -0
- lightwave2d-0.0.3/LightWave2D/components/__init__.py +0 -4
- lightwave2d-0.0.3/LightWave2D/components/__pycache__/__init__.cpython-311.pyc +0 -0
- lightwave2d-0.0.3/LightWave2D/components/__pycache__/base_class.cpython-311.pyc +0 -0
- lightwave2d-0.0.3/LightWave2D/components/__pycache__/scatterers.cpython-311.pyc +0 -0
- lightwave2d-0.0.3/LightWave2D/components/scatterers.py +0 -185
- lightwave2d-0.0.3/LightWave2D/source.py +0 -119
- lightwave2d-0.0.3/LightWave2D/utils.py +0 -95
- lightwave2d-0.0.3/README.rst +0 -10
- lightwave2d-0.0.3/developments/testings_0.py +0 -87
- {lightwave2d-0.0.3 → lightwave2d-0.0.4}/LICENSE +0 -0
- {lightwave2d-0.0.3 → lightwave2d-0.0.4}/LightWave2D/__init__.py +0 -0
- {lightwave2d-0.0.3 → lightwave2d-0.0.4}/LightWave2D/directories.py +0 -0
- {lightwave2d-0.0.3 → lightwave2d-0.0.4}/LightWave2D/grid.py +0 -0
- {lightwave2d-0.0.3 → lightwave2d-0.0.4}/LightWave2D/physics.py +0 -0
- {lightwave2d-0.0.3 → lightwave2d-0.0.4}/LightWave2D.egg-info/dependency_links.txt +0 -0
- {lightwave2d-0.0.3 → lightwave2d-0.0.4}/LightWave2D.egg-info/requires.txt +0 -0
- {lightwave2d-0.0.3 → lightwave2d-0.0.4}/LightWave2D.egg-info/top_level.txt +0 -0
- {lightwave2d-0.0.3 → lightwave2d-0.0.4}/MANIFEST.in +0 -0
- {lightwave2d-0.0.3 → lightwave2d-0.0.4}/setup.cfg +0 -0
- {lightwave2d-0.0.3 → lightwave2d-0.0.4}/setup.py +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
0.0.4
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -2,15 +2,48 @@
|
|
|
2
2
|
# -*- coding: utf-8 -*-
|
|
3
3
|
|
|
4
4
|
from typing import NoReturn
|
|
5
|
-
|
|
5
|
+
from pydantic.dataclasses import dataclass
|
|
6
6
|
import numpy
|
|
7
7
|
from LightWave2D.physics import Physics
|
|
8
8
|
import matplotlib.pyplot as plt
|
|
9
9
|
from matplotlib.patches import PathPatch
|
|
10
|
+
import matplotlib as mpl
|
|
11
|
+
import numpy as np
|
|
12
|
+
from matplotlib.path import Path
|
|
13
|
+
from matplotlib.collections import PatchCollection
|
|
14
|
+
from LightWave2D.grid import Grid
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
# Configuration dictionary for dataclasses
|
|
18
|
+
config_dict = {
|
|
19
|
+
'kw_only': True,
|
|
20
|
+
'extra': 'forbid',
|
|
21
|
+
'slots': True,
|
|
22
|
+
'arbitrary_types_allowed': True
|
|
23
|
+
}
|
|
10
24
|
|
|
11
25
|
|
|
26
|
+
@dataclass(kw_only=True, config=config_dict)
|
|
12
27
|
class BaseComponent():
|
|
28
|
+
"""
|
|
29
|
+
Represents an elliptical scatterer in a simulation grid.
|
|
30
|
+
|
|
31
|
+
Attributes:
|
|
32
|
+
grid (Grid): The grid of the simulation mesh.
|
|
33
|
+
angle (float): Rotation angle of the ellipse.
|
|
34
|
+
facecolor (str): Color of the scatterer face.
|
|
35
|
+
edgecolor (str): Color of the scatterer edge.
|
|
36
|
+
"""
|
|
37
|
+
grid: Grid
|
|
38
|
+
facecolor: str = 'lightblue'
|
|
39
|
+
edgecolor: str = 'blue'
|
|
40
|
+
alpha: float = 0.3
|
|
41
|
+
rotation: float = 0
|
|
42
|
+
|
|
13
43
|
def __post_init__(self):
|
|
44
|
+
x0, y0 = self.position
|
|
45
|
+
self.coordinate = self.grid.get_coordinate(x=x0, y=y0)
|
|
46
|
+
|
|
14
47
|
self.build_object()
|
|
15
48
|
|
|
16
49
|
def build_object(self) -> NoReturn:
|
|
@@ -19,7 +52,11 @@ class BaseComponent():
|
|
|
19
52
|
"""
|
|
20
53
|
self.epsilon_r_mesh = numpy.ones(self.grid.shape) # Background permittivity
|
|
21
54
|
|
|
22
|
-
self.
|
|
55
|
+
self.compute_polygon()
|
|
56
|
+
|
|
57
|
+
self.path = Path(self.polygon.exterior.coords)
|
|
58
|
+
|
|
59
|
+
self.path = self.path.transformed(mpl.transforms.Affine2D().rotate_around(self.coordinate.x, self.coordinate.y, self.rotation))
|
|
23
60
|
|
|
24
61
|
x_mesh, y_mesh = numpy.meshgrid(self.grid.x_stamp, self.grid.y_stamp)
|
|
25
62
|
|
|
@@ -29,21 +66,23 @@ class BaseComponent():
|
|
|
29
66
|
|
|
30
67
|
self.epsilon_r_mesh[self.idx] = self.epsilon_r
|
|
31
68
|
|
|
32
|
-
def add_to_ax(self, ax: plt.axis) ->
|
|
69
|
+
def add_to_ax(self, ax: plt.axis) -> PatchCollection:
|
|
33
70
|
"""
|
|
34
71
|
Add the scatterer to the provided axis as a circle.
|
|
35
72
|
|
|
36
73
|
Args:
|
|
37
74
|
ax (Axis): The axis to which the scatterer will be added.
|
|
38
75
|
"""
|
|
39
|
-
|
|
40
|
-
self.
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
)
|
|
45
|
-
|
|
46
|
-
|
|
76
|
+
path = Path.make_compound_path(
|
|
77
|
+
Path(np.asarray(self.polygon.exterior.coords)[:, :2]),
|
|
78
|
+
*[Path(np.asarray(ring.coords)[:, :2]) for ring in self.polygon.interiors])
|
|
79
|
+
|
|
80
|
+
patch = PathPatch(path, color=self.facecolor, edgecolor=self.edgecolor, alpha=0.4)
|
|
81
|
+
collection = PatchCollection([patch], color=self.facecolor, edgecolor=self.edgecolor, alpha=self.alpha)
|
|
82
|
+
|
|
83
|
+
ax.add_collection(collection, autolim=True)
|
|
84
|
+
ax.autoscale_view()
|
|
85
|
+
return collection
|
|
47
86
|
|
|
48
87
|
def plot(self) -> NoReturn:
|
|
49
88
|
figure, ax = plt.subplots(1, 1, figsize=(6, 6))
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
|
|
4
|
+
from typing import Tuple, Union, NoReturn
|
|
5
|
+
from pydantic.dataclasses import dataclass
|
|
6
|
+
import numpy as np
|
|
7
|
+
from LightWave2D.grid import Grid
|
|
8
|
+
from LightWave2D.components.base_class import BaseComponent
|
|
9
|
+
from matplotlib.path import Path
|
|
10
|
+
import shapely.geometry as geo
|
|
11
|
+
from matplotlib import patches
|
|
12
|
+
from shapely.affinity import scale
|
|
13
|
+
|
|
14
|
+
# Configuration dictionary for dataclasses
|
|
15
|
+
config_dict = {
|
|
16
|
+
'kw_only': True,
|
|
17
|
+
'extra': 'forbid',
|
|
18
|
+
'slots': True,
|
|
19
|
+
'arbitrary_types_allowed': True
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
@dataclass(config=config_dict)
|
|
24
|
+
class Square(BaseComponent):
|
|
25
|
+
"""
|
|
26
|
+
Represents a square scatterer in a simulation grid.
|
|
27
|
+
|
|
28
|
+
Attributes:
|
|
29
|
+
position (Tuple[Union[float, str], Union[float, str]]): Starting position of the scatterer (center of the square).
|
|
30
|
+
epsilon_r (float): Relative permittivity inside the scatterer.
|
|
31
|
+
side_length (float): Side length of the square scatterer.
|
|
32
|
+
"""
|
|
33
|
+
position: Tuple[Union[float, str], Union[float, str]]
|
|
34
|
+
epsilon_r: float
|
|
35
|
+
side_length: float
|
|
36
|
+
|
|
37
|
+
def compute_polygon(self) -> Path:
|
|
38
|
+
"""
|
|
39
|
+
Compute the path of the square scatterer.
|
|
40
|
+
"""
|
|
41
|
+
half_side = self.side_length / 2
|
|
42
|
+
x_start, x_end = self.coordinate.x - half_side, self.coordinate.x + half_side
|
|
43
|
+
y_start, y_end = self.coordinate.y - half_side, self.coordinate.y + half_side
|
|
44
|
+
|
|
45
|
+
self.polygon = geo.Polygon([
|
|
46
|
+
(x_start, y_start),
|
|
47
|
+
(x_start, y_end),
|
|
48
|
+
(x_end, y_end),
|
|
49
|
+
(x_end, y_start)
|
|
50
|
+
])
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
@dataclass(config=config_dict)
|
|
54
|
+
class Circle(BaseComponent):
|
|
55
|
+
"""
|
|
56
|
+
Represents a circular scatterer in a simulation grid.
|
|
57
|
+
|
|
58
|
+
Attributes:
|
|
59
|
+
position (Tuple[Union[float, str], Union[float, str]]): Starting position of the scatterer.
|
|
60
|
+
epsilon_r (float): Relative permittivity inside the scatterer.
|
|
61
|
+
radius (float): Radius of the scatterer.
|
|
62
|
+
"""
|
|
63
|
+
position: Tuple[Union[float, str], Union[float, str]]
|
|
64
|
+
epsilon_r: float
|
|
65
|
+
radius: float
|
|
66
|
+
|
|
67
|
+
def compute_polygon(self) -> Path:
|
|
68
|
+
"""
|
|
69
|
+
Compute the path of the circular scatterer.
|
|
70
|
+
"""
|
|
71
|
+
self.polygon = geo.Point(self.coordinate.x, self.coordinate.y).buffer(self.radius)
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
@dataclass(config=config_dict)
|
|
75
|
+
class Triangle(BaseComponent):
|
|
76
|
+
"""
|
|
77
|
+
Represents a triangular scatterer in a simulation grid.
|
|
78
|
+
|
|
79
|
+
Attributes:
|
|
80
|
+
position (Tuple[Union[float, str], Union[float, str]]): Starting position of the scatterer (center of the triangle).
|
|
81
|
+
epsilon_r (float): Relative permittivity inside the scatterer.
|
|
82
|
+
side_length (float): Side length of the triangle.
|
|
83
|
+
"""
|
|
84
|
+
position: Tuple[Union[float, str], Union[float, str]]
|
|
85
|
+
epsilon_r: float
|
|
86
|
+
side_length: float
|
|
87
|
+
|
|
88
|
+
def compute_polygon(self) -> NoReturn:
|
|
89
|
+
"""
|
|
90
|
+
Compute the path of the triangular scatterer.
|
|
91
|
+
"""
|
|
92
|
+
height = (np.sqrt(3) / 2) * self.side_length
|
|
93
|
+
|
|
94
|
+
# Vertices of an equilateral triangle centered at (coordinate.x, coordinate.y)
|
|
95
|
+
p0 = (self.coordinate.x - self.side_length / 2, self.coordinate.y - height / 3)
|
|
96
|
+
p1 = (self.coordinate.x + self.side_length / 2, self.coordinate.y - height / 3)
|
|
97
|
+
p2 = (self.coordinate.x, self.coordinate.y + 2 * height / 3)
|
|
98
|
+
|
|
99
|
+
self.polygon = geo.Polygon([p0, p1, p2])
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
@dataclass(config=config_dict)
|
|
103
|
+
class Ellipse(BaseComponent):
|
|
104
|
+
"""
|
|
105
|
+
Represents an elliptical scatterer in a simulation grid.
|
|
106
|
+
|
|
107
|
+
Attributes:
|
|
108
|
+
position (Tuple[Union[float, str], Union[float, str]]): Starting position of the scatterer.
|
|
109
|
+
width (float): Width of the ellipse.
|
|
110
|
+
height (float): Height of the ellipse.
|
|
111
|
+
epsilon_r (float): Relative permittivity inside the scatterer.
|
|
112
|
+
resolution (float): Resolution of the ellipse.
|
|
113
|
+
"""
|
|
114
|
+
position: Tuple[Union[float, str], Union[float, str]]
|
|
115
|
+
width: float
|
|
116
|
+
height: float
|
|
117
|
+
epsilon_r: float
|
|
118
|
+
resolution: float = 100
|
|
119
|
+
|
|
120
|
+
def compute_polygon(self) -> Path:
|
|
121
|
+
"""
|
|
122
|
+
Compute the path of the elliptical scatterer.
|
|
123
|
+
"""
|
|
124
|
+
ellipse_patch = patches.Ellipse(
|
|
125
|
+
(self.coordinate.x, self.coordinate.y),
|
|
126
|
+
width=self.resolution,
|
|
127
|
+
height=self.resolution * self.height / self.width,
|
|
128
|
+
)
|
|
129
|
+
|
|
130
|
+
vertices = ellipse_patch.get_verts()
|
|
131
|
+
polygon = geo.Polygon(vertices)
|
|
132
|
+
|
|
133
|
+
self.polygon = scale(polygon, xfact=self.width / self.resolution, yfact=self.width / self.resolution)
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
|
|
4
|
+
from typing import Tuple, Union, NoReturn
|
|
5
|
+
from pydantic.dataclasses import dataclass
|
|
6
|
+
from LightWave2D.grid import Grid
|
|
7
|
+
from LightWave2D.components.base_class import BaseComponent
|
|
8
|
+
from matplotlib.path import Path
|
|
9
|
+
import shapely.geometry as geo
|
|
10
|
+
|
|
11
|
+
# Configuration dictionary for dataclasses
|
|
12
|
+
config_dict = {
|
|
13
|
+
'kw_only': True,
|
|
14
|
+
'extra': 'forbid',
|
|
15
|
+
'slots': True,
|
|
16
|
+
'arbitrary_types_allowed': True
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
@dataclass(config=config_dict)
|
|
21
|
+
class Grating(BaseComponent):
|
|
22
|
+
"""
|
|
23
|
+
Represents a grating in a simulation grid.
|
|
24
|
+
|
|
25
|
+
Attributes:
|
|
26
|
+
position (Tuple[Union[float, str], Union[float, str]]): Starting position of the grating.
|
|
27
|
+
epsilon_r (float): Relative permittivity inside the grating.
|
|
28
|
+
period (float): Period of the grating.
|
|
29
|
+
duty_cycle (float): Duty cycle of the grating (fraction of period occupied by the grating material).
|
|
30
|
+
num_periods (int): Number of grating periods.
|
|
31
|
+
"""
|
|
32
|
+
position: Tuple[Union[float, str], Union[float, str]]
|
|
33
|
+
epsilon_r: float
|
|
34
|
+
period: float
|
|
35
|
+
duty_cycle: float
|
|
36
|
+
num_periods: int
|
|
37
|
+
|
|
38
|
+
def compute_polygon(self) -> NoReturn:
|
|
39
|
+
"""
|
|
40
|
+
Compute the path of the grating.
|
|
41
|
+
"""
|
|
42
|
+
bars = []
|
|
43
|
+
for i in range(self.num_periods):
|
|
44
|
+
x_start = self.coordinate.x + i * self.period
|
|
45
|
+
x_end = x_start + self.duty_cycle * self.period
|
|
46
|
+
bar = geo.box(x_start, self.coordinate.y - 0.5, x_end, self.coordinate.y + 0.5)
|
|
47
|
+
bars.append(bar)
|
|
48
|
+
|
|
49
|
+
self.polygon = geo.MultiPolygon(bars)
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
@dataclass(config=config_dict)
|
|
53
|
+
class RingResonator(BaseComponent):
|
|
54
|
+
"""
|
|
55
|
+
Represents a ring resonator in a simulation grid.
|
|
56
|
+
|
|
57
|
+
Attributes:
|
|
58
|
+
position (Tuple[Union[float, str], Union[float, str]]): Starting position of the resonator (center of the ring).
|
|
59
|
+
epsilon_r (float): Relative permittivity inside the resonator.
|
|
60
|
+
inner_radius (float): Inner radius of the ring.
|
|
61
|
+
width (float): Outer radius of the ring.
|
|
62
|
+
"""
|
|
63
|
+
grid: Grid
|
|
64
|
+
position: Tuple[Union[float, str], Union[float, str]]
|
|
65
|
+
epsilon_r: float
|
|
66
|
+
inner_radius: float
|
|
67
|
+
width: float
|
|
68
|
+
|
|
69
|
+
def compute_polygon(self) -> Path:
|
|
70
|
+
"""
|
|
71
|
+
Compute the path of the ring resonator.
|
|
72
|
+
"""
|
|
73
|
+
inner_circle = geo.Point(self.coordinate.x, self.coordinate.y).buffer(self.inner_radius)
|
|
74
|
+
outer_circle = geo.Point(self.coordinate.x, self.coordinate.y).buffer(self.inner_radius + self.width)
|
|
75
|
+
self.polygon = outer_circle.difference(inner_circle)
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
@dataclass(config=config_dict)
|
|
79
|
+
class Lense(BaseComponent):
|
|
80
|
+
"""
|
|
81
|
+
Represents a lens in a simulation grid.
|
|
82
|
+
|
|
83
|
+
Attributes:
|
|
84
|
+
position (Tuple[Union[float, str], Union[float, str]]): Starting position of the lens (center of the lens).
|
|
85
|
+
epsilon_r (float): Relative permittivity inside the lens.
|
|
86
|
+
radius (float): Radius of the lens.
|
|
87
|
+
curvature (float): Curvature of the lens (positive for convex, negative for concave).
|
|
88
|
+
"""
|
|
89
|
+
position: Tuple[Union[float, str], Union[float, str]]
|
|
90
|
+
epsilon_r: float
|
|
91
|
+
width: float
|
|
92
|
+
curvature: float
|
|
93
|
+
|
|
94
|
+
def compute_polygon(self) -> Path:
|
|
95
|
+
"""
|
|
96
|
+
Compute the path of the lens.
|
|
97
|
+
"""
|
|
98
|
+
# Create two arcs for the lens shape
|
|
99
|
+
x0 = self.coordinate.x + self.curvature - self.width / 2
|
|
100
|
+
x1 = self.coordinate.x - self.curvature + self.width / 2
|
|
101
|
+
|
|
102
|
+
arc3 = geo.Point(x0, self.coordinate.y).buffer(self.curvature, resolution=100)
|
|
103
|
+
arc4 = geo.Point(x1, self.coordinate.y).buffer(self.curvature, resolution=100)
|
|
104
|
+
|
|
105
|
+
self.polygon = arc3.intersection(arc4)
|
|
@@ -8,7 +8,6 @@ from LightWave2D.grid import Grid
|
|
|
8
8
|
from MPSPlots.render2D import SceneList
|
|
9
9
|
import shapely.geometry as geo
|
|
10
10
|
from matplotlib.path import Path
|
|
11
|
-
import cv2
|
|
12
11
|
from pydantic.dataclasses import dataclass
|
|
13
12
|
from LightWave2D.components.base_class import BaseComponent
|
|
14
13
|
import matplotlib.pyplot as plt
|
|
@@ -21,7 +20,7 @@ config_dict = dict(
|
|
|
21
20
|
)
|
|
22
21
|
|
|
23
22
|
|
|
24
|
-
@dataclass(config=config_dict)
|
|
23
|
+
@dataclass(kw_only=True, config=config_dict)
|
|
25
24
|
class PointDetector(BaseComponent):
|
|
26
25
|
"""
|
|
27
26
|
Represents a point detector within a simulation grid.
|
|
@@ -31,10 +30,8 @@ class PointDetector(BaseComponent):
|
|
|
31
30
|
position (Tuple[float | str, float | str]): The (x, y) coordinates of the point detector on the grid.
|
|
32
31
|
data (numpy.ndarray): The data collected by the detector over time.
|
|
33
32
|
"""
|
|
34
|
-
grid: Grid
|
|
35
33
|
position: Union[Tuple[float | str, float | str]]
|
|
36
34
|
data: numpy.ndarray = field(init=False)
|
|
37
|
-
facecolor: str = 'blue'
|
|
38
35
|
|
|
39
36
|
def __post_init__(self):
|
|
40
37
|
"""
|
|
@@ -85,4 +82,4 @@ class PointDetector(BaseComponent):
|
|
|
85
82
|
color=self.facecolor
|
|
86
83
|
)
|
|
87
84
|
|
|
88
|
-
|
|
85
|
+
# -
|
|
@@ -6,7 +6,8 @@ from typing import Tuple, NoReturn, Optional, Union
|
|
|
6
6
|
import numpy
|
|
7
7
|
from LightWave2D.physics import Physics
|
|
8
8
|
from LightWave2D.grid import Grid
|
|
9
|
-
from LightWave2D.components import
|
|
9
|
+
from LightWave2D.components.basic import Circle, Square, Ellipse, Triangle
|
|
10
|
+
from LightWave2D.components.complex import Lense, Grating, RingResonator
|
|
10
11
|
from LightWave2D.source import PointSource, LineSource
|
|
11
12
|
from LightWave2D.detector import PointDetector
|
|
12
13
|
from LightWave2D.pml import PML
|
|
@@ -58,7 +59,7 @@ class Experiment:
|
|
|
58
59
|
raise ValueError("Axis must be 'x' or 'y'.")
|
|
59
60
|
return gradient
|
|
60
61
|
|
|
61
|
-
def plot(self, unit_size: int =
|
|
62
|
+
def plot(self, unit_size: int = 6, colormap: Optional[Union[str, object]] = 'Blues') -> NoReturn:
|
|
62
63
|
"""
|
|
63
64
|
Generates a plot of the FDTD simulation setup using a specified colormap.
|
|
64
65
|
|
|
@@ -72,13 +73,7 @@ class Experiment:
|
|
|
72
73
|
Returns:
|
|
73
74
|
SceneList: A SceneList object that contains the constructed plot.
|
|
74
75
|
"""
|
|
75
|
-
|
|
76
|
-
figure, ax = plt.subplots(1, 1, figsize=figsize)
|
|
77
|
-
|
|
78
|
-
ax.set_title('FDTD Simulation')
|
|
79
|
-
ax.set_xlabel(r'x position [$\mu$m]')
|
|
80
|
-
ax.set_xlabel(r'y position [$\mu$m]')
|
|
81
|
-
ax.set_aspect('equal')
|
|
76
|
+
figure, ax = self.get_figure_ax(unit_size=unit_size)
|
|
82
77
|
|
|
83
78
|
# Add PML layers to the plot if present
|
|
84
79
|
if self.pml:
|
|
@@ -134,9 +129,24 @@ class Experiment:
|
|
|
134
129
|
return Square(grid=self.grid, **kwargs)
|
|
135
130
|
|
|
136
131
|
@add_to_component
|
|
137
|
-
def
|
|
132
|
+
def add_triangle(self, **kwargs) -> Square:
|
|
138
133
|
"""General method to add a component to the simulation."""
|
|
139
|
-
return
|
|
134
|
+
return Triangle(grid=self.grid, **kwargs)
|
|
135
|
+
|
|
136
|
+
@add_to_component
|
|
137
|
+
def add_lense(self, **kwargs) -> Square:
|
|
138
|
+
"""General method to add a component to the simulation."""
|
|
139
|
+
return Lense(grid=self.grid, **kwargs)
|
|
140
|
+
|
|
141
|
+
@add_to_component
|
|
142
|
+
def add_grating(self, **kwargs) -> Square:
|
|
143
|
+
"""General method to add a component to the simulation."""
|
|
144
|
+
return Grating(grid=self.grid, **kwargs)
|
|
145
|
+
|
|
146
|
+
@add_to_component
|
|
147
|
+
def add_ring_resonator(self, **kwargs) -> Square:
|
|
148
|
+
"""General method to add a component to the simulation."""
|
|
149
|
+
return RingResonator(grid=self.grid, **kwargs)
|
|
140
150
|
|
|
141
151
|
@add_to_source
|
|
142
152
|
def add_point_source(self, **kwargs) -> PointSource:
|
|
@@ -227,7 +237,7 @@ class Experiment:
|
|
|
227
237
|
for detector in self.detectors:
|
|
228
238
|
detector.update_data(field=self.Ez_t)
|
|
229
239
|
|
|
230
|
-
def plot_frame(self, frame_number: int, unit_size: int =
|
|
240
|
+
def plot_frame(self, frame_number: int, scale_max: float = 5, unit_size: int = 6, colormap: Optional[Union[str, object]] = colormaps.polytechnique.blue_black_red) -> SceneList:
|
|
231
241
|
"""
|
|
232
242
|
Creates a plot of a specific frame from the FDTD simulation.
|
|
233
243
|
|
|
@@ -242,13 +252,7 @@ class Experiment:
|
|
|
242
252
|
Returns:
|
|
243
253
|
SceneList: A SceneList object that contains the constructed plot.
|
|
244
254
|
"""
|
|
245
|
-
|
|
246
|
-
figure, ax = plt.subplots(1, 1, figsize=figsize)
|
|
247
|
-
|
|
248
|
-
ax.set_title('FDTD Simulation')
|
|
249
|
-
ax.set_xlabel(r'x position [$\mu$m]')
|
|
250
|
-
ax.set_xlabel(r'y position [$\mu$m]')
|
|
251
|
-
ax.set_aspect('equal')
|
|
255
|
+
figure, ax = self.get_figure_ax(unit_size=unit_size)
|
|
252
256
|
|
|
253
257
|
image = ax.pcolormesh(
|
|
254
258
|
self.grid.x_stamp,
|
|
@@ -262,13 +266,13 @@ class Experiment:
|
|
|
262
266
|
|
|
263
267
|
vmin, vmax = image.get_clim()
|
|
264
268
|
|
|
265
|
-
max_diff = max(abs(vmin), abs(vmax))
|
|
269
|
+
max_diff = max(abs(vmin), abs(vmax)) / scale_max
|
|
266
270
|
|
|
267
271
|
image.set_clim([-max_diff, max_diff])
|
|
268
272
|
|
|
269
273
|
plt.show()
|
|
270
274
|
|
|
271
|
-
def save_frame_as_image(self, frame_number: int, filename: str, unit_size: int =
|
|
275
|
+
def save_frame_as_image(self, frame_number: int, filename: str, scale_max: float = 5, unit_size: int = 6, dpi: int = 200, colormap: Optional[Union[str, object]] = colormaps.polytechnique.blue_black_red) -> None:
|
|
272
276
|
"""
|
|
273
277
|
Saves a specific frame from the FDTD simulation as an image file at the specified resolution.
|
|
274
278
|
|
|
@@ -285,13 +289,7 @@ class Experiment:
|
|
|
285
289
|
Returns:
|
|
286
290
|
None: This method does not return any value, but saves an image file.
|
|
287
291
|
"""
|
|
288
|
-
|
|
289
|
-
figure, ax = plt.subplots(1, 1, figsize=figsize)
|
|
290
|
-
|
|
291
|
-
ax.set_title('FDTD Simulation')
|
|
292
|
-
ax.set_xlabel(r'x position [$\mu$m]')
|
|
293
|
-
ax.set_xlabel(r'y position [$\mu$m]')
|
|
294
|
-
ax.set_aspect('equal')
|
|
292
|
+
figure, ax = self.get_figure_ax(unit_size=unit_size)
|
|
295
293
|
|
|
296
294
|
image = ax.pcolormesh(
|
|
297
295
|
self.grid.x_stamp,
|
|
@@ -305,13 +303,24 @@ class Experiment:
|
|
|
305
303
|
|
|
306
304
|
vmin, vmax = image.get_clim()
|
|
307
305
|
|
|
308
|
-
max_diff = max(abs(vmin), abs(vmax))
|
|
306
|
+
max_diff = max(abs(vmin), abs(vmax)) / scale_max
|
|
309
307
|
|
|
310
308
|
image.set_clim([-max_diff, max_diff])
|
|
311
309
|
|
|
312
310
|
plt.savefig(filename, dpi=dpi)
|
|
313
311
|
|
|
314
|
-
def
|
|
312
|
+
def get_figure_ax(self, unit_size: int = 6) -> Tuple:
|
|
313
|
+
figsize = int(unit_size), int(unit_size * self.grid.size_y / self.grid.size_x)
|
|
314
|
+
figure, ax = plt.subplots(1, 1, figsize=figsize)
|
|
315
|
+
|
|
316
|
+
ax.set_title('FDTD Simulation')
|
|
317
|
+
ax.set_xlabel(r'x position [$\mu$m]')
|
|
318
|
+
ax.set_ylabel(r'y position [$\mu$m]')
|
|
319
|
+
ax.set_aspect('equal')
|
|
320
|
+
|
|
321
|
+
return figure, ax
|
|
322
|
+
|
|
323
|
+
def render_propagation(self, skip_frame: int = 10, scale_max: float = 5, unit_size: int = 6, colormap: Optional[Union[str, object]] = colormaps.blue_black_red) -> animation.FuncAnimation:
|
|
315
324
|
"""
|
|
316
325
|
Renders the propagation of a field as an animation using matplotlib.
|
|
317
326
|
|
|
@@ -326,13 +335,7 @@ class Experiment:
|
|
|
326
335
|
Returns:
|
|
327
336
|
animation.FuncAnimation: The animation object that can be displayed or saved.
|
|
328
337
|
"""
|
|
329
|
-
|
|
330
|
-
figure, ax = plt.subplots(1, 1, figsize=figsize)
|
|
331
|
-
|
|
332
|
-
ax.set_title('FDTD Simulation')
|
|
333
|
-
ax.set_xlabel(r'x position [$\mu$m]')
|
|
334
|
-
ax.set_xlabel(r'y position [$\mu$m]')
|
|
335
|
-
ax.set_aspect('equal')
|
|
338
|
+
figure, ax = self.get_figure_ax(unit_size=unit_size)
|
|
336
339
|
|
|
337
340
|
# Initialize the field display
|
|
338
341
|
initial_field = numpy.zeros(self.Ez_t[0].shape).T
|
|
@@ -351,7 +354,7 @@ class Experiment:
|
|
|
351
354
|
artist_list.append(component.add_to_ax(ax))
|
|
352
355
|
|
|
353
356
|
# Set color limits based on the maximum field amplitude
|
|
354
|
-
max_amplitude = abs(self.Ez_t).max() /
|
|
357
|
+
max_amplitude = abs(self.Ez_t).max() / scale_max
|
|
355
358
|
field_artist.set_clim(vmin=-max_amplitude, vmax=max_amplitude)
|
|
356
359
|
|
|
357
360
|
def update(frame) -> list:
|
|
@@ -58,7 +58,7 @@ class PML():
|
|
|
58
58
|
label='PML'
|
|
59
59
|
)
|
|
60
60
|
|
|
61
|
-
def plot(self, unit_size: int =
|
|
61
|
+
def plot(self, unit_size: int = 6) -> NoReturn:
|
|
62
62
|
figsize = int(unit_size), int(unit_size * self.grid.size_y / self.grid.size_x)
|
|
63
63
|
figure, ax = plt.subplots(1, 1, figsize=figsize)
|
|
64
64
|
|